2018-03-20 15:31:28 -07:00
// SPDX-License-Identifier: GPL-2.0+
// Driver to instantiate Chromebook i2c/smbus devices.
//
// Copyright (C) 2012 Google, Inc.
// Author: Benson Leung <bleung@chromium.org>
2012-10-25 14:21:21 -07:00
2018-03-20 15:31:30 -07:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2012-10-25 14:21:21 -07:00
# include <linux/dmi.h>
# include <linux/i2c.h>
2015-08-04 16:36:29 -07:00
# include <linux/platform_data/atmel_mxt_ts.h>
2013-03-07 19:43:34 -08:00
# include <linux/input.h>
# include <linux/interrupt.h>
2018-03-20 15:31:35 -07:00
# include <linux/ioport.h>
2012-10-25 14:21:21 -07:00
# include <linux/module.h>
2018-03-20 15:31:34 -07:00
# include <linux/pci.h>
2013-10-20 20:58:25 -07:00
# include <linux/platform_device.h>
2012-10-25 14:21:21 -07:00
2013-02-21 12:14:59 -08:00
# define ATMEL_TP_I2C_ADDR 0x4b
# define ATMEL_TP_I2C_BL_ADDR 0x25
2013-02-21 12:15:00 -08:00
# define ATMEL_TS_I2C_ADDR 0x4a
# define ATMEL_TS_I2C_BL_ADDR 0x26
2012-10-25 14:21:21 -07:00
# define CYAPA_TP_I2C_ADDR 0x67
2016-05-02 08:57:16 +08:00
# define ELAN_TP_I2C_ADDR 0x15
2012-10-25 14:21:21 -07:00
# define ISL_ALS_I2C_ADDR 0x44
2013-02-01 14:34:45 -08:00
# define TAOS_ALS_I2C_ADDR 0x29
2012-10-25 14:21:21 -07:00
2013-11-25 13:10:25 -08:00
static const char * i2c_adapter_names [ ] = {
2012-10-25 14:21:21 -07:00
" SMBus I801 adapter " ,
2013-02-21 12:14:55 -08:00
" i915 gmbus vga " ,
" i915 gmbus panel " ,
2015-11-03 13:09:00 +02:00
" Synopsys DesignWare I2C adapter " ,
2012-10-25 14:21:21 -07:00
} ;
/* Keep this enum consistent with i2c_adapter_names */
enum i2c_adapter_type {
I2C_ADAPTER_SMBUS = 0 ,
2013-02-21 12:14:55 -08:00
I2C_ADAPTER_VGADDC ,
I2C_ADAPTER_PANEL ,
2018-03-20 15:31:34 -07:00
I2C_ADAPTER_DESIGNWARE ,
2012-10-25 14:21:21 -07:00
} ;
2013-10-20 20:58:24 -07:00
struct i2c_peripheral {
2018-03-20 15:31:32 -07:00
struct i2c_board_info board_info ;
unsigned short alt_addr ;
2018-03-20 15:31:35 -07:00
2018-03-20 15:31:32 -07:00
const char * dmi_name ;
2018-03-20 15:31:35 -07:00
unsigned long irqflags ;
struct resource irq_resource ;
2013-10-20 20:58:24 -07:00
enum i2c_adapter_type type ;
2018-03-20 15:31:34 -07:00
u32 pci_devid ;
2018-03-20 15:31:32 -07:00
struct i2c_client * client ;
2013-10-20 20:58:24 -07:00
} ;
2016-05-02 08:57:16 +08:00
# define MAX_I2C_PERIPHERALS 4
2013-10-20 20:58:24 -07:00
struct chromeos_laptop {
struct i2c_peripheral i2c_peripherals [ MAX_I2C_PERIPHERALS ] ;
} ;
2013-10-20 20:58:25 -07:00
static struct chromeos_laptop * cros_laptop ;
2018-03-20 15:31:32 -07:00
static struct i2c_client *
2018-03-20 15:31:34 -07:00
chromes_laptop_instantiate_i2c_device ( struct i2c_adapter * adapter ,
2018-03-20 15:31:32 -07:00
struct i2c_board_info * info ,
unsigned short alt_addr )
2012-10-25 14:21:21 -07:00
{
2015-04-14 13:50:09 -07:00
const unsigned short addr_list [ ] = { info - > addr , I2C_CLIENT_END } ;
2018-03-20 15:31:34 -07:00
struct i2c_client * client ;
2012-10-25 14:21:21 -07:00
2015-04-14 13:50:09 -07:00
/*
* Add the i2c device . If we can ' t detect it at the primary
* address we scan secondary addresses . In any case the client
* structure gets assigned primary address .
*/
client = i2c_new_probed_device ( adapter , info , addr_list , NULL ) ;
2018-03-20 15:31:32 -07:00
if ( ! client & & alt_addr ) {
2015-04-14 13:50:09 -07:00
struct i2c_board_info dummy_info = {
I2C_BOARD_INFO ( " dummy " , info - > addr ) ,
} ;
2018-03-20 15:31:32 -07:00
const unsigned short alt_addr_list [ ] = {
alt_addr , I2C_CLIENT_END
} ;
2015-04-14 13:50:09 -07:00
struct i2c_client * dummy ;
dummy = i2c_new_probed_device ( adapter , & dummy_info ,
alt_addr_list , NULL ) ;
if ( dummy ) {
2018-03-20 15:31:30 -07:00
pr_debug ( " %d-%02x is probed at %02x \n " ,
2018-03-20 15:31:34 -07:00
adapter - > nr , info - > addr , dummy - > addr ) ;
2015-04-14 13:50:09 -07:00
i2c_unregister_device ( dummy ) ;
client = i2c_new_device ( adapter , info ) ;
}
}
2012-10-25 14:21:21 -07:00
if ( ! client )
2018-03-20 15:31:34 -07:00
pr_debug ( " failed to register device %d-%02x \n " ,
adapter - > nr , info - > addr ) ;
2012-10-25 14:21:21 -07:00
else
2018-03-20 15:31:34 -07:00
pr_debug ( " added i2c device %d-%02x \n " ,
adapter - > nr , info - > addr ) ;
2012-10-25 14:21:21 -07:00
return client ;
}
2018-03-20 15:31:34 -07:00
static bool chromeos_laptop_match_adapter_devid ( struct device * dev , u32 devid )
2012-10-25 14:21:21 -07:00
{
2018-03-20 15:31:34 -07:00
struct pci_dev * pdev ;
2014-05-29 20:45:07 +02:00
2018-03-20 15:31:34 -07:00
if ( ! dev_is_pci ( dev ) )
return false ;
2012-10-25 14:21:21 -07:00
2018-03-20 15:31:34 -07:00
pdev = to_pci_dev ( dev ) ;
return devid = = PCI_DEVID ( pdev - > bus - > number , pdev - > devfn ) ;
2012-10-25 14:21:21 -07:00
}
2018-03-20 15:31:34 -07:00
static void chromeos_laptop_check_adapter ( struct i2c_adapter * adapter )
2013-02-21 12:14:58 -08:00
{
2018-03-20 15:31:34 -07:00
struct i2c_peripheral * i2c_dev ;
int i ;
2013-02-21 12:14:58 -08:00
2018-03-20 15:31:34 -07:00
for ( i = 0 ; i < MAX_I2C_PERIPHERALS ; i + + ) {
i2c_dev = & cros_laptop - > i2c_peripherals [ i ] ;
2013-02-21 12:14:57 -08:00
2018-03-20 15:31:34 -07:00
/* No more peripherals */
if ( ! i2c_dev - > board_info . addr )
break ;
2013-10-20 20:58:25 -07:00
2018-03-20 15:31:34 -07:00
/* Skip devices already created */
if ( i2c_dev - > client )
continue ;
if ( strncmp ( adapter - > name , i2c_adapter_names [ i2c_dev - > type ] ,
strlen ( i2c_adapter_names [ i2c_dev - > type ] ) ) )
continue ;
if ( i2c_dev - > pci_devid & &
! chromeos_laptop_match_adapter_devid ( adapter - > dev . parent ,
i2c_dev - > pci_devid ) ) {
continue ;
}
i2c_dev - > client =
chromes_laptop_instantiate_i2c_device ( adapter ,
& i2c_dev - > board_info ,
i2c_dev - > alt_addr ) ;
}
2013-02-01 14:34:46 -08:00
}
2018-03-20 15:31:34 -07:00
static void chromeos_laptop_detach_i2c_client ( struct i2c_client * client )
2013-10-20 20:58:25 -07:00
{
2018-03-20 15:31:32 -07:00
struct i2c_peripheral * i2c_dev ;
2013-10-20 20:58:25 -07:00
int i ;
2013-10-20 20:58:24 -07:00
for ( i = 0 ; i < MAX_I2C_PERIPHERALS ; i + + ) {
i2c_dev = & cros_laptop - > i2c_peripherals [ i ] ;
2018-03-20 15:31:34 -07:00
if ( i2c_dev - > client = = client )
i2c_dev - > client = NULL ;
}
}
2014-07-15 17:43:11 -07:00
2018-03-20 15:31:34 -07:00
static int chromeos_laptop_i2c_notifier_call ( struct notifier_block * nb ,
unsigned long action , void * data )
{
struct device * dev = data ;
switch ( action ) {
case BUS_NOTIFY_ADD_DEVICE :
if ( dev - > type = = & i2c_adapter_type )
chromeos_laptop_check_adapter ( to_i2c_adapter ( dev ) ) ;
break ;
case BUS_NOTIFY_REMOVED_DEVICE :
if ( dev - > type = = & i2c_client_type )
chromeos_laptop_detach_i2c_client ( to_i2c_client ( dev ) ) ;
break ;
2013-10-20 20:58:24 -07:00
}
2018-03-20 15:31:34 -07:00
return 0 ;
2013-02-01 14:34:45 -08:00
}
2018-03-20 15:31:34 -07:00
static struct notifier_block chromeos_laptop_i2c_notifier = {
. notifier_call = chromeos_laptop_i2c_notifier_call ,
} ;
2018-03-06 10:59:15 -08:00
static struct chromeos_laptop samsung_series_5_550 = {
2013-10-20 20:58:24 -07:00
. i2c_peripherals = {
/* Touchpad. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " cyapa " , CYAPA_TP_I2C_ADDR ) ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " trackpad " ,
. type = I2C_ADAPTER_SMBUS ,
} ,
2013-10-20 20:58:24 -07:00
/* Light Sensor. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " isl29018 " , ISL_ALS_I2C_ADDR ) ,
} ,
. dmi_name = " lightsensor " ,
. type = I2C_ADAPTER_SMBUS ,
} ,
2013-10-20 20:58:24 -07:00
} ,
} ;
2018-03-06 10:59:15 -08:00
static struct chromeos_laptop samsung_series_5 = {
2013-10-20 20:58:24 -07:00
. i2c_peripherals = {
/* Light Sensor. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " tsl2583 " , TAOS_ALS_I2C_ADDR ) ,
} ,
. type = I2C_ADAPTER_SMBUS ,
} ,
2013-10-20 20:58:24 -07:00
} ,
} ;
2018-03-20 15:31:32 -07:00
static int chromebook_pixel_tp_keys [ ] = {
KEY_RESERVED ,
KEY_RESERVED ,
KEY_RESERVED ,
KEY_RESERVED ,
KEY_RESERVED ,
BTN_LEFT
} ;
static struct mxt_platform_data chromebook_pixel_tp_platform_data = {
. t19_num_keys = ARRAY_SIZE ( chromebook_pixel_tp_keys ) ,
. t19_keymap = chromebook_pixel_tp_keys ,
} ;
2018-03-06 10:59:15 -08:00
static struct chromeos_laptop chromebook_pixel = {
2013-10-20 20:58:24 -07:00
. i2c_peripherals = {
/* Touch Screen. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " atmel_mxt_ts " ,
ATMEL_TS_I2C_ADDR ) ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " touchscreen " ,
2018-03-20 15:31:35 -07:00
. irqflags = IRQF_TRIGGER_FALLING ,
2018-03-20 15:31:32 -07:00
. type = I2C_ADAPTER_PANEL ,
. alt_addr = ATMEL_TS_I2C_BL_ADDR ,
} ,
2013-10-20 20:58:24 -07:00
/* Touchpad. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " atmel_mxt_tp " ,
ATMEL_TP_I2C_ADDR ) ,
. platform_data =
& chromebook_pixel_tp_platform_data ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " trackpad " ,
2018-03-20 15:31:35 -07:00
. irqflags = IRQF_TRIGGER_FALLING ,
2018-03-20 15:31:32 -07:00
. type = I2C_ADAPTER_VGADDC ,
. alt_addr = ATMEL_TP_I2C_BL_ADDR ,
} ,
2013-10-20 20:58:24 -07:00
/* Light Sensor. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " isl29018 " , ISL_ALS_I2C_ADDR ) ,
} ,
. dmi_name = " lightsensor " ,
. type = I2C_ADAPTER_PANEL ,
} ,
2013-10-20 20:58:24 -07:00
} ,
} ;
2018-03-06 10:59:15 -08:00
static struct chromeos_laptop hp_chromebook_14 = {
2014-06-17 14:02:01 -07:00
. i2c_peripherals = {
/* Touchpad. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " cyapa " , CYAPA_TP_I2C_ADDR ) ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " trackpad " ,
2018-03-20 15:31:34 -07:00
. type = I2C_ADAPTER_DESIGNWARE ,
2018-03-20 15:31:32 -07:00
} ,
2014-06-17 14:02:01 -07:00
} ,
} ;
2018-03-06 10:59:15 -08:00
static struct chromeos_laptop dell_chromebook_11 = {
2014-06-17 14:02:02 -07:00
. i2c_peripherals = {
/* Touchpad. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " cyapa " , CYAPA_TP_I2C_ADDR ) ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " trackpad " ,
2018-03-20 15:31:34 -07:00
. type = I2C_ADAPTER_DESIGNWARE ,
2018-03-20 15:31:32 -07:00
} ,
2016-05-02 08:57:17 +08:00
/* Elan Touchpad option. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " elan_i2c " , ELAN_TP_I2C_ADDR ) ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " trackpad " ,
2018-03-20 15:31:34 -07:00
. type = I2C_ADAPTER_DESIGNWARE ,
2018-03-20 15:31:32 -07:00
} ,
2014-06-17 14:02:02 -07:00
} ,
} ;
2018-03-06 10:59:15 -08:00
static struct chromeos_laptop toshiba_cb35 = {
2014-06-17 14:02:03 -07:00
. i2c_peripherals = {
/* Touchpad. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " cyapa " , CYAPA_TP_I2C_ADDR ) ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " trackpad " ,
2018-03-20 15:31:34 -07:00
. type = I2C_ADAPTER_DESIGNWARE ,
2018-03-20 15:31:32 -07:00
} ,
2014-06-17 14:02:03 -07:00
} ,
} ;
2018-03-06 10:59:15 -08:00
static struct chromeos_laptop acer_c7_chromebook = {
2013-10-20 20:58:24 -07:00
. i2c_peripherals = {
/* Touchpad. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " cyapa " , CYAPA_TP_I2C_ADDR ) ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " trackpad " ,
. type = I2C_ADAPTER_SMBUS ,
} ,
2013-10-20 20:58:24 -07:00
} ,
} ;
2018-03-06 10:59:15 -08:00
static struct chromeos_laptop acer_ac700 = {
2013-10-20 20:58:24 -07:00
. i2c_peripherals = {
/* Light Sensor. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " tsl2583 " , TAOS_ALS_I2C_ADDR ) ,
} ,
. type = I2C_ADAPTER_SMBUS ,
} ,
2013-10-20 20:58:24 -07:00
} ,
} ;
2018-03-06 10:59:15 -08:00
static struct chromeos_laptop acer_c720 = {
2014-06-17 14:02:00 -07:00
. i2c_peripherals = {
2014-07-15 20:00:54 -04:00
/* Touchscreen. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " atmel_mxt_ts " ,
ATMEL_TS_I2C_ADDR ) ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " touchscreen " ,
2018-03-20 15:31:35 -07:00
. irqflags = IRQF_TRIGGER_FALLING ,
2018-03-20 15:31:34 -07:00
. type = I2C_ADAPTER_DESIGNWARE ,
. pci_devid = PCI_DEVID ( 0 , PCI_DEVFN ( 0x15 , 0x2 ) ) ,
2018-03-20 15:31:32 -07:00
. alt_addr = ATMEL_TS_I2C_BL_ADDR ,
} ,
2014-06-17 14:02:00 -07:00
/* Touchpad. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " cyapa " , CYAPA_TP_I2C_ADDR ) ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " trackpad " ,
2018-03-20 15:31:34 -07:00
. type = I2C_ADAPTER_DESIGNWARE ,
. pci_devid = PCI_DEVID ( 0 , PCI_DEVFN ( 0x15 , 0x1 ) ) ,
2018-03-20 15:31:32 -07:00
} ,
2016-05-02 08:57:16 +08:00
/* Elan Touchpad option. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " elan_i2c " , ELAN_TP_I2C_ADDR ) ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " trackpad " ,
2018-03-20 15:31:34 -07:00
. type = I2C_ADAPTER_DESIGNWARE ,
. pci_devid = PCI_DEVID ( 0 , PCI_DEVFN ( 0x15 , 0x1 ) ) ,
2018-03-20 15:31:32 -07:00
} ,
2014-06-17 14:02:00 -07:00
/* Light Sensor. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " isl29018 " , ISL_ALS_I2C_ADDR ) ,
} ,
. dmi_name = " lightsensor " ,
2018-03-20 15:31:34 -07:00
. type = I2C_ADAPTER_DESIGNWARE ,
. pci_devid = PCI_DEVID ( 0 , PCI_DEVFN ( 0x15 , 0x2 ) ) ,
2018-03-20 15:31:32 -07:00
} ,
2014-06-17 14:02:00 -07:00
} ,
} ;
2018-03-06 10:59:15 -08:00
static struct chromeos_laptop hp_pavilion_14_chromebook = {
2013-10-20 20:58:24 -07:00
. i2c_peripherals = {
/* Touchpad. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " cyapa " , CYAPA_TP_I2C_ADDR ) ,
. flags = I2C_CLIENT_WAKE ,
} ,
. dmi_name = " trackpad " ,
. type = I2C_ADAPTER_SMBUS ,
} ,
2013-10-20 20:58:24 -07:00
} ,
} ;
2018-03-06 10:59:15 -08:00
static struct chromeos_laptop cr48 = {
2013-10-20 20:58:24 -07:00
. i2c_peripherals = {
/* Light Sensor. */
2018-03-20 15:31:32 -07:00
{
. board_info = {
I2C_BOARD_INFO ( " tsl2563 " , TAOS_ALS_I2C_ADDR ) ,
} ,
. type = I2C_ADAPTER_SMBUS ,
} ,
2013-10-20 20:58:24 -07:00
} ,
} ;
2017-09-14 11:59:30 +02:00
static const struct dmi_system_id chromeos_laptop_dmi_table [ ] __initconst = {
2012-10-25 14:21:21 -07:00
{
2013-10-20 20:58:24 -07:00
. ident = " Samsung Series 5 550 " ,
2012-10-25 14:21:21 -07:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " SAMSUNG " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Lumpy " ) ,
} ,
2018-03-20 15:31:33 -07:00
. driver_data = ( void * ) & samsung_series_5_550 ,
2013-02-21 12:14:59 -08:00
} ,
2012-10-25 14:21:21 -07:00
{
2013-10-20 20:58:24 -07:00
. ident = " Samsung Series 5 " ,
2012-10-25 14:21:21 -07:00
. matches = {
2013-10-20 20:58:24 -07:00
DMI_MATCH ( DMI_PRODUCT_NAME , " Alex " ) ,
2012-10-25 14:21:21 -07:00
} ,
2018-03-20 15:31:33 -07:00
. driver_data = ( void * ) & samsung_series_5 ,
2012-10-25 14:21:21 -07:00
} ,
2013-02-21 12:14:57 -08:00
{
2013-10-20 20:58:24 -07:00
. ident = " Chromebook Pixel " ,
2013-02-21 12:14:57 -08:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " GOOGLE " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Link " ) ,
} ,
2018-03-20 15:31:33 -07:00
. driver_data = ( void * ) & chromebook_pixel ,
2013-02-21 12:14:57 -08:00
} ,
2014-06-17 14:02:02 -07:00
{
. ident = " Wolf " ,
. matches = {
DMI_MATCH ( DMI_BIOS_VENDOR , " coreboot " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Wolf " ) ,
} ,
2018-03-20 15:31:33 -07:00
. driver_data = ( void * ) & dell_chromebook_11 ,
2014-06-17 14:02:02 -07:00
} ,
2014-06-17 14:02:01 -07:00
{
. ident = " HP Chromebook 14 " ,
. matches = {
DMI_MATCH ( DMI_BIOS_VENDOR , " coreboot " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Falco " ) ,
} ,
2018-03-20 15:31:33 -07:00
. driver_data = ( void * ) & hp_chromebook_14 ,
2014-06-17 14:02:01 -07:00
} ,
2014-06-17 14:02:03 -07:00
{
. ident = " Toshiba CB35 " ,
. matches = {
DMI_MATCH ( DMI_BIOS_VENDOR , " coreboot " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Leon " ) ,
} ,
2018-03-20 15:31:33 -07:00
. driver_data = ( void * ) & toshiba_cb35 ,
2014-06-17 14:02:03 -07:00
} ,
2013-02-01 14:34:44 -08:00
{
2013-10-20 20:58:24 -07:00
. ident = " Acer C7 Chromebook " ,
2013-02-01 14:34:44 -08:00
. matches = {
DMI_MATCH ( DMI_PRODUCT_NAME , " Parrot " ) ,
} ,
2018-03-20 15:31:33 -07:00
. driver_data = ( void * ) & acer_c7_chromebook ,
2013-02-10 16:20:17 -08:00
} ,
{
2013-10-20 20:58:24 -07:00
. ident = " Acer AC700 " ,
2013-02-10 16:20:17 -08:00
. matches = {
2013-10-20 20:58:24 -07:00
DMI_MATCH ( DMI_PRODUCT_NAME , " ZGB " ) ,
2013-02-10 16:20:17 -08:00
} ,
2018-03-20 15:31:33 -07:00
. driver_data = ( void * ) & acer_ac700 ,
2013-02-01 14:34:44 -08:00
} ,
2014-06-17 14:02:00 -07:00
{
. ident = " Acer C720 " ,
. matches = {
DMI_MATCH ( DMI_PRODUCT_NAME , " Peppy " ) ,
} ,
2018-03-20 15:31:33 -07:00
. driver_data = ( void * ) & acer_c720 ,
2014-06-17 14:02:00 -07:00
} ,
2013-02-01 14:34:46 -08:00
{
2013-10-20 20:58:24 -07:00
. ident = " HP Pavilion 14 Chromebook " ,
2013-02-01 14:34:46 -08:00
. matches = {
2013-10-20 20:58:24 -07:00
DMI_MATCH ( DMI_PRODUCT_NAME , " Butterfly " ) ,
2013-02-01 14:34:46 -08:00
} ,
2018-03-20 15:31:33 -07:00
. driver_data = ( void * ) & hp_pavilion_14_chromebook ,
2013-02-01 14:34:46 -08:00
} ,
2013-02-01 14:34:45 -08:00
{
2013-10-20 20:58:24 -07:00
. ident = " Cr-48 " ,
2013-02-01 14:34:45 -08:00
. matches = {
DMI_MATCH ( DMI_PRODUCT_NAME , " Mario " ) ,
} ,
2018-03-20 15:31:33 -07:00
. driver_data = ( void * ) & cr48 ,
2013-02-01 14:34:45 -08:00
} ,
2012-10-25 14:21:21 -07:00
{ }
} ;
MODULE_DEVICE_TABLE ( dmi , chromeos_laptop_dmi_table ) ;
2018-03-20 15:31:34 -07:00
static int __init chromeos_laptop_scan_adapter ( struct device * dev , void * data )
{
struct i2c_adapter * adapter ;
2013-10-20 20:58:25 -07:00
2018-03-20 15:31:34 -07:00
adapter = i2c_verify_adapter ( dev ) ;
if ( adapter )
chromeos_laptop_check_adapter ( adapter ) ;
return 0 ;
}
2013-10-20 20:58:25 -07:00
2018-03-20 15:31:33 -07:00
static int __init chromeos_laptop_get_irq_from_dmi ( const char * dmi_name )
{
const struct dmi_device * dmi_dev ;
const struct dmi_dev_onboard * dev_data ;
dmi_dev = dmi_find_device ( DMI_DEV_TYPE_DEV_ONBOARD , dmi_name , NULL ) ;
if ( ! dmi_dev ) {
pr_err ( " failed to find DMI device '%s' \n " , dmi_name ) ;
return - ENOENT ;
}
dev_data = dmi_dev - > device_data ;
if ( ! dev_data ) {
pr_err ( " failed to get data from DMI for '%s' \n " , dmi_name ) ;
return - EINVAL ;
}
return dev_data - > instance ;
}
static struct chromeos_laptop * __init
chromeos_laptop_prepare ( const struct dmi_system_id * id )
{
struct i2c_peripheral * i2c_dev ;
int irq ;
int i ;
cros_laptop = ( void * ) id - > driver_data ;
for ( i = 0 ; i < MAX_I2C_PERIPHERALS ; i + + ) {
i2c_dev = & cros_laptop - > i2c_peripherals [ i ] ;
if ( ! i2c_dev - > dmi_name )
continue ;
irq = chromeos_laptop_get_irq_from_dmi ( i2c_dev - > dmi_name ) ;
if ( irq < 0 )
return ERR_PTR ( irq ) ;
2018-03-20 15:31:35 -07:00
i2c_dev - > irq_resource = ( struct resource )
DEFINE_RES_NAMED ( irq , 1 , NULL ,
IORESOURCE_IRQ | i2c_dev - > irqflags ) ;
i2c_dev - > board_info . resources = & i2c_dev - > irq_resource ;
i2c_dev - > board_info . num_resources = 1 ;
2018-03-20 15:31:33 -07:00
}
return cros_laptop ;
}
2012-10-25 14:21:21 -07:00
static int __init chromeos_laptop_init ( void )
{
2018-03-20 15:31:33 -07:00
const struct dmi_system_id * dmi_id ;
2018-03-20 15:31:34 -07:00
int error ;
2014-05-29 20:45:07 +02:00
2018-03-20 15:31:33 -07:00
dmi_id = dmi_first_match ( chromeos_laptop_dmi_table ) ;
if ( ! dmi_id ) {
2018-03-20 15:31:30 -07:00
pr_debug ( " unsupported system \n " ) ;
2012-10-25 14:21:21 -07:00
return - ENODEV ;
}
2013-10-20 20:58:25 -07:00
2018-03-20 15:31:33 -07:00
pr_debug ( " DMI Matched %s \n " , dmi_id - > ident ) ;
cros_laptop = chromeos_laptop_prepare ( dmi_id - > driver_data ) ;
if ( IS_ERR ( cros_laptop ) )
return PTR_ERR ( cros_laptop ) ;
2018-03-20 15:31:34 -07:00
error = bus_register_notifier ( & i2c_bus_type ,
& chromeos_laptop_i2c_notifier ) ;
if ( error ) {
pr_err ( " failed to register i2c bus notifier: %d \n " , error ) ;
return error ;
2013-10-20 20:58:25 -07:00
}
2018-03-20 15:31:34 -07:00
/*
* Scan adapters that have been registered before we installed
* the notifier to make sure we do not miss any devices .
*/
i2c_for_each_dev ( NULL , chromeos_laptop_scan_adapter ) ;
2013-10-20 20:58:25 -07:00
2012-10-25 14:21:21 -07:00
return 0 ;
}
static void __exit chromeos_laptop_exit ( void )
{
2018-03-20 15:31:32 -07:00
struct i2c_peripheral * i2c_dev ;
int i ;
2013-11-27 11:34:58 +08:00
2018-03-20 15:31:34 -07:00
bus_unregister_notifier ( & i2c_bus_type , & chromeos_laptop_i2c_notifier ) ;
2018-03-20 15:31:32 -07:00
for ( i = 0 ; i < MAX_I2C_PERIPHERALS ; i + + ) {
i2c_dev = & cros_laptop - > i2c_peripherals [ i ] ;
/* No more peripherals */
if ( ! i2c_dev - > board_info . type )
break ;
2018-03-20 15:31:34 -07:00
if ( i2c_dev - > client )
2018-03-20 15:31:32 -07:00
i2c_unregister_device ( i2c_dev - > client ) ;
}
2012-10-25 14:21:21 -07:00
}
module_init ( chromeos_laptop_init ) ;
module_exit ( chromeos_laptop_exit ) ;
MODULE_DESCRIPTION ( " Chrome OS Laptop driver " ) ;
MODULE_AUTHOR ( " Benson Leung <bleung@chromium.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;