aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTaniya Das <tdas@codeaurora.org>2018-05-09 01:56:07 -0400
committerStephen Boyd <sboyd@kernel.org>2018-07-16 13:25:04 -0400
commit9c7e47025a6b9a2800eec127996ad58946e0cad4 (patch)
treebd7a36d0000b22866146b7b331f11ab41f820a27
parentce397d215ccd07b8ae3f71db689aedb85d56ab40 (diff)
clk: qcom: clk-rpmh: Add QCOM RPMh clock driver
Add the RPMh clock driver to control the RPMh managed clock resources on some of the Qualcomm Technologies, Inc. SoCs. Signed-off-by: Taniya Das <tdas@codeaurora.org> [sboyd@kernel.org: Clean up whitespace, indentation, remove cmd_db_ready check] Signed-off-by: Stephen Boyd <sboyd@kernel.org>
-rw-r--r--drivers/clk/qcom/Kconfig9
-rw-r--r--drivers/clk/qcom/Makefile1
-rw-r--r--drivers/clk/qcom/clk-rpmh.c329
3 files changed, 339 insertions, 0 deletions
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 9c3480dcc38a..2b69cf22b323 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -59,6 +59,15 @@ config QCOM_CLK_SMD_RPM
59 Say Y if you want to support the clocks exposed by the RPM on 59 Say Y if you want to support the clocks exposed by the RPM on
60 platforms such as apq8016, apq8084, msm8974 etc. 60 platforms such as apq8016, apq8084, msm8974 etc.
61 61
62config QCOM_CLK_RPMH
63 tristate "RPMh Clock Driver"
64 depends on COMMON_CLK_QCOM && QCOM_RPMH
65 help
66 RPMh manages shared resources on some Qualcomm Technologies, Inc.
67 SoCs. It accepts requests from other hardware subsystems via RSC.
68 Say Y if you want to support the clocks exposed by RPMh on
69 platforms such as SDM845.
70
62config APQ_GCC_8084 71config APQ_GCC_8084
63 tristate "APQ8084 Global Clock Controller" 72 tristate "APQ8084 Global Clock Controller"
64 select QCOM_GDSC 73 select QCOM_GDSC
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 762c01137c2f..599ab9154480 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
37obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o 37obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o
38obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o 38obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o
39obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o 39obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
40obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o
40obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o 41obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
41obj-$(CONFIG_SDM_GCC_845) += gcc-sdm845.o 42obj-$(CONFIG_SDM_GCC_845) += gcc-sdm845.o
42obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o 43obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o
diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c
new file mode 100644
index 000000000000..9f4fc7773fb2
--- /dev/null
+++ b/drivers/clk/qcom/clk-rpmh.c
@@ -0,0 +1,329 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
4 */
5
6#include <linux/clk-provider.h>
7#include <linux/err.h>
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/of.h>
11#include <linux/of_device.h>
12#include <linux/platform_device.h>
13#include <soc/qcom/cmd-db.h>
14#include <soc/qcom/rpmh.h>
15
16#include <dt-bindings/clock/qcom,rpmh.h>
17
18#define CLK_RPMH_ARC_EN_OFFSET 0
19#define CLK_RPMH_VRM_EN_OFFSET 4
20
21/**
22 * struct clk_rpmh - individual rpmh clock data structure
23 * @hw: handle between common and hardware-specific interfaces
24 * @res_name: resource name for the rpmh clock
25 * @div: clock divider to compute the clock rate
26 * @res_addr: base address of the rpmh resource within the RPMh
27 * @res_on_val: rpmh clock enable value
28 * @state: rpmh clock requested state
29 * @aggr_state: rpmh clock aggregated state
30 * @last_sent_aggr_state: rpmh clock last aggr state sent to RPMh
31 * @valid_state_mask: mask to determine the state of the rpmh clock
32 * @dev: device to which it is attached
33 * @peer: pointer to the clock rpmh sibling
34 */
35struct clk_rpmh {
36 struct clk_hw hw;
37 const char *res_name;
38 u8 div;
39 u32 res_addr;
40 u32 res_on_val;
41 u32 state;
42 u32 aggr_state;
43 u32 last_sent_aggr_state;
44 u32 valid_state_mask;
45 struct device *dev;
46 struct clk_rpmh *peer;
47};
48
49struct clk_rpmh_desc {
50 struct clk_hw **clks;
51 size_t num_clks;
52};
53
54static DEFINE_MUTEX(rpmh_clk_lock);
55
56#define __DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name, \
57 _res_en_offset, _res_on, _div) \
58 static struct clk_rpmh _platform##_##_name_active; \
59 static struct clk_rpmh _platform##_##_name = { \
60 .res_name = _res_name, \
61 .res_addr = _res_en_offset, \
62 .res_on_val = _res_on, \
63 .div = _div, \
64 .peer = &_platform##_##_name_active, \
65 .valid_state_mask = (BIT(RPMH_WAKE_ONLY_STATE) | \
66 BIT(RPMH_ACTIVE_ONLY_STATE) | \
67 BIT(RPMH_SLEEP_STATE)), \
68 .hw.init = &(struct clk_init_data){ \
69 .ops = &clk_rpmh_ops, \
70 .name = #_name, \
71 .parent_names = (const char *[]){ "xo_board" }, \
72 .num_parents = 1, \
73 }, \
74 }; \
75 static struct clk_rpmh _platform##_##_name_active = { \
76 .res_name = _res_name, \
77 .res_addr = _res_en_offset, \
78 .res_on_val = _res_on, \
79 .div = _div, \
80 .peer = &_platform##_##_name, \
81 .valid_state_mask = (BIT(RPMH_WAKE_ONLY_STATE) | \
82 BIT(RPMH_ACTIVE_ONLY_STATE)), \
83 .hw.init = &(struct clk_init_data){ \
84 .ops = &clk_rpmh_ops, \
85 .name = #_name_active, \
86 .parent_names = (const char *[]){ "xo_board" }, \
87 .num_parents = 1, \
88 }, \
89 }
90
91#define DEFINE_CLK_RPMH_ARC(_platform, _name, _name_active, _res_name, \
92 _res_on, _div) \
93 __DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name, \
94 CLK_RPMH_ARC_EN_OFFSET, _res_on, _div)
95
96#define DEFINE_CLK_RPMH_VRM(_platform, _name, _name_active, _res_name, \
97 _div) \
98 __DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name, \
99 CLK_RPMH_VRM_EN_OFFSET, 1, _div)
100
101static inline struct clk_rpmh *to_clk_rpmh(struct clk_hw *_hw)
102{
103 return container_of(_hw, struct clk_rpmh, hw);
104}
105
106static inline bool has_state_changed(struct clk_rpmh *c, u32 state)
107{
108 return (c->last_sent_aggr_state & BIT(state))
109 != (c->aggr_state & BIT(state));
110}
111
112static int clk_rpmh_send_aggregate_command(struct clk_rpmh *c)
113{
114 struct tcs_cmd cmd = { 0 };
115 u32 cmd_state, on_val;
116 enum rpmh_state state = RPMH_SLEEP_STATE;
117 int ret;
118
119 cmd.addr = c->res_addr;
120 cmd_state = c->aggr_state;
121 on_val = c->res_on_val;
122
123 for (; state <= RPMH_ACTIVE_ONLY_STATE; state++) {
124 if (has_state_changed(c, state)) {
125 if (cmd_state & BIT(state))
126 cmd.data = on_val;
127
128 ret = rpmh_write_async(c->dev, state, &cmd, 1);
129 if (ret) {
130 dev_err(c->dev, "set %s state of %s failed: (%d)\n",
131 !state ? "sleep" :
132 state == RPMH_WAKE_ONLY_STATE ?
133 "wake" : "active", c->res_name, ret);
134 return ret;
135 }
136 }
137 }
138
139 c->last_sent_aggr_state = c->aggr_state;
140 c->peer->last_sent_aggr_state = c->last_sent_aggr_state;
141
142 return 0;
143}
144
145/*
146 * Update state and aggregate state values based on enable value.
147 */
148static int clk_rpmh_aggregate_state_send_command(struct clk_rpmh *c,
149 bool enable)
150{
151 int ret;
152
153 /* Nothing required to be done if already off or on */
154 if (enable == c->state)
155 return 0;
156
157 c->state = enable ? c->valid_state_mask : 0;
158 c->aggr_state = c->state | c->peer->state;
159 c->peer->aggr_state = c->aggr_state;
160
161 ret = clk_rpmh_send_aggregate_command(c);
162 if (!ret)
163 return 0;
164
165 if (ret && enable)
166 c->state = 0;
167 else if (ret)
168 c->state = c->valid_state_mask;
169
170 WARN(1, "clk: %s failed to %s\n", c->res_name,
171 enable ? "enable" : "disable");
172 return ret;
173}
174
175static int clk_rpmh_prepare(struct clk_hw *hw)
176{
177 struct clk_rpmh *c = to_clk_rpmh(hw);
178 int ret = 0;
179
180 mutex_lock(&rpmh_clk_lock);
181 ret = clk_rpmh_aggregate_state_send_command(c, true);
182 mutex_unlock(&rpmh_clk_lock);
183
184 return ret;
185};
186
187static void clk_rpmh_unprepare(struct clk_hw *hw)
188{
189 struct clk_rpmh *c = to_clk_rpmh(hw);
190
191 mutex_lock(&rpmh_clk_lock);
192 clk_rpmh_aggregate_state_send_command(c, false);
193 mutex_unlock(&rpmh_clk_lock);
194};
195
196static unsigned long clk_rpmh_recalc_rate(struct clk_hw *hw,
197 unsigned long prate)
198{
199 struct clk_rpmh *r = to_clk_rpmh(hw);
200
201 /*
202 * RPMh clocks have a fixed rate. Return static rate.
203 */
204 return prate / r->div;
205}
206
207static const struct clk_ops clk_rpmh_ops = {
208 .prepare = clk_rpmh_prepare,
209 .unprepare = clk_rpmh_unprepare,
210 .recalc_rate = clk_rpmh_recalc_rate,
211};
212
213/* Resource name must match resource id present in cmd-db. */
214DEFINE_CLK_RPMH_ARC(sdm845, bi_tcxo, bi_tcxo_ao, "xo.lvl", 0x3, 2);
215DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk2, ln_bb_clk2_ao, "lnbclka2", 2);
216DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk3, ln_bb_clk3_ao, "lnbclka3", 2);
217DEFINE_CLK_RPMH_VRM(sdm845, rf_clk1, rf_clk1_ao, "rfclka1", 1);
218DEFINE_CLK_RPMH_VRM(sdm845, rf_clk2, rf_clk2_ao, "rfclka2", 1);
219DEFINE_CLK_RPMH_VRM(sdm845, rf_clk3, rf_clk3_ao, "rfclka3", 1);
220
221static struct clk_hw *sdm845_rpmh_clocks[] = {
222 [RPMH_CXO_CLK] = &sdm845_bi_tcxo.hw,
223 [RPMH_CXO_CLK_A] = &sdm845_bi_tcxo_ao.hw,
224 [RPMH_LN_BB_CLK2] = &sdm845_ln_bb_clk2.hw,
225 [RPMH_LN_BB_CLK2_A] = &sdm845_ln_bb_clk2_ao.hw,
226 [RPMH_LN_BB_CLK3] = &sdm845_ln_bb_clk3.hw,
227 [RPMH_LN_BB_CLK3_A] = &sdm845_ln_bb_clk3_ao.hw,
228 [RPMH_RF_CLK1] = &sdm845_rf_clk1.hw,
229 [RPMH_RF_CLK1_A] = &sdm845_rf_clk1_ao.hw,
230 [RPMH_RF_CLK2] = &sdm845_rf_clk2.hw,
231 [RPMH_RF_CLK2_A] = &sdm845_rf_clk2_ao.hw,
232 [RPMH_RF_CLK3] = &sdm845_rf_clk3.hw,
233 [RPMH_RF_CLK3_A] = &sdm845_rf_clk3_ao.hw,
234};
235
236static const struct clk_rpmh_desc clk_rpmh_sdm845 = {
237 .clks = sdm845_rpmh_clocks,
238 .num_clks = ARRAY_SIZE(sdm845_rpmh_clocks),
239};
240
241static struct clk_hw *of_clk_rpmh_hw_get(struct of_phandle_args *clkspec,
242 void *data)
243{
244 struct clk_rpmh_desc *rpmh = data;
245 unsigned int idx = clkspec->args[0];
246
247 if (idx >= rpmh->num_clks) {
248 pr_err("%s: invalid index %u\n", __func__, idx);
249 return ERR_PTR(-EINVAL);
250 }
251
252 return rpmh->clks[idx];
253}
254
255static int clk_rpmh_probe(struct platform_device *pdev)
256{
257 struct clk_hw **hw_clks;
258 struct clk_rpmh *rpmh_clk;
259 const struct clk_rpmh_desc *desc;
260 int ret, i;
261
262 desc = of_device_get_match_data(&pdev->dev);
263 if (!desc)
264 return -ENODEV;
265
266 hw_clks = desc->clks;
267
268 for (i = 0; i < desc->num_clks; i++) {
269 u32 res_addr;
270
271 rpmh_clk = to_clk_rpmh(hw_clks[i]);
272 res_addr = cmd_db_read_addr(rpmh_clk->res_name);
273 if (!res_addr) {
274 dev_err(&pdev->dev, "missing RPMh resource address for %s\n",
275 rpmh_clk->res_name);
276 return -ENODEV;
277 }
278 rpmh_clk->res_addr += res_addr;
279 rpmh_clk->dev = &pdev->dev;
280
281 ret = devm_clk_hw_register(&pdev->dev, hw_clks[i]);
282 if (ret) {
283 dev_err(&pdev->dev, "failed to register %s\n",
284 hw_clks[i]->init->name);
285 return ret;
286 }
287 }
288
289 /* typecast to silence compiler warning */
290 ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_rpmh_hw_get,
291 (void *)desc);
292 if (ret) {
293 dev_err(&pdev->dev, "Failed to add clock provider\n");
294 return ret;
295 }
296
297 dev_dbg(&pdev->dev, "Registered RPMh clocks\n");
298
299 return 0;
300}
301
302static const struct of_device_id clk_rpmh_match_table[] = {
303 { .compatible = "qcom,sdm845-rpmh-clk", .data = &clk_rpmh_sdm845},
304 { }
305};
306MODULE_DEVICE_TABLE(of, clk_rpmh_match_table);
307
308static struct platform_driver clk_rpmh_driver = {
309 .probe = clk_rpmh_probe,
310 .driver = {
311 .name = "clk-rpmh",
312 .of_match_table = clk_rpmh_match_table,
313 },
314};
315
316static int __init clk_rpmh_init(void)
317{
318 return platform_driver_register(&clk_rpmh_driver);
319}
320subsys_initcall(clk_rpmh_init);
321
322static void __exit clk_rpmh_exit(void)
323{
324 platform_driver_unregister(&clk_rpmh_driver);
325}
326module_exit(clk_rpmh_exit);
327
328MODULE_DESCRIPTION("QCOM RPMh Clock Driver");
329MODULE_LICENSE("GPL v2");