Merge tag 'iio-for-4.6b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next
Jonathan writes:
2nd round of new IIO device support, features and cleanups for the 4.6 cycle.
New Device Support
* Apex stx104 DAC
- new driver for this PC104 board. Right now DAC support only.
* ADI ad5064
- Add support for ad5625, ad5627, ad5645, ad5665, ad5667 DACs.
- Add support for Linear Technology ltc2606, ltc2607, ltc2609, ltc2616,
ltc2617, ltc2619, ltc2626, ltc2627 and ltc2629.
* ADI ad7192
- add support for the ad7193
* Invensense mpu6050
- substantial rework of driver to use regmap allowing SPI support extending
the now split driver to cover the MPU6000.
* TI adc0832
- new driver supporting ADC0831, ADC0832, ADC0834 and ADC0838 ADCs.
* TI ads1015
- new driver, note that there is an existing hwmon driver. The long term
intention is to probably remove the hwmon driver but for now we just have
guards in place to ensure this driver is not built if that one is enabled.
* TI afe4403
- new driver for this heart rate monitor / pulse oximeter front end chip.
* TI afe4404
- new driver for this heart rate monitor / pulse oximeter front end chip.
Staging Graduations
* mxs-lradc
- A combined general purpose and touch screen (input) device driver.
Originally held in staging to allow reworking into and MFD but as
that wasn't happening and isn't an absolute requirement we are moving
it out of staging.
Driver new features
* ms5611
- triggered buffer support
- IIO_CHAN_INFO_SCALE to aid the triggered buffer support.
Driver cleanups / reworks / fixes
* ad5064
- Use an enum for the register map layout to allow support of additional
chips (precursor to the new support listed above).
- Structural driver changes to allow support of the slightly different
handling for the ltc parts above.
* ad5933
- drop an exceptional & unnecessary for a function pointer.
* ad7606
- Cleanup the repeated copies of pm ops.
- consolidate the various channels specs via a sport of rearranging so only
one version is needed.
* atlas ph sensor
- add select IRQ_WORK
* hmc8543 (soon to move out of staging)
- Comment style fixes
- functionality of suspend and resume was swapped.
* spear-adc
- use devm_clk_dev instead of managing the clk lifetime by hand.
Core
* Use new dmaengine_terminate_sync call to avoid a theoretical race.
* Fix docs for mlock in struct iio_dev as it is correctly taken in some
drivers (docs used to say for core only).
* Add a helper function for calculating the scan index storage size within
the core cutting out some cut and paste versions of the same code.
This commit is contained in:
@@ -297,6 +297,20 @@ config MEN_Z188_ADC
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called men_z188_adc.
|
||||
|
||||
config MXS_LRADC
|
||||
tristate "Freescale i.MX23/i.MX28 LRADC"
|
||||
depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
|
||||
depends on INPUT
|
||||
select STMP_DEVICE
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for i.MX23/i.MX28 LRADC convertor
|
||||
built into these chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mxs-lradc.
|
||||
|
||||
config NAU7802
|
||||
tristate "Nuvoton NAU7802 ADC driver"
|
||||
depends on I2C
|
||||
@@ -362,6 +376,16 @@ config TI_ADC081C
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc081c.
|
||||
|
||||
config TI_ADC0832
|
||||
tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838"
|
||||
depends on SPI
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADC0831,
|
||||
ADC0832, ADC0834, ADC0838 ADC chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc0832.
|
||||
|
||||
config TI_ADC128S052
|
||||
tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021"
|
||||
depends on SPI
|
||||
@@ -372,6 +396,19 @@ config TI_ADC128S052
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc128s052.
|
||||
|
||||
config TI_ADS1015
|
||||
tristate "Texas Instruments ADS1015 ADC"
|
||||
depends on I2C && !SENSORS_ADS1015
|
||||
select REGMAP_I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADS1015
|
||||
ADC chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-ads1015.
|
||||
|
||||
config TI_ADS8688
|
||||
tristate "Texas Instruments ADS8688"
|
||||
depends on SPI && OF
|
||||
|
@@ -29,13 +29,16 @@ 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_MXS_LRADC) += mxs-lradc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
|
||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
|
||||
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
|
||||
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
|
||||
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
|
||||
|
1775
drivers/iio/adc/mxs-lradc.c
Normal file
1775
drivers/iio/adc/mxs-lradc.c
Normal file
File diff suppressed because it is too large
Load Diff
288
drivers/iio/adc/ti-adc0832.c
Normal file
288
drivers/iio/adc/ti-adc0832.c
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* ADC0831/ADC0832/ADC0834/ADC0838 8-bit ADC driver
|
||||
*
|
||||
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* Datasheet: http://www.ti.com/lit/ds/symlink/adc0832-n.pdf
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
enum {
|
||||
adc0831,
|
||||
adc0832,
|
||||
adc0834,
|
||||
adc0838,
|
||||
};
|
||||
|
||||
struct adc0832 {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
struct mutex lock;
|
||||
u8 mux_bits;
|
||||
|
||||
u8 tx_buf[2] ____cacheline_aligned;
|
||||
u8 rx_buf[2];
|
||||
};
|
||||
|
||||
#define ADC0832_VOLTAGE_CHANNEL(chan) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (chan1), \
|
||||
.channel2 = (chan2), \
|
||||
.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 adc0831_channels[] = {
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adc0832_channels[] = {
|
||||
ADC0832_VOLTAGE_CHANNEL(0),
|
||||
ADC0832_VOLTAGE_CHANNEL(1),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adc0834_channels[] = {
|
||||
ADC0832_VOLTAGE_CHANNEL(0),
|
||||
ADC0832_VOLTAGE_CHANNEL(1),
|
||||
ADC0832_VOLTAGE_CHANNEL(2),
|
||||
ADC0832_VOLTAGE_CHANNEL(3),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adc0838_channels[] = {
|
||||
ADC0832_VOLTAGE_CHANNEL(0),
|
||||
ADC0832_VOLTAGE_CHANNEL(1),
|
||||
ADC0832_VOLTAGE_CHANNEL(2),
|
||||
ADC0832_VOLTAGE_CHANNEL(3),
|
||||
ADC0832_VOLTAGE_CHANNEL(4),
|
||||
ADC0832_VOLTAGE_CHANNEL(5),
|
||||
ADC0832_VOLTAGE_CHANNEL(6),
|
||||
ADC0832_VOLTAGE_CHANNEL(7),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7),
|
||||
ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6),
|
||||
};
|
||||
|
||||
static int adc0831_adc_conversion(struct adc0832 *adc)
|
||||
{
|
||||
struct spi_device *spi = adc->spi;
|
||||
int ret;
|
||||
|
||||
ret = spi_read(spi, &adc->rx_buf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Skip TRI-STATE and a leading zero
|
||||
*/
|
||||
return (adc->rx_buf[0] << 2 & 0xff) | (adc->rx_buf[1] >> 6);
|
||||
}
|
||||
|
||||
static int adc0832_adc_conversion(struct adc0832 *adc, int channel,
|
||||
bool differential)
|
||||
{
|
||||
struct spi_device *spi = adc->spi;
|
||||
struct spi_transfer xfer = {
|
||||
.tx_buf = adc->tx_buf,
|
||||
.rx_buf = adc->rx_buf,
|
||||
.len = 2,
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (!adc->mux_bits)
|
||||
return adc0831_adc_conversion(adc);
|
||||
|
||||
/* start bit */
|
||||
adc->tx_buf[0] = 1 << (adc->mux_bits + 1);
|
||||
/* single-ended or differential */
|
||||
adc->tx_buf[0] |= differential ? 0 : (1 << adc->mux_bits);
|
||||
/* odd / sign */
|
||||
adc->tx_buf[0] |= (channel % 2) << (adc->mux_bits - 1);
|
||||
/* select */
|
||||
if (adc->mux_bits > 1)
|
||||
adc->tx_buf[0] |= channel / 2;
|
||||
|
||||
/* align Data output BIT7 (MSB) to 8-bit boundary */
|
||||
adc->tx_buf[0] <<= 1;
|
||||
|
||||
ret = spi_sync_transfer(spi, &xfer, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return adc->rx_buf[1];
|
||||
}
|
||||
|
||||
static int adc0832_read_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *channel, int *value,
|
||||
int *shift, long mask)
|
||||
{
|
||||
struct adc0832 *adc = iio_priv(iio);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&adc->lock);
|
||||
*value = adc0832_adc_conversion(adc, channel->channel,
|
||||
channel->differential);
|
||||
mutex_unlock(&adc->lock);
|
||||
if (*value < 0)
|
||||
return *value;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*value = regulator_get_voltage(adc->reg);
|
||||
if (*value < 0)
|
||||
return *value;
|
||||
|
||||
/* convert regulator output voltage to mV */
|
||||
*value /= 1000;
|
||||
*shift = 8;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info adc0832_info = {
|
||||
.read_raw = adc0832_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adc0832_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adc0832 *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;
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &adc0832_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
switch (spi_get_device_id(spi)->driver_data) {
|
||||
case adc0831:
|
||||
adc->mux_bits = 0;
|
||||
indio_dev->channels = adc0831_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adc0831_channels);
|
||||
break;
|
||||
case adc0832:
|
||||
adc->mux_bits = 1;
|
||||
indio_dev->channels = adc0832_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adc0832_channels);
|
||||
break;
|
||||
case adc0834:
|
||||
adc->mux_bits = 2;
|
||||
indio_dev->channels = adc0834_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adc0834_channels);
|
||||
break;
|
||||
case adc0838:
|
||||
adc->mux_bits = 3;
|
||||
indio_dev->channels = adc0838_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adc0838_channels);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc0832_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adc0832 *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static const struct of_device_id adc0832_dt_ids[] = {
|
||||
{ .compatible = "ti,adc0831", },
|
||||
{ .compatible = "ti,adc0832", },
|
||||
{ .compatible = "ti,adc0834", },
|
||||
{ .compatible = "ti,adc0838", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adc0832_dt_ids);
|
||||
|
||||
#endif
|
||||
|
||||
static const struct spi_device_id adc0832_id[] = {
|
||||
{ "adc0831", adc0831 },
|
||||
{ "adc0832", adc0832 },
|
||||
{ "adc0834", adc0834 },
|
||||
{ "adc0838", adc0838 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adc0832_id);
|
||||
|
||||
static struct spi_driver adc0832_driver = {
|
||||
.driver = {
|
||||
.name = "adc0832",
|
||||
.of_match_table = of_match_ptr(adc0832_dt_ids),
|
||||
},
|
||||
.probe = adc0832_probe,
|
||||
.remove = adc0832_remove,
|
||||
.id_table = adc0832_id,
|
||||
};
|
||||
module_spi_driver(adc0832_driver);
|
||||
|
||||
MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
|
||||
MODULE_DESCRIPTION("ADC0831/ADC0832/ADC0834/ADC0838 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
612
drivers/iio/adc/ti-ads1015.c
Normal file
612
drivers/iio/adc/ti-ads1015.c
Normal file
@@ -0,0 +1,612 @@
|
||||
/*
|
||||
* ADS1015 - Texas Instruments Analog-to-Digital Converter
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* IIO driver for ADS1015 ADC 7-bit I2C slave address:
|
||||
* * 0x48 - ADDR connected to Ground
|
||||
* * 0x49 - ADDR connected to Vdd
|
||||
* * 0x4A - ADDR connected to SDA
|
||||
* * 0x4B - ADDR connected to SCL
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/i2c/ads1015.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/types.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#define ADS1015_DRV_NAME "ads1015"
|
||||
|
||||
#define ADS1015_CONV_REG 0x00
|
||||
#define ADS1015_CFG_REG 0x01
|
||||
|
||||
#define ADS1015_CFG_DR_SHIFT 5
|
||||
#define ADS1015_CFG_MOD_SHIFT 8
|
||||
#define ADS1015_CFG_PGA_SHIFT 9
|
||||
#define ADS1015_CFG_MUX_SHIFT 12
|
||||
|
||||
#define ADS1015_CFG_DR_MASK GENMASK(7, 5)
|
||||
#define ADS1015_CFG_MOD_MASK BIT(8)
|
||||
#define ADS1015_CFG_PGA_MASK GENMASK(11, 9)
|
||||
#define ADS1015_CFG_MUX_MASK GENMASK(14, 12)
|
||||
|
||||
/* device operating modes */
|
||||
#define ADS1015_CONTINUOUS 0
|
||||
#define ADS1015_SINGLESHOT 1
|
||||
|
||||
#define ADS1015_SLEEP_DELAY_MS 2000
|
||||
#define ADS1015_DEFAULT_PGA 2
|
||||
#define ADS1015_DEFAULT_DATA_RATE 4
|
||||
#define ADS1015_DEFAULT_CHAN 0
|
||||
|
||||
enum ads1015_channels {
|
||||
ADS1015_AIN0_AIN1 = 0,
|
||||
ADS1015_AIN0_AIN3,
|
||||
ADS1015_AIN1_AIN3,
|
||||
ADS1015_AIN2_AIN3,
|
||||
ADS1015_AIN0,
|
||||
ADS1015_AIN1,
|
||||
ADS1015_AIN2,
|
||||
ADS1015_AIN3,
|
||||
ADS1015_TIMESTAMP,
|
||||
};
|
||||
|
||||
static const unsigned int ads1015_data_rate[] = {
|
||||
128, 250, 490, 920, 1600, 2400, 3300, 3300
|
||||
};
|
||||
|
||||
static const struct {
|
||||
int scale;
|
||||
int uscale;
|
||||
} ads1015_scale[] = {
|
||||
{3, 0},
|
||||
{2, 0},
|
||||
{1, 0},
|
||||
{0, 500000},
|
||||
{0, 250000},
|
||||
{0, 125000},
|
||||
{0, 125000},
|
||||
{0, 125000},
|
||||
};
|
||||
|
||||
#define ADS1015_V_CHAN(_chan, _addr) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.address = _addr, \
|
||||
.channel = _chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = _addr, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADS1015_V_DIFF_CHAN(_chan, _chan2, _addr) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.differential = 1, \
|
||||
.indexed = 1, \
|
||||
.address = _addr, \
|
||||
.channel = _chan, \
|
||||
.channel2 = _chan2, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = _addr, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
struct ads1015_data {
|
||||
struct regmap *regmap;
|
||||
/*
|
||||
* Protects ADC ops, e.g: concurrent sysfs/buffered
|
||||
* data reads, configuration updates
|
||||
*/
|
||||
struct mutex lock;
|
||||
struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
|
||||
};
|
||||
|
||||
static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return (reg == ADS1015_CFG_REG);
|
||||
}
|
||||
|
||||
static const struct regmap_config ads1015_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.max_register = ADS1015_CFG_REG,
|
||||
.writeable_reg = ads1015_is_writeable_reg,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ads1015_channels[] = {
|
||||
ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1),
|
||||
ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3),
|
||||
ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3),
|
||||
ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3),
|
||||
ADS1015_V_CHAN(0, ADS1015_AIN0),
|
||||
ADS1015_V_CHAN(1, ADS1015_AIN1),
|
||||
ADS1015_V_CHAN(2, ADS1015_AIN2),
|
||||
ADS1015_V_CHAN(3, ADS1015_AIN3),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP),
|
||||
};
|
||||
|
||||
static int ads1015_set_power_state(struct ads1015_data *data, bool on)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
pm_runtime_put_noidle(dev);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
ret = pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val)
|
||||
{
|
||||
int ret, pga, dr, conv_time;
|
||||
bool change;
|
||||
|
||||
if (chan < 0 || chan >= ADS1015_CHANNELS)
|
||||
return -EINVAL;
|
||||
|
||||
pga = data->channel_data[chan].pga;
|
||||
dr = data->channel_data[chan].data_rate;
|
||||
|
||||
ret = regmap_update_bits_check(data->regmap, ADS1015_CFG_REG,
|
||||
ADS1015_CFG_MUX_MASK |
|
||||
ADS1015_CFG_PGA_MASK,
|
||||
chan << ADS1015_CFG_MUX_SHIFT |
|
||||
pga << ADS1015_CFG_PGA_SHIFT,
|
||||
&change);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (change) {
|
||||
conv_time = DIV_ROUND_UP(USEC_PER_SEC, ads1015_data_rate[dr]);
|
||||
usleep_range(conv_time, conv_time + 1);
|
||||
}
|
||||
|
||||
return regmap_read(data->regmap, ADS1015_CONV_REG, val);
|
||||
}
|
||||
|
||||
static irqreturn_t ads1015_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ads1015_data *data = iio_priv(indio_dev);
|
||||
s16 buf[8]; /* 1x s16 ADC val + 3x s16 padding + 4x s16 timestamp */
|
||||
int chan, ret, res;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
chan = find_first_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength);
|
||||
ret = ads1015_get_adc_result(data, chan, &res);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
goto err;
|
||||
}
|
||||
|
||||
buf[0] = res;
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
|
||||
|
||||
err:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ads1015_set_scale(struct ads1015_data *data, int chan,
|
||||
int scale, int uscale)
|
||||
{
|
||||
int i, ret, rindex = -1;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ads1015_scale); i++)
|
||||
if (ads1015_scale[i].scale == scale &&
|
||||
ads1015_scale[i].uscale == uscale) {
|
||||
rindex = i;
|
||||
break;
|
||||
}
|
||||
if (rindex < 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
|
||||
ADS1015_CFG_PGA_MASK,
|
||||
rindex << ADS1015_CFG_PGA_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->channel_data[chan].pga = rindex;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ads1015_set_data_rate(struct ads1015_data *data, int chan, int rate)
|
||||
{
|
||||
int i, ret, rindex = -1;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++)
|
||||
if (ads1015_data_rate[i] == rate) {
|
||||
rindex = i;
|
||||
break;
|
||||
}
|
||||
if (rindex < 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
|
||||
ADS1015_CFG_DR_MASK,
|
||||
rindex << ADS1015_CFG_DR_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->channel_data[chan].data_rate = rindex;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ads1015_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int ret, idx;
|
||||
struct ads1015_data *data = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&data->lock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = ads1015_set_power_state(data, true);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = ads1015_get_adc_result(data, chan->address, val);
|
||||
if (ret < 0) {
|
||||
ads1015_set_power_state(data, false);
|
||||
break;
|
||||
}
|
||||
|
||||
/* 12 bit res, D0 is bit 4 in conversion register */
|
||||
*val = sign_extend32(*val >> 4, 11);
|
||||
|
||||
ret = ads1015_set_power_state(data, false);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
idx = data->channel_data[chan->address].pga;
|
||||
*val = ads1015_scale[idx].scale;
|
||||
*val2 = ads1015_scale[idx].uscale;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
idx = data->channel_data[chan->address].data_rate;
|
||||
*val = ads1015_data_rate[idx];
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ads1015_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct ads1015_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = ads1015_set_scale(data, chan->address, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = ads1015_set_data_rate(data, chan->address, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ads1015_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
return ads1015_set_power_state(iio_priv(indio_dev), true);
|
||||
}
|
||||
|
||||
static int ads1015_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
return ads1015_set_power_state(iio_priv(indio_dev), false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = {
|
||||
.preenable = ads1015_buffer_preenable,
|
||||
.postenable = iio_triggered_buffer_postenable,
|
||||
.predisable = iio_triggered_buffer_predisable,
|
||||
.postdisable = ads1015_buffer_postdisable,
|
||||
.validate_scan_mask = &iio_validate_scan_mask_onehot,
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(scale_available, "3 2 1 0.5 0.25 0.125");
|
||||
static IIO_CONST_ATTR(sampling_frequency_available,
|
||||
"128 250 490 920 1600 2400 3300");
|
||||
|
||||
static struct attribute *ads1015_attributes[] = {
|
||||
&iio_const_attr_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ads1015_attribute_group = {
|
||||
.attrs = ads1015_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info ads1015_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = ads1015_read_raw,
|
||||
.write_raw = ads1015_write_raw,
|
||||
.attrs = &ads1015_attribute_group,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int ads1015_get_channels_config_of(struct i2c_client *client)
|
||||
{
|
||||
struct ads1015_data *data = i2c_get_clientdata(client);
|
||||
struct device_node *node;
|
||||
|
||||
if (!client->dev.of_node ||
|
||||
!of_get_next_child(client->dev.of_node, NULL))
|
||||
return -EINVAL;
|
||||
|
||||
for_each_child_of_node(client->dev.of_node, node) {
|
||||
u32 pval;
|
||||
unsigned int channel;
|
||||
unsigned int pga = ADS1015_DEFAULT_PGA;
|
||||
unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
|
||||
|
||||
if (of_property_read_u32(node, "reg", &pval)) {
|
||||
dev_err(&client->dev, "invalid reg on %s\n",
|
||||
node->full_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
channel = pval;
|
||||
if (channel >= ADS1015_CHANNELS) {
|
||||
dev_err(&client->dev,
|
||||
"invalid channel index %d on %s\n",
|
||||
channel, node->full_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!of_property_read_u32(node, "ti,gain", &pval)) {
|
||||
pga = pval;
|
||||
if (pga > 6) {
|
||||
dev_err(&client->dev, "invalid gain on %s\n",
|
||||
node->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!of_property_read_u32(node, "ti,datarate", &pval)) {
|
||||
data_rate = pval;
|
||||
if (data_rate > 7) {
|
||||
dev_err(&client->dev,
|
||||
"invalid data_rate on %s\n",
|
||||
node->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
data->channel_data[channel].pga = pga;
|
||||
data->channel_data[channel].data_rate = data_rate;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ads1015_get_channels_config(struct i2c_client *client)
|
||||
{
|
||||
unsigned int k;
|
||||
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct ads1015_data *data = iio_priv(indio_dev);
|
||||
struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
/* prefer platform data */
|
||||
if (pdata) {
|
||||
memcpy(data->channel_data, pdata->channel_data,
|
||||
sizeof(data->channel_data));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
if (!ads1015_get_channels_config_of(client))
|
||||
return;
|
||||
#endif
|
||||
/* fallback on default configuration */
|
||||
for (k = 0; k < ADS1015_CHANNELS; ++k) {
|
||||
data->channel_data[k].pga = ADS1015_DEFAULT_PGA;
|
||||
data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE;
|
||||
}
|
||||
}
|
||||
|
||||
static int ads1015_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ads1015_data *data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &ads1015_info;
|
||||
indio_dev->name = ADS1015_DRV_NAME;
|
||||
indio_dev->channels = ads1015_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ads1015_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
/* we need to keep this ABI the same as used by hwmon ADS1015 driver */
|
||||
ads1015_get_channels_config(client);
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &ads1015_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
dev_err(&client->dev, "Failed to allocate register map\n");
|
||||
return PTR_ERR(data->regmap);
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ads1015_trigger_handler,
|
||||
&ads1015_buffer_setup_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
if (ret)
|
||||
goto err_buffer_cleanup;
|
||||
pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
pm_runtime_enable(&client->dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to register IIO device\n");
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ads1015_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct ads1015_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
/* power down single shot mode */
|
||||
return regmap_update_bits(data->regmap, ADS1015_CFG_REG,
|
||||
ADS1015_CFG_MOD_MASK,
|
||||
ADS1015_SINGLESHOT << ADS1015_CFG_MOD_SHIFT);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ads1015_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct ads1015_data *data = iio_priv(indio_dev);
|
||||
|
||||
return regmap_update_bits(data->regmap, ADS1015_CFG_REG,
|
||||
ADS1015_CFG_MOD_MASK,
|
||||
ADS1015_SINGLESHOT << ADS1015_CFG_MOD_SHIFT);
|
||||
}
|
||||
|
||||
static int ads1015_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct ads1015_data *data = iio_priv(indio_dev);
|
||||
|
||||
return regmap_update_bits(data->regmap, ADS1015_CFG_REG,
|
||||
ADS1015_CFG_MOD_MASK,
|
||||
ADS1015_CONTINUOUS << ADS1015_CFG_MOD_SHIFT);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops ads1015_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(ads1015_runtime_suspend,
|
||||
ads1015_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id ads1015_id[] = {
|
||||
{"ads1015", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ads1015_id);
|
||||
|
||||
static struct i2c_driver ads1015_driver = {
|
||||
.driver = {
|
||||
.name = ADS1015_DRV_NAME,
|
||||
.pm = &ads1015_pm_ops,
|
||||
},
|
||||
.probe = ads1015_probe,
|
||||
.remove = ads1015_remove,
|
||||
.id_table = ads1015_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ads1015_driver);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments ADS1015 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
Reference in New Issue
Block a user