This v2 is an addon to initial RFC:
https://lore.kernel.org/lkml/[email protected]/
Despite the "IIO backend" naming has to be changed (as pointed out by
Jonathan previously), it has been kept here, for time being. The
appropriated naming still has to be discussed later on.
In the previous RFC the "IIO backend" concept was proposed through
a set of template APIs.
This v2 implements a functionnal exemple based on STM32 DFSDM,
to bring scaling support to this peripheral.
Olivier Moysan (11):
iio: introduce iio backend device
of: property: add device link support for io-backends
dt-bindings: iio: stm32-dfsdm-adc: add scaling support
dt-bindings: iio: adc: add scaling support to sd modulator
iio: adc: stm32-dfsdm: manage dfsdm as a channel provider
iio: adc: stm32-dfsdm: adopt generic channel bindings
iio: adc: stm32-dfsdm: add scaling support to dfsdm
iio: adc: sd modulator: add scale and offset support
ARM: dts: stm32: adopt new dfsdm bindings on stm32mp151
ARM: dts: stm32: add dfsdm pins muxing on stm32mp15
ARM: dts: stm32: add dfsdm iio support on stm32mp157c-ev
.../iio/adc/sigma-delta-modulator.yaml | 9 +-
.../bindings/iio/adc/st,stm32-dfsdm-adc.yaml | 189 ++++++------------
arch/arm/boot/dts/st/stm32mp15-pinctrl.dtsi | 39 ++++
arch/arm/boot/dts/st/stm32mp151.dtsi | 18 +-
arch/arm/boot/dts/st/stm32mp157c-ev1.dts | 68 +++++++
drivers/iio/Makefile | 1 +
drivers/iio/adc/sd_adc_modulator.c | 106 ++++++++--
drivers/iio/adc/stm32-dfsdm-adc.c | 187 +++++++++++------
drivers/iio/industrialio-backend.c | 107 ++++++++++
drivers/of/property.c | 2 +
include/linux/iio/backend.h | 56 ++++++
11 files changed, 561 insertions(+), 221 deletions(-)
create mode 100644 drivers/iio/industrialio-backend.c
create mode 100644 include/linux/iio/backend.h
--
2.25.1
Add support for creating device links out of more DT properties.
Signed-off-by: Olivier Moysan <[email protected]>
---
drivers/of/property.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/of/property.c b/drivers/of/property.c
index ddc75cd50825..864e29e4707c 100644
--- a/drivers/of/property.c
+++ b/drivers/of/property.c
@@ -1244,6 +1244,7 @@ DEFINE_SIMPLE_PROP(interconnects, "interconnects", "#interconnect-cells")
DEFINE_SIMPLE_PROP(iommus, "iommus", "#iommu-cells")
DEFINE_SIMPLE_PROP(mboxes, "mboxes", "#mbox-cells")
DEFINE_SIMPLE_PROP(io_channels, "io-channel", "#io-channel-cells")
+DEFINE_SIMPLE_PROP(io_backends, "io-backend", "#io-backend-cells")
DEFINE_SIMPLE_PROP(interrupt_parent, "interrupt-parent", NULL)
DEFINE_SIMPLE_PROP(dmas, "dmas", "#dma-cells")
DEFINE_SIMPLE_PROP(power_domains, "power-domains", "#power-domain-cells")
@@ -1332,6 +1333,7 @@ static const struct supplier_bindings of_supplier_bindings[] = {
{ .parse_prop = parse_iommu_maps, .optional = true, },
{ .parse_prop = parse_mboxes, },
{ .parse_prop = parse_io_channels, },
+ { .parse_prop = parse_io_backends, },
{ .parse_prop = parse_interrupt_parent, },
{ .parse_prop = parse_dmas, .optional = true, },
{ .parse_prop = parse_power_domains, },
--
2.25.1
Register the SD modulator as an IIO backend device instead of
a standard IIO device. Expose scale and offset information to
IIO backend consumer.
Signed-off-by: Olivier Moysan <[email protected]>
---
drivers/iio/adc/sd_adc_modulator.c | 106 +++++++++++++++++++++++------
1 file changed, 86 insertions(+), 20 deletions(-)
diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c
index 327cc2097f6c..e77e7884c403 100644
--- a/drivers/iio/adc/sd_adc_modulator.c
+++ b/drivers/iio/adc/sd_adc_modulator.c
@@ -6,44 +6,110 @@
* Author: Arnaud Pouliquen <[email protected]>.
*/
+#include <linux/iio/backend.h>
#include <linux/iio/iio.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
-static const struct iio_info iio_sd_mod_iio_info;
+struct iio_sd_mod_priv {
+ struct regulator *vref;
+ int vref_mv;
+};
-static const struct iio_chan_spec iio_sd_mod_ch = {
- .type = IIO_VOLTAGE,
- .indexed = 1,
- .scan_type = {
- .sign = 'u',
- .realbits = 1,
- .shift = 0,
- },
+static int sd_mod_enable(struct iio_backend *backend)
+{
+ struct iio_sd_mod_priv *priv = backend->priv;
+ int ret;
+
+ ret = regulator_enable(priv->vref);
+ if (ret) {
+ dev_err(&backend->dev, "Failed to enable regulator: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vref);
+ priv->vref_mv = ret / 1000;
+
+ return 0;
+};
+
+static int sd_mod_disable(struct iio_backend *backend)
+{
+ struct iio_sd_mod_priv *priv = backend->priv;
+
+ return regulator_disable(priv->vref);
+};
+
+static int sd_mod_read(struct iio_backend *backend, int *val, int *val2, long mask)
+{
+ struct iio_sd_mod_priv *priv = backend->priv;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *val = priv->vref_mv;
+ *val2 = 0;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 0;
+ *val2 = 0;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+};
+
+static const struct iio_backend_ops sd_mod_ops = {
+ .enable = sd_mod_enable,
+ .disable = sd_mod_disable,
+ .read_raw = sd_mod_read,
};
static int iio_sd_mod_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct iio_dev *iio;
+ struct regulator *vref;
+ struct iio_backend *backend;
+ struct iio_sd_mod_priv *priv;
+ int ret;
- iio = devm_iio_device_alloc(dev, 0);
- if (!iio)
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
return -ENOMEM;
- iio->name = dev_name(dev);
- iio->info = &iio_sd_mod_iio_info;
- iio->modes = INDIO_BUFFER_HARDWARE;
+ vref = devm_regulator_get_optional(dev, "vref");
+ if (IS_ERR(vref)) {
+ if (PTR_ERR(vref) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(vref), "Failed to get vref\n");
+ } else {
+ priv->vref = vref;
+ }
- iio->num_channels = 1;
- iio->channels = &iio_sd_mod_ch;
+ backend = iio_backend_alloc(dev);
+ if (!backend) {
+ dev_err(dev, "Failed to allocate sd modulator backend data\n");
+ return -ENOMEM;
+ }
+
+ backend->priv = priv;
+ backend->ops = &sd_mod_ops;
+
+ ret = iio_backend_register(backend);
+ if (ret) {
+ dev_err(dev, "Failed to register backend: %d\n", ret);
+ goto err_free_backend;
+ }
- platform_set_drvdata(pdev, iio);
+ return 0;
- return devm_iio_device_register(&pdev->dev, iio);
-}
+err_free_backend:
+ iio_backend_free(&backend->dev);
+
+ return ret;
+};
static const struct of_device_id sd_adc_of_match[] = {
{ .compatible = "sd-modulator" },
--
2.25.1
Add scaling support to STM32 DFSDM.
Signed-off-by: Olivier Moysan <[email protected]>
---
drivers/iio/adc/stm32-dfsdm-adc.c | 77 +++++++++++++++++++++++++++++--
1 file changed, 73 insertions(+), 4 deletions(-)
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 96f4e0c64cdc..dba1a8ef5451 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -9,6 +9,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/iio/adc/stm32-dfsdm-adc.h>
+#include <linux/iio/backend.h>
#include <linux/iio/buffer.h>
#include <linux/iio/hw-consumer.h>
#include <linux/iio/sysfs.h>
@@ -77,6 +78,7 @@ struct stm32_dfsdm_adc {
/* ADC specific */
unsigned int oversamp;
+ struct iio_backend **backend;
struct completion completion;
u32 *buffer;
@@ -600,6 +602,8 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
struct iio_chan_spec *ch)
{
struct stm32_dfsdm_channel *df_ch;
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct iio_backend *backend;
const char *of_str;
int ret, val;
@@ -648,6 +652,12 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
if (ret != -EINVAL)
df_ch->alt_si = 0;
+ backend = fwnode_iio_backend_get(node, 0);
+ if (IS_ERR(backend))
+ return dev_err_probe(&indio_dev->dev, PTR_ERR(backend), "Failed to get backend\n");
+
+ adc->backend[df_ch->id] = backend;
+
return 0;
}
@@ -1091,7 +1101,7 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
long timeout;
- int ret;
+ int ret, idx = chan->scan_index;
reinit_completion(&adc->completion);
@@ -1101,6 +1111,13 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
+ if (!adc->backend[idx]->ops->enable)
+ return -EINVAL;
+
+ ret = adc->backend[idx]->ops->enable(adc->backend[idx]);
+ if (ret < 0)
+ return ret;
+
ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
if (ret < 0)
@@ -1134,6 +1151,8 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
stm32_dfsdm_process_data(adc, res);
stop_dfsdm:
+ ret = adc->backend[idx]->ops->disable(adc->backend[idx]);
+
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
return ret;
@@ -1198,7 +1217,14 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- int ret;
+
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
+ u32 max = flo->max << (flo->lshift - chan->scan_type.shift);
+ int ret, idx = chan->scan_index;
+
+ if (flo->lshift < chan->scan_type.shift)
+ max = flo->max >> (chan->scan_type.shift - flo->lshift);
switch (mask) {
case IIO_CHAN_INFO_RAW:
@@ -1232,6 +1258,41 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
*val = adc->sample_freq;
return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * Scale is expressed in mV.
+ * When fast mode is disabled, actual resolution may be lower
+ * than 2^n, where n=realbits-1.
+ * This leads to underestimating input voltage. To
+ * compensate this deviation, the voltage reference can be
+ * corrected with a factor = realbits resolution / actual max
+ */
+ adc->backend[idx]->ops->read_raw(adc->backend[idx], val, val2, mask);
+
+ *val = div_u64((u64)*val * (u64)BIT(DFSDM_DATA_RES - 1), max);
+ *val2 = chan->scan_type.realbits;
+ if (chan->differential)
+ *val *= 2;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_OFFSET:
+ /*
+ * DFSDM output data are in the range [-2^n,2^n],
+ * with n=realbits-1.
+ * - Differential modulator:
+ * Offset correspond to SD modulator offset.
+ * - Single ended modulator:
+ * Input is in [0V,Vref] range, where 0V corresponds to -2^n, and Vref to 2^n.
+ * Add 2^n to offset. (i.e. middle of input range)
+ * offset = offset(sd) * vref / res(sd) * max / vref.
+ */
+ adc->backend[idx]->ops->read_raw(adc->backend[idx], val, val2, mask);
+
+ *val = div_u64((u64)max * *val, BIT(*val2 - 1));
+ if (!chan->differential)
+ *val += max;
+ return IIO_VAL_INT;
}
return -EINVAL;
@@ -1360,7 +1421,10 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
* IIO_CHAN_INFO_RAW: used to compute regular conversion
* IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
*/
- ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET);
+
ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
BIT(IIO_CHAN_INFO_SAMP_FREQ);
@@ -1451,7 +1515,12 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
if (!ch)
return -ENOMEM;
- stm32_dfsdm_generic_chan_init(indio_dev, adc, ch);
+ adc->backend = devm_kzalloc(&indio_dev->dev, sizeof(*adc->backend) * adc->dfsdm->num_chs,
+ GFP_KERNEL);
+ if (!adc->backend)
+ return -ENOMEM;
+
+ ret = stm32_dfsdm_generic_chan_init(indio_dev, adc, ch);
if (ret < 0)
return ret;
--
2.25.1
This DT is an example of backend iio device used for STM32 DFSDM.
DFSDM filter1 has a single input channel, while filter0 is configured
to support scan mode with two input channels.
Signed-off-by: Olivier Moysan <[email protected]>
---
arch/arm/boot/dts/st/stm32mp157c-ev1.dts | 68 ++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/arch/arm/boot/dts/st/stm32mp157c-ev1.dts b/arch/arm/boot/dts/st/stm32mp157c-ev1.dts
index af3800501875..edeac26f39a4 100644
--- a/arch/arm/boot/dts/st/stm32mp157c-ev1.dts
+++ b/arch/arm/boot/dts/st/stm32mp157c-ev1.dts
@@ -73,6 +73,27 @@ panel_backlight: panel-backlight {
default-on;
status = "okay";
};
+
+ sd_adc0: sd-adc0 {
+ compatible = "sd-modulator";
+ #io-backend-cells = <0>;
+ vref-supply = <&v3v3>;
+ status = "okay";
+ };
+
+ sd_adc1: sd-adc1 {
+ compatible = "sd-modulator";
+ #io-backend-cells = <0>;
+ vref-supply = <&v3v3>;
+ status = "okay";
+ };
+
+ sd_adc2: sd-adc2 {
+ compatible = "sd-modulator";
+ #io-backend-cells = <0>;
+ vref-supply = <&v3v3>;
+ status = "okay";
+ };
};
&cec {
@@ -99,6 +120,53 @@ dcmi_0: endpoint {
};
};
+&dfsdm {
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&dfsdm_clkout_pins_a
+ &dfsdm_data1_pins_a &dfsdm_data3_pins_a>;
+ pinctrl-1 = <&dfsdm_clkout_sleep_pins_a
+ &dfsdm_data1_sleep_pins_a &dfsdm_data3_sleep_pins_a>;
+ spi-max-frequency = <2048000>;
+ status = "okay";
+
+ dfsdm0: filter@0 {
+ compatible = "st,stm32-dfsdm-adc";
+ st,filter-order = <3>;
+ status = "okay";
+
+ channel@0 {
+ reg = <0>;
+ label = "in0";
+ st,adc-channel-types = "SPI_F";
+ st,adc-channel-clk-src = "CLKOUT";
+ st,adc-alt-channel;
+ io-backends = <&sd_adc0>;
+ };
+
+ channel@1 {
+ reg = <1>;
+ label = "in1";
+ st,adc-channel-types = "SPI_R";
+ st,adc-channel-clk-src = "CLKOUT";
+ io-backends = <&sd_adc1>;
+ };
+ };
+
+ dfsdm1: filter@1 {
+ compatible = "st,stm32-dfsdm-adc";
+ st,filter-order = <3>;
+ status = "okay";
+
+ channel@3 {
+ reg = <3>;
+ label = "in3";
+ st,adc-channel-types = "SPI_R";
+ st,adc-channel-clk-src = "CLKOUT";
+ io-backends = <&sd_adc2>;
+ };
+ };
+};
+
&dsi {
phy-dsi-supply = <®18>;
#address-cells = <1>;
--
2.25.1