blob: 1c7e688db64e09a91dcf3f69a58c32d871e8f4a6 [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2
3/*
4
5 * Copyright (c) 2020 MediaTek Inc.
6
7 */
8#define pr_fmt(fmt) "[clkdbg] " fmt
9#include <linux/of.h>
10#include <linux/of_address.h>
11#include <linux/of_platform.h>
12#include <linux/slab.h>
13#include <linux/delay.h>
14#include <linux/proc_fs.h>
15#include <linux/fs.h>
16#include <linux/seq_file.h>
17#include <linux/uaccess.h>
18#include <linux/clk.h>
19#include <linux/clk-provider.h>
20#include <linux/pm_domain.h>
21#include <linux/pm_runtime.h>
22#include <linux/module.h>
23#include <linux/version.h>
24#include "clkdbg.h"
25#if defined(CONFIG_PM_DEBUG)
26#define CLKDBG_PM_DOMAIN 1
27#else
28#define CLKDBG_PM_DOMAIN 1 /*WTF*/
29#endif
30#define CLKDBG_PM_DOMAIN_API_4_9 1
31#define CLKDBG_CCF_API_4_4 1
32#define CLKDBG_HACK_CLK 0
33#define CLKDBG_HACK_CLK_CORE 1
34#if !CLKDBG_CCF_API_4_4
35/* backward compatible */
36static const char *clk_hw_get_name(const struct clk_hw *hw)
37{
38 return __clk_get_name(hw->clk);
39}
40static bool clk_hw_is_prepared(const struct clk_hw *hw)
41{
42 return __clk_is_prepared(hw->clk);
43}
44static bool clk_hw_is_enabled(const struct clk_hw *hw)
45{
46 return __clk_is_enabled(hw->clk);
47}
48static unsigned long clk_hw_get_rate(const struct clk_hw *hw)
49{
50 return __clk_get_rate(hw->clk);
51}
52static unsigned int clk_hw_get_num_parents(const struct clk_hw *hw)
53{
54 return __clk_get_num_parents(hw->clk);
55}
56static struct clk_hw *clk_hw_get_parent_by_index(const struct clk_hw *hw,
57 unsigned int index)
58{
59 return __clk_get_hw(clk_get_parent_by_index(hw->clk, index));
60}
61#endif /* !CLKDBG_CCF_API_4_4 */
62#if CLKDBG_HACK_CLK
63#include <linux/clk-private.h>
64static bool clk_hw_is_on(struct clk_hw *hw)
65{
66 const struct clk_ops *ops = hw->clk->ops;
67 if (ops->is_enabled)
68 return clk_hw_is_enabled(hw);
69 else if (ops->is_prepared)
70 return clk_hw_is_prepared(hw);
71 return clk_hw_is_enabled(hw) || clk_hw_is_prepared(hw);
72}
73#elif CLKDBG_HACK_CLK_CORE
74struct clk_core {
75 const char *name;
76 const struct clk_ops *ops;
77 struct clk_hw *hw;
78};
79static bool clk_hw_is_on(struct clk_hw *hw)
80{
81 const struct clk_ops *ops = hw->core->ops;
82 if (ops->is_enabled)
83 return clk_hw_is_enabled(hw);
84 else if (ops->is_prepared)
85 return clk_hw_is_prepared(hw);
86 return clk_hw_is_enabled(hw) || clk_hw_is_prepared(hw);
87}
88#else
89static bool clk_hw_is_on(struct clk_hw *hw)
90{
91 return __clk_get_enable_count(hw->clk) || clk_hw_is_prepared(hw);
92}
93#endif /* !CLKDBG_HACK_CLK && !CLKDBG_HACK_CLK_CORE */
94static const struct clkdbg_ops *clkdbg_ops;
95void set_clkdbg_ops(const struct clkdbg_ops *ops)
96{
97 clkdbg_ops = ops;
98}
99static const struct fmeter_clk *get_all_fmeter_clks(void)
100{
101 if (clkdbg_ops == NULL || clkdbg_ops->get_all_fmeter_clks == NULL)
102 return NULL;
103 return clkdbg_ops->get_all_fmeter_clks();
104}
105static void *prepare_fmeter(void)
106{
107 if (clkdbg_ops == NULL || clkdbg_ops->prepare_fmeter == NULL)
108 return NULL;
109 return clkdbg_ops->prepare_fmeter();
110}
111static void unprepare_fmeter(void *data)
112{
113 if (clkdbg_ops == NULL || clkdbg_ops->unprepare_fmeter == NULL)
114 return;
115 clkdbg_ops->unprepare_fmeter(data);
116}
117static u32 fmeter_freq(const struct fmeter_clk *fclk)
118{
119 if (clkdbg_ops == NULL || clkdbg_ops->fmeter_freq == NULL)
120 return 0;
121 return clkdbg_ops->fmeter_freq(fclk);
122}
123static const struct regname *get_all_regnames(void)
124{
125 if (clkdbg_ops == NULL || clkdbg_ops->get_all_regnames == NULL)
126 return NULL;
127 return clkdbg_ops->get_all_regnames();
128}
129static const char * const *get_all_clk_names(void)
130{
131 if (clkdbg_ops == NULL || clkdbg_ops->get_all_clk_names == NULL)
132 return NULL;
133 return clkdbg_ops->get_all_clk_names();
134}
135static const char * const *get_pwr_names(void)
136{
137 static const char * const default_pwr_names[] = {
138 [0] = "(MD)",
139 [1] = "(CONN)",
140 [2] = "(DDRPHY)",
141 [3] = "(DISP)",
142 [4] = "(MFG)",
143 [5] = "(ISP)",
144 [6] = "(INFRA)",
145 [7] = "(VDEC)",
146 [8] = "(CPU, CA7_CPUTOP)",
147 [9] = "(FC3, CA7_CPU0, CPUTOP)",
148 [10] = "(FC2, CA7_CPU1, CPU3)",
149 [11] = "(FC1, CA7_CPU2, CPU2)",
150 [12] = "(FC0, CA7_CPU3, CPU1)",
151 [13] = "(MCUSYS, CA7_DBG, CPU0)",
152 [14] = "(MCUSYS, VEN, BDP)",
153 [15] = "(CA15_CPUTOP, ETH, MCUSYS)",
154 [16] = "(CA15_CPU0, HIF)",
155 [17] = "(CA15_CPU1, CA15-CX0, INFRA_MISC)",
156 [18] = "(CA15_CPU2, CA15-CX1)",
157 [19] = "(CA15_CPU3, CA15-CPU0)",
158 [20] = "(VEN2, MJC, CA15-CPU1)",
159 [21] = "(VEN, CA15-CPUTOP)",
160 [22] = "(MFG_2D)",
161 [23] = "(MFG_ASYNC, DBG)",
162 [24] = "(AUDIO, MFG_2D)",
163 [25] = "(USB, VCORE_PDN, MFG_ASYNC)",
164 [26] = "(ARMPLL_DIV, CPUTOP_SRM_SLPB)",
165 [27] = "(MD2, CPUTOP_SRM_PDN)",
166 [28] = "(CPU3_SRM_PDN)",
167 [29] = "(CPU2_SRM_PDN)",
168 [30] = "(CPU1_SRM_PDN)",
169 [31] = "(CPU0_SRM_PDN)",
170 };
171 if (clkdbg_ops == NULL || clkdbg_ops->get_pwr_names == NULL)
172 return default_pwr_names;
173 return clkdbg_ops->get_pwr_names();
174}
175static void setup_provider_clk(struct provider_clk *pvdck)
176{
177 if (clkdbg_ops == NULL || clkdbg_ops->setup_provider_clk == NULL)
178 return;
179 clkdbg_ops->setup_provider_clk(pvdck);
180}
181static bool is_valid_reg(void __iomem *addr)
182{
183#ifdef CONFIG_64BIT
184 return ((u64)addr & 0xf0000000) != 0UL ||
185 (((u64)addr >> 32U) & 0xf0000000) != 0UL;
186#else
187 return ((u32)addr & 0xf0000000) != 0U;
188#endif
189}
190enum clkdbg_opt {
191 CLKDBG_EN_SUSPEND_SAVE_1,
192 CLKDBG_EN_SUSPEND_SAVE_2,
193 CLKDBG_EN_SUSPEND_SAVE_3,
194 CLKDBG_EN_LOG_SAVE_POINTS,
195};
196static u32 clkdbg_flags;
197static void set_clkdbg_flag(enum clkdbg_opt opt)
198{
199 clkdbg_flags |= BIT(opt);
200}
201static void clr_clkdbg_flag(enum clkdbg_opt opt)
202{
203 clkdbg_flags &= ~BIT(opt);
204}
205static bool has_clkdbg_flag(enum clkdbg_opt opt)
206{
207 return (clkdbg_flags & BIT(opt)) != 0U;
208}
209typedef void (*fn_fclk_freq_proc)(const struct fmeter_clk *fclk,
210 u32 freq, void *data);
211static void proc_all_fclk_freq(fn_fclk_freq_proc proc, void *data)
212{
213 void *fmeter_data;
214 const struct fmeter_clk *fclk;
215 fclk = get_all_fmeter_clks();
216 if (fclk == NULL || proc == NULL)
217 return;
218 fmeter_data = prepare_fmeter();
219 for (; fclk->type != FT_NULL; fclk++) {
220 u32 freq;
221 freq = fmeter_freq(fclk);
222 proc(fclk, freq, data);
223 }
224 unprepare_fmeter(fmeter_data);
225}
226static void print_fclk_freq(const struct fmeter_clk *fclk, u32 freq, void *data)
227{
228 pr_info("%2d: %-29s: %u\n", fclk->id, fclk->name, freq);
229}
230void print_fmeter_all(void)
231{
232 proc_all_fclk_freq(print_fclk_freq, NULL);
233}
234static void seq_print_fclk_freq(const struct fmeter_clk *fclk,
235 u32 freq, void *data)
236{
237 struct seq_file *s = data;
238 seq_printf(s, "%2d: %-29s: %u\n", fclk->id, fclk->name, freq);
239}
240static int seq_print_fmeter_all(struct seq_file *s, void *v)
241{
242 proc_all_fclk_freq(seq_print_fclk_freq, s);
243 return 0;
244}
245typedef void (*fn_regname_proc)(const struct regname *rn, void *data);
246static void proc_all_regname(fn_regname_proc proc, void *data)
247{
248 const struct regname *rn = get_all_regnames();
249 if (rn == NULL)
250 return;
251 for (; rn->base != NULL; rn++)
252 proc(rn, data);
253}
254static void print_reg(const struct regname *rn, void *data)
255{
256 if (!is_valid_reg(ADDR(rn)))
257 return;
258 pr_info("%-21s: [0x%08x][0x%p] = 0x%08x\n",
259 rn->name, PHYSADDR(rn), ADDR(rn), clk_readl(ADDR(rn)));
260}
261void print_regs(void)
262{
263 proc_all_regname(print_reg, NULL);
264}
265static void seq_print_reg(const struct regname *rn, void *data)
266{
267 struct seq_file *s = data;
268 const char *pg = rn->base->pg;
269 struct clk *clk;
270 struct clk_hw *c_hw;
271 bool is_pwr_on = true;
272 if (!is_valid_reg(ADDR(rn)))
273 return;
274 if (pg) {
275 clk = __clk_lookup(pg);
276 if (!clk)
277 return;
278 c_hw = __clk_get_hw(clk);
279 if (c_hw)
280 is_pwr_on = clk_hw_is_prepared(c_hw);
281 }
282 if (is_pwr_on)
283 seq_printf(s, "%-21s: [0x%08x][0x%p] = 0x%08x\n",
284 rn->name, PHYSADDR(rn), ADDR(rn), clk_readl(ADDR(rn)));
285 else
286 seq_printf(s, "%-21s: [0x%08x][0x%p] cannot read, pwr_off\n",
287 rn->name, PHYSADDR(rn), ADDR(rn));
288}
289static int seq_print_regs(struct seq_file *s, void *v)
290{
291 proc_all_regname(seq_print_reg, s);
292 return 0;
293}
294static void print_reg2(const struct regname *rn, void *data)
295{
296 if (!is_valid_reg(ADDR(rn)))
297 return;
298 pr_info("%-21s: [0x%08x][0x%p] = 0x%08x\n",
299 rn->name, PHYSADDR(rn), ADDR(rn), clk_readl(ADDR(rn)));
300 msleep(20);
301}
302static int clkdbg_dump_regs2(struct seq_file *s, void *v)
303{
304 proc_all_regname(print_reg2, s);
305 return 0;
306}
307static u32 read_spm_pwr_status(void)
308{
309 static void __iomem *scpsys_base, *pwr_sta, *pwr_sta_2nd;
310 if (clkdbg_ops == NULL || clkdbg_ops->get_spm_pwr_status == NULL) {
311 if (scpsys_base == NULL ||
312 pwr_sta == NULL || pwr_sta_2nd == NULL) {
313 scpsys_base = ioremap(0x10006000, PAGE_SIZE);
314 pwr_sta = scpsys_base + 0x16c;
315 pwr_sta_2nd = scpsys_base + 0x170;
316 }
317 return clk_readl(pwr_sta) & clk_readl(pwr_sta_2nd);
318 } else
319 return clkdbg_ops->get_spm_pwr_status();
320}
321static bool clk_hw_pwr_is_on(struct clk_hw *c_hw,
322 u32 spm_pwr_status, u32 pwr_mask)
323{
324 if ((spm_pwr_status & pwr_mask) != pwr_mask)
325 return false;
326 return clk_hw_is_on(c_hw);
327}
328static bool pvdck_pwr_is_on(struct provider_clk *pvdck, u32 spm_pwr_status)
329{
330 struct clk *c = pvdck->ck;
331 struct clk_hw *c_hw = __clk_get_hw(c);
332 return clk_hw_pwr_is_on(c_hw, spm_pwr_status, pvdck->pwr_mask);
333}
334static bool pvdck_is_on(struct provider_clk *pvdck)
335{
336 u32 val = 0;
337 if (clkdbg_ops == NULL || clkdbg_ops->is_pwr_on == NULL) {
338 if (pvdck->pwr_mask != 0U)
339 val = read_spm_pwr_status();
340 return pvdck_pwr_is_on(pvdck, val);
341 }
342 val = clkdbg_ops->is_pwr_on(pvdck);
343 return val;
344}
345static const char *ccf_state(struct clk_hw *hw)
346{
347 if (__clk_get_enable_count(hw->clk))
348 return "enabled";
349 if (clk_hw_is_prepared(hw))
350 return "prepared";
351 return "disabled";
352}
353static void dump_clk_state(const char *clkname, struct seq_file *s)
354{
355 struct clk *c = __clk_lookup(clkname);
356 struct clk *p = IS_ERR_OR_NULL(c) ? NULL : clk_get_parent(c);
357 struct clk_hw *c_hw = __clk_get_hw(c);
358 struct clk_hw *p_hw = __clk_get_hw(p);
359 if (IS_ERR_OR_NULL(c)) {
360 seq_printf(s, "[%17s: NULL]\n", clkname);
361 return;
362 }
363 seq_printf(s, "[%-17s: %8s, %3d, %3d, %10ld, %17s]\n",
364 clk_hw_get_name(c_hw),
365 ccf_state(c_hw),
366 clk_hw_is_prepared(c_hw),
367 __clk_get_enable_count(c),
368 clk_hw_get_rate(c_hw),
369 p != NULL ? clk_hw_get_name(p_hw) : "- ");
370}
371static int clkdbg_dump_state_all(struct seq_file *s, void *v)
372{
373 const char * const *ckn = get_all_clk_names();
374 if (ckn == NULL)
375 return 0;
376 for (; *ckn != NULL; ckn++)
377 dump_clk_state(*ckn, s);
378 return 0;
379}
380static const char *get_provider_name(struct device_node *node, u32 *cells)
381{
382 const char *name;
383 const char *p;
384 u32 cc = 1;
385 if (of_property_read_u32(node, "#clock-cells", &cc) != 0)
386 cc = 0;
387 if (cells != NULL)
388 *cells = cc;
389 if (cc == 0U) {
390 if (of_property_read_string(node,
391 "clock-output-names", &name) < 0)
392 name = node->name;
393 return name;
394 }
395 if (of_property_read_string(node, "compatible", &name) < 0)
396 name = node->name;
397 p = strchr(name, (int)'-');
398 if (p != NULL)
399 return p + 1;
400 else
401 return name;
402}
403struct provider_clk *get_all_provider_clks(void)
404{
405 static struct provider_clk provider_clks[1024];
406 struct device_node *node = NULL;
407 int n = 0;
408 if (provider_clks[0].ck != NULL)
409 return provider_clks;
410 do {
411 const char *node_name;
412 u32 cells;
413 node = of_find_node_with_property(node, "#clock-cells");
414 if (node == NULL)
415 break;
416 node_name = get_provider_name(node, &cells);
417 if (cells == 0U) {
418 struct clk *ck = __clk_lookup(node_name);
419 if (IS_ERR_OR_NULL(ck))
420 continue;
421 provider_clks[n].ck = ck;
422 setup_provider_clk(&provider_clks[n]);
423 ++n;
424 } else {
425 unsigned int i;
426 for (i = 0; i < 256; i++) {
427 struct of_phandle_args pa;
428 struct clk *ck;
429 pa.np = node;
430 pa.args[0] = i;
431 pa.args_count = 1;
432 ck = of_clk_get_from_provider(&pa);
433 if (PTR_ERR(ck) == -EINVAL)
434 break;
435 else if (IS_ERR_OR_NULL(ck))
436 continue;
437 provider_clks[n].ck = ck;
438 provider_clks[n].idx = i;
439 provider_clks[n].provider_name = node_name;
440 setup_provider_clk(&provider_clks[n]);
441 ++n;
442 }
443 }
444 } while (node != NULL);
445 return provider_clks;
446}
447static void dump_provider_clk(struct provider_clk *pvdck, struct seq_file *s)
448{
449 struct clk *c = pvdck->ck;
450 struct clk *p = IS_ERR_OR_NULL(c) ? NULL : clk_get_parent(c);
451 struct clk_hw *c_hw = __clk_get_hw(c);
452 struct clk_hw *p_hw = __clk_get_hw(p);
453 seq_printf(s, "[%10s: %-17s: %3s, %3d, %3d, %10ld, %17s]\n",
454 pvdck->provider_name != NULL ? pvdck->provider_name : "/ ",
455 clk_hw_get_name(c_hw),
456 pvdck_is_on(pvdck) ? "ON" : "off",
457 clk_hw_is_prepared(c_hw),
458 __clk_get_enable_count(c),
459 clk_hw_get_rate(c_hw),
460 p != NULL ? clk_hw_get_name(p_hw) : "- ");
461}
462static int clkdbg_dump_provider_clks(struct seq_file *s, void *v)
463{
464 struct provider_clk *pvdck = get_all_provider_clks();
465 for (; pvdck->ck != NULL; pvdck++)
466 dump_provider_clk(pvdck, s);
467 return 0;
468}
469static void dump_provider_mux(struct provider_clk *pvdck, struct seq_file *s)
470{
471 unsigned int i;
472 struct clk *c = pvdck->ck;
473 struct clk_hw *c_hw = __clk_get_hw(c);
474 unsigned int np = clk_hw_get_num_parents(c_hw);
475 if (np <= 1U)
476 return;
477 dump_provider_clk(pvdck, s);
478 for (i = 0; i < np; i++) {
479 struct clk_hw *p_hw = clk_hw_get_parent_by_index(c_hw, i);
480 if (IS_ERR_OR_NULL(p_hw))
481 continue;
482 seq_printf(s, "\t\t\t(%2d: %-17s: %8s, %10ld)\n",
483 i,
484 clk_hw_get_name(p_hw),
485 ccf_state(p_hw),
486 clk_hw_get_rate(p_hw));
487 }
488}
489static int clkdbg_dump_muxes(struct seq_file *s, void *v)
490{
491 struct provider_clk *pvdck = get_all_provider_clks();
492 for (; pvdck->ck != NULL; pvdck++)
493 dump_provider_mux(pvdck, s);
494 return 0;
495}
496static void show_pwr_status(u32 spm_pwr_status)
497{
498 unsigned int i;
499 const char * const *pwr_name = get_pwr_names();
500 pr_info("SPM_PWR_STATUS: 0x%08x\n\n", spm_pwr_status);
501 for (i = 0; i < 32; i++) {
502 const char *st = (spm_pwr_status & BIT(i)) != 0U ? "ON" : "off";
503 pr_info("[%2d]: %3s: %s\n", i, st, pwr_name[i]);
504 mdelay(20);
505 }
506}
507static int dump_pwr_status(u32 spm_pwr_status, struct seq_file *s)
508{
509 unsigned int i;
510 const char * const *pwr_name = get_pwr_names();
511 seq_printf(s, "SPM_PWR_STATUS: 0x%08x\n\n", spm_pwr_status);
512 for (i = 0; i < 32; i++) {
513 const char *st = (spm_pwr_status & BIT(i)) != 0U ? "ON" : "off";
514 seq_printf(s, "[%2d]: %3s: %s\n", i, st, pwr_name[i]);
515 }
516 return 0;
517}
518static int clkdbg_pwr_status(struct seq_file *s, void *v)
519{
520 return dump_pwr_status(read_spm_pwr_status(), s);
521}
522static char last_cmd[128] = "null";
523const char *get_last_cmd(void)
524{
525 return last_cmd;
526}
527static int clkop_int_ckname(int (*clkop)(struct clk *clk),
528 const char *clkop_name, const char *clk_name,
529 struct clk *ck, struct seq_file *s)
530{
531 struct clk *clk;
532 if (!IS_ERR_OR_NULL(ck)) {
533 clk = ck;
534 } else {
535 clk = __clk_lookup(clk_name);
536 if (IS_ERR_OR_NULL(clk)) {
537 seq_printf(s, "clk_lookup(%s): 0x%p\n", clk_name, clk);
538 return PTR_ERR(clk);
539 }
540 }
541 return clkop(clk);
542}
543static int clkdbg_clkop_int_ckname(int (*clkop)(struct clk *clk),
544 const char *clkop_name, struct seq_file *s, void *v)
545{
546 char cmd[sizeof(last_cmd)];
547 char *c = cmd;
548 char *ign;
549 char *clk_name;
550 int r = 0;
551 strncpy(cmd, last_cmd, sizeof(cmd));
552 cmd[sizeof(cmd) - 1UL] = '\0';
553 ign = strsep(&c, " ");
554 clk_name = strsep(&c, " ");
555 if (clk_name == NULL)
556 return 0;
557 if (strcmp(clk_name, "all") == 0) {
558 struct provider_clk *pvdck = get_all_provider_clks();
559 for (; pvdck->ck != NULL; pvdck++) {
560 r |= clkop_int_ckname(clkop, clkop_name, NULL,
561 pvdck->ck, s);
562 }
563 seq_printf(s, "%s(%s): %d\n", clkop_name, clk_name, r);
564 return r;
565 }
566 r = clkop_int_ckname(clkop, clkop_name, clk_name, NULL, s);
567 seq_printf(s, "%s(%s): %d\n", clkop_name, clk_name, r);
568 return r;
569}
570static void clkop_void_ckname(void (*clkop)(struct clk *clk),
571 const char *clkop_name, const char *clk_name,
572 struct clk *ck, struct seq_file *s)
573{
574 struct clk *clk;
575 if (!IS_ERR_OR_NULL(ck)) {
576 clk = ck;
577 } else {
578 clk = __clk_lookup(clk_name);
579 if (IS_ERR_OR_NULL(clk)) {
580 seq_printf(s, "clk_lookup(%s): 0x%p\n", clk_name, clk);
581 return;
582 }
583 }
584 clkop(clk);
585}
586static int clkdbg_clkop_void_ckname(void (*clkop)(struct clk *clk),
587 const char *clkop_name, struct seq_file *s, void *v)
588{
589 char cmd[sizeof(last_cmd)];
590 char *c = cmd;
591 char *ign;
592 char *clk_name;
593 strncpy(cmd, last_cmd, sizeof(cmd));
594 cmd[sizeof(cmd) - 1UL] = '\0';
595 ign = strsep(&c, " ");
596 clk_name = strsep(&c, " ");
597 if (clk_name == NULL)
598 return 0;
599 if (strcmp(clk_name, "all") == 0) {
600 struct provider_clk *pvdck = get_all_provider_clks();
601 for (; pvdck->ck != NULL; pvdck++) {
602 clkop_void_ckname(clkop, clkop_name, NULL,
603 pvdck->ck, s);
604 }
605 seq_printf(s, "%s(%s)\n", clkop_name, clk_name);
606 return 0;
607 }
608 clkop_void_ckname(clkop, clkop_name, clk_name, NULL, s);
609 seq_printf(s, "%s(%s)\n", clkop_name, clk_name);
610 return 0;
611}
612static int clkdbg_prepare(struct seq_file *s, void *v)
613{
614 return clkdbg_clkop_int_ckname(clk_prepare,
615 "clk_prepare", s, v);
616}
617static int clkdbg_unprepare(struct seq_file *s, void *v)
618{
619 return clkdbg_clkop_void_ckname(clk_unprepare,
620 "clk_unprepare", s, v);
621}
622static int clkdbg_enable(struct seq_file *s, void *v)
623{
624 return clkdbg_clkop_int_ckname(clk_enable,
625 "clk_enable", s, v);
626}
627static int clkdbg_disable(struct seq_file *s, void *v)
628{
629 return clkdbg_clkop_void_ckname(clk_disable,
630 "clk_disable", s, v);
631}
632static int clkdbg_prepare_enable(struct seq_file *s, void *v)
633{
634 return clkdbg_clkop_int_ckname(clk_prepare_enable,
635 "clk_prepare_enable", s, v);
636}
637static int clkdbg_disable_unprepare(struct seq_file *s, void *v)
638{
639 return clkdbg_clkop_void_ckname(clk_disable_unprepare,
640 "clk_disable_unprepare", s, v);
641}
642void prepare_enable_provider(const char *pvd)
643{
644 bool allpvd = (pvd == NULL || strcmp(pvd, "all") == 0);
645 struct provider_clk *pvdck = get_all_provider_clks();
646 for (; pvdck->ck != NULL; pvdck++) {
647 if (allpvd || (pvdck->provider_name != NULL &&
648 strcmp(pvd, pvdck->provider_name) == 0)) {
649 int r = clk_prepare_enable(pvdck->ck);
650 if (r != 0)
651 pr_info("clk_prepare_enable(): %d\n", r);
652 }
653 }
654}
655void disable_unprepare_provider(const char *pvd)
656{
657 bool allpvd = (pvd == NULL || strcmp(pvd, "all") == 0);
658 struct provider_clk *pvdck = get_all_provider_clks();
659 for (; pvdck->ck != NULL; pvdck++) {
660 if (allpvd || (pvdck->provider_name != NULL &&
661 strcmp(pvd, pvdck->provider_name) == 0))
662 clk_disable_unprepare(pvdck->ck);
663 }
664}
665static void clkpvdop(void (*pvdop)(const char *), const char *clkpvdop_name,
666 struct seq_file *s)
667{
668 char cmd[sizeof(last_cmd)];
669 char *c = cmd;
670 char *ign;
671 char *pvd_name;
672 strncpy(cmd, last_cmd, sizeof(cmd));
673 cmd[sizeof(cmd) - 1UL] = '\0';
674 ign = strsep(&c, " ");
675 pvd_name = strsep(&c, " ");
676 if (pvd_name == NULL)
677 return;
678 pvdop(pvd_name);
679 seq_printf(s, "%s(%s)\n", clkpvdop_name, pvd_name);
680}
681static int clkdbg_prepare_enable_provider(struct seq_file *s, void *v)
682{
683 clkpvdop(prepare_enable_provider, "prepare_enable_provider", s);
684 return 0;
685}
686static int clkdbg_disable_unprepare_provider(struct seq_file *s, void *v)
687{
688 clkpvdop(disable_unprepare_provider, "disable_unprepare_provider", s);
689 return 0;
690}
691static int clkdbg_set_parent(struct seq_file *s, void *v)
692{
693 char cmd[sizeof(last_cmd)];
694 char *c = cmd;
695 char *ign;
696 char *clk_name;
697 char *parent_name;
698 struct clk *clk;
699 struct clk *parent;
700 int r;
701 strncpy(cmd, last_cmd, sizeof(cmd));
702 cmd[sizeof(cmd) - 1UL] = '\0';
703 ign = strsep(&c, " ");
704 clk_name = strsep(&c, " ");
705 parent_name = strsep(&c, " ");
706 if (clk_name == NULL || parent_name == NULL)
707 return 0;
708 seq_printf(s, "clk_set_parent(%s, %s): ", clk_name, parent_name);
709 clk = __clk_lookup(clk_name);
710 if (IS_ERR_OR_NULL(clk)) {
711 seq_printf(s, "__clk_lookup(): 0x%p\n", clk);
712 return PTR_ERR(clk);
713 }
714 parent = __clk_lookup(parent_name);
715 if (IS_ERR_OR_NULL(parent)) {
716 seq_printf(s, "__clk_lookup(): 0x%p\n", parent);
717 return PTR_ERR(parent);
718 }
719 r = clk_prepare_enable(clk);
720 if (r != 0) {
721 seq_printf(s, "clk_prepare_enable(): %d\n", r);
722 return r;
723 }
724 r = clk_set_parent(clk, parent);
725 seq_printf(s, "%d\n", r);
726 clk_disable_unprepare(clk);
727 return r;
728}
729static int clkdbg_set_rate(struct seq_file *s, void *v)
730{
731 char cmd[sizeof(last_cmd)];
732 char *c = cmd;
733 char *ign;
734 char *clk_name;
735 char *rate_str;
736 struct clk *clk;
737 unsigned long rate = 0;
738 int r;
739 strncpy(cmd, last_cmd, sizeof(cmd));
740 cmd[sizeof(cmd) - 1UL] = '\0';
741 ign = strsep(&c, " ");
742 clk_name = strsep(&c, " ");
743 rate_str = strsep(&c, " ");
744 if (clk_name == NULL || rate_str == NULL)
745 return 0;
746 r = kstrtoul(rate_str, 0, &rate);
747 seq_printf(s, "clk_set_rate(%s, %lu): %d: ", clk_name, rate, r);
748 clk = __clk_lookup(clk_name);
749 if (IS_ERR_OR_NULL(clk)) {
750 seq_printf(s, "__clk_lookup(): 0x%p\n", clk);
751 return PTR_ERR(clk);
752 }
753 r = clk_set_rate(clk, rate);
754 seq_printf(s, "%d\n", r);
755 return r;
756}
757static void *reg_from_str(const char *str)
758{
759 static phys_addr_t phys;
760 static void __iomem *virt;
761 if (sizeof(void *) == sizeof(unsigned long)) {
762 unsigned long v = 0;
763 if (kstrtoul(str, 0, &v) == 0U) {
764 if ((0xf0000000 & v) < 0x20000000) {
765 if (virt != NULL && v > phys
766 && v < phys + PAGE_SIZE)
767 return virt + v - phys;
768 if (virt != NULL)
769 iounmap(virt);
770 phys = v & ~(PAGE_SIZE - 1U);
771 virt = ioremap(phys, PAGE_SIZE);
772 return virt + v - phys;
773 }
774 return (void *)((uintptr_t)v);
775 }
776 } else if (sizeof(void *) == sizeof(unsigned long long)) {
777 unsigned long long v;
778 if (kstrtoull(str, 0, &v) == 0) {
779 if ((0xfffffffff0000000ULL & v) < 0x20000000) {
780 if (virt && v > phys && v < phys + PAGE_SIZE)
781 return virt + v - phys;
782 if (virt != NULL)
783 iounmap(virt);
784 phys = v & ~(PAGE_SIZE - 1);
785 virt = ioremap(phys, PAGE_SIZE);
786 return virt + v - phys;
787 }
788 return (void *)((uintptr_t)v);
789 }
790 } else {
791 pr_warn("unexpected pointer size: sizeof(void *): %zu\n",
792 sizeof(void *));
793 }
794 pr_warn("%s(): parsing error: %s\n", __func__, str);
795 return NULL;
796}
797static int parse_reg_val_from_cmd(void __iomem **preg, unsigned long *pval)
798{
799 char cmd[sizeof(last_cmd)];
800 char *c = cmd;
801 char *ign;
802 char *reg_str;
803 char *val_str;
804 int r = 0;
805 strncpy(cmd, last_cmd, sizeof(cmd));
806 cmd[sizeof(cmd) - 1UL] = '\0';
807 ign = strsep(&c, " ");
808 reg_str = strsep(&c, " ");
809 val_str = strsep(&c, " ");
810 if (preg != NULL && reg_str != NULL) {
811 *preg = reg_from_str(reg_str);
812 if (*preg != NULL)
813 r++;
814 }
815 if (pval != NULL && val_str != NULL && kstrtoul(val_str, 0, pval) == 0)
816 r++;
817 return r;
818}
819static int clkdbg_reg_read(struct seq_file *s, void *v)
820{
821 void __iomem *reg;
822 unsigned long val = 0;
823 if (parse_reg_val_from_cmd(&reg, NULL) != 1)
824 return 0;
825 seq_printf(s, "readl(0x%p): ", reg);
826 val = clk_readl(reg);
827 seq_printf(s, "0x%08x\n", (u32)val);
828 return 0;
829}
830static int clkdbg_reg_write(struct seq_file *s, void *v)
831{
832 void __iomem *reg;
833 unsigned long val = 0;
834 if (parse_reg_val_from_cmd(&reg, &val) != 2)
835 return 0;
836 seq_printf(s, "writel(0x%p, 0x%08x): ", reg, (u32)val);
837 clk_writel(reg, val);
838 val = clk_readl(reg);
839 seq_printf(s, "0x%08x\n", (u32)val);
840 return 0;
841}
842static int clkdbg_reg_set(struct seq_file *s, void *v)
843{
844 void __iomem *reg;
845 unsigned long val = 0;
846 if (parse_reg_val_from_cmd(&reg, &val) != 2)
847 return 0;
848 seq_printf(s, "writel(0x%p, 0x%08x): ", reg, (u32)val);
849 clk_setl(reg, val);
850 val = clk_readl(reg);
851 seq_printf(s, "0x%08x\n", (u32)val);
852 return 0;
853}
854static int clkdbg_reg_clr(struct seq_file *s, void *v)
855{
856 void __iomem *reg;
857 unsigned long val = 0;
858 if (parse_reg_val_from_cmd(&reg, &val) != 2)
859 return 0;
860 seq_printf(s, "writel(0x%p, 0x%08x): ", reg, (u32)val);
861 clk_clrl(reg, val);
862 val = clk_readl(reg);
863 seq_printf(s, "0x%08x\n", (u32)val);
864 return 0;
865}
866static int parse_val_from_cmd(unsigned long *pval)
867{
868 char cmd[sizeof(last_cmd)];
869 char *c = cmd;
870 char *ign;
871 char *val_str;
872 int r = 0;
873 strncpy(cmd, last_cmd, sizeof(cmd));
874 cmd[sizeof(cmd) - 1UL] = '\0';
875 ign = strsep(&c, " ");
876 val_str = strsep(&c, " ");
877 if (pval != NULL && val_str != NULL && kstrtoul(val_str, 0, pval) == 0)
878 r++;
879 return r;
880}
881static int clkdbg_show_flags(struct seq_file *s, void *v)
882{
883 static const char * const clkdbg_opt_name[] = {
884 "CLKDBG_EN_SUSPEND_SAVE_1",
885 "CLKDBG_EN_SUSPEND_SAVE_2",
886 "CLKDBG_EN_SUSPEND_SAVE_3",
887 "CLKDBG_EN_LOG_SAVE_POINTS",
888 };
889 size_t i;
890 seq_printf(s, "clkdbg_flags: 0x%08x\n", clkdbg_flags);
891 for (i = 0; i < ARRAY_SIZE(clkdbg_opt_name); i++) {
892 const char *onff =
893 has_clkdbg_flag((enum clkdbg_opt)i) ? "ON" : "off";
894 seq_printf(s, "[%2zd]: %3s: %s\n", i, onff, clkdbg_opt_name[i]);
895 }
896 return 0;
897}
898static int clkdbg_set_flag(struct seq_file *s, void *v)
899{
900 unsigned long val = 0;
901 if (parse_val_from_cmd(&val) != 1)
902 return 0;
903 set_clkdbg_flag((enum clkdbg_opt)val);
904 seq_printf(s, "clkdbg_flags: 0x%08x\n", clkdbg_flags);
905 return 0;
906}
907static int clkdbg_clr_flag(struct seq_file *s, void *v)
908{
909 unsigned long val = 0;
910 if (parse_val_from_cmd(&val) != 1)
911 return 0;
912 clr_clkdbg_flag((enum clkdbg_opt)val);
913 seq_printf(s, "clkdbg_flags: 0x%08x\n", clkdbg_flags);
914 return 0;
915}
916#if CLKDBG_PM_DOMAIN
917/*
918 * pm_domain support
919 */
920static struct generic_pm_domain **get_all_genpd(void)
921{
922 static struct generic_pm_domain *pds[31];
923 static int num_pds;
924 const size_t maxpd = ARRAY_SIZE(pds);
925 struct device_node *node;
926#if CLKDBG_PM_DOMAIN_API_4_9
927 struct platform_device *pdev;
928 int r;
929#endif
930 if (num_pds != 0)
931 goto out;
932 node = of_find_node_with_property(NULL, "#power-domain-cells");
933 if (node == NULL)
934 return NULL;
935#if CLKDBG_PM_DOMAIN_API_4_9
936 pdev = platform_device_alloc("traverse", 0);
937#endif
938 for (num_pds = 0; num_pds < maxpd; num_pds++) {
939 struct of_phandle_args pa;
940 pa.np = node;
941 pa.args[0] = num_pds;
942 pa.args_count = 1;
943#if CLKDBG_PM_DOMAIN_API_4_9
944 r = of_genpd_add_device(&pa, &pdev->dev);
945 if (r == -EINVAL)
946 continue;
947 else if (r != 0)
948 pr_warn("%s(): of_genpd_add_device(%d)\n", __func__, r);
949 pds[num_pds] = pd_to_genpd(pdev->dev.pm_domain);
950 //r = pm_genpd_remove_device(pds[num_pds], &pdev->dev);
951 r = pm_genpd_remove_device(&pdev->dev);
952 if (r != 0)
953 pr_warn("%s(): pm_genpd_remove_device(%d)\n",
954 __func__, r);
955#else
956 pds[num_pds] = of_genpd_get_from_provider(&pa);
957#endif
958 if (IS_ERR(pds[num_pds])) {
959 pds[num_pds] = NULL;
960 break;
961 }
962 }
963#if CLKDBG_PM_DOMAIN_API_4_9
964 platform_device_put(pdev);
965#endif
966out:
967 return pds;
968}
969static struct platform_device *pdev_from_name(const char *name)
970{
971 struct generic_pm_domain **pds = get_all_genpd();
972 for (; *pds != NULL; pds++) {
973 struct pm_domain_data *pdd;
974 struct generic_pm_domain *pd = *pds;
975 if (IS_ERR_OR_NULL(pd))
976 continue;
977 list_for_each_entry(pdd, &pd->dev_list, list_node) {
978 struct device *dev = pdd->dev;
979 struct platform_device *pdev = to_platform_device(dev);
980 if (strcmp(name, pdev->name) == 0)
981 return pdev;
982 }
983 }
984 return NULL;
985}
986static struct generic_pm_domain *genpd_from_name(const char *name)
987{
988 struct generic_pm_domain **pds = get_all_genpd();
989 for (; *pds != NULL; pds++) {
990 struct generic_pm_domain *pd = *pds;
991 if (IS_ERR_OR_NULL(pd))
992 continue;
993 if (strcmp(name, pd->name) == 0)
994 return pd;
995 }
996 return NULL;
997}
998struct genpd_dev_state {
999 struct device *dev;
1000 bool active;
1001 atomic_t usage_count;
1002 unsigned int disable_depth;
1003 enum rpm_status runtime_status;
1004};
1005struct genpd_state {
1006 struct generic_pm_domain *pd;
1007 enum gpd_status status;
1008 struct genpd_dev_state *dev_state;
1009 int num_dev_state;
1010};
1011static void save_all_genpd_state(struct genpd_state *genpd_states,
1012 struct genpd_dev_state *genpd_dev_states)
1013{
1014 struct genpd_state *pdst = genpd_states;
1015 struct genpd_dev_state *devst = genpd_dev_states;
1016 struct generic_pm_domain **pds = get_all_genpd();
1017 for (; *pds != NULL; pds++) {
1018 struct pm_domain_data *pdd;
1019 struct generic_pm_domain *pd = *pds;
1020 if (IS_ERR_OR_NULL(pd))
1021 continue;
1022 pdst->pd = pd;
1023 pdst->status = pd->status;
1024 pdst->dev_state = devst;
1025 pdst->num_dev_state = 0;
1026 list_for_each_entry(pdd, &pd->dev_list, list_node) {
1027 struct device *d = pdd->dev;
1028 devst->dev = d;
1029 devst->active = pm_runtime_active(d);
1030 devst->usage_count = d->power.usage_count;
1031 devst->disable_depth = d->power.disable_depth;
1032 devst->runtime_status = d->power.runtime_status;
1033 devst++;
1034 pdst->num_dev_state++;
1035 }
1036 pdst++;
1037 }
1038 pdst->pd = NULL;
1039 devst->dev = NULL;
1040}
1041static void show_genpd_state(struct genpd_state *pdst)
1042{
1043 static const char * const gpd_status_name[] = {
1044 "ACTIVE",
1045 "POWER_OFF",
1046 };
1047 static const char * const prm_status_name[] = {
1048 "active",
1049 "resuming",
1050 "suspended",
1051 "suspending",
1052 };
1053 pr_info("domain_on [pmd_name status]\n");
1054 pr_info("\tdev_on (dev_name usage_count, disable, status)\n");
1055 pr_info("------------------------------------------------------\n");
1056 for (; pdst->pd != NULL; pdst++) {
1057 int i;
1058 struct generic_pm_domain *pd = pdst->pd;
1059 if (IS_ERR_OR_NULL(pd)) {
1060 pr_info("pd: 0x%p\n", pd);
1061 continue;
1062 }
1063 pr_info("%c [%-9s %11s]\n",
1064 (pdst->status == GPD_STATE_ACTIVE) ? '+' : '-',
1065 pd->name, gpd_status_name[pdst->status]);
1066 for (i = 0; i < pdst->num_dev_state; i++) {
1067 struct genpd_dev_state *devst = &pdst->dev_state[i];
1068 struct device *dev = devst->dev;
1069 struct platform_device *pdev = to_platform_device(dev);
1070 pr_info("\t%c (%-19s %3d, %d, %10s)\n",
1071 devst->active ? '+' : '-',
1072 pdev->name,
1073 atomic_read(&dev->power.usage_count),
1074 devst->disable_depth,
1075 prm_status_name[devst->runtime_status]);
1076 mdelay(20);
1077 }
1078 }
1079}
1080static void dump_genpd_state(struct genpd_state *pdst, struct seq_file *s)
1081{
1082 static const char * const gpd_status_name[] = {
1083 "ACTIVE",
1084 "POWER_OFF",
1085 };
1086 static const char * const prm_status_name[] = {
1087 "active",
1088 "resuming",
1089 "suspended",
1090 "suspending",
1091 };
1092 seq_puts(s, "domain_on [pmd_name status]\n");
1093 seq_puts(s, "\tdev_on (dev_name usage_count, disable, status)\n");
1094 seq_puts(s, "------------------------------------------------------\n");
1095 for (; pdst->pd != NULL; pdst++) {
1096 int i;
1097 struct generic_pm_domain *pd = pdst->pd;
1098 if (IS_ERR_OR_NULL(pd)) {
1099 seq_printf(s, "pd: 0x%p\n", pd);
1100 continue;
1101 }
1102 seq_printf(s, "%c [%-9s %11s]\n",
1103 (pdst->status == GPD_STATE_ACTIVE) ? '+' : '-',
1104 pd->name, gpd_status_name[pdst->status]);
1105 for (i = 0; i < pdst->num_dev_state; i++) {
1106 struct genpd_dev_state *devst = &pdst->dev_state[i];
1107 struct device *dev = devst->dev;
1108 struct platform_device *pdev = to_platform_device(dev);
1109 seq_printf(s, "\t%c (%-19s %3d, %d, %10s)\n",
1110 devst->active ? '+' : '-',
1111 pdev->name,
1112 atomic_read(&dev->power.usage_count),
1113 devst->disable_depth,
1114 prm_status_name[devst->runtime_status]);
1115 }
1116 }
1117}
1118static void seq_print_all_genpd(struct seq_file *s)
1119{
1120 static struct genpd_dev_state devst[100];
1121 static struct genpd_state pdst[20];
1122 save_all_genpd_state(pdst, devst);
1123 dump_genpd_state(pdst, s);
1124}
1125static int clkdbg_dump_genpd(struct seq_file *s, void *v)
1126{
1127 seq_print_all_genpd(s);
1128 return 0;
1129}
1130static int clkdbg_pm_runtime_enable(struct seq_file *s, void *v)
1131{
1132 char cmd[sizeof(last_cmd)];
1133 char *c = cmd;
1134 char *ign;
1135 char *dev_name;
1136 struct platform_device *pdev;
1137 strncpy(cmd, last_cmd, sizeof(cmd));
1138 cmd[sizeof(cmd) - 1UL] = '\0';
1139 ign = strsep(&c, " ");
1140 dev_name = strsep(&c, " ");
1141 if (dev_name == NULL)
1142 return 0;
1143 seq_printf(s, "pm_runtime_enable(%s): ", dev_name);
1144 pdev = pdev_from_name(dev_name);
1145 if (pdev != NULL) {
1146 pm_runtime_enable(&pdev->dev);
1147 seq_puts(s, "\n");
1148 } else {
1149 seq_puts(s, "NULL\n");
1150 }
1151 return 0;
1152}
1153static int clkdbg_pm_runtime_disable(struct seq_file *s, void *v)
1154{
1155 char cmd[sizeof(last_cmd)];
1156 char *c = cmd;
1157 char *ign;
1158 char *dev_name;
1159 struct platform_device *pdev;
1160 strncpy(cmd, last_cmd, sizeof(cmd));
1161 cmd[sizeof(cmd) - 1UL] = '\0';
1162 ign = strsep(&c, " ");
1163 dev_name = strsep(&c, " ");
1164 if (dev_name == NULL)
1165 return 0;
1166 seq_printf(s, "pm_runtime_disable(%s): ", dev_name);
1167 pdev = pdev_from_name(dev_name);
1168 if (pdev != NULL) {
1169 pm_runtime_disable(&pdev->dev);
1170 seq_puts(s, "\n");
1171 } else {
1172 seq_puts(s, "NULL\n");
1173 }
1174 return 0;
1175}
1176static int clkdbg_pm_runtime_get_sync(struct seq_file *s, void *v)
1177{
1178 char cmd[sizeof(last_cmd)];
1179 char *c = cmd;
1180 char *ign;
1181 char *dev_name;
1182 struct platform_device *pdev;
1183 strncpy(cmd, last_cmd, sizeof(cmd));
1184 cmd[sizeof(cmd) - 1UL] = '\0';
1185 ign = strsep(&c, " ");
1186 dev_name = strsep(&c, " ");
1187 if (dev_name == NULL)
1188 return 0;
1189 seq_printf(s, "pm_runtime_get_sync(%s): ", dev_name);
1190 pdev = pdev_from_name(dev_name);
1191 if (pdev != NULL) {
1192 int r = pm_runtime_get_sync(&pdev->dev);
1193 seq_printf(s, "%d\n", r);
1194 } else {
1195 seq_puts(s, "NULL\n");
1196 }
1197 return 0;
1198}
1199static int clkdbg_pm_runtime_put_sync(struct seq_file *s, void *v)
1200{
1201 char cmd[sizeof(last_cmd)];
1202 char *c = cmd;
1203 char *ign;
1204 char *dev_name;
1205 struct platform_device *pdev;
1206 strncpy(cmd, last_cmd, sizeof(cmd));
1207 cmd[sizeof(cmd) - 1UL] = '\0';
1208 ign = strsep(&c, " ");
1209 dev_name = strsep(&c, " ");
1210 if (dev_name == NULL)
1211 return 0;
1212 seq_printf(s, "pm_runtime_put_sync(%s): ", dev_name);
1213 pdev = pdev_from_name(dev_name);
1214 if (pdev != NULL) {
1215 int r = pm_runtime_put_sync(&pdev->dev);
1216 seq_printf(s, "%d\n", r);
1217 } else {
1218 seq_puts(s, "NULL\n");
1219 }
1220 return 0;
1221}
1222static int genpd_op(const char *gpd_op_name, struct seq_file *s)
1223{
1224 char cmd[sizeof(last_cmd)];
1225 char *c = cmd;
1226 char *ign;
1227 char *pd_name;
1228 struct generic_pm_domain *genpd;
1229 int gpd_op_id;
1230 int (*gpd_op)(struct generic_pm_domain *);
1231 int r = 0;
1232 strncpy(cmd, last_cmd, sizeof(cmd));
1233 cmd[sizeof(cmd) - 1UL] = '\0';
1234 ign = strsep(&c, " ");
1235 pd_name = strsep(&c, " ");
1236 if (pd_name == NULL)
1237 return 0;
1238 if (strcmp(gpd_op_name, "power_on") == 0)
1239 gpd_op_id = 1;
1240 else
1241 gpd_op_id = 0;
1242 if (strcmp(pd_name, "all") == 0) {
1243 struct generic_pm_domain **pds = get_all_genpd();
1244 for (; *pds != NULL; pds++) {
1245 genpd = *pds;
1246 if (IS_ERR_OR_NULL(genpd))
1247 continue;
1248 gpd_op = (gpd_op_id == 1) ?
1249 genpd->power_on : genpd->power_off;
1250 r |= gpd_op(genpd);
1251 }
1252 seq_printf(s, "%s(%s): %d\n", gpd_op_name, pd_name, r);
1253 return 0;
1254 }
1255 genpd = genpd_from_name(pd_name);
1256 if (genpd != NULL) {
1257 gpd_op = (gpd_op_id == 1) ? genpd->power_on : genpd->power_off;
1258 r = gpd_op(genpd);
1259 seq_printf(s, "%s(%s): %d\n", gpd_op_name, pd_name, r);
1260 } else {
1261 seq_printf(s, "genpd_from_name(%s): NULL\n", pd_name);
1262 }
1263 return 0;
1264}
1265static int clkdbg_pwr_on(struct seq_file *s, void *v)
1266{
1267 return genpd_op("power_on", s);
1268}
1269static int clkdbg_pwr_off(struct seq_file *s, void *v)
1270{
1271 return genpd_op("power_off", s);
1272}
1273/*
1274 * clkdbg reg_pdrv/runeg_pdrv support
1275 */
1276static int clkdbg_probe(struct platform_device *pdev)
1277{
1278 int r;
1279 pm_runtime_enable(&pdev->dev);
1280 r = pm_runtime_get_sync(&pdev->dev);
1281 if (r != 0)
1282 pr_warn("%s(): pm_runtime_get_sync(%d)\n", __func__, r);
1283 return r;
1284}
1285static int clkdbg_remove(struct platform_device *pdev)
1286{
1287 int r;
1288 r = pm_runtime_put_sync(&pdev->dev);
1289 if (r != 0)
1290 pr_warn("%s(): pm_runtime_put_sync(%d)\n", __func__, r);
1291 pm_runtime_disable(&pdev->dev);
1292 return r;
1293}
1294struct pdev_drv {
1295 struct platform_driver pdrv;
1296 struct platform_device *pdev;
1297 struct generic_pm_domain *genpd;
1298};
1299#define PDEV_DRV(_name) { \
1300 .pdrv = { \
1301 .probe = clkdbg_probe, \
1302 .remove = clkdbg_remove, \
1303 .driver = { \
1304 .name = _name, \
1305 }, \
1306 }, \
1307}
1308static struct pdev_drv pderv[] = {
1309 PDEV_DRV("clkdbg-pd0"),
1310 PDEV_DRV("clkdbg-pd1"),
1311 PDEV_DRV("clkdbg-pd2"),
1312 PDEV_DRV("clkdbg-pd3"),
1313 PDEV_DRV("clkdbg-pd4"),
1314 PDEV_DRV("clkdbg-pd5"),
1315 PDEV_DRV("clkdbg-pd6"),
1316 PDEV_DRV("clkdbg-pd7"),
1317 PDEV_DRV("clkdbg-pd8"),
1318 PDEV_DRV("clkdbg-pd9"),
1319 PDEV_DRV("clkdbg-pd10"),
1320 PDEV_DRV("clkdbg-pd11"),
1321 PDEV_DRV("clkdbg-pd12"),
1322 PDEV_DRV("clkdbg-pd13"),
1323 PDEV_DRV("clkdbg-pd14"),
1324 PDEV_DRV("clkdbg-pd15"),
1325};
1326static void reg_pdev_drv(const char *pdname, struct seq_file *s)
1327{
1328 size_t i;
1329 struct generic_pm_domain **pds = get_all_genpd();
1330 bool allpd = (pdname == NULL || strcmp(pdname, "all") == 0);
1331 int r;
1332 for (i = 0; i < ARRAY_SIZE(pderv) && *pds != NULL; i++, pds++) {
1333 const char *name = pderv[i].pdrv.driver.name;
1334 struct generic_pm_domain *pd = *pds;
1335 if (IS_ERR_OR_NULL(pd) || pderv[i].genpd != NULL)
1336 continue;
1337 if (!allpd && strcmp(pdname, pd->name) != 0)
1338 continue;
1339 pderv[i].genpd = pd;
1340 pderv[i].pdev = platform_device_alloc(name, 0);
1341 r = platform_device_add(pderv[i].pdev);
1342 if (r != 0 && s != NULL)
1343 seq_printf(s, "%s(): platform_device_add(%d)\n",
1344 __func__, r);
1345 r = pm_genpd_add_device(pd, &pderv[i].pdev->dev);
1346 if (r != 0 && s != NULL)
1347 seq_printf(s, "%s(): pm_genpd_add_device(%d)\n",
1348 __func__, r);
1349 r = platform_driver_register(&pderv[i].pdrv);
1350 if (r != 0 && s != NULL)
1351 seq_printf(s, "%s(): platform_driver_register(%d)\n",
1352 __func__, r);
1353 if (s != NULL)
1354 seq_printf(s, "%s --> %s\n", name, pd->name);
1355 }
1356}
1357static void unreg_pdev_drv(const char *pdname, struct seq_file *s)
1358{
1359 ssize_t i;
1360 bool allpd = (pdname == NULL || strcmp(pdname, "all") == 0);
1361 int r;
1362 for (i = ARRAY_SIZE(pderv) - 1L; i >= 0L; i--) {
1363 const char *name = pderv[i].pdrv.driver.name;
1364 struct generic_pm_domain *pd = pderv[i].genpd;
1365 if (IS_ERR_OR_NULL(pd))
1366 continue;
1367 if (!allpd && strcmp(pdname, pd->name) != 0)
1368 continue;
1369 r = pm_genpd_remove_device(&pderv[i].pdev->dev);
1370 //r = pm_genpd_remove_device(pd, &pderv[i].pdev->dev);
1371 if (r != 0 && s != NULL)
1372 seq_printf(s, "%s(): pm_genpd_remove_device(%d)\n",
1373 __func__, r);
1374 platform_driver_unregister(&pderv[i].pdrv);
1375 platform_device_unregister(pderv[i].pdev);
1376 pderv[i].genpd = NULL;
1377 if (s != NULL)
1378 seq_printf(s, "%s -x- %s\n", name, pd->name);
1379 }
1380}
1381static int clkdbg_reg_pdrv(struct seq_file *s, void *v)
1382{
1383 char cmd[sizeof(last_cmd)];
1384 char *c = cmd;
1385 char *ign;
1386 char *pd_name;
1387 strncpy(cmd, last_cmd, sizeof(cmd));
1388 cmd[sizeof(cmd) - 1UL] = '\0';
1389 ign = strsep(&c, " ");
1390 pd_name = strsep(&c, " ");
1391 if (pd_name == NULL)
1392 return 0;
1393 reg_pdev_drv(pd_name, s);
1394 return 0;
1395}
1396static int clkdbg_unreg_pdrv(struct seq_file *s, void *v)
1397{
1398 char cmd[sizeof(last_cmd)];
1399 char *c = cmd;
1400 char *ign;
1401 char *pd_name;
1402 strncpy(cmd, last_cmd, sizeof(cmd));
1403 cmd[sizeof(cmd) - 1UL] = '\0';
1404 ign = strsep(&c, " ");
1405 pd_name = strsep(&c, " ");
1406 if (pd_name == NULL)
1407 return 0;
1408 unreg_pdev_drv(pd_name, s);
1409 return 0;
1410}
1411#endif /* CLKDBG_PM_DOMAIN */
1412void reg_pdrv(const char *pdname)
1413{
1414#if CLKDBG_PM_DOMAIN
1415 reg_pdev_drv(pdname, NULL);
1416#endif
1417}
1418void unreg_pdrv(const char *pdname)
1419{
1420#if CLKDBG_PM_DOMAIN
1421 unreg_pdev_drv(pdname, NULL);
1422#endif
1423}
1424/*
1425 * Suspend / resume handler
1426 */
1427#include <linux/suspend.h>
1428#include <linux/syscore_ops.h>
1429struct provider_clk_state {
1430 struct provider_clk *pvdck;
1431 bool prepared;
1432 bool enabled;
1433 unsigned int enable_count;
1434 unsigned long rate;
1435 struct clk *parent;
1436};
1437struct save_point {
1438 u32 spm_pwr_status;
1439 struct provider_clk_state clks_states[512];
1440#if CLKDBG_PM_DOMAIN
1441 struct genpd_state genpd_states[20];
1442 struct genpd_dev_state genpd_dev_states[100];
1443#endif
1444};
1445static struct save_point save_point_1;
1446static struct save_point save_point_2;
1447static struct save_point save_point_3;
1448static void save_pwr_status(u32 *spm_pwr_status)
1449{
1450 *spm_pwr_status = read_spm_pwr_status();
1451}
1452static void save_all_clks_state(struct provider_clk_state *clks_states,
1453 u32 spm_pwr_status)
1454{
1455 struct provider_clk *pvdck = get_all_provider_clks();
1456 struct provider_clk_state *st = clks_states;
1457 for (; pvdck->ck != NULL; pvdck++, st++) {
1458 struct clk *c = pvdck->ck;
1459 struct clk_hw *c_hw = __clk_get_hw(c);
1460 st->pvdck = pvdck;
1461 st->prepared = clk_hw_is_prepared(c_hw);
1462 st->enabled = clk_hw_pwr_is_on(c_hw, spm_pwr_status,
1463 pvdck->pwr_mask);
1464 st->enable_count = __clk_get_enable_count(c);
1465 st->rate = clk_hw_get_rate(c_hw);
1466 st->parent = IS_ERR_OR_NULL(c) ? NULL : clk_get_parent(c);
1467 }
1468}
1469static void show_provider_clk_state(struct provider_clk_state *st)
1470{
1471 struct provider_clk *pvdck = st->pvdck;
1472 struct clk_hw *c_hw = __clk_get_hw(pvdck->ck);
1473 pr_info("[%10s: %-17s: %3s, %3d, %3d, %10ld, %17s]\n",
1474 pvdck->provider_name != NULL ? pvdck->provider_name : "/ ",
1475 clk_hw_get_name(c_hw),
1476 st->enabled ? "ON" : "off",
1477 st->prepared,
1478 st->enable_count,
1479 st->rate,
1480 st->parent != NULL ?
1481 clk_hw_get_name(__clk_get_hw(st->parent)) : "- ");
1482 mdelay(20);
1483}
1484static void dump_provider_clk_state(struct provider_clk_state *st,
1485 struct seq_file *s)
1486{
1487 struct provider_clk *pvdck = st->pvdck;
1488 struct clk_hw *c_hw = __clk_get_hw(pvdck->ck);
1489 seq_printf(s, "[%10s: %-17s: %3s, %3d, %3d, %10ld, %17s]\n",
1490 pvdck->provider_name != NULL ? pvdck->provider_name : "/ ",
1491 clk_hw_get_name(c_hw),
1492 st->enabled ? "ON" : "off",
1493 st->prepared,
1494 st->enable_count,
1495 st->rate,
1496 st->parent != NULL ?
1497 clk_hw_get_name(__clk_get_hw(st->parent)) : "- ");
1498}
1499static void show_save_point(struct save_point *sp)
1500{
1501 struct provider_clk_state *st = sp->clks_states;
1502 for (; st->pvdck != NULL; st++)
1503 show_provider_clk_state(st);
1504 pr_info("\n");
1505 show_pwr_status(sp->spm_pwr_status);
1506#if CLKDBG_PM_DOMAIN
1507 pr_info("\n");
1508 show_genpd_state(sp->genpd_states);
1509#endif
1510}
1511static void store_save_point(struct save_point *sp)
1512{
1513 save_pwr_status(&sp->spm_pwr_status);
1514 save_all_clks_state(sp->clks_states, sp->spm_pwr_status);
1515#if CLKDBG_PM_DOMAIN
1516 save_all_genpd_state(sp->genpd_states, sp->genpd_dev_states);
1517#endif
1518 if (has_clkdbg_flag(CLKDBG_EN_LOG_SAVE_POINTS))
1519 show_save_point(sp);
1520}
1521static void dump_save_point(struct save_point *sp, struct seq_file *s)
1522{
1523 struct provider_clk_state *st = sp->clks_states;
1524 for (; st->pvdck != NULL; st++)
1525 dump_provider_clk_state(st, s);
1526 seq_puts(s, "\n");
1527 dump_pwr_status(sp->spm_pwr_status, s);
1528#if CLKDBG_PM_DOMAIN
1529 seq_puts(s, "\n");
1530 dump_genpd_state(sp->genpd_states, s);
1531#endif
1532}
1533static int clkdbg_dump_suspend_clks_1(struct seq_file *s, void *v)
1534{
1535 dump_save_point(&save_point_1, s);
1536 return 0;
1537}
1538static int clkdbg_dump_suspend_clks_2(struct seq_file *s, void *v)
1539{
1540 dump_save_point(&save_point_2, s);
1541 return 0;
1542}
1543static int clkdbg_dump_suspend_clks_3(struct seq_file *s, void *v)
1544{
1545 dump_save_point(&save_point_3, s);
1546 return 0;
1547}
1548static int clkdbg_dump_suspend_clks(struct seq_file *s, void *v)
1549{
1550 if (has_clkdbg_flag(CLKDBG_EN_SUSPEND_SAVE_3) &&
1551 save_point_3.spm_pwr_status != 0U)
1552 return clkdbg_dump_suspend_clks_3(s, v);
1553 else if (has_clkdbg_flag(CLKDBG_EN_SUSPEND_SAVE_2) &&
1554 save_point_2.spm_pwr_status != 0U)
1555 return clkdbg_dump_suspend_clks_2(s, v);
1556 else if (has_clkdbg_flag(CLKDBG_EN_SUSPEND_SAVE_1) &&
1557 save_point_1.spm_pwr_status != 0U)
1558 return clkdbg_dump_suspend_clks_1(s, v);
1559 return 0;
1560}
1561static int clkdbg_pm_event_handler(struct notifier_block *nb,
1562 unsigned long event, void *ptr)
1563{
1564 switch (event) {
1565 case PM_HIBERNATION_PREPARE:
1566 case PM_SUSPEND_PREPARE:
1567 /* suspend */
1568 if (has_clkdbg_flag(CLKDBG_EN_SUSPEND_SAVE_1)) {
1569 store_save_point(&save_point_1);
1570 return NOTIFY_OK;
1571 }
1572 break;
1573 case PM_POST_HIBERNATION:
1574 case PM_POST_SUSPEND:
1575 /* resume */
1576 break;
1577 }
1578 return NOTIFY_DONE;
1579}
1580static struct notifier_block clkdbg_pm_notifier = {
1581 .notifier_call = clkdbg_pm_event_handler,
1582};
1583static int clkdbg_syscore_suspend(void)
1584{
1585 if (has_clkdbg_flag(CLKDBG_EN_SUSPEND_SAVE_2))
1586 store_save_point(&save_point_2);
1587 return 0;
1588}
1589static void clkdbg_syscore_resume(void)
1590{
1591}
1592static struct syscore_ops clkdbg_syscore_ops = {
1593 .suspend = clkdbg_syscore_suspend,
1594 .resume = clkdbg_syscore_resume,
1595};
1596static int __init clkdbg_pm_init(void)
1597{
1598 int r;
1599 register_syscore_ops(&clkdbg_syscore_ops);
1600 r = register_pm_notifier(&clkdbg_pm_notifier);
1601 if (r != 0)
1602 pr_warn("%s(): register_pm_notifier(%d)\n", __func__, r);
1603 return r;
1604}
1605subsys_initcall(clkdbg_pm_init);
1606static int clkdbg_suspend_ops_valid(suspend_state_t state)
1607{
1608 return state == PM_SUSPEND_MEM ? 1 : 0;
1609}
1610static int clkdbg_suspend_ops_begin(suspend_state_t state)
1611{
1612 return 0;
1613}
1614static int clkdbg_suspend_ops_prepare(void)
1615{
1616 return 0;
1617}
1618static int clkdbg_suspend_ops_enter(suspend_state_t state)
1619{
1620 if (has_clkdbg_flag(CLKDBG_EN_SUSPEND_SAVE_3))
1621 store_save_point(&save_point_3);
1622 return 0;
1623}
1624static void clkdbg_suspend_ops_finish(void)
1625{
1626}
1627static void clkdbg_suspend_ops_end(void)
1628{
1629}
1630static const struct platform_suspend_ops clkdbg_suspend_ops = {
1631 .valid = clkdbg_suspend_ops_valid,
1632 .begin = clkdbg_suspend_ops_begin,
1633 .prepare = clkdbg_suspend_ops_prepare,
1634 .enter = clkdbg_suspend_ops_enter,
1635 .finish = clkdbg_suspend_ops_finish,
1636 .end = clkdbg_suspend_ops_end,
1637};
1638static int clkdbg_suspend_set_ops(struct seq_file *s, void *v)
1639{
1640 suspend_set_ops(&clkdbg_suspend_ops);
1641 return 0;
1642}
1643static const struct cmd_fn *custom_cmds;
1644void set_custom_cmds(const struct cmd_fn *cmds)
1645{
1646 custom_cmds = cmds;
1647}
1648static int clkdbg_cmds(struct seq_file *s, void *v);
1649static const struct cmd_fn common_cmds[] = {
1650 CMDFN("dump_regs", seq_print_regs),
1651 CMDFN("dump_regs2", clkdbg_dump_regs2),
1652 CMDFN("dump_state", clkdbg_dump_state_all),
1653 CMDFN("dump_clks", clkdbg_dump_provider_clks),
1654 CMDFN("dump_muxes", clkdbg_dump_muxes),
1655 CMDFN("fmeter", seq_print_fmeter_all),
1656 CMDFN("pwr_status", clkdbg_pwr_status),
1657 CMDFN("prepare", clkdbg_prepare),
1658 CMDFN("unprepare", clkdbg_unprepare),
1659 CMDFN("enable", clkdbg_enable),
1660 CMDFN("disable", clkdbg_disable),
1661 CMDFN("prepare_enable", clkdbg_prepare_enable),
1662 CMDFN("disable_unprepare", clkdbg_disable_unprepare),
1663 CMDFN("prepare_enable_provider", clkdbg_prepare_enable_provider),
1664 CMDFN("disable_unprepare_provider", clkdbg_disable_unprepare_provider),
1665 CMDFN("set_parent", clkdbg_set_parent),
1666 CMDFN("set_rate", clkdbg_set_rate),
1667 CMDFN("reg_read", clkdbg_reg_read),
1668 CMDFN("reg_write", clkdbg_reg_write),
1669 CMDFN("reg_set", clkdbg_reg_set),
1670 CMDFN("reg_clr", clkdbg_reg_clr),
1671 CMDFN("show_flags", clkdbg_show_flags),
1672 CMDFN("set_flag", clkdbg_set_flag),
1673 CMDFN("clr_flag", clkdbg_clr_flag),
1674#if CLKDBG_PM_DOMAIN
1675 CMDFN("dump_genpd", clkdbg_dump_genpd),
1676 CMDFN("pm_runtime_enable", clkdbg_pm_runtime_enable),
1677 CMDFN("pm_runtime_disable", clkdbg_pm_runtime_disable),
1678 CMDFN("pm_runtime_get_sync", clkdbg_pm_runtime_get_sync),
1679 CMDFN("pm_runtime_put_sync", clkdbg_pm_runtime_put_sync),
1680 CMDFN("pwr_on", clkdbg_pwr_on),
1681 CMDFN("pwr_off", clkdbg_pwr_off),
1682 CMDFN("reg_pdrv", clkdbg_reg_pdrv),
1683 CMDFN("unreg_pdrv", clkdbg_unreg_pdrv),
1684#endif /* CLKDBG_PM_DOMAIN */
1685 CMDFN("suspend_set_ops", clkdbg_suspend_set_ops),
1686 CMDFN("dump_suspend_clks", clkdbg_dump_suspend_clks),
1687 CMDFN("dump_suspend_clks_1", clkdbg_dump_suspend_clks_1),
1688 CMDFN("dump_suspend_clks_2", clkdbg_dump_suspend_clks_2),
1689 CMDFN("dump_suspend_clks_3", clkdbg_dump_suspend_clks_3),
1690 CMDFN("cmds", clkdbg_cmds),
1691 {}
1692};
1693static int clkdbg_cmds(struct seq_file *s, void *v)
1694{
1695 const struct cmd_fn *cf;
1696 for (cf = common_cmds; cf->cmd != NULL; cf++)
1697 seq_printf(s, "%s\n", cf->cmd);
1698 for (cf = custom_cmds; cf != NULL && cf->cmd != NULL; cf++)
1699 seq_printf(s, "%s\n", cf->cmd);
1700 seq_puts(s, "\n");
1701 return 0;
1702}
1703static int clkdbg_show(struct seq_file *s, void *v)
1704{
1705 const struct cmd_fn *cf;
1706 char cmd[sizeof(last_cmd)];
1707 strncpy(cmd, last_cmd, sizeof(cmd));
1708 cmd[sizeof(cmd) - 1UL] = '\0';
1709 for (cf = custom_cmds; cf != NULL && cf->cmd != NULL; cf++) {
1710 char *c = cmd;
1711 char *token = strsep(&c, " ");
1712 if (strcmp(cf->cmd, token) == 0)
1713 return cf->fn(s, v);
1714 }
1715 for (cf = common_cmds; cf->cmd != NULL; cf++) {
1716 char *c = cmd;
1717 char *token = strsep(&c, " ");
1718 if (strcmp(cf->cmd, token) == 0)
1719 return cf->fn(s, v);
1720 }
1721 return 0;
1722}
1723static int clkdbg_open(struct inode *inode, struct file *file)
1724{
1725 return single_open(file, clkdbg_show, NULL);
1726}
1727static ssize_t clkdbg_write(
1728 struct file *file,
1729 const char __user *buffer,
1730 size_t count,
1731 loff_t *data)
1732{
1733 size_t len = 0;
1734 char *nl;
1735 len = (count < (sizeof(last_cmd) - 1UL)) ?
1736 count : (sizeof(last_cmd) - 1UL);
1737 if (copy_from_user(last_cmd, buffer, len) != 0UL)
1738 return 0;
1739 last_cmd[len] = '\0';
1740 nl = strchr(last_cmd, '\n');
1741 if (nl != NULL)
1742 *nl = '\0';
1743 return (ssize_t)len;
1744}
1745static const struct file_operations clkdbg_fops = {
1746 .owner = THIS_MODULE,
1747 .open = clkdbg_open,
1748 .read = seq_read,
1749 .write = clkdbg_write,
1750 .llseek = seq_lseek,
1751 .release = single_release,
1752};
1753/*
1754 * init functions
1755 */
1756static int __init clkdbg_debug_init(void)
1757{
1758 struct proc_dir_entry *entry;
1759 entry = proc_create("clkdbg", 0644, NULL, &clkdbg_fops);
1760 if (entry == 0)
1761 return -ENOMEM;
1762 set_clkdbg_flag(CLKDBG_EN_SUSPEND_SAVE_3);
1763 return 0;
1764}
1765module_init(clkdbg_debug_init);