[PATCH 2/2] staging: iio: imu: Add CEVA BNO08x driver

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

 



From: Jacopo Mondi <jacopo@xxxxxxxxxx>

Add support for CEVA BNO08x Sensor Hub.

The BNO08X family (BNO080/85/86) is a System in Package (SiP) that
integrates a triaxial accelerometer, triaxial gyroscope, magnetometer
and a 32-bit ARM Cortex-M0+ microcontroller running CEVA's SH-2
firmware.

Datasheet:
https://www.ceva-dsp.com/wp-content/uploads/2019/10/BNO080_085-Datasheet.pdf

Signed-off-by: Jacopo Mondi <jacopo@xxxxxxxxxx>
---
 MAINTAINERS                             |   7 +
 drivers/staging/iio/Kconfig             |   1 +
 drivers/staging/iio/Makefile            |   1 +
 drivers/staging/iio/imu/bno08x/Kconfig  |  11 +
 drivers/staging/iio/imu/bno08x/Makefile |   3 +
 drivers/staging/iio/imu/bno08x/bno08x.c | 618 ++++++++++++++++++++++++
 6 files changed, 641 insertions(+)
 create mode 100644 drivers/staging/iio/imu/bno08x/Kconfig
 create mode 100644 drivers/staging/iio/imu/bno08x/Makefile
 create mode 100644 drivers/staging/iio/imu/bno08x/bno08x.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 96b8c3560117..4b709599c82b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4314,6 +4314,13 @@ F:	certs/
 F:	scripts/extract-cert.c
 F:	scripts/sign-file.c
 
+CEVA BNO08x SENSOR HUB
+ML	Jacopo Mondi <jacopo@xxxxxxxxxx>
+L:	linux-iio@xxxxxxxxxxxxxxx
+S:	Odd Fixes
+F:	Documentation/devicetree/bindings/staging/iio/imu/ceva,bno08x.yaml
+F	drivers/staging/iio/imu/bno08x/
+
 CFAG12864B LCD DRIVER
 M:	Miguel Ojeda <ojeda@xxxxxxxxxx>
 S:	Maintained
diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index a8e970db179d..bcecace954a5 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -11,6 +11,7 @@ source "drivers/staging/iio/addac/Kconfig"
 source "drivers/staging/iio/cdc/Kconfig"
 source "drivers/staging/iio/frequency/Kconfig"
 source "drivers/staging/iio/impedance-analyzer/Kconfig"
+source "drivers/staging/iio/imu/Kconfig"
 source "drivers/staging/iio/meter/Kconfig"
 source "drivers/staging/iio/resolver/Kconfig"
 
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index b15904b99581..eafcc25b64f9 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -9,5 +9,6 @@ obj-y += addac/
 obj-y += cdc/
 obj-y += frequency/
 obj-y += impedance-analyzer/
+obj-y += imu/
 obj-y += meter/
 obj-y += resolver/
