1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2014, The Linux Foundation. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/bitops.h> |
8 | #include <linux/err.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/clk-provider.h> |
13 | #include <linux/regmap.h> |
14 | |
15 | #include <dt-bindings/clock/qcom,lcc-ipq806x.h> |
16 | |
17 | #include "common.h" |
18 | #include "clk-regmap.h" |
19 | #include "clk-pll.h" |
20 | #include "clk-rcg.h" |
21 | #include "clk-branch.h" |
22 | #include "clk-regmap-divider.h" |
23 | #include "clk-regmap-mux.h" |
24 | #include "reset.h" |
25 | |
26 | static struct clk_pll pll4 = { |
27 | .l_reg = 0x4, |
28 | .m_reg = 0x8, |
29 | .n_reg = 0xc, |
30 | .config_reg = 0x14, |
31 | .mode_reg = 0x0, |
32 | .status_reg = 0x18, |
33 | .status_bit = 16, |
34 | .clkr.hw.init = &(struct clk_init_data){ |
35 | .name = "pll4" , |
36 | .parent_data = &(const struct clk_parent_data) { |
37 | .fw_name = "pxo" , .name = "pxo_board" , |
38 | }, |
39 | .num_parents = 1, |
40 | .ops = &clk_pll_ops, |
41 | }, |
42 | }; |
43 | |
44 | static const struct pll_config pll4_config = { |
45 | .l = 0xf, |
46 | .m = 0x91, |
47 | .n = 0xc7, |
48 | .vco_val = 0x0, |
49 | .vco_mask = BIT(17) | BIT(16), |
50 | .pre_div_val = 0x0, |
51 | .pre_div_mask = BIT(19), |
52 | .post_div_val = 0x0, |
53 | .post_div_mask = BIT(21) | BIT(20), |
54 | .mn_ena_mask = BIT(22), |
55 | .main_output_mask = BIT(23), |
56 | }; |
57 | |
58 | enum { |
59 | P_PXO, |
60 | P_PLL4, |
61 | }; |
62 | |
63 | static const struct parent_map lcc_pxo_pll4_map[] = { |
64 | { P_PXO, 0 }, |
65 | { P_PLL4, 2 } |
66 | }; |
67 | |
68 | static const struct clk_parent_data lcc_pxo_pll4[] = { |
69 | { .fw_name = "pxo" , .name = "pxo_board" }, |
70 | { .fw_name = "pll4_vote" , .name = "pll4_vote" }, |
71 | }; |
72 | |
73 | static struct freq_tbl clk_tbl_aif_mi2s[] = { |
74 | { 1024000, P_PLL4, 4, 1, 96 }, |
75 | { 1411200, P_PLL4, 4, 2, 139 }, |
76 | { 1536000, P_PLL4, 4, 1, 64 }, |
77 | { 2048000, P_PLL4, 4, 1, 48 }, |
78 | { 2116800, P_PLL4, 4, 2, 93 }, |
79 | { 2304000, P_PLL4, 4, 2, 85 }, |
80 | { 2822400, P_PLL4, 4, 6, 209 }, |
81 | { 3072000, P_PLL4, 4, 1, 32 }, |
82 | { 3175200, P_PLL4, 4, 1, 31 }, |
83 | { 4096000, P_PLL4, 4, 1, 24 }, |
84 | { 4233600, P_PLL4, 4, 9, 209 }, |
85 | { 4608000, P_PLL4, 4, 3, 64 }, |
86 | { 5644800, P_PLL4, 4, 12, 209 }, |
87 | { 6144000, P_PLL4, 4, 1, 16 }, |
88 | { 6350400, P_PLL4, 4, 2, 31 }, |
89 | { 8192000, P_PLL4, 4, 1, 12 }, |
90 | { 8467200, P_PLL4, 4, 18, 209 }, |
91 | { 9216000, P_PLL4, 4, 3, 32 }, |
92 | { 11289600, P_PLL4, 4, 24, 209 }, |
93 | { 12288000, P_PLL4, 4, 1, 8 }, |
94 | { 12700800, P_PLL4, 4, 27, 209 }, |
95 | { 13824000, P_PLL4, 4, 9, 64 }, |
96 | { 16384000, P_PLL4, 4, 1, 6 }, |
97 | { 16934400, P_PLL4, 4, 41, 238 }, |
98 | { 18432000, P_PLL4, 4, 3, 16 }, |
99 | { 22579200, P_PLL4, 2, 24, 209 }, |
100 | { 24576000, P_PLL4, 4, 1, 4 }, |
101 | { 27648000, P_PLL4, 4, 9, 32 }, |
102 | { 33868800, P_PLL4, 4, 41, 119 }, |
103 | { 36864000, P_PLL4, 4, 3, 8 }, |
104 | { 45158400, P_PLL4, 1, 24, 209 }, |
105 | { 49152000, P_PLL4, 4, 1, 2 }, |
106 | { 50803200, P_PLL4, 1, 27, 209 }, |
107 | { } |
108 | }; |
109 | |
110 | static struct clk_rcg mi2s_osr_src = { |
111 | .ns_reg = 0x48, |
112 | .md_reg = 0x4c, |
113 | .mn = { |
114 | .mnctr_en_bit = 8, |
115 | .mnctr_reset_bit = 7, |
116 | .mnctr_mode_shift = 5, |
117 | .n_val_shift = 24, |
118 | .m_val_shift = 8, |
119 | .width = 8, |
120 | }, |
121 | .p = { |
122 | .pre_div_shift = 3, |
123 | .pre_div_width = 2, |
124 | }, |
125 | .s = { |
126 | .src_sel_shift = 0, |
127 | .parent_map = lcc_pxo_pll4_map, |
128 | }, |
129 | .freq_tbl = clk_tbl_aif_mi2s, |
130 | .clkr = { |
131 | .enable_reg = 0x48, |
132 | .enable_mask = BIT(9), |
133 | .hw.init = &(struct clk_init_data){ |
134 | .name = "mi2s_osr_src" , |
135 | .parent_data = lcc_pxo_pll4, |
136 | .num_parents = ARRAY_SIZE(lcc_pxo_pll4), |
137 | .ops = &clk_rcg_ops, |
138 | .flags = CLK_SET_RATE_GATE, |
139 | }, |
140 | }, |
141 | }; |
142 | |
143 | static struct clk_branch mi2s_osr_clk = { |
144 | .halt_reg = 0x50, |
145 | .halt_bit = 1, |
146 | .halt_check = BRANCH_HALT_ENABLE, |
147 | .clkr = { |
148 | .enable_reg = 0x48, |
149 | .enable_mask = BIT(17), |
150 | .hw.init = &(struct clk_init_data){ |
151 | .name = "mi2s_osr_clk" , |
152 | .parent_hws = (const struct clk_hw*[]) { |
153 | &mi2s_osr_src.clkr.hw, |
154 | }, |
155 | .num_parents = 1, |
156 | .ops = &clk_branch_ops, |
157 | .flags = CLK_SET_RATE_PARENT, |
158 | }, |
159 | }, |
160 | }; |
161 | |
162 | static struct clk_regmap_div mi2s_div_clk = { |
163 | .reg = 0x48, |
164 | .shift = 10, |
165 | .width = 4, |
166 | .clkr = { |
167 | .hw.init = &(struct clk_init_data){ |
168 | .name = "mi2s_div_clk" , |
169 | .parent_hws = (const struct clk_hw*[]) { |
170 | &mi2s_osr_src.clkr.hw, |
171 | }, |
172 | .num_parents = 1, |
173 | .ops = &clk_regmap_div_ops, |
174 | }, |
175 | }, |
176 | }; |
177 | |
178 | static struct clk_branch mi2s_bit_div_clk = { |
179 | .halt_reg = 0x50, |
180 | .halt_bit = 0, |
181 | .halt_check = BRANCH_HALT_ENABLE, |
182 | .clkr = { |
183 | .enable_reg = 0x48, |
184 | .enable_mask = BIT(15), |
185 | .hw.init = &(struct clk_init_data){ |
186 | .name = "mi2s_bit_div_clk" , |
187 | .parent_hws = (const struct clk_hw*[]) { |
188 | &mi2s_div_clk.clkr.hw, |
189 | }, |
190 | .num_parents = 1, |
191 | .ops = &clk_branch_ops, |
192 | .flags = CLK_SET_RATE_PARENT, |
193 | }, |
194 | }, |
195 | }; |
196 | |
197 | static const struct clk_parent_data lcc_mi2s_bit_div_codec_clk[] = { |
198 | { .hw = &mi2s_bit_div_clk.clkr.hw, }, |
199 | { .fw_name = "mi2s_codec" , .name = "mi2s_codec_clk" }, |
200 | }; |
201 | |
202 | static struct clk_regmap_mux mi2s_bit_clk = { |
203 | .reg = 0x48, |
204 | .shift = 14, |
205 | .width = 1, |
206 | .clkr = { |
207 | .hw.init = &(struct clk_init_data){ |
208 | .name = "mi2s_bit_clk" , |
209 | .parent_data = lcc_mi2s_bit_div_codec_clk, |
210 | .num_parents = ARRAY_SIZE(lcc_mi2s_bit_div_codec_clk), |
211 | .ops = &clk_regmap_mux_closest_ops, |
212 | .flags = CLK_SET_RATE_PARENT, |
213 | }, |
214 | }, |
215 | }; |
216 | |
217 | static struct freq_tbl clk_tbl_pcm[] = { |
218 | { 64000, P_PLL4, 4, 1, 1536 }, |
219 | { 128000, P_PLL4, 4, 1, 768 }, |
220 | { 256000, P_PLL4, 4, 1, 384 }, |
221 | { 512000, P_PLL4, 4, 1, 192 }, |
222 | { 1024000, P_PLL4, 4, 1, 96 }, |
223 | { 2048000, P_PLL4, 4, 1, 48 }, |
224 | { }, |
225 | }; |
226 | |
227 | static struct clk_rcg pcm_src = { |
228 | .ns_reg = 0x54, |
229 | .md_reg = 0x58, |
230 | .mn = { |
231 | .mnctr_en_bit = 8, |
232 | .mnctr_reset_bit = 7, |
233 | .mnctr_mode_shift = 5, |
234 | .n_val_shift = 16, |
235 | .m_val_shift = 16, |
236 | .width = 16, |
237 | }, |
238 | .p = { |
239 | .pre_div_shift = 3, |
240 | .pre_div_width = 2, |
241 | }, |
242 | .s = { |
243 | .src_sel_shift = 0, |
244 | .parent_map = lcc_pxo_pll4_map, |
245 | }, |
246 | .freq_tbl = clk_tbl_pcm, |
247 | .clkr = { |
248 | .enable_reg = 0x54, |
249 | .enable_mask = BIT(9), |
250 | .hw.init = &(struct clk_init_data){ |
251 | .name = "pcm_src" , |
252 | .parent_data = lcc_pxo_pll4, |
253 | .num_parents = ARRAY_SIZE(lcc_pxo_pll4), |
254 | .ops = &clk_rcg_ops, |
255 | .flags = CLK_SET_RATE_GATE, |
256 | }, |
257 | }, |
258 | }; |
259 | |
260 | static struct clk_branch pcm_clk_out = { |
261 | .halt_reg = 0x5c, |
262 | .halt_bit = 0, |
263 | .halt_check = BRANCH_HALT_ENABLE, |
264 | .clkr = { |
265 | .enable_reg = 0x54, |
266 | .enable_mask = BIT(11), |
267 | .hw.init = &(struct clk_init_data){ |
268 | .name = "pcm_clk_out" , |
269 | .parent_hws = (const struct clk_hw*[]) { |
270 | &pcm_src.clkr.hw, |
271 | }, |
272 | .num_parents = 1, |
273 | .ops = &clk_branch_ops, |
274 | .flags = CLK_SET_RATE_PARENT, |
275 | }, |
276 | }, |
277 | }; |
278 | |
279 | static const struct clk_parent_data lcc_pcm_clk_out_codec_clk[] = { |
280 | { .hw = &pcm_clk_out.clkr.hw, }, |
281 | { .fw_name = "pcm_codec_clk" , .name = "pcm_codec_clk" }, |
282 | }; |
283 | |
284 | static struct clk_regmap_mux pcm_clk = { |
285 | .reg = 0x54, |
286 | .shift = 10, |
287 | .width = 1, |
288 | .clkr = { |
289 | .hw.init = &(struct clk_init_data){ |
290 | .name = "pcm_clk" , |
291 | .parent_data = lcc_pcm_clk_out_codec_clk, |
292 | .num_parents = ARRAY_SIZE(lcc_pcm_clk_out_codec_clk), |
293 | .ops = &clk_regmap_mux_closest_ops, |
294 | .flags = CLK_SET_RATE_PARENT, |
295 | }, |
296 | }, |
297 | }; |
298 | |
299 | static struct freq_tbl clk_tbl_aif_osr[] = { |
300 | { 2822400, P_PLL4, 1, 147, 20480 }, |
301 | { 4096000, P_PLL4, 1, 1, 96 }, |
302 | { 5644800, P_PLL4, 1, 147, 10240 }, |
303 | { 6144000, P_PLL4, 1, 1, 64 }, |
304 | { 11289600, P_PLL4, 1, 147, 5120 }, |
305 | { 12288000, P_PLL4, 1, 1, 32 }, |
306 | { 22579200, P_PLL4, 1, 147, 2560 }, |
307 | { 24576000, P_PLL4, 1, 1, 16 }, |
308 | { }, |
309 | }; |
310 | |
311 | static struct clk_rcg spdif_src = { |
312 | .ns_reg = 0xcc, |
313 | .md_reg = 0xd0, |
314 | .mn = { |
315 | .mnctr_en_bit = 8, |
316 | .mnctr_reset_bit = 7, |
317 | .mnctr_mode_shift = 5, |
318 | .n_val_shift = 16, |
319 | .m_val_shift = 16, |
320 | .width = 8, |
321 | }, |
322 | .p = { |
323 | .pre_div_shift = 3, |
324 | .pre_div_width = 2, |
325 | }, |
326 | .s = { |
327 | .src_sel_shift = 0, |
328 | .parent_map = lcc_pxo_pll4_map, |
329 | }, |
330 | .freq_tbl = clk_tbl_aif_osr, |
331 | .clkr = { |
332 | .enable_reg = 0xcc, |
333 | .enable_mask = BIT(9), |
334 | .hw.init = &(struct clk_init_data){ |
335 | .name = "spdif_src" , |
336 | .parent_data = lcc_pxo_pll4, |
337 | .num_parents = ARRAY_SIZE(lcc_pxo_pll4), |
338 | .ops = &clk_rcg_ops, |
339 | .flags = CLK_SET_RATE_GATE, |
340 | }, |
341 | }, |
342 | }; |
343 | |
344 | static struct clk_branch spdif_clk = { |
345 | .halt_reg = 0xd4, |
346 | .halt_bit = 1, |
347 | .halt_check = BRANCH_HALT_ENABLE, |
348 | .clkr = { |
349 | .enable_reg = 0xcc, |
350 | .enable_mask = BIT(12), |
351 | .hw.init = &(struct clk_init_data){ |
352 | .name = "spdif_clk" , |
353 | .parent_hws = (const struct clk_hw*[]) { |
354 | &spdif_src.clkr.hw, |
355 | }, |
356 | .num_parents = 1, |
357 | .ops = &clk_branch_ops, |
358 | .flags = CLK_SET_RATE_PARENT, |
359 | }, |
360 | }, |
361 | }; |
362 | |
363 | static struct freq_tbl clk_tbl_ahbix[] = { |
364 | { 131072000, P_PLL4, 1, 1, 3 }, |
365 | { }, |
366 | }; |
367 | |
368 | static struct clk_rcg ahbix_clk = { |
369 | .ns_reg = 0x38, |
370 | .md_reg = 0x3c, |
371 | .mn = { |
372 | .mnctr_en_bit = 8, |
373 | .mnctr_reset_bit = 7, |
374 | .mnctr_mode_shift = 5, |
375 | .n_val_shift = 24, |
376 | .m_val_shift = 8, |
377 | .width = 8, |
378 | }, |
379 | .p = { |
380 | .pre_div_shift = 3, |
381 | .pre_div_width = 2, |
382 | }, |
383 | .s = { |
384 | .src_sel_shift = 0, |
385 | .parent_map = lcc_pxo_pll4_map, |
386 | }, |
387 | .freq_tbl = clk_tbl_ahbix, |
388 | .clkr = { |
389 | .enable_reg = 0x38, |
390 | .enable_mask = BIT(11), |
391 | .hw.init = &(struct clk_init_data){ |
392 | .name = "ahbix" , |
393 | .parent_data = lcc_pxo_pll4, |
394 | .num_parents = ARRAY_SIZE(lcc_pxo_pll4), |
395 | .ops = &clk_rcg_lcc_ops, |
396 | }, |
397 | }, |
398 | }; |
399 | |
400 | static struct clk_regmap *lcc_ipq806x_clks[] = { |
401 | [PLL4] = &pll4.clkr, |
402 | [MI2S_OSR_SRC] = &mi2s_osr_src.clkr, |
403 | [MI2S_OSR_CLK] = &mi2s_osr_clk.clkr, |
404 | [MI2S_DIV_CLK] = &mi2s_div_clk.clkr, |
405 | [MI2S_BIT_DIV_CLK] = &mi2s_bit_div_clk.clkr, |
406 | [MI2S_BIT_CLK] = &mi2s_bit_clk.clkr, |
407 | [PCM_SRC] = &pcm_src.clkr, |
408 | [PCM_CLK_OUT] = &pcm_clk_out.clkr, |
409 | [PCM_CLK] = &pcm_clk.clkr, |
410 | [SPDIF_SRC] = &spdif_src.clkr, |
411 | [SPDIF_CLK] = &spdif_clk.clkr, |
412 | [AHBIX_CLK] = &ahbix_clk.clkr, |
413 | }; |
414 | |
415 | static const struct qcom_reset_map lcc_ipq806x_resets[] = { |
416 | [LCC_PCM_RESET] = { 0x54, 13 }, |
417 | }; |
418 | |
419 | static const struct regmap_config lcc_ipq806x_regmap_config = { |
420 | .reg_bits = 32, |
421 | .reg_stride = 4, |
422 | .val_bits = 32, |
423 | .max_register = 0xfc, |
424 | .fast_io = true, |
425 | }; |
426 | |
427 | static const struct qcom_cc_desc lcc_ipq806x_desc = { |
428 | .config = &lcc_ipq806x_regmap_config, |
429 | .clks = lcc_ipq806x_clks, |
430 | .num_clks = ARRAY_SIZE(lcc_ipq806x_clks), |
431 | .resets = lcc_ipq806x_resets, |
432 | .num_resets = ARRAY_SIZE(lcc_ipq806x_resets), |
433 | }; |
434 | |
435 | static const struct of_device_id lcc_ipq806x_match_table[] = { |
436 | { .compatible = "qcom,lcc-ipq8064" }, |
437 | { } |
438 | }; |
439 | MODULE_DEVICE_TABLE(of, lcc_ipq806x_match_table); |
440 | |
441 | static int lcc_ipq806x_probe(struct platform_device *pdev) |
442 | { |
443 | u32 val; |
444 | struct regmap *regmap; |
445 | |
446 | regmap = qcom_cc_map(pdev, desc: &lcc_ipq806x_desc); |
447 | if (IS_ERR(ptr: regmap)) |
448 | return PTR_ERR(ptr: regmap); |
449 | |
450 | /* Configure the rate of PLL4 if the bootloader hasn't already */ |
451 | regmap_read(map: regmap, reg: 0x0, val: &val); |
452 | if (!val) |
453 | clk_pll_configure_sr(pll: &pll4, regmap, config: &pll4_config, fsm_mode: true); |
454 | /* Enable PLL4 source on the LPASS Primary PLL Mux */ |
455 | regmap_write(map: regmap, reg: 0xc4, val: 0x1); |
456 | |
457 | return qcom_cc_really_probe(pdev, desc: &lcc_ipq806x_desc, regmap); |
458 | } |
459 | |
460 | static struct platform_driver lcc_ipq806x_driver = { |
461 | .probe = lcc_ipq806x_probe, |
462 | .driver = { |
463 | .name = "lcc-ipq806x" , |
464 | .of_match_table = lcc_ipq806x_match_table, |
465 | }, |
466 | }; |
467 | module_platform_driver(lcc_ipq806x_driver); |
468 | |
469 | MODULE_DESCRIPTION("QCOM LCC IPQ806x Driver" ); |
470 | MODULE_LICENSE("GPL v2" ); |
471 | MODULE_ALIAS("platform:lcc-ipq806x" ); |
472 | |