chromeos_laptop.c 6.0 KB

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