linux/drivers/platform/chrome/chromeos_laptop.c
<<
>>
Prefs
   1/*
   2 *  chromeos_laptop.c - Driver to instantiate Chromebook i2c/smbus devices.
   3 *
   4 *  Author : Benson Leung <bleung@chromium.org>
   5 *
   6 *  Copyright (C) 2012 Google, Inc.
   7 *
   8 *  This program is free software; you can redistribute it and/or modify
   9 *  it under the terms of the GNU General Public License as published by
  10 *  the Free Software Foundation; either version 2 of the License, or
  11 *  (at your option) any later version.
  12 *
  13 *  This program is distributed in the hope that it will be useful,
  14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 *  GNU General Public License for more details.
  17 *
  18 *  You should have received a copy of the GNU General Public License
  19 *  along with this program; if not, write to the Free Software
  20 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21 *
  22 */
  23
  24#include <linux/dmi.h>
  25#include <linux/i2c.h>
  26#include <linux/platform_data/atmel_mxt_ts.h>
  27#include <linux/input.h>
  28#include <linux/interrupt.h>
  29#include <linux/module.h>
  30#include <linux/platform_device.h>
  31
  32#define ATMEL_TP_I2C_ADDR       0x4b
  33#define ATMEL_TP_I2C_BL_ADDR    0x25
  34#define ATMEL_TS_I2C_ADDR       0x4a
  35#define ATMEL_TS_I2C_BL_ADDR    0x26
  36#define CYAPA_TP_I2C_ADDR       0x67
  37#define ISL_ALS_I2C_ADDR        0x44
  38#define TAOS_ALS_I2C_ADDR       0x29
  39
  40#define MAX_I2C_DEVICE_DEFERRALS        5
  41
  42static struct i2c_client *als;
  43static struct i2c_client *tp;
  44static struct i2c_client *ts;
  45
  46static const char *i2c_adapter_names[] = {
  47        "SMBus I801 adapter",
  48        "i915 gmbus vga",
  49        "i915 gmbus panel",
  50        "Synopsys DesignWare I2C adapter",
  51        "Synopsys DesignWare I2C adapter",
  52};
  53
  54/* Keep this enum consistent with i2c_adapter_names */
  55enum i2c_adapter_type {
  56        I2C_ADAPTER_SMBUS = 0,
  57        I2C_ADAPTER_VGADDC,
  58        I2C_ADAPTER_PANEL,
  59        I2C_ADAPTER_DESIGNWARE_0,
  60        I2C_ADAPTER_DESIGNWARE_1,
  61};
  62
  63enum i2c_peripheral_state {
  64        UNPROBED = 0,
  65        PROBED,
  66        TIMEDOUT,
  67};
  68
  69struct i2c_peripheral {
  70        int (*add)(enum i2c_adapter_type type);
  71        enum i2c_adapter_type type;
  72        enum i2c_peripheral_state state;
  73        int tries;
  74};
  75
  76#define MAX_I2C_PERIPHERALS 3
  77
  78struct chromeos_laptop {
  79        struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS];
  80};
  81
  82static struct chromeos_laptop *cros_laptop;
  83
  84static struct i2c_board_info cyapa_device = {
  85        I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
  86        .flags          = I2C_CLIENT_WAKE,
  87};
  88
  89static struct i2c_board_info isl_als_device = {
  90        I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
  91};
  92
  93static struct i2c_board_info tsl2583_als_device = {
  94        I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
  95};
  96
  97static struct i2c_board_info tsl2563_als_device = {
  98        I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
  99};
 100
 101static int mxt_t19_keys[] = {
 102        KEY_RESERVED,
 103        KEY_RESERVED,
 104        KEY_RESERVED,
 105        KEY_RESERVED,
 106        KEY_RESERVED,
 107        BTN_LEFT
 108};
 109
 110static struct mxt_platform_data atmel_224s_tp_platform_data = {
 111        .irqflags               = IRQF_TRIGGER_FALLING,
 112        .t19_num_keys           = ARRAY_SIZE(mxt_t19_keys),
 113        .t19_keymap             = mxt_t19_keys,
 114        .suspend_mode           = MXT_SUSPEND_T9_CTRL,
 115};
 116
 117static struct i2c_board_info atmel_224s_tp_device = {
 118        I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR),
 119        .platform_data = &atmel_224s_tp_platform_data,
 120        .flags          = I2C_CLIENT_WAKE,
 121};
 122
 123static struct mxt_platform_data atmel_1664s_platform_data = {
 124        .irqflags               = IRQF_TRIGGER_FALLING,
 125        .suspend_mode           = MXT_SUSPEND_T9_CTRL,
 126};
 127
 128static struct i2c_board_info atmel_1664s_device = {
 129        I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR),
 130        .platform_data = &atmel_1664s_platform_data,
 131        .flags          = I2C_CLIENT_WAKE,
 132};
 133
 134static struct i2c_client *__add_probed_i2c_device(
 135                const char *name,
 136                int bus,
 137                struct i2c_board_info *info,
 138                const unsigned short *alt_addr_list)
 139{
 140        const struct dmi_device *dmi_dev;
 141        const struct dmi_dev_onboard *dev_data;
 142        struct i2c_adapter *adapter;
 143        struct i2c_client *client = NULL;
 144        const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
 145
 146        if (bus < 0)
 147                return NULL;
 148        /*
 149         * If a name is specified, look for irq platform information stashed
 150         * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware.
 151         */
 152        if (name) {
 153                dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
 154                if (!dmi_dev) {
 155                        pr_err("%s failed to dmi find device %s.\n",
 156                               __func__,
 157                               name);
 158                        return NULL;
 159                }
 160                dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
 161                if (!dev_data) {
 162                        pr_err("%s failed to get data from dmi for %s.\n",
 163                               __func__, name);
 164                        return NULL;
 165                }
 166                info->irq = dev_data->instance;
 167        }
 168
 169        adapter = i2c_get_adapter(bus);
 170        if (!adapter) {
 171                pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
 172                return NULL;
 173        }
 174
 175        /*
 176         * Add the i2c device. If we can't detect it at the primary
 177         * address we scan secondary addresses. In any case the client
 178         * structure gets assigned primary address.
 179         */
 180        client = i2c_new_probed_device(adapter, info, addr_list, NULL);
 181        if (!client && alt_addr_list) {
 182                struct i2c_board_info dummy_info = {
 183                        I2C_BOARD_INFO("dummy", info->addr),
 184                };
 185                struct i2c_client *dummy;
 186
 187                dummy = i2c_new_probed_device(adapter, &dummy_info,
 188                                              alt_addr_list, NULL);
 189                if (dummy) {
 190                        pr_debug("%s %d-%02x is probed at %02x\n",
 191                                  __func__, bus, info->addr, dummy->addr);
 192                        i2c_unregister_device(dummy);
 193                        client = i2c_new_device(adapter, info);
 194                }
 195        }
 196
 197        if (!client)
 198                pr_notice("%s failed to register device %d-%02x\n",
 199                          __func__, bus, info->addr);
 200        else
 201                pr_debug("%s added i2c device %d-%02x\n",
 202                         __func__, bus, info->addr);
 203
 204        i2c_put_adapter(adapter);
 205        return client;
 206}
 207
 208struct i2c_lookup {
 209        const char *name;
 210        int instance;
 211        int n;
 212};
 213
 214static int __find_i2c_adap(struct device *dev, void *data)
 215{
 216        struct i2c_lookup *lookup = data;
 217        static const char *prefix = "i2c-";
 218        struct i2c_adapter *adapter;
 219
 220        if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
 221                return 0;
 222        adapter = to_i2c_adapter(dev);
 223        if (strncmp(adapter->name, lookup->name, strlen(lookup->name)) == 0 &&
 224            lookup->n++ == lookup->instance)
 225                return 1;
 226        return 0;
 227}
 228
 229static int find_i2c_adapter_num(enum i2c_adapter_type type)
 230{
 231        struct device *dev = NULL;
 232        struct i2c_adapter *adapter;
 233        struct i2c_lookup lookup;
 234
 235        memset(&lookup, 0, sizeof(lookup));
 236        lookup.name = i2c_adapter_names[type];
 237        lookup.instance = (type == I2C_ADAPTER_DESIGNWARE_1) ? 1 : 0;
 238
 239        /* find the adapter by name */
 240        dev = bus_find_device(&i2c_bus_type, NULL, &lookup, __find_i2c_adap);
 241        if (!dev) {
 242                /* Adapters may appear later. Deferred probing will retry */
 243                pr_notice("%s: i2c adapter %s not found on system.\n", __func__,
 244                          lookup.name);
 245                return -ENODEV;
 246        }
 247        adapter = to_i2c_adapter(dev);
 248        return adapter->nr;
 249}
 250
 251/*
 252 * Takes a list of addresses in addrs as such :
 253 * { addr1, ... , addrn, I2C_CLIENT_END };
 254 * add_probed_i2c_device will use i2c_new_probed_device
 255 * and probe for devices at all of the addresses listed.
 256 * Returns NULL if no devices found.
 257 * See Documentation/i2c/instantiating-devices for more information.
 258 */
 259static struct i2c_client *add_probed_i2c_device(
 260                const char *name,
 261                enum i2c_adapter_type type,
 262                struct i2c_board_info *info,
 263                const unsigned short *addrs)
 264{
 265        return __add_probed_i2c_device(name,
 266                                       find_i2c_adapter_num(type),
 267                                       info,
 268                                       addrs);
 269}
 270
 271/*
 272 * Probes for a device at a single address, the one provided by
 273 * info->addr.
 274 * Returns NULL if no device found.
 275 */
 276static struct i2c_client *add_i2c_device(const char *name,
 277                                                enum i2c_adapter_type type,
 278                                                struct i2c_board_info *info)
 279{
 280        return __add_probed_i2c_device(name,
 281                                       find_i2c_adapter_num(type),
 282                                       info,
 283                                       NULL);
 284}
 285
 286static int setup_cyapa_tp(enum i2c_adapter_type type)
 287{
 288        if (tp)
 289                return 0;
 290
 291        /* add cyapa touchpad */
 292        tp = add_i2c_device("trackpad", type, &cyapa_device);
 293        return (!tp) ? -EAGAIN : 0;
 294}
 295
 296static int setup_atmel_224s_tp(enum i2c_adapter_type type)
 297{
 298        const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR,
 299                                             I2C_CLIENT_END };
 300        if (tp)
 301                return 0;
 302
 303        /* add atmel mxt touchpad */
 304        tp = add_probed_i2c_device("trackpad", type,
 305                                   &atmel_224s_tp_device, addr_list);
 306        return (!tp) ? -EAGAIN : 0;
 307}
 308
 309static int setup_atmel_1664s_ts(enum i2c_adapter_type type)
 310{
 311        const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR,
 312                                             I2C_CLIENT_END };
 313        if (ts)
 314                return 0;
 315
 316        /* add atmel mxt touch device */
 317        ts = add_probed_i2c_device("touchscreen", type,
 318                                   &atmel_1664s_device, addr_list);
 319        return (!ts) ? -EAGAIN : 0;
 320}
 321
 322static int setup_isl29018_als(enum i2c_adapter_type type)
 323{
 324        if (als)
 325                return 0;
 326
 327        /* add isl29018 light sensor */
 328        als = add_i2c_device("lightsensor", type, &isl_als_device);
 329        return (!als) ? -EAGAIN : 0;
 330}
 331
 332static int setup_tsl2583_als(enum i2c_adapter_type type)
 333{
 334        if (als)
 335                return 0;
 336
 337        /* add tsl2583 light sensor */
 338        als = add_i2c_device(NULL, type, &tsl2583_als_device);
 339        return (!als) ? -EAGAIN : 0;
 340}
 341
 342static int setup_tsl2563_als(enum i2c_adapter_type type)
 343{
 344        if (als)
 345                return 0;
 346
 347        /* add tsl2563 light sensor */
 348        als = add_i2c_device(NULL, type, &tsl2563_als_device);
 349        return (!als) ? -EAGAIN : 0;
 350}
 351
 352static int __init chromeos_laptop_dmi_matched(const struct dmi_system_id *id)
 353{
 354        cros_laptop = (void *)id->driver_data;
 355        pr_debug("DMI Matched %s.\n", id->ident);
 356
 357        /* Indicate to dmi_scan that processing is done. */
 358        return 1;
 359}
 360
 361static int chromeos_laptop_probe(struct platform_device *pdev)
 362{
 363        int i;
 364        int ret = 0;
 365
 366        for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
 367                struct i2c_peripheral *i2c_dev;
 368
 369                i2c_dev = &cros_laptop->i2c_peripherals[i];
 370
 371                /* No more peripherals. */
 372                if (i2c_dev->add == NULL)
 373                        break;
 374
 375                if (i2c_dev->state == TIMEDOUT || i2c_dev->state == PROBED)
 376                        continue;
 377
 378                /*
 379                 * Check that the i2c adapter is present.
 380                 * -EPROBE_DEFER if missing as the adapter may appear much
 381                 * later.
 382                 */
 383                if (find_i2c_adapter_num(i2c_dev->type) == -ENODEV) {
 384                        ret = -EPROBE_DEFER;
 385                        continue;
 386                }
 387
 388                /* Add the device. */
 389                if (i2c_dev->add(i2c_dev->type) == -EAGAIN) {
 390                        /*
 391                         * Set -EPROBE_DEFER a limited num of times
 392                         * if device is not successfully added.
 393                         */
 394                        if (++i2c_dev->tries < MAX_I2C_DEVICE_DEFERRALS) {
 395                                ret = -EPROBE_DEFER;
 396                        } else {
 397                                /* Ran out of tries. */
 398                                pr_notice("%s: Ran out of tries for device.\n",
 399                                          __func__);
 400                                i2c_dev->state = TIMEDOUT;
 401                        }
 402                } else {
 403                        i2c_dev->state = PROBED;
 404                }
 405        }
 406
 407        return ret;
 408}
 409
 410static struct chromeos_laptop samsung_series_5_550 = {
 411        .i2c_peripherals = {
 412                /* Touchpad. */
 413                { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
 414                /* Light Sensor. */
 415                { .add = setup_isl29018_als, I2C_ADAPTER_SMBUS },
 416        },
 417};
 418
 419static struct chromeos_laptop samsung_series_5 = {
 420        .i2c_peripherals = {
 421                /* Light Sensor. */
 422                { .add = setup_tsl2583_als, I2C_ADAPTER_SMBUS },
 423        },
 424};
 425
 426static struct chromeos_laptop chromebook_pixel = {
 427        .i2c_peripherals = {
 428                /* Touch Screen. */
 429                { .add = setup_atmel_1664s_ts, I2C_ADAPTER_PANEL },
 430                /* Touchpad. */
 431                { .add = setup_atmel_224s_tp, I2C_ADAPTER_VGADDC },
 432                /* Light Sensor. */
 433                { .add = setup_isl29018_als, I2C_ADAPTER_PANEL },
 434        },
 435};
 436
 437static struct chromeos_laptop hp_chromebook_14 = {
 438        .i2c_peripherals = {
 439                /* Touchpad. */
 440                { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
 441        },
 442};
 443
 444static struct chromeos_laptop dell_chromebook_11 = {
 445        .i2c_peripherals = {
 446                /* Touchpad. */
 447                { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
 448        },
 449};
 450
 451static struct chromeos_laptop toshiba_cb35 = {
 452        .i2c_peripherals = {
 453                /* Touchpad. */
 454                { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
 455        },
 456};
 457
 458static struct chromeos_laptop acer_c7_chromebook = {
 459        .i2c_peripherals = {
 460                /* Touchpad. */
 461                { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
 462        },
 463};
 464
 465static struct chromeos_laptop acer_ac700 = {
 466        .i2c_peripherals = {
 467                /* Light Sensor. */
 468                { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS },
 469        },
 470};
 471
 472static struct chromeos_laptop acer_c720 = {
 473        .i2c_peripherals = {
 474                /* Touchscreen. */
 475                { .add = setup_atmel_1664s_ts, I2C_ADAPTER_DESIGNWARE_1 },
 476                /* Touchpad. */
 477                { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
 478                /* Light Sensor. */
 479                { .add = setup_isl29018_als, I2C_ADAPTER_DESIGNWARE_1 },
 480        },
 481};
 482
 483static struct chromeos_laptop hp_pavilion_14_chromebook = {
 484        .i2c_peripherals = {
 485                /* Touchpad. */
 486                { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
 487        },
 488};
 489
 490static struct chromeos_laptop cr48 = {
 491        .i2c_peripherals = {
 492                /* Light Sensor. */
 493                { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS },
 494        },
 495};
 496
 497#define _CBDD(board_) \
 498        .callback = chromeos_laptop_dmi_matched, \
 499        .driver_data = (void *)&board_
 500
 501static struct dmi_system_id chromeos_laptop_dmi_table[] __initdata = {
 502        {
 503                .ident = "Samsung Series 5 550",
 504                .matches = {
 505                        DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
 506                        DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
 507                },
 508                _CBDD(samsung_series_5_550),
 509        },
 510        {
 511                .ident = "Samsung Series 5",
 512                .matches = {
 513                        DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
 514                },
 515                _CBDD(samsung_series_5),
 516        },
 517        {
 518                .ident = "Chromebook Pixel",
 519                .matches = {
 520                        DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
 521                        DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
 522                },
 523                _CBDD(chromebook_pixel),
 524        },
 525        {
 526                .ident = "Wolf",
 527                .matches = {
 528                        DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
 529                        DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"),
 530                },
 531                _CBDD(dell_chromebook_11),
 532        },
 533        {
 534                .ident = "HP Chromebook 14",
 535                .matches = {
 536                        DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
 537                        DMI_MATCH(DMI_PRODUCT_NAME, "Falco"),
 538                },
 539                _CBDD(hp_chromebook_14),
 540        },
 541        {
 542                .ident = "Toshiba CB35",
 543                .matches = {
 544                        DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
 545                        DMI_MATCH(DMI_PRODUCT_NAME, "Leon"),
 546                },
 547                _CBDD(toshiba_cb35),
 548        },
 549        {
 550                .ident = "Acer C7 Chromebook",
 551                .matches = {
 552                        DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
 553                },
 554                _CBDD(acer_c7_chromebook),
 555        },
 556        {
 557                .ident = "Acer AC700",
 558                .matches = {
 559                        DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
 560                },
 561                _CBDD(acer_ac700),
 562        },
 563        {
 564                .ident = "Acer C720",
 565                .matches = {
 566                        DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
 567                },
 568                _CBDD(acer_c720),
 569        },
 570        {
 571                .ident = "HP Pavilion 14 Chromebook",
 572                .matches = {
 573                        DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
 574                },
 575                _CBDD(hp_pavilion_14_chromebook),
 576        },
 577        {
 578                .ident = "Cr-48",
 579                .matches = {
 580                        DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
 581                },
 582                _CBDD(cr48),
 583        },
 584        { }
 585};
 586MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
 587
 588static struct platform_device *cros_platform_device;
 589
 590static struct platform_driver cros_platform_driver = {
 591        .driver = {
 592                .name = "chromeos_laptop",
 593        },
 594        .probe = chromeos_laptop_probe,
 595};
 596
 597static int __init chromeos_laptop_init(void)
 598{
 599        int ret;
 600
 601        if (!dmi_check_system(chromeos_laptop_dmi_table)) {
 602                pr_debug("%s unsupported system.\n", __func__);
 603                return -ENODEV;
 604        }
 605
 606        ret = platform_driver_register(&cros_platform_driver);
 607        if (ret)
 608                return ret;
 609
 610        cros_platform_device = platform_device_alloc("chromeos_laptop", -1);
 611        if (!cros_platform_device) {
 612                ret = -ENOMEM;
 613                goto fail_platform_device1;
 614        }
 615
 616        ret = platform_device_add(cros_platform_device);
 617        if (ret)
 618                goto fail_platform_device2;
 619
 620        return 0;
 621
 622fail_platform_device2:
 623        platform_device_put(cros_platform_device);
 624fail_platform_device1:
 625        platform_driver_unregister(&cros_platform_driver);
 626        return ret;
 627}
 628
 629static void __exit chromeos_laptop_exit(void)
 630{
 631        if (als)
 632                i2c_unregister_device(als);
 633        if (tp)
 634                i2c_unregister_device(tp);
 635        if (ts)
 636                i2c_unregister_device(ts);
 637
 638        platform_device_unregister(cros_platform_device);
 639        platform_driver_unregister(&cros_platform_driver);
 640}
 641
 642module_init(chromeos_laptop_init);
 643module_exit(chromeos_laptop_exit);
 644
 645MODULE_DESCRIPTION("Chrome OS Laptop driver");
 646MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
 647MODULE_LICENSE("GPL");
 648