blob: 04b6a8a2f5b5db216c0b4c8290b7dc03500c6001 [file] [log] [blame]
Fabrice Gasniera466ece2018-07-24 16:31:31 +02001// 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 */
32struct stm32h7_adc_ck_spec {
33 u32 ckmode;
34 u32 presc;
35 int div;
36};
37
38static 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
58static int stm32h7_adc_clk_sel(struct udevice *dev,
59 struct stm32_adc_common *common)
60{
61 u32 ckmode, presc;
62 unsigned long rate;
Patrick Delaunayc0d7f1f2019-06-21 15:26:45 +020063 unsigned int i;
64 int div;
Fabrice Gasniera466ece2018-07-24 16:31:31 +020065
66 /* stm32h7 bus clock is common for all ADC instances (mandatory) */
67 if (!clk_valid(&common->bclk)) {
68 dev_err(dev, "No bclk clock found\n");
69 return -ENOENT;
70 }
71
72 /*
73 * stm32h7 can use either 'bus' or 'adc' clock for analog circuitry.
74 * So, choice is to have bus clock mandatory and adc clock optional.
75 * If optional 'adc' clock has been found, then try to use it first.
76 */
77 if (clk_valid(&common->aclk)) {
78 /*
79 * Asynchronous clock modes (e.g. ckmode == 0)
80 * From spec: PLL output musn't exceed max rate
81 */
82 rate = clk_get_rate(&common->aclk);
83 if (!rate) {
84 dev_err(dev, "Invalid aclk rate: 0\n");
85 return -EINVAL;
86 }
87
88 for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
89 ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
90 presc = stm32h7_adc_ckmodes_spec[i].presc;
91 div = stm32h7_adc_ckmodes_spec[i].div;
92
93 if (ckmode)
94 continue;
95
96 if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
97 goto out;
98 }
99 }
100
101 /* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */
102 rate = clk_get_rate(&common->bclk);
103 if (!rate) {
104 dev_err(dev, "Invalid bus clock rate: 0\n");
105 return -EINVAL;
106 }
107
108 for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
109 ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
110 presc = stm32h7_adc_ckmodes_spec[i].presc;
111 div = stm32h7_adc_ckmodes_spec[i].div;
112
113 if (!ckmode)
114 continue;
115
116 if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
117 goto out;
118 }
119
120 dev_err(dev, "clk selection failed\n");
121 return -EINVAL;
122
123out:
124 /* rate used later by each ADC instance to control BOOST mode */
125 common->rate = rate / div;
126
127 /* Set common clock mode and prescaler */
128 clrsetbits_le32(common->base + STM32H7_ADC_CCR,
129 STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK,
130 ckmode << STM32H7_CKMODE_SHIFT |
131 presc << STM32H7_PRESC_SHIFT);
132
133 dev_dbg(dev, "Using %s clock/%d source at %ld kHz\n",
134 ckmode ? "bus" : "adc", div, common->rate / 1000);
135
136 return 0;
137}
138
139static int stm32_adc_core_probe(struct udevice *dev)
140{
141 struct stm32_adc_common *common = dev_get_priv(dev);
142 int ret;
143
144 common->base = dev_read_addr_ptr(dev);
145 if (!common->base) {
146 dev_err(dev, "can't get address\n");
147 return -ENOENT;
148 }
149
150 ret = device_get_supply_regulator(dev, "vref-supply", &common->vref);
151 if (ret) {
152 dev_err(dev, "can't get vref-supply: %d\n", ret);
153 return ret;
154 }
155
156 ret = regulator_get_value(common->vref);
157 if (ret < 0) {
158 dev_err(dev, "can't get vref-supply value: %d\n", ret);
159 return ret;
160 }
161 common->vref_uv = ret;
162
163 ret = clk_get_by_name(dev, "adc", &common->aclk);
164 if (!ret) {
165 ret = clk_enable(&common->aclk);
166 if (ret) {
167 dev_err(dev, "Can't enable aclk: %d\n", ret);
168 return ret;
169 }
170 }
171
172 ret = clk_get_by_name(dev, "bus", &common->bclk);
173 if (!ret) {
174 ret = clk_enable(&common->bclk);
175 if (ret) {
176 dev_err(dev, "Can't enable bclk: %d\n", ret);
177 goto err_aclk_disable;
178 }
179 }
180
181 ret = stm32h7_adc_clk_sel(dev, common);
182 if (ret)
183 goto err_bclk_disable;
184
185 return ret;
186
187err_bclk_disable:
188 if (clk_valid(&common->bclk))
189 clk_disable(&common->bclk);
190
191err_aclk_disable:
192 if (clk_valid(&common->aclk))
193 clk_disable(&common->aclk);
194
195 return ret;
196}
197
198static const struct udevice_id stm32_adc_core_ids[] = {
199 { .compatible = "st,stm32h7-adc-core" },
200 { .compatible = "st,stm32mp1-adc-core" },
201 {}
202};
203
204U_BOOT_DRIVER(stm32_adc_core) = {
205 .name = "stm32-adc-core",
206 .id = UCLASS_SIMPLE_BUS,
207 .of_match = stm32_adc_core_ids,
208 .probe = stm32_adc_core_probe,
209 .priv_auto_alloc_size = sizeof(struct stm32_adc_common),
210};