blob: cdc1ca4c0e7352645262bdd60c363441a72d3f87 [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2019 MediaTek Inc.
4 */
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
149 pr_notice("Enter CLOCK suspend Call Back !!\n");
150 check_pll_off();
151
152 return 0;
153}
154
155static void clkchk_syscore_resume(void)
156{
157 pr_notice("Enter CLOCK resume Call Back !!\n");
158}
159
160static struct syscore_ops clkchk_syscore_ops = {
161 .suspend = clkchk_syscore_suspend,
162 .resume = clkchk_syscore_resume,
163};
164
165int clkchk_init(struct clkchk_cfg_t *cfg)
166{
167 const char * const *c;
168 bool match = false;
169
170 if (cfg == NULL || cfg->compatible == NULL
171 || cfg->all_clk_names == NULL || cfg->off_pll_names == NULL) {
172 pr_warn("Invalid clkchk_cfg.\n");
173 return -EINVAL;
174 }
175
176 clkchk_cfg = cfg;
177
178 for (c = cfg->compatible; *c != NULL; c++) {
179 if (of_machine_is_compatible(*c) != 0) {
180 match = true;
181 break;
182 }
183 }
184
185 if (!match)
186 return -ENODEV;
187
188 register_syscore_ops(&clkchk_syscore_ops);
189
190 return 0;
191}