1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2018, The Linux Foundation. All rights reserved. 4 */ 5 6 #include <linux/kernel.h> 7 #include <linux/err.h> 8 #include <linux/platform_device.h> 9 #include <linux/clk-provider.h> 10 #include <linux/regmap.h> 11 #include <linux/module.h> 12 #include <linux/clk.h> 13 #include <linux/soc/qcom/smem.h> 14 15 #include <dt-bindings/clock/qcom,apss-ipq.h> 16 #include <dt-bindings/arm/qcom,ids.h> 17 18 #include "common.h" 19 #include "clk-regmap.h" 20 #include "clk-branch.h" 21 #include "clk-alpha-pll.h" 22 #include "clk-rcg.h" 23 24 enum { 25 P_XO, 26 P_GPLL0, 27 P_APSS_PLL_EARLY, 28 }; 29 30 static const struct clk_parent_data parents_apcs_alias0_clk_src[] = { 31 { .fw_name = "xo" }, 32 { .fw_name = "gpll0" }, 33 { .fw_name = "pll" }, 34 }; 35 36 static const struct parent_map parents_apcs_alias0_clk_src_map[] = { 37 { P_XO, 0 }, 38 { P_GPLL0, 4 }, 39 { P_APSS_PLL_EARLY, 5 }, 40 }; 41 42 static struct clk_rcg2 apcs_alias0_clk_src = { 43 .cmd_rcgr = 0x0050, 44 .hid_width = 5, 45 .parent_map = parents_apcs_alias0_clk_src_map, 46 .clkr.hw.init = &(struct clk_init_data){ 47 .name = "apcs_alias0_clk_src", 48 .parent_data = parents_apcs_alias0_clk_src, 49 .num_parents = ARRAY_SIZE(parents_apcs_alias0_clk_src), 50 .ops = &clk_rcg2_mux_closest_ops, 51 .flags = CLK_SET_RATE_PARENT, 52 }, 53 }; 54 55 static struct clk_branch apcs_alias0_core_clk = { 56 .halt_reg = 0x0058, 57 .clkr = { 58 .enable_reg = 0x0058, 59 .enable_mask = BIT(0), 60 .hw.init = &(struct clk_init_data){ 61 .name = "apcs_alias0_core_clk", 62 .parent_hws = (const struct clk_hw *[]){ 63 &apcs_alias0_clk_src.clkr.hw }, 64 .num_parents = 1, 65 .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 66 .ops = &clk_branch2_ops, 67 }, 68 }, 69 }; 70 71 static const struct regmap_config apss_ipq6018_regmap_config = { 72 .reg_bits = 32, 73 .reg_stride = 4, 74 .val_bits = 32, 75 .max_register = 0x1000, 76 .fast_io = true, 77 }; 78 79 static struct clk_regmap *apss_ipq6018_clks[] = { 80 [APCS_ALIAS0_CLK_SRC] = &apcs_alias0_clk_src.clkr, 81 [APCS_ALIAS0_CORE_CLK] = &apcs_alias0_core_clk.clkr, 82 }; 83 84 static const struct qcom_cc_desc apss_ipq6018_desc = { 85 .config = &apss_ipq6018_regmap_config, 86 .clks = apss_ipq6018_clks, 87 .num_clks = ARRAY_SIZE(apss_ipq6018_clks), 88 }; 89 90 static int cpu_clk_notifier_fn(struct notifier_block *nb, unsigned long action, 91 void *data) 92 { 93 struct clk_hw *hw; 94 u8 index; 95 int err; 96 97 if (action == PRE_RATE_CHANGE) 98 index = P_GPLL0; 99 else if (action == POST_RATE_CHANGE || action == ABORT_RATE_CHANGE) 100 index = P_APSS_PLL_EARLY; 101 else 102 return NOTIFY_OK; 103 104 hw = &apcs_alias0_clk_src.clkr.hw; 105 err = clk_rcg2_mux_closest_ops.set_parent(hw, index); 106 107 return notifier_from_errno(err); 108 } 109 110 static int apss_ipq6018_probe(struct platform_device *pdev) 111 { 112 struct clk_hw *hw = &apcs_alias0_clk_src.clkr.hw; 113 struct notifier_block *cpu_clk_notifier; 114 struct regmap *regmap; 115 u32 soc_id; 116 int ret; 117 118 ret = qcom_smem_get_soc_id(&soc_id); 119 if (ret) 120 return ret; 121 122 regmap = dev_get_regmap(pdev->dev.parent, NULL); 123 if (!regmap) 124 return -ENODEV; 125 126 ret = qcom_cc_really_probe(&pdev->dev, &apss_ipq6018_desc, regmap); 127 if (ret) 128 return ret; 129 130 switch (soc_id) { 131 /* Only below variants of IPQ53xx support scaling */ 132 case QCOM_ID_IPQ5332: 133 case QCOM_ID_IPQ5322: 134 case QCOM_ID_IPQ5300: 135 cpu_clk_notifier = devm_kzalloc(&pdev->dev, 136 sizeof(*cpu_clk_notifier), 137 GFP_KERNEL); 138 if (!cpu_clk_notifier) 139 return -ENOMEM; 140 141 cpu_clk_notifier->notifier_call = cpu_clk_notifier_fn; 142 143 ret = devm_clk_notifier_register(&pdev->dev, hw->clk, cpu_clk_notifier); 144 if (ret) 145 return ret; 146 break; 147 default: 148 break; 149 } 150 151 return 0; 152 } 153 154 static struct platform_driver apss_ipq6018_driver = { 155 .probe = apss_ipq6018_probe, 156 .driver = { 157 .name = "qcom,apss-ipq6018-clk", 158 }, 159 }; 160 161 module_platform_driver(apss_ipq6018_driver); 162 163 MODULE_DESCRIPTION("QCOM APSS IPQ 6018 CLK Driver"); 164 MODULE_LICENSE("GPL v2"); 165