1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
6 #include <linux/clk-provider.h>
8 #include <linux/module.h>
9 #include <linux/of_device.h>
10 #include <linux/pm_clock.h>
11 #include <linux/pm_runtime.h>
13 #include <linux/regmap.h>
15 #include <dt-bindings/clock/qcom,lpasscorecc-sc7180.h>
17 #include "clk-alpha-pll.h"
18 #include "clk-branch.h"
20 #include "clk-regmap.h"
26 P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
,
30 static struct pll_vco fabia_vco
[] = {
31 { 249600000, 2000000000, 0 },
34 static const struct alpha_pll_config lpass_lpaaudio_dig_pll_config
= {
37 .config_ctl_val
= 0x20485699,
38 .config_ctl_hi_val
= 0x00002067,
39 .test_ctl_val
= 0x40000000,
40 .test_ctl_hi_val
= 0x00000000,
41 .user_ctl_val
= 0x00005105,
42 .user_ctl_hi_val
= 0x00004805,
45 static const u8 clk_alpha_pll_regs_offset
[][PLL_OFF_MAX_REGS
] = {
46 [CLK_ALPHA_PLL_TYPE_FABIA
] = {
47 [PLL_OFF_L_VAL
] = 0x04,
48 [PLL_OFF_CAL_L_VAL
] = 0x8,
49 [PLL_OFF_USER_CTL
] = 0x0c,
50 [PLL_OFF_USER_CTL_U
] = 0x10,
51 [PLL_OFF_USER_CTL_U1
] = 0x14,
52 [PLL_OFF_CONFIG_CTL
] = 0x18,
53 [PLL_OFF_CONFIG_CTL_U
] = 0x1C,
54 [PLL_OFF_CONFIG_CTL_U1
] = 0x20,
55 [PLL_OFF_TEST_CTL
] = 0x24,
56 [PLL_OFF_TEST_CTL_U
] = 0x28,
57 [PLL_OFF_STATUS
] = 0x30,
58 [PLL_OFF_OPMODE
] = 0x38,
59 [PLL_OFF_FRAC
] = 0x40,
63 static struct clk_alpha_pll lpass_lpaaudio_dig_pll
= {
65 .vco_table
= fabia_vco
,
66 .num_vco
= ARRAY_SIZE(fabia_vco
),
67 .regs
= clk_alpha_pll_regs_offset
[CLK_ALPHA_PLL_TYPE_FABIA
],
69 .hw
.init
= &(struct clk_init_data
){
70 .name
= "lpass_lpaaudio_dig_pll",
71 .parent_data
= &(const struct clk_parent_data
){
75 .ops
= &clk_alpha_pll_fabia_ops
,
80 static const struct clk_div_table
81 post_div_table_lpass_lpaaudio_dig_pll_out_odd
[] = {
86 static struct clk_alpha_pll_postdiv lpass_lpaaudio_dig_pll_out_odd
= {
89 .post_div_table
= post_div_table_lpass_lpaaudio_dig_pll_out_odd
,
91 ARRAY_SIZE(post_div_table_lpass_lpaaudio_dig_pll_out_odd
),
93 .regs
= clk_alpha_pll_regs
[CLK_ALPHA_PLL_TYPE_FABIA
],
94 .clkr
.hw
.init
= &(struct clk_init_data
){
95 .name
= "lpass_lpaaudio_dig_pll_out_odd",
96 .parent_data
= &(const struct clk_parent_data
){
97 .hw
= &lpass_lpaaudio_dig_pll
.clkr
.hw
,
100 .flags
= CLK_SET_RATE_PARENT
,
101 .ops
= &clk_alpha_pll_postdiv_fabia_ops
,
105 static const struct parent_map lpass_core_cc_parent_map_0
[] = {
107 { P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 5 },
110 static const struct clk_parent_data lpass_core_cc_parent_data_0
[] = {
111 { .fw_name
= "bi_tcxo" },
112 { .hw
= &lpass_lpaaudio_dig_pll_out_odd
.clkr
.hw
},
115 static const struct parent_map lpass_core_cc_parent_map_2
[] = {
119 static struct clk_rcg2 core_clk_src
= {
123 .parent_map
= lpass_core_cc_parent_map_2
,
124 .clkr
.hw
.init
= &(struct clk_init_data
){
125 .name
= "core_clk_src",
126 .parent_data
= &(const struct clk_parent_data
){
127 .fw_name
= "bi_tcxo",
130 .ops
= &clk_rcg2_ops
,
134 static const struct freq_tbl ftbl_ext_mclk0_clk_src
[] = {
135 F(9600000, P_BI_TCXO
, 2, 0, 0),
136 F(19200000, P_BI_TCXO
, 1, 0, 0),
140 static const struct freq_tbl ftbl_ext_lpaif_clk_src
[] = {
141 F(256000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 15, 1, 32),
142 F(512000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 15, 1, 16),
143 F(768000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 10, 1, 16),
144 F(1024000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 15, 1, 8),
145 F(1536000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 10, 1, 8),
146 F(2048000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 15, 1, 4),
147 F(3072000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 10, 1, 4),
148 F(4096000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 15, 1, 2),
149 F(6144000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 10, 1, 2),
150 F(8192000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 15, 0, 0),
151 F(9600000, P_BI_TCXO
, 2, 0, 0),
152 F(12288000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 10, 0, 0),
153 F(19200000, P_BI_TCXO
, 1, 0, 0),
154 F(24576000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
, 5, 0, 0),
158 static struct clk_rcg2 ext_mclk0_clk_src
= {
162 .parent_map
= lpass_core_cc_parent_map_0
,
163 .freq_tbl
= ftbl_ext_mclk0_clk_src
,
164 .clkr
.hw
.init
= &(struct clk_init_data
){
165 .name
= "ext_mclk0_clk_src",
166 .parent_data
= lpass_core_cc_parent_data_0
,
168 .flags
= CLK_SET_RATE_PARENT
,
169 .ops
= &clk_rcg2_ops
,
173 static struct clk_rcg2 lpaif_pri_clk_src
= {
177 .parent_map
= lpass_core_cc_parent_map_0
,
178 .freq_tbl
= ftbl_ext_lpaif_clk_src
,
179 .clkr
.hw
.init
= &(struct clk_init_data
){
180 .name
= "lpaif_pri_clk_src",
181 .parent_data
= lpass_core_cc_parent_data_0
,
183 .flags
= CLK_SET_RATE_PARENT
,
184 .ops
= &clk_rcg2_ops
,
188 static struct clk_rcg2 lpaif_sec_clk_src
= {
192 .parent_map
= lpass_core_cc_parent_map_0
,
193 .freq_tbl
= ftbl_ext_lpaif_clk_src
,
194 .clkr
.hw
.init
= &(struct clk_init_data
){
195 .name
= "lpaif_sec_clk_src",
196 .parent_data
= lpass_core_cc_parent_data_0
,
198 .flags
= CLK_SET_RATE_PARENT
,
199 .ops
= &clk_rcg2_ops
,
203 static struct clk_branch lpass_audio_core_ext_mclk0_clk
= {
205 .halt_check
= BRANCH_HALT
,
209 .enable_reg
= 0x20014,
210 .enable_mask
= BIT(0),
211 .hw
.init
= &(struct clk_init_data
){
212 .name
= "lpass_audio_core_ext_mclk0_clk",
213 .parent_data
= &(const struct clk_parent_data
){
214 .hw
= &ext_mclk0_clk_src
.clkr
.hw
,
217 .flags
= CLK_SET_RATE_PARENT
,
218 .ops
= &clk_branch2_ops
,
223 static struct clk_branch lpass_audio_core_lpaif_pri_ibit_clk
= {
225 .halt_check
= BRANCH_HALT
,
229 .enable_reg
= 0x10018,
230 .enable_mask
= BIT(0),
231 .hw
.init
= &(struct clk_init_data
){
232 .name
= "lpass_audio_core_lpaif_pri_ibit_clk",
233 .parent_data
= &(const struct clk_parent_data
){
234 .hw
= &lpaif_pri_clk_src
.clkr
.hw
,
237 .flags
= CLK_SET_RATE_PARENT
,
238 .ops
= &clk_branch2_ops
,
243 static struct clk_branch lpass_audio_core_lpaif_sec_ibit_clk
= {
245 .halt_check
= BRANCH_HALT
,
249 .enable_reg
= 0x11018,
250 .enable_mask
= BIT(0),
251 .hw
.init
= &(struct clk_init_data
){
252 .name
= "lpass_audio_core_lpaif_sec_ibit_clk",
253 .parent_data
= &(const struct clk_parent_data
){
254 .hw
= &lpaif_sec_clk_src
.clkr
.hw
,
257 .flags
= CLK_SET_RATE_PARENT
,
258 .ops
= &clk_branch2_ops
,
263 static struct clk_branch lpass_audio_core_sysnoc_mport_core_clk
= {
265 .halt_check
= BRANCH_HALT
,
269 .enable_reg
= 0x23000,
270 .enable_mask
= BIT(0),
271 .hw
.init
= &(struct clk_init_data
){
272 .name
= "lpass_audio_core_sysnoc_mport_core_clk",
273 .parent_data
= &(const struct clk_parent_data
){
274 .hw
= &core_clk_src
.clkr
.hw
,
277 .flags
= CLK_SET_RATE_PARENT
,
278 .ops
= &clk_branch2_ops
,
283 static struct clk_regmap
*lpass_core_cc_sc7180_clocks
[] = {
284 [EXT_MCLK0_CLK_SRC
] = &ext_mclk0_clk_src
.clkr
,
285 [LPAIF_PRI_CLK_SRC
] = &lpaif_pri_clk_src
.clkr
,
286 [LPAIF_SEC_CLK_SRC
] = &lpaif_sec_clk_src
.clkr
,
287 [CORE_CLK_SRC
] = &core_clk_src
.clkr
,
288 [LPASS_AUDIO_CORE_EXT_MCLK0_CLK
] = &lpass_audio_core_ext_mclk0_clk
.clkr
,
289 [LPASS_AUDIO_CORE_LPAIF_PRI_IBIT_CLK
] =
290 &lpass_audio_core_lpaif_pri_ibit_clk
.clkr
,
291 [LPASS_AUDIO_CORE_LPAIF_SEC_IBIT_CLK
] =
292 &lpass_audio_core_lpaif_sec_ibit_clk
.clkr
,
293 [LPASS_AUDIO_CORE_SYSNOC_MPORT_CORE_CLK
] =
294 &lpass_audio_core_sysnoc_mport_core_clk
.clkr
,
295 [LPASS_LPAAUDIO_DIG_PLL
] = &lpass_lpaaudio_dig_pll
.clkr
,
296 [LPASS_LPAAUDIO_DIG_PLL_OUT_ODD
] = &lpass_lpaaudio_dig_pll_out_odd
.clkr
,
299 static struct gdsc lpass_pdc_hm_gdsc
= {
302 .name
= "lpass_pdc_hm_gdsc",
304 .pwrsts
= PWRSTS_OFF_ON
,
308 static struct gdsc lpass_audio_hm_gdsc
= {
311 .name
= "lpass_audio_hm_gdsc",
313 .pwrsts
= PWRSTS_OFF_ON
,
316 static struct gdsc lpass_core_hm_gdsc
= {
319 .name
= "lpass_core_hm_gdsc",
321 .pwrsts
= PWRSTS_OFF_ON
,
322 .flags
= RETAIN_FF_ENABLE
,
325 static struct gdsc
*lpass_core_hm_sc7180_gdscs
[] = {
326 [LPASS_CORE_HM_GDSCR
] = &lpass_core_hm_gdsc
,
329 static struct gdsc
*lpass_audio_hm_sc7180_gdscs
[] = {
330 [LPASS_PDC_HM_GDSCR
] = &lpass_pdc_hm_gdsc
,
331 [LPASS_AUDIO_HM_GDSCR
] = &lpass_audio_hm_gdsc
,
334 static struct regmap_config lpass_core_cc_sc7180_regmap_config
= {
341 static const struct qcom_cc_desc lpass_core_hm_sc7180_desc
= {
342 .config
= &lpass_core_cc_sc7180_regmap_config
,
343 .gdscs
= lpass_core_hm_sc7180_gdscs
,
344 .num_gdscs
= ARRAY_SIZE(lpass_core_hm_sc7180_gdscs
),
347 static const struct qcom_cc_desc lpass_core_cc_sc7180_desc
= {
348 .config
= &lpass_core_cc_sc7180_regmap_config
,
349 .clks
= lpass_core_cc_sc7180_clocks
,
350 .num_clks
= ARRAY_SIZE(lpass_core_cc_sc7180_clocks
),
353 static const struct qcom_cc_desc lpass_audio_hm_sc7180_desc
= {
354 .config
= &lpass_core_cc_sc7180_regmap_config
,
355 .gdscs
= lpass_audio_hm_sc7180_gdscs
,
356 .num_gdscs
= ARRAY_SIZE(lpass_audio_hm_sc7180_gdscs
),
359 static void lpass_pm_runtime_disable(void *data
)
361 pm_runtime_disable(data
);
364 static void lpass_pm_clk_destroy(void *data
)
366 pm_clk_destroy(data
);
369 static int lpass_create_pm_clks(struct platform_device
*pdev
)
373 pm_runtime_use_autosuspend(&pdev
->dev
);
374 pm_runtime_set_autosuspend_delay(&pdev
->dev
, 500);
375 pm_runtime_enable(&pdev
->dev
);
377 ret
= devm_add_action_or_reset(&pdev
->dev
, lpass_pm_runtime_disable
, &pdev
->dev
);
381 ret
= pm_clk_create(&pdev
->dev
);
384 ret
= devm_add_action_or_reset(&pdev
->dev
, lpass_pm_clk_destroy
, &pdev
->dev
);
388 ret
= pm_clk_add(&pdev
->dev
, "iface");
390 dev_err(&pdev
->dev
, "failed to acquire iface clock\n");
395 static int lpass_core_cc_sc7180_probe(struct platform_device
*pdev
)
397 const struct qcom_cc_desc
*desc
;
398 struct regmap
*regmap
;
401 ret
= lpass_create_pm_clks(pdev
);
405 lpass_core_cc_sc7180_regmap_config
.name
= "lpass_audio_cc";
406 desc
= &lpass_audio_hm_sc7180_desc
;
407 ret
= qcom_cc_probe_by_index(pdev
, 1, desc
);
411 lpass_core_cc_sc7180_regmap_config
.name
= "lpass_core_cc";
412 regmap
= qcom_cc_map(pdev
, &lpass_core_cc_sc7180_desc
);
414 return PTR_ERR(regmap
);
417 * Keep the CLK always-ON
418 * LPASS_AUDIO_CORE_SYSNOC_SWAY_CORE_CLK
420 regmap_update_bits(regmap
, 0x24000, BIT(0), BIT(0));
423 regmap_write(regmap
, 0x1008, 0x20);
424 regmap_update_bits(regmap
, 0x1014, BIT(0), BIT(0));
426 clk_fabia_pll_configure(&lpass_lpaaudio_dig_pll
, regmap
,
427 &lpass_lpaaudio_dig_pll_config
);
429 ret
= qcom_cc_really_probe(pdev
, &lpass_core_cc_sc7180_desc
, regmap
);
431 pm_runtime_mark_last_busy(&pdev
->dev
);
432 pm_runtime_put_autosuspend(&pdev
->dev
);
437 static int lpass_hm_core_probe(struct platform_device
*pdev
)
439 const struct qcom_cc_desc
*desc
;
442 ret
= lpass_create_pm_clks(pdev
);
446 lpass_core_cc_sc7180_regmap_config
.name
= "lpass_hm_core";
447 desc
= &lpass_core_hm_sc7180_desc
;
449 return qcom_cc_probe_by_index(pdev
, 0, desc
);
452 static const struct of_device_id lpass_hm_sc7180_match_table
[] = {
454 .compatible
= "qcom,sc7180-lpasshm",
458 MODULE_DEVICE_TABLE(of
, lpass_hm_sc7180_match_table
);
460 static const struct of_device_id lpass_core_cc_sc7180_match_table
[] = {
462 .compatible
= "qcom,sc7180-lpasscorecc",
466 MODULE_DEVICE_TABLE(of
, lpass_core_cc_sc7180_match_table
);
468 static const struct dev_pm_ops lpass_core_cc_pm_ops
= {
469 SET_RUNTIME_PM_OPS(pm_clk_suspend
, pm_clk_resume
, NULL
)
472 static struct platform_driver lpass_core_cc_sc7180_driver
= {
473 .probe
= lpass_core_cc_sc7180_probe
,
475 .name
= "lpass_core_cc-sc7180",
476 .of_match_table
= lpass_core_cc_sc7180_match_table
,
477 .pm
= &lpass_core_cc_pm_ops
,
481 static const struct dev_pm_ops lpass_hm_pm_ops
= {
482 SET_RUNTIME_PM_OPS(pm_clk_suspend
, pm_clk_resume
, NULL
)
485 static struct platform_driver lpass_hm_sc7180_driver
= {
486 .probe
= lpass_hm_core_probe
,
488 .name
= "lpass_hm-sc7180",
489 .of_match_table
= lpass_hm_sc7180_match_table
,
490 .pm
= &lpass_hm_pm_ops
,
494 static int __init
lpass_sc7180_init(void)
498 ret
= platform_driver_register(&lpass_core_cc_sc7180_driver
);
502 ret
= platform_driver_register(&lpass_hm_sc7180_driver
);
504 platform_driver_unregister(&lpass_core_cc_sc7180_driver
);
510 subsys_initcall(lpass_sc7180_init
);
512 static void __exit
lpass_sc7180_exit(void)
514 platform_driver_unregister(&lpass_hm_sc7180_driver
);
515 platform_driver_unregister(&lpass_core_cc_sc7180_driver
);
517 module_exit(lpass_sc7180_exit
);
519 MODULE_DESCRIPTION("QTI LPASS_CORE_CC SC7180 Driver");
520 MODULE_LICENSE("GPL v2");