2023-06-23 14:22:16

by Olivier MOYSAN

[permalink] [raw]
Subject: [RFC PATCH 0/7] iio: add iio backend device type

This RFC re-opens an old discussion regarding channel scaling
management in STM32 DFSDM driver [1]

The DFSDM is a peripheral provided by the STM32MP1x SoC family.
One objective is also to prepare the introduction of its successor in
the STM32MP12x SoC family: the MDF (Multi-function Digital Filter).
The MDF driver will have the same requirements as the DFSDM regarding
channel scaling management. So, the solution proposed here will apply
also for the future MDF driver.

[1]
https://patchwork.kernel.org/project/linux-iio/patch/[email protected]/

As a short reminder of our previous discussion, the two main options
emerging were the following ones:

- Option1: Use the DFSDM as an hardware accelerator and expose the
scaled channels on SD modulator side.
Drawbak: this solution is leading to an very complex datapath, especially
for scan mode.

- Option2: Introduce a new IIO device type (so-called backend)
Retrieve scaling information from SD modulator scaling to expose a single
IIO device on DFSDM side. This solution is derivated from rcar-gyroadc
example, but with a more standard approach.
This was discussed in
https://lore.kernel.org/lkml/20210919191414.09270f4e@jic23-huawei/

The patchset proposed in this RFC implements option2 (backend) solution.
These patches provide a minimal API implemented as a template.
The intented use of this API is illustrated through the DFSDM channel
scaling support basic implementation.

For sake of simplicity I did not include the related DT binding
in this serie.

Below are some use case examples.

* DFSDM with SD modulator backend:
-------------------------------
This use case corresponds to the example implemented in this RFC.
The channel attributes are retrieved from backend by the dfsdm, and
the resulting scaling is exposed through DFSDM IIO device sysfs

- Single channel:
+-------------+ ch attr +--------+ sysfs (compound scaling)
| sd0 backend | ---------> | dfsdm0 | -------------------------->
+-------------+ +--------+

- Scan mode:
+-------------+ ch attr +-------------+ sysfs (compound scaling)
| sd1 backend | ---------> | dfsdm1 | -------------------------->
+-------------+ +-------------+
^
|
+-------------+ ch attr |
| sd2 backend |--------------+
+-------------+


* Voltage divider in front of an adc:
----------------------------------
By way of example, here is a comparison on scaling management with
a iio-rescale device, and how it could be managed with a backend device.

- iio-rescale implementation
Scaling is exposed both on ADC and iio-rescale IIO devices.
On iio-rescale device we get the compound scaling

+---------------------------+ ch attr +------+ sysfs
| iio-rescale (div) | <--------- | adc0 | ------->
+---------------------------+ +------+
|
| sysfs (compound scaling)
v

- Backend implementation:
Compound scaling is exposed on ADC IIO device.
No scaling exposed on backend device

+---------------+ ch attr +------+ sysfs (compound scaling)
| backend (div) | ---------> | adc0 | -------------------------->
+---------------+ +------+


* Cascaded backends:
-----------------
Backends may be cascaded to allow computation of the whole chain scaling
This is not part of this RFC, but it is identified as a potential
future use case.

+---------------+ attr +-------------+ attr +--------+ sysfs
| backend (div) | ------> | sd0 backend | ------> | dfsdm0 | ------->
+---------------+ +-------------+ +--------+

Olivier Moysan (7):
iio: introduce iio backend device
of: property: add device link support for io-backends
iio: adc: stm32-dfsdm: manage dfsdm as a channel provider
iio: adc: stm32-dfsdm: adopt generic channel bindings
iio: adc: sd_adc_modulator: change to iio backend device
iio: adc: stm32-dfsdm: add scaling support to dfsdm
ARM: dts: stm32: add dfsdm iio suppport

