Messages in this thread | | | Date | Fri, 11 Nov 2011 11:41:34 +0100 | From | Michael Hennerich <> | Subject | Re: [PATCH 5/6] IIO:imu:adis16400 partial move from staging. |
| |
On 11/07/2011 03:52 PM, jic23@cam.ac.uk wrote: > From: Manuel Stahl<manuel.stahl@iis.fraunhofer.de> > > This driver made use of a few convenient corners of scan_type so > I have lifted those elements into iio_chan_spec structure for now > (in initial iio core support patch). > > Signed-off-by: Jonathan Cameron<jic23@cam.ac.uk> Acked-by: Michael Hennerich <michael.hennerich@analog.com> > --- > drivers/iio/Kconfig | 1 + > drivers/iio/Makefile | 1 + > drivers/iio/imu/Kconfig | 12 + > drivers/iio/imu/Makefile | 6 + > drivers/iio/imu/adis16400.h | 180 ++++++ > drivers/iio/imu/adis16400_core.c | 1142 ++++++++++++++++++++++++++++++++++++++ > 6 files changed, 1342 insertions(+), 0 deletions(-) > > diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig > index 5dcd9e5..308bc97 100644 > --- a/drivers/iio/Kconfig > +++ b/drivers/iio/Kconfig > @@ -12,6 +12,7 @@ menuconfig IIO > if IIO > > source "drivers/iio/adc/Kconfig" > +source "drivers/iio/imu/Kconfig" > source "drivers/iio/light/Kconfig" > > endif # IIO > diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile > index 0e59946..db3c426 100644 > --- a/drivers/iio/Makefile > +++ b/drivers/iio/Makefile > @@ -6,4 +6,5 @@ obj-$(CONFIG_IIO) += iio.o > industrialio-y := core.o > > obj-y += adc/ > +obj-y += imu/ > obj-y += light/ > diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig > new file mode 100644 > index 0000000..fe3c28c > --- /dev/null > +++ b/drivers/iio/imu/Kconfig > @@ -0,0 +1,12 @@ > +menu "Inertial measurement units (IMUs)" > + > +config IIO_ADIS16400 > + tristate "Analog Devices ADIS16400 and similar IMU SPI driver" > + depends on SPI > + help > + Say yes here to build support for Analog Devices adis16300, adis16350, > + adis16354, adis16355, adis16360, adis16362, adis16364, adis16365, > + adis16400 and adis16405 triaxial inertial sensors (adis16400 series > + also have magnetometers). > + > +endmenu > diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile > new file mode 100644 > index 0000000..eb4d31e > --- /dev/null > +++ b/drivers/iio/imu/Makefile > @@ -0,0 +1,6 @@ > +# > +# Makefile for IIO IMUs > +# > + > +iio_adis16400-y := adis16400_core.o > +obj-$(CONFIG_IIO_ADIS16400) += iio_adis16400.o > \ No newline at end of file > diff --git a/drivers/iio/imu/adis16400.h b/drivers/iio/imu/adis16400.h > new file mode 100644 > index 0000000..78377ea > --- /dev/null > +++ b/drivers/iio/imu/adis16400.h > @@ -0,0 +1,180 @@ > +/* > + * adis16400.h support Analog Devices ADIS16400 > + * 3d 18g accelerometers, > + * 3d gyroscopes, > + * 3d 2.5gauss magnetometers via SPI > + * > + * Copyright (c) 2009 Manuel Stahl<manuel.stahl@iis.fraunhofer.de> > + * Copyright (c) 2007 Jonathan Cameron<jic23@cam.ac.uk> > + * > + * Loosely based upon lis3l02dq.h > + * > + * 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. > + */ > + > +#ifndef SPI_ADIS16400_H_ > +#define SPI_ADIS16400_H_ > + > +#define ADIS16400_STARTUP_DELAY 290 /* ms */ > +#define ADIS16400_MTEST_DELAY 90 /* ms */ > + > +#define ADIS16400_READ_REG(a) a > +#define ADIS16400_WRITE_REG(a) ((a) | 0x80) > + > +#define ADIS16400_FLASH_CNT 0x00 /* Flash memory write count */ > +#define ADIS16400_SUPPLY_OUT 0x02 /* Power supply measurement */ > +#define ADIS16400_XGYRO_OUT 0x04 /* X-axis gyroscope output */ > +#define ADIS16400_YGYRO_OUT 0x06 /* Y-axis gyroscope output */ > +#define ADIS16400_ZGYRO_OUT 0x08 /* Z-axis gyroscope output */ > +#define ADIS16400_XACCL_OUT 0x0A /* X-axis accelerometer output */ > +#define ADIS16400_YACCL_OUT 0x0C /* Y-axis accelerometer output */ > +#define ADIS16400_ZACCL_OUT 0x0E /* Z-axis accelerometer output */ > +#define ADIS16400_XMAGN_OUT 0x10 /* X-axis magnetometer measurement */ > +#define ADIS16400_YMAGN_OUT 0x12 /* Y-axis magnetometer measurement */ > +#define ADIS16400_ZMAGN_OUT 0x14 /* Z-axis magnetometer measurement */ > +#define ADIS16400_TEMP_OUT 0x16 /* Temperature output */ > +#define ADIS16400_AUX_ADC 0x18 /* Auxiliary ADC measurement */ > + > +#define ADIS16350_XTEMP_OUT 0x10 /* X-axis gyroscope temperature measurement */ > +#define ADIS16350_YTEMP_OUT 0x12 /* Y-axis gyroscope temperature measurement */ > +#define ADIS16350_ZTEMP_OUT 0x14 /* Z-axis gyroscope temperature measurement */ > + > +#define ADIS16300_PITCH_OUT 0x12 /* X axis inclinometer output measurement */ > +#define ADIS16300_ROLL_OUT 0x12 /* Y axis inclinometer output measurement */ > + > +/* Calibration parameters */ > +#define ADIS16400_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */ > +#define ADIS16400_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */ > +#define ADIS16400_ZGYRO_OFF 0x1E /* Z-axis gyroscope bias offset factor */ > +#define ADIS16400_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */ > +#define ADIS16400_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */ > +#define ADIS16400_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */ > +#define ADIS16400_XMAGN_HIF 0x26 /* X-axis magnetometer, hard-iron factor */ > +#define ADIS16400_YMAGN_HIF 0x28 /* Y-axis magnetometer, hard-iron factor */ > +#define ADIS16400_ZMAGN_HIF 0x2A /* Z-axis magnetometer, hard-iron factor */ > +#define ADIS16400_XMAGN_SIF 0x2C /* X-axis magnetometer, soft-iron factor */ > +#define ADIS16400_YMAGN_SIF 0x2E /* Y-axis magnetometer, soft-iron factor */ > +#define ADIS16400_ZMAGN_SIF 0x30 /* Z-axis magnetometer, soft-iron factor */ > + > +#define ADIS16400_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */ > +#define ADIS16400_MSC_CTRL 0x34 /* Miscellaneous control */ > +#define ADIS16400_SMPL_PRD 0x36 /* Internal sample period (rate) control */ > +#define ADIS16400_SENS_AVG 0x38 /* Dynamic range and digital filter control */ > +#define ADIS16400_SLP_CNT 0x3A /* Sleep mode control */ > +#define ADIS16400_DIAG_STAT 0x3C /* System status */ > + > +/* Alarm functions */ > +#define ADIS16400_GLOB_CMD 0x3E /* System command */ > +#define ADIS16400_ALM_MAG1 0x40 /* Alarm 1 amplitude threshold */ > +#define ADIS16400_ALM_MAG2 0x42 /* Alarm 2 amplitude threshold */ > +#define ADIS16400_ALM_SMPL1 0x44 /* Alarm 1 sample size */ > +#define ADIS16400_ALM_SMPL2 0x46 /* Alarm 2 sample size */ > +#define ADIS16400_ALM_CTRL 0x48 /* Alarm control */ > +#define ADIS16400_AUX_DAC 0x4A /* Auxiliary DAC data */ > + > +#define ADIS16400_PRODUCT_ID 0x56 /* Product identifier */ > + > +#define ADIS16400_ERROR_ACTIVE (1<<14) > +#define ADIS16400_NEW_DATA (1<<14) > + > +/* MSC_CTRL */ > +#define ADIS16400_MSC_CTRL_MEM_TEST (1<<11) > +#define ADIS16400_MSC_CTRL_INT_SELF_TEST (1<<10) > +#define ADIS16400_MSC_CTRL_NEG_SELF_TEST (1<<9) > +#define ADIS16400_MSC_CTRL_POS_SELF_TEST (1<<8) > +#define ADIS16400_MSC_CTRL_GYRO_BIAS (1<<7) > +#define ADIS16400_MSC_CTRL_ACCL_ALIGN (1<<6) > +#define ADIS16400_MSC_CTRL_DATA_RDY_EN (1<<2) > +#define ADIS16400_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1) > +#define ADIS16400_MSC_CTRL_DATA_RDY_DIO2 (1<<0) > + > +/* SMPL_PRD */ > +#define ADIS16400_SMPL_PRD_TIME_BASE (1<<7) > +#define ADIS16400_SMPL_PRD_DIV_MASK 0x7F > + > +/* DIAG_STAT */ > +#define ADIS16400_DIAG_STAT_ZACCL_FAIL (1<<15) > +#define ADIS16400_DIAG_STAT_YACCL_FAIL (1<<14) > +#define ADIS16400_DIAG_STAT_XACCL_FAIL (1<<13) > +#define ADIS16400_DIAG_STAT_XGYRO_FAIL (1<<12) > +#define ADIS16400_DIAG_STAT_YGYRO_FAIL (1<<11) > +#define ADIS16400_DIAG_STAT_ZGYRO_FAIL (1<<10) > +#define ADIS16400_DIAG_STAT_ALARM2 (1<<9) > +#define ADIS16400_DIAG_STAT_ALARM1 (1<<8) > +#define ADIS16400_DIAG_STAT_FLASH_CHK (1<<6) > +#define ADIS16400_DIAG_STAT_SELF_TEST (1<<5) > +#define ADIS16400_DIAG_STAT_OVERFLOW (1<<4) > +#define ADIS16400_DIAG_STAT_SPI_FAIL (1<<3) > +#define ADIS16400_DIAG_STAT_FLASH_UPT (1<<2) > +#define ADIS16400_DIAG_STAT_POWER_HIGH (1<<1) > +#define ADIS16400_DIAG_STAT_POWER_LOW (1<<0) > + > +/* GLOB_CMD */ > +#define ADIS16400_GLOB_CMD_SW_RESET (1<<7) > +#define ADIS16400_GLOB_CMD_P_AUTO_NULL (1<<4) > +#define ADIS16400_GLOB_CMD_FLASH_UPD (1<<3) > +#define ADIS16400_GLOB_CMD_DAC_LATCH (1<<2) > +#define ADIS16400_GLOB_CMD_FAC_CALIB (1<<1) > +#define ADIS16400_GLOB_CMD_AUTO_NULL (1<<0) > + > +/* SLP_CNT */ > +#define ADIS16400_SLP_CNT_POWER_OFF (1<<8) > + > +#define ADIS16400_MAX_TX 24 > +#define ADIS16400_MAX_RX 24 > + > +#define ADIS16400_SPI_SLOW (u32)(300 * 1000) > +#define ADIS16400_SPI_BURST (u32)(1000 * 1000) > +#define ADIS16400_SPI_FAST (u32)(2000 * 1000) > + > +#define ADIS16400_HAS_PROD_ID 1 > + > +struct adis16400_chip_info { > + const struct iio_chan_spec *channels; > + const int num_channels; > + const int product_id; > + const long flags; > + unsigned int gyro_scale_micro; > + unsigned int accel_scale_micro; > +}; > + > +/** > + * struct adis16400_state - device instance specific data > + * @us: actual spi_device > + * @trig: data ready trigger registered with iio > + * @tx: transmit buffer > + * @rx: receive buffer > + * @buf_lock: mutex to protect tx and rx > + **/ > +struct adis16400_state { > + struct spi_device *us; > + struct iio_trigger *trig; > + struct mutex buf_lock; > + struct adis16400_chip_info *variant; > + > + u8 tx[ADIS16400_MAX_TX] ____cacheline_aligned; > + u8 rx[ADIS16400_MAX_RX] ____cacheline_aligned; > +}; > + > +#define ADIS16400_SCAN_SUPPLY 0 > +#define ADIS16400_SCAN_GYRO_X 1 > +#define ADIS16400_SCAN_GYRO_Y 2 > +#define ADIS16400_SCAN_GYRO_Z 3 > +#define ADIS16400_SCAN_ACC_X 4 > +#define ADIS16400_SCAN_ACC_Y 5 > +#define ADIS16400_SCAN_ACC_Z 6 > +#define ADIS16400_SCAN_MAGN_X 7 > +#define ADIS16350_SCAN_TEMP_X 7 > +#define ADIS16400_SCAN_MAGN_Y 8 > +#define ADIS16350_SCAN_TEMP_Y 8 > +#define ADIS16400_SCAN_MAGN_Z 9 > +#define ADIS16350_SCAN_TEMP_Z 9 > +#define ADIS16400_SCAN_TEMP 10 > +#define ADIS16350_SCAN_ADC_0 10 > +#define ADIS16400_SCAN_ADC_0 11 > +#define ADIS16300_SCAN_INCLI_X 12 > +#define ADIS16300_SCAN_INCLI_Y 13 > + > +#endif /* SPI_ADIS16400_H_ */ > diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c > new file mode 100644 > index 0000000..5e49742 > --- /dev/null > +++ b/drivers/iio/imu/adis16400_core.c > @@ -0,0 +1,1142 @@ > +/* > + * adis16400.c support a number of ADI IMUs > + * > + * Copyright (c) 2009 Manuel Stahl<manuel.stahl@iis.fraunhofer.de> > + * Copyright (c) 2007-2011 Jonathan Cameron<jic23@cam.ac.uk> > + * Copyright (c) 2011 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/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/list.h> > + > +#include<linux/iio/iio.h> > +#include<linux/iio/sysfs.h> > +#include "adis16400.h" > + > +enum adis16400_chip_variant { > + ADIS16300, > + ADIS16334, > + ADIS16350, > + ADIS16360, > + ADIS16362, > + ADIS16364, > + ADIS16365, > + ADIS16400, > +}; > + > +/** > + * adis16400_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 adis16400_spi_write_reg_8(struct iio_dev *indio_dev, > + u8 reg_address, > + u8 val) > +{ > + int ret; > + struct adis16400_state *st = iio_priv(indio_dev); > + > + mutex_lock(&st->buf_lock); > + st->tx[0] = ADIS16400_WRITE_REG(reg_address); > + st->tx[1] = val; > + > + ret = spi_write(st->us, st->tx, 2); > + mutex_unlock(&st->buf_lock); > + > + return ret; > +} > + > +/** > + * adis16400_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 > + * > + * At the moment the spi framework doesn't allow global setting of cs_change. > + * This means that use cannot be made of spi_write. > + */ > +static int adis16400_spi_write_reg_16(struct iio_dev *indio_dev, > + u8 lower_reg_address, > + u16 value) > +{ > + int ret; > + struct spi_message msg; > + struct adis16400_state *st = iio_priv(indio_dev); > + struct spi_transfer xfers[] = { > + { > + .tx_buf = st->tx, > + .bits_per_word = 8, > + .len = 2, > + .cs_change = 1, > + }, { > + .tx_buf = st->tx + 2, > + .bits_per_word = 8, > + .len = 2, > + }, > + }; > + > + mutex_lock(&st->buf_lock); > + st->tx[0] = ADIS16400_WRITE_REG(lower_reg_address); > + st->tx[1] = value& 0xFF; > + st->tx[2] = ADIS16400_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; > +} > + > +/** > + * adis16400_spi_read_reg_16() - read 2 bytes from a 16-bit register > + * @indio_dev: iio device > + * @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 > + * > + * At the moment the spi framework doesn't allow global setting of cs_change. > + * This means that use cannot be made of spi_read. > + **/ > +static int adis16400_spi_read_reg_16(struct iio_dev *indio_dev, > + u8 lower_reg_address, > + u16 *val) > +{ > + struct spi_message msg; > + struct adis16400_state *st = iio_priv(indio_dev); > + int ret; > + struct spi_transfer xfers[] = { > + { > + .tx_buf = st->tx, > + .bits_per_word = 8, > + .len = 2, > + .cs_change = 1, > + }, { > + .rx_buf = st->rx, > + .bits_per_word = 8, > + .len = 2, > + }, > + }; > + > + mutex_lock(&st->buf_lock); > + st->tx[0] = ADIS16400_READ_REG(lower_reg_address); > + st->tx[1] = 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 adis16400_read_frequency(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + int ret, len = 0; > + u16 t; > + int sps; > + ret = adis16400_spi_read_reg_16(indio_dev, > + ADIS16400_SMPL_PRD, > +&t); > + if (ret) > + return ret; > + sps = (t& ADIS16400_SMPL_PRD_TIME_BASE) ? 53 : 1638; > + sps /= (t& ADIS16400_SMPL_PRD_DIV_MASK) + 1; > + len = sprintf(buf, "%d SPS\n", sps); > + return len; > +} > + > +static ssize_t adis16400_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 adis16400_state *st = iio_priv(indio_dev); > + long val; > + int ret; > + u8 t; > + > + ret = strict_strtol(buf, 10,&val); > + if (ret) > + return ret; > + > + mutex_lock(&indio_dev->mlock); > + > + t = (1638 / val); > + if (t> 0) > + t--; > + t&= ADIS16400_SMPL_PRD_DIV_MASK; > + if ((t& ADIS16400_SMPL_PRD_DIV_MASK)>= 0x0A) > + st->us->max_speed_hz = ADIS16400_SPI_SLOW; > + else > + st->us->max_speed_hz = ADIS16400_SPI_FAST; > + > + ret = adis16400_spi_write_reg_8(indio_dev, > + ADIS16400_SMPL_PRD, > + t); > + > + mutex_unlock(&indio_dev->mlock); > + > + return ret ? ret : len; > +} > + > +static int adis16400_reset(struct iio_dev *indio_dev) > +{ > + int ret; > + ret = adis16400_spi_write_reg_8(indio_dev, > + ADIS16400_GLOB_CMD, > + ADIS16400_GLOB_CMD_SW_RESET); > + if (ret) > + dev_err(&indio_dev->dev, "problem resetting device"); > + > + return ret; > +} > + > +static ssize_t adis16400_write_reset(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + bool val; > + int ret; > + > + ret = strtobool(buf,&val); > + if (ret< 0) > + return ret; > + if (val) { > + ret = adis16400_reset(dev_get_drvdata(dev)); > + if (ret< 0) > + return ret; > + } > + > + return len; > +} > + > +/* Power down the device */ > +static int adis16400_stop_device(struct iio_dev *indio_dev) > +{ > + int ret; > + u16 val = ADIS16400_SLP_CNT_POWER_OFF; > + > + ret = adis16400_spi_write_reg_16(indio_dev, ADIS16400_SLP_CNT, val); > + if (ret) > + dev_err(&indio_dev->dev, > + "problem with turning device off: SLP_CNT"); > + > + return ret; > +} > + > +static int adis16400_check_status(struct iio_dev *indio_dev) > +{ > + u16 status; > + int ret; > + struct device *dev =&indio_dev->dev; > + > + ret = adis16400_spi_read_reg_16(indio_dev, > + ADIS16400_DIAG_STAT,&status); > + > + if (ret< 0) { > + dev_err(dev, "Reading status failed\n"); > + goto error_ret; > + } > + ret = status; > + if (status& ADIS16400_DIAG_STAT_ZACCL_FAIL) > + dev_err(dev, "Z-axis accelerometer self-test failure\n"); > + if (status& ADIS16400_DIAG_STAT_YACCL_FAIL) > + dev_err(dev, "Y-axis accelerometer self-test failure\n"); > + if (status& ADIS16400_DIAG_STAT_XACCL_FAIL) > + dev_err(dev, "X-axis accelerometer self-test failure\n"); > + if (status& ADIS16400_DIAG_STAT_XGYRO_FAIL) > + dev_err(dev, "X-axis gyroscope self-test failure\n"); > + if (status& ADIS16400_DIAG_STAT_YGYRO_FAIL) > + dev_err(dev, "Y-axis gyroscope self-test failure\n"); > + if (status& ADIS16400_DIAG_STAT_ZGYRO_FAIL) > + dev_err(dev, "Z-axis gyroscope self-test failure\n"); > + if (status& ADIS16400_DIAG_STAT_ALARM2) > + dev_err(dev, "Alarm 2 active\n"); > + if (status& ADIS16400_DIAG_STAT_ALARM1) > + dev_err(dev, "Alarm 1 active\n"); > + if (status& ADIS16400_DIAG_STAT_FLASH_CHK) > + dev_err(dev, "Flash checksum error\n"); > + if (status& ADIS16400_DIAG_STAT_SELF_TEST) > + dev_err(dev, "Self test error\n"); > + if (status& ADIS16400_DIAG_STAT_OVERFLOW) > + dev_err(dev, "Sensor overrange\n"); > + if (status& ADIS16400_DIAG_STAT_SPI_FAIL) > + dev_err(dev, "SPI failure\n"); > + if (status& ADIS16400_DIAG_STAT_FLASH_UPT) > + dev_err(dev, "Flash update failed\n"); > + if (status& ADIS16400_DIAG_STAT_POWER_HIGH) > + dev_err(dev, "Power supply above 5.25V\n"); > + if (status& ADIS16400_DIAG_STAT_POWER_LOW) > + dev_err(dev, "Power supply below 4.75V\n"); > + > +error_ret: > + return ret; > +} > + > +static int adis16400_self_test(struct iio_dev *indio_dev) > +{ > + int ret; > + ret = adis16400_spi_write_reg_16(indio_dev, > + ADIS16400_MSC_CTRL, > + ADIS16400_MSC_CTRL_MEM_TEST); > + if (ret) { > + dev_err(&indio_dev->dev, "problem starting self test"); > + goto err_ret; > + } > + > + msleep(ADIS16400_MTEST_DELAY); > + adis16400_check_status(indio_dev); > + > +err_ret: > + return ret; > +} > + > +static int adis16400_initial_setup(struct iio_dev *indio_dev) > +{ > + int ret; > + u16 prod_id, smp_prd; > + struct adis16400_state *st = iio_priv(indio_dev); > + > + /* use low spi speed for init */ > + st->us->max_speed_hz = ADIS16400_SPI_SLOW; > + st->us->mode = SPI_MODE_3; > + spi_setup(st->us); > + > + ret = adis16400_self_test(indio_dev); > + if (ret) { > + dev_err(&indio_dev->dev, "self test failure"); > + goto err_ret; > + } > + > + ret = adis16400_check_status(indio_dev); > + if (ret) { > + adis16400_reset(indio_dev); > + dev_err(&indio_dev->dev, "device not playing ball -> reset"); > + msleep(ADIS16400_STARTUP_DELAY); > + ret = adis16400_check_status(indio_dev); > + if (ret) { > + dev_err(&indio_dev->dev, "giving up"); > + goto err_ret; > + } > + } > + if (st->variant->flags& ADIS16400_HAS_PROD_ID) { > + ret = adis16400_spi_read_reg_16(indio_dev, > + ADIS16400_PRODUCT_ID,&prod_id); > + if (ret) > + goto err_ret; > + > + if ((prod_id& 0xF000) != st->variant->product_id) > + dev_warn(&indio_dev->dev, "incorrect id"); > + > + dev_info(&indio_dev->dev, > + "%s: prod_id 0x%04x at CS%d (irq %d)\n", > + indio_dev->name, prod_id, > + st->us->chip_select, st->us->irq); > + } > + /* use high spi speed if possible */ > + ret = adis16400_spi_read_reg_16(indio_dev, > + ADIS16400_SMPL_PRD,&smp_prd); > + if (!ret&& (smp_prd& ADIS16400_SMPL_PRD_DIV_MASK)< 0x0A) { > + st->us->max_speed_hz = ADIS16400_SPI_SLOW; > + spi_setup(st->us); > + } > + > +err_ret: > + return ret; > +} > + > +static IIO_DEVICE_ATTR(sampling_frequency, > + S_IWUSR | S_IRUGO, > + adis16400_read_frequency, > + adis16400_write_frequency, > + 0); > + > +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16400_write_reset, 0); > + > +static IIO_CONST_ATTR(sampling_frequency_available, "409 546 819 1638"); > + > +enum adis16400_chan { > + in_supply, > + gyro_x, > + gyro_y, > + gyro_z, > + accel_x, > + accel_y, > + accel_z, > + magn_x, > + magn_y, > + magn_z, > + temp, > + temp0, temp1, temp2, > + in1, > + incli_x, > + incli_y, > +}; > + > +static u8 adis16400_addresses[17][2] = { > + [in_supply] = { ADIS16400_SUPPLY_OUT }, > + [gyro_x] = { ADIS16400_XGYRO_OUT, ADIS16400_XGYRO_OFF }, > + [gyro_y] = { ADIS16400_YGYRO_OUT, ADIS16400_YGYRO_OFF }, > + [gyro_z] = { ADIS16400_ZGYRO_OUT, ADIS16400_ZGYRO_OFF }, > + [accel_x] = { ADIS16400_XACCL_OUT, ADIS16400_XACCL_OFF }, > + [accel_y] = { ADIS16400_YACCL_OUT, ADIS16400_YACCL_OFF }, > + [accel_z] = { ADIS16400_ZACCL_OUT, ADIS16400_ZACCL_OFF }, > + [magn_x] = { ADIS16400_XMAGN_OUT }, > + [magn_y] = { ADIS16400_YMAGN_OUT }, > + [magn_z] = { ADIS16400_ZMAGN_OUT }, > + [temp] = { ADIS16400_TEMP_OUT }, > + [temp0] = { ADIS16350_XTEMP_OUT }, > + [temp1] = { ADIS16350_YTEMP_OUT }, > + [temp2] = { ADIS16350_ZTEMP_OUT }, > + [in1] = { ADIS16400_AUX_ADC }, > + [incli_x] = { ADIS16300_PITCH_OUT }, > + [incli_y] = { ADIS16300_ROLL_OUT } > +}; > + > +static int adis16400_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int val, > + int val2, > + long mask) > +{ > + int ret; > + > + switch (mask) { > + case IIO_CHAN_INFO_CALIBBIAS: > + mutex_lock(&indio_dev->mlock); > + ret = adis16400_spi_write_reg_16(indio_dev, > + adis16400_addresses[chan->address][1], > + val); > + mutex_unlock(&indio_dev->mlock); > + return ret; > + default: > + return -EINVAL; > + } > +} > + > +static int adis16400_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int *val, > + int *val2, > + long mask) > +{ > + struct adis16400_state *st = iio_priv(indio_dev); > + int ret, shift; > + s16 val16; > + > + switch (mask) { > + case 0: > + mutex_lock(&indio_dev->mlock); > + ret = adis16400_spi_read_reg_16(indio_dev, > + adis16400_addresses[chan->address][0], > +&val16); > + if (ret) { > + mutex_unlock(&indio_dev->mlock); > + return ret; > + } > + val16&= (1<< chan->scan_type.realbits) - 1; > + if (chan->scan_type.sign == 's') { > + shift = 16 - chan->scan_type.realbits; > + val16 = (s16)(val16<< shift)>> shift; > + } > + *val = val16; > + mutex_unlock(&indio_dev->mlock); > + return IIO_VAL_INT; > + case IIO_CHAN_INFO_SCALE: > + switch (chan->type) { > + case IIO_ANGL_VEL: > + *val = 0; > + *val2 = st->variant->gyro_scale_micro; > + return IIO_VAL_INT_PLUS_MICRO; > + case IIO_IN: > + *val = 0; > + if (chan->channel == 0) > + *val2 = 2418; > + else > + *val2 = 806; > + return IIO_VAL_INT_PLUS_MICRO; > + case IIO_ACCEL: > + *val = 0; > + *val2 = st->variant->accel_scale_micro; > + return IIO_VAL_INT_PLUS_MICRO; > + case IIO_MAGN: > + *val = 0; > + *val2 = 500; > + return IIO_VAL_INT_PLUS_MICRO; > + case IIO_TEMP: > + *val = 0; > + *val2 = 140000; > + return IIO_VAL_INT_PLUS_MICRO; > + default: > + return -EINVAL; > + } > + case IIO_CHAN_INFO_CALIBBIAS: > + mutex_lock(&indio_dev->mlock); > + ret = adis16400_spi_read_reg_16(indio_dev, > + adis16400_addresses[chan->address][1], > +&val16); > + mutex_unlock(&indio_dev->mlock); > + if (ret) > + return ret; > + val16 = ((val16& 0xFFF)<< 4)>> 4; > + *val = val16; > + return IIO_VAL_INT; > + case IIO_CHAN_INFO_OFFSET: > + /* currently only temperature */ > + *val = 198; > + *val2 = 160000; > + return IIO_VAL_INT_PLUS_MICRO; > + default: > + return -EINVAL; > + } > +} > + > +static struct iio_chan_spec adis16400_channels[] = { > + { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .extend_name = "supply", > + .info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + .address = in_supply, > + .scan_type = { > + .realbits = 14, > + .sign = 'u', > + }, > + }, { > + .type = IIO_ANGL_VEL, > + .modified = 1, > + .channel2 = IIO_MOD_X, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = gyro_x, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ANGL_VEL, > + .modified = 1, > + .channel2 = IIO_MOD_Y, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = gyro_y, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ANGL_VEL, > + .modified = 1, > + .channel2 = IIO_MOD_Z, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = gyro_z, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_X, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_x, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_Y, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_y, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_Z, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_z, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_MAGN, > + .modified = 1, > + .channel2 = IIO_MOD_X, > + .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = magn_x, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_MAGN, > + .modified = 1, > + .channel2 = IIO_MOD_Y, > + .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = magn_y, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_MAGN, > + .modified = 1, > + .channel2 = IIO_MOD_Z, > + .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = magn_z, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_TEMP, > + .indexed = 1, > + .channel = 0, > + .info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + .address = temp, > + .scan_type = { > + .realbits = 12, > + .sign = 's', > + }, > + }, { > + .type = IIO_IN, > + .indexed = 1, > + .channel = 1, > + .info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + .address = in1, > + .scan_type = { > + .realbits = 12, > + .sign = 'u', > + }, > + }, > +}; > + > +static struct iio_chan_spec adis16350_channels[] = { > + { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .extend_name = "supply", > + .info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + .address = in_supply, > + .scan_type = { > + .realbits = 12, > + .sign = 'u', > + }, > + }, { > + .type = IIO_ANGL_VEL, > + .modified = 1, > + .channel2 = IIO_MOD_X, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = gyro_x, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ANGL_VEL, > + .modified = 1, > + .channel2 = IIO_MOD_Y, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = gyro_y, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ANGL_VEL, > + .modified = 1, > + .channel2 = IIO_MOD_Z, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = gyro_z, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_X, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_x, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_Y, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_y, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_Z, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_z, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_TEMP, > + .indexed = 1, > + .channel = 0, > + .extend_name = "x", > + .info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + .address = temp0, > + .scan_type = { > + .realbits = 12, > + .sign = 's', > + }, > + }, { > + .type = IIO_TEMP, > + .indexed = 1, > + .channel = 1, > + .extend_name = "y", > + .info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + .address = temp1, > + .scan_type = { > + .realbits = 12, > + .sign = 's', > + }, > + }, { > + .type = IIO_TEMP, > + .indexed = 1, > + .channel = 2, > + .extend_name = "z", > + .info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + .address = temp2, > + .scan_type = { > + .realbits = 12, > + .sign = 's', > + }, > + }, { > + .type = IIO_IN, > + .indexed = 1, > + .channel = 1, > + .info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + .address = in1, > + .scan_type = { > + .realbits = 12, > + .sign = 'u', > + }, > + }, > +}; > + > +static struct iio_chan_spec adis16300_channels[] = { > + { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .extend_name = "supply", > + .info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + .address = in_supply, > + .scan_type = { > + .realbits = 14, > + .sign = 'u', > + }, > + }, { > + .type = IIO_ANGL_VEL, > + .modified = 1, > + .channel2 = IIO_MOD_X, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = gyro_x, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_X, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_x, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_Y, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_y, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_Z, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_z, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_TEMP, > + .indexed = 1, > + .channel = 0, > + .info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + .address = temp, > + .scan_type = { > + .realbits = 12, > + .sign = 's', > + }, > + }, { > + .type = IIO_IN, > + .indexed = 1, > + .channel = 1, > + .info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + .address = in1, > + .scan_type = { > + .realbits = 12, > + .sign = 'u', > + }, > + }, { > + .type = IIO_INCLI, > + .modified = 1, > + .channel2 = IIO_MOD_X, > + .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = incli_x, > + .scan_type = { > + .realbits = 13, > + .sign = 's', > + }, > + }, { > + .type = IIO_INCLI, > + .modified = 1, > + .channel2 = IIO_MOD_Y, > + .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = incli_y, > + .scan_type = { > + .realbits = 13, > + .sign = 's', > + }, > + }, > +}; > + > +static const struct iio_chan_spec adis16334_channels[] = { > + { > + .type = IIO_ANGL_VEL, > + .modified = 1, > + .channel2 = IIO_MOD_X, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = gyro_x, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ANGL_VEL, > + .modified = 1, > + .channel2 = IIO_MOD_Y, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = gyro_y, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ANGL_VEL, > + .modified = 1, > + .channel2 = IIO_MOD_Z, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = gyro_z, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_X, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_x, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_Y, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_y, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_Z, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_z, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, { > + .type = IIO_TEMP, > + .indexed = 1, > + .channel = 0, > + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | > + IIO_CHAN_INFO_SCALE_SHARED_BIT, > + .address = accel_z, > + .scan_type = { > + .realbits = 14, > + .sign = 's', > + }, > + }, > +}; > + > +static struct attribute *adis16400_attributes[] = { > +&iio_dev_attr_sampling_frequency.dev_attr.attr, > +&iio_const_attr_sampling_frequency_available.dev_attr.attr, > +&iio_dev_attr_reset.dev_attr.attr, > + NULL > +}; > + > +static const struct attribute_group adis16400_attribute_group = { > + .attrs = adis16400_attributes, > +}; > + > +static struct adis16400_chip_info adis16400_chips[] = { > + [ADIS16300] = { > + .channels = adis16300_channels, > + .num_channels = ARRAY_SIZE(adis16300_channels), > + .gyro_scale_micro = 873, > + .accel_scale_micro = 5884, > + }, > + [ADIS16334] = { > + .channels = adis16334_channels, > + .num_channels = ARRAY_SIZE(adis16334_channels), > + .gyro_scale_micro = 873, > + .accel_scale_micro = 981, > + }, > + [ADIS16350] = { > + .channels = adis16350_channels, > + .num_channels = ARRAY_SIZE(adis16350_channels), > + .gyro_scale_micro = 872664, > + .accel_scale_micro = 24732, > + }, > + [ADIS16360] = { > + .channels = adis16350_channels, > + .num_channels = ARRAY_SIZE(adis16350_channels), > + .flags = ADIS16400_HAS_PROD_ID, > + .product_id = 0x3FE8, > + .gyro_scale_micro = 1279, > + .accel_scale_micro = 24732, > + }, > + [ADIS16362] = { > + .channels = adis16350_channels, > + .num_channels = ARRAY_SIZE(adis16350_channels), > + .flags = ADIS16400_HAS_PROD_ID, > + .product_id = 0x3FEA, > + .gyro_scale_micro = 1279, > + .accel_scale_micro = 24732, > + }, > + [ADIS16364] = { > + .channels = adis16350_channels, > + .num_channels = ARRAY_SIZE(adis16350_channels), > + .flags = ADIS16400_HAS_PROD_ID, > + .product_id = 0x3FEC, > + .gyro_scale_micro = 1279, > + .accel_scale_micro = 24732, > + }, > + [ADIS16365] = { > + .channels = adis16350_channels, > + .num_channels = ARRAY_SIZE(adis16350_channels), > + .flags = ADIS16400_HAS_PROD_ID, > + .product_id = 0x3FED, > + .gyro_scale_micro = 1279, > + .accel_scale_micro = 24732, > + }, > + [ADIS16400] = { > + .channels = adis16400_channels, > + .num_channels = ARRAY_SIZE(adis16400_channels), > + .flags = ADIS16400_HAS_PROD_ID, > + .product_id = 0x4015, > + .gyro_scale_micro = 873, > + .accel_scale_micro = 32656, > + } > +}; > + > +static const struct iio_info adis16400_info = { > + .driver_module = THIS_MODULE, > + .read_raw =&adis16400_read_raw, > + .write_raw =&adis16400_write_raw, > + .attrs =&adis16400_attribute_group, > +}; > + > +static int __devinit adis16400_probe(struct spi_device *spi) > +{ > + int ret; > + struct adis16400_state *st; > + struct iio_dev *indio_dev; > + > + indio_dev = iio_device_allocate(sizeof(*st)); > + if (indio_dev == NULL) { > + ret = -ENOMEM; > + goto error_ret; > + } > + st = iio_priv(indio_dev); > + /* this is only used for removal purposes */ > + spi_set_drvdata(spi, indio_dev); > + > + st->us = spi; > + mutex_init(&st->buf_lock); > + > + /* setup the industrialio driver allocated elements */ > + st->variant =&adis16400_chips[spi_get_device_id(spi)->driver_data]; > + indio_dev->dev.parent =&spi->dev; > + indio_dev->name = spi_get_device_id(spi)->name; > + indio_dev->channels = st->variant->channels; > + indio_dev->num_channels = st->variant->num_channels; > + indio_dev->info =&adis16400_info; > + > + ret = iio_device_register(indio_dev); > + if (ret) > + goto error_free_dev; > + > + /* Get the device into a sane initial state */ > + ret = adis16400_initial_setup(indio_dev); > + if (ret) > + goto error_unregister_dev; > + > + return 0; > + > +error_unregister_dev: > + iio_device_unregister(indio_dev); > +error_free_dev: > + iio_device_free(indio_dev); > +error_ret: > + return ret; > +} > + > +/* fixme, confirm ordering in this function */ > +static int adis16400_remove(struct spi_device *spi) > +{ > + int ret; > + struct iio_dev *indio_dev = spi_get_drvdata(spi); > + > + iio_device_unregister(indio_dev); > + > + ret = adis16400_stop_device(indio_dev); > + if (ret) > + return ret; > + > + iio_device_free(indio_dev); > + > + return 0; > +} > + > +static const struct spi_device_id adis16400_id[] = { > + {"adis16300", ADIS16300}, > + {"adis16334", ADIS16334}, > + {"adis16350", ADIS16350}, > + {"adis16354", ADIS16350}, > + {"adis16355", ADIS16350}, > + {"adis16360", ADIS16360}, > + {"adis16362", ADIS16362}, > + {"adis16364", ADIS16364}, > + {"adis16365", ADIS16365}, > + {"adis16400", ADIS16400}, > + {"adis16405", ADIS16400}, > + {} > +}; > + > +static struct spi_driver adis16400_driver = { > + .driver = { > + .name = "adis16400", > + .owner = THIS_MODULE, > + }, > + .id_table = adis16400_id, > + .probe = adis16400_probe, > + .remove = __devexit_p(adis16400_remove), > +}; > + > +static __init int adis16400_init(void) > +{ > + return spi_register_driver(&adis16400_driver); > +} > +module_init(adis16400_init); > + > +static __exit void adis16400_exit(void) > +{ > + spi_unregister_driver(&adis16400_driver); > +} > +module_exit(adis16400_exit); > + > +MODULE_AUTHOR("Manuel Stahl<manuel.stahl@iis.fraunhofer.de>"); > +MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver"); > +MODULE_LICENSE("GPL v2"); > -- > 1.7.7.2 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html >
-- Greetings, Michael
-- Analog Devices GmbH Wilhelm-Wagenfeld-Str. 6 80807 Muenchen Sitz der Gesellschaft: Muenchen; Registergericht: Muenchen HRB 40368; Geschaeftsfuehrer:Dr.Carsten Suckrow, Thomas Wessel, William A. Martin, Margaret Seif
| |