linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/7] iio:imu:adis: Add debugfs register access support
@ 2012-11-20 13:36 Lars-Peter Clausen
  2012-11-20 13:36 ` [PATCH 2/7] iio:imu:adis: Add support for 32bit registers Lars-Peter Clausen
                   ` (6 more replies)
  0 siblings, 7 replies; 16+ messages in thread
From: Lars-Peter Clausen @ 2012-11-20 13:36 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, drivers, Lars-Peter Clausen

Provide a IIO debugfs register access function for the ADIS library. This
function can be used by induvidual drivers to allow raw register access via
debugfs.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/iio/imu/adis.c       | 23 +++++++++++++++++++++++
 include/linux/iio/imu/adis.h | 11 +++++++++++
 2 files changed, 34 insertions(+)

diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
index 8259b77..28d4df2 100644
--- a/drivers/iio/imu/adis.c
+++ b/drivers/iio/imu/adis.c
@@ -135,6 +135,29 @@ error_ret:
 }
 EXPORT_SYMBOL_GPL(adis_read_reg_16);
 
+#ifdef CONFIG_DEBUG_FS
+
+int adis_debugfs_reg_access(struct iio_dev *indio_dev,
+	unsigned int reg, unsigned int writeval, unsigned int *readval)
+{
+	struct adis *adis = iio_device_get_drvdata(indio_dev);
+
+	if (readval) {
+		uint16_t val16;
+		int ret;
+
+		ret = adis_read_reg_16(adis, reg, &val16);
+		*readval = val16;
+
+		return ret;
+	} else {
+		return adis_write_reg_16(adis, reg, writeval);
+	}
+}
+EXPORT_SYMBOL(adis_debugfs_reg_access);
+
+#endif
+
 /**
  * adis_enable_irq() - Enable or disable data ready IRQ
  * @adis: The adis device
diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h
index 8c3304d..fce7bc3 100644
--- a/include/linux/iio/imu/adis.h
+++ b/include/linux/iio/imu/adis.h
@@ -183,4 +183,15 @@ static inline void adis_remove_trigger(struct adis *adis)
 
 #endif /* CONFIG_IIO_BUFFER */
 
+#ifdef CONFIG_DEBUG_FS
+
+int adis_debugfs_reg_access(struct iio_dev *indio_dev,
+	unsigned int reg, unsigned int writeval, unsigned int *readval);
+
+#else
+
+#define adis_debugfs_reg_access NULL
+
+#endif
+
 #endif
-- 
1.8.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/7] iio:imu:adis: Add support for 32bit registers
  2012-11-20 13:36 [PATCH 1/7] iio:imu:adis: Add debugfs register access support Lars-Peter Clausen
@ 2012-11-20 13:36 ` Lars-Peter Clausen
  2012-11-20 20:55   ` Jonathan Cameron
  2012-11-20 13:36 ` [PATCH 3/7] iio:gyro: Add support for the ADIS16136 gyroscope Lars-Peter Clausen
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 16+ messages in thread
From: Lars-Peter Clausen @ 2012-11-20 13:36 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, drivers, Lars-Peter Clausen

Some of the newer generation devices from the ADIS16XXX family have 32bit wide
register which spans two 16bit wide registers. This patch adds support for
reading and writing a 32bit wide register.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/iio/imu/adis.c        | 145 +++++++++++++++++++++++++++---------------
 drivers/iio/imu/adis_buffer.c |   2 +
 include/linux/iio/imu/adis.h  |  81 +++++++++++++++++++++--
 3 files changed, 171 insertions(+), 57 deletions(-)

diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
index 28d4df2..280a495 100644
--- a/drivers/iio/imu/adis.c
+++ b/drivers/iio/imu/adis.c
@@ -27,36 +27,10 @@
 #define ADIS_MSC_CTRL_DATA_RDY_DIO2	BIT(0)
 #define ADIS_GLOB_CMD_SW_RESET		BIT(7)
 
-/**
- * adis_write_reg_8() - Write single byte to a register
- * @adis: The adis device
- * @reg: The address of the register to be written
- * @val: The value to write
- */
-int adis_write_reg_8(struct adis *adis, unsigned int reg, uint8_t val)
-{
-	int ret;
-
-	mutex_lock(&adis->txrx_lock);
-	adis->tx[0] = ADIS_WRITE_REG(reg);
-	adis->tx[1] = val;
-
-	ret = spi_write(adis->spi, adis->tx, 2);
-	mutex_unlock(&adis->txrx_lock);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(adis_write_reg_8);
-
-/**
- * adis_write_reg_16() - Write 2 bytes to a pair of registers
- * @adis: The adis device
- * @reg: The address of the lower of the two registers
- * @val: Value to be written
- */
-int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t value)
+int adis_write_reg(struct adis *adis, unsigned int reg,
+	unsigned int value, unsigned int size)
 {
-	int ret;
+	int ret, i;
 	struct spi_message msg;
 	struct spi_transfer xfers[] = {
 		{
@@ -69,33 +43,69 @@ int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t value)
 			.tx_buf = adis->tx + 2,
 			.bits_per_word = 8,
 			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = adis->data->write_delay,
+		}, {
+			.tx_buf = adis->tx + 4,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = adis->data->write_delay,
+		}, {
+			.tx_buf = adis->tx + 6,
+			.bits_per_word = 8,
+			.len = 2,
 			.delay_usecs = adis->data->write_delay,
 		},
 	};
 
 	mutex_lock(&adis->txrx_lock);
-	adis->tx[0] = ADIS_WRITE_REG(reg);
-	adis->tx[1] = value & 0xff;
-	adis->tx[2] = ADIS_WRITE_REG(reg + 1);
-	adis->tx[3] = (value >> 8) & 0xff;
 
 	spi_message_init(&msg);
-	spi_message_add_tail(&xfers[0], &msg);
-	spi_message_add_tail(&xfers[1], &msg);
+	switch (size) {
+	case 4:
+		adis->tx[6] = ADIS_WRITE_REG(reg + 3);
+		adis->tx[7] = (value >> 24) & 0xff;
+		adis->tx[4] = ADIS_WRITE_REG(reg + 2);
+		adis->tx[5] = (value >> 16) & 0xff;
+	case 2:
+		adis->tx[2] = ADIS_WRITE_REG(reg + 1);
+		adis->tx[3] = (value >> 8) & 0xff;
+	case 1:
+		adis->tx[0] = ADIS_WRITE_REG(reg);
+		adis->tx[1] = value & 0xff;
+		break;
+	default:
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	xfers[size - 1].cs_change = 0;
+
+	for (i = 0; i < size; i++)
+		spi_message_add_tail(&xfers[i], &msg);
+
 	ret = spi_sync(adis->spi, &msg);
+	if (ret) {
+		dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n",
+				reg, ret);
+	}
+
+out_unlock:
 	mutex_unlock(&adis->txrx_lock);
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(adis_write_reg_16);
+EXPORT_SYMBOL_GPL(adis_write_reg);
 
 /**
- * adis_read_reg_16() - read 2 bytes from a 16-bit register
+ * adis_read_reg() - read 2 bytes from a 16-bit register
  * @adis: The adis device
  * @reg: The address of the lower of the two registers
  * @val: The value read back from the device
  */
-int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val)
+int adis_read_reg(struct adis *adis, unsigned int reg,
+	unsigned int *val, unsigned int size)
 {
 	struct spi_message msg;
 	int ret;
@@ -107,33 +117,61 @@ int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val)
 			.cs_change = 1,
 			.delay_usecs = adis->data->read_delay,
 		}, {
+			.tx_buf = adis->tx + 2,
 			.rx_buf = adis->rx,
 			.bits_per_word = 8,
 			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = adis->data->read_delay,
+		}, {
+			.rx_buf = adis->rx + 2,
+			.bits_per_word = 8,
+			.len = 2,
 			.delay_usecs = adis->data->read_delay,
 		},
 	};
 
 	mutex_lock(&adis->txrx_lock);
-	adis->tx[0] = ADIS_READ_REG(reg);
-	adis->tx[1] = 0;
-
 	spi_message_init(&msg);
-	spi_message_add_tail(&xfers[0], &msg);
-	spi_message_add_tail(&xfers[1], &msg);
+
+	switch (size) {
+	case 4:
+		adis->tx[0] = ADIS_READ_REG(reg + 2);
+		adis->tx[1] = 0;
+		spi_message_add_tail(&xfers[0], &msg);
+	case 2:
+		adis->tx[2] = ADIS_READ_REG(reg);
+		adis->tx[3] = 0;
+		spi_message_add_tail(&xfers[1], &msg);
+		spi_message_add_tail(&xfers[2], &msg);
+		break;
+	default:
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
 	ret = spi_sync(adis->spi, &msg);
 	if (ret) {
-		dev_err(&adis->spi->dev, "Failed to read 16 bit register 0x%02X: %d\n",
+		dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
 				reg, ret);
-		goto error_ret;
+		goto out_unlock;
 	}
-	*val = get_unaligned_be16(adis->rx);
 
-error_ret:
+	switch (size) {
+	case 4:
+		*val = get_unaligned_be32(adis->rx);
+		break;
+	case 2:
+		*val = get_unaligned_be16(adis->rx + 2);
+		break;
+	}
+
+out_unlock:
 	mutex_unlock(&adis->txrx_lock);
+
 	return ret;
 }
-EXPORT_SYMBOL_GPL(adis_read_reg_16);
+EXPORT_SYMBOL_GPL(adis_read_reg);
 
 #ifdef CONFIG_DEBUG_FS
 
@@ -304,25 +342,26 @@ int adis_single_conversion(struct iio_dev *indio_dev,
 	const struct iio_chan_spec *chan, unsigned int error_mask, int *val)
 {
 	struct adis *adis = iio_device_get_drvdata(indio_dev);
-	uint16_t val16;
+	unsigned int uval;
 	int ret;
 
 	mutex_lock(&indio_dev->mlock);
 
-	ret = adis_read_reg_16(adis, chan->address, &val16);
+	ret = adis_read_reg(adis, chan->address, &uval,
+			chan->scan_type.storagebits / 8);
 	if (ret)
 		goto err_unlock;
 
-	if (val16 & error_mask) {
+	if (uval & error_mask) {
 		ret = adis_check_status(adis);
 		if (ret)
 			goto err_unlock;
 	}
 
 	if (chan->scan_type.sign == 's')
-		*val = sign_extend32(val16, chan->scan_type.realbits - 1);
+		*val = sign_extend32(uval, chan->scan_type.realbits - 1);
 	else
-		*val = val16 & ((1 << chan->scan_type.realbits) - 1);
+		*val = uval & ((1 << chan->scan_type.realbits) - 1);
 
 	ret = IIO_VAL_INT;
 err_unlock:
diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c
index a91b4cb..7857133 100644
--- a/drivers/iio/imu/adis_buffer.c
+++ b/drivers/iio/imu/adis_buffer.c
@@ -64,6 +64,8 @@ int adis_update_scan_mode(struct iio_dev *indio_dev,
 	for (i = 0; i < indio_dev->num_channels; i++, chan++) {
 		if (!test_bit(chan->scan_index, scan_mask))
 			continue;
+		if (chan->scan_type.storagebits == 32)
+			*tx++ = cpu_to_be16((chan->address + 2) << 8);
 		*tx++ = cpu_to_be16(chan->address << 8);
 	}
 
diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h
index fce7bc3..6402a08 100644
--- a/include/linux/iio/imu/adis.h
+++ b/include/linux/iio/imu/adis.h
@@ -53,7 +53,7 @@ struct adis {
 	struct spi_transfer	*xfer;
 	void			*buffer;
 
-	uint8_t			tx[8] ____cacheline_aligned;
+	uint8_t			tx[10] ____cacheline_aligned;
 	uint8_t			rx[4];
 };
 
@@ -61,9 +61,82 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev,
 	struct spi_device *spi, const struct adis_data *data);
 int adis_reset(struct adis *adis);
 
-int adis_write_reg_8(struct adis *adis, unsigned int reg, uint8_t val);
-int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t val);
-int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val);
+int adis_write_reg(struct adis *adis, unsigned int reg,
+	unsigned int val, unsigned int size);
+int adis_read_reg(struct adis *adis, unsigned int reg,
+	unsigned int *val, unsigned int size);
+
+/**
+ * adis_write_reg_8() - Write single byte to a register
+ * @adis: The adis device
+ * @reg: The address of the register to be written
+ * @value: The value to write
+ */
+static inline int adis_write_reg_8(struct adis *adis, unsigned int reg,
+	uint8_t val)
+{
+	return adis_write_reg(adis, reg, val, 1);
+}
+
+/**
+ * adis_write_reg_16() - Write 2 bytes to a pair of registers
+ * @adis: The adis device
+ * @reg: The address of the lower of the two registers
+ * @value: Value to be written
+ */
+static inline int adis_write_reg_16(struct adis *adis, unsigned int reg,
+	uint16_t val)
+{
+	return adis_write_reg(adis, reg, val, 2);
+}
+
+/**
+ * adis_write_reg_32() - write 4 bytes to four registers
+ * @adis: The adis device
+ * @reg: The address of the lower of the four register
+ * @value: Value to be written
+ */
+static inline int adis_write_reg_32(struct adis *adis, unsigned int reg,
+	uint32_t val)
+{
+	return adis_write_reg(adis, reg, val, 4);
+}
+
+/**
+ * adis_read_reg_16() - read 2 bytes from a 16-bit register
+ * @adis: The adis device
+ * @reg: The address of the lower of the two registers
+ * @val: The value read back from the device
+ */
+static inline int adis_read_reg_16(struct adis *adis, unsigned int reg,
+	uint16_t *val)
+{
+	unsigned int tmp;
+	int ret;
+
+	ret = adis_read_reg(adis, reg, &tmp, 2);
+	*val = tmp;
+
+	return ret;
+}
+
+/**
+ * adis_read_reg_32() - read 4 bytes from a 32-bit register
+ * @adis: The adis device
+ * @reg: The address of the lower of the two registers
+ * @val: The value read back from the device
+ */
+static inline int adis_read_reg_32(struct adis *adis, unsigned int reg,
+	uint32_t *val)
+{
+	unsigned int tmp;
+	int ret;
+
+	ret = adis_read_reg(adis, reg, &tmp, 4);
+	*val = tmp;
+
+	return ret;
+}
 
 int adis_enable_irq(struct adis *adis, bool enable);
 int adis_check_status(struct adis *adis);
-- 
1.8.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 3/7] iio:gyro: Add support for the ADIS16136 gyroscope
  2012-11-20 13:36 [PATCH 1/7] iio:imu:adis: Add debugfs register access support Lars-Peter Clausen
  2012-11-20 13:36 ` [PATCH 2/7] iio:imu:adis: Add support for 32bit registers Lars-Peter Clausen
@ 2012-11-20 13:36 ` Lars-Peter Clausen
  2012-11-20 21:05   ` Jonathan Cameron
  2012-11-20 13:36 ` [PATCH 4/7] iio:imu:adis: Add paging support Lars-Peter Clausen
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 16+ messages in thread
From: Lars-Peter Clausen @ 2012-11-20 13:36 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, drivers, Lars-Peter Clausen

This patch adds support for the ADIS16133, ADIS16135, ADIS16136 single channel
gyroscopes. The main difference between them is the sensor precision.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/iio/gyro/Kconfig     |   9 +
 drivers/iio/gyro/Makefile    |   1 +
 drivers/iio/gyro/adis16136.c | 581 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 591 insertions(+)
 create mode 100644 drivers/iio/gyro/adis16136.c

diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
index 21e27e2..48ed148 100644
--- a/drivers/iio/gyro/Kconfig
+++ b/drivers/iio/gyro/Kconfig
@@ -3,6 +3,15 @@
 #
 menu "Digital gyroscope sensors"
 
+config ADIS16136
+	tristate "Analog devices ADIS16136 and similar gyroscopes driver"
+	depends on SPI_MASTER
+	select IIO_ADIS_LIB
+	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+	help
+	  Say yes here to build support for the Analog Devices ADIS16133, ADIS16135,
+	  ADIS16136 gyroscope devices.
+
 config HID_SENSOR_GYRO_3D
 	depends on HID_SENSOR_HUB
 	select IIO_BUFFER
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
index 8a895d9..702a058 100644
--- a/drivers/iio/gyro/Makefile
+++ b/drivers/iio/gyro/Makefile
@@ -2,4 +2,5 @@
 # Makefile for industrial I/O gyroscope sensor drivers
 #
 
+obj-$(CONFIG_ADIS16136) += adis16136.o
 obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c
new file mode 100644
index 0000000..277aa74
--- /dev/null
+++ b/drivers/iio/gyro/adis16136.c
@@ -0,0 +1,581 @@
+/*
+ * ADIS16133/ADIS16135/ADIS16136 gyroscope driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *   Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include <linux/iio/iio.h>
+#include <linux/debugfs.h>
+
+#define ADIS16136_REG_FLASH_CNT		0x00
+#define ADIS16136_REG_TEMP_OUT		0x02
+#define ADIS16136_REG_GYRO_OUT2		0x04
+#define ADIS16136_REG_GYRO_OUT		0x06
+#define ADIS16136_REG_GYRO_OFF2		0x08
+#define ADIS16136_REG_GYRO_OFF		0x0A
+#define ADIS16136_REG_ALM_MAG1		0x10
+#define ADIS16136_REG_ALM_MAG2		0x12
+#define ADIS16136_REG_ALM_SAMPL1	0x14
+#define ADIS16136_REG_ALM_SAMPL2	0x16
+#define ADIS16136_REG_ALM_CTRL		0x18
+#define ADIS16136_REG_GPIO_CTRL		0x1A
+#define ADIS16136_REG_MSC_CTRL		0x1C
+#define ADIS16136_REG_SMPL_PRD		0x1E
+#define ADIS16136_REG_AVG_CNT		0x20
+#define ADIS16136_REG_DEC_RATE		0x22
+#define ADIS16136_REG_SLP_CTRL		0x24
+#define ADIS16136_REG_DIAG_STAT		0x26
+#define ADIS16136_REG_GLOB_CMD		0x28
+#define ADIS16136_REG_LOT1		0x32
+#define ADIS16136_REG_LOT2		0x34
+#define ADIS16136_REG_LOT3		0x36
+#define ADIS16136_REG_PROD_ID		0x38
+#define ADIS16136_REG_SERIAL_NUM	0x3A
+
+#define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL	2
+#define ADIS16136_DIAG_STAT_SPI_FAIL		3
+#define ADIS16136_DIAG_STAT_SELF_TEST_FAIL	5
+#define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL	6
+
+#define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11)
+#define ADIS16136_MSC_CTRL_SELF_TEST BIT(10)
+
+struct adis16136_chip_info {
+	unsigned int precision;
+	unsigned int fullscale;
+};
+
+struct adis16136 {
+	const struct adis16136_chip_info *chip_info;
+
+	struct adis adis;
+};
+
+#ifdef CONFIG_DEBUG_FS
+
+static ssize_t adis16136_show_serial(struct file *file,
+		char __user *userbuf, size_t count, loff_t *ppos)
+{
+	struct adis16136 *adis16136 = file->private_data;
+	uint16_t lot1, lot2, lot3, serial;
+	char buf[20];
+	size_t len;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SERIAL_NUM,
+		&serial);
+	if (ret < 0)
+		return ret;
+
+	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1);
+	if (ret < 0)
+		return ret;
+
+	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2);
+	if (ret < 0)
+		return ret;
+
+	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3);
+	if (ret < 0)
+		return ret;
+
+	len = snprintf(buf, sizeof(buf), "%.4x%.4x%.4x-%.4x\n", lot1, lot2,
+		lot3, serial);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+
+static const struct file_operations adis16136_serial_fops = {
+	.open = simple_open,
+	.read = adis16136_show_serial,
+	.llseek = default_llseek,
+	.owner = THIS_MODULE,
+};
+
+static int adis16136_show_product_id(void *arg, u64 *val)
+{
+	struct adis16136 *adis16136 = arg;
+	u16 prod_id;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
+		&prod_id);
+	if (ret < 0)
+		return ret;
+
+	*val = prod_id;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16136_product_id_fops,
+	adis16136_show_product_id, NULL, "%llu\n");
+
+static int adis16136_show_flash_count(void *arg, u64 *val)
+{
+	struct adis16136 *adis16136 = arg;
+	uint16_t flash_count;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_FLASH_CNT,
+		&flash_count);
+	if (ret < 0)
+		return ret;
+
+	*val = flash_count;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16136_flash_count_fops,
+	adis16136_show_flash_count, NULL, "%lld\n");
+
+static int adis16136_debugfs_init(struct iio_dev *indio_dev)
+{
+	struct adis16136 *adis16136 = iio_priv(indio_dev);
+
+	debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
+		adis16136, &adis16136_serial_fops);
+	debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
+		adis16136, &adis16136_product_id_fops);
+	debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
+		adis16136, &adis16136_flash_count_fops);
+
+	return 0;
+}
+
+#else
+
+static int adis16136_debugfs_init(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+#endif
+
+static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq)
+{
+	unsigned int t;
+
+	t = 32768 / freq;
+	if (t < 0xf)
+		t = 0xf;
+	else if (t > 0xffff)
+		t = 0xffff;
+	else
+		t--;
+
+	return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t);
+}
+
+static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq)
+{
+	uint16_t t;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t);
+	if (ret < 0)
+		return ret;
+
+	*freq = 32768 / (t + 1);
+
+	return 0;
+}
+
+static ssize_t adis16136_write_frequency(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct adis16136 *adis16136 = iio_priv(indio_dev);
+	long val;
+	int ret;
+
+	ret = kstrtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	if (val == 0)
+		return -EINVAL;
+
+	ret = adis16136_set_freq(adis16136, val);
+
+	return ret ? ret : len;
+}
+
+static ssize_t adis16136_read_frequency(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct adis16136 *adis16136 = iio_priv(indio_dev);
+	unsigned int freq;
+	int ret;
+
+	ret = adis16136_get_freq(adis16136, &freq);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", freq);
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+				  adis16136_read_frequency,
+				  adis16136_write_frequency);
+
+static const unsigned adis16136_3db_divisors[] = {
+	[0] = 2, /* Special case */
+	[1] = 6,
+	[2] = 12,
+	[3] = 25,
+	[4] = 50,
+	[5] = 100,
+	[6] = 200,
+	[7] = 200, /* Not a valid setting */
+};
+
+static int adis16136_set_filter(struct iio_dev *indio_dev, int val)
+{
+	struct adis16136 *adis16136 = iio_priv(indio_dev);
+	unsigned int freq;
+	int i, ret;
+
+	ret = adis16136_get_freq(adis16136, &freq);
+	if (ret < 0)
+		return ret;
+
+	for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) {
+		if (freq / adis16136_3db_divisors[i] >= val)
+			break;
+	}
+
+	return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i);
+}
+
+static int adis16136_get_filter(struct iio_dev *indio_dev, int *val)
+{
+	struct adis16136 *adis16136 = iio_priv(indio_dev);
+	unsigned int freq;
+	uint16_t val16;
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16);
+	if (ret < 0)
+		goto err_unlock;
+
+	ret = adis16136_get_freq(adis16136, &freq);
+	if (ret < 0)
+		goto err_unlock;
+
+	*val = freq / adis16136_3db_divisors[val16 & 0x07];
+
+err_unlock:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : IIO_VAL_INT;
+}
+
+static int adis16136_read_raw(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+	struct adis16136 *adis16136 = iio_priv(indio_dev);
+	uint32_t val32;
+	int ret;
+
+	switch (info) {
+	case IIO_CHAN_INFO_RAW:
+		return adis_single_conversion(indio_dev, chan, 0, val);
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*val = adis16136->chip_info->precision;
+			*val2 = (adis16136->chip_info->fullscale << 16);
+			return IIO_VAL_FRACTIONAL;
+		case IIO_TEMP:
+			*val = 10;
+			*val2 = 697000; /* 0.010697 degree Celsius */
+			return IIO_VAL_INT_PLUS_MICRO;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_CALIBBIAS:
+		ret = adis_read_reg_32(&adis16136->adis,
+			ADIS16136_REG_GYRO_OFF2, &val32);
+		if (ret < 0)
+			return ret;
+
+		*val = sign_extend32(val32, 31);
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		return adis16136_get_filter(indio_dev, val);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int adis16136_write_raw(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int val, int val2, long info)
+{
+	struct adis16136 *adis16136 = iio_priv(indio_dev);
+
+	switch (info) {
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return adis_write_reg_32(&adis16136->adis,
+			ADIS16136_REG_GYRO_OFF2, val);
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		return adis16136_set_filter(indio_dev, val);
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
+enum {
+	ADIS16136_SCAN_GYRO,
+	ADIS16136_SCAN_TEMP,
+};
+
+static const struct iio_chan_spec adis16136_channels[] = {
+	{
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
+			IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+			IIO_CHAN_INFO_SCALE_SHARED_BIT |
+			IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT,
+		.address = ADIS16136_REG_GYRO_OUT2,
+		.scan_index = ADIS16136_SCAN_GYRO,
+		.scan_type = {
+			.sign = 's',
+			.realbits = 32,
+			.storagebits = 32,
+			.endianness = IIO_BE,
+		},
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 0,
+		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
+			IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.address = ADIS16136_REG_TEMP_OUT,
+		.scan_index = ADIS16136_SCAN_TEMP,
+		.scan_type = {
+			.sign = 's',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_BE,
+		},
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static struct attribute *adis16136_attributes[] = {
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16136_attribute_group = {
+	.attrs = adis16136_attributes,
+};
+
+static const struct iio_info adis16136_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &adis16136_attribute_group,
+	.read_raw = &adis16136_read_raw,
+	.write_raw = &adis16136_write_raw,
+	.update_scan_mode = adis_update_scan_mode,
+	.debugfs_reg_access = adis_debugfs_reg_access,
+};
+
+static int adis16136_stop_device(struct iio_dev *indio_dev)
+{
+	struct adis16136 *adis16136 = iio_priv(indio_dev);
+	int ret;
+
+	ret = adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SLP_CTRL, 0xff);
+	if (ret)
+		dev_err(&indio_dev->dev,
+			"Could not power down device: %d\n", ret);
+
+	return ret;
+}
+
+static int adis16136_initial_setup(struct iio_dev *indio_dev)
+{
+	struct adis16136 *adis16136 = iio_priv(indio_dev);
+	unsigned int device_id;
+	uint16_t prod_id;
+	int ret;
+
+	ret = adis_initial_startup(&adis16136->adis);
+	if (ret)
+		return ret;
+
+	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
+		&prod_id);
+	if (ret)
+		return ret;
+
+	sscanf(indio_dev->name, "adis%u\n", &device_id);
+
+	if (prod_id != device_id)
+		dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
+				device_id, prod_id);
+
+	return 0;
+}
+
+static const char * const adis16136_status_error_msgs[] = {
+	[ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL] = "Flash update failed",
+	[ADIS16136_DIAG_STAT_SPI_FAIL] = "SPI failure",
+	[ADIS16136_DIAG_STAT_SELF_TEST_FAIL] = "Self test error",
+	[ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error",
+};
+
+static const struct adis_data adis16136_data = {
+	.diag_stat_reg = ADIS16136_REG_DIAG_STAT,
+	.glob_cmd_reg = ADIS16136_REG_GLOB_CMD,
+	.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL,
+
+	.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST,
+	.startup_delay = 80,
+
+	.read_delay = 10,
+	.write_delay = 10,
+
+	.status_error_msgs = adis16136_status_error_msgs,
+	.status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) |
+		BIT(ADIS16136_DIAG_STAT_SPI_FAIL) |
+		BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) |
+		BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL),
+};
+
+enum adis16136_id {
+	ID_ADIS16133,
+	ID_ADIS16135,
+	ID_ADIS16136,
+};
+
+static const struct adis16136_chip_info adis16136_chip_info[] = {
+	[ID_ADIS16133] = {
+		.precision = IIO_DEGREE_TO_RAD(1200),
+		.fullscale = 24000,
+	},
+	[ID_ADIS16135] = {
+		.precision = IIO_DEGREE_TO_RAD(300),
+		.fullscale = 24000,
+	},
+	[ID_ADIS16136] = {
+		.precision = IIO_DEGREE_TO_RAD(450),
+		.fullscale = 24623,
+	},
+};
+
+static int __devinit adis16136_probe(struct spi_device *spi)
+{
+	const struct spi_device_id *id = spi_get_device_id(spi);
+	struct adis16136 *adis16136;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	indio_dev = iio_device_alloc(sizeof(*adis16136));
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, indio_dev);
+
+	adis16136 = iio_priv(indio_dev);
+
+	adis16136->chip_info = &adis16136_chip_info[id->driver_data];
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->channels = adis16136_channels;
+	indio_dev->num_channels = ARRAY_SIZE(adis16136_channels);
+	indio_dev->info = &adis16136_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis_init(&adis16136->adis, indio_dev, spi, &adis16136_data);
+	if (ret)
+		goto error_free_dev;
+
+	ret = adis_setup_buffer_and_trigger(&adis16136->adis, indio_dev, NULL);
+	if (ret)
+		goto error_free_dev;
+
+	ret = adis16136_initial_setup(indio_dev);
+	if (ret)
+		goto error_cleanup_buffer;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_stop_device;
+
+	adis16136_debugfs_init(indio_dev);
+
+	return 0;
+
+error_stop_device:
+	adis16136_stop_device(indio_dev);
+error_cleanup_buffer:
+	adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
+error_free_dev:
+	iio_device_free(indio_dev);
+	return ret;
+}
+
+static int __devexit adis16136_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct adis16136 *adis16136 = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	adis16136_stop_device(indio_dev);
+
+	adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
+
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id adis16136_ids[] = {
+	{ "adis16133", ID_ADIS16133 },
+	{ "adis16135", ID_ADIS16135 },
+	{ "adis16136", ID_ADIS16136 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, adis16136_ids);
+
+static struct spi_driver adis16136_driver = {
+	.driver = {
+		.name = "adis16136",
+		.owner = THIS_MODULE,
+	},
+	.id_table = adis16136_ids,
+	.probe = adis16136_probe,
+	.remove = __devexit_p(adis16136_remove),
+};
+module_spi_driver(adis16136_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver");
+MODULE_LICENSE("GPL v2");
-- 
1.8.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 4/7] iio:imu:adis: Add paging support
  2012-11-20 13:36 [PATCH 1/7] iio:imu:adis: Add debugfs register access support Lars-Peter Clausen
  2012-11-20 13:36 ` [PATCH 2/7] iio:imu:adis: Add support for 32bit registers Lars-Peter Clausen
  2012-11-20 13:36 ` [PATCH 3/7] iio:gyro: Add support for the ADIS16136 gyroscope Lars-Peter Clausen
@ 2012-11-20 13:36 ` Lars-Peter Clausen
  2012-11-20 21:10   ` Jonathan Cameron
  2012-11-20 13:36 ` [PATCH 5/7] iio: Add pressure channel type Lars-Peter Clausen
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 16+ messages in thread
From: Lars-Peter Clausen @ 2012-11-20 13:36 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, drivers, Lars-Peter Clausen

