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