iio: adc: stm32-adc: add support for STM32MP1
authorFabrice Gasnier <fabrice.gasnier@st.com>
Wed, 2 May 2018 07:44:50 +0000 (09:44 +0200)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Mon, 7 May 2018 17:21:00 +0000 (18:21 +0100)
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 <fabrice.gasnier@st.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/adc/stm32-adc-core.c
drivers/iio/adc/stm32-adc.c

index 40be7d9fadbf58c6afdb897a5386e271fca72a1c..ca432e7b6ff1dbb61f5c30bdd20105cc37cec753 100644 (file)
@@ -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_priv;
  * 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[] = {
@@ -511,6 +538,9 @@ static const struct of_device_id stm32_adc_of_match[] = {
        }, {
                .compatible = "st,stm32h7-adc-core",
                .data = (void *)&stm32h7_adc_priv_cfg
+       }, {
+               .compatible = "st,stm32mp1-adc-core",
+               .data = (void *)&stm32mp1_adc_priv_cfg
        }, {
        },
 };
index 9a2583caedaaefd8c028081ff947c04a5f612496..378411853d7516b1fc3121da560f9e1679583ed4 100644 (file)
@@ -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;
  * @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 const struct stm32_adc_cfg stm32h7_adc_cfg = {
        .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);