diff options
Diffstat (limited to 'drivers/iio')
39 files changed, 6735 insertions, 257 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index fc937aca71fb..b2f963be3993 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -20,6 +20,12 @@ config IIO_BUFFER if IIO_BUFFER +config IIO_BUFFER_CB +boolean "IIO callback buffer used for push in-kernel interfaces" + help + Should be selected by any drivers that do-inkernel push + usage. That is, those where the data is pushed to the consumer. + config IIO_KFIFO_BUF select IIO_TRIGGER tristate "Industrial I/O buffering based on kfifo" @@ -57,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 diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 761f2b65ac52..a0e8cdd67e4d 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o industrialio-y := industrialio-core.o industrialio-event.o inkern.o industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o +industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o @@ -13,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/ diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index b2510c4d9a5a..fe4bcd7c5b12 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -8,7 +8,7 @@ config HID_SENSOR_ACCEL_3D select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON - tristate "HID Acelerometers 3D" + tristate "HID Accelerometers 3D" help Say yes here to build support for the HID SENSOR accelerometers 3D. diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 314a4057879e..e67bb912bd19 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -197,21 +197,8 @@ static const struct iio_info accel_3d_info = { /* Function to push data to buffer */ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { - struct iio_buffer *buffer = indio_dev->buffer; - int datum_sz; - dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); - if (!buffer) { - dev_err(&indio_dev->dev, "Buffer == NULL\n"); - return; - } - datum_sz = buffer->access->get_bytes_per_datum(buffer); - if (len > datum_sz) { - dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, - datum_sz); - return; - } - iio_push_to_buffer(buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ @@ -319,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; } diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 492758120338..961b8d0a4bac 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -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 @@ -30,6 +42,18 @@ config AD7791 To compile this driver as a module, choose M here: the module will be called ad7791. +config AD7793 + tristate "Analog Devices AD7793 and similar ADCs driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7785, AD7792, AD7793, + AD7794 and AD7795 SPI analog to digital converters (ADC). + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called AD7793. + config AD7476 tristate "Analog Devices AD7476 and similar 1-channel ADCs driver" depends on SPI @@ -45,6 +69,19 @@ config AD7476 To compile this driver as a module, choose M here: the module will be called ad7476. +config AD7887 + tristate "Analog Devices AD7887 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices + AD7887 SPI analog to digital converter (ADC). + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ad7887. + config AT91_ADC tristate "Atmel AT91 ADC" depends on ARCH_AT91 @@ -60,4 +97,32 @@ config LP8788_ADC help Say yes here to build support for TI LP8788 ADC. +config MAX1363 + tristate "Maxim max1363 ADC driver" + depends on I2C + select IIO_TRIGGER + select MAX1363_RING_BUFFER + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Say yes here to build support for many Maxim i2c analog to digital + converters (ADC). (max1361, max1362, max1363, max1364, max1036, + max1037, max1038, max1039, max1136, max1136, max1137, max1138, + max1139, max1236, max1237, max11238, max1239, max11600, max11601, + max11602, max11603, max11604, max11605, max11606, max11607, + max11608, max11609, max11610, max11611, max11612, max11613, + max11614, max11615, max11616, max11617, max11644, max11645, + max11646, max11647) Provides direct access via sysfs and buffered + data via the iio dev interface. + +config TI_ADC081C + tristate "Texas Instruments ADC081C021/027" + depends on I2C + help + If you say yes here you get support for Texas Instruments ADC081C021 + and ADC081C027 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc081c. + endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 900995d5e179..472fd7cd2417 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -4,7 +4,13 @@ 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_AD7793) += ad7793.o +obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o +obj-$(CONFIG_MAX1363) += max1363.o +obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o + diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index b11f214779a2..a6f4fc5f8201 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -91,7 +91,6 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; - struct iio_buffer *buffer = indio_dev->buffer; struct ad7266_state *st = iio_priv(indio_dev); int ret; @@ -99,7 +98,7 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p) if (ret == 0) { if (indio_dev->scan_timestamp) ((s64 *)st->data)[1] = pf->timestamp; - iio_push_to_buffer(buffer, (u8 *)st->data); + iio_push_to_buffers(indio_dev, (u8 *)st->data); } iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c new file mode 100644 index 000000000000..2364807a5d6c --- /dev/null +++ b/drivers/iio/adc/ad7298.c @@ -0,0 +1,408 @@ +/* + * AD7298 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#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 <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) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT, \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec ad7298_channels[] = { + { + .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 = AD7298_CH_TEMP, + .scan_index = -1, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + }, + }, + AD7298_V_CHAN(0), + AD7298_V_CHAN(1), + AD7298_V_CHAN(2), + AD7298_V_CHAN(3), + AD7298_V_CHAN(4), + AD7298_V_CHAN(5), + AD7298_V_CHAN(6), + AD7298_V_CHAN(7), + 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; + st->tx_buf[0] = cpu_to_be16(AD7298_WRITE | st->ext_ref | + (AD7298_CH(0) >> ch)); + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + return be16_to_cpu(st->rx_buf[0]); +} + +static int ad7298_scan_temp(struct ad7298_state *st, int *val) +{ + int ret; + __be16 buf; + + buf = cpu_to_be16(AD7298_WRITE | AD7298_TSENSE | + AD7298_TAVG | st->ext_ref); + + ret = spi_write(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + buf = cpu_to_be16(0); + + ret = spi_write(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + usleep_range(101, 1000); /* sleep > 100us */ + + ret = spi_read(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + *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, + int *val2, + long m) +{ + int ret; + struct ad7298_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { + ret = -EBUSY; + } else { + if (chan->address == AD7298_CH_TEMP) + ret = ad7298_scan_temp(st, val); + else + ret = ad7298_scan_direct(st, chan->address); + } + mutex_unlock(&indio_dev->mlock); + + if (ret < 0) + return ret; + + if (chan->address != AD7298_CH_TEMP) + *val = ret & RES_MASK(AD7298_BITS); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = ad7298_get_ref_voltage(st); + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + *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; +} + +static const struct iio_info ad7298_info = { + .read_raw = &ad7298_read_raw, + .update_scan_mode = ad7298_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad7298_probe(struct spi_device *spi) +{ + struct ad7298_platform_data *pdata = spi->dev.platform_data; + struct ad7298_state *st; + struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st)); + int ret; + + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + 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; + } + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7298_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7298_channels); + indio_dev->info = &ad7298_info; + + /* Setup default message */ + + st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; + st->scan_single_xfer[0].len = 2; + st->scan_single_xfer[0].cs_change = 1; + st->scan_single_xfer[1].tx_buf = &st->tx_buf[1]; + st->scan_single_xfer[1].len = 2; + st->scan_single_xfer[1].cs_change = 1; + st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[2].len = 2; + + spi_message_init(&st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg); + 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); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &ad7298_trigger_handler, NULL); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_ring; + + return 0; + +error_cleanup_ring: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + if (st->ext_ref) + regulator_disable(st->reg); +error_put_reg: + if (st->ext_ref) + regulator_put(st->reg); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad7298_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7298_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (st->ext_ref) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7298_id[] = { + {"ad7298", 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7298_id); + +static struct spi_driver ad7298_driver = { + .driver = { + .name = "ad7298", + .owner = THIS_MODULE, + }, + .probe = ad7298_probe, + .remove = __devexit_p(ad7298_remove), + .id_table = ad7298_id, +}; +module_spi_driver(ad7298_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7298 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 7f2f45a0a48d..330248bfebae 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -76,7 +76,7 @@ static irqreturn_t ad7476_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) ((s64 *)st->data)[1] = time_ns; - iio_push_to_buffer(indio_dev->buffer, st->data); + iio_push_to_buffers(indio_dev, st->data); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c new file mode 100644 index 000000000000..334e31ff7a4e --- /dev/null +++ b/drivers/iio/adc/ad7793.c @@ -0,0 +1,876 @@ +/* + * AD7785/AD7792/AD7793/AD7794/AD7795 SPI ADC driver + * + * Copyright 2011-2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/adc/ad_sigma_delta.h> +#include <linux/platform_data/ad7793.h> + +/* Registers */ +#define AD7793_REG_COMM 0 /* Communications Register (WO, 8-bit) */ +#define AD7793_REG_STAT 0 /* Status Register (RO, 8-bit) */ +#define AD7793_REG_MODE 1 /* Mode Register (RW, 16-bit */ +#define AD7793_REG_CONF 2 /* Configuration Register (RW, 16-bit) */ +#define AD7793_REG_DATA 3 /* Data Register (RO, 16-/24-bit) */ +#define AD7793_REG_ID 4 /* ID Register (RO, 8-bit) */ +#define AD7793_REG_IO 5 /* IO Register (RO, 8-bit) */ +#define AD7793_REG_OFFSET 6 /* Offset Register (RW, 16-bit + * (AD7792)/24-bit (AD7793)) */ +#define AD7793_REG_FULLSALE 7 /* Full-Scale Register + * (RW, 16-bit (AD7792)/24-bit (AD7793)) */ + +/* Communications Register Bit Designations (AD7793_REG_COMM) */ +#define AD7793_COMM_WEN (1 << 7) /* Write Enable */ +#define AD7793_COMM_WRITE (0 << 6) /* Write Operation */ +#define AD7793_COMM_READ (1 << 6) /* Read Operation */ +#define AD7793_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */ +#define AD7793_COMM_CREAD (1 << 2) /* Continuous Read of Data Register */ + +/* Status Register Bit Designations (AD7793_REG_STAT) */ +#define AD7793_STAT_RDY (1 << 7) /* Ready */ +#define AD7793_STAT_ERR (1 << 6) /* Error (Overrange, Underrange) */ +#define AD7793_STAT_CH3 (1 << 2) /* Channel 3 */ +#define AD7793_STAT_CH2 (1 << 1) /* Channel 2 */ +#define AD7793_STAT_CH1 (1 << 0) /* Channel 1 */ + +/* Mode Register Bit Designations (AD7793_REG_MODE) */ +#define AD7793_MODE_SEL(x) (((x) & 0x7) << 13) /* Operation Mode Select */ +#define AD7793_MODE_SEL_MASK (0x7 << 13) /* Operation Mode Select mask */ +#define AD7793_MODE_CLKSRC(x) (((x) & 0x3) << 6) /* ADC Clock Source Select */ +#define AD7793_MODE_RATE(x) ((x) & 0xF) /* Filter Update Rate Select */ + +#define AD7793_MODE_CONT 0 /* Continuous Conversion Mode */ +#define AD7793_MODE_SINGLE 1 /* Single Conversion Mode */ +#define AD7793_MODE_IDLE 2 /* Idle Mode */ +#define AD7793_MODE_PWRDN 3 /* Power-Down Mode */ +#define AD7793_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */ +#define AD7793_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */ +#define AD7793_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */ +#define AD7793_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */ + +#define AD7793_CLK_INT 0 /* Internal 64 kHz Clock not + * available at the CLK pin */ +#define AD7793_CLK_INT_CO 1 /* Internal 64 kHz Clock available + * at the CLK pin */ +#define AD7793_CLK_EXT 2 /* External 64 kHz Clock */ +#define AD7793_CLK_EXT_DIV2 3 /* External Clock divided by 2 */ + +/* Configuration Register Bit Designations (AD7793_REG_CONF) */ +#define AD7793_CONF_VBIAS(x) (((x) & 0x3) << 14) /* Bias Voltage + * Generator Enable */ +#define AD7793_CONF_BO_EN (1 << 13) /* Burnout Current Enable */ +#define AD7793_CONF_UNIPOLAR (1 << 12) /* Unipolar/Bipolar Enable */ +#define AD7793_CONF_BOOST (1 << 11) /* Boost Enable */ +#define AD7793_CONF_GAIN(x) (((x) & 0x7) << 8) /* Gain Select */ +#define AD7793_CONF_REFSEL(x) ((x) << 6) /* INT/EXT Reference Select */ +#define AD7793_CONF_BUF (1 << 4) /* Buffered Mode Enable */ +#define AD7793_CONF_CHAN(x) ((x) & 0xf) /* Channel select */ +#define AD7793_CONF_CHAN_MASK 0xf /* Channel select mask */ + +#define AD7793_CH_AIN1P_AIN1M 0 /* AIN1(+) - AIN1(-) */ +#define AD7793_CH_AIN2P_AIN2M 1 /* AIN2(+) - AIN2(-) */ +#define AD7793_CH_AIN3P_AIN3M 2 /* AIN3(+) - AIN3(-) */ +#define AD7793_CH_AIN1M_AIN1M 3 /* AIN1(-) - AIN1(-) */ +#define AD7793_CH_TEMP 6 /* Temp Sensor */ +#define AD7793_CH_AVDD_MONITOR 7 /* AVDD Monitor */ + +#define AD7795_CH_AIN4P_AIN4M 4 /* AIN4(+) - AIN4(-) */ +#define AD7795_CH_AIN5P_AIN5M 5 /* AIN5(+) - AIN5(-) */ +#define AD7795_CH_AIN6P_AIN6M 6 /* AIN6(+) - AIN6(-) */ +#define AD7795_CH_AIN1M_AIN1M 8 /* AIN1(-) - AIN1(-) */ + +/* ID Register Bit Designations (AD7793_REG_ID) */ +#define AD7785_ID 0xB +#define AD7792_ID 0xA +#define AD7793_ID 0xB +#define AD7794_ID 0xF +#define AD7795_ID 0xF +#define AD7796_ID 0xA +#define AD7797_ID 0xB +#define AD7798_ID 0x8 +#define AD7799_ID 0x9 +#define AD7793_ID_MASK 0xF + +/* IO (Excitation Current Sources) Register Bit Designations (AD7793_REG_IO) */ +#define AD7793_IO_IEXC1_IOUT1_IEXC2_IOUT2 0 /* IEXC1 connect to IOUT1, + * IEXC2 connect to IOUT2 */ +#define AD7793_IO_IEXC1_IOUT2_IEXC2_IOUT1 1 /* IEXC1 connect to IOUT2, + * IEXC2 connect to IOUT1 */ +#define AD7793_IO_IEXC1_IEXC2_IOUT1 2 /* Both current sources + * IEXC1,2 connect to IOUT1 */ +#define AD7793_IO_IEXC1_IEXC2_IOUT2 3 /* Both current sources + * IEXC1,2 connect to IOUT2 */ + +#define AD7793_IO_IXCEN_10uA (1 << 0) /* Excitation Current 10uA */ +#define AD7793_IO_IXCEN_210uA (2 << 0) /* Excitation Current 210uA */ +#define AD7793_IO_IXCEN_1mA (3 << 0) /* Excitation Current 1mA */ + +/* NOTE: + * The AD7792/AD7793 features a dual use data out ready DOUT/RDY output. + * In order to avoid contentions on the SPI bus, it's therefore necessary + * to use spi bus locking. + * + * The DOUT/RDY output must also be wired to an interrupt capable GPIO. + */ + +#define AD7793_FLAG_HAS_CLKSEL BIT(0) +#define AD7793_FLAG_HAS_REFSEL BIT(1) +#define AD7793_FLAG_HAS_VBIAS BIT(2) +#define AD7793_HAS_EXITATION_CURRENT BIT(3) +#define AD7793_FLAG_HAS_GAIN BIT(4) +#define AD7793_FLAG_HAS_BUFFER BIT(5) + +struct ad7793_chip_info { + unsigned int id; + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned int flags; + + const struct iio_info *iio_info; + const u16 *sample_freq_avail; +}; + +struct ad7793_state { + const struct ad7793_chip_info *chip_info; + struct regulator *reg; + u16 int_vref_mv; + u16 mode; + u16 conf; + u32 scale_avail[8][2]; + + struct ad_sigma_delta sd; + +}; + +enum ad7793_supported_device_ids { + ID_AD7785, + ID_AD7792, + ID_AD7793, + ID_AD7794, + ID_AD7795, + ID_AD7796, + ID_AD7797, + ID_AD7798, + ID_AD7799, +}; + +static struct ad7793_state *ad_sigma_delta_to_ad7793(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7793_state, sd); +} + +static int ad7793_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd); + + st->conf &= ~AD7793_CONF_CHAN_MASK; + st->conf |= AD7793_CONF_CHAN(channel); + + return ad_sd_write_reg(&st->sd, AD7793_REG_CONF, 2, st->conf); +} + +static int ad7793_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd); + + st->mode &= ~AD7793_MODE_SEL_MASK; + st->mode |= AD7793_MODE_SEL(mode); + + return ad_sd_write_reg(&st->sd, AD7793_REG_MODE, 2, st->mode); +} + +static const struct ad_sigma_delta_info ad7793_sigma_delta_info = { + .set_channel = ad7793_set_channel, + .set_mode = ad7793_set_mode, + .has_registers = true, + .addr_shift = 3, + .read_mask = BIT(6), +}; + +static const struct ad_sd_calib_data ad7793_calib_arr[6] = { + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN1P_AIN1M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN1P_AIN1M}, + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN2P_AIN2M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN2P_AIN2M}, + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN3P_AIN3M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN3P_AIN3M} +}; + +static int ad7793_calibrate_all(struct ad7793_state *st) +{ + return ad_sd_calibrate_all(&st->sd, ad7793_calib_arr, + ARRAY_SIZE(ad7793_calib_arr)); +} + +static int ad7793_check_platform_data(struct ad7793_state *st, + const struct ad7793_platform_data *pdata) +{ + if ((pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT1 || + pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT2) && + ((pdata->exitation_current != AD7793_IX_10uA) && + (pdata->exitation_current != AD7793_IX_210uA))) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL) && + pdata->clock_src != AD7793_CLK_SRC_INT) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_REFSEL) && + pdata->refsel != AD7793_REFSEL_REFIN1) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) && + pdata->bias_voltage != AD7793_BIAS_VOLTAGE_DISABLED) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) && + pdata->exitation_current != AD7793_IX_DISABLED) + return -EINVAL; + + return 0; +} + +static int ad7793_setup(struct iio_dev *indio_dev, + const struct ad7793_platform_data *pdata, + unsigned int vref_mv) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int i, ret = -1; + unsigned long long scale_uv; + u32 id; + + ret = ad7793_check_platform_data(st, pdata); + if (ret) + return ret; + + /* reset the serial interface */ + ret = spi_write(st->sd.spi, (u8 *)&ret, sizeof(ret)); + if (ret < 0) + goto out; + usleep_range(500, 2000); /* Wait for at least 500us */ + + /* write/read test for device presence */ + ret = ad_sd_read_reg(&st->sd, AD7793_REG_ID, 1, &id); + if (ret) + goto out; + + id &= AD7793_ID_MASK; + + if (id != st->chip_info->id) { + dev_err(&st->sd.spi->dev, "device ID query failed\n"); + goto out; + } + + st->mode = AD7793_MODE_RATE(1); + st->conf = 0; + + if (st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL) + st->mode |= AD7793_MODE_CLKSRC(pdata->clock_src); + if (st->chip_info->flags & AD7793_FLAG_HAS_REFSEL) + st->conf |= AD7793_CONF_REFSEL(pdata->refsel); + if (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) + st->conf |= AD7793_CONF_VBIAS(pdata->bias_voltage); + if (pdata->buffered || !(st->chip_info->flags & AD7793_FLAG_HAS_BUFFER)) + st->conf |= AD7793_CONF_BUF; + if (pdata->boost_enable && + (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS)) + st->conf |= AD7793_CONF_BOOST; + if (pdata->burnout_current) + st->conf |= AD7793_CONF_BO_EN; + if (pdata->unipolar) + st->conf |= AD7793_CONF_UNIPOLAR; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_GAIN)) + st->conf |= AD7793_CONF_GAIN(7); + + ret = ad7793_set_mode(&st->sd, AD_SD_MODE_IDLE); + if (ret) + goto out; + + ret = ad7793_set_channel(&st->sd, 0); + if (ret) + goto out; + + if (st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) { + ret = ad_sd_write_reg(&st->sd, AD7793_REG_IO, 1, + pdata->exitation_current | + (pdata->current_source_direction << 2)); + if (ret) + goto out; + } + + ret = ad7793_calibrate_all(st); + if (ret) + goto out; + + /* Populate available ADC input ranges */ + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + scale_uv = ((u64)vref_mv * 100000000) + >> (st->chip_info->channels[0].scan_type.realbits - + (!!(st->conf & AD7793_CONF_UNIPOLAR) ? 0 : 1)); + scale_uv >>= i; + + st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10; + st->scale_avail[i][0] = scale_uv; + } + + return 0; +out: + dev_err(&st->sd.spi->dev, "setup failed\n"); + return ret; +} + +static const u16 ad7793_sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39, + 33, 19, 17, 16, 12, 10, 8, 6, 4}; + +static const u16 ad7797_sample_freq_avail[16] = {0, 0, 0, 123, 62, 50, 0, + 33, 0, 17, 16, 12, 10, 8, 6, 4}; + +static ssize_t ad7793_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7793_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", + st->chip_info->sample_freq_avail[AD7793_MODE_RATE(st->mode)]); +} + +static ssize_t ad7793_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 ad7793_state *st = iio_priv(indio_dev); + long lval; + int i, ret; + + mutex_lock(&indio_dev->mlock); + if (iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + mutex_unlock(&indio_dev->mlock); + + ret = kstrtol(buf, 10, &lval); + if (ret) + return ret; + + if (lval == 0) + return -EINVAL; + + ret = -EINVAL; + + for (i = 0; i < 16; i++) + if (lval == st->chip_info->sample_freq_avail[i]) { + mutex_lock(&indio_dev->mlock); + st->mode &= ~AD7793_MODE_RATE(-1); + st->mode |= AD7793_MODE_RATE(i); + ad_sd_write_reg(&st->sd, AD7793_REG_MODE, + sizeof(st->mode), st->mode); + mutex_unlock(&indio_dev->mlock); + ret = 0; + } + + return ret ? ret : len; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + ad7793_read_frequency, + ad7793_write_frequency); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( + "470 242 123 62 50 39 33 19 17 16 12 10 8 6 4"); + +static IIO_CONST_ATTR_NAMED(sampling_frequency_available_ad7797, + sampling_frequency_available, "123 62 50 33 17 16 12 10 8 6 4"); + +static ssize_t ad7793_show_scale_available(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7793_state *st = iio_priv(indio_dev); + int i, len = 0; + + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + len += sprintf(buf + len, "%d.%09u ", st->scale_avail[i][0], + st->scale_avail[i][1]); + + len += sprintf(buf + len, "\n"); + + return len; +} + +static IIO_DEVICE_ATTR_NAMED(in_m_in_scale_available, + in_voltage-voltage_scale_available, S_IRUGO, + ad7793_show_scale_available, NULL, 0); + +static struct attribute *ad7793_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_m_in_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7793_attribute_group = { + .attrs = ad7793_attributes, +}; + +static struct attribute *ad7797_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available_ad7797.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7797_attribute_group = { + .attrs = ad7797_attributes, +}; + +static int ad7793_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int ret; + unsigned long long scale_uv; + bool unipolar = !!(st->conf & AD7793_CONF_UNIPOLAR); + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad_sigma_delta_single_conversion(indio_dev, chan, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->differential) { + *val = st-> + scale_avail[(st->conf >> 8) & 0x7][0]; + *val2 = st-> + scale_avail[(st->conf >> 8) & 0x7][1]; + return IIO_VAL_INT_PLUS_NANO; + } else { + /* 1170mV / 2^23 * 6 */ + scale_uv = (1170ULL * 1000000000ULL * 6ULL); + } + break; + case IIO_TEMP: + /* 1170mV / 0.81 mV/C / 2^23 */ + scale_uv = 1444444444444444ULL; + break; + default: + return -EINVAL; + } + + scale_uv >>= (chan->scan_type.realbits - (unipolar ? 0 : 1)); + *val = 0; + *val2 = scale_uv; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + if (!unipolar) + *val = -(1 << (chan->scan_type.realbits - 1)); + else + *val = 0; + + /* Kelvin to Celsius */ + if (chan->type == IIO_TEMP) { + unsigned long long offset; + unsigned int shift; + + shift = chan->scan_type.realbits - (unipolar ? 0 : 1); + offset = 273ULL << shift; + do_div(offset, 1444); + *val -= offset; + } + return IIO_VAL_INT; + } + return -EINVAL; +} + +static int ad7793_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int ret, i; + unsigned int tmp; + + mutex_lock(&indio_dev->mlock); + if (iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = -EINVAL; + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + if (val2 == st->scale_avail[i][1]) { + ret = 0; + tmp = st->conf; + st->conf &= ~AD7793_CONF_GAIN(-1); + st->conf |= AD7793_CONF_GAIN(i); + + if (tmp == st->conf) + break; + + ad_sd_write_reg(&st->sd, AD7793_REG_CONF, + sizeof(st->conf), st->conf); + ad7793_calibrate_all(st); + break; + } + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&indio_dev->mlock); + return ret; +} + +static int ad7793_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static const struct iio_info ad7793_info = { + .read_raw = &ad7793_read_raw, + .write_raw = &ad7793_write_raw, + .write_raw_get_fmt = &ad7793_write_raw_get_fmt, + .attrs = &ad7793_attribute_group, + .validate_trigger = ad_sd_validate_trigger, + .driver_module = THIS_MODULE, +}; + +static const struct iio_info ad7797_info = { + .read_raw = &ad7793_read_raw, + .write_raw = &ad7793_write_raw, + .write_raw_get_fmt = &ad7793_write_raw_get_fmt, + .attrs = &ad7793_attribute_group, + .validate_trigger = ad_sd_validate_trigger, + .driver_module = THIS_MODULE, +}; + +#define DECLARE_AD7793_CHANNELS(_name, _b, _sb, _s) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), (_s)), \ + AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), (_s)), \ + AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), (_s)), \ + AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), (_s)), \ + AD_SD_TEMP_CHANNEL(4, AD7793_CH_TEMP, (_b), (_sb), (_s)), \ + AD_SD_SUPPLY_CHANNEL(5, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), (_s)), \ + IIO_CHAN_SOFT_TIMESTAMP(6), \ +} + +#define DECLARE_AD7795_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(3, 3, 3, AD7795_CH_AIN4P_AIN4M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(4, 4, 4, AD7795_CH_AIN5P_AIN5M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(5, 5, 5, AD7795_CH_AIN6P_AIN6M, (_b), (_sb), 0), \ + AD_SD_SHORTED_CHANNEL(6, 0, AD7795_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD_SD_TEMP_CHANNEL(7, AD7793_CH_TEMP, (_b), (_sb), 0), \ + AD_SD_SUPPLY_CHANNEL(8, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(9), \ +} + +#define DECLARE_AD7797_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD_SD_SHORTED_CHANNEL(1, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD_SD_TEMP_CHANNEL(2, AD7793_CH_TEMP, (_b), (_sb), 0), \ + AD_SD_SUPPLY_CHANNEL(3, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_AD7799_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ + AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ + AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD_SD_SUPPLY_CHANNEL(4, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(5), \ +} + +static DECLARE_AD7793_CHANNELS(ad7785, 20, 32, 4); +static DECLARE_AD7793_CHANNELS(ad7792, 16, 32, 0); +static DECLARE_AD7793_CHANNELS(ad7793, 24, 32, 0); +static DECLARE_AD7795_CHANNELS(ad7794, 16, 32); +static DECLARE_AD7795_CHANNELS(ad7795, 24, 32); +static DECLARE_AD7797_CHANNELS(ad7796, 16, 16); +static DECLARE_AD7797_CHANNELS(ad7797, 24, 32); +static DECLARE_AD7799_CHANNELS(ad7798, 16, 16); +static DECLARE_AD7799_CHANNELS(ad7799, 24, 32); + +static const struct ad7793_chip_info ad7793_chip_info_tbl[] = { + [ID_AD7785] = { + .id = AD7785_ID, + .channels = ad7785_channels, + .num_channels = ARRAY_SIZE(ad7785_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7792] = { + .id = AD7792_ID, + .channels = ad7792_channels, + .num_channels = ARRAY_SIZE(ad7792_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7793] = { + .id = AD7793_ID, + .channels = ad7793_channels, + .num_channels = ARRAY_SIZE(ad7793_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7794] = { + .id = AD7794_ID, + .channels = ad7794_channels, + .num_channels = ARRAY_SIZE(ad7794_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7795] = { + .id = AD7795_ID, + .channels = ad7795_channels, + .num_channels = ARRAY_SIZE(ad7795_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7796] = { + .id = AD7796_ID, + .channels = ad7796_channels, + .num_channels = ARRAY_SIZE(ad7796_channels), + .iio_info = &ad7797_info, + .sample_freq_avail = ad7797_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL, + }, + [ID_AD7797] = { + .id = AD7797_ID, + .channels = ad7797_channels, + .num_channels = ARRAY_SIZE(ad7797_channels), + .iio_info = &ad7797_info, + .sample_freq_avail = ad7797_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL, + }, + [ID_AD7798] = { + .id = AD7798_ID, + .channels = ad7798_channels, + .num_channels = ARRAY_SIZE(ad7798_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7799] = { + .id = AD7799_ID, + .channels = ad7799_channels, + .num_channels = ARRAY_SIZE(ad7799_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, +}; + +static int ad7793_probe(struct spi_device *spi) +{ + const struct ad7793_platform_data *pdata = spi->dev.platform_data; + struct ad7793_state *st; + struct iio_dev *indio_dev; + int ret, vref_mv = 0; + + if (!pdata) { + dev_err(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + if (!spi->irq) { + dev_err(&spi->dev, "no IRQ?\n"); + return -ENODEV; + } + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ad_sd_init(&st->sd, indio_dev, spi, &ad7793_sigma_delta_info); + + if (pdata->refsel != AD7793_REFSEL_INTERNAL) { + st->reg = regulator_get(&spi->dev, "refin"); + if (IS_ERR(st->reg)) { + ret = PTR_ERR(st->reg); + goto error_device_free; + } + + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + vref_mv = regulator_get_voltage(st->reg); + if (vref_mv < 0) { + ret = vref_mv; + goto error_disable_reg; + } + + vref_mv /= 1000; + } else { + vref_mv = 1170; /* Build-in ref */ + } + + st->chip_info = + &ad7793_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + spi_set_drvdata(spi, indio_dev); + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = st->chip_info->iio_info; + + ret = ad_sd_setup_buffer_and_trigger(indio_dev); + if (ret) + goto error_disable_reg; + + ret = ad7793_setup(indio_dev, pdata, vref_mv); + if (ret) + goto error_remove_trigger; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_remove_trigger; + + return 0; + +error_remove_trigger: + ad_sd_cleanup_buffer_and_trigger(indio_dev); +error_disable_reg: + if (pdata->refsel != AD7793_REFSEL_INTERNAL) + regulator_disable(st->reg); +error_put_reg: + if (pdata->refsel != AD7793_REFSEL_INTERNAL) + regulator_put(st->reg); +error_device_free: + iio_device_free(indio_dev); + + return ret; +} + +static int ad7793_remove(struct spi_device *spi) +{ + const struct ad7793_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7793_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + ad_sd_cleanup_buffer_and_trigger(indio_dev); + + if (pdata->refsel != AD7793_REFSEL_INTERNAL) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7793_id[] = { + {"ad7785", ID_AD7785}, + {"ad7792", ID_AD7792}, + {"ad7793", ID_AD7793}, + {"ad7794", ID_AD7794}, + {"ad7795", ID_AD7795}, + {"ad7796", ID_AD7796}, + {"ad7797", ID_AD7797}, + {"ad7798", ID_AD7798}, + {"ad7799", ID_AD7799}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7793_id); + +static struct spi_driver ad7793_driver = { + .driver = { + .name = "ad7793", + .owner = THIS_MODULE, + }, + .probe = ad7793_probe, + .remove = ad7793_remove, + .id_table = ad7793_id, +}; +module_spi_driver(ad7793_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7793 and simialr ADCs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c new file mode 100644 index 000000000000..81153fafac7a --- /dev/null +++ b/drivers/iio/adc/ad7887.c @@ -0,0 +1,378 @@ +/* + * AD7887 SPI ADC driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/err.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 <linux/platform_data/ad7887.h> + +#define AD7887_REF_DIS (1 << 5) /* on-chip reference disable */ +#define AD7887_DUAL (1 << 4) /* dual-channel mode */ +#define AD7887_CH_AIN1 (1 << 3) /* convert on channel 1, DUAL=1 */ +#define AD7887_CH_AIN0 (0 << 3) /* convert on channel 0, DUAL=0,1 */ +#define AD7887_PM_MODE1 (0) /* CS based shutdown */ +#define AD7887_PM_MODE2 (1) /* full on */ +#define AD7887_PM_MODE3 (2) /* auto shutdown after conversion */ +#define AD7887_PM_MODE4 (3) /* standby mode */ + +enum ad7887_channels { + AD7887_CH0, + AD7887_CH0_CH1, + AD7887_CH1, +}; + +#define RES_MASK(bits) ((1 << (bits)) - 1) + +/** + * struct ad7887_chip_info - chip specifc information + * @int_vref_mv: the internal reference voltage + * @channel: channel specification + */ +struct ad7887_chip_info { + u16 int_vref_mv; + struct iio_chan_spec channel[3]; +}; + +struct ad7887_state { + struct spi_device *spi; + const struct ad7887_chip_info *chip_info; + struct regulator *reg; + struct spi_transfer xfer[4]; + struct spi_message msg[3]; + struct spi_message *ring_msg; + unsigned char tx_cmd_buf[4]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * Buffer needs to be large enough to hold two 16 bit samples and a + * 64 bit aligned 64 bit timestamp. + */ + unsigned char data[ALIGN(4, sizeof(s64)) + sizeof(s64)] + ____cacheline_aligned; +}; + +enum ad7887_supported_device_ids { + ID_AD7887 +}; + +static int ad7887_ring_preenable(struct iio_dev *indio_dev) +{ + struct ad7887_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_sw_buffer_preenable(indio_dev); + if (ret < 0) + return ret; + + /* We know this is a single long so can 'cheat' */ + switch (*indio_dev->active_scan_mask) { + case (1 << 0): + st->ring_msg = &st->msg[AD7887_CH0]; + break; + case (1 << 1): + st->ring_msg = &st->msg[AD7887_CH1]; + /* Dummy read: push CH1 setting down to hardware */ + spi_sync(st->spi, st->ring_msg); + break; + case ((1 << 1) | (1 << 0)): + st->ring_msg = &st->msg[AD7887_CH0_CH1]; + break; + } + + return 0; +} + +static int ad7887_ring_postdisable(struct iio_dev *indio_dev) +{ + struct ad7887_state *st = iio_priv(indio_dev); + + /* dummy read: restore default CH0 settin */ + return spi_sync(st->spi, &st->msg[AD7887_CH0]); +} + +/** + * ad7887_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 ad7887_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7887_state *st = iio_priv(indio_dev); + s64 time_ns; + int b_sent; + + b_sent = spi_sync(st->spi, st->ring_msg); + if (b_sent) + goto done; + + time_ns = iio_get_time_ns(); + + if (indio_dev->scan_timestamp) + memcpy(st->data + indio_dev->scan_bytes - sizeof(s64), + &time_ns, sizeof(time_ns)); + + iio_push_to_buffers(indio_dev, st->data); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_buffer_setup_ops ad7887_ring_setup_ops = { + .preenable = &ad7887_ring_preenable, + .postenable = &iio_triggered_buffer_postenable, + .predisable = &iio_triggered_buffer_predisable, + .postdisable = &ad7887_ring_postdisable, +}; + +static int ad7887_scan_direct(struct ad7887_state *st, unsigned ch) +{ + int ret = spi_sync(st->spi, &st->msg[ch]); + if (ret) + return ret; + + return (st->data[(ch * 2)] << 8) | st->data[(ch * 2) + 1]; +} + +static int ad7887_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7887_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + if (iio_buffer_enabled(indio_dev)) + ret = -EBUSY; + else + ret = ad7887_scan_direct(st, chan->address); + mutex_unlock(&indio_dev->mlock); + + if (ret < 0) + return ret; + *val = ret >> chan->scan_type.shift; + *val &= RES_MASK(chan->scan_type.realbits); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (st->reg) { + *val = regulator_get_voltage(st->reg); + if (*val < 0) + return *val; + *val /= 1000; + } else { + *val = st->chip_info->int_vref_mv; + } + + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + + +static const struct ad7887_chip_info ad7887_chip_info_tbl[] = { + /* + * More devices added in future + */ + [ID_AD7887] = { + .channel[0] = { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 1, + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .address = 1, + .scan_index = 1, + .scan_type = IIO_ST('u', 12, 16, 0), + }, + .channel[1] = { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .address = 0, + .scan_index = 0, + .scan_type = IIO_ST('u', 12, 16, 0), + }, + .channel[2] = IIO_CHAN_SOFT_TIMESTAMP(2), + .int_vref_mv = 2500, + }, +}; + +static const struct iio_info ad7887_info = { + .read_raw = &ad7887_read_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad7887_probe(struct spi_device *spi) +{ + struct ad7887_platform_data *pdata = spi->dev.platform_data; + struct ad7887_state *st; + struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st)); + uint8_t mode; + int ret; + + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + if (!pdata || !pdata->use_onchip_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; + } + + st->chip_info = + &ad7887_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + + /* Estabilish that the iio_dev is a child of the spi device */ + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad7887_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + /* Setup default message */ + + mode = AD7887_PM_MODE4; + if (!pdata || !pdata->use_onchip_ref) + mode |= AD7887_REF_DIS; + if (pdata && pdata->en_dual) + mode |= AD7887_DUAL; + + st->tx_cmd_buf[0] = AD7887_CH_AIN0 | mode; + + st->xfer[0].rx_buf = &st->data[0]; + st->xfer[0].tx_buf = &st->tx_cmd_buf[0]; + st->xfer[0].len = 2; + + spi_message_init(&st->msg[AD7887_CH0]); + spi_message_add_tail(&st->xfer[0], &st->msg[AD7887_CH0]); + + if (pdata && pdata->en_dual) { + st->tx_cmd_buf[2] = AD7887_CH_AIN1 | mode; + + st->xfer[1].rx_buf = &st->data[0]; + st->xfer[1].tx_buf = &st->tx_cmd_buf[2]; + st->xfer[1].len = 2; + + st->xfer[2].rx_buf = &st->data[2]; + st->xfer[2].tx_buf = &st->tx_cmd_buf[0]; + st->xfer[2].len = 2; + + spi_message_init(&st->msg[AD7887_CH0_CH1]); + spi_message_add_tail(&st->xfer[1], &st->msg[AD7887_CH0_CH1]); + spi_message_add_tail(&st->xfer[2], &st->msg[AD7887_CH0_CH1]); + + st->xfer[3].rx_buf = &st->data[2]; + st->xfer[3].tx_buf = &st->tx_cmd_buf[2]; + st->xfer[3].len = 2; + + spi_message_init(&st->msg[AD7887_CH1]); + spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]); + + indio_dev->channels = st->chip_info->channel; + indio_dev->num_channels = 3; + } else { + indio_dev->channels = &st->chip_info->channel[1]; + indio_dev->num_channels = 2; + } + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &ad7887_trigger_handler, &ad7887_ring_setup_ops); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unregister_ring; + + return 0; +error_unregister_ring: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + if (st->reg) + regulator_disable(st->reg); +error_put_reg: + if (st->reg) + regulator_put(st->reg); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad7887_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7887_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (st->reg) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7887_id[] = { + {"ad7887", ID_AD7887}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7887_id); + +static struct spi_driver ad7887_driver = { + .driver = { + .name = "ad7887", + .owner = THIS_MODULE, + }, + .probe = ad7887_probe, + .remove = __devexit_p(ad7887_remove), + .id_table = ad7887_id, +}; +module_spi_driver(ad7887_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7887 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 67baa1363d7a..afe6d78c8ff0 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -391,7 +391,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) break; } - iio_push_to_buffer(indio_dev->buffer, (uint8_t *)data); + iio_push_to_buffers(indio_dev, (uint8_t *)data); iio_trigger_notify_done(indio_dev->trig); sigma_delta->irq_dis = false; diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 3ed94bf80596..03b85940f4ba 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -46,7 +46,6 @@ struct at91_adc_state { struct clk *clk; bool done; int irq; - bool irq_enabled; u16 last_value; struct mutex lock; u8 num_channels; @@ -66,7 +65,6 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *idev = pf->indio_dev; struct at91_adc_state *st = iio_priv(idev); - struct iio_buffer *buffer = idev->buffer; int i, j = 0; for (i = 0; i < idev->masklength; i++) { @@ -82,10 +80,9 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) *timestamp = pf->timestamp; } - buffer->access->store_to(buffer, (u8 *)st->buffer); + iio_push_to_buffers(indio_dev, (u8 *)st->buffer); iio_trigger_notify_done(idev->trig); - st->irq_enabled = true; /* Needed to ACK the DRDY interruption */ at91_adc_readl(st, AT91_ADC_LCDR); @@ -106,7 +103,6 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private) if (iio_buffer_enabled(idev)) { disable_irq_nosync(irq); - st->irq_enabled = false; iio_trigger_poll(idev->trig, iio_get_time_ns()); } else { st->last_value = at91_adc_readl(st, AT91_ADC_LCDR); diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c new file mode 100644 index 000000000000..1e84b5b55093 --- /dev/null +++ b/drivers/iio/adc/max1363.c @@ -0,0 +1,1700 @@ + /* + * iio/adc/max1363.c + * Copyright (C) 2008-2010 Jonathan Cameron + * + * based on linux/drivers/i2c/chips/max123x + * Copyright (C) 2002-2004 Stefan Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * 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. + * + * max1363.c + * + * Partial support for max1363 and similar chips. + * + * Not currently implemented. + * + * - Control of internal reference. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/i2c.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/buffer.h> +#include <linux/iio/driver.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/trigger_consumer.h> + +#define MAX1363_SETUP_BYTE(a) ((a) | 0x80) + +/* There is a fair bit more defined here than currently + * used, but the intention is to support everything these + * chips do in the long run */ + +/* see data sheets */ +/* max1363 and max1236, max1237, max1238, max1239 */ +#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD 0x00 +#define MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF 0x20 +#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT 0x40 +#define MAX1363_SETUP_AIN3_IS_REF_REF_IS_INT 0x60 +#define MAX1363_SETUP_POWER_UP_INT_REF 0x10 +#define MAX1363_SETUP_POWER_DOWN_INT_REF 0x00 + +/* think about includeing max11600 etc - more settings */ +#define MAX1363_SETUP_EXT_CLOCK 0x08 +#define MAX1363_SETUP_INT_CLOCK 0x00 +#define MAX1363_SETUP_UNIPOLAR 0x00 +#define MAX1363_SETUP_BIPOLAR 0x04 +#define MAX1363_SETUP_RESET 0x00 +#define MAX1363_SETUP_NORESET 0x02 +/* max1363 only - though don't care on others. + * For now monitor modes are not implemented as the relevant + * line is not connected on my test board. + * The definitions are here as I intend to add this soon. + */ +#define MAX1363_SETUP_MONITOR_SETUP 0x01 + +/* Specific to the max1363 */ +#define MAX1363_MON_RESET_CHAN(a) (1 << ((a) + 4)) +#define MAX1363_MON_INT_ENABLE 0x01 + +/* defined for readability reasons */ +/* All chips */ +#define MAX1363_CONFIG_BYTE(a) ((a)) + +#define MAX1363_CONFIG_SE 0x01 +#define MAX1363_CONFIG_DE 0x00 +#define MAX1363_CONFIG_SCAN_TO_CS 0x00 +#define MAX1363_CONFIG_SCAN_SINGLE_8 0x20 +#define MAX1363_CONFIG_SCAN_MONITOR_MODE 0x40 +#define MAX1363_CONFIG_SCAN_SINGLE_1 0x60 +/* max123{6-9} only */ +#define MAX1236_SCAN_MID_TO_CHANNEL 0x40 + +/* max1363 only - merely part of channel selects or don't care for others*/ +#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18 + +#define MAX1363_CHANNEL_SEL(a) ((a) << 1) + +/* max1363 strictly 0x06 - but doesn't matter */ +#define MAX1363_CHANNEL_SEL_MASK 0x1E +#define MAX1363_SCAN_MASK 0x60 +#define MAX1363_SE_DE_MASK 0x01 + +#define MAX1363_MAX_CHANNELS 25 +/** + * struct max1363_mode - scan mode information + * @conf: The corresponding value of the configuration register + * @modemask: Bit mask corresponding to channels enabled in this mode + */ +struct max1363_mode { + int8_t conf; + DECLARE_BITMAP(modemask, MAX1363_MAX_CHANNELS); +}; + +/* This must be maintained along side the max1363_mode_table in max1363_core */ +enum max1363_modes { + /* Single read of a single channel */ + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, + /* Differential single read */ + d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, + d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, + /* Scan to channel and mid to channel where overlapping */ + s0to1, s0to2, s2to3, s0to3, s0to4, s0to5, s0to6, + s6to7, s0to7, s6to8, s0to8, s6to9, + s0to9, s6to10, s0to10, s6to11, s0to11, + /* Differential scan to channel and mid to channel where overlapping */ + d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9, + d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2, + d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8, + d7m6to11m10, d1m0to11m10, +}; + +/** + * struct max1363_chip_info - chip specifc information + * @info: iio core function callbacks structure + * @channels: channel specification + * @num_channels: number of channels + * @mode_list: array of available scan modes + * @default_mode: the scan mode in which the chip starts up + * @int_vref_mv: the internal reference voltage + * @num_channels: number of channels + * @bits: accuracy of the adc in bits + */ +struct max1363_chip_info { + const struct iio_info *info; + const struct iio_chan_spec *channels; + int num_channels; + const enum max1363_modes *mode_list; + enum max1363_modes default_mode; + u16 int_vref_mv; + u8 num_modes; + u8 bits; +}; + +/** + * struct max1363_state - driver instance specific data + * @client: i2c_client + * @setupbyte: cache of current device setup byte + * @configbyte: cache of current device config byte + * @chip_info: chip model specific constants, available modes etc + * @current_mode: the scan mode of this chip + * @requestedmask: a valid requested set of channels + * @reg: supply regulator + * @monitor_on: whether monitor mode is enabled + * @monitor_speed: parameter corresponding to device monitor speed setting + * @mask_high: bitmask for enabled high thresholds + * @mask_low: bitmask for enabled low thresholds + * @thresh_high: high threshold values + * @thresh_low: low threshold values + */ +struct max1363_state { + struct i2c_client *client; + u8 setupbyte; + u8 configbyte; + const struct max1363_chip_info *chip_info; + const struct max1363_mode *current_mode; + u32 requestedmask; + struct regulator *reg; + + /* Using monitor modes and buffer at the same time is + currently not supported */ + bool monitor_on; + unsigned int monitor_speed:3; + u8 mask_high; + u8 mask_low; + /* 4x unipolar first then the fours bipolar ones */ + s16 thresh_high[8]; + s16 thresh_low[8]; +}; + +#define MAX1363_MODE_SINGLE(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_SINGLE_1 \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask, \ + } + +#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_TO_CS \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask, \ + } + +/* note not available for max1363 hence naming */ +#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1236_SCAN_MID_TO_CHANNEL \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask \ +} + +#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_nump) \ + | MAX1363_CONFIG_SCAN_SINGLE_1 \ + | MAX1363_CONFIG_DE, \ + .modemask[0] = _mask \ + } + +/* Can't think how to automate naming so specify for now */ +#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_TO_CS \ + | MAX1363_CONFIG_DE, \ + .modemask[0] = _mask \ + } + +/* note only available for max1363 hence naming */ +#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1236_SCAN_MID_TO_CHANNEL \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask \ +} + +static const struct max1363_mode max1363_mode_table[] = { + /* All of the single channel options first */ + MAX1363_MODE_SINGLE(0, 1 << 0), + MAX1363_MODE_SINGLE(1, 1 << 1), + MAX1363_MODE_SINGLE(2, 1 << 2), + MAX1363_MODE_SINGLE(3, 1 << 3), + MAX1363_MODE_SINGLE(4, 1 << 4), + MAX1363_MODE_SINGLE(5, 1 << 5), + MAX1363_MODE_SINGLE(6, 1 << 6), + MAX1363_MODE_SINGLE(7, 1 << 7), + MAX1363_MODE_SINGLE(8, 1 << 8), + MAX1363_MODE_SINGLE(9, 1 << 9), + MAX1363_MODE_SINGLE(10, 1 << 10), + MAX1363_MODE_SINGLE(11, 1 << 11), + + MAX1363_MODE_DIFF_SINGLE(0, 1, 1 << 12), + MAX1363_MODE_DIFF_SINGLE(2, 3, 1 << 13), + MAX1363_MODE_DIFF_SINGLE(4, 5, 1 << 14), + MAX1363_MODE_DIFF_SINGLE(6, 7, 1 << 15), + MAX1363_MODE_DIFF_SINGLE(8, 9, 1 << 16), + MAX1363_MODE_DIFF_SINGLE(10, 11, 1 << 17), + MAX1363_MODE_DIFF_SINGLE(1, 0, 1 << 18), + MAX1363_MODE_DIFF_SINGLE(3, 2, 1 << 19), + MAX1363_MODE_DIFF_SINGLE(5, 4, 1 << 20), + MAX1363_MODE_DIFF_SINGLE(7, 6, 1 << 21), + MAX1363_MODE_DIFF_SINGLE(9, 8, 1 << 22), + MAX1363_MODE_DIFF_SINGLE(11, 10, 1 << 23), + + /* The multichannel scans next */ + MAX1363_MODE_SCAN_TO_CHANNEL(1, 0x003), + MAX1363_MODE_SCAN_TO_CHANNEL(2, 0x007), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3, 0x00C), + MAX1363_MODE_SCAN_TO_CHANNEL(3, 0x00F), + MAX1363_MODE_SCAN_TO_CHANNEL(4, 0x01F), + MAX1363_MODE_SCAN_TO_CHANNEL(5, 0x03F), + MAX1363_MODE_SCAN_TO_CHANNEL(6, 0x07F), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7, 0x0C0), + MAX1363_MODE_SCAN_TO_CHANNEL(7, 0x0FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8, 0x1C0), + MAX1363_MODE_SCAN_TO_CHANNEL(8, 0x1FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9, 0x3C0), + MAX1363_MODE_SCAN_TO_CHANNEL(9, 0x3FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10, 0x7C0), + MAX1363_MODE_SCAN_TO_CHANNEL(10, 0x7FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11, 0xFC0), + MAX1363_MODE_SCAN_TO_CHANNEL(11, 0xFFF), + + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(2, 2, 0x003000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(4, 3, 0x007000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(6, 4, 0x00F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(8, 2, 0x018000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(8, 5, 0x01F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(10, 3, 0x038000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(10, 6, 0x3F000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(3, 2, 0x0C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(5, 3, 0x1C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(7, 4, 0x3C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(9, 2, 0x600000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(9, 5, 0x7C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(11, 3, 0xE00000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(11, 6, 0xFC0000), +}; + +static const struct max1363_mode +*max1363_match_mode(const unsigned long *mask, +const struct max1363_chip_info *ci) +{ + int i; + if (mask) + for (i = 0; i < ci->num_modes; i++) + if (bitmap_subset(mask, + max1363_mode_table[ci->mode_list[i]]. + modemask, + MAX1363_MAX_CHANNELS)) + return &max1363_mode_table[ci->mode_list[i]]; + return NULL; +} + +static int max1363_write_basic_config(struct i2c_client *client, + unsigned char d1, + unsigned char d2) +{ + u8 tx_buf[2] = {d1, d2}; + + return i2c_master_send(client, tx_buf, 2); +} + +static int max1363_set_scan_mode(struct max1363_state *st) +{ + st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK + | MAX1363_SCAN_MASK + | MAX1363_SE_DE_MASK); + st->configbyte |= st->current_mode->conf; + + return max1363_write_basic_config(st->client, + st->setupbyte, + st->configbyte); +} + +static int max1363_read_single_chan(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + long m) +{ + int ret = 0; + s32 data; + char rxbuf[2]; + struct max1363_state *st = iio_priv(indio_dev); + struct i2c_client *client = st->client; + + mutex_lock(&indio_dev->mlock); + /* + * If monitor mode is enabled, the method for reading a single + * channel will have to be rather different and has not yet + * been implemented. + * + * Also, cannot read directly if buffered capture enabled. + */ + if (st->monitor_on || iio_buffer_enabled(indio_dev)) { + ret = -EBUSY; + goto error_ret; + } + + /* Check to see if current scan mode is correct */ + if (st->current_mode != &max1363_mode_table[chan->address]) { + /* Update scan mode if needed */ + st->current_mode = &max1363_mode_table[chan->address]; + ret = max1363_set_scan_mode(st); + if (ret < 0) + goto error_ret; + } + if (st->chip_info->bits != 8) { + /* Get reading */ + data = i2c_master_recv(client, rxbuf, 2); + if (data < 0) { + ret = data; + goto error_ret; + } + data = (s32)(rxbuf[1]) | ((s32)(rxbuf[0] & 0x0F)) << 8; + } else { + /* Get reading */ + data = i2c_master_recv(client, rxbuf, 1); + if (data < 0) { + ret = data; + goto error_ret; + } + data = rxbuf[0]; + } + *val = data; +error_ret: + mutex_unlock(&indio_dev->mlock); + return ret; + +} + +static int max1363_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct max1363_state *st = iio_priv(indio_dev); + int ret; + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = max1363_read_single_chan(indio_dev, chan, val, m); + if (ret < 0) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if ((1 << (st->chip_info->bits + 1)) > + st->chip_info->int_vref_mv) { + *val = 0; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + } else { + *val = (st->chip_info->int_vref_mv) + >> st->chip_info->bits; + return IIO_VAL_INT; + } + default: + return -EINVAL; + } + return 0; +} + +/* Applies to max1363 */ +static const enum max1363_modes max1363_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, +}; + +#define MAX1363_EV_M \ + (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) \ + | IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)) +#define MAX1363_INFO_MASK (IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT) +#define MAX1363_CHAN_U(num, addr, si, bits, evmask) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = num, \ + .address = addr, \ + .info_mask = MAX1363_INFO_MASK, \ + .datasheet_name = "AIN"#num, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = bits, \ + .storagebits = (bits > 8) ? 16 : 8, \ + .endianness = IIO_BE, \ + }, \ + .scan_index = si, \ + .event_mask = evmask, \ + } + +/* bipolar channel */ +#define MAX1363_CHAN_B(num, num2, addr, si, bits, evmask) \ + { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .channel = num, \ + .channel2 = num2, \ + .address = addr, \ + .info_mask = MAX1363_INFO_MASK, \ + .datasheet_name = "AIN"#num"-AIN"#num2, \ + .scan_type = { \ + .sign = 's', \ + .realbits = bits, \ + .storagebits = (bits > 8) ? 16 : 8, \ + .endianness = IIO_BE, \ + }, \ + .scan_index = si, \ + .event_mask = evmask, \ + } + +#define MAX1363_4X_CHANS(bits, em) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, em), \ + MAX1363_CHAN_U(1, _s1, 1, bits, em), \ + MAX1363_CHAN_U(2, _s2, 2, bits, em), \ + MAX1363_CHAN_U(3, _s3, 3, bits, em), \ + MAX1363_CHAN_B(0, 1, d0m1, 4, bits, em), \ + MAX1363_CHAN_B(2, 3, d2m3, 5, bits, em), \ + MAX1363_CHAN_B(1, 0, d1m0, 6, bits, em), \ + MAX1363_CHAN_B(3, 2, d3m2, 7, bits, em), \ + IIO_CHAN_SOFT_TIMESTAMP(8) \ + } + +static const struct iio_chan_spec max1036_channels[] = MAX1363_4X_CHANS(8, 0); +static const struct iio_chan_spec max1136_channels[] = MAX1363_4X_CHANS(10, 0); +static const struct iio_chan_spec max1236_channels[] = MAX1363_4X_CHANS(12, 0); +static const struct iio_chan_spec max1361_channels[] = + MAX1363_4X_CHANS(10, MAX1363_EV_M); +static const struct iio_chan_spec max1363_channels[] = + MAX1363_4X_CHANS(12, MAX1363_EV_M); + +/* Applies to max1236, max1237 */ +static const enum max1363_modes max1236_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, + s2to3, +}; + +/* Applies to max1238, max1239 */ +static const enum max1363_modes max1238_mode_list[] = { + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, + s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, + s0to7, s0to8, s0to9, s0to10, s0to11, + d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, + d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, + d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11, + d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10, + s6to7, s6to8, s6to9, s6to10, s6to11, + d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10, +}; + +#define MAX1363_12X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, 0), \ + MAX1363_CHAN_U(2, _s2, 2, bits, 0), \ + MAX1363_CHAN_U(3, _s3, 3, bits, 0), \ + MAX1363_CHAN_U(4, _s4, 4, bits, 0), \ + MAX1363_CHAN_U(5, _s5, 5, bits, 0), \ + MAX1363_CHAN_U(6, _s6, 6, bits, 0), \ + MAX1363_CHAN_U(7, _s7, 7, bits, 0), \ + MAX1363_CHAN_U(8, _s8, 8, bits, 0), \ + MAX1363_CHAN_U(9, _s9, 9, bits, 0), \ + MAX1363_CHAN_U(10, _s10, 10, bits, 0), \ + MAX1363_CHAN_U(11, _s11, 11, bits, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 12, bits, 0), \ + MAX1363_CHAN_B(2, 3, d2m3, 13, bits, 0), \ + MAX1363_CHAN_B(4, 5, d4m5, 14, bits, 0), \ + MAX1363_CHAN_B(6, 7, d6m7, 15, bits, 0), \ + MAX1363_CHAN_B(8, 9, d8m9, 16, bits, 0), \ + MAX1363_CHAN_B(10, 11, d10m11, 17, bits, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 18, bits, 0), \ + MAX1363_CHAN_B(3, 2, d3m2, 19, bits, 0), \ + MAX1363_CHAN_B(5, 4, d5m4, 20, bits, 0), \ + MAX1363_CHAN_B(7, 6, d7m6, 21, bits, 0), \ + MAX1363_CHAN_B(9, 8, d9m8, 22, bits, 0), \ + MAX1363_CHAN_B(11, 10, d11m10, 23, bits, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(24) \ + } +static const struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8); +static const struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10); +static const struct iio_chan_spec max1238_channels[] = MAX1363_12X_CHANS(12); + +static const enum max1363_modes max11607_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + s2to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, +}; + +static const enum max1363_modes max11608_mode_list[] = { + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, + s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7, + s6to7, + d0m1, d2m3, d4m5, d6m7, + d1m0, d3m2, d5m4, d7m6, + d0m1to2m3, d0m1to4m5, d0m1to6m7, + d1m0to3m2, d1m0to5m4, d1m0to7m6, +}; + +#define MAX1363_8X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, 0), \ + MAX1363_CHAN_U(2, _s2, 2, bits, 0), \ + MAX1363_CHAN_U(3, _s3, 3, bits, 0), \ + MAX1363_CHAN_U(4, _s4, 4, bits, 0), \ + MAX1363_CHAN_U(5, _s5, 5, bits, 0), \ + MAX1363_CHAN_U(6, _s6, 6, bits, 0), \ + MAX1363_CHAN_U(7, _s7, 7, bits, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 8, bits, 0), \ + MAX1363_CHAN_B(2, 3, d2m3, 9, bits, 0), \ + MAX1363_CHAN_B(4, 5, d4m5, 10, bits, 0), \ + MAX1363_CHAN_B(6, 7, d6m7, 11, bits, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 12, bits, 0), \ + MAX1363_CHAN_B(3, 2, d3m2, 13, bits, 0), \ + MAX1363_CHAN_B(5, 4, d5m4, 14, bits, 0), \ + MAX1363_CHAN_B(7, 6, d7m6, 15, bits, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(16) \ +} +static const struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8); +static const struct iio_chan_spec max11608_channels[] = MAX1363_8X_CHANS(10); +static const struct iio_chan_spec max11614_channels[] = MAX1363_8X_CHANS(12); + +static const enum max1363_modes max11644_mode_list[] = { + _s0, _s1, s0to1, d0m1, d1m0, +}; + +#define MAX1363_2X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 2, bits, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 3, bits, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4) \ + } + +static const struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10); +static const struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12); + +enum { max1361, + max1362, + max1363, + max1364, + max1036, + max1037, + max1038, + max1039, + max1136, + max1137, + max1138, + max1139, + max1236, + max1237, + max1238, + max1239, + max11600, + max11601, + max11602, + max11603, + max11604, + max11605, + max11606, + max11607, + max11608, + max11609, + max11610, + max11611, + max11612, + max11613, + max11614, + max11615, + max11616, + max11617, + max11644, + max11645, + max11646, + max11647 +}; + +static const int max1363_monitor_speeds[] = { 133000, 665000, 33300, 16600, + 8300, 4200, 2000, 1000 }; + +static ssize_t max1363_monitor_show_freq(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max1363_state *st = iio_priv(dev_to_iio_dev(dev)); + return sprintf(buf, "%d\n", max1363_monitor_speeds[st->monitor_speed]); +} + +static ssize_t max1363_monitor_store_freq(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max1363_state *st = iio_priv(indio_dev); + int i, ret; + unsigned long val; + bool found = false; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(max1363_monitor_speeds); i++) + if (val == max1363_monitor_speeds[i]) { + found = true; + break; + } + if (!found) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + st->monitor_speed = i; + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, + max1363_monitor_show_freq, + max1363_monitor_store_freq); + +static IIO_CONST_ATTR(sampling_frequency_available, + "133000 665000 33300 16600 8300 4200 2000 1000"); + +static int max1363_read_thresh(struct iio_dev *indio_dev, + u64 event_code, + int *val) +{ + struct max1363_state *st = iio_priv(indio_dev); + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) + *val = st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)]; + else + *val = st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)]; + return 0; +} + +static int max1363_write_thresh(struct iio_dev *indio_dev, + u64 event_code, + int val) +{ + struct max1363_state *st = iio_priv(indio_dev); + /* make it handle signed correctly as well */ + switch (st->chip_info->bits) { + case 10: + if (val > 0x3FF) + return -EINVAL; + break; + case 12: + if (val > 0xFFF) + return -EINVAL; + break; + } + + switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { + case IIO_EV_DIR_FALLING: + st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val; + break; + case IIO_EV_DIR_RISING: + st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val; + break; + } + + return 0; +} + +static const u64 max1363_event_codes[] = { + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), +}; + +static irqreturn_t max1363_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct max1363_state *st = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(); + unsigned long mask, loc; + u8 rx; + u8 tx[2] = { st->setupbyte, + MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0 }; + + i2c_master_recv(st->client, &rx, 1); + mask = rx; + for_each_set_bit(loc, &mask, 8) + iio_push_event(indio_dev, max1363_event_codes[loc], timestamp); + i2c_master_send(st->client, tx, 2); + + return IRQ_HANDLED; +} + +static int max1363_read_event_config(struct iio_dev *indio_dev, + u64 event_code) +{ + struct max1363_state *st = iio_priv(indio_dev); + int val; + int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code); + + mutex_lock(&indio_dev->mlock); + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) + val = (1 << number) & st->mask_low; + else + val = (1 << number) & st->mask_high; + mutex_unlock(&indio_dev->mlock); + + return val; +} + +static int max1363_monitor_mode_update(struct max1363_state *st, int enabled) +{ + u8 *tx_buf; + int ret, i = 3, j; + unsigned long numelements; + int len; + const long *modemask; + + if (!enabled) { + /* transition to buffered capture is not currently supported */ + st->setupbyte &= ~MAX1363_SETUP_MONITOR_SETUP; + st->configbyte &= ~MAX1363_SCAN_MASK; + st->monitor_on = false; + return max1363_write_basic_config(st->client, + st->setupbyte, + st->configbyte); + } + + /* Ensure we are in the relevant mode */ + st->setupbyte |= MAX1363_SETUP_MONITOR_SETUP; + st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK + | MAX1363_SCAN_MASK + | MAX1363_SE_DE_MASK); + st->configbyte |= MAX1363_CONFIG_SCAN_MONITOR_MODE; + if ((st->mask_low | st->mask_high) & 0x0F) { + st->configbyte |= max1363_mode_table[s0to3].conf; + modemask = max1363_mode_table[s0to3].modemask; + } else if ((st->mask_low | st->mask_high) & 0x30) { + st->configbyte |= max1363_mode_table[d0m1to2m3].conf; + modemask = max1363_mode_table[d0m1to2m3].modemask; + } else { + st->configbyte |= max1363_mode_table[d1m0to3m2].conf; + modemask = max1363_mode_table[d1m0to3m2].modemask; + } + numelements = bitmap_weight(modemask, MAX1363_MAX_CHANNELS); + len = 3 * numelements + 3; + tx_buf = kmalloc(len, GFP_KERNEL); + if (!tx_buf) { + ret = -ENOMEM; + goto error_ret; + } + tx_buf[0] = st->configbyte; + tx_buf[1] = st->setupbyte; + tx_buf[2] = (st->monitor_speed << 1); + + /* + * So we need to do yet another bit of nefarious scan mode + * setup to match what we need. + */ + for (j = 0; j < 8; j++) + if (test_bit(j, modemask)) { + /* Establish the mode is in the scan */ + if (st->mask_low & (1 << j)) { + tx_buf[i] = (st->thresh_low[j] >> 4) & 0xFF; + tx_buf[i + 1] = (st->thresh_low[j] << 4) & 0xF0; + } else if (j < 4) { + tx_buf[i] = 0; + tx_buf[i + 1] = 0; + } else { + tx_buf[i] = 0x80; + tx_buf[i + 1] = 0; + } + if (st->mask_high & (1 << j)) { + tx_buf[i + 1] |= + (st->thresh_high[j] >> 8) & 0x0F; + tx_buf[i + 2] = st->thresh_high[j] & 0xFF; + } else if (j < 4) { + tx_buf[i + 1] |= 0x0F; + tx_buf[i + 2] = 0xFF; + } else { + tx_buf[i + 1] |= 0x07; + tx_buf[i + 2] = 0xFF; + } + i += 3; + } + + + ret = i2c_master_send(st->client, tx_buf, len); + if (ret < 0) + goto error_ret; + if (ret != len) { + ret = -EIO; + goto error_ret; + } + + /* + * Now that we hopefully have sensible thresholds in place it is + * time to turn the interrupts on. + * It is unclear from the data sheet if this should be necessary + * (i.e. whether monitor mode setup is atomic) but it appears to + * be in practice. + */ + tx_buf[0] = st->setupbyte; + tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0; + ret = i2c_master_send(st->client, tx_buf, 2); + if (ret < 0) + goto error_ret; + if (ret != 2) { + ret = -EIO; + goto error_ret; + } + ret = 0; + st->monitor_on = true; +error_ret: + + kfree(tx_buf); + + return ret; +} + +/* + * To keep this manageable we always use one of 3 scan modes. + * Scan 0...3, 0-1,2-3 and 1-0,3-2 + */ + +static inline int __max1363_check_event_mask(int thismask, int checkmask) +{ + int ret = 0; + /* Is it unipolar */ + if (thismask < 4) { + if (checkmask & ~0x0F) { + ret = -EBUSY; + goto error_ret; + } + } else if (thismask < 6) { + if (checkmask & ~0x30) { + ret = -EBUSY; + goto error_ret; + } + } else if (checkmask & ~0xC0) + ret = -EBUSY; +error_ret: + return ret; +} + +static int max1363_write_event_config(struct iio_dev *indio_dev, + u64 event_code, + int state) +{ + int ret = 0; + struct max1363_state *st = iio_priv(indio_dev); + u16 unifiedmask; + int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code); + + mutex_lock(&indio_dev->mlock); + unifiedmask = st->mask_low | st->mask_high; + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) { + + if (state == 0) + st->mask_low &= ~(1 << number); + else { + ret = __max1363_check_event_mask((1 << number), + unifiedmask); + if (ret) + goto error_ret; + st->mask_low |= (1 << number); + } + } else { + if (state == 0) + st->mask_high &= ~(1 << number); + else { + ret = __max1363_check_event_mask((1 << number), + unifiedmask); + if (ret) + goto error_ret; + st->mask_high |= (1 << number); + } + } + + max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low)); +error_ret: + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +/* + * As with scan_elements, only certain sets of these can + * be combined. + */ +static struct attribute *max1363_event_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static struct attribute_group max1363_event_attribute_group = { + .attrs = max1363_event_attributes, + .name = "events", +}; + +static int max1363_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct max1363_state *st = iio_priv(indio_dev); + + /* + * Need to figure out the current mode based upon the requested + * scan mask in iio_dev + */ + st->current_mode = max1363_match_mode(scan_mask, st->chip_info); + if (!st->current_mode) + return -EINVAL; + max1363_set_scan_mode(st); + return 0; +} + +static const struct iio_info max1238_info = { + .read_raw = &max1363_read_raw, + .driver_module = THIS_MODULE, + .update_scan_mode = &max1363_update_scan_mode, +}; + +static const struct iio_info max1363_info = { + .read_event_value = &max1363_read_thresh, + .write_event_value = &max1363_write_thresh, + .read_event_config = &max1363_read_event_config, + .write_event_config = &max1363_write_event_config, + .read_raw = &max1363_read_raw, + .update_scan_mode = &max1363_update_scan_mode, + .driver_module = THIS_MODULE, + .event_attrs = &max1363_event_attribute_group, +}; + +/* max1363 and max1368 tested - rest from data sheet */ +static const struct max1363_chip_info max1363_chip_info_tbl[] = { + [max1361] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1361_channels, + .num_channels = ARRAY_SIZE(max1361_channels), + .info = &max1363_info, + }, + [max1362] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1361_channels, + .num_channels = ARRAY_SIZE(max1361_channels), + .info = &max1363_info, + }, + [max1363] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + .info = &max1363_info, + }, + [max1364] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + .info = &max1363_info, + }, + [max1036] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max1037] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max1038] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max1039] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max1136] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max1137] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max1138] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max1139] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max1236] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1236_channels, + .num_channels = ARRAY_SIZE(max1236_channels), + }, + [max1237] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1236_channels, + .num_channels = ARRAY_SIZE(max1236_channels), + }, + [max1238] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max1239] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11600] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max11601] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max11602] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11602_channels, + .num_channels = ARRAY_SIZE(max11602_channels), + }, + [max11603] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11602_channels, + .num_channels = ARRAY_SIZE(max11602_channels), + }, + [max11604] = { + .bits = 8, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11605] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11606] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max11607] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max11608] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11608_channels, + .num_channels = ARRAY_SIZE(max11608_channels), + }, + [max11609] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11608_channels, + .num_channels = ARRAY_SIZE(max11608_channels), + }, + [max11610] = { + .bits = 10, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11611] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11612] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + }, + [max11613] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + }, + [max11614] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11614_channels, + .num_channels = ARRAY_SIZE(max11614_channels), + }, + [max11615] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11614_channels, + .num_channels = ARRAY_SIZE(max11614_channels), + }, + [max11616] = { + .bits = 12, + .int_vref_mv = 4098, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11617] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11644] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11644_channels, + .num_channels = ARRAY_SIZE(max11644_channels), + }, + [max11645] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11644_channels, + .num_channels = ARRAY_SIZE(max11644_channels), + }, + [max11646] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11646_channels, + .num_channels = ARRAY_SIZE(max11646_channels), + }, + [max11647] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11646_channels, + .num_channels = ARRAY_SIZE(max11646_channels), + }, +}; + +static int max1363_initial_setup(struct max1363_state *st) +{ + st->setupbyte = MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD + | MAX1363_SETUP_POWER_UP_INT_REF + | MAX1363_SETUP_INT_CLOCK + | MAX1363_SETUP_UNIPOLAR + | MAX1363_SETUP_NORESET; + + /* Set scan mode writes the config anyway so wait until then*/ + st->setupbyte = MAX1363_SETUP_BYTE(st->setupbyte); + st->current_mode = &max1363_mode_table[st->chip_info->default_mode]; + st->configbyte = MAX1363_CONFIG_BYTE(st->configbyte); + + return max1363_set_scan_mode(st); +} + +static int __devinit max1363_alloc_scan_masks(struct iio_dev *indio_dev) +{ + struct max1363_state *st = iio_priv(indio_dev); + unsigned long *masks; + int i; + + masks = kzalloc(BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*sizeof(long)* + (st->chip_info->num_modes + 1), GFP_KERNEL); + if (!masks) + return -ENOMEM; + + for (i = 0; i < st->chip_info->num_modes; i++) + bitmap_copy(masks + BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*i, + max1363_mode_table[st->chip_info->mode_list[i]] + .modemask, MAX1363_MAX_CHANNELS); + + indio_dev->available_scan_masks = masks; + + return 0; +} + + +static irqreturn_t max1363_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct max1363_state *st = iio_priv(indio_dev); + s64 time_ns; + __u8 *rxbuf; + int b_sent; + size_t d_size; + unsigned long numvals = bitmap_weight(st->current_mode->modemask, + MAX1363_MAX_CHANNELS); + + /* Ensure the timestamp is 8 byte aligned */ + if (st->chip_info->bits != 8) + d_size = numvals*2; + else + d_size = numvals; + if (indio_dev->scan_timestamp) { + d_size += sizeof(s64); + if (d_size % sizeof(s64)) + d_size += sizeof(s64) - (d_size % sizeof(s64)); + } + /* Monitor mode prevents reading. Whilst not currently implemented + * might as well have this test in here in the meantime as it does + * no harm. + */ + if (numvals == 0) + goto done; + + rxbuf = kmalloc(d_size, GFP_KERNEL); + if (rxbuf == NULL) + goto done; + if (st->chip_info->bits != 8) + b_sent = i2c_master_recv(st->client, rxbuf, numvals*2); + else + b_sent = i2c_master_recv(st->client, rxbuf, numvals); + if (b_sent < 0) + goto done_free; + + time_ns = iio_get_time_ns(); + + if (indio_dev->scan_timestamp) + memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); + iio_push_to_buffers(indio_dev, rxbuf); + +done_free: + kfree(rxbuf); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_buffer_setup_ops max1363_buffered_setup_ops = { + .postenable = &iio_triggered_buffer_postenable, + .preenable = &iio_sw_buffer_preenable, + .predisable = &iio_triggered_buffer_predisable, +}; + +static int max1363_register_buffered_funcs_and_init(struct iio_dev *indio_dev) +{ + struct max1363_state *st = iio_priv(indio_dev); + int ret = 0; + + indio_dev->buffer = iio_kfifo_allocate(indio_dev); + if (!indio_dev->buffer) { + ret = -ENOMEM; + goto error_ret; + } + indio_dev->pollfunc = iio_alloc_pollfunc(NULL, + &max1363_trigger_handler, + IRQF_ONESHOT, + indio_dev, + "%s_consumer%d", + st->client->name, + indio_dev->id); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_deallocate_sw_rb; + } + /* Buffer functions - here trigger setup related */ + indio_dev->setup_ops = &max1363_buffered_setup_ops; + + /* Flag that polled buffering is possible */ + indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + + return 0; + +error_deallocate_sw_rb: + iio_kfifo_free(indio_dev->buffer); +error_ret: + return ret; +} + +static void max1363_buffer_cleanup(struct iio_dev *indio_dev) +{ + /* ensure that the trigger has been detached */ + iio_dealloc_pollfunc(indio_dev->pollfunc); + iio_kfifo_free(indio_dev->buffer); +} + +static int __devinit max1363_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct max1363_state *st; + struct iio_dev *indio_dev; + + indio_dev = iio_device_alloc(sizeof(struct max1363_state)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_out; + } + + ret = iio_map_array_register(indio_dev, client->dev.platform_data); + if (ret < 0) + goto error_free_device; + + st = iio_priv(indio_dev); + + st->reg = regulator_get(&client->dev, "vcc"); + if (IS_ERR(st->reg)) { + ret = PTR_ERR(st->reg); + goto error_unregister_map; + } + + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + /* this is only used for device removal purposes */ + i2c_set_clientdata(client, indio_dev); + + st->chip_info = &max1363_chip_info_tbl[id->driver_data]; + st->client = client; + + ret = max1363_alloc_scan_masks(indio_dev); + if (ret) + goto error_disable_reg; + + /* Estabilish that the iio_dev is a child of the i2c device */ + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = st->chip_info->info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + ret = max1363_initial_setup(st); + if (ret < 0) + goto error_free_available_scan_masks; + + ret = max1363_register_buffered_funcs_and_init(indio_dev); + if (ret) + goto error_free_available_scan_masks; + + ret = iio_buffer_register(indio_dev, + st->chip_info->channels, + st->chip_info->num_channels); + if (ret) + goto error_cleanup_buffer; + + if (client->irq) { + ret = request_threaded_irq(st->client->irq, + NULL, + &max1363_event_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "max1363_event", + indio_dev); + + if (ret) + goto error_uninit_buffer; + } + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto error_free_irq; + + return 0; +error_free_irq: + free_irq(st->client->irq, indio_dev); +error_uninit_buffer: + iio_buffer_unregister(indio_dev); +error_cleanup_buffer: + max1363_buffer_cleanup(indio_dev); +error_free_available_scan_masks: + kfree(indio_dev->available_scan_masks); +error_unregister_map: + iio_map_array_unregister(indio_dev, client->dev.platform_data); +error_disable_reg: + regulator_disable(st->reg); +error_put_reg: + regulator_put(st->reg); +error_free_device: + iio_device_free(indio_dev); +error_out: + return ret; +} + +static int __devexit max1363_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct max1363_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (client->irq) + free_irq(st->client->irq, indio_dev); + iio_buffer_unregister(indio_dev); + max1363_buffer_cleanup(indio_dev); + kfree(indio_dev->available_scan_masks); + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_map_array_unregister(indio_dev, client->dev.platform_data); + iio_device_free(indio_dev); + + return 0; +} + +static const struct i2c_device_id max1363_id[] = { + { "max1361", max1361 }, + { "max1362", max1362 }, + { "max1363", max1363 }, + { "max1364", max1364 }, + { "max1036", max1036 }, + { "max1037", max1037 }, + { "max1038", max1038 }, + { "max1039", max1039 }, + { "max1136", max1136 }, + { "max1137", max1137 }, + { "max1138", max1138 }, + { "max1139", max1139 }, + { "max1236", max1236 }, + { "max1237", max1237 }, + { "max1238", max1238 }, + { "max1239", max1239 }, + { "max11600", max11600 }, + { "max11601", max11601 }, + { "max11602", max11602 }, + { "max11603", max11603 }, + { "max11604", max11604 }, + { "max11605", max11605 }, + { "max11606", max11606 }, + { "max11607", max11607 }, + { "max11608", max11608 }, + { "max11609", max11609 }, + { "max11610", max11610 }, + { "max11611", max11611 }, + { "max11612", max11612 }, + { "max11613", max11613 }, + { "max11614", max11614 }, + { "max11615", max11615 }, + { "max11616", max11616 }, + { "max11617", max11617 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, max1363_id); + +static struct i2c_driver max1363_driver = { + .driver = { + .name = "max1363", + }, + .probe = max1363_probe, + .remove = __devexit_p(max1363_remove), + .id_table = max1363_id, +}; +module_i2c_driver(max1363_driver); + +MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); +MODULE_DESCRIPTION("Maxim 1363 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c new file mode 100644 index 000000000000..f4a46dd8f43b --- /dev/null +++ b/drivers/iio/adc/ti-adc081c.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * + * 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/err.h> +#include <linux/i2c.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/regulator/consumer.h> + +struct adc081c { + struct i2c_client *i2c; + struct regulator *ref; +}; + +#define REG_CONV_RES 0x00 + +static int adc081c_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct adc081c *adc = iio_priv(iio); + int err; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES); + if (err < 0) + return err; + + *value = (err >> 4) & 0xff; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + err = regulator_get_voltage(adc->ref); + if (err < 0) + return err; + + *value = err / 1000; + *shift = 8; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + break; + } + + return -EINVAL; +} + +static const struct iio_chan_spec adc081c_channel = { + .type = IIO_VOLTAGE, + .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_RAW_SEPARATE_BIT, +}; + +static const struct iio_info adc081c_info = { + .read_raw = adc081c_read_raw, + .driver_module = THIS_MODULE, +}; + +static int adc081c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *iio; + struct adc081c *adc; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + iio = iio_device_alloc(sizeof(*adc)); + if (!iio) + return -ENOMEM; + + adc = iio_priv(iio); + adc->i2c = client; + + adc->ref = regulator_get(&client->dev, "vref"); + if (IS_ERR(adc->ref)) { + err = PTR_ERR(adc->ref); + goto iio_free; + } + + err = regulator_enable(adc->ref); + if (err < 0) + goto regulator_put; + + iio->dev.parent = &client->dev; + iio->name = dev_name(&client->dev); + iio->modes = INDIO_DIRECT_MODE; + iio->info = &adc081c_info; + + iio->channels = &adc081c_channel; + iio->num_channels = 1; + + err = iio_device_register(iio); + if (err < 0) + goto regulator_disable; + + i2c_set_clientdata(client, iio); + + return 0; + +regulator_disable: + regulator_disable(adc->ref); +regulator_put: + regulator_put(adc->ref); +iio_free: + iio_device_free(iio); + + return err; +} + +static int adc081c_remove(struct i2c_client *client) +{ + struct iio_dev *iio = i2c_get_clientdata(client); + struct adc081c *adc = iio_priv(iio); + + iio_device_unregister(iio); + regulator_disable(adc->ref); + regulator_put(adc->ref); + iio_device_free(iio); + + return 0; +} + +static const struct i2c_device_id adc081c_id[] = { + { "adc081c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adc081c_id); + +#ifdef CONFIG_OF +static const struct of_device_id adc081c_of_match[] = { + { .compatible = "ti,adc081c" }, + { } +}; +MODULE_DEVICE_TABLE(of, adc081c_of_match); +#endif + +static struct i2c_driver adc081c_driver = { + .driver = { + .name = "adc081c", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(adc081c_of_match), + }, + .probe = adc081c_probe, + .remove = adc081c_remove, + .id_table = adc081c_id, +}; +module_i2c_driver(adc081c_driver); + +MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); +MODULE_DESCRIPTION("Texas Instruments ADC081C021/027 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c new file mode 100644 index 000000000000..4d40e24f3721 --- /dev/null +++ b/drivers/iio/buffer_cb.c @@ -0,0 +1,113 @@ +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/iio/buffer.h> +#include <linux/iio/consumer.h> + +struct iio_cb_buffer { + struct iio_buffer buffer; + int (*cb)(u8 *data, void *private); + void *private; + struct iio_channel *channels; +}; + +static int iio_buffer_cb_store_to(struct iio_buffer *buffer, u8 *data) +{ + struct iio_cb_buffer *cb_buff = container_of(buffer, + struct iio_cb_buffer, + buffer); + + return cb_buff->cb(data, cb_buff->private); +} + +static struct iio_buffer_access_funcs iio_cb_access = { + .store_to = &iio_buffer_cb_store_to, +}; + +struct iio_cb_buffer *iio_channel_get_all_cb(const char *name, + int (*cb)(u8 *data, + void *private), + void *private) +{ + int ret; + struct iio_cb_buffer *cb_buff; + struct iio_dev *indio_dev; + struct iio_channel *chan; + + cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL); + if (cb_buff == NULL) { + ret = -ENOMEM; + goto error_ret; + } + + cb_buff->private = private; + cb_buff->cb = cb; + cb_buff->buffer.access = &iio_cb_access; + INIT_LIST_HEAD(&cb_buff->buffer.demux_list); + + cb_buff->channels = iio_channel_get_all(name); + if (IS_ERR(cb_buff->channels)) { + ret = PTR_ERR(cb_buff->channels); + goto error_free_cb_buff; + } + + indio_dev = cb_buff->channels[0].indio_dev; + cb_buff->buffer.scan_mask + = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long), + GFP_KERNEL); + if (cb_buff->buffer.scan_mask == NULL) { + ret = -ENOMEM; + goto error_release_channels; + } + chan = &cb_buff->channels[0]; + while (chan->indio_dev) { + if (chan->indio_dev != indio_dev) { + ret = -EINVAL; + goto error_release_channels; + } + set_bit(chan->channel->scan_index, + cb_buff->buffer.scan_mask); + chan++; + } + + return cb_buff; + +error_release_channels: + iio_channel_release_all(cb_buff->channels); +error_free_cb_buff: + kfree(cb_buff); +error_ret: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(iio_channel_get_all_cb); + +int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff) +{ + return iio_update_buffers(cb_buff->channels[0].indio_dev, + &cb_buff->buffer, + NULL); +} +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb); + +void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff) +{ + iio_update_buffers(cb_buff->channels[0].indio_dev, + NULL, + &cb_buff->buffer); +} +EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb); + +void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff) +{ + iio_channel_release_all(cb_buff->channels); + kfree(cb_buff); +} +EXPORT_SYMBOL_GPL(iio_channel_release_all_cb); + +struct iio_channel +*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer) +{ + return cb_buffer->channels; +} +EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels); diff --git a/drivers/iio/common/hid-sensors/Kconfig b/drivers/iio/common/hid-sensors/Kconfig index 8e63d81d652a..ae10778da7aa 100644 --- a/drivers/iio/common/hid-sensors/Kconfig +++ b/drivers/iio/common/hid-sensors/Kconfig @@ -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 diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index d4b790d18efb..d60198a6ca29 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -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, diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index b1c0ee5294ca..f4a6f0838327 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -67,6 +67,16 @@ config AD5446 To compile this driver as a module, choose M here: the module will be called ad5446. +config AD5449 + tristate "Analog Device AD5449 and similar DACs driver" + depends on SPI_MASTER + help + Say yes here to build support for Analog Devices AD5415, AD5426, AD5429, + AD5432, AD5439, AD5443, AD5449 Digital to Analog Converters. + + To compile this driver as a module, choose M here: the + module will be called ad5449. + config AD5504 tristate "Analog Devices AD5504/AD5501 DAC SPI driver" depends on SPI @@ -122,7 +132,7 @@ config AD5686 config MAX517 tristate "Maxim MAX517/518/519 DAC driver" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for the Maxim chips MAX517, MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs). diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index c0d333b23ba3..5b528ebb3343 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o obj-$(CONFIG_AD5064) += ad5064.o obj-$(CONFIG_AD5504) += ad5504.o obj-$(CONFIG_AD5446) += ad5446.o +obj-$(CONFIG_AD5449) += ad5449.o obj-$(CONFIG_AD5755) += ad5755.o obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c new file mode 100644 index 000000000000..0ee6f8eeba8d --- /dev/null +++ b/drivers/iio/dac/ad5449.c @@ -0,0 +1,376 @@ +/* + * AD5415, AD5426, AD5429, AD5432, AD5439, AD5443, AD5449 Digital to Analog + * Converter driver. + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2. + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/regulator/consumer.h> +#include <asm/unaligned.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include <linux/platform_data/ad5449.h> + +#define AD5449_MAX_CHANNELS 2 +#define AD5449_MAX_VREFS 2 + +#define AD5449_CMD_NOOP 0x0 +#define AD5449_CMD_LOAD_AND_UPDATE(x) (0x1 + (x) * 3) +#define AD5449_CMD_READ(x) (0x2 + (x) * 3) +#define AD5449_CMD_LOAD(x) (0x3 + (x) * 3) +#define AD5449_CMD_CTRL 13 + +#define AD5449_CTRL_SDO_OFFSET 10 +#define AD5449_CTRL_DAISY_CHAIN BIT(9) +#define AD5449_CTRL_HCLR_TO_MIDSCALE BIT(8) +#define AD5449_CTRL_SAMPLE_RISING BIT(7) + +/** + * struct ad5449_chip_info - chip specific information + * @channels: Channel specification + * @num_channels: Number of channels + * @has_ctrl: Chip has a control register + */ +struct ad5449_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + bool has_ctrl; +}; + +/** + * struct ad5449 - driver instance specific data + * @spi: the SPI device for this driver instance + * @chip_info: chip model specific constants, available modes etc + * @vref_reg: vref supply regulators + * @has_sdo: whether the SDO line is connected + * @dac_cache: Cache for the DAC values + * @data: spi transfer buffers + */ +struct ad5449 { + struct spi_device *spi; + const struct ad5449_chip_info *chip_info; + struct regulator_bulk_data vref_reg[AD5449_MAX_VREFS]; + + bool has_sdo; + uint16_t dac_cache[AD5449_MAX_CHANNELS]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be16 data[2] ____cacheline_aligned; +}; + +enum ad5449_type { + ID_AD5426, + ID_AD5429, + ID_AD5432, + ID_AD5439, + ID_AD5443, + ID_AD5449, +}; + +static int ad5449_write(struct iio_dev *indio_dev, unsigned int addr, + unsigned int val) +{ + struct ad5449 *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&indio_dev->mlock); + st->data[0] = cpu_to_be16((addr << 12) | val); + ret = spi_write(st->spi, st->data, 2); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr, + unsigned int *val) +{ + struct ad5449 *st = iio_priv(indio_dev); + int ret; + struct spi_message msg; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0], + .len = 2, + .cs_change = 1, + }, { + .tx_buf = &st->data[1], + .rx_buf = &st->data[1], + .len = 2, + }, + }; + + spi_message_init(&msg); + spi_message_add_tail(&t[0], &msg); + spi_message_add_tail(&t[1], &msg); + + mutex_lock(&indio_dev->mlock); + st->data[0] = cpu_to_be16(addr << 12); + st->data[1] = cpu_to_be16(AD5449_CMD_NOOP); + + ret = spi_sync(st->spi, &msg); + if (ret < 0) + goto out_unlock; + + *val = be16_to_cpu(st->data[1]); + +out_unlock: + mutex_unlock(&indio_dev->mlock); + return ret; +} + +static int ad5449_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct ad5449 *st = iio_priv(indio_dev); + struct regulator_bulk_data *reg; + int scale_uv; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (st->has_sdo) { + ret = ad5449_read(indio_dev, + AD5449_CMD_READ(chan->address), val); + if (ret) + return ret; + *val &= 0xfff; + } else { + *val = st->dac_cache[chan->address]; + } + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + reg = &st->vref_reg[chan->channel]; + scale_uv = regulator_get_voltage(reg->consumer); + if (scale_uv < 0) + return scale_uv; + + *val = scale_uv / 1000; + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + break; + } + + return -EINVAL; +} + +static int ad5449_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long info) +{ + struct ad5449 *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (val < 0 || val >= (1 << chan->scan_type.realbits)) + return -EINVAL; + + ret = ad5449_write(indio_dev, + AD5449_CMD_LOAD_AND_UPDATE(chan->address), + val << chan->scan_type.shift); + if (ret == 0) + st->dac_cache[chan->address] = val; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info ad5449_info = { + .read_raw = ad5449_read_raw, + .write_raw = ad5449_write_raw, + .driver_module = THIS_MODULE, +}; + +#define AD5449_CHANNEL(chan, bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .address = (chan), \ + .scan_type = IIO_ST('u', (bits), 16, 12 - (bits)), \ +} + +#define DECLARE_AD5449_CHANNELS(name, bits) \ +const struct iio_chan_spec name[] = { \ + AD5449_CHANNEL(0, bits), \ + AD5449_CHANNEL(1, bits), \ +} + +static DECLARE_AD5449_CHANNELS(ad5429_channels, 8); +static DECLARE_AD5449_CHANNELS(ad5439_channels, 10); +static DECLARE_AD5449_CHANNELS(ad5449_channels, 12); + +static const struct ad5449_chip_info ad5449_chip_info[] = { + [ID_AD5426] = { + .channels = ad5429_channels, + .num_channels = 1, + .has_ctrl = false, + }, + [ID_AD5429] = { + .channels = ad5429_channels, + .num_channels = 2, + .has_ctrl = true, + }, + [ID_AD5432] = { + .channels = ad5439_channels, + .num_channels = 1, + .has_ctrl = false, + }, + [ID_AD5439] = { + .channels = ad5439_channels, + .num_channels = 2, + .has_ctrl = true, + }, + [ID_AD5443] = { + .channels = ad5449_channels, + .num_channels = 1, + .has_ctrl = false, + }, + [ID_AD5449] = { + .channels = ad5449_channels, + .num_channels = 2, + .has_ctrl = true, + }, +}; + +static const char *ad5449_vref_name(struct ad5449 *st, int n) +{ + if (st->chip_info->num_channels == 1) + return "VREF"; + + if (n == 0) + return "VREFA"; + else + return "VREFB"; +} + +static int __devinit ad5449_spi_probe(struct spi_device *spi) +{ + struct ad5449_platform_data *pdata = spi->dev.platform_data; + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct ad5449 *st; + unsigned int i; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->chip_info = &ad5449_chip_info[id->driver_data]; + st->spi = spi; + + for (i = 0; i < st->chip_info->num_channels; ++i) + st->vref_reg[i].supply = ad5449_vref_name(st, i); + + ret = regulator_bulk_get(&spi->dev, st->chip_info->num_channels, + st->vref_reg); + if (ret) + goto error_free; + + ret = regulator_bulk_enable(st->chip_info->num_channels, st->vref_reg); + if (ret) + goto error_free_reg; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = id->name; + indio_dev->info = &ad5449_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + + if (st->chip_info->has_ctrl) { + unsigned int ctrl = 0x00; + if (pdata) { + if (pdata->hardware_clear_to_midscale) + ctrl |= AD5449_CTRL_HCLR_TO_MIDSCALE; + ctrl |= pdata->sdo_mode << AD5449_CTRL_SDO_OFFSET; + st->has_sdo = pdata->sdo_mode != AD5449_SDO_DISABLED; + } else { + st->has_sdo = true; + } + ad5449_write(indio_dev, AD5449_CMD_CTRL, ctrl); + } + + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg; + + return 0; + +error_disable_reg: + regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg); +error_free_reg: + regulator_bulk_free(st->chip_info->num_channels, st->vref_reg); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5449_spi_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5449 *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg); + regulator_bulk_free(st->chip_info->num_channels, st->vref_reg); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5449_spi_ids[] = { + { "ad5415", ID_AD5449 }, + { "ad5426", ID_AD5426 }, + { "ad5429", ID_AD5429 }, + { "ad5432", ID_AD5432 }, + { "ad5439", ID_AD5439 }, + { "ad5443", ID_AD5443 }, + { "ad5449", ID_AD5449 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5449_spi_ids); + +static struct spi_driver ad5449_spi_driver = { + .driver = { + .name = "ad5449", + .owner = THIS_MODULE, + }, + .probe = ad5449_spi_probe, + .remove = __devexit_p(ad5449_spi_remove), + .id_table = ad5449_spi_ids, +}; +module_spi_driver(ad5449_spi_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Analog Devices AD5449 and similar DACs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 6948d75e1036..bc92ff9309c2 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -188,7 +188,7 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, if (ret) return ret; - if (readin == true) + if (readin) st->pwr_down_mask |= (0x3 << (chan->channel * 2)); else st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 21e27e2fc68c..48ed1483ff27 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -3,6 +3,15 @@ # menu "Digital gyroscope sensors" +config ADIS16136 + tristate "Analog devices ADIS16136 and similar gyroscopes driver" + depends on SPI_MASTER + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER + help + Say yes here to build support for the Analog Devices ADIS16133, ADIS16135, + ADIS16136 gyroscope devices. + config HID_SENSOR_GYRO_3D depends on HID_SENSOR_HUB select IIO_BUFFER diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile index 8a895d9fcbce..702a058907e3 100644 --- a/drivers/iio/gyro/Makefile +++ b/drivers/iio/gyro/Makefile @@ -2,4 +2,5 @@ # Makefile for industrial I/O gyroscope sensor drivers # +obj-$(CONFIG_ADIS16136) += adis16136.o obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c new file mode 100644 index 000000000000..8cb0bcbfd609 --- /dev/null +++ b/drivers/iio/gyro/adis16136.c @@ -0,0 +1,580 @@ +/* + * 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/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); + unsigned int val; + int ret; + + ret = kstrtouint(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"); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 4c56ada51c39..4c8b158e40e1 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -197,21 +197,8 @@ static const struct iio_info gyro_3d_info = { /* Function to push data to buffer */ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { - struct iio_buffer *buffer = indio_dev->buffer; - int datum_sz; - dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); - if (!buffer) { - dev_err(&indio_dev->dev, "Buffer == NULL\n"); - return; - } - datum_sz = buffer->access->get_bytes_per_datum(buffer); - if (len > datum_sz) { - dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, - datum_sz); - return; - } - iio_push_to_buffer(buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ @@ -319,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; } diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig new file mode 100644 index 000000000000..3d79a40e916b --- /dev/null +++ b/drivers/iio/imu/Kconfig @@ -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. diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile new file mode 100644 index 000000000000..cfe57638f6f9 --- /dev/null +++ b/drivers/iio/imu/Makefile @@ -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 diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c new file mode 100644 index 000000000000..911255d41c1a --- /dev/null +++ b/drivers/iio/imu/adis.c @@ -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"); diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c new file mode 100644 index 000000000000..8c26a5f7cd5d --- /dev/null +++ b/drivers/iio/imu/adis16480.c @@ -0,0 +1,924 @@ +/* + * 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/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[7]; + 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 = scnprintf(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"); diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c new file mode 100644 index 000000000000..99d8e0b0dd34 --- /dev/null +++ b/drivers/iio/imu/adis_buffer.c @@ -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); diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c new file mode 100644 index 000000000000..5a24c9cac343 --- /dev/null +++ b/drivers/iio/imu/adis_trigger.c @@ -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); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index d4ad37455a67..aaadd32f9f0d 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -31,6 +31,18 @@ static const char * const iio_endian_prefix[] = { [IIO_LE] = "le", }; +static bool iio_buffer_is_active(struct iio_dev *indio_dev, + struct iio_buffer *buf) +{ + struct list_head *p; + + list_for_each(p, &indio_dev->buffer_list) + if (p == &buf->buffer_list) + return true; + + return false; +} + /** * iio_buffer_read_first_n_outer() - chrdev read for buffer access * @@ -134,7 +146,7 @@ static ssize_t iio_scan_el_store(struct device *dev, if (ret < 0) return ret; mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { + if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) { ret = -EBUSY; goto error_ret; } @@ -180,12 +192,11 @@ static ssize_t iio_scan_el_ts_store(struct device *dev, return ret; mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { + if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) { ret = -EBUSY; goto error_ret; } indio_dev->buffer->scan_timestamp = state; - indio_dev->scan_timestamp = state; error_ret: mutex_unlock(&indio_dev->mlock); @@ -371,12 +382,12 @@ ssize_t iio_buffer_write_length(struct device *dev, const char *buf, size_t len) { - int ret; - ulong val; struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_buffer *buffer = indio_dev->buffer; + unsigned int val; + int ret; - ret = strict_strtoul(buf, 10, &val); + ret = kstrtouint(buf, 10, &val); if (ret) return ret; @@ -385,7 +396,7 @@ ssize_t iio_buffer_write_length(struct device *dev, return len; mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { + if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) { ret = -EBUSY; } else { if (buffer->access->set_length) @@ -398,102 +409,14 @@ ssize_t iio_buffer_write_length(struct device *dev, } EXPORT_SYMBOL(iio_buffer_write_length); -ssize_t iio_buffer_store_enable(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - int ret; - bool requested_state, current_state; - int previous_mode; - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; - - mutex_lock(&indio_dev->mlock); - previous_mode = indio_dev->currentmode; - requested_state = !(buf[0] == '0'); - current_state = iio_buffer_enabled(indio_dev); - if (current_state == requested_state) { - printk(KERN_INFO "iio-buffer, current state requested again\n"); - goto done; - } - if (requested_state) { - if (indio_dev->setup_ops->preenable) { - ret = indio_dev->setup_ops->preenable(indio_dev); - if (ret) { - printk(KERN_ERR - "Buffer not started: " - "buffer preenable failed\n"); - goto error_ret; - } - } - if (buffer->access->request_update) { - ret = buffer->access->request_update(buffer); - if (ret) { - printk(KERN_INFO - "Buffer not started: " - "buffer parameter update failed\n"); - goto error_ret; - } - } - /* Definitely possible for devices to support both of these. */ - if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) { - if (!indio_dev->trig) { - printk(KERN_INFO - "Buffer not started: no trigger\n"); - ret = -EINVAL; - goto error_ret; - } - indio_dev->currentmode = INDIO_BUFFER_TRIGGERED; - } else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) - indio_dev->currentmode = INDIO_BUFFER_HARDWARE; - else { /* should never be reached */ - ret = -EINVAL; - goto error_ret; - } - - if (indio_dev->setup_ops->postenable) { - ret = indio_dev->setup_ops->postenable(indio_dev); - if (ret) { - printk(KERN_INFO - "Buffer not started: " - "postenable failed\n"); - indio_dev->currentmode = previous_mode; - if (indio_dev->setup_ops->postdisable) - indio_dev->setup_ops-> - postdisable(indio_dev); - goto error_ret; - } - } - } else { - if (indio_dev->setup_ops->predisable) { - ret = indio_dev->setup_ops->predisable(indio_dev); - if (ret) - goto error_ret; - } - indio_dev->currentmode = INDIO_DIRECT_MODE; - if (indio_dev->setup_ops->postdisable) { - ret = indio_dev->setup_ops->postdisable(indio_dev); - if (ret) - goto error_ret; - } - } -done: - mutex_unlock(&indio_dev->mlock); - return len; - -error_ret: - mutex_unlock(&indio_dev->mlock); - return ret; -} -EXPORT_SYMBOL(iio_buffer_store_enable); - ssize_t iio_buffer_show_enable(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); - return sprintf(buf, "%d\n", iio_buffer_enabled(indio_dev)); + return sprintf(buf, "%d\n", + iio_buffer_is_active(indio_dev, + indio_dev->buffer)); } EXPORT_SYMBOL(iio_buffer_show_enable); @@ -537,35 +460,220 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const long *mask, return bytes; } -int iio_sw_buffer_preenable(struct iio_dev *indio_dev) +int iio_update_buffers(struct iio_dev *indio_dev, + struct iio_buffer *insert_buffer, + struct iio_buffer *remove_buffer) { - struct iio_buffer *buffer = indio_dev->buffer; - dev_dbg(&indio_dev->dev, "%s\n", __func__); + int ret; + int success = 0; + struct iio_buffer *buffer; + unsigned long *compound_mask; + const unsigned long *old_mask; - /* How much space will the demuxed element take? */ - indio_dev->scan_bytes = - iio_compute_scan_bytes(indio_dev, buffer->scan_mask, - buffer->scan_timestamp); - buffer->access->set_bytes_per_datum(buffer, indio_dev->scan_bytes); + /* Wind down existing buffers - iff there are any */ + if (!list_empty(&indio_dev->buffer_list)) { + if (indio_dev->setup_ops->predisable) { + ret = indio_dev->setup_ops->predisable(indio_dev); + if (ret) + goto error_ret; + } + indio_dev->currentmode = INDIO_DIRECT_MODE; + if (indio_dev->setup_ops->postdisable) { + ret = indio_dev->setup_ops->postdisable(indio_dev); + if (ret) + goto error_ret; + } + } + /* Keep a copy of current setup to allow roll back */ + old_mask = indio_dev->active_scan_mask; + if (!indio_dev->available_scan_masks) + indio_dev->active_scan_mask = NULL; + + if (remove_buffer) + list_del(&remove_buffer->buffer_list); + if (insert_buffer) + list_add(&insert_buffer->buffer_list, &indio_dev->buffer_list); + + /* If no buffers in list, we are done */ + if (list_empty(&indio_dev->buffer_list)) { + indio_dev->currentmode = INDIO_DIRECT_MODE; + if (indio_dev->available_scan_masks == NULL) + kfree(old_mask); + return 0; + } /* What scan mask do we actually have ?*/ - if (indio_dev->available_scan_masks) + compound_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), + sizeof(long), GFP_KERNEL); + if (compound_mask == NULL) { + if (indio_dev->available_scan_masks == NULL) + kfree(old_mask); + return -ENOMEM; + } + indio_dev->scan_timestamp = 0; + + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) { + bitmap_or(compound_mask, compound_mask, buffer->scan_mask, + indio_dev->masklength); + indio_dev->scan_timestamp |= buffer->scan_timestamp; + } + if (indio_dev->available_scan_masks) { indio_dev->active_scan_mask = iio_scan_mask_match(indio_dev->available_scan_masks, indio_dev->masklength, - buffer->scan_mask); - else - indio_dev->active_scan_mask = buffer->scan_mask; - - if (indio_dev->active_scan_mask == NULL) - return -EINVAL; + compound_mask); + if (indio_dev->active_scan_mask == NULL) { + /* + * Roll back. + * Note can only occur when adding a buffer. + */ + list_del(&insert_buffer->buffer_list); + indio_dev->active_scan_mask = old_mask; + success = -EINVAL; + } + } else { + indio_dev->active_scan_mask = compound_mask; + } iio_update_demux(indio_dev); - if (indio_dev->info->update_scan_mode) - return indio_dev->info + /* Wind up again */ + if (indio_dev->setup_ops->preenable) { + ret = indio_dev->setup_ops->preenable(indio_dev); + if (ret) { + printk(KERN_ERR + "Buffer not started:" + "buffer preenable failed\n"); + goto error_remove_inserted; + } + } + indio_dev->scan_bytes = + iio_compute_scan_bytes(indio_dev, + indio_dev->active_scan_mask, + indio_dev->scan_timestamp); + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) + if (buffer->access->request_update) { + ret = buffer->access->request_update(buffer); + if (ret) { + printk(KERN_INFO + "Buffer not started:" + "buffer parameter update failed\n"); + goto error_run_postdisable; + } + } + if (indio_dev->info->update_scan_mode) { + ret = indio_dev->info ->update_scan_mode(indio_dev, indio_dev->active_scan_mask); + if (ret < 0) { + printk(KERN_INFO "update scan mode failed\n"); + goto error_run_postdisable; + } + } + /* Definitely possible for devices to support both of these.*/ + if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) { + if (!indio_dev->trig) { + printk(KERN_INFO "Buffer not started: no trigger\n"); + ret = -EINVAL; + /* Can only occur on first buffer */ + goto error_run_postdisable; + } + indio_dev->currentmode = INDIO_BUFFER_TRIGGERED; + } else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) { + indio_dev->currentmode = INDIO_BUFFER_HARDWARE; + } else { /* should never be reached */ + ret = -EINVAL; + goto error_run_postdisable; + } + + if (indio_dev->setup_ops->postenable) { + ret = indio_dev->setup_ops->postenable(indio_dev); + if (ret) { + printk(KERN_INFO + "Buffer not started: postenable failed\n"); + indio_dev->currentmode = INDIO_DIRECT_MODE; + if (indio_dev->setup_ops->postdisable) + indio_dev->setup_ops->postdisable(indio_dev); + goto error_disable_all_buffers; + } + } + + if (indio_dev->available_scan_masks) + kfree(compound_mask); + else + kfree(old_mask); + + return success; + +error_disable_all_buffers: + indio_dev->currentmode = INDIO_DIRECT_MODE; +error_run_postdisable: + if (indio_dev->setup_ops->postdisable) + indio_dev->setup_ops->postdisable(indio_dev); +error_remove_inserted: + + if (insert_buffer) + list_del(&insert_buffer->buffer_list); + indio_dev->active_scan_mask = old_mask; + kfree(compound_mask); +error_ret: + + return ret; +} +EXPORT_SYMBOL_GPL(iio_update_buffers); + +ssize_t iio_buffer_store_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + int ret; + bool requested_state; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_buffer *pbuf = indio_dev->buffer; + bool inlist; + + ret = strtobool(buf, &requested_state); + if (ret < 0) + return ret; + + mutex_lock(&indio_dev->mlock); + + /* Find out if it is in the list */ + inlist = iio_buffer_is_active(indio_dev, pbuf); + /* Already in desired state */ + if (inlist == requested_state) + goto done; + + if (requested_state) + ret = iio_update_buffers(indio_dev, + indio_dev->buffer, NULL); + else + ret = iio_update_buffers(indio_dev, + NULL, indio_dev->buffer); + + if (ret < 0) + goto done; +done: + mutex_unlock(&indio_dev->mlock); + return (ret < 0) ? ret : len; +} +EXPORT_SYMBOL(iio_buffer_store_enable); + +int iio_sw_buffer_preenable(struct iio_dev *indio_dev) +{ + struct iio_buffer *buffer; + unsigned bytes; + dev_dbg(&indio_dev->dev, "%s\n", __func__); + + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) + if (buffer->access->set_bytes_per_datum) { + bytes = iio_compute_scan_bytes(indio_dev, + buffer->scan_mask, + buffer->scan_timestamp); + + buffer->access->set_bytes_per_datum(buffer, bytes); + } return 0; } EXPORT_SYMBOL(iio_sw_buffer_preenable); @@ -599,7 +707,11 @@ static bool iio_validate_scan_mask(struct iio_dev *indio_dev, * iio_scan_mask_set() - set particular bit in the scan mask * @buffer: the buffer whose scan mask we are interested in * @bit: the bit to be set. - **/ + * + * Note that at this point we have no way of knowing what other + * buffers might request, hence this code only verifies that the + * individual buffers request is plausible. + */ int iio_scan_mask_set(struct iio_dev *indio_dev, struct iio_buffer *buffer, int bit) { @@ -682,13 +794,12 @@ static unsigned char *iio_demux(struct iio_buffer *buffer, return buffer->demux_bounce; } -int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data) +static int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data) { unsigned char *dataout = iio_demux(buffer, data); return buffer->access->store_to(buffer, dataout); } -EXPORT_SYMBOL_GPL(iio_push_to_buffer); static void iio_buffer_demux_free(struct iio_buffer *buffer) { @@ -699,10 +810,26 @@ static void iio_buffer_demux_free(struct iio_buffer *buffer) } } -int iio_update_demux(struct iio_dev *indio_dev) + +int iio_push_to_buffers(struct iio_dev *indio_dev, unsigned char *data) +{ + int ret; + struct iio_buffer *buf; + + list_for_each_entry(buf, &indio_dev->buffer_list, buffer_list) { + ret = iio_push_to_buffer(buf, data); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(iio_push_to_buffers); + +static int iio_buffer_update_demux(struct iio_dev *indio_dev, + struct iio_buffer *buffer) { const struct iio_chan_spec *ch; - struct iio_buffer *buffer = indio_dev->buffer; int ret, in_ind = -1, out_ind, length; unsigned in_loc = 0, out_loc = 0; struct iio_demux_table *p; @@ -787,4 +914,23 @@ error_clear_mux_table: return ret; } + +int iio_update_demux(struct iio_dev *indio_dev) +{ + struct iio_buffer *buffer; + int ret; + + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) { + ret = iio_buffer_update_demux(indio_dev, buffer); + if (ret < 0) + goto error_clear_mux_table; + } + return 0; + +error_clear_mux_table: + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) + iio_buffer_demux_free(buffer); + + return ret; +} EXPORT_SYMBOL_GPL(iio_update_demux); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 6eb24dbc081e..8848f16c547b 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -65,6 +65,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_CAPACITANCE] = "capacitance", [IIO_ALTVOLTAGE] = "altvoltage", [IIO_CCT] = "cct", + [IIO_PRESSURE] = "pressure", }; static const char * const iio_modifier_names[] = { @@ -397,11 +398,74 @@ static ssize_t iio_read_channel_info(struct device *dev, val2 = do_div(tmp, 1000000000LL); val = tmp; return sprintf(buf, "%d.%09u\n", val, val2); + case IIO_VAL_FRACTIONAL_LOG2: + tmp = (s64)val * 1000000000LL >> val2; + val2 = do_div(tmp, 1000000000LL); + val = tmp; + return sprintf(buf, "%d.%09u\n", val, val2); default: return 0; } } +/** + * 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, @@ -409,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) @@ -429,39 +493,9 @@ static ssize_t iio_write_channel_info(struct device *dev, return -EINVAL; } - if (buf[0] == '-') { - negative = true; - buf++; - } - - while (*buf) { - if ('0' <= *buf && *buf <= '9') { - if (integer_part) - integer = integer*10 + *buf - '0'; - else { - fract += fract_mult*(*buf - '0'); - if (fract_mult == 1) - break; - fract_mult /= 10; - } - } else if (*buf == '\n') { - if (*(buf + 1) == '\0') - break; - else - return -EINVAL; - } else if (*buf == '.') { - 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); @@ -851,6 +885,7 @@ struct iio_dev *iio_device_alloc(int sizeof_priv) return NULL; } dev_set_name(&dev->dev, "iio:device%d", dev->id); + INIT_LIST_HEAD(&dev->buffer_list); } return dev; diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index fa6543bf6731..261cae00557e 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -239,13 +239,13 @@ static ssize_t iio_ev_value_store(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - unsigned long val; + int val; int ret; if (!indio_dev->info->write_event_value) return -EINVAL; - ret = strict_strtoul(buf, 10, &val); + ret = kstrtoint(buf, 10, &val); if (ret) return ret; @@ -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) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index f2b78d4fe457..d55e98fb300e 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -78,7 +78,7 @@ int iio_map_array_unregister(struct iio_dev *indio_dev, found_it = true; break; } - if (found_it == false) { + if (!found_it) { ret = -ENODEV; goto error_ret; } @@ -203,6 +203,7 @@ struct iio_channel *iio_channel_get_all(const char *name) if (name && strcmp(name, c->map->consumer_dev_name) != 0) continue; chans[mapind].indio_dev = c->indio_dev; + chans[mapind].data = c->map->consumer_data; chans[mapind].channel = iio_chan_spec_from_name(chans[mapind].indio_dev, c->map->adc_channel_label); @@ -314,6 +315,9 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, *processed = div_s64(raw64 * (s64)scale_val * scale, scale_val2); break; + case IIO_VAL_FRACTIONAL_LOG2: + *processed = (raw64 * (s64)scale_val * scale) >> scale_val2; + break; default: return -EINVAL; } diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index 164b62b91a4b..36d210a06b28 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -164,7 +164,6 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct adjd_s311_data *data = iio_priv(indio_dev); - struct iio_buffer *buffer = indio_dev->buffer; s64 time_ns = iio_get_time_ns(); int len = 0; int i, j = 0; @@ -187,7 +186,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *(s64 *)((u8 *)data->buffer + ALIGN(len, sizeof(s64))) = time_ns; - iio_push_to_buffer(buffer, (u8 *)data->buffer); + iio_push_to_buffers(indio_dev, (u8 *)data->buffer); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 96e3691e42c4..23eeeef64e84 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -176,21 +176,8 @@ static const struct iio_info als_info = { /* Function to push data to buffer */ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { - struct iio_buffer *buffer = indio_dev->buffer; - int datum_sz; - dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); - if (!buffer) { - dev_err(&indio_dev->dev, "Buffer == NULL\n"); - return; - } - datum_sz = buffer->access->get_bytes_per_datum(buffer); - if (len > datum_sz) { - dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, - datum_sz); - return; - } - iio_push_to_buffer(buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ @@ -285,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; } diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index c4f0d274f577..8e75eb76ccd9 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -198,21 +198,8 @@ static const struct iio_info magn_3d_info = { /* Function to push data to buffer */ static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) { - struct iio_buffer *buffer = indio_dev->buffer; - int datum_sz; - dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); - if (!buffer) { - dev_err(&indio_dev->dev, "Buffer == NULL\n"); - return; - } - datum_sz = buffer->access->get_bytes_per_datum(buffer); - if (len > datum_sz) { - dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, - datum_sz); - return; - } - iio_push_to_buffer(buffer, (u8 *)data); + iio_push_to_buffers(indio_dev, (u8 *)data); } /* Callback handler to send event after all samples are received and captured */ @@ -320,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; } |