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