// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018, STMicroelectronics - All Rights Reserved * Author: Fabrice Gasnier * * Originally based on the Linux kernel v4.18 drivers/iio/adc/stm32-adc-core.c. */ #include #include #include #include #include #include #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; unsigned int i; int 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 = sizeof(struct stm32_adc_common), };