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