File Index
Files and directories
Sign in
FILE

cros_ec_sensorhub.c

File preview with syntax highlighting.

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