xref: /linux/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1cbf99241SChris Morgan // SPDX-License-Identifier: GPL-2.0-or-later
2cbf99241SChris Morgan /*
3cbf99241SChris Morgan  * Copyright (C) 2026 InvenSense, Inc.
4cbf99241SChris Morgan  */
5cbf99241SChris Morgan 
6cbf99241SChris Morgan #include <linux/array_size.h>
7cbf99241SChris Morgan #include <linux/bits.h>
8cbf99241SChris Morgan #include <linux/cleanup.h>
9cbf99241SChris Morgan #include <linux/device/devres.h>
10cbf99241SChris Morgan #include <linux/err.h>
11cbf99241SChris Morgan #include <linux/iio/iio.h>
12cbf99241SChris Morgan #include <linux/mutex.h>
13cbf99241SChris Morgan #include <linux/pm_runtime.h>
14cbf99241SChris Morgan #include <linux/regmap.h>
15cbf99241SChris Morgan #include <linux/types.h>
16cbf99241SChris Morgan 
17cbf99241SChris Morgan #include "inv_icm42607.h"
18*512d321bSChris Morgan #include "inv_icm42607_temp.h"
19cbf99241SChris Morgan 
20cbf99241SChris Morgan #define INV_ICM42607_ACCEL_CHAN(_modifier, _index, _ext_info)			\
21cbf99241SChris Morgan {										\
22cbf99241SChris Morgan 	.type = IIO_ACCEL,							\
23cbf99241SChris Morgan 	.modified = 1,								\
24cbf99241SChris Morgan 	.channel2 = _modifier,							\
25cbf99241SChris Morgan 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),				\
26cbf99241SChris Morgan 	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),			\
27cbf99241SChris Morgan 	.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),		\
28cbf99241SChris Morgan 	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
29cbf99241SChris Morgan 	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
30cbf99241SChris Morgan 	.scan_index = _index,							\
31cbf99241SChris Morgan 	.scan_type = {								\
32cbf99241SChris Morgan 		.sign = 's',							\
33cbf99241SChris Morgan 		.realbits = 16,							\
34cbf99241SChris Morgan 		.storagebits = 16,						\
35cbf99241SChris Morgan 		.endianness = IIO_BE,						\
36cbf99241SChris Morgan 	},									\
37cbf99241SChris Morgan 	.ext_info = _ext_info,							\
38cbf99241SChris Morgan }
39cbf99241SChris Morgan 
40cbf99241SChris Morgan enum inv_icm42607_accel_scan {
41cbf99241SChris Morgan 	INV_ICM42607_ACCEL_SCAN_X,
42cbf99241SChris Morgan 	INV_ICM42607_ACCEL_SCAN_Y,
43cbf99241SChris Morgan 	INV_ICM42607_ACCEL_SCAN_Z,
44*512d321bSChris Morgan 	INV_ICM42607_ACCEL_SCAN_TEMP,
45cbf99241SChris Morgan };
46cbf99241SChris Morgan 
47cbf99241SChris Morgan static const struct iio_chan_spec_ext_info inv_icm42607_accel_ext_infos[] = {
48cbf99241SChris Morgan 	IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix),
49cbf99241SChris Morgan 	{ }
50cbf99241SChris Morgan };
51cbf99241SChris Morgan 
52cbf99241SChris Morgan static const struct iio_chan_spec inv_icm42607_accel_channels[] = {
53cbf99241SChris Morgan 	INV_ICM42607_ACCEL_CHAN(IIO_MOD_X, INV_ICM42607_ACCEL_SCAN_X,
54cbf99241SChris Morgan 				inv_icm42607_accel_ext_infos),
55cbf99241SChris Morgan 	INV_ICM42607_ACCEL_CHAN(IIO_MOD_Y, INV_ICM42607_ACCEL_SCAN_Y,
56cbf99241SChris Morgan 				inv_icm42607_accel_ext_infos),
57cbf99241SChris Morgan 	INV_ICM42607_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42607_ACCEL_SCAN_Z,
58cbf99241SChris Morgan 				inv_icm42607_accel_ext_infos),
59*512d321bSChris Morgan 	INV_ICM42607_TEMP_CHAN(INV_ICM42607_ACCEL_SCAN_TEMP),
60cbf99241SChris Morgan };
61cbf99241SChris Morgan 
62cbf99241SChris Morgan static const int inv_icm42607_accel_scale_nano[][2] = {
63cbf99241SChris Morgan 	[INV_ICM42607_ACCEL_FS_16G] = { 0, 4788403 },
64cbf99241SChris Morgan 	[INV_ICM42607_ACCEL_FS_8G] = { 0, 2394202 },
65cbf99241SChris Morgan 	[INV_ICM42607_ACCEL_FS_4G] = { 0, 1197101 },
66cbf99241SChris Morgan 	[INV_ICM42607_ACCEL_FS_2G] = { 0, 598550 },
67cbf99241SChris Morgan };
68cbf99241SChris Morgan 
69cbf99241SChris Morgan static int inv_icm42607_accel_read_scale(struct iio_dev *indio_dev,
70cbf99241SChris Morgan 					 int *val, int *val2)
71cbf99241SChris Morgan {
72cbf99241SChris Morgan 	struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
73cbf99241SChris Morgan 	unsigned int idx;
74cbf99241SChris Morgan 
75cbf99241SChris Morgan 	guard(mutex)(&st->lock);
76cbf99241SChris Morgan 
77cbf99241SChris Morgan 	idx = st->conf.accel.fs;
78cbf99241SChris Morgan 
79cbf99241SChris Morgan 	*val = inv_icm42607_accel_scale_nano[idx][0];
80cbf99241SChris Morgan 	*val2 = inv_icm42607_accel_scale_nano[idx][1];
81cbf99241SChris Morgan 	return IIO_VAL_INT_PLUS_NANO;
82cbf99241SChris Morgan }
83cbf99241SChris Morgan 
84cbf99241SChris Morgan static int inv_icm42607_accel_write_scale(struct iio_dev *indio_dev,
85cbf99241SChris Morgan 					  int val, int val2)
86cbf99241SChris Morgan {
87cbf99241SChris Morgan 	struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
88cbf99241SChris Morgan 	struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
89cbf99241SChris Morgan 	size_t scales_len = ARRAY_SIZE(inv_icm42607_accel_scale_nano);
90cbf99241SChris Morgan 	struct device *dev = regmap_get_device(st->map);
91cbf99241SChris Morgan 	unsigned int idx;
92cbf99241SChris Morgan 	int ret;
93cbf99241SChris Morgan 
94cbf99241SChris Morgan 	for (idx = 0; idx < scales_len; idx++) {
95cbf99241SChris Morgan 		if (val == inv_icm42607_accel_scale_nano[idx][0] &&
96cbf99241SChris Morgan 		    val2 == inv_icm42607_accel_scale_nano[idx][1])
97cbf99241SChris Morgan 			break;
98cbf99241SChris Morgan 	}
99cbf99241SChris Morgan 	if (idx == scales_len)
100cbf99241SChris Morgan 		return -EINVAL;
101cbf99241SChris Morgan 
102cbf99241SChris Morgan 	conf.fs = idx;
103cbf99241SChris Morgan 
104cbf99241SChris Morgan 	PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
105cbf99241SChris Morgan 	ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
106cbf99241SChris Morgan 	if (ret)
107cbf99241SChris Morgan 		return ret;
108cbf99241SChris Morgan 
109cbf99241SChris Morgan 	guard(mutex)(&st->lock);
110cbf99241SChris Morgan 
111cbf99241SChris Morgan 	return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL);
112cbf99241SChris Morgan }
113cbf99241SChris Morgan 
114cbf99241SChris Morgan /* IIO format int + micro , values 0-4 reserved. */
115cbf99241SChris Morgan static const int inv_icm42607_accel_odr[][2] = {
116cbf99241SChris Morgan 	[INV_ICM42607_ODR_1600HZ] = { 1600, 0 },
117cbf99241SChris Morgan 	[INV_ICM42607_ODR_800HZ] = { 800, 0 },
118cbf99241SChris Morgan 	[INV_ICM42607_ODR_400HZ] = { 400, 0 },
119cbf99241SChris Morgan 	[INV_ICM42607_ODR_200HZ] = { 200, 0 },
120cbf99241SChris Morgan 	[INV_ICM42607_ODR_100HZ] = { 100, 0 },
121cbf99241SChris Morgan 	[INV_ICM42607_ODR_50HZ] = { 50, 0 },
122cbf99241SChris Morgan 	[INV_ICM42607_ODR_25HZ] = { 25, 0 },
123cbf99241SChris Morgan 	[INV_ICM42607_ODR_12_5HZ] = { 12, 500000 },
124cbf99241SChris Morgan 	[INV_ICM42607_ODR_6_25HZ_LP] = { 6, 250000 },
125cbf99241SChris Morgan 	[INV_ICM42607_ODR_3_125HZ_LP] = { 3, 125000 },
126cbf99241SChris Morgan 	[INV_ICM42607_ODR_1_5625HZ_LP] = { 1, 562500 },
127cbf99241SChris Morgan };
128cbf99241SChris Morgan 
129cbf99241SChris Morgan static int inv_icm42607_accel_read_odr(struct inv_icm42607_state *st,
130cbf99241SChris Morgan 				       int *val, int *val2)
131cbf99241SChris Morgan {
132cbf99241SChris Morgan 	unsigned int odr;
133cbf99241SChris Morgan 	unsigned int i;
134cbf99241SChris Morgan 
135cbf99241SChris Morgan 	guard(mutex)(&st->lock);
136cbf99241SChris Morgan 
137cbf99241SChris Morgan 	odr = st->conf.accel.odr;
138cbf99241SChris Morgan 
139cbf99241SChris Morgan 	for (i = INV_ICM42607_ODR_1600HZ; i < ARRAY_SIZE(inv_icm42607_accel_odr); i++) {
140cbf99241SChris Morgan 		if (i == odr)
141cbf99241SChris Morgan 			break;
142cbf99241SChris Morgan 	}
143cbf99241SChris Morgan 	if (i == ARRAY_SIZE(inv_icm42607_accel_odr))
144cbf99241SChris Morgan 		return -EINVAL;
145cbf99241SChris Morgan 
146cbf99241SChris Morgan 	*val = inv_icm42607_accel_odr[i][0];
147cbf99241SChris Morgan 	*val2 = inv_icm42607_accel_odr[i][1];
148cbf99241SChris Morgan 
149cbf99241SChris Morgan 	return IIO_VAL_INT_PLUS_MICRO;
150cbf99241SChris Morgan }
151cbf99241SChris Morgan 
152cbf99241SChris Morgan static int inv_icm42607_accel_write_odr(struct iio_dev *indio_dev,
153cbf99241SChris Morgan 					int val, int val2)
154cbf99241SChris Morgan {
155cbf99241SChris Morgan 	struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
156cbf99241SChris Morgan 	struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
157cbf99241SChris Morgan 	struct device *dev = regmap_get_device(st->map);
158cbf99241SChris Morgan 	unsigned int idx;
159cbf99241SChris Morgan 	int ret;
160cbf99241SChris Morgan 
161cbf99241SChris Morgan 	for (idx = INV_ICM42607_ODR_1600HZ;
162cbf99241SChris Morgan 	     idx < ARRAY_SIZE(inv_icm42607_accel_odr); idx++) {
163cbf99241SChris Morgan 		if (val == inv_icm42607_accel_odr[idx][0] &&
164cbf99241SChris Morgan 		    val2 == inv_icm42607_accel_odr[idx][1])
165cbf99241SChris Morgan 			break;
166cbf99241SChris Morgan 	}
167cbf99241SChris Morgan 	if (idx == ARRAY_SIZE(inv_icm42607_accel_odr))
168cbf99241SChris Morgan 		return -EINVAL;
169cbf99241SChris Morgan 
170cbf99241SChris Morgan 	conf.odr = idx;
171cbf99241SChris Morgan 
172cbf99241SChris Morgan 	PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
173cbf99241SChris Morgan 	ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
174cbf99241SChris Morgan 	if (ret)
175cbf99241SChris Morgan 		return ret;
176cbf99241SChris Morgan 
177cbf99241SChris Morgan 	guard(mutex)(&st->lock);
178cbf99241SChris Morgan 
179cbf99241SChris Morgan 	return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL);
180cbf99241SChris Morgan }
181cbf99241SChris Morgan 
182cbf99241SChris Morgan static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev,
183cbf99241SChris Morgan 				       struct iio_chan_spec const *chan,
184cbf99241SChris Morgan 				       int *val, int *val2, long mask)
185cbf99241SChris Morgan {
186cbf99241SChris Morgan 	struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
187cbf99241SChris Morgan 	s16 data;
188cbf99241SChris Morgan 	int ret;
189cbf99241SChris Morgan 
190cbf99241SChris Morgan 	switch (chan->type) {
191cbf99241SChris Morgan 	case IIO_ACCEL:
192cbf99241SChris Morgan 		break;
193*512d321bSChris Morgan 	case IIO_TEMP:
194*512d321bSChris Morgan 		if (mask != IIO_CHAN_INFO_SAMP_FREQ)
195*512d321bSChris Morgan 			return inv_icm42607_temp_read_raw(indio_dev, chan,
196*512d321bSChris Morgan 							  val, val2, mask);
197*512d321bSChris Morgan 		break;
198cbf99241SChris Morgan 	default:
199cbf99241SChris Morgan 		return -EINVAL;
200cbf99241SChris Morgan 	}
201cbf99241SChris Morgan 
202cbf99241SChris Morgan 	switch (mask) {
203cbf99241SChris Morgan 	case IIO_CHAN_INFO_RAW:
204cbf99241SChris Morgan 		ret = inv_icm42607_read_sensor(indio_dev, chan, &data);
205cbf99241SChris Morgan 		if (ret)
206cbf99241SChris Morgan 			return ret;
207cbf99241SChris Morgan 		*val = data;
208cbf99241SChris Morgan 		return IIO_VAL_INT;
209cbf99241SChris Morgan 	case IIO_CHAN_INFO_SCALE:
210cbf99241SChris Morgan 		return inv_icm42607_accel_read_scale(indio_dev, val, val2);
211cbf99241SChris Morgan 	case IIO_CHAN_INFO_SAMP_FREQ:
212cbf99241SChris Morgan 		return inv_icm42607_accel_read_odr(st, val, val2);
213cbf99241SChris Morgan 	default:
214cbf99241SChris Morgan 		return -EINVAL;
215cbf99241SChris Morgan 	}
216cbf99241SChris Morgan }
217cbf99241SChris Morgan 
218cbf99241SChris Morgan static int inv_icm42607_accel_read_avail(struct iio_dev *indio_dev,
219cbf99241SChris Morgan 					 struct iio_chan_spec const *chan,
220cbf99241SChris Morgan 					 const int **vals,
221cbf99241SChris Morgan 					 int *type, int *length, long mask)
222cbf99241SChris Morgan {
223cbf99241SChris Morgan 	switch (mask) {
224cbf99241SChris Morgan 	case IIO_CHAN_INFO_SCALE:
225cbf99241SChris Morgan 		if (chan->type != IIO_ACCEL)
226cbf99241SChris Morgan 			return -EINVAL;
227cbf99241SChris Morgan 		*vals = (const int *)inv_icm42607_accel_scale_nano;
228cbf99241SChris Morgan 		*type = IIO_VAL_INT_PLUS_NANO;
229cbf99241SChris Morgan 		*length = ARRAY_SIZE(inv_icm42607_accel_scale_nano) * 2;
230cbf99241SChris Morgan 		return IIO_AVAIL_LIST;
231cbf99241SChris Morgan 	case IIO_CHAN_INFO_SAMP_FREQ:
232cbf99241SChris Morgan 		*vals = (const int *)inv_icm42607_accel_odr[INV_ICM42607_ODR_1600HZ];
233cbf99241SChris Morgan 		*type = IIO_VAL_INT_PLUS_MICRO;
234cbf99241SChris Morgan 		*length = (ARRAY_SIZE(inv_icm42607_accel_odr) -
235cbf99241SChris Morgan 			   INV_ICM42607_ODR_1600HZ) * 2;
236cbf99241SChris Morgan 		return IIO_AVAIL_LIST;
237cbf99241SChris Morgan 	default:
238cbf99241SChris Morgan 		return -EINVAL;
239cbf99241SChris Morgan 	}
240cbf99241SChris Morgan }
241cbf99241SChris Morgan 
242cbf99241SChris Morgan static int inv_icm42607_accel_write_raw(struct iio_dev *indio_dev,
243cbf99241SChris Morgan 					struct iio_chan_spec const *chan,
244cbf99241SChris Morgan 					int val, int val2, long mask)
245cbf99241SChris Morgan {
246cbf99241SChris Morgan 	int ret;
247cbf99241SChris Morgan 
248cbf99241SChris Morgan 	switch (mask) {
249cbf99241SChris Morgan 	case IIO_CHAN_INFO_SCALE:
250cbf99241SChris Morgan 		if (chan->type != IIO_ACCEL)
251cbf99241SChris Morgan 			return -EINVAL;
252cbf99241SChris Morgan 		ret = inv_icm42607_accel_write_scale(indio_dev, val, val2);
253cbf99241SChris Morgan 		return ret;
254cbf99241SChris Morgan 	case IIO_CHAN_INFO_SAMP_FREQ:
255cbf99241SChris Morgan 		return inv_icm42607_accel_write_odr(indio_dev, val, val2);
256cbf99241SChris Morgan 	default:
257cbf99241SChris Morgan 		return -EINVAL;
258cbf99241SChris Morgan 	}
259cbf99241SChris Morgan }
260cbf99241SChris Morgan 
261cbf99241SChris Morgan static int inv_icm42607_accel_write_raw_get_fmt(struct iio_dev *indio_dev,
262cbf99241SChris Morgan 						struct iio_chan_spec const *chan,
263cbf99241SChris Morgan 						long mask)
264cbf99241SChris Morgan {
265cbf99241SChris Morgan 	switch (mask) {
266cbf99241SChris Morgan 	case IIO_CHAN_INFO_SCALE:
267cbf99241SChris Morgan 		if (chan->type != IIO_ACCEL)
268cbf99241SChris Morgan 			return -EINVAL;
269cbf99241SChris Morgan 		return IIO_VAL_INT_PLUS_NANO;
270cbf99241SChris Morgan 	case IIO_CHAN_INFO_SAMP_FREQ:
271cbf99241SChris Morgan 		return IIO_VAL_INT_PLUS_MICRO;
272cbf99241SChris Morgan 	default:
273cbf99241SChris Morgan 		return -EINVAL;
274cbf99241SChris Morgan 	}
275cbf99241SChris Morgan }
276cbf99241SChris Morgan 
277cbf99241SChris Morgan static const struct iio_info inv_icm42607_accel_info = {
278cbf99241SChris Morgan 	.read_raw = inv_icm42607_accel_read_raw,
279cbf99241SChris Morgan 	.read_avail = inv_icm42607_accel_read_avail,
280cbf99241SChris Morgan 	.write_raw = inv_icm42607_accel_write_raw,
281cbf99241SChris Morgan 	.write_raw_get_fmt = inv_icm42607_accel_write_raw_get_fmt,
282cbf99241SChris Morgan };
283cbf99241SChris Morgan 
284cbf99241SChris Morgan struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st)
285cbf99241SChris Morgan {
286cbf99241SChris Morgan 	struct device *dev = regmap_get_device(st->map);
287cbf99241SChris Morgan 	struct inv_icm42607_sensor_state *accel_st;
288cbf99241SChris Morgan 	struct iio_dev *indio_dev;
289cbf99241SChris Morgan 	const char *name;
290cbf99241SChris Morgan 	int ret;
291cbf99241SChris Morgan 
292cbf99241SChris Morgan 	name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->hw->name);
293cbf99241SChris Morgan 	if (!name)
294cbf99241SChris Morgan 		return ERR_PTR(-ENOMEM);
295cbf99241SChris Morgan 
296cbf99241SChris Morgan 	indio_dev = devm_iio_device_alloc(dev, sizeof(*accel_st));
297cbf99241SChris Morgan 	if (!indio_dev)
298cbf99241SChris Morgan 		return ERR_PTR(-ENOMEM);
299cbf99241SChris Morgan 
300cbf99241SChris Morgan 	accel_st = iio_priv(indio_dev);
301cbf99241SChris Morgan 	accel_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
302cbf99241SChris Morgan 	accel_st->filter = INV_ICM42607_FILTER_BW_73HZ;
303cbf99241SChris Morgan 
304cbf99241SChris Morgan 	indio_dev->name = name;
305cbf99241SChris Morgan 	indio_dev->info = &inv_icm42607_accel_info;
306cbf99241SChris Morgan 	indio_dev->modes = INDIO_DIRECT_MODE;
307cbf99241SChris Morgan 	indio_dev->channels = inv_icm42607_accel_channels;
308cbf99241SChris Morgan 	indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_accel_channels);
309cbf99241SChris Morgan 	iio_device_set_drvdata(indio_dev, st);
310cbf99241SChris Morgan 
311cbf99241SChris Morgan 	ret = devm_iio_device_register(dev, indio_dev);
312cbf99241SChris Morgan 	if (ret)
313cbf99241SChris Morgan 		return ERR_PTR(ret);
314cbf99241SChris Morgan 
315cbf99241SChris Morgan 	return indio_dev;
316cbf99241SChris Morgan }
317