// SPDX-License-Identifier: GPL-2.0-only /* * STMicroelectronics st_lsm6dsx FIFO buffer library driver * * Pattern FIFO: * The FIFO buffer can be configured to store data from gyroscope and * accelerometer. Samples are queued without any tag according to a * specific pattern based on 'FIFO data sets' (6 bytes each): * - 1st data set is reserved for gyroscope data * - 2nd data set is reserved for accelerometer data * The FIFO pattern changes depending on the ODRs and decimation factors * assigned to the FIFO data sets. The first sequence of data stored in FIFO * buffer contains the data of all the enabled FIFO data sets * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the * value of the decimation factor and ODR set for each FIFO data set. * * Supported devices: * - ISM330DLC * - LSM6DS3 * - LSM6DS3H * - LSM6DS3TR-C * - LSM6DSL * - LSM6DSM * * Tagged FIFO: * The FIFO buffer can be configured to store data from gyroscope and * accelerometer. Each sample is queued with a tag (1B) indicating data * source (gyroscope, accelerometer, hw timer). * * Supported devices: * - ASM330LHB * - ASM330LHH * - ASM330LHHX * - ASM330LHHXG1 * - ISM330DHCX * - LSM6DSO * - LSM6DSOP * - LSM6DSOX * - LSM6DSR * - LSM6DSRX * - LSM6DST * - LSM6DSTX * - LSM6DSV * * FIFO supported modes: * - BYPASS: FIFO disabled * - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index * restarts from the beginning and the oldest sample is overwritten * * Copyright 2016 STMicroelectronics Inc. * * Lorenzo Bianconi <lorenzo.bianconi@st.com> * Denis Ciocca <denis.ciocca@st.com>
*/ #include <linux/module.h> #include <linux/iio/kfifo_buf.h> #include <linux/iio/iio.h> #include <linux/iio/buffer.h> #include <linux/regmap.h> #include <linux/bitfield.h>
/* * update hw ts decimator if necessary. Decimator for hw timestamp * is always 1 or 0 in order to have a ts sample for each data * sample in FIFO
*/
ts_dec_reg = &hw->settings->ts_settings.decimator; if (ts_dec_reg->addr) { int val, ts_dec = !!hw->ts_sip;
/* * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN/ST_LSM6DSX_MAX_TAGGED_WORD_LEN * in order to avoid a kmalloc for each bus access
*/ staticinlineint st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr,
u8 *data, unsignedint data_len, unsignedint max_word_len)
{ unsignedint word_len, read_len = 0; int err;
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
err = st_lsm6dsx_read_block(hw, ST_LSM6DSX_REG_FIFO_OUTL_ADDR,
hw->buff, pattern_len,
ST_LSM6DSX_MAX_WORD_LEN); if (err < 0) {
dev_err(hw->dev, "failed to read pattern from fifo (err=%d)\n",
err); return err;
}
/* * Data are written to the FIFO with a specific pattern * depending on the configured ODRs. The first sequence of data * stored in FIFO contains the data of all enabled sensors * (e.g. Gx, Gy, Gz, Ax, Ay, Az, Ts), then data are repeated * depending on the value of the decimation factor set for each * sensor. * * Supposing the FIFO is storing data from gyroscope and * accelerometer at different ODRs: * - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz * Since the gyroscope ODR is twice the accelerometer one, the * following pattern is repeated every 9 samples: * - Gx, Gy, Gz, Ax, Ay, Az, Ts, Gx, Gy, Gz, Ts, Gx, ..
*/
ext_sip = ext_sensor ? ext_sensor->sip : 0;
gyro_sip = gyro_sensor->sip;
acc_sip = acc_sensor->sip;
ts_sip = hw->ts_sip;
offset = 0;
sip = 0;
if (ts_sip-- > 0) {
u8 data[ST_LSM6DSX_SAMPLE_SIZE];
memcpy(data, &hw->buff[offset], sizeof(data)); /* * hw timestamp is 3B long and it is stored * in FIFO using 6B as 4th FIFO data set * according to this schema: * B0 = ts[15:8], B1 = ts[23:16], B3 = ts[7:0]
*/
ts = data[1] << 16 | data[0] << 8 | data[3]; /* * check if hw timestamp engine is going to * reset (the sensor generates an interrupt * to signal the hw timestamp will reset in * 1.638s)
*/ if (!reset_ts && ts >= 0xff0000)
reset_ts = true;
ts *= hw->ts_gain;
offset += ST_LSM6DSX_SAMPLE_SIZE;
}
if (gyro_sip > 0 && !(sip % gyro_sensor->decimator)) { /* * We need to discards gyro samples during * filters settling time
*/ if (gyro_sensor->samples_to_discard > 0)
gyro_sensor->samples_to_discard--; else
iio_push_to_buffers_with_timestamp(
hw->iio_devs[ST_LSM6DSX_ID_GYRO],
&hw->scan[ST_LSM6DSX_ID_GYRO],
gyro_sensor->ts_ref + ts);
gyro_sip--;
} if (acc_sip > 0 && !(sip % acc_sensor->decimator)) { /* * We need to discards accel samples during * filters settling time
*/ if (acc_sensor->samples_to_discard > 0)
acc_sensor->samples_to_discard--; else
iio_push_to_buffers_with_timestamp(
hw->iio_devs[ST_LSM6DSX_ID_ACC],
&hw->scan[ST_LSM6DSX_ID_ACC],
acc_sensor->ts_ref + ts);
acc_sip--;
} if (ext_sip > 0 && !(sip % ext_sensor->decimator)) {
iio_push_to_buffers_with_timestamp(
hw->iio_devs[ST_LSM6DSX_ID_EXT0],
&hw->scan[ST_LSM6DSX_ID_EXT0],
ext_sensor->ts_ref + ts);
ext_sip--;
}
sip++;
}
}
if (unlikely(reset_ts)) {
err = st_lsm6dsx_reset_hw_ts(hw); if (err < 0) {
dev_err(hw->dev, "failed to reset hw ts (err=%d)\n",
err); return err;
}
} return read_len;
}
/* invalid sample during bootstrap phase */ if (val >= ST_LSM6DSX_INVALID_SAMPLE) return -EINVAL;
/* * EXT_TAG are managed in FIFO fashion so ST_LSM6DSX_EXT0_TAG * corresponds to the first enabled channel, ST_LSM6DSX_EXT1_TAG * to the second one and ST_LSM6DSX_EXT2_TAG to the last enabled * channel
*/ switch (tag) { case ST_LSM6DSX_GYRO_TAG:
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_GYRO]; break; case ST_LSM6DSX_ACC_TAG:
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC]; break; case ST_LSM6DSX_EXT0_TAG: if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0))
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0]; elseif (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1]; else
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; break; case ST_LSM6DSX_EXT1_TAG: if ((hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0)) &&
(hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1)))
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1]; else
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; break; case ST_LSM6DSX_EXT2_TAG:
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; break; default: return -EINVAL;
}
/** * st_lsm6dsx_read_tagged_fifo() - tagged hw FIFO read routine * @hw: Pointer to instance of struct st_lsm6dsx_hw. * * Read samples from the hw FIFO and push them to IIO buffers. * * Return: Number of bytes read from the FIFO
*/ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
{
u16 pattern_len = hw->sip * ST_LSM6DSX_TAGGED_SAMPLE_SIZE;
u16 fifo_len, fifo_diff_mask; /* * Alignment needed as this can ultimately be passed to a * call to iio_push_to_buffers_with_timestamp() which * must be passed a buffer that is aligned to 8 bytes so * as to allow insertion of a naturally aligned timestamp.
*/
u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8);
u8 tag; bool reset_ts = false; int i, err, read_len;
__le16 fifo_status;
s64 ts = 0;
err = st_lsm6dsx_read_locked(hw,
hw->settings->fifo_ops.fifo_diff.addr,
&fifo_status, sizeof(fifo_status)); if (err < 0) {
dev_err(hw->dev, "failed to read fifo status (err=%d)\n",
err); return err;
}
if (!pattern_len)
pattern_len = ST_LSM6DSX_TAGGED_SAMPLE_SIZE;
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
err = st_lsm6dsx_read_block(hw,
ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR,
hw->buff, pattern_len,
ST_LSM6DSX_MAX_TAGGED_WORD_LEN); if (err < 0) {
dev_err(hw->dev, "failed to read pattern from fifo (err=%d)\n",
err); return err;
}
for (i = 0; i < pattern_len;
i += ST_LSM6DSX_TAGGED_SAMPLE_SIZE) {
memcpy(iio_buff, &hw->buff[i + ST_LSM6DSX_TAG_SIZE],
ST_LSM6DSX_SAMPLE_SIZE);
tag = hw->buff[i] >> 3; if (tag == ST_LSM6DSX_TS_TAG) { /* * hw timestamp is 4B long and it is stored * in FIFO according to this schema: * B0 = ts[7:0], B1 = ts[15:8], B2 = ts[23:16], * B3 = ts[31:24]
*/
ts = le32_to_cpu(*((__le32 *)iio_buff)); /* * check if hw timestamp engine is going to * reset (the sensor generates an interrupt * to signal the hw timestamp will reset in * 1.638s)
*/ if (!reset_ts && ts >= 0xffff0000)
reset_ts = true;
ts *= hw->ts_gain;
} else {
st_lsm6dsx_push_tagged_data(hw, tag, iio_buff,
ts);
}
}
}
if (unlikely(reset_ts)) {
err = st_lsm6dsx_reset_hw_ts(hw); if (err < 0) return err;
} return read_len;
}
int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw)
{ int err;
if (!hw->settings->fifo_ops.read_fifo) return -ENOTSUPP;
if (sensor->id != ST_LSM6DSX_ID_GYRO &&
sensor->id != ST_LSM6DSX_ID_ACC) return;
/* check if drdy mask is supported in hw */ if (hw->settings->drdy_mask.addr) return;
data = &hw->settings->samples_to_discard[sensor->id]; for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) { if (data->val[i].milli_hz == sensor->odr) {
sensor->samples_to_discard = data->val[i].samples; return;
}
}
}
int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable)
{ struct st_lsm6dsx_hw *hw = sensor->hw;
u8 fifo_mask; int err;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.