diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2011-08-29 08:47:46 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-08-29 08:47:46 -0700 |
commit | 6eafa4604cfa109a89524d35d93df11c37bd66b0 (patch) | |
tree | de0eddca052ed01318df559d7cd80211dd57a0fd /drivers/staging/iio/adc | |
parent | Linux 3.1-rc4 (diff) | |
parent | staging: fix rts5139 depends & build (diff) | |
download | linux-dev-6eafa4604cfa109a89524d35d93df11c37bd66b0.tar.xz linux-dev-6eafa4604cfa109a89524d35d93df11c37bd66b0.zip |
Merge 3.1-rc4 into staging-next
This resolves a conflict with:
drivers/staging/brcm80211/brcmsmac/types.h
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/iio/adc')
30 files changed, 2347 insertions, 135 deletions
diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index b39f2e1c1fe6..25566c624671 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -161,6 +161,20 @@ config AD7816 Say yes here to build support for Analog Devices AD7816/7/8 temperature sensors and ADC. +config AD7192 + tristate "Analog Devices AD7190 AD7192 AD7195 ADC driver" + depends on SPI + select IIO_RING_BUFFER + select IIO_SW_RING + select IIO_TRIGGER + help + Say yes here to build support for Analog Devices AD7190, + AD7192 or AD7195 SPI analog to digital convertors (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 ad7192. + config ADT75 tristate "Analog Devices ADT75 temperature sensor driver" depends on I2C @@ -182,6 +196,16 @@ config ADT7410 Say yes here to build support for Analog Devices ADT7410 temperature sensors. +config AD7280 + tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System" + depends on SPI + help + Say yes here to build support for Analog Devices AD7280A + Lithium Ion Battery Monitoring System. + + To compile this driver as a module, choose M here: the + module will be called ad7280a + config MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index f02035139979..990d3fada305 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -37,6 +37,8 @@ obj-$(CONFIG_AD7745) += ad7745.o obj-$(CONFIG_AD7780) += ad7780.o obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7816) += ad7816.o +obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_ADT75) += adt75.o obj-$(CONFIG_ADT7310) += adt7310.o obj-$(CONFIG_ADT7410) += adt7410.o +obj-$(CONFIG_AD7280) += ad7280a.o diff --git a/drivers/staging/iio/adc/ad7150.c b/drivers/staging/iio/adc/ad7150.c index 04017ef6688a..c01acbc7b34e 100644 --- a/drivers/staging/iio/adc/ad7150.c +++ b/drivers/staging/iio/adc/ad7150.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" @@ -617,6 +618,8 @@ static IIO_DEV_ATTR_POWERDOWN_TIMER(S_IRUGO | S_IWUSR, ad7150_store_powerdown_timer); static struct attribute *ad7150_attributes[] = { + &iio_dev_attr_available_conversion_modes.dev_attr.attr, + &iio_dev_attr_conversion_mode.dev_attr.attr, &iio_dev_attr_available_threshold_modes.dev_attr.attr, &iio_dev_attr_threshold_mode.dev_attr.attr, &iio_dev_attr_ch1_threshold.dev_attr.attr, @@ -652,14 +655,14 @@ static irqreturn_t ad7150_event_handler(int irq, void *private) if ((int_status & AD7150_STATUS_OUT1) && !(chip->old_state & AD7150_STATUS_OUT1)) iio_push_event(indio_dev, 0, - IIO_UNMOD_EVENT_CODE(IIO_EV_CLASS_IN, + IIO_UNMOD_EVENT_CODE(IIO_IN, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), timestamp); else if ((!(int_status & AD7150_STATUS_OUT1)) && (chip->old_state & AD7150_STATUS_OUT1)) iio_push_event(indio_dev, 0, - IIO_UNMOD_EVENT_CODE(IIO_EV_CLASS_IN, + IIO_UNMOD_EVENT_CODE(IIO_IN, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), @@ -667,14 +670,14 @@ static irqreturn_t ad7150_event_handler(int irq, void *private) if ((int_status & AD7150_STATUS_OUT2) && !(chip->old_state & AD7150_STATUS_OUT2)) iio_push_event(indio_dev, 0, - IIO_UNMOD_EVENT_CODE(IIO_EV_CLASS_IN, + IIO_UNMOD_EVENT_CODE(IIO_IN, 1, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), timestamp); else if ((!(int_status & AD7150_STATUS_OUT2)) && (chip->old_state & AD7150_STATUS_OUT2)) iio_push_event(indio_dev, 0, - IIO_UNMOD_EVENT_CODE(IIO_EV_CLASS_IN, + IIO_UNMOD_EVENT_CODE(IIO_IN, 1, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), diff --git a/drivers/staging/iio/adc/ad7152.c b/drivers/staging/iio/adc/ad7152.c index 21f5f380fb5e..fcec9b620c6e 100644 --- a/drivers/staging/iio/adc/ad7152.c +++ b/drivers/staging/iio/adc/ad7152.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/i2c.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c new file mode 100644 index 000000000000..adf6d95dd34f --- /dev/null +++ b/drivers/staging/iio/adc/ad7192.c @@ -0,0 +1,1184 @@ +/* + * AD7190 AD7192 AD7195 SPI ADC driver + * + * Copyright 2011 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 "../iio.h" +#include "../sysfs.h" +#include "../ring_generic.h" +#include "../ring_sw.h" +#include "../trigger.h" +#include "../trigger_consumer.h" + +#include "ad7192.h" + +/* Registers */ +#define AD7192_REG_COMM 0 /* Communications Register (WO, 8-bit) */ +#define AD7192_REG_STAT 0 /* Status Register (RO, 8-bit) */ +#define AD7192_REG_MODE 1 /* Mode Register (RW, 24-bit */ +#define AD7192_REG_CONF 2 /* Configuration Register (RW, 24-bit) */ +#define AD7192_REG_DATA 3 /* Data Register (RO, 24/32-bit) */ +#define AD7192_REG_ID 4 /* ID Register (RO, 8-bit) */ +#define AD7192_REG_GPOCON 5 /* GPOCON Register (RO, 8-bit) */ +#define AD7192_REG_OFFSET 6 /* Offset Register (RW, 16-bit + * (AD7792)/24-bit (AD7192)) */ +#define AD7192_REG_FULLSALE 7 /* Full-Scale Register + * (RW, 16-bit (AD7792)/24-bit (AD7192)) */ + +/* Communications Register Bit Designations (AD7192_REG_COMM) */ +#define AD7192_COMM_WEN (1 << 7) /* Write Enable */ +#define AD7192_COMM_WRITE (0 << 6) /* Write Operation */ +#define AD7192_COMM_READ (1 << 6) /* Read Operation */ +#define AD7192_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */ +#define AD7192_COMM_CREAD (1 << 2) /* Continuous Read of Data Register */ + +/* Status Register Bit Designations (AD7192_REG_STAT) */ +#define AD7192_STAT_RDY (1 << 7) /* Ready */ +#define AD7192_STAT_ERR (1 << 6) /* Error (Overrange, Underrange) */ +#define AD7192_STAT_NOREF (1 << 5) /* Error no external reference */ +#define AD7192_STAT_PARITY (1 << 4) /* Parity */ +#define AD7192_STAT_CH3 (1 << 2) /* Channel 3 */ +#define AD7192_STAT_CH2 (1 << 1) /* Channel 2 */ +#define AD7192_STAT_CH1 (1 << 0) /* Channel 1 */ + +/* Mode Register Bit Designations (AD7192_REG_MODE) */ +#define AD7192_MODE_SEL(x) (((x) & 0x7) << 21) /* Operation Mode Select */ +#define AD7192_MODE_DAT_STA (1 << 20) /* Status Register transmission */ +#define AD7192_MODE_CLKSRC(x) (((x) & 0x3) << 18) /* Clock Source Select */ +#define AD7192_MODE_SINC3 (1 << 15) /* SINC3 Filter Select */ +#define AD7192_MODE_ACX (1 << 14) /* AC excitation enable(AD7195 only)*/ +#define AD7192_MODE_ENPAR (1 << 13) /* Parity Enable */ +#define AD7192_MODE_CLKDIV (1 << 12) /* Clock divide by 2 (AD7190/2 only)*/ +#define AD7192_MODE_SCYCLE (1 << 11) /* Single cycle conversion */ +#define AD7192_MODE_REJ60 (1 << 10) /* 50/60Hz notch filter */ +#define AD7192_MODE_RATE(x) ((x) & 0x3FF) /* Filter Update Rate Select */ + +/* Mode Register: AD7192_MODE_SEL options */ +#define AD7192_MODE_CONT 0 /* Continuous Conversion Mode */ +#define AD7192_MODE_SINGLE 1 /* Single Conversion Mode */ +#define AD7192_MODE_IDLE 2 /* Idle Mode */ +#define AD7192_MODE_PWRDN 3 /* Power-Down Mode */ +#define AD7192_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */ +#define AD7192_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */ +#define AD7192_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */ +#define AD7192_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */ + +/* Mode Register: AD7192_MODE_CLKSRC options */ +#define AD7192_CLK_EXT_MCLK1_2 0 /* External 4.92 MHz Clock connected + * from MCLK1 to MCLK2 */ +#define AD7192_CLK_EXT_MCLK2 1 /* External Clock applied to MCLK2 */ +#define AD7192_CLK_INT 2 /* Internal 4.92 MHz Clock not + * available at the MCLK2 pin */ +#define AD7192_CLK_INT_CO 3 /* Internal 4.92 MHz Clock available + * at the MCLK2 pin */ + + +/* Configuration Register Bit Designations (AD7192_REG_CONF) */ + +#define AD7192_CONF_CHOP (1 << 23) /* CHOP enable */ +#define AD7192_CONF_REFSEL (1 << 20) /* REFIN1/REFIN2 Reference Select */ +#define AD7192_CONF_CHAN(x) (((x) & 0xFF) << 8) /* Channel select */ +#define AD7192_CONF_BURN (1 << 7) /* Burnout current enable */ +#define AD7192_CONF_REFDET (1 << 6) /* Reference detect enable */ +#define AD7192_CONF_BUF (1 << 4) /* Buffered Mode Enable */ +#define AD7192_CONF_UNIPOLAR (1 << 3) /* Unipolar/Bipolar Enable */ +#define AD7192_CONF_GAIN(x) ((x) & 0x7) /* Gain Select */ + +#define AD7192_CH_AIN1P_AIN2M 0 /* AIN1(+) - AIN2(-) */ +#define AD7192_CH_AIN3P_AIN4M 1 /* AIN3(+) - AIN4(-) */ +#define AD7192_CH_TEMP 2 /* Temp Sensor */ +#define AD7192_CH_AIN2P_AIN2M 3 /* AIN2(+) - AIN2(-) */ +#define AD7192_CH_AIN1 4 /* AIN1 - AINCOM */ +#define AD7192_CH_AIN2 5 /* AIN2 - AINCOM */ +#define AD7192_CH_AIN3 6 /* AIN3 - AINCOM */ +#define AD7192_CH_AIN4 7 /* AIN4 - AINCOM */ + +/* ID Register Bit Designations (AD7192_REG_ID) */ +#define ID_AD7190 0x4 +#define ID_AD7192 0x0 +#define ID_AD7195 0x6 +#define AD7192_ID_MASK 0x0F + +/* GPOCON Register Bit Designations (AD7192_REG_GPOCON) */ +#define AD7192_GPOCON_BPDSW (1 << 6) /* Bridge power-down switch enable */ +#define AD7192_GPOCON_GP32EN (1 << 5) /* Digital Output P3 and P2 enable */ +#define AD7192_GPOCON_GP10EN (1 << 4) /* Digital Output P1 and P0 enable */ +#define AD7192_GPOCON_P3DAT (1 << 3) /* P3 state */ +#define AD7192_GPOCON_P2DAT (1 << 2) /* P2 state */ +#define AD7192_GPOCON_P1DAT (1 << 1) /* P1 state */ +#define AD7192_GPOCON_P0DAT (1 << 0) /* P0 state */ + +#define AD7192_INT_FREQ_MHz 4915200 + +/* NOTE: + * The AD7190/2/5 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. + */ + +struct ad7192_state { + struct spi_device *spi; + struct iio_trigger *trig; + struct regulator *reg; + struct ad7192_platform_data *pdata; + wait_queue_head_t wq_data_avail; + bool done; + bool irq_dis; + u16 int_vref_mv; + u32 mclk; + u32 f_order; + u32 mode; + u32 conf; + u32 scale_avail[8][2]; + u32 available_scan_masks[9]; + u8 gpocon; + u8 devid; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + u8 data[4] ____cacheline_aligned; +}; + +static int __ad7192_write_reg(struct ad7192_state *st, bool locked, + bool cs_change, unsigned char reg, + unsigned size, unsigned val) +{ + u8 *data = st->data; + struct spi_transfer t = { + .tx_buf = data, + .len = size + 1, + .cs_change = cs_change, + }; + struct spi_message m; + + data[0] = AD7192_COMM_WRITE | AD7192_COMM_ADDR(reg); + + switch (size) { + case 3: + data[1] = val >> 16; + data[2] = val >> 8; + data[3] = val; + break; + case 2: + data[1] = val >> 8; + data[2] = val; + break; + case 1: + data[1] = val; + break; + default: + return -EINVAL; + } + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + if (locked) + return spi_sync_locked(st->spi, &m); + else + return spi_sync(st->spi, &m); +} + +static int ad7192_write_reg(struct ad7192_state *st, + unsigned reg, unsigned size, unsigned val) +{ + return __ad7192_write_reg(st, false, false, reg, size, val); +} + +static int __ad7192_read_reg(struct ad7192_state *st, bool locked, + bool cs_change, unsigned char reg, + int *val, unsigned size) +{ + u8 *data = st->data; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = data, + .len = 1, + }, { + .rx_buf = data, + .len = size, + .cs_change = cs_change, + }, + }; + struct spi_message m; + + data[0] = AD7192_COMM_READ | AD7192_COMM_ADDR(reg); + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + if (locked) + ret = spi_sync_locked(st->spi, &m); + else + ret = spi_sync(st->spi, &m); + + if (ret < 0) + return ret; + + switch (size) { + case 3: + *val = data[0] << 16 | data[1] << 8 | data[2]; + break; + case 2: + *val = data[0] << 8 | data[1]; + break; + case 1: + *val = data[0]; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ad7192_read_reg(struct ad7192_state *st, + unsigned reg, int *val, unsigned size) +{ + return __ad7192_read_reg(st, 0, 0, reg, val, size); +} + +static int ad7192_read(struct ad7192_state *st, unsigned ch, + unsigned len, int *val) +{ + int ret; + st->conf = (st->conf & ~AD7192_CONF_CHAN(-1)) | + AD7192_CONF_CHAN(1 << ch); + st->mode = (st->mode & ~AD7192_MODE_SEL(-1)) | + AD7192_MODE_SEL(AD7192_MODE_SINGLE); + + ad7192_write_reg(st, AD7192_REG_CONF, 3, st->conf); + + spi_bus_lock(st->spi->master); + st->done = false; + + ret = __ad7192_write_reg(st, 1, 1, AD7192_REG_MODE, 3, st->mode); + if (ret < 0) + goto out; + + st->irq_dis = false; + enable_irq(st->spi->irq); + wait_event_interruptible(st->wq_data_avail, st->done); + + ret = __ad7192_read_reg(st, 1, 0, AD7192_REG_DATA, val, len); +out: + spi_bus_unlock(st->spi->master); + + return ret; +} + +static int ad7192_calibrate(struct ad7192_state *st, unsigned mode, unsigned ch) +{ + int ret; + + st->conf = (st->conf & ~AD7192_CONF_CHAN(-1)) | + AD7192_CONF_CHAN(1 << ch); + st->mode = (st->mode & ~AD7192_MODE_SEL(-1)) | AD7192_MODE_SEL(mode); + + ad7192_write_reg(st, AD7192_REG_CONF, 3, st->conf); + + spi_bus_lock(st->spi->master); + st->done = false; + + ret = __ad7192_write_reg(st, 1, 1, AD7192_REG_MODE, 3, + (st->devid != ID_AD7195) ? + st->mode | AD7192_MODE_CLKDIV : + st->mode); + if (ret < 0) + goto out; + + st->irq_dis = false; + enable_irq(st->spi->irq); + wait_event_interruptible(st->wq_data_avail, st->done); + + st->mode = (st->mode & ~AD7192_MODE_SEL(-1)) | + AD7192_MODE_SEL(AD7192_MODE_IDLE); + + ret = __ad7192_write_reg(st, 1, 0, AD7192_REG_MODE, 3, st->mode); +out: + spi_bus_unlock(st->spi->master); + + return ret; +} + +static const u8 ad7192_calib_arr[8][2] = { + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN1}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN1}, + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN2}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN2}, + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN3}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN3}, + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN4}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN4} +}; + +static int ad7192_calibrate_all(struct ad7192_state *st) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(ad7192_calib_arr); i++) { + ret = ad7192_calibrate(st, ad7192_calib_arr[i][0], + ad7192_calib_arr[i][1]); + if (ret) + goto out; + } + + return 0; +out: + dev_err(&st->spi->dev, "Calibration failed\n"); + return ret; +} + +static int ad7192_setup(struct ad7192_state *st) +{ + struct iio_dev *indio_dev = spi_get_drvdata(st->spi); + struct ad7192_platform_data *pdata = st->pdata; + unsigned long long scale_uv; + int i, ret, id; + u8 ones[6]; + + /* reset the serial interface */ + memset(&ones, 0xFF, 6); + ret = spi_write(st->spi, &ones, 6); + if (ret < 0) + goto out; + msleep(1); /* Wait for at least 500us */ + + /* write/read test for device presence */ + ret = ad7192_read_reg(st, AD7192_REG_ID, &id, 1); + if (ret) + goto out; + + id &= AD7192_ID_MASK; + + if (id != st->devid) + dev_warn(&st->spi->dev, "device ID query failed (0x%X)\n", id); + + switch (pdata->clock_source_sel) { + case AD7192_CLK_EXT_MCLK1_2: + case AD7192_CLK_EXT_MCLK2: + st->mclk = AD7192_INT_FREQ_MHz; + break; + case AD7192_CLK_INT: + case AD7192_CLK_INT_CO: + if (pdata->ext_clk_Hz) + st->mclk = pdata->ext_clk_Hz; + else + st->mclk = AD7192_INT_FREQ_MHz; + break; + default: + ret = -EINVAL; + goto out; + } + + st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) | + AD7192_MODE_CLKSRC(pdata->clock_source_sel) | + AD7192_MODE_RATE(480); + + st->conf = AD7192_CONF_GAIN(0); + + if (pdata->rej60_en) + st->mode |= AD7192_MODE_REJ60; + + if (pdata->sinc3_en) + st->mode |= AD7192_MODE_SINC3; + + if (pdata->refin2_en && (st->devid != ID_AD7195)) + st->conf |= AD7192_CONF_REFSEL; + + if (pdata->chop_en) { + st->conf |= AD7192_CONF_CHOP; + if (pdata->sinc3_en) + st->f_order = 3; /* SINC 3rd order */ + else + st->f_order = 4; /* SINC 4th order */ + } else { + st->f_order = 1; + } + + if (pdata->buf_en) + st->conf |= AD7192_CONF_BUF; + + if (pdata->unipolar_en) + st->conf |= AD7192_CONF_UNIPOLAR; + + if (pdata->burnout_curr_en) + st->conf |= AD7192_CONF_BURN; + + ret = ad7192_write_reg(st, AD7192_REG_MODE, 3, st->mode); + if (ret) + goto out; + + ret = ad7192_write_reg(st, AD7192_REG_CONF, 3, st->conf); + if (ret) + goto out; + + ret = ad7192_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)st->int_vref_mv * 100000000) + >> (indio_dev->channels[0].scan_type.realbits - + ((st->conf & AD7192_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->spi->dev, "setup failed\n"); + return ret; +} + +static int ad7192_scan_from_ring(struct ad7192_state *st, unsigned ch, int *val) +{ + struct iio_ring_buffer *ring = iio_priv_to_dev(st)->ring; + int ret; + s64 dat64[2]; + u32 *dat32 = (u32 *)dat64; + + if (!(ring->scan_mask & (1 << ch))) + return -EBUSY; + + ret = ring->access->read_last(ring, (u8 *) &dat64); + if (ret) + return ret; + + *val = *dat32; + + return 0; +} + +static int ad7192_ring_preenable(struct iio_dev *indio_dev) +{ + struct ad7192_state *st = iio_priv(indio_dev); + struct iio_ring_buffer *ring = indio_dev->ring; + size_t d_size; + unsigned channel; + + if (!ring->scan_count) + return -EINVAL; + + channel = __ffs(ring->scan_mask); + + d_size = ring->scan_count * + indio_dev->channels[0].scan_type.storagebits / 8; + + if (ring->scan_timestamp) { + d_size += sizeof(s64); + + if (d_size % sizeof(s64)) + d_size += sizeof(s64) - (d_size % sizeof(s64)); + } + + if (indio_dev->ring->access->set_bytes_per_datum) + indio_dev->ring->access->set_bytes_per_datum(indio_dev->ring, + d_size); + + st->mode = (st->mode & ~AD7192_MODE_SEL(-1)) | + AD7192_MODE_SEL(AD7192_MODE_CONT); + st->conf = (st->conf & ~AD7192_CONF_CHAN(-1)) | + AD7192_CONF_CHAN(1 << indio_dev->channels[channel].address); + + ad7192_write_reg(st, AD7192_REG_CONF, 3, st->conf); + + spi_bus_lock(st->spi->master); + __ad7192_write_reg(st, 1, 1, AD7192_REG_MODE, 3, st->mode); + + st->irq_dis = false; + enable_irq(st->spi->irq); + + return 0; +} + +static int ad7192_ring_postdisable(struct iio_dev *indio_dev) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + st->mode = (st->mode & ~AD7192_MODE_SEL(-1)) | + AD7192_MODE_SEL(AD7192_MODE_IDLE); + + st->done = false; + wait_event_interruptible(st->wq_data_avail, st->done); + + if (!st->irq_dis) + disable_irq_nosync(st->spi->irq); + + __ad7192_write_reg(st, 1, 0, AD7192_REG_MODE, 3, st->mode); + + return spi_bus_unlock(st->spi->master); +} + +/** + * ad7192_trigger_handler() bh of trigger launched polling to ring buffer + **/ +static irqreturn_t ad7192_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct iio_ring_buffer *ring = indio_dev->ring; + struct ad7192_state *st = iio_priv(indio_dev); + s64 dat64[2]; + s32 *dat32 = (s32 *)dat64; + + if (ring->scan_count) + __ad7192_read_reg(st, 1, 1, AD7192_REG_DATA, + dat32, + indio_dev->channels[0].scan_type.realbits/8); + + /* Guaranteed to be aligned with 8 byte boundary */ + if (ring->scan_timestamp) + dat64[1] = pf->timestamp; + + ring->access->store_to(ring, (u8 *)dat64, pf->timestamp); + + iio_trigger_notify_done(indio_dev->trig); + st->irq_dis = false; + enable_irq(st->spi->irq); + + return IRQ_HANDLED; +} + +static const struct iio_ring_setup_ops ad7192_ring_setup_ops = { + .preenable = &ad7192_ring_preenable, + .postenable = &iio_triggered_ring_postenable, + .predisable = &iio_triggered_ring_predisable, + .postdisable = &ad7192_ring_postdisable, +}; + +static int ad7192_register_ring_funcs_and_init(struct iio_dev *indio_dev) +{ + int ret; + + indio_dev->ring = iio_sw_rb_allocate(indio_dev); + if (!indio_dev->ring) { + ret = -ENOMEM; + goto error_ret; + } + /* Effectively select the ring buffer implementation */ + indio_dev->ring->access = &ring_sw_access_funcs; + indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time, + &ad7192_trigger_handler, + IRQF_ONESHOT, + indio_dev, + "ad7192_consumer%d", + indio_dev->id); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_deallocate_sw_rb; + } + + /* Ring buffer functions - here trigger setup related */ + indio_dev->ring->setup_ops = &ad7192_ring_setup_ops; + + /* Flag that polled ring buffering is possible */ + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; + +error_deallocate_sw_rb: + iio_sw_rb_free(indio_dev->ring); +error_ret: + return ret; +} + +static void ad7192_ring_cleanup(struct iio_dev *indio_dev) +{ + iio_dealloc_pollfunc(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} + +/** + * ad7192_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +static irqreturn_t ad7192_data_rdy_trig_poll(int irq, void *private) +{ + struct ad7192_state *st = iio_priv(private); + + st->done = true; + wake_up_interruptible(&st->wq_data_avail); + disable_irq_nosync(irq); + st->irq_dis = true; + iio_trigger_poll(st->trig, iio_get_time_ns()); + + return IRQ_HANDLED; +} + +static int ad7192_probe_trigger(struct iio_dev *indio_dev) +{ + struct ad7192_state *st = iio_priv(indio_dev); + int ret; + + st->trig = iio_allocate_trigger("%s-dev%d", + spi_get_device_id(st->spi)->name, + indio_dev->id); + if (st->trig == NULL) { + ret = -ENOMEM; + goto error_ret; + } + + ret = request_irq(st->spi->irq, + ad7192_data_rdy_trig_poll, + IRQF_TRIGGER_LOW, + spi_get_device_id(st->spi)->name, + indio_dev); + if (ret) + goto error_free_trig; + + disable_irq_nosync(st->spi->irq); + st->irq_dis = true; + st->trig->dev.parent = &st->spi->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = indio_dev; + + ret = iio_trigger_register(st->trig); + + /* select default trigger */ + indio_dev->trig = st->trig; + if (ret) + goto error_free_irq; + + return 0; + +error_free_irq: + free_irq(st->spi->irq, indio_dev); +error_free_trig: + iio_free_trigger(st->trig); +error_ret: + return ret; +} + +static void ad7192_remove_trigger(struct iio_dev *indio_dev) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + iio_trigger_unregister(st->trig); + free_irq(st->spi->irq, indio_dev); + iio_free_trigger(st->trig); +} + +static ssize_t ad7192_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7192_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->mclk / + (st->f_order * 1024 * AD7192_MODE_RATE(st->mode))); +} + +static ssize_t ad7192_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7192_state *st = iio_priv(indio_dev); + unsigned long lval; + int div, ret; + + ret = strict_strtoul(buf, 10, &lval); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + if (iio_ring_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + + div = st->mclk / (lval * st->f_order * 1024); + if (div < 1 || div > 1023) { + ret = -EINVAL; + goto out; + } + + st->mode &= ~AD7192_MODE_RATE(-1); + st->mode |= AD7192_MODE_RATE(div); + ad7192_write_reg(st, AD7192_REG_MODE, 3, st->mode); + +out: + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + ad7192_read_frequency, + ad7192_write_frequency); + + +static ssize_t ad7192_show_scale_available(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7192_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-in_scale_available, + S_IRUGO, ad7192_show_scale_available, NULL, 0); + +static IIO_DEVICE_ATTR(in_scale_available, S_IRUGO, + ad7192_show_scale_available, NULL, 0); + +static ssize_t ad7192_show_ac_excitation(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7192_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", !!(st->mode & AD7192_MODE_ACX)); +} + +static ssize_t ad7192_show_bridge_switch(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7192_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", !!(st->gpocon & AD7192_GPOCON_BPDSW)); +} + +static ssize_t ad7192_set(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7192_state *st = iio_priv(indio_dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + bool val; + + ret = strtobool(buf, &val); + if (ret < 0) + return ret; + + mutex_lock(&indio_dev->mlock); + if (iio_ring_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + + switch (this_attr->address) { + case AD7192_REG_GPOCON: + if (val) + st->gpocon |= AD7192_GPOCON_BPDSW; + else + st->gpocon &= ~AD7192_GPOCON_BPDSW; + + ad7192_write_reg(st, AD7192_REG_GPOCON, 1, st->gpocon); + break; + case AD7192_REG_MODE: + if (val) + st->mode |= AD7192_MODE_ACX; + else + st->mode &= ~AD7192_MODE_ACX; + + ad7192_write_reg(st, AD7192_REG_GPOCON, 3, st->mode); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(bridge_switch_en, S_IRUGO | S_IWUSR, + ad7192_show_bridge_switch, ad7192_set, + AD7192_REG_GPOCON); + +static IIO_DEVICE_ATTR(ac_excitation_en, S_IRUGO | S_IWUSR, + ad7192_show_ac_excitation, ad7192_set, + AD7192_REG_MODE); + +static struct attribute *ad7192_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_dev_attr_in_m_in_scale_available.dev_attr.attr, + &iio_dev_attr_in_scale_available.dev_attr.attr, + &iio_dev_attr_bridge_switch_en.dev_attr.attr, + &iio_dev_attr_ac_excitation_en.dev_attr.attr, + NULL +}; + +static mode_t ad7192_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad7192_state *st = iio_priv(dev_info); + + mode_t mode = attr->mode; + + if ((st->devid != ID_AD7195) && + (attr == &iio_dev_attr_ac_excitation_en.dev_attr.attr)) + mode = 0; + + return mode; +} + +static const struct attribute_group ad7192_attribute_group = { + .attrs = ad7192_attributes, + .is_visible = ad7192_attr_is_visible, +}; + +static int ad7192_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad7192_state *st = iio_priv(indio_dev); + int ret, smpl = 0; + bool unipolar = !!(st->conf & AD7192_CONF_UNIPOLAR); + + switch (m) { + case 0: + mutex_lock(&indio_dev->mlock); + if (iio_ring_enabled(indio_dev)) + ret = ad7192_scan_from_ring(st, + chan->scan_index, &smpl); + else + ret = ad7192_read(st, chan->address, + chan->scan_type.realbits / 8, &smpl); + mutex_unlock(&indio_dev->mlock); + + if (ret < 0) + return ret; + + *val = (smpl >> chan->scan_type.shift) & + ((1 << (chan->scan_type.realbits)) - 1); + + switch (chan->type) { + case IIO_IN: + case IIO_IN_DIFF: + if (!unipolar) + *val -= (1 << (chan->scan_type.realbits - 1)); + break; + case IIO_TEMP: + *val -= 0x800000; + *val /= 2815; /* temp Kelvin */ + *val -= 273; /* temp Celsius */ + break; + default: + return -EINVAL; + } + return IIO_VAL_INT; + + case (1 << IIO_CHAN_INFO_SCALE_SHARED): + mutex_lock(&indio_dev->mlock); + *val = st->scale_avail[AD7192_CONF_GAIN(st->conf)][0]; + *val2 = st->scale_avail[AD7192_CONF_GAIN(st->conf)][1]; + mutex_unlock(&indio_dev->mlock); + + return IIO_VAL_INT_PLUS_NANO; + + case (1 << IIO_CHAN_INFO_SCALE_SEPARATE): + *val = 1000; + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ad7192_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad7192_state *st = iio_priv(indio_dev); + int ret, i; + unsigned int tmp; + + mutex_lock(&indio_dev->mlock); + if (iio_ring_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + + switch (mask) { + case (1 << IIO_CHAN_INFO_SCALE_SHARED): + ret = -EINVAL; + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + if (val2 == st->scale_avail[i][1]) { + tmp = st->conf; + st->conf &= ~AD7192_CONF_GAIN(-1); + st->conf |= AD7192_CONF_GAIN(i); + + if (tmp != st->conf) { + ad7192_write_reg(st, AD7192_REG_CONF, + 3, st->conf); + ad7192_calibrate_all(st); + } + ret = 0; + } + + default: + ret = -EINVAL; + } + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad7192_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + if (indio_dev->trig != trig) + return -EINVAL; + + return 0; +} + +static int ad7192_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 ad7192_info = { + .read_raw = &ad7192_read_raw, + .write_raw = &ad7192_write_raw, + .write_raw_get_fmt = &ad7192_write_raw_get_fmt, + .attrs = &ad7192_attribute_group, + .validate_trigger = ad7192_validate_trigger, + .driver_module = THIS_MODULE, +}; + +#define AD7192_CHAN_DIFF(_chan, _chan2, _name, _address, _si) \ + { .type = IIO_IN_DIFF, \ + .indexed = 1, \ + .extend_name = _name, \ + .channel = _chan, \ + .channel2 = _chan2, \ + .info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED), \ + .address = _address, \ + .scan_index = _si, \ + .scan_type = IIO_ST('s', 24, 32, 0)} + +#define AD7192_CHAN(_chan, _address, _si) \ + { .type = IIO_IN, \ + .indexed = 1, \ + .channel = _chan, \ + .info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED), \ + .address = _address, \ + .scan_index = _si, \ + .scan_type = IIO_ST('s', 24, 32, 0)} + +#define AD7192_CHAN_TEMP(_chan, _address, _si) \ + { .type = IIO_TEMP, \ + .indexed = 1, \ + .channel = _chan, \ + .info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE), \ + .address = _address, \ + .scan_index = _si, \ + .scan_type = IIO_ST('s', 24, 32, 0)} + +static struct iio_chan_spec ad7192_channels[] = { + AD7192_CHAN_DIFF(1, 2, NULL, AD7192_CH_AIN1P_AIN2M, 0), + AD7192_CHAN_DIFF(3, 4, NULL, AD7192_CH_AIN3P_AIN4M, 1), + AD7192_CHAN_TEMP(0, AD7192_CH_TEMP, 2), + AD7192_CHAN_DIFF(2, 2, "shorted", AD7192_CH_AIN2P_AIN2M, 3), + AD7192_CHAN(1, AD7192_CH_AIN1, 4), + AD7192_CHAN(2, AD7192_CH_AIN2, 5), + AD7192_CHAN(3, AD7192_CH_AIN3, 6), + AD7192_CHAN(4, AD7192_CH_AIN4, 7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static int __devinit ad7192_probe(struct spi_device *spi) +{ + struct ad7192_platform_data *pdata = spi->dev.platform_data; + struct ad7192_state *st; + struct iio_dev *indio_dev; + int ret, i , voltage_uv = 0, regdone = 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_allocate_device(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + voltage_uv = regulator_get_voltage(st->reg); + } + + st->pdata = pdata; + + if (pdata && pdata->vref_mv) + st->int_vref_mv = pdata->vref_mv; + else if (voltage_uv) + st->int_vref_mv = voltage_uv / 1000; + else + dev_warn(&spi->dev, "reference voltage undefined\n"); + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + st->devid = spi_get_device_id(spi)->driver_data; + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7192_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7192_channels); + indio_dev->available_scan_masks = st->available_scan_masks; + indio_dev->info = &ad7192_info; + + for (i = 0; i < indio_dev->num_channels; i++) + st->available_scan_masks[i] = (1 << i) | (1 << + indio_dev->channels[indio_dev->num_channels - 1]. + scan_index); + + init_waitqueue_head(&st->wq_data_avail); + + ret = ad7192_register_ring_funcs_and_init(indio_dev); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unreg_ring; + regdone = 1; + + ret = ad7192_probe_trigger(indio_dev); + if (ret) + goto error_unreg_ring; + + ret = iio_ring_buffer_register_ex(indio_dev->ring, 0, + indio_dev->channels, + indio_dev->num_channels); + if (ret) + goto error_remove_trigger; + + ret = ad7192_setup(st); + if (ret) + goto error_uninitialize_ring; + + return 0; + +error_uninitialize_ring: + iio_ring_buffer_unregister(indio_dev->ring); +error_remove_trigger: + ad7192_remove_trigger(indio_dev); +error_unreg_ring: + ad7192_ring_cleanup(indio_dev); +error_disable_reg: + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); +error_put_reg: + if (!IS_ERR(st->reg)) + regulator_put(st->reg); + + if (regdone) + iio_device_unregister(indio_dev); + else + iio_free_device(indio_dev); + + return ret; +} + +static int ad7192_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7192_state *st = iio_priv(indio_dev); + + iio_ring_buffer_unregister(indio_dev->ring); + ad7192_remove_trigger(indio_dev); + ad7192_ring_cleanup(indio_dev); + + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + + iio_device_unregister(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7192_id[] = { + {"ad7190", ID_AD7190}, + {"ad7192", ID_AD7192}, + {"ad7195", ID_AD7195}, + {} +}; + +static struct spi_driver ad7192_driver = { + .driver = { + .name = "ad7192", + .owner = THIS_MODULE, + }, + .probe = ad7192_probe, + .remove = __devexit_p(ad7192_remove), + .id_table = ad7192_id, +}; + +static int __init ad7192_init(void) +{ + return spi_register_driver(&ad7192_driver); +} +module_init(ad7192_init); + +static void __exit ad7192_exit(void) +{ + spi_unregister_driver(&ad7192_driver); +} +module_exit(ad7192_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7190, AD7192, AD7195 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7192.h b/drivers/staging/iio/adc/ad7192.h new file mode 100644 index 000000000000..a0a5b61a41f1 --- /dev/null +++ b/drivers/staging/iio/adc/ad7192.h @@ -0,0 +1,47 @@ +/* + * AD7190 AD7192 AD7195 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ +#ifndef IIO_ADC_AD7192_H_ +#define IIO_ADC_AD7192_H_ + +/* + * TODO: struct ad7192_platform_data needs to go into include/linux/iio + */ + +/** + * struct ad7192_platform_data - platform/board specific information + * @vref_mv: the external reference voltage in millivolt + * @clock_source_sel: [0..3] + * 0 External 4.92 MHz clock connected from MCLK1 to MCLK2 + * 1 External Clock applied to MCLK2 + * 2 Internal 4.92 MHz Clock not available at the MCLK2 pin + * 3 Internal 4.92 MHz Clock available at the MCLK2 pin + * @ext_clk_Hz: the external clock frequency in Hz, if not set + * the driver uses the internal clock (16.776 MHz) + * @refin2_en: REFIN1/REFIN2 Reference Select (AD7190/2 only) + * @rej60_en: 50/60Hz notch filter enable + * @sinc3_en: SINC3 filter enable (default SINC4) + * @chop_en: CHOP mode enable + * @buf_en: buffered input mode enable + * @unipolar_en: unipolar mode enable + * @burnout_curr_en: constant current generators on AIN(+|-) enable + */ + +struct ad7192_platform_data { + u16 vref_mv; + u8 clock_source_sel; + u32 ext_clk_Hz; + bool refin2_en; + bool rej60_en; + bool sinc3_en; + bool chop_en; + bool buf_en; + bool unipolar_en; + bool burnout_curr_en; +}; + +#endif /* IIO_ADC_AD7192_H_ */ diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c new file mode 100644 index 000000000000..ab6cf9cb5a7f --- /dev/null +++ b/drivers/staging/iio/adc/ad7280a.c @@ -0,0 +1,992 @@ +/* + * AD7280A Lithium Ion Battery Monitoring System + * + * 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/err.h> +#include <linux/delay.h> +#include <linux/interrupt.h> + +#include "../iio.h" +#include "../sysfs.h" + +#include "ad7280a.h" + +/* Registers */ +#define AD7280A_CELL_VOLTAGE_1 0x0 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_2 0x1 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_3 0x2 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_4 0x3 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_5 0x4 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_6 0x5 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_1 0x6 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_2 0x7 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_3 0x8 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_4 0x9 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_5 0xA /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_6 0xB /* D11 to D0, Read only */ +#define AD7280A_SELF_TEST 0xC /* D11 to D0, Read only */ +#define AD7280A_CONTROL_HB 0xD /* D15 to D8, Read/write */ +#define AD7280A_CONTROL_LB 0xE /* D7 to D0, Read/write */ +#define AD7280A_CELL_OVERVOLTAGE 0xF /* D7 to D0, Read/write */ +#define AD7280A_CELL_UNDERVOLTAGE 0x10 /* D7 to D0, Read/write */ +#define AD7280A_AUX_ADC_OVERVOLTAGE 0x11 /* D7 to D0, Read/write */ +#define AD7280A_AUX_ADC_UNDERVOLTAGE 0x12 /* D7 to D0, Read/write */ +#define AD7280A_ALERT 0x13 /* D7 to D0, Read/write */ +#define AD7280A_CELL_BALANCE 0x14 /* D7 to D0, Read/write */ +#define AD7280A_CB1_TIMER 0x15 /* D7 to D0, Read/write */ +#define AD7280A_CB2_TIMER 0x16 /* D7 to D0, Read/write */ +#define AD7280A_CB3_TIMER 0x17 /* D7 to D0, Read/write */ +#define AD7280A_CB4_TIMER 0x18 /* D7 to D0, Read/write */ +#define AD7280A_CB5_TIMER 0x19 /* D7 to D0, Read/write */ +#define AD7280A_CB6_TIMER 0x1A /* D7 to D0, Read/write */ +#define AD7280A_PD_TIMER 0x1B /* D7 to D0, Read/write */ +#define AD7280A_READ 0x1C /* D7 to D0, Read/write */ +#define AD7280A_CNVST_CONTROL 0x1D /* D7 to D0, Read/write */ + +/* Bits and Masks */ +#define AD7280A_CTRL_HB_CONV_INPUT_ALL (0 << 6) +#define AD7280A_CTRL_HB_CONV_INPUT_6CELL_AUX1_3_4 (1 << 6) +#define AD7280A_CTRL_HB_CONV_INPUT_6CELL (2 << 6) +#define AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST (3 << 6) +#define AD7280A_CTRL_HB_CONV_RES_READ_ALL (0 << 4) +#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL_AUX1_3_4 (1 << 4) +#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL (2 << 4) +#define AD7280A_CTRL_HB_CONV_RES_READ_NO (3 << 4) +#define AD7280A_CTRL_HB_CONV_START_CNVST (0 << 3) +#define AD7280A_CTRL_HB_CONV_START_CS (1 << 3) +#define AD7280A_CTRL_HB_CONV_AVG_DIS (0 << 1) +#define AD7280A_CTRL_HB_CONV_AVG_2 (1 << 1) +#define AD7280A_CTRL_HB_CONV_AVG_4 (2 << 1) +#define AD7280A_CTRL_HB_CONV_AVG_8 (3 << 1) +#define AD7280A_CTRL_HB_CONV_AVG(x) ((x) << 1) +#define AD7280A_CTRL_HB_PWRDN_SW (1 << 0) + +#define AD7280A_CTRL_LB_SWRST (1 << 7) +#define AD7280A_CTRL_LB_ACQ_TIME_400ns (0 << 5) +#define AD7280A_CTRL_LB_ACQ_TIME_800ns (1 << 5) +#define AD7280A_CTRL_LB_ACQ_TIME_1200ns (2 << 5) +#define AD7280A_CTRL_LB_ACQ_TIME_1600ns (3 << 5) +#define AD7280A_CTRL_LB_ACQ_TIME(x) ((x) << 5) +#define AD7280A_CTRL_LB_MUST_SET (1 << 4) +#define AD7280A_CTRL_LB_THERMISTOR_EN (1 << 3) +#define AD7280A_CTRL_LB_LOCK_DEV_ADDR (1 << 2) +#define AD7280A_CTRL_LB_INC_DEV_ADDR (1 << 1) +#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN (1 << 0) + +#define AD7280A_ALERT_GEN_STATIC_HIGH (1 << 6) +#define AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN (3 << 6) + +#define AD7280A_ALL_CELLS (0xAD << 16) + +#define AD7280A_MAX_SPI_CLK_Hz 700000 /* < 1MHz */ +#define AD7280A_MAX_CHAIN 8 +#define AD7280A_CELLS_PER_DEV 6 +#define AD7280A_BITS 12 +#define AD7280A_NUM_CH (AD7280A_AUX_ADC_6 - \ + AD7280A_CELL_VOLTAGE_1 + 1) + +#define AD7280A_DEVADDR_MASTER 0 +#define AD7280A_DEVADDR_ALL 0x1F +/* 5-bit device address is sent LSB first */ +#define AD7280A_DEVADDR(addr) (((addr & 0x1) << 4) | ((addr & 0x2) << 3) | \ + (addr & 0x4) | ((addr & 0x8) >> 3) | \ + ((addr & 0x10) >> 4)) + +/* During a read a valid write is mandatory. + * So writing to the highest available address (Address 0x1F) + * and setting the address all parts bit to 0 is recommended + * So the TXVAL is AD7280A_DEVADDR_ALL + CRC + */ +#define AD7280A_READ_TXVAL 0xF800030A + +/* + * AD7280 CRC + * + * P(x) = x^8 + x^5 + x^3 + x^2 + x^1 + x^0 = 0b100101111 => 0x2F + */ +#define POLYNOM 0x2F +#define POLYNOM_ORDER 8 +#define HIGHBIT 1 << (POLYNOM_ORDER - 1); + +struct ad7280_state { + struct spi_device *spi; + struct iio_chan_spec *channels; + struct iio_dev_attr *iio_attr; + int slave_num; + int scan_cnt; + int readback_delay_us; + unsigned char crc_tab[256]; + unsigned char ctrl_hb; + unsigned char ctrl_lb; + unsigned char cell_threshhigh; + unsigned char cell_threshlow; + unsigned char aux_threshhigh; + unsigned char aux_threshlow; + unsigned char cb_mask[AD7280A_MAX_CHAIN]; +}; + +static void ad7280_crc8_build_table(unsigned char *crc_tab) +{ + unsigned char bit, crc; + int cnt, i; + + for (cnt = 0; cnt < 256; cnt++) { + crc = cnt; + for (i = 0; i < 8; i++) { + bit = crc & HIGHBIT; + crc <<= 1; + if (bit) + crc ^= POLYNOM; + } + crc_tab[cnt] = crc; + } +} + +static unsigned char ad7280_calc_crc8(unsigned char *crc_tab, unsigned val) +{ + unsigned char crc; + + crc = crc_tab[val >> 16 & 0xFF]; + crc = crc_tab[crc ^ (val >> 8 & 0xFF)]; + + return crc ^ (val & 0xFF); +} + +static int ad7280_check_crc(struct ad7280_state *st, unsigned val) +{ + unsigned char crc = ad7280_calc_crc8(st->crc_tab, val >> 10); + + if (crc != ((val >> 2) & 0xFF)) + return -EIO; + + return 0; +} + +/* After initiating a conversion sequence we need to wait until the + * conversion is done. The delay is typically in the range of 15..30 us + * however depending an the number of devices in the daisy chain and the + * number of averages taken, conversion delays and acquisition time options + * it may take up to 250us, in this case we better sleep instead of busy + * wait. + */ + +static void ad7280_delay(struct ad7280_state *st) +{ + if (st->readback_delay_us < 50) + udelay(st->readback_delay_us); + else + msleep(1); +} + +static int __ad7280_read32(struct spi_device *spi, unsigned *val) +{ + unsigned rx_buf, tx_buf = cpu_to_be32(AD7280A_READ_TXVAL); + int ret; + + struct spi_transfer t = { + .tx_buf = &tx_buf, + .rx_buf = &rx_buf, + .len = 4, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + ret = spi_sync(spi, &m); + if (ret) + return ret; + + *val = be32_to_cpu(rx_buf); + + return 0; +} + +static int ad7280_write(struct ad7280_state *st, unsigned devaddr, + unsigned addr, bool all, unsigned val) +{ + unsigned reg = (devaddr << 27 | addr << 21 | + (val & 0xFF) << 13 | all << 12); + + reg |= ad7280_calc_crc8(st->crc_tab, reg >> 11) << 3 | 0x2; + reg = cpu_to_be32(reg); + + return spi_write(st->spi, ®, 4); +} + +static int ad7280_read(struct ad7280_state *st, unsigned devaddr, + unsigned addr) +{ + int ret; + unsigned tmp; + + /* turns off the read operation on all parts */ + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, + AD7280A_CTRL_HB_CONV_INPUT_ALL | + AD7280A_CTRL_HB_CONV_RES_READ_NO | + st->ctrl_hb); + if (ret) + return ret; + + /* turns on the read operation on the addressed part */ + ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0, + AD7280A_CTRL_HB_CONV_INPUT_ALL | + AD7280A_CTRL_HB_CONV_RES_READ_ALL | + st->ctrl_hb); + if (ret) + return ret; + + /* Set register address on the part to be read from */ + ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2); + if (ret) + return ret; + + __ad7280_read32(st->spi, &tmp); + + if (ad7280_check_crc(st, tmp)) + return -EIO; + + if (((tmp >> 27) != devaddr) || (((tmp >> 21) & 0x3F) != addr)) + return -EFAULT; + + return (tmp >> 13) & 0xFF; +} + +static int ad7280_read_channel(struct ad7280_state *st, unsigned devaddr, + unsigned addr) +{ + int ret; + unsigned tmp; + + ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2); + if (ret) + return ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, + AD7280A_CTRL_HB_CONV_INPUT_ALL | + AD7280A_CTRL_HB_CONV_RES_READ_NO | + st->ctrl_hb); + if (ret) + return ret; + + ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0, + AD7280A_CTRL_HB_CONV_INPUT_ALL | + AD7280A_CTRL_HB_CONV_RES_READ_ALL | + AD7280A_CTRL_HB_CONV_START_CS | + st->ctrl_hb); + if (ret) + return ret; + + ad7280_delay(st); + + __ad7280_read32(st->spi, &tmp); + + if (ad7280_check_crc(st, tmp)) + return -EIO; + + if (((tmp >> 27) != devaddr) || (((tmp >> 23) & 0xF) != addr)) + return -EFAULT; + + return (tmp >> 11) & 0xFFF; +} + +static int ad7280_read_all_channels(struct ad7280_state *st, unsigned cnt, + unsigned *array) +{ + int i, ret; + unsigned tmp, sum = 0; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1, + AD7280A_CELL_VOLTAGE_1 << 2); + if (ret) + return ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, + AD7280A_CTRL_HB_CONV_INPUT_ALL | + AD7280A_CTRL_HB_CONV_RES_READ_ALL | + AD7280A_CTRL_HB_CONV_START_CS | + st->ctrl_hb); + if (ret) + return ret; + + ad7280_delay(st); + + for (i = 0; i < cnt; i++) { + __ad7280_read32(st->spi, &tmp); + + if (ad7280_check_crc(st, tmp)) + return -EIO; + + if (array) + array[i] = tmp; + /* only sum cell voltages */ + if (((tmp >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6) + sum += ((tmp >> 11) & 0xFFF); + } + + return sum; +} + +static int ad7280_chain_setup(struct ad7280_state *st) +{ + unsigned val, n; + int ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1, + AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN | + AD7280A_CTRL_LB_LOCK_DEV_ADDR | + AD7280A_CTRL_LB_MUST_SET | + AD7280A_CTRL_LB_SWRST | + st->ctrl_lb); + if (ret) + return ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1, + AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN | + AD7280A_CTRL_LB_LOCK_DEV_ADDR | + AD7280A_CTRL_LB_MUST_SET | + st->ctrl_lb); + if (ret) + return ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1, + AD7280A_CONTROL_LB << 2); + if (ret) + return ret; + + for (n = 0; n <= AD7280A_MAX_CHAIN; n++) { + __ad7280_read32(st->spi, &val); + if (val == 0) + return n - 1; + + if (ad7280_check_crc(st, val)) + return -EIO; + + if (n != AD7280A_DEVADDR(val >> 27)) + return -EIO; + } + + return -EFAULT; +} + +static ssize_t ad7280_show_balance_sw(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad7280_state *st = iio_priv(dev_info); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + return sprintf(buf, "%d\n", + !!(st->cb_mask[this_attr->address >> 8] & + (1 << ((this_attr->address & 0xFF) + 2)))); +} + +static ssize_t ad7280_store_balance_sw(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad7280_state *st = iio_priv(dev_info); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + bool readin; + int ret; + unsigned devaddr, ch; + + ret = strtobool(buf, &readin); + if (ret) + return ret; + + devaddr = this_attr->address >> 8; + ch = this_attr->address & 0xFF; + + mutex_lock(&dev_info->mlock); + if (readin) + st->cb_mask[devaddr] |= 1 << (ch + 2); + else + st->cb_mask[devaddr] &= ~(1 << (ch + 2)); + + ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE, + 0, st->cb_mask[devaddr]); + mutex_unlock(&dev_info->mlock); + + return ret ? ret : len; +} + +static ssize_t ad7280_show_balance_timer(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad7280_state *st = iio_priv(dev_info); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + unsigned msecs; + + mutex_lock(&dev_info->mlock); + ret = ad7280_read(st, this_attr->address >> 8, + this_attr->address & 0xFF); + mutex_unlock(&dev_info->mlock); + + if (ret < 0) + return ret; + + msecs = (ret >> 3) * 71500; + + return sprintf(buf, "%d\n", msecs); +} + +static ssize_t ad7280_store_balance_timer(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad7280_state *st = iio_priv(dev_info); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + long val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return ret; + + val /= 71500; + + if (val > 31) + return -EINVAL; + + mutex_lock(&dev_info->mlock); + ret = ad7280_write(st, this_attr->address >> 8, + this_attr->address & 0xFF, + 0, (val & 0x1F) << 3); + mutex_unlock(&dev_info->mlock); + + return ret ? ret : len; +} + +static struct attribute *ad7280_attributes[AD7280A_MAX_CHAIN * + AD7280A_CELLS_PER_DEV * 2 + 1]; + +static struct attribute_group ad7280_attrs_group = { + .attrs = ad7280_attributes, +}; + +static int ad7280_channel_init(struct ad7280_state *st) +{ + int dev, ch, cnt; + + st->channels = kzalloc(sizeof(*st->channels) * + ((st->slave_num + 1) * 12 + 2), GFP_KERNEL); + if (st->channels == NULL) + return -ENOMEM; + + for (dev = 0, cnt = 0; dev <= st->slave_num; dev++) + for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_AUX_ADC_6; ch++, + cnt++) { + if (ch < AD7280A_AUX_ADC_1) { + st->channels[cnt].type = IIO_IN_DIFF; + st->channels[cnt].channel = (dev * 6) + ch; + st->channels[cnt].channel2 = + st->channels[cnt].channel + 1; + } else { + st->channels[cnt].type = IIO_TEMP; + st->channels[cnt].channel = (dev * 6) + ch - 6; + } + st->channels[cnt].indexed = 1; + st->channels[cnt].info_mask = + (1 << IIO_CHAN_INFO_SCALE_SHARED); + st->channels[cnt].address = + AD7280A_DEVADDR(dev) << 8 | ch; + st->channels[cnt].scan_index = cnt; + st->channels[cnt].scan_type.sign = 'u'; + st->channels[cnt].scan_type.realbits = 12; + st->channels[cnt].scan_type.storagebits = 32; + st->channels[cnt].scan_type.shift = 0; + } + + st->channels[cnt].type = IIO_IN_DIFF; + st->channels[cnt].channel = 0; + st->channels[cnt].channel2 = dev * 6; + st->channels[cnt].address = AD7280A_ALL_CELLS; + st->channels[cnt].indexed = 1; + st->channels[cnt].info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED); + st->channels[cnt].scan_index = cnt; + st->channels[cnt].scan_type.sign = 'u'; + st->channels[cnt].scan_type.realbits = 32; + st->channels[cnt].scan_type.storagebits = 32; + st->channels[cnt].scan_type.shift = 0; + cnt++; + st->channels[cnt].type = IIO_TIMESTAMP; + st->channels[cnt].channel = -1; + st->channels[cnt].scan_index = cnt; + st->channels[cnt].scan_type.sign = 's'; + st->channels[cnt].scan_type.realbits = 64; + st->channels[cnt].scan_type.storagebits = 64; + st->channels[cnt].scan_type.shift = 0; + + return cnt + 1; +} + +static int ad7280_attr_init(struct ad7280_state *st) +{ + int dev, ch, cnt; + + st->iio_attr = kzalloc(sizeof(*st->iio_attr) * (st->slave_num + 1) * + AD7280A_CELLS_PER_DEV * 2, GFP_KERNEL); + if (st->iio_attr == NULL) + return -ENOMEM; + + for (dev = 0, cnt = 0; dev <= st->slave_num; dev++) + for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6; + ch++, cnt++) { + st->iio_attr[cnt].address = + AD7280A_DEVADDR(dev) << 8 | ch; + st->iio_attr[cnt].dev_attr.attr.mode = + S_IWUSR | S_IRUGO; + st->iio_attr[cnt].dev_attr.show = + ad7280_show_balance_sw; + st->iio_attr[cnt].dev_attr.store = + ad7280_store_balance_sw; + st->iio_attr[cnt].dev_attr.attr.name = + kasprintf(GFP_KERNEL, + "in%d-in%d_balance_switch_en", + (dev * AD7280A_CELLS_PER_DEV) + ch, + (dev * AD7280A_CELLS_PER_DEV) + ch + 1); + ad7280_attributes[cnt] = + &st->iio_attr[cnt].dev_attr.attr; + cnt++; + st->iio_attr[cnt].address = + AD7280A_DEVADDR(dev) << 8 | + (AD7280A_CB1_TIMER + ch); + st->iio_attr[cnt].dev_attr.attr.mode = + S_IWUSR | S_IRUGO; + st->iio_attr[cnt].dev_attr.show = + ad7280_show_balance_timer; + st->iio_attr[cnt].dev_attr.store = + ad7280_store_balance_timer; + st->iio_attr[cnt].dev_attr.attr.name = + kasprintf(GFP_KERNEL, "in%d-in%d_balance_timer", + (dev * AD7280A_CELLS_PER_DEV) + ch, + (dev * AD7280A_CELLS_PER_DEV) + ch + 1); + ad7280_attributes[cnt] = + &st->iio_attr[cnt].dev_attr.attr; + } + + ad7280_attributes[cnt] = NULL; + + return 0; +} + +static ssize_t ad7280_read_channel_config(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad7280_state *st = iio_priv(dev_info); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + unsigned val; + + switch (this_attr->address) { + case AD7280A_CELL_OVERVOLTAGE: + val = 1000 + (st->cell_threshhigh * 1568) / 100; + break; + case AD7280A_CELL_UNDERVOLTAGE: + val = 1000 + (st->cell_threshlow * 1568) / 100; + break; + case AD7280A_AUX_ADC_OVERVOLTAGE: + val = (st->aux_threshhigh * 196) / 10; + break; + case AD7280A_AUX_ADC_UNDERVOLTAGE: + val = (st->aux_threshlow * 196) / 10; + break; + default: + return -EINVAL; + } + + return sprintf(buf, "%d\n", val); +} + +static ssize_t ad7280_write_channel_config(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *dev_info = dev_get_drvdata(dev); + struct ad7280_state *st = iio_priv(dev_info); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + long val; + int ret; + + ret = strict_strtol(buf, 10, &val); + if (ret) + return ret; + + switch (this_attr->address) { + case AD7280A_CELL_OVERVOLTAGE: + case AD7280A_CELL_UNDERVOLTAGE: + val = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */ + break; + case AD7280A_AUX_ADC_OVERVOLTAGE: + case AD7280A_AUX_ADC_UNDERVOLTAGE: + val = (val * 10) / 196; /* LSB 19.6mV */ + break; + default: + return -EFAULT; + } + + val = clamp(val, 0L, 0xFFL); + + mutex_lock(&dev_info->mlock); + switch (this_attr->address) { + case AD7280A_CELL_OVERVOLTAGE: + st->cell_threshhigh = val; + break; + case AD7280A_CELL_UNDERVOLTAGE: + st->cell_threshlow = val; + break; + case AD7280A_AUX_ADC_OVERVOLTAGE: + st->aux_threshhigh = val; + break; + case AD7280A_AUX_ADC_UNDERVOLTAGE: + st->aux_threshlow = val; + break; + } + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, + this_attr->address, 1, val); + + mutex_unlock(&dev_info->mlock); + + return ret ? ret : len; +} + +static irqreturn_t ad7280_event_handler(int irq, void *private) +{ + struct iio_dev *dev_info = private; + struct ad7280_state *st = iio_priv(dev_info); + unsigned *channels; + int i, ret; + + channels = kzalloc(sizeof(*channels) * st->scan_cnt, GFP_KERNEL); + if (channels == NULL) + return IRQ_HANDLED; + + ret = ad7280_read_all_channels(st, st->scan_cnt, channels); + if (ret < 0) + return IRQ_HANDLED; + + for (i = 0; i < st->scan_cnt; i++) { + if (((channels[i] >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6) { + if (((channels[i] >> 11) & 0xFFF) >= + st->cell_threshhigh) + iio_push_event(dev_info, 0, + IIO_UNMOD_EVENT_CODE(IIO_IN_DIFF, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns()); + else if (((channels[i] >> 11) & 0xFFF) <= + st->cell_threshlow) + iio_push_event(dev_info, 0, + IIO_UNMOD_EVENT_CODE(IIO_IN_DIFF, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns()); + } else { + if (((channels[i] >> 11) & 0xFFF) >= st->aux_threshhigh) + iio_push_event(dev_info, 0, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns()); + else if (((channels[i] >> 11) & 0xFFF) <= + st->aux_threshlow) + iio_push_event(dev_info, 0, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns()); + } + } + + kfree(channels); + + return IRQ_HANDLED; +} + +static IIO_DEVICE_ATTR_NAMED(in_thresh_low_value, + in-in_thresh_low_value, + S_IRUGO | S_IWUSR, + ad7280_read_channel_config, + ad7280_write_channel_config, + AD7280A_CELL_UNDERVOLTAGE); + +static IIO_DEVICE_ATTR_NAMED(in_thresh_high_value, + in-in_thresh_high_value, + S_IRUGO | S_IWUSR, + ad7280_read_channel_config, + ad7280_write_channel_config, + AD7280A_CELL_OVERVOLTAGE); + +static IIO_DEVICE_ATTR(temp_thresh_low_value, + S_IRUGO | S_IWUSR, + ad7280_read_channel_config, + ad7280_write_channel_config, + AD7280A_AUX_ADC_UNDERVOLTAGE); + +static IIO_DEVICE_ATTR(temp_thresh_high_value, + S_IRUGO | S_IWUSR, + ad7280_read_channel_config, + ad7280_write_channel_config, + AD7280A_AUX_ADC_OVERVOLTAGE); + + +static struct attribute *ad7280_event_attributes[] = { + &iio_dev_attr_in_thresh_low_value.dev_attr.attr, + &iio_dev_attr_in_thresh_high_value.dev_attr.attr, + &iio_dev_attr_temp_thresh_low_value.dev_attr.attr, + &iio_dev_attr_temp_thresh_high_value.dev_attr.attr, + NULL, +}; + +static struct attribute_group ad7280_event_attrs_group = { + .attrs = ad7280_event_attributes, +}; + +static int ad7280_read_raw(struct iio_dev *dev_info, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad7280_state *st = iio_priv(dev_info); + unsigned int scale_uv; + int ret; + + switch (m) { + case 0: + mutex_lock(&dev_info->mlock); + if (chan->address == AD7280A_ALL_CELLS) + ret = ad7280_read_all_channels(st, st->scan_cnt, NULL); + else + ret = ad7280_read_channel(st, chan->address >> 8, + chan->address & 0xFF); + mutex_unlock(&dev_info->mlock); + + if (ret < 0) + return ret; + + *val = ret; + + return IIO_VAL_INT; + case (1 << IIO_CHAN_INFO_SCALE_SHARED): + if ((chan->address & 0xFF) <= AD7280A_CELL_VOLTAGE_6) + scale_uv = (4000 * 1000) >> AD7280A_BITS; + else + scale_uv = (5000 * 1000) >> AD7280A_BITS; + + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + } + return -EINVAL; +} + +static const struct iio_info ad7280_info = { + .read_raw = &ad7280_read_raw, + .num_interrupt_lines = 1, + .event_attrs = &ad7280_event_attrs_group, + .attrs = &ad7280_attrs_group, + .driver_module = THIS_MODULE, +}; + +static const struct ad7280_platform_data ad7793_default_pdata = { + .acquisition_time = AD7280A_ACQ_TIME_400ns, + .conversion_averaging = AD7280A_CONV_AVG_DIS, + .thermistor_term_en = true, +}; + +static int __devinit ad7280_probe(struct spi_device *spi) +{ + const struct ad7280_platform_data *pdata = spi->dev.platform_data; + struct ad7280_state *st; + int ret, regdone = 0; + const unsigned short tACQ_ns[4] = {465, 1010, 1460, 1890}; + const unsigned short nAVG[4] = {1, 2, 4, 8}; + struct iio_dev *indio_dev = iio_allocate_device(sizeof(*st)); + + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + + if (!pdata) + pdata = &ad7793_default_pdata; + + ad7280_crc8_build_table(st->crc_tab); + + st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_Hz; + st->spi->mode = SPI_MODE_1; + spi_setup(st->spi); + + st->ctrl_lb = AD7280A_CTRL_LB_ACQ_TIME(pdata->acquisition_time & 0x3); + st->ctrl_hb = AD7280A_CTRL_HB_CONV_AVG(pdata->conversion_averaging + & 0x3) | (pdata->thermistor_term_en ? + AD7280A_CTRL_LB_THERMISTOR_EN : 0); + + ret = ad7280_chain_setup(st); + if (ret < 0) + goto error_free_device; + + st->slave_num = ret; + st->scan_cnt = (st->slave_num + 1) * AD7280A_NUM_CH; + st->cell_threshhigh = 0xFF; + st->aux_threshhigh = 0xFF; + + /* + * Total Conversion Time = ((tACQ + tCONV) * + * (Number of Conversions per Part)) − + * tACQ + ((N - 1) * tDELAY) + * + * Readback Delay = Total Conversion Time + tWAIT + */ + + st->readback_delay_us = + ((tACQ_ns[pdata->acquisition_time & 0x3] + 695) * + (AD7280A_NUM_CH * nAVG[pdata->conversion_averaging & 0x3])) + - tACQ_ns[pdata->acquisition_time & 0x3] + + st->slave_num * 250; + + /* Convert to usecs */ + st->readback_delay_us = DIV_ROUND_UP(st->readback_delay_us, 1000); + st->readback_delay_us += 5; /* Add tWAIT */ + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = ad7280_channel_init(st); + if (ret < 0) + goto error_free_device; + + indio_dev->num_channels = ret; + indio_dev->channels = st->channels; + indio_dev->info = &ad7280_info; + + ret = ad7280_attr_init(st); + if (ret < 0) + goto error_free_channels; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_free_attr; + regdone = 1; + + if (spi->irq > 0) { + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, + AD7280A_ALERT, 1, + AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN); + if (ret) + goto error_free_attr; + + ret = ad7280_write(st, AD7280A_DEVADDR(st->slave_num), + AD7280A_ALERT, 0, + AD7280A_ALERT_GEN_STATIC_HIGH | + (pdata->chain_last_alert_ignore & 0xF)); + if (ret) + goto error_free_attr; + + ret = request_threaded_irq(spi->irq, + NULL, + ad7280_event_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + indio_dev->name, + indio_dev); + if (ret) + goto error_free_attr; + } + + return 0; + +error_free_attr: + kfree(st->iio_attr); + +error_free_channels: + kfree(st->channels); + +error_free_device: + if (regdone) + iio_device_unregister(indio_dev); + else + iio_free_device(indio_dev); + + return ret; +} + +static int __devexit ad7280_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7280_state *st = iio_priv(indio_dev); + + if (spi->irq > 0) + free_irq(spi->irq, indio_dev); + + ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1, + AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb); + + kfree(st->channels); + kfree(st->iio_attr); + iio_device_unregister(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7280_id[] = { + {"ad7280a", 0}, + {} +}; + +static struct spi_driver ad7280_driver = { + .driver = { + .name = "ad7280", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ad7280_probe, + .remove = __devexit_p(ad7280_remove), + .id_table = ad7280_id, +}; + +static int __init ad7280_init(void) +{ + return spi_register_driver(&ad7280_driver); +} +module_init(ad7280_init); + +static void __exit ad7280_exit(void) +{ + spi_unregister_driver(&ad7280_driver); +} +module_exit(ad7280_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD7280A"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7280a.h b/drivers/staging/iio/adc/ad7280a.h new file mode 100644 index 000000000000..20400b0045e5 --- /dev/null +++ b/drivers/staging/iio/adc/ad7280a.h @@ -0,0 +1,38 @@ +/* + * AD7280A Lithium Ion Battery Monitoring System + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef IIO_ADC_AD7280_H_ +#define IIO_ADC_AD7280_H_ + +/* + * TODO: struct ad7280_platform_data needs to go into include/linux/iio + */ + +#define AD7280A_ACQ_TIME_400ns 0 +#define AD7280A_ACQ_TIME_800ns 1 +#define AD7280A_ACQ_TIME_1200ns 2 +#define AD7280A_ACQ_TIME_1600ns 3 + +#define AD7280A_CONV_AVG_DIS 0 +#define AD7280A_CONV_AVG_2 1 +#define AD7280A_CONV_AVG_4 2 +#define AD7280A_CONV_AVG_8 3 + +#define AD7280A_ALERT_REMOVE_VIN5 (1 << 2) +#define AD7280A_ALERT_REMOVE_VIN4_VIN5 (2 << 2) +#define AD7280A_ALERT_REMOVE_AUX5 (1 << 0) +#define AD7280A_ALERT_REMOVE_AUX4_AUX5 (2 << 0) + +struct ad7280_platform_data { + unsigned acquisition_time; + unsigned conversion_averaging; + unsigned chain_last_alert_ignore; + bool thermistor_term_en; +}; + +#endif /* IIO_ADC_AD7280_H_ */ diff --git a/drivers/staging/iio/adc/ad7291.c b/drivers/staging/iio/adc/ad7291.c index 96cbb17bc2cd..e884c78d8047 100644 --- a/drivers/staging/iio/adc/ad7291.c +++ b/drivers/staging/iio/adc/ad7291.c @@ -14,6 +14,7 @@ #include <linux/sysfs.h> #include <linux/list.h> #include <linux/i2c.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" diff --git a/drivers/staging/iio/adc/ad7298_core.c b/drivers/staging/iio/adc/ad7298_core.c index b8e4ae29b0b5..3e5bead26367 100644 --- a/drivers/staging/iio/adc/ad7298_core.c +++ b/drivers/staging/iio/adc/ad7298_core.c @@ -14,11 +14,11 @@ #include <linux/regulator/consumer.h> #include <linux/err.h> #include <linux/delay.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" #include "../ring_generic.h" -#include "adc.h" #include "ad7298.h" diff --git a/drivers/staging/iio/adc/ad7298_ring.c b/drivers/staging/iio/adc/ad7298_ring.c index a04c03352624..741818c0b829 100644 --- a/drivers/staging/iio/adc/ad7298_ring.c +++ b/drivers/staging/iio/adc/ad7298_ring.c @@ -7,17 +7,14 @@ */ #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 "../iio.h" #include "../ring_generic.h" #include "../ring_sw.h" -#include "../trigger.h" -#include "../sysfs.h" +#include "../trigger_consumer.h" #include "ad7298.h" @@ -120,7 +117,7 @@ static int ad7298_ring_preenable(struct iio_dev *indio_dev) static irqreturn_t ad7298_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->private_data; + struct iio_dev *indio_dev = pf->indio_dev; struct ad7298_state *st = iio_priv(indio_dev); struct iio_ring_buffer *ring = indio_dev->ring; s64 time_ns; @@ -192,11 +189,6 @@ error_ret: void ad7298_ring_cleanup(struct iio_dev *indio_dev) { - if (indio_dev->trig) { - iio_put_trigger(indio_dev->trig); - iio_trigger_dettach_poll_func(indio_dev->trig, - indio_dev->pollfunc); - } iio_dealloc_pollfunc(indio_dev->pollfunc); iio_sw_rb_free(indio_dev->ring); } diff --git a/drivers/staging/iio/adc/ad7314.c b/drivers/staging/iio/adc/ad7314.c index 9070d9cac725..e3d38dbd846f 100644 --- a/drivers/staging/iio/adc/ad7314.c +++ b/drivers/staging/iio/adc/ad7314.c @@ -11,6 +11,7 @@ #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/spi/spi.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" diff --git a/drivers/staging/iio/adc/ad7476_core.c b/drivers/staging/iio/adc/ad7476_core.c index c21089894d28..bb8be5daa6b8 100644 --- a/drivers/staging/iio/adc/ad7476_core.c +++ b/drivers/staging/iio/adc/ad7476_core.c @@ -13,11 +13,11 @@ #include <linux/spi/spi.h> #include <linux/regulator/consumer.h> #include <linux/err.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" #include "../ring_generic.h" -#include "adc.h" #include "ad7476.h" diff --git a/drivers/staging/iio/adc/ad7476_ring.c b/drivers/staging/iio/adc/ad7476_ring.c index a92fc5a1a604..b109afb89846 100644 --- a/drivers/staging/iio/adc/ad7476_ring.c +++ b/drivers/staging/iio/adc/ad7476_ring.c @@ -11,14 +11,12 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/slab.h> -#include <linux/sysfs.h> #include <linux/spi/spi.h> #include "../iio.h" #include "../ring_generic.h" #include "../ring_sw.h" -#include "../trigger.h" -#include "../sysfs.h" +#include "../trigger_consumer.h" #include "ad7476.h" @@ -78,7 +76,7 @@ static int ad7476_ring_preenable(struct iio_dev *indio_dev) static irqreturn_t ad7476_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->private_data; + struct iio_dev *indio_dev = pf->indio_dev; struct ad7476_state *st = iio_priv(indio_dev); s64 time_ns; __u8 *rxbuf; @@ -154,12 +152,6 @@ error_ret: void ad7476_ring_cleanup(struct iio_dev *indio_dev) { - /* ensure that the trigger has been detached */ - if (indio_dev->trig) { - iio_put_trigger(indio_dev->trig); - iio_trigger_dettach_poll_func(indio_dev->trig, - indio_dev->pollfunc); - } iio_dealloc_pollfunc(indio_dev->pollfunc); iio_sw_rb_free(indio_dev->ring); } diff --git a/drivers/staging/iio/adc/ad7606_core.c b/drivers/staging/iio/adc/ad7606_core.c index 459371ae4dcc..ef0558130ca7 100644 --- a/drivers/staging/iio/adc/ad7606_core.c +++ b/drivers/staging/iio/adc/ad7606_core.c @@ -16,11 +16,11 @@ #include <linux/gpio.h> #include <linux/delay.h> #include <linux/sched.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" #include "../ring_generic.h" -#include "adc.h" #include "ad7606.h" diff --git a/drivers/staging/iio/adc/ad7606_ring.c b/drivers/staging/iio/adc/ad7606_ring.c index a199bf48396c..e6424300c8e7 100644 --- a/drivers/staging/iio/adc/ad7606_ring.c +++ b/drivers/staging/iio/adc/ad7606_ring.c @@ -10,13 +10,11 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/slab.h> -#include <linux/sysfs.h> #include "../iio.h" #include "../ring_generic.h" #include "../ring_sw.h" -#include "../trigger.h" -#include "../sysfs.h" +#include "../trigger_consumer.h" #include "ad7606.h" @@ -82,8 +80,7 @@ static int ad7606_ring_preenable(struct iio_dev *indio_dev) static irqreturn_t ad7606_trigger_handler_th_bh(int irq, void *p) { struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->private_data; - struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_state *st = iio_priv(pf->indio_dev); gpio_set_value(st->pdata->gpio_convst, 1); @@ -200,11 +197,6 @@ error_ret: void ad7606_ring_cleanup(struct iio_dev *indio_dev) { - if (indio_dev->trig) { - iio_put_trigger(indio_dev->trig); - iio_trigger_dettach_poll_func(indio_dev->trig, - indio_dev->pollfunc); - } iio_dealloc_pollfunc(indio_dev->pollfunc); iio_sw_rb_free(indio_dev->ring); } diff --git a/drivers/staging/iio/adc/ad7745.c b/drivers/staging/iio/adc/ad7745.c index 4c13f26aa9ae..91d598c1a14b 100644 --- a/drivers/staging/iio/adc/ad7745.c +++ b/drivers/staging/iio/adc/ad7745.c @@ -14,6 +14,7 @@ #include <linux/sysfs.h> #include <linux/list.h> #include <linux/i2c.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c index e0c7b6cc05c7..46d1f8f3fe82 100644 --- a/drivers/staging/iio/adc/ad7780.c +++ b/drivers/staging/iio/adc/ad7780.c @@ -16,11 +16,11 @@ #include <linux/err.h> #include <linux/sched.h> #include <linux/gpio.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" #include "../ring_generic.h" -#include "adc.h" #include "ad7780.h" diff --git a/drivers/staging/iio/adc/ad7793.c b/drivers/staging/iio/adc/ad7793.c index 90f6c039d6c6..1bcb3ebb1bc6 100644 --- a/drivers/staging/iio/adc/ad7793.c +++ b/drivers/staging/iio/adc/ad7793.c @@ -22,7 +22,7 @@ #include "../ring_generic.h" #include "../ring_sw.h" #include "../trigger.h" -#include "adc.h" +#include "../trigger_consumer.h" #include "ad7793.h" @@ -402,7 +402,7 @@ static int ad7793_ring_postdisable(struct iio_dev *indio_dev) static irqreturn_t ad7793_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->private_data; + struct iio_dev *indio_dev = pf->indio_dev; struct iio_ring_buffer *ring = indio_dev->ring; struct ad7793_state *st = iio_priv(indio_dev); s64 dat64[2]; @@ -470,12 +470,6 @@ error_ret: static void ad7793_ring_cleanup(struct iio_dev *indio_dev) { - /* ensure that the trigger has been detached */ - if (indio_dev->trig) { - iio_put_trigger(indio_dev->trig); - iio_trigger_dettach_poll_func(indio_dev->trig, - indio_dev->pollfunc); - } iio_dealloc_pollfunc(indio_dev->pollfunc); iio_sw_rb_free(indio_dev->ring); } diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c index 0c84217bde3d..f5aa57bb8f23 100644 --- a/drivers/staging/iio/adc/ad7816.c +++ b/drivers/staging/iio/adc/ad7816.c @@ -14,6 +14,7 @@ #include <linux/sysfs.h> #include <linux/list.h> #include <linux/spi/spi.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" @@ -246,7 +247,7 @@ static const struct attribute_group ad7816_attribute_group = { * temperature bound events */ -#define IIO_EVENT_CODE_AD7816_OTI IIO_UNMOD_EVENT_CODE(IIO_EV_CLASS_TEMP, \ +#define IIO_EVENT_CODE_AD7816_OTI IIO_UNMOD_EVENT_CODE(IIO_TEMP, \ 0, \ IIO_EV_TYPE_THRESH, \ IIO_EV_DIR_FALLING) diff --git a/drivers/staging/iio/adc/ad7887_core.c b/drivers/staging/iio/adc/ad7887_core.c index 3d9121e5c37d..74e222f92431 100644 --- a/drivers/staging/iio/adc/ad7887_core.c +++ b/drivers/staging/iio/adc/ad7887_core.c @@ -13,11 +13,12 @@ #include <linux/spi/spi.h> #include <linux/regulator/consumer.h> #include <linux/err.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" #include "../ring_generic.h" -#include "adc.h" + #include "ad7887.h" diff --git a/drivers/staging/iio/adc/ad7887_ring.c b/drivers/staging/iio/adc/ad7887_ring.c index 0ac7c0b9d71d..6b3daf41e0c6 100644 --- a/drivers/staging/iio/adc/ad7887_ring.c +++ b/drivers/staging/iio/adc/ad7887_ring.c @@ -8,17 +8,14 @@ */ #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 "../iio.h" #include "../ring_generic.h" #include "../ring_sw.h" -#include "../trigger.h" -#include "../sysfs.h" +#include "../trigger_consumer.h" #include "ad7887.h" @@ -115,7 +112,7 @@ static int ad7887_ring_postdisable(struct iio_dev *indio_dev) static irqreturn_t ad7887_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->private_data; + struct iio_dev *indio_dev = pf->indio_dev; struct ad7887_state *st = iio_priv(indio_dev); struct iio_ring_buffer *ring = indio_dev->ring; s64 time_ns; @@ -191,12 +188,6 @@ error_ret: void ad7887_ring_cleanup(struct iio_dev *indio_dev) { - /* ensure that the trigger has been detached */ - if (indio_dev->trig) { - iio_put_trigger(indio_dev->trig); - iio_trigger_dettach_poll_func(indio_dev->trig, - indio_dev->pollfunc); - } iio_dealloc_pollfunc(indio_dev->pollfunc); iio_sw_rb_free(indio_dev->ring); } diff --git a/drivers/staging/iio/adc/ad799x_core.c b/drivers/staging/iio/adc/ad799x_core.c index 92cfe2e3ea4a..33e4e2197143 100644 --- a/drivers/staging/iio/adc/ad799x_core.c +++ b/drivers/staging/iio/adc/ad799x_core.c @@ -31,12 +31,12 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/err.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" - #include "../ring_generic.h" -#include "adc.h" + #include "ad799x.h" /* @@ -336,8 +336,14 @@ static irqreturn_t ad799x_event_handler(int irq, void *private) if (status & (1 << i)) iio_push_event(indio_dev, 0, i & 0x1 ? - IIO_EVENT_CODE_IN_HIGH_THRESH(i >> 1) : - IIO_EVENT_CODE_IN_LOW_THRESH(i >> 1), + IIO_UNMOD_EVENT_CODE(IIO_IN, + (i >> 1), + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING) : + IIO_UNMOD_EVENT_CODE(IIO_IN, + (i >> 1), + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), iio_get_time_ns()); } diff --git a/drivers/staging/iio/adc/ad799x_ring.c b/drivers/staging/iio/adc/ad799x_ring.c index 0376a826c26c..8f1fc52560f1 100644 --- a/drivers/staging/iio/adc/ad799x_ring.c +++ b/drivers/staging/iio/adc/ad799x_ring.c @@ -10,10 +10,8 @@ */ #include <linux/interrupt.h> -#include <linux/device.h> #include <linux/slab.h> #include <linux/kernel.h> -#include <linux/sysfs.h> #include <linux/list.h> #include <linux/i2c.h> #include <linux/bitops.h> @@ -21,8 +19,7 @@ #include "../iio.h" #include "../ring_generic.h" #include "../ring_sw.h" -#include "../trigger.h" -#include "../sysfs.h" +#include "../trigger_consumer.h" #include "ad799x.h" @@ -108,7 +105,7 @@ static int ad799x_ring_preenable(struct iio_dev *indio_dev) static irqreturn_t ad799x_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->private_data; + struct iio_dev *indio_dev = pf->indio_dev; struct ad799x_state *st = iio_priv(indio_dev); struct iio_ring_buffer *ring = indio_dev->ring; s64 time_ns; @@ -207,12 +204,6 @@ error_ret: void ad799x_ring_cleanup(struct iio_dev *indio_dev) { - /* ensure that the trigger has been detached */ - if (indio_dev->trig) { - iio_put_trigger(indio_dev->trig); - iio_trigger_dettach_poll_func(indio_dev->trig, - indio_dev->pollfunc); - } iio_dealloc_pollfunc(indio_dev->pollfunc); iio_sw_rb_free(indio_dev->ring); } diff --git a/drivers/staging/iio/adc/adc.h b/drivers/staging/iio/adc/adc.h deleted file mode 100644 index 40c5949880b4..000000000000 --- a/drivers/staging/iio/adc/adc.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * adc.h - sysfs attributes associated with ADCs - * - * 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. - * - * Copyright (c) 2008 Jonathan Cameron <jic23@cam.ac.uk> - * - */ - -/* Deprecated */ -#define IIO_DEV_ATTR_ADC(_num, _show, _addr) \ - IIO_DEVICE_ATTR(adc_##_num, S_IRUGO, _show, NULL, _addr) - -#define IIO_DEV_ATTR_IN_RAW(_num, _show, _addr) \ - IIO_DEVICE_ATTR(in##_num##_raw, S_IRUGO, _show, NULL, _addr) - -#define IIO_DEV_ATTR_IN_NAMED_RAW(_num, _name, _show, _addr) \ - IIO_DEVICE_ATTR(in##_num##_##_name##_raw, S_IRUGO, _show, NULL, _addr) - -#define IIO_DEV_ATTR_IN_DIFF_RAW(_nump, _numn, _show, _addr) \ - IIO_DEVICE_ATTR_NAMED(in##_nump##min##_numn##_raw, \ - in##_nump-in##_numn##_raw, \ - S_IRUGO, \ - _show, \ - NULL, \ - _addr) - - -#define IIO_CONST_ATTR_IN_NAMED_OFFSET(_num, _name, _string) \ - IIO_CONST_ATTR(in##_num##_##_name##_offset, _string) - -#define IIO_CONST_ATTR_IN_NAMED_SCALE(_num, _name, _string) \ - IIO_CONST_ATTR(in##_num##_##_name##_scale, _string) - -#define IIO_EVENT_CODE_IN_HIGH_THRESH(a) \ - IIO_UNMOD_EVENT_CODE(IIO_EV_CLASS_IN, a, IIO_EV_TYPE_THRESH, \ - IIO_EV_DIR_RISING) -#define IIO_EVENT_CODE_IN_LOW_THRESH(a) \ - IIO_UNMOD_EVENT_CODE(IIO_EV_CLASS_IN, a, IIO_EV_TYPE_THRESH, \ - IIO_EV_DIR_FALLING) diff --git a/drivers/staging/iio/adc/adt7310.c b/drivers/staging/iio/adc/adt7310.c index 1a41b8034405..697ad1b91954 100644 --- a/drivers/staging/iio/adc/adt7310.c +++ b/drivers/staging/iio/adc/adt7310.c @@ -13,6 +13,7 @@ #include <linux/sysfs.h> #include <linux/list.h> #include <linux/spi/spi.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" diff --git a/drivers/staging/iio/adc/adt7410.c b/drivers/staging/iio/adc/adt7410.c index 76aa0639a55a..b7631f18ee2d 100644 --- a/drivers/staging/iio/adc/adt7410.c +++ b/drivers/staging/iio/adc/adt7410.c @@ -13,6 +13,7 @@ #include <linux/sysfs.h> #include <linux/list.h> #include <linux/i2c.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" diff --git a/drivers/staging/iio/adc/adt75.c b/drivers/staging/iio/adc/adt75.c index 38f141de6a4b..dd782776ce34 100644 --- a/drivers/staging/iio/adc/adt75.c +++ b/drivers/staging/iio/adc/adt75.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/i2c.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" @@ -257,7 +258,7 @@ static const struct attribute_group adt75_attribute_group = { * temperature bound events */ -#define IIO_EVENT_CODE_ADT75_OTI IIO_UNMOD_EVENT_CODE(IIO_EV_CLASS_TEMP, \ +#define IIO_EVENT_CODE_ADT75_OTI IIO_UNMOD_EVENT_CODE(IIO_TEMP, \ 0, \ IIO_EV_TYPE_THRESH, \ IIO_EV_DIR_FALLING) diff --git a/drivers/staging/iio/adc/max1363_core.c b/drivers/staging/iio/adc/max1363_core.c index 72b0917412ee..e10a4d828146 100644 --- a/drivers/staging/iio/adc/max1363_core.c +++ b/drivers/staging/iio/adc/max1363_core.c @@ -30,12 +30,12 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/err.h> +#include <linux/module.h> #include "../iio.h" #include "../sysfs.h" - #include "../ring_generic.h" -#include "adc.h" + #include "max1363.h" #define MAX1363_MODE_SINGLE(_num, _mask) { \ @@ -255,7 +255,7 @@ static int max1363_read_raw(struct iio_dev *indio_dev, switch (m) { case 0: ret = max1363_read_single_chan(indio_dev, chan, val, m); - if (ret) + if (ret < 0) return ret; return IIO_VAL_INT; case (1 << IIO_CHAN_INFO_SCALE_SHARED): @@ -592,10 +592,14 @@ static int max1363_write_thresh(struct iio_dev *indio_dev, } static const int max1363_event_codes[] = { - IIO_EVENT_CODE_IN_LOW_THRESH(3), IIO_EVENT_CODE_IN_HIGH_THRESH(3), - IIO_EVENT_CODE_IN_LOW_THRESH(2), IIO_EVENT_CODE_IN_HIGH_THRESH(2), - IIO_EVENT_CODE_IN_LOW_THRESH(1), IIO_EVENT_CODE_IN_HIGH_THRESH(1), - IIO_EVENT_CODE_IN_LOW_THRESH(0), IIO_EVENT_CODE_IN_HIGH_THRESH(0) + IIO_UNMOD_EVENT_CODE(IIO_IN, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_IN, 1, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_IN, 2, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_IN, 3, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_IN, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_IN, 1, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_IN, 2, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_IN, 3, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), }; static irqreturn_t max1363_event_handler(int irq, void *private) @@ -1295,6 +1299,8 @@ static int __devinit max1363_probe(struct i2c_client *client, 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; diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c index f43befd1f776..be601e07639d 100644 --- a/drivers/staging/iio/adc/max1363_ring.c +++ b/drivers/staging/iio/adc/max1363_ring.c @@ -9,19 +9,15 @@ */ #include <linux/interrupt.h> -#include <linux/device.h> #include <linux/slab.h> #include <linux/kernel.h> -#include <linux/sysfs.h> -#include <linux/list.h> #include <linux/i2c.h> #include <linux/bitops.h> #include "../iio.h" #include "../ring_generic.h" #include "../ring_sw.h" -#include "../trigger.h" -#include "../sysfs.h" +#include "../trigger_consumer.h" #include "max1363.h" @@ -108,7 +104,7 @@ static int max1363_ring_preenable(struct iio_dev *indio_dev) static irqreturn_t max1363_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->private_data; + struct iio_dev *indio_dev = pf->indio_dev; struct max1363_state *st = iio_priv(indio_dev); s64 time_ns; __u8 *rxbuf; @@ -199,11 +195,6 @@ error_ret: void max1363_ring_cleanup(struct iio_dev *indio_dev) { /* ensure that the trigger has been detached */ - if (indio_dev->trig) { - iio_put_trigger(indio_dev->trig); - iio_trigger_dettach_poll_func(indio_dev->trig, - indio_dev->pollfunc); - } iio_dealloc_pollfunc(indio_dev->pollfunc); iio_sw_rb_free(indio_dev->ring); } |