2011-06-25 17:34:12 +01:00
/*
* Support for OLPC XO - 1 System Control Interrupts ( SCI )
*
* Copyright ( C ) 2010 One Laptop per Child
* Copyright ( C ) 2006 Red Hat , Inc .
* Copyright ( C ) 2006 Advanced Micro Devices , Inc .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/cs5535.h>
2011-06-25 17:34:14 +01:00
# include <linux/device.h>
# include <linux/gpio.h>
2011-06-25 17:34:12 +01:00
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <linux/pm.h>
2012-04-12 18:18:24 +01:00
# include <linux/pm_wakeup.h>
2011-06-25 17:34:12 +01:00
# include <linux/mfd/core.h>
2011-06-25 17:34:16 +01:00
# include <linux/power_supply.h>
2011-06-25 17:34:12 +01:00
# include <linux/suspend.h>
2011-06-25 17:34:14 +01:00
# include <linux/workqueue.h>
2012-07-11 01:16:29 -07:00
# include <linux/olpc-ec.h>
2011-06-25 17:34:12 +01:00
# include <asm/io.h>
# include <asm/msr.h>
# include <asm/olpc.h>
# define DRV_NAME "olpc-xo1-sci"
# define PFX DRV_NAME ": "
static unsigned long acpi_base ;
static struct input_dev * power_button_idev ;
2011-06-25 17:34:14 +01:00
static struct input_dev * ebook_switch_idev ;
2011-06-25 17:34:15 +01:00
static struct input_dev * lid_switch_idev ;
2011-06-25 17:34:14 +01:00
2011-06-25 17:34:12 +01:00
static int sci_irq ;
2011-06-25 17:34:15 +01:00
static bool lid_open ;
static bool lid_inverted ;
static int lid_wake_mode ;
enum lid_wake_modes {
LID_WAKE_ALWAYS ,
LID_WAKE_OPEN ,
LID_WAKE_CLOSE ,
} ;
static const char * const lid_wake_mode_names [ ] = {
[ LID_WAKE_ALWAYS ] = " always " ,
[ LID_WAKE_OPEN ] = " open " ,
[ LID_WAKE_CLOSE ] = " close " ,
} ;
2011-06-25 17:34:16 +01:00
static void battery_status_changed ( void )
{
struct power_supply * psy = power_supply_get_by_name ( " olpc-battery " ) ;
if ( psy ) {
power_supply_changed ( psy ) ;
2015-03-12 08:44:15 +01:00
power_supply_put ( psy ) ;
2011-06-25 17:34:16 +01:00
}
}
static void ac_status_changed ( void )
{
struct power_supply * psy = power_supply_get_by_name ( " olpc-ac " ) ;
if ( psy ) {
power_supply_changed ( psy ) ;
2015-03-12 08:44:15 +01:00
power_supply_put ( psy ) ;
2011-06-25 17:34:16 +01:00
}
}
2011-06-25 17:34:14 +01:00
/* Report current ebook switch state through input layer */
static void send_ebook_state ( void )
{
unsigned char state ;
if ( olpc_ec_cmd ( EC_READ_EB_MODE , NULL , 0 , & state , 1 ) ) {
pr_err ( PFX " failed to get ebook state \n " ) ;
return ;
}
2012-04-12 18:18:24 +01:00
if ( ! ! test_bit ( SW_TABLET_MODE , ebook_switch_idev - > sw ) = = state )
return ; /* Nothing new to report. */
2011-06-25 17:34:14 +01:00
input_report_switch ( ebook_switch_idev , SW_TABLET_MODE , state ) ;
input_sync ( ebook_switch_idev ) ;
2012-04-12 18:18:24 +01:00
pm_wakeup_event ( & ebook_switch_idev - > dev , 0 ) ;
2011-06-25 17:34:14 +01:00
}
2011-06-25 17:34:15 +01:00
static void flip_lid_inverter ( void )
{
/* gpio is high; invert so we'll get l->h event interrupt */
if ( lid_inverted )
cs5535_gpio_clear ( OLPC_GPIO_LID , GPIO_INPUT_INVERT ) ;
else
cs5535_gpio_set ( OLPC_GPIO_LID , GPIO_INPUT_INVERT ) ;
lid_inverted = ! lid_inverted ;
}
static void detect_lid_state ( void )
{
/*
* the edge detector hookup on the gpio inputs on the geode is
* odd , to say the least . See http : //dev.laptop.org/ticket/5703
* for details , but in a nutshell : we don ' t use the edge
2018-12-03 10:47:34 +01:00
* detectors . instead , we make use of an anomaly : with the both
2011-06-25 17:34:15 +01:00
* edge detectors turned off , we still get an edge event on a
* positive edge transition . to take advantage of this , we use the
* front - end inverter to ensure that that ' s the edge we ' re always
* going to see next .
*/
int state ;
state = cs5535_gpio_isset ( OLPC_GPIO_LID , GPIO_READ_BACK ) ;
lid_open = ! state ^ ! lid_inverted ; /* x ^^ y */
if ( ! state )
return ;
flip_lid_inverter ( ) ;
}
/* Report current lid switch state through input layer */
static void send_lid_state ( void )
{
2012-04-12 18:18:24 +01:00
if ( ! ! test_bit ( SW_LID , lid_switch_idev - > sw ) = = ! lid_open )
return ; /* Nothing new to report. */
2011-06-25 17:34:15 +01:00
input_report_switch ( lid_switch_idev , SW_LID , ! lid_open ) ;
input_sync ( lid_switch_idev ) ;
2012-04-12 18:18:24 +01:00
pm_wakeup_event ( & lid_switch_idev - > dev , 0 ) ;
2011-06-25 17:34:15 +01:00
}
static ssize_t lid_wake_mode_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
const char * mode = lid_wake_mode_names [ lid_wake_mode ] ;
return sprintf ( buf , " %s \n " , mode ) ;
}
static ssize_t lid_wake_mode_set ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( lid_wake_mode_names ) ; i + + ) {
const char * mode = lid_wake_mode_names [ i ] ;
if ( strlen ( mode ) ! = count | | strncasecmp ( mode , buf , count ) )
continue ;
lid_wake_mode = i ;
return count ;
}
return - EINVAL ;
}
static DEVICE_ATTR ( lid_wake_mode , S_IWUSR | S_IRUGO , lid_wake_mode_show ,
lid_wake_mode_set ) ;
2011-06-25 17:34:14 +01:00
/*
* Process all items in the EC ' s SCI queue .
*
* This is handled in a workqueue because olpc_ec_cmd can be slow ( and
* can even timeout ) .
*
* If propagate_events is false , the queue is drained without events being
* generated for the interrupts .
*/
static void process_sci_queue ( bool propagate_events )
{
int r ;
u16 data ;
do {
r = olpc_ec_sci_query ( & data ) ;
if ( r | | ! data )
break ;
pr_debug ( PFX " SCI 0x%x received \n " , data ) ;
2011-06-25 17:34:16 +01:00
switch ( data ) {
case EC_SCI_SRC_BATERR :
case EC_SCI_SRC_BATSOC :
case EC_SCI_SRC_BATTERY :
case EC_SCI_SRC_BATCRIT :
battery_status_changed ( ) ;
break ;
case EC_SCI_SRC_ACPWR :
ac_status_changed ( ) ;
break ;
}
2011-06-25 17:34:14 +01:00
if ( data = = EC_SCI_SRC_EBOOK & & propagate_events )
send_ebook_state ( ) ;
} while ( data ) ;
if ( r )
pr_err ( PFX " Failed to clear SCI queue " ) ;
}
static void process_sci_queue_work ( struct work_struct * work )
{
process_sci_queue ( true ) ;
}
static DECLARE_WORK ( sci_work , process_sci_queue_work ) ;
2011-06-25 17:34:12 +01:00
static irqreturn_t xo1_sci_intr ( int irq , void * dev_id )
{
struct platform_device * pdev = dev_id ;
u32 sts ;
u32 gpe ;
sts = inl ( acpi_base + CS5536_PM1_STS ) ;
outl ( sts | 0xffff , acpi_base + CS5536_PM1_STS ) ;
gpe = inl ( acpi_base + CS5536_PM_GPE0_STS ) ;
outl ( 0xffffffff , acpi_base + CS5536_PM_GPE0_STS ) ;
dev_dbg ( & pdev - > dev , " sts %x gpe %x \n " , sts , gpe ) ;
2012-04-12 18:18:24 +01:00
if ( sts & CS5536_PWRBTN_FLAG ) {
if ( ! ( sts & CS5536_WAK_FLAG ) ) {
/* Only report power button input when it was pressed
* during regular operation ( as opposed to when it
* was used to wake the system ) . */
input_report_key ( power_button_idev , KEY_POWER , 1 ) ;
input_sync ( power_button_idev ) ;
input_report_key ( power_button_idev , KEY_POWER , 0 ) ;
input_sync ( power_button_idev ) ;
}
/* Report the wakeup event in all cases. */
pm_wakeup_event ( & power_button_idev - > dev , 0 ) ;
2011-06-25 17:34:12 +01:00
}
2012-04-18 23:34:02 +01:00
if ( ( sts & ( CS5536_RTC_FLAG | CS5536_WAK_FLAG ) ) = =
( CS5536_RTC_FLAG | CS5536_WAK_FLAG ) ) {
/* When the system is woken by the RTC alarm, report the
* event on the rtc device . */
struct device * rtc = bus_find_device_by_name (
& platform_bus_type , NULL , " rtc_cmos " ) ;
if ( rtc ) {
pm_wakeup_event ( rtc , 0 ) ;
put_device ( rtc ) ;
}
}
2011-06-25 17:34:14 +01:00
if ( gpe & CS5536_GPIOM7_PME_FLAG ) { /* EC GPIO */
cs5535_gpio_set ( OLPC_GPIO_ECSCI , GPIO_NEGATIVE_EDGE_STS ) ;
schedule_work ( & sci_work ) ;
}
2011-06-25 17:34:15 +01:00
cs5535_gpio_set ( OLPC_GPIO_LID , GPIO_NEGATIVE_EDGE_STS ) ;
cs5535_gpio_set ( OLPC_GPIO_LID , GPIO_POSITIVE_EDGE_STS ) ;
detect_lid_state ( ) ;
send_lid_state ( ) ;
2011-06-25 17:34:12 +01:00
return IRQ_HANDLED ;
}
static int xo1_sci_suspend ( struct platform_device * pdev , pm_message_t state )
{
if ( device_may_wakeup ( & power_button_idev - > dev ) )
olpc_xo1_pm_wakeup_set ( CS5536_PM_PWRBTN ) ;
else
olpc_xo1_pm_wakeup_clear ( CS5536_PM_PWRBTN ) ;
2011-06-25 17:34:14 +01:00
if ( device_may_wakeup ( & ebook_switch_idev - > dev ) )
olpc_ec_wakeup_set ( EC_SCI_SRC_EBOOK ) ;
else
olpc_ec_wakeup_clear ( EC_SCI_SRC_EBOOK ) ;
2011-06-25 17:34:15 +01:00
if ( ! device_may_wakeup ( & lid_switch_idev - > dev ) ) {
cs5535_gpio_clear ( OLPC_GPIO_LID , GPIO_EVENTS_ENABLE ) ;
} else if ( ( lid_open & & lid_wake_mode = = LID_WAKE_OPEN ) | |
( ! lid_open & & lid_wake_mode = = LID_WAKE_CLOSE ) ) {
flip_lid_inverter ( ) ;
/* we may have just caused an event */
cs5535_gpio_set ( OLPC_GPIO_LID , GPIO_NEGATIVE_EDGE_STS ) ;
cs5535_gpio_set ( OLPC_GPIO_LID , GPIO_POSITIVE_EDGE_STS ) ;
cs5535_gpio_set ( OLPC_GPIO_LID , GPIO_EVENTS_ENABLE ) ;
}
2011-06-25 17:34:14 +01:00
return 0 ;
}
static int xo1_sci_resume ( struct platform_device * pdev )
{
2011-06-25 17:34:15 +01:00
/*
* We don ' t know what may have happened while we were asleep .
* Reestablish our lid setup so we ' re sure to catch all transitions .
*/
detect_lid_state ( ) ;
send_lid_state ( ) ;
cs5535_gpio_set ( OLPC_GPIO_LID , GPIO_EVENTS_ENABLE ) ;
2011-06-25 17:34:14 +01:00
/* Enable all EC events */
olpc_ec_mask_write ( EC_SCI_SRC_ALL ) ;
2011-06-25 17:34:16 +01:00
/* Power/battery status might have changed too */
battery_status_changed ( ) ;
ac_status_changed ( ) ;
2011-06-25 17:34:12 +01:00
return 0 ;
}
2012-12-21 14:02:53 -08:00
static int setup_sci_interrupt ( struct platform_device * pdev )
2011-06-25 17:34:12 +01:00
{
u32 lo , hi ;
u32 sts ;
int r ;
rdmsr ( 0x51400020 , lo , hi ) ;
sci_irq = ( lo > > 20 ) & 15 ;
if ( sci_irq ) {
dev_info ( & pdev - > dev , " SCI is mapped to IRQ %d \n " , sci_irq ) ;
} else {
/* Zero means masked */
dev_info ( & pdev - > dev , " SCI unmapped. Mapping to IRQ 3 \n " ) ;
sci_irq = 3 ;
lo | = 0x00300000 ;
wrmsrl ( 0x51400020 , lo ) ;
}
/* Select level triggered in PIC */
if ( sci_irq < 8 ) {
lo = inb ( CS5536_PIC_INT_SEL1 ) ;
lo | = 1 < < sci_irq ;
outb ( lo , CS5536_PIC_INT_SEL1 ) ;
} else {
lo = inb ( CS5536_PIC_INT_SEL2 ) ;
lo | = 1 < < ( sci_irq - 8 ) ;
outb ( lo , CS5536_PIC_INT_SEL2 ) ;
}
2012-04-18 23:34:02 +01:00
/* Enable interesting SCI events, and clear pending interrupts */
2011-06-25 17:34:12 +01:00
sts = inl ( acpi_base + CS5536_PM1_STS ) ;
2012-04-18 23:34:02 +01:00
outl ( ( ( CS5536_PM_PWRBTN | CS5536_PM_RTC ) < < 16 ) | 0xffff ,
acpi_base + CS5536_PM1_STS ) ;
2011-06-25 17:34:12 +01:00
r = request_irq ( sci_irq , xo1_sci_intr , 0 , DRV_NAME , pdev ) ;
if ( r )
dev_err ( & pdev - > dev , " can't request interrupt \n " ) ;
return r ;
}
2012-12-21 14:02:53 -08:00
static int setup_ec_sci ( void )
2011-06-25 17:34:14 +01:00
{
int r ;
r = gpio_request ( OLPC_GPIO_ECSCI , " OLPC-ECSCI " ) ;
if ( r )
return r ;
gpio_direction_input ( OLPC_GPIO_ECSCI ) ;
/* Clear pending EC SCI events */
cs5535_gpio_set ( OLPC_GPIO_ECSCI , GPIO_NEGATIVE_EDGE_STS ) ;
cs5535_gpio_set ( OLPC_GPIO_ECSCI , GPIO_POSITIVE_EDGE_STS ) ;
/*
* Enable EC SCI events , and map them to both a PME and the SCI
* interrupt .
*
* Ordinarily , in addition to functioning as GPIOs , Geode GPIOs can
* be mapped to regular interrupts * or * Geode - specific Power
* Management Events ( PMEs ) - events that bring the system out of
* suspend . In this case , we want both of those things - the system
* wakeup , * and * the ability to get an interrupt when an event occurs .
*
* To achieve this , we map the GPIO to a PME , and then we use one
* of the many generic knobs on the CS5535 PIC to additionally map the
* PME to the regular SCI interrupt line .
*/
cs5535_gpio_set ( OLPC_GPIO_ECSCI , GPIO_EVENTS_ENABLE ) ;
/* Set the SCI to cause a PME event on group 7 */
cs5535_gpio_setup_event ( OLPC_GPIO_ECSCI , 7 , 1 ) ;
/* And have group 7 also fire the SCI interrupt */
cs5535_pic_unreqz_select_high ( 7 , sci_irq ) ;
return 0 ;
}
static void free_ec_sci ( void )
{
gpio_free ( OLPC_GPIO_ECSCI ) ;
}
2012-12-21 14:02:53 -08:00
static int setup_lid_events ( void )
2011-06-25 17:34:15 +01:00
{
int r ;
r = gpio_request ( OLPC_GPIO_LID , " OLPC-LID " ) ;
if ( r )
return r ;
gpio_direction_input ( OLPC_GPIO_LID ) ;
cs5535_gpio_clear ( OLPC_GPIO_LID , GPIO_INPUT_INVERT ) ;
lid_inverted = 0 ;
/* Clear edge detection and event enable for now */
cs5535_gpio_clear ( OLPC_GPIO_LID , GPIO_EVENTS_ENABLE ) ;
cs5535_gpio_clear ( OLPC_GPIO_LID , GPIO_NEGATIVE_EDGE_EN ) ;
cs5535_gpio_clear ( OLPC_GPIO_LID , GPIO_POSITIVE_EDGE_EN ) ;
cs5535_gpio_set ( OLPC_GPIO_LID , GPIO_NEGATIVE_EDGE_STS ) ;
cs5535_gpio_set ( OLPC_GPIO_LID , GPIO_POSITIVE_EDGE_STS ) ;
/* Set the LID to cause an PME event on group 6 */
cs5535_gpio_setup_event ( OLPC_GPIO_LID , 6 , 1 ) ;
/* Set PME group 6 to fire the SCI interrupt */
cs5535_gpio_set_irq ( 6 , sci_irq ) ;
/* Enable the event */
cs5535_gpio_set ( OLPC_GPIO_LID , GPIO_EVENTS_ENABLE ) ;
return 0 ;
}
static void free_lid_events ( void )
{
gpio_free ( OLPC_GPIO_LID ) ;
}
2012-12-21 14:02:53 -08:00
static int setup_power_button ( struct platform_device * pdev )
2011-06-25 17:34:12 +01:00
{
int r ;
power_button_idev = input_allocate_device ( ) ;
if ( ! power_button_idev )
return - ENOMEM ;
power_button_idev - > name = " Power Button " ;
power_button_idev - > phys = DRV_NAME " /input0 " ;
set_bit ( EV_KEY , power_button_idev - > evbit ) ;
set_bit ( KEY_POWER , power_button_idev - > keybit ) ;
power_button_idev - > dev . parent = & pdev - > dev ;
device_init_wakeup ( & power_button_idev - > dev , 1 ) ;
r = input_register_device ( power_button_idev ) ;
if ( r ) {
dev_err ( & pdev - > dev , " failed to register power button: %d \n " , r ) ;
input_free_device ( power_button_idev ) ;
}
return r ;
}
static void free_power_button ( void )
{
input_unregister_device ( power_button_idev ) ;
}
2012-12-21 14:02:53 -08:00
static int setup_ebook_switch ( struct platform_device * pdev )
2011-06-25 17:34:14 +01:00
{
int r ;
ebook_switch_idev = input_allocate_device ( ) ;
if ( ! ebook_switch_idev )
return - ENOMEM ;
ebook_switch_idev - > name = " EBook Switch " ;
ebook_switch_idev - > phys = DRV_NAME " /input1 " ;
set_bit ( EV_SW , ebook_switch_idev - > evbit ) ;
set_bit ( SW_TABLET_MODE , ebook_switch_idev - > swbit ) ;
ebook_switch_idev - > dev . parent = & pdev - > dev ;
device_set_wakeup_capable ( & ebook_switch_idev - > dev , true ) ;
r = input_register_device ( ebook_switch_idev ) ;
if ( r ) {
dev_err ( & pdev - > dev , " failed to register ebook switch: %d \n " , r ) ;
input_free_device ( ebook_switch_idev ) ;
}
return r ;
}
static void free_ebook_switch ( void )
{
input_unregister_device ( ebook_switch_idev ) ;
}
2012-12-21 14:02:53 -08:00
static int setup_lid_switch ( struct platform_device * pdev )
2011-06-25 17:34:15 +01:00
{
int r ;
lid_switch_idev = input_allocate_device ( ) ;
if ( ! lid_switch_idev )
return - ENOMEM ;
lid_switch_idev - > name = " Lid Switch " ;
lid_switch_idev - > phys = DRV_NAME " /input2 " ;
set_bit ( EV_SW , lid_switch_idev - > evbit ) ;
set_bit ( SW_LID , lid_switch_idev - > swbit ) ;
lid_switch_idev - > dev . parent = & pdev - > dev ;
device_set_wakeup_capable ( & lid_switch_idev - > dev , true ) ;
r = input_register_device ( lid_switch_idev ) ;
if ( r ) {
dev_err ( & pdev - > dev , " failed to register lid switch: %d \n " , r ) ;
goto err_register ;
}
r = device_create_file ( & lid_switch_idev - > dev , & dev_attr_lid_wake_mode ) ;
if ( r ) {
dev_err ( & pdev - > dev , " failed to create wake mode attr: %d \n " , r ) ;
goto err_create_attr ;
}
return 0 ;
err_create_attr :
input_unregister_device ( lid_switch_idev ) ;
2013-04-24 10:46:24 +08:00
lid_switch_idev = NULL ;
2011-06-25 17:34:15 +01:00
err_register :
input_free_device ( lid_switch_idev ) ;
return r ;
}
static void free_lid_switch ( void )
{
device_remove_file ( & lid_switch_idev - > dev , & dev_attr_lid_wake_mode ) ;
input_unregister_device ( lid_switch_idev ) ;
}
2012-12-21 14:02:53 -08:00
static int xo1_sci_probe ( struct platform_device * pdev )
2011-06-25 17:34:12 +01:00
{
struct resource * res ;
int r ;
/* don't run on non-XOs */
if ( ! machine_is_olpc ( ) )
return - ENODEV ;
r = mfd_cell_enable ( pdev ) ;
if ( r )
return r ;
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " can't fetch device resource info \n " ) ;
return - EIO ;
}
acpi_base = res - > start ;
r = setup_power_button ( pdev ) ;
if ( r )
return r ;
2011-06-25 17:34:14 +01:00
r = setup_ebook_switch ( pdev ) ;
if ( r )
goto err_ebook ;
2011-06-25 17:34:15 +01:00
r = setup_lid_switch ( pdev ) ;
if ( r )
goto err_lid ;
r = setup_lid_events ( ) ;
if ( r )
goto err_lidevt ;
2011-06-25 17:34:14 +01:00
r = setup_ec_sci ( ) ;
if ( r )
goto err_ecsci ;
/* Enable PME generation for EC-generated events */
2011-06-25 17:34:15 +01:00
outl ( CS5536_GPIOM6_PME_EN | CS5536_GPIOM7_PME_EN ,
acpi_base + CS5536_PM_GPE0_EN ) ;
2011-06-25 17:34:14 +01:00
/* Clear pending events */
outl ( 0xffffffff , acpi_base + CS5536_PM_GPE0_STS ) ;
process_sci_queue ( false ) ;
/* Initial sync */
send_ebook_state ( ) ;
2011-06-25 17:34:15 +01:00
detect_lid_state ( ) ;
send_lid_state ( ) ;
2011-06-25 17:34:14 +01:00
2011-06-25 17:34:12 +01:00
r = setup_sci_interrupt ( pdev ) ;
if ( r )
2011-06-25 17:34:14 +01:00
goto err_sci ;
2011-06-25 17:34:12 +01:00
2011-06-25 17:34:14 +01:00
/* Enable all EC events */
olpc_ec_mask_write ( EC_SCI_SRC_ALL ) ;
return r ;
err_sci :
free_ec_sci ( ) ;
err_ecsci :
2011-06-25 17:34:15 +01:00
free_lid_events ( ) ;
err_lidevt :
free_lid_switch ( ) ;
err_lid :
2011-06-25 17:34:14 +01:00
free_ebook_switch ( ) ;
err_ebook :
free_power_button ( ) ;
2011-06-25 17:34:12 +01:00
return r ;
}
2012-12-21 14:02:53 -08:00
static int xo1_sci_remove ( struct platform_device * pdev )
2011-06-25 17:34:12 +01:00
{
mfd_cell_disable ( pdev ) ;
free_irq ( sci_irq , pdev ) ;
2011-06-25 17:34:14 +01:00
cancel_work_sync ( & sci_work ) ;
free_ec_sci ( ) ;
2011-06-25 17:34:15 +01:00
free_lid_events ( ) ;
free_lid_switch ( ) ;
2011-06-25 17:34:14 +01:00
free_ebook_switch ( ) ;
2011-06-25 17:34:12 +01:00
free_power_button ( ) ;
acpi_base = 0 ;
return 0 ;
}
static struct platform_driver xo1_sci_driver = {
. driver = {
. name = " olpc-xo1-sci-acpi " ,
} ,
. probe = xo1_sci_probe ,
2012-12-21 14:02:53 -08:00
. remove = xo1_sci_remove ,
2011-06-25 17:34:12 +01:00
. suspend = xo1_sci_suspend ,
2011-06-25 17:34:14 +01:00
. resume = xo1_sci_resume ,
2011-06-25 17:34:12 +01:00
} ;
static int __init xo1_sci_init ( void )
{
return platform_driver_register ( & xo1_sci_driver ) ;
}
arch_initcall ( xo1_sci_init ) ;