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