chromeos_laptop.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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/module.h>
  26. #define CYAPA_TP_I2C_ADDR 0x67
  27. #define ISL_ALS_I2C_ADDR 0x44
  28. static struct i2c_client *als;
  29. static struct i2c_client *tp;
  30. const char *i2c_adapter_names[] = {
  31. "SMBus I801 adapter",
  32. };
  33. /* Keep this enum consistent with i2c_adapter_names */
  34. enum i2c_adapter_type {
  35. I2C_ADAPTER_SMBUS = 0,
  36. };
  37. static struct i2c_board_info __initdata cyapa_device = {
  38. I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
  39. .flags = I2C_CLIENT_WAKE,
  40. };
  41. static struct i2c_board_info __initdata isl_als_device = {
  42. I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
  43. };
  44. static struct i2c_client __init *__add_probed_i2c_device(
  45. const char *name,
  46. int bus,
  47. struct i2c_board_info *info,
  48. const unsigned short *addrs)
  49. {
  50. const struct dmi_device *dmi_dev;
  51. const struct dmi_dev_onboard *dev_data;
  52. struct i2c_adapter *adapter;
  53. struct i2c_client *client;
  54. if (bus < 0)
  55. return NULL;
  56. /*
  57. * If a name is specified, look for irq platform information stashed
  58. * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware.
  59. */
  60. if (name) {
  61. dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
  62. if (!dmi_dev) {
  63. pr_err("%s failed to dmi find device %s.\n",
  64. __func__,
  65. name);
  66. return NULL;
  67. }
  68. dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
  69. if (!dev_data) {
  70. pr_err("%s failed to get data from dmi for %s.\n",
  71. __func__, name);
  72. return NULL;
  73. }
  74. info->irq = dev_data->instance;
  75. }
  76. adapter = i2c_get_adapter(bus);
  77. if (!adapter) {
  78. pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
  79. return NULL;
  80. }
  81. /* add the i2c device */
  82. client = i2c_new_probed_device(adapter, info, addrs, NULL);
  83. if (!client)
  84. pr_err("%s failed to register device %d-%02x\n",
  85. __func__, bus, info->addr);
  86. else
  87. pr_debug("%s added i2c device %d-%02x\n",
  88. __func__, bus, info->addr);
  89. i2c_put_adapter(adapter);
  90. return client;
  91. }
  92. static int __init __find_i2c_adap(struct device *dev, void *data)
  93. {
  94. const char *name = data;
  95. static const char *prefix = "i2c-";
  96. struct i2c_adapter *adapter;
  97. if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
  98. return 0;
  99. adapter = to_i2c_adapter(dev);
  100. return (strncmp(adapter->name, name, strlen(name)) == 0);
  101. }
  102. static int __init find_i2c_adapter_num(enum i2c_adapter_type type)
  103. {
  104. struct device *dev = NULL;
  105. struct i2c_adapter *adapter;
  106. const char *name = i2c_adapter_names[type];
  107. /* find the adapter by name */
  108. dev = bus_find_device(&i2c_bus_type, NULL, (void *)name,
  109. __find_i2c_adap);
  110. if (!dev) {
  111. pr_err("%s: i2c adapter %s not found on system.\n", __func__,
  112. name);
  113. return -ENODEV;
  114. }
  115. adapter = to_i2c_adapter(dev);
  116. return adapter->nr;
  117. }
  118. /*
  119. * Probes for a device at a single address, the one provided by
  120. * info->addr.
  121. * Returns NULL if no device found.
  122. */
  123. static struct i2c_client __init *add_smbus_device(const char *name,
  124. struct i2c_board_info *info)
  125. {
  126. const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
  127. return __add_probed_i2c_device(name,
  128. find_i2c_adapter_num(I2C_ADAPTER_SMBUS),
  129. info,
  130. addr_list);
  131. }
  132. static int __init setup_cyapa_smbus_tp(const struct dmi_system_id *id)
  133. {
  134. /* add cyapa touchpad on smbus */
  135. tp = add_smbus_device("trackpad", &cyapa_device);
  136. return 0;
  137. }
  138. static int __init setup_isl29018_als(const struct dmi_system_id *id)
  139. {
  140. /* add isl29018 light sensor */
  141. als = add_smbus_device("lightsensor", &isl_als_device);
  142. return 0;
  143. }
  144. static struct dmi_system_id __initdata chromeos_laptop_dmi_table[] = {
  145. {
  146. .ident = "Samsung Series 5 550 - Touchpad",
  147. .matches = {
  148. DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
  149. DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
  150. },
  151. .callback = setup_cyapa_smbus_tp,
  152. },
  153. {
  154. .ident = "Samsung Series 5 550 - Light Sensor",
  155. .matches = {
  156. DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
  157. DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
  158. },
  159. .callback = setup_isl29018_als,
  160. },
  161. {
  162. .ident = "Acer C7 Chromebook - Touchpad",
  163. .matches = {
  164. DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
  165. },
  166. .callback = setup_cyapa_smbus_tp,
  167. },
  168. { }
  169. };
  170. MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
  171. static int __init chromeos_laptop_init(void)
  172. {
  173. if (!dmi_check_system(chromeos_laptop_dmi_table)) {
  174. pr_debug("%s unsupported system.\n", __func__);
  175. return -ENODEV;
  176. }
  177. return 0;
  178. }
  179. static void __exit chromeos_laptop_exit(void)
  180. {
  181. if (als)
  182. i2c_unregister_device(als);
  183. if (tp)
  184. i2c_unregister_device(tp);
  185. }
  186. module_init(chromeos_laptop_init);
  187. module_exit(chromeos_laptop_exit);
  188. MODULE_DESCRIPTION("Chrome OS Laptop driver");
  189. MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
  190. MODULE_LICENSE("GPL");