Commit 6b06d2cc authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6

* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6: (105 commits)
  sonypi: use mutex instead of semaphore
  sony-laptop: remove user visible camera controls as platform attributes
  meye: make meye use sony-laptop instead of sonypi
  sony-laptop: add a meye-usable include file for camera ops
  sony-laptop: complete the motion eye camera support in sony-laptop
  sonypi: try to detect if sony-laptop has already taken one of the known ioports
  sonypi: suggest sonypi users to try sony-laptop instead
  sony-laptop: add edge modem support (also called WWAN)
  sony-laptop: add locking on accesses to the ioport and global vars
  sony-laptop: add camera enable/disable parameter, better handle possible infinite loop
  thinkpad-acpi: make drivers/misc/thinkpad_acpi:fan_mutex static
  ACPI: thinkpad-acpi: add sysfs support to wan and bluetooth subdrivers
  ACPI: thinkpad-acpi: add sysfs support to hotkey subdriver
  ACPI: thinkpad-acpi: improve dock subdriver initialization
  ACPI: thinkpad-acpi: improve debugging for acpi helpers
  ACPI: thinkpad-acpi: improve fan control documentation
  ACPI: thinkpad-acpi: map ENXIO to EINVAL for fan sysfs
  ACPI: thinkpad-acpi: fix a fan watchdog invocation
  ACPI: thinkpad-acpi: do not arm fan watchdog if it would not work
  ACPI: thinkpad-acpi: add a fan-control feature master toggle
  ...
