~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/base/power/clock_ops.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/init.h>
 
10
#include <linux/kernel.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/slab.h>
 
16
#include <linux/err.h>
 
17
 
 
18
#ifdef CONFIG_PM
 
19
 
 
20
enum pce_status {
 
21
        PCE_STATUS_NONE = 0,
 
22
        PCE_STATUS_ACQUIRED,
 
23
        PCE_STATUS_ENABLED,
 
24
        PCE_STATUS_ERROR,
 
25
};
 
26
 
 
27
struct pm_clock_entry {
 
28
        struct list_head node;
 
29
        char *con_id;
 
30
        struct clk *clk;
 
31
        enum pce_status status;
 
32
};
 
33
 
 
34
/**
 
35
 * pm_clk_acquire - Acquire a device clock.
 
36
 * @dev: Device whose clock is to be acquired.
 
37
 * @ce: PM clock entry corresponding to the clock.
 
38
 */
 
39
static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce)
 
40
{
 
41
        ce->clk = clk_get(dev, ce->con_id);
 
42
        if (IS_ERR(ce->clk)) {
 
43
                ce->status = PCE_STATUS_ERROR;
 
44
        } else {
 
45
                ce->status = PCE_STATUS_ACQUIRED;
 
46
                dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
 
47
        }
 
48
}
 
49
 
 
50
/**
 
51
 * pm_clk_add - Start using a device clock for power management.
 
52
 * @dev: Device whose clock is going to be used for power management.
 
53
 * @con_id: Connection ID of the clock.
 
54
 *
 
55
 * Add the clock represented by @con_id to the list of clocks used for
 
56
 * the power management of @dev.
 
57
 */
 
58
int pm_clk_add(struct device *dev, const char *con_id)
 
59
{
 
60
        struct pm_subsys_data *psd = dev_to_psd(dev);
 
61
        struct pm_clock_entry *ce;
 
62
 
 
63
        if (!psd)
 
64
                return -EINVAL;
 
65
 
 
66
        ce = kzalloc(sizeof(*ce), GFP_KERNEL);
 
67
        if (!ce) {
 
68
                dev_err(dev, "Not enough memory for clock entry.\n");
 
69
                return -ENOMEM;
 
70
        }
 
71
 
 
72
        if (con_id) {
 
73
                ce->con_id = kstrdup(con_id, GFP_KERNEL);
 
74
                if (!ce->con_id) {
 
75
                        dev_err(dev,
 
76
                                "Not enough memory for clock connection ID.\n");
 
77
                        kfree(ce);
 
78
                        return -ENOMEM;
 
79
                }
 
80
        }
 
81
 
 
82
        pm_clk_acquire(dev, ce);
 
83
 
 
84
        spin_lock_irq(&psd->lock);
 
85
        list_add_tail(&ce->node, &psd->clock_list);
 
86
        spin_unlock_irq(&psd->lock);
 
87
        return 0;
 
88
}
 
89
 
 
90
/**
 
91
 * __pm_clk_remove - Destroy PM clock entry.
 
92
 * @ce: PM clock entry to destroy.
 
93
 */
 
94
static void __pm_clk_remove(struct pm_clock_entry *ce)
 
95
{
 
96
        if (!ce)
 
97
                return;
 
98
 
 
99
        if (ce->status < PCE_STATUS_ERROR) {
 
100
                if (ce->status == PCE_STATUS_ENABLED)
 
101
                        clk_disable(ce->clk);
 
102
 
 
103
                if (ce->status >= PCE_STATUS_ACQUIRED)
 
104
                        clk_put(ce->clk);
 
105
        }
 
106
 
 
107
        kfree(ce->con_id);
 
108
        kfree(ce);
 
109
}
 
110
 
 
111
/**
 
112
 * pm_clk_remove - Stop using a device clock for power management.
 
113
 * @dev: Device whose clock should not be used for PM any more.
 
114
 * @con_id: Connection ID of the clock.
 
115
 *
 
116
 * Remove the clock represented by @con_id from the list of clocks used for
 
117
 * the power management of @dev.
 
118
 */
 
119
void pm_clk_remove(struct device *dev, const char *con_id)
 
120
{
 
121
        struct pm_subsys_data *psd = dev_to_psd(dev);
 
122
        struct pm_clock_entry *ce;
 
123
 
 
124
        if (!psd)
 
125
                return;
 
126
 
 
127
        spin_lock_irq(&psd->lock);
 
128
 
 
129
        list_for_each_entry(ce, &psd->clock_list, node) {
 
130
                if (!con_id && !ce->con_id)
 
131
                        goto remove;
 
132
                else if (!con_id || !ce->con_id)
 
133
                        continue;
 
134
                else if (!strcmp(con_id, ce->con_id))
 
135
                        goto remove;
 
136
        }
 
137
 
 
138
        spin_unlock_irq(&psd->lock);
 
139
        return;
 
140
 
 
141
 remove:
 
142
        list_del(&ce->node);
 
143
        spin_unlock_irq(&psd->lock);
 
144
 
 
145
        __pm_clk_remove(ce);
 
146
}
 