arch/arm/boot/dts/stm32mp157c-ev1.dts | 62 +++++++++
drivers/iio/Makefile | 2 +
drivers/iio/adc/sd_adc_modulator.c | 92 +++++++++++---
drivers/iio/adc/stm32-dfsdm-adc.c | 176 ++++++++++++++++----------
drivers/iio/industrialio-backend.c | 59 +++++++++
drivers/of/property.c | 2 +
include/linux/iio/backend.h | 29 +++++
7 files changed, 336 insertions(+), 86 deletions(-)
create mode 100644 drivers/iio/industrialio-backend.c
create mode 100644 include/linux/iio/backend.h

--
2.25.1



2023-06-23 14:23:29

by Olivier MOYSAN

[permalink] [raw]
Subject: [RFC PATCH 3/7] iio: adc: stm32-dfsdm: manage dfsdm as a channel provider

The STM32 is currently implemented as a channels consumer
of the sigma delta modulator.
Change the topology to expose a single IIO device for DFSDM
and remove the IIO device associated to the SD modulator.
Manage the DFSDM as a channel provider to allow this change.

Signed-off-by: Olivier Moysan <[email protected]>
---
drivers/iio/adc/stm32-dfsdm-adc.c | 19 -------------------
1 file changed, 19 deletions(-)

diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index a428bdb567d5..20756d496c52 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -76,7 +76,6 @@ struct stm32_dfsdm_adc {

/* ADC specific */
unsigned int oversamp;
- struct iio_hw_consumer *hwc;
struct completion completion;
u32 *buffer;

@@ -1006,12 +1005,6 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
/* Reset adc buffer index */
adc->bufi = 0;

- if (adc->hwc) {
- ret = iio_hw_consumer_enable(adc->hwc);
- if (ret < 0)
- return ret;
- }
-
ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
if (ret < 0)
goto err_stop_hwc;
@@ -1035,8 +1028,6 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
stop_dfsdm:
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
err_stop_hwc:
- if (adc->hwc)
- iio_hw_consumer_disable(adc->hwc);

return ret;
}
@@ -1051,9 +1042,6 @@ static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)

stm32_dfsdm_stop_dfsdm(adc->dfsdm);

- if (adc->hwc)
- iio_hw_consumer_disable(adc->hwc);
-
return 0;
}

@@ -1230,7 +1218,6 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
- ret = iio_hw_consumer_enable(adc->hwc);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: IIO enable failed (channel %d)\n",
@@ -1239,7 +1226,6 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
return ret;
}
ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
- iio_hw_consumer_disable(adc->hwc);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: Conversion failed (channel %d)\n",
@@ -1449,11 +1435,6 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
return num_ch < 0 ? num_ch : -EINVAL;
}

- /* Bind to SD modulator IIO device */
- adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev);
- if (IS_ERR(adc->hwc))
- return -EPROBE_DEFER;
-
ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch),
GFP_KERNEL);
if (!ch)
--
2.25.1


2023-06-23 14:23:31

by Olivier MOYSAN

[permalink] [raw]
Subject: [RFC PATCH 1/7] iio: introduce iio backend device

Add a new device type in IIO framework.
This backend device does not compute channel attributes and does not expose
them through sysfs, as done typically in iio-rescale frontend device.
Instead, it allows to report information applying to channel
attributes through callbacks. These backend devices can be cascaded
to represent chained components.
An IIO device configured as a consumer of a backend device can compute
the channel attributes of the whole chain.

Signed-off-by: Olivier Moysan <[email protected]>
---
drivers/iio/Makefile | 2 +
drivers/iio/industrialio-backend.c | 59 ++++++++++++++++++++++++++++++
include/linux/iio/backend.h | 29 +++++++++++++++
3 files changed, 90 insertions(+)
create mode 100644 drivers/iio/industrialio-backend.c
create mode 100644 include/linux/iio/backend.h

diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 9622347a1c1b..b747fcef611d 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -8,6 +8,8 @@ industrialio-y := industrialio-core.o industrialio-event.o inkern.o
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o

