2005-04-16 15:20:36 -07:00
/* $Id: power.c,v 1.10 2001/12/11 01:57:16 davem Exp $
* power . c : Power management driver .
*
* Copyright ( C ) 1999 David S . Miller ( davem @ redhat . com )
*/
2005-07-10 16:49:28 -07:00
# define __KERNEL_SYSCALLS__
2005-04-16 15:20:36 -07:00
# include <linux/config.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/signal.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <asm/system.h>
# include <asm/ebus.h>
2005-10-06 20:43:54 -07:00
# include <asm/isa.h>
2005-04-16 15:20:36 -07:00
# include <asm/auxio.h>
# include <linux/unistd.h>
/*
* sysctl - toggle power - off restriction for serial console
* systems in machine_power_off ( )
*/
int scons_pwroff = 1 ;
# ifdef CONFIG_PCI
static void __iomem * power_reg ;
static DECLARE_WAIT_QUEUE_HEAD ( powerd_wait ) ;
static int button_pressed ;
static irqreturn_t power_handler ( int irq , void * dev_id , struct pt_regs * regs )
{
if ( button_pressed = = 0 ) {
button_pressed = 1 ;
wake_up ( & powerd_wait ) ;
}
/* FIXME: Check registers for status... */
return IRQ_HANDLED ;
}
# endif /* CONFIG_PCI */
extern void machine_halt ( void ) ;
extern void machine_alt_power_off ( void ) ;
static void ( * poweroff_method ) ( void ) = machine_alt_power_off ;
void machine_power_off ( void )
{
if ( ! serial_console | | scons_pwroff ) {
# ifdef CONFIG_PCI
if ( power_reg ) {
/* Both register bits seem to have the
* same effect , so until I figure out
* what the difference is . . .
*/
writel ( AUXIO_PCIO_CPWR_OFF | AUXIO_PCIO_SPWR_OFF , power_reg ) ;
} else
# endif /* CONFIG_PCI */
if ( poweroff_method ! = NULL ) {
poweroff_method ( ) ;
/* not reached */
}
}
machine_halt ( ) ;
}
# ifdef CONFIG_PCI
static int powerd ( void * __unused )
{
static char * envp [ ] = { " HOME=/ " , " TERM=linux " , " PATH=/sbin:/usr/sbin:/bin:/usr/bin " , NULL } ;
char * argv [ ] = { " /sbin/shutdown " , " -h " , " now " , NULL } ;
DECLARE_WAITQUEUE ( wait , current ) ;
daemonize ( " powerd " ) ;
add_wait_queue ( & powerd_wait , & wait ) ;
again :
for ( ; ; ) {
set_task_state ( current , TASK_INTERRUPTIBLE ) ;
if ( button_pressed )
break ;
flush_signals ( current ) ;
schedule ( ) ;
}
__set_current_state ( TASK_RUNNING ) ;
remove_wait_queue ( & powerd_wait , & wait ) ;
/* Ok, down we go... */
button_pressed = 0 ;
if ( execve ( " /sbin/shutdown " , argv , envp ) < 0 ) {
printk ( " powerd: shutdown execution failed \n " ) ;
add_wait_queue ( & powerd_wait , & wait ) ;
goto again ;
}
return 0 ;
}
2005-10-06 20:43:54 -07:00
static int __init has_button_interrupt ( unsigned int irq , int prom_node )
2005-04-16 15:20:36 -07:00
{
2005-10-06 20:43:54 -07:00
if ( irq = = PCI_IRQ_NONE )
2005-04-16 15:20:36 -07:00
return 0 ;
2005-10-06 20:43:54 -07:00
if ( ! prom_node_has_property ( prom_node , " button " ) )
2005-04-16 15:20:36 -07:00
return 0 ;
return 1 ;
}
2005-10-06 20:43:54 -07:00
static int __init power_probe_ebus ( struct resource * * resp , unsigned int * irq_p , int * prom_node_p )
2005-04-16 15:20:36 -07:00
{
struct linux_ebus * ebus ;
struct linux_ebus_device * edev ;
2005-10-06 20:43:54 -07:00
for_each_ebus ( ebus ) {
for_each_ebusdev ( edev , ebus ) {
if ( ! strcmp ( edev - > prom_name , " power " ) ) {
* resp = & edev - > resource [ 0 ] ;
* irq_p = edev - > irqs [ 0 ] ;
* prom_node_p = edev - > prom_node ;
return 0 ;
}
}
}
return - ENODEV ;
}
static int __init power_probe_isa ( struct resource * * resp , unsigned int * irq_p , int * prom_node_p )
{
struct sparc_isa_bridge * isa_bus ;
struct sparc_isa_device * isa_dev ;
for_each_isa ( isa_bus ) {
for_each_isadev ( isa_dev , isa_bus ) {
if ( ! strcmp ( isa_dev - > prom_name , " power " ) ) {
* resp = & isa_dev - > resource ;
* irq_p = isa_dev - > irq ;
* prom_node_p = isa_dev - > prom_node ;
return 0 ;
}
}
}
return - ENODEV ;
}
void __init power_init ( void )
{
struct resource * res = NULL ;
unsigned int irq ;
int prom_node ;
2005-04-16 15:20:36 -07:00
static int invoked ;
if ( invoked )
return ;
invoked = 1 ;
2005-10-06 20:43:54 -07:00
if ( ! power_probe_ebus ( & res , & irq , & prom_node ) )
goto found ;
if ( ! power_probe_isa ( & res , & irq , & prom_node ) )
goto found ;
2005-04-16 15:20:36 -07:00
return ;
found :
2005-10-06 20:43:54 -07:00
power_reg = ioremap ( res - > start , 0x4 ) ;
2005-04-16 15:20:36 -07:00
printk ( " power: Control reg at %p ... " , power_reg ) ;
poweroff_method = machine_halt ; /* able to use the standard halt */
2005-10-06 20:43:54 -07:00
if ( has_button_interrupt ( irq , prom_node ) ) {
2005-04-16 15:20:36 -07:00
if ( kernel_thread ( powerd , NULL , CLONE_FS ) < 0 ) {
printk ( " Failed to start power daemon. \n " ) ;
return ;
}
printk ( " powerd running. \n " ) ;
2005-10-06 20:43:54 -07:00
if ( request_irq ( irq ,
2005-04-16 15:20:36 -07:00
power_handler , SA_SHIRQ , " power " , NULL ) < 0 )
printk ( " power: Error, cannot register IRQ handler. \n " ) ;
} else {
printk ( " not using powerd. \n " ) ;
}
}
# endif /* CONFIG_PCI */