Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
[pandora-kernel.git] / arch / arm / mach-shmobile / pm_runtime.c
1 /*
2  * arch/arm/mach-shmobile/pm_runtime.c
3  *
4  * Runtime PM support code for SuperH Mobile ARM
5  *
6  *  Copyright (C) 2009-2010 Magnus Damm
7  *
8  * This file is subject to the terms and conditions of the GNU General Public
9  * License.  See the file "COPYING" in the main directory of this archive
10  * for more details.
11  */
12
13 #include <linux/init.h>
14 #include <linux/kernel.h>
15 #include <linux/io.h>
16 #include <linux/pm_runtime.h>
17 #include <linux/platform_device.h>
18 #include <linux/clk.h>
19 #include <linux/sh_clk.h>
20 #include <linux/bitmap.h>
21
22 #ifdef CONFIG_PM_RUNTIME
23 #define BIT_ONCE 0
24 #define BIT_ACTIVE 1
25 #define BIT_CLK_ENABLED 2
26
27 struct pm_runtime_data {
28         unsigned long flags;
29         struct clk *clk;
30 };
31
32 static void __devres_release(struct device *dev, void *res)
33 {
34         struct pm_runtime_data *prd = res;
35
36         dev_dbg(dev, "__devres_release()\n");
37
38         if (test_bit(BIT_CLK_ENABLED, &prd->flags))
39                 clk_disable(prd->clk);
40
41         if (test_bit(BIT_ACTIVE, &prd->flags))
42                 clk_put(prd->clk);
43 }
44
45 static struct pm_runtime_data *__to_prd(struct device *dev)
46 {
47         return devres_find(dev, __devres_release, NULL, NULL);
48 }
49
50 static void platform_pm_runtime_init(struct device *dev,
51                                      struct pm_runtime_data *prd)
52 {
53         if (prd && !test_and_set_bit(BIT_ONCE, &prd->flags)) {
54                 prd->clk = clk_get(dev, NULL);
55                 if (!IS_ERR(prd->clk)) {
56                         set_bit(BIT_ACTIVE, &prd->flags);
57                         dev_info(dev, "clocks managed by runtime pm\n");
58                 }
59         }
60 }
61
62 static void platform_pm_runtime_bug(struct device *dev,
63                                     struct pm_runtime_data *prd)
64 {
65         if (prd && !test_and_set_bit(BIT_ONCE, &prd->flags))
66                 dev_err(dev, "runtime pm suspend before resume\n");
67 }
68
69 int platform_pm_runtime_suspend(struct device *dev)
70 {
71         struct pm_runtime_data *prd = __to_prd(dev);
72
73         dev_dbg(dev, "platform_pm_runtime_suspend()\n");
74
75         platform_pm_runtime_bug(dev, prd);
76
77         if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
78                 clk_disable(prd->clk);
79                 clear_bit(BIT_CLK_ENABLED, &prd->flags);
80         }
81
82         return 0;
83 }
84
85 int platform_pm_runtime_resume(struct device *dev)
86 {
87         struct pm_runtime_data *prd = __to_prd(dev);
88
89         dev_dbg(dev, "platform_pm_runtime_resume()\n");
90
91         platform_pm_runtime_init(dev, prd);
92
93         if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
94                 clk_enable(prd->clk);
95                 set_bit(BIT_CLK_ENABLED, &prd->flags);
96         }
97
98         return 0;
99 }
100
101 int platform_pm_runtime_idle(struct device *dev)
102 {
103         /* suspend synchronously to disable clocks immediately */
104         return pm_runtime_suspend(dev);
105 }
106
107 static int platform_bus_notify(struct notifier_block *nb,
108                                unsigned long action, void *data)
109 {
110         struct device *dev = data;
111         struct pm_runtime_data *prd;
112
113         dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
114
115         if (action == BUS_NOTIFY_BIND_DRIVER) {
116                 prd = devres_alloc(__devres_release, sizeof(*prd), GFP_KERNEL);
117                 if (prd)
118                         devres_add(dev, prd);
119                 else
120                         dev_err(dev, "unable to alloc memory for runtime pm\n");
121         }
122
123         return 0;
124 }
125
126 #else /* CONFIG_PM_RUNTIME */
127
128 static int platform_bus_notify(struct notifier_block *nb,
129                                unsigned long action, void *data)
130 {
131         struct device *dev = data;
132         struct clk *clk;
133
134         dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
135
136         switch (action) {
137         case BUS_NOTIFY_BIND_DRIVER:
138                 clk = clk_get(dev, NULL);
139                 if (!IS_ERR(clk)) {
140                         clk_enable(clk);
141                         clk_put(clk);
142                         dev_info(dev, "runtime pm disabled, clock forced on\n");
143                 }
144                 break;
145         case BUS_NOTIFY_UNBOUND_DRIVER:
146                 clk = clk_get(dev, NULL);
147                 if (!IS_ERR(clk)) {
148                         clk_disable(clk);
149                         clk_put(clk);
150                         dev_info(dev, "runtime pm disabled, clock forced off\n");
151                 }
152                 break;
153         }
154
155         return 0;
156 }
157
158 #endif /* CONFIG_PM_RUNTIME */
159
160 static struct notifier_block platform_bus_notifier = {
161         .notifier_call = platform_bus_notify
162 };
163
164 static int __init sh_pm_runtime_init(void)
165 {
166         bus_register_notifier(&platform_bus_type, &platform_bus_notifier);
167         return 0;
168 }
169 core_initcall(sh_pm_runtime_init);