mirror of https://gitee.com/openkylin/linux.git
Fifth round of new IIO drivers, cleanups and fixes for the 3.8 cycle.
Here we have a number of minor fixes. * a quirk for the hid sensor driver should be a boolean option. * return an error for a failed memdup in the hid sensor driver. * Fix a return value in adt7410. * A double free in the IIO event infrastructure. * Disabling interrupts in tsl2563 was wrong (never been correct!) * Incorrect signature for the iio_buffer_register stub * Incorrect return for isl29018 write_raw callback. * A number of minor fixes as part of the various rework series. New drivers and major rework. * Introduce and use extensively an adis library for numerous spi Analog Devices mems sensors. This then moves out of staging. * Lots of new stuff then added to this library to support newer sensors. * New drivers for ADIS16136 and ADIS16480 and similar. * Core support for barometric pressure sensors. * ad7298 cleanup and move out of staging. The bulk of this is from Lars-Peter Clausen. He's been rather busy! -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iQIcBAABAgAGBQJQrR8KAAoJEFSFNJnE9BaIzmAP/0QawDmjpYsPRRDn8Ou758ri Vx4wC4Xp7g8eYysMTK/R5ScAnDSVfvF21aWppCdr/LbZhg6dTw+sDR0BlE775NbA BveAXFuGOfdDFkkMgtw+Se7Ek00uqZy5chdkrT09SmYX3592HyN9CYacF3f1EHiS GXEUHW7gau0MHk988HCj2GhoradSkNiB6L0585oadNozcmesMZmvqFxEoel+g+DJ 0JQ+KTbheLfriSDvNMD00XuEzuoLCfBlqE6Ij24fl3UTpRY/3JJqp2ddXzSO9YMM RBC4x5RGOltLYsgPmAuvZCV6T8k7rfjQDmsjmNwTrSvYkKA+0Uzj2OhNRi9Zam8z HGMuppJPhzSaR8iVOMzj5JVIs0MkjemXK/OYVvNnoCY7dDCDK66dlYXWM/L6IYDN atHOX98p56qaQpuSkZd+wNxjD0oNo3oPas+vfecMQ+zNuAhJFwh4IsQOmTzwrtz4 Wms6Gm8P5lR9WqPiCGD3p5gKVT0gDcJrmgrKSYL6eWguHsCoXeY3IlnW355xIv44 ciX/siRUvZxBr25cpGjL0E9xyZNZ7CiXowNHbuyMbxtF/5dfKdzGQppxLA8nPz+c aIZRgVvqaoeHaAB+nhb+67rriotgY1TyP4ZuvufQYqo0BMXUo9pT05a84tSXVOo4 +wBiuhTJU+c+3e/9Oka+ =SCgj -----END PGP SIGNATURE----- Merge tag 'iio-for-3.8e' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next From Jonathan: "Fifth round of new IIO drivers, cleanups and fixes for the 3.8 cycle. Here we have a number of minor fixes. * a quirk for the hid sensor driver should be a boolean option. * return an error for a failed memdup in the hid sensor driver. * Fix a return value in adt7410. * A double free in the IIO event infrastructure. * Disabling interrupts in tsl2563 was wrong (never been correct!) * Incorrect signature for the iio_buffer_register stub * Incorrect return for isl29018 write_raw callback. * A number of minor fixes as part of the various rework series. New drivers and major rework. * Introduce and use extensively an adis library for numerous spi Analog Devices mems sensors. This then moves out of staging. * Lots of new stuff then added to this library to support newer sensors. * New drivers for ADIS16136 and ADIS16480 and similar. * Core support for barometric pressure sensors. * ad7298 cleanup and move out of staging. The bulk of this is from Lars-Peter Clausen. He's been rather busy!"
This commit is contained in:
commit
3ce14acb90
|
@ -189,6 +189,14 @@ Description:
|
|||
A computed peak value based on the sum squared magnitude of
|
||||
the underlying value in the specified directions.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_raw
|
||||
KernelVersion: 3.8
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw pressure measurement from channel Y. Units after
|
||||
application of scale and offset are kilopascal.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
|
||||
|
@ -197,6 +205,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_offset
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_offset
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -226,6 +236,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_magn_scale
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -245,6 +257,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibbias
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibbias
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibbias
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibbias
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibbias
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibbias
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -262,6 +276,8 @@ What /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
|
|||
What /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
|
||||
what /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibscale
|
||||
what /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -275,6 +291,8 @@ What: /sys/.../iio:deviceX/in_voltage-voltage_scale_available
|
|||
What: /sys/.../iio:deviceX/out_voltageX_scale_available
|
||||
What: /sys/.../iio:deviceX/out_altvoltageX_scale_available
|
||||
What: /sys/.../iio:deviceX/in_capacitance_scale_available
|
||||
What: /sys/.../iio:deviceX/in_pressure_scale_available
|
||||
What: /sys/.../iio:deviceX/in_pressureY_scale_available
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -694,6 +712,8 @@ What: /sys/.../buffer/scan_elements/in_voltageY_en
|
|||
What: /sys/.../buffer/scan_elements/in_voltageY-voltageZ_en
|
||||
What: /sys/.../buffer/scan_elements/in_incli_x_en
|
||||
What: /sys/.../buffer/scan_elements/in_incli_y_en
|
||||
What: /sys/.../buffer/scan_elements/in_pressureY_en
|
||||
What: /sys/.../buffer/scan_elements/in_pressure_en
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -707,6 +727,8 @@ What: /sys/.../buffer/scan_elements/in_voltageY_type
|
|||
What: /sys/.../buffer/scan_elements/in_voltage_type
|
||||
What: /sys/.../buffer/scan_elements/in_voltageY_supply_type
|
||||
What: /sys/.../buffer/scan_elements/in_timestamp_type
|
||||
What: /sys/.../buffer/scan_elements/in_pressureY_type
|
||||
What: /sys/.../buffer/scan_elements/in_pressure_type
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -751,6 +773,8 @@ What: /sys/.../buffer/scan_elements/in_magn_z_index
|
|||
What: /sys/.../buffer/scan_elements/in_incli_x_index
|
||||
What: /sys/.../buffer/scan_elements/in_incli_y_index
|
||||
What: /sys/.../buffer/scan_elements/in_timestamp_index
|
||||
What: /sys/.../buffer/scan_elements/in_pressureY_index
|
||||
What: /sys/.../buffer/scan_elements/in_pressure_index
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
|
|
@ -63,11 +63,12 @@ config IIO_CONSUMERS_PER_TRIGGER
|
|||
source "drivers/iio/accel/Kconfig"
|
||||
source "drivers/iio/adc/Kconfig"
|
||||
source "drivers/iio/amplifiers/Kconfig"
|
||||
source "drivers/iio/light/Kconfig"
|
||||
source "drivers/iio/frequency/Kconfig"
|
||||
source "drivers/iio/dac/Kconfig"
|
||||
source "drivers/iio/common/Kconfig"
|
||||
source "drivers/iio/dac/Kconfig"
|
||||
source "drivers/iio/frequency/Kconfig"
|
||||
source "drivers/iio/gyro/Kconfig"
|
||||
source "drivers/iio/imu/Kconfig"
|
||||
source "drivers/iio/light/Kconfig"
|
||||
source "drivers/iio/magnetometer/Kconfig"
|
||||
|
||||
endif # IIO
|
||||
|
|
|
@ -14,9 +14,10 @@ obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
|
|||
obj-y += accel/
|
||||
obj-y += adc/
|
||||
obj-y += amplifiers/
|
||||
obj-y += light/
|
||||
obj-y += frequency/
|
||||
obj-y += dac/
|
||||
obj-y += common/
|
||||
obj-y += dac/
|
||||
obj-y += gyro/
|
||||
obj-y += frequency/
|
||||
obj-y += imu/
|
||||
obj-y += light/
|
||||
obj-y += magnetometer/
|
||||
|
|
|
@ -306,10 +306,10 @@ static int __devinit hid_accel_3d_probe(struct platform_device *pdev)
|
|||
goto error_free_dev;
|
||||
}
|
||||
|
||||
channels = kmemdup(accel_3d_channels,
|
||||
sizeof(accel_3d_channels),
|
||||
GFP_KERNEL);
|
||||
channels = kmemdup(accel_3d_channels, sizeof(accel_3d_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
goto error_free_dev;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,18 @@ config AD7266
|
|||
Say yes here to build support for Analog Devices AD7265 and AD7266
|
||||
ADCs.
|
||||
|
||||
config AD7298
|
||||
tristate "Analog Devices AD7298 ADC driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7298
|
||||
8 Channel ADC with temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7298.
|
||||
|
||||
config AD7791
|
||||
tristate "Analog Devices AD7791 ADC driver"
|
||||
depends on SPI
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
|
||||
obj-$(CONFIG_AD7266) += ad7266.o
|
||||
obj-$(CONFIG_AD7298) += ad7298.o
|
||||
obj-$(CONFIG_AD7476) += ad7476.o
|
||||
obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
|
|
|
@ -15,12 +15,48 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include "ad7298.h"
|
||||
#include <linux/platform_data/ad7298.h>
|
||||
|
||||
#define AD7298_WRITE (1 << 15) /* write to the control register */
|
||||
#define AD7298_REPEAT (1 << 14) /* repeated conversion enable */
|
||||
#define AD7298_CH(x) (1 << (13 - (x))) /* channel select */
|
||||
#define AD7298_TSENSE (1 << 5) /* temperature conversion enable */
|
||||
#define AD7298_EXTREF (1 << 2) /* external reference enable */
|
||||
#define AD7298_TAVG (1 << 1) /* temperature sensor averaging enable */
|
||||
#define AD7298_PDD (1 << 0) /* partial power down enable */
|
||||
|
||||
#define AD7298_MAX_CHAN 8
|
||||
#define AD7298_BITS 12
|
||||
#define AD7298_STORAGE_BITS 16
|
||||
#define AD7298_INTREF_mV 2500
|
||||
|
||||
#define AD7298_CH_TEMP 9
|
||||
|
||||
#define RES_MASK(bits) ((1 << (bits)) - 1)
|
||||
|
||||
struct ad7298_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
unsigned ext_ref;
|
||||
struct spi_transfer ring_xfer[10];
|
||||
struct spi_transfer scan_single_xfer[3];
|
||||
struct spi_message ring_msg;
|
||||
struct spi_message scan_single_msg;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 rx_buf[12] ____cacheline_aligned;
|
||||
__be16 tx_buf[2];
|
||||
};
|
||||
|
||||
#define AD7298_V_CHAN(index) \
|
||||
{ \
|
||||
|
@ -35,6 +71,7 @@
|
|||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
|
@ -44,7 +81,8 @@ static const struct iio_chan_spec ad7298_channels[] = {
|
|||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.address = AD7298_CH_TEMP,
|
||||
.scan_index = -1,
|
||||
.scan_type = {
|
||||
|
@ -64,6 +102,84 @@ static const struct iio_chan_spec ad7298_channels[] = {
|
|||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
};
|
||||
|
||||
/**
|
||||
* ad7298_update_scan_mode() setup the spi transfer buffer for the new scan mask
|
||||
**/
|
||||
static int ad7298_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *active_scan_mask)
|
||||
{
|
||||
struct ad7298_state *st = iio_priv(indio_dev);
|
||||
int i, m;
|
||||
unsigned short command;
|
||||
int scan_count;
|
||||
|
||||
/* Now compute overall size */
|
||||
scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength);
|
||||
|
||||
command = AD7298_WRITE | st->ext_ref;
|
||||
|
||||
for (i = 0, m = AD7298_CH(0); i < AD7298_MAX_CHAN; i++, m >>= 1)
|
||||
if (test_bit(i, active_scan_mask))
|
||||
command |= m;
|
||||
|
||||
st->tx_buf[0] = cpu_to_be16(command);
|
||||
|
||||
/* build spi ring message */
|
||||
st->ring_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->ring_xfer[0].len = 2;
|
||||
st->ring_xfer[0].cs_change = 1;
|
||||
st->ring_xfer[1].tx_buf = &st->tx_buf[1];
|
||||
st->ring_xfer[1].len = 2;
|
||||
st->ring_xfer[1].cs_change = 1;
|
||||
|
||||
spi_message_init(&st->ring_msg);
|
||||
spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
|
||||
spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg);
|
||||
|
||||
for (i = 0; i < scan_count; i++) {
|
||||
st->ring_xfer[i + 2].rx_buf = &st->rx_buf[i];
|
||||
st->ring_xfer[i + 2].len = 2;
|
||||
st->ring_xfer[i + 2].cs_change = 1;
|
||||
spi_message_add_tail(&st->ring_xfer[i + 2], &st->ring_msg);
|
||||
}
|
||||
/* make sure last transfer cs_change is not set */
|
||||
st->ring_xfer[i + 1].cs_change = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7298_trigger_handler() bh of trigger launched polling to ring buffer
|
||||
*
|
||||
* Currently there is no option in this driver to disable the saving of
|
||||
* timestamps within the ring.
|
||||
**/
|
||||
static irqreturn_t ad7298_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7298_state *st = iio_priv(indio_dev);
|
||||
s64 time_ns = 0;
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
if (indio_dev->scan_timestamp) {
|
||||
time_ns = iio_get_time_ns();
|
||||
memcpy((u8 *)st->rx_buf + indio_dev->scan_bytes - sizeof(s64),
|
||||
&time_ns, sizeof(time_ns));
|
||||
}
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)st->rx_buf);
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad7298_scan_direct(struct ad7298_state *st, unsigned ch)
|
||||
{
|
||||
int ret;
|
||||
|
@ -79,7 +195,7 @@ static int ad7298_scan_direct(struct ad7298_state *st, unsigned ch)
|
|||
|
||||
static int ad7298_scan_temp(struct ad7298_state *st, int *val)
|
||||
{
|
||||
int tmp, ret;
|
||||
int ret;
|
||||
__be16 buf;
|
||||
|
||||
buf = cpu_to_be16(AD7298_WRITE | AD7298_TSENSE |
|
||||
|
@ -101,26 +217,26 @@ static int ad7298_scan_temp(struct ad7298_state *st, int *val)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
tmp = be16_to_cpu(buf) & RES_MASK(AD7298_BITS);
|
||||
|
||||
/*
|
||||
* One LSB of the ADC corresponds to 0.25 deg C.
|
||||
* The temperature reading is in 12-bit twos complement format
|
||||
*/
|
||||
|
||||
if (tmp & (1 << (AD7298_BITS - 1))) {
|
||||
tmp = (4096 - tmp) * 250;
|
||||
tmp -= (2 * tmp);
|
||||
|
||||
} else {
|
||||
tmp *= 250; /* temperature in milli degrees Celsius */
|
||||
}
|
||||
|
||||
*val = tmp;
|
||||
*val = sign_extend32(be16_to_cpu(buf), 11);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7298_get_ref_voltage(struct ad7298_state *st)
|
||||
{
|
||||
int vref;
|
||||
|
||||
if (st->ext_ref) {
|
||||
vref = regulator_get_voltage(st->reg);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
return vref / 1000;
|
||||
} else {
|
||||
return AD7298_INTREF_mV;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad7298_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
|
@ -129,7 +245,6 @@ static int ad7298_read_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
int ret;
|
||||
struct ad7298_state *st = iio_priv(indio_dev);
|
||||
unsigned int scale_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
@ -154,17 +269,19 @@ static int ad7298_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
scale_uv = (st->int_vref_mv * 1000) >> AD7298_BITS;
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = (scale_uv % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = ad7298_get_ref_voltage(st);
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_TEMP:
|
||||
*val = 1;
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = ad7298_get_ref_voltage(st);
|
||||
*val2 = 10;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 1093 - 2732500 / ad7298_get_ref_voltage(st);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -179,16 +296,23 @@ static int __devinit ad7298_probe(struct spi_device *spi)
|
|||
{
|
||||
struct ad7298_platform_data *pdata = spi->dev.platform_data;
|
||||
struct ad7298_state *st;
|
||||
int ret;
|
||||
struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st));
|
||||
int ret;
|
||||
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->reg = regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
if (pdata && pdata->ext_ref)
|
||||
st->ext_ref = AD7298_EXTREF;
|
||||
|
||||
if (st->ext_ref) {
|
||||
st->reg = regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->reg)) {
|
||||
ret = PTR_ERR(st->reg);
|
||||
goto error_free;
|
||||
}
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
goto error_put_reg;
|
||||
|
@ -221,14 +345,8 @@ static int __devinit ad7298_probe(struct spi_device *spi)
|
|||
spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg);
|
||||
spi_message_add_tail(&st->scan_single_xfer[2], &st->scan_single_msg);
|
||||
|
||||
if (pdata && pdata->vref_mv) {
|
||||
st->int_vref_mv = pdata->vref_mv;
|
||||
st->ext_ref = AD7298_EXTREF;
|
||||
} else {
|
||||
st->int_vref_mv = AD7298_INTREF_mV;
|
||||
}
|
||||
|
||||
ret = ad7298_register_ring_funcs_and_init(indio_dev);
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad7298_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
|
@ -239,13 +357,14 @@ static int __devinit ad7298_probe(struct spi_device *spi)
|
|||
return 0;
|
||||
|
||||
error_cleanup_ring:
|
||||
ad7298_ring_cleanup(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
if (st->ext_ref)
|
||||
regulator_disable(st->reg);
|
||||
error_put_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
if (st->ext_ref)
|
||||
regulator_put(st->reg);
|
||||
error_free:
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return ret;
|
||||
|
@ -257,8 +376,8 @@ static int __devexit ad7298_remove(struct spi_device *spi)
|
|||
struct ad7298_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
ad7298_ring_cleanup(indio_dev);
|
||||
if (!IS_ERR(st->reg)) {
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (st->ext_ref) {
|
||||
regulator_disable(st->reg);
|
||||
regulator_put(st->reg);
|
||||
}
|
|
@ -15,7 +15,7 @@ config HID_SENSOR_IIO_COMMON
|
|||
attributes.
|
||||
|
||||
config HID_SENSOR_ENUM_BASE_QUIRKS
|
||||
tristate "ENUM base quirks for HID Sensor IIO drivers"
|
||||
bool "ENUM base quirks for HID Sensor IIO drivers"
|
||||
depends on HID_SENSOR_IIO_COMMON
|
||||
help
|
||||
Say yes here to build support for sensor hub FW using
|
||||
|
|
|
@ -36,10 +36,8 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
|||
int state_val;
|
||||
|
||||
state_val = state ? 1 : 0;
|
||||
#if (defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS) || \
|
||||
(defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS_MODULE)
|
||||
++state_val;
|
||||
#endif
|
||||
if (IS_ENABLED(CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS))
|
||||
++state_val;
|
||||
st->data_ready = state;
|
||||
sensor_hub_set_feature(st->hsdev, st->power_state.report_id,
|
||||
st->power_state.index,
|
||||
|
|
|
@ -3,6 +3,15 @@
|
|||
#
|
||||
menu "Digital gyroscope sensors"
|
||||
|
||||
config ADIS16136
|
||||
tristate "Analog devices ADIS16136 and similar gyroscopes driver"
|
||||
depends on SPI_MASTER
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for the Analog Devices ADIS16133, ADIS16135,
|
||||
ADIS16136 gyroscope devices.
|
||||
|
||||
config HID_SENSOR_GYRO_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
|
|
@ -2,4 +2,5 @@
|
|||
# Makefile for industrial I/O gyroscope sensor drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ADIS16136) += adis16136.o
|
||||
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
|
||||
|
|
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
* ADIS16133/ADIS16135/ADIS16136 gyroscope driver
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#define ADIS16136_REG_FLASH_CNT 0x00
|
||||
#define ADIS16136_REG_TEMP_OUT 0x02
|
||||
#define ADIS16136_REG_GYRO_OUT2 0x04
|
||||
#define ADIS16136_REG_GYRO_OUT 0x06
|
||||
#define ADIS16136_REG_GYRO_OFF2 0x08
|
||||
#define ADIS16136_REG_GYRO_OFF 0x0A
|
||||
#define ADIS16136_REG_ALM_MAG1 0x10
|
||||
#define ADIS16136_REG_ALM_MAG2 0x12
|
||||
#define ADIS16136_REG_ALM_SAMPL1 0x14
|
||||
#define ADIS16136_REG_ALM_SAMPL2 0x16
|
||||
#define ADIS16136_REG_ALM_CTRL 0x18
|
||||
#define ADIS16136_REG_GPIO_CTRL 0x1A
|
||||
#define ADIS16136_REG_MSC_CTRL 0x1C
|
||||
#define ADIS16136_REG_SMPL_PRD 0x1E
|
||||
#define ADIS16136_REG_AVG_CNT 0x20
|
||||
#define ADIS16136_REG_DEC_RATE 0x22
|
||||
#define ADIS16136_REG_SLP_CTRL 0x24
|
||||
#define ADIS16136_REG_DIAG_STAT 0x26
|
||||
#define ADIS16136_REG_GLOB_CMD 0x28
|
||||
#define ADIS16136_REG_LOT1 0x32
|
||||
#define ADIS16136_REG_LOT2 0x34
|
||||
#define ADIS16136_REG_LOT3 0x36
|
||||
#define ADIS16136_REG_PROD_ID 0x38
|
||||
#define ADIS16136_REG_SERIAL_NUM 0x3A
|
||||
|
||||
#define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL 2
|
||||
#define ADIS16136_DIAG_STAT_SPI_FAIL 3
|
||||
#define ADIS16136_DIAG_STAT_SELF_TEST_FAIL 5
|
||||
#define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL 6
|
||||
|
||||
#define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11)
|
||||
#define ADIS16136_MSC_CTRL_SELF_TEST BIT(10)
|
||||
|
||||
struct adis16136_chip_info {
|
||||
unsigned int precision;
|
||||
unsigned int fullscale;
|
||||
};
|
||||
|
||||
struct adis16136 {
|
||||
const struct adis16136_chip_info *chip_info;
|
||||
|
||||
struct adis adis;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static ssize_t adis16136_show_serial(struct file *file,
|
||||
char __user *userbuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct adis16136 *adis16136 = file->private_data;
|
||||
uint16_t lot1, lot2, lot3, serial;
|
||||
char buf[20];
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SERIAL_NUM,
|
||||
&serial);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%.4x%.4x%.4x-%.4x\n", lot1, lot2,
|
||||
lot3, serial);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static const struct file_operations adis16136_serial_fops = {
|
||||
.open = simple_open,
|
||||
.read = adis16136_show_serial,
|
||||
.llseek = default_llseek,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adis16136_show_product_id(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16136 *adis16136 = arg;
|
||||
u16 prod_id;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
|
||||
&prod_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = prod_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16136_product_id_fops,
|
||||
adis16136_show_product_id, NULL, "%llu\n");
|
||||
|
||||
static int adis16136_show_flash_count(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16136 *adis16136 = arg;
|
||||
uint16_t flash_count;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_FLASH_CNT,
|
||||
&flash_count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = flash_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16136_flash_count_fops,
|
||||
adis16136_show_flash_count, NULL, "%lld\n");
|
||||
|
||||
static int adis16136_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
|
||||
debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
|
||||
adis16136, &adis16136_serial_fops);
|
||||
debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
|
||||
adis16136, &adis16136_product_id_fops);
|
||||
debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
|
||||
adis16136, &adis16136_flash_count_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int adis16136_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq)
|
||||
{
|
||||
unsigned int t;
|
||||
|
||||
t = 32768 / freq;
|
||||
if (t < 0xf)
|
||||
t = 0xf;
|
||||
else if (t > 0xffff)
|
||||
t = 0xffff;
|
||||
else
|
||||
t--;
|
||||
|
||||
return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t);
|
||||
}
|
||||
|
||||
static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq)
|
||||
{
|
||||
uint16_t t;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*freq = 32768 / (t + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t adis16136_write_frequency(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val == 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = adis16136_set_freq(adis16136, val);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static ssize_t adis16136_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
unsigned int freq;
|
||||
int ret;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", freq);
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
adis16136_read_frequency,
|
||||
adis16136_write_frequency);
|
||||
|
||||
static const unsigned adis16136_3db_divisors[] = {
|
||||
[0] = 2, /* Special case */
|
||||
[1] = 6,
|
||||
[2] = 12,
|
||||
[3] = 25,
|
||||
[4] = 50,
|
||||
[5] = 100,
|
||||
[6] = 200,
|
||||
[7] = 200, /* Not a valid setting */
|
||||
};
|
||||
|
||||
static int adis16136_set_filter(struct iio_dev *indio_dev, int val)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
unsigned int freq;
|
||||
int i, ret;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) {
|
||||
if (freq / adis16136_3db_divisors[i] >= val)
|
||||
break;
|
||||
}
|
||||
|
||||
return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i);
|
||||
}
|
||||
|
||||
static int adis16136_get_filter(struct iio_dev *indio_dev, int *val)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
unsigned int freq;
|
||||
uint16_t val16;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
*val = freq / adis16136_3db_divisors[val16 & 0x07];
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int adis16136_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
uint32_t val32;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return adis_single_conversion(indio_dev, chan, 0, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = adis16136->chip_info->precision;
|
||||
*val2 = (adis16136->chip_info->fullscale << 16);
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_TEMP:
|
||||
*val = 10;
|
||||
*val2 = 697000; /* 0.010697 degree Celsius */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = adis_read_reg_32(&adis16136->adis,
|
||||
ADIS16136_REG_GYRO_OFF2, &val32);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(val32, 31);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
return adis16136_get_filter(indio_dev, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int adis16136_write_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int val, int val2, long info)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
return adis_write_reg_32(&adis16136->adis,
|
||||
ADIS16136_REG_GYRO_OFF2, val);
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
return adis16136_set_filter(indio_dev, val);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
enum {
|
||||
ADIS16136_SCAN_GYRO,
|
||||
ADIS16136_SCAN_TEMP,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16136_channels[] = {
|
||||
{
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT,
|
||||
.address = ADIS16136_REG_GYRO_OUT2,
|
||||
.scan_index = ADIS16136_SCAN_GYRO,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 32,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = ADIS16136_REG_TEMP_OUT,
|
||||
.scan_index = ADIS16136_SCAN_TEMP,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static struct attribute *adis16136_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group adis16136_attribute_group = {
|
||||
.attrs = adis16136_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info adis16136_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &adis16136_attribute_group,
|
||||
.read_raw = &adis16136_read_raw,
|
||||
.write_raw = &adis16136_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.debugfs_reg_access = adis_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static int adis16136_stop_device(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SLP_CTRL, 0xff);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev,
|
||||
"Could not power down device: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16136_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
unsigned int device_id;
|
||||
uint16_t prod_id;
|
||||
int ret;
|
||||
|
||||
ret = adis_initial_startup(&adis16136->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
|
||||
&prod_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sscanf(indio_dev->name, "adis%u\n", &device_id);
|
||||
|
||||
if (prod_id != device_id)
|
||||
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
|
||||
device_id, prod_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const adis16136_status_error_msgs[] = {
|
||||
[ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL] = "Flash update failed",
|
||||
[ADIS16136_DIAG_STAT_SPI_FAIL] = "SPI failure",
|
||||
[ADIS16136_DIAG_STAT_SELF_TEST_FAIL] = "Self test error",
|
||||
[ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16136_data = {
|
||||
.diag_stat_reg = ADIS16136_REG_DIAG_STAT,
|
||||
.glob_cmd_reg = ADIS16136_REG_GLOB_CMD,
|
||||
.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL,
|
||||
|
||||
.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST,
|
||||
.startup_delay = 80,
|
||||
|
||||
.read_delay = 10,
|
||||
.write_delay = 10,
|
||||
|
||||
.status_error_msgs = adis16136_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) |
|
||||
BIT(ADIS16136_DIAG_STAT_SPI_FAIL) |
|
||||
BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) |
|
||||
BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL),
|
||||
};
|
||||
|
||||
enum adis16136_id {
|
||||
ID_ADIS16133,
|
||||
ID_ADIS16135,
|
||||
ID_ADIS16136,
|
||||
};
|
||||
|
||||
static const struct adis16136_chip_info adis16136_chip_info[] = {
|
||||
[ID_ADIS16133] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(1200),
|
||||
.fullscale = 24000,
|
||||
},
|
||||
[ID_ADIS16135] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(300),
|
||||
.fullscale = 24000,
|
||||
},
|
||||
[ID_ADIS16136] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(450),
|
||||
.fullscale = 24623,
|
||||
},
|
||||
};
|
||||
|
||||
static int adis16136_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct adis16136 *adis16136;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*adis16136));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
adis16136 = iio_priv(indio_dev);
|
||||
|
||||
adis16136->chip_info = &adis16136_chip_info[id->driver_data];
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->channels = adis16136_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adis16136_channels);
|
||||
indio_dev->info = &adis16136_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis_init(&adis16136->adis, indio_dev, spi, &adis16136_data);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = adis_setup_buffer_and_trigger(&adis16136->adis, indio_dev, NULL);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = adis16136_initial_setup(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_stop_device;
|
||||
|
||||
adis16136_debugfs_init(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
error_stop_device:
|
||||
adis16136_stop_device(indio_dev);
|
||||
error_cleanup_buffer:
|
||||
adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16136_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16136_stop_device(indio_dev);
|
||||
|
||||
adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
|
||||
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adis16136_ids[] = {
|
||||
{ "adis16133", ID_ADIS16133 },
|
||||
{ "adis16135", ID_ADIS16135 },
|
||||
{ "adis16136", ID_ADIS16136 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16136_ids);
|
||||
|
||||
static struct spi_driver adis16136_driver = {
|
||||
.driver = {
|
||||
.name = "adis16136",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = adis16136_ids,
|
||||
.probe = adis16136_probe,
|
||||
.remove = adis16136_remove,
|
||||
};
|
||||
module_spi_driver(adis16136_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -306,10 +306,10 @@ static int __devinit hid_gyro_3d_probe(struct platform_device *pdev)
|
|||
goto error_free_dev;
|
||||
}
|
||||
|
||||
channels = kmemdup(gyro_3d_channels,
|
||||
sizeof(gyro_3d_channels),
|
||||
GFP_KERNEL);
|
||||
channels = kmemdup(gyro_3d_channels, sizeof(gyro_3d_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
goto error_free_dev;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# IIO imu drivers configuration
|
||||
#
|
||||
menu "Inertial measurement units"
|
||||
|
||||
config ADIS16480
|
||||
tristate "Analog Devices ADIS16480 and similar IMU driver"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADIS16375, ADIS16480,
|
||||
ADIS16485, ADIS16488 inertial sensors.
|
||||
|
||||
endmenu
|
||||
|
||||
config IIO_ADIS_LIB
|
||||
tristate
|
||||
help
|
||||
A set of IO helper functions for the Analog Devices ADIS* device family.
|
||||
|
||||
config IIO_ADIS_LIB_BUFFER
|
||||
bool
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
A set of buffer helper functions for the Analog Devices ADIS* device
|
||||
family.
|
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# Makefile for Inertial Measurement Units
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ADIS16480) += adis16480.o
|
||||
|
||||
adis_lib-y += adis.o
|
||||
adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o
|
||||
adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
|
||||
obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
|
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
* Common library for ADIS16XXX devices
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#define ADIS_MSC_CTRL_DATA_RDY_EN BIT(2)
|
||||
#define ADIS_MSC_CTRL_DATA_RDY_POL_HIGH BIT(1)
|
||||
#define ADIS_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
|
||||
#define ADIS_GLOB_CMD_SW_RESET BIT(7)
|
||||
|
||||
int adis_write_reg(struct adis *adis, unsigned int reg,
|
||||
unsigned int value, unsigned int size)
|
||||
{
|
||||
unsigned int page = reg / ADIS_PAGE_SIZE;
|
||||
int ret, i;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = adis->tx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 2,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 4,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 6,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 8,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&adis->txrx_lock);
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
if (adis->current_page != page) {
|
||||
adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
|
||||
adis->tx[1] = page;
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
}
|
||||
|
||||
switch (size) {
|
||||
case 4:
|
||||
adis->tx[8] = ADIS_WRITE_REG(reg + 3);
|
||||
adis->tx[9] = (value >> 24) & 0xff;
|
||||
adis->tx[6] = ADIS_WRITE_REG(reg + 2);
|
||||
adis->tx[7] = (value >> 16) & 0xff;
|
||||
case 2:
|
||||
adis->tx[4] = ADIS_WRITE_REG(reg + 1);
|
||||
adis->tx[5] = (value >> 8) & 0xff;
|
||||
case 1:
|
||||
adis->tx[2] = ADIS_WRITE_REG(reg);
|
||||
adis->tx[3] = value & 0xff;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
xfers[size].cs_change = 0;
|
||||
|
||||
for (i = 1; i <= size; i++)
|
||||
spi_message_add_tail(&xfers[i], &msg);
|
||||
|
||||
ret = spi_sync(adis->spi, &msg);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n",
|
||||
reg, ret);
|
||||
} else {
|
||||
adis->current_page = page;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&adis->txrx_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_write_reg);
|
||||
|
||||
/**
|
||||
* adis_read_reg() - read 2 bytes from a 16-bit register
|
||||
* @adis: The adis device
|
||||
* @reg: The address of the lower of the two registers
|
||||
* @val: The value read back from the device
|
||||
*/
|
||||
int adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
unsigned int *val, unsigned int size)
|
||||
{
|
||||
unsigned int page = reg / ADIS_PAGE_SIZE;
|
||||
struct spi_message msg;
|
||||
int ret;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = adis->tx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 2,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->read_delay,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 4,
|
||||
.rx_buf = adis->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->read_delay,
|
||||
}, {
|
||||
.rx_buf = adis->rx + 2,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = adis->data->read_delay,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&adis->txrx_lock);
|
||||
spi_message_init(&msg);
|
||||
|
||||
if (adis->current_page != page) {
|
||||
adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
|
||||
adis->tx[1] = page;
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
}
|
||||
|
||||
switch (size) {
|
||||
case 4:
|
||||
adis->tx[2] = ADIS_READ_REG(reg + 2);
|
||||
adis->tx[3] = 0;
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
case 2:
|
||||
adis->tx[4] = ADIS_READ_REG(reg);
|
||||
adis->tx[5] = 0;
|
||||
spi_message_add_tail(&xfers[2], &msg);
|
||||
spi_message_add_tail(&xfers[3], &msg);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = spi_sync(adis->spi, &msg);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
|
||||
reg, ret);
|
||||
goto out_unlock;
|
||||
} else {
|
||||
adis->current_page = page;
|
||||
}
|
||||
|
||||
switch (size) {
|
||||
case 4:
|
||||
*val = get_unaligned_be32(adis->rx);
|
||||
break;
|
||||
case 2:
|
||||
*val = get_unaligned_be16(adis->rx + 2);
|
||||
break;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&adis->txrx_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_read_reg);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
int adis_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg, unsigned int writeval, unsigned int *readval)
|
||||
{
|
||||
struct adis *adis = iio_device_get_drvdata(indio_dev);
|
||||
|
||||
if (readval) {
|
||||
uint16_t val16;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(adis, reg, &val16);
|
||||
*readval = val16;
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
return adis_write_reg_16(adis, reg, writeval);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(adis_debugfs_reg_access);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* adis_enable_irq() - Enable or disable data ready IRQ
|
||||
* @adis: The adis device
|
||||
* @enable: Whether to enable the IRQ
|
||||
*
|
||||
* Returns 0 on success, negative error code otherwise
|
||||
*/
|
||||
int adis_enable_irq(struct adis *adis, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
uint16_t msc;
|
||||
|
||||
if (adis->data->enable_irq)
|
||||
return adis->data->enable_irq(adis, enable);
|
||||
|
||||
ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH;
|
||||
msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2;
|
||||
if (enable)
|
||||
msc |= ADIS_MSC_CTRL_DATA_RDY_EN;
|
||||
else
|
||||
msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN;
|
||||
|
||||
ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc);
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(adis_enable_irq);
|
||||
|
||||
/**
|
||||
* adis_check_status() - Check the device for error conditions
|
||||
* @adis: The adis device
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int adis_check_status(struct adis *adis)
|
||||
{
|
||||
uint16_t status;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = adis_read_reg_16(adis, adis->data->diag_stat_reg, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
status &= adis->data->status_error_mask;
|
||||
|
||||
if (status == 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < 16; ++i) {
|
||||
if (status & BIT(i)) {
|
||||
dev_err(&adis->spi->dev, "%s.\n",
|
||||
adis->data->status_error_msgs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_check_status);
|
||||
|
||||
/**
|
||||
* adis_reset() - Reset the device
|
||||
* @adis: The adis device
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int adis_reset(struct adis *adis)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = adis_write_reg_8(adis, adis->data->glob_cmd_reg,
|
||||
ADIS_GLOB_CMD_SW_RESET);
|
||||
if (ret)
|
||||
dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_reset);
|
||||
|
||||
static int adis_self_test(struct adis *adis)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg,
|
||||
adis->data->self_test_mask);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(adis->data->startup_delay);
|
||||
|
||||
return adis_check_status(adis);
|
||||
}
|
||||
|
||||
/**
|
||||
* adis_inital_startup() - Performs device self-test
|
||||
* @adis: The adis device
|
||||
*
|
||||
* Returns 0 if the device is operational, a negative error code otherwise.
|
||||
*
|
||||
* This function should be called early on in the device initialization sequence
|
||||
* to ensure that the device is in a sane and known state and that it is usable.
|
||||
*/
|
||||
int adis_initial_startup(struct adis *adis)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = adis_self_test(adis);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n");
|
||||
adis_reset(adis);
|
||||
msleep(adis->data->startup_delay);
|
||||
ret = adis_self_test(adis);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_initial_startup);
|
||||
|
||||
/**
|
||||
* adis_single_conversion() - Performs a single sample conversion
|
||||
* @indio_dev: The IIO device
|
||||
* @chan: The IIO channel
|
||||
* @error_mask: Mask for the error bit
|
||||
* @val: Result of the conversion
|
||||
*
|
||||
* Returns IIO_VAL_INT on success, a negative error code otherwise.
|
||||
*
|
||||
* The function performs a single conversion on a given channel and post
|
||||
* processes the value accordingly to the channel spec. If a error_mask is given
|
||||
* the function will check if the mask is set in the returned raw value. If it
|
||||
* is set the function will perform a self-check. If the device does not report
|
||||
* a error bit in the channels raw value set error_mask to 0.
|
||||
*/
|
||||
int adis_single_conversion(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int error_mask, int *val)
|
||||
{
|
||||
struct adis *adis = iio_device_get_drvdata(indio_dev);
|
||||
unsigned int uval;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
ret = adis_read_reg(adis, chan->address, &uval,
|
||||
chan->scan_type.storagebits / 8);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
if (uval & error_mask) {
|
||||
ret = adis_check_status(adis);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
if (chan->scan_type.sign == 's')
|
||||
*val = sign_extend32(uval, chan->scan_type.realbits - 1);
|
||||
else
|
||||
*val = uval & ((1 << chan->scan_type.realbits) - 1);
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
err_unlock:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_single_conversion);
|
||||
|
||||
/**
|
||||
* adis_init() - Initialize adis device structure
|
||||
* @adis: The adis device
|
||||
* @indio_dev: The iio device
|
||||
* @spi: The spi device
|
||||
* @data: Chip specific data
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*
|
||||
* This function must be called, before any other adis helper function may be
|
||||
* called.
|
||||
*/
|
||||
int adis_init(struct adis *adis, struct iio_dev *indio_dev,
|
||||
struct spi_device *spi, const struct adis_data *data)
|
||||
{
|
||||
mutex_init(&adis->txrx_lock);
|
||||
adis->spi = spi;
|
||||
adis->data = data;
|
||||
iio_device_set_drvdata(indio_dev, adis);
|
||||
|
||||
if (data->has_paging) {
|
||||
/* Need to set the page before first read/write */
|
||||
adis->current_page = -1;
|
||||
} else {
|
||||
/* Page will always be 0 */
|
||||
adis->current_page = 0;
|
||||
}
|
||||
|
||||
return adis_enable_irq(adis, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_init);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Common library code for ADIS16XXX devices");
|
|
@ -0,0 +1,925 @@
|
|||
/*
|
||||
* ADIS16480 and similar IMUs driver
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#define ADIS16480_PAGE_SIZE 0x80
|
||||
|
||||
#define ADIS16480_REG(page, reg) ((page) * ADIS16480_PAGE_SIZE + (reg))
|
||||
|
||||
#define ADIS16480_REG_PAGE_ID 0x00 /* Same address on each page */
|
||||
#define ADIS16480_REG_SEQ_CNT ADIS16480_REG(0x00, 0x06)
|
||||
#define ADIS16480_REG_SYS_E_FLA ADIS16480_REG(0x00, 0x08)
|
||||
#define ADIS16480_REG_DIAG_STS ADIS16480_REG(0x00, 0x0A)
|
||||
#define ADIS16480_REG_ALM_STS ADIS16480_REG(0x00, 0x0C)
|
||||
#define ADIS16480_REG_TEMP_OUT ADIS16480_REG(0x00, 0x0E)
|
||||
#define ADIS16480_REG_X_GYRO_OUT ADIS16480_REG(0x00, 0x10)
|
||||
#define ADIS16480_REG_Y_GYRO_OUT ADIS16480_REG(0x00, 0x14)
|
||||
#define ADIS16480_REG_Z_GYRO_OUT ADIS16480_REG(0x00, 0x18)
|
||||
#define ADIS16480_REG_X_ACCEL_OUT ADIS16480_REG(0x00, 0x1C)
|
||||
#define ADIS16480_REG_Y_ACCEL_OUT ADIS16480_REG(0x00, 0x20)
|
||||
#define ADIS16480_REG_Z_ACCEL_OUT ADIS16480_REG(0x00, 0x24)
|
||||
#define ADIS16480_REG_X_MAGN_OUT ADIS16480_REG(0x00, 0x28)
|
||||
#define ADIS16480_REG_Y_MAGN_OUT ADIS16480_REG(0x00, 0x2A)
|
||||
#define ADIS16480_REG_Z_MAGN_OUT ADIS16480_REG(0x00, 0x2C)
|
||||
#define ADIS16480_REG_BAROM_OUT ADIS16480_REG(0x00, 0x2E)
|
||||
#define ADIS16480_REG_X_DELTAANG_OUT ADIS16480_REG(0x00, 0x40)
|
||||
#define ADIS16480_REG_Y_DELTAANG_OUT ADIS16480_REG(0x00, 0x44)
|
||||
#define ADIS16480_REG_Z_DELTAANG_OUT ADIS16480_REG(0x00, 0x48)
|
||||
#define ADIS16480_REG_X_DELTAVEL_OUT ADIS16480_REG(0x00, 0x4C)
|
||||
#define ADIS16480_REG_Y_DELTAVEL_OUT ADIS16480_REG(0x00, 0x50)
|
||||
#define ADIS16480_REG_Z_DELTAVEL_OUT ADIS16480_REG(0x00, 0x54)
|
||||
#define ADIS16480_REG_PROD_ID ADIS16480_REG(0x00, 0x7E)
|
||||
|
||||
#define ADIS16480_REG_X_GYRO_SCALE ADIS16480_REG(0x02, 0x04)
|
||||
#define ADIS16480_REG_Y_GYRO_SCALE ADIS16480_REG(0x02, 0x06)
|
||||
#define ADIS16480_REG_Z_GYRO_SCALE ADIS16480_REG(0x02, 0x08)
|
||||
#define ADIS16480_REG_X_ACCEL_SCALE ADIS16480_REG(0x02, 0x0A)
|
||||
#define ADIS16480_REG_Y_ACCEL_SCALE ADIS16480_REG(0x02, 0x0C)
|
||||
#define ADIS16480_REG_Z_ACCEL_SCALE ADIS16480_REG(0x02, 0x0E)
|
||||
#define ADIS16480_REG_X_GYRO_BIAS ADIS16480_REG(0x02, 0x10)
|
||||
#define ADIS16480_REG_Y_GYRO_BIAS ADIS16480_REG(0x02, 0x14)
|
||||
#define ADIS16480_REG_Z_GYRO_BIAS ADIS16480_REG(0x02, 0x18)
|
||||
#define ADIS16480_REG_X_ACCEL_BIAS ADIS16480_REG(0x02, 0x1C)
|
||||
#define ADIS16480_REG_Y_ACCEL_BIAS ADIS16480_REG(0x02, 0x20)
|
||||
#define ADIS16480_REG_Z_ACCEL_BIAS ADIS16480_REG(0x02, 0x24)
|
||||
#define ADIS16480_REG_X_HARD_IRON ADIS16480_REG(0x02, 0x28)
|
||||
#define ADIS16480_REG_Y_HARD_IRON ADIS16480_REG(0x02, 0x2A)
|
||||
#define ADIS16480_REG_Z_HARD_IRON ADIS16480_REG(0x02, 0x2C)
|
||||
#define ADIS16480_REG_BAROM_BIAS ADIS16480_REG(0x02, 0x40)
|
||||
#define ADIS16480_REG_FLASH_CNT ADIS16480_REG(0x02, 0x7C)
|
||||
|
||||
#define ADIS16480_REG_GLOB_CMD ADIS16480_REG(0x03, 0x02)
|
||||
#define ADIS16480_REG_FNCTIO_CTRL ADIS16480_REG(0x03, 0x06)
|
||||
#define ADIS16480_REG_GPIO_CTRL ADIS16480_REG(0x03, 0x08)
|
||||
#define ADIS16480_REG_CONFIG ADIS16480_REG(0x03, 0x0A)
|
||||
#define ADIS16480_REG_DEC_RATE ADIS16480_REG(0x03, 0x0C)
|
||||
#define ADIS16480_REG_SLP_CNT ADIS16480_REG(0x03, 0x10)
|
||||
#define ADIS16480_REG_FILTER_BNK0 ADIS16480_REG(0x03, 0x16)
|
||||
#define ADIS16480_REG_FILTER_BNK1 ADIS16480_REG(0x03, 0x18)
|
||||
#define ADIS16480_REG_ALM_CNFG0 ADIS16480_REG(0x03, 0x20)
|
||||
#define ADIS16480_REG_ALM_CNFG1 ADIS16480_REG(0x03, 0x22)
|
||||
#define ADIS16480_REG_ALM_CNFG2 ADIS16480_REG(0x03, 0x24)
|
||||
#define ADIS16480_REG_XG_ALM_MAGN ADIS16480_REG(0x03, 0x28)
|
||||
#define ADIS16480_REG_YG_ALM_MAGN ADIS16480_REG(0x03, 0x2A)
|
||||
#define ADIS16480_REG_ZG_ALM_MAGN ADIS16480_REG(0x03, 0x2C)
|
||||
#define ADIS16480_REG_XA_ALM_MAGN ADIS16480_REG(0x03, 0x2E)
|
||||
#define ADIS16480_REG_YA_ALM_MAGN ADIS16480_REG(0x03, 0x30)
|
||||
#define ADIS16480_REG_ZA_ALM_MAGN ADIS16480_REG(0x03, 0x32)
|
||||
#define ADIS16480_REG_XM_ALM_MAGN ADIS16480_REG(0x03, 0x34)
|
||||
#define ADIS16480_REG_YM_ALM_MAGN ADIS16480_REG(0x03, 0x36)
|
||||
#define ADIS16480_REG_ZM_ALM_MAGN ADIS16480_REG(0x03, 0x38)
|
||||
#define ADIS16480_REG_BR_ALM_MAGN ADIS16480_REG(0x03, 0x3A)
|
||||
#define ADIS16480_REG_FIRM_REV ADIS16480_REG(0x03, 0x78)
|
||||
#define ADIS16480_REG_FIRM_DM ADIS16480_REG(0x03, 0x7A)
|
||||
#define ADIS16480_REG_FIRM_Y ADIS16480_REG(0x03, 0x7C)
|
||||
|
||||
#define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20)
|
||||
|
||||
/* Each filter coefficent bank spans two pages */
|
||||
#define ADIS16480_FIR_COEF(page) (x < 60 ? ADIS16480_REG(page, (x) + 8) : \
|
||||
ADIS16480_REG((page) + 1, (x) - 60 + 8))
|
||||
#define ADIS16480_FIR_COEF_A(x) ADIS16480_FIR_COEF(0x05, (x))
|
||||
#define ADIS16480_FIR_COEF_B(x) ADIS16480_FIR_COEF(0x07, (x))
|
||||
#define ADIS16480_FIR_COEF_C(x) ADIS16480_FIR_COEF(0x09, (x))
|
||||
#define ADIS16480_FIR_COEF_D(x) ADIS16480_FIR_COEF(0x0B, (x))
|
||||
|
||||
struct adis16480_chip_info {
|
||||
unsigned int num_channels;
|
||||
const struct iio_chan_spec *channels;
|
||||
};
|
||||
|
||||
struct adis16480 {
|
||||
const struct adis16480_chip_info *chip_info;
|
||||
|
||||
struct adis adis;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static ssize_t adis16480_show_firmware_revision(struct file *file,
|
||||
char __user *userbuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct adis16480 *adis16480 = file->private_data;
|
||||
char buf[6];
|
||||
size_t len;
|
||||
u16 rev;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_REV, &rev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%x.%x\n", rev >> 8, rev & 0xff);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static const struct file_operations adis16480_firmware_revision_fops = {
|
||||
.open = simple_open,
|
||||
.read = adis16480_show_firmware_revision,
|
||||
.llseek = default_llseek,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static ssize_t adis16480_show_firmware_date(struct file *file,
|
||||
char __user *userbuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct adis16480 *adis16480 = file->private_data;
|
||||
u16 md, year;
|
||||
char buf[12];
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_Y, &year);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_DM, &md);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%.2x-%.2x-%.4x\n",
|
||||
md >> 8, md & 0xff, year);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static const struct file_operations adis16480_firmware_date_fops = {
|
||||
.open = simple_open,
|
||||
.read = adis16480_show_firmware_date,
|
||||
.llseek = default_llseek,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adis16480_show_serial_number(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16480 *adis16480 = arg;
|
||||
u16 serial;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_SERIAL_NUM,
|
||||
&serial);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = serial;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16480_serial_number_fops,
|
||||
adis16480_show_serial_number, NULL, "0x%.4llx\n");
|
||||
|
||||
static int adis16480_show_product_id(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16480 *adis16480 = arg;
|
||||
u16 prod_id;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_PROD_ID,
|
||||
&prod_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = prod_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16480_product_id_fops,
|
||||
adis16480_show_product_id, NULL, "%llu\n");
|
||||
|
||||
static int adis16480_show_flash_count(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16480 *adis16480 = arg;
|
||||
u32 flash_count;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_32(&adis16480->adis, ADIS16480_REG_FLASH_CNT,
|
||||
&flash_count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = flash_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16480_flash_count_fops,
|
||||
adis16480_show_flash_count, NULL, "%lld\n");
|
||||
|
||||
static int adis16480_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16480 *adis16480 = iio_priv(indio_dev);
|
||||
|
||||
debugfs_create_file("firmware_revision", 0400,
|
||||
indio_dev->debugfs_dentry, adis16480,
|
||||
&adis16480_firmware_revision_fops);
|
||||
debugfs_create_file("firmware_date", 0400, indio_dev->debugfs_dentry,
|
||||
adis16480, &adis16480_firmware_date_fops);
|
||||
debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
|
||||
adis16480, &adis16480_serial_number_fops);
|
||||
debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
|
||||
adis16480, &adis16480_product_id_fops);
|
||||
debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
|
||||
adis16480, &adis16480_flash_count_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int adis16480_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int adis16480_set_freq(struct adis16480 *st, unsigned int freq)
|
||||
{
|
||||
unsigned int t;
|
||||
|
||||
t = 2460000 / freq;
|
||||
if (t > 2048)
|
||||
t = 2048;
|
||||
|
||||
if (t != 0)
|
||||
t--;
|
||||
|
||||
return adis_write_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, t);
|
||||
}
|
||||
|
||||
static int adis16480_get_freq(struct adis16480 *st, unsigned int *freq)
|
||||
{
|
||||
uint16_t t;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, &t);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*freq = 2460000 / (t + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t adis16480_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
unsigned int freq;
|
||||
int ret;
|
||||
|
||||
ret = adis16480_get_freq(st, &freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d.%.3d\n", freq / 1000, freq % 1000);
|
||||
}
|
||||
|
||||
static ssize_t adis16480_write_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
int freq_int, freq_fract;
|
||||
long val;
|
||||
int ret;
|
||||
|
||||
ret = iio_str_to_fixpoint(buf, 100, &freq_int, &freq_fract);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = freq_int * 1000 + freq_fract;
|
||||
|
||||
if (val <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = adis16480_set_freq(st, val);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
adis16480_read_frequency,
|
||||
adis16480_write_frequency);
|
||||
|
||||
enum {
|
||||
ADIS16480_SCAN_GYRO_X,
|
||||
ADIS16480_SCAN_GYRO_Y,
|
||||
ADIS16480_SCAN_GYRO_Z,
|
||||
ADIS16480_SCAN_ACCEL_X,
|
||||
ADIS16480_SCAN_ACCEL_Y,
|
||||
ADIS16480_SCAN_ACCEL_Z,
|
||||
ADIS16480_SCAN_MAGN_X,
|
||||
ADIS16480_SCAN_MAGN_Y,
|
||||
ADIS16480_SCAN_MAGN_Z,
|
||||
ADIS16480_SCAN_BARO,
|
||||
ADIS16480_SCAN_TEMP,
|
||||
};
|
||||
|
||||
static const unsigned int adis16480_calibbias_regs[] = {
|
||||
[ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_BIAS,
|
||||
[ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_BIAS,
|
||||
[ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_BIAS,
|
||||
[ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_BIAS,
|
||||
[ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_BIAS,
|
||||
[ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_BIAS,
|
||||
[ADIS16480_SCAN_MAGN_X] = ADIS16480_REG_X_HARD_IRON,
|
||||
[ADIS16480_SCAN_MAGN_Y] = ADIS16480_REG_Y_HARD_IRON,
|
||||
[ADIS16480_SCAN_MAGN_Z] = ADIS16480_REG_Z_HARD_IRON,
|
||||
[ADIS16480_SCAN_BARO] = ADIS16480_REG_BAROM_BIAS,
|
||||
};
|
||||
|
||||
static const unsigned int adis16480_calibscale_regs[] = {
|
||||
[ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_SCALE,
|
||||
[ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_SCALE,
|
||||
[ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_SCALE,
|
||||
[ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_SCALE,
|
||||
[ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_SCALE,
|
||||
[ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_SCALE,
|
||||
};
|
||||
|
||||
static int adis16480_set_calibbias(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int bias)
|
||||
{
|
||||
unsigned int reg = adis16480_calibbias_regs[chan->scan_index];
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_MAGN:
|
||||
case IIO_PRESSURE:
|
||||
if (bias < -0x8000 || bias >= 0x8000)
|
||||
return -EINVAL;
|
||||
return adis_write_reg_16(&st->adis, reg, bias);
|
||||
case IIO_ANGL_VEL:
|
||||
case IIO_ACCEL:
|
||||
return adis_write_reg_32(&st->adis, reg, bias);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adis16480_get_calibbias(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *bias)
|
||||
{
|
||||
unsigned int reg = adis16480_calibbias_regs[chan->scan_index];
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
uint16_t val16;
|
||||
uint32_t val32;
|
||||
int ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_MAGN:
|
||||
case IIO_PRESSURE:
|
||||
ret = adis_read_reg_16(&st->adis, reg, &val16);
|
||||
*bias = sign_extend32(val16, 15);
|
||||
break;
|
||||
case IIO_ANGL_VEL:
|
||||
case IIO_ACCEL:
|
||||
ret = adis_read_reg_32(&st->adis, reg, &val32);
|
||||
*bias = sign_extend32(val32, 31);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int adis16480_set_calibscale(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int scale)
|
||||
{
|
||||
unsigned int reg = adis16480_calibscale_regs[chan->scan_index];
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
|
||||
if (scale < -0x8000 || scale >= 0x8000)
|
||||
return -EINVAL;
|
||||
|
||||
return adis_write_reg_16(&st->adis, reg, scale);
|
||||
}
|
||||
|
||||
static int adis16480_get_calibscale(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *scale)
|
||||
{
|
||||
unsigned int reg = adis16480_calibscale_regs[chan->scan_index];
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
uint16_t val16;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, reg, &val16);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*scale = sign_extend32(val16, 15);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const unsigned int adis16480_def_filter_freqs[] = {
|
||||
310,
|
||||
55,
|
||||
275,
|
||||
63,
|
||||
};
|
||||
|
||||
static const unsigned int ad16480_filter_data[][2] = {
|
||||
[ADIS16480_SCAN_GYRO_X] = { ADIS16480_REG_FILTER_BNK0, 0 },
|
||||
[ADIS16480_SCAN_GYRO_Y] = { ADIS16480_REG_FILTER_BNK0, 3 },
|
||||
[ADIS16480_SCAN_GYRO_Z] = { ADIS16480_REG_FILTER_BNK0, 6 },
|
||||
[ADIS16480_SCAN_ACCEL_X] = { ADIS16480_REG_FILTER_BNK0, 9 },
|
||||
[ADIS16480_SCAN_ACCEL_Y] = { ADIS16480_REG_FILTER_BNK0, 12 },
|
||||
[ADIS16480_SCAN_ACCEL_Z] = { ADIS16480_REG_FILTER_BNK1, 0 },
|
||||
[ADIS16480_SCAN_MAGN_X] = { ADIS16480_REG_FILTER_BNK1, 3 },
|
||||
[ADIS16480_SCAN_MAGN_Y] = { ADIS16480_REG_FILTER_BNK1, 6 },
|
||||
[ADIS16480_SCAN_MAGN_Z] = { ADIS16480_REG_FILTER_BNK1, 9 },
|
||||
};
|
||||
|
||||
static int adis16480_get_filter_freq(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *freq)
|
||||
{
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
unsigned int enable_mask, offset, reg;
|
||||
uint16_t val;
|
||||
int ret;
|
||||
|
||||
reg = ad16480_filter_data[chan->scan_index][0];
|
||||
offset = ad16480_filter_data[chan->scan_index][1];
|
||||
enable_mask = BIT(offset + 2);
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, reg, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!(val & enable_mask))
|
||||
*freq = 0;
|
||||
else
|
||||
*freq = adis16480_def_filter_freqs[(val >> offset) & 0x3];
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int adis16480_set_filter_freq(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int freq)
|
||||
{
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
unsigned int enable_mask, offset, reg;
|
||||
unsigned int diff, best_diff;
|
||||
unsigned int i, best_freq;
|
||||
uint16_t val;
|
||||
int ret;
|
||||
|
||||
reg = ad16480_filter_data[chan->scan_index][0];
|
||||
offset = ad16480_filter_data[chan->scan_index][1];
|
||||
enable_mask = BIT(offset + 2);
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, reg, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (freq == 0) {
|
||||
val &= ~enable_mask;
|
||||
} else {
|
||||
best_freq = 0;
|
||||
best_diff = 310;
|
||||
for (i = 0; i < ARRAY_SIZE(adis16480_def_filter_freqs); i++) {
|
||||
if (adis16480_def_filter_freqs[i] >= freq) {
|
||||
diff = adis16480_def_filter_freqs[i] - freq;
|
||||
if (diff < best_diff) {
|
||||
best_diff = diff;
|
||||
best_freq = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val &= ~(0x3 << offset);
|
||||
val |= best_freq << offset;
|
||||
val |= enable_mask;
|
||||
}
|
||||
|
||||
return adis_write_reg_16(&st->adis, reg, val);
|
||||
}
|
||||
|
||||
static int adis16480_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
||||
{
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return adis_single_conversion(indio_dev, chan, 0, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = 0;
|
||||
*val2 = IIO_DEGREE_TO_RAD(20000); /* 0.02 degree/sec */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_ACCEL:
|
||||
*val = 0;
|
||||
*val2 = IIO_G_TO_M_S_2(800); /* 0.8 mg */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_MAGN:
|
||||
*val = 0;
|
||||
*val2 = 100; /* 0.0001 gauss */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
*val = 5;
|
||||
*val2 = 650000; /* 5.65 milli degree Celsius */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_PRESSURE:
|
||||
*val = 0;
|
||||
*val2 = 4000; /* 40ubar = 0.004 kPa */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* Only the temperature channel has a offset */
|
||||
*val = 4425; /* 25 degree Celsius = 0x0000 */
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
return adis16480_get_calibbias(indio_dev, chan, val);
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
return adis16480_get_calibscale(indio_dev, chan, val);
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
return adis16480_get_filter_freq(indio_dev, chan, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int adis16480_write_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int val, int val2, long info)
|
||||
{
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
return adis16480_set_calibbias(indio_dev, chan, val);
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
return adis16480_set_calibscale(indio_dev, chan, val);
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
return adis16480_set_filter_freq(indio_dev, chan, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define ADIS16480_MOD_CHANNEL(_type, _mod, _address, _si, _info, _bits) \
|
||||
{ \
|
||||
.type = (_type), \
|
||||
.modified = 1, \
|
||||
.channel2 = (_mod), \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
_info, \
|
||||
.address = (_address), \
|
||||
.scan_index = (_si), \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = (_bits), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16480_GYRO_CHANNEL(_mod) \
|
||||
ADIS16480_MOD_CHANNEL(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \
|
||||
ADIS16480_REG_ ## _mod ## _GYRO_OUT, ADIS16480_SCAN_GYRO_ ## _mod, \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \
|
||||
32)
|
||||
|
||||
#define ADIS16480_ACCEL_CHANNEL(_mod) \
|
||||
ADIS16480_MOD_CHANNEL(IIO_ACCEL, IIO_MOD_ ## _mod, \
|
||||
ADIS16480_REG_ ## _mod ## _ACCEL_OUT, ADIS16480_SCAN_ACCEL_ ## _mod, \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \
|
||||
32)
|
||||
|
||||
#define ADIS16480_MAGN_CHANNEL(_mod) \
|
||||
ADIS16480_MOD_CHANNEL(IIO_MAGN, IIO_MOD_ ## _mod, \
|
||||
ADIS16480_REG_ ## _mod ## _MAGN_OUT, ADIS16480_SCAN_MAGN_ ## _mod, \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT, \
|
||||
16)
|
||||
|
||||
#define ADIS16480_PRESSURE_CHANNEL() \
|
||||
{ \
|
||||
.type = IIO_PRESSURE, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = ADIS16480_REG_BAROM_OUT, \
|
||||
.scan_index = ADIS16480_SCAN_BARO, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 32, \
|
||||
.storagebits = 32, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16480_TEMP_CHANNEL() { \
|
||||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \
|
||||
.address = ADIS16480_REG_TEMP_OUT, \
|
||||
.scan_index = ADIS16480_SCAN_TEMP, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16480_channels[] = {
|
||||
ADIS16480_GYRO_CHANNEL(X),
|
||||
ADIS16480_GYRO_CHANNEL(Y),
|
||||
ADIS16480_GYRO_CHANNEL(Z),
|
||||
ADIS16480_ACCEL_CHANNEL(X),
|
||||
ADIS16480_ACCEL_CHANNEL(Y),
|
||||
ADIS16480_ACCEL_CHANNEL(Z),
|
||||
ADIS16480_MAGN_CHANNEL(X),
|
||||
ADIS16480_MAGN_CHANNEL(Y),
|
||||
ADIS16480_MAGN_CHANNEL(Z),
|
||||
ADIS16480_PRESSURE_CHANNEL(),
|
||||
ADIS16480_TEMP_CHANNEL(),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(11)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16485_channels[] = {
|
||||
ADIS16480_GYRO_CHANNEL(X),
|
||||
ADIS16480_GYRO_CHANNEL(Y),
|
||||
ADIS16480_GYRO_CHANNEL(Z),
|
||||
ADIS16480_ACCEL_CHANNEL(X),
|
||||
ADIS16480_ACCEL_CHANNEL(Y),
|
||||
ADIS16480_ACCEL_CHANNEL(Z),
|
||||
ADIS16480_TEMP_CHANNEL(),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(7)
|
||||
};
|
||||
|
||||
enum adis16480_variant {
|
||||
ADIS16375,
|
||||
ADIS16480,
|
||||
ADIS16485,
|
||||
ADIS16488,
|
||||
};
|
||||
|
||||
static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16375] = {
|
||||
.channels = adis16485_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||
},
|
||||
[ADIS16480] = {
|
||||
.channels = adis16480_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16480_channels),
|
||||
},
|
||||
[ADIS16485] = {
|
||||
.channels = adis16485_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||
},
|
||||
[ADIS16488] = {
|
||||
.channels = adis16480_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16480_channels),
|
||||
},
|
||||
};
|
||||
|
||||
static struct attribute *adis16480_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group adis16480_attribute_group = {
|
||||
.attrs = adis16480_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info adis16480_info = {
|
||||
.attrs = &adis16480_attribute_group,
|
||||
.read_raw = &adis16480_read_raw,
|
||||
.write_raw = &adis16480_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adis16480_stop_device(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16480_REG_SLP_CNT, BIT(9));
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev,
|
||||
"Could not power down device: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16480_enable_irq(struct adis *adis, bool enable)
|
||||
{
|
||||
return adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL,
|
||||
enable ? BIT(3) : 0);
|
||||
}
|
||||
|
||||
static int adis16480_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
uint16_t prod_id;
|
||||
unsigned int device_id;
|
||||
int ret;
|
||||
|
||||
adis_reset(&st->adis);
|
||||
msleep(70);
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16480_REG_GLOB_CMD, BIT(1));
|
||||
if (ret)
|
||||
return ret;
|
||||
msleep(30);
|
||||
|
||||
ret = adis_check_status(&st->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16480_REG_PROD_ID, &prod_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sscanf(indio_dev->name, "adis%u\n", &device_id);
|
||||
|
||||
if (prod_id != device_id)
|
||||
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
|
||||
device_id, prod_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ADIS16480_DIAG_STAT_XGYRO_FAIL 0
|
||||
#define ADIS16480_DIAG_STAT_YGYRO_FAIL 1
|
||||
#define ADIS16480_DIAG_STAT_ZGYRO_FAIL 2
|
||||
#define ADIS16480_DIAG_STAT_XACCL_FAIL 3
|
||||
#define ADIS16480_DIAG_STAT_YACCL_FAIL 4
|
||||
#define ADIS16480_DIAG_STAT_ZACCL_FAIL 5
|
||||
#define ADIS16480_DIAG_STAT_XMAGN_FAIL 8
|
||||
#define ADIS16480_DIAG_STAT_YMAGN_FAIL 9
|
||||
#define ADIS16480_DIAG_STAT_ZMAGN_FAIL 10
|
||||
#define ADIS16480_DIAG_STAT_BARO_FAIL 11
|
||||
|
||||
static const char * const adis16480_status_error_msgs[] = {
|
||||
[ADIS16480_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
|
||||
[ADIS16480_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
|
||||
[ADIS16480_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
|
||||
[ADIS16480_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_XMAGN_FAIL] = "X-axis magnetometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_YMAGN_FAIL] = "Y-axis magnetometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_ZMAGN_FAIL] = "Z-axis magnetometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_BARO_FAIL] = "Barometer self-test failure",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16480_data = {
|
||||
.diag_stat_reg = ADIS16480_REG_DIAG_STS,
|
||||
.glob_cmd_reg = ADIS16480_REG_GLOB_CMD,
|
||||
.has_paging = true,
|
||||
|
||||
.read_delay = 5,
|
||||
.write_delay = 5,
|
||||
|
||||
.status_error_msgs = adis16480_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_BARO_FAIL),
|
||||
|
||||
.enable_irq = adis16480_enable_irq,
|
||||
};
|
||||
|
||||
static int adis16480_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct iio_dev *indio_dev;
|
||||
struct adis16480 *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->chip_info = &adis16480_chip_info[id->driver_data];
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
indio_dev->info = &adis16480_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, &adis16480_data);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = adis16480_initial_setup(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_stop_device;
|
||||
|
||||
adis16480_debugfs_init(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
error_stop_device:
|
||||
adis16480_stop_device(indio_dev);
|
||||
error_cleanup_buffer:
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16480_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16480_stop_device(indio_dev);
|
||||
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adis16480_ids[] = {
|
||||
{ "adis16375", ADIS16375 },
|
||||
{ "adis16480", ADIS16480 },
|
||||
{ "adis16485", ADIS16485 },
|
||||
{ "adis16488", ADIS16488 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16480_ids);
|
||||
|
||||
static struct spi_driver adis16480_driver = {
|
||||
.driver = {
|
||||
.name = "adis16480",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = adis16480_ids,
|
||||
.probe = adis16480_probe,
|
||||
.remove = adis16480_remove,
|
||||
};
|
||||
module_spi_driver(adis16480_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16480 IMU driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Common library for ADIS16XXX devices
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
int adis_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct adis *adis = iio_device_get_drvdata(indio_dev);
|
||||
const struct iio_chan_spec *chan;
|
||||
unsigned int scan_count;
|
||||
unsigned int i, j;
|
||||
__be16 *tx, *rx;
|
||||
|
||||
kfree(adis->xfer);
|
||||
kfree(adis->buffer);
|
||||
|
||||
scan_count = indio_dev->scan_bytes / 2;
|
||||
|
||||
adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL);
|
||||
if (!adis->xfer)
|
||||
return -ENOMEM;
|
||||
|
||||
adis->buffer = kzalloc(indio_dev->scan_bytes * 2, GFP_KERNEL);
|
||||
if (!adis->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
rx = adis->buffer;
|
||||
tx = rx + indio_dev->scan_bytes;
|
||||
|
||||
spi_message_init(&adis->msg);
|
||||
|
||||
for (j = 0; j <= scan_count; j++) {
|
||||
adis->xfer[j].bits_per_word = 8;
|
||||
if (j != scan_count)
|
||||
adis->xfer[j].cs_change = 1;
|
||||
adis->xfer[j].len = 2;
|
||||
adis->xfer[j].delay_usecs = adis->data->read_delay;
|
||||
if (j < scan_count)
|
||||
adis->xfer[j].tx_buf = &tx[j];
|
||||
if (j >= 1)
|
||||
adis->xfer[j].rx_buf = &rx[j - 1];
|
||||
spi_message_add_tail(&adis->xfer[j], &adis->msg);
|
||||
}
|
||||
|
||||
chan = indio_dev->channels;
|
||||
for (i = 0; i < indio_dev->num_channels; i++, chan++) {
|
||||
if (!test_bit(chan->scan_index, scan_mask))
|
||||
continue;
|
||||
if (chan->scan_type.storagebits == 32)
|
||||
*tx++ = cpu_to_be16((chan->address + 2) << 8);
|
||||
*tx++ = cpu_to_be16(chan->address << 8);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_update_scan_mode);
|
||||
|
||||
static irqreturn_t adis_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adis *adis = iio_device_get_drvdata(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (!adis->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (adis->data->has_paging) {
|
||||
mutex_lock(&adis->txrx_lock);
|
||||
if (adis->current_page != 0) {
|
||||
adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
|
||||
adis->tx[1] = 0;
|
||||
spi_write(adis->spi, adis->tx, 2);
|
||||
}
|
||||
}
|
||||
|
||||
ret = spi_sync(adis->spi, &adis->msg);
|
||||
if (ret)
|
||||
dev_err(&adis->spi->dev, "Failed to read data: %d", ret);
|
||||
|
||||
|
||||
if (adis->data->has_paging) {
|
||||
adis->current_page = 0;
|
||||
mutex_unlock(&adis->txrx_lock);
|
||||
}
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp) {
|
||||
void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64);
|
||||
*(s64 *)b = pf->timestamp;
|
||||
}
|
||||
|
||||
iio_push_to_buffers(indio_dev, adis->buffer);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis_setup_buffer_and_trigger() - Sets up buffer and trigger for the adis device
|
||||
* @adis: The adis device.
|
||||
* @indio_dev: The IIO device.
|
||||
* @trigger_handler: Optional trigger handler, may be NULL.
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*
|
||||
* This function sets up the buffer and trigger for a adis devices. If
|
||||
* 'trigger_handler' is NULL the default trigger handler will be used. The
|
||||
* default trigger handler will simply read the registers assigned to the
|
||||
* currently active channels.
|
||||
*
|
||||
* adis_cleanup_buffer_and_trigger() should be called to free the resources
|
||||
* allocated by this function.
|
||||
*/
|
||||
int adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev,
|
||||
irqreturn_t (*trigger_handler)(int, void *))
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!trigger_handler)
|
||||
trigger_handler = adis_trigger_handler;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (adis->spi->irq) {
|
||||
ret = adis_probe_trigger(adis, indio_dev);
|
||||
if (ret)
|
||||
goto error_buffer_cleanup;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_setup_buffer_and_trigger);
|
||||
|
||||
/**
|
||||
* adis_cleanup_buffer_and_trigger() - Free buffer and trigger resources
|
||||
* @adis: The adis device.
|
||||
* @indio_dev: The IIO device.
|
||||
*
|
||||
* Frees resources allocated by adis_setup_buffer_and_trigger()
|
||||
*/
|
||||
void adis_cleanup_buffer_and_trigger(struct adis *adis,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
if (adis->spi->irq)
|
||||
adis_remove_trigger(adis);
|
||||
kfree(adis->buffer);
|
||||
kfree(adis->xfer);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_cleanup_buffer_and_trigger);
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Common library for ADIS16XXX devices
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
static int adis_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct adis *adis = trig->private_data;
|
||||
|
||||
return adis_enable_irq(adis, state);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops adis_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &adis_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
/**
|
||||
* adis_probe_trigger() - Sets up trigger for a adis device
|
||||
* @adis: The adis device
|
||||
* @indio_dev: The IIO device
|
||||
*
|
||||
* Returns 0 on success or a negative error code
|
||||
*
|
||||
* adis_remove_trigger() should be used to free the trigger.
|
||||
*/
|
||||
int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
adis->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (adis->trig == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = request_irq(adis->spi->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
indio_dev->name,
|
||||
adis->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
|
||||
adis->trig->dev.parent = &adis->spi->dev;
|
||||
adis->trig->ops = &adis_trigger_ops;
|
||||
adis->trig->private_data = adis;
|
||||
ret = iio_trigger_register(adis->trig);
|
||||
|
||||
indio_dev->trig = adis->trig;
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(adis->spi->irq, adis->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(adis->trig);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_probe_trigger);
|
||||
|
||||
/**
|
||||
* adis_remove_trigger() - Remove trigger for a adis devices
|
||||
* @adis: The adis device
|
||||
*
|
||||
* Removes the trigger previously registered with adis_probe_trigger().
|
||||
*/
|
||||
void adis_remove_trigger(struct adis *adis)
|
||||
{
|
||||
iio_trigger_unregister(adis->trig);
|
||||
free_irq(adis->spi->irq, adis->trig);
|
||||
iio_trigger_free(adis->trig);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_remove_trigger);
|
|
@ -65,6 +65,7 @@ static const char * const iio_chan_type_name_spec[] = {
|
|||
[IIO_CAPACITANCE] = "capacitance",
|
||||
[IIO_ALTVOLTAGE] = "altvoltage",
|
||||
[IIO_CCT] = "cct",
|
||||
[IIO_PRESSURE] = "pressure",
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
|
@ -407,6 +408,64 @@ static ssize_t iio_read_channel_info(struct device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_str_to_fixpoint() - Parse a fixed-point number from a string
|
||||
* @str: The string to parse
|
||||
* @fract_mult: Multiplier for the first decimal place, should be a power of 10
|
||||
* @integer: The integer part of the number
|
||||
* @fract: The fractional part of the number
|
||||
*
|
||||
* Returns 0 on success, or a negative error code if the string could not be
|
||||
* parsed.
|
||||
*/
|
||||
int iio_str_to_fixpoint(const char *str, int fract_mult,
|
||||
int *integer, int *fract)
|
||||
{
|
||||
int i = 0, f = 0;
|
||||
bool integer_part = true, negative = false;
|
||||
|
||||
if (str[0] == '-') {
|
||||
negative = true;
|
||||
str++;
|
||||
} else if (str[0] == '+') {
|
||||
str++;
|
||||
}
|
||||
|
||||
while (*str) {
|
||||
if ('0' <= *str && *str <= '9') {
|
||||
if (integer_part) {
|
||||
i = i * 10 + *str - '0';
|
||||
} else {
|
||||
f += fract_mult * (*str - '0');
|
||||
fract_mult /= 10;
|
||||
}
|
||||
} else if (*str == '\n') {
|
||||
if (*(str + 1) == '\0')
|
||||
break;
|
||||
else
|
||||
return -EINVAL;
|
||||
} else if (*str == '.' && integer_part) {
|
||||
integer_part = false;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
|
||||
if (negative) {
|
||||
if (i)
|
||||
i = -i;
|
||||
else
|
||||
f = -f;
|
||||
}
|
||||
|
||||
*integer = i;
|
||||
*fract = f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_str_to_fixpoint);
|
||||
|
||||
static ssize_t iio_write_channel_info(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
|
@ -414,8 +473,8 @@ static ssize_t iio_write_channel_info(struct device *dev,
|
|||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
int ret, integer = 0, fract = 0, fract_mult = 100000;
|
||||
bool integer_part = true, negative = false;
|
||||
int ret, fract_mult = 100000;
|
||||
int integer, fract;
|
||||
|
||||
/* Assumes decimal - precision based on number of digits */
|
||||
if (!indio_dev->info->write_raw)
|
||||
|
@ -434,39 +493,9 @@ static ssize_t iio_write_channel_info(struct device *dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (buf[0] == '-') {
|
||||
negative = true;
|
||||
buf++;
|
||||
} else if (buf[0] == '+') {
|
||||
buf++;
|
||||
}
|
||||
|
||||
while (*buf) {
|
||||
if ('0' <= *buf && *buf <= '9') {
|
||||
if (integer_part)
|
||||
integer = integer*10 + *buf - '0';
|
||||
else {
|
||||
fract += fract_mult*(*buf - '0');
|
||||
fract_mult /= 10;
|
||||
}
|
||||
} else if (*buf == '\n') {
|
||||
if (*(buf + 1) == '\0')
|
||||
break;
|
||||
else
|
||||
return -EINVAL;
|
||||
} else if (*buf == '.' && integer_part) {
|
||||
integer_part = false;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
buf++;
|
||||
}
|
||||
if (negative) {
|
||||
if (integer)
|
||||
integer = -integer;
|
||||
else
|
||||
fract = -fract;
|
||||
}
|
||||
ret = iio_str_to_fixpoint(buf, fract_mult, &integer, &fract);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = indio_dev->info->write_raw(indio_dev, this_attr->c,
|
||||
integer, fract, this_attr->address);
|
||||
|
|
|
@ -350,15 +350,10 @@ static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev)
|
|||
ret = iio_device_add_event_sysfs(indio_dev,
|
||||
&indio_dev->channels[j]);
|
||||
if (ret < 0)
|
||||
goto error_clear_attrs;
|
||||
return ret;
|
||||
attrcount += ret;
|
||||
}
|
||||
return attrcount;
|
||||
|
||||
error_clear_attrs:
|
||||
__iio_remove_event_config_attrs(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool iio_check_for_dynamic_events(struct iio_dev *indio_dev)
|
||||
|
|
|
@ -272,10 +272,9 @@ static int __devinit hid_als_probe(struct platform_device *pdev)
|
|||
goto error_free_dev;
|
||||
}
|
||||
|
||||
channels = kmemdup(als_channels,
|
||||
sizeof(als_channels),
|
||||
GFP_KERNEL);
|
||||
channels = kmemdup(als_channels, sizeof(als_channels), GFP_KERNEL);
|
||||
if (!channels) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
goto error_free_dev;
|
||||
}
|
||||
|
|
|
@ -307,10 +307,10 @@ static int __devinit hid_magn_3d_probe(struct platform_device *pdev)
|
|||
goto error_free_dev;
|
||||
}
|
||||
|
||||
channels = kmemdup(magn_3d_channels,
|
||||
sizeof(magn_3d_channels),
|
||||
GFP_KERNEL);
|
||||
channels = kmemdup(magn_3d_channels, sizeof(magn_3d_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
goto error_free_dev;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ menu "Accelerometers"
|
|||
config ADIS16201
|
||||
tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
|
||||
depends on SPI
|
||||
select IIO_TRIGGER if IIO_BUFFER
|
||||
select IIO_SW_RING if IIO_BUFFER
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices adis16201 dual-axis
|
||||
digital inclinometer and accelerometer.
|
||||
|
@ -15,8 +15,8 @@ config ADIS16201
|
|||
config ADIS16203
|
||||
tristate "Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer"
|
||||
depends on SPI
|
||||
select IIO_TRIGGER if IIO_BUFFER
|
||||
select IIO_SW_RING if IIO_BUFFER
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices adis16203 Programmable
|
||||
360 Degrees Inclinometer.
|
||||
|
@ -24,8 +24,8 @@ config ADIS16203
|
|||
config ADIS16204
|
||||
tristate "Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder"
|
||||
depends on SPI
|
||||
select IIO_TRIGGER if IIO_BUFFER
|
||||
select IIO_SW_RING if IIO_BUFFER
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices adis16204 Programmable
|
||||
High-g Digital Impact Sensor and Recorder.
|
||||
|
@ -33,8 +33,8 @@ config ADIS16204
|
|||
config ADIS16209
|
||||
tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
|
||||
depends on SPI
|
||||
select IIO_TRIGGER if IIO_BUFFER
|
||||
select IIO_SW_RING if IIO_BUFFER
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices adis16209 dual-axis digital inclinometer
|
||||
and accelerometer.
|
||||
|
@ -42,6 +42,7 @@ config ADIS16209
|
|||
config ADIS16220
|
||||
tristate "Analog Devices ADIS16220 Programmable Digital Vibration Sensor"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
help
|
||||
Say yes here to build support for Analog Devices adis16220 programmable
|
||||
digital vibration sensor.
|
||||
|
@ -49,8 +50,8 @@ config ADIS16220
|
|||
config ADIS16240
|
||||
tristate "Analog Devices ADIS16240 Programmable Impact Sensor and Recorder"
|
||||
depends on SPI
|
||||
select IIO_TRIGGER if IIO_BUFFER
|
||||
select IIO_SW_RING if IIO_BUFFER
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices adis16240 programmable
|
||||
impact Sensor and recorder.
|
||||
|
|
|
@ -3,26 +3,21 @@
|
|||
#
|
||||
|
||||
adis16201-y := adis16201_core.o
|
||||
adis16201-$(CONFIG_IIO_BUFFER) += adis16201_ring.o adis16201_trigger.o
|
||||
obj-$(CONFIG_ADIS16201) += adis16201.o
|
||||
|
||||
adis16203-y := adis16203_core.o
|
||||
adis16203-$(CONFIG_IIO_BUFFER) += adis16203_ring.o adis16203_trigger.o
|
||||
obj-$(CONFIG_ADIS16203) += adis16203.o
|
||||
|
||||
adis16204-y := adis16204_core.o
|
||||
adis16204-$(CONFIG_IIO_BUFFER) += adis16204_ring.o adis16204_trigger.o
|
||||
obj-$(CONFIG_ADIS16204) += adis16204.o
|
||||
|
||||
adis16209-y := adis16209_core.o
|
||||
adis16209-$(CONFIG_IIO_BUFFER) += adis16209_ring.o adis16209_trigger.o
|
||||
obj-$(CONFIG_ADIS16209) += adis16209.o
|
||||
|
||||
adis16220-y := adis16220_core.o
|
||||
obj-$(CONFIG_ADIS16220) += adis16220.o
|
||||
|
||||
adis16240-y := adis16240_core.o
|
||||
adis16240-$(CONFIG_IIO_BUFFER) += adis16240_ring.o adis16240_trigger.o
|
||||
obj-$(CONFIG_ADIS16240) += adis16240.o
|
||||
|
||||
obj-$(CONFIG_KXSD9) += kxsd9.o
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
#define ADIS16201_STARTUP_DELAY 220 /* ms */
|
||||
|
||||
#define ADIS16201_READ_REG(a) a
|
||||
#define ADIS16201_WRITE_REG(a) ((a) | 0x80)
|
||||
|
||||
#define ADIS16201_FLASH_CNT 0x00 /* Flash memory write count */
|
||||
#define ADIS16201_SUPPLY_OUT 0x02 /* Output, power supply */
|
||||
#define ADIS16201_XACCL_OUT 0x04 /* Output, x-axis accelerometer */
|
||||
|
@ -36,8 +33,6 @@
|
|||
#define ADIS16201_DIAG_STAT 0x3C /* Diagnostics, system status register */
|
||||
#define ADIS16201_GLOB_CMD 0x3E /* Operation, system command register */
|
||||
|
||||
#define ADIS16201_OUTPUTS 7
|
||||
|
||||
/* MSC_CTRL */
|
||||
#define ADIS16201_MSC_CTRL_SELF_TEST_EN (1 << 8) /* Self-test enable */
|
||||
#define ADIS16201_MSC_CTRL_DATA_RDY_EN (1 << 2) /* Data-ready enable: 1 = enabled, 0 = disabled */
|
||||
|
@ -47,95 +42,25 @@
|
|||
/* DIAG_STAT */
|
||||
#define ADIS16201_DIAG_STAT_ALARM2 (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
|
||||
#define ADIS16201_DIAG_STAT_ALARM1 (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
|
||||
#define ADIS16201_DIAG_STAT_SPI_FAIL (1<<3) /* SPI communications failure */
|
||||
#define ADIS16201_DIAG_STAT_FLASH_UPT (1<<2) /* Flash update failure */
|
||||
#define ADIS16201_DIAG_STAT_POWER_HIGH (1<<1) /* Power supply above 3.625 V */
|
||||
#define ADIS16201_DIAG_STAT_POWER_LOW (1<<0) /* Power supply below 3.15 V */
|
||||
#define ADIS16201_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */
|
||||
#define ADIS16201_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */
|
||||
#define ADIS16201_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */
|
||||
#define ADIS16201_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 3.15 V */
|
||||
|
||||
/* GLOB_CMD */
|
||||
#define ADIS16201_GLOB_CMD_SW_RESET (1<<7)
|
||||
#define ADIS16201_GLOB_CMD_FACTORY_CAL (1<<1)
|
||||
|
||||
#define ADIS16201_MAX_TX 14
|
||||
#define ADIS16201_MAX_RX 14
|
||||
|
||||
#define ADIS16201_ERROR_ACTIVE (1<<14)
|
||||
|
||||
/**
|
||||
* struct adis16201_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 adis16201_state {
|
||||
struct spi_device *us;
|
||||
struct iio_trigger *trig;
|
||||
struct mutex buf_lock;
|
||||
u8 tx[14] ____cacheline_aligned;
|
||||
u8 rx[14];
|
||||
};
|
||||
|
||||
int adis16201_set_irq(struct iio_dev *indio_dev, bool enable);
|
||||
|
||||
enum adis16201_scan {
|
||||
ADIS16201_SCAN_SUPPLY,
|
||||
ADIS16201_SCAN_ACC_X,
|
||||
ADIS16201_SCAN_ACC_Y,
|
||||
ADIS16201_SCAN_AUX_ADC,
|
||||
ADIS16201_SCAN_TEMP,
|
||||
ADIS16201_SCAN_INCLI_X,
|
||||
ADIS16201_SCAN_INCLI_Y,
|
||||
ADIS16201_SCAN_SUPPLY,
|
||||
ADIS16201_SCAN_AUX_ADC,
|
||||
ADIS16201_SCAN_TEMP,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
void adis16201_remove_trigger(struct iio_dev *indio_dev);
|
||||
int adis16201_probe_trigger(struct iio_dev *indio_dev);
|
||||
|
||||
ssize_t adis16201_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
int adis16201_configure_ring(struct iio_dev *indio_dev);
|
||||
void adis16201_unconfigure_ring(struct iio_dev *indio_dev);
|
||||
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline void adis16201_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adis16201_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
adis16201_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adis16201_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adis16201_initialize_ring(struct iio_ring_buffer *ring)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void adis16201_uninitialize_ring(struct iio_ring_buffer *ring)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
#endif /* SPI_ADIS16201_H_ */
|
||||
|
|
|
@ -18,258 +18,15 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#include "adis16201.h"
|
||||
|
||||
enum adis16201_chan {
|
||||
in_supply,
|
||||
temp,
|
||||
accel_x,
|
||||
accel_y,
|
||||
incli_x,
|
||||
incli_y,
|
||||
in_aux,
|
||||
};
|
||||
|
||||
/**
|
||||
* adis16201_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 adis16201_spi_write_reg_8(struct iio_dev *indio_dev,
|
||||
u8 reg_address,
|
||||
u8 val)
|
||||
{
|
||||
int ret;
|
||||
struct adis16201_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16201_WRITE_REG(reg_address);
|
||||
st->tx[1] = val;
|
||||
|
||||
ret = spi_write(st->us, st->tx, 2);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16201_spi_write_reg_16() - write 2 bytes to a pair of registers
|
||||
* @indio_dev: iio device associated with child of actual device
|
||||
* @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 adis16201_spi_write_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 value)
|
||||
{
|
||||
int ret;
|
||||
struct spi_message msg;
|
||||
struct adis16201_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] = ADIS16201_WRITE_REG(lower_reg_address);
|
||||
st->tx[1] = value & 0xFF;
|
||||
st->tx[2] = ADIS16201_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16201_spi_read_reg_16() - read 2 bytes from a 16-bit register
|
||||
* @indio_dev: iio device associated with child of actual 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 adis16201_spi_read_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 *val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16201_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,
|
||||
.delay_usecs = 20,
|
||||
}, {
|
||||
.rx_buf = st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = 20,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16201_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 int adis16201_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct adis16201_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = adis16201_spi_write_reg_8(indio_dev,
|
||||
ADIS16201_GLOB_CMD,
|
||||
ADIS16201_GLOB_CMD_SW_RESET);
|
||||
if (ret)
|
||||
dev_err(&st->us->dev, "problem resetting device");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adis16201_set_irq(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
u16 msc;
|
||||
|
||||
ret = adis16201_spi_read_reg_16(indio_dev, ADIS16201_MSC_CTRL, &msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
msc |= ADIS16201_MSC_CTRL_ACTIVE_HIGH;
|
||||
msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_DIO1;
|
||||
if (enable)
|
||||
msc |= ADIS16201_MSC_CTRL_DATA_RDY_EN;
|
||||
else
|
||||
msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_EN;
|
||||
|
||||
ret = adis16201_spi_write_reg_16(indio_dev, ADIS16201_MSC_CTRL, msc);
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16201_check_status(struct iio_dev *indio_dev)
|
||||
{
|
||||
u16 status;
|
||||
int ret;
|
||||
|
||||
ret = adis16201_spi_read_reg_16(indio_dev,
|
||||
ADIS16201_DIAG_STAT, &status);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Reading status failed\n");
|
||||
goto error_ret;
|
||||
}
|
||||
ret = status & 0xF;
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
|
||||
if (status & ADIS16201_DIAG_STAT_SPI_FAIL)
|
||||
dev_err(&indio_dev->dev, "SPI failure\n");
|
||||
if (status & ADIS16201_DIAG_STAT_FLASH_UPT)
|
||||
dev_err(&indio_dev->dev, "Flash update failed\n");
|
||||
if (status & ADIS16201_DIAG_STAT_POWER_HIGH)
|
||||
dev_err(&indio_dev->dev, "Power supply above 3.625V\n");
|
||||
if (status & ADIS16201_DIAG_STAT_POWER_LOW)
|
||||
dev_err(&indio_dev->dev, "Power supply below 3.15V\n");
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16201_self_test(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16201_spi_write_reg_16(indio_dev,
|
||||
ADIS16201_MSC_CTRL,
|
||||
ADIS16201_MSC_CTRL_SELF_TEST_EN);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "problem starting self test");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
ret = adis16201_check_status(indio_dev);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16201_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &indio_dev->dev;
|
||||
|
||||
/* Disable IRQ */
|
||||
ret = adis16201_set_irq(indio_dev, false);
|
||||
if (ret) {
|
||||
dev_err(dev, "disable irq failed");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Do self test */
|
||||
ret = adis16201_self_test(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "self test failure");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Read status register to check the result */
|
||||
ret = adis16201_check_status(indio_dev);
|
||||
if (ret) {
|
||||
adis16201_reset(indio_dev);
|
||||
dev_err(dev, "device not playing ball -> reset");
|
||||
msleep(ADIS16201_STARTUP_DELAY);
|
||||
ret = adis16201_check_status(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "giving up");
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 adis16201_addresses[7][2] = {
|
||||
[in_supply] = { ADIS16201_SUPPLY_OUT, },
|
||||
[temp] = { ADIS16201_TEMP_OUT },
|
||||
[accel_x] = { ADIS16201_XACCL_OUT, ADIS16201_XACCL_OFFS },
|
||||
[accel_y] = { ADIS16201_YACCL_OUT, ADIS16201_YACCL_OFFS },
|
||||
[in_aux] = { ADIS16201_AUX_ADC },
|
||||
[incli_x] = { ADIS16201_XINCL_OUT },
|
||||
[incli_y] = { ADIS16201_YINCL_OUT },
|
||||
static const u8 adis16201_addresses[] = {
|
||||
[ADIS16201_SCAN_ACC_X] = ADIS16201_XACCL_OFFS,
|
||||
[ADIS16201_SCAN_ACC_Y] = ADIS16201_YACCL_OFFS,
|
||||
[ADIS16201_SCAN_INCLI_X] = ADIS16201_XINCL_OFFS,
|
||||
[ADIS16201_SCAN_INCLI_Y] = ADIS16201_YINCL_OFFS,
|
||||
};
|
||||
|
||||
static int adis16201_read_raw(struct iio_dev *indio_dev,
|
||||
|
@ -277,6 +34,7 @@ static int adis16201_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int bits;
|
||||
u8 addr;
|
||||
|
@ -284,29 +42,8 @@ static int adis16201_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16201_addresses[chan->address][0];
|
||||
ret = adis16201_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val16 & ADIS16201_ERROR_ACTIVE) {
|
||||
ret = adis16201_check_status(indio_dev);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
val16 = val16 & ((1 << chan->scan_type.realbits) - 1);
|
||||
if (chan->scan_type.sign == 's')
|
||||
val16 = (s16)(val16 <<
|
||||
(16 - chan->scan_type.realbits)) >>
|
||||
(16 - chan->scan_type.realbits);
|
||||
*val = val16;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
return adis_single_conversion(indio_dev, chan,
|
||||
ADIS16201_ERROR_ACTIVE, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
|
@ -349,8 +86,8 @@ static int adis16201_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16201_addresses[chan->address][1];
|
||||
ret = adis16201_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
addr = adis16201_addresses[chan->scan_index];
|
||||
ret = adis_read_reg_16(st, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
|
@ -370,6 +107,7 @@ static int adis16201_write_raw(struct iio_dev *indio_dev,
|
|||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int bits;
|
||||
s16 val16;
|
||||
u8 addr;
|
||||
|
@ -386,124 +124,61 @@ static int adis16201_write_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
val16 = val & ((1 << bits) - 1);
|
||||
addr = adis16201_addresses[chan->address][1];
|
||||
return adis16201_spi_write_reg_16(indio_dev, addr, val16);
|
||||
addr = adis16201_addresses[chan->scan_index];
|
||||
return adis_write_reg_16(st, addr, val16);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16201_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.extend_name = "supply",
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = in_supply,
|
||||
.scan_index = ADIS16201_SCAN_SUPPLY,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.address = temp,
|
||||
.scan_index = ADIS16201_SCAN_TEMP,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
|
||||
.address = accel_x,
|
||||
.scan_index = ADIS16201_SCAN_ACC_X,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
|
||||
.address = accel_y,
|
||||
.scan_index = ADIS16201_SCAN_ACC_Y,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = in_aux,
|
||||
.scan_index = ADIS16201_SCAN_AUX_ADC,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_INCLI,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
|
||||
.address = incli_x,
|
||||
.scan_index = ADIS16201_SCAN_INCLI_X,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_INCLI,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
|
||||
.address = incli_y,
|
||||
.scan_index = ADIS16201_SCAN_INCLI_Y,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
},
|
||||
ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT, ADIS16201_SCAN_SUPPLY, 12),
|
||||
ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT, ADIS16201_SCAN_TEMP, 12),
|
||||
ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT, ADIS16201_SCAN_ACC_X,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14),
|
||||
ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT, ADIS16201_SCAN_ACC_Y,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14),
|
||||
ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC, ADIS16201_SCAN_AUX_ADC, 12),
|
||||
ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT, ADIS16201_SCAN_INCLI_X,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14),
|
||||
ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT, ADIS16201_SCAN_INCLI_Y,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(7)
|
||||
};
|
||||
|
||||
static const struct iio_info adis16201_info = {
|
||||
.read_raw = &adis16201_read_raw,
|
||||
.write_raw = &adis16201_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const char * const adis16201_status_error_msgs[] = {
|
||||
[ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
|
||||
[ADIS16201_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
|
||||
[ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
|
||||
[ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16201_data = {
|
||||
.read_delay = 20,
|
||||
.msc_ctrl_reg = ADIS16201_MSC_CTRL,
|
||||
.glob_cmd_reg = ADIS16201_GLOB_CMD,
|
||||
.diag_stat_reg = ADIS16201_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN,
|
||||
.startup_delay = ADIS16201_STARTUP_DELAY,
|
||||
|
||||
.status_error_msgs = adis16201_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) |
|
||||
BIT(ADIS16201_DIAG_STAT_FLASH_UPT_BIT) |
|
||||
BIT(ADIS16201_DIAG_STAT_POWER_HIGH_BIT) |
|
||||
BIT(ADIS16201_DIAG_STAT_POWER_LOW_BIT),
|
||||
};
|
||||
|
||||
static int __devinit adis16201_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct adis16201_state *st;
|
||||
struct adis *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
/* setup the industrialio driver allocated elements */
|
||||
|
@ -516,9 +191,6 @@ static int __devinit adis16201_probe(struct spi_device *spi)
|
|||
/* this is only used for removal purposes */
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->us = spi;
|
||||
mutex_init(&st->buf_lock);
|
||||
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &adis16201_info;
|
||||
|
@ -527,40 +199,25 @@ static int __devinit adis16201_probe(struct spi_device *spi)
|
|||
indio_dev->num_channels = ARRAY_SIZE(adis16201_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis16201_configure_ring(indio_dev);
|
||||
ret = adis_init(st, indio_dev, spi, &adis16201_data);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = iio_buffer_register(indio_dev,
|
||||
adis16201_channels,
|
||||
ARRAY_SIZE(adis16201_channels));
|
||||
if (ret) {
|
||||
printk(KERN_ERR "failed to initialize the ring\n");
|
||||
goto error_unreg_ring_funcs;
|
||||
}
|
||||
|
||||
if (spi->irq) {
|
||||
ret = adis16201_probe_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_uninitialize_ring;
|
||||
}
|
||||
|
||||
/* Get the device into a sane initial state */
|
||||
ret = adis16201_initial_setup(indio_dev);
|
||||
ret = adis_initial_startup(st);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
adis16201_remove_trigger(indio_dev);
|
||||
error_uninitialize_ring:
|
||||
iio_buffer_unregister(indio_dev);
|
||||
error_unreg_ring_funcs:
|
||||
adis16201_unconfigure_ring(indio_dev);
|
||||
error_cleanup_buffer_trigger:
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
error_ret:
|
||||
|
@ -570,11 +227,10 @@ static int __devinit adis16201_probe(struct spi_device *spi)
|
|||
static int __devexit adis16201_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16201_remove_trigger(indio_dev);
|
||||
iio_buffer_unregister(indio_dev);
|
||||
adis16201_unconfigure_ring(indio_dev);
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include "../ring_sw.h"
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include "adis16201.h"
|
||||
|
||||
|
||||
/**
|
||||
* adis16201_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 adis16201_read_ring_data(struct iio_dev *indio_dev, u8 *rx)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16201_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[ADIS16201_OUTPUTS + 1];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
memset(xfers, 0, sizeof(xfers));
|
||||
for (i = 0; i <= ADIS16201_OUTPUTS; i++) {
|
||||
xfers[i].bits_per_word = 8;
|
||||
xfers[i].cs_change = 1;
|
||||
xfers[i].len = 2;
|
||||
xfers[i].delay_usecs = 20;
|
||||
if (i < ADIS16201_OUTPUTS) {
|
||||
xfers[i].tx_buf = st->tx + 2 * i;
|
||||
st->tx[2 * i] = ADIS16201_READ_REG(ADIS16201_SUPPLY_OUT +
|
||||
2 * i);
|
||||
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;
|
||||
}
|
||||
|
||||
/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
|
||||
* specific to be rolled into the core.
|
||||
*/
|
||||
static irqreturn_t adis16201_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adis16201_state *st = iio_priv(indio_dev);
|
||||
|
||||
int i = 0;
|
||||
s16 *data;
|
||||
|
||||
data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
dev_err(&st->us->dev, "memory alloc failed in ring bh");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)
|
||||
&& adis16201_read_ring_data(indio_dev, st->rx) >= 0)
|
||||
for (; i < bitmap_weight(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength); i++)
|
||||
data[i] = be16_to_cpup((__be16 *)&(st->rx[i*2]));
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp)
|
||||
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)data);
|
||||
|
||||
kfree(data);
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_dealloc_pollfunc(indio_dev->pollfunc);
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops adis16201_ring_setup_ops = {
|
||||
.preenable = &iio_sw_buffer_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
};
|
||||
|
||||
int adis16201_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iio_buffer *ring;
|
||||
|
||||
ring = iio_sw_rb_allocate(indio_dev);
|
||||
if (!ring) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
ring->scan_timestamp = true;
|
||||
indio_dev->setup_ops = &adis16201_ring_setup_ops;
|
||||
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
&adis16201_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
indio_dev,
|
||||
"adis16201_consumer%d",
|
||||
indio_dev->id);
|
||||
if (indio_dev->pollfunc == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_iio_sw_rb_free;
|
||||
}
|
||||
|
||||
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
|
||||
return 0;
|
||||
error_iio_sw_rb_free:
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include "adis16201.h"
|
||||
|
||||
/**
|
||||
* adis16201_data_rdy_trigger_set_state() set datardy interrupt state
|
||||
**/
|
||||
static int adis16201_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
|
||||
return adis16201_set_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops adis16201_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &adis16201_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int adis16201_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct adis16201_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->trig = iio_trigger_alloc("adis16201-dev%d", indio_dev->id);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
ret = request_irq(st->us->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"adis16201",
|
||||
st->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
st->trig->dev.parent = &st->us->dev;
|
||||
st->trig->ops = &adis16201_trigger_ops;
|
||||
st->trig->private_data = indio_dev;
|
||||
ret = iio_trigger_register(st->trig);
|
||||
|
||||
/* select default trigger */
|
||||
indio_dev->trig = st->trig;
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(st->us->irq, st->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(st->trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void adis16201_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16201_state *state = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(state->trig);
|
||||
free_irq(state->us->irq, state->trig);
|
||||
iio_trigger_free(state->trig);
|
||||
}
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
#define ADIS16203_STARTUP_DELAY 220 /* ms */
|
||||
|
||||
#define ADIS16203_READ_REG(a) a
|
||||
#define ADIS16203_WRITE_REG(a) ((a) | 0x80)
|
||||
|
||||
#define ADIS16203_FLASH_CNT 0x00 /* Flash memory write count */
|
||||
#define ADIS16203_SUPPLY_OUT 0x02 /* Output, power supply */
|
||||
#define ADIS16203_AUX_ADC 0x08 /* Output, auxiliary ADC input */
|
||||
|
@ -27,8 +24,6 @@
|
|||
#define ADIS16203_DIAG_STAT 0x3C /* Diagnostics, system status register */
|
||||
#define ADIS16203_GLOB_CMD 0x3E /* Operation, system command register */
|
||||
|
||||
#define ADIS16203_OUTPUTS 5
|
||||
|
||||
/* MSC_CTRL */
|
||||
#define ADIS16203_MSC_CTRL_PWRUP_SELF_TEST (1 << 10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
|
||||
#define ADIS16203_MSC_CTRL_REVERSE_ROT_EN (1 << 9) /* Reverses rotation of both inclination outputs */
|
||||
|
@ -40,86 +35,25 @@
|
|||
/* DIAG_STAT */
|
||||
#define ADIS16203_DIAG_STAT_ALARM2 (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
|
||||
#define ADIS16203_DIAG_STAT_ALARM1 (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
|
||||
#define ADIS16203_DIAG_STAT_SELFTEST_FAIL (1<<5) /* Self-test diagnostic error flag */
|
||||
#define ADIS16203_DIAG_STAT_SPI_FAIL (1<<3) /* SPI communications failure */
|
||||
#define ADIS16203_DIAG_STAT_FLASH_UPT (1<<2) /* Flash update failure */
|
||||
#define ADIS16203_DIAG_STAT_POWER_HIGH (1<<1) /* Power supply above 3.625 V */
|
||||
#define ADIS16203_DIAG_STAT_POWER_LOW (1<<0) /* Power supply below 3.15 V */
|
||||
#define ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT 5 /* Self-test diagnostic error flag */
|
||||
#define ADIS16203_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */
|
||||
#define ADIS16203_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */
|
||||
#define ADIS16203_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */
|
||||
#define ADIS16203_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 3.15 V */
|
||||
|
||||
/* GLOB_CMD */
|
||||
#define ADIS16203_GLOB_CMD_SW_RESET (1<<7)
|
||||
#define ADIS16203_GLOB_CMD_CLEAR_STAT (1<<4)
|
||||
#define ADIS16203_GLOB_CMD_FACTORY_CAL (1<<1)
|
||||
|
||||
#define ADIS16203_MAX_TX 12
|
||||
#define ADIS16203_MAX_RX 10
|
||||
|
||||
#define ADIS16203_ERROR_ACTIVE (1<<14)
|
||||
|
||||
/**
|
||||
* struct adis16203_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 adis16203_state {
|
||||
struct spi_device *us;
|
||||
struct iio_trigger *trig;
|
||||
struct mutex buf_lock;
|
||||
u8 tx[ADIS16203_MAX_TX] ____cacheline_aligned;
|
||||
u8 rx[ADIS16203_MAX_RX];
|
||||
};
|
||||
|
||||
int adis16203_set_irq(struct iio_dev *indio_dev, bool enable);
|
||||
|
||||
enum adis16203_scan {
|
||||
ADIS16203_SCAN_INCLI_X,
|
||||
ADIS16203_SCAN_INCLI_Y,
|
||||
ADIS16203_SCAN_SUPPLY,
|
||||
ADIS16203_SCAN_AUX_ADC,
|
||||
ADIS16203_SCAN_TEMP,
|
||||
ADIS16203_SCAN_INCLI_X,
|
||||
ADIS16203_SCAN_INCLI_Y,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
void adis16203_remove_trigger(struct iio_dev *indio_dev);
|
||||
int adis16203_probe_trigger(struct iio_dev *indio_dev);
|
||||
|
||||
ssize_t adis16203_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
int adis16203_configure_ring(struct iio_dev *indio_dev);
|
||||
void adis16203_unconfigure_ring(struct iio_dev *indio_dev);
|
||||
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline void adis16203_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adis16203_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
adis16203_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adis16203_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void adis16203_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
#endif /* SPI_ADIS16203_H_ */
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* ADIS16203 Programmable Digital Vibration Sensor driver
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
* Copyright 2030 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
@ -18,252 +18,14 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#include "adis16203.h"
|
||||
|
||||
/**
|
||||
* adis16203_spi_write_reg_8() - write single byte to a register
|
||||
* @indio_dev: iio device associated with child of actual device
|
||||
* @reg_address: the address of the register to be written
|
||||
* @val: the value to write
|
||||
**/
|
||||
static int adis16203_spi_write_reg_8(struct iio_dev *indio_dev,
|
||||
u8 reg_address,
|
||||
u8 val)
|
||||
{
|
||||
int ret;
|
||||
struct adis16203_state *st = iio_priv(indio_dev);
|
||||
#define DRIVER_NAME "adis16203"
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16203_WRITE_REG(reg_address);
|
||||
st->tx[1] = val;
|
||||
|
||||
ret = spi_write(st->us, st->tx, 2);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16203_spi_write_reg_16() - write 2 bytes to a pair of registers
|
||||
* @indio_dev: iio device associated with child of actual device
|
||||
* @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 adis16203_spi_write_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 value)
|
||||
{
|
||||
int ret;
|
||||
struct spi_message msg;
|
||||
struct adis16203_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] = ADIS16203_WRITE_REG(lower_reg_address);
|
||||
st->tx[1] = value & 0xFF;
|
||||
st->tx[2] = ADIS16203_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16203_spi_read_reg_16() - read 2 bytes from a 16-bit register
|
||||
* @indio_dev: iio device associated with child of actual 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 adis16203_spi_read_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 *val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16203_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,
|
||||
.delay_usecs = 20,
|
||||
}, {
|
||||
.rx_buf = st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = 20,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16203_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 int adis16203_check_status(struct iio_dev *indio_dev)
|
||||
{
|
||||
u16 status;
|
||||
int ret;
|
||||
|
||||
ret = adis16203_spi_read_reg_16(indio_dev,
|
||||
ADIS16203_DIAG_STAT,
|
||||
&status);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Reading status failed\n");
|
||||
goto error_ret;
|
||||
}
|
||||
ret = status & 0x1F;
|
||||
|
||||
if (status & ADIS16203_DIAG_STAT_SELFTEST_FAIL)
|
||||
dev_err(&indio_dev->dev, "Self test failure\n");
|
||||
if (status & ADIS16203_DIAG_STAT_SPI_FAIL)
|
||||
dev_err(&indio_dev->dev, "SPI failure\n");
|
||||
if (status & ADIS16203_DIAG_STAT_FLASH_UPT)
|
||||
dev_err(&indio_dev->dev, "Flash update failed\n");
|
||||
if (status & ADIS16203_DIAG_STAT_POWER_HIGH)
|
||||
dev_err(&indio_dev->dev, "Power supply above 3.625V\n");
|
||||
if (status & ADIS16203_DIAG_STAT_POWER_LOW)
|
||||
dev_err(&indio_dev->dev, "Power supply below 3.15V\n");
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16203_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16203_spi_write_reg_8(indio_dev,
|
||||
ADIS16203_GLOB_CMD,
|
||||
ADIS16203_GLOB_CMD_SW_RESET);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev, "problem resetting device");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adis16203_set_irq(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
u16 msc;
|
||||
|
||||
ret = adis16203_spi_read_reg_16(indio_dev, ADIS16203_MSC_CTRL, &msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
msc |= ADIS16203_MSC_CTRL_ACTIVE_HIGH;
|
||||
msc &= ~ADIS16203_MSC_CTRL_DATA_RDY_DIO1;
|
||||
if (enable)
|
||||
msc |= ADIS16203_MSC_CTRL_DATA_RDY_EN;
|
||||
else
|
||||
msc &= ~ADIS16203_MSC_CTRL_DATA_RDY_EN;
|
||||
|
||||
ret = adis16203_spi_write_reg_16(indio_dev, ADIS16203_MSC_CTRL, msc);
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16203_self_test(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16203_spi_write_reg_16(indio_dev,
|
||||
ADIS16203_MSC_CTRL,
|
||||
ADIS16203_MSC_CTRL_SELF_TEST_EN);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "problem starting self test");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
adis16203_check_status(indio_dev);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16203_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Disable IRQ */
|
||||
ret = adis16203_set_irq(indio_dev, false);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "disable irq failed");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Do self test */
|
||||
ret = adis16203_self_test(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "self test failure");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Read status register to check the result */
|
||||
ret = adis16203_check_status(indio_dev);
|
||||
if (ret) {
|
||||
adis16203_reset(indio_dev);
|
||||
dev_err(&indio_dev->dev, "device not playing ball -> reset");
|
||||
msleep(ADIS16203_STARTUP_DELAY);
|
||||
ret = adis16203_check_status(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "giving up");
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum adis16203_chan {
|
||||
in_supply,
|
||||
in_aux,
|
||||
incli_x,
|
||||
incli_y,
|
||||
temp,
|
||||
};
|
||||
|
||||
static u8 adis16203_addresses[5][2] = {
|
||||
[in_supply] = { ADIS16203_SUPPLY_OUT },
|
||||
[in_aux] = { ADIS16203_AUX_ADC },
|
||||
[incli_x] = { ADIS16203_XINCL_OUT, ADIS16203_INCL_NULL},
|
||||
[incli_y] = { ADIS16203_YINCL_OUT },
|
||||
[temp] = { ADIS16203_TEMP_OUT }
|
||||
static const u8 adis16203_addresses[] = {
|
||||
[ADIS16203_SCAN_INCLI_X] = ADIS16203_INCL_NULL,
|
||||
};
|
||||
|
||||
static int adis16203_write_raw(struct iio_dev *indio_dev,
|
||||
|
@ -272,9 +34,10 @@ static int adis16203_write_raw(struct iio_dev *indio_dev,
|
|||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
/* currently only one writable parameter which keeps this simple */
|
||||
u8 addr = adis16203_addresses[chan->address][1];
|
||||
return adis16203_spi_write_reg_16(indio_dev, addr, val & 0x3FFF);
|
||||
u8 addr = adis16203_addresses[chan->scan_index];
|
||||
return adis_write_reg_16(st, addr, val & 0x3FFF);
|
||||
}
|
||||
|
||||
static int adis16203_read_raw(struct iio_dev *indio_dev,
|
||||
|
@ -282,35 +45,15 @@ static int adis16203_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int bits;
|
||||
u8 addr;
|
||||
s16 val16;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16203_addresses[chan->address][0];
|
||||
ret = adis16203_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val16 & ADIS16203_ERROR_ACTIVE) {
|
||||
ret = adis16203_check_status(indio_dev);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
val16 = val16 & ((1 << chan->scan_type.realbits) - 1);
|
||||
if (chan->scan_type.sign == 's')
|
||||
val16 = (s16)(val16 <<
|
||||
(16 - chan->scan_type.realbits)) >>
|
||||
(16 - chan->scan_type.realbits);
|
||||
*val = val16;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
return adis_single_conversion(indio_dev, chan,
|
||||
ADIS16203_ERROR_ACTIVE, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
|
@ -339,8 +82,8 @@ static int adis16203_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
bits = 14;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16203_addresses[chan->address][1];
|
||||
ret = adis16203_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
addr = adis16203_addresses[chan->scan_index];
|
||||
ret = adis_read_reg_16(st, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
|
@ -356,89 +99,53 @@ static int adis16203_read_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
static const struct iio_chan_spec adis16203_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.extend_name = "supply",
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = in_supply,
|
||||
.scan_index = ADIS16203_SCAN_SUPPLY,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = in_aux,
|
||||
.scan_index = ADIS16203_SCAN_AUX_ADC,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_INCLI,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
|
||||
.address = incli_x,
|
||||
.scan_index = ADIS16203_SCAN_INCLI_X,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, { /* Fixme: Not what it appears to be - see data sheet */
|
||||
.type = IIO_INCLI,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT,
|
||||
.address = incli_y,
|
||||
.scan_index = ADIS16203_SCAN_INCLI_Y,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.address = temp,
|
||||
.scan_index = ADIS16203_SCAN_TEMP,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
},
|
||||
ADIS_SUPPLY_CHAN(ADIS16203_SUPPLY_OUT, ADIS16203_SCAN_SUPPLY, 12),
|
||||
ADIS_AUX_ADC_CHAN(ADIS16203_AUX_ADC, ADIS16203_SCAN_AUX_ADC, 12),
|
||||
ADIS_INCLI_CHAN(X, ADIS16203_XINCL_OUT, ADIS16203_SCAN_INCLI_X,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14),
|
||||
/* Fixme: Not what it appears to be - see data sheet */
|
||||
ADIS_INCLI_CHAN(Y, ADIS16203_YINCL_OUT, ADIS16203_SCAN_INCLI_Y, 0, 14),
|
||||
ADIS_TEMP_CHAN(ADIS16203_TEMP_OUT, ADIS16203_SCAN_TEMP, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(5),
|
||||
};
|
||||
|
||||
static const struct iio_info adis16203_info = {
|
||||
.read_raw = &adis16203_read_raw,
|
||||
.write_raw = &adis16203_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const char * const adis16203_status_error_msgs[] = {
|
||||
[ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
|
||||
[ADIS16203_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
|
||||
[ADIS16203_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
|
||||
[ADIS16203_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
|
||||
[ADIS16203_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16203_data = {
|
||||
.read_delay = 20,
|
||||
.msc_ctrl_reg = ADIS16203_MSC_CTRL,
|
||||
.glob_cmd_reg = ADIS16203_GLOB_CMD,
|
||||
.diag_stat_reg = ADIS16203_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16203_MSC_CTRL_SELF_TEST_EN,
|
||||
.startup_delay = ADIS16203_STARTUP_DELAY,
|
||||
|
||||
.status_error_msgs = adis16203_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT) |
|
||||
BIT(ADIS16203_DIAG_STAT_SPI_FAIL_BIT) |
|
||||
BIT(ADIS16203_DIAG_STAT_FLASH_UPT_BIT) |
|
||||
BIT(ADIS16203_DIAG_STAT_POWER_HIGH_BIT) |
|
||||
BIT(ADIS16203_DIAG_STAT_POWER_LOW_BIT),
|
||||
};
|
||||
|
||||
static int __devinit adis16203_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct adis16203_state *st;
|
||||
struct adis *st;
|
||||
|
||||
/* setup the industrialio driver allocated elements */
|
||||
indio_dev = iio_device_alloc(sizeof(*st));
|
||||
|
@ -449,8 +156,6 @@ static int __devinit adis16203_probe(struct spi_device *spi)
|
|||
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);
|
||||
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
|
@ -459,41 +164,27 @@ static int __devinit adis16203_probe(struct spi_device *spi)
|
|||
indio_dev->info = &adis16203_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis16203_configure_ring(indio_dev);
|
||||
ret = adis_init(st, indio_dev, spi, &adis16203_data);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = iio_buffer_register(indio_dev,
|
||||
adis16203_channels,
|
||||
ARRAY_SIZE(adis16203_channels));
|
||||
if (ret) {
|
||||
printk(KERN_ERR "failed to initialize the ring\n");
|
||||
goto error_unreg_ring_funcs;
|
||||
}
|
||||
|
||||
if (spi->irq) {
|
||||
ret = adis16203_probe_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_uninitialize_ring;
|
||||
}
|
||||
ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
/* Get the device into a sane initial state */
|
||||
ret = adis16203_initial_setup(indio_dev);
|
||||
ret = adis_initial_startup(st);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
adis16203_remove_trigger(indio_dev);
|
||||
error_uninitialize_ring:
|
||||
iio_buffer_unregister(indio_dev);
|
||||
error_unreg_ring_funcs:
|
||||
adis16203_unconfigure_ring(indio_dev);
|
||||
error_cleanup_buffer_trigger:
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
error_ret:
|
||||
|
@ -503,11 +194,10 @@ static int __devinit adis16203_probe(struct spi_device *spi)
|
|||
static int __devexit adis16203_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16203_remove_trigger(indio_dev);
|
||||
iio_buffer_unregister(indio_dev);
|
||||
adis16203_unconfigure_ring(indio_dev);
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include "../ring_sw.h"
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include "adis16203.h"
|
||||
|
||||
/**
|
||||
* adis16203_read_ring_data() read data registers which will be placed into ring
|
||||
* @indio_dev: the IIO device
|
||||
* @rx: somewhere to pass back the value read
|
||||
**/
|
||||
static int adis16203_read_ring_data(struct iio_dev *indio_dev, u8 *rx)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16203_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[ADIS16203_OUTPUTS + 1];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
memset(xfers, 0, sizeof(xfers));
|
||||
for (i = 0; i <= ADIS16203_OUTPUTS; i++) {
|
||||
xfers[i].bits_per_word = 8;
|
||||
xfers[i].cs_change = 1;
|
||||
xfers[i].len = 2;
|
||||
xfers[i].delay_usecs = 20;
|
||||
xfers[i].tx_buf = st->tx + 2 * i;
|
||||
if (i < 1) /* SUPPLY_OUT: 0x02, AUX_ADC: 0x08 */
|
||||
st->tx[2 * i] = ADIS16203_READ_REG(ADIS16203_SUPPLY_OUT + 2 * i);
|
||||
else
|
||||
st->tx[2 * i] = ADIS16203_READ_REG(ADIS16203_SUPPLY_OUT + 2 * i + 6);
|
||||
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;
|
||||
}
|
||||
|
||||
/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
|
||||
* specific to be rolled into the core.
|
||||
*/
|
||||
static irqreturn_t adis16203_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adis16203_state *st = iio_priv(indio_dev);
|
||||
|
||||
int i = 0;
|
||||
s16 *data;
|
||||
|
||||
data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
dev_err(&st->us->dev, "memory alloc failed in ring bh");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength) &&
|
||||
adis16203_read_ring_data(indio_dev, st->rx) >= 0)
|
||||
for (; i < bitmap_weight(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength); i++)
|
||||
data[i] = be16_to_cpup((__be16 *)&(st->rx[i*2]));
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp)
|
||||
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)data);
|
||||
|
||||
kfree(data);
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void adis16203_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_dealloc_pollfunc(indio_dev->pollfunc);
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops adis16203_ring_setup_ops = {
|
||||
.preenable = &iio_sw_buffer_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
};
|
||||
|
||||
int adis16203_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iio_buffer *ring;
|
||||
|
||||
ring = iio_sw_rb_allocate(indio_dev);
|
||||
if (!ring) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
ring->scan_timestamp = true;
|
||||
indio_dev->setup_ops = &adis16203_ring_setup_ops;
|
||||
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
&adis16203_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
indio_dev,
|
||||
"adis16203_consumer%d",
|
||||
indio_dev->id);
|
||||
if (indio_dev->pollfunc == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_iio_sw_rb_free;
|
||||
}
|
||||
|
||||
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
|
||||
return 0;
|
||||
|
||||
error_iio_sw_rb_free:
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include "adis16203.h"
|
||||
|
||||
/**
|
||||
* adis16203_data_rdy_trigger_set_state() set datardy interrupt state
|
||||
**/
|
||||
static int adis16203_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
|
||||
return adis16203_set_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops adis16203_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &adis16203_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int adis16203_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct adis16203_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->trig = iio_trigger_alloc("adis16203-dev%d", indio_dev->id);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = request_irq(st->us->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"adis16203",
|
||||
st->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
|
||||
st->trig->dev.parent = &st->us->dev;
|
||||
st->trig->ops = &adis16203_trigger_ops;
|
||||
st->trig->private_data = indio_dev;
|
||||
ret = iio_trigger_register(st->trig);
|
||||
|
||||
/* select default trigger */
|
||||
indio_dev->trig = st->trig;
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(st->us->irq, st->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(st->trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void adis16203_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16203_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(st->trig);
|
||||
free_irq(st->us->irq, st->trig);
|
||||
iio_trigger_free(st->trig);
|
||||
}
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
#define ADIS16204_STARTUP_DELAY 220 /* ms */
|
||||
|
||||
#define ADIS16204_READ_REG(a) a
|
||||
#define ADIS16204_WRITE_REG(a) ((a) | 0x80)
|
||||
|
||||
#define ADIS16204_FLASH_CNT 0x00 /* Flash memory write count */
|
||||
#define ADIS16204_SUPPLY_OUT 0x02 /* Output, power supply */
|
||||
#define ADIS16204_XACCL_OUT 0x04 /* Output, x-axis accelerometer */
|
||||
|
@ -35,8 +32,6 @@
|
|||
#define ADIS16204_DIAG_STAT 0x3C /* Diagnostics, system status register */
|
||||
#define ADIS16204_GLOB_CMD 0x3E /* Operation, system command register */
|
||||
|
||||
#define ADIS16204_OUTPUTS 5
|
||||
|
||||
/* MSC_CTRL */
|
||||
#define ADIS16204_MSC_CTRL_PWRUP_SELF_TEST (1 << 10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
|
||||
#define ADIS16204_MSC_CTRL_SELF_TEST_EN (1 << 8) /* Self-test enable */
|
||||
|
@ -47,87 +42,27 @@
|
|||
/* DIAG_STAT */
|
||||
#define ADIS16204_DIAG_STAT_ALARM2 (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
|
||||
#define ADIS16204_DIAG_STAT_ALARM1 (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
|
||||
#define ADIS16204_DIAG_STAT_SELFTEST_FAIL (1<<5) /* Self-test diagnostic error flag: 1 = error condition,
|
||||
#define ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT 5 /* Self-test diagnostic error flag: 1 = error condition,
|
||||
0 = normal operation */
|
||||
#define ADIS16204_DIAG_STAT_SPI_FAIL (1<<3) /* SPI communications failure */
|
||||
#define ADIS16204_DIAG_STAT_FLASH_UPT (1<<2) /* Flash update failure */
|
||||
#define ADIS16204_DIAG_STAT_POWER_HIGH (1<<1) /* Power supply above 3.625 V */
|
||||
#define ADIS16204_DIAG_STAT_POWER_LOW (1<<0) /* Power supply below 2.975 V */
|
||||
#define ADIS16204_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */
|
||||
#define ADIS16204_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */
|
||||
#define ADIS16204_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */
|
||||
#define ADIS16204_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 2.975 V */
|
||||
|
||||
/* GLOB_CMD */
|
||||
#define ADIS16204_GLOB_CMD_SW_RESET (1<<7)
|
||||
#define ADIS16204_GLOB_CMD_CLEAR_STAT (1<<4)
|
||||
#define ADIS16204_GLOB_CMD_FACTORY_CAL (1<<1)
|
||||
|
||||
#define ADIS16204_MAX_TX 24
|
||||
#define ADIS16204_MAX_RX 24
|
||||
|
||||
#define ADIS16204_ERROR_ACTIVE (1<<14)
|
||||
|
||||
/**
|
||||
* struct adis16204_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 adis16204_state {
|
||||
struct spi_device *us;
|
||||
struct iio_trigger *trig;
|
||||
struct mutex buf_lock;
|
||||
u8 tx[ADIS16204_MAX_TX] ____cacheline_aligned;
|
||||
u8 rx[ADIS16204_MAX_RX];
|
||||
};
|
||||
|
||||
int adis16204_set_irq(struct iio_dev *indio_dev, bool enable);
|
||||
|
||||
enum adis16204_scan {
|
||||
ADIS16204_SCAN_SUPPLY,
|
||||
ADIS16204_SCAN_ACC_X,
|
||||
ADIS16204_SCAN_ACC_Y,
|
||||
ADIS16204_SCAN_ACC_XY,
|
||||
ADIS16204_SCAN_SUPPLY,
|
||||
ADIS16204_SCAN_AUX_ADC,
|
||||
ADIS16204_SCAN_TEMP,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
void adis16204_remove_trigger(struct iio_dev *indio_dev);
|
||||
int adis16204_probe_trigger(struct iio_dev *indio_dev);
|
||||
|
||||
ssize_t adis16204_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
int adis16204_configure_ring(struct iio_dev *indio_dev);
|
||||
void adis16204_unconfigure_ring(struct iio_dev *indio_dev);
|
||||
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline void adis16204_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adis16204_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
adis16204_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adis16204_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void adis16204_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
#endif /* SPI_ADIS16204_H_ */
|
||||
|
|
|
@ -21,259 +21,16 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#include "adis16204.h"
|
||||
|
||||
/**
|
||||
* adis16204_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 adis16204_spi_write_reg_8(struct iio_dev *indio_dev,
|
||||
u8 reg_address,
|
||||
u8 val)
|
||||
{
|
||||
int ret;
|
||||
struct adis16204_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16204_WRITE_REG(reg_address);
|
||||
st->tx[1] = val;
|
||||
|
||||
ret = spi_write(st->us, st->tx, 2);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16204_spi_write_reg_16() - write 2 bytes to a pair of registers
|
||||
* @indio_dev: iio device associated with child of actual device
|
||||
* @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 adis16204_spi_write_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 value)
|
||||
{
|
||||
int ret;
|
||||
struct spi_message msg;
|
||||
struct adis16204_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,
|
||||
.cs_change = 1,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16204_WRITE_REG(lower_reg_address);
|
||||
st->tx[1] = value & 0xFF;
|
||||
st->tx[2] = ADIS16204_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16204_spi_read_reg_16() - read 2 bytes from a 16-bit register
|
||||
* @indio_dev: iio device associated with child of actual 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 adis16204_spi_read_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 *val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16204_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,
|
||||
.delay_usecs = 20,
|
||||
}, {
|
||||
.rx_buf = st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = 20,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16204_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 int adis16204_check_status(struct iio_dev *indio_dev)
|
||||
{
|
||||
u16 status;
|
||||
int ret;
|
||||
|
||||
ret = adis16204_spi_read_reg_16(indio_dev,
|
||||
ADIS16204_DIAG_STAT, &status);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Reading status failed\n");
|
||||
goto error_ret;
|
||||
}
|
||||
ret = status & 0x1F;
|
||||
|
||||
if (status & ADIS16204_DIAG_STAT_SELFTEST_FAIL)
|
||||
dev_err(&indio_dev->dev, "Self test failure\n");
|
||||
if (status & ADIS16204_DIAG_STAT_SPI_FAIL)
|
||||
dev_err(&indio_dev->dev, "SPI failure\n");
|
||||
if (status & ADIS16204_DIAG_STAT_FLASH_UPT)
|
||||
dev_err(&indio_dev->dev, "Flash update failed\n");
|
||||
if (status & ADIS16204_DIAG_STAT_POWER_HIGH)
|
||||
dev_err(&indio_dev->dev, "Power supply above 3.625V\n");
|
||||
if (status & ADIS16204_DIAG_STAT_POWER_LOW)
|
||||
dev_err(&indio_dev->dev, "Power supply below 2.975V\n");
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16204_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16204_spi_write_reg_8(indio_dev,
|
||||
ADIS16204_GLOB_CMD,
|
||||
ADIS16204_GLOB_CMD_SW_RESET);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev, "problem resetting device");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adis16204_set_irq(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
u16 msc;
|
||||
|
||||
ret = adis16204_spi_read_reg_16(indio_dev, ADIS16204_MSC_CTRL, &msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
msc |= ADIS16204_MSC_CTRL_ACTIVE_HIGH;
|
||||
msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_DIO2;
|
||||
if (enable)
|
||||
msc |= ADIS16204_MSC_CTRL_DATA_RDY_EN;
|
||||
else
|
||||
msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_EN;
|
||||
|
||||
ret = adis16204_spi_write_reg_16(indio_dev, ADIS16204_MSC_CTRL, msc);
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16204_self_test(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16204_spi_write_reg_16(indio_dev,
|
||||
ADIS16204_MSC_CTRL,
|
||||
ADIS16204_MSC_CTRL_SELF_TEST_EN);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "problem starting self test");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
adis16204_check_status(indio_dev);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16204_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Disable IRQ */
|
||||
ret = adis16204_set_irq(indio_dev, false);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "disable irq failed");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Do self test */
|
||||
ret = adis16204_self_test(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "self test failure");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Read status register to check the result */
|
||||
ret = adis16204_check_status(indio_dev);
|
||||
if (ret) {
|
||||
adis16204_reset(indio_dev);
|
||||
dev_err(&indio_dev->dev, "device not playing ball -> reset");
|
||||
msleep(ADIS16204_STARTUP_DELAY);
|
||||
ret = adis16204_check_status(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "giving up");
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Unique to this driver currently */
|
||||
|
||||
enum adis16204_channel {
|
||||
in_supply,
|
||||
in_aux,
|
||||
temp,
|
||||
accel_x,
|
||||
accel_y,
|
||||
accel_xy,
|
||||
};
|
||||
|
||||
static u8 adis16204_addresses[6][3] = {
|
||||
[in_supply] = { ADIS16204_SUPPLY_OUT },
|
||||
[in_aux] = { ADIS16204_AUX_ADC },
|
||||
[temp] = { ADIS16204_TEMP_OUT },
|
||||
[accel_x] = { ADIS16204_XACCL_OUT, ADIS16204_XACCL_NULL,
|
||||
ADIS16204_X_PEAK_OUT },
|
||||
[accel_y] = { ADIS16204_XACCL_OUT, ADIS16204_YACCL_NULL,
|
||||
ADIS16204_Y_PEAK_OUT },
|
||||
[accel_xy] = { ADIS16204_XY_RSS_OUT, 0,
|
||||
ADIS16204_XY_PEAK_OUT },
|
||||
static const u8 adis16204_addresses[][2] = {
|
||||
[ADIS16204_SCAN_ACC_X] = { ADIS16204_XACCL_NULL, ADIS16204_X_PEAK_OUT },
|
||||
[ADIS16204_SCAN_ACC_Y] = { ADIS16204_YACCL_NULL, ADIS16204_Y_PEAK_OUT },
|
||||
[ADIS16204_SCAN_ACC_XY] = { 0, ADIS16204_XY_PEAK_OUT },
|
||||
};
|
||||
|
||||
static int adis16204_read_raw(struct iio_dev *indio_dev,
|
||||
|
@ -281,6 +38,7 @@ static int adis16204_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int bits;
|
||||
u8 addr;
|
||||
|
@ -289,29 +47,8 @@ static int adis16204_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16204_addresses[chan->address][0];
|
||||
ret = adis16204_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val16 & ADIS16204_ERROR_ACTIVE) {
|
||||
ret = adis16204_check_status(indio_dev);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
val16 = val16 & ((1 << chan->scan_type.realbits) - 1);
|
||||
if (chan->scan_type.sign == 's')
|
||||
val16 = (s16)(val16 <<
|
||||
(16 - chan->scan_type.realbits)) >>
|
||||
(16 - chan->scan_type.realbits);
|
||||
*val = val16;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
return adis_single_conversion(indio_dev, chan,
|
||||
ADIS16204_ERROR_ACTIVE, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
|
@ -351,14 +88,14 @@ static int adis16204_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_PEAK:
|
||||
if (mask == IIO_CHAN_INFO_CALIBBIAS) {
|
||||
bits = 12;
|
||||
addrind = 1;
|
||||
addrind = 0;
|
||||
} else { /* PEAK_SEPARATE */
|
||||
bits = 14;
|
||||
addrind = 2;
|
||||
addrind = 1;
|
||||
}
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16204_addresses[chan->address][addrind];
|
||||
ret = adis16204_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
addr = adis16204_addresses[chan->scan_index][addrind];
|
||||
ret = adis_read_reg_16(st, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
|
@ -378,6 +115,7 @@ static int adis16204_write_raw(struct iio_dev *indio_dev,
|
|||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int bits;
|
||||
s16 val16;
|
||||
u8 addr;
|
||||
|
@ -391,112 +129,63 @@ static int adis16204_write_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
val16 = val & ((1 << bits) - 1);
|
||||
addr = adis16204_addresses[chan->address][1];
|
||||
return adis16204_spi_write_reg_16(indio_dev, addr, val16);
|
||||
addr = adis16204_addresses[chan->scan_index][1];
|
||||
return adis_write_reg_16(st, addr, val16);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16204_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1, /* Note was not previously indexed */
|
||||
.channel = 0,
|
||||
.extend_name = "supply",
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = in_supply,
|
||||
.scan_index = ADIS16204_SCAN_SUPPLY,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = in_aux,
|
||||
.scan_index = ADIS16204_SCAN_AUX_ADC,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.address = temp,
|
||||
.scan_index = ADIS16204_SCAN_TEMP,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
ADIS_SUPPLY_CHAN(ADIS16204_SUPPLY_OUT, ADIS16204_SCAN_SUPPLY, 12),
|
||||
ADIS_AUX_ADC_CHAN(ADIS16204_AUX_ADC, ADIS16204_SCAN_AUX_ADC, 12),
|
||||
ADIS_TEMP_CHAN(ADIS16204_TEMP_OUT, ADIS16204_SCAN_TEMP, 12),
|
||||
ADIS_ACCEL_CHAN(X, ADIS16204_XACCL_OUT, ADIS16204_SCAN_ACC_X,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_PEAK_SEPARATE_BIT,
|
||||
.address = accel_x,
|
||||
.scan_index = ADIS16204_SCAN_ACC_X,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 14),
|
||||
ADIS_ACCEL_CHAN(Y, ADIS16204_YACCL_OUT, ADIS16204_SCAN_ACC_Y,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_PEAK_SEPARATE_BIT,
|
||||
.address = accel_y,
|
||||
.scan_index = ADIS16204_SCAN_ACC_Y,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 14),
|
||||
ADIS_ACCEL_CHAN(ROOT_SUM_SQUARED_X_Y, ADIS16204_XY_RSS_OUT,
|
||||
ADIS16204_SCAN_ACC_XY, IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 14),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(5),
|
||||
{
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_ROOT_SUM_SQUARED_X_Y,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_PEAK_SEPARATE_BIT,
|
||||
.address = accel_xy,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info adis16204_info = {
|
||||
.read_raw = &adis16204_read_raw,
|
||||
.write_raw = &adis16204_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const char * const adis16204_status_error_msgs[] = {
|
||||
[ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
|
||||
[ADIS16204_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
|
||||
[ADIS16204_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
|
||||
[ADIS16204_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
|
||||
[ADIS16204_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16204_data = {
|
||||
.read_delay = 20,
|
||||
.msc_ctrl_reg = ADIS16204_MSC_CTRL,
|
||||
.glob_cmd_reg = ADIS16204_GLOB_CMD,
|
||||
.diag_stat_reg = ADIS16204_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16204_MSC_CTRL_SELF_TEST_EN,
|
||||
.startup_delay = ADIS16204_STARTUP_DELAY,
|
||||
|
||||
.status_error_msgs = adis16204_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT) |
|
||||
BIT(ADIS16204_DIAG_STAT_SPI_FAIL_BIT) |
|
||||
BIT(ADIS16204_DIAG_STAT_FLASH_UPT_BIT) |
|
||||
BIT(ADIS16204_DIAG_STAT_POWER_HIGH_BIT) |
|
||||
BIT(ADIS16204_DIAG_STAT_POWER_LOW_BIT),
|
||||
};
|
||||
|
||||
static int __devinit adis16204_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct adis16204_state *st;
|
||||
struct adis *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
/* setup the industrialio driver allocated elements */
|
||||
|
@ -508,8 +197,6 @@ static int __devinit adis16204_probe(struct spi_device *spi)
|
|||
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);
|
||||
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
|
@ -518,40 +205,26 @@ static int __devinit adis16204_probe(struct spi_device *spi)
|
|||
indio_dev->num_channels = ARRAY_SIZE(adis16204_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis16204_configure_ring(indio_dev);
|
||||
ret = adis_init(st, indio_dev, spi, &adis16204_data);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = iio_buffer_register(indio_dev,
|
||||
adis16204_channels,
|
||||
6);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "failed to initialize the ring\n");
|
||||
goto error_unreg_ring_funcs;
|
||||
}
|
||||
|
||||
if (spi->irq) {
|
||||
ret = adis16204_probe_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_uninitialize_ring;
|
||||
}
|
||||
ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
/* Get the device into a sane initial state */
|
||||
ret = adis16204_initial_setup(indio_dev);
|
||||
ret = adis_initial_startup(st);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
adis16204_remove_trigger(indio_dev);
|
||||
error_uninitialize_ring:
|
||||
iio_buffer_unregister(indio_dev);
|
||||
error_unreg_ring_funcs:
|
||||
adis16204_unconfigure_ring(indio_dev);
|
||||
error_cleanup_buffer_trigger:
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
error_ret:
|
||||
|
@ -561,11 +234,10 @@ static int __devinit adis16204_probe(struct spi_device *spi)
|
|||
static int __devexit adis16204_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16204_remove_trigger(indio_dev);
|
||||
iio_buffer_unregister(indio_dev);
|
||||
adis16204_unconfigure_ring(indio_dev);
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include "../ring_sw.h"
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include "adis16204.h"
|
||||
|
||||
/**
|
||||
* adis16204_read_ring_data() read data registers which will be placed into ring
|
||||
* @indio_dev: the IIO device
|
||||
* @rx: somewhere to pass back the value read
|
||||
**/
|
||||
static int adis16204_read_ring_data(struct iio_dev *indio_dev, u8 *rx)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16204_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[ADIS16204_OUTPUTS + 1];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
memset(xfers, 0, sizeof(xfers));
|
||||
for (i = 0; i <= ADIS16204_OUTPUTS; i++) {
|
||||
xfers[i].bits_per_word = 8;
|
||||
xfers[i].cs_change = 1;
|
||||
xfers[i].len = 2;
|
||||
xfers[i].delay_usecs = 20;
|
||||
xfers[i].tx_buf = st->tx + 2 * i;
|
||||
st->tx[2 * i]
|
||||
= ADIS16204_READ_REG(ADIS16204_SUPPLY_OUT + 2 * i);
|
||||
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;
|
||||
}
|
||||
|
||||
/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
|
||||
* specific to be rolled into the core.
|
||||
*/
|
||||
static irqreturn_t adis16204_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adis16204_state *st = iio_priv(indio_dev);
|
||||
int i = 0;
|
||||
s16 *data;
|
||||
|
||||
data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
dev_err(&st->us->dev, "memory alloc failed in ring bh");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength) &&
|
||||
adis16204_read_ring_data(indio_dev, st->rx) >= 0)
|
||||
for (; i < bitmap_weight(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength); i++)
|
||||
data[i] = be16_to_cpup((__be16 *)&(st->rx[i*2]));
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp)
|
||||
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)data);
|
||||
|
||||
kfree(data);
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void adis16204_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_dealloc_pollfunc(indio_dev->pollfunc);
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops adis16204_ring_setup_ops = {
|
||||
.preenable = &iio_sw_buffer_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
};
|
||||
|
||||
int adis16204_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iio_buffer *ring;
|
||||
|
||||
ring = iio_sw_rb_allocate(indio_dev);
|
||||
if (!ring) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
ring->scan_timestamp = true;
|
||||
indio_dev->setup_ops = &adis16204_ring_setup_ops;
|
||||
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
&adis16204_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
indio_dev,
|
||||
"%s_consumer%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (indio_dev->pollfunc == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_iio_sw_rb_free;
|
||||
}
|
||||
|
||||
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
|
||||
return 0;
|
||||
|
||||
error_iio_sw_rb_free:
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include "adis16204.h"
|
||||
|
||||
/**
|
||||
* adis16204_data_rdy_trigger_set_state() set datardy interrupt state
|
||||
**/
|
||||
static int adis16204_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
|
||||
return adis16204_set_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops adis16204_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &adis16204_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int adis16204_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct adis16204_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->trig = iio_trigger_alloc("adis16204-dev%d", indio_dev->id);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = request_irq(st->us->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"adis16204",
|
||||
st->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
|
||||
st->trig->dev.parent = &st->us->dev;
|
||||
st->trig->ops = &adis16204_trigger_ops;
|
||||
st->trig->private_data = indio_dev;
|
||||
ret = iio_trigger_register(st->trig);
|
||||
|
||||
/* select default trigger */
|
||||
indio_dev->trig = st->trig;
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(st->us->irq, st->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(st->trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void adis16204_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16204_state *state = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(state->trig);
|
||||
free_irq(state->us->irq, state->trig);
|
||||
iio_trigger_free(state->trig);
|
||||
}
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
#define ADIS16209_STARTUP_DELAY 220 /* ms */
|
||||
|
||||
#define ADIS16209_READ_REG(a) a
|
||||
#define ADIS16209_WRITE_REG(a) ((a) | 0x80)
|
||||
|
||||
/* Flash memory write count */
|
||||
#define ADIS16209_FLASH_CNT 0x00
|
||||
/* Output, power supply */
|
||||
|
@ -61,8 +58,6 @@
|
|||
/* Operation, system command register */
|
||||
#define ADIS16209_GLOB_CMD 0x3E
|
||||
|
||||
#define ADIS16209_OUTPUTS 8
|
||||
|
||||
/* MSC_CTRL */
|
||||
/* Self-test at power-on: 1 = disabled, 0 = enabled */
|
||||
#define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST (1 << 10)
|
||||
|
@ -81,44 +76,23 @@
|
|||
/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
|
||||
#define ADIS16209_DIAG_STAT_ALARM1 (1<<8)
|
||||
/* Self-test diagnostic error flag: 1 = error condition, 0 = normal operation */
|
||||
#define ADIS16209_DIAG_STAT_SELFTEST_FAIL (1<<5)
|
||||
#define ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT 5
|
||||
/* SPI communications failure */
|
||||
#define ADIS16209_DIAG_STAT_SPI_FAIL (1<<3)
|
||||
#define ADIS16209_DIAG_STAT_SPI_FAIL_BIT 3
|
||||
/* Flash update failure */
|
||||
#define ADIS16209_DIAG_STAT_FLASH_UPT (1<<2)
|
||||
#define ADIS16209_DIAG_STAT_FLASH_UPT_BIT 2
|
||||
/* Power supply above 3.625 V */
|
||||
#define ADIS16209_DIAG_STAT_POWER_HIGH (1<<1)
|
||||
#define ADIS16209_DIAG_STAT_POWER_HIGH_BIT 1
|
||||
/* Power supply below 3.15 V */
|
||||
#define ADIS16209_DIAG_STAT_POWER_LOW (1<<0)
|
||||
#define ADIS16209_DIAG_STAT_POWER_LOW_BIT 0
|
||||
|
||||
/* GLOB_CMD */
|
||||
#define ADIS16209_GLOB_CMD_SW_RESET (1<<7)
|
||||
#define ADIS16209_GLOB_CMD_CLEAR_STAT (1<<4)
|
||||
#define ADIS16209_GLOB_CMD_FACTORY_CAL (1<<1)
|
||||
|
||||
#define ADIS16209_MAX_TX 24
|
||||
#define ADIS16209_MAX_RX 24
|
||||
|
||||
#define ADIS16209_ERROR_ACTIVE (1<<14)
|
||||
|
||||
/**
|
||||
* struct adis16209_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 adis16209_state {
|
||||
struct spi_device *us;
|
||||
struct iio_trigger *trig;
|
||||
struct mutex buf_lock;
|
||||
u8 tx[ADIS16209_MAX_TX] ____cacheline_aligned;
|
||||
u8 rx[ADIS16209_MAX_RX];
|
||||
};
|
||||
|
||||
int adis16209_set_irq(struct iio_dev *indio_dev, bool enable);
|
||||
|
||||
#define ADIS16209_SCAN_SUPPLY 0
|
||||
#define ADIS16209_SCAN_ACC_X 1
|
||||
#define ADIS16209_SCAN_ACC_Y 2
|
||||
|
@ -128,45 +102,4 @@ int adis16209_set_irq(struct iio_dev *indio_dev, bool enable);
|
|||
#define ADIS16209_SCAN_INCLI_Y 6
|
||||
#define ADIS16209_SCAN_ROT 7
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
|
||||
void adis16209_remove_trigger(struct iio_dev *indio_dev);
|
||||
int adis16209_probe_trigger(struct iio_dev *indio_dev);
|
||||
|
||||
ssize_t adis16209_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
int adis16209_configure_ring(struct iio_dev *indio_dev);
|
||||
void adis16209_unconfigure_ring(struct iio_dev *indio_dev);
|
||||
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline void adis16209_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adis16209_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
adis16209_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adis16209_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void adis16209_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
#endif /* SPI_ADIS16209_H_ */
|
||||
|
|
|
@ -19,260 +19,19 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#include "adis16209.h"
|
||||
|
||||
/**
|
||||
* adis16209_spi_write_reg_8() - write single byte to a register
|
||||
* @indio_dev: iio device associated with actual device
|
||||
* @reg_address: the address of the register to be written
|
||||
* @val: the value to write
|
||||
**/
|
||||
static int adis16209_spi_write_reg_8(struct iio_dev *indio_dev,
|
||||
u8 reg_address,
|
||||
u8 val)
|
||||
{
|
||||
int ret;
|
||||
struct adis16209_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16209_WRITE_REG(reg_address);
|
||||
st->tx[1] = val;
|
||||
|
||||
ret = spi_write(st->us, st->tx, 2);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16209_spi_write_reg_16() - write 2 bytes to a pair of registers
|
||||
* @indio_dev: iio device associated actual device
|
||||
* @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 adis16209_spi_write_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 value)
|
||||
{
|
||||
int ret;
|
||||
struct spi_message msg;
|
||||
struct adis16209_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = st->tx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = 30,
|
||||
}, {
|
||||
.tx_buf = st->tx + 2,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = 30,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16209_WRITE_REG(lower_reg_address);
|
||||
st->tx[1] = value & 0xFF;
|
||||
st->tx[2] = ADIS16209_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16209_spi_read_reg_16() - read 2 bytes from a 16-bit register
|
||||
* @indio_dev: iio device associated with 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 adis16209_spi_read_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 *val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16209_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,
|
||||
.delay_usecs = 30,
|
||||
}, {
|
||||
.rx_buf = st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = 30,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16209_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 int adis16209_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16209_spi_write_reg_8(indio_dev,
|
||||
ADIS16209_GLOB_CMD,
|
||||
ADIS16209_GLOB_CMD_SW_RESET);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev, "problem resetting device");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adis16209_set_irq(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
u16 msc;
|
||||
|
||||
ret = adis16209_spi_read_reg_16(indio_dev, ADIS16209_MSC_CTRL, &msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
msc |= ADIS16209_MSC_CTRL_ACTIVE_HIGH;
|
||||
msc &= ~ADIS16209_MSC_CTRL_DATA_RDY_DIO2;
|
||||
if (enable)
|
||||
msc |= ADIS16209_MSC_CTRL_DATA_RDY_EN;
|
||||
else
|
||||
msc &= ~ADIS16209_MSC_CTRL_DATA_RDY_EN;
|
||||
|
||||
ret = adis16209_spi_write_reg_16(indio_dev, ADIS16209_MSC_CTRL, msc);
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16209_check_status(struct iio_dev *indio_dev)
|
||||
{
|
||||
u16 status;
|
||||
int ret;
|
||||
|
||||
ret = adis16209_spi_read_reg_16(indio_dev,
|
||||
ADIS16209_DIAG_STAT, &status);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Reading status failed\n");
|
||||
goto error_ret;
|
||||
}
|
||||
ret = status & 0x1F;
|
||||
|
||||
if (status & ADIS16209_DIAG_STAT_SELFTEST_FAIL)
|
||||
dev_err(&indio_dev->dev, "Self test failure\n");
|
||||
if (status & ADIS16209_DIAG_STAT_SPI_FAIL)
|
||||
dev_err(&indio_dev->dev, "SPI failure\n");
|
||||
if (status & ADIS16209_DIAG_STAT_FLASH_UPT)
|
||||
dev_err(&indio_dev->dev, "Flash update failed\n");
|
||||
if (status & ADIS16209_DIAG_STAT_POWER_HIGH)
|
||||
dev_err(&indio_dev->dev, "Power supply above 3.625V\n");
|
||||
if (status & ADIS16209_DIAG_STAT_POWER_LOW)
|
||||
dev_err(&indio_dev->dev, "Power supply below 3.15V\n");
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16209_self_test(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16209_spi_write_reg_16(indio_dev,
|
||||
ADIS16209_MSC_CTRL,
|
||||
ADIS16209_MSC_CTRL_SELF_TEST_EN);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "problem starting self test");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
adis16209_check_status(indio_dev);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16209_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Disable IRQ */
|
||||
ret = adis16209_set_irq(indio_dev, false);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "disable irq failed");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Do self test */
|
||||
ret = adis16209_self_test(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "self test failure");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Read status register to check the result */
|
||||
ret = adis16209_check_status(indio_dev);
|
||||
if (ret) {
|
||||
adis16209_reset(indio_dev);
|
||||
dev_err(&indio_dev->dev, "device not playing ball -> reset");
|
||||
msleep(ADIS16209_STARTUP_DELAY);
|
||||
ret = adis16209_check_status(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "giving up");
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum adis16209_chan {
|
||||
in_supply,
|
||||
temp,
|
||||
accel_x,
|
||||
accel_y,
|
||||
incli_x,
|
||||
incli_y,
|
||||
in_aux,
|
||||
rot,
|
||||
};
|
||||
|
||||
static const u8 adis16209_addresses[8][2] = {
|
||||
[in_supply] = { ADIS16209_SUPPLY_OUT },
|
||||
[in_aux] = { ADIS16209_AUX_ADC },
|
||||
[accel_x] = { ADIS16209_XACCL_OUT, ADIS16209_XACCL_NULL },
|
||||
[accel_y] = { ADIS16209_YACCL_OUT, ADIS16209_YACCL_NULL },
|
||||
[incli_x] = { ADIS16209_XINCL_OUT, ADIS16209_XINCL_NULL },
|
||||
[incli_y] = { ADIS16209_YINCL_OUT, ADIS16209_YINCL_NULL },
|
||||
[rot] = { ADIS16209_ROT_OUT },
|
||||
[temp] = { ADIS16209_TEMP_OUT },
|
||||
static const u8 adis16209_addresses[8][1] = {
|
||||
[ADIS16209_SCAN_SUPPLY] = { },
|
||||
[ADIS16209_SCAN_AUX_ADC] = { },
|
||||
[ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL },
|
||||
[ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL },
|
||||
[ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL },
|
||||
[ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL },
|
||||
[ADIS16209_SCAN_ROT] = { },
|
||||
[ADIS16209_SCAN_TEMP] = { },
|
||||
};
|
||||
|
||||
static int adis16209_write_raw(struct iio_dev *indio_dev,
|
||||
|
@ -281,6 +40,7 @@ static int adis16209_write_raw(struct iio_dev *indio_dev,
|
|||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int bits;
|
||||
s16 val16;
|
||||
u8 addr;
|
||||
|
@ -295,8 +55,8 @@ static int adis16209_write_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
val16 = val & ((1 << bits) - 1);
|
||||
addr = adis16209_addresses[chan->address][1];
|
||||
return adis16209_spi_write_reg_16(indio_dev, addr, val16);
|
||||
addr = adis16209_addresses[chan->scan_index][0];
|
||||
return adis_write_reg_16(st, addr, val16);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -306,6 +66,7 @@ static int adis16209_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int bits;
|
||||
u8 addr;
|
||||
|
@ -313,29 +74,8 @@ static int adis16209_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16209_addresses[chan->address][0];
|
||||
ret = adis16209_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val16 & ADIS16209_ERROR_ACTIVE) {
|
||||
ret = adis16209_check_status(indio_dev);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
val16 = val16 & ((1 << chan->scan_type.realbits) - 1);
|
||||
if (chan->scan_type.sign == 's')
|
||||
val16 = (s16)(val16 <<
|
||||
(16 - chan->scan_type.realbits)) >>
|
||||
(16 - chan->scan_type.realbits);
|
||||
*val = val16;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
return adis_single_conversion(indio_dev, chan,
|
||||
ADIS16209_ERROR_ACTIVE, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
|
@ -374,8 +114,8 @@ static int adis16209_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16209_addresses[chan->address][1];
|
||||
ret = adis16209_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
addr = adis16209_addresses[chan->scan_index][0];
|
||||
ret = adis_read_reg_16(st, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
|
@ -390,128 +130,56 @@ static int adis16209_read_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
static const struct iio_chan_spec adis16209_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.extend_name = "supply",
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = in_supply,
|
||||
.scan_index = ADIS16209_SCAN_SUPPLY,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 0,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.address = temp,
|
||||
.scan_index = ADIS16209_SCAN_TEMP,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
|
||||
.address = accel_x,
|
||||
.scan_index = ADIS16209_SCAN_ACC_X,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
|
||||
.address = accel_y,
|
||||
.scan_index = ADIS16209_SCAN_ACC_Y,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = in_aux,
|
||||
.scan_index = ADIS16209_SCAN_AUX_ADC,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_INCLI,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT,
|
||||
.address = incli_x,
|
||||
.scan_index = ADIS16209_SCAN_INCLI_X,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_INCLI,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT,
|
||||
.address = incli_y,
|
||||
.scan_index = ADIS16209_SCAN_INCLI_Y,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ROT,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT,
|
||||
.address = rot,
|
||||
.scan_index = ADIS16209_SCAN_ROT,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
},
|
||||
ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT, ADIS16209_SCAN_SUPPLY, 14),
|
||||
ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT, ADIS16209_SCAN_TEMP, 12),
|
||||
ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT, ADIS16209_SCAN_ACC_X,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14),
|
||||
ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT, ADIS16209_SCAN_ACC_Y,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14),
|
||||
ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC, ADIS16209_SCAN_AUX_ADC, 12),
|
||||
ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT, ADIS16209_SCAN_INCLI_X, 0, 14),
|
||||
ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT, ADIS16209_SCAN_INCLI_Y, 0, 14),
|
||||
ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT, ADIS16209_SCAN_ROT, 0, 14),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8)
|
||||
};
|
||||
|
||||
static const struct iio_info adis16209_info = {
|
||||
.read_raw = &adis16209_read_raw,
|
||||
.write_raw = &adis16209_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const char * const adis16209_status_error_msgs[] = {
|
||||
[ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
|
||||
[ADIS16209_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
|
||||
[ADIS16209_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
|
||||
[ADIS16209_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
|
||||
[ADIS16209_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16209_data = {
|
||||
.read_delay = 30,
|
||||
.msc_ctrl_reg = ADIS16209_MSC_CTRL,
|
||||
.glob_cmd_reg = ADIS16209_GLOB_CMD,
|
||||
.diag_stat_reg = ADIS16209_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN,
|
||||
.startup_delay = ADIS16209_STARTUP_DELAY,
|
||||
|
||||
.status_error_msgs = adis16209_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT) |
|
||||
BIT(ADIS16209_DIAG_STAT_SPI_FAIL_BIT) |
|
||||
BIT(ADIS16209_DIAG_STAT_FLASH_UPT_BIT) |
|
||||
BIT(ADIS16209_DIAG_STAT_POWER_HIGH_BIT) |
|
||||
BIT(ADIS16209_DIAG_STAT_POWER_LOW_BIT),
|
||||
};
|
||||
|
||||
|
||||
static int __devinit adis16209_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct adis16209_state *st;
|
||||
struct adis *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
/* setup the industrialio driver allocated elements */
|
||||
|
@ -523,8 +191,6 @@ static int __devinit adis16209_probe(struct spi_device *spi)
|
|||
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);
|
||||
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
|
@ -533,40 +199,25 @@ static int __devinit adis16209_probe(struct spi_device *spi)
|
|||
indio_dev->num_channels = ARRAY_SIZE(adis16209_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis16209_configure_ring(indio_dev);
|
||||
ret = adis_init(st, indio_dev, spi, &adis16209_data);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = iio_buffer_register(indio_dev,
|
||||
adis16209_channels,
|
||||
ARRAY_SIZE(adis16209_channels));
|
||||
if (ret) {
|
||||
printk(KERN_ERR "failed to initialize the ring\n");
|
||||
goto error_unreg_ring_funcs;
|
||||
}
|
||||
|
||||
if (spi->irq) {
|
||||
ret = adis16209_probe_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_uninitialize_ring;
|
||||
}
|
||||
|
||||
/* Get the device into a sane initial state */
|
||||
ret = adis16209_initial_setup(indio_dev);
|
||||
ret = adis_initial_startup(st);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
adis16209_remove_trigger(indio_dev);
|
||||
error_uninitialize_ring:
|
||||
iio_buffer_unregister(indio_dev);
|
||||
error_unreg_ring_funcs:
|
||||
adis16209_unconfigure_ring(indio_dev);
|
||||
error_cleanup_buffer_trigger:
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
error_ret:
|
||||
|
@ -576,11 +227,10 @@ static int __devinit adis16209_probe(struct spi_device *spi)
|
|||
static int __devexit adis16209_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16209_remove_trigger(indio_dev);
|
||||
iio_buffer_unregister(indio_dev);
|
||||
adis16209_unconfigure_ring(indio_dev);
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include "../ring_sw.h"
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include "adis16209.h"
|
||||
|
||||
/**
|
||||
* adis16209_read_ring_data() read data registers which will be placed into ring
|
||||
* @indio_dev: the IIO device
|
||||
* @rx: somewhere to pass back the value read
|
||||
**/
|
||||
static int adis16209_read_ring_data(struct iio_dev *indio_dev, u8 *rx)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16209_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[ADIS16209_OUTPUTS + 1];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
memset(xfers, 0, sizeof(xfers));
|
||||
for (i = 0; i <= ADIS16209_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;
|
||||
st->tx[2 * i]
|
||||
= ADIS16209_READ_REG(ADIS16209_SUPPLY_OUT + 2 * i);
|
||||
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;
|
||||
}
|
||||
|
||||
/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
|
||||
* specific to be rolled into the core.
|
||||
*/
|
||||
static irqreturn_t adis16209_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adis16209_state *st = iio_priv(indio_dev);
|
||||
int i = 0;
|
||||
s16 *data;
|
||||
|
||||
data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
dev_err(&st->us->dev, "memory alloc failed in ring bh");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength) &&
|
||||
adis16209_read_ring_data(indio_dev, st->rx) >= 0)
|
||||
for (; i < bitmap_weight(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength); i++)
|
||||
data[i] = be16_to_cpup((__be16 *)&(st->rx[i*2]));
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp)
|
||||
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)data);
|
||||
|
||||
kfree(data);
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void adis16209_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_dealloc_pollfunc(indio_dev->pollfunc);
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops adis16209_ring_setup_ops = {
|
||||
.preenable = &iio_sw_buffer_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
};
|
||||
|
||||
int adis16209_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iio_buffer *ring;
|
||||
|
||||
ring = iio_sw_rb_allocate(indio_dev);
|
||||
if (!ring) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
ring->scan_timestamp = true;
|
||||
indio_dev->setup_ops = &adis16209_ring_setup_ops;
|
||||
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
&adis16209_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
indio_dev,
|
||||
"%s_consumer%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (indio_dev->pollfunc == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_iio_sw_rb_free;
|
||||
}
|
||||
|
||||
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
|
||||
return 0;
|
||||
|
||||
error_iio_sw_rb_free:
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include "adis16209.h"
|
||||
|
||||
/**
|
||||
* adis16209_data_rdy_trigger_set_state() set datardy interrupt state
|
||||
**/
|
||||
static int adis16209_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
|
||||
return adis16209_set_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops adis16209_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &adis16209_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int adis16209_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct adis16209_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->trig = iio_trigger_alloc("adis16209-dev%d", indio_dev->id);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = request_irq(st->us->irq,
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"adis16209",
|
||||
st->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
st->trig->dev.parent = &st->us->dev;
|
||||
st->trig->ops = &adis16209_trigger_ops;
|
||||
st->trig->private_data = indio_dev;
|
||||
ret = iio_trigger_register(st->trig);
|
||||
|
||||
/* select default trigger */
|
||||
indio_dev->trig = st->trig;
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(st->us->irq, st->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(st->trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void adis16209_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16209_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(st->trig);
|
||||
free_irq(st->us->irq, st->trig);
|
||||
iio_trigger_free(st->trig);
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
#ifndef SPI_ADIS16220_H_
|
||||
#define SPI_ADIS16220_H_
|
||||
|
||||
#define ADIS16220_STARTUP_DELAY 220 /* ms */
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#define ADIS16220_READ_REG(a) a
|
||||
#define ADIS16220_WRITE_REG(a) ((a) | 0x80)
|
||||
#define ADIS16220_STARTUP_DELAY 220 /* ms */
|
||||
|
||||
/* Flash memory write count */
|
||||
#define ADIS16220_FLASH_CNT 0x00
|
||||
|
@ -102,15 +101,15 @@
|
|||
#define ADIS16220_DIAG_STAT_FLASH_CHK (1<<6)
|
||||
#define ADIS16220_DIAG_STAT_SELF_TEST (1<<5)
|
||||
/* Capture period violation/interruption */
|
||||
#define ADIS16220_DIAG_STAT_VIOLATION (1<<4)
|
||||
#define ADIS16220_DIAG_STAT_VIOLATION_BIT 4
|
||||
/* SPI communications failure */
|
||||
#define ADIS16220_DIAG_STAT_SPI_FAIL (1<<3)
|
||||
#define ADIS16220_DIAG_STAT_SPI_FAIL_BIT 3
|
||||
/* Flash update failure */
|
||||
#define ADIS16220_DIAG_STAT_FLASH_UPT (1<<2)
|
||||
#define ADIS16220_DIAG_STAT_FLASH_UPT_BIT 2
|
||||
/* Power supply above 3.625 V */
|
||||
#define ADIS16220_DIAG_STAT_POWER_HIGH (1<<1)
|
||||
#define ADIS16220_DIAG_STAT_POWER_HIGH_BIT 1
|
||||
/* Power supply below 3.15 V */
|
||||
#define ADIS16220_DIAG_STAT_POWER_LOW (1<<0)
|
||||
#define ADIS16220_DIAG_STAT_POWER_LOW_BIT 0
|
||||
|
||||
/* GLOB_CMD */
|
||||
#define ADIS16220_GLOB_CMD_SW_RESET (1<<7)
|
||||
|
@ -125,13 +124,14 @@
|
|||
|
||||
/**
|
||||
* struct adis16220_state - device instance specific data
|
||||
* @us: actual spi_device
|
||||
* @adis: adis device
|
||||
* @tx: transmit buffer
|
||||
* @rx: receive buffer
|
||||
* @buf_lock: mutex to protect tx and rx
|
||||
**/
|
||||
struct adis16220_state {
|
||||
struct spi_device *us;
|
||||
struct adis adis;
|
||||
|
||||
struct mutex buf_lock;
|
||||
u8 tx[ADIS16220_MAX_TX] ____cacheline_aligned;
|
||||
u8 rx[ADIS16220_MAX_RX];
|
||||
|
|
|
@ -20,136 +20,19 @@
|
|||
|
||||
#include "adis16220.h"
|
||||
|
||||
/**
|
||||
* adis16220_spi_write_reg_8() - write single byte to a register
|
||||
* @indio_dev: iio device associated with child of actual device
|
||||
* @reg_address: the address of the register to be written
|
||||
* @val: the value to write
|
||||
**/
|
||||
static int adis16220_spi_write_reg_8(struct iio_dev *indio_dev,
|
||||
u8 reg_address,
|
||||
u8 val)
|
||||
{
|
||||
int ret;
|
||||
struct adis16220_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16220_WRITE_REG(reg_address);
|
||||
st->tx[1] = val;
|
||||
|
||||
ret = spi_write(st->us, st->tx, 2);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16220_spi_write_reg_16() - write 2 bytes to a pair of registers
|
||||
* @indio_dev: iio device associated with child of actual device
|
||||
* @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 adis16220_spi_write_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 value)
|
||||
{
|
||||
int ret;
|
||||
struct spi_message msg;
|
||||
struct adis16220_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = st->tx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = 35,
|
||||
}, {
|
||||
.tx_buf = st->tx + 2,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = 35,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16220_WRITE_REG(lower_reg_address);
|
||||
st->tx[1] = value & 0xFF;
|
||||
st->tx[2] = ADIS16220_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16220_spi_read_reg_16() - read 2 bytes from a 16-bit register
|
||||
* @indio_dev: iio device associated with child of actual 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 adis16220_spi_read_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 *val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16220_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,
|
||||
.delay_usecs = 35,
|
||||
}, {
|
||||
.rx_buf = st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = 35,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16220_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 adis16220_read_16bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis16220_state *st = iio_priv(indio_dev);
|
||||
ssize_t ret;
|
||||
s16 val = 0;
|
||||
|
||||
/* Take the iio_dev status lock */
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis16220_spi_read_reg_16(indio_dev, this_attr->address,
|
||||
ret = adis_read_reg_16(&st->adis, this_attr->address,
|
||||
(u16 *)&val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret)
|
||||
|
@ -164,13 +47,14 @@ static ssize_t adis16220_write_16bit(struct device *dev,
|
|||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
struct adis16220_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u16 val;
|
||||
|
||||
ret = kstrtou16(buf, 10, &val);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
ret = adis16220_spi_write_reg_16(indio_dev, this_attr->address, val);
|
||||
ret = adis_write_reg_16(&st->adis, this_attr->address, val);
|
||||
|
||||
error_ret:
|
||||
return ret ? ret : len;
|
||||
|
@ -178,10 +62,11 @@ static ssize_t adis16220_write_16bit(struct device *dev,
|
|||
|
||||
static int adis16220_capture(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16220_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
ret = adis16220_spi_write_reg_16(indio_dev,
|
||||
ADIS16220_GLOB_CMD,
|
||||
0xBF08); /* initiates a manual data capture */
|
||||
|
||||
/* initiates a manual data capture */
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16220_GLOB_CMD, 0xBF08);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev, "problem beginning capture");
|
||||
|
||||
|
@ -190,18 +75,6 @@ static int adis16220_capture(struct iio_dev *indio_dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int adis16220_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16220_spi_write_reg_8(indio_dev,
|
||||
ADIS16220_GLOB_CMD,
|
||||
ADIS16220_GLOB_CMD_SW_RESET);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev, "problem resetting device");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t adis16220_write_capture(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
|
@ -222,81 +95,6 @@ static ssize_t adis16220_write_capture(struct device *dev,
|
|||
return len;
|
||||
}
|
||||
|
||||
static int adis16220_check_status(struct iio_dev *indio_dev)
|
||||
{
|
||||
u16 status;
|
||||
int ret;
|
||||
|
||||
ret = adis16220_spi_read_reg_16(indio_dev, ADIS16220_DIAG_STAT,
|
||||
&status);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Reading status failed\n");
|
||||
goto error_ret;
|
||||
}
|
||||
ret = status & 0x7F;
|
||||
|
||||
if (status & ADIS16220_DIAG_STAT_VIOLATION)
|
||||
dev_err(&indio_dev->dev,
|
||||
"Capture period violation/interruption\n");
|
||||
if (status & ADIS16220_DIAG_STAT_SPI_FAIL)
|
||||
dev_err(&indio_dev->dev, "SPI failure\n");
|
||||
if (status & ADIS16220_DIAG_STAT_FLASH_UPT)
|
||||
dev_err(&indio_dev->dev, "Flash update failed\n");
|
||||
if (status & ADIS16220_DIAG_STAT_POWER_HIGH)
|
||||
dev_err(&indio_dev->dev, "Power supply above 3.625V\n");
|
||||
if (status & ADIS16220_DIAG_STAT_POWER_LOW)
|
||||
dev_err(&indio_dev->dev, "Power supply below 3.15V\n");
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16220_self_test(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16220_spi_write_reg_16(indio_dev,
|
||||
ADIS16220_MSC_CTRL,
|
||||
ADIS16220_MSC_CTRL_SELF_TEST_EN);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "problem starting self test");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
adis16220_check_status(indio_dev);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16220_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Do self test */
|
||||
ret = adis16220_self_test(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "self test failure");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Read status register to check the result */
|
||||
ret = adis16220_check_status(indio_dev);
|
||||
if (ret) {
|
||||
adis16220_reset(indio_dev);
|
||||
dev_err(&indio_dev->dev, "device not playing ball -> reset");
|
||||
msleep(ADIS16220_STARTUP_DELAY);
|
||||
ret = adis16220_check_status(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "giving up");
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t adis16220_capture_buffer_read(struct iio_dev *indio_dev,
|
||||
char *buf,
|
||||
loff_t off,
|
||||
|
@ -333,7 +131,7 @@ static ssize_t adis16220_capture_buffer_read(struct iio_dev *indio_dev,
|
|||
count = ADIS16220_CAPTURE_SIZE - off;
|
||||
|
||||
/* write the begin position of capture buffer */
|
||||
ret = adis16220_spi_write_reg_16(indio_dev,
|
||||
ret = adis_write_reg_16(&st->adis,
|
||||
ADIS16220_CAPT_PNTR,
|
||||
off > 1);
|
||||
if (ret)
|
||||
|
@ -342,8 +140,9 @@ static ssize_t adis16220_capture_buffer_read(struct iio_dev *indio_dev,
|
|||
/* read count/2 values from capture buffer */
|
||||
mutex_lock(&st->buf_lock);
|
||||
|
||||
|
||||
for (i = 0; i < count; i += 2) {
|
||||
st->tx[i] = ADIS16220_READ_REG(addr);
|
||||
st->tx[i] = ADIS_READ_REG(addr);
|
||||
st->tx[i + 1] = 0;
|
||||
}
|
||||
xfers[1].len = count;
|
||||
|
@ -351,7 +150,7 @@ static ssize_t adis16220_capture_buffer_read(struct iio_dev *indio_dev,
|
|||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
ret = spi_sync(st->us, &msg);
|
||||
ret = spi_sync(st->adis.spi, &msg);
|
||||
if (ret) {
|
||||
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
@ -472,6 +271,8 @@ static int adis16220_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis16220_state *st = iio_priv(indio_dev);
|
||||
const struct adis16220_address_spec *addr;
|
||||
int ret = -EINVAL;
|
||||
int addrind = 0;
|
||||
u16 uval;
|
||||
|
@ -516,28 +317,21 @@ static int adis16220_read_raw(struct iio_dev *indio_dev,
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (adis16220_addresses[chan->address][addrind].sign) {
|
||||
ret = adis16220_spi_read_reg_16(indio_dev,
|
||||
adis16220_addresses[chan
|
||||
->address]
|
||||
[addrind].addr,
|
||||
&sval);
|
||||
addr = &adis16220_addresses[chan->address][addrind];
|
||||
if (addr->sign) {
|
||||
ret = adis_read_reg_16(&st->adis, addr->addr, &sval);
|
||||
if (ret)
|
||||
return ret;
|
||||
bits = adis16220_addresses[chan->address][addrind].bits;
|
||||
bits = addr->bits;
|
||||
sval &= (1 << bits) - 1;
|
||||
sval = (s16)(sval << (16 - bits)) >> (16 - bits);
|
||||
*val = sval;
|
||||
return IIO_VAL_INT;
|
||||
} else {
|
||||
ret = adis16220_spi_read_reg_16(indio_dev,
|
||||
adis16220_addresses[chan
|
||||
->address]
|
||||
[addrind].addr,
|
||||
&uval);
|
||||
ret = adis_read_reg_16(&st->adis, addr->addr, &uval);
|
||||
if (ret)
|
||||
return ret;
|
||||
bits = adis16220_addresses[chan->address][addrind].bits;
|
||||
bits = addr->bits;
|
||||
uval &= (1 << bits) - 1;
|
||||
*val = uval;
|
||||
return IIO_VAL_INT;
|
||||
|
@ -601,6 +395,32 @@ static const struct iio_info adis16220_info = {
|
|||
.read_raw = &adis16220_read_raw,
|
||||
};
|
||||
|
||||
static const char * const adis16220_status_error_msgs[] = {
|
||||
[ADIS16220_DIAG_STAT_VIOLATION_BIT] = "Capture period violation/interruption",
|
||||
[ADIS16220_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
|
||||
[ADIS16220_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
|
||||
[ADIS16220_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
|
||||
[ADIS16220_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16220_data = {
|
||||
.read_delay = 35,
|
||||
.write_delay = 35,
|
||||
.msc_ctrl_reg = ADIS16220_MSC_CTRL,
|
||||
.glob_cmd_reg = ADIS16220_GLOB_CMD,
|
||||
.diag_stat_reg = ADIS16220_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16220_MSC_CTRL_SELF_TEST_EN,
|
||||
.startup_delay = ADIS16220_STARTUP_DELAY,
|
||||
|
||||
.status_error_msgs = adis16220_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16220_DIAG_STAT_VIOLATION_BIT) |
|
||||
BIT(ADIS16220_DIAG_STAT_SPI_FAIL_BIT) |
|
||||
BIT(ADIS16220_DIAG_STAT_FLASH_UPT_BIT) |
|
||||
BIT(ADIS16220_DIAG_STAT_POWER_HIGH_BIT) |
|
||||
BIT(ADIS16220_DIAG_STAT_POWER_LOW_BIT),
|
||||
};
|
||||
|
||||
static int __devinit adis16220_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
|
@ -618,9 +438,6 @@ static int __devinit adis16220_probe(struct spi_device *spi)
|
|||
/* this is only used for removal purposes */
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->us = spi;
|
||||
mutex_init(&st->buf_lock);
|
||||
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &adis16220_info;
|
||||
|
@ -644,8 +461,11 @@ static int __devinit adis16220_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
goto error_rm_adc1_bin;
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, &adis16220_data);
|
||||
if (ret)
|
||||
goto error_rm_adc2_bin;
|
||||
/* Get the device into a sane initial state */
|
||||
ret = adis16220_initial_setup(indio_dev);
|
||||
ret = adis_initial_startup(&st->adis);
|
||||
if (ret)
|
||||
goto error_rm_adc2_bin;
|
||||
return 0;
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
#define ADIS16240_STARTUP_DELAY 220 /* ms */
|
||||
|
||||
#define ADIS16240_READ_REG(a) a
|
||||
#define ADIS16240_WRITE_REG(a) ((a) | 0x80)
|
||||
|
||||
/* Flash memory write count */
|
||||
#define ADIS16240_FLASH_CNT 0x00
|
||||
/* Output, power supply */
|
||||
|
@ -75,8 +72,6 @@
|
|||
/* System command */
|
||||
#define ADIS16240_GLOB_CMD 0x4A
|
||||
|
||||
#define ADIS16240_OUTPUTS 6
|
||||
|
||||
/* MSC_CTRL */
|
||||
/* Enables sum-of-squares output (XYZPEAK_OUT) */
|
||||
#define ADIS16240_MSC_CTRL_XYZPEAK_OUT_EN (1 << 15)
|
||||
|
@ -101,17 +96,17 @@
|
|||
/* Flash test, checksum flag: 1 = mismatch, 0 = match */
|
||||
#define ADIS16240_DIAG_STAT_CHKSUM (1<<6)
|
||||
/* Power-on, self-test flag: 1 = failure, 0 = pass */
|
||||
#define ADIS16240_DIAG_STAT_PWRON_FAIL (1<<5)
|
||||
#define ADIS16240_DIAG_STAT_PWRON_FAIL_BIT 5
|
||||
/* Power-on self-test: 1 = in-progress, 0 = complete */
|
||||
#define ADIS16240_DIAG_STAT_PWRON_BUSY (1<<4)
|
||||
/* SPI communications failure */
|
||||
#define ADIS16240_DIAG_STAT_SPI_FAIL (1<<3)
|
||||
#define ADIS16240_DIAG_STAT_SPI_FAIL_BIT 3
|
||||
/* Flash update failure */
|
||||
#define ADIS16240_DIAG_STAT_FLASH_UPT (1<<2)
|
||||
#define ADIS16240_DIAG_STAT_FLASH_UPT_BIT 2
|
||||
/* Power supply above 3.625 V */
|
||||
#define ADIS16240_DIAG_STAT_POWER_HIGH (1<<1)
|
||||
#define ADIS16240_DIAG_STAT_POWER_HIGH_BIT 1
|
||||
/* Power supply below 3.15 V */
|
||||
#define ADIS16240_DIAG_STAT_POWER_LOW (1<<0)
|
||||
#define ADIS16240_DIAG_STAT_POWER_LOW_BIT 0
|
||||
|
||||
/* GLOB_CMD */
|
||||
#define ADIS16240_GLOB_CMD_RESUME (1<<8)
|
||||
|
@ -120,77 +115,15 @@
|
|||
|
||||
#define ADIS16240_ERROR_ACTIVE (1<<14)
|
||||
|
||||
#define ADIS16240_MAX_TX 24
|
||||
#define ADIS16240_MAX_RX 24
|
||||
|
||||
/**
|
||||
* struct adis16240_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 adis16240_state {
|
||||
struct spi_device *us;
|
||||
struct iio_trigger *trig;
|
||||
struct mutex buf_lock;
|
||||
u8 tx[ADIS16240_MAX_TX] ____cacheline_aligned;
|
||||
u8 rx[ADIS16240_MAX_RX];
|
||||
};
|
||||
|
||||
int adis16240_set_irq(struct iio_dev *indio_dev, bool enable);
|
||||
|
||||
/* At the moment triggers are only used for ring buffer
|
||||
* filling. This may change!
|
||||
*/
|
||||
|
||||
#define ADIS16240_SCAN_SUPPLY 0
|
||||
#define ADIS16240_SCAN_ACC_X 1
|
||||
#define ADIS16240_SCAN_ACC_Y 2
|
||||
#define ADIS16240_SCAN_ACC_Z 3
|
||||
#define ADIS16240_SCAN_ACC_X 0
|
||||
#define ADIS16240_SCAN_ACC_Y 1
|
||||
#define ADIS16240_SCAN_ACC_Z 2
|
||||
#define ADIS16240_SCAN_SUPPLY 3
|
||||
#define ADIS16240_SCAN_AUX_ADC 4
|
||||
#define ADIS16240_SCAN_TEMP 5
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
void adis16240_remove_trigger(struct iio_dev *indio_dev);
|
||||
int adis16240_probe_trigger(struct iio_dev *indio_dev);
|
||||
|
||||
ssize_t adis16240_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
|
||||
int adis16240_configure_ring(struct iio_dev *indio_dev);
|
||||
void adis16240_unconfigure_ring(struct iio_dev *indio_dev);
|
||||
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline void adis16240_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adis16240_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
adis16240_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adis16240_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void adis16240_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
#endif /* SPI_ADIS16240_H_ */
|
||||
|
|
|
@ -22,149 +22,29 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#include "adis16240.h"
|
||||
|
||||
static int adis16240_check_status(struct iio_dev *indio_dev);
|
||||
|
||||
/**
|
||||
* adis16240_spi_write_reg_8() - write single byte to a register
|
||||
* @indio_dev: iio_dev associated with device
|
||||
* @reg_address: the address of the register to be written
|
||||
* @val: the value to write
|
||||
**/
|
||||
static int adis16240_spi_write_reg_8(struct iio_dev *indio_dev,
|
||||
u8 reg_address,
|
||||
u8 val)
|
||||
{
|
||||
int ret;
|
||||
struct adis16240_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16240_WRITE_REG(reg_address);
|
||||
st->tx[1] = val;
|
||||
|
||||
ret = spi_write(st->us, st->tx, 2);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16240_spi_write_reg_16() - write 2 bytes to a pair of registers
|
||||
* @indio_dev: iio_dev for this device
|
||||
* @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 adis16240_spi_write_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 value)
|
||||
{
|
||||
int ret;
|
||||
struct spi_message msg;
|
||||
struct adis16240_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = st->tx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = 35,
|
||||
}, {
|
||||
.tx_buf = st->tx + 2,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = 35,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16240_WRITE_REG(lower_reg_address);
|
||||
st->tx[1] = value & 0xFF;
|
||||
st->tx[2] = ADIS16240_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis16240_spi_read_reg_16() - read 2 bytes from a 16-bit register
|
||||
* @indio_dev: iio_dev for this 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 adis16240_spi_read_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 *val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16240_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,
|
||||
.delay_usecs = 35,
|
||||
}, {
|
||||
.rx_buf = st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = 35,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16240_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 adis16240_spi_read_signed(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf,
|
||||
unsigned bits)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
s16 val = 0;
|
||||
unsigned shift = 16 - bits;
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
|
||||
ret = adis16240_spi_read_reg_16(indio_dev,
|
||||
ret = adis_read_reg_16(st,
|
||||
this_attr->address, (u16 *)&val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val & ADIS16240_ERROR_ACTIVE)
|
||||
adis16240_check_status(indio_dev);
|
||||
adis_check_status(st);
|
||||
|
||||
val = ((s16)(val << shift) >> shift);
|
||||
return sprintf(buf, "%d\n", val);
|
||||
|
@ -185,152 +65,16 @@ static ssize_t adis16240_read_12bit_signed(struct device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int adis16240_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16240_spi_write_reg_8(indio_dev,
|
||||
ADIS16240_GLOB_CMD,
|
||||
ADIS16240_GLOB_CMD_SW_RESET);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev, "problem resetting device");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adis16240_set_irq(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
u16 msc;
|
||||
|
||||
ret = adis16240_spi_read_reg_16(indio_dev,
|
||||
ADIS16240_MSC_CTRL, &msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
msc |= ADIS16240_MSC_CTRL_ACTIVE_HIGH;
|
||||
msc &= ~ADIS16240_MSC_CTRL_DATA_RDY_DIO2;
|
||||
if (enable)
|
||||
msc |= ADIS16240_MSC_CTRL_DATA_RDY_EN;
|
||||
else
|
||||
msc &= ~ADIS16240_MSC_CTRL_DATA_RDY_EN;
|
||||
|
||||
ret = adis16240_spi_write_reg_16(indio_dev,
|
||||
ADIS16240_MSC_CTRL, msc);
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16240_self_test(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16240_spi_write_reg_16(indio_dev,
|
||||
ADIS16240_MSC_CTRL,
|
||||
ADIS16240_MSC_CTRL_SELF_TEST_EN);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "problem starting self test");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
msleep(ADIS16240_STARTUP_DELAY);
|
||||
|
||||
adis16240_check_status(indio_dev);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16240_check_status(struct iio_dev *indio_dev)
|
||||
{
|
||||
u16 status;
|
||||
int ret;
|
||||
struct device *dev = &indio_dev->dev;
|
||||
|
||||
ret = adis16240_spi_read_reg_16(indio_dev,
|
||||
ADIS16240_DIAG_STAT, &status);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Reading status failed\n");
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = status & 0x2F;
|
||||
if (status & ADIS16240_DIAG_STAT_PWRON_FAIL)
|
||||
dev_err(dev, "Power-on, self-test fail\n");
|
||||
if (status & ADIS16240_DIAG_STAT_SPI_FAIL)
|
||||
dev_err(dev, "SPI failure\n");
|
||||
if (status & ADIS16240_DIAG_STAT_FLASH_UPT)
|
||||
dev_err(dev, "Flash update failed\n");
|
||||
if (status & ADIS16240_DIAG_STAT_POWER_HIGH)
|
||||
dev_err(dev, "Power supply above 3.625V\n");
|
||||
if (status & ADIS16240_DIAG_STAT_POWER_LOW)
|
||||
dev_err(dev, "Power supply below 2.225V\n");
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16240_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &indio_dev->dev;
|
||||
|
||||
/* Disable IRQ */
|
||||
ret = adis16240_set_irq(indio_dev, false);
|
||||
if (ret) {
|
||||
dev_err(dev, "disable irq failed");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Do self test */
|
||||
ret = adis16240_self_test(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "self test failure");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Read status register to check the result */
|
||||
ret = adis16240_check_status(indio_dev);
|
||||
if (ret) {
|
||||
adis16240_reset(indio_dev);
|
||||
dev_err(dev, "device not playing ball -> reset");
|
||||
msleep(ADIS16240_STARTUP_DELAY);
|
||||
ret = adis16240_check_status(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "giving up");
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(in_accel_xyz_squared_peak_raw, S_IRUGO,
|
||||
adis16240_read_12bit_signed, NULL,
|
||||
ADIS16240_XYZPEAK_OUT);
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("4096");
|
||||
|
||||
enum adis16240_chan {
|
||||
in_supply,
|
||||
in_aux,
|
||||
accel_x,
|
||||
accel_y,
|
||||
accel_z,
|
||||
temp,
|
||||
};
|
||||
|
||||
static const u8 adis16240_addresses[6][3] = {
|
||||
[in_supply] = { ADIS16240_SUPPLY_OUT },
|
||||
[in_aux] = { ADIS16240_AUX_ADC },
|
||||
[accel_x] = { ADIS16240_XACCL_OUT, ADIS16240_XACCL_OFF,
|
||||
ADIS16240_XPEAK_OUT },
|
||||
[accel_y] = { ADIS16240_YACCL_OUT, ADIS16240_YACCL_OFF,
|
||||
ADIS16240_YPEAK_OUT },
|
||||
[accel_z] = { ADIS16240_ZACCL_OUT, ADIS16240_ZACCL_OFF,
|
||||
ADIS16240_ZPEAK_OUT },
|
||||
[temp] = { ADIS16240_TEMP_OUT },
|
||||
static const u8 adis16240_addresses[][2] = {
|
||||
[ADIS16240_SCAN_ACC_X] = { ADIS16240_XACCL_OFF, ADIS16240_XPEAK_OUT },
|
||||
[ADIS16240_SCAN_ACC_Y] = { ADIS16240_YACCL_OFF, ADIS16240_YPEAK_OUT },
|
||||
[ADIS16240_SCAN_ACC_Z] = { ADIS16240_ZACCL_OFF, ADIS16240_ZPEAK_OUT },
|
||||
};
|
||||
|
||||
static int adis16240_read_raw(struct iio_dev *indio_dev,
|
||||
|
@ -338,6 +82,7 @@ static int adis16240_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int bits;
|
||||
u8 addr;
|
||||
|
@ -345,29 +90,8 @@ static int adis16240_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16240_addresses[chan->address][0];
|
||||
ret = adis16240_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val16 & ADIS16240_ERROR_ACTIVE) {
|
||||
ret = adis16240_check_status(indio_dev);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
val16 = val16 & ((1 << chan->scan_type.realbits) - 1);
|
||||
if (chan->scan_type.sign == 's')
|
||||
val16 = (s16)(val16 <<
|
||||
(16 - chan->scan_type.realbits)) >>
|
||||
(16 - chan->scan_type.realbits);
|
||||
*val = val16;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
return adis_single_conversion(indio_dev, chan,
|
||||
ADIS16240_ERROR_ACTIVE, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
|
@ -400,8 +124,8 @@ static int adis16240_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
bits = 10;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16240_addresses[chan->address][1];
|
||||
ret = adis16240_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
addr = adis16240_addresses[chan->scan_index][0];
|
||||
ret = adis_read_reg_16(st, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
|
@ -414,8 +138,8 @@ static int adis16240_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_PEAK:
|
||||
bits = 10;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16240_addresses[chan->address][2];
|
||||
ret = adis16240_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
addr = adis16240_addresses[chan->scan_index][1];
|
||||
ret = adis_read_reg_16(st, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
|
@ -435,104 +159,32 @@ static int adis16240_write_raw(struct iio_dev *indio_dev,
|
|||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int bits = 10;
|
||||
s16 val16;
|
||||
u8 addr;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
val16 = val & ((1 << bits) - 1);
|
||||
addr = adis16240_addresses[chan->address][1];
|
||||
return adis16240_spi_write_reg_16(indio_dev, addr, val16);
|
||||
addr = adis16240_addresses[chan->scan_index][0];
|
||||
return adis_write_reg_16(st, addr, val16);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16240_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.extend_name = "supply",
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = in_supply,
|
||||
.scan_index = ADIS16240_SCAN_SUPPLY,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 10,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.address = in_aux,
|
||||
.scan_index = ADIS16240_SCAN_AUX_ADC,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 10,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
ADIS_SUPPLY_CHAN(ADIS16240_SUPPLY_OUT, ADIS16240_SCAN_SUPPLY, 10),
|
||||
ADIS_AUX_ADC_CHAN(ADIS16240_AUX_ADC, ADIS16240_SCAN_AUX_ADC, 10),
|
||||
ADIS_ACCEL_CHAN(X, ADIS16240_XACCL_OUT, ADIS16240_SCAN_ACC_X,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_PEAK_SEPARATE_BIT,
|
||||
.address = accel_x,
|
||||
.scan_index = ADIS16240_SCAN_ACC_X,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 10,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 10),
|
||||
ADIS_ACCEL_CHAN(Y, ADIS16240_YACCL_OUT, ADIS16240_SCAN_ACC_Y,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_PEAK_SEPARATE_BIT,
|
||||
.address = accel_y,
|
||||
.scan_index = ADIS16240_SCAN_ACC_Y,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 10,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 10),
|
||||
ADIS_ACCEL_CHAN(Z, ADIS16240_ZACCL_OUT, ADIS16240_SCAN_ACC_Z,
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_PEAK_SEPARATE_BIT,
|
||||
.address = accel_z,
|
||||
.scan_index = ADIS16240_SCAN_ACC_Z,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 10,
|
||||
.storagebits = 16,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = temp,
|
||||
.scan_index = ADIS16240_SCAN_TEMP,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 10,
|
||||
.storagebits = 16,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 10),
|
||||
ADIS_TEMP_CHAN(ADIS16240_TEMP_OUT, ADIS16240_SCAN_TEMP, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(6)
|
||||
};
|
||||
|
||||
|
@ -550,13 +202,40 @@ static const struct iio_info adis16240_info = {
|
|||
.attrs = &adis16240_attribute_group,
|
||||
.read_raw = &adis16240_read_raw,
|
||||
.write_raw = &adis16240_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const char * const adis16240_status_error_msgs[] = {
|
||||
[ADIS16240_DIAG_STAT_PWRON_FAIL_BIT] = "Power on, self-test failed",
|
||||
[ADIS16240_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
|
||||
[ADIS16240_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
|
||||
[ADIS16240_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
|
||||
[ADIS16240_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.225V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16240_data = {
|
||||
.write_delay = 35,
|
||||
.read_delay = 35,
|
||||
.msc_ctrl_reg = ADIS16240_MSC_CTRL,
|
||||
.glob_cmd_reg = ADIS16240_GLOB_CMD,
|
||||
.diag_stat_reg = ADIS16240_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16240_MSC_CTRL_SELF_TEST_EN,
|
||||
.startup_delay = ADIS16240_STARTUP_DELAY,
|
||||
|
||||
.status_error_msgs = adis16240_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16240_DIAG_STAT_PWRON_FAIL_BIT) |
|
||||
BIT(ADIS16240_DIAG_STAT_SPI_FAIL_BIT) |
|
||||
BIT(ADIS16240_DIAG_STAT_FLASH_UPT_BIT) |
|
||||
BIT(ADIS16240_DIAG_STAT_POWER_HIGH_BIT) |
|
||||
BIT(ADIS16240_DIAG_STAT_POWER_LOW_BIT),
|
||||
};
|
||||
|
||||
static int __devinit adis16240_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct adis16240_state *st;
|
||||
struct adis *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
/* setup the industrialio driver allocated elements */
|
||||
|
@ -569,9 +248,6 @@ static int __devinit adis16240_probe(struct spi_device *spi)
|
|||
/* this is only used for removal purposes */
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->us = spi;
|
||||
mutex_init(&st->buf_lock);
|
||||
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &adis16240_info;
|
||||
|
@ -579,39 +255,24 @@ static int __devinit adis16240_probe(struct spi_device *spi)
|
|||
indio_dev->num_channels = ARRAY_SIZE(adis16240_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis16240_configure_ring(indio_dev);
|
||||
ret = adis_init(st, indio_dev, spi, &adis16240_data);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = iio_buffer_register(indio_dev,
|
||||
adis16240_channels,
|
||||
ARRAY_SIZE(adis16240_channels));
|
||||
if (ret) {
|
||||
printk(KERN_ERR "failed to initialize the ring\n");
|
||||
goto error_unreg_ring_funcs;
|
||||
}
|
||||
|
||||
if (spi->irq) {
|
||||
ret = adis16240_probe_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_uninitialize_ring;
|
||||
}
|
||||
|
||||
/* Get the device into a sane initial state */
|
||||
ret = adis16240_initial_setup(indio_dev);
|
||||
ret = adis_initial_startup(st);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
adis16240_remove_trigger(indio_dev);
|
||||
error_uninitialize_ring:
|
||||
iio_buffer_unregister(indio_dev);
|
||||
error_unreg_ring_funcs:
|
||||
adis16240_unconfigure_ring(indio_dev);
|
||||
error_cleanup_buffer_trigger:
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
error_ret:
|
||||
|
@ -620,13 +281,11 @@ static int __devinit adis16240_probe(struct spi_device *spi)
|
|||
|
||||
static int __devexit adis16240_remove(struct spi_device *spi)
|
||||
{
|
||||
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16240_remove_trigger(indio_dev);
|
||||
iio_buffer_unregister(indio_dev);
|
||||
adis16240_unconfigure_ring(indio_dev);
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,132 +0,0 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include "../ring_sw.h"
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include "adis16240.h"
|
||||
|
||||
/**
|
||||
* adis16240_read_ring_data() read data registers which will be placed into ring
|
||||
* @indio_dev: the IIO device
|
||||
* @rx: somewhere to pass back the value read
|
||||
**/
|
||||
static int adis16240_read_ring_data(struct iio_dev *indio_dev, u8 *rx)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16240_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[ADIS16240_OUTPUTS + 1];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
memset(xfers, 0, sizeof(xfers));
|
||||
for (i = 0; i <= ADIS16240_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;
|
||||
st->tx[2 * i]
|
||||
= ADIS16240_READ_REG(ADIS16240_SUPPLY_OUT + 2 * i);
|
||||
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 irqreturn_t adis16240_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adis16240_state *st = iio_priv(indio_dev);
|
||||
|
||||
int i = 0;
|
||||
s16 *data;
|
||||
|
||||
data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
dev_err(&st->us->dev, "memory alloc failed in ring bh");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength) &&
|
||||
adis16240_read_ring_data(indio_dev, st->rx) >= 0)
|
||||
for (; i < bitmap_weight(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength); i++)
|
||||
data[i] = be16_to_cpup((__be16 *)&(st->rx[i*2]));
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp)
|
||||
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)data);
|
||||
|
||||
kfree(data);
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void adis16240_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_dealloc_pollfunc(indio_dev->pollfunc);
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops adis16240_ring_setup_ops = {
|
||||
.preenable = &iio_sw_buffer_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
};
|
||||
|
||||
int adis16240_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iio_buffer *ring;
|
||||
|
||||
ring = iio_sw_rb_allocate(indio_dev);
|
||||
if (!ring) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
ring->scan_timestamp = true;
|
||||
indio_dev->setup_ops = &adis16240_ring_setup_ops;
|
||||
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
&adis16240_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
indio_dev,
|
||||
"%s_consumer%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (indio_dev->pollfunc == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_iio_sw_rb_free;
|
||||
}
|
||||
|
||||
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
|
||||
return 0;
|
||||
|
||||
error_iio_sw_rb_free:
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include "adis16240.h"
|
||||
|
||||
/**
|
||||
* adis16240_data_rdy_trigger_set_state() set datardy interrupt state
|
||||
**/
|
||||
static int adis16240_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
|
||||
return adis16240_set_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops adis16240_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &adis16240_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int adis16240_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct adis16240_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->trig = iio_trigger_alloc("adis16240-dev%d", indio_dev->id);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = request_irq(st->us->irq,
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"adis16240",
|
||||
st->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
|
||||
st->trig->dev.parent = &st->us->dev;
|
||||
st->trig->ops = &adis16240_trigger_ops;
|
||||
st->trig->private_data = indio_dev;
|
||||
ret = iio_trigger_register(st->trig);
|
||||
|
||||
/* select default trigger */
|
||||
indio_dev->trig = st->trig;
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(st->us->irq, st->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(st->trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void adis16240_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16240_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(st->trig);
|
||||
free_irq(st->us->irq, st->trig);
|
||||
iio_trigger_free(st->trig);
|
||||
}
|
|
@ -10,17 +10,6 @@ config AD7291
|
|||
Say yes here to build support for Analog Devices AD7291
|
||||
8 Channel ADC with temperature sensor.
|
||||
|
||||
config AD7298
|
||||
tristate "Analog Devices AD7298 ADC driver"
|
||||
depends on SPI
|
||||
select IIO_TRIGGERED_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7298
|
||||
8 Channel ADC with temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7298.
|
||||
|
||||
config AD7606
|
||||
tristate "Analog Devices AD7606 ADC driver"
|
||||
depends on GPIOLIB
|
||||
|
|
|
@ -12,10 +12,6 @@ ad799x-y := ad799x_core.o
|
|||
ad799x-$(CONFIG_AD799X_RING_BUFFER) += ad799x_ring.o
|
||||
obj-$(CONFIG_AD799X) += ad799x.o
|
||||
|
||||
ad7298-y := ad7298_core.o
|
||||
ad7298-$(CONFIG_IIO_BUFFER) += ad7298_ring.o
|
||||
obj-$(CONFIG_AD7298) += ad7298.o
|
||||
|
||||
obj-$(CONFIG_AD7291) += ad7291.o
|
||||
obj-$(CONFIG_AD7780) += ad7780.o
|
||||
obj-$(CONFIG_AD7793) += ad7793.o
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* AD7298 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef IIO_ADC_AD7298_H_
|
||||
#define IIO_ADC_AD7298_H_
|
||||
|
||||
#define AD7298_WRITE (1 << 15) /* write to the control register */
|
||||
#define AD7298_REPEAT (1 << 14) /* repeated conversion enable */
|
||||
#define AD7298_CH(x) (1 << (13 - (x))) /* channel select */
|
||||
#define AD7298_TSENSE (1 << 5) /* temperature conversion enable */
|
||||
#define AD7298_EXTREF (1 << 2) /* external reference enable */
|
||||
#define AD7298_TAVG (1 << 1) /* temperature sensor averaging enable */
|
||||
#define AD7298_PDD (1 << 0) /* partial power down enable */
|
||||
|
||||
#define AD7298_MAX_CHAN 8
|
||||
#define AD7298_BITS 12
|
||||
#define AD7298_STORAGE_BITS 16
|
||||
#define AD7298_INTREF_mV 2500
|
||||
|
||||
#define AD7298_CH_TEMP 9
|
||||
|
||||
#define RES_MASK(bits) ((1 << (bits)) - 1)
|
||||
|
||||
/*
|
||||
* TODO: struct ad7298_platform_data needs to go into include/linux/iio
|
||||
*/
|
||||
|
||||
struct ad7298_platform_data {
|
||||
/* External Vref voltage applied */
|
||||
u16 vref_mv;
|
||||
};
|
||||
|
||||
struct ad7298_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
u16 int_vref_mv;
|
||||
unsigned ext_ref;
|
||||
struct spi_transfer ring_xfer[10];
|
||||
struct spi_transfer scan_single_xfer[3];
|
||||
struct spi_message ring_msg;
|
||||
struct spi_message scan_single_msg;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
unsigned short rx_buf[8] ____cacheline_aligned;
|
||||
unsigned short tx_buf[2];
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
int ad7298_register_ring_funcs_and_init(struct iio_dev *indio_dev);
|
||||
void ad7298_ring_cleanup(struct iio_dev *indio_dev);
|
||||
int ad7298_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *active_scan_mask);
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline int
|
||||
ad7298_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ad7298_ring_cleanup(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
#define ad7298_update_scan_mode NULL
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
#endif /* IIO_ADC_AD7298_H_ */
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* AD7298 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011-2012 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include "ad7298.h"
|
||||
|
||||
/**
|
||||
* ad7298_update_scan_mode() setup the spi transfer buffer for the new scan mask
|
||||
**/
|
||||
int ad7298_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *active_scan_mask)
|
||||
{
|
||||
struct ad7298_state *st = iio_priv(indio_dev);
|
||||
int i, m;
|
||||
unsigned short command;
|
||||
int scan_count;
|
||||
|
||||
/* Now compute overall size */
|
||||
scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength);
|
||||
|
||||
command = AD7298_WRITE | st->ext_ref;
|
||||
|
||||
for (i = 0, m = AD7298_CH(0); i < AD7298_MAX_CHAN; i++, m >>= 1)
|
||||
if (test_bit(i, active_scan_mask))
|
||||
command |= m;
|
||||
|
||||
st->tx_buf[0] = cpu_to_be16(command);
|
||||
|
||||
/* build spi ring message */
|
||||
st->ring_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->ring_xfer[0].len = 2;
|
||||
st->ring_xfer[0].cs_change = 1;
|
||||
st->ring_xfer[1].tx_buf = &st->tx_buf[1];
|
||||
st->ring_xfer[1].len = 2;
|
||||
st->ring_xfer[1].cs_change = 1;
|
||||
|
||||
spi_message_init(&st->ring_msg);
|
||||
spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
|
||||
spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg);
|
||||
|
||||
for (i = 0; i < scan_count; i++) {
|
||||
st->ring_xfer[i + 2].rx_buf = &st->rx_buf[i];
|
||||
st->ring_xfer[i + 2].len = 2;
|
||||
st->ring_xfer[i + 2].cs_change = 1;
|
||||
spi_message_add_tail(&st->ring_xfer[i + 2], &st->ring_msg);
|
||||
}
|
||||
/* make sure last transfer cs_change is not set */
|
||||
st->ring_xfer[i + 1].cs_change = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7298_trigger_handler() bh of trigger launched polling to ring buffer
|
||||
*
|
||||
* Currently there is no option in this driver to disable the saving of
|
||||
* timestamps within the ring.
|
||||
**/
|
||||
static irqreturn_t ad7298_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7298_state *st = iio_priv(indio_dev);
|
||||
s64 time_ns = 0;
|
||||
__u16 buf[16];
|
||||
int b_sent, i;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
if (indio_dev->scan_timestamp) {
|
||||
time_ns = iio_get_time_ns();
|
||||
memcpy((u8 *)buf + indio_dev->scan_bytes - sizeof(s64),
|
||||
&time_ns, sizeof(time_ns));
|
||||
}
|
||||
|
||||
for (i = 0; i < bitmap_weight(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength); i++)
|
||||
buf[i] = be16_to_cpu(st->rx_buf[i]);
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)buf);
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int ad7298_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
return iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad7298_trigger_handler, NULL);
|
||||
}
|
||||
|
||||
void ad7298_ring_cleanup(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
|
@ -181,7 +181,7 @@ static ssize_t adt7410_store_mode(struct device *dev,
|
|||
|
||||
chip->config = config;
|
||||
|
||||
return ret;
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
|
||||
|
|
|
@ -12,7 +12,6 @@ adis16130-y := adis16130_core.o
|
|||
obj-$(CONFIG_ADIS16130) += adis16130.o
|
||||
|
||||
adis16260-y := adis16260_core.o
|
||||
adis16260-$(CONFIG_IIO_BUFFER) += adis16260_ring.o adis16260_trigger.o
|
||||
obj-$(CONFIG_ADIS16260) += adis16260.o
|
||||
|
||||
adis16251-y := adis16251_core.o
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
#ifndef SPI_ADIS16260_H_
|
||||
#define SPI_ADIS16260_H_
|
||||
|
||||
#include "adis16260_platform_data.h"
|
||||
#include <linux/iio/imu/adis.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 */
|
||||
|
@ -34,8 +33,6 @@
|
|||
* 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)
|
||||
|
||||
|
@ -60,13 +57,13 @@
|
|||
/* 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)
|
||||
#define ADIS16260_DIAG_STAT_FLASH_CHK_BIT 6
|
||||
#define ADIS16260_DIAG_STAT_SELF_TEST_BIT 5
|
||||
#define ADIS16260_DIAG_STAT_OVERFLOW_BIT 4
|
||||
#define ADIS16260_DIAG_STAT_SPI_FAIL_BIT 3
|
||||
#define ADIS16260_DIAG_STAT_FLASH_UPT_BIT 2
|
||||
#define ADIS16260_DIAG_STAT_POWER_HIGH_BIT 1
|
||||
#define ADIS16260_DIAG_STAT_POWER_LOW_BIT 0
|
||||
|
||||
/* GLOB_CMD */
|
||||
#define ADIS16260_GLOB_CMD_SW_RESET (1<<7)
|
||||
|
@ -75,82 +72,27 @@
|
|||
#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
|
||||
* @trig: data ready trigger registered with iio
|
||||
* @buf_lock: mutex to protect tx and rx
|
||||
* @negate: negate the scale parameter
|
||||
* @tx: transmit buffer
|
||||
* @rx: receive buffer
|
||||
**/
|
||||
struct adis16260_state {
|
||||
struct spi_device *us;
|
||||
struct iio_trigger *trig;
|
||||
struct mutex buf_lock;
|
||||
unsigned negate:1;
|
||||
u8 tx[ADIS16260_MAX_TX] ____cacheline_aligned;
|
||||
u8 rx[ADIS16260_MAX_RX];
|
||||
unsigned negate:1;
|
||||
struct adis adis;
|
||||
};
|
||||
|
||||
int adis16260_set_irq(struct iio_dev *indio_dev, bool enable);
|
||||
|
||||
/* At the moment triggers are only used for ring buffer
|
||||
* filling. This may change!
|
||||
*/
|
||||
|
||||
#define ADIS16260_SCAN_SUPPLY 0
|
||||
#define ADIS16260_SCAN_GYRO 1
|
||||
#define ADIS16260_SCAN_GYRO 0
|
||||
#define ADIS16260_SCAN_SUPPLY 1
|
||||
#define ADIS16260_SCAN_AUX_ADC 2
|
||||
#define ADIS16260_SCAN_TEMP 3
|
||||
#define ADIS16260_SCAN_ANGL 4
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
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);
|
||||
|
||||
#else /* CONFIG_IIO_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)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
#endif /* SPI_ADIS16260_H_ */
|
||||
|
|
|
@ -24,132 +24,13 @@
|
|||
|
||||
#include "adis16260.h"
|
||||
|
||||
static int adis16260_check_status(struct iio_dev *indio_dev);
|
||||
|
||||
/**
|
||||
* adis16260_spi_write_reg_8() - write single byte to a register
|
||||
* @indio_dev: iio_dev for the device
|
||||
* @reg_address: the address of the register to be written
|
||||
* @val: the value to write
|
||||
**/
|
||||
static int adis16260_spi_write_reg_8(struct iio_dev *indio_dev,
|
||||
u8 reg_address,
|
||||
u8 val)
|
||||
{
|
||||
int ret;
|
||||
struct adis16260_state *st = iio_priv(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
|
||||
* @indio_dev: iio_dev for the device
|
||||
* @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 iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 value)
|
||||
{
|
||||
int ret;
|
||||
struct spi_message msg;
|
||||
struct adis16260_state *st = iio_priv(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,
|
||||
.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
|
||||
* @indio_dev: iio_dev for the 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 adis16260_spi_read_reg_16(struct iio_dev *indio_dev,
|
||||
u8 lower_reg_address,
|
||||
u16 *val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16260_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,
|
||||
.delay_usecs = 30,
|
||||
}, {
|
||||
.rx_buf = st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = 30,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16260_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 adis16260_read_frequency_available(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis16260_state *st = iio_priv(indio_dev);
|
||||
if (spi_get_device_id(st->us)->driver_data)
|
||||
if (spi_get_device_id(st->adis.spi)->driver_data)
|
||||
return sprintf(buf, "%s\n", "0.129 ~ 256");
|
||||
else
|
||||
return sprintf(buf, "%s\n", "256 2048");
|
||||
|
@ -164,13 +45,11 @@ static ssize_t adis16260_read_frequency(struct device *dev,
|
|||
int ret, len = 0;
|
||||
u16 t;
|
||||
int sps;
|
||||
ret = adis16260_spi_read_reg_16(indio_dev,
|
||||
ADIS16260_SMPL_PRD,
|
||||
&t);
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16260_SMPL_PRD, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (spi_get_device_id(st->us)->driver_data) /* If an adis16251 */
|
||||
if (spi_get_device_id(st->adis.spi)->driver_data) /* If an adis16251 */
|
||||
sps = (t & ADIS16260_SMPL_PRD_TIME_BASE) ? 8 : 256;
|
||||
else
|
||||
sps = (t & ADIS16260_SMPL_PRD_TIME_BASE) ? 66 : 2048;
|
||||
|
@ -197,7 +76,7 @@ static ssize_t adis16260_write_frequency(struct device *dev,
|
|||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (spi_get_device_id(st->us)) {
|
||||
if (spi_get_device_id(st->adis.spi)->driver_data) {
|
||||
t = (256 / val);
|
||||
if (t > 0)
|
||||
t--;
|
||||
|
@ -209,10 +88,10 @@ static ssize_t adis16260_write_frequency(struct device *dev,
|
|||
t &= ADIS16260_SMPL_PRD_DIV_MASK;
|
||||
}
|
||||
if ((t & ADIS16260_SMPL_PRD_DIV_MASK) >= 0x0A)
|
||||
st->us->max_speed_hz = ADIS16260_SPI_SLOW;
|
||||
st->adis.spi->max_speed_hz = ADIS16260_SPI_SLOW;
|
||||
else
|
||||
st->us->max_speed_hz = ADIS16260_SPI_FAST;
|
||||
ret = adis16260_spi_write_reg_8(indio_dev,
|
||||
st->adis.spi->max_speed_hz = ADIS16260_SPI_FAST;
|
||||
ret = adis_write_reg_8(&st->adis,
|
||||
ADIS16260_SMPL_PRD,
|
||||
t);
|
||||
|
||||
|
@ -221,140 +100,20 @@ static ssize_t adis16260_write_frequency(struct device *dev,
|
|||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static int adis16260_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16260_spi_write_reg_8(indio_dev,
|
||||
ADIS16260_GLOB_CMD,
|
||||
ADIS16260_GLOB_CMD_SW_RESET);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev, "problem resetting device");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adis16260_set_irq(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
int ret;
|
||||
u16 msc;
|
||||
ret = adis16260_spi_read_reg_16(indio_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(indio_dev, ADIS16260_MSC_CTRL, msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Power down the device */
|
||||
static int adis16260_stop_device(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16260_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u16 val = ADIS16260_SLP_CNT_POWER_OFF;
|
||||
|
||||
ret = adis16260_spi_write_reg_16(indio_dev, ADIS16260_SLP_CNT, val);
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16260_SLP_CNT, val);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev, "problem with turning device off: SLP_CNT");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16260_self_test(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
ret = adis16260_spi_write_reg_16(indio_dev,
|
||||
ADIS16260_MSC_CTRL,
|
||||
ADIS16260_MSC_CTRL_MEM_TEST);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "problem starting self test");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
adis16260_check_status(indio_dev);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16260_check_status(struct iio_dev *indio_dev)
|
||||
{
|
||||
u16 status;
|
||||
int ret;
|
||||
struct device *dev = &indio_dev->dev;
|
||||
|
||||
ret = adis16260_spi_read_reg_16(indio_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 iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &indio_dev->dev;
|
||||
|
||||
/* Disable IRQ */
|
||||
ret = adis16260_set_irq(indio_dev, false);
|
||||
if (ret) {
|
||||
dev_err(dev, "disable irq failed");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Do self test */
|
||||
ret = adis16260_self_test(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "self test failure");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
/* Read status register to check the result */
|
||||
ret = adis16260_check_status(indio_dev);
|
||||
if (ret) {
|
||||
adis16260_reset(indio_dev);
|
||||
dev_err(dev, "device not playing ball -> reset");
|
||||
msleep(ADIS16260_STARTUP_DELAY);
|
||||
ret = adis16260_check_status(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "giving up");
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
adis16260_read_frequency,
|
||||
adis16260_write_frequency);
|
||||
|
@ -362,100 +121,26 @@ static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
|||
static IIO_DEVICE_ATTR(sampling_frequency_available,
|
||||
S_IRUGO, adis16260_read_frequency_available, NULL, 0);
|
||||
|
||||
enum adis16260_channel {
|
||||
gyro,
|
||||
temp,
|
||||
in_supply,
|
||||
in_aux,
|
||||
angle,
|
||||
};
|
||||
#define ADIS16260_GYRO_CHANNEL_SET(axis, mod) \
|
||||
struct iio_chan_spec adis16260_channels_##axis[] = { \
|
||||
{ \
|
||||
.type = IIO_ANGL_VEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = gyro, \
|
||||
.scan_index = ADIS16260_SCAN_GYRO, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 14, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
}, { \
|
||||
.type = IIO_ANGL, \
|
||||
.modified = 1, \
|
||||
.channel2 = mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \
|
||||
.address = angle, \
|
||||
.scan_index = ADIS16260_SCAN_ANGL, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 14, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
}, { \
|
||||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = temp, \
|
||||
.scan_index = ADIS16260_SCAN_TEMP, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
}, { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.extend_name = "supply", \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = in_supply, \
|
||||
.scan_index = ADIS16260_SCAN_SUPPLY, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
}, { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = 1, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = in_aux, \
|
||||
.scan_index = ADIS16260_SCAN_AUX_ADC, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
}, \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(5), \
|
||||
}
|
||||
struct iio_chan_spec adis16260_channels_##axis[] = { \
|
||||
ADIS_GYRO_CHAN(mod, ADIS16260_GYRO_OUT, ADIS16260_SCAN_GYRO, \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, 14), \
|
||||
ADIS_INCLI_CHAN(mod, ADIS16260_ANGL_OUT, ADIS16260_SCAN_ANGL, 0, 14), \
|
||||
ADIS_TEMP_CHAN(ADIS16260_TEMP_OUT, ADIS16260_SCAN_TEMP, 12), \
|
||||
ADIS_SUPPLY_CHAN(ADIS16260_SUPPLY_OUT, ADIS16260_SCAN_SUPPLY, 12), \
|
||||
ADIS_AUX_ADC_CHAN(ADIS16260_AUX_ADC, ADIS16260_SCAN_AUX_ADC, 12), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(5), \
|
||||
}
|
||||
|
||||
static const ADIS16260_GYRO_CHANNEL_SET(x, IIO_MOD_X);
|
||||
static const ADIS16260_GYRO_CHANNEL_SET(y, IIO_MOD_Y);
|
||||
static const ADIS16260_GYRO_CHANNEL_SET(z, IIO_MOD_Z);
|
||||
static const ADIS16260_GYRO_CHANNEL_SET(x, X);
|
||||
static const ADIS16260_GYRO_CHANNEL_SET(y, Y);
|
||||
static const ADIS16260_GYRO_CHANNEL_SET(z, Z);
|
||||
|
||||
static const u8 adis16260_addresses[5][3] = {
|
||||
[gyro] = { ADIS16260_GYRO_OUT,
|
||||
ADIS16260_GYRO_OFF,
|
||||
ADIS16260_GYRO_SCALE },
|
||||
[angle] = { ADIS16260_ANGL_OUT },
|
||||
[in_supply] = { ADIS16260_SUPPLY_OUT },
|
||||
[in_aux] = { ADIS16260_AUX_ADC },
|
||||
[temp] = { ADIS16260_TEMP_OUT },
|
||||
static const u8 adis16260_addresses[][2] = {
|
||||
[ADIS16260_SCAN_GYRO] = { ADIS16260_GYRO_OFF, ADIS16260_GYRO_SCALE },
|
||||
};
|
||||
|
||||
static int adis16260_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
|
@ -469,34 +154,13 @@ static int adis16260_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16260_addresses[chan->address][0];
|
||||
ret = adis16260_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val16 & ADIS16260_ERROR_ACTIVE) {
|
||||
ret = adis16260_check_status(indio_dev);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
val16 = val16 & ((1 << chan->scan_type.realbits) - 1);
|
||||
if (chan->scan_type.sign == 's')
|
||||
val16 = (s16)(val16 <<
|
||||
(16 - chan->scan_type.realbits)) >>
|
||||
(16 - chan->scan_type.realbits);
|
||||
*val = val16;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
return adis_single_conversion(indio_dev, chan,
|
||||
ADIS16260_ERROR_ACTIVE, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = 0;
|
||||
if (spi_get_device_id(st->us)->driver_data) {
|
||||
if (spi_get_device_id(st->adis.spi)->driver_data) {
|
||||
/* 0.01832 degree / sec */
|
||||
*val2 = IIO_DEGREE_TO_RAD(18320);
|
||||
} else {
|
||||
|
@ -533,8 +197,8 @@ static int adis16260_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16260_addresses[chan->address][1];
|
||||
ret = adis16260_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
addr = adis16260_addresses[chan->scan_index][0];
|
||||
ret = adis_read_reg_16(&st->adis, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
|
@ -553,8 +217,8 @@ static int adis16260_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
addr = adis16260_addresses[chan->address][2];
|
||||
ret = adis16260_spi_read_reg_16(indio_dev, addr, &val16);
|
||||
addr = adis16260_addresses[chan->scan_index][1];
|
||||
ret = adis_read_reg_16(&st->adis, addr, &val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
|
@ -572,18 +236,19 @@ static int adis16260_write_raw(struct iio_dev *indio_dev,
|
|||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis16260_state *st = iio_priv(indio_dev);
|
||||
int bits = 12;
|
||||
s16 val16;
|
||||
u8 addr;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
val16 = val & ((1 << bits) - 1);
|
||||
addr = adis16260_addresses[chan->address][1];
|
||||
return adis16260_spi_write_reg_16(indio_dev, addr, val16);
|
||||
addr = adis16260_addresses[chan->scan_index][0];
|
||||
return adis_write_reg_16(&st->adis, addr, val16);
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
val16 = val & ((1 << bits) - 1);
|
||||
addr = adis16260_addresses[chan->address][2];
|
||||
return adis16260_spi_write_reg_16(indio_dev, addr, val16);
|
||||
addr = adis16260_addresses[chan->scan_index][1];
|
||||
return adis_write_reg_16(&st->adis, addr, val16);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -602,9 +267,40 @@ static const struct iio_info adis16260_info = {
|
|||
.attrs = &adis16260_attribute_group,
|
||||
.read_raw = &adis16260_read_raw,
|
||||
.write_raw = &adis16260_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const char * const adis1620_status_error_msgs[] = {
|
||||
[ADIS16260_DIAG_STAT_FLASH_CHK_BIT] = "Flash checksum error",
|
||||
[ADIS16260_DIAG_STAT_SELF_TEST_BIT] = "Self test error",
|
||||
[ADIS16260_DIAG_STAT_OVERFLOW_BIT] = "Sensor overrange",
|
||||
[ADIS16260_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
|
||||
[ADIS16260_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
|
||||
[ADIS16260_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 5.25",
|
||||
[ADIS16260_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 4.75",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16260_data = {
|
||||
.write_delay = 30,
|
||||
.read_delay = 30,
|
||||
.msc_ctrl_reg = ADIS16260_MSC_CTRL,
|
||||
.glob_cmd_reg = ADIS16260_GLOB_CMD,
|
||||
.diag_stat_reg = ADIS16260_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16260_MSC_CTRL_MEM_TEST,
|
||||
.startup_delay = ADIS16260_STARTUP_DELAY,
|
||||
|
||||
.status_error_msgs = adis1620_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16260_DIAG_STAT_FLASH_CHK_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_SELF_TEST_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_OVERFLOW_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_SPI_FAIL_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_FLASH_UPT_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_POWER_HIGH_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_POWER_LOW_BIT),
|
||||
};
|
||||
|
||||
static int __devinit adis16260_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
|
@ -624,10 +320,7 @@ static int __devinit adis16260_probe(struct spi_device *spi)
|
|||
/* this is only used for removal purposes */
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->us = spi;
|
||||
mutex_init(&st->buf_lock);
|
||||
|
||||
indio_dev->name = spi_get_device_id(st->us)->name;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &adis16260_info;
|
||||
indio_dev->num_channels
|
||||
|
@ -651,17 +344,14 @@ static int __devinit adis16260_probe(struct spi_device *spi)
|
|||
indio_dev->num_channels = ARRAY_SIZE(adis16260_channels_x);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis16260_configure_ring(indio_dev);
|
||||
ret = adis_init(&st->adis, indio_dev, spi, &adis16260_data);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = iio_buffer_register(indio_dev,
|
||||
indio_dev->channels,
|
||||
ARRAY_SIZE(adis16260_channels_x));
|
||||
if (ret) {
|
||||
printk(KERN_ERR "failed to initialize the ring\n");
|
||||
goto error_unreg_ring_funcs;
|
||||
}
|
||||
if (indio_dev->buffer) {
|
||||
/* Set default scan mode */
|
||||
iio_scan_mask_set(indio_dev, indio_dev->buffer,
|
||||
|
@ -675,28 +365,19 @@ static int __devinit adis16260_probe(struct spi_device *spi)
|
|||
iio_scan_mask_set(indio_dev, indio_dev->buffer,
|
||||
ADIS16260_SCAN_ANGL);
|
||||
}
|
||||
if (spi->irq) {
|
||||
ret = adis16260_probe_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_uninitialize_ring;
|
||||
}
|
||||
|
||||
/* Get the device into a sane initial state */
|
||||
ret = adis16260_initial_setup(indio_dev);
|
||||
ret = adis_initial_startup(&st->adis);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
goto error_cleanup_buffer_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
adis16260_remove_trigger(indio_dev);
|
||||
error_uninitialize_ring:
|
||||
iio_buffer_unregister(indio_dev);
|
||||
error_unreg_ring_funcs:
|
||||
adis16260_unconfigure_ring(indio_dev);
|
||||
error_cleanup_buffer_trigger:
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
error_ret:
|
||||
|
@ -706,12 +387,11 @@ static int __devinit adis16260_probe(struct spi_device *spi)
|
|||
static int __devexit adis16260_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis16260_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16260_stop_device(indio_dev);
|
||||
adis16260_remove_trigger(indio_dev);
|
||||
iio_buffer_unregister(indio_dev);
|
||||
adis16260_unconfigure_ring(indio_dev);
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include "../ring_sw.h"
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include "adis16260.h"
|
||||
|
||||
/**
|
||||
* adis16260_read_ring_data() read data registers which will be placed into ring
|
||||
* @indio_dev: the IIO device
|
||||
* @rx: somewhere to pass back the value read
|
||||
**/
|
||||
static int adis16260_read_ring_data(struct iio_dev *indio_dev, u8 *rx)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16260_state *st = iio_priv(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 irqreturn_t adis16260_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adis16260_state *st = iio_priv(indio_dev);
|
||||
int i = 0;
|
||||
s16 *data;
|
||||
|
||||
data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
dev_err(&st->us->dev, "memory alloc failed in ring bh");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength) &&
|
||||
adis16260_read_ring_data(indio_dev, st->rx) >= 0)
|
||||
for (; i < bitmap_weight(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength); i++)
|
||||
data[i] = be16_to_cpup((__be16 *)&(st->rx[i*2]));
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp)
|
||||
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)data);
|
||||
|
||||
kfree(data);
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void adis16260_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_dealloc_pollfunc(indio_dev->pollfunc);
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops adis16260_ring_setup_ops = {
|
||||
.preenable = &iio_sw_buffer_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
};
|
||||
|
||||
int adis16260_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iio_buffer *ring;
|
||||
|
||||
ring = iio_sw_rb_allocate(indio_dev);
|
||||
if (!ring) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
ring->scan_timestamp = true;
|
||||
indio_dev->setup_ops = &adis16260_ring_setup_ops;
|
||||
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
&adis16260_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
indio_dev,
|
||||
"adis16260_consumer%d",
|
||||
indio_dev->id);
|
||||
if (indio_dev->pollfunc == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_iio_sw_rb_free;
|
||||
}
|
||||
|
||||
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
|
||||
return 0;
|
||||
|
||||
error_iio_sw_rb_free:
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include "adis16260.h"
|
||||
|
||||
/**
|
||||
* 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 iio_dev *indio_dev = trig->private_data;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
|
||||
return adis16260_set_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops adis16260_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &adis16260_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int adis16260_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct adis16260_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->trig = iio_trigger_alloc("%s-dev%d",
|
||||
spi_get_device_id(st->us)->name,
|
||||
indio_dev->id);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = request_irq(st->us->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"adis16260",
|
||||
st->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
|
||||
st->trig->dev.parent = &st->us->dev;
|
||||
st->trig->ops = &adis16260_trigger_ops;
|
||||
st->trig->private_data = indio_dev;
|
||||
ret = iio_trigger_register(st->trig);
|
||||
|
||||
/* select default trigger */
|
||||
indio_dev->trig = st->trig;
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(st->us->irq, st->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(st->trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void adis16260_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16260_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(st->trig);
|
||||
free_irq(st->us->irq, st->trig);
|
||||
iio_trigger_free(st->trig);
|
||||
}
|
|
@ -356,7 +356,7 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int isl29018_read_raw(struct iio_dev *indio_dev,
|
||||
|
|
|
@ -652,7 +652,7 @@ static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
if (!state && (chip->intr & 0x30)) {
|
||||
chip->intr |= ~0x30;
|
||||
chip->intr &= ~0x30;
|
||||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
TSL2563_CMD | TSL2563_REG_INT,
|
||||
chip->intr);
|
||||
|
@ -814,7 +814,7 @@ static int __devexit tsl2563_remove(struct i2c_client *client)
|
|||
if (!chip->int_enabled)
|
||||
cancel_delayed_work(&chip->poweroff_work);
|
||||
/* Ensure that interrupts are disabled - then flush any bottom halves */
|
||||
chip->intr |= ~0x30;
|
||||
chip->intr &= ~0x30;
|
||||
i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | TSL2563_REG_INT,
|
||||
chip->intr);
|
||||
flush_scheduled_work();
|
||||
|
|
|
@ -195,7 +195,7 @@ bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
|
|||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline int iio_buffer_register(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec *channels,
|
||||
const struct iio_chan_spec *channels,
|
||||
int num_channels)
|
||||
{
|
||||
return 0;
|
||||
|
|
|
@ -620,6 +620,9 @@ static inline struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev)
|
|||
};
|
||||
#endif
|
||||
|
||||
int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
|
||||
int *fract);
|
||||
|
||||
/**
|
||||
* IIO_DEGREE_TO_RAD() - Convert degree to rad
|
||||
* @deg: A value in degree
|
||||
|
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Common library for ADIS16XXX devices
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#ifndef __IIO_ADIS_H__
|
||||
#define __IIO_ADIS_H__
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iio/types.h>
|
||||
|
||||
#define ADIS_WRITE_REG(reg) ((0x80 | (reg)))
|
||||
#define ADIS_READ_REG(reg) ((reg) & 0x7f)
|
||||
|
||||
#define ADIS_PAGE_SIZE 0x80
|
||||
#define ADIS_REG_PAGE_ID 0x00
|
||||
|
||||
struct adis;
|
||||
|
||||
/**
|
||||
* struct adis_data - ADIS chip variant specific data
|
||||
* @read_delay: SPI delay for read operations in us
|
||||
* @write_delay: SPI delay for write operations in us
|
||||
* @glob_cmd_reg: Register address of the GLOB_CMD register
|
||||
* @msc_ctrl_reg: Register address of the MSC_CTRL register
|
||||
* @diag_stat_reg: Register address of the DIAG_STAT register
|
||||
* @status_error_msgs: Array of error messgaes
|
||||
* @status_error_mask:
|
||||
*/
|
||||
struct adis_data {
|
||||
unsigned int read_delay;
|
||||
unsigned int write_delay;
|
||||
|
||||
unsigned int glob_cmd_reg;
|
||||
unsigned int msc_ctrl_reg;
|
||||
unsigned int diag_stat_reg;
|
||||
|
||||
unsigned int self_test_mask;
|
||||
unsigned int startup_delay;
|
||||
|
||||
const char * const *status_error_msgs;
|
||||
unsigned int status_error_mask;
|
||||
|
||||
int (*enable_irq)(struct adis *adis, bool enable);
|
||||
|
||||
bool has_paging;
|
||||
};
|
||||
|
||||
struct adis {
|
||||
struct spi_device *spi;
|
||||
struct iio_trigger *trig;
|
||||
|
||||
const struct adis_data *data;
|
||||
|
||||
struct mutex txrx_lock;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer *xfer;
|
||||
unsigned int current_page;
|
||||
void *buffer;
|
||||
|
||||
uint8_t tx[10] ____cacheline_aligned;
|
||||
uint8_t rx[4];
|
||||
};
|
||||
|
||||
int adis_init(struct adis *adis, struct iio_dev *indio_dev,
|
||||
struct spi_device *spi, const struct adis_data *data);
|
||||
int adis_reset(struct adis *adis);
|
||||
|
||||
int adis_write_reg(struct adis *adis, unsigned int reg,
|
||||
unsigned int val, unsigned int size);
|
||||
int adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
unsigned int *val, unsigned int size);
|
||||
|
||||
/**
|
||||
* adis_write_reg_8() - Write single byte to a register
|
||||
* @adis: The adis device
|
||||
* @reg: The address of the register to be written
|
||||
* @value: The value to write
|
||||
*/
|
||||
static inline int adis_write_reg_8(struct adis *adis, unsigned int reg,
|
||||
uint8_t val)
|
||||
{
|
||||
return adis_write_reg(adis, reg, val, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* adis_write_reg_16() - Write 2 bytes to a pair of registers
|
||||
* @adis: The adis device
|
||||
* @reg: The address of the lower of the two registers
|
||||
* @value: Value to be written
|
||||
*/
|
||||
static inline int adis_write_reg_16(struct adis *adis, unsigned int reg,
|
||||
uint16_t val)
|
||||
{
|
||||
return adis_write_reg(adis, reg, val, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* adis_write_reg_32() - write 4 bytes to four registers
|
||||
* @adis: The adis device
|
||||
* @reg: The address of the lower of the four register
|
||||
* @value: Value to be written
|
||||
*/
|
||||
static inline int adis_write_reg_32(struct adis *adis, unsigned int reg,
|
||||
uint32_t val)
|
||||
{
|
||||
return adis_write_reg(adis, reg, val, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* adis_read_reg_16() - read 2 bytes from a 16-bit register
|
||||
* @adis: The adis device
|
||||
* @reg: The address of the lower of the two registers
|
||||
* @val: The value read back from the device
|
||||
*/
|
||||
static inline int adis_read_reg_16(struct adis *adis, unsigned int reg,
|
||||
uint16_t *val)
|
||||
{
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg(adis, reg, &tmp, 2);
|
||||
*val = tmp;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis_read_reg_32() - read 4 bytes from a 32-bit register
|
||||
* @adis: The adis device
|
||||
* @reg: The address of the lower of the two registers
|
||||
* @val: The value read back from the device
|
||||
*/
|
||||
static inline int adis_read_reg_32(struct adis *adis, unsigned int reg,
|
||||
uint32_t *val)
|
||||
{
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg(adis, reg, &tmp, 4);
|
||||
*val = tmp;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adis_enable_irq(struct adis *adis, bool enable);
|
||||
int adis_check_status(struct adis *adis);
|
||||
|
||||
int adis_initial_startup(struct adis *adis);
|
||||
|
||||
int adis_single_conversion(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int error_mask,
|
||||
int *val);
|
||||
|
||||
#define ADIS_VOLTAGE_CHAN(addr, si, chan, name, bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (chan), \
|
||||
.extend_name = name, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = (si), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS_SUPPLY_CHAN(addr, si, bits) \
|
||||
ADIS_VOLTAGE_CHAN(addr, si, 0, "supply", bits)
|
||||
|
||||
#define ADIS_AUX_ADC_CHAN(addr, si, bits) \
|
||||
ADIS_VOLTAGE_CHAN(addr, si, 1, NULL, bits)
|
||||
|
||||
#define ADIS_TEMP_CHAN(addr, si, bits) { \
|
||||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = (si), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS_MOD_CHAN(_type, mod, addr, si, info, bits) { \
|
||||
.type = (_type), \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
info, \
|
||||
.address = (addr), \
|
||||
.scan_index = (si), \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS_ACCEL_CHAN(mod, addr, si, info, bits) \
|
||||
ADIS_MOD_CHAN(IIO_ACCEL, mod, addr, si, info, bits)
|
||||
|
||||
#define ADIS_GYRO_CHAN(mod, addr, si, info, bits) \
|
||||
ADIS_MOD_CHAN(IIO_ANGL_VEL, mod, addr, si, info, bits)
|
||||
|
||||
#define ADIS_INCLI_CHAN(mod, addr, si, info, bits) \
|
||||
ADIS_MOD_CHAN(IIO_INCLI, mod, addr, si, info, bits)
|
||||
|
||||
#define ADIS_ROT_CHAN(mod, addr, si, info, bits) \
|
||||
ADIS_MOD_CHAN(IIO_ROT, mod, addr, si, info, bits)
|
||||
|
||||
#ifdef CONFIG_IIO_ADIS_LIB_BUFFER
|
||||
|
||||
int adis_setup_buffer_and_trigger(struct adis *adis,
|
||||
struct iio_dev *indio_dev, irqreturn_t (*trigger_handler)(int, void *));
|
||||
void adis_cleanup_buffer_and_trigger(struct adis *adis,
|
||||
struct iio_dev *indio_dev);
|
||||
|
||||
int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev);
|
||||
void adis_remove_trigger(struct adis *adis);
|
||||
|
||||
int adis_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask);
|
||||
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline int adis_setup_buffer_and_trigger(struct adis *adis,
|
||||
struct iio_dev *indio_dev, irqreturn_t (*trigger_handler)(int, void *))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void adis_cleanup_buffer_and_trigger(struct adis *adis,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adis_probe_trigger(struct adis *adis,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void adis_remove_trigger(struct adis *adis)
|
||||
{
|
||||
}
|
||||
|
||||
#define adis_update_scan_mode NULL
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
int adis_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg, unsigned int writeval, unsigned int *readval);
|
||||
|
||||
#else
|
||||
|
||||
#define adis_debugfs_reg_access NULL
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -28,6 +28,7 @@ enum iio_chan_type {
|
|||
IIO_CAPACITANCE,
|
||||
IIO_ALTVOLTAGE,
|
||||
IIO_CCT,
|
||||
IIO_PRESSURE,
|
||||
};
|
||||
|
||||
enum iio_modifier {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* AD7298 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_PLATFORM_DATA_AD7298_H__
|
||||
#define __LINUX_PLATFORM_DATA_AD7298_H__
|
||||
|
||||
/**
|
||||
* struct ad7298_platform_data - Platform data for the ad7298 ADC driver
|
||||
* @ext_ref: Whether to use an external reference voltage.
|
||||
**/
|
||||
struct ad7298_platform_data {
|
||||
bool ext_ref;
|
||||
};
|
||||
|
||||
#endif /* IIO_ADC_AD7298_H_ */
|
Loading…
Reference in New Issue