diff options
Diffstat (limited to 'drivers/staging/iio/imu/adis16400_core.c')
-rw-r--r-- | drivers/staging/iio/imu/adis16400_core.c | 735 |
1 files changed, 446 insertions, 289 deletions
diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c index cfb108a1545b..fe89802e3fe7 100644 --- a/drivers/staging/iio/imu/adis16400_core.c +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -6,6 +6,7 @@ * * Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de> * Copyright (c) 2007 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 @@ -37,7 +38,17 @@ #define DRIVER_NAME "adis16400" -static int adis16400_check_status(struct device *dev); +enum adis16400_chip_variant { + ADIS16300, + ADIS16350, + ADIS16360, + ADIS16362, + ADIS16364, + ADIS16365, + ADIS16400, +}; + +static int adis16400_check_status(struct iio_dev *indio_dev); /* At the moment the spi framework doesn't allow global setting of cs_change. * It's in the likely to be added comment at the top of spi.h. @@ -50,13 +61,12 @@ static int adis16400_check_status(struct device *dev); * @reg_address: the address of the register to be written * @val: the value to write **/ -static int adis16400_spi_write_reg_8(struct device *dev, - u8 reg_address, - u8 val) +static int adis16400_spi_write_reg_8(struct iio_dev *indio_dev, + u8 reg_address, + u8 val) { int ret; - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + struct adis16400_state *st = iio_priv(indio_dev); mutex_lock(&st->buf_lock); st->tx[0] = ADIS16400_WRITE_REG(reg_address); @@ -75,14 +85,13 @@ static int adis16400_spi_write_reg_8(struct device *dev, * is assumed to have address one greater. * @val: value to be written **/ -static int adis16400_spi_write_reg_16(struct device *dev, +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 iio_dev *indio_dev = dev_get_drvdata(dev); - struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + struct adis16400_state *st = iio_priv(indio_dev); struct spi_transfer xfers[] = { { .tx_buf = st->tx, @@ -93,7 +102,6 @@ static int adis16400_spi_write_reg_16(struct device *dev, .tx_buf = st->tx + 2, .bits_per_word = 8, .len = 2, - .cs_change = 1, }, }; @@ -114,18 +122,17 @@ static int adis16400_spi_write_reg_16(struct device *dev, /** * adis16400_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) + * @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 **/ -static int adis16400_spi_read_reg_16(struct device *dev, +static int adis16400_spi_read_reg_16(struct iio_dev *indio_dev, u8 lower_reg_address, u16 *val) { struct spi_message msg; - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + struct adis16400_state *st = iio_priv(indio_dev); int ret; struct spi_transfer xfers[] = { { @@ -137,7 +144,6 @@ static int adis16400_spi_read_reg_16(struct device *dev, .rx_buf = st->rx, .bits_per_word = 8, .len = 2, - .cs_change = 1, }, }; @@ -164,100 +170,15 @@ error_ret: return ret; } -static ssize_t adis16400_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 = adis16400_spi_read_reg_16(dev, this_attr->address, (u16 *)&val); - if (ret) - return ret; - - if (val & ADIS16400_ERROR_ACTIVE) - adis16400_check_status(dev); - val = ((s16)(val << shift) >> shift); - return sprintf(buf, "%d\n", val); -} - -static ssize_t adis16400_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 = adis16400_spi_read_reg_16(dev, this_attr->address, &val); - if (ret) - return ret; - - if (val & ADIS16400_ERROR_ACTIVE) - adis16400_check_status(dev); - - return sprintf(buf, "%u\n", val & 0x0FFF); -} - -static ssize_t adis16400_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 = adis16400_spi_read_signed(dev, attr, buf, 14); - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static ssize_t adis16400_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 = adis16400_spi_read_signed(dev, attr, buf, 12); - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static ssize_t adis16400_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 = adis16400_spi_write_reg_16(dev, this_attr->address, val); - -error_ret: - return ret ? ret : len; -} - 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(dev, + ret = adis16400_spi_read_reg_16(indio_dev, ADIS16400_SMPL_PRD, &t); if (ret) @@ -274,7 +195,7 @@ static ssize_t adis16400_write_frequency(struct device *dev, size_t len) { struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct adis16400_state *st = iio_dev_get_devdata(indio_dev); + struct adis16400_state *st = iio_priv(indio_dev); long val; int ret; u8 t; @@ -294,7 +215,7 @@ static ssize_t adis16400_write_frequency(struct device *dev, else st->us->max_speed_hz = ADIS16400_SPI_FAST; - ret = adis16400_spi_write_reg_8(dev, + ret = adis16400_spi_write_reg_8(indio_dev, ADIS16400_SMPL_PRD, t); @@ -303,14 +224,14 @@ static ssize_t adis16400_write_frequency(struct device *dev, return ret ? ret : len; } -static int adis16400_reset(struct device *dev) +static int adis16400_reset(struct iio_dev *indio_dev) { int ret; - ret = adis16400_spi_write_reg_8(dev, + ret = adis16400_spi_write_reg_8(indio_dev, ADIS16400_GLOB_CMD, ADIS16400_GLOB_CMD_SW_RESET); if (ret) - dev_err(dev, "problem resetting device"); + dev_err(&indio_dev->dev, "problem resetting device"); return ret; } @@ -319,22 +240,24 @@ static ssize_t adis16400_write_reset(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { + struct iio_dev *indio_dev = dev_get_drvdata(dev); + if (len < 1) return -1; switch (buf[0]) { case '1': case 'y': case 'Y': - return adis16400_reset(dev); + return adis16400_reset(indio_dev); } return -1; } -int adis16400_set_irq(struct device *dev, bool enable) +int adis16400_set_irq(struct iio_dev *indio_dev, bool enable) { int ret; u16 msc; - ret = adis16400_spi_read_reg_16(dev, ADIS16400_MSC_CTRL, &msc); + ret = adis16400_spi_read_reg_16(indio_dev, ADIS16400_MSC_CTRL, &msc); if (ret) goto error_ret; @@ -344,7 +267,7 @@ int adis16400_set_irq(struct device *dev, bool enable) else msc &= ~ADIS16400_MSC_CTRL_DATA_RDY_EN; - ret = adis16400_spi_write_reg_16(dev, ADIS16400_MSC_CTRL, msc); + ret = adis16400_spi_write_reg_16(indio_dev, ADIS16400_MSC_CTRL, msc); if (ret) goto error_ret; @@ -353,41 +276,45 @@ error_ret: } /* Power down the device */ -static int adis16400_stop_device(struct device *dev) +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(dev, ADIS16400_SLP_CNT, val); + ret = adis16400_spi_write_reg_16(indio_dev, ADIS16400_SLP_CNT, val); if (ret) - dev_err(dev, "problem with turning device off: SLP_CNT"); + dev_err(&indio_dev->dev, + "problem with turning device off: SLP_CNT"); return ret; } -static int adis16400_self_test(struct device *dev) +static int adis16400_self_test(struct iio_dev *indio_dev) { int ret; - ret = adis16400_spi_write_reg_16(dev, + ret = adis16400_spi_write_reg_16(indio_dev, ADIS16400_MSC_CTRL, ADIS16400_MSC_CTRL_MEM_TEST); if (ret) { - dev_err(dev, "problem starting self test"); + dev_err(&indio_dev->dev, "problem starting self test"); goto err_ret; } - adis16400_check_status(dev); + msleep(ADIS16400_MTEST_DELAY); + adis16400_check_status(indio_dev); err_ret: return ret; } -static int adis16400_check_status(struct device *dev) +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(dev, ADIS16400_DIAG_STAT, &status); + ret = adis16400_spi_read_reg_16(indio_dev, + ADIS16400_DIAG_STAT, &status); if (ret < 0) { dev_err(dev, "Reading status failed\n"); @@ -429,11 +356,12 @@ error_ret: return ret; } -static int adis16400_initial_setup(struct adis16400_state *st) +static int adis16400_initial_setup(struct iio_dev *indio_dev) { int ret; u16 prod_id, smp_prd; - struct device *dev = &st->indio_dev->dev; + struct device *dev = &indio_dev->dev; + struct adis16400_state *st = iio_priv(indio_dev); /* use low spi speed for init */ st->us->max_speed_hz = ADIS16400_SPI_SLOW; @@ -441,44 +369,46 @@ static int adis16400_initial_setup(struct adis16400_state *st) spi_setup(st->us); /* Disable IRQ */ - ret = adis16400_set_irq(dev, false); + ret = adis16400_set_irq(indio_dev, false); if (ret) { dev_err(dev, "disable irq failed"); goto err_ret; } /* Do self test */ - ret = adis16400_self_test(dev); + ret = adis16400_self_test(indio_dev); if (ret) { dev_err(dev, "self test failure"); goto err_ret; } /* Read status register to check the result */ - ret = adis16400_check_status(dev); + ret = adis16400_check_status(indio_dev); if (ret) { - adis16400_reset(dev); + adis16400_reset(indio_dev); dev_err(dev, "device not playing ball -> reset"); msleep(ADIS16400_STARTUP_DELAY); - ret = adis16400_check_status(dev); + ret = adis16400_check_status(indio_dev); if (ret) { dev_err(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; - ret = adis16400_spi_read_reg_16(dev, ADIS16400_PRODUCT_ID, &prod_id); - if (ret) - goto err_ret; - - if (prod_id != ADIS16400_PRODUCT_ID_DEFAULT) - dev_warn(dev, "unknown product id"); - - printk(KERN_INFO DRIVER_NAME ": prod_id 0x%04x at CS%d (irq %d)\n", - prod_id, st->us->chip_select, st->us->irq); + if ((prod_id & 0xF000) != st->variant->product_id) + dev_warn(dev, "incorrect id"); + printk(KERN_INFO DRIVER_NAME ": prod_id 0x%04x at CS%d (irq %d)\n", + prod_id, st->us->chip_select, st->us->irq); + } /* use high spi speed if possible */ - ret = adis16400_spi_read_reg_16(dev, ADIS16400_SMPL_PRD, &smp_prd); + 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); @@ -490,58 +420,6 @@ err_ret: return ret; } -#define ADIS16400_DEV_ATTR_CALIBBIAS(_channel, _reg) \ - IIO_DEV_ATTR_##_channel##_CALIBBIAS(S_IWUSR | S_IRUGO, \ - adis16400_read_12bit_signed, \ - adis16400_write_16bit, \ - _reg) - -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_X, ADIS16400_XGYRO_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Y, ADIS16400_XGYRO_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Z, ADIS16400_XGYRO_OFF); - -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_X, ADIS16400_XACCL_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Y, ADIS16400_XACCL_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Z, ADIS16400_XACCL_OFF); - - -static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16400_read_14bit_signed, - ADIS16400_SUPPLY_OUT); -static IIO_CONST_ATTR_IN_NAMED_SCALE(0, supply, "0.002418 V"); - -static IIO_DEV_ATTR_GYRO_X(adis16400_read_14bit_signed, - ADIS16400_XGYRO_OUT); -static IIO_DEV_ATTR_GYRO_Y(adis16400_read_14bit_signed, - ADIS16400_YGYRO_OUT); -static IIO_DEV_ATTR_GYRO_Z(adis16400_read_14bit_signed, - ADIS16400_ZGYRO_OUT); -static IIO_CONST_ATTR(gyro_scale, "0.0008726646"); - -static IIO_DEV_ATTR_ACCEL_X(adis16400_read_14bit_signed, - ADIS16400_XACCL_OUT); -static IIO_DEV_ATTR_ACCEL_Y(adis16400_read_14bit_signed, - ADIS16400_YACCL_OUT); -static IIO_DEV_ATTR_ACCEL_Z(adis16400_read_14bit_signed, - ADIS16400_ZACCL_OUT); -static IIO_CONST_ATTR(accel_scale, "0.0326561445"); - -static IIO_DEV_ATTR_MAGN_X(adis16400_read_14bit_signed, - ADIS16400_XMAGN_OUT); -static IIO_DEV_ATTR_MAGN_Y(adis16400_read_14bit_signed, - ADIS16400_YMAGN_OUT); -static IIO_DEV_ATTR_MAGN_Z(adis16400_read_14bit_signed, - ADIS16400_ZMAGN_OUT); -static IIO_CONST_ATTR(magn_scale, "0.0005 Gs"); - - -static IIO_DEV_ATTR_TEMP_RAW(adis16400_read_12bit_signed); -static IIO_CONST_ATTR_TEMP_OFFSET("198.16 K"); -static IIO_CONST_ATTR_TEMP_SCALE("0.14 K"); - -static IIO_DEV_ATTR_IN_RAW(1, adis16400_read_12bit_unsigned, - ADIS16400_AUX_ADC); -static IIO_CONST_ATTR(in1_scale, "0.000806 V"); - static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, adis16400_read_frequency, adis16400_write_frequency); @@ -550,46 +428,273 @@ static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16400_write_reset, 0); static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("409 546 819 1638"); -static IIO_CONST_ATTR_NAME("adis16400"); +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 +}; -static struct attribute *adis16400_event_attributes[] = { - NULL +static u8 adis16400_addresses[16][2] = { + [in_supply] = { ADIS16400_SUPPLY_OUT, 0 }, + [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, 0 }, + [magn_y] = { ADIS16400_YMAGN_OUT, 0 }, + [magn_z] = { ADIS16400_ZMAGN_OUT, 0 }, + [temp] = { ADIS16400_TEMP_OUT, 0 }, + [temp0] = { ADIS16350_XTEMP_OUT }, + [temp1] = { ADIS16350_YTEMP_OUT }, + [temp2] = { ADIS16350_ZTEMP_OUT }, + [in1] = { ADIS16400_AUX_ADC , 0 }, }; -static struct attribute_group adis16400_event_attribute_group = { - .attrs = adis16400_event_attributes, +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 (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE): + 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; + s16 val16; + int shift; + + 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 (1 << IIO_CHAN_INFO_SCALE_SHARED): + case (1 << IIO_CHAN_INFO_SCALE_SEPARATE): + switch (chan->type) { + case IIO_GYRO: + *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 (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE): + 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 (1 << IIO_CHAN_INFO_OFFSET_SEPARATE): + /* currently only temperature */ + *val = 198; + *val2 = 160000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static struct iio_chan_spec adis16400_channels[] = { + IIO_CHAN(IIO_IN, 0, 1, 0, "supply", 0, 0, + (1 << IIO_CHAN_INFO_SCALE_SEPARATE), + in_supply, ADIS16400_SCAN_SUPPLY, + IIO_ST('u', 14, 16, 0), 0), + IIO_CHAN(IIO_GYRO, 1, 0, 0, NULL, 0, IIO_MOD_X, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + gyro_x, ADIS16400_SCAN_GYRO_X, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_GYRO, 1, 0, 0, NULL, 0, IIO_MOD_Y, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + gyro_y, ADIS16400_SCAN_GYRO_Y, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_GYRO, 1, 0, 0, NULL, 0, IIO_MOD_Z, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + gyro_z, ADIS16400_SCAN_GYRO_Z, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_X, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + accel_x, ADIS16400_SCAN_ACC_X, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_Y, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + accel_y, ADIS16400_SCAN_ACC_Y, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_Z, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + accel_z, ADIS16400_SCAN_ACC_Z, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_MAGN, 1, 0, 0, NULL, 0, IIO_MOD_X, + (1 << IIO_CHAN_INFO_SCALE_SHARED), + magn_x, ADIS16400_SCAN_MAGN_X, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_MAGN, 1, 0, 0, NULL, 0, IIO_MOD_Y, + (1 << IIO_CHAN_INFO_SCALE_SHARED), + magn_y, ADIS16400_SCAN_MAGN_Y, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_MAGN, 1, 0, 0, NULL, 0, IIO_MOD_Z, + (1 << IIO_CHAN_INFO_SCALE_SHARED), + magn_z, ADIS16400_SCAN_MAGN_Z, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_TEMP, 0, 1, 0, NULL, 0, 0, + (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SEPARATE), + temp, ADIS16400_SCAN_TEMP, IIO_ST('s', 12, 16, 0), 0), + IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 1, 0, + (1 << IIO_CHAN_INFO_SCALE_SEPARATE), + in1, ADIS16400_SCAN_ADC_0, IIO_ST('s', 12, 16, 0), 0), + IIO_CHAN_SOFT_TIMESTAMP(12) +}; + +static struct iio_chan_spec adis16350_channels[] = { + IIO_CHAN(IIO_IN, 0, 1, 0, "supply", 0, 0, + (1 << IIO_CHAN_INFO_SCALE_SEPARATE), + 0, ADIS16400_SCAN_SUPPLY, IIO_ST('u', 12, 16, 0), 0), + IIO_CHAN(IIO_GYRO, 1, 0, 0, NULL, 0, IIO_MOD_X, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 1, ADIS16400_SCAN_GYRO_X, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_GYRO, 1, 0, 0, NULL, 0, IIO_MOD_Y, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 2, ADIS16400_SCAN_GYRO_Y, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_GYRO, 1, 0, 0, NULL, 0, IIO_MOD_Z, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 3, ADIS16400_SCAN_GYRO_Z, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_X, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 4, ADIS16400_SCAN_ACC_X, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_Y, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 0, ADIS16400_SCAN_ACC_Y, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_Z, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 0, ADIS16400_SCAN_ACC_Z, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_TEMP, 0, 1, 0, "x", 0, 0, + (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SEPARATE), + 0, ADIS16350_SCAN_TEMP_X, IIO_ST('s', 12, 16, 0), 0), + IIO_CHAN(IIO_TEMP, 0, 1, 0, "y", 1, 0, + (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SEPARATE), + 0, ADIS16350_SCAN_TEMP_Y, IIO_ST('s', 12, 16, 0), 0), + IIO_CHAN(IIO_TEMP, 0, 1, 0, "z", 2, 0, + (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SEPARATE), + 0, ADIS16350_SCAN_TEMP_Z, IIO_ST('s', 12, 16, 0), 0), + IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 1, 0, + (1 << IIO_CHAN_INFO_SCALE_SEPARATE), + 0, ADIS16350_SCAN_ADC_0, IIO_ST('s', 12, 16, 0), 0), + IIO_CHAN_SOFT_TIMESTAMP(11) +}; + +static struct iio_chan_spec adis16300_channels[] = { + IIO_CHAN(IIO_IN, 0, 1, 0, "supply", 0, 0, + (1 << IIO_CHAN_INFO_SCALE_SEPARATE), + 0, ADIS16400_SCAN_SUPPLY, IIO_ST('u', 12, 16, 0), 0), + IIO_CHAN(IIO_GYRO, 1, 0, 0, NULL, 0, IIO_MOD_X, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 1, ADIS16400_SCAN_GYRO_X, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_X, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 4, ADIS16400_SCAN_ACC_X, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_Y, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 0, ADIS16400_SCAN_ACC_Y, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_Z, + (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 0, ADIS16400_SCAN_ACC_Z, IIO_ST('s', 14, 16, 0), 0), + IIO_CHAN(IIO_TEMP, 0, 1, 0, NULL, 0, 0, + (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) | + (1 << IIO_CHAN_INFO_SCALE_SEPARATE), + 0, ADIS16400_SCAN_TEMP, IIO_ST('s', 12, 16, 0), 0), + IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 1, 0, + (1 << IIO_CHAN_INFO_SCALE_SEPARATE), + 0, ADIS16350_SCAN_ADC_0, IIO_ST('s', 12, 16, 0), 0), + IIO_CHAN(IIO_INCLI, 1, 0, 0, NULL, 0, IIO_MOD_X, + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 0, ADIS16300_SCAN_INCLI_X, IIO_ST('s', 13, 16, 0), 0), + IIO_CHAN(IIO_INCLI, 1, 0, 0, NULL, 0, IIO_MOD_Y, + (1 << IIO_CHAN_INFO_SCALE_SHARED), + 0, ADIS16300_SCAN_INCLI_Y, IIO_ST('s', 13, 16, 0), 0), + IIO_CHAN_SOFT_TIMESTAMP(14) }; static struct attribute *adis16400_attributes[] = { - &iio_dev_attr_gyro_x_calibbias.dev_attr.attr, - &iio_dev_attr_gyro_y_calibbias.dev_attr.attr, - &iio_dev_attr_gyro_z_calibbias.dev_attr.attr, - &iio_dev_attr_accel_x_calibbias.dev_attr.attr, - &iio_dev_attr_accel_y_calibbias.dev_attr.attr, - &iio_dev_attr_accel_z_calibbias.dev_attr.attr, - &iio_dev_attr_in0_supply_raw.dev_attr.attr, - &iio_const_attr_in0_supply_scale.dev_attr.attr, - &iio_dev_attr_gyro_x_raw.dev_attr.attr, - &iio_dev_attr_gyro_y_raw.dev_attr.attr, - &iio_dev_attr_gyro_z_raw.dev_attr.attr, - &iio_const_attr_gyro_scale.dev_attr.attr, - &iio_dev_attr_accel_x_raw.dev_attr.attr, - &iio_dev_attr_accel_y_raw.dev_attr.attr, - &iio_dev_attr_accel_z_raw.dev_attr.attr, - &iio_const_attr_accel_scale.dev_attr.attr, - &iio_dev_attr_magn_x_raw.dev_attr.attr, - &iio_dev_attr_magn_y_raw.dev_attr.attr, - &iio_dev_attr_magn_z_raw.dev_attr.attr, - &iio_const_attr_magn_scale.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_in1_raw.dev_attr.attr, - &iio_const_attr_in1_scale.dev_attr.attr, &iio_dev_attr_sampling_frequency.dev_attr.attr, &iio_const_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_reset.dev_attr.attr, - &iio_const_attr_name.dev_attr.attr, NULL }; @@ -597,101 +702,147 @@ 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, + .default_scan_mask = (1 << ADIS16400_SCAN_SUPPLY) | + (1 << ADIS16400_SCAN_GYRO_X) | (1 << ADIS16400_SCAN_ACC_X) | + (1 << ADIS16400_SCAN_ACC_Y) | (1 << ADIS16400_SCAN_ACC_Z) | + (1 << ADIS16400_SCAN_TEMP) | (1 << ADIS16400_SCAN_ADC_0) | + (1 << ADIS16300_SCAN_INCLI_X) | (1 << ADIS16300_SCAN_INCLI_Y) | + (1 << 14), + }, + [ADIS16350] = { + .channels = adis16350_channels, + .num_channels = ARRAY_SIZE(adis16350_channels), + .gyro_scale_micro = 872664, + .accel_scale_micro = 24732, + .default_scan_mask = 0x7FF, + .flags = ADIS16400_NO_BURST, + }, + [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, + .default_scan_mask = 0x7FF, + }, + [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, + .default_scan_mask = 0x7FF, + }, + [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, + .default_scan_mask = 0x7FF, + }, + [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, + .default_scan_mask = 0x7FF, + }, + [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, + .default_scan_mask = 0xFFF, + } +}; + +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, regdone = 0; - struct adis16400_state *st = kzalloc(sizeof *st, GFP_KERNEL); - if (!st) { + struct adis16400_state *st; + struct iio_dev *indio_dev = iio_allocate_device(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, st); + spi_set_drvdata(spi, indio_dev); - /* Allocate the comms buffers */ - st->rx = kzalloc(sizeof(*st->rx)*ADIS16400_MAX_RX, GFP_KERNEL); - if (st->rx == NULL) { - ret = -ENOMEM; - goto error_free_st; - } - st->tx = kzalloc(sizeof(*st->tx)*ADIS16400_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 = &adis16400_event_attribute_group; - st->indio_dev->attrs = &adis16400_attribute_group; - st->indio_dev->dev_data = (void *)(st); - st->indio_dev->driver_module = THIS_MODULE; - st->indio_dev->modes = INDIO_DIRECT_MODE; - - ret = adis16400_configure_ring(st->indio_dev); + /* 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; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis16400_configure_ring(indio_dev); if (ret) goto error_free_dev; - ret = iio_device_register(st->indio_dev); + ret = iio_device_register(indio_dev); if (ret) goto error_unreg_ring_funcs; regdone = 1; - ret = iio_ring_buffer_register(st->indio_dev->ring, 0); + ret = iio_ring_buffer_register_ex(indio_dev->ring, 0, + st->variant->channels, + st->variant->num_channels); if (ret) { - printk(KERN_ERR "failed to initialize the ring\n"); + dev_err(&spi->dev, "failed to initialize the ring\n"); goto error_unreg_ring_funcs; } if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) { - ret = iio_register_interrupt_line(spi->irq, - st->indio_dev, - 0, - IRQF_TRIGGER_RISING, - "adis16400"); + ret = adis16400_probe_trigger(indio_dev); if (ret) goto error_uninitialize_ring; - - ret = adis16400_probe_trigger(st->indio_dev); - if (ret) - goto error_unregister_line; } /* Get the device into a sane initial state */ - ret = adis16400_initial_setup(st); + ret = adis16400_initial_setup(indio_dev); if (ret) goto error_remove_trigger; return 0; error_remove_trigger: - if (st->indio_dev->modes & INDIO_RING_TRIGGERED) - adis16400_remove_trigger(st->indio_dev); -error_unregister_line: - if (st->indio_dev->modes & INDIO_RING_TRIGGERED) - iio_unregister_interrupt_line(st->indio_dev, 0); + if (indio_dev->modes & INDIO_RING_TRIGGERED) + adis16400_remove_trigger(indio_dev); error_uninitialize_ring: - iio_ring_buffer_unregister(st->indio_dev->ring); + iio_ring_buffer_unregister(indio_dev->ring); error_unreg_ring_funcs: - adis16400_unconfigure_ring(st->indio_dev); + adis16400_unconfigure_ring(indio_dev); error_free_dev: if (regdone) - iio_device_unregister(st->indio_dev); + iio_device_unregister(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); + iio_free_device(indio_dev); error_ret: return ret; } @@ -700,25 +851,16 @@ error_ret: static int adis16400_remove(struct spi_device *spi) { int ret; - struct adis16400_state *st = spi_get_drvdata(spi); - struct iio_dev *indio_dev = st->indio_dev; + struct iio_dev *indio_dev = spi_get_drvdata(spi); - ret = adis16400_stop_device(&(indio_dev->dev)); + ret = adis16400_stop_device(indio_dev); if (ret) goto err_ret; - flush_scheduled_work(); - adis16400_remove_trigger(indio_dev); - if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) - iio_unregister_interrupt_line(indio_dev, 0); - - iio_ring_buffer_unregister(st->indio_dev->ring); + iio_ring_buffer_unregister(indio_dev->ring); adis16400_unconfigure_ring(indio_dev); iio_device_unregister(indio_dev); - kfree(st->tx); - kfree(st->rx); - kfree(st); return 0; @@ -726,11 +868,26 @@ err_ret: return ret; } +static const struct spi_device_id adis16400_id[] = { + {"adis16300", ADIS16300}, + {"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), }; |