root/drivers/platform/chrome/chromeos_laptop.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. chromes_laptop_instantiate_i2c_device
  2. chromeos_laptop_match_adapter_devid
  3. chromeos_laptop_check_adapter
  4. chromeos_laptop_adjust_client
  5. chromeos_laptop_detach_i2c_client
  6. chromeos_laptop_i2c_notifier_call
  7. chromeos_laptop_scan_peripherals
  8. chromeos_laptop_get_irq_from_dmi
  9. chromeos_laptop_setup_irq
  10. chromeos_laptop_prepare_i2c_peripherals
  11. chromeos_laptop_prepare_acpi_peripherals
  12. chromeos_laptop_destroy
  13. chromeos_laptop_prepare
  14. chromeos_laptop_init
  15. chromeos_laptop_exit

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

/* [<][>][^][v][top][bottom][index][help] */