• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/arch/arm/mach-omap2/
1/*
2 * OMAP Power Management debug routines
3 *
4 * Copyright (C) 2005 Texas Instruments, Inc.
5 * Copyright (C) 2006-2008 Nokia Corporation
6 *
7 * Written by:
8 * Richard Woodruff <r-woodruff2@ti.com>
9 * Tony Lindgren
10 * Juha Yrjola
11 * Amit Kucheria <amit.kucheria@nokia.com>
12 * Igor Stoppa <igor.stoppa@nokia.com>
13 * Jouni Hogander
14 *
15 * Based on pm.c for omap2
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License version 2 as
19 * published by the Free Software Foundation.
20 */
21
22#include <linux/kernel.h>
23#include <linux/sched.h>
24#include <linux/clk.h>
25#include <linux/err.h>
26#include <linux/io.h>
27#include <linux/module.h>
28#include <linux/slab.h>
29
30#include <plat/clock.h>
31#include <plat/board.h>
32#include <plat/powerdomain.h>
33#include <plat/clockdomain.h>
34
35#include "prm.h"
36#include "cm.h"
37#include "pm.h"
38
39int omap2_pm_debug;
40
41#define DUMP_PRM_MOD_REG(mod, reg)    \
42	regs[reg_count].name = #mod "." #reg; \
43	regs[reg_count++].val = prm_read_mod_reg(mod, reg)
44#define DUMP_CM_MOD_REG(mod, reg)     \
45	regs[reg_count].name = #mod "." #reg; \
46	regs[reg_count++].val = cm_read_mod_reg(mod, reg)
47#define DUMP_PRM_REG(reg) \
48	regs[reg_count].name = #reg; \
49	regs[reg_count++].val = __raw_readl(reg)
50#define DUMP_CM_REG(reg) \
51	regs[reg_count].name = #reg; \
52	regs[reg_count++].val = __raw_readl(reg)
53#define DUMP_INTC_REG(reg, off) \
54	regs[reg_count].name = #reg; \
55	regs[reg_count++].val = \
56			 __raw_readl(OMAP2_L4_IO_ADDRESS(0x480fe000 + (off)))
57
58void omap2_pm_dump(int mode, int resume, unsigned int us)
59{
60	struct reg {
61		const char *name;
62		u32 val;
63	} regs[32];
64	int reg_count = 0, i;
65	const char *s1 = NULL, *s2 = NULL;
66
67	if (!resume) {
68	} else {
69		DUMP_PRM_MOD_REG(CORE_MOD, PM_WKST1);
70		if (cpu_is_omap24xx())
71			DUMP_PRM_MOD_REG(CORE_MOD, OMAP24XX_PM_WKST2);
72		DUMP_PRM_MOD_REG(WKUP_MOD, PM_WKST);
73		DUMP_PRM_MOD_REG(OCP_MOD, OMAP2_PRCM_IRQSTATUS_MPU_OFFSET);
74		DUMP_INTC_REG(INTC_PENDING_IRQ0, 0x0098);
75		DUMP_INTC_REG(INTC_PENDING_IRQ1, 0x00b8);
76		DUMP_INTC_REG(INTC_PENDING_IRQ2, 0x00d8);
77	}
78
79	switch (mode) {
80	case 0:
81		s1 = "full";
82		s2 = "retention";
83		break;
84	case 1:
85		s1 = "MPU";
86		s2 = "retention";
87		break;
88	case 2:
89		s1 = "MPU";
90		s2 = "idle";
91		break;
92	}
93
94	if (!resume)
95#ifdef CONFIG_NO_HZ
96		printk(KERN_INFO
97		       "--- Going to %s %s (next timer after %u ms)\n", s1, s2,
98		       jiffies_to_msecs(get_next_timer_interrupt(jiffies) -
99					jiffies));
100#else
101		printk(KERN_INFO "--- Going to %s %s\n", s1, s2);
102#endif
103	else
104		printk(KERN_INFO "--- Woke up (slept for %u.%03u ms)\n",
105			us / 1000, us % 1000);
106
107	for (i = 0; i < reg_count; i++)
108		printk(KERN_INFO "%-20s: 0x%08x\n", regs[i].name, regs[i].val);
109}
110
111#ifdef CONFIG_DEBUG_FS
112#include <linux/debugfs.h>
113#include <linux/seq_file.h>
114
115static void pm_dbg_regset_store(u32 *ptr);
116
117struct dentry *pm_dbg_dir;
118
119static int pm_dbg_init_done;
120
121static int __init pm_dbg_init(void);
122
123enum {
124	DEBUG_FILE_COUNTERS = 0,
125	DEBUG_FILE_TIMERS,
126};
127
128struct pm_module_def {
129	char name[8]; /* Name of the module */
130	short type; /* CM or PRM */
131	unsigned short offset;
132	int low; /* First register address on this module */
133	int high; /* Last register address on this module */
134};
135
136#define MOD_CM 0
137#define MOD_PRM 1
138
139static const struct pm_module_def *pm_dbg_reg_modules;
140static const struct pm_module_def omap3_pm_reg_modules[] = {
141	{ "IVA2", MOD_CM, OMAP3430_IVA2_MOD, 0, 0x4c },
142	{ "OCP", MOD_CM, OCP_MOD, 0, 0x10 },
143	{ "MPU", MOD_CM, MPU_MOD, 4, 0x4c },
144	{ "CORE", MOD_CM, CORE_MOD, 0, 0x4c },
145	{ "SGX", MOD_CM, OMAP3430ES2_SGX_MOD, 0, 0x4c },
146	{ "WKUP", MOD_CM, WKUP_MOD, 0, 0x40 },
147	{ "CCR", MOD_CM, PLL_MOD, 0, 0x70 },
148	{ "DSS", MOD_CM, OMAP3430_DSS_MOD, 0, 0x4c },
149	{ "CAM", MOD_CM, OMAP3430_CAM_MOD, 0, 0x4c },
150	{ "PER", MOD_CM, OMAP3430_PER_MOD, 0, 0x4c },
151	{ "EMU", MOD_CM, OMAP3430_EMU_MOD, 0x40, 0x54 },
152	{ "NEON", MOD_CM, OMAP3430_NEON_MOD, 0x20, 0x48 },
153	{ "USB", MOD_CM, OMAP3430ES2_USBHOST_MOD, 0, 0x4c },
154
155	{ "IVA2", MOD_PRM, OMAP3430_IVA2_MOD, 0x50, 0xfc },
156	{ "OCP", MOD_PRM, OCP_MOD, 4, 0x1c },
157	{ "MPU", MOD_PRM, MPU_MOD, 0x58, 0xe8 },
158	{ "CORE", MOD_PRM, CORE_MOD, 0x58, 0xf8 },
159	{ "SGX", MOD_PRM, OMAP3430ES2_SGX_MOD, 0x58, 0xe8 },
160	{ "WKUP", MOD_PRM, WKUP_MOD, 0xa0, 0xb0 },
161	{ "CCR", MOD_PRM, PLL_MOD, 0x40, 0x70 },
162	{ "DSS", MOD_PRM, OMAP3430_DSS_MOD, 0x58, 0xe8 },
163	{ "CAM", MOD_PRM, OMAP3430_CAM_MOD, 0x58, 0xe8 },
164	{ "PER", MOD_PRM, OMAP3430_PER_MOD, 0x58, 0xe8 },
165	{ "EMU", MOD_PRM, OMAP3430_EMU_MOD, 0x58, 0xe4 },
166	{ "GLBL", MOD_PRM, OMAP3430_GR_MOD, 0x20, 0xe4 },
167	{ "NEON", MOD_PRM, OMAP3430_NEON_MOD, 0x58, 0xe8 },
168	{ "USB", MOD_PRM, OMAP3430ES2_USBHOST_MOD, 0x58, 0xe8 },
169	{ "", 0, 0, 0, 0 },
170};
171
172#define PM_DBG_MAX_REG_SETS 4
173
174static void *pm_dbg_reg_set[PM_DBG_MAX_REG_SETS];
175
176static int pm_dbg_get_regset_size(void)
177{
178	static int regset_size;
179
180	if (regset_size == 0) {
181		int i = 0;
182
183		while (pm_dbg_reg_modules[i].name[0] != 0) {
184			regset_size += pm_dbg_reg_modules[i].high +
185				4 - pm_dbg_reg_modules[i].low;
186			i++;
187		}
188	}
189	return regset_size;
190}
191
192static int pm_dbg_show_regs(struct seq_file *s, void *unused)
193{
194	int i, j;
195	unsigned long val;
196	int reg_set = (int)s->private;
197	u32 *ptr;
198	void *store = NULL;
199	int regs;
200	int linefeed;
201
202	if (reg_set == 0) {
203		store = kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL);
204		ptr = store;
205		pm_dbg_regset_store(ptr);
206	} else {
207		ptr = pm_dbg_reg_set[reg_set - 1];
208	}
209
210	i = 0;
211
212	while (pm_dbg_reg_modules[i].name[0] != 0) {
213		regs = 0;
214		linefeed = 0;
215		if (pm_dbg_reg_modules[i].type == MOD_CM)
216			seq_printf(s, "MOD: CM_%s (%08x)\n",
217				pm_dbg_reg_modules[i].name,
218				(u32)(OMAP3430_CM_BASE +
219				pm_dbg_reg_modules[i].offset));
220		else
221			seq_printf(s, "MOD: PRM_%s (%08x)\n",
222				pm_dbg_reg_modules[i].name,
223				(u32)(OMAP3430_PRM_BASE +
224				pm_dbg_reg_modules[i].offset));
225
226		for (j = pm_dbg_reg_modules[i].low;
227			j <= pm_dbg_reg_modules[i].high; j += 4) {
228			val = *(ptr++);
229			if (val != 0) {
230				regs++;
231				if (linefeed) {
232					seq_printf(s, "\n");
233					linefeed = 0;
234				}
235				seq_printf(s, "  %02x => %08lx", j, val);
236				if (regs % 4 == 0)
237					linefeed = 1;
238			}
239		}
240		seq_printf(s, "\n");
241		i++;
242	}
243
244	if (store != NULL)
245		kfree(store);
246
247	return 0;
248}
249
250static void pm_dbg_regset_store(u32 *ptr)
251{
252	int i, j;
253	u32 val;
254
255	i = 0;
256
257	while (pm_dbg_reg_modules[i].name[0] != 0) {
258		for (j = pm_dbg_reg_modules[i].low;
259			j <= pm_dbg_reg_modules[i].high; j += 4) {
260			if (pm_dbg_reg_modules[i].type == MOD_CM)
261				val = cm_read_mod_reg(
262					pm_dbg_reg_modules[i].offset, j);
263			else
264				val = prm_read_mod_reg(
265					pm_dbg_reg_modules[i].offset, j);
266			*(ptr++) = val;
267		}
268		i++;
269	}
270}
271
272int pm_dbg_regset_save(int reg_set)
273{
274	if (pm_dbg_reg_set[reg_set-1] == NULL)
275		return -EINVAL;
276
277	pm_dbg_regset_store(pm_dbg_reg_set[reg_set-1]);
278
279	return 0;
280}
281
282static const char pwrdm_state_names[][PWRDM_MAX_PWRSTS] = {
283	"OFF",
284	"RET",
285	"INA",
286	"ON"
287};
288
289void pm_dbg_update_time(struct powerdomain *pwrdm, int prev)
290{
291	s64 t;
292
293	if (!pm_dbg_init_done)
294		return ;
295
296	/* Update timer for previous state */
297	t = sched_clock();
298
299	pwrdm->state_timer[prev] += t - pwrdm->timer;
300
301	pwrdm->timer = t;
302}
303
304static int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user)
305{
306	struct seq_file *s = (struct seq_file *)user;
307
308	if (strcmp(clkdm->name, "emu_clkdm") == 0 ||
309		strcmp(clkdm->name, "wkup_clkdm") == 0 ||
310		strncmp(clkdm->name, "dpll", 4) == 0)
311		return 0;
312
313	seq_printf(s, "%s->%s (%d)", clkdm->name,
314			clkdm->pwrdm.ptr->name,
315			atomic_read(&clkdm->usecount));
316	seq_printf(s, "\n");
317
318	return 0;
319}
320
321static int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user)
322{
323	struct seq_file *s = (struct seq_file *)user;
324	int i;
325
326	if (strcmp(pwrdm->name, "emu_pwrdm") == 0 ||
327		strcmp(pwrdm->name, "wkup_pwrdm") == 0 ||
328		strncmp(pwrdm->name, "dpll", 4) == 0)
329		return 0;
330
331	if (pwrdm->state != pwrdm_read_pwrst(pwrdm))
332		printk(KERN_ERR "pwrdm state mismatch(%s) %d != %d\n",
333			pwrdm->name, pwrdm->state, pwrdm_read_pwrst(pwrdm));
334
335	seq_printf(s, "%s (%s)", pwrdm->name,
336			pwrdm_state_names[pwrdm->state]);
337	for (i = 0; i < PWRDM_MAX_PWRSTS; i++)
338		seq_printf(s, ",%s:%d", pwrdm_state_names[i],
339			pwrdm->state_counter[i]);
340
341	seq_printf(s, ",RET-LOGIC-OFF:%d", pwrdm->ret_logic_off_counter);
342	for (i = 0; i < pwrdm->banks; i++)
343		seq_printf(s, ",RET-MEMBANK%d-OFF:%d", i + 1,
344				pwrdm->ret_mem_off_counter[i]);
345
346	seq_printf(s, "\n");
347
348	return 0;
349}
350
351static int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *user)
352{
353	struct seq_file *s = (struct seq_file *)user;
354	int i;
355
356	if (strcmp(pwrdm->name, "emu_pwrdm") == 0 ||
357		strcmp(pwrdm->name, "wkup_pwrdm") == 0 ||
358		strncmp(pwrdm->name, "dpll", 4) == 0)
359		return 0;
360
361	pwrdm_state_switch(pwrdm);
362
363	seq_printf(s, "%s (%s)", pwrdm->name,
364		pwrdm_state_names[pwrdm->state]);
365
366	for (i = 0; i < 4; i++)
367		seq_printf(s, ",%s:%lld", pwrdm_state_names[i],
368			pwrdm->state_timer[i]);
369
370	seq_printf(s, "\n");
371	return 0;
372}
373
374static int pm_dbg_show_counters(struct seq_file *s, void *unused)
375{
376	pwrdm_for_each(pwrdm_dbg_show_counter, s);
377	clkdm_for_each(clkdm_dbg_show_counter, s);
378
379	return 0;
380}
381
382static int pm_dbg_show_timers(struct seq_file *s, void *unused)
383{
384	pwrdm_for_each(pwrdm_dbg_show_timer, s);
385	return 0;
386}
387
388static int pm_dbg_open(struct inode *inode, struct file *file)
389{
390	switch ((int)inode->i_private) {
391	case DEBUG_FILE_COUNTERS:
392		return single_open(file, pm_dbg_show_counters,
393			&inode->i_private);
394	case DEBUG_FILE_TIMERS:
395	default:
396		return single_open(file, pm_dbg_show_timers,
397			&inode->i_private);
398	};
399}
400
401static int pm_dbg_reg_open(struct inode *inode, struct file *file)
402{
403	return single_open(file, pm_dbg_show_regs, inode->i_private);
404}
405
406static const struct file_operations debug_fops = {
407	.open           = pm_dbg_open,
408	.read           = seq_read,
409	.llseek         = seq_lseek,
410	.release        = single_release,
411};
412
413static const struct file_operations debug_reg_fops = {
414	.open           = pm_dbg_reg_open,
415	.read           = seq_read,
416	.llseek         = seq_lseek,
417	.release        = single_release,
418};
419
420int pm_dbg_regset_init(int reg_set)
421{
422	char name[2];
423
424	if (!pm_dbg_init_done)
425		pm_dbg_init();
426
427	if (reg_set < 1 || reg_set > PM_DBG_MAX_REG_SETS ||
428		pm_dbg_reg_set[reg_set-1] != NULL)
429		return -EINVAL;
430
431	pm_dbg_reg_set[reg_set-1] =
432		kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL);
433
434	if (pm_dbg_reg_set[reg_set-1] == NULL)
435		return -ENOMEM;
436
437	if (pm_dbg_dir != NULL) {
438		sprintf(name, "%d", reg_set);
439
440		(void) debugfs_create_file(name, S_IRUGO,
441			pm_dbg_dir, (void *)reg_set, &debug_reg_fops);
442	}
443
444	return 0;
445}
446
447static int pwrdm_suspend_get(void *data, u64 *val)
448{
449	int ret;
450	ret = omap3_pm_get_suspend_state((struct powerdomain *)data);
451	*val = ret;
452
453	if (ret >= 0)
454		return 0;
455	return *val;
456}
457
458static int pwrdm_suspend_set(void *data, u64 val)
459{
460	return omap3_pm_set_suspend_state((struct powerdomain *)data, (int)val);
461}
462
463DEFINE_SIMPLE_ATTRIBUTE(pwrdm_suspend_fops, pwrdm_suspend_get,
464			pwrdm_suspend_set, "%llu\n");
465
466static int __init pwrdms_setup(struct powerdomain *pwrdm, void *dir)
467{
468	int i;
469	s64 t;
470	struct dentry *d;
471
472	t = sched_clock();
473
474	for (i = 0; i < 4; i++)
475		pwrdm->state_timer[i] = 0;
476
477	pwrdm->timer = t;
478
479	if (strncmp(pwrdm->name, "dpll", 4) == 0)
480		return 0;
481
482	d = debugfs_create_dir(pwrdm->name, (struct dentry *)dir);
483
484	(void) debugfs_create_file("suspend", S_IRUGO|S_IWUSR, d,
485			(void *)pwrdm, &pwrdm_suspend_fops);
486
487	return 0;
488}
489
490static int option_get(void *data, u64 *val)
491{
492	u32 *option = data;
493
494	*val = *option;
495
496	return 0;
497}
498
499static int option_set(void *data, u64 val)
500{
501	u32 *option = data;
502
503	if (option == &wakeup_timer_milliseconds && val >= 1000)
504		return -EINVAL;
505
506	*option = val;
507
508	if (option == &enable_off_mode)
509		omap3_pm_off_mode_enable(val);
510
511	return 0;
512}
513
514DEFINE_SIMPLE_ATTRIBUTE(pm_dbg_option_fops, option_get, option_set, "%llu\n");
515
516static int __init pm_dbg_init(void)
517{
518	int i;
519	struct dentry *d;
520	char name[2];
521
522	if (pm_dbg_init_done)
523		return 0;
524
525	if (cpu_is_omap34xx())
526		pm_dbg_reg_modules = omap3_pm_reg_modules;
527	else {
528		printk(KERN_ERR "%s: only OMAP3 supported\n", __func__);
529		return -ENODEV;
530	}
531
532	d = debugfs_create_dir("pm_debug", NULL);
533	if (IS_ERR(d))
534		return PTR_ERR(d);
535
536	(void) debugfs_create_file("count", S_IRUGO,
537		d, (void *)DEBUG_FILE_COUNTERS, &debug_fops);
538	(void) debugfs_create_file("time", S_IRUGO,
539		d, (void *)DEBUG_FILE_TIMERS, &debug_fops);
540
541	pwrdm_for_each(pwrdms_setup, (void *)d);
542
543	pm_dbg_dir = debugfs_create_dir("registers", d);
544	if (IS_ERR(pm_dbg_dir))
545		return PTR_ERR(pm_dbg_dir);
546
547	(void) debugfs_create_file("current", S_IRUGO,
548		pm_dbg_dir, (void *)0, &debug_reg_fops);
549
550	for (i = 0; i < PM_DBG_MAX_REG_SETS; i++)
551		if (pm_dbg_reg_set[i] != NULL) {
552			sprintf(name, "%d", i+1);
553			(void) debugfs_create_file(name, S_IRUGO,
554				pm_dbg_dir, (void *)(i+1), &debug_reg_fops);
555
556		}
557
558	(void) debugfs_create_file("enable_off_mode", S_IRUGO | S_IWUGO, d,
559				   &enable_off_mode, &pm_dbg_option_fops);
560	(void) debugfs_create_file("sleep_while_idle", S_IRUGO | S_IWUGO, d,
561				   &sleep_while_idle, &pm_dbg_option_fops);
562	(void) debugfs_create_file("wakeup_timer_seconds", S_IRUGO | S_IWUGO, d,
563				   &wakeup_timer_seconds, &pm_dbg_option_fops);
564	pm_dbg_init_done = 1;
565
566	return 0;
567}
568arch_initcall(pm_dbg_init);
569
570#endif
571