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 .
*
* Copyright ( C ) 2001 Keith M Wesolowski
* Copyright ( C ) 2001 Paul Mundt
* Copyright ( C ) 2003 Guido Guenther < agx @ sigxcpu . org >
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/notifier.h>
# include <linux/delay.h>
# include <linux/ds17287rtc.h>
# include <linux/interrupt.h>
2006-01-18 20:37:07 +03:00
# include <linux/pm.h>
2005-04-17 02:20:36 +04:00
# include <asm/addrspace.h>
# include <asm/irq.h>
# include <asm/reboot.h>
# include <asm/system.h>
# include <asm/wbflush.h>
# include <asm/ip32/mace.h>
# include <asm/ip32/crime.h>
# include <asm/ip32/ip32_ints.h>
# 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 ;
static int has_paniced , shuting_down ;
static void ip32_machine_restart ( char * command ) __attribute__ ( ( noreturn ) ) ;
static void ip32_machine_halt ( void ) __attribute__ ( ( noreturn ) ) ;
static void ip32_machine_power_off ( void ) __attribute__ ( ( noreturn ) ) ;
static void ip32_machine_restart ( char * cmd )
{
crime - > control = CRIME_CONTROL_HARD_RESET ;
while ( 1 ) ;
}
static inline void ip32_machine_halt ( void )
{
ip32_machine_power_off ( ) ;
}
static void ip32_machine_power_off ( void )
{
volatile unsigned char reg_a , xctrl_a , xctrl_b ;
disable_irq ( MACEISA_RTC_IRQ ) ;
reg_a = CMOS_READ ( RTC_REG_A ) ;
/* setup for kickstart & wake-up (DS12287 Ref. Man. p. 19) */
reg_a & = ~ DS_REGA_DV2 ;
reg_a | = DS_REGA_DV1 ;
CMOS_WRITE ( reg_a | DS_REGA_DV0 , RTC_REG_A ) ;
wbflush ( ) ;
xctrl_b = CMOS_READ ( DS_B1_XCTRL4B )
| DS_XCTRL4B_ABE | DS_XCTRL4B_KFE ;
CMOS_WRITE ( xctrl_b , DS_B1_XCTRL4B ) ;
xctrl_a = CMOS_READ ( DS_B1_XCTRL4A ) & ~ DS_XCTRL4A_IFS ;
CMOS_WRITE ( xctrl_a , DS_B1_XCTRL4A ) ;
wbflush ( ) ;
/* adios amigos... */
CMOS_WRITE ( xctrl_a | DS_XCTRL4A_PAB , DS_B1_XCTRL4A ) ;
CMOS_WRITE ( reg_a , RTC_REG_A ) ;
wbflush ( ) ;
while ( 1 ) ;
}
static void power_timeout ( unsigned long data )
{
ip32_machine_power_off ( ) ;
}
static void blink_timeout ( unsigned long data )
{
unsigned long led = mace - > perif . ctrl . misc ^ MACEISA_LED_RED ;
mace - > perif . ctrl . misc = led ;
mod_timer ( & blink_timer , jiffies + data ) ;
}
static void debounce ( unsigned long data )
{
volatile unsigned char reg_a , reg_c , xctrl_a ;
reg_c = CMOS_READ ( RTC_INTR_FLAGS ) ;
CMOS_WRITE ( reg_a | DS_REGA_DV0 , RTC_REG_A ) ;
wbflush ( ) ;
xctrl_a = CMOS_READ ( DS_B1_XCTRL4A ) ;
if ( ( xctrl_a & DS_XCTRL4A_IFS ) | | ( reg_c & RTC_IRQF ) ) {
/* Interrupt still being sent. */
debounce_timer . expires = jiffies + 50 ;
add_timer ( & debounce_timer ) ;
/* clear interrupt source */
CMOS_WRITE ( xctrl_a & ~ DS_XCTRL4A_IFS , DS_B1_XCTRL4A ) ;
CMOS_WRITE ( reg_a & ~ DS_REGA_DV0 , RTC_REG_A ) ;
return ;
}
CMOS_WRITE ( reg_a & ~ DS_REGA_DV0 , RTC_REG_A ) ;
if ( has_paniced )
ip32_machine_restart ( NULL ) ;
enable_irq ( MACEISA_RTC_IRQ ) ;
}
static inline void ip32_power_button ( void )
{
if ( has_paniced )
return ;
if ( shuting_down | | kill_proc ( 1 , SIGINT , 1 ) ) {
/* No init process or button pressed twice. */
ip32_machine_power_off ( ) ;
}
shuting_down = 1 ;
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 ) ;
}
static irqreturn_t ip32_rtc_int ( int irq , void * dev_id , struct pt_regs * regs )
{
volatile unsigned char reg_c ;
reg_c = CMOS_READ ( RTC_INTR_FLAGS ) ;
if ( ! ( reg_c & RTC_IRQF ) ) {
2005-09-04 02:56:17 +04:00
printk ( KERN_WARNING
2005-04-17 02:20:36 +04:00
" %s: RTC IRQ without RTC_IRQF \n " , __FUNCTION__ ) ;
}
/* Wait until interrupt goes away */
disable_irq ( MACEISA_RTC_IRQ ) ;
init_timer ( & debounce_timer ) ;
debounce_timer . function = debounce ;
debounce_timer . expires = jiffies + 50 ;
add_timer ( & debounce_timer ) ;
printk ( KERN_DEBUG " Power button pressed \n " ) ;
ip32_power_button ( ) ;
return IRQ_HANDLED ;
}
static int panic_event ( struct notifier_block * this , unsigned long event ,
void * ptr )
{
unsigned long led ;
if ( has_paniced )
return NOTIFY_DONE ;
has_paniced = 1 ;
/* turn off the green LED */
led = mace - > perif . ctrl . misc | MACEISA_LED_GREEN ;
mace - > perif . ctrl . misc = led ;
blink_timer . data = PANIC_FREQ ;
blink_timeout ( PANIC_FREQ ) ;
return NOTIFY_DONE ;
}
static struct notifier_block panic_block = {
. notifier_call = panic_event ,
} ;
static __init int ip32_reboot_setup ( void )
{
/* turn on the green led only */
unsigned long led = mace - > perif . ctrl . misc ;
led | = MACEISA_LED_RED ;
led & = ~ MACEISA_LED_GREEN ;
mace - > perif . ctrl . misc = led ;
_machine_restart = ip32_machine_restart ;
_machine_halt = ip32_machine_halt ;
2006-01-18 20:37:07 +03:00
pm_power_off = ip32_machine_power_off ;
2005-04-17 02:20:36 +04:00
init_timer ( & blink_timer ) ;
blink_timer . function = blink_timeout ;
notifier_chain_register ( & panic_notifier_list , & panic_block ) ;
request_irq ( MACEISA_RTC_IRQ , ip32_rtc_int , 0 , " rtc " , NULL ) ;
return 0 ;
}
subsys_initcall ( ip32_reboot_setup ) ;