blob: b97294e2d95ba022d9ac61807315218bdab530d0 [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. Wysocki3d5c3032011-07-01 22:13:44 +020020struct pm_clk_data {
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020021 struct list_head clock_list;
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +020022 spinlock_t lock;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020023};
24
25enum pce_status {
26 PCE_STATUS_NONE = 0,
27 PCE_STATUS_ACQUIRED,
28 PCE_STATUS_ENABLED,
29 PCE_STATUS_ERROR,
30};
31
32struct pm_clock_entry {
33 struct list_head node;
34 char *con_id;
35 struct clk *clk;
36 enum pce_status status;
37};
38
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020039static struct pm_clk_data *__to_pcd(struct device *dev)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020040{
41 return dev ? dev->power.subsys_data : NULL;
42}
43
44/**
Rafael J. Wysockie8b364b2011-09-26 19:40:23 +020045 * 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/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020061 * pm_clk_add - Start using a device clock for power management.
62 * @dev: Device whose clock is going to be used for power management.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020063 * @con_id: Connection ID of the clock.
64 *
65 * Add the clock represented by @con_id to the list of clocks used for
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020066 * the power management of @dev.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020067 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020068int pm_clk_add(struct device *dev, const char *con_id)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020069{
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020070 struct pm_clk_data *pcd = __to_pcd(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020071 struct pm_clock_entry *ce;
72
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020073 if (!pcd)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020074 return -EINVAL;
75
76 ce = kzalloc(sizeof(*ce), GFP_KERNEL);
77 if (!ce) {
78 dev_err(dev, "Not enough memory for clock entry.\n");
79 return -ENOMEM;
80 }
81
82 if (con_id) {
83 ce->con_id = kstrdup(con_id, GFP_KERNEL);
84 if (!ce->con_id) {
85 dev_err(dev,
86 "Not enough memory for clock connection ID.\n");
87 kfree(ce);
88 return -ENOMEM;
89 }
90 }
91
Rafael J. Wysockie8b364b2011-09-26 19:40:23 +020092 pm_clk_acquire(dev, ce);
93
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +020094 spin_lock_irq(&pcd->lock);
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +020095 list_add_tail(&ce->node, &pcd->clock_list);
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +020096 spin_unlock_irq(&pcd->lock);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +020097 return 0;
98}
99
100/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200101 * __pm_clk_remove - Destroy PM clock entry.
102 * @ce: PM clock entry to destroy.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200103 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200104static void __pm_clk_remove(struct pm_clock_entry *ce)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200105{
106 if (!ce)
107 return;
108
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200109 if (ce->status < PCE_STATUS_ERROR) {
110 if (ce->status == PCE_STATUS_ENABLED)
111 clk_disable(ce->clk);
112
113 if (ce->status >= PCE_STATUS_ACQUIRED)
114 clk_put(ce->clk);
115 }
116
117 if (ce->con_id)
118 kfree(ce->con_id);
119
120 kfree(ce);
121}
122
123/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200124 * pm_clk_remove - Stop using a device clock for power management.
125 * @dev: Device whose clock should not be used for PM any more.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200126 * @con_id: Connection ID of the clock.
127 *
128 * Remove the clock represented by @con_id from the list of clocks used for
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200129 * the power management of @dev.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200130 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200131void pm_clk_remove(struct device *dev, const char *con_id)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200132{
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200133 struct pm_clk_data *pcd = __to_pcd(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200134 struct pm_clock_entry *ce;
135
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200136 if (!pcd)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200137 return;
138
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200139 spin_lock_irq(&pcd->lock);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200140
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200141 list_for_each_entry(ce, &pcd->clock_list, node) {
Rafael J. Wysockie8b364b2011-09-26 19:40:23 +0200142 if (!con_id && !ce->con_id)
143 goto remove;
144 else if (!con_id || !ce->con_id)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200145 continue;
Rafael J. Wysockie8b364b2011-09-26 19:40:23 +0200146 else if (!strcmp(con_id, ce->con_id))
147 goto remove;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200148 }
149
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200150 spin_unlock_irq(&pcd->lock);
Rafael J. Wysockie8b364b2011-09-26 19:40:23 +0200151 return;
152
153 remove:
154 list_del(&ce->node);
155 spin_unlock_irq(&pcd->lock);
156
157 __pm_clk_remove(ce);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200158}
159
160/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200161 * pm_clk_init - Initialize a device's list of power management clocks.
162 * @dev: Device to initialize the list of PM clocks for.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200163 *
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200164 * Allocate a struct pm_clk_data object, initialize its lock member and
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200165 * make the @dev's power.subsys_data field point to it.
166 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200167int pm_clk_init(struct device *dev)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200168{
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200169 struct pm_clk_data *pcd;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200170
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200171 pcd = kzalloc(sizeof(*pcd), GFP_KERNEL);
172 if (!pcd) {
173 dev_err(dev, "Not enough memory for PM clock data.\n");
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200174 return -ENOMEM;
175 }
176
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200177 INIT_LIST_HEAD(&pcd->clock_list);
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200178 spin_lock_init(&pcd->lock);
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200179 dev->power.subsys_data = pcd;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200180 return 0;
181}
182
183/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200184 * pm_clk_destroy - Destroy a device's list of power management clocks.
185 * @dev: Device to destroy the list of PM clocks for.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200186 *
187 * Clear the @dev's power.subsys_data field, remove the list of clock entries
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200188 * from the struct pm_clk_data object pointed to by it before and free
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200189 * that object.
190 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200191void pm_clk_destroy(struct device *dev)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200192{
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200193 struct pm_clk_data *pcd = __to_pcd(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200194 struct pm_clock_entry *ce, *c;
Rafael J. Wysockie8b364b2011-09-26 19:40:23 +0200195 struct list_head list;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200196
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200197 if (!pcd)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200198 return;
199
200 dev->power.subsys_data = NULL;
Rafael J. Wysockie8b364b2011-09-26 19:40:23 +0200201 INIT_LIST_HEAD(&list);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200202
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200203 spin_lock_irq(&pcd->lock);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200204
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200205 list_for_each_entry_safe_reverse(ce, c, &pcd->clock_list, node)
Rafael J. Wysockie8b364b2011-09-26 19:40:23 +0200206 list_move(&ce->node, &list);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200207
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200208 spin_unlock_irq(&pcd->lock);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200209
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200210 kfree(pcd);
Rafael J. Wysockie8b364b2011-09-26 19:40:23 +0200211
212 list_for_each_entry_safe_reverse(ce, c, &list, node) {
213 list_del(&ce->node);
214 __pm_clk_remove(ce);
215 }
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200216}
217
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200218#endif /* CONFIG_PM */
219
220#ifdef CONFIG_PM_RUNTIME
221
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200222/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200223 * pm_clk_suspend - Disable clocks in a device's PM clock list.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200224 * @dev: Device to disable the clocks for.
225 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200226int pm_clk_suspend(struct device *dev)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200227{
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200228 struct pm_clk_data *pcd = __to_pcd(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200229 struct pm_clock_entry *ce;
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200230 unsigned long flags;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200231
232 dev_dbg(dev, "%s()\n", __func__);
233
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200234 if (!pcd)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200235 return 0;
236
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200237 spin_lock_irqsave(&pcd->lock, flags);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200238
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200239 list_for_each_entry_reverse(ce, &pcd->clock_list, node) {
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200240 if (ce->status < PCE_STATUS_ERROR) {
241 clk_disable(ce->clk);
242 ce->status = PCE_STATUS_ACQUIRED;
243 }
244 }
245
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200246 spin_unlock_irqrestore(&pcd->lock, flags);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200247
248 return 0;
249}
250
251/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200252 * pm_clk_resume - Enable clocks in a device's PM clock list.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200253 * @dev: Device to enable the clocks for.
254 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200255int pm_clk_resume(struct device *dev)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200256{
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200257 struct pm_clk_data *pcd = __to_pcd(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200258 struct pm_clock_entry *ce;
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200259 unsigned long flags;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200260
261 dev_dbg(dev, "%s()\n", __func__);
262
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200263 if (!pcd)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200264 return 0;
265
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200266 spin_lock_irqsave(&pcd->lock, flags);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200267
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200268 list_for_each_entry(ce, &pcd->clock_list, node) {
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200269 if (ce->status < PCE_STATUS_ERROR) {
270 clk_enable(ce->clk);
271 ce->status = PCE_STATUS_ENABLED;
272 }
273 }
274
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200275 spin_unlock_irqrestore(&pcd->lock, flags);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200276
277 return 0;
278}
279
280/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200281 * pm_clk_notify - Notify routine for device addition and removal.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200282 * @nb: Notifier block object this function is a member of.
283 * @action: Operation being carried out by the caller.
284 * @data: Device the routine is being run for.
285 *
286 * For this function to work, @nb must be a member of an object of type
287 * struct pm_clk_notifier_block containing all of the requisite data.
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200288 * Specifically, the pm_domain member of that object is copied to the device's
289 * pm_domain field and its con_ids member is used to populate the device's list
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200290 * of PM clocks, depending on @action.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200291 *
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200292 * If the device's pm_domain field is already populated with a value different
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200293 * from the one stored in the struct pm_clk_notifier_block object, the function
294 * does nothing.
295 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200296static int pm_clk_notify(struct notifier_block *nb,
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200297 unsigned long action, void *data)
298{
299 struct pm_clk_notifier_block *clknb;
300 struct device *dev = data;
Rafael J. Wysocki3b3eca32011-06-07 23:34:58 +0200301 char **con_id;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200302 int error;
303
304 dev_dbg(dev, "%s() %ld\n", __func__, action);
305
306 clknb = container_of(nb, struct pm_clk_notifier_block, nb);
307
308 switch (action) {
309 case BUS_NOTIFY_ADD_DEVICE:
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200310 if (dev->pm_domain)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200311 break;
312
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200313 error = pm_clk_init(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200314 if (error)
315 break;
316
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200317 dev->pm_domain = clknb->pm_domain;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200318 if (clknb->con_ids[0]) {
Rafael J. Wysocki3b3eca32011-06-07 23:34:58 +0200319 for (con_id = clknb->con_ids; *con_id; con_id++)
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200320 pm_clk_add(dev, *con_id);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200321 } else {
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200322 pm_clk_add(dev, NULL);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200323 }
324
325 break;
326 case BUS_NOTIFY_DEL_DEVICE:
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200327 if (dev->pm_domain != clknb->pm_domain)
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200328 break;
329
Rafael J. Wysocki564b9052011-06-23 01:52:55 +0200330 dev->pm_domain = NULL;
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200331 pm_clk_destroy(dev);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200332 break;
333 }
334
335 return 0;
336}
337
338#else /* !CONFIG_PM_RUNTIME */
339
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200340#ifdef CONFIG_PM
341
342/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200343 * pm_clk_suspend - Disable clocks in a device's PM clock list.
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200344 * @dev: Device to disable the clocks for.
345 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200346int pm_clk_suspend(struct device *dev)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200347{
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200348 struct pm_clk_data *pcd = __to_pcd(dev);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200349 struct pm_clock_entry *ce;
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200350 unsigned long flags;
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200351
352 dev_dbg(dev, "%s()\n", __func__);
353
354 /* If there is no driver, the clocks are already disabled. */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200355 if (!pcd || !dev->driver)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200356 return 0;
357
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200358 spin_lock_irqsave(&pcd->lock, flags);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200359
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200360 list_for_each_entry_reverse(ce, &pcd->clock_list, node)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200361 clk_disable(ce->clk);
362
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200363 spin_unlock_irqrestore(&pcd->lock, flags);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200364
365 return 0;
366}
367
368/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200369 * pm_clk_resume - Enable clocks in a device's PM clock list.
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200370 * @dev: Device to enable the clocks for.
371 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200372int pm_clk_resume(struct device *dev)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200373{
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200374 struct pm_clk_data *pcd = __to_pcd(dev);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200375 struct pm_clock_entry *ce;
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200376 unsigned long flags;
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200377
378 dev_dbg(dev, "%s()\n", __func__);
379
380 /* If there is no driver, the clocks should remain disabled. */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200381 if (!pcd || !dev->driver)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200382 return 0;
383
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200384 spin_lock_irqsave(&pcd->lock, flags);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200385
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200386 list_for_each_entry(ce, &pcd->clock_list, node)
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200387 clk_enable(ce->clk);
388
Rafael J. Wysockib7ab83e2011-08-24 21:40:56 +0200389 spin_unlock_irqrestore(&pcd->lock, flags);
Rafael J. Wysockib7b95922011-07-01 22:13:37 +0200390
391 return 0;
392}
393
394#endif /* CONFIG_PM */
395
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200396/**
397 * enable_clock - Enable a device clock.
398 * @dev: Device whose clock is to be enabled.
399 * @con_id: Connection ID of the clock.
400 */
401static void enable_clock(struct device *dev, const char *con_id)
402{
403 struct clk *clk;
404
405 clk = clk_get(dev, con_id);
406 if (!IS_ERR(clk)) {
407 clk_enable(clk);
408 clk_put(clk);
409 dev_info(dev, "Runtime PM disabled, clock forced on.\n");
410 }
411}
412
413/**
414 * disable_clock - Disable a device clock.
415 * @dev: Device whose clock is to be disabled.
416 * @con_id: Connection ID of the clock.
417 */
418static void disable_clock(struct device *dev, const char *con_id)
419{
420 struct clk *clk;
421
422 clk = clk_get(dev, con_id);
423 if (!IS_ERR(clk)) {
424 clk_disable(clk);
425 clk_put(clk);
426 dev_info(dev, "Runtime PM disabled, clock forced off.\n");
427 }
428}
429
430/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200431 * pm_clk_notify - Notify routine for device addition and removal.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200432 * @nb: Notifier block object this function is a member of.
433 * @action: Operation being carried out by the caller.
434 * @data: Device the routine is being run for.
435 *
436 * For this function to work, @nb must be a member of an object of type
437 * struct pm_clk_notifier_block containing all of the requisite data.
438 * Specifically, the con_ids member of that object is used to enable or disable
439 * the device's clocks, depending on @action.
440 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200441static int pm_clk_notify(struct notifier_block *nb,
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200442 unsigned long action, void *data)
443{
444 struct pm_clk_notifier_block *clknb;
445 struct device *dev = data;
Rafael J. Wysocki3b3eca32011-06-07 23:34:58 +0200446 char **con_id;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200447
448 dev_dbg(dev, "%s() %ld\n", __func__, action);
449
450 clknb = container_of(nb, struct pm_clk_notifier_block, nb);
451
452 switch (action) {
Rafael J. Wysocki4d1518f2011-06-21 23:24:33 +0200453 case BUS_NOTIFY_BIND_DRIVER:
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200454 if (clknb->con_ids[0]) {
Rafael J. Wysocki3b3eca32011-06-07 23:34:58 +0200455 for (con_id = clknb->con_ids; *con_id; con_id++)
456 enable_clock(dev, *con_id);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200457 } else {
458 enable_clock(dev, NULL);
459 }
460 break;
Rafael J. Wysocki4d1518f2011-06-21 23:24:33 +0200461 case BUS_NOTIFY_UNBOUND_DRIVER:
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200462 if (clknb->con_ids[0]) {
Rafael J. Wysocki3b3eca32011-06-07 23:34:58 +0200463 for (con_id = clknb->con_ids; *con_id; con_id++)
464 disable_clock(dev, *con_id);
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200465 } else {
466 disable_clock(dev, NULL);
467 }
468 break;
469 }
470
471 return 0;
472}
473
474#endif /* !CONFIG_PM_RUNTIME */
475
476/**
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200477 * pm_clk_add_notifier - Add bus type notifier for power management clocks.
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200478 * @bus: Bus type to add the notifier to.
479 * @clknb: Notifier to be added to the given bus type.
480 *
481 * The nb member of @clknb is not expected to be initialized and its
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200482 * notifier_call member will be replaced with pm_clk_notify(). However,
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200483 * the remaining members of @clknb should be populated prior to calling this
484 * routine.
485 */
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200486void pm_clk_add_notifier(struct bus_type *bus,
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200487 struct pm_clk_notifier_block *clknb)
488{
489 if (!bus || !clknb)
490 return;
491
Rafael J. Wysocki3d5c3032011-07-01 22:13:44 +0200492 clknb->nb.notifier_call = pm_clk_notify;
Rafael J. Wysocki85eb8c82011-04-30 00:25:44 +0200493 bus_register_notifier(bus, &clknb->nb);
494}