From 4bb104e4f30d2202657b77c948f9ef1366ac438e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 17 Dec 2023 17:41:45 +0100 Subject: iio: buffer: Use IIO_SEPARATE instead of a hard-coded 0 Use an explicit IIO_SEPARATE instead of 0 for the 'shared_by' parameter when calling __iio_add_chan_devattr(). For some reason, commit 3704432fb1fd ("iio: refactor info mask and ext_info attribute creation.") updated only 1 place out of 4. Update the remaining ones now. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/1d17f57423172fcb9d9797cfe7c8282f356049c2.1702831285.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 09c41e9ccf87..b581a7e80566 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -616,7 +616,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, &iio_show_fixed_type, NULL, 0, - 0, + IIO_SEPARATE, &indio_dev->dev, buffer, &buffer->buffer_attr_list); @@ -629,7 +629,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, &iio_scan_el_show, &iio_scan_el_store, chan->scan_index, - 0, + IIO_SEPARATE, &indio_dev->dev, buffer, &buffer->buffer_attr_list); @@ -639,7 +639,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, &iio_scan_el_ts_show, &iio_scan_el_ts_store, chan->scan_index, - 0, + IIO_SEPARATE, &indio_dev->dev, buffer, &buffer->buffer_attr_list); -- cgit From ee9ec49046951eff704752669f0c388b506ddbdf Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 19 Dec 2023 18:50:02 +0100 Subject: iio: buffer-dma: Get rid of outgoing queue The buffer-dma code was using two queues, incoming and outgoing, to manage the state of the blocks in use. While this totally works, it adds some complexity to the code, especially since the code only manages 2 blocks. It is much easier to just check each block's state manually, and keep a counter for the next block to dequeue. Since the new DMABUF based API wouldn't use the outgoing queue anyway, getting rid of it now makes the upcoming changes simpler. With this change, the IIO_BLOCK_STATE_DEQUEUED is now useless, and can be removed. Signed-off-by: Paul Cercueil Link: https://lore.kernel.org/r/20231219175009.65482-2-paul@crapouillou.net Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dma.c | 44 +++++++++++++++------------- include/linux/iio/buffer-dma.h | 7 ++--- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index d348af8b9705..1fc91467d1aa 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -179,7 +179,7 @@ static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block( } block->size = size; - block->state = IIO_BLOCK_STATE_DEQUEUED; + block->state = IIO_BLOCK_STATE_DONE; block->queue = queue; INIT_LIST_HEAD(&block->head); kref_init(&block->kref); @@ -191,16 +191,8 @@ static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block( static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block) { - struct iio_dma_buffer_queue *queue = block->queue; - - /* - * The buffer has already been freed by the application, just drop the - * reference. - */ - if (block->state != IIO_BLOCK_STATE_DEAD) { + if (block->state != IIO_BLOCK_STATE_DEAD) block->state = IIO_BLOCK_STATE_DONE; - list_add_tail(&block->head, &queue->outgoing); - } } /** @@ -261,7 +253,6 @@ static bool iio_dma_block_reusable(struct iio_dma_buffer_block *block) * not support abort and has not given back the block yet. */ switch (block->state) { - case IIO_BLOCK_STATE_DEQUEUED: case IIO_BLOCK_STATE_QUEUED: case IIO_BLOCK_STATE_DONE: return true; @@ -317,7 +308,6 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer) * dead. This means we can reset the lists without having to fear * corrution. */ - INIT_LIST_HEAD(&queue->outgoing); spin_unlock_irq(&queue->list_lock); INIT_LIST_HEAD(&queue->incoming); @@ -456,14 +446,20 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue( struct iio_dma_buffer_queue *queue) { struct iio_dma_buffer_block *block; + unsigned int idx; spin_lock_irq(&queue->list_lock); - block = list_first_entry_or_null(&queue->outgoing, struct - iio_dma_buffer_block, head); - if (block != NULL) { - list_del(&block->head); - block->state = IIO_BLOCK_STATE_DEQUEUED; + + idx = queue->fileio.next_dequeue; + block = queue->fileio.blocks[idx]; + + if (block->state == IIO_BLOCK_STATE_DONE) { + idx = (idx + 1) % ARRAY_SIZE(queue->fileio.blocks); + queue->fileio.next_dequeue = idx; + } else { + block = NULL; } + spin_unlock_irq(&queue->list_lock); return block; @@ -539,6 +535,7 @@ size_t iio_dma_buffer_data_available(struct iio_buffer *buf) struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf); struct iio_dma_buffer_block *block; size_t data_available = 0; + unsigned int i; /* * For counting the available bytes we'll use the size of the block not @@ -552,8 +549,15 @@ size_t iio_dma_buffer_data_available(struct iio_buffer *buf) data_available += queue->fileio.active_block->size; spin_lock_irq(&queue->list_lock); - list_for_each_entry(block, &queue->outgoing, head) - data_available += block->size; + + for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { + block = queue->fileio.blocks[i]; + + if (block != queue->fileio.active_block + && block->state == IIO_BLOCK_STATE_DONE) + data_available += block->size; + } + spin_unlock_irq(&queue->list_lock); mutex_unlock(&queue->lock); @@ -617,7 +621,6 @@ int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, queue->ops = ops; INIT_LIST_HEAD(&queue->incoming); - INIT_LIST_HEAD(&queue->outgoing); mutex_init(&queue->lock); spin_lock_init(&queue->list_lock); @@ -645,7 +648,6 @@ void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue) continue; queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD; } - INIT_LIST_HEAD(&queue->outgoing); spin_unlock_irq(&queue->list_lock); INIT_LIST_HEAD(&queue->incoming); diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h index 6564bdcdac66..18d3702fa95d 100644 --- a/include/linux/iio/buffer-dma.h +++ b/include/linux/iio/buffer-dma.h @@ -19,14 +19,12 @@ struct device; /** * enum iio_block_state - State of a struct iio_dma_buffer_block - * @IIO_BLOCK_STATE_DEQUEUED: Block is not queued * @IIO_BLOCK_STATE_QUEUED: Block is on the incoming queue * @IIO_BLOCK_STATE_ACTIVE: Block is currently being processed by the DMA * @IIO_BLOCK_STATE_DONE: Block is on the outgoing queue * @IIO_BLOCK_STATE_DEAD: Block has been marked as to be freed */ enum iio_block_state { - IIO_BLOCK_STATE_DEQUEUED, IIO_BLOCK_STATE_QUEUED, IIO_BLOCK_STATE_ACTIVE, IIO_BLOCK_STATE_DONE, @@ -73,12 +71,15 @@ struct iio_dma_buffer_block { * @active_block: Block being used in read() * @pos: Read offset in the active block * @block_size: Size of each block + * @next_dequeue: index of next block that will be dequeued */ struct iio_dma_buffer_queue_fileio { struct iio_dma_buffer_block *blocks[2]; struct iio_dma_buffer_block *active_block; size_t pos; size_t block_size; + + unsigned int next_dequeue; }; /** @@ -93,7 +94,6 @@ struct iio_dma_buffer_queue_fileio { * list and typically also a list of active blocks in the part that handles * the DMA controller * @incoming: List of buffers on the incoming queue - * @outgoing: List of buffers on the outgoing queue * @active: Whether the buffer is currently active * @fileio: FileIO state */ @@ -105,7 +105,6 @@ struct iio_dma_buffer_queue { struct mutex lock; spinlock_t list_lock; struct list_head incoming; - struct list_head outgoing; bool active; -- cgit From 996b2e046aee6e34f36c66254db128c10d6116f0 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Tue, 19 Dec 2023 18:50:03 +0100 Subject: iio: buffer-dma: split iio_dma_buffer_fileio_free() function This change splits the logic into a separate function, which will be re-used later. Signed-off-by: Alexandru Ardelean Cc: Alexandru Ardelean Signed-off-by: Paul Cercueil Link: https://lore.kernel.org/r/20231219175009.65482-3-paul@crapouillou.net Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dma.c | 43 ++++++++++++++++------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index 1fc91467d1aa..5610ba67925e 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -346,6 +346,29 @@ out_unlock: } EXPORT_SYMBOL_GPL(iio_dma_buffer_request_update); +static void iio_dma_buffer_fileio_free(struct iio_dma_buffer_queue *queue) +{ + unsigned int i; + + spin_lock_irq(&queue->list_lock); + for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { + if (!queue->fileio.blocks[i]) + continue; + queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD; + } + spin_unlock_irq(&queue->list_lock); + + INIT_LIST_HEAD(&queue->incoming); + + for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { + if (!queue->fileio.blocks[i]) + continue; + iio_buffer_block_put(queue->fileio.blocks[i]); + queue->fileio.blocks[i] = NULL; + } + queue->fileio.active_block = NULL; +} + static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue, struct iio_dma_buffer_block *block) { @@ -638,27 +661,9 @@ EXPORT_SYMBOL_GPL(iio_dma_buffer_init); */ void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue) { - unsigned int i; - mutex_lock(&queue->lock); - spin_lock_irq(&queue->list_lock); - for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { - if (!queue->fileio.blocks[i]) - continue; - queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD; - } - spin_unlock_irq(&queue->list_lock); - - INIT_LIST_HEAD(&queue->incoming); - - for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { - if (!queue->fileio.blocks[i]) - continue; - iio_buffer_block_put(queue->fileio.blocks[i]); - queue->fileio.blocks[i] = NULL; - } - queue->fileio.active_block = NULL; + iio_dma_buffer_fileio_free(queue); queue->ops = NULL; mutex_unlock(&queue->lock); -- cgit From 6b626eee66a88b671585c1804667a4fb10a434ef Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Wed, 20 Dec 2023 20:49:52 +0530 Subject: dt-bindings: iio: dac: add MCP4821 Adds support for MCP48xx series of DACs. Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/22244B.pdf #MCP48x1 Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/20002249B.pdf #MCP48x2 Reviewed-by: Rob Herring Signed-off-by: Anshul Dalal Link: https://lore.kernel.org/r/20231220151954.154595-1-anshulusr@gmail.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/dac/microchip,mcp4821.yaml | 86 ++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/microchip,mcp4821.yaml diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp4821.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp4821.yaml new file mode 100644 index 000000000000..0dc577c33918 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/microchip,mcp4821.yaml @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/microchip,mcp4821.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip MCP4821 and similar DACs + +description: | + Supports MCP48x1 (single channel) and MCP48x2 (dual channel) series of DACs. + Device supports simplex communication over SPI in Mode 0 and Mode 3. + + +---------+--------------+-------------+ + | Device | Resolution | Channels | + |---------|--------------|-------------| + | MCP4801 | 8-bit | 1 | + | MCP4802 | 8-bit | 2 | + | MCP4811 | 10-bit | 1 | + | MCP4812 | 10-bit | 2 | + | MCP4821 | 12-bit | 1 | + | MCP4822 | 12-bit | 2 | + +---------+--------------+-------------+ + + Datasheet: + MCP48x1: https://ww1.microchip.com/downloads/en/DeviceDoc/22244B.pdf + MCP48x2: https://ww1.microchip.com/downloads/en/DeviceDoc/20002249B.pdf + +maintainers: + - Anshul Dalal + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - microchip,mcp4801 + - microchip,mcp4802 + - microchip,mcp4811 + - microchip,mcp4812 + - microchip,mcp4821 + - microchip,mcp4822 + + reg: + maxItems: 1 + + vdd-supply: true + + ldac-gpios: + description: | + Active Low LDAC (Latch DAC Input) pin used to update the DAC output. + maxItems: 1 + + powerdown-gpios: + description: | + Active Low SHDN pin used to enter the shutdown mode. + maxItems: 1 + + spi-cpha: true + spi-cpol: true + +required: + - compatible + - reg + - vdd-supply + +additionalProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + compatible = "microchip,mcp4821"; + reg = <0>; + vdd-supply = <&vdd_regulator>; + ldac-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + powerdown-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>; + spi-cpha; + spi-cpol; + }; + }; -- cgit From cdf3ecb0d8d0a83c940a8892b3e8aeaf35dc4353 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Wed, 20 Dec 2023 20:49:53 +0530 Subject: iio: dac: driver for MCP4821 Adds driver for the MCP48xx series of DACs. Device uses a simplex SPI channel. To set the value of an output channel, a 16-bit data of following format must be written: Bit field | Description 15 [MSB] | Channel selection bit 0 -> Channel A 1 -> Channel B 13 | Output Gain Selection bit 0 -> 2x Gain (Vref = 4.096V) 1 -> 1x Gain (Vref = 2.048V) 12 | Output Shutdown Control bit 0 -> Shutdown the selected channel 1 -> Active mode operation 11-0 [LSB]| DAC Input Data bits Value's big endian representation is taken as input for the selected DAC channel. For devices with a resolution of less than 12-bits, only the x most significant bits are considered where x is the resolution of the device. Reference: Page#22 [MCP48x2 Datasheet] Supported devices: +---------+--------------+-------------+ | Device | Resolution | Channels | |---------|--------------|-------------| | MCP4801 | 8-bit | 1 | | MCP4802 | 8-bit | 2 | | MCP4811 | 10-bit | 1 | | MCP4812 | 10-bit | 2 | | MCP4821 | 12-bit | 1 | | MCP4822 | 12-bit | 2 | +---------+--------------+-------------+ Devices tested: MCP4821 [12-bit single channel] MCP4802 [8-bit dual channel] Tested on Raspberry Pi Zero 2W Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/22244B.pdf #MCP48x1 Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/20002249B.pdf #MCP48x2 Signed-off-by: Anshul Dalal Link: https://lore.kernel.org/r/20231220151954.154595-2-anshulusr@gmail.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 ++ drivers/iio/dac/Kconfig | 10 ++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/mcp4821.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 drivers/iio/dac/mcp4821.c diff --git a/MAINTAINERS b/MAINTAINERS index 066707fddd90..f61df0128c0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13174,6 +13174,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531 F: drivers/iio/potentiometer/mcp4018.c F: drivers/iio/potentiometer/mcp4531.c +MCP4821 DAC DRIVER +M: Anshul Dalal +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/dac/microchip,mcp4821.yaml +F: drivers/iio/dac/mcp4821.c + MCR20A IEEE-802.15.4 RADIO DRIVER M: Stefan Schmidt L: linux-wpan@vger.kernel.org diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 93b8be183de6..34eb40bb9529 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -400,6 +400,16 @@ config MCP4728 To compile this driver as a module, choose M here: the module will be called mcp4728. +config MCP4821 + tristate "MCP4801/02/11/12/21/22 DAC driver" + depends on SPI + help + Say yes here to build the driver for the Microchip MCP4801 + MCP4802, MCP4811, MCP4812, MCP4821 and MCP4822 DAC devices. + + To compile this driver as a module, choose M here: the module + will be called mcp4821. + config MCP4922 tristate "MCP4902, MCP4912, MCP4922 DAC driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 5b2bac900d5a..55bf89739d14 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_MAX5522) += max5522.o obj-$(CONFIG_MAX5821) += max5821.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4728) += mcp4728.o +obj-$(CONFIG_MCP4821) += mcp4821.o obj-$(CONFIG_MCP4922) += mcp4922.o obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o obj-$(CONFIG_STM32_DAC) += stm32-dac.o diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c new file mode 100644 index 000000000000..8a0480d33845 --- /dev/null +++ b/drivers/iio/dac/mcp4821.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Anshul Dalal + * + * Driver for Microchip MCP4801, MCP4802, MCP4811, MCP4812, MCP4821 and MCP4822 + * + * Based on the work of: + * Michael Welling (MCP4922 Driver) + * + * Datasheet: + * MCP48x1: https://ww1.microchip.com/downloads/en/DeviceDoc/22244B.pdf + * MCP48x2: https://ww1.microchip.com/downloads/en/DeviceDoc/20002249B.pdf + * + * TODO: + * - Configurable gain + * - Regulator control + */ + +#include +#include +#include + +#include +#include + +#include + +#define MCP4821_ACTIVE_MODE BIT(12) +#define MCP4802_SECOND_CHAN BIT(15) + +/* DAC uses an internal Voltage reference of 4.096V at a gain of 2x */ +#define MCP4821_2X_GAIN_VREF_MV 4096 + +enum mcp4821_supported_drvice_ids { + ID_MCP4801, + ID_MCP4802, + ID_MCP4811, + ID_MCP4812, + ID_MCP4821, + ID_MCP4822, +}; + +struct mcp4821_state { + struct spi_device *spi; + u16 dac_value[2]; +}; + +struct mcp4821_chip_info { + const char *name; + int num_channels; + const struct iio_chan_spec channels[2]; +}; + +#define MCP4821_CHAN(channel_id, resolution) \ + { \ + .type = IIO_VOLTAGE, .output = 1, .indexed = 1, \ + .channel = (channel_id), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .realbits = (resolution), \ + .shift = 12 - (resolution), \ + }, \ + } + +static const struct mcp4821_chip_info mcp4821_chip_info_table[6] = { + [ID_MCP4801] = { + .name = "mcp4801", + .num_channels = 1, + .channels = { + MCP4821_CHAN(0, 8), + }, + }, + [ID_MCP4802] = { + .name = "mcp4802", + .num_channels = 2, + .channels = { + MCP4821_CHAN(0, 8), + MCP4821_CHAN(1, 8), + }, + }, + [ID_MCP4811] = { + .name = "mcp4811", + .num_channels = 1, + .channels = { + MCP4821_CHAN(0, 10), + }, + }, + [ID_MCP4812] = { + .name = "mcp4812", + .num_channels = 2, + .channels = { + MCP4821_CHAN(0, 10), + MCP4821_CHAN(1, 10), + }, + }, + [ID_MCP4821] = { + .name = "mcp4821", + .num_channels = 1, + .channels = { + MCP4821_CHAN(0, 12), + }, + }, + [ID_MCP4822] = { + .name = "mcp4822", + .num_channels = 2, + .channels = { + MCP4821_CHAN(0, 12), + MCP4821_CHAN(1, 12), + }, + }, +}; + +static int mcp4821_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct mcp4821_state *state; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + state = iio_priv(indio_dev); + *val = state->dac_value[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = MCP4821_2X_GAIN_VREF_MV; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int mcp4821_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct mcp4821_state *state = iio_priv(indio_dev); + u16 write_val; + __be16 write_buffer; + int ret; + + if (val2 != 0) + return -EINVAL; + + if (val < 0 || val >= BIT(chan->scan_type.realbits)) + return -EINVAL; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + write_val = MCP4821_ACTIVE_MODE | val << chan->scan_type.shift; + if (chan->channel) + write_val |= MCP4802_SECOND_CHAN; + + write_buffer = cpu_to_be16(write_val); + ret = spi_write(state->spi, &write_buffer, sizeof(write_buffer)); + if (ret) { + dev_err(&state->spi->dev, "Failed to write to device: %d", ret); + return ret; + } + + state->dac_value[chan->channel] = val; + + return 0; +} + +static const struct iio_info mcp4821_info = { + .read_raw = &mcp4821_read_raw, + .write_raw = &mcp4821_write_raw, +}; + +static int mcp4821_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct mcp4821_state *state; + const struct mcp4821_chip_info *info; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); + if (indio_dev == NULL) + return -ENOMEM; + + state = iio_priv(indio_dev); + state->spi = spi; + + info = spi_get_device_match_data(spi); + indio_dev->name = info->name; + indio_dev->info = &mcp4821_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = info->channels; + indio_dev->num_channels = info->num_channels; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +#define MCP4821_COMPATIBLE(of_compatible, id) \ + { \ + .compatible = of_compatible, \ + .data = &mcp4821_chip_info_table[id] \ + } + +static const struct of_device_id mcp4821_of_table[] = { + MCP4821_COMPATIBLE("microchip,mcp4801", ID_MCP4801), + MCP4821_COMPATIBLE("microchip,mcp4802", ID_MCP4802), + MCP4821_COMPATIBLE("microchip,mcp4811", ID_MCP4811), + MCP4821_COMPATIBLE("microchip,mcp4812", ID_MCP4812), + MCP4821_COMPATIBLE("microchip,mcp4821", ID_MCP4821), + MCP4821_COMPATIBLE("microchip,mcp4822", ID_MCP4822), + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mcp4821_of_table); + +static const struct spi_device_id mcp4821_id_table[] = { + { "mcp4801", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4801]}, + { "mcp4802", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4802]}, + { "mcp4811", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4811]}, + { "mcp4812", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812]}, + { "mcp4821", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821]}, + { "mcp4822", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822]}, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, mcp4821_id_table); + +static struct spi_driver mcp4821_driver = { + .driver = { + .name = "mcp4821", + .of_match_table = mcp4821_of_table, + }, + .probe = mcp4821_probe, + .id_table = mcp4821_id_table, +}; +module_spi_driver(mcp4821_driver); + +MODULE_AUTHOR("Anshul Dalal "); +MODULE_DESCRIPTION("Microchip MCP4821 DAC Driver"); +MODULE_LICENSE("GPL"); -- cgit From 020e71c7ffc25dfe29ed9be6c2d39af7bd7f661f Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 19 Dec 2023 17:26:01 -0300 Subject: iio: adc: ad7091r: Allow users to configure device events AD7091R-5 devices are supported by the ad7091r-5 driver together with the ad7091r-base driver. Those drivers declared iio events for notifying user space when ADC readings fall bellow the thresholds of low limit registers or above the values set in high limit registers. However, to configure iio events and their thresholds, a set of callback functions must be implemented and those were not present until now. The consequence of trying to configure ad7091r-5 events without the proper callback functions was a null pointer dereference in the kernel because the pointers to the callback functions were not set. Implement event configuration callbacks allowing users to read/write event thresholds and enable/disable event generation. Since the event spec structs are generic to AD7091R devices, also move those from the ad7091r-5 driver the base driver so they can be reused when support for ad7091r-2/-4/-8 be added. Fixes: ca69300173b6 ("iio: adc: Add support for AD7091R5 ADC") Suggested-by: David Lechner Signed-off-by: Marcelo Schmitt Link: https://lore.kernel.org/r/59552d3548dabd56adc3107b7b4869afee2b0c3c.1703013352.git.marcelo.schmitt1@gmail.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7091r-base.c | 156 +++++++++++++++++++++++++++++++++++++++++ drivers/iio/adc/ad7091r-base.h | 6 ++ drivers/iio/adc/ad7091r5.c | 28 ++------ 3 files changed, 166 insertions(+), 24 deletions(-) diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index d3d287d3b953..6d93da154810 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -50,6 +51,27 @@ struct ad7091r_state { struct mutex lock; /*lock to prevent concurent reads */ }; +const struct iio_event_spec ad7091r_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), + }, +}; +EXPORT_SYMBOL_NS_GPL(ad7091r_events, IIO_AD7091R); + static int ad7091r_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) { int ret, conf; @@ -169,8 +191,142 @@ unlock: return ret; } +static int ad7091r_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 ad7091r_state *st = iio_priv(indio_dev); + int val, ret; + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = regmap_read(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + &val); + if (ret) + return ret; + return val != AD7091R_HIGH_LIMIT; + case IIO_EV_DIR_FALLING: + ret = regmap_read(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + &val); + if (ret) + return ret; + return val != AD7091R_LOW_LIMIT; + default: + return -EINVAL; + } +} + +static int ad7091r_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 ad7091r_state *st = iio_priv(indio_dev); + + if (state) { + return regmap_set_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_ALERT_EN); + } else { + /* + * Set thresholds either to 0 or to 2^12 - 1 as appropriate to + * prevent alerts and thus disable event generation. + */ + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_write(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + AD7091R_HIGH_LIMIT); + case IIO_EV_DIR_FALLING: + return regmap_write(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + AD7091R_LOW_LIMIT); + default: + return -EINVAL; + } + } +} + +static int ad7091r_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 ad7091r_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + ret = regmap_read(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + ret = regmap_read(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + val); + if (ret) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + ret = regmap_read(st->map, + AD7091R_REG_CH_HYSTERESIS(chan->channel), + val); + if (ret) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7091r_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 ad7091r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_write(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + val); + case IIO_EV_DIR_FALLING: + return regmap_write(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + val); + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + return regmap_write(st->map, + AD7091R_REG_CH_HYSTERESIS(chan->channel), + val); + default: + return -EINVAL; + } +} + static const struct iio_info ad7091r_info = { .read_raw = ad7091r_read_raw, + .read_event_config = &ad7091r_read_event_config, + .write_event_config = &ad7091r_write_event_config, + .read_event_value = &ad7091r_read_event_value, + .write_event_value = &ad7091r_write_event_value, }; static irqreturn_t ad7091r_event_handler(int irq, void *private) diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h index 509748aef9b1..7a78976a2f80 100644 --- a/drivers/iio/adc/ad7091r-base.h +++ b/drivers/iio/adc/ad7091r-base.h @@ -8,6 +8,10 @@ #ifndef __DRIVERS_IIO_ADC_AD7091R_BASE_H__ #define __DRIVERS_IIO_ADC_AD7091R_BASE_H__ +/* AD7091R_REG_CH_LIMIT */ +#define AD7091R_HIGH_LIMIT 0xFFF +#define AD7091R_LOW_LIMIT 0x0 + struct device; struct ad7091r_state; @@ -17,6 +21,8 @@ struct ad7091r_chip_info { unsigned int vref_mV; }; +extern const struct iio_event_spec ad7091r_events[3]; + extern const struct regmap_config ad7091r_regmap_config; int ad7091r_probe(struct device *dev, const char *name, diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index 2f048527b7b7..dae98c95ebb8 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -12,26 +12,6 @@ #include "ad7091r-base.h" -static const struct iio_event_spec ad7091r5_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 AD7091R_CHANNEL(idx, bits, ev, num_ev) { \ .type = IIO_VOLTAGE, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ @@ -44,10 +24,10 @@ static const struct iio_event_spec ad7091r5_events[] = { .scan_type.realbits = bits, \ } static const struct iio_chan_spec ad7091r5_channels_irq[] = { - AD7091R_CHANNEL(0, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), - AD7091R_CHANNEL(1, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), - AD7091R_CHANNEL(2, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), - AD7091R_CHANNEL(3, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), + AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), }; static const struct iio_chan_spec ad7091r5_channels_noirq[] = { -- cgit From e71c5c89bcb165a02df35325aa13d1ee40112401 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 19 Dec 2023 17:26:27 -0300 Subject: iio: adc: ad7091r: Enable internal vref if external vref is not supplied The ADC needs a voltage reference to work correctly. Users can provide an external voltage reference or use the chip internal reference to operate the ADC. The availability of an in chip reference for the ADC saves the user from having to supply an external voltage reference, which makes the external reference an optional property as described in the device tree documentation. Though, to use the internal reference, it must be enabled by writing to the configuration register. Enable AD7091R internal voltage reference if no external vref is supplied. Fixes: 260442cc5be4 ("iio: adc: ad7091r5: Add scale and external VREF support") Signed-off-by: Marcelo Schmitt Link: https://lore.kernel.org/r/b865033fa6a4fc4bf2b4a98ec51a6144e0f64f77.1703013352.git.marcelo.schmitt1@gmail.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7091r-base.c | 7 +++++++ drivers/iio/adc/ad7091r-base.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index 6d93da154810..7ccc9b44dcd8 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -406,7 +406,14 @@ int ad7091r_probe(struct device *dev, const char *name, if (IS_ERR(st->vref)) { if (PTR_ERR(st->vref) == -EPROBE_DEFER) return -EPROBE_DEFER; + st->vref = NULL; + /* Enable internal vref */ + ret = regmap_set_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_INT_VREF); + if (ret) + return dev_err_probe(st->dev, ret, + "Error on enable internal reference\n"); } else { ret = regulator_enable(st->vref); if (ret) diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h index 7a78976a2f80..b9e1c8bf3440 100644 --- a/drivers/iio/adc/ad7091r-base.h +++ b/drivers/iio/adc/ad7091r-base.h @@ -8,6 +8,8 @@ #ifndef __DRIVERS_IIO_ADC_AD7091R_BASE_H__ #define __DRIVERS_IIO_ADC_AD7091R_BASE_H__ +#define AD7091R_REG_CONF_INT_VREF BIT(0) + /* AD7091R_REG_CH_LIMIT */ #define AD7091R_HIGH_LIMIT 0xFFF #define AD7091R_LOW_LIMIT 0x0 -- cgit From 5b035ed0a5b4857f8e0dc1328dd66e930ea6eedc Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 19 Dec 2023 17:27:04 -0300 Subject: iio: adc: ad7091r: Move generic AD7091R code to base driver and header file Some code generic to AD7091R devices such as channel definitions were in the AD7091R-5 driver. There was also some generic register definitions declared in the base driver which would make more sense to be in the header file. The device state struct will be needed for the ad7091r8 driver in a follow up patch so that ought to be moved to the header file as well. Lastly, a couple of regmap callback functions are also capable of abstracting characteristics of different AD7091R devices and those are now being exported to IIO_AD7091R name space. Move AD7091R generic code either to the base driver or to the header file so both the ad7091r5 and the ad7091r8 driver can use those declaration in follow up patches. Signed-off-by: Marcelo Schmitt Link: https://lore.kernel.org/r/6376fc523ee503d47ec499e2cd2ef13bfb5fd8ba.1703013352.git.marcelo.schmitt1@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7091r-base.c | 29 ++++------------------------- drivers/iio/adc/ad7091r-base.h | 39 ++++++++++++++++++++++++++++++++++++++- drivers/iio/adc/ad7091r5.c | 11 ----------- 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index 7ccc9b44dcd8..3f05e94c756f 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -16,14 +16,6 @@ #include "ad7091r-base.h" -#define AD7091R_REG_RESULT 0 -#define AD7091R_REG_CHANNEL 1 -#define AD7091R_REG_CONF 2 -#define AD7091R_REG_ALERT 3 -#define AD7091R_REG_CH_LOW_LIMIT(ch) ((ch) * 3 + 4) -#define AD7091R_REG_CH_HIGH_LIMIT(ch) ((ch) * 3 + 5) -#define AD7091R_REG_CH_HYSTERESIS(ch) ((ch) * 3 + 6) - /* AD7091R_REG_RESULT */ #define AD7091R_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x3) #define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff) @@ -36,21 +28,6 @@ #define AD7091R_REG_CONF_MODE_MASK \ (AD7091R_REG_CONF_AUTO | AD7091R_REG_CONF_CMD) -enum ad7091r_mode { - AD7091R_MODE_SAMPLE, - AD7091R_MODE_COMMAND, - AD7091R_MODE_AUTOCYCLE, -}; - -struct ad7091r_state { - struct device *dev; - struct regmap *map; - struct regulator *vref; - const struct ad7091r_chip_info *chip_info; - enum ad7091r_mode mode; - struct mutex lock; /*lock to prevent concurent reads */ -}; - const struct iio_event_spec ad7091r_events[] = { { .type = IIO_EV_TYPE_THRESH, @@ -432,7 +409,7 @@ int ad7091r_probe(struct device *dev, const char *name, } EXPORT_SYMBOL_NS_GPL(ad7091r_probe, IIO_AD7091R); -static bool ad7091r_writeable_reg(struct device *dev, unsigned int reg) +bool ad7091r_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { case AD7091R_REG_RESULT: @@ -442,8 +419,9 @@ static bool ad7091r_writeable_reg(struct device *dev, unsigned int reg) return true; } } +EXPORT_SYMBOL_NS_GPL(ad7091r_writeable_reg, IIO_AD7091R); -static bool ad7091r_volatile_reg(struct device *dev, unsigned int reg) +bool ad7091r_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case AD7091R_REG_RESULT: @@ -453,6 +431,7 @@ static bool ad7091r_volatile_reg(struct device *dev, unsigned int reg) return false; } } +EXPORT_SYMBOL_NS_GPL(ad7091r_volatile_reg, IIO_AD7091R); const struct regmap_config ad7091r_regmap_config = { .reg_bits = 8, diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h index b9e1c8bf3440..b63c658a9d23 100644 --- a/drivers/iio/adc/ad7091r-base.h +++ b/drivers/iio/adc/ad7091r-base.h @@ -8,14 +8,48 @@ #ifndef __DRIVERS_IIO_ADC_AD7091R_BASE_H__ #define __DRIVERS_IIO_ADC_AD7091R_BASE_H__ +#define AD7091R_REG_RESULT 0 +#define AD7091R_REG_CHANNEL 1 +#define AD7091R_REG_CONF 2 +#define AD7091R_REG_ALERT 3 +#define AD7091R_REG_CH_LOW_LIMIT(ch) ((ch) * 3 + 4) +#define AD7091R_REG_CH_HIGH_LIMIT(ch) ((ch) * 3 + 5) +#define AD7091R_REG_CH_HYSTERESIS(ch) ((ch) * 3 + 6) + #define AD7091R_REG_CONF_INT_VREF BIT(0) /* AD7091R_REG_CH_LIMIT */ #define AD7091R_HIGH_LIMIT 0xFFF #define AD7091R_LOW_LIMIT 0x0 +#define AD7091R_CHANNEL(idx, bits, ev, num_ev) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .channel = idx, \ + .event_spec = ev, \ + .num_event_specs = num_ev, \ + .scan_type.storagebits = 16, \ + .scan_type.realbits = bits, \ +} + struct device; -struct ad7091r_state; + +enum ad7091r_mode { + AD7091R_MODE_SAMPLE, + AD7091R_MODE_COMMAND, + AD7091R_MODE_AUTOCYCLE, +}; + +struct ad7091r_state { + struct device *dev; + struct regmap *map; + struct regulator *vref; + const struct ad7091r_chip_info *chip_info; + enum ad7091r_mode mode; + struct mutex lock; /*lock to prevent concurent reads */ +}; struct ad7091r_chip_info { unsigned int num_channels; @@ -31,4 +65,7 @@ int ad7091r_probe(struct device *dev, const char *name, const struct ad7091r_chip_info *chip_info, struct regmap *map, int irq); +bool ad7091r_volatile_reg(struct device *dev, unsigned int reg); +bool ad7091r_writeable_reg(struct device *dev, unsigned int reg); + #endif /* __DRIVERS_IIO_ADC_AD7091R_BASE_H__ */ diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index dae98c95ebb8..9d3ccfca94ec 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -12,17 +12,6 @@ #include "ad7091r-base.h" -#define AD7091R_CHANNEL(idx, bits, ev, num_ev) { \ - .type = IIO_VOLTAGE, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .indexed = 1, \ - .channel = idx, \ - .event_spec = ev, \ - .num_event_specs = num_ev, \ - .scan_type.storagebits = 16, \ - .scan_type.realbits = bits, \ -} static const struct iio_chan_spec ad7091r5_channels_irq[] = { AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), -- cgit From ca1a679049c12a100f8a72ff7e9363d08851f4d8 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 19 Dec 2023 17:27:36 -0300 Subject: iio: adc: ad7091r: Move chip init data to container struct AD7091R designs may differ on their communication protocol and resources required for proper setup. Extract what is design specific into a init_info struct so the base driver can use data and callback functions from that struct rather than checking which specific chip is connected during device initialization. Signed-off-by: Marcelo Schmitt Link: https://lore.kernel.org/r/1aca2261e227474dc58ce26442845947bcde9b14.1703013352.git.marcelo.schmitt1@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7091r-base.c | 27 ++++++++++++-------------- drivers/iio/adc/ad7091r-base.h | 15 ++++++++++++--- drivers/iio/adc/ad7091r5.c | 43 ++++++++++++++++++++++++++++++------------ 3 files changed, 55 insertions(+), 30 deletions(-) diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index 3f05e94c756f..b5f9423e4e6d 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -342,7 +342,7 @@ static void ad7091r_remove(void *data) } int ad7091r_probe(struct device *dev, const char *name, - const struct ad7091r_chip_info *chip_info, + const struct ad7091r_init_info *init_info, struct regmap *map, int irq) { struct iio_dev *iio_dev; @@ -355,17 +355,16 @@ int ad7091r_probe(struct device *dev, const char *name, st = iio_priv(iio_dev); st->dev = dev; - st->chip_info = chip_info; - st->map = map; + init_info->init_adc_regmap(st, init_info->regmap_config); + if (IS_ERR(st->map)) + return dev_err_probe(st->dev, PTR_ERR(st->map), + "Error initializing regmap\n"); - iio_dev->name = name; iio_dev->info = &ad7091r_info; iio_dev->modes = INDIO_DIRECT_MODE; - iio_dev->num_channels = chip_info->num_channels; - iio_dev->channels = chip_info->channels; - if (irq) { + st->chip_info = init_info->info_irq; ret = regmap_update_bits(st->map, AD7091R_REG_CONF, AD7091R_REG_CONF_ALERT_EN, BIT(4)); if (ret) @@ -377,8 +376,14 @@ int ad7091r_probe(struct device *dev, const char *name, IRQF_ONESHOT, name, iio_dev); if (ret) return ret; + } else { + st->chip_info = init_info->info_no_irq; } + iio_dev->name = st->chip_info->name; + iio_dev->num_channels = st->chip_info->num_channels; + iio_dev->channels = st->chip_info->channels; + st->vref = devm_regulator_get_optional(dev, "vref"); if (IS_ERR(st->vref)) { if (PTR_ERR(st->vref) == -EPROBE_DEFER) @@ -433,14 +438,6 @@ bool ad7091r_volatile_reg(struct device *dev, unsigned int reg) } EXPORT_SYMBOL_NS_GPL(ad7091r_volatile_reg, IIO_AD7091R); -const struct regmap_config ad7091r_regmap_config = { - .reg_bits = 8, - .val_bits = 16, - .writeable_reg = ad7091r_writeable_reg, - .volatile_reg = ad7091r_volatile_reg, -}; -EXPORT_SYMBOL_NS_GPL(ad7091r_regmap_config, IIO_AD7091R); - MODULE_AUTHOR("Beniamin Bia "); MODULE_DESCRIPTION("Analog Devices AD7091Rx multi-channel converters"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h index b63c658a9d23..b3c43a200825 100644 --- a/drivers/iio/adc/ad7091r-base.h +++ b/drivers/iio/adc/ad7091r-base.h @@ -8,6 +8,8 @@ #ifndef __DRIVERS_IIO_ADC_AD7091R_BASE_H__ #define __DRIVERS_IIO_ADC_AD7091R_BASE_H__ +#include + #define AD7091R_REG_RESULT 0 #define AD7091R_REG_CHANNEL 1 #define AD7091R_REG_CONF 2 @@ -52,17 +54,24 @@ struct ad7091r_state { }; struct ad7091r_chip_info { + const char *name; unsigned int num_channels; const struct iio_chan_spec *channels; unsigned int vref_mV; }; -extern const struct iio_event_spec ad7091r_events[3]; +struct ad7091r_init_info { + const struct ad7091r_chip_info *info_irq; + const struct ad7091r_chip_info *info_no_irq; + const struct regmap_config *regmap_config; + void (*init_adc_regmap)(struct ad7091r_state *st, + const struct regmap_config *regmap_conf); +}; -extern const struct regmap_config ad7091r_regmap_config; +extern const struct iio_event_spec ad7091r_events[3]; int ad7091r_probe(struct device *dev, const char *name, - const struct ad7091r_chip_info *chip_info, + const struct ad7091r_init_info *init_info, struct regmap *map, int irq); bool ad7091r_volatile_reg(struct device *dev, unsigned int reg); diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index 9d3ccfca94ec..4b8f9ba078eb 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -27,42 +27,61 @@ static const struct iio_chan_spec ad7091r5_channels_noirq[] = { }; static const struct ad7091r_chip_info ad7091r5_chip_info_irq = { + .name = "ad7091r-5", .channels = ad7091r5_channels_irq, .num_channels = ARRAY_SIZE(ad7091r5_channels_irq), .vref_mV = 2500, }; static const struct ad7091r_chip_info ad7091r5_chip_info_noirq = { + .name = "ad7091r-5", .channels = ad7091r5_channels_noirq, .num_channels = ARRAY_SIZE(ad7091r5_channels_noirq), .vref_mV = 2500, }; +static const struct regmap_config ad7091r_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .writeable_reg = ad7091r_writeable_reg, + .volatile_reg = ad7091r_volatile_reg, +}; + +static void ad7091r5_regmap_init(struct ad7091r_state *st, + const struct regmap_config *regmap_conf) +{ + struct i2c_client *i2c = container_of(st->dev, struct i2c_client, dev); + + st->map = devm_regmap_init_i2c(i2c, regmap_conf); +} + +static struct ad7091r_init_info ad7091r5_init_info = { + .info_irq = &ad7091r5_chip_info_irq, + .info_no_irq = &ad7091r5_chip_info_noirq, + .regmap_config = &ad7091r_regmap_config, + .init_adc_regmap = &ad7091r5_regmap_init +}; + static int ad7091r5_i2c_probe(struct i2c_client *i2c) { const struct i2c_device_id *id = i2c_client_get_device_id(i2c); - const struct ad7091r_chip_info *chip_info; - struct regmap *map = devm_regmap_init_i2c(i2c, &ad7091r_regmap_config); - - if (IS_ERR(map)) - return PTR_ERR(map); + const struct ad7091r_init_info *init_info; - if (i2c->irq) - chip_info = &ad7091r5_chip_info_irq; - else - chip_info = &ad7091r5_chip_info_noirq; + init_info = i2c_get_match_data(i2c); + if (!init_info) + return -EINVAL; - return ad7091r_probe(&i2c->dev, id->name, chip_info, map, i2c->irq); + return ad7091r_probe(&i2c->dev, id->name, init_info, NULL, i2c->irq); } static const struct of_device_id ad7091r5_dt_ids[] = { - { .compatible = "adi,ad7091r5" }, + { .compatible = "adi,ad7091r5", .data = &ad7091r5_init_info }, {}, }; MODULE_DEVICE_TABLE(of, ad7091r5_dt_ids); static const struct i2c_device_id ad7091r5_i2c_ids[] = { - {"ad7091r5", 0}, + {"ad7091r5", (kernel_ulong_t)&ad7091r5_init_info }, {} }; MODULE_DEVICE_TABLE(i2c, ad7091r5_i2c_ids); -- cgit From 6ff545a9b87cdc920ee9988fec2edae14c1f5ecb Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 19 Dec 2023 17:28:05 -0300 Subject: iio: adc: ad7091r: Remove unneeded probe parameters With the grouping of ad7091r initialization data and callbacks into the init_info struct, there is no more need to pass the device name and register map through probe function parameters as those will be available in the init_info object. Remove probe parameters that are not needed anymore. Signed-off-by: Marcelo Schmitt Link: https://lore.kernel.org/r/090a6b461410a374511a8c73659de28b2665f96b.1703013352.git.marcelo.schmitt1@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7091r-base.c | 8 ++++---- drivers/iio/adc/ad7091r-base.h | 5 ++--- drivers/iio/adc/ad7091r5.c | 3 +-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index b5f9423e4e6d..2c65c63db40c 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -341,9 +341,8 @@ static void ad7091r_remove(void *data) regulator_disable(st->vref); } -int ad7091r_probe(struct device *dev, const char *name, - const struct ad7091r_init_info *init_info, - struct regmap *map, int irq) +int ad7091r_probe(struct device *dev, const struct ad7091r_init_info *init_info, + int irq) { struct iio_dev *iio_dev; struct ad7091r_state *st; @@ -373,7 +372,8 @@ int ad7091r_probe(struct device *dev, const char *name, ret = devm_request_threaded_irq(dev, irq, NULL, ad7091r_event_handler, IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, name, iio_dev); + IRQF_ONESHOT, + st->chip_info->name, iio_dev); if (ret) return ret; } else { diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h index b3c43a200825..a57ec36491f5 100644 --- a/drivers/iio/adc/ad7091r-base.h +++ b/drivers/iio/adc/ad7091r-base.h @@ -70,9 +70,8 @@ struct ad7091r_init_info { extern const struct iio_event_spec ad7091r_events[3]; -int ad7091r_probe(struct device *dev, const char *name, - const struct ad7091r_init_info *init_info, - struct regmap *map, int irq); +int ad7091r_probe(struct device *dev, const struct ad7091r_init_info *init_info, + int irq); bool ad7091r_volatile_reg(struct device *dev, unsigned int reg); bool ad7091r_writeable_reg(struct device *dev, unsigned int reg); diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index 4b8f9ba078eb..cb4c7865f0cc 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -64,14 +64,13 @@ static struct ad7091r_init_info ad7091r5_init_info = { static int ad7091r5_i2c_probe(struct i2c_client *i2c) { - const struct i2c_device_id *id = i2c_client_get_device_id(i2c); const struct ad7091r_init_info *init_info; init_info = i2c_get_match_data(i2c); if (!init_info) return -EINVAL; - return ad7091r_probe(&i2c->dev, id->name, init_info, NULL, i2c->irq); + return ad7091r_probe(&i2c->dev, init_info, i2c->irq); } static const struct of_device_id ad7091r5_dt_ids[] = { -- cgit From 7e3ebda32d6e8f10acbde2b204f6bf4020f7ef95 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 19 Dec 2023 17:28:45 -0300 Subject: iio: adc: ad7091r: Set device mode through chip_info callback AD7091R-5 devices have a few modes of operation (sample, command, autocycle) which are set by writing to configuration register fields. Follow up patches will add support for AD7091R-2/-4/-8 which don't have those operation modes nor the register fields for setting them. Make ad7091r_set_mode() a callback function of AD7091R chip_info struct so the base driver can appropriately handle each design without having to check which actual chip is connected. Signed-off-by: Marcelo Schmitt Link: https://lore.kernel.org/r/5140336980f66c2c45f05895c3b68e2f65fba1c2.1703013352.git.marcelo.schmitt1@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7091r-base.c | 38 +------------------------------------- drivers/iio/adc/ad7091r-base.h | 8 ++++++++ drivers/iio/adc/ad7091r5.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index 2c65c63db40c..a4ca2e21e023 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -20,14 +20,6 @@ #define AD7091R_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x3) #define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff) -/* AD7091R_REG_CONF */ -#define AD7091R_REG_CONF_ALERT_EN BIT(4) -#define AD7091R_REG_CONF_AUTO BIT(8) -#define AD7091R_REG_CONF_CMD BIT(10) - -#define AD7091R_REG_CONF_MODE_MASK \ - (AD7091R_REG_CONF_AUTO | AD7091R_REG_CONF_CMD) - const struct iio_event_spec ad7091r_events[] = { { .type = IIO_EV_TYPE_THRESH, @@ -49,34 +41,6 @@ const struct iio_event_spec ad7091r_events[] = { }; EXPORT_SYMBOL_NS_GPL(ad7091r_events, IIO_AD7091R); -static int ad7091r_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) -{ - int ret, conf; - - switch (mode) { - case AD7091R_MODE_SAMPLE: - conf = 0; - break; - case AD7091R_MODE_COMMAND: - conf = AD7091R_REG_CONF_CMD; - break; - case AD7091R_MODE_AUTOCYCLE: - conf = AD7091R_REG_CONF_AUTO; - break; - default: - return -EINVAL; - } - - ret = regmap_update_bits(st->map, AD7091R_REG_CONF, - AD7091R_REG_CONF_MODE_MASK, conf); - if (ret) - return ret; - - st->mode = mode; - - return 0; -} - static int ad7091r_set_channel(struct ad7091r_state *st, unsigned int channel) { unsigned int dummy; @@ -406,7 +370,7 @@ int ad7091r_probe(struct device *dev, const struct ad7091r_init_info *init_info, } /* Use command mode by default to convert only desired channels*/ - ret = ad7091r_set_mode(st, AD7091R_MODE_COMMAND); + ret = st->chip_info->set_mode(st, AD7091R_MODE_COMMAND); if (ret) return ret; diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h index a57ec36491f5..7ba5065a63dd 100644 --- a/drivers/iio/adc/ad7091r-base.h +++ b/drivers/iio/adc/ad7091r-base.h @@ -18,7 +18,14 @@ #define AD7091R_REG_CH_HIGH_LIMIT(ch) ((ch) * 3 + 5) #define AD7091R_REG_CH_HYSTERESIS(ch) ((ch) * 3 + 6) +/* AD7091R_REG_CONF */ #define AD7091R_REG_CONF_INT_VREF BIT(0) +#define AD7091R_REG_CONF_ALERT_EN BIT(4) +#define AD7091R_REG_CONF_AUTO BIT(8) +#define AD7091R_REG_CONF_CMD BIT(10) + +#define AD7091R_REG_CONF_MODE_MASK \ + (AD7091R_REG_CONF_AUTO | AD7091R_REG_CONF_CMD) /* AD7091R_REG_CH_LIMIT */ #define AD7091R_HIGH_LIMIT 0xFFF @@ -58,6 +65,7 @@ struct ad7091r_chip_info { unsigned int num_channels; const struct iio_chan_spec *channels; unsigned int vref_mV; + int (*set_mode)(struct ad7091r_state *st, enum ad7091r_mode mode); }; struct ad7091r_init_info { diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index cb4c7865f0cc..e1163b42609c 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -26,11 +26,40 @@ static const struct iio_chan_spec ad7091r5_channels_noirq[] = { AD7091R_CHANNEL(3, 12, NULL, 0), }; +static int ad7091r5_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) +{ + int ret, conf; + + switch (mode) { + case AD7091R_MODE_SAMPLE: + conf = 0; + break; + case AD7091R_MODE_COMMAND: + conf = AD7091R_REG_CONF_CMD; + break; + case AD7091R_MODE_AUTOCYCLE: + conf = AD7091R_REG_CONF_AUTO; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_MODE_MASK, conf); + if (ret) + return ret; + + st->mode = mode; + + return 0; +} + static const struct ad7091r_chip_info ad7091r5_chip_info_irq = { .name = "ad7091r-5", .channels = ad7091r5_channels_irq, .num_channels = ARRAY_SIZE(ad7091r5_channels_irq), .vref_mV = 2500, + .set_mode = &ad7091r5_set_mode, }; static const struct ad7091r_chip_info ad7091r5_chip_info_noirq = { @@ -38,6 +67,7 @@ static const struct ad7091r_chip_info ad7091r5_chip_info_noirq = { .channels = ad7091r5_channels_noirq, .num_channels = ARRAY_SIZE(ad7091r5_channels_noirq), .vref_mV = 2500, + .set_mode = &ad7091r5_set_mode, }; static const struct regmap_config ad7091r_regmap_config = { -- cgit From 8eb5976abfc5990367ca660ce8e222bba6f812c3 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 19 Dec 2023 17:29:30 -0300 Subject: iio: adc: ad7091r: Add chip_info callback to get conversion result channel AD7091R-5 and AD7091R-2/-4/-8 have slightly different register field layout and due to that require different masks for getting the index of the channel associated with each read. Add a callback function so the base driver can get correct channel ID for each chip variant. Signed-off-by: Marcelo Schmitt Link: https://lore.kernel.org/r/1f7a40b4839b3a1c3f1a0654a1b329bea870feb6.1703013352.git.marcelo.schmitt1@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7091r-base.c | 6 +----- drivers/iio/adc/ad7091r-base.h | 6 ++++++ drivers/iio/adc/ad7091r5.c | 7 +++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index a4ca2e21e023..665e930d67d0 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -16,10 +16,6 @@ #include "ad7091r-base.h" -/* AD7091R_REG_RESULT */ -#define AD7091R_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x3) -#define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff) - const struct iio_event_spec ad7091r_events[] = { { .type = IIO_EV_TYPE_THRESH, @@ -74,7 +70,7 @@ static int ad7091r_read_one(struct iio_dev *iio_dev, if (ret) return ret; - if (AD7091R_REG_RESULT_CH_ID(val) != channel) + if (st->chip_info->reg_result_chan_id(val) != channel) return -EIO; *read_val = AD7091R_REG_RESULT_CONV_RESULT(val); diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h index 7ba5065a63dd..b50024ca33e9 100644 --- a/drivers/iio/adc/ad7091r-base.h +++ b/drivers/iio/adc/ad7091r-base.h @@ -18,6 +18,11 @@ #define AD7091R_REG_CH_HIGH_LIMIT(ch) ((ch) * 3 + 5) #define AD7091R_REG_CH_HYSTERESIS(ch) ((ch) * 3 + 6) +/* AD7091R_REG_RESULT */ +#define AD7091R5_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x3) +#define AD7091R8_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x7) +#define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff) + /* AD7091R_REG_CONF */ #define AD7091R_REG_CONF_INT_VREF BIT(0) #define AD7091R_REG_CONF_ALERT_EN BIT(4) @@ -65,6 +70,7 @@ struct ad7091r_chip_info { unsigned int num_channels; const struct iio_chan_spec *channels; unsigned int vref_mV; + unsigned int (*reg_result_chan_id)(unsigned int val); int (*set_mode)(struct ad7091r_state *st, enum ad7091r_mode mode); }; diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index e1163b42609c..a75837334157 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -54,11 +54,17 @@ static int ad7091r5_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) return 0; } +static unsigned int ad7091r5_reg_result_chan_id(unsigned int val) +{ + return AD7091R5_REG_RESULT_CH_ID(val); +} + static const struct ad7091r_chip_info ad7091r5_chip_info_irq = { .name = "ad7091r-5", .channels = ad7091r5_channels_irq, .num_channels = ARRAY_SIZE(ad7091r5_channels_irq), .vref_mV = 2500, + .reg_result_chan_id = &ad7091r5_reg_result_chan_id, .set_mode = &ad7091r5_set_mode, }; @@ -67,6 +73,7 @@ static const struct ad7091r_chip_info ad7091r5_chip_info_noirq = { .channels = ad7091r5_channels_noirq, .num_channels = ARRAY_SIZE(ad7091r5_channels_noirq), .vref_mV = 2500, + .reg_result_chan_id = &ad7091r5_reg_result_chan_id, .set_mode = &ad7091r5_set_mode, }; -- cgit From 276ceecaa2e8bd0b7b0437e3556fccca991a04f7 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 19 Dec 2023 17:29:52 -0300 Subject: iio: adc: Split AD7091R-5 config symbol Split AD7091R-5 kconfig symbol into one symbol for the base AD7091R driver and another one for the I2C interface AD7091R-5 driver. Signed-off-by: Marcelo Schmitt Link: https://lore.kernel.org/r/8cae37c611c1b0fe3faef7a4b8c4cc915eaeddc7.1703013352.git.marcelo.schmitt1@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 4 ++++ drivers/iio/adc/Makefile | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 4eebd5161419..9d2d32d09166 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -36,9 +36,13 @@ config AD4130 To compile this driver as a module, choose M here: the module will be called ad4130. +config AD7091R + tristate + config AD7091R5 tristate "Analog Devices AD7091R5 ADC Driver" depends on I2C + select AD7091R select REGMAP_I2C help Say yes here to build support for Analog Devices AD7091R-5 ADC. diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index c0803383a7cc..1e289d674d4d 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -7,7 +7,8 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD4130) += ad4130.o -obj-$(CONFIG_AD7091R5) += ad7091r5.o ad7091r-base.o +obj-$(CONFIG_AD7091R) += ad7091r-base.o +obj-$(CONFIG_AD7091R5) += ad7091r5.o obj-$(CONFIG_AD7124) += ad7124.o obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_AD7266) += ad7266.o -- cgit From 6875b85729f82614a8127792ad0b04c17fb56487 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 19 Dec 2023 17:32:04 -0300 Subject: dt-bindings: iio: Add AD7091R-8 Add device tree documentation for AD7091R-8. Reviewed-by: Conor Dooley Signed-off-by: Marcelo Schmitt Link: https://lore.kernel.org/r/632eb34801ae54feda453b6a65d60fc8ac2891fd.1703013352.git.marcelo.schmitt1@gmail.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/adi,ad7091r5.yaml | 82 ++++++++++++++++++++-- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml index ce7ba634643c..ddec9747436c 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml @@ -4,36 +4,92 @@ $id: http://devicetree.org/schemas/iio/adc/adi,ad7091r5.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Analog Devices AD7091R5 4-Channel 12-Bit ADC +title: Analog Devices AD7091R-2/-4/-5/-8 Multi-Channel 12-Bit ADCs maintainers: - Michael Hennerich + - Marcelo Schmitt description: | - Analog Devices AD7091R5 4-Channel 12-Bit ADC + Analog Devices AD7091R5 4-Channel 12-Bit ADC supporting I2C interface https://www.analog.com/media/en/technical-documentation/data-sheets/ad7091r-5.pdf + Analog Devices AD7091R-2/AD7091R-4/AD7091R-8 2-/4-/8-Channel 12-Bit ADCs + supporting SPI interface + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7091R-2_7091R-4_7091R-8.pdf properties: compatible: enum: + - adi,ad7091r2 + - adi,ad7091r4 - adi,ad7091r5 + - adi,ad7091r8 reg: maxItems: 1 + vdd-supply: + description: + Provide VDD power to the sensor (VDD range is from 2.7V to 5.25V). + + vdrive-supply: + description: + Determines the voltage level at which the interface logic will operate. + The V_drive voltage range is from 1.8V to 5.25V and must not exceed VDD by + more than 0.3V. + vref-supply: description: Phandle to the vref power supply - interrupts: + convst-gpios: + description: + GPIO connected to the CONVST pin. + This logic input is used to initiate conversions on the analog + input channels. maxItems: 1 + reset-gpios: + maxItems: 1 + + interrupts: + description: + Interrupt for signaling when conversion results exceed the high limit for + ADC readings or fall below the low limit for them. Interrupt source must + be attached to ALERT/BUSY/GPO0 pin. + maxItems: 1 required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + + # AD7091R-2 does not have ALERT/BUSY/GPO pin + - if: + properties: + compatible: + contains: + enum: + - adi,ad7091r2 + then: + properties: + interrupts: false + + - if: + properties: + compatible: + contains: + enum: + - adi,ad7091r2 + - adi,ad7091r4 + - adi,ad7091r8 + then: + required: + - convst-gpios + +unevaluatedProperties: false examples: - | @@ -51,4 +107,22 @@ examples: interrupt-parent = <&gpio>; }; }; + - | + #include + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad7091r8"; + reg = <0x0>; + spi-max-frequency = <1000000>; + vref-supply = <&adc_vref>; + convst-gpios = <&gpio 25 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>; + interrupts = <22 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; + }; + }; ... -- cgit From 0b76ff46c46332f3415be4dc5f9771d5eb2f0f18 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 19 Dec 2023 17:32:36 -0300 Subject: iio: adc: Add support for AD7091R-8 Add support for Analog Devices AD7091R-2, AD7091R-4, and AD7091R-8 low power 12-Bit SAR ADCs with SPI interface. Extend ad7091r-base driver so it can be used by the AD7091R-8 driver. Signed-off-by: Marcelo Schmitt Link: https://lore.kernel.org/r/09d1d1c4b39cecc528488efac6094233715f5659.1703013352.git.marcelo.schmitt1@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 12 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad7091r-base.c | 6 + drivers/iio/adc/ad7091r-base.h | 6 + drivers/iio/adc/ad7091r8.c | 272 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 297 insertions(+) create mode 100644 drivers/iio/adc/ad7091r8.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 9d2d32d09166..3b73c509bd68 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -47,6 +47,18 @@ config AD7091R5 help Say yes here to build support for Analog Devices AD7091R-5 ADC. +config AD7091R8 + tristate "Analog Devices AD7091R8 ADC Driver" + depends on SPI + select AD7091R + select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD7091R-2, AD7091R-4, + and AD7091R-8 ADC. + + To compile this driver as a module, choose M here: the module will be + called ad7091r8. + config AD7124 tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver" depends on SPI_MASTER diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 1e289d674d4d..d2fda54a3259 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD4130) += ad4130.o obj-$(CONFIG_AD7091R) += ad7091r-base.o obj-$(CONFIG_AD7091R5) += ad7091r5.o +obj-$(CONFIG_AD7091R8) += ad7091r8.o obj-$(CONFIG_AD7124) += ad7124.o obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_AD7266) += ad7266.o diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index 665e930d67d0..f4255b91acfc 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -322,6 +322,12 @@ int ad7091r_probe(struct device *dev, const struct ad7091r_init_info *init_info, iio_dev->info = &ad7091r_info; iio_dev->modes = INDIO_DIRECT_MODE; + if (init_info->setup) { + ret = init_info->setup(st); + if (ret < 0) + return ret; + } + if (irq) { st->chip_info = init_info->info_irq; ret = regmap_update_bits(st->map, AD7091R_REG_CONF, diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h index b50024ca33e9..696bf7a897bb 100644 --- a/drivers/iio/adc/ad7091r-base.h +++ b/drivers/iio/adc/ad7091r-base.h @@ -49,6 +49,7 @@ } struct device; +struct gpio_desc; enum ad7091r_mode { AD7091R_MODE_SAMPLE, @@ -59,10 +60,14 @@ enum ad7091r_mode { struct ad7091r_state { struct device *dev; struct regmap *map; + struct gpio_desc *convst_gpio; + struct gpio_desc *reset_gpio; struct regulator *vref; const struct ad7091r_chip_info *chip_info; enum ad7091r_mode mode; struct mutex lock; /*lock to prevent concurent reads */ + __be16 tx_buf __aligned(IIO_DMA_MINALIGN); + __be16 rx_buf; }; struct ad7091r_chip_info { @@ -80,6 +85,7 @@ struct ad7091r_init_info { const struct regmap_config *regmap_config; void (*init_adc_regmap)(struct ad7091r_state *st, const struct regmap_config *regmap_conf); + int (*setup)(struct ad7091r_state *st); }; extern const struct iio_event_spec ad7091r_events[3]; diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c new file mode 100644 index 000000000000..57700f124803 --- /dev/null +++ b/drivers/iio/adc/ad7091r8.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7091R8 12-bit SAR ADC driver + * + * Copyright 2023 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "ad7091r-base.h" + +#define AD7091R8_REG_ADDR_MSK GENMASK(15, 11) +#define AD7091R8_RD_WR_FLAG_MSK BIT(10) +#define AD7091R8_REG_DATA_MSK GENMASK(9, 0) + +#define AD7091R_SPI_REGMAP_CONFIG(n) { \ + .reg_bits = 8, \ + .val_bits = 16, \ + .volatile_reg = ad7091r_volatile_reg, \ + .writeable_reg = ad7091r_writeable_reg, \ + .max_register = AD7091R_REG_CH_HYSTERESIS(n), \ +} + +static int ad7091r8_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) +{ + /* AD7091R-2/-4/-8 don't set sample/command/autocycle mode in conf reg */ + st->mode = mode; + return 0; +} + +static unsigned int ad7091r8_reg_result_chan_id(unsigned int val) +{ + return AD7091R8_REG_RESULT_CH_ID(val); +} + +#define AD7091R_SPI_CHIP_INFO(_n, _name) { \ + .name = _name, \ + .channels = ad7091r##_n##_channels, \ + .num_channels = ARRAY_SIZE(ad7091r##_n##_channels), \ + .vref_mV = 2500, \ + .reg_result_chan_id = &ad7091r8_reg_result_chan_id, \ + .set_mode = &ad7091r8_set_mode, \ +} + +#define AD7091R_SPI_CHIP_INFO_IRQ(_n, _name) { \ + .name = _name, \ + .channels = ad7091r##_n##_channels_irq, \ + .num_channels = ARRAY_SIZE(ad7091r##_n##_channels_irq), \ + .vref_mV = 2500, \ + .reg_result_chan_id = &ad7091r8_reg_result_chan_id, \ + .set_mode = &ad7091r8_set_mode, \ +} + +enum ad7091r8_info_ids { + AD7091R2_INFO, + AD7091R4_INFO, + AD7091R4_INFO_IRQ, + AD7091R8_INFO, + AD7091R8_INFO_IRQ, +}; + +static const struct iio_chan_spec ad7091r2_channels[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), +}; + +static const struct iio_chan_spec ad7091r4_channels[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), + AD7091R_CHANNEL(2, 12, NULL, 0), + AD7091R_CHANNEL(3, 12, NULL, 0), +}; + +static const struct iio_chan_spec ad7091r4_channels_irq[] = { + AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), +}; + +static const struct iio_chan_spec ad7091r8_channels[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), + AD7091R_CHANNEL(2, 12, NULL, 0), + AD7091R_CHANNEL(3, 12, NULL, 0), + AD7091R_CHANNEL(4, 12, NULL, 0), + AD7091R_CHANNEL(5, 12, NULL, 0), + AD7091R_CHANNEL(6, 12, NULL, 0), + AD7091R_CHANNEL(7, 12, NULL, 0), +}; + +static const struct iio_chan_spec ad7091r8_channels_irq[] = { + AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(4, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(5, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(6, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(7, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), +}; + +static void ad7091r_pulse_convst(struct ad7091r_state *st) +{ + gpiod_set_value_cansleep(st->convst_gpio, 1); + gpiod_set_value_cansleep(st->convst_gpio, 0); +} + +static int ad7091r_regmap_bus_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct ad7091r_state *st = context; + struct spi_device *spi = container_of(st->dev, struct spi_device, dev); + int ret; + + struct spi_transfer t[] = { + { + .tx_buf = &st->tx_buf, + .len = 2, + .cs_change = 1, + }, { + .rx_buf = &st->rx_buf, + .len = 2, + } + }; + + if (reg == AD7091R_REG_RESULT) + ad7091r_pulse_convst(st); + + st->tx_buf = cpu_to_be16(reg << 11); + + ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + *val = be16_to_cpu(st->rx_buf); + return 0; +} + +static int ad7091r_regmap_bus_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct ad7091r_state *st = context; + struct spi_device *spi = container_of(st->dev, struct spi_device, dev); + + /* + * AD7091R-2/-4/-8 protocol (datasheet page 31) is to do a single SPI + * transfer with reg address set in bits B15:B11 and value set in B9:B0. + */ + st->tx_buf = cpu_to_be16(FIELD_PREP(AD7091R8_REG_DATA_MSK, val) | + FIELD_PREP(AD7091R8_RD_WR_FLAG_MSK, 1) | + FIELD_PREP(AD7091R8_REG_ADDR_MSK, reg)); + + return spi_write(spi, &st->tx_buf, 2); +} + +static struct regmap_bus ad7091r8_regmap_bus = { + .reg_read = ad7091r_regmap_bus_reg_read, + .reg_write = ad7091r_regmap_bus_reg_write, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static const struct ad7091r_chip_info ad7091r8_infos[] = { + [AD7091R2_INFO] = AD7091R_SPI_CHIP_INFO(2, "ad7091r-2"), + [AD7091R4_INFO] = AD7091R_SPI_CHIP_INFO(4, "ad7091r-4"), + [AD7091R4_INFO_IRQ] = AD7091R_SPI_CHIP_INFO_IRQ(4, "ad7091r-4"), + [AD7091R8_INFO] = AD7091R_SPI_CHIP_INFO(8, "ad7091r-8"), + [AD7091R8_INFO_IRQ] = AD7091R_SPI_CHIP_INFO_IRQ(8, "ad7091r-8") +}; + +static const struct regmap_config ad7091r2_reg_conf = AD7091R_SPI_REGMAP_CONFIG(2); +static const struct regmap_config ad7091r4_reg_conf = AD7091R_SPI_REGMAP_CONFIG(4); +static const struct regmap_config ad7091r8_reg_conf = AD7091R_SPI_REGMAP_CONFIG(8); + +static void ad7091r8_regmap_init(struct ad7091r_state *st, + const struct regmap_config *regmap_conf) +{ + st->map = devm_regmap_init(st->dev, &ad7091r8_regmap_bus, st, + regmap_conf); +} + +static int ad7091r8_gpio_setup(struct ad7091r_state *st) +{ + st->convst_gpio = devm_gpiod_get(st->dev, "convst", GPIOD_OUT_LOW); + if (IS_ERR(st->convst_gpio)) + return dev_err_probe(st->dev, PTR_ERR(st->convst_gpio), + "Error getting convst GPIO\n"); + + st->reset_gpio = devm_gpiod_get_optional(st->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->reset_gpio)) + return dev_err_probe(st->dev, PTR_ERR(st->convst_gpio), + "Error on requesting reset GPIO\n"); + + if (st->reset_gpio) { + fsleep(20); + gpiod_set_value_cansleep(st->reset_gpio, 0); + } + + return 0; +} + +static struct ad7091r_init_info ad7091r2_init_info = { + .info_no_irq = &ad7091r8_infos[AD7091R2_INFO], + .regmap_config = &ad7091r2_reg_conf, + .init_adc_regmap = &ad7091r8_regmap_init, + .setup = &ad7091r8_gpio_setup +}; + +static struct ad7091r_init_info ad7091r4_init_info = { + .info_no_irq = &ad7091r8_infos[AD7091R4_INFO], + .info_irq = &ad7091r8_infos[AD7091R4_INFO_IRQ], + .regmap_config = &ad7091r4_reg_conf, + .init_adc_regmap = &ad7091r8_regmap_init, + .setup = &ad7091r8_gpio_setup +}; + +static struct ad7091r_init_info ad7091r8_init_info = { + .info_no_irq = &ad7091r8_infos[AD7091R8_INFO], + .info_irq = &ad7091r8_infos[AD7091R8_INFO_IRQ], + .regmap_config = &ad7091r8_reg_conf, + .init_adc_regmap = &ad7091r8_regmap_init, + .setup = &ad7091r8_gpio_setup +}; + +static int ad7091r8_spi_probe(struct spi_device *spi) +{ + const struct ad7091r_init_info *init_info; + + init_info = spi_get_device_match_data(spi); + if (!init_info) + return -EINVAL; + + return ad7091r_probe(&spi->dev, init_info, spi->irq); +} + +static const struct of_device_id ad7091r8_of_match[] = { + { .compatible = "adi,ad7091r2", .data = &ad7091r2_init_info }, + { .compatible = "adi,ad7091r4", .data = &ad7091r4_init_info }, + { .compatible = "adi,ad7091r8", .data = &ad7091r8_init_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7091r8_of_match); + +static const struct spi_device_id ad7091r8_spi_id[] = { + { "ad7091r2", (kernel_ulong_t)&ad7091r2_init_info }, + { "ad7091r4", (kernel_ulong_t)&ad7091r4_init_info }, + { "ad7091r8", (kernel_ulong_t)&ad7091r8_init_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7091r8_spi_id); + +static struct spi_driver ad7091r8_driver = { + .driver = { + .name = "ad7091r8", + .of_match_table = ad7091r8_of_match, + }, + .probe = ad7091r8_spi_probe, + .id_table = ad7091r8_spi_id, +}; +module_spi_driver(ad7091r8_driver); + +MODULE_AUTHOR("Marcelo Schmitt "); +MODULE_DESCRIPTION("Analog Devices AD7091R8 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_AD7091R); -- cgit From de35d40926810cd3767f25a7ed24f890137f93b9 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 19 Dec 2023 17:32:59 -0300 Subject: MAINTAINERS: Add MAINTAINERS entry for AD7091R The driver for AD7091R was added in ca693001: iio: adc: Add support for AD7091R5 ADC but no MAINTAINERS file entry was added for it since then. Add a proper MAINTAINERS file entry for the AD7091R driver. Signed-off-by: Marcelo Schmitt Link: https://lore.kernel.org/r/4247e653354f8eb362264189db24c612d5e4e131.1703013352.git.marcelo.schmitt1@gmail.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f61df0128c0b..a31b1b20c6f6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1122,6 +1122,14 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml F: drivers/iio/adc/ad4130.c +ANALOG DEVICES INC AD7091R DRIVER +M: Marcelo Schmitt +L: linux-iio@vger.kernel.org +S: Supported +W: http://ez.analog.com/community/linux-device-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ad7091r* +F: drivers/iio/adc/drivers/iio/adc/ad7091r* + ANALOG DEVICES INC AD7192 DRIVER M: Alexandru Tachici L: linux-iio@vger.kernel.org -- cgit From 8645e659e2d227f6ce8fcea1ac640c324fbbb3e6 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 22 Dec 2023 21:05:56 -0800 Subject: iio: linux/iio.h: fix Excess kernel-doc description warning Remove the @of_xlate: lines to prevent the kernel-doc warning: include/linux/iio/iio.h:534: warning: Excess struct member 'of_xlate' description in 'iio_info' Signed-off-by: Randy Dunlap Cc: Jonathan Cameron Cc: Lars-Peter Clausen Cc: linux-iio@vger.kernel.org Link: https://lore.kernel.org/r/20231223050556.13948-1-rdunlap@infradead.org Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index d0ce3b71106a..c5b36d2c1e73 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -434,13 +434,7 @@ struct iio_trigger; /* forward declaration */ * @update_scan_mode: function to configure device and scan buffer when * channels have changed * @debugfs_reg_access: function to read or write register value of device - * @of_xlate: function pointer to obtain channel specifier index. - * When #iio-cells is greater than '0', the driver could - * provide a custom of_xlate function that reads the - * *args* and returns the appropriate index in registered - * IIO channels array. * @fwnode_xlate: fwnode based function pointer to obtain channel specifier index. - * Functionally the same as @of_xlate. * @hwfifo_set_watermark: function pointer to set the current hardware * fifo watermark level; see hwfifo_* entries in * Documentation/ABI/testing/sysfs-bus-iio for details on -- cgit