123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
- * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
- *
- * Originally based on the Linux kernel v4.18 drivers/iio/adc/stm32-adc.c.
- */
- #include <common.h>
- #include <adc.h>
- #include <asm/io.h>
- #include <linux/iopoll.h>
- #include "stm32-adc-core.h"
- /* STM32H7 - Registers for each ADC instance */
- #define STM32H7_ADC_ISR 0x00
- #define STM32H7_ADC_CR 0x08
- #define STM32H7_ADC_CFGR 0x0C
- #define STM32H7_ADC_SMPR1 0x14
- #define STM32H7_ADC_SMPR2 0x18
- #define STM32H7_ADC_PCSEL 0x1C
- #define STM32H7_ADC_SQR1 0x30
- #define STM32H7_ADC_DR 0x40
- #define STM32H7_ADC_DIFSEL 0xC0
- /* STM32H7_ADC_ISR - bit fields */
- #define STM32MP1_VREGREADY BIT(12)
- #define STM32H7_EOC BIT(2)
- #define STM32H7_ADRDY BIT(0)
- /* STM32H7_ADC_CR - bit fields */
- #define STM32H7_DEEPPWD BIT(29)
- #define STM32H7_ADVREGEN BIT(28)
- #define STM32H7_BOOST BIT(8)
- #define STM32H7_ADSTART BIT(2)
- #define STM32H7_ADDIS BIT(1)
- #define STM32H7_ADEN BIT(0)
- /* STM32H7_ADC_CFGR bit fields */
- #define STM32H7_EXTEN GENMASK(11, 10)
- #define STM32H7_DMNGT GENMASK(1, 0)
- /* STM32H7_ADC_SQR1 - bit fields */
- #define STM32H7_SQ1_SHIFT 6
- /* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */
- #define STM32H7_BOOST_CLKRATE 20000000UL
- #define STM32_ADC_CH_MAX 20 /* max number of channels */
- #define STM32_ADC_TIMEOUT_US 100000
- struct stm32_adc_cfg {
- unsigned int max_channels;
- unsigned int num_bits;
- bool has_vregready;
- };
- struct stm32_adc {
- void __iomem *regs;
- int active_channel;
- const struct stm32_adc_cfg *cfg;
- };
- static int stm32_adc_stop(struct udevice *dev)
- {
- struct stm32_adc *adc = dev_get_priv(dev);
- setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADDIS);
- 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);
- adc->active_channel = -1;
- return 0;
- }
- static int stm32_adc_start_channel(struct udevice *dev, int channel)
- {
- struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
- struct stm32_adc_common *common = dev_get_priv(dev_get_parent(dev));
- struct stm32_adc *adc = dev_get_priv(dev);
- int ret;
- u32 val;
- /* 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)
- setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST);
- /* Wait for startup time */
- if (!adc->cfg->has_vregready) {
- udelay(20);
- } else {
- ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val,
- val & STM32MP1_VREGREADY,
- STM32_ADC_TIMEOUT_US);
- if (ret < 0) {
- stm32_adc_stop(dev);
- dev_err(dev, "Failed to enable vreg: %d\n", ret);
- return ret;
- }
- }
- /* Only use single ended channels */
- writel(0, adc->regs + STM32H7_ADC_DIFSEL);
- /* Enable ADC, Poll for ADRDY to be set (after adc startup time) */
- setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADEN);
- ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val,
- val & STM32H7_ADRDY, STM32_ADC_TIMEOUT_US);
- if (ret < 0) {
- stm32_adc_stop(dev);
- dev_err(dev, "Failed to enable ADC: %d\n", ret);
- return ret;
- }
- /* Preselect channels */
- 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);
- writel(0xffffffff, adc->regs + STM32H7_ADC_SMPR2);
- /* 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);
- adc->active_channel = channel;
- return 0;
- }
- static int stm32_adc_channel_data(struct udevice *dev, int channel,
- unsigned int *data)
- {
- struct stm32_adc *adc = dev_get_priv(dev);
- int ret;
- u32 val;
- if (channel != adc->active_channel) {
- dev_err(dev, "Requested channel is not active!\n");
- return -EINVAL;
- }
- setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADSTART);
- ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val,
- val & STM32H7_EOC, STM32_ADC_TIMEOUT_US);
- if (ret < 0) {
- dev_err(dev, "conversion timed out: %d\n", ret);
- return ret;
- }
- *data = readl(adc->regs + STM32H7_ADC_DR);
- return 0;
- }
- static int stm32_adc_chan_of_init(struct udevice *dev)
- {
- struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
- struct stm32_adc *adc = dev_get_priv(dev);
- u32 chans[STM32_ADC_CH_MAX];
- int i, num_channels, ret;
- /* Retrieve single ended channels listed in device tree */
- num_channels = dev_read_size(dev, "st,adc-channels");
- if (num_channels < 0) {
- dev_err(dev, "can't get st,adc-channels: %d\n", num_channels);
- return num_channels;
- }
- num_channels /= sizeof(u32);
- if (num_channels > adc->cfg->max_channels) {
- dev_err(dev, "too many st,adc-channels: %d\n", num_channels);
- return -EINVAL;
- }
- ret = dev_read_u32_array(dev, "st,adc-channels", chans, num_channels);
- if (ret < 0) {
- dev_err(dev, "can't read st,adc-channels: %d\n", ret);
- return ret;
- }
- for (i = 0; i < num_channels; i++) {
- if (chans[i] >= adc->cfg->max_channels) {
- dev_err(dev, "bad channel %u\n", chans[i]);
- return -EINVAL;
- }
- uc_pdata->channel_mask |= 1 << chans[i];
- }
- uc_pdata->data_mask = (1 << adc->cfg->num_bits) - 1;
- uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
- uc_pdata->data_timeout_us = 100000;
- return 0;
- }
- static int stm32_adc_probe(struct udevice *dev)
- {
- struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
- struct stm32_adc_common *common = dev_get_priv(dev_get_parent(dev));
- struct stm32_adc *adc = dev_get_priv(dev);
- int offset;
- offset = dev_read_u32_default(dev, "reg", -ENODATA);
- if (offset < 0) {
- dev_err(dev, "Can't read reg property\n");
- return offset;
- }
- adc->regs = common->base + offset;
- adc->cfg = (const struct stm32_adc_cfg *)dev_get_driver_data(dev);
- /* VDD supplied by common vref pin */
- uc_pdata->vdd_supply = common->vref;
- uc_pdata->vdd_microvolts = common->vref_uv;
- uc_pdata->vss_microvolts = 0;
- return stm32_adc_chan_of_init(dev);
- }
- static const struct adc_ops stm32_adc_ops = {
- .start_channel = stm32_adc_start_channel,
- .channel_data = stm32_adc_channel_data,
- .stop = stm32_adc_stop,
- };
- static const struct stm32_adc_cfg stm32h7_adc_cfg = {
- .num_bits = 16,
- .max_channels = STM32_ADC_CH_MAX,
- };
- static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
- .num_bits = 16,
- .max_channels = STM32_ADC_CH_MAX,
- .has_vregready = true,
- };
- static const struct udevice_id stm32_adc_ids[] = {
- { .compatible = "st,stm32h7-adc",
- .data = (ulong)&stm32h7_adc_cfg },
- { .compatible = "st,stm32mp1-adc",
- .data = (ulong)&stm32mp1_adc_cfg },
- {}
- };
- U_BOOT_DRIVER(stm32_adc) = {
- .name = "stm32-adc",
- .id = UCLASS_ADC,
- .of_match = stm32_adc_ids,
- .probe = stm32_adc_probe,
- .ops = &stm32_adc_ops,
- .priv_auto_alloc_size = sizeof(struct stm32_adc),
- };
|