|
9 | 9 | #include <linux/clk-provider.h>
|
10 | 10 | #include <linux/regmap.h>
|
11 | 11 | #include <linux/module.h>
|
| 12 | +#include <linux/clk.h> |
| 13 | +#include <linux/soc/qcom/smem.h> |
12 | 14 |
|
13 | 15 | #include <dt-bindings/clock/qcom,apss-ipq.h>
|
| 16 | +#include <dt-bindings/arm/qcom,ids.h> |
14 | 17 |
|
15 | 18 | #include "common.h"
|
16 | 19 | #include "clk-regmap.h"
|
|
20 | 23 |
|
21 | 24 | enum {
|
22 | 25 | P_XO,
|
| 26 | + P_GPLL0, |
23 | 27 | P_APSS_PLL_EARLY,
|
24 | 28 | };
|
25 | 29 |
|
26 | 30 | static const struct clk_parent_data parents_apcs_alias0_clk_src[] = {
|
27 | 31 | { .fw_name = "xo" },
|
| 32 | + { .fw_name = "gpll0" }, |
28 | 33 | { .fw_name = "pll" },
|
29 | 34 | };
|
30 | 35 |
|
31 | 36 | static const struct parent_map parents_apcs_alias0_clk_src_map[] = {
|
32 | 37 | { P_XO, 0 },
|
| 38 | + { P_GPLL0, 4 }, |
33 | 39 | { P_APSS_PLL_EARLY, 5 },
|
34 | 40 | };
|
35 | 41 |
|
@@ -81,15 +87,68 @@ static const struct qcom_cc_desc apss_ipq6018_desc = {
|
81 | 87 | .num_clks = ARRAY_SIZE(apss_ipq6018_clks),
|
82 | 88 | };
|
83 | 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 | + |
84 | 110 | static int apss_ipq6018_probe(struct platform_device *pdev)
|
85 | 111 | {
|
| 112 | + struct clk_hw *hw = &apcs_alias0_clk_src.clkr.hw; |
| 113 | + struct notifier_block *cpu_clk_notifier; |
86 | 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; |
87 | 121 |
|
88 | 122 | regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
89 | 123 | if (!regmap)
|
90 | 124 | return -ENODEV;
|
91 | 125 |
|
92 |
| - return qcom_cc_really_probe(pdev, &apss_ipq6018_desc, regmap); |
| 126 | + ret = qcom_cc_really_probe(pdev, &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; |
93 | 152 | }
|
94 | 153 |
|
95 | 154 | static struct platform_driver apss_ipq6018_driver = {
|
|
0 commit comments