Statistics
| Branch: | Revision:

scoutos / prex-0.9.0 / bsp / drv / dev / base / pm.c @ 03e9c04a

History | View | Annotate | Download (10.2 KB)

1
/*-
2
 * Copyright (c) 2005-2009, Kohsuke Ohtani
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 * 3. Neither the name of the author nor the names of any co-contributors
14
 *    may be used to endorse or promote products derived from this software
15
 *    without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29

    
30
/*
31
 * pm.c - power management driver
32
 */
33

    
34
#include <sys/ioctl.h>
35
#include <sys/capability.h>
36
#include <sys/power.h>
37
#include <sys/signal.h>
38
#include <driver.h>
39
#include <devctl.h>
40
#include <cons.h>
41
#include <pm.h>
42

    
43
/* #define DEBUG_PM 1 */
44

    
45
#ifdef DEBUG_PM
46
#define DPRINTF(a) printf a
47
#else
48
#define DPRINTF(a)
49
#endif
50

    
51
struct pm_softc {
52
        device_t        dev;                /* device object */
53
        int                isopen;                /* number of open counts */
54
        int                policy;                /* power management policy */
55
        int                timer_active;        /* true if pm timer is staring */
56
        timer_t                timer;                /* pm timer */
57
        u_long                idlecnt;        /* idle counter in sec */
58
        u_long                dimtime;        /* auto dim (lcd off) time in sec */
59
        u_long                sustime;        /* auto suspend time in sec */
60
        task_t                powtask;        /* task for power server */
61
        int                lcd_on;                /* true if lcd is off */
62
        device_t        lcd_dev;        /* lcd device */
63
        int                lastevt;        /* last event */
64
};
65

    
66
static int        pm_open(device_t, int);
67
static int        pm_close(device_t);
68
static int        pm_ioctl(device_t, u_long, void *);
69
static int        pm_init(struct driver *);
70
static void        pm_stop_timer(void);
71
static void        pm_update_timer(void);
72
static void        pm_timeout(void *);
73

    
74

    
75
static struct devops pm_devops = {
76
        /* open */        pm_open,
77
        /* close */        pm_close,
78
        /* read */        no_read,
79
        /* write */        no_write,
80
        /* ioctl */        pm_ioctl,
81
        /* devctl */        no_devctl,
82
};
83

    
84
struct driver pm_driver = {
85
        /* name */        "pm",
86
        /* devops */        &pm_devops,
87
        /* devsz */        sizeof(struct pm_softc),
88
        /* flags */        0,
89
        /* probe */        NULL,
90
        /* init */        pm_init,
91
        /* unload */        NULL,
92
};
93

    
94
/*
95
 * Pointer to the PM state.  There can be only one PM instance.
96
 */
