Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6
[linux-2.6] / arch / sparc64 / kernel / power.c
1 /* power.c: Power management driver.
2  *
3  * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net)
4  */
5
6 #include <linux/kernel.h>
7 #include <linux/module.h>
8 #include <linux/init.h>
9 #include <linux/sched.h>
10 #include <linux/signal.h>
11 #include <linux/delay.h>
12 #include <linux/interrupt.h>
13 #include <linux/pm.h>
14 #include <linux/syscalls.h>
15 #include <linux/reboot.h>
16
17 #include <asm/system.h>
18 #include <asm/auxio.h>
19 #include <asm/prom.h>
20 #include <asm/of_device.h>
21 #include <asm/io.h>
22 #include <asm/sstate.h>
23 #include <asm/reboot.h>
24
25 #include <linux/unistd.h>
26
27 /*
28  * sysctl - toggle power-off restriction for serial console 
29  * systems in machine_power_off()
30  */
31 int scons_pwroff = 1; 
32
33 static void __iomem *power_reg;
34
35 static irqreturn_t power_handler(int irq, void *dev_id)
36 {
37         orderly_poweroff(true);
38
39         /* FIXME: Check registers for status... */
40         return IRQ_HANDLED;
41 }
42
43 static void (*poweroff_method)(void) = machine_alt_power_off;
44
45 void machine_power_off(void)
46 {
47         sstate_poweroff();
48         if (strcmp(of_console_device->type, "serial") || scons_pwroff) {
49                 if (power_reg) {
50                         /* Both register bits seem to have the
51                          * same effect, so until I figure out
52                          * what the difference is...
53                          */
54                         writel(AUXIO_PCIO_CPWR_OFF | AUXIO_PCIO_SPWR_OFF, power_reg);
55                 } else {
56                         if (poweroff_method != NULL) {
57                                 poweroff_method();
58                                 /* not reached */
59                         }
60                 }
61         }
62         machine_halt();
63 }
64
65 void (*pm_power_off)(void) = machine_power_off;
66 EXPORT_SYMBOL(pm_power_off);
67
68 static int __init has_button_interrupt(unsigned int irq, struct device_node *dp)
69 {
70         if (irq == 0xffffffff)
71                 return 0;
72         if (!of_find_property(dp, "button", NULL))
73                 return 0;
74
75         return 1;
76 }
77
78 static int __devinit power_probe(struct of_device *op, const struct of_device_id *match)
79 {
80         struct resource *res = &op->resource[0];
81         unsigned int irq= op->irqs[0];
82
83         power_reg = of_ioremap(res, 0, 0x4, "power");
84
85         printk(KERN_INFO "%s: Control reg at %lx\n",
86                op->node->name, res->start);
87
88         poweroff_method = machine_halt;  /* able to use the standard halt */
89
90         if (has_button_interrupt(irq, op->node)) {
91                 if (request_irq(irq,
92                                 power_handler, 0, "power", NULL) < 0)
93                         printk(KERN_ERR "power: Cannot setup IRQ handler.\n");
94         }
95
96         return 0;
97 }
98
99 static struct of_device_id power_match[] = {
100         {
101                 .name = "power",
102         },
103         {},
104 };
105
106 static struct of_platform_driver power_driver = {
107         .match_table    = power_match,
108         .probe          = power_probe,
109         .driver         = {
110                 .name   = "power",
111         },
112 };
113
114 void __init power_init(void)
115 {
116         of_register_driver(&power_driver, &of_platform_bus_type);
117         return;
118 }