Re: [PATCH v7 2/2] iio: accel: Add support for the Bosch-Sensortec BMI088

From: Jonathan Cameron
Date: Sat Jan 23 2021 - 10:53:23 EST


On Thu, 21 Jan 2021 16:56:59 +0100
Mike Looijmans <mike.looijmans@xxxxxxxx> wrote:

> The BMI088 is a combined module with both accelerometer and gyroscope.
> This adds the accelerometer driver support for the SPI interface.
> The gyroscope part is already supported by the BMG160 driver.
>
> Signed-off-by: Mike Looijmans <mike.looijmans@xxxxxxxx>
>

..

> diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c
> new file mode 100644
> index 000000000000..d7dc6f3e2fa6
> --- /dev/null
> +++ b/drivers/iio/accel/bmi088-accel-core.c
> @@ -0,0 +1,632 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * 3-axis accelerometer driver supporting following Bosch-Sensortec chips:
> + * - BMI088
> + *
> + * Copyright (c) 2018-2021, Topic Embedded Products
> + */
> +
> +#include <asm/unaligned.h>

Ah. I forgot to highlight this one. Oddly convention is
to do alphabetical order, but in groups. Generic first
(e.g. linux/*.h) and then specific (asm/*) and finally
as you have it, local headers.

Mind you it's not universal in the kernel, but I think this
is one of the more common patterns and it's definitely
preferred in IIO.

> +#include <linux/acpi.h>
> +#include <linux/delay.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/pm.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include "bmi088-accel.h"
> +
...

> +static int bmi088_accel_set_power_state_on(struct bmi088_accel_data *data)

Linus Walleij's query in v6 needs addressing (it crossed with you posting v7 I think)

> +{
> + struct device *dev = regmap_get_device(data->regmap);
> + int ret;
> +
> + ret = pm_runtime_get_sync(dev);
> + if (ret < 0) {
> + pm_runtime_put_noidle(dev);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int bmi088_accel_set_power_state_off(struct bmi088_accel_data *data)
> +{
> + struct device *dev = regmap_get_device(data->regmap);
> + int ret;
> +
> + pm_runtime_mark_last_busy(dev);
> + ret = pm_runtime_put_autosuspend(dev);
> +
> + return ret < 0 ? ret : 0;
> +}
> +
> +/*
> + * The register ACC_PWR_CTRL enables and disables the accelerometer and the
> + * temperature sensor.
> + */
> +static int bmi088_accel_enable(struct bmi088_accel_data *data, bool on_off)
> +{
> + struct device *dev = regmap_get_device(data->regmap);
> + unsigned int val = on_off ? 0x4 : 0x0;
> + int ret;
> +
> + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, val);
> + if (ret) {
> + dev_err(dev, "Error writing ACC_PWR_CTRL reg\n");
> + return ret;
> + }
> + /* Datasheet recommends to wait at least 5ms before communication */
> + usleep_range(5000, 6000);
> +
> + return 0;
> +}
> +
> +/* In suspend mode, only the accelerometer is powered down. */
> +static int bmi088_accel_set_mode(struct bmi088_accel_data *data,
> + enum bmi088_power_modes mode)
> +{
> + struct device *dev = regmap_get_device(data->regmap);
> + unsigned int val = mode == BMI088_ACCEL_MODE_SUSPEND ? 0x3 : 0x0;
> + int ret;
> +
> + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, val);
> + if (ret) {
> + dev_err(dev, "Error writing ACCEL_PWR_CONF reg\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
...

> +int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap,
> + int irq, const char *name, bool block_supported)
> +{
> + struct bmi088_accel_data *data;
> + struct iio_dev *indio_dev;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + data = iio_priv(indio_dev);
> + dev_set_drvdata(dev, indio_dev);
> +
> + data->regmap = regmap;
> +
> + ret = bmi088_accel_chip_init(data);
> + if (ret)
> + return ret;
> +
> + mutex_init(&data->mutex);
> +
> + indio_dev->dev.parent = dev;
> + indio_dev->channels = data->chip_info->channels;
> + indio_dev->num_channels = data->chip_info->num_channels;
> + indio_dev->name = name ? name : data->chip_info->name;
> + indio_dev->available_scan_masks = bmi088_accel_scan_masks;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->info = &bmi088_accel_info;
> +
> + ret = pm_runtime_set_active(dev);
> + if (ret)
> + return ret;
> +
> + pm_runtime_enable(dev);
> + pm_runtime_set_autosuspend_delay(dev, BMI088_AUTO_SUSPEND_DELAY_MS);
> + pm_runtime_use_autosuspend(dev);
> +
> + ret = iio_device_register(indio_dev);
> + if (ret)
> + dev_err(dev, "Unable to register iio device\n");
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(bmi088_accel_core_probe);
> +
> +static void bmi088_accel_suspend_impl(struct bmi088_accel_data *data)
> +{
> + mutex_lock(&data->mutex);
> + bmi088_accel_set_mode(data, BMI088_ACCEL_MODE_SUSPEND);
> + bmi088_accel_enable(data, false);
> + mutex_unlock(&data->mutex);
> +}accel_supsn
> +
> +int bmi088_accel_core_remove(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct bmi088_accel_data *data = iio_priv(indio_dev);
> +
> + iio_device_unregister(indio_dev);
> +
> + pm_runtime_disable(dev);
> + pm_runtime_set_suspended(dev);
> + pm_runtime_put_noidle(dev);
> + bmi088_accel_suspend_impl(data);
Sorry, did read your response to v6 review quick enough or I'd have
commented on it there.

Whilst it seems convenient I would rather you didn't use this shared
function. It obscures what is going on a little so it's not obvious
which elements of probe this is undoing.

the accel_enable is buried in *chip_init() so I'd ideally like to
see an *chip_uninit() that undoes only what was done in chip_init()
(next to it so it's easy to check!)

The set_mode() bit is harder to identify in probe and actually makes
me a bit suspicious. If we probe the driver and immediately read
I suspect the device may not actually be turned on?


> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(bmi088_accel_core_remove);
> +
> +/* When going into system sleep, put the chip in power down */
> +static int __maybe_unused bmi088_accel_suspend(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct bmi088_accel_data *data = iio_priv(indio_dev);
> +
> + bmi088_accel_suspend_impl(data);
> +
> + return 0;
> +}
> +
> +static int __maybe_unused bmi088_accel_resume(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct bmi088_accel_data *data = iio_priv(indio_dev);
> +
> + mutex_lock(&data->mutex);
> + bmi088_accel_enable(data, true);
> + bmi088_accel_set_mode(data, BMI088_ACCEL_MODE_ACTIVE);
> + mutex_unlock(&data->mutex);
> +
> + return 0;
> +}
> +
> +/* For runtime PM put the chip in suspend mode */
> +static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct bmi088_accel_data *data = iio_priv(indio_dev);
> +
> + return bmi088_accel_set_mode(data, BMI088_ACCEL_MODE_SUSPEND);
> +}
> +
> +static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct bmi088_accel_data *data = iio_priv(indio_dev);
> + int ret;
> +
> + ret = bmi088_accel_set_mode(data, BMI088_ACCEL_MODE_ACTIVE);
> + if (ret)
> + return ret;
> +
> + usleep_range(BMI088_ACCEL_MAX_STARTUP_TIME_US,
> + BMI088_ACCEL_MAX_STARTUP_TIME_US * 2);
> +
> + return 0;
> +}
> +
> +const struct dev_pm_ops bmi088_accel_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(bmi088_accel_suspend, bmi088_accel_resume)
> + SET_RUNTIME_PM_OPS(bmi088_accel_runtime_suspend,
> + bmi088_accel_runtime_resume, NULL)
> +};
> +EXPORT_SYMBOL_GPL(bmi088_accel_pm_ops);
> +
> +MODULE_AUTHOR("Niek van Agt <niek.van.agt@xxxxxxxxxxxxxxxxx>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("BMI088 accelerometer driver (core)");

...