aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/sh/include/asm/device.h9
-rw-r--r--arch/sh/kernel/cpu/shmobile/Makefile3
-rw-r--r--arch/sh/kernel/cpu/shmobile/pm_runtime.c319
-rw-r--r--drivers/sh/Makefile12
4 files changed, 1 insertions, 342 deletions
diff --git a/arch/sh/include/asm/device.h b/arch/sh/include/asm/device.h
index b16debfe8c1e..783ecdc64e25 100644
--- a/arch/sh/include/asm/device.h
+++ b/arch/sh/include/asm/device.h
@@ -14,15 +14,6 @@ int platform_resource_setup_memory(struct platform_device *pdev,
14 14
15void plat_early_device_setup(void); 15void plat_early_device_setup(void);
16 16
17#define PDEV_ARCHDATA_FLAG_INIT 0
18#define PDEV_ARCHDATA_FLAG_IDLE 1
19#define PDEV_ARCHDATA_FLAG_SUSP 2
20
21struct pdev_archdata { 17struct pdev_archdata {
22 int hwblk_id; 18 int hwblk_id;
23#ifdef CONFIG_PM_RUNTIME
24 unsigned long flags;
25 struct list_head entry;
26 struct mutex mutex;
27#endif
28}; 19};
diff --git a/arch/sh/kernel/cpu/shmobile/Makefile b/arch/sh/kernel/cpu/shmobile/Makefile
index 7eb4502dfb61..e8a5111e848a 100644
--- a/arch/sh/kernel/cpu/shmobile/Makefile
+++ b/arch/sh/kernel/cpu/shmobile/Makefile
@@ -5,6 +5,3 @@
5# Power Management & Sleep mode 5# Power Management & Sleep mode
6obj-$(CONFIG_PM) += pm.o sleep.o 6obj-$(CONFIG_PM) += pm.o sleep.o
7obj-$(CONFIG_CPU_IDLE) += cpuidle.o 7obj-$(CONFIG_CPU_IDLE) += cpuidle.o
8ifneq ($(CONFIG_CPU_SUBTYPE_SH7722)$(CONFIG_CPU_SUBTYPE_SH7723)$(CONFIG_CPU_SUBTYPE_SH7724),y)
9obj-$(CONFIG_PM_RUNTIME) += pm_runtime.o
10endif
diff --git a/arch/sh/kernel/cpu/shmobile/pm_runtime.c b/arch/sh/kernel/cpu/shmobile/pm_runtime.c
deleted file mode 100644
index bf280c812d2f..000000000000
--- a/arch/sh/kernel/cpu/shmobile/pm_runtime.c
+++ /dev/null
@@ -1,319 +0,0 @@
1/*
2 * arch/sh/kernel/cpu/shmobile/pm_runtime.c
3 *
4 * Runtime PM support code for SuperH Mobile
5 *
6 * Copyright (C) 2009 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#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/io.h>
15#include <linux/pm_runtime.h>
16#include <linux/platform_device.h>
17#include <linux/mutex.h>
18#include <asm/hwblk.h>
19
20static DEFINE_SPINLOCK(hwblk_lock);
21static LIST_HEAD(hwblk_idle_list);
22static struct work_struct hwblk_work;
23
24extern struct hwblk_info *hwblk_info;
25
26static void platform_pm_runtime_not_idle(struct platform_device *pdev)
27{
28 unsigned long flags;
29
30 /* remove device from idle list */
31 spin_lock_irqsave(&hwblk_lock, flags);
32 if (test_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.flags)) {
33 list_del(&pdev->archdata.entry);
34 __clear_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.flags);
35 }
36 spin_unlock_irqrestore(&hwblk_lock, flags);
37}
38
39static int __platform_pm_runtime_resume(struct platform_device *pdev)
40{
41 struct device *d = &pdev->dev;
42 struct pdev_archdata *ad = &pdev->archdata;
43 int hwblk = ad->hwblk_id;
44 int ret = -ENOSYS;
45
46 dev_dbg(d, "__platform_pm_runtime_resume() [%d]\n", hwblk);
47
48 if (d->driver) {
49 hwblk_enable(hwblk_info, hwblk);
50 ret = 0;
51
52 if (test_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags)) {
53 if (d->driver->pm && d->driver->pm->runtime_resume)
54 ret = d->driver->pm->runtime_resume(d);
55
56 if (!ret)
57 clear_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags);
58 else
59 hwblk_disable(hwblk_info, hwblk);
60 }
61 }
62
63 dev_dbg(d, "__platform_pm_runtime_resume() [%d] - returns %d\n",
64 hwblk, ret);
65
66 return ret;
67}
68
69static int __platform_pm_runtime_suspend(struct platform_device *pdev)
70{
71 struct device *d = &pdev->dev;
72 struct pdev_archdata *ad = &pdev->archdata;
73 int hwblk = ad->hwblk_id;
74 int ret = -ENOSYS;
75
76 dev_dbg(d, "__platform_pm_runtime_suspend() [%d]\n", hwblk);
77
78 if (d->driver) {
79 BUG_ON(!test_bit(PDEV_ARCHDATA_FLAG_IDLE, &ad->flags));
80 ret = 0;
81
82 if (d->driver->pm && d->driver->pm->runtime_suspend) {
83 hwblk_enable(hwblk_info, hwblk);
84 ret = d->driver->pm->runtime_suspend(d);
85 hwblk_disable(hwblk_info, hwblk);
86 }
87
88 if (!ret) {
89 set_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags);
90 platform_pm_runtime_not_idle(pdev);
91 hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_IDLE);
92 }
93 }
94
95 dev_dbg(d, "__platform_pm_runtime_suspend() [%d] - returns %d\n",
96 hwblk, ret);
97
98 return ret;
99}
100
101static void platform_pm_runtime_work(struct work_struct *work)
102{
103 struct platform_device *pdev;
104 unsigned long flags;
105 int ret;
106
107 /* go through the idle list and suspend one device at a time */
108 do {
109 spin_lock_irqsave(&hwblk_lock, flags);
110 if (list_empty(&hwblk_idle_list))
111 pdev = NULL;
112 else
113 pdev = list_first_entry(&hwblk_idle_list,
114 struct platform_device,
115 archdata.entry);
116 spin_unlock_irqrestore(&hwblk_lock, flags);
117
118 if (pdev) {
119 mutex_lock(&pdev->archdata.mutex);
120 ret = __platform_pm_runtime_suspend(pdev);
121
122 /* at this point the platform device may be:
123 * suspended: ret = 0, FLAG_SUSP set, clock stopped
124 * failed: ret < 0, FLAG_IDLE set, clock stopped
125 */
126 mutex_unlock(&pdev->archdata.mutex);
127 } else {
128 ret = -ENODEV;
129 }
130 } while (!ret);
131}
132
133/* this function gets called from cpuidle context when all devices in the
134 * main power domain are unused but some are counted as idle, ie the hwblk
135 * counter values are (HWBLK_CNT_USAGE == 0) && (HWBLK_CNT_IDLE != 0)
136 */
137void platform_pm_runtime_suspend_idle(void)
138{
139 queue_work(pm_wq, &hwblk_work);
140}
141
142static int default_platform_runtime_suspend(struct device *dev)
143{
144 struct platform_device *pdev = to_platform_device(dev);
145 struct pdev_archdata *ad = &pdev->archdata;
146 unsigned long flags;
147 int hwblk = ad->hwblk_id;
148 int ret = 0;
149
150 dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);
151
152 /* ignore off-chip platform devices */
153 if (!hwblk)
154 goto out;
155
156 /* interrupt context not allowed */
157 might_sleep();
158
159 /* catch misconfigured drivers not starting with resume */
160 if (test_bit(PDEV_ARCHDATA_FLAG_INIT, &ad->flags)) {
161 ret = -EINVAL;
162 goto out;
163 }
164
165 /* serialize */
166 mutex_lock(&ad->mutex);
167
168 /* disable clock */
169 hwblk_disable(hwblk_info, hwblk);
170
171 /* put device on idle list */
172 spin_lock_irqsave(&hwblk_lock, flags);
173 list_add_tail(&ad->entry, &hwblk_idle_list);
174 __set_bit(PDEV_ARCHDATA_FLAG_IDLE, &ad->flags);
175 spin_unlock_irqrestore(&hwblk_lock, flags);
176
177 /* increase idle count */
178 hwblk_cnt_inc(hwblk_info, hwblk, HWBLK_CNT_IDLE);
179
180 /* at this point the platform device is:
181 * idle: ret = 0, FLAG_IDLE set, clock stopped
182 */
183 mutex_unlock(&ad->mutex);
184
185out:
186 dev_dbg(dev, "%s() [%d] returns %d\n",
187 __func__, hwblk, ret);
188
189 return ret;
190}
191
192static int default_platform_runtime_resume(struct device *dev)
193{
194 struct platform_device *pdev = to_platform_device(dev);
195 struct pdev_archdata *ad = &pdev->archdata;
196 int hwblk = ad->hwblk_id;
197 int ret = 0;
198
199 dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);
200
201 /* ignore off-chip platform devices */
202 if (!hwblk)
203 goto out;
204
205 /* interrupt context not allowed */
206 might_sleep();
207
208 /* serialize */
209 mutex_lock(&ad->mutex);
210
211 /* make sure device is removed from idle list */
212 platform_pm_runtime_not_idle(pdev);
213
214 /* decrease idle count */
215 if (!test_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags) &&
216 !test_bit(PDEV_ARCHDATA_FLAG_SUSP, &pdev->archdata.flags))
217 hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_IDLE);
218
219 /* resume the device if needed */
220 ret = __platform_pm_runtime_resume(pdev);
221
222 /* the driver has been initialized now, so clear the init flag */
223 clear_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
224
225 /* at this point the platform device may be:
226 * resumed: ret = 0, flags = 0, clock started
227 * failed: ret < 0, FLAG_SUSP set, clock stopped
228 */
229 mutex_unlock(&ad->mutex);
230out:
231 dev_dbg(dev, "%s() [%d] returns %d\n",
232 __func__, hwblk, ret);
233
234 return ret;
235}
236
237static int default_platform_runtime_idle(struct device *dev)
238{
239 struct platform_device *pdev = to_platform_device(dev);
240 int hwblk = pdev->archdata.hwblk_id;
241 int ret = 0;
242
243 dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);
244
245 /* ignore off-chip platform devices */
246 if (!hwblk)
247 goto out;
248
249 /* interrupt context not allowed, use pm_runtime_put()! */
250 might_sleep();
251
252 /* suspend synchronously to disable clocks immediately */
253 ret = pm_runtime_suspend(dev);
254out:
255 dev_dbg(dev, "%s() [%d] done!\n", __func__, hwblk);
256 return ret;
257}
258
259static struct dev_pm_domain default_pm_domain = {
260 .ops = {
261 .runtime_suspend = default_platform_runtime_suspend,
262 .runtime_resume = default_platform_runtime_resume,
263 .runtime_idle = default_platform_runtime_idle,
264 USE_PLATFORM_PM_SLEEP_OPS
265 },
266};
267
268static int platform_bus_notify(struct notifier_block *nb,
269 unsigned long action, void *data)
270{
271 struct device *dev = data;
272 struct platform_device *pdev = to_platform_device(dev);
273 int hwblk = pdev->archdata.hwblk_id;
274
275 /* ignore off-chip platform devices */
276 if (!hwblk)
277 return 0;
278
279 switch (action) {
280 case BUS_NOTIFY_ADD_DEVICE:
281 INIT_LIST_HEAD(&pdev->archdata.entry);
282 mutex_init(&pdev->archdata.mutex);
283 /* platform devices without drivers should be disabled */
284 hwblk_enable(hwblk_info, hwblk);
285 hwblk_disable(hwblk_info, hwblk);
286 /* make sure driver re-inits itself once */
287 __set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
288 dev->pm_domain = &default_pm_domain;
289 break;
290 /* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
291 case BUS_NOTIFY_BOUND_DRIVER:
292 /* keep track of number of devices in use per hwblk */
293 hwblk_cnt_inc(hwblk_info, hwblk, HWBLK_CNT_DEVICES);
294 break;
295 case BUS_NOTIFY_UNBOUND_DRIVER:
296 /* keep track of number of devices in use per hwblk */
297 hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_DEVICES);
298 /* make sure driver re-inits itself once */
299 __set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
300 break;
301 case BUS_NOTIFY_DEL_DEVICE:
302 dev->pm_domain = NULL;
303 break;
304 }
305 return 0;
306}
307
308static struct notifier_block platform_bus_notifier = {
309 .notifier_call = platform_bus_notify
310};
311
312static int __init sh_pm_runtime_init(void)
313{
314 INIT_WORK(&hwblk_work, platform_pm_runtime_work);
315
316 bus_register_notifier(&platform_bus_type, &platform_bus_notifier);
317 return 0;
318}
319core_initcall(sh_pm_runtime_init);
diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile
index 51f171b01482..c393894ff357 100644
--- a/drivers/sh/Makefile
+++ b/drivers/sh/Makefile
@@ -7,14 +7,4 @@ obj-$(CONFIG_HAVE_CLK) += clk/
7obj-$(CONFIG_MAPLE) += maple/ 7obj-$(CONFIG_MAPLE) += maple/
8obj-$(CONFIG_SUPERHYWAY) += superhyway/ 8obj-$(CONFIG_SUPERHYWAY) += superhyway/
9obj-$(CONFIG_GENERIC_GPIO) += pfc.o 9obj-$(CONFIG_GENERIC_GPIO) += pfc.o
10 10obj-$(CONFIG_PM_RUNTIME) += pm_runtime.o
11#
12# For the moment we only use this framework for ARM-based SH/R-Mobile
13# platforms and generic SH. SH-based SH-Mobile platforms are still using
14# an older framework that is pending up-porting, at which point this
15# special casing can go away.
16#
17obj-$(CONFIG_SUPERH)$(CONFIG_ARCH_SHMOBILE) += pm_runtime.o
18obj-$(CONFIG_CPU_SUBTYPE_SH7722) += pm_runtime.o
19obj-$(CONFIG_CPU_SUBTYPE_SH7723) += pm_runtime.o
20obj-$(CONFIG_CPU_SUBTYPE_SH7724) += pm_runtime.o