2005-04-17 02:20:36 +04:00
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
# include <linux/suspend.h>
# include <linux/bcd.h>
# include <asm/uaccess.h>
# include <acpi/acpi_bus.h>
# include <acpi/acpi_drivers.h>
# ifdef CONFIG_X86
# include <linux/mc146818rtc.h>
# endif
# include "sleep.h"
# define _COMPONENT ACPI_SYSTEM_COMPONENT
2005-08-05 08:44:28 +04:00
ACPI_MODULE_NAME ( " sleep " )
2005-04-15 07:12:56 +04:00
# ifdef CONFIG_ACPI_SLEEP_PROC_SLEEP
2005-04-17 02:20:36 +04:00
static int acpi_system_sleep_seq_show ( struct seq_file * seq , void * offset )
{
2005-08-05 08:44:28 +04:00
int i ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_system_sleep_seq_show " ) ;
for ( i = 0 ; i < = ACPI_STATE_S5 ; i + + ) {
if ( sleep_states [ i ] ) {
2005-08-05 08:44:28 +04:00
seq_printf ( seq , " S%d " , i ) ;
2005-04-17 02:20:36 +04:00
}
}
seq_puts ( seq , " \n " ) ;
return 0 ;
}
static int acpi_system_sleep_open_fs ( struct inode * inode , struct file * file )
{
return single_open ( file , acpi_system_sleep_seq_show , PDE ( inode ) - > data ) ;
}
static ssize_t
2005-08-05 08:44:28 +04:00
acpi_system_write_sleep ( struct file * file ,
const char __user * buffer , size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
char str [ 12 ] ;
u32 state = 0 ;
int error = 0 ;
2005-04-17 02:20:36 +04:00
if ( count > sizeof ( str ) - 1 )
goto Done ;
2005-08-05 08:44:28 +04:00
memset ( str , 0 , sizeof ( str ) ) ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( str , buffer , count ) )
return - EFAULT ;
/* Check for S4 bios request */
2005-08-05 08:44:28 +04:00
if ( ! strcmp ( str , " 4b " ) ) {
2005-04-17 02:20:36 +04:00
error = acpi_suspend ( 4 ) ;
goto Done ;
}
state = simple_strtoul ( str , NULL , 0 ) ;
# ifdef CONFIG_SOFTWARE_SUSPEND
if ( state = = 4 ) {
error = software_suspend ( ) ;
goto Done ;
}
# endif
error = acpi_suspend ( state ) ;
2005-08-05 08:44:28 +04:00
Done :
2005-04-17 02:20:36 +04:00
return error ? error : count ;
}
2005-08-05 08:44:28 +04:00
# endif /* CONFIG_ACPI_SLEEP_PROC_SLEEP */
2005-04-17 02:20:36 +04:00
static int acpi_system_alarm_seq_show ( struct seq_file * seq , void * offset )
{
2005-08-05 08:44:28 +04:00
u32 sec , min , hr ;
u32 day , mo , yr ;
unsigned char rtc_control = 0 ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_system_alarm_seq_show " ) ;
spin_lock_irqsave ( & rtc_lock , flags ) ;
sec = CMOS_READ ( RTC_SECONDS_ALARM ) ;
min = CMOS_READ ( RTC_MINUTES_ALARM ) ;
hr = CMOS_READ ( RTC_HOURS_ALARM ) ;
rtc_control = CMOS_READ ( RTC_CONTROL ) ;
/* If we ever get an FACP with proper values... */
if ( acpi_gbl_FADT - > day_alrm )
/* ACPI spec: only low 6 its should be cared */
day = CMOS_READ ( acpi_gbl_FADT - > day_alrm ) & 0x3F ;
else
2005-08-05 08:44:28 +04:00
day = CMOS_READ ( RTC_DAY_OF_MONTH ) ;
2005-04-17 02:20:36 +04:00
if ( acpi_gbl_FADT - > mon_alrm )
mo = CMOS_READ ( acpi_gbl_FADT - > mon_alrm ) ;
else
mo = CMOS_READ ( RTC_MONTH ) ;
if ( acpi_gbl_FADT - > century )
2005-08-05 08:44:28 +04:00
yr = CMOS_READ ( acpi_gbl_FADT - > century ) * 100 +
CMOS_READ ( RTC_YEAR ) ;
2005-04-17 02:20:36 +04:00
else
yr = CMOS_READ ( RTC_YEAR ) ;
spin_unlock_irqrestore ( & rtc_lock , flags ) ;
if ( ! ( rtc_control & RTC_DM_BINARY ) | | RTC_ALWAYS_BCD ) {
BCD_TO_BIN ( sec ) ;
BCD_TO_BIN ( min ) ;
BCD_TO_BIN ( hr ) ;
BCD_TO_BIN ( day ) ;
BCD_TO_BIN ( mo ) ;
BCD_TO_BIN ( yr ) ;
}
2005-08-05 08:44:28 +04:00
/* we're trusting the FADT (see above) */
2005-04-17 02:20:36 +04:00
if ( ! acpi_gbl_FADT - > century )
2005-08-05 08:44:28 +04:00
/* If we're not trusting the FADT, we should at least make it
* right for _this_ century . . . ehm , what is _this_ century ?
*
* TBD :
* ASAP : find piece of code in the kernel , e . g . star tracker driver ,
* which we can trust to determine the century correctly . Atom
* watch driver would be nice , too . . .
*
* if that has not happened , change for first release in 2050 :
* if ( yr < 50 )
* yr + = 2100 ;
* else
* yr + = 2000 ; // current line of code
*
* if that has not happened either , please do on 2099 / 12 / 31 : 23 : 59 : 59
* s / 2000 / 2100
*
*/
2005-04-17 02:20:36 +04:00
yr + = 2000 ;
2005-08-05 08:44:28 +04:00
seq_printf ( seq , " %4.4u- " , yr ) ;
( mo > 12 ) ? seq_puts ( seq , " **- " ) : seq_printf ( seq , " %2.2u- " , mo ) ;
( day > 31 ) ? seq_puts ( seq , " ** " ) : seq_printf ( seq , " %2.2u " , day ) ;
( hr > 23 ) ? seq_puts ( seq , " **: " ) : seq_printf ( seq , " %2.2u: " , hr ) ;
( min > 59 ) ? seq_puts ( seq , " **: " ) : seq_printf ( seq , " %2.2u: " , min ) ;
2005-04-17 02:20:36 +04:00
( sec > 59 ) ? seq_puts ( seq , " ** \n " ) : seq_printf ( seq , " %2.2u \n " , sec ) ;
return 0 ;
}
static int acpi_system_alarm_open_fs ( struct inode * inode , struct file * file )
{
return single_open ( file , acpi_system_alarm_seq_show , PDE ( inode ) - > data ) ;
}
2005-08-05 08:44:28 +04:00
static int get_date_field ( char * * p , u32 * value )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
char * next = NULL ;
char * string_end = NULL ;
int result = - EINVAL ;
2005-04-17 02:20:36 +04:00
/*
* Try to find delimeter , only to insert null . The end of the
* string won ' t have one , but is still valid .
*/
next = strpbrk ( * p , " - : " ) ;
if ( next )
* next + + = ' \0 ' ;
* value = simple_strtoul ( * p , & string_end , 10 ) ;
/* Signal success if we got a good digit */
if ( string_end ! = * p )
result = 0 ;
if ( next )
* p = next ;
return result ;
}
static ssize_t
2005-08-05 08:44:28 +04:00
acpi_system_write_alarm ( struct file * file ,
const char __user * buffer , size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
char alarm_string [ 30 ] = { ' \0 ' } ;
char * p = alarm_string ;
u32 sec , min , hr , day , mo , yr ;
int adjust = 0 ;
unsigned char rtc_control = 0 ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_system_write_alarm " ) ;
if ( count > sizeof ( alarm_string ) - 1 )
return_VALUE ( - EINVAL ) ;
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( alarm_string , buffer , count ) )
return_VALUE ( - EFAULT ) ;
alarm_string [ count ] = ' \0 ' ;
/* check for time adjustment */
if ( alarm_string [ 0 ] = = ' + ' ) {
p + + ;
adjust = 1 ;
}
if ( ( result = get_date_field ( & p , & yr ) ) )
goto end ;
if ( ( result = get_date_field ( & p , & mo ) ) )
goto end ;
if ( ( result = get_date_field ( & p , & day ) ) )
goto end ;
if ( ( result = get_date_field ( & p , & hr ) ) )
goto end ;
if ( ( result = get_date_field ( & p , & min ) ) )
goto end ;
if ( ( result = get_date_field ( & p , & sec ) ) )
goto end ;
if ( sec > 59 ) {
min + = 1 ;
sec - = 60 ;
}
if ( min > 59 ) {
hr + = 1 ;
min - = 60 ;
}
if ( hr > 23 ) {
day + = 1 ;
hr - = 24 ;
}
if ( day > 31 ) {
mo + = 1 ;
day - = 31 ;
}
if ( mo > 12 ) {
yr + = 1 ;
mo - = 12 ;
}
spin_lock_irq ( & rtc_lock ) ;
rtc_control = CMOS_READ ( RTC_CONTROL ) ;
if ( ! ( rtc_control & RTC_DM_BINARY ) | | RTC_ALWAYS_BCD ) {
BIN_TO_BCD ( yr ) ;
BIN_TO_BCD ( mo ) ;
BIN_TO_BCD ( day ) ;
BIN_TO_BCD ( hr ) ;
BIN_TO_BCD ( min ) ;
BIN_TO_BCD ( sec ) ;
}
if ( adjust ) {
2005-08-05 08:44:28 +04:00
yr + = CMOS_READ ( RTC_YEAR ) ;
mo + = CMOS_READ ( RTC_MONTH ) ;
2005-04-17 02:20:36 +04:00
day + = CMOS_READ ( RTC_DAY_OF_MONTH ) ;
2005-08-05 08:44:28 +04:00
hr + = CMOS_READ ( RTC_HOURS ) ;
2005-04-17 02:20:36 +04:00
min + = CMOS_READ ( RTC_MINUTES ) ;
sec + = CMOS_READ ( RTC_SECONDS ) ;
}
spin_unlock_irq ( & rtc_lock ) ;
if ( ! ( rtc_control & RTC_DM_BINARY ) | | RTC_ALWAYS_BCD ) {
BCD_TO_BIN ( yr ) ;
BCD_TO_BIN ( mo ) ;
BCD_TO_BIN ( day ) ;
BCD_TO_BIN ( hr ) ;
BCD_TO_BIN ( min ) ;
BCD_TO_BIN ( sec ) ;
}
if ( sec > 59 ) {
min + + ;
sec - = 60 ;
}
if ( min > 59 ) {
hr + + ;
min - = 60 ;
}
if ( hr > 23 ) {
day + + ;
hr - = 24 ;
}
if ( day > 31 ) {
mo + + ;
day - = 31 ;
}
if ( mo > 12 ) {
yr + + ;
mo - = 12 ;
}
if ( ! ( rtc_control & RTC_DM_BINARY ) | | RTC_ALWAYS_BCD ) {
BIN_TO_BCD ( yr ) ;
BIN_TO_BCD ( mo ) ;
BIN_TO_BCD ( day ) ;
BIN_TO_BCD ( hr ) ;
BIN_TO_BCD ( min ) ;
BIN_TO_BCD ( sec ) ;
}
spin_lock_irq ( & rtc_lock ) ;
/*
* Disable alarm interrupt before setting alarm timer or else
* when ACPI_EVENT_RTC is enabled , a spurious ACPI interrupt occurs
*/
rtc_control & = ~ RTC_AIE ;
CMOS_WRITE ( rtc_control , RTC_CONTROL ) ;
CMOS_READ ( RTC_INTR_FLAGS ) ;
/* write the fields the rtc knows about */
CMOS_WRITE ( hr , RTC_HOURS_ALARM ) ;
CMOS_WRITE ( min , RTC_MINUTES_ALARM ) ;
CMOS_WRITE ( sec , RTC_SECONDS_ALARM ) ;
/*
* If the system supports an enhanced alarm it will have non - zero
* offsets into the CMOS RAM here - - which for some reason are pointing
* to the RTC area of memory .
*/
if ( acpi_gbl_FADT - > day_alrm )
CMOS_WRITE ( day , acpi_gbl_FADT - > day_alrm ) ;
if ( acpi_gbl_FADT - > mon_alrm )
CMOS_WRITE ( mo , acpi_gbl_FADT - > mon_alrm ) ;
if ( acpi_gbl_FADT - > century )
2005-08-05 08:44:28 +04:00
CMOS_WRITE ( yr / 100 , acpi_gbl_FADT - > century ) ;
2005-04-17 02:20:36 +04:00
/* enable the rtc alarm interrupt */
rtc_control | = RTC_AIE ;
CMOS_WRITE ( rtc_control , RTC_CONTROL ) ;
CMOS_READ ( RTC_INTR_FLAGS ) ;
spin_unlock_irq ( & rtc_lock ) ;
acpi_clear_event ( ACPI_EVENT_RTC ) ;
acpi_enable_event ( ACPI_EVENT_RTC , 0 ) ;
* ppos + = count ;
result = 0 ;
2005-08-05 08:44:28 +04:00
end :
2005-04-17 02:20:36 +04:00
return_VALUE ( result ? result : count ) ;
}
2005-08-05 08:44:28 +04:00
extern struct list_head acpi_wakeup_device_list ;
2005-04-17 02:20:36 +04:00
extern spinlock_t acpi_device_lock ;
static int
acpi_system_wakeup_device_seq_show ( struct seq_file * seq , void * offset )
{
2005-08-05 08:44:28 +04:00
struct list_head * node , * next ;
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " Device Sleep state Status \n " ) ;
spin_lock ( & acpi_device_lock ) ;
list_for_each_safe ( node , next , & acpi_wakeup_device_list ) {
2005-08-05 08:44:28 +04:00
struct acpi_device * dev =
container_of ( node , struct acpi_device , wakeup_list ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev - > wakeup . flags . valid )
continue ;
spin_unlock ( & acpi_device_lock ) ;
2005-08-05 08:37:45 +04:00
seq_printf ( seq , " %4s %4d %s%8s \n " ,
2005-08-05 08:44:28 +04:00
dev - > pnp . bus_id ,
( u32 ) dev - > wakeup . sleep_state ,
2005-08-05 08:37:45 +04:00
dev - > wakeup . flags . run_wake ? " * " : " " ,
dev - > wakeup . state . enabled ? " enabled " : " disabled " ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & acpi_device_lock ) ;
}
spin_unlock ( & acpi_device_lock ) ;
return 0 ;
}
static ssize_t
2005-08-05 08:44:28 +04:00
acpi_system_write_wakeup_device ( struct file * file ,
const char __user * buffer ,
size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct list_head * node , * next ;
char strbuf [ 5 ] ;
char str [ 5 ] = " " ;
int len = count ;
2005-04-17 02:20:36 +04:00
struct acpi_device * found_dev = NULL ;
2005-08-05 08:44:28 +04:00
if ( len > 4 )
len = 4 ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( strbuf , buffer , len ) )
return - EFAULT ;
strbuf [ len ] = ' \0 ' ;
sscanf ( strbuf , " %s " , str ) ;
spin_lock ( & acpi_device_lock ) ;
list_for_each_safe ( node , next , & acpi_wakeup_device_list ) {
2005-08-05 08:44:28 +04:00
struct acpi_device * dev =
container_of ( node , struct acpi_device , wakeup_list ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev - > wakeup . flags . valid )
continue ;
if ( ! strncmp ( dev - > pnp . bus_id , str , 4 ) ) {
2005-08-05 08:44:28 +04:00
dev - > wakeup . state . enabled =
dev - > wakeup . state . enabled ? 0 : 1 ;
2005-04-17 02:20:36 +04:00
found_dev = dev ;
break ;
}
}
if ( found_dev ) {
list_for_each_safe ( node , next , & acpi_wakeup_device_list ) {
2005-08-05 08:44:28 +04:00
struct acpi_device * dev = container_of ( node ,
struct
acpi_device ,
wakeup_list ) ;
2005-04-17 02:20:36 +04:00
if ( ( dev ! = found_dev ) & &
2005-08-05 08:44:28 +04:00
( dev - > wakeup . gpe_number = =
found_dev - > wakeup . gpe_number )
& & ( dev - > wakeup . gpe_device = =
found_dev - > wakeup . gpe_device ) ) {
printk ( KERN_WARNING
" ACPI: '%s' and '%s' have the same GPE, "
" can't disable/enable one seperately \n " ,
dev - > pnp . bus_id , found_dev - > pnp . bus_id ) ;
dev - > wakeup . state . enabled =
found_dev - > wakeup . state . enabled ;
2005-04-17 02:20:36 +04:00
}
}
}
spin_unlock ( & acpi_device_lock ) ;
return count ;
}
static int
acpi_system_wakeup_device_open_fs ( struct inode * inode , struct file * file )
{
2005-08-05 08:44:28 +04:00
return single_open ( file , acpi_system_wakeup_device_seq_show ,
PDE ( inode ) - > data ) ;
2005-04-17 02:20:36 +04:00
}
static struct file_operations acpi_system_wakeup_device_fops = {
2005-08-05 08:44:28 +04:00
. open = acpi_system_wakeup_device_open_fs ,
. read = seq_read ,
. write = acpi_system_write_wakeup_device ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
2005-04-15 07:12:56 +04:00
# ifdef CONFIG_ACPI_SLEEP_PROC_SLEEP
2005-04-17 02:20:36 +04:00
static struct file_operations acpi_system_sleep_fops = {
2005-08-05 08:44:28 +04:00
. open = acpi_system_sleep_open_fs ,
. read = seq_read ,
. write = acpi_system_write_sleep ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
2005-08-05 08:44:28 +04:00
# endif /* CONFIG_ACPI_SLEEP_PROC_SLEEP */
2005-04-17 02:20:36 +04:00
static struct file_operations acpi_system_alarm_fops = {
2005-08-05 08:44:28 +04:00
. open = acpi_system_alarm_open_fs ,
. read = seq_read ,
. write = acpi_system_write_alarm ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
2005-08-05 08:44:28 +04:00
static u32 rtc_handler ( void * context )
2005-04-17 02:20:36 +04:00
{
acpi_clear_event ( ACPI_EVENT_RTC ) ;
acpi_disable_event ( ACPI_EVENT_RTC , 0 ) ;
return ACPI_INTERRUPT_HANDLED ;
}
static int acpi_sleep_proc_init ( void )
{
2005-08-05 08:37:45 +04:00
struct proc_dir_entry * entry = NULL ;
2005-04-17 02:20:36 +04:00
if ( acpi_disabled )
return 0 ;
2005-08-05 08:44:28 +04:00
2005-04-15 07:12:56 +04:00
# ifdef CONFIG_ACPI_SLEEP_PROC_SLEEP
2005-08-05 08:37:45 +04:00
/* 'sleep' [R/W] */
2005-08-05 08:44:28 +04:00
entry =
create_proc_entry ( " sleep " , S_IFREG | S_IRUGO | S_IWUSR ,
acpi_root_dir ) ;
2005-04-17 02:20:36 +04:00
if ( entry )
entry - > proc_fops = & acpi_system_sleep_fops ;
2005-04-15 07:12:56 +04:00
# endif
2005-04-17 02:20:36 +04:00
/* 'alarm' [R/W] */
2005-08-05 08:44:28 +04:00
entry =
create_proc_entry ( " alarm " , S_IFREG | S_IRUGO | S_IWUSR ,
acpi_root_dir ) ;
2005-04-17 02:20:36 +04:00
if ( entry )
entry - > proc_fops = & acpi_system_alarm_fops ;
2005-08-05 08:37:45 +04:00
/* 'wakeup device' [R/W] */
2005-08-05 08:44:28 +04:00
entry =
create_proc_entry ( " wakeup " , S_IFREG | S_IRUGO | S_IWUSR ,
acpi_root_dir ) ;
2005-04-17 02:20:36 +04:00
if ( entry )
entry - > proc_fops = & acpi_system_wakeup_device_fops ;
acpi_install_fixed_event_handler ( ACPI_EVENT_RTC , rtc_handler , NULL ) ;
return 0 ;
}
late_initcall ( acpi_sleep_proc_init ) ;