Some of the newer generation devices from the ADIS16XXX series have more
registers than what can be supported with the current register addressing
scheme. These devices implement register paging to support a larger register
range. Each page is 128 registers large and the currently active page can be
selected via register 0x00 in each page. This patch implements transparent
paging inside the common adis library. The register read/write interface stays
the same and when a register is accessed the library automatically switches to
the correct page if it is not already selected. The page number is encoded in
the upper bits of the register number, e.g. register 0x5 of page 1 is 0x85.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/iio/imu/adis.c        | 70 +++++++++++++++++++++++++++++++++----------
 drivers/iio/imu/adis_buffer.c | 15 ++++++++++
 include/linux/iio/imu/adis.h  | 10 +++++--
 3 files changed, 77 insertions(+), 18 deletions(-)

diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
index 280a495..c4ea04f 100644
--- a/drivers/iio/imu/adis.c
+++ b/drivers/iio/imu/adis.c
@@ -30,6 +30,7 @@
 int adis_write_reg(struct adis *adis, unsigned int reg,
 	unsigned int value, unsigned int size)
 {
+	unsigned int page = reg / ADIS_PAGE_SIZE;
 	int ret, i;
 	struct spi_message msg;
 	struct spi_transfer xfers[] = {
@@ -56,39 +57,53 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
 			.bits_per_word = 8,
 			.len = 2,
 			.delay_usecs = adis->data->write_delay,
+		}, {
+			.tx_buf = adis->tx + 8,
+			.bits_per_word = 8,
+			.len = 2,
+			.delay_usecs = adis->data->write_delay,
 		},
 	};
 
 	mutex_lock(&adis->txrx_lock);
 
 	spi_message_init(&msg);
+
+	if (adis->current_page != page) {
+		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
+		adis->tx[1] = page;
+		spi_message_add_tail(&xfers[0], &msg);
+	}
+
 	switch (size) {
 	case 4:
-		adis->tx[6] = ADIS_WRITE_REG(reg + 3);
-		adis->tx[7] = (value >> 24) & 0xff;
-		adis->tx[4] = ADIS_WRITE_REG(reg + 2);
-		adis->tx[5] = (value >> 16) & 0xff;
+		adis->tx[8] = ADIS_WRITE_REG(reg + 3);
+		adis->tx[9] = (value >> 24) & 0xff;
+		adis->tx[6] = ADIS_WRITE_REG(reg + 2);
+		adis->tx[7] = (value >> 16) & 0xff;
 	case 2:
-		adis->tx[2] = ADIS_WRITE_REG(reg + 1);
-		adis->tx[3] = (value >> 8) & 0xff;
+		adis->tx[4] = ADIS_WRITE_REG(reg + 1);
+		adis->tx[5] = (value >> 8) & 0xff;
 	case 1:
-		adis->tx[0] = ADIS_WRITE_REG(reg);
-		adis->tx[1] = value & 0xff;
+		adis->tx[2] = ADIS_WRITE_REG(reg);
+		adis->tx[3] = value & 0xff;
 		break;
 	default:
 		ret = -EINVAL;
 		goto out_unlock;
 	}
 
-	xfers[size - 1].cs_change = 0;
+	xfers[size].cs_change = 0;
 
-	for (i = 0; i < size; i++)
+	for (i = 1; i <= size; i++)
 		spi_message_add_tail(&xfers[i], &msg);
 
 	ret = spi_sync(adis->spi, &msg);
 	if (ret) {
 		dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n",
 				reg, ret);
+	} else {
+		adis->current_page = page;
 	}
 
 out_unlock:
