mirror of
https://github.com/torvalds/linux.git
synced 2026-04-26 10:32:25 -04:00
Third set of new device support, cleanups and features for IIO in the 4.12 cycle Somewhat dominated in patch numbers of last of the outreachy application window related patches (they are still coming, despite window being closed which is good to see!) Good set of new drivers as well. New device support * ASPEED ADC - new driver * cpcap PMIC ADC - new driver * hid-humidity - driver for HID compatible humidity sensors. * ltc2497 ADC - new driver * mpu6050 - bring bindings up to date and add trivial support for 9250 * rockchip-saradc - update bindings to cover rk3328 * vl6180 light, proximity and time of flight sensor. - new driver Features * meson-saradc - add calibration Cleanup and minor fixes * ad5504 - constify attribute_group structure - drop casting of void * * ad7150 - replace some shifts of 1 by BIT macro usage * ad7152 - blank lines between function definitions * ad7280a - octal permissions. * ad7606 - replace use of core mlock mutex with a local lock * ad7746 - replace some shifts of 1 by BIT macro usage - function parameter alignment - drop some excessive brackets (introduced in last pull request) * ad7753 - white space cleanup * ad7754 - includes in alphabetical order and groupped appropriately. - change from missuse of internal mlock mutex to using the buffer lock to also protect values during frequency update. * ad779x - constify attribute_group structures * ad9832 - octal permissions * adis16060 - remove use of core mlock mutex in favour of adding a local _spi_write_then_read which can use the local buffer protection lock. - fix naming of above function. * adis16203 - remove locking during reads of calibbias that doesn't protect anything not protected elsewhere. * adis16209 - remove unnecessary braces in single statement if * adis16240 - remove unnecessary braces in single statement if * adt7136 - drop excess blank lines and put some in between functions. * ams-iaq - replace comma with semi colon. Not actual bug, just unusual syntax. * apds9960 - constify attribute group structure * as3935 - constify attribute group structure * bm1750 - constify attribute group structure * cros_ec - devm version of triggered buffer setup to simplify code. * exynos - drop casting of void * * hdc100x - constify attribute_group structure * hid-accel - fix wrong scale for newly introduced gravity sensor. * hts221 - drop casting of void * * hx711 - constify attribute_group structure * imx7d_adc - drop casting of void * * lm35333 - constify attribute_group structure * lsm6dsx - drop casting of void * - hold ODR configuration until enabling to avoid a race condition. * max1027 - drop casting of void * * max11100 - fix a comma where semicolon was intended (no actual bug, just odd) * max1363 - constify attribute_group structure * ms sensors - drop casting of void * * rockchip_saradc - drop casting of void * * sun4i-gpadc - fix missing dependency on THERMAL or presence of stubs (issue only introduced in pervious set) - drop casting of void * * tsl2x7x - fix wrong standard deviation calc. Note these aren't actually used for anything at the moment so bug didn't really matter. - constify attribute group structure. * vf610adc - drop casting of void * * vz89x - replace comma with semicolon. Not actual bug, just odd syntax. * zpa2326 - drop casting of void *
456 lines
11 KiB
C
456 lines
11 KiB
C
/*
|
|
* STMicroelectronics st_lsm6dsx FIFO buffer library driver
|
|
*
|
|
* LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM: 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.
|
|
* 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>
|
|
*
|
|
* Licensed under the GPL-2.
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/iio/kfifo_buf.h>
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/buffer.h>
|
|
|
|
#include "st_lsm6dsx.h"
|
|
|
|
#define ST_LSM6DSX_REG_FIFO_THL_ADDR 0x06
|
|
#define ST_LSM6DSX_REG_FIFO_THH_ADDR 0x07
|
|
#define ST_LSM6DSX_FIFO_TH_MASK GENMASK(11, 0)
|
|
#define ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR 0x08
|
|
#define ST_LSM6DSX_REG_FIFO_MODE_ADDR 0x0a
|
|
#define ST_LSM6DSX_FIFO_MODE_MASK GENMASK(2, 0)
|
|
#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3)
|
|
#define ST_LSM6DSX_REG_FIFO_DIFFL_ADDR 0x3a
|
|
#define ST_LSM6DSX_FIFO_DIFF_MASK GENMASK(11, 0)
|
|
#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12)
|
|
#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e
|
|
|
|
#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08
|
|
|
|
struct st_lsm6dsx_decimator_entry {
|
|
u8 decimator;
|
|
u8 val;
|
|
};
|
|
|
|
static const
|
|
struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = {
|
|
{ 0, 0x0 },
|
|
{ 1, 0x1 },
|
|
{ 2, 0x2 },
|
|
{ 3, 0x3 },
|
|
{ 4, 0x4 },
|
|
{ 8, 0x5 },
|
|
{ 16, 0x6 },
|
|
{ 32, 0x7 },
|
|
};
|
|
|
|
static int st_lsm6dsx_get_decimator_val(u8 val)
|
|
{
|
|
const int max_size = ARRAY_SIZE(st_lsm6dsx_decimator_table);
|
|
int i;
|
|
|
|
for (i = 0; i < max_size; i++)
|
|
if (st_lsm6dsx_decimator_table[i].decimator == val)
|
|
break;
|
|
|
|
return i == max_size ? 0 : st_lsm6dsx_decimator_table[i].val;
|
|
}
|
|
|
|
static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
|
|
u16 *max_odr, u16 *min_odr)
|
|
{
|
|
struct st_lsm6dsx_sensor *sensor;
|
|
int i;
|
|
|
|
*max_odr = 0, *min_odr = ~0;
|
|
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
|
sensor = iio_priv(hw->iio_devs[i]);
|
|
|
|
if (!(hw->enable_mask & BIT(sensor->id)))
|
|
continue;
|
|
|
|
*max_odr = max_t(u16, *max_odr, sensor->odr);
|
|
*min_odr = min_t(u16, *min_odr, sensor->odr);
|
|
}
|
|
}
|
|
|
|
static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
|
|
{
|
|
struct st_lsm6dsx_sensor *sensor;
|
|
u16 max_odr, min_odr, sip = 0;
|
|
int err, i;
|
|
u8 data;
|
|
|
|
st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr);
|
|
|
|
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
|
sensor = iio_priv(hw->iio_devs[i]);
|
|
|
|
/* update fifo decimators and sample in pattern */
|
|
if (hw->enable_mask & BIT(sensor->id)) {
|
|
sensor->sip = sensor->odr / min_odr;
|
|
sensor->decimator = max_odr / sensor->odr;
|
|
data = st_lsm6dsx_get_decimator_val(sensor->decimator);
|
|
} else {
|
|
sensor->sip = 0;
|
|
sensor->decimator = 0;
|
|
data = 0;
|
|
}
|
|
|
|
err = st_lsm6dsx_write_with_mask(hw,
|
|
ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR,
|
|
sensor->decimator_mask, data);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
sip += sensor->sip;
|
|
}
|
|
hw->sip = sip;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
|
|
enum st_lsm6dsx_fifo_mode fifo_mode)
|
|
{
|
|
u8 data;
|
|
int err;
|
|
|
|
switch (fifo_mode) {
|
|
case ST_LSM6DSX_FIFO_BYPASS:
|
|
data = fifo_mode;
|
|
break;
|
|
case ST_LSM6DSX_FIFO_CONT:
|
|
data = (ST_LSM6DSX_MAX_FIFO_ODR_VAL <<
|
|
__ffs(ST_LSM6DSX_FIFO_ODR_MASK)) | fifo_mode;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
|
|
sizeof(data), &data);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
hw->fifo_mode = fifo_mode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
|
|
{
|
|
u16 fifo_watermark = ~0, cur_watermark, sip = 0;
|
|
struct st_lsm6dsx_hw *hw = sensor->hw;
|
|
struct st_lsm6dsx_sensor *cur_sensor;
|
|
__le16 wdata;
|
|
int i, err;
|
|
u8 data;
|
|
|
|
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
|
cur_sensor = iio_priv(hw->iio_devs[i]);
|
|
|
|
if (!(hw->enable_mask & BIT(cur_sensor->id)))
|
|
continue;
|
|
|
|
cur_watermark = (cur_sensor == sensor) ? watermark
|
|
: cur_sensor->watermark;
|
|
|
|
fifo_watermark = min_t(u16, fifo_watermark, cur_watermark);
|
|
sip += cur_sensor->sip;
|
|
}
|
|
|
|
if (!sip)
|
|
return 0;
|
|
|
|
fifo_watermark = max_t(u16, fifo_watermark, sip);
|
|
fifo_watermark = (fifo_watermark / sip) * sip;
|
|
fifo_watermark = fifo_watermark * ST_LSM6DSX_SAMPLE_DEPTH;
|
|
|
|
mutex_lock(&hw->lock);
|
|
|
|
err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_THH_ADDR,
|
|
sizeof(data), &data);
|
|
if (err < 0)
|
|
goto out;
|
|
|
|
fifo_watermark = ((data << 8) & ~ST_LSM6DSX_FIFO_TH_MASK) |
|
|
(fifo_watermark & ST_LSM6DSX_FIFO_TH_MASK);
|
|
|
|
wdata = cpu_to_le16(fifo_watermark);
|
|
err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_THL_ADDR,
|
|
sizeof(wdata), (u8 *)&wdata);
|
|
out:
|
|
mutex_unlock(&hw->lock);
|
|
|
|
return err < 0 ? err : 0;
|
|
}
|
|
|
|
/**
|
|
* st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DS3H-LSM6DSL-LSM6DSM read FIFO 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
|
|
*/
|
|
static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
|
{
|
|
u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE;
|
|
int err, acc_sip, gyro_sip, read_len, samples, offset;
|
|
struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor;
|
|
s64 acc_ts, acc_delta_ts, gyro_ts, gyro_delta_ts;
|
|
u8 iio_buff[ALIGN(ST_LSM6DSX_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)];
|
|
u8 buff[pattern_len];
|
|
__le16 fifo_status;
|
|
|
|
err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_DIFFL_ADDR,
|
|
sizeof(fifo_status), (u8 *)&fifo_status);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK))
|
|
return 0;
|
|
|
|
fifo_len = (le16_to_cpu(fifo_status) & ST_LSM6DSX_FIFO_DIFF_MASK) *
|
|
ST_LSM6DSX_CHAN_SIZE;
|
|
samples = fifo_len / ST_LSM6DSX_SAMPLE_SIZE;
|
|
fifo_len = (fifo_len / pattern_len) * pattern_len;
|
|
|
|
/*
|
|
* compute delta timestamp between two consecutive samples
|
|
* in order to estimate queueing time of data generated
|
|
* by the sensor
|
|
*/
|
|
acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
|
|
acc_ts = acc_sensor->ts - acc_sensor->delta_ts;
|
|
acc_delta_ts = div_s64(acc_sensor->delta_ts * acc_sensor->decimator,
|
|
samples);
|
|
|
|
gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
|
|
gyro_ts = gyro_sensor->ts - gyro_sensor->delta_ts;
|
|
gyro_delta_ts = div_s64(gyro_sensor->delta_ts * gyro_sensor->decimator,
|
|
samples);
|
|
|
|
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
|
|
err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_OUTL_ADDR,
|
|
sizeof(buff), buff);
|
|
if (err < 0)
|
|
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), 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, Gx, Gy, Gz
|
|
*/
|
|
gyro_sip = gyro_sensor->sip;
|
|
acc_sip = acc_sensor->sip;
|
|
offset = 0;
|
|
|
|
while (acc_sip > 0 || gyro_sip > 0) {
|
|
if (gyro_sip-- > 0) {
|
|
memcpy(iio_buff, &buff[offset],
|
|
ST_LSM6DSX_SAMPLE_SIZE);
|
|
iio_push_to_buffers_with_timestamp(
|
|
hw->iio_devs[ST_LSM6DSX_ID_GYRO],
|
|
iio_buff, gyro_ts);
|
|
offset += ST_LSM6DSX_SAMPLE_SIZE;
|
|
gyro_ts += gyro_delta_ts;
|
|
}
|
|
|
|
if (acc_sip-- > 0) {
|
|
memcpy(iio_buff, &buff[offset],
|
|
ST_LSM6DSX_SAMPLE_SIZE);
|
|
iio_push_to_buffers_with_timestamp(
|
|
hw->iio_devs[ST_LSM6DSX_ID_ACC],
|
|
iio_buff, acc_ts);
|
|
offset += ST_LSM6DSX_SAMPLE_SIZE;
|
|
acc_ts += acc_delta_ts;
|
|
}
|
|
}
|
|
}
|
|
|
|
return read_len;
|
|
}
|
|
|
|
static int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw)
|
|
{
|
|
int err;
|
|
|
|
mutex_lock(&hw->fifo_lock);
|
|
|
|
st_lsm6dsx_read_fifo(hw);
|
|
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS);
|
|
|
|
mutex_unlock(&hw->fifo_lock);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
|
|
{
|
|
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
|
struct st_lsm6dsx_hw *hw = sensor->hw;
|
|
int err;
|
|
|
|
if (hw->fifo_mode != ST_LSM6DSX_FIFO_BYPASS) {
|
|
err = st_lsm6dsx_flush_fifo(hw);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
if (enable) {
|
|
err = st_lsm6dsx_sensor_enable(sensor);
|
|
if (err < 0)
|
|
return err;
|
|
} else {
|
|
err = st_lsm6dsx_sensor_disable(sensor);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
err = st_lsm6dsx_update_decimators(hw);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = st_lsm6dsx_update_watermark(sensor, sensor->watermark);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (hw->enable_mask) {
|
|
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
/*
|
|
* store enable buffer timestamp as reference to compute
|
|
* first delta timestamp
|
|
*/
|
|
sensor->ts = iio_get_time_ns(iio_dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
|
|
{
|
|
struct st_lsm6dsx_hw *hw = private;
|
|
struct st_lsm6dsx_sensor *sensor;
|
|
int i;
|
|
|
|
if (!hw->sip)
|
|
return IRQ_NONE;
|
|
|
|
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
|
sensor = iio_priv(hw->iio_devs[i]);
|
|
|
|
if (sensor->sip > 0) {
|
|
s64 timestamp;
|
|
|
|
timestamp = iio_get_time_ns(hw->iio_devs[i]);
|
|
sensor->delta_ts = timestamp - sensor->ts;
|
|
sensor->ts = timestamp;
|
|
}
|
|
}
|
|
|
|
return IRQ_WAKE_THREAD;
|
|
}
|
|
|
|
static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
|
|
{
|
|
struct st_lsm6dsx_hw *hw = private;
|
|
int count;
|
|
|
|
mutex_lock(&hw->fifo_lock);
|
|
count = st_lsm6dsx_read_fifo(hw);
|
|
mutex_unlock(&hw->fifo_lock);
|
|
|
|
return !count ? IRQ_NONE : IRQ_HANDLED;
|
|
}
|
|
|
|
static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev)
|
|
{
|
|
return st_lsm6dsx_update_fifo(iio_dev, true);
|
|
}
|
|
|
|
static int st_lsm6dsx_buffer_postdisable(struct iio_dev *iio_dev)
|
|
{
|
|
return st_lsm6dsx_update_fifo(iio_dev, false);
|
|
}
|
|
|
|
static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
|
|
.preenable = st_lsm6dsx_buffer_preenable,
|
|
.postdisable = st_lsm6dsx_buffer_postdisable,
|
|
};
|
|
|
|
int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
|
|
{
|
|
struct iio_buffer *buffer;
|
|
unsigned long irq_type;
|
|
int i, err;
|
|
|
|
irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
|
|
|
|
switch (irq_type) {
|
|
case IRQF_TRIGGER_HIGH:
|
|
case IRQF_TRIGGER_RISING:
|
|
break;
|
|
default:
|
|
dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = devm_request_threaded_irq(hw->dev, hw->irq,
|
|
st_lsm6dsx_handler_irq,
|
|
st_lsm6dsx_handler_thread,
|
|
irq_type | IRQF_ONESHOT,
|
|
"lsm6dsx", hw);
|
|
if (err) {
|
|
dev_err(hw->dev, "failed to request trigger irq %d\n",
|
|
hw->irq);
|
|
return err;
|
|
}
|
|
|
|
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
|
buffer = devm_iio_kfifo_allocate(hw->dev);
|
|
if (!buffer)
|
|
return -ENOMEM;
|
|
|
|
iio_device_attach_buffer(hw->iio_devs[i], buffer);
|
|
hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
|
|
hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops;
|
|
}
|
|
|
|
return 0;
|
|
}
|