summaryrefslogtreecommitdiff
path: root/drivers/adc/stm32-adc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/adc/stm32-adc.c')
-rw-r--r--drivers/adc/stm32-adc.c88
1 files changed, 74 insertions, 14 deletions
diff --git a/drivers/adc/stm32-adc.c b/drivers/adc/stm32-adc.c
index d50f00f1233..b11f771b71c 100644
--- a/drivers/adc/stm32-adc.c
+++ b/drivers/adc/stm32-adc.c
@@ -49,29 +49,68 @@
/* STM32H7_ADC_SQR1 - bit fields */
#define STM32H7_SQ1_SHIFT 6
+/* STM32H7_ADC_DIFSEL - bit fields */
+#define STM32H7_DIFSEL_SHIFT 0
+#define STM32H7_DIFSEL_MASK GENMASK(19, 0)
+
/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */
#define STM32H7_BOOST_CLKRATE 20000000UL
+/* STM32MP13 - Registers for each ADC instance */
+#define STM32MP13_ADC_DIFSEL 0xB0
+
+/* STM32MP13_ADC_CFGR specific bit fields */
+#define STM32MP13_DMAEN BIT(0)
+#define STM32MP13_DMACFG BIT(1)
+
+/* STM32MP13_ADC_DIFSEL - bit fields */
+#define STM32MP13_DIFSEL_SHIFT 0
+#define STM32MP13_DIFSEL_MASK GENMASK(18, 0)
+
#define STM32_ADC_CH_MAX 20 /* max number of channels */
#define STM32_ADC_TIMEOUT_US 100000
+struct stm32_adc {
+ void __iomem *regs;
+ int active_channel;
+ const struct stm32_adc_cfg *cfg;
+};
+
+struct stm32_adc_regs {
+ int reg;
+ int mask;
+ int shift;
+};
+
+struct stm32_adc_regspec {
+ const struct stm32_adc_regs difsel;
+};
+
struct stm32_adc_cfg {
+ const struct stm32_adc_regspec *regs;
unsigned int max_channels;
unsigned int num_bits;
bool has_vregready;
+ bool has_boostmode;
+ bool has_linearcal;
+ bool has_presel;
};
-struct stm32_adc {
- void __iomem *regs;
- int active_channel;
- const struct stm32_adc_cfg *cfg;
+static const struct stm32_adc_regspec stm32h7_adc_regspec = {
+ .difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK },
+};
+
+static const struct stm32_adc_regspec stm32mp13_adc_regspec = {
+ .difsel = { STM32MP13_ADC_DIFSEL, STM32MP13_DIFSEL_MASK },
};
static void stm32_adc_enter_pwr_down(struct udevice *dev)
{
struct stm32_adc *adc = dev_get_priv(dev);
- clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST);
+ if (adc->cfg->has_boostmode)
+ clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST);
+
/* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD);
}
@@ -90,8 +129,7 @@ static int stm32_adc_exit_pwr_down(struct udevice *dev)
/* Exit deep power down, then enable ADC voltage regulator */
clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD);
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADVREGEN);
-
- if (common->rate > STM32H7_BOOST_CLKRATE)
+ if (adc->cfg->has_boostmode && common->rate > STM32H7_BOOST_CLKRATE)
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST);
/* Wait for startup time */
@@ -134,7 +172,7 @@ static int stm32_adc_start_channel(struct udevice *dev, int channel)
return ret;
/* Only use single ended channels */
- writel(0, adc->regs + STM32H7_ADC_DIFSEL);
+ clrbits_le32(adc->regs + adc->cfg->regs->difsel.reg, adc->cfg->regs->difsel.mask);
/* Enable ADC, Poll for ADRDY to be set (after adc startup time) */
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADEN);
@@ -147,7 +185,8 @@ static int stm32_adc_start_channel(struct udevice *dev, int channel)
}
/* Preselect channels */
- writel(uc_pdata->channel_mask, adc->regs + STM32H7_ADC_PCSEL);
+ if (adc->cfg->has_presel)
+ writel(uc_pdata->channel_mask, adc->regs + STM32H7_ADC_PCSEL);
/* Set sampling time to max value by default */
writel(0xffffffff, adc->regs + STM32H7_ADC_SMPR1);
@@ -156,9 +195,11 @@ static int stm32_adc_start_channel(struct udevice *dev, int channel)
/* Program regular sequence: chan in SQ1 & len = 0 for one channel */
writel(channel << STM32H7_SQ1_SHIFT, adc->regs + STM32H7_ADC_SQR1);
- /* Trigger detection disabled (conversion can be launched in SW) */
- clrbits_le32(adc->regs + STM32H7_ADC_CFGR, STM32H7_EXTEN |
- STM32H7_DMNGT);
+ /*
+ * Trigger detection disabled (conversion can be launched in SW)
+ * STM32H7_DMNGT is equivalent to STM32MP13_DMAEN & STM32MP13_DMACFG
+ */
+ clrbits_le32(adc->regs + STM32H7_ADC_CFGR, STM32H7_EXTEN | STM32H7_DMNGT);
adc->active_channel = channel;
return 0;
@@ -206,7 +247,7 @@ static int stm32_adc_selfcalib(struct udevice *dev)
{
struct stm32_adc *adc = dev_get_priv(dev);
int ret;
- u32 val;
+ u32 val, mask;
/*
* Select calibration mode:
@@ -231,7 +272,10 @@ static int stm32_adc_selfcalib(struct udevice *dev)
* - Linearity calibration (needs to be done only once for single/diff)
* will run simultaneously with offset calibration.
*/
- setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADCALDIF | STM32H7_ADCALLIN);
+ mask = STM32H7_ADCALDIF;
+ if (adc->cfg->has_linearcal)
+ mask |= STM32H7_ADCALLIN;
+ setbits_le32(adc->regs + STM32H7_ADC_CR, mask);
/* Start calibration, then wait for completion */
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADCAL);
@@ -394,14 +438,28 @@ static const struct adc_ops stm32_adc_ops = {
};
static const struct stm32_adc_cfg stm32h7_adc_cfg = {
+ .regs = &stm32h7_adc_regspec,
.num_bits = 16,
.max_channels = STM32_ADC_CH_MAX,
+ .has_boostmode = true,
+ .has_linearcal = true,
+ .has_presel = true,
};
static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
+ .regs = &stm32h7_adc_regspec,
.num_bits = 16,
.max_channels = STM32_ADC_CH_MAX,
.has_vregready = true,
+ .has_boostmode = true,
+ .has_linearcal = true,
+ .has_presel = true,
+};
+
+static const struct stm32_adc_cfg stm32mp13_adc_cfg = {
+ .regs = &stm32mp13_adc_regspec,
+ .num_bits = 12,
+ .max_channels = STM32_ADC_CH_MAX - 1,
};
static const struct udevice_id stm32_adc_ids[] = {
@@ -409,6 +467,8 @@ static const struct udevice_id stm32_adc_ids[] = {
.data = (ulong)&stm32h7_adc_cfg },
{ .compatible = "st,stm32mp1-adc",
.data = (ulong)&stm32mp1_adc_cfg },
+ { .compatible = "st,stm32mp13-adc",
+ .data = (ulong)&stm32mp13_adc_cfg },
{}
};