chromeos_laptop.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. // SPDX-License-Identifier: GPL-2.0+
  2. // Driver to instantiate Chromebook i2c/smbus devices.
  3. //
  4. // Copyright (C) 2012 Google, Inc.
  5. // Author: Benson Leung <bleung@chromium.org>
  6. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  7. #include <linux/dmi.h>
  8. #include <linux/i2c.h>
  9. #include <linux/input.h>
  10. #include <linux/interrupt.h>
  11. #include <linux/ioport.h>
  12. #include <linux/module.h>
  13. #include <linux/pci.h>
  14. #include <linux/platform_device.h>
  15. #include <linux/property.h>
  16. #define ATMEL_TP_I2C_ADDR 0x4b
  17. #define ATMEL_TP_I2C_BL_ADDR 0x25
  18. #define ATMEL_TS_I2C_ADDR 0x4a
  19. #define ATMEL_TS_I2C_BL_ADDR 0x26
  20. #define CYAPA_TP_I2C_ADDR 0x67
  21. #define ELAN_TP_I2C_ADDR 0x15
  22. #define ISL_ALS_I2C_ADDR 0x44
  23. #define TAOS_ALS_I2C_ADDR 0x29
  24. static const char *i2c_adapter_names[] = {
  25. "SMBus I801 adapter",
  26. "i915 gmbus vga",
  27. "i915 gmbus panel",
  28. "Synopsys DesignWare I2C adapter",
  29. };
  30. /* Keep this enum consistent with i2c_adapter_names */
  31. enum i2c_adapter_type {
  32. I2C_ADAPTER_SMBUS = 0,
  33. I2C_ADAPTER_VGADDC,
  34. I2C_ADAPTER_PANEL,
  35. I2C_ADAPTER_DESIGNWARE,
  36. };
  37. struct i2c_peripheral {
  38. struct i2c_board_info board_info;
  39. unsigned short alt_addr;
  40. const char *dmi_name;
  41. unsigned long irqflags;
  42. struct resource irq_resource;
  43. enum i2c_adapter_type type;
  44. u32 pci_devid;
  45. struct i2c_client *client;
  46. };
  47. struct chromeos_laptop {
  48. /*
  49. * Note that we can't mark this pointer as const because
  50. * i2c_new_probed_device() changes passed in I2C board info, so.
  51. */
  52. struct i2c_peripheral *i2c_peripherals;
  53. unsigned int num_i2c_peripherals;
  54. };
  55. static const struct chromeos_laptop *cros_laptop;
  56. static struct i2c_client *
  57. chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter,
  58. struct i2c_board_info *info,
  59. unsigned short alt_addr)
  60. {
  61. const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
  62. struct i2c_client *client;
  63. /*
  64. * Add the i2c device. If we can't detect it at the primary
  65. * address we scan secondary addresses. In any case the client
  66. * structure gets assigned primary address.
  67. */
  68. client = i2c_new_probed_device(adapter, info, addr_list, NULL);
  69. if (!client && alt_addr) {
  70. struct i2c_board_info dummy_info = {
  71. I2C_BOARD_INFO("dummy", info->addr),
  72. };
  73. const unsigned short alt_addr_list[] = {
  74. alt_addr, I2C_CLIENT_END
  75. };
  76. struct i2c_client *dummy;
  77. dummy = i2c_new_probed_device(adapter, &dummy_info,
  78. alt_addr_list, NULL);
  79. if (dummy) {
  80. pr_debug("%d-%02x is probed at %02x\n",
  81. adapter->nr, info->addr, dummy->addr);
  82. i2c_unregister_device(dummy);
  83. client = i2c_new_device(adapter, info);
  84. }
  85. }
  86. if (!client)
  87. pr_debug("failed to register device %d-%02x\n",
  88. adapter->nr, info->addr);
  89. else
  90. pr_debug("added i2c device %d-%02x\n",
  91. adapter->nr, info->addr);
  92. return client;
  93. }
  94. static bool chromeos_laptop_match_adapter_devid(struct device *dev, u32 devid)
  95. {
  96. struct pci_dev *pdev;
  97. if (!dev_is_pci(dev))
  98. return false;
  99. pdev = to_pci_dev(dev);
  100. return devid == PCI_DEVID(pdev->bus->number, pdev->devfn);
  101. }
  102. static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter)
  103. {
  104. struct i2c_peripheral *i2c_dev;
  105. int i;
  106. for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
  107. i2c_dev = &cros_laptop->i2c_peripherals[i];
  108. /* Skip devices already created */
  109. if (i2c_dev->client)
  110. continue;
  111. if (strncmp(adapter->name, i2c_adapter_names[i2c_dev->type],
  112. strlen(i2c_adapter_names[i2c_dev->type])))
  113. continue;
  114. if (i2c_dev->pci_devid &&
  115. !chromeos_laptop_match_adapter_devid(adapter->dev.parent,
  116. i2c_dev->pci_devid)) {
  117. continue;
  118. }
  119. i2c_dev->client =
  120. chromes_laptop_instantiate_i2c_device(adapter,
  121. &i2c_dev->board_info,
  122. i2c_dev->alt_addr);
  123. }
  124. }
  125. static void chromeos_laptop_detach_i2c_client(struct i2c_client *client)
  126. {
  127. struct i2c_peripheral *i2c_dev;
  128. int i;
  129. for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
  130. i2c_dev = &cros_laptop->i2c_peripherals[i];
  131. if (i2c_dev->client == client)
  132. i2c_dev->client = NULL;
  133. }
  134. }
  135. static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb,
  136. unsigned long action, void *data)
  137. {
  138. struct device *dev = data;
  139. switch (action) {
  140. case BUS_NOTIFY_ADD_DEVICE:
  141. if (dev->type == &i2c_adapter_type)
  142. chromeos_laptop_check_adapter(to_i2c_adapter(dev));
  143. break;
  144. case BUS_NOTIFY_REMOVED_DEVICE:
  145. if (dev->type == &i2c_client_type)
  146. chromeos_laptop_detach_i2c_client(to_i2c_client(dev));
  147. break;
  148. }
  149. return 0;
  150. }
  151. static struct notifier_block chromeos_laptop_i2c_notifier = {
  152. .notifier_call = chromeos_laptop_i2c_notifier_call,
  153. };
  154. #define DECLARE_CROS_LAPTOP(_name) \
  155. static const struct chromeos_laptop _name __initconst = { \
  156. .i2c_peripherals = _name##_peripherals, \
  157. .num_i2c_peripherals = ARRAY_SIZE(_name##_peripherals), \
  158. }
  159. static struct i2c_peripheral samsung_series_5_550_peripherals[] __initdata = {
  160. /* Touchpad. */
  161. {
  162. .board_info = {
  163. I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
  164. .flags = I2C_CLIENT_WAKE,
  165. },
  166. .dmi_name = "trackpad",
  167. .type = I2C_ADAPTER_SMBUS,
  168. },
  169. /* Light Sensor. */
  170. {
  171. .board_info = {
  172. I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
  173. },
  174. .dmi_name = "lightsensor",
  175. .type = I2C_ADAPTER_SMBUS,
  176. },
  177. };
  178. DECLARE_CROS_LAPTOP(samsung_series_5_550);
  179. static struct i2c_peripheral samsung_series_5_peripherals[] __initdata = {
  180. /* Light Sensor. */
  181. {
  182. .board_info = {
  183. I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
  184. },
  185. .type = I2C_ADAPTER_SMBUS,
  186. },
  187. };
  188. DECLARE_CROS_LAPTOP(samsung_series_5);
  189. static const int chromebook_pixel_tp_keys[] __initconst = {
  190. KEY_RESERVED,
  191. KEY_RESERVED,
  192. KEY_RESERVED,
  193. KEY_RESERVED,
  194. KEY_RESERVED,
  195. BTN_LEFT
  196. };
  197. static const struct property_entry
  198. chromebook_pixel_trackpad_props[] __initconst = {
  199. PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_pixel_tp_keys),
  200. { }
  201. };
  202. static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = {
  203. /* Touch Screen. */
  204. {
  205. .board_info = {
  206. I2C_BOARD_INFO("atmel_mxt_ts",
  207. ATMEL_TS_I2C_ADDR),
  208. .flags = I2C_CLIENT_WAKE,
  209. },
  210. .dmi_name = "touchscreen",
  211. .irqflags = IRQF_TRIGGER_FALLING,
  212. .type = I2C_ADAPTER_PANEL,
  213. .alt_addr = ATMEL_TS_I2C_BL_ADDR,
  214. },
  215. /* Touchpad. */
  216. {
  217. .board_info = {
  218. I2C_BOARD_INFO("atmel_mxt_tp",
  219. ATMEL_TP_I2C_ADDR),
  220. .properties =
  221. chromebook_pixel_trackpad_props,
  222. .flags = I2C_CLIENT_WAKE,
  223. },
  224. .dmi_name = "trackpad",
  225. .irqflags = IRQF_TRIGGER_FALLING,
  226. .type = I2C_ADAPTER_VGADDC,
  227. .alt_addr = ATMEL_TP_I2C_BL_ADDR,
  228. },
  229. /* Light Sensor. */
  230. {
  231. .board_info = {
  232. I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
  233. },
  234. .dmi_name = "lightsensor",
  235. .type = I2C_ADAPTER_PANEL,
  236. },
  237. };
  238. DECLARE_CROS_LAPTOP(chromebook_pixel);
  239. static struct i2c_peripheral hp_chromebook_14_peripherals[] __initdata = {
  240. /* Touchpad. */
  241. {
  242. .board_info = {
  243. I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
  244. .flags = I2C_CLIENT_WAKE,
  245. },
  246. .dmi_name = "trackpad",
  247. .type = I2C_ADAPTER_DESIGNWARE,
  248. },
  249. };
  250. DECLARE_CROS_LAPTOP(hp_chromebook_14);
  251. static struct i2c_peripheral dell_chromebook_11_peripherals[] __initdata = {
  252. /* Touchpad. */
  253. {
  254. .board_info = {
  255. I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
  256. .flags = I2C_CLIENT_WAKE,
  257. },
  258. .dmi_name = "trackpad",
  259. .type = I2C_ADAPTER_DESIGNWARE,
  260. },
  261. /* Elan Touchpad option. */
  262. {
  263. .board_info = {
  264. I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
  265. .flags = I2C_CLIENT_WAKE,
  266. },
  267. .dmi_name = "trackpad",
  268. .type = I2C_ADAPTER_DESIGNWARE,
  269. },
  270. };
  271. DECLARE_CROS_LAPTOP(dell_chromebook_11);
  272. static struct i2c_peripheral toshiba_cb35_peripherals[] __initdata = {
  273. /* Touchpad. */
  274. {
  275. .board_info = {
  276. I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
  277. .flags = I2C_CLIENT_WAKE,
  278. },
  279. .dmi_name = "trackpad",
  280. .type = I2C_ADAPTER_DESIGNWARE,
  281. },
  282. };
  283. DECLARE_CROS_LAPTOP(toshiba_cb35);
  284. static struct i2c_peripheral acer_c7_chromebook_peripherals[] __initdata = {
  285. /* Touchpad. */
  286. {
  287. .board_info = {
  288. I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
  289. .flags = I2C_CLIENT_WAKE,
  290. },
  291. .dmi_name = "trackpad",
  292. .type = I2C_ADAPTER_SMBUS,
  293. },
  294. };
  295. DECLARE_CROS_LAPTOP(acer_c7_chromebook);
  296. static struct i2c_peripheral acer_ac700_peripherals[] __initdata = {
  297. /* Light Sensor. */
  298. {
  299. .board_info = {
  300. I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
  301. },
  302. .type = I2C_ADAPTER_SMBUS,
  303. },
  304. };
  305. DECLARE_CROS_LAPTOP(acer_ac700);
  306. static struct i2c_peripheral acer_c720_peripherals[] __initdata = {
  307. /* Touchscreen. */
  308. {
  309. .board_info = {
  310. I2C_BOARD_INFO("atmel_mxt_ts",
  311. ATMEL_TS_I2C_ADDR),
  312. .flags = I2C_CLIENT_WAKE,
  313. },
  314. .dmi_name = "touchscreen",
  315. .irqflags = IRQF_TRIGGER_FALLING,
  316. .type = I2C_ADAPTER_DESIGNWARE,
  317. .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
  318. .alt_addr = ATMEL_TS_I2C_BL_ADDR,
  319. },
  320. /* Touchpad. */
  321. {
  322. .board_info = {
  323. I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
  324. .flags = I2C_CLIENT_WAKE,
  325. },
  326. .dmi_name = "trackpad",
  327. .type = I2C_ADAPTER_DESIGNWARE,
  328. .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
  329. },
  330. /* Elan Touchpad option. */
  331. {
  332. .board_info = {
  333. I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
  334. .flags = I2C_CLIENT_WAKE,
  335. },
  336. .dmi_name = "trackpad",
  337. .type = I2C_ADAPTER_DESIGNWARE,
  338. .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
  339. },
  340. /* Light Sensor. */
  341. {
  342. .board_info = {
  343. I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
  344. },
  345. .dmi_name = "lightsensor",
  346. .type = I2C_ADAPTER_DESIGNWARE,
  347. .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
  348. },
  349. };
  350. DECLARE_CROS_LAPTOP(acer_c720);
  351. static struct i2c_peripheral
  352. hp_pavilion_14_chromebook_peripherals[] __initdata = {
  353. /* Touchpad. */
  354. {
  355. .board_info = {
  356. I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
  357. .flags = I2C_CLIENT_WAKE,
  358. },
  359. .dmi_name = "trackpad",
  360. .type = I2C_ADAPTER_SMBUS,
  361. },
  362. };
  363. DECLARE_CROS_LAPTOP(hp_pavilion_14_chromebook);
  364. static struct i2c_peripheral cr48_peripherals[] __initdata = {
  365. /* Light Sensor. */
  366. {
  367. .board_info = {
  368. I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
  369. },
  370. .type = I2C_ADAPTER_SMBUS,
  371. },
  372. };
  373. DECLARE_CROS_LAPTOP(cr48);
  374. static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
  375. {
  376. .ident = "Samsung Series 5 550",
  377. .matches = {
  378. DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
  379. DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
  380. },
  381. .driver_data = (void *)&samsung_series_5_550,
  382. },
  383. {
  384. .ident = "Samsung Series 5",
  385. .matches = {
  386. DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
  387. },
  388. .driver_data = (void *)&samsung_series_5,
  389. },
  390. {
  391. .ident = "Chromebook Pixel",
  392. .matches = {
  393. DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
  394. DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
  395. },
  396. .driver_data = (void *)&chromebook_pixel,
  397. },
  398. {
  399. .ident = "Wolf",
  400. .matches = {
  401. DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
  402. DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"),
  403. },
  404. .driver_data = (void *)&dell_chromebook_11,
  405. },
  406. {
  407. .ident = "HP Chromebook 14",
  408. .matches = {
  409. DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
  410. DMI_MATCH(DMI_PRODUCT_NAME, "Falco"),
  411. },
  412. .driver_data = (void *)&hp_chromebook_14,
  413. },
  414. {
  415. .ident = "Toshiba CB35",
  416. .matches = {
  417. DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
  418. DMI_MATCH(DMI_PRODUCT_NAME, "Leon"),
  419. },
  420. .driver_data = (void *)&toshiba_cb35,
  421. },
  422. {
  423. .ident = "Acer C7 Chromebook",
  424. .matches = {
  425. DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
  426. },
  427. .driver_data = (void *)&acer_c7_chromebook,
  428. },
  429. {
  430. .ident = "Acer AC700",
  431. .matches = {
  432. DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
  433. },
  434. .driver_data = (void *)&acer_ac700,
  435. },
  436. {
  437. .ident = "Acer C720",
  438. .matches = {
  439. DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
  440. },
  441. .driver_data = (void *)&acer_c720,
  442. },
  443. {
  444. .ident = "HP Pavilion 14 Chromebook",
  445. .matches = {
  446. DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
  447. },
  448. .driver_data = (void *)&hp_pavilion_14_chromebook,
  449. },
  450. {
  451. .ident = "Cr-48",
  452. .matches = {
  453. DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
  454. },
  455. .driver_data = (void *)&cr48,
  456. },
  457. { }
  458. };
  459. MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
  460. static int __init chromeos_laptop_scan_adapter(struct device *dev, void *data)
  461. {
  462. struct i2c_adapter *adapter;
  463. adapter = i2c_verify_adapter(dev);
  464. if (adapter)
  465. chromeos_laptop_check_adapter(adapter);
  466. return 0;
  467. }
  468. static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name)
  469. {
  470. const struct dmi_device *dmi_dev;
  471. const struct dmi_dev_onboard *dev_data;
  472. dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, dmi_name, NULL);
  473. if (!dmi_dev) {
  474. pr_err("failed to find DMI device '%s'\n", dmi_name);
  475. return -ENOENT;
  476. }
  477. dev_data = dmi_dev->device_data;
  478. if (!dev_data) {
  479. pr_err("failed to get data from DMI for '%s'\n", dmi_name);
  480. return -EINVAL;
  481. }
  482. return dev_data->instance;
  483. }
  484. static int __init chromeos_laptop_setup_irq(struct i2c_peripheral *i2c_dev)
  485. {
  486. int irq;
  487. if (i2c_dev->dmi_name) {
  488. irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name);
  489. if (irq < 0)
  490. return irq;
  491. i2c_dev->irq_resource = (struct resource)
  492. DEFINE_RES_NAMED(irq, 1, NULL,
  493. IORESOURCE_IRQ | i2c_dev->irqflags);
  494. i2c_dev->board_info.resources = &i2c_dev->irq_resource;
  495. i2c_dev->board_info.num_resources = 1;
  496. }
  497. return 0;
  498. }
  499. static struct chromeos_laptop * __init
  500. chromeos_laptop_prepare(const struct chromeos_laptop *src)
  501. {
  502. struct chromeos_laptop *cros_laptop;
  503. struct i2c_peripheral *i2c_dev;
  504. struct i2c_board_info *info;
  505. int error;
  506. int i;
  507. cros_laptop = kzalloc(sizeof(*cros_laptop), GFP_KERNEL);
  508. if (!cros_laptop)
  509. return ERR_PTR(-ENOMEM);
  510. cros_laptop->i2c_peripherals = kmemdup(src->i2c_peripherals,
  511. src->num_i2c_peripherals *
  512. sizeof(*src->i2c_peripherals),
  513. GFP_KERNEL);
  514. if (!cros_laptop->i2c_peripherals) {
  515. error = -ENOMEM;
  516. goto err_free_cros_laptop;
  517. }
  518. cros_laptop->num_i2c_peripherals = src->num_i2c_peripherals;
  519. for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
  520. i2c_dev = &cros_laptop->i2c_peripherals[i];
  521. info = &i2c_dev->board_info;
  522. error = chromeos_laptop_setup_irq(i2c_dev);
  523. if (error)
  524. goto err_destroy_cros_peripherals;
  525. /* We need to deep-copy properties */
  526. if (info->properties) {
  527. info->properties =
  528. property_entries_dup(info->properties);
  529. if (IS_ERR(info->properties)) {
  530. error = PTR_ERR(info->properties);
  531. goto err_destroy_cros_peripherals;
  532. }
  533. }
  534. }
  535. return cros_laptop;
  536. err_destroy_cros_peripherals:
  537. while (--i >= 0) {
  538. i2c_dev = &cros_laptop->i2c_peripherals[i];
  539. info = &i2c_dev->board_info;
  540. if (info->properties)
  541. property_entries_free(info->properties);
  542. }
  543. kfree(cros_laptop->i2c_peripherals);
  544. err_free_cros_laptop:
  545. kfree(cros_laptop);
  546. return ERR_PTR(error);
  547. }
  548. static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop)
  549. {
  550. struct i2c_peripheral *i2c_dev;
  551. struct i2c_board_info *info;
  552. int i;
  553. for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
  554. i2c_dev = &cros_laptop->i2c_peripherals[i];
  555. info = &i2c_dev->board_info;
  556. if (i2c_dev->client)
  557. i2c_unregister_device(i2c_dev->client);
  558. if (info->properties)
  559. property_entries_free(info->properties);
  560. }
  561. kfree(cros_laptop->i2c_peripherals);
  562. kfree(cros_laptop);
  563. }
  564. static int __init chromeos_laptop_init(void)
  565. {
  566. const struct dmi_system_id *dmi_id;
  567. int error;
  568. dmi_id = dmi_first_match(chromeos_laptop_dmi_table);
  569. if (!dmi_id) {
  570. pr_debug("unsupported system\n");
  571. return -ENODEV;
  572. }
  573. pr_debug("DMI Matched %s\n", dmi_id->ident);
  574. cros_laptop = chromeos_laptop_prepare((void *)dmi_id->driver_data);
  575. if (IS_ERR(cros_laptop))
  576. return PTR_ERR(cros_laptop);
  577. error = bus_register_notifier(&i2c_bus_type,
  578. &chromeos_laptop_i2c_notifier);
  579. if (error) {
  580. pr_err("failed to register i2c bus notifier: %d\n", error);
  581. chromeos_laptop_destroy(cros_laptop);
  582. return error;
  583. }
  584. /*
  585. * Scan adapters that have been registered before we installed
  586. * the notifier to make sure we do not miss any devices.
  587. */
  588. i2c_for_each_dev(NULL, chromeos_laptop_scan_adapter);
  589. return 0;
  590. }
  591. static void __exit chromeos_laptop_exit(void)
  592. {
  593. bus_unregister_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier);
  594. chromeos_laptop_destroy(cros_laptop);
  595. }
  596. module_init(chromeos_laptop_init);
  597. module_exit(chromeos_laptop_exit);
  598. MODULE_DESCRIPTION("Chrome OS Laptop driver");
  599. MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
  600. MODULE_LICENSE("GPL");