18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Copyright (c) 2018, The Linux Foundation. All rights reserved.
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/kernel.h>
58c2ecf20Sopenharmony_ci#include <linux/init.h>
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/of.h>
118c2ecf20Sopenharmony_ci#include <linux/of_device.h>
128c2ecf20Sopenharmony_ci#include <linux/clk.h>
138c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "clk-krait.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic unsigned int sec_mux_map[] = {
198c2ecf20Sopenharmony_ci	2,
208c2ecf20Sopenharmony_ci	0,
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic unsigned int pri_mux_map[] = {
248c2ecf20Sopenharmony_ci	1,
258c2ecf20Sopenharmony_ci	2,
268c2ecf20Sopenharmony_ci	0,
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * Notifier function for switching the muxes to safe parent
318c2ecf20Sopenharmony_ci * while the hfpll is getting reprogrammed.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_cistatic int krait_notifier_cb(struct notifier_block *nb,
348c2ecf20Sopenharmony_ci			     unsigned long event,
358c2ecf20Sopenharmony_ci			     void *data)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	int ret = 0;
388c2ecf20Sopenharmony_ci	struct krait_mux_clk *mux = container_of(nb, struct krait_mux_clk,
398c2ecf20Sopenharmony_ci						 clk_nb);
408c2ecf20Sopenharmony_ci	/* Switch to safe parent */
418c2ecf20Sopenharmony_ci	if (event == PRE_RATE_CHANGE) {
428c2ecf20Sopenharmony_ci		mux->old_index = krait_mux_clk_ops.get_parent(&mux->hw);
438c2ecf20Sopenharmony_ci		ret = krait_mux_clk_ops.set_parent(&mux->hw, mux->safe_sel);
448c2ecf20Sopenharmony_ci		mux->reparent = false;
458c2ecf20Sopenharmony_ci	/*
468c2ecf20Sopenharmony_ci	 * By the time POST_RATE_CHANGE notifier is called,
478c2ecf20Sopenharmony_ci	 * clk framework itself would have changed the parent for the new rate.
488c2ecf20Sopenharmony_ci	 * Only otherwise, put back to the old parent.
498c2ecf20Sopenharmony_ci	 */
508c2ecf20Sopenharmony_ci	} else if (event == POST_RATE_CHANGE) {
518c2ecf20Sopenharmony_ci		if (!mux->reparent)
528c2ecf20Sopenharmony_ci			ret = krait_mux_clk_ops.set_parent(&mux->hw,
538c2ecf20Sopenharmony_ci							   mux->old_index);
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	return notifier_from_errno(ret);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int krait_notifier_register(struct device *dev, struct clk *clk,
608c2ecf20Sopenharmony_ci				   struct krait_mux_clk *mux)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	int ret = 0;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	mux->clk_nb.notifier_call = krait_notifier_cb;
658c2ecf20Sopenharmony_ci	ret = clk_notifier_register(clk, &mux->clk_nb);
668c2ecf20Sopenharmony_ci	if (ret)
678c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register clock notifier: %d\n", ret);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return ret;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int
738c2ecf20Sopenharmony_cikrait_add_div(struct device *dev, int id, const char *s, unsigned int offset)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct krait_div2_clk *div;
768c2ecf20Sopenharmony_ci	struct clk_init_data init = {
778c2ecf20Sopenharmony_ci		.num_parents = 1,
788c2ecf20Sopenharmony_ci		.ops = &krait_div2_clk_ops,
798c2ecf20Sopenharmony_ci		.flags = CLK_SET_RATE_PARENT,
808c2ecf20Sopenharmony_ci	};
818c2ecf20Sopenharmony_ci	const char *p_names[1];
828c2ecf20Sopenharmony_ci	struct clk *clk;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
858c2ecf20Sopenharmony_ci	if (!div)
868c2ecf20Sopenharmony_ci		return -ENOMEM;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	div->width = 2;
898c2ecf20Sopenharmony_ci	div->shift = 6;
908c2ecf20Sopenharmony_ci	div->lpl = id >= 0;
918c2ecf20Sopenharmony_ci	div->offset = offset;
928c2ecf20Sopenharmony_ci	div->hw.init = &init;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
958c2ecf20Sopenharmony_ci	if (!init.name)
968c2ecf20Sopenharmony_ci		return -ENOMEM;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	init.parent_names = p_names;
998c2ecf20Sopenharmony_ci	p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
1008c2ecf20Sopenharmony_ci	if (!p_names[0]) {
1018c2ecf20Sopenharmony_ci		kfree(init.name);
1028c2ecf20Sopenharmony_ci		return -ENOMEM;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	clk = devm_clk_register(dev, &div->hw);
1068c2ecf20Sopenharmony_ci	kfree(p_names[0]);
1078c2ecf20Sopenharmony_ci	kfree(init.name);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(clk);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int
1138c2ecf20Sopenharmony_cikrait_add_sec_mux(struct device *dev, int id, const char *s,
1148c2ecf20Sopenharmony_ci		  unsigned int offset, bool unique_aux)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	int ret;
1178c2ecf20Sopenharmony_ci	struct krait_mux_clk *mux;
1188c2ecf20Sopenharmony_ci	static const char *sec_mux_list[] = {
1198c2ecf20Sopenharmony_ci		"acpu_aux",
1208c2ecf20Sopenharmony_ci		"qsb",
1218c2ecf20Sopenharmony_ci	};
1228c2ecf20Sopenharmony_ci	struct clk_init_data init = {
1238c2ecf20Sopenharmony_ci		.parent_names = sec_mux_list,
1248c2ecf20Sopenharmony_ci		.num_parents = ARRAY_SIZE(sec_mux_list),
1258c2ecf20Sopenharmony_ci		.ops = &krait_mux_clk_ops,
1268c2ecf20Sopenharmony_ci		.flags = CLK_SET_RATE_PARENT,
1278c2ecf20Sopenharmony_ci	};
1288c2ecf20Sopenharmony_ci	struct clk *clk;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
1318c2ecf20Sopenharmony_ci	if (!mux)
1328c2ecf20Sopenharmony_ci		return -ENOMEM;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	mux->offset = offset;
1358c2ecf20Sopenharmony_ci	mux->lpl = id >= 0;
1368c2ecf20Sopenharmony_ci	mux->mask = 0x3;
1378c2ecf20Sopenharmony_ci	mux->shift = 2;
1388c2ecf20Sopenharmony_ci	mux->parent_map = sec_mux_map;
1398c2ecf20Sopenharmony_ci	mux->hw.init = &init;
1408c2ecf20Sopenharmony_ci	mux->safe_sel = 0;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
1438c2ecf20Sopenharmony_ci	if (!init.name)
1448c2ecf20Sopenharmony_ci		return -ENOMEM;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (unique_aux) {
1478c2ecf20Sopenharmony_ci		sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
1488c2ecf20Sopenharmony_ci		if (!sec_mux_list[0]) {
1498c2ecf20Sopenharmony_ci			clk = ERR_PTR(-ENOMEM);
1508c2ecf20Sopenharmony_ci			goto err_aux;
1518c2ecf20Sopenharmony_ci		}
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	clk = devm_clk_register(dev, &mux->hw);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ret = krait_notifier_register(dev, clk, mux);
1578c2ecf20Sopenharmony_ci	if (ret)
1588c2ecf20Sopenharmony_ci		goto unique_aux;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ciunique_aux:
1618c2ecf20Sopenharmony_ci	if (unique_aux)
1628c2ecf20Sopenharmony_ci		kfree(sec_mux_list[0]);
1638c2ecf20Sopenharmony_cierr_aux:
1648c2ecf20Sopenharmony_ci	kfree(init.name);
1658c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(clk);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic struct clk *
1698c2ecf20Sopenharmony_cikrait_add_pri_mux(struct device *dev, int id, const char *s,
1708c2ecf20Sopenharmony_ci		  unsigned int offset)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	int ret;
1738c2ecf20Sopenharmony_ci	struct krait_mux_clk *mux;
1748c2ecf20Sopenharmony_ci	const char *p_names[3];
1758c2ecf20Sopenharmony_ci	struct clk_init_data init = {
1768c2ecf20Sopenharmony_ci		.parent_names = p_names,
1778c2ecf20Sopenharmony_ci		.num_parents = ARRAY_SIZE(p_names),
1788c2ecf20Sopenharmony_ci		.ops = &krait_mux_clk_ops,
1798c2ecf20Sopenharmony_ci		.flags = CLK_SET_RATE_PARENT,
1808c2ecf20Sopenharmony_ci	};
1818c2ecf20Sopenharmony_ci	struct clk *clk;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
1848c2ecf20Sopenharmony_ci	if (!mux)
1858c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	mux->mask = 0x3;
1888c2ecf20Sopenharmony_ci	mux->shift = 0;
1898c2ecf20Sopenharmony_ci	mux->offset = offset;
1908c2ecf20Sopenharmony_ci	mux->lpl = id >= 0;
1918c2ecf20Sopenharmony_ci	mux->parent_map = pri_mux_map;
1928c2ecf20Sopenharmony_ci	mux->hw.init = &init;
1938c2ecf20Sopenharmony_ci	mux->safe_sel = 2;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
1968c2ecf20Sopenharmony_ci	if (!init.name)
1978c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
2008c2ecf20Sopenharmony_ci	if (!p_names[0]) {
2018c2ecf20Sopenharmony_ci		clk = ERR_PTR(-ENOMEM);
2028c2ecf20Sopenharmony_ci		goto err_p0;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
2068c2ecf20Sopenharmony_ci	if (!p_names[1]) {
2078c2ecf20Sopenharmony_ci		clk = ERR_PTR(-ENOMEM);
2088c2ecf20Sopenharmony_ci		goto err_p1;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
2128c2ecf20Sopenharmony_ci	if (!p_names[2]) {
2138c2ecf20Sopenharmony_ci		clk = ERR_PTR(-ENOMEM);
2148c2ecf20Sopenharmony_ci		goto err_p2;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	clk = devm_clk_register(dev, &mux->hw);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	ret = krait_notifier_register(dev, clk, mux);
2208c2ecf20Sopenharmony_ci	if (ret)
2218c2ecf20Sopenharmony_ci		goto err_p3;
2228c2ecf20Sopenharmony_cierr_p3:
2238c2ecf20Sopenharmony_ci	kfree(p_names[2]);
2248c2ecf20Sopenharmony_cierr_p2:
2258c2ecf20Sopenharmony_ci	kfree(p_names[1]);
2268c2ecf20Sopenharmony_cierr_p1:
2278c2ecf20Sopenharmony_ci	kfree(p_names[0]);
2288c2ecf20Sopenharmony_cierr_p0:
2298c2ecf20Sopenharmony_ci	kfree(init.name);
2308c2ecf20Sopenharmony_ci	return clk;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci/* id < 0 for L2, otherwise id == physical CPU number */
2348c2ecf20Sopenharmony_cistatic struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	int ret;
2378c2ecf20Sopenharmony_ci	unsigned int offset;
2388c2ecf20Sopenharmony_ci	void *p = NULL;
2398c2ecf20Sopenharmony_ci	const char *s;
2408c2ecf20Sopenharmony_ci	struct clk *clk;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (id >= 0) {
2438c2ecf20Sopenharmony_ci		offset = 0x4501 + (0x1000 * id);
2448c2ecf20Sopenharmony_ci		s = p = kasprintf(GFP_KERNEL, "%d", id);
2458c2ecf20Sopenharmony_ci		if (!s)
2468c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
2478c2ecf20Sopenharmony_ci	} else {
2488c2ecf20Sopenharmony_ci		offset = 0x500;
2498c2ecf20Sopenharmony_ci		s = "_l2";
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	ret = krait_add_div(dev, id, s, offset);
2538c2ecf20Sopenharmony_ci	if (ret) {
2548c2ecf20Sopenharmony_ci		clk = ERR_PTR(ret);
2558c2ecf20Sopenharmony_ci		goto err;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	ret = krait_add_sec_mux(dev, id, s, offset, unique_aux);
2598c2ecf20Sopenharmony_ci	if (ret) {
2608c2ecf20Sopenharmony_ci		clk = ERR_PTR(ret);
2618c2ecf20Sopenharmony_ci		goto err;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	clk = krait_add_pri_mux(dev, id, s, offset);
2658c2ecf20Sopenharmony_cierr:
2668c2ecf20Sopenharmony_ci	kfree(p);
2678c2ecf20Sopenharmony_ci	return clk;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	unsigned int idx = clkspec->args[0];
2738c2ecf20Sopenharmony_ci	struct clk **clks = data;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (idx >= 5) {
2768c2ecf20Sopenharmony_ci		pr_err("%s: invalid clock index %d\n", __func__, idx);
2778c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return clks[idx] ? : ERR_PTR(-ENODEV);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic const struct of_device_id krait_cc_match_table[] = {
2848c2ecf20Sopenharmony_ci	{ .compatible = "qcom,krait-cc-v1", (void *)1UL },
2858c2ecf20Sopenharmony_ci	{ .compatible = "qcom,krait-cc-v2" },
2868c2ecf20Sopenharmony_ci	{}
2878c2ecf20Sopenharmony_ci};
2888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, krait_cc_match_table);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int krait_cc_probe(struct platform_device *pdev)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
2938c2ecf20Sopenharmony_ci	const struct of_device_id *id;
2948c2ecf20Sopenharmony_ci	unsigned long cur_rate, aux_rate;
2958c2ecf20Sopenharmony_ci	int cpu;
2968c2ecf20Sopenharmony_ci	struct clk *clk;
2978c2ecf20Sopenharmony_ci	struct clk **clks;
2988c2ecf20Sopenharmony_ci	struct clk *l2_pri_mux_clk;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	id = of_match_device(krait_cc_match_table, dev);
3018c2ecf20Sopenharmony_ci	if (!id)
3028c2ecf20Sopenharmony_ci		return -ENODEV;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
3058c2ecf20Sopenharmony_ci	clk = clk_register_fixed_rate(dev, "qsb", NULL, 0, 1);
3068c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
3078c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (!id->data) {
3108c2ecf20Sopenharmony_ci		clk = clk_register_fixed_factor(dev, "acpu_aux",
3118c2ecf20Sopenharmony_ci						"gpll0_vote", 0, 1, 2);
3128c2ecf20Sopenharmony_ci		if (IS_ERR(clk))
3138c2ecf20Sopenharmony_ci			return PTR_ERR(clk);
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* Krait configurations have at most 4 CPUs and one L2 */
3178c2ecf20Sopenharmony_ci	clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL);
3188c2ecf20Sopenharmony_ci	if (!clks)
3198c2ecf20Sopenharmony_ci		return -ENOMEM;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
3228c2ecf20Sopenharmony_ci		clk = krait_add_clks(dev, cpu, id->data);
3238c2ecf20Sopenharmony_ci		if (IS_ERR(clk))
3248c2ecf20Sopenharmony_ci			return PTR_ERR(clk);
3258c2ecf20Sopenharmony_ci		clks[cpu] = clk;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	l2_pri_mux_clk = krait_add_clks(dev, -1, id->data);
3298c2ecf20Sopenharmony_ci	if (IS_ERR(l2_pri_mux_clk))
3308c2ecf20Sopenharmony_ci		return PTR_ERR(l2_pri_mux_clk);
3318c2ecf20Sopenharmony_ci	clks[4] = l2_pri_mux_clk;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/*
3348c2ecf20Sopenharmony_ci	 * We don't want the CPU or L2 clocks to be turned off at late init
3358c2ecf20Sopenharmony_ci	 * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
3368c2ecf20Sopenharmony_ci	 * refcount of these clocks. Any cpufreq/hotplug manager can assume
3378c2ecf20Sopenharmony_ci	 * that the clocks have already been prepared and enabled by the time
3388c2ecf20Sopenharmony_ci	 * they take over.
3398c2ecf20Sopenharmony_ci	 */
3408c2ecf20Sopenharmony_ci	for_each_online_cpu(cpu) {
3418c2ecf20Sopenharmony_ci		clk_prepare_enable(l2_pri_mux_clk);
3428c2ecf20Sopenharmony_ci		WARN(clk_prepare_enable(clks[cpu]),
3438c2ecf20Sopenharmony_ci		     "Unable to turn on CPU%d clock", cpu);
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/*
3478c2ecf20Sopenharmony_ci	 * Force reinit of HFPLLs and muxes to overwrite any potential
3488c2ecf20Sopenharmony_ci	 * incorrect configuration of HFPLLs and muxes by the bootloader.
3498c2ecf20Sopenharmony_ci	 * While at it, also make sure the cores are running at known rates
3508c2ecf20Sopenharmony_ci	 * and print the current rate.
3518c2ecf20Sopenharmony_ci	 *
3528c2ecf20Sopenharmony_ci	 * The clocks are set to aux clock rate first to make sure the
3538c2ecf20Sopenharmony_ci	 * secondary mux is not sourcing off of QSB. The rate is then set to
3548c2ecf20Sopenharmony_ci	 * two different rates to force a HFPLL reinit under all
3558c2ecf20Sopenharmony_ci	 * circumstances.
3568c2ecf20Sopenharmony_ci	 */
3578c2ecf20Sopenharmony_ci	cur_rate = clk_get_rate(l2_pri_mux_clk);
3588c2ecf20Sopenharmony_ci	aux_rate = 384000000;
3598c2ecf20Sopenharmony_ci	if (cur_rate == 1) {
3608c2ecf20Sopenharmony_ci		pr_info("L2 @ QSB rate. Forcing new rate.\n");
3618c2ecf20Sopenharmony_ci		cur_rate = aux_rate;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci	clk_set_rate(l2_pri_mux_clk, aux_rate);
3648c2ecf20Sopenharmony_ci	clk_set_rate(l2_pri_mux_clk, 2);
3658c2ecf20Sopenharmony_ci	clk_set_rate(l2_pri_mux_clk, cur_rate);
3668c2ecf20Sopenharmony_ci	pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000);
3678c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
3688c2ecf20Sopenharmony_ci		clk = clks[cpu];
3698c2ecf20Sopenharmony_ci		cur_rate = clk_get_rate(clk);
3708c2ecf20Sopenharmony_ci		if (cur_rate == 1) {
3718c2ecf20Sopenharmony_ci			pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu);
3728c2ecf20Sopenharmony_ci			cur_rate = aux_rate;
3738c2ecf20Sopenharmony_ci		}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		clk_set_rate(clk, aux_rate);
3768c2ecf20Sopenharmony_ci		clk_set_rate(clk, 2);
3778c2ecf20Sopenharmony_ci		clk_set_rate(clk, cur_rate);
3788c2ecf20Sopenharmony_ci		pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000);
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	of_clk_add_provider(dev->of_node, krait_of_get, clks);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	return 0;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic struct platform_driver krait_cc_driver = {
3878c2ecf20Sopenharmony_ci	.probe = krait_cc_probe,
3888c2ecf20Sopenharmony_ci	.driver = {
3898c2ecf20Sopenharmony_ci		.name = "krait-cc",
3908c2ecf20Sopenharmony_ci		.of_match_table = krait_cc_match_table,
3918c2ecf20Sopenharmony_ci	},
3928c2ecf20Sopenharmony_ci};
3938c2ecf20Sopenharmony_cimodule_platform_driver(krait_cc_driver);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Krait CPU Clock Driver");
3968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
3978c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:krait-cc");
398