1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * HID Sensors Driver
4 * Copyright (c) 2012, Intel Corporation.
5 */
6#include <linux/device.h>
7#include <linux/platform_device.h>
8#include <linux/module.h>
9#include <linux/interrupt.h>
10#include <linux/irq.h>
11#include <linux/slab.h>
12#include <linux/delay.h>
13#include <linux/hid-sensor-hub.h>
14#include <linux/iio/iio.h>
15#include <linux/iio/sysfs.h>
16#include <linux/iio/buffer.h>
17#include "../common/hid-sensors/hid-sensor-trigger.h"
18
19enum accel_3d_channel {
20	CHANNEL_SCAN_INDEX_X,
21	CHANNEL_SCAN_INDEX_Y,
22	CHANNEL_SCAN_INDEX_Z,
23	ACCEL_3D_CHANNEL_MAX,
24};
25
26struct accel_3d_state {
27	struct hid_sensor_hub_callbacks callbacks;
28	struct hid_sensor_common common_attributes;
29	struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
30	/* Ensure timestamp is naturally aligned */
31	struct {
32		u32 accel_val[3];
33		s64 timestamp __aligned(8);
34	} scan;
35	int scale_pre_decml;
36	int scale_post_decml;
37	int scale_precision;
38	int value_offset;
39	int64_t timestamp;
40};
41
42static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
43	HID_USAGE_SENSOR_ACCEL_X_AXIS,
44	HID_USAGE_SENSOR_ACCEL_Y_AXIS,
45	HID_USAGE_SENSOR_ACCEL_Z_AXIS
46};
47
48/* Channel definitions */
49static const struct iio_chan_spec accel_3d_channels[] = {
50	{
51		.type = IIO_ACCEL,
52		.modified = 1,
53		.channel2 = IIO_MOD_X,
54		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
55		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
56		BIT(IIO_CHAN_INFO_SCALE) |
57		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
58		BIT(IIO_CHAN_INFO_HYSTERESIS),
59		.scan_index = CHANNEL_SCAN_INDEX_X,
60	}, {
61		.type = IIO_ACCEL,
62		.modified = 1,
63		.channel2 = IIO_MOD_Y,
64		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
65		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
66		BIT(IIO_CHAN_INFO_SCALE) |
67		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
68		BIT(IIO_CHAN_INFO_HYSTERESIS),
69		.scan_index = CHANNEL_SCAN_INDEX_Y,
70	}, {
71		.type = IIO_ACCEL,
72		.modified = 1,
73		.channel2 = IIO_MOD_Z,
74		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
75		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
76		BIT(IIO_CHAN_INFO_SCALE) |
77		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
78		BIT(IIO_CHAN_INFO_HYSTERESIS),
79		.scan_index = CHANNEL_SCAN_INDEX_Z,
80	},
81	IIO_CHAN_SOFT_TIMESTAMP(3)
82};
83
84/* Channel definitions */
85static const struct iio_chan_spec gravity_channels[] = {
86	{
87		.type = IIO_GRAVITY,
88		.modified = 1,
89		.channel2 = IIO_MOD_X,
90		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
91		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
92		BIT(IIO_CHAN_INFO_SCALE) |
93		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
94		BIT(IIO_CHAN_INFO_HYSTERESIS),
95		.scan_index = CHANNEL_SCAN_INDEX_X,
96	}, {
97		.type = IIO_GRAVITY,
98		.modified = 1,
99		.channel2 = IIO_MOD_Y,
100		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
101		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
102		BIT(IIO_CHAN_INFO_SCALE) |
103		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
104		BIT(IIO_CHAN_INFO_HYSTERESIS),
105		.scan_index = CHANNEL_SCAN_INDEX_Y,
106	}, {
107		.type = IIO_GRAVITY,
108		.modified = 1,
109		.channel2 = IIO_MOD_Z,
110		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
111		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
112		BIT(IIO_CHAN_INFO_SCALE) |
113		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
114		BIT(IIO_CHAN_INFO_HYSTERESIS),
115		.scan_index = CHANNEL_SCAN_INDEX_Z,
116	}
117};
118
119/* Adjust channel real bits based on report descriptor */
120static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
121						int channel, int size)
122{
123	channels[channel].scan_type.sign = 's';
124	/* Real storage bits will change based on the report desc. */
125	channels[channel].scan_type.realbits = size * 8;
126	/* Maximum size of a sample to capture is u32 */
127	channels[channel].scan_type.storagebits = sizeof(u32) * 8;
128}
129
130/* Channel read_raw handler */
131static int accel_3d_read_raw(struct iio_dev *indio_dev,
132			      struct iio_chan_spec const *chan,
133			      int *val, int *val2,
134			      long mask)
135{
136	struct accel_3d_state *accel_state = iio_priv(indio_dev);
137	int report_id = -1;
138	u32 address;
139	int ret_type;
140	s32 min;
141	struct hid_sensor_hub_device *hsdev =
142					accel_state->common_attributes.hsdev;
143
144	*val = 0;
145	*val2 = 0;
146	switch (mask) {
147	case IIO_CHAN_INFO_RAW:
148		hid_sensor_power_state(&accel_state->common_attributes, true);
149		report_id = accel_state->accel[chan->scan_index].report_id;
150		min = accel_state->accel[chan->scan_index].logical_minimum;
151		address = accel_3d_addresses[chan->scan_index];
152		if (report_id >= 0)
153			*val = sensor_hub_input_attr_get_raw_value(
154					accel_state->common_attributes.hsdev,
155					hsdev->usage, address, report_id,
156					SENSOR_HUB_SYNC,
157					min < 0);
158		else {
159			*val = 0;
160			hid_sensor_power_state(&accel_state->common_attributes,
161						 false);
162			return -EINVAL;
163		}
164		hid_sensor_power_state(&accel_state->common_attributes, false);
165		ret_type = IIO_VAL_INT;
166		break;
167	case IIO_CHAN_INFO_SCALE:
168		*val = accel_state->scale_pre_decml;
169		*val2 = accel_state->scale_post_decml;
170		ret_type = accel_state->scale_precision;
171		break;
172	case IIO_CHAN_INFO_OFFSET:
173		*val = accel_state->value_offset;
174		ret_type = IIO_VAL_INT;
175		break;
176	case IIO_CHAN_INFO_SAMP_FREQ:
177		ret_type = hid_sensor_read_samp_freq_value(
178			&accel_state->common_attributes, val, val2);
179		break;
180	case IIO_CHAN_INFO_HYSTERESIS:
181		ret_type = hid_sensor_read_raw_hyst_value(
182			&accel_state->common_attributes, val, val2);
183		break;
184	default:
185		ret_type = -EINVAL;
186		break;
187	}
188
189	return ret_type;
190}
191
192/* Channel write_raw handler */
193static int accel_3d_write_raw(struct iio_dev *indio_dev,
194			       struct iio_chan_spec const *chan,
195			       int val,
196			       int val2,
197			       long mask)
198{
199	struct accel_3d_state *accel_state = iio_priv(indio_dev);
200	int ret = 0;
201
202	switch (mask) {
203	case IIO_CHAN_INFO_SAMP_FREQ:
204		ret = hid_sensor_write_samp_freq_value(
205				&accel_state->common_attributes, val, val2);
206		break;
207	case IIO_CHAN_INFO_HYSTERESIS:
208		ret = hid_sensor_write_raw_hyst_value(
209				&accel_state->common_attributes, val, val2);
210		break;
211	default:
212		ret = -EINVAL;
213	}
214
215	return ret;
216}
217
218static const struct iio_info accel_3d_info = {
219	.read_raw = &accel_3d_read_raw,
220	.write_raw = &accel_3d_write_raw,
221};
222
223/* Function to push data to buffer */
224static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data,
225				 int len, int64_t timestamp)
226{
227	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
228	iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
229}
230
231/* Callback handler to send event after all samples are received and captured */
232static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
233				unsigned usage_id,
234				void *priv)
235{
236	struct iio_dev *indio_dev = platform_get_drvdata(priv);
237	struct accel_3d_state *accel_state = iio_priv(indio_dev);
238
239	dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n");
240	if (atomic_read(&accel_state->common_attributes.data_ready)) {
241		if (!accel_state->timestamp)
242			accel_state->timestamp = iio_get_time_ns(indio_dev);
243
244		hid_sensor_push_data(indio_dev,
245				     &accel_state->scan,
246				     sizeof(accel_state->scan),
247				     accel_state->timestamp);
248
249		accel_state->timestamp = 0;
250	}
251
252	return 0;
253}
254
255/* Capture samples in local storage */
256static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
257				unsigned usage_id,
258				size_t raw_len, char *raw_data,
259				void *priv)
260{
261	struct iio_dev *indio_dev = platform_get_drvdata(priv);
262	struct accel_3d_state *accel_state = iio_priv(indio_dev);
263	int offset;
264	int ret = -EINVAL;
265
266	switch (usage_id) {
267	case HID_USAGE_SENSOR_ACCEL_X_AXIS:
268	case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
269	case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
270		offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
271		accel_state->scan.accel_val[CHANNEL_SCAN_INDEX_X + offset] =
272						*(u32 *)raw_data;
273		ret = 0;
274	break;
275	case HID_USAGE_SENSOR_TIME_TIMESTAMP:
276		accel_state->timestamp =
277			hid_sensor_convert_timestamp(
278					&accel_state->common_attributes,
279					*(int64_t *)raw_data);
280		ret = 0;
281	break;
282	default:
283		break;
284	}
285
286	return ret;
287}
288
289/* Parse report which is specific to an usage id*/
290static int accel_3d_parse_report(struct platform_device *pdev,
291				struct hid_sensor_hub_device *hsdev,
292				struct iio_chan_spec *channels,
293				unsigned usage_id,
294				struct accel_3d_state *st)
295{
296	int ret;
297	int i;
298
299	for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
300		ret = sensor_hub_input_get_attribute_info(hsdev,
301				HID_INPUT_REPORT,
302				usage_id,
303				HID_USAGE_SENSOR_ACCEL_X_AXIS + i,
304				&st->accel[CHANNEL_SCAN_INDEX_X + i]);
305		if (ret < 0)
306			break;
307		accel_3d_adjust_channel_bit_mask(channels,
308				CHANNEL_SCAN_INDEX_X + i,
309				st->accel[CHANNEL_SCAN_INDEX_X + i].size);
310	}
311	dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
312			st->accel[0].index,
313			st->accel[0].report_id,
314			st->accel[1].index, st->accel[1].report_id,
315			st->accel[2].index, st->accel[2].report_id);
316
317	st->scale_precision = hid_sensor_format_scale(
318				hsdev->usage,
319				&st->accel[CHANNEL_SCAN_INDEX_X],
320				&st->scale_pre_decml, &st->scale_post_decml);
321
322	/* Set Sensitivity field ids, when there is no individual modifier */
323	if (st->common_attributes.sensitivity.index < 0) {
324		sensor_hub_input_get_attribute_info(hsdev,
325			HID_FEATURE_REPORT, usage_id,
326			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
327			HID_USAGE_SENSOR_DATA_ACCELERATION,
328			&st->common_attributes.sensitivity);
329		dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
330			st->common_attributes.sensitivity.index,
331			st->common_attributes.sensitivity.report_id);
332	}
333
334	return ret;
335}
336
337/* Function to initialize the processing for usage id */
338static int hid_accel_3d_probe(struct platform_device *pdev)
339{
340	int ret = 0;
341	const char *name;
342	struct iio_dev *indio_dev;
343	struct accel_3d_state *accel_state;
344	const struct iio_chan_spec *channel_spec;
345	int channel_size;
346
347	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
348
349	indio_dev = devm_iio_device_alloc(&pdev->dev,
350					  sizeof(struct accel_3d_state));
351	if (indio_dev == NULL)
352		return -ENOMEM;
353
354	platform_set_drvdata(pdev, indio_dev);
355
356	accel_state = iio_priv(indio_dev);
357	accel_state->common_attributes.hsdev = hsdev;
358	accel_state->common_attributes.pdev = pdev;
359
360	if (hsdev->usage == HID_USAGE_SENSOR_ACCEL_3D) {
361		name = "accel_3d";
362		channel_spec = accel_3d_channels;
363		channel_size = sizeof(accel_3d_channels);
364		indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
365	} else {
366		name = "gravity";
367		channel_spec = gravity_channels;
368		channel_size = sizeof(gravity_channels);
369		indio_dev->num_channels = ARRAY_SIZE(gravity_channels);
370	}
371	ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage,
372					&accel_state->common_attributes);
373	if (ret) {
374		dev_err(&pdev->dev, "failed to setup common attributes\n");
375		return ret;
376	}
377	indio_dev->channels = kmemdup(channel_spec, channel_size, GFP_KERNEL);
378
379	if (!indio_dev->channels) {
380		dev_err(&pdev->dev, "failed to duplicate channels\n");
381		return -ENOMEM;
382	}
383	ret = accel_3d_parse_report(pdev, hsdev,
384				(struct iio_chan_spec *)indio_dev->channels,
385				hsdev->usage, accel_state);
386	if (ret) {
387		dev_err(&pdev->dev, "failed to setup attributes\n");
388		goto error_free_dev_mem;
389	}
390
391	indio_dev->info = &accel_3d_info;
392	indio_dev->name = name;
393	indio_dev->modes = INDIO_DIRECT_MODE;
394
395	atomic_set(&accel_state->common_attributes.data_ready, 0);
396
397	ret = hid_sensor_setup_trigger(indio_dev, name,
398					&accel_state->common_attributes);
399	if (ret < 0) {
400		dev_err(&pdev->dev, "trigger setup failed\n");
401		goto error_free_dev_mem;
402	}
403
404	ret = iio_device_register(indio_dev);
405	if (ret) {
406		dev_err(&pdev->dev, "device register failed\n");
407		goto error_remove_trigger;
408	}
409
410	accel_state->callbacks.send_event = accel_3d_proc_event;
411	accel_state->callbacks.capture_sample = accel_3d_capture_sample;
412	accel_state->callbacks.pdev = pdev;
413	ret = sensor_hub_register_callback(hsdev, hsdev->usage,
414					&accel_state->callbacks);
415	if (ret < 0) {
416		dev_err(&pdev->dev, "callback reg failed\n");
417		goto error_iio_unreg;
418	}
419
420	return ret;
421
422error_iio_unreg:
423	iio_device_unregister(indio_dev);
424error_remove_trigger:
425	hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
426error_free_dev_mem:
427	kfree(indio_dev->channels);
428	return ret;
429}
430
431/* Function to deinitialize the processing for usage id */
432static int hid_accel_3d_remove(struct platform_device *pdev)
433{
434	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
435	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
436	struct accel_3d_state *accel_state = iio_priv(indio_dev);
437
438	sensor_hub_remove_callback(hsdev, hsdev->usage);
439	iio_device_unregister(indio_dev);
440	hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
441	kfree(indio_dev->channels);
442
443	return 0;
444}
445
446static const struct platform_device_id hid_accel_3d_ids[] = {
447	{
448		/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
449		.name = "HID-SENSOR-200073",
450	},
451	{	/* gravity sensor */
452		.name = "HID-SENSOR-20007b",
453	},
454	{ /* sentinel */ }
455};
456MODULE_DEVICE_TABLE(platform, hid_accel_3d_ids);
457
458static struct platform_driver hid_accel_3d_platform_driver = {
459	.id_table = hid_accel_3d_ids,
460	.driver = {
461		.name	= KBUILD_MODNAME,
462		.pm	= &hid_sensor_pm_ops,
463	},
464	.probe		= hid_accel_3d_probe,
465	.remove		= hid_accel_3d_remove,
466};
467module_platform_driver(hid_accel_3d_platform_driver);
468
469MODULE_DESCRIPTION("HID Sensor Accel 3D");
470MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
471MODULE_LICENSE("GPL");
472