xref: /openbmc/u-boot/drivers/adc/stm32-adc-core.c (revision 09d84117)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
4  * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
5  *
6  * Originally based on the Linux kernel v4.18 drivers/iio/adc/stm32-adc-core.c.
7  */
8 
9 #include <common.h>
10 #include <asm/io.h>
11 #include <power/regulator.h>
12 #include "stm32-adc-core.h"
13 
14 /* STM32H7 - common registers for all ADC instances */
15 #define STM32H7_ADC_CCR			(STM32_ADCX_COMN_OFFSET + 0x08)
16 
17 /* STM32H7_ADC_CCR - bit fields */
18 #define STM32H7_PRESC_SHIFT		18
19 #define STM32H7_PRESC_MASK		GENMASK(21, 18)
20 #define STM32H7_CKMODE_SHIFT		16
21 #define STM32H7_CKMODE_MASK		GENMASK(17, 16)
22 
23 /* STM32 H7 maximum analog clock rate (from datasheet) */
24 #define STM32H7_ADC_MAX_CLK_RATE	36000000
25 
26 /**
27  * struct stm32h7_adc_ck_spec - specification for stm32h7 adc clock
28  * @ckmode: ADC clock mode, Async or sync with prescaler.
29  * @presc: prescaler bitfield for async clock mode
30  * @div: prescaler division ratio
31  */
32 struct stm32h7_adc_ck_spec {
33 	u32 ckmode;
34 	u32 presc;
35 	int div;
36 };
37 
38 static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
39 	/* 00: CK_ADC[1..3]: Asynchronous clock modes */
40 	{ 0, 0, 1 },
41 	{ 0, 1, 2 },
42 	{ 0, 2, 4 },
43 	{ 0, 3, 6 },
44 	{ 0, 4, 8 },
45 	{ 0, 5, 10 },
46 	{ 0, 6, 12 },
47 	{ 0, 7, 16 },
48 	{ 0, 8, 32 },
49 	{ 0, 9, 64 },
50 	{ 0, 10, 128 },
51 	{ 0, 11, 256 },
52 	/* HCLK used: Synchronous clock modes (1, 2 or 4 prescaler) */
53 	{ 1, 0, 1 },
54 	{ 2, 0, 2 },
55 	{ 3, 0, 4 },
56 };
57 
58 static int stm32h7_adc_clk_sel(struct udevice *dev,
59 			       struct stm32_adc_common *common)
60 {
61 	u32 ckmode, presc;
62 	unsigned long rate;
63 	int i, div;
64 
65 	/* stm32h7 bus clock is common for all ADC instances (mandatory) */
66 	if (!clk_valid(&common->bclk)) {
67 		dev_err(dev, "No bclk clock found\n");
68 		return -ENOENT;
69 	}
70 
71 	/*
72 	 * stm32h7 can use either 'bus' or 'adc' clock for analog circuitry.
73 	 * So, choice is to have bus clock mandatory and adc clock optional.
74 	 * If optional 'adc' clock has been found, then try to use it first.
75 	 */
76 	if (clk_valid(&common->aclk)) {
77 		/*
78 		 * Asynchronous clock modes (e.g. ckmode == 0)
79 		 * From spec: PLL output musn't exceed max rate
80 		 */
81 		rate = clk_get_rate(&common->aclk);
82 		if (!rate) {
83 			dev_err(dev, "Invalid aclk rate: 0\n");
84 			return -EINVAL;
85 		}
86 
87 		for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
88 			ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
89 			presc = stm32h7_adc_ckmodes_spec[i].presc;
90 			div = stm32h7_adc_ckmodes_spec[i].div;
91 
92 			if (ckmode)
93 				continue;
94 
95 			if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
96 				goto out;
97 		}
98 	}
99 
100 	/* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */
101 	rate = clk_get_rate(&common->bclk);
102 	if (!rate) {
103 		dev_err(dev, "Invalid bus clock rate: 0\n");
104 		return -EINVAL;
105 	}
106 
107 	for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
108 		ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
109 		presc = stm32h7_adc_ckmodes_spec[i].presc;
110 		div = stm32h7_adc_ckmodes_spec[i].div;
111 
112 		if (!ckmode)
113 			continue;
114 
115 		if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
116 			goto out;
117 	}
118 
119 	dev_err(dev, "clk selection failed\n");
120 	return -EINVAL;
121 
122 out:
123 	/* rate used later by each ADC instance to control BOOST mode */
124 	common->rate = rate / div;
125 
126 	/* Set common clock mode and prescaler */
127 	clrsetbits_le32(common->base + STM32H7_ADC_CCR,
128 			STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK,
129 			ckmode << STM32H7_CKMODE_SHIFT |
130 			presc << STM32H7_PRESC_SHIFT);
131 
132 	dev_dbg(dev, "Using %s clock/%d source at %ld kHz\n",
133 		ckmode ? "bus" : "adc", div, common->rate / 1000);
134 
135 	return 0;
136 }
137 
138 static int stm32_adc_core_probe(struct udevice *dev)
139 {
140 	struct stm32_adc_common *common = dev_get_priv(dev);
141 	int ret;
142 
143 	common->base = dev_read_addr_ptr(dev);
144 	if (!common->base) {
145 		dev_err(dev, "can't get address\n");
146 		return -ENOENT;
147 	}
148 
149 	ret = device_get_supply_regulator(dev, "vref-supply", &common->vref);
150 	if (ret) {
151 		dev_err(dev, "can't get vref-supply: %d\n", ret);
152 		return ret;
153 	}
154 
155 	ret = regulator_get_value(common->vref);
156 	if (ret < 0) {
157 		dev_err(dev, "can't get vref-supply value: %d\n", ret);
158 		return ret;
159 	}
160 	common->vref_uv = ret;
161 
162 	ret = clk_get_by_name(dev, "adc", &common->aclk);
163 	if (!ret) {
164 		ret = clk_enable(&common->aclk);
165 		if (ret) {
166 			dev_err(dev, "Can't enable aclk: %d\n", ret);
167 			return ret;
168 		}
169 	}
170 
171 	ret = clk_get_by_name(dev, "bus", &common->bclk);
172 	if (!ret) {
173 		ret = clk_enable(&common->bclk);
174 		if (ret) {
175 			dev_err(dev, "Can't enable bclk: %d\n", ret);
176 			goto err_aclk_disable;
177 		}
178 	}
179 
180 	ret = stm32h7_adc_clk_sel(dev, common);
181 	if (ret)
182 		goto err_bclk_disable;
183 
184 	return ret;
185 
186 err_bclk_disable:
187 	if (clk_valid(&common->bclk))
188 		clk_disable(&common->bclk);
189 
190 err_aclk_disable:
191 	if (clk_valid(&common->aclk))
192 		clk_disable(&common->aclk);
193 
194 	return ret;
195 }
196 
197 static const struct udevice_id stm32_adc_core_ids[] = {
198 	{ .compatible = "st,stm32h7-adc-core" },
199 	{ .compatible = "st,stm32mp1-adc-core" },
200 	{}
201 };
202 
203 U_BOOT_DRIVER(stm32_adc_core) = {
204 	.name  = "stm32-adc-core",
205 	.id = UCLASS_SIMPLE_BUS,
206 	.of_match = stm32_adc_core_ids,
207 	.probe = stm32_adc_core_probe,
208 	.priv_auto_alloc_size = sizeof(struct stm32_adc_common),
209 };
210