1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License.  See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 1997, 1998, 2001, 03, 05, 06 by Ralf Baechle
7 */
8#include <linux/linkage.h>
9#include <linux/init.h>
10#include <linux/ds1286.h>
11#include <linux/module.h>
12#include <linux/interrupt.h>
13#include <linux/kernel.h>
14#include <linux/sched.h>
15#include <linux/notifier.h>
16#include <linux/pm.h>
17#include <linux/timer.h>
18
19#include <asm/io.h>
20#include <asm/irq.h>
21#include <asm/reboot.h>
22#include <asm/sgialib.h>
23#include <asm/sgi/ioc.h>
24#include <asm/sgi/hpc3.h>
25#include <asm/sgi/mc.h>
26#include <asm/sgi/ip22.h>
27
28/*
29 * Just powerdown if init hasn't done after POWERDOWN_TIMEOUT seconds.
30 * I'm not sure if this feature is a good idea, for now it's here just to
31 * make the power button make behave just like under IRIX.
32 */
33#define POWERDOWN_TIMEOUT	120
34
35/*
36 * Blink frequency during reboot grace period and when panicked.
37 */
38#define POWERDOWN_FREQ		(HZ / 4)
39#define PANIC_FREQ		(HZ / 8)
40
41static struct timer_list power_timer, blink_timer, debounce_timer;
42
43#define MACHINE_PANICED		1
44#define MACHINE_SHUTTING_DOWN	2
45
46static int machine_state;
47
48static void __noreturn sgi_machine_power_off(void)
49{
50	unsigned int tmp;
51
52	local_irq_disable();
53
54	/* Disable watchdog */
55	tmp = hpc3c0->rtcregs[RTC_CMD] & 0xff;
56	hpc3c0->rtcregs[RTC_CMD] = tmp | RTC_WAM;
57	hpc3c0->rtcregs[RTC_WSEC] = 0;
58	hpc3c0->rtcregs[RTC_WHSEC] = 0;
59
60	while (1) {
61		sgioc->panel = ~SGIOC_PANEL_POWERON;
62		/* Good bye cruel world ...  */
63
64		/* If we're still running, we probably got sent an alarm
65		   interrupt.  Read the flag to clear it.  */
66		tmp = hpc3c0->rtcregs[RTC_HOURS_ALARM];
67	}
68}
69
70static void __noreturn sgi_machine_restart(char *command)
71{
72	if (machine_state & MACHINE_SHUTTING_DOWN)
73		sgi_machine_power_off();
74	sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT;
75	while (1);
76}
77
78static void __noreturn sgi_machine_halt(void)
79{
80	if (machine_state & MACHINE_SHUTTING_DOWN)
81		sgi_machine_power_off();
82	ArcEnterInteractiveMode();
83}
84
85static void power_timeout(unsigned long data)
86{
87	sgi_machine_power_off();
88}
89
90static void blink_timeout(unsigned long data)
91{
92	/* XXX fix this for fullhouse  */
93	sgi_ioc_reset ^= (SGIOC_RESET_LC0OFF|SGIOC_RESET_LC1OFF);
94	sgioc->reset = sgi_ioc_reset;
95
96	mod_timer(&blink_timer, jiffies + data);
97}
98
99static void debounce(unsigned long data)
100{
101	del_timer(&debounce_timer);
102	if (sgint->istat1 & SGINT_ISTAT1_PWR) {
103		/* Interrupt still being sent. */
104		debounce_timer.expires = jiffies + (HZ / 20); /* 0.05s  */
105		add_timer(&debounce_timer);
106
107		sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR |
108			       SGIOC_PANEL_VOLDNINTR | SGIOC_PANEL_VOLDNHOLD |
109			       SGIOC_PANEL_VOLUPINTR | SGIOC_PANEL_VOLUPHOLD;
110
111		return;
112	}
113
114	if (machine_state & MACHINE_PANICED)
115		sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT;
116
117	enable_irq(SGI_PANEL_IRQ);
118}
119
120static inline void power_button(void)
121{
122	if (machine_state & MACHINE_PANICED)
123		return;
124
125	if ((machine_state & MACHINE_SHUTTING_DOWN) ||
126			kill_cad_pid(SIGINT, 1)) {
127		/* No init process or button pressed twice.  */
128		sgi_machine_power_off();
129	}
130
131	machine_state |= MACHINE_SHUTTING_DOWN;
132	blink_timer.data = POWERDOWN_FREQ;
133	blink_timeout(POWERDOWN_FREQ);
134
135	init_timer(&power_timer);
136	power_timer.function = power_timeout;
137	power_timer.expires = jiffies + POWERDOWN_TIMEOUT * HZ;
138	add_timer(&power_timer);
139}
140
141static irqreturn_t panel_int(int irq, void *dev_id)
142{
143	unsigned int buttons;
144
145	buttons = sgioc->panel;
146	sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR;
147
148	if (sgint->istat1 & SGINT_ISTAT1_PWR) {
149		/* Wait until interrupt goes away */
150		disable_irq_nosync(SGI_PANEL_IRQ);
151		init_timer(&debounce_timer);
152		debounce_timer.function = debounce;
153		debounce_timer.expires = jiffies + 5;
154		add_timer(&debounce_timer);
155	}
156
157	/* Power button was pressed
158	 * ioc.ps page 22: "The Panel Register is called Power Control by Full
159	 * House. Only lowest 2 bits are used. Guiness uses upper four bits
160	 * for volume control". This is not true, all bits are pulled high
161	 * on fullhouse */
162	if (!(buttons & SGIOC_PANEL_POWERINTR))
163		power_button();
164
165	return IRQ_HANDLED;
166}
167
168static int panic_event(struct notifier_block *this, unsigned long event,
169                      void *ptr)
170{
171	if (machine_state & MACHINE_PANICED)
172		return NOTIFY_DONE;
173	machine_state |= MACHINE_PANICED;
174
175	blink_timer.data = PANIC_FREQ;
176	blink_timeout(PANIC_FREQ);
177
178	return NOTIFY_DONE;
179}
180
181static struct notifier_block panic_block = {
182	.notifier_call	= panic_event,
183};
184
185static int __init reboot_setup(void)
186{
187	int res;
188
189	_machine_restart = sgi_machine_restart;
190	_machine_halt = sgi_machine_halt;
191	pm_power_off = sgi_machine_power_off;
192
193	res = request_irq(SGI_PANEL_IRQ, panel_int, 0, "Front Panel", NULL);
194	if (res) {
195		printk(KERN_ERR "Allocation of front panel IRQ failed\n");
196		return res;
197	}
198
199	init_timer(&blink_timer);
200	blink_timer.function = blink_timeout;
201	atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
202
203	return 0;
204}
205
206subsys_initcall(reboot_setup);
207