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