IIO: 2nd set of new device support, features and cleanup for 6.8

A late/optimistic second pull request. The bots have been poking them
 since Wednesday without any issues. There are a few fixes in the
 ad7091r5 driver as part of rework to enable the ad7091r8 parts that
 are included at start of that series.
 
 Includes pre-work for major changes to the DMA buffers that should
 land in 6.9 and will greatly improve performance and flexibility for
 high performance devices by enabling DMABUF based zero copy transfers
 when we don't need to bounce the data via user space.
 
 New device support
 ------------------
 adi,ad7091r8
  - Major refactor of existing adi,ad7091r5 driver to separate out useful
    shared library code that can be used by I2C and SPI parts.
  - Use that library from a new driver supporting the AD7091R-2, AD7091R-4
    and AD7091R-8 12-Bit SPI ADCs.
  - Series includes some late breaking fixes for the ad7091r5.
 
 microchip,mcp4821
  - New driver for MCP4801, MCP4802, MCP4811, MCP4812, MCP4821 and MCP4822
    I2C single / dual channel DACs.
 
 Cleanup
 -------
 buffers:
  - Use IIO_SEPARATE in place of some hard-coded 0 values.
 dma-buffers:
  - Simplify things to not use an outgoing queue given it only ever has
    up to two elements and we only need to track which is first.
  - Split the iio_dma_buffer_fileio_free() function up to make it easier
    to read and enable reuse in a series lining up for 6.9
 iio.h
  - Drop some stale documentation of struct fields that don't exist.
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmWQCpURHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0FojRBRAAvLHuZ3j9QaFWWFPZqcPnKMeXc//VCycW
 +Mzlx4O8nC6gYqB5SVvJiEf+lB25wBXNcolme9v3RdM88WZ+plbosogkApV6g3y3
 ox1Bg2vRPot0yxVpIn6rm5x0mHiRK3abramoPT4FIpx02biYuatOg8oWYnOuo320
 O26F5ewYSdX/lJcpq1WhIJmRU/rwW0yQZsGGXWdT4IN/URQl0e+NzqUUFBFT+N1p
 vnjymdRn6pKEnMAAWLJES0dnyZ5q0UyYNi7VEvKz+i7baXcTcoWnsmgun0ubPnKF
 HYYNuPUnW6inh8A2mfjcZw7ToUeR5H0Eepz+/N0fil+uvFZmiu5/Yge3U2YHx3+5
 YU/5rcvg874FN81HlaRjo5i0jUrLfApAKjYODaqLOhOT0cVbmEoYfBWVbCQ+SbV3
 RdTeoQtv5a6GOmnnxjk0yblZOvqnZapgZKJn6BbJqZb3muRscpLOrdGeA57f9/n7
 c6/SQ0wmMzBl2Xv4+XJTniHmS1r8p05zUp3e3bPerRLIcRBc7Pfm8cqqL09ftX1f
 9taH3Nt8ghiRdES7zbrFpZOlfNM7YlX/4ThCY3M35V317fdc5bWu1npOWerNjVbR
 GORKD5lVpXHS8mYQOrSQjzDM2BBxSud74YZBv91x2E6z2dr3HjospI6rgObhyzIz
 VhV8Ax+7NWc=
 =JzXL
 -----END PGP SIGNATURE-----

Merge tag 'iio-for-6.8b' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next

Jonathan writes:

IIO: 2nd set of new device support, features and cleanup for 6.8

A late/optimistic second pull request. The bots have been poking them
since Wednesday without any issues. There are a few fixes in the
ad7091r5 driver as part of rework to enable the ad7091r8 parts that
are included at start of that series.

Includes pre-work for major changes to the DMA buffers that should
land in 6.9 and will greatly improve performance and flexibility for
high performance devices by enabling DMABUF based zero copy transfers
when we don't need to bounce the data via user space.

New device support
------------------
adi,ad7091r8
 - Major refactor of existing adi,ad7091r5 driver to separate out useful
   shared library code that can be used by I2C and SPI parts.
 - Use that library from a new driver supporting the AD7091R-2, AD7091R-4
   and AD7091R-8 12-Bit SPI ADCs.
 - Series includes some late breaking fixes for the ad7091r5.

microchip,mcp4821
 - New driver for MCP4801, MCP4802, MCP4811, MCP4812, MCP4821 and MCP4822
   I2C single / dual channel DACs.

Cleanup
-------
buffers:
 - Use IIO_SEPARATE in place of some hard-coded 0 values.
dma-buffers:
 - Simplify things to not use an outgoing queue given it only ever has
   up to two elements and we only need to track which is first.
 - Split the iio_dma_buffer_fileio_free() function up to make it easier
   to read and enable reuse in a series lining up for 6.9
iio.h
 - Drop some stale documentation of struct fields that don't exist.

* tag 'iio-for-6.8b' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio:
  iio: linux/iio.h: fix Excess kernel-doc description warning
  MAINTAINERS: Add MAINTAINERS entry for AD7091R
  iio: adc: Add support for AD7091R-8
  dt-bindings: iio: Add AD7091R-8
  iio: adc: Split AD7091R-5 config symbol
  iio: adc: ad7091r: Add chip_info callback to get conversion result channel
  iio: adc: ad7091r: Set device mode through chip_info callback
  iio: adc: ad7091r: Remove unneeded probe parameters
  iio: adc: ad7091r: Move chip init data to container struct
  iio: adc: ad7091r: Move generic AD7091R code to base driver and header file
  iio: adc: ad7091r: Enable internal vref if external vref is not supplied
  iio: adc: ad7091r: Allow users to configure device events
  iio: dac: driver for MCP4821
  dt-bindings: iio: dac: add MCP4821
  iio: buffer-dma: split iio_dma_buffer_fileio_free() function
  iio: buffer-dma: Get rid of outgoing queue
  iio: buffer: Use IIO_SEPARATE instead of a hard-coded 0
