2008-03-13 12:55:46 +01:00
/*
2009-12-03 07:45:08 +00:00
* eeepc - laptop . c - Asus Eee PC extras
2008-03-13 12:55:46 +01:00
*
* Based on asus_acpi . c as patched for the Eee PC by Asus :
* ftp : //ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
* Based on eee . c from eeepc - linux
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
2009-06-25 13:25:37 +02:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2008-03-13 12:55:46 +01:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/platform_device.h>
2008-03-13 12:56:37 +01:00
# include <linux/backlight.h>
# include <linux/fb.h>
2008-03-13 12:57:18 +01:00
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
2008-03-13 12:55:46 +01:00
# include <acpi/acpi_drivers.h>
# include <acpi/acpi_bus.h>
# include <linux/uaccess.h>
2008-08-19 12:13:20 +01:00
# include <linux/input.h>
2010-01-06 22:07:39 +01:00
# include <linux/input/sparse-keymap.h>
2008-08-19 12:13:20 +01:00
# include <linux/rfkill.h>
2009-01-20 16:17:48 +01:00
# include <linux/pci.h>
2009-06-25 13:25:36 +02:00
# include <linux/pci_hotplug.h>
2009-12-03 07:44:52 +00:00
# include <linux/leds.h>
2010-01-06 22:07:37 +01:00
# include <linux/dmi.h>
2008-03-13 12:55:46 +01:00
# define EEEPC_LAPTOP_VERSION "0.1"
2009-12-03 07:45:08 +00:00
# define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver"
# define EEEPC_LAPTOP_FILE "eeepc"
2008-03-13 12:55:46 +01:00
2009-12-03 07:45:08 +00:00
# define EEEPC_ACPI_CLASS "hotkey"
# define EEEPC_ACPI_DEVICE_NAME "Hotkey"
# define EEEPC_ACPI_HID "ASUS010"
2008-03-13 12:55:46 +01:00
2009-12-03 07:45:07 +00:00
MODULE_AUTHOR ( " Corentin Chary, Eric Cooper " ) ;
2009-12-03 07:45:08 +00:00
MODULE_DESCRIPTION ( EEEPC_LAPTOP_NAME ) ;
2009-12-03 07:45:07 +00:00
MODULE_LICENSE ( " GPL " ) ;
2008-03-13 12:55:46 +01:00
2010-01-06 22:07:40 +01:00
static bool hotplug_disabled ;
module_param ( hotplug_disabled , bool , 0644 ) ;
MODULE_PARM_DESC ( hotplug_disabled ,
" Disable hotplug for wireless device. "
" If your laptop need that, please report to "
" acpi4asus-user@lists.sourceforge.net. " ) ;
2008-03-13 12:55:46 +01:00
/*
* Definitions for Asus EeePC
*/
2008-03-13 12:56:37 +01:00
# define NOTIFY_BRN_MIN 0x20
# define NOTIFY_BRN_MAX 0x2f
2008-03-13 12:55:46 +01:00
enum {
DISABLE_ASL_WLAN = 0x0001 ,
DISABLE_ASL_BLUETOOTH = 0x0002 ,
DISABLE_ASL_IRDA = 0x0004 ,
DISABLE_ASL_CAMERA = 0x0008 ,
DISABLE_ASL_TV = 0x0010 ,
DISABLE_ASL_GPS = 0x0020 ,
DISABLE_ASL_DISPLAYSWITCH = 0x0040 ,
DISABLE_ASL_MODEM = 0x0080 ,
2009-06-16 19:28:52 +00:00
DISABLE_ASL_CARDREADER = 0x0100 ,
DISABLE_ASL_3G = 0x0200 ,
DISABLE_ASL_WIMAX = 0x0400 ,
DISABLE_ASL_HWCF = 0x0800
2008-03-13 12:55:46 +01:00
} ;
enum {
CM_ASL_WLAN = 0 ,
CM_ASL_BLUETOOTH ,
CM_ASL_IRDA ,
CM_ASL_1394 ,
CM_ASL_CAMERA ,
CM_ASL_TV ,
CM_ASL_GPS ,
CM_ASL_DVDROM ,
CM_ASL_DISPLAYSWITCH ,
CM_ASL_PANELBRIGHT ,
CM_ASL_BIOSFLASH ,
CM_ASL_ACPIFLASH ,
CM_ASL_CPUFV ,
CM_ASL_CPUTEMPERATURE ,
CM_ASL_FANCPU ,
CM_ASL_FANCHASSIS ,
CM_ASL_USBPORT1 ,
CM_ASL_USBPORT2 ,
CM_ASL_USBPORT3 ,
CM_ASL_MODEM ,
CM_ASL_CARDREADER ,
2009-06-16 19:28:52 +00:00
CM_ASL_3G ,
CM_ASL_WIMAX ,
CM_ASL_HWCF ,
CM_ASL_LID ,
CM_ASL_TYPE ,
CM_ASL_PANELPOWER , /*P901*/
CM_ASL_TPD
2008-03-13 12:55:46 +01:00
} ;
2008-06-25 19:25:47 +03:00
static const char * cm_getv [ ] = {
2008-12-03 20:31:11 +00:00
" WLDG " , " BTHG " , NULL , NULL ,
2008-03-13 12:55:46 +01:00
" CAMG " , NULL , NULL , NULL ,
NULL , " PBLG " , NULL , NULL ,
" CFVG " , NULL , NULL , NULL ,
" USBG " , NULL , NULL , " MODG " ,
2009-06-16 19:28:52 +00:00
" CRDG " , " M3GG " , " WIMG " , " HWCF " ,
" LIDG " , " TYPE " , " PBPG " , " TPDG "
2008-03-13 12:55:46 +01:00
} ;
2008-06-25 19:25:47 +03:00
static const char * cm_setv [ ] = {
2008-12-03 20:31:11 +00:00
" WLDS " , " BTHS " , NULL , NULL ,
2008-03-13 12:55:46 +01:00
" CAMS " , NULL , NULL , NULL ,
" SDSP " , " PBLS " , " HDPS " , NULL ,
" CFVS " , NULL , NULL , NULL ,
" USBG " , NULL , NULL , " MODS " ,
2009-06-16 19:28:52 +00:00
" CRDS " , " M3GS " , " WIMS " , NULL ,
NULL , NULL , " PBPS " , " TPDS "
2008-03-13 12:55:46 +01:00
} ;
2009-12-03 07:45:09 +00:00
static const struct key_entry eeepc_keymap [ ] = {
2010-01-06 22:07:39 +01:00
{ KE_KEY , 0x10 , { KEY_WLAN } } ,
{ KE_KEY , 0x11 , { KEY_WLAN } } ,
{ KE_KEY , 0x12 , { KEY_PROG1 } } ,
{ KE_KEY , 0x13 , { KEY_MUTE } } ,
{ KE_KEY , 0x14 , { KEY_VOLUMEDOWN } } ,
{ KE_KEY , 0x15 , { KEY_VOLUMEUP } } ,
{ KE_KEY , 0x16 , { KEY_DISPLAY_OFF } } ,
{ KE_KEY , 0x1a , { KEY_COFFEE } } ,
{ KE_KEY , 0x1b , { KEY_ZOOM } } ,
{ KE_KEY , 0x1c , { KEY_PROG2 } } ,
{ KE_KEY , 0x1d , { KEY_PROG3 } } ,
{ KE_KEY , NOTIFY_BRN_MIN , { KEY_BRIGHTNESSDOWN } } ,
{ KE_KEY , NOTIFY_BRN_MAX , { KEY_BRIGHTNESSUP } } ,
{ KE_KEY , 0x30 , { KEY_SWITCHVIDEOMODE } } ,
{ KE_KEY , 0x31 , { KEY_SWITCHVIDEOMODE } } ,
{ KE_KEY , 0x32 , { KEY_SWITCHVIDEOMODE } } ,
{ KE_KEY , 0x37 , { KEY_F13 } } , /* Disable Touchpad */
{ KE_KEY , 0x38 , { KEY_F14 } } ,
{ KE_END , 0 } ,
2008-08-19 12:13:20 +01:00
} ;
2008-03-13 12:55:46 +01:00
/*
2009-12-03 07:45:07 +00:00
* This is the main structure , we can use it to store useful information
2008-03-13 12:55:46 +01:00
*/
2009-12-03 07:45:08 +00:00
struct eeepc_laptop {
2009-12-03 07:45:09 +00:00
acpi_handle handle ; /* the handle of the acpi device */
2009-12-03 07:45:07 +00:00
u32 cm_supported ; /* the control methods supported
by this BIOS */
2010-01-06 22:07:37 +01:00
bool cpufv_disabled ;
2010-01-06 22:07:38 +01:00
bool hotplug_disabled ;
2009-12-03 07:45:07 +00:00
u16 event_count [ 128 ] ; /* count for each event */
2008-03-13 12:55:46 +01:00
2009-12-03 07:45:09 +00:00
struct platform_device * platform_device ;
struct device * hwmon_device ;
struct backlight_device * backlight_device ;
2009-06-25 13:25:36 +02:00
2009-12-03 07:45:07 +00:00
struct input_dev * inputdev ;
2009-12-03 07:45:09 +00:00
struct key_entry * keymap ;
2009-06-25 13:25:36 +02:00
2009-12-03 07:45:07 +00:00
struct rfkill * wlan_rfkill ;
struct rfkill * bluetooth_rfkill ;
struct rfkill * wwan3g_rfkill ;
struct rfkill * wimax_rfkill ;
2008-03-13 12:56:37 +01:00
2009-12-03 07:45:07 +00:00
struct hotplug_slot * hotplug_slot ;
struct mutex hotplug_lock ;
2008-03-13 12:57:18 +01:00
2009-12-03 07:45:09 +00:00
struct led_classdev tpd_led ;
int tpd_led_wk ;
struct workqueue_struct * led_workqueue ;
struct work_struct tpd_led_work ;
2008-03-13 12:56:37 +01:00
} ;
2008-03-13 12:55:46 +01:00
/*
* ACPI Helpers
*/
2009-12-03 07:45:02 +00:00
static int write_acpi_int ( acpi_handle handle , const char * method , int val )
2008-03-13 12:55:46 +01:00
{
struct acpi_object_list params ;
union acpi_object in_obj ;
acpi_status status ;
params . count = 1 ;
params . pointer = & in_obj ;
in_obj . type = ACPI_TYPE_INTEGER ;
in_obj . integer . value = val ;
2009-12-03 07:45:02 +00:00
status = acpi_evaluate_object ( handle , ( char * ) method , & params , NULL ) ;
2008-03-13 12:55:46 +01:00
return ( status = = AE_OK ? 0 : - 1 ) ;
}
static int read_acpi_int ( acpi_handle handle , const char * method , int * val )
{
acpi_status status ;
2008-10-10 02:22:59 -04:00
unsigned long long result ;
2008-03-13 12:55:46 +01:00
status = acpi_evaluate_integer ( handle , ( char * ) method , NULL , & result ) ;
if ( ACPI_FAILURE ( status ) ) {
* val = - 1 ;
return - 1 ;
} else {
* val = result ;
return 0 ;
}
}
2009-12-03 07:45:09 +00:00
static int set_acpi ( struct eeepc_laptop * eeepc , int cm , int value )
2008-03-13 12:55:46 +01:00
{
2009-12-03 07:44:59 +00:00
const char * method = cm_setv [ cm ] ;
2008-03-13 12:55:46 +01:00
2009-12-03 07:44:59 +00:00
if ( method = = NULL )
return - ENODEV ;
2009-12-03 07:45:08 +00:00
if ( ( eeepc - > cm_supported & ( 0x1 < < cm ) ) = = 0 )
2009-12-03 07:44:59 +00:00
return - ENODEV ;
2008-03-13 12:56:37 +01:00
2009-12-03 07:45:08 +00:00
if ( write_acpi_int ( eeepc - > handle , method , value ) )
2009-12-03 07:44:59 +00:00
pr_warning ( " Error writing %s \n " , method ) ;
2008-03-13 12:55:46 +01:00
return 0 ;
2008-03-13 12:56:37 +01:00
}
2009-12-03 07:45:09 +00:00
static int get_acpi ( struct eeepc_laptop * eeepc , int cm )
2008-03-13 12:56:37 +01:00
{
2009-12-03 07:44:59 +00:00
const char * method = cm_getv [ cm ] ;
int value ;
2008-03-13 12:56:37 +01:00
2009-12-03 07:44:59 +00:00
if ( method = = NULL )
return - ENODEV ;
2009-12-03 07:45:08 +00:00
if ( ( eeepc - > cm_supported & ( 0x1 < < cm ) ) = = 0 )
2009-12-03 07:44:59 +00:00
return - ENODEV ;
2008-08-19 12:13:20 +01:00
2009-12-03 07:45:08 +00:00
if ( read_acpi_int ( eeepc - > handle , method , & value ) )
2009-12-03 07:44:59 +00:00
pr_warning ( " Error reading %s \n " , method ) ;
2008-03-13 12:55:46 +01:00
return value ;
2008-08-19 12:13:20 +01:00
}
2009-12-03 07:45:14 +00:00
static int acpi_setter_handle ( struct eeepc_laptop * eeepc , int cm ,
acpi_handle * handle )
2008-08-19 12:13:20 +01:00
{
2009-12-03 07:45:09 +00:00
const char * method = cm_setv [ cm ] ;
acpi_status status ;
2008-08-19 12:13:20 +01:00
2009-12-03 07:45:09 +00:00
if ( method = = NULL )
return - ENODEV ;
if ( ( eeepc - > cm_supported & ( 0x1 < < cm ) ) = = 0 )
return - ENODEV ;
2008-08-19 12:13:20 +01:00
2009-12-03 07:45:09 +00:00
status = acpi_get_handle ( eeepc - > handle , ( char * ) method ,
2009-12-03 07:45:14 +00:00
handle ) ;
2009-12-03 07:45:09 +00:00
if ( status ! = AE_OK ) {
pr_warning ( " Error finding %s \n " , method ) ;
return - ENODEV ;
}
return 0 ;
2009-06-16 19:28:45 +00:00
}
2009-12-03 07:45:09 +00:00
2008-03-13 12:55:46 +01:00
/*
* Sys helpers
*/
static int parse_arg ( const char * buf , unsigned long count , int * val )
{
if ( ! count )
return 0 ;
if ( sscanf ( buf , " %i " , val ) ! = 1 )
return - EINVAL ;
return count ;
}
2009-12-03 07:45:09 +00:00
static ssize_t store_sys_acpi ( struct device * dev , int cm ,
const char * buf , size_t count )
2008-03-13 12:55:46 +01:00
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = dev_get_drvdata ( dev ) ;
2008-03-13 12:55:46 +01:00
int rv , value ;
rv = parse_arg ( buf , count , & value ) ;
if ( rv > 0 )
2009-12-03 07:45:09 +00:00
value = set_acpi ( eeepc , cm , value ) ;
2009-06-25 13:25:40 +02:00
if ( value < 0 )
2009-12-03 07:44:45 +00:00
return - EIO ;
2008-03-13 12:55:46 +01:00
return rv ;
}
2009-12-03 07:45:09 +00:00
static ssize_t show_sys_acpi ( struct device * dev , int cm , char * buf )
2008-03-13 12:55:46 +01:00
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = dev_get_drvdata ( dev ) ;
int value = get_acpi ( eeepc , cm ) ;
2009-06-25 13:25:40 +02:00
if ( value < 0 )
2009-12-03 07:44:45 +00:00
return - EIO ;
2009-06-25 13:25:40 +02:00
return sprintf ( buf , " %d \n " , value ) ;
2008-03-13 12:55:46 +01:00
}
2009-12-03 07:44:45 +00:00
# define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
2008-03-13 12:55:46 +01:00
static ssize_t show_ # # _name ( struct device * dev , \
struct device_attribute * attr , \
char * buf ) \
{ \
2009-12-03 07:45:09 +00:00
return show_sys_acpi ( dev , _cm , buf ) ; \
2008-03-13 12:55:46 +01:00
} \
static ssize_t store_ # # _name ( struct device * dev , \
struct device_attribute * attr , \
const char * buf , size_t count ) \
{ \
2009-12-03 07:45:09 +00:00
return store_sys_acpi ( dev , _cm , buf , count ) ; \
2008-03-13 12:55:46 +01:00
} \
static struct device_attribute dev_attr_ # # _name = { \
. attr = { \
. name = __stringify ( _name ) , \
2009-12-03 07:44:45 +00:00
. mode = _mode } , \
2008-03-13 12:55:46 +01:00
. show = show_ # # _name , \
. store = store_ # # _name , \
}
2009-12-03 07:44:45 +00:00
EEEPC_CREATE_DEVICE_ATTR ( camera , 0644 , CM_ASL_CAMERA ) ;
EEEPC_CREATE_DEVICE_ATTR ( cardr , 0644 , CM_ASL_CARDREADER ) ;
EEEPC_CREATE_DEVICE_ATTR ( disp , 0200 , CM_ASL_DISPLAYSWITCH ) ;
2009-06-16 19:28:56 +00:00
struct eeepc_cpufv {
int num ;
int cur ;
} ;
2009-12-03 07:45:09 +00:00
static int get_cpufv ( struct eeepc_laptop * eeepc , struct eeepc_cpufv * c )
2009-06-16 19:28:56 +00:00
{
2009-12-03 07:45:09 +00:00
c - > cur = get_acpi ( eeepc , CM_ASL_CPUFV ) ;
2009-06-16 19:28:56 +00:00
c - > num = ( c - > cur > > 8 ) & 0xff ;
c - > cur & = 0xff ;
if ( c - > cur < 0 | | c - > num < = 0 | | c - > num > 12 )
return - ENODEV ;
return 0 ;
}
static ssize_t show_available_cpufv ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = dev_get_drvdata ( dev ) ;
2009-06-16 19:28:56 +00:00
struct eeepc_cpufv c ;
int i ;
ssize_t len = 0 ;
2009-12-03 07:45:09 +00:00
if ( get_cpufv ( eeepc , & c ) )
2009-06-16 19:28:56 +00:00
return - ENODEV ;
for ( i = 0 ; i < c . num ; i + + )
len + = sprintf ( buf + len , " %d " , i ) ;
len + = sprintf ( buf + len , " \n " ) ;
return len ;
}
static ssize_t show_cpufv ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = dev_get_drvdata ( dev ) ;
2009-06-16 19:28:56 +00:00
struct eeepc_cpufv c ;
2009-12-03 07:45:09 +00:00
if ( get_cpufv ( eeepc , & c ) )
2009-06-16 19:28:56 +00:00
return - ENODEV ;
return sprintf ( buf , " %#x \n " , ( c . num < < 8 ) | c . cur ) ;
}
static ssize_t store_cpufv ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = dev_get_drvdata ( dev ) ;
2009-06-16 19:28:56 +00:00
struct eeepc_cpufv c ;
int rv , value ;
2010-01-06 22:07:37 +01:00
if ( eeepc - > cpufv_disabled )
return - EPERM ;
2009-12-03 07:45:09 +00:00
if ( get_cpufv ( eeepc , & c ) )
2009-06-16 19:28:56 +00:00
return - ENODEV ;
rv = parse_arg ( buf , count , & value ) ;
if ( rv < 0 )
return rv ;
if ( ! rv | | value < 0 | | value > = c . num )
return - EINVAL ;
2009-12-03 07:45:09 +00:00
set_acpi ( eeepc , CM_ASL_CPUFV , value ) ;
2009-06-16 19:28:56 +00:00
return rv ;
}
2010-01-06 22:07:37 +01:00
static ssize_t show_cpufv_disabled ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct eeepc_laptop * eeepc = dev_get_drvdata ( dev ) ;
return sprintf ( buf , " %d \n " , eeepc - > cpufv_disabled ) ;
}
static ssize_t store_cpufv_disabled ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct eeepc_laptop * eeepc = dev_get_drvdata ( dev ) ;
int rv , value ;
rv = parse_arg ( buf , count , & value ) ;
if ( rv < 0 )
return rv ;
switch ( value ) {
case 0 :
if ( eeepc - > cpufv_disabled )
pr_warning ( " cpufv enabled (not officially supported "
" on this model) \n " ) ;
eeepc - > cpufv_disabled = false ;
return rv ;
case 1 :
return - EPERM ;
default :
return - EINVAL ;
}
}
2009-06-16 19:28:56 +00:00
static struct device_attribute dev_attr_cpufv = {
. attr = {
. name = " cpufv " ,
. mode = 0644 } ,
. show = show_cpufv ,
. store = store_cpufv
} ;
static struct device_attribute dev_attr_available_cpufv = {
. attr = {
. name = " available_cpufv " ,
. mode = 0444 } ,
. show = show_available_cpufv
} ;
2008-03-13 12:55:46 +01:00
2010-01-06 22:07:37 +01:00
static struct device_attribute dev_attr_cpufv_disabled = {
. attr = {
. name = " cpufv_disabled " ,
. mode = 0644 } ,
. show = show_cpufv_disabled ,
. store = store_cpufv_disabled
} ;
2008-03-13 12:55:46 +01:00
static struct attribute * platform_attributes [ ] = {
& dev_attr_camera . attr ,
& dev_attr_cardr . attr ,
& dev_attr_disp . attr ,
2009-04-27 09:23:40 +02:00
& dev_attr_cpufv . attr ,
2009-06-16 19:28:56 +00:00
& dev_attr_available_cpufv . attr ,
2010-01-06 22:07:37 +01:00
& dev_attr_cpufv_disabled . attr ,
2008-03-13 12:55:46 +01:00
NULL
} ;
static struct attribute_group platform_attribute_group = {
. attrs = platform_attributes
} ;
2009-12-03 07:45:09 +00:00
static int eeepc_platform_init ( struct eeepc_laptop * eeepc )
2008-08-19 12:13:20 +01:00
{
2009-12-03 07:45:06 +00:00
int result ;
2008-08-19 12:13:20 +01:00
2009-12-03 07:45:09 +00:00
eeepc - > platform_device = platform_device_alloc ( EEEPC_LAPTOP_FILE , - 1 ) ;
if ( ! eeepc - > platform_device )
2009-12-03 07:45:06 +00:00
return - ENOMEM ;
2009-12-03 07:45:09 +00:00
platform_set_drvdata ( eeepc - > platform_device , eeepc ) ;
2008-08-19 12:13:20 +01:00
2009-12-03 07:45:09 +00:00
result = platform_device_add ( eeepc - > platform_device ) ;
2009-12-03 07:45:06 +00:00
if ( result )
goto fail_platform_device ;
2008-08-19 12:13:20 +01:00
2009-12-03 07:45:09 +00:00
result = sysfs_create_group ( & eeepc - > platform_device - > dev . kobj ,
2009-12-03 07:45:06 +00:00
& platform_attribute_group ) ;
if ( result )
goto fail_sysfs ;
return 0 ;
2008-08-19 12:13:20 +01:00
2009-12-03 07:45:06 +00:00
fail_sysfs :
2009-12-03 07:45:09 +00:00
platform_device_del ( eeepc - > platform_device ) ;
2009-12-03 07:45:06 +00:00
fail_platform_device :
2009-12-03 07:45:09 +00:00
platform_device_put ( eeepc - > platform_device ) ;
2009-12-03 07:45:06 +00:00
return result ;
2008-08-19 12:13:20 +01:00
}
2009-12-03 07:45:09 +00:00
static void eeepc_platform_exit ( struct eeepc_laptop * eeepc )
2008-08-19 12:13:20 +01:00
{
2009-12-03 07:45:09 +00:00
sysfs_remove_group ( & eeepc - > platform_device - > dev . kobj ,
2009-12-03 07:45:06 +00:00
& platform_attribute_group ) ;
2009-12-03 07:45:09 +00:00
platform_device_unregister ( eeepc - > platform_device ) ;
2009-12-03 07:45:06 +00:00
}
2008-08-19 12:13:20 +01:00
2009-12-03 07:44:52 +00:00
/*
* LEDs
*/
/*
* These functions actually update the LED ' s , and are called from a
* workqueue . By doing this as separate work rather than when the LED
* subsystem asks , we avoid messing with the Asus ACPI stuff during a
* potentially bad time , such as a timer interrupt .
*/
2009-12-03 07:45:09 +00:00
static void tpd_led_update ( struct work_struct * work )
{
struct eeepc_laptop * eeepc ;
2008-08-19 12:13:20 +01:00
2009-12-03 07:45:09 +00:00
eeepc = container_of ( work , struct eeepc_laptop , tpd_led_work ) ;
2009-12-03 07:44:52 +00:00
2009-12-03 07:45:09 +00:00
set_acpi ( eeepc , CM_ASL_TPD , eeepc - > tpd_led_wk ) ;
2008-08-19 12:13:20 +01:00
}
2009-12-03 07:44:52 +00:00
static void tpd_led_set ( struct led_classdev * led_cdev ,
enum led_brightness value )
2008-08-19 12:13:20 +01:00
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc ;
2008-08-19 12:13:20 +01:00
2009-12-03 07:45:09 +00:00
eeepc = container_of ( led_cdev , struct eeepc_laptop , tpd_led ) ;
2008-08-19 12:13:20 +01:00
2009-12-03 07:45:09 +00:00
eeepc - > tpd_led_wk = ( value > 0 ) ? 1 : 0 ;
queue_work ( eeepc - > led_workqueue , & eeepc - > tpd_led_work ) ;
2008-08-19 12:13:20 +01:00
}
2009-12-03 07:45:09 +00:00
static int eeepc_led_init ( struct eeepc_laptop * eeepc )
2009-06-25 13:25:41 +02:00
{
2009-12-03 07:45:07 +00:00
int rv ;
2009-06-25 13:25:41 +02:00
2009-12-03 07:45:09 +00:00
if ( get_acpi ( eeepc , CM_ASL_TPD ) = = - ENODEV )
2009-12-03 07:45:07 +00:00
return 0 ;
2009-06-25 13:25:41 +02:00
2009-12-03 07:45:09 +00:00
eeepc - > led_workqueue = create_singlethread_workqueue ( " led_workqueue " ) ;
if ( ! eeepc - > led_workqueue )
2009-12-03 07:45:07 +00:00
return - ENOMEM ;
2009-12-03 07:45:09 +00:00
INIT_WORK ( & eeepc - > tpd_led_work , tpd_led_update ) ;
2009-06-25 13:25:41 +02:00
2009-12-03 07:45:09 +00:00
eeepc - > tpd_led . name = " eeepc::touchpad " ;
eeepc - > tpd_led . brightness_set = tpd_led_set ;
eeepc - > tpd_led . max_brightness = 1 ;
2008-03-13 12:55:46 +01:00
2009-12-03 07:45:09 +00:00
rv = led_classdev_register ( & eeepc - > platform_device - > dev ,
& eeepc - > tpd_led ) ;
2009-12-03 07:45:07 +00:00
if ( rv ) {
2009-12-03 07:45:09 +00:00
destroy_workqueue ( eeepc - > led_workqueue ) ;
2009-12-03 07:45:07 +00:00
return rv ;
2008-03-13 12:55:46 +01:00
}
2008-08-19 12:13:20 +01:00
2008-03-13 12:55:46 +01:00
return 0 ;
}
2009-12-03 07:45:09 +00:00
static void eeepc_led_exit ( struct eeepc_laptop * eeepc )
2008-03-13 12:56:37 +01:00
{
2009-12-03 07:45:09 +00:00
if ( eeepc - > tpd_led . dev )
led_classdev_unregister ( & eeepc - > tpd_led ) ;
if ( eeepc - > led_workqueue )
destroy_workqueue ( eeepc - > led_workqueue ) ;
2008-03-13 12:56:37 +01:00
}
2009-06-25 13:25:36 +02:00
2009-12-03 07:45:07 +00:00
/*
* PCI hotplug ( for wlan rfkill )
*/
2009-12-03 07:45:09 +00:00
static bool eeepc_wlan_rfkill_blocked ( struct eeepc_laptop * eeepc )
2009-12-03 07:45:07 +00:00
{
2009-12-03 07:45:09 +00:00
if ( get_acpi ( eeepc , CM_ASL_WLAN ) = = 1 )
2009-12-03 07:45:07 +00:00
return false ;
return true ;
2009-06-25 13:25:36 +02:00
}
2009-12-03 07:45:09 +00:00
static void eeepc_rfkill_hotplug ( struct eeepc_laptop * eeepc )
2009-01-20 16:17:48 +01:00
{
struct pci_dev * dev ;
2009-08-28 12:56:32 +00:00
struct pci_bus * bus ;
2009-12-03 07:45:09 +00:00
bool blocked = eeepc_wlan_rfkill_blocked ( eeepc ) ;
2010-02-22 16:03:58 +00:00
bool absent ;
u32 l ;
2009-01-20 16:17:48 +01:00
2009-12-03 07:45:08 +00:00
if ( eeepc - > wlan_rfkill )
rfkill_set_sw_state ( eeepc - > wlan_rfkill , blocked ) ;
2009-08-28 12:56:32 +00:00
2009-12-03 07:45:08 +00:00
mutex_lock ( & eeepc - > hotplug_lock ) ;
2009-08-28 12:56:33 +00:00
2009-12-03 07:45:08 +00:00
if ( eeepc - > hotplug_slot ) {
2009-08-28 12:56:34 +00:00
bus = pci_find_bus ( 0 , 1 ) ;
if ( ! bus ) {
pr_warning ( " Unable to find PCI bus 1? \n " ) ;
2009-08-28 12:56:33 +00:00
goto out_unlock ;
2009-01-20 16:17:48 +01:00
}
2009-08-28 12:56:34 +00:00
2010-02-22 16:03:58 +00:00
if ( pci_bus_read_config_dword ( bus , 0 , PCI_VENDOR_ID , & l ) ) {
pr_err ( " Unable to read PCI config space? \n " ) ;
goto out_unlock ;
}
absent = ( l = = 0xffffffff ) ;
if ( blocked ! = absent ) {
pr_warning ( " BIOS says wireless lan is %s, "
" but the pci device is %s \n " ,
blocked ? " blocked " : " unblocked " ,
absent ? " absent " : " present " ) ;
pr_warning ( " skipped wireless hotplug as probably "
" inappropriate for this model \n " ) ;
goto out_unlock ;
}
2009-08-28 12:56:34 +00:00
if ( ! blocked ) {
dev = pci_get_slot ( bus , 0 ) ;
if ( dev ) {
/* Device already present */
pci_dev_put ( dev ) ;
goto out_unlock ;
}
dev = pci_scan_single_device ( bus , 0 ) ;
if ( dev ) {
pci_bus_assign_resources ( bus ) ;
if ( pci_bus_add_device ( dev ) )
pr_err ( " Unable to hotplug wifi \n " ) ;
}
} else {
dev = pci_get_slot ( bus , 0 ) ;
if ( dev ) {
pci_remove_bus_device ( dev ) ;
pci_dev_put ( dev ) ;
}
2009-01-20 16:17:48 +01:00
}
}
2009-08-28 12:56:33 +00:00
out_unlock :
2009-12-03 07:45:08 +00:00
mutex_unlock ( & eeepc - > hotplug_lock ) ;
2009-01-20 16:17:48 +01:00
}
2009-06-16 14:53:52 +01:00
static void eeepc_rfkill_notify ( acpi_handle handle , u32 event , void * data )
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = data ;
2009-06-16 14:53:52 +01:00
if ( event ! = ACPI_NOTIFY_BUS_CHECK )
return ;
2009-12-03 07:45:09 +00:00
eeepc_rfkill_hotplug ( eeepc ) ;
2008-03-13 12:55:46 +01:00
}
2009-12-03 07:45:09 +00:00
static int eeepc_register_rfkill_notifier ( struct eeepc_laptop * eeepc ,
char * node )
2009-01-20 16:17:48 +01:00
{
2009-12-03 07:45:09 +00:00
acpi_status status ;
2009-01-20 16:17:48 +01:00
acpi_handle handle ;
status = acpi_get_handle ( NULL , node , & handle ) ;
if ( ACPI_SUCCESS ( status ) ) {
status = acpi_install_notify_handler ( handle ,
ACPI_SYSTEM_NOTIFY ,
eeepc_rfkill_notify ,
2009-12-03 07:45:09 +00:00
eeepc ) ;
2009-01-20 16:17:48 +01:00
if ( ACPI_FAILURE ( status ) )
2009-06-25 13:25:37 +02:00
pr_warning ( " Failed to register notify on %s \n " , node ) ;
2009-01-20 16:17:48 +01:00
} else
return - ENODEV ;
return 0 ;
}
2009-12-03 07:45:09 +00:00
static void eeepc_unregister_rfkill_notifier ( struct eeepc_laptop * eeepc ,
char * node )
2009-01-20 16:17:48 +01:00
{
acpi_status status = AE_OK ;
acpi_handle handle ;
status = acpi_get_handle ( NULL , node , & handle ) ;
if ( ACPI_SUCCESS ( status ) ) {
status = acpi_remove_notify_handler ( handle ,
ACPI_SYSTEM_NOTIFY ,
eeepc_rfkill_notify ) ;
if ( ACPI_FAILURE ( status ) )
2009-06-25 13:25:37 +02:00
pr_err ( " Error removing rfkill notify handler %s \n " ,
2009-01-20 16:17:48 +01:00
node ) ;
}
}
2009-12-03 07:45:07 +00:00
static int eeepc_get_adapter_status ( struct hotplug_slot * hotplug_slot ,
u8 * value )
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = hotplug_slot - > private ;
int val = get_acpi ( eeepc , CM_ASL_WLAN ) ;
2009-12-03 07:45:07 +00:00
if ( val = = 1 | | val = = 0 )
* value = val ;
else
return - EINVAL ;
return 0 ;
}
2009-06-25 13:25:36 +02:00
static void eeepc_cleanup_pci_hotplug ( struct hotplug_slot * hotplug_slot )
{
kfree ( hotplug_slot - > info ) ;
kfree ( hotplug_slot ) ;
}
2009-12-03 07:45:07 +00:00
static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
. owner = THIS_MODULE ,
. get_adapter_status = eeepc_get_adapter_status ,
. get_power_status = eeepc_get_adapter_status ,
} ;
2009-12-03 07:45:09 +00:00
static int eeepc_setup_pci_hotplug ( struct eeepc_laptop * eeepc )
2009-06-25 13:25:36 +02:00
{
int ret = - ENOMEM ;
struct pci_bus * bus = pci_find_bus ( 0 , 1 ) ;
if ( ! bus ) {
2009-06-25 13:25:37 +02:00
pr_err ( " Unable to find wifi PCI bus \n " ) ;
2009-06-25 13:25:36 +02:00
return - ENODEV ;
}
2009-12-03 07:45:08 +00:00
eeepc - > hotplug_slot = kzalloc ( sizeof ( struct hotplug_slot ) , GFP_KERNEL ) ;
if ( ! eeepc - > hotplug_slot )
2009-06-25 13:25:36 +02:00
goto error_slot ;
2009-12-03 07:45:08 +00:00
eeepc - > hotplug_slot - > info = kzalloc ( sizeof ( struct hotplug_slot_info ) ,
2009-06-25 13:25:36 +02:00
GFP_KERNEL ) ;
2009-12-03 07:45:08 +00:00
if ( ! eeepc - > hotplug_slot - > info )
2009-06-25 13:25:36 +02:00
goto error_info ;
2009-12-03 07:45:08 +00:00
eeepc - > hotplug_slot - > private = eeepc ;
eeepc - > hotplug_slot - > release = & eeepc_cleanup_pci_hotplug ;
eeepc - > hotplug_slot - > ops = & eeepc_hotplug_slot_ops ;
eeepc_get_adapter_status ( eeepc - > hotplug_slot ,
& eeepc - > hotplug_slot - > info - > adapter_status ) ;
2009-06-25 13:25:36 +02:00
2009-12-03 07:45:08 +00:00
ret = pci_hp_register ( eeepc - > hotplug_slot , bus , 0 , " eeepc-wifi " ) ;
2009-06-25 13:25:36 +02:00
if ( ret ) {
2009-06-25 13:25:37 +02:00
pr_err ( " Unable to register hotplug slot - %d \n " , ret ) ;
2009-06-25 13:25:36 +02:00
goto error_register ;
}
return 0 ;
error_register :
2009-12-03 07:45:08 +00:00
kfree ( eeepc - > hotplug_slot - > info ) ;
2009-06-25 13:25:36 +02:00
error_info :
2009-12-03 07:45:08 +00:00
kfree ( eeepc - > hotplug_slot ) ;
eeepc - > hotplug_slot = NULL ;
2009-06-25 13:25:36 +02:00
error_slot :
return ret ;
}
2009-12-03 07:45:07 +00:00
/*
* Rfkill devices
*/
static int eeepc_rfkill_set ( void * data , bool blocked )
2009-06-16 14:53:52 +01:00
{
2009-12-03 07:45:09 +00:00
acpi_handle handle = data ;
2009-08-28 12:56:40 +00:00
2009-12-03 07:45:09 +00:00
return write_acpi_int ( handle , NULL , ! blocked ) ;
2009-08-28 12:56:40 +00:00
}
2009-06-16 14:53:52 +01:00
2009-12-03 07:45:07 +00:00
static const struct rfkill_ops eeepc_rfkill_ops = {
. set_block = eeepc_rfkill_set ,
} ;
2009-12-03 07:45:09 +00:00
static int eeepc_new_rfkill ( struct eeepc_laptop * eeepc ,
struct rfkill * * rfkill ,
const char * name ,
2009-12-03 07:45:07 +00:00
enum rfkill_type type , int cm )
{
2009-12-03 07:45:09 +00:00
acpi_handle handle ;
2009-12-03 07:45:07 +00:00
int result ;
2009-12-03 07:45:09 +00:00
result = acpi_setter_handle ( eeepc , cm , & handle ) ;
2009-12-03 07:45:07 +00:00
if ( result < 0 )
return result ;
2009-12-03 07:45:09 +00:00
* rfkill = rfkill_alloc ( name , & eeepc - > platform_device - > dev , type ,
& eeepc_rfkill_ops , handle ) ;
2009-06-16 14:53:52 +01:00
2009-12-03 07:45:07 +00:00
if ( ! * rfkill )
return - EINVAL ;
2009-12-03 07:45:09 +00:00
rfkill_init_sw_state ( * rfkill , get_acpi ( eeepc , cm ) ! = 1 ) ;
2009-12-03 07:45:07 +00:00
result = rfkill_register ( * rfkill ) ;
if ( result ) {
rfkill_destroy ( * rfkill ) ;
* rfkill = NULL ;
return result ;
}
2009-06-16 14:53:52 +01:00
return 0 ;
}
2009-12-03 07:45:09 +00:00
static void eeepc_rfkill_exit ( struct eeepc_laptop * eeepc )
2009-12-03 07:45:07 +00:00
{
2009-12-03 07:45:09 +00:00
eeepc_unregister_rfkill_notifier ( eeepc , " \\ _SB.PCI0.P0P5 " ) ;
eeepc_unregister_rfkill_notifier ( eeepc , " \\ _SB.PCI0.P0P6 " ) ;
eeepc_unregister_rfkill_notifier ( eeepc , " \\ _SB.PCI0.P0P7 " ) ;
2009-12-03 07:45:08 +00:00
if ( eeepc - > wlan_rfkill ) {
rfkill_unregister ( eeepc - > wlan_rfkill ) ;
rfkill_destroy ( eeepc - > wlan_rfkill ) ;
eeepc - > wlan_rfkill = NULL ;
2009-12-03 07:45:07 +00:00
}
/*
* Refresh pci hotplug in case the rfkill state was changed after
* eeepc_unregister_rfkill_notifier ( )
*/
2009-12-03 07:45:09 +00:00
eeepc_rfkill_hotplug ( eeepc ) ;
2009-12-03 07:45:08 +00:00
if ( eeepc - > hotplug_slot )
pci_hp_deregister ( eeepc - > hotplug_slot ) ;
2009-12-03 07:45:07 +00:00
2009-12-03 07:45:08 +00:00
if ( eeepc - > bluetooth_rfkill ) {
rfkill_unregister ( eeepc - > bluetooth_rfkill ) ;
rfkill_destroy ( eeepc - > bluetooth_rfkill ) ;
eeepc - > bluetooth_rfkill = NULL ;
2009-12-03 07:45:07 +00:00
}
2009-12-03 07:45:08 +00:00
if ( eeepc - > wwan3g_rfkill ) {
rfkill_unregister ( eeepc - > wwan3g_rfkill ) ;
rfkill_destroy ( eeepc - > wwan3g_rfkill ) ;
eeepc - > wwan3g_rfkill = NULL ;
2009-12-03 07:45:07 +00:00
}
2009-12-03 07:45:08 +00:00
if ( eeepc - > wimax_rfkill ) {
rfkill_unregister ( eeepc - > wimax_rfkill ) ;
rfkill_destroy ( eeepc - > wimax_rfkill ) ;
eeepc - > wimax_rfkill = NULL ;
2009-12-03 07:45:07 +00:00
}
}
2009-12-03 07:45:09 +00:00
static int eeepc_rfkill_init ( struct eeepc_laptop * eeepc )
2009-12-03 07:45:07 +00:00
{
int result = 0 ;
2009-12-03 07:45:08 +00:00
mutex_init ( & eeepc - > hotplug_lock ) ;
2009-12-03 07:45:07 +00:00
2009-12-03 07:45:09 +00:00
result = eeepc_new_rfkill ( eeepc , & eeepc - > wlan_rfkill ,
" eeepc-wlan " , RFKILL_TYPE_WLAN ,
CM_ASL_WLAN ) ;
2009-12-03 07:45:07 +00:00
if ( result & & result ! = - ENODEV )
goto exit ;
2009-12-03 07:45:09 +00:00
result = eeepc_new_rfkill ( eeepc , & eeepc - > bluetooth_rfkill ,
" eeepc-bluetooth " , RFKILL_TYPE_BLUETOOTH ,
CM_ASL_BLUETOOTH ) ;
2009-12-03 07:45:07 +00:00
if ( result & & result ! = - ENODEV )
goto exit ;
2009-12-03 07:45:09 +00:00
result = eeepc_new_rfkill ( eeepc , & eeepc - > wwan3g_rfkill ,
" eeepc-wwan3g " , RFKILL_TYPE_WWAN ,
CM_ASL_3G ) ;
2009-12-03 07:45:07 +00:00
if ( result & & result ! = - ENODEV )
goto exit ;
2009-12-03 07:45:09 +00:00
result = eeepc_new_rfkill ( eeepc , & eeepc - > wimax_rfkill ,
" eeepc-wimax " , RFKILL_TYPE_WIMAX ,
CM_ASL_WIMAX ) ;
2009-12-03 07:45:07 +00:00
if ( result & & result ! = - ENODEV )
goto exit ;
2010-01-06 22:07:38 +01:00
if ( eeepc - > hotplug_disabled )
return 0 ;
2009-12-03 07:45:09 +00:00
result = eeepc_setup_pci_hotplug ( eeepc ) ;
2009-12-03 07:45:07 +00:00
/*
* If we get - EBUSY then something else is handling the PCI hotplug -
* don ' t fail in this case
*/
if ( result = = - EBUSY )
result = 0 ;
2009-12-03 07:45:09 +00:00
eeepc_register_rfkill_notifier ( eeepc , " \\ _SB.PCI0.P0P5 " ) ;
eeepc_register_rfkill_notifier ( eeepc , " \\ _SB.PCI0.P0P6 " ) ;
eeepc_register_rfkill_notifier ( eeepc , " \\ _SB.PCI0.P0P7 " ) ;
2009-12-03 07:45:07 +00:00
/*
* Refresh pci hotplug in case the rfkill state was changed during
* setup .
*/
2009-12-03 07:45:09 +00:00
eeepc_rfkill_hotplug ( eeepc ) ;
2009-12-03 07:45:07 +00:00
exit :
if ( result & & result ! = - ENODEV )
2009-12-03 07:45:09 +00:00
eeepc_rfkill_exit ( eeepc ) ;
2009-12-03 07:45:07 +00:00
return result ;
}
2008-03-13 12:57:18 +01:00
/*
2009-12-03 07:45:07 +00:00
* Platform driver - hibernate / resume callbacks
2008-03-13 12:57:18 +01:00
*/
2009-12-03 07:45:09 +00:00
static int eeepc_hotk_thaw ( struct device * device )
2009-06-16 14:53:52 +01:00
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = dev_get_drvdata ( device ) ;
2009-12-03 07:45:08 +00:00
if ( eeepc - > wlan_rfkill ) {
2009-06-16 14:53:52 +01:00
bool wlan ;
2009-08-28 12:56:39 +00:00
/*
* Work around bios bug - acpi _PTS turns off the wireless led
* during suspend . Normally it restores it on resume , but
2009-08-28 12:56:40 +00:00
* we should kick it ourselves in case hibernation is aborted .
2009-06-16 14:53:52 +01:00
*/
2009-12-03 07:45:09 +00:00
wlan = get_acpi ( eeepc , CM_ASL_WLAN ) ;
set_acpi ( eeepc , CM_ASL_WLAN , wlan ) ;
2009-08-28 12:56:40 +00:00
}
return 0 ;
}
2009-06-16 14:53:52 +01:00
2009-12-03 07:45:09 +00:00
static int eeepc_hotk_restore ( struct device * device )
2009-08-28 12:56:40 +00:00
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = dev_get_drvdata ( device ) ;
2009-08-28 12:56:40 +00:00
/* Refresh both wlan rfkill state and pci hotplug */
2009-12-03 07:45:08 +00:00
if ( eeepc - > wlan_rfkill )
2009-12-03 07:45:09 +00:00
eeepc_rfkill_hotplug ( eeepc ) ;
2009-06-16 14:53:52 +01:00
2009-12-03 07:45:08 +00:00
if ( eeepc - > bluetooth_rfkill )
rfkill_set_sw_state ( eeepc - > bluetooth_rfkill ,
2009-12-03 07:45:09 +00:00
get_acpi ( eeepc , CM_ASL_BLUETOOTH ) ! = 1 ) ;
2009-12-03 07:45:08 +00:00
if ( eeepc - > wwan3g_rfkill )
rfkill_set_sw_state ( eeepc - > wwan3g_rfkill ,
2009-12-03 07:45:09 +00:00
get_acpi ( eeepc , CM_ASL_3G ) ! = 1 ) ;
2009-12-03 07:45:08 +00:00
if ( eeepc - > wimax_rfkill )
rfkill_set_sw_state ( eeepc - > wimax_rfkill ,
2009-12-03 07:45:09 +00:00
get_acpi ( eeepc , CM_ASL_WIMAX ) ! = 1 ) ;
2009-06-16 14:53:52 +01:00
return 0 ;
}
2009-12-15 22:34:48 -05:00
static const struct dev_pm_ops eeepc_pm_ops = {
2009-12-03 07:45:09 +00:00
. thaw = eeepc_hotk_thaw ,
. restore = eeepc_hotk_restore ,
2009-12-03 07:45:07 +00:00
} ;
static struct platform_driver platform_driver = {
. driver = {
2009-12-03 07:45:08 +00:00
. name = EEEPC_LAPTOP_FILE ,
2009-12-03 07:45:07 +00:00
. owner = THIS_MODULE ,
. pm = & eeepc_pm_ops ,
}
} ;
2008-03-13 12:57:18 +01:00
/*
2009-12-03 07:45:07 +00:00
* Hwmon device
2008-03-13 12:57:18 +01:00
*/
2009-12-03 07:45:07 +00:00
# define EEEPC_EC_SC00 0x61
# define EEEPC_EC_FAN_PWM (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */
# define EEEPC_EC_FAN_HRPM (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */
# define EEEPC_EC_FAN_LRPM (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */
# define EEEPC_EC_SFB0 0xD0
# define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */
2008-03-13 12:57:18 +01:00
static int eeepc_get_fan_pwm ( void )
{
2009-12-03 07:45:03 +00:00
u8 value = 0 ;
2008-03-13 12:57:18 +01:00
2009-12-03 07:45:03 +00:00
ec_read ( EEEPC_EC_FAN_PWM , & value ) ;
return value * 255 / 100 ;
2008-03-13 12:57:18 +01:00
}
static void eeepc_set_fan_pwm ( int value )
{
2008-10-09 15:33:57 +02:00
value = SENSORS_LIMIT ( value , 0 , 255 ) ;
value = value * 100 / 255 ;
2009-12-03 07:45:03 +00:00
ec_write ( EEEPC_EC_FAN_PWM , value ) ;
2008-03-13 12:57:18 +01:00
}
static int eeepc_get_fan_rpm ( void )
{
2009-12-03 07:45:03 +00:00
u8 high = 0 ;
u8 low = 0 ;
2008-03-13 12:57:18 +01:00
2009-12-03 07:45:03 +00:00
ec_read ( EEEPC_EC_FAN_HRPM , & high ) ;
ec_read ( EEEPC_EC_FAN_LRPM , & low ) ;
return high < < 8 | low ;
2008-03-13 12:57:18 +01:00
}
static int eeepc_get_fan_ctrl ( void )
{
2009-12-03 07:45:03 +00:00
u8 value = 0 ;
2008-03-13 12:57:18 +01:00
2009-12-03 07:45:03 +00:00
ec_read ( EEEPC_EC_FAN_CTRL , & value ) ;
2009-12-03 07:44:56 +00:00
if ( value & 0x02 )
return 1 ; /* manual */
else
return 2 ; /* automatic */
2008-03-13 12:57:18 +01:00
}
static void eeepc_set_fan_ctrl ( int manual )
{
2009-12-03 07:45:03 +00:00
u8 value = 0 ;
2008-03-13 12:57:18 +01:00
2009-12-03 07:45:03 +00:00
ec_read ( EEEPC_EC_FAN_CTRL , & value ) ;
2009-12-03 07:44:56 +00:00
if ( manual = = 1 )
2008-03-13 12:57:18 +01:00
value | = 0x02 ;
else
value & = ~ 0x02 ;
2009-12-03 07:45:03 +00:00
ec_write ( EEEPC_EC_FAN_CTRL , value ) ;
2008-03-13 12:57:18 +01:00
}
static ssize_t store_sys_hwmon ( void ( * set ) ( int ) , const char * buf , size_t count )
{
int rv , value ;
rv = parse_arg ( buf , count , & value ) ;
if ( rv > 0 )
set ( value ) ;
return rv ;
}
static ssize_t show_sys_hwmon ( int ( * get ) ( void ) , char * buf )
{
return sprintf ( buf , " %d \n " , get ( ) ) ;
}
# define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
static ssize_t show_ # # _name ( struct device * dev , \
struct device_attribute * attr , \
char * buf ) \
{ \
return show_sys_hwmon ( _set , buf ) ; \
} \
static ssize_t store_ # # _name ( struct device * dev , \
struct device_attribute * attr , \
const char * buf , size_t count ) \
{ \
return store_sys_hwmon ( _get , buf , count ) ; \
} \
static SENSOR_DEVICE_ATTR ( _name , _mode , show_ # # _name , store_ # # _name , 0 ) ;
EEEPC_CREATE_SENSOR_ATTR ( fan1_input , S_IRUGO , eeepc_get_fan_rpm , NULL ) ;
2008-10-09 15:33:57 +02:00
EEEPC_CREATE_SENSOR_ATTR ( pwm1 , S_IRUGO | S_IWUSR ,
2008-03-13 12:57:18 +01:00
eeepc_get_fan_pwm , eeepc_set_fan_pwm ) ;
EEEPC_CREATE_SENSOR_ATTR ( pwm1_enable , S_IRUGO | S_IWUSR ,
eeepc_get_fan_ctrl , eeepc_set_fan_ctrl ) ;
2008-10-09 15:33:57 +02:00
static ssize_t
show_name ( struct device * dev , struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " eeepc \n " ) ;
}
static SENSOR_DEVICE_ATTR ( name , S_IRUGO , show_name , NULL , 0 ) ;
2008-03-13 12:57:18 +01:00
static struct attribute * hwmon_attributes [ ] = {
2008-10-09 15:33:57 +02:00
& sensor_dev_attr_pwm1 . dev_attr . attr ,
2008-03-13 12:57:18 +01:00
& sensor_dev_attr_fan1_input . dev_attr . attr ,
& sensor_dev_attr_pwm1_enable . dev_attr . attr ,
2008-10-09 15:33:57 +02:00
& sensor_dev_attr_name . dev_attr . attr ,
2008-03-13 12:57:18 +01:00
NULL
} ;
static struct attribute_group hwmon_attribute_group = {
. attrs = hwmon_attributes
} ;
2009-12-03 07:45:09 +00:00
static void eeepc_hwmon_exit ( struct eeepc_laptop * eeepc )
2008-03-13 12:56:37 +01:00
{
2008-03-13 12:57:18 +01:00
struct device * hwmon ;
2009-12-03 07:45:09 +00:00
hwmon = eeepc - > hwmon_device ;
2008-03-13 12:57:18 +01:00
if ( ! hwmon )
2009-12-03 07:45:09 +00:00
return ;
2008-03-13 12:57:18 +01:00
sysfs_remove_group ( & hwmon - > kobj ,
& hwmon_attribute_group ) ;
2008-08-20 14:08:57 -07:00
hwmon_device_unregister ( hwmon ) ;
2009-12-03 07:45:09 +00:00
eeepc - > hwmon_device = NULL ;
2009-01-20 16:17:40 +01:00
}
2009-12-03 07:45:09 +00:00
static int eeepc_hwmon_init ( struct eeepc_laptop * eeepc )
2009-01-20 16:17:40 +01:00
{
2009-12-03 07:45:07 +00:00
struct device * hwmon ;
2009-06-25 13:25:38 +02:00
int result ;
2009-12-03 07:45:09 +00:00
hwmon = hwmon_device_register ( & eeepc - > platform_device - > dev ) ;
2009-12-03 07:45:07 +00:00
if ( IS_ERR ( hwmon ) ) {
pr_err ( " Could not register eeepc hwmon device \n " ) ;
2009-12-03 07:45:09 +00:00
eeepc - > hwmon_device = NULL ;
2009-12-03 07:45:07 +00:00
return PTR_ERR ( hwmon ) ;
2009-08-29 10:28:30 +02:00
}
2009-12-03 07:45:09 +00:00
eeepc - > hwmon_device = hwmon ;
2009-12-03 07:45:07 +00:00
result = sysfs_create_group ( & hwmon - > kobj ,
& hwmon_attribute_group ) ;
if ( result )
2009-12-03 07:45:09 +00:00
eeepc_hwmon_exit ( eeepc ) ;
2009-12-03 07:45:07 +00:00
return result ;
2009-01-20 16:17:40 +01:00
}
2009-12-03 07:45:07 +00:00
/*
* Backlight device
*/
static int read_brightness ( struct backlight_device * bd )
2009-01-20 16:17:40 +01:00
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = bl_get_data ( bd ) ;
return get_acpi ( eeepc , CM_ASL_PANELBRIGHT ) ;
2008-03-13 12:56:37 +01:00
}
2009-12-03 07:45:07 +00:00
static int set_brightness ( struct backlight_device * bd , int value )
2008-03-13 12:57:18 +01:00
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = bl_get_data ( bd ) ;
2008-03-13 12:57:18 +01:00
2009-12-03 07:45:09 +00:00
return set_acpi ( eeepc , CM_ASL_PANELBRIGHT , value ) ;
2008-03-13 12:57:18 +01:00
}
2009-12-03 07:45:07 +00:00
static int update_bl_status ( struct backlight_device * bd )
2009-06-25 13:25:38 +02:00
{
2009-12-03 07:45:07 +00:00
return set_brightness ( bd , bd - > props . brightness ) ;
}
2009-06-25 13:25:38 +02:00
2009-12-03 07:45:07 +00:00
static struct backlight_ops eeepcbl_ops = {
. get_brightness = read_brightness ,
. update_status = update_bl_status ,
} ;
2009-06-25 13:25:38 +02:00
2009-12-03 07:45:09 +00:00
static int eeepc_backlight_notify ( struct eeepc_laptop * eeepc )
2009-12-03 07:45:07 +00:00
{
2009-12-03 07:45:09 +00:00
struct backlight_device * bd = eeepc - > backlight_device ;
2009-12-03 07:45:07 +00:00
int old = bd - > props . brightness ;
2009-06-25 13:25:38 +02:00
2009-12-03 07:45:07 +00:00
backlight_force_update ( bd , BACKLIGHT_UPDATE_HOTKEY ) ;
2009-06-25 13:25:38 +02:00
2009-12-03 07:45:07 +00:00
return old ;
2009-06-25 13:25:38 +02:00
}
2009-12-03 07:45:09 +00:00
static int eeepc_backlight_init ( struct eeepc_laptop * eeepc )
2009-06-25 13:25:38 +02:00
{
2010-02-17 16:39:44 -05:00
struct backlight_properties props ;
2008-03-13 12:56:37 +01:00
struct backlight_device * bd ;
2009-06-29 09:40:07 +01:00
2010-02-17 16:39:44 -05:00
memset ( & props , 0 , sizeof ( struct backlight_properties ) ) ;
props . max_brightness = 15 ;
2009-12-03 07:45:09 +00:00
bd = backlight_device_register ( EEEPC_LAPTOP_FILE ,
2010-02-17 16:39:44 -05:00
& eeepc - > platform_device - > dev , eeepc ,
& eeepcbl_ops , & props ) ;
2008-03-13 12:56:37 +01:00
if ( IS_ERR ( bd ) ) {
2009-06-25 13:25:37 +02:00
pr_err ( " Could not register eeepc backlight device \n " ) ;
2009-12-03 07:45:09 +00:00
eeepc - > backlight_device = NULL ;
2008-03-13 12:56:37 +01:00
return PTR_ERR ( bd ) ;
}
2009-12-03 07:45:09 +00:00
eeepc - > backlight_device = bd ;
bd - > props . brightness = read_brightness ( bd ) ;
2008-03-13 12:56:37 +01:00
bd - > props . power = FB_BLANK_UNBLANK ;
backlight_update_status ( bd ) ;
return 0 ;
}
2009-06-25 13:25:38 +02:00
2009-12-03 07:45:09 +00:00
static void eeepc_backlight_exit ( struct eeepc_laptop * eeepc )
2008-03-13 12:57:18 +01:00
{
2009-12-03 07:45:09 +00:00
if ( eeepc - > backlight_device )
backlight_device_unregister ( eeepc - > backlight_device ) ;
eeepc - > backlight_device = NULL ;
2009-12-03 07:45:07 +00:00
}
2009-06-25 13:25:38 +02:00
2009-12-03 07:45:07 +00:00
/*
* Input device ( i . e . hotkeys )
*/
2010-01-06 22:07:39 +01:00
static int eeepc_input_init ( struct eeepc_laptop * eeepc )
2009-12-03 07:45:07 +00:00
{
2010-01-06 22:07:39 +01:00
struct input_dev * input ;
int error ;
2009-06-25 13:25:42 +02:00
2010-01-06 22:07:39 +01:00
input = input_allocate_device ( ) ;
if ( ! input ) {
pr_info ( " Unable to allocate input device \n " ) ;
return - ENOMEM ;
2008-03-13 12:57:18 +01:00
}
2009-08-28 12:56:34 +00:00
2010-01-06 22:07:39 +01:00
input - > name = " Asus EeePC extra buttons " ;
input - > phys = EEEPC_LAPTOP_FILE " /input0 " ;
input - > id . bustype = BUS_HOST ;
input - > dev . parent = & eeepc - > platform_device - > dev ;
2009-06-25 13:25:38 +02:00
2010-01-06 22:07:39 +01:00
error = sparse_keymap_setup ( input , eeepc_keymap , NULL ) ;
if ( error ) {
pr_err ( " Unable to setup input device keymap \n " ) ;
goto err_free_dev ;
2008-03-13 12:56:37 +01:00
}
2009-12-03 07:45:07 +00:00
2010-01-06 22:07:39 +01:00
error = input_register_device ( input ) ;
if ( error ) {
pr_err ( " Unable to register input device \n " ) ;
goto err_free_keymap ;
2008-03-13 12:57:18 +01:00
}
2009-12-03 07:45:07 +00:00
2010-01-06 22:07:39 +01:00
eeepc - > inputdev = input ;
2009-08-28 12:56:36 +00:00
return 0 ;
2010-01-06 22:07:39 +01:00
err_free_keymap :
sparse_keymap_free ( input ) ;
err_free_dev :
input_free_device ( input ) ;
return error ;
2009-08-28 12:56:36 +00:00
}
2009-12-03 07:45:09 +00:00
static void eeepc_input_exit ( struct eeepc_laptop * eeepc )
2008-03-13 12:55:46 +01:00
{
2009-12-03 07:45:09 +00:00
if ( eeepc - > inputdev ) {
2009-12-03 07:45:08 +00:00
input_unregister_device ( eeepc - > inputdev ) ;
2009-12-03 07:45:09 +00:00
kfree ( eeepc - > keymap ) ;
}
2009-12-03 07:45:07 +00:00
}
2009-12-03 07:44:52 +00:00
2009-12-03 07:45:07 +00:00
/*
* ACPI driver
*/
2009-12-03 07:45:08 +00:00
static void eeepc_acpi_notify ( struct acpi_device * device , u32 event )
2009-12-03 07:45:07 +00:00
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = acpi_driver_data ( device ) ;
2009-12-03 07:45:07 +00:00
u16 count ;
2009-12-03 07:44:52 +00:00
2009-12-03 07:45:07 +00:00
if ( event > ACPI_MAX_SYS_NOTIFY )
return ;
2009-12-03 07:45:08 +00:00
count = eeepc - > event_count [ event % 128 ] + + ;
2009-12-03 07:45:09 +00:00
acpi_bus_generate_proc_event ( device , event , count ) ;
acpi_bus_generate_netlink_event ( device - > pnp . device_class ,
dev_name ( & device - > dev ) , event ,
2009-12-03 07:45:07 +00:00
count ) ;
2009-12-03 07:44:52 +00:00
2009-12-03 07:45:15 +00:00
/* Brightness events are special */
2009-12-03 07:45:07 +00:00
if ( event > = NOTIFY_BRN_MIN & & event < = NOTIFY_BRN_MAX ) {
2009-12-03 07:45:15 +00:00
/* Ignore them completely if the acpi video driver is used */
if ( eeepc - > backlight_device ! = NULL ) {
int old_brightness , new_brightness ;
/* Update the backlight device. */
old_brightness = eeepc_backlight_notify ( eeepc ) ;
/* Convert event to keypress (obsolescent hack) */
new_brightness = event - NOTIFY_BRN_MIN ;
if ( new_brightness < old_brightness ) {
event = NOTIFY_BRN_MIN ; /* brightness down */
} else if ( new_brightness > old_brightness ) {
event = NOTIFY_BRN_MAX ; /* brightness up */
} else {
/*
* no change in brightness - already at min / max ,
* event will be desired value ( or else ignored )
*/
}
2010-01-06 22:07:39 +01:00
sparse_keymap_report_event ( eeepc - > inputdev , event ,
1 , true ) ;
2009-12-03 07:45:07 +00:00
}
2009-12-03 07:45:15 +00:00
} else {
/* Everything else is a bona-fide keypress event */
2010-01-06 22:07:39 +01:00
sparse_keymap_report_event ( eeepc - > inputdev , event , 1 , true ) ;
2009-12-03 07:45:07 +00:00
}
}
2010-01-06 22:07:37 +01:00
static void eeepc_dmi_check ( struct eeepc_laptop * eeepc )
{
const char * model ;
2010-01-06 22:07:38 +01:00
model = dmi_get_system_info ( DMI_PRODUCT_NAME ) ;
if ( ! model )
return ;
2010-01-06 22:07:37 +01:00
/*
* Blacklist for setting cpufv ( cpu speed ) .
*
* EeePC 4 G ( " 701 " ) implements CFVS , but it is not supported
* by the pre - installed OS , and the original option to change it
* in the BIOS setup screen was removed in later versions .
*
* Judging by the lack of " Super Hybrid Engine " on Asus product pages ,
* this applies to all " 701 " models ( 4 G / 4 G Surf / 2 G Surf ) .
*
* So Asus made a deliberate decision not to support it on this model .
* We have several reports that using it can cause the system to hang
*
* The hang has also been reported on a " 702 " ( Model name " 8G " ? ) .
*
* We avoid dmi_check_system ( ) / dmi_match ( ) , because they use
* substring matching . We don ' t want to affect the " 701SD "
* and " 701SDX " models , because they do support S . H . E .
*/
if ( strcmp ( model , " 701 " ) = = 0 | | strcmp ( model , " 702 " ) = = 0 ) {
eeepc - > cpufv_disabled = true ;
pr_info ( " model %s does not officially support setting cpu "
" speed \n " , model ) ;
pr_info ( " cpufv disabled to avoid instability \n " ) ;
}
2010-01-06 22:07:38 +01:00
/*
* Blacklist for wlan hotplug
*
* Eeepc 1005 HA doesn ' t work like others models and don ' t need the
* hotplug code . In fact , current hotplug code seems to unplug another
* device . . .
*/
2010-02-20 11:02:24 +00:00
if ( strcmp ( model , " 1005HA " ) = = 0 | | strcmp ( model , " 1201N " ) = = 0 | |
strcmp ( model , " 1005PE " ) = = 0 ) {
2010-01-06 22:07:38 +01:00
eeepc - > hotplug_disabled = true ;
pr_info ( " wlan hotplug disabled \n " ) ;
}
2010-01-06 22:07:37 +01:00
}
2009-12-03 07:45:09 +00:00
static void cmsg_quirk ( struct eeepc_laptop * eeepc , int cm , const char * name )
2009-12-03 07:45:07 +00:00
{
int dummy ;
/* Some BIOSes do not report cm although it is avaliable.
Check if cm_getv [ cm ] works and , if yes , assume cm should be set . */
2009-12-03 07:45:08 +00:00
if ( ! ( eeepc - > cm_supported & ( 1 < < cm ) )
& & ! read_acpi_int ( eeepc - > handle , cm_getv [ cm ] , & dummy ) ) {
2009-12-03 07:45:07 +00:00
pr_info ( " %s (%x) not reported by BIOS, "
" enabling anyway \n " , name , 1 < < cm ) ;
2009-12-03 07:45:08 +00:00
eeepc - > cm_supported | = 1 < < cm ;
2009-12-03 07:45:07 +00:00
}
}
2009-12-03 07:45:09 +00:00
static void cmsg_quirks ( struct eeepc_laptop * eeepc )
2009-12-03 07:45:07 +00:00
{
2009-12-03 07:45:09 +00:00
cmsg_quirk ( eeepc , CM_ASL_LID , " LID " ) ;
cmsg_quirk ( eeepc , CM_ASL_TYPE , " TYPE " ) ;
cmsg_quirk ( eeepc , CM_ASL_PANELPOWER , " PANELPOWER " ) ;
cmsg_quirk ( eeepc , CM_ASL_TPD , " TPD " ) ;
2009-12-03 07:45:07 +00:00
}
2009-12-03 07:45:14 +00:00
static int eeepc_acpi_init ( struct eeepc_laptop * eeepc ,
struct acpi_device * device )
2009-12-03 07:45:07 +00:00
{
unsigned int init_flags ;
2008-03-13 12:55:46 +01:00
int result ;
2009-12-03 07:45:09 +00:00
result = acpi_bus_get_status ( device ) ;
2009-08-28 12:56:35 +00:00
if ( result )
2009-12-03 07:45:07 +00:00
return result ;
2009-12-03 07:45:09 +00:00
if ( ! device - > status . present ) {
2009-12-03 07:45:07 +00:00
pr_err ( " Hotkey device not present, aborting \n " ) ;
return - ENODEV ;
}
2009-06-16 19:28:45 +00:00
2009-12-03 07:45:07 +00:00
init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH ;
pr_notice ( " Hotkey init flags 0x%x \n " , init_flags ) ;
2009-12-03 07:45:08 +00:00
if ( write_acpi_int ( eeepc - > handle , " INIT " , init_flags ) ) {
2009-12-03 07:45:07 +00:00
pr_err ( " Hotkey initialization failed \n " ) ;
return - ENODEV ;
2008-03-13 12:55:46 +01:00
}
2009-12-03 07:45:07 +00:00
/* get control methods supported */
2009-12-03 07:45:08 +00:00
if ( read_acpi_int ( eeepc - > handle , " CMSG " , & eeepc - > cm_supported ) ) {
2009-12-03 07:45:07 +00:00
pr_err ( " Get control methods supported failed \n " ) ;
return - ENODEV ;
2009-12-03 07:44:58 +00:00
}
2009-12-03 07:45:09 +00:00
cmsg_quirks ( eeepc ) ;
2009-12-03 07:45:08 +00:00
pr_info ( " Get control methods supported: 0x%x \n " , eeepc - > cm_supported ) ;
2009-12-03 07:44:57 +00:00
2009-12-03 07:44:52 +00:00
return 0 ;
}
2009-12-03 07:45:09 +00:00
static void __devinit eeepc_enable_camera ( struct eeepc_laptop * eeepc )
2009-12-03 07:45:07 +00:00
{
/*
* If the following call to set_acpi ( ) fails , it ' s because there ' s no
* camera so we can ignore the error .
*/
2009-12-03 07:45:09 +00:00
if ( get_acpi ( eeepc , CM_ASL_CAMERA ) = = 0 )
set_acpi ( eeepc , CM_ASL_CAMERA , 1 ) ;
2009-12-03 07:45:07 +00:00
}
2009-12-03 07:45:09 +00:00
static bool eeepc_device_present ;
2009-12-03 07:45:08 +00:00
static int __devinit eeepc_acpi_add ( struct acpi_device * device )
2008-03-13 12:55:46 +01:00
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc ;
2008-03-13 12:55:46 +01:00
int result ;
2009-12-03 07:45:08 +00:00
pr_notice ( EEEPC_LAPTOP_NAME " \n " ) ;
eeepc = kzalloc ( sizeof ( struct eeepc_laptop ) , GFP_KERNEL ) ;
if ( ! eeepc )
2009-08-28 12:56:35 +00:00
return - ENOMEM ;
2009-12-03 07:45:08 +00:00
eeepc - > handle = device - > handle ;
strcpy ( acpi_device_name ( device ) , EEEPC_ACPI_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , EEEPC_ACPI_CLASS ) ;
device - > driver_data = eeepc ;
2009-06-16 19:28:45 +00:00
2010-01-06 22:07:40 +01:00
eeepc - > hotplug_disabled = hotplug_disabled ;
2010-01-06 22:07:37 +01:00
eeepc_dmi_check ( eeepc ) ;
2009-12-03 07:45:09 +00:00
result = eeepc_acpi_init ( eeepc , device ) ;
2008-03-13 12:55:46 +01:00
if ( result )
2009-12-03 07:45:06 +00:00
goto fail_platform ;
2009-12-03 07:45:09 +00:00
eeepc_enable_camera ( eeepc ) ;
2009-06-25 13:25:38 +02:00
2009-12-03 07:45:09 +00:00
/*
* Register the platform device first . It is used as a parent for the
* sub - devices below .
*
* Note that if there are multiple instances of this ACPI device it
* will bail out , because the platform device is registered with a
* fixed name . Of course it doesn ' t make sense to have more than one ,
* and machine - specific scripts find the fixed name convenient . But
* It ' s also good for us to exclude multiple instances because both
* our hwmon and our wlan rfkill subdevice use global ACPI objects
* ( the EC and the wlan PCI slot respectively ) .
*/
result = eeepc_platform_init ( eeepc ) ;
2008-03-13 12:55:46 +01:00
if ( result )
2009-12-03 07:45:06 +00:00
goto fail_platform ;
2009-06-25 13:25:39 +02:00
if ( ! acpi_video_backlight_support ( ) ) {
2009-12-03 07:45:09 +00:00
result = eeepc_backlight_init ( eeepc ) ;
2009-06-25 13:25:39 +02:00
if ( result )
goto fail_backlight ;
} else
2009-12-03 07:45:06 +00:00
pr_info ( " Backlight controlled by ACPI video driver \n " ) ;
2009-06-25 13:25:39 +02:00
2009-12-03 07:45:09 +00:00
result = eeepc_input_init ( eeepc ) ;
2009-08-28 12:56:36 +00:00
if ( result )
goto fail_input ;
2009-12-03 07:45:09 +00:00
result = eeepc_hwmon_init ( eeepc ) ;
2009-06-25 13:25:39 +02:00
if ( result )
goto fail_hwmon ;
2009-12-03 07:45:09 +00:00
result = eeepc_led_init ( eeepc ) ;
2009-12-03 07:44:52 +00:00
if ( result )
goto fail_led ;
2009-12-03 07:45:09 +00:00
result = eeepc_rfkill_init ( eeepc ) ;
2009-06-25 13:25:38 +02:00
if ( result )
goto fail_rfkill ;
2009-12-03 07:45:09 +00:00
eeepc_device_present = true ;
2008-03-13 12:55:46 +01:00
return 0 ;
2009-08-28 12:56:35 +00:00
2009-06-25 13:25:38 +02:00
fail_rfkill :
2009-12-03 07:45:09 +00:00
eeepc_led_exit ( eeepc ) ;
2009-12-03 07:44:52 +00:00
fail_led :
2009-12-03 07:45:09 +00:00
eeepc_hwmon_exit ( eeepc ) ;
2009-06-25 13:25:39 +02:00
fail_hwmon :
2009-12-03 07:45:09 +00:00
eeepc_input_exit ( eeepc ) ;
2009-08-28 12:56:36 +00:00
fail_input :
2009-12-03 07:45:09 +00:00
eeepc_backlight_exit ( eeepc ) ;
2009-06-25 13:25:39 +02:00
fail_backlight :
2009-12-03 07:45:09 +00:00
eeepc_platform_exit ( eeepc ) ;
2009-12-03 07:45:06 +00:00
fail_platform :
2009-12-03 07:45:08 +00:00
kfree ( eeepc ) ;
2009-08-28 12:56:35 +00:00
2008-03-13 12:55:46 +01:00
return result ;
}
2009-12-03 07:45:08 +00:00
static int eeepc_acpi_remove ( struct acpi_device * device , int type )
2009-08-28 12:56:35 +00:00
{
2009-12-03 07:45:09 +00:00
struct eeepc_laptop * eeepc = acpi_driver_data ( device ) ;
2009-08-28 12:56:35 +00:00
2009-12-03 07:45:09 +00:00
eeepc_backlight_exit ( eeepc ) ;
eeepc_rfkill_exit ( eeepc ) ;
eeepc_input_exit ( eeepc ) ;
eeepc_hwmon_exit ( eeepc ) ;
eeepc_led_exit ( eeepc ) ;
eeepc_platform_exit ( eeepc ) ;
2009-08-28 12:56:35 +00:00
2009-12-03 07:45:08 +00:00
kfree ( eeepc ) ;
2009-08-28 12:56:35 +00:00
return 0 ;
}
2009-12-03 07:45:07 +00:00
static const struct acpi_device_id eeepc_device_ids [ ] = {
2009-12-03 07:45:08 +00:00
{ EEEPC_ACPI_HID , 0 } ,
2009-12-03 07:45:07 +00:00
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , eeepc_device_ids ) ;
2009-12-03 07:45:08 +00:00
static struct acpi_driver eeepc_acpi_driver = {
. name = EEEPC_LAPTOP_NAME ,
. class = EEEPC_ACPI_CLASS ,
2009-12-03 07:45:07 +00:00
. owner = THIS_MODULE ,
. ids = eeepc_device_ids ,
. flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS ,
. ops = {
2009-12-03 07:45:08 +00:00
. add = eeepc_acpi_add ,
. remove = eeepc_acpi_remove ,
. notify = eeepc_acpi_notify ,
2009-12-03 07:45:07 +00:00
} ,
} ;
2009-08-28 12:56:35 +00:00
static int __init eeepc_laptop_init ( void )
{
int result ;
2009-12-03 07:45:05 +00:00
result = platform_driver_register ( & platform_driver ) ;
2009-08-28 12:56:35 +00:00
if ( result < 0 )
return result ;
2009-12-03 07:45:05 +00:00
2009-12-03 07:45:08 +00:00
result = acpi_bus_register_driver ( & eeepc_acpi_driver ) ;
2009-12-03 07:45:05 +00:00
if ( result < 0 )
goto fail_acpi_driver ;
2010-01-06 22:07:39 +01:00
2009-12-03 07:45:09 +00:00
if ( ! eeepc_device_present ) {
2009-12-03 07:45:05 +00:00
result = - ENODEV ;
goto fail_no_device ;
2009-08-28 12:56:35 +00:00
}
2010-01-06 22:07:39 +01:00
2009-08-28 12:56:35 +00:00
return 0 ;
2009-12-03 07:45:05 +00:00
fail_no_device :
2009-12-03 07:45:08 +00:00
acpi_bus_unregister_driver ( & eeepc_acpi_driver ) ;
2009-12-03 07:45:05 +00:00
fail_acpi_driver :
platform_driver_unregister ( & platform_driver ) ;
return result ;
2009-08-28 12:56:35 +00:00
}
static void __exit eeepc_laptop_exit ( void )
{
2009-12-03 07:45:08 +00:00
acpi_bus_unregister_driver ( & eeepc_acpi_driver ) ;
2009-12-03 07:45:05 +00:00
platform_driver_unregister ( & platform_driver ) ;
2009-08-28 12:56:35 +00:00
}
2008-03-13 12:55:46 +01:00
module_init ( eeepc_laptop_init ) ;
module_exit ( eeepc_laptop_exit ) ;