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 42 static struct i2c_client *als; 43 static struct i2c_client *tp; 44 static struct i2c_client *ts; 45 46 static 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 */ 55 enum 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 63 enum i2c_peripheral_state { 64 UNPROBED = 0, 65 PROBED, 66 TIMEDOUT, 67 }; 68 69 struct 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 78 struct chromeos_laptop { 79 struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS]; 80 }; 81 82 static struct chromeos_laptop *cros_laptop; 83 84 static struct i2c_board_info cyapa_device = { 85 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), 86 .flags = I2C_CLIENT_WAKE, 87 }; 88 89 static struct i2c_board_info isl_als_device = { 90 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), 91 }; 92 93 static struct i2c_board_info tsl2583_als_device = { 94 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), 95 }; 96 97 static struct i2c_board_info tsl2563_als_device = { 98 I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), 99 }; 100 101 static int mxt_t19_keys[] = { 102 KEY_RESERVED, 103 KEY_RESERVED, 104 KEY_RESERVED, 105 KEY_RESERVED, 106 KEY_RESERVED, 107 BTN_LEFT 108 }; 109 110 static 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 116 static 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 122 static struct mxt_platform_data atmel_1664s_platform_data = { 123 .irqflags = IRQF_TRIGGER_FALLING, 124 }; 125 126 static 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 132 static 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 206 struct i2c_lookup { 207 const char *name; 208 int instance; 209 int n; 210 }; 211 212 static 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 227 static 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 */ 257 static 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 */ 274 static 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 284 static 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 294 static 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 307 static 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 320 static 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 330 static 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 340 static 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 350 static 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 359 static 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 408 static 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 417 static struct chromeos_laptop samsung_series_5 = { 418 .i2c_peripherals = { 419 /* Light Sensor. */ 420 { .add = setup_tsl2583_als, I2C_ADAPTER_SMBUS }, 421 }, 422 }; 423 424 static 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 435 static struct chromeos_laptop hp_chromebook_14 = { 436 .i2c_peripherals = { 437 /* Touchpad. */ 438 { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, 439 }, 440 }; 441 442 static struct chromeos_laptop dell_chromebook_11 = { 443 .i2c_peripherals = { 444 /* Touchpad. */ 445 { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, 446 }, 447 }; 448 449 static struct chromeos_laptop toshiba_cb35 = { 450 .i2c_peripherals = { 451 /* Touchpad. */ 452 { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, 453 }, 454 }; 455 456 static struct chromeos_laptop acer_c7_chromebook = { 457 .i2c_peripherals = { 458 /* Touchpad. */ 459 { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS }, 460 }, 461 }; 462 463 static struct chromeos_laptop acer_ac700 = { 464 .i2c_peripherals = { 465 /* Light Sensor. */ 466 { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS }, 467 }, 468 }; 469 470 static 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 481 static struct chromeos_laptop hp_pavilion_14_chromebook = { 482 .i2c_peripherals = { 483 /* Touchpad. */ 484 { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS }, 485 }, 486 }; 487 488 static 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 499 static 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 }; 584 MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table); 585 586 static struct platform_device *cros_platform_device; 587 588 static struct platform_driver cros_platform_driver = { 589 .driver = { 590 .name = "chromeos_laptop", 591 }, 592 .probe = chromeos_laptop_probe, 593 }; 594 595 static 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 620 fail_platform_device2: 621 platform_device_put(cros_platform_device); 622 fail_platform_device1: 623 platform_driver_unregister(&cros_platform_driver); 624 return ret; 625 } 626 627 static 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 640 module_init(chromeos_laptop_init); 641 module_exit(chromeos_laptop_exit); 642 643 MODULE_DESCRIPTION("Chrome OS Laptop driver"); 644 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>"); 645 MODULE_LICENSE("GPL"); 646