blob: 2109132dd57e1610fb7ce1d33605e38fdf39d113 [file] [log] [blame]
Osvaldo Banuelos11d36232017-01-23 17:53:27 -08001/*
2 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
Deepak Katragadda9abd7942017-06-13 14:20:09 -070014#define pr_fmt(fmt) "clk: %s: " fmt, __func__
15
Osvaldo Banuelos11d36232017-01-23 17:53:27 -080016#include <linux/kernel.h>
17#include <linux/err.h>
18#include <linux/platform_device.h>
19#include <linux/module.h>
20#include <linux/of.h>
21#include <linux/of_device.h>
22#include <linux/clk.h>
23#include <linux/clk-provider.h>
24#include <linux/regmap.h>
25#include <soc/qcom/cmd-db.h>
26#include <soc/qcom/rpmh.h>
27#include <dt-bindings/clock/qcom,rpmh.h>
28
29#include "common.h"
30#include "clk-regmap.h"
31
32#define CLK_RPMH_ARC_EN_OFFSET 0
33#define CLK_RPMH_VRM_EN_OFFSET 4
34#define CLK_RPMH_VRM_OFF_VAL 0
35#define CLK_RPMH_VRM_ON_VAL 1
36#define CLK_RPMH_APPS_RSC_AO_STATE_MASK (BIT(RPMH_WAKE_ONLY_STATE) | \
37 BIT(RPMH_ACTIVE_ONLY_STATE))
38#define CLK_RPMH_APPS_RSC_STATE_MASK (BIT(RPMH_WAKE_ONLY_STATE) | \
39 BIT(RPMH_ACTIVE_ONLY_STATE) | \
40 BIT(RPMH_SLEEP_STATE))
41
42struct clk_rpmh {
43 const char *res_name;
44 u32 res_addr;
45 u32 res_en_offset;
46 u32 res_on_val;
47 u32 res_off_val;
48 u32 state;
49 u32 aggr_state;
50 u32 last_sent_aggr_state;
51 u32 valid_state_mask;
52 struct rsc_type *rsc;
53 unsigned long rate;
54 struct clk_rpmh *peer;
55 struct clk_hw hw;
56};
57
58struct rsc_type {
59 struct rpmh_client *rpmh_client;
60 const char *mbox_name;
61 const bool use_awake_state;
62};
63
64struct rpmh_cc {
65 struct clk_onecell_data data;
66 struct clk *clks[];
67};
68
69struct clk_rpmh_desc {
70 struct clk_hw **clks;
71 size_t num_clks;
72};
73
74static DEFINE_MUTEX(rpmh_clk_lock);
75
76#define __DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name, \
77 _res_en_offset, _res_on, _res_off, _rsc_id, _rate, \
78 _state_mask, _state_on_mask) \
79 static struct clk_rpmh _platform##_##_name_active; \
80 static struct clk_rpmh _platform##_##_name = { \
81 .res_name = _res_name, \
82 .res_en_offset = _res_en_offset, \
83 .res_on_val = _res_on, \
84 .res_off_val = _res_off, \
85 .rsc = _rsc_id, \
86 .rate = _rate, \
87 .peer = &_platform##_##_name_active, \
88 .valid_state_mask = _state_mask, \
89 .hw.init = &(struct clk_init_data){ \
90 .ops = &clk_rpmh_ops, \
91 .name = #_name, \
Osvaldo Banuelos11d36232017-01-23 17:53:27 -080092 }, \
93 }; \
94 static struct clk_rpmh _platform##_##_name_active = { \
95 .res_name = _res_name, \
96 .res_en_offset = _res_en_offset, \
97 .res_on_val = _res_on, \
98 .res_off_val = _res_off, \
99 .rsc = _rsc_id, \
100 .rate = _rate, \
101 .peer = &_platform##_##_name, \
102 .valid_state_mask = _state_on_mask, \
103 .hw.init = &(struct clk_init_data){ \
104 .ops = &clk_rpmh_ops, \
105 .name = #_name_active, \
Osvaldo Banuelos11d36232017-01-23 17:53:27 -0800106 }, \
107 }
108
109#define DEFINE_CLK_RPMH_ARC(_platform, _name, _name_active, _res_name, \
110 _res_on, _res_off, _rsc_id, _rate, _state_mask, \
111 _state_on_mask) \
112 __DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name, \
113 CLK_RPMH_ARC_EN_OFFSET, _res_on, _res_off, _rsc_id, \
114 _rate, _state_mask, _state_on_mask)
115
116#define DEFINE_CLK_RPMH_VRM(_platform, _name, _name_active, _res_name, \
117 _rsc_id, _rate, _state_mask, _state_on_mask) \
118 __DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name, \
119 CLK_RPMH_VRM_EN_OFFSET, CLK_RPMH_VRM_ON_VAL, \
120 CLK_RPMH_VRM_OFF_VAL, _rsc_id, _rate, _state_mask, \
121 _state_on_mask)
122
123#define DEFINE_RSC_TYPE(name, mbox_id, awake_state) \
124 static struct rsc_type name = { \
125 .rpmh_client = NULL, \
126 .mbox_name = mbox_id, \
127 .use_awake_state = awake_state, \
128 }
129
130static inline struct clk_rpmh *to_clk_rpmh(struct clk_hw *_hw)
131{
132 return container_of(_hw, struct clk_rpmh, hw);
133}
134
135static inline bool has_state_changed(struct clk_rpmh *c, u32 state)
136{
137 return ((c->last_sent_aggr_state & BIT(state))
138 != (c->aggr_state & BIT(state)));
139}
140
141static int clk_rpmh_send_aggregate_command(struct clk_rpmh *c)
142{
143 struct tcs_cmd cmd = { 0 };
144 int ret = 0;
145
146 cmd.addr = c->res_addr + c->res_en_offset;
147
148 if (has_state_changed(c, RPMH_SLEEP_STATE)) {
149 cmd.data = (c->aggr_state >> RPMH_SLEEP_STATE) & 1
150 ? c->res_on_val : c->res_off_val;
151 ret = rpmh_write_async(c->rsc->rpmh_client, RPMH_SLEEP_STATE,
152 &cmd, 1);
153 if (ret) {
154 pr_err("rpmh_write_async(%s, state=%d) failed (%d)\n",
155 c->res_name, RPMH_SLEEP_STATE, ret);
156 return ret;
157 }
158 }
159
160 if (has_state_changed(c, RPMH_WAKE_ONLY_STATE)) {
161 cmd.data = (c->aggr_state >> RPMH_WAKE_ONLY_STATE) & 1
162 ? c->res_on_val : c->res_off_val;
163 ret = rpmh_write_async(c->rsc->rpmh_client,
164 RPMH_WAKE_ONLY_STATE, &cmd, 1);
165 if (ret) {
166 pr_err("rpmh_write_async(%s, state=%d) failed (%d)\n",
167 c->res_name, RPMH_WAKE_ONLY_STATE, ret);
168 return ret;
169 }
170 }
171
172 if (has_state_changed(c, RPMH_ACTIVE_ONLY_STATE)) {
173 cmd.data = (c->aggr_state >> RPMH_ACTIVE_ONLY_STATE) & 1
174 ? c->res_on_val : c->res_off_val;
175 ret = rpmh_write(c->rsc->rpmh_client, RPMH_ACTIVE_ONLY_STATE,
176 &cmd, 1);
177 if (ret) {
178 pr_err("rpmh_write(%s, state=%d) failed (%d)\n",
179 c->res_name, RPMH_ACTIVE_ONLY_STATE, ret);
180 return ret;
181 }
182 }
183
184 if (has_state_changed(c, RPMH_AWAKE_STATE)) {
185 cmd.data = (c->aggr_state >> RPMH_AWAKE_STATE) & 1
186 ? c->res_on_val : c->res_off_val;
187 ret = rpmh_write(c->rsc->rpmh_client, RPMH_AWAKE_STATE,
188 &cmd, 1);
189 if (ret) {
190 pr_err("rpmh_write(%s, state=%d) failed (%d)\n",
191 c->res_name, RPMH_AWAKE_STATE, ret);
192 return ret;
193 }
194 }
195
196 c->last_sent_aggr_state = c->aggr_state;
197 c->peer->last_sent_aggr_state = c->last_sent_aggr_state;
198
199 return 0;
200}
201
202static void clk_rpmh_aggregate_state(struct clk_rpmh *c, bool enable)
203{
204 /* Update state and aggregate state values based on enable value. */
205 c->state = enable ? c->valid_state_mask : 0;
206 c->aggr_state = c->state | c->peer->state;
207 c->peer->aggr_state = c->aggr_state;
208}
209
210static int clk_rpmh_prepare(struct clk_hw *hw)
211{
212 struct clk_rpmh *c = to_clk_rpmh(hw);
213 int ret = 0;
214
215 mutex_lock(&rpmh_clk_lock);
216
217 if (c->state)
218 goto out;
219
220 clk_rpmh_aggregate_state(c, true);
221
222 ret = clk_rpmh_send_aggregate_command(c);
223
224 if (ret)
225 c->state = 0;
226
227out:
228 mutex_unlock(&rpmh_clk_lock);
229 return ret;
230};
231
232static void clk_rpmh_unprepare(struct clk_hw *hw)
233{
234 struct clk_rpmh *c = to_clk_rpmh(hw);
235 int ret = 0;
236
237 mutex_lock(&rpmh_clk_lock);
238
239 if (!c->state)
240 goto out;
241
242 clk_rpmh_aggregate_state(c, false);
243
244 ret = clk_rpmh_send_aggregate_command(c);
245
246 if (ret) {
247 c->state = c->valid_state_mask;
Deepak Katragadda9abd7942017-06-13 14:20:09 -0700248 WARN(1, "clk: %s failed to disable\n", c->res_name);
Osvaldo Banuelos11d36232017-01-23 17:53:27 -0800249 }
250
251out:
252 mutex_unlock(&rpmh_clk_lock);
253 return;
254};
255
256static unsigned long clk_rpmh_recalc_rate(struct clk_hw *hw,
257 unsigned long parent_rate)
258{
259 struct clk_rpmh *r = to_clk_rpmh(hw);
260
261 /*
262 * RPMh clocks have a fixed rate. Return static rate set
263 * at init time.
264 */
265 return r->rate;
266}
267
268static const struct clk_ops clk_rpmh_ops = {
269 .prepare = clk_rpmh_prepare,
270 .unprepare = clk_rpmh_unprepare,
271 .recalc_rate = clk_rpmh_recalc_rate,
272};
273
274/* Use awake state instead of active-only on RSCs that do not have an AMC. */
275DEFINE_RSC_TYPE(apps_rsc, "apps", false);
276DEFINE_RSC_TYPE(disp_rsc, "display", true);
277
278/* Resource name must match resource id present in cmd-db. */
279DEFINE_CLK_RPMH_ARC(sdm845, bi_tcxo, bi_tcxo_ao, "xo.lvl", 0x3, 0x0,
280 &apps_rsc, 19200000, CLK_RPMH_APPS_RSC_STATE_MASK,
281 CLK_RPMH_APPS_RSC_AO_STATE_MASK);
Osvaldo Banuelos11d36232017-01-23 17:53:27 -0800282DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk2, ln_bb_clk2_ao, "lnbclka2", &apps_rsc,
283 19200000, CLK_RPMH_APPS_RSC_STATE_MASK,
284 CLK_RPMH_APPS_RSC_AO_STATE_MASK);
285DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk3, ln_bb_clk3_ao, "lnbclka3", &apps_rsc,
286 19200000, CLK_RPMH_APPS_RSC_STATE_MASK,
287 CLK_RPMH_APPS_RSC_AO_STATE_MASK);
288DEFINE_CLK_RPMH_VRM(sdm845, rf_clk1, rf_clk1_ao, "rfclka1", &apps_rsc,
289 38400000, CLK_RPMH_APPS_RSC_STATE_MASK,
290 CLK_RPMH_APPS_RSC_AO_STATE_MASK);
291DEFINE_CLK_RPMH_VRM(sdm845, rf_clk2, rf_clk2_ao, "rfclka2", &apps_rsc,
292 38400000, CLK_RPMH_APPS_RSC_STATE_MASK,
293 CLK_RPMH_APPS_RSC_AO_STATE_MASK);
294DEFINE_CLK_RPMH_VRM(sdm845, rf_clk3, rf_clk3_ao, "rfclka3", &apps_rsc,
295 38400000, CLK_RPMH_APPS_RSC_STATE_MASK,
296 CLK_RPMH_APPS_RSC_AO_STATE_MASK);
297
298static struct clk_hw *sdm845_rpmh_clocks[] = {
299 [RPMH_CXO_CLK] = &sdm845_bi_tcxo.hw,
300 [RPMH_CXO_CLK_A] = &sdm845_bi_tcxo_ao.hw,
Osvaldo Banuelos11d36232017-01-23 17:53:27 -0800301 [RPMH_LN_BB_CLK2] = &sdm845_ln_bb_clk2.hw,
302 [RPMH_LN_BB_CLK2_A] = &sdm845_ln_bb_clk2_ao.hw,
303 [RPMH_LN_BB_CLK3] = &sdm845_ln_bb_clk3.hw,
304 [RPMH_LN_BB_CLK3_A] = &sdm845_ln_bb_clk3_ao.hw,
305 [RPMH_RF_CLK1] = &sdm845_rf_clk1.hw,
306 [RPMH_RF_CLK1_A] = &sdm845_rf_clk1_ao.hw,
307 [RPMH_RF_CLK2] = &sdm845_rf_clk2.hw,
308 [RPMH_RF_CLK2_A] = &sdm845_rf_clk2_ao.hw,
309 [RPMH_RF_CLK3] = &sdm845_rf_clk3.hw,
310 [RPMH_RF_CLK3_A] = &sdm845_rf_clk3_ao.hw,
311};
312
313static const struct clk_rpmh_desc clk_rpmh_sdm845 = {
314 .clks = sdm845_rpmh_clocks,
315 .num_clks = ARRAY_SIZE(sdm845_rpmh_clocks),
316};
317
318static const struct of_device_id clk_rpmh_match_table[] = {
319 { .compatible = "qcom,rpmh-clk-sdm845", .data = &clk_rpmh_sdm845},
Deepak Katragadda443bd8d2017-08-28 22:30:19 +0530320 { .compatible = "qcom,rpmh-clk-sdm670", .data = &clk_rpmh_sdm845},
Osvaldo Banuelos11d36232017-01-23 17:53:27 -0800321 { }
322};
323MODULE_DEVICE_TABLE(of, clk_rpmh_match_table);
324
Deepak Katragadda443bd8d2017-08-28 22:30:19 +0530325static void clk_rpmh_sdm670_fixup_sdm670(void)
326{
327 sdm845_rpmh_clocks[RPMH_RF_CLK3] = NULL;
328 sdm845_rpmh_clocks[RPMH_RF_CLK3_A] = NULL;
329}
330
331static int clk_rpmh_sdm670_fixup(struct platform_device *pdev)
332{
333 const char *compat = NULL;
334 int compatlen = 0;
335
336 compat = of_get_property(pdev->dev.of_node, "compatible", &compatlen);
337 if (!compat || (compatlen <= 0))
338 return -EINVAL;
339
340 if (!strcmp(compat, "qcom,rpmh-clk-sdm670"))
341 clk_rpmh_sdm670_fixup_sdm670();
342
343 return 0;
344}
345
Osvaldo Banuelos11d36232017-01-23 17:53:27 -0800346static int clk_rpmh_probe(struct platform_device *pdev)
347{
348 struct clk **clks;
349 struct clk *clk;
350 struct rpmh_cc *rcc;
351 struct clk_onecell_data *data;
352 int ret;
353 size_t num_clks, i;
354 struct clk_hw **hw_clks;
355 struct clk_rpmh *rpmh_clk;
356 const struct clk_rpmh_desc *desc;
357 struct property *prop;
358 const char *mbox_name;
359
360 desc = of_device_get_match_data(&pdev->dev);
361 if (!desc) {
362 ret = -EINVAL;
363 goto err;
364 }
365
366 ret = cmd_db_ready();
367 if (ret) {
368 if (ret != -EPROBE_DEFER) {
369 dev_err(&pdev->dev, "Command DB not available (%d)\n",
370 ret);
371 goto err;
372 }
373 return ret;
374 }
375
376 of_property_for_each_string(pdev->dev.of_node, "mbox-names", prop,
377 mbox_name) {
378 if (!strcmp(apps_rsc.mbox_name, mbox_name)) {
379 apps_rsc.rpmh_client = rpmh_get_byname(pdev, mbox_name);
380 if (IS_ERR(apps_rsc.rpmh_client)) {
381 ret = PTR_ERR(apps_rsc.rpmh_client);
382 if (ret != -EPROBE_DEFER) {
383 dev_err(&pdev->dev,
384 "failed to request RPMh client for %s (%d)\n",
385 mbox_name, ret);
386 goto err;
387 }
388 return ret;
389 }
390 }
391
392 if (!strcmp(disp_rsc.mbox_name, mbox_name)) {
393 disp_rsc.rpmh_client = rpmh_get_byname(pdev, mbox_name);
394 if (IS_ERR(disp_rsc.rpmh_client)) {
395 ret = PTR_ERR(disp_rsc.rpmh_client);
396 if (ret != -EPROBE_DEFER) {
397 dev_err(&pdev->dev,
398 "failed to request RPMh client for %s (%d)\n",
399 mbox_name, ret);
400 goto err2;
401 }
402 return ret;
403 }
404 }
405 }
406
407 if (!apps_rsc.rpmh_client) {
408 dev_err(&pdev->dev, "%s mbox is missing\n", apps_rsc.mbox_name);
409 ret = -EINVAL;
410 goto err2;
411 }
412
Deepak Katragadda443bd8d2017-08-28 22:30:19 +0530413 ret = clk_rpmh_sdm670_fixup(pdev);
414 if (ret)
415 return ret;
416
Osvaldo Banuelos11d36232017-01-23 17:53:27 -0800417 hw_clks = desc->clks;
418 num_clks = desc->num_clks;
419
420 rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*clks) * num_clks,
421 GFP_KERNEL);
422 if (!rcc) {
423 ret = -ENOMEM;
424 goto err2;
425 }
426
427 clks = rcc->clks;
428 data = &rcc->data;
429 data->clks = clks;
430 data->clk_num = num_clks;
431
432 for (i = 0; i < num_clks; i++) {
Deepak Katragadda443bd8d2017-08-28 22:30:19 +0530433 if (!hw_clks[i]) {
434 clks[i] = ERR_PTR(-ENOENT);
435 continue;
436 }
437
Osvaldo Banuelos11d36232017-01-23 17:53:27 -0800438 rpmh_clk = to_clk_rpmh(hw_clks[i]);
439 rpmh_clk->res_addr = cmd_db_get_addr(rpmh_clk->res_name);
440 if (!rpmh_clk->res_addr) {
441 dev_err(&pdev->dev, "missing RPMh resource address for %s\n",
442 rpmh_clk->res_name);
443 ret = -ENODEV;
444 goto err2;
445 }
446
447 clk = devm_clk_register(&pdev->dev, hw_clks[i]);
448 if (IS_ERR(clk)) {
449 ret = PTR_ERR(clk);
450 goto err2;
451 }
452
453 clks[i] = clk;
454 }
455
456 ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
457 data);
458 if (ret)
459 goto err2;
460
461 dev_info(&pdev->dev, "Registered RPMh clocks\n");
462 return ret;
463
464err2:
465 rpmh_release(apps_rsc.rpmh_client);
466 if (disp_rsc.rpmh_client)
467 rpmh_release(disp_rsc.rpmh_client);
468err:
469 dev_err(&pdev->dev, "Error registering RPMh Clock driver (%d)\n", ret);
470 return ret;
471}
472
473static struct platform_driver clk_rpmh_driver = {
474 .probe = clk_rpmh_probe,
475 .driver = {
476 .name = "clk-rpmh",
477 .of_match_table = clk_rpmh_match_table,
478 },
479};
480
481static int __init clk_rpmh_init(void)
482{
483 return platform_driver_register(&clk_rpmh_driver);
484}
Deepak Katragaddaef44e102017-06-21 10:30:46 -0700485subsys_initcall(clk_rpmh_init);
Osvaldo Banuelos11d36232017-01-23 17:53:27 -0800486
487static void __exit clk_rpmh_exit(void)
488{
489 platform_driver_unregister(&clk_rpmh_driver);
490}
491module_exit(clk_rpmh_exit);
492
493MODULE_DESCRIPTION("QTI RPMh Clock Driver");
494MODULE_LICENSE("GPL v2");
495MODULE_ALIAS("platform:clk-rpmh");