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
6bfe5c9d
Commit
6bfe5c9d
authored
Dec 16, 2006
by
Len Brown
Browse files
Options
Browse Files
Download
Plain Diff
Pull platform-drivers into test branch
parents
b3617350
f9ff43a6
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1131 additions
and
221 deletions
+1131
-221
Documentation/ibm-acpi.txt
Documentation/ibm-acpi.txt
+120
-31
drivers/acpi/Kconfig
drivers/acpi/Kconfig
+14
-0
drivers/acpi/asus_acpi.c
drivers/acpi/asus_acpi.c
+46
-16
drivers/acpi/ibm_acpi.c
drivers/acpi/ibm_acpi.c
+886
-151
drivers/acpi/toshiba_acpi.c
drivers/acpi/toshiba_acpi.c
+65
-23
No files found.
Documentation/ibm-acpi.txt
View file @
6bfe5c9d
...
...
@@ -398,25 +398,67 @@ Temperature sensors -- /proc/acpi/ibm/thermal
Most ThinkPads include six or more separate temperature sensors but
only expose the CPU temperature through the standard ACPI methods.
This feature shows readings from up to eight different sensors. Some
readings may not be valid, e.g. may show large negative values. For
example, on the X40, a typical output may be:
This feature shows readings from up to eight different sensors on older
ThinkPads, and it has experimental support for up to sixteen different
sensors on newer ThinkPads. Readings from sensors that are not available
return -128.
No commands can be written to this file.
EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the
implementation directly accesses hardware registers and may not work as
expected. USE WITH CAUTION! To use this feature, you need to supply the
experimental=1 parameter when loading the module. When EXPERIMENTAL
mode is enabled, reading the first 8 sensors on newer ThinkPads will
also use an new experimental thermal sensor access mode.
For example, on the X40, a typical output may be:
temperatures: 42 42 45 41 36 -128 33 -128
Thomas Gruber took his R51 apart and traced all six active sensors in
his laptop (the location of sensors may vary on other models):
EXPERIMENTAL: On the T43/p, a typical output may be:
temperatures: 48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128
The mapping of thermal sensors to physical locations varies depending on
system-board model (and thus, on ThinkPad model).
http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that
tries to track down these locations for various models.
Most (newer?) models seem to follow this pattern:
1: CPU
2:
Mini PCI Module
3:
HDD
2:
(depends on model)
3:
(depends on model)
4: GPU
5: Battery
6: N/A
7: Battery
8: N/A
5: Main battery: main sensor
6: Bay battery: main sensor
7: Main battery: secondary sensor
8: Bay battery: secondary sensor
9-15: (depends on model)
For the R51 (source: Thomas Gruber):
2: Mini-PCI
3: Internal HDD
For the T43, T43/p (source: Shmidoax/Thinkwiki.org)
http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p
2: System board, left side (near PCMCIA slot), reported as HDAPS temp
3: PCMCIA slot
9: MCH (northbridge) to DRAM Bus
10: ICH (southbridge), under Mini-PCI card, under touchpad
11: Power regulator, underside of system board, below F2 key
The A31 has a very atypical layout for the thermal sensors
(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31)
1: CPU
2: Main Battery: main sensor
3: Power Converter
4: Bay Battery: main sensor
5: MCH (northbridge)
6: PCMCIA/ambient
7: Main Battery: secondary sensor
8: Bay Battery: secondary sensor
No commands can be written to this file.
EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump
------------------------------------------------------------------------
...
...
@@ -529,27 +571,57 @@ directly accesses hardware registers and may not work as expected. USE
WITH CAUTION! To use this feature, you need to supply the
experimental=1 parameter when loading the module.
This feature attempts to show the current fan speed. The speed is read
directly from the hardware registers of the embedded controller. This
is known to work on later R, T and X series ThinkPads but may show a
bogus value on other models.
This feature attempts to show the current fan speed, control mode and
other fan data that might be available. The speed is read directly
from the hardware registers of the embedded controller. This is known
to work on later R, T and X series ThinkPads but may show a bogus
value on other models.
Most ThinkPad fans work in "levels". Level 0 stops the fan. The higher
the level, the higher the fan speed, although adjacent levels often map
to the same fan speed. 7 is the highest level, where the fan reaches
the maximum recommended speed. Level "auto" means the EC changes the
fan level according to some internal algorithm, usually based on
readings from the thermal sensors. Level "disengaged" means the EC
disables the speed-locked closed-loop fan control, and drives the fan as
fast as it can go, which might exceed hardware limits, so use this level
with caution.
The fan usually ramps up or down slowly from one speed to another,
and it is normal for the EC to take several seconds to react to fan
commands.
The fan may be enabled or disabled with the following commands:
echo enable >/proc/acpi/ibm/fan
echo disable >/proc/acpi/ibm/fan
Placing a fan on level 0 is the same as disabling it. Enabling a fan
will try to place it in a safe level if it is too slow or disabled.
WARNING WARNING WARNING: do not leave the fan disabled unless you are
monitoring
the temperature sensor readings and you are ready to enable
it if necessary to avoid overheating.
monitoring
all of the temperature sensor readings and you are ready to
enable
it if necessary to avoid overheating.
The fan only runs if it's enabled *and* the various temperature
sensors which control it read high enough. On the X40, this seems to
depend on the CPU and HDD temperatures. Specifically, the fan is
turned on when either the CPU temperature climbs to 56 degrees or the
HDD temperature climbs to 46 degrees. The fan is turned off when the
CPU temperature drops to 49 degrees and the HDD temperature drops to
41 degrees. These thresholds cannot currently be controlled.
An enabled fan in level "auto" may stop spinning if the EC decides the
ThinkPad is cool enough and doesn't need the extra airflow. This is
normal, and the EC will spin the fan up if the varios thermal readings
rise too much.
On the X40, this seems to depend on the CPU and HDD temperatures.
Specifically, the fan is turned on when either the CPU temperature
climbs to 56 degrees or the HDD temperature climbs to 46 degrees. The
fan is turned off when the CPU temperature drops to 49 degrees and the
HDD temperature drops to 41 degrees. These thresholds cannot
currently be controlled.
The fan level can be controlled with the command:
echo 'level <level>' > /proc/acpi/ibm/thermal
Where <level> is an integer from 0 to 7, or one of the words "auto"
or "disengaged" (without the quotes). Not all ThinkPads support the
"auto" and "disengaged" levels.
On the X31 and X40 (and ONLY on those models), the fan speed can be
controlled to a certain degree. Once the fan is running, it can be
...
...
@@ -562,12 +634,9 @@ about 3700 to about 7350. Values outside this range either do not have
any effect or the fan speed eventually settles somewhere in that
range. The fan cannot be stopped or started with this command.
On the 570, temperature readings are not available through this
feature and the fan control works a little differently. The fan speed
is reported in levels from 0 (off) to 7 (max) and can be controlled
with the following command:
echo 'level <level>' > /proc/acpi/ibm/thermal
The ThinkPad's ACPI DSDT code will reprogram the fan on its own when
certain conditions are met. It will override any fan programming done
through ibm-acpi.
EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan
---------------------------------------
...
...
@@ -601,6 +670,26 @@ example:
modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable
The ibm-acpi kernel driver can be programmed to revert the fan level
to a safe setting if userspace does not issue one of the fan commands:
"enable", "disable", "level" or "watchdog" within a configurable
ammount of time. To do this, use the "watchdog" command.
echo 'watchdog <interval>' > /proc/acpi/ibm/fan
Interval is the ammount of time in seconds to wait for one of the
above mentioned fan commands before reseting the fan level to a safe
one. If set to zero, the watchdog is disabled (default). When the
watchdog timer runs out, it does the exact equivalent of the "enable"
fan command.
Note that the watchdog timer stops after it enables the fan. It will
be rearmed again automatically (using the same interval) when one of
the above mentioned fan commands is received. The fan watchdog is,
therefore, not suitable to protect against fan mode changes made
through means other than the "enable", "disable", and "level" fan
commands.
Example Configuration
---------------------
...
...
drivers/acpi/Kconfig
View file @
6bfe5c9d
...
...
@@ -173,6 +173,7 @@ config ACPI_NUMA
config ACPI_ASUS
tristate "ASUS/Medion Laptop Extras"
depends on X86
select BACKLIGHT_CLASS_DEVICE
---help---
This driver provides support for extra features of ACPI-compatible
ASUS laptops. As some of Medion laptops are made by ASUS, it may also
...
...
@@ -201,6 +202,7 @@ config ACPI_ASUS
config ACPI_IBM
tristate "IBM ThinkPad Laptop Extras"
depends on X86
select BACKLIGHT_CLASS_DEVICE
---help---
This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video
...
...
@@ -223,9 +225,21 @@ config ACPI_IBM_DOCK
If you are not sure, say N here.
config ACPI_IBM_BAY
bool "Legacy Removable Bay Support"
depends on ACPI_IBM
depends on ACPI_BAY=n
default n
---help---
Allows the ibm_acpi driver to handle removable bays.
This support is obsoleted by CONFIG_ACPI_BAY.
If you are not sure, say N here.
config ACPI_TOSHIBA
tristate "Toshiba Laptop Extras"
depends on X86
select BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to certain system settings
on "legacy free" Toshiba laptops. These laptops can be recognized by
...
...
drivers/acpi/asus_acpi.c
View file @
6bfe5c9d
...
...
@@ -35,6 +35,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/backlight.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <asm/uaccess.h>
...
...
@@ -402,6 +403,8 @@ static struct model_data model_conf[END_MODEL] = {
/* procdir we use */
static
struct
proc_dir_entry
*
asus_proc_dir
;
static
struct
backlight_device
*
asus_backlight_device
;
/*
* This header is made available to allow proper configuration given model,
* revision number , ... this info cannot go in struct asus_hotk because it is
...
...
@@ -779,7 +782,7 @@ proc_write_lcd(struct file *file, const char __user * buffer,
return
rv
;
}
static
int
read_brightness
(
voi
d
)
static
int
read_brightness
(
struct
backlight_device
*
b
d
)
{
int
value
;
...
...
@@ -801,9 +804,10 @@ static int read_brightness(void)
/*
* Change the brightness level
*/
static
void
set_brightness
(
int
value
)
static
int
set_brightness
(
int
value
)
{
acpi_status
status
=
0
;
int
ret
=
0
;
/* SPLV laptop */
if
(
hotk
->
methods
->
brightness_set
)
{
...
...
@@ -811,11 +815,12 @@ static void set_brightness(int value)
value
,
NULL
))
printk
(
KERN_WARNING
"Asus ACPI: Error changing brightness
\n
"
);
return
;
ret
=
-
EIO
;
goto
out
;
}
/* No SPLV method if we are here, act as appropriate */
value
-=
read_brightness
();
value
-=
read_brightness
(
NULL
);
while
(
value
!=
0
)
{
status
=
acpi_evaluate_object
(
NULL
,
(
value
>
0
)
?
hotk
->
methods
->
brightness_up
:
...
...
@@ -825,15 +830,22 @@ static void set_brightness(int value)
if
(
ACPI_FAILURE
(
status
))
printk
(
KERN_WARNING
"Asus ACPI: Error changing brightness
\n
"
);
ret
=
-
EIO
;
}
return
;
out:
return
ret
;
}
static
int
set_brightness_status
(
struct
backlight_device
*
bd
)
{
return
set_brightness
(
bd
->
props
->
brightness
);
}
static
int
proc_read_brn
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
return
sprintf
(
page
,
"%d
\n
"
,
read_brightness
());
return
sprintf
(
page
,
"%d
\n
"
,
read_brightness
(
NULL
));
}
static
int
...
...
@@ -1333,6 +1345,26 @@ static int asus_hotk_remove(struct acpi_device *device, int type)
return
0
;
}
static
struct
backlight_properties
asus_backlight_data
=
{
.
owner
=
THIS_MODULE
,
.
get_brightness
=
read_brightness
,
.
update_status
=
set_brightness_status
,
.
max_brightness
=
15
,
};
static
void
__exit
asus_acpi_exit
(
void
)
{
if
(
asus_backlight_device
)
backlight_device_unregister
(
asus_backlight_device
);
acpi_bus_unregister_driver
(
&
asus_hotk_driver
);
remove_proc_entry
(
PROC_ASUS
,
acpi_root_dir
);
kfree
(
asus_info
);
return
;
}
static
int
__init
asus_acpi_init
(
void
)
{
int
result
;
...
...
@@ -1370,17 +1402,15 @@ static int __init asus_acpi_init(void)
return
result
;
}
return
0
;
}
static
void
__exit
asus_acpi_exit
(
void
)
{
acpi_bus_unregister_driver
(
&
asus_hotk_driver
);
remove_proc_entry
(
PROC_ASUS
,
acpi_root_dir
);
kfree
(
asus_info
);
asus_backlight_device
=
backlight_device_register
(
"asus"
,
NULL
,
&
asus_backlight_data
);
if
(
IS_ERR
(
asus_backlight_device
))
{
printk
(
KERN_ERR
"Could not register asus backlight device
\n
"
);
asus_backlight_device
=
NULL
;
asus_acpi_exit
();
}
return
;
return
0
;
}
module_init
(
asus_acpi_init
);
...
...
drivers/acpi/ibm_acpi.c
View file @
6bfe5c9d
...
...
@@ -3,6 +3,7 @@
*
*
* Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
* Copyright (C) 2006 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
*
* 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
...
...
@@ -19,10 +20,14 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define IBM_VERSION "0.1
2a
"
#define IBM_VERSION "0.1
3
"
/*
* Changelog:
*
* 2006-11-22 0.13 new maintainer
* changelog now lives in git commit history, and will
* not be updated further in-file.
*
* 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels
* 2005-03-17 0.11 support for 600e, 770x
...
...
@@ -77,9 +82,16 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/proc_fs.h>
#include <linux/backlight.h>
#include <asm/uaccess.h>
#include <linux/dmi.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acnamesp.h>
...
...
@@ -88,7 +100,7 @@
#define IBM_FILE "ibm_acpi"
#define IBM_URL "http://ibm-acpi.sf.net/"
MODULE_AUTHOR
(
"Borislav Deianov"
);
MODULE_AUTHOR
(
"Borislav Deianov
, Henrique de Moraes Holschuh
"
);
MODULE_DESCRIPTION
(
IBM_DESC
);
MODULE_VERSION
(
IBM_VERSION
);
MODULE_LICENSE
(
"GPL"
);
...
...
@@ -116,28 +128,6 @@ static acpi_handle root_handle = NULL;
static char *object##_path; \
static char *object##_paths[] = { paths }
/*
* The following models are supported to various degrees:
*
* 570, 600e, 600x, 770e, 770x
* A20m, A21e, A21m, A21p, A22p, A30, A30p, A31, A31p
* G40, G41
* R30, R31, R32, R40, R40e, R50, R50e, R50p, R51
* T20, T21, T22, T23, T30, T40, T40p, T41, T41p, T42, T42p, T43
* X20, X21, X22, X23, X24, X30, X31, X40
*
* The following models have no supported features:
*
* 240, 240x, i1400
*
* Still missing DSDTs for the following models:
*
* A20p, A22e, A22m
* R52
* S31
* T43p
*/
IBM_HANDLE
(
ec
,
root
,
"
\\
_SB.PCI0.ISA.EC0"
,
/* 240, 240x */
"
\\
_SB.PCI.ISA.EC"
,
/* 570 */
"
\\
_SB.PCI0.ISA0.EC0"
,
/* 600e/x, 770e, 770x */
...
...
@@ -167,8 +157,10 @@ IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
"
\\
_SB.PCI.ISA.SLCE"
,
/* 570 */
);
/* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
#endif
#ifdef CONFIG_ACPI_IBM_BAY
IBM_HANDLE
(
bay
,
root
,
"
\\
_SB.PCI.IDE.SECN.MAST"
,
/* 570 */
"
\\
_SB.PCI0.IDE0.IDES.IDSM"
,
/* 600e/x, 770e, 770x */
"
\\
_SB.PCI0.SATA.SCND.MSTR"
,
/* T60, X60, Z60 */
"
\\
_SB.PCI0.IDE0.SCND.MSTR"
,
/* all others */
);
/* A21e, R30, R31 */
...
...
@@ -183,6 +175,7 @@ IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
IBM_HANDLE
(
bay2_ej
,
bay2
,
"_EJ3"
,
/* 600e/x, 770e, A3x */
"_EJ0"
,
/* 770x */
);
/* all others */
#endif
/* don't list other alternatives as we install a notify handler on the 570 */
IBM_HANDLE
(
pci
,
root
,
"
\\
_SB.PCI"
);
/* 570 */
...
...
@@ -203,7 +196,7 @@ IBM_HANDLE(led, ec, "SLED", /* 570 */
IBM_HANDLE
(
beep
,
ec
,
"BEEP"
);
/* all except R30, R31 */
IBM_HANDLE
(
ecrd
,
ec
,
"ECRD"
);
/* 570 */
IBM_HANDLE
(
ecwr
,
ec
,
"ECWR"
);
/* 570 */
IBM_HANDLE
(
fans
,
ec
,
"FANS"
);
/* X31, X40 */
IBM_HANDLE
(
fans
,
ec
,
"FANS"
);
/* X31, X40
, X41
*/
IBM_HANDLE
(
gfan
,
ec
,
"GFAN"
,
/* 570 */
"
\\
FSPD"
,
/* 600e/x, 770e, 770x */
...
...
@@ -216,6 +209,152 @@ IBM_HANDLE(sfan, ec, "SFAN", /* 570 */
#define IBM_HKEY_HID "IBM0068"
#define IBM_PCI_HID "PNP0A03"
enum
thermal_access_mode
{
IBMACPI_THERMAL_NONE
=
0
,
/* No thermal support */
IBMACPI_THERMAL_ACPI_TMP07
,
/* Use ACPI TMP0-7 */
IBMACPI_THERMAL_ACPI_UPDT
,
/* Use ACPI TMP0-7 with UPDT */
IBMACPI_THERMAL_TPEC_8
,
/* Use ACPI EC regs, 8 sensors */
IBMACPI_THERMAL_TPEC_16
,
/* Use ACPI EC regs, 16 sensors */
};
#define IBMACPI_MAX_THERMAL_SENSORS 16
/* Max thermal sensors supported */
struct
ibm_thermal_sensors_struct
{
s32
temp
[
IBMACPI_MAX_THERMAL_SENSORS
];
};
/*
* FAN ACCESS MODES
*
* IBMACPI_FAN_RD_ACPI_GFAN:
* ACPI GFAN method: returns fan level
*
* see IBMACPI_FAN_WR_ACPI_SFAN
* EC 0x2f not available if GFAN exists
*
* IBMACPI_FAN_WR_ACPI_SFAN:
* ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
*
* EC 0x2f might be available *for reading*, but never for writing.
*
* IBMACPI_FAN_WR_TPEC:
* ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported
* on almost all ThinkPads
*
* Fan speed changes of any sort (including those caused by the
* disengaged mode) are usually done slowly by the firmware as the
* maximum ammount of fan duty cycle change per second seems to be
* limited.
*
* Reading is not available if GFAN exists.
* Writing is not available if SFAN exists.
*
* Bits
* 7 automatic mode engaged;
* (default operation mode of the ThinkPad)
* fan level is ignored in this mode.
* 6 disengage mode (takes precedence over bit 7);
* not available on all thinkpads. May disable
* the tachometer, and speeds up fan to 100% duty-cycle,
* which speeds it up far above the standard RPM
* levels. It is not impossible that it could cause
* hardware damage.
* 5-3 unused in some models. Extra bits for fan level
* in others, but still useless as all values above
* 7 map to the same speed as level 7 in these models.
* 2-0 fan level (0..7 usually)
* 0x00 = stop
* 0x07 = max (set when temperatures critical)
* Some ThinkPads may have other levels, see
* IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
*
* FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
* boot. Apparently the EC does not intialize it, so unless ACPI DSDT
* does so, its initial value is meaningless (0x07).
*
* For firmware bugs, refer to:
* http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
*
* ----
*
* ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
* Main fan tachometer reading (in RPM)
*
* This register is present on all ThinkPads with a new-style EC, and
* it is known not to be present on the A21m/e, and T22, as there is
* something else in offset 0x84 according to the ACPI DSDT. Other
* ThinkPads from this same time period (and earlier) probably lack the
* tachometer as well.
*
* Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
* was never fixed by IBM to report the EC firmware version string
* probably support the tachometer (like the early X models), so
* detecting it is quite hard. We need more data to know for sure.
*
* FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
* might result.
*
* FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this
* register is not invalidated in ThinkPads that disable tachometer
* readings. Thus, the tachometer readings go stale.
*
* For firmware bugs, refer to:
* http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
*
* IBMACPI_FAN_WR_ACPI_FANS:
* ThinkPad X31, X40, X41. Not available in the X60.
*
* FANS ACPI handle: takes three arguments: low speed, medium speed,
* high speed. ACPI DSDT seems to map these three speeds to levels
* as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
* (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
*
* The speeds are stored on handles
* (FANA:FAN9), (FANC:FANB), (FANE:FAND).
*
* There are three default speed sets, acessible as handles:
* FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
*
* ACPI DSDT switches which set is in use depending on various
* factors.
*
* IBMACPI_FAN_WR_TPEC is also available and should be used to
* command the fan. The X31/X40/X41 seems to have 8 fan levels,
* but the ACPI tables just mention level 7.
*/
enum
fan_status_access_mode
{
IBMACPI_FAN_NONE
=
0
,
/* No fan status or control */
IBMACPI_FAN_RD_ACPI_GFAN
,
/* Use ACPI GFAN */
IBMACPI_FAN_RD_TPEC
,
/* Use ACPI EC regs 0x2f, 0x84-0x85 */
};
enum
fan_control_access_mode
{
IBMACPI_FAN_WR_NONE
=
0
,
/* No fan control */
IBMACPI_FAN_WR_ACPI_SFAN
,
/* Use ACPI SFAN */
IBMACPI_FAN_WR_TPEC
,
/* Use ACPI EC reg 0x2f */
IBMACPI_FAN_WR_ACPI_FANS
,
/* Use ACPI FANS and EC reg 0x2f */
};
enum
fan_control_commands
{
IBMACPI_FAN_CMD_SPEED
=
0x0001
,
/* speed command */
IBMACPI_FAN_CMD_LEVEL
=
0x0002
,
/* level command */
IBMACPI_FAN_CMD_ENABLE
=
0x0004
,
/* enable/disable cmd,
* and also watchdog cmd */
};
enum
{
/* Fan control constants */
fan_status_offset
=
0x2f
,
/* EC register 0x2f */
fan_rpm_offset
=
0x84
,
/* EC register 0x84: LSB, 0x85 MSB (RPM)
* 0x84 must be read before 0x85 */
IBMACPI_FAN_EC_DISENGAGED
=
0x40
,
/* EC mode: tachometer
* disengaged */
IBMACPI_FAN_EC_AUTO
=
0x80
,
/* EC mode: auto fan
* control */
};
static
char
*
ibm_thinkpad_ec_found
=
NULL
;
struct
ibm_struct
{
char
*
name
;
char
param
[
32
];
...
...
@@ -243,6 +382,8 @@ struct ibm_struct {
static
struct
proc_dir_entry
*
proc_dir
=
NULL
;
static
struct
backlight_device
*
ibm_backlight_device
=
NULL
;
#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
#define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
...
...
@@ -581,8 +722,7 @@ static int wan_status(void)
{
int
status
;
if
(
!
wan_supported
||
!
acpi_evalf
(
hkey_handle
,
&
status
,
"GWAN"
,
"d"
))
if
(
!
wan_supported
||
!
acpi_evalf
(
hkey_handle
,
&
status
,
"GWAN"
,
"d"
))
status
=
0
;
return
status
;
...
...
@@ -630,12 +770,15 @@ static int wan_write(char *buf)
return
0
;
}
static
int
video_supported
;
static
int
video_orig_autosw
;
enum
video_access_mode
{
IBMACPI_VIDEO_NONE
=
0
,
IBMACPI_VIDEO_570
,
/* 570 */
IBMACPI_VIDEO_770
,
/* 600e/x, 770e, 770x */
IBMACPI_VIDEO_NEW
,
/* all others */
};
#define VIDEO_570 1
#define VIDEO_770 2
#define VIDEO_NEW 3
static
enum
video_access_mode
video_supported
;
static
int
video_orig_autosw
;
static
int
video_init
(
void
)
{
...
...
@@ -647,16 +790,16 @@ static int video_init(void)
if
(
!
vid_handle
)
/* video switching not supported on R30, R31 */
video_supported
=
0
;
video_supported
=
IBMACPI_VIDEO_NONE
;
else
if
(
acpi_evalf
(
vid_handle
,
&
video_orig_autosw
,
"SWIT"
,
"qd"
))
/* 570 */
video_supported
=
VIDEO_570
;
video_supported
=
IBMACPI_
VIDEO_570
;
else
if
(
acpi_evalf
(
vid_handle
,
&
video_orig_autosw
,
"^VADL"
,
"qd"
))
/* 600e/x, 770e, 770x */
video_supported
=
VIDEO_770
;
video_supported
=
IBMACPI_
VIDEO_770
;
else
/* all others */
video_supported
=
VIDEO_NEW
;
video_supported
=
IBMACPI_
VIDEO_NEW
;
return
0
;
}
...
...
@@ -666,15 +809,15 @@ static int video_status(void)
int
status
=
0
;
int
i
;
if
(
video_supported
==
VIDEO_570
)
{
if
(
video_supported
==
IBMACPI_
VIDEO_570
)
{
if
(
acpi_evalf
(
NULL
,
&
i
,
"
\\
_SB.PHS"
,
"dd"
,
0x87
))
status
=
i
&
3
;
}
else
if
(
video_supported
==
VIDEO_770
)
{
}
else
if
(
video_supported
==
IBMACPI_
VIDEO_770
)
{
if
(
acpi_evalf
(
NULL
,
&
i
,
"
\\
VCDL"
,
"d"
))
status
|=
0x01
*
i
;
if
(
acpi_evalf
(
NULL
,
&
i
,
"
\\
VCDC"
,
"d"
))
status
|=
0x02
*
i
;
}
else
if
(
video_supported
==
VIDEO_NEW
)
{
}
else
if
(
video_supported
==
IBMACPI_
VIDEO_NEW
)
{
acpi_evalf
(
NULL
,
NULL
,
"
\\
VUPS"
,
"vd"
,
1
);
if
(
acpi_evalf
(
NULL
,
&
i
,
"
\\
VCDC"
,
"d"
))
status
|=
0x02
*
i
;
...
...
@@ -693,9 +836,10 @@ static int video_autosw(void)
{
int
autosw
=
0
;
if
(
video_supported
==
VIDEO_570
)
if
(
video_supported
==
IBMACPI_
VIDEO_570
)
acpi_evalf
(
vid_handle
,
&
autosw
,
"SWIT"
,
"d"
);
else
if
(
video_supported
==
VIDEO_770
||
video_supported
==
VIDEO_NEW
)
else
if
(
video_supported
==
IBMACPI_VIDEO_770
||
video_supported
==
IBMACPI_VIDEO_NEW
)
acpi_evalf
(
vid_handle
,
&
autosw
,
"^VDEE"
,
"d"
);
return
autosw
&
1
;
...
...
@@ -715,12 +859,12 @@ static int video_read(char *p)
len
+=
sprintf
(
p
+
len
,
"status:
\t\t
supported
\n
"
);
len
+=
sprintf
(
p
+
len
,
"lcd:
\t\t
%s
\n
"
,
enabled
(
status
,
0
));
len
+=
sprintf
(
p
+
len
,
"crt:
\t\t
%s
\n
"
,
enabled
(
status
,
1
));
if
(
video_supported
==
VIDEO_NEW
)
if
(
video_supported
==
IBMACPI_
VIDEO_NEW
)
len
+=
sprintf
(
p
+
len
,
"dvi:
\t\t
%s
\n
"
,
enabled
(
status
,
3
));
len
+=
sprintf
(
p
+
len
,
"auto:
\t\t
%s
\n
"
,
enabled
(
autosw
,
0
));
len
+=
sprintf
(
p
+
len
,
"commands:
\t
lcd_enable, lcd_disable
\n
"
);
len
+=
sprintf
(
p
+
len
,
"commands:
\t
crt_enable, crt_disable
\n
"
);
if
(
video_supported
==
VIDEO_NEW
)
if
(
video_supported
==
IBMACPI_
VIDEO_NEW
)
len
+=
sprintf
(
p
+
len
,
"commands:
\t
dvi_enable, dvi_disable
\n
"
);
len
+=
sprintf
(
p
+
len
,
"commands:
\t
auto_enable, auto_disable
\n
"
);
len
+=
sprintf
(
p
+
len
,
"commands:
\t
video_switch, expand_toggle
\n
"
);
...
...
@@ -735,7 +879,7 @@ static int video_switch(void)
if
(
!
acpi_evalf
(
vid_handle
,
NULL
,
"_DOS"
,
"vd"
,
1
))
return
-
EIO
;
ret
=
video_supported
==
VIDEO_570
?
ret
=
video_supported
==
IBMACPI_
VIDEO_570
?
acpi_evalf
(
ec_handle
,
NULL
,
"_Q16"
,
"v"
)
:
acpi_evalf
(
vid_handle
,
NULL
,
"VSWT"
,
"v"
);
acpi_evalf
(
vid_handle
,
NULL
,
"_DOS"
,
"vd"
,
autosw
);
...
...
@@ -745,9 +889,9 @@ static int video_switch(void)
static
int
video_expand
(
void
)
{
if
(
video_supported
==
VIDEO_570
)
if
(
video_supported
==
IBMACPI_
VIDEO_570
)
return
acpi_evalf
(
ec_handle
,
NULL
,
"_Q17"
,
"v"
);
else
if
(
video_supported
==
VIDEO_770
)
else
if
(
video_supported
==
IBMACPI_
VIDEO_770
)
return
acpi_evalf
(
vid_handle
,
NULL
,
"VEXP"
,
"v"
);
else
return
acpi_evalf
(
NULL
,
NULL
,
"
\\
VEXP"
,
"v"
);
...
...
@@ -757,10 +901,10 @@ static int video_switch2(int status)
{
int
ret
;
if
(
video_supported
==
VIDEO_570
)
{
if
(
video_supported
==
IBMACPI_
VIDEO_570
)
{
ret
=
acpi_evalf
(
NULL
,
NULL
,
"
\\
_SB.PHS2"
,
"vdd"
,
0x8b
,
status
|
0x80
);
}
else
if
(
video_supported
==
VIDEO_770
)
{
}
else
if
(
video_supported
==
IBMACPI_
VIDEO_770
)
{
int
autosw
=
video_autosw
();
if
(
!
acpi_evalf
(
vid_handle
,
NULL
,
"_DOS"
,
"vd"
,
1
))
return
-
EIO
;
...
...
@@ -796,10 +940,10 @@ static int video_write(char *buf)
enable
|=
0x02
;
}
else
if
(
strlencmp
(
cmd
,
"crt_disable"
)
==
0
)
{
disable
|=
0x02
;
}
else
if
(
video_supported
==
VIDEO_NEW
&&
}
else
if
(
video_supported
==
IBMACPI_
VIDEO_NEW
&&
strlencmp
(
cmd
,
"dvi_enable"
)
==
0
)
{
enable
|=
0x08
;
}
else
if
(
video_supported
==
VIDEO_NEW
&&
}
else
if
(
video_supported
==
IBMACPI_
VIDEO_NEW
&&
strlencmp
(
cmd
,
"dvi_disable"
)
==
0
)
{
disable
|=
0x08
;
}
else
if
(
strlencmp
(
cmd
,
"auto_enable"
)
==
0
)
{
...
...
@@ -898,6 +1042,7 @@ static int light_write(char *buf)
return
0
;
}
#if defined(CONFIG_ACPI_IBM_DOCK) || defined(CONFIG_ACPI_IBM_BAY)
static
int
_sta
(
acpi_handle
handle
)
{
int
status
;
...
...
@@ -907,6 +1052,7 @@ static int _sta(acpi_handle handle)
return
status
;
}
#endif
#ifdef CONFIG_ACPI_IBM_DOCK
#define dock_docked() (_sta(dock_handle) & 1)
...
...
@@ -972,6 +1118,7 @@ static void dock_notify(struct ibm_struct *ibm, u32 event)
}
#endif
#ifdef CONFIG_ACPI_IBM_BAY
static
int
bay_status_supported
;
static
int
bay_status2_supported
;
static
int
bay_eject_supported
;
...
...
@@ -1047,6 +1194,7 @@ static void bay_notify(struct ibm_struct *ibm, u32 event)
{
acpi_bus_generate_event
(
ibm
->
device
,
event
,
0
);
}
#endif
static
int
cmos_read
(
char
*
p
)
{
...
...
@@ -1094,26 +1242,28 @@ static int cmos_write(char *buf)
return
0
;
}
static
int
led_supported
;
#define LED_570 1
#define LED_OLD 2
#define LED_NEW 3
enum
led_access_mode
{
IBMACPI_LED_NONE
=
0
,
IBMACPI_LED_570
,
/* 570 */
IBMACPI_LED_OLD
,
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
IBMACPI_LED_NEW
,
/* all others */
};
static
enum
led_access_mode
led_supported
;
static
int
led_init
(
void
)
{
if
(
!
led_handle
)
/* led not supported on R30, R31 */
led_supported
=
0
;
led_supported
=
IBMACPI_LED_NONE
;
else
if
(
strlencmp
(
led_path
,
"SLED"
)
==
0
)
/* 570 */
led_supported
=
LED_570
;
led_supported
=
IBMACPI_
LED_570
;
else
if
(
strlencmp
(
led_path
,
"SYSL"
)
==
0
)
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
led_supported
=
LED_OLD
;
led_supported
=
IBMACPI_
LED_OLD
;
else
/* all others */
led_supported
=
LED_NEW
;
led_supported
=
IBMACPI_
LED_NEW
;
return
0
;
}
...
...
@@ -1130,7 +1280,7 @@ static int led_read(char *p)
}
len
+=
sprintf
(
p
+
len
,
"status:
\t\t
supported
\n
"
);
if
(
led_supported
==
LED_570
)
{
if
(
led_supported
==
IBMACPI_
LED_570
)
{
/* 570 */
int
i
,
status
;
for
(
i
=
0
;
i
<
8
;
i
++
)
{
...
...
@@ -1179,13 +1329,13 @@ static int led_write(char *buf)
}
else
return
-
EINVAL
;
if
(
led_supported
==
LED_570
)
{
if
(
led_supported
==
IBMACPI_
LED_570
)
{
/* 570 */
led
=
1
<<
led
;
if
(
!
acpi_evalf
(
led_handle
,
NULL
,
NULL
,
"vdd"
,
led
,
led_sled_arg1
[
ind
]))
return
-
EIO
;
}
else
if
(
led_supported
==
LED_OLD
)
{
}
else
if
(
led_supported
==
IBMACPI_
LED_OLD
)
{
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
led
=
1
<<
led
;
ret
=
ec_write
(
EC_HLMS
,
led
);
...
...
@@ -1272,50 +1422,142 @@ static int acpi_ec_write(int i, u8 v)
return
1
;
}
static
int
thermal_tmp_supported
;
static
int
thermal_updt_supported
;
static
enum
thermal_access_mode
thermal_read_mode
;
static
int
thermal_init
(
void
)
{
/* temperatures not supported on 570, G4x, R30, R31, R32 */
thermal_tmp_supported
=
acpi_evalf
(
ec_handle
,
NULL
,
"TMP7"
,
"qv"
);
u8
t
,
ta1
,
ta2
;
int
i
;
int
acpi_tmp7
=
acpi_evalf
(
ec_handle
,
NULL
,
"TMP7"
,
"qv"
);
if
(
ibm_thinkpad_ec_found
&&
experimental
)
{
/*
* Direct EC access mode: sensors at registers
* 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
* non-implemented, thermal sensors return 0x80 when
* not available
*/
/* 600e/x, 770e, 770x */
thermal_updt_supported
=
acpi_evalf
(
ec_handle
,
NULL
,
"UPDT"
,
"qv"
);
ta1
=
ta2
=
0
;
for
(
i
=
0
;
i
<
8
;
i
++
)
{
if
(
likely
(
acpi_ec_read
(
0x78
+
i
,
&
t
)))
{
ta1
|=
t
;
}
else
{
ta1
=
0
;
break
;
}
if
(
likely
(
acpi_ec_read
(
0xC0
+
i
,
&
t
)))
{
ta2
|=
t
;
}
else
{
ta1
=
0
;
break
;
}
}
if
(
ta1
==
0
)
{
/* This is sheer paranoia, but we handle it anyway */
if
(
acpi_tmp7
)
{
printk
(
IBM_ERR
"ThinkPad ACPI EC access misbehaving, "
"falling back to ACPI TMPx access mode
\n
"
);
thermal_read_mode
=
IBMACPI_THERMAL_ACPI_TMP07
;
}
else
{
printk
(
IBM_ERR
"ThinkPad ACPI EC access misbehaving, "
"disabling thermal sensors access
\n
"
);
thermal_read_mode
=
IBMACPI_THERMAL_NONE
;
}
}
else
{
thermal_read_mode
=
(
ta2
!=
0
)
?
IBMACPI_THERMAL_TPEC_16
:
IBMACPI_THERMAL_TPEC_8
;
}
}
else
if
(
acpi_tmp7
)
{
if
(
acpi_evalf
(
ec_handle
,
NULL
,
"UPDT"
,
"qv"
))
{
/* 600e/x, 770e, 770x */
thermal_read_mode
=
IBMACPI_THERMAL_ACPI_UPDT
;
}
else
{
/* Standard ACPI TMPx access, max 8 sensors */
thermal_read_mode
=
IBMACPI_THERMAL_ACPI_TMP07
;
}
}
else
{
/* temperatures not supported on 570, G4x, R30, R31, R32 */
thermal_read_mode
=
IBMACPI_THERMAL_NONE
;
}
return
0
;
}
static
int
thermal_
read
(
char
*
p
)
static
int
thermal_
get_sensors
(
struct
ibm_thermal_sensors_struct
*
s
)
{
int
len
=
0
;
int
i
,
t
;
s8
tmp
;
char
tmpi
[]
=
"TMPi"
;
if
(
!
thermal_tmp_supported
)
len
+=
sprintf
(
p
+
len
,
"temperatures:
\t
not supported
\n
"
);
else
{
int
i
,
t
;
char
tmpi
[]
=
"TMPi"
;
s8
tmp
[
8
];
if
(
!
s
)
return
-
EINVAL
;
if
(
thermal_updt_supported
)
if
(
!
acpi_evalf
(
ec_handle
,
NULL
,
"UPDT"
,
"v"
))
switch
(
thermal_read_mode
)
{
#if IBMACPI_MAX_THERMAL_SENSORS >= 16
case
IBMACPI_THERMAL_TPEC_16
:
for
(
i
=
0
;
i
<
8
;
i
++
)
{
if
(
!
acpi_ec_read
(
0xC0
+
i
,
&
tmp
))
return
-
EIO
;
s
->
temp
[
i
+
8
]
=
tmp
*
1000
;
}
/* fallthrough */
#endif
case
IBMACPI_THERMAL_TPEC_8
:
for
(
i
=
0
;
i
<
8
;
i
++
)
{
if
(
!
acpi_ec_read
(
0x78
+
i
,
&
tmp
))
return
-
EIO
;
s
->
temp
[
i
]
=
tmp
*
1000
;
}
return
(
thermal_read_mode
==
IBMACPI_THERMAL_TPEC_16
)
?
16
:
8
;
case
IBMACPI_THERMAL_ACPI_UPDT
:
if
(
!
acpi_evalf
(
ec_handle
,
NULL
,
"UPDT"
,
"v"
))
return
-
EIO
;
for
(
i
=
0
;
i
<
8
;
i
++
)
{
tmpi
[
3
]
=
'0'
+
i
;
if
(
!
acpi_evalf
(
ec_handle
,
&
t
,
tmpi
,
"d"
))
return
-
EIO
;
if
(
thermal_updt_supported
)
tmp
[
i
]
=
(
t
-
2732
+
5
)
/
10
;
else
tmp
[
i
]
=
t
;
s
->
temp
[
i
]
=
(
t
-
2732
)
*
100
;
}
return
8
;
len
+=
sprintf
(
p
+
len
,
"temperatures:
\t
%d %d %d %d %d %d %d %d
\n
"
,
tmp
[
0
],
tmp
[
1
],
tmp
[
2
],
tmp
[
3
],
tmp
[
4
],
tmp
[
5
],
tmp
[
6
],
tmp
[
7
]);
case
IBMACPI_THERMAL_ACPI_TMP07
:
for
(
i
=
0
;
i
<
8
;
i
++
)
{
tmpi
[
3
]
=
'0'
+
i
;
if
(
!
acpi_evalf
(
ec_handle
,
&
t
,
tmpi
,
"d"
))
return
-
EIO
;
s
->
temp
[
i
]
=
t
*
1000
;
}
return
8
;
case
IBMACPI_THERMAL_NONE
:
default:
return
0
;
}
}
static
int
thermal_read
(
char
*
p
)
{
int
len
=
0
;
int
n
,
i
;
struct
ibm_thermal_sensors_struct
t
;
n
=
thermal_get_sensors
(
&
t
);
if
(
unlikely
(
n
<
0
))
return
n
;
len
+=
sprintf
(
p
+
len
,
"temperatures:
\t
"
);
if
(
n
>
0
)
{
for
(
i
=
0
;
i
<
(
n
-
1
);
i
++
)
len
+=
sprintf
(
p
+
len
,
"%d "
,
t
.
temp
[
i
]
/
1000
);
len
+=
sprintf
(
p
+
len
,
"%d
\n
"
,
t
.
temp
[
i
]
/
1000
);
}
else
len
+=
sprintf
(
p
+
len
,
"not supported
\n
"
);
return
len
;
}
...
...
@@ -1381,12 +1623,23 @@ static int ecdump_write(char *buf)
static
int
brightness_offset
=
0x31
;
static
int
brightness_get
(
struct
backlight_device
*
bd
)
{
u8
level
;
if
(
!
acpi_ec_read
(
brightness_offset
,
&
level
))
return
-
EIO
;
level
&=
0x7
;
return
level
;
}
static
int
brightness_read
(
char
*
p
)
{
int
len
=
0
;
u8
level
;
int
level
;
if
(
!
acpi_ec_read
(
brightness_offset
,
&
level
)
)
{
if
(
(
level
=
brightness_get
(
NULL
))
<
0
)
{
len
+=
sprintf
(
p
+
len
,
"level:
\t\t
unreadable
\n
"
);
}
else
{
len
+=
sprintf
(
p
+
len
,
"level:
\t\t
%d
\n
"
,
level
&
0x7
);
...
...
@@ -1401,16 +1654,34 @@ static int brightness_read(char *p)
#define BRIGHTNESS_UP 4
#define BRIGHTNESS_DOWN 5
static
int
brightness_
write
(
char
*
buf
)
static
int
brightness_
set
(
int
value
)
{
int
cmos_cmd
,
inc
,
i
;
u8
level
;
int
current_value
=
brightness_get
(
NULL
);
value
&=
7
;
cmos_cmd
=
value
>
current_value
?
BRIGHTNESS_UP
:
BRIGHTNESS_DOWN
;
inc
=
value
>
current_value
?
1
:
-
1
;
for
(
i
=
current_value
;
i
!=
value
;
i
+=
inc
)
{
if
(
!
cmos_eval
(
cmos_cmd
))
return
-
EIO
;
if
(
!
acpi_ec_write
(
brightness_offset
,
i
+
inc
))
return
-
EIO
;
}
return
0
;
}
static
int
brightness_write
(
char
*
buf
)
{
int
level
;
int
new_level
;
char
*
cmd
;
while
((
cmd
=
next_cmd
(
&
buf
)))
{
if
(
!
acpi_ec_read
(
brightness_offset
,
&
level
)
)
return
-
EIO
;
if
(
(
level
=
brightness_get
(
NULL
))
<
0
)
return
level
;
level
&=
7
;
if
(
strlencmp
(
cmd
,
"up"
)
==
0
)
{
...
...
@@ -1423,19 +1694,44 @@ static int brightness_write(char *buf)
}
else
return
-
EINVAL
;
cmos_cmd
=
new_level
>
level
?
BRIGHTNESS_UP
:
BRIGHTNESS_DOWN
;
inc
=
new_level
>
level
?
1
:
-
1
;
for
(
i
=
level
;
i
!=
new_level
;
i
+=
inc
)
{
if
(
!
cmos_eval
(
cmos_cmd
))
return
-
EIO
;
if
(
!
acpi_ec_write
(
brightness_offset
,
i
+
inc
))
return
-
EIO
;
}
brightness_set
(
new_level
);
}
return
0
;
}
static
int
brightness_update_status
(
struct
backlight_device
*
bd
)
{
return
brightness_set
(
bd
->
props
->
brightness
);
}
static
struct
backlight_properties
ibm_backlight_data
=
{
.
owner
=
THIS_MODULE
,
.
get_brightness
=
brightness_get
,
.
update_status
=
brightness_update_status
,
.
max_brightness
=
7
,
};
static
int
brightness_init
(
void
)
{
ibm_backlight_device
=
backlight_device_register
(
"ibm"
,
NULL
,
&
ibm_backlight_data
);
if
(
IS_ERR
(
ibm_backlight_device
))
{
printk
(
IBM_ERR
"Could not register backlight device
\n
"
);
return
PTR_ERR
(
ibm_backlight_device
);
}
return
0
;
}
static
void
brightness_exit
(
void
)
{
if
(
ibm_backlight_device
)
{
backlight_device_unregister
(
ibm_backlight_device
);
ibm_backlight_device
=
NULL
;
}
}
static
int
volume_offset
=
0x30
;
static
int
volume_read
(
char
*
p
)
...
...
@@ -1522,86 +1818,482 @@ static int volume_write(char *buf)
return
0
;
}
static
int
fan_status_offset
=
0x2f
;
static
int
fan_rpm_offset
=
0x84
;
static
enum
fan_status_access_mode
fan_status_access_mode
;
static
enum
fan_control_access_mode
fan_control_access_mode
;
static
enum
fan_control_commands
fan_control_commands
;
static
int
fan_read
(
char
*
p
)
static
int
fan_control_status_known
;
static
u8
fan_control_initial_status
;
static
void
fan_watchdog_fire
(
void
*
ignored
);
static
int
fan_watchdog_maxinterval
;
static
DECLARE_WORK
(
fan_watchdog_task
,
fan_watchdog_fire
,
NULL
);
static
int
fan_init
(
void
)
{
int
len
=
0
;
int
s
;
u8
lo
,
hi
,
status
;
fan_status_access_mode
=
IBMACPI_FAN_NONE
;
fan_control_access_mode
=
IBMACPI_FAN_WR_NONE
;
fan_control_commands
=
0
;
fan_control_status_known
=
1
;
fan_watchdog_maxinterval
=
0
;
if
(
gfan_handle
)
{
/* 570, 600e/x, 770e, 770x */
if
(
!
acpi_evalf
(
gfan_handle
,
&
s
,
NULL
,
"d"
))
return
-
EIO
;
fan_status_access_mode
=
IBMACPI_FAN_RD_ACPI_GFAN
;
}
else
{
/* all other ThinkPads: note that even old-style
* ThinkPad ECs supports the fan control register */
if
(
likely
(
acpi_ec_read
(
fan_status_offset
,
&
fan_control_initial_status
)))
{
fan_status_access_mode
=
IBMACPI_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
&&
ibm_thinkpad_ec_found
&&
((
ibm_thinkpad_ec_found
[
0
]
==
'1'
&&
ibm_thinkpad_ec_found
[
1
]
==
'Y'
)
||
(
ibm_thinkpad_ec_found
[
0
]
==
'7'
&&
(
ibm_thinkpad_ec_found
[
1
]
==
'6'
||
ibm_thinkpad_ec_found
[
1
]
==
'8'
||
ibm_thinkpad_ec_found
[
1
]
==
'0'
))
))
{
printk
(
IBM_NOTICE
"fan_init: initial fan status is "
"unknown, assuming it is in auto "
"mode
\n
"
);
fan_control_status_known
=
0
;
}
}
else
{
printk
(
IBM_ERR
"ThinkPad ACPI EC access misbehaving, "
"fan status and control unavailable
\n
"
);
return
0
;
}
}
len
+=
sprintf
(
p
+
len
,
"level:
\t\t
%d
\n
"
,
s
);
if
(
sfan_handle
)
{
/* 570, 770x-JL */
fan_control_access_mode
=
IBMACPI_FAN_WR_ACPI_SFAN
;
fan_control_commands
|=
IBMACPI_FAN_CMD_LEVEL
|
IBMACPI_FAN_CMD_ENABLE
;
}
else
{
if
(
!
gfan_handle
)
{
/* gfan without sfan means no fan control */
/* all other models implement TP EC 0x2f control */
if
(
fans_handle
)
{
/* X31, X40, X41 */
fan_control_access_mode
=
IBMACPI_FAN_WR_ACPI_FANS
;
fan_control_commands
|=
IBMACPI_FAN_CMD_SPEED
|
IBMACPI_FAN_CMD_LEVEL
|
IBMACPI_FAN_CMD_ENABLE
;
}
else
{
fan_control_access_mode
=
IBMACPI_FAN_WR_TPEC
;
fan_control_commands
|=
IBMACPI_FAN_CMD_LEVEL
|
IBMACPI_FAN_CMD_ENABLE
;
}
}
}
return
0
;
}
static
int
fan_get_status
(
u8
*
status
)
{
u8
s
;
/* TODO:
* Add IBMACPI_FAN_RD_ACPI_FANS ? */
switch
(
fan_status_access_mode
)
{
case
IBMACPI_FAN_RD_ACPI_GFAN
:
/* 570, 600e/x, 770e, 770x */
if
(
unlikely
(
!
acpi_evalf
(
gfan_handle
,
&
s
,
NULL
,
"d"
)))
return
-
EIO
;
if
(
likely
(
status
))
*
status
=
s
&
0x07
;
break
;
case
IBMACPI_FAN_RD_TPEC
:
/* all except 570, 600e/x, 770e, 770x */
if
(
!
acpi_ec_read
(
fan_status_offset
,
&
status
))
len
+=
sprintf
(
p
+
len
,
"status:
\t\t
unreadable
\n
"
);
else
len
+=
sprintf
(
p
+
len
,
"status:
\t\t
%s
\n
"
,
enabled
(
status
,
7
));
if
(
unlikely
(
!
acpi_ec_read
(
fan_status_offset
,
&
s
)))
return
-
EIO
;
if
(
!
acpi_ec_read
(
fan_rpm_offset
,
&
lo
)
||
!
acpi_ec_read
(
fan_rpm_offset
+
1
,
&
hi
))
len
+=
sprintf
(
p
+
len
,
"speed:
\t\t
unreadable
\n
"
);
else
len
+=
sprintf
(
p
+
len
,
"speed:
\t\t
%d
\n
"
,
(
hi
<<
8
)
+
lo
);
if
(
likely
(
status
))
*
status
=
s
;
break
;
default:
return
-
ENXIO
;
}
if
(
sfan_handle
)
/* 570, 770x-JL */
len
+=
sprintf
(
p
+
len
,
"commands:
\t
level <level>"
" (<level> is 0-7)
\n
"
);
if
(
!
gfan_handle
)
return
0
;
}
static
int
fan_get_speed
(
unsigned
int
*
speed
)
{
u8
hi
,
lo
;
switch
(
fan_status_access_mode
)
{
case
IBMACPI_FAN_RD_TPEC
:
/* all except 570, 600e/x, 770e, 770x */
len
+=
sprintf
(
p
+
len
,
"commands:
\t
enable, disable
\n
"
);
if
(
fans_handle
)
/* X31, X40 */
if
(
unlikely
(
!
acpi_ec_read
(
fan_rpm_offset
,
&
lo
)
||
!
acpi_ec_read
(
fan_rpm_offset
+
1
,
&
hi
)))
return
-
EIO
;
if
(
likely
(
speed
))
*
speed
=
(
hi
<<
8
)
|
lo
;
break
;
default:
return
-
ENXIO
;
}
return
0
;
}
static
void
fan_exit
(
void
)
{
cancel_delayed_work
(
&
fan_watchdog_task
);
flush_scheduled_work
();
}
static
void
fan_watchdog_reset
(
void
)
{
static
int
fan_watchdog_active
=
0
;
if
(
fan_watchdog_active
)
cancel_delayed_work
(
&
fan_watchdog_task
);
if
(
fan_watchdog_maxinterval
>
0
)
{
fan_watchdog_active
=
1
;
if
(
!
schedule_delayed_work
(
&
fan_watchdog_task
,
msecs_to_jiffies
(
fan_watchdog_maxinterval
*
1000
)))
{
printk
(
IBM_ERR
"failed to schedule the fan watchdog, "
"watchdog will not trigger
\n
"
);
}
}
else
fan_watchdog_active
=
0
;
}
static
int
fan_read
(
char
*
p
)
{
int
len
=
0
;
int
rc
;
u8
status
;
unsigned
int
speed
=
0
;
switch
(
fan_status_access_mode
)
{
case
IBMACPI_FAN_RD_ACPI_GFAN
:
/* 570, 600e/x, 770e, 770x */
if
((
rc
=
fan_get_status
(
&
status
))
<
0
)
return
rc
;
len
+=
sprintf
(
p
+
len
,
"status:
\t\t
%s
\n
"
"level:
\t\t
%d
\n
"
,
(
status
!=
0
)
?
"enabled"
:
"disabled"
,
status
);
break
;
case
IBMACPI_FAN_RD_TPEC
:
/* all except 570, 600e/x, 770e, 770x */
if
((
rc
=
fan_get_status
(
&
status
))
<
0
)
return
rc
;
if
(
unlikely
(
!
fan_control_status_known
))
{
if
(
status
!=
fan_control_initial_status
)
fan_control_status_known
=
1
;
else
/* Return most likely status. In fact, it
* might be the only possible status */
status
=
IBMACPI_FAN_EC_AUTO
;
}
len
+=
sprintf
(
p
+
len
,
"status:
\t\t
%s
\n
"
,
(
status
!=
0
)
?
"enabled"
:
"disabled"
);
/* No ThinkPad boots on disengaged mode, we can safely
* assume the tachometer is online if fan control status
* was unknown */
if
((
rc
=
fan_get_speed
(
&
speed
))
<
0
)
return
rc
;
len
+=
sprintf
(
p
+
len
,
"speed:
\t\t
%d
\n
"
,
speed
);
if
(
status
&
IBMACPI_FAN_EC_DISENGAGED
)
/* Disengaged mode takes precedence */
len
+=
sprintf
(
p
+
len
,
"level:
\t\t
disengaged
\n
"
);
else
if
(
status
&
IBMACPI_FAN_EC_AUTO
)
len
+=
sprintf
(
p
+
len
,
"level:
\t\t
auto
\n
"
);
else
len
+=
sprintf
(
p
+
len
,
"level:
\t\t
%d
\n
"
,
status
);
break
;
case
IBMACPI_FAN_NONE
:
default:
len
+=
sprintf
(
p
+
len
,
"status:
\t\t
not supported
\n
"
);
}
if
(
fan_control_commands
&
IBMACPI_FAN_CMD_LEVEL
)
{
len
+=
sprintf
(
p
+
len
,
"commands:
\t
level <level>"
);
switch
(
fan_control_access_mode
)
{
case
IBMACPI_FAN_WR_ACPI_SFAN
:
len
+=
sprintf
(
p
+
len
,
" (<level> is 0-7)
\n
"
);
break
;
default:
len
+=
sprintf
(
p
+
len
,
" (<level> is 0-7, "
"auto, disengaged)
\n
"
);
break
;
}
}
if
(
fan_control_commands
&
IBMACPI_FAN_CMD_ENABLE
)
len
+=
sprintf
(
p
+
len
,
"commands:
\t
enable, disable
\n
"
"commands:
\t
watchdog <timeout> (<timeout> is 0 (off), "
"1-120 (seconds))
\n
"
);
if
(
fan_control_commands
&
IBMACPI_FAN_CMD_SPEED
)
len
+=
sprintf
(
p
+
len
,
"commands:
\t
speed <speed>"
" (<speed> is 0-65535)
\n
"
);
return
len
;
}
static
int
fan_
write
(
char
*
buf
)
static
int
fan_
set_level
(
int
level
)
{
char
*
cmd
;
int
level
,
speed
;
while
((
cmd
=
next_cmd
(
&
buf
)))
{
if
(
sfan_handle
&&
sscanf
(
cmd
,
"level %d"
,
&
level
)
==
1
&&
level
>=
0
&&
level
<=
7
)
{
/* 570, 770x-JL */
switch
(
fan_control_access_mode
)
{
case
IBMACPI_FAN_WR_ACPI_SFAN
:
if
(
level
>=
0
&&
level
<=
7
)
{
if
(
!
acpi_evalf
(
sfan_handle
,
NULL
,
NULL
,
"vd"
,
level
))
return
-
EIO
;
}
else
if
(
!
gfan_handle
&&
strlencmp
(
cmd
,
"enable"
)
==
0
)
{
/* all except 570, 600e/x, 770e, 770x */
if
(
!
acpi_ec_write
(
fan_status_offset
,
0x80
))
return
-
EIO
;
}
else
if
(
!
gfan_handle
&&
strlencmp
(
cmd
,
"disable"
)
==
0
)
{
/* all except 570, 600e/x, 770e, 770x */
if
(
!
acpi_ec_write
(
fan_status_offset
,
0x00
))
return
-
EIO
;
}
else
if
(
fans_handle
&&
sscanf
(
cmd
,
"speed %d"
,
&
speed
)
==
1
&&
speed
>=
0
&&
speed
<=
65535
)
{
/* X31, X40 */
}
else
return
-
EINVAL
;
break
;
case
IBMACPI_FAN_WR_ACPI_FANS
:
case
IBMACPI_FAN_WR_TPEC
:
if
((
level
!=
IBMACPI_FAN_EC_AUTO
)
&&
(
level
!=
IBMACPI_FAN_EC_DISENGAGED
)
&&
((
level
<
0
)
||
(
level
>
7
)))
return
-
EINVAL
;
if
(
!
acpi_ec_write
(
fan_status_offset
,
level
))
return
-
EIO
;
else
fan_control_status_known
=
1
;
break
;
default:
return
-
ENXIO
;
}
return
0
;
}
static
int
fan_set_enable
(
void
)
{
u8
s
;
int
rc
;
switch
(
fan_control_access_mode
)
{
case
IBMACPI_FAN_WR_ACPI_FANS
:
case
IBMACPI_FAN_WR_TPEC
:
if
((
rc
=
fan_get_status
(
&
s
))
<
0
)
return
rc
;
/* Don't go out of emergency fan mode */
if
(
s
!=
7
)
s
=
IBMACPI_FAN_EC_AUTO
;
if
(
!
acpi_ec_write
(
fan_status_offset
,
s
))
return
-
EIO
;
else
fan_control_status_known
=
1
;
break
;
case
IBMACPI_FAN_WR_ACPI_SFAN
:
if
((
rc
=
fan_get_status
(
&
s
))
<
0
)
return
rc
;
s
&=
0x07
;
/* Set fan to at least level 4 */
if
(
s
<
4
)
s
=
4
;
if
(
!
acpi_evalf
(
sfan_handle
,
NULL
,
NULL
,
"vd"
,
s
))
return
-
EIO
;
break
;
default:
return
-
ENXIO
;
}
return
0
;
}
static
int
fan_set_disable
(
void
)
{
switch
(
fan_control_access_mode
)
{
case
IBMACPI_FAN_WR_ACPI_FANS
:
case
IBMACPI_FAN_WR_TPEC
:
if
(
!
acpi_ec_write
(
fan_status_offset
,
0x00
))
return
-
EIO
;
else
fan_control_status_known
=
1
;
break
;
case
IBMACPI_FAN_WR_ACPI_SFAN
:
if
(
!
acpi_evalf
(
sfan_handle
,
NULL
,
NULL
,
"vd"
,
0x00
))
return
-
EIO
;
break
;
default:
return
-
ENXIO
;
}
return
0
;
}
static
int
fan_set_speed
(
int
speed
)
{
switch
(
fan_control_access_mode
)
{
case
IBMACPI_FAN_WR_ACPI_FANS
:
if
(
speed
>=
0
&&
speed
<=
65535
)
{
if
(
!
acpi_evalf
(
fans_handle
,
NULL
,
NULL
,
"vddd"
,
speed
,
speed
,
speed
))
return
-
EIO
;
}
else
return
-
EINVAL
;
}
break
;
default:
return
-
ENXIO
;
}
return
0
;
}
static
int
fan_write_cmd_level
(
const
char
*
cmd
,
int
*
rc
)
{
int
level
;
if
(
strlencmp
(
cmd
,
"level auto"
)
==
0
)
level
=
IBMACPI_FAN_EC_AUTO
;
else
if
(
strlencmp
(
cmd
,
"level disengaged"
)
==
0
)
level
=
IBMACPI_FAN_EC_DISENGAGED
;
else
if
(
sscanf
(
cmd
,
"level %d"
,
&
level
)
!=
1
)
return
0
;
if
((
*
rc
=
fan_set_level
(
level
))
==
-
ENXIO
)
printk
(
IBM_ERR
"level command accepted for unsupported "
"access mode %d"
,
fan_control_access_mode
);
return
1
;
}
static
int
fan_write_cmd_enable
(
const
char
*
cmd
,
int
*
rc
)
{
if
(
strlencmp
(
cmd
,
"enable"
)
!=
0
)
return
0
;
if
((
*
rc
=
fan_set_enable
())
==
-
ENXIO
)
printk
(
IBM_ERR
"enable command accepted for unsupported "
"access mode %d"
,
fan_control_access_mode
);
return
1
;
}
static
int
fan_write_cmd_disable
(
const
char
*
cmd
,
int
*
rc
)
{
if
(
strlencmp
(
cmd
,
"disable"
)
!=
0
)
return
0
;
if
((
*
rc
=
fan_set_disable
())
==
-
ENXIO
)
printk
(
IBM_ERR
"disable command accepted for unsupported "
"access mode %d"
,
fan_control_access_mode
);
return
1
;
}
static
int
fan_write_cmd_speed
(
const
char
*
cmd
,
int
*
rc
)
{
int
speed
;
/* TODO:
* Support speed <low> <medium> <high> ? */
if
(
sscanf
(
cmd
,
"speed %d"
,
&
speed
)
!=
1
)
return
0
;
if
((
*
rc
=
fan_set_speed
(
speed
))
==
-
ENXIO
)
printk
(
IBM_ERR
"speed command accepted for unsupported "
"access mode %d"
,
fan_control_access_mode
);
return
1
;
}
static
int
fan_write_cmd_watchdog
(
const
char
*
cmd
,
int
*
rc
)
{
int
interval
;
if
(
sscanf
(
cmd
,
"watchdog %d"
,
&
interval
)
!=
1
)
return
0
;
if
(
interval
<
0
||
interval
>
120
)
*
rc
=
-
EINVAL
;
else
fan_watchdog_maxinterval
=
interval
;
return
1
;
}
static
int
fan_write
(
char
*
buf
)
{
char
*
cmd
;
int
rc
=
0
;
while
(
!
rc
&&
(
cmd
=
next_cmd
(
&
buf
)))
{
if
(
!
((
fan_control_commands
&
IBMACPI_FAN_CMD_LEVEL
)
&&
fan_write_cmd_level
(
cmd
,
&
rc
))
&&
!
((
fan_control_commands
&
IBMACPI_FAN_CMD_ENABLE
)
&&
(
fan_write_cmd_enable
(
cmd
,
&
rc
)
||
fan_write_cmd_disable
(
cmd
,
&
rc
)
||
fan_write_cmd_watchdog
(
cmd
,
&
rc
)))
&&
!
((
fan_control_commands
&
IBMACPI_FAN_CMD_SPEED
)
&&
fan_write_cmd_speed
(
cmd
,
&
rc
))
)
rc
=
-
EINVAL
;
else
if
(
!
rc
)
fan_watchdog_reset
();
}
return
rc
;
}
static
void
fan_watchdog_fire
(
void
*
ignored
)
{
printk
(
IBM_NOTICE
"fan watchdog: enabling fan
\n
"
);
if
(
fan_set_enable
())
{
printk
(
IBM_ERR
"fan watchdog: error while enabling fan
\n
"
);
/* reschedule for later */
fan_watchdog_reset
();
}
}
static
struct
ibm_struct
ibms
[]
=
{
{
.
name
=
"driver"
,
...
...
@@ -1662,6 +2354,7 @@ static struct ibm_struct ibms[] = {
.
type
=
ACPI_SYSTEM_NOTIFY
,
},
#endif
#ifdef CONFIG_ACPI_IBM_BAY
{
.
name
=
"bay"
,
.
init
=
bay_init
,
...
...
@@ -1671,6 +2364,7 @@ static struct ibm_struct ibms[] = {
.
handle
=
&
bay_handle
,
.
type
=
ACPI_SYSTEM_NOTIFY
,
},
#endif
{
.
name
=
"cmos"
,
.
read
=
cmos_read
,
...
...
@@ -1702,6 +2396,8 @@ static struct ibm_struct ibms[] = {
.
name
=
"brightness"
,
.
read
=
brightness_read
,
.
write
=
brightness_write
,
.
init
=
brightness_init
,
.
exit
=
brightness_exit
,
},
{
.
name
=
"volume"
,
...
...
@@ -1712,6 +2408,8 @@ static struct ibm_struct ibms[] = {
.
name
=
"fan"
,
.
read
=
fan_read
,
.
write
=
fan_write
,
.
init
=
fan_init
,
.
exit
=
fan_exit
,
.
experimental
=
1
,
},
};
...
...
@@ -1825,7 +2523,7 @@ static int __init register_driver(struct ibm_struct *ibm)
}
memset
(
ibm
->
driver
,
0
,
sizeof
(
struct
acpi_driver
));
sprintf
(
ibm
->
driver
->
name
,
"%s
/
%s"
,
IBM_NAME
,
ibm
->
name
);
sprintf
(
ibm
->
driver
->
name
,
"%s
_
%s"
,
IBM_NAME
,
ibm
->
name
);
ibm
->
driver
->
ids
=
ibm
->
hid
;
ibm
->
driver
->
ops
.
add
=
&
ibm_device_add
;
...
...
@@ -1954,7 +2652,9 @@ IBM_PARAM(light);
#ifdef CONFIG_ACPI_IBM_DOCK
IBM_PARAM
(
dock
);
#endif
#ifdef CONFIG_ACPI_IBM_BAY
IBM_PARAM
(
bay
);
#endif
IBM_PARAM
(
cmos
);
IBM_PARAM
(
led
);
IBM_PARAM
(
beep
);
...
...
@@ -1971,6 +2671,33 @@ static void acpi_ibm_exit(void)
ibm_exit
(
&
ibms
[
i
]);
remove_proc_entry
(
IBM_DIR
,
acpi_root_dir
);
if
(
ibm_thinkpad_ec_found
)
kfree
(
ibm_thinkpad_ec_found
);
}
static
char
*
__init
check_dmi_for_ec
(
void
)
{
struct
dmi_device
*
dev
=
NULL
;
char
ec_fw_string
[
18
];
/*
* ThinkPad T23 or newer, A31 or newer, R50e or newer,
* X32 or newer, all Z series; Some models must have an
* up-to-date BIOS or they will not be detected.
*
* See http://thinkwiki.org/wiki/List_of_DMI_IDs
*/
while
((
dev
=
dmi_find_device
(
DMI_DEV_TYPE_OEM_STRING
,
NULL
,
dev
)))
{
if
(
sscanf
(
dev
->
name
,
"IBM ThinkPad Embedded Controller -[%17c"
,
ec_fw_string
)
==
1
)
{
ec_fw_string
[
sizeof
(
ec_fw_string
)
-
1
]
=
0
;
ec_fw_string
[
strcspn
(
ec_fw_string
,
" ]"
)]
=
0
;
return
kstrdup
(
ec_fw_string
,
GFP_KERNEL
);
}
}
return
NULL
;
}
static
int
__init
acpi_ibm_init
(
void
)
...
...
@@ -1992,6 +2719,12 @@ static int __init acpi_ibm_init(void)
return
-
ENODEV
;
}
/* Models with newer firmware report the EC in DMI */
ibm_thinkpad_ec_found
=
check_dmi_for_ec
();
if
(
ibm_thinkpad_ec_found
)
printk
(
IBM_INFO
"ThinkPad EC firmware %s
\n
"
,
ibm_thinkpad_ec_found
);
/* these handles are not required */
IBM_HANDLE_INIT
(
vid
);
IBM_HANDLE_INIT
(
vid2
);
...
...
@@ -2004,12 +2737,14 @@ static int __init acpi_ibm_init(void)
IBM_HANDLE_INIT
(
dock
);
#endif
IBM_HANDLE_INIT
(
pci
);
#ifdef CONFIG_ACPI_IBM_BAY
IBM_HANDLE_INIT
(
bay
);
if
(
bay_handle
)
IBM_HANDLE_INIT
(
bay_ej
);
IBM_HANDLE_INIT
(
bay2
);
if
(
bay2_handle
)
IBM_HANDLE_INIT
(
bay2_ej
);
#endif
IBM_HANDLE_INIT
(
beep
);
IBM_HANDLE_INIT
(
ecrd
);
IBM_HANDLE_INIT
(
ecwr
);
...
...
drivers/acpi/toshiba_acpi.c
View file @
6bfe5c9d
...
...
@@ -41,6 +41,8 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/backlight.h>
#include <asm/uaccess.h>
#include <acpi/acpi_drivers.h>
...
...
@@ -210,6 +212,7 @@ static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result)
}
static
struct
proc_dir_entry
*
toshiba_proc_dir
/*= 0*/
;
static
struct
backlight_device
*
toshiba_backlight_device
;
static
int
force_fan
;
static
int
last_key_event
;
static
int
key_event_valid
;
...
...
@@ -271,14 +274,23 @@ dispatch_write(struct file *file, const char __user * buffer,
return
result
;
}
static
char
*
read_lcd
(
char
*
p
)
static
int
get_lcd
(
struct
backlight_device
*
bd
)
{
u32
hci_result
;
u32
value
;
hci_read1
(
HCI_LCD_BRIGHTNESS
,
&
value
,
&
hci_result
);
if
(
hci_result
==
HCI_SUCCESS
)
{
value
=
value
>>
HCI_LCD_BRIGHTNESS_SHIFT
;
return
(
value
>>
HCI_LCD_BRIGHTNESS_SHIFT
);
}
else
return
-
EFAULT
;
}
static
char
*
read_lcd
(
char
*
p
)
{
int
value
=
get_lcd
(
NULL
);
if
(
value
>=
0
)
{
p
+=
sprintf
(
p
,
"brightness: %d
\n
"
,
value
);
p
+=
sprintf
(
p
,
"brightness_levels: %d
\n
"
,
HCI_LCD_BRIGHTNESS_LEVELS
);
...
...
@@ -289,22 +301,34 @@ static char *read_lcd(char *p)
return
p
;
}
static
int
set_lcd
(
int
value
)
{
u32
hci_result
;
value
=
value
<<
HCI_LCD_BRIGHTNESS_SHIFT
;
hci_write1
(
HCI_LCD_BRIGHTNESS
,
value
,
&
hci_result
);
if
(
hci_result
!=
HCI_SUCCESS
)
return
-
EFAULT
;
return
0
;
}
static
int
set_lcd_status
(
struct
backlight_device
*
bd
)
{
return
set_lcd
(
bd
->
props
->
brightness
);
}
static
unsigned
long
write_lcd
(
const
char
*
buffer
,
unsigned
long
count
)
{
int
value
;
u32
hci_resul
t
;
int
ret
=
coun
t
;
if
(
sscanf
(
buffer
,
" brightness : %i"
,
&
value
)
==
1
&&
value
>=
0
&&
value
<
HCI_LCD_BRIGHTNESS_LEVELS
)
{
value
=
value
<<
HCI_LCD_BRIGHTNESS_SHIFT
;
hci_write1
(
HCI_LCD_BRIGHTNESS
,
value
,
&
hci_result
);
if
(
hci_result
!=
HCI_SUCCESS
)
return
-
EFAULT
;
}
else
{
return
-
EINVAL
;
}
return
count
;
value
>=
0
&&
value
<
HCI_LCD_BRIGHTNESS_LEVELS
)
ret
=
set_lcd
(
value
);
else
ret
=
-
EINVAL
;
return
ret
;
}
static
char
*
read_video
(
char
*
p
)
...
...
@@ -506,6 +530,26 @@ static acpi_status __exit remove_device(void)
return
AE_OK
;
}
static
struct
backlight_properties
toshiba_backlight_data
=
{
.
owner
=
THIS_MODULE
,
.
get_brightness
=
get_lcd
,
.
update_status
=
set_lcd_status
,
.
max_brightness
=
HCI_LCD_BRIGHTNESS_LEVELS
-
1
,
};
static
void
__exit
toshiba_acpi_exit
(
void
)
{
if
(
toshiba_backlight_device
)
backlight_device_unregister
(
toshiba_backlight_device
);
remove_device
();
if
(
toshiba_proc_dir
)
remove_proc_entry
(
PROC_TOSHIBA
,
acpi_root_dir
);
return
;
}
static
int
__init
toshiba_acpi_init
(
void
)
{
acpi_status
status
=
AE_OK
;
...
...
@@ -546,17 +590,15 @@ static int __init toshiba_acpi_init(void)
remove_proc_entry
(
PROC_TOSHIBA
,
acpi_root_dir
);
}
return
(
ACPI_SUCCESS
(
status
))
?
0
:
-
ENODEV
;
}
static
void
__exit
toshiba_acpi_exit
(
void
)
{
remove_device
();
if
(
toshiba_proc_dir
)
remove_proc_entry
(
PROC_TOSHIBA
,
acpi_root_dir
);
toshiba_backlight_device
=
backlight_device_register
(
"toshiba"
,
NULL
,
&
toshiba_backlight_data
);
if
(
IS_ERR
(
toshiba_backlight_device
))
{
printk
(
KERN_ERR
"Could not register toshiba backlight device
\n
"
);
toshiba_backlight_device
=
NULL
;
toshiba_acpi_exit
();
}
return
;
return
(
ACPI_SUCCESS
(
status
))
?
0
:
-
ENODEV
;
}
module_init
(
toshiba_acpi_init
);
...
...
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