@@ -107,6 +122,7 @@ EXPORT_SYMBOL_GPL(adis_write_reg);
 int adis_read_reg(struct adis *adis, unsigned int reg,
 	unsigned int *val, unsigned int size)
 {
+	unsigned int page = reg / ADIS_PAGE_SIZE;
 	struct spi_message msg;
 	int ret;
 	struct spi_transfer xfers[] = {
@@ -115,9 +131,15 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
 			.bits_per_word = 8,
 			.len = 2,
 			.cs_change = 1,
-			.delay_usecs = adis->data->read_delay,
+			.delay_usecs = adis->data->write_delay,
 		}, {
 			.tx_buf = adis->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = adis->data->read_delay,
+		}, {
+			.tx_buf = adis->tx + 4,
 			.rx_buf = adis->rx,
 			.bits_per_word = 8,
 			.len = 2,
@@ -134,16 +156,22 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
 	mutex_lock(&adis->txrx_lock);
 	spi_message_init(&msg);
 
+	if (adis->current_page != page) {
+		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
+		adis->tx[1] = page;
+		spi_message_add_tail(&xfers[0], &msg);
+	}
+
 	switch (size) {
 	case 4:
-		adis->tx[0] = ADIS_READ_REG(reg + 2);
-		adis->tx[1] = 0;
-		spi_message_add_tail(&xfers[0], &msg);
-	case 2:
-		adis->tx[2] = ADIS_READ_REG(reg);
+		adis->tx[2] = ADIS_READ_REG(reg + 2);
 		adis->tx[3] = 0;
 		spi_message_add_tail(&xfers[1], &msg);
+	case 2:
+		adis->tx[4] = ADIS_READ_REG(reg);
+		adis->tx[5] = 0;
 		spi_message_add_tail(&xfers[2], &msg);
+		spi_message_add_tail(&xfers[3], &msg);
 		break;
 	default:
 		ret = -EINVAL;
@@ -155,6 +183,8 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
 		dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
 				reg, ret);
 		goto out_unlock;
+	} else {
+		adis->current_page = page;
 	}
 
 	switch (size) {
@@ -390,6 +420,14 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev,
 	adis->data = data;
 	iio_device_set_drvdata(indio_dev, adis);
 
+	if (data->has_paging) {
+		/* Need to set the page before first read/write */
+		adis->current_page = -1;
+	} else {
+		/* Page will always be 0 */
+		adis->current_page = 0;
+	}
+
 	return adis_enable_irq(adis, false);
 }
 EXPORT_SYMBOL_GPL(adis_init);
diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c
index 7857133..99d8e0b 100644
--- a/drivers/iio/imu/adis_buffer.c
+++ b/drivers/iio/imu/adis_buffer.c
@@ -83,10 +83,25 @@ static irqreturn_t adis_trigger_handler(int irq, void *p)
 	if (!adis->buffer)
 		return -ENOMEM;
 
+	if (adis->data->has_paging) {
+		mutex_lock(&adis->txrx_lock);
+		if (adis->current_page != 0) {
+			adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
+			adis->tx[1] = 0;
+			spi_write(adis->spi, adis->tx, 2);
+		}
+	}
+
 	ret = spi_sync(adis->spi, &adis->msg);
 	if (ret)
 		dev_err(&adis->spi->dev, "Failed to read data: %d", ret);
 
+
+	if (adis->data->has_paging) {
+		adis->current_page = 0;
+		mutex_unlock(&adis->txrx_lock);
+	}
+
 	/* Guaranteed to be aligned with 8 byte boundary */
 	if (indio_dev->scan_timestamp) {
 		void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64);
diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h
index 6402a08..e82cd08 100644
--- a/include/linux/iio/imu/adis.h
+++ b/include/linux/iio/imu/adis.h
@@ -14,8 +14,11 @@
 #include <linux/interrupt.h>
 #include <linux/iio/types.h>
 
-#define ADIS_WRITE_REG(reg) (0x80 | (reg))
-#define ADIS_READ_REG(reg) (reg)
+#define ADIS_WRITE_REG(reg) ((0x80 | (reg)))
+#define ADIS_READ_REG(reg) ((reg) & 0x7f)
+
+#define ADIS_PAGE_SIZE 0x80
+#define ADIS_REG_PAGE_ID 0x00
 
 /**
  * struct adis_data - ADIS chip variant specific data
@@ -40,6 +43,8 @@ struct adis_data {
 
 	const char * const *status_error_msgs;
 	unsigned int status_error_mask;
+
+	bool has_paging;
 };
 
 struct adis {
@@ -51,6 +56,7 @@ struct adis {
 	struct mutex		txrx_lock;
 	struct spi_message	msg;
 	struct spi_transfer	*xfer;
+	unsigned int		current_page;
 	void			*buffer;
 
 	uint8_t			tx[10] ____cacheline_aligned;
-- 
1.8.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 5/7] iio: Add pressure channel type
  2012-11-20 13:36 [PATCH 1/7] iio:imu:adis: Add debugfs register access support Lars-Peter Clausen
                   ` (2 preceding siblings ...)
  2012-11-20 13:36 ` [PATCH 4/7] iio:imu:adis: Add paging support Lars-Peter Clausen
@ 2012-11-20 13:36 ` Lars-Peter Clausen
  2012-11-20 21:12   ` Jonathan Cameron
  2012-11-20 13:36 ` [PATCH 6/7] iio: Factor out fixed point number parsing into its own function Lars-Peter Clausen
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 16+ messages in thread
From: Lars-Peter Clausen @ 2012-11-20 13:36 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, drivers, Lars-Peter Clausen

This patch adds support for a new IIO channel type for pressure measurements.
This can for example be used for barometric pressure sensors.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 Documentation/ABI/testing/sysfs-bus-iio | 24 ++++++++++++++++++++++++
 drivers/iio/industrialio-core.c         |  1 +
 include/linux/iio/types.h               |  1 +
 3 files changed, 26 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 2f06d40..f08d0f7 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -189,6 +189,14 @@ Description:
 		A computed peak value based on the sum squared magnitude of
 		the underlying value in the specified directions.
 
+What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_raw
+KernelVersion:	3.8
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Raw pressure measurement from channel Y. Units after
+		application of scale and offset are kilopascal.
+
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_x_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
@@ -197,6 +205,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_voltage_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_tempY_offset
 What:		/sys/bus/iio/devices/iio:deviceX/in_temp_offset
+What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
+What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_offset
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -226,6 +236,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_magn_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
 What:		/sys/bus/iio/devices/iio:deviceX/in_magn_z_scale
+What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
+What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_scale
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -245,6 +257,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibbias
 What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibbias
 What:		/sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibbias
 What:		/sys/bus/iio/devices/iio:deviceX/in_proximity0_calibbias
+What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_calibbias
+What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_calibbias
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -262,6 +276,8 @@ What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
 What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
 what		/sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibscale
 what		/sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale
+What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale
+What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -275,6 +291,8 @@ What:		/sys/.../iio:deviceX/in_voltage-voltage_scale_available
 What:		/sys/.../iio:deviceX/out_voltageX_scale_available
 What:		/sys/.../iio:deviceX/out_altvoltageX_scale_available
 What:		/sys/.../iio:deviceX/in_capacitance_scale_available
+What:		/sys/.../iio:deviceX/in_pressure_scale_available
+What:		/sys/.../iio:deviceX/in_pressureY_scale_available
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -694,6 +712,8 @@ What:		/sys/.../buffer/scan_elements/in_voltageY_en
 What:		/sys/.../buffer/scan_elements/in_voltageY-voltageZ_en
 What:		/sys/.../buffer/scan_elements/in_incli_x_en
 What:		/sys/.../buffer/scan_elements/in_incli_y_en
+What:		/sys/.../buffer/scan_elements/in_pressureY_en
+What:		/sys/.../buffer/scan_elements/in_pressure_en
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -707,6 +727,8 @@ What:		/sys/.../buffer/scan_elements/in_voltageY_type
 What:		/sys/.../buffer/scan_elements/in_voltage_type
 What:		/sys/.../buffer/scan_elements/in_voltageY_supply_type
 What:		/sys/.../buffer/scan_elements/in_timestamp_type
+What:		/sys/.../buffer/scan_elements/in_pressureY_type
+What:		/sys/.../buffer/scan_elements/in_pressure_type
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -751,6 +773,8 @@ What:		/sys/.../buffer/scan_elements/in_magn_z_index
 What:		/sys/.../buffer/scan_elements/in_incli_x_index
 What:		/sys/.../buffer/scan_elements/in_incli_y_index
 What:		/sys/.../buffer/scan_elements/in_timestamp_index
+What:		/sys/.../buffer/scan_elements/in_pressureY_index
+What:		/sys/.../buffer/scan_elements/in_pressure_index
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 060a404..3dccd6c 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -65,6 +65,7 @@ static const char * const iio_chan_type_name_spec[] = {
 	[IIO_CAPACITANCE] = "capacitance",
 	[IIO_ALTVOLTAGE] = "altvoltage",
 	[IIO_CCT] = "cct",
+	[IIO_PRESSURE] = "pressure",
 };
 
 static const char * const iio_modifier_names[] = {
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index 87b196a..88bf0f0 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -28,6 +28,7 @@ enum iio_chan_type {
 	IIO_CAPACITANCE,
 	IIO_ALTVOLTAGE,
 	IIO_CCT,
+	IIO_PRESSURE,
 };
 
 enum iio_modifier {
-- 
1.8.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 6/7] iio: Factor out fixed point number parsing into its own function
  2012-11-20 13:36 [PATCH 1/7] iio:imu:adis: Add debugfs register access support Lars-Peter Clausen
                   ` (3 preceding siblings ...)
  2012-11-20 13:36 ` [PATCH 5/7] iio: Add pressure channel type Lars-Peter Clausen
@ 2012-11-20 13:36 ` Lars-Peter Clausen
  2012-11-20 21:14   ` Jonathan Cameron
  2012-11-20 13:36 ` [PATCH 7/7] iio:imu: Add support for the ADIS16480 and similar IMUs Lars-Peter Clausen
  2012-11-20 19:51 ` [PATCH 1/7] iio:imu:adis: Add debugfs register access support Jonathan Cameron
  6 siblings, 1 reply; 16+ messages in thread
From: Lars-Peter Clausen @ 2012-11-20 13:36 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, drivers, Lars-Peter Clausen

Factor out the code for parsing fixed point numbers into its own function and
make this function globally available. This allows us to reuse the code to parse
fixed point numbers in individual IIO drivers.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/iio/industrialio-core.c | 98 ++++++++++++++++++++++++++---------------
 include/linux/iio/iio.h         |  3 ++
 2 files changed, 66 insertions(+), 35 deletions(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 3dccd6c..8848f16 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -408,6 +408,64 @@ static ssize_t iio_read_channel_info(struct device *dev,
 	}
 }
 
+/**
+ * iio_str_to_fixpoint() - Parse a fixed-point number from a string
+ * @str: The string to parse
+ * @fract_mult: Multiplier for the first decimal place, should be a power of 10
+ * @integer: The integer part of the number
+ * @fract: The fractional part of the number
+ *
+ * Returns 0 on success, or a negative error code if the string could not be
+ * parsed.
+ */
+int iio_str_to_fixpoint(const char *str, int fract_mult,
+	int *integer, int *fract)
+{
+	int i = 0, f = 0;
+	bool integer_part = true, negative = false;
+
+	if (str[0] == '-') {
+		negative = true;
+		str++;
+	} else if (str[0] == '+') {
+		str++;
+	}
+
+	while (*str) {
+		if ('0' <= *str && *str <= '9') {
+			if (integer_part) {
+				i = i * 10 + *str - '0';
+			} else {
+				f += fract_mult * (*str - '0');
+				fract_mult /= 10;
+			}
+		} else if (*str == '\n') {
+			if (*(str + 1) == '\0')
+				break;
+			else
+				return -EINVAL;
+		} else if (*str == '.' && integer_part) {
+			integer_part = false;
+		} else {
+			return -EINVAL;
+		}
+		str++;
+	}
+
+	if (negative) {
+		if (i)
+			i = -i;
+		else
+			f = -f;
+	}
+
+	*integer = i;
+	*fract = f;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iio_str_to_fixpoint);
+
 static ssize_t iio_write_channel_info(struct device *dev,
 				      struct device_attribute *attr,
 				      const char *buf,
@@ -415,8 +473,8 @@ static ssize_t iio_write_channel_info(struct device *dev,
 {
 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
-	int ret, integer = 0, fract = 0, fract_mult = 100000;
-	bool integer_part = true, negative = false;
+	int ret, fract_mult = 100000;
+	int integer, fract;
 
 	/* Assumes decimal - precision based on number of digits */
 	if (!indio_dev->info->write_raw)
@@ -435,39 +493,9 @@ static ssize_t iio_write_channel_info(struct device *dev,
 			return -EINVAL;
 		}
 
-	if (buf[0] == '-') {
-		negative = true;
-		buf++;
-	} else if (buf[0] == '+') {
-		buf++;
-	}
-
-	while (*buf) {
-		if ('0' <= *buf && *buf <= '9') {
-			if (integer_part)
-				integer = integer*10 + *buf - '0';
-			else {
-				fract += fract_mult*(*buf - '0');
-				fract_mult /= 10;
-			}
-		} else if (*buf == '\n') {
-			if (*(buf + 1) == '\0')
-				break;
-			else
-				return -EINVAL;
-		} else if (*buf == '.' && integer_part) {
-			integer_part = false;
-		} else {
-			return -EINVAL;
-		}
-		buf++;
-	}
-	if (negative) {
-		if (integer)
-			integer = -integer;
-		else
-			fract = -fract;
-	}
+	ret = iio_str_to_fixpoint(buf, fract_mult, &integer, &fract);
+	if (ret)
+		return ret;
 
 	ret = indio_dev->info->write_raw(indio_dev, this_attr->c,
 					 integer, fract, this_attr->address);
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index adca93a..da8c776 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -620,6 +620,9 @@ static inline struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev)
 };
 #endif
 
+int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
+	int *fract);
+
 /**
  * IIO_DEGREE_TO_RAD() - Convert degree to rad
  * @deg: A value in degree
-- 
1.8.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 7/7] iio:imu: Add support for the ADIS16480 and similar IMUs
  2012-11-20 13:36 [PATCH 1/7] iio:imu:adis: Add debugfs register access support Lars-Peter Clausen
                   ` (4 preceding siblings ...)
  2012-11-20 13:36 ` [PATCH 6/7] iio: Factor out fixed point number parsing into its own function Lars-Peter Clausen
@ 2012-11-20 13:36 ` Lars-Peter Clausen
  2012-11-20 21:24   ` Jonathan Cameron
  2012-11-20 19:51 ` [PATCH 1/7] iio:imu:adis: Add debugfs register access support Jonathan Cameron
  6 siblings, 1 reply; 16+ messages in thread
From: Lars-Peter Clausen @ 2012-11-20 13:36 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, drivers, Lars-Peter Clausen

This patch adds support for the ADIS16375, ADIS16480, ADIS16485, ADIS16488 6
degree to 10 degree of freedom IMUs.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/iio/imu/Kconfig      |  16 +
 drivers/iio/imu/Makefile     |   2 +
 drivers/iio/imu/adis.c       |   3 +
 drivers/iio/imu/adis16480.c  | 925 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/iio/imu/adis.h |   4 +
 5 files changed, 950 insertions(+)
 create mode 100644 drivers/iio/imu/adis16480.c

diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index c24410c..8685a16 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -1,3 +1,19 @@
+#
+# IIO imu drivers configuration
+#
+menu "Inertial measurement units"
+
+config ADIS16480
+	tristate "Analog Devices ADIS16480 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 ADIS16375, ADIS16480,
+	  ADIS16485, ADIS16488 inertial sensors.
+
+endmenu
+
 config IIO_ADIS_LIB
 	tristate
 	help
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index 97676ab..cfe5763 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -2,6 +2,8 @@
 # Makefile for Inertial Measurement Units
 #
 
+obj-$(CONFIG_ADIS16480) += adis16480.o
+
 adis_lib-y += adis.o
 adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o
 adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
index c4ea04f..911255d 100644
--- a/drivers/iio/imu/adis.c
+++ b/drivers/iio/imu/adis.c
@@ -238,6 +238,9 @@ int adis_enable_irq(struct adis *adis, bool enable)
 	int ret = 0;
 	uint16_t msc;
 
+	if (adis->data->enable_irq)
+		return adis->data->enable_irq(adis, enable);
+
 	ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc);
 	if (ret)
 		goto error_ret;
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
new file mode 100644
index 0000000..a058527
--- /dev/null
+++ b/drivers/iio/imu/adis16480.c
@@ -0,0 +1,925 @@
+/*
+ * ADIS16480 and similar IMUs driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include <linux/iio/iio.h>
+#include <linux/debugfs.h>
+
+#define ADIS16480_PAGE_SIZE 0x80
+
+#define ADIS16480_REG(page, reg) ((page) * ADIS16480_PAGE_SIZE + (reg))
+
+#define ADIS16480_REG_PAGE_ID 0x00 /* Same address on each page */
+#define ADIS16480_REG_SEQ_CNT			ADIS16480_REG(0x00, 0x06)
+#define ADIS16480_REG_SYS_E_FLA			ADIS16480_REG(0x00, 0x08)
+#define ADIS16480_REG_DIAG_STS			ADIS16480_REG(0x00, 0x0A)
+#define ADIS16480_REG_ALM_STS			ADIS16480_REG(0x00, 0x0C)
+#define ADIS16480_REG_TEMP_OUT			ADIS16480_REG(0x00, 0x0E)
+#define ADIS16480_REG_X_GYRO_OUT		ADIS16480_REG(0x00, 0x10)
+#define ADIS16480_REG_Y_GYRO_OUT		ADIS16480_REG(0x00, 0x14)
+#define ADIS16480_REG_Z_GYRO_OUT		ADIS16480_REG(0x00, 0x18)
+#define ADIS16480_REG_X_ACCEL_OUT		ADIS16480_REG(0x00, 0x1C)
+#define ADIS16480_REG_Y_ACCEL_OUT		ADIS16480_REG(0x00, 0x20)
+#define ADIS16480_REG_Z_ACCEL_OUT		ADIS16480_REG(0x00, 0x24)
+#define ADIS16480_REG_X_MAGN_OUT		ADIS16480_REG(0x00, 0x28)
+#define ADIS16480_REG_Y_MAGN_OUT		ADIS16480_REG(0x00, 0x2A)
+#define ADIS16480_REG_Z_MAGN_OUT		ADIS16480_REG(0x00, 0x2C)
+#define ADIS16480_REG_BAROM_OUT			ADIS16480_REG(0x00, 0x2E)
+#define ADIS16480_REG_X_DELTAANG_OUT		ADIS16480_REG(0x00, 0x40)
+#define ADIS16480_REG_Y_DELTAANG_OUT		ADIS16480_REG(0x00, 0x44)
+#define ADIS16480_REG_Z_DELTAANG_OUT		ADIS16480_REG(0x00, 0x48)
+#define ADIS16480_REG_X_DELTAVEL_OUT		ADIS16480_REG(0x00, 0x4C)
+#define ADIS16480_REG_Y_DELTAVEL_OUT		ADIS16480_REG(0x00, 0x50)
+#define ADIS16480_REG_Z_DELTAVEL_OUT		ADIS16480_REG(0x00, 0x54)
+#define ADIS16480_REG_PROD_ID			ADIS16480_REG(0x00, 0x7E)
+
+#define ADIS16480_REG_X_GYRO_SCALE		ADIS16480_REG(0x02, 0x04)
+#define ADIS16480_REG_Y_GYRO_SCALE		ADIS16480_REG(0x02, 0x06)
+#define ADIS16480_REG_Z_GYRO_SCALE		ADIS16480_REG(0x02, 0x08)
+#define ADIS16480_REG_X_ACCEL_SCALE		ADIS16480_REG(0x02, 0x0A)
+#define ADIS16480_REG_Y_ACCEL_SCALE		ADIS16480_REG(0x02, 0x0C)
+#define ADIS16480_REG_Z_ACCEL_SCALE		ADIS16480_REG(0x02, 0x0E)
+#define ADIS16480_REG_X_GYRO_BIAS		ADIS16480_REG(0x02, 0x10)
+#define ADIS16480_REG_Y_GYRO_BIAS		ADIS16480_REG(0x02, 0x14)
+#define ADIS16480_REG_Z_GYRO_BIAS		ADIS16480_REG(0x02, 0x18)
+#define ADIS16480_REG_X_ACCEL_BIAS		ADIS16480_REG(0x02, 0x1C)
+#define ADIS16480_REG_Y_ACCEL_BIAS		ADIS16480_REG(0x02, 0x20)
+#define ADIS16480_REG_Z_ACCEL_BIAS		ADIS16480_REG(0x02, 0x24)
+#define ADIS16480_REG_X_HARD_IRON		ADIS16480_REG(0x02, 0x28)
+#define ADIS16480_REG_Y_HARD_IRON		ADIS16480_REG(0x02, 0x2A)
+#define ADIS16480_REG_Z_HARD_IRON		ADIS16480_REG(0x02, 0x2C)
+#define ADIS16480_REG_BAROM_BIAS		ADIS16480_REG(0x02, 0x40)
+#define ADIS16480_REG_FLASH_CNT			ADIS16480_REG(0x02, 0x7C)
+
+#define ADIS16480_REG_GLOB_CMD			ADIS16480_REG(0x03, 0x02)
+#define ADIS16480_REG_FNCTIO_CTRL		ADIS16480_REG(0x03, 0x06)
+#define ADIS16480_REG_GPIO_CTRL			ADIS16480_REG(0x03, 0x08)
+#define ADIS16480_REG_CONFIG			ADIS16480_REG(0x03, 0x0A)
+#define ADIS16480_REG_DEC_RATE			ADIS16480_REG(0x03, 0x0C)
+#define ADIS16480_REG_SLP_CNT			ADIS16480_REG(0x03, 0x10)
+#define ADIS16480_REG_FILTER_BNK0		ADIS16480_REG(0x03, 0x16)
+#define ADIS16480_REG_FILTER_BNK1		ADIS16480_REG(0x03, 0x18)
+#define ADIS16480_REG_ALM_CNFG0			ADIS16480_REG(0x03, 0x20)
+#define ADIS16480_REG_ALM_CNFG1			ADIS16480_REG(0x03, 0x22)
+#define ADIS16480_REG_ALM_CNFG2			ADIS16480_REG(0x03, 0x24)
+#define ADIS16480_REG_XG_ALM_MAGN		ADIS16480_REG(0x03, 0x28)
+#define ADIS16480_REG_YG_ALM_MAGN		ADIS16480_REG(0x03, 0x2A)
+#define ADIS16480_REG_ZG_ALM_MAGN		ADIS16480_REG(0x03, 0x2C)
+#define ADIS16480_REG_XA_ALM_MAGN		ADIS16480_REG(0x03, 0x2E)
+#define ADIS16480_REG_YA_ALM_MAGN		ADIS16480_REG(0x03, 0x30)
+#define ADIS16480_REG_ZA_ALM_MAGN		ADIS16480_REG(0x03, 0x32)
+#define ADIS16480_REG_XM_ALM_MAGN		ADIS16480_REG(0x03, 0x34)
+#define ADIS16480_REG_YM_ALM_MAGN		ADIS16480_REG(0x03, 0x36)
+#define ADIS16480_REG_ZM_ALM_MAGN		ADIS16480_REG(0x03, 0x38)
+#define ADIS16480_REG_BR_ALM_MAGN		ADIS16480_REG(0x03, 0x3A)
+#define ADIS16480_REG_FIRM_REV			ADIS16480_REG(0x03, 0x78)
+#define ADIS16480_REG_FIRM_DM			ADIS16480_REG(0x03, 0x7A)
+#define ADIS16480_REG_FIRM_Y			ADIS16480_REG(0x03, 0x7C)
+
+#define ADIS16480_REG_SERIAL_NUM		ADIS16480_REG(0x04, 0x20)
+
+/* Each filter coefficent bank spans two pages */
+#define ADIS16480_FIR_COEF(page) (x < 60 ? ADIS16480_REG(page, (x) + 8) : \
+		ADIS16480_REG((page) + 1, (x) - 60 + 8))
+#define ADIS16480_FIR_COEF_A(x)			ADIS16480_FIR_COEF(0x05, (x))
+#define ADIS16480_FIR_COEF_B(x)			ADIS16480_FIR_COEF(0x07, (x))
+#define ADIS16480_FIR_COEF_C(x)			ADIS16480_FIR_COEF(0x09, (x))
+#define ADIS16480_FIR_COEF_D(x)			ADIS16480_FIR_COEF(0x0B, (x))
+
+struct adis16480_chip_info {
+	unsigned int num_channels;
+	const struct iio_chan_spec *channels;
+};
+
+struct adis16480 {
+	const struct adis16480_chip_info *chip_info;
+
+	struct adis adis;
+};
+
+#ifdef CONFIG_DEBUG_FS
+
+static ssize_t adis16480_show_firmware_revision(struct file *file,
+		char __user *userbuf, size_t count, loff_t *ppos)
+{
+	struct adis16480 *adis16480 = file->private_data;
+	char buf[6];
+	size_t len;
+	u16 rev;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_REV, &rev);
+	if (ret < 0)
+		return ret;
+
+	len = snprintf(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 adis16480_firmware_revision_fops = {
+	.open = simple_open,
+	.read = adis16480_show_firmware_revision,
+	.llseek = default_llseek,
+	.owner = THIS_MODULE,
+};
+
+static ssize_t adis16480_show_firmware_date(struct file *file,
+		char __user *userbuf, size_t count, loff_t *ppos)
+{
+	struct adis16480 *adis16480 = file->private_data;
+	u16 md, year;
+	char buf[12];
+	size_t len;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_Y, &year);
+	if (ret < 0)
+		return ret;
+
+	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_DM, &md);
+	if (ret < 0)
+		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 adis16480_firmware_date_fops = {
+	.open = simple_open,
+	.read = adis16480_show_firmware_date,
+	.llseek = default_llseek,
+	.owner = THIS_MODULE,
+};
+
+static int adis16480_show_serial_number(void *arg, u64 *val)
+{
+	struct adis16480 *adis16480 = arg;
+	u16 serial;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_SERIAL_NUM,
+		&serial);
+	if (ret < 0)
+		return ret;
+
+	*val = serial;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16480_serial_number_fops,
+	adis16480_show_serial_number, NULL, "0x%.4llx\n");
+
+static int adis16480_show_product_id(void *arg, u64 *val)
+{
+	struct adis16480 *adis16480 = arg;
+	u16 prod_id;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_PROD_ID,
+		&prod_id);
+	if (ret < 0)
+		return ret;
+
+	*val = prod_id;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16480_product_id_fops,
+	adis16480_show_product_id, NULL, "%llu\n");
+
+static int adis16480_show_flash_count(void *arg, u64 *val)
+{
+	struct adis16480 *adis16480 = arg;
+	u32 flash_count;
+	int ret;
+
+	ret = adis_read_reg_32(&adis16480->adis, ADIS16480_REG_FLASH_CNT,
+		&flash_count);
+	if (ret < 0)
+		return ret;
+
+	*val = flash_count;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16480_flash_count_fops,
+	adis16480_show_flash_count, NULL, "%lld\n");
+
+static int adis16480_debugfs_init(struct iio_dev *indio_dev)
+{
+	struct adis16480 *adis16480 = iio_priv(indio_dev);
+
+	debugfs_create_file("firmware_revision", 0400,
+		indio_dev->debugfs_dentry, adis16480,
+		&adis16480_firmware_revision_fops);
+	debugfs_create_file("firmware_date", 0400, indio_dev->debugfs_dentry,
+		adis16480, &adis16480_firmware_date_fops);
+	debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
+		adis16480, &adis16480_serial_number_fops);
+	debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
+		adis16480, &adis16480_product_id_fops);
+	debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
+		adis16480, &adis16480_flash_count_fops);
+
+	return 0;
+}
+
+#else
+
+static int adis16480_debugfs_init(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+#endif
+
+static int adis16480_set_freq(struct adis16480 *st, unsigned int freq)
+{
+	unsigned int t;
+
+	t = 2460000 / freq;
+	if (t > 2048)
+		t = 2048;
+
+	if (t != 0)
+		t--;
+
+	return adis_write_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, t);
+}
+
+static int adis16480_get_freq(struct adis16480 *st, unsigned int *freq)
+{
+	uint16_t t;
+	int ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, &t);
+	if (ret < 0)
+		return ret;
+
+	*freq = 2460000 / (t + 1);
+
+	return 0;
+}
+
+static ssize_t adis16480_read_frequency(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct adis16480 *st = iio_priv(indio_dev);
+	unsigned int freq;
+	int ret;
+
+	ret = adis16480_get_freq(st, &freq);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d.%.3d\n", freq / 1000, freq % 1000);
+}
+
+static ssize_t adis16480_write_frequency(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct adis16480 *st = iio_priv(indio_dev);
+	int freq_int, freq_fract;
+	long val;
+	int ret;
+
+	ret = iio_str_to_fixpoint(buf, 100, &freq_int, &freq_fract);
+	if (ret)
+		return ret;
+
+	val = freq_int * 1000 + freq_fract;
+
+	if (val <= 0)
+		return -EINVAL;
+
+	ret = adis16480_set_freq(st, val);
+
+	return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+			      adis16480_read_frequency,
+			      adis16480_write_frequency);
+
+enum {
+	ADIS16480_SCAN_GYRO_X,
+	ADIS16480_SCAN_GYRO_Y,
+	ADIS16480_SCAN_GYRO_Z,
+	ADIS16480_SCAN_ACCEL_X,
+	ADIS16480_SCAN_ACCEL_Y,
+	ADIS16480_SCAN_ACCEL_Z,
+	ADIS16480_SCAN_MAGN_X,
+	ADIS16480_SCAN_MAGN_Y,
+	ADIS16480_SCAN_MAGN_Z,
+	ADIS16480_SCAN_BARO,
+	ADIS16480_SCAN_TEMP,
+};
+
+static const unsigned int adis16480_calibbias_regs[] = {
+	[ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_BIAS,
+	[ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_BIAS,
+	[ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_BIAS,
+	[ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_BIAS,
+	[ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_BIAS,
+	[ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_BIAS,
+	[ADIS16480_SCAN_MAGN_X] = ADIS16480_REG_X_HARD_IRON,
+	[ADIS16480_SCAN_MAGN_Y] = ADIS16480_REG_Y_HARD_IRON,
+	[ADIS16480_SCAN_MAGN_Z] = ADIS16480_REG_Z_HARD_IRON,
+	[ADIS16480_SCAN_BARO] = ADIS16480_REG_BAROM_BIAS,
+};
+
+static const unsigned int adis16480_calibscale_regs[] = {
+	[ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_SCALE,
+	[ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_SCALE,
+	[ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_SCALE,
+	[ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_SCALE,
+	[ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_SCALE,
+	[ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_SCALE,
+};
+
+static int adis16480_set_calibbias(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int bias)
+{
+	unsigned int reg = adis16480_calibbias_regs[chan->scan_index];
+	struct adis16480 *st = iio_priv(indio_dev);
+
+	switch (chan->type) {
+	case IIO_MAGN:
+	case IIO_PRESSURE:
+		if (bias < -0x8000 || bias >= 0x8000)
+			return -EINVAL;
+		return adis_write_reg_16(&st->adis, reg, bias);
+	case IIO_ANGL_VEL:
+	case IIO_ACCEL:
+		return adis_write_reg_32(&st->adis, reg, bias);
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
+static int adis16480_get_calibbias(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *bias)
+{
+	unsigned int reg = adis16480_calibbias_regs[chan->scan_index];
+	struct adis16480 *st = iio_priv(indio_dev);
+	uint16_t val16;
+	uint32_t val32;
+	int ret;
+
+	switch (chan->type) {
+	case IIO_MAGN:
+	case IIO_PRESSURE:
+		ret = adis_read_reg_16(&st->adis, reg, &val16);
+		*bias = sign_extend32(val16, 15);
+		break;
+	case IIO_ANGL_VEL:
+	case IIO_ACCEL:
+		ret = adis_read_reg_32(&st->adis, reg, &val32);
+		*bias = sign_extend32(val32, 31);
+		break;
+	default:
+			ret = -EINVAL;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	return IIO_VAL_INT;
+}
+
+static int adis16480_set_calibscale(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int scale)
+{
+	unsigned int reg = adis16480_calibscale_regs[chan->scan_index];
+	struct adis16480 *st = iio_priv(indio_dev);
+
+	if (scale < -0x8000 || scale >= 0x8000)
+		return -EINVAL;
+
+	return adis_write_reg_16(&st->adis, reg, scale);
+}
+
+static int adis16480_get_calibscale(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *scale)
+{
+	unsigned int reg = adis16480_calibscale_regs[chan->scan_index];
+	struct adis16480 *st = iio_priv(indio_dev);
+	uint16_t val16;
+	int ret;
+
+	ret = adis_read_reg_16(&st->adis, reg, &val16);
+	if (ret < 0)
+		return ret;
+
+	*scale = sign_extend32(val16, 15);
+	return IIO_VAL_INT;
+}
+
+static const unsigned int adis16480_def_filter_freqs[] = {
+	310,
+	55,
+	275,
+	63,
+};
+
+static const unsigned int ad16480_filter_data[][2] = {
+	[ADIS16480_SCAN_GYRO_X]		= { ADIS16480_REG_FILTER_BNK0, 0 },
+	[ADIS16480_SCAN_GYRO_Y]		= { ADIS16480_REG_FILTER_BNK0, 3 },
+	[ADIS16480_SCAN_GYRO_Z]		= { ADIS16480_REG_FILTER_BNK0, 6 },
+	[ADIS16480_SCAN_ACCEL_X]	= { ADIS16480_REG_FILTER_BNK0, 9 },
+	[ADIS16480_SCAN_ACCEL_Y]	= { ADIS16480_REG_FILTER_BNK0, 12 },
+	[ADIS16480_SCAN_ACCEL_Z]	= { ADIS16480_REG_FILTER_BNK1, 0 },
+	[ADIS16480_SCAN_MAGN_X]		= { ADIS16480_REG_FILTER_BNK1, 3 },
+	[ADIS16480_SCAN_MAGN_Y]		= { ADIS16480_REG_FILTER_BNK1, 6 },
+	[ADIS16480_SCAN_MAGN_Z]		= { ADIS16480_REG_FILTER_BNK1, 9 },
+};
+
+static int adis16480_get_filter_freq(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *freq)
+{
+	struct adis16480 *st = iio_priv(indio_dev);
+	unsigned int enable_mask, offset, reg;
+	uint16_t val;
+	int ret;
+
+	reg = ad16480_filter_data[chan->scan_index][0];
+	offset = ad16480_filter_data[chan->scan_index][1];
+	enable_mask = BIT(offset + 2);
+
+	ret = adis_read_reg_16(&st->adis, reg, &val);
+	if (ret < 0)
+		return ret;
+
+	if (!(val & enable_mask))
+		*freq = 0;
+	else
+		*freq = adis16480_def_filter_freqs[(val >> offset) & 0x3];
+
+	return IIO_VAL_INT;
+}
+
+static int adis16480_set_filter_freq(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, unsigned int freq)
+{
+	struct adis16480 *st = iio_priv(indio_dev);
+	unsigned int enable_mask, offset, reg;
+	unsigned int diff, best_diff;
+	unsigned int i, best_freq;
+	uint16_t val;
+	int ret;
+
+	reg = ad16480_filter_data[chan->scan_index][0];
+	offset = ad16480_filter_data[chan->scan_index][1];
+	enable_mask = BIT(offset + 2);
+
+	ret = adis_read_reg_16(&st->adis, reg, &val);
+	if (ret < 0)
+		return ret;
+
+	if (freq == 0) {
+		val &= ~enable_mask;
+	} else {
+		best_freq = 0;
+		best_diff = 310;
+		for (i = 0; i < ARRAY_SIZE(adis16480_def_filter_freqs); i++) {
+			if (adis16480_def_filter_freqs[i] >= freq) {
+				diff = adis16480_def_filter_freqs[i] - freq;
+				if (diff < best_diff) {
+					best_diff = diff;
+					best_freq = i;
+				}
+			}
+		}
+
+		val &= ~(0x3 << offset);
+		val |= best_freq << offset;
+		val |= enable_mask;
+	}
+
+	return adis_write_reg_16(&st->adis, reg, val);
+}
+
+static int adis16480_read_raw(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+	switch (info) {
+	case IIO_CHAN_INFO_RAW:
+		return adis_single_conversion(indio_dev, chan, 0, val);
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*val = 0;
+			*val2 = IIO_DEGREE_TO_RAD(20000); /* 0.02 degree/sec */
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_ACCEL:
+			*val = 0;
+			*val2 = IIO_G_TO_M_S_2(800); /* 0.8 mg */
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_MAGN:
+			*val = 0;
+			*val2 = 100; /* 0.0001 gauss */
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_TEMP:
+			*val = 5;
+			*val2 = 650000; /* 5.65 milli degree Celsius */
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_PRESSURE:
+			*val = 0;
+			*val2 = 4000; /* 40ubar = 0.004 kPa */
+			return IIO_VAL_INT_PLUS_MICRO;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_OFFSET:
+		/* Only the temperature channel has a offset */
+		*val = 4425; /* 25 degree Celsius = 0x0000 */
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return adis16480_get_calibbias(indio_dev, chan, val);
+	case IIO_CHAN_INFO_CALIBSCALE:
+		return adis16480_get_calibscale(indio_dev, chan, val);
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		return adis16480_get_filter_freq(indio_dev, chan, val);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int adis16480_write_raw(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int val, int val2, long info)
+{
+	switch (info) {
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return adis16480_set_calibbias(indio_dev, chan, val);
+	case IIO_CHAN_INFO_CALIBSCALE:
+		return adis16480_set_calibscale(indio_dev, chan, val);
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		return adis16480_set_filter_freq(indio_dev, chan, val);
+	default:
+		return -EINVAL;
+	}
+}
+
+#define ADIS16480_MOD_CHANNEL(_type, _mod, _address, _si, _info, _bits) \
+	{ \
+		.type = (_type), \
+		.modified = 1, \
+		.channel2 = (_mod), \
+		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+			IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
+			IIO_CHAN_INFO_SCALE_SHARED_BIT | \
+			_info, \
+		.address = (_address), \
+		.scan_index = (_si), \
+		.scan_type = { \
+			.sign = 's', \
+			.realbits = (_bits), \
+			.storagebits = (_bits), \
+			.endianness = IIO_BE, \
+		}, \
+	}
+
+#define ADIS16480_GYRO_CHANNEL(_mod) \
+	ADIS16480_MOD_CHANNEL(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \
+	ADIS16480_REG_ ## _mod ## _GYRO_OUT, ADIS16480_SCAN_GYRO_ ## _mod, \
+	IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \
+	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \
+	32)
+
+#define ADIS16480_ACCEL_CHANNEL(_mod) \
+	ADIS16480_MOD_CHANNEL(IIO_ACCEL, IIO_MOD_ ## _mod, \
+	ADIS16480_REG_ ## _mod ## _ACCEL_OUT, ADIS16480_SCAN_ACCEL_ ## _mod, \
+	IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \
+	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \
+	32)
+
+#define ADIS16480_MAGN_CHANNEL(_mod) \
+	ADIS16480_MOD_CHANNEL(IIO_MAGN, IIO_MOD_ ## _mod, \
+	ADIS16480_REG_ ## _mod ## _MAGN_OUT, ADIS16480_SCAN_MAGN_ ## _mod, \
+	IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT, \
+	16)
+
+#define ADIS16480_PRESSURE_CHANNEL() \
+	{ \
+		.type = IIO_PRESSURE, \
+		.indexed = 1, \
+		.channel = 0, \
+		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+			IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
+			IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+		.address = ADIS16480_REG_BAROM_OUT, \
+		.scan_index = ADIS16480_SCAN_BARO, \
+		.scan_type = { \
+			.sign = 's', \
+			.realbits = 32, \
+			.storagebits = 32, \
+			.endianness = IIO_BE, \
+		}, \
+	}
+
+#define ADIS16480_TEMP_CHANNEL() { \
+		.type = IIO_TEMP, \
+		.indexed = 1, \
+		.channel = 0, \
+		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+			IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
+			IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \
+		.address = ADIS16480_REG_TEMP_OUT, \
+		.scan_index = ADIS16480_SCAN_TEMP, \
+		.scan_type = { \
+			.sign = 's', \
+			.realbits = 16, \
+			.storagebits = 16, \
+			.endianness = IIO_BE, \
+		}, \
+	}
+
+static const struct iio_chan_spec adis16480_channels[] = {
+	ADIS16480_GYRO_CHANNEL(X),
+	ADIS16480_GYRO_CHANNEL(Y),
+	ADIS16480_GYRO_CHANNEL(Z),
+	ADIS16480_ACCEL_CHANNEL(X),
+	ADIS16480_ACCEL_CHANNEL(Y),
+	ADIS16480_ACCEL_CHANNEL(Z),
+	ADIS16480_MAGN_CHANNEL(X),
+	ADIS16480_MAGN_CHANNEL(Y),
+	ADIS16480_MAGN_CHANNEL(Z),
+	ADIS16480_PRESSURE_CHANNEL(),
+	ADIS16480_TEMP_CHANNEL(),
+	IIO_CHAN_SOFT_TIMESTAMP(11)
+};
+
+static const struct iio_chan_spec adis16485_channels[] = {
+	ADIS16480_GYRO_CHANNEL(X),
+	ADIS16480_GYRO_CHANNEL(Y),
+	ADIS16480_GYRO_CHANNEL(Z),
+	ADIS16480_ACCEL_CHANNEL(X),
+	ADIS16480_ACCEL_CHANNEL(Y),
+	ADIS16480_ACCEL_CHANNEL(Z),
+	ADIS16480_TEMP_CHANNEL(),
+	IIO_CHAN_SOFT_TIMESTAMP(7)
+};
+
+enum adis16480_variant {
+	ADIS16375,
+	ADIS16480,
+	ADIS16485,
+	ADIS16488,
+};
+
+static const struct adis16480_chip_info adis16480_chip_info[] = {
+	[ADIS16375] = {
+		.channels = adis16485_channels,
+		.num_channels = ARRAY_SIZE(adis16485_channels),
+	},
+	[ADIS16480] = {
+		.channels = adis16480_channels,
+		.num_channels = ARRAY_SIZE(adis16480_channels),
+	},
+	[ADIS16485] = {
+		.channels = adis16485_channels,
+		.num_channels = ARRAY_SIZE(adis16485_channels),
+	},
+	[ADIS16488] = {
+		.channels = adis16480_channels,
+		.num_channels = ARRAY_SIZE(adis16480_channels),
+	},
+};
+
+static struct attribute *adis16480_attributes[] = {
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16480_attribute_group = {
+	.attrs = adis16480_attributes,
+};
+
+static const struct iio_info adis16480_info = {
+	.attrs = &adis16480_attribute_group,
+	.read_raw = &adis16480_read_raw,
+	.write_raw = &adis16480_write_raw,
+	.update_scan_mode = adis_update_scan_mode,
+	.driver_module = THIS_MODULE,
+};
+
+static int adis16480_stop_device(struct iio_dev *indio_dev)
+{
+	struct adis16480 *st = iio_priv(indio_dev);
+	int ret;
+
+	ret = adis_write_reg_16(&st->adis, ADIS16480_REG_SLP_CNT, BIT(9));
+	if (ret)
+		dev_err(&indio_dev->dev,
+			"Could not power down device: %d\n", ret);
+
+	return ret;
+}
+
+static int adis16480_enable_irq(struct adis *adis, bool enable)
+{
+	return adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL,
+		enable ? BIT(3) : 0);
+}
+
+static int adis16480_initial_setup(struct iio_dev *indio_dev)
+{
+	struct adis16480 *st = iio_priv(indio_dev);
+	uint16_t prod_id;
+	unsigned int device_id;
+	int ret;
+
+	adis_reset(&st->adis);
+	msleep(70);
+
+	ret = adis_write_reg_16(&st->adis, ADIS16480_REG_GLOB_CMD, BIT(1));
+	if (ret)
+		return ret;
+	msleep(30);
+
+	ret = adis_check_status(&st->adis);
+	if (ret)
+		return ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16480_REG_PROD_ID, &prod_id);
+	if (ret)
+		return ret;
+
+	sscanf(indio_dev->name, "adis%u\n", &device_id);
+
+	if (prod_id != device_id)
+		dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
+				device_id, prod_id);
+
+	return 0;
+}
+
+#define ADIS16480_DIAG_STAT_XGYRO_FAIL 0
+#define ADIS16480_DIAG_STAT_YGYRO_FAIL 1
+#define ADIS16480_DIAG_STAT_ZGYRO_FAIL 2
+#define ADIS16480_DIAG_STAT_XACCL_FAIL 3
+#define ADIS16480_DIAG_STAT_YACCL_FAIL 4
+#define ADIS16480_DIAG_STAT_ZACCL_FAIL 5
+#define ADIS16480_DIAG_STAT_XMAGN_FAIL 8
+#define ADIS16480_DIAG_STAT_YMAGN_FAIL 9
+#define ADIS16480_DIAG_STAT_ZMAGN_FAIL 10
+#define ADIS16480_DIAG_STAT_BARO_FAIL 11
+
+static const char * const adis16480_status_error_msgs[] = {
+	[ADIS16480_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
+	[ADIS16480_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
+	[ADIS16480_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
+	[ADIS16480_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
+	[ADIS16480_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
+	[ADIS16480_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
+	[ADIS16480_DIAG_STAT_XMAGN_FAIL] = "X-axis magnetometer self-test failure",
+	[ADIS16480_DIAG_STAT_YMAGN_FAIL] = "Y-axis magnetometer self-test failure",
+	[ADIS16480_DIAG_STAT_ZMAGN_FAIL] = "Z-axis magnetometer self-test failure",
+	[ADIS16480_DIAG_STAT_BARO_FAIL] = "Barometer self-test failure",
+};
+
+static const struct adis_data adis16480_data = {
+	.diag_stat_reg = ADIS16480_REG_DIAG_STS,
+	.glob_cmd_reg = ADIS16480_REG_GLOB_CMD,
+	.has_paging = true,
+
+	.read_delay = 5,
+	.write_delay = 5,
+
+	.status_error_msgs = adis16480_status_error_msgs,
+	.status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_BARO_FAIL),
+
+	.enable_irq = adis16480_enable_irq,
+};
+
+static int __devinit adis16480_probe(struct spi_device *spi)
+{
+	const struct spi_device_id *id = spi_get_device_id(spi);
+	struct iio_dev *indio_dev;
+	struct adis16480 *st;
+	int ret;
+
+	indio_dev = iio_device_alloc(sizeof(*st));
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, indio_dev);
+
+	st = iio_priv(indio_dev);
+
+	st->chip_info = &adis16480_chip_info[id->driver_data];
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->channels = st->chip_info->channels;
+	indio_dev->num_channels = st->chip_info->num_channels;
+	indio_dev->info = &adis16480_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis_init(&st->adis, indio_dev, spi, &adis16480_data);
+	if (ret)
+		goto error_free_dev;
+
+	ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
+	if (ret)
+		goto error_free_dev;
+
+	ret = adis16480_initial_setup(indio_dev);
+	if (ret)
+		goto error_cleanup_buffer;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_stop_device;
+
+	adis16480_debugfs_init(indio_dev);
+
+	return 0;
+
+error_stop_device:
+	adis16480_stop_device(indio_dev);
+error_cleanup_buffer:
+	adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+error_free_dev:
+	iio_device_free(indio_dev);
+	return ret;
+}
+
+static int __devexit adis16480_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct adis16480 *st = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	adis16480_stop_device(indio_dev);
+
+	adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id adis16480_ids[] = {
+	{ "adis16375", ADIS16375 },
+	{ "adis16480", ADIS16480 },
+	{ "adis16485", ADIS16485 },
+	{ "adis16488", ADIS16488 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, adis16480_ids);
+
+static struct spi_driver adis16480_driver = {
+	.driver = {
+		.name = "adis16480",
+		.owner = THIS_MODULE,
+	},
+	.id_table = adis16480_ids,
+	.probe = adis16480_probe,
+	.remove = __devexit_p(adis16480_remove),
+};
+module_spi_driver(adis16480_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices ADIS16480 IMU driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h
index e82cd08..ff781dc 100644
--- a/include/linux/iio/imu/adis.h
+++ b/include/linux/iio/imu/adis.h
@@ -20,6 +20,8 @@
 #define ADIS_PAGE_SIZE 0x80
 #define ADIS_REG_PAGE_ID 0x00
 
+struct adis;
+
 /**
  * struct adis_data - ADIS chip variant specific data
  * @read_delay: SPI delay for read operations in us
@@ -44,6 +46,8 @@ struct adis_data {
 	const char * const *status_error_msgs;
 	unsigned int status_error_mask;
 
+	int (*enable_irq)(struct adis *adis, bool enable);
+
 	bool has_paging;
 };
 
-- 
1.8.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 1/7] iio:imu:adis: Add debugfs register access support
  2012-11-20 13:36 [PATCH 1/7] iio:imu:adis: Add debugfs register access support Lars-Peter Clausen
                   ` (5 preceding siblings ...)
  2012-11-20 13:36 ` [PATCH 7/7] iio:imu: Add support for the ADIS16480 and similar IMUs Lars-Peter Clausen
@ 2012-11-20 19:51 ` Jonathan Cameron
  6 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2012-11-20 19:51 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Jonathan Cameron, linux-iio, drivers

On 11/20/2012 01:36 PM, Lars-Peter Clausen wrote:
> Provide a IIO debugfs register access function for the ADIS library. This
> function can be used by induvidual drivers to allow raw register access via
> debugfs.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
added to togreg branch of iio.git
> ---
>  drivers/iio/imu/adis.c       | 23 +++++++++++++++++++++++
>  include/linux/iio/imu/adis.h | 11 +++++++++++
>  2 files changed, 34 insertions(+)
> 
> diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
> index 8259b77..28d4df2 100644
> --- a/drivers/iio/imu/adis.c
> +++ b/drivers/iio/imu/adis.c
> @@ -135,6 +135,29 @@ error_ret:
>  }
>  EXPORT_SYMBOL_GPL(adis_read_reg_16);
>  
> +#ifdef CONFIG_DEBUG_FS
> +
> +int adis_debugfs_reg_access(struct iio_dev *indio_dev,
> +	unsigned int reg, unsigned int writeval, unsigned int *readval)
> +{
> +	struct adis *adis = iio_device_get_drvdata(indio_dev);
> +
> +	if (readval) {
> +		uint16_t val16;
> +		int ret;
> +
> +		ret = adis_read_reg_16(adis, reg, &val16);
> +		*readval = val16;
> +
> +		return ret;
> +	} else {
> +		return adis_write_reg_16(adis, reg, writeval);
> +	}
> +}
> +EXPORT_SYMBOL(adis_debugfs_reg_access);
> +
> +#endif
> +
>  /**
>   * adis_enable_irq() - Enable or disable data ready IRQ
>   * @adis: The adis device
> diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h
> index 8c3304d..fce7bc3 100644
> --- a/include/linux/iio/imu/adis.h
> +++ b/include/linux/iio/imu/adis.h
> @@ -183,4 +183,15 @@ static inline void adis_remove_trigger(struct adis *adis)
>  
>  #endif /* CONFIG_IIO_BUFFER */
>  
> +#ifdef CONFIG_DEBUG_FS
> +
> +int adis_debugfs_reg_access(struct iio_dev *indio_dev,
> +	unsigned int reg, unsigned int writeval, unsigned int *readval);
> +
> +#else
> +
> +#define adis_debugfs_reg_access NULL
> +
> +#endif
> +
>  #endif
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/7] iio:imu:adis: Add support for 32bit registers
  2012-11-20 13:36 ` [PATCH 2/7] iio:imu:adis: Add support for 32bit registers Lars-Peter Clausen
@ 2012-11-20 20:55   ` Jonathan Cameron
  0 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2012-11-20 20:55 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Jonathan Cameron, linux-iio, drivers

On 11/20/2012 01:36 PM, Lars-Peter Clausen wrote:
> Some of the newer generation devices from the ADIS16XXX family have 32bit wide
> register which spans two 16bit wide registers. This patch adds support for
> reading and writing a 32bit wide register.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
added to togreg branch of iio.git
> ---
>  drivers/iio/imu/adis.c        | 145 +++++++++++++++++++++++++++---------------
>  drivers/iio/imu/adis_buffer.c |   2 +
>  include/linux/iio/imu/adis.h  |  81 +++++++++++++++++++++--
>  3 files changed, 171 insertions(+), 57 deletions(-)
> 
> diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
> index 28d4df2..280a495 100644
> --- a/drivers/iio/imu/adis.c
> +++ b/drivers/iio/imu/adis.c
> @@ -27,36 +27,10 @@
>  #define ADIS_MSC_CTRL_DATA_RDY_DIO2	BIT(0)
>  #define ADIS_GLOB_CMD_SW_RESET		BIT(7)
>  
> -/**
> - * adis_write_reg_8() - Write single byte to a register
> - * @adis: The adis device
> - * @reg: The address of the register to be written
> - * @val: The value to write
> - */
> -int adis_write_reg_8(struct adis *adis, unsigned int reg, uint8_t val)
> -{
> -	int ret;
> -
> -	mutex_lock(&adis->txrx_lock);
> -	adis->tx[0] = ADIS_WRITE_REG(reg);
> -	adis->tx[1] = val;
> -
> -	ret = spi_write(adis->spi, adis->tx, 2);
> -	mutex_unlock(&adis->txrx_lock);
> -
> -	return ret;
> -}
> -EXPORT_SYMBOL_GPL(adis_write_reg_8);
> -
> -/**
> - * adis_write_reg_16() - Write 2 bytes to a pair of registers
> - * @adis: The adis device
> - * @reg: The address of the lower of the two registers
> - * @val: Value to be written
> - */
> -int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t value)
> +int adis_write_reg(struct adis *adis, unsigned int reg,
> +	unsigned int value, unsigned int size)
>  {
> -	int ret;
> +	int ret, i;
>  	struct spi_message msg;
>  	struct spi_transfer xfers[] = {
>  		{
> @@ -69,33 +43,69 @@ int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t value)
>  			.tx_buf = adis->tx + 2,
>  			.bits_per_word = 8,
>  			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = adis->data->write_delay,
> +		}, {
> +			.tx_buf = adis->tx + 4,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = adis->data->write_delay,
> +		}, {
> +			.tx_buf = adis->tx + 6,
> +			.bits_per_word = 8,
> +			.len = 2,
>  			.delay_usecs = adis->data->write_delay,
>  		},
>  	};
>  
>  	mutex_lock(&adis->txrx_lock);
> -	adis->tx[0] = ADIS_WRITE_REG(reg);
> -	adis->tx[1] = value & 0xff;
> -	adis->tx[2] = ADIS_WRITE_REG(reg + 1);
> -	adis->tx[3] = (value >> 8) & 0xff;
>  
>  	spi_message_init(&msg);
> -	spi_message_add_tail(&xfers[0], &msg);
> -	spi_message_add_tail(&xfers[1], &msg);
> +	switch (size) {
> +	case 4:
> +		adis->tx[6] = ADIS_WRITE_REG(reg + 3);
> +		adis->tx[7] = (value >> 24) & 0xff;
> +		adis->tx[4] = ADIS_WRITE_REG(reg + 2);
> +		adis->tx[5] = (value >> 16) & 0xff;
> +	case 2:
> +		adis->tx[2] = ADIS_WRITE_REG(reg + 1);
> +		adis->tx[3] = (value >> 8) & 0xff;
> +	case 1:
> +		adis->tx[0] = ADIS_WRITE_REG(reg);
> +		adis->tx[1] = value & 0xff;
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		goto out_unlock;
> +	}
> +
> +	xfers[size - 1].cs_change = 0;
> +
> +	for (i = 0; i < size; i++)
> +		spi_message_add_tail(&xfers[i], &msg);
> +
>  	ret = spi_sync(adis->spi, &msg);
> +	if (ret) {
> +		dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n",
> +				reg, ret);
> +	}
> +
> +out_unlock:
>  	mutex_unlock(&adis->txrx_lock);
>  
>  	return ret;
>  }
> -EXPORT_SYMBOL_GPL(adis_write_reg_16);
> +EXPORT_SYMBOL_GPL(adis_write_reg);
>  
>  /**
> - * adis_read_reg_16() - read 2 bytes from a 16-bit register
> + * adis_read_reg() - read 2 bytes from a 16-bit register
>   * @adis: The adis device
>   * @reg: The address of the lower of the two registers
>   * @val: The value read back from the device
>   */
> -int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val)
> +int adis_read_reg(struct adis *adis, unsigned int reg,
> +	unsigned int *val, unsigned int size)
>  {
>  	struct spi_message msg;
>  	int ret;
> @@ -107,33 +117,61 @@ int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val)
>  			.cs_change = 1,
>  			.delay_usecs = adis->data->read_delay,
>  		}, {
> +			.tx_buf = adis->tx + 2,
>  			.rx_buf = adis->rx,
>  			.bits_per_word = 8,
>  			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = adis->data->read_delay,
> +		}, {
> +			.rx_buf = adis->rx + 2,
> +			.bits_per_word = 8,
> +			.len = 2,
>  			.delay_usecs = adis->data->read_delay,
>  		},
>  	};
>  
>  	mutex_lock(&adis->txrx_lock);
> -	adis->tx[0] = ADIS_READ_REG(reg);
> -	adis->tx[1] = 0;
> -
>  	spi_message_init(&msg);
> -	spi_message_add_tail(&xfers[0], &msg);
> -	spi_message_add_tail(&xfers[1], &msg);
> +
> +	switch (size) {
> +	case 4:
> +		adis->tx[0] = ADIS_READ_REG(reg + 2);
> +		adis->tx[1] = 0;
> +		spi_message_add_tail(&xfers[0], &msg);
> +	case 2:
> +		adis->tx[2] = ADIS_READ_REG(reg);
> +		adis->tx[3] = 0;
> +		spi_message_add_tail(&xfers[1], &msg);
> +		spi_message_add_tail(&xfers[2], &msg);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		goto out_unlock;
> +	}
> +
>  	ret = spi_sync(adis->spi, &msg);
>  	if (ret) {
> -		dev_err(&adis->spi->dev, "Failed to read 16 bit register 0x%02X: %d\n",
> +		dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
>  				reg, ret);
> -		goto error_ret;
> +		goto out_unlock;
>  	}
> -	*val = get_unaligned_be16(adis->rx);
>  
> -error_ret:
> +	switch (size) {
> +	case 4:
> +		*val = get_unaligned_be32(adis->rx);
> +		break;
> +	case 2:
> +		*val = get_unaligned_be16(adis->rx + 2);
> +		break;
> +	}
> +
> +out_unlock:
>  	mutex_unlock(&adis->txrx_lock);
> +
>  	return ret;
>  }
> -EXPORT_SYMBOL_GPL(adis_read_reg_16);
> +EXPORT_SYMBOL_GPL(adis_read_reg);
>  
>  #ifdef CONFIG_DEBUG_FS
>  
> @@ -304,25 +342,26 @@ int adis_single_conversion(struct iio_dev *indio_dev,
>  	const struct iio_chan_spec *chan, unsigned int error_mask, int *val)
>  {
>  	struct adis *adis = iio_device_get_drvdata(indio_dev);
> -	uint16_t val16;
> +	unsigned int uval;
>  	int ret;
>  
>  	mutex_lock(&indio_dev->mlock);
>  
> -	ret = adis_read_reg_16(adis, chan->address, &val16);
> +	ret = adis_read_reg(adis, chan->address, &uval,
> +			chan->scan_type.storagebits / 8);
>  	if (ret)
>  		goto err_unlock;
>  
> -	if (val16 & error_mask) {
> +	if (uval & error_mask) {
>  		ret = adis_check_status(adis);
>  		if (ret)
>  			goto err_unlock;
>  	}
>  
>  	if (chan->scan_type.sign == 's')
> -		*val = sign_extend32(val16, chan->scan_type.realbits - 1);
> +		*val = sign_extend32(uval, chan->scan_type.realbits - 1);
>  	else
> -		*val = val16 & ((1 << chan->scan_type.realbits) - 1);
> +		*val = uval & ((1 << chan->scan_type.realbits) - 1);
>  
>  	ret = IIO_VAL_INT;
>  err_unlock:
> diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c
> index a91b4cb..7857133 100644
> --- a/drivers/iio/imu/adis_buffer.c
> +++ b/drivers/iio/imu/adis_buffer.c
> @@ -64,6 +64,8 @@ int adis_update_scan_mode(struct iio_dev *indio_dev,
>  	for (i = 0; i < indio_dev->num_channels; i++, chan++) {
>  		if (!test_bit(chan->scan_index, scan_mask))
>  			continue;
> +		if (chan->scan_type.storagebits == 32)
> +			*tx++ = cpu_to_be16((chan->address + 2) << 8);
>  		*tx++ = cpu_to_be16(chan->address << 8);
>  	}
>  
> diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h
> index fce7bc3..6402a08 100644
> --- a/include/linux/iio/imu/adis.h
> +++ b/include/linux/iio/imu/adis.h
> @@ -53,7 +53,7 @@ struct adis {
>  	struct spi_transfer	*xfer;
>  	void			*buffer;
>  
> -	uint8_t			tx[8] ____cacheline_aligned;
> +	uint8_t			tx[10] ____cacheline_aligned;
>  	uint8_t			rx[4];
>  };
>  
> @@ -61,9 +61,82 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev,
>  	struct spi_device *spi, const struct adis_data *data);
>  int adis_reset(struct adis *adis);
>  
> -int adis_write_reg_8(struct adis *adis, unsigned int reg, uint8_t val);
> -int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t val);
> -int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val);
> +int adis_write_reg(struct adis *adis, unsigned int reg,
> +	unsigned int val, unsigned int size);
> +int adis_read_reg(struct adis *adis, unsigned int reg,
> +	unsigned int *val, unsigned int size);
> +
> +/**
> + * adis_write_reg_8() - Write single byte to a register
> + * @adis: The adis device
> + * @reg: The address of the register to be written
> + * @value: The value to write
> + */
> +static inline int adis_write_reg_8(struct adis *adis, unsigned int reg,
> +	uint8_t val)
> +{
> +	return adis_write_reg(adis, reg, val, 1);
> +}
> +
> +/**
> + * adis_write_reg_16() - Write 2 bytes to a pair of registers
> + * @adis: The adis device
> + * @reg: The address of the lower of the two registers
> + * @value: Value to be written
> + */
> +static inline int adis_write_reg_16(struct adis *adis, unsigned int reg,
> +	uint16_t val)
> +{
> +	return adis_write_reg(adis, reg, val, 2);
> +}
> +
> +/**
> + * adis_write_reg_32() - write 4 bytes to four registers
> + * @adis: The adis device
> + * @reg: The address of the lower of the four register
> + * @value: Value to be written
> + */
> +static inline int adis_write_reg_32(struct adis *adis, unsigned int reg,
> +	uint32_t val)
> +{
> +	return adis_write_reg(adis, reg, val, 4);
> +}
> +
> +/**
> + * adis_read_reg_16() - read 2 bytes from a 16-bit register
> + * @adis: The adis device
> + * @reg: The address of the lower of the two registers
> + * @val: The value read back from the device
> + */
> +static inline int adis_read_reg_16(struct adis *adis, unsigned int reg,
> +	uint16_t *val)
> +{
> +	unsigned int tmp;
> +	int ret;
> +
> +	ret = adis_read_reg(adis, reg, &tmp, 2);
> +	*val = tmp;
> +
> +	return ret;
> +}
> +
> +/**
> + * adis_read_reg_32() - read 4 bytes from a 32-bit register
> + * @adis: The adis device
> + * @reg: The address of the lower of the two registers
> + * @val: The value read back from the device
> + */
> +static inline int adis_read_reg_32(struct adis *adis, unsigned int reg,
> +	uint32_t *val)
> +{
> +	unsigned int tmp;
> +	int ret;
> +
> +	ret = adis_read_reg(adis, reg, &tmp, 4);
> +	*val = tmp;
> +
> +	return ret;
> +}
>  
>  int adis_enable_irq(struct adis *adis, bool enable);
>  int adis_check_status(struct adis *adis);
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/7] iio:gyro: Add support for the ADIS16136 gyroscope
  2012-11-20 13:36 ` [PATCH 3/7] iio:gyro: Add support for the ADIS16136 gyroscope Lars-Peter Clausen
@ 2012-11-20 21:05   ` Jonathan Cameron
  2012-11-20 21:15     ` Lars-Peter Clausen
  0 siblings, 1 reply; 16+ messages in thread
From: Jonathan Cameron @ 2012-11-20 21:05 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Jonathan Cameron, linux-iio, drivers

On 11/20/2012 01:36 PM, Lars-Peter Clausen wrote:
> This patch adds support for the ADIS16133, ADIS16135, ADIS16136 single channel
> gyroscopes. The main difference between them is the sensor precision.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
A nice driver.

The only thing I could pick up on was the 'interesting' comments alongside
the 3db divisors.

Still I just wanted to comment on something really ;)

added to togreg branch of iio.git

> ---
>  drivers/iio/gyro/Kconfig     |   9 +
>  drivers/iio/gyro/Makefile    |   1 +
>  drivers/iio/gyro/adis16136.c | 581 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 591 insertions(+)
>  create mode 100644 drivers/iio/gyro/adis16136.c
> 
> diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
> index 21e27e2..48ed148 100644
> --- a/drivers/iio/gyro/Kconfig
> +++ b/drivers/iio/gyro/Kconfig
> @@ -3,6 +3,15 @@
>  #
>  menu "Digital gyroscope sensors"
>  
> +config ADIS16136
> +	tristate "Analog devices ADIS16136 and similar gyroscopes driver"
> +	depends on SPI_MASTER
> +	select IIO_ADIS_LIB
> +	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
> +	help
> +	  Say yes here to build support for the Analog Devices ADIS16133, ADIS16135,
> +	  ADIS16136 gyroscope devices.
> +
>  config HID_SENSOR_GYRO_3D
>  	depends on HID_SENSOR_HUB
>  	select IIO_BUFFER
> diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
> index 8a895d9..702a058 100644
> --- a/drivers/iio/gyro/Makefile
> +++ b/drivers/iio/gyro/Makefile
> @@ -2,4 +2,5 @@
>  # Makefile for industrial I/O gyroscope sensor drivers
>  #
>  
> +obj-$(CONFIG_ADIS16136) += adis16136.o
>  obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
> diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c
> new file mode 100644
> index 0000000..277aa74
> --- /dev/null
> +++ b/drivers/iio/gyro/adis16136.c
> @@ -0,0 +1,581 @@
> +/*
> + * ADIS16133/ADIS16135/ADIS16136 gyroscope driver
> + *
> + * Copyright 2012 Analog Devices Inc.
> + *   Author: Lars-Peter Clausen <lars@metafoo.de>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/spi/spi.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/module.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/imu/adis.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/debugfs.h>
> +
> +#define ADIS16136_REG_FLASH_CNT		0x00
> +#define ADIS16136_REG_TEMP_OUT		0x02
> +#define ADIS16136_REG_GYRO_OUT2		0x04
> +#define ADIS16136_REG_GYRO_OUT		0x06
> +#define ADIS16136_REG_GYRO_OFF2		0x08
> +#define ADIS16136_REG_GYRO_OFF		0x0A
> +#define ADIS16136_REG_ALM_MAG1		0x10
> +#define ADIS16136_REG_ALM_MAG2		0x12
> +#define ADIS16136_REG_ALM_SAMPL1	0x14
> +#define ADIS16136_REG_ALM_SAMPL2	0x16
> +#define ADIS16136_REG_ALM_CTRL		0x18
> +#define ADIS16136_REG_GPIO_CTRL		0x1A
> +#define ADIS16136_REG_MSC_CTRL		0x1C
> +#define ADIS16136_REG_SMPL_PRD		0x1E
> +#define ADIS16136_REG_AVG_CNT		0x20
> +#define ADIS16136_REG_DEC_RATE		0x22
> +#define ADIS16136_REG_SLP_CTRL		0x24
> +#define ADIS16136_REG_DIAG_STAT		0x26
> +#define ADIS16136_REG_GLOB_CMD		0x28
> +#define ADIS16136_REG_LOT1		0x32
> +#define ADIS16136_REG_LOT2		0x34
> +#define ADIS16136_REG_LOT3		0x36
> +#define ADIS16136_REG_PROD_ID		0x38
> +#define ADIS16136_REG_SERIAL_NUM	0x3A
> +
> +#define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL	2
> +#define ADIS16136_DIAG_STAT_SPI_FAIL		3
> +#define ADIS16136_DIAG_STAT_SELF_TEST_FAIL	5
> +#define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL	6
> +
> +#define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11)
> +#define ADIS16136_MSC_CTRL_SELF_TEST BIT(10)
> +
> +struct adis16136_chip_info {
> +	unsigned int precision;
> +	unsigned int fullscale;
> +};
> +
> +struct adis16136 {
> +	const struct adis16136_chip_info *chip_info;
> +
> +	struct adis adis;
> +};
> +
> +#ifdef CONFIG_DEBUG_FS
> +
> +static ssize_t adis16136_show_serial(struct file *file,
> +		char __user *userbuf, size_t count, loff_t *ppos)
> +{
> +	struct adis16136 *adis16136 = file->private_data;
> +	uint16_t lot1, lot2, lot3, serial;
> +	char buf[20];
> +	size_t len;
> +	int ret;
> +
> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SERIAL_NUM,
> +		&serial);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3);
> +	if (ret < 0)
> +		return ret;
> +
> +	len = snprintf(buf, sizeof(buf), "%.4x%.4x%.4x-%.4x\n", lot1, lot2,
> +		lot3, serial);
> +
> +	return simple_read_from_buffer(userbuf, count, ppos, buf, len);
> +}
> +
> +static const struct file_operations adis16136_serial_fops = {
> +	.open = simple_open,
> +	.read = adis16136_show_serial,
> +	.llseek = default_llseek,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int adis16136_show_product_id(void *arg, u64 *val)
> +{
> +	struct adis16136 *adis16136 = arg;
> +	u16 prod_id;
> +	int ret;
> +
> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
> +		&prod_id);
> +	if (ret < 0)
> +		return ret;
> +
> +	*val = prod_id;
> +
> +	return 0;
> +}
> +DEFINE_SIMPLE_ATTRIBUTE(adis16136_product_id_fops,
> +	adis16136_show_product_id, NULL, "%llu\n");
> +
> +static int adis16136_show_flash_count(void *arg, u64 *val)
> +{
> +	struct adis16136 *adis16136 = arg;
> +	uint16_t flash_count;
> +	int ret;
> +
> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_FLASH_CNT,
> +		&flash_count);
> +	if (ret < 0)
> +		return ret;
> +
> +	*val = flash_count;
> +
> +	return 0;
> +}
> +DEFINE_SIMPLE_ATTRIBUTE(adis16136_flash_count_fops,
> +	adis16136_show_flash_count, NULL, "%lld\n");
> +
> +static int adis16136_debugfs_init(struct iio_dev *indio_dev)
> +{
> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
> +
> +	debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
> +		adis16136, &adis16136_serial_fops);
> +	debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
> +		adis16136, &adis16136_product_id_fops);
> +	debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
> +		adis16136, &adis16136_flash_count_fops);
> +
> +	return 0;
> +}
> +
> +#else
> +
> +static int adis16136_debugfs_init(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +
> +#endif
> +
> +static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq)
> +{
> +	unsigned int t;
> +
> +	t = 32768 / freq;
> +	if (t < 0xf)
> +		t = 0xf;
> +	else if (t > 0xffff)
> +		t = 0xffff;
> +	else
> +		t--;
> +
> +	return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t);
> +}
> +
> +static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq)
> +{
> +	uint16_t t;
> +	int ret;
> +
> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t);
> +	if (ret < 0)
> +		return ret;
> +
> +	*freq = 32768 / (t + 1);
> +
> +	return 0;
> +}
> +
> +static ssize_t adis16136_write_frequency(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
> +	long val;
> +	int ret;
> +
> +	ret = kstrtol(buf, 10, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val == 0)
> +		return -EINVAL;
> +
> +	ret = adis16136_set_freq(adis16136, val);
> +
> +	return ret ? ret : len;
> +}
> +
> +static ssize_t adis16136_read_frequency(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
> +	unsigned int freq;
> +	int ret;
> +
> +	ret = adis16136_get_freq(adis16136, &freq);
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d\n", freq);
> +}
> +
> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
> +				  adis16136_read_frequency,
> +				  adis16136_write_frequency);
> +
I love the comments.  Useful sounding but I have no idea what
they actually mean!
> +static const unsigned adis16136_3db_divisors[] = {
> +	[0] = 2, /* Special case */
> +	[1] = 6,
> +	[2] = 12,
> +	[3] = 25,
> +	[4] = 50,
> +	[5] = 100,
> +	[6] = 200,
> +	[7] = 200, /* Not a valid setting */
> +};
> +
> +static int adis16136_set_filter(struct iio_dev *indio_dev, int val)
> +{
> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
> +	unsigned int freq;
> +	int i, ret;
> +
> +	ret = adis16136_get_freq(adis16136, &freq);
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) {
> +		if (freq / adis16136_3db_divisors[i] >= val)
> +			break;
> +	}
> +
> +	return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i);
> +}
> +
> +static int adis16136_get_filter(struct iio_dev *indio_dev, int *val)
> +{
> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
> +	unsigned int freq;
> +	uint16_t val16;
> +	int ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16);
> +	if (ret < 0)
> +		goto err_unlock;
> +
> +	ret = adis16136_get_freq(adis16136, &freq);
> +	if (ret < 0)
> +		goto err_unlock;
> +
> +	*val = freq / adis16136_3db_divisors[val16 & 0x07];
> +
> +err_unlock:
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret ? ret : IIO_VAL_INT;
> +}
> +
> +static int adis16136_read_raw(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan, int *val, int *val2, long info)
> +{
> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
> +	uint32_t val32;
> +	int ret;
> +
> +	switch (info) {
> +	case IIO_CHAN_INFO_RAW:
> +		return adis_single_conversion(indio_dev, chan, 0, val);
> +	case IIO_CHAN_INFO_SCALE:
> +		switch (chan->type) {
> +		case IIO_ANGL_VEL:
> +			*val = adis16136->chip_info->precision;
> +			*val2 = (adis16136->chip_info->fullscale << 16);
> +			return IIO_VAL_FRACTIONAL;
> +		case IIO_TEMP:
> +			*val = 10;
> +			*val2 = 697000; /* 0.010697 degree Celsius */
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_CALIBBIAS:
> +		ret = adis_read_reg_32(&adis16136->adis,
> +			ADIS16136_REG_GYRO_OFF2, &val32);
> +		if (ret < 0)
> +			return ret;
> +
> +		*val = sign_extend32(val32, 31);
> +
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
> +		return adis16136_get_filter(indio_dev, val);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int adis16136_write_raw(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan, int val, int val2, long info)
> +{
> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
> +
> +	switch (info) {
> +	case IIO_CHAN_INFO_CALIBBIAS:
> +		return adis_write_reg_32(&adis16136->adis,
> +			ADIS16136_REG_GYRO_OFF2, val);
> +	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
> +		return adis16136_set_filter(indio_dev, val);
> +	default:
> +		break;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +enum {
> +	ADIS16136_SCAN_GYRO,
> +	ADIS16136_SCAN_TEMP,
> +};
> +
> +static const struct iio_chan_spec adis16136_channels[] = {
> +	{
> +		.type = IIO_ANGL_VEL,
> +		.modified = 1,
> +		.channel2 = IIO_MOD_X,
> +		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
> +			IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +			IIO_CHAN_INFO_SCALE_SHARED_BIT |
> +			IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT,
> +		.address = ADIS16136_REG_GYRO_OUT2,
> +		.scan_index = ADIS16136_SCAN_GYRO,
> +		.scan_type = {
> +			.sign = 's',
> +			.realbits = 32,
> +			.storagebits = 32,
> +			.endianness = IIO_BE,
> +		},
> +	}, {
> +		.type = IIO_TEMP,
> +		.indexed = 1,
> +		.channel = 0,
> +		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
> +			IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +		.address = ADIS16136_REG_TEMP_OUT,
> +		.scan_index = ADIS16136_SCAN_TEMP,
> +		.scan_type = {
> +			.sign = 's',
> +			.realbits = 16,
> +			.storagebits = 16,
> +			.endianness = IIO_BE,
> +		},
> +	},
> +	IIO_CHAN_SOFT_TIMESTAMP(2),
> +};
> +
> +static struct attribute *adis16136_attributes[] = {
> +	&iio_dev_attr_sampling_frequency.dev_attr.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group adis16136_attribute_group = {
> +	.attrs = adis16136_attributes,
> +};
> +
> +static const struct iio_info adis16136_info = {
> +	.driver_module = THIS_MODULE,
> +	.attrs = &adis16136_attribute_group,
> +	.read_raw = &adis16136_read_raw,
> +	.write_raw = &adis16136_write_raw,
> +	.update_scan_mode = adis_update_scan_mode,
> +	.debugfs_reg_access = adis_debugfs_reg_access,
> +};
> +
> +static int adis16136_stop_device(struct iio_dev *indio_dev)
> +{
> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
> +	int ret;
> +
> +	ret = adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SLP_CTRL, 0xff);
> +	if (ret)
> +		dev_err(&indio_dev->dev,
> +			"Could not power down device: %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int adis16136_initial_setup(struct iio_dev *indio_dev)
> +{
> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
> +	unsigned int device_id;
> +	uint16_t prod_id;
> +	int ret;
> +
> +	ret = adis_initial_startup(&adis16136->adis);
> +	if (ret)
> +		return ret;
> +
> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
> +		&prod_id);
> +	if (ret)
> +		return ret;
> +
> +	sscanf(indio_dev->name, "adis%u\n", &device_id);
> +
> +	if (prod_id != device_id)
> +		dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
> +				device_id, prod_id);
> +
> +	return 0;
> +}
> +
> +static const char * const adis16136_status_error_msgs[] = {
> +	[ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL] = "Flash update failed",
> +	[ADIS16136_DIAG_STAT_SPI_FAIL] = "SPI failure",
> +	[ADIS16136_DIAG_STAT_SELF_TEST_FAIL] = "Self test error",
> +	[ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error",
> +};
> +
> +static const struct adis_data adis16136_data = {
> +	.diag_stat_reg = ADIS16136_REG_DIAG_STAT,
> +	.glob_cmd_reg = ADIS16136_REG_GLOB_CMD,
> +	.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL,
> +
> +	.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST,
> +	.startup_delay = 80,
> +
> +	.read_delay = 10,
> +	.write_delay = 10,
> +
> +	.status_error_msgs = adis16136_status_error_msgs,
> +	.status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) |
> +		BIT(ADIS16136_DIAG_STAT_SPI_FAIL) |
> +		BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) |
> +		BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL),
> +};
> +
> +enum adis16136_id {
> +	ID_ADIS16133,
> +	ID_ADIS16135,
> +	ID_ADIS16136,
> +};
> +
> +static const struct adis16136_chip_info adis16136_chip_info[] = {
> +	[ID_ADIS16133] = {
> +		.precision = IIO_DEGREE_TO_RAD(1200),
> +		.fullscale = 24000,
> +	},
> +	[ID_ADIS16135] = {
> +		.precision = IIO_DEGREE_TO_RAD(300),
> +		.fullscale = 24000,
> +	},
> +	[ID_ADIS16136] = {
> +		.precision = IIO_DEGREE_TO_RAD(450),
> +		.fullscale = 24623,
> +	},
> +};
> +
> +static int __devinit adis16136_probe(struct spi_device *spi)
> +{
> +	const struct spi_device_id *id = spi_get_device_id(spi);
> +	struct adis16136 *adis16136;
> +	struct iio_dev *indio_dev;
> +	int ret;
> +
> +	indio_dev = iio_device_alloc(sizeof(*adis16136));
> +	if (indio_dev == NULL)
> +		return -ENOMEM;
> +
> +	spi_set_drvdata(spi, indio_dev);
> +
> +	adis16136 = iio_priv(indio_dev);
> +
> +	adis16136->chip_info = &adis16136_chip_info[id->driver_data];
> +	indio_dev->dev.parent = &spi->dev;
> +	indio_dev->name = spi_get_device_id(spi)->name;
> +	indio_dev->channels = adis16136_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(adis16136_channels);
> +	indio_dev->info = &adis16136_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = adis_init(&adis16136->adis, indio_dev, spi, &adis16136_data);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	ret = adis_setup_buffer_and_trigger(&adis16136->adis, indio_dev, NULL);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	ret = adis16136_initial_setup(indio_dev);
> +	if (ret)
> +		goto error_cleanup_buffer;
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret)
> +		goto error_stop_device;
> +
> +	adis16136_debugfs_init(indio_dev);
> +
> +	return 0;
> +
> +error_stop_device:
> +	adis16136_stop_device(indio_dev);
> +error_cleanup_buffer:
> +	adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
> +error_free_dev:
> +	iio_device_free(indio_dev);
> +	return ret;
> +}
> +
> +static int __devexit adis16136_remove(struct spi_device *spi)
> +{
> +	struct iio_dev *indio_dev = spi_get_drvdata(spi);
> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
> +
> +	iio_device_unregister(indio_dev);
> +	adis16136_stop_device(indio_dev);
> +
> +	adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
> +
> +	iio_device_free(indio_dev);
> +
> +	return 0;
> +}
> +
> +static const struct spi_device_id adis16136_ids[] = {
> +	{ "adis16133", ID_ADIS16133 },
> +	{ "adis16135", ID_ADIS16135 },
> +	{ "adis16136", ID_ADIS16136 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(spi, adis16136_ids);
> +
> +static struct spi_driver adis16136_driver = {
> +	.driver = {
> +		.name = "adis16136",
> +		.owner = THIS_MODULE,
> +	},
> +	.id_table = adis16136_ids,
> +	.probe = adis16136_probe,
> +	.remove = __devexit_p(adis16136_remove),
> +};
> +module_spi_driver(adis16136_driver);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver");
> +MODULE_LICENSE("GPL v2");
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/7] iio:imu:adis: Add paging support
  2012-11-20 13:36 ` [PATCH 4/7] iio:imu:adis: Add paging support Lars-Peter Clausen
@ 2012-11-20 21:10   ` Jonathan Cameron
  0 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2012-11-20 21:10 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Jonathan Cameron, linux-iio, drivers

On 11/20/2012 01:36 PM, Lars-Peter Clausen wrote:
> Some of the newer generation devices from the ADIS16XXX series have more
> registers than what can be supported with the current register addressing
> scheme. These devices implement register paging to support a larger register
> range. Each page is 128 registers large and the currently active page can be
> selected via register 0x00 in each page. This patch implements transparent
> paging inside the common adis library. The register read/write interface stays
> the same and when a register is accessed the library automatically switches to
> the correct page if it is not already selected. The page number is encoded in
> the upper bits of the register number, e.g. register 0x5 of page 1 is 0x85.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
added to togreg branch of iio.git

> ---
>  drivers/iio/imu/adis.c        | 70 +++++++++++++++++++++++++++++++++----------
>  drivers/iio/imu/adis_buffer.c | 15 ++++++++++
>  include/linux/iio/imu/adis.h  | 10 +++++--
>  3 files changed, 77 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
> index 280a495..c4ea04f 100644
> --- a/drivers/iio/imu/adis.c
> +++ b/drivers/iio/imu/adis.c
> @@ -30,6 +30,7 @@
>  int adis_write_reg(struct adis *adis, unsigned int reg,
>  	unsigned int value, unsigned int size)
>  {
> +	unsigned int page = reg / ADIS_PAGE_SIZE;
>  	int ret, i;
>  	struct spi_message msg;
>  	struct spi_transfer xfers[] = {
> @@ -56,39 +57,53 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
>  			.bits_per_word = 8,
>  			.len = 2,
>  			.delay_usecs = adis->data->write_delay,
> +		}, {
> +			.tx_buf = adis->tx + 8,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.delay_usecs = adis->data->write_delay,
>  		},
>  	};
>  
>  	mutex_lock(&adis->txrx_lock);
>  
>  	spi_message_init(&msg);
> +
> +	if (adis->current_page != page) {
> +		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
> +		adis->tx[1] = page;
> +		spi_message_add_tail(&xfers[0], &msg);
> +	}
> +
>  	switch (size) {
>  	case 4:
> -		adis->tx[6] = ADIS_WRITE_REG(reg + 3);
> -		adis->tx[7] = (value >> 24) & 0xff;
> -		adis->tx[4] = ADIS_WRITE_REG(reg + 2);
> -		adis->tx[5] = (value >> 16) & 0xff;
> +		adis->tx[8] = ADIS_WRITE_REG(reg + 3);
> +		adis->tx[9] = (value >> 24) & 0xff;
> +		adis->tx[6] = ADIS_WRITE_REG(reg + 2);
> +		adis->tx[7] = (value >> 16) & 0xff;
>  	case 2:
> -		adis->tx[2] = ADIS_WRITE_REG(reg + 1);
> -		adis->tx[3] = (value >> 8) & 0xff;
> +		adis->tx[4] = ADIS_WRITE_REG(reg + 1);
> +		adis->tx[5] = (value >> 8) & 0xff;
>  	case 1:
> -		adis->tx[0] = ADIS_WRITE_REG(reg);
> -		adis->tx[1] = value & 0xff;
> +		adis->tx[2] = ADIS_WRITE_REG(reg);
> +		adis->tx[3] = value & 0xff;
>  		break;
>  	default:
>  		ret = -EINVAL;
>  		goto out_unlock;
>  	}
>  
> -	xfers[size - 1].cs_change = 0;
> +	xfers[size].cs_change = 0;
>  
> -	for (i = 0; i < size; i++)
> +	for (i = 1; i <= size; i++)
>  		spi_message_add_tail(&xfers[i], &msg);
>  
>  	ret = spi_sync(adis->spi, &msg);
>  	if (ret) {
>  		dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n",
>  				reg, ret);
> +	} else {
> +		adis->current_page = page;
>  	}
>  
>  out_unlock:
> @@ -107,6 +122,7 @@ EXPORT_SYMBOL_GPL(adis_write_reg);
>  int adis_read_reg(struct adis *adis, unsigned int reg,
>  	unsigned int *val, unsigned int size)
>  {
> +	unsigned int page = reg / ADIS_PAGE_SIZE;
>  	struct spi_message msg;
>  	int ret;
>  	struct spi_transfer xfers[] = {
> @@ -115,9 +131,15 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
>  			.bits_per_word = 8,
>  			.len = 2,
>  			.cs_change = 1,
> -			.delay_usecs = adis->data->read_delay,
> +			.delay_usecs = adis->data->write_delay,
>  		}, {
>  			.tx_buf = adis->tx + 2,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = adis->data->read_delay,
> +		}, {
> +			.tx_buf = adis->tx + 4,
>  			.rx_buf = adis->rx,
>  			.bits_per_word = 8,
>  			.len = 2,
> @@ -134,16 +156,22 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
>  	mutex_lock(&adis->txrx_lock);
>  	spi_message_init(&msg);
>  
> +	if (adis->current_page != page) {
> +		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
> +		adis->tx[1] = page;
> +		spi_message_add_tail(&xfers[0], &msg);
> +	}
> +
>  	switch (size) {
>  	case 4:
> -		adis->tx[0] = ADIS_READ_REG(reg + 2);
> -		adis->tx[1] = 0;
> -		spi_message_add_tail(&xfers[0], &msg);
> -	case 2:
> -		adis->tx[2] = ADIS_READ_REG(reg);
> +		adis->tx[2] = ADIS_READ_REG(reg + 2);
>  		adis->tx[3] = 0;
>  		spi_message_add_tail(&xfers[1], &msg);
> +	case 2:
> +		adis->tx[4] = ADIS_READ_REG(reg);
> +		adis->tx[5] = 0;
>  		spi_message_add_tail(&xfers[2], &msg);
> +		spi_message_add_tail(&xfers[3], &msg);
>  		break;
>  	default:
>  		ret = -EINVAL;
> @@ -155,6 +183,8 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
>  		dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
>  				reg, ret);
>  		goto out_unlock;
> +	} else {
> +		adis->current_page = page;
>  	}
>  
>  	switch (size) {
> @@ -390,6 +420,14 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev,
>  	adis->data = data;
>  	iio_device_set_drvdata(indio_dev, adis);
>  
> +	if (data->has_paging) {
> +		/* Need to set the page before first read/write */
> +		adis->current_page = -1;
> +	} else {
> +		/* Page will always be 0 */
> +		adis->current_page = 0;
> +	}
> +
>  	return adis_enable_irq(adis, false);
>  }
>  EXPORT_SYMBOL_GPL(adis_init);
> diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c
> index 7857133..99d8e0b 100644
> --- a/drivers/iio/imu/adis_buffer.c
> +++ b/drivers/iio/imu/adis_buffer.c
> @@ -83,10 +83,25 @@ static irqreturn_t adis_trigger_handler(int irq, void *p)
>  	if (!adis->buffer)
>  		return -ENOMEM;
>  
> +	if (adis->data->has_paging) {
> +		mutex_lock(&adis->txrx_lock);
> +		if (adis->current_page != 0) {
> +			adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
> +			adis->tx[1] = 0;
> +			spi_write(adis->spi, adis->tx, 2);
> +		}
> +	}
> +
>  	ret = spi_sync(adis->spi, &adis->msg);
>  	if (ret)
>  		dev_err(&adis->spi->dev, "Failed to read data: %d", ret);
>  
> +
> +	if (adis->data->has_paging) {
> +		adis->current_page = 0;
> +		mutex_unlock(&adis->txrx_lock);
> +	}
> +
>  	/* Guaranteed to be aligned with 8 byte boundary */
>  	if (indio_dev->scan_timestamp) {
>  		void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64);
> diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h
> index 6402a08..e82cd08 100644
> --- a/include/linux/iio/imu/adis.h
> +++ b/include/linux/iio/imu/adis.h
> @@ -14,8 +14,11 @@
>  #include <linux/interrupt.h>
>  #include <linux/iio/types.h>
>  
> -#define ADIS_WRITE_REG(reg) (0x80 | (reg))
> -#define ADIS_READ_REG(reg) (reg)
> +#define ADIS_WRITE_REG(reg) ((0x80 | (reg)))
> +#define ADIS_READ_REG(reg) ((reg) & 0x7f)
> +
> +#define ADIS_PAGE_SIZE 0x80
> +#define ADIS_REG_PAGE_ID 0x00
>  
>  /**
>   * struct adis_data - ADIS chip variant specific data
> @@ -40,6 +43,8 @@ struct adis_data {
>  
>  	const char * const *status_error_msgs;
>  	unsigned int status_error_mask;
> +
> +	bool has_paging;
>  };
>  
>  struct adis {
> @@ -51,6 +56,7 @@ struct adis {
>  	struct mutex		txrx_lock;
>  	struct spi_message	msg;
>  	struct spi_transfer	*xfer;
> +	unsigned int		current_page;
>  	void			*buffer;
>  
>  	uint8_t			tx[10] ____cacheline_aligned;
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 5/7] iio: Add pressure channel type
  2012-11-20 13:36 ` [PATCH 5/7] iio: Add pressure channel type Lars-Peter Clausen
@ 2012-11-20 21:12   ` Jonathan Cameron
  0 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2012-11-20 21:12 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Jonathan Cameron, linux-iio, drivers

On 11/20/2012 01:36 PM, Lars-Peter Clausen wrote:
> This patch adds support for a new IIO channel type for pressure measurements.
> This can for example be used for barometric pressure sensors.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Whilst we have had quite a few pressure drivers mentioned, it is nice
to finally see one turn up!
added to togreg branch of iio.git
> ---
>  Documentation/ABI/testing/sysfs-bus-iio | 24 ++++++++++++++++++++++++
>  drivers/iio/industrialio-core.c         |  1 +
>  include/linux/iio/types.h               |  1 +
>  3 files changed, 26 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 2f06d40..f08d0f7 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -189,6 +189,14 @@ Description:
>  		A computed peak value based on the sum squared magnitude of
>  		the underlying value in the specified directions.
>  
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_raw
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_raw
> +KernelVersion:	3.8
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Raw pressure measurement from channel Y. Units after
> +		application of scale and offset are kilopascal.
> +
>  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_offset
>  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_x_offset
>  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
> @@ -197,6 +205,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_offset
>  What:		/sys/bus/iio/devices/iio:deviceX/in_voltage_offset
>  What:		/sys/bus/iio/devices/iio:deviceX/in_tempY_offset
>  What:		/sys/bus/iio/devices/iio:deviceX/in_temp_offset
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_offset
>  KernelVersion:	2.6.35
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> @@ -226,6 +236,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_magn_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_magn_z_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_scale
>  KernelVersion:	2.6.35
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> @@ -245,6 +257,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibbias
>  What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibbias
>  What:		/sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibbias
>  What:		/sys/bus/iio/devices/iio:deviceX/in_proximity0_calibbias
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_calibbias
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_calibbias
>  KernelVersion:	2.6.35
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> @@ -262,6 +276,8 @@ What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
>  What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
>  what		/sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibscale
>  what		/sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale
>  KernelVersion:	2.6.35
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> @@ -275,6 +291,8 @@ What:		/sys/.../iio:deviceX/in_voltage-voltage_scale_available
>  What:		/sys/.../iio:deviceX/out_voltageX_scale_available
>  What:		/sys/.../iio:deviceX/out_altvoltageX_scale_available
>  What:		/sys/.../iio:deviceX/in_capacitance_scale_available
> +What:		/sys/.../iio:deviceX/in_pressure_scale_available
> +What:		/sys/.../iio:deviceX/in_pressureY_scale_available
>  KernelVersion:	2.6.35
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> @@ -694,6 +712,8 @@ What:		/sys/.../buffer/scan_elements/in_voltageY_en
>  What:		/sys/.../buffer/scan_elements/in_voltageY-voltageZ_en
>  What:		/sys/.../buffer/scan_elements/in_incli_x_en
>  What:		/sys/.../buffer/scan_elements/in_incli_y_en
> +What:		/sys/.../buffer/scan_elements/in_pressureY_en
> +What:		/sys/.../buffer/scan_elements/in_pressure_en
>  KernelVersion:	2.6.37
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> @@ -707,6 +727,8 @@ What:		/sys/.../buffer/scan_elements/in_voltageY_type
>  What:		/sys/.../buffer/scan_elements/in_voltage_type
>  What:		/sys/.../buffer/scan_elements/in_voltageY_supply_type
>  What:		/sys/.../buffer/scan_elements/in_timestamp_type
> +What:		/sys/.../buffer/scan_elements/in_pressureY_type
> +What:		/sys/.../buffer/scan_elements/in_pressure_type
>  KernelVersion:	2.6.37
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> @@ -751,6 +773,8 @@ What:		/sys/.../buffer/scan_elements/in_magn_z_index
>  What:		/sys/.../buffer/scan_elements/in_incli_x_index
>  What:		/sys/.../buffer/scan_elements/in_incli_y_index
>  What:		/sys/.../buffer/scan_elements/in_timestamp_index
> +What:		/sys/.../buffer/scan_elements/in_pressureY_index
> +What:		/sys/.../buffer/scan_elements/in_pressure_index
>  KernelVersion:	2.6.37
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 060a404..3dccd6c 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -65,6 +65,7 @@ static const char * const iio_chan_type_name_spec[] = {
>  	[IIO_CAPACITANCE] = "capacitance",
>  	[IIO_ALTVOLTAGE] = "altvoltage",
>  	[IIO_CCT] = "cct",
> +	[IIO_PRESSURE] = "pressure",
>  };
>  
>  static const char * const iio_modifier_names[] = {
> diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> index 87b196a..88bf0f0 100644
> --- a/include/linux/iio/types.h
> +++ b/include/linux/iio/types.h
> @@ -28,6 +28,7 @@ enum iio_chan_type {
>  	IIO_CAPACITANCE,
>  	IIO_ALTVOLTAGE,
>  	IIO_CCT,
> +	IIO_PRESSURE,
>  };
>  
>  enum iio_modifier {
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 6/7] iio: Factor out fixed point number parsing into its own function
  2012-11-20 13:36 ` [PATCH 6/7] iio: Factor out fixed point number parsing into its own function Lars-Peter Clausen
@ 2012-11-20 21:14   ` Jonathan Cameron
  0 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2012-11-20 21:14 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Jonathan Cameron, linux-iio, drivers

On 11/20/2012 01:36 PM, Lars-Peter Clausen wrote:
> Factor out the code for parsing fixed point numbers into its own function and
> make this function globally available. This allows us to reuse the code to parse
> fixed point numbers in individual IIO drivers.
> 
Sensible thing to do.
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
added to togreg branch of iio.git
> ---
>  drivers/iio/industrialio-core.c | 98 ++++++++++++++++++++++++++---------------
>  include/linux/iio/iio.h         |  3 ++
>  2 files changed, 66 insertions(+), 35 deletions(-)
> 
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 3dccd6c..8848f16 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -408,6 +408,64 @@ static ssize_t iio_read_channel_info(struct device *dev,
>  	}
>  }
>  
> +/**
> + * iio_str_to_fixpoint() - Parse a fixed-point number from a string
> + * @str: The string to parse
> + * @fract_mult: Multiplier for the first decimal place, should be a power of 10
> + * @integer: The integer part of the number
> + * @fract: The fractional part of the number
> + *
> + * Returns 0 on success, or a negative error code if the string could not be
> + * parsed.
> + */
> +int iio_str_to_fixpoint(const char *str, int fract_mult,
> +	int *integer, int *fract)
> +{
> +	int i = 0, f = 0;
> +	bool integer_part = true, negative = false;
> +
> +	if (str[0] == '-') {
> +		negative = true;
> +		str++;
> +	} else if (str[0] == '+') {
> +		str++;
> +	}
> +
> +	while (*str) {
> +		if ('0' <= *str && *str <= '9') {
> +			if (integer_part) {
> +				i = i * 10 + *str - '0';
> +			} else {
> +				f += fract_mult * (*str - '0');
> +				fract_mult /= 10;
> +			}
> +		} else if (*str == '\n') {
> +			if (*(str + 1) == '\0')
> +				break;
> +			else
> +				return -EINVAL;
> +		} else if (*str == '.' && integer_part) {
> +			integer_part = false;
> +		} else {
> +			return -EINVAL;
> +		}
> +		str++;
> +	}
> +
> +	if (negative) {
> +		if (i)
> +			i = -i;
> +		else
> +			f = -f;
> +	}
> +
> +	*integer = i;
> +	*fract = f;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(iio_str_to_fixpoint);
> +
>  static ssize_t iio_write_channel_info(struct device *dev,
>  				      struct device_attribute *attr,
>  				      const char *buf,
> @@ -415,8 +473,8 @@ static ssize_t iio_write_channel_info(struct device *dev,
>  {
>  	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>  	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> -	int ret, integer = 0, fract = 0, fract_mult = 100000;
> -	bool integer_part = true, negative = false;
> +	int ret, fract_mult = 100000;
> +	int integer, fract;
>  
>  	/* Assumes decimal - precision based on number of digits */
>  	if (!indio_dev->info->write_raw)
> @@ -435,39 +493,9 @@ static ssize_t iio_write_channel_info(struct device *dev,
>  			return -EINVAL;
>  		}
>  
> -	if (buf[0] == '-') {
> -		negative = true;
> -		buf++;
> -	} else if (buf[0] == '+') {
> -		buf++;
> -	}
> -
> -	while (*buf) {
> -		if ('0' <= *buf && *buf <= '9') {
> -			if (integer_part)
> -				integer = integer*10 + *buf - '0';
> -			else {
> -				fract += fract_mult*(*buf - '0');
> -				fract_mult /= 10;
> -			}
> -		} else if (*buf == '\n') {
> -			if (*(buf + 1) == '\0')
> -				break;
> -			else
> -				return -EINVAL;
> -		} else if (*buf == '.' && integer_part) {
> -			integer_part = false;
> -		} else {
> -			return -EINVAL;
> -		}
> -		buf++;
> -	}
> -	if (negative) {
> -		if (integer)
> -			integer = -integer;
> -		else
> -			fract = -fract;
> -	}
> +	ret = iio_str_to_fixpoint(buf, fract_mult, &integer, &fract);
> +	if (ret)
> +		return ret;
>  
>  	ret = indio_dev->info->write_raw(indio_dev, this_attr->c,
>  					 integer, fract, this_attr->address);
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index adca93a..da8c776 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -620,6 +620,9 @@ static inline struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev)
>  };
>  #endif
>  
> +int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
> +	int *fract);
> +
>  /**
>   * IIO_DEGREE_TO_RAD() - Convert degree to rad
>   * @deg: A value in degree
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/7] iio:gyro: Add support for the ADIS16136 gyroscope
  2012-11-20 21:05   ` Jonathan Cameron
@ 2012-11-20 21:15     ` Lars-Peter Clausen
  2012-11-20 21:26       ` Jonathan Cameron
  0 siblings, 1 reply; 16+ messages in thread
From: Lars-Peter Clausen @ 2012-11-20 21:15 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: Jonathan Cameron, linux-iio, drivers

On 11/20/2012 10:05 PM, Jonathan Cameron wrote:
> On 11/20/2012 01:36 PM, Lars-Peter Clausen wrote:
>> This patch adds support for the ADIS16133, ADIS16135, ADIS16136 single channel
>> gyroscopes. The main difference between them is the sensor precision.
>>
>> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> A nice driver.
> 
> The only thing I could pick up on was the 'interesting' comments alongside
> the 3db divisors.
> 
> Still I just wanted to comment on something really ;)
> 
> added to togreg branch of iio.git
> 

just remembered that I wanted to remove the __devinit/__devexit annotations
before sending this patch out, but then forgot about it. Can you fix this up?
Otherwise I'll just sent a follow up patch. Same for the adis16480 driver.

Thanks,
- Lars

>> ---
>>  drivers/iio/gyro/Kconfig     |   9 +
>>  drivers/iio/gyro/Makefile    |   1 +
>>  drivers/iio/gyro/adis16136.c | 581 +++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 591 insertions(+)
>>  create mode 100644 drivers/iio/gyro/adis16136.c
>>
>> diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
>> index 21e27e2..48ed148 100644
>> --- a/drivers/iio/gyro/Kconfig
>> +++ b/drivers/iio/gyro/Kconfig
>> @@ -3,6 +3,15 @@
>>  #
>>  menu "Digital gyroscope sensors"
>>  
>> +config ADIS16136
>> +	tristate "Analog devices ADIS16136 and similar gyroscopes driver"
>> +	depends on SPI_MASTER
>> +	select IIO_ADIS_LIB
>> +	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
>> +	help
>> +	  Say yes here to build support for the Analog Devices ADIS16133, ADIS16135,
>> +	  ADIS16136 gyroscope devices.
>> +
>>  config HID_SENSOR_GYRO_3D
>>  	depends on HID_SENSOR_HUB
>>  	select IIO_BUFFER
>> diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
>> index 8a895d9..702a058 100644
>> --- a/drivers/iio/gyro/Makefile
>> +++ b/drivers/iio/gyro/Makefile
>> @@ -2,4 +2,5 @@
>>  # Makefile for industrial I/O gyroscope sensor drivers
>>  #
>>  
>> +obj-$(CONFIG_ADIS16136) += adis16136.o
>>  obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
>> diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c
>> new file mode 100644
>> index 0000000..277aa74
>> --- /dev/null
>> +++ b/drivers/iio/gyro/adis16136.c
>> @@ -0,0 +1,581 @@
>> +/*
>> + * ADIS16133/ADIS16135/ADIS16136 gyroscope driver
>> + *
>> + * Copyright 2012 Analog Devices Inc.
>> + *   Author: Lars-Peter Clausen <lars@metafoo.de>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/delay.h>
>> +#include <linux/mutex.h>
>> +#include <linux/device.h>
>> +#include <linux/kernel.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/slab.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/module.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/imu/adis.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/debugfs.h>
>> +
>> +#define ADIS16136_REG_FLASH_CNT		0x00
>> +#define ADIS16136_REG_TEMP_OUT		0x02
>> +#define ADIS16136_REG_GYRO_OUT2		0x04
>> +#define ADIS16136_REG_GYRO_OUT		0x06
>> +#define ADIS16136_REG_GYRO_OFF2		0x08
>> +#define ADIS16136_REG_GYRO_OFF		0x0A
>> +#define ADIS16136_REG_ALM_MAG1		0x10
>> +#define ADIS16136_REG_ALM_MAG2		0x12
>> +#define ADIS16136_REG_ALM_SAMPL1	0x14
>> +#define ADIS16136_REG_ALM_SAMPL2	0x16
>> +#define ADIS16136_REG_ALM_CTRL		0x18
>> +#define ADIS16136_REG_GPIO_CTRL		0x1A
>> +#define ADIS16136_REG_MSC_CTRL		0x1C
>> +#define ADIS16136_REG_SMPL_PRD		0x1E
>> +#define ADIS16136_REG_AVG_CNT		0x20
>> +#define ADIS16136_REG_DEC_RATE		0x22
>> +#define ADIS16136_REG_SLP_CTRL		0x24
>> +#define ADIS16136_REG_DIAG_STAT		0x26
>> +#define ADIS16136_REG_GLOB_CMD		0x28
>> +#define ADIS16136_REG_LOT1		0x32
>> +#define ADIS16136_REG_LOT2		0x34
>> +#define ADIS16136_REG_LOT3		0x36
>> +#define ADIS16136_REG_PROD_ID		0x38
>> +#define ADIS16136_REG_SERIAL_NUM	0x3A
>> +
>> +#define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL	2
>> +#define ADIS16136_DIAG_STAT_SPI_FAIL		3
>> +#define ADIS16136_DIAG_STAT_SELF_TEST_FAIL	5
>> +#define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL	6
>> +
>> +#define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11)
>> +#define ADIS16136_MSC_CTRL_SELF_TEST BIT(10)
>> +
>> +struct adis16136_chip_info {
>> +	unsigned int precision;
>> +	unsigned int fullscale;
>> +};
>> +
>> +struct adis16136 {
>> +	const struct adis16136_chip_info *chip_info;
>> +
>> +	struct adis adis;
>> +};
>> +
>> +#ifdef CONFIG_DEBUG_FS
>> +
>> +static ssize_t adis16136_show_serial(struct file *file,
>> +		char __user *userbuf, size_t count, loff_t *ppos)
>> +{
>> +	struct adis16136 *adis16136 = file->private_data;
>> +	uint16_t lot1, lot2, lot3, serial;
>> +	char buf[20];
>> +	size_t len;
>> +	int ret;
>> +
>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SERIAL_NUM,
>> +		&serial);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	len = snprintf(buf, sizeof(buf), "%.4x%.4x%.4x-%.4x\n", lot1, lot2,
>> +		lot3, serial);
>> +
>> +	return simple_read_from_buffer(userbuf, count, ppos, buf, len);
>> +}
>> +
>> +static const struct file_operations adis16136_serial_fops = {
>> +	.open = simple_open,
>> +	.read = adis16136_show_serial,
>> +	.llseek = default_llseek,
>> +	.owner = THIS_MODULE,
>> +};
>> +
>> +static int adis16136_show_product_id(void *arg, u64 *val)
>> +{
>> +	struct adis16136 *adis16136 = arg;
>> +	u16 prod_id;
>> +	int ret;
>> +
>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
>> +		&prod_id);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	*val = prod_id;
>> +
>> +	return 0;
>> +}
>> +DEFINE_SIMPLE_ATTRIBUTE(adis16136_product_id_fops,
>> +	adis16136_show_product_id, NULL, "%llu\n");
>> +
>> +static int adis16136_show_flash_count(void *arg, u64 *val)
>> +{
>> +	struct adis16136 *adis16136 = arg;
>> +	uint16_t flash_count;
>> +	int ret;
>> +
>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_FLASH_CNT,
>> +		&flash_count);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	*val = flash_count;
>> +
>> +	return 0;
>> +}
>> +DEFINE_SIMPLE_ATTRIBUTE(adis16136_flash_count_fops,
>> +	adis16136_show_flash_count, NULL, "%lld\n");
>> +
>> +static int adis16136_debugfs_init(struct iio_dev *indio_dev)
>> +{
>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>> +
>> +	debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
>> +		adis16136, &adis16136_serial_fops);
>> +	debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
>> +		adis16136, &adis16136_product_id_fops);
>> +	debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
>> +		adis16136, &adis16136_flash_count_fops);
>> +
>> +	return 0;
>> +}
>> +
>> +#else
>> +
>> +static int adis16136_debugfs_init(struct iio_dev *indio_dev)
>> +{
>> +	return 0;
>> +}
>> +
>> +#endif
>> +
>> +static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq)
>> +{
>> +	unsigned int t;
>> +
>> +	t = 32768 / freq;
>> +	if (t < 0xf)
>> +		t = 0xf;
>> +	else if (t > 0xffff)
>> +		t = 0xffff;
>> +	else
>> +		t--;
>> +
>> +	return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t);
>> +}
>> +
>> +static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq)
>> +{
>> +	uint16_t t;
>> +	int ret;
>> +
>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	*freq = 32768 / (t + 1);
>> +
>> +	return 0;
>> +}
>> +
>> +static ssize_t adis16136_write_frequency(struct device *dev,
>> +	struct device_attribute *attr, const char *buf, size_t len)
>> +{
>> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>> +	long val;
>> +	int ret;
>> +
>> +	ret = kstrtol(buf, 10, &val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (val == 0)
>> +		return -EINVAL;
>> +
>> +	ret = adis16136_set_freq(adis16136, val);
>> +
>> +	return ret ? ret : len;
>> +}
>> +
>> +static ssize_t adis16136_read_frequency(struct device *dev,
>> +	struct device_attribute *attr, char *buf)
>> +{
>> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>> +	unsigned int freq;
>> +	int ret;
>> +
>> +	ret = adis16136_get_freq(adis16136, &freq);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return sprintf(buf, "%d\n", freq);
>> +}
>> +
>> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
>> +				  adis16136_read_frequency,
>> +				  adis16136_write_frequency);
>> +
> I love the comments.  Useful sounding but I have no idea what
> they actually mean!
>> +static const unsigned adis16136_3db_divisors[] = {
>> +	[0] = 2, /* Special case */
>> +	[1] = 6,
>> +	[2] = 12,
>> +	[3] = 25,
>> +	[4] = 50,
>> +	[5] = 100,
>> +	[6] = 200,
>> +	[7] = 200, /* Not a valid setting */
>> +};
>> +
>> +static int adis16136_set_filter(struct iio_dev *indio_dev, int val)
>> +{
>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>> +	unsigned int freq;
>> +	int i, ret;
>> +
>> +	ret = adis16136_get_freq(adis16136, &freq);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) {
>> +		if (freq / adis16136_3db_divisors[i] >= val)
>> +			break;
>> +	}
>> +
>> +	return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i);
>> +}
>> +
>> +static int adis16136_get_filter(struct iio_dev *indio_dev, int *val)
>> +{
>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>> +	unsigned int freq;
>> +	uint16_t val16;
>> +	int ret;
>> +
>> +	mutex_lock(&indio_dev->mlock);
>> +
>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16);
>> +	if (ret < 0)
>> +		goto err_unlock;
>> +
>> +	ret = adis16136_get_freq(adis16136, &freq);
>> +	if (ret < 0)
>> +		goto err_unlock;
>> +
>> +	*val = freq / adis16136_3db_divisors[val16 & 0x07];
>> +
>> +err_unlock:
>> +	mutex_unlock(&indio_dev->mlock);
>> +
>> +	return ret ? ret : IIO_VAL_INT;
>> +}
>> +
>> +static int adis16136_read_raw(struct iio_dev *indio_dev,
>> +	const struct iio_chan_spec *chan, int *val, int *val2, long info)
>> +{
>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>> +	uint32_t val32;
>> +	int ret;
>> +
>> +	switch (info) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		return adis_single_conversion(indio_dev, chan, 0, val);
>> +	case IIO_CHAN_INFO_SCALE:
>> +		switch (chan->type) {
>> +		case IIO_ANGL_VEL:
>> +			*val = adis16136->chip_info->precision;
>> +			*val2 = (adis16136->chip_info->fullscale << 16);
>> +			return IIO_VAL_FRACTIONAL;
>> +		case IIO_TEMP:
>> +			*val = 10;
>> +			*val2 = 697000; /* 0.010697 degree Celsius */
>> +			return IIO_VAL_INT_PLUS_MICRO;
>> +		default:
>> +			return -EINVAL;
>> +		}
>> +	case IIO_CHAN_INFO_CALIBBIAS:
>> +		ret = adis_read_reg_32(&adis16136->adis,
>> +			ADIS16136_REG_GYRO_OFF2, &val32);
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		*val = sign_extend32(val32, 31);
>> +
>> +		return IIO_VAL_INT;
>> +	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
>> +		return adis16136_get_filter(indio_dev, val);
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static int adis16136_write_raw(struct iio_dev *indio_dev,
>> +	const struct iio_chan_spec *chan, int val, int val2, long info)
>> +{
>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>> +
>> +	switch (info) {
>> +	case IIO_CHAN_INFO_CALIBBIAS:
>> +		return adis_write_reg_32(&adis16136->adis,
>> +			ADIS16136_REG_GYRO_OFF2, val);
>> +	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
>> +		return adis16136_set_filter(indio_dev, val);
>> +	default:
>> +		break;
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +enum {
>> +	ADIS16136_SCAN_GYRO,
>> +	ADIS16136_SCAN_TEMP,
>> +};
>> +
>> +static const struct iio_chan_spec adis16136_channels[] = {
>> +	{
>> +		.type = IIO_ANGL_VEL,
>> +		.modified = 1,
>> +		.channel2 = IIO_MOD_X,
>> +		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
>> +			IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
>> +			IIO_CHAN_INFO_SCALE_SHARED_BIT |
>> +			IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT,
>> +		.address = ADIS16136_REG_GYRO_OUT2,
>> +		.scan_index = ADIS16136_SCAN_GYRO,
>> +		.scan_type = {
>> +			.sign = 's',
>> +			.realbits = 32,
>> +			.storagebits = 32,
>> +			.endianness = IIO_BE,
>> +		},
>> +	}, {
>> +		.type = IIO_TEMP,
>> +		.indexed = 1,
>> +		.channel = 0,
>> +		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
>> +			IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
>> +		.address = ADIS16136_REG_TEMP_OUT,
>> +		.scan_index = ADIS16136_SCAN_TEMP,
>> +		.scan_type = {
>> +			.sign = 's',
>> +			.realbits = 16,
>> +			.storagebits = 16,
>> +			.endianness = IIO_BE,
>> +		},
>> +	},
>> +	IIO_CHAN_SOFT_TIMESTAMP(2),
>> +};
>> +
>> +static struct attribute *adis16136_attributes[] = {
>> +	&iio_dev_attr_sampling_frequency.dev_attr.attr,
>> +	NULL
>> +};
>> +
>> +static const struct attribute_group adis16136_attribute_group = {
>> +	.attrs = adis16136_attributes,
>> +};
>> +
>> +static const struct iio_info adis16136_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.attrs = &adis16136_attribute_group,
>> +	.read_raw = &adis16136_read_raw,
>> +	.write_raw = &adis16136_write_raw,
>> +	.update_scan_mode = adis_update_scan_mode,
>> +	.debugfs_reg_access = adis_debugfs_reg_access,
>> +};
>> +
>> +static int adis16136_stop_device(struct iio_dev *indio_dev)
>> +{
>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	ret = adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SLP_CTRL, 0xff);
>> +	if (ret)
>> +		dev_err(&indio_dev->dev,
>> +			"Could not power down device: %d\n", ret);
>> +
>> +	return ret;
>> +}
>> +
>> +static int adis16136_initial_setup(struct iio_dev *indio_dev)
>> +{
>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>> +	unsigned int device_id;
>> +	uint16_t prod_id;
>> +	int ret;
>> +
>> +	ret = adis_initial_startup(&adis16136->adis);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
>> +		&prod_id);
>> +	if (ret)
>> +		return ret;
>> +
>> +	sscanf(indio_dev->name, "adis%u\n", &device_id);
>> +
>> +	if (prod_id != device_id)
>> +		dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
>> +				device_id, prod_id);
>> +
>> +	return 0;
>> +}
>> +
>> +static const char * const adis16136_status_error_msgs[] = {
>> +	[ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL] = "Flash update failed",
>> +	[ADIS16136_DIAG_STAT_SPI_FAIL] = "SPI failure",
>> +	[ADIS16136_DIAG_STAT_SELF_TEST_FAIL] = "Self test error",
>> +	[ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error",
>> +};
>> +
>> +static const struct adis_data adis16136_data = {
>> +	.diag_stat_reg = ADIS16136_REG_DIAG_STAT,
>> +	.glob_cmd_reg = ADIS16136_REG_GLOB_CMD,
>> +	.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL,
>> +
>> +	.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST,
>> +	.startup_delay = 80,
>> +
>> +	.read_delay = 10,
>> +	.write_delay = 10,
>> +
>> +	.status_error_msgs = adis16136_status_error_msgs,
>> +	.status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) |
>> +		BIT(ADIS16136_DIAG_STAT_SPI_FAIL) |
>> +		BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) |
>> +		BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL),
>> +};
>> +
>> +enum adis16136_id {
>> +	ID_ADIS16133,
>> +	ID_ADIS16135,
>> +	ID_ADIS16136,
>> +};
>> +
>> +static const struct adis16136_chip_info adis16136_chip_info[] = {
>> +	[ID_ADIS16133] = {
>> +		.precision = IIO_DEGREE_TO_RAD(1200),
>> +		.fullscale = 24000,
>> +	},
>> +	[ID_ADIS16135] = {
>> +		.precision = IIO_DEGREE_TO_RAD(300),
>> +		.fullscale = 24000,
>> +	},
>> +	[ID_ADIS16136] = {
>> +		.precision = IIO_DEGREE_TO_RAD(450),
>> +		.fullscale = 24623,
>> +	},
>> +};
>> +
>> +static int __devinit adis16136_probe(struct spi_device *spi)
>> +{
>> +	const struct spi_device_id *id = spi_get_device_id(spi);
>> +	struct adis16136 *adis16136;
>> +	struct iio_dev *indio_dev;
>> +	int ret;
>> +
>> +	indio_dev = iio_device_alloc(sizeof(*adis16136));
>> +	if (indio_dev == NULL)
>> +		return -ENOMEM;
>> +
>> +	spi_set_drvdata(spi, indio_dev);
>> +
>> +	adis16136 = iio_priv(indio_dev);
>> +
>> +	adis16136->chip_info = &adis16136_chip_info[id->driver_data];
>> +	indio_dev->dev.parent = &spi->dev;
>> +	indio_dev->name = spi_get_device_id(spi)->name;
>> +	indio_dev->channels = adis16136_channels;
>> +	indio_dev->num_channels = ARRAY_SIZE(adis16136_channels);
>> +	indio_dev->info = &adis16136_info;
>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> +	ret = adis_init(&adis16136->adis, indio_dev, spi, &adis16136_data);
>> +	if (ret)
>> +		goto error_free_dev;
>> +
>> +	ret = adis_setup_buffer_and_trigger(&adis16136->adis, indio_dev, NULL);
>> +	if (ret)
>> +		goto error_free_dev;
>> +
>> +	ret = adis16136_initial_setup(indio_dev);
>> +	if (ret)
>> +		goto error_cleanup_buffer;
>> +
>> +	ret = iio_device_register(indio_dev);
>> +	if (ret)
>> +		goto error_stop_device;
>> +
>> +	adis16136_debugfs_init(indio_dev);
>> +
>> +	return 0;
>> +
>> +error_stop_device:
>> +	adis16136_stop_device(indio_dev);
>> +error_cleanup_buffer:
>> +	adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
>> +error_free_dev:
>> +	iio_device_free(indio_dev);
>> +	return ret;
>> +}
>> +
>> +static int __devexit adis16136_remove(struct spi_device *spi)
>> +{
>> +	struct iio_dev *indio_dev = spi_get_drvdata(spi);
>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>> +
>> +	iio_device_unregister(indio_dev);
>> +	adis16136_stop_device(indio_dev);
>> +
>> +	adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
>> +
>> +	iio_device_free(indio_dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct spi_device_id adis16136_ids[] = {
>> +	{ "adis16133", ID_ADIS16133 },
>> +	{ "adis16135", ID_ADIS16135 },
>> +	{ "adis16136", ID_ADIS16136 },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(spi, adis16136_ids);
>> +
>> +static struct spi_driver adis16136_driver = {
>> +	.driver = {
>> +		.name = "adis16136",
>> +		.owner = THIS_MODULE,
>> +	},
>> +	.id_table = adis16136_ids,
>> +	.probe = adis16136_probe,
>> +	.remove = __devexit_p(adis16136_remove),
>> +};
>> +module_spi_driver(adis16136_driver);
>> +
>> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
>> +MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver");
>> +MODULE_LICENSE("GPL v2");
>>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 7/7] iio:imu: Add support for the ADIS16480 and similar IMUs
  2012-11-20 13:36 ` [PATCH 7/7] iio:imu: Add support for the ADIS16480 and similar IMUs Lars-Peter Clausen
@ 2012-11-20 21:24   ` Jonathan Cameron
  0 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2012-11-20 21:24 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Jonathan Cameron, linux-iio, drivers

On 11/20/2012 01:36 PM, Lars-Peter Clausen wrote:
> This patch adds support for the ADIS16375, ADIS16480, ADIS16485, ADIS16488 6
> degree to 10 degree of freedom IMUs.

Only one minor nitpick.. docs in your adis library have slipped a little
behind the code with some new element struct adis_data not described
(they are obvious, but consistency is king ;)
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Added to togreg branch of iio.git.

Lots of nice stuff in this series.  Thanks!

> ---
>  drivers/iio/imu/Kconfig      |  16 +
>  drivers/iio/imu/Makefile     |   2 +
>  drivers/iio/imu/adis.c       |   3 +
>  drivers/iio/imu/adis16480.c  | 925 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/iio/imu/adis.h |   4 +
>  5 files changed, 950 insertions(+)
>  create mode 100644 drivers/iio/imu/adis16480.c
>
> diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
> index c24410c..8685a16 100644
> --- a/drivers/iio/imu/Kconfig
> +++ b/drivers/iio/imu/Kconfig
> @@ -1,3 +1,19 @@
> +#
> +# IIO imu drivers configuration
> +#
> +menu "Inertial measurement units"
> +
> +config ADIS16480
> +	tristate "Analog Devices ADIS16480 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 ADIS16375, ADIS16480,
> +	  ADIS16485, ADIS16488 inertial sensors.
> +
> +endmenu
> +
>  config IIO_ADIS_LIB
>  	tristate
>  	help
> diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
> index 97676ab..cfe5763 100644
> --- a/drivers/iio/imu/Makefile
> +++ b/drivers/iio/imu/Makefile
> @@ -2,6 +2,8 @@
>  # Makefile for Inertial Measurement Units
>  #
>
> +obj-$(CONFIG_ADIS16480) += adis16480.o
> +
>  adis_lib-y += adis.o
>  adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o
>  adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
> diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
> index c4ea04f..911255d 100644
> --- a/drivers/iio/imu/adis.c
> +++ b/drivers/iio/imu/adis.c
> @@ -238,6 +238,9 @@ int adis_enable_irq(struct adis *adis, bool enable)
>  	int ret = 0;
>  	uint16_t msc;
>
> +	if (adis->data->enable_irq)
> +		return adis->data->enable_irq(adis, enable);
> +
>  	ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc);
>  	if (ret)
>  		goto error_ret;
> diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
> new file mode 100644
> index 0000000..a058527
> --- /dev/null
> +++ b/drivers/iio/imu/adis16480.c
> @@ -0,0 +1,925 @@
> +/*
> + * ADIS16480 and similar IMUs driver
> + *
> + * Copyright 2012 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/spi/spi.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/module.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/imu/adis.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/debugfs.h>
> +
> +#define ADIS16480_PAGE_SIZE 0x80
> +
> +#define ADIS16480_REG(page, reg) ((page) * ADIS16480_PAGE_SIZE + (reg))
> +
> +#define ADIS16480_REG_PAGE_ID 0x00 /* Same address on each page */
> +#define ADIS16480_REG_SEQ_CNT			ADIS16480_REG(0x00, 0x06)
> +#define ADIS16480_REG_SYS_E_FLA			ADIS16480_REG(0x00, 0x08)
> +#define ADIS16480_REG_DIAG_STS			ADIS16480_REG(0x00, 0x0A)
> +#define ADIS16480_REG_ALM_STS			ADIS16480_REG(0x00, 0x0C)
> +#define ADIS16480_REG_TEMP_OUT			ADIS16480_REG(0x00, 0x0E)
> +#define ADIS16480_REG_X_GYRO_OUT		ADIS16480_REG(0x00, 0x10)
> +#define ADIS16480_REG_Y_GYRO_OUT		ADIS16480_REG(0x00, 0x14)
> +#define ADIS16480_REG_Z_GYRO_OUT		ADIS16480_REG(0x00, 0x18)
> +#define ADIS16480_REG_X_ACCEL_OUT		ADIS16480_REG(0x00, 0x1C)
> +#define ADIS16480_REG_Y_ACCEL_OUT		ADIS16480_REG(0x00, 0x20)
> +#define ADIS16480_REG_Z_ACCEL_OUT		ADIS16480_REG(0x00, 0x24)
> +#define ADIS16480_REG_X_MAGN_OUT		ADIS16480_REG(0x00, 0x28)
> +#define ADIS16480_REG_Y_MAGN_OUT		ADIS16480_REG(0x00, 0x2A)
> +#define ADIS16480_REG_Z_MAGN_OUT		ADIS16480_REG(0x00, 0x2C)
> +#define ADIS16480_REG_BAROM_OUT			ADIS16480_REG(0x00, 0x2E)
> +#define ADIS16480_REG_X_DELTAANG_OUT		ADIS16480_REG(0x00, 0x40)
> +#define ADIS16480_REG_Y_DELTAANG_OUT		ADIS16480_REG(0x00, 0x44)
> +#define ADIS16480_REG_Z_DELTAANG_OUT		ADIS16480_REG(0x00, 0x48)
> +#define ADIS16480_REG_X_DELTAVEL_OUT		ADIS16480_REG(0x00, 0x4C)
> +#define ADIS16480_REG_Y_DELTAVEL_OUT		ADIS16480_REG(0x00, 0x50)
> +#define ADIS16480_REG_Z_DELTAVEL_OUT		ADIS16480_REG(0x00, 0x54)
> +#define ADIS16480_REG_PROD_ID			ADIS16480_REG(0x00, 0x7E)
> +
> +#define ADIS16480_REG_X_GYRO_SCALE		ADIS16480_REG(0x02, 0x04)
> +#define ADIS16480_REG_Y_GYRO_SCALE		ADIS16480_REG(0x02, 0x06)
> +#define ADIS16480_REG_Z_GYRO_SCALE		ADIS16480_REG(0x02, 0x08)
> +#define ADIS16480_REG_X_ACCEL_SCALE		ADIS16480_REG(0x02, 0x0A)
> +#define ADIS16480_REG_Y_ACCEL_SCALE		ADIS16480_REG(0x02, 0x0C)
> +#define ADIS16480_REG_Z_ACCEL_SCALE		ADIS16480_REG(0x02, 0x0E)
> +#define ADIS16480_REG_X_GYRO_BIAS		ADIS16480_REG(0x02, 0x10)
> +#define ADIS16480_REG_Y_GYRO_BIAS		ADIS16480_REG(0x02, 0x14)
> +#define ADIS16480_REG_Z_GYRO_BIAS		ADIS16480_REG(0x02, 0x18)
> +#define ADIS16480_REG_X_ACCEL_BIAS		ADIS16480_REG(0x02, 0x1C)
> +#define ADIS16480_REG_Y_ACCEL_BIAS		ADIS16480_REG(0x02, 0x20)
> +#define ADIS16480_REG_Z_ACCEL_BIAS		ADIS16480_REG(0x02, 0x24)
> +#define ADIS16480_REG_X_HARD_IRON		ADIS16480_REG(0x02, 0x28)
> +#define ADIS16480_REG_Y_HARD_IRON		ADIS16480_REG(0x02, 0x2A)
> +#define ADIS16480_REG_Z_HARD_IRON		ADIS16480_REG(0x02, 0x2C)
> +#define ADIS16480_REG_BAROM_BIAS		ADIS16480_REG(0x02, 0x40)
> +#define ADIS16480_REG_FLASH_CNT			ADIS16480_REG(0x02, 0x7C)
> +
> +#define ADIS16480_REG_GLOB_CMD			ADIS16480_REG(0x03, 0x02)
> +#define ADIS16480_REG_FNCTIO_CTRL		ADIS16480_REG(0x03, 0x06)
> +#define ADIS16480_REG_GPIO_CTRL			ADIS16480_REG(0x03, 0x08)
> +#define ADIS16480_REG_CONFIG			ADIS16480_REG(0x03, 0x0A)
> +#define ADIS16480_REG_DEC_RATE			ADIS16480_REG(0x03, 0x0C)
> +#define ADIS16480_REG_SLP_CNT			ADIS16480_REG(0x03, 0x10)
> +#define ADIS16480_REG_FILTER_BNK0		ADIS16480_REG(0x03, 0x16)
> +#define ADIS16480_REG_FILTER_BNK1		ADIS16480_REG(0x03, 0x18)
> +#define ADIS16480_REG_ALM_CNFG0			ADIS16480_REG(0x03, 0x20)
> +#define ADIS16480_REG_ALM_CNFG1			ADIS16480_REG(0x03, 0x22)
> +#define ADIS16480_REG_ALM_CNFG2			ADIS16480_REG(0x03, 0x24)
> +#define ADIS16480_REG_XG_ALM_MAGN		ADIS16480_REG(0x03, 0x28)
> +#define ADIS16480_REG_YG_ALM_MAGN		ADIS16480_REG(0x03, 0x2A)
> +#define ADIS16480_REG_ZG_ALM_MAGN		ADIS16480_REG(0x03, 0x2C)
> +#define ADIS16480_REG_XA_ALM_MAGN		ADIS16480_REG(0x03, 0x2E)
> +#define ADIS16480_REG_YA_ALM_MAGN		ADIS16480_REG(0x03, 0x30)
> +#define ADIS16480_REG_ZA_ALM_MAGN		ADIS16480_REG(0x03, 0x32)
> +#define ADIS16480_REG_XM_ALM_MAGN		ADIS16480_REG(0x03, 0x34)
> +#define ADIS16480_REG_YM_ALM_MAGN		ADIS16480_REG(0x03, 0x36)
> +#define ADIS16480_REG_ZM_ALM_MAGN		ADIS16480_REG(0x03, 0x38)
> +#define ADIS16480_REG_BR_ALM_MAGN		ADIS16480_REG(0x03, 0x3A)
> +#define ADIS16480_REG_FIRM_REV			ADIS16480_REG(0x03, 0x78)
> +#define ADIS16480_REG_FIRM_DM			ADIS16480_REG(0x03, 0x7A)
> +#define ADIS16480_REG_FIRM_Y			ADIS16480_REG(0x03, 0x7C)
> +
> +#define ADIS16480_REG_SERIAL_NUM		ADIS16480_REG(0x04, 0x20)
> +
> +/* Each filter coefficent bank spans two pages */
> +#define ADIS16480_FIR_COEF(page) (x < 60 ? ADIS16480_REG(page, (x) + 8) : \
> +		ADIS16480_REG((page) + 1, (x) - 60 + 8))
> +#define ADIS16480_FIR_COEF_A(x)			ADIS16480_FIR_COEF(0x05, (x))
> +#define ADIS16480_FIR_COEF_B(x)			ADIS16480_FIR_COEF(0x07, (x))
> +#define ADIS16480_FIR_COEF_C(x)			ADIS16480_FIR_COEF(0x09, (x))
> +#define ADIS16480_FIR_COEF_D(x)			ADIS16480_FIR_COEF(0x0B, (x))
> +
> +struct adis16480_chip_info {
> +	unsigned int num_channels;
> +	const struct iio_chan_spec *channels;
> +};
> +
> +struct adis16480 {
> +	const struct adis16480_chip_info *chip_info;
> +
> +	struct adis adis;
> +};
> +
> +#ifdef CONFIG_DEBUG_FS
> +
> +static ssize_t adis16480_show_firmware_revision(struct file *file,
> +		char __user *userbuf, size_t count, loff_t *ppos)
> +{
> +	struct adis16480 *adis16480 = file->private_data;
> +	char buf[6];
> +	size_t len;
> +	u16 rev;
> +	int ret;
> +
> +	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_REV, &rev);
> +	if (ret < 0)
> +		return ret;
> +
> +	len = snprintf(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 adis16480_firmware_revision_fops = {
> +	.open = simple_open,
> +	.read = adis16480_show_firmware_revision,
> +	.llseek = default_llseek,
> +	.owner = THIS_MODULE,
> +};
> +
> +static ssize_t adis16480_show_firmware_date(struct file *file,
> +		char __user *userbuf, size_t count, loff_t *ppos)
> +{
> +	struct adis16480 *adis16480 = file->private_data;
> +	u16 md, year;
> +	char buf[12];
> +	size_t len;
> +	int ret;
> +
> +	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_Y, &year);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_DM, &md);
> +	if (ret < 0)
> +		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 adis16480_firmware_date_fops = {
> +	.open = simple_open,
> +	.read = adis16480_show_firmware_date,
> +	.llseek = default_llseek,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int adis16480_show_serial_number(void *arg, u64 *val)
> +{
> +	struct adis16480 *adis16480 = arg;
> +	u16 serial;
> +	int ret;
> +
> +	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_SERIAL_NUM,
> +		&serial);
> +	if (ret < 0)
> +		return ret;
> +
> +	*val = serial;
> +
> +	return 0;
> +}
> +DEFINE_SIMPLE_ATTRIBUTE(adis16480_serial_number_fops,
> +	adis16480_show_serial_number, NULL, "0x%.4llx\n");
> +
> +static int adis16480_show_product_id(void *arg, u64 *val)
> +{
> +	struct adis16480 *adis16480 = arg;
> +	u16 prod_id;
> +	int ret;
> +
> +	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_PROD_ID,
> +		&prod_id);
> +	if (ret < 0)
> +		return ret;
> +
> +	*val = prod_id;
> +
> +	return 0;
> +}
> +DEFINE_SIMPLE_ATTRIBUTE(adis16480_product_id_fops,
> +	adis16480_show_product_id, NULL, "%llu\n");
> +
> +static int adis16480_show_flash_count(void *arg, u64 *val)
> +{
> +	struct adis16480 *adis16480 = arg;
> +	u32 flash_count;
> +	int ret;
> +
> +	ret = adis_read_reg_32(&adis16480->adis, ADIS16480_REG_FLASH_CNT,
> +		&flash_count);
> +	if (ret < 0)
> +		return ret;
> +
> +	*val = flash_count;
> +
> +	return 0;
> +}
> +DEFINE_SIMPLE_ATTRIBUTE(adis16480_flash_count_fops,
> +	adis16480_show_flash_count, NULL, "%lld\n");
> +
> +static int adis16480_debugfs_init(struct iio_dev *indio_dev)
> +{
> +	struct adis16480 *adis16480 = iio_priv(indio_dev);
> +
> +	debugfs_create_file("firmware_revision", 0400,
> +		indio_dev->debugfs_dentry, adis16480,
> +		&adis16480_firmware_revision_fops);
> +	debugfs_create_file("firmware_date", 0400, indio_dev->debugfs_dentry,
> +		adis16480, &adis16480_firmware_date_fops);
> +	debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
> +		adis16480, &adis16480_serial_number_fops);
> +	debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
> +		adis16480, &adis16480_product_id_fops);
> +	debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
> +		adis16480, &adis16480_flash_count_fops);
> +
> +	return 0;
> +}
> +
> +#else
> +
> +static int adis16480_debugfs_init(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +
> +#endif
> +
> +static int adis16480_set_freq(struct adis16480 *st, unsigned int freq)
> +{
> +	unsigned int t;
> +
> +	t = 2460000 / freq;
> +	if (t > 2048)
> +		t = 2048;
> +
> +	if (t != 0)
> +		t--;
> +
> +	return adis_write_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, t);
> +}
> +
> +static int adis16480_get_freq(struct adis16480 *st, unsigned int *freq)
> +{
> +	uint16_t t;
> +	int ret;
> +
> +	ret = adis_read_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, &t);
> +	if (ret < 0)
> +		return ret;
> +
> +	*freq = 2460000 / (t + 1);
> +
> +	return 0;
> +}
> +
> +static ssize_t adis16480_read_frequency(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct adis16480 *st = iio_priv(indio_dev);
> +	unsigned int freq;
> +	int ret;
> +
> +	ret = adis16480_get_freq(st, &freq);
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d.%.3d\n", freq / 1000, freq % 1000);
> +}
> +
> +static ssize_t adis16480_write_frequency(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct adis16480 *st = iio_priv(indio_dev);
> +	int freq_int, freq_fract;
> +	long val;
> +	int ret;
> +
> +	ret = iio_str_to_fixpoint(buf, 100, &freq_int, &freq_fract);
> +	if (ret)
> +		return ret;
> +
> +	val = freq_int * 1000 + freq_fract;
> +
> +	if (val <= 0)
> +		return -EINVAL;
> +
> +	ret = adis16480_set_freq(st, val);
> +
> +	return ret ? ret : len;
> +}
> +
> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
> +			      adis16480_read_frequency,
> +			      adis16480_write_frequency);
> +
> +enum {
> +	ADIS16480_SCAN_GYRO_X,
> +	ADIS16480_SCAN_GYRO_Y,
> +	ADIS16480_SCAN_GYRO_Z,
> +	ADIS16480_SCAN_ACCEL_X,
> +	ADIS16480_SCAN_ACCEL_Y,
> +	ADIS16480_SCAN_ACCEL_Z,
> +	ADIS16480_SCAN_MAGN_X,
> +	ADIS16480_SCAN_MAGN_Y,
> +	ADIS16480_SCAN_MAGN_Z,
> +	ADIS16480_SCAN_BARO,
> +	ADIS16480_SCAN_TEMP,
> +};
> +
> +static const unsigned int adis16480_calibbias_regs[] = {
> +	[ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_BIAS,
> +	[ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_BIAS,
> +	[ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_BIAS,
> +	[ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_BIAS,
> +	[ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_BIAS,
> +	[ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_BIAS,
> +	[ADIS16480_SCAN_MAGN_X] = ADIS16480_REG_X_HARD_IRON,
> +	[ADIS16480_SCAN_MAGN_Y] = ADIS16480_REG_Y_HARD_IRON,
> +	[ADIS16480_SCAN_MAGN_Z] = ADIS16480_REG_Z_HARD_IRON,
> +	[ADIS16480_SCAN_BARO] = ADIS16480_REG_BAROM_BIAS,
> +};
> +
> +static const unsigned int adis16480_calibscale_regs[] = {
> +	[ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_SCALE,
> +	[ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_SCALE,
> +	[ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_SCALE,
> +	[ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_SCALE,
> +	[ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_SCALE,
> +	[ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_SCALE,
> +};
> +
> +static int adis16480_set_calibbias(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan, int bias)
> +{
> +	unsigned int reg = adis16480_calibbias_regs[chan->scan_index];
> +	struct adis16480 *st = iio_priv(indio_dev);
> +
> +	switch (chan->type) {
> +	case IIO_MAGN:
> +	case IIO_PRESSURE:
> +		if (bias < -0x8000 || bias >= 0x8000)
> +			return -EINVAL;
> +		return adis_write_reg_16(&st->adis, reg, bias);
> +	case IIO_ANGL_VEL:
> +	case IIO_ACCEL:
> +		return adis_write_reg_32(&st->adis, reg, bias);
> +	default:
> +		break;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int adis16480_get_calibbias(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan, int *bias)
> +{
> +	unsigned int reg = adis16480_calibbias_regs[chan->scan_index];
> +	struct adis16480 *st = iio_priv(indio_dev);
> +	uint16_t val16;
> +	uint32_t val32;
> +	int ret;
> +
> +	switch (chan->type) {
> +	case IIO_MAGN:
> +	case IIO_PRESSURE:
> +		ret = adis_read_reg_16(&st->adis, reg, &val16);
> +		*bias = sign_extend32(val16, 15);
> +		break;
> +	case IIO_ANGL_VEL:
> +	case IIO_ACCEL:
> +		ret = adis_read_reg_32(&st->adis, reg, &val32);
> +		*bias = sign_extend32(val32, 31);
> +		break;
> +	default:
> +			ret = -EINVAL;
> +	}
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int adis16480_set_calibscale(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan, int scale)
> +{
> +	unsigned int reg = adis16480_calibscale_regs[chan->scan_index];
> +	struct adis16480 *st = iio_priv(indio_dev);
> +
> +	if (scale < -0x8000 || scale >= 0x8000)
> +		return -EINVAL;
> +
> +	return adis_write_reg_16(&st->adis, reg, scale);
> +}
> +
> +static int adis16480_get_calibscale(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan, int *scale)
> +{
> +	unsigned int reg = adis16480_calibscale_regs[chan->scan_index];
> +	struct adis16480 *st = iio_priv(indio_dev);
> +	uint16_t val16;
> +	int ret;
> +
> +	ret = adis_read_reg_16(&st->adis, reg, &val16);
> +	if (ret < 0)
> +		return ret;
> +
> +	*scale = sign_extend32(val16, 15);
> +	return IIO_VAL_INT;
> +}
> +
> +static const unsigned int adis16480_def_filter_freqs[] = {
> +	310,
> +	55,
> +	275,
> +	63,
> +};
> +
> +static const unsigned int ad16480_filter_data[][2] = {
> +	[ADIS16480_SCAN_GYRO_X]		= { ADIS16480_REG_FILTER_BNK0, 0 },
> +	[ADIS16480_SCAN_GYRO_Y]		= { ADIS16480_REG_FILTER_BNK0, 3 },
> +	[ADIS16480_SCAN_GYRO_Z]		= { ADIS16480_REG_FILTER_BNK0, 6 },
> +	[ADIS16480_SCAN_ACCEL_X]	= { ADIS16480_REG_FILTER_BNK0, 9 },
> +	[ADIS16480_SCAN_ACCEL_Y]	= { ADIS16480_REG_FILTER_BNK0, 12 },
> +	[ADIS16480_SCAN_ACCEL_Z]	= { ADIS16480_REG_FILTER_BNK1, 0 },
> +	[ADIS16480_SCAN_MAGN_X]		= { ADIS16480_REG_FILTER_BNK1, 3 },
> +	[ADIS16480_SCAN_MAGN_Y]		= { ADIS16480_REG_FILTER_BNK1, 6 },
> +	[ADIS16480_SCAN_MAGN_Z]		= { ADIS16480_REG_FILTER_BNK1, 9 },
> +};
> +
> +static int adis16480_get_filter_freq(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan, int *freq)
> +{
> +	struct adis16480 *st = iio_priv(indio_dev);
> +	unsigned int enable_mask, offset, reg;
> +	uint16_t val;
> +	int ret;
> +
> +	reg = ad16480_filter_data[chan->scan_index][0];
> +	offset = ad16480_filter_data[chan->scan_index][1];
> +	enable_mask = BIT(offset + 2);
> +
> +	ret = adis_read_reg_16(&st->adis, reg, &val);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (!(val & enable_mask))
> +		*freq = 0;
> +	else
> +		*freq = adis16480_def_filter_freqs[(val >> offset) & 0x3];
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int adis16480_set_filter_freq(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan, unsigned int freq)
> +{
> +	struct adis16480 *st = iio_priv(indio_dev);
> +	unsigned int enable_mask, offset, reg;
> +	unsigned int diff, best_diff;
> +	unsigned int i, best_freq;
> +	uint16_t val;
> +	int ret;
> +
> +	reg = ad16480_filter_data[chan->scan_index][0];
> +	offset = ad16480_filter_data[chan->scan_index][1];
> +	enable_mask = BIT(offset + 2);
> +
> +	ret = adis_read_reg_16(&st->adis, reg, &val);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (freq == 0) {
> +		val &= ~enable_mask;
> +	} else {
> +		best_freq = 0;
> +		best_diff = 310;
> +		for (i = 0; i < ARRAY_SIZE(adis16480_def_filter_freqs); i++) {
> +			if (adis16480_def_filter_freqs[i] >= freq) {
> +				diff = adis16480_def_filter_freqs[i] - freq;
> +				if (diff < best_diff) {
> +					best_diff = diff;
> +					best_freq = i;
> +				}
> +			}
> +		}
> +
> +		val &= ~(0x3 << offset);
> +		val |= best_freq << offset;
> +		val |= enable_mask;
> +	}
> +
> +	return adis_write_reg_16(&st->adis, reg, val);
> +}
> +
> +static int adis16480_read_raw(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan, int *val, int *val2, long info)
> +{
> +	switch (info) {
> +	case IIO_CHAN_INFO_RAW:
> +		return adis_single_conversion(indio_dev, chan, 0, val);
> +	case IIO_CHAN_INFO_SCALE:
> +		switch (chan->type) {
> +		case IIO_ANGL_VEL:
> +			*val = 0;
> +			*val2 = IIO_DEGREE_TO_RAD(20000); /* 0.02 degree/sec */
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		case IIO_ACCEL:
> +			*val = 0;
> +			*val2 = IIO_G_TO_M_S_2(800); /* 0.8 mg */
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		case IIO_MAGN:
> +			*val = 0;
> +			*val2 = 100; /* 0.0001 gauss */
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		case IIO_TEMP:
> +			*val = 5;
> +			*val2 = 650000; /* 5.65 milli degree Celsius */
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		case IIO_PRESSURE:
> +			*val = 0;
> +			*val2 = 4000; /* 40ubar = 0.004 kPa */
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_OFFSET:
> +		/* Only the temperature channel has a offset */
> +		*val = 4425; /* 25 degree Celsius = 0x0000 */
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_CALIBBIAS:
> +		return adis16480_get_calibbias(indio_dev, chan, val);
> +	case IIO_CHAN_INFO_CALIBSCALE:
> +		return adis16480_get_calibscale(indio_dev, chan, val);
> +	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
> +		return adis16480_get_filter_freq(indio_dev, chan, val);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int adis16480_write_raw(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan, int val, int val2, long info)
> +{
> +	switch (info) {
> +	case IIO_CHAN_INFO_CALIBBIAS:
> +		return adis16480_set_calibbias(indio_dev, chan, val);
> +	case IIO_CHAN_INFO_CALIBSCALE:
> +		return adis16480_set_calibscale(indio_dev, chan, val);
> +	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
> +		return adis16480_set_filter_freq(indio_dev, chan, val);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +#define ADIS16480_MOD_CHANNEL(_type, _mod, _address, _si, _info, _bits) \
> +	{ \
> +		.type = (_type), \
> +		.modified = 1, \
> +		.channel2 = (_mod), \
> +		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
> +			IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
> +			IIO_CHAN_INFO_SCALE_SHARED_BIT | \
> +			_info, \
> +		.address = (_address), \
> +		.scan_index = (_si), \
> +		.scan_type = { \
> +			.sign = 's', \
> +			.realbits = (_bits), \
> +			.storagebits = (_bits), \
> +			.endianness = IIO_BE, \
> +		}, \
> +	}
> +
> +#define ADIS16480_GYRO_CHANNEL(_mod) \
> +	ADIS16480_MOD_CHANNEL(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \
> +	ADIS16480_REG_ ## _mod ## _GYRO_OUT, ADIS16480_SCAN_GYRO_ ## _mod, \
> +	IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \
> +	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \
> +	32)
> +
> +#define ADIS16480_ACCEL_CHANNEL(_mod) \
> +	ADIS16480_MOD_CHANNEL(IIO_ACCEL, IIO_MOD_ ## _mod, \
> +	ADIS16480_REG_ ## _mod ## _ACCEL_OUT, ADIS16480_SCAN_ACCEL_ ## _mod, \
> +	IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \
> +	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \
> +	32)
> +
> +#define ADIS16480_MAGN_CHANNEL(_mod) \
> +	ADIS16480_MOD_CHANNEL(IIO_MAGN, IIO_MOD_ ## _mod, \
> +	ADIS16480_REG_ ## _mod ## _MAGN_OUT, ADIS16480_SCAN_MAGN_ ## _mod, \
> +	IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT, \
> +	16)
> +
> +#define ADIS16480_PRESSURE_CHANNEL() \
> +	{ \
> +		.type = IIO_PRESSURE, \
> +		.indexed = 1, \
> +		.channel = 0, \
> +		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
> +			IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
> +			IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
> +		.address = ADIS16480_REG_BAROM_OUT, \
> +		.scan_index = ADIS16480_SCAN_BARO, \
> +		.scan_type = { \
> +			.sign = 's', \
> +			.realbits = 32, \
> +			.storagebits = 32, \
> +			.endianness = IIO_BE, \
> +		}, \
> +	}
> +
> +#define ADIS16480_TEMP_CHANNEL() { \
> +		.type = IIO_TEMP, \
> +		.indexed = 1, \
> +		.channel = 0, \
> +		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
> +			IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
> +			IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \
> +		.address = ADIS16480_REG_TEMP_OUT, \
> +		.scan_index = ADIS16480_SCAN_TEMP, \
> +		.scan_type = { \
> +			.sign = 's', \
> +			.realbits = 16, \
> +			.storagebits = 16, \
> +			.endianness = IIO_BE, \
> +		}, \
> +	}
> +
> +static const struct iio_chan_spec adis16480_channels[] = {
> +	ADIS16480_GYRO_CHANNEL(X),
> +	ADIS16480_GYRO_CHANNEL(Y),
> +	ADIS16480_GYRO_CHANNEL(Z),
> +	ADIS16480_ACCEL_CHANNEL(X),
> +	ADIS16480_ACCEL_CHANNEL(Y),
> +	ADIS16480_ACCEL_CHANNEL(Z),
> +	ADIS16480_MAGN_CHANNEL(X),
> +	ADIS16480_MAGN_CHANNEL(Y),
> +	ADIS16480_MAGN_CHANNEL(Z),
> +	ADIS16480_PRESSURE_CHANNEL(),
> +	ADIS16480_TEMP_CHANNEL(),
> +	IIO_CHAN_SOFT_TIMESTAMP(11)
> +};
> +
> +static const struct iio_chan_spec adis16485_channels[] = {
> +	ADIS16480_GYRO_CHANNEL(X),
> +	ADIS16480_GYRO_CHANNEL(Y),
> +	ADIS16480_GYRO_CHANNEL(Z),
> +	ADIS16480_ACCEL_CHANNEL(X),
> +	ADIS16480_ACCEL_CHANNEL(Y),
> +	ADIS16480_ACCEL_CHANNEL(Z),
> +	ADIS16480_TEMP_CHANNEL(),
> +	IIO_CHAN_SOFT_TIMESTAMP(7)
> +};
> +
> +enum adis16480_variant {
> +	ADIS16375,
> +	ADIS16480,
> +	ADIS16485,
> +	ADIS16488,
> +};
> +
> +static const struct adis16480_chip_info adis16480_chip_info[] = {
> +	[ADIS16375] = {
> +		.channels = adis16485_channels,
> +		.num_channels = ARRAY_SIZE(adis16485_channels),
> +	},
> +	[ADIS16480] = {
> +		.channels = adis16480_channels,
> +		.num_channels = ARRAY_SIZE(adis16480_channels),
> +	},
> +	[ADIS16485] = {
> +		.channels = adis16485_channels,
> +		.num_channels = ARRAY_SIZE(adis16485_channels),
> +	},
> +	[ADIS16488] = {
> +		.channels = adis16480_channels,
> +		.num_channels = ARRAY_SIZE(adis16480_channels),
> +	},
> +};
> +
> +static struct attribute *adis16480_attributes[] = {
> +	&iio_dev_attr_sampling_frequency.dev_attr.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group adis16480_attribute_group = {
> +	.attrs = adis16480_attributes,
> +};
> +
> +static const struct iio_info adis16480_info = {
> +	.attrs = &adis16480_attribute_group,
> +	.read_raw = &adis16480_read_raw,
> +	.write_raw = &adis16480_write_raw,
> +	.update_scan_mode = adis_update_scan_mode,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int adis16480_stop_device(struct iio_dev *indio_dev)
> +{
> +	struct adis16480 *st = iio_priv(indio_dev);
> +	int ret;
> +
> +	ret = adis_write_reg_16(&st->adis, ADIS16480_REG_SLP_CNT, BIT(9));
> +	if (ret)
> +		dev_err(&indio_dev->dev,
> +			"Could not power down device: %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int adis16480_enable_irq(struct adis *adis, bool enable)
> +{
> +	return adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL,
> +		enable ? BIT(3) : 0);
> +}
> +
> +static int adis16480_initial_setup(struct iio_dev *indio_dev)
> +{
> +	struct adis16480 *st = iio_priv(indio_dev);
> +	uint16_t prod_id;
> +	unsigned int device_id;
> +	int ret;
> +
> +	adis_reset(&st->adis);
> +	msleep(70);
> +
> +	ret = adis_write_reg_16(&st->adis, ADIS16480_REG_GLOB_CMD, BIT(1));
> +	if (ret)
> +		return ret;
> +	msleep(30);
> +
> +	ret = adis_check_status(&st->adis);
> +	if (ret)
> +		return ret;
> +
> +	ret = adis_read_reg_16(&st->adis, ADIS16480_REG_PROD_ID, &prod_id);
> +	if (ret)
> +		return ret;
> +
> +	sscanf(indio_dev->name, "adis%u\n", &device_id);
> +
> +	if (prod_id != device_id)
> +		dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
> +				device_id, prod_id);
> +
> +	return 0;
> +}
> +
> +#define ADIS16480_DIAG_STAT_XGYRO_FAIL 0
> +#define ADIS16480_DIAG_STAT_YGYRO_FAIL 1
> +#define ADIS16480_DIAG_STAT_ZGYRO_FAIL 2
> +#define ADIS16480_DIAG_STAT_XACCL_FAIL 3
> +#define ADIS16480_DIAG_STAT_YACCL_FAIL 4
> +#define ADIS16480_DIAG_STAT_ZACCL_FAIL 5
> +#define ADIS16480_DIAG_STAT_XMAGN_FAIL 8
> +#define ADIS16480_DIAG_STAT_YMAGN_FAIL 9
> +#define ADIS16480_DIAG_STAT_ZMAGN_FAIL 10
> +#define ADIS16480_DIAG_STAT_BARO_FAIL 11
> +
> +static const char * const adis16480_status_error_msgs[] = {
> +	[ADIS16480_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
> +	[ADIS16480_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
> +	[ADIS16480_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
> +	[ADIS16480_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
> +	[ADIS16480_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
> +	[ADIS16480_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
> +	[ADIS16480_DIAG_STAT_XMAGN_FAIL] = "X-axis magnetometer self-test failure",
> +	[ADIS16480_DIAG_STAT_YMAGN_FAIL] = "Y-axis magnetometer self-test failure",
> +	[ADIS16480_DIAG_STAT_ZMAGN_FAIL] = "Z-axis magnetometer self-test failure",
> +	[ADIS16480_DIAG_STAT_BARO_FAIL] = "Barometer self-test failure",
> +};
> +
> +static const struct adis_data adis16480_data = {
> +	.diag_stat_reg = ADIS16480_REG_DIAG_STS,
> +	.glob_cmd_reg = ADIS16480_REG_GLOB_CMD,
> +	.has_paging = true,
> +
> +	.read_delay = 5,
> +	.write_delay = 5,
> +
> +	.status_error_msgs = adis16480_status_error_msgs,
> +	.status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) |
> +		BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) |
> +		BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) |
> +		BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) |
> +		BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) |
> +		BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) |
> +		BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) |
> +		BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) |
> +		BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) |
> +		BIT(ADIS16480_DIAG_STAT_BARO_FAIL),
> +
> +	.enable_irq = adis16480_enable_irq,
> +};
> +
> +static int __devinit adis16480_probe(struct spi_device *spi)
> +{
> +	const struct spi_device_id *id = spi_get_device_id(spi);
> +	struct iio_dev *indio_dev;
> +	struct adis16480 *st;
> +	int ret;
> +
> +	indio_dev = iio_device_alloc(sizeof(*st));
> +	if (indio_dev == NULL)
> +		return -ENOMEM;
> +
> +	spi_set_drvdata(spi, indio_dev);
> +
> +	st = iio_priv(indio_dev);
> +
> +	st->chip_info = &adis16480_chip_info[id->driver_data];
> +	indio_dev->dev.parent = &spi->dev;
> +	indio_dev->name = spi_get_device_id(spi)->name;
> +	indio_dev->channels = st->chip_info->channels;
> +	indio_dev->num_channels = st->chip_info->num_channels;
> +	indio_dev->info = &adis16480_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = adis_init(&st->adis, indio_dev, spi, &adis16480_data);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	ret = adis16480_initial_setup(indio_dev);
> +	if (ret)
> +		goto error_cleanup_buffer;
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret)
> +		goto error_stop_device;
> +
> +	adis16480_debugfs_init(indio_dev);
> +
> +	return 0;
> +
> +error_stop_device:
> +	adis16480_stop_device(indio_dev);
> +error_cleanup_buffer:
> +	adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
> +error_free_dev:
> +	iio_device_free(indio_dev);
> +	return ret;
> +}
> +
> +static int __devexit adis16480_remove(struct spi_device *spi)
> +{
> +	struct iio_dev *indio_dev = spi_get_drvdata(spi);
> +	struct adis16480 *st = iio_priv(indio_dev);
> +
> +	iio_device_unregister(indio_dev);
> +	adis16480_stop_device(indio_dev);
> +
> +	adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
> +
> +	iio_device_free(indio_dev);
> +
> +	return 0;
> +}
> +
> +static const struct spi_device_id adis16480_ids[] = {
> +	{ "adis16375", ADIS16375 },
> +	{ "adis16480", ADIS16480 },
> +	{ "adis16485", ADIS16485 },
> +	{ "adis16488", ADIS16488 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(spi, adis16480_ids);
> +
> +static struct spi_driver adis16480_driver = {
> +	.driver = {
> +		.name = "adis16480",
> +		.owner = THIS_MODULE,
> +	},
> +	.id_table = adis16480_ids,
> +	.probe = adis16480_probe,
> +	.remove = __devexit_p(adis16480_remove),
> +};
> +module_spi_driver(adis16480_driver);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_DESCRIPTION("Analog Devices ADIS16480 IMU driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h
> index e82cd08..ff781dc 100644
> --- a/include/linux/iio/imu/adis.h
> +++ b/include/linux/iio/imu/adis.h
> @@ -20,6 +20,8 @@
>  #define ADIS_PAGE_SIZE 0x80
>  #define ADIS_REG_PAGE_ID 0x00
>
> +struct adis;
> +
>  /**
>   * struct adis_data - ADIS chip variant specific data
>   * @read_delay: SPI delay for read operations in us
> @@ -44,6 +46,8 @@ struct adis_data {
>  	const char * const *status_error_msgs;
>  	unsigned int status_error_mask;
>
> +	int (*enable_irq)(struct adis *adis, bool enable);
> +
>  	bool has_paging;
>  };
>
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/7] iio:gyro: Add support for the ADIS16136 gyroscope
  2012-11-20 21:15     ` Lars-Peter Clausen
@ 2012-11-20 21:26       ` Jonathan Cameron
  0 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2012-11-20 21:26 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Jonathan Cameron, linux-iio, drivers

On 11/20/2012 09:15 PM, Lars-Peter Clausen wrote:
> On 11/20/2012 10:05 PM, Jonathan Cameron wrote:
>> On 11/20/2012 01:36 PM, Lars-Peter Clausen wrote:
>>> This patch adds support for the ADIS16133, ADIS16135, ADIS16136 single channel
>>> gyroscopes. The main difference between them is the sensor precision.
>>>
>>> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
>> A nice driver.
>>
>> The only thing I could pick up on was the 'interesting' comments alongside
>> the 3db divisors.
>>
>> Still I just wanted to comment on something really ;)
>>
>> added to togreg branch of iio.git
>>
> 
> just remembered that I wanted to remove the __devinit/__devexit annotations
> before sending this patch out, but then forgot about it. Can you fix this up?
> Otherwise I'll just sent a follow up patch. Same for the adis16480 driver.
Will fixup.  I clean forgot that was going on...
> 
> Thanks,
> - Lars
> 
>>> ---
>>>  drivers/iio/gyro/Kconfig     |   9 +
>>>  drivers/iio/gyro/Makefile    |   1 +
>>>  drivers/iio/gyro/adis16136.c | 581 +++++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 591 insertions(+)
>>>  create mode 100644 drivers/iio/gyro/adis16136.c
>>>
>>> diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
>>> index 21e27e2..48ed148 100644
>>> --- a/drivers/iio/gyro/Kconfig
>>> +++ b/drivers/iio/gyro/Kconfig
>>> @@ -3,6 +3,15 @@
>>>  #
>>>  menu "Digital gyroscope sensors"
>>>  
>>> +config ADIS16136
>>> +	tristate "Analog devices ADIS16136 and similar gyroscopes driver"
>>> +	depends on SPI_MASTER
>>> +	select IIO_ADIS_LIB
>>> +	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
>>> +	help
>>> +	  Say yes here to build support for the Analog Devices ADIS16133, ADIS16135,
>>> +	  ADIS16136 gyroscope devices.
>>> +
>>>  config HID_SENSOR_GYRO_3D
>>>  	depends on HID_SENSOR_HUB
>>>  	select IIO_BUFFER
>>> diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
>>> index 8a895d9..702a058 100644
>>> --- a/drivers/iio/gyro/Makefile
>>> +++ b/drivers/iio/gyro/Makefile
>>> @@ -2,4 +2,5 @@
>>>  # Makefile for industrial I/O gyroscope sensor drivers
>>>  #
>>>  
>>> +obj-$(CONFIG_ADIS16136) += adis16136.o
>>>  obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
>>> diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c
>>> new file mode 100644
>>> index 0000000..277aa74
>>> --- /dev/null
>>> +++ b/drivers/iio/gyro/adis16136.c
>>> @@ -0,0 +1,581 @@
>>> +/*
>>> + * ADIS16133/ADIS16135/ADIS16136 gyroscope driver
>>> + *
>>> + * Copyright 2012 Analog Devices Inc.
>>> + *   Author: Lars-Peter Clausen <lars@metafoo.de>
>>> + *
>>> + * Licensed under the GPL-2.
>>> + */
>>> +
>>> +#include <linux/interrupt.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/device.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/spi/spi.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/sysfs.h>
>>> +#include <linux/module.h>
>>> +
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/sysfs.h>
>>> +#include <linux/iio/buffer.h>
>>> +#include <linux/iio/imu/adis.h>
>>> +
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/debugfs.h>
>>> +
>>> +#define ADIS16136_REG_FLASH_CNT		0x00
>>> +#define ADIS16136_REG_TEMP_OUT		0x02
>>> +#define ADIS16136_REG_GYRO_OUT2		0x04
>>> +#define ADIS16136_REG_GYRO_OUT		0x06
>>> +#define ADIS16136_REG_GYRO_OFF2		0x08
>>> +#define ADIS16136_REG_GYRO_OFF		0x0A
>>> +#define ADIS16136_REG_ALM_MAG1		0x10
>>> +#define ADIS16136_REG_ALM_MAG2		0x12
>>> +#define ADIS16136_REG_ALM_SAMPL1	0x14
>>> +#define ADIS16136_REG_ALM_SAMPL2	0x16
>>> +#define ADIS16136_REG_ALM_CTRL		0x18
>>> +#define ADIS16136_REG_GPIO_CTRL		0x1A
>>> +#define ADIS16136_REG_MSC_CTRL		0x1C
>>> +#define ADIS16136_REG_SMPL_PRD		0x1E
>>> +#define ADIS16136_REG_AVG_CNT		0x20
>>> +#define ADIS16136_REG_DEC_RATE		0x22
>>> +#define ADIS16136_REG_SLP_CTRL		0x24
>>> +#define ADIS16136_REG_DIAG_STAT		0x26
>>> +#define ADIS16136_REG_GLOB_CMD		0x28
>>> +#define ADIS16136_REG_LOT1		0x32
>>> +#define ADIS16136_REG_LOT2		0x34
>>> +#define ADIS16136_REG_LOT3		0x36
>>> +#define ADIS16136_REG_PROD_ID		0x38
>>> +#define ADIS16136_REG_SERIAL_NUM	0x3A
>>> +
>>> +#define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL	2
>>> +#define ADIS16136_DIAG_STAT_SPI_FAIL		3
>>> +#define ADIS16136_DIAG_STAT_SELF_TEST_FAIL	5
>>> +#define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL	6
>>> +
>>> +#define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11)
>>> +#define ADIS16136_MSC_CTRL_SELF_TEST BIT(10)
>>> +
>>> +struct adis16136_chip_info {
>>> +	unsigned int precision;
>>> +	unsigned int fullscale;
>>> +};
>>> +
>>> +struct adis16136 {
>>> +	const struct adis16136_chip_info *chip_info;
>>> +
>>> +	struct adis adis;
>>> +};
>>> +
>>> +#ifdef CONFIG_DEBUG_FS
>>> +
>>> +static ssize_t adis16136_show_serial(struct file *file,
>>> +		char __user *userbuf, size_t count, loff_t *ppos)
>>> +{
>>> +	struct adis16136 *adis16136 = file->private_data;
>>> +	uint16_t lot1, lot2, lot3, serial;
>>> +	char buf[20];
>>> +	size_t len;
>>> +	int ret;
>>> +
>>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SERIAL_NUM,
>>> +		&serial);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	len = snprintf(buf, sizeof(buf), "%.4x%.4x%.4x-%.4x\n", lot1, lot2,
>>> +		lot3, serial);
>>> +
>>> +	return simple_read_from_buffer(userbuf, count, ppos, buf, len);
>>> +}
>>> +
>>> +static const struct file_operations adis16136_serial_fops = {
>>> +	.open = simple_open,
>>> +	.read = adis16136_show_serial,
>>> +	.llseek = default_llseek,
>>> +	.owner = THIS_MODULE,
>>> +};
>>> +
>>> +static int adis16136_show_product_id(void *arg, u64 *val)
>>> +{
>>> +	struct adis16136 *adis16136 = arg;
>>> +	u16 prod_id;
>>> +	int ret;
>>> +
>>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
>>> +		&prod_id);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	*val = prod_id;
>>> +
>>> +	return 0;
>>> +}
>>> +DEFINE_SIMPLE_ATTRIBUTE(adis16136_product_id_fops,
>>> +	adis16136_show_product_id, NULL, "%llu\n");
>>> +
>>> +static int adis16136_show_flash_count(void *arg, u64 *val)
>>> +{
>>> +	struct adis16136 *adis16136 = arg;
>>> +	uint16_t flash_count;
>>> +	int ret;
>>> +
>>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_FLASH_CNT,
>>> +		&flash_count);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	*val = flash_count;
>>> +
>>> +	return 0;
>>> +}
>>> +DEFINE_SIMPLE_ATTRIBUTE(adis16136_flash_count_fops,
>>> +	adis16136_show_flash_count, NULL, "%lld\n");
>>> +
>>> +static int adis16136_debugfs_init(struct iio_dev *indio_dev)
>>> +{
>>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>>> +
>>> +	debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
>>> +		adis16136, &adis16136_serial_fops);
>>> +	debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
>>> +		adis16136, &adis16136_product_id_fops);
>>> +	debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
>>> +		adis16136, &adis16136_flash_count_fops);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +#else
>>> +
>>> +static int adis16136_debugfs_init(struct iio_dev *indio_dev)
>>> +{
>>> +	return 0;
>>> +}
>>> +
>>> +#endif
>>> +
>>> +static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq)
>>> +{
>>> +	unsigned int t;
>>> +
>>> +	t = 32768 / freq;
>>> +	if (t < 0xf)
>>> +		t = 0xf;
>>> +	else if (t > 0xffff)
>>> +		t = 0xffff;
>>> +	else
>>> +		t--;
>>> +
>>> +	return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t);
>>> +}
>>> +
>>> +static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq)
>>> +{
>>> +	uint16_t t;
>>> +	int ret;
>>> +
>>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	*freq = 32768 / (t + 1);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static ssize_t adis16136_write_frequency(struct device *dev,
>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>>> +	long val;
>>> +	int ret;
>>> +
>>> +	ret = kstrtol(buf, 10, &val);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	if (val == 0)
>>> +		return -EINVAL;
>>> +
>>> +	ret = adis16136_set_freq(adis16136, val);
>>> +
>>> +	return ret ? ret : len;
>>> +}
>>> +
>>> +static ssize_t adis16136_read_frequency(struct device *dev,
>>> +	struct device_attribute *attr, char *buf)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>>> +	unsigned int freq;
>>> +	int ret;
>>> +
>>> +	ret = adis16136_get_freq(adis16136, &freq);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	return sprintf(buf, "%d\n", freq);
>>> +}
>>> +
>>> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
>>> +				  adis16136_read_frequency,
>>> +				  adis16136_write_frequency);
>>> +
>> I love the comments.  Useful sounding but I have no idea what
>> they actually mean!
>>> +static const unsigned adis16136_3db_divisors[] = {
>>> +	[0] = 2, /* Special case */
>>> +	[1] = 6,
>>> +	[2] = 12,
>>> +	[3] = 25,
>>> +	[4] = 50,
>>> +	[5] = 100,
>>> +	[6] = 200,
>>> +	[7] = 200, /* Not a valid setting */
>>> +};
>>> +
>>> +static int adis16136_set_filter(struct iio_dev *indio_dev, int val)
>>> +{
>>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>>> +	unsigned int freq;
>>> +	int i, ret;
>>> +
>>> +	ret = adis16136_get_freq(adis16136, &freq);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) {
>>> +		if (freq / adis16136_3db_divisors[i] >= val)
>>> +			break;
>>> +	}
>>> +
>>> +	return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i);
>>> +}
>>> +
>>> +static int adis16136_get_filter(struct iio_dev *indio_dev, int *val)
>>> +{
>>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>>> +	unsigned int freq;
>>> +	uint16_t val16;
>>> +	int ret;
>>> +
>>> +	mutex_lock(&indio_dev->mlock);
>>> +
>>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16);
>>> +	if (ret < 0)
>>> +		goto err_unlock;
>>> +
>>> +	ret = adis16136_get_freq(adis16136, &freq);
>>> +	if (ret < 0)
>>> +		goto err_unlock;
>>> +
>>> +	*val = freq / adis16136_3db_divisors[val16 & 0x07];
>>> +
>>> +err_unlock:
>>> +	mutex_unlock(&indio_dev->mlock);
>>> +
>>> +	return ret ? ret : IIO_VAL_INT;
>>> +}
>>> +
>>> +static int adis16136_read_raw(struct iio_dev *indio_dev,
>>> +	const struct iio_chan_spec *chan, int *val, int *val2, long info)
>>> +{
>>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>>> +	uint32_t val32;
>>> +	int ret;
>>> +
>>> +	switch (info) {
>>> +	case IIO_CHAN_INFO_RAW:
>>> +		return adis_single_conversion(indio_dev, chan, 0, val);
>>> +	case IIO_CHAN_INFO_SCALE:
>>> +		switch (chan->type) {
>>> +		case IIO_ANGL_VEL:
>>> +			*val = adis16136->chip_info->precision;
>>> +			*val2 = (adis16136->chip_info->fullscale << 16);
>>> +			return IIO_VAL_FRACTIONAL;
>>> +		case IIO_TEMP:
>>> +			*val = 10;
>>> +			*val2 = 697000; /* 0.010697 degree Celsius */
>>> +			return IIO_VAL_INT_PLUS_MICRO;
>>> +		default:
>>> +			return -EINVAL;
>>> +		}
>>> +	case IIO_CHAN_INFO_CALIBBIAS:
>>> +		ret = adis_read_reg_32(&adis16136->adis,
>>> +			ADIS16136_REG_GYRO_OFF2, &val32);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +
>>> +		*val = sign_extend32(val32, 31);
>>> +
>>> +		return IIO_VAL_INT;
>>> +	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
>>> +		return adis16136_get_filter(indio_dev, val);
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +}
>>> +
>>> +static int adis16136_write_raw(struct iio_dev *indio_dev,
>>> +	const struct iio_chan_spec *chan, int val, int val2, long info)
>>> +{
>>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>>> +
>>> +	switch (info) {
>>> +	case IIO_CHAN_INFO_CALIBBIAS:
>>> +		return adis_write_reg_32(&adis16136->adis,
>>> +			ADIS16136_REG_GYRO_OFF2, val);
>>> +	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
>>> +		return adis16136_set_filter(indio_dev, val);
>>> +	default:
>>> +		break;
>>> +	}
>>> +
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +enum {
>>> +	ADIS16136_SCAN_GYRO,
>>> +	ADIS16136_SCAN_TEMP,
>>> +};
>>> +
>>> +static const struct iio_chan_spec adis16136_channels[] = {
>>> +	{
>>> +		.type = IIO_ANGL_VEL,
>>> +		.modified = 1,
>>> +		.channel2 = IIO_MOD_X,
>>> +		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
>>> +			IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
>>> +			IIO_CHAN_INFO_SCALE_SHARED_BIT |
>>> +			IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT,
>>> +		.address = ADIS16136_REG_GYRO_OUT2,
>>> +		.scan_index = ADIS16136_SCAN_GYRO,
>>> +		.scan_type = {
>>> +			.sign = 's',
>>> +			.realbits = 32,
>>> +			.storagebits = 32,
>>> +			.endianness = IIO_BE,
>>> +		},
>>> +	}, {
>>> +		.type = IIO_TEMP,
>>> +		.indexed = 1,
>>> +		.channel = 0,
>>> +		.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
>>> +			IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
>>> +		.address = ADIS16136_REG_TEMP_OUT,
>>> +		.scan_index = ADIS16136_SCAN_TEMP,
>>> +		.scan_type = {
>>> +			.sign = 's',
>>> +			.realbits = 16,
>>> +			.storagebits = 16,
>>> +			.endianness = IIO_BE,
>>> +		},
>>> +	},
>>> +	IIO_CHAN_SOFT_TIMESTAMP(2),
>>> +};
>>> +
>>> +static struct attribute *adis16136_attributes[] = {
>>> +	&iio_dev_attr_sampling_frequency.dev_attr.attr,
>>> +	NULL
>>> +};
>>> +
>>> +static const struct attribute_group adis16136_attribute_group = {
>>> +	.attrs = adis16136_attributes,
>>> +};
>>> +
>>> +static const struct iio_info adis16136_info = {
>>> +	.driver_module = THIS_MODULE,
>>> +	.attrs = &adis16136_attribute_group,
>>> +	.read_raw = &adis16136_read_raw,
>>> +	.write_raw = &adis16136_write_raw,
>>> +	.update_scan_mode = adis_update_scan_mode,
>>> +	.debugfs_reg_access = adis_debugfs_reg_access,
>>> +};
>>> +
>>> +static int adis16136_stop_device(struct iio_dev *indio_dev)
>>> +{
>>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	ret = adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SLP_CTRL, 0xff);
>>> +	if (ret)
>>> +		dev_err(&indio_dev->dev,
>>> +			"Could not power down device: %d\n", ret);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int adis16136_initial_setup(struct iio_dev *indio_dev)
>>> +{
>>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>>> +	unsigned int device_id;
>>> +	uint16_t prod_id;
>>> +	int ret;
>>> +
>>> +	ret = adis_initial_startup(&adis16136->adis);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
>>> +		&prod_id);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	sscanf(indio_dev->name, "adis%u\n", &device_id);
>>> +
>>> +	if (prod_id != device_id)
>>> +		dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
>>> +				device_id, prod_id);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const char * const adis16136_status_error_msgs[] = {
>>> +	[ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL] = "Flash update failed",
>>> +	[ADIS16136_DIAG_STAT_SPI_FAIL] = "SPI failure",
>>> +	[ADIS16136_DIAG_STAT_SELF_TEST_FAIL] = "Self test error",
>>> +	[ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error",
>>> +};
>>> +
>>> +static const struct adis_data adis16136_data = {
>>> +	.diag_stat_reg = ADIS16136_REG_DIAG_STAT,
>>> +	.glob_cmd_reg = ADIS16136_REG_GLOB_CMD,
>>> +	.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL,
>>> +
>>> +	.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST,
>>> +	.startup_delay = 80,
>>> +
>>> +	.read_delay = 10,
>>> +	.write_delay = 10,
>>> +
>>> +	.status_error_msgs = adis16136_status_error_msgs,
>>> +	.status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) |
>>> +		BIT(ADIS16136_DIAG_STAT_SPI_FAIL) |
>>> +		BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) |
>>> +		BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL),
>>> +};
>>> +
>>> +enum adis16136_id {
>>> +	ID_ADIS16133,
>>> +	ID_ADIS16135,
>>> +	ID_ADIS16136,
>>> +};
>>> +
>>> +static const struct adis16136_chip_info adis16136_chip_info[] = {
>>> +	[ID_ADIS16133] = {
>>> +		.precision = IIO_DEGREE_TO_RAD(1200),
>>> +		.fullscale = 24000,
>>> +	},
>>> +	[ID_ADIS16135] = {
>>> +		.precision = IIO_DEGREE_TO_RAD(300),
>>> +		.fullscale = 24000,
>>> +	},
>>> +	[ID_ADIS16136] = {
>>> +		.precision = IIO_DEGREE_TO_RAD(450),
>>> +		.fullscale = 24623,
>>> +	},
>>> +};
>>> +
>>> +static int __devinit adis16136_probe(struct spi_device *spi)
>>> +{
>>> +	const struct spi_device_id *id = spi_get_device_id(spi);
>>> +	struct adis16136 *adis16136;
>>> +	struct iio_dev *indio_dev;
>>> +	int ret;
>>> +
>>> +	indio_dev = iio_device_alloc(sizeof(*adis16136));
>>> +	if (indio_dev == NULL)
>>> +		return -ENOMEM;
>>> +
>>> +	spi_set_drvdata(spi, indio_dev);
>>> +
>>> +	adis16136 = iio_priv(indio_dev);
>>> +
>>> +	adis16136->chip_info = &adis16136_chip_info[id->driver_data];
>>> +	indio_dev->dev.parent = &spi->dev;
>>> +	indio_dev->name = spi_get_device_id(spi)->name;
>>> +	indio_dev->channels = adis16136_channels;
>>> +	indio_dev->num_channels = ARRAY_SIZE(adis16136_channels);
>>> +	indio_dev->info = &adis16136_info;
>>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>>> +
>>> +	ret = adis_init(&adis16136->adis, indio_dev, spi, &adis16136_data);
>>> +	if (ret)
>>> +		goto error_free_dev;
>>> +
>>> +	ret = adis_setup_buffer_and_trigger(&adis16136->adis, indio_dev, NULL);
>>> +	if (ret)
>>> +		goto error_free_dev;
>>> +
>>> +	ret = adis16136_initial_setup(indio_dev);
>>> +	if (ret)
>>> +		goto error_cleanup_buffer;
>>> +
>>> +	ret = iio_device_register(indio_dev);
>>> +	if (ret)
>>> +		goto error_stop_device;
>>> +
>>> +	adis16136_debugfs_init(indio_dev);
>>> +
>>> +	return 0;
>>> +
>>> +error_stop_device:
>>> +	adis16136_stop_device(indio_dev);
>>> +error_cleanup_buffer:
>>> +	adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
>>> +error_free_dev:
>>> +	iio_device_free(indio_dev);
>>> +	return ret;
>>> +}
>>> +
>>> +static int __devexit adis16136_remove(struct spi_device *spi)
>>> +{
>>> +	struct iio_dev *indio_dev = spi_get_drvdata(spi);
>>> +	struct adis16136 *adis16136 = iio_priv(indio_dev);
>>> +
>>> +	iio_device_unregister(indio_dev);
>>> +	adis16136_stop_device(indio_dev);
>>> +
>>> +	adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
>>> +
>>> +	iio_device_free(indio_dev);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct spi_device_id adis16136_ids[] = {
>>> +	{ "adis16133", ID_ADIS16133 },
>>> +	{ "adis16135", ID_ADIS16135 },
>>> +	{ "adis16136", ID_ADIS16136 },
>>> +	{ }
>>> +};
>>> +MODULE_DEVICE_TABLE(spi, adis16136_ids);
>>> +
>>> +static struct spi_driver adis16136_driver = {
>>> +	.driver = {
>>> +		.name = "adis16136",
>>> +		.owner = THIS_MODULE,
>>> +	},
>>> +	.id_table = adis16136_ids,
>>> +	.probe = adis16136_probe,
>>> +	.remove = __devexit_p(adis16136_remove),
>>> +};
>>> +module_spi_driver(adis16136_driver);
>>> +
>>> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
>>> +MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver");
>>> +MODULE_LICENSE("GPL v2");
>>>
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2012-11-20 21:26 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-11-20 13:36 [PATCH 1/7] iio:imu:adis: Add debugfs register access support Lars-Peter Clausen
2012-11-20 13:36 ` [PATCH 2/7] iio:imu:adis: Add support for 32bit registers Lars-Peter Clausen
2012-11-20 20:55   ` Jonathan Cameron
2012-11-20 13:36 ` [PATCH 3/7] iio:gyro: Add support for the ADIS16136 gyroscope Lars-Peter Clausen
2012-11-20 21:05   ` Jonathan Cameron
2012-11-20 21:15     ` Lars-Peter Clausen
2012-11-20 21:26       ` Jonathan Cameron
2012-11-20 13:36 ` [PATCH 4/7] iio:imu:adis: Add paging support Lars-Peter Clausen
2012-11-20 21:10   ` Jonathan Cameron
2012-11-20 13:36 ` [PATCH 5/7] iio: Add pressure channel type Lars-Peter Clausen
2012-11-20 21:12   ` Jonathan Cameron
2012-11-20 13:36 ` [PATCH 6/7] iio: Factor out fixed point number parsing into its own function Lars-Peter Clausen
2012-11-20 21:14   ` Jonathan Cameron
2012-11-20 13:36 ` [PATCH 7/7] iio:imu: Add support for the ADIS16480 and similar IMUs Lars-Peter Clausen
2012-11-20 21:24   ` Jonathan Cameron
2012-11-20 19:51 ` [PATCH 1/7] iio:imu:adis: Add debugfs register access support Jonathan Cameron

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).