1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2021, The Linux Foundation. All rights reserved.
4 * Copyright (c) 2021, Konrad Dybcio <konrad.dybcio@somainline.org>
5 * Copyright (c) 2025, Luca Weiss <luca.weiss@fairphone.com>
6 */
7
8 #include <linux/clk-provider.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/regmap.h>
12
13 #include <dt-bindings/clock/qcom,sm6350-videocc.h>
14
15 #include "clk-alpha-pll.h"
16 #include "clk-branch.h"
17 #include "clk-rcg.h"
18 #include "clk-regmap.h"
19 #include "common.h"
20 #include "gdsc.h"
21
22 enum {
23 DT_IFACE,
24 DT_BI_TCXO,
25 DT_SLEEP_CLK,
26 };
27
28 enum {
29 P_BI_TCXO,
30 P_CHIP_SLEEP_CLK,
31 P_VIDEO_PLL0_OUT_EVEN,
32 };
33
34 static const struct pll_vco fabia_vco[] = {
35 { 125000000, 1000000000, 1 },
36 };
37
38 /* 600 MHz */
39 static const struct alpha_pll_config video_pll0_config = {
40 .l = 0x1f,
41 .alpha = 0x4000,
42 .config_ctl_val = 0x20485699,
43 .config_ctl_hi_val = 0x00002067,
44 .test_ctl_val = 0x40000000,
45 .test_ctl_hi_val = 0x00000002,
46 .user_ctl_val = 0x00000101,
47 .user_ctl_hi_val = 0x00004005,
48 };
49
50 static struct clk_alpha_pll video_pll0 = {
51 .offset = 0x0,
52 .vco_table = fabia_vco,
53 .num_vco = ARRAY_SIZE(fabia_vco),
54 .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA],
55 .clkr = {
56 .hw.init = &(const struct clk_init_data) {
57 .name = "video_pll0",
58 .parent_data = &(const struct clk_parent_data) {
59 .index = DT_BI_TCXO,
60 },
61 .num_parents = 1,
62 .ops = &clk_alpha_pll_fabia_ops,
63 },
64 },
65 };
66
67 static const struct clk_div_table post_div_table_video_pll0_out_even[] = {
68 { 0x1, 2 },
69 { }
70 };
71
72 static struct clk_alpha_pll_postdiv video_pll0_out_even = {
73 .offset = 0x0,
74 .post_div_shift = 8,
75 .post_div_table = post_div_table_video_pll0_out_even,
76 .num_post_div = ARRAY_SIZE(post_div_table_video_pll0_out_even),
77 .width = 4,
78 .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA],
79 .clkr.hw.init = &(const struct clk_init_data) {
80 .name = "video_pll0_out_even",
81 .parent_hws = (const struct clk_hw*[]) {
82 &video_pll0.clkr.hw,
83 },
84 .num_parents = 1,
85 .flags = CLK_SET_RATE_PARENT,
86 .ops = &clk_alpha_pll_postdiv_fabia_ops,
87 },
88 };
89
90 static const struct parent_map video_cc_parent_map_0[] = {
91 { P_BI_TCXO, 0 },
92 { P_VIDEO_PLL0_OUT_EVEN, 3 },
93 };
94
95 static const struct clk_parent_data video_cc_parent_data_0[] = {
96 { .index = DT_BI_TCXO },
97 { .hw = &video_pll0_out_even.clkr.hw },
98 };
99
100 static const struct parent_map video_cc_parent_map_1[] = {
101 { P_CHIP_SLEEP_CLK, 0 },
102 };
103
104 static const struct clk_parent_data video_cc_parent_data_1[] = {
105 { .index = DT_SLEEP_CLK },
106 };
107
108 static const struct freq_tbl ftbl_video_cc_iris_clk_src[] = {
109 F(133250000, P_VIDEO_PLL0_OUT_EVEN, 2, 0, 0),
110 F(240000000, P_VIDEO_PLL0_OUT_EVEN, 1.5, 0, 0),
111 F(300000000, P_VIDEO_PLL0_OUT_EVEN, 1, 0, 0),
112 F(380000000, P_VIDEO_PLL0_OUT_EVEN, 1, 0, 0),
113 F(460000000, P_VIDEO_PLL0_OUT_EVEN, 1, 0, 0),
114 { }
115 };
116
117 static struct clk_rcg2 video_cc_iris_clk_src = {
118 .cmd_rcgr = 0x1000,
119 .mnd_width = 0,
120 .hid_width = 5,
121 .parent_map = video_cc_parent_map_0,
122 .freq_tbl = ftbl_video_cc_iris_clk_src,
123 .clkr.hw.init = &(const struct clk_init_data) {
124 .name = "video_cc_iris_clk_src",
125 .parent_data = video_cc_parent_data_0,
126 .num_parents = ARRAY_SIZE(video_cc_parent_data_0),
127 .flags = CLK_SET_RATE_PARENT,
128 .ops = &clk_rcg2_shared_ops,
129 },
130 };
131
132 static const struct freq_tbl ftbl_video_cc_sleep_clk_src[] = {
133 F(32764, P_CHIP_SLEEP_CLK, 1, 0, 0),
134 { }
135 };
136
137 static struct clk_rcg2 video_cc_sleep_clk_src = {
138 .cmd_rcgr = 0x701c,
139 .mnd_width = 0,
140 .hid_width = 5,
141 .parent_map = video_cc_parent_map_1,
142 .freq_tbl = ftbl_video_cc_sleep_clk_src,
143 .clkr.hw.init = &(const struct clk_init_data) {
144 .name = "video_cc_sleep_clk_src",
145 .parent_data = video_cc_parent_data_1,
146 .num_parents = ARRAY_SIZE(video_cc_parent_data_1),
147 .ops = &clk_rcg2_ops,
148 },
149 };
150
151 static struct clk_branch video_cc_iris_ahb_clk = {
152 .halt_reg = 0x5004,
153 .halt_check = BRANCH_VOTED,
154 .clkr = {
155 .enable_reg = 0x5004,
156 .enable_mask = BIT(0),
157 .hw.init = &(const struct clk_init_data) {
158 .name = "video_cc_iris_ahb_clk",
159 .parent_hws = (const struct clk_hw*[]) {
160 &video_cc_iris_clk_src.clkr.hw,
161 },
162 .num_parents = 1,
163 .flags = CLK_SET_RATE_PARENT,
164 .ops = &clk_branch2_ops,
165 },
166 },
167 };
168
169 static struct clk_branch video_cc_mvs0_axi_clk = {
170 .halt_reg = 0x800c,
171 .halt_check = BRANCH_HALT,
172 .clkr = {
173 .enable_reg = 0x800c,
174 .enable_mask = BIT(0),
175 .hw.init = &(const struct clk_init_data) {
176 .name = "video_cc_mvs0_axi_clk",
177 .ops = &clk_branch2_ops,
178 },
179 },
180 };
181
182 static struct clk_branch video_cc_mvs0_core_clk = {
183 .halt_reg = 0x3010,
184 .halt_check = BRANCH_VOTED,
185 .hwcg_reg = 0x3010,
186 .hwcg_bit = 1,
187 .clkr = {
188 .enable_reg = 0x3010,
189 .enable_mask = BIT(0),
190 .hw.init = &(const struct clk_init_data) {
191 .name = "video_cc_mvs0_core_clk",
192 .parent_hws = (const struct clk_hw*[]) {
193 &video_cc_iris_clk_src.clkr.hw,
194 },
195 .num_parents = 1,
196 .flags = CLK_SET_RATE_PARENT,
197 .ops = &clk_branch2_ops,
198 },
199 },
200 };
201
202 static struct clk_branch video_cc_mvsc_core_clk = {
203 .halt_reg = 0x2014,
204 .halt_check = BRANCH_HALT,
205 .clkr = {
206 .enable_reg = 0x2014,
207 .enable_mask = BIT(0),
208 .hw.init = &(const struct clk_init_data) {
209 .name = "video_cc_mvsc_core_clk",
210 .parent_hws = (const struct clk_hw*[]) {
211 &video_cc_iris_clk_src.clkr.hw,
212 },
213 .num_parents = 1,
214 .flags = CLK_SET_RATE_PARENT,
215 .ops = &clk_branch2_ops,
216 },
217 },
218 };
219
220 static struct clk_branch video_cc_mvsc_ctl_axi_clk = {
221 .halt_reg = 0x8004,
222 .halt_check = BRANCH_HALT,
223 .clkr = {
224 .enable_reg = 0x8004,
225 .enable_mask = BIT(0),
226 .hw.init = &(const struct clk_init_data) {
227 .name = "video_cc_mvsc_ctl_axi_clk",
228 .ops = &clk_branch2_ops,
229 },
230 },
231 };
232
233 static struct clk_branch video_cc_sleep_clk = {
234 .halt_reg = 0x7034,
235 .halt_check = BRANCH_HALT,
236 .clkr = {
237 .enable_reg = 0x7034,
238 .enable_mask = BIT(0),
239 .hw.init = &(const struct clk_init_data) {
240 .name = "video_cc_sleep_clk",
241 .parent_hws = (const struct clk_hw*[]) {
242 &video_cc_sleep_clk_src.clkr.hw,
243 },
244 .num_parents = 1,
245 .flags = CLK_SET_RATE_PARENT,
246 .ops = &clk_branch2_ops,
247 },
248 },
249 };
250
251 static struct clk_branch video_cc_venus_ahb_clk = {
252 .halt_reg = 0x801c,
253 .halt_check = BRANCH_HALT,
254 .clkr = {
255 .enable_reg = 0x801c,
256 .enable_mask = BIT(0),
257 .hw.init = &(const struct clk_init_data) {
258 .name = "video_cc_venus_ahb_clk",
259 .ops = &clk_branch2_ops,
260 },
261 },
262 };
263
264 static struct gdsc mvsc_gdsc = {
265 .gdscr = 0x2004,
266 .en_rest_wait_val = 0x2,
267 .en_few_wait_val = 0x2,
268 .clk_dis_wait_val = 0x6,
269 .pd = {
270 .name = "mvsc_gdsc",
271 },
272 .pwrsts = PWRSTS_OFF_ON,
273 };
274
275 static struct gdsc mvs0_gdsc = {
276 .gdscr = 0x3004,
277 .en_rest_wait_val = 0x2,
278 .en_few_wait_val = 0x2,
279 .clk_dis_wait_val = 0x6,
280 .pd = {
281 .name = "mvs0_gdsc",
282 },
283 .pwrsts = PWRSTS_OFF_ON,
284 .flags = HW_CTRL_TRIGGER,
285 };
286
287 static struct gdsc *video_cc_sm6350_gdscs[] = {
288 [MVSC_GDSC] = &mvsc_gdsc,
289 [MVS0_GDSC] = &mvs0_gdsc,
290 };
291
292 static struct clk_regmap *video_cc_sm6350_clocks[] = {
293 [VIDEO_CC_IRIS_AHB_CLK] = &video_cc_iris_ahb_clk.clkr,
294 [VIDEO_CC_IRIS_CLK_SRC] = &video_cc_iris_clk_src.clkr,
295 [VIDEO_CC_MVS0_AXI_CLK] = &video_cc_mvs0_axi_clk.clkr,
296 [VIDEO_CC_MVS0_CORE_CLK] = &video_cc_mvs0_core_clk.clkr,
297 [VIDEO_CC_MVSC_CORE_CLK] = &video_cc_mvsc_core_clk.clkr,
298 [VIDEO_CC_MVSC_CTL_AXI_CLK] = &video_cc_mvsc_ctl_axi_clk.clkr,
299 [VIDEO_CC_SLEEP_CLK] = &video_cc_sleep_clk.clkr,
300 [VIDEO_CC_SLEEP_CLK_SRC] = &video_cc_sleep_clk_src.clkr,
301 [VIDEO_CC_VENUS_AHB_CLK] = &video_cc_venus_ahb_clk.clkr,
302 [VIDEO_PLL0] = &video_pll0.clkr,
303 [VIDEO_PLL0_OUT_EVEN] = &video_pll0_out_even.clkr,
304 };
305
306 static const struct regmap_config video_cc_sm6350_regmap_config = {
307 .reg_bits = 32,
308 .reg_stride = 4,
309 .val_bits = 32,
310 .max_register = 0xb000,
311 .fast_io = true,
312 };
313
314 static const struct qcom_cc_desc video_cc_sm6350_desc = {
315 .config = &video_cc_sm6350_regmap_config,
316 .clks = video_cc_sm6350_clocks,
317 .num_clks = ARRAY_SIZE(video_cc_sm6350_clocks),
318 .gdscs = video_cc_sm6350_gdscs,
319 .num_gdscs = ARRAY_SIZE(video_cc_sm6350_gdscs),
320 };
321
322 static const struct of_device_id video_cc_sm6350_match_table[] = {
323 { .compatible = "qcom,sm6350-videocc" },
324 { }
325 };
326 MODULE_DEVICE_TABLE(of, video_cc_sm6350_match_table);
327
video_cc_sm6350_probe(struct platform_device * pdev)328 static int video_cc_sm6350_probe(struct platform_device *pdev)
329 {
330 struct regmap *regmap;
331
332 regmap = qcom_cc_map(pdev, &video_cc_sm6350_desc);
333 if (IS_ERR(regmap))
334 return PTR_ERR(regmap);
335
336 clk_fabia_pll_configure(&video_pll0, regmap, &video_pll0_config);
337
338 /* Keep some clocks always-on */
339 qcom_branch_set_clk_en(regmap, 0x7018); /* VIDEO_CC_XO_CLK */
340
341 return qcom_cc_really_probe(&pdev->dev, &video_cc_sm6350_desc, regmap);
342 }
343
344 static struct platform_driver video_cc_sm6350_driver = {
345 .probe = video_cc_sm6350_probe,
346 .driver = {
347 .name = "video_cc-sm6350",
348 .of_match_table = video_cc_sm6350_match_table,
349 },
350 };
351
352 module_platform_driver(video_cc_sm6350_driver);
353
354 MODULE_DESCRIPTION("QTI VIDEO_CC SM6350 Driver");
355 MODULE_LICENSE("GPL");
356