blob: d50110a0d53837e0254478d3e2ece09e7a92e723 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001// SPDX-License-Identifier: GPL-2.0
2//
3// Copyright (c) 2018 MediaTek Inc.
4// Author: Weiyi Lu <weiyi.lu@mediatek.com>
5
6#define pr_fmt(fmt) "[clkchk] " fmt
7
8#include <linux/clk-provider.h>
9#include <linux/syscore_ops.h>
10#include "clkchk.h"
11
12#define AEE_EXCP_CHECK_PLL_FAIL 0
13#define CLKDBG_CCF_API_4_4 1
14#define MAX_PLLS 32
15
16#if AEE_EXCP_CHECK_PLL_FAIL
17#include <mt-plat/aee.h>
18#endif
19
20#if !CLKDBG_CCF_API_4_4
21
22/* backward compatible */
23
24static const char *clk_hw_get_name(const struct clk_hw *hw)
25{
26 return __clk_get_name(hw->clk);
27}
28
29static bool clk_hw_is_prepared(const struct clk_hw *hw)
30{
31 return __clk_is_prepared(hw->clk);
32}
33
34static bool clk_hw_is_enabled(const struct clk_hw *hw)
35{
36 return __clk_is_enabled(hw->clk);
37}
38
39static unsigned long clk_hw_get_rate(const struct clk_hw *hw)
40{
41 return __clk_get_rate(hw->clk);
42}
43
44static struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw)
45{
46 return __clk_get_hw(clk_get_parent(hw->clk));
47}
48
49#endif /* !CLKDBG_CCF_API_4_4 */
50
51static struct clkchk_cfg_t *clkchk_cfg;
52
53static const char *ccf_state(struct clk_hw *hw)
54{
55 if (__clk_get_enable_count(hw->clk))
56 return "enabled";
57
58 if (clk_hw_is_prepared(hw))
59 return "prepared";
60
61 return "disabled";
62}
63
64static void print_enabled_clks(void)
65{
66 const char * const *cn = clkchk_cfg->all_clk_names;
67
68 pr_warn("enabled clks:\n");
69
70 for (; *cn != NULL; cn++) {
71 struct clk *c = __clk_lookup(*cn);
72 struct clk_hw *c_hw = __clk_get_hw(c);
73 struct clk_hw *p_hw;
74
75 if (IS_ERR_OR_NULL(c) || c_hw == NULL)
76 continue;
77
78 p_hw = clk_hw_get_parent(c_hw);
79
80 if (p_hw == NULL)
81 continue;
82
83 if (!clk_hw_is_prepared(c_hw) &&
84 __clk_get_enable_count(c) <= 0U)
85 continue;
86
87 pr_warn("[%-17s: %8s, %3d, %3d, %10ld, %17s]\n",
88 clk_hw_get_name(c_hw),
89 ccf_state(c_hw),
90 clk_hw_is_prepared(c_hw),
91 __clk_get_enable_count(c),
92 clk_hw_get_rate(c_hw),
93 p_hw != NULL ? clk_hw_get_name(p_hw) : "- ");
94 }
95}
96
97static void check_pll_off(void)
98{
99 static struct clk *off_plls[MAX_PLLS];
100
101 struct clk **c;
102 int invalid = 0;
103 char buf[128] = {0};
104 int n = 0;
105
106 if (off_plls[0] == NULL) {
107 const char * const *pn = clkchk_cfg->off_pll_names;
108 struct clk **end = off_plls + MAX_PLLS - 1;
109
110 for (c = off_plls; *pn != NULL && c < end; pn++, c++)
111 *c = __clk_lookup(*pn);
112 }
113
114 for (c = off_plls; *c != NULL; c++) {
115 struct clk_hw *c_hw = __clk_get_hw(*c);
116
117 if (c_hw == NULL)
118 continue;
119
120 if (!clk_hw_is_prepared(c_hw) && !clk_hw_is_enabled(c_hw))
121 continue;
122
123 n += snprintf(buf + n, sizeof(buf) - (size_t)n, "%s ",
124 clk_hw_get_name(c_hw));
125
126 invalid++;
127 }
128
129 if (invalid == 0)
130 return;
131
132 /* invalid. output debug info */
133
134 pr_warn("unexpected unclosed PLL: %s\n", buf);
135 print_enabled_clks();
136
137#if AEE_EXCP_CHECK_PLL_FAIL
138 if (clkchk_cfg->aee_excp_on_fail)
139 aee_kernel_exception("clkchk", "unclosed PLL: %s\n", buf);
140#endif
141
142 if (clkchk_cfg->warn_on_fail)
143 WARN_ON(true);
144}
145
146static int clkchk_syscore_suspend(void)
147{
148 check_pll_off();
149
150 return 0;
151}
152
153static void clkchk_syscore_resume(void)
154{
155}
156
157static struct syscore_ops clkchk_syscore_ops = {
158 .suspend = clkchk_syscore_suspend,
159 .resume = clkchk_syscore_resume,
160};
161
162int clkchk_init(struct clkchk_cfg_t *cfg)
163{
164 const char * const *c;
165 bool match = false;
166
167 if (cfg == NULL || cfg->compatible == NULL
168 || cfg->all_clk_names == NULL || cfg->off_pll_names == NULL) {
169 pr_warn("Invalid clkchk_cfg.\n");
170 return -EINVAL;
171 }
172
173 clkchk_cfg = cfg;
174
175 for (c = cfg->compatible; *c != NULL; c++) {
176 if (of_machine_is_compatible(*c) != 0) {
177 match = true;
178 break;
179 }
180 }
181
182 if (!match)
183 return -ENODEV;
184
185 register_syscore_ops(&clkchk_syscore_ops);
186
187 return 0;
188}