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