1/* $Id: power.c,v 1.1.1.1 2008/10/15 03:26:19 james26_jang 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/init.h>
10#include <linux/sched.h>
11#include <linux/signal.h>
12#include <linux/delay.h>
13
14#include <asm/ebus.h>
15
16#define __KERNEL_SYSCALLS__
17#include <linux/unistd.h>
18
19#ifdef CONFIG_PCI
20static unsigned long power_reg = 0UL;
21#define POWER_SYSTEM_OFF (1 << 0)
22#define POWER_COURTESY_OFF (1 << 1)
23
24static DECLARE_WAIT_QUEUE_HEAD(powerd_wait);
25static int button_pressed;
26
27static void power_handler(int irq, void *dev_id, struct pt_regs *regs)
28{
29	if (button_pressed == 0) {
30		wake_up(&powerd_wait);
31		button_pressed = 1;
32	}
33}
34#endif /* CONFIG_PCI */
35
36extern void machine_halt(void);
37extern void machine_alt_power_off(void);
38static void (*poweroff_method)(void) = machine_alt_power_off;
39
40extern int serial_console;
41
42void machine_power_off(void)
43{
44	if (!serial_console) {
45#ifdef CONFIG_PCI
46		if (power_reg != 0UL) {
47			/* Both register bits seem to have the
48			 * same effect, so until I figure out
49			 * what the difference is...
50			 */
51			writel(POWER_COURTESY_OFF | POWER_SYSTEM_OFF, power_reg);
52		} else
53#endif /* CONFIG_PCI */
54			if (poweroff_method != NULL) {
55				poweroff_method();
56				/* not reached */
57			}
58	}
59	machine_halt();
60}
61
62#ifdef CONFIG_PCI
63static int powerd(void *__unused)
64{
65	static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
66	char *argv[] = { "/sbin/shutdown", "-h", "now", NULL };
67
68	daemonize();
69	sprintf(current->comm, "powerd");
70
71again:
72	while (button_pressed == 0) {
73		spin_lock_irq(&current->sigmask_lock);
74		flush_signals(current);
75		spin_unlock_irq(&current->sigmask_lock);
76		interruptible_sleep_on(&powerd_wait);
77	}
78
79	/* Ok, down we go... */
80	if (execve("/sbin/shutdown", argv, envp) < 0) {
81		printk("powerd: shutdown execution failed\n");
82		button_pressed = 0;
83		goto again;
84	}
85	return 0;
86}
87
88void __init power_init(void)
89{
90	struct linux_ebus *ebus;
91	struct linux_ebus_device *edev;
92	static int invoked;
93
94	if (invoked)
95		return;
96	invoked = 1;
97
98	for_each_ebus(ebus) {
99		for_each_ebusdev(edev, ebus) {
100			if (!strcmp(edev->prom_name, "power"))
101				goto found;
102		}
103	}
104	return;
105
106found:
107	power_reg = (unsigned long)ioremap(edev->resource[0].start, 0x4);
108	printk("power: Control reg at %016lx ... ", power_reg);
109	poweroff_method = machine_halt; /* able to use the standard poweroff */
110	if (edev->irqs[0] != PCI_IRQ_NONE) {
111		if (kernel_thread(powerd, 0, CLONE_FS) < 0) {
112			printk("Failed to start power daemon.\n");
113			return;
114		}
115		printk("powerd running.\n");
116
117		if (request_irq(edev->irqs[0],
118				power_handler, SA_SHIRQ, "power",
119				(void *) power_reg) < 0)
120			printk("power: Error, cannot register IRQ handler.\n");
121	} else {
122		printk("not using powerd.\n");
123	}
124}
125#endif /* CONFIG_PCI */
126