// SPDX-License-Identifier: GPL-2.0-or-later /* Copyright (C) 2025 Invensense, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inv_icm45600_buffer.h" #include "inv_icm45600.h" static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg, u8 *data, size_t count) { const struct device *dev = regmap_get_device(map); struct inv_icm45600_state *st = dev_get_drvdata(dev); unsigned int d; size_t i; int ret; st->buffer.ireg[0] = FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg); st->buffer.ireg[1] = FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg); /* Burst write address. */ ret = regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, st->buffer.ireg, 2); /* * Wait while the device is busy processing the address. * Datasheet: 13.3 MINIMUM WAIT TIME-GAP */ fsleep(INV_ICM45600_IREG_DELAY_US); if (ret) return ret; /* Read the data. */ for (i = 0; i < count; i++) { ret = regmap_read(map, INV_ICM45600_REG_IREG_DATA, &d); /* * Wait while the device is busy processing the address. * Datasheet: 13.3 MINIMUM WAIT TIME-GAP */ fsleep(INV_ICM45600_IREG_DELAY_US); if (ret) return ret; data[i] = d; } return 0; } static int inv_icm45600_ireg_write(struct regmap *map, unsigned int reg, const u8 *data, size_t count) { const struct device *dev = regmap_get_device(map); struct inv_icm45600_state *st = dev_get_drvdata(dev); size_t i; int ret; st->buffer.ireg[0] = FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg); st->buffer.ireg[1] = FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg); st->buffer.ireg[2] = data[0]; /* Burst write address and first byte. */ ret = regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, st->buffer.ireg, 3); /* * Wait while the device is busy processing the address. * Datasheet: 13.3 MINIMUM WAIT TIME-GAP */ fsleep(INV_ICM45600_IREG_DELAY_US); if (ret) return ret; /* Write the remaining bytes. */ for (i = 1; i < count; i++) { ret = regmap_write(map, INV_ICM45600_REG_IREG_DATA, data[i]); /* * Wait while the device is busy processing the address. * Datasheet: 13.3 MINIMUM WAIT TIME-GAP */ fsleep(INV_ICM45600_IREG_DELAY_US); if (ret) return ret; } return 0; } static int inv_icm45600_read(void *context, const void *reg_buf, size_t reg_size, void *val_buf, size_t val_size) { unsigned int reg = be16_to_cpup(reg_buf); struct regmap *map = context; if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg)) return inv_icm45600_ireg_read(map, reg, val_buf, val_size); return regmap_bulk_read(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg), val_buf, val_size); } static int inv_icm45600_write(void *context, const void *data, size_t count) { const u8 *d = data; unsigned int reg = be16_to_cpup(data); struct regmap *map = context; if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg)) return inv_icm45600_ireg_write(map, reg, d + 2, count - 2); return regmap_bulk_write(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg), d + 2, count - 2); } static const struct regmap_bus inv_icm45600_regmap_bus = { .read = inv_icm45600_read, .write = inv_icm45600_write, }; static const struct regmap_config inv_icm45600_regmap_config = { .reg_bits = 16, .val_bits = 8, }; /* These are the chip initial default configurations (default FS value is based on icm45686) */ static const struct inv_icm45600_conf inv_icm45600_default_conf = { .gyro = { .mode = INV_ICM45600_SENSOR_MODE_OFF, .fs = INV_ICM45686_GYRO_FS_2000DPS, .odr = INV_ICM45600_ODR_800HZ_LN, .filter = INV_ICM45600_GYRO_LP_AVG_SEL_8X, }, .accel = { .mode = INV_ICM45600_SENSOR_MODE_OFF, .fs = INV_ICM45686_ACCEL_FS_16G, .odr = INV_ICM45600_ODR_800HZ_LN, .filter = INV_ICM45600_ACCEL_LP_AVG_SEL_4X, }, }; static const struct inv_icm45600_conf inv_icm45686_default_conf = { .gyro = { .mode = INV_ICM45600_SENSOR_MODE_OFF, .fs = INV_ICM45686_GYRO_FS_4000DPS, .odr = INV_ICM45600_ODR_800HZ_LN, .filter = INV_ICM45600_GYRO_LP_AVG_SEL_8X, }, .accel = { .mode = INV_ICM45600_SENSOR_MODE_OFF, .fs = INV_ICM45686_ACCEL_FS_32G, .odr = INV_ICM45600_ODR_800HZ_LN, .filter = INV_ICM45600_ACCEL_LP_AVG_SEL_4X, }, }; const struct inv_icm45600_chip_info inv_icm45605_chip_info = { .whoami = INV_ICM45600_WHOAMI_ICM45605, .name = "icm45605", .conf = &inv_icm45600_default_conf, .accel_scales = (const int *)inv_icm45600_accel_scale, .accel_scales_len = INV_ICM45600_ACCEL_FS_MAX, .gyro_scales = (const int *)inv_icm45600_gyro_scale, .gyro_scales_len = INV_ICM45600_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45605_chip_info, "IIO_ICM45600"); const struct inv_icm45600_chip_info inv_icm45606_chip_info = { .whoami = INV_ICM45600_WHOAMI_ICM45606, .name = "icm45606", .conf = &inv_icm45600_default_conf, .accel_scales = (const int *)inv_icm45600_accel_scale, .accel_scales_len = INV_ICM45600_ACCEL_FS_MAX, .gyro_scales = (const int *)inv_icm45600_gyro_scale, .gyro_scales_len = INV_ICM45600_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45606_chip_info, "IIO_ICM45600"); const struct inv_icm45600_chip_info inv_icm45608_chip_info = { .whoami = INV_ICM45600_WHOAMI_ICM45608, .name = "icm45608", .conf = &inv_icm45600_default_conf, .accel_scales = (const int *)inv_icm45600_accel_scale, .accel_scales_len = INV_ICM45600_ACCEL_FS_MAX, .gyro_scales = (const int *)inv_icm45600_gyro_scale, .gyro_scales_len = INV_ICM45600_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45608_chip_info, "IIO_ICM45600"); const struct inv_icm45600_chip_info inv_icm45634_chip_info = { .whoami = INV_ICM45600_WHOAMI_ICM45634, .name = "icm45634", .conf = &inv_icm45600_default_conf, .accel_scales = (const int *)inv_icm45600_accel_scale, .accel_scales_len = INV_ICM45600_ACCEL_FS_MAX, .gyro_scales = (const int *)inv_icm45600_gyro_scale, .gyro_scales_len = INV_ICM45600_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45634_chip_info, "IIO_ICM45600"); const struct inv_icm45600_chip_info inv_icm45686_chip_info = { .whoami = INV_ICM45600_WHOAMI_ICM45686, .name = "icm45686", .conf = &inv_icm45686_default_conf, .accel_scales = (const int *)inv_icm45686_accel_scale, .accel_scales_len = INV_ICM45686_ACCEL_FS_MAX, .gyro_scales = (const int *)inv_icm45686_gyro_scale, .gyro_scales_len = INV_ICM45686_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45686_chip_info, "IIO_ICM45600"); const struct inv_icm45600_chip_info inv_icm45687_chip_info = { .whoami = INV_ICM45600_WHOAMI_ICM45687, .name = "icm45687", .conf = &inv_icm45686_default_conf, .accel_scales = (const int *)inv_icm45686_accel_scale, .accel_scales_len = INV_ICM45686_ACCEL_FS_MAX, .gyro_scales = (const int *)inv_icm45686_gyro_scale, .gyro_scales_len = INV_ICM45686_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45687_chip_info, "IIO_ICM45600"); const struct inv_icm45600_chip_info inv_icm45688p_chip_info = { .whoami = INV_ICM45600_WHOAMI_ICM45688P, .name = "icm45688p", .conf = &inv_icm45686_default_conf, .accel_scales = (const int *)inv_icm45686_accel_scale, .accel_scales_len = INV_ICM45686_ACCEL_FS_MAX, .gyro_scales = (const int *)inv_icm45686_gyro_scale, .gyro_scales_len = INV_ICM45686_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45688p_chip_info, "IIO_ICM45600"); const struct inv_icm45600_chip_info inv_icm45689_chip_info = { .whoami = INV_ICM45600_WHOAMI_ICM45689, .name = "icm45689", .conf = &inv_icm45686_default_conf, .accel_scales = (const int *)inv_icm45686_accel_scale, .accel_scales_len = INV_ICM45686_ACCEL_FS_MAX, .gyro_scales = (const int *)inv_icm45686_gyro_scale, .gyro_scales_len = INV_ICM45686_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45689_chip_info, "IIO_ICM45600"); const struct iio_mount_matrix * inv_icm45600_get_mount_matrix(const struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { const struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev); return &st->orientation; } u32 inv_icm45600_odr_to_period(enum inv_icm45600_odr odr) { static const u32 odr_periods[INV_ICM45600_ODR_MAX] = { /* 3 first values are reserved, left to 0 */ [INV_ICM45600_ODR_6400HZ_LN] = 156250, [INV_ICM45600_ODR_3200HZ_LN] = 312500, [INV_ICM45600_ODR_1600HZ_LN] = 625000, [INV_ICM45600_ODR_800HZ_LN] = 1250000, [INV_ICM45600_ODR_400HZ] = 2500000, [INV_ICM45600_ODR_200HZ] = 5000000, [INV_ICM45600_ODR_100HZ] = 10000000, [INV_ICM45600_ODR_50HZ] = 20000000, [INV_ICM45600_ODR_25HZ] = 40000000, [INV_ICM45600_ODR_12_5HZ] = 80000000, [INV_ICM45600_ODR_6_25HZ_LP] = 160000000, [INV_ICM45600_ODR_3_125HZ_LP] = 320000000, [INV_ICM45600_ODR_1_5625HZ_LP] = 640000000, }; return odr_periods[odr]; } static int inv_icm45600_set_pwr_mgmt0(struct inv_icm45600_state *st, enum inv_icm45600_sensor_mode gyro, enum inv_icm45600_sensor_mode accel, unsigned int *sleep_ms) { enum inv_icm45600_sensor_mode oldgyro = st->conf.gyro.mode; enum inv_icm45600_sensor_mode oldaccel = st->conf.accel.mode; unsigned int sleepval; unsigned int val; int ret; /* if nothing changed, exit */ if (gyro == oldgyro && accel == oldaccel) return 0; val = FIELD_PREP(INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK, gyro) | FIELD_PREP(INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK, accel); ret = regmap_write(st->map, INV_ICM45600_REG_PWR_MGMT0, val); if (ret) return ret; st->conf.gyro.mode = gyro; st->conf.accel.mode = accel; /* Compute the required wait time for sensors to stabilize. */ sleepval = 0; if (accel != oldaccel && oldaccel == INV_ICM45600_SENSOR_MODE_OFF) sleepval = max(sleepval, INV_ICM45600_ACCEL_STARTUP_TIME_MS); if (gyro != oldgyro) { if (oldgyro == INV_ICM45600_SENSOR_MODE_OFF) sleepval = max(sleepval, INV_ICM45600_GYRO_STARTUP_TIME_MS); else if (gyro == INV_ICM45600_SENSOR_MODE_OFF) sleepval = max(sleepval, INV_ICM45600_GYRO_STOP_TIME_MS); } /* Deferred sleep value if sleep pointer is provided or direct sleep */ if (sleep_ms) *sleep_ms = sleepval; else if (sleepval) msleep(sleepval); return 0; } static void inv_icm45600_set_default_conf(struct inv_icm45600_sensor_conf *conf, struct inv_icm45600_sensor_conf *oldconf) { /* Sanitize missing values with current values. */ if (conf->mode == U8_MAX) conf->mode = oldconf->mode; if (conf->fs == U8_MAX) conf->fs = oldconf->fs; if (conf->odr == U8_MAX) conf->odr = oldconf->odr; if (conf->filter == U8_MAX) conf->filter = oldconf->filter; } int inv_icm45600_set_accel_conf(struct inv_icm45600_state *st, struct inv_icm45600_sensor_conf *conf, unsigned int *sleep_ms) { struct inv_icm45600_sensor_conf *oldconf = &st->conf.accel; unsigned int val; int ret; inv_icm45600_set_default_conf(conf, oldconf); /* Force the power mode against the ODR when sensor is on. */ if (conf->mode > INV_ICM45600_SENSOR_MODE_STANDBY) { if (conf->odr <= INV_ICM45600_ODR_800HZ_LN) { conf->mode = INV_ICM45600_SENSOR_MODE_LOW_NOISE; } else { conf->mode = INV_ICM45600_SENSOR_MODE_LOW_POWER; /* sanitize averaging value depending on ODR for low-power mode */ /* maximum 1x @400Hz */ if (conf->odr == INV_ICM45600_ODR_400HZ) conf->filter = INV_ICM45600_ACCEL_LP_AVG_SEL_1X; else conf->filter = INV_ICM45600_ACCEL_LP_AVG_SEL_4X; } } /* Set accel fullscale & odr. */ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) { val = FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_FS_MASK, conf->fs) | FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_ODR_MASK, conf->odr); ret = regmap_write(st->map, INV_ICM45600_REG_ACCEL_CONFIG0, val); if (ret) return ret; oldconf->fs = conf->fs; oldconf->odr = conf->odr; } /* Set accel low-power average filter. */ if (conf->filter != oldconf->filter) { ret = regmap_write(st->map, INV_ICM45600_IPREG_SYS2_REG_129, conf->filter); if (ret) return ret; oldconf->filter = conf->filter; } /* Update the sensor accel mode. */ return inv_icm45600_set_pwr_mgmt0(st, st->conf.gyro.mode, conf->mode, sleep_ms); } int inv_icm45600_set_gyro_conf(struct inv_icm45600_state *st, struct inv_icm45600_sensor_conf *conf, unsigned int *sleep_ms) { struct inv_icm45600_sensor_conf *oldconf = &st->conf.gyro; unsigned int val; int ret; inv_icm45600_set_default_conf(conf, oldconf); /* Force the power mode against ODR when sensor is on. */ if (conf->mode > INV_ICM45600_SENSOR_MODE_STANDBY) { if (conf->odr >= INV_ICM45600_ODR_6_25HZ_LP) { conf->mode = INV_ICM45600_SENSOR_MODE_LOW_POWER; conf->filter = INV_ICM45600_GYRO_LP_AVG_SEL_8X; } else { conf->mode = INV_ICM45600_SENSOR_MODE_LOW_NOISE; } } /* Set gyro fullscale & odr. */ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) { val = FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_FS_MASK, conf->fs) | FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_ODR_MASK, conf->odr); ret = regmap_write(st->map, INV_ICM45600_REG_GYRO_CONFIG0, val); if (ret) return ret; oldconf->fs = conf->fs; oldconf->odr = conf->odr; } /* Set gyro low-power average filter. */ if (conf->filter != oldconf->filter) { val = FIELD_PREP(INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK, conf->filter); ret = regmap_update_bits(st->map, INV_ICM45600_IPREG_SYS1_REG_170, INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK, val); if (ret) return ret; oldconf->filter = conf->filter; } /* Update the sensor gyro mode. */ return inv_icm45600_set_pwr_mgmt0(st, conf->mode, st->conf.accel.mode, sleep_ms); } int inv_icm45600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval) { struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev); guard(mutex)(&st->lock); if (readval) return regmap_read(st->map, reg, readval); else return regmap_write(st->map, reg, writeval); } static int inv_icm45600_set_conf(struct inv_icm45600_state *st, const struct inv_icm45600_conf *conf) { unsigned int val; int ret; val = FIELD_PREP(INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK, conf->gyro.mode) | FIELD_PREP(INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK, conf->accel.mode); ret = regmap_write(st->map, INV_ICM45600_REG_PWR_MGMT0, val); if (ret) return ret; val = FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_FS_MASK, conf->gyro.fs) | FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_ODR_MASK, conf->gyro.odr); ret = regmap_write(st->map, INV_ICM45600_REG_GYRO_CONFIG0, val); if (ret) return ret; val = FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_FS_MASK, conf->accel.fs) | FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr); ret = regmap_write(st->map, INV_ICM45600_REG_ACCEL_CONFIG0, val); if (ret) return ret; /* Save configuration. */ st->conf = *conf; return 0; } /** * inv_icm45600_setup() - check and setup chip * @st: driver internal state * @chip_info: detected chip description * @reset: define whether a reset is required or not * @bus_setup: callback for setting up bus specific registers * * Returns: 0 on success, a negative error code otherwise. */ static int inv_icm45600_setup(struct inv_icm45600_state *st, const struct inv_icm45600_chip_info *chip_info, bool reset, inv_icm45600_bus_setup bus_setup) { const struct device *dev = regmap_get_device(st->map); unsigned int val; int ret; /* Set chip bus configuration if specified. */ if (bus_setup) { ret = bus_setup(st); if (ret) return ret; } /* Check chip self-identification value. */ ret = regmap_read(st->map, INV_ICM45600_REG_WHOAMI, &val); if (ret) return ret; if (val != chip_info->whoami) { /* * SPI interface has no ack mechanism. * 0xFF or 0x00 whoami means no response from the device. */ if (val == U8_MAX || val == 0) return dev_err_probe(dev, -ENODEV, "Invalid whoami %#02x expected %#02x (%s)\n", val, chip_info->whoami, chip_info->name); dev_warn(dev, "Unexpected whoami %#02x expected %#02x (%s)\n", val, chip_info->whoami, chip_info->name); } st->chip_info = chip_info; if (reset) { /* Reset previous state. */ ret = regmap_write(st->map, INV_ICM45600_REG_MISC2, INV_ICM45600_MISC2_SOFT_RESET); if (ret) return ret; /* * IMU reset time. * Datasheet: 16.84 REG_MISC2 */ fsleep(USEC_PER_MSEC); if (bus_setup) { ret = bus_setup(st); if (ret) return ret; } ret = regmap_read(st->map, INV_ICM45600_REG_INT_STATUS, &val); if (ret) return ret; if (!(val & INV_ICM45600_INT_STATUS_RESET_DONE)) { dev_err(dev, "reset error, reset done bit not set\n"); return -ENODEV; } } return inv_icm45600_set_conf(st, chip_info->conf); } static irqreturn_t inv_icm45600_irq_timestamp(int irq, void *_data) { struct inv_icm45600_state *st = _data; st->timestamp.gyro = iio_get_time_ns(st->indio_gyro); st->timestamp.accel = iio_get_time_ns(st->indio_accel); return IRQ_WAKE_THREAD; } static irqreturn_t inv_icm45600_irq_handler(int irq, void *_data) { struct inv_icm45600_state *st = _data; struct device *dev = regmap_get_device(st->map); unsigned int mask, status; int ret; guard(mutex)(&st->lock); ret = regmap_read(st->map, INV_ICM45600_REG_INT_STATUS, &status); if (ret) return IRQ_HANDLED; /* Read the FIFO data. */ mask = INV_ICM45600_INT_STATUS_FIFO_THS | INV_ICM45600_INT_STATUS_FIFO_FULL; if (status & mask) { ret = inv_icm45600_buffer_fifo_read(st, 0); if (ret) { dev_err(dev, "FIFO read error %d\n", ret); return IRQ_HANDLED; } ret = inv_icm45600_buffer_fifo_parse(st); if (ret) dev_err(dev, "FIFO parsing error %d\n", ret); } /* FIFO full warning. */ if (status & INV_ICM45600_INT_STATUS_FIFO_FULL) dev_warn(dev, "FIFO full possible data lost!\n"); return IRQ_HANDLED; } /** * inv_icm45600_irq_init() - initialize int pin and interrupt handler * @st: driver internal state * @irq: irq number * @irq_type: irq trigger type * @open_drain: true if irq is open drain, false for push-pull * * Returns: 0 on success, a negative error code otherwise. */ static int inv_icm45600_irq_init(struct inv_icm45600_state *st, int irq, int irq_type, bool open_drain) { struct device *dev = regmap_get_device(st->map); unsigned int val; int ret; /* Configure INT1 interrupt: default is active low on edge. */ switch (irq_type) { case IRQF_TRIGGER_RISING: case IRQF_TRIGGER_HIGH: val = INV_ICM45600_INT1_CONFIG2_ACTIVE_HIGH; break; default: val = INV_ICM45600_INT1_CONFIG2_ACTIVE_LOW; break; } switch (irq_type) { case IRQF_TRIGGER_LOW: case IRQF_TRIGGER_HIGH: val |= INV_ICM45600_INT1_CONFIG2_LATCHED; break; default: break; } if (!open_drain) val |= INV_ICM45600_INT1_CONFIG2_PUSH_PULL; ret = regmap_write(st->map, INV_ICM45600_REG_INT1_CONFIG2, val); if (ret) return ret; return devm_request_threaded_irq(dev, irq, inv_icm45600_irq_timestamp, inv_icm45600_irq_handler, irq_type | IRQF_ONESHOT, "inv_icm45600", st); } static int inv_icm45600_timestamp_setup(struct inv_icm45600_state *st) { /* Enable timestamps. */ return regmap_set_bits(st->map, INV_ICM45600_REG_SMC_CONTROL_0, INV_ICM45600_SMC_CONTROL_0_TMST_EN); } static int inv_icm45600_enable_regulator_vddio(struct inv_icm45600_state *st) { int ret; ret = regulator_enable(st->vddio_supply); if (ret) return ret; /* * Wait a little for supply ramp. * Duration is empirically defined. */ fsleep(3 * USEC_PER_MSEC); return 0; } static void inv_icm45600_disable_vddio_reg(void *_data) { struct inv_icm45600_state *st = _data; struct device *dev = regmap_get_device(st->map); if (pm_runtime_status_suspended(dev)) return; regulator_disable(st->vddio_supply); } int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chip_info *chip_info, bool reset, inv_icm45600_bus_setup bus_setup) { struct device *dev = regmap_get_device(regmap); struct inv_icm45600_state *st; struct regmap *regmap_custom; struct fwnode_handle *fwnode; int irq, irq_type; bool open_drain; int ret; /* Get INT1 only supported interrupt. */ fwnode = dev_fwnode(dev); irq = fwnode_irq_get_byname(fwnode, "int1"); if (irq < 0) return dev_err_probe(dev, irq, "Missing int1 interrupt\n"); irq_type = irq_get_trigger_type(irq); open_drain = device_property_read_bool(dev, "drive-open-drain"); regmap_custom = devm_regmap_init(dev, &inv_icm45600_regmap_bus, regmap, &inv_icm45600_regmap_config); if (IS_ERR(regmap_custom)) return dev_err_probe(dev, PTR_ERR(regmap_custom), "Failed to register regmap\n"); st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); if (!st) return -ENOMEM; dev_set_drvdata(dev, st); st->fifo.data = devm_kzalloc(dev, 8192, GFP_KERNEL); if (!st->fifo.data) return -ENOMEM; ret = devm_mutex_init(dev, &st->lock); if (ret) return ret; st->map = regmap_custom; ret = iio_read_mount_matrix(dev, &st->orientation); if (ret) return dev_err_probe(dev, ret, "Failed to retrieve mounting matrix\n"); st->vddio_supply = devm_regulator_get(dev, "vddio"); if (IS_ERR(st->vddio_supply)) return PTR_ERR(st->vddio_supply); ret = devm_regulator_get_enable(dev, "vdd"); if (ret) return dev_err_probe(dev, ret, "Failed to get vdd regulator\n"); /* * Supply ramp time + Start-up time. * Datasheet: 3.3.2 A.C. Electrical Characteristics */ fsleep(5 * USEC_PER_MSEC); ret = inv_icm45600_enable_regulator_vddio(st); if (ret) return ret; ret = devm_add_action_or_reset(dev, inv_icm45600_disable_vddio_reg, st); if (ret) return ret; ret = inv_icm45600_setup(st, chip_info, reset, bus_setup); if (ret) return ret; ret = inv_icm45600_timestamp_setup(st); if (ret) return ret; ret = inv_icm45600_buffer_init(st); if (ret) return ret; st->indio_gyro = inv_icm45600_gyro_init(st); if (IS_ERR(st->indio_gyro)) return PTR_ERR(st->indio_gyro); st->indio_accel = inv_icm45600_accel_init(st); if (IS_ERR(st->indio_accel)) return PTR_ERR(st->indio_accel); ret = inv_icm45600_irq_init(st, irq, irq_type, open_drain); if (ret) return ret; ret = devm_pm_runtime_set_active_enabled(dev); if (ret) return ret; pm_runtime_get_noresume(dev); pm_runtime_set_autosuspend_delay(dev, 2 * USEC_PER_MSEC); pm_runtime_use_autosuspend(dev); pm_runtime_put(dev); return 0; } EXPORT_SYMBOL_NS_GPL(inv_icm45600_core_probe, "IIO_ICM45600"); /* * Suspend saves sensors state and turns everything off. */ static int inv_icm45600_suspend(struct device *dev) { struct inv_icm45600_state *st = dev_get_drvdata(dev); int ret; scoped_guard(mutex, &st->lock) { /* Disable FIFO data streaming. */ if (st->fifo.on) { unsigned int val; /* Clear FIFO_CONFIG3_IF_EN before changing the FIFO configuration */ ret = regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, INV_ICM45600_FIFO_CONFIG3_IF_EN); if (ret) return ret; val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK, INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS); ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0, INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val); if (ret) return ret; } /* Save sensors states */ st->suspended.gyro = st->conf.gyro.mode; st->suspended.accel = st->conf.accel.mode; } return pm_runtime_force_suspend(dev); } /* * System resume gets the system back on and restores the sensors state. * Manually put runtime power management in system active state. */ static int inv_icm45600_resume(struct device *dev) { struct inv_icm45600_state *st = dev_get_drvdata(dev); int ret; ret = pm_runtime_force_resume(dev); if (ret) return ret; scoped_guard(mutex, &st->lock) { /* Restore sensors state. */ ret = inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro, st->suspended.accel, NULL); if (ret) return ret; /* Restore FIFO data streaming. */ if (st->fifo.on) { struct inv_icm45600_sensor_state *gyro_st = iio_priv(st->indio_gyro); struct inv_icm45600_sensor_state *accel_st = iio_priv(st->indio_accel); unsigned int val; inv_sensors_timestamp_reset(&gyro_st->ts); inv_sensors_timestamp_reset(&accel_st->ts); val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK, INV_ICM45600_FIFO_CONFIG0_MODE_STREAM); ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0, INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val); if (ret) return ret; /* FIFO_CONFIG3_IF_EN must only be set at end of FIFO the configuration */ ret = regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, INV_ICM45600_FIFO_CONFIG3_IF_EN); if (ret) return ret; } } return ret; } /* Runtime suspend will turn off sensors that are enabled by iio devices. */ static int inv_icm45600_runtime_suspend(struct device *dev) { struct inv_icm45600_state *st = dev_get_drvdata(dev); int ret; guard(mutex)(&st->lock); /* disable all sensors */ ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF, INV_ICM45600_SENSOR_MODE_OFF, NULL); if (ret) return ret; regulator_disable(st->vddio_supply); return 0; } /* Sensors are enabled by iio devices, no need to turn them back on here. */ static int inv_icm45600_runtime_resume(struct device *dev) { struct inv_icm45600_state *st = dev_get_drvdata(dev); guard(mutex)(&st->lock); return inv_icm45600_enable_regulator_vddio(st); } static int _inv_icm45600_temp_read(struct inv_icm45600_state *st, s16 *temp) { struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_KEEP_VALUES; int ret; /* Make sure a sensor is on. */ if (st->conf.gyro.mode == INV_ICM45600_SENSOR_MODE_OFF && st->conf.accel.mode == INV_ICM45600_SENSOR_MODE_OFF) { conf.mode = INV_ICM45600_SENSOR_MODE_LOW_POWER; ret = inv_icm45600_set_accel_conf(st, &conf, NULL); if (ret) return ret; } ret = regmap_bulk_read(st->map, INV_ICM45600_REG_TEMP_DATA, &st->buffer.u16, sizeof(st->buffer.u16)); if (ret) return ret; *temp = (s16)le16_to_cpup(&st->buffer.u16); if (*temp == INV_ICM45600_DATA_INVALID) return -EINVAL; return 0; } static int inv_icm45600_temp_read(struct inv_icm45600_state *st, s16 *temp) { struct device *dev = regmap_get_device(st->map); int ret; ret = pm_runtime_resume_and_get(dev); if (ret) return ret; scoped_guard(mutex, &st->lock) ret = _inv_icm45600_temp_read(st, temp); pm_runtime_put_autosuspend(dev); return ret; } int inv_icm45600_temp_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev); s16 temp; int ret; if (chan->type != IIO_TEMP) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: ret = inv_icm45600_temp_read(st, &temp); if (ret) return ret; *val = temp; return IIO_VAL_INT; /* * T°C = (temp / 128) + 25 * Tm°C = 1000 * ((temp * 100 / 12800) + 25) * scale: 100000 / 13248 = 7.8125 * offset: 25000 */ case IIO_CHAN_INFO_SCALE: *val = 7; *val2 = 812500; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_OFFSET: *val = 25000; return IIO_VAL_INT; default: return -EINVAL; } } EXPORT_NS_GPL_DEV_PM_OPS(inv_icm45600_pm_ops, IIO_ICM45600) = { SYSTEM_SLEEP_PM_OPS(inv_icm45600_suspend, inv_icm45600_resume) RUNTIME_PM_OPS(inv_icm45600_runtime_suspend, inv_icm45600_runtime_resume, NULL) }; MODULE_AUTHOR("InvenSense, Inc."); MODULE_DESCRIPTION("InvenSense ICM-456xx device driver"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("IIO_INV_SENSORS_TIMESTAMP");