diff options
author | Vincent Guittot <vincent.guittot@stericsson.com> | 2010-12-03 12:18:39 -0500 |
---|---|---|
committer | Linus Walleij <linus.walleij@stericsson.com> | 2010-12-20 07:32:45 -0500 |
commit | 763eef8b5b64dbbfc0f6273af9a57024069785a9 (patch) | |
tree | 3e24b74aad069a1d3248ac78d53da3bd8944a233 /arch | |
parent | abda3a24a99998279fe890ea8a789ebe4d605d78 (diff) |
ux500: add debugfs support for powerdebug
Signed-off-by: Vincent Guittot <vincent.guittot@stericsson.com>
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-ux500/clock.c | 195 | ||||
-rw-r--r-- | arch/arm/mach-ux500/clock.h | 4 |
2 files changed, 198 insertions, 1 deletions
diff --git a/arch/arm/mach-ux500/clock.c b/arch/arm/mach-ux500/clock.c index 00e9ab304ca7..912d1cc18c57 100644 --- a/arch/arm/mach-ux500/clock.c +++ b/arch/arm/mach-ux500/clock.c | |||
@@ -20,6 +20,12 @@ | |||
20 | #include <mach/hardware.h> | 20 | #include <mach/hardware.h> |
21 | #include "clock.h" | 21 | #include "clock.h" |
22 | 22 | ||
23 | #ifdef CONFIG_DEBUG_FS | ||
24 | #include <linux/debugfs.h> | ||
25 | #include <linux/uaccess.h> /* for copy_from_user */ | ||
26 | static LIST_HEAD(clk_list); | ||
27 | #endif | ||
28 | |||
23 | #define PRCC_PCKEN 0x00 | 29 | #define PRCC_PCKEN 0x00 |
24 | #define PRCC_PCKDIS 0x04 | 30 | #define PRCC_PCKDIS 0x04 |
25 | #define PRCC_KCKEN 0x08 | 31 | #define PRCC_KCKEN 0x08 |
@@ -286,6 +292,7 @@ static struct clkops clk_prcc_ops = { | |||
286 | }; | 292 | }; |
287 | 293 | ||
288 | static struct clk clk_32khz = { | 294 | static struct clk clk_32khz = { |
295 | .name = "clk_32khz", | ||
289 | .rate = 32000, | 296 | .rate = 32000, |
290 | }; | 297 | }; |
291 | 298 | ||
@@ -422,7 +429,9 @@ static DEFINE_PRCC_CLK_CUSTOM(7, mtu0_ed, 2, -1, NULL, clk_mtu_get_rate, 0); | |||
422 | static DEFINE_PRCC_CLK(7, wdg_ed, 1, -1, NULL); | 429 | static DEFINE_PRCC_CLK(7, wdg_ed, 1, -1, NULL); |
423 | static DEFINE_PRCC_CLK(7, cfgreg_ed, 0, -1, NULL); | 430 | static DEFINE_PRCC_CLK(7, cfgreg_ed, 0, -1, NULL); |
424 | 431 | ||
425 | static struct clk clk_dummy_apb_pclk; | 432 | static struct clk clk_dummy_apb_pclk = { |
433 | .name = "apb_pclk", | ||
434 | }; | ||
426 | 435 | ||
427 | static struct clk_lookup u8500_common_clks[] = { | 436 | static struct clk_lookup u8500_common_clks[] = { |
428 | CLK(dummy_apb_pclk, NULL, "apb_pclk"), | 437 | CLK(dummy_apb_pclk, NULL, "apb_pclk"), |
@@ -568,6 +577,183 @@ static struct clk_lookup u8500_v1_clks[] = { | |||
568 | CLK(uiccclk, "uicc", NULL), | 577 | CLK(uiccclk, "uicc", NULL), |
569 | }; | 578 | }; |
570 | 579 | ||
580 | #ifdef CONFIG_DEBUG_FS | ||
581 | /* | ||
582 | * debugfs support to trace clock tree hierarchy and attributes with | ||
583 | * powerdebug | ||
584 | */ | ||
585 | static struct dentry *clk_debugfs_root; | ||
586 | |||
587 | void __init clk_debugfs_add_table(struct clk_lookup *cl, size_t num) | ||
588 | { | ||
589 | while (num--) { | ||
590 | /* Check that the clock has not been already registered */ | ||
591 | if (!(cl->clk->list.prev != cl->clk->list.next)) | ||
592 | list_add_tail(&cl->clk->list, &clk_list); | ||
593 | |||
594 | cl++; | ||
595 | } | ||
596 | } | ||
597 | |||
598 | static ssize_t usecount_dbg_read(struct file *file, char __user *buf, | ||
599 | size_t size, loff_t *off) | ||
600 | { | ||
601 | struct clk *clk = file->f_dentry->d_inode->i_private; | ||
602 | char cusecount[128]; | ||
603 | unsigned int len; | ||
604 | |||
605 | len = sprintf(cusecount, "%u\n", clk->enabled); | ||
606 | return simple_read_from_buffer(buf, size, off, cusecount, len); | ||
607 | } | ||
608 | |||
609 | static ssize_t rate_dbg_read(struct file *file, char __user *buf, | ||
610 | size_t size, loff_t *off) | ||
611 | { | ||
612 | struct clk *clk = file->f_dentry->d_inode->i_private; | ||
613 | char crate[128]; | ||
614 | unsigned int rate; | ||
615 | unsigned int len; | ||
616 | |||
617 | rate = clk_get_rate(clk); | ||
618 | len = sprintf(crate, "%u\n", rate); | ||
619 | return simple_read_from_buffer(buf, size, off, crate, len); | ||
620 | } | ||
621 | |||
622 | static const struct file_operations usecount_fops = { | ||
623 | .read = usecount_dbg_read, | ||
624 | }; | ||
625 | |||
626 | static const struct file_operations set_rate_fops = { | ||
627 | .read = rate_dbg_read, | ||
628 | }; | ||
629 | |||
630 | static struct dentry *clk_debugfs_register_dir(struct clk *c, | ||
631 | struct dentry *p_dentry) | ||
632 | { | ||
633 | struct dentry *d, *clk_d, *child, *child_tmp; | ||
634 | char s[255]; | ||
635 | char *p = s; | ||
636 | |||
637 | if (c->name == NULL) | ||
638 | p += sprintf(p, "BUG"); | ||
639 | else | ||
640 | p += sprintf(p, "%s", c->name); | ||
641 | |||
642 | clk_d = debugfs_create_dir(s, p_dentry); | ||
643 | if (!clk_d) | ||
644 | return NULL; | ||
645 | |||
646 | d = debugfs_create_file("usecount", S_IRUGO, | ||
647 | clk_d, c, &usecount_fops); | ||
648 | if (!d) | ||
649 | goto err_out; | ||
650 | d = debugfs_create_file("rate", S_IRUGO, | ||
651 | clk_d, c, &set_rate_fops); | ||
652 | if (!d) | ||
653 | goto err_out; | ||
654 | /* | ||
655 | * TODO : not currently available in ux500 | ||
656 | * d = debugfs_create_x32("flags", S_IRUGO, clk_d, (u32 *)&c->flags); | ||
657 | * if (!d) | ||
658 | * goto err_out; | ||
659 | */ | ||
660 | |||
661 | return clk_d; | ||
662 | |||
663 | err_out: | ||
664 | d = clk_d; | ||
665 | list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child) | ||
666 | debugfs_remove(child); | ||
667 | debugfs_remove(clk_d); | ||
668 | return NULL; | ||
669 | } | ||
670 | |||
671 | static void clk_debugfs_remove_dir(struct dentry *cdentry) | ||
672 | { | ||
673 | struct dentry *d, *child, *child_tmp; | ||
674 | |||
675 | d = cdentry; | ||
676 | list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child) | ||
677 | debugfs_remove(child); | ||
678 | debugfs_remove(cdentry); | ||
679 | return ; | ||
680 | } | ||
681 | |||
682 | static int clk_debugfs_register_one(struct clk *c) | ||
683 | { | ||
684 | struct clk *pa = c->parent_periph; | ||
685 | struct clk *bpa = c->parent_cluster; | ||
686 | |||
687 | if (!(bpa && !pa)) { | ||
688 | c->dent = clk_debugfs_register_dir(c, | ||
689 | pa ? pa->dent : clk_debugfs_root); | ||
690 | if (!c->dent) | ||
691 | return -ENOMEM; | ||
692 | } | ||
693 | |||
694 | if (bpa) { | ||
695 | c->dent_bus = clk_debugfs_register_dir(c, | ||
696 | bpa->dent_bus ? bpa->dent_bus : bpa->dent); | ||
697 | if ((!c->dent_bus) && (c->dent)) { | ||
698 | clk_debugfs_remove_dir(c->dent); | ||
699 | c->dent = NULL; | ||
700 | return -ENOMEM; | ||
701 | } | ||
702 | } | ||
703 | return 0; | ||
704 | } | ||
705 | |||
706 | static int clk_debugfs_register(struct clk *c) | ||
707 | { | ||
708 | int err; | ||
709 | struct clk *pa = c->parent_periph; | ||
710 | struct clk *bpa = c->parent_cluster; | ||
711 | |||
712 | if (pa && (!pa->dent && !pa->dent_bus)) { | ||
713 | err = clk_debugfs_register(pa); | ||
714 | if (err) | ||
715 | return err; | ||
716 | } | ||
717 | |||
718 | if (bpa && (!bpa->dent && !bpa->dent_bus)) { | ||
719 | err = clk_debugfs_register(bpa); | ||
720 | if (err) | ||
721 | return err; | ||
722 | } | ||
723 | |||
724 | if ((!c->dent) && (!c->dent_bus)) { | ||
725 | err = clk_debugfs_register_one(c); | ||
726 | if (err) | ||
727 | return err; | ||
728 | } | ||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | static int __init clk_debugfs_init(void) | ||
733 | { | ||
734 | struct clk *c; | ||
735 | struct dentry *d; | ||
736 | int err; | ||
737 | |||
738 | d = debugfs_create_dir("clock", NULL); | ||
739 | if (!d) | ||
740 | return -ENOMEM; | ||
741 | clk_debugfs_root = d; | ||
742 | |||
743 | list_for_each_entry(c, &clk_list, list) { | ||
744 | err = clk_debugfs_register(c); | ||
745 | if (err) | ||
746 | goto err_out; | ||
747 | } | ||
748 | return 0; | ||
749 | err_out: | ||
750 | debugfs_remove_recursive(clk_debugfs_root); | ||
751 | return err; | ||
752 | } | ||
753 | |||
754 | late_initcall(clk_debugfs_init); | ||
755 | #endif /* defined(CONFIG_DEBUG_FS) */ | ||
756 | |||
571 | int __init clk_init(void) | 757 | int __init clk_init(void) |
572 | { | 758 | { |
573 | if (cpu_is_u8500ed()) { | 759 | if (cpu_is_u8500ed()) { |
@@ -588,5 +774,12 @@ int __init clk_init(void) | |||
588 | else | 774 | else |
589 | clkdev_add_table(u8500_v1_clks, ARRAY_SIZE(u8500_v1_clks)); | 775 | clkdev_add_table(u8500_v1_clks, ARRAY_SIZE(u8500_v1_clks)); |
590 | 776 | ||
777 | #ifdef CONFIG_DEBUG_FS | ||
778 | clk_debugfs_add_table(u8500_common_clks, ARRAY_SIZE(u8500_common_clks)); | ||
779 | if (cpu_is_u8500ed()) | ||
780 | clk_debugfs_add_table(u8500_ed_clks, ARRAY_SIZE(u8500_ed_clks)); | ||
781 | else | ||
782 | clk_debugfs_add_table(u8500_v1_clks, ARRAY_SIZE(u8500_v1_clks)); | ||
783 | #endif | ||
591 | return 0; | 784 | return 0; |
592 | } | 785 | } |
diff --git a/arch/arm/mach-ux500/clock.h b/arch/arm/mach-ux500/clock.h index a05802501527..074490705229 100644 --- a/arch/arm/mach-ux500/clock.h +++ b/arch/arm/mach-ux500/clock.h | |||
@@ -90,6 +90,10 @@ struct clk { | |||
90 | 90 | ||
91 | struct clk *parent_cluster; | 91 | struct clk *parent_cluster; |
92 | struct clk *parent_periph; | 92 | struct clk *parent_periph; |
93 | #if defined(CONFIG_DEBUG_FS) | ||
94 | struct dentry *dent; /* For visible tree hierarchy */ | ||
95 | struct dentry *dent_bus; /* For visible tree hierarchy */ | ||
96 | #endif | ||
93 | }; | 97 | }; |
94 | 98 | ||
95 | #define DEFINE_PRCMU_CLK(_name, _cg_off, _cg_bit, _reg) \ | 99 | #define DEFINE_PRCMU_CLK(_name, _cg_off, _cg_bit, _reg) \ |