cros_ec_sensorhub.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Sensor HUB driver that discovers sensors behind a ChromeOS Embedded
  4. * Controller.
  5. *
  6. * Copyright 2019 Google LLC
  7. */
  8. #include <linux/init.h>
  9. #include <linux/device.h>
  10. #include <linux/mod_devicetable.h>
  11. #include <linux/module.h>
  12. #include <linux/platform_data/cros_ec_commands.h>
  13. #include <linux/platform_data/cros_ec_proto.h>
  14. #include <linux/platform_data/cros_ec_sensorhub.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/slab.h>
  17. #include <linux/types.h>
  18. #define DRV_NAME "cros-ec-sensorhub"
  19. static void cros_ec_sensorhub_free_sensor(void *arg)
  20. {
  21. struct platform_device *pdev = arg;
  22. platform_device_unregister(pdev);
  23. }
  24. static int cros_ec_sensorhub_allocate_sensor(struct device *parent,
  25. char *sensor_name,
  26. int sensor_num)
  27. {
  28. struct cros_ec_sensor_platform sensor_platforms = {
  29. .sensor_num = sensor_num,
  30. };
  31. struct platform_device *pdev;
  32. pdev = platform_device_register_data(parent, sensor_name,
  33. PLATFORM_DEVID_AUTO,
  34. &sensor_platforms,
  35. sizeof(sensor_platforms));
  36. if (IS_ERR(pdev))
  37. return PTR_ERR(pdev);
  38. return devm_add_action_or_reset(parent,
  39. cros_ec_sensorhub_free_sensor,
  40. pdev);
  41. }
  42. static int cros_ec_sensorhub_register(struct device *dev,
  43. struct cros_ec_sensorhub *sensorhub)
  44. {
  45. int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 };
  46. struct cros_ec_command *msg = sensorhub->msg;
  47. struct cros_ec_dev *ec = sensorhub->ec;
  48. int ret, i;
  49. char *name;
  50. msg->version = 1;
  51. msg->insize = sizeof(struct ec_response_motion_sense);
  52. msg->outsize = sizeof(struct ec_params_motion_sense);
  53. for (i = 0; i < sensorhub->sensor_num; i++) {
  54. sensorhub->params->cmd = MOTIONSENSE_CMD_INFO;
  55. sensorhub->params->info.sensor_num = i;
  56. ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
  57. if (ret < 0) {
  58. dev_warn(dev, "no info for EC sensor %d : %d/%d\n",
  59. i, ret, msg->result);
  60. continue;
  61. }
  62. switch (sensorhub->resp->info.type) {
  63. case MOTIONSENSE_TYPE_ACCEL:
  64. name = "cros-ec-accel";
  65. break;
  66. case MOTIONSENSE_TYPE_BARO:
  67. name = "cros-ec-baro";
  68. break;
  69. case MOTIONSENSE_TYPE_GYRO:
  70. name = "cros-ec-gyro";
  71. break;
  72. case MOTIONSENSE_TYPE_MAG:
  73. name = "cros-ec-mag";
  74. break;
  75. case MOTIONSENSE_TYPE_PROX:
  76. name = "cros-ec-prox";
  77. break;
  78. case MOTIONSENSE_TYPE_LIGHT:
  79. name = "cros-ec-light";
  80. break;
  81. case MOTIONSENSE_TYPE_ACTIVITY:
  82. name = "cros-ec-activity";
  83. break;
  84. default:
  85. dev_warn(dev, "unknown type %d\n",
  86. sensorhub->resp->info.type);
  87. continue;
  88. }
  89. ret = cros_ec_sensorhub_allocate_sensor(dev, name, i);
  90. if (ret)
  91. return ret;
  92. sensor_type[sensorhub->resp->info.type]++;
  93. }
  94. if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2)
  95. ec->has_kb_wake_angle = true;
  96. if (cros_ec_check_features(ec,
  97. EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
  98. ret = cros_ec_sensorhub_allocate_sensor(dev,
  99. "cros-ec-lid-angle",
  100. 0);
  101. if (ret)
  102. return ret;
  103. }
  104. return 0;
  105. }
  106. static int cros_ec_sensorhub_probe(struct platform_device *pdev)
  107. {
  108. struct device *dev = &pdev->dev;
  109. struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
  110. struct cros_ec_sensorhub *data;
  111. struct cros_ec_command *msg;
  112. int ret, i, sensor_num;
  113. msg = devm_kzalloc(dev, sizeof(struct cros_ec_command) +
  114. max((u16)sizeof(struct ec_params_motion_sense),
  115. ec->ec_dev->max_response), GFP_KERNEL);
  116. if (!msg)
  117. return -ENOMEM;
  118. msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
  119. data = devm_kzalloc(dev, sizeof(struct cros_ec_sensorhub), GFP_KERNEL);
  120. if (!data)
  121. return -ENOMEM;
  122. mutex_init(&data->cmd_lock);
  123. data->dev = dev;
  124. data->ec = ec;
  125. data->msg = msg;
  126. data->params = (struct ec_params_motion_sense *)msg->data;
  127. data->resp = (struct ec_response_motion_sense *)msg->data;
  128. dev_set_drvdata(dev, data);
  129. /* Check whether this EC is a sensor hub. */
  130. if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) {
  131. sensor_num = cros_ec_get_sensor_count(ec);
  132. if (sensor_num < 0) {
  133. dev_err(dev,
  134. "Unable to retrieve sensor information (err:%d)\n",
  135. sensor_num);
  136. return sensor_num;
  137. }
  138. if (sensor_num == 0) {
  139. dev_err(dev, "Zero sensors reported.\n");
  140. return -EINVAL;
  141. }
  142. data->sensor_num = sensor_num;
  143. /*
  144. * Prepare the ring handler before enumering the
  145. * sensors.
  146. */
  147. if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
  148. ret = cros_ec_sensorhub_ring_allocate(data);
  149. if (ret)
  150. return ret;
  151. }
  152. /* Enumerate the sensors.*/
  153. ret = cros_ec_sensorhub_register(dev, data);
  154. if (ret)
  155. return ret;
  156. /*
  157. * When the EC does not have a FIFO, the sensors will query
  158. * their data themselves via sysfs or a software trigger.
  159. */
  160. if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
  161. ret = cros_ec_sensorhub_ring_add(data);
  162. if (ret)
  163. return ret;
  164. /*
  165. * The msg and its data is not under the control of the
  166. * ring handler.
  167. */
  168. return devm_add_action_or_reset(dev,
  169. cros_ec_sensorhub_ring_remove,
  170. data);
  171. }
  172. } else {
  173. /*
  174. * If the device has sensors but does not claim to
  175. * be a sensor hub, we are in legacy mode.
  176. */
  177. data->sensor_num = 2;
  178. for (i = 0; i < data->sensor_num; i++) {
  179. ret = cros_ec_sensorhub_allocate_sensor(dev,
  180. "cros-ec-accel-legacy", i);
  181. if (ret)
  182. return ret;
  183. }
  184. }
  185. return 0;
  186. }
  187. #ifdef CONFIG_PM_SLEEP
  188. /*
  189. * When the EC is suspending, we must stop sending interrupt,
  190. * we may use the same interrupt line for waking up the device.
  191. * Tell the EC to stop sending non-interrupt event on the iio ring.
  192. */
  193. static int cros_ec_sensorhub_suspend(struct device *dev)
  194. {
  195. struct cros_ec_sensorhub *sensorhub = dev_get_drvdata(dev);
  196. struct cros_ec_dev *ec = sensorhub->ec;
  197. if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
  198. return cros_ec_sensorhub_ring_fifo_enable(sensorhub, false);
  199. return 0;
  200. }
  201. static int cros_ec_sensorhub_resume(struct device *dev)
  202. {
  203. struct cros_ec_sensorhub *sensorhub = dev_get_drvdata(dev);
  204. struct cros_ec_dev *ec = sensorhub->ec;
  205. if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO))
  206. return cros_ec_sensorhub_ring_fifo_enable(sensorhub, true);
  207. return 0;
  208. }
  209. #endif
  210. static SIMPLE_DEV_PM_OPS(cros_ec_sensorhub_pm_ops,
  211. cros_ec_sensorhub_suspend,
  212. cros_ec_sensorhub_resume);
  213. static const struct platform_device_id cros_ec_sensorhub_id[] = {
  214. { DRV_NAME, 0 },
  215. {}
  216. };
  217. MODULE_DEVICE_TABLE(platform, cros_ec_sensorhub_id);
  218. static struct platform_driver cros_ec_sensorhub_driver = {
  219. .driver = {
  220. .name = DRV_NAME,
  221. .pm = &cros_ec_sensorhub_pm_ops,
  222. },
  223. .probe = cros_ec_sensorhub_probe,
  224. .id_table = cros_ec_sensorhub_id,
  225. };
  226. module_platform_driver(cros_ec_sensorhub_driver);
  227. MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
  228. MODULE_DESCRIPTION("ChromeOS EC MEMS Sensor Hub Driver");
  229. MODULE_LICENSE("GPL");