1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
4 * Copyright (c) 2025, Luca Weiss <luca.weiss@fairphone.com>
5 */
6
7 #include <linux/clk-provider.h>
8 #include <linux/module.h>
9 #include <linux/platform_device.h>
10 #include <linux/regmap.h>
11
12 #include <dt-bindings/clock/qcom,milos-videocc.h>
13
14 #include "clk-alpha-pll.h"
15 #include "clk-branch.h"
16 #include "clk-rcg.h"
17 #include "clk-regmap.h"
18 #include "clk-regmap-divider.h"
19 #include "common.h"
20 #include "gdsc.h"
21 #include "reset.h"
22
23 /* Need to match the order of clocks in DT binding */
24 enum {
25 DT_BI_TCXO,
26 DT_BI_TCXO_AO,
27 DT_SLEEP_CLK,
28 DT_IFACE,
29 };
30
31 enum {
32 P_BI_TCXO,
33 P_SLEEP_CLK,
34 P_VIDEO_CC_PLL0_OUT_MAIN,
35 };
36
37 static const struct pll_vco lucid_ole_vco[] = {
38 { 249600000, 2300000000, 0 },
39 };
40
41 /* 604.8 MHz Configuration */
42 static const struct alpha_pll_config video_cc_pll0_config = {
43 .l = 0x1f,
44 .alpha = 0x8000,
45 .config_ctl_val = 0x20485699,
46 .config_ctl_hi_val = 0x00182261,
47 .config_ctl_hi1_val = 0x82aa299c,
48 .test_ctl_val = 0x00000000,
49 .test_ctl_hi_val = 0x00000003,
50 .test_ctl_hi1_val = 0x00009000,
51 .test_ctl_hi2_val = 0x00000034,
52 .user_ctl_val = 0x00000000,
53 .user_ctl_hi_val = 0x00000005,
54 };
55
56 static struct clk_alpha_pll video_cc_pll0 = {
57 .offset = 0x0,
58 .config = &video_cc_pll0_config,
59 .vco_table = lucid_ole_vco,
60 .num_vco = ARRAY_SIZE(lucid_ole_vco),
61 .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
62 .clkr = {
63 .hw.init = &(const struct clk_init_data) {
64 .name = "video_cc_pll0",
65 .parent_data = &(const struct clk_parent_data) {
66 .index = DT_BI_TCXO,
67 },
68 .num_parents = 1,
69 .ops = &clk_alpha_pll_lucid_evo_ops,
70 },
71 },
72 };
73
74 static const struct parent_map video_cc_parent_map_0[] = {
75 { P_BI_TCXO, 0 },
76 };
77
78 static const struct clk_parent_data video_cc_parent_data_0[] = {
79 { .index = DT_BI_TCXO },
80 };
81
82 static const struct clk_parent_data video_cc_parent_data_0_ao[] = {
83 { .index = DT_BI_TCXO_AO },
84 };
85
86 static const struct parent_map video_cc_parent_map_1[] = {
87 { P_BI_TCXO, 0 },
88 { P_VIDEO_CC_PLL0_OUT_MAIN, 1 },
89 };
90
91 static const struct clk_parent_data video_cc_parent_data_1[] = {
92 { .index = DT_BI_TCXO },
93 { .hw = &video_cc_pll0.clkr.hw },
94 };
95
96 static const struct parent_map video_cc_parent_map_2[] = {
97 { P_SLEEP_CLK, 0 },
98 };
99
100 static const struct clk_parent_data video_cc_parent_data_2_ao[] = {
101 { .index = DT_SLEEP_CLK },
102 };
103
104 static const struct freq_tbl ftbl_video_cc_ahb_clk_src[] = {
105 F(19200000, P_BI_TCXO, 1, 0, 0),
106 { }
107 };
108
109 static struct clk_rcg2 video_cc_ahb_clk_src = {
110 .cmd_rcgr = 0x8030,
111 .mnd_width = 0,
112 .hid_width = 5,
113 .parent_map = video_cc_parent_map_0,
114 .freq_tbl = ftbl_video_cc_ahb_clk_src,
115 .clkr.hw.init = &(const struct clk_init_data) {
116 .name = "video_cc_ahb_clk_src",
117 .parent_data = video_cc_parent_data_0_ao,
118 .num_parents = ARRAY_SIZE(video_cc_parent_data_0_ao),
119 .flags = CLK_SET_RATE_PARENT,
120 .ops = &clk_rcg2_shared_ops,
121 },
122 };
123
124 static const struct freq_tbl ftbl_video_cc_mvs0_clk_src[] = {
125 F(604800000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0),
126 F(720000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0),
127 F(1014000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0),
128 F(1098000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0),
129 F(1332000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0),
130 F(1656000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0),
131 { }
132 };
133
134 static struct clk_rcg2 video_cc_mvs0_clk_src = {
135 .cmd_rcgr = 0x8000,
136 .mnd_width = 0,
137 .hid_width = 5,
138 .parent_map = video_cc_parent_map_1,
139 .freq_tbl = ftbl_video_cc_mvs0_clk_src,
140 .clkr.hw.init = &(const struct clk_init_data) {
141 .name = "video_cc_mvs0_clk_src",
142 .parent_data = video_cc_parent_data_1,
143 .num_parents = ARRAY_SIZE(video_cc_parent_data_1),
144 .flags = CLK_SET_RATE_PARENT,
145 .ops = &clk_rcg2_shared_ops,
146 },
147 };
148
149 static const struct freq_tbl ftbl_video_cc_sleep_clk_src[] = {
150 F(32000, P_SLEEP_CLK, 1, 0, 0),
151 { }
152 };
153
154 static struct clk_rcg2 video_cc_sleep_clk_src = {
155 .cmd_rcgr = 0x8128,
156 .mnd_width = 0,
157 .hid_width = 5,
158 .parent_map = video_cc_parent_map_2,
159 .freq_tbl = ftbl_video_cc_sleep_clk_src,
160 .clkr.hw.init = &(const struct clk_init_data) {
161 .name = "video_cc_sleep_clk_src",
162 .parent_data = video_cc_parent_data_2_ao,
163 .num_parents = ARRAY_SIZE(video_cc_parent_data_2_ao),
164 .flags = CLK_SET_RATE_PARENT,
165 .ops = &clk_rcg2_ops,
166 },
167 };
168
169 static struct clk_rcg2 video_cc_xo_clk_src = {
170 .cmd_rcgr = 0x810c,
171 .mnd_width = 0,
172 .hid_width = 5,
173 .parent_map = video_cc_parent_map_0,
174 .freq_tbl = ftbl_video_cc_ahb_clk_src,
175 .clkr.hw.init = &(const struct clk_init_data) {
176 .name = "video_cc_xo_clk_src",
177 .parent_data = video_cc_parent_data_0,
178 .num_parents = ARRAY_SIZE(video_cc_parent_data_0),
179 .flags = CLK_SET_RATE_PARENT,
180 .ops = &clk_rcg2_ops,
181 },
182 };
183
184 static struct clk_regmap_div video_cc_mvs0_div_clk_src = {
185 .reg = 0x80c4,
186 .shift = 0,
187 .width = 4,
188 .clkr.hw.init = &(const struct clk_init_data) {
189 .name = "video_cc_mvs0_div_clk_src",
190 .parent_hws = (const struct clk_hw*[]) {
191 &video_cc_mvs0_clk_src.clkr.hw,
192 },
193 .num_parents = 1,
194 .flags = CLK_SET_RATE_PARENT,
195 .ops = &clk_regmap_div_ro_ops,
196 },
197 };
198
199 static struct clk_regmap_div video_cc_mvs0c_div2_div_clk_src = {
200 .reg = 0x8070,
201 .shift = 0,
202 .width = 4,
203 .clkr.hw.init = &(const struct clk_init_data) {
204 .name = "video_cc_mvs0c_div2_div_clk_src",
205 .parent_hws = (const struct clk_hw*[]) {
206 &video_cc_mvs0_clk_src.clkr.hw,
207 },
208 .num_parents = 1,
209 .flags = CLK_SET_RATE_PARENT,
210 .ops = &clk_regmap_div_ro_ops,
211 },
212 };
213
214 static struct clk_branch video_cc_mvs0_clk = {
215 .halt_reg = 0x80b8,
216 .halt_check = BRANCH_HALT_VOTED,
217 .hwcg_reg = 0x80b8,
218 .hwcg_bit = 1,
219 .clkr = {
220 .enable_reg = 0x80b8,
221 .enable_mask = BIT(0),
222 .hw.init = &(const struct clk_init_data) {
223 .name = "video_cc_mvs0_clk",
224 .parent_hws = (const struct clk_hw*[]) {
225 &video_cc_mvs0_div_clk_src.clkr.hw,
226 },
227 .num_parents = 1,
228 .flags = CLK_SET_RATE_PARENT,
229 .ops = &clk_branch2_ops,
230 },
231 },
232 };
233
234 static struct clk_branch video_cc_mvs0_shift_clk = {
235 .halt_reg = 0x8144,
236 .halt_check = BRANCH_HALT_VOTED,
237 .hwcg_reg = 0x8144,
238 .hwcg_bit = 1,
239 .clkr = {
240 .enable_reg = 0x8144,
241 .enable_mask = BIT(0),
242 .hw.init = &(const struct clk_init_data) {
243 .name = "video_cc_mvs0_shift_clk",
244 .parent_hws = (const struct clk_hw*[]) {
245 &video_cc_xo_clk_src.clkr.hw,
246 },
247 .num_parents = 1,
248 .flags = CLK_SET_RATE_PARENT,
249 .ops = &clk_branch2_ops,
250 },
251 },
252 };
253
254 static struct clk_branch video_cc_mvs0c_clk = {
255 .halt_reg = 0x8064,
256 .halt_check = BRANCH_HALT,
257 .clkr = {
258 .enable_reg = 0x8064,
259 .enable_mask = BIT(0),
260 .hw.init = &(const struct clk_init_data) {
261 .name = "video_cc_mvs0c_clk",
262 .parent_hws = (const struct clk_hw*[]) {
263 &video_cc_mvs0c_div2_div_clk_src.clkr.hw,
264 },
265 .num_parents = 1,
266 .flags = CLK_SET_RATE_PARENT,
267 .ops = &clk_branch2_ops,
268 },
269 },
270 };
271
272 static struct clk_branch video_cc_mvs0c_shift_clk = {
273 .halt_reg = 0x8148,
274 .halt_check = BRANCH_HALT_VOTED,
275 .hwcg_reg = 0x8148,
276 .hwcg_bit = 1,
277 .clkr = {
278 .enable_reg = 0x8148,
279 .enable_mask = BIT(0),
280 .hw.init = &(const struct clk_init_data) {
281 .name = "video_cc_mvs0c_shift_clk",
282 .parent_hws = (const struct clk_hw*[]) {
283 &video_cc_xo_clk_src.clkr.hw,
284 },
285 .num_parents = 1,
286 .flags = CLK_SET_RATE_PARENT,
287 .ops = &clk_branch2_ops,
288 },
289 },
290 };
291
292 static struct gdsc video_cc_mvs0c_gdsc = {
293 .gdscr = 0x804c,
294 .en_rest_wait_val = 0x2,
295 .en_few_wait_val = 0x2,
296 .clk_dis_wait_val = 0x6,
297 .pd = {
298 .name = "video_cc_mvs0c_gdsc",
299 },
300 .pwrsts = PWRSTS_OFF_ON,
301 .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE,
302 };
303
304 static struct gdsc video_cc_mvs0_gdsc = {
305 .gdscr = 0x80a4,
306 .en_rest_wait_val = 0x2,
307 .en_few_wait_val = 0x2,
308 .clk_dis_wait_val = 0x6,
309 .pd = {
310 .name = "video_cc_mvs0_gdsc",
311 },
312 .pwrsts = PWRSTS_OFF_ON,
313 .parent = &video_cc_mvs0c_gdsc.pd,
314 .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | HW_CTRL_TRIGGER,
315 };
316
317 static struct clk_regmap *video_cc_milos_clocks[] = {
318 [VIDEO_CC_AHB_CLK_SRC] = &video_cc_ahb_clk_src.clkr,
319 [VIDEO_CC_MVS0_CLK] = &video_cc_mvs0_clk.clkr,
320 [VIDEO_CC_MVS0_CLK_SRC] = &video_cc_mvs0_clk_src.clkr,
321 [VIDEO_CC_MVS0_DIV_CLK_SRC] = &video_cc_mvs0_div_clk_src.clkr,
322 [VIDEO_CC_MVS0_SHIFT_CLK] = &video_cc_mvs0_shift_clk.clkr,
323 [VIDEO_CC_MVS0C_CLK] = &video_cc_mvs0c_clk.clkr,
324 [VIDEO_CC_MVS0C_DIV2_DIV_CLK_SRC] = &video_cc_mvs0c_div2_div_clk_src.clkr,
325 [VIDEO_CC_MVS0C_SHIFT_CLK] = &video_cc_mvs0c_shift_clk.clkr,
326 [VIDEO_CC_PLL0] = &video_cc_pll0.clkr,
327 [VIDEO_CC_SLEEP_CLK_SRC] = &video_cc_sleep_clk_src.clkr,
328 [VIDEO_CC_XO_CLK_SRC] = &video_cc_xo_clk_src.clkr,
329 };
330
331 static struct gdsc *video_cc_milos_gdscs[] = {
332 [VIDEO_CC_MVS0C_GDSC] = &video_cc_mvs0c_gdsc,
333 [VIDEO_CC_MVS0_GDSC] = &video_cc_mvs0_gdsc,
334 };
335
336 static const struct qcom_reset_map video_cc_milos_resets[] = {
337 [VIDEO_CC_INTERFACE_BCR] = { 0x80f0 },
338 [VIDEO_CC_MVS0_BCR] = { 0x80a0 },
339 [VIDEO_CC_MVS0C_CLK_ARES] = { 0x8064, 2 },
340 [VIDEO_CC_MVS0C_BCR] = { 0x8048 },
341 };
342
343 static struct clk_alpha_pll *video_cc_milos_plls[] = {
344 &video_cc_pll0,
345 };
346
347 static const u32 video_cc_milos_critical_cbcrs[] = {
348 0x80f4, /* VIDEO_CC_AHB_CLK */
349 0x8140, /* VIDEO_CC_SLEEP_CLK */
350 0x8124, /* VIDEO_CC_XO_CLK */
351 };
352
353 static const struct regmap_config video_cc_milos_regmap_config = {
354 .reg_bits = 32,
355 .reg_stride = 4,
356 .val_bits = 32,
357 .max_register = 0x9f50,
358 .fast_io = true,
359 };
360
361 static const struct qcom_cc_driver_data video_cc_milos_driver_data = {
362 .alpha_plls = video_cc_milos_plls,
363 .num_alpha_plls = ARRAY_SIZE(video_cc_milos_plls),
364 .clk_cbcrs = video_cc_milos_critical_cbcrs,
365 .num_clk_cbcrs = ARRAY_SIZE(video_cc_milos_critical_cbcrs),
366 };
367
368 static const struct qcom_cc_desc video_cc_milos_desc = {
369 .config = &video_cc_milos_regmap_config,
370 .clks = video_cc_milos_clocks,
371 .num_clks = ARRAY_SIZE(video_cc_milos_clocks),
372 .resets = video_cc_milos_resets,
373 .num_resets = ARRAY_SIZE(video_cc_milos_resets),
374 .gdscs = video_cc_milos_gdscs,
375 .num_gdscs = ARRAY_SIZE(video_cc_milos_gdscs),
376 .use_rpm = true,
377 .driver_data = &video_cc_milos_driver_data,
378 };
379
380 static const struct of_device_id video_cc_milos_match_table[] = {
381 { .compatible = "qcom,milos-videocc" },
382 { }
383 };
384 MODULE_DEVICE_TABLE(of, video_cc_milos_match_table);
385
video_cc_milos_probe(struct platform_device * pdev)386 static int video_cc_milos_probe(struct platform_device *pdev)
387 {
388 return qcom_cc_probe(pdev, &video_cc_milos_desc);
389 }
390
391 static struct platform_driver video_cc_milos_driver = {
392 .probe = video_cc_milos_probe,
393 .driver = {
394 .name = "video_cc-milos",
395 .of_match_table = video_cc_milos_match_table,
396 },
397 };
398
399 module_platform_driver(video_cc_milos_driver);
400
401 MODULE_DESCRIPTION("QTI VIDEO_CC Milos Driver");
402 MODULE_LICENSE("GPL");
403