Commit 5db9f38d authored by Marcus Folkesson's avatar Marcus Folkesson Committed by Jonathan Cameron

iio: adc: mcp3911: add support for buffers

Add support for buffers to make the driver fit for more use cases.
Signed-off-by: default avatarMarcus Folkesson <marcus.folkesson@gmail.com>
Reviewed-by: default avatarAndy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20220815061625.35568-6-marcus.folkesson@gmail.comSigned-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 0e0a07ad
...@@ -732,6 +732,8 @@ config MCP3422 ...@@ -732,6 +732,8 @@ config MCP3422
config MCP3911 config MCP3911
tristate "Microchip Technology MCP3911 driver" tristate "Microchip Technology MCP3911 driver"
depends on SPI depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help help
Say yes here to build support for Microchip Technology's MCP3911 Say yes here to build support for Microchip Technology's MCP3911
analog to digital converter. analog to digital converter.
......
...@@ -5,16 +5,25 @@ ...@@ -5,16 +5,25 @@
* Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com> * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
* Copyright (C) 2018 Kent Gustavsson <kent@minoris.se> * Copyright (C) 2018 Kent Gustavsson <kent@minoris.se>
*/ */
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/iio/iio.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/trigger.h>
#include <asm/unaligned.h>
#define MCP3911_REG_CHANNEL0 0x00 #define MCP3911_REG_CHANNEL0 0x00
#define MCP3911_REG_CHANNEL1 0x03 #define MCP3911_REG_CHANNEL1 0x03
#define MCP3911_REG_MOD 0x06 #define MCP3911_REG_MOD 0x06
...@@ -22,6 +31,7 @@ ...@@ -22,6 +31,7 @@
#define MCP3911_REG_GAIN 0x09 #define MCP3911_REG_GAIN 0x09
#define MCP3911_REG_STATUSCOM 0x0a #define MCP3911_REG_STATUSCOM 0x0a
#define MCP3911_STATUSCOM_READ GENMASK(7, 6)
#define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4) #define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4)
#define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3) #define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3)
#define MCP3911_STATUSCOM_EN_OFFCAL BIT(2) #define MCP3911_STATUSCOM_EN_OFFCAL BIT(2)
...@@ -54,6 +64,13 @@ struct mcp3911 { ...@@ -54,6 +64,13 @@ struct mcp3911 {
struct regulator *vref; struct regulator *vref;
struct clk *clki; struct clk *clki;
u32 dev_addr; u32 dev_addr;
struct {
u32 channels[MCP3911_NUM_CHANNELS];
s64 ts __aligned(8);
} scan;
u8 tx_buf __aligned(IIO_DMA_MINALIGN);
u8 rx_buf[MCP3911_NUM_CHANNELS * 3];
}; };
static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len) static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len)
...@@ -196,16 +213,66 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev, ...@@ -196,16 +213,66 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev,
.type = IIO_VOLTAGE, \ .type = IIO_VOLTAGE, \
.indexed = 1, \ .indexed = 1, \
.channel = idx, \ .channel = idx, \
.scan_index = idx, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_OFFSET) | \ BIT(IIO_CHAN_INFO_OFFSET) | \
BIT(IIO_CHAN_INFO_SCALE), \ BIT(IIO_CHAN_INFO_SCALE), \
.scan_type = { \
.sign = 's', \
.realbits = 24, \
.storagebits = 32, \
.endianness = IIO_BE, \
}, \
} }
static const struct iio_chan_spec mcp3911_channels[] = { static const struct iio_chan_spec mcp3911_channels[] = {
MCP3911_CHAN(0), MCP3911_CHAN(0),
MCP3911_CHAN(1), MCP3911_CHAN(1),
IIO_CHAN_SOFT_TIMESTAMP(2),
}; };
static irqreturn_t mcp3911_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct mcp3911 *adc = iio_priv(indio_dev);
struct spi_transfer xfer[] = {
{
.tx_buf = &adc->tx_buf,
.len = 1,
}, {
.rx_buf = adc->rx_buf,
.len = sizeof(adc->rx_buf),
},
};
int scan_index;
int i = 0;
int ret;
mutex_lock(&adc->lock);
adc->tx_buf = MCP3911_REG_READ(MCP3911_CHANNEL(0), adc->dev_addr);
ret = spi_sync_transfer(adc->spi, xfer, ARRAY_SIZE(xfer));
if (ret < 0) {
dev_warn(&adc->spi->dev,
"failed to get conversion data\n");
goto out;
}
for_each_set_bit(scan_index, indio_dev->active_scan_mask, indio_dev->masklength) {
const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index];
adc->scan.channels[i] = get_unaligned_be24(&adc->rx_buf[scan_chan->channel * 3]);
i++;
}
iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan,
iio_get_time_ns(indio_dev));
out:
mutex_unlock(&adc->lock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static const struct iio_info mcp3911_info = { static const struct iio_info mcp3911_info = {
.read_raw = mcp3911_read_raw, .read_raw = mcp3911_read_raw,
.write_raw = mcp3911_write_raw, .write_raw = mcp3911_write_raw,
...@@ -214,7 +281,7 @@ static const struct iio_info mcp3911_info = { ...@@ -214,7 +281,7 @@ static const struct iio_info mcp3911_info = {
static int mcp3911_config(struct mcp3911 *adc) static int mcp3911_config(struct mcp3911 *adc)
{ {
struct device *dev = &adc->spi->dev; struct device *dev = &adc->spi->dev;
u32 configreg; u32 regval;
int ret; int ret;
ret = device_property_read_u32(dev, "microchip,device-addr", &adc->dev_addr); ret = device_property_read_u32(dev, "microchip,device-addr", &adc->dev_addr);
...@@ -233,29 +300,43 @@ static int mcp3911_config(struct mcp3911 *adc) ...@@ -233,29 +300,43 @@ static int mcp3911_config(struct mcp3911 *adc)
} }
dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr); dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr);
ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2); ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &regval, 2);
if (ret) if (ret)
return ret; return ret;
regval &= ~MCP3911_CONFIG_VREFEXT;
if (adc->vref) { if (adc->vref) {
dev_dbg(&adc->spi->dev, "use external voltage reference\n"); dev_dbg(&adc->spi->dev, "use external voltage reference\n");
configreg |= MCP3911_CONFIG_VREFEXT; regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 1);
} else { } else {
dev_dbg(&adc->spi->dev, dev_dbg(&adc->spi->dev,
"use internal voltage reference (1.2V)\n"); "use internal voltage reference (1.2V)\n");
configreg &= ~MCP3911_CONFIG_VREFEXT; regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 0);
} }
regval &= ~MCP3911_CONFIG_CLKEXT;
if (adc->clki) { if (adc->clki) {
dev_dbg(&adc->spi->dev, "use external clock as clocksource\n"); dev_dbg(&adc->spi->dev, "use external clock as clocksource\n");
configreg |= MCP3911_CONFIG_CLKEXT; regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 1);
} else { } else {
dev_dbg(&adc->spi->dev, dev_dbg(&adc->spi->dev,
"use crystal oscillator as clocksource\n"); "use crystal oscillator as clocksource\n");
configreg &= ~MCP3911_CONFIG_CLKEXT; regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 0);
} }
return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2); ret = mcp3911_write(adc, MCP3911_REG_CONFIG, regval, 2);
if (ret)
return ret;
ret = mcp3911_read(adc, MCP3911_REG_STATUSCOM, &regval, 2);
if (ret)
return ret;
/* Address counter incremented, cycle through register types */
regval &= ~MCP3911_STATUSCOM_READ;
regval |= FIELD_PREP(MCP3911_STATUSCOM_READ, 0x02);
return mcp3911_write(adc, MCP3911_REG_STATUSCOM, regval, 2);
} }
static void mcp3911_cleanup_regulator(void *vref) static void mcp3911_cleanup_regulator(void *vref)
...@@ -324,6 +405,12 @@ static int mcp3911_probe(struct spi_device *spi) ...@@ -324,6 +405,12 @@ static int mcp3911_probe(struct spi_device *spi)
mutex_init(&adc->lock); mutex_init(&adc->lock);
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
NULL,
mcp3911_trigger_handler, NULL);
if (ret)
return ret;
return devm_iio_device_register(&adc->spi->dev, indio_dev); return devm_iio_device_register(&adc->spi->dev, indio_dev);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment