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 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