+industrialio-$(CONFIG_IIO_BACKEND) += industrialio-backend.o
+
obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
obj-$(CONFIG_IIO_GTS_HELPER) += industrialio-gts-helper.o
obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
new file mode 100644
index 000000000000..6a0f071f8682
--- /dev/null
+++ b/drivers/iio/industrialio-backend.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+/* The industrial I/O core, backend handling functions
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/iio/backend.h>
+
+struct iio_backend *iio_backend_alloc(struct device *parent)
+{
+ struct iio_backend *backend;
+
+ /*
+ * Allocate backend struct
+ * Init backend device & struct
+ */
+
+ return backend;
+};
+EXPORT_SYMBOL(iio_backend_alloc);
+
+int iio_backend_register(struct iio_backend *backend)
+{
+ /*
+ * Add device to device hierarchy
+ * (Call device_add())
+ */
+
+ return 0;
+};
+EXPORT_SYMBOL(iio_backend_register);
+
+void iio_backend_unregister(struct iio_backend *backend)
+{
+ /*
+ * Free backend struct and delete device
+ * (Call device_del())
+ */
+};
+EXPORT_SYMBOL(iio_backend_unregister);
+
+struct iio_backend *iio_backend_get(struct device *dev)
+{
+ struct iio_backend *backend;
+
+ /*
+ * Get backend in from device node
+ * (call fwnode_property_get_reference_args() with io-backends prop)
+ * Allocate backend
+ * Find backend device from phandle
+ * (call bus_find_device_by_fwnode())
+ * Return backend
+ */
+
+ return backend;
+};
+EXPORT_SYMBOL(iio_backend_get);
diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
new file mode 100644
index 000000000000..e089e5e6cef0
--- /dev/null
+++ b/include/linux/iio/backend.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * The industrial I/O core, backend handling functions
+ *
+ * Author: Olivier Moysan <[email protected]>.
+ */
+
+struct iio_backend_ops;
+
+struct iio_backend {
+ const struct iio_backend_ops *ops;
+ struct module *owner;
+ int id;
+ const char *name;
+ struct device dev;
+ struct list_head list;
+ void *priv;
+};
+
+struct iio_backend_ops {
+ int (*enable)(struct iio_backend *backend);
+ int (*disable)(struct iio_backend *backend);
+ int (*read_raw)(struct iio_backend *backend, int *val, int *val2, long mask);
+};
+
+struct iio_backend *iio_backend_alloc(struct device *parent);
+int iio_backend_register(struct iio_backend *backend);
+void iio_backend_unregister(struct iio_backend *backend);
+struct iio_backend *iio_backend_get(struct device *dev);
--
2.25.1


2023-06-23 14:30:59

by Olivier MOYSAN

[permalink] [raw]
Subject: [RFC PATCH 6/7] iio: adc: stm32-dfsdm: add scaling support to dfsdm

Add scaling support to STM32 DFSDM.

Signed-off-by: Olivier Moysan <[email protected]>
---
drivers/iio/adc/sd_adc_modulator.c | 31 ++++++++++++++-
drivers/iio/adc/stm32-dfsdm-adc.c | 64 ++++++++++++++++++++++++++++--
2 files changed, 91 insertions(+), 4 deletions(-)

diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c
index 61246a52dc79..749c46da9c72 100644
--- a/drivers/iio/adc/sd_adc_modulator.c
+++ b/drivers/iio/adc/sd_adc_modulator.c
@@ -12,15 +12,23 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>

struct iio_sd_mod_priv {
+ struct regulator *vref;
+ int vref_mv;
};

static int sd_mod_enable(struct iio_backend *backend)
{
struct iio_sd_mod_priv *priv = backend->priv;
+ int ret;

/* PM resume */
+ ret = regulator_enable(priv->vref);
+
+ ret = regulator_get_voltage(priv->vref);
+ priv->vref_mv = ret / 1000;

return 0;
};
@@ -30,6 +38,7 @@ static int sd_mod_disable(struct iio_backend *backend)
struct iio_sd_mod_priv *priv = backend->priv;

