From e1120aede049a04a9de0cbc8d6633be2b3b5fd7b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 20 Mar 2018 15:31:29 -0700 Subject: [PATCH 01/29] platform/chrome: chromeos_laptop - stop setting suspend mode for Atmel devices Atmel touch controller driver no longer respects suspend mode specified in platform data, so let's stop setting it. Signed-off-by: Dmitry Torokhov Signed-off-by: Benson Leung --- drivers/platform/chrome/chromeos_laptop.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index d8599736a41a2..3d57d3ffe4b5e 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -117,7 +117,6 @@ static struct mxt_platform_data atmel_224s_tp_platform_data = { .irqflags = IRQF_TRIGGER_FALLING, .t19_num_keys = ARRAY_SIZE(mxt_t19_keys), .t19_keymap = mxt_t19_keys, - .suspend_mode = MXT_SUSPEND_T9_CTRL, }; static struct i2c_board_info atmel_224s_tp_device = { @@ -128,7 +127,6 @@ static struct i2c_board_info atmel_224s_tp_device = { static struct mxt_platform_data atmel_1664s_platform_data = { .irqflags = IRQF_TRIGGER_FALLING, - .suspend_mode = MXT_SUSPEND_T9_CTRL, }; static struct i2c_board_info atmel_1664s_device = { From 3bd910acbacd9be2ca849f790691a3c7f0996660 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 20 Mar 2018 15:31:30 -0700 Subject: [PATCH 02/29] platform/chrome: chromeos_laptop - introduce pr_fmt() Define pr_fmt() to standardize driver messages. Signed-off-by: Dmitry Torokhov Signed-off-by: Benson Leung --- drivers/platform/chrome/chromeos_laptop.c | 31 ++++++++++------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 3d57d3ffe4b5e..f9a9a4839d952 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -21,6 +21,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -156,15 +158,12 @@ static struct i2c_client *__add_probed_i2c_device( if (name) { dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL); if (!dmi_dev) { - pr_err("%s failed to dmi find device %s.\n", - __func__, - name); + pr_err("failed to dmi find device %s\n", name); return NULL; } dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data; if (!dev_data) { - pr_err("%s failed to get data from dmi for %s.\n", - __func__, name); + pr_err("failed to get data from dmi for %s\n", name); return NULL; } info->irq = dev_data->instance; @@ -172,7 +171,7 @@ static struct i2c_client *__add_probed_i2c_device( adapter = i2c_get_adapter(bus); if (!adapter) { - pr_err("%s failed to get i2c adapter %d.\n", __func__, bus); + pr_err("failed to get i2c adapter %d\n", bus); return NULL; } @@ -191,19 +190,18 @@ static struct i2c_client *__add_probed_i2c_device( dummy = i2c_new_probed_device(adapter, &dummy_info, alt_addr_list, NULL); if (dummy) { - pr_debug("%s %d-%02x is probed at %02x\n", - __func__, bus, info->addr, dummy->addr); + pr_debug("%d-%02x is probed at %02x\n", + bus, info->addr, dummy->addr); i2c_unregister_device(dummy); client = i2c_new_device(adapter, info); } } if (!client) - pr_notice("%s failed to register device %d-%02x\n", - __func__, bus, info->addr); + pr_notice("failed to register device %d-%02x\n", + bus, info->addr); else - pr_debug("%s added i2c device %d-%02x\n", - __func__, bus, info->addr); + pr_debug("added i2c device %d-%02x\n", bus, info->addr); i2c_put_adapter(adapter); return client; @@ -244,7 +242,7 @@ static int find_i2c_adapter_num(enum i2c_adapter_type type) dev = bus_find_device(&i2c_bus_type, NULL, &lookup, __find_i2c_adap); if (!dev) { /* Adapters may appear later. Deferred probing will retry */ - pr_notice("%s: i2c adapter %s not found on system.\n", __func__, + pr_notice("i2c adapter %s not found on system.\n", lookup.name); return -ENODEV; } @@ -366,7 +364,7 @@ static int setup_tsl2563_als(enum i2c_adapter_type type) static int __init chromeos_laptop_dmi_matched(const struct dmi_system_id *id) { cros_laptop = (void *)id->driver_data; - pr_debug("DMI Matched %s.\n", id->ident); + pr_debug("DMI Matched %s\n", id->ident); /* Indicate to dmi_scan that processing is done. */ return 1; @@ -409,8 +407,7 @@ static int chromeos_laptop_probe(struct platform_device *pdev) ret = -EPROBE_DEFER; } else { /* Ran out of tries. */ - pr_notice("%s: Ran out of tries for device.\n", - __func__); + pr_notice("ran out of tries for device.\n"); i2c_dev->state = TIMEDOUT; } } else { @@ -617,7 +614,7 @@ static int __init chromeos_laptop_init(void) int ret; if (!dmi_check_system(chromeos_laptop_dmi_table)) { - pr_debug("%s unsupported system.\n", __func__); + pr_debug("unsupported system\n"); return -ENODEV; } From e34e959d4b1c525ef05d16cc8c32441bba8b470e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 20 Mar 2018 15:31:31 -0700 Subject: [PATCH 03/29] platform/chrome: chromeos_laptop - factor out getting IRQ from DMI This will make code instantiating I2C device a bit clearer. Signed-off-by: Dmitry Torokhov Signed-off-by: Benson Leung --- drivers/platform/chrome/chromeos_laptop.c | 35 +++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index f9a9a4839d952..881a1b5c17f9e 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -137,36 +137,47 @@ static struct i2c_board_info atmel_1664s_device = { .flags = I2C_CLIENT_WAKE, }; +static int 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 i2c_client *__add_probed_i2c_device( const char *name, int bus, struct i2c_board_info *info, const unsigned short *alt_addr_list) { - const struct dmi_device *dmi_dev; - const struct dmi_dev_onboard *dev_data; struct i2c_adapter *adapter; struct i2c_client *client = NULL; const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END }; if (bus < 0) return NULL; + /* * If a name is specified, look for irq platform information stashed * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware. */ if (name) { - dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL); - if (!dmi_dev) { - pr_err("failed to dmi find device %s\n", name); - return NULL; - } - dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data; - if (!dev_data) { - pr_err("failed to get data from dmi for %s\n", name); + info->irq = chromeos_laptop_get_irq_from_dmi(name); + if (info->irq < 0) return NULL; - } - info->irq = dev_data->instance; } adapter = i2c_get_adapter(bus); From ffa0bb3954c903e1597ccb0b086ae49e79b1878d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 20 Mar 2018 15:31:32 -0700 Subject: [PATCH 04/29] platform/chrome: chromeos_laptop - rework i2c peripherals initialization Instead of having separate setup() functions responsible for instantiating i2c client for each peripheral, let's generalize the behavior and use common code for instantiating all i2c peripherals. Signed-off-by: Dmitry Torokhov Signed-off-by: Benson Leung --- drivers/platform/chrome/chromeos_laptop.c | 467 +++++++++++----------- 1 file changed, 235 insertions(+), 232 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 881a1b5c17f9e..479d813a9b57f 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -42,10 +42,6 @@ #define MAX_I2C_DEVICE_DEFERRALS 5 -static struct i2c_client *als; -static struct i2c_client *tp; -static struct i2c_client *ts; - static const char *i2c_adapter_names[] = { "SMBus I801 adapter", "i915 gmbus vga", @@ -67,12 +63,17 @@ enum i2c_peripheral_state { UNPROBED = 0, PROBED, TIMEDOUT, + FAILED, }; struct i2c_peripheral { - int (*add)(enum i2c_adapter_type type); + struct i2c_board_info board_info; + unsigned short alt_addr; + const char *dmi_name; enum i2c_adapter_type type; + enum i2c_peripheral_state state; + struct i2c_client *client; int tries; }; @@ -84,59 +85,6 @@ struct chromeos_laptop { static struct chromeos_laptop *cros_laptop; -static struct i2c_board_info cyapa_device = { - I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, -}; - -static struct i2c_board_info elantech_device = { - I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, -}; - -static struct i2c_board_info isl_als_device = { - I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), -}; - -static struct i2c_board_info tsl2583_als_device = { - I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), -}; - -static struct i2c_board_info tsl2563_als_device = { - I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), -}; - -static int mxt_t19_keys[] = { - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - BTN_LEFT -}; - -static struct mxt_platform_data atmel_224s_tp_platform_data = { - .irqflags = IRQF_TRIGGER_FALLING, - .t19_num_keys = ARRAY_SIZE(mxt_t19_keys), - .t19_keymap = mxt_t19_keys, -}; - -static struct i2c_board_info atmel_224s_tp_device = { - I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR), - .platform_data = &atmel_224s_tp_platform_data, - .flags = I2C_CLIENT_WAKE, -}; - -static struct mxt_platform_data atmel_1664s_platform_data = { - .irqflags = IRQF_TRIGGER_FALLING, -}; - -static struct i2c_board_info atmel_1664s_device = { - I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), - .platform_data = &atmel_1664s_platform_data, - .flags = I2C_CLIENT_WAKE, -}; - static int chromeos_laptop_get_irq_from_dmi(const char *dmi_name) { const struct dmi_device *dmi_dev; @@ -157,29 +105,15 @@ static int chromeos_laptop_get_irq_from_dmi(const char *dmi_name) return dev_data->instance; } -static struct i2c_client *__add_probed_i2c_device( - const char *name, - int bus, - struct i2c_board_info *info, - const unsigned short *alt_addr_list) +static struct i2c_client * +chromes_laptop_instantiate_i2c_device(int bus, + struct i2c_board_info *info, + unsigned short alt_addr) { struct i2c_adapter *adapter; struct i2c_client *client = NULL; const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END }; - if (bus < 0) - return NULL; - - /* - * If a name is specified, look for irq platform information stashed - * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware. - */ - if (name) { - info->irq = chromeos_laptop_get_irq_from_dmi(name); - if (info->irq < 0) - return NULL; - } - adapter = i2c_get_adapter(bus); if (!adapter) { pr_err("failed to get i2c adapter %d\n", bus); @@ -192,10 +126,13 @@ static struct i2c_client *__add_probed_i2c_device( * structure gets assigned primary address. */ client = i2c_new_probed_device(adapter, info, addr_list, NULL); - if (!client && alt_addr_list) { + if (!client && alt_addr) { struct i2c_board_info dummy_info = { I2C_BOARD_INFO("dummy", info->addr), }; + const unsigned short alt_addr_list[] = { + alt_addr, I2C_CLIENT_END + }; struct i2c_client *dummy; dummy = i2c_new_probed_device(adapter, &dummy_info, @@ -261,115 +198,53 @@ static int find_i2c_adapter_num(enum i2c_adapter_type type) return adapter->nr; } -/* - * Takes a list of addresses in addrs as such : - * { addr1, ... , addrn, I2C_CLIENT_END }; - * add_probed_i2c_device will use i2c_new_probed_device - * and probe for devices at all of the addresses listed. - * Returns NULL if no devices found. - * See Documentation/i2c/instantiating-devices for more information. - */ -static struct i2c_client *add_probed_i2c_device( - const char *name, - enum i2c_adapter_type type, - struct i2c_board_info *info, - const unsigned short *addrs) -{ - return __add_probed_i2c_device(name, - find_i2c_adapter_num(type), - info, - addrs); -} - -/* - * Probes for a device at a single address, the one provided by - * info->addr. - * Returns NULL if no device found. - */ -static struct i2c_client *add_i2c_device(const char *name, - enum i2c_adapter_type type, - struct i2c_board_info *info) -{ - return __add_probed_i2c_device(name, - find_i2c_adapter_num(type), - info, - NULL); -} - -static int setup_cyapa_tp(enum i2c_adapter_type type) -{ - if (tp) - return 0; - - /* add cyapa touchpad */ - tp = add_i2c_device("trackpad", type, &cyapa_device); - return (!tp) ? -EAGAIN : 0; -} - -static int setup_atmel_224s_tp(enum i2c_adapter_type type) +static int chromeos_laptop_add_peripheral(struct i2c_peripheral *i2c_dev) { - const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR, - I2C_CLIENT_END }; - if (tp) - return 0; - - /* add atmel mxt touchpad */ - tp = add_probed_i2c_device("trackpad", type, - &atmel_224s_tp_device, addr_list); - return (!tp) ? -EAGAIN : 0; -} - -static int setup_elantech_tp(enum i2c_adapter_type type) -{ - if (tp) - return 0; + struct i2c_client *client; + int bus; + int irq; - /* add elantech touchpad */ - tp = add_i2c_device("trackpad", type, &elantech_device); - return (!tp) ? -EAGAIN : 0; -} - -static int setup_atmel_1664s_ts(enum i2c_adapter_type type) -{ - const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR, - I2C_CLIENT_END }; - if (ts) - return 0; - - /* add atmel mxt touch device */ - ts = add_probed_i2c_device("touchscreen", type, - &atmel_1664s_device, addr_list); - return (!ts) ? -EAGAIN : 0; -} - -static int setup_isl29018_als(enum i2c_adapter_type type) -{ - if (als) - return 0; + /* + * Check that the i2c adapter is present. + * -EPROBE_DEFER if missing as the adapter may appear much + * later. + */ + bus = find_i2c_adapter_num(i2c_dev->type); + if (bus < 0) + return bus == -ENODEV ? -EPROBE_DEFER : bus; - /* add isl29018 light sensor */ - als = add_i2c_device("lightsensor", type, &isl_als_device); - return (!als) ? -EAGAIN : 0; -} + if (i2c_dev->dmi_name) { + irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name); + if (irq < 0) { + i2c_dev->state = FAILED; + return irq; + } -static int setup_tsl2583_als(enum i2c_adapter_type type) -{ - if (als) - return 0; + i2c_dev->board_info.irq = irq; + } - /* add tsl2583 light sensor */ - als = add_i2c_device(NULL, type, &tsl2583_als_device); - return (!als) ? -EAGAIN : 0; -} + client = chromes_laptop_instantiate_i2c_device(bus, + &i2c_dev->board_info, + i2c_dev->alt_addr); + if (!client) { + /* + * Set -EPROBE_DEFER a limited num of times + * if device is not successfully added. + */ + if (++i2c_dev->tries < MAX_I2C_DEVICE_DEFERRALS) { + return -EPROBE_DEFER; + } else { + /* Ran out of tries. */ + pr_notice("ran out of tries for device.\n"); + i2c_dev->state = TIMEDOUT; + return -EIO; + } + } -static int setup_tsl2563_als(enum i2c_adapter_type type) -{ - if (als) - return 0; + i2c_dev->client = client; + i2c_dev->state = PROBED; - /* add tsl2563 light sensor */ - als = add_i2c_device(NULL, type, &tsl2563_als_device); - return (!als) ? -EAGAIN : 0; + return 0; } static int __init chromeos_laptop_dmi_matched(const struct dmi_system_id *id) @@ -383,47 +258,22 @@ static int __init chromeos_laptop_dmi_matched(const struct dmi_system_id *id) static int chromeos_laptop_probe(struct platform_device *pdev) { + struct i2c_peripheral *i2c_dev; int i; int ret = 0; for (i = 0; i < MAX_I2C_PERIPHERALS; i++) { - struct i2c_peripheral *i2c_dev; - i2c_dev = &cros_laptop->i2c_peripherals[i]; /* No more peripherals. */ - if (i2c_dev->add == NULL) + if (!i2c_dev->board_info.addr) break; - if (i2c_dev->state == TIMEDOUT || i2c_dev->state == PROBED) + if (i2c_dev->state != UNPROBED) continue; - /* - * Check that the i2c adapter is present. - * -EPROBE_DEFER if missing as the adapter may appear much - * later. - */ - if (find_i2c_adapter_num(i2c_dev->type) == -ENODEV) { + if (chromeos_laptop_add_peripheral(i2c_dev) == -EPROBE_DEFER) ret = -EPROBE_DEFER; - continue; - } - - /* Add the device. */ - if (i2c_dev->add(i2c_dev->type) == -EAGAIN) { - /* - * Set -EPROBE_DEFER a limited num of times - * if device is not successfully added. - */ - if (++i2c_dev->tries < MAX_I2C_DEVICE_DEFERRALS) { - ret = -EPROBE_DEFER; - } else { - /* Ran out of tries. */ - pr_notice("ran out of tries for device.\n"); - i2c_dev->state = TIMEDOUT; - } - } else { - i2c_dev->state = PROBED; - } } return ret; @@ -432,91 +282,237 @@ static int chromeos_laptop_probe(struct platform_device *pdev) static struct chromeos_laptop samsung_series_5_550 = { .i2c_peripherals = { /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS }, + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_SMBUS, + }, /* Light Sensor. */ - { .add = setup_isl29018_als, I2C_ADAPTER_SMBUS }, + { + .board_info = { + I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), + }, + .dmi_name = "lightsensor", + .type = I2C_ADAPTER_SMBUS, + }, }, }; static struct chromeos_laptop samsung_series_5 = { .i2c_peripherals = { /* Light Sensor. */ - { .add = setup_tsl2583_als, I2C_ADAPTER_SMBUS }, + { + .board_info = { + I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), + }, + .type = I2C_ADAPTER_SMBUS, + }, }, }; +static struct mxt_platform_data atmel_1664s_platform_data = { + .irqflags = IRQF_TRIGGER_FALLING, +}; + +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 = { + .irqflags = IRQF_TRIGGER_FALLING, + .t19_num_keys = ARRAY_SIZE(chromebook_pixel_tp_keys), + .t19_keymap = chromebook_pixel_tp_keys, +}; + static struct chromeos_laptop chromebook_pixel = { .i2c_peripherals = { /* Touch Screen. */ - { .add = setup_atmel_1664s_ts, I2C_ADAPTER_PANEL }, + { + .board_info = { + I2C_BOARD_INFO("atmel_mxt_ts", + ATMEL_TS_I2C_ADDR), + .platform_data = &atmel_1664s_platform_data, + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "touchscreen", + .type = I2C_ADAPTER_PANEL, + .alt_addr = ATMEL_TS_I2C_BL_ADDR, + }, /* Touchpad. */ - { .add = setup_atmel_224s_tp, I2C_ADAPTER_VGADDC }, + { + .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", + .type = I2C_ADAPTER_VGADDC, + .alt_addr = ATMEL_TP_I2C_BL_ADDR, + }, /* Light Sensor. */ - { .add = setup_isl29018_als, I2C_ADAPTER_PANEL }, + { + .board_info = { + I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), + }, + .dmi_name = "lightsensor", + .type = I2C_ADAPTER_PANEL, + }, }, }; static struct chromeos_laptop hp_chromebook_14 = { .i2c_peripherals = { /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE_0, + }, }, }; static struct chromeos_laptop dell_chromebook_11 = { .i2c_peripherals = { /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE_0, + }, /* Elan Touchpad option. */ - { .add = setup_elantech_tp, I2C_ADAPTER_DESIGNWARE_0 }, + { + .board_info = { + I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE_0, + }, }, }; static struct chromeos_laptop toshiba_cb35 = { .i2c_peripherals = { /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE_0, + }, }, }; static struct chromeos_laptop acer_c7_chromebook = { .i2c_peripherals = { /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS }, + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_SMBUS, + }, }, }; static struct chromeos_laptop acer_ac700 = { .i2c_peripherals = { /* Light Sensor. */ - { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS }, + { + .board_info = { + I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), + }, + .type = I2C_ADAPTER_SMBUS, + }, }, }; static struct chromeos_laptop acer_c720 = { .i2c_peripherals = { /* Touchscreen. */ - { .add = setup_atmel_1664s_ts, I2C_ADAPTER_DESIGNWARE_1 }, + { + .board_info = { + I2C_BOARD_INFO("atmel_mxt_ts", + ATMEL_TS_I2C_ADDR), + .platform_data = &atmel_1664s_platform_data, + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "touchscreen", + .type = I2C_ADAPTER_DESIGNWARE_1, + .alt_addr = ATMEL_TS_I2C_BL_ADDR, + }, /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE_0, + }, /* Elan Touchpad option. */ - { .add = setup_elantech_tp, I2C_ADAPTER_DESIGNWARE_0 }, + { + .board_info = { + I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE_0, + }, /* Light Sensor. */ - { .add = setup_isl29018_als, I2C_ADAPTER_DESIGNWARE_1 }, + { + .board_info = { + I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), + }, + .dmi_name = "lightsensor", + .type = I2C_ADAPTER_DESIGNWARE_1, + }, }, }; static struct chromeos_laptop hp_pavilion_14_chromebook = { .i2c_peripherals = { /* Touchpad. */ - { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS }, + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, + }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_SMBUS, + }, }, }; static struct chromeos_laptop cr48 = { .i2c_peripherals = { /* Light Sensor. */ - { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS }, + { + .board_info = { + I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), + }, + .type = I2C_ADAPTER_SMBUS, + }, }, }; @@ -654,15 +650,22 @@ static int __init chromeos_laptop_init(void) static void __exit chromeos_laptop_exit(void) { - if (als) - i2c_unregister_device(als); - if (tp) - i2c_unregister_device(tp); - if (ts) - i2c_unregister_device(ts); + struct i2c_peripheral *i2c_dev; + int i; platform_device_unregister(cros_platform_device); platform_driver_unregister(&cros_platform_driver); + + 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; + + if (i2c_dev->state == PROBED) + i2c_unregister_device(i2c_dev->client); + } } module_init(chromeos_laptop_init); From 2d76941daa5ee8fb75f0e3a45f71bf5a41d6dcb4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 20 Mar 2018 15:31:33 -0700 Subject: [PATCH 05/29] platform/chrome: chromeos_laptop - parse DMI IRQ data once Instead of trying to parse DMI IRQ data every time we try to instantiate a device, let's do it once, when we identify the device we are working with. This allows us to mark chromeos_laptop_get_irq_from_dmi() as __init and discard it once module is initialized. Signed-off-by: Dmitry Torokhov Signed-off-by: Benson Leung --- drivers/platform/chrome/chromeos_laptop.c | 120 ++++++++++++---------- 1 file changed, 64 insertions(+), 56 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 479d813a9b57f..c91301ec40df3 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -85,26 +85,6 @@ struct chromeos_laptop { static struct chromeos_laptop *cros_laptop; -static int 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 i2c_client * chromes_laptop_instantiate_i2c_device(int bus, struct i2c_board_info *info, @@ -202,7 +182,6 @@ static int chromeos_laptop_add_peripheral(struct i2c_peripheral *i2c_dev) { struct i2c_client *client; int bus; - int irq; /* * Check that the i2c adapter is present. @@ -213,16 +192,6 @@ static int chromeos_laptop_add_peripheral(struct i2c_peripheral *i2c_dev) if (bus < 0) return bus == -ENODEV ? -EPROBE_DEFER : bus; - if (i2c_dev->dmi_name) { - irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name); - if (irq < 0) { - i2c_dev->state = FAILED; - return irq; - } - - i2c_dev->board_info.irq = irq; - } - client = chromes_laptop_instantiate_i2c_device(bus, &i2c_dev->board_info, i2c_dev->alt_addr); @@ -247,15 +216,6 @@ static int chromeos_laptop_add_peripheral(struct i2c_peripheral *i2c_dev) return 0; } -static int __init chromeos_laptop_dmi_matched(const struct dmi_system_id *id) -{ - cros_laptop = (void *)id->driver_data; - pr_debug("DMI Matched %s\n", id->ident); - - /* Indicate to dmi_scan that processing is done. */ - return 1; -} - static int chromeos_laptop_probe(struct platform_device *pdev) { struct i2c_peripheral *i2c_dev; @@ -516,10 +476,6 @@ static struct chromeos_laptop cr48 = { }, }; -#define _CBDD(board_) \ - .callback = chromeos_laptop_dmi_matched, \ - .driver_data = (void *)&board_ - static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { { .ident = "Samsung Series 5 550", @@ -527,14 +483,14 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"), DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"), }, - _CBDD(samsung_series_5_550), + .driver_data = (void *)&samsung_series_5_550, }, { .ident = "Samsung Series 5", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Alex"), }, - _CBDD(samsung_series_5), + .driver_data = (void *)&samsung_series_5, }, { .ident = "Chromebook Pixel", @@ -542,7 +498,7 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), DMI_MATCH(DMI_PRODUCT_NAME, "Link"), }, - _CBDD(chromebook_pixel), + .driver_data = (void *)&chromebook_pixel, }, { .ident = "Wolf", @@ -550,7 +506,7 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"), }, - _CBDD(dell_chromebook_11), + .driver_data = (void *)&dell_chromebook_11, }, { .ident = "HP Chromebook 14", @@ -558,7 +514,7 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), DMI_MATCH(DMI_PRODUCT_NAME, "Falco"), }, - _CBDD(hp_chromebook_14), + .driver_data = (void *)&hp_chromebook_14, }, { .ident = "Toshiba CB35", @@ -566,42 +522,42 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), DMI_MATCH(DMI_PRODUCT_NAME, "Leon"), }, - _CBDD(toshiba_cb35), + .driver_data = (void *)&toshiba_cb35, }, { .ident = "Acer C7 Chromebook", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"), }, - _CBDD(acer_c7_chromebook), + .driver_data = (void *)&acer_c7_chromebook, }, { .ident = "Acer AC700", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), }, - _CBDD(acer_ac700), + .driver_data = (void *)&acer_ac700, }, { .ident = "Acer C720", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"), }, - _CBDD(acer_c720), + .driver_data = (void *)&acer_c720, }, { .ident = "HP Pavilion 14 Chromebook", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"), }, - _CBDD(hp_pavilion_14_chromebook), + .driver_data = (void *)&hp_pavilion_14_chromebook, }, { .ident = "Cr-48", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Mario"), }, - _CBDD(cr48), + .driver_data = (void *)&cr48, }, { } }; @@ -616,15 +572,67 @@ static struct platform_driver cros_platform_driver = { .probe = chromeos_laptop_probe, }; +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); + } + + return cros_laptop; +} + + static int __init chromeos_laptop_init(void) { + const struct dmi_system_id *dmi_id; int ret; - if (!dmi_check_system(chromeos_laptop_dmi_table)) { + dmi_id = dmi_first_match(chromeos_laptop_dmi_table); + if (!dmi_id) { pr_debug("unsupported system\n"); return -ENODEV; } + 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); + ret = platform_driver_register(&cros_platform_driver); if (ret) return ret; From 07781cae252ae793ace9bf0a0e1378100b64af7c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 20 Mar 2018 15:31:34 -0700 Subject: [PATCH 06/29] platform/chrome: chromeos_laptop - use I2C notifier to create devices Instead of using platform device and deferrals to handle the case when i2C adapters appear late in the game, and not handling device unbinding all that well, let's switch to using I2C bus notifier to get told when a new I2C adapter appears in the system, and attempt to add appropriate devices at that time. In case when we have 2 Designware adapters in the system (Acer C720), instead of counting and hoping they get enumerate din the right order, let's switch to using their PCI devids (slot/function) that should be stable. Signed-off-by: Dmitry Torokhov Signed-off-by: Benson Leung --- drivers/platform/chrome/chromeos_laptop.c | 247 +++++++++------------- 1 file changed, 102 insertions(+), 145 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index c91301ec40df3..f5125b863fa0a 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #define ATMEL_TP_I2C_ADDR 0x4b @@ -40,14 +41,11 @@ #define ISL_ALS_I2C_ADDR 0x44 #define TAOS_ALS_I2C_ADDR 0x29 -#define MAX_I2C_DEVICE_DEFERRALS 5 - static const char *i2c_adapter_names[] = { "SMBus I801 adapter", "i915 gmbus vga", "i915 gmbus panel", "Synopsys DesignWare I2C adapter", - "Synopsys DesignWare I2C adapter", }; /* Keep this enum consistent with i2c_adapter_names */ @@ -55,15 +53,7 @@ enum i2c_adapter_type { I2C_ADAPTER_SMBUS = 0, I2C_ADAPTER_VGADDC, I2C_ADAPTER_PANEL, - I2C_ADAPTER_DESIGNWARE_0, - I2C_ADAPTER_DESIGNWARE_1, -}; - -enum i2c_peripheral_state { - UNPROBED = 0, - PROBED, - TIMEDOUT, - FAILED, + I2C_ADAPTER_DESIGNWARE, }; struct i2c_peripheral { @@ -71,10 +61,9 @@ struct i2c_peripheral { unsigned short alt_addr; const char *dmi_name; enum i2c_adapter_type type; + u32 pci_devid; - enum i2c_peripheral_state state; struct i2c_client *client; - int tries; }; #define MAX_I2C_PERIPHERALS 4 @@ -86,19 +75,12 @@ struct chromeos_laptop { static struct chromeos_laptop *cros_laptop; static struct i2c_client * -chromes_laptop_instantiate_i2c_device(int bus, +chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter, struct i2c_board_info *info, unsigned short alt_addr) { - struct i2c_adapter *adapter; - struct i2c_client *client = NULL; const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END }; - - adapter = i2c_get_adapter(bus); - if (!adapter) { - pr_err("failed to get i2c adapter %d\n", bus); - return NULL; - } + struct i2c_client *client; /* * Add the i2c device. If we can't detect it at the primary @@ -119,126 +101,103 @@ chromes_laptop_instantiate_i2c_device(int bus, alt_addr_list, NULL); if (dummy) { pr_debug("%d-%02x is probed at %02x\n", - bus, info->addr, dummy->addr); + adapter->nr, info->addr, dummy->addr); i2c_unregister_device(dummy); client = i2c_new_device(adapter, info); } } if (!client) - pr_notice("failed to register device %d-%02x\n", - bus, info->addr); + pr_debug("failed to register device %d-%02x\n", + adapter->nr, info->addr); else - pr_debug("added i2c device %d-%02x\n", bus, info->addr); + pr_debug("added i2c device %d-%02x\n", + adapter->nr, info->addr); - i2c_put_adapter(adapter); return client; } -struct i2c_lookup { - const char *name; - int instance; - int n; -}; - -static int __find_i2c_adap(struct device *dev, void *data) +static bool chromeos_laptop_match_adapter_devid(struct device *dev, u32 devid) { - struct i2c_lookup *lookup = data; - static const char *prefix = "i2c-"; - struct i2c_adapter *adapter; + struct pci_dev *pdev; - if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0) - return 0; - adapter = to_i2c_adapter(dev); - if (strncmp(adapter->name, lookup->name, strlen(lookup->name)) == 0 && - lookup->n++ == lookup->instance) - return 1; - return 0; -} + if (!dev_is_pci(dev)) + return false; -static int find_i2c_adapter_num(enum i2c_adapter_type type) -{ - struct device *dev = NULL; - struct i2c_adapter *adapter; - struct i2c_lookup lookup; - - memset(&lookup, 0, sizeof(lookup)); - lookup.name = i2c_adapter_names[type]; - lookup.instance = (type == I2C_ADAPTER_DESIGNWARE_1) ? 1 : 0; - - /* find the adapter by name */ - dev = bus_find_device(&i2c_bus_type, NULL, &lookup, __find_i2c_adap); - if (!dev) { - /* Adapters may appear later. Deferred probing will retry */ - pr_notice("i2c adapter %s not found on system.\n", - lookup.name); - return -ENODEV; - } - adapter = to_i2c_adapter(dev); - return adapter->nr; + pdev = to_pci_dev(dev); + return devid == PCI_DEVID(pdev->bus->number, pdev->devfn); } -static int chromeos_laptop_add_peripheral(struct i2c_peripheral *i2c_dev) +static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter) { - struct i2c_client *client; - int bus; + struct i2c_peripheral *i2c_dev; + int i; - /* - * Check that the i2c adapter is present. - * -EPROBE_DEFER if missing as the adapter may appear much - * later. - */ - bus = find_i2c_adapter_num(i2c_dev->type); - if (bus < 0) - return bus == -ENODEV ? -EPROBE_DEFER : bus; - - client = chromes_laptop_instantiate_i2c_device(bus, - &i2c_dev->board_info, - i2c_dev->alt_addr); - if (!client) { - /* - * Set -EPROBE_DEFER a limited num of times - * if device is not successfully added. - */ - if (++i2c_dev->tries < MAX_I2C_DEVICE_DEFERRALS) { - return -EPROBE_DEFER; - } else { - /* Ran out of tries. */ - pr_notice("ran out of tries for device.\n"); - i2c_dev->state = TIMEDOUT; - return -EIO; - } - } + for (i = 0; i < MAX_I2C_PERIPHERALS; i++) { + i2c_dev = &cros_laptop->i2c_peripherals[i]; - i2c_dev->client = client; - i2c_dev->state = PROBED; + /* No more peripherals */ + if (!i2c_dev->board_info.addr) + break; - return 0; + /* 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); + } } -static int chromeos_laptop_probe(struct platform_device *pdev) +static void chromeos_laptop_detach_i2c_client(struct i2c_client *client) { struct i2c_peripheral *i2c_dev; int i; - int ret = 0; for (i = 0; i < MAX_I2C_PERIPHERALS; i++) { i2c_dev = &cros_laptop->i2c_peripherals[i]; - /* No more peripherals. */ - if (!i2c_dev->board_info.addr) - break; - - if (i2c_dev->state != UNPROBED) - continue; + if (i2c_dev->client == client) + i2c_dev->client = NULL; + } +} - if (chromeos_laptop_add_peripheral(i2c_dev) == -EPROBE_DEFER) - ret = -EPROBE_DEFER; +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; } - return ret; + return 0; } +static struct notifier_block chromeos_laptop_i2c_notifier = { + .notifier_call = chromeos_laptop_i2c_notifier_call, +}; + static struct chromeos_laptop samsung_series_5_550 = { .i2c_peripherals = { /* Touchpad. */ @@ -339,7 +298,7 @@ static struct chromeos_laptop hp_chromebook_14 = { .flags = I2C_CLIENT_WAKE, }, .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE_0, + .type = I2C_ADAPTER_DESIGNWARE, }, }, }; @@ -353,7 +312,7 @@ static struct chromeos_laptop dell_chromebook_11 = { .flags = I2C_CLIENT_WAKE, }, .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE_0, + .type = I2C_ADAPTER_DESIGNWARE, }, /* Elan Touchpad option. */ { @@ -362,7 +321,7 @@ static struct chromeos_laptop dell_chromebook_11 = { .flags = I2C_CLIENT_WAKE, }, .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE_0, + .type = I2C_ADAPTER_DESIGNWARE, }, }, }; @@ -376,7 +335,7 @@ static struct chromeos_laptop toshiba_cb35 = { .flags = I2C_CLIENT_WAKE, }, .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE_0, + .type = I2C_ADAPTER_DESIGNWARE, }, }, }; @@ -418,7 +377,8 @@ static struct chromeos_laptop acer_c720 = { .flags = I2C_CLIENT_WAKE, }, .dmi_name = "touchscreen", - .type = I2C_ADAPTER_DESIGNWARE_1, + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)), .alt_addr = ATMEL_TS_I2C_BL_ADDR, }, /* Touchpad. */ @@ -428,7 +388,8 @@ static struct chromeos_laptop acer_c720 = { .flags = I2C_CLIENT_WAKE, }, .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE_0, + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)), }, /* Elan Touchpad option. */ { @@ -437,7 +398,8 @@ static struct chromeos_laptop acer_c720 = { .flags = I2C_CLIENT_WAKE, }, .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE_0, + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)), }, /* Light Sensor. */ { @@ -445,7 +407,8 @@ static struct chromeos_laptop acer_c720 = { I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), }, .dmi_name = "lightsensor", - .type = I2C_ADAPTER_DESIGNWARE_1, + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)), }, }, }; @@ -563,14 +526,16 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { }; MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table); -static struct platform_device *cros_platform_device; +static int __init chromeos_laptop_scan_adapter(struct device *dev, void *data) +{ + struct i2c_adapter *adapter; -static struct platform_driver cros_platform_driver = { - .driver = { - .name = "chromeos_laptop", - }, - .probe = chromeos_laptop_probe, -}; + adapter = i2c_verify_adapter(dev); + if (adapter) + chromeos_laptop_check_adapter(adapter); + + return 0; +} static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name) { @@ -619,7 +584,7 @@ chromeos_laptop_prepare(const struct dmi_system_id *id) static int __init chromeos_laptop_init(void) { const struct dmi_system_id *dmi_id; - int ret; + int error; dmi_id = dmi_first_match(chromeos_laptop_dmi_table); if (!dmi_id) { @@ -633,27 +598,20 @@ static int __init chromeos_laptop_init(void) if (IS_ERR(cros_laptop)) return PTR_ERR(cros_laptop); - ret = platform_driver_register(&cros_platform_driver); - if (ret) - return ret; - - cros_platform_device = platform_device_alloc("chromeos_laptop", -1); - if (!cros_platform_device) { - ret = -ENOMEM; - goto fail_platform_device1; + 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; } - ret = platform_device_add(cros_platform_device); - if (ret) - goto fail_platform_device2; + /* + * 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); return 0; - -fail_platform_device2: - platform_device_put(cros_platform_device); -fail_platform_device1: - platform_driver_unregister(&cros_platform_driver); - return ret; } static void __exit chromeos_laptop_exit(void) @@ -661,8 +619,7 @@ static void __exit chromeos_laptop_exit(void) struct i2c_peripheral *i2c_dev; int i; - platform_device_unregister(cros_platform_device); - platform_driver_unregister(&cros_platform_driver); + bus_unregister_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier); for (i = 0; i < MAX_I2C_PERIPHERALS; i++) { i2c_dev = &cros_laptop->i2c_peripherals[i]; @@ -671,7 +628,7 @@ static void __exit chromeos_laptop_exit(void) if (!i2c_dev->board_info.type) break; - if (i2c_dev->state == PROBED) + if (i2c_dev->client) i2c_unregister_device(i2c_dev->client); } } From 1b679928887499a58b4c43b9499d37f8d0b480d1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 20 Mar 2018 15:31:35 -0700 Subject: [PATCH 07/29] platform/chrome: chromeos_laptop - rely on I2C to set up interrupt trigger Instead of passing interrupt flags via platform data to drivers, or hoping that drivers will do the right thing and set it up the way we need, let's set up IRQ resource and attach it to the I2C board info, and let I2C core set it up for us. Signed-off-by: Dmitry Torokhov Signed-off-by: Benson Leung --- drivers/platform/chrome/chromeos_laptop.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index f5125b863fa0a..a03bd078f48ca 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -59,7 +60,11 @@ enum i2c_adapter_type { struct i2c_peripheral { struct i2c_board_info board_info; unsigned short alt_addr; + const char *dmi_name; + unsigned long irqflags; + struct resource irq_resource; + enum i2c_adapter_type type; u32 pci_devid; @@ -232,10 +237,6 @@ static struct chromeos_laptop samsung_series_5 = { }, }; -static struct mxt_platform_data atmel_1664s_platform_data = { - .irqflags = IRQF_TRIGGER_FALLING, -}; - static int chromebook_pixel_tp_keys[] = { KEY_RESERVED, KEY_RESERVED, @@ -246,7 +247,6 @@ static int chromebook_pixel_tp_keys[] = { }; static struct mxt_platform_data chromebook_pixel_tp_platform_data = { - .irqflags = IRQF_TRIGGER_FALLING, .t19_num_keys = ARRAY_SIZE(chromebook_pixel_tp_keys), .t19_keymap = chromebook_pixel_tp_keys, }; @@ -258,10 +258,10 @@ static struct chromeos_laptop chromebook_pixel = { .board_info = { I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), - .platform_data = &atmel_1664s_platform_data, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "touchscreen", + .irqflags = IRQF_TRIGGER_FALLING, .type = I2C_ADAPTER_PANEL, .alt_addr = ATMEL_TS_I2C_BL_ADDR, }, @@ -275,6 +275,7 @@ static struct chromeos_laptop chromebook_pixel = { .flags = I2C_CLIENT_WAKE, }, .dmi_name = "trackpad", + .irqflags = IRQF_TRIGGER_FALLING, .type = I2C_ADAPTER_VGADDC, .alt_addr = ATMEL_TP_I2C_BL_ADDR, }, @@ -373,10 +374,10 @@ static struct chromeos_laptop acer_c720 = { .board_info = { I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), - .platform_data = &atmel_1664s_platform_data, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "touchscreen", + .irqflags = IRQF_TRIGGER_FALLING, .type = I2C_ADAPTER_DESIGNWARE, .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)), .alt_addr = ATMEL_TS_I2C_BL_ADDR, @@ -575,6 +576,12 @@ chromeos_laptop_prepare(const struct dmi_system_id *id) irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name); if (irq < 0) return ERR_PTR(irq); + + 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; } return cros_laptop; From 00c49798250ba76eb8ed2871bf55fde2e88b4ab3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 20 Mar 2018 15:31:36 -0700 Subject: [PATCH 08/29] platform/chrome: chromeos_laptop - use device properties for Pixel Now that Atmel driver uses generic device properties we can use them instead of platform data when setting up touchpad on the original Google Pixel. Signed-off-by: Dmitry Torokhov Signed-off-by: Benson Leung --- drivers/platform/chrome/chromeos_laptop.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index a03bd078f48ca..bfc4526e8bce5 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -25,13 +25,13 @@ #include #include -#include #include #include #include #include #include #include +#include #define ATMEL_TP_I2C_ADDR 0x4b #define ATMEL_TP_I2C_BL_ADDR 0x25 @@ -246,9 +246,9 @@ static int chromebook_pixel_tp_keys[] = { 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, +static const struct property_entry chromebook_pixel_trackpad_props[] = { + PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_pixel_tp_keys), + { } }; static struct chromeos_laptop chromebook_pixel = { @@ -270,8 +270,8 @@ static struct chromeos_laptop chromebook_pixel = { .board_info = { I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR), - .platform_data = - &chromebook_pixel_tp_platform_data, + .properties = + chromebook_pixel_trackpad_props, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "trackpad", From 663de6dd4a78dee60462d2e8333f67229348723b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 20 Mar 2018 15:31:37 -0700 Subject: [PATCH 09/29] platform/chrome: chromeos_laptop - discard data for unneeded boards Mark board data as __intconst/__initdata and make a copy of appropriate entry once we identified the board we are running on. The rest of the data will be discarded once the kernel finished booting (or module finished loading). Signed-off-by: Dmitry Torokhov Signed-off-by: Benson Leung --- drivers/platform/chrome/chromeos_laptop.c | 476 ++++++++++++---------- 1 file changed, 264 insertions(+), 212 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index bfc4526e8bce5..fda9e09283c87 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -71,13 +71,16 @@ struct i2c_peripheral { struct i2c_client *client; }; -#define MAX_I2C_PERIPHERALS 4 - struct chromeos_laptop { - struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS]; + /* + * Note that we can't mark this pointer as const because + * i2c_new_probed_device() changes passed in I2C board info, so. + */ + struct i2c_peripheral *i2c_peripherals; + unsigned int num_i2c_peripherals; }; -static struct chromeos_laptop *cros_laptop; +static const struct chromeos_laptop *cros_laptop; static struct i2c_client * chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter, @@ -138,13 +141,9 @@ static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter) struct i2c_peripheral *i2c_dev; int i; - for (i = 0; i < MAX_I2C_PERIPHERALS; i++) { + for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { i2c_dev = &cros_laptop->i2c_peripherals[i]; - /* No more peripherals */ - if (!i2c_dev->board_info.addr) - break; - /* Skip devices already created */ if (i2c_dev->client) continue; @@ -171,7 +170,7 @@ static void chromeos_laptop_detach_i2c_client(struct i2c_client *client) struct i2c_peripheral *i2c_dev; int i; - for (i = 0; i < MAX_I2C_PERIPHERALS; i++) { + for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { i2c_dev = &cros_laptop->i2c_peripherals[i]; if (i2c_dev->client == client) @@ -203,41 +202,45 @@ static struct notifier_block chromeos_laptop_i2c_notifier = { .notifier_call = chromeos_laptop_i2c_notifier_call, }; -static struct chromeos_laptop samsung_series_5_550 = { - .i2c_peripherals = { - /* Touchpad. */ - { - .board_info = { - I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "trackpad", - .type = I2C_ADAPTER_SMBUS, +#define DECLARE_CROS_LAPTOP(_name) \ +static const struct chromeos_laptop _name __initconst = { \ + .i2c_peripherals = _name##_peripherals, \ + .num_i2c_peripherals = ARRAY_SIZE(_name##_peripherals), \ +} + +static struct i2c_peripheral samsung_series_5_550_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, }, - /* Light Sensor. */ - { - .board_info = { - I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), - }, - .dmi_name = "lightsensor", - .type = I2C_ADAPTER_SMBUS, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_SMBUS, + }, + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), }, + .dmi_name = "lightsensor", + .type = I2C_ADAPTER_SMBUS, }, }; +DECLARE_CROS_LAPTOP(samsung_series_5_550); -static struct chromeos_laptop samsung_series_5 = { - .i2c_peripherals = { - /* Light Sensor. */ - { - .board_info = { - I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), - }, - .type = I2C_ADAPTER_SMBUS, +static struct i2c_peripheral samsung_series_5_peripherals[] __initdata = { + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), }, + .type = I2C_ADAPTER_SMBUS, }, }; +DECLARE_CROS_LAPTOP(samsung_series_5); -static int chromebook_pixel_tp_keys[] = { +static const int chromebook_pixel_tp_keys[] __initconst = { KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, @@ -246,199 +249,192 @@ static int chromebook_pixel_tp_keys[] = { BTN_LEFT }; -static const struct property_entry chromebook_pixel_trackpad_props[] = { +static const struct property_entry +chromebook_pixel_trackpad_props[] __initconst = { PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_pixel_tp_keys), { } }; -static struct chromeos_laptop chromebook_pixel = { - .i2c_peripherals = { - /* Touch Screen. */ - { - .board_info = { - I2C_BOARD_INFO("atmel_mxt_ts", - ATMEL_TS_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "touchscreen", - .irqflags = IRQF_TRIGGER_FALLING, - .type = I2C_ADAPTER_PANEL, - .alt_addr = ATMEL_TS_I2C_BL_ADDR, +static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = { + /* Touch Screen. */ + { + .board_info = { + I2C_BOARD_INFO("atmel_mxt_ts", + ATMEL_TS_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, }, - /* Touchpad. */ - { - .board_info = { - I2C_BOARD_INFO("atmel_mxt_tp", - ATMEL_TP_I2C_ADDR), - .properties = - chromebook_pixel_trackpad_props, - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "trackpad", - .irqflags = IRQF_TRIGGER_FALLING, - .type = I2C_ADAPTER_VGADDC, - .alt_addr = ATMEL_TP_I2C_BL_ADDR, + .dmi_name = "touchscreen", + .irqflags = IRQF_TRIGGER_FALLING, + .type = I2C_ADAPTER_PANEL, + .alt_addr = ATMEL_TS_I2C_BL_ADDR, + }, + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("atmel_mxt_tp", + ATMEL_TP_I2C_ADDR), + .properties = + chromebook_pixel_trackpad_props, + .flags = I2C_CLIENT_WAKE, }, - /* Light Sensor. */ - { - .board_info = { - I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), - }, - .dmi_name = "lightsensor", - .type = I2C_ADAPTER_PANEL, + .dmi_name = "trackpad", + .irqflags = IRQF_TRIGGER_FALLING, + .type = I2C_ADAPTER_VGADDC, + .alt_addr = ATMEL_TP_I2C_BL_ADDR, + }, + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), }, + .dmi_name = "lightsensor", + .type = I2C_ADAPTER_PANEL, }, }; +DECLARE_CROS_LAPTOP(chromebook_pixel); -static struct chromeos_laptop hp_chromebook_14 = { - .i2c_peripherals = { - /* Touchpad. */ - { - .board_info = { - I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE, +static struct i2c_peripheral hp_chromebook_14_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, }, }; +DECLARE_CROS_LAPTOP(hp_chromebook_14); -static struct chromeos_laptop dell_chromebook_11 = { - .i2c_peripherals = { - /* Touchpad. */ - { - .board_info = { - I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE, +static struct i2c_peripheral dell_chromebook_11_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, }, - /* Elan Touchpad option. */ - { - .board_info = { - I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, + }, + /* Elan Touchpad option. */ + { + .board_info = { + I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, }, }; +DECLARE_CROS_LAPTOP(dell_chromebook_11); -static struct chromeos_laptop toshiba_cb35 = { - .i2c_peripherals = { - /* Touchpad. */ - { - .board_info = { - I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE, +static struct i2c_peripheral toshiba_cb35_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, }, }; +DECLARE_CROS_LAPTOP(toshiba_cb35); -static struct chromeos_laptop acer_c7_chromebook = { - .i2c_peripherals = { - /* Touchpad. */ - { - .board_info = { - I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "trackpad", - .type = I2C_ADAPTER_SMBUS, +static struct i2c_peripheral acer_c7_chromebook_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_SMBUS, }, }; +DECLARE_CROS_LAPTOP(acer_c7_chromebook); -static struct chromeos_laptop acer_ac700 = { - .i2c_peripherals = { - /* Light Sensor. */ - { - .board_info = { - I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), - }, - .type = I2C_ADAPTER_SMBUS, +static struct i2c_peripheral acer_ac700_peripherals[] __initdata = { + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), }, + .type = I2C_ADAPTER_SMBUS, }, }; +DECLARE_CROS_LAPTOP(acer_ac700); -static struct chromeos_laptop acer_c720 = { - .i2c_peripherals = { - /* Touchscreen. */ - { - .board_info = { - I2C_BOARD_INFO("atmel_mxt_ts", - ATMEL_TS_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "touchscreen", - .irqflags = IRQF_TRIGGER_FALLING, - .type = I2C_ADAPTER_DESIGNWARE, - .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)), - .alt_addr = ATMEL_TS_I2C_BL_ADDR, +static struct i2c_peripheral acer_c720_peripherals[] __initdata = { + /* Touchscreen. */ + { + .board_info = { + I2C_BOARD_INFO("atmel_mxt_ts", + ATMEL_TS_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, }, - /* Touchpad. */ - { - .board_info = { - I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE, - .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)), + .dmi_name = "touchscreen", + .irqflags = IRQF_TRIGGER_FALLING, + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)), + .alt_addr = ATMEL_TS_I2C_BL_ADDR, + }, + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, }, - /* Elan Touchpad option. */ - { - .board_info = { - I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "trackpad", - .type = I2C_ADAPTER_DESIGNWARE, - .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)), + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)), + }, + /* Elan Touchpad option. */ + { + .board_info = { + I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, }, - /* Light Sensor. */ - { - .board_info = { - I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), - }, - .dmi_name = "lightsensor", - .type = I2C_ADAPTER_DESIGNWARE, - .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)), + .dmi_name = "trackpad", + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)), + }, + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), }, + .dmi_name = "lightsensor", + .type = I2C_ADAPTER_DESIGNWARE, + .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)), }, }; +DECLARE_CROS_LAPTOP(acer_c720); -static struct chromeos_laptop hp_pavilion_14_chromebook = { - .i2c_peripherals = { - /* Touchpad. */ - { - .board_info = { - I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), - .flags = I2C_CLIENT_WAKE, - }, - .dmi_name = "trackpad", - .type = I2C_ADAPTER_SMBUS, +static struct i2c_peripheral +hp_pavilion_14_chromebook_peripherals[] __initdata = { + /* Touchpad. */ + { + .board_info = { + I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, }, + .dmi_name = "trackpad", + .type = I2C_ADAPTER_SMBUS, }, }; +DECLARE_CROS_LAPTOP(hp_pavilion_14_chromebook); -static struct chromeos_laptop cr48 = { - .i2c_peripherals = { - /* Light Sensor. */ - { - .board_info = { - I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), - }, - .type = I2C_ADAPTER_SMBUS, +static struct i2c_peripheral cr48_peripherals[] __initdata = { + /* Light Sensor. */ + { + .board_info = { + I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), }, + .type = I2C_ADAPTER_SMBUS, }, }; +DECLARE_CROS_LAPTOP(cr48); static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { { @@ -558,24 +554,14 @@ static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name) return dev_data->instance; } -static struct chromeos_laptop * __init -chromeos_laptop_prepare(const struct dmi_system_id *id) +static int __init chromeos_laptop_setup_irq(struct i2c_peripheral *i2c_dev) { - 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; + if (i2c_dev->dmi_name) { irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name); if (irq < 0) - return ERR_PTR(irq); + return irq; i2c_dev->irq_resource = (struct resource) DEFINE_RES_NAMED(irq, 1, NULL, @@ -584,9 +570,87 @@ chromeos_laptop_prepare(const struct dmi_system_id *id) i2c_dev->board_info.num_resources = 1; } + return 0; +} + +static struct chromeos_laptop * __init +chromeos_laptop_prepare(const struct chromeos_laptop *src) +{ + struct chromeos_laptop *cros_laptop; + struct i2c_peripheral *i2c_dev; + struct i2c_board_info *info; + int error; + int i; + + cros_laptop = kzalloc(sizeof(*cros_laptop), GFP_KERNEL); + if (!cros_laptop) + return ERR_PTR(-ENOMEM); + + cros_laptop->i2c_peripherals = kmemdup(src->i2c_peripherals, + src->num_i2c_peripherals * + sizeof(*src->i2c_peripherals), + GFP_KERNEL); + if (!cros_laptop->i2c_peripherals) { + error = -ENOMEM; + goto err_free_cros_laptop; + } + + cros_laptop->num_i2c_peripherals = src->num_i2c_peripherals; + + for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { + i2c_dev = &cros_laptop->i2c_peripherals[i]; + info = &i2c_dev->board_info; + + error = chromeos_laptop_setup_irq(i2c_dev); + if (error) + goto err_destroy_cros_peripherals; + + /* We need to deep-copy properties */ + if (info->properties) { + info->properties = + property_entries_dup(info->properties); + if (IS_ERR(info->properties)) { + error = PTR_ERR(info->properties); + goto err_destroy_cros_peripherals; + } + } + } + return cros_laptop; + +err_destroy_cros_peripherals: + while (--i >= 0) { + i2c_dev = &cros_laptop->i2c_peripherals[i]; + info = &i2c_dev->board_info; + if (info->properties) + property_entries_free(info->properties); + } + kfree(cros_laptop->i2c_peripherals); +err_free_cros_laptop: + kfree(cros_laptop); + return ERR_PTR(error); } +static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop) +{ + struct i2c_peripheral *i2c_dev; + struct i2c_board_info *info; + int i; + + for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { + i2c_dev = &cros_laptop->i2c_peripherals[i]; + info = &i2c_dev->board_info; + + if (i2c_dev->client) + i2c_unregister_device(i2c_dev->client); + + if (info->properties) + property_entries_free(info->properties); + } + + kfree(cros_laptop->i2c_peripherals); + kfree(cros_laptop); +} static int __init chromeos_laptop_init(void) { @@ -601,7 +665,7 @@ static int __init chromeos_laptop_init(void) pr_debug("DMI Matched %s\n", dmi_id->ident); - cros_laptop = chromeos_laptop_prepare(dmi_id->driver_data); + cros_laptop = chromeos_laptop_prepare((void *)dmi_id->driver_data); if (IS_ERR(cros_laptop)) return PTR_ERR(cros_laptop); @@ -609,6 +673,7 @@ static int __init chromeos_laptop_init(void) &chromeos_laptop_i2c_notifier); if (error) { pr_err("failed to register i2c bus notifier: %d\n", error); + chromeos_laptop_destroy(cros_laptop); return error; } @@ -623,21 +688,8 @@ static int __init chromeos_laptop_init(void) static void __exit chromeos_laptop_exit(void) { - struct i2c_peripheral *i2c_dev; - int i; - bus_unregister_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier); - - 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; - - if (i2c_dev->client) - i2c_unregister_device(i2c_dev->client); - } + chromeos_laptop_destroy(cros_laptop); } module_init(chromeos_laptop_init); From fbf60df25d2564689d82dd714f008f0e2e406a0f Mon Sep 17 00:00:00 2001 From: Wenkai Du Date: Thu, 22 Feb 2018 13:21:17 -0800 Subject: [PATCH 10/29] platform/chrome: cros_ec_lpc: wake up from s2idle on Chrome EC Chrome platform installed a Chrome EC notify handler which prevents default EC GPE handler getting called. Add pm_system_wakeup to the Chrome EC notify handler so wake up from s2idle can happen. Suggested-by: Rafael J. Wysocki Signed-off-by: Wenkai Du Signed-off-by: Benson Leung --- drivers/platform/chrome/cros_ec_lpc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index af89e82eecd23..2a40c2b1a7ff2 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -31,6 +31,7 @@ #include #include #include +#include #define DRV_NAME "cros_ec_lpcs" #define ACPI_DRV_NAME "GOOG0004" @@ -235,6 +236,9 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data) cros_ec_get_next_event(ec_dev, NULL) > 0) blocking_notifier_call_chain(&ec_dev->event_notifier, 0, ec_dev); + + if (value == ACPI_NOTIFY_DEVICE_WAKE) + pm_system_wakeup(); } static int cros_ec_lpc_probe(struct platform_device *pdev) From 77d3e62bccc49f4081cf9969c53ad12949dfef79 Mon Sep 17 00:00:00 2001 From: Salvatore Bellizzi Date: Wed, 7 Mar 2018 14:56:43 +0100 Subject: [PATCH 11/29] platform/chrome: cros_ec_lpc: Add support for Google devices using custom coreboot firmware This patch adds generic device information to the DMI table of the cros_ec_lpc driver, needed for Chromebooks/boxes using a custom coreboot firmware. The DMI info would not contain "Google_*" as BIOS version string, instead the system vendor string would still be "GOOGLE", so this seems to be a reasonable match for every Chromebook/box running a custom firmware. Signed-off-by: Salvatore Bellizzi Signed-off-by: Vittorio Gambaletta Signed-off-by: Benson Leung --- drivers/platform/chrome/cros_ec_lpc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 2a40c2b1a7ff2..3682e15392513 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -345,6 +345,18 @@ static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = { DMI_MATCH(DMI_BIOS_VERSION, "Google_"), }, }, + { + /* + * If the box is running custom coreboot firmware then the + * DMI BIOS version string will not be matched by "Google_", + * but the system vendor string will still be matched by + * "GOOGLE". + */ + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), + }, + }, { /* x86-link, the Chromebook Pixel. */ .matches = { From 2a8331bc2a632e3b33d90d4036e36f53b1d4b931 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Fri, 23 Mar 2018 18:42:42 +0100 Subject: [PATCH 12/29] platform/chrome: cros_ec_sysfs: Modify error handling When accessing a sysfs attribute, if the EC command fails, -EPROTO is now returned instead of an error message as it is unlikely an app is parsing the error message to do something meaningful. Also, this patch makes use of cros_ec_cmd_xfer_status() instead of cros_ec_cmd_xfer() so an error message is printed in the syslog. Signed-off-by: Gwendal Grignou Signed-off-by: Enric Balletbo i Serra Signed-off-by: Benson Leung --- drivers/platform/chrome/cros_ec_sysfs.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index da0a719d32f75..c03621e523a3e 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -114,15 +114,9 @@ static ssize_t store_ec_reboot(struct device *dev, msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset; msg->outsize = sizeof(*param); msg->insize = 0; - ret = cros_ec_cmd_xfer(ec->ec_dev, msg); - if (ret < 0) { + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + if (ret < 0) count = ret; - goto exit; - } - if (msg->result != EC_RES_SUCCESS) { - dev_dbg(ec->dev, "EC result %d\n", msg->result); - count = -EINVAL; - } exit: kfree(msg); return count; @@ -150,17 +144,11 @@ static ssize_t show_ec_version(struct device *dev, msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; msg->insize = sizeof(*r_ver); msg->outsize = 0; - ret = cros_ec_cmd_xfer(ec->ec_dev, msg); + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); if (ret < 0) { count = ret; goto exit; } - if (msg->result != EC_RES_SUCCESS) { - count = scnprintf(buf, PAGE_SIZE, - "ERROR: EC returned %d\n", msg->result); - goto exit; - } - r_ver = (struct ec_response_get_version *)msg->data; /* Strings should be null-terminated, but let's be sure. */ r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0'; @@ -255,14 +243,9 @@ static ssize_t show_ec_flashinfo(struct device *dev, msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset; msg->insize = sizeof(*resp); msg->outsize = 0; - ret = cros_ec_cmd_xfer(ec->ec_dev, msg); + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); if (ret < 0) goto exit; - if (msg->result != EC_RES_SUCCESS) { - ret = scnprintf(buf, PAGE_SIZE, - "ERROR: EC returned %d\n", msg->result); - goto exit; - } resp = (struct ec_response_flash_info *)msg->data; From c09fb8f011b59006e3c8799feb144cf85b51abf7 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Fri, 23 Mar 2018 18:42:43 +0100 Subject: [PATCH 13/29] platform/chrome: cros_ec_sysfs: introduce to_cros_ec_dev define. Add a define to get the cros_ec_dev from device and use it. Suggested-by: Andy Shevchenko Signed-off-by: Enric Balletbo i Serra Reviewed-by: Andy Shevchenko Signed-off-by: Benson Leung --- drivers/platform/chrome/cros_ec_sysfs.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index c03621e523a3e..85bb9580439eb 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -34,6 +34,8 @@ #include #include +#define to_cros_ec_dev(dev) container_of(dev, struct cros_ec_dev, class_dev) + /* Accessor functions */ static ssize_t show_ec_reboot(struct device *dev, @@ -70,8 +72,7 @@ static ssize_t store_ec_reboot(struct device *dev, int got_cmd = 0, offset = 0; int i; int ret; - struct cros_ec_dev *ec = container_of(dev, - struct cros_ec_dev, class_dev); + struct cros_ec_dev *ec = to_cros_ec_dev(dev); msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL); if (!msg) @@ -132,8 +133,7 @@ static ssize_t show_ec_version(struct device *dev, struct cros_ec_command *msg; int ret; int count = 0; - struct cros_ec_dev *ec = container_of(dev, - struct cros_ec_dev, class_dev); + struct cros_ec_dev *ec = to_cros_ec_dev(dev); msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); if (!msg) @@ -231,8 +231,7 @@ static ssize_t show_ec_flashinfo(struct device *dev, struct ec_response_flash_info *resp; struct cros_ec_command *msg; int ret; - struct cros_ec_dev *ec = container_of(dev, - struct cros_ec_dev, class_dev); + struct cros_ec_dev *ec = to_cros_ec_dev(dev); msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); if (!msg) From 0c4f2367e5da79a602af17b690918b8d46398c25 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Fri, 23 Mar 2018 18:42:44 +0100 Subject: [PATCH 14/29] platform/chrome: cros_ec_sysfs: use permission-specific DEVICE_ATTR variants Use DEVICE_ATTR variants for read/write attributes. This simplifies the source code, improves readbility, and reduces the chance of inconsistencies. Suggested-by: Andy Shevchenko Signed-off-by: Enric Balletbo i Serra Reviewed-by: Andy Shevchenko Signed-off-by: Benson Leung --- drivers/platform/chrome/cros_ec_sysfs.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index 85bb9580439eb..78ae0d3760e49 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -38,8 +38,8 @@ /* Accessor functions */ -static ssize_t show_ec_reboot(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t reboot_show(struct device *dev, + struct device_attribute *attr, char *buf) { int count = 0; @@ -50,9 +50,9 @@ static ssize_t show_ec_reboot(struct device *dev, return count; } -static ssize_t store_ec_reboot(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t reboot_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { static const struct { const char * const str; @@ -123,8 +123,8 @@ static ssize_t store_ec_reboot(struct device *dev, return count; } -static ssize_t show_ec_version(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t version_show(struct device *dev, + struct device_attribute *attr, char *buf) { static const char * const image_names[] = {"unknown", "RO", "RW"}; struct ec_response_get_version *r_ver; @@ -225,8 +225,8 @@ static ssize_t show_ec_version(struct device *dev, return count; } -static ssize_t show_ec_flashinfo(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t flashinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct ec_response_flash_info *resp; struct cros_ec_command *msg; @@ -260,9 +260,9 @@ static ssize_t show_ec_flashinfo(struct device *dev, /* Module initialization */ -static DEVICE_ATTR(reboot, S_IWUSR | S_IRUGO, show_ec_reboot, store_ec_reboot); -static DEVICE_ATTR(version, S_IRUGO, show_ec_version, NULL); -static DEVICE_ATTR(flashinfo, S_IRUGO, show_ec_flashinfo, NULL); +static DEVICE_ATTR_RW(reboot); +static DEVICE_ATTR_RO(version); +static DEVICE_ATTR_RO(flashinfo); static struct attribute *__ec_attrs[] = { &dev_attr_reboot.attr, From 70a4c69866515cc63e338d87c0a5c2a42e6fe073 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Fri, 23 Mar 2018 18:42:45 +0100 Subject: [PATCH 15/29] platform/chrome: cros_ec_debugfs: Use octal permissions '0444' Fixed the following checkpatch warning: WARNING: Symbolic permissions 'S_IRUGO' are not preferred. Consider using octal permissions '0444'. Signed-off-by: Enric Balletbo i Serra Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Benson Leung --- drivers/platform/chrome/cros_ec_debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index 0e88e18362c10..5394030225682 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -288,7 +288,7 @@ static int cros_ec_create_console_log(struct cros_ec_debugfs *debug_info) init_waitqueue_head(&debug_info->log_wq); if (!debugfs_create_file("console_log", - S_IFREG | S_IRUGO, + S_IFREG | 0444, debug_info->dir, debug_info, &cros_ec_console_log_fops)) @@ -341,7 +341,7 @@ static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info) debug_info->panicinfo_blob.size = ret; if (!debugfs_create_blob("panicinfo", - S_IFREG | S_IRUGO, + S_IFREG | 0444, debug_info->dir, &debug_info->panicinfo_blob)) { ret = -ENOMEM; From 975efde885a04cc50e65f5ee4c973c6ef4243fed Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Fri, 23 Mar 2018 18:42:47 +0100 Subject: [PATCH 16/29] platform/chrome: mfd/cros_ec_dev: Add sysfs entry to set keyboard wake lid angle This adds a sysfs attribute (/sys/class/chromeos/cros_ec/kb_wake_angle) used to set and get the keyboard wake lid angle. This attribute is present only if 2 accelerometers are controlled by the EC. This patch also moves the cros_ec features check before the device is added so the features map obtained from the EC is ready on time. Signed-off-by: Gwendal Grignou Signed-off-by: Enric Balletbo i Serra Reviewed-by: Andy Shevchenko Acked-by: Lee Jones Signed-off-by: Benson Leung --- drivers/mfd/cros_ec_dev.c | 31 ++++------ drivers/platform/chrome/cros_ec_sysfs.c | 81 +++++++++++++++++++++++++ include/linux/mfd/cros_ec.h | 2 + 3 files changed, 96 insertions(+), 18 deletions(-) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index e4fafdd96e5eb..eafd06f62a3af 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -305,8 +305,8 @@ static void cros_ec_sensors_register(struct cros_ec_dev *ec) resp = (struct ec_response_motion_sense *)msg->data; sensor_num = resp->dump.sensor_count; - /* Allocate 2 extra sensors in case lid angle or FIFO are needed */ - sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2), + /* Allocate 1 extra sensors in FIFO are needed */ + sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 1), GFP_KERNEL); if (sensor_cells == NULL) goto error; @@ -362,16 +362,10 @@ static void cros_ec_sensors_register(struct cros_ec_dev *ec) sensor_type[resp->info.type]++; id++; } - if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) { - sensor_platforms[id].sensor_num = sensor_num; - sensor_cells[id].name = "cros-ec-angle"; - sensor_cells[id].id = 0; - sensor_cells[id].platform_data = &sensor_platforms[id]; - sensor_cells[id].pdata_size = - sizeof(struct cros_ec_sensor_platform); - id++; - } + if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) + ec->has_kb_wake_angle = true; + if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) { sensor_cells[id].name = "cros-ec-ring"; id++; @@ -424,6 +418,14 @@ static int ec_device_probe(struct platform_device *pdev) goto failed; } + /* check whether this EC is a sensor hub. */ + if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) + cros_ec_sensors_register(ec); + + /* Take control of the lightbar from the EC. */ + lb_manual_suspend_ctrl(ec, 1); + + /* We can now add the sysfs class, we know which parameter to show */ retval = cdev_device_add(&ec->cdev, &ec->class_dev); if (retval) { dev_err(dev, "cdev_device_add failed => %d\n", retval); @@ -433,13 +435,6 @@ static int ec_device_probe(struct platform_device *pdev) if (cros_ec_debugfs_init(ec)) dev_warn(dev, "failed to create debugfs directory\n"); - /* check whether this EC is a sensor hub. */ - if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) - cros_ec_sensors_register(ec); - - /* Take control of the lightbar from the EC. */ - lb_manual_suspend_ctrl(ec, 1); - return 0; failed: diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index 78ae0d3760e49..5a6db3fe213ad 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -258,21 +258,102 @@ static ssize_t flashinfo_show(struct device *dev, return ret; } +/* Keyboard wake angle control */ +static ssize_t kb_wake_angle_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cros_ec_dev *ec = to_cros_ec_dev(dev); + struct ec_response_motion_sense *resp; + struct ec_params_motion_sense *param; + struct cros_ec_command *msg; + int ret; + + msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + param = (struct ec_params_motion_sense *)msg->data; + msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; + msg->version = 2; + param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE; + param->kb_wake_angle.data = EC_MOTION_SENSE_NO_VALUE; + msg->outsize = sizeof(*param); + msg->insize = sizeof(*resp); + + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + if (ret < 0) + goto exit; + + resp = (struct ec_response_motion_sense *)msg->data; + ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->kb_wake_angle.ret); +exit: + kfree(msg); + return ret; +} + +static ssize_t kb_wake_angle_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cros_ec_dev *ec = to_cros_ec_dev(dev); + struct ec_params_motion_sense *param; + struct cros_ec_command *msg; + u16 angle; + int ret; + + ret = kstrtou16(buf, 0, &angle); + if (ret) + return ret; + + msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + param = (struct ec_params_motion_sense *)msg->data; + msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; + msg->version = 2; + param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE; + param->kb_wake_angle.data = angle; + msg->outsize = sizeof(*param); + msg->insize = sizeof(struct ec_response_motion_sense); + + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + kfree(msg); + if (ret < 0) + return ret; + return count; +} + /* Module initialization */ static DEVICE_ATTR_RW(reboot); static DEVICE_ATTR_RO(version); static DEVICE_ATTR_RO(flashinfo); +static DEVICE_ATTR_RW(kb_wake_angle); static struct attribute *__ec_attrs[] = { + &dev_attr_kb_wake_angle.attr, &dev_attr_reboot.attr, &dev_attr_version.attr, &dev_attr_flashinfo.attr, NULL, }; +static umode_t cros_ec_ctrl_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct cros_ec_dev *ec = to_cros_ec_dev(dev); + + if (a == &dev_attr_kb_wake_angle.attr && !ec->has_kb_wake_angle) + return 0; + + return a->mode; +} + struct attribute_group cros_ec_attr_group = { .attrs = __ec_attrs, + .is_visible = cros_ec_ctrl_visible, }; EXPORT_SYMBOL(cros_ec_attr_group); diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index c61535979b8fc..2d4e23c9ea0af 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -183,6 +183,7 @@ struct cros_ec_debugfs; * @ec_dev: cros_ec_device structure to talk to the physical device * @dev: pointer to the platform device * @debug_info: cros_ec_debugfs structure for debugging information + * @has_kb_wake_angle: true if at least 2 accelerometer are connected to the EC. * @cmd_offset: offset to apply for each command. */ struct cros_ec_dev { @@ -191,6 +192,7 @@ struct cros_ec_dev { struct cros_ec_device *ec_dev; struct device *dev; struct cros_ec_debugfs *debug_info; + bool has_kb_wake_angle; u16 cmd_offset; u32 features[2]; }; From 360ca3d469c35bef1e94133ed015c628b2440563 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 22 May 2018 16:08:41 -0700 Subject: [PATCH 17/29] platform/chrome: cros_ec_lpc: do not try DMI match when ACPI device found Older models of Chromebooks did not describe the LPC EC in their ACPI tables; starting with Strago-based devices Google is using GOOG0004 device to describe EC LPC. DMI-based match is fragile and does not work reliably, especially when using custom firmware. It is also not needed when we can locate the right ACPI device, so let's stop bailing out when DMI does not match but the right ACPI device is present. Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Benson Leung --- drivers/platform/chrome/cros_ec_lpc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 3682e15392513..31c8b8c49e458 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -435,7 +435,13 @@ static int __init cros_ec_lpc_init(void) int ret; acpi_status status; - if (!dmi_check_system(cros_ec_lpc_dmi_table)) { + status = acpi_get_devices(ACPI_DRV_NAME, cros_ec_lpc_parse_device, + &cros_ec_lpc_acpi_device_found, NULL); + if (ACPI_FAILURE(status)) + pr_warn(DRV_NAME ": Looking for %s failed\n", ACPI_DRV_NAME); + + if (!cros_ec_lpc_acpi_device_found && + !dmi_check_system(cros_ec_lpc_dmi_table)) { pr_err(DRV_NAME ": unsupported system.\n"); return -ENODEV; } @@ -450,11 +456,6 @@ static int __init cros_ec_lpc_init(void) return ret; } - status = acpi_get_devices(ACPI_DRV_NAME, cros_ec_lpc_parse_device, - &cros_ec_lpc_acpi_device_found, NULL); - if (ACPI_FAILURE(status)) - pr_warn(DRV_NAME ": Looking for %s failed\n", ACPI_DRV_NAME); - if (!cros_ec_lpc_acpi_device_found) { /* Register the device, and it'll get hooked up automatically */ ret = platform_device_register(&cros_ec_lpc_device); From 2d3128881398ec9edad45d89252f6902180390ba Mon Sep 17 00:00:00 2001 From: Wenkai Du Date: Thu, 22 Feb 2018 13:30:52 -0800 Subject: [PATCH 18/29] Revert "mfd: cros_ec: Add ACPI GPE handler for LID0 devices" This reverts commit e04653a9dcf4d98defe2149c885382e5cc72082f. It is no longer needed to install Chrome EC GPE handler to have GPE enabled in suspend to idle path. It is found that with this handler installed, EC wake up doesn't work because default EC event handler that can wake up system is not getting called. Signed-off-by: Wenkai Du Acked-by: Benson Leung Signed-off-by: Lee Jones --- drivers/mfd/Makefile | 1 - drivers/mfd/cros_ec.c | 15 +---- drivers/mfd/cros_ec_acpi_gpe.c | 103 --------------------------------- include/linux/mfd/cros_ec.h | 18 ------ 4 files changed, 3 insertions(+), 134 deletions(-) delete mode 100644 drivers/mfd/cros_ec_acpi_gpe.c diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d9d2cf0d32efc..e9fd20dba18d8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o cros_ec_core-objs := cros_ec.o -cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index d61024141e2b6..c221163d5b9ea 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -173,8 +173,6 @@ int cros_ec_register(struct cros_ec_device *ec_dev) dev_info(dev, "Chrome EC device registered\n"); - cros_ec_acpi_install_gpe_handler(dev); - return 0; fail_mfd: @@ -188,8 +186,6 @@ int cros_ec_remove(struct cros_ec_device *ec_dev) { mfd_remove_devices(ec_dev->dev); - cros_ec_acpi_remove_gpe_handler(); - if (ec_dev->irq) free_irq(ec_dev->irq, ec_dev); @@ -204,14 +200,9 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev) int ret; u8 sleep_event; - if (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) { - sleep_event = HOST_SLEEP_EVENT_S3_SUSPEND; - } else { - sleep_event = HOST_SLEEP_EVENT_S0IX_SUSPEND; - - /* Clearing the GPE status for any pending event */ - cros_ec_acpi_clear_gpe(); - } + sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ? + HOST_SLEEP_EVENT_S3_SUSPEND : + HOST_SLEEP_EVENT_S0IX_SUSPEND; ret = cros_ec_sleep_event(ec_dev, sleep_event); if (ret < 0) diff --git a/drivers/mfd/cros_ec_acpi_gpe.c b/drivers/mfd/cros_ec_acpi_gpe.c deleted file mode 100644 index 56d305dab2d48..0000000000000 --- a/drivers/mfd/cros_ec_acpi_gpe.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * ChromeOS EC multi-function device - * - * Copyright (C) 2017 Google, Inc - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * The ChromeOS EC multi function device is used to mux all the requests - * to the EC device for its multiple features: keyboard controller, - * battery charging and regulator control, firmware update. - */ -#include - -#define ACPI_LID_DEVICE "LID0" - -static int ec_wake_gpe = -EINVAL; - -/* - * This handler indicates to ACPI core that this GPE should stay enabled for - * lid to work in suspend to idle path. - */ -static u32 cros_ec_gpe_handler(acpi_handle gpe_device, u32 gpe_number, - void *data) -{ - return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; -} - -/* - * Get ACPI GPE for LID0 device. - */ -static int cros_ec_get_ec_wake_gpe(struct device *dev) -{ - struct acpi_device *cros_acpi_dev; - struct acpi_device *adev; - acpi_handle handle; - acpi_status status; - int ret; - - cros_acpi_dev = ACPI_COMPANION(dev); - - if (!cros_acpi_dev || !cros_acpi_dev->parent || - !cros_acpi_dev->parent->handle) - return -EINVAL; - - status = acpi_get_handle(cros_acpi_dev->parent->handle, ACPI_LID_DEVICE, - &handle); - if (ACPI_FAILURE(status)) - return -EINVAL; - - ret = acpi_bus_get_device(handle, &adev); - if (ret) - return ret; - - return adev->wakeup.gpe_number; -} - -int cros_ec_acpi_install_gpe_handler(struct device *dev) -{ - acpi_status status; - - ec_wake_gpe = cros_ec_get_ec_wake_gpe(dev); - - if (ec_wake_gpe < 0) - return ec_wake_gpe; - - status = acpi_install_gpe_handler(NULL, ec_wake_gpe, - ACPI_GPE_EDGE_TRIGGERED, - &cros_ec_gpe_handler, NULL); - if (ACPI_FAILURE(status)) - return -ENODEV; - - dev_info(dev, "Initialized, GPE = 0x%x\n", ec_wake_gpe); - - return 0; -} - -void cros_ec_acpi_remove_gpe_handler(void) -{ - acpi_status status; - - if (ec_wake_gpe < 0) - return; - - status = acpi_remove_gpe_handler(NULL, ec_wake_gpe, - &cros_ec_gpe_handler); - if (ACPI_FAILURE(status)) - pr_err("failed to remove gpe handler\n"); -} - -void cros_ec_acpi_clear_gpe(void) -{ - if (ec_wake_gpe < 0) - return; - - acpi_clear_gpe(NULL, ec_wake_gpe); -} diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 2d4e23c9ea0af..4ff0cec979b00 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -328,22 +328,4 @@ extern struct attribute_group cros_ec_vbc_attr_group; int cros_ec_debugfs_init(struct cros_ec_dev *ec); void cros_ec_debugfs_remove(struct cros_ec_dev *ec); -/* ACPI GPE handler */ -#ifdef CONFIG_ACPI - -int cros_ec_acpi_install_gpe_handler(struct device *dev); -void cros_ec_acpi_remove_gpe_handler(void); -void cros_ec_acpi_clear_gpe(void); - -#else /* CONFIG_ACPI */ - -static inline int cros_ec_acpi_install_gpe_handler(struct device *dev) -{ - return -ENODEV; -} -static inline void cros_ec_acpi_remove_gpe_handler(void) {} -static inline void cros_ec_acpi_clear_gpe(void) {} - -#endif /* CONFIG_ACPI */ - #endif /* __LINUX_MFD_CROS_EC_H */ From 906eb6793e87b5b5b3bf4fda921e3ffd0db47283 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 22 May 2018 17:23:10 -0700 Subject: [PATCH 19/29] mfd: cros_ec: Retry commands when EC is known to be busy Commit 001dde9400d5 ("mfd: cros ec: spi: Fix "in progress" error signaling") pointed out some bad code, but its analysis and conclusion was not 100% correct. It *is* correct that we should not propagate result==EC_RES_IN_PROGRESS for transport errors, because this has a special meaning -- that we should follow up with EC_CMD_GET_COMMS_STATUS until the EC is no longer busy. This is definitely the wrong thing for many commands, because among other problems, EC_CMD_GET_COMMS_STATUS doesn't actually retrieve any RX data from the EC, so commands that expected some data back will instead start processing junk. For such commands, the right answer is to either propagate the error (and return that error to the caller) or resend the original command (*not* EC_CMD_GET_COMMS_STATUS). Unfortunately, commit 001dde9400d5 forgets a crucial point: that for some long-running operations, the EC physically cannot respond to commands any more. For example, with EC_CMD_FLASH_ERASE, the EC may be re-flashing its own code regions, so it can't respond to SPI interrupts. Instead, the EC prepares us ahead of time for being busy for a "long" time, and fills its hardware buffer with EC_SPI_PAST_END. Thus, we expect to see several "transport" errors (or, messages filled with EC_SPI_PAST_END). So we should really translate that to a retryable error (-EAGAIN) and continue sending EC_CMD_GET_COMMS_STATUS until we get a ready status. IOW, it is actually important to treat some of these "junk" values as retryable errors. Together with commit 001dde9400d5, this resolves bugs like the following: 1. EC_CMD_FLASH_ERASE now works again (with commit 001dde9400d5, we would abort the first time we saw EC_SPI_PAST_END) 2. Before commit 001dde9400d5, transport errors (e.g., EC_SPI_RX_BAD_DATA) seen in other commands (e.g., EC_CMD_RTC_GET_VALUE) used to yield junk data in the RX buffer; they will now yield -EAGAIN return values, and tools like 'hwclock' will simply fail instead of retrieving and re-programming undefined time values Fixes: 001dde9400d5 ("mfd: cros ec: spi: Fix "in progress" error signaling") Signed-off-by: Brian Norris Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_spi.c | 24 ++++++++++++++++++++---- drivers/platform/chrome/cros_ec_proto.c | 2 ++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 1b52b8557034b..2060d1483043d 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -419,10 +419,25 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, /* Verify that EC can process command */ for (i = 0; i < len; i++) { rx_byte = rx_buf[i]; + /* + * Seeing the PAST_END, RX_BAD_DATA, or NOT_READY + * markers are all signs that the EC didn't fully + * receive our command. e.g., if the EC is flashing + * itself, it can't respond to any commands and instead + * clocks out EC_SPI_PAST_END from its SPI hardware + * buffer. Similar occurrences can happen if the AP is + * too slow to clock out data after asserting CS -- the + * EC will abort and fill its buffer with + * EC_SPI_RX_BAD_DATA. + * + * In all cases, these errors should be safe to retry. + * Report -EAGAIN and let the caller decide what to do + * about that. + */ if (rx_byte == EC_SPI_PAST_END || rx_byte == EC_SPI_RX_BAD_DATA || rx_byte == EC_SPI_NOT_READY) { - ret = -EREMOTEIO; + ret = -EAGAIN; break; } } @@ -431,7 +446,7 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, if (!ret) ret = cros_ec_spi_receive_packet(ec_dev, ec_msg->insize + sizeof(*response)); - else + else if (ret != -EAGAIN) dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); final_ret = terminate_request(ec_dev); @@ -537,10 +552,11 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, /* Verify that EC can process command */ for (i = 0; i < len; i++) { rx_byte = rx_buf[i]; + /* See comments in cros_ec_pkt_xfer_spi() */ if (rx_byte == EC_SPI_PAST_END || rx_byte == EC_SPI_RX_BAD_DATA || rx_byte == EC_SPI_NOT_READY) { - ret = -EREMOTEIO; + ret = -EAGAIN; break; } } @@ -549,7 +565,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, if (!ret) ret = cros_ec_spi_receive_response(ec_dev, ec_msg->insize + EC_MSG_TX_PROTO_BYTES); - else + else if (ret != -EAGAIN) dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); final_ret = terminate_request(ec_dev); diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index e7bbdf947bbcf..8350ca2311c73 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -91,6 +91,8 @@ static int send_command(struct cros_ec_device *ec_dev, usleep_range(10000, 11000); ret = (*xfer_fxn)(ec_dev, status_msg); + if (ret == -EAGAIN) + continue; if (ret < 0) break; From 7764be9560b3bc829ab9609df124fe6591fe1d6a Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Mon, 30 Jan 2017 15:47:22 -0800 Subject: [PATCH 20/29] platform: chrome: Add Tablet Switch ACPI driver Add a kernel driver for GOOG0006, an ACPI driver reporting an event when the tablet switch status changes. On an ACPI based convertible chromebook check evtest display tablet mode switch changes: Available devices: .. /dev/input/event3: Tablet Mode Switch .. Testing ... (interrupt to exit) Event: time 1484879712.604360, type 5 (EV_SW), code 1 (SW_TABLET_MODE), value 1 Event: time 1484879712.604360, -------------- SYN_REPORT ------------ Event: time 1484879715.132228, type 5 (EV_SW), code 1 (SW_TABLET_MODE), value 0 Event: time 1484879715.132228, -------------- SYN_REPORT ------------ ... Check state is updated at resume time when different from suspend time. Signed-off-by: Gwendal Grignou Signed-off-by: Benson Leung --- drivers/platform/chrome/Kconfig | 10 ++ drivers/platform/chrome/Makefile | 1 + drivers/platform/chrome/chromeos_tbmc.c | 128 ++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 drivers/platform/chrome/chromeos_tbmc.c diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index e728a96cabfd2..57aabe6e94a36 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -38,6 +38,16 @@ config CHROMEOS_PSTORE If you have a supported Chromebook, choose Y or M here. The module will be called chromeos_pstore. +config CHROMEOS_TBMC + tristate "ChromeOS Tablet Switch Controller" + depends on ACPI + help + This option adds a driver for the tablet switch on + select Chrome OS systems. + + To compile this driver as a module, choose M here: the + module will be called chromeos_tbmc. + config CROS_EC_CTL tristate diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index ff3b369911f0e..e44c37a63fa99 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o +obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o cros_ec_ctl-objs := cros_ec_sysfs.o cros_ec_lightbar.o \ cros_ec_vbc.o cros_ec_debugfs.o obj-$(CONFIG_CROS_EC_CTL) += cros_ec_ctl.o diff --git a/drivers/platform/chrome/chromeos_tbmc.c b/drivers/platform/chrome/chromeos_tbmc.c new file mode 100644 index 0000000000000..c87e53319daed --- /dev/null +++ b/drivers/platform/chrome/chromeos_tbmc.c @@ -0,0 +1,128 @@ +/* + * chromeos_tbmc - Driver to detect Tablet Mode for ChromeOS convertible. + * + * Copyright 2017 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * On Chromebook using ACPI, this device listens for notification + * from GOOG0006 and issue method TBMC to retrieve the status. + * + * GOOG0006 issues the notification when it receives EC_HOST_EVENT_MODE_CHANGE + * from the EC. + * Method TBMC reads EC_ACPI_MEM_DEVICE_ORIENTATION byte from the shared + * memory region. + */ + +#include +#include +#include +#include +#include + +#define DRV_NAME "chromeos_tbmc" +#define ACPI_DRV_NAME "GOOG0006" + +static int chromeos_tbmc_query_switch(struct acpi_device *adev, + struct input_dev *idev) +{ + unsigned long long state; + acpi_status status; + + status = acpi_evaluate_integer(adev->handle, "TBMC", NULL, &state); + if (ACPI_FAILURE(status)) + return -ENODEV; + + /* input layer checks if event is redundant */ + input_report_switch(idev, SW_TABLET_MODE, state); + input_sync(idev); + + return 0; +} + +static __maybe_unused int chromeos_tbmc_resume(struct device *dev) +{ + struct acpi_device *adev = to_acpi_device(dev); + + return chromeos_tbmc_query_switch(adev, adev->driver_data); +} + +static void chromeos_tbmc_notify(struct acpi_device *adev, u32 event) +{ + switch (event) { + case 0x80: + chromeos_tbmc_query_switch(adev, adev->driver_data); + break; + default: + dev_err(&adev->dev, "Unexpected event: 0x%08X\n", event); + } +} + +static int chromeos_tbmc_open(struct input_dev *idev) +{ + struct acpi_device *adev = input_get_drvdata(idev); + + return chromeos_tbmc_query_switch(adev, idev); +} + +static int chromeos_tbmc_add(struct acpi_device *adev) +{ + struct input_dev *idev; + struct device *dev = &adev->dev; + int ret; + + idev = devm_input_allocate_device(dev); + if (!idev) + return -ENOMEM; + + idev->name = "Tablet Mode Switch"; + idev->phys = acpi_device_hid(adev); + + idev->id.bustype = BUS_HOST; + idev->id.version = 1; + idev->id.product = 0; + idev->open = chromeos_tbmc_open; + + input_set_drvdata(idev, adev); + adev->driver_data = idev; + + input_set_capability(idev, EV_SW, SW_TABLET_MODE); + ret = input_register_device(idev); + if (ret) { + dev_err(dev, "cannot register input device\n"); + return ret; + } + return 0; +} + +static const struct acpi_device_id chromeos_tbmc_acpi_device_ids[] = { + { ACPI_DRV_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, chromeos_tbmc_acpi_device_ids); + +static const SIMPLE_DEV_PM_OPS(chromeos_tbmc_pm_ops, NULL, + chromeos_tbmc_resume); + +static struct acpi_driver chromeos_tbmc_driver = { + .name = DRV_NAME, + .class = DRV_NAME, + .ids = chromeos_tbmc_acpi_device_ids, + .ops = { + .add = chromeos_tbmc_add, + .notify = chromeos_tbmc_notify, + }, + .drv.pm = &chromeos_tbmc_pm_ops, +}; + +module_acpi_driver(chromeos_tbmc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ChromeOS ACPI tablet switch driver"); From 3c4c60d01f0efa7617c25174e7d56d7f594ea172 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 3 May 2018 17:41:34 -0700 Subject: [PATCH 21/29] platform/chrome: chromeos_laptop - supply properties for ACPI devices BayTrail-based and newer Chromebooks describe their peripherals in ACPI; unfortunately their description is not complete, and peripherals drivers, such as driver for Atmel Touch controllers, has to resort to DMI-matching to configure the peripherals properly. To avoid polluting peripheral driver code, let's teach chromeos_laptop driver to supply missing data via generic device properties. Note we supply "compatible" string for Atmel peripherals not because it is needed for matching devices and driver (matching is still done on ACPI HID entries), but because peripherals driver will be using presence of "compatible" property to determine if device properties have been attached to the device, and fail to bind if they are absent. Signed-off-by: Dmitry Torokhov Signed-off-by: Benson Leung --- drivers/platform/chrome/chromeos_laptop.c | 307 ++++++++++++++++++++-- 1 file changed, 278 insertions(+), 29 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index fda9e09283c87..6288b71e3d0c1 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -23,6 +23,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -71,6 +72,11 @@ struct i2c_peripheral { struct i2c_client *client; }; +struct acpi_peripheral { + char hid[ACPI_ID_LEN]; + const struct property_entry *properties; +}; + struct chromeos_laptop { /* * Note that we can't mark this pointer as const because @@ -78,6 +84,9 @@ struct chromeos_laptop { */ struct i2c_peripheral *i2c_peripherals; unsigned int num_i2c_peripherals; + + const struct acpi_peripheral *acpi_peripherals; + unsigned int num_acpi_peripherals; }; static const struct chromeos_laptop *cros_laptop; @@ -165,6 +174,38 @@ static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter) } } +static bool chromeos_laptop_adjust_client(struct i2c_client *client) +{ + const struct acpi_peripheral *acpi_dev; + struct acpi_device_id acpi_ids[2] = { }; + int i; + int error; + + if (!has_acpi_companion(&client->dev)) + return false; + + for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) { + acpi_dev = &cros_laptop->acpi_peripherals[i]; + + memcpy(acpi_ids[0].id, acpi_dev->hid, ACPI_ID_LEN); + + if (acpi_match_device(acpi_ids, &client->dev)) { + error = device_add_properties(&client->dev, + acpi_dev->properties); + if (error) { + dev_err(&client->dev, + "failed to add properties: %d\n", + error); + break; + } + + return true; + } + } + + return false; +} + static void chromeos_laptop_detach_i2c_client(struct i2c_client *client) { struct i2c_peripheral *i2c_dev; @@ -187,6 +228,8 @@ static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb, case BUS_NOTIFY_ADD_DEVICE: if (dev->type == &i2c_adapter_type) chromeos_laptop_check_adapter(to_i2c_adapter(dev)); + else if (dev->type == &i2c_client_type) + chromeos_laptop_adjust_client(to_i2c_client(dev)); break; case BUS_NOTIFY_REMOVED_DEVICE: @@ -208,6 +251,12 @@ static const struct chromeos_laptop _name __initconst = { \ .num_i2c_peripherals = ARRAY_SIZE(_name##_peripherals), \ } +#define DECLARE_ACPI_CROS_LAPTOP(_name) \ +static const struct chromeos_laptop _name __initconst = { \ + .acpi_peripherals = _name##_peripherals, \ + .num_acpi_peripherals = ARRAY_SIZE(_name##_peripherals), \ +} + static struct i2c_peripheral samsung_series_5_550_peripherals[] __initdata = { /* Touchpad. */ { @@ -251,16 +300,25 @@ static const int chromebook_pixel_tp_keys[] __initconst = { static const struct property_entry chromebook_pixel_trackpad_props[] __initconst = { + PROPERTY_ENTRY_STRING("compatible", "atmel,maxtouch"), PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_pixel_tp_keys), { } }; +static const struct property_entry +chromebook_atmel_touchscreen_props[] __initconst = { + PROPERTY_ENTRY_STRING("compatible", "atmel,maxtouch"), + { } +}; + static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = { /* Touch Screen. */ { .board_info = { I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), + .properties = + chromebook_atmel_touchscreen_props, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "touchscreen", @@ -371,6 +429,8 @@ static struct i2c_peripheral acer_c720_peripherals[] __initdata = { .board_info = { I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), + .properties = + chromebook_atmel_touchscreen_props, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "touchscreen", @@ -436,6 +496,47 @@ static struct i2c_peripheral cr48_peripherals[] __initdata = { }; DECLARE_CROS_LAPTOP(cr48); +static const u32 samus_touchpad_buttons[] __initconst = { + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + BTN_LEFT +}; + +static const struct property_entry samus_trackpad_props[] __initconst = { + PROPERTY_ENTRY_STRING("compatible", "atmel,maxtouch"), + PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", samus_touchpad_buttons), + { } +}; + +static struct acpi_peripheral samus_peripherals[] __initdata = { + /* Touchpad */ + { + .hid = "ATML0000", + .properties = samus_trackpad_props, + }, + /* Touchsceen */ + { + .hid = "ATML0001", + .properties = chromebook_atmel_touchscreen_props, + }, +}; +DECLARE_ACPI_CROS_LAPTOP(samus); + +static struct acpi_peripheral generic_atmel_peripherals[] __initdata = { + /* Touchpad */ + { + .hid = "ATML0000", + .properties = chromebook_pixel_trackpad_props, + }, + /* Touchsceen */ + { + .hid = "ATML0001", + .properties = chromebook_atmel_touchscreen_props, + }, +}; +DECLARE_ACPI_CROS_LAPTOP(generic_atmel); + static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { { .ident = "Samsung Series 5 550", @@ -519,17 +620,64 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { }, .driver_data = (void *)&cr48, }, + /* Devices with peripherals incompletely described in ACPI */ + { + .ident = "Chromebook Pro", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_PRODUCT_NAME, "Caroline"), + }, + .driver_data = (void *)&samus, + }, + { + .ident = "Google Pixel 2 (2015)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Samus"), + }, + .driver_data = (void *)&samus, + }, + { + /* + * Other Chromebooks with Atmel touch controllers: + * - Celes, Winky (touchpad) + * - Clapper, Expresso, Rambi, Glimmer (touchscreen) + */ + .ident = "Other Chromebook", + .matches = { + /* + * This will match all Google devices, not only devices + * with Atmel, but we will validate that the device + * actually has matching peripherals. + */ + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), + }, + .driver_data = (void *)&generic_atmel, + }, { } }; MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table); -static int __init chromeos_laptop_scan_adapter(struct device *dev, void *data) +static int __init chromeos_laptop_scan_peripherals(struct device *dev, void *data) { - struct i2c_adapter *adapter; + int error; - adapter = i2c_verify_adapter(dev); - if (adapter) - chromeos_laptop_check_adapter(adapter); + if (dev->type == &i2c_adapter_type) { + chromeos_laptop_check_adapter(to_i2c_adapter(dev)); + } else if (dev->type == &i2c_client_type) { + if (chromeos_laptop_adjust_client(to_i2c_client(dev))) { + /* + * Now that we have needed properties re-trigger + * driver probe in case driver was initialized + * earlier and probe failed. + */ + error = device_attach(dev); + if (error < 0) + dev_warn(dev, + "%s: device_attach() failed: %d\n", + __func__, error); + } + } return 0; } @@ -573,27 +721,24 @@ static int __init chromeos_laptop_setup_irq(struct i2c_peripheral *i2c_dev) return 0; } -static struct chromeos_laptop * __init -chromeos_laptop_prepare(const struct chromeos_laptop *src) +static int __init +chromeos_laptop_prepare_i2c_peripherals(struct chromeos_laptop *cros_laptop, + const struct chromeos_laptop *src) { - struct chromeos_laptop *cros_laptop; struct i2c_peripheral *i2c_dev; struct i2c_board_info *info; - int error; int i; + int error; - cros_laptop = kzalloc(sizeof(*cros_laptop), GFP_KERNEL); - if (!cros_laptop) - return ERR_PTR(-ENOMEM); + if (!src->num_i2c_peripherals) + return 0; cros_laptop->i2c_peripherals = kmemdup(src->i2c_peripherals, src->num_i2c_peripherals * sizeof(*src->i2c_peripherals), GFP_KERNEL); - if (!cros_laptop->i2c_peripherals) { - error = -ENOMEM; - goto err_free_cros_laptop; - } + if (!cros_laptop->i2c_peripherals) + return -ENOMEM; cros_laptop->num_i2c_peripherals = src->num_i2c_peripherals; @@ -603,7 +748,7 @@ chromeos_laptop_prepare(const struct chromeos_laptop *src) error = chromeos_laptop_setup_irq(i2c_dev); if (error) - goto err_destroy_cros_peripherals; + goto err_out; /* We need to deep-copy properties */ if (info->properties) { @@ -611,14 +756,14 @@ chromeos_laptop_prepare(const struct chromeos_laptop *src) property_entries_dup(info->properties); if (IS_ERR(info->properties)) { error = PTR_ERR(info->properties); - goto err_destroy_cros_peripherals; + goto err_out; } } } - return cros_laptop; + return 0; -err_destroy_cros_peripherals: +err_out: while (--i >= 0) { i2c_dev = &cros_laptop->i2c_peripherals[i]; info = &i2c_dev->board_info; @@ -626,13 +771,74 @@ chromeos_laptop_prepare(const struct chromeos_laptop *src) property_entries_free(info->properties); } kfree(cros_laptop->i2c_peripherals); -err_free_cros_laptop: - kfree(cros_laptop); - return ERR_PTR(error); + return error; +} + +static int __init +chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop, + const struct chromeos_laptop *src) +{ + struct acpi_peripheral *acpi_peripherals; + struct acpi_peripheral *acpi_dev; + const struct acpi_peripheral *src_dev; + int n_peripherals = 0; + int i; + int error; + + for (i = 0; i < src->num_acpi_peripherals; i++) { + if (acpi_dev_present(src->acpi_peripherals[i].hid, NULL, -1)) + n_peripherals++; + } + + if (!n_peripherals) + return 0; + + acpi_peripherals = kcalloc(n_peripherals, + sizeof(*src->acpi_peripherals), + GFP_KERNEL); + if (!acpi_peripherals) + return -ENOMEM; + + acpi_dev = acpi_peripherals; + for (i = 0; i < src->num_acpi_peripherals; i++) { + src_dev = &src->acpi_peripherals[i]; + if (!acpi_dev_present(src_dev->hid, NULL, -1)) + continue; + + *acpi_dev = *src_dev; + + /* We need to deep-copy properties */ + if (src_dev->properties) { + acpi_dev->properties = + property_entries_dup(src_dev->properties); + if (IS_ERR(acpi_dev->properties)) { + error = PTR_ERR(acpi_dev->properties); + goto err_out; + } + } + + acpi_dev++; + } + + cros_laptop->acpi_peripherals = acpi_peripherals; + cros_laptop->num_acpi_peripherals = n_peripherals; + + return 0; + +err_out: + while (--i >= 0) { + acpi_dev = &acpi_peripherals[i]; + if (acpi_dev->properties) + property_entries_free(acpi_dev->properties); + } + + kfree(acpi_peripherals); + return error; } static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop) { + const struct acpi_peripheral *acpi_dev; struct i2c_peripheral *i2c_dev; struct i2c_board_info *info; int i; @@ -648,10 +854,41 @@ static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop) property_entries_free(info->properties); } + for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) { + acpi_dev = &cros_laptop->acpi_peripherals[i]; + + if (acpi_dev->properties) + property_entries_free(acpi_dev->properties); + } + kfree(cros_laptop->i2c_peripherals); + kfree(cros_laptop->acpi_peripherals); kfree(cros_laptop); } +static struct chromeos_laptop * __init +chromeos_laptop_prepare(const struct chromeos_laptop *src) +{ + struct chromeos_laptop *cros_laptop; + int error; + + cros_laptop = kzalloc(sizeof(*cros_laptop), GFP_KERNEL); + if (!cros_laptop) + return ERR_PTR(-ENOMEM); + + error = chromeos_laptop_prepare_i2c_peripherals(cros_laptop, src); + if (!error) + error = chromeos_laptop_prepare_acpi_peripherals(cros_laptop, + src); + + if (error) { + chromeos_laptop_destroy(cros_laptop); + return ERR_PTR(error); + } + + return cros_laptop; +} + static int __init chromeos_laptop_init(void) { const struct dmi_system_id *dmi_id; @@ -669,21 +906,33 @@ static int __init chromeos_laptop_init(void) if (IS_ERR(cros_laptop)) return PTR_ERR(cros_laptop); + if (!cros_laptop->num_i2c_peripherals && + !cros_laptop->num_acpi_peripherals) { + pr_debug("no relevant devices detected\n"); + error = -ENODEV; + goto err_destroy_cros_laptop; + } + error = bus_register_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier); if (error) { - pr_err("failed to register i2c bus notifier: %d\n", error); - chromeos_laptop_destroy(cros_laptop); - return error; + pr_err("failed to register i2c bus notifier: %d\n", + error); + goto err_destroy_cros_laptop; } /* - * Scan adapters that have been registered before we installed - * the notifier to make sure we do not miss any devices. + * Scan adapters that have been registered and clients that have + * been created before we installed the notifier to make sure + * we do not miss any devices. */ - i2c_for_each_dev(NULL, chromeos_laptop_scan_adapter); + i2c_for_each_dev(NULL, chromeos_laptop_scan_peripherals); return 0; + +err_destroy_cros_laptop: + chromeos_laptop_destroy(cros_laptop); + return error; } static void __exit chromeos_laptop_exit(void) From 91075334263f188e22ebff365554184cb9282c79 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 28 May 2018 17:58:46 +0200 Subject: [PATCH 22/29] platform: chrome: Add input dependency for tablet switch driver Without CONFIG_INPUT, or with a modular input layer and built-in tablet driver, we get a link error: ERROR: "input_event" [drivers/platform/chrome/chromeos_tbmc.ko] undefined! ERROR: "input_register_device" [drivers/platform/chrome/chromeos_tbmc.ko] undefined! ERROR: "input_set_capability" [drivers/platform/chrome/chromeos_tbmc.ko] undefined! ERROR: "devm_input_allocate_device" [drivers/platform/chrome/chromeos_tbmc.ko] undefined! This adds the corresponding Kconfig dependency Fixes: b418f74170d7 ("platform: chrome: Add Tablet Switch ACPI driver") Signed-off-by: Arnd Bergmann Signed-off-by: Benson Leung --- drivers/platform/chrome/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 57aabe6e94a36..cb0df9eb3e0fb 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -41,6 +41,7 @@ config CHROMEOS_PSTORE config CHROMEOS_TBMC tristate "ChromeOS Tablet Switch Controller" depends on ACPI + depends on INPUT help This option adds a driver for the tablet switch on select Chrome OS systems. From a6953b2bd4274b504d49baf21373279ffb1b0b39 Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Wed, 18 Apr 2018 12:23:58 +0200 Subject: [PATCH 23/29] mfd: cros_ec: Fail early if we cannot identify the EC If we cannot communicate with the EC chip to detect the protocol version and its features, it's very likely useless to continue. Else we will commit all kind of uninformed mistakes (using the wrong protocol, the wrong buffer size, mixing the EC with other chips). Signed-off-by: Vincent Palatin Acked-by: Benson Leung Signed-off-by: Enric Balletbo i Serra Reviewed-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index c221163d5b9ea..1e52769707786 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -112,7 +112,11 @@ int cros_ec_register(struct cros_ec_device *ec_dev) mutex_init(&ec_dev->lock); - cros_ec_query_all(ec_dev); + err = cros_ec_query_all(ec_dev); + if (err) { + dev_err(dev, "Cannot identify the EC: error %d\n", err); + return err; + } if (ec_dev->irq) { err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, From 25c5ab9acb39c37924d164bcd4da6174a3f18ac0 Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Wed, 18 Apr 2018 12:23:59 +0200 Subject: [PATCH 24/29] mfd: cros_ec: Free IRQ automatically Free the IRQ we might have requested when removing the cros_ec device, so we can unload and reload the driver properly. Signed-off-by: Vincent Palatin Signed-off-by: Enric Balletbo i Serra Acked-by: Vincent Palatin Reviewed-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 1e52769707786..58e05069163e2 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -119,9 +119,9 @@ int cros_ec_register(struct cros_ec_device *ec_dev) } if (ec_dev->irq) { - err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "chromeos-ec", ec_dev); + err = devm_request_threaded_irq(dev, ec_dev->irq, NULL, + ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "chromeos-ec", ec_dev); if (err) { dev_err(dev, "Failed to request IRQ %d: %d", ec_dev->irq, err); @@ -135,7 +135,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev) dev_err(dev, "Failed to register Embedded Controller subdevice %d\n", err); - goto fail_mfd; + return err; } if (ec_dev->max_passthru) { @@ -153,7 +153,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev) dev_err(dev, "Failed to register Power Delivery subdevice %d\n", err); - goto fail_mfd; + return err; } } @@ -162,7 +162,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev) if (err) { mfd_remove_devices(dev); dev_err(dev, "Failed to register sub-devices\n"); - goto fail_mfd; + return err; } } @@ -178,11 +178,6 @@ int cros_ec_register(struct cros_ec_device *ec_dev) dev_info(dev, "Chrome EC device registered\n"); return 0; - -fail_mfd: - if (ec_dev->irq) - free_irq(ec_dev->irq, ec_dev); - return err; } EXPORT_SYMBOL(cros_ec_register); @@ -190,9 +185,6 @@ int cros_ec_remove(struct cros_ec_device *ec_dev) { mfd_remove_devices(ec_dev->dev); - if (ec_dev->irq) - free_irq(ec_dev->irq, ec_dev); - return 0; } EXPORT_SYMBOL(cros_ec_remove); From 490447477df1501f77302372d635f262bf7dcca5 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 18 Apr 2018 12:24:00 +0200 Subject: [PATCH 25/29] mfd: cros_ec: Don't try to grab log when suspended We should stop our worker thread while we're suspended. If we don't then we'll get messages like: cros-ec-spi spi5.0: spi transfer failed: -108 cros-ec-spi spi5.0: cs-deassert spi transfer failed: -108 cros-ec-ctl cros-ec-ctl.0.auto: EC communication failed Signed-off-by: Douglas Anderson Signed-off-by: Enric Balletbo i Serra Reviewed-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_dev.c | 4 ++++ drivers/platform/chrome/cros_ec_debugfs.c | 20 ++++++++++++++++++++ include/linux/mfd/cros_ec.h | 2 ++ 3 files changed, 26 insertions(+) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index eafd06f62a3af..5a7d4e1dea708 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -466,6 +466,8 @@ static __maybe_unused int ec_device_suspend(struct device *dev) { struct cros_ec_dev *ec = dev_get_drvdata(dev); + cros_ec_debugfs_suspend(ec); + lb_suspend(ec); return 0; @@ -475,6 +477,8 @@ static __maybe_unused int ec_device_resume(struct device *dev) { struct cros_ec_dev *ec = dev_get_drvdata(dev); + cros_ec_debugfs_resume(ec); + lb_resume(ec); return 0; diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index 5394030225682..8be739b7385b9 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -398,3 +398,23 @@ void cros_ec_debugfs_remove(struct cros_ec_dev *ec) cros_ec_cleanup_console_log(ec->debug_info); } EXPORT_SYMBOL(cros_ec_debugfs_remove); + +void cros_ec_debugfs_suspend(struct cros_ec_dev *ec) +{ + /* + * cros_ec_debugfs_init() failures are non-fatal; it's also possible + * that we initted things but decided that console log wasn't supported. + * We'll use the same set of checks that cros_ec_debugfs_remove() + + * cros_ec_cleanup_console_log() end up using to handle those cases. + */ + if (ec->debug_info && ec->debug_info->log_buffer.buf) + cancel_delayed_work_sync(&ec->debug_info->log_poll_work); +} +EXPORT_SYMBOL(cros_ec_debugfs_suspend); + +void cros_ec_debugfs_resume(struct cros_ec_dev *ec) +{ + if (ec->debug_info && ec->debug_info->log_buffer.buf) + schedule_delayed_work(&ec->debug_info->log_poll_work, 0); +} +EXPORT_SYMBOL(cros_ec_debugfs_resume); diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 4ff0cec979b00..b8e750d912003 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -327,5 +327,7 @@ extern struct attribute_group cros_ec_vbc_attr_group; /* debugfs stuff */ int cros_ec_debugfs_init(struct cros_ec_dev *ec); void cros_ec_debugfs_remove(struct cros_ec_dev *ec); +void cros_ec_debugfs_suspend(struct cros_ec_dev *ec); +void cros_ec_debugfs_resume(struct cros_ec_dev *ec); #endif /* __LINUX_MFD_CROS_EC_H */ From a4b6301a78994c5986e8289e27a117b6ed348fd0 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 18 Apr 2018 12:24:01 +0200 Subject: [PATCH 26/29] mfd: cros_ec_dev: Register cros-ec-rtc driver as a subdevice Check whether this EC instance has RTC host command support and instatiate the RTC driver as a subdevice in such case. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_dev.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 5a7d4e1dea708..a393b3c11aa06 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -383,6 +383,10 @@ static void cros_ec_sensors_register(struct cros_ec_dev *ec) kfree(msg); } +static const struct mfd_cell cros_ec_rtc_cells[] = { + { .name = "cros-ec-rtc" } +}; + static int ec_device_probe(struct platform_device *pdev) { int retval = -ENOMEM; @@ -422,6 +426,18 @@ static int ec_device_probe(struct platform_device *pdev) if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) cros_ec_sensors_register(ec); + /* Check whether this EC instance has RTC host command support */ + if (cros_ec_check_features(ec, EC_FEATURE_RTC)) { + retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, + cros_ec_rtc_cells, + ARRAY_SIZE(cros_ec_rtc_cells), + NULL, 0, NULL); + if (retval) + dev_err(ec->dev, + "failed to add cros-ec-rtc device: %d\n", + retval); + } + /* Take control of the lightbar from the EC. */ lb_manual_suspend_ctrl(ec, 1); From 2ad83d1c3996c36fd4eef9773f5d80b67a6a57c6 Mon Sep 17 00:00:00 2001 From: Daniel Hung-yu Wu Date: Wed, 18 Apr 2018 12:24:02 +0200 Subject: [PATCH 27/29] mfd: cros_ec_dev: Register shutdown function for debugfs Reboot or shutdown during delayed works could corrupt communication with EC and certain I2C controller may not be able to recover from the error state. This patch registers a shutdown callback used to cancel the debugfs log worker thread. Signed-off-by: Daniel Hung-yu Wu Signed-off-by: Enric Balletbo i Serra Reviewed-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_dev.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index a393b3c11aa06..1f889523885d4 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -472,6 +472,14 @@ static int ec_device_remove(struct platform_device *pdev) return 0; } +static void ec_device_shutdown(struct platform_device *pdev) +{ + struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); + + /* Be sure to clear up debugfs delayed works */ + cros_ec_debugfs_remove(ec); +} + static const struct platform_device_id cros_ec_id[] = { { DRV_NAME, 0 }, { /* sentinel */ }, @@ -514,6 +522,7 @@ static struct platform_driver cros_ec_dev_driver = { }, .probe = ec_device_probe, .remove = ec_device_remove, + .shutdown = ec_device_shutdown, }; static int __init cros_ec_dev_init(void) From f080fde1fff81cc537b085a72d027a35fcd45327 Mon Sep 17 00:00:00 2001 From: Wei-Ning Huang Date: Wed, 18 Apr 2018 12:24:03 +0200 Subject: [PATCH 28/29] mfd: cros_ec_i2c: Add ACPI module device table Add ACPI module device table for matching cros-ec devices to load the cros_ec_i2c driver automatically. Signed-off-by: Wei-Ning Huang Acked-by: Benson Leung Signed-off-by: Enric Balletbo i Serra Reviewed-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_dev.c | 2 +- drivers/mfd/cros_ec_i2c.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 1f889523885d4..1d6dc5c7a19d4 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -482,7 +482,7 @@ static void ec_device_shutdown(struct platform_device *pdev) static const struct platform_device_id cros_ec_id[] = { { DRV_NAME, 0 }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, cros_ec_id); diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index 9f70de1e4c705..02a7bacdc056b 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -13,6 +13,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -344,11 +345,13 @@ static int cros_ec_i2c_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(cros_ec_i2c_pm_ops, cros_ec_i2c_suspend, cros_ec_i2c_resume); +#ifdef CONFIG_OF static const struct of_device_id cros_ec_i2c_of_match[] = { { .compatible = "google,cros-ec-i2c", }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, cros_ec_i2c_of_match); +#endif static const struct i2c_device_id cros_ec_i2c_id[] = { { "cros-ec-i2c", 0 }, @@ -356,9 +359,18 @@ static const struct i2c_device_id cros_ec_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, cros_ec_i2c_id); +#ifdef CONFIG_ACPI +static const struct acpi_device_id cros_ec_i2c_acpi_id[] = { + { "GOOG0008", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, cros_ec_i2c_acpi_id); +#endif + static struct i2c_driver cros_ec_driver = { .driver = { .name = "cros-ec-i2c", + .acpi_match_table = ACPI_PTR(cros_ec_i2c_acpi_id), .of_match_table = of_match_ptr(cros_ec_i2c_of_match), .pm = &cros_ec_i2c_pm_ops, }, From 09011e9f3b29b19185e8e0cac0e48a5e57a2b6d9 Mon Sep 17 00:00:00 2001 From: Daniel Stuart Date: Sat, 20 Jul 2019 00:17:08 -0300 Subject: [PATCH 29/29] GalliumOS: Config: Enable TBMC as a module This was implemented by 7764be9560b3bc829ab9609df124fe6591fe1d6a --- galliumos/changelog | 7 +++++++ galliumos/config | 1 + 2 files changed, 8 insertions(+) diff --git a/galliumos/changelog b/galliumos/changelog index 9193b58cf2721..8d2521d162912 100644 --- a/galliumos/changelog +++ b/galliumos/changelog @@ -1,3 +1,10 @@ +linux-4.16.18-galliumos (4.16.18-galliumos4) bismuth; urgency=medium + + * Add config for Tablet Switch ACPI Driver (TBMC) + - CONFIG_CHROMEOS_TBMC=m + + -- Daniel Stuart Sat, 20 Jul 2019 00:17:08 -0300 + linux-4.16.18-galliumos (4.16.18-galliumos4) bismuth; urgency=medium * Add config for Apollo Lake chipset and audio diff --git a/galliumos/config b/galliumos/config index 704f2dd8de376..eda9b87136589 100644 --- a/galliumos/config +++ b/galliumos/config @@ -5340,6 +5340,7 @@ CONFIG_PMC_ATOM=y CONFIG_CHROME_PLATFORMS=y CONFIG_CHROMEOS_LAPTOP=m CONFIG_CHROMEOS_PSTORE=m +CONFIG_CHROMEOS_TBMC=m CONFIG_CROS_EC_CTL=m CONFIG_CROS_EC_LPC=m CONFIG_CROS_EC_LPC_MEC=y