diff options
author | Rajendra Nayak <rnayak@ti.com> | 2011-07-10 07:56:55 -0400 |
---|---|---|
committer | Paul Walmsley <paul@pwsan.com> | 2011-07-10 07:56:55 -0400 |
commit | 555e74ea08bfc04a0136f976cbaa200addf1ba87 (patch) | |
tree | afac050a2669fcf1e47d764785f9007c568b9e15 | |
parent | b86cfb52a145d8ddad66b98c39c6764f3883cd5a (diff) |
OMAP2+: clockdomain: Add per clkdm lock to prevent concurrent state programming
Since the clkdm state programming is now done from within the hwmod
framework (which uses a per-hwmod lock) instead of the being done
from the clock framework (which used a global lock), there is now a
need to have per-clkdm locking to prevent races between different
hwmods/modules belonging to the same clock domain concurrently
programming the clkdm state.
Signed-off-by: Rajendra Nayak <rnayak@ti.com>
Signed-off-by: Benoit Cousson <b-cousson@ti.com>
Cc: Paul Walmsley <paul@pwsan.com>
Signed-off-by: Paul Walmsley <paul@pwsan.com>
-rw-r--r-- | arch/arm/mach-omap2/clockdomain.c | 47 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clockdomain.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clockdomain2xxx_3xxx.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clockdomain44xx.c | 13 |
4 files changed, 50 insertions, 18 deletions
diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c index 239b558853f5..ab7db083f97f 100644 --- a/arch/arm/mach-omap2/clockdomain.c +++ b/arch/arm/mach-omap2/clockdomain.c | |||
@@ -92,6 +92,8 @@ static int _clkdm_register(struct clockdomain *clkdm) | |||
92 | 92 | ||
93 | pwrdm_add_clkdm(pwrdm, clkdm); | 93 | pwrdm_add_clkdm(pwrdm, clkdm); |
94 | 94 | ||
95 | spin_lock_init(&clkdm->lock); | ||
96 | |||
95 | pr_debug("clockdomain: registered %s\n", clkdm->name); | 97 | pr_debug("clockdomain: registered %s\n", clkdm->name); |
96 | 98 | ||
97 | return 0; | 99 | return 0; |
@@ -690,6 +692,9 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm) | |||
690 | */ | 692 | */ |
691 | int clkdm_sleep(struct clockdomain *clkdm) | 693 | int clkdm_sleep(struct clockdomain *clkdm) |
692 | { | 694 | { |
695 | int ret; | ||
696 | unsigned long flags; | ||
697 | |||
693 | if (!clkdm) | 698 | if (!clkdm) |
694 | return -EINVAL; | 699 | return -EINVAL; |
695 | 700 | ||
@@ -704,9 +709,11 @@ int clkdm_sleep(struct clockdomain *clkdm) | |||
704 | 709 | ||
705 | pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name); | 710 | pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name); |
706 | 711 | ||
712 | spin_lock_irqsave(&clkdm->lock, flags); | ||
707 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; | 713 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; |
708 | 714 | ret = arch_clkdm->clkdm_sleep(clkdm); | |
709 | return arch_clkdm->clkdm_sleep(clkdm); | 715 | spin_unlock_irqrestore(&clkdm->lock, flags); |
716 | return ret; | ||
710 | } | 717 | } |
711 | 718 | ||
712 | /** | 719 | /** |
@@ -720,6 +727,9 @@ int clkdm_sleep(struct clockdomain *clkdm) | |||
720 | */ | 727 | */ |
721 | int clkdm_wakeup(struct clockdomain *clkdm) | 728 | int clkdm_wakeup(struct clockdomain *clkdm) |
722 | { | 729 | { |
730 | int ret; | ||
731 | unsigned long flags; | ||
732 | |||
723 | if (!clkdm) | 733 | if (!clkdm) |
724 | return -EINVAL; | 734 | return -EINVAL; |
725 | 735 | ||
@@ -734,9 +744,11 @@ int clkdm_wakeup(struct clockdomain *clkdm) | |||
734 | 744 | ||
735 | pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name); | 745 | pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name); |
736 | 746 | ||
747 | spin_lock_irqsave(&clkdm->lock, flags); | ||
737 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; | 748 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; |
738 | 749 | ret = arch_clkdm->clkdm_wakeup(clkdm); | |
739 | return arch_clkdm->clkdm_wakeup(clkdm); | 750 | spin_unlock_irqrestore(&clkdm->lock, flags); |
751 | return ret; | ||
740 | } | 752 | } |
741 | 753 | ||
742 | /** | 754 | /** |
@@ -751,6 +763,8 @@ int clkdm_wakeup(struct clockdomain *clkdm) | |||
751 | */ | 763 | */ |
752 | void clkdm_allow_idle(struct clockdomain *clkdm) | 764 | void clkdm_allow_idle(struct clockdomain *clkdm) |
753 | { | 765 | { |
766 | unsigned long flags; | ||
767 | |||
754 | if (!clkdm) | 768 | if (!clkdm) |
755 | return; | 769 | return; |
756 | 770 | ||
@@ -766,10 +780,11 @@ void clkdm_allow_idle(struct clockdomain *clkdm) | |||
766 | pr_debug("clockdomain: enabling automatic idle transitions for %s\n", | 780 | pr_debug("clockdomain: enabling automatic idle transitions for %s\n", |
767 | clkdm->name); | 781 | clkdm->name); |
768 | 782 | ||
783 | spin_lock_irqsave(&clkdm->lock, flags); | ||
769 | clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED; | 784 | clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED; |
770 | |||
771 | arch_clkdm->clkdm_allow_idle(clkdm); | 785 | arch_clkdm->clkdm_allow_idle(clkdm); |
772 | pwrdm_clkdm_state_switch(clkdm); | 786 | pwrdm_clkdm_state_switch(clkdm); |
787 | spin_unlock_irqrestore(&clkdm->lock, flags); | ||
773 | } | 788 | } |
774 | 789 | ||
775 | /** | 790 | /** |
@@ -783,6 +798,8 @@ void clkdm_allow_idle(struct clockdomain *clkdm) | |||
783 | */ | 798 | */ |
784 | void clkdm_deny_idle(struct clockdomain *clkdm) | 799 | void clkdm_deny_idle(struct clockdomain *clkdm) |
785 | { | 800 | { |
801 | unsigned long flags; | ||
802 | |||
786 | if (!clkdm) | 803 | if (!clkdm) |
787 | return; | 804 | return; |
788 | 805 | ||
@@ -798,9 +815,10 @@ void clkdm_deny_idle(struct clockdomain *clkdm) | |||
798 | pr_debug("clockdomain: disabling automatic idle transitions for %s\n", | 815 | pr_debug("clockdomain: disabling automatic idle transitions for %s\n", |
799 | clkdm->name); | 816 | clkdm->name); |
800 | 817 | ||
818 | spin_lock_irqsave(&clkdm->lock, flags); | ||
801 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; | 819 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; |
802 | |||
803 | arch_clkdm->clkdm_deny_idle(clkdm); | 820 | arch_clkdm->clkdm_deny_idle(clkdm); |
821 | spin_unlock_irqrestore(&clkdm->lock, flags); | ||
804 | } | 822 | } |
805 | 823 | ||
806 | /** | 824 | /** |
@@ -816,16 +834,25 @@ void clkdm_deny_idle(struct clockdomain *clkdm) | |||
816 | */ | 834 | */ |
817 | bool clkdm_in_hwsup(struct clockdomain *clkdm) | 835 | bool clkdm_in_hwsup(struct clockdomain *clkdm) |
818 | { | 836 | { |
837 | bool ret; | ||
838 | unsigned long flags; | ||
839 | |||
819 | if (!clkdm) | 840 | if (!clkdm) |
820 | return false; | 841 | return false; |
821 | 842 | ||
822 | return (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false; | 843 | spin_lock_irqsave(&clkdm->lock, flags); |
844 | ret = (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false; | ||
845 | spin_unlock_irqrestore(&clkdm->lock, flags); | ||
846 | |||
847 | return ret; | ||
823 | } | 848 | } |
824 | 849 | ||
825 | /* Clockdomain-to-clock/hwmod framework interface code */ | 850 | /* Clockdomain-to-clock/hwmod framework interface code */ |
826 | 851 | ||
827 | static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm) | 852 | static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm) |
828 | { | 853 | { |
854 | unsigned long flags; | ||
855 | |||
829 | if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable) | 856 | if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable) |
830 | return -EINVAL; | 857 | return -EINVAL; |
831 | 858 | ||
@@ -837,9 +864,11 @@ static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm) | |||
837 | if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps) | 864 | if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps) |
838 | return 0; | 865 | return 0; |
839 | 866 | ||
867 | spin_lock_irqsave(&clkdm->lock, flags); | ||
840 | arch_clkdm->clkdm_clk_enable(clkdm); | 868 | arch_clkdm->clkdm_clk_enable(clkdm); |
841 | pwrdm_wait_transition(clkdm->pwrdm.ptr); | 869 | pwrdm_wait_transition(clkdm->pwrdm.ptr); |
842 | pwrdm_clkdm_state_switch(clkdm); | 870 | pwrdm_clkdm_state_switch(clkdm); |
871 | spin_unlock_irqrestore(&clkdm->lock, flags); | ||
843 | 872 | ||
844 | pr_debug("clockdomain: clkdm %s: enabled\n", clkdm->name); | 873 | pr_debug("clockdomain: clkdm %s: enabled\n", clkdm->name); |
845 | 874 | ||
@@ -848,6 +877,8 @@ static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm) | |||
848 | 877 | ||
849 | static int _clkdm_clk_hwmod_disable(struct clockdomain *clkdm) | 878 | static int _clkdm_clk_hwmod_disable(struct clockdomain *clkdm) |
850 | { | 879 | { |
880 | unsigned long flags; | ||
881 | |||
851 | if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) | 882 | if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) |
852 | return -EINVAL; | 883 | return -EINVAL; |
853 | 884 | ||
@@ -859,8 +890,10 @@ static int _clkdm_clk_hwmod_disable(struct clockdomain *clkdm) | |||
859 | if (atomic_dec_return(&clkdm->usecount) > 0) | 890 | if (atomic_dec_return(&clkdm->usecount) > 0) |
860 | return 0; | 891 | return 0; |
861 | 892 | ||
893 | spin_lock_irqsave(&clkdm->lock, flags); | ||
862 | arch_clkdm->clkdm_clk_disable(clkdm); | 894 | arch_clkdm->clkdm_clk_disable(clkdm); |
863 | pwrdm_clkdm_state_switch(clkdm); | 895 | pwrdm_clkdm_state_switch(clkdm); |
896 | spin_unlock_irqrestore(&clkdm->lock, flags); | ||
864 | 897 | ||
865 | pr_debug("clockdomain: clkdm %s: disabled\n", clkdm->name); | 898 | pr_debug("clockdomain: clkdm %s: disabled\n", clkdm->name); |
866 | 899 | ||
diff --git a/arch/arm/mach-omap2/clockdomain.h b/arch/arm/mach-omap2/clockdomain.h index 8782a5cadfa6..1e50c88b8a07 100644 --- a/arch/arm/mach-omap2/clockdomain.h +++ b/arch/arm/mach-omap2/clockdomain.h | |||
@@ -17,6 +17,7 @@ | |||
17 | #define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H | 17 | #define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H |
18 | 18 | ||
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/spinlock.h> | ||
20 | 21 | ||
21 | #include "powerdomain.h" | 22 | #include "powerdomain.h" |
22 | #include <plat/clock.h> | 23 | #include <plat/clock.h> |
@@ -128,6 +129,7 @@ struct clockdomain { | |||
128 | const struct omap_chip_id omap_chip; | 129 | const struct omap_chip_id omap_chip; |
129 | atomic_t usecount; | 130 | atomic_t usecount; |
130 | struct list_head node; | 131 | struct list_head node; |
132 | spinlock_t lock; | ||
131 | }; | 133 | }; |
132 | 134 | ||
133 | /** | 135 | /** |
diff --git a/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c b/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c index 48d0db7e6069..f740edb111f4 100644 --- a/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c +++ b/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c | |||
@@ -183,7 +183,8 @@ static int omap2_clkdm_clk_enable(struct clockdomain *clkdm) | |||
183 | _clkdm_add_autodeps(clkdm); | 183 | _clkdm_add_autodeps(clkdm); |
184 | _enable_hwsup(clkdm); | 184 | _enable_hwsup(clkdm); |
185 | } else { | 185 | } else { |
186 | clkdm_wakeup(clkdm); | 186 | if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) |
187 | omap2_clkdm_wakeup(clkdm); | ||
187 | } | 188 | } |
188 | 189 | ||
189 | return 0; | 190 | return 0; |
@@ -205,7 +206,8 @@ static int omap2_clkdm_clk_disable(struct clockdomain *clkdm) | |||
205 | _clkdm_del_autodeps(clkdm); | 206 | _clkdm_del_autodeps(clkdm); |
206 | _enable_hwsup(clkdm); | 207 | _enable_hwsup(clkdm); |
207 | } else { | 208 | } else { |
208 | clkdm_sleep(clkdm); | 209 | if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP) |
210 | omap2_clkdm_sleep(clkdm); | ||
209 | } | 211 | } |
210 | 212 | ||
211 | return 0; | 213 | return 0; |
diff --git a/arch/arm/mach-omap2/clockdomain44xx.c b/arch/arm/mach-omap2/clockdomain44xx.c index a1a4ecd26544..b43706aa08bd 100644 --- a/arch/arm/mach-omap2/clockdomain44xx.c +++ b/arch/arm/mach-omap2/clockdomain44xx.c | |||
@@ -95,13 +95,8 @@ static void omap4_clkdm_deny_idle(struct clockdomain *clkdm) | |||
95 | 95 | ||
96 | static int omap4_clkdm_clk_enable(struct clockdomain *clkdm) | 96 | static int omap4_clkdm_clk_enable(struct clockdomain *clkdm) |
97 | { | 97 | { |
98 | bool hwsup = false; | 98 | if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) |
99 | 99 | return omap4_clkdm_wakeup(clkdm); | |
100 | hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition, | ||
101 | clkdm->cm_inst, clkdm->clkdm_offs); | ||
102 | |||
103 | if (!hwsup) | ||
104 | clkdm_wakeup(clkdm); | ||
105 | 100 | ||
106 | return 0; | 101 | return 0; |
107 | } | 102 | } |
@@ -113,8 +108,8 @@ static int omap4_clkdm_clk_disable(struct clockdomain *clkdm) | |||
113 | hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition, | 108 | hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition, |
114 | clkdm->cm_inst, clkdm->clkdm_offs); | 109 | clkdm->cm_inst, clkdm->clkdm_offs); |
115 | 110 | ||
116 | if (!hwsup) | 111 | if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) |
117 | clkdm_sleep(clkdm); | 112 | omap4_clkdm_sleep(clkdm); |
118 | 113 | ||
119 | return 0; | 114 | return 0; |
120 | } | 115 | } |