Re: [RFC 8/8] iio: bmc150: add support for hardware fifo

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Octavian Purdila writes:
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>
A few minor bits inline.  The implementation seems fine, I am just
unconvinced that the user interface is the best way to go.
---
 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 = &reg_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;

Hmm... These are likely to be less than accurate...
+
+	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:
Some defines and a local enum should get rid of the magic numbers here.
+		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
--
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




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux