2005-04-17 02:20:36 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
2006-01-15 21:10:39 +03:00
* Copyright ( C ) 1997 , 1998 , 2001 , 03 , 05 by Ralf Baechle
2005-04-17 02:20:36 +04:00
*/
2006-01-15 21:10:39 +03:00
# include <linux/linkage.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/ds1286.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/notifier.h>
# include <linux/timer.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/system.h>
# include <asm/reboot.h>
# include <asm/sgialib.h>
# include <asm/sgi/ioc.h>
# include <asm/sgi/hpc3.h>
# include <asm/sgi/mc.h>
# include <asm/sgi/ip22.h>
/*
* Just powerdown if init hasn ' t done after POWERDOWN_TIMEOUT seconds .
* I ' m not sure if this feature is a good idea , for now it ' s here just to
* make the power button make behave just like under IRIX .
*/
# define POWERDOWN_TIMEOUT 120
/*
* Blink frequency during reboot grace period and when paniced .
*/
# define POWERDOWN_FREQ (HZ / 4)
# define PANIC_FREQ (HZ / 8)
static struct timer_list power_timer , blink_timer , debounce_timer , volume_timer ;
# define MACHINE_PANICED 1
# define MACHINE_SHUTTING_DOWN 2
2006-01-15 21:10:39 +03:00
static int machine_state ;
2005-04-17 02:20:36 +04:00
2006-01-15 21:10:39 +03:00
static void ATTRIB_NORET sgi_machine_power_off ( void )
2005-04-17 02:20:36 +04:00
{
unsigned int tmp ;
local_irq_disable ( ) ;
/* Disable watchdog */
tmp = hpc3c0 - > rtcregs [ RTC_CMD ] & 0xff ;
hpc3c0 - > rtcregs [ RTC_CMD ] = tmp | RTC_WAM ;
hpc3c0 - > rtcregs [ RTC_WSEC ] = 0 ;
hpc3c0 - > rtcregs [ RTC_WHSEC ] = 0 ;
while ( 1 ) {
sgioc - > panel = ~ SGIOC_PANEL_POWERON ;
/* Good bye cruel world ... */
/* If we're still running, we probably got sent an alarm
interrupt . Read the flag to clear it . */
tmp = hpc3c0 - > rtcregs [ RTC_HOURS_ALARM ] ;
}
}
2006-01-15 21:10:39 +03:00
static void ATTRIB_NORET sgi_machine_restart ( char * command )
{
if ( machine_state & MACHINE_SHUTTING_DOWN )
sgi_machine_power_off ( ) ;
sgimc - > cpuctrl0 | = SGIMC_CCTRL0_SYSINIT ;
while ( 1 ) ;
}
static void ATTRIB_NORET sgi_machine_halt ( void )
{
if ( machine_state & MACHINE_SHUTTING_DOWN )
sgi_machine_power_off ( ) ;
ArcEnterInteractiveMode ( ) ;
}
2005-04-17 02:20:36 +04:00
static void power_timeout ( unsigned long data )
{
sgi_machine_power_off ( ) ;
}
static void blink_timeout ( unsigned long data )
{
/* XXX fix this for fullhouse */
sgi_ioc_reset ^ = ( SGIOC_RESET_LC0OFF | SGIOC_RESET_LC1OFF ) ;
sgioc - > reset = sgi_ioc_reset ;
2006-01-15 21:10:39 +03:00
mod_timer ( & blink_timer , jiffies + data ) ;
2005-04-17 02:20:36 +04:00
}
static void debounce ( unsigned long data )
{
del_timer ( & debounce_timer ) ;
if ( sgint - > istat1 & SGINT_ISTAT1_PWR ) {
/* Interrupt still being sent. */
2006-01-15 21:10:39 +03:00
debounce_timer . expires = jiffies + ( HZ / 20 ) ; /* 0.05s */
2005-04-17 02:20:36 +04:00
add_timer ( & debounce_timer ) ;
sgioc - > panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR |
SGIOC_PANEL_VOLDNINTR | SGIOC_PANEL_VOLDNHOLD |
SGIOC_PANEL_VOLUPINTR | SGIOC_PANEL_VOLUPHOLD ;
return ;
}
if ( machine_state & MACHINE_PANICED )
sgimc - > cpuctrl0 | = SGIMC_CCTRL0_SYSINIT ;
enable_irq ( SGI_PANEL_IRQ ) ;
}
static inline void power_button ( void )
{
if ( machine_state & MACHINE_PANICED )
return ;
if ( ( machine_state & MACHINE_SHUTTING_DOWN ) | | kill_proc ( 1 , SIGINT , 1 ) ) {
/* No init process or button pressed twice. */
sgi_machine_power_off ( ) ;
}
machine_state | = MACHINE_SHUTTING_DOWN ;
blink_timer . data = POWERDOWN_FREQ ;
blink_timeout ( POWERDOWN_FREQ ) ;
init_timer ( & power_timer ) ;
power_timer . function = power_timeout ;
power_timer . expires = jiffies + POWERDOWN_TIMEOUT * HZ ;
add_timer ( & power_timer ) ;
}
void ( * indy_volume_button ) ( int ) = NULL ;
EXPORT_SYMBOL ( indy_volume_button ) ;
static inline void volume_up_button ( unsigned long data )
{
del_timer ( & volume_timer ) ;
if ( indy_volume_button )
indy_volume_button ( 1 ) ;
if ( sgint - > istat1 & SGINT_ISTAT1_PWR ) {
2006-01-15 21:10:39 +03:00
volume_timer . expires = jiffies + ( HZ / 100 ) ;
2005-04-17 02:20:36 +04:00
add_timer ( & volume_timer ) ;
}
}
static inline void volume_down_button ( unsigned long data )
{
del_timer ( & volume_timer ) ;
if ( indy_volume_button )
indy_volume_button ( - 1 ) ;
if ( sgint - > istat1 & SGINT_ISTAT1_PWR ) {
2006-01-15 21:10:39 +03:00
volume_timer . expires = jiffies + ( HZ / 100 ) ;
2005-04-17 02:20:36 +04:00
add_timer ( & volume_timer ) ;
}
}
static irqreturn_t panel_int ( int irq , void * dev_id , struct pt_regs * regs )
{
unsigned int buttons ;
buttons = sgioc - > panel ;
sgioc - > panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR ;
if ( sgint - > istat1 & SGINT_ISTAT1_PWR ) {
/* Wait until interrupt goes away */
disable_irq ( SGI_PANEL_IRQ ) ;
init_timer ( & debounce_timer ) ;
debounce_timer . function = debounce ;
debounce_timer . expires = jiffies + 5 ;
add_timer ( & debounce_timer ) ;
}
2005-09-04 02:56:17 +04:00
/* Power button was pressed
2005-04-17 02:20:36 +04:00
* ioc . ps page 22 : " The Panel Register is called Power Control by Full
* House . Only lowest 2 bits are used . Guiness uses upper four bits
* for volume control " . This is not true, all bits are pulled high
* on fullhouse */
if ( ip22_is_fullhouse ( ) | | ! ( buttons & SGIOC_PANEL_POWERINTR ) ) {
power_button ( ) ;
return IRQ_HANDLED ;
}
/* TODO: mute/unmute */
/* Volume up button was pressed */
if ( ! ( buttons & SGIOC_PANEL_VOLUPINTR ) ) {
init_timer ( & volume_timer ) ;
volume_timer . function = volume_up_button ;
2006-01-15 21:10:39 +03:00
volume_timer . expires = jiffies + ( HZ / 100 ) ;
2005-04-17 02:20:36 +04:00
add_timer ( & volume_timer ) ;
}
/* Volume down button was pressed */
if ( ! ( buttons & SGIOC_PANEL_VOLDNINTR ) ) {
init_timer ( & volume_timer ) ;
volume_timer . function = volume_down_button ;
2006-01-15 21:10:39 +03:00
volume_timer . expires = jiffies + ( HZ / 100 ) ;
2005-04-17 02:20:36 +04:00
add_timer ( & volume_timer ) ;
}
return IRQ_HANDLED ;
}
static int panic_event ( struct notifier_block * this , unsigned long event ,
void * ptr )
{
if ( machine_state & MACHINE_PANICED )
return NOTIFY_DONE ;
machine_state | = MACHINE_PANICED ;
blink_timer . data = PANIC_FREQ ;
blink_timeout ( PANIC_FREQ ) ;
return NOTIFY_DONE ;
}
static struct notifier_block panic_block = {
. notifier_call = panic_event ,
} ;
static int __init reboot_setup ( void )
{
_machine_restart = sgi_machine_restart ;
_machine_halt = sgi_machine_halt ;
_machine_power_off = sgi_machine_power_off ;
request_irq ( SGI_PANEL_IRQ , panel_int , 0 , " Front Panel " , NULL ) ;
init_timer ( & blink_timer ) ;
blink_timer . function = blink_timeout ;
notifier_chain_register ( & panic_notifier_list , & panic_block ) ;
return 0 ;
}
subsys_initcall ( reboot_setup ) ;