147
 
 
148
/**
 
149
 * pm_clk_init - Initialize a device's list of power management clocks.
 
150
 * @dev: Device to initialize the list of PM clocks for.
 
151
 *
 
152
 * Initialize the lock and clock_list members of the device's pm_subsys_data
 
153
 * object.
 
154
 */
 
155
void pm_clk_init(struct device *dev)
 
156
{
 
157
        struct pm_subsys_data *psd = dev_to_psd(dev);
 
158
        if (psd)
 
159
                INIT_LIST_HEAD(&psd->clock_list);
 
160
}
 
161
 
 
162
/**
 
163
 * pm_clk_create - Create and initialize a device's list of PM clocks.
 
164
 * @dev: Device to create and initialize the list of PM clocks for.
 
165
 *
 
166
 * Allocate a struct pm_subsys_data object, initialize its lock and clock_list
 
167
 * members and make the @dev's power.subsys_data field point to it.
 
168
 */
 
169
int pm_clk_create(struct device *dev)
 
170
{
 
171
        int ret = dev_pm_get_subsys_data(dev);
 
172
        return ret < 0 ? ret : 0;
 
173
}
 
174
 
 
175
/**
 
176
 * pm_clk_destroy - Destroy a device's list of power management clocks.
 
177
 * @dev: Device to destroy the list of PM clocks for.
 
178
 *
 
179
 * Clear the @dev's power.subsys_data field, remove the list of clock entries
 
180
 * from the struct pm_subsys_data object pointed to by it before and free
 
181
 * that object.
 
182
 */
 
183
void pm_clk_destroy(struct device *dev)
 
184
{
 
185
        struct pm_subsys_data *psd = dev_to_psd(dev);
 
186
        struct pm_clock_entry *ce, *c;
 
187
        struct list_head list;
 
188
 
 
189
        if (!psd)
 
190
                return;
 
191
 
 
192
        INIT_LIST_HEAD(&list);
 
193
 
 
194
        spin_lock_irq(&psd->lock);
 
195
 
 
196
        list_for_each_entry_safe_reverse(ce, c, &psd->clock_list, node)
 
197
                list_move(&ce->node, &list);
 
198
 
 
199
        spin_unlock_irq(&psd->lock);
 
200
 
 
201
        dev_pm_put_subsys_data(dev);
 
202
 
 
203
        list_for_each_entry_safe_reverse(ce, c, &list, node) {
 
204
                list_del(&ce->node);
 
205
                __pm_clk_remove(ce);
 
206
        }
 
207
}
 
208
 
 
209
#endif /* CONFIG_PM */
 
210
 
 
211
#ifdef CONFIG_PM_RUNTIME
 
212
 
 
213
/**
 
214
 * pm_clk_suspend - Disable clocks in a device's PM clock list.
 
215
 * @dev: Device to disable the clocks for.
 
216
 */
 
217
int pm_clk_suspend(struct device *dev)
 
218
{
 
219
        struct pm_subsys_data *psd = dev_to_psd(dev);
 
220
        struct pm_clock_entry *ce;
 
221
        unsigned long flags;
 
222
 
 
223
        dev_dbg(dev, "%s()\n", __func__);
 
224
 
 
225
        if (!psd)
 
226
                return 0;
 
227
 
 
228
        spin_lock_irqsave(&psd->lock, flags);
 
229
 
 
230
        list_for_each_entry_reverse(ce, &psd->clock_list, node) {
 
231
                if (ce->status < PCE_STATUS_ERROR) {
 
232
                        if (ce->status == PCE_STATUS_ENABLED)
 
233
                                clk_disable(ce->clk);
 
234
                        ce->status = PCE_STATUS_ACQUIRED;
 
235
                }
 
236
        }
 
237
 
 
238
        spin_unlock_irqrestore(&psd->lock, flags);
 
239
 
 
240
        return 0;
 
241
}
 
242
 
 
243
/**
 
244
 * pm_clk_resume - Enable clocks in a device's PM clock list.
 
245
 * @dev: Device to enable the clocks for.
 
246
 */
 
247
int pm_clk_resume(struct device *dev)
 