97
static struct pm_softc *pm_softc;
98

    
99
static int
100
pm_open(device_t dev, int mode)
101
{
102
        struct pm_softc *sc = pm_softc;
103

    
104
        if (!task_capable(CAP_POWERMGMT))
105
                return EPERM;
106

    
107
        if (sc->isopen > 0)
108
                return EBUSY;
109

    
110
        sc->isopen++;
111
        return 0;
112
}
113

    
114
static int
115
pm_close(device_t dev)
116
{
117
        struct pm_softc *sc = pm_softc;
118

    
119
        if (!task_capable(CAP_POWERMGMT))
120
                return EPERM;
121

    
122
        if (sc->isopen != 1)
123
                return EINVAL;
124

    
125
        sc->isopen--;
126
        return 0;
127
}
128

    
129
static int
130
pm_ioctl(device_t dev, u_long cmd, void *arg)
131
{
132
        struct pm_softc *sc = pm_softc;
133
        int error = 0;
134
        int policy, state, event;
135

    
136
        if (!task_capable(CAP_POWERMGMT))
137
                return EPERM;
138

    
139
        switch (cmd) {
140

    
141
        case PMIOC_CONNECT:
142
                /* Connection request from the power server */
143
                if (copyin(arg, &sc->powtask, sizeof(task_t)))
144
                        return EFAULT;
145
                DPRINTF(("pm: connect power server\n"));
146
                break;
147

    
148
        case PMIOC_QUERY_EVENT:
149
                event = sc->lastevt;
150
                sc->lastevt = PME_NO_EVENT;
151
                if (copyout(&event, arg, sizeof(int)))
152
                        return EFAULT;
153
                DPRINTF(("pm: query event=%d\n", event));
154
                break;
155

    
156
        case PMIOC_SET_POWER:
157
                if (copyin(arg, &state, sizeof(int)))
158
                        return EFAULT;
159

    
160
                switch (state) {
161
                case PWR_SUSPEND:
162
                case PWR_OFF:
163
                case PWR_REBOOT:
164
                        pm_set_power(state);
165
                        break;
166
                default:
167
                        error = EINVAL;
168
                        break;
169
                }
170
                break;
171

    
172
        case PMIOC_GET_POLICY:
173
                if (copyout(&sc->policy, arg, sizeof(int)))
174
                        return EFAULT;
175
                DPRINTF(("pm: get policy %d\n", sc->policy));
176
                break;
177

    
178
        case PMIOC_SET_POLICY:
179
                if (copyin(arg, &policy, sizeof(int)))
180
                        return EFAULT;
181
                if (policy != PM_POWERSAVE && policy != PM_PERFORMANCE)
182
                        return EINVAL;
183

    
184
                DPRINTF(("pm: set policy %d\n", policy));
185

    
186
                if (policy == sc->policy) {
187
                        /* same policy */
188
                        break;
189
                }
190
                /* Call devctl() routine for all devices */
191
                device_broadcast(DEVCTL_PM_CHGPOLICY, &policy, 1);
192

    
193
                sc->policy = policy;
194
                if (policy == PM_POWERSAVE)
195
                        pm_update_timer();
196
                else
197
                        pm_stop_timer();
198
                break;
199

    
200
        case PMIOC_GET_SUSTMR:
201
                if (copyout(&sc->sustime, arg, sizeof(u_long)))
202
                        return EFAULT;
203
                break;
204

    
205
        case PMIOC_SET_SUSTMR:
206
                if (copyin(arg, &sc->sustime, sizeof(u_long)))
207
                        return EFAULT;
208
                DPRINTF(("pm: set sustmr=%d\n", sc->sustime));
209
                pm_update_timer();
210
                break;
211

    
212
        case PMIOC_GET_DIMTMR:
213
                if (copyout(&sc->dimtime, arg, sizeof(u_long)))
214
                        return EFAULT;
215
                break;
216

    
217
        case PMIOC_SET_DIMTMR:
218
                if (copyin(arg, &sc->dimtime, sizeof(u_long)))
219
                        return EFAULT;
220
                DPRINTF(("pm: set dimtmr=%d\n", sc->dimtime));
221
                pm_update_timer();
222
                break;
223

    
224
        default:
225
                return EINVAL;
226
        }
227
        return error;
228
}
229

    
230

    
231
static void
232
pm_stop_timer(void)
233
{
234
        struct pm_softc *sc = pm_softc;
235
        int s;
236

    
237
        DPRINTF(("pm: stop timer\n"));
238

    
239
        s = splhigh();
240
        if (sc->timer_active) {
241
                timer_stop(&sc->timer);
242
                sc->idlecnt = 0;
243
                sc->timer_active = 0;
244
        }
245
        splx(s);
246
}
247

    
248
static void
249
pm_update_timer(void)
250
{
251
        struct pm_softc *sc = pm_softc;
252
        int s;
253

    
254
        if (sc->policy != PM_POWERSAVE)
255
                return;
256

    
257
        s = splhigh();
258
        sc->idlecnt = 0;
259
        if (sc->timer_active) {
260
                if (sc->sustime == 0 && sc->dimtime == 0)
261
                        timer_stop(&sc->timer);
262
        } else {
263
                if (sc->sustime != 0 || sc->dimtime != 0) {
264
                        DPRINTF(("pm: start timer\n"));
265
                        timer_callout(&sc->timer, 1000, &pm_timeout, sc);
266
                              sc->timer_active = 1;
267
                }
268
        }
269
}
270

    
271

    
272
static int
273
pm_suspend(void)
274
{
275
        int error;
276

    
277
        DPRINTF(("pm: suspend system...\n"));
278

    
279
        pm_stop_timer();
280
        error = device_broadcast(DEVCTL_PM_POWERDOWN, NULL, 1);
281
        if (error) {
282
                device_broadcast(DEVCTL_PM_POWERUP, NULL, 1);
283
                return error;
284
        }
285
        machine_powerdown(PWR_SUSPEND);
286
        return 0;
287
}
288

    
289
static int
290
pm_resume(void)
291
{
292

    
293
        DPRINTF(("pm: resume...\n"));
294

    
295
        device_broadcast(DEVCTL_PM_POWERUP, NULL, 1);
296
        pm_update_timer();
297
        return 0;
298
}
299

    
300
static int
301
pm_poweroff(void)
302
{
303

    
304
        DPRINTF(("pm: power off...\n"));
305

    
306
        pm_stop_timer();
307
        device_broadcast(DEVCTL_PM_POWERDOWN, NULL, 1);
308
        driver_shutdown();
309

    
310
#ifdef CONFIG_CONS
311
        cons_puts("\nThe system is halted. You can turn off power.");
312
#endif
313
        machine_powerdown(PWR_OFF);
314

    
315
        /* NOTREACHED */
316
        return 0;
317
}
318

    
319
static int
320
pm_reboot(void)
321
{
322

    
323
        DPRINTF(("pm: rebooting...\n"));
324

    
325
        pm_stop_timer();
326
        device_broadcast(DEVCTL_PM_POWERDOWN, NULL, 1);
327
        driver_shutdown();
328
        machine_powerdown(PWR_REBOOT);
329

    
330
        /* NOTREACHED */
331
        return 0;
332
}
333

    
334
static void
335
pm_lcd_off(void)
336
{
337
        struct pm_softc *sc = pm_softc;
338

    
339
        DPRINTF(("pm: LCD off\n"));
340

    
341
        if (sc->lcd_dev != NODEV && sc->lcd_on) {
342
                device_control(sc->lcd_dev, DEVCTL_PM_LCDOFF, NULL);
343
                if (sc->sustime == 0)
344
                        pm_stop_timer();
345
                sc->lcd_on = 0;
346
        }
347
}
348

    
349
static void
350
pm_lcd_on(void)
351
{
352
        struct pm_softc *sc = pm_softc;
353

    
354
        DPRINTF(("pm: LCD on\n"));
355

    
356
        if (sc->lcd_dev != NODEV && !sc->lcd_on) {
357
                device_control(sc->lcd_dev, DEVCTL_PM_LCDON, NULL);
358
                pm_update_timer();
359
                sc->lcd_on = 1;
360
        }
361
}
362

    
363
static void
364
pm_timeout(void *arg)
365
{
366
        struct pm_softc *sc = arg;
367
        int s, reload;
368

    
369
        s = splhigh();
370
        sc->idlecnt++;
371
        splx(s);
372

    
373
        DPRINTF(("pm: idlecnt=%d\n", sc->idlecnt));
374

    
375
        if (sc->sustime != 0 && sc->idlecnt >= sc->sustime) {
376
#ifdef CONFIG_CONS
377
                cons_puts("\nThe system is about to suspend...");
378
#endif
379
                pm_suspend();
380
        } else {
381
                reload = 0;
382
                if (sc->dimtime != 0 && sc->idlecnt >= sc->dimtime) {
383
                        pm_lcd_off();
384
                        if (sc->sustime != 0)
385
                                reload = 1;
386
                } else
387
                        reload = 1;
388

    
389
                if (reload)
390
                        timer_callout(&sc->timer, 1000, &pm_timeout, sc);
391
        }
392
}
393

    
394
/*
395
 * PM service for other drivers.
396
 */
