This patch adds support for hardware fifo. Fifo and stream mode are supported as well as fifo-full and fifo-watermark events. Signed-off-by: Octavian Purdila <octavian.purdila@xxxxxxxxx> --- drivers/iio/accel/bmc150-accel.c | 249 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 243 insertions(+), 6 deletions(-) diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c index 72d2dca..404cfec 100644 --- a/drivers/iio/accel/bmc150-accel.c +++ b/drivers/iio/accel/bmc150-accel.c @@ -67,7 +67,9 @@ #define BMC150_ACCEL_INT_MAP_0_BIT_SLOPE BIT(2) #define BMC150_ACCEL_REG_INT_MAP_1 0x1A -#define BMC150_ACCEL_INT_MAP_1_BIT_DATA BIT(0) +#define BMC150_ACCEL_INT_MAP_1_BIT_DATA BIT(0) +#define BMC150_ACCEL_INT_MAP_1_BIT_FWM BIT(1) +#define BMC150_ACCEL_INT_MAP_1_BIT_FFULL BIT(2) #define BMC150_ACCEL_REG_INT_RST_LATCH 0x21 #define BMC150_ACCEL_INT_MODE_LATCH_RESET 0x80 @@ -80,7 +82,9 @@ #define BMC150_ACCEL_INT_EN_BIT_SLP_Z BIT(2) #define BMC150_ACCEL_REG_INT_EN_1 0x17 -#define BMC150_ACCEL_INT_EN_BIT_DATA_EN BIT(4) +#define BMC150_ACCEL_INT_EN_BIT_DATA_EN BIT(4) +#define BMC150_ACCEL_INT_EN_BIT_FFULL_EN BIT(5) +#define BMC150_ACCEL_INT_EN_BIT_FWM_EN BIT(6) #define BMC150_ACCEL_REG_INT_OUT_CTRL 0x20 #define BMC150_ACCEL_INT_OUT_CTRL_INT1_LVL BIT(0) @@ -119,6 +123,13 @@ #define BMC150_ACCEL_AXIS_TO_REG(axis) (BMC150_ACCEL_REG_XOUT_L + (axis * 2)) #define BMC150_AUTO_SUSPEND_DELAY_MS 2000 +#define BMC150_ACCEL_REG_FIFO_STATUS 0x0E +#define BMC150_ACCEL_REG_FIFO_CONFIG0 0x30 +#define BMC150_ACCEL_REG_FIFO_CONFIG1 0x3E +#define BMC150_ACCEL_REG_FIFO_DATA 0x3F +#define BMC150_ACCEL_REG_FIFO_ +#define BMC150_ACCEL_FIFO_LENGTH 32 + enum bmc150_accel_axis { AXIS_X, AXIS_Y, @@ -177,12 +188,13 @@ struct bmc150_accel_event { u32 duration; u32 threshold; } slope; + u8 watermark; }; }; -#define BMC150_ACCEL_INTERRUPTS 2 +#define BMC150_ACCEL_INTERRUPTS 4 #define BMC150_ACCEL_TRIGGERS 2 -#define BMC150_ACCEL_EVENTS 1 +#define BMC150_ACCEL_EVENTS 3 struct bmc150_accel_data { struct i2c_client *client; @@ -191,6 +203,7 @@ struct bmc150_accel_data { struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS]; struct bmc150_accel_event events[BMC150_ACCEL_EVENTS]; struct mutex mutex; + int fifo_mode; s16 buffer[8]; u8 bw_bits; u32 range; @@ -483,6 +496,18 @@ bmc150_accel_interrupts[BMC150_ACCEL_INTERRUPTS] = { BMC150_ACCEL_INT_EN_BIT_SLP_Y | BMC150_ACCEL_INT_EN_BIT_SLP_Z }, + { /* fifo full interrupt */ + .map_reg = BMC150_ACCEL_REG_INT_MAP_1, + .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_FFULL, + .en_reg = BMC150_ACCEL_REG_INT_EN_1, + .en_bitmask = BMC150_ACCEL_INT_EN_BIT_FFULL_EN, + }, + { /* fifo watermark interrupt */ + .map_reg = BMC150_ACCEL_REG_INT_MAP_1, + .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_FWM, + .en_reg = BMC150_ACCEL_REG_INT_EN_1, + .en_bitmask = BMC150_ACCEL_INT_EN_BIT_FWM_EN, + }, }; static void bmc150_accel_interrupts_setup(struct iio_dev *indio_dev, @@ -699,11 +724,85 @@ static int bmc150_accel_read_raw(struct iio_dev *indio_dev, ret = bmc150_accel_get_bw(data, val, val2); mutex_unlock(&data->mutex); return ret; + case IIO_CHAN_INFO_FIFO_LENGTH: + *val2 = 0; + *val = BMC150_ACCEL_FIFO_LENGTH; + return IIO_VAL_INT; default: return -EINVAL; } } +static int bmc150_accel_fifo_flush(struct iio_dev *indio_dev) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + int ret, i; + u8 count; + u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3]; + u8 reg_fifo_data = BMC150_ACCEL_REG_FIFO_DATA; + struct i2c_msg msg[2]; + int64_t tstamp; + int sample_freq = 0, sec, ms; + + ret = bmc150_accel_get_bw(data, &sec, &ms); + if (ret == 0) + sample_freq = sec * 1000000000 + ms * 1000; + + ret = i2c_smbus_read_byte_data(data->client, + BMC150_ACCEL_REG_FIFO_STATUS); + if (ret < 0) { + dev_err(&indio_dev->dev, "Error reading reg_fifo_status\n"); + return ret; + } + + count = ret & 0x7F; + + i = i2c_smbus_read_byte_data(data->client, + BMC150_ACCEL_REG_FIFO_CONFIG1); + if (!count) + return 0; + + msg[0].addr = data->client->addr; + msg[0].flags = 0; + msg[0].buf = ®_fifo_data; + msg[0].len = sizeof(reg_fifo_data); + + msg[1].addr = data->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = (u8 *)buffer; + msg[1].len = count * 3 * 2; + + ret = i2c_transfer(data->client->adapter, msg, 2); + if (ret != 2) { + dev_err(&indio_dev->dev, "Error reading reg_fifo_data\n"); + return ret; + } + + if (!data->timestamp) + data->timestamp = iio_get_time_ns(); + + tstamp = data->timestamp - count * sample_freq; + + for (i = 0; i < count; i++) { + u16 sample[8]; + int j, bit; + + j = 0; + for_each_set_bit(bit, indio_dev->buffer->scan_mask, + indio_dev->masklength) { + memcpy(&sample[j++], &buffer[i * 3 + bit], 2); + } + + iio_push_to_buffers_with_timestamp(indio_dev, sample, tstamp); + + tstamp += sample_freq; + } + + data->timestamp = 0; + + return 0; +} + static int bmc150_accel_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -725,6 +824,8 @@ static int bmc150_accel_write_raw(struct iio_dev *indio_dev, ret = bmc150_accel_set_scale(data, val2); mutex_unlock(&data->mutex); return ret; + case IIO_CHAN_INFO_FIFO_FLUSH: + return bmc150_accel_fifo_flush(indio_dev); default: ret = -EINVAL; } @@ -764,6 +865,22 @@ static int bmc150_accel_event_roc_read(struct bmc150_accel_event *event, return IIO_VAL_INT; } +static int bmc150_accel_event_watermark_read(struct bmc150_accel_event *event, + enum iio_event_info info, + int *val, int *val2) +{ + *val2 = 0; + switch (info) { + case IIO_EV_INFO_VALUE: + *val = event->watermark; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; +} + static int bmc150_accel_read_event(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, @@ -807,6 +924,38 @@ static int bmc150_accel_event_roc_write(struct bmc150_accel_event *event, return ret; } +static int bmc150_accel_event_watermark_write(struct bmc150_accel_event *event, + enum iio_event_info info, + int val, int val2) +{ + struct bmc150_accel_data *data = event->data; + int ret; + + + switch (info) { + case IIO_EV_INFO_VALUE: + if (val > BMC150_ACCEL_FIFO_LENGTH) { + ret = -EINVAL; + break; + } + mutex_lock(&data->mutex); + ret = i2c_smbus_write_byte_data(data->client, + BMC150_ACCEL_REG_FIFO_CONFIG0, + val); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_fifo_config0\n"); + break; + } + event->watermark = val; + mutex_unlock(&data->mutex); + break; + default: + ret = -EINVAL; + } + + return ret; +} + static int bmc150_accel_write_event(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, @@ -900,6 +1049,76 @@ static const struct iio_event_spec bmc150_accel_events[BMC150_ACCEL_EVENTS] = { BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD) }, + { + .type = IIO_EV_TYPE_FIFO_FULL, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) + }, + { + .type = IIO_EV_TYPE_FIFO_WATERMARK, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) + }, +}; + + +static const char * const bmc150_accel_fifo_modes[] = { + "disabled", + "fifo", + "stream" +}; + +static int bmc150_accel_fifo_mode_set(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + int ret; + u8 val; + + switch (mode) { + case 0: + val = 0; + break; + case 1: + val = 0x40; + break; + case 2: + val = 0x80; + break; + default: + return -EINVAL; + } + + ret = i2c_smbus_write_byte_data(data->client, + BMC150_ACCEL_REG_FIFO_CONFIG1, val); + if (ret < 0) + dev_err(&indio_dev->dev, "Error writing reg_fifo_config1\n"); + + data->fifo_mode = mode; + + return ret; +} + +static int bmc150_accel_fifo_mode_get(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + + return data->fifo_mode; +} + +static const struct iio_enum bmc150_accel_fifo_mode_enum = { + .items = bmc150_accel_fifo_modes, + .num_items = ARRAY_SIZE(bmc150_accel_fifo_modes), + .set = bmc150_accel_fifo_mode_set, + .get = bmc150_accel_fifo_mode_get, +}; + +static const struct iio_chan_spec_ext_info bmc150_accel_ext_info[] = { + IIO_FIFO_EXT_INFO(&bmc150_accel_fifo_mode_enum, IIO_SHARED_BY_TYPE), + {}, }; #define BMC150_ACCEL_CHANNEL(_axis, bits) { \ @@ -908,7 +1127,9 @@ static const struct iio_event_spec bmc150_accel_events[BMC150_ACCEL_EVENTS] = { .channel2 = IIO_MOD_##_axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_FIFO_LENGTH) | \ + BIT(IIO_CHAN_INFO_FIFO_FLUSH), \ .scan_index = AXIS_##_axis, \ .scan_type = { \ .sign = 's', \ @@ -917,7 +1138,8 @@ static const struct iio_event_spec bmc150_accel_events[BMC150_ACCEL_EVENTS] = { .shift = 16 - (bits), \ }, \ .event_spec = bmc150_accel_events, \ - .num_event_specs = ARRAY_SIZE(bmc150_accel_events) \ + .num_event_specs = ARRAY_SIZE(bmc150_accel_events), \ + .ext_info = bmc150_accel_ext_info, \ } #define BMC150_ACCEL_CHANNELS(bits) { \ @@ -1286,6 +1508,17 @@ static struct { .read = bmc150_accel_event_roc_read, .write = bmc150_accel_event_roc_write, }, + { + .intr = 2, + .type = IIO_EV_TYPE_FIFO_FULL, + }, + { + .intr = 3, + .type = IIO_EV_TYPE_FIFO_WATERMARK, + .read = bmc150_accel_event_watermark_read, + .write = bmc150_accel_event_watermark_write, + }, + }; static void bmc150_accel_events_setup(struct iio_dev *indio_dev, @@ -1460,6 +1693,7 @@ static int bmc150_accel_resume(struct device *dev) mutex_lock(&data->mutex); if (atomic_read(&data->active_intr)) bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0); + bmc150_accel_fifo_mode_set(indio_dev, NULL, data->fifo_mode); mutex_unlock(&data->mutex); return 0; @@ -1489,6 +1723,9 @@ static int bmc150_accel_runtime_resume(struct device *dev) ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0); if (ret < 0) return ret; + ret = bmc150_accel_fifo_mode_set(indio_dev, NULL, data->fifo_mode); + if (ret < 0) + return ret; sleep_val = bmc150_accel_get_startup_times(data); if (sleep_val < 20) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html