mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-29 15:28:50 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
302
drivers/iio/adc/Kconfig
Normal file
302
drivers/iio/adc/Kconfig
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
#
|
||||
# ADC drivers
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Analog to digital converters"
|
||||
|
||||
config AD_SIGMA_DELTA
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
|
||||
config AD7266
|
||||
tristate "Analog Devices AD7265/AD7266 ADC driver"
|
||||
depends on SPI_MASTER
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7265 and AD7266
|
||||
ADCs.
|
||||
|
||||
config AD7291
|
||||
tristate "Analog Devices AD7291 ADC driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7291
|
||||
8 Channel ADC with temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7291.
|
||||
|
||||
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 AD7476
|
||||
tristate "Analog Devices AD7476 and similar 1-channel ADCs driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7273, AD7274, AD7276,
|
||||
AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
|
||||
AD7495, AD7910, AD7920, AD7920 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 ad7476.
|
||||
|
||||
config AD7791
|
||||
tristate "Analog Devices AD7791 ADC driver"
|
||||
depends on SPI
|
||||
select AD_SIGMA_DELTA
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7787, AD7788, AD7789,
|
||||
AD7790 and AD7791 SPI analog to digital converters (ADC). If unsure, say
|
||||
N (but it is safe to say "Y").
|
||||
|
||||
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 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 AD7923
|
||||
tristate "Analog Devices AD7923 and similar ADCs driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices
|
||||
AD7904, AD7914, AD7923, AD7924 4 Channel ADCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7923.
|
||||
|
||||
config AD799X
|
||||
tristate "Analog Devices AD799x ADC driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices:
|
||||
ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, ad7998
|
||||
i2c analog to digital converters (ADC). Provides direct access
|
||||
via sysfs.
|
||||
|
||||
config AT91_ADC
|
||||
tristate "Atmel AT91 ADC"
|
||||
depends on ARCH_AT91
|
||||
depends on INPUT
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select SYSFS
|
||||
help
|
||||
Say yes here to build support for Atmel AT91 ADC.
|
||||
|
||||
config EXYNOS_ADC
|
||||
tristate "Exynos ADC driver support"
|
||||
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
|
||||
help
|
||||
Core support for the ADC block found in the Samsung EXYNOS series
|
||||
of SoCs for drivers such as the touchscreen and hwmon to use to share
|
||||
this resource.
|
||||
|
||||
config LP8788_ADC
|
||||
tristate "LP8788 ADC driver"
|
||||
depends on MFD_LP8788
|
||||
help
|
||||
Say yes here to build support for TI LP8788 ADC.
|
||||
|
||||
config MAX1027
|
||||
tristate "Maxim max1027 ADC driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Maxim SPI ADC models
|
||||
max1027, max1029 and max1031.
|
||||
|
||||
config MAX1363
|
||||
tristate "Maxim max1363 ADC driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
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 MCP320X
|
||||
tristate "Microchip Technology MCP3204/08"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Microchip Technology's MCP3204 or
|
||||
MCP3208 analog to digital converter.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp320x.
|
||||
|
||||
config MCP3422
|
||||
tristate "Microchip Technology MCP3422/3/4/6/7/8 driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Microchip Technology's
|
||||
MCP3422, MCP3423, MCP3424, MCP3426, MCP3427 or MCP3428
|
||||
analog to digital converters.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp3422.
|
||||
|
||||
config MEN_Z188_ADC
|
||||
tristate "MEN 16z188 ADC IP Core support"
|
||||
depends on MCB
|
||||
help
|
||||
Say yes here to enable support for the MEN 16z188 ADC IP-Core on a MCB
|
||||
carrier.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called men_z188_adc.
|
||||
|
||||
config NAU7802
|
||||
tristate "Nuvoton NAU7802 ADC driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Nuvoton NAU7802 ADC.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called nau7802.
|
||||
|
||||
config ROCKCHIP_SARADC
|
||||
tristate "Rockchip SARADC driver"
|
||||
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
|
||||
help
|
||||
Say yes here to build support for the SARADC found in SoCs from
|
||||
Rockchip.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rockchip_saradc.
|
||||
|
||||
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.
|
||||
|
||||
config TI_ADC128S052
|
||||
tristate "Texas Instruments ADC128S052"
|
||||
depends on SPI
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADC128S052
|
||||
chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc128s052.
|
||||
|
||||
config TI_AM335X_ADC
|
||||
tristate "TI's AM335X ADC driver"
|
||||
depends on MFD_TI_AM335X_TSCADC
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
help
|
||||
Say yes here to build support for Texas Instruments ADC
|
||||
driver which is also a MFD client.
|
||||
|
||||
config TWL4030_MADC
|
||||
tristate "TWL4030 MADC (Monitoring A/D Converter)"
|
||||
depends on TWL4030_CORE
|
||||
help
|
||||
This driver provides support for Triton TWL4030-MADC. The
|
||||
driver supports both RT and SW conversion methods.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called twl4030-madc.
|
||||
|
||||
config TWL6030_GPADC
|
||||
tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"
|
||||
depends on TWL4030_CORE
|
||||
default n
|
||||
help
|
||||
Say yes here if you want support for the TWL6030/TWL6032 General
|
||||
Purpose A/D Converter. This will add support for battery type
|
||||
detection, battery voltage and temperature measurement, die
|
||||
temperature measurement, system supply voltage, audio accessory,
|
||||
USB ID detection.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called twl6030-gpadc.
|
||||
|
||||
config VF610_ADC
|
||||
tristate "Freescale vf610 ADC driver"
|
||||
depends on OF
|
||||
help
|
||||
Say yes here to support for Vybrid board analog-to-digital converter.
|
||||
Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called vf610_adc.
|
||||
|
||||
config VIPERBOARD_ADC
|
||||
tristate "Viperboard ADC support"
|
||||
depends on MFD_VIPERBOARD && USB
|
||||
help
|
||||
Say yes here to access the ADC part of the Nano River
|
||||
Technologies Viperboard.
|
||||
|
||||
config XILINX_XADC
|
||||
tristate "Xilinx XADC driver"
|
||||
depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to have support for the Xilinx XADC. The driver does support
|
||||
both the ZYNQ interface to the XADC as well as the AXI-XADC interface.
|
||||
|
||||
The driver can also be build as a module. If so, the module will be called
|
||||
xilinx-xadc.
|
||||
|
||||
endmenu
|
||||
34
drivers/iio/adc/Makefile
Normal file
34
drivers/iio/adc/Makefile
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#
|
||||
# Makefile for IIO ADC drivers
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
|
||||
obj-$(CONFIG_AD7266) += ad7266.o
|
||||
obj-$(CONFIG_AD7291) += ad7291.o
|
||||
obj-$(CONFIG_AD7298) += ad7298.o
|
||||
obj-$(CONFIG_AD7923) += ad7923.o
|
||||
obj-$(CONFIG_AD7476) += ad7476.o
|
||||
obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AD7793) += ad7793.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
obj-$(CONFIG_AD799X) += ad799x.o
|
||||
obj-$(CONFIG_AT91_ADC) += at91_adc.o
|
||||
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_MAX1027) += max1027.o
|
||||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
|
||||
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
|
||||
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
|
||||
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
|
||||
xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
|
||||
obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
|
||||
522
drivers/iio/adc/ad7266.c
Normal file
522
drivers/iio/adc/ad7266.c
Normal file
|
|
@ -0,0 +1,522 @@
|
|||
/*
|
||||
* AD7266/65 SPI ADC driver
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/interrupt.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/platform_data/ad7266.h>
|
||||
|
||||
struct ad7266_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
unsigned long vref_mv;
|
||||
|
||||
struct spi_transfer single_xfer[3];
|
||||
struct spi_message single_msg;
|
||||
|
||||
enum ad7266_range range;
|
||||
enum ad7266_mode mode;
|
||||
bool fixed_addr;
|
||||
struct gpio gpios[3];
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
* The buffer needs to be large enough to hold two samples (4 bytes) and
|
||||
* the naturally aligned timestamp (8 bytes).
|
||||
*/
|
||||
struct {
|
||||
__be16 sample[2];
|
||||
s64 timestamp;
|
||||
} data ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ad7266_wakeup(struct ad7266_state *st)
|
||||
{
|
||||
/* Any read with >= 2 bytes will wake the device */
|
||||
return spi_read(st->spi, &st->data.sample[0], 2);
|
||||
}
|
||||
|
||||
static int ad7266_powerdown(struct ad7266_state *st)
|
||||
{
|
||||
/* Any read with < 2 bytes will powerdown the device */
|
||||
return spi_read(st->spi, &st->data.sample[0], 1);
|
||||
}
|
||||
|
||||
static int ad7266_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
return ad7266_wakeup(st);
|
||||
}
|
||||
|
||||
static int ad7266_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
return ad7266_powerdown(st);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
|
||||
.preenable = &ad7266_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
.postdisable = &ad7266_postdisable,
|
||||
};
|
||||
|
||||
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 ad7266_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_read(st->spi, st->data.sample, 4);
|
||||
if (ret == 0) {
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &st->data,
|
||||
pf->timestamp);
|
||||
}
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad7266_select_input(struct ad7266_state *st, unsigned int nr)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (st->fixed_addr)
|
||||
return;
|
||||
|
||||
switch (st->mode) {
|
||||
case AD7266_MODE_SINGLE_ENDED:
|
||||
nr >>= 1;
|
||||
break;
|
||||
case AD7266_MODE_PSEUDO_DIFF:
|
||||
nr |= 1;
|
||||
break;
|
||||
case AD7266_MODE_DIFF:
|
||||
nr &= ~1;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
gpio_set_value(st->gpios[i].gpio, (bool)(nr & BIT(i)));
|
||||
}
|
||||
|
||||
static int ad7266_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
unsigned int nr = find_first_bit(scan_mask, indio_dev->masklength);
|
||||
|
||||
ad7266_select_input(st, nr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7266_read_single(struct ad7266_state *st, int *val,
|
||||
unsigned int address)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ad7266_select_input(st, address);
|
||||
|
||||
ret = spi_sync(st->spi, &st->single_msg);
|
||||
*val = be16_to_cpu(st->data.sample[address % 2]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7266_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long m)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
unsigned long scale_mv;
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
ret = ad7266_read_single(st, val, chan->address);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = (*val >> 2) & 0xfff;
|
||||
if (chan->scan_type.sign == 's')
|
||||
*val = sign_extend32(*val, 11);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_mv = st->vref_mv;
|
||||
if (st->mode == AD7266_MODE_DIFF)
|
||||
scale_mv *= 2;
|
||||
if (st->range == AD7266_RANGE_2VREF)
|
||||
scale_mv *= 2;
|
||||
|
||||
*val = scale_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (st->range == AD7266_RANGE_2VREF &&
|
||||
st->mode != AD7266_MODE_DIFF)
|
||||
*val = 2048;
|
||||
else
|
||||
*val = 0;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define AD7266_CHAN(_chan, _sign) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan), \
|
||||
.address = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
| BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_index = (_chan), \
|
||||
.scan_type = { \
|
||||
.sign = (_sign), \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 2, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS(_name, _sign) \
|
||||
const struct iio_chan_spec ad7266_channels_##_name[] = { \
|
||||
AD7266_CHAN(0, (_sign)), \
|
||||
AD7266_CHAN(1, (_sign)), \
|
||||
AD7266_CHAN(2, (_sign)), \
|
||||
AD7266_CHAN(3, (_sign)), \
|
||||
AD7266_CHAN(4, (_sign)), \
|
||||
AD7266_CHAN(5, (_sign)), \
|
||||
AD7266_CHAN(6, (_sign)), \
|
||||
AD7266_CHAN(7, (_sign)), \
|
||||
AD7266_CHAN(8, (_sign)), \
|
||||
AD7266_CHAN(9, (_sign)), \
|
||||
AD7266_CHAN(10, (_sign)), \
|
||||
AD7266_CHAN(11, (_sign)), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(13), \
|
||||
}
|
||||
|
||||
#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(_name, _sign) \
|
||||
const struct iio_chan_spec ad7266_channels_##_name##_fixed[] = { \
|
||||
AD7266_CHAN(0, (_sign)), \
|
||||
AD7266_CHAN(1, (_sign)), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2), \
|
||||
}
|
||||
|
||||
static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(u, 'u');
|
||||
static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(s, 's');
|
||||
static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(u, 'u');
|
||||
static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(s, 's');
|
||||
|
||||
#define AD7266_CHAN_DIFF(_chan, _sign) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan) * 2, \
|
||||
.channel2 = (_chan) * 2 + 1, \
|
||||
.address = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
| BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_index = (_chan), \
|
||||
.scan_type = { \
|
||||
.sign = _sign, \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 2, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
.differential = 1, \
|
||||
}
|
||||
|
||||
#define AD7266_DECLARE_DIFF_CHANNELS(_name, _sign) \
|
||||
const struct iio_chan_spec ad7266_channels_diff_##_name[] = { \
|
||||
AD7266_CHAN_DIFF(0, (_sign)), \
|
||||
AD7266_CHAN_DIFF(1, (_sign)), \
|
||||
AD7266_CHAN_DIFF(2, (_sign)), \
|
||||
AD7266_CHAN_DIFF(3, (_sign)), \
|
||||
AD7266_CHAN_DIFF(4, (_sign)), \
|
||||
AD7266_CHAN_DIFF(5, (_sign)), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(6), \
|
||||
}
|
||||
|
||||
static AD7266_DECLARE_DIFF_CHANNELS(s, 's');
|
||||
static AD7266_DECLARE_DIFF_CHANNELS(u, 'u');
|
||||
|
||||
#define AD7266_DECLARE_DIFF_CHANNELS_FIXED(_name, _sign) \
|
||||
const struct iio_chan_spec ad7266_channels_diff_fixed_##_name[] = { \
|
||||
AD7266_CHAN_DIFF(0, (_sign)), \
|
||||
AD7266_CHAN_DIFF(1, (_sign)), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2), \
|
||||
}
|
||||
|
||||
static AD7266_DECLARE_DIFF_CHANNELS_FIXED(s, 's');
|
||||
static AD7266_DECLARE_DIFF_CHANNELS_FIXED(u, 'u');
|
||||
|
||||
static const struct iio_info ad7266_info = {
|
||||
.read_raw = &ad7266_read_raw,
|
||||
.update_scan_mode = &ad7266_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const unsigned long ad7266_available_scan_masks[] = {
|
||||
0x003,
|
||||
0x00c,
|
||||
0x030,
|
||||
0x0c0,
|
||||
0x300,
|
||||
0xc00,
|
||||
0x000,
|
||||
};
|
||||
|
||||
static const unsigned long ad7266_available_scan_masks_diff[] = {
|
||||
0x003,
|
||||
0x00c,
|
||||
0x030,
|
||||
0x000,
|
||||
};
|
||||
|
||||
static const unsigned long ad7266_available_scan_masks_fixed[] = {
|
||||
0x003,
|
||||
0x000,
|
||||
};
|
||||
|
||||
struct ad7266_chan_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
const unsigned long *scan_masks;
|
||||
};
|
||||
|
||||
#define AD7266_CHAN_INFO_INDEX(_differential, _signed, _fixed) \
|
||||
(((_differential) << 2) | ((_signed) << 1) | ((_fixed) << 0))
|
||||
|
||||
static const struct ad7266_chan_info ad7266_chan_infos[] = {
|
||||
[AD7266_CHAN_INFO_INDEX(0, 0, 0)] = {
|
||||
.channels = ad7266_channels_u,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_u),
|
||||
.scan_masks = ad7266_available_scan_masks,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(0, 0, 1)] = {
|
||||
.channels = ad7266_channels_u_fixed,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_u_fixed),
|
||||
.scan_masks = ad7266_available_scan_masks_fixed,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(0, 1, 0)] = {
|
||||
.channels = ad7266_channels_s,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_s),
|
||||
.scan_masks = ad7266_available_scan_masks,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(0, 1, 1)] = {
|
||||
.channels = ad7266_channels_s_fixed,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_s_fixed),
|
||||
.scan_masks = ad7266_available_scan_masks_fixed,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(1, 0, 0)] = {
|
||||
.channels = ad7266_channels_diff_u,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_diff_u),
|
||||
.scan_masks = ad7266_available_scan_masks_diff,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(1, 0, 1)] = {
|
||||
.channels = ad7266_channels_diff_fixed_u,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_u),
|
||||
.scan_masks = ad7266_available_scan_masks_fixed,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(1, 1, 0)] = {
|
||||
.channels = ad7266_channels_diff_s,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_diff_s),
|
||||
.scan_masks = ad7266_available_scan_masks_diff,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(1, 1, 1)] = {
|
||||
.channels = ad7266_channels_diff_fixed_s,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_s),
|
||||
.scan_masks = ad7266_available_scan_masks_fixed,
|
||||
},
|
||||
};
|
||||
|
||||
static void ad7266_init_channels(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
bool is_differential, is_signed;
|
||||
const struct ad7266_chan_info *chan_info;
|
||||
int i;
|
||||
|
||||
is_differential = st->mode != AD7266_MODE_SINGLE_ENDED;
|
||||
is_signed = (st->range == AD7266_RANGE_2VREF) |
|
||||
(st->mode == AD7266_MODE_DIFF);
|
||||
|
||||
i = AD7266_CHAN_INFO_INDEX(is_differential, is_signed, st->fixed_addr);
|
||||
chan_info = &ad7266_chan_infos[i];
|
||||
|
||||
indio_dev->channels = chan_info->channels;
|
||||
indio_dev->num_channels = chan_info->num_channels;
|
||||
indio_dev->available_scan_masks = chan_info->scan_masks;
|
||||
indio_dev->masklength = chan_info->num_channels - 1;
|
||||
}
|
||||
|
||||
static const char * const ad7266_gpio_labels[] = {
|
||||
"AD0", "AD1", "AD2",
|
||||
};
|
||||
|
||||
static int ad7266_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7266_platform_data *pdata = spi->dev.platform_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad7266_state *st;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (!IS_ERR_OR_NULL(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->vref_mv = ret / 1000;
|
||||
} else {
|
||||
/* Use internal reference */
|
||||
st->vref_mv = 2500;
|
||||
}
|
||||
|
||||
if (pdata) {
|
||||
st->fixed_addr = pdata->fixed_addr;
|
||||
st->mode = pdata->mode;
|
||||
st->range = pdata->range;
|
||||
|
||||
if (!st->fixed_addr) {
|
||||
for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) {
|
||||
st->gpios[i].gpio = pdata->addr_gpios[i];
|
||||
st->gpios[i].flags = GPIOF_OUT_INIT_LOW;
|
||||
st->gpios[i].label = ad7266_gpio_labels[i];
|
||||
}
|
||||
ret = gpio_request_array(st->gpios,
|
||||
ARRAY_SIZE(st->gpios));
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
}
|
||||
} else {
|
||||
st->fixed_addr = true;
|
||||
st->range = AD7266_RANGE_VREF;
|
||||
st->mode = AD7266_MODE_DIFF;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->spi = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &ad7266_info;
|
||||
|
||||
ad7266_init_channels(indio_dev);
|
||||
|
||||
/* wakeup */
|
||||
st->single_xfer[0].rx_buf = &st->data.sample[0];
|
||||
st->single_xfer[0].len = 2;
|
||||
st->single_xfer[0].cs_change = 1;
|
||||
/* conversion */
|
||||
st->single_xfer[1].rx_buf = st->data.sample;
|
||||
st->single_xfer[1].len = 4;
|
||||
st->single_xfer[1].cs_change = 1;
|
||||
/* powerdown */
|
||||
st->single_xfer[2].tx_buf = &st->data.sample[0];
|
||||
st->single_xfer[2].len = 1;
|
||||
|
||||
spi_message_init(&st->single_msg);
|
||||
spi_message_add_tail(&st->single_xfer[0], &st->single_msg);
|
||||
spi_message_add_tail(&st->single_xfer[1], &st->single_msg);
|
||||
spi_message_add_tail(&st->single_xfer[2], &st->single_msg);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&ad7266_trigger_handler, &iio_triggered_buffer_setup_ops);
|
||||
if (ret)
|
||||
goto error_free_gpios;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_buffer_cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
error_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_free_gpios:
|
||||
if (!st->fixed_addr)
|
||||
gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
|
||||
error_disable_reg:
|
||||
if (!IS_ERR_OR_NULL(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7266_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (!st->fixed_addr)
|
||||
gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
|
||||
if (!IS_ERR_OR_NULL(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7266_id[] = {
|
||||
{"ad7265", 0},
|
||||
{"ad7266", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7266_id);
|
||||
|
||||
static struct spi_driver ad7266_driver = {
|
||||
.driver = {
|
||||
.name = "ad7266",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7266_probe,
|
||||
.remove = ad7266_remove,
|
||||
.id_table = ad7266_id,
|
||||
};
|
||||
module_spi_driver(ad7266_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7266/65 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
585
drivers/iio/adc/ad7291.c
Normal file
585
drivers/iio/adc/ad7291.c
Normal file
|
|
@ -0,0 +1,585 @@
|
|||
/*
|
||||
* AD7291 8-Channel, I2C, 12-Bit SAR ADC with Temperature Sensor
|
||||
*
|
||||
* Copyright 2010-2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
|
||||
#include <linux/platform_data/ad7291.h>
|
||||
|
||||
/*
|
||||
* Simplified handling
|
||||
*
|
||||
* If no events enabled - single polled channel read
|
||||
* If event enabled direct reads disable unless channel
|
||||
* is in the read mask.
|
||||
*
|
||||
* The noise-delayed bit as per datasheet suggestion is always enabled.
|
||||
*/
|
||||
|
||||
/*
|
||||
* AD7291 registers definition
|
||||
*/
|
||||
#define AD7291_COMMAND 0x00
|
||||
#define AD7291_VOLTAGE 0x01
|
||||
#define AD7291_T_SENSE 0x02
|
||||
#define AD7291_T_AVERAGE 0x03
|
||||
#define AD7291_DATA_HIGH(x) ((x) * 3 + 0x4)
|
||||
#define AD7291_DATA_LOW(x) ((x) * 3 + 0x5)
|
||||
#define AD7291_HYST(x) ((x) * 3 + 0x6)
|
||||
#define AD7291_VOLTAGE_ALERT_STATUS 0x1F
|
||||
#define AD7291_T_ALERT_STATUS 0x20
|
||||
|
||||
#define AD7291_BITS 12
|
||||
#define AD7291_VOLTAGE_LIMIT_COUNT 8
|
||||
|
||||
|
||||
/*
|
||||
* AD7291 command
|
||||
*/
|
||||
#define AD7291_AUTOCYCLE BIT(0)
|
||||
#define AD7291_RESET BIT(1)
|
||||
#define AD7291_ALERT_CLEAR BIT(2)
|
||||
#define AD7291_ALERT_POLARITY BIT(3)
|
||||
#define AD7291_EXT_REF BIT(4)
|
||||
#define AD7291_NOISE_DELAY BIT(5)
|
||||
#define AD7291_T_SENSE_MASK BIT(7)
|
||||
#define AD7291_VOLTAGE_MASK GENMASK(15, 8)
|
||||
#define AD7291_VOLTAGE_OFFSET 8
|
||||
|
||||
/*
|
||||
* AD7291 value masks
|
||||
*/
|
||||
#define AD7291_VALUE_MASK GENMASK(11, 0)
|
||||
|
||||
/*
|
||||
* AD7291 alert register bits
|
||||
*/
|
||||
#define AD7291_T_LOW BIT(0)
|
||||
#define AD7291_T_HIGH BIT(1)
|
||||
#define AD7291_T_AVG_LOW BIT(2)
|
||||
#define AD7291_T_AVG_HIGH BIT(3)
|
||||
#define AD7291_V_LOW(x) BIT((x) * 2)
|
||||
#define AD7291_V_HIGH(x) BIT((x) * 2 + 1)
|
||||
|
||||
|
||||
struct ad7291_chip_info {
|
||||
struct i2c_client *client;
|
||||
struct regulator *reg;
|
||||
u16 command;
|
||||
u16 c_mask; /* Active voltage channels for events */
|
||||
struct mutex state_lock;
|
||||
};
|
||||
|
||||
static int ad7291_i2c_read(struct ad7291_chip_info *chip, u8 reg, u16 *data)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
int ret = 0;
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(client, reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "I2C read error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*data = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7291_i2c_write(struct ad7291_chip_info *chip, u8 reg, u16 data)
|
||||
{
|
||||
return i2c_smbus_write_word_swapped(chip->client, reg, data);
|
||||
}
|
||||
|
||||
static irqreturn_t ad7291_event_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct ad7291_chip_info *chip = iio_priv(private);
|
||||
u16 t_status, v_status;
|
||||
u16 command;
|
||||
int i;
|
||||
s64 timestamp = iio_get_time_ns();
|
||||
|
||||
if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (ad7291_i2c_read(chip, AD7291_VOLTAGE_ALERT_STATUS, &v_status))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (!(t_status || v_status))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
command = chip->command | AD7291_ALERT_CLEAR;
|
||||
ad7291_i2c_write(chip, AD7291_COMMAND, command);
|
||||
|
||||
command = chip->command & ~AD7291_ALERT_CLEAR;
|
||||
ad7291_i2c_write(chip, AD7291_COMMAND, command);
|
||||
|
||||
/* For now treat t_sense and t_sense_average the same */
|
||||
if ((t_status & AD7291_T_LOW) || (t_status & AD7291_T_AVG_LOW))
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
timestamp);
|
||||
if ((t_status & AD7291_T_HIGH) || (t_status & AD7291_T_AVG_HIGH))
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
timestamp);
|
||||
|
||||
for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) {
|
||||
if (v_status & AD7291_V_LOW(i))
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
|
||||
i,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
timestamp);
|
||||
if (v_status & AD7291_V_HIGH(i))
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
|
||||
i,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
timestamp);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int ad7291_threshold_reg(const struct iio_chan_spec *chan,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info)
|
||||
{
|
||||
unsigned int offset;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
offset = chan->channel;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
offset = AD7291_VOLTAGE_OFFSET;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
if (dir == IIO_EV_DIR_FALLING)
|
||||
return AD7291_DATA_HIGH(offset);
|
||||
else
|
||||
return AD7291_DATA_LOW(offset);
|
||||
case IIO_EV_INFO_HYSTERESIS:
|
||||
return AD7291_HYST(offset);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7291_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u16 uval;
|
||||
|
||||
ret = ad7291_i2c_read(chip, ad7291_threshold_reg(chan, dir, info),
|
||||
&uval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE)
|
||||
*val = uval & AD7291_VALUE_MASK;
|
||||
|
||||
else
|
||||
*val = sign_extend32(uval, 11);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int ad7291_write_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int val, int val2)
|
||||
{
|
||||
struct ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
|
||||
if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) {
|
||||
if (val > AD7291_VALUE_MASK || val < 0)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (val > 2047 || val < -2048)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ad7291_i2c_write(chip, ad7291_threshold_reg(chan, dir, info),
|
||||
val);
|
||||
}
|
||||
|
||||
static int ad7291_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
/*
|
||||
* To be enabled the channel must simply be on. If any are enabled
|
||||
* we are in continuous sampling mode
|
||||
*/
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
return !!(chip->c_mask & BIT(15 - chan->channel));
|
||||
case IIO_TEMP:
|
||||
/* always on */
|
||||
return 1;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int ad7291_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
int state)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
unsigned int mask;
|
||||
u16 regval;
|
||||
|
||||
mutex_lock(&chip->state_lock);
|
||||
regval = chip->command;
|
||||
/*
|
||||
* To be enabled the channel must simply be on. If any are enabled
|
||||
* use continuous sampling mode.
|
||||
* Possible to disable temp as well but that makes single read tricky.
|
||||
*/
|
||||
|
||||
mask = BIT(15 - chan->channel);
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
if ((!state) && (chip->c_mask & mask))
|
||||
chip->c_mask &= ~mask;
|
||||
else if (state && (!(chip->c_mask & mask)))
|
||||
chip->c_mask |= mask;
|
||||
else
|
||||
break;
|
||||
|
||||
regval &= ~AD7291_AUTOCYCLE;
|
||||
regval |= chip->c_mask;
|
||||
if (chip->c_mask) /* Enable autocycle? */
|
||||
regval |= AD7291_AUTOCYCLE;
|
||||
|
||||
ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
|
||||
chip->command = regval;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
error_ret:
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7291_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
int ret;
|
||||
struct ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
u16 regval;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
mutex_lock(&chip->state_lock);
|
||||
/* If in autocycle mode drop through */
|
||||
if (chip->command & AD7291_AUTOCYCLE) {
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
/* Enable this channel alone */
|
||||
regval = chip->command & (~AD7291_VOLTAGE_MASK);
|
||||
regval |= BIT(15 - chan->channel);
|
||||
ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
/* Read voltage */
|
||||
ret = i2c_smbus_read_word_swapped(chip->client,
|
||||
AD7291_VOLTAGE);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
*val = ret & AD7291_VALUE_MASK;
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_TEMP:
|
||||
/* Assumes tsense bit of command register always set */
|
||||
ret = i2c_smbus_read_word_swapped(chip->client,
|
||||
AD7291_T_SENSE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret, 11);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_AVERAGE_RAW:
|
||||
ret = i2c_smbus_read_word_swapped(chip->client,
|
||||
AD7291_T_AVERAGE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret, 11);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
if (chip->reg) {
|
||||
int vref;
|
||||
|
||||
vref = regulator_get_voltage(chip->reg);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
*val = vref / 1000;
|
||||
} else {
|
||||
*val = 2500;
|
||||
}
|
||||
*val2 = AD7291_BITS;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_TEMP:
|
||||
/*
|
||||
* One LSB of the ADC corresponds to 0.25 deg C.
|
||||
* The temperature reading is in 12-bit twos
|
||||
* complement format
|
||||
*/
|
||||
*val = 250;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_event_spec ad7291_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_HYSTERESIS),
|
||||
},
|
||||
};
|
||||
|
||||
#define AD7291_VOLTAGE_CHAN(_chan) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.indexed = 1, \
|
||||
.channel = _chan, \
|
||||
.event_spec = ad7291_events, \
|
||||
.num_event_specs = ARRAY_SIZE(ad7291_events), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad7291_channels[] = {
|
||||
AD7291_VOLTAGE_CHAN(0),
|
||||
AD7291_VOLTAGE_CHAN(1),
|
||||
AD7291_VOLTAGE_CHAN(2),
|
||||
AD7291_VOLTAGE_CHAN(3),
|
||||
AD7291_VOLTAGE_CHAN(4),
|
||||
AD7291_VOLTAGE_CHAN(5),
|
||||
AD7291_VOLTAGE_CHAN(6),
|
||||
AD7291_VOLTAGE_CHAN(7),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.event_spec = ad7291_events,
|
||||
.num_event_specs = ARRAY_SIZE(ad7291_events),
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info ad7291_info = {
|
||||
.read_raw = &ad7291_read_raw,
|
||||
.read_event_config = &ad7291_read_event_config,
|
||||
.write_event_config = &ad7291_write_event_config,
|
||||
.read_event_value = &ad7291_read_event_value,
|
||||
.write_event_value = &ad7291_write_event_value,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad7291_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ad7291_platform_data *pdata = client->dev.platform_data;
|
||||
struct ad7291_chip_info *chip;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
chip = iio_priv(indio_dev);
|
||||
|
||||
if (pdata && pdata->use_external_ref) {
|
||||
chip->reg = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(chip->reg))
|
||||
return PTR_ERR(chip->reg);
|
||||
|
||||
ret = regulator_enable(chip->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_init(&chip->state_lock);
|
||||
/* this is only used for device removal purposes */
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
chip->client = client;
|
||||
|
||||
chip->command = AD7291_NOISE_DELAY |
|
||||
AD7291_T_SENSE_MASK | /* Tsense always enabled */
|
||||
AD7291_ALERT_POLARITY; /* set irq polarity low level */
|
||||
|
||||
if (pdata && pdata->use_external_ref)
|
||||
chip->command |= AD7291_EXT_REF;
|
||||
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->channels = ad7291_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7291_channels);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &ad7291_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = ad7291_i2c_write(chip, AD7291_COMMAND, AD7291_RESET);
|
||||
if (ret) {
|
||||
ret = -EIO;
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command);
|
||||
if (ret) {
|
||||
ret = -EIO;
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = request_threaded_irq(client->irq,
|
||||
NULL,
|
||||
&ad7291_event_handler,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
id->name,
|
||||
indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_unreg_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_unreg_irq:
|
||||
if (client->irq)
|
||||
free_irq(client->irq, indio_dev);
|
||||
error_disable_reg:
|
||||
if (chip->reg)
|
||||
regulator_disable(chip->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7291_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (client->irq)
|
||||
free_irq(client->irq, indio_dev);
|
||||
|
||||
if (chip->reg)
|
||||
regulator_disable(chip->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad7291_id[] = {
|
||||
{ "ad7291", 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, ad7291_id);
|
||||
|
||||
static struct i2c_driver ad7291_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = ad7291_probe,
|
||||
.remove = ad7291_remove,
|
||||
.id_table = ad7291_id,
|
||||
};
|
||||
module_i2c_driver(ad7291_driver);
|
||||
|
||||
MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7291 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
391
drivers/iio/adc/ad7298.c
Normal file
391
drivers/iio/adc/ad7298.c
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* 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/bitops.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 BIT(15) /* write to the control register */
|
||||
#define AD7298_REPEAT BIT(14) /* repeated conversion enable */
|
||||
#define AD7298_CH(x) BIT(13 - (x)) /* channel select */
|
||||
#define AD7298_TSENSE BIT(5) /* temperature conversion enable */
|
||||
#define AD7298_EXTREF BIT(2) /* external reference enable */
|
||||
#define AD7298_TAVG BIT(1) /* temperature sensor averaging enable */
|
||||
#define AD7298_PDD BIT(0) /* partial power down enable */
|
||||
|
||||
#define AD7298_MAX_CHAN 8
|
||||
#define AD7298_INTREF_mV 2500
|
||||
|
||||
#define AD7298_CH_TEMP 9
|
||||
|
||||
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_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.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_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.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);
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns());
|
||||
|
||||
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 & GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
|
||||
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 ad7298_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7298_platform_data *pdata = spi->dev.platform_data;
|
||||
struct ad7298_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
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 = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int 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);
|
||||
|
||||
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 = 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");
|
||||
315
drivers/iio/adc/ad7476.c
Normal file
315
drivers/iio/adc/ad7476.c
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#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/bitops.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>
|
||||
|
||||
struct ad7476_state;
|
||||
|
||||
struct ad7476_chip_info {
|
||||
unsigned int int_vref_uv;
|
||||
struct iio_chan_spec channel[2];
|
||||
void (*reset)(struct ad7476_state *);
|
||||
};
|
||||
|
||||
struct ad7476_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad7476_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
struct spi_transfer xfer;
|
||||
struct spi_message msg;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
* Make the buffer large enough for one 16 bit sample and one 64 bit
|
||||
* aligned 64 bit timestamp.
|
||||
*/
|
||||
unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)]
|
||||
____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad7476_supported_device_ids {
|
||||
ID_AD7091R,
|
||||
ID_AD7276,
|
||||
ID_AD7277,
|
||||
ID_AD7278,
|
||||
ID_AD7466,
|
||||
ID_AD7467,
|
||||
ID_AD7468,
|
||||
ID_AD7495,
|
||||
ID_AD7940,
|
||||
};
|
||||
|
||||
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7476_state *st = iio_priv(indio_dev);
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->msg);
|
||||
if (b_sent < 0)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
|
||||
iio_get_time_ns());
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad7091_reset(struct ad7476_state *st)
|
||||
{
|
||||
/* Any transfers with 8 scl cycles will reset the device */
|
||||
spi_read(st->spi, st->data, 1);
|
||||
}
|
||||
|
||||
static int ad7476_scan_direct(struct ad7476_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = spi_sync(st->spi, &st->msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpup((__be16 *)st->data);
|
||||
}
|
||||
|
||||
static int ad7476_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret;
|
||||
struct ad7476_state *st = iio_priv(indio_dev);
|
||||
int scale_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = ad7476_scan_direct(st);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (ret >> st->chip_info->channel[0].scan_type.shift) &
|
||||
GENMASK(st->chip_info->channel[0].scan_type.realbits - 1, 0);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (!st->chip_info->int_vref_uv) {
|
||||
scale_uv = regulator_get_voltage(st->reg);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
} else {
|
||||
scale_uv = st->chip_info->int_vref_uv;
|
||||
}
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define _AD7476_CHAN(bits, _shift, _info_mask_sep) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.info_mask_separate = _info_mask_sep, \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = (_shift), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
|
||||
BIT(IIO_CHAN_INFO_RAW))
|
||||
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
|
||||
BIT(IIO_CHAN_INFO_RAW))
|
||||
#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
|
||||
|
||||
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
||||
[ID_AD7091R] = {
|
||||
.channel[0] = AD7091R_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.reset = ad7091_reset,
|
||||
},
|
||||
[ID_AD7276] = {
|
||||
.channel[0] = AD7940_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7277] = {
|
||||
.channel[0] = AD7940_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7278] = {
|
||||
.channel[0] = AD7940_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7466] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7467] = {
|
||||
.channel[0] = AD7476_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7468] = {
|
||||
.channel[0] = AD7476_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7495] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.int_vref_uv = 2500000,
|
||||
},
|
||||
[ID_AD7940] = {
|
||||
.channel[0] = AD7940_CHAN(14),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info ad7476_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &ad7476_read_raw,
|
||||
};
|
||||
|
||||
static int ad7476_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7476_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->chip_info =
|
||||
&ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
/* Establish 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->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channel;
|
||||
indio_dev->num_channels = 2;
|
||||
indio_dev->info = &ad7476_info;
|
||||
/* Setup default message */
|
||||
|
||||
st->xfer.rx_buf = &st->data;
|
||||
st->xfer.len = st->chip_info->channel[0].scan_type.storagebits / 8;
|
||||
|
||||
spi_message_init(&st->msg);
|
||||
spi_message_add_tail(&st->xfer, &st->msg);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad7476_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
if (st->chip_info->reset)
|
||||
st->chip_info->reset(st);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_ring_unregister;
|
||||
return 0;
|
||||
|
||||
error_ring_unregister:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7476_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7476_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7476_id[] = {
|
||||
{"ad7091r", ID_AD7091R},
|
||||
{"ad7273", ID_AD7277},
|
||||
{"ad7274", ID_AD7276},
|
||||
{"ad7276", ID_AD7276},
|
||||
{"ad7277", ID_AD7277},
|
||||
{"ad7278", ID_AD7278},
|
||||
{"ad7466", ID_AD7466},
|
||||
{"ad7467", ID_AD7467},
|
||||
{"ad7468", ID_AD7468},
|
||||
{"ad7475", ID_AD7466},
|
||||
{"ad7476", ID_AD7466},
|
||||
{"ad7476a", ID_AD7466},
|
||||
{"ad7477", ID_AD7467},
|
||||
{"ad7477a", ID_AD7467},
|
||||
{"ad7478", ID_AD7468},
|
||||
{"ad7478a", ID_AD7468},
|
||||
{"ad7495", ID_AD7495},
|
||||
{"ad7910", ID_AD7467},
|
||||
{"ad7920", ID_AD7466},
|
||||
{"ad7940", ID_AD7940},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
||||
|
||||
static struct spi_driver ad7476_driver = {
|
||||
.driver = {
|
||||
.name = "ad7476",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7476_probe,
|
||||
.remove = ad7476_remove,
|
||||
.id_table = ad7476_id,
|
||||
};
|
||||
module_spi_driver(ad7476_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
453
drivers/iio/adc/ad7791.c
Normal file
453
drivers/iio/adc/ad7791.c
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
* AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* 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/ad7791.h>
|
||||
|
||||
#define AD7791_REG_COMM 0x0 /* For writes */
|
||||
#define AD7791_REG_STATUS 0x0 /* For reads */
|
||||
#define AD7791_REG_MODE 0x1
|
||||
#define AD7791_REG_FILTER 0x2
|
||||
#define AD7791_REG_DATA 0x3
|
||||
|
||||
#define AD7791_MODE_CONTINUOUS 0x00
|
||||
#define AD7791_MODE_SINGLE 0x02
|
||||
#define AD7791_MODE_POWERDOWN 0x03
|
||||
|
||||
#define AD7791_CH_AIN1P_AIN1N 0x00
|
||||
#define AD7791_CH_AIN2 0x01
|
||||
#define AD7791_CH_AIN1N_AIN1N 0x02
|
||||
#define AD7791_CH_AVDD_MONITOR 0x03
|
||||
|
||||
#define AD7791_FILTER_CLK_DIV_1 (0x0 << 4)
|
||||
#define AD7791_FILTER_CLK_DIV_2 (0x1 << 4)
|
||||
#define AD7791_FILTER_CLK_DIV_4 (0x2 << 4)
|
||||
#define AD7791_FILTER_CLK_DIV_8 (0x3 << 4)
|
||||
#define AD7791_FILTER_CLK_MASK (0x3 << 4)
|
||||
#define AD7791_FILTER_RATE_120 0x0
|
||||
#define AD7791_FILTER_RATE_100 0x1
|
||||
#define AD7791_FILTER_RATE_33_3 0x2
|
||||
#define AD7791_FILTER_RATE_20 0x3
|
||||
#define AD7791_FILTER_RATE_16_6 0x4
|
||||
#define AD7791_FILTER_RATE_16_7 0x5
|
||||
#define AD7791_FILTER_RATE_13_3 0x6
|
||||
#define AD7791_FILTER_RATE_9_5 0x7
|
||||
#define AD7791_FILTER_RATE_MASK 0x7
|
||||
|
||||
#define AD7791_MODE_BUFFER BIT(1)
|
||||
#define AD7791_MODE_UNIPOLAR BIT(2)
|
||||
#define AD7791_MODE_BURNOUT BIT(3)
|
||||
#define AD7791_MODE_SEL_MASK (0x3 << 6)
|
||||
#define AD7791_MODE_SEL(x) ((x) << 6)
|
||||
|
||||
#define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \
|
||||
AD_SD_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \
|
||||
(bits), (storagebits), 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \
|
||||
(bits), (storagebits), 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3), \
|
||||
}
|
||||
|
||||
static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32);
|
||||
static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16);
|
||||
static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32);
|
||||
|
||||
enum {
|
||||
AD7787,
|
||||
AD7788,
|
||||
AD7789,
|
||||
AD7790,
|
||||
AD7791,
|
||||
};
|
||||
|
||||
enum ad7791_chip_info_flags {
|
||||
AD7791_FLAG_HAS_FILTER = (1 << 0),
|
||||
AD7791_FLAG_HAS_BUFFER = (1 << 1),
|
||||
AD7791_FLAG_HAS_UNIPOLAR = (1 << 2),
|
||||
AD7791_FLAG_HAS_BURNOUT = (1 << 3),
|
||||
};
|
||||
|
||||
struct ad7791_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
enum ad7791_chip_info_flags flags;
|
||||
};
|
||||
|
||||
static const struct ad7791_chip_info ad7791_chip_infos[] = {
|
||||
[AD7787] = {
|
||||
.channels = ad7787_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7787_channels),
|
||||
.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
|
||||
AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
|
||||
},
|
||||
[AD7788] = {
|
||||
.channels = ad7790_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7790_channels),
|
||||
.flags = AD7791_FLAG_HAS_UNIPOLAR,
|
||||
},
|
||||
[AD7789] = {
|
||||
.channels = ad7791_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7791_channels),
|
||||
.flags = AD7791_FLAG_HAS_UNIPOLAR,
|
||||
},
|
||||
[AD7790] = {
|
||||
.channels = ad7790_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7790_channels),
|
||||
.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
|
||||
AD7791_FLAG_HAS_BURNOUT,
|
||||
},
|
||||
[AD7791] = {
|
||||
.channels = ad7791_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7791_channels),
|
||||
.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
|
||||
AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
|
||||
},
|
||||
};
|
||||
|
||||
struct ad7791_state {
|
||||
struct ad_sigma_delta sd;
|
||||
uint8_t mode;
|
||||
uint8_t filter;
|
||||
|
||||
struct regulator *reg;
|
||||
const struct ad7791_chip_info *info;
|
||||
};
|
||||
|
||||
static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd)
|
||||
{
|
||||
return container_of(sd, struct ad7791_state, sd);
|
||||
}
|
||||
|
||||
static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
|
||||
{
|
||||
ad_sd_set_comm(sd, channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7791_set_mode(struct ad_sigma_delta *sd,
|
||||
enum ad_sigma_delta_mode mode)
|
||||
{
|
||||
struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd);
|
||||
|
||||
switch (mode) {
|
||||
case AD_SD_MODE_CONTINUOUS:
|
||||
mode = AD7791_MODE_CONTINUOUS;
|
||||
break;
|
||||
case AD_SD_MODE_SINGLE:
|
||||
mode = AD7791_MODE_SINGLE;
|
||||
break;
|
||||
case AD_SD_MODE_IDLE:
|
||||
case AD_SD_MODE_POWERDOWN:
|
||||
mode = AD7791_MODE_POWERDOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
st->mode &= ~AD7791_MODE_SEL_MASK;
|
||||
st->mode |= AD7791_MODE_SEL(mode);
|
||||
|
||||
return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode);
|
||||
}
|
||||
|
||||
static const struct ad_sigma_delta_info ad7791_sigma_delta_info = {
|
||||
.set_channel = ad7791_set_channel,
|
||||
.set_mode = ad7791_set_mode,
|
||||
.has_registers = true,
|
||||
.addr_shift = 4,
|
||||
.read_mask = BIT(3),
|
||||
};
|
||||
|
||||
static int ad7791_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return ad_sigma_delta_single_conversion(indio_dev, chan, val);
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/**
|
||||
* Unipolar: 0 to VREF
|
||||
* Bipolar -VREF to VREF
|
||||
**/
|
||||
if (unipolar)
|
||||
*val = 0;
|
||||
else
|
||||
*val = -(1 << (chan->scan_type.realbits - 1));
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* The monitor channel uses an internal reference. */
|
||||
if (chan->address == AD7791_CH_AVDD_MONITOR) {
|
||||
/*
|
||||
* The signal is attenuated by a factor of 5 and
|
||||
* compared against a 1.17V internal reference.
|
||||
*/
|
||||
*val = 1170 * 5;
|
||||
} else {
|
||||
int voltage_uv;
|
||||
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
if (voltage_uv < 0)
|
||||
return voltage_uv;
|
||||
|
||||
*val = voltage_uv / 1000;
|
||||
}
|
||||
if (unipolar)
|
||||
*val2 = chan->scan_type.realbits;
|
||||
else
|
||||
*val2 = chan->scan_type.realbits - 1;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const char * const ad7791_sample_freq_avail[] = {
|
||||
[AD7791_FILTER_RATE_120] = "120",
|
||||
[AD7791_FILTER_RATE_100] = "100",
|
||||
[AD7791_FILTER_RATE_33_3] = "33.3",
|
||||
[AD7791_FILTER_RATE_20] = "20",
|
||||
[AD7791_FILTER_RATE_16_6] = "16.6",
|
||||
[AD7791_FILTER_RATE_16_7] = "16.7",
|
||||
[AD7791_FILTER_RATE_13_3] = "13.3",
|
||||
[AD7791_FILTER_RATE_9_5] = "9.5",
|
||||
};
|
||||
|
||||
static ssize_t ad7791_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK;
|
||||
|
||||
return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]);
|
||||
}
|
||||
|
||||
static ssize_t ad7791_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 ad7791_state *st = iio_priv(indio_dev);
|
||||
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 = -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) {
|
||||
if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) {
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->filter &= ~AD7791_FILTER_RATE_MASK;
|
||||
st->filter |= i;
|
||||
ad_sd_write_reg(&st->sd, AD7791_REG_FILTER,
|
||||
sizeof(st->filter), st->filter);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
ad7791_read_frequency,
|
||||
ad7791_write_frequency);
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5");
|
||||
|
||||
static struct attribute *ad7791_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7791_attribute_group = {
|
||||
.attrs = ad7791_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7791_info = {
|
||||
.read_raw = &ad7791_read_raw,
|
||||
.attrs = &ad7791_attribute_group,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7791_no_filter_info = {
|
||||
.read_raw = &ad7791_read_raw,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad7791_setup(struct ad7791_state *st,
|
||||
struct ad7791_platform_data *pdata)
|
||||
{
|
||||
/* Set to poweron-reset default values */
|
||||
st->mode = AD7791_MODE_BUFFER;
|
||||
st->filter = AD7791_FILTER_RATE_16_6;
|
||||
|
||||
if (!pdata)
|
||||
return 0;
|
||||
|
||||
if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered)
|
||||
st->mode &= ~AD7791_MODE_BUFFER;
|
||||
|
||||
if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) &&
|
||||
pdata->burnout_current)
|
||||
st->mode |= AD7791_MODE_BURNOUT;
|
||||
|
||||
if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar)
|
||||
st->mode |= AD7791_MODE_UNIPOLAR;
|
||||
|
||||
return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode),
|
||||
st->mode);
|
||||
}
|
||||
|
||||
static int ad7791_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7791_platform_data *pdata = spi->dev.platform_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad7791_state *st;
|
||||
int ret;
|
||||
|
||||
if (!spi->irq) {
|
||||
dev_err(&spi->dev, "Missing IRQ.\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "refin");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data];
|
||||
ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info);
|
||||
|
||||
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->info->channels;
|
||||
indio_dev->num_channels = st->info->num_channels;
|
||||
if (st->info->flags & AD7791_FLAG_HAS_FILTER)
|
||||
indio_dev->info = &ad7791_info;
|
||||
else
|
||||
indio_dev->info = &ad7791_no_filter_info;
|
||||
|
||||
ret = ad_sd_setup_buffer_and_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = ad7791_setup(st, pdata);
|
||||
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:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7791_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7791_spi_ids[] = {
|
||||
{ "ad7787", AD7787 },
|
||||
{ "ad7788", AD7788 },
|
||||
{ "ad7789", AD7789 },
|
||||
{ "ad7790", AD7790 },
|
||||
{ "ad7791", AD7791 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7791_spi_ids);
|
||||
|
||||
static struct spi_driver ad7791_driver = {
|
||||
.driver = {
|
||||
.name = "ad7791",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7791_probe,
|
||||
.remove = ad7791_remove,
|
||||
.id_table = ad7791_spi_ids,
|
||||
};
|
||||
module_spi_driver(ad7791_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Device AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
865
drivers/iio/adc/ad7793.c
Normal file
865
drivers/iio/adc/ad7793.c
Normal file
|
|
@ -0,0 +1,865 @@
|
|||
/*
|
||||
* 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 = devm_iio_device_alloc(&spi->dev, 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 = devm_regulator_get(&spi->dev, "refin");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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");
|
||||
369
drivers/iio/adc/ad7887.c
Normal file
369
drivers/iio/adc/ad7887.c
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* 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/bitops.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 BIT(5) /* on-chip reference disable */
|
||||
#define AD7887_DUAL BIT(4) /* dual-channel mode */
|
||||
#define AD7887_CH_AIN1 BIT(3) /* convert on channel 1, DUAL=1 */
|
||||
#define AD7887_CH_AIN0 0 /* 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,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/* 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);
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
|
||||
iio_get_time_ns());
|
||||
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 &= GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
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_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = 1,
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
.shift = 0,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
.channel[1] = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = 0,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
.shift = 0,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
.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 ad7887_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7887_platform_data *pdata = spi->dev.platform_data;
|
||||
struct ad7887_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
uint8_t mode;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
if (!pdata || !pdata->use_onchip_ref) {
|
||||
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int 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);
|
||||
|
||||
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 = 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");
|
||||
371
drivers/iio/adc/ad7923.c
Normal file
371
drivers/iio/adc/ad7923.c
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* AD7904/AD7914/AD7923/AD7924 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc (from AD7923 Driver)
|
||||
* Copyright 2012 CS Systemes d'Information
|
||||
*
|
||||
* 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>
|
||||
|
||||
#define AD7923_WRITE_CR (1 << 11) /* write control register */
|
||||
#define AD7923_RANGE (1 << 1) /* range to REFin */
|
||||
#define AD7923_CODING (1 << 0) /* coding is straight binary */
|
||||
#define AD7923_PM_MODE_AS (1) /* auto shutdown */
|
||||
#define AD7923_PM_MODE_FS (2) /* full shutdown */
|
||||
#define AD7923_PM_MODE_OPS (3) /* normal operation */
|
||||
#define AD7923_CHANNEL_0 (0) /* analog input 0 */
|
||||
#define AD7923_CHANNEL_1 (1) /* analog input 1 */
|
||||
#define AD7923_CHANNEL_2 (2) /* analog input 2 */
|
||||
#define AD7923_CHANNEL_3 (3) /* analog input 3 */
|
||||
#define AD7923_SEQUENCE_OFF (0) /* no sequence fonction */
|
||||
#define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */
|
||||
#define AD7923_SEQUENCE_ON (3) /* continuous sequence */
|
||||
|
||||
#define AD7923_MAX_CHAN 4
|
||||
|
||||
#define AD7923_PM_MODE_WRITE(mode) (mode << 4) /* write mode */
|
||||
#define AD7923_CHANNEL_WRITE(channel) (channel << 6) /* write channel */
|
||||
#define AD7923_SEQUENCE_WRITE(sequence) (((sequence & 1) << 3) \
|
||||
+ ((sequence & 2) << 9))
|
||||
/* write sequence fonction */
|
||||
/* left shift for CR : bit 11 transmit in first */
|
||||
#define AD7923_SHIFT_REGISTER 4
|
||||
|
||||
/* val = value, dec = left shift, bits = number of bits of the mask */
|
||||
#define EXTRACT(val, dec, bits) ((val >> dec) & ((1 << bits) - 1))
|
||||
|
||||
struct ad7923_state {
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer ring_xfer[5];
|
||||
struct spi_transfer scan_single_xfer[2];
|
||||
struct spi_message ring_msg;
|
||||
struct spi_message scan_single_msg;
|
||||
|
||||
struct regulator *reg;
|
||||
|
||||
unsigned int settings;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 rx_buf[4] ____cacheline_aligned;
|
||||
__be16 tx_buf[4];
|
||||
};
|
||||
|
||||
struct ad7923_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
enum ad7923_id {
|
||||
AD7904,
|
||||
AD7914,
|
||||
AD7924,
|
||||
};
|
||||
|
||||
#define AD7923_V_CHAN(index, bits) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = index, \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD7923_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
AD7923_V_CHAN(0, bits), \
|
||||
AD7923_V_CHAN(1, bits), \
|
||||
AD7923_V_CHAN(2, bits), \
|
||||
AD7923_V_CHAN(3, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
||||
}
|
||||
|
||||
static DECLARE_AD7923_CHANNELS(ad7904, 8);
|
||||
static DECLARE_AD7923_CHANNELS(ad7914, 10);
|
||||
static DECLARE_AD7923_CHANNELS(ad7924, 12);
|
||||
|
||||
static const struct ad7923_chip_info ad7923_chip_info[] = {
|
||||
[AD7904] = {
|
||||
.channels = ad7904_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7904_channels),
|
||||
},
|
||||
[AD7914] = {
|
||||
.channels = ad7914_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7914_channels),
|
||||
},
|
||||
[AD7924] = {
|
||||
.channels = ad7924_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7924_channels),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* ad7923_update_scan_mode() setup the spi transfer buffer for the new scan mask
|
||||
**/
|
||||
static int ad7923_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *active_scan_mask)
|
||||
{
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
int i, cmd, len;
|
||||
|
||||
len = 0;
|
||||
for_each_set_bit(i, active_scan_mask, AD7923_MAX_CHAN) {
|
||||
cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(i) |
|
||||
AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
|
||||
st->settings;
|
||||
cmd <<= AD7923_SHIFT_REGISTER;
|
||||
st->tx_buf[len++] = cpu_to_be16(cmd);
|
||||
}
|
||||
/* build spi ring message */
|
||||
st->ring_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->ring_xfer[0].len = len;
|
||||
st->ring_xfer[0].cs_change = 1;
|
||||
|
||||
spi_message_init(&st->ring_msg);
|
||||
spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
st->ring_xfer[i + 1].rx_buf = &st->rx_buf[i];
|
||||
st->ring_xfer[i + 1].len = 2;
|
||||
st->ring_xfer[i + 1].cs_change = 1;
|
||||
spi_message_add_tail(&st->ring_xfer[i + 1], &st->ring_msg);
|
||||
}
|
||||
/* make sure last transfer cs_change is not set */
|
||||
st->ring_xfer[i + 1].cs_change = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7923_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 ad7923_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns());
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad7923_scan_direct(struct ad7923_state *st, unsigned ch)
|
||||
{
|
||||
int ret, cmd;
|
||||
|
||||
cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(ch) |
|
||||
AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
|
||||
st->settings;
|
||||
cmd <<= AD7923_SHIFT_REGISTER;
|
||||
st->tx_buf[0] = cpu_to_be16(cmd);
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->rx_buf[0]);
|
||||
}
|
||||
|
||||
static int ad7923_get_range(struct ad7923_state *st)
|
||||
{
|
||||
int vref;
|
||||
|
||||
vref = regulator_get_voltage(st->reg);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
vref /= 1000;
|
||||
|
||||
if (!(st->settings & AD7923_RANGE))
|
||||
vref *= 2;
|
||||
|
||||
return vref;
|
||||
}
|
||||
|
||||
static int ad7923_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret;
|
||||
struct ad7923_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 = ad7923_scan_direct(st, chan->address);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (chan->address == EXTRACT(ret, 12, 4))
|
||||
*val = EXTRACT(ret, 0, 12);
|
||||
else
|
||||
return -EIO;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = ad7923_get_range(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7923_info = {
|
||||
.read_raw = &ad7923_read_raw,
|
||||
.update_scan_mode = ad7923_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad7923_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7923_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct ad7923_chip_info *info;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
st->settings = AD7923_CODING | AD7923_RANGE |
|
||||
AD7923_PM_MODE_WRITE(AD7923_PM_MODE_OPS);
|
||||
|
||||
info = &ad7923_chip_info[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = info->channels;
|
||||
indio_dev->num_channels = info->num_channels;
|
||||
indio_dev->info = &ad7923_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].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[1].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);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "refin");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad7923_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:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7923_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7923_id[] = {
|
||||
{"ad7904", AD7904},
|
||||
{"ad7914", AD7914},
|
||||
{"ad7923", AD7924},
|
||||
{"ad7924", AD7924},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7923_id);
|
||||
|
||||
static struct spi_driver ad7923_driver = {
|
||||
.driver = {
|
||||
.name = "ad7923",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7923_probe,
|
||||
.remove = ad7923_remove,
|
||||
.id_table = ad7923_id,
|
||||
};
|
||||
module_spi_driver(ad7923_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
905
drivers/iio/adc/ad799x.c
Normal file
905
drivers/iio/adc/ad799x.c
Normal file
|
|
@ -0,0 +1,905 @@
|
|||
/*
|
||||
* iio/adc/ad799x.c
|
||||
* Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc.
|
||||
*
|
||||
* based on iio/adc/max1363
|
||||
* 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.
|
||||
*
|
||||
* ad799x.c
|
||||
*
|
||||
* Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997,
|
||||
* ad7998 and similar chips.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define AD799X_CHANNEL_SHIFT 4
|
||||
|
||||
/*
|
||||
* AD7991, AD7995 and AD7999 defines
|
||||
*/
|
||||
|
||||
#define AD7991_REF_SEL 0x08
|
||||
#define AD7991_FLTR 0x04
|
||||
#define AD7991_BIT_TRIAL_DELAY 0x02
|
||||
#define AD7991_SAMPLE_DELAY 0x01
|
||||
|
||||
/*
|
||||
* AD7992, AD7993, AD7994, AD7997 and AD7998 defines
|
||||
*/
|
||||
|
||||
#define AD7998_FLTR BIT(3)
|
||||
#define AD7998_ALERT_EN BIT(2)
|
||||
#define AD7998_BUSY_ALERT BIT(1)
|
||||
#define AD7998_BUSY_ALERT_POL BIT(0)
|
||||
|
||||
#define AD7998_CONV_RES_REG 0x0
|
||||
#define AD7998_ALERT_STAT_REG 0x1
|
||||
#define AD7998_CONF_REG 0x2
|
||||
#define AD7998_CYCLE_TMR_REG 0x3
|
||||
|
||||
#define AD7998_DATALOW_REG(x) ((x) * 3 + 0x4)
|
||||
#define AD7998_DATAHIGH_REG(x) ((x) * 3 + 0x5)
|
||||
#define AD7998_HYST_REG(x) ((x) * 3 + 0x6)
|
||||
|
||||
#define AD7998_CYC_MASK GENMASK(2, 0)
|
||||
#define AD7998_CYC_DIS 0x0
|
||||
#define AD7998_CYC_TCONF_32 0x1
|
||||
#define AD7998_CYC_TCONF_64 0x2
|
||||
#define AD7998_CYC_TCONF_128 0x3
|
||||
#define AD7998_CYC_TCONF_256 0x4
|
||||
#define AD7998_CYC_TCONF_512 0x5
|
||||
#define AD7998_CYC_TCONF_1024 0x6
|
||||
#define AD7998_CYC_TCONF_2048 0x7
|
||||
|
||||
#define AD7998_ALERT_STAT_CLEAR 0xFF
|
||||
|
||||
/*
|
||||
* AD7997 and AD7997 defines
|
||||
*/
|
||||
|
||||
#define AD7997_8_READ_SINGLE BIT(7)
|
||||
#define AD7997_8_READ_SEQUENCE (BIT(6) | BIT(5) | BIT(4))
|
||||
|
||||
enum {
|
||||
ad7991,
|
||||
ad7995,
|
||||
ad7999,
|
||||
ad7992,
|
||||
ad7993,
|
||||
ad7994,
|
||||
ad7997,
|
||||
ad7998
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad799x_chip_config - chip specific information
|
||||
* @channel: channel specification
|
||||
* @default_config: device default configuration
|
||||
* @info: pointer to iio_info struct
|
||||
*/
|
||||
struct ad799x_chip_config {
|
||||
const struct iio_chan_spec channel[9];
|
||||
u16 default_config;
|
||||
const struct iio_info *info;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad799x_chip_info - chip specific information
|
||||
* @num_channels: number of channels
|
||||
* @noirq_config: device configuration w/o IRQ
|
||||
* @irq_config: device configuration w/IRQ
|
||||
*/
|
||||
struct ad799x_chip_info {
|
||||
int num_channels;
|
||||
const struct ad799x_chip_config noirq_config;
|
||||
const struct ad799x_chip_config irq_config;
|
||||
};
|
||||
|
||||
struct ad799x_state {
|
||||
struct i2c_client *client;
|
||||
const struct ad799x_chip_config *chip_config;
|
||||
struct regulator *reg;
|
||||
struct regulator *vref;
|
||||
unsigned id;
|
||||
u16 config;
|
||||
|
||||
u8 *rx_buf;
|
||||
unsigned int transfer_size;
|
||||
};
|
||||
|
||||
static int ad799x_write_config(struct ad799x_state *st, u16 val)
|
||||
{
|
||||
switch (st->id) {
|
||||
case ad7997:
|
||||
case ad7998:
|
||||
return i2c_smbus_write_word_swapped(st->client, AD7998_CONF_REG,
|
||||
val);
|
||||
case ad7992:
|
||||
case ad7993:
|
||||
case ad7994:
|
||||
return i2c_smbus_write_byte_data(st->client, AD7998_CONF_REG,
|
||||
val);
|
||||
default:
|
||||
/* Will be written when doing a conversion */
|
||||
st->config = val;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad799x_read_config(struct ad799x_state *st)
|
||||
{
|
||||
switch (st->id) {
|
||||
case ad7997:
|
||||
case ad7998:
|
||||
return i2c_smbus_read_word_swapped(st->client, AD7998_CONF_REG);
|
||||
case ad7992:
|
||||
case ad7993:
|
||||
case ad7994:
|
||||
return i2c_smbus_read_byte_data(st->client, AD7998_CONF_REG);
|
||||
default:
|
||||
/* No readback support */
|
||||
return st->config;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ad799x_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 ad799x_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
int b_sent;
|
||||
u8 cmd;
|
||||
|
||||
switch (st->id) {
|
||||
case ad7991:
|
||||
case ad7995:
|
||||
case ad7999:
|
||||
cmd = st->config |
|
||||
(*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT);
|
||||
break;
|
||||
case ad7992:
|
||||
case ad7993:
|
||||
case ad7994:
|
||||
cmd = (*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT) |
|
||||
AD7998_CONV_RES_REG;
|
||||
break;
|
||||
case ad7997:
|
||||
case ad7998:
|
||||
cmd = AD7997_8_READ_SEQUENCE | AD7998_CONV_RES_REG;
|
||||
break;
|
||||
default:
|
||||
cmd = 0;
|
||||
}
|
||||
|
||||
b_sent = i2c_smbus_read_i2c_block_data(st->client,
|
||||
cmd, st->transfer_size, st->rx_buf);
|
||||
if (b_sent < 0)
|
||||
goto out;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns());
|
||||
out:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad799x_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
kfree(st->rx_buf);
|
||||
st->rx_buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (!st->rx_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
st->transfer_size = bitmap_weight(scan_mask, indio_dev->masklength) * 2;
|
||||
|
||||
switch (st->id) {
|
||||
case ad7992:
|
||||
case ad7993:
|
||||
case ad7994:
|
||||
case ad7997:
|
||||
case ad7998:
|
||||
st->config &= ~(GENMASK(7, 0) << AD799X_CHANNEL_SHIFT);
|
||||
st->config |= (*scan_mask << AD799X_CHANNEL_SHIFT);
|
||||
return ad799x_write_config(st, st->config);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch)
|
||||
{
|
||||
u8 cmd;
|
||||
|
||||
switch (st->id) {
|
||||
case ad7991:
|
||||
case ad7995:
|
||||
case ad7999:
|
||||
cmd = st->config | (BIT(ch) << AD799X_CHANNEL_SHIFT);
|
||||
break;
|
||||
case ad7992:
|
||||
case ad7993:
|
||||
case ad7994:
|
||||
cmd = BIT(ch) << AD799X_CHANNEL_SHIFT;
|
||||
break;
|
||||
case ad7997:
|
||||
case ad7998:
|
||||
cmd = (ch << AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return i2c_smbus_read_word_swapped(st->client, cmd);
|
||||
}
|
||||
|
||||
static int ad799x_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret;
|
||||
struct ad799x_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 = ad799x_scan_direct(st, chan->scan_index);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (ret >> chan->scan_type.shift) &
|
||||
GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(st->vref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
static const unsigned int ad7998_frequencies[] = {
|
||||
[AD7998_CYC_DIS] = 0,
|
||||
[AD7998_CYC_TCONF_32] = 15625,
|
||||
[AD7998_CYC_TCONF_64] = 7812,
|
||||
[AD7998_CYC_TCONF_128] = 3906,
|
||||
[AD7998_CYC_TCONF_512] = 976,
|
||||
[AD7998_CYC_TCONF_1024] = 488,
|
||||
[AD7998_CYC_TCONF_2048] = 244,
|
||||
};
|
||||
|
||||
static ssize_t ad799x_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
int ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%u\n", ad7998_frequencies[ret & AD7998_CYC_MASK]);
|
||||
}
|
||||
|
||||
static ssize_t ad799x_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 ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
long val;
|
||||
int ret, i;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG);
|
||||
if (ret < 0)
|
||||
goto error_ret_mutex;
|
||||
/* Wipe the bits clean */
|
||||
ret &= ~AD7998_CYC_MASK;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7998_frequencies); i++)
|
||||
if (val == ad7998_frequencies[i])
|
||||
break;
|
||||
if (i == ARRAY_SIZE(ad7998_frequencies)) {
|
||||
ret = -EINVAL;
|
||||
goto error_ret_mutex;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(st->client, AD7998_CYCLE_TMR_REG,
|
||||
ret | i);
|
||||
if (ret < 0)
|
||||
goto error_ret_mutex;
|
||||
ret = len;
|
||||
|
||||
error_ret_mutex:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad799x_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (!(st->config & AD7998_ALERT_EN))
|
||||
return 0;
|
||||
|
||||
if ((st->config >> AD799X_CHANNEL_SHIFT) & BIT(chan->scan_index))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad799x_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
int state)
|
||||
{
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
ret = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (state)
|
||||
st->config |= BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT;
|
||||
else
|
||||
st->config &= ~(BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT);
|
||||
|
||||
if (st->config >> AD799X_CHANNEL_SHIFT)
|
||||
st->config |= AD7998_ALERT_EN;
|
||||
else
|
||||
st->config &= ~AD7998_ALERT_EN;
|
||||
|
||||
ret = ad799x_write_config(st, st->config);
|
||||
|
||||
done:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int ad799x_threshold_reg(const struct iio_chan_spec *chan,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info)
|
||||
{
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
if (dir == IIO_EV_DIR_FALLING)
|
||||
return AD7998_DATALOW_REG(chan->channel);
|
||||
else
|
||||
return AD7998_DATAHIGH_REG(chan->channel);
|
||||
case IIO_EV_INFO_HYSTERESIS:
|
||||
return AD7998_HYST_REG(chan->channel);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad799x_write_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int val, int val2)
|
||||
{
|
||||
int ret;
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = i2c_smbus_write_word_swapped(st->client,
|
||||
ad799x_threshold_reg(chan, dir, info),
|
||||
val << chan->scan_type.shift);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad799x_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
{
|
||||
int ret;
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = i2c_smbus_read_word_swapped(st->client,
|
||||
ad799x_threshold_reg(chan, dir, info));
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (ret >> chan->scan_type.shift) &
|
||||
GENMASK(chan->scan_type.realbits - 1 , 0);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static irqreturn_t ad799x_event_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct ad799x_state *st = iio_priv(private);
|
||||
int i, ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(st->client, AD7998_ALERT_STAT_REG);
|
||||
if (ret <= 0)
|
||||
goto done;
|
||||
|
||||
if (i2c_smbus_write_byte_data(st->client, AD7998_ALERT_STAT_REG,
|
||||
AD7998_ALERT_STAT_CLEAR) < 0)
|
||||
goto done;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (ret & BIT(i))
|
||||
iio_push_event(indio_dev,
|
||||
i & 0x1 ?
|
||||
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
|
||||
(i >> 1),
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING) :
|
||||
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
|
||||
(i >> 1),
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
|
||||
done:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
ad799x_read_frequency,
|
||||
ad799x_write_frequency);
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("15625 7812 3906 1953 976 488 244 0");
|
||||
|
||||
static struct attribute *ad799x_event_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ad799x_event_attrs_group = {
|
||||
.attrs = ad799x_event_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
static const struct iio_info ad7991_info = {
|
||||
.read_raw = &ad799x_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7993_4_7_8_noirq_info = {
|
||||
.read_raw = &ad799x_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
.update_scan_mode = ad799x_update_scan_mode,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7993_4_7_8_irq_info = {
|
||||
.read_raw = &ad799x_read_raw,
|
||||
.event_attrs = &ad799x_event_attrs_group,
|
||||
.read_event_config = &ad799x_read_event_config,
|
||||
.write_event_config = &ad799x_write_event_config,
|
||||
.read_event_value = &ad799x_read_event_value,
|
||||
.write_event_value = &ad799x_write_event_value,
|
||||
.driver_module = THIS_MODULE,
|
||||
.update_scan_mode = ad799x_update_scan_mode,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec ad799x_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_HYSTERESIS),
|
||||
},
|
||||
};
|
||||
|
||||
#define _AD799X_CHANNEL(_index, _realbits, _ev_spec, _num_ev_spec) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_index), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = (_index), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_realbits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (_realbits), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
.event_spec = _ev_spec, \
|
||||
.num_event_specs = _num_ev_spec, \
|
||||
}
|
||||
|
||||
#define AD799X_CHANNEL(_index, _realbits) \
|
||||
_AD799X_CHANNEL(_index, _realbits, NULL, 0)
|
||||
|
||||
#define AD799X_CHANNEL_WITH_EVENTS(_index, _realbits) \
|
||||
_AD799X_CHANNEL(_index, _realbits, ad799x_events, \
|
||||
ARRAY_SIZE(ad799x_events))
|
||||
|
||||
static const struct ad799x_chip_info ad799x_chip_info_tbl[] = {
|
||||
[ad7991] = {
|
||||
.num_channels = 5,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 12),
|
||||
AD799X_CHANNEL(1, 12),
|
||||
AD799X_CHANNEL(2, 12),
|
||||
AD799X_CHANNEL(3, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.info = &ad7991_info,
|
||||
},
|
||||
},
|
||||
[ad7995] = {
|
||||
.num_channels = 5,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 10),
|
||||
AD799X_CHANNEL(1, 10),
|
||||
AD799X_CHANNEL(2, 10),
|
||||
AD799X_CHANNEL(3, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.info = &ad7991_info,
|
||||
},
|
||||
},
|
||||
[ad7999] = {
|
||||
.num_channels = 5,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 8),
|
||||
AD799X_CHANNEL(1, 8),
|
||||
AD799X_CHANNEL(2, 8),
|
||||
AD799X_CHANNEL(3, 8),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.info = &ad7991_info,
|
||||
},
|
||||
},
|
||||
[ad7992] = {
|
||||
.num_channels = 3,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 12),
|
||||
AD799X_CHANNEL(1, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
},
|
||||
.info = &ad7993_4_7_8_noirq_info,
|
||||
},
|
||||
.irq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL_WITH_EVENTS(0, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(1, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
},
|
||||
.default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
|
||||
.info = &ad7993_4_7_8_irq_info,
|
||||
},
|
||||
},
|
||||
[ad7993] = {
|
||||
.num_channels = 5,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 10),
|
||||
AD799X_CHANNEL(1, 10),
|
||||
AD799X_CHANNEL(2, 10),
|
||||
AD799X_CHANNEL(3, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.info = &ad7993_4_7_8_noirq_info,
|
||||
},
|
||||
.irq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL_WITH_EVENTS(0, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(1, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(2, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(3, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
|
||||
.info = &ad7993_4_7_8_irq_info,
|
||||
},
|
||||
},
|
||||
[ad7994] = {
|
||||
.num_channels = 5,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 12),
|
||||
AD799X_CHANNEL(1, 12),
|
||||
AD799X_CHANNEL(2, 12),
|
||||
AD799X_CHANNEL(3, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.info = &ad7993_4_7_8_noirq_info,
|
||||
},
|
||||
.irq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL_WITH_EVENTS(0, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(1, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(2, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(3, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
|
||||
.info = &ad7993_4_7_8_irq_info,
|
||||
},
|
||||
},
|
||||
[ad7997] = {
|
||||
.num_channels = 9,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 10),
|
||||
AD799X_CHANNEL(1, 10),
|
||||
AD799X_CHANNEL(2, 10),
|
||||
AD799X_CHANNEL(3, 10),
|
||||
AD799X_CHANNEL(4, 10),
|
||||
AD799X_CHANNEL(5, 10),
|
||||
AD799X_CHANNEL(6, 10),
|
||||
AD799X_CHANNEL(7, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
},
|
||||
.info = &ad7993_4_7_8_noirq_info,
|
||||
},
|
||||
.irq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL_WITH_EVENTS(0, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(1, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(2, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(3, 10),
|
||||
AD799X_CHANNEL(4, 10),
|
||||
AD799X_CHANNEL(5, 10),
|
||||
AD799X_CHANNEL(6, 10),
|
||||
AD799X_CHANNEL(7, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
},
|
||||
.default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
|
||||
.info = &ad7993_4_7_8_irq_info,
|
||||
},
|
||||
},
|
||||
[ad7998] = {
|
||||
.num_channels = 9,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 12),
|
||||
AD799X_CHANNEL(1, 12),
|
||||
AD799X_CHANNEL(2, 12),
|
||||
AD799X_CHANNEL(3, 12),
|
||||
AD799X_CHANNEL(4, 12),
|
||||
AD799X_CHANNEL(5, 12),
|
||||
AD799X_CHANNEL(6, 12),
|
||||
AD799X_CHANNEL(7, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
},
|
||||
.info = &ad7993_4_7_8_noirq_info,
|
||||
},
|
||||
.irq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL_WITH_EVENTS(0, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(1, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(2, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(3, 12),
|
||||
AD799X_CHANNEL(4, 12),
|
||||
AD799X_CHANNEL(5, 12),
|
||||
AD799X_CHANNEL(6, 12),
|
||||
AD799X_CHANNEL(7, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
},
|
||||
.default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
|
||||
.info = &ad7993_4_7_8_irq_info,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int ad799x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct ad799x_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct ad799x_chip_info *chip_info =
|
||||
&ad799x_chip_info_tbl[id->driver_data];
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
/* this is only used for device removal purposes */
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
st->id = id->driver_data;
|
||||
if (client->irq > 0 && chip_info->irq_config.info)
|
||||
st->chip_config = &chip_info->irq_config;
|
||||
else
|
||||
st->chip_config = &chip_info->noirq_config;
|
||||
|
||||
/* TODO: Add pdata options for filtering and bit delay */
|
||||
|
||||
st->reg = devm_regulator_get(&client->dev, "vcc");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->vref = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(st->vref)) {
|
||||
ret = PTR_ERR(st->vref);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
ret = regulator_enable(st->vref);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = st->chip_config->info;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_config->channel;
|
||||
indio_dev->num_channels = chip_info->num_channels;
|
||||
|
||||
ret = ad799x_write_config(st, st->chip_config->default_config);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
ret = ad799x_read_config(st);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
st->config = ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad799x_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_vref;
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev,
|
||||
client->irq,
|
||||
NULL,
|
||||
ad799x_event_handler,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
client->name,
|
||||
indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_ring;
|
||||
}
|
||||
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_vref:
|
||||
regulator_disable(st->vref);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad799x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->vref);
|
||||
regulator_disable(st->reg);
|
||||
kfree(st->rx_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad799x_id[] = {
|
||||
{ "ad7991", ad7991 },
|
||||
{ "ad7995", ad7995 },
|
||||
{ "ad7999", ad7999 },
|
||||
{ "ad7992", ad7992 },
|
||||
{ "ad7993", ad7993 },
|
||||
{ "ad7994", ad7994 },
|
||||
{ "ad7997", ad7997 },
|
||||
{ "ad7998", ad7998 },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, ad799x_id);
|
||||
|
||||
static struct i2c_driver ad799x_driver = {
|
||||
.driver = {
|
||||
.name = "ad799x",
|
||||
},
|
||||
.probe = ad799x_probe,
|
||||
.remove = ad799x_remove,
|
||||
.id_table = ad799x_id,
|
||||
};
|
||||
module_i2c_driver(ad799x_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD799x ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
553
drivers/iio/adc/ad_sigma_delta.c
Normal file
553
drivers/iio/adc/ad_sigma_delta.c
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
/*
|
||||
* Support code for Analog Devices Sigma-Delta ADCs
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/err.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 <asm/unaligned.h>
|
||||
|
||||
|
||||
#define AD_SD_COMM_CHAN_MASK 0x3
|
||||
|
||||
#define AD_SD_REG_COMM 0x00
|
||||
#define AD_SD_REG_DATA 0x03
|
||||
|
||||
/**
|
||||
* ad_sd_set_comm() - Set communications register
|
||||
*
|
||||
* @sigma_delta: The sigma delta device
|
||||
* @comm: New value for the communications register
|
||||
*/
|
||||
void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm)
|
||||
{
|
||||
/* Some variants use the lower two bits of the communications register
|
||||
* to select the channel */
|
||||
sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_set_comm);
|
||||
|
||||
/**
|
||||
* ad_sd_write_reg() - Write a register
|
||||
*
|
||||
* @sigma_delta: The sigma delta device
|
||||
* @reg: Address of the register
|
||||
* @size: Size of the register (0-3)
|
||||
* @val: Value to write to the register
|
||||
*
|
||||
* Returns 0 on success, an error code otherwise.
|
||||
**/
|
||||
int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
|
||||
unsigned int size, unsigned int val)
|
||||
{
|
||||
uint8_t *data = sigma_delta->data;
|
||||
struct spi_transfer t = {
|
||||
.tx_buf = data,
|
||||
.len = size + 1,
|
||||
.cs_change = sigma_delta->bus_locked,
|
||||
};
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
||||
data[0] = (reg << sigma_delta->info->addr_shift) | sigma_delta->comm;
|
||||
|
||||
switch (size) {
|
||||
case 3:
|
||||
data[1] = val >> 16;
|
||||
data[2] = val >> 8;
|
||||
data[3] = val;
|
||||
break;
|
||||
case 2:
|
||||
put_unaligned_be16(val, &data[1]);
|
||||
break;
|
||||
case 1:
|
||||
data[1] = val;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t, &m);
|
||||
|
||||
if (sigma_delta->bus_locked)
|
||||
ret = spi_sync_locked(sigma_delta->spi, &m);
|
||||
else
|
||||
ret = spi_sync(sigma_delta->spi, &m);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_write_reg);
|
||||
|
||||
static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
|
||||
unsigned int reg, unsigned int size, uint8_t *val)
|
||||
{
|
||||
uint8_t *data = sigma_delta->data;
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = data,
|
||||
.len = 1,
|
||||
}, {
|
||||
.rx_buf = val,
|
||||
.len = size,
|
||||
.cs_change = sigma_delta->bus_locked,
|
||||
},
|
||||
};
|
||||
struct spi_message m;
|
||||
|
||||
spi_message_init(&m);
|
||||
|
||||
if (sigma_delta->info->has_registers) {
|
||||
data[0] = reg << sigma_delta->info->addr_shift;
|
||||
data[0] |= sigma_delta->info->read_mask;
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
}
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
if (sigma_delta->bus_locked)
|
||||
ret = spi_sync_locked(sigma_delta->spi, &m);
|
||||
else
|
||||
ret = spi_sync(sigma_delta->spi, &m);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad_sd_read_reg() - Read a register
|
||||
*
|
||||
* @sigma_delta: The sigma delta device
|
||||
* @reg: Address of the register
|
||||
* @size: Size of the register (1-4)
|
||||
* @val: Read value
|
||||
*
|
||||
* Returns 0 on success, an error code otherwise.
|
||||
**/
|
||||
int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta,
|
||||
unsigned int reg, unsigned int size, unsigned int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad_sd_read_reg_raw(sigma_delta, reg, size, sigma_delta->data);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
switch (size) {
|
||||
case 4:
|
||||
*val = get_unaligned_be32(sigma_delta->data);
|
||||
break;
|
||||
case 3:
|
||||
*val = (sigma_delta->data[0] << 16) |
|
||||
(sigma_delta->data[1] << 8) |
|
||||
sigma_delta->data[2];
|
||||
break;
|
||||
case 2:
|
||||
*val = get_unaligned_be16(sigma_delta->data);
|
||||
break;
|
||||
case 1:
|
||||
*val = sigma_delta->data[0];
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_read_reg);
|
||||
|
||||
static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
|
||||
unsigned int mode, unsigned int channel)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad_sigma_delta_set_channel(sigma_delta, channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_bus_lock(sigma_delta->spi->master);
|
||||
sigma_delta->bus_locked = true;
|
||||
reinit_completion(&sigma_delta->completion);
|
||||
|
||||
ret = ad_sigma_delta_set_mode(sigma_delta, mode);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
ret = wait_for_completion_timeout(&sigma_delta->completion, 2*HZ);
|
||||
if (ret == 0) {
|
||||
sigma_delta->irq_dis = true;
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
ret = -EIO;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
out:
|
||||
sigma_delta->bus_locked = false;
|
||||
spi_bus_unlock(sigma_delta->spi->master);
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad_sd_calibrate_all() - Performs channel calibration
|
||||
* @sigma_delta: The sigma delta device
|
||||
* @cb: Array of channels and calibration type to perform
|
||||
* @n: Number of items in cb
|
||||
*
|
||||
* Returns 0 on success, an error code otherwise.
|
||||
**/
|
||||
int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta,
|
||||
const struct ad_sd_calib_data *cb, unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
ret = ad_sd_calibrate(sigma_delta, cb[i].mode, cb[i].channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_calibrate_all);
|
||||
|
||||
/**
|
||||
* ad_sigma_delta_single_conversion() - Performs a single data conversion
|
||||
* @indio_dev: The IIO device
|
||||
* @chan: The conversion is done for this channel
|
||||
* @val: Pointer to the location where to store the read value
|
||||
*
|
||||
* Returns: 0 on success, an error value otherwise.
|
||||
*/
|
||||
int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
unsigned int sample, raw_sample;
|
||||
int ret = 0;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ad_sigma_delta_set_channel(sigma_delta, chan->address);
|
||||
|
||||
spi_bus_lock(sigma_delta->spi->master);
|
||||
sigma_delta->bus_locked = true;
|
||||
reinit_completion(&sigma_delta->completion);
|
||||
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
|
||||
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
ret = wait_for_completion_interruptible_timeout(
|
||||
&sigma_delta->completion, HZ);
|
||||
|
||||
sigma_delta->bus_locked = false;
|
||||
spi_bus_unlock(sigma_delta->spi->master);
|
||||
|
||||
if (ret == 0)
|
||||
ret = -EIO;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_DATA,
|
||||
DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8),
|
||||
&raw_sample);
|
||||
|
||||
out:
|
||||
if (!sigma_delta->irq_dis) {
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
sigma_delta->irq_dis = true;
|
||||
}
|
||||
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sample = raw_sample >> chan->scan_type.shift;
|
||||
sample &= (1 << chan->scan_type.realbits) - 1;
|
||||
*val = sample;
|
||||
|
||||
ret = ad_sigma_delta_postprocess_sample(sigma_delta, raw_sample);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sigma_delta_single_conversion);
|
||||
|
||||
static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
unsigned int channel;
|
||||
int ret;
|
||||
|
||||
ret = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
channel = find_first_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength);
|
||||
ret = ad_sigma_delta_set_channel(sigma_delta,
|
||||
indio_dev->channels[channel].address);
|
||||
if (ret)
|
||||
goto err_predisable;
|
||||
|
||||
spi_bus_lock(sigma_delta->spi->master);
|
||||
sigma_delta->bus_locked = true;
|
||||
ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_unlock:
|
||||
spi_bus_unlock(sigma_delta->spi->master);
|
||||
err_predisable:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
|
||||
reinit_completion(&sigma_delta->completion);
|
||||
wait_for_completion_timeout(&sigma_delta->completion, HZ);
|
||||
|
||||
if (!sigma_delta->irq_dis) {
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
sigma_delta->irq_dis = true;
|
||||
}
|
||||
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
|
||||
|
||||
sigma_delta->bus_locked = false;
|
||||
return spi_bus_unlock(sigma_delta->spi->master);
|
||||
}
|
||||
|
||||
static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
unsigned int reg_size;
|
||||
uint8_t data[16];
|
||||
int ret;
|
||||
|
||||
memset(data, 0x00, 16);
|
||||
|
||||
reg_size = indio_dev->channels[0].scan_type.realbits +
|
||||
indio_dev->channels[0].scan_type.shift;
|
||||
reg_size = DIV_ROUND_UP(reg_size, 8);
|
||||
|
||||
switch (reg_size) {
|
||||
case 4:
|
||||
case 2:
|
||||
case 1:
|
||||
ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
|
||||
reg_size, &data[0]);
|
||||
break;
|
||||
case 3:
|
||||
/* We store 24 bit samples in a 32 bit word. Keep the upper
|
||||
* byte set to zero. */
|
||||
ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
|
||||
reg_size, &data[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = {
|
||||
.postenable = &ad_sd_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
.postdisable = &ad_sd_buffer_postdisable,
|
||||
.validate_scan_mask = &iio_validate_scan_mask_onehot,
|
||||
};
|
||||
|
||||
static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = private;
|
||||
|
||||
complete(&sigma_delta->completion);
|
||||
disable_irq_nosync(irq);
|
||||
sigma_delta->irq_dis = true;
|
||||
iio_trigger_poll(sigma_delta->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad_sd_validate_trigger() - validate_trigger callback for ad_sigma_delta devices
|
||||
* @indio_dev: The IIO device
|
||||
* @trig: The new trigger
|
||||
*
|
||||
* Returns: 0 if the 'trig' matches the trigger registered by the ad_sigma_delta
|
||||
* device, -EINVAL otherwise.
|
||||
*/
|
||||
int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
|
||||
if (sigma_delta->trig != trig)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_validate_trigger);
|
||||
|
||||
static const struct iio_trigger_ops ad_sd_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad_sd_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
int ret;
|
||||
|
||||
sigma_delta->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (sigma_delta->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
sigma_delta->trig->ops = &ad_sd_trigger_ops;
|
||||
init_completion(&sigma_delta->completion);
|
||||
|
||||
ret = request_irq(sigma_delta->spi->irq,
|
||||
ad_sd_data_rdy_trig_poll,
|
||||
IRQF_TRIGGER_LOW,
|
||||
indio_dev->name,
|
||||
sigma_delta);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
|
||||
if (!sigma_delta->irq_dis) {
|
||||
sigma_delta->irq_dis = true;
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
}
|
||||
sigma_delta->trig->dev.parent = &sigma_delta->spi->dev;
|
||||
iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta);
|
||||
|
||||
ret = iio_trigger_register(sigma_delta->trig);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
/* select default trigger */
|
||||
indio_dev->trig = iio_trigger_get(sigma_delta->trig);
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(sigma_delta->spi->irq, sigma_delta);
|
||||
error_free_trig:
|
||||
iio_trigger_free(sigma_delta->trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ad_sd_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
|
||||
iio_trigger_unregister(sigma_delta->trig);
|
||||
free_irq(sigma_delta->spi->irq, sigma_delta);
|
||||
iio_trigger_free(sigma_delta->trig);
|
||||
}
|
||||
|
||||
/**
|
||||
* ad_sd_setup_buffer_and_trigger() -
|
||||
* @indio_dev: The IIO device
|
||||
*/
|
||||
int ad_sd_setup_buffer_and_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&ad_sd_trigger_handler, &ad_sd_buffer_setup_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad_sd_probe_trigger(indio_dev);
|
||||
if (ret) {
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_setup_buffer_and_trigger);
|
||||
|
||||
/**
|
||||
* ad_sd_cleanup_buffer_and_trigger() -
|
||||
* @indio_dev: The IIO device
|
||||
*/
|
||||
void ad_sd_cleanup_buffer_and_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
ad_sd_remove_trigger(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_cleanup_buffer_and_trigger);
|
||||
|
||||
/**
|
||||
* ad_sd_init() - Initializes a ad_sigma_delta struct
|
||||
* @sigma_delta: The ad_sigma_delta device
|
||||
* @indio_dev: The IIO device which the Sigma Delta device is used for
|
||||
* @spi: The SPI device for the ad_sigma_delta device
|
||||
* @info: Device specific callbacks and options
|
||||
*
|
||||
* This function needs to be called before any other operations are performed on
|
||||
* the ad_sigma_delta struct.
|
||||
*/
|
||||
int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
|
||||
struct spi_device *spi, const struct ad_sigma_delta_info *info)
|
||||
{
|
||||
sigma_delta->spi = spi;
|
||||
sigma_delta->info = info;
|
||||
iio_device_set_drvdata(indio_dev, sigma_delta);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_init);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1439
drivers/iio/adc/at91_adc.c
Normal file
1439
drivers/iio/adc/at91_adc.c
Normal file
File diff suppressed because it is too large
Load diff
889
drivers/iio/adc/exynos_adc.c
Normal file
889
drivers/iio/adc/exynos_adc.c
Normal file
|
|
@ -0,0 +1,889 @@
|
|||
/*
|
||||
* exynos_adc.c - Support for ADC in EXYNOS SoCs
|
||||
*
|
||||
* 8 ~ 10 channel, 10/12-bit ADC
|
||||
*
|
||||
* Copyright (C) 2013 Naveen Krishna Chatradhi <ch.naveen@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
#include <soc/samsung/exynos-powermode.h>
|
||||
|
||||
/* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */
|
||||
#define ADC_V1_CON(x) ((x) + 0x00)
|
||||
#define ADC_V1_DLY(x) ((x) + 0x08)
|
||||
#define ADC_V1_DATX(x) ((x) + 0x0C)
|
||||
#define ADC_V1_INTCLR(x) ((x) + 0x18)
|
||||
#define ADC_V1_MUX(x) ((x) + 0x1c)
|
||||
|
||||
/* S3C2410 ADC registers definitions */
|
||||
#define ADC_S3C2410_MUX(x) ((x) + 0x18)
|
||||
|
||||
/* Future ADC_V2 registers definitions */
|
||||
#define ADC_V2_CON1(x) ((x) + 0x00)
|
||||
#define ADC_V2_CON2(x) ((x) + 0x04)
|
||||
#define ADC_V2_STAT(x) ((x) + 0x08)
|
||||
#define ADC_V2_INT_EN(x) ((x) + 0x10)
|
||||
#define ADC_V2_INT_ST(x) ((x) + 0x14)
|
||||
#define ADC_V2_VER(x) ((x) + 0x20)
|
||||
|
||||
/* Sharing ADC_V3 registers definitions */
|
||||
#define ADC_V3_DAT(x) ((x) + 0x08)
|
||||
#define ADC_V3_DAT_SUM(x) ((x) + 0x0C)
|
||||
#define ADC_V3_DBG_DATA(x) ((x) + 0x1C)
|
||||
|
||||
/* Bit definitions for ADC_V1 */
|
||||
#define ADC_V1_CON_RES (1u << 16)
|
||||
#define ADC_V1_CON_PRSCEN (1u << 14)
|
||||
#define ADC_V1_CON_PRSCLV(x) (((x) & 0xFF) << 6)
|
||||
#define ADC_V1_CON_STANDBY (1u << 2)
|
||||
|
||||
/* Bit definitions for S3C2410 ADC */
|
||||
#define ADC_S3C2410_CON_SELMUX(x) (((x) & 7) << 3)
|
||||
#define ADC_S3C2410_DATX_MASK 0x3FF
|
||||
#define ADC_S3C2416_CON_RES_SEL (1u << 3)
|
||||
|
||||
/* Bit definitions for ADC_V2 */
|
||||
#define ADC_V2_CON1_SOFT_RESET (1u << 2)
|
||||
#define ADC_V2_CON1_SOFT_NON_RESET (1u << 1)
|
||||
|
||||
#define ADC_V2_CON2_OSEL (1u << 10)
|
||||
#define ADC_V2_CON2_ESEL (1u << 9)
|
||||
#define ADC_V2_CON2_HIGHF (1u << 8)
|
||||
#define ADC_V2_CON2_C_TIME(x) (((x) & 7) << 4)
|
||||
#define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0)
|
||||
#define ADC_V2_CON2_ACH_MASK 0xF
|
||||
|
||||
/* Bit definitions for ADC_V3 */
|
||||
#define ADC_V3_DAT_FLAG (1u << 31)
|
||||
|
||||
#define MAX_ADC_V3_CHANNELS 8
|
||||
#define MAX_ADC_V2_CHANNELS 10
|
||||
#define MAX_ADC_V1_CHANNELS 8
|
||||
#define MAX_EXYNOS3250_ADC_CHANNELS 2
|
||||
|
||||
/* Bit definitions common for ADC_V1, ADC_V2, ADC_V3 */
|
||||
#define ADC_CON_EN_START (1u << 0)
|
||||
#define ADC_DATX_MASK 0xFFF
|
||||
|
||||
#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100))
|
||||
|
||||
struct exynos_adc {
|
||||
struct exynos_adc_data *data;
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
void __iomem *enable_reg;
|
||||
struct clk *clk;
|
||||
struct clk *sclk;
|
||||
unsigned int irq;
|
||||
struct regulator *vdd;
|
||||
bool needs_adc_phy;
|
||||
|
||||
struct completion completion;
|
||||
|
||||
u32 value;
|
||||
unsigned int version;
|
||||
int idle_ip_index;
|
||||
};
|
||||
|
||||
struct exynos_adc_data {
|
||||
int num_channels;
|
||||
bool needs_sclk;
|
||||
u32 mask;
|
||||
|
||||
void (*init_hw)(struct exynos_adc *info);
|
||||
void (*exit_hw)(struct exynos_adc *info);
|
||||
void (*clear_irq)(struct exynos_adc *info);
|
||||
void (*start_conv)(struct exynos_adc *info, unsigned long addr);
|
||||
irqreturn_t (*adc_isr)(int irq, void *dev_id);
|
||||
};
|
||||
|
||||
static void exynos_adc_unprepare_clk(struct exynos_adc *info)
|
||||
{
|
||||
if (info->data->needs_sclk)
|
||||
clk_unprepare(info->sclk);
|
||||
clk_unprepare(info->clk);
|
||||
}
|
||||
|
||||
static int exynos_adc_prepare_clk(struct exynos_adc *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare(info->clk);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed preparing adc clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (info->data->needs_sclk) {
|
||||
ret = clk_prepare(info->sclk);
|
||||
if (ret) {
|
||||
clk_unprepare(info->clk);
|
||||
dev_err(info->dev,
|
||||
"failed preparing sclk_adc clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_adc_disable_clk(struct exynos_adc *info)
|
||||
{
|
||||
if (info->data->needs_sclk)
|
||||
clk_disable(info->sclk);
|
||||
clk_disable(info->clk);
|
||||
}
|
||||
|
||||
static int exynos_adc_enable_clk(struct exynos_adc *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(info->clk);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed enabling adc clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (info->data->needs_sclk) {
|
||||
ret = clk_enable(info->sclk);
|
||||
if (ret) {
|
||||
clk_disable(info->clk);
|
||||
dev_err(info->dev,
|
||||
"failed enabling sclk_adc clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_adc_enable_access(struct exynos_adc *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
exynos_update_ip_idle_status(info->idle_ip_index, 0);
|
||||
if (info->needs_adc_phy)
|
||||
writel(1, info->enable_reg);
|
||||
|
||||
if (info->vdd) {
|
||||
ret = regulator_enable(info->vdd);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
ret = exynos_adc_prepare_clk(info);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = exynos_adc_enable_clk(info);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
exynos_update_ip_idle_status(info->idle_ip_index, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exynos_adc_disable_access(struct exynos_adc *info)
|
||||
{
|
||||
exynos_adc_disable_clk(info);
|
||||
exynos_adc_unprepare_clk(info);
|
||||
if (info->vdd)
|
||||
regulator_disable(info->vdd);
|
||||
|
||||
if (info->needs_adc_phy)
|
||||
writel(0, info->enable_reg);
|
||||
exynos_update_ip_idle_status(info->idle_ip_index, 1);
|
||||
}
|
||||
|
||||
static void exynos_adc_v1_init_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con1;
|
||||
|
||||
/* set default prescaler values and Enable prescaler */
|
||||
con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
|
||||
|
||||
/* Enable 12-bit ADC resolution */
|
||||
con1 |= ADC_V1_CON_RES;
|
||||
writel(con1, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v1_exit_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con;
|
||||
|
||||
con = readl(ADC_V1_CON(info->regs));
|
||||
con |= ADC_V1_CON_STANDBY;
|
||||
writel(con, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v1_clear_irq(struct exynos_adc *info)
|
||||
{
|
||||
writel(1, ADC_V1_INTCLR(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v1_start_conv(struct exynos_adc *info,
|
||||
unsigned long addr)
|
||||
{
|
||||
u32 con1;
|
||||
|
||||
writel(addr, ADC_V1_MUX(info->regs));
|
||||
|
||||
con1 = readl(ADC_V1_CON(info->regs));
|
||||
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static irqreturn_t exynos_adc_v1_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct exynos_adc *info = (struct exynos_adc *)dev_id;
|
||||
u32 mask = info->data->mask;
|
||||
|
||||
/* Read value */
|
||||
info->value = readl(ADC_V1_DATX(info->regs)) & mask;
|
||||
|
||||
/* clear irq */
|
||||
if (info->data->clear_irq)
|
||||
info->data->clear_irq(info);
|
||||
|
||||
complete(&info->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct exynos_adc_data exynos_adc_v1_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
.clear_irq = exynos_adc_v1_clear_irq,
|
||||
.start_conv = exynos_adc_v1_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static void exynos_adc_s3c2416_start_conv(struct exynos_adc *info,
|
||||
unsigned long addr)
|
||||
{
|
||||
u32 con1;
|
||||
|
||||
/* Enable 12 bit ADC resolution */
|
||||
con1 = readl(ADC_V1_CON(info->regs));
|
||||
con1 |= ADC_S3C2416_CON_RES_SEL;
|
||||
writel(con1, ADC_V1_CON(info->regs));
|
||||
|
||||
/* Select channel for S3C2416 */
|
||||
writel(addr, ADC_S3C2410_MUX(info->regs));
|
||||
|
||||
con1 = readl(ADC_V1_CON(info->regs));
|
||||
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static struct exynos_adc_data const exynos_adc_s3c2416_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
.start_conv = exynos_adc_s3c2416_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static void exynos_adc_s3c2443_start_conv(struct exynos_adc *info,
|
||||
unsigned long addr)
|
||||
{
|
||||
u32 con1;
|
||||
|
||||
/* Select channel for S3C2433 */
|
||||
writel(addr, ADC_S3C2410_MUX(info->regs));
|
||||
|
||||
con1 = readl(ADC_V1_CON(info->regs));
|
||||
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static struct exynos_adc_data const exynos_adc_s3c2443_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
.start_conv = exynos_adc_s3c2443_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info,
|
||||
unsigned long addr)
|
||||
{
|
||||
u32 con1;
|
||||
|
||||
con1 = readl(ADC_V1_CON(info->regs));
|
||||
con1 &= ~ADC_S3C2410_CON_SELMUX(0x7);
|
||||
con1 |= ADC_S3C2410_CON_SELMUX(addr);
|
||||
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static struct exynos_adc_data const exynos_adc_s3c24xx_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
.start_conv = exynos_adc_s3c64xx_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static struct exynos_adc_data const exynos_adc_s3c64xx_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
.clear_irq = exynos_adc_v1_clear_irq,
|
||||
.start_conv = exynos_adc_s3c64xx_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static void exynos_adc_v2_init_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con1, con2;
|
||||
|
||||
con1 = ADC_V2_CON1_SOFT_RESET;
|
||||
writel(con1, ADC_V2_CON1(info->regs));
|
||||
|
||||
con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
|
||||
ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(6);
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
/* Enable interrupts */
|
||||
writel(1, ADC_V2_INT_EN(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v2_exit_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con2;
|
||||
|
||||
con2 = readl(ADC_V2_CON2(info->regs));
|
||||
con2 &= ~(ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
|
||||
ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(7));
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
/* Disable interrupts */
|
||||
writel(0, ADC_V2_INT_EN(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v2_clear_irq(struct exynos_adc *info)
|
||||
{
|
||||
writel(1, ADC_V2_INT_ST(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v2_start_conv(struct exynos_adc *info,
|
||||
unsigned long addr)
|
||||
{
|
||||
u32 con1, con2;
|
||||
|
||||
con2 = readl(ADC_V2_CON2(info->regs));
|
||||
con2 &= ~ADC_V2_CON2_ACH_MASK;
|
||||
con2 |= ADC_V2_CON2_ACH_SEL(addr);
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
con1 = readl(ADC_V2_CON1(info->regs));
|
||||
writel(con1 | ADC_CON_EN_START, ADC_V2_CON1(info->regs));
|
||||
}
|
||||
|
||||
static const struct exynos_adc_data exynos_adc_v2_data = {
|
||||
.num_channels = MAX_ADC_V2_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v2_init_hw,
|
||||
.exit_hw = exynos_adc_v2_exit_hw,
|
||||
.clear_irq = exynos_adc_v2_clear_irq,
|
||||
.start_conv = exynos_adc_v2_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static const struct exynos_adc_data exynos3250_adc_data = {
|
||||
.num_channels = MAX_EXYNOS3250_ADC_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
.needs_sclk = true,
|
||||
|
||||
.init_hw = exynos_adc_v2_init_hw,
|
||||
.exit_hw = exynos_adc_v2_exit_hw,
|
||||
.clear_irq = exynos_adc_v2_clear_irq,
|
||||
.start_conv = exynos_adc_v2_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static void exynos_adc_v3_init_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con1, con2;
|
||||
|
||||
con1 = ADC_V2_CON1_SOFT_RESET;
|
||||
writel(con1, ADC_V2_CON1(info->regs));
|
||||
|
||||
con1 = ADC_V2_CON1_SOFT_NON_RESET;
|
||||
writel(con1, ADC_V2_CON1(info->regs));
|
||||
|
||||
con2 = ADC_V2_CON2_C_TIME(6);
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
/* Enable interrupts */
|
||||
writel(1, ADC_V2_INT_EN(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v3_exit_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con2;
|
||||
|
||||
con2 = readl(ADC_V2_CON2(info->regs));
|
||||
con2 &= ~ADC_V2_CON2_C_TIME(7);
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
/* Disable interrupts */
|
||||
writel(0, ADC_V2_INT_EN(info->regs));
|
||||
}
|
||||
|
||||
static irqreturn_t exynos_adc_v3_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct exynos_adc *info = (struct exynos_adc *)dev_id;
|
||||
u32 mask = info->data->mask;
|
||||
|
||||
/* Read value */
|
||||
info->value = readl(ADC_V3_DAT(info->regs)) & mask;
|
||||
|
||||
/* clear irq */
|
||||
if (info->data->clear_irq)
|
||||
info->data->clear_irq(info);
|
||||
|
||||
complete(&info->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct exynos_adc_data exynos_adc_v3_data = {
|
||||
.num_channels = MAX_ADC_V3_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v3_init_hw,
|
||||
.exit_hw = exynos_adc_v3_exit_hw,
|
||||
.clear_irq = exynos_adc_v2_clear_irq,
|
||||
.start_conv = exynos_adc_v2_start_conv,
|
||||
.adc_isr = exynos_adc_v3_isr,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_adc_match[] = {
|
||||
{
|
||||
.compatible = "samsung,s3c2410-adc",
|
||||
.data = &exynos_adc_s3c24xx_data,
|
||||
}, {
|
||||
.compatible = "samsung,s3c2416-adc",
|
||||
.data = &exynos_adc_s3c2416_data,
|
||||
}, {
|
||||
.compatible = "samsung,s3c2440-adc",
|
||||
.data = &exynos_adc_s3c24xx_data,
|
||||
}, {
|
||||
.compatible = "samsung,s3c2443-adc",
|
||||
.data = &exynos_adc_s3c2443_data,
|
||||
}, {
|
||||
.compatible = "samsung,s3c6410-adc",
|
||||
.data = &exynos_adc_s3c64xx_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos-adc-v1",
|
||||
.data = &exynos_adc_v1_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos-adc-v2",
|
||||
.data = &exynos_adc_v2_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos-adc-v3",
|
||||
.data = &exynos_adc_v3_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos3250-adc",
|
||||
.data = &exynos3250_adc_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_adc_match);
|
||||
|
||||
static struct exynos_adc_data *exynos_adc_get_data(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(exynos_adc_match, pdev->dev.of_node);
|
||||
return (struct exynos_adc_data *)match->data;
|
||||
}
|
||||
|
||||
static int exynos_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
reinit_completion(&info->completion);
|
||||
ret = exynos_adc_enable_access(info);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
enable_irq(info->irq);
|
||||
if (info->data->init_hw)
|
||||
info->data->init_hw(info);
|
||||
|
||||
/* Select the channel to be used and Trigger conversion */
|
||||
if (info->data->start_conv)
|
||||
info->data->start_conv(info, chan->address);
|
||||
|
||||
timeout = wait_for_completion_timeout
|
||||
(&info->completion, EXYNOS_ADC_TIMEOUT);
|
||||
if (timeout == 0) {
|
||||
dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
*val = info->value;
|
||||
*val2 = 0;
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
|
||||
if (info->data->exit_hw)
|
||||
info->data->exit_hw(info);
|
||||
|
||||
disable_irq(info->irq);
|
||||
exynos_adc_disable_access(info);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int exynos_adc_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (readval == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = exynos_adc_enable_access(info);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*readval = readl(info->regs + reg);
|
||||
|
||||
exynos_adc_disable_access(info);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info exynos_adc_iio_info = {
|
||||
.read_raw = &exynos_read_raw,
|
||||
.debugfs_reg_access = &exynos_adc_reg_access,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define ADC_CHANNEL(_index, _id) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.address = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.datasheet_name = _id, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec exynos_adc_iio_channels[] = {
|
||||
ADC_CHANNEL(0, "adc0"),
|
||||
ADC_CHANNEL(1, "adc1"),
|
||||
ADC_CHANNEL(2, "adc2"),
|
||||
ADC_CHANNEL(3, "adc3"),
|
||||
ADC_CHANNEL(4, "adc4"),
|
||||
ADC_CHANNEL(5, "adc5"),
|
||||
ADC_CHANNEL(6, "adc6"),
|
||||
ADC_CHANNEL(7, "adc7"),
|
||||
ADC_CHANNEL(8, "adc8"),
|
||||
ADC_CHANNEL(9, "adc9"),
|
||||
};
|
||||
|
||||
static int exynos_adc_remove_devices(struct device *dev, void *c)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct exynos_adc *info = NULL;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct iio_dev *indio_dev = NULL;
|
||||
struct resource *mem;
|
||||
int ret = -ENODEV;
|
||||
int irq;
|
||||
|
||||
if (!np)
|
||||
return ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
|
||||
info->data = exynos_adc_get_data(pdev);
|
||||
if (!info->data) {
|
||||
dev_err(&pdev->dev, "failed getting exynos_adc_data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_find_property(np, "samsung,adc-phy-control", NULL))
|
||||
info->needs_adc_phy = true;
|
||||
else
|
||||
info->needs_adc_phy = false;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
|
||||
if (info->needs_adc_phy) {
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
info->enable_reg = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->enable_reg))
|
||||
return PTR_ERR(info->enable_reg);
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
info->irq = irq;
|
||||
info->dev = &pdev->dev;
|
||||
info->idle_ip_index = exynos_get_idle_ip_index(dev_name(&pdev->dev));
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
info->clk = devm_clk_get(&pdev->dev, "gate_adcif");
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
|
||||
PTR_ERR(info->clk));
|
||||
return PTR_ERR(info->clk);
|
||||
}
|
||||
|
||||
if (info->data->needs_sclk) {
|
||||
info->sclk = devm_clk_get(&pdev->dev, "sclk");
|
||||
if (IS_ERR(info->sclk)) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed getting sclk clock, err = %ld\n",
|
||||
PTR_ERR(info->sclk));
|
||||
return PTR_ERR(info->sclk);
|
||||
}
|
||||
}
|
||||
|
||||
info->vdd = devm_regulator_get(&pdev->dev, "vdd");
|
||||
if (IS_ERR(info->vdd)) {
|
||||
dev_err(&pdev->dev, "failed getting regulator, err = %ld\n",
|
||||
PTR_ERR(info->vdd));
|
||||
info->vdd = NULL;
|
||||
}
|
||||
|
||||
exynos_update_ip_idle_status(info->idle_ip_index, 0);
|
||||
if (info->vdd) {
|
||||
ret = regulator_enable(info->vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = exynos_adc_prepare_clk(info);
|
||||
if (ret)
|
||||
goto err_disable_reg;
|
||||
|
||||
ret = exynos_adc_enable_clk(info);
|
||||
if (ret)
|
||||
goto err_unprepare_clk;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &exynos_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = exynos_adc_iio_channels;
|
||||
indio_dev->num_channels = info->data->num_channels;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, info->irq, info->data->adc_isr,
|
||||
0, dev_name(&pdev->dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
|
||||
info->irq);
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
disable_irq(info->irq);
|
||||
exynos_adc_disable_clk(info);
|
||||
exynos_adc_unprepare_clk(info);
|
||||
if (info->vdd)
|
||||
regulator_disable(info->vdd);
|
||||
exynos_update_ip_idle_status(info->idle_ip_index, 1);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed adding child nodes\n");
|
||||
goto err_of_populate;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Probed successfully driver.\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_of_populate:
|
||||
device_for_each_child(&indio_dev->dev, NULL,
|
||||
exynos_adc_remove_devices);
|
||||
iio_device_unregister(indio_dev);
|
||||
err_irq:
|
||||
free_irq(info->irq, info);
|
||||
err_disable_clk:
|
||||
if (info->data->exit_hw)
|
||||
info->data->exit_hw(info);
|
||||
exynos_adc_disable_clk(info);
|
||||
err_unprepare_clk:
|
||||
exynos_adc_unprepare_clk(info);
|
||||
err_disable_reg:
|
||||
if (info->vdd)
|
||||
regulator_disable(info->vdd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
device_for_each_child(&indio_dev->dev, NULL,
|
||||
exynos_adc_remove_devices);
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
ret = exynos_adc_enable_access(info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (info->data->exit_hw)
|
||||
info->data->exit_hw(info);
|
||||
|
||||
exynos_adc_disable_access(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int exynos_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = exynos_adc_enable_access(info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (info->data->exit_hw)
|
||||
info->data->exit_hw(info);
|
||||
|
||||
exynos_adc_disable_access(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = exynos_adc_enable_access(info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (info->data->init_hw)
|
||||
info->data->init_hw(info);
|
||||
|
||||
exynos_adc_disable_access(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops,
|
||||
exynos_adc_suspend,
|
||||
exynos_adc_resume);
|
||||
|
||||
static struct platform_driver exynos_adc_driver = {
|
||||
.probe = exynos_adc_probe,
|
||||
.remove = exynos_adc_remove,
|
||||
.driver = {
|
||||
.name = "exynos-adc",
|
||||
.of_match_table = exynos_adc_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(exynos_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Naveen Krishna Chatradhi <ch.naveen@samsung.com>");
|
||||
MODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
254
drivers/iio/adc/lp8788_adc.c
Normal file
254
drivers/iio/adc/lp8788_adc.c
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* TI LP8788 MFD - ADC driver
|
||||
*
|
||||
* Copyright 2012 Texas Instruments
|
||||
*
|
||||
* Author: Milo(Woogyom) Kim <milo.kim@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/mfd/lp8788.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* register address */
|
||||
#define LP8788_ADC_CONF 0x60
|
||||
#define LP8788_ADC_RAW 0x61
|
||||
#define LP8788_ADC_DONE 0x63
|
||||
|
||||
#define ADC_CONV_START 1
|
||||
|
||||
struct lp8788_adc {
|
||||
struct lp8788 *lp;
|
||||
struct iio_map *map;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static const int lp8788_scale[LPADC_MAX] = {
|
||||
[LPADC_VBATT_5P5] = 1343101,
|
||||
[LPADC_VIN_CHG] = 3052503,
|
||||
[LPADC_IBATT] = 610500,
|
||||
[LPADC_IC_TEMP] = 61050,
|
||||
[LPADC_VBATT_6P0] = 1465201,
|
||||
[LPADC_VBATT_5P0] = 1221001,
|
||||
[LPADC_ADC1] = 610500,
|
||||
[LPADC_ADC2] = 610500,
|
||||
[LPADC_VDD] = 1025641,
|
||||
[LPADC_VCOIN] = 757020,
|
||||
[LPADC_ADC3] = 610500,
|
||||
[LPADC_ADC4] = 610500,
|
||||
};
|
||||
|
||||
static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id,
|
||||
int *val)
|
||||
{
|
||||
unsigned int msb;
|
||||
unsigned int lsb;
|
||||
unsigned int result;
|
||||
u8 data;
|
||||
u8 rawdata[2];
|
||||
int size = ARRAY_SIZE(rawdata);
|
||||
int retry = 5;
|
||||
int ret;
|
||||
|
||||
data = (id << 1) | ADC_CONV_START;
|
||||
ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data);
|
||||
if (ret)
|
||||
goto err_io;
|
||||
|
||||
/* retry until adc conversion is done */
|
||||
data = 0;
|
||||
while (retry--) {
|
||||
usleep_range(100, 200);
|
||||
|
||||
ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data);
|
||||
if (ret)
|
||||
goto err_io;
|
||||
|
||||
/* conversion done */
|
||||
if (data)
|
||||
break;
|
||||
}
|
||||
|
||||
ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size);
|
||||
if (ret)
|
||||
goto err_io;
|
||||
|
||||
msb = (rawdata[0] << 4) & 0x00000ff0;
|
||||
lsb = (rawdata[1] >> 4) & 0x0000000f;
|
||||
result = msb | lsb;
|
||||
*val = result;
|
||||
|
||||
return 0;
|
||||
|
||||
err_io:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp8788_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct lp8788_adc *adc = iio_priv(indio_dev);
|
||||
enum lp8788_adc_id id = chan->channel;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = lp8788_scale[id] / 1000000;
|
||||
*val2 = lp8788_scale[id] % 1000000;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info lp8788_adc_info = {
|
||||
.read_raw = &lp8788_adc_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define LP8788_CHAN(_id, _type) { \
|
||||
.type = _type, \
|
||||
.indexed = 1, \
|
||||
.channel = LPADC_##_id, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = #_id, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec lp8788_adc_channels[] = {
|
||||
[LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE),
|
||||
[LPADC_VIN_CHG] = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE),
|
||||
[LPADC_IBATT] = LP8788_CHAN(IBATT, IIO_CURRENT),
|
||||
[LPADC_IC_TEMP] = LP8788_CHAN(IC_TEMP, IIO_TEMP),
|
||||
[LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE),
|
||||
[LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE),
|
||||
[LPADC_ADC1] = LP8788_CHAN(ADC1, IIO_VOLTAGE),
|
||||
[LPADC_ADC2] = LP8788_CHAN(ADC2, IIO_VOLTAGE),
|
||||
[LPADC_VDD] = LP8788_CHAN(VDD, IIO_VOLTAGE),
|
||||
[LPADC_VCOIN] = LP8788_CHAN(VCOIN, IIO_VOLTAGE),
|
||||
[LPADC_ADC3] = LP8788_CHAN(ADC3, IIO_VOLTAGE),
|
||||
[LPADC_ADC4] = LP8788_CHAN(ADC4, IIO_VOLTAGE),
|
||||
};
|
||||
|
||||
/* default maps used by iio consumer (lp8788-charger driver) */
|
||||
static struct iio_map lp8788_default_iio_maps[] = {
|
||||
{
|
||||
.consumer_dev_name = "lp8788-charger",
|
||||
.consumer_channel = "lp8788_vbatt_5p0",
|
||||
.adc_channel_label = "VBATT_5P0",
|
||||
},
|
||||
{
|
||||
.consumer_dev_name = "lp8788-charger",
|
||||
.consumer_channel = "lp8788_adc1",
|
||||
.adc_channel_label = "ADC1",
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int lp8788_iio_map_register(struct iio_dev *indio_dev,
|
||||
struct lp8788_platform_data *pdata,
|
||||
struct lp8788_adc *adc)
|
||||
{
|
||||
struct iio_map *map;
|
||||
int ret;
|
||||
|
||||
map = (!pdata || !pdata->adc_pdata) ?
|
||||
lp8788_default_iio_maps : pdata->adc_pdata;
|
||||
|
||||
ret = iio_map_array_register(indio_dev, map);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "iio map err: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc->map = map;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lp8788_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
|
||||
struct iio_dev *indio_dev;
|
||||
struct lp8788_adc *adc;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->lp = lp;
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = pdev->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &lp8788_adc_info;
|
||||
indio_dev->channels = lp8788_adc_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "iio dev register err: %d\n", ret);
|
||||
goto err_iio_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_iio_device:
|
||||
iio_map_array_unregister(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp8788_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver lp8788_adc_driver = {
|
||||
.probe = lp8788_adc_probe,
|
||||
.remove = lp8788_adc_remove,
|
||||
.driver = {
|
||||
.name = LP8788_DEV_ADC,
|
||||
},
|
||||
};
|
||||
module_platform_driver(lp8788_adc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver");
|
||||
MODULE_AUTHOR("Milo Kim");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:lp8788-adc");
|
||||
521
drivers/iio/adc/max1027.c
Normal file
521
drivers/iio/adc/max1027.c
Normal file
|
|
@ -0,0 +1,521 @@
|
|||
/*
|
||||
* iio/adc/max1027.c
|
||||
* Copyright (C) 2014 Philippe Reynes
|
||||
*
|
||||
* based on linux/drivers/iio/ad7923.c
|
||||
* Copyright 2011 Analog Devices Inc (from AD7923 Driver)
|
||||
* Copyright 2012 CS Systemes d'Information
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* max1027.c
|
||||
*
|
||||
* Partial support for max1027 and similar chips.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define MAX1027_CONV_REG BIT(7)
|
||||
#define MAX1027_SETUP_REG BIT(6)
|
||||
#define MAX1027_AVG_REG BIT(5)
|
||||
#define MAX1027_RST_REG BIT(4)
|
||||
|
||||
/* conversion register */
|
||||
#define MAX1027_TEMP BIT(0)
|
||||
#define MAX1027_SCAN_0_N (0x00 << 1)
|
||||
#define MAX1027_SCAN_N_M (0x01 << 1)
|
||||
#define MAX1027_SCAN_N (0x02 << 1)
|
||||
#define MAX1027_NOSCAN (0x03 << 1)
|
||||
#define MAX1027_CHAN(n) ((n) << 3)
|
||||
|
||||
/* setup register */
|
||||
#define MAX1027_UNIPOLAR 0x02
|
||||
#define MAX1027_BIPOLAR 0x03
|
||||
#define MAX1027_REF_MODE0 (0x00 << 2)
|
||||
#define MAX1027_REF_MODE1 (0x01 << 2)
|
||||
#define MAX1027_REF_MODE2 (0x02 << 2)
|
||||
#define MAX1027_REF_MODE3 (0x03 << 2)
|
||||
#define MAX1027_CKS_MODE0 (0x00 << 4)
|
||||
#define MAX1027_CKS_MODE1 (0x01 << 4)
|
||||
#define MAX1027_CKS_MODE2 (0x02 << 4)
|
||||
#define MAX1027_CKS_MODE3 (0x03 << 4)
|
||||
|
||||
/* averaging register */
|
||||
#define MAX1027_NSCAN_4 0x00
|
||||
#define MAX1027_NSCAN_8 0x01
|
||||
#define MAX1027_NSCAN_12 0x02
|
||||
#define MAX1027_NSCAN_16 0x03
|
||||
#define MAX1027_NAVG_4 (0x00 << 2)
|
||||
#define MAX1027_NAVG_8 (0x01 << 2)
|
||||
#define MAX1027_NAVG_16 (0x02 << 2)
|
||||
#define MAX1027_NAVG_32 (0x03 << 2)
|
||||
#define MAX1027_AVG_EN BIT(4)
|
||||
|
||||
enum max1027_id {
|
||||
max1027,
|
||||
max1029,
|
||||
max1031,
|
||||
};
|
||||
|
||||
static const struct spi_device_id max1027_id[] = {
|
||||
{"max1027", max1027},
|
||||
{"max1029", max1029},
|
||||
{"max1031", max1031},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, max1027_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id max1027_adc_dt_ids[] = {
|
||||
{ .compatible = "maxim,max1027" },
|
||||
{ .compatible = "maxim,max1029" },
|
||||
{ .compatible = "maxim,max1031" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids);
|
||||
#endif
|
||||
|
||||
#define MAX1027_V_CHAN(index) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = index + 1, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 10, \
|
||||
.storagebits = 16, \
|
||||
.shift = 2, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define MAX1027_T_CHAN \
|
||||
{ \
|
||||
.type = IIO_TEMP, \
|
||||
.channel = 0, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = 0, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max1027_channels[] = {
|
||||
MAX1027_T_CHAN,
|
||||
MAX1027_V_CHAN(0),
|
||||
MAX1027_V_CHAN(1),
|
||||
MAX1027_V_CHAN(2),
|
||||
MAX1027_V_CHAN(3),
|
||||
MAX1027_V_CHAN(4),
|
||||
MAX1027_V_CHAN(5),
|
||||
MAX1027_V_CHAN(6),
|
||||
MAX1027_V_CHAN(7)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max1029_channels[] = {
|
||||
MAX1027_T_CHAN,
|
||||
MAX1027_V_CHAN(0),
|
||||
MAX1027_V_CHAN(1),
|
||||
MAX1027_V_CHAN(2),
|
||||
MAX1027_V_CHAN(3),
|
||||
MAX1027_V_CHAN(4),
|
||||
MAX1027_V_CHAN(5),
|
||||
MAX1027_V_CHAN(6),
|
||||
MAX1027_V_CHAN(7),
|
||||
MAX1027_V_CHAN(8),
|
||||
MAX1027_V_CHAN(9),
|
||||
MAX1027_V_CHAN(10),
|
||||
MAX1027_V_CHAN(11)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max1031_channels[] = {
|
||||
MAX1027_T_CHAN,
|
||||
MAX1027_V_CHAN(0),
|
||||
MAX1027_V_CHAN(1),
|
||||
MAX1027_V_CHAN(2),
|
||||
MAX1027_V_CHAN(3),
|
||||
MAX1027_V_CHAN(4),
|
||||
MAX1027_V_CHAN(5),
|
||||
MAX1027_V_CHAN(6),
|
||||
MAX1027_V_CHAN(7),
|
||||
MAX1027_V_CHAN(8),
|
||||
MAX1027_V_CHAN(9),
|
||||
MAX1027_V_CHAN(10),
|
||||
MAX1027_V_CHAN(11),
|
||||
MAX1027_V_CHAN(12),
|
||||
MAX1027_V_CHAN(13),
|
||||
MAX1027_V_CHAN(14),
|
||||
MAX1027_V_CHAN(15)
|
||||
};
|
||||
|
||||
static const unsigned long max1027_available_scan_masks[] = {
|
||||
0x000001ff,
|
||||
0x00000000,
|
||||
};
|
||||
|
||||
static const unsigned long max1029_available_scan_masks[] = {
|
||||
0x00001fff,
|
||||
0x00000000,
|
||||
};
|
||||
|
||||
static const unsigned long max1031_available_scan_masks[] = {
|
||||
0x0001ffff,
|
||||
0x00000000,
|
||||
};
|
||||
|
||||
struct max1027_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
const unsigned long *available_scan_masks;
|
||||
};
|
||||
|
||||
static const struct max1027_chip_info max1027_chip_info_tbl[] = {
|
||||
[max1027] = {
|
||||
.channels = max1027_channels,
|
||||
.num_channels = ARRAY_SIZE(max1027_channels),
|
||||
.available_scan_masks = max1027_available_scan_masks,
|
||||
},
|
||||
[max1029] = {
|
||||
.channels = max1029_channels,
|
||||
.num_channels = ARRAY_SIZE(max1029_channels),
|
||||
.available_scan_masks = max1029_available_scan_masks,
|
||||
},
|
||||
[max1031] = {
|
||||
.channels = max1031_channels,
|
||||
.num_channels = ARRAY_SIZE(max1031_channels),
|
||||
.available_scan_masks = max1031_available_scan_masks,
|
||||
},
|
||||
};
|
||||
|
||||
struct max1027_state {
|
||||
const struct max1027_chip_info *info;
|
||||
struct spi_device *spi;
|
||||
struct iio_trigger *trig;
|
||||
__be16 *buffer;
|
||||
struct mutex lock;
|
||||
|
||||
u8 reg ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int max1027_read_single_value(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val)
|
||||
{
|
||||
int ret;
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
dev_warn(&indio_dev->dev, "trigger mode already enabled");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Start acquisition on conversion register write */
|
||||
st->reg = MAX1027_SETUP_REG | MAX1027_REF_MODE2 | MAX1027_CKS_MODE2;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"Failed to configure setup register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure conversion register with the requested chan */
|
||||
st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) |
|
||||
MAX1027_NOSCAN | !!(chan->type == IIO_TEMP);
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"Failed to configure conversion register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* For an unknown reason, when we use the mode "10" (write
|
||||
* conversion register), the interrupt doesn't occur every time.
|
||||
* So we just wait 1 ms.
|
||||
*/
|
||||
mdelay(1);
|
||||
|
||||
/* Read result */
|
||||
ret = spi_read(st->spi, st->buffer, (chan->type == IIO_TEMP) ? 4 : 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = be16_to_cpu(st->buffer[0]);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int max1027_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret = 0;
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = max1027_read_single_value(indio_dev, chan, val);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
*val = 1;
|
||||
*val2 = 8;
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
break;
|
||||
case IIO_VOLTAGE:
|
||||
*val = 2500;
|
||||
*val2 = 10;
|
||||
ret = IIO_VAL_FRACTIONAL_LOG2;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max1027_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
u8 *val = (u8 *)st->buffer;
|
||||
|
||||
if (readval != NULL)
|
||||
return -EINVAL;
|
||||
|
||||
*val = (u8)writeval;
|
||||
return spi_write(st->spi, val, 1);
|
||||
}
|
||||
|
||||
static int max1027_validate_trigger(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->trig != trig)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max1027_set_trigger_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (state) {
|
||||
/* Start acquisition on cnvst */
|
||||
st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE0 |
|
||||
MAX1027_REF_MODE2;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Scan from 0 to max */
|
||||
st->reg = MAX1027_CONV_REG | MAX1027_CHAN(0) |
|
||||
MAX1027_SCAN_N_M | MAX1027_TEMP;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
/* Start acquisition on conversion register write */
|
||||
st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE2 |
|
||||
MAX1027_REF_MODE2;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max1027_validate_device(struct iio_trigger *trig,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
|
||||
|
||||
if (indio != indio_dev)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t max1027_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = (struct iio_poll_func *)private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
|
||||
pr_debug("%s(irq=%d, private=0x%p)\n", __func__, irq, private);
|
||||
|
||||
/* fill buffer with all channel */
|
||||
spi_read(st->spi, st->buffer, indio_dev->masklength * 2);
|
||||
|
||||
iio_push_to_buffers(indio_dev, st->buffer);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops max1027_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.validate_device = &max1027_validate_device,
|
||||
.set_trigger_state = &max1027_set_trigger_state,
|
||||
};
|
||||
|
||||
static const struct iio_info max1027_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &max1027_read_raw,
|
||||
.validate_trigger = &max1027_validate_trigger,
|
||||
.debugfs_reg_access = &max1027_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static int max1027_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct max1027_state *st;
|
||||
|
||||
pr_debug("%s: probe(spi = 0x%p)\n", __func__, spi);
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
pr_err("Can't allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->spi = spi;
|
||||
st->info = &max1027_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
mutex_init(&st->lock);
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &max1027_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->info->channels;
|
||||
indio_dev->num_channels = st->info->num_channels;
|
||||
indio_dev->available_scan_masks = st->info->available_scan_masks;
|
||||
|
||||
st->buffer = devm_kmalloc(&indio_dev->dev,
|
||||
indio_dev->num_channels * 2,
|
||||
GFP_KERNEL);
|
||||
if (st->buffer == NULL) {
|
||||
dev_err(&indio_dev->dev, "Can't allocate bufffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&max1027_trigger_handler, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to setup buffer\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger",
|
||||
indio_dev->name);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&indio_dev->dev, "Failed to allocate iio trigger\n");
|
||||
goto fail_trigger_alloc;
|
||||
}
|
||||
|
||||
st->trig->ops = &max1027_trigger_ops;
|
||||
st->trig->dev.parent = &spi->dev;
|
||||
iio_trigger_set_drvdata(st->trig, indio_dev);
|
||||
iio_trigger_register(st->trig);
|
||||
|
||||
ret = devm_request_threaded_irq(&spi->dev, spi->irq,
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
NULL,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
spi->dev.driver->name, st->trig);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n");
|
||||
goto fail_dev_register;
|
||||
}
|
||||
|
||||
/* Disable averaging */
|
||||
st->reg = MAX1027_AVG_REG;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to configure averaging register\n");
|
||||
goto fail_dev_register;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to register iio device\n");
|
||||
goto fail_dev_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_dev_register:
|
||||
fail_trigger_alloc:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max1027_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
|
||||
pr_debug("%s: remove(spi = 0x%p)\n", __func__, spi);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver max1027_driver = {
|
||||
.driver = {
|
||||
.name = "max1027",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = max1027_probe,
|
||||
.remove = max1027_remove,
|
||||
.id_table = max1027_id,
|
||||
};
|
||||
module_spi_driver(max1027_driver);
|
||||
|
||||
MODULE_AUTHOR("Philippe Reynes <tremyfr@yahoo.fr>");
|
||||
MODULE_DESCRIPTION("MAX1027/MAX1029/MAX1031 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1701
drivers/iio/adc/max1363.c
Normal file
1701
drivers/iio/adc/max1363.c
Normal file
File diff suppressed because it is too large
Load diff
249
drivers/iio/adc/mcp320x.c
Normal file
249
drivers/iio/adc/mcp320x.c
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
|
||||
*
|
||||
* Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips.
|
||||
* Datasheet can be found here:
|
||||
* http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf
|
||||
*
|
||||
* 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/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define MCP_SINGLE_ENDED (1 << 3)
|
||||
#define MCP_START_BIT (1 << 4)
|
||||
|
||||
enum {
|
||||
mcp3204,
|
||||
mcp3208,
|
||||
};
|
||||
|
||||
struct mcp320x {
|
||||
struct spi_device *spi;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer transfer[2];
|
||||
|
||||
u8 tx_buf;
|
||||
u8 rx_buf[2];
|
||||
|
||||
struct regulator *reg;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
adc->tx_buf = msg;
|
||||
ret = spi_sync(adc->spi, &adc->msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ((adc->rx_buf[0] & 0x3f) << 6) |
|
||||
(adc->rx_buf[1] >> 2);
|
||||
}
|
||||
|
||||
static int mcp320x_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct mcp320x *adc = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (channel->differential)
|
||||
ret = mcp320x_adc_conversion(adc,
|
||||
MCP_START_BIT | channel->address);
|
||||
else
|
||||
ret = mcp320x_adc_conversion(adc,
|
||||
MCP_START_BIT | MCP_SINGLE_ENDED |
|
||||
channel->address);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
*val = ret;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Digital output code = (4096 * Vin) / Vref */
|
||||
ret = regulator_get_voltage(adc->reg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = 12;
|
||||
ret = IIO_VAL_FRACTIONAL_LOG2;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MCP320X_VOLTAGE_CHANNEL(num) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (num), \
|
||||
.address = (num), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
#define MCP320X_VOLTAGE_CHANNEL_DIFF(num) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (num * 2), \
|
||||
.channel2 = (num * 2 + 1), \
|
||||
.address = (num * 2), \
|
||||
.differential = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mcp3204_channels[] = {
|
||||
MCP320X_VOLTAGE_CHANNEL(0),
|
||||
MCP320X_VOLTAGE_CHANNEL(1),
|
||||
MCP320X_VOLTAGE_CHANNEL(2),
|
||||
MCP320X_VOLTAGE_CHANNEL(3),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3208_channels[] = {
|
||||
MCP320X_VOLTAGE_CHANNEL(0),
|
||||
MCP320X_VOLTAGE_CHANNEL(1),
|
||||
MCP320X_VOLTAGE_CHANNEL(2),
|
||||
MCP320X_VOLTAGE_CHANNEL(3),
|
||||
MCP320X_VOLTAGE_CHANNEL(4),
|
||||
MCP320X_VOLTAGE_CHANNEL(5),
|
||||
MCP320X_VOLTAGE_CHANNEL(6),
|
||||
MCP320X_VOLTAGE_CHANNEL(7),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(1),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(2),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(3),
|
||||
};
|
||||
|
||||
static const struct iio_info mcp320x_info = {
|
||||
.read_raw = mcp320x_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
struct mcp3208_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
static const struct mcp3208_chip_info mcp3208_chip_infos[] = {
|
||||
[mcp3204] = {
|
||||
.channels = mcp3204_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3204_channels)
|
||||
},
|
||||
[mcp3208] = {
|
||||
.channels = mcp3208_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3208_channels)
|
||||
},
|
||||
};
|
||||
|
||||
static int mcp320x_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp320x *adc;
|
||||
const struct mcp3208_chip_info *chip_info;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->spi = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mcp320x_info;
|
||||
|
||||
chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data];
|
||||
indio_dev->channels = chip_info->channels;
|
||||
indio_dev->num_channels = chip_info->num_channels;
|
||||
|
||||
adc->transfer[0].tx_buf = &adc->tx_buf;
|
||||
adc->transfer[0].len = sizeof(adc->tx_buf);
|
||||
adc->transfer[1].rx_buf = adc->rx_buf;
|
||||
adc->transfer[1].len = sizeof(adc->rx_buf);
|
||||
|
||||
spi_message_init_with_transfers(&adc->msg, adc->transfer,
|
||||
ARRAY_SIZE(adc->transfer));
|
||||
|
||||
adc->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(adc->reg))
|
||||
return PTR_ERR(adc->reg);
|
||||
|
||||
ret = regulator_enable(adc->reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto reg_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
reg_disable:
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp320x_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct mcp320x *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id mcp320x_id[] = {
|
||||
{ "mcp3204", mcp3204 },
|
||||
{ "mcp3208", mcp3208 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, mcp320x_id);
|
||||
|
||||
static struct spi_driver mcp320x_driver = {
|
||||
.driver = {
|
||||
.name = "mcp320x",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mcp320x_probe,
|
||||
.remove = mcp320x_remove,
|
||||
.id_table = mcp320x_id,
|
||||
};
|
||||
module_spi_driver(mcp320x_driver);
|
||||
|
||||
MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
|
||||
MODULE_DESCRIPTION("Microchip Technology MCP3204/08");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
417
drivers/iio/adc/mcp3422.c
Normal file
417
drivers/iio/adc/mcp3422.c
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* mcp3422.c - driver for the Microchip mcp3422/3/4/6/7/8 chip family
|
||||
*
|
||||
* Copyright (C) 2013, Angelo Compagnucci
|
||||
* Author: Angelo Compagnucci <angelo.compagnucci@gmail.com>
|
||||
*
|
||||
* Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf
|
||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf
|
||||
*
|
||||
* This driver exports the value of analog input voltage to sysfs, the
|
||||
* voltage unit is nV.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* Masks */
|
||||
#define MCP3422_CHANNEL_MASK 0x60
|
||||
#define MCP3422_PGA_MASK 0x03
|
||||
#define MCP3422_SRATE_MASK 0x0C
|
||||
#define MCP3422_SRATE_240 0x0
|
||||
#define MCP3422_SRATE_60 0x1
|
||||
#define MCP3422_SRATE_15 0x2
|
||||
#define MCP3422_SRATE_3 0x3
|
||||
#define MCP3422_PGA_1 0
|
||||
#define MCP3422_PGA_2 1
|
||||
#define MCP3422_PGA_4 2
|
||||
#define MCP3422_PGA_8 3
|
||||
#define MCP3422_CONT_SAMPLING 0x10
|
||||
|
||||
#define MCP3422_CHANNEL(config) (((config) & MCP3422_CHANNEL_MASK) >> 5)
|
||||
#define MCP3422_PGA(config) ((config) & MCP3422_PGA_MASK)
|
||||
#define MCP3422_SAMPLE_RATE(config) (((config) & MCP3422_SRATE_MASK) >> 2)
|
||||
|
||||
#define MCP3422_CHANNEL_VALUE(value) (((value) << 5) & MCP3422_CHANNEL_MASK)
|
||||
#define MCP3422_PGA_VALUE(value) ((value) & MCP3422_PGA_MASK)
|
||||
#define MCP3422_SAMPLE_RATE_VALUE(value) ((value << 2) & MCP3422_SRATE_MASK)
|
||||
|
||||
#define MCP3422_CHAN(_index) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
|
||||
| BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const int mcp3422_scales[4][4] = {
|
||||
{ 1000000, 500000, 250000, 125000 },
|
||||
{ 250000 , 125000, 62500 , 31250 },
|
||||
{ 62500 , 31250 , 15625 , 7812 },
|
||||
{ 15625 , 7812 , 3906 , 1953 } };
|
||||
|
||||
/* Constant msleep times for data acquisitions */
|
||||
static const int mcp3422_read_times[4] = {
|
||||
[MCP3422_SRATE_240] = 1000 / 240,
|
||||
[MCP3422_SRATE_60] = 1000 / 60,
|
||||
[MCP3422_SRATE_15] = 1000 / 15,
|
||||
[MCP3422_SRATE_3] = 1000 / 3 };
|
||||
|
||||
/* sample rates to integer conversion table */
|
||||
static const int mcp3422_sample_rates[4] = {
|
||||
[MCP3422_SRATE_240] = 240,
|
||||
[MCP3422_SRATE_60] = 60,
|
||||
[MCP3422_SRATE_15] = 15,
|
||||
[MCP3422_SRATE_3] = 3 };
|
||||
|
||||
/* sample rates to sign extension table */
|
||||
static const int mcp3422_sign_extend[4] = {
|
||||
[MCP3422_SRATE_240] = 11,
|
||||
[MCP3422_SRATE_60] = 13,
|
||||
[MCP3422_SRATE_15] = 15,
|
||||
[MCP3422_SRATE_3] = 17 };
|
||||
|
||||
/* Client data (each client gets its own) */
|
||||
struct mcp3422 {
|
||||
struct i2c_client *i2c;
|
||||
u8 id;
|
||||
u8 config;
|
||||
u8 pga[4];
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static int mcp3422_update_config(struct mcp3422 *adc, u8 newconfig)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
ret = i2c_master_send(adc->i2c, &newconfig, 1);
|
||||
if (ret > 0) {
|
||||
adc->config = newconfig;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3422_read(struct mcp3422 *adc, int *value, u8 *config)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
|
||||
u8 buf[4] = {0, 0, 0, 0};
|
||||
u32 temp;
|
||||
|
||||
if (sample_rate == MCP3422_SRATE_3) {
|
||||
ret = i2c_master_recv(adc->i2c, buf, 4);
|
||||
temp = buf[0] << 16 | buf[1] << 8 | buf[2];
|
||||
*config = buf[3];
|
||||
} else {
|
||||
ret = i2c_master_recv(adc->i2c, buf, 3);
|
||||
temp = buf[0] << 8 | buf[1];
|
||||
*config = buf[2];
|
||||
}
|
||||
|
||||
*value = sign_extend32(temp, mcp3422_sign_extend[sample_rate]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3422_read_channel(struct mcp3422 *adc,
|
||||
struct iio_chan_spec const *channel, int *value)
|
||||
{
|
||||
int ret;
|
||||
u8 config;
|
||||
u8 req_channel = channel->channel;
|
||||
|
||||
if (req_channel != MCP3422_CHANNEL(adc->config)) {
|
||||
config = adc->config;
|
||||
config &= ~MCP3422_CHANNEL_MASK;
|
||||
config |= MCP3422_CHANNEL_VALUE(req_channel);
|
||||
config &= ~MCP3422_PGA_MASK;
|
||||
config |= MCP3422_PGA_VALUE(adc->pga[req_channel]);
|
||||
ret = mcp3422_update_config(adc, config);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
msleep(mcp3422_read_times[MCP3422_SAMPLE_RATE(adc->config)]);
|
||||
}
|
||||
|
||||
return mcp3422_read(adc, value, &config);
|
||||
}
|
||||
|
||||
static int mcp3422_read_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *channel, int *val1,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct mcp3422 *adc = iio_priv(iio);
|
||||
int err;
|
||||
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
|
||||
u8 pga = MCP3422_PGA(adc->config);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
err = mcp3422_read_channel(adc, channel, val1);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
|
||||
*val1 = 0;
|
||||
*val2 = mcp3422_scales[sample_rate][pga];
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val1 = mcp3422_sample_rates[MCP3422_SAMPLE_RATE(adc->config)];
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp3422_write_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *channel, int val1,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct mcp3422 *adc = iio_priv(iio);
|
||||
u8 temp;
|
||||
u8 config = adc->config;
|
||||
u8 req_channel = channel->channel;
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(config);
|
||||
u8 i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val1 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mcp3422_scales[0]); i++) {
|
||||
if (val2 == mcp3422_scales[sample_rate][i]) {
|
||||
adc->pga[req_channel] = i;
|
||||
|
||||
config &= ~MCP3422_CHANNEL_MASK;
|
||||
config |= MCP3422_CHANNEL_VALUE(req_channel);
|
||||
config &= ~MCP3422_PGA_MASK;
|
||||
config |= MCP3422_PGA_VALUE(adc->pga[req_channel]);
|
||||
|
||||
return mcp3422_update_config(adc, config);
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
switch (val1) {
|
||||
case 240:
|
||||
temp = MCP3422_SRATE_240;
|
||||
break;
|
||||
case 60:
|
||||
temp = MCP3422_SRATE_60;
|
||||
break;
|
||||
case 15:
|
||||
temp = MCP3422_SRATE_15;
|
||||
break;
|
||||
case 3:
|
||||
if (adc->id > 4)
|
||||
return -EINVAL;
|
||||
temp = MCP3422_SRATE_3;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config &= ~MCP3422_CHANNEL_MASK;
|
||||
config |= MCP3422_CHANNEL_VALUE(req_channel);
|
||||
config &= ~MCP3422_SRATE_MASK;
|
||||
config |= MCP3422_SAMPLE_RATE_VALUE(temp);
|
||||
|
||||
return mcp3422_update_config(adc, config);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp3422_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t mcp3422_show_samp_freqs(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
if (adc->id > 4)
|
||||
return sprintf(buf, "240 60 15\n");
|
||||
|
||||
return sprintf(buf, "240 60 15 3\n");
|
||||
}
|
||||
|
||||
static ssize_t mcp3422_show_scales(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev));
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
|
||||
|
||||
return sprintf(buf, "0.%09u 0.%09u 0.%09u 0.%09u\n",
|
||||
mcp3422_scales[sample_rate][0],
|
||||
mcp3422_scales[sample_rate][1],
|
||||
mcp3422_scales[sample_rate][2],
|
||||
mcp3422_scales[sample_rate][3]);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
|
||||
mcp3422_show_samp_freqs, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO,
|
||||
mcp3422_show_scales, NULL, 0);
|
||||
|
||||
static struct attribute *mcp3422_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group mcp3422_attribute_group = {
|
||||
.attrs = mcp3422_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3422_channels[] = {
|
||||
MCP3422_CHAN(0),
|
||||
MCP3422_CHAN(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3424_channels[] = {
|
||||
MCP3422_CHAN(0),
|
||||
MCP3422_CHAN(1),
|
||||
MCP3422_CHAN(2),
|
||||
MCP3422_CHAN(3),
|
||||
};
|
||||
|
||||
static const struct iio_info mcp3422_info = {
|
||||
.read_raw = mcp3422_read_raw,
|
||||
.write_raw = mcp3422_write_raw,
|
||||
.write_raw_get_fmt = mcp3422_write_raw_get_fmt,
|
||||
.attrs = &mcp3422_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mcp3422_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp3422 *adc;
|
||||
int err;
|
||||
u8 config;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->i2c = client;
|
||||
adc->id = (u8)(id->driver_data);
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mcp3422_info;
|
||||
|
||||
switch (adc->id) {
|
||||
case 2:
|
||||
case 3:
|
||||
case 6:
|
||||
case 7:
|
||||
indio_dev->channels = mcp3422_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mcp3422_channels);
|
||||
break;
|
||||
case 4:
|
||||
case 8:
|
||||
indio_dev->channels = mcp3424_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mcp3424_channels);
|
||||
break;
|
||||
}
|
||||
|
||||
/* meaningful default configuration */
|
||||
config = (MCP3422_CONT_SAMPLING
|
||||
| MCP3422_CHANNEL_VALUE(1)
|
||||
| MCP3422_PGA_VALUE(MCP3422_PGA_1)
|
||||
| MCP3422_SAMPLE_RATE_VALUE(MCP3422_SRATE_240));
|
||||
mcp3422_update_config(adc, config);
|
||||
|
||||
err = devm_iio_device_register(&client->dev, indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp3422_id[] = {
|
||||
{ "mcp3422", 2 },
|
||||
{ "mcp3423", 3 },
|
||||
{ "mcp3424", 4 },
|
||||
{ "mcp3426", 6 },
|
||||
{ "mcp3427", 7 },
|
||||
{ "mcp3428", 8 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp3422_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id mcp3422_of_match[] = {
|
||||
{ .compatible = "mcp3422" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp3422_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver mcp3422_driver = {
|
||||
.driver = {
|
||||
.name = "mcp3422",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(mcp3422_of_match),
|
||||
},
|
||||
.probe = mcp3422_probe,
|
||||
.id_table = mcp3422_id,
|
||||
};
|
||||
module_i2c_driver(mcp3422_driver);
|
||||
|
||||
MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>");
|
||||
MODULE_DESCRIPTION("Microchip mcp3422/3/4/6/7/8 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
173
drivers/iio/adc/men_z188_adc.c
Normal file
173
drivers/iio/adc/men_z188_adc.c
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* MEN 16z188 Analog to Digial Converter
|
||||
*
|
||||
* Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
|
||||
* Author: Johannes Thumshirn <johannes.thumshirn@men.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mcb.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define Z188_ADC_MAX_CHAN 8
|
||||
#define Z188_ADC_GAIN 0x0700000
|
||||
#define Z188_MODE_VOLTAGE BIT(27)
|
||||
#define Z188_CFG_AUTO 0x1
|
||||
#define Z188_CTRL_REG 0x40
|
||||
|
||||
#define ADC_DATA(x) (((x) >> 2) & 0x7ffffc)
|
||||
#define ADC_OVR(x) ((x) & 0x1)
|
||||
|
||||
struct z188_adc {
|
||||
struct resource *mem;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
#define Z188_ADC_CHANNEL(idx) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec z188_adc_iio_channels[] = {
|
||||
Z188_ADC_CHANNEL(0),
|
||||
Z188_ADC_CHANNEL(1),
|
||||
Z188_ADC_CHANNEL(2),
|
||||
Z188_ADC_CHANNEL(3),
|
||||
Z188_ADC_CHANNEL(4),
|
||||
Z188_ADC_CHANNEL(5),
|
||||
Z188_ADC_CHANNEL(6),
|
||||
Z188_ADC_CHANNEL(7),
|
||||
};
|
||||
|
||||
static int z188_iio_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long info)
|
||||
{
|
||||
struct z188_adc *adc = iio_priv(iio_dev);
|
||||
int ret;
|
||||
u16 tmp;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
tmp = readw(adc->base + chan->channel * 4);
|
||||
|
||||
if (ADC_OVR(tmp)) {
|
||||
dev_info(&iio_dev->dev,
|
||||
"Oversampling error on ADC channel %d\n",
|
||||
chan->channel);
|
||||
return -EIO;
|
||||
}
|
||||
*val = ADC_DATA(tmp);
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct iio_info z188_adc_info = {
|
||||
.read_raw = &z188_iio_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void men_z188_config_channels(void __iomem *addr)
|
||||
{
|
||||
int i;
|
||||
u32 cfg;
|
||||
u32 ctl;
|
||||
|
||||
ctl = readl(addr + Z188_CTRL_REG);
|
||||
ctl |= Z188_CFG_AUTO;
|
||||
writel(ctl, addr + Z188_CTRL_REG);
|
||||
|
||||
for (i = 0; i < Z188_ADC_MAX_CHAN; i++) {
|
||||
cfg = readl(addr + i);
|
||||
cfg &= ~Z188_ADC_GAIN;
|
||||
cfg |= Z188_MODE_VOLTAGE;
|
||||
writel(cfg, addr + i);
|
||||
}
|
||||
}
|
||||
|
||||
static int men_z188_probe(struct mcb_device *dev,
|
||||
const struct mcb_device_id *id)
|
||||
{
|
||||
struct z188_adc *adc;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&dev->dev, sizeof(struct z188_adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
indio_dev->name = "z188-adc";
|
||||
indio_dev->dev.parent = &dev->dev;
|
||||
indio_dev->info = &z188_adc_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = z188_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(z188_adc_iio_channels);
|
||||
|
||||
mem = mcb_request_mem(dev, "z188-adc");
|
||||
if (IS_ERR(mem))
|
||||
return PTR_ERR(mem);
|
||||
|
||||
adc->base = ioremap(mem->start, resource_size(mem));
|
||||
if (adc->base == NULL)
|
||||
goto err;
|
||||
|
||||
men_z188_config_channels(adc->base);
|
||||
|
||||
adc->mem = mem;
|
||||
mcb_set_drvdata(dev, indio_dev);
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
|
||||
err:
|
||||
mcb_release_mem(mem);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static void men_z188_remove(struct mcb_device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = mcb_get_drvdata(dev);
|
||||
struct z188_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iounmap(adc->base);
|
||||
mcb_release_mem(adc->mem);
|
||||
}
|
||||
|
||||
static const struct mcb_device_id men_z188_ids[] = {
|
||||
{ .device = 0xbc },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(mcb, men_z188_ids);
|
||||
|
||||
static struct mcb_driver men_z188_driver = {
|
||||
.driver = {
|
||||
.name = "z188-adc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = men_z188_probe,
|
||||
.remove = men_z188_remove,
|
||||
.id_table = men_z188_ids,
|
||||
};
|
||||
module_mcb_driver(men_z188_driver);
|
||||
|
||||
MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core");
|
||||
MODULE_ALIAS("mcb:16z188");
|
||||
582
drivers/iio/adc/nau7802.c
Normal file
582
drivers/iio/adc/nau7802.c
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
/*
|
||||
* Driver for the Nuvoton NAU7802 ADC
|
||||
*
|
||||
* Copyright 2013 Free Electrons
|
||||
*
|
||||
* Licensed under the GPLv2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define NAU7802_REG_PUCTRL 0x00
|
||||
#define NAU7802_PUCTRL_RR(x) (x << 0)
|
||||
#define NAU7802_PUCTRL_RR_BIT NAU7802_PUCTRL_RR(1)
|
||||
#define NAU7802_PUCTRL_PUD(x) (x << 1)
|
||||
#define NAU7802_PUCTRL_PUD_BIT NAU7802_PUCTRL_PUD(1)
|
||||
#define NAU7802_PUCTRL_PUA(x) (x << 2)
|
||||
#define NAU7802_PUCTRL_PUA_BIT NAU7802_PUCTRL_PUA(1)
|
||||
#define NAU7802_PUCTRL_PUR(x) (x << 3)
|
||||
#define NAU7802_PUCTRL_PUR_BIT NAU7802_PUCTRL_PUR(1)
|
||||
#define NAU7802_PUCTRL_CS(x) (x << 4)
|
||||
#define NAU7802_PUCTRL_CS_BIT NAU7802_PUCTRL_CS(1)
|
||||
#define NAU7802_PUCTRL_CR(x) (x << 5)
|
||||
#define NAU7802_PUCTRL_CR_BIT NAU7802_PUCTRL_CR(1)
|
||||
#define NAU7802_PUCTRL_AVDDS(x) (x << 7)
|
||||
#define NAU7802_PUCTRL_AVDDS_BIT NAU7802_PUCTRL_AVDDS(1)
|
||||
#define NAU7802_REG_CTRL1 0x01
|
||||
#define NAU7802_CTRL1_VLDO(x) (x << 3)
|
||||
#define NAU7802_CTRL1_GAINS(x) (x)
|
||||
#define NAU7802_CTRL1_GAINS_BITS 0x07
|
||||
#define NAU7802_REG_CTRL2 0x02
|
||||
#define NAU7802_CTRL2_CHS(x) (x << 7)
|
||||
#define NAU7802_CTRL2_CRS(x) (x << 4)
|
||||
#define NAU7802_SAMP_FREQ_320 0x07
|
||||
#define NAU7802_CTRL2_CHS_BIT NAU7802_CTRL2_CHS(1)
|
||||
#define NAU7802_REG_ADC_B2 0x12
|
||||
#define NAU7802_REG_ADC_B1 0x13
|
||||
#define NAU7802_REG_ADC_B0 0x14
|
||||
#define NAU7802_REG_ADC_CTRL 0x15
|
||||
|
||||
#define NAU7802_MIN_CONVERSIONS 6
|
||||
|
||||
struct nau7802_state {
|
||||
struct i2c_client *client;
|
||||
s32 last_value;
|
||||
struct mutex lock;
|
||||
struct mutex data_lock;
|
||||
u32 vref_mv;
|
||||
u32 conversion_count;
|
||||
u32 min_conversions;
|
||||
u8 sample_rate;
|
||||
u32 scale_avail[8];
|
||||
struct completion value_ok;
|
||||
};
|
||||
|
||||
#define NAU7802_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (chan), \
|
||||
.scan_index = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec nau7802_chan_array[] = {
|
||||
NAU7802_CHANNEL(0),
|
||||
NAU7802_CHANNEL(1),
|
||||
};
|
||||
|
||||
static const u16 nau7802_sample_freq_avail[] = {10, 20, 40, 80,
|
||||
10, 10, 10, 320};
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 40 80 320");
|
||||
|
||||
static struct attribute *nau7802_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group nau7802_attribute_group = {
|
||||
.attrs = nau7802_attributes,
|
||||
};
|
||||
|
||||
static int nau7802_set_gain(struct nau7802_state *st, int gain)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
st->conversion_count = 0;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1);
|
||||
if (ret < 0)
|
||||
goto nau7802_sysfs_set_gain_out;
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1,
|
||||
(ret & (~NAU7802_CTRL1_GAINS_BITS)) |
|
||||
gain);
|
||||
|
||||
nau7802_sysfs_set_gain_out:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nau7802_read_conversion(struct nau7802_state *st)
|
||||
{
|
||||
int data;
|
||||
|
||||
mutex_lock(&st->data_lock);
|
||||
data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B2);
|
||||
if (data < 0)
|
||||
goto nau7802_read_conversion_out;
|
||||
st->last_value = data << 16;
|
||||
|
||||
data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B1);
|
||||
if (data < 0)
|
||||
goto nau7802_read_conversion_out;
|
||||
st->last_value |= data << 8;
|
||||
|
||||
data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B0);
|
||||
if (data < 0)
|
||||
goto nau7802_read_conversion_out;
|
||||
st->last_value |= data;
|
||||
|
||||
st->last_value = sign_extend32(st->last_value, 23);
|
||||
|
||||
nau7802_read_conversion_out:
|
||||
mutex_unlock(&st->data_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Conversions are synchronised on the rising edge of NAU7802_PUCTRL_CS_BIT
|
||||
*/
|
||||
static int nau7802_sync(struct nau7802_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL,
|
||||
ret | NAU7802_PUCTRL_CS_BIT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t nau7802_eoc_trigger(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
int status;
|
||||
|
||||
status = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
|
||||
if (status < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (!(status & NAU7802_PUCTRL_CR_BIT))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (nau7802_read_conversion(st) < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/*
|
||||
* Because there is actually only one ADC for both channels, we have to
|
||||
* wait for enough conversions to happen before getting a significant
|
||||
* value when changing channels and the values are far apart.
|
||||
*/
|
||||
if (st->conversion_count < NAU7802_MIN_CONVERSIONS)
|
||||
st->conversion_count++;
|
||||
if (st->conversion_count >= NAU7802_MIN_CONVERSIONS)
|
||||
complete_all(&st->value_ok);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int nau7802_read_irq(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val)
|
||||
{
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
reinit_completion(&st->value_ok);
|
||||
enable_irq(st->client->irq);
|
||||
|
||||
nau7802_sync(st);
|
||||
|
||||
/* read registers to ensure we flush everything */
|
||||
ret = nau7802_read_conversion(st);
|
||||
if (ret < 0)
|
||||
goto read_chan_info_failure;
|
||||
|
||||
/* Wait for a conversion to finish */
|
||||
ret = wait_for_completion_interruptible_timeout(&st->value_ok,
|
||||
msecs_to_jiffies(1000));
|
||||
if (ret == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
if (ret < 0)
|
||||
goto read_chan_info_failure;
|
||||
|
||||
disable_irq(st->client->irq);
|
||||
|
||||
*val = st->last_value;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
read_chan_info_failure:
|
||||
disable_irq(st->client->irq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nau7802_read_poll(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val)
|
||||
{
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
nau7802_sync(st);
|
||||
|
||||
/* read registers to ensure we flush everything */
|
||||
ret = nau7802_read_conversion(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Because there is actually only one ADC for both channels, we have to
|
||||
* wait for enough conversions to happen before getting a significant
|
||||
* value when changing channels and the values are far appart.
|
||||
*/
|
||||
do {
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
while (!(ret & NAU7802_PUCTRL_CR_BIT)) {
|
||||
if (st->sample_rate != NAU7802_SAMP_FREQ_320)
|
||||
msleep(20);
|
||||
else
|
||||
mdelay(4);
|
||||
ret = i2c_smbus_read_byte_data(st->client,
|
||||
NAU7802_REG_PUCTRL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nau7802_read_conversion(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (st->conversion_count < NAU7802_MIN_CONVERSIONS)
|
||||
st->conversion_count++;
|
||||
} while (st->conversion_count < NAU7802_MIN_CONVERSIONS);
|
||||
|
||||
*val = st->last_value;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int nau7802_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&st->lock);
|
||||
/*
|
||||
* Select the channel to use
|
||||
* - Channel 1 is value 0 in the CHS register
|
||||
* - Channel 2 is value 1 in the CHS register
|
||||
*/
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (((ret & NAU7802_CTRL2_CHS_BIT) && !chan->channel) ||
|
||||
(!(ret & NAU7802_CTRL2_CHS_BIT) &&
|
||||
chan->channel)) {
|
||||
st->conversion_count = 0;
|
||||
ret = i2c_smbus_write_byte_data(st->client,
|
||||
NAU7802_REG_CTRL2,
|
||||
NAU7802_CTRL2_CHS(chan->channel) |
|
||||
NAU7802_CTRL2_CRS(st->sample_rate));
|
||||
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (st->client->irq)
|
||||
ret = nau7802_read_irq(indio_dev, chan, val);
|
||||
else
|
||||
ret = nau7802_read_poll(indio_dev, chan, val);
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* We have 24 bits of signed data, that means 23 bits of data
|
||||
* plus the sign bit
|
||||
*/
|
||||
*val = st->vref_mv;
|
||||
*val2 = 23 + (ret & NAU7802_CTRL1_GAINS_BITS);
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = nau7802_sample_freq_avail[st->sample_rate];
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int nau7802_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
|
||||
if (val2 == st->scale_avail[i])
|
||||
return nau7802_set_gain(st, i);
|
||||
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
for (i = 0; i < ARRAY_SIZE(nau7802_sample_freq_avail); i++)
|
||||
if (val == nau7802_sample_freq_avail[i]) {
|
||||
mutex_lock(&st->lock);
|
||||
st->sample_rate = i;
|
||||
st->conversion_count = 0;
|
||||
ret = i2c_smbus_write_byte_data(st->client,
|
||||
NAU7802_REG_CTRL2,
|
||||
NAU7802_CTRL2_CRS(st->sample_rate));
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int nau7802_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 nau7802_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &nau7802_read_raw,
|
||||
.write_raw = &nau7802_write_raw,
|
||||
.write_raw_get_fmt = nau7802_write_raw_get_fmt,
|
||||
.attrs = &nau7802_attribute_group,
|
||||
};
|
||||
|
||||
static int nau7802_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct nau7802_state *st;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
int i, ret;
|
||||
u8 data;
|
||||
u32 tmp = 0;
|
||||
|
||||
if (!client->dev.of_node) {
|
||||
dev_err(&client->dev, "No device tree node available.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &nau7802_info;
|
||||
|
||||
st->client = client;
|
||||
|
||||
/* Reset the device */
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL,
|
||||
NAU7802_PUCTRL_RR_BIT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enter normal operation mode */
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL,
|
||||
NAU7802_PUCTRL_PUD_BIT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* After about 200 usecs, the device should be ready and then
|
||||
* the Power Up bit will be set to 1. If not, wait for it.
|
||||
*/
|
||||
udelay(210);
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!(ret & NAU7802_PUCTRL_PUR_BIT))
|
||||
return ret;
|
||||
|
||||
of_property_read_u32(np, "nuvoton,vldo", &tmp);
|
||||
st->vref_mv = tmp;
|
||||
|
||||
data = NAU7802_PUCTRL_PUD_BIT | NAU7802_PUCTRL_PUA_BIT |
|
||||
NAU7802_PUCTRL_CS_BIT;
|
||||
if (tmp >= 2400)
|
||||
data |= NAU7802_PUCTRL_AVDDS_BIT;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_ADC_CTRL, 0x30);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (tmp >= 2400) {
|
||||
data = NAU7802_CTRL1_VLDO((4500 - tmp) / 300);
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1,
|
||||
data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Populate available ADC input ranges */
|
||||
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
|
||||
st->scale_avail[i] = (((u64)st->vref_mv) * 1000000000ULL)
|
||||
>> (23 + i);
|
||||
|
||||
init_completion(&st->value_ok);
|
||||
|
||||
/*
|
||||
* The ADC fires continuously and we can't do anything about
|
||||
* it. So we need to have the IRQ disabled by default, and we
|
||||
* will enable them back when we will need them..
|
||||
*/
|
||||
if (client->irq) {
|
||||
ret = request_threaded_irq(client->irq,
|
||||
NULL,
|
||||
nau7802_eoc_trigger,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
client->dev.driver->name,
|
||||
indio_dev);
|
||||
if (ret) {
|
||||
/*
|
||||
* What may happen here is that our IRQ controller is
|
||||
* not able to get level interrupt but this is required
|
||||
* by this ADC as when going over 40 sample per second,
|
||||
* the interrupt line may stay high between conversions.
|
||||
* So, we continue no matter what but we switch to
|
||||
* polling mode.
|
||||
*/
|
||||
dev_info(&client->dev,
|
||||
"Failed to allocate IRQ, using polling mode\n");
|
||||
client->irq = 0;
|
||||
} else
|
||||
disable_irq(client->irq);
|
||||
}
|
||||
|
||||
if (!client->irq) {
|
||||
/*
|
||||
* We are polling, use the fastest sample rate by
|
||||
* default
|
||||
*/
|
||||
st->sample_rate = NAU7802_SAMP_FREQ_320;
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL2,
|
||||
NAU7802_CTRL2_CRS(st->sample_rate));
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
}
|
||||
|
||||
/* Setup the ADC channels available on the board */
|
||||
indio_dev->num_channels = ARRAY_SIZE(nau7802_chan_array);
|
||||
indio_dev->channels = nau7802_chan_array;
|
||||
|
||||
mutex_init(&st->lock);
|
||||
mutex_init(&st->data_lock);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Couldn't register the device.\n");
|
||||
goto error_device_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_device_register:
|
||||
mutex_destroy(&st->lock);
|
||||
mutex_destroy(&st->data_lock);
|
||||
error_free_irq:
|
||||
if (client->irq)
|
||||
free_irq(client->irq, indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nau7802_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
mutex_destroy(&st->lock);
|
||||
mutex_destroy(&st->data_lock);
|
||||
if (client->irq)
|
||||
free_irq(client->irq, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id nau7802_i2c_id[] = {
|
||||
{ "nau7802", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id);
|
||||
|
||||
static const struct of_device_id nau7802_dt_ids[] = {
|
||||
{ .compatible = "nuvoton,nau7802" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, nau7802_dt_ids);
|
||||
|
||||
static struct i2c_driver nau7802_driver = {
|
||||
.probe = nau7802_probe,
|
||||
.remove = nau7802_remove,
|
||||
.id_table = nau7802_i2c_id,
|
||||
.driver = {
|
||||
.name = "nau7802",
|
||||
.of_match_table = nau7802_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(nau7802_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Nuvoton NAU7802 ADC Driver");
|
||||
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
|
||||
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
|
||||
316
drivers/iio/adc/rockchip_saradc.c
Normal file
316
drivers/iio/adc/rockchip_saradc.c
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* Rockchip Successive Approximation Register (SAR) A/D Converter
|
||||
* Copyright (C) 2014 ROCKCHIP, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define SARADC_DATA 0x00
|
||||
#define SARADC_DATA_MASK 0x3ff
|
||||
|
||||
#define SARADC_STAS 0x04
|
||||
#define SARADC_STAS_BUSY BIT(0)
|
||||
|
||||
#define SARADC_CTRL 0x08
|
||||
#define SARADC_CTRL_IRQ_STATUS BIT(6)
|
||||
#define SARADC_CTRL_IRQ_ENABLE BIT(5)
|
||||
#define SARADC_CTRL_POWER_CTRL BIT(3)
|
||||
#define SARADC_CTRL_CHN_MASK 0x7
|
||||
|
||||
#define SARADC_DLY_PU_SOC 0x0c
|
||||
#define SARADC_DLY_PU_SOC_MASK 0x3f
|
||||
|
||||
#define SARADC_BITS 10
|
||||
#define SARADC_TIMEOUT msecs_to_jiffies(100)
|
||||
|
||||
struct rockchip_saradc {
|
||||
void __iomem *regs;
|
||||
struct clk *pclk;
|
||||
struct clk *clk;
|
||||
struct completion completion;
|
||||
struct regulator *vref;
|
||||
u16 last_val;
|
||||
};
|
||||
|
||||
static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct rockchip_saradc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
reinit_completion(&info->completion);
|
||||
|
||||
/* 8 clock periods as delay between power up and start cmd */
|
||||
writel_relaxed(8, info->regs + SARADC_DLY_PU_SOC);
|
||||
|
||||
/* Select the channel to be used and trigger conversion */
|
||||
writel(SARADC_CTRL_POWER_CTRL
|
||||
| (chan->channel & SARADC_CTRL_CHN_MASK)
|
||||
| SARADC_CTRL_IRQ_ENABLE,
|
||||
info->regs + SARADC_CTRL);
|
||||
|
||||
if (!wait_for_completion_timeout(&info->completion,
|
||||
SARADC_TIMEOUT)) {
|
||||
writel_relaxed(0, info->regs + SARADC_CTRL);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
*val = info->last_val;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(info->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "failed to get voltage\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = SARADC_BITS;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct rockchip_saradc *info = (struct rockchip_saradc *)dev_id;
|
||||
|
||||
/* Read value */
|
||||
info->last_val = readl_relaxed(info->regs + SARADC_DATA);
|
||||
info->last_val &= SARADC_DATA_MASK;
|
||||
|
||||
/* Clear irq & power down adc */
|
||||
writel_relaxed(0, info->regs + SARADC_CTRL);
|
||||
|
||||
complete(&info->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_info rockchip_saradc_iio_info = {
|
||||
.read_raw = rockchip_saradc_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define ADC_CHANNEL(_index, _id) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = _id, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec rockchip_saradc_iio_channels[] = {
|
||||
ADC_CHANNEL(0, "adc0"),
|
||||
ADC_CHANNEL(1, "adc1"),
|
||||
ADC_CHANNEL(2, "adc2"),
|
||||
};
|
||||
|
||||
static int rockchip_saradc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rockchip_saradc *info = NULL;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct iio_dev *indio_dev = NULL;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
info = iio_priv(indio_dev);
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
|
||||
0, dev_name(&pdev->dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->pclk = devm_clk_get(&pdev->dev, "apb_pclk");
|
||||
if (IS_ERR(info->pclk)) {
|
||||
dev_err(&pdev->dev, "failed to get pclk\n");
|
||||
return PTR_ERR(info->pclk);
|
||||
}
|
||||
|
||||
info->clk = devm_clk_get(&pdev->dev, "saradc");
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get adc clock\n");
|
||||
return PTR_ERR(info->clk);
|
||||
}
|
||||
|
||||
info->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(info->vref)) {
|
||||
dev_err(&pdev->dev, "failed to get regulator, %ld\n",
|
||||
PTR_ERR(info->vref));
|
||||
return PTR_ERR(info->vref);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use a default of 1MHz for the converter clock.
|
||||
* This may become user-configurable in the future.
|
||||
*/
|
||||
ret = clk_set_rate(info->clk, 1000000);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to set adc clk rate, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable vref regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(info->pclk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable pclk\n");
|
||||
goto err_reg_voltage;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable converter clock\n");
|
||||
goto err_pclk;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &rockchip_saradc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
indio_dev->channels = rockchip_saradc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(info->clk);
|
||||
err_pclk:
|
||||
clk_disable_unprepare(info->pclk);
|
||||
err_reg_voltage:
|
||||
regulator_disable(info->vref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_saradc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct rockchip_saradc *info = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
clk_disable_unprepare(info->clk);
|
||||
clk_disable_unprepare(info->pclk);
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int rockchip_saradc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct rockchip_saradc *info = iio_priv(indio_dev);
|
||||
|
||||
clk_disable_unprepare(info->clk);
|
||||
clk_disable_unprepare(info->pclk);
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_saradc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct rockchip_saradc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(info->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops,
|
||||
rockchip_saradc_suspend, rockchip_saradc_resume);
|
||||
|
||||
static const struct of_device_id rockchip_saradc_match[] = {
|
||||
{ .compatible = "rockchip,saradc" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
|
||||
|
||||
static struct platform_driver rockchip_saradc_driver = {
|
||||
.probe = rockchip_saradc_probe,
|
||||
.remove = rockchip_saradc_remove,
|
||||
.driver = {
|
||||
.name = "rockchip-saradc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = rockchip_saradc_match,
|
||||
.pm = &rockchip_saradc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rockchip_saradc_driver);
|
||||
154
drivers/iio/adc/ti-adc081c.c
Normal file
154
drivers/iio/adc/ti-adc081c.c
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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/of.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_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
};
|
||||
|
||||
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 = devm_iio_device_alloc(&client->dev, sizeof(*adc));
|
||||
if (!iio)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(iio);
|
||||
adc->i2c = client;
|
||||
|
||||
adc->ref = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(adc->ref))
|
||||
return PTR_ERR(adc->ref);
|
||||
|
||||
err = regulator_enable(adc->ref);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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");
|
||||
179
drivers/iio/adc/ti-adc128s052.c
Normal file
179
drivers/iio/adc/ti-adc128s052.c
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com>
|
||||
*
|
||||
* Driver for Texas Instruments' ADC128S052 ADC chip.
|
||||
* Datasheet can be found here:
|
||||
* http://www.ti.com/lit/ds/symlink/adc128s052.pdf
|
||||
*
|
||||
* 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/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
struct adc128 {
|
||||
struct spi_device *spi;
|
||||
|
||||
struct regulator *reg;
|
||||
struct mutex lock;
|
||||
|
||||
u8 buffer[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int adc128_adc_conversion(struct adc128 *adc, u8 channel)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
adc->buffer[0] = channel << 3;
|
||||
adc->buffer[1] = 0;
|
||||
|
||||
ret = spi_write(adc->spi, &adc->buffer, 2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&adc->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spi_read(adc->spi, &adc->buffer, 2);
|
||||
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ((adc->buffer[0] << 8 | adc->buffer[1]) & 0xFFF);
|
||||
}
|
||||
|
||||
static int adc128_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct adc128 *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
||||
ret = adc128_adc_conversion(adc, channel->channel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
|
||||
ret = regulator_get_voltage(adc->reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = 12;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define ADC128_VOLTAGE_CHANNEL(num) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (num), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adc128_channels[] = {
|
||||
ADC128_VOLTAGE_CHANNEL(0),
|
||||
ADC128_VOLTAGE_CHANNEL(1),
|
||||
ADC128_VOLTAGE_CHANNEL(2),
|
||||
ADC128_VOLTAGE_CHANNEL(3),
|
||||
ADC128_VOLTAGE_CHANNEL(4),
|
||||
ADC128_VOLTAGE_CHANNEL(5),
|
||||
ADC128_VOLTAGE_CHANNEL(6),
|
||||
ADC128_VOLTAGE_CHANNEL(7),
|
||||
};
|
||||
|
||||
static const struct iio_info adc128_info = {
|
||||
.read_raw = adc128_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adc128_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adc128 *adc;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->spi = spi;
|
||||
|
||||
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->info = &adc128_info;
|
||||
|
||||
indio_dev->channels = adc128_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adc128_channels);
|
||||
|
||||
adc->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(adc->reg))
|
||||
return PTR_ERR(adc->reg);
|
||||
|
||||
ret = regulator_enable(adc->reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc128_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adc128 *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adc128_id[] = {
|
||||
{ "adc128s052", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adc128_id);
|
||||
|
||||
static struct spi_driver adc128_driver = {
|
||||
.driver = {
|
||||
.name = "adc128s052",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adc128_probe,
|
||||
.remove = adc128_remove,
|
||||
.id_table = adc128_id,
|
||||
};
|
||||
module_spi_driver(adc128_driver);
|
||||
|
||||
MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments ADC128S052");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
558
drivers/iio/adc/ti_am335x_adc.c
Normal file
558
drivers/iio/adc/ti_am335x_adc.c
Normal file
|
|
@ -0,0 +1,558 @@
|
|||
/*
|
||||
* TI ADC MFD driver
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
#include <linux/mfd/ti_am335x_tscadc.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
struct tiadc_device {
|
||||
struct ti_tscadc_dev *mfd_tscadc;
|
||||
int channels;
|
||||
u8 channel_line[8];
|
||||
u8 channel_step[8];
|
||||
int buffer_en_ch_steps;
|
||||
u16 data[8];
|
||||
};
|
||||
|
||||
static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
|
||||
{
|
||||
return readl(adc->mfd_tscadc->tscadc_base + reg);
|
||||
}
|
||||
|
||||
static void tiadc_writel(struct tiadc_device *adc, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
writel(val, adc->mfd_tscadc->tscadc_base + reg);
|
||||
}
|
||||
|
||||
static u32 get_adc_step_mask(struct tiadc_device *adc_dev)
|
||||
{
|
||||
u32 step_en;
|
||||
|
||||
step_en = ((1 << adc_dev->channels) - 1);
|
||||
step_en <<= TOTAL_STEPS - adc_dev->channels + 1;
|
||||
return step_en;
|
||||
}
|
||||
|
||||
static u32 get_adc_chan_step_mask(struct tiadc_device *adc_dev,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) {
|
||||
if (chan->channel == adc_dev->channel_line[i]) {
|
||||
u32 step;
|
||||
|
||||
step = adc_dev->channel_step[i];
|
||||
/* +1 for the charger */
|
||||
return 1 << (step + 1);
|
||||
}
|
||||
}
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan)
|
||||
{
|
||||
return 1 << adc_dev->channel_step[chan];
|
||||
}
|
||||
|
||||
static void tiadc_step_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
unsigned int stepconfig;
|
||||
int i, steps;
|
||||
|
||||
/*
|
||||
* There are 16 configurable steps and 8 analog input
|
||||
* lines available which are shared between Touchscreen and ADC.
|
||||
*
|
||||
* Steps backwards i.e. from 16 towards 0 are used by ADC
|
||||
* depending on number of input lines needed.
|
||||
* Channel would represent which analog input
|
||||
* needs to be given to ADC to digitalize data.
|
||||
*/
|
||||
|
||||
steps = TOTAL_STEPS - adc_dev->channels;
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
|
||||
| STEPCONFIG_MODE_SWCNT;
|
||||
else
|
||||
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
|
||||
|
||||
for (i = 0; i < adc_dev->channels; i++) {
|
||||
int chan;
|
||||
|
||||
chan = adc_dev->channel_line[i];
|
||||
tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
|
||||
stepconfig | STEPCONFIG_INP(chan));
|
||||
tiadc_writel(adc_dev, REG_STEPDELAY(steps),
|
||||
STEPCONFIG_OPENDLY);
|
||||
adc_dev->channel_step[i] = steps;
|
||||
steps++;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t tiadc_irq_h(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
unsigned int status, config;
|
||||
status = tiadc_readl(adc_dev, REG_IRQSTATUS);
|
||||
|
||||
/*
|
||||
* ADC and touchscreen share the IRQ line.
|
||||
* FIFO0 interrupts are used by TSC. Handle FIFO1 IRQs here only
|
||||
*/
|
||||
if (status & IRQENB_FIFO1OVRRUN) {
|
||||
/* FIFO Overrun. Clear flag. Disable/Enable ADC to recover */
|
||||
config = tiadc_readl(adc_dev, REG_CTRL);
|
||||
config &= ~(CNTRLREG_TSCSSENB);
|
||||
tiadc_writel(adc_dev, REG_CTRL, config);
|
||||
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN
|
||||
| IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES);
|
||||
tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB));
|
||||
return IRQ_HANDLED;
|
||||
} else if (status & IRQENB_FIFO1THRES) {
|
||||
/* Disable irq and wake worker thread */
|
||||
tiadc_writel(adc_dev, REG_IRQCLR, IRQENB_FIFO1THRES);
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t tiadc_worker_h(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int i, k, fifo1count, read;
|
||||
u16 *data = adc_dev->data;
|
||||
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
for (k = 0; k < fifo1count; k = k + i) {
|
||||
for (i = 0; i < (indio_dev->scan_bytes)/2; i++) {
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
data[i] = read & FIFOREAD_DATA_MASK;
|
||||
}
|
||||
iio_push_to_buffers(indio_dev, (u8 *) data);
|
||||
}
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES);
|
||||
tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int i, fifo1count, read;
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
|
||||
IRQENB_FIFO1OVRRUN |
|
||||
IRQENB_FIFO1UNDRFLW));
|
||||
|
||||
/* Flush FIFO. Needed in corner cases in simultaneous tsc/adc use */
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
for (i = 0; i < fifo1count; i++)
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct iio_buffer *buffer = indio_dev->buffer;
|
||||
unsigned int enb = 0;
|
||||
u8 bit;
|
||||
|
||||
tiadc_step_config(indio_dev);
|
||||
for_each_set_bit(bit, buffer->scan_mask, adc_dev->channels)
|
||||
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
|
||||
adc_dev->buffer_en_ch_steps = enb;
|
||||
|
||||
am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
|
||||
| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
|
||||
tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
|
||||
| IRQENB_FIFO1OVRRUN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int fifo1count, i, read;
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
|
||||
IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
|
||||
am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
|
||||
adc_dev->buffer_en_ch_steps = 0;
|
||||
|
||||
/* Flush FIFO of leftover data in the time it takes to disable adc */
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
for (i = 0; i < fifo1count; i++)
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
tiadc_step_config(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops tiadc_buffer_setup_ops = {
|
||||
.preenable = &tiadc_buffer_preenable,
|
||||
.postenable = &tiadc_buffer_postenable,
|
||||
.predisable = &tiadc_buffer_predisable,
|
||||
.postdisable = &tiadc_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
|
||||
irqreturn_t (*pollfunc_bh)(int irq, void *p),
|
||||
irqreturn_t (*pollfunc_th)(int irq, void *p),
|
||||
int irq,
|
||||
unsigned long flags,
|
||||
const struct iio_buffer_setup_ops *setup_ops)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
buffer = iio_kfifo_allocate(indio_dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_attach_buffer(indio_dev, buffer);
|
||||
|
||||
ret = request_threaded_irq(irq, pollfunc_th, pollfunc_bh,
|
||||
flags, indio_dev->name, indio_dev);
|
||||
if (ret)
|
||||
goto error_kfifo_free;
|
||||
|
||||
indio_dev->setup_ops = setup_ops;
|
||||
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
|
||||
|
||||
ret = iio_buffer_register(indio_dev,
|
||||
indio_dev->channels,
|
||||
indio_dev->num_channels);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(irq, indio_dev);
|
||||
error_kfifo_free:
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tiadc_iio_buffered_hardware_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
||||
free_irq(adc_dev->mfd_tscadc->irq, indio_dev);
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
iio_buffer_unregister(indio_dev);
|
||||
}
|
||||
|
||||
|
||||
static const char * const chan_name_ain[] = {
|
||||
"AIN0",
|
||||
"AIN1",
|
||||
"AIN2",
|
||||
"AIN3",
|
||||
"AIN4",
|
||||
"AIN5",
|
||||
"AIN6",
|
||||
"AIN7",
|
||||
};
|
||||
|
||||
static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *chan_array;
|
||||
struct iio_chan_spec *chan;
|
||||
int i;
|
||||
|
||||
indio_dev->num_channels = channels;
|
||||
chan_array = kcalloc(channels,
|
||||
sizeof(struct iio_chan_spec), GFP_KERNEL);
|
||||
if (chan_array == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
chan = chan_array;
|
||||
for (i = 0; i < channels; i++, chan++) {
|
||||
|
||||
chan->type = IIO_VOLTAGE;
|
||||
chan->indexed = 1;
|
||||
chan->channel = adc_dev->channel_line[i];
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
chan->datasheet_name = chan_name_ain[chan->channel];
|
||||
chan->scan_index = i;
|
||||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = 12;
|
||||
chan->scan_type.storagebits = 16;
|
||||
}
|
||||
|
||||
indio_dev->channels = chan_array;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tiadc_channels_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
kfree(indio_dev->channels);
|
||||
}
|
||||
|
||||
static int tiadc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int i, map_val;
|
||||
unsigned int fifo1count, read, stepid;
|
||||
bool found = false;
|
||||
u32 step_en;
|
||||
unsigned long timeout;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
step_en = get_adc_chan_step_mask(adc_dev, chan);
|
||||
if (!step_en)
|
||||
return -EINVAL;
|
||||
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
while (fifo1count--)
|
||||
tiadc_readl(adc_dev, REG_FIFO1);
|
||||
|
||||
am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en);
|
||||
|
||||
timeout = jiffies + usecs_to_jiffies
|
||||
(IDLE_TIMEOUT * adc_dev->channels);
|
||||
/* Wait for Fifo threshold interrupt */
|
||||
while (1) {
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
if (fifo1count)
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
map_val = adc_dev->channel_step[chan->scan_index];
|
||||
|
||||
/*
|
||||
* We check the complete FIFO. We programmed just one entry but in case
|
||||
* something went wrong we left empty handed (-EAGAIN previously) and
|
||||
* then the value apeared somehow in the FIFO we would have two entries.
|
||||
* Therefore we read every item and keep only the latest version of the
|
||||
* requested channel.
|
||||
*/
|
||||
for (i = 0; i < fifo1count; i++) {
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
stepid = read & FIFOREAD_CHNLID_MASK;
|
||||
stepid = stepid >> 0x10;
|
||||
|
||||
if (stepid == map_val) {
|
||||
read = read & FIFOREAD_DATA_MASK;
|
||||
found = true;
|
||||
*val = (u16) read;
|
||||
}
|
||||
}
|
||||
am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
|
||||
|
||||
if (found == false)
|
||||
return -EBUSY;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const struct iio_info tiadc_info = {
|
||||
.read_raw = &tiadc_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int tiadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct tiadc_device *adc_dev;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct property *prop;
|
||||
const __be32 *cur;
|
||||
int err;
|
||||
u32 val;
|
||||
int channels = 0;
|
||||
|
||||
if (!node) {
|
||||
dev_err(&pdev->dev, "Could not find valid DT data.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct tiadc_device));
|
||||
if (indio_dev == NULL) {
|
||||
dev_err(&pdev->dev, "failed to allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
adc_dev = iio_priv(indio_dev);
|
||||
|
||||
adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
|
||||
|
||||
of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
|
||||
adc_dev->channel_line[channels] = val;
|
||||
channels++;
|
||||
}
|
||||
adc_dev->channels = channels;
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &tiadc_info;
|
||||
|
||||
tiadc_step_config(indio_dev);
|
||||
tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
|
||||
|
||||
err = tiadc_channel_init(indio_dev, adc_dev->channels);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = tiadc_iio_buffered_hardware_setup(indio_dev,
|
||||
&tiadc_worker_h,
|
||||
&tiadc_irq_h,
|
||||
adc_dev->mfd_tscadc->irq,
|
||||
IRQF_SHARED,
|
||||
&tiadc_buffer_setup_ops);
|
||||
|
||||
if (err)
|
||||
goto err_free_channels;
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err)
|
||||
goto err_buffer_unregister;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_buffer_unregister:
|
||||
tiadc_iio_buffered_hardware_remove(indio_dev);
|
||||
err_free_channels:
|
||||
tiadc_channels_remove(indio_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tiadc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
u32 step_en;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
tiadc_iio_buffered_hardware_remove(indio_dev);
|
||||
tiadc_channels_remove(indio_dev);
|
||||
|
||||
step_en = get_adc_step_mask(adc_dev);
|
||||
am335x_tsc_se_clr(adc_dev->mfd_tscadc, step_en);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tiadc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct ti_tscadc_dev *tscadc_dev;
|
||||
unsigned int idle;
|
||||
|
||||
tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
|
||||
if (!device_may_wakeup(tscadc_dev->dev)) {
|
||||
idle = tiadc_readl(adc_dev, REG_CTRL);
|
||||
idle &= ~(CNTRLREG_TSCSSENB);
|
||||
tiadc_writel(adc_dev, REG_CTRL, (idle |
|
||||
CNTRLREG_POWERDOWN));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
unsigned int restore;
|
||||
|
||||
/* Make sure ADC is powered up */
|
||||
restore = tiadc_readl(adc_dev, REG_CTRL);
|
||||
restore &= ~(CNTRLREG_POWERDOWN);
|
||||
tiadc_writel(adc_dev, REG_CTRL, restore);
|
||||
|
||||
tiadc_step_config(indio_dev);
|
||||
am335x_tsc_se_set_cache(adc_dev->mfd_tscadc,
|
||||
adc_dev->buffer_en_ch_steps);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tiadc_pm_ops = {
|
||||
.suspend = tiadc_suspend,
|
||||
.resume = tiadc_resume,
|
||||
};
|
||||
#define TIADC_PM_OPS (&tiadc_pm_ops)
|
||||
#else
|
||||
#define TIADC_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct of_device_id ti_adc_dt_ids[] = {
|
||||
{ .compatible = "ti,am3359-adc", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
|
||||
|
||||
static struct platform_driver tiadc_driver = {
|
||||
.driver = {
|
||||
.name = "TI-am335x-adc",
|
||||
.pm = TIADC_PM_OPS,
|
||||
.of_match_table = ti_adc_dt_ids,
|
||||
},
|
||||
.probe = tiadc_probe,
|
||||
.remove = tiadc_remove,
|
||||
};
|
||||
module_platform_driver(tiadc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("TI ADC controller driver");
|
||||
MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
895
drivers/iio/adc/twl4030-madc.c
Normal file
895
drivers/iio/adc/twl4030-madc.c
Normal file
|
|
@ -0,0 +1,895 @@
|
|||
/*
|
||||
*
|
||||
* TWL4030 MADC module driver-This driver monitors the real time
|
||||
* conversion of analog signals like battery temperature,
|
||||
* battery type, battery level etc.
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* J Keerthy <j-keerthy@ti.com>
|
||||
*
|
||||
* Based on twl4030-madc.c
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
* Mikko Ylinen <mikko.k.ylinen@nokia.com>
|
||||
*
|
||||
* Amit Kucheria <amit.kucheria@canonical.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c/twl.h>
|
||||
#include <linux/i2c/twl4030-madc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
/**
|
||||
* struct twl4030_madc_data - a container for madc info
|
||||
* @dev: Pointer to device structure for madc
|
||||
* @lock: Mutex protecting this data structure
|
||||
* @requests: Array of request struct corresponding to SW1, SW2 and RT
|
||||
* @use_second_irq: IRQ selection (main or co-processor)
|
||||
* @imr: Interrupt mask register of MADC
|
||||
* @isr: Interrupt status register of MADC
|
||||
*/
|
||||
struct twl4030_madc_data {
|
||||
struct device *dev;
|
||||
struct mutex lock; /* mutex protecting this data structure */
|
||||
struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
|
||||
bool use_second_irq;
|
||||
u8 imr;
|
||||
u8 isr;
|
||||
};
|
||||
|
||||
static int twl4030_madc_read(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct twl4030_madc_data *madc = iio_priv(iio_dev);
|
||||
struct twl4030_madc_request req;
|
||||
int ret;
|
||||
|
||||
req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
|
||||
|
||||
req.channels = BIT(chan->channel);
|
||||
req.active = false;
|
||||
req.func_cb = NULL;
|
||||
req.type = TWL4030_MADC_WAIT;
|
||||
req.raw = !(mask == IIO_CHAN_INFO_PROCESSED);
|
||||
req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW);
|
||||
|
||||
ret = twl4030_madc_conversion(&req);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = req.rbuf[chan->channel];
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const struct iio_info twl4030_madc_iio_info = {
|
||||
.read_raw = &twl4030_madc_read,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \
|
||||
.type = _type, \
|
||||
.channel = _channel, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
.datasheet_name = _name, \
|
||||
.indexed = 1, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
|
||||
TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"),
|
||||
TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"),
|
||||
TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"),
|
||||
TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"),
|
||||
TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"),
|
||||
TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"),
|
||||
TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"),
|
||||
TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"),
|
||||
TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"),
|
||||
TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"),
|
||||
TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"),
|
||||
TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"),
|
||||
TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"),
|
||||
TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"),
|
||||
TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"),
|
||||
TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"),
|
||||
};
|
||||
|
||||
static struct twl4030_madc_data *twl4030_madc;
|
||||
|
||||
struct twl4030_prescale_divider_ratios {
|
||||
s16 numerator;
|
||||
s16 denominator;
|
||||
};
|
||||
|
||||
static const struct twl4030_prescale_divider_ratios
|
||||
twl4030_divider_ratios[16] = {
|
||||
{1, 1}, /* CHANNEL 0 No Prescaler */
|
||||
{1, 1}, /* CHANNEL 1 No Prescaler */
|
||||
{6, 10}, /* CHANNEL 2 */
|
||||
{6, 10}, /* CHANNEL 3 */
|
||||
{6, 10}, /* CHANNEL 4 */
|
||||
{6, 10}, /* CHANNEL 5 */
|
||||
{6, 10}, /* CHANNEL 6 */
|
||||
{6, 10}, /* CHANNEL 7 */
|
||||
{3, 14}, /* CHANNEL 8 */
|
||||
{1, 3}, /* CHANNEL 9 */
|
||||
{1, 1}, /* CHANNEL 10 No Prescaler */
|
||||
{15, 100}, /* CHANNEL 11 */
|
||||
{1, 4}, /* CHANNEL 12 */
|
||||
{1, 1}, /* CHANNEL 13 Reserved channels */
|
||||
{1, 1}, /* CHANNEL 14 Reseved channels */
|
||||
{5, 11}, /* CHANNEL 15 */
|
||||
};
|
||||
|
||||
|
||||
/* Conversion table from -3 to 55 degrees Celcius */
|
||||
static int twl4030_therm_tbl[] = {
|
||||
30800, 29500, 28300, 27100,
|
||||
26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700,
|
||||
17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100,
|
||||
12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280,
|
||||
8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710,
|
||||
6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920,
|
||||
4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670,
|
||||
3550
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure containing the registers
|
||||
* of different conversion methods supported by MADC.
|
||||
* Hardware or RT real time conversion request initiated by external host
|
||||
* processor for RT Signal conversions.
|
||||
* External host processors can also request for non RT conversions
|
||||
* SW1 and SW2 software conversions also called asynchronous or GPC request.
|
||||
*/
|
||||
static
|
||||
const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
|
||||
[TWL4030_MADC_RT] = {
|
||||
.sel = TWL4030_MADC_RTSELECT_LSB,
|
||||
.avg = TWL4030_MADC_RTAVERAGE_LSB,
|
||||
.rbase = TWL4030_MADC_RTCH0_LSB,
|
||||
},
|
||||
[TWL4030_MADC_SW1] = {
|
||||
.sel = TWL4030_MADC_SW1SELECT_LSB,
|
||||
.avg = TWL4030_MADC_SW1AVERAGE_LSB,
|
||||
.rbase = TWL4030_MADC_GPCH0_LSB,
|
||||
.ctrl = TWL4030_MADC_CTRL_SW1,
|
||||
},
|
||||
[TWL4030_MADC_SW2] = {
|
||||
.sel = TWL4030_MADC_SW2SELECT_LSB,
|
||||
.avg = TWL4030_MADC_SW2AVERAGE_LSB,
|
||||
.rbase = TWL4030_MADC_GPCH0_LSB,
|
||||
.ctrl = TWL4030_MADC_CTRL_SW2,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* twl4030_madc_channel_raw_read() - Function to read a particular channel value
|
||||
* @madc: pointer to struct twl4030_madc_data
|
||||
* @reg: lsb of ADC Channel
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
|
||||
{
|
||||
u16 val;
|
||||
int ret;
|
||||
/*
|
||||
* For each ADC channel, we have MSB and LSB register pair. MSB address
|
||||
* is always LSB address+1. reg parameter is the address of LSB register
|
||||
*/
|
||||
ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read register 0x%X\n", reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (int)(val >> 6);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return battery temperature in degrees Celsius
|
||||
* Or < 0 on failure.
|
||||
*/
|
||||
static int twl4030battery_temperature(int raw_volt)
|
||||
{
|
||||
u8 val;
|
||||
int temp, curr, volt, res, ret;
|
||||
|
||||
volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
|
||||
/* Getting and calculating the supply current in micro amperes */
|
||||
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
|
||||
REG_BCICTL2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
|
||||
/* Getting and calculating the thermistor resistance in ohms */
|
||||
res = volt * 1000 / curr;
|
||||
/* calculating temperature */
|
||||
for (temp = 58; temp >= 0; temp--) {
|
||||
int actual = twl4030_therm_tbl[temp];
|
||||
if ((actual - res) >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return temp + 1;
|
||||
}
|
||||
|
||||
static int twl4030battery_current(int raw_volt)
|
||||
{
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
|
||||
TWL4030_BCI_BCICTL1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */
|
||||
return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1;
|
||||
else /* slope of 0.88 mV/mA */
|
||||
return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to read channel values
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @reg_base - Base address of the first channel
|
||||
* @Channels - 16 bit bitmap. If the bit is set, channel's value is read
|
||||
* @buf - The channel values are stored here. if read fails error
|
||||
* @raw - Return raw values without conversion
|
||||
* value is stored
|
||||
* Returns the number of successfully read channels.
|
||||
*/
|
||||
static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
|
||||
u8 reg_base, unsigned
|
||||
long channels, int *buf,
|
||||
bool raw)
|
||||
{
|
||||
int count = 0;
|
||||
int i;
|
||||
u8 reg;
|
||||
|
||||
for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
|
||||
reg = reg_base + (2 * i);
|
||||
buf[i] = twl4030_madc_channel_raw_read(madc, reg);
|
||||
if (buf[i] < 0) {
|
||||
dev_err(madc->dev, "Unable to read register 0x%X\n",
|
||||
reg);
|
||||
return buf[i];
|
||||
}
|
||||
if (raw) {
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
switch (i) {
|
||||
case 10:
|
||||
buf[i] = twl4030battery_current(buf[i]);
|
||||
if (buf[i] < 0) {
|
||||
dev_err(madc->dev, "err reading current\n");
|
||||
return buf[i];
|
||||
} else {
|
||||
count++;
|
||||
buf[i] = buf[i] - 750;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
buf[i] = twl4030battery_temperature(buf[i]);
|
||||
if (buf[i] < 0) {
|
||||
dev_err(madc->dev, "err reading temperature\n");
|
||||
return buf[i];
|
||||
} else {
|
||||
buf[i] -= 3;
|
||||
count++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
count++;
|
||||
/* Analog Input (V) = conv_result * step_size / R
|
||||
* conv_result = decimal value of 10-bit conversion
|
||||
* result
|
||||
* step size = 1.5 / (2 ^ 10 -1)
|
||||
* R = Prescaler ratio for input channels.
|
||||
* Result given in mV hence multiplied by 1000.
|
||||
*/
|
||||
buf[i] = (buf[i] * 3 * 1000 *
|
||||
twl4030_divider_ratios[i].denominator)
|
||||
/ (2 * 1023 *
|
||||
twl4030_divider_ratios[i].numerator);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables irq.
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @id - irq number to be enabled
|
||||
* can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
|
||||
* corresponding to RT, SW1, SW2 conversion requests.
|
||||
* If the i2c read fails it returns an error else returns 0.
|
||||
*/
|
||||
static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read imr register 0x%X\n",
|
||||
madc->imr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
val &= ~(1 << id);
|
||||
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev,
|
||||
"unable to write imr register 0x%X\n", madc->imr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disables irq.
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @id - irq number to be disabled
|
||||
* can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
|
||||
* corresponding to RT, SW1, SW2 conversion requests.
|
||||
* Returns error if i2c read/write fails.
|
||||
*/
|
||||
static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read imr register 0x%X\n",
|
||||
madc->imr);
|
||||
return ret;
|
||||
}
|
||||
val |= (1 << id);
|
||||
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev,
|
||||
"unable to write imr register 0x%X\n", madc->imr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
|
||||
{
|
||||
struct twl4030_madc_data *madc = _madc;
|
||||
const struct twl4030_madc_conversion_method *method;
|
||||
u8 isr_val, imr_val;
|
||||
int i, len, ret;
|
||||
struct twl4030_madc_request *r;
|
||||
|
||||
mutex_lock(&madc->lock);
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read isr register 0x%X\n",
|
||||
madc->isr);
|
||||
goto err_i2c;
|
||||
}
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read imr register 0x%X\n",
|
||||
madc->imr);
|
||||
goto err_i2c;
|
||||
}
|
||||
isr_val &= ~imr_val;
|
||||
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
|
||||
if (!(isr_val & (1 << i)))
|
||||
continue;
|
||||
ret = twl4030_madc_disable_irq(madc, i);
|
||||
if (ret < 0)
|
||||
dev_dbg(madc->dev, "Disable interrupt failed %d\n", i);
|
||||
madc->requests[i].result_pending = 1;
|
||||
}
|
||||
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
|
||||
r = &madc->requests[i];
|
||||
/* No pending results for this method, move to next one */
|
||||
if (!r->result_pending)
|
||||
continue;
|
||||
method = &twl4030_conversion_methods[r->method];
|
||||
/* Read results */
|
||||
len = twl4030_madc_read_channels(madc, method->rbase,
|
||||
r->channels, r->rbuf, r->raw);
|
||||
/* Return results to caller */
|
||||
if (r->func_cb != NULL) {
|
||||
r->func_cb(len, r->channels, r->rbuf);
|
||||
r->func_cb = NULL;
|
||||
}
|
||||
/* Free request */
|
||||
r->result_pending = 0;
|
||||
r->active = 0;
|
||||
}
|
||||
mutex_unlock(&madc->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
||||
err_i2c:
|
||||
/*
|
||||
* In case of error check whichever request is active
|
||||
* and service the same.
|
||||
*/
|
||||
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
|
||||
r = &madc->requests[i];
|
||||
if (r->active == 0)
|
||||
continue;
|
||||
method = &twl4030_conversion_methods[r->method];
|
||||
/* Read results */
|
||||
len = twl4030_madc_read_channels(madc, method->rbase,
|
||||
r->channels, r->rbuf, r->raw);
|
||||
/* Return results to caller */
|
||||
if (r->func_cb != NULL) {
|
||||
r->func_cb(len, r->channels, r->rbuf);
|
||||
r->func_cb = NULL;
|
||||
}
|
||||
/* Free request */
|
||||
r->result_pending = 0;
|
||||
r->active = 0;
|
||||
}
|
||||
mutex_unlock(&madc->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
|
||||
struct twl4030_madc_request *req)
|
||||
{
|
||||
struct twl4030_madc_request *p;
|
||||
int ret;
|
||||
|
||||
p = &madc->requests[req->method];
|
||||
memcpy(p, req, sizeof(*req));
|
||||
ret = twl4030_madc_enable_irq(madc, req->method);
|
||||
if (ret < 0) {
|
||||
dev_err(madc->dev, "enable irq failed!!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function which enables the madc conversion
|
||||
* by writing to the control register.
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1
|
||||
* corresponding to RT SW1 or SW2 conversion methods.
|
||||
* Returns 0 if succeeds else a negative error value
|
||||
*/
|
||||
static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
|
||||
int conv_method)
|
||||
{
|
||||
const struct twl4030_madc_conversion_method *method;
|
||||
int ret = 0;
|
||||
|
||||
if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2)
|
||||
return -ENOTSUPP;
|
||||
|
||||
method = &twl4030_conversion_methods[conv_method];
|
||||
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START,
|
||||
method->ctrl);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to write ctrl register 0x%X\n",
|
||||
method->ctrl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function that waits for conversion to be ready
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @timeout_ms - timeout value in milliseconds
|
||||
* @status_reg - ctrl register
|
||||
* returns 0 if succeeds else a negative error value
|
||||
*/
|
||||
static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
|
||||
unsigned int timeout_ms,
|
||||
u8 status_reg)
|
||||
{
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
||||
do {
|
||||
u8 reg;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, ®, status_reg);
|
||||
if (ret) {
|
||||
dev_err(madc->dev,
|
||||
"unable to read status register 0x%X\n",
|
||||
status_reg);
|
||||
return ret;
|
||||
}
|
||||
if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
|
||||
return 0;
|
||||
usleep_range(500, 2000);
|
||||
} while (!time_after(jiffies, timeout));
|
||||
dev_err(madc->dev, "conversion timeout!\n");
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* An exported function which can be called from other kernel drivers.
|
||||
* @req twl4030_madc_request structure
|
||||
* req->rbuf will be filled with read values of channels based on the
|
||||
* channel index. If a particular channel reading fails there will
|
||||
* be a negative error value in the corresponding array element.
|
||||
* returns 0 if succeeds else error value
|
||||
*/
|
||||
int twl4030_madc_conversion(struct twl4030_madc_request *req)
|
||||
{
|
||||
const struct twl4030_madc_conversion_method *method;
|
||||
int ret;
|
||||
|
||||
if (!req || !twl4030_madc)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&twl4030_madc->lock);
|
||||
if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/* Do we have a conversion request ongoing */
|
||||
if (twl4030_madc->requests[req->method].active) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
method = &twl4030_conversion_methods[req->method];
|
||||
/* Select channels to be converted */
|
||||
ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel);
|
||||
if (ret) {
|
||||
dev_err(twl4030_madc->dev,
|
||||
"unable to write sel register 0x%X\n", method->sel);
|
||||
goto out;
|
||||
}
|
||||
/* Select averaging for all channels if do_avg is set */
|
||||
if (req->do_avg) {
|
||||
ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels,
|
||||
method->avg);
|
||||
if (ret) {
|
||||
dev_err(twl4030_madc->dev,
|
||||
"unable to write avg register 0x%X\n",
|
||||
method->avg);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {
|
||||
ret = twl4030_madc_set_irq(twl4030_madc, req);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
twl4030_madc->requests[req->method].active = 1;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
/* With RT method we should not be here anymore */
|
||||
if (req->method == TWL4030_MADC_RT) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
twl4030_madc->requests[req->method].active = 1;
|
||||
/* Wait until conversion is ready (ctrl register returns EOC) */
|
||||
ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl);
|
||||
if (ret) {
|
||||
twl4030_madc->requests[req->method].active = 0;
|
||||
goto out;
|
||||
}
|
||||
ret = twl4030_madc_read_channels(twl4030_madc, method->rbase,
|
||||
req->channels, req->rbuf, req->raw);
|
||||
twl4030_madc->requests[req->method].active = 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&twl4030_madc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
|
||||
|
||||
int twl4030_get_madc_conversion(int channel_no)
|
||||
{
|
||||
struct twl4030_madc_request req;
|
||||
int temp = 0;
|
||||
int ret;
|
||||
|
||||
req.channels = (1 << channel_no);
|
||||
req.method = TWL4030_MADC_SW2;
|
||||
req.active = 0;
|
||||
req.raw = 0;
|
||||
req.func_cb = NULL;
|
||||
ret = twl4030_madc_conversion(&req);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (req.rbuf[channel_no] > 0)
|
||||
temp = req.rbuf[channel_no];
|
||||
|
||||
return temp;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
|
||||
|
||||
/**
|
||||
* twl4030_madc_set_current_generator() - setup bias current
|
||||
*
|
||||
* @madc: pointer to twl4030_madc_data struct
|
||||
* @chan: can be one of the two values:
|
||||
* TWL4030_BCI_ITHEN
|
||||
* Enables bias current for main battery type reading
|
||||
* TWL4030_BCI_TYPEN
|
||||
* Enables bias current for main battery temperature sensing
|
||||
* @on: enable or disable chan.
|
||||
*
|
||||
* Function to enable or disable bias current for
|
||||
* main battery type reading or temperature sensing
|
||||
*/
|
||||
static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
|
||||
int chan, int on)
|
||||
{
|
||||
int ret;
|
||||
int regmask;
|
||||
u8 regval;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
|
||||
®val, TWL4030_BCI_BCICTL1);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X",
|
||||
TWL4030_BCI_BCICTL1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
|
||||
if (on)
|
||||
regval |= regmask;
|
||||
else
|
||||
regval &= ~regmask;
|
||||
|
||||
ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
|
||||
regval, TWL4030_BCI_BCICTL1);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n",
|
||||
TWL4030_BCI_BCICTL1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function that sets MADC software power on bit to enable MADC
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @on - Enable or disable MADC software power on bit.
|
||||
* returns error if i2c read/write fails else 0
|
||||
*/
|
||||
static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
|
||||
{
|
||||
u8 regval;
|
||||
int ret;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
|
||||
®val, TWL4030_MADC_CTRL1);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n",
|
||||
TWL4030_MADC_CTRL1);
|
||||
return ret;
|
||||
}
|
||||
if (on)
|
||||
regval |= TWL4030_MADC_MADCON;
|
||||
else
|
||||
regval &= ~TWL4030_MADC_MADCON;
|
||||
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n",
|
||||
TWL4030_MADC_CTRL1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize MADC and request for threaded irq
|
||||
*/
|
||||
static int twl4030_madc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct twl4030_madc_data *madc;
|
||||
struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int irq, ret;
|
||||
u8 regval;
|
||||
struct iio_dev *iio_dev = NULL;
|
||||
|
||||
if (!pdata && !np) {
|
||||
dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc));
|
||||
if (!iio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
madc = iio_priv(iio_dev);
|
||||
madc->dev = &pdev->dev;
|
||||
|
||||
iio_dev->name = dev_name(&pdev->dev);
|
||||
iio_dev->dev.parent = &pdev->dev;
|
||||
iio_dev->dev.of_node = pdev->dev.of_node;
|
||||
iio_dev->info = &twl4030_madc_iio_info;
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->channels = twl4030_madc_iio_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels);
|
||||
|
||||
/*
|
||||
* Phoenix provides 2 interrupt lines. The first one is connected to
|
||||
* the OMAP. The other one can be connected to the other processor such
|
||||
* as modem. Hence two separate ISR and IMR registers.
|
||||
*/
|
||||
if (pdata)
|
||||
madc->use_second_irq = (pdata->irq_line != 1);
|
||||
else
|
||||
madc->use_second_irq = of_property_read_bool(np,
|
||||
"ti,system-uses-second-madc-irq");
|
||||
|
||||
madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 :
|
||||
TWL4030_MADC_IMR1;
|
||||
madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 :
|
||||
TWL4030_MADC_ISR1;
|
||||
|
||||
ret = twl4030_madc_set_power(madc, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = twl4030_madc_set_current_generator(madc, 0, 1);
|
||||
if (ret < 0)
|
||||
goto err_current_generator;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
|
||||
®val, TWL4030_BCI_BCICTL1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n",
|
||||
TWL4030_BCI_BCICTL1);
|
||||
goto err_i2c;
|
||||
}
|
||||
regval |= TWL4030_BCI_MESBAT;
|
||||
ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
|
||||
regval, TWL4030_BCI_BCICTL1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n",
|
||||
TWL4030_BCI_BCICTL1);
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
/* Check that MADC clock is on */
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
|
||||
TWL4030_REG_GPBR1);
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
/* If MADC clk is not on, turn it on */
|
||||
if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
|
||||
dev_info(&pdev->dev, "clk disabled, enabling\n");
|
||||
regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
|
||||
ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
|
||||
TWL4030_REG_GPBR1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
|
||||
TWL4030_REG_GPBR1);
|
||||
goto err_i2c;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, iio_dev);
|
||||
mutex_init(&madc->lock);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
twl4030_madc_threaded_irq_handler,
|
||||
IRQF_TRIGGER_RISING, "twl4030_madc", madc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not request irq\n");
|
||||
goto err_i2c;
|
||||
}
|
||||
twl4030_madc = madc;
|
||||
|
||||
ret = iio_device_register(iio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not register iio device\n");
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_i2c:
|
||||
twl4030_madc_set_current_generator(madc, 0, 0);
|
||||
err_current_generator:
|
||||
twl4030_madc_set_power(madc, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int twl4030_madc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *iio_dev = platform_get_drvdata(pdev);
|
||||
struct twl4030_madc_data *madc = iio_priv(iio_dev);
|
||||
|
||||
iio_device_unregister(iio_dev);
|
||||
|
||||
twl4030_madc_set_current_generator(madc, 0, 0);
|
||||
twl4030_madc_set_power(madc, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id twl_madc_of_match[] = {
|
||||
{ .compatible = "ti,twl4030-madc", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, twl_madc_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver twl4030_madc_driver = {
|
||||
.probe = twl4030_madc_probe,
|
||||
.remove = twl4030_madc_remove,
|
||||
.driver = {
|
||||
.name = "twl4030_madc",
|
||||
.of_match_table = of_match_ptr(twl_madc_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(twl4030_madc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("TWL4030 ADC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("J Keerthy");
|
||||
MODULE_ALIAS("platform:twl4030_madc");
|
||||
1009
drivers/iio/adc/twl6030-gpadc.c
Normal file
1009
drivers/iio/adc/twl6030-gpadc.c
Normal file
File diff suppressed because it is too large
Load diff
741
drivers/iio/adc/vf610_adc.c
Normal file
741
drivers/iio/adc/vf610_adc.c
Normal file
|
|
@ -0,0 +1,741 @@
|
|||
/*
|
||||
* Freescale Vybrid vf610 ADC driver
|
||||
*
|
||||
* Copyright 2013 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
/* This will be the driver name the kernel reports */
|
||||
#define DRIVER_NAME "vf610-adc"
|
||||
|
||||
/* Vybrid/IMX ADC registers */
|
||||
#define VF610_REG_ADC_HC0 0x00
|
||||
#define VF610_REG_ADC_HC1 0x04
|
||||
#define VF610_REG_ADC_HS 0x08
|
||||
#define VF610_REG_ADC_R0 0x0c
|
||||
#define VF610_REG_ADC_R1 0x10
|
||||
#define VF610_REG_ADC_CFG 0x14
|
||||
#define VF610_REG_ADC_GC 0x18
|
||||
#define VF610_REG_ADC_GS 0x1c
|
||||
#define VF610_REG_ADC_CV 0x20
|
||||
#define VF610_REG_ADC_OFS 0x24
|
||||
#define VF610_REG_ADC_CAL 0x28
|
||||
#define VF610_REG_ADC_PCTL 0x30
|
||||
|
||||
/* Configuration register field define */
|
||||
#define VF610_ADC_MODE_BIT8 0x00
|
||||
#define VF610_ADC_MODE_BIT10 0x04
|
||||
#define VF610_ADC_MODE_BIT12 0x08
|
||||
#define VF610_ADC_MODE_MASK 0x0c
|
||||
#define VF610_ADC_BUSCLK2_SEL 0x01
|
||||
#define VF610_ADC_ALTCLK_SEL 0x02
|
||||
#define VF610_ADC_ADACK_SEL 0x03
|
||||
#define VF610_ADC_ADCCLK_MASK 0x03
|
||||
#define VF610_ADC_CLK_DIV2 0x20
|
||||
#define VF610_ADC_CLK_DIV4 0x40
|
||||
#define VF610_ADC_CLK_DIV8 0x60
|
||||
#define VF610_ADC_CLK_MASK 0x60
|
||||
#define VF610_ADC_ADLSMP_LONG 0x10
|
||||
#define VF610_ADC_ADSTS_MASK 0x300
|
||||
#define VF610_ADC_ADLPC_EN 0x80
|
||||
#define VF610_ADC_ADHSC_EN 0x400
|
||||
#define VF610_ADC_REFSEL_VALT 0x100
|
||||
#define VF610_ADC_REFSEL_VBG 0x1000
|
||||
#define VF610_ADC_ADTRG_HARD 0x2000
|
||||
#define VF610_ADC_AVGS_8 0x4000
|
||||
#define VF610_ADC_AVGS_16 0x8000
|
||||
#define VF610_ADC_AVGS_32 0xC000
|
||||
#define VF610_ADC_AVGS_MASK 0xC000
|
||||
#define VF610_ADC_OVWREN 0x10000
|
||||
|
||||
/* General control register field define */
|
||||
#define VF610_ADC_ADACKEN 0x1
|
||||
#define VF610_ADC_DMAEN 0x2
|
||||
#define VF610_ADC_ACREN 0x4
|
||||
#define VF610_ADC_ACFGT 0x8
|
||||
#define VF610_ADC_ACFE 0x10
|
||||
#define VF610_ADC_AVGEN 0x20
|
||||
#define VF610_ADC_ADCON 0x40
|
||||
#define VF610_ADC_CAL 0x80
|
||||
|
||||
/* Other field define */
|
||||
#define VF610_ADC_ADCHC(x) ((x) & 0xF)
|
||||
#define VF610_ADC_AIEN (0x1 << 7)
|
||||
#define VF610_ADC_CONV_DISABLE 0x1F
|
||||
#define VF610_ADC_HS_COCO0 0x1
|
||||
#define VF610_ADC_CALF 0x2
|
||||
#define VF610_ADC_TIMEOUT msecs_to_jiffies(100)
|
||||
|
||||
enum clk_sel {
|
||||
VF610_ADCIOC_BUSCLK_SET,
|
||||
VF610_ADCIOC_ALTCLK_SET,
|
||||
VF610_ADCIOC_ADACK_SET,
|
||||
};
|
||||
|
||||
enum vol_ref {
|
||||
VF610_ADCIOC_VR_VREF_SET,
|
||||
VF610_ADCIOC_VR_VALT_SET,
|
||||
VF610_ADCIOC_VR_VBG_SET,
|
||||
};
|
||||
|
||||
enum average_sel {
|
||||
VF610_ADC_SAMPLE_1,
|
||||
VF610_ADC_SAMPLE_4,
|
||||
VF610_ADC_SAMPLE_8,
|
||||
VF610_ADC_SAMPLE_16,
|
||||
VF610_ADC_SAMPLE_32,
|
||||
};
|
||||
|
||||
struct vf610_adc_feature {
|
||||
enum clk_sel clk_sel;
|
||||
enum vol_ref vol_ref;
|
||||
|
||||
int clk_div;
|
||||
int sample_rate;
|
||||
int res_mode;
|
||||
|
||||
bool lpm;
|
||||
bool calibration;
|
||||
bool ovwren;
|
||||
};
|
||||
|
||||
struct vf610_adc {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
|
||||
u32 vref_uv;
|
||||
u32 value;
|
||||
struct regulator *vref;
|
||||
struct vf610_adc_feature adc_feature;
|
||||
|
||||
u32 sample_freq_avail[5];
|
||||
|
||||
struct completion completion;
|
||||
};
|
||||
|
||||
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
|
||||
|
||||
#define VF610_ADC_CHAN(_idx, _chan_type) { \
|
||||
.type = (_chan_type), \
|
||||
.indexed = 1, \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
|
||||
VF610_ADC_CHAN(0, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(1, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(2, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(3, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(4, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(5, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(6, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(7, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(8, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(9, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(10, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(11, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(12, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(13, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(14, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(15, IIO_VOLTAGE),
|
||||
/* sentinel */
|
||||
};
|
||||
|
||||
static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
|
||||
{
|
||||
unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Calculate ADC sample frequencies
|
||||
* Sample time unit is ADCK cycles. ADCK clk source is ipg clock,
|
||||
* which is the same as bus clock.
|
||||
*
|
||||
* ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
|
||||
* SFCAdder: fixed to 6 ADCK cycles
|
||||
* AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
|
||||
* BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
|
||||
* LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
|
||||
*/
|
||||
adck_rate = ipg_rate / info->adc_feature.clk_div;
|
||||
for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
|
||||
info->sample_freq_avail[i] =
|
||||
adck_rate / (6 + vf610_hw_avgs[i] * (25 + 3));
|
||||
}
|
||||
|
||||
static inline void vf610_adc_cfg_init(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &info->adc_feature;
|
||||
|
||||
/* set default Configuration for ADC controller */
|
||||
adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET;
|
||||
adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET;
|
||||
|
||||
adc_feature->calibration = true;
|
||||
adc_feature->ovwren = true;
|
||||
|
||||
adc_feature->res_mode = 12;
|
||||
adc_feature->sample_rate = 1;
|
||||
adc_feature->lpm = true;
|
||||
|
||||
/* Use a save ADCK which is below 20MHz on all devices */
|
||||
adc_feature->clk_div = 8;
|
||||
|
||||
vf610_adc_calculate_rates(info);
|
||||
}
|
||||
|
||||
static void vf610_adc_cfg_post_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &info->adc_feature;
|
||||
int cfg_data = 0;
|
||||
int gc_data = 0;
|
||||
|
||||
switch (adc_feature->clk_sel) {
|
||||
case VF610_ADCIOC_ALTCLK_SET:
|
||||
cfg_data |= VF610_ADC_ALTCLK_SEL;
|
||||
break;
|
||||
case VF610_ADCIOC_ADACK_SET:
|
||||
cfg_data |= VF610_ADC_ADACK_SEL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* low power set for calibration */
|
||||
cfg_data |= VF610_ADC_ADLPC_EN;
|
||||
|
||||
/* enable high speed for calibration */
|
||||
cfg_data |= VF610_ADC_ADHSC_EN;
|
||||
|
||||
/* voltage reference */
|
||||
switch (adc_feature->vol_ref) {
|
||||
case VF610_ADCIOC_VR_VREF_SET:
|
||||
break;
|
||||
case VF610_ADCIOC_VR_VALT_SET:
|
||||
cfg_data |= VF610_ADC_REFSEL_VALT;
|
||||
break;
|
||||
case VF610_ADCIOC_VR_VBG_SET:
|
||||
cfg_data |= VF610_ADC_REFSEL_VBG;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error voltage reference\n");
|
||||
}
|
||||
|
||||
/* data overwrite enable */
|
||||
if (adc_feature->ovwren)
|
||||
cfg_data |= VF610_ADC_OVWREN;
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
writel(gc_data, info->regs + VF610_REG_ADC_GC);
|
||||
}
|
||||
|
||||
static void vf610_adc_calibration(struct vf610_adc *info)
|
||||
{
|
||||
int adc_gc, hc_cfg;
|
||||
int timeout;
|
||||
|
||||
if (!info->adc_feature.calibration)
|
||||
return;
|
||||
|
||||
/* enable calibration interrupt */
|
||||
hc_cfg = VF610_ADC_AIEN | VF610_ADC_CONV_DISABLE;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
|
||||
adc_gc = readl(info->regs + VF610_REG_ADC_GC);
|
||||
writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
timeout = wait_for_completion_timeout
|
||||
(&info->completion, VF610_ADC_TIMEOUT);
|
||||
if (timeout == 0)
|
||||
dev_err(info->dev, "Timeout for adc calibration\n");
|
||||
|
||||
adc_gc = readl(info->regs + VF610_REG_ADC_GS);
|
||||
if (adc_gc & VF610_ADC_CALF)
|
||||
dev_err(info->dev, "ADC calibration failed\n");
|
||||
|
||||
info->adc_feature.calibration = false;
|
||||
}
|
||||
|
||||
static void vf610_adc_cfg_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &(info->adc_feature);
|
||||
int cfg_data;
|
||||
|
||||
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
|
||||
|
||||
cfg_data &= ~VF610_ADC_ADLPC_EN;
|
||||
if (adc_feature->lpm)
|
||||
cfg_data |= VF610_ADC_ADLPC_EN;
|
||||
|
||||
cfg_data &= ~VF610_ADC_ADHSC_EN;
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
}
|
||||
|
||||
static void vf610_adc_sample_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &(info->adc_feature);
|
||||
int cfg_data, gc_data;
|
||||
|
||||
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
|
||||
gc_data = readl(info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
/* resolution mode */
|
||||
cfg_data &= ~VF610_ADC_MODE_MASK;
|
||||
switch (adc_feature->res_mode) {
|
||||
case 8:
|
||||
cfg_data |= VF610_ADC_MODE_BIT8;
|
||||
break;
|
||||
case 10:
|
||||
cfg_data |= VF610_ADC_MODE_BIT10;
|
||||
break;
|
||||
case 12:
|
||||
cfg_data |= VF610_ADC_MODE_BIT12;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error resolution mode\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* clock select and clock divider */
|
||||
cfg_data &= ~(VF610_ADC_CLK_MASK | VF610_ADC_ADCCLK_MASK);
|
||||
switch (adc_feature->clk_div) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
cfg_data |= VF610_ADC_CLK_DIV2;
|
||||
break;
|
||||
case 4:
|
||||
cfg_data |= VF610_ADC_CLK_DIV4;
|
||||
break;
|
||||
case 8:
|
||||
cfg_data |= VF610_ADC_CLK_DIV8;
|
||||
break;
|
||||
case 16:
|
||||
switch (adc_feature->clk_sel) {
|
||||
case VF610_ADCIOC_BUSCLK_SET:
|
||||
cfg_data |= VF610_ADC_BUSCLK2_SEL | VF610_ADC_CLK_DIV8;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error clk divider\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use the short sample mode */
|
||||
cfg_data &= ~(VF610_ADC_ADLSMP_LONG | VF610_ADC_ADSTS_MASK);
|
||||
|
||||
/* update hardware average selection */
|
||||
cfg_data &= ~VF610_ADC_AVGS_MASK;
|
||||
gc_data &= ~VF610_ADC_AVGEN;
|
||||
switch (adc_feature->sample_rate) {
|
||||
case VF610_ADC_SAMPLE_1:
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_4:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_8:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_8;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_16:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_16;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_32:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_32;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev,
|
||||
"error hardware sample average select\n");
|
||||
}
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
writel(gc_data, info->regs + VF610_REG_ADC_GC);
|
||||
}
|
||||
|
||||
static void vf610_adc_hw_init(struct vf610_adc *info)
|
||||
{
|
||||
/* CFG: Feature set */
|
||||
vf610_adc_cfg_post_set(info);
|
||||
vf610_adc_sample_set(info);
|
||||
|
||||
/* adc calibration */
|
||||
vf610_adc_calibration(info);
|
||||
|
||||
/* CFG: power and speed set */
|
||||
vf610_adc_cfg_set(info);
|
||||
}
|
||||
|
||||
static int vf610_adc_read_data(struct vf610_adc *info)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = readl(info->regs + VF610_REG_ADC_R0);
|
||||
|
||||
switch (info->adc_feature.res_mode) {
|
||||
case 8:
|
||||
result &= 0xFF;
|
||||
break;
|
||||
case 10:
|
||||
result &= 0x3FF;
|
||||
break;
|
||||
case 12:
|
||||
result &= 0xFFF;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct vf610_adc *info = (struct vf610_adc *)dev_id;
|
||||
int coco;
|
||||
|
||||
coco = readl(info->regs + VF610_REG_ADC_HS);
|
||||
if (coco & VF610_ADC_HS_COCO0) {
|
||||
info->value = vf610_adc_read_data(info);
|
||||
complete(&info->completion);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static ssize_t vf610_show_samp_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(dev_to_iio_dev(dev));
|
||||
size_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(info->sample_freq_avail); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||||
"%u ", info->sample_freq_avail[i]);
|
||||
|
||||
/* replace trailing space by newline */
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(vf610_show_samp_freq_avail);
|
||||
|
||||
static struct attribute *vf610_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group vf610_attribute_group = {
|
||||
.attrs = vf610_attributes,
|
||||
};
|
||||
|
||||
static int vf610_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
unsigned int hc_cfg;
|
||||
long ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
reinit_completion(&info->completion);
|
||||
|
||||
hc_cfg = VF610_ADC_ADCHC(chan->channel);
|
||||
hc_cfg |= VF610_ADC_AIEN;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
ret = wait_for_completion_interruptible_timeout
|
||||
(&info->completion, VF610_ADC_TIMEOUT);
|
||||
if (ret == 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = info->value;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = info->vref_uv / 1000;
|
||||
*val2 = info->adc_feature.res_mode;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = info->sample_freq_avail[info->adc_feature.sample_rate];
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vf610_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
for (i = 0;
|
||||
i < ARRAY_SIZE(info->sample_freq_avail);
|
||||
i++)
|
||||
if (val == info->sample_freq_avail[i]) {
|
||||
info->adc_feature.sample_rate = i;
|
||||
vf610_adc_sample_set(info);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vf610_adc_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
|
||||
if ((readval == NULL) ||
|
||||
(!(reg % 4) || (reg > VF610_REG_ADC_PCTL)))
|
||||
return -EINVAL;
|
||||
|
||||
*readval = readl(info->regs + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info vf610_adc_iio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &vf610_read_raw,
|
||||
.write_raw = &vf610_write_raw,
|
||||
.debugfs_reg_access = &vf610_adc_reg_access,
|
||||
.attrs = &vf610_attribute_group,
|
||||
};
|
||||
|
||||
static const struct of_device_id vf610_adc_match[] = {
|
||||
{ .compatible = "fsl,vf610-adc", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vf610_adc_match);
|
||||
|
||||
static int vf610_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vf610_adc *info;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct vf610_adc));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "Failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(info->dev, irq,
|
||||
vf610_adc_isr, 0,
|
||||
dev_name(&pdev->dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->clk = devm_clk_get(&pdev->dev, "adc");
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
|
||||
PTR_ERR(info->clk));
|
||||
ret = PTR_ERR(info->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(info->vref))
|
||||
return PTR_ERR(info->vref);
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info->vref_uv = regulator_get_voltage(info->vref);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &vf610_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = vf610_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(vf610_adc_iio_channels);
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Could not prepare or enable the clock.\n");
|
||||
goto error_adc_clk_enable;
|
||||
}
|
||||
|
||||
vf610_adc_cfg_init(info);
|
||||
vf610_adc_hw_init(info);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Couldn't register the device.\n");
|
||||
goto error_iio_device_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
error_iio_device_register:
|
||||
clk_disable_unprepare(info->clk);
|
||||
error_adc_clk_enable:
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vf610_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(info->vref);
|
||||
clk_disable_unprepare(info->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int vf610_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int hc_cfg;
|
||||
|
||||
/* ADC controller enters to stop mode */
|
||||
hc_cfg = readl(info->regs + VF610_REG_ADC_HC0);
|
||||
hc_cfg |= VF610_ADC_CONV_DISABLE;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
|
||||
clk_disable_unprepare(info->clk);
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vf610_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vf610_adc_hw_init(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops,
|
||||
vf610_adc_suspend,
|
||||
vf610_adc_resume);
|
||||
|
||||
static struct platform_driver vf610_adc_driver = {
|
||||
.probe = vf610_adc_probe,
|
||||
.remove = vf610_adc_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = vf610_adc_match,
|
||||
.pm = &vf610_adc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(vf610_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Fugang Duan <B38611@freescale.com>");
|
||||
MODULE_DESCRIPTION("Freescale VF610 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
157
drivers/iio/adc/viperboard_adc.c
Normal file
157
drivers/iio/adc/viperboard_adc.c
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Nano River Technologies viperboard IIO ADC driver
|
||||
*
|
||||
* (C) 2012 by Lemonage GmbH
|
||||
* Author: Lars Poeschel <poeschel@lemonage.de>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/mfd/viperboard.h>
|
||||
|
||||
#define VPRBRD_ADC_CMD_GET 0x00
|
||||
|
||||
struct vprbrd_adc_msg {
|
||||
u8 cmd;
|
||||
u8 chan;
|
||||
u8 val;
|
||||
} __packed;
|
||||
|
||||
struct vprbrd_adc {
|
||||
struct vprbrd *vb;
|
||||
};
|
||||
|
||||
#define VPRBRD_ADC_CHANNEL(_index) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
|
||||
static struct iio_chan_spec const vprbrd_adc_iio_channels[] = {
|
||||
VPRBRD_ADC_CHANNEL(0),
|
||||
VPRBRD_ADC_CHANNEL(1),
|
||||
VPRBRD_ADC_CHANNEL(2),
|
||||
VPRBRD_ADC_CHANNEL(3),
|
||||
};
|
||||
|
||||
static int vprbrd_iio_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long info)
|
||||
{
|
||||
int ret, error = 0;
|
||||
struct vprbrd_adc *adc = iio_priv(iio_dev);
|
||||
struct vprbrd *vb = adc->vb;
|
||||
struct vprbrd_adc_msg *admsg = (struct vprbrd_adc_msg *)vb->buf;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&vb->lock);
|
||||
|
||||
admsg->cmd = VPRBRD_ADC_CMD_GET;
|
||||
admsg->chan = chan->channel;
|
||||
admsg->val = 0x00;
|
||||
|
||||
ret = usb_control_msg(vb->usb_dev,
|
||||
usb_sndctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
|
||||
VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg,
|
||||
sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
|
||||
if (ret != sizeof(struct vprbrd_adc_msg)) {
|
||||
dev_err(&iio_dev->dev, "usb send error on adc read\n");
|
||||
error = -EREMOTEIO;
|
||||
}
|
||||
|
||||
ret = usb_control_msg(vb->usb_dev,
|
||||
usb_rcvctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
|
||||
VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, admsg,
|
||||
sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
|
||||
|
||||
*val = admsg->val;
|
||||
|
||||
mutex_unlock(&vb->lock);
|
||||
|
||||
if (ret != sizeof(struct vprbrd_adc_msg)) {
|
||||
dev_err(&iio_dev->dev, "usb recv error on adc read\n");
|
||||
error = -EREMOTEIO;
|
||||
}
|
||||
|
||||
if (error)
|
||||
goto error;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
error:
|
||||
return error;
|
||||
}
|
||||
|
||||
static const struct iio_info vprbrd_adc_iio_info = {
|
||||
.read_raw = &vprbrd_iio_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int vprbrd_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
|
||||
struct vprbrd_adc *adc;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
/* registering iio */
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->vb = vb;
|
||||
indio_dev->name = "viperboard adc";
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &vprbrd_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = vprbrd_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(vprbrd_adc_iio_channels);
|
||||
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not register iio (adc)");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver vprbrd_adc_driver = {
|
||||
.driver = {
|
||||
.name = "viperboard-adc",
|
||||
},
|
||||
.probe = vprbrd_adc_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(vprbrd_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
|
||||
MODULE_DESCRIPTION("IIO ADC driver for Nano River Techs Viperboard");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:viperboard-adc");
|
||||
1336
drivers/iio/adc/xilinx-xadc-core.c
Normal file
1336
drivers/iio/adc/xilinx-xadc-core.c
Normal file
File diff suppressed because it is too large
Load diff
248
drivers/iio/adc/xilinx-xadc-events.c
Normal file
248
drivers/iio/adc/xilinx-xadc-events.c
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* Xilinx XADC driver
|
||||
*
|
||||
* Copyright 2013 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clauen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "xilinx-xadc.h"
|
||||
|
||||
static const struct iio_chan_spec *xadc_event_to_channel(
|
||||
struct iio_dev *indio_dev, unsigned int event)
|
||||
{
|
||||
switch (event) {
|
||||
case XADC_THRESHOLD_OT_MAX:
|
||||
case XADC_THRESHOLD_TEMP_MAX:
|
||||
return &indio_dev->channels[0];
|
||||
case XADC_THRESHOLD_VCCINT_MAX:
|
||||
case XADC_THRESHOLD_VCCAUX_MAX:
|
||||
return &indio_dev->channels[event];
|
||||
default:
|
||||
return &indio_dev->channels[event-1];
|
||||
}
|
||||
}
|
||||
|
||||
static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event)
|
||||
{
|
||||
const struct iio_chan_spec *chan;
|
||||
|
||||
/* Temperature threshold error, we don't handle this yet */
|
||||
if (event == 0)
|
||||
return;
|
||||
|
||||
chan = xadc_event_to_channel(indio_dev, event);
|
||||
|
||||
if (chan->type == IIO_TEMP) {
|
||||
/*
|
||||
* The temperature channel only supports over-temperature
|
||||
* events.
|
||||
*/
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
|
||||
IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns());
|
||||
} else {
|
||||
/*
|
||||
* For other channels we don't know whether it is a upper or
|
||||
* lower threshold event. Userspace will have to check the
|
||||
* channel value if it wants to know.
|
||||
*/
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
|
||||
IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
}
|
||||
|
||||
void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for_each_set_bit(i, &events, 8)
|
||||
xadc_handle_event(indio_dev, i);
|
||||
}
|
||||
|
||||
static unsigned xadc_get_threshold_offset(const struct iio_chan_spec *chan,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
unsigned int offset;
|
||||
|
||||
if (chan->type == IIO_TEMP) {
|
||||
offset = XADC_THRESHOLD_OT_MAX;
|
||||
} else {
|
||||
if (chan->channel < 2)
|
||||
offset = chan->channel + 1;
|
||||
else
|
||||
offset = chan->channel + 6;
|
||||
}
|
||||
|
||||
if (dir == IIO_EV_DIR_FALLING)
|
||||
offset += 4;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static unsigned int xadc_get_alarm_mask(const struct iio_chan_spec *chan)
|
||||
{
|
||||
if (chan->type == IIO_TEMP) {
|
||||
return XADC_ALARM_OT_MASK;
|
||||
} else {
|
||||
switch (chan->channel) {
|
||||
case 0:
|
||||
return XADC_ALARM_VCCINT_MASK;
|
||||
case 1:
|
||||
return XADC_ALARM_VCCAUX_MASK;
|
||||
case 2:
|
||||
return XADC_ALARM_VCCBRAM_MASK;
|
||||
case 3:
|
||||
return XADC_ALARM_VCCPINT_MASK;
|
||||
case 4:
|
||||
return XADC_ALARM_VCCPAUX_MASK;
|
||||
case 5:
|
||||
return XADC_ALARM_VCCODDR_MASK;
|
||||
default:
|
||||
/* We will never get here */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int xadc_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
|
||||
return (bool)(xadc->alarm_mask & xadc_get_alarm_mask(chan));
|
||||
}
|
||||
|
||||
int xadc_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
unsigned int alarm = xadc_get_alarm_mask(chan);
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
uint16_t cfg, old_cfg;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
|
||||
if (state)
|
||||
xadc->alarm_mask |= alarm;
|
||||
else
|
||||
xadc->alarm_mask &= ~alarm;
|
||||
|
||||
xadc->ops->update_alarm(xadc, xadc->alarm_mask);
|
||||
|
||||
ret = _xadc_read_adc_reg(xadc, XADC_REG_CONF1, &cfg);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
old_cfg = cfg;
|
||||
cfg |= XADC_CONF1_ALARM_MASK;
|
||||
cfg &= ~((xadc->alarm_mask & 0xf0) << 4); /* bram, pint, paux, ddr */
|
||||
cfg &= ~((xadc->alarm_mask & 0x08) >> 3); /* ot */
|
||||
cfg &= ~((xadc->alarm_mask & 0x07) << 1); /* temp, vccint, vccaux */
|
||||
if (old_cfg != cfg)
|
||||
ret = _xadc_write_adc_reg(xadc, XADC_REG_CONF1, cfg);
|
||||
|
||||
err_out:
|
||||
mutex_unlock(&xadc->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register value is msb aligned, the lower 4 bits are ignored */
|
||||
#define XADC_THRESHOLD_VALUE_SHIFT 4
|
||||
|
||||
int xadc_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
{
|
||||
unsigned int offset = xadc_get_threshold_offset(chan, dir);
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
*val = xadc->threshold[offset];
|
||||
break;
|
||||
case IIO_EV_INFO_HYSTERESIS:
|
||||
*val = xadc->temp_hysteresis;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val >>= XADC_THRESHOLD_VALUE_SHIFT;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
int xadc_write_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info,
|
||||
int val, int val2)
|
||||
{
|
||||
unsigned int offset = xadc_get_threshold_offset(chan, dir);
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
val <<= XADC_THRESHOLD_VALUE_SHIFT;
|
||||
|
||||
if (val < 0 || val > 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
xadc->threshold[offset] = val;
|
||||
break;
|
||||
case IIO_EV_INFO_HYSTERESIS:
|
||||
xadc->temp_hysteresis = val;
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&xadc->mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (chan->type == IIO_TEMP) {
|
||||
/*
|
||||
* According to the datasheet we need to set the lower 4 bits to
|
||||
* 0x3, otherwise 125 degree celsius will be used as the
|
||||
* threshold.
|
||||
*/
|
||||
val |= 0x3;
|
||||
|
||||
/*
|
||||
* Since we store the hysteresis as relative (to the threshold)
|
||||
* value, but the hardware expects an absolute value we need to
|
||||
* recalcualte this value whenever the hysteresis or the
|
||||
* threshold changes.
|
||||
*/
|
||||
if (xadc->threshold[offset] < xadc->temp_hysteresis)
|
||||
xadc->threshold[offset + 4] = 0;
|
||||
else
|
||||
xadc->threshold[offset + 4] = xadc->threshold[offset] -
|
||||
xadc->temp_hysteresis;
|
||||
ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset + 4),
|
||||
xadc->threshold[offset + 4]);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (info == IIO_EV_INFO_VALUE)
|
||||
ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset), val);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&xadc->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
209
drivers/iio/adc/xilinx-xadc.h
Normal file
209
drivers/iio/adc/xilinx-xadc.h
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* Xilinx XADC driver
|
||||
*
|
||||
* Copyright 2013 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clauen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef __IIO_XILINX_XADC__
|
||||
#define __IIO_XILINX_XADC__
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
struct iio_dev;
|
||||
struct clk;
|
||||
struct xadc_ops;
|
||||
struct platform_device;
|
||||
|
||||
void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events);
|
||||
|
||||
int xadc_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir);
|
||||
int xadc_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state);
|
||||
int xadc_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info,
|
||||
int *val, int *val2);
|
||||
int xadc_write_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info,
|
||||
int val, int val2);
|
||||
|
||||
enum xadc_external_mux_mode {
|
||||
XADC_EXTERNAL_MUX_NONE,
|
||||
XADC_EXTERNAL_MUX_SINGLE,
|
||||
XADC_EXTERNAL_MUX_DUAL,
|
||||
};
|
||||
|
||||
struct xadc {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
|
||||
const struct xadc_ops *ops;
|
||||
|
||||
uint16_t threshold[16];
|
||||
uint16_t temp_hysteresis;
|
||||
unsigned int alarm_mask;
|
||||
|
||||
uint16_t *data;
|
||||
|
||||
struct iio_trigger *trigger;
|
||||
struct iio_trigger *convst_trigger;
|
||||
struct iio_trigger *samplerate_trigger;
|
||||
|
||||
enum xadc_external_mux_mode external_mux_mode;
|
||||
|
||||
unsigned int zynq_alarm;
|
||||
unsigned int zynq_masked_alarm;
|
||||
unsigned int zynq_intmask;
|
||||
struct delayed_work zynq_unmask_work;
|
||||
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
struct completion completion;
|
||||
};
|
||||
|
||||
struct xadc_ops {
|
||||
int (*read)(struct xadc *, unsigned int, uint16_t *);
|
||||
int (*write)(struct xadc *, unsigned int, uint16_t);
|
||||
int (*setup)(struct platform_device *pdev, struct iio_dev *indio_dev,
|
||||
int irq);
|
||||
void (*update_alarm)(struct xadc *, unsigned int);
|
||||
unsigned long (*get_dclk_rate)(struct xadc *);
|
||||
irqreturn_t (*interrupt_handler)(int, void *);
|
||||
irqreturn_t (*threaded_interrupt_handler)(int, void *);
|
||||
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t *val)
|
||||
{
|
||||
lockdep_assert_held(&xadc->mutex);
|
||||
return xadc->ops->read(xadc, reg, val);
|
||||
}
|
||||
|
||||
static inline int _xadc_write_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t val)
|
||||
{
|
||||
lockdep_assert_held(&xadc->mutex);
|
||||
return xadc->ops->write(xadc, reg, val);
|
||||
}
|
||||
|
||||
static inline int xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
ret = _xadc_read_adc_reg(xadc, reg, val);
|
||||
mutex_unlock(&xadc->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int xadc_write_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
ret = _xadc_write_adc_reg(xadc, reg, val);
|
||||
mutex_unlock(&xadc->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* XADC hardmacro register definitions */
|
||||
#define XADC_REG_TEMP 0x00
|
||||
#define XADC_REG_VCCINT 0x01
|
||||
#define XADC_REG_VCCAUX 0x02
|
||||
#define XADC_REG_VPVN 0x03
|
||||
#define XADC_REG_VREFP 0x04
|
||||
#define XADC_REG_VREFN 0x05
|
||||
#define XADC_REG_VCCBRAM 0x06
|
||||
|
||||
#define XADC_REG_VCCPINT 0x0d
|
||||
#define XADC_REG_VCCPAUX 0x0e
|
||||
#define XADC_REG_VCCO_DDR 0x0f
|
||||
#define XADC_REG_VAUX(x) (0x10 + (x))
|
||||
|
||||
#define XADC_REG_MAX_TEMP 0x20
|
||||
#define XADC_REG_MAX_VCCINT 0x21
|
||||
#define XADC_REG_MAX_VCCAUX 0x22
|
||||
#define XADC_REG_MAX_VCCBRAM 0x23
|
||||
#define XADC_REG_MIN_TEMP 0x24
|
||||
#define XADC_REG_MIN_VCCINT 0x25
|
||||
#define XADC_REG_MIN_VCCAUX 0x26
|
||||
#define XADC_REG_MIN_VCCBRAM 0x27
|
||||
#define XADC_REG_MAX_VCCPINT 0x28
|
||||
#define XADC_REG_MAX_VCCPAUX 0x29
|
||||
#define XADC_REG_MAX_VCCO_DDR 0x2a
|
||||
#define XADC_REG_MIN_VCCPINT 0x2b
|
||||
#define XADC_REG_MIN_VCCPAUX 0x2c
|
||||
#define XADC_REG_MIN_VCCO_DDR 0x2d
|
||||
|
||||
#define XADC_REG_CONF0 0x40
|
||||
#define XADC_REG_CONF1 0x41
|
||||
#define XADC_REG_CONF2 0x42
|
||||
#define XADC_REG_SEQ(x) (0x48 + (x))
|
||||
#define XADC_REG_INPUT_MODE(x) (0x4c + (x))
|
||||
#define XADC_REG_THRESHOLD(x) (0x50 + (x))
|
||||
|
||||
#define XADC_REG_FLAG 0x3f
|
||||
|
||||
#define XADC_CONF0_EC BIT(9)
|
||||
#define XADC_CONF0_ACQ BIT(8)
|
||||
#define XADC_CONF0_MUX BIT(11)
|
||||
#define XADC_CONF0_CHAN(x) (x)
|
||||
|
||||
#define XADC_CONF1_SEQ_MASK (0xf << 12)
|
||||
#define XADC_CONF1_SEQ_DEFAULT (0 << 12)
|
||||
#define XADC_CONF1_SEQ_SINGLE_PASS (1 << 12)
|
||||
#define XADC_CONF1_SEQ_CONTINUOUS (2 << 12)
|
||||
#define XADC_CONF1_SEQ_SINGLE_CHANNEL (3 << 12)
|
||||
#define XADC_CONF1_SEQ_SIMULTANEOUS (4 << 12)
|
||||
#define XADC_CONF1_SEQ_INDEPENDENT (8 << 12)
|
||||
#define XADC_CONF1_ALARM_MASK 0x0f0f
|
||||
|
||||
#define XADC_CONF2_DIV_MASK 0xff00
|
||||
#define XADC_CONF2_DIV_OFFSET 8
|
||||
|
||||
#define XADC_CONF2_PD_MASK (0x3 << 4)
|
||||
#define XADC_CONF2_PD_NONE (0x0 << 4)
|
||||
#define XADC_CONF2_PD_ADC_B (0x2 << 4)
|
||||
#define XADC_CONF2_PD_BOTH (0x3 << 4)
|
||||
|
||||
#define XADC_ALARM_TEMP_MASK BIT(0)
|
||||
#define XADC_ALARM_VCCINT_MASK BIT(1)
|
||||
#define XADC_ALARM_VCCAUX_MASK BIT(2)
|
||||
#define XADC_ALARM_OT_MASK BIT(3)
|
||||
#define XADC_ALARM_VCCBRAM_MASK BIT(4)
|
||||
#define XADC_ALARM_VCCPINT_MASK BIT(5)
|
||||
#define XADC_ALARM_VCCPAUX_MASK BIT(6)
|
||||
#define XADC_ALARM_VCCODDR_MASK BIT(7)
|
||||
|
||||
#define XADC_THRESHOLD_TEMP_MAX 0x0
|
||||
#define XADC_THRESHOLD_VCCINT_MAX 0x1
|
||||
#define XADC_THRESHOLD_VCCAUX_MAX 0x2
|
||||
#define XADC_THRESHOLD_OT_MAX 0x3
|
||||
#define XADC_THRESHOLD_TEMP_MIN 0x4
|
||||
#define XADC_THRESHOLD_VCCINT_MIN 0x5
|
||||
#define XADC_THRESHOLD_VCCAUX_MIN 0x6
|
||||
#define XADC_THRESHOLD_OT_MIN 0x7
|
||||
#define XADC_THRESHOLD_VCCBRAM_MAX 0x8
|
||||
#define XADC_THRESHOLD_VCCPINT_MAX 0x9
|
||||
#define XADC_THRESHOLD_VCCPAUX_MAX 0xa
|
||||
#define XADC_THRESHOLD_VCCODDR_MAX 0xb
|
||||
#define XADC_THRESHOLD_VCCBRAM_MIN 0xc
|
||||
#define XADC_THRESHOLD_VCCPINT_MIN 0xd
|
||||
#define XADC_THRESHOLD_VCCPAUX_MIN 0xe
|
||||
#define XADC_THRESHOLD_VCCODDR_MIN 0xf
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue