enum stm32_adc_int_ch {
STM32_ADC_INT_CH_NONE = -1,
STM32_ADC_INT_CH_VDDCORE,
+ STM32_ADC_INT_CH_VDDCPU,
+ STM32_ADC_INT_CH_VDDQ_DDR,
STM32_ADC_INT_CH_VREFINT,
STM32_ADC_INT_CH_VBAT,
STM32_ADC_INT_CH_NB,
static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = {
{ "vddcore", STM32_ADC_INT_CH_VDDCORE },
+ { "vddcpu", STM32_ADC_INT_CH_VDDCPU },
+ { "vddq_ddr", STM32_ADC_INT_CH_VDDQ_DDR },
{ "vrefint", STM32_ADC_INT_CH_VREFINT },
{ "vbat", STM32_ADC_INT_CH_VBAT },
};
* @exten: trigger control register & bitfield
* @extsel: trigger selection register & bitfield
* @res: resolution selection register & bitfield
+ * @difsel: differential mode selection register & bitfield
+ * @calfact_s: single-ended calibration factors register & bitfield
+ * @calfact_d: differential calibration factors register & bitfield
* @smpr: smpr1 & smpr2 registers offset array
* @smp_bits: smpr1 & smpr2 index and bitfields
- * @or_vdd: option register & vddcore bitfield
+ * @or_vddcore: option register & vddcore bitfield
+ * @or_vddcpu: option register & vddcpu bitfield
+ * @or_vddq_ddr: option register & vddq_ddr bitfield
* @ccr_vbat: common register & vbat bitfield
* @ccr_vref: common register & vrefint bitfield
*/
const struct stm32_adc_regs exten;
const struct stm32_adc_regs extsel;
const struct stm32_adc_regs res;
+ const struct stm32_adc_regs difsel;
+ const struct stm32_adc_regs calfact_s;
+ const struct stm32_adc_regs calfact_d;
const u32 smpr[2];
const struct stm32_adc_regs *smp_bits;
- const struct stm32_adc_regs or_vdd;
+ const struct stm32_adc_regs or_vddcore;
+ const struct stm32_adc_regs or_vddcpu;
+ const struct stm32_adc_regs or_vddq_ddr;
const struct stm32_adc_regs ccr_vbat;
const struct stm32_adc_regs ccr_vref;
};
* @trigs: external trigger sources
* @clk_required: clock is required
* @has_vregready: vregready status flag presence
+ * @has_boostmode: boost mode support flag
+ * @has_linearcal: linear calibration support flag
+ * @has_presel: channel preselection support flag
* @prepare: optional prepare routine (power-up, enable)
* @start_conv: routine to start conversions
* @stop_conv: routine to stop conversions
struct stm32_adc_trig_info *trigs;
bool clk_required;
bool has_vregready;
+ bool has_boostmode;
+ bool has_linearcal;
+ bool has_presel;
int (*prepare)(struct iio_dev *);
void (*start_conv)(struct iio_dev *, bool dma);
void (*stop_conv)(struct iio_dev *);
.num_res = ARRAY_SIZE(stm32h7_adc_resolutions),
};
+/* stm32mp13 can have up to 19 channels */
+static const struct stm32_adc_info stm32mp13_adc_info = {
+ .max_channels = 19,
+ .resolutions = stm32f4_adc_resolutions,
+ .num_res = ARRAY_SIZE(stm32f4_adc_resolutions),
+};
+
/*
* stm32f4_sq - describe regular sequence registers
* - L: sequence len (register & bit field)
.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
STM32H7_EXTSEL_SHIFT },
.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
+ .difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK},
+ .calfact_s = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_S_MASK,
+ STM32H7_CALFACT_S_SHIFT },
+ .calfact_d = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_D_MASK,
+ STM32H7_CALFACT_D_SHIFT },
.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
.smp_bits = stm32h7_smp_bits,
};
+/* STM32MP13 programmable sampling time (ADC clock cycles, rounded down) */
+static const unsigned int stm32mp13_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
+ 2, 6, 12, 24, 47, 92, 247, 640,
+};
+
+static const struct stm32_adc_regspec stm32mp13_adc_regspec = {
+ .dr = STM32H7_ADC_DR,
+ .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
+ .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
+ .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
+ .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
+ .sqr = stm32h7_sq,
+ .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
+ .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
+ STM32H7_EXTSEL_SHIFT },
+ .res = { STM32H7_ADC_CFGR, STM32MP13_RES_MASK, STM32MP13_RES_SHIFT },
+ .difsel = { STM32MP13_ADC_DIFSEL, STM32MP13_DIFSEL_MASK},
+ .calfact_s = { STM32MP13_ADC_CALFACT, STM32MP13_CALFACT_S_MASK,
+ STM32MP13_CALFACT_S_SHIFT },
+ .calfact_d = { STM32MP13_ADC_CALFACT, STM32MP13_CALFACT_D_MASK,
+ STM32MP13_CALFACT_D_SHIFT },
+ .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
+ .smp_bits = stm32h7_smp_bits,
+ .or_vddcore = { STM32MP13_ADC2_OR, STM32MP13_OP0 },
+ .or_vddcpu = { STM32MP13_ADC2_OR, STM32MP13_OP1 },
+ .or_vddq_ddr = { STM32MP13_ADC2_OR, STM32MP13_OP2 },
+ .ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
+ .ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
+};
+
static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
.dr = STM32H7_ADC_DR,
.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
STM32H7_EXTSEL_SHIFT },
.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
+ .difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK},
+ .calfact_s = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_S_MASK,
+ STM32H7_CALFACT_S_SHIFT },
+ .calfact_d = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_D_MASK,
+ STM32H7_CALFACT_D_SHIFT },
.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
.smp_bits = stm32h7_smp_bits,
- .or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
+ .or_vddcore = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
.ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
.ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
};
switch (i) {
case STM32_ADC_INT_CH_VDDCORE:
dev_dbg(&indio_dev->dev, "Enable VDDCore\n");
- stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg,
- adc->cfg->regs->or_vdd.mask);
+ stm32_adc_set_bits(adc, adc->cfg->regs->or_vddcore.reg,
+ adc->cfg->regs->or_vddcore.mask);
+ break;
+ case STM32_ADC_INT_CH_VDDCPU:
+ dev_dbg(&indio_dev->dev, "Enable VDDCPU\n");
+ stm32_adc_set_bits(adc, adc->cfg->regs->or_vddcpu.reg,
+ adc->cfg->regs->or_vddcpu.mask);
+ break;
+ case STM32_ADC_INT_CH_VDDQ_DDR:
+ dev_dbg(&indio_dev->dev, "Enable VDDQ_DDR\n");
+ stm32_adc_set_bits(adc, adc->cfg->regs->or_vddq_ddr.reg,
+ adc->cfg->regs->or_vddq_ddr.mask);
break;
case STM32_ADC_INT_CH_VREFINT:
dev_dbg(&indio_dev->dev, "Enable VREFInt\n");
switch (i) {
case STM32_ADC_INT_CH_VDDCORE:
- stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg,
- adc->cfg->regs->or_vdd.mask);
+ stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddcore.reg,
+ adc->cfg->regs->or_vddcore.mask);
+ break;
+ case STM32_ADC_INT_CH_VDDCPU:
+ stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddcpu.reg,
+ adc->cfg->regs->or_vddcpu.mask);
+ break;
+ case STM32_ADC_INT_CH_VDDQ_DDR:
+ stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddq_ddr.reg,
+ adc->cfg->regs->or_vddq_ddr.mask);
break;
case STM32_ADC_INT_CH_VREFINT:
stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vref.reg,
if (ret)
dev_warn(&indio_dev->dev, "stop failed\n");
+ /* STM32H7_DMNGT_MASK covers STM32MP13_DMAEN & STM32MP13_DMACFG */
stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK);
}
stm32_adc_set_bits(adc, adc->cfg->regs->isr_eoc.reg, msk);
}
+static void stm32mp13_adc_start_conv(struct iio_dev *indio_dev, bool dma)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ if (dma)
+ stm32_adc_set_bits(adc, STM32H7_ADC_CFGR,
+ STM32MP13_DMAEN | STM32MP13_DMACFG);
+
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART);
+}
+
static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev)
{
struct stm32_adc *adc = iio_priv(indio_dev);
stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN);
- if (adc->common->rate > STM32H7_BOOST_CLKRATE)
+ if (adc->cfg->has_boostmode &&
+ adc->common->rate > STM32H7_BOOST_CLKRATE)
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
/* Wait for startup time */
static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc)
{
- stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
+ if (adc->cfg->has_boostmode)
+ stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
/* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
int i, ret;
u32 lincalrdyw_mask, val;
+ if (!adc->cfg->has_linearcal)
+ goto skip_linearcal;
+
/* Read linearity calibration */
lincalrdyw_mask = STM32H7_LINCALRDYW6;
for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
lincalrdyw_mask >>= 1;
}
+skip_linearcal:
/* Read offset calibration */
- val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT);
- adc->cal.calfact_s = (val & STM32H7_CALFACT_S_MASK);
- adc->cal.calfact_s >>= STM32H7_CALFACT_S_SHIFT;
- adc->cal.calfact_d = (val & STM32H7_CALFACT_D_MASK);
- adc->cal.calfact_d >>= STM32H7_CALFACT_D_SHIFT;
+ val = stm32_adc_readl(adc, adc->cfg->regs->calfact_s.reg);
+ adc->cal.calfact_s = (val & adc->cfg->regs->calfact_s.mask);
+ adc->cal.calfact_s >>= adc->cfg->regs->calfact_s.shift;
+ adc->cal.calfact_d = (val & adc->cfg->regs->calfact_d.mask);
+ adc->cal.calfact_d >>= adc->cfg->regs->calfact_d.shift;
adc->cal.calibrated = true;
return 0;
int i, ret;
u32 lincalrdyw_mask, val;
- val = (adc->cal.calfact_s << STM32H7_CALFACT_S_SHIFT) |
- (adc->cal.calfact_d << STM32H7_CALFACT_D_SHIFT);
- stm32_adc_writel(adc, STM32H7_ADC_CALFACT, val);
+ val = (adc->cal.calfact_s << adc->cfg->regs->calfact_s.shift) |
+ (adc->cal.calfact_d << adc->cfg->regs->calfact_d.shift);
+ stm32_adc_writel(adc, adc->cfg->regs->calfact_s.reg, val);
+
+ if (!adc->cfg->has_linearcal)
+ return 0;
lincalrdyw_mask = STM32H7_LINCALRDYW6;
for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
{
struct stm32_adc *adc = iio_priv(indio_dev);
int ret;
+ u32 msk = STM32H7_ADCALDIF;
u32 val;
if (adc->cal.calibrated)
return true;
+ if (adc->cfg->has_linearcal)
+ msk |= STM32H7_ADCALLIN;
/* ADC must be disabled for calibration */
stm32h7_adc_disable(indio_dev);
* - Offset calibration for single ended inputs
* - No linearity calibration (do it later, before reading it)
*/
- stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF);
- stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALLIN);
+ stm32_adc_clr_bits(adc, STM32H7_ADC_CR, msk);
/* Start calibration, then wait for completion */
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
* - Linearity calibration (needs to be done only once for single/diff)
* will run simultaneously with offset calibration.
*/
- stm32_adc_set_bits(adc, STM32H7_ADC_CR,
- STM32H7_ADCALDIF | STM32H7_ADCALLIN);
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, msk);
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
!(val & STM32H7_ADCAL), 100,
}
out:
- stm32_adc_clr_bits(adc, STM32H7_ADC_CR,
- STM32H7_ADCALDIF | STM32H7_ADCALLIN);
+ stm32_adc_clr_bits(adc, STM32H7_ADC_CR, msk);
return ret;
}
stm32_adc_int_ch_enable(indio_dev);
- stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
+ stm32_adc_writel(adc, adc->cfg->regs->difsel.reg, adc->difsel);
ret = stm32h7_adc_enable(indio_dev);
if (ret)
if (ret)
goto disable;
- stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel);
+ if (adc->cfg->has_presel)
+ stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel);
return 0;
{
struct stm32_adc *adc = iio_priv(indio_dev);
- stm32_adc_writel(adc, STM32H7_ADC_PCSEL, 0);
+ if (adc->cfg->has_presel)
+ stm32_adc_writel(adc, STM32H7_ADC_PCSEL, 0);
stm32h7_adc_disable(indio_dev);
stm32_adc_int_ch_disable(adc);
stm32h7_adc_enter_pwr_down(adc);
adc->pcsel |= BIT(chan->channel);
if (differential) {
/* pre-build diff channels mask */
- adc->difsel |= BIT(chan->channel);
+ adc->difsel |= BIT(chan->channel) & adc->cfg->regs->difsel.mask;
/* Also add negative input to pre-selected channels */
adc->pcsel |= BIT(chan->channel2);
}
for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
+ /* Check internal channel availability */
+ switch (i) {
+ case STM32_ADC_INT_CH_VDDCORE:
+ if (!adc->cfg->regs->or_vddcore.reg)
+ dev_warn(&indio_dev->dev,
+ "%s channel not available\n", ch_name);
+ break;
+ case STM32_ADC_INT_CH_VDDCPU:
+ if (!adc->cfg->regs->or_vddcpu.reg)
+ dev_warn(&indio_dev->dev,
+ "%s channel not available\n", ch_name);
+ break;
+ case STM32_ADC_INT_CH_VDDQ_DDR:
+ if (!adc->cfg->regs->or_vddq_ddr.reg)
+ dev_warn(&indio_dev->dev,
+ "%s channel not available\n", ch_name);
+ break;
+ case STM32_ADC_INT_CH_VREFINT:
+ if (!adc->cfg->regs->ccr_vref.reg)
+ dev_warn(&indio_dev->dev,
+ "%s channel not available\n", ch_name);
+ break;
+ case STM32_ADC_INT_CH_VBAT:
+ if (!adc->cfg->regs->ccr_vbat.reg)
+ dev_warn(&indio_dev->dev,
+ "%s channel not available\n", ch_name);
+ break;
+ }
+
if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT) {
adc->int_ch[i] = chan;
break;
.regs = &stm32h7_adc_regspec,
.adc_info = &stm32h7_adc_info,
.trigs = stm32h7_adc_trigs,
+ .has_boostmode = true,
+ .has_linearcal = true,
+ .has_presel = true,
.start_conv = stm32h7_adc_start_conv,
.stop_conv = stm32h7_adc_stop_conv,
.prepare = stm32h7_adc_prepare,
.adc_info = &stm32h7_adc_info,
.trigs = stm32h7_adc_trigs,
.has_vregready = true,
+ .has_boostmode = true,
+ .has_linearcal = true,
+ .has_presel = true,
.start_conv = stm32h7_adc_start_conv,
.stop_conv = stm32h7_adc_stop_conv,
.prepare = stm32h7_adc_prepare,
.ts_vrefint_ns = 4300,
};
+static const struct stm32_adc_cfg stm32mp13_adc_cfg = {
+ .regs = &stm32mp13_adc_regspec,
+ .adc_info = &stm32mp13_adc_info,
+ .trigs = stm32h7_adc_trigs,
+ .start_conv = stm32mp13_adc_start_conv,
+ .stop_conv = stm32h7_adc_stop_conv,
+ .prepare = stm32h7_adc_prepare,
+ .unprepare = stm32h7_adc_unprepare,
+ .smp_cycles = stm32mp13_adc_smp_cycles,
+ .irq_clear = stm32h7_adc_irq_clear,
+};
+
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 },
+ { .compatible = "st,stm32mp13-adc", .data = (void *)&stm32mp13_adc_cfg },
{},
};
MODULE_DEVICE_TABLE(of, stm32_adc_of_match);