diff options
Diffstat (limited to 'drivers/iio/adc/stm32-adc.c')
-rw-r--r-- | drivers/iio/adc/stm32-adc.c | 762 |
1 files changed, 701 insertions, 61 deletions
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index c28e7ff80e11..5bfcc1f13105 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -31,9 +31,11 @@ #include <linux/iio/triggered_buffer.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/of.h> +#include <linux/of_device.h> #include "stm32-adc-core.h" @@ -76,6 +78,78 @@ #define STM32F4_DMA BIT(8) #define STM32F4_ADON BIT(0) +/* STM32H7 - Registers for each ADC instance */ +#define STM32H7_ADC_ISR 0x00 +#define STM32H7_ADC_IER 0x04 +#define STM32H7_ADC_CR 0x08 +#define STM32H7_ADC_CFGR 0x0C +#define STM32H7_ADC_PCSEL 0x1C +#define STM32H7_ADC_SQR1 0x30 +#define STM32H7_ADC_SQR2 0x34 +#define STM32H7_ADC_SQR3 0x38 +#define STM32H7_ADC_SQR4 0x3C +#define STM32H7_ADC_DR 0x40 +#define STM32H7_ADC_CALFACT 0xC4 +#define STM32H7_ADC_CALFACT2 0xC8 + +/* STM32H7_ADC_ISR - bit fields */ +#define STM32H7_EOC BIT(2) +#define STM32H7_ADRDY BIT(0) + +/* STM32H7_ADC_IER - bit fields */ +#define STM32H7_EOCIE STM32H7_EOC + +/* STM32H7_ADC_CR - bit fields */ +#define STM32H7_ADCAL BIT(31) +#define STM32H7_ADCALDIF BIT(30) +#define STM32H7_DEEPPWD BIT(29) +#define STM32H7_ADVREGEN BIT(28) +#define STM32H7_LINCALRDYW6 BIT(27) +#define STM32H7_LINCALRDYW5 BIT(26) +#define STM32H7_LINCALRDYW4 BIT(25) +#define STM32H7_LINCALRDYW3 BIT(24) +#define STM32H7_LINCALRDYW2 BIT(23) +#define STM32H7_LINCALRDYW1 BIT(22) +#define STM32H7_ADCALLIN BIT(16) +#define STM32H7_BOOST BIT(8) +#define STM32H7_ADSTP BIT(4) +#define STM32H7_ADSTART BIT(2) +#define STM32H7_ADDIS BIT(1) +#define STM32H7_ADEN BIT(0) + +/* STM32H7_ADC_CFGR bit fields */ +#define STM32H7_EXTEN_SHIFT 10 +#define STM32H7_EXTEN_MASK GENMASK(11, 10) +#define STM32H7_EXTSEL_SHIFT 5 +#define STM32H7_EXTSEL_MASK GENMASK(9, 5) +#define STM32H7_RES_SHIFT 2 +#define STM32H7_RES_MASK GENMASK(4, 2) +#define STM32H7_DMNGT_SHIFT 0 +#define STM32H7_DMNGT_MASK GENMASK(1, 0) + +enum stm32h7_adc_dmngt { + STM32H7_DMNGT_DR_ONLY, /* Regular data in DR only */ + STM32H7_DMNGT_DMA_ONESHOT, /* DMA one shot mode */ + STM32H7_DMNGT_DFSDM, /* DFSDM mode */ + STM32H7_DMNGT_DMA_CIRC, /* DMA circular mode */ +}; + +/* STM32H7_ADC_CALFACT - bit fields */ +#define STM32H7_CALFACT_D_SHIFT 16 +#define STM32H7_CALFACT_D_MASK GENMASK(26, 16) +#define STM32H7_CALFACT_S_SHIFT 0 +#define STM32H7_CALFACT_S_MASK GENMASK(10, 0) + +/* STM32H7_ADC_CALFACT2 - bit fields */ +#define STM32H7_LINCALFACT_SHIFT 0 +#define STM32H7_LINCALFACT_MASK GENMASK(29, 0) + +/* Number of linear calibration shadow registers / LINCALRDYW control bits */ +#define STM32H7_LINCALFACT_NUM 6 + +/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */ +#define STM32H7_BOOST_CLKRATE 20000000UL + #define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */ #define STM32_ADC_TIMEOUT_US 100000 #define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000)) @@ -121,6 +195,18 @@ struct stm32_adc_trig_info { }; /** + * struct stm32_adc_calib - optional adc calibration data + * @calfact_s: Calibration offset for single ended channels + * @calfact_d: Calibration offset in differential + * @lincalfact: Linearity calibration factor + */ +struct stm32_adc_calib { + u32 calfact_s; + u32 calfact_d; + u32 lincalfact[STM32H7_LINCALFACT_NUM]; +}; + +/** * stm32_adc_regs - stm32 ADC misc registers & bitfield desc * @reg: register offset * @mask: bitfield mask @@ -133,9 +219,56 @@ struct stm32_adc_regs { }; /** + * stm32_adc_regspec - stm32 registers definition, compatible dependent data + * @dr: data register offset + * @ier_eoc: interrupt enable register & eocie bitfield + * @isr_eoc: interrupt status register & eoc bitfield + * @sqr: reference to sequence registers array + * @exten: trigger control register & bitfield + * @extsel: trigger selection register & bitfield + * @res: resolution selection register & bitfield + */ +struct stm32_adc_regspec { + const u32 dr; + const struct stm32_adc_regs ier_eoc; + const struct stm32_adc_regs isr_eoc; + const struct stm32_adc_regs *sqr; + const struct stm32_adc_regs exten; + const struct stm32_adc_regs extsel; + const struct stm32_adc_regs res; +}; + +struct stm32_adc; + +/** + * stm32_adc_cfg - stm32 compatible configuration data + * @regs: registers descriptions + * @adc_info: per instance input channels definitions + * @trigs: external trigger sources + * @clk_required: clock is required + * @selfcalib: optional routine for self-calibration + * @prepare: optional prepare routine (power-up, enable) + * @start_conv: routine to start conversions + * @stop_conv: routine to stop conversions + * @unprepare: optional unprepare routine (disable, power-down) + */ +struct stm32_adc_cfg { + const struct stm32_adc_regspec *regs; + const struct stm32_adc_info *adc_info; + struct stm32_adc_trig_info *trigs; + bool clk_required; + int (*selfcalib)(struct stm32_adc *); + int (*prepare)(struct stm32_adc *); + void (*start_conv)(struct stm32_adc *, bool dma); + void (*stop_conv)(struct stm32_adc *); + void (*unprepare)(struct stm32_adc *); +}; + +/** * struct stm32_adc - private data of each ADC IIO instance * @common: reference to ADC block common data * @offset: ADC instance register offset in ADC block + * @cfg: compatible configuration data * @completion: end of single conversion completion * @buffer: data buffer * @clk: clock for this adc instance @@ -149,10 +282,13 @@ struct stm32_adc_regs { * @rx_buf: dma rx buffer cpu address * @rx_dma_buf: dma rx buffer bus address * @rx_buf_sz: dma rx buffer size + * @pcsel bitmask to preselect channels on some devices + * @cal: optional calibration data on some devices */ struct stm32_adc { struct stm32_adc_common *common; u32 offset; + const struct stm32_adc_cfg *cfg; struct completion completion; u16 buffer[STM32_ADC_MAX_SQ]; struct clk *clk; @@ -166,6 +302,8 @@ struct stm32_adc { u8 *rx_buf; dma_addr_t rx_dma_buf; unsigned int rx_buf_sz; + u32 pcsel; + struct stm32_adc_calib cal; }; /** @@ -180,8 +318,26 @@ struct stm32_adc_chan_spec { const char *name; }; -/* Input definitions common for all STM32F4 instances */ -static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = { +/** + * struct stm32_adc_info - stm32 ADC, per instance config data + * @channels: Reference to stm32 channels spec + * @max_channels: Number of channels + * @resolutions: available resolutions + * @num_res: number of available resolutions + */ +struct stm32_adc_info { + const struct stm32_adc_chan_spec *channels; + int max_channels; + const unsigned int *resolutions; + const unsigned int num_res; +}; + +/* + * Input definitions common for all instances: + * stm32f4 can have up to 16 channels + * stm32h7 can have up to 20 channels + */ +static const struct stm32_adc_chan_spec stm32_adc_channels[] = { { IIO_VOLTAGE, 0, "in0" }, { IIO_VOLTAGE, 1, "in1" }, { IIO_VOLTAGE, 2, "in2" }, @@ -198,6 +354,10 @@ static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = { { IIO_VOLTAGE, 13, "in13" }, { IIO_VOLTAGE, 14, "in14" }, { IIO_VOLTAGE, 15, "in15" }, + { IIO_VOLTAGE, 16, "in16" }, + { IIO_VOLTAGE, 17, "in17" }, + { IIO_VOLTAGE, 18, "in18" }, + { IIO_VOLTAGE, 19, "in19" }, }; static const unsigned int stm32f4_adc_resolutions[] = { @@ -205,6 +365,25 @@ static const unsigned int stm32f4_adc_resolutions[] = { 12, 10, 8, 6, }; +static const struct stm32_adc_info stm32f4_adc_info = { + .channels = stm32_adc_channels, + .max_channels = 16, + .resolutions = stm32f4_adc_resolutions, + .num_res = ARRAY_SIZE(stm32f4_adc_resolutions), +}; + +static const unsigned int stm32h7_adc_resolutions[] = { + /* sorted values so the index matches RES[2:0] in STM32H7_ADC_CFGR */ + 16, 14, 12, 10, 8, +}; + +static const struct stm32_adc_info stm32h7_adc_info = { + .channels = stm32_adc_channels, + .max_channels = 20, + .resolutions = stm32h7_adc_resolutions, + .num_res = ARRAY_SIZE(stm32h7_adc_resolutions), +}; + /** * stm32f4_sq - describe regular sequence registers * - L: sequence len (register & bit field) @@ -252,6 +431,69 @@ static struct stm32_adc_trig_info stm32f4_adc_trigs[] = { {}, /* sentinel */ }; +static const struct stm32_adc_regspec stm32f4_adc_regspec = { + .dr = STM32F4_ADC_DR, + .ier_eoc = { STM32F4_ADC_CR1, STM32F4_EOCIE }, + .isr_eoc = { STM32F4_ADC_SR, STM32F4_EOC }, + .sqr = stm32f4_sq, + .exten = { STM32F4_ADC_CR2, STM32F4_EXTEN_MASK, STM32F4_EXTEN_SHIFT }, + .extsel = { STM32F4_ADC_CR2, STM32F4_EXTSEL_MASK, + STM32F4_EXTSEL_SHIFT }, + .res = { STM32F4_ADC_CR1, STM32F4_RES_MASK, STM32F4_RES_SHIFT }, +}; + +static const struct stm32_adc_regs stm32h7_sq[STM32_ADC_MAX_SQ + 1] = { + /* L: len bit field description to be kept as first element */ + { STM32H7_ADC_SQR1, GENMASK(3, 0), 0 }, + /* SQ1..SQ16 registers & bit fields (reg, mask, shift) */ + { STM32H7_ADC_SQR1, GENMASK(10, 6), 6 }, + { STM32H7_ADC_SQR1, GENMASK(16, 12), 12 }, + { STM32H7_ADC_SQR1, GENMASK(22, 18), 18 }, + { STM32H7_ADC_SQR1, GENMASK(28, 24), 24 }, + { STM32H7_ADC_SQR2, GENMASK(4, 0), 0 }, + { STM32H7_ADC_SQR2, GENMASK(10, 6), 6 }, + { STM32H7_ADC_SQR2, GENMASK(16, 12), 12 }, + { STM32H7_ADC_SQR2, GENMASK(22, 18), 18 }, + { STM32H7_ADC_SQR2, GENMASK(28, 24), 24 }, + { STM32H7_ADC_SQR3, GENMASK(4, 0), 0 }, + { STM32H7_ADC_SQR3, GENMASK(10, 6), 6 }, + { STM32H7_ADC_SQR3, GENMASK(16, 12), 12 }, + { STM32H7_ADC_SQR3, GENMASK(22, 18), 18 }, + { STM32H7_ADC_SQR3, GENMASK(28, 24), 24 }, + { STM32H7_ADC_SQR4, GENMASK(4, 0), 0 }, + { STM32H7_ADC_SQR4, GENMASK(10, 6), 6 }, +}; + +/* STM32H7 external trigger sources for all instances */ +static struct stm32_adc_trig_info stm32h7_adc_trigs[] = { + { TIM1_CH1, STM32_EXT0 }, + { TIM1_CH2, STM32_EXT1 }, + { TIM1_CH3, STM32_EXT2 }, + { TIM2_CH2, STM32_EXT3 }, + { TIM3_TRGO, STM32_EXT4 }, + { TIM4_CH4, STM32_EXT5 }, + { TIM8_TRGO, STM32_EXT7 }, + { TIM8_TRGO2, STM32_EXT8 }, + { TIM1_TRGO, STM32_EXT9 }, + { TIM1_TRGO2, STM32_EXT10 }, + { TIM2_TRGO, STM32_EXT11 }, + { TIM4_TRGO, STM32_EXT12 }, + { TIM6_TRGO, STM32_EXT13 }, + { TIM3_CH4, STM32_EXT15 }, + {}, +}; + +static const struct stm32_adc_regspec stm32h7_adc_regspec = { + .dr = STM32H7_ADC_DR, + .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE }, + .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC }, + .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, STM32H7_RES_MASK, STM32H7_RES_SHIFT }, +}; + /** * STM32 ADC registers access routines * @adc: stm32 adc instance @@ -265,6 +507,12 @@ static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg) return readl_relaxed(adc->common->base + adc->offset + reg); } +#define stm32_adc_readl_addr(addr) stm32_adc_readl(adc, addr) + +#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \ + readx_poll_timeout(stm32_adc_readl_addr, reg, val, \ + cond, sleep_us, timeout_us) + static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg) { return readw_relaxed(adc->common->base + adc->offset + reg); @@ -299,7 +547,8 @@ static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits) */ static void stm32_adc_conv_irq_enable(struct stm32_adc *adc) { - stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE); + stm32_adc_set_bits(adc, adc->cfg->regs->ier_eoc.reg, + adc->cfg->regs->ier_eoc.mask); }; /** @@ -308,19 +557,22 @@ static void stm32_adc_conv_irq_enable(struct stm32_adc *adc) */ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc) { - stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE); + stm32_adc_clr_bits(adc, adc->cfg->regs->ier_eoc.reg, + adc->cfg->regs->ier_eoc.mask); } static void stm32_adc_set_res(struct stm32_adc *adc) { - u32 val = stm32_adc_readl(adc, STM32F4_ADC_CR1); + const struct stm32_adc_regs *res = &adc->cfg->regs->res; + u32 val; - val = (val & ~STM32F4_RES_MASK) | (adc->res << STM32F4_RES_SHIFT); - stm32_adc_writel(adc, STM32F4_ADC_CR1, val); + val = stm32_adc_readl(adc, res->reg); + val = (val & ~res->mask) | (adc->res << res->shift); + stm32_adc_writel(adc, res->reg, val); } /** - * stm32_adc_start_conv() - Start conversions for regular channels. + * stm32f4_adc_start_conv() - Start conversions for regular channels. * @adc: stm32 adc instance * @dma: use dma to transfer conversion result * @@ -329,7 +581,7 @@ static void stm32_adc_set_res(struct stm32_adc *adc) * conversions, in IIO buffer modes. Otherwise, use ADC interrupt with direct * DR read instead (e.g. read_raw, or triggered buffer mode without DMA). */ -static void stm32_adc_start_conv(struct stm32_adc *adc, bool dma) +static void stm32f4_adc_start_conv(struct stm32_adc *adc, bool dma) { stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN); @@ -347,7 +599,7 @@ static void stm32_adc_start_conv(struct stm32_adc *adc, bool dma) stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART); } -static void stm32_adc_stop_conv(struct stm32_adc *adc) +static void stm32f4_adc_stop_conv(struct stm32_adc *adc) { stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK); stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT); @@ -357,6 +609,324 @@ static void stm32_adc_stop_conv(struct stm32_adc *adc) STM32F4_ADON | STM32F4_DMA | STM32F4_DDS); } +static void stm32h7_adc_start_conv(struct stm32_adc *adc, bool dma) +{ + enum stm32h7_adc_dmngt dmngt; + unsigned long flags; + u32 val; + + if (dma) + dmngt = STM32H7_DMNGT_DMA_CIRC; + else + dmngt = STM32H7_DMNGT_DR_ONLY; + + spin_lock_irqsave(&adc->lock, flags); + val = stm32_adc_readl(adc, STM32H7_ADC_CFGR); + val = (val & ~STM32H7_DMNGT_MASK) | (dmngt << STM32H7_DMNGT_SHIFT); + stm32_adc_writel(adc, STM32H7_ADC_CFGR, val); + spin_unlock_irqrestore(&adc->lock, flags); + + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART); +} + +static void stm32h7_adc_stop_conv(struct stm32_adc *adc) +{ + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + int ret; + u32 val; + + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTP); + + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & (STM32H7_ADSTART)), + 100, STM32_ADC_TIMEOUT_US); + if (ret) + dev_warn(&indio_dev->dev, "stop failed\n"); + + stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK); +} + +static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc) +{ + /* 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); + + if (adc->common->rate > STM32H7_BOOST_CLKRATE) + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST); + + /* Wait for startup time */ + usleep_range(10, 20); +} + +static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc) +{ + 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); +} + +static int stm32h7_adc_enable(struct stm32_adc *adc) +{ + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + int ret; + u32 val; + + /* Clear ADRDY by writing one, then enable ADC */ + stm32_adc_set_bits(adc, STM32H7_ADC_ISR, STM32H7_ADRDY); + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADEN); + + /* Poll for ADRDY to be set (after adc startup time) */ + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val, + val & STM32H7_ADRDY, + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADEN); + dev_err(&indio_dev->dev, "Failed to enable ADC\n"); + } + + return ret; +} + +static void stm32h7_adc_disable(struct stm32_adc *adc) +{ + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + int ret; + u32 val; + + /* Disable ADC and wait until it's effectively disabled */ + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADDIS); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & STM32H7_ADEN), 100, + STM32_ADC_TIMEOUT_US); + if (ret) + dev_warn(&indio_dev->dev, "Failed to disable\n"); +} + +/** + * stm32h7_adc_read_selfcalib() - read calibration shadow regs, save result + * @adc: stm32 adc instance + */ +static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc) +{ + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + int i, ret; + u32 lincalrdyw_mask, val; + + /* Enable adc so LINCALRDYW1..6 bits are writable */ + ret = stm32h7_adc_enable(adc); + if (ret) + return ret; + + /* Read linearity calibration */ + lincalrdyw_mask = STM32H7_LINCALRDYW6; + for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) { + /* Clear STM32H7_LINCALRDYW[6..1]: transfer calib to CALFACT2 */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask); + + /* Poll: wait calib data to be ready in CALFACT2 register */ + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & lincalrdyw_mask), + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "Failed to read calfact\n"); + goto disable; + } + + val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2); + adc->cal.lincalfact[i] = (val & STM32H7_LINCALFACT_MASK); + adc->cal.lincalfact[i] >>= STM32H7_LINCALFACT_SHIFT; + + lincalrdyw_mask >>= 1; + } + + /* 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; + +disable: + stm32h7_adc_disable(adc); + + return ret; +} + +/** + * stm32h7_adc_restore_selfcalib() - Restore saved self-calibration result + * @adc: stm32 adc instance + * Note: ADC must be enabled, with no on-going conversions. + */ +static int stm32h7_adc_restore_selfcalib(struct stm32_adc *adc) +{ + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + 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); + + lincalrdyw_mask = STM32H7_LINCALRDYW6; + for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) { + /* + * Write saved calibration data to shadow registers: + * Write CALFACT2, and set LINCALRDYW[6..1] bit to trigger + * data write. Then poll to wait for complete transfer. + */ + val = adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT; + stm32_adc_writel(adc, STM32H7_ADC_CALFACT2, val); + stm32_adc_set_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + val & lincalrdyw_mask, + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "Failed to write calfact\n"); + return ret; + } + + /* + * Read back calibration data, has two effects: + * - It ensures bits LINCALRDYW[6..1] are kept cleared + * for next time calibration needs to be restored. + * - BTW, bit clear triggers a read, then check data has been + * correctly written. + */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & lincalrdyw_mask), + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "Failed to read calfact\n"); + return ret; + } + val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2); + if (val != adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT) { + dev_err(&indio_dev->dev, "calfact not consistent\n"); + return -EIO; + } + + lincalrdyw_mask >>= 1; + } + + return 0; +} + +/** + * Fixed timeout value for ADC calibration. + * worst cases: + * - low clock frequency + * - maximum prescalers + * Calibration requires: + * - 131,072 ADC clock cycle for the linear calibration + * - 20 ADC clock cycle for the offset calibration + * + * Set to 100ms for now + */ +#define STM32H7_ADC_CALIB_TIMEOUT_US 100000 + +/** + * stm32h7_adc_selfcalib() - Procedure to calibrate ADC (from power down) + * @adc: stm32 adc instance + * Exit from power down, calibrate ADC, then return to power down. + */ +static int stm32h7_adc_selfcalib(struct stm32_adc *adc) +{ + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + int ret; + u32 val; + + stm32h7_adc_exit_pwr_down(adc); + + /* + * Select calibration mode: + * - 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); + + /* Start calibration, then wait for completion */ + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & STM32H7_ADCAL), 100, + STM32H7_ADC_CALIB_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "calibration failed\n"); + goto pwr_dwn; + } + + /* + * Select calibration mode, then start calibration: + * - Offset calibration for differential input + * - 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, STM32H7_ADCAL); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & STM32H7_ADCAL), 100, + STM32H7_ADC_CALIB_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "calibration failed\n"); + goto pwr_dwn; + } + + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, + STM32H7_ADCALDIF | STM32H7_ADCALLIN); + + /* Read calibration result for future reference */ + ret = stm32h7_adc_read_selfcalib(adc); + +pwr_dwn: + stm32h7_adc_enter_pwr_down(adc); + + return ret; +} + +/** + * stm32h7_adc_prepare() - Leave power down mode to enable ADC. + * @adc: stm32 adc instance + * Leave power down mode. + * Enable ADC. + * Restore calibration data. + * Pre-select channels that may be used in PCSEL (required by input MUX / IO). + */ +static int stm32h7_adc_prepare(struct stm32_adc *adc) +{ + int ret; + + stm32h7_adc_exit_pwr_down(adc); + + ret = stm32h7_adc_enable(adc); + if (ret) + goto pwr_dwn; + + ret = stm32h7_adc_restore_selfcalib(adc); + if (ret) + goto disable; + + stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel); + + return 0; + +disable: + stm32h7_adc_disable(adc); +pwr_dwn: + stm32h7_adc_enter_pwr_down(adc); + + return ret; +} + +static void stm32h7_adc_unprepare(struct stm32_adc *adc) +{ + stm32h7_adc_disable(adc); + stm32h7_adc_enter_pwr_down(adc); +} + /** * stm32_adc_conf_scan_seq() - Build regular channels scan sequence * @indio_dev: IIO device @@ -371,6 +941,7 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_regs *sqr = adc->cfg->regs->sqr; const struct iio_chan_spec *chan; u32 val, bit; int i = 0; @@ -388,20 +959,20 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev, dev_dbg(&indio_dev->dev, "%s chan %d to SQ%d\n", __func__, chan->channel, i); - val = stm32_adc_readl(adc, stm32f4_sq[i].reg); - val &= ~stm32f4_sq[i].mask; - val |= chan->channel << stm32f4_sq[i].shift; - stm32_adc_writel(adc, stm32f4_sq[i].reg, val); + val = stm32_adc_readl(adc, sqr[i].reg); + val &= ~sqr[i].mask; + val |= chan->channel << sqr[i].shift; + stm32_adc_writel(adc, sqr[i].reg, val); } if (!i) return -EINVAL; /* Sequence len */ - val = stm32_adc_readl(adc, stm32f4_sq[0].reg); - val &= ~stm32f4_sq[0].mask; - val |= ((i - 1) << stm32f4_sq[0].shift); - stm32_adc_writel(adc, stm32f4_sq[0].reg, val); + val = stm32_adc_readl(adc, sqr[0].reg); + val &= ~sqr[0].mask; + val |= ((i - 1) << sqr[0].shift); + stm32_adc_writel(adc, sqr[0].reg, val); return 0; } @@ -412,19 +983,21 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev, * * Returns trigger extsel value, if trig matches, -EINVAL otherwise. */ -static int stm32_adc_get_trig_extsel(struct iio_trigger *trig) +static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev, + struct iio_trigger *trig) { + struct stm32_adc *adc = iio_priv(indio_dev); int i; /* lookup triggers registered by stm32 timer trigger driver */ - for (i = 0; stm32f4_adc_trigs[i].name; i++) { + for (i = 0; adc->cfg->trigs[i].name; i++) { /** * Checking both stm32 timer trigger type and trig name * should be safe against arbitrary trigger names. */ if (is_stm32_timer_trigger(trig) && - !strcmp(stm32f4_adc_trigs[i].name, trig->name)) { - return stm32f4_adc_trigs[i].extsel; + !strcmp(adc->cfg->trigs[i].name, trig->name)) { + return adc->cfg->trigs[i].extsel; } } @@ -449,7 +1022,7 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev, int ret; if (trig) { - ret = stm32_adc_get_trig_extsel(trig); + ret = stm32_adc_get_trig_extsel(indio_dev, trig); if (ret < 0) return ret; @@ -459,11 +1032,11 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev, } spin_lock_irqsave(&adc->lock, flags); - val = stm32_adc_readl(adc, STM32F4_ADC_CR2); - val &= ~(STM32F4_EXTEN_MASK | STM32F4_EXTSEL_MASK); - val |= exten << STM32F4_EXTEN_SHIFT; - val |= extsel << STM32F4_EXTSEL_SHIFT; - stm32_adc_writel(adc, STM32F4_ADC_CR2, val); + val = stm32_adc_readl(adc, adc->cfg->regs->exten.reg); + val &= ~(adc->cfg->regs->exten.mask | adc->cfg->regs->extsel.mask); + val |= exten << adc->cfg->regs->exten.shift; + val |= extsel << adc->cfg->regs->extsel.shift; + stm32_adc_writel(adc, adc->cfg->regs->exten.reg, val); spin_unlock_irqrestore(&adc->lock, flags); return 0; @@ -515,6 +1088,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, int *res) { struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_regspec *regs = adc->cfg->regs; long timeout; u32 val; int ret; @@ -523,21 +1097,27 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, adc->bufi = 0; + if (adc->cfg->prepare) { + ret = adc->cfg->prepare(adc); + if (ret) + return ret; + } + /* Program chan number in regular sequence (SQ1) */ - val = stm32_adc_readl(adc, stm32f4_sq[1].reg); - val &= ~stm32f4_sq[1].mask; - val |= chan->channel << stm32f4_sq[1].shift; - stm32_adc_writel(adc, stm32f4_sq[1].reg, val); + val = stm32_adc_readl(adc, regs->sqr[1].reg); + val &= ~regs->sqr[1].mask; + val |= chan->channel << regs->sqr[1].shift; + stm32_adc_writel(adc, regs->sqr[1].reg, val); /* Set regular sequence len (0 for 1 conversion) */ - stm32_adc_clr_bits(adc, stm32f4_sq[0].reg, stm32f4_sq[0].mask); + stm32_adc_clr_bits(adc, regs->sqr[0].reg, regs->sqr[0].mask); /* Trigger detection disabled (conversion can be launched in SW) */ - stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK); + stm32_adc_clr_bits(adc, regs->exten.reg, regs->exten.mask); stm32_adc_conv_irq_enable(adc); - stm32_adc_start_conv(adc, false); + adc->cfg->start_conv(adc, false); timeout = wait_for_completion_interruptible_timeout( &adc->completion, STM32_ADC_TIMEOUT); @@ -550,10 +1130,13 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, ret = IIO_VAL_INT; } - stm32_adc_stop_conv(adc); + adc->cfg->stop_conv(adc); stm32_adc_conv_irq_disable(adc); + if (adc->cfg->unprepare) + adc->cfg->unprepare(adc); + return ret; } @@ -590,11 +1173,12 @@ static irqreturn_t stm32_adc_isr(int irq, void *data) { struct stm32_adc *adc = data; struct iio_dev *indio_dev = iio_priv_to_dev(adc); - u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR); + const struct stm32_adc_regspec *regs = adc->cfg->regs; + u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg); - if (status & STM32F4_EOC) { + if (status & regs->isr_eoc.mask) { /* Reading DR also clears EOC status flag */ - adc->buffer[adc->bufi] = stm32_adc_readw(adc, STM32F4_ADC_DR); + adc->buffer[adc->bufi] = stm32_adc_readw(adc, regs->dr); if (iio_buffer_enabled(indio_dev)) { adc->bufi++; if (adc->bufi >= adc->num_conv) { @@ -621,7 +1205,7 @@ static irqreturn_t stm32_adc_isr(int irq, void *data) static int stm32_adc_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) { - return stm32_adc_get_trig_extsel(trig) < 0 ? -EINVAL : 0; + return stm32_adc_get_trig_extsel(indio_dev, trig) < 0 ? -EINVAL : 0; } static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) @@ -777,10 +1361,16 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev) struct stm32_adc *adc = iio_priv(indio_dev); int ret; + if (adc->cfg->prepare) { + ret = adc->cfg->prepare(adc); + if (ret) + return ret; + } + ret = stm32_adc_set_trig(indio_dev, indio_dev->trig); if (ret) { dev_err(&indio_dev->dev, "Can't set trigger\n"); - return ret; + goto err_unprepare; } ret = stm32_adc_dma_start(indio_dev); @@ -799,7 +1389,7 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev) if (!adc->dma_chan) stm32_adc_conv_irq_enable(adc); - stm32_adc_start_conv(adc, !!adc->dma_chan); + adc->cfg->start_conv(adc, !!adc->dma_chan); return 0; @@ -808,6 +1398,9 @@ err_stop_dma: dmaengine_terminate_all(adc->dma_chan); err_clr_trig: stm32_adc_set_trig(indio_dev, NULL); +err_unprepare: + if (adc->cfg->unprepare) + adc->cfg->unprepare(adc); return ret; } @@ -817,7 +1410,7 @@ static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev) struct stm32_adc *adc = iio_priv(indio_dev); int ret; - stm32_adc_stop_conv(adc); + adc->cfg->stop_conv(adc); if (!adc->dma_chan) stm32_adc_conv_irq_disable(adc); @@ -831,6 +1424,9 @@ static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev) if (stm32_adc_set_trig(indio_dev, NULL)) dev_err(&indio_dev->dev, "Can't clear trigger\n"); + if (adc->cfg->unprepare) + adc->cfg->unprepare(adc); + return ret; } @@ -895,12 +1491,12 @@ static int stm32_adc_of_get_resolution(struct iio_dev *indio_dev) u32 res; if (of_property_read_u32(node, "assigned-resolution-bits", &res)) - res = stm32f4_adc_resolutions[0]; + res = adc->cfg->adc_info->resolutions[0]; - for (i = 0; i < ARRAY_SIZE(stm32f4_adc_resolutions); i++) - if (res == stm32f4_adc_resolutions[i]) + for (i = 0; i < adc->cfg->adc_info->num_res; i++) + if (res == adc->cfg->adc_info->resolutions[i]) break; - if (i >= ARRAY_SIZE(stm32f4_adc_resolutions)) { + if (i >= adc->cfg->adc_info->num_res) { dev_err(&indio_dev->dev, "Bad resolution: %u bits\n", res); return -EINVAL; } @@ -926,14 +1522,19 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); chan->scan_type.sign = 'u'; - chan->scan_type.realbits = stm32f4_adc_resolutions[adc->res]; + chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res]; chan->scan_type.storagebits = 16; chan->ext_info = stm32_adc_ext_info; + + /* pre-build selected channels mask */ + adc->pcsel |= BIT(chan->channel); } static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) { struct device_node *node = indio_dev->dev.of_node; + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; struct property *prop; const __be32 *cur; struct iio_chan_spec *channels; @@ -942,7 +1543,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) num_channels = of_property_count_u32_elems(node, "st,adc-channels"); if (num_channels < 0 || - num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) { + num_channels >= adc_info->max_channels) { dev_err(&indio_dev->dev, "Bad st,adc-channels?\n"); return num_channels < 0 ? num_channels : -EINVAL; } @@ -953,12 +1554,12 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) return -ENOMEM; of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) { - if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) { + if (val >= adc_info->max_channels) { dev_err(&indio_dev->dev, "Invalid channel %d\n", val); return -EINVAL; } stm32_adc_chan_init_one(indio_dev, &channels[scan_index], - &stm32f4_adc123_channels[val], + &adc_info->channels[val], scan_index); scan_index++; } @@ -990,7 +1591,7 @@ static int stm32_adc_dma_request(struct iio_dev *indio_dev) /* Configure DMA channel to read data register */ memset(&config, 0, sizeof(config)); config.src_addr = (dma_addr_t)adc->common->phys_base; - config.src_addr += adc->offset + STM32F4_ADC_DR; + config.src_addr += adc->offset + adc->cfg->regs->dr; config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; ret = dmaengine_slave_config(adc->dma_chan, &config); @@ -1011,6 +1612,7 @@ err_release: static int stm32_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; struct stm32_adc *adc; int ret; @@ -1025,6 +1627,8 @@ static int stm32_adc_probe(struct platform_device *pdev) adc->common = dev_get_drvdata(pdev->dev.parent); spin_lock_init(&adc->lock); init_completion(&adc->completion); + adc->cfg = (const struct stm32_adc_cfg *) + of_match_device(dev->driver->of_match_table, dev)->data; indio_dev->name = dev_name(&pdev->dev); indio_dev->dev.parent = &pdev->dev; @@ -1055,14 +1659,21 @@ static int stm32_adc_probe(struct platform_device *pdev) adc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(adc->clk)) { - dev_err(&pdev->dev, "Can't get clock\n"); - return PTR_ERR(adc->clk); + ret = PTR_ERR(adc->clk); + if (ret == -ENOENT && !adc->cfg->clk_required) { + adc->clk = NULL; + } else { + dev_err(&pdev->dev, "Can't get clock\n"); + return ret; + } } - ret = clk_prepare_enable(adc->clk); - if (ret < 0) { - dev_err(&pdev->dev, "clk enable failed\n"); - return ret; + if (adc->clk) { + ret = clk_prepare_enable(adc->clk); + if (ret < 0) { + dev_err(&pdev->dev, "clk enable failed\n"); + return ret; + } } ret = stm32_adc_of_get_resolution(indio_dev); @@ -1070,6 +1681,12 @@ static int stm32_adc_probe(struct platform_device *pdev) goto err_clk_disable; stm32_adc_set_res(adc); + if (adc->cfg->selfcalib) { + ret = adc->cfg->selfcalib(adc); + if (ret) + goto err_clk_disable; + } + ret = stm32_adc_chan_of_init(indio_dev); if (ret < 0) goto err_clk_disable; @@ -1106,7 +1723,8 @@ err_dma_disable: dma_release_channel(adc->dma_chan); } err_clk_disable: - clk_disable_unprepare(adc->clk); + if (adc->clk) + clk_disable_unprepare(adc->clk); return ret; } @@ -1124,13 +1742,35 @@ static int stm32_adc_remove(struct platform_device *pdev) adc->rx_buf, adc->rx_dma_buf); dma_release_channel(adc->dma_chan); } - clk_disable_unprepare(adc->clk); + if (adc->clk) + clk_disable_unprepare(adc->clk); return 0; } +static const struct stm32_adc_cfg stm32f4_adc_cfg = { + .regs = &stm32f4_adc_regspec, + .adc_info = &stm32f4_adc_info, + .trigs = stm32f4_adc_trigs, + .clk_required = true, + .start_conv = stm32f4_adc_start_conv, + .stop_conv = stm32f4_adc_stop_conv, +}; + +static const struct stm32_adc_cfg stm32h7_adc_cfg = { + .regs = &stm32h7_adc_regspec, + .adc_info = &stm32h7_adc_info, + .trigs = stm32h7_adc_trigs, + .selfcalib = stm32h7_adc_selfcalib, + .start_conv = stm32h7_adc_start_conv, + .stop_conv = stm32h7_adc_stop_conv, + .prepare = stm32h7_adc_prepare, + .unprepare = stm32h7_adc_unprepare, +}; + static const struct of_device_id stm32_adc_of_match[] = { - { .compatible = "st,stm32f4-adc" }, + { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg }, + { .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg }, {}, }; MODULE_DEVICE_TABLE(of, stm32_adc_of_match); |