From: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
To: Jacopo Mondi <jacopo+renesas@jmondi.org>
Cc: Jonathan Cameron <jic23@kernel.org>,
Lars-Peter Clausen <lars@metafoo.de>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Andrea Merello <andrea.merello@gmail.com>,
Magnus Damm <magnus.damm@gmail.com>, <linux-iio@vger.kernel.org>,
<linux-staging@lists.linux.dev>, Jacopo Mondi <jacopo@jmondi.org>
Subject: Re: [PATCH 2/2] staging: iio: imu: Add CEVA BNO08x driver
Date: Fri, 17 Jun 2022 18:49:41 +0100 [thread overview]
Message-ID: <20220617184941.00001fcb@Huawei.com> (raw)
In-Reply-To: <20220616100006.22045-3-jacopo+renesas@jmondi.org>
On Thu, 16 Jun 2022 12:00:06 +0200
Jacopo Mondi <jacopo+renesas@jmondi.org> wrote:
> From: Jacopo Mondi <jacopo@jmondi.org>
>
> 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@jmondi.org>
Hi Jacopo
Review for whoever takes this forwards in the future :)
It's proving hard enough to get drivers that have been in staging
for years out, don't want to add new ones!
Jonathan
> ---
> 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@jmondi.org>
> +L: linux-iio@vger.kernel.org
> +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@kernel.org>
> 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@jmondi.org>
> + */
> +
> +#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);
Trigger isn't guaranteed to be the one you think it is :)
also with an interrupt doing other things, seems unlikely
you can always assume trigger fired...
> +
> + 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);
FIELD_GET() with appropriate mask and an unaligned_get_le16 probably (assuming
the LSB is next to the MSB)
> +
> + 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;
unaligned_put_le16()
> + 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)
Wait for seems a bit confusing to me as I read it as waiting for a timeout
which is a little odd :)
bn08x_read_cargo_timeout() maybe? That's not great either thogu.
> +{
> + 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);
put_unaligned_le32()
> +
> + 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);
Use get_unaligned_le16()
> + 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;
return bn08x_enable...
> +}
> +
> +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;
> +
I'm lazy - if it's less than 0 it's definitely less than 24 so no need for separate
paths unless you add some dev_dbg/err or similar which might make sense here.
> + 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);
If it's a single scan with multiple components, you need to
build the entire scan up here, then push it to the buffer in one go.
> +
> + /* 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);
Why use MAX_SIZE for sizing then 20 here?
> + 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]);
This is sometimes useful info. I'd put it out with dev_info() but up to you.
> +
> + 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);
Error path, I would go with dev_err() as it's fatal.
> + 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);
I'm not immediately seeing a user - perhaps there isn't one and this
should go away.
> +
> + 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;
If triggered buffer is registered, this will be set by the core.
Only DIRECT_MODE should be set in drivers.
> +
> + 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,
Not in the driver, that's for firmware to specify. We have lots of old
drivers doing this which we can't cleanly fix, but don't do it in a new
one.
> + iio_dev->name, iio_dev);
Unusual to create/register trigger if no interrupts provided...
Need some explanation.
> + 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);
No validate callbacks, so I assume the device can use other triggers.
If that's the case then don't set a default. Which one to use is
a userspace policy decision. If it can only use this one then enforce it.
> +
> + ret = bno08x_soft_reset(bno08x);
> + if (ret)
> + return ret;
> +
> + bno08x_check_prod_id(bno08x);
handle any errors returned.
> +
> + 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@jmondi.org>");
> +MODULE_DESCRIPTION("CEVA BNO08x IMU driver");
> +MODULE_LICENSE("GPL");
next prev parent reply other threads:[~2022-06-17 17:49 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-16 10:00 [PATCH 0/2] staging: iio: imu: Add CEVA BNO08x through the staging tree Jacopo Mondi
2022-06-16 10:00 ` [PATCH 1/2] dt-bindings: staging: iio: imu: Document CEVA BNO08x Jacopo Mondi
2022-06-27 22:25 ` Rob Herring
2022-06-16 10:00 ` [PATCH 2/2] staging: iio: imu: Add CEVA BNO08x driver Jacopo Mondi
2022-06-16 10:26 ` Greg Kroah-Hartman
2022-06-16 10:36 ` Jacopo Mondi
2022-06-16 10:57 ` Greg Kroah-Hartman
2022-06-16 12:30 ` Jacopo Mondi
2022-06-17 17:20 ` Jonathan Cameron
2022-06-16 11:12 ` Dan Carpenter
2022-06-17 17:49 ` Jonathan Cameron [this message]
2022-06-20 9:14 ` Dan Carpenter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220617184941.00001fcb@Huawei.com \
--to=jonathan.cameron@huawei.com \
--cc=andrea.merello@gmail.com \
--cc=gregkh@linuxfoundation.org \
--cc=jacopo+renesas@jmondi.org \
--cc=jacopo@jmondi.org \
--cc=jic23@kernel.org \
--cc=lars@metafoo.de \
--cc=linux-iio@vger.kernel.org \
--cc=linux-staging@lists.linux.dev \
--cc=magnus.damm@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).