• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/sh/kernel/cpu/shmobile/
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
142int platform_pm_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, "platform_pm_runtime_suspend() [%d]\n", 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, &pdev->archdata.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(&pdev->archdata.entry, &hwblk_idle_list);
174	__set_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.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, "platform_pm_runtime_suspend() [%d] returns %d\n",
187		hwblk, ret);
188
189	return ret;
190}
191
192int platform_pm_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, "platform_pm_runtime_resume() [%d]\n", 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, "platform_pm_runtime_resume() [%d] returns %d\n",
232		hwblk, ret);
233
234	return ret;
235}
236
237int platform_pm_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, "platform_pm_runtime_idle() [%d]\n", 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, "platform_pm_runtime_idle() [%d] done!\n", hwblk);
256	return ret;
257}
258
259static int platform_bus_notify(struct notifier_block *nb,
260			       unsigned long action, void *data)
261{
262	struct device *dev = data;
263	struct platform_device *pdev = to_platform_device(dev);
264	int hwblk = pdev->archdata.hwblk_id;
265
266	/* ignore off-chip platform devices */
267	if (!hwblk)
268		return 0;
269
270	switch (action) {
271	case BUS_NOTIFY_ADD_DEVICE:
272		INIT_LIST_HEAD(&pdev->archdata.entry);
273		mutex_init(&pdev->archdata.mutex);
274		/* platform devices without drivers should be disabled */
275		hwblk_enable(hwblk_info, hwblk);
276		hwblk_disable(hwblk_info, hwblk);
277		/* make sure driver re-inits itself once */
278		__set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
279		break;
280	/* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
281	case BUS_NOTIFY_BOUND_DRIVER:
282		/* keep track of number of devices in use per hwblk */
283		hwblk_cnt_inc(hwblk_info, hwblk, HWBLK_CNT_DEVICES);
284		break;
285	case BUS_NOTIFY_UNBOUND_DRIVER:
286		/* keep track of number of devices in use per hwblk */
287		hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_DEVICES);
288		/* make sure driver re-inits itself once */
289		__set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
290		break;
291	case BUS_NOTIFY_DEL_DEVICE:
292		break;
293	}
294	return 0;
295}
296
297static struct notifier_block platform_bus_notifier = {
298	.notifier_call = platform_bus_notify
299};
300
301static int __init sh_pm_runtime_init(void)
302{
303	INIT_WORK(&hwblk_work, platform_pm_runtime_work);
304
305	bus_register_notifier(&platform_bus_type, &platform_bus_notifier);
306	return 0;
307}
308core_initcall(sh_pm_runtime_init);
309