// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2021 Rockchip Electronics Co., Ltd */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "icm40605.h" #define ICM40605_CHANNEL(_type, _axis, _index) { \ .type = _type, \ .modified = 1, \ .channel2 = _axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .scan_index = _index, \ .scan_type = { \ .sign = 's', \ .realbits = 16, \ .storagebits = 16, \ .endianness = IIO_BE, \ }, \ } #define ICM40605_TEMP_CHANNEL(_type, _index) { \ .type = _type, \ .channel = -1, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .scan_index = _index, \ .scan_type = { \ .sign = 's', \ .realbits = 8, \ .storagebits = 8, \ .endianness = IIO_BE, \ }, \ } const struct regmap_config icm40605_regmap_config = { .reg_bits = 8, .val_bits = 8, }; EXPORT_SYMBOL(icm40605_regmap_config); struct icm40605_regs { u8 data; u8 config; u8 config_odr_mask; u8 config_fsr_mask; }; static struct icm40605_regs icm40605_regs[] = { [ICM40605_ACCEL] = { .data = MPUREG_ACCEL_DATA_X0_UI, .config = MPUREG_ACCEL_CONFIG0_REG, .config_odr_mask = BIT_ACCEL_ODR_NONFLAME_MASK, .config_fsr_mask = BIT_ACCEL_UI_FS_SEL_MASK, }, [ICM40605_GYRO] = { .data = MPUREG_GYRO_DATA_X0_UI, .config = MPUREG_GYRO_CONFIG0_REG, .config_odr_mask = BIT_GYRO_ODR_NONFLAME_MASK, .config_fsr_mask = BIT_GYRO_UI_FS_SEL_MASK, }, [ICM40605_TEMP] = { .data = MPUREG_TEMP_DATA0_UI, .config = MPUREG_GYRO_CONFIG1_REG, .config_odr_mask = 0, .config_fsr_mask = 0, }, }; struct icm40605_scale { u8 bits; int scale; int uscale; }; struct icm40605_odr { u8 bits; int odr; int divider; }; static const struct icm40605_scale icm40605_accel_scale[] = { { ICM40605_ACCEL_FSR_2G, 0, 598}, { ICM40605_ACCEL_FSR_4G, 4, 1196}, { ICM40605_ACCEL_FSR_8G, 8, 2393}, { ICM40605_ACCEL_FSR_16G, 16, 4785}, }; static const struct icm40605_scale icm40605_gyro_scale[] = { { ICM40605_GYRO_FSR_2000DPS, 0, 1064225}, { ICM40605_GYRO_FSR_1000DPS, 0, 532113}, { ICM40605_GYRO_FSR_500DPS, 0, 266462}, { ICM40605_GYRO_FSR_250DPS, 0, 133231}, { ICM40605_GYRO_FSR_125DPS, 0, 66615}, { ICM40605_GYRO_FSR_62_5DPS, 0, 33289}, { ICM40605_GYRO_FSR_31_25DPS, 0, 16644}, { ICM40605_GYRO_FSR_15_625DPS, 0, 8322}, }; struct icm40605_scale_item { const struct icm40605_scale *tbl; int num; int format; }; static const struct icm40605_scale_item icm40605_scale_table[] = { [ICM40605_ACCEL] = { .tbl = icm40605_accel_scale, .num = ARRAY_SIZE(icm40605_accel_scale), .format = IIO_VAL_INT_PLUS_MICRO, }, [ICM40605_GYRO] = { .tbl = icm40605_gyro_scale, .num = ARRAY_SIZE(icm40605_gyro_scale), .format = IIO_VAL_INT_PLUS_NANO, }, }; static const struct icm40605_odr icm40605_accel_odr[] = { {ICM40605_ACCEL_ODR_8KHZ, 8000, 8}, {ICM40605_ACCEL_ODR_4KHZ, 4000, 4}, {ICM40605_ACCEL_ODR_2KHZ, 2000, 2}, {ICM40605_ACCEL_ODR_1KHZ, 1000, 1}, {ICM40605_ACCEL_ODR_200HZ, 200, -5}, {ICM40605_ACCEL_ODR_100HZ, 100, -10}, {ICM40605_ACCEL_ODR_50HZ, 50, -20}, {ICM40605_ACCEL_ODR_25HZ, 25, -40}, }; static const struct icm40605_odr icm40605_gyro_odr[] = { {ICM40605_GYRO_ODR_8KHZ, 8000, 8}, {ICM40605_GYRO_ODR_4KHZ, 4000, 4}, {ICM40605_GYRO_ODR_2KHZ, 2000, 2}, {ICM40605_GYRO_ODR_1KHZ, 1000, 1}, {ICM40605_GYRO_ODR_200HZ, 200, -5}, {ICM40605_GYRO_ODR_100HZ, 100, -10}, {ICM40605_GYRO_ODR_50HZ, 50, -20}, {ICM40605_GYRO_ODR_25HZ, 25, -40}, }; struct icm40605_odr_item { const struct icm40605_odr *tbl; int num; }; static const struct icm40605_odr_item icm40605_odr_table[] = { [ICM40605_ACCEL] = { .tbl = icm40605_accel_odr, .num = ARRAY_SIZE(icm40605_accel_odr), }, [ICM40605_GYRO] = { .tbl = icm40605_gyro_odr, .num = ARRAY_SIZE(icm40605_gyro_odr), }, }; static const struct iio_chan_spec icm40605_channels[] = { ICM40605_CHANNEL(IIO_ACCEL, IIO_MOD_X, ICM40605_SCAN_ACCEL_X), ICM40605_CHANNEL(IIO_ACCEL, IIO_MOD_Y, ICM40605_SCAN_ACCEL_Y), ICM40605_CHANNEL(IIO_ACCEL, IIO_MOD_Z, ICM40605_SCAN_ACCEL_Z), ICM40605_CHANNEL(IIO_ANGL_VEL, IIO_MOD_X, ICM40605_SCAN_GYRO_X), ICM40605_CHANNEL(IIO_ANGL_VEL, IIO_MOD_Y, ICM40605_SCAN_GYRO_Y), ICM40605_CHANNEL(IIO_ANGL_VEL, IIO_MOD_Z, ICM40605_SCAN_GYRO_Z), ICM40605_TEMP_CHANNEL(IIO_TEMP, ICM40605_SCAN_TEMP), IIO_CHAN_SOFT_TIMESTAMP(ICM40605_SCAN_TIMESTAMP), }; static const unsigned long icm40605_scan_masks[] = { /* 3-axis accel + temp */ BIT(ICM40605_SCAN_ACCEL_X) | BIT(ICM40605_SCAN_ACCEL_Y) | BIT(ICM40605_SCAN_ACCEL_Z) | BIT(ICM40605_SCAN_TEMP), /* 3-axis gyro + temp */ BIT(ICM40605_SCAN_GYRO_X) | BIT(ICM40605_SCAN_GYRO_Y) | BIT(ICM40605_SCAN_GYRO_Z) | BIT(ICM40605_SCAN_TEMP), /* 6-axis accel + gyro + temp */ BIT(ICM40605_SCAN_ACCEL_X) | BIT(ICM40605_SCAN_ACCEL_Y) | BIT(ICM40605_SCAN_ACCEL_Z) | BIT(ICM40605_SCAN_GYRO_X) | BIT(ICM40605_SCAN_GYRO_Y) | BIT(ICM40605_SCAN_GYRO_Z) | BIT(ICM40605_SCAN_TEMP), 0, }; static int icm40605_chip_init(struct icm40605_data *data, bool use_spi); static enum icm40605_sensor_type icm40605_to_sensor(enum iio_chan_type iio_type) { switch (iio_type) { case IIO_ACCEL: return ICM40605_ACCEL; case IIO_ANGL_VEL: return ICM40605_GYRO; case IIO_TEMP: return ICM40605_TEMP; default: return -EINVAL; } } static int icm40605_gyro_on(struct icm40605_data *data) { int ret; unsigned int cmd; // IGNORE:Gyro Workaround 9396 ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, 0x04); if (ret < 0) return ret; ret = regmap_write(data->regmap, 0x10, 0x0d); if (ret < 0) return ret; ret = regmap_write(data->regmap, 0x12, 0x01); if (ret < 0) return ret; // Enable Gyro ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, ICM40605_BANK0); if (ret < 0) return ret; ret = regmap_read(data->regmap, MPUREG_PWR_MGMT_0_REG, &cmd); if (ret < 0) return ret; cmd |= (BIT_GYRO_MODE_LN); // Gyro on in LNM usleep_range(50, 51); ret = regmap_write(data->regmap, MPUREG_PWR_MGMT_0_REG, cmd); if (ret < 0) return ret; usleep_range(200, 201); //workaround 9136 ret = regmap_read(data->regmap, MPUREG_PWR_MGMT_0_REG, &cmd); if (ret < 0) return ret; // IGNORE: Gyro Workaround 9396 msleep(20); ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, 0x04); if (ret < 0) return ret; ret = regmap_write(data->regmap, 0x10, 0x2a); //set 42 to reg 0x10 if (ret < 0) return ret; msleep(100); return ret; } static int icm40605_gyro_off(struct icm40605_data *data) { int ret; unsigned int cmd; ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, ICM40605_BANK0); if (ret < 0) return ret; ret = regmap_read(data->regmap, MPUREG_PWR_MGMT_0_REG, &cmd); if (ret < 0) return ret; cmd &= (~BIT_GYRO_MODE_LN); msleep(80); ret = regmap_write(data->regmap, MPUREG_PWR_MGMT_0_REG, cmd); if (ret < 0) return ret; msleep(200); return ret; } static int icm40605_accel_on(struct icm40605_data *data) { int ret; unsigned int cmd; ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, ICM40605_BANK0); if (ret < 0) return ret; ret = regmap_read(data->regmap, MPUREG_PWR_MGMT_0_REG, &cmd); if (ret < 0) return ret; cmd |= (BIT_ACCEL_MODE_LN); // Accel on in LNM ret = regmap_write(data->regmap, MPUREG_PWR_MGMT_0_REG, cmd); if (ret < 0) return ret; usleep_range(200, 201); return ret; } static int icm40605_accel_off(struct icm40605_data *data) { int ret; unsigned int cmd; ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, ICM40605_BANK0); if (ret < 0) return ret; ret = regmap_read(data->regmap, MPUREG_PWR_MGMT_0_REG, &cmd); if (ret < 0) return ret; cmd &= (~BIT_ACCEL_MODE_LN); // Accel on in LNM usleep_range(50, 51); ret = regmap_write(data->regmap, MPUREG_PWR_MGMT_0_REG, cmd); if (ret < 0) return ret; usleep_range(200, 201); return ret; } static int icm40605_temp_on(struct icm40605_data *data) { int ret; unsigned int cmd; ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, ICM40605_BANK0); if (ret) return ret; ret = regmap_read(data->regmap, MPUREG_PWR_MGMT_0_REG, &cmd); if (ret) return ret; cmd |= (BIT_TEMP_DIS); ret = regmap_write(data->regmap, MPUREG_PWR_MGMT_0_REG, cmd); if (ret < 0) return ret; usleep_range(200, 250); return ret; } static int icm40605_temp_off(struct icm40605_data *data) { int ret; unsigned int cmd; ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, ICM40605_BANK0); if (ret < 0) return ret; ret = regmap_read(data->regmap, MPUREG_PWR_MGMT_0_REG, &cmd); if (ret < 0) return ret; cmd &= (~BIT_TEMP_DIS); ret = regmap_write(data->regmap, MPUREG_PWR_MGMT_0_REG, cmd); if (ret < 0) return ret; usleep_range(200, 250); return ret; } int icm40605_set_mode(struct icm40605_data *data, enum icm40605_sensor_type t, bool mode) { int ret; if (t == ICM40605_GYRO) { if (mode) ret = icm40605_gyro_on(data); else ret = icm40605_gyro_off(data); } else if (t == ICM40605_ACCEL) { if (mode) ret = icm40605_accel_on(data); else ret = icm40605_accel_off(data); } else if (t == ICM40605_TEMP) { if (mode) ret = icm40605_temp_on(data); else ret = icm40605_temp_off(data); } else { return -EINVAL; } return ret; } static int __icm40605_set_odr(struct icm40605_data *data, enum icm40605_sensor_type t, int sel, int divider) { int ret; unsigned int cmd; ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, ICM40605_BANK0); if (ret < 0) return ret; if (t == ICM40605_ACCEL) { ret = regmap_read(data->regmap, MPUREG_ACCEL_CONFIG0_REG, &cmd); if (ret < 0) return ret; if (!((cmd & BIT_GYRO_ODR_NONFLAME_MASK) & (~sel))) return 0; cmd &= (~BIT_GYRO_ODR_NONFLAME_MASK); cmd |= sel; ret = regmap_write(data->regmap, MPUREG_ACCEL_CONFIG0_REG, cmd); if (!ret) { data->chip_config.accel_odr = sel; data->period_divider = (data->period_divider && data->period_divider > divider) ? data->period_divider : divider; } } else if (t == ICM40605_GYRO) { ret = regmap_read(data->regmap, MPUREG_GYRO_CONFIG0_REG, &cmd); if (ret < 0) return ret; if (!((cmd & BIT_ACCEL_ODR_NONFLAME_MASK) & (~sel))) return 0; cmd &= (~BIT_ACCEL_ODR_NONFLAME_MASK); cmd |= sel; ret = regmap_write(data->regmap, MPUREG_GYRO_CONFIG0_REG, cmd); if (!ret) { data->chip_config.gyro_odr = sel; data->period_divider = (data->period_divider && data->period_divider > divider) ? data->period_divider : divider; } } else { return -EINVAL; } return ret; } static int __icm40605_set_fsr(struct icm40605_data *data, enum icm40605_sensor_type t, int sel) { int ret; unsigned int cmd; ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, ICM40605_BANK0); if (ret < 0) return ret; if (t == ICM40605_ACCEL) { ret = regmap_read(data->regmap, MPUREG_ACCEL_CONFIG0_REG, &cmd); if (ret < 0) return ret; cmd &= (~BIT_ACCEL_UI_FS_SEL_MASK); cmd |= sel; ret = regmap_write(data->regmap, MPUREG_ACCEL_CONFIG0_REG, cmd); } else if (t == ICM40605_GYRO) { ret = regmap_read(data->regmap, MPUREG_GYRO_CONFIG0_REG, &cmd); if (ret < 0) return ret; cmd &= (~BIT_GYRO_UI_FS_SEL_MASK); cmd |= sel; ret = regmap_write(data->regmap, MPUREG_GYRO_CONFIG0_REG, cmd); } else { return -EINVAL; } return ret; } static int icm40605_set_scale(struct icm40605_data *data, enum icm40605_sensor_type t, int scale, int uscale) { int i; if (t >= ARRAY_SIZE(icm40605_scale_table)) return 0; for (i = 0; i < icm40605_scale_table[t].num; i++) if (icm40605_scale_table[t].tbl[i].uscale == uscale && icm40605_scale_table[t].tbl[i].scale == scale) break; if (i == icm40605_scale_table[t].num) return -EINVAL; dev_info(regmap_get_device(data->regmap), "set scale t: %d, bit: %x\n", t, icm40605_scale_table[t].tbl[i].bits); return __icm40605_set_fsr(data, t, icm40605_scale_table[t].tbl[i].bits); } static int icm40605_get_scale(struct icm40605_data *data, enum icm40605_sensor_type t, int *scale, int *uscale) { int i, ret, val; if (t < 0 || (t >= ARRAY_SIZE(icm40605_scale_table))) return -EINVAL; ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, ICM40605_BANK0); if (ret < 0) return ret; ret = regmap_read(data->regmap, icm40605_regs[t].config, &val); if (ret < 0) return ret; val &= icm40605_regs[t].config_fsr_mask; for (i = 0; i < icm40605_scale_table[t].num; i++) if (icm40605_scale_table[t].tbl[i].bits == val) { *scale = icm40605_scale_table[t].tbl[i].scale; *uscale = icm40605_scale_table[t].tbl[i].uscale; return icm40605_scale_table[t].format; } return -EINVAL; } static int icm40605_get_data(struct icm40605_data *data, int chan_type, int axis, int *val) { u8 reg; int ret; __be16 sample; int t = icm40605_to_sensor(chan_type); // TODO: check type if (t < 0) { *val = -1; return 0; } if (t == ICM40605_TEMP) reg = icm40605_regs[t].data; else reg = icm40605_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample); do { ret = regmap_bulk_read(data->regmap, reg, (u8 *)&sample, 2); if (ret) { ret = -EINVAL; break; } *val = (short)be16_to_cpup(&sample); dev_info(regmap_get_device(data->regmap), "icm40605get_data: ch: %d, aix: %d, reg: %x, val: %x\n", chan_type, axis, sample, *val); } while (0); return ret; } static int icm40605_set_odr(struct icm40605_data *data, enum icm40605_sensor_type t, int odr) { int i; if (t == ICM40605_ACCEL) data->accel_frequency = odr; if (t == ICM40605_GYRO) data->gyro_frequency = odr; if (t >= ARRAY_SIZE(icm40605_odr_table)) return 0; for (i = 0; i < icm40605_odr_table[t].num; i++) if (icm40605_odr_table[t].tbl[i].odr == odr) break; if (i >= icm40605_odr_table[t].num) return -EINVAL; dev_info(regmap_get_device(data->regmap), "set odr t: %d, bit: %x\n", t, icm40605_odr_table[t].tbl[i].bits); return __icm40605_set_odr(data, t, icm40605_odr_table[t].tbl[i].bits, icm40605_odr_table[t].tbl[i].divider); } static int icm40605_get_odr(struct icm40605_data *data, enum icm40605_sensor_type t, int *odr) { int i, val, ret; if (t < 0 || t >= ARRAY_SIZE(icm40605_odr_table)) return 0; ret = regmap_read(data->regmap, icm40605_regs[t].config, &val); if (ret < 0) return ret; val &= icm40605_regs[t].config_odr_mask; for (i = 0; i < icm40605_odr_table[t].num; i++) if (val == icm40605_odr_table[t].tbl[i].bits) break; if (i >= icm40605_odr_table[t].num) return -EINVAL; *odr = icm40605_odr_table[t].tbl[i].odr; return 0; } static int icm40605_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { int ret; struct icm40605_data *data = iio_priv(indio_dev); switch (mask) { case IIO_CHAN_INFO_RAW: ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; mutex_lock(&data->lock); icm40605_get_data(data, chan->type, chan->channel2, val); mutex_unlock(&data->lock); iio_device_release_direct_mode(indio_dev); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: mutex_lock(&data->lock); ret = icm40605_get_scale(data, icm40605_to_sensor(chan->type), val, val2); mutex_unlock(&data->lock); return ret; case IIO_CHAN_INFO_SAMP_FREQ: mutex_lock(&data->lock); ret = icm40605_get_odr(data, icm40605_to_sensor(chan->type), val); mutex_unlock(&data->lock); return ret < 0 ? ret : IIO_VAL_INT; default: return -EINVAL; } return 0; } static int icm40605_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { int ret = 0; struct icm40605_data *data = iio_priv(indio_dev); mutex_lock(&data->lock); ret = iio_device_claim_direct_mode(indio_dev); if (ret) goto error_write_raw_unlock; switch (mask) { case IIO_CHAN_INFO_SCALE: ret = icm40605_set_scale(data, icm40605_to_sensor(chan->type), val, val2); break; case IIO_CHAN_INFO_SAMP_FREQ: ret = icm40605_set_odr(data, icm40605_to_sensor(chan->type), val); break; default: ret = -EINVAL; } error_write_raw_unlock: iio_device_release_direct_mode(indio_dev); mutex_unlock(&data->lock); return ret; } static int icm40605_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) { switch (mask) { case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ANGL_VEL: return IIO_VAL_INT_PLUS_NANO; default: return IIO_VAL_INT_PLUS_MICRO; } default: return IIO_VAL_INT_PLUS_MICRO; } return -EINVAL; } static IIO_CONST_ATTR(in_accel_sampling_frequency_available, "25 50 100 200 1000 2000 4000 8000"); static IIO_CONST_ATTR(in_anglvel_sampling_frequency_available, "25 50 100 200 1000 2000 4000 8000"); static IIO_CONST_ATTR(in_accel_scale_available, "0.000598 0.001196 0.002393 0.004785"); static IIO_CONST_ATTR(in_anglvel_scale_available, "0.001064225 0.000532113 0.000266462 0.000133231 0.000066615, 0.000033289, 0.000016644, 0.000008322"); static struct attribute *icm40605_attrs[] = { &iio_const_attr_in_accel_sampling_frequency_available.dev_attr.attr, &iio_const_attr_in_anglvel_sampling_frequency_available.dev_attr.attr, &iio_const_attr_in_accel_scale_available.dev_attr.attr, &iio_const_attr_in_anglvel_scale_available.dev_attr.attr, NULL, }; static const struct attribute_group icm40605_attrs_group = { .attrs = icm40605_attrs, }; static const struct iio_info icm40605_info = { .read_raw = icm40605_read_raw, .write_raw = icm40605_write_raw, .write_raw_get_fmt = &icm40605_write_raw_get_fmt, .attrs = &icm40605_attrs_group, }; static const char *icm40605_match_acpi_device(struct device *dev) { const struct acpi_device_id *id; id = acpi_match_device(dev->driver->acpi_match_table, dev); if (!id) return NULL; return dev_name(dev); } static void icm40605_disable_vdd_reg(void *_data) { struct icm40605_data *st = _data; const struct device *dev = regmap_get_device(st->regmap); int ret; ret = regulator_disable(st->vdd_supply); if (ret) dev_err(dev, "failed to disable vdd error %d\n", ret); } static int icm40605_chip_init(struct icm40605_data *data, bool use_spi) { int ret; unsigned int regval; struct device *dev = regmap_get_device(data->regmap); // who am i ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, ICM40605_BANK0); if (ret) return ret; ret = regmap_read(data->regmap, MPUREG_WHO_AM_I, ®val); if (ret) return ret; dev_info(dev, "i am: %x, 40605: %x\n", regval, BIT_I_AM_ICM40605); if (regval != BIT_I_AM_ICM40605) return -EINVAL; ret = gpio_direction_output(data->int1_gpio, 0); if (ret < 0) dev_err(dev, "can't set gpio!\r\n"); // reset ret = regmap_write(data->regmap, MPUREG_CHIP_CONFIG_REG, BIT_SOFT_RESET_CHIP_CONFIG); if (ret < 0) return ret; usleep_range(20000, 21000); ret = gpio_direction_input(data->int1_gpio); if (ret < 0) dev_err(dev, "gpio_direction_input failed!\r\n"); if (use_spi) { regval &= (~BIT_SPI_I2C_SEL_MASK); regval |= BIT_SEL_I2C_DISABLE; regval |= BIT_FIFO_COUNT_REC; ret = regmap_write(data->regmap, MPUREG_INTF_CONFIG0_REG, regval); if (ret < 0) return ret; } else { regval &= (~BIT_SPI_I2C_SEL_MASK); regval |= BIT_SEL_SPI_DISABLE; ret = regmap_write(data->regmap, MPUREG_INTF_CONFIG0_REG, regval); if (ret < 0) return ret; } ret = regmap_write(data->regmap, MPUREG_DRIVE_CONFIG_REG, BIT_SPI_SPEED_5M); if (ret) return ret; // TODO: test spi link ret = regmap_write(data->regmap, MPUREG_REG_BANK_SEL, ICM40605_BANK0); if (ret < 0) return ret; ret = regmap_read(data->regmap, MPUREG_WHO_AM_I, ®val); if (ret) return ret; dev_info(dev, "after reset i am: %x, 40605: %x\n", regval, BIT_I_AM_ICM40605); if (regval != BIT_I_AM_ICM40605) return -EINVAL; ret = icm40605_set_mode(data, ICM40605_ACCEL, true); if (ret < 0) return ret; ret = icm40605_set_mode(data, ICM40605_GYRO, true); if (ret < 0) return ret; // init gyro and accel para icm40605_set_odr(data, ICM40605_ACCEL, 1000); icm40605_set_odr(data, ICM40605_GYRO, 1000); __icm40605_set_fsr(data, ICM40605_ACCEL, ICM40605_ACCEL_FSR_2G); __icm40605_set_fsr(data, ICM40605_GYRO, ICM40605_GYRO_FSR_2000DPS); ret = regmap_write(data->regmap, MPUREG_INT_SOURCE0_REG, 0x0); if (ret < 0) return ret; // set int mode ret = regmap_write(data->regmap, MPUREG_INT_CONFIG_REG, data->irq_mask); if (ret < 0) return ret; dev_info(dev, "icm40605chip_init out\n"); return ret; } static void icm40605_chip_uninit(struct icm40605_data *data) { icm40605_set_mode(data, ICM40605_GYRO, false); icm40605_set_mode(data, ICM40605_ACCEL, false); } int icm40605_core_probe(struct regmap *regmap, int irq, const char *name, int chip_type, bool use_spi) { struct iio_dev *indio_dev; struct icm40605_data *data; struct device *dev = regmap_get_device(regmap); int ret; struct irq_data *desc; int irq_type; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); memset(data, 0, sizeof(struct icm40605_data)); mutex_init(&data->lock); data->chip_type = chip_type; data->powerup_count = 0; data->irq = irq; data->regmap = regmap; /* get the node */ data->node = of_find_node_by_name(NULL, "icm40605"); if (data->node == NULL) dev_err(dev, "spi node not find!\n"); data->int1_gpio = of_get_named_gpio(data->node, "int1-gpio", 0); if (data->int1_gpio < 0) { dev_err(dev, "Could not get int1_gpio!\n"); return -EINVAL; } desc = irq_get_irq_data(irq); if (!desc) { dev_err(dev, "Could not find IRQ %d\n", irq); return -EINVAL; } irq_type = irqd_get_trigger_type(desc); if (!irq_type) irq_type = IRQF_TRIGGER_RISING; if (irq_type == IRQF_TRIGGER_RISING) data->irq_mask = BIT_ONLY_INT1_ACTIVE_HIGH; else if (irq_type == IRQF_TRIGGER_FALLING) data->irq_mask = BIT_ONLY_INT1_ACTIVE_LOW; else { dev_err(dev, "Invalid interrupt type 0x%x specified\n", irq_type); return -EINVAL; } data->vdd_supply = devm_regulator_get(dev, "vcc_avdd"); if (IS_ERR(data->vdd_supply)) return PTR_ERR(data->vdd_supply); ret = regulator_enable(data->vdd_supply); if (ret) return ret; ret = devm_add_action_or_reset(dev, icm40605_disable_vdd_reg, data); if (ret) return ret; ret = icm40605_chip_init(data, use_spi); if (ret < 0) { dev_err(dev, "icm40605_chip_init fail\n"); return ret; } dev_set_drvdata(dev, indio_dev); if (!name && ACPI_HANDLE(dev)) name = icm40605_match_acpi_device(dev); indio_dev->dev.parent = dev; indio_dev->channels = icm40605_channels; indio_dev->num_channels = ARRAY_SIZE(icm40605_channels); indio_dev->available_scan_masks = icm40605_scan_masks; indio_dev->name = name; indio_dev->info = &icm40605_info; indio_dev->modes = INDIO_BUFFER_TRIGGERED; ret = devm_iio_triggered_buffer_setup(dev, indio_dev, iio_pollfunc_store_time, icm40605_read_fifo, NULL); if (ret < 0) goto uninit; ret = icm40605_probe_trigger(indio_dev, irq_type); if (ret) { dev_err(dev, "trigger probe fail %d\n", ret); return ret; } ret = devm_iio_device_register(dev, indio_dev); if (ret < 0) { dev_err(dev, "devm_iio_device_register fail\n"); goto uninit; } ret = pm_runtime_set_active(dev); if (ret) return ret; return 0; uninit: icm40605_chip_uninit(data); return ret; } EXPORT_SYMBOL_GPL(icm40605_core_probe); /* * System resume gets the system back on and restores the sensors state. * Manually put runtime power management in system active state. */ static int __maybe_unused sleep_icm40605_resume(struct device *dev) { struct icm40605_data *st = iio_priv(dev_get_drvdata(dev)); struct iio_dev *indio_dev = dev_get_drvdata(dev); int ret; mutex_lock(&st->lock); ret = regulator_enable(st->vdd_supply); if (ret) goto out_unlock; usleep_range(3000, 4000); pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); icm40605_set_odr(st, ICM40605_GYRO, st->gyro_frequency_buff); icm40605_set_odr(st, ICM40605_ACCEL, st->accel_frequency_buff); ret = icm40605_set_enable(indio_dev, 1); if (ret) goto out_unlock; devm_regulator_put(st->vdd_supply); out_unlock: mutex_unlock(&st->lock); return ret; } /* * Suspend saves sensors state and turns everything off. * Check first if runtime suspend has not already done the job. */ static int __maybe_unused sleep_icm40605_suspend(struct device *dev) { struct icm40605_data *st = iio_priv(dev_get_drvdata(dev)); struct iio_dev *indio_dev = dev_get_drvdata(dev); int ret; mutex_lock(&st->lock); st->gyro_frequency_buff = st->gyro_frequency; st->accel_frequency_buff = st->accel_frequency; if (pm_runtime_suspended(dev)) { ret = 0; goto out_unlock; } ret = icm40605_set_enable(indio_dev, 0); if (ret) goto out_unlock; regulator_disable(st->vdd_supply); devm_regulator_put(st->vdd_supply); out_unlock: mutex_unlock(&st->lock); return ret; } void icm40605_core_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct icm40605_data *data = iio_priv(indio_dev); icm40605_chip_uninit(data); } EXPORT_SYMBOL_GPL(icm40605_core_remove); const struct dev_pm_ops inv_icm42600_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(sleep_icm40605_suspend, sleep_icm40605_resume) }; EXPORT_SYMBOL_GPL(inv_icm42600_pm_ops); MODULE_AUTHOR("ALLEN CHEN"); MODULE_DESCRIPTION("Invensense ICM40605 SPI driver"); MODULE_LICENSE("GPL v2");