]>
Commit | Line | Data |
---|---|---|
a466ecec FG |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2018, STMicroelectronics - All Rights Reserved | |
4 | * Author: Fabrice Gasnier <[email protected]> | |
5 | * | |
6 | * Originally based on the Linux kernel v4.18 drivers/iio/adc/stm32-adc.c. | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <adc.h> | |
c273da07 | 11 | #include <dm.h> |
a466ecec | 12 | #include <asm/io.h> |
336d4615 | 13 | #include <dm/device_compat.h> |
cd93d625 | 14 | #include <linux/bitops.h> |
c05ed00a | 15 | #include <linux/delay.h> |
a466ecec FG |
16 | #include <linux/iopoll.h> |
17 | #include "stm32-adc-core.h" | |
18 | ||
19 | /* STM32H7 - Registers for each ADC instance */ | |
20 | #define STM32H7_ADC_ISR 0x00 | |
21 | #define STM32H7_ADC_CR 0x08 | |
22 | #define STM32H7_ADC_CFGR 0x0C | |
23 | #define STM32H7_ADC_SMPR1 0x14 | |
24 | #define STM32H7_ADC_SMPR2 0x18 | |
25 | #define STM32H7_ADC_PCSEL 0x1C | |
26 | #define STM32H7_ADC_SQR1 0x30 | |
27 | #define STM32H7_ADC_DR 0x40 | |
28 | #define STM32H7_ADC_DIFSEL 0xC0 | |
29 | ||
30 | /* STM32H7_ADC_ISR - bit fields */ | |
31 | #define STM32MP1_VREGREADY BIT(12) | |
32 | #define STM32H7_EOC BIT(2) | |
33 | #define STM32H7_ADRDY BIT(0) | |
34 | ||
35 | /* STM32H7_ADC_CR - bit fields */ | |
36 | #define STM32H7_DEEPPWD BIT(29) | |
37 | #define STM32H7_ADVREGEN BIT(28) | |
38 | #define STM32H7_BOOST BIT(8) | |
39 | #define STM32H7_ADSTART BIT(2) | |
40 | #define STM32H7_ADDIS BIT(1) | |
41 | #define STM32H7_ADEN BIT(0) | |
42 | ||
43 | /* STM32H7_ADC_CFGR bit fields */ | |
44 | #define STM32H7_EXTEN GENMASK(11, 10) | |
45 | #define STM32H7_DMNGT GENMASK(1, 0) | |
46 | ||
47 | /* STM32H7_ADC_SQR1 - bit fields */ | |
48 | #define STM32H7_SQ1_SHIFT 6 | |
49 | ||
50 | /* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */ | |
51 | #define STM32H7_BOOST_CLKRATE 20000000UL | |
52 | ||
53 | #define STM32_ADC_CH_MAX 20 /* max number of channels */ | |
54 | #define STM32_ADC_TIMEOUT_US 100000 | |
55 | ||
56 | struct stm32_adc_cfg { | |
57 | unsigned int max_channels; | |
58 | unsigned int num_bits; | |
59 | bool has_vregready; | |
60 | }; | |
61 | ||
62 | struct stm32_adc { | |
63 | void __iomem *regs; | |
64 | int active_channel; | |
65 | const struct stm32_adc_cfg *cfg; | |
66 | }; | |
67 | ||
68 | static int stm32_adc_stop(struct udevice *dev) | |
69 | { | |
70 | struct stm32_adc *adc = dev_get_priv(dev); | |
71 | ||
72 | setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADDIS); | |
73 | clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST); | |
74 | /* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */ | |
75 | setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD); | |
76 | adc->active_channel = -1; | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
81 | static int stm32_adc_start_channel(struct udevice *dev, int channel) | |
82 | { | |
83 | struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
84 | struct stm32_adc_common *common = dev_get_priv(dev_get_parent(dev)); | |
85 | struct stm32_adc *adc = dev_get_priv(dev); | |
86 | int ret; | |
87 | u32 val; | |
88 | ||
89 | /* Exit deep power down, then enable ADC voltage regulator */ | |
90 | clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD); | |
91 | setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADVREGEN); | |
92 | if (common->rate > STM32H7_BOOST_CLKRATE) | |
93 | setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST); | |
94 | ||
95 | /* Wait for startup time */ | |
96 | if (!adc->cfg->has_vregready) { | |
97 | udelay(20); | |
98 | } else { | |
99 | ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val, | |
100 | val & STM32MP1_VREGREADY, | |
101 | STM32_ADC_TIMEOUT_US); | |
102 | if (ret < 0) { | |
103 | stm32_adc_stop(dev); | |
104 | dev_err(dev, "Failed to enable vreg: %d\n", ret); | |
105 | return ret; | |
106 | } | |
107 | } | |
108 | ||
109 | /* Only use single ended channels */ | |
110 | writel(0, adc->regs + STM32H7_ADC_DIFSEL); | |
111 | ||
112 | /* Enable ADC, Poll for ADRDY to be set (after adc startup time) */ | |
113 | setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADEN); | |
114 | ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val, | |
115 | val & STM32H7_ADRDY, STM32_ADC_TIMEOUT_US); | |
116 | if (ret < 0) { | |
117 | stm32_adc_stop(dev); | |
118 | dev_err(dev, "Failed to enable ADC: %d\n", ret); | |
119 | return ret; | |
120 | } | |
121 | ||
122 | /* Preselect channels */ | |
123 | writel(uc_pdata->channel_mask, adc->regs + STM32H7_ADC_PCSEL); | |
124 | ||
125 | /* Set sampling time to max value by default */ | |
126 | writel(0xffffffff, adc->regs + STM32H7_ADC_SMPR1); | |
127 | writel(0xffffffff, adc->regs + STM32H7_ADC_SMPR2); | |
128 | ||
129 | /* Program regular sequence: chan in SQ1 & len = 0 for one channel */ | |
130 | writel(channel << STM32H7_SQ1_SHIFT, adc->regs + STM32H7_ADC_SQR1); | |
131 | ||
132 | /* Trigger detection disabled (conversion can be launched in SW) */ | |
133 | clrbits_le32(adc->regs + STM32H7_ADC_CFGR, STM32H7_EXTEN | | |
134 | STM32H7_DMNGT); | |
135 | adc->active_channel = channel; | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
140 | static int stm32_adc_channel_data(struct udevice *dev, int channel, | |
141 | unsigned int *data) | |
142 | { | |
143 | struct stm32_adc *adc = dev_get_priv(dev); | |
144 | int ret; | |
145 | u32 val; | |
146 | ||
147 | if (channel != adc->active_channel) { | |
148 | dev_err(dev, "Requested channel is not active!\n"); | |
149 | return -EINVAL; | |
150 | } | |
151 | ||
152 | setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADSTART); | |
153 | ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val, | |
154 | val & STM32H7_EOC, STM32_ADC_TIMEOUT_US); | |
155 | if (ret < 0) { | |
156 | dev_err(dev, "conversion timed out: %d\n", ret); | |
157 | return ret; | |
158 | } | |
159 | ||
160 | *data = readl(adc->regs + STM32H7_ADC_DR); | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
165 | static int stm32_adc_chan_of_init(struct udevice *dev) | |
166 | { | |
167 | struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
168 | struct stm32_adc *adc = dev_get_priv(dev); | |
169 | u32 chans[STM32_ADC_CH_MAX]; | |
0fb03656 PD |
170 | unsigned int i, num_channels; |
171 | int ret; | |
a466ecec FG |
172 | |
173 | /* Retrieve single ended channels listed in device tree */ | |
0fb03656 PD |
174 | ret = dev_read_size(dev, "st,adc-channels"); |
175 | if (ret < 0) { | |
176 | dev_err(dev, "can't get st,adc-channels: %d\n", ret); | |
177 | return ret; | |
a466ecec | 178 | } |
0fb03656 | 179 | num_channels = ret / sizeof(u32); |
a466ecec FG |
180 | |
181 | if (num_channels > adc->cfg->max_channels) { | |
182 | dev_err(dev, "too many st,adc-channels: %d\n", num_channels); | |
183 | return -EINVAL; | |
184 | } | |
185 | ||
186 | ret = dev_read_u32_array(dev, "st,adc-channels", chans, num_channels); | |
187 | if (ret < 0) { | |
188 | dev_err(dev, "can't read st,adc-channels: %d\n", ret); | |
189 | return ret; | |
190 | } | |
191 | ||
192 | for (i = 0; i < num_channels; i++) { | |
193 | if (chans[i] >= adc->cfg->max_channels) { | |
194 | dev_err(dev, "bad channel %u\n", chans[i]); | |
195 | return -EINVAL; | |
196 | } | |
197 | uc_pdata->channel_mask |= 1 << chans[i]; | |
198 | } | |
199 | ||
200 | uc_pdata->data_mask = (1 << adc->cfg->num_bits) - 1; | |
201 | uc_pdata->data_format = ADC_DATA_FORMAT_BIN; | |
202 | uc_pdata->data_timeout_us = 100000; | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | static int stm32_adc_probe(struct udevice *dev) | |
208 | { | |
209 | struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); | |
210 | struct stm32_adc_common *common = dev_get_priv(dev_get_parent(dev)); | |
211 | struct stm32_adc *adc = dev_get_priv(dev); | |
212 | int offset; | |
213 | ||
214 | offset = dev_read_u32_default(dev, "reg", -ENODATA); | |
215 | if (offset < 0) { | |
216 | dev_err(dev, "Can't read reg property\n"); | |
217 | return offset; | |
218 | } | |
219 | adc->regs = common->base + offset; | |
220 | adc->cfg = (const struct stm32_adc_cfg *)dev_get_driver_data(dev); | |
221 | ||
222 | /* VDD supplied by common vref pin */ | |
223 | uc_pdata->vdd_supply = common->vref; | |
224 | uc_pdata->vdd_microvolts = common->vref_uv; | |
225 | uc_pdata->vss_microvolts = 0; | |
226 | ||
227 | return stm32_adc_chan_of_init(dev); | |
228 | } | |
229 | ||
230 | static const struct adc_ops stm32_adc_ops = { | |
231 | .start_channel = stm32_adc_start_channel, | |
232 | .channel_data = stm32_adc_channel_data, | |
233 | .stop = stm32_adc_stop, | |
234 | }; | |
235 | ||
236 | static const struct stm32_adc_cfg stm32h7_adc_cfg = { | |
237 | .num_bits = 16, | |
238 | .max_channels = STM32_ADC_CH_MAX, | |
239 | }; | |
240 | ||
241 | static const struct stm32_adc_cfg stm32mp1_adc_cfg = { | |
242 | .num_bits = 16, | |
243 | .max_channels = STM32_ADC_CH_MAX, | |
244 | .has_vregready = true, | |
245 | }; | |
246 | ||
247 | static const struct udevice_id stm32_adc_ids[] = { | |
248 | { .compatible = "st,stm32h7-adc", | |
249 | .data = (ulong)&stm32h7_adc_cfg }, | |
250 | { .compatible = "st,stm32mp1-adc", | |
251 | .data = (ulong)&stm32mp1_adc_cfg }, | |
252 | {} | |
253 | }; | |
254 | ||
255 | U_BOOT_DRIVER(stm32_adc) = { | |
256 | .name = "stm32-adc", | |
257 | .id = UCLASS_ADC, | |
258 | .of_match = stm32_adc_ids, | |
259 | .probe = stm32_adc_probe, | |
260 | .ops = &stm32_adc_ops, | |
41575d8e | 261 | .priv_auto = sizeof(struct stm32_adc), |
a466ecec | 262 | }; |