397
int
398
pm_set_power(int state)
399
{
400
        int error;
401

    
402
        switch (state) {
403
        case PWR_ON:
404
                error = pm_resume();
405
                break;
406
        case PWR_SUSPEND:
407
                error = pm_suspend();
408
                break;
409
        case PWR_OFF:
410
                error = pm_poweroff();
411
                break;
412
        case PWR_REBOOT:
413
                error = pm_reboot();
414
                break;
415
        default:
416
                error = EINVAL;
417
        }
418
        return error;
419
}
420

    
421
/*
422
 * PM event notification.
423
 */
424
void
425
pm_notify(int event)
426
{
427
        struct pm_softc *sc = pm_softc;
428
        int s;
429

    
430
        if (event == PME_USER_ACTIVITY) {
431
                /*
432
                 * Reload suspend timer for user activity.
433
                 */
434
                s = splhigh();
435
                sc->idlecnt = 0;
436
                splx(s);
437

    
438
                if (!sc->lcd_on)
439
                        pm_lcd_on();
440
                return;
441
        }
442

    
443
        DPRINTF(("pm: notify %d\n", event));
444

    
445
        if (sc->powtask != TASK_NULL) {
446
                /*
447
                 * Power server exists.
448
                 */
449
                switch (event) {
450
                case PME_PWRBTN_PRESS:
451
                case PME_SLPBTN_PRESS:
452
                case PME_LOW_BATTERY:
453
                case PME_LCD_CLOSE:
454
                        /*
455
                         * Post an exception to the power server.
456
                         * Then, the power server will query PM event.
457
                         */
458
                        sc->lastevt = event;
459
                        DPRINTF(("pm: post %d\n", event));
460
                        exception_post(sc->powtask, SIGPWR);
461
                        break;
462
                case PME_LCD_OPEN:
463
                        sc->lastevt = PME_NO_EVENT;
464
                        pm_lcd_on();
465
                        break;
466
                }
467
        } else {
468
                /*
469
                 * No power server.
470
                 * Map power event to default action.
471
                 */
472
                switch (event) {
473
                case PME_PWRBTN_PRESS:
474
                        pm_poweroff();
475
                        break;
476
                case PME_SLPBTN_PRESS:
477
                case PME_LOW_BATTERY:
478
                        pm_suspend();
479
                        break;
480
                case PME_LCD_OPEN:
481
                        pm_lcd_on();
482
                        break;
483
                case PME_LCD_CLOSE:
484
                        pm_lcd_off();
485
                        break;
486
                }
487
        }
488
}
489

    
490
void
491
pm_attach_lcd(device_t dev)
492
{
493

    
494
        ASSERT(pm_softc != NULL);
495

    
496
        pm_softc->lcd_dev = dev;
497
}
498

    
499
static int
500
pm_init(struct driver *self)
501
{
502
        struct pm_softc *sc;
503
        device_t dev;
504

    
505
        /* Create device object */
506
        dev = device_create(self, "pm", D_CHR|D_PROT);
507

    
508
        sc = device_private(dev);
509
        sc->dev = dev;
510
        sc->isopen = 0;
511
        sc->policy = DEFAULT_POWER_POLICY;
512
        sc->idlecnt = 0;
513
        sc->dimtime = 0;
514
        sc->sustime = 0;
515
        sc->timer_active = 0;
516
        sc->powtask = TASK_NULL;
517
        sc->lcd_dev = NODEV;
518
        sc->lcd_on = 1;
519
        sc->lastevt = PME_NO_EVENT;
520

    
521
        pm_softc = sc;
522

    
523
        DPRINTF(("Power policy: %s mode\n",
524
                 (sc->policy == PM_POWERSAVE) ? "power save" : "performance"));
525
        return 0;
526
}