// SPDX-License-Identifier: GPL-2.0 /* * IIO core driver for Bosch BMI323 6-Axis IMU. * * Copyright (C) 2023, Jagath Jog J * * Datasheet: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi323-ds000.pdf */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bmi323.h" enum bmi323_sensor_type { BMI323_ACCEL, BMI323_GYRO, BMI323_SENSORS_CNT, }; enum bmi323_opr_mode { ACC_GYRO_MODE_DISABLE = 0x00, GYRO_DRIVE_MODE_ENABLED = 0x01, ACC_GYRO_MODE_DUTYCYCLE = 0x03, ACC_GYRO_MODE_CONTINOUS = 0x04, ACC_GYRO_MODE_HIGH_PERF = 0x07, }; enum bmi323_state { BMI323_IDLE, BMI323_BUFFER_DRDY_TRIGGERED, BMI323_BUFFER_FIFO, }; enum bmi323_irq_pin { BMI323_IRQ_DISABLED, BMI323_IRQ_INT1, BMI323_IRQ_INT2, }; enum bmi323_3db_bw { BMI323_BW_ODR_BY_2, BMI323_BW_ODR_BY_4, }; enum bmi323_scan { BMI323_ACCEL_X, BMI323_ACCEL_Y, BMI323_ACCEL_Z, BMI323_GYRO_X, BMI323_GYRO_Y, BMI323_GYRO_Z, BMI323_CHAN_MAX }; struct bmi323_hw { u8 data; u8 config; const int (*scale_table)[2]; int scale_table_len; }; /* * The accelerometer supports +-2G/4G/8G/16G ranges, and the resolution of * each sample is 16 bits, signed. * At +-8G the scale can calculated by * ((8 + 8) * 9.80665 / (2^16 - 1)) * 10^6 = 2394.23819 scale in micro * */ static const int bmi323_accel_scale[][2] = { { 0, 598 }, { 0, 1197 }, { 0, 2394 }, { 0, 4788 }, }; static const int bmi323_gyro_scale[][2] = { { 0, 66 }, { 0, 133 }, { 0, 266 }, { 0, 532 }, { 0, 1065 }, }; static const int bmi323_accel_gyro_avrg[] = {0, 2, 4, 8, 16, 32, 64}; static const struct bmi323_hw bmi323_hw[2] = { [BMI323_ACCEL] = { .data = BMI323_ACCEL_X_REG, .config = BMI323_ACC_CONF_REG, .scale_table = bmi323_accel_scale, .scale_table_len = ARRAY_SIZE(bmi323_accel_scale), }, [BMI323_GYRO] = { .data = BMI323_GYRO_X_REG, .config = BMI323_GYRO_CONF_REG, .scale_table = bmi323_gyro_scale, .scale_table_len = ARRAY_SIZE(bmi323_gyro_scale), }, }; struct bmi323_data { struct device *dev; struct regmap *regmap; struct iio_mount_matrix orientation; enum bmi323_irq_pin irq_pin; struct iio_trigger *trig; bool drdy_trigger_enabled; enum bmi323_state state; s64 fifo_tstamp, old_fifo_tstamp; u32 odrns[BMI323_SENSORS_CNT]; u32 odrhz[BMI323_SENSORS_CNT]; unsigned int feature_events; /* * Lock to protect the members of device's private data from concurrent * access and also to serialize the access of extended registers. * See bmi323_write_ext_reg(..) for more info. */ struct mutex mutex; int watermark; __le16 fifo_buff[BMI323_FIFO_FULL_IN_WORDS] __aligned(IIO_DMA_MINALIGN); struct { __le16 channels[BMI323_CHAN_MAX]; s64 ts __aligned(8); } buffer; __le16 steps_count[BMI323_STEP_LEN]; }; static const struct iio_mount_matrix * bmi323_get_mount_matrix(const struct iio_dev *idev, const struct iio_chan_spec *chan) { struct bmi323_data *data = iio_priv(idev); return &data->orientation; } static const struct iio_chan_spec_ext_info bmi323_ext_info[] = { IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, bmi323_get_mount_matrix), { } }; static const struct iio_event_spec bmi323_step_wtrmrk_event = { .type = IIO_EV_TYPE_CHANGE, .dir = IIO_EV_DIR_NONE, .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE), }; static const struct iio_event_spec bmi323_accel_event[] = { { .type = IIO_EV_TYPE_MAG, .dir = IIO_EV_DIR_FALLING, .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_PERIOD) | BIT(IIO_EV_INFO_HYSTERESIS) | BIT(IIO_EV_INFO_ENABLE), }, { .type = IIO_EV_TYPE_MAG, .dir = IIO_EV_DIR_RISING, .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_PERIOD) | BIT(IIO_EV_INFO_HYSTERESIS) | BIT(IIO_EV_INFO_ENABLE), }, { .type = IIO_EV_TYPE_GESTURE, .dir = IIO_EV_DIR_SINGLETAP, .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_RESET_TIMEOUT), }, { .type = IIO_EV_TYPE_GESTURE, .dir = IIO_EV_DIR_DOUBLETAP, .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_RESET_TIMEOUT) | BIT(IIO_EV_INFO_TAP2_MIN_DELAY), }, }; #define BMI323_ACCEL_CHANNEL(_type, _axis, _index) { \ .type = _type, \ .modified = 1, \ .channel2 = IIO_MOD_##_axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .info_mask_shared_by_type_available = \ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_index = _index, \ .scan_type = { \ .sign = 's', \ .realbits = 16, \ .storagebits = 16, \ .endianness = IIO_LE, \ }, \ .ext_info = bmi323_ext_info, \ .event_spec = bmi323_accel_event, \ .num_event_specs = ARRAY_SIZE(bmi323_accel_event), \ } #define BMI323_GYRO_CHANNEL(_type, _axis, _index) { \ .type = _type, \ .modified = 1, \ .channel2 = IIO_MOD_##_axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .info_mask_shared_by_type_available = \ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_index = _index, \ .scan_type = { \ .sign = 's', \ .realbits = 16, \ .storagebits = 16, \ .endianness = IIO_LE, \ }, \ .ext_info = bmi323_ext_info, \ } static const struct iio_chan_spec bmi323_channels[] = { BMI323_ACCEL_CHANNEL(IIO_ACCEL, X, BMI323_ACCEL_X), BMI323_ACCEL_CHANNEL(IIO_ACCEL, Y, BMI323_ACCEL_Y), BMI323_ACCEL_CHANNEL(IIO_ACCEL, Z, BMI323_ACCEL_Z), BMI323_GYRO_CHANNEL(IIO_ANGL_VEL, X, BMI323_GYRO_X), BMI323_GYRO_CHANNEL(IIO_ANGL_VEL, Y, BMI323_GYRO_Y), BMI323_GYRO_CHANNEL(IIO_ANGL_VEL, Z, BMI323_GYRO_Z), { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), .scan_index = -1, }, { .type = IIO_STEPS, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | BIT(IIO_CHAN_INFO_ENABLE), .scan_index = -1, .event_spec = &bmi323_step_wtrmrk_event, .num_event_specs = 1, }, IIO_CHAN_SOFT_TIMESTAMP(BMI323_CHAN_MAX), }; static const int bmi323_acc_gyro_odr[][2] = { { 0, 781250 }, { 1, 562500 }, { 3, 125000 }, { 6, 250000 }, { 12, 500000 }, { 25, 0 }, { 50, 0 }, { 100, 0 }, { 200, 0 }, { 400, 0 }, { 800, 0 }, }; static const int bmi323_acc_gyro_odrns[] = { 1280 * MEGA, 640 * MEGA, 320 * MEGA, 160 * MEGA, 80 * MEGA, 40 * MEGA, 20 * MEGA, 10 * MEGA, 5 * MEGA, 2500 * KILO, 1250 * KILO, }; static enum bmi323_sensor_type bmi323_iio_to_sensor(enum iio_chan_type iio_type) { switch (iio_type) { case IIO_ACCEL: return BMI323_ACCEL; case IIO_ANGL_VEL: return BMI323_GYRO; default: return -EINVAL; } } static int bmi323_set_mode(struct bmi323_data *data, enum bmi323_sensor_type sensor, enum bmi323_opr_mode mode) { guard(mutex)(&data->mutex); return regmap_update_bits(data->regmap, bmi323_hw[sensor].config, BMI323_ACC_GYRO_CONF_MODE_MSK, FIELD_PREP(BMI323_ACC_GYRO_CONF_MODE_MSK, mode)); } /* * When writing data to extended register there must be no communication to * any other register before write transaction is complete. * See datasheet section 6.2 Extended Register Map Description. */ static int bmi323_write_ext_reg(struct bmi323_data *data, unsigned int ext_addr, unsigned int ext_data) { int ret, feature_status; ret = regmap_read(data->regmap, BMI323_FEAT_DATA_STATUS, &feature_status); if (ret) return ret; if (!FIELD_GET(BMI323_FEAT_DATA_TX_RDY_MSK, feature_status)) return -EBUSY; ret = regmap_write(data->regmap, BMI323_FEAT_DATA_ADDR, ext_addr); if (ret) return ret; return regmap_write(data->regmap, BMI323_FEAT_DATA_TX, ext_data); } /* * When reading data from extended register there must be no communication to * any other register before read transaction is complete. * See datasheet section 6.2 Extended Register Map Description. */ static int bmi323_read_ext_reg(struct bmi323_data *data, unsigned int ext_addr, unsigned int *ext_data) { int ret, feature_status; ret = regmap_read(data->regmap, BMI323_FEAT_DATA_STATUS, &feature_status); if (ret) return ret; if (!FIELD_GET(BMI323_FEAT_DATA_TX_RDY_MSK, feature_status)) return -EBUSY; ret = regmap_write(data->regmap, BMI323_FEAT_DATA_ADDR, ext_addr); if (ret) return ret; return regmap_read(data->regmap, BMI323_FEAT_DATA_TX, ext_data); } static int bmi323_update_ext_reg(struct bmi323_data *data, unsigned int ext_addr, unsigned int mask, unsigned int ext_data) { unsigned int value; int ret; ret = bmi323_read_ext_reg(data, ext_addr, &value); if (ret) return ret; set_mask_bits(&value, mask, ext_data); return bmi323_write_ext_reg(data, ext_addr, value); } static int bmi323_get_error_status(struct bmi323_data *data) { int error, ret; guard(mutex)(&data->mutex); ret = regmap_read(data->regmap, BMI323_ERR_REG, &error); if (ret) return ret; if (error) dev_err(data->dev, "Sensor error 0x%x\n", error); return error; } static int bmi323_feature_engine_events(struct bmi323_data *data, const unsigned int event_mask, bool state) { unsigned int value; int ret; ret = regmap_read(data->regmap, BMI323_FEAT_IO0_REG, &value); if (ret) return ret; /* Register must be cleared before changing an active config */ ret = regmap_write(data->regmap, BMI323_FEAT_IO0_REG, 0); if (ret) return ret; if (state) value |= event_mask; else value &= ~event_mask; ret = regmap_write(data->regmap, BMI323_FEAT_IO0_REG, value); if (ret) return ret; return regmap_write(data->regmap, BMI323_FEAT_IO_STATUS_REG, BMI323_FEAT_IO_STATUS_MSK); } static int bmi323_step_wtrmrk_en(struct bmi323_data *data, int state) { enum bmi323_irq_pin step_irq; int ret; guard(mutex)(&data->mutex); if (!FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK, data->feature_events)) return -EINVAL; if (state) step_irq = data->irq_pin; else step_irq = BMI323_IRQ_DISABLED; ret = bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG, BMI323_STEP_SC1_WTRMRK_MSK, FIELD_PREP(BMI323_STEP_SC1_WTRMRK_MSK, state ? 1 : 0)); if (ret) return ret; return regmap_update_bits(data->regmap, BMI323_INT_MAP1_REG, BMI323_STEP_CNT_MSK, FIELD_PREP(BMI323_STEP_CNT_MSK, step_irq)); } static int bmi323_motion_config_reg(enum iio_event_direction dir) { switch (dir) { case IIO_EV_DIR_RISING: return BMI323_ANYMO1_REG; case IIO_EV_DIR_FALLING: return BMI323_NOMO1_REG; default: return -EINVAL; } } static int bmi323_motion_event_en(struct bmi323_data *data, enum iio_event_direction dir, int state) { unsigned int state_value = state ? BMI323_FEAT_XYZ_MSK : 0; int config, ret, msk, raw, field_value; enum bmi323_irq_pin motion_irq; int irq_msk, irq_field_val; if (state) motion_irq = data->irq_pin; else motion_irq = BMI323_IRQ_DISABLED; switch (dir) { case IIO_EV_DIR_RISING: msk = BMI323_FEAT_IO0_XYZ_MOTION_MSK; raw = 512; config = BMI323_ANYMO1_REG; irq_msk = BMI323_MOTION_MSK; irq_field_val = FIELD_PREP(BMI323_MOTION_MSK, motion_irq); field_value = FIELD_PREP(BMI323_FEAT_IO0_XYZ_MOTION_MSK, state_value); break; case IIO_EV_DIR_FALLING: msk = BMI323_FEAT_IO0_XYZ_NOMOTION_MSK; raw = 0; config = BMI323_NOMO1_REG; irq_msk = BMI323_NOMOTION_MSK; irq_field_val = FIELD_PREP(BMI323_NOMOTION_MSK, motion_irq); field_value = FIELD_PREP(BMI323_FEAT_IO0_XYZ_NOMOTION_MSK, state_value); break; default: return -EINVAL; } guard(mutex)(&data->mutex); ret = bmi323_feature_engine_events(data, msk, state); if (ret) return ret; ret = bmi323_update_ext_reg(data, config, BMI323_MO1_REF_UP_MSK, FIELD_PREP(BMI323_MO1_REF_UP_MSK, 0)); if (ret) return ret; /* Set initial value to avoid interrupts while enabling*/ ret = bmi323_update_ext_reg(data, config, BMI323_MO1_SLOPE_TH_MSK, FIELD_PREP(BMI323_MO1_SLOPE_TH_MSK, raw)); if (ret) return ret; ret = regmap_update_bits(data->regmap, BMI323_INT_MAP1_REG, irq_msk, irq_field_val); if (ret) return ret; set_mask_bits(&data->feature_events, msk, field_value); return 0; } static int bmi323_tap_event_en(struct bmi323_data *data, enum iio_event_direction dir, int state) { enum bmi323_irq_pin tap_irq; int ret, tap_enabled; guard(mutex)(&data->mutex); if (data->odrhz[BMI323_ACCEL] < 200) { dev_err(data->dev, "Invalid accelerometer parameter\n"); return -EINVAL; } switch (dir) { case IIO_EV_DIR_SINGLETAP: ret = bmi323_feature_engine_events(data, BMI323_FEAT_IO0_S_TAP_MSK, state); if (ret) return ret; set_mask_bits(&data->feature_events, BMI323_FEAT_IO0_S_TAP_MSK, FIELD_PREP(BMI323_FEAT_IO0_S_TAP_MSK, state)); break; case IIO_EV_DIR_DOUBLETAP: ret = bmi323_feature_engine_events(data, BMI323_FEAT_IO0_D_TAP_MSK, state); if (ret) return ret; set_mask_bits(&data->feature_events, BMI323_FEAT_IO0_D_TAP_MSK, FIELD_PREP(BMI323_FEAT_IO0_D_TAP_MSK, state)); break; default: return -EINVAL; } tap_enabled = FIELD_GET(BMI323_FEAT_IO0_S_TAP_MSK | BMI323_FEAT_IO0_D_TAP_MSK, data->feature_events); if (tap_enabled) tap_irq = data->irq_pin; else tap_irq = BMI323_IRQ_DISABLED; ret = regmap_update_bits(data->regmap, BMI323_INT_MAP2_REG, BMI323_TAP_MSK, FIELD_PREP(BMI323_TAP_MSK, tap_irq)); if (ret) return ret; if (!state) return 0; ret = bmi323_update_ext_reg(data, BMI323_TAP1_REG, BMI323_TAP1_MAX_PEAKS_MSK, FIELD_PREP(BMI323_TAP1_MAX_PEAKS_MSK, 0x04)); if (ret) return ret; ret = bmi323_update_ext_reg(data, BMI323_TAP1_REG, BMI323_TAP1_AXIS_SEL_MSK, FIELD_PREP(BMI323_TAP1_AXIS_SEL_MSK, BMI323_AXIS_XYZ_MSK)); if (ret) return ret; return bmi323_update_ext_reg(data, BMI323_TAP1_REG, BMI323_TAP1_TIMOUT_MSK, FIELD_PREP(BMI323_TAP1_TIMOUT_MSK, 0)); } static ssize_t in_accel_gesture_tap_wait_dur_show(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct bmi323_data *data = iio_priv(indio_dev); unsigned int reg_value, raw; int ret, val[2]; scoped_guard(mutex, &data->mutex) { ret = bmi323_read_ext_reg(data, BMI323_TAP2_REG, ®_value); if (ret) return ret; } raw = FIELD_GET(BMI323_TAP2_MAX_DUR_MSK, reg_value); val[0] = raw / BMI323_MAX_GES_DUR_SCALE; val[1] = BMI323_RAW_TO_MICRO(raw, BMI323_MAX_GES_DUR_SCALE); return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(val), val); } static ssize_t in_accel_gesture_tap_wait_dur_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct bmi323_data *data = iio_priv(indio_dev); int ret, val_int, val_fract, raw; ret = iio_str_to_fixpoint(buf, 100000, &val_int, &val_fract); if (ret) return ret; raw = BMI323_INT_MICRO_TO_RAW(val_int, val_fract, BMI323_MAX_GES_DUR_SCALE); if (!in_range(raw, 0, 64)) return -EINVAL; guard(mutex)(&data->mutex); ret = bmi323_update_ext_reg(data, BMI323_TAP2_REG, BMI323_TAP2_MAX_DUR_MSK, FIELD_PREP(BMI323_TAP2_MAX_DUR_MSK, raw)); if (ret) return ret; return len; } /* * Maximum duration from first tap within the second tap is expected to happen. * This timeout is applicable only if gesture_tap_wait_timeout is enabled. */ static IIO_DEVICE_ATTR_RW(in_accel_gesture_tap_wait_dur, 0); static ssize_t in_accel_gesture_tap_wait_timeout_show(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct bmi323_data *data = iio_priv(indio_dev); unsigned int reg_value, raw; int ret; scoped_guard(mutex, &data->mutex) { ret = bmi323_read_ext_reg(data, BMI323_TAP1_REG, ®_value); if (ret) return ret; } raw = FIELD_GET(BMI323_TAP1_TIMOUT_MSK, reg_value); return iio_format_value(buf, IIO_VAL_INT, 1, &raw); } static ssize_t in_accel_gesture_tap_wait_timeout_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct bmi323_data *data = iio_priv(indio_dev); bool val; int ret; ret = kstrtobool(buf, &val); if (ret) return ret; guard(mutex)(&data->mutex); ret = bmi323_update_ext_reg(data, BMI323_TAP1_REG, BMI323_TAP1_TIMOUT_MSK, FIELD_PREP(BMI323_TAP1_TIMOUT_MSK, val)); if (ret) return ret; return len; } /* Enable/disable gesture confirmation with wait time */ static IIO_DEVICE_ATTR_RW(in_accel_gesture_tap_wait_timeout, 0); static IIO_CONST_ATTR(in_accel_gesture_tap_wait_dur_available, "[0.0 0.04 2.52]"); static IIO_CONST_ATTR(in_accel_gesture_doubletap_tap2_min_delay_available, "[0.005 0.005 0.075]"); static IIO_CONST_ATTR(in_accel_gesture_tap_reset_timeout_available, "[0.04 0.04 0.6]"); static IIO_CONST_ATTR(in_accel_gesture_tap_value_available, "[0.0 0.002 1.99]"); static IIO_CONST_ATTR(in_accel_mag_value_available, "[0.0 0.002 7.99]"); static IIO_CONST_ATTR(in_accel_mag_period_available, "[0.0 0.02 162.0]"); static IIO_CONST_ATTR(in_accel_mag_hysteresis_available, "[0.0 0.002 1.99]"); static struct attribute *bmi323_event_attributes[] = { &iio_const_attr_in_accel_gesture_tap_value_available.dev_attr.attr, &iio_const_attr_in_accel_gesture_tap_reset_timeout_available.dev_attr.attr, &iio_const_attr_in_accel_gesture_doubletap_tap2_min_delay_available.dev_attr.attr, &iio_const_attr_in_accel_gesture_tap_wait_dur_available.dev_attr.attr, &iio_dev_attr_in_accel_gesture_tap_wait_timeout.dev_attr.attr, &iio_dev_attr_in_accel_gesture_tap_wait_dur.dev_attr.attr, &iio_const_attr_in_accel_mag_value_available.dev_attr.attr, &iio_const_attr_in_accel_mag_period_available.dev_attr.attr, &iio_const_attr_in_accel_mag_hysteresis_available.dev_attr.attr, NULL }; static const struct attribute_group bmi323_event_attribute_group = { .attrs = bmi323_event_attributes, }; static int bmi323_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, int state) { struct bmi323_data *data = iio_priv(indio_dev); switch (type) { case IIO_EV_TYPE_MAG: return bmi323_motion_event_en(data, dir, state); case IIO_EV_TYPE_GESTURE: return bmi323_tap_event_en(data, dir, state); case IIO_EV_TYPE_CHANGE: return bmi323_step_wtrmrk_en(data, state); default: return -EINVAL; } } static int bmi323_read_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir) { struct bmi323_data *data = iio_priv(indio_dev); int ret, value, reg_val; guard(mutex)(&data->mutex); switch (chan->type) { case IIO_ACCEL: switch (dir) { case IIO_EV_DIR_SINGLETAP: ret = FIELD_GET(BMI323_FEAT_IO0_S_TAP_MSK, data->feature_events); break; case IIO_EV_DIR_DOUBLETAP: ret = FIELD_GET(BMI323_FEAT_IO0_D_TAP_MSK, data->feature_events); break; case IIO_EV_DIR_RISING: value = FIELD_GET(BMI323_FEAT_IO0_XYZ_MOTION_MSK, data->feature_events); ret = value ? 1 : 0; break; case IIO_EV_DIR_FALLING: value = FIELD_GET(BMI323_FEAT_IO0_XYZ_NOMOTION_MSK, data->feature_events); ret = value ? 1 : 0; break; default: ret = -EINVAL; break; } return ret; case IIO_STEPS: ret = regmap_read(data->regmap, BMI323_INT_MAP1_REG, ®_val); if (ret) return ret; return FIELD_GET(BMI323_STEP_CNT_MSK, reg_val) ? 1 : 0; default: return -EINVAL; } } static int bmi323_write_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, enum iio_event_info info, int val, int val2) { struct bmi323_data *data = iio_priv(indio_dev); unsigned int raw; int reg; guard(mutex)(&data->mutex); switch (type) { case IIO_EV_TYPE_GESTURE: switch (info) { case IIO_EV_INFO_VALUE: if (!in_range(val, 0, 2)) return -EINVAL; raw = BMI323_INT_MICRO_TO_RAW(val, val2, BMI323_TAP_THRES_SCALE); return bmi323_update_ext_reg(data, BMI323_TAP2_REG, BMI323_TAP2_THRES_MSK, FIELD_PREP(BMI323_TAP2_THRES_MSK, raw)); case IIO_EV_INFO_RESET_TIMEOUT: if (val || !in_range(val2, 40000, 560001)) return -EINVAL; raw = BMI323_INT_MICRO_TO_RAW(val, val2, BMI323_QUITE_TIM_GES_SCALE); return bmi323_update_ext_reg(data, BMI323_TAP3_REG, BMI323_TAP3_QT_AFT_GES_MSK, FIELD_PREP(BMI323_TAP3_QT_AFT_GES_MSK, raw)); case IIO_EV_INFO_TAP2_MIN_DELAY: if (val || !in_range(val2, 5000, 70001)) return -EINVAL; raw = BMI323_INT_MICRO_TO_RAW(val, val2, BMI323_DUR_BW_TAP_SCALE); return bmi323_update_ext_reg(data, BMI323_TAP3_REG, BMI323_TAP3_QT_BW_TAP_MSK, FIELD_PREP(BMI323_TAP3_QT_BW_TAP_MSK, raw)); default: return -EINVAL; } case IIO_EV_TYPE_MAG: reg = bmi323_motion_config_reg(dir); if (reg < 0) return -EINVAL; switch (info) { case IIO_EV_INFO_VALUE: if (!in_range(val, 0, 8)) return -EINVAL; raw = BMI323_INT_MICRO_TO_RAW(val, val2, BMI323_MOTION_THRES_SCALE); return bmi323_update_ext_reg(data, reg, BMI323_MO1_SLOPE_TH_MSK, FIELD_PREP(BMI323_MO1_SLOPE_TH_MSK, raw)); case IIO_EV_INFO_PERIOD: if (!in_range(val, 0, 163)) return -EINVAL; raw = BMI323_INT_MICRO_TO_RAW(val, val2, BMI323_MOTION_DURAT_SCALE); return bmi323_update_ext_reg(data, reg + BMI323_MO3_OFFSET, BMI323_MO3_DURA_MSK, FIELD_PREP(BMI323_MO3_DURA_MSK, raw)); case IIO_EV_INFO_HYSTERESIS: if (!in_range(val, 0, 2)) return -EINVAL; raw = BMI323_INT_MICRO_TO_RAW(val, val2, BMI323_MOTION_HYSTR_SCALE); return bmi323_update_ext_reg(data, reg + BMI323_MO2_OFFSET, BMI323_MO2_HYSTR_MSK, FIELD_PREP(BMI323_MO2_HYSTR_MSK, raw)); default: return -EINVAL; } case IIO_EV_TYPE_CHANGE: if (!in_range(val, 0, 20461)) return -EINVAL; raw = val / 20; return bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG, BMI323_STEP_SC1_WTRMRK_MSK, FIELD_PREP(BMI323_STEP_SC1_WTRMRK_MSK, raw)); default: return -EINVAL; } } static int bmi323_read_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, enum iio_event_info info, int *val, int *val2) { struct bmi323_data *data = iio_priv(indio_dev); unsigned int raw, reg_value; int ret, reg; guard(mutex)(&data->mutex); switch (type) { case IIO_EV_TYPE_GESTURE: switch (info) { case IIO_EV_INFO_VALUE: ret = bmi323_read_ext_reg(data, BMI323_TAP2_REG, ®_value); if (ret) return ret; raw = FIELD_GET(BMI323_TAP2_THRES_MSK, reg_value); *val = raw / BMI323_TAP_THRES_SCALE; *val2 = BMI323_RAW_TO_MICRO(raw, BMI323_TAP_THRES_SCALE); return IIO_VAL_INT_PLUS_MICRO; case IIO_EV_INFO_RESET_TIMEOUT: ret = bmi323_read_ext_reg(data, BMI323_TAP3_REG, ®_value); if (ret) return ret; raw = FIELD_GET(BMI323_TAP3_QT_AFT_GES_MSK, reg_value); *val = 0; *val2 = BMI323_RAW_TO_MICRO(raw, BMI323_QUITE_TIM_GES_SCALE); return IIO_VAL_INT_PLUS_MICRO; case IIO_EV_INFO_TAP2_MIN_DELAY: ret = bmi323_read_ext_reg(data, BMI323_TAP3_REG, ®_value); if (ret) return ret; raw = FIELD_GET(BMI323_TAP3_QT_BW_TAP_MSK, reg_value); *val = 0; *val2 = BMI323_RAW_TO_MICRO(raw, BMI323_DUR_BW_TAP_SCALE); return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } case IIO_EV_TYPE_MAG: reg = bmi323_motion_config_reg(dir); if (reg < 0) return -EINVAL; switch (info) { case IIO_EV_INFO_VALUE: ret = bmi323_read_ext_reg(data, reg, ®_value); if (ret) return ret; raw = FIELD_GET(BMI323_MO1_SLOPE_TH_MSK, reg_value); *val = raw / BMI323_MOTION_THRES_SCALE; *val2 = BMI323_RAW_TO_MICRO(raw, BMI323_MOTION_THRES_SCALE); return IIO_VAL_INT_PLUS_MICRO; case IIO_EV_INFO_PERIOD: ret = bmi323_read_ext_reg(data, reg + BMI323_MO3_OFFSET, ®_value); if (ret) return ret; raw = FIELD_GET(BMI323_MO3_DURA_MSK, reg_value); *val = raw / BMI323_MOTION_DURAT_SCALE; *val2 = BMI323_RAW_TO_MICRO(raw, BMI323_MOTION_DURAT_SCALE); return IIO_VAL_INT_PLUS_MICRO; case IIO_EV_INFO_HYSTERESIS: ret = bmi323_read_ext_reg(data, reg + BMI323_MO2_OFFSET, ®_value); if (ret) return ret; raw = FIELD_GET(BMI323_MO2_HYSTR_MSK, reg_value); *val = raw / BMI323_MOTION_HYSTR_SCALE; *val2 = BMI323_RAW_TO_MICRO(raw, BMI323_MOTION_HYSTR_SCALE); return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } case IIO_EV_TYPE_CHANGE: ret = bmi323_read_ext_reg(data, BMI323_STEP_SC1_REG, ®_value); if (ret) return ret; raw = FIELD_GET(BMI323_STEP_SC1_WTRMRK_MSK, reg_value); *val = raw * 20; return IIO_VAL_INT; default: return -EINVAL; } } static int __bmi323_fifo_flush(struct iio_dev *indio_dev) { struct bmi323_data *data = iio_priv(indio_dev); int i, ret, fifo_lvl, frame_count, bit, index; __le16 *frame, *pchannels; u64 sample_period; s64 tstamp; guard(mutex)(&data->mutex); ret = regmap_read(data->regmap, BMI323_FIFO_FILL_LEVEL_REG, &fifo_lvl); if (ret) return ret; fifo_lvl = min(fifo_lvl, BMI323_FIFO_FULL_IN_WORDS); frame_count = fifo_lvl / BMI323_FIFO_FRAME_LENGTH; if (!frame_count) return -EINVAL; if (fifo_lvl % BMI323_FIFO_FRAME_LENGTH) dev_warn(data->dev, "Bad FIFO alignment\n"); /* * Approximate timestamps for each of the sample based on the sampling * frequency, timestamp for last sample and number of samples. */ if (data->old_fifo_tstamp) { sample_period = data->fifo_tstamp - data->old_fifo_tstamp; do_div(sample_period, frame_count); } else { sample_period = data->odrns[BMI323_ACCEL]; } tstamp = data->fifo_tstamp - (frame_count - 1) * sample_period; ret = regmap_noinc_read(data->regmap, BMI323_FIFO_DATA_REG, &data->fifo_buff[0], fifo_lvl * BMI323_BYTES_PER_SAMPLE); if (ret) return ret; for (i = 0; i < frame_count; i++) { frame = &data->fifo_buff[i * BMI323_FIFO_FRAME_LENGTH]; pchannels = &data->buffer.channels[0]; index = 0; for_each_set_bit(bit, indio_dev->active_scan_mask, BMI323_CHAN_MAX) pchannels[index++] = frame[bit]; iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, tstamp); tstamp += sample_period; } return frame_count; } static int bmi323_set_watermark(struct iio_dev *indio_dev, unsigned int val) { struct bmi323_data *data = iio_priv(indio_dev); val = min(val, (u32)BMI323_FIFO_FULL_IN_FRAMES); guard(mutex)(&data->mutex); data->watermark = val; return 0; } static int bmi323_fifo_disable(struct bmi323_data *data) { int ret; guard(mutex)(&data->mutex); ret = regmap_write(data->regmap, BMI323_FIFO_CONF_REG, 0); if (ret) return ret; ret = regmap_update_bits(data->regmap, BMI323_INT_MAP2_REG, BMI323_FIFO_WTRMRK_MSK, FIELD_PREP(BMI323_FIFO_WTRMRK_MSK, 0)); if (ret) return ret; data->fifo_tstamp = 0; data->state = BMI323_IDLE; return 0; } static int bmi323_buffer_predisable(struct iio_dev *indio_dev) { struct bmi323_data *data = iio_priv(indio_dev); if (iio_device_get_current_mode(indio_dev) == INDIO_BUFFER_TRIGGERED) return 0; return bmi323_fifo_disable(data); } static int bmi323_update_watermark(struct bmi323_data *data) { int wtrmrk; wtrmrk = data->watermark * BMI323_FIFO_FRAME_LENGTH; return regmap_write(data->regmap, BMI323_FIFO_WTRMRK_REG, wtrmrk); } static int bmi323_fifo_enable(struct bmi323_data *data) { int ret; guard(mutex)(&data->mutex); ret = regmap_update_bits(data->regmap, BMI323_FIFO_CONF_REG, BMI323_FIFO_CONF_ACC_GYR_EN_MSK, FIELD_PREP(BMI323_FIFO_CONF_ACC_GYR_EN_MSK, BMI323_FIFO_ACC_GYR_MSK)); if (ret) return ret; ret = regmap_update_bits(data->regmap, BMI323_INT_MAP2_REG, BMI323_FIFO_WTRMRK_MSK, FIELD_PREP(BMI323_FIFO_WTRMRK_MSK, data->irq_pin)); if (ret) return ret; ret = bmi323_update_watermark(data); if (ret) return ret; ret = regmap_write(data->regmap, BMI323_FIFO_CTRL_REG, BMI323_FIFO_FLUSH_MSK); if (ret) return ret; data->state = BMI323_BUFFER_FIFO; return 0; } static int bmi323_buffer_preenable(struct iio_dev *indio_dev) { struct bmi323_data *data = iio_priv(indio_dev); guard(mutex)(&data->mutex); /* * When the ODR of the accelerometer and gyroscope do not match, the * maximum ODR value between the accelerometer and gyroscope is used * for FIFO and the signal with lower ODR will insert dummy frame. * So allow buffer read only when ODR's of accelero and gyro are equal. * See datasheet section 5.7 "FIFO Data Buffering". */ if (data->odrns[BMI323_ACCEL] != data->odrns[BMI323_GYRO]) { dev_err(data->dev, "Accelero and Gyro ODR doesn't match\n"); return -EINVAL; } return 0; } static int bmi323_buffer_postenable(struct iio_dev *indio_dev) { struct bmi323_data *data = iio_priv(indio_dev); if (iio_device_get_current_mode(indio_dev) == INDIO_BUFFER_TRIGGERED) return 0; return bmi323_fifo_enable(data); } static ssize_t hwfifo_watermark_show(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct bmi323_data *data = iio_priv(indio_dev); int wm; scoped_guard(mutex, &data->mutex) wm = data->watermark; return sysfs_emit(buf, "%d\n", wm); } static IIO_DEVICE_ATTR_RO(hwfifo_watermark, 0); static ssize_t hwfifo_enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct bmi323_data *data = iio_priv(indio_dev); bool state; scoped_guard(mutex, &data->mutex) state = data->state == BMI323_BUFFER_FIFO; return sysfs_emit(buf, "%d\n", state); } static IIO_DEVICE_ATTR_RO(hwfifo_enabled, 0); static const struct iio_dev_attr *bmi323_fifo_attributes[] = { &iio_dev_attr_hwfifo_watermark, &iio_dev_attr_hwfifo_enabled, NULL }; static const struct iio_buffer_setup_ops bmi323_buffer_ops = { .preenable = bmi323_buffer_preenable, .postenable = bmi323_buffer_postenable, .predisable = bmi323_buffer_predisable, }; static irqreturn_t bmi323_irq_thread_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct bmi323_data *data = iio_priv(indio_dev); unsigned int status_addr, status, feature_event; s64 timestamp = iio_get_time_ns(indio_dev); int ret; if (data->irq_pin == BMI323_IRQ_INT1) status_addr = BMI323_STATUS_INT1_REG; else status_addr = BMI323_STATUS_INT2_REG; scoped_guard(mutex, &data->mutex) { ret = regmap_read(data->regmap, status_addr, &status); if (ret) return IRQ_NONE; } if (!status || FIELD_GET(BMI323_STATUS_ERROR_MSK, status)) return IRQ_NONE; if (FIELD_GET(BMI323_STATUS_FIFO_WTRMRK_MSK, status)) { data->old_fifo_tstamp = data->fifo_tstamp; data->fifo_tstamp = iio_get_time_ns(indio_dev); ret = __bmi323_fifo_flush(indio_dev); if (ret < 0) return IRQ_NONE; } if (FIELD_GET(BMI323_STATUS_ACC_GYR_DRDY_MSK, status)) iio_trigger_poll_nested(data->trig); if (FIELD_GET(BMI323_STATUS_MOTION_MSK, status)) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, IIO_EV_TYPE_MAG, IIO_EV_DIR_RISING), timestamp); if (FIELD_GET(BMI323_STATUS_NOMOTION_MSK, status)) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, IIO_EV_TYPE_MAG, IIO_EV_DIR_FALLING), timestamp); if (FIELD_GET(BMI323_STATUS_STP_WTR_MSK, status)) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD, IIO_EV_TYPE_CHANGE, IIO_EV_DIR_NONE), timestamp); if (FIELD_GET(BMI323_STATUS_TAP_MSK, status)) { scoped_guard(mutex, &data->mutex) { ret = regmap_read(data->regmap, BMI323_FEAT_EVNT_EXT_REG, &feature_event); if (ret) return IRQ_NONE; } if (FIELD_GET(BMI323_FEAT_EVNT_EXT_S_MSK, feature_event)) { iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, IIO_EV_TYPE_GESTURE, IIO_EV_DIR_SINGLETAP), timestamp); } if (FIELD_GET(BMI323_FEAT_EVNT_EXT_D_MSK, feature_event)) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, IIO_EV_TYPE_GESTURE, IIO_EV_DIR_DOUBLETAP), timestamp); } return IRQ_HANDLED; } static int bmi323_set_drdy_irq(struct bmi323_data *data, enum bmi323_irq_pin irq_pin) { int ret; ret = regmap_update_bits(data->regmap, BMI323_INT_MAP2_REG, BMI323_GYR_DRDY_MSK, FIELD_PREP(BMI323_GYR_DRDY_MSK, irq_pin)); if (ret) return ret; return regmap_update_bits(data->regmap, BMI323_INT_MAP2_REG, BMI323_ACC_DRDY_MSK, FIELD_PREP(BMI323_ACC_DRDY_MSK, irq_pin)); } static int bmi323_data_rdy_trigger_set_state(struct iio_trigger *trig, bool state) { struct bmi323_data *data = iio_trigger_get_drvdata(trig); enum bmi323_irq_pin irq_pin; guard(mutex)(&data->mutex); if (data->state == BMI323_BUFFER_FIFO) { dev_warn(data->dev, "Can't set trigger when FIFO enabled\n"); return -EBUSY; } if (state) { data->state = BMI323_BUFFER_DRDY_TRIGGERED; irq_pin = data->irq_pin; } else { data->state = BMI323_IDLE; irq_pin = BMI323_IRQ_DISABLED; } return bmi323_set_drdy_irq(data, irq_pin); } static const struct iio_trigger_ops bmi323_trigger_ops = { .set_trigger_state = &bmi323_data_rdy_trigger_set_state, }; static irqreturn_t bmi323_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bmi323_data *data = iio_priv(indio_dev); int ret, bit, index = 0; /* Lock to protect the data->buffer */ guard(mutex)(&data->mutex); if (*indio_dev->active_scan_mask == BMI323_ALL_CHAN_MSK) { ret = regmap_bulk_read(data->regmap, BMI323_ACCEL_X_REG, &data->buffer.channels, ARRAY_SIZE(data->buffer.channels)); if (ret) return IRQ_NONE; } else { for_each_set_bit(bit, indio_dev->active_scan_mask, BMI323_CHAN_MAX) { ret = regmap_raw_read(data->regmap, BMI323_ACCEL_X_REG + bit, &data->buffer.channels[index++], BMI323_BYTES_PER_SAMPLE); if (ret) return IRQ_NONE; } } iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; } static int bmi323_set_average(struct bmi323_data *data, enum bmi323_sensor_type sensor, int avg) { int raw = ARRAY_SIZE(bmi323_accel_gyro_avrg); while (raw--) if (avg == bmi323_accel_gyro_avrg[raw]) break; if (raw < 0) return -EINVAL; guard(mutex)(&data->mutex); return regmap_update_bits(data->regmap, bmi323_hw[sensor].config, BMI323_ACC_GYRO_CONF_AVG_MSK, FIELD_PREP(BMI323_ACC_GYRO_CONF_AVG_MSK, raw)); } static int bmi323_get_average(struct bmi323_data *data, enum bmi323_sensor_type sensor, int *avg) { int ret, value, raw; scoped_guard(mutex, &data->mutex) { ret = regmap_read(data->regmap, bmi323_hw[sensor].config, &value); if (ret) return ret; } raw = FIELD_GET(BMI323_ACC_GYRO_CONF_AVG_MSK, value); *avg = bmi323_accel_gyro_avrg[raw]; return IIO_VAL_INT; } static int bmi323_enable_steps(struct bmi323_data *data, int val) { int ret; guard(mutex)(&data->mutex); if (data->odrhz[BMI323_ACCEL] < 200) { dev_err(data->dev, "Invalid accelerometer parameter\n"); return -EINVAL; } ret = bmi323_feature_engine_events(data, BMI323_FEAT_IO0_STP_CNT_MSK, val ? 1 : 0); if (ret) return ret; set_mask_bits(&data->feature_events, BMI323_FEAT_IO0_STP_CNT_MSK, FIELD_PREP(BMI323_FEAT_IO0_STP_CNT_MSK, val ? 1 : 0)); return 0; } static int bmi323_read_steps(struct bmi323_data *data, int *val) { int ret; guard(mutex)(&data->mutex); if (!FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK, data->feature_events)) return -EINVAL; ret = regmap_bulk_read(data->regmap, BMI323_FEAT_IO2_REG, data->steps_count, ARRAY_SIZE(data->steps_count)); if (ret) return ret; *val = get_unaligned_le32(data->steps_count); return IIO_VAL_INT; } static int bmi323_read_axis(struct bmi323_data *data, struct iio_chan_spec const *chan, int *val) { enum bmi323_sensor_type sensor; unsigned int value; u8 addr; int ret; ret = bmi323_get_error_status(data); if (ret) return -EINVAL; sensor = bmi323_iio_to_sensor(chan->type); addr = bmi323_hw[sensor].data + (chan->channel2 - IIO_MOD_X); scoped_guard(mutex, &data->mutex) { ret = regmap_read(data->regmap, addr, &value); if (ret) return ret; } *val = sign_extend32(value, chan->scan_type.realbits - 1); return IIO_VAL_INT; } static int bmi323_get_temp_data(struct bmi323_data *data, int *val) { unsigned int value; int ret; ret = bmi323_get_error_status(data); if (ret) return -EINVAL; scoped_guard(mutex, &data->mutex) { ret = regmap_read(data->regmap, BMI323_TEMP_REG, &value); if (ret) return ret; } *val = sign_extend32(value, 15); return IIO_VAL_INT; } static int bmi323_get_odr(struct bmi323_data *data, enum bmi323_sensor_type sensor, int *odr, int *uodr) { int ret, value, odr_raw; scoped_guard(mutex, &data->mutex) { ret = regmap_read(data->regmap, bmi323_hw[sensor].config, &value); if (ret) return ret; } odr_raw = FIELD_GET(BMI323_ACC_GYRO_CONF_ODR_MSK, value); *odr = bmi323_acc_gyro_odr[odr_raw - 1][0]; *uodr = bmi323_acc_gyro_odr[odr_raw - 1][1]; return IIO_VAL_INT_PLUS_MICRO; } static int bmi323_configure_power_mode(struct bmi323_data *data, enum bmi323_sensor_type sensor, int odr_index) { enum bmi323_opr_mode mode; if (bmi323_acc_gyro_odr[odr_index][0] > 25) mode = ACC_GYRO_MODE_CONTINOUS; else mode = ACC_GYRO_MODE_DUTYCYCLE; return bmi323_set_mode(data, sensor, mode); } static int bmi323_set_odr(struct bmi323_data *data, enum bmi323_sensor_type sensor, int odr, int uodr) { int odr_raw, ret; odr_raw = ARRAY_SIZE(bmi323_acc_gyro_odr); while (odr_raw--) if (odr == bmi323_acc_gyro_odr[odr_raw][0] && uodr == bmi323_acc_gyro_odr[odr_raw][1]) break; if (odr_raw < 0) return -EINVAL; ret = bmi323_configure_power_mode(data, sensor, odr_raw); if (ret) return -EINVAL; guard(mutex)(&data->mutex); data->odrhz[sensor] = bmi323_acc_gyro_odr[odr_raw][0]; data->odrns[sensor] = bmi323_acc_gyro_odrns[odr_raw]; odr_raw++; return regmap_update_bits(data->regmap, bmi323_hw[sensor].config, BMI323_ACC_GYRO_CONF_ODR_MSK, FIELD_PREP(BMI323_ACC_GYRO_CONF_ODR_MSK, odr_raw)); } static int bmi323_get_scale(struct bmi323_data *data, enum bmi323_sensor_type sensor, int *val2) { int ret, value, scale_raw; scoped_guard(mutex, &data->mutex) { ret = regmap_read(data->regmap, bmi323_hw[sensor].config, &value); if (ret) return ret; } scale_raw = FIELD_GET(BMI323_ACC_GYRO_CONF_SCL_MSK, value); *val2 = bmi323_hw[sensor].scale_table[scale_raw][1]; return IIO_VAL_INT_PLUS_MICRO; } static int bmi323_set_scale(struct bmi323_data *data, enum bmi323_sensor_type sensor, int val, int val2) { int scale_raw; scale_raw = bmi323_hw[sensor].scale_table_len; while (scale_raw--) if (val == bmi323_hw[sensor].scale_table[scale_raw][0] && val2 == bmi323_hw[sensor].scale_table[scale_raw][1]) break; if (scale_raw < 0) return -EINVAL; guard(mutex)(&data->mutex); return regmap_update_bits(data->regmap, bmi323_hw[sensor].config, BMI323_ACC_GYRO_CONF_SCL_MSK, FIELD_PREP(BMI323_ACC_GYRO_CONF_SCL_MSK, scale_raw)); } static int bmi323_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, const int **vals, int *type, int *length, long mask) { enum bmi323_sensor_type sensor; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: *type = IIO_VAL_INT_PLUS_MICRO; *vals = (const int *)bmi323_acc_gyro_odr; *length = ARRAY_SIZE(bmi323_acc_gyro_odr) * 2; return IIO_AVAIL_LIST; case IIO_CHAN_INFO_SCALE: sensor = bmi323_iio_to_sensor(chan->type); *type = IIO_VAL_INT_PLUS_MICRO; *vals = (const int *)bmi323_hw[sensor].scale_table; *length = bmi323_hw[sensor].scale_table_len * 2; return IIO_AVAIL_LIST; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: *type = IIO_VAL_INT; *vals = (const int *)bmi323_accel_gyro_avrg; *length = ARRAY_SIZE(bmi323_accel_gyro_avrg); return IIO_AVAIL_LIST; default: return -EINVAL; } } static int bmi323_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct bmi323_data *data = iio_priv(indio_dev); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: iio_device_claim_direct_scoped(return -EBUSY, indio_dev) return bmi323_set_odr(data, bmi323_iio_to_sensor(chan->type), val, val2); unreachable(); case IIO_CHAN_INFO_SCALE: iio_device_claim_direct_scoped(return -EBUSY, indio_dev) return bmi323_set_scale(data, bmi323_iio_to_sensor(chan->type), val, val2); unreachable(); case IIO_CHAN_INFO_OVERSAMPLING_RATIO: iio_device_claim_direct_scoped(return -EBUSY, indio_dev) return bmi323_set_average(data, bmi323_iio_to_sensor(chan->type), val); unreachable(); case IIO_CHAN_INFO_ENABLE: return bmi323_enable_steps(data, val); case IIO_CHAN_INFO_PROCESSED: { guard(mutex)(&data->mutex); if (val || !FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK, data->feature_events)) return -EINVAL; /* Clear step counter value */ return bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG, BMI323_STEP_SC1_RST_CNT_MSK, FIELD_PREP(BMI323_STEP_SC1_RST_CNT_MSK, 1)); } default: return -EINVAL; } } static int bmi323_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct bmi323_data *data = iio_priv(indio_dev); switch (mask) { case IIO_CHAN_INFO_PROCESSED: return bmi323_read_steps(data, val); case IIO_CHAN_INFO_RAW: switch (chan->type) { case IIO_ACCEL: case IIO_ANGL_VEL: iio_device_claim_direct_scoped(return -EBUSY, indio_dev) return bmi323_read_axis(data, chan, val); unreachable(); case IIO_TEMP: return bmi323_get_temp_data(data, val); default: return -EINVAL; } case IIO_CHAN_INFO_SAMP_FREQ: return bmi323_get_odr(data, bmi323_iio_to_sensor(chan->type), val, val2); case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ACCEL: case IIO_ANGL_VEL: *val = 0; return bmi323_get_scale(data, bmi323_iio_to_sensor(chan->type), val2); case IIO_TEMP: *val = BMI323_TEMP_SCALE / MEGA; *val2 = BMI323_TEMP_SCALE % MEGA; return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } case IIO_CHAN_INFO_OVERSAMPLING_RATIO: return bmi323_get_average(data, bmi323_iio_to_sensor(chan->type), val); case IIO_CHAN_INFO_OFFSET: switch (chan->type) { case IIO_TEMP: *val = BMI323_TEMP_OFFSET; return IIO_VAL_INT; default: return -EINVAL; } case IIO_CHAN_INFO_ENABLE: scoped_guard(mutex, &data->mutex) *val = FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK, data->feature_events); return IIO_VAL_INT; default: return -EINVAL; } } static const struct iio_info bmi323_info = { .read_raw = bmi323_read_raw, .write_raw = bmi323_write_raw, .read_avail = bmi323_read_avail, .hwfifo_set_watermark = bmi323_set_watermark, .write_event_config = bmi323_write_event_config, .read_event_config = bmi323_read_event_config, .write_event_value = bmi323_write_event_value, .read_event_value = bmi323_read_event_value, .event_attrs = &bmi323_event_attribute_group, }; #define BMI323_SCAN_MASK_ACCEL_3AXIS \ (BIT(BMI323_ACCEL_X) | BIT(BMI323_ACCEL_Y) | BIT(BMI323_ACCEL_Z)) #define BMI323_SCAN_MASK_GYRO_3AXIS \ (BIT(BMI323_GYRO_X) | BIT(BMI323_GYRO_Y) | BIT(BMI323_GYRO_Z)) static const unsigned long bmi323_avail_scan_masks[] = { /* 3-axis accel */ BMI323_SCAN_MASK_ACCEL_3AXIS, /* 3-axis gyro */ BMI323_SCAN_MASK_GYRO_3AXIS, /* 3-axis accel + 3-axis gyro */ BMI323_SCAN_MASK_ACCEL_3AXIS | BMI323_SCAN_MASK_GYRO_3AXIS, 0 }; static int bmi323_int_pin_config(struct bmi323_data *data, enum bmi323_irq_pin irq_pin, bool active_high, bool open_drain, bool latch) { unsigned int mask, field_value; int ret; ret = regmap_update_bits(data->regmap, BMI323_IO_INT_CONF_REG, BMI323_IO_INT_LTCH_MSK, FIELD_PREP(BMI323_IO_INT_LTCH_MSK, latch)); if (ret) return ret; ret = bmi323_update_ext_reg(data, BMI323_GEN_SET1_REG, BMI323_GEN_HOLD_DUR_MSK, FIELD_PREP(BMI323_GEN_HOLD_DUR_MSK, 0)); if (ret) return ret; switch (irq_pin) { case BMI323_IRQ_INT1: mask = BMI323_IO_INT1_LVL_OD_OP_MSK; field_value = FIELD_PREP(BMI323_IO_INT1_LVL_MSK, active_high) | FIELD_PREP(BMI323_IO_INT1_OD_MSK, open_drain) | FIELD_PREP(BMI323_IO_INT1_OP_EN_MSK, 1); break; case BMI323_IRQ_INT2: mask = BMI323_IO_INT2_LVL_OD_OP_MSK; field_value = FIELD_PREP(BMI323_IO_INT2_LVL_MSK, active_high) | FIELD_PREP(BMI323_IO_INT2_OD_MSK, open_drain) | FIELD_PREP(BMI323_IO_INT2_OP_EN_MSK, 1); break; default: return -EINVAL; } return regmap_update_bits(data->regmap, BMI323_IO_INT_CTR_REG, mask, field_value); } static int bmi323_trigger_probe(struct bmi323_data *data, struct iio_dev *indio_dev) { bool open_drain, active_high, latch; struct fwnode_handle *fwnode; enum bmi323_irq_pin irq_pin; int ret, irq, irq_type; struct irq_data *desc; fwnode = dev_fwnode(data->dev); if (!fwnode) return -ENODEV; irq = fwnode_irq_get_byname(fwnode, "INT1"); if (irq > 0) { irq_pin = BMI323_IRQ_INT1; } else { irq = fwnode_irq_get_byname(fwnode, "INT2"); if (irq < 0) return 0; irq_pin = BMI323_IRQ_INT2; } desc = irq_get_irq_data(irq); if (!desc) return dev_err_probe(data->dev, -EINVAL, "Could not find IRQ %d\n", irq); irq_type = irqd_get_trigger_type(desc); switch (irq_type) { case IRQF_TRIGGER_RISING: latch = false; active_high = true; break; case IRQF_TRIGGER_HIGH: latch = true; active_high = true; break; case IRQF_TRIGGER_FALLING: latch = false; active_high = false; break; case IRQF_TRIGGER_LOW: latch = true; active_high = false; break; default: return dev_err_probe(data->dev, -EINVAL, "Invalid interrupt type 0x%x specified\n", irq_type); } open_drain = fwnode_property_read_bool(fwnode, "drive-open-drain"); ret = bmi323_int_pin_config(data, irq_pin, active_high, open_drain, latch); if (ret) return dev_err_probe(data->dev, ret, "Failed to configure irq line\n"); data->trig = devm_iio_trigger_alloc(data->dev, "%s-trig-%d", indio_dev->name, irq_pin); if (!data->trig) return -ENOMEM; data->trig->ops = &bmi323_trigger_ops; iio_trigger_set_drvdata(data->trig, data); ret = devm_request_threaded_irq(data->dev, irq, NULL, bmi323_irq_thread_handler, IRQF_ONESHOT, "bmi323-int", indio_dev); if (ret) return dev_err_probe(data->dev, ret, "Failed to request IRQ\n"); ret = devm_iio_trigger_register(data->dev, data->trig); if (ret) return dev_err_probe(data->dev, ret, "Trigger registration failed\n"); data->irq_pin = irq_pin; return 0; } static int bmi323_feature_engine_enable(struct bmi323_data *data, bool en) { unsigned int feature_status; int ret; if (!en) return regmap_write(data->regmap, BMI323_FEAT_CTRL_REG, 0); ret = regmap_write(data->regmap, BMI323_FEAT_IO2_REG, 0x012c); if (ret) return ret; ret = regmap_write(data->regmap, BMI323_FEAT_IO_STATUS_REG, BMI323_FEAT_IO_STATUS_MSK); if (ret) return ret; ret = regmap_write(data->regmap, BMI323_FEAT_CTRL_REG, BMI323_FEAT_ENG_EN_MSK); if (ret) return ret; /* * It takes around 4 msec to enable the Feature engine, so check * the status of the feature engine every 2 msec for a maximum * of 5 trials. */ ret = regmap_read_poll_timeout(data->regmap, BMI323_FEAT_IO1_REG, feature_status, FIELD_GET(BMI323_FEAT_IO1_ERR_MSK, feature_status) == 1, BMI323_FEAT_ENG_POLL, BMI323_FEAT_ENG_TIMEOUT); if (ret) return dev_err_probe(data->dev, -EINVAL, "Failed to enable feature engine\n"); return 0; } static void bmi323_disable(void *data_ptr) { struct bmi323_data *data = data_ptr; bmi323_set_mode(data, BMI323_ACCEL, ACC_GYRO_MODE_DISABLE); bmi323_set_mode(data, BMI323_GYRO, ACC_GYRO_MODE_DISABLE); } static int bmi323_set_bw(struct bmi323_data *data, enum bmi323_sensor_type sensor, enum bmi323_3db_bw bw) { return regmap_update_bits(data->regmap, bmi323_hw[sensor].config, BMI323_ACC_GYRO_CONF_BW_MSK, FIELD_PREP(BMI323_ACC_GYRO_CONF_BW_MSK, bw)); } static int bmi323_init(struct bmi323_data *data) { int ret, val; /* * Perform soft reset to make sure the device is in a known state after * start up. A delay of 1.5 ms is required after reset. * See datasheet section 5.17 "Soft Reset". */ ret = regmap_write(data->regmap, BMI323_CMD_REG, BMI323_RST_VAL); if (ret) return ret; usleep_range(1500, 2000); /* * Dummy read is required to enable SPI interface after reset. * See datasheet section 7.2.1 "Protocol Selection". */ regmap_read(data->regmap, BMI323_CHIP_ID_REG, &val); ret = regmap_read(data->regmap, BMI323_STATUS_REG, &val); if (ret) return ret; if (!FIELD_GET(BMI323_STATUS_POR_MSK, val)) return dev_err_probe(data->dev, -EINVAL, "Sensor initialization error\n"); ret = regmap_read(data->regmap, BMI323_CHIP_ID_REG, &val); if (ret) return ret; if (FIELD_GET(BMI323_CHIP_ID_MSK, val) != BMI323_CHIP_ID_VAL) return dev_err_probe(data->dev, -EINVAL, "Chip ID mismatch\n"); ret = bmi323_feature_engine_enable(data, true); if (ret) return ret; ret = regmap_read(data->regmap, BMI323_ERR_REG, &val); if (ret) return ret; if (val) return dev_err_probe(data->dev, -EINVAL, "Sensor power error = 0x%x\n", val); /* * Set the Bandwidth coefficient which defines the 3 dB cutoff * frequency in relation to the ODR. */ ret = bmi323_set_bw(data, BMI323_ACCEL, BMI323_BW_ODR_BY_2); if (ret) return ret; ret = bmi323_set_bw(data, BMI323_GYRO, BMI323_BW_ODR_BY_2); if (ret) return ret; ret = bmi323_set_odr(data, BMI323_ACCEL, 25, 0); if (ret) return ret; ret = bmi323_set_odr(data, BMI323_GYRO, 25, 0); if (ret) return ret; return devm_add_action_or_reset(data->dev, bmi323_disable, data); } int bmi323_core_probe(struct device *dev) { static const char * const regulator_names[] = { "vdd", "vddio" }; struct iio_dev *indio_dev; struct bmi323_data *data; struct regmap *regmap; int ret; regmap = dev_get_regmap(dev, NULL); if (!regmap) return dev_err_probe(dev, -ENODEV, "Failed to get regmap\n"); indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return dev_err_probe(dev, -ENOMEM, "Failed to allocate device\n"); ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulator_names), regulator_names); if (ret) return dev_err_probe(dev, ret, "Failed to enable regulators\n"); data = iio_priv(indio_dev); data->dev = dev; data->regmap = regmap; mutex_init(&data->mutex); ret = bmi323_init(data); if (ret) return -EINVAL; ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; indio_dev->name = "bmi323-imu"; indio_dev->info = &bmi323_info; indio_dev->channels = bmi323_channels; indio_dev->num_channels = ARRAY_SIZE(bmi323_channels); indio_dev->available_scan_masks = bmi323_avail_scan_masks; indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; dev_set_drvdata(data->dev, indio_dev); ret = bmi323_trigger_probe(data, indio_dev); if (ret) return -EINVAL; ret = devm_iio_triggered_buffer_setup_ext(data->dev, indio_dev, &iio_pollfunc_store_time, bmi323_trigger_handler, IIO_BUFFER_DIRECTION_IN, &bmi323_buffer_ops, bmi323_fifo_attributes); if (ret) return dev_err_probe(data->dev, ret, "Failed to setup trigger buffer\n"); ret = devm_iio_device_register(data->dev, indio_dev); if (ret) return dev_err_probe(data->dev, ret, "Unable to register iio device\n"); return 0; } EXPORT_SYMBOL_NS_GPL(bmi323_core_probe, IIO_BMI323); MODULE_DESCRIPTION("Bosch BMI323 IMU driver"); MODULE_AUTHOR("Jagath Jog J "); MODULE_LICENSE("GPL");