Merge from Linus' tree
[linux-2.6] / arch / sparc64 / kernel / power.c
1 /* $Id: power.c,v 1.10 2001/12/11 01:57:16 davem Exp $
2  * power.c: Power management driver.
3  *
4  * Copyright (C) 1999 David S. Miller (davem@redhat.com)
5  */
6
7 #define __KERNEL_SYSCALLS__
8
9 #include <linux/config.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/sched.h>
14 #include <linux/signal.h>
15 #include <linux/delay.h>
16 #include <linux/interrupt.h>
17
18 #include <asm/system.h>
19 #include <asm/ebus.h>
20 #include <asm/isa.h>
21 #include <asm/auxio.h>
22
23 #include <linux/unistd.h>
24
25 /*
26  * sysctl - toggle power-off restriction for serial console 
27  * systems in machine_power_off()
28  */
29 int scons_pwroff = 1; 
30
31 #ifdef CONFIG_PCI
32 static void __iomem *power_reg;
33
34 static DECLARE_WAIT_QUEUE_HEAD(powerd_wait);
35 static int button_pressed;
36
37 static irqreturn_t power_handler(int irq, void *dev_id, struct pt_regs *regs)
38 {
39         if (button_pressed == 0) {
40                 button_pressed = 1;
41                 wake_up(&powerd_wait);
42         }
43
44         /* FIXME: Check registers for status... */
45         return IRQ_HANDLED;
46 }
47 #endif /* CONFIG_PCI */
48
49 extern void machine_halt(void);
50 extern void machine_alt_power_off(void);
51 static void (*poweroff_method)(void) = machine_alt_power_off;
52
53 void machine_power_off(void)
54 {
55         if (!serial_console || scons_pwroff) {
56 #ifdef CONFIG_PCI
57                 if (power_reg) {
58                         /* Both register bits seem to have the
59                          * same effect, so until I figure out
60                          * what the difference is...
61                          */
62                         writel(AUXIO_PCIO_CPWR_OFF | AUXIO_PCIO_SPWR_OFF, power_reg);
63                 } else
64 #endif /* CONFIG_PCI */
65                         if (poweroff_method != NULL) {
66                                 poweroff_method();
67                                 /* not reached */
68                         }
69         }
70         machine_halt();
71 }
72
73 #ifdef CONFIG_PCI
74 static int powerd(void *__unused)
75 {
76         static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
77         char *argv[] = { "/sbin/shutdown", "-h", "now", NULL };
78         DECLARE_WAITQUEUE(wait, current);
79
80         daemonize("powerd");
81
82         add_wait_queue(&powerd_wait, &wait);
83 again:
84         for (;;) {
85                 set_task_state(current, TASK_INTERRUPTIBLE);
86                 if (button_pressed)
87                         break;
88                 flush_signals(current);
89                 schedule();
90         }
91         __set_current_state(TASK_RUNNING);
92         remove_wait_queue(&powerd_wait, &wait);
93
94         /* Ok, down we go... */
95         button_pressed = 0;
96         if (execve("/sbin/shutdown", argv, envp) < 0) {
97                 printk("powerd: shutdown execution failed\n");
98                 add_wait_queue(&powerd_wait, &wait);
99                 goto again;
100         }
101         return 0;
102 }
103
104 static int __init has_button_interrupt(unsigned int irq, int prom_node)
105 {
106         if (irq == PCI_IRQ_NONE)
107                 return 0;
108         if (!prom_node_has_property(prom_node, "button"))
109                 return 0;
110
111         return 1;
112 }
113
114 static int __init power_probe_ebus(struct resource **resp, unsigned int *irq_p, int *prom_node_p)
115 {
116         struct linux_ebus *ebus;
117         struct linux_ebus_device *edev;
118
119         for_each_ebus(ebus) {
120                 for_each_ebusdev(edev, ebus) {
121                         if (!strcmp(edev->prom_name, "power")) {
122                                 *resp = &edev->resource[0];
123                                 *irq_p = edev->irqs[0];
124                                 *prom_node_p = edev->prom_node;
125                                 return 0;
126                         }
127                 }
128         }
129         return -ENODEV;
130 }
131
132 static int __init power_probe_isa(struct resource **resp, unsigned int *irq_p, int *prom_node_p)
133 {
134         struct sparc_isa_bridge *isa_bus;
135         struct sparc_isa_device *isa_dev;
136
137         for_each_isa(isa_bus) {
138                 for_each_isadev(isa_dev, isa_bus) {
139                         if (!strcmp(isa_dev->prom_name, "power")) {
140                                 *resp = &isa_dev->resource;
141                                 *irq_p = isa_dev->irq;
142                                 *prom_node_p = isa_dev->prom_node;
143                                 return 0;
144                         }
145                 }
146         }
147         return -ENODEV;
148 }
149
150 void __init power_init(void)
151 {
152         struct resource *res = NULL;
153         unsigned int irq;
154         int prom_node;
155         static int invoked;
156
157         if (invoked)
158                 return;
159         invoked = 1;
160
161         if (!power_probe_ebus(&res, &irq, &prom_node))
162                 goto found;
163
164         if (!power_probe_isa(&res, &irq, &prom_node))
165                 goto found;
166
167         return;
168
169 found:
170         power_reg = ioremap(res->start, 0x4);
171         printk("power: Control reg at %p ... ", power_reg);
172         poweroff_method = machine_halt;  /* able to use the standard halt */
173         if (has_button_interrupt(irq, prom_node)) {
174                 if (kernel_thread(powerd, NULL, CLONE_FS) < 0) {
175                         printk("Failed to start power daemon.\n");
176                         return;
177                 }
178                 printk("powerd running.\n");
179
180                 if (request_irq(irq,
181                                 power_handler, SA_SHIRQ, "power", NULL) < 0)
182                         printk("power: Error, cannot register IRQ handler.\n");
183         } else {
184                 printk("not using powerd.\n");
185         }
186 }
187 #endif /* CONFIG_PCI */