162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 1997, 1998, 2001, 03, 05, 06 by Ralf Baechle 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/linkage.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/rtc/ds1286.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/sched/signal.h> 1462306a36Sopenharmony_ci#include <linux/panic_notifier.h> 1562306a36Sopenharmony_ci#include <linux/pm.h> 1662306a36Sopenharmony_ci#include <linux/timer.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm/io.h> 1962306a36Sopenharmony_ci#include <asm/irq.h> 2062306a36Sopenharmony_ci#include <asm/reboot.h> 2162306a36Sopenharmony_ci#include <asm/sgialib.h> 2262306a36Sopenharmony_ci#include <asm/sgi/ioc.h> 2362306a36Sopenharmony_ci#include <asm/sgi/hpc3.h> 2462306a36Sopenharmony_ci#include <asm/sgi/mc.h> 2562306a36Sopenharmony_ci#include <asm/sgi/ip22.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * Just powerdown if init hasn't done after POWERDOWN_TIMEOUT seconds. 2962306a36Sopenharmony_ci * I'm not sure if this feature is a good idea, for now it's here just to 3062306a36Sopenharmony_ci * make the power button make behave just like under IRIX. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci#define POWERDOWN_TIMEOUT 120 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Blink frequency during reboot grace period and when panicked. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci#define POWERDOWN_FREQ (HZ / 4) 3862306a36Sopenharmony_ci#define PANIC_FREQ (HZ / 8) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic struct timer_list power_timer, blink_timer, debounce_timer; 4162306a36Sopenharmony_cistatic unsigned long blink_timer_timeout; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define MACHINE_PANICKED 1 4462306a36Sopenharmony_ci#define MACHINE_SHUTTING_DOWN 2 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int machine_state; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void __noreturn sgi_machine_power_off(void) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci unsigned int tmp; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci local_irq_disable(); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* Disable watchdog */ 5562306a36Sopenharmony_ci tmp = hpc3c0->rtcregs[RTC_CMD] & 0xff; 5662306a36Sopenharmony_ci hpc3c0->rtcregs[RTC_CMD] = tmp | RTC_WAM; 5762306a36Sopenharmony_ci hpc3c0->rtcregs[RTC_WSEC] = 0; 5862306a36Sopenharmony_ci hpc3c0->rtcregs[RTC_WHSEC] = 0; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci while (1) { 6162306a36Sopenharmony_ci sgioc->panel = ~SGIOC_PANEL_POWERON; 6262306a36Sopenharmony_ci /* Good bye cruel world ... */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* If we're still running, we probably got sent an alarm 6562306a36Sopenharmony_ci interrupt. Read the flag to clear it. */ 6662306a36Sopenharmony_ci tmp = hpc3c0->rtcregs[RTC_HOURS_ALARM]; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void __noreturn sgi_machine_restart(char *command) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci if (machine_state & MACHINE_SHUTTING_DOWN) 7362306a36Sopenharmony_ci sgi_machine_power_off(); 7462306a36Sopenharmony_ci sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT; 7562306a36Sopenharmony_ci while (1); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void __noreturn sgi_machine_halt(void) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci if (machine_state & MACHINE_SHUTTING_DOWN) 8162306a36Sopenharmony_ci sgi_machine_power_off(); 8262306a36Sopenharmony_ci ArcEnterInteractiveMode(); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic void power_timeout(struct timer_list *unused) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci sgi_machine_power_off(); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void blink_timeout(struct timer_list *unused) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci /* XXX fix this for fullhouse */ 9362306a36Sopenharmony_ci sgi_ioc_reset ^= (SGIOC_RESET_LC0OFF|SGIOC_RESET_LC1OFF); 9462306a36Sopenharmony_ci sgioc->reset = sgi_ioc_reset; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci mod_timer(&blink_timer, jiffies + blink_timer_timeout); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void debounce(struct timer_list *unused) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci del_timer(&debounce_timer); 10262306a36Sopenharmony_ci if (sgint->istat1 & SGINT_ISTAT1_PWR) { 10362306a36Sopenharmony_ci /* Interrupt still being sent. */ 10462306a36Sopenharmony_ci debounce_timer.expires = jiffies + (HZ / 20); /* 0.05s */ 10562306a36Sopenharmony_ci add_timer(&debounce_timer); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR | 10862306a36Sopenharmony_ci SGIOC_PANEL_VOLDNINTR | SGIOC_PANEL_VOLDNHOLD | 10962306a36Sopenharmony_ci SGIOC_PANEL_VOLUPINTR | SGIOC_PANEL_VOLUPHOLD; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (machine_state & MACHINE_PANICKED) 11562306a36Sopenharmony_ci sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci enable_irq(SGI_PANEL_IRQ); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic inline void power_button(void) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci if (machine_state & MACHINE_PANICKED) 12362306a36Sopenharmony_ci return; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if ((machine_state & MACHINE_SHUTTING_DOWN) || 12662306a36Sopenharmony_ci kill_cad_pid(SIGINT, 1)) { 12762306a36Sopenharmony_ci /* No init process or button pressed twice. */ 12862306a36Sopenharmony_ci sgi_machine_power_off(); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci machine_state |= MACHINE_SHUTTING_DOWN; 13262306a36Sopenharmony_ci blink_timer_timeout = POWERDOWN_FREQ; 13362306a36Sopenharmony_ci blink_timeout(&blink_timer); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci timer_setup(&power_timer, power_timeout, 0); 13662306a36Sopenharmony_ci power_timer.expires = jiffies + POWERDOWN_TIMEOUT * HZ; 13762306a36Sopenharmony_ci add_timer(&power_timer); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic irqreturn_t panel_int(int irq, void *dev_id) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci unsigned int buttons; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci buttons = sgioc->panel; 14562306a36Sopenharmony_ci sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (sgint->istat1 & SGINT_ISTAT1_PWR) { 14862306a36Sopenharmony_ci /* Wait until interrupt goes away */ 14962306a36Sopenharmony_ci disable_irq_nosync(SGI_PANEL_IRQ); 15062306a36Sopenharmony_ci timer_setup(&debounce_timer, debounce, 0); 15162306a36Sopenharmony_ci debounce_timer.expires = jiffies + 5; 15262306a36Sopenharmony_ci add_timer(&debounce_timer); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Power button was pressed 15662306a36Sopenharmony_ci * ioc.ps page 22: "The Panel Register is called Power Control by Full 15762306a36Sopenharmony_ci * House. Only lowest 2 bits are used. Guiness uses upper four bits 15862306a36Sopenharmony_ci * for volume control". This is not true, all bits are pulled high 15962306a36Sopenharmony_ci * on fullhouse */ 16062306a36Sopenharmony_ci if (!(buttons & SGIOC_PANEL_POWERINTR)) 16162306a36Sopenharmony_ci power_button(); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return IRQ_HANDLED; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int panic_event(struct notifier_block *this, unsigned long event, 16762306a36Sopenharmony_ci void *ptr) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci if (machine_state & MACHINE_PANICKED) 17062306a36Sopenharmony_ci return NOTIFY_DONE; 17162306a36Sopenharmony_ci machine_state |= MACHINE_PANICKED; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci blink_timer_timeout = PANIC_FREQ; 17462306a36Sopenharmony_ci blink_timeout(&blink_timer); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return NOTIFY_DONE; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic struct notifier_block panic_block = { 18062306a36Sopenharmony_ci .notifier_call = panic_event, 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int __init reboot_setup(void) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci int res; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci _machine_restart = sgi_machine_restart; 18862306a36Sopenharmony_ci _machine_halt = sgi_machine_halt; 18962306a36Sopenharmony_ci pm_power_off = sgi_machine_power_off; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci res = request_irq(SGI_PANEL_IRQ, panel_int, 0, "Front Panel", NULL); 19262306a36Sopenharmony_ci if (res) { 19362306a36Sopenharmony_ci printk(KERN_ERR "Allocation of front panel IRQ failed\n"); 19462306a36Sopenharmony_ci return res; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci timer_setup(&blink_timer, blink_timeout, 0); 19862306a36Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, &panic_block); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cisubsys_initcall(reboot_setup); 204