]>
Commit | Line | Data |
---|---|---|
358bdf89 DT |
1 | /* |
2 | * Author: Daniel Thompson <daniel.thompson@linaro.org> | |
3 | * | |
4 | * Inspired by clk-asm9260.c . | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along with | |
16 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <linux/clk-provider.h> | |
20 | #include <linux/err.h> | |
21 | #include <linux/io.h> | |
861adc44 GF |
22 | #include <linux/iopoll.h> |
23 | #include <linux/ioport.h> | |
358bdf89 DT |
24 | #include <linux/slab.h> |
25 | #include <linux/spinlock.h> | |
26 | #include <linux/of.h> | |
27 | #include <linux/of_address.h> | |
861adc44 GF |
28 | #include <linux/regmap.h> |
29 | #include <linux/mfd/syscon.h> | |
358bdf89 DT |
30 | |
31 | #define STM32F4_RCC_PLLCFGR 0x04 | |
32 | #define STM32F4_RCC_CFGR 0x08 | |
33 | #define STM32F4_RCC_AHB1ENR 0x30 | |
34 | #define STM32F4_RCC_AHB2ENR 0x34 | |
35 | #define STM32F4_RCC_AHB3ENR 0x38 | |
36 | #define STM32F4_RCC_APB1ENR 0x40 | |
37 | #define STM32F4_RCC_APB2ENR 0x44 | |
861adc44 GF |
38 | #define STM32F4_RCC_BDCR 0x70 |
39 | #define STM32F4_RCC_CSR 0x74 | |
358bdf89 DT |
40 | |
41 | struct stm32f4_gate_data { | |
42 | u8 offset; | |
43 | u8 bit_idx; | |
44 | const char *name; | |
45 | const char *parent_name; | |
46 | unsigned long flags; | |
47 | }; | |
48 | ||
a064a07f | 49 | static const struct stm32f4_gate_data stm32f429_gates[] __initconst = { |
358bdf89 DT |
50 | { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, |
51 | { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, | |
52 | { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, | |
53 | { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, | |
54 | { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, | |
55 | { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, | |
56 | { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, | |
57 | { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, | |
58 | { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, | |
59 | { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, | |
60 | { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, | |
61 | { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, | |
62 | { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, | |
63 | { STM32F4_RCC_AHB1ENR, 20, "ccmdatam", "ahb_div" }, | |
64 | { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, | |
65 | { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, | |
66 | { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, | |
67 | { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, | |
68 | { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, | |
69 | { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, | |
70 | { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, | |
71 | { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, | |
72 | { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, | |
73 | ||
74 | { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, | |
75 | { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, | |
76 | { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, | |
77 | { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, | |
78 | { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, | |
79 | ||
80 | { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", | |
81 | CLK_IGNORE_UNUSED }, | |
82 | ||
83 | { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, | |
84 | { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, | |
85 | { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, | |
86 | { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, | |
87 | { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, | |
88 | { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, | |
89 | { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, | |
90 | { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, | |
91 | { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, | |
92 | { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, | |
93 | { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, | |
94 | { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, | |
95 | { STM32F4_RCC_APB1ENR, 17, "uart2", "apb1_div" }, | |
96 | { STM32F4_RCC_APB1ENR, 18, "uart3", "apb1_div" }, | |
97 | { STM32F4_RCC_APB1ENR, 19, "uart4", "apb1_div" }, | |
98 | { STM32F4_RCC_APB1ENR, 20, "uart5", "apb1_div" }, | |
99 | { STM32F4_RCC_APB1ENR, 21, "i2c1", "apb1_div" }, | |
100 | { STM32F4_RCC_APB1ENR, 22, "i2c2", "apb1_div" }, | |
101 | { STM32F4_RCC_APB1ENR, 23, "i2c3", "apb1_div" }, | |
102 | { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, | |
103 | { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, | |
104 | { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, | |
105 | { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, | |
106 | { STM32F4_RCC_APB1ENR, 30, "uart7", "apb1_div" }, | |
107 | { STM32F4_RCC_APB1ENR, 31, "uart8", "apb1_div" }, | |
108 | ||
109 | { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, | |
110 | { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, | |
111 | { STM32F4_RCC_APB2ENR, 4, "usart1", "apb2_div" }, | |
112 | { STM32F4_RCC_APB2ENR, 5, "usart6", "apb2_div" }, | |
113 | { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, | |
114 | { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, | |
115 | { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, | |
116 | { STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" }, | |
117 | { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, | |
118 | { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, | |
119 | { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, | |
120 | { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, | |
121 | { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, | |
122 | { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, | |
123 | { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, | |
124 | { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, | |
125 | { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, | |
126 | { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, | |
127 | }; | |
128 | ||
a064a07f GF |
129 | static const struct stm32f4_gate_data stm32f469_gates[] __initconst = { |
130 | { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, | |
131 | { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, | |
132 | { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, | |
133 | { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, | |
134 | { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, | |
135 | { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, | |
136 | { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, | |
137 | { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, | |
138 | { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, | |
139 | { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, | |
140 | { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, | |
141 | { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, | |
142 | { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, | |
143 | { STM32F4_RCC_AHB1ENR, 20, "ccmdatam", "ahb_div" }, | |
144 | { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, | |
145 | { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, | |
146 | { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, | |
147 | { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, | |
148 | { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, | |
149 | { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, | |
150 | { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, | |
151 | { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, | |
152 | { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, | |
153 | ||
154 | { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, | |
155 | { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, | |
156 | { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, | |
157 | { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, | |
158 | { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, | |
159 | ||
160 | { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", | |
161 | CLK_IGNORE_UNUSED }, | |
162 | { STM32F4_RCC_AHB3ENR, 1, "qspi", "ahb_div", | |
163 | CLK_IGNORE_UNUSED }, | |
164 | ||
165 | { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, | |
166 | { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, | |
167 | { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, | |
168 | { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, | |
169 | { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, | |
170 | { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, | |
171 | { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, | |
172 | { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, | |
173 | { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, | |
174 | { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, | |
175 | { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, | |
176 | { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, | |
177 | { STM32F4_RCC_APB1ENR, 17, "uart2", "apb1_div" }, | |
178 | { STM32F4_RCC_APB1ENR, 18, "uart3", "apb1_div" }, | |
179 | { STM32F4_RCC_APB1ENR, 19, "uart4", "apb1_div" }, | |
180 | { STM32F4_RCC_APB1ENR, 20, "uart5", "apb1_div" }, | |
181 | { STM32F4_RCC_APB1ENR, 21, "i2c1", "apb1_div" }, | |
182 | { STM32F4_RCC_APB1ENR, 22, "i2c2", "apb1_div" }, | |
183 | { STM32F4_RCC_APB1ENR, 23, "i2c3", "apb1_div" }, | |
184 | { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, | |
185 | { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, | |
186 | { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, | |
187 | { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, | |
188 | { STM32F4_RCC_APB1ENR, 30, "uart7", "apb1_div" }, | |
189 | { STM32F4_RCC_APB1ENR, 31, "uart8", "apb1_div" }, | |
190 | ||
191 | { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, | |
192 | { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, | |
193 | { STM32F4_RCC_APB2ENR, 4, "usart1", "apb2_div" }, | |
194 | { STM32F4_RCC_APB2ENR, 5, "usart6", "apb2_div" }, | |
195 | { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, | |
196 | { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, | |
197 | { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, | |
198 | { STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" }, | |
199 | { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, | |
200 | { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, | |
201 | { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, | |
202 | { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, | |
203 | { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, | |
204 | { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, | |
205 | { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, | |
206 | { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, | |
207 | { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, | |
208 | { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, | |
209 | }; | |
210 | ||
4261a881 | 211 | enum { SYSTICK, FCLK, CLK_LSI, CLK_LSE, CLK_HSE_RTC, CLK_RTC, END_PRIMARY_CLK }; |
358bdf89 DT |
212 | |
213 | /* | |
214 | * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx | |
215 | * have gate bits associated with them. Its combined hweight is 71. | |
216 | */ | |
a064a07f GF |
217 | #define MAX_GATE_MAP 3 |
218 | ||
219 | static const u64 stm32f42xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull, | |
220 | 0x0000000000000001ull, | |
221 | 0x04777f33f6fec9ffull }; | |
222 | ||
223 | static const u64 stm32f46xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull, | |
224 | 0x0000000000000003ull, | |
225 | 0x0c777f33f6fec9ffull }; | |
226 | ||
227 | static const u64 *stm32f4_gate_map; | |
228 | ||
229 | static struct clk_hw **clks; | |
358bdf89 | 230 | |
358bdf89 DT |
231 | static DEFINE_SPINLOCK(stm32f4_clk_lock); |
232 | static void __iomem *base; | |
233 | ||
861adc44 GF |
234 | static struct regmap *pdrm; |
235 | ||
358bdf89 DT |
236 | /* |
237 | * "Multiplier" device for APBx clocks. | |
238 | * | |
239 | * The APBx dividers are power-of-two dividers and, if *not* running in 1:1 | |
240 | * mode, they also tap out the one of the low order state bits to run the | |
241 | * timers. ST datasheets represent this feature as a (conditional) clock | |
242 | * multiplier. | |
243 | */ | |
244 | struct clk_apb_mul { | |
245 | struct clk_hw hw; | |
246 | u8 bit_idx; | |
247 | }; | |
248 | ||
249 | #define to_clk_apb_mul(_hw) container_of(_hw, struct clk_apb_mul, hw) | |
250 | ||
251 | static unsigned long clk_apb_mul_recalc_rate(struct clk_hw *hw, | |
252 | unsigned long parent_rate) | |
253 | { | |
254 | struct clk_apb_mul *am = to_clk_apb_mul(hw); | |
255 | ||
256 | if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx)) | |
257 | return parent_rate * 2; | |
258 | ||
259 | return parent_rate; | |
260 | } | |
261 | ||
262 | static long clk_apb_mul_round_rate(struct clk_hw *hw, unsigned long rate, | |
263 | unsigned long *prate) | |
264 | { | |
265 | struct clk_apb_mul *am = to_clk_apb_mul(hw); | |
266 | unsigned long mult = 1; | |
267 | ||
268 | if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx)) | |
269 | mult = 2; | |
270 | ||
98d8a60e | 271 | if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { |
358bdf89 DT |
272 | unsigned long best_parent = rate / mult; |
273 | ||
17ae4b40 | 274 | *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent); |
358bdf89 DT |
275 | } |
276 | ||
277 | return *prate * mult; | |
278 | } | |
279 | ||
280 | static int clk_apb_mul_set_rate(struct clk_hw *hw, unsigned long rate, | |
281 | unsigned long parent_rate) | |
282 | { | |
283 | /* | |
284 | * We must report success but we can do so unconditionally because | |
285 | * clk_apb_mul_round_rate returns values that ensure this call is a | |
286 | * nop. | |
287 | */ | |
288 | ||
289 | return 0; | |
290 | } | |
291 | ||
292 | static const struct clk_ops clk_apb_mul_factor_ops = { | |
293 | .round_rate = clk_apb_mul_round_rate, | |
294 | .set_rate = clk_apb_mul_set_rate, | |
295 | .recalc_rate = clk_apb_mul_recalc_rate, | |
296 | }; | |
297 | ||
298 | static struct clk *clk_register_apb_mul(struct device *dev, const char *name, | |
299 | const char *parent_name, | |
300 | unsigned long flags, u8 bit_idx) | |
301 | { | |
302 | struct clk_apb_mul *am; | |
303 | struct clk_init_data init; | |
304 | struct clk *clk; | |
305 | ||
306 | am = kzalloc(sizeof(*am), GFP_KERNEL); | |
307 | if (!am) | |
308 | return ERR_PTR(-ENOMEM); | |
309 | ||
310 | am->bit_idx = bit_idx; | |
311 | am->hw.init = &init; | |
312 | ||
313 | init.name = name; | |
314 | init.ops = &clk_apb_mul_factor_ops; | |
315 | init.flags = flags; | |
316 | init.parent_names = &parent_name; | |
317 | init.num_parents = 1; | |
318 | ||
319 | clk = clk_register(dev, &am->hw); | |
320 | ||
321 | if (IS_ERR(clk)) | |
322 | kfree(am); | |
323 | ||
324 | return clk; | |
325 | } | |
326 | ||
327 | /* | |
328 | * Decode current PLL state and (statically) model the state we inherit from | |
329 | * the bootloader. | |
330 | */ | |
331 | static void stm32f4_rcc_register_pll(const char *hse_clk, const char *hsi_clk) | |
332 | { | |
333 | unsigned long pllcfgr = readl(base + STM32F4_RCC_PLLCFGR); | |
334 | ||
335 | unsigned long pllm = pllcfgr & 0x3f; | |
336 | unsigned long plln = (pllcfgr >> 6) & 0x1ff; | |
337 | unsigned long pllp = BIT(((pllcfgr >> 16) & 3) + 1); | |
338 | const char *pllsrc = pllcfgr & BIT(22) ? hse_clk : hsi_clk; | |
339 | unsigned long pllq = (pllcfgr >> 24) & 0xf; | |
340 | ||
341 | clk_register_fixed_factor(NULL, "vco", pllsrc, 0, plln, pllm); | |
342 | clk_register_fixed_factor(NULL, "pll", "vco", 0, 1, pllp); | |
343 | clk_register_fixed_factor(NULL, "pll48", "vco", 0, 1, pllq); | |
344 | } | |
345 | ||
346 | /* | |
347 | * Converts the primary and secondary indices (as they appear in DT) to an | |
348 | * offset into our struct clock array. | |
349 | */ | |
350 | static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary) | |
351 | { | |
a064a07f | 352 | u64 table[MAX_GATE_MAP]; |
358bdf89 DT |
353 | |
354 | if (primary == 1) { | |
861adc44 | 355 | if (WARN_ON(secondary >= END_PRIMARY_CLK)) |
358bdf89 DT |
356 | return -EINVAL; |
357 | return secondary; | |
358 | } | |
359 | ||
a064a07f | 360 | memcpy(table, stm32f4_gate_map, sizeof(table)); |
358bdf89 DT |
361 | |
362 | /* only bits set in table can be used as indices */ | |
15ab3827 | 363 | if (WARN_ON(secondary >= BITS_PER_BYTE * sizeof(table) || |
358bdf89 DT |
364 | 0 == (table[BIT_ULL_WORD(secondary)] & |
365 | BIT_ULL_MASK(secondary)))) | |
366 | return -EINVAL; | |
367 | ||
368 | /* mask out bits above our current index */ | |
369 | table[BIT_ULL_WORD(secondary)] &= | |
370 | GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0); | |
371 | ||
861adc44 | 372 | return END_PRIMARY_CLK - 1 + hweight64(table[0]) + |
358bdf89 DT |
373 | (BIT_ULL_WORD(secondary) >= 1 ? hweight64(table[1]) : 0) + |
374 | (BIT_ULL_WORD(secondary) >= 2 ? hweight64(table[2]) : 0); | |
375 | } | |
376 | ||
4e950d1e | 377 | static struct clk_hw * |
358bdf89 DT |
378 | stm32f4_rcc_lookup_clk(struct of_phandle_args *clkspec, void *data) |
379 | { | |
380 | int i = stm32f4_rcc_lookup_clk_idx(clkspec->args[0], clkspec->args[1]); | |
381 | ||
382 | if (i < 0) | |
383 | return ERR_PTR(-EINVAL); | |
384 | ||
385 | return clks[i]; | |
386 | } | |
387 | ||
861adc44 GF |
388 | #define to_rgclk(_rgate) container_of(_rgate, struct stm32_rgate, gate) |
389 | ||
390 | static inline void disable_power_domain_write_protection(void) | |
391 | { | |
392 | if (pdrm) | |
393 | regmap_update_bits(pdrm, 0x00, (1 << 8), (1 << 8)); | |
394 | } | |
395 | ||
396 | static inline void enable_power_domain_write_protection(void) | |
397 | { | |
398 | if (pdrm) | |
399 | regmap_update_bits(pdrm, 0x00, (1 << 8), (0 << 8)); | |
400 | } | |
401 | ||
4261a881 GF |
402 | static inline void sofware_reset_backup_domain(void) |
403 | { | |
404 | unsigned long val; | |
405 | ||
406 | val = readl(base + STM32F4_RCC_BDCR); | |
407 | writel(val | BIT(16), base + STM32F4_RCC_BDCR); | |
408 | writel(val & ~BIT(16), base + STM32F4_RCC_BDCR); | |
409 | } | |
410 | ||
861adc44 GF |
411 | struct stm32_rgate { |
412 | struct clk_gate gate; | |
413 | u8 bit_rdy_idx; | |
414 | }; | |
415 | ||
416 | #define RTC_TIMEOUT 1000000 | |
417 | ||
418 | static int rgclk_enable(struct clk_hw *hw) | |
419 | { | |
420 | struct clk_gate *gate = to_clk_gate(hw); | |
421 | struct stm32_rgate *rgate = to_rgclk(gate); | |
422 | u32 reg; | |
423 | int ret; | |
424 | ||
425 | disable_power_domain_write_protection(); | |
426 | ||
427 | clk_gate_ops.enable(hw); | |
428 | ||
429 | ret = readl_relaxed_poll_timeout_atomic(gate->reg, reg, | |
430 | reg & rgate->bit_rdy_idx, 1000, RTC_TIMEOUT); | |
431 | ||
432 | enable_power_domain_write_protection(); | |
433 | return ret; | |
434 | } | |
435 | ||
436 | static void rgclk_disable(struct clk_hw *hw) | |
437 | { | |
438 | clk_gate_ops.disable(hw); | |
439 | } | |
440 | ||
441 | static int rgclk_is_enabled(struct clk_hw *hw) | |
442 | { | |
443 | return clk_gate_ops.is_enabled(hw); | |
444 | } | |
445 | ||
446 | static const struct clk_ops rgclk_ops = { | |
447 | .enable = rgclk_enable, | |
448 | .disable = rgclk_disable, | |
449 | .is_enabled = rgclk_is_enabled, | |
450 | }; | |
451 | ||
452 | static struct clk_hw *clk_register_rgate(struct device *dev, const char *name, | |
453 | const char *parent_name, unsigned long flags, | |
454 | void __iomem *reg, u8 bit_idx, u8 bit_rdy_idx, | |
455 | u8 clk_gate_flags, spinlock_t *lock) | |
456 | { | |
457 | struct stm32_rgate *rgate; | |
458 | struct clk_init_data init = { NULL }; | |
459 | struct clk_hw *hw; | |
460 | int ret; | |
461 | ||
462 | rgate = kzalloc(sizeof(*rgate), GFP_KERNEL); | |
463 | if (!rgate) | |
464 | return ERR_PTR(-ENOMEM); | |
465 | ||
466 | init.name = name; | |
467 | init.ops = &rgclk_ops; | |
468 | init.flags = flags; | |
469 | init.parent_names = &parent_name; | |
470 | init.num_parents = 1; | |
471 | ||
472 | rgate->bit_rdy_idx = bit_rdy_idx; | |
473 | ||
474 | rgate->gate.lock = lock; | |
475 | rgate->gate.reg = reg; | |
476 | rgate->gate.bit_idx = bit_idx; | |
477 | rgate->gate.hw.init = &init; | |
478 | ||
479 | hw = &rgate->gate.hw; | |
480 | ret = clk_hw_register(dev, hw); | |
481 | if (ret) { | |
482 | kfree(rgate); | |
483 | hw = ERR_PTR(ret); | |
484 | } | |
485 | ||
486 | return hw; | |
487 | } | |
488 | ||
4261a881 GF |
489 | static int cclk_gate_enable(struct clk_hw *hw) |
490 | { | |
491 | int ret; | |
492 | ||
493 | disable_power_domain_write_protection(); | |
494 | ||
495 | ret = clk_gate_ops.enable(hw); | |
496 | ||
497 | enable_power_domain_write_protection(); | |
498 | ||
499 | return ret; | |
500 | } | |
501 | ||
502 | static void cclk_gate_disable(struct clk_hw *hw) | |
503 | { | |
504 | disable_power_domain_write_protection(); | |
505 | ||
506 | clk_gate_ops.disable(hw); | |
507 | ||
508 | enable_power_domain_write_protection(); | |
509 | } | |
510 | ||
511 | static int cclk_gate_is_enabled(struct clk_hw *hw) | |
512 | { | |
513 | return clk_gate_ops.is_enabled(hw); | |
514 | } | |
515 | ||
516 | static const struct clk_ops cclk_gate_ops = { | |
517 | .enable = cclk_gate_enable, | |
518 | .disable = cclk_gate_disable, | |
519 | .is_enabled = cclk_gate_is_enabled, | |
520 | }; | |
521 | ||
522 | static u8 cclk_mux_get_parent(struct clk_hw *hw) | |
523 | { | |
524 | return clk_mux_ops.get_parent(hw); | |
525 | } | |
526 | ||
527 | static int cclk_mux_set_parent(struct clk_hw *hw, u8 index) | |
528 | { | |
529 | int ret; | |
530 | ||
531 | disable_power_domain_write_protection(); | |
532 | ||
533 | sofware_reset_backup_domain(); | |
534 | ||
535 | ret = clk_mux_ops.set_parent(hw, index); | |
536 | ||
537 | enable_power_domain_write_protection(); | |
538 | ||
539 | return ret; | |
540 | } | |
541 | ||
542 | static const struct clk_ops cclk_mux_ops = { | |
543 | .get_parent = cclk_mux_get_parent, | |
544 | .set_parent = cclk_mux_set_parent, | |
545 | }; | |
546 | ||
547 | static struct clk_hw *stm32_register_cclk(struct device *dev, const char *name, | |
548 | const char * const *parent_names, int num_parents, | |
549 | void __iomem *reg, u8 bit_idx, u8 shift, unsigned long flags, | |
550 | spinlock_t *lock) | |
551 | { | |
552 | struct clk_hw *hw; | |
553 | struct clk_gate *gate; | |
554 | struct clk_mux *mux; | |
555 | ||
556 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); | |
557 | if (!gate) { | |
558 | hw = ERR_PTR(-EINVAL); | |
559 | goto fail; | |
560 | } | |
561 | ||
562 | mux = kzalloc(sizeof(*mux), GFP_KERNEL); | |
563 | if (!mux) { | |
564 | kfree(gate); | |
565 | hw = ERR_PTR(-EINVAL); | |
566 | goto fail; | |
567 | } | |
568 | ||
569 | gate->reg = reg; | |
570 | gate->bit_idx = bit_idx; | |
571 | gate->flags = 0; | |
572 | gate->lock = lock; | |
573 | ||
574 | mux->reg = reg; | |
575 | mux->shift = shift; | |
576 | mux->mask = 3; | |
577 | mux->flags = 0; | |
578 | ||
579 | hw = clk_hw_register_composite(dev, name, parent_names, num_parents, | |
580 | &mux->hw, &cclk_mux_ops, | |
581 | NULL, NULL, | |
582 | &gate->hw, &cclk_gate_ops, | |
583 | flags); | |
584 | ||
585 | if (IS_ERR(hw)) { | |
586 | kfree(gate); | |
587 | kfree(mux); | |
588 | } | |
589 | ||
590 | fail: | |
591 | return hw; | |
592 | } | |
593 | ||
358bdf89 DT |
594 | static const char *sys_parents[] __initdata = { "hsi", NULL, "pll" }; |
595 | ||
596 | static const struct clk_div_table ahb_div_table[] = { | |
597 | { 0x0, 1 }, { 0x1, 1 }, { 0x2, 1 }, { 0x3, 1 }, | |
598 | { 0x4, 1 }, { 0x5, 1 }, { 0x6, 1 }, { 0x7, 1 }, | |
599 | { 0x8, 2 }, { 0x9, 4 }, { 0xa, 8 }, { 0xb, 16 }, | |
600 | { 0xc, 64 }, { 0xd, 128 }, { 0xe, 256 }, { 0xf, 512 }, | |
601 | { 0 }, | |
602 | }; | |
603 | ||
604 | static const struct clk_div_table apb_div_table[] = { | |
605 | { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, | |
606 | { 4, 2 }, { 5, 4 }, { 6, 8 }, { 7, 16 }, | |
607 | { 0 }, | |
608 | }; | |
609 | ||
4261a881 GF |
610 | static const char *rtc_parents[4] = { |
611 | "no-clock", "lse", "lsi", "hse-rtc" | |
612 | }; | |
613 | ||
a064a07f GF |
614 | struct stm32f4_clk_data { |
615 | const struct stm32f4_gate_data *gates_data; | |
616 | const u64 *gates_map; | |
617 | int gates_num; | |
618 | }; | |
619 | ||
620 | static const struct stm32f4_clk_data stm32f429_clk_data = { | |
621 | .gates_data = stm32f429_gates, | |
622 | .gates_map = stm32f42xx_gate_map, | |
623 | .gates_num = ARRAY_SIZE(stm32f429_gates), | |
624 | }; | |
625 | ||
626 | static const struct stm32f4_clk_data stm32f469_clk_data = { | |
627 | .gates_data = stm32f469_gates, | |
628 | .gates_map = stm32f46xx_gate_map, | |
629 | .gates_num = ARRAY_SIZE(stm32f469_gates), | |
630 | }; | |
631 | ||
632 | static const struct of_device_id stm32f4_of_match[] = { | |
633 | { | |
634 | .compatible = "st,stm32f42xx-rcc", | |
635 | .data = &stm32f429_clk_data | |
636 | }, | |
637 | { | |
638 | .compatible = "st,stm32f469-rcc", | |
639 | .data = &stm32f469_clk_data | |
640 | }, | |
641 | {} | |
642 | }; | |
643 | ||
358bdf89 DT |
644 | static void __init stm32f4_rcc_init(struct device_node *np) |
645 | { | |
646 | const char *hse_clk; | |
647 | int n; | |
a064a07f GF |
648 | const struct of_device_id *match; |
649 | const struct stm32f4_clk_data *data; | |
358bdf89 DT |
650 | |
651 | base = of_iomap(np, 0); | |
652 | if (!base) { | |
653 | pr_err("%s: unable to map resource", np->name); | |
654 | return; | |
655 | } | |
656 | ||
861adc44 GF |
657 | pdrm = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); |
658 | if (IS_ERR(pdrm)) { | |
659 | pdrm = NULL; | |
660 | pr_warn("%s: Unable to get syscfg\n", __func__); | |
661 | } | |
662 | ||
a064a07f GF |
663 | match = of_match_node(stm32f4_of_match, np); |
664 | if (WARN_ON(!match)) | |
665 | return; | |
666 | ||
667 | data = match->data; | |
668 | ||
669 | clks = kmalloc_array(data->gates_num + END_PRIMARY_CLK, | |
670 | sizeof(*clks), GFP_KERNEL); | |
671 | if (!clks) | |
672 | goto fail; | |
673 | ||
674 | stm32f4_gate_map = data->gates_map; | |
675 | ||
358bdf89 DT |
676 | hse_clk = of_clk_get_parent_name(np, 0); |
677 | ||
678 | clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0, | |
679 | 16000000, 160000); | |
680 | stm32f4_rcc_register_pll(hse_clk, "hsi"); | |
681 | ||
682 | sys_parents[1] = hse_clk; | |
683 | clk_register_mux_table( | |
684 | NULL, "sys", sys_parents, ARRAY_SIZE(sys_parents), 0, | |
685 | base + STM32F4_RCC_CFGR, 0, 3, 0, NULL, &stm32f4_clk_lock); | |
686 | ||
687 | clk_register_divider_table(NULL, "ahb_div", "sys", | |
688 | CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, | |
689 | 4, 4, 0, ahb_div_table, &stm32f4_clk_lock); | |
690 | ||
691 | clk_register_divider_table(NULL, "apb1_div", "ahb_div", | |
692 | CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, | |
693 | 10, 3, 0, apb_div_table, &stm32f4_clk_lock); | |
694 | clk_register_apb_mul(NULL, "apb1_mul", "apb1_div", | |
695 | CLK_SET_RATE_PARENT, 12); | |
696 | ||
697 | clk_register_divider_table(NULL, "apb2_div", "ahb_div", | |
698 | CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, | |
699 | 13, 3, 0, apb_div_table, &stm32f4_clk_lock); | |
700 | clk_register_apb_mul(NULL, "apb2_mul", "apb2_div", | |
701 | CLK_SET_RATE_PARENT, 15); | |
702 | ||
4e950d1e | 703 | clks[SYSTICK] = clk_hw_register_fixed_factor(NULL, "systick", "ahb_div", |
358bdf89 | 704 | 0, 1, 8); |
4e950d1e | 705 | clks[FCLK] = clk_hw_register_fixed_factor(NULL, "fclk", "ahb_div", |
358bdf89 DT |
706 | 0, 1, 1); |
707 | ||
a064a07f GF |
708 | for (n = 0; n < data->gates_num; n++) { |
709 | const struct stm32f4_gate_data *gd; | |
710 | unsigned int secondary; | |
711 | int idx; | |
712 | ||
713 | gd = &data->gates_data[n]; | |
714 | secondary = 8 * (gd->offset - STM32F4_RCC_AHB1ENR) + | |
715 | gd->bit_idx; | |
716 | idx = stm32f4_rcc_lookup_clk_idx(0, secondary); | |
358bdf89 DT |
717 | |
718 | if (idx < 0) | |
719 | goto fail; | |
720 | ||
4e950d1e | 721 | clks[idx] = clk_hw_register_gate( |
358bdf89 DT |
722 | NULL, gd->name, gd->parent_name, gd->flags, |
723 | base + gd->offset, gd->bit_idx, 0, &stm32f4_clk_lock); | |
724 | ||
334e125b | 725 | if (IS_ERR(clks[idx])) { |
358bdf89 DT |
726 | pr_err("%s: Unable to register leaf clock %s\n", |
727 | np->full_name, gd->name); | |
728 | goto fail; | |
729 | } | |
730 | } | |
731 | ||
861adc44 GF |
732 | clks[CLK_LSI] = clk_register_rgate(NULL, "lsi", "clk-lsi", 0, |
733 | base + STM32F4_RCC_CSR, 0, 2, 0, &stm32f4_clk_lock); | |
734 | ||
735 | if (IS_ERR(clks[CLK_LSI])) { | |
736 | pr_err("Unable to register lsi clock\n"); | |
737 | goto fail; | |
738 | } | |
739 | ||
740 | clks[CLK_LSE] = clk_register_rgate(NULL, "lse", "clk-lse", 0, | |
741 | base + STM32F4_RCC_BDCR, 0, 2, 0, &stm32f4_clk_lock); | |
742 | ||
743 | if (IS_ERR(clks[CLK_LSE])) { | |
744 | pr_err("Unable to register lse clock\n"); | |
745 | goto fail; | |
746 | } | |
747 | ||
4261a881 GF |
748 | clks[CLK_HSE_RTC] = clk_hw_register_divider(NULL, "hse-rtc", "clk-hse", |
749 | 0, base + STM32F4_RCC_CFGR, 16, 5, 0, | |
750 | &stm32f4_clk_lock); | |
751 | ||
752 | if (IS_ERR(clks[CLK_HSE_RTC])) { | |
753 | pr_err("Unable to register hse-rtc clock\n"); | |
754 | goto fail; | |
755 | } | |
756 | ||
757 | clks[CLK_RTC] = stm32_register_cclk(NULL, "rtc", rtc_parents, 4, | |
758 | base + STM32F4_RCC_BDCR, 15, 8, 0, &stm32f4_clk_lock); | |
759 | ||
760 | if (IS_ERR(clks[CLK_RTC])) { | |
761 | pr_err("Unable to register rtc clock\n"); | |
762 | goto fail; | |
763 | } | |
764 | ||
4e950d1e | 765 | of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL); |
358bdf89 DT |
766 | return; |
767 | fail: | |
a064a07f | 768 | kfree(clks); |
358bdf89 DT |
769 | iounmap(base); |
770 | } | |
a064a07f GF |
771 | CLK_OF_DECLARE(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init); |
772 | CLK_OF_DECLARE(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init); |