1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2022, 2023 Linaro Ltd.
5 #include <linux/bitfield.h>
7 #include <linux/clk-provider.h>
8 #include <linux/interconnect-clk.h>
9 #include <linux/interconnect-provider.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/regmap.h>
15 #include <dt-bindings/interconnect/qcom,msm8996-cbf.h>
17 #include "clk-alpha-pll.h"
18 #include "clk-regmap.h"
20 /* Need to match the order of clocks in DT binding */
33 #define DIV_THRESHOLD 600000000
35 #define CBF_MUX_OFFSET 0x18
36 #define CBF_MUX_PARENT_MASK GENMASK(1, 0)
37 #define CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK GENMASK(5, 4)
38 #define CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL \
39 FIELD_PREP(CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 0x03)
40 #define CBF_MUX_AUTO_CLK_SEL_BIT BIT(6)
42 #define CBF_PLL_OFFSET 0xf000
44 static struct alpha_pll_config cbfpll_config
= {
46 .config_ctl_val
= 0x200d4828,
47 .config_ctl_hi_val
= 0x006,
48 .test_ctl_val
= 0x1c000000,
49 .test_ctl_hi_val
= 0x00004000,
50 .pre_div_mask
= BIT(12),
51 .post_div_mask
= 0x3 << 8,
52 .post_div_val
= 0x1 << 8,
53 .main_output_mask
= BIT(0),
54 .early_output_mask
= BIT(3),
57 static struct clk_alpha_pll cbf_pll
= {
58 .offset
= CBF_PLL_OFFSET
,
59 .regs
= clk_alpha_pll_regs
[CLK_ALPHA_PLL_TYPE_HUAYRA_APSS
],
60 .flags
= SUPPORTS_DYNAMIC_UPDATE
| SUPPORTS_FSM_MODE
,
61 .clkr
.hw
.init
= &(struct clk_init_data
){
63 .parent_data
= (const struct clk_parent_data
[]) {
67 .ops
= &clk_alpha_pll_hwfsm_ops
,
71 static struct clk_fixed_factor cbf_pll_postdiv
= {
74 .hw
.init
= &(struct clk_init_data
){
75 .name
= "cbf_pll_postdiv",
76 .parent_hws
= (const struct clk_hw
*[]){
80 .ops
= &clk_fixed_factor_ops
,
81 .flags
= CLK_SET_RATE_PARENT
,
85 static const struct clk_parent_data cbf_mux_parent_data
[] = {
87 { .hw
= &cbf_pll
.clkr
.hw
},
88 { .hw
= &cbf_pll_postdiv
.hw
},
89 { .index
= DT_APCS_AUX
},
92 struct clk_cbf_8996_mux
{
94 struct notifier_block nb
;
95 struct clk_regmap clkr
;
98 static struct clk_cbf_8996_mux
*to_clk_cbf_8996_mux(struct clk_regmap
*clkr
)
100 return container_of(clkr
, struct clk_cbf_8996_mux
, clkr
);
103 static int cbf_clk_notifier_cb(struct notifier_block
*nb
, unsigned long event
,
106 static u8
clk_cbf_8996_mux_get_parent(struct clk_hw
*hw
)
108 struct clk_regmap
*clkr
= to_clk_regmap(hw
);
109 struct clk_cbf_8996_mux
*mux
= to_clk_cbf_8996_mux(clkr
);
112 regmap_read(clkr
->regmap
, mux
->reg
, &val
);
114 return FIELD_GET(CBF_MUX_PARENT_MASK
, val
);
117 static int clk_cbf_8996_mux_set_parent(struct clk_hw
*hw
, u8 index
)
119 struct clk_regmap
*clkr
= to_clk_regmap(hw
);
120 struct clk_cbf_8996_mux
*mux
= to_clk_cbf_8996_mux(clkr
);
123 val
= FIELD_PREP(CBF_MUX_PARENT_MASK
, index
);
125 return regmap_update_bits(clkr
->regmap
, mux
->reg
, CBF_MUX_PARENT_MASK
, val
);
128 static int clk_cbf_8996_mux_determine_rate(struct clk_hw
*hw
,
129 struct clk_rate_request
*req
)
131 struct clk_hw
*parent
;
133 if (req
->rate
< (DIV_THRESHOLD
/ cbf_pll_postdiv
.div
))
136 if (req
->rate
< DIV_THRESHOLD
)
137 parent
= clk_hw_get_parent_by_index(hw
, CBF_DIV_INDEX
);
139 parent
= clk_hw_get_parent_by_index(hw
, CBF_PLL_INDEX
);
144 req
->best_parent_rate
= clk_hw_round_rate(parent
, req
->rate
);
145 req
->best_parent_hw
= parent
;
150 static const struct clk_ops clk_cbf_8996_mux_ops
= {
151 .set_parent
= clk_cbf_8996_mux_set_parent
,
152 .get_parent
= clk_cbf_8996_mux_get_parent
,
153 .determine_rate
= clk_cbf_8996_mux_determine_rate
,
156 static struct clk_cbf_8996_mux cbf_mux
= {
157 .reg
= CBF_MUX_OFFSET
,
158 .nb
.notifier_call
= cbf_clk_notifier_cb
,
159 .clkr
.hw
.init
= &(struct clk_init_data
) {
161 .parent_data
= cbf_mux_parent_data
,
162 .num_parents
= ARRAY_SIZE(cbf_mux_parent_data
),
163 .ops
= &clk_cbf_8996_mux_ops
,
164 /* CPU clock is critical and should never be gated */
165 .flags
= CLK_SET_RATE_PARENT
| CLK_IS_CRITICAL
,
169 static int cbf_clk_notifier_cb(struct notifier_block
*nb
, unsigned long event
,
172 struct clk_notifier_data
*cnd
= data
;
175 case PRE_RATE_CHANGE
:
177 * Avoid overvolting. clk_core_set_rate_nolock() walks from top
178 * to bottom, so it will change the rate of the PLL before
179 * chaging the parent of PMUX. This can result in pmux getting
180 * clocked twice the expected rate.
182 * Manually switch to PLL/2 here.
184 if (cnd
->old_rate
> DIV_THRESHOLD
&&
185 cnd
->new_rate
< DIV_THRESHOLD
)
186 clk_cbf_8996_mux_set_parent(&cbf_mux
.clkr
.hw
, CBF_DIV_INDEX
);
188 case ABORT_RATE_CHANGE
:
189 /* Revert manual change */
190 if (cnd
->new_rate
< DIV_THRESHOLD
&&
191 cnd
->old_rate
> DIV_THRESHOLD
)
192 clk_cbf_8996_mux_set_parent(&cbf_mux
.clkr
.hw
, CBF_PLL_INDEX
);
198 return notifier_from_errno(0);
201 static struct clk_hw
*cbf_msm8996_hw_clks
[] = {
205 static struct clk_regmap
*cbf_msm8996_clks
[] = {
210 static const struct regmap_config cbf_msm8996_regmap_config
= {
214 .max_register
= 0x10000,
216 .val_format_endian
= REGMAP_ENDIAN_LITTLE
,
219 #ifdef CONFIG_INTERCONNECT
221 /* Random ID that doesn't clash with main qnoc and OSM */
222 #define CBF_MASTER_NODE 2000
224 static int qcom_msm8996_cbf_icc_register(struct platform_device
*pdev
, struct clk_hw
*cbf_hw
)
226 struct device
*dev
= &pdev
->dev
;
227 struct clk
*clk
= devm_clk_hw_get_clk(dev
, cbf_hw
, "cbf");
228 const struct icc_clk_data data
[] = {
232 .master_id
= MASTER_CBF_M4M
,
233 .slave_id
= SLAVE_CBF_M4M
,
236 struct icc_provider
*provider
;
238 provider
= icc_clk_register(dev
, CBF_MASTER_NODE
, ARRAY_SIZE(data
), data
);
239 if (IS_ERR(provider
))
240 return PTR_ERR(provider
);
242 platform_set_drvdata(pdev
, provider
);
247 static void qcom_msm8996_cbf_icc_remove(struct platform_device
*pdev
)
249 struct icc_provider
*provider
= platform_get_drvdata(pdev
);
251 icc_clk_unregister(provider
);
253 #define qcom_msm8996_cbf_icc_sync_state icc_sync_state
255 static int qcom_msm8996_cbf_icc_register(struct platform_device
*pdev
, struct clk_hw
*cbf_hw
)
257 dev_warn(&pdev
->dev
, "CONFIG_INTERCONNECT is disabled, CBF clock is fixed\n");
261 #define qcom_msm8996_cbf_icc_remove(pdev) { }
262 #define qcom_msm8996_cbf_icc_sync_state NULL
265 static int qcom_msm8996_cbf_probe(struct platform_device
*pdev
)
268 struct regmap
*regmap
;
269 struct device
*dev
= &pdev
->dev
;
272 base
= devm_platform_ioremap_resource(pdev
, 0);
274 return PTR_ERR(base
);
276 regmap
= devm_regmap_init_mmio(dev
, base
, &cbf_msm8996_regmap_config
);
278 return PTR_ERR(regmap
);
280 /* Select GPLL0 for 300MHz for the CBF clock */
281 regmap_write(regmap
, CBF_MUX_OFFSET
, 0x3);
283 /* Ensure write goes through before PLLs are reconfigured */
286 /* Set the auto clock sel always-on source to GPLL0/2 (300MHz) */
287 regmap_update_bits(regmap
, CBF_MUX_OFFSET
,
288 CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK
,
289 CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL
);
291 clk_alpha_pll_configure(&cbf_pll
, regmap
, &cbfpll_config
);
293 /* Wait for PLL(s) to lock */
296 /* Enable auto clock selection for CBF */
297 regmap_update_bits(regmap
, CBF_MUX_OFFSET
,
298 CBF_MUX_AUTO_CLK_SEL_BIT
,
299 CBF_MUX_AUTO_CLK_SEL_BIT
);
301 /* Ensure write goes through before muxes are switched */
304 /* Switch CBF to use the primary PLL */
305 regmap_update_bits(regmap
, CBF_MUX_OFFSET
, CBF_MUX_PARENT_MASK
, 0x1);
307 if (of_device_is_compatible(dev
->of_node
, "qcom,msm8996pro-cbf")) {
308 cbfpll_config
.post_div_val
= 0x3 << 8;
309 cbf_pll_postdiv
.div
= 4;
312 for (i
= 0; i
< ARRAY_SIZE(cbf_msm8996_hw_clks
); i
++) {
313 ret
= devm_clk_hw_register(dev
, cbf_msm8996_hw_clks
[i
]);
318 for (i
= 0; i
< ARRAY_SIZE(cbf_msm8996_clks
); i
++) {
319 ret
= devm_clk_register_regmap(dev
, cbf_msm8996_clks
[i
]);
324 ret
= devm_clk_notifier_register(dev
, cbf_mux
.clkr
.hw
.clk
, &cbf_mux
.nb
);
328 ret
= devm_of_clk_add_hw_provider(dev
, of_clk_hw_simple_get
, &cbf_mux
.clkr
.hw
);
332 return qcom_msm8996_cbf_icc_register(pdev
, &cbf_mux
.clkr
.hw
);
335 static void qcom_msm8996_cbf_remove(struct platform_device
*pdev
)
337 qcom_msm8996_cbf_icc_remove(pdev
);
340 static const struct of_device_id qcom_msm8996_cbf_match_table
[] = {
341 { .compatible
= "qcom,msm8996-cbf" },
342 { .compatible
= "qcom,msm8996pro-cbf" },
345 MODULE_DEVICE_TABLE(of
, qcom_msm8996_cbf_match_table
);
347 static struct platform_driver qcom_msm8996_cbf_driver
= {
348 .probe
= qcom_msm8996_cbf_probe
,
349 .remove
= qcom_msm8996_cbf_remove
,
351 .name
= "qcom-msm8996-cbf",
352 .of_match_table
= qcom_msm8996_cbf_match_table
,
353 .sync_state
= qcom_msm8996_cbf_icc_sync_state
,
357 /* Register early enough to fix the clock to be used for other cores */
358 static int __init
qcom_msm8996_cbf_init(void)
360 return platform_driver_register(&qcom_msm8996_cbf_driver
);
362 postcore_initcall(qcom_msm8996_cbf_init
);
364 static void __exit
qcom_msm8996_cbf_exit(void)
366 platform_driver_unregister(&qcom_msm8996_cbf_driver
);
368 module_exit(qcom_msm8996_cbf_exit
);
370 MODULE_DESCRIPTION("QCOM MSM8996 CPU Bus Fabric Clock Driver");
371 MODULE_LICENSE("GPL");