aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/power/clock_ops.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-09-26 13:40:23 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2011-09-26 13:40:23 -0400
commite8b364b88cc4001b21c28c1ecf1e1e3ffbe162e6 (patch)
tree980f84081c2278ff2b6bb4a113d949a1730759a8 /drivers/base/power/clock_ops.c
parentf9d81f61c84aca693bc353dfef4b8c36c2e5e1b5 (diff)
PM / Clocks: Do not acquire a mutex under a spinlock
Commit b7ab83e (PM: Use spinlock instead of mutex in clock management functions) introduced a regression causing clocks_mutex to be acquired under a spinlock. This happens because pm_clk_suspend() and pm_clk_resume() call pm_clk_acquire() under pcd->lock, but pm_clk_acquire() executes clk_get() which causes clocks_mutex to be acquired. Similarly, __pm_clk_remove(), executed under pcd->lock, calls clk_put(), which also causes clocks_mutex to be acquired. To fix those problems make pm_clk_add() call pm_clk_acquire(), so that pm_clk_suspend() and pm_clk_resume() don't have to do that. Change pm_clk_remove() and pm_clk_destroy() to separate modifications of the pcd->clock_list list from the actual removal of PM clock entry objects done by __pm_clk_remove(). Reported-and-tested-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/base/power/clock_ops.c')
-rw-r--r--drivers/base/power/clock_ops.c75
1 files changed, 38 insertions, 37 deletions
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index 2c18d584066d..b97294e2d95b 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -42,6 +42,22 @@ static struct pm_clk_data *__to_pcd(struct device *dev)
42} 42}
43 43
44/** 44/**
45 * pm_clk_acquire - Acquire a device clock.
46 * @dev: Device whose clock is to be acquired.
47 * @ce: PM clock entry corresponding to the clock.
48 */
49static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce)
50{
51 ce->clk = clk_get(dev, ce->con_id);
52 if (IS_ERR(ce->clk)) {
53 ce->status = PCE_STATUS_ERROR;
54 } else {
55 ce->status = PCE_STATUS_ACQUIRED;
56 dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
57 }
58}
59
60/**
45 * pm_clk_add - Start using a device clock for power management. 61 * pm_clk_add - Start using a device clock for power management.
46 * @dev: Device whose clock is going to be used for power management. 62 * @dev: Device whose clock is going to be used for power management.
47 * @con_id: Connection ID of the clock. 63 * @con_id: Connection ID of the clock.
@@ -73,6 +89,8 @@ int pm_clk_add(struct device *dev, const char *con_id)
73 } 89 }
74 } 90 }
75 91
92 pm_clk_acquire(dev, ce);
93
76 spin_lock_irq(&pcd->lock); 94 spin_lock_irq(&pcd->lock);
77 list_add_tail(&ce->node, &pcd->clock_list); 95 list_add_tail(&ce->node, &pcd->clock_list);
78 spin_unlock_irq(&pcd->lock); 96 spin_unlock_irq(&pcd->lock);
@@ -82,17 +100,12 @@ int pm_clk_add(struct device *dev, const char *con_id)
82/** 100/**
83 * __pm_clk_remove - Destroy PM clock entry. 101 * __pm_clk_remove - Destroy PM clock entry.
84 * @ce: PM clock entry to destroy. 102 * @ce: PM clock entry to destroy.
85 *
86 * This routine must be called under the spinlock protecting the PM list of
87 * clocks corresponding the the @ce's device.
88 */ 103 */
89static void __pm_clk_remove(struct pm_clock_entry *ce) 104static void __pm_clk_remove(struct pm_clock_entry *ce)
90{ 105{
91 if (!ce) 106 if (!ce)
92 return; 107 return;
93 108
94 list_del(&ce->node);
95
96 if (ce->status < PCE_STATUS_ERROR) { 109 if (ce->status < PCE_STATUS_ERROR) {
97 if (ce->status == PCE_STATUS_ENABLED) 110 if (ce->status == PCE_STATUS_ENABLED)
98 clk_disable(ce->clk); 111 clk_disable(ce->clk);
@@ -126,18 +139,22 @@ void pm_clk_remove(struct device *dev, const char *con_id)
126 spin_lock_irq(&pcd->lock); 139 spin_lock_irq(&pcd->lock);
127 140
128 list_for_each_entry(ce, &pcd->clock_list, node) { 141 list_for_each_entry(ce, &pcd->clock_list, node) {
129 if (!con_id && !ce->con_id) { 142 if (!con_id && !ce->con_id)
130 __pm_clk_remove(ce); 143 goto remove;
131 break; 144 else if (!con_id || !ce->con_id)
132 } else if (!con_id || !ce->con_id) {
133 continue; 145 continue;
134 } else if (!strcmp(con_id, ce->con_id)) { 146 else if (!strcmp(con_id, ce->con_id))
135 __pm_clk_remove(ce); 147 goto remove;
136 break;
137 }
138 } 148 }
139 149
140 spin_unlock_irq(&pcd->lock); 150 spin_unlock_irq(&pcd->lock);
151 return;
152
153 remove:
154 list_del(&ce->node);
155 spin_unlock_irq(&pcd->lock);
156
157 __pm_clk_remove(ce);
141} 158}
142 159
143/** 160/**
@@ -175,20 +192,27 @@ void pm_clk_destroy(struct device *dev)
175{ 192{
176 struct pm_clk_data *pcd = __to_pcd(dev); 193 struct pm_clk_data *pcd = __to_pcd(dev);
177 struct pm_clock_entry *ce, *c; 194 struct pm_clock_entry *ce, *c;
195 struct list_head list;
178 196
179 if (!pcd) 197 if (!pcd)
180 return; 198 return;
181 199
182 dev->power.subsys_data = NULL; 200 dev->power.subsys_data = NULL;
201 INIT_LIST_HEAD(&list);
183 202
184 spin_lock_irq(&pcd->lock); 203 spin_lock_irq(&pcd->lock);
185 204
186 list_for_each_entry_safe_reverse(ce, c, &pcd->clock_list, node) 205 list_for_each_entry_safe_reverse(ce, c, &pcd->clock_list, node)
187 __pm_clk_remove(ce); 206 list_move(&ce->node, &list);
188 207
189 spin_unlock_irq(&pcd->lock); 208 spin_unlock_irq(&pcd->lock);
190 209
191 kfree(pcd); 210 kfree(pcd);
211
212 list_for_each_entry_safe_reverse(ce, c, &list, node) {
213 list_del(&ce->node);
214 __pm_clk_remove(ce);
215 }
192} 216}
193 217
194#endif /* CONFIG_PM */ 218#endif /* CONFIG_PM */
@@ -196,23 +220,6 @@ void pm_clk_destroy(struct device *dev)
196#ifdef CONFIG_PM_RUNTIME 220#ifdef CONFIG_PM_RUNTIME
197 221
198/** 222/**
199 * pm_clk_acquire - Acquire a device clock.
200 * @dev: Device whose clock is to be acquired.
201 * @con_id: Connection ID of the clock.
202 */
203static void pm_clk_acquire(struct device *dev,
204 struct pm_clock_entry *ce)
205{
206 ce->clk = clk_get(dev, ce->con_id);
207 if (IS_ERR(ce->clk)) {
208 ce->status = PCE_STATUS_ERROR;
209 } else {
210 ce->status = PCE_STATUS_ACQUIRED;
211 dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
212 }
213}
214
215/**
216 * pm_clk_suspend - Disable clocks in a device's PM clock list. 223 * pm_clk_suspend - Disable clocks in a device's PM clock list.
217 * @dev: Device to disable the clocks for. 224 * @dev: Device to disable the clocks for.
218 */ 225 */
@@ -230,9 +237,6 @@ int pm_clk_suspend(struct device *dev)
230 spin_lock_irqsave(&pcd->lock, flags); 237 spin_lock_irqsave(&pcd->lock, flags);
231 238
232 list_for_each_entry_reverse(ce, &pcd->clock_list, node) { 239 list_for_each_entry_reverse(ce, &pcd->clock_list, node) {
233 if (ce->status == PCE_STATUS_NONE)
234 pm_clk_acquire(dev, ce);
235
236 if (ce->status < PCE_STATUS_ERROR) { 240 if (ce->status < PCE_STATUS_ERROR) {
237 clk_disable(ce->clk); 241 clk_disable(ce->clk);
238 ce->status = PCE_STATUS_ACQUIRED; 242 ce->status = PCE_STATUS_ACQUIRED;
@@ -262,9 +266,6 @@ int pm_clk_resume(struct device *dev)
262 spin_lock_irqsave(&pcd->lock, flags); 266 spin_lock_irqsave(&pcd->lock, flags);
263 267
264 list_for_each_entry(ce, &pcd->clock_list, node) { 268 list_for_each_entry(ce, &pcd->clock_list, node) {
265 if (ce->status == PCE_STATUS_NONE)
266 pm_clk_acquire(dev, ce);
267
268 if (ce->status < PCE_STATUS_ERROR) { 269 if (ce->status < PCE_STATUS_ERROR) {
269 clk_enable(ce->clk); 270 clk_enable(ce->clk);
270 ce->status = PCE_STATUS_ENABLED; 271 ce->status = PCE_STATUS_ENABLED;