1 | /* |
2 | * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks |
3 | * |
4 | * Copyright (c) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. |
5 | * |
6 | * This file is released under the GPLv2. |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/device.h> |
11 | #include <linux/io.h> |
12 | #include <linux/pm.h> |
13 | #include <linux/pm_clock.h> |
14 | #include <linux/clk.h> |
15 | #include <linux/clkdev.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/err.h> |
18 | #include <linux/pm_domain.h> |
19 | #include <linux/pm_runtime.h> |
20 | |
21 | #ifdef CONFIG_PM_CLK |
22 | |
23 | enum pce_status { |
24 | PCE_STATUS_NONE = 0, |
25 | PCE_STATUS_ACQUIRED, |
26 | PCE_STATUS_ENABLED, |
27 | PCE_STATUS_ERROR, |
28 | }; |
29 | |
30 | struct pm_clock_entry { |
31 | struct list_head node; |
32 | char *con_id; |
33 | struct clk *clk; |
34 | enum pce_status status; |
35 | }; |
36 | |
37 | /** |
38 | * pm_clk_enable - Enable a clock, reporting any errors |
39 | * @dev: The device for the given clock |
40 | * @ce: PM clock entry corresponding to the clock. |
41 | */ |
42 | static inline void __pm_clk_enable(struct device *dev, struct pm_clock_entry *ce) |
43 | { |
44 | int ret; |
45 | |
46 | if (ce->status < PCE_STATUS_ERROR) { |
47 | ret = clk_enable(ce->clk); |
48 | if (!ret) |
49 | ce->status = PCE_STATUS_ENABLED; |
50 | else |
51 | dev_err(dev, "%s: failed to enable clk %p, error %d\n" , |
52 | __func__, ce->clk, ret); |
53 | } |
54 | } |
55 | |
56 | /** |
57 | * pm_clk_acquire - Acquire a device clock. |
58 | * @dev: Device whose clock is to be acquired. |
59 | * @ce: PM clock entry corresponding to the clock. |
60 | */ |
61 | static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce) |
62 | { |
63 | if (!ce->clk) |
64 | ce->clk = clk_get(dev, ce->con_id); |
65 | if (IS_ERR(ce->clk)) { |
66 | ce->status = PCE_STATUS_ERROR; |
67 | } else { |
68 | if (clk_prepare(ce->clk)) { |
69 | ce->status = PCE_STATUS_ERROR; |
70 | dev_err(dev, "clk_prepare() failed\n" ); |
71 | } else { |
72 | ce->status = PCE_STATUS_ACQUIRED; |
73 | dev_dbg(dev, |
74 | "Clock %pC con_id %s managed by runtime PM.\n" , |
75 | ce->clk, ce->con_id); |
76 | } |
77 | } |
78 | } |
79 | |
80 | static int __pm_clk_add(struct device *dev, const char *con_id, |
81 | struct clk *clk) |
82 | { |
83 | struct pm_subsys_data *psd = dev_to_psd(dev); |
84 | struct pm_clock_entry *ce; |
85 | |
86 | if (!psd) |
87 | return -EINVAL; |
88 | |
89 | ce = kzalloc(sizeof(*ce), GFP_KERNEL); |
90 | if (!ce) |
91 | return -ENOMEM; |
92 | |
93 | if (con_id) { |
94 | ce->con_id = kstrdup(con_id, GFP_KERNEL); |
95 | if (!ce->con_id) { |
96 | dev_err(dev, |
97 | "Not enough memory for clock connection ID.\n" ); |
98 | kfree(ce); |
99 | return -ENOMEM; |
100 | } |
101 | } else { |
102 | if (IS_ERR(clk)) { |
103 | kfree(ce); |
104 | return -ENOENT; |
105 | } |
106 | ce->clk = clk; |
107 | } |
108 | |
109 | pm_clk_acquire(dev, ce); |
110 | |
111 | spin_lock_irq(&psd->lock); |
112 | list_add_tail(&ce->node, &psd->clock_list); |
113 | spin_unlock_irq(&psd->lock); |
114 | return 0; |
115 | } |
116 | |
117 | /** |
118 | * pm_clk_add - Start using a device clock for power management. |
119 | * @dev: Device whose clock is going to be used for power management. |
120 | * @con_id: Connection ID of the clock. |
121 | * |
122 | * Add the clock represented by @con_id to the list of clocks used for |
123 | * the power management of @dev. |
124 | */ |
125 | int pm_clk_add(struct device *dev, const char *con_id) |
126 | { |
127 | return __pm_clk_add(dev, con_id, NULL); |
128 | } |
129 | EXPORT_SYMBOL_GPL(pm_clk_add); |
130 | |
131 | /** |
132 | * pm_clk_add_clk - Start using a device clock for power management. |
133 | * @dev: Device whose clock is going to be used for power management. |
134 | * @clk: Clock pointer |
135 | * |
136 | * Add the clock to the list of clocks used for the power management of @dev. |
137 | * The power-management code will take control of the clock reference, so |
138 | * callers should not call clk_put() on @clk after this function sucessfully |
139 | * returned. |
140 | */ |
141 | int pm_clk_add_clk(struct device *dev, struct clk *clk) |
142 | { |
143 | return __pm_clk_add(dev, NULL, clk); |
144 | } |
145 | EXPORT_SYMBOL_GPL(pm_clk_add_clk); |
146 | |
147 | |
148 | /** |
149 | * of_pm_clk_add_clk - Start using a device clock for power management. |
150 | * @dev: Device whose clock is going to be used for power management. |
151 | * @name: Name of clock that is going to be used for power management. |
152 | * |
153 | * Add the clock described in the 'clocks' device-tree node that matches |
154 | * with the 'name' provided, to the list of clocks used for the power |
155 | * management of @dev. On success, returns 0. Returns a negative error |
156 | * code if the clock is not found or cannot be added. |
157 | */ |
158 | int of_pm_clk_add_clk(struct device *dev, const char *name) |
159 | { |
160 | struct clk *clk; |
161 | int ret; |
162 | |
163 | if (!dev || !dev->of_node || !name) |
164 | return -EINVAL; |
165 | |
166 | clk = of_clk_get_by_name(dev->of_node, name); |
167 | if (IS_ERR(clk)) |
168 | return PTR_ERR(clk); |
169 | |
170 | ret = pm_clk_add_clk(dev, clk); |
171 | if (ret) { |
172 | clk_put(clk); |
173 | return ret; |
174 | } |
175 | |
176 | return 0; |
177 | } |
178 | EXPORT_SYMBOL_GPL(of_pm_clk_add_clk); |
179 | |
180 | /** |
181 | * of_pm_clk_add_clks - Start using device clock(s) for power management. |
182 | * @dev: Device whose clock(s) is going to be used for power management. |
183 | * |
184 | * Add a series of clocks described in the 'clocks' device-tree node for |
185 | * a device to the list of clocks used for the power management of @dev. |
186 | * On success, returns the number of clocks added. Returns a negative |
187 | * error code if there are no clocks in the device node for the device |
188 | * or if adding a clock fails. |
189 | */ |
190 | int of_pm_clk_add_clks(struct device *dev) |
191 | { |
192 | struct clk **clks; |
193 | int i, count; |
194 | int ret; |
195 | |
196 | if (!dev || !dev->of_node) |
197 | return -EINVAL; |
198 | |
199 | count = of_count_phandle_with_args(dev->of_node, "clocks" , |
200 | "#clock-cells" ); |
201 | if (count <= 0) |
202 | return -ENODEV; |
203 | |
204 | clks = kcalloc(count, sizeof(*clks), GFP_KERNEL); |
205 | if (!clks) |
206 | return -ENOMEM; |
207 | |
208 | for (i = 0; i < count; i++) { |
209 | clks[i] = of_clk_get(dev->of_node, i); |
210 | if (IS_ERR(clks[i])) { |
211 | ret = PTR_ERR(clks[i]); |
212 | goto error; |
213 | } |
214 | |
215 | ret = pm_clk_add_clk(dev, clks[i]); |
216 | if (ret) { |
217 | clk_put(clks[i]); |
218 | goto error; |
219 | } |
220 | } |
221 | |
222 | kfree(clks); |
223 | |
224 | return i; |
225 | |
226 | error: |
227 | while (i--) |
228 | pm_clk_remove_clk(dev, clks[i]); |
229 | |
230 | kfree(clks); |
231 | |
232 | return ret; |
233 | } |
234 | EXPORT_SYMBOL_GPL(of_pm_clk_add_clks); |
235 | |
236 | /** |
237 | * __pm_clk_remove - Destroy PM clock entry. |
238 | * @ce: PM clock entry to destroy. |
239 | */ |
240 | static void __pm_clk_remove(struct pm_clock_entry *ce) |
241 | { |
242 | if (!ce) |
243 | return; |
244 | |
245 | if (ce->status < PCE_STATUS_ERROR) { |
246 | if (ce->status == PCE_STATUS_ENABLED) |
247 | clk_disable(ce->clk); |
248 | |
249 | if (ce->status >= PCE_STATUS_ACQUIRED) { |
250 | clk_unprepare(ce->clk); |
251 | clk_put(ce->clk); |
252 | } |
253 | } |
254 | |
255 | kfree(ce->con_id); |
256 | kfree(ce); |
257 | } |
258 | |
259 | /** |
260 | * pm_clk_remove - Stop using a device clock for power management. |
261 | * @dev: Device whose clock should not be used for PM any more. |
262 | * @con_id: Connection ID of the clock. |
263 | * |
264 | * Remove the clock represented by @con_id from the list of clocks used for |
265 | * the power management of @dev. |
266 | */ |
267 | void pm_clk_remove(struct device *dev, const char *con_id) |
268 | { |
269 | struct pm_subsys_data *psd = dev_to_psd(dev); |
270 | struct pm_clock_entry *ce; |
271 | |
272 | if (!psd) |
273 | return; |
274 | |
275 | spin_lock_irq(&psd->lock); |
276 | |
277 | list_for_each_entry(ce, &psd->clock_list, node) { |
278 | if (!con_id && !ce->con_id) |
279 | goto remove; |
280 | else if (!con_id || !ce->con_id) |
281 | continue; |
282 | else if (!strcmp(con_id, ce->con_id)) |
283 | goto remove; |
284 | } |
285 | |
286 | spin_unlock_irq(&psd->lock); |
287 | return; |
288 | |
289 | remove: |
290 | list_del(&ce->node); |
291 | spin_unlock_irq(&psd->lock); |
292 | |
293 | __pm_clk_remove(ce); |
294 | } |
295 | EXPORT_SYMBOL_GPL(pm_clk_remove); |
296 | |
297 | /** |
298 | * pm_clk_remove_clk - Stop using a device clock for power management. |
299 | * @dev: Device whose clock should not be used for PM any more. |
300 | * @clk: Clock pointer |
301 | * |
302 | * Remove the clock pointed to by @clk from the list of clocks used for |
303 | * the power management of @dev. |
304 | */ |
305 | void pm_clk_remove_clk(struct device *dev, struct clk *clk) |
306 | { |
307 | struct pm_subsys_data *psd = dev_to_psd(dev); |
308 | struct pm_clock_entry *ce; |
309 | |
310 | if (!psd || !clk) |
311 | return; |
312 | |
313 | spin_lock_irq(&psd->lock); |
314 | |
315 | list_for_each_entry(ce, &psd->clock_list, node) { |
316 | if (clk == ce->clk) |
317 | goto remove; |
318 | } |
319 | |
320 | spin_unlock_irq(&psd->lock); |
321 | return; |
322 | |
323 | remove: |
324 | list_del(&ce->node); |
325 | spin_unlock_irq(&psd->lock); |
326 | |
327 | __pm_clk_remove(ce); |
328 | } |
329 | EXPORT_SYMBOL_GPL(pm_clk_remove_clk); |
330 | |
331 | /** |
332 | * pm_clk_init - Initialize a device's list of power management clocks. |
333 | * @dev: Device to initialize the list of PM clocks for. |
334 | * |
335 | * Initialize the lock and clock_list members of the device's pm_subsys_data |
336 | * object. |
337 | */ |
338 | void pm_clk_init(struct device *dev) |
339 | { |
340 | struct pm_subsys_data *psd = dev_to_psd(dev); |
341 | if (psd) |
342 | INIT_LIST_HEAD(&psd->clock_list); |
343 | } |
344 | EXPORT_SYMBOL_GPL(pm_clk_init); |
345 | |
346 | /** |
347 | * pm_clk_create - Create and initialize a device's list of PM clocks. |
348 | * @dev: Device to create and initialize the list of PM clocks for. |
349 | * |
350 | * Allocate a struct pm_subsys_data object, initialize its lock and clock_list |
351 | * members and make the @dev's power.subsys_data field point to it. |
352 | */ |
353 | int pm_clk_create(struct device *dev) |
354 | { |
355 | return dev_pm_get_subsys_data(dev); |
356 | } |
357 | EXPORT_SYMBOL_GPL(pm_clk_create); |
358 | |
359 | /** |
360 | * pm_clk_destroy - Destroy a device's list of power management clocks. |
361 | * @dev: Device to destroy the list of PM clocks for. |
362 | * |
363 | * Clear the @dev's power.subsys_data field, remove the list of clock entries |
364 | * from the struct pm_subsys_data object pointed to by it before and free |
365 | * that object. |
366 | */ |
367 | void pm_clk_destroy(struct device *dev) |
368 | { |
369 | struct pm_subsys_data *psd = dev_to_psd(dev); |
370 | struct pm_clock_entry *ce, *c; |
371 | struct list_head list; |
372 | |
373 | if (!psd) |
374 | return; |
375 | |
376 | INIT_LIST_HEAD(&list); |
377 | |
378 | spin_lock_irq(&psd->lock); |
379 | |
380 | list_for_each_entry_safe_reverse(ce, c, &psd->clock_list, node) |
381 | list_move(&ce->node, &list); |
382 | |
383 | spin_unlock_irq(&psd->lock); |
384 | |
385 | dev_pm_put_subsys_data(dev); |
386 | |
387 | list_for_each_entry_safe_reverse(ce, c, &list, node) { |
388 | list_del(&ce->node); |
389 | __pm_clk_remove(ce); |
390 | } |
391 | } |
392 | EXPORT_SYMBOL_GPL(pm_clk_destroy); |
393 | |
394 | /** |
395 | * pm_clk_suspend - Disable clocks in a device's PM clock list. |
396 | * @dev: Device to disable the clocks for. |
397 | */ |
398 | int pm_clk_suspend(struct device *dev) |
399 | { |
400 | struct pm_subsys_data *psd = dev_to_psd(dev); |
401 | struct pm_clock_entry *ce; |
402 | unsigned long flags; |
403 | |
404 | dev_dbg(dev, "%s()\n" , __func__); |
405 | |
406 | if (!psd) |
407 | return 0; |
408 | |
409 | spin_lock_irqsave(&psd->lock, flags); |
410 | |
411 | list_for_each_entry_reverse(ce, &psd->clock_list, node) { |
412 | if (ce->status < PCE_STATUS_ERROR) { |
413 | if (ce->status == PCE_STATUS_ENABLED) |
414 | clk_disable(ce->clk); |
415 | ce->status = PCE_STATUS_ACQUIRED; |
416 | } |
417 | } |
418 | |
419 | spin_unlock_irqrestore(&psd->lock, flags); |
420 | |
421 | return 0; |
422 | } |
423 | EXPORT_SYMBOL_GPL(pm_clk_suspend); |
424 | |
425 | /** |
426 | * pm_clk_resume - Enable clocks in a device's PM clock list. |
427 | * @dev: Device to enable the clocks for. |
428 | */ |
429 | int pm_clk_resume(struct device *dev) |
430 | { |
431 | struct pm_subsys_data *psd = dev_to_psd(dev); |
432 | struct pm_clock_entry *ce; |
433 | unsigned long flags; |
434 | |
435 | dev_dbg(dev, "%s()\n" , __func__); |
436 | |
437 | if (!psd) |
438 | return 0; |
439 | |
440 | spin_lock_irqsave(&psd->lock, flags); |
441 | |
442 | list_for_each_entry(ce, &psd->clock_list, node) |
443 | __pm_clk_enable(dev, ce); |
444 | |
445 | spin_unlock_irqrestore(&psd->lock, flags); |
446 | |
447 | return 0; |
448 | } |
449 | EXPORT_SYMBOL_GPL(pm_clk_resume); |
450 | |
451 | /** |
452 | * pm_clk_notify - Notify routine for device addition and removal. |
453 | * @nb: Notifier block object this function is a member of. |
454 | * @action: Operation being carried out by the caller. |
455 | * @data: Device the routine is being run for. |
456 | * |
457 | * For this function to work, @nb must be a member of an object of type |
458 | * struct pm_clk_notifier_block containing all of the requisite data. |
459 | * Specifically, the pm_domain member of that object is copied to the device's |
460 | * pm_domain field and its con_ids member is used to populate the device's list |
461 | * of PM clocks, depending on @action. |
462 | * |
463 | * If the device's pm_domain field is already populated with a value different |
464 | * from the one stored in the struct pm_clk_notifier_block object, the function |
465 | * does nothing. |
466 | */ |
467 | static int pm_clk_notify(struct notifier_block *nb, |
468 | unsigned long action, void *data) |
469 | { |
470 | struct pm_clk_notifier_block *clknb; |
471 | struct device *dev = data; |
472 | char **con_id; |
473 | int error; |
474 | |
475 | dev_dbg(dev, "%s() %ld\n" , __func__, action); |
476 | |
477 | clknb = container_of(nb, struct pm_clk_notifier_block, nb); |
478 | |
479 | switch (action) { |
480 | case BUS_NOTIFY_ADD_DEVICE: |
481 | if (dev->pm_domain) |
482 | break; |
483 | |
484 | error = pm_clk_create(dev); |
485 | if (error) |
486 | break; |
487 | |
488 | dev_pm_domain_set(dev, clknb->pm_domain); |
489 | if (clknb->con_ids[0]) { |
490 | for (con_id = clknb->con_ids; *con_id; con_id++) |
491 | pm_clk_add(dev, *con_id); |
492 | } else { |
493 | pm_clk_add(dev, NULL); |
494 | } |
495 | |
496 | break; |
497 | case BUS_NOTIFY_DEL_DEVICE: |
498 | if (dev->pm_domain != clknb->pm_domain) |
499 | break; |
500 | |
501 | dev_pm_domain_set(dev, NULL); |
502 | pm_clk_destroy(dev); |
503 | break; |
504 | } |
505 | |
506 | return 0; |
507 | } |
508 | |
509 | int pm_clk_runtime_suspend(struct device *dev) |
510 | { |
511 | int ret; |
512 | |
513 | dev_dbg(dev, "%s\n" , __func__); |
514 | |
515 | ret = pm_generic_runtime_suspend(dev); |
516 | if (ret) { |
517 | dev_err(dev, "failed to suspend device\n" ); |
518 | return ret; |
519 | } |
520 | |
521 | ret = pm_clk_suspend(dev); |
522 | if (ret) { |
523 | dev_err(dev, "failed to suspend clock\n" ); |
524 | pm_generic_runtime_resume(dev); |
525 | return ret; |
526 | } |
527 | |
528 | return 0; |
529 | } |
530 | EXPORT_SYMBOL_GPL(pm_clk_runtime_suspend); |
531 | |
532 | int pm_clk_runtime_resume(struct device *dev) |
533 | { |
534 | int ret; |
535 | |
536 | dev_dbg(dev, "%s\n" , __func__); |
537 | |
538 | ret = pm_clk_resume(dev); |
539 | if (ret) { |
540 | dev_err(dev, "failed to resume clock\n" ); |
541 | return ret; |
542 | } |
543 | |
544 | return pm_generic_runtime_resume(dev); |
545 | } |
546 | EXPORT_SYMBOL_GPL(pm_clk_runtime_resume); |
547 | |
548 | #else /* !CONFIG_PM_CLK */ |
549 | |
550 | /** |
551 | * enable_clock - Enable a device clock. |
552 | * @dev: Device whose clock is to be enabled. |
553 | * @con_id: Connection ID of the clock. |
554 | */ |
555 | static void enable_clock(struct device *dev, const char *con_id) |
556 | { |
557 | struct clk *clk; |
558 | |
559 | clk = clk_get(dev, con_id); |
560 | if (!IS_ERR(clk)) { |
561 | clk_prepare_enable(clk); |
562 | clk_put(clk); |
563 | dev_info(dev, "Runtime PM disabled, clock forced on.\n" ); |
564 | } |
565 | } |
566 | |
567 | /** |
568 | * disable_clock - Disable a device clock. |
569 | * @dev: Device whose clock is to be disabled. |
570 | * @con_id: Connection ID of the clock. |
571 | */ |
572 | static void disable_clock(struct device *dev, const char *con_id) |
573 | { |
574 | struct clk *clk; |
575 | |
576 | clk = clk_get(dev, con_id); |
577 | if (!IS_ERR(clk)) { |
578 | clk_disable_unprepare(clk); |
579 | clk_put(clk); |
580 | dev_info(dev, "Runtime PM disabled, clock forced off.\n" ); |
581 | } |
582 | } |
583 | |
584 | /** |
585 | * pm_clk_notify - Notify routine for device addition and removal. |
586 | * @nb: Notifier block object this function is a member of. |
587 | * @action: Operation being carried out by the caller. |
588 | * @data: Device the routine is being run for. |
589 | * |
590 | * For this function to work, @nb must be a member of an object of type |
591 | * struct pm_clk_notifier_block containing all of the requisite data. |
592 | * Specifically, the con_ids member of that object is used to enable or disable |
593 | * the device's clocks, depending on @action. |
594 | */ |
595 | static int pm_clk_notify(struct notifier_block *nb, |
596 | unsigned long action, void *data) |
597 | { |
598 | struct pm_clk_notifier_block *clknb; |
599 | struct device *dev = data; |
600 | char **con_id; |
601 | |
602 | dev_dbg(dev, "%s() %ld\n" , __func__, action); |
603 | |
604 | clknb = container_of(nb, struct pm_clk_notifier_block, nb); |
605 | |
606 | switch (action) { |
607 | case BUS_NOTIFY_BIND_DRIVER: |
608 | if (clknb->con_ids[0]) { |
609 | for (con_id = clknb->con_ids; *con_id; con_id++) |
610 | enable_clock(dev, *con_id); |
611 | } else { |
612 | enable_clock(dev, NULL); |
613 | } |
614 | break; |
615 | case BUS_NOTIFY_DRIVER_NOT_BOUND: |
616 | case BUS_NOTIFY_UNBOUND_DRIVER: |
617 | if (clknb->con_ids[0]) { |
618 | for (con_id = clknb->con_ids; *con_id; con_id++) |
619 | disable_clock(dev, *con_id); |
620 | } else { |
621 | disable_clock(dev, NULL); |
622 | } |
623 | break; |
624 | } |
625 | |
626 | return 0; |
627 | } |
628 | |
629 | #endif /* !CONFIG_PM_CLK */ |
630 | |
631 | /** |
632 | * pm_clk_add_notifier - Add bus type notifier for power management clocks. |
633 | * @bus: Bus type to add the notifier to. |
634 | * @clknb: Notifier to be added to the given bus type. |
635 | * |
636 | * The nb member of @clknb is not expected to be initialized and its |
637 | * notifier_call member will be replaced with pm_clk_notify(). However, |
638 | * the remaining members of @clknb should be populated prior to calling this |
639 | * routine. |
640 | */ |
641 | void pm_clk_add_notifier(struct bus_type *bus, |
642 | struct pm_clk_notifier_block *clknb) |
643 | { |
644 | if (!bus || !clknb) |
645 | return; |
646 | |
647 | clknb->nb.notifier_call = pm_clk_notify; |
648 | bus_register_notifier(bus, &clknb->nb); |
649 | } |
650 | EXPORT_SYMBOL_GPL(pm_clk_add_notifier); |
651 | |