Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
79491ca4
Commit
79491ca4
authored
Jan 16, 2009
by
Len Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'thinkpad-acpi' into release
parents
f1f055f1
aa2fbcec
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
791 additions
and
155 deletions
+791
-155
Documentation/laptops/thinkpad-acpi.txt
Documentation/laptops/thinkpad-acpi.txt
+22
-3
drivers/platform/x86/Kconfig
drivers/platform/x86/Kconfig
+11
-0
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/thinkpad_acpi.c
+758
-152
No files found.
Documentation/laptops/thinkpad-acpi.txt
View file @
79491ca4
ThinkPad ACPI Extras Driver
Version 0.2
1
May 29th,
2008
Version 0.2
2
November 23rd,
2008
Borislav Deianov <borislav@users.sf.net>
Henrique de Moraes Holschuh <hmh@hmh.eng.br>
...
...
@@ -16,7 +16,8 @@ supported by the generic Linux ACPI drivers.
This driver used to be named ibm-acpi until kernel 2.6.21 and release
0.13-20070314. It used to be in the drivers/acpi tree, but it was
moved to the drivers/misc tree and renamed to thinkpad-acpi for kernel
2.6.22, and release 0.14.
2.6.22, and release 0.14. It was moved to drivers/platform/x86 for
kernel 2.6.29 and release 0.22.
The driver is named "thinkpad-acpi". In some places, like module
names, "thinkpad_acpi" is used because of userspace issues.
...
...
@@ -1412,6 +1413,24 @@ Sysfs notes:
rfkill controller switch "tpacpi_wwan_sw": refer to
Documentation/rfkill.txt for details.
EXPERIMENTAL: UWB
-----------------
This feature is marked EXPERIMENTAL because it has not been extensively
tested and validated in various ThinkPad models yet. The feature may not
work as expected. USE WITH CAUTION! To use this feature, you need to supply
the experimental=1 parameter when loading the module.
sysfs rfkill class: switch "tpacpi_uwb_sw"
This feature exports an rfkill controller for the UWB device, if one is
present and enabled in the BIOS.
Sysfs notes:
rfkill controller switch "tpacpi_uwb_sw": refer to
Documentation/rfkill.txt for details.
Multiple Commands, Module Parameters
------------------------------------
...
...
drivers/platform/x86/Kconfig
View file @
79491ca4
...
...
@@ -192,6 +192,17 @@ config THINKPAD_ACPI
If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
config THINKPAD_ACPI_DEBUGFACILITIES
bool "Maintainer debug facilities"
depends on THINKPAD_ACPI
default n
---help---
Enables extra stuff in the thinkpad-acpi which is completely useless
for normal use. Read the driver source to find out what it does.
Say N here, unless you were told by a kernel maintainer to do
otherwise.
config THINKPAD_ACPI_DEBUG
bool "Verbose debug mode"
depends on THINKPAD_ACPI
...
...
drivers/platform/x86/thinkpad_acpi.c
View file @
79491ca4
...
...
@@ -21,7 +21,7 @@
* 02110-1301, USA.
*/
#define TPACPI_VERSION "0.2
1
"
#define TPACPI_VERSION "0.2
2
"
#define TPACPI_SYSFS_VERSION 0x020200
/*
...
...
@@ -122,6 +122,27 @@ enum {
#define TPACPI_HKEY_INPUT_PRODUCT 0x5054
/* "TP" */
#define TPACPI_HKEY_INPUT_VERSION 0x4101
/* ACPI \WGSV commands */
enum
{
TP_ACPI_WGSV_GET_STATE
=
0x01
,
/* Get state information */
TP_ACPI_WGSV_PWR_ON_ON_RESUME
=
0x02
,
/* Resume WWAN powered on */
TP_ACPI_WGSV_PWR_OFF_ON_RESUME
=
0x03
,
/* Resume WWAN powered off */
TP_ACPI_WGSV_SAVE_STATE
=
0x04
,
/* Save state for S4/S5 */
};
/* TP_ACPI_WGSV_GET_STATE bits */
enum
{
TP_ACPI_WGSV_STATE_WWANEXIST
=
0x0001
,
/* WWAN hw available */
TP_ACPI_WGSV_STATE_WWANPWR
=
0x0002
,
/* WWAN radio enabled */
TP_ACPI_WGSV_STATE_WWANPWRRES
=
0x0004
,
/* WWAN state at resume */
TP_ACPI_WGSV_STATE_WWANBIOSOFF
=
0x0008
,
/* WWAN disabled in BIOS */
TP_ACPI_WGSV_STATE_BLTHEXIST
=
0x0001
,
/* BLTH hw available */
TP_ACPI_WGSV_STATE_BLTHPWR
=
0x0002
,
/* BLTH radio enabled */
TP_ACPI_WGSV_STATE_BLTHPWRRES
=
0x0004
,
/* BLTH state at resume */
TP_ACPI_WGSV_STATE_BLTHBIOSOFF
=
0x0008
,
/* BLTH disabled in BIOS */
TP_ACPI_WGSV_STATE_UWBEXIST
=
0x0010
,
/* UWB hw available */
TP_ACPI_WGSV_STATE_UWBPWR
=
0x0020
,
/* UWB radio enabled */
};
/****************************************************************************
* Main driver
...
...
@@ -148,10 +169,13 @@ enum {
enum
{
TPACPI_RFK_BLUETOOTH_SW_ID
=
0
,
TPACPI_RFK_WWAN_SW_ID
,
TPACPI_RFK_UWB_SW_ID
,
};
/* Debugging */
#define TPACPI_LOG TPACPI_FILE ": "
#define TPACPI_ALERT KERN_ALERT TPACPI_LOG
#define TPACPI_CRIT KERN_CRIT TPACPI_LOG
#define TPACPI_ERR KERN_ERR TPACPI_LOG
#define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG
#define TPACPI_INFO KERN_INFO TPACPI_LOG
...
...
@@ -201,6 +225,7 @@ struct ibm_struct {
void
(
*
exit
)
(
void
);
void
(
*
resume
)
(
void
);
void
(
*
suspend
)
(
pm_message_t
state
);
void
(
*
shutdown
)
(
void
);
struct
list_head
all_drivers
;
...
...
@@ -239,6 +264,7 @@ static struct {
u32
bright_16levels
:
1
;
u32
bright_acpimode
:
1
;
u32
wan
:
1
;
u32
uwb
:
1
;
u32
fan_ctrl_status_undef
:
1
;
u32
input_device_registered
:
1
;
u32
platform_drv_registered
:
1
;
...
...
@@ -288,6 +314,18 @@ struct tpacpi_led_classdev {
unsigned
int
led
;
};
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
static
int
dbg_wlswemul
;
static
int
tpacpi_wlsw_emulstate
;
static
int
dbg_bluetoothemul
;
static
int
tpacpi_bluetooth_emulstate
;
static
int
dbg_wwanemul
;
static
int
tpacpi_wwan_emulstate
;
static
int
dbg_uwbemul
;
static
int
tpacpi_uwb_emulstate
;
#endif
/****************************************************************************
****************************************************************************
*
...
...
@@ -728,6 +766,18 @@ static int tpacpi_resume_handler(struct platform_device *pdev)
return
0
;
}
static
void
tpacpi_shutdown_handler
(
struct
platform_device
*
pdev
)
{
struct
ibm_struct
*
ibm
,
*
itmp
;
list_for_each_entry_safe
(
ibm
,
itmp
,
&
tpacpi_all_drivers
,
all_drivers
)
{
if
(
ibm
->
shutdown
)
(
ibm
->
shutdown
)();
}
}
static
struct
platform_driver
tpacpi_pdriver
=
{
.
driver
=
{
.
name
=
TPACPI_DRVR_NAME
,
...
...
@@ -735,6 +785,7 @@ static struct platform_driver tpacpi_pdriver = {
},
.
suspend
=
tpacpi_suspend_handler
,
.
resume
=
tpacpi_resume_handler
,
.
shutdown
=
tpacpi_shutdown_handler
,
};
static
struct
platform_driver
tpacpi_hwmon_pdriver
=
{
...
...
@@ -922,11 +973,27 @@ static int __init tpacpi_new_rfkill(const unsigned int id,
struct
rfkill
**
rfk
,
const
enum
rfkill_type
rfktype
,
const
char
*
name
,
const
bool
set_default
,
int
(
*
toggle_radio
)(
void
*
,
enum
rfkill_state
),
int
(
*
get_state
)(
void
*
,
enum
rfkill_state
*
))
{
int
res
;
enum
rfkill_state
initial_state
;
enum
rfkill_state
initial_state
=
RFKILL_STATE_SOFT_BLOCKED
;
res
=
get_state
(
NULL
,
&
initial_state
);
if
(
res
<
0
)
{
printk
(
TPACPI_ERR
"failed to read initial state for %s, error %d; "
"will turn radio off
\n
"
,
name
,
res
);
}
else
if
(
set_default
)
{
/* try to set the initial state as the default for the rfkill
* type, since we ask the firmware to preserve it across S5 in
* NVRAM */
rfkill_set_default
(
rfktype
,
(
initial_state
==
RFKILL_STATE_UNBLOCKED
)
?
RFKILL_STATE_UNBLOCKED
:
RFKILL_STATE_SOFT_BLOCKED
);
}
*
rfk
=
rfkill_allocate
(
&
tpacpi_pdev
->
dev
,
rfktype
);
if
(
!*
rfk
)
{
...
...
@@ -938,8 +1005,6 @@ static int __init tpacpi_new_rfkill(const unsigned int id,
(
*
rfk
)
->
name
=
name
;
(
*
rfk
)
->
get_state
=
get_state
;
(
*
rfk
)
->
toggle_radio
=
toggle_radio
;
if
(
!
get_state
(
NULL
,
&
initial_state
))
(
*
rfk
)
->
state
=
initial_state
;
res
=
rfkill_register
(
*
rfk
);
...
...
@@ -1006,6 +1071,119 @@ static DRIVER_ATTR(version, S_IRUGO,
/* --------------------------------------------------------------------- */
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
static
void
tpacpi_send_radiosw_update
(
void
);
/* wlsw_emulstate ------------------------------------------------------ */
static
ssize_t
tpacpi_driver_wlsw_emulstate_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
!!
tpacpi_wlsw_emulstate
);
}
static
ssize_t
tpacpi_driver_wlsw_emulstate_store
(
struct
device_driver
*
drv
,
const
char
*
buf
,
size_t
count
)
{
unsigned
long
t
;
if
(
parse_strtoul
(
buf
,
1
,
&
t
))
return
-
EINVAL
;
if
(
tpacpi_wlsw_emulstate
!=
t
)
{
tpacpi_wlsw_emulstate
=
!!
t
;
tpacpi_send_radiosw_update
();
}
else
tpacpi_wlsw_emulstate
=
!!
t
;
return
count
;
}
static
DRIVER_ATTR
(
wlsw_emulstate
,
S_IWUSR
|
S_IRUGO
,
tpacpi_driver_wlsw_emulstate_show
,
tpacpi_driver_wlsw_emulstate_store
);
/* bluetooth_emulstate ------------------------------------------------- */
static
ssize_t
tpacpi_driver_bluetooth_emulstate_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
!!
tpacpi_bluetooth_emulstate
);
}
static
ssize_t
tpacpi_driver_bluetooth_emulstate_store
(
struct
device_driver
*
drv
,
const
char
*
buf
,
size_t
count
)
{
unsigned
long
t
;
if
(
parse_strtoul
(
buf
,
1
,
&
t
))
return
-
EINVAL
;
tpacpi_bluetooth_emulstate
=
!!
t
;
return
count
;
}
static
DRIVER_ATTR
(
bluetooth_emulstate
,
S_IWUSR
|
S_IRUGO
,
tpacpi_driver_bluetooth_emulstate_show
,
tpacpi_driver_bluetooth_emulstate_store
);
/* wwan_emulstate ------------------------------------------------- */
static
ssize_t
tpacpi_driver_wwan_emulstate_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
!!
tpacpi_wwan_emulstate
);
}
static
ssize_t
tpacpi_driver_wwan_emulstate_store
(
struct
device_driver
*
drv
,
const
char
*
buf
,
size_t
count
)
{
unsigned
long
t
;
if
(
parse_strtoul
(
buf
,
1
,
&
t
))
return
-
EINVAL
;
tpacpi_wwan_emulstate
=
!!
t
;
return
count
;
}
static
DRIVER_ATTR
(
wwan_emulstate
,
S_IWUSR
|
S_IRUGO
,
tpacpi_driver_wwan_emulstate_show
,
tpacpi_driver_wwan_emulstate_store
);
/* uwb_emulstate ------------------------------------------------- */
static
ssize_t
tpacpi_driver_uwb_emulstate_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
!!
tpacpi_uwb_emulstate
);
}
static
ssize_t
tpacpi_driver_uwb_emulstate_store
(
struct
device_driver
*
drv
,
const
char
*
buf
,
size_t
count
)
{
unsigned
long
t
;
if
(
parse_strtoul
(
buf
,
1
,
&
t
))
return
-
EINVAL
;
tpacpi_uwb_emulstate
=
!!
t
;
return
count
;
}
static
DRIVER_ATTR
(
uwb_emulstate
,
S_IWUSR
|
S_IRUGO
,
tpacpi_driver_uwb_emulstate_show
,
tpacpi_driver_uwb_emulstate_store
);
#endif
/* --------------------------------------------------------------------- */
static
struct
driver_attribute
*
tpacpi_driver_attributes
[]
=
{
&
driver_attr_debug_level
,
&
driver_attr_version
,
&
driver_attr_interface_version
,
...
...
@@ -1022,6 +1200,17 @@ static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
i
++
;
}
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
!
res
&&
dbg_wlswemul
)
res
=
driver_create_file
(
drv
,
&
driver_attr_wlsw_emulstate
);
if
(
!
res
&&
dbg_bluetoothemul
)
res
=
driver_create_file
(
drv
,
&
driver_attr_bluetooth_emulstate
);
if
(
!
res
&&
dbg_wwanemul
)
res
=
driver_create_file
(
drv
,
&
driver_attr_wwan_emulstate
);
if
(
!
res
&&
dbg_uwbemul
)
res
=
driver_create_file
(
drv
,
&
driver_attr_uwb_emulstate
);
#endif
return
res
;
}
...
...
@@ -1031,6 +1220,13 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv)
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
tpacpi_driver_attributes
);
i
++
)
driver_remove_file
(
drv
,
tpacpi_driver_attributes
[
i
]);
#ifdef THINKPAD_ACPI_DEBUGFACILITIES
driver_remove_file
(
drv
,
&
driver_attr_wlsw_emulstate
);
driver_remove_file
(
drv
,
&
driver_attr_bluetooth_emulstate
);
driver_remove_file
(
drv
,
&
driver_attr_wwan_emulstate
);
driver_remove_file
(
drv
,
&
driver_attr_uwb_emulstate
);
#endif
}
/****************************************************************************
...
...
@@ -1216,6 +1412,12 @@ static struct attribute_set *hotkey_dev_attributes;
static
int
hotkey_get_wlsw
(
int
*
status
)
{
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
dbg_wlswemul
)
{
*
status
=
!!
tpacpi_wlsw_emulstate
;
return
0
;
}
#endif
if
(
!
acpi_evalf
(
hkey_handle
,
status
,
"WLSW"
,
"d"
))
return
-
EIO
;
return
0
;
...
...
@@ -1678,7 +1880,7 @@ static ssize_t hotkey_mask_show(struct device *dev,
{
int
res
;
if
(
mutex_lock_
interrupti
ble
(
&
hotkey_mutex
))
if
(
mutex_lock_
killa
ble
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
res
=
hotkey_mask_get
();
mutex_unlock
(
&
hotkey_mutex
);
...
...
@@ -1697,7 +1899,7 @@ static ssize_t hotkey_mask_store(struct device *dev,
if
(
parse_strtoul
(
buf
,
0xffffffffUL
,
&
t
))
return
-
EINVAL
;
if
(
mutex_lock_
interrupti
ble
(
&
hotkey_mutex
))
if
(
mutex_lock_
killa
ble
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
res
=
hotkey_mask_set
(
t
);
...
...
@@ -1783,7 +1985,7 @@ static ssize_t hotkey_source_mask_store(struct device *dev,
((
t
&
~
TPACPI_HKEY_NVRAM_KNOWN_MASK
)
!=
0
))
return
-
EINVAL
;
if
(
mutex_lock_
interrupti
ble
(
&
hotkey_mutex
))
if
(
mutex_lock_
killa
ble
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
HOTKEY_CONFIG_CRITICAL_START
...
...
@@ -1818,7 +2020,7 @@ static ssize_t hotkey_poll_freq_store(struct device *dev,
if
(
parse_strtoul
(
buf
,
25
,
&
t
))
return
-
EINVAL
;
if
(
mutex_lock_
interrupti
ble
(
&
hotkey_mutex
))
if
(
mutex_lock_
killa
ble
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
hotkey_poll_freq
=
t
;
...
...
@@ -1958,6 +2160,7 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
static
void
bluetooth_update_rfk
(
void
);
static
void
wan_update_rfk
(
void
);
static
void
uwb_update_rfk
(
void
);
static
void
tpacpi_send_radiosw_update
(
void
)
{
int
wlsw
;
...
...
@@ -1967,6 +2170,8 @@ static void tpacpi_send_radiosw_update(void)
bluetooth_update_rfk
();
if
(
tp_features
.
wan
)
wan_update_rfk
();
if
(
tp_features
.
uwb
)
uwb_update_rfk
();
if
(
tp_features
.
hotkey_wlsw
&&
!
hotkey_get_wlsw
(
&
wlsw
))
{
mutex_lock
(
&
tpacpi_inputdev_send_mutex
);
...
...
@@ -2222,6 +2427,13 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
hotkey_source_mask
,
hotkey_poll_freq
);
#endif
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
dbg_wlswemul
)
{
tp_features
.
hotkey_wlsw
=
1
;
printk
(
TPACPI_INFO
"radio switch emulation enabled
\n
"
);
}
else
#endif
/* Not all thinkpads have a hardware radio switch */
if
(
acpi_evalf
(
hkey_handle
,
&
status
,
"WLSW"
,
"qd"
))
{
tp_features
.
hotkey_wlsw
=
1
;
...
...
@@ -2361,13 +2573,154 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
return
(
res
<
0
)
?
res
:
1
;
}
static
bool
hotkey_notify_hotkey
(
const
u32
hkey
,
bool
*
send_acpi_ev
,
bool
*
ignore_acpi_ev
)
{
/* 0x1000-0x1FFF: key presses */
unsigned
int
scancode
=
hkey
&
0xfff
;
*
send_acpi_ev
=
true
;
*
ignore_acpi_ev
=
false
;
if
(
scancode
>
0
&&
scancode
<
0x21
)
{
scancode
--
;
if
(
!
(
hotkey_source_mask
&
(
1
<<
scancode
)))
{
tpacpi_input_send_key
(
scancode
);
*
send_acpi_ev
=
false
;
}
else
{
*
ignore_acpi_ev
=
true
;
}
return
true
;
}
return
false
;
}
static
bool
hotkey_notify_wakeup
(
const
u32
hkey
,
bool
*
send_acpi_ev
,
bool
*
ignore_acpi_ev
)
{
/* 0x2000-0x2FFF: Wakeup reason */
*
send_acpi_ev
=
true
;
*
ignore_acpi_ev
=
false
;
switch
(
hkey
)
{
case
0x2304
:
/* suspend, undock */
case
0x2404
:
/* hibernation, undock */
hotkey_wakeup_reason
=
TP_ACPI_WAKEUP_UNDOCK
;
*
ignore_acpi_ev
=
true
;
break
;
case
0x2305
:
/* suspend, bay eject */
case
0x2405
:
/* hibernation, bay eject */
hotkey_wakeup_reason
=
TP_ACPI_WAKEUP_BAYEJ
;
*
ignore_acpi_ev
=
true
;
break
;
case
0x2313
:
/* Battery on critical low level (S3) */
case
0x2413
:
/* Battery on critical low level (S4) */
printk
(
TPACPI_ALERT
"EMERGENCY WAKEUP: battery almost empty
\n
"
);
/* how to auto-heal: */
/* 2313: woke up from S3, go to S4/S5 */
/* 2413: woke up from S4, go to S5 */
break
;
default:
return
false
;
}
if
(
hotkey_wakeup_reason
!=
TP_ACPI_WAKEUP_NONE
)
{
printk
(
TPACPI_INFO
"woke up due to a hot-unplug "
"request...
\n
"
);
hotkey_wakeup_reason_notify_change
();
}
return
true
;
}
static
bool
hotkey_notify_usrevent
(
const
u32
hkey
,
bool
*
send_acpi_ev
,
bool
*
ignore_acpi_ev
)
{
/* 0x5000-0x5FFF: human interface helpers */
*
send_acpi_ev
=
true
;
*
ignore_acpi_ev
=
false
;
switch
(
hkey
)
{
case
0x5010
:
/* Lenovo new BIOS: brightness changed */
case
0x500b
:
/* X61t: tablet pen inserted into bay */
case
0x500c
:
/* X61t: tablet pen removed from bay */
return
true
;
case
0x5009
:
/* X41t-X61t: swivel up (tablet mode) */
case
0x500a
:
/* X41t-X61t: swivel down (normal mode) */
tpacpi_input_send_tabletsw
();
hotkey_tablet_mode_notify_change
();
*
send_acpi_ev
=
false
;
return
true
;
case
0x5001
:
case
0x5002
:
/* LID switch events. Do not propagate */
*
ignore_acpi_ev
=
true
;
return
true
;
default:
return
false
;
}
}
static
bool
hotkey_notify_thermal
(
const
u32
hkey
,
bool
*
send_acpi_ev
,
bool
*
ignore_acpi_ev
)
{
/* 0x6000-0x6FFF: thermal alarms */
*
send_acpi_ev
=
true
;
*
ignore_acpi_ev
=
false
;
switch
(
hkey
)
{
case
0x6011
:
printk
(
TPACPI_CRIT
"THERMAL ALARM: battery is too hot!
\n
"
);
/* recommended action: warn user through gui */
return
true
;
case
0x6012
:
printk
(
TPACPI_ALERT
"THERMAL EMERGENCY: battery is extremely hot!
\n
"
);
/* recommended action: immediate sleep/hibernate */
return
true
;
case
0x6021
:
printk
(
TPACPI_CRIT
"THERMAL ALARM: "
"a sensor reports something is too hot!
\n
"
);
/* recommended action: warn user through gui, that */
/* some internal component is too hot */
return
true
;
case
0x6022
:
printk
(
TPACPI_ALERT
"THERMAL EMERGENCY: "
"a sensor reports something is extremely hot!
\n
"
);
/* recommended action: immediate sleep/hibernate */
return
true
;
case
0x6030
:
printk
(
TPACPI_INFO
"EC reports that Thermal Table has changed
\n
"
);
/* recommended action: do nothing, we don't have
* Lenovo ATM information */
return
true
;
default:
printk
(
TPACPI_ALERT
"THERMAL ALERT: unknown thermal alarm received
\n
"
);
return
false
;
}
}
static
void
hotkey_notify
(
struct
ibm_struct
*
ibm
,
u32
event
)
{
u32
hkey
;
unsigned
int
scancode
;
int
send_acpi_ev
;
int
ignore_acpi_ev
;
int
unk_ev
;
bool
send_acpi_ev
;
bool
ignore_acpi_ev
;
bool
known_ev
;
if
(
event
!=
0x80
)
{
printk
(
TPACPI_ERR
...
...
@@ -2375,7 +2728,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
/* forward it to userspace, maybe it knows how to handle it */
acpi_bus_generate_netlink_event
(
ibm
->
acpi
->
device
->
pnp
.
device_class
,
ibm
->
acpi
->
device
->
dev
.
bus_id
,
dev_name
(
&
ibm
->
acpi
->
device
->
dev
)
,
event
,
0
);
return
;
}
...
...
@@ -2391,107 +2744,72 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
return
;
}
send_acpi_ev
=
1
;
ignore_acpi_ev
=
0
;
unk_ev
=
0
;
send_acpi_ev
=
true
;
ignore_acpi_ev
=
false
;
switch
(
hkey
>>
12
)
{
case
1
:
/* 0x1000-0x1FFF: key presses */
scancode
=
hkey
&
0xfff
;
if
(
scancode
>
0
&&
scancode
<
0x21
)
{
scancode
--
;
if
(
!
(
hotkey_source_mask
&
(
1
<<
scancode
)))
{
tpacpi_input_send_key
(
scancode
);
send_acpi_ev
=
0
;
}
else
{
ignore_acpi_ev
=
1
;
}
}
else
{
unk_ev
=
1
;
}
known_ev
=
hotkey_notify_hotkey
(
hkey
,
&
send_acpi_ev
,
&
ignore_acpi_ev
);
break
;
case
2
:
/* Wakeup reason */
switch
(
hkey
)
{
case
0x2304
:
/* suspend, undock */
case
0x2404
:
/* hibernation, undock */
hotkey_wakeup_reason
=
TP_ACPI_WAKEUP_UNDOCK
;
ignore_acpi_ev
=
1
;
break
;
case
0x2305
:
/* suspend, bay eject */
case
0x2405
:
/* hibernation, bay eject */
hotkey_wakeup_reason
=
TP_ACPI_WAKEUP_BAYEJ
;
ignore_acpi_ev
=
1
;
break
;
default:
unk_ev
=
1
;
}
if
(
hotkey_wakeup_reason
!=
TP_ACPI_WAKEUP_NONE
)
{
printk
(
TPACPI_INFO
"woke up due to a hot-unplug "
"request...
\n
"
);
hotkey_wakeup_reason_notify_change
();
}
/* 0x2000-0x2FFF: Wakeup reason */
known_ev
=
hotkey_notify_wakeup
(
hkey
,
&
send_acpi_ev
,
&
ignore_acpi_ev
);
break
;
case
3
:
/* bay-related wakeups */
/*
0x3000-0x3FFF:
bay-related wakeups */
if
(
hkey
==
0x3003
)
{
hotkey_autosleep_ack
=
1
;
printk
(
TPACPI_INFO
"bay ejected
\n
"
);
hotkey_wakeup_hotunplug_complete_notify_change
();
known_ev
=
true
;
}
else
{
unk_ev
=
1
;
known_ev
=
false
;
}
break
;
case
4
:
/* dock-related wakeups */
/*
0x4000-0x4FFF:
dock-related wakeups */
if
(
hkey
==
0x4003
)
{
hotkey_autosleep_ack
=
1
;
printk
(
TPACPI_INFO
"undocked
\n
"
);
hotkey_wakeup_hotunplug_complete_notify_change
();
known_ev
=
true
;
}
else
{
unk_ev
=
1
;
known_ev
=
false
;
}
break
;
case
5
:
/* 0x5000-0x5FFF: human interface helpers */
switch
(
hkey
)
{
case
0x5010
:
/* Lenovo new BIOS: brightness changed */
case
0x500b
:
/* X61t: tablet pen inserted into bay */
case
0x500c
:
/* X61t: tablet pen removed from bay */
known_ev
=
hotkey_notify_usrevent
(
hkey
,
&
send_acpi_ev
,
&
ignore_acpi_ev
);
break
;
case
0x5009
:
/* X41t-X61t: swivel up (tablet mode) */
case
0x500a
:
/* X41t-X61t: swivel down (normal mode) */
tpacpi_input_send_tabletsw
();
hotkey_tablet_mode_notify_change
();
send_acpi_ev
=
0
;
break
;
case
0x5001
:
case
0x5002
:
/* LID switch events. Do not propagate */
ignore_acpi_ev
=
1
;
break
;
default:
unk_ev
=
1
;
}
case
6
:
/* 0x6000-0x6FFF: thermal alarms */
known_ev
=
hotkey_notify_thermal
(
hkey
,
&
send_acpi_ev
,
&
ignore_acpi_ev
);
break
;
case
7
:
/* 0x7000-0x7FFF: misc */
if
(
tp_features
.
hotkey_wlsw
&&
hkey
==
0x7000
)
{
tpacpi_send_radiosw_update
();
send_acpi_ev
=
0
;
known_ev
=
true
;
break
;
}
/* fallthrough to default */
default:
unk_ev
=
1
;
known_ev
=
false
;
}
if
(
unk
_ev
)
{
if
(
!
known
_ev
)
{
printk
(
TPACPI_NOTICE
"unhandled HKEY event 0x%04x
\n
"
,
hkey
);
printk
(
TPACPI_NOTICE
"please report the conditions when this "
"event happened to %s
\n
"
,
TPACPI_MAIL
);
}
/* Legacy events */
...
...
@@ -2505,7 +2823,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
if
(
!
ignore_acpi_ev
&&
send_acpi_ev
)
{
acpi_bus_generate_netlink_event
(
ibm
->
acpi
->
device
->
pnp
.
device_class
,
ibm
->
acpi
->
device
->
dev
.
bus_id
,
dev_name
(
&
ibm
->
acpi
->
device
->
dev
)
,
event
,
hkey
);
}
}
...
...
@@ -2544,7 +2862,7 @@ static int hotkey_read(char *p)
return
len
;
}
if
(
mutex_lock_
interrupti
ble
(
&
hotkey_mutex
))
if
(
mutex_lock_
killa
ble
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
res
=
hotkey_status_get
(
&
status
);
if
(
!
res
)
...
...
@@ -2575,7 +2893,7 @@ static int hotkey_write(char *buf)
if
(
!
tp_features
.
hotkey
)
return
-
ENODEV
;
if
(
mutex_lock_
interrupti
ble
(
&
hotkey_mutex
))
if
(
mutex_lock_
killa
ble
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
status
=
-
1
;
...
...
@@ -2640,11 +2958,28 @@ enum {
/* ACPI GBDC/SBDC bits */
TP_ACPI_BLUETOOTH_HWPRESENT
=
0x01
,
/* Bluetooth hw available */
TP_ACPI_BLUETOOTH_RADIOSSW
=
0x02
,
/* Bluetooth radio enabled */
TP_ACPI_BLUETOOTH_UNK
=
0x04
,
/* unknown function */
TP_ACPI_BLUETOOTH_RESUMECTRL
=
0x04
,
/* Bluetooth state at resume:
off / last state */
};
enum
{
/* ACPI \BLTH commands */
TP_ACPI_BLTH_GET_ULTRAPORT_ID
=
0x00
,
/* Get Ultraport BT ID */
TP_ACPI_BLTH_GET_PWR_ON_RESUME
=
0x01
,
/* Get power-on-resume state */
TP_ACPI_BLTH_PWR_ON_ON_RESUME
=
0x02
,
/* Resume powered on */
TP_ACPI_BLTH_PWR_OFF_ON_RESUME
=
0x03
,
/* Resume powered off */
TP_ACPI_BLTH_SAVE_STATE
=
0x05
,
/* Save state for S4/S5 */
};
static
struct
rfkill
*
tpacpi_bluetooth_rfkill
;
static
void
bluetooth_suspend
(
pm_message_t
state
)
{
/* Try to make sure radio will resume powered off */
acpi_evalf
(
NULL
,
NULL
,
"
\\
BLTH"
,
"vd"
,
TP_ACPI_BLTH_PWR_OFF_ON_RESUME
);
}
static
int
bluetooth_get_radiosw
(
void
)
{
int
status
;
...
...
@@ -2656,6 +2991,12 @@ static int bluetooth_get_radiosw(void)
if
(
tp_features
.
hotkey_wlsw
&&
!
hotkey_get_wlsw
(
&
status
)
&&
!
status
)
return
RFKILL_STATE_HARD_BLOCKED
;
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
dbg_bluetoothemul
)
return
(
tpacpi_bluetooth_emulstate
)
?
RFKILL_STATE_UNBLOCKED
:
RFKILL_STATE_SOFT_BLOCKED
;
#endif
if
(
!
acpi_evalf
(
hkey_handle
,
&
status
,
"GBDC"
,
"d"
))
return
-
EIO
;
...
...
@@ -2689,12 +3030,20 @@ static int bluetooth_set_radiosw(int radio_on, int update_rfk)
&&
radio_on
)
return
-
EPERM
;
if
(
!
acpi_evalf
(
hkey_handle
,
&
status
,
"GBDC"
,
"d"
))
return
-
EIO
;
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
dbg_bluetoothemul
)
{
tpacpi_bluetooth_emulstate
=
!!
radio_on
;
if
(
update_rfk
)
bluetooth_update_rfk
();
return
0
;
}
#endif
/* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */
if
(
radio_on
)
status
|
=
TP_ACPI_BLUETOOTH_RADIOSSW
;
status
=
TP_ACPI_BLUETOOTH_RADIOSSW
;
else
status
&=
~
TP_ACPI_BLUETOOTH_RADIOSSW
;
status
=
0
;
if
(
!
acpi_evalf
(
hkey_handle
,
NULL
,
"SBDC"
,
"vd"
,
status
))
return
-
EIO
;
...
...
@@ -2765,8 +3114,19 @@ static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state)
return
bluetooth_set_radiosw
((
state
==
RFKILL_STATE_UNBLOCKED
),
0
);
}
static
void
bluetooth_shutdown
(
void
)
{
/* Order firmware to save current state to NVRAM */
if
(
!
acpi_evalf
(
NULL
,
NULL
,
"
\\
BLTH"
,
"vd"
,
TP_ACPI_BLTH_SAVE_STATE
))
printk
(
TPACPI_NOTICE
"failed to save bluetooth state to NVRAM
\n
"
);
}
static
void
bluetooth_exit
(
void
)
{
bluetooth_shutdown
();
if
(
tpacpi_bluetooth_rfkill
)
rfkill_unregister
(
tpacpi_bluetooth_rfkill
);
...
...
@@ -2792,6 +3152,13 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
str_supported
(
tp_features
.
bluetooth
),
status
);
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
dbg_bluetoothemul
)
{
tp_features
.
bluetooth
=
1
;
printk
(
TPACPI_INFO
"bluetooth switch emulation enabled
\n
"
);
}
else
#endif
if
(
tp_features
.
bluetooth
&&
!
(
status
&
TP_ACPI_BLUETOOTH_HWPRESENT
))
{
/* no bluetooth hardware present in system */
...
...
@@ -2812,6 +3179,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
&
tpacpi_bluetooth_rfkill
,
RFKILL_TYPE_BLUETOOTH
,
"tpacpi_bluetooth_sw"
,
true
,
tpacpi_bluetooth_rfk_set
,
tpacpi_bluetooth_rfk_get
);
if
(
res
)
{
...
...
@@ -2864,6 +3232,8 @@ static struct ibm_struct bluetooth_driver_data = {
.
read
=
bluetooth_read
,
.
write
=
bluetooth_write
,
.
exit
=
bluetooth_exit
,
.
suspend
=
bluetooth_suspend
,
.
shutdown
=
bluetooth_shutdown
,
};
/*************************************************************************
...
...
@@ -2874,11 +3244,19 @@ enum {
/* ACPI GWAN/SWAN bits */
TP_ACPI_WANCARD_HWPRESENT
=
0x01
,
/* Wan hw available */
TP_ACPI_WANCARD_RADIOSSW
=
0x02
,
/* Wan radio enabled */
TP_ACPI_WANCARD_UNK
=
0x04
,
/* unknown function */
TP_ACPI_WANCARD_RESUMECTRL
=
0x04
,
/* Wan state at resume:
off / last state */
};
static
struct
rfkill
*
tpacpi_wan_rfkill
;
static
void
wan_suspend
(
pm_message_t
state
)
{
/* Try to make sure radio will resume powered off */
acpi_evalf
(
NULL
,
NULL
,
"
\\
WGSV"
,
"qvd"
,
TP_ACPI_WGSV_PWR_OFF_ON_RESUME
);
}
static
int
wan_get_radiosw
(
void
)
{
int
status
;
...
...
@@ -2890,6 +3268,12 @@ static int wan_get_radiosw(void)
if
(
tp_features
.
hotkey_wlsw
&&
!
hotkey_get_wlsw
(
&
status
)
&&
!
status
)
return
RFKILL_STATE_HARD_BLOCKED
;
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
dbg_wwanemul
)
return
(
tpacpi_wwan_emulstate
)
?
RFKILL_STATE_UNBLOCKED
:
RFKILL_STATE_SOFT_BLOCKED
;
#endif
if
(
!
acpi_evalf
(
hkey_handle
,
&
status
,
"GWAN"
,
"d"
))
return
-
EIO
;
...
...
@@ -2923,12 +3307,20 @@ static int wan_set_radiosw(int radio_on, int update_rfk)
&&
radio_on
)
return
-
EPERM
;
if
(
!
acpi_evalf
(
hkey_handle
,
&
status
,
"GWAN"
,
"d"
))
return
-
EIO
;
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
dbg_wwanemul
)
{
tpacpi_wwan_emulstate
=
!!
radio_on
;
if
(
update_rfk
)
wan_update_rfk
();
return
0
;
}
#endif
/* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */
if
(
radio_on
)
status
|
=
TP_ACPI_WANCARD_RADIOSSW
;
status
=
TP_ACPI_WANCARD_RADIOSSW
;
else
status
&=
~
TP_ACPI_WANCARD_RADIOSSW
;
status
=
0
;
if
(
!
acpi_evalf
(
hkey_handle
,
NULL
,
"SWAN"
,
"vd"
,
status
))
return
-
EIO
;
...
...
@@ -2999,8 +3391,19 @@ static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state)
return
wan_set_radiosw
((
state
==
RFKILL_STATE_UNBLOCKED
),
0
);
}
static
void
wan_shutdown
(
void
)
{
/* Order firmware to save current state to NVRAM */
if
(
!
acpi_evalf
(
NULL
,
NULL
,
"
\\
WGSV"
,
"vd"
,
TP_ACPI_WGSV_SAVE_STATE
))
printk
(
TPACPI_NOTICE
"failed to save WWAN state to NVRAM
\n
"
);
}
static
void
wan_exit
(
void
)
{
wan_shutdown
();
if
(
tpacpi_wan_rfkill
)
rfkill_unregister
(
tpacpi_wan_rfkill
);
...
...
@@ -3024,6 +3427,13 @@ static int __init wan_init(struct ibm_init_struct *iibm)
str_supported
(
tp_features
.
wan
),
status
);
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
dbg_wwanemul
)
{
tp_features
.
wan
=
1
;
printk
(
TPACPI_INFO
"wwan switch emulation enabled
\n
"
);
}
else
#endif
if
(
tp_features
.
wan
&&
!
(
status
&
TP_ACPI_WANCARD_HWPRESENT
))
{
/* no wan hardware present in system */
...
...
@@ -3044,6 +3454,7 @@ static int __init wan_init(struct ibm_init_struct *iibm)
&
tpacpi_wan_rfkill
,
RFKILL_TYPE_WWAN
,
"tpacpi_wwan_sw"
,
true
,
tpacpi_wan_rfk_set
,
tpacpi_wan_rfk_get
);
if
(
res
)
{
...
...
@@ -3096,6 +3507,164 @@ static struct ibm_struct wan_driver_data = {
.
read
=
wan_read
,
.
write
=
wan_write
,
.
exit
=
wan_exit
,
.
suspend
=
wan_suspend
,
.
shutdown
=
wan_shutdown
,
};
/*************************************************************************
* UWB subdriver
*/
enum
{
/* ACPI GUWB/SUWB bits */
TP_ACPI_UWB_HWPRESENT
=
0x01
,
/* UWB hw available */
TP_ACPI_UWB_RADIOSSW
=
0x02
,
/* UWB radio enabled */
};
static
struct
rfkill
*
tpacpi_uwb_rfkill
;
static
int
uwb_get_radiosw
(
void
)
{
int
status
;
if
(
!
tp_features
.
uwb
)
return
-
ENODEV
;
/* WLSW overrides UWB in firmware/hardware, reflect that */
if
(
tp_features
.
hotkey_wlsw
&&
!
hotkey_get_wlsw
(
&
status
)
&&
!
status
)
return
RFKILL_STATE_HARD_BLOCKED
;
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
dbg_uwbemul
)
return
(
tpacpi_uwb_emulstate
)
?
RFKILL_STATE_UNBLOCKED
:
RFKILL_STATE_SOFT_BLOCKED
;
#endif
if
(
!
acpi_evalf
(
hkey_handle
,
&
status
,
"GUWB"
,
"d"
))
return
-
EIO
;
return
((
status
&
TP_ACPI_UWB_RADIOSSW
)
!=
0
)
?
RFKILL_STATE_UNBLOCKED
:
RFKILL_STATE_SOFT_BLOCKED
;
}
static
void
uwb_update_rfk
(
void
)
{
int
status
;
if
(
!
tpacpi_uwb_rfkill
)
return
;
status
=
uwb_get_radiosw
();
if
(
status
<
0
)
return
;
rfkill_force_state
(
tpacpi_uwb_rfkill
,
status
);
}
static
int
uwb_set_radiosw
(
int
radio_on
,
int
update_rfk
)
{
int
status
;
if
(
!
tp_features
.
uwb
)
return
-
ENODEV
;
/* WLSW overrides UWB in firmware/hardware, but there is no
* reason to risk weird behaviour. */
if
(
tp_features
.
hotkey_wlsw
&&
!
hotkey_get_wlsw
(
&
status
)
&&
!
status
&&
radio_on
)
return
-
EPERM
;
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
dbg_uwbemul
)
{
tpacpi_uwb_emulstate
=
!!
radio_on
;
if
(
update_rfk
)
uwb_update_rfk
();
return
0
;
}
#endif
status
=
(
radio_on
)
?
TP_ACPI_UWB_RADIOSSW
:
0
;
if
(
!
acpi_evalf
(
hkey_handle
,
NULL
,
"SUWB"
,
"vd"
,
status
))
return
-
EIO
;
if
(
update_rfk
)
uwb_update_rfk
();
return
0
;
}
/* --------------------------------------------------------------------- */
static
int
tpacpi_uwb_rfk_get
(
void
*
data
,
enum
rfkill_state
*
state
)
{
int
uwbs
=
uwb_get_radiosw
();
if
(
uwbs
<
0
)
return
uwbs
;
*
state
=
uwbs
;
return
0
;
}
static
int
tpacpi_uwb_rfk_set
(
void
*
data
,
enum
rfkill_state
state
)
{
return
uwb_set_radiosw
((
state
==
RFKILL_STATE_UNBLOCKED
),
0
);
}
static
void
uwb_exit
(
void
)
{
if
(
tpacpi_uwb_rfkill
)
rfkill_unregister
(
tpacpi_uwb_rfkill
);
}
static
int
__init
uwb_init
(
struct
ibm_init_struct
*
iibm
)
{
int
res
;
int
status
=
0
;
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing uwb subdriver
\n
"
);
TPACPI_ACPIHANDLE_INIT
(
hkey
);
tp_features
.
uwb
=
hkey_handle
&&
acpi_evalf
(
hkey_handle
,
&
status
,
"GUWB"
,
"qd"
);
vdbg_printk
(
TPACPI_DBG_INIT
,
"uwb is %s, status 0x%02x
\n
"
,
str_supported
(
tp_features
.
uwb
),
status
);
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if
(
dbg_uwbemul
)
{
tp_features
.
uwb
=
1
;
printk
(
TPACPI_INFO
"uwb switch emulation enabled
\n
"
);
}
else
#endif
if
(
tp_features
.
uwb
&&
!
(
status
&
TP_ACPI_UWB_HWPRESENT
))
{
/* no uwb hardware present in system */
tp_features
.
uwb
=
0
;
dbg_printk
(
TPACPI_DBG_INIT
,
"uwb hardware not installed
\n
"
);
}
if
(
!
tp_features
.
uwb
)
return
1
;
res
=
tpacpi_new_rfkill
(
TPACPI_RFK_UWB_SW_ID
,
&
tpacpi_uwb_rfkill
,
RFKILL_TYPE_UWB
,
"tpacpi_uwb_sw"
,
false
,
tpacpi_uwb_rfk_set
,
tpacpi_uwb_rfk_get
);
return
res
;
}
static
struct
ibm_struct
uwb_driver_data
=
{
.
name
=
"uwb"
,
.
exit
=
uwb_exit
,
.
flags
.
experimental
=
1
,
};
/*************************************************************************
...
...
@@ -3724,7 +4293,7 @@ static void dock_notify(struct ibm_struct *ibm, u32 event)
}
acpi_bus_generate_proc_event
(
ibm
->
acpi
->
device
,
event
,
data
);
acpi_bus_generate_netlink_event
(
ibm
->
acpi
->
device
->
pnp
.
device_class
,
ibm
->
acpi
->
device
->
dev
.
bus_id
,
dev_name
(
&
ibm
->
acpi
->
device
->
dev
)
,
event
,
data
);
}
...
...
@@ -3826,7 +4395,7 @@ static void bay_notify(struct ibm_struct *ibm, u32 event)
{
acpi_bus_generate_proc_event
(
ibm
->
acpi
->
device
,
event
,
0
);
acpi_bus_generate_netlink_event
(
ibm
->
acpi
->
device
->
pnp
.
device_class
,
ibm
->
acpi
->
device
->
dev
.
bus_id
,
dev_name
(
&
ibm
->
acpi
->
device
->
dev
)
,
event
,
0
);
}
...
...
@@ -4850,7 +5419,7 @@ static int brightness_set(int value)
value
<
0
)
return
-
EINVAL
;
res
=
mutex_lock_
interrupti
ble
(
&
brightness_mutex
);
res
=
mutex_lock_
killa
ble
(
&
brightness_mutex
);
if
(
res
<
0
)
return
res
;
...
...
@@ -5333,6 +5902,60 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */
"JFNS"
,
/* 770x-JL */
);
/* all others */
/*
* Unitialized HFSP quirk: ACPI DSDT and EC fail to initialize the
* HFSP register at boot, so it contains 0x07 but the Thinkpad could
* be in auto mode (0x80).
*
* This is corrected by any write to HFSP either by the driver, or
* by the firmware.
*
* We assume 0x07 really means auto mode while this quirk is active,
* as this is far more likely than the ThinkPad being in level 7,
* which is only used by the firmware during thermal emergencies.
*/
static
void
fan_quirk1_detect
(
void
)
{
/* In some ThinkPads, neither the EC nor the ACPI
* DSDT initialize the HFSP register, and it ends up
* being initially set to 0x07 when it *could* be
* either 0x07 or 0x80.
*
* Enable for TP-1Y (T43), TP-78 (R51e),
* TP-76 (R52), TP-70 (T43, R52), which are known
* to be buggy. */
if
(
fan_control_initial_status
==
0x07
)
{
switch
(
thinkpad_id
.
ec_model
)
{
case
0x5931
:
/* TP-1Y */
case
0x3837
:
/* TP-78 */
case
0x3637
:
/* TP-76 */
case
0x3037
:
/* TP-70 */
printk
(
TPACPI_NOTICE
"fan_init: initial fan status is unknown, "
"assuming it is in auto mode
\n
"
);
tp_features
.
fan_ctrl_status_undef
=
1
;
;;
}
}
}
static
void
fan_quirk1_handle
(
u8
*
fan_status
)
{
if
(
unlikely
(
tp_features
.
fan_ctrl_status_undef
))
{
if
(
*
fan_status
!=
fan_control_initial_status
)
{
/* something changed the HFSP regisnter since
* driver init time, so it is not undefined
* anymore */
tp_features
.
fan_ctrl_status_undef
=
0
;
}
else
{
/* Return most likely status. In fact, it
* might be the only possible status */
*
fan_status
=
TP_EC_FAN_AUTO
;
}
}
}
/*
* Call with fan_mutex held
*/
...
...
@@ -5371,8 +5994,10 @@ static int fan_get_status(u8 *status)
if
(
unlikely
(
!
acpi_ec_read
(
fan_status_offset
,
&
s
)))
return
-
EIO
;
if
(
likely
(
status
))
if
(
likely
(
status
))
{
*
status
=
s
;
fan_quirk1_handle
(
status
);
}
break
;
...
...
@@ -5388,7 +6013,7 @@ static int fan_get_status_safe(u8 *status)
int
rc
;
u8
s
;
if
(
mutex_lock_
interrupti
ble
(
&
fan_mutex
))
if
(
mutex_lock_
killa
ble
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
rc
=
fan_get_status
(
&
s
);
if
(
!
rc
)
...
...
@@ -5471,7 +6096,7 @@ static int fan_set_level_safe(int level)
if
(
!
fan_control_allowed
)
return
-
EPERM
;
if
(
mutex_lock_
interrupti
ble
(
&
fan_mutex
))
if
(
mutex_lock_
killa
ble
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
if
(
level
==
TPACPI_FAN_LAST_LEVEL
)
...
...
@@ -5493,7 +6118,7 @@ static int fan_set_enable(void)
if
(
!
fan_control_allowed
)
return
-
EPERM
;
if
(
mutex_lock_
interrupti
ble
(
&
fan_mutex
))
if
(
mutex_lock_
killa
ble
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
switch
(
fan_control_access_mode
)
{
...
...
@@ -5548,7 +6173,7 @@ static int fan_set_disable(void)
if
(
!
fan_control_allowed
)
return
-
EPERM
;
if
(
mutex_lock_
interrupti
ble
(
&
fan_mutex
))
if
(
mutex_lock_
killa
ble
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
rc
=
0
;
...
...
@@ -5586,7 +6211,7 @@ static int fan_set_speed(int speed)
if
(
!
fan_control_allowed
)
return
-
EPERM
;
if
(
mutex_lock_
interrupti
ble
(
&
fan_mutex
))
if
(
mutex_lock_
killa
ble
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
rc
=
0
;
...
...
@@ -5682,16 +6307,6 @@ static ssize_t fan_pwm1_enable_show(struct device *dev,
if
(
res
)
return
res
;
if
(
unlikely
(
tp_features
.
fan_ctrl_status_undef
))
{
if
(
status
!=
fan_control_initial_status
)
{
tp_features
.
fan_ctrl_status_undef
=
0
;
}
else
{
/* Return most likely status. In fact, it
* might be the only possible status */
status
=
TP_EC_FAN_AUTO
;
}
}
if
(
status
&
TP_EC_FAN_FULLSPEED
)
{
mode
=
0
;
}
else
if
(
status
&
TP_EC_FAN_AUTO
)
{
...
...
@@ -5756,14 +6371,6 @@ static ssize_t fan_pwm1_show(struct device *dev,
if
(
res
)
return
res
;
if
(
unlikely
(
tp_features
.
fan_ctrl_status_undef
))
{
if
(
status
!=
fan_control_initial_status
)
{
tp_features
.
fan_ctrl_status_undef
=
0
;
}
else
{
status
=
TP_EC_FAN_AUTO
;
}
}
if
((
status
&
(
TP_EC_FAN_AUTO
|
TP_EC_FAN_FULLSPEED
))
!=
0
)
status
=
fan_control_desired_level
;
...
...
@@ -5788,7 +6395,7 @@ static ssize_t fan_pwm1_store(struct device *dev,
/* scale down from 0-255 to 0-7 */
newlevel
=
(
s
>>
5
)
&
0x07
;
if
(
mutex_lock_
interrupti
ble
(
&
fan_mutex
))
if
(
mutex_lock_
killa
ble
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
rc
=
fan_get_status
(
&
status
);
...
...
@@ -5895,29 +6502,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
if
(
likely
(
acpi_ec_read
(
fan_status_offset
,
&
fan_control_initial_status
)))
{
fan_status_access_mode
=
TPACPI_FAN_RD_TPEC
;
/* In some ThinkPads, neither the EC nor the ACPI
* DSDT initialize the fan status, and it ends up
* being set to 0x07 when it *could* be either
* 0x07 or 0x80.
*
* Enable for TP-1Y (T43), TP-78 (R51e),
* TP-76 (R52), TP-70 (T43, R52), which are known
* to be buggy. */
if
(
fan_control_initial_status
==
0x07
)
{
switch
(
thinkpad_id
.
ec_model
)
{
case
0x5931
:
/* TP-1Y */
case
0x3837
:
/* TP-78 */
case
0x3637
:
/* TP-76 */
case
0x3037
:
/* TP-70 */
printk
(
TPACPI_NOTICE
"fan_init: initial fan status "
"is unknown, assuming it is "
"in auto mode
\n
"
);
tp_features
.
fan_ctrl_status_undef
=
1
;
;;
}
}
fan_quirk1_detect
();
}
else
{
printk
(
TPACPI_ERR
"ThinkPad ACPI EC access misbehaving, "
...
...
@@ -6106,15 +6691,6 @@ static int fan_read(char *p)
if
(
rc
<
0
)
return
rc
;
if
(
unlikely
(
tp_features
.
fan_ctrl_status_undef
))
{
if
(
status
!=
fan_control_initial_status
)
tp_features
.
fan_ctrl_status_undef
=
0
;
else
/* Return most likely status. In fact, it
* might be the only possible status */
status
=
TP_EC_FAN_AUTO
;
}
len
+=
sprintf
(
p
+
len
,
"status:
\t\t
%s
\n
"
,
(
status
!=
0
)
?
"enabled"
:
"disabled"
);
...
...
@@ -6563,6 +7139,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
.
init
=
wan_init
,
.
data
=
&
wan_driver_data
,
},
{
.
init
=
uwb_init
,
.
data
=
&
uwb_driver_data
,
},
#ifdef CONFIG_THINKPAD_ACPI_VIDEO
{
.
init
=
video_init
,
...
...
@@ -6701,6 +7281,32 @@ TPACPI_PARAM(brightness);
TPACPI_PARAM
(
volume
);
TPACPI_PARAM
(
fan
);
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
module_param
(
dbg_wlswemul
,
uint
,
0
);
MODULE_PARM_DESC
(
dbg_wlswemul
,
"Enables WLSW emulation"
);
module_param_named
(
wlsw_state
,
tpacpi_wlsw_emulstate
,
bool
,
0
);
MODULE_PARM_DESC
(
wlsw_state
,
"Initial state of the emulated WLSW switch"
);
module_param
(
dbg_bluetoothemul
,
uint
,
0
);
MODULE_PARM_DESC
(
dbg_bluetoothemul
,
"Enables bluetooth switch emulation"
);
module_param_named
(
bluetooth_state
,
tpacpi_bluetooth_emulstate
,
bool
,
0
);
MODULE_PARM_DESC
(
bluetooth_state
,
"Initial state of the emulated bluetooth switch"
);
module_param
(
dbg_wwanemul
,
uint
,
0
);
MODULE_PARM_DESC
(
dbg_wwanemul
,
"Enables WWAN switch emulation"
);
module_param_named
(
wwan_state
,
tpacpi_wwan_emulstate
,
bool
,
0
);
MODULE_PARM_DESC
(
wwan_state
,
"Initial state of the emulated WWAN switch"
);
module_param
(
dbg_uwbemul
,
uint
,
0
);
MODULE_PARM_DESC
(
dbg_uwbemul
,
"Enables UWB switch emulation"
);
module_param_named
(
uwb_state
,
tpacpi_uwb_emulstate
,
bool
,
0
);
MODULE_PARM_DESC
(
uwb_state
,
"Initial state of the emulated UWB switch"
);
#endif
static
void
thinkpad_acpi_module_exit
(
void
)
{
struct
ibm_struct
*
ibm
,
*
itmp
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment