blob: b7f1db4f594531f580b6fb4f3e70e09a9cbb8417 [file] [log] [blame]
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +02001/*
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/init.h>
10#include <linux/kernel.h>
11#include <linux/io.h>
12#include <linux/pm.h>
13#include <linux/pm_runtime.h>
14#include <linux/clk.h>
15#include <linux/slab.h>
16#include <linux/err.h>
17
Rafael J. Wysockib7b95922011-07-01 22:13:37 +020018#ifdef CONFIG_PM
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020019
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020020enum pce_status {
21 PCE_STATUS_NONE = 0,
22 PCE_STATUS_ACQUIRED,
23 PCE_STATUS_ENABLED,
24 PCE_STATUS_ERROR,
25};
26
27struct pm_clock_entry {
28 struct list_head node;
29 char *con_id;
30 struct clk *clk;
31 enum pce_status status;
32};
33
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020034/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020035 * pm_clk_add - Start using a device clock for power management.
36 * @dev: Device whose clock is going to be used for power management.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020037 * @con_id: Connection ID of the clock.
38 *
39 * Add the clock represented by @con_id to the list of clocks used for
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020040 * the power management of @dev.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020041 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020042int pm_clk_add(struct device *dev, const char *con_id)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020043{
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +020044 struct pm_subsys_data *psd = dev_to_psd(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020045 struct pm_clock_entry *ce;
46
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +020047 if (!psd)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020048 return -EINVAL;
49
50 ce = kzalloc(sizeof(*ce), GFP_KERNEL);
51 if (!ce) {
52 dev_err(dev, "Not enough memory for clock entry.\n");
53 return -ENOMEM;
54 }
55
56 if (con_id) {
57 ce->con_id = kstrdup(con_id, GFP_KERNEL);
58 if (!ce->con_id) {
59 dev_err(dev,
60 "Not enough memory for clock connection ID.\n");
61 kfree(ce);
62 return -ENOMEM;
63 }
64 }
65
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +020066 spin_lock_irq(&psd->lock);
67 list_add_tail(&ce->node, &psd->clock_list);
68 spin_unlock_irq(&psd->lock);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020069 return 0;
70}
71
72/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020073 * __pm_clk_remove - Destroy PM clock entry.
74 * @ce: PM clock entry to destroy.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020075 *
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +020076 * This routine must be called under the spinlock protecting the PM list of
77 * clocks corresponding the the @ce's device.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020078 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020079static void __pm_clk_remove(struct pm_clock_entry *ce)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020080{
81 if (!ce)
82 return;
83
84 list_del(&ce->node);
85
86 if (ce->status < PCE_STATUS_ERROR) {
87 if (ce->status == PCE_STATUS_ENABLED)
88 clk_disable(ce->clk);
89
90 if (ce->status >= PCE_STATUS_ACQUIRED)
91 clk_put(ce->clk);
92 }
93
94 if (ce->con_id)
95 kfree(ce->con_id);
96
97 kfree(ce);
98}
99
100/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200101 * pm_clk_remove - Stop using a device clock for power management.
102 * @dev: Device whose clock should not be used for PM any more.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200103 * @con_id: Connection ID of the clock.
104 *
105 * Remove the clock represented by @con_id from the list of clocks used for
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200106 * the power management of @dev.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200107 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200108void pm_clk_remove(struct device *dev, const char *con_id)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200109{
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200110 struct pm_subsys_data *psd = dev_to_psd(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200111 struct pm_clock_entry *ce;
112
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200113 if (!psd)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200114 return;
115
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200116 spin_lock_irq(&psd->lock);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200117
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200118 list_for_each_entry(ce, &psd->clock_list, node) {
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200119 if (!con_id && !ce->con_id) {
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200120 __pm_clk_remove(ce);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200121 break;
122 } else if (!con_id || !ce->con_id) {
123 continue;
124 } else if (!strcmp(con_id, ce->con_id)) {
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200125 __pm_clk_remove(ce);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200126 break;
127 }
128 }
129
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200130 spin_unlock_irq(&psd->lock);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200131}
132
133/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200134 * pm_clk_init - Initialize a device's list of power management clocks.
135 * @dev: Device to initialize the list of PM clocks for.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200136 *
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200137 * Initialize the lock and clock_list members of the device's pm_subsys_data
138 * object.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200139 */
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200140void pm_clk_init(struct device *dev)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200141{
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200142 struct pm_subsys_data *psd = dev_to_psd(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200143
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200144 if (!psd)
145 return;
146
147 INIT_LIST_HEAD(&psd->clock_list);
148 spin_lock_init(&psd->lock);
149}
150
151/**
152 * pm_clk_create - Create and initialize a device's list of PM clocks.
153 * @dev: Device to create and initialize the list of PM clocks for.
154 *
155 * Allocate a struct pm_subsys_data object, initialize its lock and clock_list
156 * members and make the @dev's power.subsys_data field point to it.
157 */
158int pm_clk_create(struct device *dev)
159{
160 struct pm_subsys_data *psd;
161
162 psd = kzalloc(sizeof(*psd), GFP_KERNEL);
163 if (!psd) {
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200164 dev_err(dev, "Not enough memory for PM clock data.\n");
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200165 return -ENOMEM;
166 }
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200167 dev->power.subsys_data = psd;
168 pm_clk_init(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200169 return 0;
170}
171
172/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200173 * pm_clk_destroy - Destroy a device's list of power management clocks.
174 * @dev: Device to destroy the list of PM clocks for.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200175 *
176 * Clear the @dev's power.subsys_data field, remove the list of clock entries
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200177 * from the struct pm_subsys_data object pointed to by it before and free
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200178 * that object.
179 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200180void pm_clk_destroy(struct device *dev)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200181{
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200182 struct pm_subsys_data *psd = dev_to_psd(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200183 struct pm_clock_entry *ce, *c;
184
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200185 if (!psd)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200186 return;
187
188 dev->power.subsys_data = NULL;
189
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200190 spin_lock_irq(&psd->lock);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200191
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200192 list_for_each_entry_safe_reverse(ce, c, &psd->clock_list, node)
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200193 __pm_clk_remove(ce);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200194
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200195 spin_unlock_irq(&psd->lock);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200196
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200197 kfree(psd);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200198}
199
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200200#endif /* CONFIG_PM */
201
202#ifdef CONFIG_PM_RUNTIME
203
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200204/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200205 * pm_clk_acquire - Acquire a device clock.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200206 * @dev: Device whose clock is to be acquired.
207 * @con_id: Connection ID of the clock.
208 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200209static void pm_clk_acquire(struct device *dev,
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200210 struct pm_clock_entry *ce)
211{
212 ce->clk = clk_get(dev, ce->con_id);
213 if (IS_ERR(ce->clk)) {
214 ce->status = PCE_STATUS_ERROR;
215 } else {
216 ce->status = PCE_STATUS_ACQUIRED;
217 dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
218 }
219}
220
221/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200222 * pm_clk_suspend - Disable clocks in a device's PM clock list.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200223 * @dev: Device to disable the clocks for.
224 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200225int pm_clk_suspend(struct device *dev)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200226{
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200227 struct pm_subsys_data *psd = dev_to_psd(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200228 struct pm_clock_entry *ce;
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200229 unsigned long flags;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200230
231 dev_dbg(dev, "%s()\n", __func__);
232
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200233 if (!psd)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200234 return 0;
235
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200236 spin_lock_irqsave(&psd->lock, flags);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200237
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200238 list_for_each_entry_reverse(ce, &psd->clock_list, node) {
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200239 if (ce->status == PCE_STATUS_NONE)
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200240 pm_clk_acquire(dev, ce);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200241
242 if (ce->status < PCE_STATUS_ERROR) {
243 clk_disable(ce->clk);
244 ce->status = PCE_STATUS_ACQUIRED;
245 }
246 }
247
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200248 spin_unlock_irqrestore(&psd->lock, flags);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200249
250 return 0;
251}
252
253/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200254 * pm_clk_resume - Enable clocks in a device's PM clock list.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200255 * @dev: Device to enable the clocks for.
256 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200257int pm_clk_resume(struct device *dev)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200258{
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200259 struct pm_subsys_data *psd = dev_to_psd(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200260 struct pm_clock_entry *ce;
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200261 unsigned long flags;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200262
263 dev_dbg(dev, "%s()\n", __func__);
264
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200265 if (!psd)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200266 return 0;
267
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200268 spin_lock_irqsave(&psd->lock, flags);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200269
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200270 list_for_each_entry(ce, &psd->clock_list, node) {
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200271 if (ce->status == PCE_STATUS_NONE)
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200272 pm_clk_acquire(dev, ce);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200273
274 if (ce->status < PCE_STATUS_ERROR) {
275 clk_enable(ce->clk);
276 ce->status = PCE_STATUS_ENABLED;
277 }
278 }
279
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200280 spin_unlock_irqrestore(&psd->lock, flags);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200281
282 return 0;
283}
284
285/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200286 * pm_clk_notify - Notify routine for device addition and removal.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200287 * @nb: Notifier block object this function is a member of.
288 * @action: Operation being carried out by the caller.
289 * @data: Device the routine is being run for.
290 *
291 * For this function to work, @nb must be a member of an object of type
292 * struct pm_clk_notifier_block containing all of the requisite data.
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200293 * Specifically, the pm_domain member of that object is copied to the device's
294 * pm_domain field and its con_ids member is used to populate the device's list
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200295 * of PM clocks, depending on @action.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200296 *
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200297 * If the device's pm_domain field is already populated with a value different
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200298 * from the one stored in the struct pm_clk_notifier_block object, the function
299 * does nothing.
300 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200301static int pm_clk_notify(struct notifier_block *nb,
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200302 unsigned long action, void *data)
303{
304 struct pm_clk_notifier_block *clknb;
305 struct device *dev = data;
Rafael J. Wysocki3b3eca32011-06-07 23:34:58 +0200306 char **con_id;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200307 int error;
308
309 dev_dbg(dev, "%s() %ld\n", __func__, action);
310
311 clknb = container_of(nb, struct pm_clk_notifier_block, nb);
312
313 switch (action) {
314 case BUS_NOTIFY_ADD_DEVICE:
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200315 if (dev->pm_domain)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200316 break;
317
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200318 error = pm_clk_create(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200319 if (error)
320 break;
321
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200322 dev->pm_domain = clknb->pm_domain;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200323 if (clknb->con_ids[0]) {
Rafael J. Wysocki3b3eca32011-06-07 23:34:58 +0200324 for (con_id = clknb->con_ids; *con_id; con_id++)
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200325 pm_clk_add(dev, *con_id);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200326 } else {
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200327 pm_clk_add(dev, NULL);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200328 }
329
330 break;
331 case BUS_NOTIFY_DEL_DEVICE:
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200332 if (dev->pm_domain != clknb->pm_domain)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200333 break;
334
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200335 dev->pm_domain = NULL;
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200336 pm_clk_destroy(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200337 break;
338 }
339
340 return 0;
341}
342
343#else /* !CONFIG_PM_RUNTIME */
344
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200345#ifdef CONFIG_PM
346
347/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200348 * pm_clk_suspend - Disable clocks in a device's PM clock list.
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200349 * @dev: Device to disable the clocks for.
350 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200351int pm_clk_suspend(struct device *dev)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200352{
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200353 struct pm_subsys_data *psd = dev_to_psd(dev);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200354 struct pm_clock_entry *ce;
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200355 unsigned long flags;
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200356
357 dev_dbg(dev, "%s()\n", __func__);
358
359 /* If there is no driver, the clocks are already disabled. */
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200360 if (!psd || !dev->driver)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200361 return 0;
362
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200363 spin_lock_irqsave(&psd->lock, flags);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200364
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200365 list_for_each_entry_reverse(ce, &psd->clock_list, node)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200366 clk_disable(ce->clk);
367
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200368 spin_unlock_irqrestore(&psd->lock, flags);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200369
370 return 0;
371}
372
373/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200374 * pm_clk_resume - Enable clocks in a device's PM clock list.
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200375 * @dev: Device to enable the clocks for.
376 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200377int pm_clk_resume(struct device *dev)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200378{
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200379 struct pm_subsys_data *psd = dev_to_psd(dev);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200380 struct pm_clock_entry *ce;
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200381 unsigned long flags;
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200382
383 dev_dbg(dev, "%s()\n", __func__);
384
385 /* If there is no driver, the clocks should remain disabled. */
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200386 if (!psd || !dev->driver)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200387 return 0;
388
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200389 spin_lock_irqsave(&psd->lock, flags);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200390
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200391 list_for_each_entry(ce, &psd->clock_list, node)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200392 clk_enable(ce->clk);
393
Rafael J. Wysocki5c095a02011-08-25 15:33:50 +0200394 spin_unlock_irqrestore(&psd->lock, flags);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200395
396 return 0;
397}
398
399#endif /* CONFIG_PM */
400
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200401/**
402 * enable_clock - Enable a device clock.
403 * @dev: Device whose clock is to be enabled.
404 * @con_id: Connection ID of the clock.
405 */
406static void enable_clock(struct device *dev, const char *con_id)
407{
408 struct clk *clk;
409
410 clk = clk_get(dev, con_id);
411 if (!IS_ERR(clk)) {
412 clk_enable(clk);
413 clk_put(clk);
414 dev_info(dev, "Runtime PM disabled, clock forced on.\n");
415 }
416}
417
418/**
419 * disable_clock - Disable a device clock.
420 * @dev: Device whose clock is to be disabled.
421 * @con_id: Connection ID of the clock.
422 */
423static void disable_clock(struct device *dev, const char *con_id)
424{
425 struct clk *clk;
426
427 clk = clk_get(dev, con_id);
428 if (!IS_ERR(clk)) {
429 clk_disable(clk);
430 clk_put(clk);
431 dev_info(dev, "Runtime PM disabled, clock forced off.\n");
432 }
433}
434
435/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200436 * pm_clk_notify - Notify routine for device addition and removal.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200437 * @nb: Notifier block object this function is a member of.
438 * @action: Operation being carried out by the caller.
439 * @data: Device the routine is being run for.
440 *
441 * For this function to work, @nb must be a member of an object of type
442 * struct pm_clk_notifier_block containing all of the requisite data.
443 * Specifically, the con_ids member of that object is used to enable or disable
444 * the device's clocks, depending on @action.
445 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200446static int pm_clk_notify(struct notifier_block *nb,
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200447 unsigned long action, void *data)
448{
449 struct pm_clk_notifier_block *clknb;
450 struct device *dev = data;
Rafael J. Wysocki3b3eca32011-06-07 23:34:58 +0200451 char **con_id;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200452
453 dev_dbg(dev, "%s() %ld\n", __func__, action);
454
455 clknb = container_of(nb, struct pm_clk_notifier_block, nb);
456
457 switch (action) {
Rafael J. Wysocki4d1518f2011-06-21 23:24:33 +0200458 case BUS_NOTIFY_BIND_DRIVER:
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200459 if (clknb->con_ids[0]) {
Rafael J. Wysocki3b3eca32011-06-07 23:34:58 +0200460 for (con_id = clknb->con_ids; *con_id; con_id++)
461 enable_clock(dev, *con_id);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200462 } else {
463 enable_clock(dev, NULL);
464 }
465 break;
Rafael J. Wysocki4d1518f2011-06-21 23:24:33 +0200466 case BUS_NOTIFY_UNBOUND_DRIVER:
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200467 if (clknb->con_ids[0]) {
Rafael J. Wysocki3b3eca32011-06-07 23:34:58 +0200468 for (con_id = clknb->con_ids; *con_id; con_id++)
469 disable_clock(dev, *con_id);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200470 } else {
471 disable_clock(dev, NULL);
472 }
473 break;
474 }
475
476 return 0;
477}
478
479#endif /* !CONFIG_PM_RUNTIME */
480
481/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200482 * pm_clk_add_notifier - Add bus type notifier for power management clocks.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200483 * @bus: Bus type to add the notifier to.
484 * @clknb: Notifier to be added to the given bus type.
485 *
486 * The nb member of @clknb is not expected to be initialized and its
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200487 * notifier_call member will be replaced with pm_clk_notify(). However,
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200488 * the remaining members of @clknb should be populated prior to calling this
489 * routine.
490 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200491void pm_clk_add_notifier(struct bus_type *bus,
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200492 struct pm_clk_notifier_block *clknb)
493{
494 if (!bus || !clknb)
495 return;
496
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200497 clknb->nb.notifier_call = pm_clk_notify;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200498 bus_register_notifier(bus, &clknb->nb);
499}