diff --git a/drivers/sensor/adi/adxl345/adxl345.c b/drivers/sensor/adi/adxl345/adxl345.c index 4ead4f5867b2d..c9b5ef2290c05 100644 --- a/drivers/sensor/adi/adxl345/adxl345.c +++ b/drivers/sensor/adi/adxl345/adxl345.c @@ -17,6 +17,12 @@ LOG_MODULE_REGISTER(ADXL345, CONFIG_SENSOR_LOG_LEVEL); +static const uint8_t adxl345_fifo_ctl_trigger_init[] = { + [ADXL345_FIFO_CTL_TRIGGER_INT1] = ADXL345_INT1, + [ADXL345_FIFO_CTL_TRIGGER_INT2] = ADXL345_INT2, + [ADXL345_FIFO_CTL_TRIGGER_UNSET] = ADXL345_INT_UNSET, +}; + #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) static bool adxl345_bus_is_ready_i2c(const union adxl345_bus *bus) { @@ -81,43 +87,138 @@ int adxl345_reg_write(const struct device *dev, uint8_t addr, uint8_t *data, return adxl345_reg_access(dev, ADXL345_WRITE_CMD, addr, data, len); } -int adxl345_reg_read(const struct device *dev, uint8_t addr, uint8_t *data, - uint8_t len) +int adxl345_raw_reg_read(const struct device *dev, uint8_t addr, + uint8_t *data, uint8_t len) { return adxl345_reg_access(dev, ADXL345_READ_CMD, addr, data, len); } -int adxl345_reg_write_byte(const struct device *dev, uint8_t addr, uint8_t val) +int adxl345_reg_read_byte(const struct device *dev, uint8_t addr, uint8_t *buf) + { - return adxl345_reg_write(dev, addr, &val, 1); -} + struct adxl345_dev_data *data = dev->data; -int adxl345_reg_read_byte(const struct device *dev, uint8_t addr, uint8_t *buf) + /* caching for particular config registers */ + switch (addr) { + case ADXL345_POWER_CTL_REG: + *buf = data->cache_reg_power_ctl; + return 0; + case ADXL345_INT_ENABLE_REG: + *buf = data->cache_reg_int_enable; + return 0; + case ADXL345_INT_MAP_REG: + *buf = data->cache_reg_int_map; + return 0; + case ADXL345_DATA_FORMAT_REG: + *buf = data->cache_reg_data_format; + return 0; + case ADXL345_RATE_REG: + *buf = data->cache_reg_rate; + return 0; + case ADXL345_FIFO_CTL_REG: + *buf = data->cache_reg_fifo_ctl; + return 0; + case ADXL345_THRESH_ACT_REG: + *buf = data->cache_reg_act_thresh; + return 0; + default: +#if defined(CONFIG_ADXL345_STREAM) + LOG_DBG("Fall through reading 0x%02x on RTIO might need a %s", + addr, "callback for evaluation!"); + return adxl345_rtio_reg_read(dev, addr, buf, 1, NULL, NULL); +#else + return adxl345_raw_reg_read(dev, addr, buf, 1); +#endif + } +} +int adxl345_reg_write_byte(const struct device *dev, uint8_t addr, uint8_t val) { - return adxl345_reg_read(dev, addr, buf, 1); + struct adxl345_dev_data *data = dev->data; + + /* caching for particular config registers */ + switch (addr) { + case ADXL345_POWER_CTL_REG: + data->cache_reg_power_ctl = val; + break; + case ADXL345_INT_ENABLE_REG: + data->cache_reg_int_enable = val; + break; + case ADXL345_INT_MAP_REG: + data->cache_reg_int_map = val; + break; + case ADXL345_DATA_FORMAT_REG: + data->cache_reg_data_format = val; + break; + case ADXL345_RATE_REG: + data->cache_reg_rate = val; + break; + case ADXL345_FIFO_CTL_REG: + data->cache_reg_fifo_ctl = val; + break; + case ADXL345_THRESH_ACT_REG: + data->cache_reg_act_thresh = val; + break; + default: + break; + } + +#if defined(CONFIG_ADXL345_STREAM) + const struct adxl345_dev_config *cfg = dev->config; + struct rtio_sqe *write_reg_sqe = rtio_sqe_acquire(data->rtio_ctx); + + rtio_sqe_prep_tiny_write(write_reg_sqe, data->iodev, RTIO_PRIO_NORM, + &addr, sizeof(addr), NULL); + write_reg_sqe->flags |= RTIO_SQE_TRANSACTION; + + /* + * Use a tiny write for register and an additional write for register + * content. It's not possible to pass two byte directly as tiny write, + * since the sqe buffer was defined holding just one byte. + */ + struct rtio_sqe *write_buf_sqe = rtio_sqe_acquire(data->rtio_ctx); + + rtio_sqe_prep_tiny_write(write_buf_sqe, data->iodev, RTIO_PRIO_NORM, + &val, sizeof(val), NULL); + + if (cfg->bus_type == ADXL345_BUS_I2C) { + write_buf_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + } + + rtio_submit(data->rtio_ctx, 2); /* keep control commands blocking */ + + return 0; +#else + return adxl345_reg_write(dev, addr, &val, 1); +#endif } int adxl345_reg_write_mask(const struct device *dev, - uint8_t reg_addr, - uint8_t mask, - uint8_t data) + uint8_t reg_addr, + uint8_t mask, + uint8_t data) { + uint8_t regval, tmp; int ret; - uint8_t tmp; - ret = adxl345_reg_read_byte(dev, reg_addr, &tmp); + ret = adxl345_reg_read_byte(dev, reg_addr, ®val); if (ret) { return ret; } - tmp &= ~mask; - tmp |= data; + tmp = regval & ~mask; + tmp |= data & mask; return adxl345_reg_write_byte(dev, reg_addr, tmp); } +int adxl345_reg_assign_bits(const struct device *dev, uint8_t reg, uint8_t mask, + bool en) +{ + return adxl345_reg_write_mask(dev, reg, mask, en ? mask : 0x00); +} + static inline bool adxl345_bus_is_ready(const struct device *dev) { const struct adxl345_dev_config *cfg = dev->config; @@ -125,102 +226,120 @@ static inline bool adxl345_bus_is_ready(const struct device *dev) return cfg->bus_is_ready(&cfg->bus); } -int adxl345_get_status(const struct device *dev, - uint8_t *status1, - uint16_t *fifo_entries) +int adxl345_set_measure_en(const struct device *dev, bool en) { - uint8_t buf[2], length = 1U; - int ret; + return adxl345_reg_assign_bits(dev, ADXL345_POWER_CTL_REG, + ADXL345_POWER_CTL_MODE_MSK, en); +} + +int adxl345_get_fifo_entries(const struct device *dev) +{ + uint8_t regval; + int rc; - ret = adxl345_reg_read(dev, ADXL345_INT_SOURCE, buf, length); + rc = adxl345_reg_read_byte(dev, ADXL345_FIFO_STATUS_REG, ®val); + if (rc) { + return rc; + } - *status1 = buf[0]; - ret = adxl345_reg_read(dev, ADXL345_FIFO_STATUS_REG, buf+1, length); - if (fifo_entries) { - *fifo_entries = buf[1] & 0x3F; + return FIELD_GET(ADXL345_FIFO_ENTRIES_MSK, regval); +} + +int adxl345_get_status(const struct device *dev, uint8_t *status) +{ + return adxl345_reg_read_byte(dev, ADXL345_INT_SOURCE_REG, status); +} + +int adxl345_raw_flush_fifo(const struct device *dev) +{ +#if defined(CONFIG_ADXL345_TRIGGER) + uint8_t sample_line[ADXL345_FIFO_SAMPLE_SIZE]; + int rc; + + rc = adxl345_set_measure_en(dev, false); + if (rc) { + return rc; } - return ret; + while (true) { + rc = adxl345_get_fifo_entries(dev); + if (rc < 0) { + return rc; + } + + if (rc == 0) { + break; + } + + /* + * For FIFO_MODE_TRIGGERED (re)setting the FIFO to BYPASSED and back to + * TRIGGERED is enough to reset just the FIFO (as described on p.21 of + * the data sheet). In this case measurement and optionally GPIO lines, + * too, need to be turned off in forehand and turned on again + * afterwards. + * + * When FIFO_MODE_STREAMED is in place, reading out the FIFO elements + * actually resets the FIFO and resets also the particular interrupt + * trigger in INT_SOURCE register e.g. such as watermark, tap events, + * activity, etc. Resetting a STREAMED FIFO is supposed to happen while + * the sensor continues running. Thus measurement is not supposed to be + * turned off except in case of overrun i.e. error. In this case, just + * switching to BYPASSED does not reset any the INT_SOURCE bit flags. + * Therefore the entries must be read. + */ + uint8_t fifo_entries = rc + 1; + + while (fifo_entries > 0) { /* Read FIFO entries + 1 sample lines */ + rc = adxl345_raw_reg_read(dev, ADXL345_REG_DATA_XYZ_REGS, + sample_line, ADXL345_FIFO_SAMPLE_SIZE); + if (rc) { + return rc; + } + + fifo_entries--; + } + } +#endif /* CONFIG_ADXL345_TRIGGER */ + + return adxl345_set_measure_en(dev, true); } /** * Configure the operating parameters for the FIFO. * @param dev - The device structure. - * @param mode - FIFO Mode. Specifies FIFO operating mode. - * Accepted values: ADXL345_FIFO_BYPASSED - * ADXL345_FIFO_STREAMED - * ADXL345_FIFO_TRIGGERED - * ADXL345_FIFO_OLD_SAVED - * @param trigger - FIFO trigger. Links trigger event to appropriate INT. - * Accepted values: ADXL345_INT1 - * ADXL345_INT2 + * @param mode - FIFO Mode. Specifies FIFO operating mode, currently either + * ADXL345_FIFO_BYPASSED or ADXL345_FIFO_STREAMED. + * @param trigger - Currently ignored, thus set to ADXL345_INT_UNSET. * @param fifo_samples - FIFO Samples. Watermark number of FIFO samples that * triggers a FIFO_FULL condition when reached. * Values range from 0 to 32. - + * + * Note: The terms "trigger", "ADXL345_FIFO_TRIGGERED", "ADXL345_FIFO_STREAMED" + * and "ADXL345_FIFO_BYPASSED" in this context are specific to the ADXL345 + * sensor and are defined in its datasheet. These terms are unrelated to + * Zephyr's TRIGGER or STREAM APIs. + * * @return 0 in case of success, negative error code otherwise. */ int adxl345_configure_fifo(const struct device *dev, enum adxl345_fifo_mode mode, enum adxl345_fifo_trigger trigger, - uint16_t fifo_samples) + uint8_t fifo_samples) { struct adxl345_dev_data *data = dev->data; uint8_t fifo_config; - int ret; - - if (fifo_samples > 32) { - return -EINVAL; - } - - fifo_config = (ADXL345_FIFO_CTL_TRIGGER_MODE(trigger) | - ADXL345_FIFO_CTL_MODE_MODE(mode) | - ADXL345_FIFO_CTL_SAMPLES_MODE(fifo_samples)); - ret = adxl345_reg_write_byte(dev, ADXL345_FIFO_CTL_REG, fifo_config); - if (ret) { - return ret; - } - - data->fifo_config.fifo_trigger = trigger; + fifo_config = adxl345_fifo_ctl_mode_init[mode]; data->fifo_config.fifo_mode = mode; - data->fifo_config.fifo_samples = fifo_samples; - return 0; -} + fifo_config |= adxl345_fifo_ctl_trigger_init[trigger]; + data->fifo_config.fifo_trigger = trigger; -/** - * Set the mode of operation. - * @param dev - The device structure. - * @param op_mode - Mode of operation. - * Accepted values: ADXL345_STANDBY - * ADXL345_MEASURE - * @return 0 in case of success, negative error code otherwise. - */ -int adxl345_set_op_mode(const struct device *dev, enum adxl345_op_mode op_mode) -{ - return adxl345_reg_write_mask(dev, ADXL345_POWER_CTL_REG, - ADXL345_POWER_CTL_MEASURE_MSK, - ADXL345_POWER_CTL_MEASURE_MODE(op_mode)); -} + fifo_samples = MIN(fifo_samples, ADXL345_FIFO_CTL_SAMPLES_MSK); + fifo_config |= fifo_samples; + data->fifo_config.fifo_samples = fifo_samples; -/** - * Set Output data rate. - * @param dev - The device structure. - * @param odr - Output data rate. - * Accepted values: ADXL345_ODR_12_5HZ - * ADXL345_ODR_25HZ - * ADXL345_ODR_50HZ - * ADXL345_ODR_100HZ - * ADXL345_ODR_200HZ - * ADXL345_ODR_400HZ - * @return 0 in case of success, negative error code otherwise. - */ -static int adxl345_set_odr(const struct device *dev, enum adxl345_odr odr) -{ - return adxl345_reg_write_mask(dev, ADXL345_RATE_REG, - ADXL345_ODR_MSK, - ADXL345_ODR_MODE(odr)); + return adxl345_reg_write_byte(dev, ADXL345_FIFO_CTL_REG, fifo_config); } static int adxl345_attr_set_odr(const struct device *dev, @@ -254,14 +373,37 @@ static int adxl345_attr_set_odr(const struct device *dev, return -EINVAL; } - int ret = adxl345_set_odr(dev, odr); + data->odr = odr; + + return adxl345_reg_write_mask(dev, ADXL345_RATE_REG, ADXL345_ODR_MSK, + ADXL345_ODR_MODE(odr)); +} + +#if !defined(CONFIG_ADXL345_STREAM) +/* + * In RTIO the watermark i.e. FIFO entries, will be used to lay out the memory + * pool for elements to hold. Thus watermark needs to be defined in the + * devicetree. If by attriute setting a greater number of entries will be set, + * it would crash. + */ +static int adxl345_attr_set_watermark(const struct device *dev, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + struct adxl345_dev_data *data = dev->data; + uint8_t wm = val->val1; - if (ret == 0) { - data->odr = odr; + if (wm < 1 || wm > ADXL345_MAX_FIFO_SIZE) { + return -EINVAL; } - return ret; + data->fifo_config.fifo_samples = wm; + + return adxl345_reg_write_mask(dev, ADXL345_FIFO_CTL_REG, + ADXL345_FIFO_CTL_SAMPLES_MSK, wm); } +#endif static int adxl345_attr_set(const struct device *dev, enum sensor_channel chan, @@ -273,99 +415,149 @@ static int adxl345_attr_set(const struct device *dev, return adxl345_attr_set_odr(dev, chan, attr, val); case SENSOR_ATTR_UPPER_THRESH: return adxl345_reg_write_byte(dev, ADXL345_THRESH_ACT_REG, val->val1); +#if !defined(CONFIG_ADXL345_STREAM) + case SENSOR_ATTR_MAX: + return adxl345_attr_set_watermark(dev, chan, attr, val); +#endif default: return -ENOTSUP; } } int adxl345_read_sample(const struct device *dev, - struct adxl345_sample *sample) + struct adxl345_sample *sample) { - int16_t raw_x, raw_y, raw_z; - uint8_t axis_data[6], status1; - struct adxl345_dev_data *data = dev->data; - - if (!IS_ENABLED(CONFIG_ADXL345_TRIGGER)) { - do { - adxl345_get_status(dev, &status1, NULL); - } while (!(ADXL345_STATUS_DATA_RDY(status1))); - } - - int rc = adxl345_reg_read(dev, ADXL345_X_AXIS_DATA_0_REG, axis_data, 6); + uint8_t axis_data[ADXL345_FIFO_SAMPLE_SIZE]; + int rc; + rc = adxl345_raw_reg_read(dev, ADXL345_REG_DATA_XYZ_REGS, + axis_data, ADXL345_FIFO_SAMPLE_SIZE); if (rc < 0) { LOG_ERR("Samples read failed with rc=%d\n", rc); return rc; } - raw_x = axis_data[0] | (axis_data[1] << 8); - raw_y = axis_data[2] | (axis_data[3] << 8); - raw_z = axis_data[4] | (axis_data[5] << 8); + sample->x = axis_data[0] | axis_data[1] << 8; + sample->y = axis_data[2] | axis_data[3] << 8; + sample->z = axis_data[4] | axis_data[5] << 8; - sample->x = raw_x; - sample->y = raw_y; - sample->z = raw_z; +#ifdef CONFIG_ADXL345_TRIGGER + struct adxl345_dev_data *data = dev->data; + sample->is_full_res = data->is_full_res; /* needed for decoder */ sample->selected_range = data->selected_range; - sample->is_full_res = data->is_full_res; +#endif - return 0; + return rc; } -void adxl345_accel_convert(struct sensor_value *val, int16_t sample) +/** + * adxl345_accel_convert - The fallback conversion of raw measurements. + * @out: Converted value for output, containing the initialized fractional. + * @sample: Input raw measurement. + * When working without decoder, neither TRIGGER, nor STREAM is enabled, + * this small converter is used. It assumes full scale resolution and 8g. + */ +void adxl345_accel_convert(struct sensor_value *out, int16_t sample) { - if (sample & BIT(9)) { - sample |= ADXL345_COMPLEMENT; + /* full resolution enabled w/ 8g */ + if (sample & BIT(11)) { + sample |= ADXL345_COMPLEMENT_MASK(12); } - - val->val1 = ((sample * SENSOR_G) / 32) / 1000000; - val->val2 = ((sample * SENSOR_G) / 32) % 1000000; + out->val1 = ((sample * SENSOR_G) / 32) / 1000000; + out->val2 = ((sample * SENSOR_G) / 32) % 1000000; } static int adxl345_sample_fetch(const struct device *dev, enum sensor_channel chan) { struct adxl345_dev_data *data = dev->data; - struct adxl345_sample sample; + uint8_t count; int rc; - rc = adxl345_read_sample(dev, &sample); - if (rc < 0) { - LOG_ERR("Failed to fetch sample rc=%d\n", rc); - return rc; + count = 1; + + /* FIFO BYPASSED is the only mode not using a FIFO buffer */ + if (data->fifo_config.fifo_mode != ADXL345_FIFO_BYPASSED) { + count = adxl345_get_fifo_entries(dev); + if (count < 0) { + return -EIO; + } } - data->samples.x = sample.x; - data->samples.y = sample.y; - data->samples.z = sample.z; + + __ASSERT_NO_MSG(count <= ARRAY_SIZE(data->sample)); + + for (uint8_t s = 0; s < count; s++) { + rc = adxl345_read_sample(dev, &data->sample[s]); + if (rc < 0) { + LOG_ERR("Failed to fetch sample rc=%d\n", rc); + return rc; + } + +#ifdef CONFIG_ADXL345_STREAM + data->sample[s].is_fifo = 0; +#endif + } + + /* new sample available, reset book-keeping */ + data->sample_idx = 0; + data->fifo_entries = count; return 0; } +/** + * adxl345_channel_get - Read a single element of one or three axis. + * @dev: The sensor device. + * @chan: The axis channel, can be x, y, z or xyz. + * @val: The resulting value after conversion of the raw axis data. Val can be + * a single value or an array of three values, where the index correspondes to + * x, y, z axis entries. + */ static int adxl345_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { struct adxl345_dev_data *data = dev->data; + int idx; + + if (data->fifo_entries <= 0) { /* empty */ + val->val1 = 0; + val->val2 = 0; + if (chan == SENSOR_CHAN_ACCEL_XYZ) { + val[1].val1 = 0; + val[1].val2 = 0; + val[2].val1 = 0; + val[2].val2 = 0; + } + return -ENOTSUP; + } + + data->sample_idx = data->sample_idx % data->fifo_entries; + idx = data->sample_idx; switch (chan) { case SENSOR_CHAN_ACCEL_X: - adxl345_accel_convert(val, data->samples.x); + adxl345_accel_convert(val, data->sample[idx].x); break; case SENSOR_CHAN_ACCEL_Y: - adxl345_accel_convert(val, data->samples.y); + adxl345_accel_convert(val, data->sample[idx].y); break; case SENSOR_CHAN_ACCEL_Z: - adxl345_accel_convert(val, data->samples.z); + adxl345_accel_convert(val, data->sample[idx].z); break; case SENSOR_CHAN_ACCEL_XYZ: - adxl345_accel_convert(val++, data->samples.x); - adxl345_accel_convert(val++, data->samples.y); - adxl345_accel_convert(val, data->samples.z); + adxl345_accel_convert(val++, data->sample[idx].x); + adxl345_accel_convert(val++, data->sample[idx].y); + adxl345_accel_convert(val, data->sample[idx].z); + break; default: return -ENOTSUP; } + data->sample_idx++; + return 0; } @@ -382,141 +574,137 @@ static DEVICE_API(sensor, adxl345_api_funcs) = { #endif }; -#ifdef CONFIG_ADXL345_TRIGGER -/** - * Configure the INT1 and INT2 interrupt pins. - * @param dev - The device structure. - * @param int1 - INT1 interrupt pins. - * @return 0 in case of success, negative error code otherwise. - */ -static int adxl345_interrupt_config(const struct device *dev, - uint8_t int1) -{ - int ret; - const struct adxl345_dev_config *cfg = dev->config; - - ret = adxl345_reg_write_byte(dev, ADXL345_INT_MAP, cfg->route_to_int2 ? int1 : ~int1); - if (ret) { - return ret; - } - - ret = adxl345_reg_write_byte(dev, ADXL345_INT_ENABLE, int1); - if (ret) { - return ret; - } - - uint8_t samples; - - ret = adxl345_reg_read_byte(dev, ADXL345_INT_MAP, &samples); - ret = adxl345_reg_read_byte(dev, ADXL345_INT_ENABLE, &samples); -#ifdef CONFIG_ADXL345_TRIGGER - gpio_pin_interrupt_configure_dt(&cfg->interrupt, - GPIO_INT_EDGE_TO_ACTIVE); -#endif - return 0; -} -#endif - static int adxl345_init(const struct device *dev) { - int rc; struct adxl345_dev_data *data = dev->data; const struct adxl345_dev_config *cfg = dev->config; - uint8_t dev_id, full_res; + enum adxl345_fifo_mode fifo_mode; + uint8_t dev_id; + uint8_t int_en; + uint8_t fifo_samples; + uint8_t regval; + int rc; if (!adxl345_bus_is_ready(dev)) { LOG_ERR("bus not ready"); return -ENODEV; } - rc = adxl345_reg_read_byte(dev, ADXL345_DEVICE_ID_REG, &dev_id); + rc = adxl345_raw_reg_read(dev, ADXL345_DEVICE_ID_REG, &dev_id, 1); if (rc < 0 || dev_id != ADXL345_PART_ID) { LOG_ERR("Read PART ID failed: 0x%x\n", rc); return -ENODEV; } -#if CONFIG_ADXL345_STREAM - rc = adxl345_reg_write_byte(dev, ADXL345_FIFO_CTL_REG, ADXL345_FIFO_STREAM_MODE); - if (rc < 0) { - LOG_ERR("FIFO enable failed\n"); - return -EIO; + /* + * Preset used config registers and init caches in order to use bit + * changes also for STREAM mode in a more efficient way + */ + rc = adxl345_reg_write_byte(dev, ADXL345_POWER_CTL_REG, 0x00); + if (rc) { + return rc; } -#endif - rc = adxl345_reg_write_byte(dev, ADXL345_DATA_FORMAT_REG, ADXL345_RANGE_8G); - if (rc < 0) { - LOG_ERR("Data format set failed\n"); - return -EIO; + rc = adxl345_reg_write_byte(dev, ADXL345_INT_ENABLE_REG, 0x00); + if (rc) { + return rc; } - data->selected_range = ADXL345_RANGE_8G; - - rc = adxl345_reg_write_byte(dev, ADXL345_RATE_REG, ADXL345_RATE_25HZ); - if (rc < 0) { - LOG_ERR("Rate setting failed\n"); - return -EIO; + rc = adxl345_reg_write_byte(dev, ADXL345_INT_MAP_REG, 0x00); + if (rc) { + return rc; } - rc = adxl345_configure_fifo(dev, - IS_ENABLED(CONFIG_ADXL345_STREAM) ? ADXL345_FIFO_STREAMED : - ADXL345_FIFO_BYPASSED, - ADXL345_INT2, - cfg->fifo_config.fifo_samples); + rc = adxl345_reg_write_byte(dev, ADXL345_RATE_REG, 0x00); if (rc) { return rc; } - rc = adxl345_reg_write_byte(dev, ADXL345_POWER_CTL_REG, ADXL345_ENABLE_MEASURE_BIT); - if (rc < 0) { - LOG_ERR("Enable measure bit failed\n"); - return -EIO; + rc = adxl345_reg_write_byte(dev, ADXL345_FIFO_CTL_REG, ADXL345_FIFO_CTL_MODE_BYPASSED); + if (rc) { + return rc; } -#ifdef CONFIG_ADXL345_TRIGGER - rc = adxl345_init_interrupt(dev); + data->selected_range = ADXL345_RANGE_8G; + data->is_full_res = true; + + /* + * Reset the following sensor fields (in case of warm starts) + * - turn off measurements as MSB values, use left justified vals + * - configure full resolution accordingly + * - turn off interrupt inversion + * - turn off 3-wire SPI + * - turn off self test mode + */ + regval = (data->is_full_res ? ADXL345_DATA_FORMAT_FULL_RES : 0x00); + regval |= adxl345_range_init[data->selected_range]; + rc = adxl345_reg_write_byte(dev, ADXL345_DATA_FORMAT_REG, regval); if (rc < 0) { - LOG_ERR("Failed to initialize interrupt!"); + LOG_ERR("Data format set failed\n"); return -EIO; } - rc = adxl345_set_odr(dev, cfg->odr); + rc = adxl345_reg_write_mask(dev, ADXL345_RATE_REG, ADXL345_ODR_MSK, + ADXL345_ODR_MODE(cfg->odr)); if (rc) { + LOG_ERR("Rate setting failed\n"); return rc; } - rc = adxl345_interrupt_config(dev, ADXL345_INT_MAP_WATERMARK_MSK); - if (rc) { - return rc; + + fifo_mode = ADXL345_FIFO_BYPASSED; + fifo_samples = 0; + int_en = 0x00; +#if defined(CONFIG_ADXL345_TRIGGER) || defined(CONFIG_ADXL345_STREAM) + if (adxl345_init_interrupt(dev)) { + LOG_INF("No IRQ lines specified, fallback to FIFO BYPASSED"); + fifo_mode = ADXL345_FIFO_BYPASSED; + } else { + LOG_INF("Set FIFO STREAMED mode"); + fifo_mode = ADXL345_FIFO_STREAMED; + fifo_samples = cfg->fifo_samples; + + /* + * Currently, map all interrupts to the (same) gpio line + * configured in the device tree. This is usually sufficient, + * also since not every hardware will have both gpio lines + * soldered. Anyway, for individual interrupt mapping, set up + * DTB bindings. + */ + + rc = adxl345_reg_assign_bits(dev, ADXL345_INT_MAP_REG, UCHAR_MAX, + (cfg->drdy_pad == 2)); + if (rc) { + return rc; + } } - rc = adxl345_interrupt_config(dev, ADXL345_INT_MAP_ACT_MSK); +#endif + rc = adxl345_configure_fifo(dev, fifo_mode, ADXL345_INT_UNSET, fifo_samples); if (rc) { return rc; } -#endif - rc = adxl345_reg_read_byte(dev, ADXL345_DATA_FORMAT_REG, &full_res); - uint8_t is_full_res_set = (full_res & ADXL345_DATA_FORMAT_FULL_RES) != 0; + if (fifo_mode == ADXL345_FIFO_BYPASSED) { + return adxl345_set_measure_en(dev, true); + } - data->is_full_res = is_full_res_set; return 0; } #ifdef CONFIG_ADXL345_TRIGGER - -#define ADXL345_CFG_IRQ(inst) \ - COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, int1_gpios), \ - ( \ - .interrupt = GPIO_DT_SPEC_INST_GET(inst, int1_gpios), \ - .route_to_int2 = false, \ - ), \ - ( \ - .interrupt = GPIO_DT_SPEC_INST_GET(inst, int2_gpios), \ - .route_to_int2 = true, \ - )) - +#define ADXL345_CFG_IRQ(inst) \ + .gpio_int1 = GPIO_DT_SPEC_INST_GET_OR(inst, int1_gpios, {0}), \ + .gpio_int2 = GPIO_DT_SPEC_INST_GET_OR(inst, int2_gpios, {0}), \ + .drdy_pad = DT_INST_PROP_OR(inst, drdy_pin, -1), \ + .fifo_samples = DT_INST_PROP_OR(inst, fifo_watermark, 1), #else #define ADXL345_CFG_IRQ(inst) #endif /* CONFIG_ADXL345_TRIGGER */ +#define ADXL345_CONFIG_COMMON(inst) \ + IF_ENABLED(UTIL_OR(DT_INST_NODE_HAS_PROP(inst, int1_gpios), \ + DT_INST_NODE_HAS_PROP(inst, int2_gpios)), \ + (ADXL345_CFG_IRQ(inst))) + #define ADXL345_RTIO_SPI_DEFINE(inst) \ COND_CODE_1(CONFIG_SPI_RTIO, \ (SPI_DT_IODEV_DEFINE(adxl345_iodev_##inst, DT_DRV_INST(inst), \ @@ -544,15 +732,12 @@ static int adxl345_init(const struct device *dev) COND_CODE_1(DT_INST_ON_BUS(inst, i2c), \ (ADXL345_RTIO_I2C_DEFINE(inst)), \ ()) \ - RTIO_DEFINE(adxl345_rtio_ctx_##inst, \ - 2 * DT_INST_PROP(inst, fifo_watermark) + 2, \ - 2 * DT_INST_PROP(inst, fifo_watermark) + 2); + RTIO_DEFINE(adxl345_rtio_ctx_##inst, \ + 4 * ADXL345_MAX_FIFO_SIZE, \ + 4 * ADXL345_MAX_FIFO_SIZE); #define ADXL345_CONFIG(inst) \ - .odr = DT_INST_PROP(inst, odr), \ - .fifo_config.fifo_mode = ADXL345_FIFO_STREAMED, \ - .fifo_config.fifo_trigger = ADXL345_INT2, \ - .fifo_config.fifo_samples = DT_INST_PROP_OR(inst, fifo_watermark, 0), + .odr = DT_INST_PROP_OR(inst, odr, ADXL345_RATE_25HZ), #define ADXL345_CONFIG_SPI(inst) \ { \ @@ -565,9 +750,10 @@ static int adxl345_init(const struct device *dev) .reg_access = adxl345_reg_access_spi, \ .bus_type = ADXL345_BUS_SPI, \ ADXL345_CONFIG(inst) \ - COND_CODE_1(UTIL_OR(DT_INST_NODE_HAS_PROP(inst, int1_gpios), \ - DT_INST_NODE_HAS_PROP(inst, int2_gpios)), \ - (ADXL345_CFG_IRQ(inst)), ()) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, int1_gpios), \ + (ADXL345_CONFIG_COMMON(inst)), ()) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, int2_gpios), \ + (ADXL345_CONFIG_COMMON(inst)), ()) \ } #define ADXL345_CONFIG_I2C(inst) \ @@ -577,9 +763,10 @@ static int adxl345_init(const struct device *dev) .reg_access = adxl345_reg_access_i2c, \ .bus_type = ADXL345_BUS_I2C, \ ADXL345_CONFIG(inst) \ - COND_CODE_1(UTIL_OR(DT_INST_NODE_HAS_PROP(inst, int1_gpios), \ - DT_INST_NODE_HAS_PROP(inst, int2_gpios)), \ - (ADXL345_CFG_IRQ(inst)), ()) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, int1_gpios), \ + (ADXL345_CONFIG_COMMON(inst)), ()) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, int2_gpios), \ + (ADXL345_CONFIG_COMMON(inst)), ()) \ } #define ADXL345_DEFINE(inst) \ @@ -589,10 +776,10 @@ static int adxl345_init(const struct device *dev) "Streaming requires fifo-watermark property. Please set it in the" \ "device-tree node properties"); \ BUILD_ASSERT(COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, fifo_watermark), \ - ((DT_INST_PROP(inst, fifo_watermark) > 0) && \ + ((DT_INST_PROP(inst, fifo_watermark) > 1) && \ (DT_INST_PROP(inst, fifo_watermark) < 32)), \ (true)), \ - "fifo-watermark must be between 1 and 32. Please set it in " \ + "fifo-watermark must be between 2 and 32. Please set it in " \ "the device-tree node properties"); \ \ IF_ENABLED(CONFIG_ADXL345_STREAM, (ADXL345_RTIO_DEFINE(inst))); \ @@ -606,6 +793,11 @@ static int adxl345_init(const struct device *dev) \ SENSOR_DEVICE_DT_INST_DEFINE(inst, adxl345_init, NULL, \ &adxl345_data_##inst, &adxl345_config_##inst, POST_KERNEL, \ - CONFIG_SENSOR_INIT_PRIORITY, &adxl345_api_funcs); + CONFIG_SENSOR_INIT_PRIORITY, &adxl345_api_funcs); \ + \ + IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, drdy_pin), \ + (BUILD_ASSERT(DT_INST_NODE_HAS_PROP(inst, \ + CONCAT(int, DT_INST_PROP(inst, drdy_pin), _gpios)), \ + "No GPIO pin defined for ADXL345 DRDY interrupt");)) DT_INST_FOREACH_STATUS_OKAY(ADXL345_DEFINE) diff --git a/drivers/sensor/adi/adxl345/adxl345.h b/drivers/sensor/adi/adxl345/adxl345.h index 4a7ebea3ba1b9..cd99935c5cf9d 100644 --- a/drivers/sensor/adi/adxl345/adxl345.h +++ b/drivers/sensor/adi/adxl345/adxl345.h @@ -35,11 +35,12 @@ #define ADXL345_READ_CMD 0x80 #define ADXL345_MULTIBYTE_FLAG 0x40 -#define ADXL345_REG_READ(x) ((x & 0xFF) | ADXL345_READ_CMD) +#define ADXL345_REG_READ(x) (FIELD_GET(UCHAR_MAX, x) | ADXL345_READ_CMD) +#define ADXL345_REG_READ_MULTIBYTE(x) (ADXL345_REG_READ(x) | ADXL345_MULTIBYTE_FLAG) -#define SAMPLE_SIZE 6 -#define SAMPLE_MASK 0x3F -#define SAMPLE_NUM 0x1F +#define ADXL345_FIFO_SAMPLE_SIZE 6 +#define ADXL345_FIFO_ENTRIES_MSK GENMASK(5, 0) /* FIFO status entries */ +#define ADXL345_FIFO_CTL_SAMPLES_MSK GENMASK(4, 0) /* FIFO control samples */ /* Registers */ #define ADXL345_DEVICE_ID_REG 0x00 @@ -47,18 +48,33 @@ #define ADXL345_POWER_CTL_REG 0x2d #define ADXL345_DATA_FORMAT_REG 0x31 #define ADXL345_DATA_FORMAT_FULL_RES 0x08 -#define ADXL345_X_AXIS_DATA_0_REG 0x32 +#define ADXL345_REG_DATA_XYZ_REGS 0x32 #define ADXL345_FIFO_CTL_REG 0x38 #define ADXL345_FIFO_STATUS_REG 0x39 #define ADXL345_PART_ID 0xe5 -#define ADXL345_RANGE_2G 0x0 -#define ADXL345_RANGE_4G 0x1 -#define ADXL345_RANGE_8G 0x2 -#define ADXL345_RANGE_16G 0x3 +#define ADXL345_DATA_FORMAT_RANGE_2G 0x0 +#define ADXL345_DATA_FORMAT_RANGE_4G 0x1 +#define ADXL345_DATA_FORMAT_RANGE_8G 0x2 +#define ADXL345_DATA_FORMAT_RANGE_16G 0x3 + +enum adxl345_range { + ADXL345_RANGE_2G, + ADXL345_RANGE_4G, + ADXL345_RANGE_8G, + ADXL345_RANGE_16G, +}; + +static const uint8_t adxl345_range_init[] = { + [ADXL345_RANGE_2G] = ADXL345_DATA_FORMAT_RANGE_2G, + [ADXL345_RANGE_4G] = ADXL345_DATA_FORMAT_RANGE_4G, + [ADXL345_RANGE_8G] = ADXL345_DATA_FORMAT_RANGE_8G, + [ADXL345_RANGE_16G] = ADXL345_DATA_FORMAT_RANGE_16G, +}; + #define ADXL345_RATE_25HZ 0x8 -#define ADXL345_ENABLE_MEASURE_BIT (1 << 3) + #define ADXL345_FIFO_STREAM_MODE (1 << 7) #define ADXL345_FIFO_COUNT_MASK 0x3f #define ADXL345_COMPLEMENT_MASK(x) GENMASK(15, (x)) @@ -66,9 +82,9 @@ #define ADXL345_MAX_FIFO_SIZE 32 -#define ADXL345_INT_ENABLE 0x2Eu -#define ADXL345_INT_MAP 0x2Fu -#define ADXL345_INT_SOURCE 0x30u +#define ADXL345_INT_ENABLE_REG 0x2E +#define ADXL345_INT_MAP_REG 0x2F +#define ADXL345_INT_SOURCE_REG 0x30 #define ADXL345_THRESH_ACT_REG 0x24 #define ADXL345_ACT_INACT_CTL_REG 0x27 @@ -86,22 +102,14 @@ #define ADXL345_STATUS_ACTIVITY(x) (((x) >> 4) & 0x1) /* ADXL345_INT_MAP */ -#define ADXL345_INT_MAP_OVERRUN_MSK BIT(0) -#define ADXL345_INT_MAP_OVERRUN_MODE(x) (((x) & 0x1) << 0) -#define ADXL345_INT_MAP_WATERMARK_MSK BIT(1) -#define ADXL345_INT_MAP_WATERMARK_MODE(x) (((x) & 0x1) << 1) -#define ADXL345_INT_MAP_FREE_FALL_MSK BIT(2) -#define ADXL345_INT_MAP_FREE_FALL_MODE(x) (((x) & 0x1) << 2) -#define ADXL345_INT_MAP_INACT_MSK BIT(3) -#define ADXL345_INT_MAP_INACT_MODE(x) (((x) & 0x1) << 3) -#define ADXL345_INT_MAP_ACT_MSK BIT(4) -#define ADXL345_INT_MAP_ACT_MODE(x) (((x) & 0x1) << 4) -#define ADXL345_INT_MAP_DOUBLE_TAP_MSK BIT(5) -#define ADXL345_INT_MAP_DOUBLE_TAP_MODE(x) (((x) & 0x1) << 5) -#define ADXL345_INT_MAP_SINGLE_TAP_MSK BIT(6) -#define ADXL345_INT_MAP_SINGLE_TAP_MODE(x) (((x) & 0x1) << 6) -#define ADXL345_INT_MAP_DATA_RDY_MSK BIT(7) -#define ADXL345_INT_MAP_DATA_RDY_MODE(x) (((x) & 0x1) << 7) +#define ADXL345_INT_OVERRUN BIT(0) +#define ADXL345_INT_WATERMARK BIT(1) +#define ADXL345_INT_FREE_FALL BIT(2) +#define ADXL345_INT_INACT BIT(3) +#define ADXL345_INT_ACT BIT(4) +#define ADXL345_INT_DOUBLE_TAP BIT(5) +#define ADXL345_INT_SINGLE_TAP BIT(6) +#define ADXL345_INT_DATA_RDY BIT(7) /* POWER_CTL */ #define ADXL345_POWER_CTL_WAKEUP_4HZ BIT(0) @@ -110,20 +118,31 @@ #define ADXL345_POWER_CTL_WAKEUP_2HZ_MODE(x) (((x) & 0x1) << 1) #define ADXL345_POWER_CTL_SLEEP BIT(2) #define ADXL345_POWER_CTL_SLEEP_MODE(x) (((x) & 0x1) << 2) -#define ADXL345_POWER_CTL_MEASURE_MSK GENMASK(3, 3) -#define ADXL345_POWER_CTL_MEASURE_MODE(x) (((x) & 0x1) << 3) -#define ADXL345_POWER_CTL_STANDBY_MODE(x) (((x) & 0x0) << 3) +#define ADXL345_POWER_CTL_MODE_MSK BIT(3) /* ADXL345_FIFO_CTL */ -#define ADXL345_FIFO_CTL_MODE_MSK GENMASK(7, 6) -#define ADXL345_FIFO_CTL_MODE_MODE(x) (((x) & 0x3) << 6) -#define ADXL345_FIFO_CTL_TRIGGER_MSK BIT(5) -#define ADXL345_FIFO_CTL_TRIGGER_MODE(x) (((x) & 0x1) << 5) -#define ADXL345_FIFO_CTL_SAMPLES_MSK BIT(0) -#define ADXL345_FIFO_CTL_SAMPLES_MODE(x) ((x) & 0x1F) +#define ADXL345_FIFO_CTL_MODE_MSK GENMASK(7, 6) +#define ADXL345_FIFO_CTL_MODE_BYPASSED 0x0 +#define ADXL345_FIFO_CTL_MODE_OLD_SAVED 0x40 +#define ADXL345_FIFO_CTL_MODE_STREAMED 0x80 +#define ADXL345_FIFO_CTL_MODE_TRIGGERED 0xc0 -#define ADXL345_ODR_MSK GENMASK(3, 0) -#define ADXL345_ODR_MODE(x) ((x) & 0xF) +enum adxl345_fifo_mode { + ADXL345_FIFO_BYPASSED, + ADXL345_FIFO_OLD_SAVED, + ADXL345_FIFO_STREAMED, + ADXL345_FIFO_TRIGGERED, +}; + +static const uint8_t adxl345_fifo_ctl_mode_init[] = { + [ADXL345_FIFO_BYPASSED] = ADXL345_FIFO_CTL_MODE_BYPASSED, + [ADXL345_FIFO_OLD_SAVED] = ADXL345_FIFO_CTL_MODE_OLD_SAVED, + [ADXL345_FIFO_STREAMED] = ADXL345_FIFO_CTL_MODE_STREAMED, + [ADXL345_FIFO_TRIGGERED] = ADXL345_FIFO_CTL_MODE_TRIGGERED, +}; + +#define ADXL345_ODR_MSK GENMASK(3, 0) +#define ADXL345_ODR_MODE(x) FIELD_GET(ADXL345_ODR_MSK, x) #define ADXL345_BUS_I2C 0 #define ADXL345_BUS_SPI 1 @@ -137,48 +156,61 @@ enum adxl345_odr { ADXL345_ODR_400HZ = ADXL345_DT_ODR_400, }; +#define ADXL345_FIFO_CTL_TRIGGER_INT1 0x0 +#define ADXL345_FIFO_CTL_TRIGGER_INT2 BIT(5) +#define ADXL345_FIFO_CTL_TRIGGER_UNSET 0x0 + enum adxl345_fifo_trigger { ADXL345_INT1, - ADXL345_INT2 -}; - -enum adxl345_fifo_mode { - ADXL345_FIFO_BYPASSED, - ADXL345_FIFO_OLD_SAVED, - ADXL345_FIFO_STREAMED, - ADXL345_FIFO_TRIGGERED + ADXL345_INT2, + ADXL345_INT_UNSET, }; struct adxl345_fifo_config { enum adxl345_fifo_mode fifo_mode; enum adxl345_fifo_trigger fifo_trigger; - uint16_t fifo_samples; + uint8_t fifo_samples; }; -enum adxl345_op_mode { - ADXL345_STANDBY, - ADXL345_MEASURE -}; +struct adxl345_sample { +#ifdef CONFIG_ADXL345_STREAM + uint8_t is_fifo: 1; + uint8_t res: 7; +#endif /* CONFIG_ADXL345_STREAM */ + enum adxl345_range selected_range; + bool is_full_res; + int16_t x; + int16_t y; + int16_t z; +} __attribute__((__packed__)); struct adxl345_dev_data { - struct { - int16_t x; - int16_t y; - int16_t z; - } samples; + uint8_t cache_reg_power_ctl; + uint8_t cache_reg_int_enable; + uint8_t cache_reg_int_map; + uint8_t cache_reg_data_format; + uint8_t cache_reg_rate; + uint8_t cache_reg_fifo_ctl; + uint8_t cache_reg_act_thresh; + struct adxl345_sample sample[ADXL345_MAX_FIFO_SIZE]; + uint8_t fifo_entries; /* the actual read FIFO entries */ + uint8_t sample_idx; /* index counting up sample_number entries */ struct adxl345_fifo_config fifo_config; - uint8_t is_full_res; - uint8_t selected_range; + bool is_full_res; + enum adxl345_range selected_range; enum adxl345_odr odr; #ifdef CONFIG_ADXL345_TRIGGER - struct gpio_callback gpio_cb; + struct gpio_callback int1_cb; + struct gpio_callback int2_cb; - sensor_trigger_handler_t th_handler; - const struct sensor_trigger *th_trigger; sensor_trigger_handler_t drdy_handler; const struct sensor_trigger *drdy_trigger; sensor_trigger_handler_t act_handler; const struct sensor_trigger *act_trigger; + sensor_trigger_handler_t wm_handler; + const struct sensor_trigger *wm_trigger; + sensor_trigger_handler_t overrun_handler; + const struct sensor_trigger *overrun_trigger; const struct device *dev; #if defined(CONFIG_ADXL345_TRIGGER_OWN_THREAD) @@ -193,20 +225,17 @@ struct adxl345_dev_data { struct rtio_iodev_sqe *sqe; struct rtio *rtio_ctx; struct rtio_iodev *iodev; - uint8_t status1; - uint8_t fifo_ent[1]; + uint8_t reg_int_source; /* interrupt status register */ + uint8_t reg_fifo_status; /* FIFO status register */ uint64_t timestamp; struct rtio *r_cb; - uint8_t fifo_watermark_irq; - uint8_t fifo_samples; - uint16_t fifo_total_bytes; #endif /* CONFIG_ADXL345_STREAM */ }; struct adxl345_fifo_data { uint8_t is_fifo: 1; uint8_t is_full_res: 1; - uint8_t selected_range: 2; + enum adxl345_range selected_range: 2; uint8_t sample_set_size: 4; uint8_t int_status; uint16_t accel_odr: 4; @@ -214,18 +243,6 @@ struct adxl345_fifo_data { uint64_t timestamp; } __attribute__((__packed__)); -struct adxl345_sample { -#ifdef CONFIG_ADXL345_STREAM - uint8_t is_fifo: 1; - uint8_t res: 7; -#endif /* CONFIG_ADXL345_STREAM */ - uint8_t selected_range; - bool is_full_res; - int16_t x; - int16_t y; - int16_t z; -}; - union adxl345_bus { #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) struct i2c_dt_spec i2c; @@ -244,21 +261,28 @@ struct adxl345_dev_config { adxl345_bus_is_ready_fn bus_is_ready; adxl345_reg_access_fn reg_access; enum adxl345_odr odr; - bool op_mode; - struct adxl345_fifo_config fifo_config; uint8_t bus_type; #ifdef CONFIG_ADXL345_TRIGGER - struct gpio_dt_spec interrupt; - bool route_to_int2; + struct gpio_dt_spec gpio_int1; + struct gpio_dt_spec gpio_int2; + int8_t drdy_pad; + uint8_t fifo_samples; #endif }; +int adxl345_set_gpios_en(const struct device *dev, bool enable); +int adxl345_set_measure_en(const struct device *dev, bool en); + +int adxl345_raw_flush_fifo(const struct device *dev); +#ifdef CONFIG_ADXL345_STREAM +int adxl345_rtio_flush_fifo(const struct device *dev); void adxl345_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe); void adxl345_stream_irq_handler(const struct device *dev); +#endif #ifdef CONFIG_ADXL345_TRIGGER -int adxl345_get_status(const struct device *dev, - uint8_t *status, uint16_t *fifo_entries); +int adxl345_get_fifo_entries(const struct device *dev); +int adxl345_get_status(const struct device *dev, uint8_t *status); int adxl345_trigger_set(const struct device *dev, const struct sensor_trigger *trig, @@ -273,20 +297,25 @@ int adxl345_reg_write_mask(const struct device *dev, uint8_t mask, uint8_t data); +int adxl345_reg_assign_bits(const struct device *dev, uint8_t reg, uint8_t mask, + bool en); + int adxl345_reg_access(const struct device *dev, uint8_t cmd, uint8_t addr, uint8_t *data, size_t len); int adxl345_reg_write(const struct device *dev, uint8_t addr, uint8_t *data, uint8_t len); -int adxl345_reg_read(const struct device *dev, uint8_t addr, uint8_t *data, - uint8_t len); +#if defined(CONFIG_ADXL345_STREAM) +int adxl345_rtio_reg_read(const struct device *dev, uint8_t reg, + uint8_t *buf, size_t buflen, void *userdata, + rtio_callback_t cb); +#endif int adxl345_reg_write_byte(const struct device *dev, uint8_t addr, uint8_t val); int adxl345_reg_read_byte(const struct device *dev, uint8_t addr, uint8_t *buf); -int adxl345_set_op_mode(const struct device *dev, enum adxl345_op_mode op_mode); int adxl345_read_sample(const struct device *dev, struct adxl345_sample *sample); #ifdef CONFIG_SENSOR_ASYNC_API void adxl345_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe); @@ -294,9 +323,13 @@ int adxl345_get_decoder(const struct device *dev, const struct sensor_decoder_ap void adxl345_accel_convert(struct sensor_value *val, int16_t sample); #endif /* CONFIG_SENSOR_ASYNC_API */ -#ifdef CONFIG_ADXL345_STREAM int adxl345_configure_fifo(const struct device *dev, enum adxl345_fifo_mode mode, - enum adxl345_fifo_trigger trigger, uint16_t fifo_samples); + enum adxl345_fifo_trigger trigger, uint8_t fifo_samples); +#ifdef CONFIG_ADXL345_STREAM size_t adxl345_get_packet_size(const struct adxl345_dev_config *cfg); #endif /* CONFIG_ADXL345_STREAM */ + +int adxl345_raw_reg_read(const struct device *dev, uint8_t addr, uint8_t *data, + uint8_t len); + #endif /* ZEPHYR_DRIVERS_SENSOR_ADX345_ADX345_H_ */ diff --git a/drivers/sensor/adi/adxl345/adxl345_decoder.c b/drivers/sensor/adi/adxl345/adxl345_decoder.c index aee7af41a325b..5553125e2de80 100644 --- a/drivers/sensor/adi/adxl345/adxl345_decoder.c +++ b/drivers/sensor/adi/adxl345/adxl345_decoder.c @@ -51,8 +51,9 @@ static const uint32_t range_to_shift[] = { [ADXL345_RANGE_16G] = 8, }; -static inline void adxl345_accel_convert_q31(q31_t *out, int16_t sample, int32_t range, - uint8_t is_full_res) +static inline void adxl345_accel_convert_q31(q31_t *out, int16_t sample, + enum adxl345_range range, + bool is_full_res) { if (is_full_res) { switch (range) { @@ -113,17 +114,18 @@ static int adxl345_decode_stream(const uint8_t *buffer, struct sensor_chan_spec } struct sensor_three_axis_data *data = (struct sensor_three_axis_data *)data_out; + enum adxl345_range selected_range = enc_data->selected_range; memset(data, 0, sizeof(struct sensor_three_axis_data)); data->header.base_timestamp_ns = enc_data->timestamp; data->header.reading_count = 1; - data->shift = range_to_shift[enc_data->selected_range]; + data->shift = range_to_shift[selected_range]; buffer += sizeof(struct adxl345_fifo_data); uint8_t sample_set_size = enc_data->sample_set_size; uint64_t period_ns = accel_period_ns[enc_data->accel_odr]; - uint8_t is_full_res = enc_data->is_full_res; + bool is_full_res = enc_data->is_full_res; /* Calculate which sample is decoded. */ if ((uint8_t *)*fit >= buffer) { @@ -147,15 +149,15 @@ static int adxl345_decode_stream(const uint8_t *buffer, struct sensor_chan_spec uint8_t buff_offset = 0; adxl345_accel_convert_q31(&data->readings[count].x, *(int16_t *)buffer, - enc_data->selected_range, is_full_res); + selected_range, is_full_res); buff_offset = 2; adxl345_accel_convert_q31(&data->readings[count].y, - *(int16_t *)(buffer + buff_offset), - enc_data->selected_range, is_full_res); + *(int16_t *)(buffer + buff_offset), + selected_range, is_full_res); buff_offset += 2; adxl345_accel_convert_q31(&data->readings[count].z, - *(int16_t *)(buffer + buff_offset), - enc_data->selected_range, is_full_res); + *(int16_t *)(buffer + buff_offset), + selected_range, is_full_res); break; default: return -ENOTSUP; @@ -275,7 +277,7 @@ static bool adxl345_decoder_has_trigger(const uint8_t *buffer, enum sensor_trigg switch (trigger) { case SENSOR_TRIG_FIFO_WATERMARK: - return FIELD_GET(ADXL345_INT_MAP_WATERMARK_MSK, data->int_status); + return FIELD_GET(ADXL345_INT_WATERMARK, data->int_status); default: return false; } diff --git a/drivers/sensor/adi/adxl345/adxl345_rtio.c b/drivers/sensor/adi/adxl345/adxl345_rtio.c index c8f0d682a067f..10d9eeb08cdff 100644 --- a/drivers/sensor/adi/adxl345/adxl345_rtio.c +++ b/drivers/sensor/adi/adxl345/adxl345_rtio.c @@ -52,9 +52,10 @@ void adxl345_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) __ASSERT_NO_MSG(req); rtio_work_req_submit(req, iodev_sqe, adxl345_submit_fetch); - } else if (IS_ENABLED(CONFIG_ADXL345_STREAM)) { + } +#if defined(CONFIG_ADXL345_STREAM) adxl345_submit_stream(dev, iodev_sqe); - } else { +#else rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP); - } +#endif } diff --git a/drivers/sensor/adi/adxl345/adxl345_stream.c b/drivers/sensor/adi/adxl345/adxl345_stream.c index 9550efb43bdd3..626a0ba0ab677 100644 --- a/drivers/sensor/adi/adxl345/adxl345_stream.c +++ b/drivers/sensor/adi/adxl345/adxl345_stream.c @@ -11,99 +11,160 @@ LOG_MODULE_DECLARE(ADXL345, CONFIG_SENSOR_LOG_LEVEL); -void adxl345_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +/* auxiliary functions */ + +int adxl345_rtio_reg_read(const struct device *dev, uint8_t reg, + uint8_t *buf, size_t buflen, void *userdata, + rtio_callback_t cb) { - const struct sensor_read_config *cfg = - (const struct sensor_read_config *) iodev_sqe->sqe.iodev->data; struct adxl345_dev_data *data = (struct adxl345_dev_data *)dev->data; - const struct adxl345_dev_config *cfg_345 = dev->config; - uint8_t int_value = (uint8_t)~ADXL345_INT_MAP_WATERMARK_MSK; - uint8_t fifo_watermark_irq = 0; - int rc = gpio_pin_interrupt_configure_dt(&cfg_345->interrupt, - GPIO_INT_DISABLE); + const struct adxl345_dev_config *cfg = dev->config; + uint8_t r = buflen > 1 ? ADXL345_REG_READ_MULTIBYTE(reg) : ADXL345_REG_READ(reg); + struct rtio_sqe *write_sqe = rtio_sqe_acquire(data->rtio_ctx); - if (rc < 0) { - return; + if (!write_sqe) { + LOG_WRN("write_sqe failed"); + goto err; } + rtio_sqe_prep_tiny_write(write_sqe, data->iodev, RTIO_PRIO_NORM, &r, + sizeof(r), NULL); + write_sqe->flags |= RTIO_SQE_TRANSACTION; - for (size_t i = 0; i < cfg->count; i++) { - if (cfg->triggers[i].trigger == SENSOR_TRIG_FIFO_WATERMARK) { - int_value = ADXL345_INT_MAP_WATERMARK_MSK; - fifo_watermark_irq = 1; - } + struct rtio_sqe *read_sqe = rtio_sqe_acquire(data->rtio_ctx); + + if (!read_sqe) { + LOG_WRN("read_sqe failed"); + goto err; } - uint8_t status; - if (fifo_watermark_irq != data->fifo_watermark_irq) { - data->fifo_watermark_irq = fifo_watermark_irq; - rc = adxl345_reg_write_mask(dev, ADXL345_INT_MAP, ADXL345_INT_MAP_WATERMARK_MSK, - cfg_345->route_to_int2 ? int_value : ~int_value); - if (rc < 0) { - return; - } - /* Flush the FIFO by disabling it. Save current mode for after the reset. */ - enum adxl345_fifo_mode current_fifo_mode = data->fifo_config.fifo_mode; + rtio_sqe_prep_read(read_sqe, data->iodev, RTIO_PRIO_NORM, + buf, buflen, userdata); - if (current_fifo_mode == ADXL345_FIFO_BYPASSED) { - current_fifo_mode = ADXL345_FIFO_STREAMED; - } - adxl345_configure_fifo(dev, ADXL345_FIFO_BYPASSED, data->fifo_config.fifo_trigger, - data->fifo_config.fifo_samples); - adxl345_configure_fifo(dev, current_fifo_mode, data->fifo_config.fifo_trigger, - data->fifo_config.fifo_samples); - rc = adxl345_reg_read_byte(dev, ADXL345_FIFO_STATUS_REG, &status); + if (cfg->bus_type == ADXL345_BUS_I2C) { + read_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; } - rc = gpio_pin_interrupt_configure_dt(&cfg_345->interrupt, - GPIO_INT_EDGE_TO_ACTIVE); - if (rc < 0) { - return; + if (cb) { + read_sqe->flags |= RTIO_SQE_CHAINED; + struct rtio_sqe *check_status_sqe = rtio_sqe_acquire(data->rtio_ctx); + + if (!check_status_sqe) { + LOG_WRN("check_status_sqe failed"); + goto err; + } + rtio_sqe_prep_callback_no_cqe(check_status_sqe, cb, (void *)dev, + userdata); } - data->sqe = iodev_sqe; + + return rtio_submit(data->rtio_ctx, 0); +err: + LOG_WRN("low on memory"); + return -ENOMEM; } -static void adxl345_irq_en_cb(struct rtio *r, const struct rtio_sqe *sqe, int result, void *arg) +static void adxl345_sqe_done(const struct device *dev, + struct rtio_iodev_sqe *iodev_sqe, int res) { - ARG_UNUSED(result); + if (res < 0) { + LOG_WRN("res == %d < 0)", res); + rtio_iodev_sqe_err(iodev_sqe, res); + } else { + rtio_iodev_sqe_ok(iodev_sqe, res); + } + adxl345_set_gpios_en(dev, true); +} - const struct device *dev = (const struct device *)arg; - const struct adxl345_dev_config *cfg = dev->config; +static void adxl345_rtio_init_hdr(struct adxl345_fifo_data *hdr, + struct adxl345_dev_data *data, + uint32_t fifo_byte_count) +{ + hdr->is_fifo = 1; + hdr->timestamp = data->timestamp; + hdr->int_status = data->reg_int_source; + hdr->is_full_res = data->is_full_res; + hdr->selected_range = data->selected_range; + hdr->accel_odr = data->odr; + hdr->sample_set_size = ADXL345_FIFO_SAMPLE_SIZE; - gpio_pin_interrupt_configure_dt(&cfg->interrupt, GPIO_INT_EDGE_TO_ACTIVE); + hdr->fifo_byte_count = fifo_byte_count; } -static void adxl345_fifo_flush_rtio(const struct device *dev) +static int adxl345_rtio_init_buffer(struct adxl345_dev_data *data, + uint8_t **read_buf, uint16_t fifo_bytes, + struct rtio_iodev_sqe *current_iodev_sqe) { - struct adxl345_dev_data *data = dev->data; - uint8_t fifo_config; + const size_t min_read_size = sizeof(struct adxl345_fifo_data) + ADXL345_FIFO_SAMPLE_SIZE; + const size_t ideal_read_size = sizeof(struct adxl345_fifo_data) + fifo_bytes; + uint8_t *buf; + uint32_t buf_avail, buf_length; - fifo_config = (ADXL345_FIFO_CTL_TRIGGER_MODE(data->fifo_config.fifo_trigger) | - ADXL345_FIFO_CTL_MODE_MODE(ADXL345_FIFO_BYPASSED) | - ADXL345_FIFO_CTL_SAMPLES_MODE(data->fifo_config.fifo_samples)); + if (rtio_sqe_rx_buf(current_iodev_sqe, min_read_size, ideal_read_size, &buf, + &buf_length)) { + LOG_ERR("Failed to get buffer"); + return -EINVAL; + } + buf_avail = buf_length - sizeof(struct adxl345_fifo_data); + uint32_t read_len = MIN(fifo_bytes, buf_avail); - struct rtio_sqe *write_fifo_addr = rtio_sqe_acquire(data->rtio_ctx); - const uint8_t reg_addr_w2[2] = {ADXL345_FIFO_CTL_REG, fifo_config}; + if (buf_avail < fifo_bytes) { + uint32_t pkts = read_len / ADXL345_FIFO_SAMPLE_SIZE; - rtio_sqe_prep_tiny_write(write_fifo_addr, data->iodev, RTIO_PRIO_NORM, reg_addr_w2, - 2, NULL); + read_len = pkts * ADXL345_FIFO_SAMPLE_SIZE; + } - fifo_config = (ADXL345_FIFO_CTL_TRIGGER_MODE(data->fifo_config.fifo_trigger) | - ADXL345_FIFO_CTL_MODE_MODE(data->fifo_config.fifo_mode) | - ADXL345_FIFO_CTL_SAMPLES_MODE(data->fifo_config.fifo_samples)); + __ASSERT_NO_MSG(read_len % ADXL345_FIFO_SAMPLE_SIZE == 0); + adxl345_rtio_init_hdr((struct adxl345_fifo_data *)buf, data, read_len); - write_fifo_addr = rtio_sqe_acquire(data->rtio_ctx); - const uint8_t reg_addr_w3[2] = {ADXL345_FIFO_CTL_REG, fifo_config}; + *read_buf = buf + sizeof(struct adxl345_fifo_data); - rtio_sqe_prep_tiny_write(write_fifo_addr, data->iodev, RTIO_PRIO_NORM, reg_addr_w3, - 2, NULL); - write_fifo_addr->flags |= RTIO_SQE_CHAINED; + return 0; +} - struct rtio_sqe *complete_op = rtio_sqe_acquire(data->rtio_ctx); +static int adxl345_rtio_cqe_consume(struct adxl345_dev_data *data) +{ + struct rtio_cqe *cqe = rtio_cqe_consume(data->rtio_ctx); + int res = 0; - rtio_sqe_prep_callback(complete_op, adxl345_irq_en_cb, (void *)dev, NULL); - rtio_submit(data->rtio_ctx, 0); + do { + cqe = rtio_cqe_consume(data->rtio_ctx); + if (cqe) { + if ((cqe->result < 0 && res == 0)) { + LOG_ERR("Bus error: %d", cqe->result); + res = cqe->result; + } + rtio_cqe_release(data->rtio_ctx, cqe); + } + } while (cqe); + + return res; +} + +static int adxl345_check_streaming(struct adxl345_dev_data *data, + struct sensor_read_config **ptr_read_config) +{ + if (!data->sqe) { + LOG_ERR("data->iodev_sqe was NULL"); + return false; + } + + *ptr_read_config = (struct sensor_read_config *)data->sqe->sqe.iodev->data; + struct sensor_read_config *read_config = *ptr_read_config; + + if (!read_config) { + LOG_WRN("read_config was NULL"); + return false; + } + + if (!read_config->is_streaming) { + LOG_WRN("is_streaming of read_config was false/NULL"); + return false; + } + + return true; } +/* streaming callbacks and calls */ + static void adxl345_fifo_read_cb(struct rtio *rtio_ctx, const struct rtio_sqe *sqe, int result, void *arg) { @@ -111,15 +172,9 @@ static void adxl345_fifo_read_cb(struct rtio *rtio_ctx, const struct rtio_sqe *s const struct device *dev = (const struct device *)arg; struct adxl345_dev_data *data = (struct adxl345_dev_data *) dev->data; - const struct adxl345_dev_config *cfg = (const struct adxl345_dev_config *) dev->config; struct rtio_iodev_sqe *iodev_sqe = sqe->userdata; - if (data->fifo_samples == 0) { - data->fifo_total_bytes = 0; - rtio_iodev_sqe_ok(iodev_sqe, 0); - gpio_pin_interrupt_configure_dt(&cfg->interrupt, GPIO_INT_EDGE_TO_ACTIVE); - } - + adxl345_sqe_done(dev, iodev_sqe, data->fifo_entries); } static void adxl345_process_fifo_samples_cb(struct rtio *r, const struct rtio_sqe *sqe, @@ -129,114 +184,47 @@ static void adxl345_process_fifo_samples_cb(struct rtio *r, const struct rtio_sq const struct device *dev = (const struct device *)arg; struct adxl345_dev_data *data = (struct adxl345_dev_data *) dev->data; - const struct adxl345_dev_config *cfg = (const struct adxl345_dev_config *) dev->config; struct rtio_iodev_sqe *current_sqe = data->sqe; - uint16_t fifo_samples = (data->fifo_ent[0]) & SAMPLE_MASK; - size_t sample_set_size = SAMPLE_SIZE; - uint16_t fifo_bytes = fifo_samples * SAMPLE_SIZE; + data->fifo_entries = FIELD_GET(ADXL345_FIFO_ENTRIES_MSK, data->reg_fifo_status); + uint8_t fifo_entries = data->fifo_entries; + uint16_t fifo_bytes = fifo_entries * ADXL345_FIFO_SAMPLE_SIZE; data->sqe = NULL; /* Not inherently an underrun/overrun as we may have a buffer to fill next time */ if (current_sqe == NULL) { LOG_ERR("No pending SQE"); - gpio_pin_interrupt_configure_dt(&cfg->interrupt, GPIO_INT_EDGE_TO_ACTIVE); + adxl345_set_gpios_en(dev, true); return; } - const size_t min_read_size = sizeof(struct adxl345_fifo_data) + sample_set_size; - const size_t ideal_read_size = sizeof(struct adxl345_fifo_data) + fifo_bytes; - - uint8_t *buf; - uint32_t buf_len; - - if (rtio_sqe_rx_buf(current_sqe, min_read_size, ideal_read_size, &buf, &buf_len) != 0) { - LOG_ERR("Failed to get buffer"); - rtio_iodev_sqe_err(current_sqe, -ENOMEM); - gpio_pin_interrupt_configure_dt(&cfg->interrupt, GPIO_INT_EDGE_TO_ACTIVE); - return; - } - LOG_DBG("Requesting buffer [%u, %u] got %u", (unsigned int)min_read_size, - (unsigned int)ideal_read_size, buf_len); - - /* Read FIFO and call back to rtio with rtio_sqe completion */ - struct adxl345_fifo_data *hdr = (struct adxl345_fifo_data *) buf; - - hdr->is_fifo = 1; - hdr->timestamp = data->timestamp; - hdr->int_status = data->status1; - hdr->is_full_res = data->is_full_res; - hdr->selected_range = data->selected_range; - hdr->accel_odr = data->odr; - hdr->sample_set_size = sample_set_size; - - uint32_t buf_avail = buf_len; + uint8_t *read_buf; - buf_avail -= sizeof(*hdr); - - uint32_t read_len = MIN(fifo_bytes, buf_avail); - - if (buf_avail < fifo_bytes) { - uint32_t pkts = read_len / sample_set_size; - - read_len = pkts * sample_set_size; + if (adxl345_rtio_init_buffer(data, &read_buf, fifo_bytes, current_sqe)) { + goto err; } - ((struct adxl345_fifo_data *)buf)->fifo_byte_count = read_len; - - uint8_t *read_buf = buf + sizeof(*hdr); - /* Flush completions */ - struct rtio_cqe *cqe; - int res = 0; - - do { - cqe = rtio_cqe_consume(data->rtio_ctx); - if (cqe != NULL) { - if ((cqe->result < 0 && res == 0)) { - LOG_ERR("Bus error: %d", cqe->result); - res = cqe->result; - } - rtio_cqe_release(data->rtio_ctx, cqe); - } - } while (cqe != NULL); - - /* Bail/cancel attempt to read sensor on any error */ - if (res != 0) { - rtio_iodev_sqe_err(current_sqe, res); - return; + if (adxl345_rtio_cqe_consume(data)) { + goto err; } - - data->fifo_samples = fifo_samples; - for (size_t i = 0; i < fifo_samples; i++) { - struct rtio_sqe *write_fifo_addr = rtio_sqe_acquire(data->rtio_ctx); - struct rtio_sqe *read_fifo_data = rtio_sqe_acquire(data->rtio_ctx); - - data->fifo_samples--; - const uint8_t reg_addr = ADXL345_REG_READ(ADXL345_X_AXIS_DATA_0_REG) - | ADXL345_MULTIBYTE_FLAG; - - rtio_sqe_prep_tiny_write(write_fifo_addr, data->iodev, RTIO_PRIO_NORM, ®_addr, - 1, NULL); - write_fifo_addr->flags |= RTIO_SQE_TRANSACTION; - rtio_sqe_prep_read(read_fifo_data, data->iodev, RTIO_PRIO_NORM, - read_buf + data->fifo_total_bytes, - SAMPLE_SIZE, current_sqe); - data->fifo_total_bytes += SAMPLE_SIZE; - if (cfg->bus_type == ADXL345_BUS_I2C) { - read_fifo_data->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; - } - if (i == fifo_samples-1) { - struct rtio_sqe *complete_op = rtio_sqe_acquire(data->rtio_ctx); - - read_fifo_data->flags |= RTIO_SQE_CHAINED; - rtio_sqe_prep_callback(complete_op, adxl345_fifo_read_cb, (void *)dev, - current_sqe); + for (size_t i = 0; i < fifo_entries; i++) { + data->fifo_entries--; + if (adxl345_rtio_reg_read(dev, ADXL345_REG_DATA_XYZ_REGS, + (read_buf + i * ADXL345_FIFO_SAMPLE_SIZE), + ADXL345_FIFO_SAMPLE_SIZE, current_sqe, + (i == fifo_entries - 1) ? adxl345_fifo_read_cb : NULL)) { + LOG_WRN("RTIO reading the XYZ regs failed"); + goto err; } - rtio_submit(data->rtio_ctx, 0); ARG_UNUSED(rtio_cqe_consume(data->rtio_ctx)); } + + return; +err: + LOG_WRN("Failed."); + adxl345_sqe_done(dev, current_sqe, -ENOMEM); } static void adxl345_process_status1_cb(struct rtio *r, const struct rtio_sqe *sqe, @@ -246,73 +234,42 @@ static void adxl345_process_status1_cb(struct rtio *r, const struct rtio_sqe *sq const struct device *dev = (const struct device *)arg; struct adxl345_dev_data *data = (struct adxl345_dev_data *) dev->data; - const struct adxl345_dev_config *cfg = (const struct adxl345_dev_config *) dev->config; struct rtio_iodev_sqe *current_sqe = data->sqe; struct sensor_read_config *read_config; - uint8_t status1 = data->status1; - - if (data->sqe == NULL) { - return; - } - read_config = (struct sensor_read_config *)data->sqe->sqe.iodev->data; - - if (read_config == NULL) { - return; - } - - if (read_config->is_streaming == false) { + if (!adxl345_check_streaming(data, &read_config)) { + LOG_WRN("Failed! RTIO not setup for streaming"); return; } - gpio_pin_interrupt_configure_dt(&cfg->interrupt, GPIO_INT_DISABLE); - struct sensor_stream_trigger *fifo_wmark_cfg = NULL; + struct sensor_stream_trigger *fifo_full_cfg = NULL; + enum sensor_stream_data_opt data_opt = SENSOR_STREAM_DATA_DROP; for (int i = 0; i < read_config->count; ++i) { - if (read_config->triggers[i].trigger == SENSOR_TRIG_FIFO_WATERMARK) { - fifo_wmark_cfg = &read_config->triggers[i]; + switch (read_config->triggers[i].trigger) { + case SENSOR_TRIG_FIFO_WATERMARK: + if (FIELD_GET(ADXL345_INT_WATERMARK, data->reg_int_source)) { + fifo_wmark_cfg = &read_config->triggers[i]; + data_opt = MIN(data_opt, fifo_wmark_cfg->opt); + } continue; - } - } - - bool fifo_full_irq = false; - - if ((fifo_wmark_cfg != NULL) - && FIELD_GET(ADXL345_INT_MAP_WATERMARK_MSK, status1)) { - fifo_full_irq = true; - } - - if (!fifo_full_irq) { - gpio_pin_interrupt_configure_dt(&cfg->interrupt, GPIO_INT_EDGE_TO_ACTIVE); - return; - } - - /* Flush completions */ - struct rtio_cqe *cqe; - int res = 0; - - do { - cqe = rtio_cqe_consume(data->rtio_ctx); - if (cqe != NULL) { - if ((cqe->result < 0) && (res == 0)) { - LOG_ERR("Bus error: %d", cqe->result); - res = cqe->result; + case SENSOR_TRIG_FIFO_FULL: + if (FIELD_GET(ADXL345_INT_OVERRUN, data->reg_int_source)) { + fifo_full_cfg = &read_config->triggers[i]; + data_opt = MIN(data_opt, fifo_full_cfg->opt); } - rtio_cqe_release(data->rtio_ctx, cqe); + continue; + default: + LOG_WRN("SENSOR_* case not covered"); + goto err; } - } while (cqe != NULL); - - /* Bail/cancel attempt to read sensor on any error */ - if (res != 0) { - rtio_iodev_sqe_err(current_sqe, res); - return; } - enum sensor_stream_data_opt data_opt; - - if (fifo_wmark_cfg != NULL) { - data_opt = fifo_wmark_cfg->opt; + /* Flush completions, in case cancel out */ + if (adxl345_rtio_cqe_consume(data)) { + LOG_WRN("CQE consume failed"); + goto err; } if (data_opt == SENSOR_STREAM_DATA_NOP || data_opt == SENSOR_STREAM_DATA_DROP) { @@ -323,80 +280,181 @@ static void adxl345_process_status1_cb(struct rtio *r, const struct rtio_sqe *sq data->sqe = NULL; if (rtio_sqe_rx_buf(current_sqe, sizeof(struct adxl345_fifo_data), sizeof(struct adxl345_fifo_data), &buf, &buf_len) != 0) { - rtio_iodev_sqe_err(current_sqe, -ENOMEM); - gpio_pin_interrupt_configure_dt(&cfg->interrupt, GPIO_INT_EDGE_TO_ACTIVE); - return; + goto err; } - struct adxl345_fifo_data *rx_data = (struct adxl345_fifo_data *)buf; - memset(buf, 0, buf_len); - rx_data->is_fifo = 1; - rx_data->timestamp = data->timestamp; - rx_data->int_status = status1; - rx_data->fifo_byte_count = 0; - rtio_iodev_sqe_ok(current_sqe, 0); + adxl345_rtio_init_hdr((struct adxl345_fifo_data *)buf, data, 0); if (data_opt == SENSOR_STREAM_DATA_DROP) { - /* Flush the FIFO by disabling it. Save current mode for after the reset. */ - adxl345_fifo_flush_rtio(dev); + /* + * Flush the FIFO by disabling it. Save current mode + * for after the reset. + */ + adxl345_rtio_flush_fifo(dev); } - gpio_pin_interrupt_configure_dt(&cfg->interrupt, GPIO_INT_EDGE_TO_ACTIVE); + adxl345_sqe_done(dev, current_sqe, 0); return; } - struct rtio_sqe *write_fifo_addr = rtio_sqe_acquire(data->rtio_ctx); - struct rtio_sqe *read_fifo_data = rtio_sqe_acquire(data->rtio_ctx); - struct rtio_sqe *complete_op = rtio_sqe_acquire(data->rtio_ctx); - const uint8_t reg_addr = ADXL345_REG_READ(ADXL345_FIFO_STATUS_REG); + if (adxl345_rtio_reg_read(dev, ADXL345_FIFO_STATUS_REG, &data->reg_fifo_status, + sizeof(data->reg_fifo_status), current_sqe, + adxl345_process_fifo_samples_cb)) { + LOG_WRN("Reading the FIFO samples failed"); + goto err; + } - rtio_sqe_prep_tiny_write(write_fifo_addr, data->iodev, RTIO_PRIO_NORM, ®_addr, 1, NULL); - write_fifo_addr->flags |= RTIO_SQE_TRANSACTION; - rtio_sqe_prep_read(read_fifo_data, data->iodev, RTIO_PRIO_NORM, data->fifo_ent, 1, - current_sqe); - read_fifo_data->flags |= RTIO_SQE_CHAINED; - if (cfg->bus_type == ADXL345_BUS_I2C) { - read_fifo_data->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + return; +err: + LOG_WRN("Failed."); + adxl345_sqe_done(dev, current_sqe, -ENOMEM); +} + +/** + * adxl345_rtio_flush_fifo - Reset the FIFO and interrupt status registers. + * @dev The device node. + * + * Consume all FIFO elements, since this not just resets FIFO_STATUS, but also INT_SOURCE entries. + * When using Analog's STREAM FIFO mode, FIFO elements must be consumed. For Analog's + * TRIGGER FIFO mode, it would be sufficient to switch FIFO modes to BYPASS FIFO mode and + * back to Analog's STREAM FIFO mode, since in this mode, the FIFO gets only activated once if the + * trigger bit of the sensor is set by one of the sensor events, i.e. in terms of Analog Devices a + * "trigger event". The INT SOURCE does not need to be reset here, but using the sensor in a + * permanent sensing mode, such as Analog's STREAM FIFO mode requires a reset of the INT_SOURCE + * register, too. Thus, it is required to consume the FIFO elements. + * + * @return 0 for success, or error number. + */ +int adxl345_rtio_flush_fifo(const struct device *dev) +{ + struct adxl345_dev_data *data = dev->data; + struct rtio_iodev_sqe *current_sqe = data->sqe; + int8_t fifo_entries = ADXL345_MAX_FIFO_SIZE; + struct sensor_read_config *read_config = NULL; + + if (!adxl345_check_streaming(data, &read_config)) { + LOG_WRN("adxl345_check_streaming() failed"); + return 0; + } + + if (adxl345_rtio_cqe_consume(data)) { + LOG_WRN("adxl345_rtio_cqe_consume() failed"); + goto err; + } + + if (adxl345_set_measure_en(dev, false)) { + LOG_WRN("adxl345_set_measure_en() failed"); + goto err; + } + + const struct adxl345_dev_config *cfg = dev->config; + uint8_t reg = ADXL345_REG_DATA_XYZ_REGS; + uint8_t reg_addr = ADXL345_REG_READ_MULTIBYTE(reg); + size_t buflen = ADXL345_FIFO_SAMPLE_SIZE; + + for (size_t i = 0; i < fifo_entries; i++) { + uint8_t dummy[6]; + uint8_t *buf = dummy; + struct rtio_sqe *write_sqe = rtio_sqe_acquire(data->rtio_ctx); + + if (!write_sqe) { + LOG_WRN("write_sqe failed"); + goto err; + } + rtio_sqe_prep_tiny_write(write_sqe, data->iodev, + RTIO_PRIO_NORM, ®_addr, + sizeof(reg_addr), NULL); + write_sqe->flags |= RTIO_SQE_TRANSACTION; + + struct rtio_sqe *read_sqe = rtio_sqe_acquire(data->rtio_ctx); + + if (!read_sqe) { + LOG_WRN("read_sqe failed"); + goto err; + } + rtio_sqe_prep_read(read_sqe, data->iodev, RTIO_PRIO_NORM, + buf, buflen, + current_sqe); + + if (cfg->bus_type == ADXL345_BUS_I2C) { + read_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + } + rtio_submit(data->rtio_ctx, 2); /* cautiously keep blocking */ + ARG_UNUSED(rtio_cqe_consume(data->rtio_ctx)); + } + + if (adxl345_set_measure_en(dev, true)) { + LOG_WRN("adxl345_set_measure_en() failed"); + goto err; + } + + return 0; +err: + LOG_WRN("Failed."); + return -EINVAL; +} + +/* Consumer calls */ + +static bool first = true; +void adxl345_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + const struct sensor_read_config *cfg = + (const struct sensor_read_config *)iodev_sqe->sqe.iodev->data; + struct adxl345_dev_data *data = (struct adxl345_dev_data *)dev->data; + int rc; + + data->sqe = iodev_sqe; + + if (first) { + /* Initialize measurement and start with flushed registers */ + adxl345_rtio_flush_fifo(dev); + adxl345_set_gpios_en(dev, true); + first = false; } - rtio_sqe_prep_callback(complete_op, adxl345_process_fifo_samples_cb, (void *)dev, - current_sqe); - rtio_submit(data->rtio_ctx, 0); + for (size_t i = 0; i < cfg->count; i++) { + if (cfg->triggers[i].trigger == SENSOR_TRIG_FIFO_WATERMARK) { + rc = adxl345_reg_assign_bits(dev, ADXL345_INT_ENABLE_REG, + ADXL345_INT_WATERMARK, true); + if (rc) { + LOG_WRN("adxl345_reg_assign_bits() failed"); + return; + } + } + } } void adxl345_stream_irq_handler(const struct device *dev) { struct adxl345_dev_data *data = (struct adxl345_dev_data *) dev->data; - const struct adxl345_dev_config *cfg = (const struct adxl345_dev_config *) dev->config; + struct rtio_iodev_sqe *current_sqe = data->sqe; uint64_t cycles; - int rc; - if (data->sqe == NULL) { + if (!current_sqe) { return; } - rc = sensor_clock_get_cycles(&cycles); - if (rc != 0) { + if (sensor_clock_get_cycles(&cycles)) { LOG_ERR("Failed to get sensor clock cycles"); - rtio_iodev_sqe_err(data->sqe, rc); - return; + goto err; } data->timestamp = sensor_clock_cycles_to_ns(cycles); - struct rtio_sqe *write_status_addr = rtio_sqe_acquire(data->rtio_ctx); - struct rtio_sqe *read_status_reg = rtio_sqe_acquire(data->rtio_ctx); - struct rtio_sqe *check_status_reg = rtio_sqe_acquire(data->rtio_ctx); - uint8_t reg = ADXL345_REG_READ(ADXL345_INT_SOURCE); - - rtio_sqe_prep_tiny_write(write_status_addr, data->iodev, RTIO_PRIO_NORM, ®, 1, NULL); - write_status_addr->flags |= RTIO_SQE_TRANSACTION; - rtio_sqe_prep_read(read_status_reg, data->iodev, RTIO_PRIO_NORM, &data->status1, 1, NULL); - read_status_reg->flags |= RTIO_SQE_CHAINED; - if (cfg->bus_type == ADXL345_BUS_I2C) { - read_status_reg->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + if (adxl345_rtio_reg_read(dev, ADXL345_INT_SOURCE_REG, + &data->reg_int_source, + sizeof(data->reg_int_source), + NULL, adxl345_process_status1_cb)) { + LOG_ERR("Processing the FIFO status failed"); + goto err; } - rtio_sqe_prep_callback(check_status_reg, adxl345_process_status1_cb, (void *)dev, NULL); - rtio_submit(data->rtio_ctx, 0); + + adxl345_sqe_done(dev, current_sqe, 0); + + return; +err: + LOG_WRN("Failed."); + adxl345_sqe_done(dev, current_sqe, -ENOMEM); } diff --git a/drivers/sensor/adi/adxl345/adxl345_trigger.c b/drivers/sensor/adi/adxl345/adxl345_trigger.c index cb7b61e361142..afdfe2e3f873e 100644 --- a/drivers/sensor/adi/adxl345/adxl345_trigger.c +++ b/drivers/sensor/adi/adxl345/adxl345_trigger.c @@ -16,47 +16,129 @@ #include LOG_MODULE_DECLARE(ADXL345, CONFIG_SENSOR_LOG_LEVEL); +static int adxl345_set_int_pad_state(const struct device *dev, uint8_t pad, + bool en) +{ + const struct adxl345_dev_config *cfg = dev->config; + int state = en ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE; + + /* in case of neither INT_1 nor INT_2 being defined */ + if (!cfg->gpio_int1.port && !cfg->gpio_int2.port) { + return -ENOTSUP; + } + + if (pad == 1) { + return gpio_pin_interrupt_configure_dt(&cfg->gpio_int1, state); + } else if (pad == 2) { + return gpio_pin_interrupt_configure_dt(&cfg->gpio_int2, state); + } + + /* pad may be -1, e.g. if no INT line defined in DT */ + return -EINVAL; +} + +int adxl345_set_gpios_en(const struct device *dev, bool en) +{ + const struct adxl345_dev_config *cfg = dev->config; + + return adxl345_set_int_pad_state(dev, cfg->drdy_pad, en); +} + #if defined(CONFIG_ADXL345_TRIGGER_OWN_THREAD) || defined(CONFIG_ADXL345_TRIGGER_GLOBAL_THREAD) static void adxl345_thread_cb(const struct device *dev) { - const struct adxl345_dev_config *cfg = dev->config; struct adxl345_dev_data *drv_data = dev->data; - uint8_t status1; + uint8_t status; int ret; - /* Clear the status */ - if (adxl345_get_status(dev, &status1, NULL) < 0) { - return; + ret = adxl345_get_status(dev, &status); + __ASSERT(ret == 0, "Interrupt configuration failed"); + + if (FIELD_GET(ADXL345_INT_DATA_RDY, status)) { + if (drv_data->drdy_handler) { + /* + * A handler needs to flush FIFO, i.e. fetch and get + * samples to get new events + */ + drv_data->drdy_handler(dev, drv_data->drdy_trigger); + } + } + + if (FIELD_GET(ADXL345_INT_WATERMARK, status)) { + if (drv_data->wm_handler) { + /* + * A handler needs to implement fetch, then get FIFO + * entries according to configured watermark in order + * to obtain new sensor events + */ + drv_data->wm_handler(dev, drv_data->wm_trigger); + } } - if ((drv_data->drdy_handler != NULL) && - ADXL345_STATUS_DATA_RDY(status1)) { - drv_data->drdy_handler(dev, drv_data->drdy_trigger); + /* handle FIFO: overrun */ + if (FIELD_GET(ADXL345_INT_OVERRUN, status)) { + if (drv_data->overrun_handler) { + /* + * A handler may handle read outs, the fallback flushes + * the fifo and interrupt status register + */ + drv_data->overrun_handler(dev, drv_data->overrun_trigger); + } + + /* + * If overrun handling is enabled, reset status register and + * fifo here, if not handled before in any way + */ + adxl345_raw_flush_fifo(dev); } - if ((drv_data->act_handler != NULL) && - ADXL345_STATUS_ACTIVITY(status1)) { - drv_data->act_handler(dev, drv_data->act_trigger); + if (drv_data->act_trigger && FIELD_GET(ADXL345_INT_ACT, status)) { + if (drv_data->act_handler) { + drv_data->act_handler(dev, drv_data->act_trigger); + } } - ret = gpio_pin_interrupt_configure_dt(&cfg->interrupt, - GPIO_INT_EDGE_TO_ACTIVE); + ret = adxl345_set_gpios_en(dev, true); __ASSERT(ret == 0, "Interrupt configuration failed"); } #endif -static void adxl345_gpio_callback(const struct device *dev, - struct gpio_callback *cb, uint32_t pins) +static void adxl345_int1_gpio_callback(const struct device *dev, + struct gpio_callback *cb, + uint32_t pins) { struct adxl345_dev_data *drv_data = - CONTAINER_OF(cb, struct adxl345_dev_data, gpio_cb); - const struct adxl345_dev_config *cfg = drv_data->dev->config; + CONTAINER_OF(cb, struct adxl345_dev_data, int1_cb); - gpio_pin_interrupt_configure_dt(&cfg->interrupt, GPIO_INT_DISABLE); + ARG_UNUSED(pins); - if (IS_ENABLED(CONFIG_ADXL345_STREAM)) { - adxl345_stream_irq_handler(drv_data->dev); - } + adxl345_set_int_pad_state(dev, 1, false); + +#if defined(CONFIG_ADXL345_STREAM) + adxl345_stream_irq_handler(drv_data->dev); +#endif + +#if defined(CONFIG_ADXL345_TRIGGER_OWN_THREAD) + k_sem_give(&drv_data->gpio_sem); +#elif defined(CONFIG_ADXL345_TRIGGER_GLOBAL_THREAD) + k_work_submit(&drv_data->work); +#endif +} + +static void adxl345_int2_gpio_callback(const struct device *dev, + struct gpio_callback *cb, + uint32_t pins) +{ + struct adxl345_dev_data *drv_data = + CONTAINER_OF(cb, struct adxl345_dev_data, int2_cb); + + ARG_UNUSED(pins); + + adxl345_set_int_pad_state(dev, 2, false); + +#if defined(CONFIG_ADXL345_STREAM) + adxl345_stream_irq_handler(drv_data->dev); +#endif #if defined(CONFIG_ADXL345_TRIGGER_OWN_THREAD) k_sem_give(&drv_data->gpio_sem); @@ -95,37 +177,69 @@ int adxl345_trigger_set(const struct device *dev, { const struct adxl345_dev_config *cfg = dev->config; struct adxl345_dev_data *drv_data = dev->data; - struct adxl345_sample sample; - uint8_t int_mask, int_en; - uint8_t samples_count; int ret; - ret = gpio_pin_interrupt_configure_dt(&cfg->interrupt, - GPIO_INT_DISABLE); - if (ret < 0) { - return ret; + if (!cfg->gpio_int1.port && !cfg->gpio_int2.port) { + /* might be in FIFO BYPASS mode */ + goto done; + } + + /* generally turn off interrupts */ + ret = adxl345_set_gpios_en(dev, false); + if (ret) { + goto done; + } + + if (!handler) { + goto done; } switch (trig->type) { case SENSOR_TRIG_DATA_READY: drv_data->drdy_handler = handler; drv_data->drdy_trigger = trig; - /** Enabling DRDY means not using Watermark interrupt as both - * are served by reading data-register: two clients can't be - * served simultaneously. - */ - int_mask = ADXL345_INT_MAP_DATA_RDY_MSK | - ADXL345_INT_MAP_OVERRUN_MSK | - ADXL345_INT_MAP_WATERMARK_MSK; + + ret = adxl345_reg_assign_bits(dev, ADXL345_INT_ENABLE_REG, + ADXL345_INT_DATA_RDY, + true); + if (ret) { + return ret; + } + break; + case SENSOR_TRIG_FIFO_WATERMARK: + drv_data->wm_handler = handler; + drv_data->wm_trigger = trig; + ret = adxl345_reg_assign_bits(dev, ADXL345_INT_ENABLE_REG, + ADXL345_INT_WATERMARK, + true); + if (ret) { + return ret; + } + break; + case SENSOR_TRIG_FIFO_FULL: + drv_data->overrun_handler = handler; + drv_data->overrun_trigger = trig; + ret = adxl345_reg_assign_bits(dev, ADXL345_INT_ENABLE_REG, + ADXL345_INT_OVERRUN, + true); + if (ret) { + return ret; + } break; case SENSOR_TRIG_MOTION: drv_data->act_handler = handler; drv_data->act_trigger = trig; - int_mask = ADXL345_INT_MAP_ACT_MSK; ret = adxl345_reg_write_byte(dev, ADXL345_ACT_INACT_CTL_REG, ADXL345_ACT_AC_DC | ADXL345_ACT_X_EN | ADXL345_ACT_Y_EN | ADXL345_ACT_Z_EN); - if (ret < 0) { + if (ret) { + return ret; + } + + ret = adxl345_reg_assign_bits(dev, ADXL345_INT_ENABLE_REG, + ADXL345_INT_ACT, + true); + if (ret) { return ret; } break; @@ -134,48 +248,13 @@ int adxl345_trigger_set(const struct device *dev, return -ENOTSUP; } - if (handler) { - int_en = ADXL345_INT_MAP_DATA_RDY_MSK; - } else { - int_en = 0U; - } - -#ifdef CONFIG_ADXL345_STREAM - (void)adxl345_configure_fifo(dev, ADXL345_FIFO_BYPASSED, ADXL345_INT2, 0); -#endif - - ret = adxl345_reg_write_mask(dev, ADXL345_INT_ENABLE, int_mask, 0); - if (ret < 0) { - return ret; - } - - ret = adxl345_reg_write_mask(dev, ADXL345_INT_MAP, int_mask, - cfg->route_to_int2 ? int_en : ~int_en); - if (ret < 0) { - return ret; - } - - /* Clear status and read sample-set to clear interrupt flag */ - (void)adxl345_read_sample(dev, &sample); - - ret = adxl345_reg_read_byte(dev, ADXL345_FIFO_STATUS_REG, &samples_count); - if (ret < 0) { - LOG_ERR("Failed to read FIFO status rc = %d\n", ret); - return ret; - } - - ret = gpio_pin_interrupt_configure_dt(&cfg->interrupt, - GPIO_INT_EDGE_TO_ACTIVE); - if (ret < 0) { +done: + ret = adxl345_set_gpios_en(dev, true); + if (ret) { return ret; } - ret = adxl345_reg_write_mask(dev, ADXL345_INT_ENABLE, int_mask, int_en); - if (ret < 0) { - return ret; - } - - return 0; + return adxl345_raw_flush_fifo(dev); } int adxl345_init_interrupt(const struct device *dev) @@ -184,24 +263,23 @@ int adxl345_init_interrupt(const struct device *dev) struct adxl345_dev_data *drv_data = dev->data; int ret; - if (!gpio_is_ready_dt(&cfg->interrupt)) { - LOG_ERR("GPIO port %s not ready", cfg->interrupt.port->name); - return -EINVAL; + /* TRIGGER is set, but no INT line was defined in DT */ + if (!cfg->gpio_int1.port && !cfg->gpio_int2.port) { + return -ENOTSUP; } - ret = gpio_pin_configure_dt(&cfg->interrupt, GPIO_INPUT); - if (ret < 0) { - return ret; + if (cfg->gpio_int1.port) { + if (!gpio_is_ready_dt(&cfg->gpio_int1)) { + LOG_ERR("INT_1 line defined, but not ready"); + return -ENODEV; + } } - gpio_init_callback(&drv_data->gpio_cb, - adxl345_gpio_callback, - BIT(cfg->interrupt.pin)); - - ret = gpio_add_callback(cfg->interrupt.port, &drv_data->gpio_cb); - if (ret < 0) { - LOG_ERR("Failed to set gpio callback!"); - return ret; + if (cfg->gpio_int2.port) { + if (!gpio_is_ready_dt(&cfg->gpio_int2)) { + LOG_ERR("INT_2 line defined, but not ready"); + return -ENODEV; + } } drv_data->dev = dev; @@ -220,5 +298,37 @@ int adxl345_init_interrupt(const struct device *dev) drv_data->work.handler = adxl345_work_cb; #endif + if (cfg->gpio_int1.port) { + ret = gpio_pin_configure_dt(&cfg->gpio_int1, GPIO_INPUT); + if (ret) { + return ret; + } + + gpio_init_callback(&drv_data->int1_cb, adxl345_int1_gpio_callback, + BIT(cfg->gpio_int1.pin)); + + ret = gpio_add_callback(cfg->gpio_int1.port, &drv_data->int1_cb); + if (ret) { + LOG_ERR("Failed to set INT_1 gpio callback!"); + return -EIO; + } + } + + if (cfg->gpio_int2.port) { + ret = gpio_pin_configure_dt(&cfg->gpio_int2, GPIO_INPUT); + if (ret) { + return ret; + } + + gpio_init_callback(&drv_data->int2_cb, adxl345_int2_gpio_callback, + BIT(cfg->gpio_int2.pin)); + + ret = gpio_add_callback(cfg->gpio_int2.port, &drv_data->int2_cb); + if (ret) { + LOG_ERR("Failed to set INT_2 gpio callback!"); + return -EIO; + } + } + return 0; } diff --git a/dts/bindings/sensor/adi,adxl345-common.yaml b/dts/bindings/sensor/adi,adxl345-common.yaml index 0ce7191c67a7d..b56fcdd9e5c03 100644 --- a/dts/bindings/sensor/adi,adxl345-common.yaml +++ b/dts/bindings/sensor/adi,adxl345-common.yaml @@ -35,7 +35,7 @@ properties: type: int description: | Specify the FIFO watermark level in frame count. - Valid range: 1 - 31 + Valid range: 2 - 31 int1-gpios: type: phandle-array @@ -43,8 +43,6 @@ properties: The INT1 signal defaults to active high as produced by the sensor. The property value should ensure the flags properly describe the signal that is presented to the driver. - Either this or INT2 will be used to route interrupts. If both - are defined, then int1 is prioritized. int2-gpios: type: phandle-array @@ -52,3 +50,12 @@ properties: The INT2 signal defaults to active high as produced by the sensor. The property value should ensure the flags properly describe the signal that is presented to the driver. + + drdy-pin: + type: int + enum: [1, 2] + description: | + This property selects the interrupt line (INT1 or INT2) used to deliver + the data ready signal, if enabled. + - 1 + - 2