This commit is contained in:
Greg Kroah-Hartman 2023-12-31 10:04:32 +00:00
commit b1a1eaf618
16 changed files with 1107 additions and 193 deletions

View File

@ -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 <michael.hennerich@analog.com>
- Marcelo Schmitt <marcelo.schmitt@analog.com>
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 <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
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>;
};
};
...

View File

@ -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 <anshulusr@gmail.com>
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 <dt-bindings/gpio/gpio.h>
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;
};
};

View File

@ -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 <marcelo.schmitt@analog.com>
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 <alexandru.tachici@analog.com>
L: linux-iio@vger.kernel.org
@ -13180,6 +13188,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 <anshulusr@gmail.com>
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 <stefan@datenfreihafen.org>
L: linux-wpan@vger.kernel.org

View File

@ -36,13 +36,29 @@ 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.
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

View File

@ -7,7 +7,9 @@
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_AD7091R8) += ad7091r8.o
obj-$(CONFIG_AD7124) += ad7124.o
obj-$(CONFIG_AD7192) += ad7192.o
obj-$(CONFIG_AD7266) += ad7266.o

View File

@ -6,6 +6,7 @@
*/
#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
@ -15,68 +16,26 @@
#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)
/* 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)
enum ad7091r_mode {
AD7091R_MODE_SAMPLE,
AD7091R_MODE_COMMAND,
AD7091R_MODE_AUTOCYCLE,
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),
},
};
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 */
};
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;
}
EXPORT_SYMBOL_NS_GPL(ad7091r_events, IIO_AD7091R);
static int ad7091r_set_channel(struct ad7091r_state *st, unsigned int channel)
{
@ -111,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);
@ -169,8 +128,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)
@ -208,9 +301,8 @@ static void ad7091r_remove(void *data)
regulator_disable(st->vref);
}
int ad7091r_probe(struct device *dev, const char *name,
const struct ad7091r_chip_info *chip_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;
@ -222,17 +314,22 @@ 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 (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,
AD7091R_REG_CONF_ALERT_EN, BIT(4));
if (ret)
@ -241,16 +338,30 @@ 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 {
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)
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)
@ -261,7 +372,7 @@ int ad7091r_probe(struct device *dev, const char *name,
}
/* 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;
@ -269,7 +380,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:
@ -279,8 +390,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:
@ -290,14 +402,7 @@ static bool ad7091r_volatile_reg(struct device *dev, unsigned int reg)
return false;
}
}
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);
EXPORT_SYMBOL_NS_GPL(ad7091r_volatile_reg, IIO_AD7091R);
MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7091Rx multi-channel converters");

View File

@ -8,19 +8,92 @@
#ifndef __DRIVERS_IIO_ADC_AD7091R_BASE_H__
#define __DRIVERS_IIO_ADC_AD7091R_BASE_H__
#include <linux/regmap.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 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)
#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
#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;
struct gpio_desc;
enum ad7091r_mode {
AD7091R_MODE_SAMPLE,
AD7091R_MODE_COMMAND,
AD7091R_MODE_AUTOCYCLE,
};
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 {
const char *name;
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);
};
extern const struct regmap_config ad7091r_regmap_config;
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);
int (*setup)(struct ad7091r_state *st);
};
int ad7091r_probe(struct device *dev, const char *name,
const struct ad7091r_chip_info *chip_info,
struct regmap *map, int irq);
extern const struct iio_event_spec ad7091r_events[3];
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);
#endif /* __DRIVERS_IIO_ADC_AD7091R_BASE_H__ */

View File

@ -12,42 +12,11 @@
#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), \
.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, 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[] = {
@ -57,43 +26,98 @@ 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 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,
};
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,
.reg_result_chan_id = &ad7091r5_reg_result_chan_id,
.set_mode = &ad7091r5_set_mode,
};
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);
const struct ad7091r_init_info *init_info;
if (IS_ERR(map))
return PTR_ERR(map);
init_info = i2c_get_match_data(i2c);
if (!init_info)
return -EINVAL;
if (i2c->irq)
chip_info = &ad7091r5_chip_info_irq;
else
chip_info = &ad7091r5_chip_info_noirq;
return ad7091r_probe(&i2c->dev, id->name, chip_info, map, i2c->irq);
return ad7091r_probe(&i2c->dev, init_info, 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);

272
drivers/iio/adc/ad7091r8.c Normal file
View File

@ -0,0 +1,272 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Analog Devices AD7091R8 12-bit SAR ADC driver
*
* Copyright 2023 Analog Devices Inc.
*/
#include <linux/bitfield.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>
#include <linux/spi/spi.h>
#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 <marcelo.schmitt@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7091R8 ADC driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_AD7091R);

View File

@ -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);
@ -356,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)
{
@ -456,14 +469,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 +558,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 +572,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 +644,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);
@ -635,28 +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;
}
INIT_LIST_HEAD(&queue->outgoing);
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);

View File

@ -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

View File

@ -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

236
drivers/iio/dac/mcp4821.c Normal file
View File

@ -0,0 +1,236 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com>
*
* 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 <linux/module.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
#include <asm/unaligned.h>
#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 <anshulusr@gmail.com>");
MODULE_DESCRIPTION("Microchip MCP4821 DAC Driver");
MODULE_LICENSE("GPL");

View File

@ -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);

View File

@ -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;

View File

@ -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