960 lines · c
1// SPDX-License-Identifier: GPL-2.0+2// Driver to instantiate Chromebook i2c/smbus devices.3//4// Copyright (C) 2012 Google, Inc.5// Author: Benson Leung <bleung@chromium.org>6 7#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt8 9#include <linux/acpi.h>10#include <linux/dmi.h>11#include <linux/i2c.h>12#include <linux/input.h>13#include <linux/interrupt.h>14#include <linux/ioport.h>15#include <linux/module.h>16#include <linux/pci.h>17#include <linux/platform_device.h>18#include <linux/property.h>19 20#define ATMEL_TP_I2C_ADDR 0x4b21#define ATMEL_TP_I2C_BL_ADDR 0x2522#define ATMEL_TS_I2C_ADDR 0x4a23#define ATMEL_TS_I2C_BL_ADDR 0x2624#define CYAPA_TP_I2C_ADDR 0x6725#define ELAN_TP_I2C_ADDR 0x1526#define ISL_ALS_I2C_ADDR 0x4427#define TAOS_ALS_I2C_ADDR 0x2928 29static const char *i2c_adapter_names[] = {30 "SMBus I801 adapter",31 "i915 gmbus vga",32 "i915 gmbus panel",33 "Synopsys DesignWare I2C adapter",34};35 36/* Keep this enum consistent with i2c_adapter_names */37enum i2c_adapter_type {38 I2C_ADAPTER_SMBUS = 0,39 I2C_ADAPTER_VGADDC,40 I2C_ADAPTER_PANEL,41 I2C_ADAPTER_DESIGNWARE,42};43 44struct i2c_peripheral {45 struct i2c_board_info board_info;46 unsigned short alt_addr;47 48 const char *dmi_name;49 unsigned long irqflags;50 struct resource irq_resource;51 52 enum i2c_adapter_type type;53 u32 pci_devid;54 55 const struct property_entry *properties;56 57 struct i2c_client *client;58};59 60struct acpi_peripheral {61 char hid[ACPI_ID_LEN];62 struct software_node swnode;63 struct i2c_client *client;64};65 66struct chromeos_laptop {67 /*68 * Note that we can't mark this pointer as const because69 * i2c_new_scanned_device() changes passed in I2C board info, so.70 */71 struct i2c_peripheral *i2c_peripherals;72 unsigned int num_i2c_peripherals;73 74 struct acpi_peripheral *acpi_peripherals;75 unsigned int num_acpi_peripherals;76};77 78static const struct chromeos_laptop *cros_laptop;79 80static struct i2c_client *81chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter,82 struct i2c_board_info *info,83 unsigned short alt_addr)84{85 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };86 struct i2c_client *client;87 88 /*89 * Add the i2c device. If we can't detect it at the primary90 * address we scan secondary addresses. In any case the client91 * structure gets assigned primary address.92 */93 client = i2c_new_scanned_device(adapter, info, addr_list, NULL);94 if (IS_ERR(client) && alt_addr) {95 struct i2c_board_info dummy_info = {96 I2C_BOARD_INFO("dummy", info->addr),97 };98 const unsigned short alt_addr_list[] = {99 alt_addr, I2C_CLIENT_END100 };101 struct i2c_client *dummy;102 103 dummy = i2c_new_scanned_device(adapter, &dummy_info,104 alt_addr_list, NULL);105 if (!IS_ERR(dummy)) {106 pr_debug("%d-%02x is probed at %02x\n",107 adapter->nr, info->addr, dummy->addr);108 i2c_unregister_device(dummy);109 client = i2c_new_client_device(adapter, info);110 }111 }112 113 if (IS_ERR(client)) {114 client = NULL;115 pr_debug("failed to register device %d-%02x\n",116 adapter->nr, info->addr);117 } else {118 pr_debug("added i2c device %d-%02x\n",119 adapter->nr, info->addr);120 }121 122 return client;123}124 125static bool chromeos_laptop_match_adapter_devid(struct device *dev, u32 devid)126{127 struct pci_dev *pdev;128 129 if (!dev_is_pci(dev))130 return false;131 132 pdev = to_pci_dev(dev);133 return devid == pci_dev_id(pdev);134}135 136static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter)137{138 struct i2c_peripheral *i2c_dev;139 int i;140 141 for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {142 i2c_dev = &cros_laptop->i2c_peripherals[i];143 144 /* Skip devices already created */145 if (i2c_dev->client)146 continue;147 148 if (strncmp(adapter->name, i2c_adapter_names[i2c_dev->type],149 strlen(i2c_adapter_names[i2c_dev->type])))150 continue;151 152 if (i2c_dev->pci_devid &&153 !chromeos_laptop_match_adapter_devid(adapter->dev.parent,154 i2c_dev->pci_devid)) {155 continue;156 }157 158 i2c_dev->client =159 chromes_laptop_instantiate_i2c_device(adapter,160 &i2c_dev->board_info,161 i2c_dev->alt_addr);162 }163}164 165static bool chromeos_laptop_adjust_client(struct i2c_client *client)166{167 struct acpi_peripheral *acpi_dev;168 struct acpi_device_id acpi_ids[2] = { };169 int i;170 int error;171 172 if (!has_acpi_companion(&client->dev))173 return false;174 175 for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) {176 acpi_dev = &cros_laptop->acpi_peripherals[i];177 178 memcpy(acpi_ids[0].id, acpi_dev->hid, ACPI_ID_LEN);179 180 if (acpi_match_device(acpi_ids, &client->dev)) {181 error = device_add_software_node(&client->dev, &acpi_dev->swnode);182 if (error) {183 dev_err(&client->dev,184 "failed to add properties: %d\n",185 error);186 break;187 }188 189 acpi_dev->client = client;190 191 return true;192 }193 }194 195 return false;196}197 198static void chromeos_laptop_detach_i2c_client(struct i2c_client *client)199{200 struct acpi_peripheral *acpi_dev;201 struct i2c_peripheral *i2c_dev;202 int i;203 204 if (has_acpi_companion(&client->dev))205 for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) {206 acpi_dev = &cros_laptop->acpi_peripherals[i];207 208 if (acpi_dev->client == client) {209 acpi_dev->client = NULL;210 return;211 }212 }213 else214 for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {215 i2c_dev = &cros_laptop->i2c_peripherals[i];216 217 if (i2c_dev->client == client) {218 i2c_dev->client = NULL;219 return;220 }221 }222}223 224static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb,225 unsigned long action, void *data)226{227 struct device *dev = data;228 229 switch (action) {230 case BUS_NOTIFY_ADD_DEVICE:231 if (dev->type == &i2c_adapter_type)232 chromeos_laptop_check_adapter(to_i2c_adapter(dev));233 else if (dev->type == &i2c_client_type)234 chromeos_laptop_adjust_client(to_i2c_client(dev));235 break;236 237 case BUS_NOTIFY_REMOVED_DEVICE:238 if (dev->type == &i2c_client_type)239 chromeos_laptop_detach_i2c_client(to_i2c_client(dev));240 break;241 }242 243 return 0;244}245 246static struct notifier_block chromeos_laptop_i2c_notifier = {247 .notifier_call = chromeos_laptop_i2c_notifier_call,248};249 250#define DECLARE_CROS_LAPTOP(_name) \251static const struct chromeos_laptop _name __initconst = { \252 .i2c_peripherals = _name##_peripherals, \253 .num_i2c_peripherals = ARRAY_SIZE(_name##_peripherals), \254}255 256#define DECLARE_ACPI_CROS_LAPTOP(_name) \257static const struct chromeos_laptop _name __initconst = { \258 .acpi_peripherals = _name##_peripherals, \259 .num_acpi_peripherals = ARRAY_SIZE(_name##_peripherals), \260}261 262static struct i2c_peripheral samsung_series_5_550_peripherals[] __initdata = {263 /* Touchpad. */264 {265 .board_info = {266 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),267 .flags = I2C_CLIENT_WAKE,268 },269 .dmi_name = "trackpad",270 .type = I2C_ADAPTER_SMBUS,271 },272 /* Light Sensor. */273 {274 .board_info = {275 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),276 },277 .dmi_name = "lightsensor",278 .type = I2C_ADAPTER_SMBUS,279 },280};281DECLARE_CROS_LAPTOP(samsung_series_5_550);282 283static struct i2c_peripheral samsung_series_5_peripherals[] __initdata = {284 /* Light Sensor. */285 {286 .board_info = {287 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),288 },289 .type = I2C_ADAPTER_SMBUS,290 },291};292DECLARE_CROS_LAPTOP(samsung_series_5);293 294static const int chromebook_pixel_tp_keys[] __initconst = {295 KEY_RESERVED,296 KEY_RESERVED,297 KEY_RESERVED,298 KEY_RESERVED,299 KEY_RESERVED,300 BTN_LEFT301};302 303static const struct property_entry304chromebook_pixel_trackpad_props[] __initconst = {305 PROPERTY_ENTRY_STRING("compatible", "atmel,maxtouch"),306 PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_pixel_tp_keys),307 { }308};309 310static const struct property_entry311chromebook_atmel_touchscreen_props[] __initconst = {312 PROPERTY_ENTRY_STRING("compatible", "atmel,maxtouch"),313 { }314};315 316static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = {317 /* Touch Screen. */318 {319 .board_info = {320 I2C_BOARD_INFO("atmel_mxt_ts",321 ATMEL_TS_I2C_ADDR),322 .flags = I2C_CLIENT_WAKE,323 },324 .dmi_name = "touchscreen",325 .irqflags = IRQF_TRIGGER_FALLING,326 .type = I2C_ADAPTER_PANEL,327 .alt_addr = ATMEL_TS_I2C_BL_ADDR,328 .properties = chromebook_atmel_touchscreen_props,329 },330 /* Touchpad. */331 {332 .board_info = {333 I2C_BOARD_INFO("atmel_mxt_tp",334 ATMEL_TP_I2C_ADDR),335 .flags = I2C_CLIENT_WAKE,336 },337 .dmi_name = "trackpad",338 .irqflags = IRQF_TRIGGER_FALLING,339 .type = I2C_ADAPTER_VGADDC,340 .alt_addr = ATMEL_TP_I2C_BL_ADDR,341 .properties = chromebook_pixel_trackpad_props,342 },343 /* Light Sensor. */344 {345 .board_info = {346 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),347 },348 .dmi_name = "lightsensor",349 .type = I2C_ADAPTER_PANEL,350 },351};352DECLARE_CROS_LAPTOP(chromebook_pixel);353 354static struct i2c_peripheral hp_chromebook_14_peripherals[] __initdata = {355 /* Touchpad. */356 {357 .board_info = {358 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),359 .flags = I2C_CLIENT_WAKE,360 },361 .dmi_name = "trackpad",362 .type = I2C_ADAPTER_DESIGNWARE,363 },364};365DECLARE_CROS_LAPTOP(hp_chromebook_14);366 367static struct i2c_peripheral dell_chromebook_11_peripherals[] __initdata = {368 /* Touchpad. */369 {370 .board_info = {371 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),372 .flags = I2C_CLIENT_WAKE,373 },374 .dmi_name = "trackpad",375 .type = I2C_ADAPTER_DESIGNWARE,376 },377 /* Elan Touchpad option. */378 {379 .board_info = {380 I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),381 .flags = I2C_CLIENT_WAKE,382 },383 .dmi_name = "trackpad",384 .type = I2C_ADAPTER_DESIGNWARE,385 },386};387DECLARE_CROS_LAPTOP(dell_chromebook_11);388 389static struct i2c_peripheral toshiba_cb35_peripherals[] __initdata = {390 /* Touchpad. */391 {392 .board_info = {393 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),394 .flags = I2C_CLIENT_WAKE,395 },396 .dmi_name = "trackpad",397 .type = I2C_ADAPTER_DESIGNWARE,398 },399};400DECLARE_CROS_LAPTOP(toshiba_cb35);401 402static struct i2c_peripheral acer_c7_chromebook_peripherals[] __initdata = {403 /* Touchpad. */404 {405 .board_info = {406 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),407 .flags = I2C_CLIENT_WAKE,408 },409 .dmi_name = "trackpad",410 .type = I2C_ADAPTER_SMBUS,411 },412};413DECLARE_CROS_LAPTOP(acer_c7_chromebook);414 415static struct i2c_peripheral acer_ac700_peripherals[] __initdata = {416 /* Light Sensor. */417 {418 .board_info = {419 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),420 },421 .type = I2C_ADAPTER_SMBUS,422 },423};424DECLARE_CROS_LAPTOP(acer_ac700);425 426static struct i2c_peripheral acer_c720_peripherals[] __initdata = {427 /* Touchscreen. */428 {429 .board_info = {430 I2C_BOARD_INFO("atmel_mxt_ts",431 ATMEL_TS_I2C_ADDR),432 .flags = I2C_CLIENT_WAKE,433 },434 .dmi_name = "touchscreen",435 .irqflags = IRQF_TRIGGER_FALLING,436 .type = I2C_ADAPTER_DESIGNWARE,437 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),438 .alt_addr = ATMEL_TS_I2C_BL_ADDR,439 .properties = chromebook_atmel_touchscreen_props,440 },441 /* Touchpad. */442 {443 .board_info = {444 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),445 .flags = I2C_CLIENT_WAKE,446 },447 .dmi_name = "trackpad",448 .type = I2C_ADAPTER_DESIGNWARE,449 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),450 },451 /* Elan Touchpad option. */452 {453 .board_info = {454 I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),455 .flags = I2C_CLIENT_WAKE,456 },457 .dmi_name = "trackpad",458 .type = I2C_ADAPTER_DESIGNWARE,459 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),460 },461 /* Light Sensor. */462 {463 .board_info = {464 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),465 },466 .dmi_name = "lightsensor",467 .type = I2C_ADAPTER_DESIGNWARE,468 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),469 },470};471DECLARE_CROS_LAPTOP(acer_c720);472 473static struct i2c_peripheral474hp_pavilion_14_chromebook_peripherals[] __initdata = {475 /* Touchpad. */476 {477 .board_info = {478 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),479 .flags = I2C_CLIENT_WAKE,480 },481 .dmi_name = "trackpad",482 .type = I2C_ADAPTER_SMBUS,483 },484};485DECLARE_CROS_LAPTOP(hp_pavilion_14_chromebook);486 487static struct i2c_peripheral cr48_peripherals[] __initdata = {488 /* Light Sensor. */489 {490 .board_info = {491 I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),492 },493 .type = I2C_ADAPTER_SMBUS,494 },495};496DECLARE_CROS_LAPTOP(cr48);497 498static const u32 samus_touchpad_buttons[] __initconst = {499 KEY_RESERVED,500 KEY_RESERVED,501 KEY_RESERVED,502 BTN_LEFT503};504 505static const struct property_entry samus_trackpad_props[] __initconst = {506 PROPERTY_ENTRY_STRING("compatible", "atmel,maxtouch"),507 PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", samus_touchpad_buttons),508 { }509};510 511static struct acpi_peripheral samus_peripherals[] __initdata = {512 /* Touchpad */513 {514 .hid = "ATML0000",515 .swnode = {516 .properties = samus_trackpad_props,517 },518 },519 /* Touchsceen */520 {521 .hid = "ATML0001",522 .swnode = {523 .properties = chromebook_atmel_touchscreen_props,524 },525 },526};527DECLARE_ACPI_CROS_LAPTOP(samus);528 529static struct acpi_peripheral generic_atmel_peripherals[] __initdata = {530 /* Touchpad */531 {532 .hid = "ATML0000",533 .swnode = {534 .properties = chromebook_pixel_trackpad_props,535 },536 },537 /* Touchsceen */538 {539 .hid = "ATML0001",540 .swnode = {541 .properties = chromebook_atmel_touchscreen_props,542 },543 },544};545DECLARE_ACPI_CROS_LAPTOP(generic_atmel);546 547static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {548 {549 .ident = "Samsung Series 5 550",550 .matches = {551 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),552 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),553 },554 .driver_data = (void *)&samsung_series_5_550,555 },556 {557 .ident = "Samsung Series 5",558 .matches = {559 DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),560 },561 .driver_data = (void *)&samsung_series_5,562 },563 {564 .ident = "Chromebook Pixel",565 .matches = {566 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),567 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),568 },569 .driver_data = (void *)&chromebook_pixel,570 },571 {572 .ident = "Wolf",573 .matches = {574 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),575 DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"),576 },577 .driver_data = (void *)&dell_chromebook_11,578 },579 {580 .ident = "HP Chromebook 14",581 .matches = {582 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),583 DMI_MATCH(DMI_PRODUCT_NAME, "Falco"),584 },585 .driver_data = (void *)&hp_chromebook_14,586 },587 {588 .ident = "Toshiba CB35",589 .matches = {590 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),591 DMI_MATCH(DMI_PRODUCT_NAME, "Leon"),592 },593 .driver_data = (void *)&toshiba_cb35,594 },595 {596 .ident = "Acer C7 Chromebook",597 .matches = {598 DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),599 },600 .driver_data = (void *)&acer_c7_chromebook,601 },602 {603 .ident = "Acer AC700",604 .matches = {605 DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),606 },607 .driver_data = (void *)&acer_ac700,608 },609 {610 .ident = "Acer C720",611 .matches = {612 DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),613 },614 .driver_data = (void *)&acer_c720,615 },616 {617 .ident = "HP Pavilion 14 Chromebook",618 .matches = {619 DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),620 },621 .driver_data = (void *)&hp_pavilion_14_chromebook,622 },623 {624 .ident = "Cr-48",625 .matches = {626 DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),627 },628 .driver_data = (void *)&cr48,629 },630 /* Devices with peripherals incompletely described in ACPI */631 {632 .ident = "Chromebook Pro",633 .matches = {634 DMI_MATCH(DMI_SYS_VENDOR, "Google"),635 DMI_MATCH(DMI_PRODUCT_NAME, "Caroline"),636 },637 .driver_data = (void *)&samus,638 },639 {640 .ident = "Google Pixel 2 (2015)",641 .matches = {642 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),643 DMI_MATCH(DMI_PRODUCT_NAME, "Samus"),644 },645 .driver_data = (void *)&samus,646 },647 {648 .ident = "Samsung Chromebook 3",649 .matches = {650 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),651 DMI_MATCH(DMI_PRODUCT_NAME, "Celes"),652 },653 .driver_data = (void *)&samus,654 },655 {656 /*657 * Other Chromebooks with Atmel touch controllers:658 * - Winky (touchpad)659 * - Clapper, Expresso, Rambi, Glimmer (touchscreen)660 */661 .ident = "Other Chromebook",662 .matches = {663 /*664 * This will match all Google devices, not only devices665 * with Atmel, but we will validate that the device666 * actually has matching peripherals.667 */668 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),669 },670 .driver_data = (void *)&generic_atmel,671 },672 { }673};674MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);675 676static int __init chromeos_laptop_scan_peripherals(struct device *dev, void *data)677{678 int error;679 680 if (dev->type == &i2c_adapter_type) {681 chromeos_laptop_check_adapter(to_i2c_adapter(dev));682 } else if (dev->type == &i2c_client_type) {683 if (chromeos_laptop_adjust_client(to_i2c_client(dev))) {684 /*685 * Now that we have needed properties re-trigger686 * driver probe in case driver was initialized687 * earlier and probe failed.688 */689 error = device_attach(dev);690 if (error < 0)691 dev_warn(dev,692 "%s: device_attach() failed: %d\n",693 __func__, error);694 }695 }696 697 return 0;698}699 700static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name)701{702 const struct dmi_device *dmi_dev;703 const struct dmi_dev_onboard *dev_data;704 705 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, dmi_name, NULL);706 if (!dmi_dev) {707 pr_err("failed to find DMI device '%s'\n", dmi_name);708 return -ENOENT;709 }710 711 dev_data = dmi_dev->device_data;712 if (!dev_data) {713 pr_err("failed to get data from DMI for '%s'\n", dmi_name);714 return -EINVAL;715 }716 717 return dev_data->instance;718}719 720static int __init chromeos_laptop_setup_irq(struct i2c_peripheral *i2c_dev)721{722 int irq;723 724 if (i2c_dev->dmi_name) {725 irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name);726 if (irq < 0)727 return irq;728 729 i2c_dev->irq_resource = (struct resource)730 DEFINE_RES_NAMED(irq, 1, NULL,731 IORESOURCE_IRQ | i2c_dev->irqflags);732 i2c_dev->board_info.resources = &i2c_dev->irq_resource;733 i2c_dev->board_info.num_resources = 1;734 }735 736 return 0;737}738 739static int __init740chromeos_laptop_prepare_i2c_peripherals(struct chromeos_laptop *cros_laptop,741 const struct chromeos_laptop *src)742{743 struct i2c_peripheral *i2c_peripherals;744 struct i2c_peripheral *i2c_dev;745 struct i2c_board_info *info;746 int i;747 int error;748 749 if (!src->num_i2c_peripherals)750 return 0;751 752 i2c_peripherals = kmemdup_array(src->i2c_peripherals,753 src->num_i2c_peripherals,754 sizeof(*i2c_peripherals), GFP_KERNEL);755 if (!i2c_peripherals)756 return -ENOMEM;757 758 for (i = 0; i < src->num_i2c_peripherals; i++) {759 i2c_dev = &i2c_peripherals[i];760 info = &i2c_dev->board_info;761 762 error = chromeos_laptop_setup_irq(i2c_dev);763 if (error)764 goto err_out;765 766 /* Create primary fwnode for the device - copies everything */767 if (i2c_dev->properties) {768 info->fwnode = fwnode_create_software_node(i2c_dev->properties, NULL);769 if (IS_ERR(info->fwnode)) {770 error = PTR_ERR(info->fwnode);771 goto err_out;772 }773 }774 }775 776 cros_laptop->i2c_peripherals = i2c_peripherals;777 cros_laptop->num_i2c_peripherals = src->num_i2c_peripherals;778 779 return 0;780 781err_out:782 while (--i >= 0) {783 i2c_dev = &i2c_peripherals[i];784 info = &i2c_dev->board_info;785 if (!IS_ERR_OR_NULL(info->fwnode))786 fwnode_remove_software_node(info->fwnode);787 }788 kfree(i2c_peripherals);789 return error;790}791 792static int __init793chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop,794 const struct chromeos_laptop *src)795{796 struct acpi_peripheral *acpi_peripherals;797 struct acpi_peripheral *acpi_dev;798 const struct acpi_peripheral *src_dev;799 int n_peripherals = 0;800 int i;801 int error;802 803 for (i = 0; i < src->num_acpi_peripherals; i++) {804 if (acpi_dev_present(src->acpi_peripherals[i].hid, NULL, -1))805 n_peripherals++;806 }807 808 if (!n_peripherals)809 return 0;810 811 acpi_peripherals = kcalloc(n_peripherals,812 sizeof(*src->acpi_peripherals),813 GFP_KERNEL);814 if (!acpi_peripherals)815 return -ENOMEM;816 817 acpi_dev = acpi_peripherals;818 for (i = 0; i < src->num_acpi_peripherals; i++) {819 src_dev = &src->acpi_peripherals[i];820 if (!acpi_dev_present(src_dev->hid, NULL, -1))821 continue;822 823 *acpi_dev = *src_dev;824 825 /* We need to deep-copy properties */826 if (src_dev->swnode.properties) {827 acpi_dev->swnode.properties =828 property_entries_dup(src_dev->swnode.properties);829 if (IS_ERR(acpi_dev->swnode.properties)) {830 error = PTR_ERR(acpi_dev->swnode.properties);831 goto err_out;832 }833 }834 835 acpi_dev++;836 }837 838 cros_laptop->acpi_peripherals = acpi_peripherals;839 cros_laptop->num_acpi_peripherals = n_peripherals;840 841 return 0;842 843err_out:844 while (--i >= 0) {845 acpi_dev = &acpi_peripherals[i];846 if (!IS_ERR_OR_NULL(acpi_dev->swnode.properties))847 property_entries_free(acpi_dev->swnode.properties);848 }849 850 kfree(acpi_peripherals);851 return error;852}853 854static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop)855{856 const struct acpi_peripheral *acpi_dev;857 struct i2c_peripheral *i2c_dev;858 int i;859 860 for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {861 i2c_dev = &cros_laptop->i2c_peripherals[i];862 i2c_unregister_device(i2c_dev->client);863 }864 865 for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) {866 acpi_dev = &cros_laptop->acpi_peripherals[i];867 868 if (acpi_dev->client)869 device_remove_software_node(&acpi_dev->client->dev);870 871 property_entries_free(acpi_dev->swnode.properties);872 }873 874 kfree(cros_laptop->i2c_peripherals);875 kfree(cros_laptop->acpi_peripherals);876 kfree(cros_laptop);877}878 879static struct chromeos_laptop * __init880chromeos_laptop_prepare(const struct chromeos_laptop *src)881{882 struct chromeos_laptop *cros_laptop;883 int error;884 885 cros_laptop = kzalloc(sizeof(*cros_laptop), GFP_KERNEL);886 if (!cros_laptop)887 return ERR_PTR(-ENOMEM);888 889 error = chromeos_laptop_prepare_i2c_peripherals(cros_laptop, src);890 if (!error)891 error = chromeos_laptop_prepare_acpi_peripherals(cros_laptop,892 src);893 894 if (error) {895 chromeos_laptop_destroy(cros_laptop);896 return ERR_PTR(error);897 }898 899 return cros_laptop;900}901 902static int __init chromeos_laptop_init(void)903{904 const struct dmi_system_id *dmi_id;905 int error;906 907 dmi_id = dmi_first_match(chromeos_laptop_dmi_table);908 if (!dmi_id) {909 pr_debug("unsupported system\n");910 return -ENODEV;911 }912 913 pr_debug("DMI Matched %s\n", dmi_id->ident);914 915 cros_laptop = chromeos_laptop_prepare((void *)dmi_id->driver_data);916 if (IS_ERR(cros_laptop))917 return PTR_ERR(cros_laptop);918 919 if (!cros_laptop->num_i2c_peripherals &&920 !cros_laptop->num_acpi_peripherals) {921 pr_debug("no relevant devices detected\n");922 error = -ENODEV;923 goto err_destroy_cros_laptop;924 }925 926 error = bus_register_notifier(&i2c_bus_type,927 &chromeos_laptop_i2c_notifier);928 if (error) {929 pr_err("failed to register i2c bus notifier: %d\n",930 error);931 goto err_destroy_cros_laptop;932 }933 934 /*935 * Scan adapters that have been registered and clients that have936 * been created before we installed the notifier to make sure937 * we do not miss any devices.938 */939 i2c_for_each_dev(NULL, chromeos_laptop_scan_peripherals);940 941 return 0;942 943err_destroy_cros_laptop:944 chromeos_laptop_destroy(cros_laptop);945 return error;946}947 948static void __exit chromeos_laptop_exit(void)949{950 bus_unregister_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier);951 chromeos_laptop_destroy(cros_laptop);952}953 954module_init(chromeos_laptop_init);955module_exit(chromeos_laptop_exit);956 957MODULE_DESCRIPTION("Chrome OS Laptop driver");958MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");959MODULE_LICENSE("GPL");960