/* PM suspend */
+ regulator_disable(priv->vref);

return 0;
};
@@ -39,7 +48,15 @@ static int sd_mod_read(struct iio_backend *backend, int *val, int *val2, long ma
struct iio_sd_mod_priv *priv = backend->priv;

switch (mask) {
- /* Process channel info */
+ 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;
@@ -54,12 +71,24 @@ static const struct iio_backend_ops sd_mod_ops = {
static int iio_sd_mod_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct regulator *vref;
struct iio_backend *backend;
struct iio_sd_mod_priv *priv;
int ret;

priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);

+ vref = devm_regulator_get_optional(dev, "vref");
+ if (IS_ERR(vref)) {
+ ret = PTR_ERR(vref);
+ if (ret != -ENODEV) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "vref get failed, %d\n", ret);
+ return ret;
+ }
+ }
+ priv->vref = vref;
+
backend = iio_backend_alloc(dev);

backend->priv = priv;
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 2e76497cee51..468d72ab079d 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>
@@ -76,6 +77,7 @@ struct stm32_dfsdm_adc {

/* ADC specific */
unsigned int oversamp;
+ struct iio_backend **backend;
struct completion completion;
u32 *buffer;

@@ -599,6 +601,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;

@@ -647,6 +651,9 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
if (ret != -EINVAL)
df_ch->alt_si = 0;

+ backend = iio_backend_get(node->dev);
+ adc->backend[df_ch->id] = backend;
+
return 0;
}

@@ -1090,7 +1097,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);

@@ -1100,6 +1107,8 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
if (ret < 0)
return ret;

+ adc->backend[idx]->ops->enable(adc->backend[idx]);
+
ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
if (ret < 0)
@@ -1133,6 +1142,8 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
stm32_dfsdm_process_data(adc, res);

stop_dfsdm:
+ adc->backend[idx]->ops->disable(adc->backend[idx]);
+
stm32_dfsdm_stop_dfsdm(adc->dfsdm);

return ret;
@@ -1197,7 +1208,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:
@@ -1231,6 +1249,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-1],
+ * 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.
+ * 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;
@@ -1358,7 +1411,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);

@@ -1454,6 +1510,8 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
if (ret < 0)
return ret;

+ adc->backend = devm_kzalloc(&indio_dev->dev, sizeof(*adc->backend) * num_ch, GFP_KERNEL);
+
indio_dev->num_channels = num_ch;
indio_dev->channels = ch;

--
2.25.1


2023-06-23 14:34:09

by Olivier MOYSAN

[permalink] [raw]
Subject: [RFC PATCH 4/7] iio: adc: stm32-dfsdm: adopt generic channel bindings

Adopt the generic channel bindings to ease the configuration
of the DFSDM channels as consumers of the SD modulator backend device.
Also adopt unified device property API in the same patch for this RFC.

Signed-off-by: Olivier Moysan <[email protected]>
---
drivers/iio/adc/stm32-dfsdm-adc.c | 93 ++++++++++++++++---------------
1 file changed, 49 insertions(+), 44 deletions(-)

diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 20756d496c52..2e76497cee51 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -595,45 +595,35 @@ static int stm32_dfsdm_filter_configure(struct iio_dev *indio_dev,

static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
struct iio_dev *indio_dev,
+ struct fwnode_handle *node,
struct iio_chan_spec *ch)
{
struct stm32_dfsdm_channel *df_ch;
const char *of_str;
- int chan_idx = ch->scan_index;
int ret, val;

- ret = of_property_read_u32_index(indio_dev->dev.of_node,
- "st,adc-channels", chan_idx,
- &ch->channel);
+ ret = fwnode_property_read_u32(node, "reg", &ch->channel);
if (ret < 0) {
- dev_err(&indio_dev->dev,
- " Error parsing 'st,adc-channels' for idx %d\n",
- chan_idx);
+ dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
return ret;
}
if (ch->channel >= dfsdm->num_chs) {
- dev_err(&indio_dev->dev,
- " Error bad channel number %d (max = %d)\n",
+ dev_err(&indio_dev->dev, " Error bad channel number %d (max = %d)\n",
ch->channel, dfsdm->num_chs);
return -EINVAL;
}

- ret = of_property_read_string_index(indio_dev->dev.of_node,
- "st,adc-channel-names", chan_idx,
- &ch->datasheet_name);
+ ret = fwnode_property_read_string(node, "label", &ch->datasheet_name);
if (ret < 0) {
dev_err(&indio_dev->dev,
- " Error parsing 'st,adc-channel-names' for idx %d\n",
- chan_idx);
+ " Error parsing 'label' for idx %d\n", ch->channel);
return ret;
}

df_ch = &dfsdm->ch_list[ch->channel];
df_ch->id = ch->channel;

- ret = of_property_read_string_index(indio_dev->dev.of_node,
- "st,adc-channel-types", chan_idx,
- &of_str);
+ ret = fwnode_property_read_string(node, "st,adc-channel-types", &of_str);
if (!ret) {
val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
if (val < 0)
@@ -643,9 +633,7 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
}
df_ch->type = val;

- ret = of_property_read_string_index(indio_dev->dev.of_node,
- "st,adc-channel-clk-src", chan_idx,
- &of_str);
+ ret = fwnode_property_read_string(node, "st,adc-channel-clk-src", &of_str);
if (!ret) {
val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
if (val < 0)
@@ -655,10 +643,8 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
}
df_ch->src = val;

- ret = of_property_read_u32_index(indio_dev->dev.of_node,
- "st,adc-alt-channel", chan_idx,
- &df_ch->alt_si);
- if (ret < 0)
+ ret = fwnode_property_read_u32(node, "st,adc-alt-channel", &df_ch->alt_si);
+ if (ret != -EINVAL)
df_ch->alt_si = 0;

return 0;
@@ -1353,14 +1339,17 @@ static int stm32_dfsdm_dma_request(struct device *dev,
}

static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+ struct fwnode_handle *child,
struct iio_chan_spec *ch)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int ret;

- ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch);
- if (ret < 0)
+ ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, child, ch);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed to parse channel %d\n", ch->scan_index);
return ret;
+ }

ch->type = IIO_VOLTAGE;
ch->indexed = 1;
@@ -1386,6 +1375,31 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
&adc->dfsdm->ch_list[ch->channel]);
}

+static int stm32_dfsdm_generic_chan_init(struct iio_dev *indio_dev, struct stm32_dfsdm_adc *adc,
+ struct iio_chan_spec *channels)
+{
+ struct fwnode_handle *child;
+ int chan_idx = 0, ret;
+
+ device_for_each_child_node(&indio_dev->dev, child) {
+ channels[chan_idx].scan_index = chan_idx;
+ ret = stm32_dfsdm_adc_chan_init_one(indio_dev, child, &channels[chan_idx]);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Channels init failed\n");
+ goto err;
+ }
+
+ chan_idx++;
+ }
+
+ return chan_idx;
+
+err:
+ fwnode_handle_put(child);
+
+ return ret;
+}
+
static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev)
{
struct iio_chan_spec *ch;
@@ -1399,7 +1413,7 @@ static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev)

ch->scan_index = 0;

- ret = stm32_dfsdm_adc_chan_init_one(indio_dev, ch);
+ stm32_dfsdm_generic_chan_init(indio_dev, adc, ch);
if (ret < 0) {
dev_err(&indio_dev->dev, "Channels init failed\n");
return ret;
@@ -1421,33 +1435,24 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
struct iio_chan_spec *ch;
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int num_ch;
- int ret, chan_idx;
+ int ret;

adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING;
ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp);
if (ret < 0)
return ret;

