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