diff options
| -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) \ |