- num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
- "st,adc-channels");
- if (num_ch < 0 || num_ch > adc->dfsdm->num_chs) {
- dev_err(&indio_dev->dev, "Bad st,adc-channels\n");
- return num_ch < 0 ? num_ch : -EINVAL;
- }
+ num_ch = device_get_child_node_count(&indio_dev->dev);
+ if (!num_ch)
+ return -EINVAL;

- ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch),
- GFP_KERNEL);
+ ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch), GFP_KERNEL);
if (!ch)
return -ENOMEM;

- for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
- ch[chan_idx].scan_index = chan_idx;
- ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &ch[chan_idx]);
- if (ret < 0) {
- dev_err(&indio_dev->dev, "Channels init failed\n");
- return ret;
- }
- }
+ stm32_dfsdm_generic_chan_init(indio_dev, adc, ch);
+ if (ret < 0)
+ return ret;

indio_dev->num_channels = num_ch;
indio_dev->channels = ch;
--
2.25.1


2023-06-23 14:44:03

by Olivier MOYSAN

[permalink] [raw]
Subject: [RFC PATCH 7/7] ARM: dts: stm32: add dfsdm iio suppport

This DT is an example of backend iio device use for STM32 DFSDM.
DFSDM filter0 has a single input channel, while filter1 is configured
for scan mode with two input channels.

Signed-off-by: Olivier Moysan <[email protected]>
---
arch/arm/boot/dts/stm32mp157c-ev1.dts | 62 +++++++++++++++++++++++++++
1 file changed, 62 insertions(+)

diff --git a/arch/arm/boot/dts/stm32mp157c-ev1.dts b/arch/arm/boot/dts/stm32mp157c-ev1.dts
index ba8e9d9a42fa..ebd67a219df2 100644
--- a/arch/arm/boot/dts/stm32mp157c-ev1.dts
+++ b/arch/arm/boot/dts/stm32mp157c-ev1.dts
@@ -73,6 +73,24 @@ panel_backlight: panel-backlight {
default-on;
status = "okay";
};
+
+ sd_adc0: simple-sd-adc0 {
+ compatible = "sd-modulator";
+ io-backend-cells = <0>;
+ vref-supply = <&v3v3>;
+ };
+
+ sd_adc1: simple-sd-adc1 {
+ compatible = "sd-modulator";
+ io-backend-cells = <0>;
+ vref-supply = <&v3v3>;
+ };
+
+ sd_adc2: simple-sd-adc2 {
+ compatible = "sd-modulator";
+ io-backend-cells = <0>;
+ vref-supply = <&v3v3>;
+ };
};

&cec {
@@ -99,6 +117,50 @@ dcmi_0: endpoint {
};
};

+&dfsdm {
+ spi-max-frequency = <2048000>;
+
+ clocks = <&rcc DFSDM_K>, <&rcc ADFSDM_K>;
+ clock-names = "dfsdm", "audio";
+ status = "disabled";
+
+ dfsdm0: filter@0 {
+ compatible = "st,stm32-dfsdm-adc";
+ st,filter-order = <3>;
+ status = "okay";
+
+ channel@1 {
+ reg = <1>;
+ label = "in1";
+ st,adc-channel-types = "SPI_R";
+ st,adc-channel-clk-src = "CLKOUT";
+ io-backend = <&sd_adc0>;
+ };
+ };
+
+ dfsdm1: filter@1 {
+ compatible = "st,stm32-dfsdm-adc";
+ st,filter-order = <3>;
+ status = "okay";
+
+ channel@2 {
+ reg = <2>;
+ label = "in2";
+ st,adc-channel-types = "SPI_R";
+ st,adc-channel-clk-src = "CLKOUT";
+ io-backend = <&sd_adc1>;
+ };
+
+ channel@3 {
+ reg = <3>;
+ label = "in3";
+ st,adc-channel-types = "SPI_F";
+ st,adc-channel-clk-src = "CLKOUT";
+ io-backend = <&sd_adc2>;
+ };
+ };
+};
+
&dsi {
phy-dsi-supply = <&reg18>;
status = "okay";
--
2.25.1