linux/drivers/platform/chrome/chromeos_laptop.c
<<
>>
Prefs
   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
  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 because
  69         * 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 primary
  90         * address we scan secondary addresses. In any case the client
  91         * 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_END
 100                };
 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        else
 214                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_LEFT
 301};
 302
 303static const struct property_entry
 304chromebook_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_entry
 311chromebook_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_peripheral
 474hp_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_LEFT
 503};
 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 devices
 665                         * with Atmel, but we will validate that the device
 666                         * 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-trigger
 686                         * driver probe in case driver was initialized
 687                         * 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 __init
 740chromeos_laptop_prepare_i2c_peripherals(struct chromeos_laptop *cros_laptop,
 741                                        const struct chromeos_laptop *src)
 742{
 743        struct i2c_peripheral *i2c_dev;
 744        struct i2c_board_info *info;
 745        int i;
 746        int error;
 747
 748        if (!src->num_i2c_peripherals)
 749                return 0;
 750
 751        cros_laptop->i2c_peripherals = kmemdup(src->i2c_peripherals,
 752                                               src->num_i2c_peripherals *
 753                                                sizeof(*src->i2c_peripherals),
 754                                               GFP_KERNEL);
 755        if (!cros_laptop->i2c_peripherals)
 756                return -ENOMEM;
 757
 758        cros_laptop->num_i2c_peripherals = src->num_i2c_peripherals;
 759
 760        for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
 761                i2c_dev = &cros_laptop->i2c_peripherals[i];
 762                info = &i2c_dev->board_info;
 763
 764                error = chromeos_laptop_setup_irq(i2c_dev);
 765                if (error)
 766                        goto err_out;
 767
 768                /* Create primary fwnode for the device - copies everything */
 769                if (i2c_dev->properties) {
 770                        info->fwnode = fwnode_create_software_node(i2c_dev->properties, NULL);
 771                        if (IS_ERR(info->fwnode)) {
 772                                error = PTR_ERR(info->fwnode);
 773                                goto err_out;
 774                        }
 775                }
 776        }
 777
 778        return 0;
 779
 780err_out:
 781        while (--i >= 0) {
 782                i2c_dev = &cros_laptop->i2c_peripherals[i];
 783                info = &i2c_dev->board_info;
 784                if (!IS_ERR_OR_NULL(info->fwnode))
 785                        fwnode_remove_software_node(info->fwnode);
 786        }
 787        kfree(cros_laptop->i2c_peripherals);
 788        return error;
 789}
 790
 791static int __init
 792chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop,
 793                                        const struct chromeos_laptop *src)
 794{
 795        struct acpi_peripheral *acpi_peripherals;
 796        struct acpi_peripheral *acpi_dev;
 797        const struct acpi_peripheral *src_dev;
 798        int n_peripherals = 0;
 799        int i;
 800        int error;
 801
 802        for (i = 0; i < src->num_acpi_peripherals; i++) {
 803                if (acpi_dev_present(src->acpi_peripherals[i].hid, NULL, -1))
 804                        n_peripherals++;
 805        }
 806
 807        if (!n_peripherals)
 808                return 0;
 809
 810        acpi_peripherals = kcalloc(n_peripherals,
 811                                   sizeof(*src->acpi_peripherals),
 812                                   GFP_KERNEL);
 813        if (!acpi_peripherals)
 814                return -ENOMEM;
 815
 816        acpi_dev = acpi_peripherals;
 817        for (i = 0; i < src->num_acpi_peripherals; i++) {
 818                src_dev = &src->acpi_peripherals[i];
 819                if (!acpi_dev_present(src_dev->hid, NULL, -1))
 820                        continue;
 821
 822                *acpi_dev = *src_dev;
 823
 824                /* We need to deep-copy properties */
 825                if (src_dev->swnode.properties) {
 826                        acpi_dev->swnode.properties =
 827                                property_entries_dup(src_dev->swnode.properties);
 828                        if (IS_ERR(acpi_dev->swnode.properties)) {
 829                                error = PTR_ERR(acpi_dev->swnode.properties);
 830                                goto err_out;
 831                        }
 832                }
 833
 834                acpi_dev++;
 835        }
 836
 837        cros_laptop->acpi_peripherals = acpi_peripherals;
 838        cros_laptop->num_acpi_peripherals = n_peripherals;
 839
 840        return 0;
 841
 842err_out:
 843        while (--i >= 0) {
 844                acpi_dev = &acpi_peripherals[i];
 845                if (!IS_ERR_OR_NULL(acpi_dev->swnode.properties))
 846                        property_entries_free(acpi_dev->swnode.properties);
 847        }
 848
 849        kfree(acpi_peripherals);
 850        return error;
 851}
 852
 853static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop)
 854{
 855        const struct acpi_peripheral *acpi_dev;
 856        struct i2c_peripheral *i2c_dev;
 857        int i;
 858
 859        for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
 860                i2c_dev = &cros_laptop->i2c_peripherals[i];
 861                i2c_unregister_device(i2c_dev->client);
 862        }
 863
 864        for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) {
 865                acpi_dev = &cros_laptop->acpi_peripherals[i];
 866
 867                if (acpi_dev->client)
 868                        device_remove_software_node(&acpi_dev->client->dev);
 869
 870                property_entries_free(acpi_dev->swnode.properties);
 871        }
 872
 873        kfree(cros_laptop->i2c_peripherals);
 874        kfree(cros_laptop->acpi_peripherals);
 875        kfree(cros_laptop);
 876}
 877
 878static struct chromeos_laptop * __init
 879chromeos_laptop_prepare(const struct chromeos_laptop *src)
 880{
 881        struct chromeos_laptop *cros_laptop;
 882        int error;
 883
 884        cros_laptop = kzalloc(sizeof(*cros_laptop), GFP_KERNEL);
 885        if (!cros_laptop)
 886                return ERR_PTR(-ENOMEM);
 887
 888        error = chromeos_laptop_prepare_i2c_peripherals(cros_laptop, src);
 889        if (!error)
 890                error = chromeos_laptop_prepare_acpi_peripherals(cros_laptop,
 891                                                                 src);
 892
 893        if (error) {
 894                chromeos_laptop_destroy(cros_laptop);
 895                return ERR_PTR(error);
 896        }
 897
 898        return cros_laptop;
 899}
 900
 901static int __init chromeos_laptop_init(void)
 902{
 903        const struct dmi_system_id *dmi_id;
 904        int error;
 905
 906        dmi_id = dmi_first_match(chromeos_laptop_dmi_table);
 907        if (!dmi_id) {
 908                pr_debug("unsupported system\n");
 909                return -ENODEV;
 910        }
 911
 912        pr_debug("DMI Matched %s\n", dmi_id->ident);
 913
 914        cros_laptop = chromeos_laptop_prepare((void *)dmi_id->driver_data);
 915        if (IS_ERR(cros_laptop))
 916                return PTR_ERR(cros_laptop);
 917
 918        if (!cros_laptop->num_i2c_peripherals &&
 919            !cros_laptop->num_acpi_peripherals) {
 920                pr_debug("no relevant devices detected\n");
 921                error = -ENODEV;
 922                goto err_destroy_cros_laptop;
 923        }
 924
 925        error = bus_register_notifier(&i2c_bus_type,
 926                                      &chromeos_laptop_i2c_notifier);
 927        if (error) {
 928                pr_err("failed to register i2c bus notifier: %d\n",
 929                       error);
 930                goto err_destroy_cros_laptop;
 931        }
 932
 933        /*
 934         * Scan adapters that have been registered and clients that have
 935         * been created before we installed the notifier to make sure
 936         * we do not miss any devices.
 937         */
 938        i2c_for_each_dev(NULL, chromeos_laptop_scan_peripherals);
 939
 940        return 0;
 941
 942err_destroy_cros_laptop:
 943        chromeos_laptop_destroy(cros_laptop);
 944        return error;
 945}
 946
 947static void __exit chromeos_laptop_exit(void)
 948{
 949        bus_unregister_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier);
 950        chromeos_laptop_destroy(cros_laptop);
 951}
 952
 953module_init(chromeos_laptop_init);
 954module_exit(chromeos_laptop_exit);
 955
 956MODULE_DESCRIPTION("Chrome OS Laptop driver");
 957MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
 958MODULE_LICENSE("GPL");
 959