248
{
 
249
        struct pm_subsys_data *psd = dev_to_psd(dev);
 
250
        struct pm_clock_entry *ce;
 
251
        unsigned long flags;
 
252
 
 
253
        dev_dbg(dev, "%s()\n", __func__);
 
254
 
 
255
        if (!psd)
 
256
                return 0;
 
257
 
 
258
        spin_lock_irqsave(&psd->lock, flags);
 
259
 
 
260
        list_for_each_entry(ce, &psd->clock_list, node) {
 
261
                if (ce->status < PCE_STATUS_ERROR) {
 
262
                        clk_enable(ce->clk);
 
263
                        ce->status = PCE_STATUS_ENABLED;
 
264
                }
 
265
        }
 
266
 
 
267
        spin_unlock_irqrestore(&psd->lock, flags);
 
268
 
 
269
        return 0;
 
270
}
 
271
 
 
272
/**
 
273
 * pm_clk_notify - Notify routine for device addition and removal.
 
274
 * @nb: Notifier block object this function is a member of.
 
275
 * @action: Operation being carried out by the caller.
 
276
 * @data: Device the routine is being run for.
 
277
 *
 
278
 * For this function to work, @nb must be a member of an object of type
 
279
 * struct pm_clk_notifier_block containing all of the requisite data.
 
280
 * Specifically, the pm_domain member of that object is copied to the device's
 
281
 * pm_domain field and its con_ids member is used to populate the device's list
 
282
 * of PM clocks, depending on @action.
 
283
 *
 
284
 * If the device's pm_domain field is already populated with a value different
 
285
 * from the one stored in the struct pm_clk_notifier_block object, the function
 
286
 * does nothing.
 
287
 */
 
288
static int pm_clk_notify(struct notifier_block *nb,
 
289
                                 unsigned long action, void *data)
 
290
{
 
291
        struct pm_clk_notifier_block *clknb;
 
292
        struct device *dev = data;
 
293
        char **con_id;
 
294
        int error;
 
295
 
 
296
        dev_dbg(dev, "%s() %ld\n", __func__, action);
 
297
 
 
298
        clknb = container_of(nb, struct pm_clk_notifier_block, nb);
 
299
 
 
300
        switch (action) {
 
301
        case BUS_NOTIFY_ADD_DEVICE:
 
302
                if (dev->pm_domain)
 
303
                        break;
 
304
 
 
305
                error = pm_clk_create(dev);
 
306
                if (error)
 
307
                        break;
 
308
 
 
309
                dev->pm_domain = clknb->pm_domain;
 
310
                if (clknb->con_ids[0]) {
 
311
                        for (con_id = clknb->con_ids; *con_id; con_id++)
 
312
                                pm_clk_add(dev, *con_id);
 
313
                } else {
 
314
                        pm_clk_add(dev, NULL);
 
315
                }
 
316
 
 
317
                break;
 
318
        case BUS_NOTIFY_DEL_DEVICE:
 
319
                if (dev->pm_domain != clknb->pm_domain)
 
320
                        break;
 
321
 
 
322
                dev->pm_domain = NULL;
 
323
                pm_clk_destroy(dev);
 
324
                break;
 
325
        }
 
326
 
 
327
        return 0;
 
328
}
 
329
 
 
330
#else /* !CONFIG_PM_RUNTIME */
 
331
 
 
332
#ifdef CONFIG_PM
 
333
 
 
334
/**
 
335
 * pm_clk_suspend - Disable clocks in a device's PM clock list.
 
336
 * @dev: Device to disable the clocks for.
 
337
 */
 
338
int pm_clk_suspend(struct device *dev)
 
339
{
 
340
        struct pm_subsys_data *psd = dev_to_psd(dev);
 
341
        struct pm_clock_entry *ce;
 
342
        unsigned long flags;
 
343
 
 
344
        dev_dbg(dev, "%s()\n", __func__);
 
345
 
 
346
        /* If there is no driver, the clocks are already disabled. */
 
347
        if (!psd || !dev->driver)
 
348
                return 0;
 
349
 
 
350
        spin_lock_irqsave(&psd->lock, flags);
 
351
 
 
352
        list_for_each_entry_reverse(ce, &psd->clock_list, node)
 
353
                clk_disable(ce->clk);
 
354
 
 
355
        spin_unlock_irqrestore(&psd->lock, flags);
 
356
 
 
357
        return 0;
 
358
}
 
359
 
 
360
/**
 
361
 * pm_clk_resume - Enable clocks in a device's PM clock list.
 
362
 * @dev: Device to enable the clocks for.
 
363
 */
 
364
int pm_clk_resume(struct device *dev)
 
