diff options
Diffstat (limited to 'drivers/iio/imu/bmi323')
-rw-r--r-- | drivers/iio/imu/bmi323/bmi323.h | 2 | ||||
-rw-r--r-- | drivers/iio/imu/bmi323/bmi323_core.c | 292 | ||||
-rw-r--r-- | drivers/iio/imu/bmi323/bmi323_i2c.c | 26 | ||||
-rw-r--r-- | drivers/iio/imu/bmi323/bmi323_spi.c | 5 |
4 files changed, 254 insertions, 71 deletions
diff --git a/drivers/iio/imu/bmi323/bmi323.h b/drivers/iio/imu/bmi323/bmi323.h index dff126d41658..b4cfe92600a4 100644 --- a/drivers/iio/imu/bmi323/bmi323.h +++ b/drivers/iio/imu/bmi323/bmi323.h @@ -141,7 +141,6 @@ #define BMI323_STEP_SC1_REG 0x10 #define BMI323_STEP_SC1_WTRMRK_MSK GENMASK(9, 0) #define BMI323_STEP_SC1_RST_CNT_MSK BIT(10) -#define BMI323_STEP_SC1_REG 0x10 #define BMI323_STEP_LEN 2 /* Tap gesture config registers */ @@ -205,5 +204,6 @@ struct device; int bmi323_core_probe(struct device *dev); extern const struct regmap_config bmi323_regmap_config; +extern const struct dev_pm_ops bmi323_core_pm_ops; #endif diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c index 183af482828f..7f386c5e58b4 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -19,7 +19,7 @@ #include <linux/regulator/consumer.h> #include <linux/units.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/iio/buffer.h> #include <linux/iio/events.h> @@ -118,6 +118,38 @@ static const struct bmi323_hw bmi323_hw[2] = { }, }; +static const unsigned int bmi323_reg_savestate[] = { + BMI323_INT_MAP1_REG, + BMI323_INT_MAP2_REG, + BMI323_IO_INT_CTR_REG, + BMI323_IO_INT_CONF_REG, + BMI323_ACC_CONF_REG, + BMI323_GYRO_CONF_REG, + BMI323_FEAT_IO0_REG, + BMI323_FIFO_WTRMRK_REG, + BMI323_FIFO_CONF_REG +}; + +static const unsigned int bmi323_ext_reg_savestate[] = { + BMI323_GEN_SET1_REG, + BMI323_TAP1_REG, + BMI323_TAP2_REG, + BMI323_TAP3_REG, + BMI323_FEAT_IO0_S_TAP_MSK, + BMI323_STEP_SC1_REG, + BMI323_ANYMO1_REG, + BMI323_NOMO1_REG, + BMI323_ANYMO1_REG + BMI323_MO2_OFFSET, + BMI323_NOMO1_REG + BMI323_MO2_OFFSET, + BMI323_ANYMO1_REG + BMI323_MO3_OFFSET, + BMI323_NOMO1_REG + BMI323_MO3_OFFSET +}; + +struct bmi323_regs_runtime_pm { + unsigned int reg_settings[ARRAY_SIZE(bmi323_reg_savestate)]; + unsigned int ext_reg_settings[ARRAY_SIZE(bmi323_ext_reg_savestate)]; +}; + struct bmi323_data { struct device *dev; struct regmap *regmap; @@ -130,6 +162,7 @@ struct bmi323_data { u32 odrns[BMI323_SENSORS_CNT]; u32 odrhz[BMI323_SENSORS_CNT]; unsigned int feature_events; + struct bmi323_regs_runtime_pm runtime_pm_status; /* * Lock to protect the members of device's private data from concurrent @@ -141,7 +174,7 @@ struct bmi323_data { __le16 fifo_buff[BMI323_FIFO_FULL_IN_WORDS] __aligned(IIO_DMA_MINALIGN); struct { __le16 channels[BMI323_CHAN_MAX]; - s64 ts __aligned(8); + aligned_s64 ts; } buffer; __le16 steps_count[BMI323_STEP_LEN]; }; @@ -434,7 +467,7 @@ static int bmi323_feature_engine_events(struct bmi323_data *data, BMI323_FEAT_IO_STATUS_MSK); } -static int bmi323_step_wtrmrk_en(struct bmi323_data *data, int state) +static int bmi323_step_wtrmrk_en(struct bmi323_data *data, bool state) { enum bmi323_irq_pin step_irq; int ret; @@ -451,7 +484,7 @@ static int bmi323_step_wtrmrk_en(struct bmi323_data *data, int state) 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)); + state)); if (ret) return ret; @@ -473,7 +506,7 @@ static int bmi323_motion_config_reg(enum iio_event_direction dir) } static int bmi323_motion_event_en(struct bmi323_data *data, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { unsigned int state_value = state ? BMI323_FEAT_XYZ_MSK : 0; int config, ret, msk, raw, field_value; @@ -537,7 +570,7 @@ static int bmi323_motion_event_en(struct bmi323_data *data, } static int bmi323_tap_event_en(struct bmi323_data *data, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { enum bmi323_irq_pin tap_irq; int ret, tap_enabled; @@ -752,7 +785,7 @@ static const struct attribute_group bmi323_event_attribute_group = { 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) + enum iio_event_direction dir, bool state) { struct bmi323_data *data = iio_priv(indio_dev); @@ -1391,7 +1424,7 @@ static irqreturn_t bmi323_trigger_handler(int irq, void *p) &data->buffer.channels, ARRAY_SIZE(data->buffer.channels)); if (ret) - return IRQ_NONE; + goto out; } else { for_each_set_bit(bit, indio_dev->active_scan_mask, BMI323_CHAN_MAX) { @@ -1400,13 +1433,14 @@ static irqreturn_t bmi323_trigger_handler(int irq, void *p) &data->buffer.channels[index++], BMI323_BYTES_PER_SAMPLE); if (ret) - return IRQ_NONE; + goto out; } } iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, iio_get_time_ns(indio_dev)); +out: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; @@ -1668,52 +1702,41 @@ static int bmi323_write_raw(struct iio_dev *indio_dev, int val2, long mask) { struct bmi323_data *data = iio_priv(indio_dev); - int ret; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - ret = bmi323_set_odr(data, bmi323_iio_to_sensor(chan->type), - val, val2); - iio_device_release_direct_mode(indio_dev); - return ret; + 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: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - ret = bmi323_set_scale(data, bmi323_iio_to_sensor(chan->type), - val, val2); - iio_device_release_direct_mode(indio_dev); - return ret; + 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: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - ret = bmi323_set_average(data, bmi323_iio_to_sensor(chan->type), - val); - - iio_device_release_direct_mode(indio_dev); - return ret; + 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: - scoped_guard(mutex, &data->mutex) { - if (val || !FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK, - data->feature_events)) - return -EINVAL; + case IIO_CHAN_INFO_PROCESSED: { + guard(mutex)(&data->mutex); - /* Clear step counter value */ - ret = bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG, - BMI323_STEP_SC1_RST_CNT_MSK, - FIELD_PREP(BMI323_STEP_SC1_RST_CNT_MSK, - 1)); - } - return ret; + 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; } @@ -1724,7 +1747,6 @@ static int bmi323_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct bmi323_data *data = iio_priv(indio_dev); - int ret; switch (mask) { case IIO_CHAN_INFO_PROCESSED: @@ -1733,14 +1755,10 @@ static int bmi323_read_raw(struct iio_dev *indio_dev, switch (chan->type) { case IIO_ACCEL: case IIO_ANGL_VEL: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - ret = bmi323_read_axis(data, chan, val); - - iio_device_release_direct_mode(indio_dev); - return ret; + 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: @@ -1863,7 +1881,6 @@ static int bmi323_trigger_probe(struct bmi323_data *data, 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) @@ -1880,12 +1897,7 @@ static int bmi323_trigger_probe(struct bmi323_data *data, 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); + irq_type = irq_get_trigger_type(irq); switch (irq_type) { case IRQF_TRIGGER_RISING: latch = false; @@ -1987,6 +1999,11 @@ static void bmi323_disable(void *data_ptr) bmi323_set_mode(data, BMI323_ACCEL, ACC_GYRO_MODE_DISABLE); bmi323_set_mode(data, BMI323_GYRO, ACC_GYRO_MODE_DISABLE); + + /* + * Place the peripheral in its lowest power consuming state. + */ + regmap_write(data->regmap, BMI323_CMD_REG, BMI323_RST_VAL); } static int bmi323_set_bw(struct bmi323_data *data, @@ -2045,6 +2062,13 @@ static int bmi323_init(struct bmi323_data *data) return dev_err_probe(data->dev, -EINVAL, "Sensor power error = 0x%x\n", val); + return 0; +} + +static int bmi323_init_reset(struct bmi323_data *data) +{ + int ret; + /* * Set the Bandwidth coefficient which defines the 3 dB cutoff * frequency in relation to the ODR. @@ -2093,15 +2117,23 @@ int bmi323_core_probe(struct device *dev) data = iio_priv(indio_dev); data->dev = dev; data->regmap = regmap; + data->irq_pin = BMI323_IRQ_DISABLED; + data->state = BMI323_IDLE; mutex_init(&data->mutex); ret = bmi323_init(data); if (ret) return -EINVAL; - ret = iio_read_mount_matrix(dev, &data->orientation); + ret = bmi323_init_reset(data); if (ret) - return ret; + return -EINVAL; + + if (!iio_read_acpi_mount_matrix(dev, &data->orientation, "ROTM")) { + ret = iio_read_mount_matrix(dev, &data->orientation); + if (ret) + return ret; + } indio_dev->name = "bmi323-imu"; indio_dev->info = &bmi323_info; @@ -2130,9 +2162,137 @@ int bmi323_core_probe(struct device *dev) return dev_err_probe(data->dev, ret, "Unable to register iio device\n"); + return bmi323_fifo_disable(data); +} +EXPORT_SYMBOL_NS_GPL(bmi323_core_probe, "IIO_BMI323"); + +static int bmi323_core_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi323_data *data = iio_priv(indio_dev); + struct bmi323_regs_runtime_pm *savestate = &data->runtime_pm_status; + int ret; + + guard(mutex)(&data->mutex); + + ret = iio_device_suspend_triggering(indio_dev); + if (ret) + return ret; + + /* Save registers meant to be restored by resume pm callback. */ + for (unsigned int i = 0; i < ARRAY_SIZE(bmi323_reg_savestate); i++) { + ret = regmap_read(data->regmap, bmi323_reg_savestate[i], + &savestate->reg_settings[i]); + if (ret) { + dev_err(data->dev, + "Error reading bmi323 reg 0x%x: %d\n", + bmi323_reg_savestate[i], ret); + return ret; + } + } + + for (unsigned int i = 0; i < ARRAY_SIZE(bmi323_ext_reg_savestate); i++) { + ret = bmi323_read_ext_reg(data, bmi323_ext_reg_savestate[i], + &savestate->ext_reg_settings[i]); + if (ret) { + dev_err(data->dev, + "Error reading bmi323 external reg 0x%x: %d\n", + bmi323_ext_reg_savestate[i], ret); + return ret; + } + } + + /* Perform soft reset to place the device in its lowest power state. */ + ret = regmap_write(data->regmap, BMI323_CMD_REG, BMI323_RST_VAL); + if (ret) + return ret; + return 0; } -EXPORT_SYMBOL_NS_GPL(bmi323_core_probe, IIO_BMI323); + +static int bmi323_core_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi323_data *data = iio_priv(indio_dev); + struct bmi323_regs_runtime_pm *savestate = &data->runtime_pm_status; + unsigned int val; + int ret; + + guard(mutex)(&data->mutex); + + /* + * Perform the device power-on and initial setup once again + * after being reset in the lower power state by runtime-pm. + */ + ret = bmi323_init(data); + if (ret) { + dev_err(data->dev, "Device power-on and init failed: %d", ret); + return ret; + } + + /* Register must be cleared before changing an active config */ + ret = regmap_write(data->regmap, BMI323_FEAT_IO0_REG, 0); + if (ret) { + dev_err(data->dev, "Error stopping feature engine\n"); + return ret; + } + + for (unsigned int i = 0; i < ARRAY_SIZE(bmi323_ext_reg_savestate); i++) { + ret = bmi323_write_ext_reg(data, bmi323_ext_reg_savestate[i], + savestate->ext_reg_settings[i]); + if (ret) { + dev_err(data->dev, + "Error writing bmi323 external reg 0x%x: %d\n", + bmi323_ext_reg_savestate[i], ret); + return ret; + } + } + + for (unsigned int i = 0; i < ARRAY_SIZE(bmi323_reg_savestate); i++) { + ret = regmap_write(data->regmap, bmi323_reg_savestate[i], + savestate->reg_settings[i]); + if (ret) { + dev_err(data->dev, + "Error writing bmi323 reg 0x%x: %d\n", + bmi323_reg_savestate[i], ret); + return ret; + } + } + + /* + * Clear old FIFO samples that might be generated before suspend + * or generated from a peripheral state not equal to the saved one. + */ + if (data->state == BMI323_BUFFER_FIFO) { + ret = regmap_write(data->regmap, BMI323_FIFO_CTRL_REG, + BMI323_FIFO_FLUSH_MSK); + if (ret) { + dev_err(data->dev, "Error flushing FIFO buffer: %d\n", ret); + return ret; + } + } + + ret = regmap_read(data->regmap, BMI323_ERR_REG, &val); + if (ret) { + dev_err(data->dev, + "Error reading bmi323 error register: %d\n", ret); + return ret; + } + + if (val) { + dev_err(data->dev, + "Sensor power error in PM = 0x%x\n", val); + return -EINVAL; + } + + return iio_device_resume_triggering(indio_dev); +} + +const struct dev_pm_ops bmi323_core_pm_ops = { + RUNTIME_PM_OPS(bmi323_core_runtime_suspend, + bmi323_core_runtime_resume, NULL) +}; +EXPORT_SYMBOL_NS_GPL(bmi323_core_pm_ops, "IIO_BMI323"); MODULE_DESCRIPTION("Bosch BMI323 IMU driver"); MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>"); diff --git a/drivers/iio/imu/bmi323/bmi323_i2c.c b/drivers/iio/imu/bmi323/bmi323_i2c.c index 20a8001b9956..8457fe304db8 100644 --- a/drivers/iio/imu/bmi323/bmi323_i2c.c +++ b/drivers/iio/imu/bmi323/bmi323_i2c.c @@ -61,7 +61,7 @@ static int bmi323_regmap_i2c_write(void *context, const void *data, data + sizeof(u8)); } -static struct regmap_bus bmi323_regmap_bus = { +static const struct regmap_bus bmi323_regmap_bus = { .read = bmi323_regmap_i2c_read, .write = bmi323_regmap_i2c_write, }; @@ -93,6 +93,26 @@ static int bmi323_i2c_probe(struct i2c_client *i2c) return bmi323_core_probe(dev); } +static const struct acpi_device_id bmi323_acpi_match[] = { + /* + * The "BOSC0200" identifier used here is not unique to bmi323 devices. + * The same "BOSC0200" identifier is found in the ACPI tables of devices + * using the bmc150 chip. This creates a conflict with duplicate ACPI + * identifiers which multiple drivers want to use. If a non-bmi323 + * device starts to load with this "BOSC0200" ACPI match here, then the + * chip ID check portion should fail because the chip IDs received (via + * i2c) are unique between bmc150 and bmi323 and the driver should + * relinquish the device. If and when a different driver (such as + * bmc150) starts to load with the "BOSC0200" ACPI match, a short reset + * should ensure that the device is not in a bad state during that + * driver initialization. This device reset does occur in both the + * bmi323 and bmc150 init sequences. + */ + { "BOSC0200" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, bmi323_acpi_match); + static const struct i2c_device_id bmi323_i2c_ids[] = { { "bmi323" }, { } @@ -108,7 +128,9 @@ MODULE_DEVICE_TABLE(of, bmi323_of_i2c_match); static struct i2c_driver bmi323_i2c_driver = { .driver = { .name = "bmi323", + .pm = pm_ptr(&bmi323_core_pm_ops), .of_match_table = bmi323_of_i2c_match, + .acpi_match_table = bmi323_acpi_match, }, .probe = bmi323_i2c_probe, .id_table = bmi323_i2c_ids, @@ -118,4 +140,4 @@ module_i2c_driver(bmi323_i2c_driver); MODULE_DESCRIPTION("Bosch BMI323 IMU driver"); MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(IIO_BMI323); +MODULE_IMPORT_NS("IIO_BMI323"); diff --git a/drivers/iio/imu/bmi323/bmi323_spi.c b/drivers/iio/imu/bmi323/bmi323_spi.c index 7b1e8127d0dd..fd56ab620750 100644 --- a/drivers/iio/imu/bmi323/bmi323_spi.c +++ b/drivers/iio/imu/bmi323/bmi323_spi.c @@ -36,7 +36,7 @@ static int bmi323_regmap_spi_write(void *context, const void *data, return spi_write(spi, data_buff + 1, count - 1); } -static struct regmap_bus bmi323_regmap_bus = { +static const struct regmap_bus bmi323_regmap_bus = { .read = bmi323_regmap_spi_read, .write = bmi323_regmap_spi_write, }; @@ -79,6 +79,7 @@ MODULE_DEVICE_TABLE(of, bmi323_of_spi_match); static struct spi_driver bmi323_spi_driver = { .driver = { .name = "bmi323", + .pm = pm_ptr(&bmi323_core_pm_ops), .of_match_table = bmi323_of_spi_match, }, .probe = bmi323_spi_probe, @@ -89,4 +90,4 @@ module_spi_driver(bmi323_spi_driver); MODULE_DESCRIPTION("Bosch BMI323 IMU driver"); MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(IIO_BMI323); +MODULE_IMPORT_NS("IIO_BMI323"); |