11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * This file is subject to the terms and conditions of the GNU General Public 31da177e4SLinus Torvalds * License. See the file "COPYING" in the main directory of this archive 41da177e4SLinus Torvalds * for more details. 51da177e4SLinus Torvalds * 6fcdb27adSRalf Baechle * Copyright (C) 1997, 1998, 2001, 03, 05, 06 by Ralf Baechle 71da177e4SLinus Torvalds */ 871baa1a5SRalf Baechle #include <linux/linkage.h> 91da177e4SLinus Torvalds #include <linux/init.h> 101da177e4SLinus Torvalds #include <linux/ds1286.h> 111da177e4SLinus Torvalds #include <linux/module.h> 121da177e4SLinus Torvalds #include <linux/interrupt.h> 131da177e4SLinus Torvalds #include <linux/kernel.h> 141da177e4SLinus Torvalds #include <linux/sched.h> 151da177e4SLinus Torvalds #include <linux/notifier.h> 16fcdb27adSRalf Baechle #include <linux/pm.h> 171da177e4SLinus Torvalds #include <linux/timer.h> 181da177e4SLinus Torvalds 191da177e4SLinus Torvalds #include <asm/io.h> 201da177e4SLinus Torvalds #include <asm/irq.h> 211da177e4SLinus Torvalds #include <asm/system.h> 221da177e4SLinus Torvalds #include <asm/reboot.h> 231da177e4SLinus Torvalds #include <asm/sgialib.h> 241da177e4SLinus Torvalds #include <asm/sgi/ioc.h> 251da177e4SLinus Torvalds #include <asm/sgi/hpc3.h> 261da177e4SLinus Torvalds #include <asm/sgi/mc.h> 271da177e4SLinus Torvalds #include <asm/sgi/ip22.h> 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds /* 301da177e4SLinus Torvalds * Just powerdown if init hasn't done after POWERDOWN_TIMEOUT seconds. 311da177e4SLinus Torvalds * I'm not sure if this feature is a good idea, for now it's here just to 321da177e4SLinus Torvalds * make the power button make behave just like under IRIX. 331da177e4SLinus Torvalds */ 341da177e4SLinus Torvalds #define POWERDOWN_TIMEOUT 120 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds /* 37f18190bdSLee Revell * Blink frequency during reboot grace period and when panicked. 381da177e4SLinus Torvalds */ 391da177e4SLinus Torvalds #define POWERDOWN_FREQ (HZ / 4) 401da177e4SLinus Torvalds #define PANIC_FREQ (HZ / 8) 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds static struct timer_list power_timer, blink_timer, debounce_timer, volume_timer; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds #define MACHINE_PANICED 1 451da177e4SLinus Torvalds #define MACHINE_SHUTTING_DOWN 2 461da177e4SLinus Torvalds 4771baa1a5SRalf Baechle static int machine_state; 481da177e4SLinus Torvalds 4971baa1a5SRalf Baechle static void ATTRIB_NORET sgi_machine_power_off(void) 501da177e4SLinus Torvalds { 511da177e4SLinus Torvalds unsigned int tmp; 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds local_irq_disable(); 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds /* Disable watchdog */ 561da177e4SLinus Torvalds tmp = hpc3c0->rtcregs[RTC_CMD] & 0xff; 571da177e4SLinus Torvalds hpc3c0->rtcregs[RTC_CMD] = tmp | RTC_WAM; 581da177e4SLinus Torvalds hpc3c0->rtcregs[RTC_WSEC] = 0; 591da177e4SLinus Torvalds hpc3c0->rtcregs[RTC_WHSEC] = 0; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds while (1) { 621da177e4SLinus Torvalds sgioc->panel = ~SGIOC_PANEL_POWERON; 631da177e4SLinus Torvalds /* Good bye cruel world ... */ 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds /* If we're still running, we probably got sent an alarm 661da177e4SLinus Torvalds interrupt. Read the flag to clear it. */ 671da177e4SLinus Torvalds tmp = hpc3c0->rtcregs[RTC_HOURS_ALARM]; 681da177e4SLinus Torvalds } 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 7171baa1a5SRalf Baechle static void ATTRIB_NORET sgi_machine_restart(char *command) 7271baa1a5SRalf Baechle { 7371baa1a5SRalf Baechle if (machine_state & MACHINE_SHUTTING_DOWN) 7471baa1a5SRalf Baechle sgi_machine_power_off(); 7571baa1a5SRalf Baechle sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT; 7671baa1a5SRalf Baechle while (1); 7771baa1a5SRalf Baechle } 7871baa1a5SRalf Baechle 7971baa1a5SRalf Baechle static void ATTRIB_NORET sgi_machine_halt(void) 8071baa1a5SRalf Baechle { 8171baa1a5SRalf Baechle if (machine_state & MACHINE_SHUTTING_DOWN) 8271baa1a5SRalf Baechle sgi_machine_power_off(); 8371baa1a5SRalf Baechle ArcEnterInteractiveMode(); 8471baa1a5SRalf Baechle } 8571baa1a5SRalf Baechle 861da177e4SLinus Torvalds static void power_timeout(unsigned long data) 871da177e4SLinus Torvalds { 881da177e4SLinus Torvalds sgi_machine_power_off(); 891da177e4SLinus Torvalds } 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds static void blink_timeout(unsigned long data) 921da177e4SLinus Torvalds { 931da177e4SLinus Torvalds /* XXX fix this for fullhouse */ 941da177e4SLinus Torvalds sgi_ioc_reset ^= (SGIOC_RESET_LC0OFF|SGIOC_RESET_LC1OFF); 951da177e4SLinus Torvalds sgioc->reset = sgi_ioc_reset; 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds mod_timer(&blink_timer, jiffies + data); 981da177e4SLinus Torvalds } 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds static void debounce(unsigned long data) 1011da177e4SLinus Torvalds { 1021da177e4SLinus Torvalds del_timer(&debounce_timer); 1031da177e4SLinus Torvalds if (sgint->istat1 & SGINT_ISTAT1_PWR) { 1041da177e4SLinus Torvalds /* Interrupt still being sent. */ 10571baa1a5SRalf Baechle debounce_timer.expires = jiffies + (HZ / 20); /* 0.05s */ 1061da177e4SLinus Torvalds add_timer(&debounce_timer); 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR | 1091da177e4SLinus Torvalds SGIOC_PANEL_VOLDNINTR | SGIOC_PANEL_VOLDNHOLD | 1101da177e4SLinus Torvalds SGIOC_PANEL_VOLUPINTR | SGIOC_PANEL_VOLUPHOLD; 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds return; 1131da177e4SLinus Torvalds } 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds if (machine_state & MACHINE_PANICED) 1161da177e4SLinus Torvalds sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT; 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds enable_irq(SGI_PANEL_IRQ); 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds static inline void power_button(void) 1221da177e4SLinus Torvalds { 1231da177e4SLinus Torvalds if (machine_state & MACHINE_PANICED) 1241da177e4SLinus Torvalds return; 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds if ((machine_state & MACHINE_SHUTTING_DOWN) || kill_proc(1,SIGINT,1)) { 1271da177e4SLinus Torvalds /* No init process or button pressed twice. */ 1281da177e4SLinus Torvalds sgi_machine_power_off(); 1291da177e4SLinus Torvalds } 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds machine_state |= MACHINE_SHUTTING_DOWN; 1321da177e4SLinus Torvalds blink_timer.data = POWERDOWN_FREQ; 1331da177e4SLinus Torvalds blink_timeout(POWERDOWN_FREQ); 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds init_timer(&power_timer); 1361da177e4SLinus Torvalds power_timer.function = power_timeout; 1371da177e4SLinus Torvalds power_timer.expires = jiffies + POWERDOWN_TIMEOUT * HZ; 1381da177e4SLinus Torvalds add_timer(&power_timer); 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds void (*indy_volume_button)(int) = NULL; 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds EXPORT_SYMBOL(indy_volume_button); 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds static inline void volume_up_button(unsigned long data) 1461da177e4SLinus Torvalds { 1471da177e4SLinus Torvalds del_timer(&volume_timer); 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds if (indy_volume_button) 1501da177e4SLinus Torvalds indy_volume_button(1); 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds if (sgint->istat1 & SGINT_ISTAT1_PWR) { 15371baa1a5SRalf Baechle volume_timer.expires = jiffies + (HZ / 100); 1541da177e4SLinus Torvalds add_timer(&volume_timer); 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds } 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds static inline void volume_down_button(unsigned long data) 1591da177e4SLinus Torvalds { 1601da177e4SLinus Torvalds del_timer(&volume_timer); 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds if (indy_volume_button) 1631da177e4SLinus Torvalds indy_volume_button(-1); 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds if (sgint->istat1 & SGINT_ISTAT1_PWR) { 16671baa1a5SRalf Baechle volume_timer.expires = jiffies + (HZ / 100); 1671da177e4SLinus Torvalds add_timer(&volume_timer); 1681da177e4SLinus Torvalds } 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds static irqreturn_t panel_int(int irq, void *dev_id, struct pt_regs *regs) 1721da177e4SLinus Torvalds { 1731da177e4SLinus Torvalds unsigned int buttons; 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds buttons = sgioc->panel; 1761da177e4SLinus Torvalds sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR; 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds if (sgint->istat1 & SGINT_ISTAT1_PWR) { 1791da177e4SLinus Torvalds /* Wait until interrupt goes away */ 1801da177e4SLinus Torvalds disable_irq(SGI_PANEL_IRQ); 1811da177e4SLinus Torvalds init_timer(&debounce_timer); 1821da177e4SLinus Torvalds debounce_timer.function = debounce; 1831da177e4SLinus Torvalds debounce_timer.expires = jiffies + 5; 1841da177e4SLinus Torvalds add_timer(&debounce_timer); 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds /* Power button was pressed 1881da177e4SLinus Torvalds * ioc.ps page 22: "The Panel Register is called Power Control by Full 1891da177e4SLinus Torvalds * House. Only lowest 2 bits are used. Guiness uses upper four bits 1901da177e4SLinus Torvalds * for volume control". This is not true, all bits are pulled high 1911da177e4SLinus Torvalds * on fullhouse */ 1921da177e4SLinus Torvalds if (ip22_is_fullhouse() || !(buttons & SGIOC_PANEL_POWERINTR)) { 1931da177e4SLinus Torvalds power_button(); 1941da177e4SLinus Torvalds return IRQ_HANDLED; 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds /* TODO: mute/unmute */ 1971da177e4SLinus Torvalds /* Volume up button was pressed */ 1981da177e4SLinus Torvalds if (!(buttons & SGIOC_PANEL_VOLUPINTR)) { 1991da177e4SLinus Torvalds init_timer(&volume_timer); 2001da177e4SLinus Torvalds volume_timer.function = volume_up_button; 20171baa1a5SRalf Baechle volume_timer.expires = jiffies + (HZ / 100); 2021da177e4SLinus Torvalds add_timer(&volume_timer); 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds /* Volume down button was pressed */ 2051da177e4SLinus Torvalds if (!(buttons & SGIOC_PANEL_VOLDNINTR)) { 2061da177e4SLinus Torvalds init_timer(&volume_timer); 2071da177e4SLinus Torvalds volume_timer.function = volume_down_button; 20871baa1a5SRalf Baechle volume_timer.expires = jiffies + (HZ / 100); 2091da177e4SLinus Torvalds add_timer(&volume_timer); 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds return IRQ_HANDLED; 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds static int panic_event(struct notifier_block *this, unsigned long event, 2161da177e4SLinus Torvalds void *ptr) 2171da177e4SLinus Torvalds { 2181da177e4SLinus Torvalds if (machine_state & MACHINE_PANICED) 2191da177e4SLinus Torvalds return NOTIFY_DONE; 2201da177e4SLinus Torvalds machine_state |= MACHINE_PANICED; 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds blink_timer.data = PANIC_FREQ; 2231da177e4SLinus Torvalds blink_timeout(PANIC_FREQ); 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds return NOTIFY_DONE; 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds static struct notifier_block panic_block = { 2291da177e4SLinus Torvalds .notifier_call = panic_event, 2301da177e4SLinus Torvalds }; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds static int __init reboot_setup(void) 2331da177e4SLinus Torvalds { 2341da177e4SLinus Torvalds _machine_restart = sgi_machine_restart; 2351da177e4SLinus Torvalds _machine_halt = sgi_machine_halt; 236fcdb27adSRalf Baechle pm_power_off = sgi_machine_power_off; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds request_irq(SGI_PANEL_IRQ, panel_int, 0, "Front Panel", NULL); 2391da177e4SLinus Torvalds init_timer(&blink_timer); 2401da177e4SLinus Torvalds blink_timer.function = blink_timeout; 241e041c683SAlan Stern atomic_notifier_chain_register(&panic_notifier_list, &panic_block); 2421da177e4SLinus Torvalds 2431da177e4SLinus Torvalds return 0; 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds subsys_initcall(reboot_setup); 247