diff options
Diffstat (limited to 'arch/sparc64/kernel/power.c')
-rw-r--r-- | arch/sparc64/kernel/power.c | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/arch/sparc64/kernel/power.c b/arch/sparc64/kernel/power.c new file mode 100644 index 000000000000..52f14e399b1c --- /dev/null +++ b/arch/sparc64/kernel/power.c | |||
@@ -0,0 +1,150 @@ | |||
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 | #include <linux/config.h> | ||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/signal.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | |||
16 | #include <asm/system.h> | ||
17 | #include <asm/ebus.h> | ||
18 | #include <asm/auxio.h> | ||
19 | |||
20 | #define __KERNEL_SYSCALLS__ | ||
21 | #include <linux/unistd.h> | ||
22 | |||
23 | /* | ||
24 | * sysctl - toggle power-off restriction for serial console | ||
25 | * systems in machine_power_off() | ||
26 | */ | ||
27 | int scons_pwroff = 1; | ||
28 | |||
29 | #ifdef CONFIG_PCI | ||
30 | static void __iomem *power_reg; | ||
31 | |||
32 | static DECLARE_WAIT_QUEUE_HEAD(powerd_wait); | ||
33 | static int button_pressed; | ||
34 | |||
35 | static irqreturn_t power_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
36 | { | ||
37 | if (button_pressed == 0) { | ||
38 | button_pressed = 1; | ||
39 | wake_up(&powerd_wait); | ||
40 | } | ||
41 | |||
42 | /* FIXME: Check registers for status... */ | ||
43 | return IRQ_HANDLED; | ||
44 | } | ||
45 | #endif /* CONFIG_PCI */ | ||
46 | |||
47 | extern void machine_halt(void); | ||
48 | extern void machine_alt_power_off(void); | ||
49 | static void (*poweroff_method)(void) = machine_alt_power_off; | ||
50 | |||
51 | void machine_power_off(void) | ||
52 | { | ||
53 | if (!serial_console || scons_pwroff) { | ||
54 | #ifdef CONFIG_PCI | ||
55 | if (power_reg) { | ||
56 | /* Both register bits seem to have the | ||
57 | * same effect, so until I figure out | ||
58 | * what the difference is... | ||
59 | */ | ||
60 | writel(AUXIO_PCIO_CPWR_OFF | AUXIO_PCIO_SPWR_OFF, power_reg); | ||
61 | } else | ||
62 | #endif /* CONFIG_PCI */ | ||
63 | if (poweroff_method != NULL) { | ||
64 | poweroff_method(); | ||
65 | /* not reached */ | ||
66 | } | ||
67 | } | ||
68 | machine_halt(); | ||
69 | } | ||
70 | |||
71 | EXPORT_SYMBOL(machine_power_off); | ||
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(struct linux_ebus_device *edev) | ||
105 | { | ||
106 | if (edev->irqs[0] == PCI_IRQ_NONE) | ||
107 | return 0; | ||
108 | if (!prom_node_has_property(edev->prom_node, "button")) | ||
109 | return 0; | ||
110 | |||
111 | return 1; | ||
112 | } | ||
113 | |||
114 | void __init power_init(void) | ||
115 | { | ||
116 | struct linux_ebus *ebus; | ||
117 | struct linux_ebus_device *edev; | ||
118 | static int invoked; | ||
119 | |||
120 | if (invoked) | ||
121 | return; | ||
122 | invoked = 1; | ||
123 | |||
124 | for_each_ebus(ebus) { | ||
125 | for_each_ebusdev(edev, ebus) { | ||
126 | if (!strcmp(edev->prom_name, "power")) | ||
127 | goto found; | ||
128 | } | ||
129 | } | ||
130 | return; | ||
131 | |||
132 | found: | ||
133 | power_reg = ioremap(edev->resource[0].start, 0x4); | ||
134 | printk("power: Control reg at %p ... ", power_reg); | ||
135 | poweroff_method = machine_halt; /* able to use the standard halt */ | ||
136 | if (has_button_interrupt(edev)) { | ||
137 | if (kernel_thread(powerd, NULL, CLONE_FS) < 0) { | ||
138 | printk("Failed to start power daemon.\n"); | ||
139 | return; | ||
140 | } | ||
141 | printk("powerd running.\n"); | ||
142 | |||
143 | if (request_irq(edev->irqs[0], | ||
144 | power_handler, SA_SHIRQ, "power", NULL) < 0) | ||
145 | printk("power: Error, cannot register IRQ handler.\n"); | ||
146 | } else { | ||
147 | printk("not using powerd.\n"); | ||
148 | } | ||
149 | } | ||
150 | #endif /* CONFIG_PCI */ | ||