parents b9099ff6 aac60c11
......@@ -181,19 +181,41 @@ and is between 256 and 4096 characters. It is defined in the file
that require a timer override, but don't have
HPET
acpi_dbg_layer= [HW,ACPI]
acpi.debug_layer= [HW,ACPI]
Format: <int>
Each bit of the <int> indicates an ACPI debug layer,
1: enable, 0: disable. It is useful for boot time
debugging. After system has booted up, it can be set
via /proc/acpi/debug_layer.
acpi_dbg_level= [HW,ACPI]
via /sys/module/acpi/parameters/debug_layer.
CONFIG_ACPI_DEBUG must be enabled for this to produce any output.
Available bits (add the numbers together) to enable debug output
for specific parts of the ACPI subsystem:
0x01 utilities 0x02 hardware 0x04 events 0x08 tables
0x10 namespace 0x20 parser 0x40 dispatcher
0x80 executer 0x100 resources 0x200 acpica debugger
0x400 os services 0x800 acpica disassembler.
The number can be in decimal or prefixed with 0x in hex.
Warning: Many of these options can produce a lot of
output and make your system unusable. Be very careful.
acpi.debug_level= [HW,ACPI]
Format: <int>
Each bit of the <int> indicates an ACPI debug level,
1: enable, 0: disable. It is useful for boot time
debugging. After system has booted up, it can be set
via /proc/acpi/debug_level.
via /sys/module/acpi/parameters/debug_level.
CONFIG_ACPI_DEBUG must be enabled for this to produce any output.
Available bits (add the numbers together) to enable different
debug output levels of the ACPI subsystem:
0x01 error 0x02 warn 0x04 init 0x08 debug object
0x10 info 0x20 init names 0x40 parse 0x80 load
0x100 dispatch 0x200 execute 0x400 names 0x800 operation region
0x1000 bfield 0x2000 tables 0x4000 values 0x8000 objects
0x10000 resources 0x20000 user requests 0x40000 package.
The number can be in decimal or prefixed with 0x in hex.
Warning: Many of these options can produce a lot of
output and make your system unusable. Be very careful.
acpi_fake_ecdt [HW,ACPI] Workaround failure due to BIOS lacking ECDT
......
......@@ -3,12 +3,18 @@ Sony Notebook Control Driver (SNC) Readme
Copyright (C) 2004- 2005 Stelian Pop <stelian@popies.net>
Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
This mini-driver drives the SNC device present in the ACPI BIOS of
the Sony Vaio laptops.
This mini-driver drives the SNC and SPIC device present in the ACPI BIOS of the
Sony Vaio laptops. This driver mixes both devices functions under the same
(hopefully consistent) interface. This also means that the sonypi driver is
obsoleted by sony-laptop now.
It gives access to some extra laptop functionalities. In its current
form, this driver let the user set or query the screen brightness
through the backlight subsystem and remove/apply power to some devices.
Fn keys (hotkeys):
------------------
Some models report hotkeys through the SNC or SPIC devices, such events are
reported both through the ACPI subsystem as acpi events and through the INPUT
subsystem. See the logs of acpid or /proc/acpi/event and
/proc/bus/input/devices to find out what those events are and which input
devices are created by the driver.
Backlight control:
------------------
......@@ -39,6 +45,8 @@ The files are:
audiopower power on/off the internal sound card
lanpower power on/off the internal ethernet card
(only in debug mode)
bluetoothpower power on/off the internal bluetooth device
fanspeed get/set the fan speed
Note that some files may be missing if they are not supported
by your particular laptop model.
......@@ -76,9 +84,9 @@ The sony-laptop driver creates, for some of those methods (the most
current ones found on several Vaio models), an entry under
/sys/devices/platform/sony-laptop, just like the 'cdpower' one.
You can create other entries corresponding to your own laptop methods by
further editing the source (see the 'sony_acpi_values' table, and add a new
further editing the source (see the 'sony_nc_values' table, and add a new
entry to this table with your get/set method names using the
HANDLE_NAMES macro).
SNC_HANDLE_NAMES macro).
Your mission, should you accept it, is to try finding out what
those entries are for, by reading/writing random values from/to those
......@@ -87,6 +95,9 @@ files and find out what is the impact on your laptop.
Should you find anything interesting, please report it back to me,
I will not disavow all knowledge of your actions :)
See also http://www.linux.it/~malattia/wiki/index.php/Sony_drivers for other
useful info.
Bugs/Limitations:
-----------------
......
IBM ThinkPad ACPI Extras Driver
ThinkPad ACPI Extras Driver
Version 0.12
17 August 2005
Version 0.14
April 21st, 2007
Borislav Deianov <borislav@users.sf.net>
Henrique de Moraes Holschuh <hmh@hmh.eng.br>
http://ibm-acpi.sf.net/
This is a Linux ACPI driver for the IBM ThinkPad laptops. It supports
various features of these laptops which are accessible through the
ACPI framework but not otherwise supported by the generic Linux ACPI
drivers.
This is a Linux driver for the IBM and Lenovo ThinkPad laptops. It
supports various features of these laptops which are accessible
through the ACPI and ACPI EC framework, but not otherwise fully
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.
Status
......@@ -21,7 +27,7 @@ detailed description):
- Fn key combinations
- Bluetooth enable and disable
- video output switching, expansion control
- video output switching, expansion control
- ThinkLight on and off
- limited docking and undocking
- UltraBay eject
......@@ -32,7 +38,7 @@ detailed description):
- Experimental: embedded controller register dump
- LCD brightness control
- Volume control
- Experimental: fan speed, fan enable/disable
- Fan control and monitoring: fan speed, fan enable/disable
- Experimental: WAN enable and disable
A compatibility table by model and feature is maintained on the web
......@@ -42,6 +48,8 @@ Please include the following information in your report:
- ThinkPad model name
- a copy of your DSDT, from /proc/acpi/dsdt
- a copy of the output of dmidecode, with serial numbers
and UUIDs masked off
- which driver features work and which don't
- the observed behavior of non-working features
......@@ -52,25 +60,85 @@ Installation
------------
If you are compiling this driver as included in the Linux kernel
sources, simply enable the CONFIG_ACPI_IBM option (Power Management /
ACPI / IBM ThinkPad Laptop Extras).
sources, simply enable the CONFIG_THINKPAD_ACPI option, and optionally
enable the CONFIG_THINKPAD_ACPI_BAY option if you want the
thinkpad-specific bay functionality.
Features
--------
The driver creates the /proc/acpi/ibm directory. There is a file under
that directory for each feature described below. Note that while the
driver is still in the alpha stage, the exact proc file format and
commands supported by the various features is guaranteed to change
frequently.
The driver exports two different interfaces to userspace, which can be
used to access the features it provides. One is a legacy procfs-based
interface, which will be removed at some time in the distant future.
The other is a new sysfs-based interface which is not complete yet.
Driver version -- /proc/acpi/ibm/driver
---------------------------------------
The procfs interface creates the /proc/acpi/ibm directory. There is a
file under that directory for each feature it supports. The procfs
interface is mostly frozen, and will change very little if at all: it
will not be extended to add any new functionality in the driver, instead
all new functionality will be implemented on the sysfs interface.
The sysfs interface tries to blend in the generic Linux sysfs subsystems
and classes as much as possible. Since some of these subsystems are not
yet ready or stabilized, it is expected that this interface will change,
and any and all userspace programs must deal with it.
Notes about the sysfs interface:
Unlike what was done with the procfs interface, correctness when talking
to the sysfs interfaces will be enforced, as will correctness in the
thinkpad-acpi's implementation of sysfs interfaces.
Also, any bugs in the thinkpad-acpi sysfs driver code or in the
thinkpad-acpi's implementation of the sysfs interfaces will be fixed for
maximum correctness, even if that means changing an interface in
non-compatible ways. As these interfaces mature both in the kernel and
in thinkpad-acpi, such changes should become quite rare.
Applications interfacing to the thinkpad-acpi sysfs interfaces must
follow all sysfs guidelines and correctly process all errors (the sysfs
interface makes extensive use of errors). File descriptors and open /
close operations to the sysfs inodes must also be properly implemented.
The version of thinkpad-acpi's sysfs interface is exported by the driver
as a driver attribute (see below).
Sysfs driver attributes are on the driver's sysfs attribute space,
for 2.6.20 this is /sys/bus/platform/drivers/thinkpad-acpi/.
Sysfs device attributes are on the driver's sysfs attribute space,
for 2.6.20 this is /sys/devices/platform/thinkpad-acpi/.
Driver version
--------------
procfs: /proc/acpi/ibm/driver
sysfs driver attribute: version
The driver name and version. No commands can be written to this file.
Hot keys -- /proc/acpi/ibm/hotkey
---------------------------------
Sysfs interface version
-----------------------
sysfs driver attribute: interface_version
Version of the thinkpad-acpi sysfs interface, as an unsigned long
(output in hex format: 0xAAAABBCC), where:
AAAA - major revision
BB - minor revision
CC - bugfix revision
The sysfs interface version changelog for the driver can be found at the
end of this document. Changes to the sysfs interface done by the kernel
subsystems are not documented here, nor are they tracked by this
attribute.
Hot keys
--------
procfs: /proc/acpi/ibm/hotkey
sysfs device attribute: hotkey/*
Without this driver, only the Fn-F4 key (sleep button) generates an
ACPI event. With the driver loaded, the hotkey feature enabled and the
......@@ -84,15 +152,6 @@ All labeled Fn-Fx key combinations generate distinct events. In
addition, the lid microswitch and some docking station buttons may
also generate such events.
The following commands can be written to this file:
echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature
echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature
echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys
echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys
... any other 4-hex-digit mask ...
echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
The bit mask allows some control over which hot keys generate ACPI
events. Not all bits in the mask can be modified. Not all bits that
can be modified do anything. Not all hot keys can be individually
......@@ -124,15 +183,77 @@ buttons do not generate ACPI events even with this driver. They *can*
be used through the "ThinkPad Buttons" utility, see
http://www.nongnu.org/tpb/
Bluetooth -- /proc/acpi/ibm/bluetooth
-------------------------------------
procfs notes:
The following commands can be written to the /proc/acpi/ibm/hotkey file:
echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature
echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature
echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys
echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys
... any other 4-hex-digit mask ...
echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
sysfs notes:
The hot keys attributes are in a hotkey/ subdirectory off the
thinkpad device.
bios_enabled:
Returns the status of the hot keys feature when
thinkpad-acpi was loaded. Upon module unload, the hot
key feature status will be restored to this value.
0: hot keys were disabled
1: hot keys were enabled
bios_mask:
Returns the hot keys mask when thinkpad-acpi was loaded.
Upon module unload, the hot keys mask will be restored
to this value.
enable:
Enables/disables the hot keys feature, and reports
current status of the hot keys feature.
0: disables the hot keys feature / feature disabled
1: enables the hot keys feature / feature enabled
mask:
bit mask to enable ACPI event generation for each hot
key (see above). Returns the current status of the hot
keys mask, and allows one to modify it.
This feature shows the presence and current state of a Bluetooth
device. If Bluetooth is installed, the following commands can be used:
Bluetooth
---------
procfs: /proc/acpi/ibm/bluetooth
sysfs device attribute: bluetooth/enable
This feature shows the presence and current state of a ThinkPad
Bluetooth device in the internal ThinkPad CDC slot.
Procfs notes:
If Bluetooth is installed, the following commands can be used:
echo enable > /proc/acpi/ibm/bluetooth
echo disable > /proc/acpi/ibm/bluetooth
Sysfs notes:
If the Bluetooth CDC card is installed, it can be enabled /
disabled through the "bluetooth/enable" thinkpad-acpi device
attribute, and its current status can also be queried.
enable:
0: disables Bluetooth / Bluetooth is disabled
1: enables Bluetooth / Bluetooth is enabled.
Note: this interface will be probably be superseeded by the
generic rfkill class.
Video output control -- /proc/acpi/ibm/video
--------------------------------------------
......@@ -209,7 +330,7 @@ hot plugging of devices in the Linux ACPI framework. If the laptop was
booted while not in the dock, the following message is shown in the
logs:
Mar 17 01:42:34 aero kernel: ibm_acpi: dock device not present
Mar 17 01:42:34 aero kernel: thinkpad_acpi: dock device not present
In this case, no dock-related events are generated but the dock and
undock commands described below still work. They can be executed
......@@ -269,7 +390,7 @@ This is due to the current lack of support for hot plugging of devices
in the Linux ACPI framework. If the laptop was booted without the
UltraBay, the following message is shown in the logs:
Mar 17 01:42:34 aero kernel: ibm_acpi: bay device not present
Mar 17 01:42:34 aero kernel: thinkpad_acpi: bay device not present
In this case, no bay-related events are generated but the eject
command described below still works. It can be executed manually or
......@@ -313,23 +434,19 @@ supported. Use "eject2" instead of "eject" for the second bay.
Note: the UltraBay eject support on the 600e/x, A22p and A3x is
EXPERIMENTAL and may not work as expected. USE WITH CAUTION!
CMOS control -- /proc/acpi/ibm/cmos
-----------------------------------
CMOS control
------------
procfs: /proc/acpi/ibm/cmos
sysfs device attribute: cmos_command
This feature is used internally by the ACPI firmware to control the
ThinkLight on most newer ThinkPad models. It may also control LCD
brightness, sounds volume and more, but only on some models.
The commands are non-negative integer numbers:
echo 0 >/proc/acpi/ibm/cmos
echo 1 >/proc/acpi/ibm/cmos
echo 2 >/proc/acpi/ibm/cmos
...
The range of valid numbers is 0 to 21, but not all have an effect and
the behavior varies from model to model. Here is the behavior on the
X40 (tpb is the ThinkPad Buttons utility):
The range of valid cmos command numbers is 0 to 21, but not all have an
effect and the behavior varies from model to model. Here is the behavior
on the X40 (tpb is the ThinkPad Buttons utility):
0 - no effect but tpb reports "Volume down"
1 - no effect but tpb reports "Volume up"
......@@ -342,6 +459,9 @@ X40 (tpb is the ThinkPad Buttons utility):
13 - ThinkLight off
14 - no effect but tpb reports ThinkLight status change
The cmos command interface is prone to firmware split-brain problems, as
in newer ThinkPads it is just a compatibility layer.
LED control -- /proc/acpi/ibm/led
---------------------------------
......@@ -393,17 +513,17 @@ X40:
16 - one medium-pitched beep repeating constantly, stop with 17
17 - stop 16
Temperature sensors -- /proc/acpi/ibm/thermal
---------------------------------------------
Temperature sensors
-------------------
procfs: /proc/acpi/ibm/thermal
sysfs device attributes: (hwmon) temp*_input
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 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.
sensors on newer ThinkPads.
EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the
implementation directly accesses hardware registers and may not work as
......@@ -460,6 +580,20 @@ The A31 has a very atypical layout for the thermal sensors
8: Bay Battery: secondary sensor
Procfs notes:
Readings from sensors that are not available return -128.
No commands can be written to this file.
Sysfs notes:
Sensors that are not available return the ENXIO error. This
status may change at runtime, as there are hotplug thermal
sensors, like those inside the batteries and docks.
thinkpad-acpi thermal sensors are reported through the hwmon
subsystem, and follow all of the hwmon guidelines at
Documentation/hwmon.
EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump
------------------------------------------------------------------------
......@@ -472,7 +606,7 @@ This feature dumps the values of 256 embedded controller
registers. Values which have changed since the last time the registers
were dumped are marked with a star:
[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f
EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00
EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00
......@@ -503,7 +637,7 @@ vary. The second ensures that the fan-related values do vary, since
the fan speed fluctuates a bit. The third will (hopefully) mark the
fan register with a star:
[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f
EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00
EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00
......@@ -533,19 +667,59 @@ registers contain the current battery capacity, etc. If you experiment
with this, do send me your results (including some complete dumps with
a description of the conditions when they were taken.)
LCD brightness control -- /proc/acpi/ibm/brightness
---------------------------------------------------
LCD brightness control
----------------------
procfs: /proc/acpi/ibm/brightness
sysfs backlight device "thinkpad_screen"
This feature allows software control of the LCD brightness on ThinkPad
models which don't have a hardware brightness slider. The available
commands are:
models which don't have a hardware brightness slider.
It has some limitations: the LCD backlight cannot be actually turned on or off
by this interface, and in many ThinkPad models, the "dim while on battery"
functionality will be enabled by the BIOS when this interface is used, and
cannot be controlled.
The backlight control has eight levels, ranging from 0 to 7. Some of the
levels may not be distinct.
Procfs notes:
The available commands are:
echo up >/proc/acpi/ibm/brightness
echo down >/proc/acpi/ibm/brightness
echo 'level <level>' >/proc/acpi/ibm/brightness
The <level> number range is 0 to 7, although not all of them may be
distinct. The current brightness level is shown in the file.
Sysfs notes:
The interface is implemented through the backlight sysfs class, which is poorly
documented at this time.
Locate the thinkpad_screen device under /sys/class/backlight, and inside it
there will be the following attributes:
max_brightness:
Reads the maximum brightness the hardware can be set to.
The minimum is always zero.
actual_brightness:
Reads what brightness the screen is set to at this instant.
brightness:
Writes request the driver to change brightness to the given
value. Reads will tell you what brightness the driver is trying
to set the display to when "power" is set to zero and the display
has not been dimmed by a kernel power management event.
power:
power management mode, where 0 is "display on", and 1 to 3 will
dim the display backlight to brightness level 0 because
thinkpad-acpi cannot really turn the backlight off. Kernel
power management events can temporarily increase the current
power management level, i.e. they can dim the display.
Volume control -- /proc/acpi/ibm/volume
---------------------------------------
......@@ -563,41 +737,42 @@ distinct. The unmute the volume after the mute command, use either the
up or down command (the level command will not unmute the volume).
The current volume level and mute state is shown in the file.
EXPERIMENTAL: fan speed, fan enable/disable -- /proc/acpi/ibm/fan
-----------------------------------------------------------------
Fan control and monitoring: fan speed, fan enable/disable
---------------------------------------------------------
This 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.
procfs: /proc/acpi/ibm/fan
sysfs device attributes: (hwmon) fan_input, pwm1, pwm1_enable
NOTE NOTE NOTE: fan control operations are disabled by default for
safety reasons. To enable them, the module parameter "fan_control=1"
must be given to thinkpad-acpi.
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
to work on later R, T, X and Z 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.
Fan levels:
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.
Most ThinkPad fans work in "levels" at the firmware interface. 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.
The fan may be enabled or disabled with the following commands:
Level "auto" means the EC changes the fan level according to some
internal algorithm, usually based on readings from the thermal sensors.
echo enable >/proc/acpi/ibm/fan
echo disable >/proc/acpi/ibm/fan
There is also a "full-speed" level, also known as "disengaged" level.
In this level, 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.
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.
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 full-speed level may take up to two minutes to ramp up to
maximum speed, and in some ThinkPads, the tachometer readings go stale
while the EC is transitioning to the full-speed level.
WARNING WARNING WARNING: do not leave the fan disabled unless you are
monitoring all of the temperature sensor readings and you are ready to
......@@ -615,46 +790,146 @@ 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 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 thinkpad-acpi.
The thinkpad-acpi kernel driver can be programmed to revert the fan
level to a safe setting if userspace does not issue one of the procfs
fan commands: "enable", "disable", "level" or "watchdog", or if there
are no writes to pwm1_enable (or to pwm1 *if and only if* pwm1_enable is
set to 1, manual mode) within a configurable amount of time of up to
120 seconds. This functionality is called fan safety watchdog.
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" procfs fan
commands, or the hwmon fan control sysfs interface.
Procfs notes:
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.
The fan level can be controlled with the command:
echo 'level <level>' > /proc/acpi/ibm/thermal
echo 'level <level>' > /proc/acpi/ibm/fan
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.
Where <level> is an integer from 0 to 7, or one of the words "auto" or
"full-speed" (without the quotes). Not all ThinkPads support the "auto"
and "full-speed" levels. The driver accepts "disengaged" as an alias for
"full-speed", and reports it as "disengaged" for backwards
compatibility.
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
controlled to a certain degree. Once the fan is running, it can be
forced to run faster or slower with the following command:
echo 'speed <speed>' > /proc/acpi/ibm/thermal
echo 'speed <speed>' > /proc/acpi/ibm/fan
The sustainable range of fan speeds on the X40 appears to be from
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.
The sustainable range of fan speeds on the X40 appears to be from 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. This functionality
is incomplete, and not available through the sysfs interface.
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.
To program the safety watchdog, use the "watchdog" command.
EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan
---------------------------------------
echo 'watchdog <interval in seconds>' > /proc/acpi/ibm/fan
If you want to disable the watchdog, use 0 as the interval.
Sysfs notes:
The sysfs interface follows the hwmon subsystem guidelines for the most
part, and the exception is the fan safety watchdog.
Writes to any of the sysfs attributes may return the EINVAL error if
that operation is not supported in a given ThinkPad or if the parameter
is out-of-bounds, and EPERM if it is forbidden. They may also return
EINTR (interrupted system call), and EIO (I/O error while trying to talk
to the firmware).
Features not yet implemented by the driver return ENOSYS.
hwmon device attribute pwm1_enable:
0: PWM offline (fan is set to full-speed mode)
1: Manual PWM control (use pwm1 to set fan level)
2: Hardware PWM control (EC "auto" mode)
3: reserved (Software PWM control, not implemented yet)
Modes 0 and 2 are not supported by all ThinkPads, and the
driver is not always able to detect this. If it does know a
mode is unsupported, it will return -EINVAL.
hwmon device attribute pwm1:
Fan level, scaled from the firmware values of 0-7 to the hwmon
scale of 0-255. 0 means fan stopped, 255 means highest normal
speed (level 7).
This attribute only commands the fan if pmw1_enable is set to 1
(manual PWM control).
hwmon device attribute fan1_input:
Fan tachometer reading, in RPM. May go stale on certain
ThinkPads while the EC transitions the PWM to offline mode,
which can take up to two minutes. May return rubbish on older
ThinkPads.
driver attribute fan_watchdog:
Fan safety watchdog timer interval, in seconds. Minimum is
1 second, maximum is 120 seconds. 0 disables the watchdog.
To stop the fan: set pwm1 to zero, and pwm1_enable to 1.
To start the fan in a safe mode: set pwm1_enable to 2. If that fails
with EINVAL, try to set pwm1_enable to 1 and pwm1 to at least 128 (255
would be the safest choice, though).
EXPERIMENTAL: WAN
-----------------
procfs: /proc/acpi/ibm/wan
sysfs device attribute: wwan/enable
This 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.
This feature shows the presence and current state of a WAN (Sierra
Wireless EV-DO) device. If WAN is installed, the following commands can
be used:
This feature shows the presence and current state of a W-WAN (Sierra
Wireless EV-DO) device.
It was tested on a Lenovo Thinkpad X60. It should probably work on other
Thinkpad models which come with this module installed.
Procfs notes:
If the W-WAN card is installed, the following commands can be used:
echo enable > /proc/acpi/ibm/wan
echo disable > /proc/acpi/ibm/wan
It was tested on a Lenovo Thinkpad X60. It should probably work on other
Thinkpad models which come with this module installed.
Sysfs notes:
If the W-WAN card is installed, it can be enabled /
disabled through the "wwan/enable" thinkpad-acpi device
attribute, and its current status can also be queried.
enable:
0: disables WWAN card / WWAN card is disabled
1: enables WWAN card / WWAN card is enabled.
Note: this interface will be probably be superseeded by the
generic rfkill class.
Multiple Commands, Module Parameters
------------------------------------
......@@ -665,64 +940,42 @@ separating them with commas, for example:
echo enable,0xffff > /proc/acpi/ibm/hotkey
echo lcd_disable,crt_enable > /proc/acpi/ibm/video
Commands can also be specified when loading the ibm_acpi module, for
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
---------------------
The ACPI support in the kernel is intended to be used in conjunction
with a user-space daemon, acpid. The configuration files for this
daemon control what actions are taken in response to various ACPI
events. An example set of configuration files are included in the
config/ directory of the tarball package available on the web
site. Note that these are provided for illustration purposes only and
may need to be adapted to your particular setup.
The following utility scripts are used by the example action
scripts (included with ibm-acpi for completeness):
/usr/local/sbin/idectl -- from the hdparm source distribution,
see http://www.ibiblio.org/pub/Linux/system/hardware
/usr/local/sbin/laptop_mode -- from the Linux kernel source
distribution, see Documentation/laptop-mode.txt
/sbin/service -- comes with Redhat/Fedora distributions
/usr/sbin/hibernate -- from the Software Suspend 2 distribution,
see http://softwaresuspend.berlios.de/
Toan T Nguyen <ntt@physics.ucla.edu> notes that Suse uses the
powersave program to suspend ('powersave --suspend-to-ram') or
hibernate ('powersave --suspend-to-disk'). This means that the
hibernate script is not needed on that distribution.
Henrik Brix Andersen <brix@gentoo.org> has written a Gentoo ACPI event
handler script for the X31. You can get the latest version from
http://dev.gentoo.org/~brix/files/x31.sh
David Schweikert <dws@ee.eth.ch> has written an alternative blank.sh
script which works on Debian systems. This scripts has now been
extended to also work on Fedora systems and included as the default
blank.sh in the distribution.
Commands can also be specified when loading the thinkpad-acpi module,
for example:
modprobe thinkpad_acpi hotkey=enable,0xffff video=auto_disable
Enabling debugging output
-------------------------
The module takes a debug paramater which can be used to selectively
enable various classes of debugging output, for example:
modprobe ibm_acpi debug=0xffff
will enable all debugging output classes. It takes a bitmask, so
to enable more than one output class, just add their values.
Debug bitmask Description
0x0001 Initialization and probing
0x0002 Removal
There is also a kernel build option to enable more debugging
information, which may be necessary to debug driver problems.
The level of debugging information output by the driver can be changed
at runtime through sysfs, using the driver attribute debug_level. The
attribute takes the same bitmask as the debug module parameter above.
Force loading of module
-----------------------
If thinkpad-acpi refuses to detect your ThinkPad, you can try to specify
the module parameter force_load=1. Regardless of whether this works or
not, please contact ibm-acpi-devel@lists.sourceforge.net with a report.
Sysfs interface changelog:
0x000100: Initial sysfs support, as a single platform driver and
device.
......@@ -5,10 +5,9 @@ Vaio Picturebook Motion Eye Camera Driver Readme
Copyright (C) 2000 Andrew Tridgell <tridge@samba.org>
This driver enable the use of video4linux compatible applications with the
Motion Eye camera. This driver requires the "Sony Vaio Programmable I/O
Control Device" driver (which can be found in the "Character drivers"
section of the kernel configuration utility) to be compiled and installed
(using its "camera=1" parameter).
Motion Eye camera. This driver requires the "Sony Laptop Extras" driver (which
can be found in the "Misc devices" section of the kernel configuration utility)
to be compiled and installed (using its "camera=1" parameter).
It can do at maximum 30 fps @ 320x240 or 15 fps @ 640x480.
......
......@@ -1658,15 +1658,6 @@ W: http://www.ia64-linux.org/
T: git kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
S: Maintained
IBM ACPI EXTRAS DRIVER
P: Henrique de Moraes Holschuh
M: ibm-acpi@hmh.eng.br
L: ibm-acpi-devel@lists.sourceforge.net
W: http://ibm-acpi.sourceforge.net
W: http://thinkwiki.org/wiki/Ibm-acpi
T: git repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
S: Maintained
SN-IA64 (Itanium) SUB-PLATFORM
P: Jes Sorensen
M: jes@sgi.com
......@@ -3166,6 +3157,15 @@ P: Chris Zankel
M: chris@zankel.net
S: Maintained
THINKPAD ACPI EXTRAS DRIVER
P: Henrique de Moraes Holschuh
M: ibm-acpi@hmh.eng.br
L: ibm-acpi-devel@lists.sourceforge.net
W: http://ibm-acpi.sourceforge.net
W: http://thinkwiki.org/wiki/Ibm-acpi
T: git repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
S: Maintained
UltraSPARC (sparc64):
P: David S. Miller
M: davem@davemloft.net
......
......@@ -23,10 +23,13 @@ static int __init nvidia_hpet_check(struct acpi_table_header *header)
static int __init check_bridge(int vendor, int device)
{
#ifdef CONFIG_ACPI
static int warned;
/* According to Nvidia all timer overrides are bogus unless HPET
is enabled. */
if (!acpi_use_timer_override && vendor == PCI_VENDOR_ID_NVIDIA) {
if (acpi_table_parse(ACPI_SIG_HPET, nvidia_hpet_check)) {
if (!warned && acpi_table_parse(ACPI_SIG_HPET,
nvidia_hpet_check)) {
warned = 1;
acpi_skip_timer_override = 1;
printk(KERN_INFO "Nvidia board "
"detected. Ignoring ACPI "
......
......@@ -85,8 +85,8 @@ config ACPI_PROCFS
depends on ACPI
default y
---help---
Procfs interface for ACPI is made optional for back-compatible.
As the same functions are duplicated in sysfs interface
The Procfs interface for ACPI is made optional for backward compatibility.
As the same functions are duplicated in the sysfs interface
and this proc interface will be removed some time later,
it's marked as deprecated.
( /proc/acpi/debug_layer && debug_level are deprecated by
......@@ -218,43 +218,6 @@ config ACPI_ASUS
NOTE: This driver is deprecated and will probably be removed soon,
use asus-laptop instead.
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
output switching, ThinkLight control, UltraBay eject and more.
For more information about this driver see <file:Documentation/ibm-acpi.txt>
and <http://ibm-acpi.sf.net/> .
If you have an IBM ThinkPad laptop, say Y or M here.
config ACPI_IBM_DOCK
bool "Legacy Docking Station Support"
depends on ACPI_IBM
depends on ACPI_DOCK=n
default n
---help---
Allows the ibm_acpi driver to handle docking station events.
This support is obsoleted by CONFIG_HOTPLUG_PCI_ACPI. It will
allow locking and removing the laptop from the docking station,
but will not properly connect PCI devices.
If you are not sure, say N here.
config ACPI_IBM_BAY
bool "Legacy Removable Bay Support"
depends on ACPI_IBM
default y
---help---
Allows the ibm_acpi driver to handle removable bays. It will allow
disabling the device in the bay, and also generate notifications when
the bay lever is ejected or inserted.
If you are not sure, say Y here.
config ACPI_TOSHIBA
tristate "Toshiba Laptop Extras"
depends on X86
......@@ -388,11 +351,10 @@ config ACPI_HOTPLUG_MEMORY
config ACPI_SBS
tristate "Smart Battery System (EXPERIMENTAL)"
depends on X86 && I2C
depends on X86
depends on EXPERIMENTAL
help
This driver adds support for the Smart Battery System.
Depends on I2C (Device Drivers ---> I2C support)
A "Smart Battery" is quite old and quite rare compared
to today's ACPI "Control Method" battery.
......
#
# Makefile for the Linux ACPI interpreter
#
#
export ACPI_CFLAGS
......@@ -32,16 +32,17 @@ obj-y += osl.o utils.o \
processor-objs += processor_core.o processor_throttling.o \
processor_idle.o processor_thermal.o
ifdef CONFIG_CPU_FREQ
processor-objs += processor_perflib.o
processor-objs += processor_perflib.o
endif
obj-y += sleep/
obj-y += bus.o glue.o
obj-y += scan.o
# Keep EC driver first. Initialization of others depend on it.
obj-$(CONFIG_ACPI_EC) += ec.o
obj-$(CONFIG_ACPI_AC) += ac.o
obj-$(CONFIG_ACPI_BATTERY) += battery.o
obj-$(CONFIG_ACPI_BUTTON) += button.o
obj-$(CONFIG_ACPI_EC) += ec.o
obj-$(CONFIG_ACPI_FAN) += fan.o
obj-$(CONFIG_ACPI_DOCK) += dock.o
obj-$(CONFIG_ACPI_BAY) += bay.o
......@@ -55,8 +56,7 @@ obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o
obj-$(CONFIG_ACPI_DEBUG) += debug.o
obj-$(CONFIG_ACPI_NUMA) += numa.o
obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o
obj-y += cm_sbs.o
obj-$(CONFIG_ACPI_SBS) += i2c_ec.o sbs.o
obj-$(CONFIG_ACPI_SBS) += sbs.o
......@@ -44,11 +44,6 @@ MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
MODULE_DESCRIPTION("Hotplug Mem Driver");
MODULE_LICENSE("GPL");
/* ACPI _STA method values */
#define ACPI_MEMORY_STA_PRESENT (0x00000001UL)
#define ACPI_MEMORY_STA_ENABLED (0x00000002UL)
#define ACPI_MEMORY_STA_FUNCTIONAL (0x00000008UL)
/* Memory Device States */
#define MEMORY_INVALID_STATE 0
#define MEMORY_POWER_ON_STATE 1
......@@ -204,9 +199,9 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
* Check for device status. Device should be
* present/enabled/functioning.
*/
if (!((current_status & ACPI_MEMORY_STA_PRESENT)
&& (current_status & ACPI_MEMORY_STA_ENABLED)
&& (current_status & ACPI_MEMORY_STA_FUNCTIONAL)))
if (!((current_status & ACPI_STA_DEVICE_PRESENT)
&& (current_status & ACPI_STA_DEVICE_ENABLED)
&& (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
return -ENODEV;
return 0;
......@@ -286,7 +281,7 @@ static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
return -ENODEV;
/* Check for device status. Device should be disabled */
if (current_status & ACPI_MEMORY_STA_ENABLED)
if (current_status & ACPI_STA_DEVICE_ENABLED)
return -EINVAL;
return 0;
......
......@@ -103,7 +103,9 @@ int acpi_bus_get_status(struct acpi_device *device)
else if (device->parent)
device->status = device->parent->status;
else
STRUCT_TO_INT(device->status) = 0x0F;
STRUCT_TO_INT(device->status) =
ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING;
if (device->status.functional && !device->status.present) {
printk(KERN_WARNING PREFIX "Device [%s] status [%08x]: "
......
......@@ -49,8 +49,6 @@ MODULE_AUTHOR("Anil S Keshavamurthy");
MODULE_DESCRIPTION("ACPI container driver");
MODULE_LICENSE("GPL");
#define ACPI_STA_PRESENT (0x00000001)
static int acpi_container_add(struct acpi_device *device);
static int acpi_container_remove(struct acpi_device *device, int type);
......@@ -75,13 +73,13 @@ static int is_device_present(acpi_handle handle)
status = acpi_get_handle(handle, "_STA", &temp);
if (ACPI_FAILURE(status))
return 1; /* _STA not found, assmue device present */
return 1; /* _STA not found, assume device present */
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
if (ACPI_FAILURE(status))
return 0; /* Firmware error */
return ((sta & ACPI_STA_PRESENT) == ACPI_STA_PRESENT);
return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT);
}
/*******************************************************************/
......
......@@ -29,6 +29,7 @@
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <linux/stddef.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
......@@ -667,6 +668,23 @@ static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
}
DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
/*
* show_dock_uid - read method for "uid" file in sysfs
*/
static ssize_t show_dock_uid(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long lbuf;
acpi_status status = acpi_evaluate_integer(dock_station->handle, "_UID", NULL, &lbuf);
if(ACPI_FAILURE(status)) {
return 0;
}
return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf);
}
DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
/**
* dock_add - add a new dock station
* @handle: the dock station handle
......@@ -715,6 +733,13 @@ static int dock_add(acpi_handle handle)
kfree(dock_station);
return ret;
}
ret = device_create_file(&dock_device.dev, &dev_attr_uid);
if (ret) {
printk("Error %d adding sysfs file\n", ret);
platform_device_unregister(&dock_device);
kfree(dock_station);
return ret;
}
/* Find dependent devices */
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
......
/*
* acpi_ec.c - ACPI Embedded Controller Driver ($Revision: 38 $)
* ec.c - ACPI Embedded Controller Driver (v2.0)
*
* Copyright (C) 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
* Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com>
* Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
......@@ -91,9 +93,9 @@ static struct acpi_driver acpi_ec_driver = {
};
/* If we find an EC via the ECDT, we need to keep a ptr to its context */
/* External interfaces use first EC only, so remember */
static struct acpi_ec {
acpi_handle handle;
unsigned long uid;
unsigned long gpe;
unsigned long command_addr;
unsigned long data_addr;
......@@ -101,12 +103,8 @@ static struct acpi_ec {
struct mutex lock;
atomic_t query_pending;
atomic_t event_count;
atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */
wait_queue_head_t wait;
} *ec_ecdt;
/* External interfaces use first EC only, so remember */
static struct acpi_device *first_ec;
} *boot_ec, *first_ec;
/* --------------------------------------------------------------------------
Transaction Management
......@@ -173,56 +171,6 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, unsigned count)
return -ETIME;
}
#ifdef ACPI_FUTURE_USAGE
/*
* Note: samsung nv5000 doesn't work with ec burst mode.
* http://bugzilla.kernel.org/show_bug.cgi?id=4980
*/
int acpi_ec_enter_burst_mode(struct acpi_ec *ec)
{
u8 tmp = 0;
u8 status = 0;
status = acpi_ec_read_status(ec);
if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) {
status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
if (status)
goto end;
acpi_ec_write_cmd(ec, ACPI_EC_BURST_ENABLE);
status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1);
tmp = acpi_ec_read_data(ec);
if (tmp != 0x90) { /* Burst ACK byte */
return -EINVAL;
}
}
atomic_set(&ec->leaving_burst, 0);
return 0;
end:
ACPI_EXCEPTION((AE_INFO, status, "EC wait, burst mode"));
return -1;
}
int acpi_ec_leave_burst_mode(struct acpi_ec *ec)
{
u8 status = 0;
status = acpi_ec_read_status(ec);
if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)) {
status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
if (status)
goto end;
acpi_ec_write_cmd(ec, ACPI_EC_BURST_DISABLE);
acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
}
atomic_set(&ec->leaving_burst, 1);
return 0;
end:
ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode"));
return -1;
}
#endif /* ACPI_FUTURE_USAGE */
static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
const u8 * wdata, unsigned wdata_len,
u8 * rdata, unsigned rdata_len)
......@@ -312,6 +260,21 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command,
return status;
}
/*
* Note: samsung nv5000 doesn't work with ec burst mode.
* http://bugzilla.kernel.org/show_bug.cgi?id=4980
*/
int acpi_ec_burst_enable(struct acpi_ec *ec)
{
u8 d;
return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1);
}
int acpi_ec_burst_disable(struct acpi_ec *ec)
{
return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0);
}
static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data)
{
int result;
......@@ -333,18 +296,33 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data)
/*
* Externally callable EC access functions. For now, assume 1 EC only
*/
int ec_burst_enable(void)
{
if (!first_ec)
return -ENODEV;
return acpi_ec_burst_enable(first_ec);
}
EXPORT_SYMBOL(ec_burst_enable);
int ec_burst_disable(void)
{
if (!first_ec)
return -ENODEV;
return acpi_ec_burst_disable(first_ec);
}
EXPORT_SYMBOL(ec_burst_disable);
int ec_read(u8 addr, u8 * val)
{
struct acpi_ec *ec;
int err;
u8 temp_data;
if (!first_ec)
return -ENODEV;
ec = acpi_driver_data(first_ec);
err = acpi_ec_read(ec, addr, &temp_data);
err = acpi_ec_read(first_ec, addr, &temp_data);
if (!err) {
*val = temp_data;
......@@ -357,15 +335,12 @@ EXPORT_SYMBOL(ec_read);
int ec_write(u8 addr, u8 val)
{
struct acpi_ec *ec;
int err;
if (!first_ec)
return -ENODEV;
ec = acpi_driver_data(first_ec);
err = acpi_ec_write(ec, addr, val);
err = acpi_ec_write(first_ec, addr, val);
return err;
}
......@@ -376,14 +351,10 @@ int ec_transaction(u8 command,
const u8 * wdata, unsigned wdata_len,
u8 * rdata, unsigned rdata_len)
{
struct acpi_ec *ec;
if (!first_ec)
return -ENODEV;
ec = acpi_driver_data(first_ec);
return acpi_ec_transaction(ec, command, wdata,
return acpi_ec_transaction(first_ec, command, wdata,
wdata_len, rdata, rdata_len);
}
......@@ -420,7 +391,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data)
static void acpi_ec_gpe_query(void *ec_cxt)
{
struct acpi_ec *ec = (struct acpi_ec *)ec_cxt;
struct acpi_ec *ec = ec_cxt;
u8 value = 0;
char object_name[8];
......@@ -438,8 +409,9 @@ static u32 acpi_ec_gpe_handler(void *data)
{
acpi_status status = AE_OK;
u8 value;
struct acpi_ec *ec = (struct acpi_ec *)data;
struct acpi_ec *ec = data;
atomic_inc(&ec->event_count);
if (acpi_ec_mode == EC_INTR) {
wake_up(&ec->wait);
}
......@@ -482,7 +454,7 @@ acpi_ec_space_handler(u32 function,
void *handler_context, void *region_context)
{
int result = 0;
struct acpi_ec *ec = NULL;
struct acpi_ec *ec = handler_context;
u64 temp = *value;
acpi_integer f_v = 0;
int i = 0;
......@@ -494,8 +466,6 @@ acpi_ec_space_handler(u32 function,
return AE_BAD_PARAMETER;
}
ec = (struct acpi_ec *)handler_context;
next_byte:
switch (function) {
case ACPI_READ:
......@@ -551,18 +521,16 @@ static struct proc_dir_entry *acpi_ec_dir;
static int acpi_ec_read_info(struct seq_file *seq, void *offset)
{
struct acpi_ec *ec = (struct acpi_ec *)seq->private;
struct acpi_ec *ec = seq->private;
if (!ec)
goto end;
seq_printf(seq, "gpe: 0x%02x\n", (u32) ec->gpe);
seq_printf(seq, "ports: 0x%02x, 0x%02x\n",
(u32) ec->command_addr, (u32) ec->data_addr);
seq_printf(seq, "use global lock: %s\n",
seq_printf(seq, "gpe:\t\t\t0x%02x\n", (u32) ec->gpe);
seq_printf(seq, "ports:\t\t\t0x%02x, 0x%02x\n",
(unsigned)ec->command_addr, (unsigned)ec->data_addr);
seq_printf(seq, "use global lock:\t%s\n",
ec->global_lock ? "yes" : "no");
acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
end:
return 0;
}
......@@ -619,154 +587,122 @@ static int acpi_ec_remove_fs(struct acpi_device *device)
/* --------------------------------------------------------------------------
Driver Interface
-------------------------------------------------------------------------- */
static acpi_status
ec_parse_io_ports(struct acpi_resource *resource, void *context);
static acpi_status
ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval);
static struct acpi_ec *make_acpi_ec(void)
{
struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
if (!ec)
return NULL;
atomic_set(&ec->query_pending, 1);
atomic_set(&ec->event_count, 1);
mutex_init(&ec->lock);
init_waitqueue_head(&ec->wait);
return ec;
}
static int acpi_ec_add(struct acpi_device *device)
{
int result = 0;
acpi_status status = AE_OK;
struct acpi_ec *ec = NULL;
if (!device)
return -EINVAL;
ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
if (!ec)
return -ENOMEM;
ec->handle = device->handle;
ec->uid = -1;
mutex_init(&ec->lock);
atomic_set(&ec->query_pending, 0);
atomic_set(&ec->event_count, 1);
if (acpi_ec_mode == EC_INTR) {
atomic_set(&ec->leaving_burst, 1);
init_waitqueue_head(&ec->wait);
}
strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_EC_CLASS);
acpi_driver_data(device) = ec;
/* Use the global lock for all EC transactions? */
acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock);
/* XXX we don't test uids, because on some boxes ecdt uid = 0, see:
http://bugzilla.kernel.org/show_bug.cgi?id=6111 */
if (ec_ecdt) {
acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler);
acpi_remove_gpe_handler(NULL, ec_ecdt->gpe,
&acpi_ec_gpe_handler);
ec = make_acpi_ec();
if (!ec)
return -ENOMEM;
kfree(ec_ecdt);
status = ec_parse_device(device->handle, 0, ec, NULL);
if (status != AE_CTRL_TERMINATE) {
kfree(ec);
return -EINVAL;
}
/* Get GPE bit assignment (EC events). */
/* TODO: Add support for _GPE returning a package */
status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"Obtaining GPE bit assignment"));
result = -ENODEV;
goto end;
}
/* Check if we found the boot EC */
if (boot_ec) {
if (boot_ec->gpe == ec->gpe) {
/* We might have incorrect info for GL at boot time */
mutex_lock(&boot_ec->lock);
boot_ec->global_lock = ec->global_lock;
mutex_unlock(&boot_ec->lock);
kfree(ec);
ec = boot_ec;
}
} else
first_ec = ec;
ec->handle = device->handle;
acpi_driver_data(device) = ec;
result = acpi_ec_add_fs(device);
if (result)
goto end;
acpi_ec_add_fs(device);
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.",
acpi_device_name(device), acpi_device_bid(device),
(u32) ec->gpe));
if (!first_ec)
first_ec = device;
end:
if (result)
kfree(ec);
return result;
return 0;
}
static int acpi_ec_remove(struct acpi_device *device, int type)
{
struct acpi_ec *ec = NULL;
struct acpi_ec *ec;
if (!device)
return -EINVAL;
ec = acpi_driver_data(device);
acpi_ec_remove_fs(device);
acpi_driver_data(device) = NULL;
if (ec == first_ec)
first_ec = NULL;
kfree(ec);
/* Don't touch boot EC */
if (boot_ec != ec)
kfree(ec);
return 0;
}
static acpi_status
acpi_ec_io_ports(struct acpi_resource *resource, void *context)
ec_parse_io_ports(struct acpi_resource *resource, void *context)
{
struct acpi_ec *ec = (struct acpi_ec *)context;
struct acpi_ec *ec = context;
if (resource->type != ACPI_RESOURCE_TYPE_IO) {
if (resource->type != ACPI_RESOURCE_TYPE_IO)
return AE_OK;
}
/*
* The first address region returned is the data port, and
* the second address region returned is the status/command
* port.
*/
if (ec->data_addr == 0) {
if (ec->data_addr == 0)
ec->data_addr = resource->data.io.minimum;
} else if (ec->command_addr == 0) {
else if (ec->command_addr == 0)
ec->command_addr = resource->data.io.minimum;
} else {
else
return AE_CTRL_TERMINATE;
}
return AE_OK;
}
static int acpi_ec_start(struct acpi_device *device)
static int ec_install_handlers(struct acpi_ec *ec)
{
acpi_status status = AE_OK;
struct acpi_ec *ec = NULL;
if (!device)
return -EINVAL;
ec = acpi_driver_data(device);
if (!ec)
return -EINVAL;
/*
* Get I/O port addresses. Convert to GAS format.
*/
status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS,
acpi_ec_io_ports, ec);
if (ACPI_FAILURE(status) || ec->command_addr == 0) {
ACPI_EXCEPTION((AE_INFO, status,
"Error getting I/O port addresses"));
return -ENODEV;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx",
ec->gpe, ec->command_addr, ec->data_addr));
/*
* Install GPE handler
*/
acpi_status status;
status = acpi_install_gpe_handler(NULL, ec->gpe,
ACPI_GPE_EDGE_TRIGGERED,
&acpi_ec_gpe_handler, ec);
if (ACPI_FAILURE(status)) {
if (ACPI_FAILURE(status))
return -ENODEV;
}
acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME);
acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
......@@ -779,18 +715,49 @@ static int acpi_ec_start(struct acpi_device *device)
return -ENODEV;
}
return AE_OK;
/* EC is fully operational, allow queries */
atomic_set(&ec->query_pending, 0);
return 0;
}
static int acpi_ec_start(struct acpi_device *device)
{
struct acpi_ec *ec;
if (!device)
return -EINVAL;
ec = acpi_driver_data(device);
if (!ec)
return -EINVAL;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx",
ec->gpe, ec->command_addr, ec->data_addr));
/* Boot EC is already working */
if (ec == boot_ec)
return 0;
return ec_install_handlers(ec);
}
static int acpi_ec_stop(struct acpi_device *device, int type)
{
acpi_status status = AE_OK;
struct acpi_ec *ec = NULL;
acpi_status status;
struct acpi_ec *ec;
if (!device)
return -EINVAL;
ec = acpi_driver_data(device);
if (!ec)
return -EINVAL;
/* Don't touch boot EC */
if (ec == boot_ec)
return 0;
status = acpi_remove_address_space_handler(ec->handle,
ACPI_ADR_SPACE_EC,
......@@ -805,164 +772,67 @@ static int acpi_ec_stop(struct acpi_device *device, int type)
return 0;
}
static acpi_status __init
acpi_fake_ecdt_callback(acpi_handle handle,
u32 Level, void *context, void **retval)
static acpi_status
ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
{
acpi_status status;
mutex_init(&ec_ecdt->lock);
atomic_set(&ec_ecdt->event_count, 1);
if (acpi_ec_mode == EC_INTR) {
init_waitqueue_head(&ec_ecdt->wait);
}
struct acpi_ec *ec = context;
status = acpi_walk_resources(handle, METHOD_NAME__CRS,
acpi_ec_io_ports, ec_ecdt);
ec_parse_io_ports, ec);
if (ACPI_FAILURE(status))
return status;
ec_ecdt->uid = -1;
acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid);
status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe);
/* Get GPE bit assignment (EC events). */
/* TODO: Add support for _GPE returning a package */
status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe);
if (ACPI_FAILURE(status))
return status;
ec_ecdt->global_lock = TRUE;
ec_ecdt->handle = handle;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx",
ec_ecdt->gpe, ec_ecdt->command_addr,
ec_ecdt->data_addr));
return AE_CTRL_TERMINATE;
}
/*
* Some BIOS (such as some from Gateway laptops) access EC region very early
* such as in BAT0._INI or EC._INI before an EC device is found and
* do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily
* required, but if EC regison is accessed early, it is required.
* The routine tries to workaround the BIOS bug by pre-scan EC device
* It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any
* op region (since _REG isn't invoked yet). The assumption is true for
* all systems found.
*/
static int __init acpi_ec_fake_ecdt(void)
{
acpi_status status;
int ret = 0;
/* Use the global lock for all EC transactions? */
acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock);
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Try to make an fake ECDT"));
ec->handle = handle;
ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
if (!ec_ecdt) {
ret = -ENOMEM;
goto error;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx",
ec->gpe, ec->command_addr, ec->data_addr));
status = acpi_get_devices(ACPI_EC_HID,
acpi_fake_ecdt_callback, NULL, NULL);
if (ACPI_FAILURE(status)) {
kfree(ec_ecdt);
ec_ecdt = NULL;
ret = -ENODEV;
ACPI_EXCEPTION((AE_INFO, status, "Can't make an fake ECDT"));
goto error;
}
return 0;
error:
return ret;
return AE_CTRL_TERMINATE;
}
static int __init acpi_ec_get_real_ecdt(void)
int __init acpi_ec_ecdt_probe(void)
{
int ret;
acpi_status status;
struct acpi_table_ecdt *ecdt_ptr;
status = acpi_get_table(ACPI_SIG_ECDT, 1,
(struct acpi_table_header **)&ecdt_ptr);
if (ACPI_FAILURE(status))
return -ENODEV;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT"));
boot_ec = make_acpi_ec();
if (!boot_ec)
return -ENOMEM;
/*
* Generate a temporary ec context to use until the namespace is scanned
* Generate a boot ec context
*/
ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
if (!ec_ecdt)
return -ENOMEM;
mutex_init(&ec_ecdt->lock);
atomic_set(&ec_ecdt->event_count, 1);
if (acpi_ec_mode == EC_INTR) {
init_waitqueue_head(&ec_ecdt->wait);
}
ec_ecdt->command_addr = ecdt_ptr->control.address;
ec_ecdt->data_addr = ecdt_ptr->data.address;
ec_ecdt->gpe = ecdt_ptr->gpe;
/* use the GL just to be safe */
ec_ecdt->global_lock = TRUE;
ec_ecdt->uid = ecdt_ptr->uid;
status = acpi_get_handle(NULL, ecdt_ptr->id, &ec_ecdt->handle);
if (ACPI_FAILURE(status)) {
status = acpi_get_table(ACPI_SIG_ECDT, 1,
(struct acpi_table_header **)&ecdt_ptr);
if (ACPI_FAILURE(status))
goto error;
}
return 0;
error:
ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT"));
kfree(ec_ecdt);
ec_ecdt = NULL;
return -ENODEV;
}
static int __initdata acpi_fake_ecdt_enabled;
int __init acpi_ec_ecdt_probe(void)
{
acpi_status status;
int ret;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT"));
ret = acpi_ec_get_real_ecdt();
/* Try to make a fake ECDT */
if (ret && acpi_fake_ecdt_enabled) {
ret = acpi_ec_fake_ecdt();
}
boot_ec->command_addr = ecdt_ptr->control.address;
boot_ec->data_addr = ecdt_ptr->data.address;
boot_ec->gpe = ecdt_ptr->gpe;
boot_ec->handle = ACPI_ROOT_OBJECT;
if (ret)
ret = ec_install_handlers(boot_ec);
if (!ret) {
first_ec = boot_ec;
return 0;
/*
* Install GPE handler
*/
status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe,
ACPI_GPE_EDGE_TRIGGERED,
&acpi_ec_gpe_handler, ec_ecdt);
if (ACPI_FAILURE(status)) {
goto error;
}
acpi_set_gpe_type(NULL, ec_ecdt->gpe, ACPI_GPE_TYPE_RUNTIME);
acpi_enable_gpe(NULL, ec_ecdt->gpe, ACPI_NOT_ISR);
status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler,
&acpi_ec_space_setup,
ec_ecdt);
if (ACPI_FAILURE(status)) {
acpi_remove_gpe_handler(NULL, ec_ecdt->gpe,
&acpi_ec_gpe_handler);
goto error;
}
return 0;
error:
ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT"));
kfree(ec_ecdt);
ec_ecdt = NULL;
kfree(boot_ec);
boot_ec = NULL;
return -ENODEV;
}
......@@ -1003,13 +873,6 @@ static void __exit acpi_ec_exit(void)
}
#endif /* 0 */
static int __init acpi_fake_ecdt_setup(char *str)
{
acpi_fake_ecdt_enabled = 1;
return 1;
}
__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup);
static int __init acpi_ec_set_intr_mode(char *str)
{
int intr;
......@@ -1017,12 +880,8 @@ static int __init acpi_ec_set_intr_mode(char *str)
if (!get_option(&str, &intr))
return 0;
if (intr) {
acpi_ec_mode = EC_INTR;
} else {
acpi_ec_mode = EC_POLL;
}
acpi_ec_driver.ops.add = acpi_ec_add;
acpi_ec_mode = (intr) ? EC_INTR : EC_POLL;
printk(KERN_NOTICE PREFIX "%s mode.\n", intr ? "interrupt" : "polling");
return 1;
......
/*
* SMBus driver for ACPI Embedded Controller ($Revision: 1.3 $)
*
* Copyright (c) 2002, 2005 Ducrot Bruno
* Copyright (c) 2005 Rich Townsend (tiny hacks & tweaks)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/acpi.h>
#include <linux/delay.h>
#include "i2c_ec.h"
#define xudelay(t) udelay(t)
#define xmsleep(t) msleep(t)
#define ACPI_EC_HC_COMPONENT 0x00080000
#define ACPI_EC_HC_CLASS "ec_hc_smbus"
#define ACPI_EC_HC_HID "ACPI0001"
#define ACPI_EC_HC_DEVICE_NAME "EC HC smbus"
#define _COMPONENT ACPI_EC_HC_COMPONENT
ACPI_MODULE_NAME("i2c_ec");
static int acpi_ec_hc_add(struct acpi_device *device);
static int acpi_ec_hc_remove(struct acpi_device *device, int type);
static struct acpi_driver acpi_ec_hc_driver = {
.name = "i2c_ec",
.class = ACPI_EC_HC_CLASS,
.ids = ACPI_EC_HC_HID,
.ops = {
.add = acpi_ec_hc_add,
.remove = acpi_ec_hc_remove,
},
};
/* Various bit mask for EC_SC (R) */
#define OBF 0x01
#define IBF 0x02
#define CMD 0x08
#define BURST 0x10
#define SCI_EVT 0x20
#define SMI_EVT 0x40
/* Commands for EC_SC (W) */
#define RD_EC 0x80
#define WR_EC 0x81
#define BE_EC 0x82
#define BD_EC 0x83
#define QR_EC 0x84
/*
* ACPI 2.0 chapter 13 SMBus 2.0 EC register model
*/
#define ACPI_EC_SMB_PRTCL 0x00 /* protocol, PEC */
#define ACPI_EC_SMB_STS 0x01 /* status */
#define ACPI_EC_SMB_ADDR 0x02 /* address */
#define ACPI_EC_SMB_CMD 0x03 /* command */
#define ACPI_EC_SMB_DATA 0x04 /* 32 data registers */
#define ACPI_EC_SMB_BCNT 0x24 /* number of data bytes */
#define ACPI_EC_SMB_ALRM_A 0x25 /* alarm address */
#define ACPI_EC_SMB_ALRM_D 0x26 /* 2 bytes alarm data */
#define ACPI_EC_SMB_STS_DONE 0x80
#define ACPI_EC_SMB_STS_ALRM 0x40
#define ACPI_EC_SMB_STS_RES 0x20
#define ACPI_EC_SMB_STS_STATUS 0x1f
#define ACPI_EC_SMB_STATUS_OK 0x00
#define ACPI_EC_SMB_STATUS_FAIL 0x07
#define ACPI_EC_SMB_STATUS_DNAK 0x10
#define ACPI_EC_SMB_STATUS_DERR 0x11
#define ACPI_EC_SMB_STATUS_CMD_DENY 0x12
#define ACPI_EC_SMB_STATUS_UNKNOWN 0x13
#define ACPI_EC_SMB_STATUS_ACC_DENY 0x17
#define ACPI_EC_SMB_STATUS_TIMEOUT 0x18
#define ACPI_EC_SMB_STATUS_NOTSUP 0x19
#define ACPI_EC_SMB_STATUS_BUSY 0x1A
#define ACPI_EC_SMB_STATUS_PEC 0x1F
#define ACPI_EC_SMB_PRTCL_WRITE 0x00
#define ACPI_EC_SMB_PRTCL_READ 0x01
#define ACPI_EC_SMB_PRTCL_QUICK 0x02
#define ACPI_EC_SMB_PRTCL_BYTE 0x04
#define ACPI_EC_SMB_PRTCL_BYTE_DATA 0x06
#define ACPI_EC_SMB_PRTCL_WORD_DATA 0x08
#define ACPI_EC_SMB_PRTCL_BLOCK_DATA 0x0a
#define ACPI_EC_SMB_PRTCL_PROC_CALL 0x0c
#define ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL 0x0d
#define ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA 0x4a
#define ACPI_EC_SMB_PRTCL_PEC 0x80
/* Length of pre/post transaction sleep (msec) */
#define ACPI_EC_SMB_TRANSACTION_SLEEP 1
#define ACPI_EC_SMB_ACCESS_SLEEP1 1
#define ACPI_EC_SMB_ACCESS_SLEEP2 10
static int acpi_ec_smb_read(struct acpi_ec_smbus *smbus, u8 address, u8 * data)
{
u8 val;
int err;
err = ec_read(smbus->base + address, &val);
if (!err) {
*data = val;
}
xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP);
return (err);
}
static int acpi_ec_smb_write(struct acpi_ec_smbus *smbus, u8 address, u8 data)
{
int err;
err = ec_write(smbus->base + address, data);
return (err);
}
static int
acpi_ec_smb_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data *data)
{
struct acpi_ec_smbus *smbus = adap->algo_data;
unsigned char protocol, len = 0, pec, temp[2] = { 0, 0 };
int i;
if (read_write == I2C_SMBUS_READ) {
protocol = ACPI_EC_SMB_PRTCL_READ;
} else {
protocol = ACPI_EC_SMB_PRTCL_WRITE;
}
pec = (flags & I2C_CLIENT_PEC) ? ACPI_EC_SMB_PRTCL_PEC : 0;
switch (size) {
case I2C_SMBUS_QUICK:
protocol |= ACPI_EC_SMB_PRTCL_QUICK;
read_write = I2C_SMBUS_WRITE;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_WRITE) {
acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte);
}
protocol |= ACPI_EC_SMB_PRTCL_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte);
}
protocol |= ACPI_EC_SMB_PRTCL_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word);
acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1,
data->word >> 8);
}
protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA | pec;
break;
case I2C_SMBUS_BLOCK_DATA:
acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
len = min_t(u8, data->block[0], 32);
acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
for (i = 0; i < len; i++)
acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i,
data->block[i + 1]);
}
protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA | pec;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
len = min_t(u8, data->block[0], 32);
acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
if (read_write == I2C_SMBUS_WRITE) {
for (i = 0; i < len; i++) {
acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i,
data->block[i + 1]);
}
}
protocol |= ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA;
break;
case I2C_SMBUS_PROC_CALL:
acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word);
acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, data->word >> 8);
protocol = ACPI_EC_SMB_PRTCL_PROC_CALL | pec;
read_write = I2C_SMBUS_READ;
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
protocol |= pec;
len = min_t(u8, data->block[0], 31);
acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
for (i = 0; i < len; i++)
acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i,
data->block[i + 1]);
protocol = ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL | pec;
read_write = I2C_SMBUS_READ;
break;
default:
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "EC SMBus adapter: "
"Unsupported transaction %d\n", size));
return (-1);
}
acpi_ec_smb_write(smbus, ACPI_EC_SMB_ADDR, addr << 1);
acpi_ec_smb_write(smbus, ACPI_EC_SMB_PRTCL, protocol);
acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
xudelay(500);
acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
}
if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2);
acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
}
if ((~temp[0] & ACPI_EC_SMB_STS_DONE)
|| (temp[0] & ACPI_EC_SMB_STS_STATUS)) {
return (-1);
}
if (read_write == I2C_SMBUS_WRITE) {
return (0);
}
switch (size) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, &data->byte);
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, temp + 0);
acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + 1, temp + 1);
data->word = (temp[1] << 8) | temp[0];
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_PROC_CALL:
len = 0;
acpi_ec_smb_read(smbus, ACPI_EC_SMB_BCNT, &len);
len = min_t(u8, len, 32);
case I2C_SMBUS_I2C_BLOCK_DATA:
for (i = 0; i < len; i++)
acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + i,
data->block + i + 1);
data->block[0] = len;
break;
}
return (0);
}
static u32 acpi_ec_smb_func(struct i2c_adapter *adapter)
{
return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA |
I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC);
}
static const struct i2c_algorithm acpi_ec_smbus_algorithm = {
.smbus_xfer = acpi_ec_smb_access,
.functionality = acpi_ec_smb_func,
};
static int acpi_ec_hc_add(struct acpi_device *device)
{
int status;
unsigned long val;
struct acpi_ec_hc *ec_hc;
struct acpi_ec_smbus *smbus;
if (!device) {
return -EINVAL;
}
ec_hc = kzalloc(sizeof(struct acpi_ec_hc), GFP_KERNEL);
if (!ec_hc) {
return -ENOMEM;
}
smbus = kzalloc(sizeof(struct acpi_ec_smbus), GFP_KERNEL);
if (!smbus) {
kfree(ec_hc);
return -ENOMEM;
}
ec_hc->handle = device->handle;
strcpy(acpi_device_name(device), ACPI_EC_HC_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_EC_HC_CLASS);
acpi_driver_data(device) = ec_hc;
status = acpi_evaluate_integer(ec_hc->handle, "_EC", NULL, &val);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error obtaining _EC\n"));
kfree(ec_hc);
kfree(smbus);
return -EIO;
}
smbus->ec = acpi_driver_data(device->parent);
smbus->base = (val & 0xff00ull) >> 8;
smbus->alert = val & 0xffull;
smbus->adapter.owner = THIS_MODULE;
smbus->adapter.algo = &acpi_ec_smbus_algorithm;
smbus->adapter.algo_data = smbus;
smbus->adapter.dev.parent = &device->dev;
if (i2c_add_adapter(&smbus->adapter)) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"EC SMBus adapter: Failed to register adapter\n"));
kfree(smbus);
kfree(ec_hc);
return -EIO;
}
ec_hc->smbus = smbus;
printk(KERN_INFO PREFIX "%s [%s]\n",
acpi_device_name(device), acpi_device_bid(device));
return AE_OK;
}
static int acpi_ec_hc_remove(struct acpi_device *device, int type)
{
struct acpi_ec_hc *ec_hc;
if (!device) {
return -EINVAL;
}
ec_hc = acpi_driver_data(device);
i2c_del_adapter(&ec_hc->smbus->adapter);
kfree(ec_hc->smbus);
kfree(ec_hc);
return AE_OK;
}
static int __init acpi_ec_hc_init(void)
{
int result;
result = acpi_bus_register_driver(&acpi_ec_hc_driver);
if (result < 0) {
return -ENODEV;
}
return 0;
}
static void __exit acpi_ec_hc_exit(void)
{
acpi_bus_unregister_driver(&acpi_ec_hc_driver);
}
struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device)
{
return acpi_driver_data(device->parent);
}
EXPORT_SYMBOL(acpi_get_ec_hc);
module_init(acpi_ec_hc_init);
module_exit(acpi_ec_hc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ducrot Bruno");
MODULE_DESCRIPTION("ACPI EC SMBus driver");
/*
* SMBus driver for ACPI Embedded Controller ($Revision: 1.2 $)
*
* Copyright (c) 2002, 2005 Ducrot Bruno
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2.
*/
struct acpi_ec_smbus {
struct i2c_adapter adapter;
union acpi_ec *ec;
int base;
int alert;
};
struct acpi_ec_hc {
acpi_handle handle;
struct acpi_ec_smbus *smbus;
};
struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device);
......@@ -70,8 +70,6 @@
#define ACPI_PROCESSOR_LIMIT_USER 0
#define ACPI_PROCESSOR_LIMIT_THERMAL 1
#define ACPI_STA_PRESENT 0x00000001
#define _COMPONENT ACPI_PROCESSOR_COMPONENT
ACPI_MODULE_NAME("processor_core");
......@@ -779,7 +777,7 @@ static int is_processor_present(acpi_handle handle)
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
if (ACPI_FAILURE(status) || !(sta & ACPI_STA_PRESENT)) {
if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) {
ACPI_EXCEPTION((AE_INFO, status, "Processor Device is not present"));
return 0;
}
......
......@@ -51,14 +51,6 @@
#include <asm/apic.h>
#endif
/*
* Include the apic definitions for x86 to have the APIC timer related defines
* available also for UP (on SMP it gets magically included via linux/smp.h).
*/
#ifdef CONFIG_X86
#include <asm/apic.h>
#endif
#include <asm/io.h>
#include <asm/uaccess.h>
......
......@@ -30,30 +30,10 @@
#include <linux/seq_file.h>
#include <asm/uaccess.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include "i2c_ec.h"
#define DEF_CAPACITY_UNIT 3
#define MAH_CAPACITY_UNIT 1
#define MWH_CAPACITY_UNIT 2
#define CAPACITY_UNIT DEF_CAPACITY_UNIT
#define REQUEST_UPDATE_MODE 1
#define QUEUE_UPDATE_MODE 2
#define DATA_TYPE_COMMON 0
#define DATA_TYPE_INFO 1
#define DATA_TYPE_STATE 2
#define DATA_TYPE_ALARM 3
#define DATA_TYPE_AC_STATE 4
extern struct proc_dir_entry *acpi_lock_ac_dir(void);
extern struct proc_dir_entry *acpi_lock_battery_dir(void);
extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
#define ACPI_SBS_COMPONENT 0x00080000
#define ACPI_SBS_CLASS "sbs"
#define ACPI_AC_CLASS "ac_adapter"
......@@ -74,39 +54,75 @@ extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
#define _COMPONENT ACPI_SBS_COMPONENT
#define MAX_SBS_BAT 4
#define MAX_SMBUS_ERR 1
ACPI_MODULE_NAME("sbs");
MODULE_AUTHOR("Rich Townsend");
MODULE_DESCRIPTION("Smart Battery System ACPI interface driver");
MODULE_LICENSE("GPL");
static struct semaphore sbs_sem;
#define xmsleep(t) msleep(t)
#define ACPI_EC_SMB_PRTCL 0x00 /* protocol, PEC */
#define ACPI_EC_SMB_STS 0x01 /* status */
#define ACPI_EC_SMB_ADDR 0x02 /* address */
#define ACPI_EC_SMB_CMD 0x03 /* command */
#define ACPI_EC_SMB_DATA 0x04 /* 32 data registers */
#define ACPI_EC_SMB_BCNT 0x24 /* number of data bytes */
#define UPDATE_MODE QUEUE_UPDATE_MODE
/* REQUEST_UPDATE_MODE QUEUE_UPDATE_MODE */
#define UPDATE_INFO_MODE 0
#define UPDATE_TIME 60
#define UPDATE_TIME2 0
#define ACPI_EC_SMB_STS_DONE 0x80
#define ACPI_EC_SMB_STS_STATUS 0x1f
static int capacity_mode = CAPACITY_UNIT;
static int update_mode = UPDATE_MODE;
static int update_info_mode = UPDATE_INFO_MODE;
static int update_time = UPDATE_TIME;
static int update_time2 = UPDATE_TIME2;
#define ACPI_EC_SMB_PRTCL_WRITE 0x00
#define ACPI_EC_SMB_PRTCL_READ 0x01
#define ACPI_EC_SMB_PRTCL_WORD_DATA 0x08
#define ACPI_EC_SMB_PRTCL_BLOCK_DATA 0x0a
module_param(capacity_mode, int, 0);
module_param(update_mode, int, 0);
module_param(update_info_mode, int, 0);
module_param(update_time, int, 0);
module_param(update_time2, int, 0);
#define ACPI_EC_SMB_TRANSACTION_SLEEP 1
#define ACPI_EC_SMB_ACCESS_SLEEP1 1
#define ACPI_EC_SMB_ACCESS_SLEEP2 10
#define DEF_CAPACITY_UNIT 3
#define MAH_CAPACITY_UNIT 1
#define MWH_CAPACITY_UNIT 2
#define CAPACITY_UNIT DEF_CAPACITY_UNIT
#define REQUEST_UPDATE_MODE 1
#define QUEUE_UPDATE_MODE 2
#define DATA_TYPE_COMMON 0
#define DATA_TYPE_INFO 1
#define DATA_TYPE_STATE 2
#define DATA_TYPE_ALARM 3
#define DATA_TYPE_AC_STATE 4
extern struct proc_dir_entry *acpi_lock_ac_dir(void);
extern struct proc_dir_entry *acpi_lock_battery_dir(void);
extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
#define MAX_SBS_BAT 4
#define ACPI_SBS_BLOCK_MAX 32
#define ACPI_SBS_SMBUS_READ 1
#define ACPI_SBS_SMBUS_WRITE 2
#define ACPI_SBS_WORD_DATA 1
#define ACPI_SBS_BLOCK_DATA 2
#define UPDATE_DELAY 10
/* 0 - every time, > 0 - by update_time */
static unsigned int update_time = 120;
static unsigned int capacity_mode = CAPACITY_UNIT;
module_param(update_time, uint, 0644);
module_param(capacity_mode, uint, 0444);
static int acpi_sbs_add(struct acpi_device *device);
static int acpi_sbs_remove(struct acpi_device *device, int type);
static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus);
static void acpi_sbs_update_queue(void *data);
static int acpi_sbs_resume(struct acpi_device *device);
static struct acpi_driver acpi_sbs_driver = {
.name = "sbs",
......@@ -115,9 +131,14 @@ static struct acpi_driver acpi_sbs_driver = {
.ops = {
.add = acpi_sbs_add,
.remove = acpi_sbs_remove,
.resume = acpi_sbs_resume,
},
};
struct acpi_ac {
int ac_present;
};
struct acpi_battery_info {
int capacity_mode;
s16 full_charge_capacity;
......@@ -126,18 +147,16 @@ struct acpi_battery_info {
int vscale;
int ipscale;
s16 serial_number;
char manufacturer_name[I2C_SMBUS_BLOCK_MAX + 3];
char device_name[I2C_SMBUS_BLOCK_MAX + 3];
char device_chemistry[I2C_SMBUS_BLOCK_MAX + 3];
char manufacturer_name[ACPI_SBS_BLOCK_MAX + 3];
char device_name[ACPI_SBS_BLOCK_MAX + 3];
char device_chemistry[ACPI_SBS_BLOCK_MAX + 3];
};
struct acpi_battery_state {
s16 voltage;
s16 amperage;
s16 remaining_capacity;
s16 average_time_to_empty;
s16 average_time_to_full;
s16 battery_status;
s16 battery_state;
};
struct acpi_battery_alarm {
......@@ -146,9 +165,9 @@ struct acpi_battery_alarm {
struct acpi_battery {
int alive;
int battery_present;
int id;
int init_state;
int battery_present;
struct acpi_sbs *sbs;
struct acpi_battery_info info;
struct acpi_battery_state state;
......@@ -158,186 +177,251 @@ struct acpi_battery {
struct acpi_sbs {
acpi_handle handle;
int base;
struct acpi_device *device;
struct acpi_ec_smbus *smbus;
struct mutex mutex;
int sbsm_present;
int sbsm_batteries_supported;
int ac_present;
struct proc_dir_entry *ac_entry;
struct acpi_ac ac;
struct acpi_battery battery[MAX_SBS_BAT];
int update_info_mode;
int zombie;
int update_time;
int update_time2;
struct timer_list update_timer;
int run_cnt;
int update_proc_flg;
};
static void acpi_update_delay(struct acpi_sbs *sbs);
static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type);
static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type);
static void acpi_sbs_update_time(void *data);
union sbs_rw_data {
u16 word;
u8 block[ACPI_SBS_BLOCK_MAX + 2];
};
static int acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr,
char read_write, u8 command, int size,
union sbs_rw_data *data);
/* --------------------------------------------------------------------------
SMBus Communication
-------------------------------------------------------------------------- */
static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus)
static int acpi_ec_sbs_read(struct acpi_sbs *sbs, u8 address, u8 * data)
{
union i2c_smbus_data data;
int result = 0;
char *err_str;
int err_number;
u8 val;
int err;
data.word = 0;
err = ec_read(sbs->base + address, &val);
if (!err) {
*data = val;
}
xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP);
return (err);
}
result = smbus->adapter.algo->
smbus_xfer(&smbus->adapter,
ACPI_SB_SMBUS_ADDR,
0, I2C_SMBUS_READ, 0x16, I2C_SMBUS_BLOCK_DATA, &data);
static int acpi_ec_sbs_write(struct acpi_sbs *sbs, u8 address, u8 data)
{
int err;
err_number = (data.word & 0x000f);
err = ec_write(sbs->base + address, data);
return (err);
}
switch (data.word & 0x000f) {
case 0x0000:
err_str = "unexpected bus error";
break;
case 0x0001:
err_str = "busy";
break;
case 0x0002:
err_str = "reserved command";
break;
case 0x0003:
err_str = "unsupported command";
break;
case 0x0004:
err_str = "access denied";
static int
acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr,
char read_write, u8 command, int size,
union sbs_rw_data *data)
{
unsigned char protocol, len = 0, temp[2] = { 0, 0 };
int i;
if (read_write == ACPI_SBS_SMBUS_READ) {
protocol = ACPI_EC_SMB_PRTCL_READ;
} else {
protocol = ACPI_EC_SMB_PRTCL_WRITE;
}
switch (size) {
case ACPI_SBS_WORD_DATA:
acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command);
if (read_write == ACPI_SBS_SMBUS_WRITE) {
acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA, data->word);
acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + 1,
data->word >> 8);
}
protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA;
break;
case 0x0005:
err_str = "overflow/underflow";
case ACPI_SBS_BLOCK_DATA:
acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command);
if (read_write == ACPI_SBS_SMBUS_WRITE) {
len = min_t(u8, data->block[0], 32);
acpi_ec_sbs_write(sbs, ACPI_EC_SMB_BCNT, len);
for (i = 0; i < len; i++)
acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + i,
data->block[i + 1]);
}
protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA;
break;
case 0x0006:
err_str = "bad size";
default:
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"unsupported transaction %d", size));
return (-1);
}
acpi_ec_sbs_write(sbs, ACPI_EC_SMB_ADDR, addr << 1);
acpi_ec_sbs_write(sbs, ACPI_EC_SMB_PRTCL, protocol);
acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
xmsleep(ACPI_EC_SMB_ACCESS_SLEEP1);
acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
}
if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2);
acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
}
if ((~temp[0] & ACPI_EC_SMB_STS_DONE)
|| (temp[0] & ACPI_EC_SMB_STS_STATUS)) {
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"transaction %d error", size));
return (-1);
}
if (read_write == ACPI_SBS_SMBUS_WRITE) {
return (0);
}
switch (size) {
case ACPI_SBS_WORD_DATA:
acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA, temp);
acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + 1, temp + 1);
data->word = (temp[1] << 8) | temp[0];
break;
case 0x0007:
err_str = "unknown error";
case ACPI_SBS_BLOCK_DATA:
len = 0;
acpi_ec_sbs_read(sbs, ACPI_EC_SMB_BCNT, &len);
len = min_t(u8, len, 32);
for (i = 0; i < len; i++)
acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + i,
data->block + i + 1);
data->block[0] = len;
break;
default:
err_str = "unrecognized error";
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"unsupported transaction %d", size));
return (-1);
}
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"%s: ret %i, err %i\n", err_str, result, err_number));
return (0);
}
static int
acpi_sbs_smbus_read_word(struct acpi_ec_smbus *smbus, int addr, int func,
u16 * word,
void (*err_handler) (struct acpi_ec_smbus * smbus))
acpi_sbs_read_word(struct acpi_sbs *sbs, int addr, int func, u16 * word)
{
union i2c_smbus_data data;
union sbs_rw_data data;
int result = 0;
int i;
if (err_handler == NULL) {
err_handler = acpi_battery_smbus_err_handler;
}
for (i = 0; i < MAX_SMBUS_ERR; i++) {
result =
smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0,
I2C_SMBUS_READ, func,
I2C_SMBUS_WORD_DATA, &data);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"try %i: smbus->adapter.algo->smbus_xfer() failed\n",
i));
if (err_handler) {
err_handler(smbus);
}
} else {
*word = data.word;
break;
}
result = acpi_ec_sbs_access(sbs, addr,
ACPI_SBS_SMBUS_READ, func,
ACPI_SBS_WORD_DATA, &data);
if (result) {
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_ec_sbs_access() failed"));
} else {
*word = data.word;
}
return result;
}
static int
acpi_sbs_smbus_read_str(struct acpi_ec_smbus *smbus, int addr, int func,
char *str,
void (*err_handler) (struct acpi_ec_smbus * smbus))
acpi_sbs_read_str(struct acpi_sbs *sbs, int addr, int func, char *str)
{
union i2c_smbus_data data;
union sbs_rw_data data;
int result = 0;
int i;
if (err_handler == NULL) {
err_handler = acpi_battery_smbus_err_handler;
}
for (i = 0; i < MAX_SMBUS_ERR; i++) {
result =
smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0,
I2C_SMBUS_READ, func,
I2C_SMBUS_BLOCK_DATA,
&data);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"try %i: smbus->adapter.algo->smbus_xfer() failed\n",
i));
if (err_handler) {
err_handler(smbus);
}
} else {
strncpy(str, (const char *)data.block + 1,
data.block[0]);
str[data.block[0]] = 0;
break;
}
result = acpi_ec_sbs_access(sbs, addr,
ACPI_SBS_SMBUS_READ, func,
ACPI_SBS_BLOCK_DATA, &data);
if (result) {
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_ec_sbs_access() failed"));
} else {
strncpy(str, (const char *)data.block + 1, data.block[0]);
str[data.block[0]] = 0;
}
return result;
}
static int
acpi_sbs_smbus_write_word(struct acpi_ec_smbus *smbus, int addr, int func,
int word,
void (*err_handler) (struct acpi_ec_smbus * smbus))
acpi_sbs_write_word(struct acpi_sbs *sbs, int addr, int func, int word)
{
union i2c_smbus_data data;
union sbs_rw_data data;
int result = 0;
int i;
if (err_handler == NULL) {
err_handler = acpi_battery_smbus_err_handler;
}
data.word = word;
for (i = 0; i < MAX_SMBUS_ERR; i++) {
result =
smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0,
I2C_SMBUS_WRITE, func,
I2C_SMBUS_WORD_DATA, &data);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"try %i: smbus->adapter.algo"
"->smbus_xfer() failed\n", i));
if (err_handler) {
err_handler(smbus);
}
} else {
break;
}
result = acpi_ec_sbs_access(sbs, addr,
ACPI_SBS_SMBUS_WRITE, func,
ACPI_SBS_WORD_DATA, &data);
if (result) {
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_ec_sbs_access() failed"));
}
return result;
}
static int sbs_zombie(struct acpi_sbs *sbs)
{
return (sbs->zombie);
}
static int sbs_mutex_lock(struct acpi_sbs *sbs)
{
if (sbs_zombie(sbs)) {
return -ENODEV;
}
mutex_lock(&sbs->mutex);
return 0;
}
static void sbs_mutex_unlock(struct acpi_sbs *sbs)
{
mutex_unlock(&sbs->mutex);
}
/* --------------------------------------------------------------------------
Smart Battery System Management
-------------------------------------------------------------------------- */
/* Smart Battery */
static int acpi_check_update_proc(struct acpi_sbs *sbs)
{
acpi_status status = AE_OK;
if (update_time == 0) {
sbs->update_proc_flg = 0;
return 0;
}
if (sbs->update_proc_flg == 0) {
status = acpi_os_execute(OSL_GPE_HANDLER,
acpi_sbs_update_time, sbs);
if (status != AE_OK) {
ACPI_EXCEPTION((AE_INFO, status,
"acpi_os_execute() failed"));
return 1;
}
sbs->update_proc_flg = 1;
}
return 0;
}
static int acpi_sbs_generate_event(struct acpi_device *device,
int event, int state, char *bid, char *class)
......@@ -366,12 +450,11 @@ static int acpi_battery_get_present(struct acpi_battery *battery)
int result = 0;
int is_present = 0;
result = acpi_sbs_smbus_read_word(battery->sbs->smbus,
ACPI_SBSM_SMBUS_ADDR, 0x01,
&state, NULL);
result = acpi_sbs_read_word(battery->sbs,
ACPI_SBSM_SMBUS_ADDR, 0x01, &state);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
}
if (!result) {
is_present = (state & 0x000f) & (1 << battery->id);
......@@ -381,45 +464,33 @@ static int acpi_battery_get_present(struct acpi_battery *battery)
return result;
}
static int acpi_battery_is_present(struct acpi_battery *battery)
{
return (battery->battery_present);
}
static int acpi_ac_is_present(struct acpi_sbs *sbs)
{
return (sbs->ac_present);
}
static int acpi_battery_select(struct acpi_battery *battery)
{
struct acpi_ec_smbus *smbus = battery->sbs->smbus;
struct acpi_sbs *sbs = battery->sbs;
int result = 0;
s16 state;
int foo;
if (battery->sbs->sbsm_present) {
if (sbs->sbsm_present) {
/* Take special care not to knobble other nibbles of
* state (aka selector_state), since
* it causes charging to halt on SBSELs */
result =
acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01,
&state, NULL);
acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, &state);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
foo = (state & 0x0fff) | (1 << (battery->id + 12));
result =
acpi_sbs_smbus_write_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01,
foo, NULL);
acpi_sbs_write_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, foo);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_write_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_write_word() failed"));
goto end;
}
}
......@@ -430,15 +501,14 @@ static int acpi_battery_select(struct acpi_battery *battery)
static int acpi_sbsm_get_info(struct acpi_sbs *sbs)
{
struct acpi_ec_smbus *smbus = sbs->smbus;
int result = 0;
s16 battery_system_info;
result = acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x04,
&battery_system_info, NULL);
result = acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x04,
&battery_system_info);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
......@@ -451,53 +521,50 @@ static int acpi_sbsm_get_info(struct acpi_sbs *sbs)
static int acpi_battery_get_info(struct acpi_battery *battery)
{
struct acpi_ec_smbus *smbus = battery->sbs->smbus;
struct acpi_sbs *sbs = battery->sbs;
int result = 0;
s16 battery_mode;
s16 specification_info;
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03,
&battery_mode,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03,
&battery_mode);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
battery->info.capacity_mode = (battery_mode & 0x8000) >> 15;
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x10,
&battery->info.full_charge_capacity,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x10,
&battery->info.full_charge_capacity);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x18,
&battery->info.design_capacity,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x18,
&battery->info.design_capacity);
if (result) {
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x19,
&battery->info.design_voltage,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x19,
&battery->info.design_voltage);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1a,
&specification_info,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1a,
&specification_info);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
......@@ -529,37 +596,35 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
battery->info.ipscale = 1;
}
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1c,
&battery->info.serial_number,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1c,
&battery->info.serial_number);
if (result) {
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x20,
battery->info.manufacturer_name,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x20,
battery->info.manufacturer_name);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_str() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_str() failed"));
goto end;
}
result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x21,
battery->info.device_name,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x21,
battery->info.device_name);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_str() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_str() failed"));
goto end;
}
result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x22,
battery->info.device_chemistry,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x22,
battery->info.device_chemistry);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_str() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_str() failed"));
goto end;
}
......@@ -567,103 +632,60 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
return result;
}
static void acpi_update_delay(struct acpi_sbs *sbs)
{
if (sbs->zombie) {
return;
}
if (sbs->update_time2 > 0) {
msleep(sbs->update_time2 * 1000);
}
}
static int acpi_battery_get_state(struct acpi_battery *battery)
{
struct acpi_ec_smbus *smbus = battery->sbs->smbus;
struct acpi_sbs *sbs = battery->sbs;
int result = 0;
acpi_update_delay(battery->sbs);
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x09,
&battery->state.voltage,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x09,
&battery->state.voltage);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
acpi_update_delay(battery->sbs);
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0a,
&battery->state.amperage,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0a,
&battery->state.amperage);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
acpi_update_delay(battery->sbs);
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0f,
&battery->state.remaining_capacity,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0f,
&battery->state.remaining_capacity);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
acpi_update_delay(battery->sbs);
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x12,
&battery->state.average_time_to_empty,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x16,
&battery->state.battery_state);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
acpi_update_delay(battery->sbs);
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x13,
&battery->state.average_time_to_full,
&acpi_battery_smbus_err_handler);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
goto end;
}
acpi_update_delay(battery->sbs);
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x16,
&battery->state.battery_status,
&acpi_battery_smbus_err_handler);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
goto end;
}
acpi_update_delay(battery->sbs);
end:
return result;
}
static int acpi_battery_get_alarm(struct acpi_battery *battery)
{
struct acpi_ec_smbus *smbus = battery->sbs->smbus;
struct acpi_sbs *sbs = battery->sbs;
int result = 0;
result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01,
&battery->alarm.remaining_capacity,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01,
&battery->alarm.remaining_capacity);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
acpi_update_delay(battery->sbs);
end:
return result;
......@@ -672,15 +694,15 @@ static int acpi_battery_get_alarm(struct acpi_battery *battery)
static int acpi_battery_set_alarm(struct acpi_battery *battery,
unsigned long alarm)
{
struct acpi_ec_smbus *smbus = battery->sbs->smbus;
struct acpi_sbs *sbs = battery->sbs;
int result = 0;
s16 battery_mode;
int foo;
result = acpi_battery_select(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_select() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_select() failed"));
goto end;
}
......@@ -688,33 +710,29 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery,
if (alarm > 0) {
result =
acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03,
&battery_mode,
&acpi_battery_smbus_err_handler);
acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03,
&battery_mode);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
result =
acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01,
battery_mode & 0xbfff,
&acpi_battery_smbus_err_handler);
acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01,
battery_mode & 0xbfff);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_write_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_write_word() failed"));
goto end;
}
}
foo = alarm / (battery->info.capacity_mode ? 10 : 1);
result = acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01,
foo,
&acpi_battery_smbus_err_handler);
result = acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, foo);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_write_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_write_word() failed"));
goto end;
}
......@@ -725,6 +743,7 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery,
static int acpi_battery_set_mode(struct acpi_battery *battery)
{
struct acpi_sbs *sbs = battery->sbs;
int result = 0;
s16 battery_mode;
......@@ -732,12 +751,11 @@ static int acpi_battery_set_mode(struct acpi_battery *battery)
goto end;
}
result = acpi_sbs_smbus_read_word(battery->sbs->smbus,
ACPI_SB_SMBUS_ADDR, 0x03,
&battery_mode, NULL);
result = acpi_sbs_read_word(sbs,
ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
......@@ -746,21 +764,19 @@ static int acpi_battery_set_mode(struct acpi_battery *battery)
} else {
battery_mode |= 0x8000;
}
result = acpi_sbs_smbus_write_word(battery->sbs->smbus,
ACPI_SB_SMBUS_ADDR, 0x03,
battery_mode, NULL);
result = acpi_sbs_write_word(sbs,
ACPI_SB_SMBUS_ADDR, 0x03, battery_mode);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_write_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_write_word() failed"));
goto end;
}
result = acpi_sbs_smbus_read_word(battery->sbs->smbus,
ACPI_SB_SMBUS_ADDR, 0x03,
&battery_mode, NULL);
result = acpi_sbs_read_word(sbs,
ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
......@@ -774,36 +790,36 @@ static int acpi_battery_init(struct acpi_battery *battery)
result = acpi_battery_select(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_init() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_select() failed"));
goto end;
}
result = acpi_battery_set_mode(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_set_mode() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_set_mode() failed"));
goto end;
}
result = acpi_battery_get_info(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_get_info() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_get_info() failed"));
goto end;
}
result = acpi_battery_get_state(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_get_state() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_get_state() failed"));
goto end;
}
result = acpi_battery_get_alarm(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_get_alarm() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_get_alarm() failed"));
goto end;
}
......@@ -813,20 +829,19 @@ static int acpi_battery_init(struct acpi_battery *battery)
static int acpi_ac_get_present(struct acpi_sbs *sbs)
{
struct acpi_ec_smbus *smbus = sbs->smbus;
int result = 0;
s16 charger_status;
result = acpi_sbs_smbus_read_word(smbus, ACPI_SBC_SMBUS_ADDR, 0x13,
&charger_status, NULL);
result = acpi_sbs_read_word(sbs, ACPI_SBC_SMBUS_ADDR, 0x13,
&charger_status);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_smbus_read_word() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_read_word() failed"));
goto end;
}
sbs->ac_present = (charger_status & 0x8000) >> 15;
sbs->ac.ac_present = (charger_status & 0x8000) >> 15;
end:
......@@ -852,8 +867,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir,
if (!*dir) {
*dir = proc_mkdir(dir_name, parent_dir);
if (!*dir) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"proc_mkdir() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"proc_mkdir() failed"));
return -ENODEV;
}
(*dir)->owner = THIS_MODULE;
......@@ -863,8 +878,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir,
if (info_fops) {
entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir);
if (!entry) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"create_proc_entry() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"create_proc_entry() failed"));
} else {
entry->proc_fops = info_fops;
entry->data = data;
......@@ -876,8 +891,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir,
if (state_fops) {
entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir);
if (!entry) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"create_proc_entry() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"create_proc_entry() failed"));
} else {
entry->proc_fops = state_fops;
entry->data = data;
......@@ -889,8 +904,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir,
if (alarm_fops) {
entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IRUGO, *dir);
if (!entry) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"create_proc_entry() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"create_proc_entry() failed"));
} else {
entry->proc_fops = alarm_fops;
entry->data = data;
......@@ -923,24 +938,27 @@ static struct proc_dir_entry *acpi_battery_dir = NULL;
static int acpi_battery_read_info(struct seq_file *seq, void *offset)
{
struct acpi_battery *battery = seq->private;
struct acpi_sbs *sbs = battery->sbs;
int cscale;
int result = 0;
if (battery->sbs->zombie) {
if (sbs_mutex_lock(sbs)) {
return -ENODEV;
}
down(&sbs_sem);
result = acpi_check_update_proc(sbs);
if (result)
goto end;
if (update_mode == REQUEST_UPDATE_MODE) {
result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_INFO);
if (update_time == 0) {
result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_INFO);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_update_run() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_update_run() failed"));
}
}
if (acpi_battery_is_present(battery)) {
if (battery->battery_present) {
seq_printf(seq, "present: yes\n");
} else {
seq_printf(seq, "present: no\n");
......@@ -952,13 +970,13 @@ static int acpi_battery_read_info(struct seq_file *seq, void *offset)
} else {
cscale = battery->info.ipscale;
}
seq_printf(seq, "design capacity: %i%s",
seq_printf(seq, "design capacity: %i%s\n",
battery->info.design_capacity * cscale,
battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
battery->info.capacity_mode ? "0 mWh" : " mAh");
seq_printf(seq, "last full capacity: %i%s",
seq_printf(seq, "last full capacity: %i%s\n",
battery->info.full_charge_capacity * cscale,
battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
battery->info.capacity_mode ? "0 mWh" : " mAh");
seq_printf(seq, "battery technology: rechargeable\n");
......@@ -984,7 +1002,7 @@ static int acpi_battery_read_info(struct seq_file *seq, void *offset)
end:
up(&sbs_sem);
sbs_mutex_unlock(sbs);
return result;
}
......@@ -996,26 +1014,29 @@ static int acpi_battery_info_open_fs(struct inode *inode, struct file *file)
static int acpi_battery_read_state(struct seq_file *seq, void *offset)
{
struct acpi_battery *battery = (struct acpi_battery *)seq->private;
struct acpi_battery *battery = seq->private;
struct acpi_sbs *sbs = battery->sbs;
int result = 0;
int cscale;
int foo;
if (battery->sbs->zombie) {
if (sbs_mutex_lock(sbs)) {
return -ENODEV;
}
down(&sbs_sem);
result = acpi_check_update_proc(sbs);
if (result)
goto end;
if (update_mode == REQUEST_UPDATE_MODE) {
result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_STATE);
if (update_time == 0) {
result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_STATE);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_update_run() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_update_run() failed"));
}
}
if (acpi_battery_is_present(battery)) {
if (battery->battery_present) {
seq_printf(seq, "present: yes\n");
} else {
seq_printf(seq, "present: no\n");
......@@ -1028,7 +1049,7 @@ static int acpi_battery_read_state(struct seq_file *seq, void *offset)
cscale = battery->info.ipscale;
}
if (battery->state.battery_status & 0x0010) {
if (battery->state.battery_state & 0x0010) {
seq_printf(seq, "capacity state: critical\n");
} else {
seq_printf(seq, "capacity state: ok\n");
......@@ -1052,16 +1073,16 @@ static int acpi_battery_read_state(struct seq_file *seq, void *offset)
battery->info.capacity_mode ? "mW" : "mA");
}
seq_printf(seq, "remaining capacity: %i%s",
seq_printf(seq, "remaining capacity: %i%s\n",
battery->state.remaining_capacity * cscale,
battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
battery->info.capacity_mode ? "0 mWh" : " mAh");
seq_printf(seq, "present voltage: %i mV\n",
battery->state.voltage * battery->info.vscale);
end:
up(&sbs_sem);
sbs_mutex_unlock(sbs);
return result;
}
......@@ -1074,24 +1095,27 @@ static int acpi_battery_state_open_fs(struct inode *inode, struct file *file)
static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
{
struct acpi_battery *battery = seq->private;
struct acpi_sbs *sbs = battery->sbs;
int result = 0;
int cscale;
if (battery->sbs->zombie) {
if (sbs_mutex_lock(sbs)) {
return -ENODEV;
}
down(&sbs_sem);
result = acpi_check_update_proc(sbs);
if (result)
goto end;
if (update_mode == REQUEST_UPDATE_MODE) {
result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_ALARM);
if (update_time == 0) {
result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_ALARM);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_update_run() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_update_run() failed"));
}
}
if (!acpi_battery_is_present(battery)) {
if (!battery->battery_present) {
seq_printf(seq, "present: no\n");
goto end;
}
......@@ -1104,16 +1128,16 @@ static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
seq_printf(seq, "alarm: ");
if (battery->alarm.remaining_capacity) {
seq_printf(seq, "%i%s",
seq_printf(seq, "%i%s\n",
battery->alarm.remaining_capacity * cscale,
battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
battery->info.capacity_mode ? "0 mWh" : " mAh");
} else {
seq_printf(seq, "disabled\n");
}
end:
up(&sbs_sem);
sbs_mutex_unlock(sbs);
return result;
}
......@@ -1124,16 +1148,19 @@ acpi_battery_write_alarm(struct file *file, const char __user * buffer,
{
struct seq_file *seq = file->private_data;
struct acpi_battery *battery = seq->private;
struct acpi_sbs *sbs = battery->sbs;
char alarm_string[12] = { '\0' };
int result, old_alarm, new_alarm;
if (battery->sbs->zombie) {
if (sbs_mutex_lock(sbs)) {
return -ENODEV;
}
down(&sbs_sem);
result = acpi_check_update_proc(sbs);
if (result)
goto end;
if (!acpi_battery_is_present(battery)) {
if (!battery->battery_present) {
result = -ENODEV;
goto end;
}
......@@ -1155,21 +1182,21 @@ acpi_battery_write_alarm(struct file *file, const char __user * buffer,
result = acpi_battery_set_alarm(battery, new_alarm);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_set_alarm() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_set_alarm() failed"));
acpi_battery_set_alarm(battery, old_alarm);
goto end;
}
result = acpi_battery_get_alarm(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_get_alarm() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_get_alarm() failed"));
acpi_battery_set_alarm(battery, old_alarm);
goto end;
}
end:
up(&sbs_sem);
sbs_mutex_unlock(sbs);
if (result) {
return result;
......@@ -1217,24 +1244,22 @@ static int acpi_ac_read_state(struct seq_file *seq, void *offset)
struct acpi_sbs *sbs = seq->private;
int result;
if (sbs->zombie) {
if (sbs_mutex_lock(sbs)) {
return -ENODEV;
}
down(&sbs_sem);
if (update_mode == REQUEST_UPDATE_MODE) {
result = acpi_sbs_update_run(sbs, DATA_TYPE_AC_STATE);
if (update_time == 0) {
result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_AC_STATE);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_update_run() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_update_run() failed"));
}
}
seq_printf(seq, "state: %s\n",
sbs->ac_present ? "on-line" : "off-line");
sbs->ac.ac_present ? "on-line" : "off-line");
up(&sbs_sem);
sbs_mutex_unlock(sbs);
return 0;
}
......@@ -1275,25 +1300,25 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
result = acpi_battery_select(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_select() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_select() failed"));
goto end;
}
result = acpi_battery_get_present(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_get_present() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_get_present() failed"));
goto end;
}
is_present = acpi_battery_is_present(battery);
is_present = battery->battery_present;
if (is_present) {
result = acpi_battery_init(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_init() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_init() failed"));
goto end;
}
battery->init_state = 1;
......@@ -1308,12 +1333,16 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
&acpi_battery_state_fops,
&acpi_battery_alarm_fops, battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_generic_add_fs() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_generic_add_fs() failed"));
goto end;
}
battery->alive = 1;
printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n",
ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), dir_name,
sbs->battery->battery_present ? "present" : "absent");
end:
return result;
}
......@@ -1333,8 +1362,8 @@ static int acpi_ac_add(struct acpi_sbs *sbs)
result = acpi_ac_get_present(sbs);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_ac_get_present() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_ac_get_present() failed"));
goto end;
}
......@@ -1343,11 +1372,15 @@ static int acpi_ac_add(struct acpi_sbs *sbs)
ACPI_AC_DIR_NAME,
NULL, &acpi_ac_state_fops, NULL, sbs);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_generic_add_fs() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_generic_add_fs() failed"));
goto end;
}
printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n",
ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
ACPI_AC_DIR_NAME, sbs->ac.ac_present ? "on-line" : "off-line");
end:
return result;
......@@ -1361,45 +1394,85 @@ static void acpi_ac_remove(struct acpi_sbs *sbs)
}
}
static void acpi_sbs_update_queue_run(unsigned long data)
static void acpi_sbs_update_time_run(unsigned long data)
{
acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_queue, (void *)data);
acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_time, (void *)data);
}
static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type)
static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type)
{
struct acpi_battery *battery;
int result = 0;
int old_ac_present;
int old_battery_present;
int new_ac_present;
int new_battery_present;
int id;
int result = 0, cnt;
int old_ac_present = -1;
int old_battery_present = -1;
int new_ac_present = -1;
int new_battery_present = -1;
int id_min = 0, id_max = MAX_SBS_BAT - 1;
char dir_name[32];
int do_battery_init, do_ac_init;
s16 old_remaining_capacity;
int do_battery_init = 0, do_ac_init = 0;
int old_remaining_capacity = 0;
int update_ac = 1, update_battery = 1;
int up_tm = update_time;
if (sbs->zombie) {
if (sbs_zombie(sbs)) {
goto end;
}
old_ac_present = acpi_ac_is_present(sbs);
if (id >= 0) {
id_min = id_max = id;
}
if (data_type == DATA_TYPE_COMMON && up_tm > 0) {
cnt = up_tm / (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
if (sbs->run_cnt % cnt != 0) {
update_battery = 0;
}
}
sbs->run_cnt++;
if (!update_ac && !update_battery) {
goto end;
}
old_ac_present = sbs->ac.ac_present;
result = acpi_ac_get_present(sbs);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_ac_get_present() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_ac_get_present() failed"));
}
new_ac_present = acpi_ac_is_present(sbs);
new_ac_present = sbs->ac.ac_present;
do_ac_init = (old_ac_present != new_ac_present);
if (sbs->run_cnt == 1 && data_type == DATA_TYPE_COMMON) {
do_ac_init = 1;
}
if (data_type == DATA_TYPE_AC_STATE) {
if (do_ac_init) {
result = acpi_sbs_generate_event(sbs->device,
ACPI_SBS_AC_NOTIFY_STATUS,
new_ac_present,
ACPI_AC_DIR_NAME,
ACPI_AC_CLASS);
if (result) {
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_generate_event() failed"));
}
}
if (data_type == DATA_TYPE_COMMON) {
if (!do_ac_init && !update_battery) {
goto end;
}
}
if (data_type == DATA_TYPE_AC_STATE && !do_ac_init) {
goto end;
}
for (id = 0; id < MAX_SBS_BAT; id++) {
for (id = id_min; id <= id_max; id++) {
battery = &sbs->battery[id];
if (battery->alive == 0) {
continue;
......@@ -1407,94 +1480,92 @@ static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type)
old_remaining_capacity = battery->state.remaining_capacity;
old_battery_present = acpi_battery_is_present(battery);
old_battery_present = battery->battery_present;
result = acpi_battery_select(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_select() failed\n"));
}
if (sbs->zombie) {
goto end;
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_select() failed"));
}
result = acpi_battery_get_present(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_get_present() failed\n"));
}
if (sbs->zombie) {
goto end;
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_get_present() failed"));
}
new_battery_present = acpi_battery_is_present(battery);
new_battery_present = battery->battery_present;
do_battery_init = ((old_battery_present != new_battery_present)
&& new_battery_present);
if (sbs->zombie) {
if (!new_battery_present)
goto event;
if (do_ac_init || do_battery_init) {
result = acpi_battery_init(battery);
if (result) {
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_init() "
"failed"));
}
}
if (sbs_zombie(sbs)) {
goto end;
}
if (do_ac_init || do_battery_init ||
update_info_mode || sbs->update_info_mode) {
if (sbs->update_info_mode) {
sbs->update_info_mode = 0;
} else {
sbs->update_info_mode = 1;
}
result = acpi_battery_init(battery);
if ((data_type == DATA_TYPE_COMMON
|| data_type == DATA_TYPE_INFO)
&& new_battery_present) {
result = acpi_battery_get_info(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_init() "
"failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_get_info() failed"));
}
}
if (data_type == DATA_TYPE_INFO) {
continue;
}
if (sbs->zombie) {
if (sbs_zombie(sbs)) {
goto end;
}
if (new_battery_present) {
result = acpi_battery_get_alarm(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_get_alarm() "
"failed\n"));
}
if (data_type == DATA_TYPE_ALARM) {
continue;
}
if ((data_type == DATA_TYPE_COMMON
|| data_type == DATA_TYPE_STATE)
&& new_battery_present) {
result = acpi_battery_get_state(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_get_state() "
"failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_get_state() failed"));
}
}
if (sbs->zombie) {
goto end;
if (data_type == DATA_TYPE_STATE) {
goto event;
}
if (data_type != DATA_TYPE_COMMON) {
continue;
if (sbs_zombie(sbs)) {
goto end;
}
if (old_battery_present != new_battery_present) {
sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
result = acpi_sbs_generate_event(sbs->device,
ACPI_SBS_BATTERY_NOTIFY_STATUS,
new_battery_present,
dir_name,
ACPI_BATTERY_CLASS);
if ((data_type == DATA_TYPE_COMMON
|| data_type == DATA_TYPE_ALARM)
&& new_battery_present) {
result = acpi_battery_get_alarm(battery);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_generate_event() "
"failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_get_alarm() "
"failed"));
}
}
if (old_remaining_capacity != battery->state.remaining_capacity) {
if (data_type == DATA_TYPE_ALARM) {
continue;
}
if (sbs_zombie(sbs)) {
goto end;
}
event:
if (old_battery_present != new_battery_present || do_ac_init ||
old_remaining_capacity !=
battery->state.remaining_capacity) {
sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
result = acpi_sbs_generate_event(sbs->device,
ACPI_SBS_BATTERY_NOTIFY_STATUS,
......@@ -1502,138 +1573,120 @@ static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type)
dir_name,
ACPI_BATTERY_CLASS);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_generate_event() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_generate_event() "
"failed"));
}
}
}
if (sbs->zombie) {
goto end;
}
if (data_type != DATA_TYPE_COMMON) {
goto end;
}
if (old_ac_present != new_ac_present) {
result = acpi_sbs_generate_event(sbs->device,
ACPI_SBS_AC_NOTIFY_STATUS,
new_ac_present,
ACPI_AC_DIR_NAME,
ACPI_AC_CLASS);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_generate_event() failed\n"));
}
}
end:
return result;
}
static void acpi_sbs_update_queue(void *data)
static void acpi_sbs_update_time(void *data)
{
struct acpi_sbs *sbs = data;
unsigned long delay = -1;
int result;
unsigned int up_tm = update_time;
if (sbs->zombie) {
goto end;
}
if (sbs_mutex_lock(sbs))
return;
result = acpi_sbs_update_run(sbs, DATA_TYPE_COMMON);
result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_COMMON);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_update_run() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_update_run() failed"));
}
if (sbs->zombie) {
if (sbs_zombie(sbs)) {
goto end;
}
if (update_mode == REQUEST_UPDATE_MODE) {
goto end;
if (!up_tm) {
if (timer_pending(&sbs->update_timer))
del_timer(&sbs->update_timer);
} else {
delay = (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
delay = jiffies + HZ * delay;
if (timer_pending(&sbs->update_timer)) {
mod_timer(&sbs->update_timer, delay);
} else {
sbs->update_timer.data = (unsigned long)data;
sbs->update_timer.function = acpi_sbs_update_time_run;
sbs->update_timer.expires = delay;
add_timer(&sbs->update_timer);
}
}
delay = jiffies + HZ * update_time;
sbs->update_timer.data = (unsigned long)data;
sbs->update_timer.function = acpi_sbs_update_queue_run;
sbs->update_timer.expires = delay;
add_timer(&sbs->update_timer);
end:
;
sbs_mutex_unlock(sbs);
}
static int acpi_sbs_add(struct acpi_device *device)
{
struct acpi_sbs *sbs = NULL;
struct acpi_ec_hc *ec_hc = NULL;
int result, remove_result = 0;
int result = 0, remove_result = 0;
unsigned long sbs_obj;
int id, cnt;
int id;
acpi_status status = AE_OK;
unsigned long val;
status =
acpi_evaluate_integer(device->parent->handle, "_EC", NULL, &val);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Error obtaining _EC"));
return -EIO;
}
sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
if (!sbs) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "kmalloc() failed\n"));
return -ENOMEM;
ACPI_EXCEPTION((AE_INFO, AE_ERROR, "kzalloc() failed"));
result = -ENOMEM;
goto end;
}
cnt = 0;
while (cnt < 10) {
cnt++;
ec_hc = acpi_get_ec_hc(device);
if (ec_hc) {
break;
}
msleep(1000);
}
mutex_init(&sbs->mutex);
if (!ec_hc) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_get_ec_hc() failed: "
"NO driver found for EC HC SMBus\n"));
result = -ENODEV;
goto end;
}
sbs_mutex_lock(sbs);
sbs->base = (val & 0xff00ull) >> 8;
sbs->device = device;
sbs->smbus = ec_hc->smbus;
strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_SBS_CLASS);
acpi_driver_data(device) = sbs;
sbs->update_time = 0;
sbs->update_time2 = 0;
result = acpi_ac_add(sbs);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_ac_add() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR, "acpi_ac_add() failed"));
goto end;
}
result = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj);
if (ACPI_FAILURE(result)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_evaluate_integer() failed\n"));
status = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj);
if (status) {
ACPI_EXCEPTION((AE_INFO, status,
"acpi_evaluate_integer() failed"));
result = -EIO;
goto end;
}
if (sbs_obj > 0) {
result = acpi_sbsm_get_info(sbs);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbsm_get_info() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbsm_get_info() failed"));
goto end;
}
sbs->sbsm_present = 1;
}
if (sbs->sbsm_present == 0) {
result = acpi_battery_add(sbs, 0);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_add() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_add() failed"));
goto end;
}
} else {
......@@ -1641,9 +1694,8 @@ static int acpi_sbs_add(struct acpi_device *device)
if ((sbs->sbsm_batteries_supported & (1 << id))) {
result = acpi_battery_add(sbs, id);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_battery_add() "
"failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_battery_add() failed"));
goto end;
}
}
......@@ -1653,33 +1705,26 @@ static int acpi_sbs_add(struct acpi_device *device)
sbs->handle = device->handle;
init_timer(&sbs->update_timer);
if (update_mode == QUEUE_UPDATE_MODE) {
status = acpi_os_execute(OSL_GPE_HANDLER,
acpi_sbs_update_queue, sbs);
if (status != AE_OK) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_os_execute() failed\n"));
}
}
sbs->update_time = update_time;
sbs->update_time2 = update_time2;
printk(KERN_INFO PREFIX "%s [%s]\n",
acpi_device_name(device), acpi_device_bid(device));
result = acpi_check_update_proc(sbs);
if (result)
goto end;
end:
sbs_mutex_unlock(sbs);
if (result) {
remove_result = acpi_sbs_remove(device, 0);
if (remove_result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_sbs_remove() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_sbs_remove() failed"));
}
}
return result;
}
int acpi_sbs_remove(struct acpi_device *device, int type)
static int acpi_sbs_remove(struct acpi_device *device, int type)
{
struct acpi_sbs *sbs;
int id;
......@@ -1688,15 +1733,14 @@ int acpi_sbs_remove(struct acpi_device *device, int type)
return -EINVAL;
}
sbs = (struct acpi_sbs *)acpi_driver_data(device);
sbs = acpi_driver_data(device);
if (!sbs) {
return -EINVAL;
}
sbs_mutex_lock(sbs);
sbs->zombie = 1;
sbs->update_time = 0;
sbs->update_time2 = 0;
del_timer_sync(&sbs->update_timer);
acpi_os_wait_events_complete(NULL);
del_timer_sync(&sbs->update_timer);
......@@ -1707,11 +1751,41 @@ int acpi_sbs_remove(struct acpi_device *device, int type)
acpi_ac_remove(sbs);
sbs_mutex_unlock(sbs);
mutex_destroy(&sbs->mutex);
kfree(sbs);
return 0;
}
static void acpi_sbs_rmdirs(void)
{
if (acpi_ac_dir) {
acpi_unlock_ac_dir(acpi_ac_dir);
acpi_ac_dir = NULL;
}
if (acpi_battery_dir) {
acpi_unlock_battery_dir(acpi_battery_dir);
acpi_battery_dir = NULL;
}
}
static int acpi_sbs_resume(struct acpi_device *device)
{
struct acpi_sbs *sbs;
if (!device)
return -EINVAL;
sbs = device->driver_data;
sbs->run_cnt = 0;
return 0;
}
static int __init acpi_sbs_init(void)
{
int result = 0;
......@@ -1719,35 +1793,34 @@ static int __init acpi_sbs_init(void)
if (acpi_disabled)
return -ENODEV;
init_MUTEX(&sbs_sem);
if (capacity_mode != DEF_CAPACITY_UNIT
&& capacity_mode != MAH_CAPACITY_UNIT
&& capacity_mode != MWH_CAPACITY_UNIT) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_sbs_init: "
"invalid capacity_mode = %d\n",
capacity_mode));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"invalid capacity_mode = %d", capacity_mode));
return -EINVAL;
}
acpi_ac_dir = acpi_lock_ac_dir();
if (!acpi_ac_dir) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_lock_ac_dir() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_lock_ac_dir() failed"));
return -ENODEV;
}
acpi_battery_dir = acpi_lock_battery_dir();
if (!acpi_battery_dir) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_lock_battery_dir() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_lock_battery_dir() failed"));
acpi_sbs_rmdirs();
return -ENODEV;
}
result = acpi_bus_register_driver(&acpi_sbs_driver);
if (result < 0) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"acpi_bus_register_driver() failed\n"));
ACPI_EXCEPTION((AE_INFO, AE_ERROR,
"acpi_bus_register_driver() failed"));
acpi_sbs_rmdirs();
return -ENODEV;
}
......@@ -1756,13 +1829,9 @@ static int __init acpi_sbs_init(void)
static void __exit acpi_sbs_exit(void)
{
acpi_bus_unregister_driver(&acpi_sbs_driver);
acpi_unlock_ac_dir(acpi_ac_dir);
acpi_ac_dir = NULL;
acpi_unlock_battery_dir(acpi_battery_dir);
acpi_battery_dir = NULL;
acpi_sbs_rmdirs();
return;
}
......
......@@ -1068,7 +1068,9 @@ acpi_add_single_object(struct acpi_device **child,
}
break;
default:
STRUCT_TO_INT(device->status) = 0x0F;
STRUCT_TO_INT(device->status) =
ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING;
break;
}
......
......@@ -350,21 +350,31 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
{
struct list_head *node, *next;
seq_printf(seq, "Device Sleep state Status\n");
seq_printf(seq, "Device\tS-state\t Status Sysfs node\n");
spin_lock(&acpi_device_lock);
list_for_each_safe(node, next, &acpi_wakeup_device_list) {
struct acpi_device *dev =
container_of(node, struct acpi_device, wakeup_list);
struct device *ldev;
if (!dev->wakeup.flags.valid)
continue;
spin_unlock(&acpi_device_lock);
seq_printf(seq, "%4s %4d %s%8s\n",
ldev = acpi_get_physical_device(dev->handle);
seq_printf(seq, "%s\t S%d\t%c%-8s ",
dev->pnp.bus_id,
(u32) dev->wakeup.sleep_state,
dev->wakeup.flags.run_wake ? "*" : "",
dev->wakeup.flags.run_wake ? '*' : ' ',
dev->wakeup.state.enabled ? "enabled" : "disabled");
if (ldev)
seq_printf(seq, "%s:%s",
ldev->bus ? ldev->bus->name : "no-bus",
ldev->bus_id);
seq_printf(seq, "\n");
put_device(ldev);
spin_lock(&acpi_device_lock);
}
spin_unlock(&acpi_device_lock);
......
......@@ -347,6 +347,18 @@ static void acpi_tb_convert_fadt(void)
acpi_gbl_xpm1b_enable.space_id = acpi_gbl_FADT.xpm1a_event_block.space_id;
}
/*
* For ACPI 1.0 FADTs, ensure that reserved fields (which should be zero)
* are indeed zero. This will workaround BIOSs that inadvertently placed
* values in these fields.
*/
if (acpi_gbl_FADT.header.revision < 3) {
acpi_gbl_FADT.preferred_profile = 0;
acpi_gbl_FADT.pstate_control = 0;
acpi_gbl_FADT.cst_control = 0;
acpi_gbl_FADT.boot_flags = 0;
}
}
/******************************************************************************
......
/*
* Sony Programmable I/O Control Device driver for VAIO
*
* Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
*
* Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
*
* Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
......@@ -95,6 +97,11 @@ module_param(useinput, int, 0444);
MODULE_PARM_DESC(useinput,
"set this if you would like sonypi to feed events to the input subsystem");
static int check_ioport = 1;
module_param(check_ioport, int, 0444);
MODULE_PARM_DESC(check_ioport,
"set this to 0 if you think the automatic ioport check for sony-laptop is wrong");
#define SONYPI_DEVICE_MODEL_TYPE1 1
#define SONYPI_DEVICE_MODEL_TYPE2 2
#define SONYPI_DEVICE_MODEL_TYPE3 3
......@@ -477,7 +484,7 @@ static struct sonypi_device {
u16 evtype_offset;
int camera_power;
int bluetooth_power;
struct semaphore lock;
struct mutex lock;
struct kfifo *fifo;
spinlock_t fifo_lock;
wait_queue_head_t fifo_proc_list;
......@@ -884,7 +891,7 @@ int sonypi_camera_command(int command, u8 value)
if (!camera)
return -EIO;
down(&sonypi_device.lock);
mutex_lock(&sonypi_device.lock);
switch (command) {
case SONYPI_COMMAND_SETCAMERA:
......@@ -919,7 +926,7 @@ int sonypi_camera_command(int command, u8 value)
command);
break;
}
up(&sonypi_device.lock);
mutex_unlock(&sonypi_device.lock);
return 0;
}
......@@ -938,20 +945,20 @@ static int sonypi_misc_fasync(int fd, struct file *filp, int on)
static int sonypi_misc_release(struct inode *inode, struct file *file)
{
sonypi_misc_fasync(-1, file, 0);
down(&sonypi_device.lock);
mutex_lock(&sonypi_device.lock);
sonypi_device.open_count--;
up(&sonypi_device.lock);
mutex_unlock(&sonypi_device.lock);
return 0;
}
static int sonypi_misc_open(struct inode *inode, struct file *file)
{
down(&sonypi_device.lock);
mutex_lock(&sonypi_device.lock);
/* Flush input queue on first open */
if (!sonypi_device.open_count)
kfifo_reset(sonypi_device.fifo);
sonypi_device.open_count++;
up(&sonypi_device.lock);
mutex_unlock(&sonypi_device.lock);
return 0;
}
......@@ -1001,7 +1008,7 @@ static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
u8 val8;
u16 val16;
down(&sonypi_device.lock);
mutex_lock(&sonypi_device.lock);
switch (cmd) {
case SONYPI_IOCGBRT:
if (sonypi_ec_read(SONYPI_LCD_LIGHT, &val8)) {
......@@ -1101,7 +1108,7 @@ static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
default:
ret = -EINVAL;
}
up(&sonypi_device.lock);
mutex_unlock(&sonypi_device.lock);
return ret;
}
......@@ -1260,6 +1267,28 @@ static int __devinit sonypi_create_input_devices(void)
static int __devinit sonypi_setup_ioports(struct sonypi_device *dev,
const struct sonypi_ioport_list *ioport_list)
{
/* try to detect if sony-laptop is being used and thus
* has already requested one of the known ioports.
* As in the deprecated check_region this is racy has we have
* multiple ioports available and one of them can be requested
* between this check and the subsequent request. Anyway, as an
* attempt to be some more user-friendly as we currently are,
* this is enough.
*/
const struct sonypi_ioport_list *check = ioport_list;
while (check_ioport && check->port1) {
if (!request_region(check->port1,
sonypi_device.region_size,
"Sony Programable I/O Device Check")) {
printk(KERN_ERR "sonypi: ioport 0x%.4x busy, using sony-laptop? "
"if not use check_ioport=0\n",
check->port1);
return -EBUSY;
}
release_region(check->port1, sonypi_device.region_size);
check++;
}
while (ioport_list->port1) {
if (request_region(ioport_list->port1,
......@@ -1321,6 +1350,10 @@ static int __devinit sonypi_probe(struct platform_device *dev)
struct pci_dev *pcidev;
int error;
printk(KERN_WARNING "sonypi: please try the sony-laptop module instead "
"and report failures, see also "
"http://www.linux.it/~malattia/wiki/index.php/Sony_drivers\n");
spin_lock_init(&sonypi_device.fifo_lock);
sonypi_device.fifo = kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL,
&sonypi_device.fifo_lock);
......@@ -1330,7 +1363,7 @@ static int __devinit sonypi_probe(struct platform_device *dev)
}
init_waitqueue_head(&sonypi_device.fifo_proc_list);
init_MUTEX(&sonypi_device.lock);
mutex_init(&sonypi_device.lock);
sonypi_device.bluetooth_power = -1;
if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
......
......@@ -26,7 +26,7 @@
/*
* The I/O port the PMTMR resides at.
* The location is detected during setup_arch(),
* in arch/i386/acpi/boot.c
* in arch/i386/kernel/acpi/boot.c
*/
u32 pmtmr_ioport __read_mostly;
......
......@@ -577,14 +577,14 @@ config VIDEO_ZORAN_AVS6EYES
config VIDEO_MEYE
tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
depends on PCI && SONYPI && VIDEO_V4L1
depends on PCI && SONY_LAPTOP && VIDEO_V4L1
---help---
This is the video4linux driver for the Motion Eye camera found
in the Vaio Picturebook laptops. Please read the material in
<file:Documentation/video4linux/meye.txt> for more information.
If you say Y or M here, you need to say Y or M to "Sony Programmable
I/O Control Device" in the character device section.
If you say Y or M here, you need to say Y or M to "Sony Laptop
Extras" in the misc device section.
To compile this driver as a module, choose M here: the
module will be called meye.
......
......@@ -925,13 +925,13 @@ static int meye_do_ioctl(struct inode *inode, struct file *file,
if (p->palette != VIDEO_PALETTE_YUV422 && p->palette != VIDEO_PALETTE_YUYV)
return -EINVAL;
mutex_lock(&meye.lock);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS,
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS,
p->brightness >> 10);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE,
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE,
p->hue >> 10);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR,
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR,
p->colour >> 10);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST,
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST,
p->contrast >> 10);
meye.picture = *p;
mutex_unlock(&meye.lock);
......@@ -1043,11 +1043,11 @@ static int meye_do_ioctl(struct inode *inode, struct file *file,
meye.params.quality != jp->quality)
mchip_hic_stop(); /* need restart */
meye.params = *jp;
sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS,
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS,
meye.params.sharpness);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC,
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC,
meye.params.agc);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE,
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE,
meye.params.picture);
mutex_unlock(&meye.lock);
break;
......@@ -1287,38 +1287,38 @@ static int meye_do_ioctl(struct inode *inode, struct file *file,
mutex_lock(&meye.lock);
switch (c->id) {
case V4L2_CID_BRIGHTNESS:
sonypi_camera_command(
SONYPI_COMMAND_SETCAMERABRIGHTNESS, c->value);
sony_pic_camera_command(
SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value);
meye.picture.brightness = c->value << 10;
break;
case V4L2_CID_HUE:
sonypi_camera_command(
SONYPI_COMMAND_SETCAMERAHUE, c->value);
sony_pic_camera_command(
SONY_PIC_COMMAND_SETCAMERAHUE, c->value);
meye.picture.hue = c->value << 10;
break;
case V4L2_CID_CONTRAST:
sonypi_camera_command(
SONYPI_COMMAND_SETCAMERACONTRAST, c->value);
sony_pic_camera_command(
SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value);
meye.picture.contrast = c->value << 10;
break;
case V4L2_CID_SATURATION:
sonypi_camera_command(
SONYPI_COMMAND_SETCAMERACOLOR, c->value);
sony_pic_camera_command(
SONY_PIC_COMMAND_SETCAMERACOLOR, c->value);
meye.picture.colour = c->value << 10;
break;
case V4L2_CID_AGC:
sonypi_camera_command(
SONYPI_COMMAND_SETCAMERAAGC, c->value);
sony_pic_camera_command(
SONY_PIC_COMMAND_SETCAMERAAGC, c->value);
meye.params.agc = c->value;
break;
case V4L2_CID_SHARPNESS:
sonypi_camera_command(
SONYPI_COMMAND_SETCAMERASHARPNESS, c->value);
sony_pic_camera_command(
SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value);
meye.params.sharpness = c->value;
break;
case V4L2_CID_PICTURE:
sonypi_camera_command(
SONYPI_COMMAND_SETCAMERAPICTURE, c->value);
sony_pic_camera_command(
SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value);
meye.params.picture = c->value;
break;
case V4L2_CID_JPEGQUAL:
......@@ -1848,7 +1848,7 @@ static int __devinit meye_probe(struct pci_dev *pcidev,
memcpy(meye.video_dev, &meye_template, sizeof(meye_template));
meye.video_dev->dev = &meye.mchip_dev->dev;
if ((ret = sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 1))) {
if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) {
printk(KERN_ERR "meye: unable to power on the camera\n");
printk(KERN_ERR "meye: did you enable the camera in "
"sonypi using the module options ?\n");
......@@ -1928,13 +1928,13 @@ static int __devinit meye_probe(struct pci_dev *pcidev,
meye.params.picture = 0;
meye.params.framerate = 0;
sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, 32);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, 32);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, 32);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, 32);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, 32);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, 0);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, 48);
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, 32);
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, 32);
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, 32);
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, 32);
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, 32);
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0);
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48);
printk(KERN_INFO "meye: Motion Eye Camera Driver v%s.\n",
MEYE_DRIVER_VERSION);
......@@ -1953,7 +1953,7 @@ static int __devinit meye_probe(struct pci_dev *pcidev,
outregions:
pci_disable_device(meye.mchip_dev);
outenabledev:
sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0);
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0);
outsonypienable:
kfifo_free(meye.doneq);
outkfifoalloc2:
......@@ -1986,7 +1986,7 @@ static void __devexit meye_remove(struct pci_dev *pcidev)
pci_disable_device(meye.mchip_dev);
sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0);
sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0);
kfifo_free(meye.doneq);
kfifo_free(meye.grabq);
......
......@@ -255,7 +255,7 @@
/****************************************************************************/
/* Sony Programmable I/O Controller for accessing the camera commands */
#include <linux/sonypi.h>
#include <linux/sony-laptop.h>
/* private API definitions */
#include <linux/meye.h>
......
......@@ -112,14 +112,70 @@ config SONY_LAPTOP
depends on X86 && ACPI
select BACKLIGHT_CLASS_DEVICE
---help---
This mini-driver drives the SNC device present in the ACPI BIOS of
the Sony Vaio laptops.
This mini-driver drives the SNC and SPIC devices present in the ACPI
BIOS of the Sony Vaio laptops.
It gives access to some extra laptop functionalities. In its current
form, this driver let the user set or query the screen brightness
through the backlight subsystem and remove/apply power to some
It gives access to some extra laptop functionalities like Bluetooth,
screen brightness control, Fn keys and allows powering on/off some
devices.
Read <file:Documentation/sony-laptop.txt> for more information.
config SONY_LAPTOP_OLD
bool "Sonypi compatibility"
depends on SONY_LAPTOP
---help---
Build the sonypi driver compatibility code into the sony-laptop driver.
config THINKPAD_ACPI
tristate "ThinkPad ACPI Laptop Extras"
depends on X86 && ACPI
select BACKLIGHT_CLASS_DEVICE
select HWMON
---help---
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video
output switching, ThinkLight control, UltraBay eject and more.
For more information about this driver see
<file:Documentation/thinkpad-acpi.txt> and <http://ibm-acpi.sf.net/> .
This driver was formely known as ibm-acpi.
If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
config THINKPAD_ACPI_DEBUG
bool "Verbose debug mode"
depends on THINKPAD_ACPI
default n
---help---
Enables extra debugging information, at the expense of a slightly
increase in driver size.
If you are not sure, say N here.
config THINKPAD_ACPI_DOCK
bool "Legacy Docking Station Support"
depends on THINKPAD_ACPI
depends on ACPI_DOCK=n
default n
---help---
Allows the thinkpad_acpi driver to handle docking station events.
This support was made obsolete by the generic ACPI docking station
support (CONFIG_ACPI_DOCK). It will allow locking and removing the
laptop from the docking station, but will not properly connect PCI
devices.
If you are not sure, say N here.
config THINKPAD_ACPI_BAY
bool "Legacy Removable Bay Support"
depends on THINKPAD_ACPI
default y
---help---
Allows the thinkpad_acpi driver to handle removable bays. It will
eletrically disable the device in the bay, and also generate
notifications when the bay lever is ejected or inserted.
If you are not sure, say Y here.
endmenu
......@@ -12,3 +12,4 @@ obj-$(CONFIG_TIFM_CORE) += tifm_core.o
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
obj-$(CONFIG_SGI_IOC4) += ioc4.o
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
......@@ -3,7 +3,7 @@
*
*
* Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
* Copyright (C) 2006 Corentin Chary
* Copyright (C) 2006-2007 Corentin Chary
*
* 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
......@@ -48,7 +48,7 @@
#include <acpi/acpi_bus.h>
#include <asm/uaccess.h>
#define ASUS_LAPTOP_VERSION "0.40"
#define ASUS_LAPTOP_VERSION "0.41"
#define ASUS_HOTK_NAME "Asus Laptop Support"
#define ASUS_HOTK_CLASS "hotkey"
......@@ -81,7 +81,8 @@
#define TLED_ON 0x08 //touchpad LED
#define RLED_ON 0x10 //Record LED
#define PLED_ON 0x20 //Phone LED
#define LCD_ON 0x40 //LCD backlight
#define GLED_ON 0x40 //Gaming LED
#define LCD_ON 0x80 //LCD backlight
#define ASUS_LOG ASUS_HOTK_FILE ": "
#define ASUS_ERR KERN_ERR ASUS_LOG
......@@ -94,6 +95,19 @@ MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
MODULE_DESCRIPTION(ASUS_HOTK_NAME);
MODULE_LICENSE("GPL");
/* WAPF defines the behavior of the Fn+Fx wlan key
* The significance of values is yet to be found, but
* most of the time:
* 0x0 will do nothing
* 0x1 will allow to control the device with Fn+Fx key.
* 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
* 0x5 like 0x1 or 0x4
* So, if something doesn't work as you want, just try other values =)
*/
static uint wapf = 1;
module_param(wapf, uint, 0644);
MODULE_PARM_DESC(wapf, "WAPF value");
#define ASUS_HANDLE(object, paths...) \
static acpi_handle object##_handle = NULL; \
static char *object##_paths[] = { paths }
......@@ -103,6 +117,7 @@ ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED");
ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED");
ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED"); /* W1JC */
ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED"); /* A7J */
ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */
/* LEDD */
ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM");
......@@ -221,6 +236,7 @@ ASUS_LED(mled, "mail");
ASUS_LED(tled, "touchpad");
ASUS_LED(rled, "record");
ASUS_LED(pled, "phone");
ASUS_LED(gled, "gaming");
/*
* This function evaluates an ACPI method, given an int as parameter, the
......@@ -245,32 +261,19 @@ static int write_acpi_int(acpi_handle handle, const char *method, int val,
return (status == AE_OK);
}
static int read_acpi_int(acpi_handle handle, const char *method, int *val,
struct acpi_object_list *params)
{
struct acpi_buffer output;
union acpi_object out_obj;
acpi_status status;
output.length = sizeof(out_obj);
output.pointer = &out_obj;
status = acpi_evaluate_object(handle, (char *)method, params, &output);
*val = out_obj.integer.value;
return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
}
static int read_wireless_status(int mask)
{
int status;
ulong status;
acpi_status rv = AE_OK;
if (!wireless_status_handle)
return (hotk->status & mask) ? 1 : 0;
if (read_acpi_int(wireless_status_handle, NULL, &status, NULL)) {
return (status & mask) ? 1 : 0;
} else
rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
if (ACPI_FAILURE(rv))
printk(ASUS_WARNING "Error reading Wireless status\n");
else
return (status & mask) ? 1 : 0;
return (hotk->status & mask) ? 1 : 0;
}
......@@ -285,19 +288,28 @@ static int read_status(int mask)
return (hotk->status & mask) ? 1 : 0;
}
static void write_status(acpi_handle handle, int out, int mask, int invert)
static void write_status(acpi_handle handle, int out, int mask)
{
hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask);
if (invert) /* invert target value */
switch (mask) {
case MLED_ON:
out = !out & 0x1;
break;
case GLED_ON:
out = (out & 0x1) + 1;
break;
default:
out &= 0x1;
break;
}
if (handle && !write_acpi_int(handle, NULL, out, NULL))
printk(ASUS_WARNING " write failed\n");
printk(ASUS_WARNING " write failed %x\n", mask);
}
/* /sys/class/led handlers */
#define ASUS_LED_HANDLER(object, mask, invert) \
#define ASUS_LED_HANDLER(object, mask) \
static void object##_led_set(struct led_classdev *led_cdev, \
enum led_brightness value) \
{ \
......@@ -307,13 +319,14 @@ static void write_status(acpi_handle handle, int out, int mask, int invert)
static void object##_led_update(struct work_struct *ignored) \
{ \
int value = object##_led_wk; \
write_status(object##_set_handle, value, (mask), (invert)); \
write_status(object##_set_handle, value, (mask)); \
}
ASUS_LED_HANDLER(mled, MLED_ON, 1);
ASUS_LED_HANDLER(pled, PLED_ON, 0);
ASUS_LED_HANDLER(rled, RLED_ON, 0);
ASUS_LED_HANDLER(tled, TLED_ON, 0);
ASUS_LED_HANDLER(mled, MLED_ON);
ASUS_LED_HANDLER(pled, PLED_ON);
ASUS_LED_HANDLER(rled, RLED_ON);
ASUS_LED_HANDLER(tled, TLED_ON);
ASUS_LED_HANDLER(gled, GLED_ON);
static int get_lcd_state(void)
{
......@@ -338,7 +351,7 @@ static int set_lcd_state(int value)
printk(ASUS_WARNING "Error switching LCD\n");
}
write_status(NULL, lcd, LCD_ON, 0);
write_status(NULL, lcd, LCD_ON);
return 0;
}
......@@ -354,9 +367,11 @@ static void lcd_blank(int blank)
static int read_brightness(struct backlight_device *bd)
{
int value;
ulong value;
acpi_status rv = AE_OK;
if (!read_acpi_int(brightness_get_handle, NULL, &value, NULL))
rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
if (ACPI_FAILURE(rv))
printk(ASUS_WARNING "Error reading brightness\n");
return value;
......@@ -403,8 +418,10 @@ static ssize_t show_infos(struct device *dev,
struct device_attribute *attr, char *page)
{
int len = 0;
int temp;
ulong temp;
char buf[16]; //enough for all info
acpi_status rv = AE_OK;
/*
* We use the easy way, we don't care of off and count, so we don't set eof
* to 1
......@@ -418,9 +435,10 @@ static ssize_t show_infos(struct device *dev,
* bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
* The significance of others is yet to be found.
*/
if (read_acpi_int(hotk->handle, "SFUN", &temp, NULL))
len +=
sprintf(page + len, "SFUN value : 0x%04x\n", temp);
rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
if (!ACPI_FAILURE(rv))
len += sprintf(page + len, "SFUN value : 0x%04x\n",
(uint) temp);
/*
* Another value for userspace: the ASYM method returns 0x02 for
* battery low and 0x04 for battery critical, its readings tend to be
......@@ -428,9 +446,10 @@ static ssize_t show_infos(struct device *dev,
* Note: since not all the laptops provide this method, errors are
* silently ignored.
*/
if (read_acpi_int(hotk->handle, "ASYM", &temp, NULL))
len +=
sprintf(page + len, "ASYM value : 0x%04x\n", temp);
rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
if (!ACPI_FAILURE(rv))
len += sprintf(page + len, "ASYM value : 0x%04x\n",
(uint) temp);
if (asus_info) {
snprintf(buf, 16, "%d", asus_info->length);
len += sprintf(page + len, "DSDT length : %s\n", buf);
......@@ -465,7 +484,7 @@ static int parse_arg(const char *buf, unsigned long count, int *val)
}
static ssize_t store_status(const char *buf, size_t count,
acpi_handle handle, int mask, int invert)
acpi_handle handle, int mask)
{
int rv, value;
int out = 0;
......@@ -474,7 +493,7 @@ static ssize_t store_status(const char *buf, size_t count,
if (rv > 0)
out = value ? 1 : 0;
write_status(handle, out, mask, invert);
write_status(handle, out, mask);
return rv;
}
......@@ -515,7 +534,7 @@ static ssize_t show_wlan(struct device *dev,
static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
return store_status(buf, count, wl_switch_handle, WL_ON, 0);
return store_status(buf, count, wl_switch_handle, WL_ON);
}
/*
......@@ -531,7 +550,7 @@ static ssize_t store_bluetooth(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
return store_status(buf, count, bt_switch_handle, BT_ON, 0);
return store_status(buf, count, bt_switch_handle, BT_ON);
}
/*
......@@ -547,12 +566,15 @@ static void set_display(int value)
static int read_display(void)
{
int value = 0;
ulong value = 0;
acpi_status rv = AE_OK;
/* In most of the case, we know how to set the display, but sometime
we can't read it */
if (display_get_handle) {
if (!read_acpi_int(display_get_handle, NULL, &value, NULL))
rv = acpi_evaluate_integer(display_get_handle, NULL,
NULL, &value);
if (ACPI_FAILURE(rv))
printk(ASUS_WARNING "Error reading display status\n");
}
......@@ -656,10 +678,10 @@ static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
* switched
*/
if (event == ATKD_LCD_ON) {
write_status(NULL, 1, LCD_ON, 0);
write_status(NULL, 1, LCD_ON);
lcd_blank(FB_BLANK_UNBLANK);
} else if (event == ATKD_LCD_OFF) {
write_status(NULL, 0, LCD_ON, 0);
write_status(NULL, 0, LCD_ON);
lcd_blank(FB_BLANK_POWERDOWN);
}
......@@ -771,7 +793,7 @@ static int asus_hotk_get_info(void)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *model = NULL;
int bsts_result, hwrs_result;
ulong bsts_result, hwrs_result;
char *string = NULL;
acpi_status status;
......@@ -794,11 +816,16 @@ static int asus_hotk_get_info(void)
}
/* This needs to be called for some laptops to init properly */
if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result, NULL))
status =
acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
if (ACPI_FAILURE(status))
printk(ASUS_WARNING "Error calling BSTS\n");
else if (bsts_result)
printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n",
bsts_result);
(uint) bsts_result);
/* This too ... */
write_acpi_int(hotk->handle, "CWAP", wapf, NULL);
/*
* Try to match the object returned by INIT to the specific model.
......@@ -831,6 +858,7 @@ static int asus_hotk_get_info(void)
ASUS_HANDLE_INIT(tled_set);
ASUS_HANDLE_INIT(rled_set);
ASUS_HANDLE_INIT(pled_set);
ASUS_HANDLE_INIT(gled_set);
ASUS_HANDLE_INIT(ledd_set);
......@@ -840,7 +868,9 @@ static int asus_hotk_get_info(void)
* The significance of others is yet to be found.
* If we don't find the method, we assume the device are present.
*/
if (!read_acpi_int(hotk->handle, "HRWS", &hwrs_result, NULL))
status =
acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result);
if (ACPI_FAILURE(status))
hwrs_result = WL_HWRS | BT_HWRS;
if (hwrs_result & WL_HWRS)
......@@ -928,11 +958,15 @@ static int asus_hotk_add(struct acpi_device *device)
asus_hotk_found = 1;
/* WLED and BLED are on by default */
write_status(bt_switch_handle, 1, BT_ON, 0);
write_status(wl_switch_handle, 1, WL_ON, 0);
write_status(bt_switch_handle, 1, BT_ON);
write_status(wl_switch_handle, 1, WL_ON);
/* If the h/w switch is off, we need to check the real status */
write_status(NULL, read_status(BT_ON), BT_ON);
write_status(NULL, read_status(WL_ON), WL_ON);
/* LCD Backlight is on by default */
write_status(NULL, 1, LCD_ON, 0);
write_status(NULL, 1, LCD_ON);
/* LED display is off by default */
hotk->ledd_status = 0xFFF;
......@@ -991,6 +1025,7 @@ static void asus_led_exit(void)
ASUS_LED_UNREGISTER(tled);
ASUS_LED_UNREGISTER(pled);
ASUS_LED_UNREGISTER(rled);
ASUS_LED_UNREGISTER(gled);
destroy_workqueue(led_workqueue);
}
......@@ -1062,6 +1097,10 @@ static int asus_led_init(struct device *dev)
if (rv)
return rv;
rv = ASUS_LED_REGISTER(gled, dev);
if (rv)
return rv;
led_workqueue = create_singlethread_workqueue("led_workqueue");
if (!led_workqueue)
return -ENOMEM;
......
/*
* ACPI Sony Notebook Control Driver (SNC)
* ACPI Sony Notebook Control Driver (SNC and SPIC)
*
* Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
* Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
......@@ -7,6 +7,25 @@
* Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
* which are copyrighted by their respective authors.
*
* The SNY6001 driver part is based on the sonypi driver which includes
* material from:
*
* Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
*
* Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
*
* Copyright (C) 2001-2002 Alcve <www.alcove.com>
*
* Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
*
* Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
*
* Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
*
* Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
*
* Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
......@@ -31,40 +50,404 @@
#include <linux/backlight.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/dmi.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/kfifo.h>
#include <linux/workqueue.h>
#include <linux/acpi.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <asm/uaccess.h>
#include <linux/sonypi.h>
#include <linux/sony-laptop.h>
#ifdef CONFIG_SONY_LAPTOP_OLD
#include <linux/poll.h>
#include <linux/miscdevice.h>
#endif
#define ACPI_SNC_CLASS "sony"
#define ACPI_SNC_HID "SNY5001"
#define ACPI_SNC_DRIVER_NAME "ACPI Sony Notebook Control Driver v0.4"
#define DRV_PFX "sony-laptop: "
#define dprintk(msg...) do { \
if (debug) printk(KERN_WARNING DRV_PFX msg); \
} while (0)
/* the device uses 1-based values, while the backlight subsystem uses
0-based values */
#define SONY_MAX_BRIGHTNESS 8
#define SONY_LAPTOP_DRIVER_VERSION "0.5"
#define LOG_PFX KERN_WARNING "sony-laptop: "
#define SONY_NC_CLASS "sony-nc"
#define SONY_NC_HID "SNY5001"
#define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver"
#define SONY_PIC_CLASS "sony-pic"
#define SONY_PIC_HID "SNY6001"
#define SONY_PIC_DRIVER_NAME "Sony Programmable IO Control Driver"
MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME);
MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");
MODULE_LICENSE("GPL");
MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);
static int debug;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
"the development of this driver");
static ssize_t sony_acpi_show(struct device *, struct device_attribute *,
static int no_spic; /* = 0 */
module_param(no_spic, int, 0444);
MODULE_PARM_DESC(no_spic,
"set this if you don't want to enable the SPIC device");
static int compat; /* = 0 */
module_param(compat, int, 0444);
MODULE_PARM_DESC(compat,
"set this if you want to enable backward compatibility mode");
static unsigned long mask = 0xffffffff;
module_param(mask, ulong, 0644);
MODULE_PARM_DESC(mask,
"set this to the mask of event you want to enable (see doc)");
static int camera; /* = 0 */
module_param(camera, int, 0444);
MODULE_PARM_DESC(camera,
"set this to 1 to enable Motion Eye camera controls "
"(only use it if you have a C1VE or C1VN model)");
#ifdef CONFIG_SONY_LAPTOP_OLD
static int minor = -1;
module_param(minor, int, 0);
MODULE_PARM_DESC(minor,
"minor number of the misc device for the SPIC compatibility code, "
"default is -1 (automatic)");
#endif
/*********** Input Devices ***********/
#define SONY_LAPTOP_BUF_SIZE 128
struct sony_laptop_input_s {
atomic_t users;
struct input_dev *jog_dev;
struct input_dev *key_dev;
struct kfifo *fifo;
spinlock_t fifo_lock;
struct workqueue_struct *wq;
};
static struct sony_laptop_input_s sony_laptop_input = {
.users = ATOMIC_INIT(0),
};
struct sony_laptop_keypress {
struct input_dev *dev;
int key;
};
/* Correspondance table between sonypi events and input layer events */
static struct {
int sonypiev;
int inputev;
} sony_laptop_inputkeys[] = {
{ SONYPI_EVENT_CAPTURE_PRESSED, KEY_CAMERA },
{ SONYPI_EVENT_FNKEY_ONLY, KEY_FN },
{ SONYPI_EVENT_FNKEY_ESC, KEY_FN_ESC },
{ SONYPI_EVENT_FNKEY_F1, KEY_FN_F1 },
{ SONYPI_EVENT_FNKEY_F2, KEY_FN_F2 },
{ SONYPI_EVENT_FNKEY_F3, KEY_FN_F3 },
{ SONYPI_EVENT_FNKEY_F4, KEY_FN_F4 },
{ SONYPI_EVENT_FNKEY_F5, KEY_FN_F5 },
{ SONYPI_EVENT_FNKEY_F6, KEY_FN_F6 },
{ SONYPI_EVENT_FNKEY_F7, KEY_FN_F7 },
{ SONYPI_EVENT_FNKEY_F8, KEY_FN_F8 },
{ SONYPI_EVENT_FNKEY_F9, KEY_FN_F9 },
{ SONYPI_EVENT_FNKEY_F10, KEY_FN_F10 },
{ SONYPI_EVENT_FNKEY_F11, KEY_FN_F11 },
{ SONYPI_EVENT_FNKEY_F12, KEY_FN_F12 },
{ SONYPI_EVENT_FNKEY_1, KEY_FN_1 },
{ SONYPI_EVENT_FNKEY_2, KEY_FN_2 },
{ SONYPI_EVENT_FNKEY_D, KEY_FN_D },
{ SONYPI_EVENT_FNKEY_E, KEY_FN_E },
{ SONYPI_EVENT_FNKEY_F, KEY_FN_F },
{ SONYPI_EVENT_FNKEY_S, KEY_FN_S },
{ SONYPI_EVENT_FNKEY_B, KEY_FN_B },
{ SONYPI_EVENT_BLUETOOTH_PRESSED, KEY_BLUE },
{ SONYPI_EVENT_BLUETOOTH_ON, KEY_BLUE },
{ SONYPI_EVENT_PKEY_P1, KEY_PROG1 },
{ SONYPI_EVENT_PKEY_P2, KEY_PROG2 },
{ SONYPI_EVENT_PKEY_P3, KEY_PROG3 },
{ SONYPI_EVENT_BACK_PRESSED, KEY_BACK },
{ SONYPI_EVENT_HELP_PRESSED, KEY_HELP },
{ SONYPI_EVENT_ZOOM_PRESSED, KEY_ZOOM },
{ SONYPI_EVENT_THUMBPHRASE_PRESSED, BTN_THUMB },
{ 0, 0 },
};
/* release buttons after a short delay if pressed */
static void do_sony_laptop_release_key(struct work_struct *work)
{
struct sony_laptop_keypress kp;
while (kfifo_get(sony_laptop_input.fifo, (unsigned char *)&kp,
sizeof(kp)) == sizeof(kp)) {
msleep(10);
input_report_key(kp.dev, kp.key, 0);
input_sync(kp.dev);
}
}
static DECLARE_WORK(sony_laptop_release_key_work,
do_sony_laptop_release_key);
/* forward event to the input subsytem */
static void sony_laptop_report_input_event(u8 event)
{
struct input_dev *jog_dev = sony_laptop_input.jog_dev;
struct input_dev *key_dev = sony_laptop_input.key_dev;
struct sony_laptop_keypress kp = { NULL };
int i;
if (event == SONYPI_EVENT_FNKEY_RELEASED) {
/* Nothing, not all VAIOs generate this event */
return;
}
/* report events */
switch (event) {
/* jog_dev events */
case SONYPI_EVENT_JOGDIAL_UP:
case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
input_report_rel(jog_dev, REL_WHEEL, 1);
input_sync(jog_dev);
return;
case SONYPI_EVENT_JOGDIAL_DOWN:
case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
input_report_rel(jog_dev, REL_WHEEL, -1);
input_sync(jog_dev);
return;
/* key_dev events */
case SONYPI_EVENT_JOGDIAL_PRESSED:
kp.key = BTN_MIDDLE;
kp.dev = jog_dev;
break;
default:
for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++)
if (event == sony_laptop_inputkeys[i].sonypiev) {
kp.dev = key_dev;
kp.key = sony_laptop_inputkeys[i].inputev;
break;
}
break;
}
if (kp.dev) {
input_report_key(kp.dev, kp.key, 1);
input_sync(kp.dev);
kfifo_put(sony_laptop_input.fifo,
(unsigned char *)&kp, sizeof(kp));
if (!work_pending(&sony_laptop_release_key_work))
queue_work(sony_laptop_input.wq,
&sony_laptop_release_key_work);
} else
dprintk("unknown input event %.2x\n", event);
}
static int sony_laptop_setup_input(void)
{
struct input_dev *jog_dev;
struct input_dev *key_dev;
int i;
int error;
/* don't run again if already initialized */
if (atomic_add_return(1, &sony_laptop_input.users) > 1)
return 0;
/* kfifo */
spin_lock_init(&sony_laptop_input.fifo_lock);
sony_laptop_input.fifo =
kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
&sony_laptop_input.fifo_lock);
if (IS_ERR(sony_laptop_input.fifo)) {
printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
error = PTR_ERR(sony_laptop_input.fifo);
goto err_dec_users;
}
/* init workqueue */
sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop");
if (!sony_laptop_input.wq) {
printk(KERN_ERR DRV_PFX
"Unabe to create workqueue.\n");
error = -ENXIO;
goto err_free_kfifo;
}
/* input keys */
key_dev = input_allocate_device();
if (!key_dev) {
error = -ENOMEM;
goto err_destroy_wq;
}
key_dev->name = "Sony Vaio Keys";
key_dev->id.bustype = BUS_ISA;
key_dev->id.vendor = PCI_VENDOR_ID_SONY;
/* Initialize the Input Drivers: special keys */
key_dev->evbit[0] = BIT(EV_KEY);
for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++)
if (sony_laptop_inputkeys[i].inputev)
set_bit(sony_laptop_inputkeys[i].inputev,
key_dev->keybit);
error = input_register_device(key_dev);
if (error)
goto err_free_keydev;
sony_laptop_input.key_dev = key_dev;
/* jogdial */
jog_dev = input_allocate_device();
if (!jog_dev) {
error = -ENOMEM;
goto err_unregister_keydev;
}
jog_dev->name = "Sony Vaio Jogdial";
jog_dev->id.bustype = BUS_ISA;
jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
jog_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
jog_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_MIDDLE);
jog_dev->relbit[0] = BIT(REL_WHEEL);
error = input_register_device(jog_dev);
if (error)
goto err_free_jogdev;
sony_laptop_input.jog_dev = jog_dev;
return 0;
err_free_jogdev:
input_free_device(jog_dev);
err_unregister_keydev:
input_unregister_device(key_dev);
/* to avoid kref underflow below at input_free_device */
key_dev = NULL;
err_free_keydev:
input_free_device(key_dev);
err_destroy_wq:
destroy_workqueue(sony_laptop_input.wq);
err_free_kfifo:
kfifo_free(sony_laptop_input.fifo);
err_dec_users:
atomic_dec(&sony_laptop_input.users);
return error;
}
static void sony_laptop_remove_input(void)
{
/* cleanup only after the last user has gone */
if (!atomic_dec_and_test(&sony_laptop_input.users))
return;
/* flush workqueue first */
flush_workqueue(sony_laptop_input.wq);
/* destroy input devs */
input_unregister_device(sony_laptop_input.key_dev);
sony_laptop_input.key_dev = NULL;
if (sony_laptop_input.jog_dev) {
input_unregister_device(sony_laptop_input.jog_dev);
sony_laptop_input.jog_dev = NULL;
}
destroy_workqueue(sony_laptop_input.wq);
kfifo_free(sony_laptop_input.fifo);
}
/*********** Platform Device ***********/
static atomic_t sony_pf_users = ATOMIC_INIT(0);
static struct platform_driver sony_pf_driver = {
.driver = {
.name = "sony-laptop",
.owner = THIS_MODULE,
}
};
static struct platform_device *sony_pf_device;
static int sony_pf_add(void)
{
int ret = 0;
/* don't run again if already initialized */
if (atomic_add_return(1, &sony_pf_users) > 1)
return 0;
ret = platform_driver_register(&sony_pf_driver);
if (ret)
goto out;
sony_pf_device = platform_device_alloc("sony-laptop", -1);
if (!sony_pf_device) {
ret = -ENOMEM;
goto out_platform_registered;
}
ret = platform_device_add(sony_pf_device);
if (ret)
goto out_platform_alloced;
return 0;
out_platform_alloced:
platform_device_put(sony_pf_device);
sony_pf_device = NULL;
out_platform_registered:
platform_driver_unregister(&sony_pf_driver);
out:
atomic_dec(&sony_pf_users);
return ret;
}
static void sony_pf_remove(void)
{
/* deregister only after the last user has gone */
if (!atomic_dec_and_test(&sony_pf_users))
return;
platform_device_del(sony_pf_device);
platform_device_put(sony_pf_device);
platform_driver_unregister(&sony_pf_driver);
}
/*********** SNC (SNY5001) Device ***********/
/* the device uses 1-based values, while the backlight subsystem uses
0-based values */
#define SONY_MAX_BRIGHTNESS 8
#define SNC_VALIDATE_IN 0
#define SNC_VALIDATE_OUT 1
static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *,
char *);
static ssize_t sony_acpi_store(struct device *, struct device_attribute *,
static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *,
const char *, size_t);
static int boolean_validate(const int, const int);
static int brightness_default_validate(const int, const int);
#define SNC_VALIDATE_IN 0
#define SNC_VALIDATE_OUT 1
struct sony_acpi_value {
struct sony_nc_value {
char *name; /* name of the entry */
char **acpiget; /* names of the ACPI get function */
char **acpiset; /* names of the ACPI set function */
......@@ -75,65 +458,65 @@ struct sony_acpi_value {
struct device_attribute devattr; /* sysfs atribute */
};
#define HANDLE_NAMES(_name, _values...) \
#define SNC_HANDLE_NAMES(_name, _values...) \
static char *snc_##_name[] = { _values, NULL }
#define SONY_ACPI_VALUE(_name, _getters, _setters, _validate, _debug) \
#define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \
{ \
.name = __stringify(_name), \
.acpiget = _getters, \
.acpiset = _setters, \
.validate = _validate, \
.debug = _debug, \
.devattr = __ATTR(_name, 0, sony_acpi_show, sony_acpi_store), \
.devattr = __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \
}
#define SONY_ACPI_VALUE_NULL { .name = NULL }
#define SNC_HANDLE_NULL { .name = NULL }
HANDLE_NAMES(fnkey_get, "GHKE");
SNC_HANDLE_NAMES(fnkey_get, "GHKE");
HANDLE_NAMES(brightness_def_get, "GPBR");
HANDLE_NAMES(brightness_def_set, "SPBR");
SNC_HANDLE_NAMES(brightness_def_get, "GPBR");
SNC_HANDLE_NAMES(brightness_def_set, "SPBR");
HANDLE_NAMES(cdpower_get, "GCDP");
HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
SNC_HANDLE_NAMES(cdpower_get, "GCDP");
SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
HANDLE_NAMES(audiopower_get, "GAZP");
HANDLE_NAMES(audiopower_set, "AZPW");
SNC_HANDLE_NAMES(audiopower_get, "GAZP");
SNC_HANDLE_NAMES(audiopower_set, "AZPW");
HANDLE_NAMES(lanpower_get, "GLNP");
HANDLE_NAMES(lanpower_set, "LNPW");
SNC_HANDLE_NAMES(lanpower_get, "GLNP");
SNC_HANDLE_NAMES(lanpower_set, "LNPW");
HANDLE_NAMES(PID_get, "GPID");
SNC_HANDLE_NAMES(PID_get, "GPID");
HANDLE_NAMES(CTR_get, "GCTR");
HANDLE_NAMES(CTR_set, "SCTR");
SNC_HANDLE_NAMES(CTR_get, "GCTR");
SNC_HANDLE_NAMES(CTR_set, "SCTR");
HANDLE_NAMES(PCR_get, "GPCR");
HANDLE_NAMES(PCR_set, "SPCR");
SNC_HANDLE_NAMES(PCR_get, "GPCR");
SNC_HANDLE_NAMES(PCR_set, "SPCR");
HANDLE_NAMES(CMI_get, "GCMI");
HANDLE_NAMES(CMI_set, "SCMI");
SNC_HANDLE_NAMES(CMI_get, "GCMI");
SNC_HANDLE_NAMES(CMI_set, "SCMI");
static struct sony_acpi_value sony_acpi_values[] = {
SONY_ACPI_VALUE(brightness_default, snc_brightness_def_get,
static struct sony_nc_value sony_nc_values[] = {
SNC_HANDLE(brightness_default, snc_brightness_def_get,
snc_brightness_def_set, brightness_default_validate, 0),
SONY_ACPI_VALUE(fnkey, snc_fnkey_get, NULL, NULL, 0),
SONY_ACPI_VALUE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
SONY_ACPI_VALUE(audiopower, snc_audiopower_get, snc_audiopower_set,
SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0),
SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set,
boolean_validate, 0),
SONY_ACPI_VALUE(lanpower, snc_lanpower_get, snc_lanpower_set,
SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set,
boolean_validate, 1),
/* unknown methods */
SONY_ACPI_VALUE(PID, snc_PID_get, NULL, NULL, 1),
SONY_ACPI_VALUE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
SONY_ACPI_VALUE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
SONY_ACPI_VALUE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
SONY_ACPI_VALUE_NULL
SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1),
SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
SNC_HANDLE_NULL
};
static acpi_handle sony_acpi_handle;
static struct acpi_device *sony_acpi_acpi_device = NULL;
static acpi_handle sony_nc_acpi_handle;
static struct acpi_device *sony_nc_acpi_device = NULL;
/*
* acpi_evaluate_object wrappers
......@@ -153,7 +536,7 @@ static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
return 0;
}
printk(LOG_PFX "acpi_callreadfunc failed\n");
printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n");
return -1;
}
......@@ -179,7 +562,7 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
if (status == AE_OK) {
if (result != NULL) {
if (out_obj.type != ACPI_TYPE_INTEGER) {
printk(LOG_PFX "acpi_evaluate_object bad "
printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad "
"return type\n");
return -1;
}
......@@ -188,13 +571,13 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
return 0;
}
printk(LOG_PFX "acpi_evaluate_object failed\n");
printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n");
return -1;
}
/*
* sony_acpi_values input/output validate functions
* sony_nc_values input/output validate functions
*/
/* brightness_default_validate:
......@@ -229,19 +612,19 @@ static int boolean_validate(const int direction, const int value)
}
/*
* Sysfs show/store common to all sony_acpi_values
* Sysfs show/store common to all sony_nc_values
*/
static ssize_t sony_acpi_show(struct device *dev, struct device_attribute *attr,
static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr,
char *buffer)
{
int value;
struct sony_acpi_value *item =
container_of(attr, struct sony_acpi_value, devattr);
struct sony_nc_value *item =
container_of(attr, struct sony_nc_value, devattr);
if (!*item->acpiget)
return -EIO;
if (acpi_callgetfunc(sony_acpi_handle, *item->acpiget, &value) < 0)
if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0)
return -EIO;
if (item->validate)
......@@ -250,13 +633,13 @@ static ssize_t sony_acpi_show(struct device *dev, struct device_attribute *attr,
return snprintf(buffer, PAGE_SIZE, "%d\n", value);
}
static ssize_t sony_acpi_store(struct device *dev,
static ssize_t sony_nc_sysfs_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
int value;
struct sony_acpi_value *item =
container_of(attr, struct sony_acpi_value, devattr);
struct sony_nc_value *item =
container_of(attr, struct sony_nc_value, devattr);
if (!item->acpiset)
return -EIO;
......@@ -272,118 +655,20 @@ static ssize_t sony_acpi_store(struct device *dev,
if (value < 0)
return value;
if (acpi_callsetfunc(sony_acpi_handle, *item->acpiset, value, NULL) < 0)
if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0)
return -EIO;
item->value = value;
item->valid = 1;
return count;
}
/*
* Platform device
*/
static struct platform_driver sncpf_driver = {
.driver = {
.name = "sony-laptop",
.owner = THIS_MODULE,
}
};
static struct platform_device *sncpf_device;
static int sony_snc_pf_add(void)
{
acpi_handle handle;
struct sony_acpi_value *item;
int ret = 0;
ret = platform_driver_register(&sncpf_driver);
if (ret)
goto out;
sncpf_device = platform_device_alloc("sony-laptop", -1);
if (!sncpf_device) {
ret = -ENOMEM;
goto out_platform_registered;
}
ret = platform_device_add(sncpf_device);
if (ret)
goto out_platform_alloced;
for (item = sony_acpi_values; item->name; ++item) {
if (!debug && item->debug)
continue;
/* find the available acpiget as described in the DSDT */
for (; item->acpiget && *item->acpiget; ++item->acpiget) {
if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle,
*item->acpiget,
&handle))) {
if (debug)
printk(LOG_PFX "Found %s getter: %s\n",
item->name, *item->acpiget);
item->devattr.attr.mode |= S_IRUGO;
break;
}
}
/* find the available acpiset as described in the DSDT */
for (; item->acpiset && *item->acpiset; ++item->acpiset) {
if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle,
*item->acpiset,
&handle))) {
if (debug)
printk(LOG_PFX "Found %s setter: %s\n",
item->name, *item->acpiset);
item->devattr.attr.mode |= S_IWUSR;
break;
}
}
if (item->devattr.attr.mode != 0) {
ret =
device_create_file(&sncpf_device->dev,
&item->devattr);
if (ret)
goto out_sysfs;
}
}
return 0;
out_sysfs:
for (item = sony_acpi_values; item->name; ++item) {
device_remove_file(&sncpf_device->dev, &item->devattr);
}
platform_device_del(sncpf_device);
out_platform_alloced:
platform_device_put(sncpf_device);
out_platform_registered:
platform_driver_unregister(&sncpf_driver);
out:
return ret;
}
static void sony_snc_pf_remove(void)
{
struct sony_acpi_value *item;
for (item = sony_acpi_values; item->name; ++item) {
device_remove_file(&sncpf_device->dev, &item->devattr);
}
platform_device_del(sncpf_device);
platform_device_put(sncpf_device);
platform_driver_unregister(&sncpf_driver);
}
/*
* Backlight device
*/
static int sony_backlight_update_status(struct backlight_device *bd)
{
return acpi_callsetfunc(sony_acpi_handle, "SBRT",
return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
bd->props.brightness + 1, NULL);
}
......@@ -391,7 +676,7 @@ static int sony_backlight_get_brightness(struct backlight_device *bd)
{
int value;
if (acpi_callgetfunc(sony_acpi_handle, "GBRT", &value))
if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value))
return 0;
/* brightness levels are 1-based, while backlight ones are 0-based */
return value - 1;
......@@ -408,9 +693,9 @@ static struct backlight_ops sony_backlight_ops = {
*/
static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
{
if (debug)
printk(LOG_PFX "sony_acpi_notify, event: %d\n", event);
acpi_bus_generate_event(sony_acpi_acpi_device, 1, event);
dprintk("sony_acpi_notify, event: %d\n", event);
sony_laptop_report_input_event(event);
acpi_bus_generate_event(sony_nc_acpi_device, 1, event);
}
static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
......@@ -422,7 +707,7 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
node = (struct acpi_namespace_node *)handle;
operand = (union acpi_operand_object *)node->object;
printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
(u32) operand->method.param_count);
return AE_OK;
......@@ -431,16 +716,16 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
/*
* ACPI device
*/
static int sony_acpi_resume(struct acpi_device *device)
static int sony_nc_resume(struct acpi_device *device)
{
struct sony_acpi_value *item;
struct sony_nc_value *item;
for (item = sony_acpi_values; item->name; item++) {
for (item = sony_nc_values; item->name; item++) {
int ret;
if (!item->valid)
continue;
ret = acpi_callsetfunc(sony_acpi_handle, *item->acpiset,
ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
item->value, NULL);
if (ret < 0) {
printk("%s: %d\n", __FUNCTION__, ret);
......@@ -450,42 +735,55 @@ static int sony_acpi_resume(struct acpi_device *device)
return 0;
}
static int sony_acpi_add(struct acpi_device *device)
static int sony_nc_add(struct acpi_device *device)
{
acpi_status status;
int result = 0;
acpi_handle handle;
struct sony_nc_value *item;
printk(KERN_INFO DRV_PFX "%s v%s.\n",
SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
sony_acpi_acpi_device = device;
sony_nc_acpi_device = device;
strcpy(acpi_device_class(device), "sony/hotkey");
sony_acpi_handle = device->handle;
sony_nc_acpi_handle = device->handle;
if (debug) {
status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_acpi_handle,
status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
1, sony_walk_callback, NULL, NULL);
if (ACPI_FAILURE(status)) {
printk(LOG_PFX "unable to walk acpi resources\n");
printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n");
result = -ENODEV;
goto outwalk;
}
}
status = acpi_install_notify_handler(sony_acpi_handle,
/* setup input devices and helper fifo */
result = sony_laptop_setup_input();
if (result) {
printk(KERN_ERR DRV_PFX
"Unabe to create input devices.\n");
goto outwalk;
}
status = acpi_install_notify_handler(sony_nc_acpi_handle,
ACPI_DEVICE_NOTIFY,
sony_acpi_notify, NULL);
if (ACPI_FAILURE(status)) {
printk(LOG_PFX "unable to install notify handler\n");
printk(KERN_WARNING DRV_PFX "unable to install notify handler\n");
result = -ENODEV;
goto outwalk;
goto outinput;
}
if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle, "GBRT", &handle))) {
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) {
sony_backlight_device = backlight_device_register("sony", NULL,
NULL,
&sony_backlight_ops);
if (IS_ERR(sony_backlight_device)) {
printk(LOG_PFX "unable to register backlight device\n");
printk(KERN_WARNING DRV_PFX "unable to register backlight device\n");
sony_backlight_device = NULL;
} else {
sony_backlight_device->props.brightness =
......@@ -497,68 +795,1497 @@ static int sony_acpi_add(struct acpi_device *device)
}
if (sony_snc_pf_add())
result = sony_pf_add();
if (result)
goto outbacklight;
printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
/* create sony_pf sysfs attributes related to the SNC device */
for (item = sony_nc_values; item->name; ++item) {
if (!debug && item->debug)
continue;
/* find the available acpiget as described in the DSDT */
for (; item->acpiget && *item->acpiget; ++item->acpiget) {
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
*item->acpiget,
&handle))) {
dprintk("Found %s getter: %s\n",
item->name, *item->acpiget);
item->devattr.attr.mode |= S_IRUGO;
break;
}
}
/* find the available acpiset as described in the DSDT */
for (; item->acpiset && *item->acpiset; ++item->acpiset) {
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
*item->acpiset,
&handle))) {
dprintk("Found %s setter: %s\n",
item->name, *item->acpiset);
item->devattr.attr.mode |= S_IWUSR;
break;
}
}
if (item->devattr.attr.mode != 0) {
result =
device_create_file(&sony_pf_device->dev,
&item->devattr);
if (result)
goto out_sysfs;
}
}
return 0;
out_sysfs:
for (item = sony_nc_values; item->name; ++item) {
device_remove_file(&sony_pf_device->dev, &item->devattr);
}
sony_pf_remove();
outbacklight:
if (sony_backlight_device)
backlight_device_unregister(sony_backlight_device);
status = acpi_remove_notify_handler(sony_acpi_handle,
status = acpi_remove_notify_handler(sony_nc_acpi_handle,
ACPI_DEVICE_NOTIFY,
sony_acpi_notify);
if (ACPI_FAILURE(status))
printk(LOG_PFX "unable to remove notify handler\n");
printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
outinput:
sony_laptop_remove_input();
outwalk:
return result;
}
static int sony_acpi_remove(struct acpi_device *device, int type)
static int sony_nc_remove(struct acpi_device *device, int type)
{
acpi_status status;
struct sony_nc_value *item;
if (sony_backlight_device)
backlight_device_unregister(sony_backlight_device);
sony_acpi_acpi_device = NULL;
sony_nc_acpi_device = NULL;
status = acpi_remove_notify_handler(sony_acpi_handle,
status = acpi_remove_notify_handler(sony_nc_acpi_handle,
ACPI_DEVICE_NOTIFY,
sony_acpi_notify);
if (ACPI_FAILURE(status))
printk(LOG_PFX "unable to remove notify handler\n");
printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
sony_snc_pf_remove();
for (item = sony_nc_values; item->name; ++item) {
device_remove_file(&sony_pf_device->dev, &item->devattr);
}
printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
sony_pf_remove();
sony_laptop_remove_input();
dprintk(SONY_NC_DRIVER_NAME " removed.\n");
return 0;
}
static struct acpi_driver sony_acpi_driver = {
.name = ACPI_SNC_DRIVER_NAME,
.class = ACPI_SNC_CLASS,
.ids = ACPI_SNC_HID,
static struct acpi_driver sony_nc_driver = {
.name = SONY_NC_DRIVER_NAME,
.class = SONY_NC_CLASS,
.ids = SONY_NC_HID,
.owner = THIS_MODULE,
.ops = {
.add = sony_acpi_add,
.remove = sony_acpi_remove,
.resume = sony_acpi_resume,
.add = sony_nc_add,
.remove = sony_nc_remove,
.resume = sony_nc_resume,
},
};
static int __init sony_acpi_init(void)
{
return acpi_bus_register_driver(&sony_acpi_driver);
/*********** SPIC (SNY6001) Device ***********/
#define SONYPI_DEVICE_TYPE1 0x00000001
#define SONYPI_DEVICE_TYPE2 0x00000002
#define SONYPI_DEVICE_TYPE3 0x00000004
#define SONY_PIC_EV_MASK 0xff
struct sony_pic_ioport {
struct acpi_resource_io io;
struct list_head list;
};
struct sony_pic_irq {
struct acpi_resource_irq irq;
struct list_head list;
};
struct sony_pic_dev {
int model;
u8 camera_power;
u8 bluetooth_power;
u8 wwan_power;
struct acpi_device *acpi_dev;
struct sony_pic_irq *cur_irq;
struct sony_pic_ioport *cur_ioport;
struct list_head interrupts;
struct list_head ioports;
struct mutex lock;
};
static struct sony_pic_dev spic_dev = {
.interrupts = LIST_HEAD_INIT(spic_dev.interrupts),
.ioports = LIST_HEAD_INIT(spic_dev.ioports),
};
/* Event masks */
#define SONYPI_JOGGER_MASK 0x00000001
#define SONYPI_CAPTURE_MASK 0x00000002
#define SONYPI_FNKEY_MASK 0x00000004
#define SONYPI_BLUETOOTH_MASK 0x00000008
#define SONYPI_PKEY_MASK 0x00000010
#define SONYPI_BACK_MASK 0x00000020
#define SONYPI_HELP_MASK 0x00000040
#define SONYPI_LID_MASK 0x00000080
#define SONYPI_ZOOM_MASK 0x00000100
#define SONYPI_THUMBPHRASE_MASK 0x00000200
#define SONYPI_MEYE_MASK 0x00000400
#define SONYPI_MEMORYSTICK_MASK 0x00000800
#define SONYPI_BATTERY_MASK 0x00001000
#define SONYPI_WIRELESS_MASK 0x00002000
struct sonypi_event {
u8 data;
u8 event;
};
/* The set of possible button release events */
static struct sonypi_event sonypi_releaseev[] = {
{ 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0, 0 }
};
/* The set of possible jogger events */
static struct sonypi_event sonypi_joggerev[] = {
{ 0x1f, SONYPI_EVENT_JOGDIAL_UP },
{ 0x01, SONYPI_EVENT_JOGDIAL_DOWN },
{ 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED },
{ 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED },
{ 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP },
{ 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN },
{ 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED },
{ 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED },
{ 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP },
{ 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN },
{ 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED },
{ 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED },
{ 0x40, SONYPI_EVENT_JOGDIAL_PRESSED },
{ 0, 0 }
};
/* The set of possible capture button events */
static struct sonypi_event sonypi_captureev[] = {
{ 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED },
{ 0x07, SONYPI_EVENT_CAPTURE_PRESSED },
{ 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED },
{ 0, 0 }
};
/* The set of possible fnkeys events */
static struct sonypi_event sonypi_fnkeyev[] = {
{ 0x10, SONYPI_EVENT_FNKEY_ESC },
{ 0x11, SONYPI_EVENT_FNKEY_F1 },
{ 0x12, SONYPI_EVENT_FNKEY_F2 },
{ 0x13, SONYPI_EVENT_FNKEY_F3 },
{ 0x14, SONYPI_EVENT_FNKEY_F4 },
{ 0x15, SONYPI_EVENT_FNKEY_F5 },
{ 0x16, SONYPI_EVENT_FNKEY_F6 },
{ 0x17, SONYPI_EVENT_FNKEY_F7 },
{ 0x18, SONYPI_EVENT_FNKEY_F8 },
{ 0x19, SONYPI_EVENT_FNKEY_F9 },
{ 0x1a, SONYPI_EVENT_FNKEY_F10 },
{ 0x1b, SONYPI_EVENT_FNKEY_F11 },
{ 0x1c, SONYPI_EVENT_FNKEY_F12 },
{ 0x1f, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x21, SONYPI_EVENT_FNKEY_1 },
{ 0x22, SONYPI_EVENT_FNKEY_2 },
{ 0x31, SONYPI_EVENT_FNKEY_D },
{ 0x32, SONYPI_EVENT_FNKEY_E },
{ 0x33, SONYPI_EVENT_FNKEY_F },
{ 0x34, SONYPI_EVENT_FNKEY_S },
{ 0x35, SONYPI_EVENT_FNKEY_B },
{ 0x36, SONYPI_EVENT_FNKEY_ONLY },
{ 0, 0 }
};
/* The set of possible program key events */
static struct sonypi_event sonypi_pkeyev[] = {
{ 0x01, SONYPI_EVENT_PKEY_P1 },
{ 0x02, SONYPI_EVENT_PKEY_P2 },
{ 0x04, SONYPI_EVENT_PKEY_P3 },
{ 0x5c, SONYPI_EVENT_PKEY_P1 },
{ 0, 0 }
};
/* The set of possible bluetooth events */
static struct sonypi_event sonypi_blueev[] = {
{ 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED },
{ 0x59, SONYPI_EVENT_BLUETOOTH_ON },
{ 0x5a, SONYPI_EVENT_BLUETOOTH_OFF },
{ 0, 0 }
};
/* The set of possible wireless events */
static struct sonypi_event sonypi_wlessev[] = {
{ 0x59, SONYPI_EVENT_WIRELESS_ON },
{ 0x5a, SONYPI_EVENT_WIRELESS_OFF },
{ 0, 0 }
};
/* The set of possible back button events */
static struct sonypi_event sonypi_backev[] = {
{ 0x20, SONYPI_EVENT_BACK_PRESSED },
{ 0, 0 }
};
/* The set of possible help button events */
static struct sonypi_event sonypi_helpev[] = {
{ 0x3b, SONYPI_EVENT_HELP_PRESSED },
{ 0, 0 }
};
/* The set of possible lid events */
static struct sonypi_event sonypi_lidev[] = {
{ 0x51, SONYPI_EVENT_LID_CLOSED },
{ 0x50, SONYPI_EVENT_LID_OPENED },
{ 0, 0 }
};
/* The set of possible zoom events */
static struct sonypi_event sonypi_zoomev[] = {
{ 0x39, SONYPI_EVENT_ZOOM_PRESSED },
{ 0, 0 }
};
/* The set of possible thumbphrase events */
static struct sonypi_event sonypi_thumbphraseev[] = {
{ 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED },
{ 0, 0 }
};
/* The set of possible motioneye camera events */
static struct sonypi_event sonypi_meyeev[] = {
{ 0x00, SONYPI_EVENT_MEYE_FACE },
{ 0x01, SONYPI_EVENT_MEYE_OPPOSITE },
{ 0, 0 }
};
/* The set of possible memorystick events */
static struct sonypi_event sonypi_memorystickev[] = {
{ 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT },
{ 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT },
{ 0, 0 }
};
/* The set of possible battery events */
static struct sonypi_event sonypi_batteryev[] = {
{ 0x20, SONYPI_EVENT_BATTERY_INSERT },
{ 0x30, SONYPI_EVENT_BATTERY_REMOVE },
{ 0, 0 }
};
static struct sonypi_eventtypes {
int model;
u8 data;
unsigned long mask;
struct sonypi_event * events;
} sony_pic_eventtypes[] = {
{ SONYPI_DEVICE_TYPE1, 0, 0xffffffff, sonypi_releaseev },
{ SONYPI_DEVICE_TYPE1, 0x70, SONYPI_MEYE_MASK, sonypi_meyeev },
{ SONYPI_DEVICE_TYPE1, 0x30, SONYPI_LID_MASK, sonypi_lidev },
{ SONYPI_DEVICE_TYPE1, 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev },
{ SONYPI_DEVICE_TYPE1, 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev },
{ SONYPI_DEVICE_TYPE1, 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
{ SONYPI_DEVICE_TYPE1, 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
{ SONYPI_DEVICE_TYPE1, 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev },
{ SONYPI_DEVICE_TYPE1, 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
{ SONYPI_DEVICE_TYPE1, 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev },
{ SONYPI_DEVICE_TYPE2, 0, 0xffffffff, sonypi_releaseev },
{ SONYPI_DEVICE_TYPE2, 0x38, SONYPI_LID_MASK, sonypi_lidev },
{ SONYPI_DEVICE_TYPE2, 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev },
{ SONYPI_DEVICE_TYPE2, 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev },
{ SONYPI_DEVICE_TYPE2, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
{ SONYPI_DEVICE_TYPE2, 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
{ SONYPI_DEVICE_TYPE2, 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev },
{ SONYPI_DEVICE_TYPE2, 0x11, SONYPI_BACK_MASK, sonypi_backev },
{ SONYPI_DEVICE_TYPE2, 0x21, SONYPI_HELP_MASK, sonypi_helpev },
{ SONYPI_DEVICE_TYPE2, 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev },
{ SONYPI_DEVICE_TYPE2, 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev },
{ SONYPI_DEVICE_TYPE2, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
{ SONYPI_DEVICE_TYPE2, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
{ SONYPI_DEVICE_TYPE2, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
{ SONYPI_DEVICE_TYPE3, 0, 0xffffffff, sonypi_releaseev },
{ SONYPI_DEVICE_TYPE3, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
{ SONYPI_DEVICE_TYPE3, 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
{ SONYPI_DEVICE_TYPE3, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
{ SONYPI_DEVICE_TYPE3, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
{ SONYPI_DEVICE_TYPE3, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
{ 0 }
};
static int sony_pic_detect_device_type(void)
{
struct pci_dev *pcidev;
int model = 0;
if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82371AB_3, NULL)))
model = SONYPI_DEVICE_TYPE1;
else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_ICH6_1, NULL)))
model = SONYPI_DEVICE_TYPE3;
else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_ICH7_1, NULL)))
model = SONYPI_DEVICE_TYPE3;
else
model = SONYPI_DEVICE_TYPE2;
if (pcidev)
pci_dev_put(pcidev);
printk(KERN_INFO DRV_PFX "detected Type%d model\n",
model == SONYPI_DEVICE_TYPE1 ? 1 :
model == SONYPI_DEVICE_TYPE2 ? 2 : 3);
return model;
}
#define ITERATIONS_LONG 10000
#define ITERATIONS_SHORT 10
#define wait_on_command(command, iterations) { \
unsigned int n = iterations; \
while (--n && (command)) \
udelay(1); \
if (!n) \
dprintk("command failed at %s : %s (line %d)\n", \
__FILE__, __FUNCTION__, __LINE__); \
}
static u8 sony_pic_call1(u8 dev)
{
u8 v1, v2;
wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
ITERATIONS_LONG);
outb(dev, spic_dev.cur_ioport->io.minimum + 4);
v1 = inb_p(spic_dev.cur_ioport->io.minimum + 4);
v2 = inb_p(spic_dev.cur_ioport->io.minimum);
dprintk("sony_pic_call1: 0x%.4x\n", (v2 << 8) | v1);
return v2;
}
static u8 sony_pic_call2(u8 dev, u8 fn)
{
u8 v1;
wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
ITERATIONS_LONG);
outb(dev, spic_dev.cur_ioport->io.minimum + 4);
wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
ITERATIONS_LONG);
outb(fn, spic_dev.cur_ioport->io.minimum);
v1 = inb_p(spic_dev.cur_ioport->io.minimum);
dprintk("sony_pic_call2: 0x%.4x\n", v1);
return v1;
}
static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
{
u8 v1;
wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
outb(dev, spic_dev.cur_ioport->io.minimum + 4);
wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
outb(fn, spic_dev.cur_ioport->io.minimum);
wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
outb(v, spic_dev.cur_ioport->io.minimum);
v1 = inb_p(spic_dev.cur_ioport->io.minimum);
dprintk("sony_pic_call3: 0x%.4x\n", v1);
return v1;
}
/* camera tests and poweron/poweroff */
#define SONYPI_CAMERA_PICTURE 5
#define SONYPI_CAMERA_CONTROL 0x10
#define SONYPI_CAMERA_BRIGHTNESS 0
#define SONYPI_CAMERA_CONTRAST 1
#define SONYPI_CAMERA_HUE 2
#define SONYPI_CAMERA_COLOR 3
#define SONYPI_CAMERA_SHARPNESS 4
#define SONYPI_CAMERA_EXPOSURE_MASK 0xC
#define SONYPI_CAMERA_WHITE_BALANCE_MASK 0x3
#define SONYPI_CAMERA_PICTURE_MODE_MASK 0x30
#define SONYPI_CAMERA_MUTE_MASK 0x40
/* the rest don't need a loop until not 0xff */
#define SONYPI_CAMERA_AGC 6
#define SONYPI_CAMERA_AGC_MASK 0x30
#define SONYPI_CAMERA_SHUTTER_MASK 0x7
#define SONYPI_CAMERA_SHUTDOWN_REQUEST 7
#define SONYPI_CAMERA_CONTROL 0x10
#define SONYPI_CAMERA_STATUS 7
#define SONYPI_CAMERA_STATUS_READY 0x2
#define SONYPI_CAMERA_STATUS_POSITION 0x4
#define SONYPI_DIRECTION_BACKWARDS 0x4
#define SONYPI_CAMERA_REVISION 8
#define SONYPI_CAMERA_ROMVERSION 9
static int __sony_pic_camera_ready(void)
{
u8 v;
v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS);
return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY));
}
static int __sony_pic_camera_off(void)
{
if (!camera) {
printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
return -ENODEV;
}
wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE,
SONYPI_CAMERA_MUTE_MASK),
ITERATIONS_SHORT);
if (spic_dev.camera_power) {
sony_pic_call2(0x91, 0);
spic_dev.camera_power = 0;
}
return 0;
}
static int __sony_pic_camera_on(void)
{
int i, j, x;
if (!camera) {
printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
return -ENODEV;
}
if (spic_dev.camera_power)
return 0;
for (j = 5; j > 0; j--) {
for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++)
msleep(10);
sony_pic_call1(0x93);
for (i = 400; i > 0; i--) {
if (__sony_pic_camera_ready())
break;
msleep(10);
}
if (i)
break;
}
if (j == 0) {
printk(KERN_WARNING DRV_PFX "failed to power on camera\n");
return -ENODEV;
}
wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL,
0x5a),
ITERATIONS_SHORT);
spic_dev.camera_power = 1;
return 0;
}
/* External camera command (exported to the motion eye v4l driver) */
int sony_pic_camera_command(int command, u8 value)
{
if (!camera)
return -EIO;
mutex_lock(&spic_dev.lock);
switch (command) {
case SONY_PIC_COMMAND_SETCAMERA:
if (value)
__sony_pic_camera_on();
else
__sony_pic_camera_off();
break;
case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS:
wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value),
ITERATIONS_SHORT);
break;
case SONY_PIC_COMMAND_SETCAMERACONTRAST:
wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value),
ITERATIONS_SHORT);
break;
case SONY_PIC_COMMAND_SETCAMERAHUE:
wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value),
ITERATIONS_SHORT);
break;
case SONY_PIC_COMMAND_SETCAMERACOLOR:
wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value),
ITERATIONS_SHORT);
break;
case SONY_PIC_COMMAND_SETCAMERASHARPNESS:
wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value),
ITERATIONS_SHORT);
break;
case SONY_PIC_COMMAND_SETCAMERAPICTURE:
wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value),
ITERATIONS_SHORT);
break;
case SONY_PIC_COMMAND_SETCAMERAAGC:
wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value),
ITERATIONS_SHORT);
break;
default:
printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n",
command);
break;
}
mutex_unlock(&spic_dev.lock);
return 0;
}
EXPORT_SYMBOL(sony_pic_camera_command);
/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */
static void sony_pic_set_wwanpower(u8 state)
{
state = !!state;
mutex_lock(&spic_dev.lock);
if (spic_dev.wwan_power == state) {
mutex_unlock(&spic_dev.lock);
return;
}
sony_pic_call2(0xB0, state);
spic_dev.wwan_power = state;
mutex_unlock(&spic_dev.lock);
}
static ssize_t sony_pic_wwanpower_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
unsigned long value;
if (count > 31)
return -EINVAL;
value = simple_strtoul(buffer, NULL, 10);
sony_pic_set_wwanpower(value);
return count;
}
static ssize_t sony_pic_wwanpower_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
ssize_t count;
mutex_lock(&spic_dev.lock);
count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power);
mutex_unlock(&spic_dev.lock);
return count;
}
/* bluetooth subsystem power state */
static void __sony_pic_set_bluetoothpower(u8 state)
{
state = !!state;
if (spic_dev.bluetooth_power == state)
return;
sony_pic_call2(0x96, state);
sony_pic_call1(0x82);
spic_dev.bluetooth_power = state;
}
static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
unsigned long value;
if (count > 31)
return -EINVAL;
value = simple_strtoul(buffer, NULL, 10);
mutex_lock(&spic_dev.lock);
__sony_pic_set_bluetoothpower(value);
mutex_unlock(&spic_dev.lock);
return count;
}
static ssize_t sony_pic_bluetoothpower_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
ssize_t count = 0;
mutex_lock(&spic_dev.lock);
count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power);
mutex_unlock(&spic_dev.lock);
return count;
}
/* fan speed */
/* FAN0 information (reverse engineered from ACPI tables) */
#define SONY_PIC_FAN0_STATUS 0x93
static int sony_pic_set_fanspeed(unsigned long value)
{
return ec_write(SONY_PIC_FAN0_STATUS, value);
}
static int sony_pic_get_fanspeed(u8 *value)
{
return ec_read(SONY_PIC_FAN0_STATUS, value);
}
static ssize_t sony_pic_fanspeed_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
unsigned long value;
if (count > 31)
return -EINVAL;
value = simple_strtoul(buffer, NULL, 10);
if (sony_pic_set_fanspeed(value))
return -EIO;
return count;
}
static ssize_t sony_pic_fanspeed_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
u8 value = 0;
if (sony_pic_get_fanspeed(&value))
return -EIO;
return snprintf(buffer, PAGE_SIZE, "%d\n", value);
}
#define SPIC_ATTR(_name, _mode) \
struct device_attribute spic_attr_##_name = __ATTR(_name, \
_mode, sony_pic_## _name ##_show, \
sony_pic_## _name ##_store)
static SPIC_ATTR(bluetoothpower, 0644);
static SPIC_ATTR(wwanpower, 0644);
static SPIC_ATTR(fanspeed, 0644);
static struct attribute *spic_attributes[] = {
&spic_attr_bluetoothpower.attr,
&spic_attr_wwanpower.attr,
&spic_attr_fanspeed.attr,
NULL
};
static struct attribute_group spic_attribute_group = {
.attrs = spic_attributes
};
/******** SONYPI compatibility **********/
#ifdef CONFIG_SONY_LAPTOP_OLD
/* battery / brightness / temperature addresses */
#define SONYPI_BAT_FLAGS 0x81
#define SONYPI_LCD_LIGHT 0x96
#define SONYPI_BAT1_PCTRM 0xa0
#define SONYPI_BAT1_LEFT 0xa2
#define SONYPI_BAT1_MAXRT 0xa4
#define SONYPI_BAT2_PCTRM 0xa8
#define SONYPI_BAT2_LEFT 0xaa
#define SONYPI_BAT2_MAXRT 0xac
#define SONYPI_BAT1_MAXTK 0xb0
#define SONYPI_BAT1_FULL 0xb2
#define SONYPI_BAT2_MAXTK 0xb8
#define SONYPI_BAT2_FULL 0xba
#define SONYPI_TEMP_STATUS 0xC1
struct sonypi_compat_s {
struct fasync_struct *fifo_async;
struct kfifo *fifo;
spinlock_t fifo_lock;
wait_queue_head_t fifo_proc_list;
atomic_t open_count;
};
static struct sonypi_compat_s sonypi_compat = {
.open_count = ATOMIC_INIT(0),
};
static int sonypi_misc_fasync(int fd, struct file *filp, int on)
{
int retval;
retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
if (retval < 0)
return retval;
return 0;
}
static int sonypi_misc_release(struct inode *inode, struct file *file)
{
sonypi_misc_fasync(-1, file, 0);
atomic_dec(&sonypi_compat.open_count);
return 0;
}
static int sonypi_misc_open(struct inode *inode, struct file *file)
{
/* Flush input queue on first open */
if (atomic_inc_return(&sonypi_compat.open_count) == 1)
kfifo_reset(sonypi_compat.fifo);
return 0;
}
static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
ssize_t ret;
unsigned char c;
if ((kfifo_len(sonypi_compat.fifo) == 0) &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
ret = wait_event_interruptible(sonypi_compat.fifo_proc_list,
kfifo_len(sonypi_compat.fifo) != 0);
if (ret)
return ret;
while (ret < count &&
(kfifo_get(sonypi_compat.fifo, &c, sizeof(c)) == sizeof(c))) {
if (put_user(c, buf++))
return -EFAULT;
ret++;
}
if (ret > 0) {
struct inode *inode = file->f_path.dentry->d_inode;
inode->i_atime = current_fs_time(inode->i_sb);
}
return ret;
}
static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait)
{
poll_wait(file, &sonypi_compat.fifo_proc_list, wait);
if (kfifo_len(sonypi_compat.fifo))
return POLLIN | POLLRDNORM;
return 0;
}
static int ec_read16(u8 addr, u16 *value)
{
u8 val_lb, val_hb;
if (ec_read(addr, &val_lb))
return -1;
if (ec_read(addr + 1, &val_hb))
return -1;
*value = val_lb | (val_hb << 8);
return 0;
}
static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
unsigned int cmd, unsigned long arg)
{
int ret = 0;
void __user *argp = (void __user *)arg;
u8 val8;
u16 val16;
int value;
mutex_lock(&spic_dev.lock);
switch (cmd) {
case SONYPI_IOCGBRT:
if (sony_backlight_device == NULL) {
ret = -EIO;
break;
}
if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) {
ret = -EIO;
break;
}
val8 = ((value & 0xff) - 1) << 5;
if (copy_to_user(argp, &val8, sizeof(val8)))
ret = -EFAULT;
break;
case SONYPI_IOCSBRT:
if (sony_backlight_device == NULL) {
ret = -EIO;
break;
}
if (copy_from_user(&val8, argp, sizeof(val8))) {
ret = -EFAULT;
break;
}
if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
(val8 >> 5) + 1, NULL)) {
ret = -EIO;
break;
}
/* sync the backlight device status */
sony_backlight_device->props.brightness =
sony_backlight_get_brightness(sony_backlight_device);
break;
case SONYPI_IOCGBAT1CAP:
if (ec_read16(SONYPI_BAT1_FULL, &val16)) {
ret = -EIO;
break;
}
if (copy_to_user(argp, &val16, sizeof(val16)))
ret = -EFAULT;
break;
case SONYPI_IOCGBAT1REM:
if (ec_read16(SONYPI_BAT1_LEFT, &val16)) {
ret = -EIO;
break;
}
if (copy_to_user(argp, &val16, sizeof(val16)))
ret = -EFAULT;
break;
case SONYPI_IOCGBAT2CAP:
if (ec_read16(SONYPI_BAT2_FULL, &val16)) {
ret = -EIO;
break;
}
if (copy_to_user(argp, &val16, sizeof(val16)))
ret = -EFAULT;
break;
case SONYPI_IOCGBAT2REM:
if (ec_read16(SONYPI_BAT2_LEFT, &val16)) {
ret = -EIO;
break;
}
if (copy_to_user(argp, &val16, sizeof(val16)))
ret = -EFAULT;
break;
case SONYPI_IOCGBATFLAGS:
if (ec_read(SONYPI_BAT_FLAGS, &val8)) {
ret = -EIO;
break;
}
val8 &= 0x07;
if (copy_to_user(argp, &val8, sizeof(val8)))
ret = -EFAULT;
break;
case SONYPI_IOCGBLUE:
val8 = spic_dev.bluetooth_power;
if (copy_to_user(argp, &val8, sizeof(val8)))
ret = -EFAULT;
break;
case SONYPI_IOCSBLUE:
if (copy_from_user(&val8, argp, sizeof(val8))) {
ret = -EFAULT;
break;
}
__sony_pic_set_bluetoothpower(val8);
break;
/* FAN Controls */
case SONYPI_IOCGFAN:
if (sony_pic_get_fanspeed(&val8)) {
ret = -EIO;
break;
}
if (copy_to_user(argp, &val8, sizeof(val8)))
ret = -EFAULT;
break;
case SONYPI_IOCSFAN:
if (copy_from_user(&val8, argp, sizeof(val8))) {
ret = -EFAULT;
break;
}
if (sony_pic_set_fanspeed(val8))
ret = -EIO;
break;
/* GET Temperature (useful under APM) */
case SONYPI_IOCGTEMP:
if (ec_read(SONYPI_TEMP_STATUS, &val8)) {
ret = -EIO;
break;
}
if (copy_to_user(argp, &val8, sizeof(val8)))
ret = -EFAULT;
break;
default:
ret = -EINVAL;
}
mutex_unlock(&spic_dev.lock);
return ret;
}
static const struct file_operations sonypi_misc_fops = {
.owner = THIS_MODULE,
.read = sonypi_misc_read,
.poll = sonypi_misc_poll,
.open = sonypi_misc_open,
.release = sonypi_misc_release,
.fasync = sonypi_misc_fasync,
.ioctl = sonypi_misc_ioctl,
};
static struct miscdevice sonypi_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "sonypi",
.fops = &sonypi_misc_fops,
};
static void sonypi_compat_report_event(u8 event)
{
kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event));
kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN);
wake_up_interruptible(&sonypi_compat.fifo_proc_list);
}
static int sonypi_compat_init(void)
{
int error;
spin_lock_init(&sonypi_compat.fifo_lock);
sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
&sonypi_compat.fifo_lock);
if (IS_ERR(sonypi_compat.fifo)) {
printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
return PTR_ERR(sonypi_compat.fifo);
}
init_waitqueue_head(&sonypi_compat.fifo_proc_list);
if (minor != -1)
sonypi_misc_device.minor = minor;
error = misc_register(&sonypi_misc_device);
if (error) {
printk(KERN_ERR DRV_PFX "misc_register failed\n");
goto err_free_kfifo;
}
if (minor == -1)
printk(KERN_INFO DRV_PFX "device allocated minor is %d\n",
sonypi_misc_device.minor);
return 0;
err_free_kfifo:
kfifo_free(sonypi_compat.fifo);
return error;
}
static void sonypi_compat_exit(void)
{
misc_deregister(&sonypi_misc_device);
kfifo_free(sonypi_compat.fifo);
}
#else
static int sonypi_compat_init(void) { return 0; }
static void sonypi_compat_exit(void) { }
static void sonypi_compat_report_event(u8 event) { }
#endif /* CONFIG_SONY_LAPTOP_OLD */
/*
* ACPI callbacks
*/
static acpi_status
sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
{
u32 i;
struct sony_pic_dev *dev = (struct sony_pic_dev *)context;
switch (resource->type) {
case ACPI_RESOURCE_TYPE_START_DEPENDENT:
case ACPI_RESOURCE_TYPE_END_DEPENDENT:
return AE_OK;
case ACPI_RESOURCE_TYPE_IRQ:
{
struct acpi_resource_irq *p = &resource->data.irq;
struct sony_pic_irq *interrupt = NULL;
if (!p || !p->interrupt_count) {
/*
* IRQ descriptors may have no IRQ# bits set,
* particularly those those w/ _STA disabled
*/
dprintk("Blank IRQ resource\n");
return AE_OK;
}
for (i = 0; i < p->interrupt_count; i++) {
if (!p->interrupts[i]) {
printk(KERN_WARNING DRV_PFX
"Invalid IRQ %d\n",
p->interrupts[i]);
continue;
}
interrupt = kzalloc(sizeof(*interrupt),
GFP_KERNEL);
if (!interrupt)
return AE_ERROR;
list_add_tail(&interrupt->list, &dev->interrupts);
interrupt->irq.triggering = p->triggering;
interrupt->irq.polarity = p->polarity;
interrupt->irq.sharable = p->sharable;
interrupt->irq.interrupt_count = 1;
interrupt->irq.interrupts[0] = p->interrupts[i];
}
return AE_OK;
}
case ACPI_RESOURCE_TYPE_IO:
{
struct acpi_resource_io *io = &resource->data.io;
struct sony_pic_ioport *ioport = NULL;
if (!io) {
dprintk("Blank IO resource\n");
return AE_OK;
}
ioport = kzalloc(sizeof(*ioport), GFP_KERNEL);
if (!ioport)
return AE_ERROR;
list_add_tail(&ioport->list, &dev->ioports);
memcpy(&ioport->io, io, sizeof(*io));
return AE_OK;
}
default:
dprintk("Resource %d isn't an IRQ nor an IO port\n",
resource->type);
case ACPI_RESOURCE_TYPE_END_TAG:
return AE_OK;
}
return AE_CTRL_TERMINATE;
}
static int sony_pic_possible_resources(struct acpi_device *device)
{
int result = 0;
acpi_status status = AE_OK;
if (!device)
return -EINVAL;
/* get device status */
/* see acpi_pci_link_get_current acpi_pci_link_get_possible */
dprintk("Evaluating _STA\n");
result = acpi_bus_get_status(device);
if (result) {
printk(KERN_WARNING DRV_PFX "Unable to read status\n");
goto end;
}
if (!device->status.enabled)
dprintk("Device disabled\n");
else
dprintk("Device enabled\n");
/*
* Query and parse 'method'
*/
dprintk("Evaluating %s\n", METHOD_NAME__PRS);
status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,
sony_pic_read_possible_resource, &spic_dev);
if (ACPI_FAILURE(status)) {
printk(KERN_WARNING DRV_PFX
"Failure evaluating %s\n",
METHOD_NAME__PRS);
result = -ENODEV;
}
end:
return result;
}
/*
* Disable the spic device by calling its _DIS method
*/
static int sony_pic_disable(struct acpi_device *device)
{
if (ACPI_FAILURE(acpi_evaluate_object(device->handle, "_DIS", 0, NULL)))
return -ENXIO;
dprintk("Device disabled\n");
return 0;
}
/*
* Based on drivers/acpi/pci_link.c:acpi_pci_link_set
*
* Call _SRS to set current resources
*/
static int sony_pic_enable(struct acpi_device *device,
struct sony_pic_ioport *ioport, struct sony_pic_irq *irq)
{
acpi_status status;
int result = 0;
struct {
struct acpi_resource io_res;
struct acpi_resource irq_res;
struct acpi_resource end;
} *resource;
struct acpi_buffer buffer = { 0, NULL };
if (!ioport || !irq)
return -EINVAL;
/* init acpi_buffer */
resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL);
if (!resource)
return -ENOMEM;
buffer.length = sizeof(*resource) + 1;
buffer.pointer = resource;
/* setup io resource */
resource->io_res.type = ACPI_RESOURCE_TYPE_IO;
resource->io_res.length = sizeof(struct acpi_resource);
memcpy(&resource->io_res.data.io, &ioport->io,
sizeof(struct acpi_resource_io));
/* setup irq resource */
resource->irq_res.type = ACPI_RESOURCE_TYPE_IRQ;
resource->irq_res.length = sizeof(struct acpi_resource);
memcpy(&resource->irq_res.data.irq, &irq->irq,
sizeof(struct acpi_resource_irq));
/* we requested a shared irq */
resource->irq_res.data.irq.sharable = ACPI_SHARED;
resource->end.type = ACPI_RESOURCE_TYPE_END_TAG;
/* Attempt to set the resource */
dprintk("Evaluating _SRS\n");
status = acpi_set_current_resources(device->handle, &buffer);
/* check for total failure */
if (ACPI_FAILURE(status)) {
printk(KERN_ERR DRV_PFX "Error evaluating _SRS");
result = -ENODEV;
goto end;
}
/* Necessary device initializations calls (from sonypi) */
sony_pic_call1(0x82);
sony_pic_call2(0x81, 0xff);
sony_pic_call1(compat ? 0x92 : 0x82);
end:
kfree(resource);
return result;
}
/*****************
*
* ISR: some event is available
*
*****************/
static irqreturn_t sony_pic_irq(int irq, void *dev_id)
{
int i, j;
u32 port_val = 0;
u8 ev = 0;
u8 data_mask = 0;
u8 device_event = 0;
struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id;
acpi_os_read_port(dev->cur_ioport->io.minimum, &port_val,
dev->cur_ioport->io.address_length);
ev = port_val & SONY_PIC_EV_MASK;
data_mask = 0xff & (port_val >> (dev->cur_ioport->io.address_length - 8));
dprintk("event (0x%.8x [%.2x] [%.2x]) at port 0x%.4x\n",
port_val, ev, data_mask, dev->cur_ioport->io.minimum);
if (ev == 0x00 || ev == 0xff)
return IRQ_HANDLED;
for (i = 0; sony_pic_eventtypes[i].model; i++) {
if (spic_dev.model != sony_pic_eventtypes[i].model)
continue;
if ((data_mask & sony_pic_eventtypes[i].data) !=
sony_pic_eventtypes[i].data)
continue;
if (!(mask & sony_pic_eventtypes[i].mask))
continue;
for (j = 0; sony_pic_eventtypes[i].events[j].event; j++) {
if (ev == sony_pic_eventtypes[i].events[j].data) {
device_event =
sony_pic_eventtypes[i].events[j].event;
goto found;
}
}
}
return IRQ_HANDLED;
found:
sony_laptop_report_input_event(device_event);
acpi_bus_generate_event(spic_dev.acpi_dev, 1, device_event);
sonypi_compat_report_event(device_event);
return IRQ_HANDLED;
}
/*****************
*
* ACPI driver
*
*****************/
static int sony_pic_remove(struct acpi_device *device, int type)
{
struct sony_pic_ioport *io, *tmp_io;
struct sony_pic_irq *irq, *tmp_irq;
sonypi_compat_exit();
if (sony_pic_disable(device)) {
printk(KERN_ERR DRV_PFX "Couldn't disable device.\n");
return -ENXIO;
}
free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
release_region(spic_dev.cur_ioport->io.minimum,
spic_dev.cur_ioport->io.address_length);
sony_laptop_remove_input();
/* pf attrs */
sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
sony_pf_remove();
list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
list_del(&io->list);
kfree(io);
}
list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
list_del(&irq->list);
kfree(irq);
}
spic_dev.cur_ioport = NULL;
spic_dev.cur_irq = NULL;
dprintk(SONY_PIC_DRIVER_NAME " removed.\n");
return 0;
}
static int sony_pic_add(struct acpi_device *device)
{
int result;
struct sony_pic_ioport *io, *tmp_io;
struct sony_pic_irq *irq, *tmp_irq;
printk(KERN_INFO DRV_PFX "%s v%s.\n",
SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
spic_dev.acpi_dev = device;
strcpy(acpi_device_class(device), "sony/hotkey");
spic_dev.model = sony_pic_detect_device_type();
mutex_init(&spic_dev.lock);
/* read _PRS resources */
result = sony_pic_possible_resources(device);
if (result) {
printk(KERN_ERR DRV_PFX
"Unabe to read possible resources.\n");
goto err_free_resources;
}
/* setup input devices and helper fifo */
result = sony_laptop_setup_input();
if (result) {
printk(KERN_ERR DRV_PFX
"Unabe to create input devices.\n");
goto err_free_resources;
}
/* request io port */
list_for_each_entry(io, &spic_dev.ioports, list) {
if (request_region(io->io.minimum, io->io.address_length,
"Sony Programable I/O Device")) {
dprintk("I/O port: 0x%.4x (0x%.4x) + 0x%.2x\n",
io->io.minimum, io->io.maximum,
io->io.address_length);
spic_dev.cur_ioport = io;
break;
}
}
if (!spic_dev.cur_ioport) {
printk(KERN_ERR DRV_PFX "Failed to request_region.\n");
result = -ENODEV;
goto err_remove_input;
}
/* request IRQ */
list_for_each_entry(irq, &spic_dev.interrupts, list) {
if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
IRQF_SHARED, "sony-laptop", &spic_dev)) {
dprintk("IRQ: %d - triggering: %d - "
"polarity: %d - shr: %d\n",
irq->irq.interrupts[0],
irq->irq.triggering,
irq->irq.polarity,
irq->irq.sharable);
spic_dev.cur_irq = irq;
break;
}
}
if (!spic_dev.cur_irq) {
printk(KERN_ERR DRV_PFX "Failed to request_irq.\n");
result = -ENODEV;
goto err_release_region;
}
/* set resource status _SRS */
result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
if (result) {
printk(KERN_ERR DRV_PFX "Couldn't enable device.\n");
goto err_free_irq;
}
spic_dev.bluetooth_power = -1;
/* create device attributes */
result = sony_pf_add();
if (result)
goto err_disable_device;
result = sysfs_create_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
if (result)
goto err_remove_pf;
if (sonypi_compat_init())
goto err_remove_pf;
return 0;
err_remove_pf:
sony_pf_remove();
err_disable_device:
sony_pic_disable(device);
err_free_irq:
free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
err_release_region:
release_region(spic_dev.cur_ioport->io.minimum,
spic_dev.cur_ioport->io.address_length);
err_remove_input:
sony_laptop_remove_input();
err_free_resources:
list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
list_del(&io->list);
kfree(io);
}
list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
list_del(&irq->list);
kfree(irq);
}
spic_dev.cur_ioport = NULL;
spic_dev.cur_irq = NULL;
return result;
}
static int sony_pic_suspend(struct acpi_device *device, pm_message_t state)
{
if (sony_pic_disable(device))
return -ENXIO;
return 0;
}
static int sony_pic_resume(struct acpi_device *device)
{
sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
return 0;
}
static struct acpi_driver sony_pic_driver = {
.name = SONY_PIC_DRIVER_NAME,
.class = SONY_PIC_CLASS,
.ids = SONY_PIC_HID,
.owner = THIS_MODULE,
.ops = {
.add = sony_pic_add,
.remove = sony_pic_remove,
.suspend = sony_pic_suspend,
.resume = sony_pic_resume,
},
};
static struct dmi_system_id __initdata sonypi_dmi_table[] = {
{
.ident = "Sony Vaio",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"),
},
},
{
.ident = "Sony Vaio",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"),
},
},
{ }
};
static int __init sony_laptop_init(void)
{
int result;
if (!no_spic && dmi_check_system(sonypi_dmi_table)) {
result = acpi_bus_register_driver(&sony_pic_driver);
if (result) {
printk(KERN_ERR DRV_PFX
"Unable to register SPIC driver.");
goto out;
}
}
result = acpi_bus_register_driver(&sony_nc_driver);
if (result) {
printk(KERN_ERR DRV_PFX "Unable to register SNC driver.");
goto out_unregister_pic;
}
return 0;
out_unregister_pic:
if (!no_spic)
acpi_bus_unregister_driver(&sony_pic_driver);
out:
return result;
}
static void __exit sony_acpi_exit(void)
static void __exit sony_laptop_exit(void)
{
acpi_bus_unregister_driver(&sony_acpi_driver);
acpi_bus_unregister_driver(&sony_nc_driver);
if (!no_spic)
acpi_bus_unregister_driver(&sony_pic_driver);
}
module_init(sony_acpi_init);
module_exit(sony_acpi_exit);
module_init(sony_laptop_init);
module_exit(sony_laptop_exit);
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* thinkpad_acpi.h - ThinkPad ACPI Extras
*
*
* Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
* Copyright (C) 2006-2007 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef __THINKPAD_ACPI_H__
#define __THINKPAD_ACPI_H__
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/sysfs.h>
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/platform_device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.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>
/****************************************************************************
* Main driver
*/
#define IBM_NAME "thinkpad"
#define IBM_DESC "ThinkPad ACPI Extras"
#define IBM_FILE "thinkpad_acpi"
#define IBM_URL "http://ibm-acpi.sf.net/"
#define IBM_MAIL "ibm-acpi-devel@lists.sourceforge.net"
#define IBM_PROC_DIR "ibm"
#define IBM_ACPI_EVENT_PREFIX "ibm"
#define IBM_DRVR_NAME IBM_FILE
#define IBM_LOG IBM_FILE ": "
#define IBM_ERR KERN_ERR IBM_LOG
#define IBM_NOTICE KERN_NOTICE IBM_LOG
#define IBM_INFO KERN_INFO IBM_LOG
#define IBM_DEBUG KERN_DEBUG IBM_LOG
#define IBM_MAX_ACPI_ARGS 3
/* ThinkPad CMOS commands */
#define TP_CMOS_VOLUME_DOWN 0
#define TP_CMOS_VOLUME_UP 1
#define TP_CMOS_VOLUME_MUTE 2
#define TP_CMOS_BRIGHTNESS_UP 4
#define TP_CMOS_BRIGHTNESS_DOWN 5
#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)))
/* Debugging */
#define TPACPI_DBG_ALL 0xffff
#define TPACPI_DBG_ALL 0xffff
#define TPACPI_DBG_INIT 0x0001
#define TPACPI_DBG_EXIT 0x0002
#define dbg_printk(a_dbg_level, format, arg...) \
do { if (dbg_level & a_dbg_level) \
printk(IBM_DEBUG "%s: " format, __func__ , ## arg); } while (0)
#ifdef CONFIG_THINKPAD_ACPI_DEBUG
#define vdbg_printk(a_dbg_level, format, arg...) \
dbg_printk(a_dbg_level, format, ## arg)
static const char *str_supported(int is_supported);
#else
#define vdbg_printk(a_dbg_level, format, arg...)
#endif
/* ACPI HIDs */
#define IBM_HKEY_HID "IBM0068"
#define IBM_PCI_HID "PNP0A03"
/* ACPI helpers */
static int __must_check acpi_evalf(acpi_handle handle,
void *res, char *method, char *fmt, ...);
static int __must_check acpi_ec_read(int i, u8 * p);
static int __must_check acpi_ec_write(int i, u8 v);
static int __must_check _sta(acpi_handle handle);
/* ACPI handles */
static acpi_handle root_handle; /* root namespace */
static acpi_handle ec_handle; /* EC */
static acpi_handle ecrd_handle, ecwr_handle; /* 570 EC access */
static acpi_handle cmos_handle, hkey_handle; /* basic thinkpad handles */
static void drv_acpi_handle_init(char *name,
acpi_handle *handle, acpi_handle parent,
char **paths, int num_paths, char **path);
#define IBM_ACPIHANDLE_INIT(object) \
drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \
object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
/* ThinkPad ACPI helpers */
static int issue_thinkpad_cmos_command(int cmos_cmd);
/* procfs support */
static struct proc_dir_entry *proc_dir;
/* procfs helpers */
static int dispatch_procfs_read(char *page, char **start, off_t off,
int count, int *eof, void *data);
static int dispatch_procfs_write(struct file *file,
const char __user * userbuf,
unsigned long count, void *data);
static char *next_cmd(char **cmds);
/* sysfs support */
struct attribute_set {
unsigned int members, max_members;
struct attribute_group group;
};
static struct attribute_set *create_attr_set(unsigned int max_members,
const char* name);
#define destroy_attr_set(_set) \
kfree(_set);
static int add_to_attr_set(struct attribute_set* s, struct attribute *attr);
static int add_many_to_attr_set(struct attribute_set* s,
struct attribute **attr,
unsigned int count);
#define register_attr_set_with_sysfs(_attr_set, _kobj) \
sysfs_create_group(_kobj, &_attr_set->group)
static void delete_attr_set(struct attribute_set* s, struct kobject *kobj);
static int parse_strtoul(const char *buf, unsigned long max,
unsigned long *value);
/* Device model */
static struct platform_device *tpacpi_pdev;
static struct class_device *tpacpi_hwmon;
static struct platform_driver tpacpi_pdriver;
static int tpacpi_create_driver_attributes(struct device_driver *drv);
static void tpacpi_remove_driver_attributes(struct device_driver *drv);
/* Module */
static int experimental;
static u32 dbg_level;
static int force_load;
static char *ibm_thinkpad_ec_found;
static char* check_dmi_for_ec(void);
static int thinkpad_acpi_module_init(void);
static void thinkpad_acpi_module_exit(void);
/****************************************************************************
* Subdrivers
*/
struct ibm_struct;
struct tp_acpi_drv_struct {
char *hid;
struct acpi_driver *driver;
void (*notify) (struct ibm_struct *, u32);
acpi_handle *handle;
u32 type;
struct acpi_device *device;
};
struct ibm_struct {
char *name;
int (*read) (char *);
int (*write) (char *);
void (*exit) (void);
struct list_head all_drivers;
struct tp_acpi_drv_struct *acpi;
struct {
u8 acpi_driver_registered:1;
u8 acpi_notify_installed:1;
u8 proc_created:1;
u8 init_called:1;
u8 experimental:1;
} flags;
};
struct ibm_init_struct {
char param[32];
int (*init) (struct ibm_init_struct *);
struct ibm_struct *data;
};
static struct {
#ifdef CONFIG_THINKPAD_ACPI_BAY
u16 bay_status:1;
u16 bay_eject:1;
u16 bay_status2:1;
u16 bay_eject2:1;
#endif
u16 bluetooth:1;
u16 hotkey:1;
u16 hotkey_mask:1;
u16 light:1;
u16 light_status:1;
u16 wan:1;
u16 fan_ctrl_status_undef:1;
} tp_features;
static struct list_head tpacpi_all_drivers;
static struct ibm_init_struct ibms_init[];
static int set_ibm_param(const char *val, struct kernel_param *kp);
static int ibm_init(struct ibm_init_struct *iibm);
static void ibm_exit(struct ibm_struct *ibm);
/*
* procfs master subdriver
*/
static int thinkpad_acpi_driver_init(struct ibm_init_struct *iibm);
static int thinkpad_acpi_driver_read(char *p);
/*
* Bay subdriver
*/
#ifdef CONFIG_THINKPAD_ACPI_BAY
static acpi_handle bay_handle, bay_ej_handle;
static acpi_handle bay2_handle, bay2_ej_handle;
static int bay_init(struct ibm_init_struct *iibm);
static void bay_notify(struct ibm_struct *ibm, u32 event);
static int bay_read(char *p);
static int bay_write(char *buf);
#endif /* CONFIG_THINKPAD_ACPI_BAY */
/*
* Beep subdriver
*/
static acpi_handle beep_handle;
static int beep_read(char *p);
static int beep_write(char *buf);
/*
* Bluetooth subdriver
*/
#define TPACPI_BLUETH_SYSFS_GROUP "bluetooth"
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 */
};
static int bluetooth_init(struct ibm_init_struct *iibm);
static int bluetooth_get_radiosw(void);
static int bluetooth_set_radiosw(int radio_on);
static int bluetooth_read(char *p);
static int bluetooth_write(char *buf);
/*
* Brightness (backlight) subdriver
*/
#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
static struct backlight_device *ibm_backlight_device;
static int brightness_offset = 0x31;
static int brightness_init(struct ibm_init_struct *iibm);
static void brightness_exit(void);
static int brightness_get(struct backlight_device *bd);
static int brightness_set(int value);
static int brightness_update_status(struct backlight_device *bd);
static int brightness_read(char *p);
static int brightness_write(char *buf);
/*
* CMOS subdriver
*/
static int cmos_read(char *p);
static int cmos_write(char *buf);
/*
* Dock subdriver
*/
#ifdef CONFIG_THINKPAD_ACPI_DOCK
static acpi_handle pci_handle;
static acpi_handle dock_handle;
static void dock_notify(struct ibm_struct *ibm, u32 event);
static int dock_read(char *p);
static int dock_write(char *buf);
#endif /* CONFIG_THINKPAD_ACPI_DOCK */
/*
* EC dump subdriver
*/
static int ecdump_read(char *p) ;
static int ecdump_write(char *buf);
/*
* Fan subdriver
*/
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 */
TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */
TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */
TPACPI_FAN_LAST_LEVEL = 0x100, /* Use cached last-seen fan level */
};
enum fan_status_access_mode {
TPACPI_FAN_NONE = 0, /* No fan status or control */
TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */
TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */
};
enum fan_control_access_mode {
TPACPI_FAN_WR_NONE = 0, /* No fan control */
TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */
TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */
TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */
};
enum fan_control_commands {
TPACPI_FAN_CMD_SPEED = 0x0001, /* speed command */
TPACPI_FAN_CMD_LEVEL = 0x0002, /* level command */
TPACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd,
* and also watchdog cmd */
};
static int fan_control_allowed;
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 u8 fan_control_initial_status;
static u8 fan_control_desired_level;
static int fan_watchdog_maxinterval;
static struct mutex fan_mutex;
static acpi_handle fans_handle, gfan_handle, sfan_handle;
static int fan_init(struct ibm_init_struct *iibm);
static void fan_exit(void);
static int fan_get_status(u8 *status);
static int fan_get_status_safe(u8 *status);
static int fan_get_speed(unsigned int *speed);
static void fan_update_desired_level(u8 status);
static void fan_watchdog_fire(struct work_struct *ignored);
static void fan_watchdog_reset(void);
static int fan_set_level(int level);
static int fan_set_level_safe(int level);
static int fan_set_enable(void);
static int fan_set_disable(void);
static int fan_set_speed(int speed);
static int fan_read(char *p);
static int fan_write(char *buf);
static int fan_write_cmd_level(const char *cmd, int *rc);
static int fan_write_cmd_enable(const char *cmd, int *rc);
static int fan_write_cmd_disable(const char *cmd, int *rc);
static int fan_write_cmd_speed(const char *cmd, int *rc);
static int fan_write_cmd_watchdog(const char *cmd, int *rc);
/*
* Hotkey subdriver
*/
#define TPACPI_HOTKEY_SYSFS_GROUP "hotkey"
static int hotkey_orig_status;
static int hotkey_orig_mask;
static struct mutex hotkey_mutex;
static int hotkey_init(struct ibm_init_struct *iibm);
static void hotkey_exit(void);
static int hotkey_get(int *status, int *mask);
static int hotkey_set(int status, int mask);
static void hotkey_notify(struct ibm_struct *ibm, u32 event);
static int hotkey_read(char *p);
static int hotkey_write(char *buf);
/*
* LED subdriver
*/
enum led_access_mode {
TPACPI_LED_NONE = 0,
TPACPI_LED_570, /* 570 */
TPACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
TPACPI_LED_NEW, /* all others */
};
enum { /* For TPACPI_LED_OLD */
TPACPI_LED_EC_HLCL = 0x0c, /* EC reg to get led to power on */
TPACPI_LED_EC_HLBL = 0x0d, /* EC reg to blink a lit led */
TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */
};
static enum led_access_mode led_supported;
static acpi_handle led_handle;
static int led_init(struct ibm_init_struct *iibm);
static int led_read(char *p);
static int led_write(char *buf);
/*
* Light (thinklight) subdriver
*/
static acpi_handle lght_handle, ledb_handle;
static int light_init(struct ibm_init_struct *iibm);
static int light_read(char *p);
static int light_write(char *buf);
/*
* Thermal subdriver
*/
enum thermal_access_mode {
TPACPI_THERMAL_NONE = 0, /* No thermal support */
TPACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */
TPACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */
TPACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */
TPACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */
};
enum { /* TPACPI_THERMAL_TPEC_* */
TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */
TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */
TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */
};
#define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */
struct ibm_thermal_sensors_struct {
s32 temp[TPACPI_MAX_THERMAL_SENSORS];
};
static enum thermal_access_mode thermal_read_mode;
static int thermal_init(struct ibm_init_struct *iibm);
static int thermal_get_sensor(int idx, s32 *value);
static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s);
static int thermal_read(char *p);
/*
* Video subdriver
*/
enum video_access_mode {
TPACPI_VIDEO_NONE = 0,
TPACPI_VIDEO_570, /* 570 */
TPACPI_VIDEO_770, /* 600e/x, 770e, 770x */
TPACPI_VIDEO_NEW, /* all others */
};
enum { /* video status flags, based on VIDEO_570 */
TP_ACPI_VIDEO_S_LCD = 0x01, /* LCD output enabled */
TP_ACPI_VIDEO_S_CRT = 0x02, /* CRT output enabled */
TP_ACPI_VIDEO_S_DVI = 0x08, /* DVI output enabled */
};
enum { /* TPACPI_VIDEO_570 constants */
TP_ACPI_VIDEO_570_PHSCMD = 0x87, /* unknown magic constant :( */
TP_ACPI_VIDEO_570_PHSMASK = 0x03, /* PHS bits that map to
* video_status_flags */
TP_ACPI_VIDEO_570_PHS2CMD = 0x8b, /* unknown magic constant :( */
TP_ACPI_VIDEO_570_PHS2SET = 0x80, /* unknown magic constant :( */
};
static enum video_access_mode video_supported;
static int video_orig_autosw;
static acpi_handle vid_handle, vid2_handle;
static int video_init(struct ibm_init_struct *iibm);
static void video_exit(void);
static int video_outputsw_get(void);
static int video_outputsw_set(int status);
static int video_autosw_get(void);
static int video_autosw_set(int enable);
static int video_outputsw_cycle(void);
static int video_expand_toggle(void);
static int video_read(char *p);
static int video_write(char *buf);
/*
* Volume subdriver
*/
static int volume_offset = 0x30;
static int volume_read(char *p);
static int volume_write(char *buf);
/*
* Wan subdriver
*/
#define TPACPI_WAN_SYSFS_GROUP "wwan"
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 */
};
static int wan_init(struct ibm_init_struct *iibm);
static int wan_get_radiosw(void);
static int wan_set_radiosw(int radio_on);
static int wan_read(char *p);
static int wan_write(char *buf);
#endif /* __THINKPAD_ACPI_H */
......@@ -317,6 +317,10 @@ static int __init acpi_pci_init(void)
{
int ret;
if (acpi_gbl_FADT.boot_flags & BAF_MSI_NOT_SUPPORTED) {
printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n");
pci_no_msi();
}
ret = register_acpi_bus_type(&acpi_pci_bus);
if (ret)
return 0;
......
......@@ -276,6 +276,7 @@ enum acpi_prefered_pm_profiles {
#define BAF_LEGACY_DEVICES 0x0001
#define BAF_8042_KEYBOARD_CONTROLLER 0x0002
#define BAF_MSI_NOT_SUPPORTED 0x0008
#define FADT2_REVISION_ID 3
#define FADT2_MINUS_REVISION_ID 2
......
#ifndef _SONYLAPTOP_H_
#define _SONYLAPTOP_H_
#include <linux/types.h>
#ifdef __KERNEL__
/* used only for communication between v4l and sony-laptop */
#define SONY_PIC_COMMAND_GETCAMERA 1 /* obsolete */
#define SONY_PIC_COMMAND_SETCAMERA 2
#define SONY_PIC_COMMAND_GETCAMERABRIGHTNESS 3 /* obsolete */
#define SONY_PIC_COMMAND_SETCAMERABRIGHTNESS 4
#define SONY_PIC_COMMAND_GETCAMERACONTRAST 5 /* obsolete */
#define SONY_PIC_COMMAND_SETCAMERACONTRAST 6
#define SONY_PIC_COMMAND_GETCAMERAHUE 7 /* obsolete */
#define SONY_PIC_COMMAND_SETCAMERAHUE 8
#define SONY_PIC_COMMAND_GETCAMERACOLOR 9 /* obsolete */
#define SONY_PIC_COMMAND_SETCAMERACOLOR 10
#define SONY_PIC_COMMAND_GETCAMERASHARPNESS 11 /* obsolete */
#define SONY_PIC_COMMAND_SETCAMERASHARPNESS 12
#define SONY_PIC_COMMAND_GETCAMERAPICTURE 13 /* obsolete */
#define SONY_PIC_COMMAND_SETCAMERAPICTURE 14
#define SONY_PIC_COMMAND_GETCAMERAAGC 15 /* obsolete */
#define SONY_PIC_COMMAND_SETCAMERAAGC 16
#define SONY_PIC_COMMAND_GETCAMERADIRECTION 17 /* obsolete */
#define SONY_PIC_COMMAND_GETCAMERAROMVERSION 18 /* obsolete */
#define SONY_PIC_COMMAND_GETCAMERAREVISION 19 /* obsolete */
int sony_pic_camera_command(int command, u8 value);
#endif /* __KERNEL__ */
#endif /* _SONYLAPTOP_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment