1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2026 InvenSense, Inc. 4 */ 5 6 #include <linux/bitfield.h> 7 #include <linux/cleanup.h> 8 #include <linux/device.h> 9 #include <linux/err.h> 10 #include <linux/iio/iio.h> 11 #include <linux/mutex.h> 12 #include <linux/pm_runtime.h> 13 #include <linux/regmap.h> 14 #include <linux/types.h> 15 #include <linux/unaligned.h> 16 17 #include "inv_icm42607.h" 18 #include "inv_icm42607_temp.h" 19 20 static int inv_icm42607_temp_read(struct inv_icm42607_state *st, s16 *temp) 21 { 22 struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT; 23 struct device *dev = regmap_get_device(st->map); 24 int ret, gyro_mode, accel_mode; 25 unsigned int val; 26 u8 raw[2]; 27 28 PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); 29 ret = PM_RUNTIME_ACQUIRE_ERR(&pm); 30 if (ret) 31 return ret; 32 33 guard(mutex)(&st->lock); 34 35 /* 36 * Check if both the gyro and accel are off and if so, enable one 37 * of them. The temp sensor cannot be read if both the gyro and 38 * accel sensor are off. Prefer to enable the accel over the gyro 39 * as the datasheet says the gyro uses 5x more power and it has 40 * a minimum run time of 45ms. 41 */ 42 ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val); 43 if (ret) 44 return ret; 45 46 accel_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val); 47 gyro_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val); 48 if (!gyro_mode && !accel_mode) { 49 /* enable accel sensor */ 50 conf.mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE; 51 ret = inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL); 52 if (ret) 53 return ret; 54 } 55 56 ret = regmap_bulk_read(st->map, INV_ICM42607_REG_TEMP_DATA1, 57 raw, sizeof(raw)); 58 if (ret) 59 return ret; 60 61 *temp = get_unaligned_be16(raw); 62 if (*temp == INV_ICM42607_DATA_INVALID) 63 return -EINVAL; 64 65 return 0; 66 } 67 68 int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev, 69 struct iio_chan_spec const *chan, 70 int *val, int *val2, long mask) 71 { 72 struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev); 73 s16 temp; 74 int ret; 75 76 switch (mask) { 77 case IIO_CHAN_INFO_RAW: 78 ret = inv_icm42607_temp_read(st, &temp); 79 if (ret) 80 return ret; 81 *val = temp; 82 return IIO_VAL_INT; 83 /* 84 * T°C = (temp / 128) + 25 85 * Tm°C = 1000 * ((temp * 100 / 12800) + 25) 86 * scale: 100000 / 12800 ~= 7.8125 87 * offset: 3200 88 */ 89 case IIO_CHAN_INFO_SCALE: 90 *val = 7; 91 *val2 = 812500000; 92 return IIO_VAL_INT_PLUS_NANO; 93 case IIO_CHAN_INFO_OFFSET: 94 *val = 3200; 95 return IIO_VAL_INT; 96 default: 97 return -EINVAL; 98 } 99 } 100