diff options
author | David Brownell <david-b@pacbell.net> | 2007-08-09 23:56:07 -0400 |
---|---|---|
committer | Haavard Skinnemoen <hskinnemoen@atmel.com> | 2007-10-11 05:38:41 -0400 |
commit | d938b89392bd3ff64e0610d8c4e0d3f7091d98db (patch) | |
tree | ba1b94b3387e031506fbac50f398afc1cff949fb | |
parent | 2417a130bd4013f804983c62cb116bc9ec7f8e2d (diff) |
[AVR32] /sys/kernel/debug/at32ap_clk
When debugfs is available, /sys/kernel/debug/at32ap_clk will provide a
dump of the power manager registers and of the current clock tree. This
can help sorting out various surprises, and when making runtime PM work.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
-rw-r--r-- | arch/avr32/mach-at32ap/clock.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/arch/avr32/mach-at32ap/clock.c b/arch/avr32/mach-at32ap/clock.c index 0f8c89c9f832..4642117cc9ab 100644 --- a/arch/avr32/mach-at32ap/clock.c +++ b/arch/avr32/mach-at32ap/clock.c | |||
@@ -150,3 +150,119 @@ struct clk *clk_get_parent(struct clk *clk) | |||
150 | return clk->parent; | 150 | return clk->parent; |
151 | } | 151 | } |
152 | EXPORT_SYMBOL(clk_get_parent); | 152 | EXPORT_SYMBOL(clk_get_parent); |
153 | |||
154 | |||
155 | |||
156 | #ifdef CONFIG_DEBUG_FS | ||
157 | |||
158 | /* /sys/kernel/debug/at32ap_clk */ | ||
159 | |||
160 | #include <linux/io.h> | ||
161 | #include <linux/debugfs.h> | ||
162 | #include <linux/seq_file.h> | ||
163 | #include "pm.h" | ||
164 | |||
165 | |||
166 | #define NEST_DELTA 2 | ||
167 | #define NEST_MAX 6 | ||
168 | |||
169 | struct clkinf { | ||
170 | struct seq_file *s; | ||
171 | unsigned nest; | ||
172 | }; | ||
173 | |||
174 | static void | ||
175 | dump_clock(struct clk *parent, struct clkinf *r) | ||
176 | { | ||
177 | unsigned nest = r->nest; | ||
178 | char buf[16 + NEST_MAX]; | ||
179 | struct clk *clk; | ||
180 | unsigned i; | ||
181 | |||
182 | /* skip clocks coupled to devices that aren't registered */ | ||
183 | if (parent->dev && !parent->dev->bus_id[0] && !parent->users) | ||
184 | return; | ||
185 | |||
186 | /* <nest spaces> name <pad to end> */ | ||
187 | memset(buf, ' ', sizeof(buf) - 1); | ||
188 | buf[sizeof(buf) - 1] = 0; | ||
189 | i = strlen(parent->name); | ||
190 | memcpy(buf + nest, parent->name, | ||
191 | min(i, (unsigned)(sizeof(buf) - 1 - nest))); | ||
192 | |||
193 | seq_printf(r->s, "%s%c users=%2d %-3s %9ld Hz", | ||
194 | buf, parent->set_parent ? '*' : ' ', | ||
195 | parent->users, | ||
196 | parent->users ? "on" : "off", /* NOTE: not-paranoid!! */ | ||
197 | clk_get_rate(parent)); | ||
198 | if (parent->dev) | ||
199 | seq_printf(r->s, ", for %s", parent->dev->bus_id); | ||
200 | seq_printf(r->s, "\n"); | ||
201 | |||
202 | /* cost of this scan is small, but not linear... */ | ||
203 | r->nest = nest + NEST_DELTA; | ||
204 | for (i = 3; i < at32_nr_clocks; i++) { | ||
205 | clk = at32_clock_list[i]; | ||
206 | if (clk->parent == parent) | ||
207 | dump_clock(clk, r); | ||
208 | } | ||
209 | r->nest = nest; | ||
210 | } | ||
211 | |||
212 | static int clk_show(struct seq_file *s, void *unused) | ||
213 | { | ||
214 | struct clkinf r; | ||
215 | int i; | ||
216 | |||
217 | /* show all the power manager registers */ | ||
218 | seq_printf(s, "MCCTRL = %8x\n", pm_readl(MCCTRL)); | ||
219 | seq_printf(s, "CKSEL = %8x\n", pm_readl(CKSEL)); | ||
220 | seq_printf(s, "CPUMASK = %8x\n", pm_readl(CPU_MASK)); | ||
221 | seq_printf(s, "HSBMASK = %8x\n", pm_readl(HSB_MASK)); | ||
222 | seq_printf(s, "PBAMASK = %8x\n", pm_readl(PBA_MASK)); | ||
223 | seq_printf(s, "PBBMASK = %8x\n", pm_readl(PBB_MASK)); | ||
224 | seq_printf(s, "PLL0 = %8x\n", pm_readl(PLL0)); | ||
225 | seq_printf(s, "PLL1 = %8x\n", pm_readl(PLL1)); | ||
226 | seq_printf(s, "IMR = %8x\n", pm_readl(IMR)); | ||
227 | for (i = 0; i < 8; i++) { | ||
228 | if (i == 5) | ||
229 | continue; | ||
230 | seq_printf(s, "GCCTRL%d = %8x\n", i, pm_readl(GCCTRL(i))); | ||
231 | } | ||
232 | |||
233 | seq_printf(s, "\n"); | ||
234 | |||
235 | /* show clock tree as derived from the three oscillators | ||
236 | * we "know" are at the head of the list | ||
237 | */ | ||
238 | r.s = s; | ||
239 | r.nest = 0; | ||
240 | dump_clock(at32_clock_list[0], &r); | ||
241 | dump_clock(at32_clock_list[1], &r); | ||
242 | dump_clock(at32_clock_list[2], &r); | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static int clk_open(struct inode *inode, struct file *file) | ||
248 | { | ||
249 | return single_open(file, clk_show, NULL); | ||
250 | } | ||
251 | |||
252 | static const struct file_operations clk_operations = { | ||
253 | .open = clk_open, | ||
254 | .read = seq_read, | ||
255 | .llseek = seq_lseek, | ||
256 | .release = single_release, | ||
257 | }; | ||
258 | |||
259 | static int __init clk_debugfs_init(void) | ||
260 | { | ||
261 | (void) debugfs_create_file("at32ap_clk", S_IFREG | S_IRUGO, | ||
262 | NULL, NULL, &clk_operations); | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | postcore_initcall(clk_debugfs_init); | ||
267 | |||
268 | #endif | ||