Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/sh/kernel/cpu/shmobile/pm_runtime.c
17292 views
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
20
static DEFINE_SPINLOCK(hwblk_lock);
21
static LIST_HEAD(hwblk_idle_list);
22
static struct work_struct hwblk_work;
23
24
extern struct hwblk_info *hwblk_info;
25
26
static 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
39
static 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
69
static 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
101
static 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
*/
137
void platform_pm_runtime_suspend_idle(void)
138
{
139
queue_work(pm_wq, &hwblk_work);
140
}
141
142
static 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
185
out:
186
dev_dbg(dev, "%s() [%d] returns %d\n",
187
__func__, hwblk, ret);
188
189
return ret;
190
}
191
192
static 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);
230
out:
231
dev_dbg(dev, "%s() [%d] returns %d\n",
232
__func__, hwblk, ret);
233
234
return ret;
235
}
236
237
static 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);
254
out:
255
dev_dbg(dev, "%s() [%d] done!\n", __func__, hwblk);
256
return ret;
257
}
258
259
static struct dev_power_domain default_power_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
268
static 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->pwr_domain = &default_power_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->pwr_domain = NULL;
303
break;
304
}
305
return 0;
306
}
307
308
static struct notifier_block platform_bus_notifier = {
309
.notifier_call = platform_bus_notify
310
};
311
312
static 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
}
319
core_initcall(sh_pm_runtime_init);
320
321