1239268Sgonzo// SPDX-License-Identifier: GPL-2.0
2244469Scognet/*
3239268Sgonzo * Copyright (c) 2022, 2023 Linaro Ltd.
4239268Sgonzo */
5239268Sgonzo#include <linux/bitfield.h>
6239268Sgonzo#include <linux/clk.h>
7239268Sgonzo#include <linux/clk-provider.h>
8239268Sgonzo#include <linux/interconnect-clk.h>
9239268Sgonzo#include <linux/interconnect-provider.h>
10239268Sgonzo#include <linux/of.h>
11239268Sgonzo#include <linux/module.h>
12239268Sgonzo#include <linux/platform_device.h>
13239268Sgonzo#include <linux/regmap.h>
14239268Sgonzo
15239268Sgonzo#include <dt-bindings/interconnect/qcom,msm8996-cbf.h>
16239268Sgonzo
17239268Sgonzo#include "clk-alpha-pll.h"
18239268Sgonzo#include "clk-regmap.h"
19239268Sgonzo
20239268Sgonzo/* Need to match the order of clocks in DT binding */
21239268Sgonzoenum {
22239268Sgonzo	DT_XO,
23239268Sgonzo	DT_APCS_AUX,
24239268Sgonzo};
25239268Sgonzo
26239268Sgonzoenum {
27239268Sgonzo	CBF_XO_INDEX,
28239268Sgonzo	CBF_PLL_INDEX,
29239268Sgonzo	CBF_DIV_INDEX,
30239268Sgonzo	CBF_APCS_AUX_INDEX,
31239268Sgonzo};
32239268Sgonzo
33239268Sgonzo#define DIV_THRESHOLD		600000000
34239268Sgonzo
35239268Sgonzo#define CBF_MUX_OFFSET		0x18
36239268Sgonzo#define CBF_MUX_PARENT_MASK		GENMASK(1, 0)
37239268Sgonzo#define CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK GENMASK(5, 4)
38239268Sgonzo#define CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL \
39239268Sgonzo	FIELD_PREP(CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 0x03)
40239268Sgonzo#define CBF_MUX_AUTO_CLK_SEL_BIT	BIT(6)
41239268Sgonzo
42239268Sgonzo#define CBF_PLL_OFFSET 0xf000
43239268Sgonzo
44244469Scognetstatic struct alpha_pll_config cbfpll_config = {
45239268Sgonzo	.l = 72,
46239268Sgonzo	.config_ctl_val = 0x200d4828,
47239268Sgonzo	.config_ctl_hi_val = 0x006,
48239268Sgonzo	.test_ctl_val = 0x1c000000,
49246713Skib	.test_ctl_hi_val = 0x00004000,
50239268Sgonzo	.pre_div_mask = BIT(12),
51239268Sgonzo	.post_div_mask = 0x3 << 8,
52246713Skib	.post_div_val = 0x1 << 8,
53239268Sgonzo	.main_output_mask = BIT(0),
54239268Sgonzo	.early_output_mask = BIT(3),
55239268Sgonzo};
56239268Sgonzo
57239268Sgonzostatic struct clk_alpha_pll cbf_pll = {
58244469Scognet	.offset = CBF_PLL_OFFSET,
59244469Scognet	.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_HUAYRA_APSS],
60239268Sgonzo	.flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE,
61239268Sgonzo	.clkr.hw.init = &(struct clk_init_data){
62239268Sgonzo		.name = "cbf_pll",
63239268Sgonzo		.parent_data = (const struct clk_parent_data[]) {
64239268Sgonzo			{ .index = DT_XO, },
65239268Sgonzo		},
66239268Sgonzo		.num_parents = 1,
67239268Sgonzo		.ops = &clk_alpha_pll_hwfsm_ops,
68239268Sgonzo	},
69239268Sgonzo};
70239268Sgonzo
71239268Sgonzostatic struct clk_fixed_factor cbf_pll_postdiv = {
72239268Sgonzo	.mult = 1,
73239268Sgonzo	.div = 2,
74239268Sgonzo	.hw.init = &(struct clk_init_data){
75239268Sgonzo		.name = "cbf_pll_postdiv",
76239268Sgonzo		.parent_hws = (const struct clk_hw*[]){
77239268Sgonzo			&cbf_pll.clkr.hw
78239268Sgonzo		},
79239268Sgonzo		.num_parents = 1,
80239268Sgonzo		.ops = &clk_fixed_factor_ops,
81239268Sgonzo		.flags = CLK_SET_RATE_PARENT,
82239268Sgonzo	},
83239268Sgonzo};
84239268Sgonzo
85239268Sgonzostatic const struct clk_parent_data cbf_mux_parent_data[] = {
86239268Sgonzo	{ .index = DT_XO },
87239268Sgonzo	{ .hw = &cbf_pll.clkr.hw },
88239268Sgonzo	{ .hw = &cbf_pll_postdiv.hw },
89239268Sgonzo	{ .index = DT_APCS_AUX },
90239268Sgonzo};
91239268Sgonzo
92239268Sgonzostruct clk_cbf_8996_mux {
93239268Sgonzo	u32 reg;
94239268Sgonzo	struct notifier_block nb;
95239268Sgonzo	struct clk_regmap clkr;
96239268Sgonzo};
97244469Scognet
98244469Scognetstatic struct clk_cbf_8996_mux *to_clk_cbf_8996_mux(struct clk_regmap *clkr)
99244469Scognet{
100244469Scognet	return container_of(clkr, struct clk_cbf_8996_mux, clkr);
101244469Scognet}
102244469Scognet
103244469Scognetstatic int cbf_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
104239268Sgonzo			       void *data);
105244469Scognet
106239268Sgonzostatic u8 clk_cbf_8996_mux_get_parent(struct clk_hw *hw)
107239268Sgonzo{
108239268Sgonzo	struct clk_regmap *clkr = to_clk_regmap(hw);
109239268Sgonzo	struct clk_cbf_8996_mux *mux = to_clk_cbf_8996_mux(clkr);
110239268Sgonzo	u32 val;
111239268Sgonzo
112246713Skib	regmap_read(clkr->regmap, mux->reg, &val);
113239268Sgonzo
114239268Sgonzo	return FIELD_GET(CBF_MUX_PARENT_MASK, val);
115239268Sgonzo}
116239268Sgonzo
117239268Sgonzostatic int clk_cbf_8996_mux_set_parent(struct clk_hw *hw, u8 index)
118239268Sgonzo{
119239268Sgonzo	struct clk_regmap *clkr = to_clk_regmap(hw);
120239268Sgonzo	struct clk_cbf_8996_mux *mux = to_clk_cbf_8996_mux(clkr);
121239268Sgonzo	u32 val;
122239268Sgonzo
123239268Sgonzo	val = FIELD_PREP(CBF_MUX_PARENT_MASK, index);
124239268Sgonzo
125239268Sgonzo	return regmap_update_bits(clkr->regmap, mux->reg, CBF_MUX_PARENT_MASK, val);
126239268Sgonzo}
127239268Sgonzo
128239268Sgonzostatic int clk_cbf_8996_mux_determine_rate(struct clk_hw *hw,
129239268Sgonzo					   struct clk_rate_request *req)
130239268Sgonzo{
131239268Sgonzo	struct clk_hw *parent;
132239268Sgonzo
133239268Sgonzo	if (req->rate < (DIV_THRESHOLD / cbf_pll_postdiv.div))
134239268Sgonzo		return -EINVAL;
135239268Sgonzo
136239268Sgonzo	if (req->rate < DIV_THRESHOLD)
137239268Sgonzo		parent = clk_hw_get_parent_by_index(hw, CBF_DIV_INDEX);
138239268Sgonzo	else
139239268Sgonzo		parent = clk_hw_get_parent_by_index(hw, CBF_PLL_INDEX);
140239268Sgonzo
141239268Sgonzo	if (!parent)
142239268Sgonzo		return -EINVAL;
143239268Sgonzo
144239268Sgonzo	req->best_parent_rate = clk_hw_round_rate(parent, req->rate);
145239268Sgonzo	req->best_parent_hw = parent;
146239268Sgonzo
147239268Sgonzo	return 0;
148239268Sgonzo}
149239268Sgonzo
150239268Sgonzostatic const struct clk_ops clk_cbf_8996_mux_ops = {
151239268Sgonzo	.set_parent = clk_cbf_8996_mux_set_parent,
152239268Sgonzo	.get_parent = clk_cbf_8996_mux_get_parent,
153239268Sgonzo	.determine_rate = clk_cbf_8996_mux_determine_rate,
154239268Sgonzo};
155239268Sgonzo
156239268Sgonzostatic struct clk_cbf_8996_mux cbf_mux = {
157246713Skib	.reg = CBF_MUX_OFFSET,
158239268Sgonzo	.nb.notifier_call = cbf_clk_notifier_cb,
159239268Sgonzo	.clkr.hw.init = &(struct clk_init_data) {
160239268Sgonzo		.name = "cbf_mux",
161244469Scognet		.parent_data = cbf_mux_parent_data,
162244469Scognet		.num_parents = ARRAY_SIZE(cbf_mux_parent_data),
163239268Sgonzo		.ops = &clk_cbf_8996_mux_ops,
164246713Skib		/* CPU clock is critical and should never be gated */
165246713Skib		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
166239268Sgonzo	},
167239268Sgonzo};
168239268Sgonzo
169239268Sgonzostatic int cbf_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
170239268Sgonzo			       void *data)
171239268Sgonzo{
172239268Sgonzo	struct clk_notifier_data *cnd = data;
173239268Sgonzo
174239268Sgonzo	switch (event) {
175239268Sgonzo	case PRE_RATE_CHANGE:
176239268Sgonzo		/*
177246713Skib		 * Avoid overvolting. clk_core_set_rate_nolock() walks from top
178246713Skib		 * to bottom, so it will change the rate of the PLL before
179239268Sgonzo		 * chaging the parent of PMUX. This can result in pmux getting
180254061Scognet		 * clocked twice the expected rate.
181246713Skib		 *
182239268Sgonzo		 * Manually switch to PLL/2 here.
183246713Skib		 */
184246713Skib		if (cnd->old_rate > DIV_THRESHOLD &&
185246713Skib		    cnd->new_rate < DIV_THRESHOLD)
186246713Skib			clk_cbf_8996_mux_set_parent(&cbf_mux.clkr.hw, CBF_DIV_INDEX);
187239268Sgonzo		break;
188244469Scognet	case ABORT_RATE_CHANGE:
189244469Scognet		/* Revert manual change */
190244469Scognet		if (cnd->new_rate < DIV_THRESHOLD &&
191244469Scognet		    cnd->old_rate > DIV_THRESHOLD)
192244469Scognet			clk_cbf_8996_mux_set_parent(&cbf_mux.clkr.hw, CBF_PLL_INDEX);
193252652Sgonzo		break;
194244469Scognet	default:
195252652Sgonzo		break;
196252652Sgonzo	}
197244469Scognet
198244469Scognet	return notifier_from_errno(0);
199244469Scognet};
200244469Scognet
201244469Scognetstatic struct clk_hw *cbf_msm8996_hw_clks[] = {
202252652Sgonzo	&cbf_pll_postdiv.hw,
203244469Scognet};
204252652Sgonzo
205252652Sgonzostatic struct clk_regmap *cbf_msm8996_clks[] = {
206252652Sgonzo	&cbf_pll.clkr,
207252652Sgonzo	&cbf_mux.clkr,
208252652Sgonzo};
209252652Sgonzo
210252652Sgonzostatic const struct regmap_config cbf_msm8996_regmap_config = {
211252652Sgonzo	.reg_bits		= 32,
212252652Sgonzo	.reg_stride		= 4,
213244469Scognet	.val_bits		= 32,
214244469Scognet	.max_register		= 0x10000,
215244469Scognet	.fast_io		= true,
216244469Scognet	.val_format_endian	= REGMAP_ENDIAN_LITTLE,
217244469Scognet};
218244469Scognet
219244469Scognet#ifdef CONFIG_INTERCONNECT
220244469Scognet
221252652Sgonzo/* Random ID that doesn't clash with main qnoc and OSM */
222244469Scognet#define CBF_MASTER_NODE 2000
223244469Scognet
224244469Scognetstatic int qcom_msm8996_cbf_icc_register(struct platform_device *pdev, struct clk_hw *cbf_hw)
225244469Scognet{
226244469Scognet	struct device *dev = &pdev->dev;
227267992Shselasky	struct clk *clk = devm_clk_hw_get_clk(dev, cbf_hw, "cbf");
228267992Shselasky	const struct icc_clk_data data[] = {
229244469Scognet		{ .clk = clk, .name = "cbf", },
230267992Shselasky	};
231244469Scognet	struct icc_provider *provider;
232269136Sian
233269136Sian	provider = icc_clk_register(dev, CBF_MASTER_NODE, ARRAY_SIZE(data), data);
234269136Sian	if (IS_ERR(provider))
235269136Sian		return PTR_ERR(provider);
236269136Sian
237269136Sian	platform_set_drvdata(pdev, provider);
238269136Sian
239269136Sian	return 0;
240269136Sian}
241269136Sian
242269136Sianstatic void qcom_msm8996_cbf_icc_remove(struct platform_device *pdev)
243269136Sian{
244269206Sian	struct icc_provider *provider = platform_get_drvdata(pdev);
245269206Sian
246239268Sgonzo	icc_clk_unregister(provider);
247239268Sgonzo}
248269136Sian#define qcom_msm8996_cbf_icc_sync_state icc_sync_state
249269136Sian#else
250269136Sianstatic int qcom_msm8996_cbf_icc_register(struct platform_device *pdev,  struct clk_hw *cbf_hw)
251269136Sian{
252239268Sgonzo	dev_warn(&pdev->dev, "CONFIG_INTERCONNECT is disabled, CBF clock is fixed\n");
253239268Sgonzo
254239268Sgonzo	return 0;
255239268Sgonzo}
256239268Sgonzo#define qcom_msm8996_cbf_icc_remove(pdev) { }
257239268Sgonzo#define qcom_msm8996_cbf_icc_sync_state NULL
258239268Sgonzo#endif
259239268Sgonzo
260239268Sgonzostatic int qcom_msm8996_cbf_probe(struct platform_device *pdev)
261269206Sian{
262269206Sian	void __iomem *base;
263269206Sian	struct regmap *regmap;
264269206Sian	struct device *dev = &pdev->dev;
265269206Sian	int i, ret;
266269206Sian
267269206Sian	base = devm_platform_ioremap_resource(pdev, 0);
268269206Sian	if (IS_ERR(base))
269269206Sian		return PTR_ERR(base);
270269206Sian
271269206Sian	regmap = devm_regmap_init_mmio(dev, base, &cbf_msm8996_regmap_config);
272269206Sian	if (IS_ERR(regmap))
273269206Sian		return PTR_ERR(regmap);
274269206Sian
275269206Sian	/* Select GPLL0 for 300MHz for the CBF clock */
276269206Sian	regmap_write(regmap, CBF_MUX_OFFSET, 0x3);
277269206Sian
278269206Sian	/* Ensure write goes through before PLLs are reconfigured */
279269206Sian	udelay(5);
280269206Sian
281239268Sgonzo	/* Set the auto clock sel always-on source to GPLL0/2 (300MHz) */
282239268Sgonzo	regmap_update_bits(regmap, CBF_MUX_OFFSET,
283239268Sgonzo			   CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK,
284239268Sgonzo			   CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL);
285239268Sgonzo
286239268Sgonzo	clk_alpha_pll_configure(&cbf_pll, regmap, &cbfpll_config);
287239268Sgonzo
288239268Sgonzo	/* Wait for PLL(s) to lock */
289239268Sgonzo	udelay(50);
290239268Sgonzo
291239268Sgonzo	/* Enable auto clock selection for CBF */
292239268Sgonzo	regmap_update_bits(regmap, CBF_MUX_OFFSET,
293239268Sgonzo			   CBF_MUX_AUTO_CLK_SEL_BIT,
294239268Sgonzo			   CBF_MUX_AUTO_CLK_SEL_BIT);
295239268Sgonzo
296239268Sgonzo	/* Ensure write goes through before muxes are switched */
297239268Sgonzo	udelay(5);
298239268Sgonzo
299239268Sgonzo	/* Switch CBF to use the primary PLL */
300239268Sgonzo	regmap_update_bits(regmap, CBF_MUX_OFFSET, CBF_MUX_PARENT_MASK, 0x1);
301239268Sgonzo
302239268Sgonzo	if (of_device_is_compatible(dev->of_node, "qcom,msm8996pro-cbf")) {
303239268Sgonzo		cbfpll_config.post_div_val = 0x3 << 8;
304239268Sgonzo		cbf_pll_postdiv.div = 4;
305239268Sgonzo	}
306254061Scognet
307239268Sgonzo	for (i = 0; i < ARRAY_SIZE(cbf_msm8996_hw_clks); i++) {
308239268Sgonzo		ret = devm_clk_hw_register(dev, cbf_msm8996_hw_clks[i]);
309239268Sgonzo		if (ret)
310239268Sgonzo			return ret;
311239268Sgonzo	}
312239268Sgonzo
313239268Sgonzo	for (i = 0; i < ARRAY_SIZE(cbf_msm8996_clks); i++) {
314269206Sian		ret = devm_clk_register_regmap(dev, cbf_msm8996_clks[i]);
315269206Sian		if (ret)
316239268Sgonzo			return ret;
317239268Sgonzo	}
318239268Sgonzo
319239268Sgonzo	ret = devm_clk_notifier_register(dev, cbf_mux.clkr.hw.clk, &cbf_mux.nb);
320239268Sgonzo	if (ret)
321239268Sgonzo		return ret;
322239268Sgonzo
323239268Sgonzo	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &cbf_mux.clkr.hw);
324239268Sgonzo	if (ret)
325239268Sgonzo		return ret;
326239268Sgonzo
327239268Sgonzo	return qcom_msm8996_cbf_icc_register(pdev, &cbf_mux.clkr.hw);
328239268Sgonzo}
329239268Sgonzo
330239268Sgonzostatic void qcom_msm8996_cbf_remove(struct platform_device *pdev)
331239268Sgonzo{
332239268Sgonzo	qcom_msm8996_cbf_icc_remove(pdev);
333239268Sgonzo}
334239268Sgonzo
335239268Sgonzostatic const struct of_device_id qcom_msm8996_cbf_match_table[] = {
336239268Sgonzo	{ .compatible = "qcom,msm8996-cbf" },
337239268Sgonzo	{ .compatible = "qcom,msm8996pro-cbf" },
338239268Sgonzo	{ /* sentinel */ },
339239268Sgonzo};
340239268SgonzoMODULE_DEVICE_TABLE(of, qcom_msm8996_cbf_match_table);
341239268Sgonzo
342239268Sgonzostatic struct platform_driver qcom_msm8996_cbf_driver = {
343239268Sgonzo	.probe = qcom_msm8996_cbf_probe,
344239268Sgonzo	.remove_new = qcom_msm8996_cbf_remove,
345239268Sgonzo	.driver = {
346239268Sgonzo		.name = "qcom-msm8996-cbf",
347239268Sgonzo		.of_match_table = qcom_msm8996_cbf_match_table,
348239268Sgonzo		.sync_state = qcom_msm8996_cbf_icc_sync_state,
349239268Sgonzo	},
350239268Sgonzo};
351239268Sgonzo
352239268Sgonzo/* Register early enough to fix the clock to be used for other cores */
353239268Sgonzostatic int __init qcom_msm8996_cbf_init(void)
354239268Sgonzo{
355239268Sgonzo	return platform_driver_register(&qcom_msm8996_cbf_driver);
356239268Sgonzo}
357239268Sgonzopostcore_initcall(qcom_msm8996_cbf_init);
358239268Sgonzo
359239268Sgonzostatic void __exit qcom_msm8996_cbf_exit(void)
360239268Sgonzo{
361239268Sgonzo	platform_driver_unregister(&qcom_msm8996_cbf_driver);
362239268Sgonzo}
363239268Sgonzomodule_exit(qcom_msm8996_cbf_exit);
364239268Sgonzo
365239268SgonzoMODULE_DESCRIPTION("QCOM MSM8996 CPU Bus Fabric Clock Driver");
366239268SgonzoMODULE_LICENSE("GPL");
367239268Sgonzo