365
{
 
366
        struct pm_subsys_data *psd = dev_to_psd(dev);
 
367
        struct pm_clock_entry *ce;
 
368
        unsigned long flags;
 
369
 
 
370
        dev_dbg(dev, "%s()\n", __func__);
 
371
 
 
372
        /* If there is no driver, the clocks should remain disabled. */
 
373
        if (!psd || !dev->driver)
 
374
                return 0;
 
375
 
 
376
        spin_lock_irqsave(&psd->lock, flags);
 
377
 
 
378
        list_for_each_entry(ce, &psd->clock_list, node)
 
379
                clk_enable(ce->clk);
 
380
 
 
381
        spin_unlock_irqrestore(&psd->lock, flags);
 
382
 
 
383
        return 0;
 
384
}
 
385
 
 
386
#endif /* CONFIG_PM */
 
387
 
 
388
/**
 
389
 * enable_clock - Enable a device clock.
 
390
 * @dev: Device whose clock is to be enabled.
 
391
 * @con_id: Connection ID of the clock.
 
392
 */
 
393
static void enable_clock(struct device *dev, const char *con_id)
 
394
{
 
395
        struct clk *clk;
 
396
 
 
397
        clk = clk_get(dev, con_id);
 
398
        if (!IS_ERR(clk)) {
 
399
                clk_enable(clk);
 
400
                clk_put(clk);
 
401
                dev_info(dev, "Runtime PM disabled, clock forced on.\n");
 
402
        }
 
403
}
 
404
 
 
405
/**
 
406
 * disable_clock - Disable a device clock.
 
407
 * @dev: Device whose clock is to be disabled.
 
408
 * @con_id: Connection ID of the clock.
 
409
 */
 
410
static void disable_clock(struct device *dev, const char *con_id)
 
411
{
 
412
        struct clk *clk;
 
413
 
 
414
        clk = clk_get(dev, con_id);
 
415
        if (!IS_ERR(clk)) {
 
416
                clk_disable(clk);
 
417
                clk_put(clk);
 
418
                dev_info(dev, "Runtime PM disabled, clock forced off.\n");
 
419
        }
 
420
}
 
421
 
 
422
/**
 
423
 * pm_clk_notify - Notify routine for device addition and removal.
 
424
 * @nb: Notifier block object this function is a member of.
 
425
 * @action: Operation being carried out by the caller.
 
426
 * @data: Device the routine is being run for.
 
427
 *
 
428
 * For this function to work, @nb must be a member of an object of type
 
429
 * struct pm_clk_notifier_block containing all of the requisite data.
 
430
 * Specifically, the con_ids member of that object is used to enable or disable
 
431
 * the device's clocks, depending on @action.
 
432
 */
 
433
static int pm_clk_notify(struct notifier_block *nb,
 
434
                                 unsigned long action, void *data)
 
435
{
 
436
        struct pm_clk_notifier_block *clknb;
 
437
        struct device *dev = data;
 
438
        char **con_id;
 
439
 
 
440
        dev_dbg(dev, "%s() %ld\n", __func__, action);
 
441
 
 
442
        clknb = container_of(nb, struct pm_clk_notifier_block, nb);
 
443
 
 
444
        switch (action) {
 
445
        case BUS_NOTIFY_BIND_DRIVER:
 
446
                if (clknb->con_ids[0]) {
 
447
                        for (con_id = clknb->con_ids; *con_id; con_id++)
 
448
                                enable_clock(dev, *con_id);
 
449
                } else {
 
450
                        enable_clock(dev, NULL);
 
451
                }
 
452
                break;
 
453
        case BUS_NOTIFY_UNBOUND_DRIVER:
 
454
                if (clknb->con_ids[0]) {
 
455
                        for (con_id = clknb->con_ids; *con_id; con_id++)
 
456
                                disable_clock(dev, *con_id);
 
457
                } else {
 
458
                        disable_clock(dev, NULL);
 
459
                }
 
460
                break;
 
461
        }
 
462
 
 
463
        return 0;
 
464
}
 
465
 
 
466
#endif /* !CONFIG_PM_RUNTIME */
 
467
 
 
468
/**
 
469
 * pm_clk_add_notifier - Add bus type notifier for power management clocks.
 
470
 * @bus: Bus type to add the notifier to.
 
471
 * @clknb: Notifier to be added to the given bus type.
 
472
 *
 
473
 * The nb member of @clknb is not expected to be initialized and its
 
474
 * notifier_call member will be replaced with pm_clk_notify().  However,
 
475
 * the remaining members of @clknb should be populated prior to calling this
 
476
 * routine.
 
477
 */
 
478
void pm_clk_add_notifier(struct bus_type *bus,
 
479
                                 struct pm_clk_notifier_block *clknb)
 
480
{
 
481
        if (!bus || !clknb)
 
482
                return;
 
483
 
 
484
        clknb->nb.notifier_call = pm_clk_notify;
 
485
        bus_register_notifier(bus, &clknb->nb);
 
486
}