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