2005-04-17 02:20:36 +04: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 )
*/
# 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>
2006-01-10 00:59:12 +03:00
# include <linux/pm.h>
2006-10-02 13:18:26 +04:00
# include <linux/syscalls.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
# include <asm/auxio.h>
2006-06-30 02:22:46 +04:00
# include <asm/prom.h>
# include <asm/of_device.h>
# include <asm/io.h>
2005-04-17 02:20:36 +04:00
# include <linux/unistd.h>
/*
* sysctl - toggle power - off restriction for serial console
* systems in machine_power_off ( )
*/
int scons_pwroff = 1 ;
# ifdef CONFIG_PCI
2006-06-30 02:22:46 +04:00
# include <linux/pci.h>
2005-04-17 02:20:36 +04:00
static void __iomem * power_reg ;
static DECLARE_WAIT_QUEUE_HEAD ( powerd_wait ) ;
static int button_pressed ;
2006-10-08 16:23:28 +04:00
static irqreturn_t power_handler ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
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 ( ) ;
}
2006-01-10 00:59:12 +03:00
void ( * pm_power_off ) ( void ) = machine_power_off ;
EXPORT_SYMBOL ( pm_power_off ) ;
2005-04-17 02:20:36 +04:00
# 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 ;
2006-10-02 13:18:26 +04:00
if ( kernel_execve ( " /sbin/shutdown " , argv , envp ) < 0 ) {
2005-04-17 02:20:36 +04:00
printk ( " powerd: shutdown execution failed \n " ) ;
add_wait_queue ( & powerd_wait , & wait ) ;
goto again ;
}
return 0 ;
}
2006-06-23 06:12:03 +04:00
static int __init has_button_interrupt ( unsigned int irq , struct device_node * dp )
2005-04-17 02:20:36 +04:00
{
2005-10-07 07:43:54 +04:00
if ( irq = = PCI_IRQ_NONE )
2005-04-17 02:20:36 +04:00
return 0 ;
2006-06-23 06:12:03 +04:00
if ( ! of_find_property ( dp , " button " , NULL ) )
2005-04-17 02:20:36 +04:00
return 0 ;
return 1 ;
}
2006-06-30 02:22:46 +04:00
static int __devinit power_probe ( struct of_device * op , const struct of_device_id * match )
2005-04-17 02:20:36 +04:00
{
2006-06-30 02:22:46 +04:00
struct resource * res = & op - > resource [ 0 ] ;
unsigned int irq = op - > irqs [ 0 ] ;
2006-06-23 12:44:10 +04:00
2006-06-30 02:22:46 +04:00
power_reg = of_ioremap ( res , 0 , 0x4 , " power " ) ;
printk ( " %s: Control reg at %lx ... " ,
op - > node - > name , res - > start ) ;
2006-06-23 12:44:10 +04:00
2005-04-17 02:20:36 +04:00
poweroff_method = machine_halt ; /* able to use the standard halt */
2006-06-23 12:44:10 +04:00
2006-06-30 02:22:46 +04:00
if ( has_button_interrupt ( irq , op - > node ) ) {
2005-04-17 02:20:36 +04:00
if ( kernel_thread ( powerd , NULL , CLONE_FS ) < 0 ) {
printk ( " Failed to start power daemon. \n " ) ;
2006-06-30 02:22:46 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
printk ( " powerd running. \n " ) ;
2005-10-07 07:43:54 +04:00
if ( request_irq ( irq ,
2006-06-30 01:39:11 +04:00
power_handler , 0 , " power " , NULL ) < 0 )
2005-04-17 02:20:36 +04:00
printk ( " power: Error, cannot register IRQ handler. \n " ) ;
} else {
printk ( " not using powerd. \n " ) ;
}
2006-06-30 02:22:46 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-06-23 12:44:10 +04:00
static struct of_device_id power_match [ ] = {
{
. name = " power " ,
} ,
{ } ,
} ;
2006-06-30 02:22:46 +04:00
static struct of_platform_driver power_driver = {
2006-06-23 12:44:10 +04:00
. name = " power " ,
. match_table = power_match ,
2006-06-30 02:22:46 +04:00
. probe = power_probe ,
2006-06-23 12:44:10 +04:00
} ;
void __init power_init ( void )
{
2006-06-30 02:22:46 +04:00
of_register_driver ( & power_driver , & of_bus_type ) ;
2006-06-23 12:44:10 +04:00
return ;
}
2005-04-17 02:20:36 +04:00
# endif /* CONFIG_PCI */