123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- // 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-core.c.
- */
- #include <common.h>
- #include <asm/io.h>
- #include <power/regulator.h>
- #include "stm32-adc-core.h"
- /* STM32H7 - common registers for all ADC instances */
- #define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
- /* STM32H7_ADC_CCR - bit fields */
- #define STM32H7_PRESC_SHIFT 18
- #define STM32H7_PRESC_MASK GENMASK(21, 18)
- #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
- /**
- * struct stm32h7_adc_ck_spec - specification for stm32h7 adc clock
- * @ckmode: ADC clock mode, Async or sync with prescaler.
- * @presc: prescaler bitfield for async clock mode
- * @div: prescaler division ratio
- */
- struct stm32h7_adc_ck_spec {
- u32 ckmode;
- u32 presc;
- int div;
- };
- static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
- /* 00: CK_ADC[1..3]: Asynchronous clock modes */
- { 0, 0, 1 },
- { 0, 1, 2 },
- { 0, 2, 4 },
- { 0, 3, 6 },
- { 0, 4, 8 },
- { 0, 5, 10 },
- { 0, 6, 12 },
- { 0, 7, 16 },
- { 0, 8, 32 },
- { 0, 9, 64 },
- { 0, 10, 128 },
- { 0, 11, 256 },
- /* HCLK used: Synchronous clock modes (1, 2 or 4 prescaler) */
- { 1, 0, 1 },
- { 2, 0, 2 },
- { 3, 0, 4 },
- };
- static int stm32h7_adc_clk_sel(struct udevice *dev,
- struct stm32_adc_common *common)
- {
- u32 ckmode, presc;
- unsigned long rate;
- int i, div;
- /* stm32h7 bus clock is common for all ADC instances (mandatory) */
- if (!clk_valid(&common->bclk)) {
- dev_err(dev, "No bclk clock found\n");
- return -ENOENT;
- }
- /*
- * stm32h7 can use either 'bus' or 'adc' clock for analog circuitry.
- * So, choice is to have bus clock mandatory and adc clock optional.
- * If optional 'adc' clock has been found, then try to use it first.
- */
- if (clk_valid(&common->aclk)) {
- /*
- * Asynchronous clock modes (e.g. ckmode == 0)
- * From spec: PLL output musn't exceed max rate
- */
- rate = clk_get_rate(&common->aclk);
- if (!rate) {
- dev_err(dev, "Invalid aclk rate: 0\n");
- return -EINVAL;
- }
- for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
- ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
- presc = stm32h7_adc_ckmodes_spec[i].presc;
- div = stm32h7_adc_ckmodes_spec[i].div;
- if (ckmode)
- continue;
- if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
- goto out;
- }
- }
- /* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */
- rate = clk_get_rate(&common->bclk);
- if (!rate) {
- dev_err(dev, "Invalid bus clock rate: 0\n");
- return -EINVAL;
- }
- for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
- ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
- presc = stm32h7_adc_ckmodes_spec[i].presc;
- div = stm32h7_adc_ckmodes_spec[i].div;
- if (!ckmode)
- continue;
- if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
- goto out;
- }
- dev_err(dev, "clk selection failed\n");
- return -EINVAL;
- out:
- /* rate used later by each ADC instance to control BOOST mode */
- common->rate = rate / div;
- /* Set common clock mode and prescaler */
- clrsetbits_le32(common->base + STM32H7_ADC_CCR,
- STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK,
- ckmode << STM32H7_CKMODE_SHIFT |
- presc << STM32H7_PRESC_SHIFT);
- dev_dbg(dev, "Using %s clock/%d source at %ld kHz\n",
- ckmode ? "bus" : "adc", div, common->rate / 1000);
- return 0;
- }
- static int stm32_adc_core_probe(struct udevice *dev)
- {
- struct stm32_adc_common *common = dev_get_priv(dev);
- int ret;
- common->base = dev_read_addr_ptr(dev);
- if (!common->base) {
- dev_err(dev, "can't get address\n");
- return -ENOENT;
- }
- ret = device_get_supply_regulator(dev, "vref-supply", &common->vref);
- if (ret) {
- dev_err(dev, "can't get vref-supply: %d\n", ret);
- return ret;
- }
- ret = regulator_get_value(common->vref);
- if (ret < 0) {
- dev_err(dev, "can't get vref-supply value: %d\n", ret);
- return ret;
- }
- common->vref_uv = ret;
- ret = clk_get_by_name(dev, "adc", &common->aclk);
- if (!ret) {
- ret = clk_enable(&common->aclk);
- if (ret) {
- dev_err(dev, "Can't enable aclk: %d\n", ret);
- return ret;
- }
- }
- ret = clk_get_by_name(dev, "bus", &common->bclk);
- if (!ret) {
- ret = clk_enable(&common->bclk);
- if (ret) {
- dev_err(dev, "Can't enable bclk: %d\n", ret);
- goto err_aclk_disable;
- }
- }
- ret = stm32h7_adc_clk_sel(dev, common);
- if (ret)
- goto err_bclk_disable;
- return ret;
- err_bclk_disable:
- if (clk_valid(&common->bclk))
- clk_disable(&common->bclk);
- err_aclk_disable:
- if (clk_valid(&common->aclk))
- clk_disable(&common->aclk);
- return ret;
- }
- static const struct udevice_id stm32_adc_core_ids[] = {
- { .compatible = "st,stm32h7-adc-core" },
- { .compatible = "st,stm32mp1-adc-core" },
- {}
- };
- U_BOOT_DRIVER(stm32_adc_core) = {
- .name = "stm32-adc-core",
- .id = UCLASS_SIMPLE_BUS,
- .of_match = stm32_adc_core_ids,
- .probe = stm32_adc_core_probe,
- .priv_auto_alloc_size = sizeof(struct stm32_adc_common),
- };
|