chromeos_laptop.c 16 KB

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