Commit | Line | Data |
---|---|---|
5de363b6 | 1 | // SPDX-License-Identifier: GPL-2.0 |
85eb8c8d RW |
2 | /* |
3 | * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks | |
4 | * | |
5 | * Copyright (c) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. | |
85eb8c8d RW |
6 | */ |
7 | ||
85eb8c8d | 8 | #include <linux/kernel.h> |
51990e82 | 9 | #include <linux/device.h> |
85eb8c8d RW |
10 | #include <linux/io.h> |
11 | #include <linux/pm.h> | |
b5e8d269 | 12 | #include <linux/pm_clock.h> |
85eb8c8d | 13 | #include <linux/clk.h> |
245bd6f6 | 14 | #include <linux/clkdev.h> |
7e186d9d | 15 | #include <linux/of_clk.h> |
85eb8c8d RW |
16 | #include <linux/slab.h> |
17 | #include <linux/err.h> | |
989561de | 18 | #include <linux/pm_domain.h> |
75f50400 | 19 | #include <linux/pm_runtime.h> |
85eb8c8d | 20 | |
a6175616 | 21 | #ifdef CONFIG_PM_CLK |
85eb8c8d | 22 | |
85eb8c8d RW |
23 | enum pce_status { |
24 | PCE_STATUS_NONE = 0, | |
25 | PCE_STATUS_ACQUIRED, | |
0bfa0820 | 26 | PCE_STATUS_PREPARED, |
85eb8c8d RW |
27 | PCE_STATUS_ENABLED, |
28 | PCE_STATUS_ERROR, | |
29 | }; | |
30 | ||
31 | struct pm_clock_entry { | |
32 | struct list_head node; | |
33 | char *con_id; | |
34 | struct clk *clk; | |
35 | enum pce_status status; | |
0bfa0820 | 36 | bool enabled_when_prepared; |
85eb8c8d RW |
37 | }; |
38 | ||
0bfa0820 NP |
39 | /** |
40 | * pm_clk_list_lock - ensure exclusive access for modifying the PM clock | |
41 | * entry list. | |
42 | * @psd: pm_subsys_data instance corresponding to the PM clock entry list | |
43 | * and clk_op_might_sleep count to be modified. | |
44 | * | |
45 | * Get exclusive access before modifying the PM clock entry list and the | |
46 | * clock_op_might_sleep count to guard against concurrent modifications. | |
47 | * This also protects against a concurrent clock_op_might_sleep and PM clock | |
48 | * entry list usage in pm_clk_suspend()/pm_clk_resume() that may or may not | |
49 | * happen in atomic context, hence both the mutex and the spinlock must be | |
50 | * taken here. | |
51 | */ | |
52 | static void pm_clk_list_lock(struct pm_subsys_data *psd) | |
53 | __acquires(&psd->lock) | |
54 | { | |
55 | mutex_lock(&psd->clock_mutex); | |
56 | spin_lock_irq(&psd->lock); | |
57 | } | |
58 | ||
59 | /** | |
60 | * pm_clk_list_unlock - counterpart to pm_clk_list_lock(). | |
61 | * @psd: the same pm_subsys_data instance previously passed to | |
62 | * pm_clk_list_lock(). | |
63 | */ | |
64 | static void pm_clk_list_unlock(struct pm_subsys_data *psd) | |
65 | __releases(&psd->lock) | |
66 | { | |
67 | spin_unlock_irq(&psd->lock); | |
68 | mutex_unlock(&psd->clock_mutex); | |
69 | } | |
70 | ||
71 | /** | |
72 | * pm_clk_op_lock - ensure exclusive access for performing clock operations. | |
73 | * @psd: pm_subsys_data instance corresponding to the PM clock entry list | |
74 | * and clk_op_might_sleep count being used. | |
75 | * @flags: stored irq flags. | |
76 | * @fn: string for the caller function's name. | |
77 | * | |
78 | * This is used by pm_clk_suspend() and pm_clk_resume() to guard | |
79 | * against concurrent modifications to the clock entry list and the | |
80 | * clock_op_might_sleep count. If clock_op_might_sleep is != 0 then | |
81 | * only the mutex can be locked and those functions can only be used in | |
82 | * non atomic context. If clock_op_might_sleep == 0 then these functions | |
83 | * may be used in any context and only the spinlock can be locked. | |
84 | * Returns -EINVAL if called in atomic context when clock ops might sleep. | |
85 | */ | |
86 | static int pm_clk_op_lock(struct pm_subsys_data *psd, unsigned long *flags, | |
87 | const char *fn) | |
88 | /* sparse annotations don't work here as exit state isn't static */ | |
89 | { | |
90 | bool atomic_context = in_atomic() || irqs_disabled(); | |
91 | ||
92 | try_again: | |
93 | spin_lock_irqsave(&psd->lock, *flags); | |
94 | if (!psd->clock_op_might_sleep) { | |
95 | /* the __release is there to work around sparse limitations */ | |
96 | __release(&psd->lock); | |
97 | return 0; | |
98 | } | |
99 | ||
100 | /* bail out if in atomic context */ | |
101 | if (atomic_context) { | |
102 | pr_err("%s: atomic context with clock_ops_might_sleep = %d", | |
103 | fn, psd->clock_op_might_sleep); | |
104 | spin_unlock_irqrestore(&psd->lock, *flags); | |
105 | might_sleep(); | |
106 | return -EPERM; | |
107 | } | |
108 | ||
109 | /* we must switch to the mutex */ | |
110 | spin_unlock_irqrestore(&psd->lock, *flags); | |
111 | mutex_lock(&psd->clock_mutex); | |
112 | ||
113 | /* | |
114 | * There was a possibility for psd->clock_op_might_sleep | |
115 | * to become 0 above. Keep the mutex only if not the case. | |
116 | */ | |
117 | if (likely(psd->clock_op_might_sleep)) | |
118 | return 0; | |
119 | ||
120 | mutex_unlock(&psd->clock_mutex); | |
121 | goto try_again; | |
122 | } | |
123 | ||
124 | /** | |
125 | * pm_clk_op_unlock - counterpart to pm_clk_op_lock(). | |
126 | * @psd: the same pm_subsys_data instance previously passed to | |
127 | * pm_clk_op_lock(). | |
128 | * @flags: irq flags provided by pm_clk_op_lock(). | |
129 | */ | |
130 | static void pm_clk_op_unlock(struct pm_subsys_data *psd, unsigned long *flags) | |
131 | /* sparse annotations don't work here as entry state isn't static */ | |
132 | { | |
133 | if (psd->clock_op_might_sleep) { | |
134 | mutex_unlock(&psd->clock_mutex); | |
135 | } else { | |
136 | /* the __acquire is there to work around sparse limitations */ | |
137 | __acquire(&psd->lock); | |
138 | spin_unlock_irqrestore(&psd->lock, *flags); | |
139 | } | |
140 | } | |
141 | ||
5cda3fbb | 142 | /** |
33b688e3 | 143 | * __pm_clk_enable - Enable a clock, reporting any errors |
5cda3fbb | 144 | * @dev: The device for the given clock |
471f7707 | 145 | * @ce: PM clock entry corresponding to the clock. |
5cda3fbb | 146 | */ |
f4745a92 | 147 | static inline void __pm_clk_enable(struct device *dev, struct pm_clock_entry *ce) |
5cda3fbb | 148 | { |
471f7707 SG |
149 | int ret; |
150 | ||
0bfa0820 NP |
151 | switch (ce->status) { |
152 | case PCE_STATUS_ACQUIRED: | |
153 | ret = clk_prepare_enable(ce->clk); | |
154 | break; | |
155 | case PCE_STATUS_PREPARED: | |
471f7707 | 156 | ret = clk_enable(ce->clk); |
0bfa0820 NP |
157 | break; |
158 | default: | |
159 | return; | |
471f7707 | 160 | } |
0bfa0820 NP |
161 | if (!ret) |
162 | ce->status = PCE_STATUS_ENABLED; | |
163 | else | |
164 | dev_err(dev, "%s: failed to enable clk %p, error %d\n", | |
165 | __func__, ce->clk, ret); | |
5cda3fbb BD |
166 | } |
167 | ||
e8b364b8 RW |
168 | /** |
169 | * pm_clk_acquire - Acquire a device clock. | |
170 | * @dev: Device whose clock is to be acquired. | |
171 | * @ce: PM clock entry corresponding to the clock. | |
172 | */ | |
173 | static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce) | |
174 | { | |
245bd6f6 GU |
175 | if (!ce->clk) |
176 | ce->clk = clk_get(dev, ce->con_id); | |
e8b364b8 RW |
177 | if (IS_ERR(ce->clk)) { |
178 | ce->status = PCE_STATUS_ERROR; | |
0bfa0820 NP |
179 | return; |
180 | } else if (clk_is_enabled_when_prepared(ce->clk)) { | |
181 | /* we defer preparing the clock in that case */ | |
182 | ce->status = PCE_STATUS_ACQUIRED; | |
183 | ce->enabled_when_prepared = true; | |
184 | } else if (clk_prepare(ce->clk)) { | |
185 | ce->status = PCE_STATUS_ERROR; | |
186 | dev_err(dev, "clk_prepare() failed\n"); | |
187 | return; | |
e8b364b8 | 188 | } else { |
0bfa0820 | 189 | ce->status = PCE_STATUS_PREPARED; |
e8b364b8 | 190 | } |
0bfa0820 NP |
191 | dev_dbg(dev, "Clock %pC con_id %s managed by runtime PM.\n", |
192 | ce->clk, ce->con_id); | |
e8b364b8 RW |
193 | } |
194 | ||
245bd6f6 GU |
195 | static int __pm_clk_add(struct device *dev, const char *con_id, |
196 | struct clk *clk) | |
85eb8c8d | 197 | { |
5c095a0e | 198 | struct pm_subsys_data *psd = dev_to_psd(dev); |
85eb8c8d RW |
199 | struct pm_clock_entry *ce; |
200 | ||
5c095a0e | 201 | if (!psd) |
85eb8c8d RW |
202 | return -EINVAL; |
203 | ||
204 | ce = kzalloc(sizeof(*ce), GFP_KERNEL); | |
59d84ca8 | 205 | if (!ce) |
85eb8c8d | 206 | return -ENOMEM; |
85eb8c8d RW |
207 | |
208 | if (con_id) { | |
209 | ce->con_id = kstrdup(con_id, GFP_KERNEL); | |
210 | if (!ce->con_id) { | |
85eb8c8d RW |
211 | kfree(ce); |
212 | return -ENOMEM; | |
213 | } | |
245bd6f6 | 214 | } else { |
772b0508 | 215 | if (IS_ERR(clk)) { |
245bd6f6 GU |
216 | kfree(ce); |
217 | return -ENOENT; | |
218 | } | |
219 | ce->clk = clk; | |
85eb8c8d RW |
220 | } |
221 | ||
e8b364b8 RW |
222 | pm_clk_acquire(dev, ce); |
223 | ||
0bfa0820 | 224 | pm_clk_list_lock(psd); |
5c095a0e | 225 | list_add_tail(&ce->node, &psd->clock_list); |
0bfa0820 NP |
226 | if (ce->enabled_when_prepared) |
227 | psd->clock_op_might_sleep++; | |
228 | pm_clk_list_unlock(psd); | |
85eb8c8d RW |
229 | return 0; |
230 | } | |
231 | ||
245bd6f6 GU |
232 | /** |
233 | * pm_clk_add - Start using a device clock for power management. | |
234 | * @dev: Device whose clock is going to be used for power management. | |
235 | * @con_id: Connection ID of the clock. | |
236 | * | |
237 | * Add the clock represented by @con_id to the list of clocks used for | |
238 | * the power management of @dev. | |
239 | */ | |
240 | int pm_clk_add(struct device *dev, const char *con_id) | |
241 | { | |
242 | return __pm_clk_add(dev, con_id, NULL); | |
243 | } | |
29b968b2 | 244 | EXPORT_SYMBOL_GPL(pm_clk_add); |
245bd6f6 GU |
245 | |
246 | /** | |
247 | * pm_clk_add_clk - Start using a device clock for power management. | |
248 | * @dev: Device whose clock is going to be used for power management. | |
249 | * @clk: Clock pointer | |
250 | * | |
251 | * Add the clock to the list of clocks used for the power management of @dev. | |
772b0508 SB |
252 | * The power-management code will take control of the clock reference, so |
253 | * callers should not call clk_put() on @clk after this function sucessfully | |
254 | * returned. | |
245bd6f6 GU |
255 | */ |
256 | int pm_clk_add_clk(struct device *dev, struct clk *clk) | |
257 | { | |
258 | return __pm_clk_add(dev, NULL, clk); | |
259 | } | |
29b968b2 | 260 | EXPORT_SYMBOL_GPL(pm_clk_add_clk); |
245bd6f6 | 261 | |
02113ba9 JH |
262 | /** |
263 | * of_pm_clk_add_clks - Start using device clock(s) for power management. | |
264 | * @dev: Device whose clock(s) is going to be used for power management. | |
265 | * | |
266 | * Add a series of clocks described in the 'clocks' device-tree node for | |
267 | * a device to the list of clocks used for the power management of @dev. | |
268 | * On success, returns the number of clocks added. Returns a negative | |
269 | * error code if there are no clocks in the device node for the device | |
270 | * or if adding a clock fails. | |
271 | */ | |
272 | int of_pm_clk_add_clks(struct device *dev) | |
273 | { | |
274 | struct clk **clks; | |
5e2e2f9f | 275 | int i, count; |
02113ba9 JH |
276 | int ret; |
277 | ||
278 | if (!dev || !dev->of_node) | |
279 | return -EINVAL; | |
280 | ||
7e186d9d | 281 | count = of_clk_get_parent_count(dev->of_node); |
0b26985a | 282 | if (count <= 0) |
02113ba9 JH |
283 | return -ENODEV; |
284 | ||
285 | clks = kcalloc(count, sizeof(*clks), GFP_KERNEL); | |
286 | if (!clks) | |
287 | return -ENOMEM; | |
288 | ||
289 | for (i = 0; i < count; i++) { | |
290 | clks[i] = of_clk_get(dev->of_node, i); | |
291 | if (IS_ERR(clks[i])) { | |
292 | ret = PTR_ERR(clks[i]); | |
293 | goto error; | |
294 | } | |
295 | ||
296 | ret = pm_clk_add_clk(dev, clks[i]); | |
297 | if (ret) { | |
298 | clk_put(clks[i]); | |
299 | goto error; | |
300 | } | |
301 | } | |
302 | ||
303 | kfree(clks); | |
304 | ||
305 | return i; | |
306 | ||
307 | error: | |
308 | while (i--) | |
309 | pm_clk_remove_clk(dev, clks[i]); | |
310 | ||
311 | kfree(clks); | |
312 | ||
313 | return ret; | |
314 | } | |
29b968b2 | 315 | EXPORT_SYMBOL_GPL(of_pm_clk_add_clks); |
02113ba9 | 316 | |
85eb8c8d | 317 | /** |
3d5c3036 RW |
318 | * __pm_clk_remove - Destroy PM clock entry. |
319 | * @ce: PM clock entry to destroy. | |
85eb8c8d | 320 | */ |
3d5c3036 | 321 | static void __pm_clk_remove(struct pm_clock_entry *ce) |
85eb8c8d RW |
322 | { |
323 | if (!ce) | |
324 | return; | |
325 | ||
0bfa0820 NP |
326 | switch (ce->status) { |
327 | case PCE_STATUS_ENABLED: | |
328 | clk_disable(ce->clk); | |
329 | fallthrough; | |
330 | case PCE_STATUS_PREPARED: | |
331 | clk_unprepare(ce->clk); | |
332 | fallthrough; | |
333 | case PCE_STATUS_ACQUIRED: | |
334 | case PCE_STATUS_ERROR: | |
335 | if (!IS_ERR(ce->clk)) | |
85eb8c8d | 336 | clk_put(ce->clk); |
0bfa0820 NP |
337 | break; |
338 | default: | |
339 | break; | |
85eb8c8d RW |
340 | } |
341 | ||
0ab1e79b | 342 | kfree(ce->con_id); |
85eb8c8d RW |
343 | kfree(ce); |
344 | } | |
345 | ||
02113ba9 JH |
346 | /** |
347 | * pm_clk_remove_clk - Stop using a device clock for power management. | |
348 | * @dev: Device whose clock should not be used for PM any more. | |
349 | * @clk: Clock pointer | |
350 | * | |
351 | * Remove the clock pointed to by @clk from the list of clocks used for | |
352 | * the power management of @dev. | |
353 | */ | |
354 | void pm_clk_remove_clk(struct device *dev, struct clk *clk) | |
355 | { | |
356 | struct pm_subsys_data *psd = dev_to_psd(dev); | |
357 | struct pm_clock_entry *ce; | |
358 | ||
359 | if (!psd || !clk) | |
360 | return; | |
361 | ||
0bfa0820 | 362 | pm_clk_list_lock(psd); |
02113ba9 JH |
363 | |
364 | list_for_each_entry(ce, &psd->clock_list, node) { | |
365 | if (clk == ce->clk) | |
366 | goto remove; | |
367 | } | |
368 | ||
0bfa0820 | 369 | pm_clk_list_unlock(psd); |
02113ba9 JH |
370 | return; |
371 | ||
372 | remove: | |
373 | list_del(&ce->node); | |
0bfa0820 NP |
374 | if (ce->enabled_when_prepared) |
375 | psd->clock_op_might_sleep--; | |
376 | pm_clk_list_unlock(psd); | |
02113ba9 JH |
377 | |
378 | __pm_clk_remove(ce); | |
379 | } | |
29b968b2 | 380 | EXPORT_SYMBOL_GPL(pm_clk_remove_clk); |
02113ba9 | 381 | |
85eb8c8d | 382 | /** |
3d5c3036 RW |
383 | * pm_clk_init - Initialize a device's list of power management clocks. |
384 | * @dev: Device to initialize the list of PM clocks for. | |
85eb8c8d | 385 | * |
5c095a0e | 386 | * Initialize the lock and clock_list members of the device's pm_subsys_data |
0bfa0820 | 387 | * object, set the count of clocks that might sleep to 0. |
85eb8c8d | 388 | */ |
5c095a0e | 389 | void pm_clk_init(struct device *dev) |
85eb8c8d | 390 | { |
5c095a0e | 391 | struct pm_subsys_data *psd = dev_to_psd(dev); |
0bfa0820 | 392 | if (psd) { |
ef27bed1 | 393 | INIT_LIST_HEAD(&psd->clock_list); |
0bfa0820 NP |
394 | mutex_init(&psd->clock_mutex); |
395 | psd->clock_op_might_sleep = 0; | |
396 | } | |
5c095a0e | 397 | } |
29b968b2 | 398 | EXPORT_SYMBOL_GPL(pm_clk_init); |
5c095a0e RW |
399 | |
400 | /** | |
401 | * pm_clk_create - Create and initialize a device's list of PM clocks. | |
402 | * @dev: Device to create and initialize the list of PM clocks for. | |
403 | * | |
404 | * Allocate a struct pm_subsys_data object, initialize its lock and clock_list | |
405 | * members and make the @dev's power.subsys_data field point to it. | |
406 | */ | |
407 | int pm_clk_create(struct device *dev) | |
408 | { | |
77254950 | 409 | return dev_pm_get_subsys_data(dev); |
85eb8c8d | 410 | } |
29b968b2 | 411 | EXPORT_SYMBOL_GPL(pm_clk_create); |
85eb8c8d RW |
412 | |
413 | /** | |
3d5c3036 RW |
414 | * pm_clk_destroy - Destroy a device's list of power management clocks. |
415 | * @dev: Device to destroy the list of PM clocks for. | |
85eb8c8d RW |
416 | * |
417 | * Clear the @dev's power.subsys_data field, remove the list of clock entries | |
5c095a0e | 418 | * from the struct pm_subsys_data object pointed to by it before and free |
85eb8c8d RW |
419 | * that object. |
420 | */ | |
3d5c3036 | 421 | void pm_clk_destroy(struct device *dev) |
85eb8c8d | 422 | { |
5c095a0e | 423 | struct pm_subsys_data *psd = dev_to_psd(dev); |
85eb8c8d | 424 | struct pm_clock_entry *ce, *c; |
e8b364b8 | 425 | struct list_head list; |
85eb8c8d | 426 | |
5c095a0e | 427 | if (!psd) |
85eb8c8d RW |
428 | return; |
429 | ||
e8b364b8 | 430 | INIT_LIST_HEAD(&list); |
85eb8c8d | 431 | |
0bfa0820 | 432 | pm_clk_list_lock(psd); |
85eb8c8d | 433 | |
5c095a0e | 434 | list_for_each_entry_safe_reverse(ce, c, &psd->clock_list, node) |
e8b364b8 | 435 | list_move(&ce->node, &list); |
0bfa0820 | 436 | psd->clock_op_might_sleep = 0; |
85eb8c8d | 437 | |
0bfa0820 | 438 | pm_clk_list_unlock(psd); |
85eb8c8d | 439 | |
ef27bed1 | 440 | dev_pm_put_subsys_data(dev); |
e8b364b8 RW |
441 | |
442 | list_for_each_entry_safe_reverse(ce, c, &list, node) { | |
443 | list_del(&ce->node); | |
444 | __pm_clk_remove(ce); | |
445 | } | |
85eb8c8d | 446 | } |
29b968b2 | 447 | EXPORT_SYMBOL_GPL(pm_clk_destroy); |
85eb8c8d | 448 | |
a649136b DB |
449 | static void pm_clk_destroy_action(void *data) |
450 | { | |
451 | pm_clk_destroy(data); | |
452 | } | |
453 | ||
454 | int devm_pm_clk_create(struct device *dev) | |
455 | { | |
456 | int ret; | |
457 | ||
458 | ret = pm_clk_create(dev); | |
459 | if (ret) | |
460 | return ret; | |
461 | ||
462 | return devm_add_action_or_reset(dev, pm_clk_destroy_action, dev); | |
463 | } | |
464 | EXPORT_SYMBOL_GPL(devm_pm_clk_create); | |
465 | ||
85eb8c8d | 466 | /** |
3d5c3036 | 467 | * pm_clk_suspend - Disable clocks in a device's PM clock list. |
85eb8c8d RW |
468 | * @dev: Device to disable the clocks for. |
469 | */ | |
3d5c3036 | 470 | int pm_clk_suspend(struct device *dev) |
85eb8c8d | 471 | { |
5c095a0e | 472 | struct pm_subsys_data *psd = dev_to_psd(dev); |
85eb8c8d | 473 | struct pm_clock_entry *ce; |
b7ab83ed | 474 | unsigned long flags; |
0bfa0820 | 475 | int ret; |
85eb8c8d RW |
476 | |
477 | dev_dbg(dev, "%s()\n", __func__); | |
478 | ||
5c095a0e | 479 | if (!psd) |
85eb8c8d RW |
480 | return 0; |
481 | ||
0bfa0820 NP |
482 | ret = pm_clk_op_lock(psd, &flags, __func__); |
483 | if (ret) | |
484 | return ret; | |
85eb8c8d | 485 | |
5c095a0e | 486 | list_for_each_entry_reverse(ce, &psd->clock_list, node) { |
0bfa0820 NP |
487 | if (ce->status == PCE_STATUS_ENABLED) { |
488 | if (ce->enabled_when_prepared) { | |
489 | clk_disable_unprepare(ce->clk); | |
490 | ce->status = PCE_STATUS_ACQUIRED; | |
491 | } else { | |
24050956 | 492 | clk_disable(ce->clk); |
0bfa0820 NP |
493 | ce->status = PCE_STATUS_PREPARED; |
494 | } | |
85eb8c8d RW |
495 | } |
496 | } | |
497 | ||
0bfa0820 | 498 | pm_clk_op_unlock(psd, &flags); |
85eb8c8d RW |
499 | |
500 | return 0; | |
501 | } | |
29b968b2 | 502 | EXPORT_SYMBOL_GPL(pm_clk_suspend); |
85eb8c8d RW |
503 | |
504 | /** | |
3d5c3036 | 505 | * pm_clk_resume - Enable clocks in a device's PM clock list. |
85eb8c8d RW |
506 | * @dev: Device to enable the clocks for. |
507 | */ | |
3d5c3036 | 508 | int pm_clk_resume(struct device *dev) |
85eb8c8d | 509 | { |
5c095a0e | 510 | struct pm_subsys_data *psd = dev_to_psd(dev); |
85eb8c8d | 511 | struct pm_clock_entry *ce; |
b7ab83ed | 512 | unsigned long flags; |
0bfa0820 | 513 | int ret; |
85eb8c8d RW |
514 | |
515 | dev_dbg(dev, "%s()\n", __func__); | |
516 | ||
5c095a0e | 517 | if (!psd) |
85eb8c8d RW |
518 | return 0; |
519 | ||
0bfa0820 NP |
520 | ret = pm_clk_op_lock(psd, &flags, __func__); |
521 | if (ret) | |
522 | return ret; | |
85eb8c8d | 523 | |
471f7707 SG |
524 | list_for_each_entry(ce, &psd->clock_list, node) |
525 | __pm_clk_enable(dev, ce); | |
85eb8c8d | 526 | |
0bfa0820 | 527 | pm_clk_op_unlock(psd, &flags); |
85eb8c8d RW |
528 | |
529 | return 0; | |
530 | } | |
29b968b2 | 531 | EXPORT_SYMBOL_GPL(pm_clk_resume); |
85eb8c8d RW |
532 | |
533 | /** | |
3d5c3036 | 534 | * pm_clk_notify - Notify routine for device addition and removal. |
85eb8c8d RW |
535 | * @nb: Notifier block object this function is a member of. |
536 | * @action: Operation being carried out by the caller. | |
537 | * @data: Device the routine is being run for. | |
538 | * | |
539 | * For this function to work, @nb must be a member of an object of type | |
540 | * struct pm_clk_notifier_block containing all of the requisite data. | |
564b905a RW |
541 | * Specifically, the pm_domain member of that object is copied to the device's |
542 | * pm_domain field and its con_ids member is used to populate the device's list | |
3d5c3036 | 543 | * of PM clocks, depending on @action. |
85eb8c8d | 544 | * |
564b905a | 545 | * If the device's pm_domain field is already populated with a value different |
85eb8c8d RW |
546 | * from the one stored in the struct pm_clk_notifier_block object, the function |
547 | * does nothing. | |
548 | */ | |
3d5c3036 | 549 | static int pm_clk_notify(struct notifier_block *nb, |
85eb8c8d RW |
550 | unsigned long action, void *data) |
551 | { | |
552 | struct pm_clk_notifier_block *clknb; | |
553 | struct device *dev = data; | |
3b3eca31 | 554 | char **con_id; |
85eb8c8d RW |
555 | int error; |
556 | ||
557 | dev_dbg(dev, "%s() %ld\n", __func__, action); | |
558 | ||
559 | clknb = container_of(nb, struct pm_clk_notifier_block, nb); | |
560 | ||
561 | switch (action) { | |
562 | case BUS_NOTIFY_ADD_DEVICE: | |
564b905a | 563 | if (dev->pm_domain) |
85eb8c8d RW |
564 | break; |
565 | ||
5c095a0e | 566 | error = pm_clk_create(dev); |
85eb8c8d RW |
567 | if (error) |
568 | break; | |
569 | ||
989561de | 570 | dev_pm_domain_set(dev, clknb->pm_domain); |
85eb8c8d | 571 | if (clknb->con_ids[0]) { |
3b3eca31 | 572 | for (con_id = clknb->con_ids; *con_id; con_id++) |
3d5c3036 | 573 | pm_clk_add(dev, *con_id); |
85eb8c8d | 574 | } else { |
3d5c3036 | 575 | pm_clk_add(dev, NULL); |
85eb8c8d RW |
576 | } |
577 | ||
578 | break; | |
579 | case BUS_NOTIFY_DEL_DEVICE: | |
564b905a | 580 | if (dev->pm_domain != clknb->pm_domain) |
85eb8c8d RW |
581 | break; |
582 | ||
989561de | 583 | dev_pm_domain_set(dev, NULL); |
3d5c3036 | 584 | pm_clk_destroy(dev); |
85eb8c8d RW |
585 | break; |
586 | } | |
587 | ||
588 | return 0; | |
589 | } | |
590 | ||
75f50400 RN |
591 | int pm_clk_runtime_suspend(struct device *dev) |
592 | { | |
593 | int ret; | |
594 | ||
595 | dev_dbg(dev, "%s\n", __func__); | |
596 | ||
597 | ret = pm_generic_runtime_suspend(dev); | |
598 | if (ret) { | |
599 | dev_err(dev, "failed to suspend device\n"); | |
600 | return ret; | |
601 | } | |
602 | ||
603 | ret = pm_clk_suspend(dev); | |
604 | if (ret) { | |
605 | dev_err(dev, "failed to suspend clock\n"); | |
606 | pm_generic_runtime_resume(dev); | |
607 | return ret; | |
608 | } | |
609 | ||
610 | return 0; | |
611 | } | |
29b968b2 | 612 | EXPORT_SYMBOL_GPL(pm_clk_runtime_suspend); |
75f50400 RN |
613 | |
614 | int pm_clk_runtime_resume(struct device *dev) | |
615 | { | |
616 | int ret; | |
617 | ||
618 | dev_dbg(dev, "%s\n", __func__); | |
619 | ||
620 | ret = pm_clk_resume(dev); | |
621 | if (ret) { | |
622 | dev_err(dev, "failed to resume clock\n"); | |
623 | return ret; | |
624 | } | |
625 | ||
626 | return pm_generic_runtime_resume(dev); | |
627 | } | |
29b968b2 | 628 | EXPORT_SYMBOL_GPL(pm_clk_runtime_resume); |
75f50400 | 629 | |
a6175616 | 630 | #else /* !CONFIG_PM_CLK */ |
b7b95920 | 631 | |
85eb8c8d RW |
632 | /** |
633 | * enable_clock - Enable a device clock. | |
634 | * @dev: Device whose clock is to be enabled. | |
635 | * @con_id: Connection ID of the clock. | |
636 | */ | |
637 | static void enable_clock(struct device *dev, const char *con_id) | |
638 | { | |
639 | struct clk *clk; | |
640 | ||
641 | clk = clk_get(dev, con_id); | |
642 | if (!IS_ERR(clk)) { | |
c122f27e | 643 | clk_prepare_enable(clk); |
85eb8c8d RW |
644 | clk_put(clk); |
645 | dev_info(dev, "Runtime PM disabled, clock forced on.\n"); | |
646 | } | |
647 | } | |
648 | ||
649 | /** | |
650 | * disable_clock - Disable a device clock. | |
651 | * @dev: Device whose clock is to be disabled. | |
652 | * @con_id: Connection ID of the clock. | |
653 | */ | |
654 | static void disable_clock(struct device *dev, const char *con_id) | |
655 | { | |
656 | struct clk *clk; | |
657 | ||
658 | clk = clk_get(dev, con_id); | |
659 | if (!IS_ERR(clk)) { | |
c122f27e | 660 | clk_disable_unprepare(clk); |
85eb8c8d RW |
661 | clk_put(clk); |
662 | dev_info(dev, "Runtime PM disabled, clock forced off.\n"); | |
663 | } | |
664 | } | |
665 | ||
666 | /** | |
3d5c3036 | 667 | * pm_clk_notify - Notify routine for device addition and removal. |
85eb8c8d RW |
668 | * @nb: Notifier block object this function is a member of. |
669 | * @action: Operation being carried out by the caller. | |
670 | * @data: Device the routine is being run for. | |
671 | * | |
672 | * For this function to work, @nb must be a member of an object of type | |
673 | * struct pm_clk_notifier_block containing all of the requisite data. | |
674 | * Specifically, the con_ids member of that object is used to enable or disable | |
675 | * the device's clocks, depending on @action. | |
676 | */ | |
3d5c3036 | 677 | static int pm_clk_notify(struct notifier_block *nb, |
85eb8c8d RW |
678 | unsigned long action, void *data) |
679 | { | |
680 | struct pm_clk_notifier_block *clknb; | |
681 | struct device *dev = data; | |
3b3eca31 | 682 | char **con_id; |
85eb8c8d RW |
683 | |
684 | dev_dbg(dev, "%s() %ld\n", __func__, action); | |
685 | ||
686 | clknb = container_of(nb, struct pm_clk_notifier_block, nb); | |
687 | ||
688 | switch (action) { | |
4d1518f5 | 689 | case BUS_NOTIFY_BIND_DRIVER: |
85eb8c8d | 690 | if (clknb->con_ids[0]) { |
3b3eca31 RW |
691 | for (con_id = clknb->con_ids; *con_id; con_id++) |
692 | enable_clock(dev, *con_id); | |
85eb8c8d RW |
693 | } else { |
694 | enable_clock(dev, NULL); | |
695 | } | |
696 | break; | |
d35818a9 | 697 | case BUS_NOTIFY_DRIVER_NOT_BOUND: |
4d1518f5 | 698 | case BUS_NOTIFY_UNBOUND_DRIVER: |
85eb8c8d | 699 | if (clknb->con_ids[0]) { |
3b3eca31 RW |
700 | for (con_id = clknb->con_ids; *con_id; con_id++) |
701 | disable_clock(dev, *con_id); | |
85eb8c8d RW |
702 | } else { |
703 | disable_clock(dev, NULL); | |
704 | } | |
705 | break; | |
706 | } | |
707 | ||
708 | return 0; | |
709 | } | |
710 | ||
a6175616 | 711 | #endif /* !CONFIG_PM_CLK */ |
85eb8c8d RW |
712 | |
713 | /** | |
3d5c3036 | 714 | * pm_clk_add_notifier - Add bus type notifier for power management clocks. |
85eb8c8d RW |
715 | * @bus: Bus type to add the notifier to. |
716 | * @clknb: Notifier to be added to the given bus type. | |
717 | * | |
718 | * The nb member of @clknb is not expected to be initialized and its | |
3d5c3036 | 719 | * notifier_call member will be replaced with pm_clk_notify(). However, |
85eb8c8d RW |
720 | * the remaining members of @clknb should be populated prior to calling this |
721 | * routine. | |
722 | */ | |
db2292b0 | 723 | void pm_clk_add_notifier(const struct bus_type *bus, |
85eb8c8d RW |
724 | struct pm_clk_notifier_block *clknb) |
725 | { | |
726 | if (!bus || !clknb) | |
727 | return; | |
728 | ||
3d5c3036 | 729 | clknb->nb.notifier_call = pm_clk_notify; |
85eb8c8d RW |
730 | bus_register_notifier(bus, &clknb->nb); |
731 | } | |
29b968b2 | 732 | EXPORT_SYMBOL_GPL(pm_clk_add_notifier); |