Re: [PATCH 4/5] iio: imu: Add support for adis16475

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

 



On Thu, 5 Mar 2020 11:39:36 +0100
Lars-Peter Clausen <lars@xxxxxxxxxx> wrote:

> On 3/5/20 10:58 AM, Sa, Nuno wrote:
> > On Wed, 2020-03-04 at 17:59 +0000, Sa, Nuno wrote:  
> >> [External]
> >>
> >> On Tue, 2020-03-03 at 21:08 +0000, Jonathan Cameron wrote:  
> >>> [External]
> >>>
> >>> On Tue, 25 Feb 2020 13:41:51 +0100
> >>> Nuno Sá <nuno.sa@xxxxxxxxxx> wrote:
> >>>  
> >>>> Support ADIS16475 and similar IMU devices. These devices are
> >>>> a precision, miniature MEMS inertial measurement unit (IMU) that
> >>>> includes a triaxial gyroscope and a triaxial accelerometer. Each
> >>>> inertial sensor combines with signal conditioning that optimizes
> >>>> dynamic performance.
> >>>>
> >>>> The driver adds support for the following devices:
> >>>>   * adis16470, adis16475, adis16477, adis16465, adis16467,
> >>>> adis16500,
> >>>>     adis16505, adis16507.
> >>>>
> >>>> Signed-off-by: Nuno Sá <nuno.sa@xxxxxxxxxx>  
> >>> A few bits and pieces inline.
> >>>
> >>> Thanks,
> >>>
> >>> Jonathan
> >>>
> >>>  
> >>>> ---
> >>>>   .../ABI/testing/sysfs-bus-iio-imu-adis16475   |    7 +
> >>>>   MAINTAINERS                                   |    8 +
> >>>>   drivers/iio/imu/Kconfig                       |   13 +
> >>>>   drivers/iio/imu/Makefile                      |    1 +
> >>>>   drivers/iio/imu/adis16475.c                   | 1304
> >>>> +++++++++++++++++
> >>>>   5 files changed, 1333 insertions(+)
> >>>>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-imu-
> >>>> adis16475
> >>>>   create mode 100644 drivers/iio/imu/adis16475.c
> >>>>
> >>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-imu-
> >>>> adis16475
> >>>> b/Documentation/ABI/testing/sysfs-bus-iio-imu-adis16475
> >>>> new file mode 100644
> >>>> index 000000000000..e2c3776035ea
> >>>> --- /dev/null
> >>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-imu-adis16475
> >>>> @@ -0,0 +1,7 @@
> >>>> +What:		/sys/bus/iio/devices/iio:deviceX/burst_mode_ena
> >>>> ble
> >>>> +KernelVersion:
> >>>> +Contact:	linux-iio@xxxxxxxxxxxxxxx
> >>>> +Description:
> >>>> +		Use the device burst read mode when reading buffered
> >>>> +		data. This mode provides a way to read a batch of
> >>>> +		output data registers, using a continuous stream of
> >>>> bits.  
> >>> See comment on this below.  I'm not keen on this being exposed to
> >>> userspace
> >>> because it will rarely have any idea how to set it.
> >>>  
> >>>> diff --git a/MAINTAINERS b/MAINTAINERS
> >>>> index 8fa40c3eb72a..f11262f1f3bb 100644
> >>>> --- a/MAINTAINERS
> >>>> +++ b/MAINTAINERS
> >>>> @@ -1008,6 +1008,14 @@ W:	
> >>>> http://ez.analog.com/community/linux-device-drivers
> >>>>   F:	drivers/iio/imu/adis16460.c
> >>>>   F:	Documentation/devicetree/bindings/iio/imu/adi,adis16460
> >>>> .yaml
> >>>>   
> >>>> +ANALOG DEVICES INC ADIS16475 DRIVER
> >>>> +M:	Nuno Sa <nuno.sa@xxxxxxxxxx>
> >>>> +L:	linux-iio@xxxxxxxxxxxxxxx
> >>>> +W:	http://ez.analog.com/community/linux-device-drivers
> >>>> +S:	Supported
> >>>> +F:	drivers/iio/imu/adis16475.c
> >>>> +F:	Documentation/ABI/testing/sysfs-bus-iio-imu-adis16475
> >>>> +
> >>>>   ANALOG DEVICES INC ADM1177 DRIVER
> >>>>   M:	Beniamin Bia <beniamin.bia@xxxxxxxxxx>
> >>>>   M:	Michael Hennerich <Michael.Hennerich@xxxxxxxxxx>
> >>>> diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
> >>>> index 60bb1029e759..fc4123d518bc 100644
> >>>> --- a/drivers/iio/imu/Kconfig
> >>>> +++ b/drivers/iio/imu/Kconfig
> >>>> @@ -29,6 +29,19 @@ config ADIS16460
> >>>>   	  To compile this driver as a module, choose M here: the module
> >>>> will be
> >>>>   	  called adis16460.
> >>>>   
> >>>> +config ADIS16475
> >>>> +	tristate "Analog Devices ADIS16475 and similar IMU driver"
> >>>> +	depends on SPI
> >>>> +	select IIO_ADIS_LIB
> >>>> +	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
> >>>> +	help
> >>>> +	  Say yes here to build support for Analog Devices ADIS16470,
> >>>> ADIS16475,
> >>>> +	  ADIS16477, ADIS16465, ADIS16467, ADIS16500, ADIS16505,
> >>>> ADIS16507 inertial
> >>>> +	  sensors.
> >>>> +
> >>>> +	  To compile this driver as a module, choose M here: the module
> >>>> will be
> >>>> +	  called adis16475.
> >>>> +
> >>>>   config ADIS16480
> >>>>   	tristate "Analog Devices ADIS16480 and similar IMU driver"
> >>>>   	depends on SPI
> >>>> diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
> >>>> index 5237fd4bc384..88b2c4555230 100644
> >>>> --- a/drivers/iio/imu/Makefile
> >>>> +++ b/drivers/iio/imu/Makefile
> >>>> @@ -6,6 +6,7 @@
> >>>>   # When adding new entries keep the list in alphabetical order
> >>>>   obj-$(CONFIG_ADIS16400) += adis16400.o
> >>>>   obj-$(CONFIG_ADIS16460) += adis16460.o
> >>>> +obj-$(CONFIG_ADIS16475) += adis16475.o
> >>>>   obj-$(CONFIG_ADIS16480) += adis16480.o
> >>>>   
> >>>>   adis_lib-y += adis.o
> >>>> diff --git a/drivers/iio/imu/adis16475.c
> >>>> b/drivers/iio/imu/adis16475.c
> >>>> new file mode 100644
> >>>> index 000000000000..f7c637734ec8
> >>>> --- /dev/null
> >>>> +++ b/drivers/iio/imu/adis16475.c
> >>>> @@ -0,0 +1,1304 @@
> >>>> +// SPDX-License-Identifier: GPL-2.0
> >>>> +/*
> >>>> + * ADIS16475 IMU driver
> >>>> + *
> >>>> + * Copyright 2019 Analog Devices Inc.
> >>>> + */
> >>>> +#include <asm/unaligned.h>
> >>>> +#include <linux/bitfield.h>
> >>>> +#include <linux/bitops.h>
> >>>> +#include <linux/clk.h>
> >>>> +#include <linux/debugfs.h>
> >>>> +#include <linux/delay.h>
> >>>> +#include <linux/device.h>
> >>>> +#include <linux/kernel.h>
> >>>> +#include <linux/iio/buffer.h>
> >>>> +#include <linux/iio/iio.h>
> >>>> +#include <linux/iio/imu/adis.h>
> >>>> +#include <linux/iio/sysfs.h>
> >>>> +#include <linux/iio/trigger_consumer.h>
> >>>> +#include <linux/irq.h>
> >>>> +#include <linux/module.h>
> >>>> +#include <linux/spi/spi.h>
> >>>> +
> >>>> +#define ADIS16475_REG_DIAG_STAT		0x02
> >>>> +#define ADIS16475_REG_X_GYRO_L		0x04
> >>>> +#define ADIS16475_REG_Y_GYRO_L		0x08
> >>>> +#define ADIS16475_REG_Z_GYRO_L		0x0C
> >>>> +#define ADIS16475_REG_X_ACCEL_L		0x10
> >>>> +#define ADIS16475_REG_Y_ACCEL_L		0x14
> >>>> +#define ADIS16475_REG_Z_ACCEL_L		0x18
> >>>> +#define ADIS16475_REG_TEMP_OUT		0x1c
> >>>> +#define ADIS16475_REG_X_GYRO_BIAS_L	0x40
> >>>> +#define ADIS16475_REG_Y_GYRO_BIAS_L	0x44
> >>>> +#define ADIS16475_REG_Z_GYRO_BIAS_L	0x48
> >>>> +#define ADIS16475_REG_X_ACCEL_BIAS_L	0x4c
> >>>> +#define ADIS16475_REG_Y_ACCEL_BIAS_L	0x50
> >>>> +#define ADIS16475_REG_Z_ACCEL_BIAS_L	0x54
> >>>> +#define ADIS16475_REG_FILT_CTRL		0x5c
> >>>> +#define ADIS16475_FILT_CTRL_MASK	GENMASK(2, 0)
> >>>> +#define ADIS16475_FILT_CTRL(x)		FIELD_PREP(ADIS16475_FI
> >>>> LT_CTRL_MASK, x)
> >>>> +#define ADIS16475_REG_MSG_CTRL		0x60
> >>>> +#define ADIS16475_MSG_CTRL_DR_POL_MASK	BIT(0)
> >>>> +#define ADIS16475_MSG_CTRL_DR_POL(x) \
> >>>> +				FIELD_PREP(ADIS16475_MSG_CTRL_DR_POL_MA
> >>>> SK, x)
> >>>> +#define ADIS16475_EXT_CLK_MASK		GENMASK(4, 2)
> >>>> +#define ADIS16475_EXT_CLK(x)		FIELD_PREP(ADIS16475_EX
> >>>> T_CLK_MASK, x)
> >>>> +#define ADIS16475_REG_UP_SCALE		0x62
> >>>> +#define ADIS16475_REG_DEC_RATE		0x64
> >>>> +#define ADIS16475_REG_GLOB_CMD		0x68
> >>>> +#define ADIS16475_REG_FIRM_REV		0x6c
> >>>> +#define ADIS16475_REG_FIRM_DM		0x6e
> >>>> +#define ADIS16475_REG_FIRM_Y		0x70
> >>>> +#define ADIS16475_REG_PROD_ID		0x72
> >>>> +#define ADIS16475_REG_SERIAL_NUM	0x74
> >>>> +#define ADIS16475_REG_FLASH_CNT		0x7c
> >>>> +#define ADIS16500_BURST32_MASK		BIT(9)
> >>>> +#define ADIS16500_BURST32(x)		FIELD_PREP(ADIS16500_BU
> >>>> RST32_MASK, x)
> >>>> +/* number of data elements in burst mode */
> >>>> +#define ADIS16475_BURST_MAX_DATA	10
> >>>> +#define ADIS16475_MAX_SCAN_DATA		15
> >>>> +
> >>>> +enum clk_mode {
> >>>> +	ADIS16475_CLK_DIRECT = 1,
> >>>> +	ADIS16475_CLK_SCALED,
> >>>> +	ADIS16475_CLK_OUTPUT,
> >>>> +	ADIS16475_CLK_PULSE = 5,
> >>>> +};
> >>>> +
> >>>> +struct adis16475_clks {
> >>>> +	const char *name;
> >>>> +	enum clk_mode clk_mode;
> >>>> +	u16 min_rate;
> >>>> +	u16 max_rate;
> >>>> +};
> >>>> +
> >>>> +struct adis16475_chip_info {
> >>>> +	const struct iio_chan_spec *channels;
> >>>> +	const struct adis16475_clks *clks;
> >>>> +	const struct adis_data adis_data;
> >>>> +	u32 num_channels;
> >>>> +	u32 gyro_max_val;
> >>>> +	u32 gyro_max_scale;
> >>>> +	u32 accel_max_val;
> >>>> +	u32 accel_max_scale;
> >>>> +	u32 temp_scale;
> >>>> +	u32 int_clk;
> >>>> +	u16 max_dec;
> >>>> +	u8 num_clks;
> >>>> +	bool has_burst32;
> >>>> +};
> >>>> +
> >>>> +struct adis16475 {
> >>>> +	const struct adis16475_chip_info *info;
> >>>> +	struct adis adis;
> >>>> +	u32 clk_freq;
> >>>> +	u32 cached_spi_speed_hz;
> >>>> +	bool burst32;
> >>>> +};
> >>>> +
> >>>> +enum {
> >>>> +	ADIS16475_SCAN_GYRO_X,
> >>>> +	ADIS16475_SCAN_GYRO_Y,
> >>>> +	ADIS16475_SCAN_GYRO_Z,
> >>>> +	ADIS16475_SCAN_ACCEL_X,
> >>>> +	ADIS16475_SCAN_ACCEL_Y,
> >>>> +	ADIS16475_SCAN_ACCEL_Z,
> >>>> +	ADIS16475_SCAN_TEMP,
> >>>> +	ADIS16475_SCAN_DIAG_S_FLAGS,
> >>>> +	ADIS16475_SCAN_CRC_FAILURE,
> >>>> +};
> >>>> +
> >>>> +#ifdef CONFIG_DEBUG_FS
> >>>> +static ssize_t adis16475_show_firmware_revision(struct file
> >>>> *file,
> >>>> +						char __user *userbuf,
> >>>> +						size_t count, loff_t
> >>>> *ppos)
> >>>> +{
> >>>> +	struct adis16475 *st = file->private_data;
> >>>> +	char buf[7];
> >>>> +	size_t len;
> >>>> +	u16 rev;
> >>>> +	int ret;
> >>>> +
> >>>> +	ret = adis_read_reg_16(&st->adis, ADIS16475_REG_FIRM_REV,
> >>>> &rev);
> >>>> +	if (ret)
> >>>> +		return ret;
> >>>> +
> >>>> +	len = scnprintf(buf, sizeof(buf), "%x.%x\n", rev >> 8, rev &
> >>>> 0xff);
> >>>> +
> >>>> +	return simple_read_from_buffer(userbuf, count, ppos, buf, len);
> >>>> +}
> >>>> +
> >>>> +static const struct file_operations
> >>>> adis16475_firmware_revision_fops = {
> >>>> +	.open = simple_open,
> >>>> +	.read = adis16475_show_firmware_revision,
> >>>> +	.llseek = default_llseek,
> >>>> +	.owner = THIS_MODULE,
> >>>> +};
> >>>> +
> >>>> +static ssize_t adis16475_show_firmware_date(struct file *file,
> >>>> +					    char __user *userbuf,
> >>>> +					    size_t count, loff_t *ppos)
> >>>> +{
> >>>> +	struct adis16475 *st = file->private_data;
> >>>> +	u16 md, year;
> >>>> +	char buf[12];
> >>>> +	size_t len;
> >>>> +	int ret;
> >>>> +
> >>>> +	ret = adis_read_reg_16(&st->adis, ADIS16475_REG_FIRM_Y, &year);
> >>>> +	if (ret)
> >>>> +		return ret;
> >>>> +
> >>>> +	ret = adis_read_reg_16(&st->adis, ADIS16475_REG_FIRM_DM, &md);
> >>>> +	if (ret)
> >>>> +		return ret;
> >>>> +
> >>>> +	len = snprintf(buf, sizeof(buf), "%.2x-%.2x-%.4x\n", md >> 8,
> >>>> md & 0xff,
> >>>> +		       year);
> >>>> +
> >>>> +	return simple_read_from_buffer(userbuf, count, ppos, buf, len);
> >>>> +}
> >>>> +
> >>>> +static const struct file_operations adis16475_firmware_date_fops
> >>>> =
> >>>> {
> >>>> +	.open = simple_open,
> >>>> +	.read = adis16475_show_firmware_date,
> >>>> +	.llseek = default_llseek,
> >>>> +	.owner = THIS_MODULE,
> >>>> +};
> >>>> +
> >>>> +static int adis16475_show_serial_number(void *arg, u64 *val)
> >>>> +{
> >>>> +	struct adis16475 *st = arg;
> >>>> +	u16 serial;
> >>>> +	int ret;
> >>>> +
> >>>> +	ret = adis_read_reg_16(&st->adis, ADIS16475_REG_SERIAL_NUM,
> >>>> &serial);
> >>>> +	if (ret)
> >>>> +		return ret;
> >>>> +
> >>>> +	*val = serial;
> >>>> +
> >>>> +	return 0;
> >>>> +}
> >>>> +DEFINE_SIMPLE_ATTRIBUTE(adis16475_serial_number_fops,
> >>>> +			adis16475_show_serial_number, NULL,
> >>>> "0x%.4llx\n");
> >>>> +
> >>>> +static int adis16475_show_product_id(void *arg, u64 *val)
> >>>> +{
> >>>> +	struct adis16475 *st = arg;
> >>>> +	u16 prod_id;
> >>>> +	int ret;
> >>>> +
> >>>> +	ret = adis_read_reg_16(&st->adis, ADIS16475_REG_PROD_ID,
> >>>> &prod_id);
> >>>> +	if (ret)
> >>>> +		return ret;
> >>>> +
> >>>> +	*val = prod_id;
> >>>> +
> >>>> +	return 0;
> >>>> +}
> >>>> +DEFINE_SIMPLE_ATTRIBUTE(adis16475_product_id_fops,
> >>>> +			adis16475_show_product_id, NULL, "%llu\n");
> >>>> +
> >>>> +static int adis16475_show_flash_count(void *arg, u64 *val)
> >>>> +{
> >>>> +	struct adis16475 *st = arg;
> >>>> +	u32 flash_count;
> >>>> +	int ret;
> >>>> +
> >>>> +	ret = adis_read_reg_32(&st->adis, ADIS16475_REG_FLASH_CNT,
> >>>> +			       &flash_count);
> >>>> +	if (ret)
> >>>> +		return ret;
> >>>> +
> >>>> +	*val = flash_count;
> >>>> +
> >>>> +	return 0;
> >>>> +}
> >>>> +DEFINE_SIMPLE_ATTRIBUTE(adis16475_flash_count_fops,
> >>>> +			adis16475_show_flash_count, NULL, "%lld\n");
> >>>> +
> >>>> +static int adis16475_debugfs_init(struct iio_dev *indio_dev)
> >>>> +{
> >>>> +	struct adis16475 *st = iio_priv(indio_dev);
> >>>> +
> >>>> +	debugfs_create_file("serial_number", 0400, indio_dev-  
> >>>>> debugfs_dentry,  
> >>>> +			    st, &adis16475_serial_number_fops);
> >>>> +	debugfs_create_file("product_id", 0400, indio_dev-  
> >>>>> debugfs_dentry, st,  
> >>>> +			    &adis16475_product_id_fops);
> >>>> +	debugfs_create_file("flash_count", 0400, indio_dev-  
> >>>>> debugfs_dentry, st,  
> >>>> +			    &adis16475_flash_count_fops);
> >>>> +	debugfs_create_file("firmware_revision", 0400,
> >>>> +			    indio_dev->debugfs_dentry, st,
> >>>> +			    &adis16475_firmware_revision_fops);
> >>>> +	debugfs_create_file("firmware_date", 0400, indio_dev-  
> >>>>> debugfs_dentry,  
> >>>> +			    st, &adis16475_firmware_date_fops);
> >>>> +	return 0;
> >>>> +}
> >>>> +#else
> >>>> +static int adis16475_debugfs_init(struct iio_dev *indio_dev)
> >>>> +{
> >>>> +	return 0;
> >>>> +}
> >>>> +#endif
> >>>> +
> >>>> +static ssize_t adis16475_burst_mode_enable_get(struct device
> >>>> *dev,
> >>>> +					       struct device_attribute
> >>>> *attr,
> >>>> +					       char *buf)
> >>>> +{
> >>>> +	struct adis16475 *st = iio_priv(dev_to_iio_dev(dev));
> >>>> +
> >>>> +	return sprintf(buf, "%d\n", st->adis.burst->en);
> >>>> +}
> >>>> +
> >>>> +static ssize_t adis16475_burst_mode_enable_set(struct device
> >>>> *dev,
> >>>> +					       struct device_attribute
> >>>> *attr,
> >>>> +					       const char *buf, size_t
> >>>> len)
> >>>> +{
> >>>> +	struct adis16475 *st = iio_priv(dev_to_iio_dev(dev));
> >>>> +	bool val;
> >>>> +	int ret;
> >>>> +
> >>>> +	ret = kstrtobool(buf, &val);
> >>>> +	if (ret)
> >>>> +		return ret;
> >>>> +
> >>>> +	if (val)
> >>>> +		/* 1MHz max in burst mode */
> >>>> +		st->adis.spi->max_speed_hz = 1000000;
> >>>> +	else
> >>>> +		st->adis.spi->max_speed_hz = st->cached_spi_speed_hz;
> >>>> +
> >>>> +	st->adis.burst->en = val;
> >>>> +
> >>>> +	return len;
> >>>> +}
> >>>> +
> >>>> +static IIO_DEVICE_ATTR(burst_mode_enable, 0644,
> >>>> +		       adis16475_burst_mode_enable_get,
> >>>> +		       adis16475_burst_mode_enable_set, 0);
> >>>> +
> >>>> +static struct attribute *adis16475_attributes[] = {
> >>>> +	&iio_dev_attr_burst_mode_enable.dev_attr.attr,  
> >>> Hmm.  Normally we try to avoid exposing this and make the decision
> >>> automatically based on which channels are enabled.  
> >> Hmm. In that case, the decision would probably have to go to the
> >> library since it is there that the "preparations" for buffered mode
> >> are
> >> done. Mostly, the data we are interested in the burst data is gyro,
> >> accel and temp. So to make the decision based on which channels are
> >> enabled is not that straight. Should we enable it only when all
> >> channels are enabled? Some of them (and which)?  
> > I thought a little bit more about it and we could do some math in the
> > lib to know when using burst mode becomes faster (depending on enabled
> > scan elements). The library would need to know about the burst max spi
> > frequency and also some minor changes in how burst_len is being
> > calculated (drivers should explicitly set this rather than the
> > library). The question is, is it worth it to go with this extra work
> > :)?  
> 
> You can leave it out of the initial version and always use burst mode 
> and then add support for this in a follow up patch if it is worth it.
> 
> - Lars
> 
Agreed.   No one tends to buy an expensive IMU to just sample one channel
quickly!  Hence I doubt anyone will ever run it in a mode where burst
mode doesn't make sense.

Thanks,

Jonathan





[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