Jonathan, thanks! On Wed, May 12, 2010 at 3:22 AM, Jonathan Cameron <jic23@xxxxxxxxx> wrote: > From: Barry Song <Barry.Song@xxxxxxxxxx> > > --- > As with previous analog devices drivers this one is pretty close > to a lift from the blackfin tree with fixes for change in sysfs > abi and checkpatch. > > This one introduces a new attribute - angle_raw > which is an on device computed rotation angle from an arbitary > zero. Will need documenting in the abi at some point. > > Incidentally Barry, I'm thinking that one needs angle_scale > as it isn't exactly in standard units. Can be added as > a cleanup patch later. Yes, it is lost. there should be angle_scale later. > Also, I notice you are using a gmail > address for the module author bit. Would you prefer I put > that as the author address in git? that doesn't matter :-) > > For those wondering why this seems to be from Barry, it is > because it is generated by git format-patch and I want to > make sure I credit the writer when sending these on. > > Thanks, > > Jonathan > > drivers/staging/iio/Kconfig | 1 + > drivers/staging/iio/Makefile | 1 + > drivers/staging/iio/gyro/Kconfig | 13 + > drivers/staging/iio/gyro/Makefile | 7 + > drivers/staging/iio/gyro/adis16260.h | 175 +++++++ > drivers/staging/iio/gyro/adis16260_core.c | 661 ++++++++++++++++++++++++++ > drivers/staging/iio/gyro/adis16260_ring.c | 256 ++++++++++ > drivers/staging/iio/gyro/adis16260_trigger.c | 124 +++++ > drivers/staging/iio/gyro/gyro.h | 2 + > 9 files changed, 1240 insertions(+), 0 deletions(-) > > diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig > index ed38ef4..b0e6244 100644 > --- a/drivers/staging/iio/Kconfig > +++ b/drivers/staging/iio/Kconfig > @@ -41,6 +41,7 @@ config IIO_TRIGGER > > source "drivers/staging/iio/accel/Kconfig" > source "drivers/staging/iio/adc/Kconfig" > +source "drivers/staging/iio/gyro/Kconfig" > source "drivers/staging/iio/imu/Kconfig" > source "drivers/staging/iio/light/Kconfig" > > diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile > index 92b81c2..3502b39 100644 > --- a/drivers/staging/iio/Makefile > +++ b/drivers/staging/iio/Makefile > @@ -11,6 +11,7 @@ obj-$(CONFIG_IIO_SW_RING) += ring_sw.o > > obj-y += accel/ > obj-y += adc/ > +obj-y += gyro/ > obj-y += imu/ > obj-y += light/ > > diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig > new file mode 100644 > index 0000000..c404361 > --- /dev/null > +++ b/drivers/staging/iio/gyro/Kconfig > @@ -0,0 +1,13 @@ > +# > +# IIO Digital Gyroscope Sensor drivers configuration > +# > +comment "Digital gyroscope sensors" > + > +config ADIS16260 > + tristate "Analog Devices ADIS16260/5 Digital Gyroscope Sensor SPI driver" > + depends on SPI > + select IIO_TRIGGER if IIO_RING_BUFFER > + select IIO_SW_RING if IIO_RING_BUFFER > + help > + Say yes here to build support for Analog Devices adis16260/5 > + programmable digital gyroscope sensor. > diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile > new file mode 100644 > index 0000000..6d2c547 > --- /dev/null > +++ b/drivers/staging/iio/gyro/Makefile > @@ -0,0 +1,7 @@ > + > +# Makefile for digital gyroscope sensor drivers > +# > + > +adis16260-y := adis16260_core.o > +adis16260-$(CONFIG_IIO_RING_BUFFER) += adis16260_ring.o adis16260_trigger.o > +obj-$(CONFIG_ADIS16260) += adis16260.o > diff --git a/drivers/staging/iio/gyro/adis16260.h b/drivers/staging/iio/gyro/adis16260.h > new file mode 100644 > index 0000000..f19efb4 > --- /dev/null > +++ b/drivers/staging/iio/gyro/adis16260.h > @@ -0,0 +1,175 @@ > +#ifndef SPI_ADIS16260_H_ > +#define SPI_ADIS16260_H_ > + > +#define ADIS16260_STARTUP_DELAY 220 /* ms */ > + > +#define ADIS16260_READ_REG(a) a > +#define ADIS16260_WRITE_REG(a) ((a) | 0x80) > + > +#define ADIS16260_FLASH_CNT 0x00 /* Flash memory write count */ > +#define ADIS16260_SUPPLY_OUT 0x02 /* Power supply measurement */ > +#define ADIS16260_GYRO_OUT 0x04 /* X-axis gyroscope output */ > +#define ADIS16260_AUX_ADC 0x0A /* analog input channel measurement */ > +#define ADIS16260_TEMP_OUT 0x0C /* internal temperature measurement */ > +#define ADIS16260_ANGL_OUT 0x0E /* angle displacement */ > +#define ADIS16260_GYRO_OFF 0x14 /* Calibration, offset/bias adjustment */ > +#define ADIS16260_GYRO_SCALE 0x16 /* Calibration, scale adjustment */ > +#define ADIS16260_ALM_MAG1 0x20 /* Alarm 1 magnitude/polarity setting */ > +#define ADIS16260_ALM_MAG2 0x22 /* Alarm 2 magnitude/polarity setting */ > +#define ADIS16260_ALM_SMPL1 0x24 /* Alarm 1 dynamic rate of change setting */ > +#define ADIS16260_ALM_SMPL2 0x26 /* Alarm 2 dynamic rate of change setting */ > +#define ADIS16260_ALM_CTRL 0x28 /* Alarm control */ > +#define ADIS16260_AUX_DAC 0x30 /* Auxiliary DAC data */ > +#define ADIS16260_GPIO_CTRL 0x32 /* Control, digital I/O line */ > +#define ADIS16260_MSC_CTRL 0x34 /* Control, data ready, self-test settings */ > +#define ADIS16260_SMPL_PRD 0x36 /* Control, internal sample rate */ > +#define ADIS16260_SENS_AVG 0x38 /* Control, dynamic range, filtering */ > +#define ADIS16260_SLP_CNT 0x3A /* Control, sleep mode initiation */ > +#define ADIS16260_DIAG_STAT 0x3C /* Diagnostic, error flags */ > +#define ADIS16260_GLOB_CMD 0x3E /* Control, global commands */ > +#define ADIS16260_LOT_ID1 0x52 /* Lot Identification Code 1 */ > +#define ADIS16260_LOT_ID2 0x54 /* Lot Identification Code 2 */ > +#define ADIS16260_PROD_ID 0x56 /* Product identifier; > + * convert to decimal = 16,265/16,260 */ > +#define ADIS16260_SERIAL_NUM 0x58 /* Serial number */ > + > +#define ADIS16260_OUTPUTS 5 > + > +#define ADIS16260_ERROR_ACTIVE (1<<14) > +#define ADIS16260_NEW_DATA (1<<15) > + > +/* MSC_CTRL */ > +#define ADIS16260_MSC_CTRL_MEM_TEST (1<<11) > +/* Internal self-test enable */ > +#define ADIS16260_MSC_CTRL_INT_SELF_TEST (1<<10) > +#define ADIS16260_MSC_CTRL_NEG_SELF_TEST (1<<9) > +#define ADIS16260_MSC_CTRL_POS_SELF_TEST (1<<8) > +#define ADIS16260_MSC_CTRL_DATA_RDY_EN (1<<2) > +#define ADIS16260_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1) > +#define ADIS16260_MSC_CTRL_DATA_RDY_DIO2 (1<<0) > + > +/* SMPL_PRD */ > +/* Time base (tB): 0 = 1.953 ms, 1 = 60.54 ms */ > +#define ADIS16260_SMPL_PRD_TIME_BASE (1<<7) > +#define ADIS16260_SMPL_PRD_DIV_MASK 0x7F > + > +/* SLP_CNT */ > +#define ADIS16260_SLP_CNT_POWER_OFF 0x80 > + > +/* DIAG_STAT */ > +#define ADIS16260_DIAG_STAT_ALARM2 (1<<9) > +#define ADIS16260_DIAG_STAT_ALARM1 (1<<8) > +#define ADIS16260_DIAG_STAT_FLASH_CHK (1<<6) > +#define ADIS16260_DIAG_STAT_SELF_TEST (1<<5) > +#define ADIS16260_DIAG_STAT_OVERFLOW (1<<4) > +#define ADIS16260_DIAG_STAT_SPI_FAIL (1<<3) > +#define ADIS16260_DIAG_STAT_FLASH_UPT (1<<2) > +#define ADIS16260_DIAG_STAT_POWER_HIGH (1<<1) > +#define ADIS16260_DIAG_STAT_POWER_LOW (1<<0) > + > +/* GLOB_CMD */ > +#define ADIS16260_GLOB_CMD_SW_RESET (1<<7) > +#define ADIS16260_GLOB_CMD_FLASH_UPD (1<<3) > +#define ADIS16260_GLOB_CMD_DAC_LATCH (1<<2) > +#define ADIS16260_GLOB_CMD_FAC_CALIB (1<<1) > +#define ADIS16260_GLOB_CMD_AUTO_NULL (1<<0) > + > +#define ADIS16260_MAX_TX 24 > +#define ADIS16260_MAX_RX 24 > + > +#define ADIS16260_SPI_SLOW (u32)(300 * 1000) > +#define ADIS16260_SPI_BURST (u32)(1000 * 1000) > +#define ADIS16260_SPI_FAST (u32)(2000 * 1000) > + > +/** > + * struct adis16260_state - device instance specific data > + * @us: actual spi_device > + * @work_trigger_to_ring: bh for triggered event handling > + * @work_cont_thresh: CLEAN > + * @inter: used to check if new interrupt has been triggered > + * @last_timestamp: passing timestamp from th to bh of interrupt handler > + * @indio_dev: industrial I/O device structure > + * @trig: data ready trigger registered with iio > + * @tx: transmit buffer > + * @rx: recieve buffer > + * @buf_lock: mutex to protect tx and rx > + **/ > +struct adis16260_state { > + struct spi_device *us; > + struct work_struct work_trigger_to_ring; > + struct iio_work_cont work_cont_thresh; > + s64 last_timestamp; > + struct iio_dev *indio_dev; > + struct iio_trigger *trig; > + u8 *tx; > + u8 *rx; > + struct mutex buf_lock; > +}; > + > +int adis16260_set_irq(struct device *dev, bool enable); > + > +#ifdef CONFIG_IIO_RING_BUFFER > +/* At the moment triggers are only used for ring buffer > + * filling. This may change! > + */ > + > +enum adis16260_scan { > + ADIS16260_SCAN_SUPPLY, > + ADIS16260_SCAN_GYRO, > + ADIS16260_SCAN_AUX_ADC, > + ADIS16260_SCAN_TEMP, > + ADIS16260_SCAN_ANGL, > +}; > + > +void adis16260_remove_trigger(struct iio_dev *indio_dev); > +int adis16260_probe_trigger(struct iio_dev *indio_dev); > + > +ssize_t adis16260_read_data_from_ring(struct device *dev, > + struct device_attribute *attr, > + char *buf); > + > + > +int adis16260_configure_ring(struct iio_dev *indio_dev); > +void adis16260_unconfigure_ring(struct iio_dev *indio_dev); > + > +int adis16260_initialize_ring(struct iio_ring_buffer *ring); > +void adis16260_uninitialize_ring(struct iio_ring_buffer *ring); > +#else /* CONFIG_IIO_RING_BUFFER */ > + > +static inline void adis16260_remove_trigger(struct iio_dev *indio_dev) > +{ > +} > + > +static inline int adis16260_probe_trigger(struct iio_dev *indio_dev) > +{ > + return 0; > +} > + > +static inline ssize_t > +adis16260_read_data_from_ring(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + return 0; > +} > + > +static int adis16260_configure_ring(struct iio_dev *indio_dev) > +{ > + return 0; > +} > + > +static inline void adis16260_unconfigure_ring(struct iio_dev *indio_dev) > +{ > +} > + > +static inline int adis16260_initialize_ring(struct iio_ring_buffer *ring) > +{ > + return 0; > +} > + > +static inline void adis16260_uninitialize_ring(struct iio_ring_buffer *ring) > +{ > +} > + > +#endif /* CONFIG_IIO_RING_BUFFER */ > +#endif /* SPI_ADIS16260_H_ */ > diff --git a/drivers/staging/iio/gyro/adis16260_core.c b/drivers/staging/iio/gyro/adis16260_core.c > new file mode 100644 > index 0000000..c93f4d5 > --- /dev/null > +++ b/drivers/staging/iio/gyro/adis16260_core.c > @@ -0,0 +1,661 @@ > +/* > + * ADIS16260 Programmable Digital Gyroscope Sensor Driver > + * > + * Copyright 2010 Analog Devices Inc. > + * > + * Licensed under the GPL-2 or later. > + */ > + > +#include <linux/interrupt.h> > +#include <linux/irq.h> > +#include <linux/gpio.h> > +#include <linux/delay.h> > +#include <linux/mutex.h> > +#include <linux/device.h> > +#include <linux/kernel.h> > +#include <linux/spi/spi.h> > + > +#include <linux/sysfs.h> > +#include <linux/list.h> > + > +#include "../iio.h" > +#include "../sysfs.h" > +#include "../adc/adc.h" > +#include "gyro.h" > + > +#include "adis16260.h" > + > +#define DRIVER_NAME "adis16260" > + > +static int adis16260_check_status(struct device *dev); > + > +/** > + * adis16260_spi_write_reg_8() - write single byte to a register > + * @dev: device associated with child of actual device (iio_dev or iio_trig) > + * @reg_address: the address of the register to be written > + * @val: the value to write > + **/ > +static int adis16260_spi_write_reg_8(struct device *dev, > + u8 reg_address, > + u8 val) > +{ > + int ret; > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); > + > + mutex_lock(&st->buf_lock); > + st->tx[0] = ADIS16260_WRITE_REG(reg_address); > + st->tx[1] = val; > + > + ret = spi_write(st->us, st->tx, 2); > + mutex_unlock(&st->buf_lock); > + > + return ret; > +} > + > +/** > + * adis16260_spi_write_reg_16() - write 2 bytes to a pair of registers > + * @dev: device associated with child of actual device (iio_dev or iio_trig) > + * @reg_address: the address of the lower of the two registers. Second register > + * is assumed to have address one greater. > + * @val: value to be written > + **/ > +static int adis16260_spi_write_reg_16(struct device *dev, > + u8 lower_reg_address, > + u16 value) > +{ > + int ret; > + struct spi_message msg; > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); > + struct spi_transfer xfers[] = { > + { > + .tx_buf = st->tx, > + .bits_per_word = 8, > + .len = 2, > + .cs_change = 1, > + .delay_usecs = 20, > + }, { > + .tx_buf = st->tx + 2, > + .bits_per_word = 8, > + .len = 2, > + .cs_change = 1, > + .delay_usecs = 20, > + }, > + }; > + > + mutex_lock(&st->buf_lock); > + st->tx[0] = ADIS16260_WRITE_REG(lower_reg_address); > + st->tx[1] = value & 0xFF; > + st->tx[2] = ADIS16260_WRITE_REG(lower_reg_address + 1); > + st->tx[3] = (value >> 8) & 0xFF; > + > + spi_message_init(&msg); > + spi_message_add_tail(&xfers[0], &msg); > + spi_message_add_tail(&xfers[1], &msg); > + ret = spi_sync(st->us, &msg); > + mutex_unlock(&st->buf_lock); > + > + return ret; > +} > + > +/** > + * adis16260_spi_read_reg_16() - read 2 bytes from a 16-bit register > + * @dev: device associated with child of actual device (iio_dev or iio_trig) > + * @reg_address: the address of the lower of the two registers. Second register > + * is assumed to have address one greater. > + * @val: somewhere to pass back the value read > + **/ > +static int adis16260_spi_read_reg_16(struct device *dev, > + u8 lower_reg_address, > + u16 *val) > +{ > + struct spi_message msg; > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); > + int ret; > + struct spi_transfer xfers[] = { > + { > + .tx_buf = st->tx, > + .bits_per_word = 8, > + .len = 2, > + .cs_change = 1, > + .delay_usecs = 30, > + }, { > + .rx_buf = st->rx, > + .bits_per_word = 8, > + .len = 2, > + .cs_change = 1, > + .delay_usecs = 30, > + }, > + }; > + > + mutex_lock(&st->buf_lock); > + st->tx[0] = ADIS16260_READ_REG(lower_reg_address); > + st->tx[1] = 0; > + st->tx[2] = 0; > + st->tx[3] = 0; > + > + spi_message_init(&msg); > + spi_message_add_tail(&xfers[0], &msg); > + spi_message_add_tail(&xfers[1], &msg); > + ret = spi_sync(st->us, &msg); > + if (ret) { > + dev_err(&st->us->dev, > + "problem when reading 16 bit register 0x%02X", > + lower_reg_address); > + goto error_ret; > + } > + *val = (st->rx[0] << 8) | st->rx[1]; > + > +error_ret: > + mutex_unlock(&st->buf_lock); > + return ret; > +} > + > +static ssize_t adis16260_spi_read_signed(struct device *dev, > + struct device_attribute *attr, > + char *buf, > + unsigned bits) > +{ > + int ret; > + s16 val = 0; > + unsigned shift = 16 - bits; > + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); > + > + ret = adis16260_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); > + if (ret) > + return ret; > + > + if (val & ADIS16260_ERROR_ACTIVE) > + adis16260_check_status(dev); > + val = ((s16)(val << shift) >> shift); > + return sprintf(buf, "%d\n", val); > +} > + > +static ssize_t adis16260_read_12bit_unsigned(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + int ret; > + u16 val = 0; > + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); > + > + ret = adis16260_spi_read_reg_16(dev, this_attr->address, &val); > + if (ret) > + return ret; > + > + if (val & ADIS16260_ERROR_ACTIVE) > + adis16260_check_status(dev); > + > + return sprintf(buf, "%u\n", val & 0x0FFF); > +} > + > +static ssize_t adis16260_read_12bit_signed(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + ssize_t ret; > + > + /* Take the iio_dev status lock */ > + mutex_lock(&indio_dev->mlock); > + ret = adis16260_spi_read_signed(dev, attr, buf, 12); > + mutex_unlock(&indio_dev->mlock); > + > + return ret; > +} > + > +static ssize_t adis16260_read_14bit_signed(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + ssize_t ret; > + > + /* Take the iio_dev status lock */ > + mutex_lock(&indio_dev->mlock); > + ret = adis16260_spi_read_signed(dev, attr, buf, 14); > + mutex_unlock(&indio_dev->mlock); > + > + return ret; > +} > + > +static ssize_t adis16260_write_16bit(struct device *dev, > + struct device_attribute *attr, > + const char *buf, > + size_t len) > +{ > + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); > + int ret; > + long val; > + > + ret = strict_strtol(buf, 10, &val); > + if (ret) > + goto error_ret; > + ret = adis16260_spi_write_reg_16(dev, this_attr->address, val); > + > +error_ret: > + return ret ? ret : len; > +} > + > +static ssize_t adis16260_read_frequency(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + int ret, len = 0; > + u16 t; > + int sps; > + ret = adis16260_spi_read_reg_16(dev, > + ADIS16260_SMPL_PRD, > + &t); > + if (ret) > + return ret; > + sps = (t & ADIS16260_SMPL_PRD_TIME_BASE) ? 66 : 2048; > + sps /= (t & ADIS16260_SMPL_PRD_DIV_MASK) + 1; > + len = sprintf(buf, "%d SPS\n", sps); > + return len; > +} > + > +static ssize_t adis16260_write_frequency(struct device *dev, > + struct device_attribute *attr, > + const char *buf, > + size_t len) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); > + long val; > + int ret; > + u8 t; > + > + ret = strict_strtol(buf, 10, &val); > + if (ret) > + return ret; > + > + mutex_lock(&indio_dev->mlock); > + > + t = (2048 / val); > + if (t > 0) > + t--; > + t &= ADIS16260_SMPL_PRD_DIV_MASK; > + if ((t & ADIS16260_SMPL_PRD_DIV_MASK) >= 0x0A) > + st->us->max_speed_hz = ADIS16260_SPI_SLOW; > + else > + st->us->max_speed_hz = ADIS16260_SPI_FAST; > + > + ret = adis16260_spi_write_reg_8(dev, > + ADIS16260_SMPL_PRD, > + t); > + > + mutex_unlock(&indio_dev->mlock); > + > + return ret ? ret : len; > +} > + > +static int adis16260_reset(struct device *dev) > +{ > + int ret; > + ret = adis16260_spi_write_reg_8(dev, > + ADIS16260_GLOB_CMD, > + ADIS16260_GLOB_CMD_SW_RESET); > + if (ret) > + dev_err(dev, "problem resetting device"); > + > + return ret; > +} > + > +static ssize_t adis16260_write_reset(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + if (len < 1) > + return -EINVAL; > + switch (buf[0]) { > + case '1': > + case 'y': > + case 'Y': > + return adis16260_reset(dev); > + } > + return -EINVAL; > +} > + > +int adis16260_set_irq(struct device *dev, bool enable) > +{ > + int ret; > + u16 msc; > + ret = adis16260_spi_read_reg_16(dev, ADIS16260_MSC_CTRL, &msc); > + if (ret) > + goto error_ret; > + > + msc |= ADIS16260_MSC_CTRL_DATA_RDY_POL_HIGH; > + if (enable) > + msc |= ADIS16260_MSC_CTRL_DATA_RDY_EN; > + else > + msc &= ~ADIS16260_MSC_CTRL_DATA_RDY_EN; > + > + ret = adis16260_spi_write_reg_16(dev, ADIS16260_MSC_CTRL, msc); > + if (ret) > + goto error_ret; > + > +error_ret: > + return ret; > +} > + > +/* Power down the device */ > +static int adis16260_stop_device(struct device *dev) > +{ > + int ret; > + u16 val = ADIS16260_SLP_CNT_POWER_OFF; > + > + ret = adis16260_spi_write_reg_16(dev, ADIS16260_SLP_CNT, val); > + if (ret) > + dev_err(dev, "problem with turning device off: SLP_CNT"); > + > + return ret; > +} > + > +static int adis16260_self_test(struct device *dev) > +{ > + int ret; > + ret = adis16260_spi_write_reg_16(dev, > + ADIS16260_MSC_CTRL, > + ADIS16260_MSC_CTRL_MEM_TEST); > + if (ret) { > + dev_err(dev, "problem starting self test"); > + goto err_ret; > + } > + > + adis16260_check_status(dev); > + > +err_ret: > + return ret; > +} > + > +static int adis16260_check_status(struct device *dev) > +{ > + u16 status; > + int ret; > + > + ret = adis16260_spi_read_reg_16(dev, ADIS16260_DIAG_STAT, &status); > + > + if (ret < 0) { > + dev_err(dev, "Reading status failed\n"); > + goto error_ret; > + } > + ret = status & 0x7F; > + if (status & ADIS16260_DIAG_STAT_FLASH_CHK) > + dev_err(dev, "Flash checksum error\n"); > + if (status & ADIS16260_DIAG_STAT_SELF_TEST) > + dev_err(dev, "Self test error\n"); > + if (status & ADIS16260_DIAG_STAT_OVERFLOW) > + dev_err(dev, "Sensor overrange\n"); > + if (status & ADIS16260_DIAG_STAT_SPI_FAIL) > + dev_err(dev, "SPI failure\n"); > + if (status & ADIS16260_DIAG_STAT_FLASH_UPT) > + dev_err(dev, "Flash update failed\n"); > + if (status & ADIS16260_DIAG_STAT_POWER_HIGH) > + dev_err(dev, "Power supply above 5.25V\n"); > + if (status & ADIS16260_DIAG_STAT_POWER_LOW) > + dev_err(dev, "Power supply below 4.75V\n"); > + > +error_ret: > + return ret; > +} > + > +static int adis16260_initial_setup(struct adis16260_state *st) > +{ > + int ret; > + struct device *dev = &st->indio_dev->dev; > + > + /* Disable IRQ */ > + ret = adis16260_set_irq(dev, false); > + if (ret) { > + dev_err(dev, "disable irq failed"); > + goto err_ret; > + } > + > + /* Do self test */ > + ret = adis16260_self_test(dev); > + if (ret) { > + dev_err(dev, "self test failure"); > + goto err_ret; > + } > + > + /* Read status register to check the result */ > + ret = adis16260_check_status(dev); > + if (ret) { > + adis16260_reset(dev); > + dev_err(dev, "device not playing ball -> reset"); > + msleep(ADIS16260_STARTUP_DELAY); > + ret = adis16260_check_status(dev); > + if (ret) { > + dev_err(dev, "giving up"); > + goto err_ret; > + } > + } > + > + printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n", > + st->us->chip_select, st->us->irq); > + > +err_ret: > + return ret; > +} > + > +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, > + adis16260_read_12bit_unsigned, > + ADIS16260_SUPPLY_OUT); > +static IIO_CONST_ATTR(in_supply_scale, "0.0018315"); > + > +static IIO_DEV_ATTR_GYRO(adis16260_read_14bit_signed, > + ADIS16260_GYRO_OUT); > +static IIO_DEV_ATTR_GYRO_SCALE(S_IWUSR | S_IRUGO, > + adis16260_read_14bit_signed, > + adis16260_write_16bit, > + ADIS16260_GYRO_SCALE); > +static IIO_DEV_ATTR_GYRO_OFFSET(S_IWUSR | S_IRUGO, > + adis16260_read_12bit_signed, > + adis16260_write_16bit, > + ADIS16260_GYRO_OFF); > + > +static IIO_DEV_ATTR_TEMP_RAW(adis16260_read_12bit_unsigned); > +static IIO_CONST_ATTR(temp_offset, "25"); > +static IIO_CONST_ATTR(temp_scale, "0.1453"); > + > +static IIO_DEV_ATTR_IN_RAW(0, adis16260_read_12bit_unsigned, > + ADIS16260_AUX_ADC); > +static IIO_CONST_ATTR(in0_scale, "0.0006105"); > + > +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, > + adis16260_read_frequency, > + adis16260_write_frequency); > +static IIO_DEV_ATTR_ANGL(adis16260_read_14bit_signed, > + ADIS16260_ANGL_OUT); > + > +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16260_write_reset, 0); > + > +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("256 2048"); > + > +static IIO_CONST_ATTR(name, "adis16260"); > + > +static struct attribute *adis16260_event_attributes[] = { > + NULL > +}; > + > +static struct attribute_group adis16260_event_attribute_group = { > + .attrs = adis16260_event_attributes, > +}; > + > +static struct attribute *adis16260_attributes[] = { > + &iio_dev_attr_in_supply_raw.dev_attr.attr, > + &iio_const_attr_in_supply_scale.dev_attr.attr, > + &iio_dev_attr_gyro_raw.dev_attr.attr, > + &iio_dev_attr_gyro_scale.dev_attr.attr, > + &iio_dev_attr_gyro_offset.dev_attr.attr, > + &iio_dev_attr_angl_raw.dev_attr.attr, > + &iio_dev_attr_temp_raw.dev_attr.attr, > + &iio_const_attr_temp_offset.dev_attr.attr, > + &iio_const_attr_temp_scale.dev_attr.attr, > + &iio_dev_attr_in0_raw.dev_attr.attr, > + &iio_const_attr_in0_scale.dev_attr.attr, > + &iio_dev_attr_sampling_frequency.dev_attr.attr, > + &iio_const_attr_available_sampling_frequency.dev_attr.attr, > + &iio_dev_attr_reset.dev_attr.attr, > + &iio_const_attr_name.dev_attr.attr, > + NULL > +}; > + > +static const struct attribute_group adis16260_attribute_group = { > + .attrs = adis16260_attributes, > +}; > + > +static int __devinit adis16260_probe(struct spi_device *spi) > +{ > + int ret, regdone = 0; > + struct adis16260_state *st = kzalloc(sizeof *st, GFP_KERNEL); > + if (!st) { > + ret = -ENOMEM; > + goto error_ret; > + } > + /* this is only used for removal purposes */ > + spi_set_drvdata(spi, st); > + > + /* Allocate the comms buffers */ > + st->rx = kzalloc(sizeof(*st->rx)*ADIS16260_MAX_RX, GFP_KERNEL); > + if (st->rx == NULL) { > + ret = -ENOMEM; > + goto error_free_st; > + } > + st->tx = kzalloc(sizeof(*st->tx)*ADIS16260_MAX_TX, GFP_KERNEL); > + if (st->tx == NULL) { > + ret = -ENOMEM; > + goto error_free_rx; > + } > + st->us = spi; > + mutex_init(&st->buf_lock); > + /* setup the industrialio driver allocated elements */ > + st->indio_dev = iio_allocate_device(); > + if (st->indio_dev == NULL) { > + ret = -ENOMEM; > + goto error_free_tx; > + } > + > + st->indio_dev->dev.parent = &spi->dev; > + st->indio_dev->num_interrupt_lines = 1; > + st->indio_dev->event_attrs = &adis16260_event_attribute_group; > + st->indio_dev->attrs = &adis16260_attribute_group; > + st->indio_dev->dev_data = (void *)(st); > + st->indio_dev->driver_module = THIS_MODULE; > + st->indio_dev->modes = INDIO_DIRECT_MODE; > + > + ret = adis16260_configure_ring(st->indio_dev); > + if (ret) > + goto error_free_dev; > + > + ret = iio_device_register(st->indio_dev); > + if (ret) > + goto error_unreg_ring_funcs; > + regdone = 1; > + > + ret = adis16260_initialize_ring(st->indio_dev->ring); > + if (ret) { > + printk(KERN_ERR "failed to initialize the ring\n"); > + goto error_unreg_ring_funcs; > + } > + > + if (spi->irq) { > + ret = iio_register_interrupt_line(spi->irq, > + st->indio_dev, > + 0, > + IRQF_TRIGGER_RISING, > + "adis16260"); > + if (ret) > + goto error_uninitialize_ring; > + > + ret = adis16260_probe_trigger(st->indio_dev); > + if (ret) > + goto error_unregister_line; > + } > + > + /* Get the device into a sane initial state */ > + ret = adis16260_initial_setup(st); > + if (ret) > + goto error_remove_trigger; > + return 0; > + > +error_remove_trigger: > + adis16260_remove_trigger(st->indio_dev); > +error_unregister_line: > + if (spi->irq) > + iio_unregister_interrupt_line(st->indio_dev, 0); > +error_uninitialize_ring: > + adis16260_uninitialize_ring(st->indio_dev->ring); > +error_unreg_ring_funcs: > + adis16260_unconfigure_ring(st->indio_dev); > +error_free_dev: > + if (regdone) > + iio_device_unregister(st->indio_dev); > + else > + iio_free_device(st->indio_dev); > +error_free_tx: > + kfree(st->tx); > +error_free_rx: > + kfree(st->rx); > +error_free_st: > + kfree(st); > +error_ret: > + return ret; > +} > + > +static int adis16260_remove(struct spi_device *spi) > +{ > + int ret; > + struct adis16260_state *st = spi_get_drvdata(spi); > + struct iio_dev *indio_dev = st->indio_dev; > + > + ret = adis16260_stop_device(&(indio_dev->dev)); > + if (ret) > + goto err_ret; > + > + flush_scheduled_work(); > + > + adis16260_remove_trigger(indio_dev); > + if (spi->irq) > + iio_unregister_interrupt_line(indio_dev, 0); > + > + adis16260_uninitialize_ring(indio_dev->ring); > + iio_device_unregister(indio_dev); > + adis16260_unconfigure_ring(indio_dev); > + kfree(st->tx); > + kfree(st->rx); > + kfree(st); > + > + return 0; > + > +err_ret: > + return ret; > +} > + > +static struct spi_driver adis16260_driver = { > + .driver = { > + .name = "adis16260", > + .owner = THIS_MODULE, > + }, > + .probe = adis16260_probe, > + .remove = __devexit_p(adis16260_remove), > +}; > + > +static __init int adis16260_init(void) > +{ > + return spi_register_driver(&adis16260_driver); > +} > +module_init(adis16260_init); > + > +static __exit void adis16260_exit(void) > +{ > + spi_unregister_driver(&adis16260_driver); > +} > +module_exit(adis16260_exit); > + > +MODULE_AUTHOR("Barry Song <21cnbao@xxxxxxxxx>"); > +MODULE_DESCRIPTION("Analog Devices ADIS16260/5 Digital Gyroscope Sensor"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/staging/iio/gyro/adis16260_ring.c b/drivers/staging/iio/gyro/adis16260_ring.c > new file mode 100644 > index 0000000..4c4390c > --- /dev/null > +++ b/drivers/staging/iio/gyro/adis16260_ring.c > @@ -0,0 +1,256 @@ > +#include <linux/interrupt.h> > +#include <linux/irq.h> > +#include <linux/gpio.h> > +#include <linux/workqueue.h> > +#include <linux/mutex.h> > +#include <linux/device.h> > +#include <linux/kernel.h> > +#include <linux/spi/spi.h> > +#include <linux/sysfs.h> > +#include <linux/list.h> > + > +#include "../iio.h" > +#include "../sysfs.h" > +#include "../ring_sw.h" > +#include "../accel/accel.h" > +#include "../trigger.h" > +#include "adis16260.h" > + > +/** > + * combine_8_to_16() utility function to munge to u8s into u16 > + **/ > +static inline u16 combine_8_to_16(u8 lower, u8 upper) > +{ > + u16 _lower = lower; > + u16 _upper = upper; > + return _lower | (_upper << 8); > +} > + > +static IIO_SCAN_EL_C(supply, ADIS16260_SCAN_SUPPLY, IIO_UNSIGNED(12), > + ADIS16260_SUPPLY_OUT, NULL); > +static IIO_SCAN_EL_C(gyro, ADIS16260_SCAN_GYRO, IIO_SIGNED(14), > + ADIS16260_GYRO_OUT, NULL); > +static IIO_SCAN_EL_C(aux_adc, ADIS16260_SCAN_AUX_ADC, IIO_SIGNED(14), > + ADIS16260_AUX_ADC, NULL); > +static IIO_SCAN_EL_C(temp, ADIS16260_SCAN_TEMP, IIO_UNSIGNED(12), > + ADIS16260_TEMP_OUT, NULL); > +static IIO_SCAN_EL_C(angl, ADIS16260_SCAN_ANGL, IIO_UNSIGNED(12), > + ADIS16260_ANGL_OUT, NULL); > + > +static IIO_SCAN_EL_TIMESTAMP(5); > + > +static struct attribute *adis16260_scan_el_attrs[] = { > + &iio_scan_el_supply.dev_attr.attr, > + &iio_scan_el_gyro.dev_attr.attr, > + &iio_scan_el_aux_adc.dev_attr.attr, > + &iio_scan_el_temp.dev_attr.attr, > + &iio_scan_el_angl.dev_attr.attr, > + &iio_scan_el_timestamp.dev_attr.attr, > + NULL, > +}; > + > +static struct attribute_group adis16260_scan_el_group = { > + .attrs = adis16260_scan_el_attrs, > + .name = "scan_elements", > +}; > + > +/** > + * adis16260_poll_func_th() top half interrupt handler called by trigger > + * @private_data: iio_dev > + **/ > +static void adis16260_poll_func_th(struct iio_dev *indio_dev) > +{ > + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); > + st->last_timestamp = indio_dev->trig->timestamp; > + schedule_work(&st->work_trigger_to_ring); > +} > + > +/** > + * adis16260_read_ring_data() read data registers which will be placed into ring > + * @dev: device associated with child of actual device (iio_dev or iio_trig) > + * @rx: somewhere to pass back the value read > + **/ > +static int adis16260_read_ring_data(struct device *dev, u8 *rx) > +{ > + struct spi_message msg; > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct adis16260_state *st = iio_dev_get_devdata(indio_dev); > + struct spi_transfer xfers[ADIS16260_OUTPUTS + 1]; > + int ret; > + int i; > + > + mutex_lock(&st->buf_lock); > + > + spi_message_init(&msg); > + > + memset(xfers, 0, sizeof(xfers)); > + for (i = 0; i <= ADIS16260_OUTPUTS; i++) { > + xfers[i].bits_per_word = 8; > + xfers[i].cs_change = 1; > + xfers[i].len = 2; > + xfers[i].delay_usecs = 30; > + xfers[i].tx_buf = st->tx + 2 * i; > + if (i < 2) /* SUPPLY_OUT:0x02 GYRO_OUT:0x04 */ > + st->tx[2 * i] > + = ADIS16260_READ_REG(ADIS16260_SUPPLY_OUT > + + 2 * i); > + else /* 0x06 to 0x09 is reserved */ > + st->tx[2 * i] > + = ADIS16260_READ_REG(ADIS16260_SUPPLY_OUT > + + 2 * i + 4); > + st->tx[2 * i + 1] = 0; > + if (i >= 1) > + xfers[i].rx_buf = rx + 2 * (i - 1); > + spi_message_add_tail(&xfers[i], &msg); > + } > + > + ret = spi_sync(st->us, &msg); > + if (ret) > + dev_err(&st->us->dev, "problem when burst reading"); > + > + mutex_unlock(&st->buf_lock); > + > + return ret; > +} > + > + > +static void adis16260_trigger_bh_to_ring(struct work_struct *work_s) > +{ > + struct adis16260_state *st > + = container_of(work_s, struct adis16260_state, > + work_trigger_to_ring); > + > + int i = 0; > + s16 *data; > + size_t datasize = st->indio_dev > + ->ring->access.get_bpd(st->indio_dev->ring); > + > + data = kmalloc(datasize , GFP_KERNEL); > + if (data == NULL) { > + dev_err(&st->us->dev, "memory alloc failed in ring bh"); > + return; > + } > + > + if (st->indio_dev->scan_count) > + if (adis16260_read_ring_data(&st->indio_dev->dev, st->rx) >= 0) > + for (; i < st->indio_dev->scan_count; i++) { > + data[i] = combine_8_to_16(st->rx[i*2+1], > + st->rx[i*2]); > + } > + > + /* Guaranteed to be aligned with 8 byte boundary */ > + if (st->indio_dev->scan_timestamp) > + *((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp; > + > + st->indio_dev->ring->access.store_to(st->indio_dev->ring, > + (u8 *)data, > + st->last_timestamp); > + > + iio_trigger_notify_done(st->indio_dev->trig); > + kfree(data); > + > + return; > +} > + > +static int adis16260_data_rdy_ring_preenable(struct iio_dev *indio_dev) > +{ > + size_t size; > + dev_dbg(&indio_dev->dev, "%s\n", __func__); > + /* Check if there are any scan elements enabled, if not fail*/ > + if (!(indio_dev->scan_count || indio_dev->scan_timestamp)) > + return -EINVAL; > + > + if (indio_dev->ring->access.set_bpd) { > + if (indio_dev->scan_timestamp) > + if (indio_dev->scan_count) > + /* Timestamp (aligned s64) and data */ > + size = (((indio_dev->scan_count * sizeof(s16)) > + + sizeof(s64) - 1) > + & ~(sizeof(s64) - 1)) > + + sizeof(s64); > + else /* Timestamp only */ > + size = sizeof(s64); > + else /* Data only */ > + size = indio_dev->scan_count*sizeof(s16); > + indio_dev->ring->access.set_bpd(indio_dev->ring, size); > + } > + > + return 0; > +} > + > +static int adis16260_data_rdy_ring_postenable(struct iio_dev *indio_dev) > +{ > + return indio_dev->trig > + ? iio_trigger_attach_poll_func(indio_dev->trig, > + indio_dev->pollfunc) > + : 0; > +} > + > +static int adis16260_data_rdy_ring_predisable(struct iio_dev *indio_dev) > +{ > + return indio_dev->trig > + ? iio_trigger_dettach_poll_func(indio_dev->trig, > + indio_dev->pollfunc) > + : 0; > +} > + > +void adis16260_unconfigure_ring(struct iio_dev *indio_dev) > +{ > + kfree(indio_dev->pollfunc); > + iio_sw_rb_free(indio_dev->ring); > +} > + > +int adis16260_configure_ring(struct iio_dev *indio_dev) > +{ > + int ret = 0; > + struct adis16260_state *st = indio_dev->dev_data; > + struct iio_ring_buffer *ring; > + INIT_WORK(&st->work_trigger_to_ring, adis16260_trigger_bh_to_ring); > + /* Set default scan mode */ > + > + iio_scan_mask_set(indio_dev, iio_scan_el_supply.number); > + iio_scan_mask_set(indio_dev, iio_scan_el_gyro.number); > + iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number); > + iio_scan_mask_set(indio_dev, iio_scan_el_temp.number); > + iio_scan_mask_set(indio_dev, iio_scan_el_angl.number); > + indio_dev->scan_timestamp = true; > + > + indio_dev->scan_el_attrs = &adis16260_scan_el_group; > + > + ring = iio_sw_rb_allocate(indio_dev); > + if (!ring) { > + ret = -ENOMEM; > + return ret; > + } > + indio_dev->ring = ring; > + /* Effectively select the ring buffer implementation */ > + iio_ring_sw_register_funcs(&ring->access); > + ring->preenable = &adis16260_data_rdy_ring_preenable; > + ring->postenable = &adis16260_data_rdy_ring_postenable; > + ring->predisable = &adis16260_data_rdy_ring_predisable; > + ring->owner = THIS_MODULE; > + > + indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); > + if (indio_dev->pollfunc == NULL) { > + ret = -ENOMEM; > + goto error_iio_sw_rb_free;; > + } > + indio_dev->pollfunc->poll_func_main = &adis16260_poll_func_th; > + indio_dev->pollfunc->private_data = indio_dev; > + indio_dev->modes |= INDIO_RING_TRIGGERED; > + return 0; > + > +error_iio_sw_rb_free: > + iio_sw_rb_free(indio_dev->ring); > + return ret; > +} > + > +int adis16260_initialize_ring(struct iio_ring_buffer *ring) > +{ > + return iio_ring_buffer_register(ring, 0); > +} > + > +void adis16260_uninitialize_ring(struct iio_ring_buffer *ring) > +{ > + iio_ring_buffer_unregister(ring); > +} > diff --git a/drivers/staging/iio/gyro/adis16260_trigger.c b/drivers/staging/iio/gyro/adis16260_trigger.c > new file mode 100644 > index 0000000..b3c5659 > --- /dev/null > +++ b/drivers/staging/iio/gyro/adis16260_trigger.c > @@ -0,0 +1,124 @@ > +#include <linux/interrupt.h> > +#include <linux/irq.h> > +#include <linux/mutex.h> > +#include <linux/device.h> > +#include <linux/kernel.h> > +#include <linux/sysfs.h> > +#include <linux/list.h> > +#include <linux/spi/spi.h> > + > +#include "../iio.h" > +#include "../sysfs.h" > +#include "../trigger.h" > +#include "adis16260.h" > + > +/** > + * adis16260_data_rdy_trig_poll() the event handler for the data rdy trig > + **/ > +static int adis16260_data_rdy_trig_poll(struct iio_dev *dev_info, > + int index, > + s64 timestamp, > + int no_test) > +{ > + struct adis16260_state *st = iio_dev_get_devdata(dev_info); > + struct iio_trigger *trig = st->trig; > + > + trig->timestamp = timestamp; > + iio_trigger_poll(trig); > + > + return IRQ_HANDLED; > +} > + > +IIO_EVENT_SH(data_rdy_trig, &adis16260_data_rdy_trig_poll); > + > +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); > + > +static struct attribute *adis16260_trigger_attrs[] = { > + &dev_attr_name.attr, > + NULL, > +}; > + > +static const struct attribute_group adis16260_trigger_attr_group = { > + .attrs = adis16260_trigger_attrs, > +}; > + > +/** > + * adis16260_data_rdy_trigger_set_state() set datardy interrupt state > + **/ > +static int adis16260_data_rdy_trigger_set_state(struct iio_trigger *trig, > + bool state) > +{ > + struct adis16260_state *st = trig->private_data; > + struct iio_dev *indio_dev = st->indio_dev; > + int ret = 0; > + > + dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state); > + ret = adis16260_set_irq(&st->indio_dev->dev, state); > + if (state == false) { > + iio_remove_event_from_list(&iio_event_data_rdy_trig, > + &indio_dev->interrupts[0] > + ->ev_list); > + flush_scheduled_work(); > + } else { > + iio_add_event_to_list(&iio_event_data_rdy_trig, > + &indio_dev->interrupts[0]->ev_list); > + } > + return ret; > +} > + > +/** > + * adis16260_trig_try_reen() try renabling irq for data rdy trigger > + * @trig: the datardy trigger > + **/ > +static int adis16260_trig_try_reen(struct iio_trigger *trig) > +{ > + struct adis16260_state *st = trig->private_data; > + enable_irq(st->us->irq); > + return 0; > +} > + > +int adis16260_probe_trigger(struct iio_dev *indio_dev) > +{ > + int ret; > + struct adis16260_state *st = indio_dev->dev_data; > + > + st->trig = iio_allocate_trigger(); > + st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); > + if (!st->trig->name) { > + ret = -ENOMEM; > + goto error_free_trig; > + } > + snprintf((char *)st->trig->name, > + IIO_TRIGGER_NAME_LENGTH, > + "adis16260-dev%d", indio_dev->id); > + st->trig->dev.parent = &st->us->dev; > + st->trig->owner = THIS_MODULE; > + st->trig->private_data = st; > + st->trig->set_trigger_state = &adis16260_data_rdy_trigger_set_state; > + st->trig->try_reenable = &adis16260_trig_try_reen; > + st->trig->control_attrs = &adis16260_trigger_attr_group; > + ret = iio_trigger_register(st->trig); > + > + /* select default trigger */ > + indio_dev->trig = st->trig; > + if (ret) > + goto error_free_trig_name; > + > + return 0; > + > +error_free_trig_name: > + kfree(st->trig->name); > +error_free_trig: > + iio_free_trigger(st->trig); > + > + return ret; > +} > + > +void adis16260_remove_trigger(struct iio_dev *indio_dev) > +{ > + struct adis16260_state *state = indio_dev->dev_data; > + > + iio_trigger_unregister(state->trig); > + kfree(state->trig->name); > + iio_free_trigger(state->trig); > +} > diff --git a/drivers/staging/iio/gyro/gyro.h b/drivers/staging/iio/gyro/gyro.h > index 16f6ffa..f68edab 100644 > --- a/drivers/staging/iio/gyro/gyro.h > +++ b/drivers/staging/iio/gyro/gyro.h > @@ -39,3 +39,5 @@ > #define IIO_DEV_ATTR_GYRO_Z(_show, _addr) \ > IIO_DEVICE_ATTR(gyro_z_raw, S_IRUGO, _show, NULL, _addr) > > +#define IIO_DEV_ATTR_ANGL(_show, _addr) \ > + IIO_DEVICE_ATTR(angl_raw, S_IRUGO, _show, NULL, _addr) > -- > 1.7.0.4 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html