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