pm-debug.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  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. #include <linux/kernel.h>
  22. #include <linux/sched.h>
  23. #include <linux/clk.h>
  24. #include <linux/err.h>
  25. #include <linux/io.h>
  26. #include <linux/module.h>
  27. #include <linux/slab.h>
  28. #include <plat/clock.h>
  29. #include <plat/board.h>
  30. #include "powerdomain.h"
  31. #include "clockdomain.h"
  32. #include <plat/dmtimer.h>
  33. #include <plat/omap-pm.h>
  34. #include "cm2xxx_3xxx.h"
  35. #include "prm2xxx_3xxx.h"
  36. #include "pm.h"
  37. u32 enable_off_mode;
  38. u32 wakeup_timer_seconds;
  39. u32 wakeup_timer_milliseconds;
  40. void omap2_pm_wakeup_on_timer(u32 seconds, u32 milliseconds)
  41. {
  42. u32 tick_rate, cycles;
  43. if (!seconds && !milliseconds)
  44. return;
  45. tick_rate = clk_get_rate(omap_dm_timer_get_fclk(gptimer_wakeup));
  46. cycles = tick_rate * seconds + tick_rate * milliseconds / 1000;
  47. omap_dm_timer_stop(gptimer_wakeup);
  48. omap_dm_timer_set_load_start(gptimer_wakeup, 0, 0xffffffff - cycles);
  49. pr_info("PM: Resume timer in %u.%03u secs"
  50. " (%d ticks at %d ticks/sec.)\n",
  51. seconds, milliseconds, cycles, tick_rate);
  52. }
  53. #ifdef CONFIG_DEBUG_FS
  54. #include <linux/debugfs.h>
  55. #include <linux/seq_file.h>
  56. static void pm_dbg_regset_store(u32 *ptr);
  57. static struct dentry *pm_dbg_dir;
  58. static int pm_dbg_init_done;
  59. static int pm_dbg_init(void);
  60. enum {
  61. DEBUG_FILE_COUNTERS = 0,
  62. DEBUG_FILE_TIMERS,
  63. };
  64. struct pm_module_def {
  65. char name[8]; /* Name of the module */
  66. short type; /* CM or PRM */
  67. unsigned short offset;
  68. int low; /* First register address on this module */
  69. int high; /* Last register address on this module */
  70. };
  71. #define MOD_CM 0
  72. #define MOD_PRM 1
  73. static const struct pm_module_def *pm_dbg_reg_modules;
  74. static const struct pm_module_def omap3_pm_reg_modules[] = {
  75. { "IVA2", MOD_CM, OMAP3430_IVA2_MOD, 0, 0x4c },
  76. { "OCP", MOD_CM, OCP_MOD, 0, 0x10 },
  77. { "MPU", MOD_CM, MPU_MOD, 4, 0x4c },
  78. { "CORE", MOD_CM, CORE_MOD, 0, 0x4c },
  79. { "SGX", MOD_CM, OMAP3430ES2_SGX_MOD, 0, 0x4c },
  80. { "WKUP", MOD_CM, WKUP_MOD, 0, 0x40 },
  81. { "CCR", MOD_CM, PLL_MOD, 0, 0x70 },
  82. { "DSS", MOD_CM, OMAP3430_DSS_MOD, 0, 0x4c },
  83. { "CAM", MOD_CM, OMAP3430_CAM_MOD, 0, 0x4c },
  84. { "PER", MOD_CM, OMAP3430_PER_MOD, 0, 0x4c },
  85. { "EMU", MOD_CM, OMAP3430_EMU_MOD, 0x40, 0x54 },
  86. { "NEON", MOD_CM, OMAP3430_NEON_MOD, 0x20, 0x48 },
  87. { "USB", MOD_CM, OMAP3430ES2_USBHOST_MOD, 0, 0x4c },
  88. { "IVA2", MOD_PRM, OMAP3430_IVA2_MOD, 0x50, 0xfc },
  89. { "OCP", MOD_PRM, OCP_MOD, 4, 0x1c },
  90. { "MPU", MOD_PRM, MPU_MOD, 0x58, 0xe8 },
  91. { "CORE", MOD_PRM, CORE_MOD, 0x58, 0xf8 },
  92. { "SGX", MOD_PRM, OMAP3430ES2_SGX_MOD, 0x58, 0xe8 },
  93. { "WKUP", MOD_PRM, WKUP_MOD, 0xa0, 0xb0 },
  94. { "CCR", MOD_PRM, PLL_MOD, 0x40, 0x70 },
  95. { "DSS", MOD_PRM, OMAP3430_DSS_MOD, 0x58, 0xe8 },
  96. { "CAM", MOD_PRM, OMAP3430_CAM_MOD, 0x58, 0xe8 },
  97. { "PER", MOD_PRM, OMAP3430_PER_MOD, 0x58, 0xe8 },
  98. { "EMU", MOD_PRM, OMAP3430_EMU_MOD, 0x58, 0xe4 },
  99. { "GLBL", MOD_PRM, OMAP3430_GR_MOD, 0x20, 0xe4 },
  100. { "NEON", MOD_PRM, OMAP3430_NEON_MOD, 0x58, 0xe8 },
  101. { "USB", MOD_PRM, OMAP3430ES2_USBHOST_MOD, 0x58, 0xe8 },
  102. { "", 0, 0, 0, 0 },
  103. };
  104. #define PM_DBG_MAX_REG_SETS 4
  105. static void *pm_dbg_reg_set[PM_DBG_MAX_REG_SETS];
  106. static int pm_dbg_get_regset_size(void)
  107. {
  108. static int regset_size;
  109. if (regset_size == 0) {
  110. int i = 0;
  111. while (pm_dbg_reg_modules[i].name[0] != 0) {
  112. regset_size += pm_dbg_reg_modules[i].high +
  113. 4 - pm_dbg_reg_modules[i].low;
  114. i++;
  115. }
  116. }
  117. return regset_size;
  118. }
  119. static int pm_dbg_show_regs(struct seq_file *s, void *unused)
  120. {
  121. int i, j;
  122. unsigned long val;
  123. int reg_set = (int)s->private;
  124. u32 *ptr;
  125. void *store = NULL;
  126. int regs;
  127. int linefeed;
  128. if (reg_set == 0) {
  129. store = kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL);
  130. ptr = store;
  131. pm_dbg_regset_store(ptr);
  132. } else {
  133. ptr = pm_dbg_reg_set[reg_set - 1];
  134. }
  135. i = 0;
  136. while (pm_dbg_reg_modules[i].name[0] != 0) {
  137. regs = 0;
  138. linefeed = 0;
  139. if (pm_dbg_reg_modules[i].type == MOD_CM)
  140. seq_printf(s, "MOD: CM_%s (%08x)\n",
  141. pm_dbg_reg_modules[i].name,
  142. (u32)(OMAP3430_CM_BASE +
  143. pm_dbg_reg_modules[i].offset));
  144. else
  145. seq_printf(s, "MOD: PRM_%s (%08x)\n",
  146. pm_dbg_reg_modules[i].name,
  147. (u32)(OMAP3430_PRM_BASE +
  148. pm_dbg_reg_modules[i].offset));
  149. for (j = pm_dbg_reg_modules[i].low;
  150. j <= pm_dbg_reg_modules[i].high; j += 4) {
  151. val = *(ptr++);
  152. if (val != 0) {
  153. regs++;
  154. if (linefeed) {
  155. seq_printf(s, "\n");
  156. linefeed = 0;
  157. }
  158. seq_printf(s, " %02x => %08lx", j, val);
  159. if (regs % 4 == 0)
  160. linefeed = 1;
  161. }
  162. }
  163. seq_printf(s, "\n");
  164. i++;
  165. }
  166. if (store != NULL)
  167. kfree(store);
  168. return 0;
  169. }
  170. static void pm_dbg_regset_store(u32 *ptr)
  171. {
  172. int i, j;
  173. u32 val;
  174. i = 0;
  175. while (pm_dbg_reg_modules[i].name[0] != 0) {
  176. for (j = pm_dbg_reg_modules[i].low;
  177. j <= pm_dbg_reg_modules[i].high; j += 4) {
  178. if (pm_dbg_reg_modules[i].type == MOD_CM)
  179. val = omap2_cm_read_mod_reg(
  180. pm_dbg_reg_modules[i].offset, j);
  181. else
  182. val = omap2_prm_read_mod_reg(
  183. pm_dbg_reg_modules[i].offset, j);
  184. *(ptr++) = val;
  185. }
  186. i++;
  187. }
  188. }
  189. int pm_dbg_regset_save(int reg_set)
  190. {
  191. if (pm_dbg_reg_set[reg_set-1] == NULL)
  192. return -EINVAL;
  193. pm_dbg_regset_store(pm_dbg_reg_set[reg_set-1]);
  194. return 0;
  195. }
  196. static const char pwrdm_state_names[][PWRDM_MAX_PWRSTS] = {
  197. "OFF",
  198. "RET",
  199. "INA",
  200. "ON"
  201. };
  202. void pm_dbg_update_time(struct powerdomain *pwrdm, int prev)
  203. {
  204. s64 t;
  205. if (!pm_dbg_init_done)
  206. return ;
  207. /* Update timer for previous state */
  208. t = sched_clock();
  209. pwrdm->state_timer[prev] += t - pwrdm->timer;
  210. pwrdm->timer = t;
  211. }
  212. static int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user)
  213. {
  214. struct seq_file *s = (struct seq_file *)user;
  215. if (strcmp(clkdm->name, "emu_clkdm") == 0 ||
  216. strcmp(clkdm->name, "wkup_clkdm") == 0 ||
  217. strncmp(clkdm->name, "dpll", 4) == 0)
  218. return 0;
  219. seq_printf(s, "%s->%s (%d)", clkdm->name,
  220. clkdm->pwrdm.ptr->name,
  221. atomic_read(&clkdm->usecount));
  222. seq_printf(s, "\n");
  223. return 0;
  224. }
  225. static int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user)
  226. {
  227. struct seq_file *s = (struct seq_file *)user;
  228. int i;
  229. if (strcmp(pwrdm->name, "emu_pwrdm") == 0 ||
  230. strcmp(pwrdm->name, "wkup_pwrdm") == 0 ||
  231. strncmp(pwrdm->name, "dpll", 4) == 0)
  232. return 0;
  233. if (pwrdm->state != pwrdm_read_pwrst(pwrdm))
  234. printk(KERN_ERR "pwrdm state mismatch(%s) %d != %d\n",
  235. pwrdm->name, pwrdm->state, pwrdm_read_pwrst(pwrdm));
  236. seq_printf(s, "%s (%s)", pwrdm->name,
  237. pwrdm_state_names[pwrdm->state]);
  238. for (i = 0; i < PWRDM_MAX_PWRSTS; i++)
  239. seq_printf(s, ",%s:%d", pwrdm_state_names[i],
  240. pwrdm->state_counter[i]);
  241. seq_printf(s, ",RET-LOGIC-OFF:%d", pwrdm->ret_logic_off_counter);
  242. for (i = 0; i < pwrdm->banks; i++)
  243. seq_printf(s, ",RET-MEMBANK%d-OFF:%d", i + 1,
  244. pwrdm->ret_mem_off_counter[i]);
  245. seq_printf(s, "\n");
  246. return 0;
  247. }
  248. static int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *user)
  249. {
  250. struct seq_file *s = (struct seq_file *)user;
  251. int i;
  252. if (strcmp(pwrdm->name, "emu_pwrdm") == 0 ||
  253. strcmp(pwrdm->name, "wkup_pwrdm") == 0 ||
  254. strncmp(pwrdm->name, "dpll", 4) == 0)
  255. return 0;
  256. pwrdm_state_switch(pwrdm);
  257. seq_printf(s, "%s (%s)", pwrdm->name,
  258. pwrdm_state_names[pwrdm->state]);
  259. for (i = 0; i < 4; i++)
  260. seq_printf(s, ",%s:%lld", pwrdm_state_names[i],
  261. pwrdm->state_timer[i]);
  262. seq_printf(s, "\n");
  263. return 0;
  264. }
  265. static int pm_dbg_show_counters(struct seq_file *s, void *unused)
  266. {
  267. pwrdm_for_each(pwrdm_dbg_show_counter, s);
  268. clkdm_for_each(clkdm_dbg_show_counter, s);
  269. return 0;
  270. }
  271. static int pm_dbg_show_timers(struct seq_file *s, void *unused)
  272. {
  273. pwrdm_for_each(pwrdm_dbg_show_timer, s);
  274. return 0;
  275. }
  276. static int pm_dbg_open(struct inode *inode, struct file *file)
  277. {
  278. switch ((int)inode->i_private) {
  279. case DEBUG_FILE_COUNTERS:
  280. return single_open(file, pm_dbg_show_counters,
  281. &inode->i_private);
  282. case DEBUG_FILE_TIMERS:
  283. default:
  284. return single_open(file, pm_dbg_show_timers,
  285. &inode->i_private);
  286. };
  287. }
  288. static int pm_dbg_reg_open(struct inode *inode, struct file *file)
  289. {
  290. return single_open(file, pm_dbg_show_regs, inode->i_private);
  291. }
  292. static const struct file_operations debug_fops = {
  293. .open = pm_dbg_open,
  294. .read = seq_read,
  295. .llseek = seq_lseek,
  296. .release = single_release,
  297. };
  298. static const struct file_operations debug_reg_fops = {
  299. .open = pm_dbg_reg_open,
  300. .read = seq_read,
  301. .llseek = seq_lseek,
  302. .release = single_release,
  303. };
  304. int pm_dbg_regset_init(int reg_set)
  305. {
  306. char name[2];
  307. if (!pm_dbg_init_done)
  308. pm_dbg_init();
  309. if (reg_set < 1 || reg_set > PM_DBG_MAX_REG_SETS ||
  310. pm_dbg_reg_set[reg_set-1] != NULL)
  311. return -EINVAL;
  312. pm_dbg_reg_set[reg_set-1] =
  313. kmalloc(pm_dbg_get_regset_size(), GFP_KERNEL);
  314. if (pm_dbg_reg_set[reg_set-1] == NULL)
  315. return -ENOMEM;
  316. if (pm_dbg_dir != NULL) {
  317. sprintf(name, "%d", reg_set);
  318. (void) debugfs_create_file(name, S_IRUGO,
  319. pm_dbg_dir, (void *)reg_set, &debug_reg_fops);
  320. }
  321. return 0;
  322. }
  323. static int pwrdm_suspend_get(void *data, u64 *val)
  324. {
  325. int ret = -EINVAL;
  326. if (cpu_is_omap34xx())
  327. ret = omap3_pm_get_suspend_state((struct powerdomain *)data);
  328. *val = ret;
  329. if (ret >= 0)
  330. return 0;
  331. return *val;
  332. }
  333. static int pwrdm_suspend_set(void *data, u64 val)
  334. {
  335. if (cpu_is_omap34xx())
  336. return omap3_pm_set_suspend_state(
  337. (struct powerdomain *)data, (int)val);
  338. return -EINVAL;
  339. }
  340. DEFINE_SIMPLE_ATTRIBUTE(pwrdm_suspend_fops, pwrdm_suspend_get,
  341. pwrdm_suspend_set, "%llu\n");
  342. static int __init pwrdms_setup(struct powerdomain *pwrdm, void *dir)
  343. {
  344. int i;
  345. s64 t;
  346. struct dentry *d;
  347. t = sched_clock();
  348. for (i = 0; i < 4; i++)
  349. pwrdm->state_timer[i] = 0;
  350. pwrdm->timer = t;
  351. if (strncmp(pwrdm->name, "dpll", 4) == 0)
  352. return 0;
  353. d = debugfs_create_dir(pwrdm->name, (struct dentry *)dir);
  354. (void) debugfs_create_file("suspend", S_IRUGO|S_IWUSR, d,
  355. (void *)pwrdm, &pwrdm_suspend_fops);
  356. return 0;
  357. }
  358. static int option_get(void *data, u64 *val)
  359. {
  360. u32 *option = data;
  361. *val = *option;
  362. return 0;
  363. }
  364. static int option_set(void *data, u64 val)
  365. {
  366. u32 *option = data;
  367. if (option == &wakeup_timer_milliseconds && val >= 1000)
  368. return -EINVAL;
  369. *option = val;
  370. if (option == &enable_off_mode) {
  371. if (val)
  372. omap_pm_enable_off_mode();
  373. else
  374. omap_pm_disable_off_mode();
  375. if (cpu_is_omap34xx())
  376. omap3_pm_off_mode_enable(val);
  377. }
  378. return 0;
  379. }
  380. DEFINE_SIMPLE_ATTRIBUTE(pm_dbg_option_fops, option_get, option_set, "%llu\n");
  381. static int pm_dbg_init(void)
  382. {
  383. int i;
  384. struct dentry *d;
  385. char name[2];
  386. if (pm_dbg_init_done)
  387. return 0;
  388. if (cpu_is_omap34xx())
  389. pm_dbg_reg_modules = omap3_pm_reg_modules;
  390. else {
  391. printk(KERN_ERR "%s: only OMAP3 supported\n", __func__);
  392. return -ENODEV;
  393. }
  394. d = debugfs_create_dir("pm_debug", NULL);
  395. if (IS_ERR(d))
  396. return PTR_ERR(d);
  397. (void) debugfs_create_file("count", S_IRUGO,
  398. d, (void *)DEBUG_FILE_COUNTERS, &debug_fops);
  399. (void) debugfs_create_file("time", S_IRUGO,
  400. d, (void *)DEBUG_FILE_TIMERS, &debug_fops);
  401. pwrdm_for_each(pwrdms_setup, (void *)d);
  402. pm_dbg_dir = debugfs_create_dir("registers", d);
  403. if (IS_ERR(pm_dbg_dir))
  404. return PTR_ERR(pm_dbg_dir);
  405. (void) debugfs_create_file("current", S_IRUGO,
  406. pm_dbg_dir, (void *)0, &debug_reg_fops);
  407. for (i = 0; i < PM_DBG_MAX_REG_SETS; i++)
  408. if (pm_dbg_reg_set[i] != NULL) {
  409. sprintf(name, "%d", i+1);
  410. (void) debugfs_create_file(name, S_IRUGO,
  411. pm_dbg_dir, (void *)(i+1), &debug_reg_fops);
  412. }
  413. (void) debugfs_create_file("enable_off_mode", S_IRUGO | S_IWUSR, d,
  414. &enable_off_mode, &pm_dbg_option_fops);
  415. (void) debugfs_create_file("wakeup_timer_seconds", S_IRUGO | S_IWUSR, d,
  416. &wakeup_timer_seconds, &pm_dbg_option_fops);
  417. (void) debugfs_create_file("wakeup_timer_milliseconds",
  418. S_IRUGO | S_IWUSR, d, &wakeup_timer_milliseconds,
  419. &pm_dbg_option_fops);
  420. pm_dbg_init_done = 1;
  421. return 0;
  422. }
  423. arch_initcall(pm_dbg_init);
  424. #endif