diff --git a/drivers/staging/iio/imu/bno08x/Kconfig b/drivers/staging/iio/imu/bno08x/Kconfig
new file mode 100644
index 000000000000..109ac11ed4f9
--- /dev/null
+++ b/drivers/staging/iio/imu/bno08x/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config CEVA_BNO08x_IMU
+	tristate "CEVA BNO08x IMU driver"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say yes here to build support for CEVA BNO08x IMU.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called bno08x.
diff --git a/drivers/staging/iio/imu/bno08x/Makefile b/drivers/staging/iio/imu/bno08x/Makefile
new file mode 100644
index 000000000000..48bbbf12a366
--- /dev/null
+++ b/drivers/staging/iio/imu/bno08x/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_CEVA_BNO08x_IMU) += bno08x.o
diff --git a/drivers/staging/iio/imu/bno08x/bno08x.c b/drivers/staging/iio/imu/bno08x/bno08x.c
new file mode 100644
index 000000000000..e07735a85b34
--- /dev/null
+++ b/drivers/staging/iio/imu/bno08x/bno08x.c
@@ -0,0 +1,618 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CEVA BNO08x IMU driver.
+ *
+ * Copyright (c) 2021 Jacopo Mondi <jacopo@xxxxxxxxxx>
+ */
+
+#include <linux/atomic.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+
+#define DRIVER_NAME "bno08x"
+
+#define BNO08x_SHTP_HDR_SIZE		0x4
+#define BNO08x_SHTP_HDR_LEN_LSB		0x0
+#define BNO08x_SHTP_HDR_LEN_MSB		0x1
+#define BNO08x_SHTP_HDR_CHAN		0x2
+#define BNO08x_SHTP_HDR_SEQ		0x3
+
+#define BNO08x_SHTP_MAX_SIZE		128
+#define BNO08x_SHTP_ADV_MAX_SIZE	512
+
+#define BNO08x_SHTP_SOFT_RESET		0x01
+#define BNO08x_SHTP_PROD_ID		0xf8
+#define BNO08x_SHTP_PROD_ID_REQ		0xf9
+
+#define BNO08x_SHTP_GET_FEATURE_RESP	0xfc
+#define BNO08x_SHTP_SET_FEATURE_CMD	0xfd
+#define BNO08x_SHTP_GET_FEATURE_REQ	0xfe
+
+#define BNO08x_ROT_SCAN_INDEX		0x01
+#define BNO08x_REPORTID_ROTATION_VEC	0x05
+#define BNO08x_REPORTID_TIMESTAMP_BASE	0xfb
+
+enum bno08x_shtp_channels {
+	BNO08x_SHTP_COMMAND_CHAN,
+	BNO08x_SHTP_EXECTUABLE_CHAN,
+	BNO08x_SHTP_CONTROL_CHAN,
+	BNO08x_SHTP_REPORTS_CHAN,
+	BNO08x_SHTP_WAKE_REPORTS_CHAN,
+	BNO08x_SHTP_GYRO_CHAN,
+	BNO08x_NUM_CHANNELS,
+};
+
+struct bno08x_dev {
+	struct i2c_client *client;
+
+	struct iio_trigger *trig;
+
+	/* Completions for cargo transfer. */
+	struct completion cargo_ready;
+	struct completion waiters_done;
+
+	/* cargo sequence number per channel. */
+	u32 seq_num[BNO08x_NUM_CHANNELS];
+
+	/* Mask of the enabled report ids. */
+	u64 enabled_reports_mask;
+
+#define BNO08x_CARGO_BUFFER_SIZE	1024lu
+	struct {
+		u8 buffer[BNO08x_CARGO_BUFFER_SIZE];
+		size_t len;
+		atomic_t waiters;
+
+		/* Protects access to the cargo buffer content. */
+		struct mutex mutex;
+	} cargo;
+};
+
+static irqreturn_t bno08x_interrupt(int irq, void *dev_id)
+{
+	struct iio_dev *iio_dev = dev_id;
+
+	if (iio_dev->trig)
+		iio_trigger_poll((void *)iio_dev->trig);
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t bno08x_dump_cargo(int irq, void *dev_id)
+{
+	struct iio_dev *iio_dev = dev_id;
+	struct bno08x_dev *bno08x = iio_priv(iio_dev);
+	struct i2c_client *client = bno08x->client;
+	u8 *cargo = bno08x->cargo.buffer;
+	size_t to_read;
+	size_t len;
+	int ret;
+
+	mutex_lock(&bno08x->cargo.mutex);
+
+	/* Read only the header first to know how many bytes we expect to receive. */
+	ret = i2c_master_recv(client, bno08x->cargo.buffer, BNO08x_SHTP_HDR_SIZE);
+	if (ret != BNO08x_SHTP_HDR_SIZE)
+		goto out_unlock;
+
+	/*
+	 * Clear the top bit: it means a cargo is a continuation of the last one.
+	 * Ignore it for now.
+	 */
+	len = (cargo[BNO08x_SHTP_HDR_LEN_MSB] << 8 |
+	       cargo[BNO08x_SHTP_HDR_LEN_LSB]) & ~BIT(15);
+
+	if (len == 0)
+		goto out_complete;
+
+	if (len > BNO08x_CARGO_BUFFER_SIZE)
+		dev_warn(&client->dev,
+			 "Cargo size exceeds buffer: content will be unusable\n");
+
+	/*
+	 * Read the full cargo now that we know its length.
+	 *
+	 * If the reported length exceeds the max transfer size, read the cargo
+	 * in chunks. Its content will be unusable though.
+	 */
+	to_read = len;
+	len = min(len, BNO08x_CARGO_BUFFER_SIZE);
+	while (to_read) {
+		memset(bno08x->cargo.buffer, 0, len);
+
+		ret = i2c_master_recv(client, bno08x->cargo.buffer, len);
+		if (ret != len) {
+			dev_err(&client->dev,
+				"Failed to read cargo of size %lu: %d\n",
+				len, ret);
+			goto out_complete;
+		}
+
+		to_read -= len;
+	}
+
+	bno08x->cargo.len = len;
+
+out_complete:
+	if (atomic_read(&bno08x->cargo.waiters) == 0)
+		goto out_unlock;
+
+	/*
+	 * Wake up all waiters and let them read the cargo buffer.
+	 *
+	 * Unlock the cargo mutex only after all waiters are done, to avoid
+	 * this interrupt handler re-writing the buffer content with the next
+	 * cargo before waiters have read it.
+	 */
+	complete_all(&bno08x->cargo_ready);
+
+	wait_for_completion(&bno08x->waiters_done);
+	reinit_completion(&bno08x->waiters_done);
+
+out_unlock:
+	mutex_unlock(&bno08x->cargo.mutex);
+
+	return IRQ_HANDLED;
+}
+
+static int bno08x_write_cargo(struct bno08x_dev *bno08x,
+			      enum bno08x_shtp_channels channel,
+			      u8 *cargo, u16 length)
+{
+	u16 cargo_length = length + BNO08x_SHTP_HDR_SIZE;
+	struct i2c_client *client = bno08x->client;
+	int ret;
+
+	/* Assemble header */
+	cargo[BNO08x_SHTP_HDR_LEN_LSB] = cargo_length & 0xff;
+	cargo[BNO08x_SHTP_HDR_LEN_MSB] = cargo_length >> 8;
+	cargo[BNO08x_SHTP_HDR_CHAN] = channel;
+	cargo[BNO08x_SHTP_HDR_SEQ] = bno08x->seq_num[channel]++;
+
+	ret = i2c_master_send(client, cargo, cargo_length);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed to write cargo to channel %u: %d\n",
+			channel, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int bno08x_wait_for_cargo_timeout(struct bno08x_dev *bno08x, u8 *cargo,
+					 size_t len)
+{
+	static const unsigned long complete_timeout = 500; /* msecs */
+	struct i2c_client *client = bno08x->client;
+	int ret = 0;
+
+	atomic_inc(&bno08x->cargo.waiters);
+
+	if (!wait_for_completion_timeout(&bno08x->cargo_ready,
+					 msecs_to_jiffies(complete_timeout))) {
+		dev_dbg(&client->dev, "Wait for chip interrupt timeout.\n");
+		ret = -EIO;
+		goto out_unlock;
+	}
+
+	/* The caller is not interested in the data. */
+	if (!len)
+		goto out_unlock;
+
+	/*
+	 * Multiple readers might want to inspect the cargo content in response
+	 * to the complete_all(cargo_ready) signal.
+	 *
+	 * Copy data into the caller buffer to allow multiple threads to
+	 * access the cargo content independently.
+	 */
+	ret = min(len, bno08x->cargo.len);
+	memcpy(cargo, bno08x->cargo.buffer, ret);
+
+out_unlock:
+	/* The last waiter unlocks the cargo read routine. */
+	if (atomic_dec_return(&bno08x->cargo.waiters) == 0) {
+		reinit_completion(&bno08x->cargo_ready);
+		complete(&bno08x->waiters_done);
+	}
+
+	return ret;
+}
+
+static const struct iio_chan_spec bno08x_iio_channels[] = {
+	{
+		.type = IIO_ROT,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index = BNO08x_ROT_SCAN_INDEX,
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.storagebits = 16,
+			.shift = 0,
+			.endianness = IIO_LE,
+		},
+	},
+};
+
+static int bno08x_enable_feature_report(struct bno08x_dev *bno08x, u8 report_id)
+{
+	struct i2c_client *client = bno08x->client;
+	/* Reporting interval hardcoded to 5 msec. */
+	unsigned int report_interval_usec = 5000;
+	u8 cargo[BNO08x_SHTP_MAX_SIZE] = {};
+	u8 *data = cargo + BNO08x_SHTP_HDR_SIZE;
+	unsigned int max_attempts = 50;
+	int ret;
+
+	if (bno08x->enabled_reports_mask & BIT(report_id))
+		return 0;
+
+	/*
+	 * Enable reporting the requested feature. The full "feature set
+	 * request" package size is 17 bytes.
+	 */
+	data[0] = BNO08x_SHTP_SET_FEATURE_CMD;
+	data[1] = report_id;
+
+	data[5] = (report_interval_usec >> 0);
+	data[6] = (report_interval_usec >> 8);
+	data[7] = (report_interval_usec >> 16);
+	data[8] = (report_interval_usec >> 24);
+
+	ret = bno08x_write_cargo(bno08x, BNO08x_SHTP_CONTROL_CHAN, cargo, 17);
+	if (ret)
+		return ret;
+
+	/*
+	 * Verify the requested feature is enabled inspecting the
+	 * 'feature request status" response cargo.
+	 */
+	memset(cargo, 0, BNO08x_SHTP_MAX_SIZE);
+	data[0] = BNO08x_SHTP_GET_FEATURE_REQ;
+	data[1] = report_id;
+
+	ret = bno08x_write_cargo(bno08x, BNO08x_SHTP_CONTROL_CHAN, cargo, 2);
+	if (ret)
+		return ret;
+
+	/*
+	 * Wait for a "get feature response". The datasheet says it will "arrive
+	 * some time in the future". Read up to 10 packets then give up.
+	 */
+	while (--max_attempts) {
+		memset(cargo, 0, 5);
+		ret = bno08x_wait_for_cargo_timeout(bno08x, cargo, 5);
+		if (ret < 0)
+			continue;
+
+		if (ret < 5)
+			continue;
+
+		if (data[0] == BNO08x_SHTP_GET_FEATURE_RESP ||
+		    data[1] == report_id)
+			break;
+	}
+	if (!max_attempts) {
+		dev_err(&client->dev,
+			"Failed to parse GET_FEATURE_RESPONSE: bad header\n");
+		return -EINVAL;
+	}
+
+	bno08x->enabled_reports_mask |= BIT(report_id);
+	dev_dbg(&client->dev, "Reporting of feature %x enabled!\n", report_id);
+
+	return 0;
+}
+
+static int bno08x_read_raw_rotation(struct bno08x_dev *bno08x, int *vals,
+				    int *val_len, int max_len)
+{
+	struct i2c_client *client = bno08x->client;
+	unsigned int max_attempts = 50;
+	u8 cargo[24];
+	int ret;
+
+	if (max_len < 3)
+		return -EINVAL;
+
+	ret = bno08x_enable_feature_report(bno08x, BNO08x_REPORTID_ROTATION_VEC);
+	if (ret)
+		return ret;
+
+	/*
+	 * Read the rotation vector in I, J and K quaternions.
+	 *
+	 * We're going to receive several other cargos before an actual rotation
+	 * vector input report, so we parse the cargo fields and only proceed
+	 * with a cargo that refers to the rotation vector report id.
+	 *
+	 * Use a sentinel to avoid looping forever, unfortunately we can't
+	 * really know how many cargos we have to discard before receiving the
+	 * 'right' one.
+	 */
+	while (--max_attempts) {
+		ret = bno08x_wait_for_cargo_timeout(bno08x, cargo, 24);
+		if (ret < 0)
+			continue;
+
+		if (ret != 24)
+			continue;
+
+		/*
+		 * Parse the cargo content to make sure it's a well-formed input
+		 * report containing rotation vector information.
+		 *
+		 * The layout of an input report cargo is reported by Figure 5.2
+		 * of "BNO080/85/86 Datasheet 1000-3927 v.1.11".
+		 *
+		 * The rotation vector input report is 24 bytes long.
+		 */
+		if (cargo[2] == BNO08x_SHTP_REPORTS_CHAN &&
+		    cargo[4] == BNO08x_REPORTID_TIMESTAMP_BASE &&
+		    cargo[9] == BNO08x_REPORTID_ROTATION_VEC)
+			break;
+	}
+	if (!max_attempts) {
+		dev_err(&client->dev, "Failed to receive the correct input report\n");
+		return -EINVAL;
+	}
+
+	/* Read the I, J, K quaternions. */
+	vals[0] = cargo[13] | (cargo[14] << 8);
+	vals[1] = cargo[15] | (cargo[16] << 8);
+	vals[2] = cargo[17] | (cargo[18] << 8);
+	*val_len = 3;
+
+	return IIO_VAL_INT_MULTIPLE;
+}
+
+static int bno08x_read_raw_multi(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan, int max_len,
+				 int *vals, int *val_len, long mask)
+{
+	struct bno08x_dev *bno08x = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		return bno08x_read_raw_rotation(bno08x, vals, val_len, max_len);
+
+	case IIO_CHAN_INFO_SCALE:
+		vals[0] = 0;
+		return IIO_VAL_INT;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct iio_info bno08x_iio_info = {
+	.read_raw_multi = bno08x_read_raw_multi,
+};
+
+static int bno08x_data_rdy_trigger_set_state(struct iio_trigger *trig,
+					     bool enable)
+{
+	struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
+	struct bno08x_dev *bno08x = iio_priv(iio_dev);
+	int ret;
+
+	if (!enable)
+		return 0;
+
+	ret = bno08x_enable_feature_report(bno08x, BNO08x_REPORTID_ROTATION_VEC);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct iio_trigger_ops bno08x_trigger_ops = {
+	.set_trigger_state = &bno08x_data_rdy_trigger_set_state,
+};
+
+static irqreturn_t bno08x_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *iio_dev = pf->indio_dev;
+	struct bno08x_dev *bno08x = iio_priv(iio_dev);
+	u8 cargo[BNO08x_CARGO_BUFFER_SIZE];
+	int ret;
+
+	ret = bno08x_wait_for_cargo_timeout(bno08x, cargo, BNO08x_CARGO_BUFFER_SIZE);
+	if (ret < 0)
+		goto done;
+
+	if (ret < 24)
+		goto done;
+
+	/* Make sure the cargo matches the active scan channel. */
+	if (*iio_dev->active_scan_mask & BIT(BNO08x_ROT_SCAN_INDEX) &&
+	    cargo[2] == BNO08x_SHTP_REPORTS_CHAN &&
+	    cargo[4] == BNO08x_REPORTID_TIMESTAMP_BASE &&
+	    cargo[9] == BNO08x_REPORTID_ROTATION_VEC) {
+		u16 data;
+
+		/* Unit quaternion I component. */
+		data = le16_to_cpu(cargo[13]);
+		iio_push_to_buffers_with_timestamp(iio_dev, &data, pf->timestamp);
+
+		/* Unit quaternion J component. */
+		data = le16_to_cpu(cargo[15]);
+		iio_push_to_buffers_with_timestamp(iio_dev, &data, pf->timestamp);
+
+		/* Unit quaternion K component. */
+		data = le16_to_cpu(cargo[17]);
+		iio_push_to_buffers_with_timestamp(iio_dev, &data, pf->timestamp);
+	}
+
+done:
+	iio_trigger_notify_done(iio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static int bno08x_check_prod_id(struct bno08x_dev *bno08x)
+{
+	struct i2c_client *client = bno08x->client;
+	u8 cargo[BNO08x_SHTP_MAX_SIZE] = {};
+	u8 *data = cargo + BNO08x_SHTP_HDR_SIZE;
+	int ret;
+
+	data[0] = BNO08x_SHTP_PROD_ID_REQ;
+	ret = bno08x_write_cargo(bno08x, BNO08x_SHTP_CONTROL_CHAN, cargo, 2);
+	if (ret)
+		return ret;
+
+	while (true) {
+		ret = bno08x_wait_for_cargo_timeout(bno08x, cargo, 20);
+		if (ret < 0) {
+			dev_dbg(&client->dev, "Failed to read PROD_ID: %d\n", ret);
+			return ret;
+		}
+
+		if (ret >= 20 && data[0] == BNO08x_SHTP_PROD_ID)
+			break;
+	}
+
+	dev_dbg(&client->dev, "FW version: 0x%x.%x\n", data[2], data[3]);
+
+	return 0;
+}
+
+static int bno08x_soft_reset(struct bno08x_dev *bno08x)
+{
+	struct i2c_client *client = bno08x->client;
+	u8 cargo[20] = {};
+	int ret;
+
+	/* Soft reset the device: write 0x01 to EXECUTABLE channel. */
+	cargo[BNO08x_SHTP_HDR_SIZE] = BNO08x_SHTP_SOFT_RESET;
+	ret = bno08x_write_cargo(bno08x, BNO08x_SHTP_EXECTUABLE_CHAN, cargo, 1);
+	if (ret) {
+		dev_err(&client->dev, "Failed to soft-reset BNO08x\n");
+		return ret;
+	}
+
+	/*
+	 * Now read the all the channel advertisments packets.
+	 * We don't care about the content.
+	 */
+	do {
+		ret = bno08x_wait_for_cargo_timeout(bno08x, cargo, 0);
+		if (ret < 0) {
+			dev_dbg(&client->dev,
+				"Failed to read channel advertisments: %d\n", ret);
+			return ret;
+		}
+	} while (ret != 0);
+
+	/*
+	 * Give the chip some time to stabilize. There's no mention of any
+	 * delay required after startup in the manual, but this makes operation
+	 * stable through module load/unload.
+	 */
+	usleep_range(15000, 20000);
+
+	return 0;
+}
+
+static int bno08x_probe(struct i2c_client *client)
+{
+	struct bno08x_dev *bno08x;
+	struct iio_dev *iio_dev;
+	int ret;
+
+	iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*bno08x));
+	if (!iio_dev)
+		return -ENOMEM;
+
+	dev_set_drvdata(&client->dev, iio_dev);
+
+	bno08x = iio_priv(iio_dev);
+	bno08x->client = client;
+
+	mutex_init(&bno08x->cargo.mutex);
+	init_completion(&bno08x->cargo_ready);
+	init_completion(&bno08x->waiters_done);
+
+	iio_dev->info = &bno08x_iio_info;
+	iio_dev->name = DRIVER_NAME;
+	iio_dev->channels = bno08x_iio_channels;
+	iio_dev->num_channels = ARRAY_SIZE(bno08x_iio_channels);
+	iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
+
+	ret = devm_iio_triggered_buffer_setup(&client->dev, iio_dev,
+					      iio_pollfunc_store_time,
+					      bno08x_trigger_handler, NULL);
+	if (ret)
+		return ret;
+
+	bno08x->trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",
+					      DRIVER_NAME,
+					      iio_device_id(iio_dev));
+	if (!bno08x->trig)
+		return -ENOMEM;
+
+	if (client->irq > 0) {
+		ret = devm_request_threaded_irq(&client->dev, client->irq,
+						bno08x_interrupt,
+						bno08x_dump_cargo,
+						IRQF_TRIGGER_FALLING,
+						iio_dev->name, iio_dev);
+		if (ret)
+			return ret;
+	}
+
+	bno08x->trig->dev.parent = &client->dev;
+	bno08x->trig->ops = &bno08x_trigger_ops;
+	iio_trigger_set_drvdata(bno08x->trig, iio_dev);
+
+	ret = devm_iio_trigger_register(&client->dev, bno08x->trig);
+	if (ret)
+		return ret;
+
+	iio_dev->trig = iio_trigger_get(bno08x->trig);
+
+	ret = bno08x_soft_reset(bno08x);
+	if (ret)
+		return ret;
+
+	bno08x_check_prod_id(bno08x);
+
+	return devm_iio_device_register(&client->dev, iio_dev);
+}
+
+static const struct of_device_id bno08x_of_match[] = {
+	{ .compatible = "ceva,bno08x" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, bno08x_of_match);
+
+static struct i2c_driver bno08x_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = of_match_ptr(bno08x_of_match),
+	},
+	.probe_new = bno08x_probe,
+};
+module_i2c_driver(bno08x_driver);
+
+MODULE_AUTHOR("Jacopo Mondi <jacopo@xxxxxxxxxx>");
+MODULE_DESCRIPTION("CEVA BNO08x IMU driver");
+MODULE_LICENSE("GPL");
-- 
2.35.1





[Index of Archives]     [Linux Driver Development]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux