Add support for STM32MP1 Analog to Digital Converter variant.
It's quite similar to STM32H7 ADC and re-use most of existing driver.
---
Changes in v2:
- Update dt-bindings following Rob's remark: STM32MP1 ADC has two
interrupt lines
Fabrice Gasnier (3):
dt-bindings: iio: stm32-adc: add support for STM32MP1
iio: adc: stm32-adc: add support for STM32MP1
ARM: dts: stm32: Add ADC support to stm32mp157c
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 6 +-
arch/arm/boot/dts/stm32mp157c.dtsi | 32 +++++++++++
drivers/iio/adc/stm32-adc-core.c | 66 ++++++++++++++++------
drivers/iio/adc/stm32-adc.c | 47 +++++++++++++--
4 files changed, 128 insertions(+), 23 deletions(-)
--
1.9.1
Add support for STM32MP1 ADC. It's quite similar to STM32H7 ADC.
Introduce new compatible to handle variants of this hardware such as
vregready flag, trigger list, interrupts, clock rate.
Signed-off-by: Fabrice Gasnier <[email protected]>
---
drivers/iio/adc/stm32-adc-core.c | 66 +++++++++++++++++++++++++++++-----------
drivers/iio/adc/stm32-adc.c | 47 +++++++++++++++++++++++++---
2 files changed, 91 insertions(+), 22 deletions(-)
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index 40be7d9..ca432e7 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -34,9 +34,6 @@
#define STM32F4_ADC_ADCPRE_SHIFT 16
#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
-/* STM32 F4 maximum analog clock rate (from datasheet) */
-#define STM32F4_ADC_MAX_CLK_RATE 36000000
-
/* STM32H7 - common registers for all ADC instances */
#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
@@ -51,9 +48,6 @@
#define STM32H7_CKMODE_SHIFT 16
#define STM32H7_CKMODE_MASK GENMASK(17, 16)
-/* STM32 H7 maximum analog clock rate (from datasheet) */
-#define STM32H7_ADC_MAX_CLK_RATE 36000000
-
/**
* stm32_adc_common_regs - stm32 common registers, compatible dependent data
* @csr: common status register offset
@@ -74,15 +68,17 @@ struct stm32_adc_common_regs {
* stm32_adc_priv_cfg - stm32 core compatible configuration data
* @regs: common registers for all instances
* @clk_sel: clock selection routine
+ * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
*/
struct stm32_adc_priv_cfg {
const struct stm32_adc_common_regs *regs;
int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
+ u32 max_clk_rate_hz;
};
/**
* struct stm32_adc_priv - stm32 ADC core private data
- * @irq: irq for ADC block
+ * @irq: irq(s) for ADC block
* @domain: irq domain reference
* @aclk: clock reference for the analog circuitry
* @bclk: bus clock common for all ADCs, depends on part used
@@ -91,7 +87,7 @@ struct stm32_adc_priv_cfg {
* @common: common data for all ADC instances
*/
struct stm32_adc_priv {
- int irq;
+ int irq[STM32_ADC_MAX_ADCS];
struct irq_domain *domain;
struct clk *aclk;
struct clk *bclk;
@@ -133,7 +129,7 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
}
for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
- if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
+ if ((rate / stm32f4_pclk_div[i]) <= priv->cfg->max_clk_rate_hz)
break;
}
if (i >= ARRAY_SIZE(stm32f4_pclk_div)) {
@@ -222,7 +218,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
if (ckmode)
continue;
- if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
+ if ((rate / div) <= priv->cfg->max_clk_rate_hz)
goto out;
}
}
@@ -242,7 +238,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
if (!ckmode)
continue;
- if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
+ if ((rate / div) <= priv->cfg->max_clk_rate_hz)
goto out;
}
@@ -328,11 +324,24 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
struct stm32_adc_priv *priv)
{
struct device_node *np = pdev->dev.of_node;
+ unsigned int i;
+
+ for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
+ priv->irq[i] = platform_get_irq(pdev, i);
+ if (priv->irq[i] < 0) {
+ /*
+ * At least one interrupt must be provided, make others
+ * optional:
+ * - stm32f4/h7 shares a common interrupt.
+ * - stm32mp1, has one line per ADC (either for ADC1,
+ * ADC2 or both).
+ */
+ if (i && priv->irq[i] == -ENXIO)
+ continue;
+ dev_err(&pdev->dev, "failed to get irq\n");
- priv->irq = platform_get_irq(pdev, 0);
- if (priv->irq < 0) {
- dev_err(&pdev->dev, "failed to get irq\n");
- return priv->irq;
+ return priv->irq[i];
+ }
}
priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
@@ -343,8 +352,12 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
return -ENOMEM;
}
- irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
- irq_set_handler_data(priv->irq, priv);
+ for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
+ if (priv->irq[i] < 0)
+ continue;
+ irq_set_chained_handler(priv->irq[i], stm32_adc_irq_handler);
+ irq_set_handler_data(priv->irq[i], priv);
+ }
return 0;
}
@@ -353,11 +366,17 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
struct stm32_adc_priv *priv)
{
int hwirq;
+ unsigned int i;
for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
irq_domain_remove(priv->domain);
- irq_set_chained_handler(priv->irq, NULL);
+
+ for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
+ if (priv->irq[i] < 0)
+ continue;
+ irq_set_chained_handler(priv->irq[i], NULL);
+ }
}
static int stm32_adc_probe(struct platform_device *pdev)
@@ -497,11 +516,19 @@ static int stm32_adc_remove(struct platform_device *pdev)
static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
.regs = &stm32f4_adc_common_regs,
.clk_sel = stm32f4_adc_clk_sel,
+ .max_clk_rate_hz = 36000000,
};
static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
.regs = &stm32h7_adc_common_regs,
.clk_sel = stm32h7_adc_clk_sel,
+ .max_clk_rate_hz = 36000000,
+};
+
+static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
+ .regs = &stm32h7_adc_common_regs,
+ .clk_sel = stm32h7_adc_clk_sel,
+ .max_clk_rate_hz = 40000000,
};
static const struct of_device_id stm32_adc_of_match[] = {
@@ -512,6 +539,9 @@ static int stm32_adc_remove(struct platform_device *pdev)
.compatible = "st,stm32h7-adc-core",
.data = (void *)&stm32h7_adc_priv_cfg
}, {
+ .compatible = "st,stm32mp1-adc-core",
+ .data = (void *)&stm32mp1_adc_priv_cfg
+ }, {
},
};
MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 9a2583ca..3784118 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -84,6 +84,7 @@
#define STM32H7_ADC_CALFACT2 0xC8
/* STM32H7_ADC_ISR - bit fields */
+#define STM32MP1_VREGREADY BIT(12)
#define STM32H7_EOC BIT(2)
#define STM32H7_ADRDY BIT(0)
@@ -249,6 +250,7 @@ struct stm32_adc_regspec {
* @adc_info: per instance input channels definitions
* @trigs: external trigger sources
* @clk_required: clock is required
+ * @has_vregready: vregready status flag presence
* @selfcalib: optional routine for self-calibration
* @prepare: optional prepare routine (power-up, enable)
* @start_conv: routine to start conversions
@@ -261,6 +263,7 @@ struct stm32_adc_cfg {
const struct stm32_adc_info *adc_info;
struct stm32_adc_trig_info *trigs;
bool clk_required;
+ bool has_vregready;
int (*selfcalib)(struct stm32_adc *);
int (*prepare)(struct stm32_adc *);
void (*start_conv)(struct stm32_adc *, bool dma);
@@ -695,8 +698,12 @@ static void stm32h7_adc_stop_conv(struct stm32_adc *adc)
stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK);
}
-static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
+static int stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ int ret;
+ u32 val;
+
/* Exit deep power down, then enable ADC voltage regulator */
stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN);
@@ -705,7 +712,20 @@ static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
/* Wait for startup time */
- usleep_range(10, 20);
+ if (!adc->cfg->has_vregready) {
+ usleep_range(10, 20);
+ return 0;
+ }
+
+ ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val,
+ val & STM32MP1_VREGREADY, 100,
+ STM32_ADC_TIMEOUT_US);
+ if (ret) {
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
+ dev_err(&indio_dev->dev, "Failed to exit power down\n");
+ }
+
+ return ret;
}
static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc)
@@ -888,7 +908,9 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
int ret;
u32 val;
- stm32h7_adc_exit_pwr_down(adc);
+ ret = stm32h7_adc_exit_pwr_down(adc);
+ if (ret)
+ return ret;
/*
* Select calibration mode:
@@ -952,7 +974,10 @@ static int stm32h7_adc_prepare(struct stm32_adc *adc)
{
int ret;
- stm32h7_adc_exit_pwr_down(adc);
+ ret = stm32h7_adc_exit_pwr_down(adc);
+ if (ret)
+ return ret;
+
stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
ret = stm32h7_adc_enable(adc);
@@ -1944,9 +1969,23 @@ static int stm32_adc_remove(struct platform_device *pdev)
.smp_cycles = stm32h7_adc_smp_cycles,
};
+static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
+ .regs = &stm32h7_adc_regspec,
+ .adc_info = &stm32h7_adc_info,
+ .trigs = stm32h7_adc_trigs,
+ .has_vregready = true,
+ .selfcalib = stm32h7_adc_selfcalib,
+ .start_conv = stm32h7_adc_start_conv,
+ .stop_conv = stm32h7_adc_stop_conv,
+ .prepare = stm32h7_adc_prepare,
+ .unprepare = stm32h7_adc_unprepare,
+ .smp_cycles = stm32h7_adc_smp_cycles,
+};
+
static const struct of_device_id stm32_adc_of_match[] = {
{ .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg },
{ .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg },
+ { .compatible = "st,stm32mp1-adc", .data = (void *)&stm32mp1_adc_cfg },
{},
};
MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
--
1.9.1
Document support for STM32MP1 ADC. It's quite similar to STM32H7 ADC.
Introduce "st,stm32mp1-adc" compatible to handle variants of this
hardware such as vregready flag, interrupts, clock rate.
Signed-off-by: Fabrice Gasnier <[email protected]>
---
Changes in v2:
- Update dt-bindings following Rob's remark: STM32MP1 ADC has two
interrupt lines
---
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
index e8bb824..f1ead43 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -24,8 +24,11 @@ Required properties:
- compatible: Should be one of:
"st,stm32f4-adc-core"
"st,stm32h7-adc-core"
+ "st,stm32mp1-adc-core"
- reg: Offset and length of the ADC block register set.
-- interrupts: Must contain the interrupt for ADC block.
+- interrupts: One or more interrupts for ADC block. Some parts like stm32f4
+ and stm32h7 share a common ADC interrupt line. stm32mp1 has two separate
+ interrupt lines, one for each ADC within ADC block.
- clocks: Core can use up to two clocks, depending on part used:
- "adc" clock: for the analog circuitry, common to all ADCs.
It's required on stm32f4.
@@ -53,6 +56,7 @@ Required properties:
- compatible: Should be one of:
"st,stm32f4-adc"
"st,stm32h7-adc"
+ "st,stm32mp1-adc"
- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
- clocks: Input clock private to this ADC instance. It's required only on
stm32f4, that has per instance clock input for registers access.
--
1.9.1
stm32mp157c has an ADC block with two physical ADCs.
Signed-off-by: Fabrice Gasnier <[email protected]>
---
arch/arm/boot/dts/stm32mp157c.dtsi | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
index bc3eddc..7758a90 100644
--- a/arch/arm/boot/dts/stm32mp157c.dtsi
+++ b/arch/arm/boot/dts/stm32mp157c.dtsi
@@ -160,6 +160,38 @@
status = "disabled";
};
+ adc: adc@48003000 {
+ compatible = "st,stm32mp1-adc-core";
+ reg = <0x48003000 0x400>;
+ interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rcc ADC12>, <&rcc ADC12_K>;
+ clock-names = "bus", "adc";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+
+ adc1: adc@0 {
+ compatible = "st,stm32mp1-adc";
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ interrupt-parent = <&adc>;
+ interrupts = <0>;
+ status = "disabled";
+ };
+
+ adc2: adc@100 {
+ compatible = "st,stm32mp1-adc";
+ #io-channel-cells = <1>;
+ reg = <0x100>;
+ interrupt-parent = <&adc>;
+ interrupts = <1>;
+ status = "disabled";
+ };
+ };
+
rcc: rcc@50000000 {
compatible = "st,stm32mp1-rcc", "syscon";
reg = <0x50000000 0x1000>;
--
1.9.1
On Wed, 2 May 2018 09:44:48 +0200
Fabrice Gasnier <[email protected]> wrote:
> Add support for STM32MP1 Analog to Digital Converter variant.
> It's quite similar to STM32H7 ADC and re-use most of existing driver.
This series looks good to me. I just want to give Rob in particular
time to have another look if he likes.
Give me a poke if I seem to have forgotten it in a week or so.
Thanks,
Jonathan
>
> ---
> Changes in v2:
> - Update dt-bindings following Rob's remark: STM32MP1 ADC has two
> interrupt lines
>
> Fabrice Gasnier (3):
> dt-bindings: iio: stm32-adc: add support for STM32MP1
> iio: adc: stm32-adc: add support for STM32MP1
> ARM: dts: stm32: Add ADC support to stm32mp157c
>
> .../devicetree/bindings/iio/adc/st,stm32-adc.txt | 6 +-
> arch/arm/boot/dts/stm32mp157c.dtsi | 32 +++++++++++
> drivers/iio/adc/stm32-adc-core.c | 66 ++++++++++++++++------
> drivers/iio/adc/stm32-adc.c | 47 +++++++++++++--
> 4 files changed, 128 insertions(+), 23 deletions(-)
>
On Wed, May 02, 2018 at 09:44:49AM +0200, Fabrice Gasnier wrote:
> Document support for STM32MP1 ADC. It's quite similar to STM32H7 ADC.
> Introduce "st,stm32mp1-adc" compatible to handle variants of this
> hardware such as vregready flag, interrupts, clock rate.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> ---
> Changes in v2:
> - Update dt-bindings following Rob's remark: STM32MP1 ADC has two
> interrupt lines
> ---
> Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
Reviewed-by: Rob Herring <[email protected]>
On Mon, 7 May 2018 11:28:31 -0500
Rob Herring <[email protected]> wrote:
> On Wed, May 02, 2018 at 09:44:49AM +0200, Fabrice Gasnier wrote:
> > Document support for STM32MP1 ADC. It's quite similar to STM32H7 ADC.
> > Introduce "st,stm32mp1-adc" compatible to handle variants of this
> > hardware such as vregready flag, interrupts, clock rate.
> >
> > Signed-off-by: Fabrice Gasnier <[email protected]>
> > ---
> > Changes in v2:
> > - Update dt-bindings following Rob's remark: STM32MP1 ADC has two
> > interrupt lines
> > ---
> > Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt | 6 +++++-
> > 1 file changed, 5 insertions(+), 1 deletion(-)
>
> Reviewed-by: Rob Herring <[email protected]>
>
Thanks. Applied to the togreg branch of iio.git and pushed out as
testing for the autobuilders to play with it.
Jonathan
On Wed, 2 May 2018 09:44:50 +0200
Fabrice Gasnier <[email protected]> wrote:
> Add support for STM32MP1 ADC. It's quite similar to STM32H7 ADC.
> Introduce new compatible to handle variants of this hardware such as
> vregready flag, trigger list, interrupts, clock rate.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Applied to the togreg branch of iio.git and pushed out as testing for the
autobuilders to play with it.
Thanks,
Jonathan
> ---
> drivers/iio/adc/stm32-adc-core.c | 66 +++++++++++++++++++++++++++++-----------
> drivers/iio/adc/stm32-adc.c | 47 +++++++++++++++++++++++++---
> 2 files changed, 91 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
> index 40be7d9..ca432e7 100644
> --- a/drivers/iio/adc/stm32-adc-core.c
> +++ b/drivers/iio/adc/stm32-adc-core.c
> @@ -34,9 +34,6 @@
> #define STM32F4_ADC_ADCPRE_SHIFT 16
> #define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
>
> -/* STM32 F4 maximum analog clock rate (from datasheet) */
> -#define STM32F4_ADC_MAX_CLK_RATE 36000000
> -
> /* STM32H7 - common registers for all ADC instances */
> #define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
> #define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
> @@ -51,9 +48,6 @@
> #define STM32H7_CKMODE_SHIFT 16
> #define STM32H7_CKMODE_MASK GENMASK(17, 16)
>
> -/* STM32 H7 maximum analog clock rate (from datasheet) */
> -#define STM32H7_ADC_MAX_CLK_RATE 36000000
> -
> /**
> * stm32_adc_common_regs - stm32 common registers, compatible dependent data
> * @csr: common status register offset
> @@ -74,15 +68,17 @@ struct stm32_adc_common_regs {
> * stm32_adc_priv_cfg - stm32 core compatible configuration data
> * @regs: common registers for all instances
> * @clk_sel: clock selection routine
> + * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
> */
> struct stm32_adc_priv_cfg {
> const struct stm32_adc_common_regs *regs;
> int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
> + u32 max_clk_rate_hz;
> };
>
> /**
> * struct stm32_adc_priv - stm32 ADC core private data
> - * @irq: irq for ADC block
> + * @irq: irq(s) for ADC block
> * @domain: irq domain reference
> * @aclk: clock reference for the analog circuitry
> * @bclk: bus clock common for all ADCs, depends on part used
> @@ -91,7 +87,7 @@ struct stm32_adc_priv_cfg {
> * @common: common data for all ADC instances
> */
> struct stm32_adc_priv {
> - int irq;
> + int irq[STM32_ADC_MAX_ADCS];
> struct irq_domain *domain;
> struct clk *aclk;
> struct clk *bclk;
> @@ -133,7 +129,7 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
> }
>
> for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
> - if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
> + if ((rate / stm32f4_pclk_div[i]) <= priv->cfg->max_clk_rate_hz)
> break;
> }
> if (i >= ARRAY_SIZE(stm32f4_pclk_div)) {
> @@ -222,7 +218,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
> if (ckmode)
> continue;
>
> - if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
> + if ((rate / div) <= priv->cfg->max_clk_rate_hz)
> goto out;
> }
> }
> @@ -242,7 +238,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
> if (!ckmode)
> continue;
>
> - if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
> + if ((rate / div) <= priv->cfg->max_clk_rate_hz)
> goto out;
> }
>
> @@ -328,11 +324,24 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
> struct stm32_adc_priv *priv)
> {
> struct device_node *np = pdev->dev.of_node;
> + unsigned int i;
> +
> + for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
> + priv->irq[i] = platform_get_irq(pdev, i);
> + if (priv->irq[i] < 0) {
> + /*
> + * At least one interrupt must be provided, make others
> + * optional:
> + * - stm32f4/h7 shares a common interrupt.
> + * - stm32mp1, has one line per ADC (either for ADC1,
> + * ADC2 or both).
> + */
> + if (i && priv->irq[i] == -ENXIO)
> + continue;
> + dev_err(&pdev->dev, "failed to get irq\n");
>
> - priv->irq = platform_get_irq(pdev, 0);
> - if (priv->irq < 0) {
> - dev_err(&pdev->dev, "failed to get irq\n");
> - return priv->irq;
> + return priv->irq[i];
> + }
> }
>
> priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
> @@ -343,8 +352,12 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
> return -ENOMEM;
> }
>
> - irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
> - irq_set_handler_data(priv->irq, priv);
> + for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
> + if (priv->irq[i] < 0)
> + continue;
> + irq_set_chained_handler(priv->irq[i], stm32_adc_irq_handler);
> + irq_set_handler_data(priv->irq[i], priv);
> + }
>
> return 0;
> }
> @@ -353,11 +366,17 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
> struct stm32_adc_priv *priv)
> {
> int hwirq;
> + unsigned int i;
>
> for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
> irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
> irq_domain_remove(priv->domain);
> - irq_set_chained_handler(priv->irq, NULL);
> +
> + for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
> + if (priv->irq[i] < 0)
> + continue;
> + irq_set_chained_handler(priv->irq[i], NULL);
> + }
> }
>
> static int stm32_adc_probe(struct platform_device *pdev)
> @@ -497,11 +516,19 @@ static int stm32_adc_remove(struct platform_device *pdev)
> static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
> .regs = &stm32f4_adc_common_regs,
> .clk_sel = stm32f4_adc_clk_sel,
> + .max_clk_rate_hz = 36000000,
> };
>
> static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
> .regs = &stm32h7_adc_common_regs,
> .clk_sel = stm32h7_adc_clk_sel,
> + .max_clk_rate_hz = 36000000,
> +};
> +
> +static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
> + .regs = &stm32h7_adc_common_regs,
> + .clk_sel = stm32h7_adc_clk_sel,
> + .max_clk_rate_hz = 40000000,
> };
>
> static const struct of_device_id stm32_adc_of_match[] = {
> @@ -512,6 +539,9 @@ static int stm32_adc_remove(struct platform_device *pdev)
> .compatible = "st,stm32h7-adc-core",
> .data = (void *)&stm32h7_adc_priv_cfg
> }, {
> + .compatible = "st,stm32mp1-adc-core",
> + .data = (void *)&stm32mp1_adc_priv_cfg
> + }, {
> },
> };
> MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index 9a2583ca..3784118 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -84,6 +84,7 @@
> #define STM32H7_ADC_CALFACT2 0xC8
>
> /* STM32H7_ADC_ISR - bit fields */
> +#define STM32MP1_VREGREADY BIT(12)
> #define STM32H7_EOC BIT(2)
> #define STM32H7_ADRDY BIT(0)
>
> @@ -249,6 +250,7 @@ struct stm32_adc_regspec {
> * @adc_info: per instance input channels definitions
> * @trigs: external trigger sources
> * @clk_required: clock is required
> + * @has_vregready: vregready status flag presence
> * @selfcalib: optional routine for self-calibration
> * @prepare: optional prepare routine (power-up, enable)
> * @start_conv: routine to start conversions
> @@ -261,6 +263,7 @@ struct stm32_adc_cfg {
> const struct stm32_adc_info *adc_info;
> struct stm32_adc_trig_info *trigs;
> bool clk_required;
> + bool has_vregready;
> int (*selfcalib)(struct stm32_adc *);
> int (*prepare)(struct stm32_adc *);
> void (*start_conv)(struct stm32_adc *, bool dma);
> @@ -695,8 +698,12 @@ static void stm32h7_adc_stop_conv(struct stm32_adc *adc)
> stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK);
> }
>
> -static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
> +static int stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
> {
> + struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> + int ret;
> + u32 val;
> +
> /* Exit deep power down, then enable ADC voltage regulator */
> stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
> stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN);
> @@ -705,7 +712,20 @@ static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
> stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
>
> /* Wait for startup time */
> - usleep_range(10, 20);
> + if (!adc->cfg->has_vregready) {
> + usleep_range(10, 20);
> + return 0;
> + }
> +
> + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val,
> + val & STM32MP1_VREGREADY, 100,
> + STM32_ADC_TIMEOUT_US);
> + if (ret) {
> + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
> + dev_err(&indio_dev->dev, "Failed to exit power down\n");
> + }
> +
> + return ret;
> }
>
> static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc)
> @@ -888,7 +908,9 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
> int ret;
> u32 val;
>
> - stm32h7_adc_exit_pwr_down(adc);
> + ret = stm32h7_adc_exit_pwr_down(adc);
> + if (ret)
> + return ret;
>
> /*
> * Select calibration mode:
> @@ -952,7 +974,10 @@ static int stm32h7_adc_prepare(struct stm32_adc *adc)
> {
> int ret;
>
> - stm32h7_adc_exit_pwr_down(adc);
> + ret = stm32h7_adc_exit_pwr_down(adc);
> + if (ret)
> + return ret;
> +
> stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
>
> ret = stm32h7_adc_enable(adc);
> @@ -1944,9 +1969,23 @@ static int stm32_adc_remove(struct platform_device *pdev)
> .smp_cycles = stm32h7_adc_smp_cycles,
> };
>
> +static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
> + .regs = &stm32h7_adc_regspec,
> + .adc_info = &stm32h7_adc_info,
> + .trigs = stm32h7_adc_trigs,
> + .has_vregready = true,
> + .selfcalib = stm32h7_adc_selfcalib,
> + .start_conv = stm32h7_adc_start_conv,
> + .stop_conv = stm32h7_adc_stop_conv,
> + .prepare = stm32h7_adc_prepare,
> + .unprepare = stm32h7_adc_unprepare,
> + .smp_cycles = stm32h7_adc_smp_cycles,
> +};
> +
> static const struct of_device_id stm32_adc_of_match[] = {
> { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg },
> { .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg },
> + { .compatible = "st,stm32mp1-adc", .data = (void *)&stm32mp1_adc_cfg },
> {},
> };
> MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
On Wed, 2 May 2018 09:44:51 +0200
Fabrice Gasnier <[email protected]> wrote:
> stm32mp157c has an ADC block with two physical ADCs.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
The driver support is now queued up in the IIO tree and should
be in Linux next later this week.
Thanks,
Jonathan
> ---
> arch/arm/boot/dts/stm32mp157c.dtsi | 32 ++++++++++++++++++++++++++++++++
> 1 file changed, 32 insertions(+)
>
> diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
> index bc3eddc..7758a90 100644
> --- a/arch/arm/boot/dts/stm32mp157c.dtsi
> +++ b/arch/arm/boot/dts/stm32mp157c.dtsi
> @@ -160,6 +160,38 @@
> status = "disabled";
> };
>
> + adc: adc@48003000 {
> + compatible = "st,stm32mp1-adc-core";
> + reg = <0x48003000 0x400>;
> + interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&rcc ADC12>, <&rcc ADC12_K>;
> + clock-names = "bus", "adc";
> + interrupt-controller;
> + #interrupt-cells = <1>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> + status = "disabled";
> +
> + adc1: adc@0 {
> + compatible = "st,stm32mp1-adc";
> + #io-channel-cells = <1>;
> + reg = <0x0>;
> + interrupt-parent = <&adc>;
> + interrupts = <0>;
> + status = "disabled";
> + };
> +
> + adc2: adc@100 {
> + compatible = "st,stm32mp1-adc";
> + #io-channel-cells = <1>;
> + reg = <0x100>;
> + interrupt-parent = <&adc>;
> + interrupts = <1>;
> + status = "disabled";
> + };
> + };
> +
> rcc: rcc@50000000 {
> compatible = "st,stm32mp1-rcc", "syscon";
> reg = <0x50000000 0x1000>;
On 05/07/2018 07:23 PM, Jonathan Cameron wrote:
> On Wed, 2 May 2018 09:44:51 +0200
> Fabrice Gasnier <[email protected]> wrote:
>
>> stm32mp157c has an ADC block with two physical ADCs.
>>
>> Signed-off-by: Fabrice Gasnier <[email protected]>
> The driver support is now queued up in the IIO tree and should
> be in Linux next later this week.
Hi,
Many Thanks Jonathan :-)
Alex, I just sent an updated version (v3) of this patch (with additional
dmas).
Best Regards,
Fabrice
>
> Thanks,
>
> Jonathan
>
>> ---
>> arch/arm/boot/dts/stm32mp157c.dtsi | 32 ++++++++++++++++++++++++++++++++
>> 1 file changed, 32 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
>> index bc3eddc..7758a90 100644
>> --- a/arch/arm/boot/dts/stm32mp157c.dtsi
>> +++ b/arch/arm/boot/dts/stm32mp157c.dtsi
>> @@ -160,6 +160,38 @@
>> status = "disabled";
>> };
>>
>> + adc: adc@48003000 {
>> + compatible = "st,stm32mp1-adc-core";
>> + reg = <0x48003000 0x400>;
>> + interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
>> + <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
>> + clocks = <&rcc ADC12>, <&rcc ADC12_K>;
>> + clock-names = "bus", "adc";
>> + interrupt-controller;
>> + #interrupt-cells = <1>;
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + status = "disabled";
>> +
>> + adc1: adc@0 {
>> + compatible = "st,stm32mp1-adc";
>> + #io-channel-cells = <1>;
>> + reg = <0x0>;
>> + interrupt-parent = <&adc>;
>> + interrupts = <0>;
>> + status = "disabled";
>> + };
>> +
>> + adc2: adc@100 {
>> + compatible = "st,stm32mp1-adc";
>> + #io-channel-cells = <1>;
>> + reg = <0x100>;
>> + interrupt-parent = <&adc>;
>> + interrupts = <1>;
>> + status = "disabled";
>> + };
>> + };
>> +
>> rcc: rcc@50000000 {
>> compatible = "st,stm32mp1-rcc", "syscon";
>> reg = <0x50000000 0x1000>;
>