#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include "inv_icm42600.h"
#include "inv_icm42600_buffer.h"
static const struct regmap_range_cfg inv_icm42600_regmap_ranges[] = {
{
.name = "user banks",
.range_min = 0x0000,
.range_max = 0x4FFF,
.selector_reg = INV_ICM42600_REG_BANK_SEL,
.selector_mask = INV_ICM42600_BANK_SEL_MASK,
.selector_shift = 0,
.window_start = 0,
.window_len = 0x1000,
},
};
static const struct regmap_range inv_icm42600_regmap_volatile_yes_ranges[] = {
regmap_reg_range(0x001D, 0x002A),
regmap_reg_range(0x002D, 0x0038),
regmap_reg_range(0x004B, 0x004B),
regmap_reg_range(0x006C, 0x006D),
regmap_reg_range(0x1062, 0x1064),
};
static const struct regmap_range inv_icm42600_regmap_volatile_no_ranges[] = {
regmap_reg_range(0x0000, 0x001C),
regmap_reg_range(0x006E, 0x1061),
regmap_reg_range(0x1065, 0x4FFF),
};
static const struct regmap_access_table inv_icm42600_regmap_volatile_accesses[] = {
{
.yes_ranges = inv_icm42600_regmap_volatile_yes_ranges,
.n_yes_ranges = ARRAY_SIZE(inv_icm42600_regmap_volatile_yes_ranges),
.no_ranges = inv_icm42600_regmap_volatile_no_ranges,
.n_no_ranges = ARRAY_SIZE(inv_icm42600_regmap_volatile_no_ranges),
},
};
static const struct regmap_range inv_icm42600_regmap_rd_noinc_no_ranges[] = {
regmap_reg_range(0x0000, INV_ICM42600_REG_FIFO_DATA - 1),
regmap_reg_range(INV_ICM42600_REG_FIFO_DATA + 1, 0x4FFF),
};
static const struct regmap_access_table inv_icm42600_regmap_rd_noinc_accesses[] = {
{
.no_ranges = inv_icm42600_regmap_rd_noinc_no_ranges,
.n_no_ranges = ARRAY_SIZE(inv_icm42600_regmap_rd_noinc_no_ranges),
},
};
const struct regmap_config inv_icm42600_regmap_config = {
.name = "inv_icm42600",
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x4FFF,
.ranges = inv_icm42600_regmap_ranges,
.num_ranges = ARRAY_SIZE(inv_icm42600_regmap_ranges),
.volatile_table = inv_icm42600_regmap_volatile_accesses,
.rd_noinc_table = inv_icm42600_regmap_rd_noinc_accesses,
.cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_NS_GPL(inv_icm42600_regmap_config, "IIO_ICM42600");
const struct regmap_config inv_icm42600_spi_regmap_config = {
.name = "inv_icm42600",
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x4FFF,
.ranges = inv_icm42600_regmap_ranges,
.num_ranges = ARRAY_SIZE(inv_icm42600_regmap_ranges),
.volatile_table = inv_icm42600_regmap_volatile_accesses,
.rd_noinc_table = inv_icm42600_regmap_rd_noinc_accesses,
.cache_type = REGCACHE_MAPLE,
.use_single_write = true,
};
EXPORT_SYMBOL_NS_GPL(inv_icm42600_spi_regmap_config, "IIO_ICM42600");
struct inv_icm42600_hw {
u8 whoami;
const char *name;
const struct inv_icm42600_conf *conf;
};
static const struct inv_icm42600_conf inv_icm42600_default_conf = {
.gyro = {
.mode = INV_ICM42600_SENSOR_MODE_OFF,
.fs = INV_ICM42600_GYRO_FS_2000DPS,
.odr = INV_ICM42600_ODR_50HZ,
.filter = INV_ICM42600_FILTER_BW_ODR_DIV_2,
},
.accel = {
.mode = INV_ICM42600_SENSOR_MODE_OFF,
.fs = INV_ICM42600_ACCEL_FS_16G,
.odr = INV_ICM42600_ODR_50HZ,
.filter = INV_ICM42600_FILTER_BW_ODR_DIV_2,
},
.temp_en = false,
};
static const struct inv_icm42600_conf inv_icm42686_default_conf = {
.gyro = {
.mode = INV_ICM42600_SENSOR_MODE_OFF,
.fs = INV_ICM42686_GYRO_FS_4000DPS,
.odr = INV_ICM42600_ODR_50HZ,
.filter = INV_ICM42600_FILTER_BW_ODR_DIV_2,
},
.accel = {
.mode = INV_ICM42600_SENSOR_MODE_OFF,
.fs = INV_ICM42686_ACCEL_FS_32G,
.odr = INV_ICM42600_ODR_50HZ,
.filter = INV_ICM42600_FILTER_BW_ODR_DIV_2,
},
.temp_en = false,
};
static const struct inv_icm42600_hw inv_icm42600_hw[INV_CHIP_NB] = {
[INV_CHIP_ICM42600] = {
.whoami = INV_ICM42600_WHOAMI_ICM42600,
.name = "icm42600",
.conf = &inv_icm42600_default_conf,
},
[INV_CHIP_ICM42602] = {
.whoami = INV_ICM42600_WHOAMI_ICM42602,
.name = "icm42602",
.conf = &inv_icm42600_default_conf,
},
[INV_CHIP_ICM42605] = {
.whoami = INV_ICM42600_WHOAMI_ICM42605,
.name = "icm42605",
.conf = &inv_icm42600_default_conf,
},
[INV_CHIP_ICM42686] = {
.whoami = INV_ICM42600_WHOAMI_ICM42686,
.name = "icm42686",
.conf = &inv_icm42686_default_conf,
},
[INV_CHIP_ICM42622] = {
.whoami = INV_ICM42600_WHOAMI_ICM42622,
.name = "icm42622",
.conf = &inv_icm42600_default_conf,
},
[INV_CHIP_ICM42688] = {
.whoami = INV_ICM42600_WHOAMI_ICM42688,
.name = "icm42688",
.conf = &inv_icm42600_default_conf,
},
[INV_CHIP_ICM42631] = {
.whoami = INV_ICM42600_WHOAMI_ICM42631,
.name = "icm42631",
.conf = &inv_icm42600_default_conf,
},
};
const struct iio_mount_matrix *
inv_icm42600_get_mount_matrix(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
const struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
return &st->orientation;
}
u32 inv_icm42600_odr_to_period(enum inv_icm42600_odr odr)
{
static u32 odr_periods[INV_ICM42600_ODR_NB] = {
0, 0, 0,
125000,
250000,
500000,
1000000,
5000000,
10000000,
20000000,
40000000,
80000000,
160000000,
320000000,
640000000,
2000000,
};
return odr_periods[odr];
}
static int inv_icm42600_set_pwr_mgmt0(struct inv_icm42600_state *st,
enum inv_icm42600_sensor_mode gyro,
enum inv_icm42600_sensor_mode accel,
bool temp, unsigned int *sleep_ms)
{
enum inv_icm42600_sensor_mode oldgyro = st->conf.gyro.mode;
enum inv_icm42600_sensor_mode oldaccel = st->conf.accel.mode;
bool oldtemp = st->conf.temp_en;
unsigned int sleepval;
unsigned int val;
int ret;
if (gyro == oldgyro && accel == oldaccel && temp == oldtemp)
return 0;
val = INV_ICM42600_PWR_MGMT0_GYRO(gyro) |
INV_ICM42600_PWR_MGMT0_ACCEL(accel);
if (!temp)
val |= INV_ICM42600_PWR_MGMT0_TEMP_DIS;
ret = regmap_write(st->map, INV_ICM42600_REG_PWR_MGMT0, val);
if (ret)
return ret;
st->conf.gyro.mode = gyro;
st->conf.accel.mode = accel;
st->conf.temp_en = temp;
sleepval = 0;
if (temp && !oldtemp) {
if (sleepval < INV_ICM42600_TEMP_STARTUP_TIME_MS)
sleepval = INV_ICM42600_TEMP_STARTUP_TIME_MS;
}
if (accel != oldaccel && oldaccel == INV_ICM42600_SENSOR_MODE_OFF) {
usleep_range(200, 300);
if (sleepval < INV_ICM42600_ACCEL_STARTUP_TIME_MS)
sleepval = INV_ICM42600_ACCEL_STARTUP_TIME_MS;
}
if (gyro != oldgyro) {
if (oldgyro == INV_ICM42600_SENSOR_MODE_OFF) {
usleep_range(200, 300);
if (sleepval < INV_ICM42600_GYRO_STARTUP_TIME_MS)
sleepval = INV_ICM42600_GYRO_STARTUP_TIME_MS;
} else if (gyro == INV_ICM42600_SENSOR_MODE_OFF) {
if (sleepval < INV_ICM42600_GYRO_STOP_TIME_MS)
sleepval = INV_ICM42600_GYRO_STOP_TIME_MS;
}
}
if (sleep_ms)
*sleep_ms = sleepval;
else if (sleepval)
msleep(sleepval);
return 0;
}
int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st,
struct inv_icm42600_sensor_conf *conf,
unsigned int *sleep_ms)
{
struct inv_icm42600_sensor_conf *oldconf = &st->conf.accel;
unsigned int val;
int ret;
if (conf->mode < 0)
conf->mode = oldconf->mode;
if (conf->fs < 0)
conf->fs = oldconf->fs;
if (conf->odr < 0)
conf->odr = oldconf->odr;
if (conf->filter < 0)
conf->filter = oldconf->filter;
switch (conf->mode) {
case INV_ICM42600_SENSOR_MODE_LOW_POWER:
case INV_ICM42600_SENSOR_MODE_LOW_NOISE:
if (conf->odr <= INV_ICM42600_ODR_1KHZ_LN) {
conf->mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
conf->filter = INV_ICM42600_FILTER_BW_ODR_DIV_2;
} else if (conf->odr >= INV_ICM42600_ODR_6_25HZ_LP &&
conf->odr <= INV_ICM42600_ODR_1_5625HZ_LP) {
conf->mode = INV_ICM42600_SENSOR_MODE_LOW_POWER;
conf->filter = INV_ICM42600_FILTER_AVG_16X;
}
break;
default:
break;
}
if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
val = INV_ICM42600_ACCEL_CONFIG0_FS(conf->fs) |
INV_ICM42600_ACCEL_CONFIG0_ODR(conf->odr);
ret = regmap_write(st->map, INV_ICM42600_REG_ACCEL_CONFIG0, val);
if (ret)
return ret;
oldconf->fs = conf->fs;
oldconf->odr = conf->odr;
}
if (conf->filter != oldconf->filter) {
val = INV_ICM42600_GYRO_ACCEL_CONFIG0_ACCEL_FILT(conf->filter) |
INV_ICM42600_GYRO_ACCEL_CONFIG0_GYRO_FILT(st->conf.gyro.filter);
ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_ACCEL_CONFIG0, val);
if (ret)
return ret;
oldconf->filter = conf->filter;
}
return inv_icm42600_set_pwr_mgmt0(st, st->conf.gyro.mode, conf->mode,
st->conf.temp_en, sleep_ms);
}
int inv_icm42600_set_gyro_conf(struct inv_icm42600_state *st,
struct inv_icm42600_sensor_conf *conf,
unsigned int *sleep_ms)
{
struct inv_icm42600_sensor_conf *oldconf = &st->conf.gyro;
unsigned int val;
int ret;
if (conf->mode < 0)
conf->mode = oldconf->mode;
if (conf->fs < 0)
conf->fs = oldconf->fs;
if (conf->odr < 0)
conf->odr = oldconf->odr;
if (conf->filter < 0)
conf->filter = oldconf->filter;
if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
val = INV_ICM42600_GYRO_CONFIG0_FS(conf->fs) |
INV_ICM42600_GYRO_CONFIG0_ODR(conf->odr);
ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_CONFIG0, val);
if (ret)
return ret;
oldconf->fs = conf->fs;
oldconf->odr = conf->odr;
}
if (conf->filter != oldconf->filter) {
val = INV_ICM42600_GYRO_ACCEL_CONFIG0_ACCEL_FILT(st->conf.accel.filter) |
INV_ICM42600_GYRO_ACCEL_CONFIG0_GYRO_FILT(conf->filter);
ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_ACCEL_CONFIG0, val);
if (ret)
return ret;
oldconf->filter = conf->filter;
}
return inv_icm42600_set_pwr_mgmt0(st, conf->mode, st->conf.accel.mode,
st->conf.temp_en, sleep_ms);
return 0;
}
int inv_icm42600_set_temp_conf(struct inv_icm42600_state *st, bool enable,
unsigned int *sleep_ms)
{
return inv_icm42600_set_pwr_mgmt0(st, st->conf.gyro.mode,
st->conf.accel.mode, enable,
sleep_ms);
}
int inv_icm42600_enable_wom(struct inv_icm42600_state *st)
{
int ret;
ret = regmap_write(st->map, INV_ICM42600_REG_SMD_CONFIG,
INV_ICM42600_SMD_CONFIG_SMD_MODE_WOM |
INV_ICM42600_SMD_CONFIG_WOM_MODE);
if (ret)
return ret;
return regmap_set_bits(st->map, INV_ICM42600_REG_INT_SOURCE1,
INV_ICM42600_INT_SOURCE1_WOM_INT1_EN);
}
int inv_icm42600_disable_wom(struct inv_icm42600_state *st)
{
int ret;
ret = regmap_clear_bits(st->map, INV_ICM42600_REG_INT_SOURCE1,
INV_ICM42600_INT_SOURCE1_WOM_INT1_EN);
if (ret)
return ret;
return regmap_write(st->map, INV_ICM42600_REG_SMD_CONFIG,
INV_ICM42600_SMD_CONFIG_SMD_MODE_OFF);
}
int inv_icm42600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
guard(mutex)(&st->lock);
if (readval)
return regmap_read(st->map, reg, readval);
return regmap_write(st->map, reg, writeval);
}
static int inv_icm42600_set_conf(struct inv_icm42600_state *st,
const struct inv_icm42600_conf *conf)
{
unsigned int val;
int ret;
val = INV_ICM42600_PWR_MGMT0_GYRO(conf->gyro.mode) |
INV_ICM42600_PWR_MGMT0_ACCEL(conf->accel.mode);
if (!conf->temp_en)
val |= INV_ICM42600_PWR_MGMT0_TEMP_DIS;
ret = regmap_write(st->map, INV_ICM42600_REG_PWR_MGMT0, val);
if (ret)
return ret;
val = INV_ICM42600_GYRO_CONFIG0_FS(conf->gyro.fs) |
INV_ICM42600_GYRO_CONFIG0_ODR(conf->gyro.odr);
ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_CONFIG0, val);
if (ret)
return ret;
val = INV_ICM42600_ACCEL_CONFIG0_FS(conf->accel.fs) |
INV_ICM42600_ACCEL_CONFIG0_ODR(conf->accel.odr);
ret = regmap_write(st->map, INV_ICM42600_REG_ACCEL_CONFIG0, val);
if (ret)
return ret;
val = INV_ICM42600_GYRO_ACCEL_CONFIG0_ACCEL_FILT(conf->accel.filter) |
INV_ICM42600_GYRO_ACCEL_CONFIG0_GYRO_FILT(conf->gyro.filter);
ret = regmap_write(st->map, INV_ICM42600_REG_GYRO_ACCEL_CONFIG0, val);
if (ret)
return ret;
st->conf = *conf;
return 0;
}
static int inv_icm42600_setup(struct inv_icm42600_state *st,
inv_icm42600_bus_setup bus_setup)
{
const struct inv_icm42600_hw *hw = &inv_icm42600_hw[st->chip];
const struct device *dev = regmap_get_device(st->map);
unsigned int val;
int ret;
ret = regmap_read(st->map, INV_ICM42600_REG_WHOAMI, &val);
if (ret)
return ret;
if (val != hw->whoami) {
dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n",
val, hw->whoami, hw->name);
return -ENODEV;
}
st->name = hw->name;
ret = regmap_write(st->map, INV_ICM42600_REG_DEVICE_CONFIG,
INV_ICM42600_DEVICE_CONFIG_SOFT_RESET);
if (ret)
return ret;
msleep(INV_ICM42600_RESET_TIME_MS);
ret = regmap_read(st->map, INV_ICM42600_REG_INT_STATUS, &val);
if (ret)
return ret;
if (!(val & INV_ICM42600_INT_STATUS_RESET_DONE)) {
dev_err(dev, "reset error, reset done bit not set\n");
return -ENODEV;
}
ret = bus_setup(st);
if (ret)
return ret;
ret = regmap_set_bits(st->map, INV_ICM42600_REG_INTF_CONFIG0,
INV_ICM42600_INTF_CONFIG0_SENSOR_DATA_ENDIAN);
if (ret)
return ret;
ret = regmap_update_bits(st->map, INV_ICM42600_REG_INTF_CONFIG1,
INV_ICM42600_INTF_CONFIG1_ACCEL_LP_CLK_RC,
INV_ICM42600_INTF_CONFIG1_ACCEL_LP_CLK_RC);
if (ret)
return ret;
return inv_icm42600_set_conf(st, hw->conf);
}
static irqreturn_t inv_icm42600_irq_timestamp(int irq, void *_data)
{
struct inv_icm42600_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_icm42600_irq_handler(int irq, void *_data)
{
struct inv_icm42600_state *st = _data;
struct device *dev = regmap_get_device(st->map);
unsigned int status;
int ret;
mutex_lock(&st->lock);
if (st->apex.on) {
unsigned int status2, status3;
ret = regmap_bulk_read(st->map, INV_ICM42600_REG_INT_STATUS2, st->buffer, 2);
if (ret)
goto out_unlock;
status2 = st->buffer[0];
status3 = st->buffer[1];
inv_icm42600_accel_handle_events(st->indio_accel, status2, status3,
st->timestamp.accel);
}
ret = regmap_read(st->map, INV_ICM42600_REG_INT_STATUS, &status);
if (ret)
goto out_unlock;
if (status & INV_ICM42600_INT_STATUS_FIFO_FULL)
dev_warn(dev, "FIFO full data lost!\n");
if (status & INV_ICM42600_INT_STATUS_FIFO_THS) {
ret = inv_icm42600_buffer_fifo_read(st, 0);
if (ret) {
dev_err(dev, "FIFO read error %d\n", ret);
goto out_unlock;
}
ret = inv_icm42600_buffer_fifo_parse(st);
if (ret)
dev_err(dev, "FIFO parsing error %d\n", ret);
}
out_unlock:
mutex_unlock(&st->lock);
return IRQ_HANDLED;
}
static int inv_icm42600_irq_init(struct inv_icm42600_state *st, int irq,
int irq_type, bool open_drain)
{
struct device *dev = regmap_get_device(st->map);
unsigned int val;
int ret;
switch (irq_type) {
case IRQF_TRIGGER_RISING:
case IRQF_TRIGGER_HIGH:
val = INV_ICM42600_INT_CONFIG_INT1_ACTIVE_HIGH;
break;
default:
val = INV_ICM42600_INT_CONFIG_INT1_ACTIVE_LOW;
break;
}
switch (irq_type) {
case IRQF_TRIGGER_LOW:
case IRQF_TRIGGER_HIGH:
val |= INV_ICM42600_INT_CONFIG_INT1_LATCHED;
break;
default:
break;
}
if (!open_drain)
val |= INV_ICM42600_INT_CONFIG_INT1_PUSH_PULL;
ret = regmap_write(st->map, INV_ICM42600_REG_INT_CONFIG, val);
if (ret)
return ret;
ret = regmap_clear_bits(st->map, INV_ICM42600_REG_INT_CONFIG1,
INV_ICM42600_INT_CONFIG1_ASYNC_RESET);
if (ret)
return ret;
irq_type |= IRQF_ONESHOT;
return devm_request_threaded_irq(dev, irq, inv_icm42600_irq_timestamp,
inv_icm42600_irq_handler, irq_type,
"inv_icm42600", st);
}
static int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st)
{
unsigned int val;
val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN |
INV_ICM42600_TMST_CONFIG_TMST_EN;
return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG,
INV_ICM42600_TMST_CONFIG_MASK, val);
}
static int inv_icm42600_enable_regulator_vddio(struct inv_icm42600_state *st)
{
int ret;
ret = regulator_enable(st->vddio_supply);
if (ret)
return ret;
usleep_range(3000, 4000);
return 0;
}
static void inv_icm42600_disable_vddio_reg(void *_data)
{
struct inv_icm42600_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_icm42600_core_probe(struct regmap *regmap, int chip,
inv_icm42600_bus_setup bus_setup)
{
struct device *dev = regmap_get_device(regmap);
struct fwnode_handle *fwnode = dev_fwnode(dev);
struct inv_icm42600_state *st;
int irq, irq_type;
bool open_drain;
int ret;
if (chip <= INV_CHIP_INVALID || chip >= INV_CHIP_NB) {
dev_err(dev, "invalid chip = %d\n", chip);
return -ENODEV;
}
irq = fwnode_irq_get_byname(fwnode, "INT1");
if (irq < 0 && irq != -EPROBE_DEFER) {
dev_info(dev, "no INT1 interrupt defined, fallback to first interrupt\n");
irq = fwnode_irq_get(fwnode, 0);
}
if (irq < 0)
return dev_err_probe(dev, irq, "error missing INT1 interrupt\n");
irq_type = irq_get_trigger_type(irq);
if (!irq_type)
irq_type = IRQF_TRIGGER_FALLING;
open_drain = device_property_read_bool(dev, "drive-open-drain");
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
dev_set_drvdata(dev, st);
mutex_init(&st->lock);
st->chip = chip;
st->map = regmap;
st->irq = irq;
ret = iio_read_mount_matrix(dev, &st->orientation);
if (ret) {
dev_err(dev, "failed to retrieve mounting matrix %d\n", ret);
return ret;
}
ret = devm_regulator_get_enable(dev, "vdd");
if (ret)
return dev_err_probe(dev, ret,
"Failed to get vdd regulator\n");
msleep(INV_ICM42600_POWER_UP_TIME_MS);
st->vddio_supply = devm_regulator_get(dev, "vddio");
if (IS_ERR(st->vddio_supply))
return PTR_ERR(st->vddio_supply);
ret = inv_icm42600_enable_regulator_vddio(st);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, inv_icm42600_disable_vddio_reg, st);
if (ret)
return ret;
ret = inv_icm42600_setup(st, bus_setup);
if (ret)
return ret;
ret = inv_icm42600_timestamp_setup(st);
if (ret)
return ret;
ret = inv_icm42600_buffer_init(st);
if (ret)
return ret;
st->indio_gyro = inv_icm42600_gyro_init(st);
if (IS_ERR(st->indio_gyro))
return PTR_ERR(st->indio_gyro);
st->indio_accel = inv_icm42600_accel_init(st);
if (IS_ERR(st->indio_accel))
return PTR_ERR(st->indio_accel);
ret = inv_icm42600_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_set_autosuspend_delay(dev, INV_ICM42600_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
return ret;
}
EXPORT_SYMBOL_NS_GPL(inv_icm42600_core_probe, "IIO_ICM42600");
static int inv_icm42600_suspend(struct device *dev)
{
struct inv_icm42600_state *st = dev_get_drvdata(dev);
struct device *accel_dev;
bool wakeup;
int accel_conf;
int ret;
guard(mutex)(&st->lock);
st->suspended.gyro = st->conf.gyro.mode;
st->suspended.accel = st->conf.accel.mode;
st->suspended.temp = st->conf.temp_en;
if (pm_runtime_suspended(dev))
return 0;
if (st->fifo.on) {
ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG,
INV_ICM42600_FIFO_CONFIG_BYPASS);
if (ret)
return ret;
}
accel_dev = &st->indio_accel->dev;
wakeup = st->apex.on && device_may_wakeup(accel_dev);
if (wakeup) {
accel_conf = st->conf.accel.mode;
enable_irq_wake(st->irq);
disable_irq(st->irq);
} else {
if (st->apex.wom.enable) {
ret = inv_icm42600_disable_wom(st);
if (ret)
return ret;
}
accel_conf = INV_ICM42600_SENSOR_MODE_OFF;
}
ret = inv_icm42600_set_pwr_mgmt0(st, INV_ICM42600_SENSOR_MODE_OFF,
accel_conf, false, NULL);
if (ret)
return ret;
if (!wakeup)
regulator_disable(st->vddio_supply);
return 0;
}
static int inv_icm42600_resume(struct device *dev)
{
struct inv_icm42600_state *st = dev_get_drvdata(dev);
struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro);
struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel);
struct device *accel_dev;
bool wakeup;
int ret;
guard(mutex)(&st->lock);
if (pm_runtime_suspended(dev))
return 0;
accel_dev = &st->indio_accel->dev;
wakeup = st->apex.on && device_may_wakeup(accel_dev);
if (wakeup) {
enable_irq(st->irq);
disable_irq_wake(st->irq);
} else {
ret = inv_icm42600_enable_regulator_vddio(st);
if (ret)
return ret;
}
ret = inv_icm42600_set_pwr_mgmt0(st, st->suspended.gyro,
st->suspended.accel,
st->suspended.temp, NULL);
if (ret)
return ret;
if (!wakeup && st->apex.wom.enable) {
ret = inv_icm42600_enable_wom(st);
if (ret)
return ret;
}
if (st->fifo.on) {
inv_sensors_timestamp_reset(&gyro_st->ts);
inv_sensors_timestamp_reset(&accel_st->ts);
ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG,
INV_ICM42600_FIFO_CONFIG_STREAM);
}
return 0;
}
static int inv_icm42600_runtime_suspend(struct device *dev)
{
struct inv_icm42600_state *st = dev_get_drvdata(dev);
int ret;
guard(mutex)(&st->lock);
ret = inv_icm42600_set_pwr_mgmt0(st, INV_ICM42600_SENSOR_MODE_OFF,
INV_ICM42600_SENSOR_MODE_OFF, false,
NULL);
if (ret)
return ret;
regulator_disable(st->vddio_supply);
return 0;
}
static int inv_icm42600_runtime_resume(struct device *dev)
{
struct inv_icm42600_state *st = dev_get_drvdata(dev);
guard(mutex)(&st->lock);
return inv_icm42600_enable_regulator_vddio(st);
}
EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42600_pm_ops, IIO_ICM42600) = {
SYSTEM_SLEEP_PM_OPS(inv_icm42600_suspend, inv_icm42600_resume)
RUNTIME_PM_OPS(inv_icm42600_runtime_suspend,
inv_icm42600_runtime_resume, NULL)
};
MODULE_AUTHOR("InvenSense, Inc.");
MODULE_DESCRIPTION("InvenSense ICM-426xx device driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_INV_SENSORS_TIMESTAMP");