aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/power/clock_ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/power/clock_ops.c')
-rw-r--r--drivers/base/power/clock_ops.c111
1 files changed, 58 insertions, 53 deletions
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index a846b2f95cf..b97294e2d95 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -19,7 +19,7 @@
19 19
20struct pm_clk_data { 20struct pm_clk_data {
21 struct list_head clock_list; 21 struct list_head clock_list;
22 struct mutex lock; 22 spinlock_t lock;
23}; 23};
24 24
25enum pce_status { 25enum pce_status {
@@ -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,26 +89,23 @@ int pm_clk_add(struct device *dev, const char *con_id)
73 } 89 }
74 } 90 }
75 91
76 mutex_lock(&pcd->lock); 92 pm_clk_acquire(dev, ce);
93
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 mutex_unlock(&pcd->lock); 96 spin_unlock_irq(&pcd->lock);
79 return 0; 97 return 0;
80} 98}
81 99
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 mutex protecting the PM list of clocks
87 * 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);
@@ -123,21 +136,25 @@ void pm_clk_remove(struct device *dev, const char *con_id)
123 if (!pcd) 136 if (!pcd)
124 return; 137 return;
125 138
126 mutex_lock(&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 mutex_unlock(&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/**
@@ -158,7 +175,7 @@ int pm_clk_init(struct device *dev)
158 } 175 }
159 176
160 INIT_LIST_HEAD(&pcd->clock_list); 177 INIT_LIST_HEAD(&pcd->clock_list);
161 mutex_init(&pcd->lock); 178 spin_lock_init(&pcd->lock);
162 dev->power.subsys_data = pcd; 179 dev->power.subsys_data = pcd;
163 return 0; 180 return 0;
164} 181}
@@ -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 mutex_lock(&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 mutex_unlock(&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 */
@@ -220,25 +227,23 @@ int pm_clk_suspend(struct device *dev)
220{ 227{
221 struct pm_clk_data *pcd = __to_pcd(dev); 228 struct pm_clk_data *pcd = __to_pcd(dev);
222 struct pm_clock_entry *ce; 229 struct pm_clock_entry *ce;
230 unsigned long flags;
223 231
224 dev_dbg(dev, "%s()\n", __func__); 232 dev_dbg(dev, "%s()\n", __func__);
225 233
226 if (!pcd) 234 if (!pcd)
227 return 0; 235 return 0;
228 236
229 mutex_lock(&pcd->lock); 237 spin_lock_irqsave(&pcd->lock, flags);
230 238
231 list_for_each_entry_reverse(ce, &pcd->clock_list, node) { 239 list_for_each_entry_reverse(ce, &pcd->clock_list, node) {
232 if (ce->status == PCE_STATUS_NONE)
233 pm_clk_acquire(dev, ce);
234
235 if (ce->status < PCE_STATUS_ERROR) { 240 if (ce->status < PCE_STATUS_ERROR) {
236 clk_disable(ce->clk); 241 clk_disable(ce->clk);
237 ce->status = PCE_STATUS_ACQUIRED; 242 ce->status = PCE_STATUS_ACQUIRED;
238 } 243 }
239 } 244 }
240 245
241 mutex_unlock(&pcd->lock); 246 spin_unlock_irqrestore(&pcd->lock, flags);
242 247
243 return 0; 248 return 0;
244} 249}
@@ -251,25 +256,23 @@ int pm_clk_resume(struct device *dev)
251{ 256{
252 struct pm_clk_data *pcd = __to_pcd(dev); 257 struct pm_clk_data *pcd = __to_pcd(dev);
253 struct pm_clock_entry *ce; 258 struct pm_clock_entry *ce;
259 unsigned long flags;
254 260
255 dev_dbg(dev, "%s()\n", __func__); 261 dev_dbg(dev, "%s()\n", __func__);
256 262
257 if (!pcd) 263 if (!pcd)
258 return 0; 264 return 0;
259 265
260 mutex_lock(&pcd->lock); 266 spin_lock_irqsave(&pcd->lock, flags);
261 267
262 list_for_each_entry(ce, &pcd->clock_list, node) { 268 list_for_each_entry(ce, &pcd->clock_list, node) {
263 if (ce->status == PCE_STATUS_NONE)
264 pm_clk_acquire(dev, ce);
265
266 if (ce->status < PCE_STATUS_ERROR) { 269 if (ce->status < PCE_STATUS_ERROR) {
267 clk_enable(ce->clk); 270 clk_enable(ce->clk);
268 ce->status = PCE_STATUS_ENABLED; 271 ce->status = PCE_STATUS_ENABLED;
269 } 272 }
270 } 273 }
271 274
272 mutex_unlock(&pcd->lock); 275 spin_unlock_irqrestore(&pcd->lock, flags);
273 276
274 return 0; 277 return 0;
275} 278}
@@ -344,6 +347,7 @@ int pm_clk_suspend(struct device *dev)
344{ 347{
345 struct pm_clk_data *pcd = __to_pcd(dev); 348 struct pm_clk_data *pcd = __to_pcd(dev);
346 struct pm_clock_entry *ce; 349 struct pm_clock_entry *ce;
350 unsigned long flags;
347 351
348 dev_dbg(dev, "%s()\n", __func__); 352 dev_dbg(dev, "%s()\n", __func__);
349 353
@@ -351,12 +355,12 @@ int pm_clk_suspend(struct device *dev)
351 if (!pcd || !dev->driver) 355 if (!pcd || !dev->driver)
352 return 0; 356 return 0;
353 357
354 mutex_lock(&pcd->lock); 358 spin_lock_irqsave(&pcd->lock, flags);
355 359
356 list_for_each_entry_reverse(ce, &pcd->clock_list, node) 360 list_for_each_entry_reverse(ce, &pcd->clock_list, node)
357 clk_disable(ce->clk); 361 clk_disable(ce->clk);
358 362
359 mutex_unlock(&pcd->lock); 363 spin_unlock_irqrestore(&pcd->lock, flags);
360 364
361 return 0; 365 return 0;
362} 366}
@@ -369,6 +373,7 @@ int pm_clk_resume(struct device *dev)
369{ 373{
370 struct pm_clk_data *pcd = __to_pcd(dev); 374 struct pm_clk_data *pcd = __to_pcd(dev);
371 struct pm_clock_entry *ce; 375 struct pm_clock_entry *ce;
376 unsigned long flags;
372 377
373 dev_dbg(dev, "%s()\n", __func__); 378 dev_dbg(dev, "%s()\n", __func__);
374 379
@@ -376,12 +381,12 @@ int pm_clk_resume(struct device *dev)
376 if (!pcd || !dev->driver) 381 if (!pcd || !dev->driver)
377 return 0; 382 return 0;
378 383
379 mutex_lock(&pcd->lock); 384 spin_lock_irqsave(&pcd->lock, flags);
380 385
381 list_for_each_entry(ce, &pcd->clock_list, node) 386 list_for_each_entry(ce, &pcd->clock_list, node)
382 clk_enable(ce->clk); 387 clk_enable(ce->clk);
383 388
384 mutex_unlock(&pcd->lock); 389 spin_unlock_irqrestore(&pcd->lock, flags);
385 390
386 return 0; 391 return 0;
387} 392}