diff options
Diffstat (limited to 'drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c')
| -rw-r--r-- | drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c | 313 |
1 files changed, 228 insertions, 85 deletions
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index 8e47dccdd40f..d6a1eeb151ca 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -30,7 +30,6 @@ #include "st_lsm6dsx.h" -#define ST_LSM6DSX_MAX_SLV_NUM 3 #define ST_LSM6DSX_SLV_ADDR(n, base) ((base) + (n) * 3) #define ST_LSM6DSX_SLV_SUB_ADDR(n, base) ((base) + 1 + (n) * 3) #define ST_LSM6DSX_SLV_CONFIG(n, base) ((base) + 2 + (n) * 3) @@ -51,16 +50,18 @@ static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = { .addr = 0x60, .mask = GENMASK(3, 2), }, - .odr_avl[0] = { 10, 0x0 }, - .odr_avl[1] = { 20, 0x1 }, - .odr_avl[2] = { 50, 0x2 }, - .odr_avl[3] = { 100, 0x3 }, + .odr_avl[0] = { 10000, 0x0 }, + .odr_avl[1] = { 20000, 0x1 }, + .odr_avl[2] = { 50000, 0x2 }, + .odr_avl[3] = { 100000, 0x3 }, + .odr_len = 4, }, .fs_table = { .fs_avl[0] = { .gain = 1500, .val = 0x0, }, /* 1500 uG/LSB */ + .fs_len = 1, }, .temp_comp = { .addr = 0x60, @@ -87,23 +88,89 @@ static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = { .len = 6, }, }, + /* LIS3MDL */ + { + .i2c_addr = { 0x1e }, + .wai = { + .addr = 0x0f, + .val = 0x3d, + }, + .id = ST_LSM6DSX_ID_MAGN, + .odr_table = { + .reg = { + .addr = 0x20, + .mask = GENMASK(4, 2), + }, + .odr_avl[0] = { 1000, 0x0 }, + .odr_avl[1] = { 2000, 0x1 }, + .odr_avl[2] = { 3000, 0x2 }, + .odr_avl[3] = { 5000, 0x3 }, + .odr_avl[4] = { 10000, 0x4 }, + .odr_avl[5] = { 20000, 0x5 }, + .odr_avl[6] = { 40000, 0x6 }, + .odr_avl[7] = { 80000, 0x7 }, + .odr_len = 8, + }, + .fs_table = { + .reg = { + .addr = 0x21, + .mask = GENMASK(6, 5), + }, + .fs_avl[0] = { + .gain = 146, + .val = 0x00, + }, /* 4000 uG/LSB */ + .fs_avl[1] = { + .gain = 292, + .val = 0x01, + }, /* 8000 uG/LSB */ + .fs_avl[2] = { + .gain = 438, + .val = 0x02, + }, /* 12000 uG/LSB */ + .fs_avl[3] = { + .gain = 584, + .val = 0x03, + }, /* 16000 uG/LSB */ + .fs_len = 4, + }, + .pwr_table = { + .reg = { + .addr = 0x22, + .mask = GENMASK(1, 0), + }, + .off_val = 0x2, + .on_val = 0x0, + }, + .bdu = { + .addr = 0x24, + .mask = BIT(6), + }, + .out = { + .addr = 0x28, + .len = 6, + }, + }, }; static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw) { struct st_lsm6dsx_sensor *sensor; + u32 odr, timeout; sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); - msleep((2000U / sensor->odr) + 1); + odr = (hw->enable_mask & BIT(ST_LSM6DSX_ID_ACC)) ? sensor->odr : 12500; + /* set 10ms as minimum timeout for i2c slave configuration */ + timeout = max_t(u32, 2000000U / odr + 1, 10); + msleep(timeout); } -/** - * st_lsm6dsx_shub_read_reg - read i2c controller register +/* + * st_lsm6dsx_shub_read_output - read i2c controller register * * Read st_lsm6dsx i2c controller register */ -static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr, - u8 *data, int len) +int st_lsm6dsx_shub_read_output(struct st_lsm6dsx_hw *hw, u8 *data, int len) { const struct st_lsm6dsx_shub_settings *hub_settings; int err; @@ -111,20 +178,24 @@ static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr, mutex_lock(&hw->page_lock); hub_settings = &hw->settings->shub_settings; - err = st_lsm6dsx_set_page(hw, true); - if (err < 0) - goto out; + if (hub_settings->shub_out.sec_page) { + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + } - err = regmap_bulk_read(hw->regmap, addr, data, len); + err = regmap_bulk_read(hw->regmap, hub_settings->shub_out.addr, + data, len); - st_lsm6dsx_set_page(hw, false); + if (hub_settings->shub_out.sec_page) + st_lsm6dsx_set_page(hw, false); out: mutex_unlock(&hw->page_lock); return err; } -/** +/* * st_lsm6dsx_shub_write_reg - write i2c controller register * * Write st_lsm6dsx i2c controller register @@ -184,22 +255,25 @@ static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor, mutex_lock(&hw->page_lock); hub_settings = &hw->settings->shub_settings; - err = st_lsm6dsx_set_page(hw, true); - if (err < 0) - goto out; + if (hub_settings->master_en.sec_page) { + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + } data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->master_en.mask); err = regmap_update_bits(hw->regmap, hub_settings->master_en.addr, hub_settings->master_en.mask, data); - st_lsm6dsx_set_page(hw, false); + if (hub_settings->master_en.sec_page) + st_lsm6dsx_set_page(hw, false); out: mutex_unlock(&hw->page_lock); return err; } -/** +/* * st_lsm6dsx_shub_read - read data from slave device register * * Read data from slave device register. SLV0 is used for @@ -210,16 +284,21 @@ st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr, u8 *data, int len) { const struct st_lsm6dsx_shub_settings *hub_settings; + u8 config[3], slv_addr, slv_config = 0; struct st_lsm6dsx_hw *hw = sensor->hw; - u8 config[3], slv_addr; + const struct st_lsm6dsx_reg *aux_sens; int err; hub_settings = &hw->settings->shub_settings; slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); + aux_sens = &hw->settings->shub_settings.aux_sens; + /* do not overwrite aux_sens */ + if (slv_addr + 2 == aux_sens->addr) + slv_config = ST_LSM6DSX_SHIFT_VAL(3, aux_sens->mask); config[0] = (sensor->ext_info.addr << 1) | 1; config[1] = addr; - config[2] = len & ST_LS6DSX_READ_OP_MASK; + config[2] = (len & ST_LS6DSX_READ_OP_MASK) | slv_config; err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); @@ -232,17 +311,21 @@ st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr, st_lsm6dsx_shub_wait_complete(hw); - err = st_lsm6dsx_shub_read_reg(hw, hub_settings->shub_out, data, - len & ST_LS6DSX_READ_OP_MASK); + err = st_lsm6dsx_shub_read_output(hw, data, + len & ST_LS6DSX_READ_OP_MASK); + if (err < 0) + return err; st_lsm6dsx_shub_master_enable(sensor, false); - memset(config, 0, sizeof(config)); + config[0] = hub_settings->pause; + config[1] = 0; + config[2] = slv_config; return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); } -/** +/* * st_lsm6dsx_shub_write - write data to slave device register * * Write data from slave device register. SLV0 is used for @@ -294,7 +377,8 @@ st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr, st_lsm6dsx_shub_master_enable(sensor, false); } - memset(config, 0, sizeof(config)); + config[0] = hub_settings->pause; + config[1] = 0; return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); } @@ -316,17 +400,18 @@ st_lsm6dsx_shub_write_with_mask(struct st_lsm6dsx_sensor *sensor, static int st_lsm6dsx_shub_get_odr_val(struct st_lsm6dsx_sensor *sensor, - u16 odr, u16 *val) + u32 odr, u16 *val) { const struct st_lsm6dsx_ext_dev_settings *settings; int i; settings = sensor->ext_info.settings; - for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) - if (settings->odr_table.odr_avl[i].hz == odr) + for (i = 0; i < settings->odr_table.odr_len; i++) { + if (settings->odr_table.odr_avl[i].milli_hz == odr) break; + } - if (i == ST_LSM6DSX_ODR_LIST_SIZE) + if (i == settings->odr_table.odr_len) return -EINVAL; *val = settings->odr_table.odr_avl[i].val; @@ -334,7 +419,7 @@ st_lsm6dsx_shub_get_odr_val(struct st_lsm6dsx_sensor *sensor, } static int -st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr) +st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u32 odr) { const struct st_lsm6dsx_ext_dev_settings *settings; u16 val; @@ -401,7 +486,8 @@ int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) settings = sensor->ext_info.settings; if (enable) { - err = st_lsm6dsx_shub_set_odr(sensor, sensor->odr); + err = st_lsm6dsx_shub_set_odr(sensor, + sensor->ext_info.slv_odr); if (err < 0) return err; } else { @@ -439,14 +525,15 @@ st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor, if (err < 0) return err; - delay = 1000000 / sensor->odr; + delay = 1000000000 / sensor->ext_info.slv_odr; usleep_range(delay, 2 * delay); len = min_t(int, sizeof(data), ch->scan_type.realbits >> 3); err = st_lsm6dsx_shub_read(sensor, ch->address, data, len); + if (err < 0) + return err; - st_lsm6dsx_shub_set_enable(sensor, false); - + err = st_lsm6dsx_shub_set_enable(sensor, false); if (err < 0) return err; @@ -471,16 +558,16 @@ st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(iio_dev); - if (ret) - break; + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; ret = st_lsm6dsx_shub_read_oneshot(sensor, ch, val); - iio_device_release_direct_mode(iio_dev); + iio_device_release_direct(iio_dev); break; case IIO_CHAN_INFO_SAMP_FREQ: - *val = sensor->odr; - ret = IIO_VAL_INT; + *val = sensor->ext_info.slv_odr / 1000; + *val2 = (sensor->ext_info.slv_odr % 1000) * 1000; + ret = IIO_VAL_INT_PLUS_MICRO; break; case IIO_CHAN_INFO_SCALE: *val = 0; @@ -496,34 +583,88 @@ st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev, } static int -st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) +st_lsm6dsx_shub_set_full_scale(struct st_lsm6dsx_sensor *sensor, + u32 gain) { - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - int err; + const struct st_lsm6dsx_fs_table_entry *fs_table; + int i, err; + + fs_table = &sensor->ext_info.settings->fs_table; + if (!fs_table->reg.addr) + return -ENOTSUPP; + + for (i = 0; i < fs_table->fs_len; i++) { + if (fs_table->fs_avl[i].gain == gain) + break; + } + + if (i == fs_table->fs_len) + return -EINVAL; - err = iio_device_claim_direct_mode(iio_dev); - if (err) + err = st_lsm6dsx_shub_write_with_mask(sensor, fs_table->reg.addr, + fs_table->reg.mask, + fs_table->fs_avl[i].val); + if (err < 0) return err; + sensor->gain = gain; + + return 0; +} + +static int +__st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int err; + switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: { + struct st_lsm6dsx_hw *hw = sensor->hw; + struct st_lsm6dsx_sensor *ref_sensor; + u8 odr_val; u16 data; + int odr; + val = val * 1000 + val2 / 1000; err = st_lsm6dsx_shub_get_odr_val(sensor, val, &data); - if (!err) - sensor->odr = val; - break; + if (err) + return err; + + ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + odr = st_lsm6dsx_check_odr(ref_sensor, val, &odr_val); + if (odr < 0) + return odr; + + sensor->ext_info.slv_odr = val; + sensor->odr = odr; + sensor->hwfifo_odr_mHz = odr; + return 0; } + case IIO_CHAN_INFO_SCALE: + return st_lsm6dsx_shub_set_full_scale(sensor, val2); default: - err = -EINVAL; - break; + return -EINVAL; } +} + +static int +st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; - iio_device_release_direct_mode(iio_dev); + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; - return err; + ret = __st_lsm6dsx_shub_write_raw(iio_dev, chan, val, val2, mask); + + iio_device_release_direct(iio_dev); + + return ret; } static ssize_t @@ -536,12 +677,11 @@ st_lsm6dsx_shub_sampling_freq_avail(struct device *dev, int i, len = 0; settings = sensor->ext_info.settings; - for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) { - u16 val = settings->odr_table.odr_avl[i].hz; + for (i = 0; i < settings->odr_table.odr_len; i++) { + u32 val = settings->odr_table.odr_avl[i].milli_hz; - if (val > 0) - len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", - val); + len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%03d ", + val / 1000, val % 1000); } buf[len - 1] = '\n'; @@ -557,13 +697,9 @@ static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev, int i, len = 0; settings = sensor->ext_info.settings; - for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) { - u16 val = settings->fs_table.fs_avl[i].gain; - - if (val > 0) - len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", - val); - } + for (i = 0; i < settings->fs_table.fs_len; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", + settings->fs_table.fs_avl[i].gain); buf[len - 1] = '\n'; return len; @@ -572,18 +708,18 @@ static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev, static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail); static IIO_DEVICE_ATTR(in_scale_available, 0444, st_lsm6dsx_shub_scale_avail, NULL, 0); -static struct attribute *st_lsm6dsx_ext_attributes[] = { +static struct attribute *st_lsm6dsx_shub_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_scale_available.dev_attr.attr, NULL, }; -static const struct attribute_group st_lsm6dsx_ext_attribute_group = { - .attrs = st_lsm6dsx_ext_attributes, +static const struct attribute_group st_lsm6dsx_shub_attribute_group = { + .attrs = st_lsm6dsx_shub_attributes, }; -static const struct iio_info st_lsm6dsx_ext_info = { - .attrs = &st_lsm6dsx_ext_attribute_group, +static const struct iio_info st_lsm6dsx_shub_info = { + .attrs = &st_lsm6dsx_shub_attribute_group, .read_raw = st_lsm6dsx_shub_read_raw, .write_raw = st_lsm6dsx_shub_write_raw, .hwfifo_set_watermark = st_lsm6dsx_set_watermark, @@ -595,6 +731,7 @@ st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw, const struct st_lsm6dsx_ext_dev_settings *info, u8 i2c_addr, const char *name) { + enum st_lsm6dsx_sensor_id ref_id = ST_LSM6DSX_ID_ACC; struct iio_chan_spec *ext_channels; struct st_lsm6dsx_sensor *sensor; struct iio_dev *iio_dev; @@ -604,13 +741,14 @@ st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw, return NULL; iio_dev->modes = INDIO_DIRECT_MODE; - iio_dev->dev.parent = hw->dev; - iio_dev->info = &st_lsm6dsx_ext_info; + iio_dev->info = &st_lsm6dsx_shub_info; sensor = iio_priv(iio_dev); sensor->id = id; sensor->hw = hw; - sensor->odr = info->odr_table.odr_avl[0].hz; + sensor->odr = hw->settings->odr_table[ref_id].odr_avl[0].milli_hz; + sensor->hwfifo_odr_mHz = sensor->odr; + sensor->ext_info.slv_odr = info->odr_table.odr_avl[0].milli_hz; sensor->gain = info->fs_table.fs_avl[0].gain; sensor->ext_info.settings = info; sensor->ext_info.addr = i2c_addr; @@ -688,14 +826,19 @@ st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, const struct st_lsm6dsx_ext_dev_settings *settings) { const struct st_lsm6dsx_shub_settings *hub_settings; + u8 config[3], data, slv_addr, slv_config = 0; + const struct st_lsm6dsx_reg *aux_sens; struct st_lsm6dsx_sensor *sensor; - u8 config[3], data, slv_addr; bool found = false; int i, err; + sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); hub_settings = &hw->settings->shub_settings; + aux_sens = &hw->settings->shub_settings.aux_sens; slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); - sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + /* do not overwrite aux_sens */ + if (slv_addr + 2 == aux_sens->addr) + slv_config = ST_LSM6DSX_SHIFT_VAL(3, aux_sens->mask); for (i = 0; i < ARRAY_SIZE(settings->i2c_addr); i++) { if (!settings->i2c_addr[i]) @@ -704,7 +847,7 @@ st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, /* read wai slave register */ config[0] = (settings->i2c_addr[i] << 1) | 0x1; config[1] = settings->wai.addr; - config[2] = 0x1; + config[2] = 0x1 | slv_config; err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); @@ -717,9 +860,7 @@ st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, st_lsm6dsx_shub_wait_complete(hw); - err = st_lsm6dsx_shub_read_reg(hw, - hub_settings->shub_out, - &data, sizeof(data)); + err = st_lsm6dsx_shub_read_output(hw, &data, sizeof(data)); st_lsm6dsx_shub_master_enable(sensor, false); @@ -735,7 +876,9 @@ st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, } /* reset SLV0 channel */ - memset(config, 0, sizeof(config)); + config[0] = hub_settings->pause; + config[1] = 0; + config[2] = slv_config; err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); if (err < 0) @@ -770,7 +913,7 @@ int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name) if (err < 0) return err; - if (++num_ext_dev >= ST_LSM6DSX_MAX_SLV_NUM) + if (++num_ext_dev >= hw->settings->shub_settings.num_ext_dev) break; id++; } |
