Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6
* 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6: (107 commits)
smc911x: fix compilation breakage wjen debug is on
[netdrvr] eexpress: minor corrections
add NAPI support to sb1250-mac.c
ixgb: ROUND_UP macro cleanup in drivers/net/ixgb
e1000: ROUND_UP macro cleanup in drivers/net/e1000
Generic HDLC sparse annotations
e100: Optionally use I/O mode only to access register space
e100: allow bad MAC address when running with invalid eeprom csum
ehea: fix for dlpar support
ehea: fix for sysfs entries
3C509: Remove unnecessary include of <linux/pm_legacy.h>
NetXen: Fix for vmalloc issues
NetXen: Fixes for Power PC architecture
NetXen: Port swap feature for multi port cards
NetXen: Removal of redundant macros
NetXen: Multi PCI support for Quad cards
NetXen: Removal of redundant argument passing
NetXen: Use multiple PCI functions
[netdrvr e100] experiment with doing RX in a similar manner to eepro100
[PATCH] ieee80211: add missing global needed by IEEE80211_DEBUG_XXXX
...
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 2017942..84c3bd0 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -181,19 +181,41 @@
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.
+ 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_dbg_level= [HW,ACPI]
+ 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
diff --git a/Documentation/sony-laptop.txt b/Documentation/sony-laptop.txt
index dfd26df..7a5c1a8 100644
--- a/Documentation/sony-laptop.txt
+++ b/Documentation/sony-laptop.txt
@@ -3,12 +3,18 @@
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 @@
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 @@
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 @@
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:
-----------------
diff --git a/Documentation/ibm-acpi.txt b/Documentation/thinkpad-acpi.txt
similarity index 60%
rename from Documentation/ibm-acpi.txt
rename to Documentation/thinkpad-acpi.txt
index 0132d36..2d48033 100644
--- a/Documentation/ibm-acpi.txt
+++ b/Documentation/thinkpad-acpi.txt
@@ -1,16 +1,22 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
------------
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 @@
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 @@
be used through the "ThinkPad Buttons" utility, see
http://www.nongnu.org/tpb/
-Bluetooth -- /proc/acpi/ibm/bluetooth
--------------------------------------
+procfs notes:
-This feature shows the presence and current state of a Bluetooth
-device. If Bluetooth is installed, the following commands can be used:
+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.
+
+
+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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
HDD temperature drops to 41 degrees. These thresholds cannot
currently be controlled.
-The fan level can be controlled with the command:
-
- echo 'level <level>' > /proc/acpi/ibm/thermal
-
-Where <level> is an integer from 0 to 7, or one of the words "auto"
-or "disengaged" (without the quotes). Not all ThinkPads support the
-"auto" and "disengaged" levels.
-
-On the X31 and X40 (and ONLY on those models), the fan speed can be
-controlled to a certain degree. Once the fan is running, it can be
-forced to run faster or slower with the following command:
-
- echo 'speed <speed>' > /proc/acpi/ibm/thermal
-
-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 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.
+through thinkpad-acpi.
-EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan
----------------------------------------
+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/fan
+
+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
+forced to run faster or slower with the following command:
+
+ 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. This functionality
+is incomplete, and not available through the sysfs interface.
+
+To program the safety watchdog, use the "watchdog" command.
+
+ 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 @@
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:
+Commands can also be specified when loading the thinkpad-acpi module,
+for example:
- modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable
+ modprobe thinkpad_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.
+Enabling debugging output
+-------------------------
- echo 'watchdog <interval>' > /proc/acpi/ibm/fan
+The module takes a debug paramater which can be used to selectively
+enable various classes of debugging output, for example:
-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.
+ modprobe ibm_acpi debug=0xffff
-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.
+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.
-Example Configuration
----------------------
+Sysfs interface changelog:
-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.
+0x000100: Initial sysfs support, as a single platform driver and
+ device.
diff --git a/Documentation/video4linux/meye.txt b/Documentation/video4linux/meye.txt
index ecb3416..5e51c59 100644
--- a/Documentation/video4linux/meye.txt
+++ b/Documentation/video4linux/meye.txt
@@ -5,10 +5,9 @@
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.
diff --git a/MAINTAINERS b/MAINTAINERS
index d4743b4..af1c792 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1658,15 +1658,6 @@
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
@@ -3182,6 +3173,15 @@
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
diff --git a/arch/i386/defconfig b/arch/i386/defconfig
index f4efd66..c96911c 100644
--- a/arch/i386/defconfig
+++ b/arch/i386/defconfig
@@ -692,7 +692,6 @@
CONFIG_SATA_VIA=y
# CONFIG_SATA_VITESSE is not set
# CONFIG_SATA_INIC162X is not set
-CONFIG_SATA_INTEL_COMBINED=y
CONFIG_SATA_ACPI=y
# CONFIG_PATA_ALI is not set
# CONFIG_PATA_AMD is not set
diff --git a/arch/i386/kernel/acpi/earlyquirk.c b/arch/i386/kernel/acpi/earlyquirk.c
index a7d22d9..8f7efd3 100644
--- a/arch/i386/kernel/acpi/earlyquirk.c
+++ b/arch/i386/kernel/acpi/earlyquirk.c
@@ -23,10 +23,13 @@
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 "
diff --git a/arch/parisc/configs/c3000_defconfig b/arch/parisc/configs/c3000_defconfig
index 782906b..eb2f9a3 100644
--- a/arch/parisc/configs/c3000_defconfig
+++ b/arch/parisc/configs/c3000_defconfig
@@ -435,7 +435,6 @@
# CONFIG_SCSI_SATA_ULI is not set
CONFIG_SCSI_SATA_VIA=m
# CONFIG_SCSI_SATA_VITESSE is not set
-CONFIG_SCSI_SATA_INTEL_COMBINED=y
# CONFIG_SCSI_DMX3191D is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
# CONFIG_SCSI_IPS is not set
diff --git a/arch/x86_64/defconfig b/arch/x86_64/defconfig
index 7a1e251..b263788 100644
--- a/arch/x86_64/defconfig
+++ b/arch/x86_64/defconfig
@@ -631,7 +631,6 @@
CONFIG_SATA_VIA=y
# CONFIG_SATA_VITESSE is not set
# CONFIG_SATA_INIC162X is not set
-CONFIG_SATA_INTEL_COMBINED=y
CONFIG_SATA_ACPI=y
# CONFIG_PATA_ALI is not set
# CONFIG_PATA_AMD is not set
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index e2ce4a9..139f41f 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -85,8 +85,8 @@
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 @@
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_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.
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 5956e9f..d4336f1 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -1,6 +1,6 @@
#
# Makefile for the Linux ACPI interpreter
-#
+#
export ACPI_CFLAGS
@@ -32,16 +32,17 @@
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_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
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index c261726..e65628a 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -44,11 +44,6 @@
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 @@
* 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 @@
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;
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index dd49ea0..e5084ec 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -103,7 +103,9 @@
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]: "
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c
index 0930d94..0dd3bf7 100644
--- a/drivers/acpi/container.c
+++ b/drivers/acpi/container.c
@@ -49,8 +49,6 @@
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 @@
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);
}
/*******************************************************************/
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index 54a697f..4546bf8 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -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 @@
}
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 @@
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,
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index a802962..e08cf98 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1,6 +1,8 @@
/*
- * 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 @@
};
/* 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 @@
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 @@
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 @@
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 @@
/*
* 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 @@
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 @@
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 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 @@
{
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 @@
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 @@
return AE_BAD_PARAMETER;
}
- ec = (struct acpi_ec *)handler_context;
-
next_byte:
switch (function) {
case ACPI_READ:
@@ -551,18 +521,16 @@
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 @@
/* --------------------------------------------------------------------------
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);
+ strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
+ strcpy(acpi_device_class(device), ACPI_EC_CLASS);
+
+ ec = make_acpi_ec();
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);
+ status = ec_parse_device(device->handle, 0, ec, NULL);
+ if (status != AE_CTRL_TERMINATE) {
+ kfree(ec);
+ return -EINVAL;
}
- strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_EC_CLASS);
+
+ /* 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;
- /* 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);
-
- kfree(ec_ecdt);
- }
-
- /* 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;
- }
-
- 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,19 +715,50 @@
return -ENODEV;
}
- return AE_OK;
+ /* EC is fully operational, allow queries */
+ atomic_set(&ec->query_pending, 0);
+
+ return 0;
}
-static int acpi_ec_stop(struct acpi_device *device, int type)
+static int acpi_ec_start(struct acpi_device *device)
{
- acpi_status status = AE_OK;
- struct acpi_ec *ec = NULL;
+ 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;
+ 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,
&acpi_ec_space_handler);
@@ -805,164 +772,67 @@
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;
+
+ /* Use the global lock for all EC transactions? */
+ acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock);
+
+ ec->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));
+ ec->gpe, ec->command_addr, ec->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)
+int __init acpi_ec_ecdt_probe(void)
{
- acpi_status status;
- int ret = 0;
-
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Try to make an fake ECDT"));
-
- ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
- if (!ec_ecdt) {
- ret = -ENOMEM;
- goto error;
- }
-
- 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;
-}
-
-static int __init acpi_ec_get_real_ecdt(void)
-{
+ int ret;
acpi_status status;
struct acpi_table_ecdt *ecdt_ptr;
+ boot_ec = make_acpi_ec();
+ if (!boot_ec)
+ return -ENOMEM;
+ /*
+ * Generate a boot ec context
+ */
+
status = acpi_get_table(ACPI_SIG_ECDT, 1,
(struct acpi_table_header **)&ecdt_ptr);
if (ACPI_FAILURE(status))
- return -ENODEV;
+ goto error;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT"));
- /*
- * Generate a temporary ec context to use until the namespace is scanned
- */
- ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
- if (!ec_ecdt)
- return -ENOMEM;
+ 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;
- 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)) {
- 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;
-
- ret = acpi_ec_get_real_ecdt();
- /* Try to make a fake ECDT */
- if (ret && acpi_fake_ecdt_enabled) {
- ret = acpi_ec_fake_ecdt();
- }
-
- 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 @@
}
#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 @@
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;
diff --git a/drivers/acpi/i2c_ec.c b/drivers/acpi/i2c_ec.c
deleted file mode 100644
index acab4a4..0000000
--- a/drivers/acpi/i2c_ec.c
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * 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");
diff --git a/drivers/acpi/i2c_ec.h b/drivers/acpi/i2c_ec.h
deleted file mode 100644
index 7c53fb7..0000000
--- a/drivers/acpi/i2c_ec.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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);
diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c
deleted file mode 100644
index dc10966..0000000
--- a/drivers/acpi/ibm_acpi.c
+++ /dev/null
@@ -1,2798 +0,0 @@
-/*
- * ibm_acpi.c - IBM ThinkPad ACPI Extras
- *
- *
- * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
- * Copyright (C) 2006 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#define IBM_VERSION "0.13"
-
-/*
- * Changelog:
- *
- * 2006-11-22 0.13 new maintainer
- * changelog now lives in git commit history, and will
- * not be updated further in-file.
- *
- * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels
- * 2005-03-17 0.11 support for 600e, 770x
- * thanks to Jamie Lentin <lentinj@dial.pipex.com>
- * support for 770e, G41
- * G40 and G41 don't have a thinklight
- * temperatures no longer experimental
- * experimental brightness control
- * experimental volume control
- * experimental fan enable/disable
- * 2005-01-16 0.10 fix module loading on R30, R31
- * 2005-01-16 0.9 support for 570, R30, R31
- * ultrabay support on A22p, A3x
- * limit arg for cmos, led, beep, drop experimental status
- * more capable led control on A21e, A22p, T20-22, X20
- * experimental temperatures and fan speed
- * experimental embedded controller register dump
- * mark more functions as __init, drop incorrect __exit
- * use MODULE_VERSION
- * thanks to Henrik Brix Andersen <brix@gentoo.org>
- * fix parameter passing on module loading
- * thanks to Rusty Russell <rusty@rustcorp.com.au>
- * thanks to Jim Radford <radford@blackbean.org>
- * 2004-11-08 0.8 fix init error case, don't return from a macro
- * thanks to Chris Wright <chrisw@osdl.org>
- * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20
- * fix led control on A21e
- * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device
- * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20
- * proc file format changed
- * video_switch command
- * experimental cmos control
- * experimental led control
- * experimental acpi sounds
- * 2004-09-16 0.4 support for module parameters
- * hotkey mask can be prefixed by 0x
- * video output switching
- * video expansion control
- * ultrabay eject support
- * removed lcd brightness/on/off control, didn't work
- * 2004-08-17 0.3 support for R40
- * lcd off, brightness control
- * thinklight on/off
- * 2004-08-14 0.2 support for T series, X20
- * bluetooth enable/disable
- * hotkey events disabled by default
- * removed fan control, currently useless
- * 2004-08-09 0.1 initial release, support for X series
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/string.h>
-
-#include <linux/proc_fs.h>
-#include <linux/backlight.h>
-#include <linux/fb.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>
-
-#define IBM_NAME "ibm"
-#define IBM_DESC "IBM ThinkPad ACPI Extras"
-#define IBM_FILE "ibm_acpi"
-#define IBM_URL "http://ibm-acpi.sf.net/"
-
-MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
-MODULE_DESCRIPTION(IBM_DESC);
-MODULE_VERSION(IBM_VERSION);
-MODULE_LICENSE("GPL");
-
-#define IBM_DIR IBM_NAME
-
-#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
-
-#define __unused __attribute__ ((unused))
-
-static int experimental;
-module_param(experimental, int, 0);
-
-static acpi_handle root_handle = NULL;
-
-#define IBM_HANDLE(object, parent, paths...) \
- static acpi_handle object##_handle; \
- static acpi_handle *object##_parent = &parent##_handle; \
- static char *object##_path; \
- static char *object##_paths[] = { paths }
-
-IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
- "\\_SB.PCI.ISA.EC", /* 570 */
- "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */
- "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
- "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */
- "\\_SB.PCI0.ICH3.EC0", /* R31 */
- "\\_SB.PCI0.LPC.EC", /* all others */
- );
-
-IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
- "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
- "\\_SB.PCI0.VID0", /* 770e */
- "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
- "\\_SB.PCI0.AGP.VID", /* all others */
- ); /* R30, R31 */
-
-IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */
-
-IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */
- "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */
- "\\CMS", /* R40, R40e */
- ); /* all others */
-#ifdef CONFIG_ACPI_IBM_DOCK
-IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
- "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
- "\\_SB.PCI0.PCI1.DOCK", /* all others */
- "\\_SB.PCI.ISA.SLCE", /* 570 */
- ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
-#endif
-#ifdef CONFIG_ACPI_IBM_BAY
-IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */
- "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
- "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */
- "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
- ); /* A21e, R30, R31 */
-
-IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */
- "_EJ0", /* all others */
- ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
-
-IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
- "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
- ); /* all others */
-
-IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */
- "_EJ0", /* 770x */
- ); /* all others */
-#endif /* CONFIG_ACPI_IBM_BAY */
-
-/* don't list other alternatives as we install a notify handler on the 570 */
-IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
-
-IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
- "^HKEY", /* R30, R31 */
- "HKEY", /* all others */
- ); /* 570 */
-
-IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
-IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */
-
-IBM_HANDLE(led, ec, "SLED", /* 570 */
- "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
- "LED", /* all others */
- ); /* R30, R31 */
-
-IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
-IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */
-IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */
-IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */
-
-IBM_HANDLE(gfan, ec, "GFAN", /* 570 */
- "\\FSPD", /* 600e/x, 770e, 770x */
- ); /* all others */
-
-IBM_HANDLE(sfan, ec, "SFAN", /* 570 */
- "JFNS", /* 770x-JL */
- ); /* all others */
-
-#define IBM_HKEY_HID "IBM0068"
-#define IBM_PCI_HID "PNP0A03"
-
-enum thermal_access_mode {
- IBMACPI_THERMAL_NONE = 0, /* No thermal support */
- IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */
- IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */
- IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */
- IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */
-};
-
-#define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */
-struct ibm_thermal_sensors_struct {
- s32 temp[IBMACPI_MAX_THERMAL_SENSORS];
-};
-
-/*
- * FAN ACCESS MODES
- *
- * IBMACPI_FAN_RD_ACPI_GFAN:
- * ACPI GFAN method: returns fan level
- *
- * see IBMACPI_FAN_WR_ACPI_SFAN
- * EC 0x2f not available if GFAN exists
- *
- * IBMACPI_FAN_WR_ACPI_SFAN:
- * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
- *
- * EC 0x2f might be available *for reading*, but never for writing.
- *
- * IBMACPI_FAN_WR_TPEC:
- * ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported
- * on almost all ThinkPads
- *
- * Fan speed changes of any sort (including those caused by the
- * disengaged mode) are usually done slowly by the firmware as the
- * maximum ammount of fan duty cycle change per second seems to be
- * limited.
- *
- * Reading is not available if GFAN exists.
- * Writing is not available if SFAN exists.
- *
- * Bits
- * 7 automatic mode engaged;
- * (default operation mode of the ThinkPad)
- * fan level is ignored in this mode.
- * 6 disengage mode (takes precedence over bit 7);
- * not available on all thinkpads. May disable
- * the tachometer, and speeds up fan to 100% duty-cycle,
- * which speeds it up far above the standard RPM
- * levels. It is not impossible that it could cause
- * hardware damage.
- * 5-3 unused in some models. Extra bits for fan level
- * in others, but still useless as all values above
- * 7 map to the same speed as level 7 in these models.
- * 2-0 fan level (0..7 usually)
- * 0x00 = stop
- * 0x07 = max (set when temperatures critical)
- * Some ThinkPads may have other levels, see
- * IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
- *
- * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
- * boot. Apparently the EC does not intialize it, so unless ACPI DSDT
- * does so, its initial value is meaningless (0x07).
- *
- * For firmware bugs, refer to:
- * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
- *
- * ----
- *
- * ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
- * Main fan tachometer reading (in RPM)
- *
- * This register is present on all ThinkPads with a new-style EC, and
- * it is known not to be present on the A21m/e, and T22, as there is
- * something else in offset 0x84 according to the ACPI DSDT. Other
- * ThinkPads from this same time period (and earlier) probably lack the
- * tachometer as well.
- *
- * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
- * was never fixed by IBM to report the EC firmware version string
- * probably support the tachometer (like the early X models), so
- * detecting it is quite hard. We need more data to know for sure.
- *
- * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
- * might result.
- *
- * FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this
- * register is not invalidated in ThinkPads that disable tachometer
- * readings. Thus, the tachometer readings go stale.
- *
- * For firmware bugs, refer to:
- * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
- *
- * IBMACPI_FAN_WR_ACPI_FANS:
- * ThinkPad X31, X40, X41. Not available in the X60.
- *
- * FANS ACPI handle: takes three arguments: low speed, medium speed,
- * high speed. ACPI DSDT seems to map these three speeds to levels
- * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
- * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
- *
- * The speeds are stored on handles
- * (FANA:FAN9), (FANC:FANB), (FANE:FAND).
- *
- * There are three default speed sets, acessible as handles:
- * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
- *
- * ACPI DSDT switches which set is in use depending on various
- * factors.
- *
- * IBMACPI_FAN_WR_TPEC is also available and should be used to
- * command the fan. The X31/X40/X41 seems to have 8 fan levels,
- * but the ACPI tables just mention level 7.
- */
-
-enum fan_status_access_mode {
- IBMACPI_FAN_NONE = 0, /* No fan status or control */
- IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */
- IBMACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */
-};
-
-enum fan_control_access_mode {
- IBMACPI_FAN_WR_NONE = 0, /* No fan control */
- IBMACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */
- IBMACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */
- IBMACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */
-};
-
-enum fan_control_commands {
- IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */
- IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */
- IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd,
- * and also watchdog cmd */
-};
-
-enum { /* Fan control constants */
- fan_status_offset = 0x2f, /* EC register 0x2f */
- fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM)
- * 0x84 must be read before 0x85 */
-
- IBMACPI_FAN_EC_DISENGAGED = 0x40, /* EC mode: tachometer
- * disengaged */
- IBMACPI_FAN_EC_AUTO = 0x80, /* EC mode: auto fan
- * control */
-};
-
-static char *ibm_thinkpad_ec_found = NULL;
-
-struct ibm_struct {
- char *name;
- char param[32];
-
- char *hid;
- struct acpi_driver *driver;
-
- int (*init) (void);
- int (*read) (char *);
- int (*write) (char *);
- void (*exit) (void);
-
- void (*notify) (struct ibm_struct *, u32);
- acpi_handle *handle;
- int type;
- struct acpi_device *device;
-
- int driver_registered;
- int proc_created;
- int init_called;
- int notify_installed;
-
- int experimental;
-};
-
-static struct proc_dir_entry *proc_dir = NULL;
-
-static struct backlight_device *ibm_backlight_device = NULL;
-
-#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
-#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
-#define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
-
-static int acpi_evalf(acpi_handle handle,
- void *res, char *method, char *fmt, ...)
-{
- char *fmt0 = fmt;
- struct acpi_object_list params;
- union acpi_object in_objs[IBM_MAX_ACPI_ARGS];
- struct acpi_buffer result, *resultp;
- union acpi_object out_obj;
- acpi_status status;
- va_list ap;
- char res_type;
- int success;
- int quiet;
-
- if (!*fmt) {
- printk(IBM_ERR "acpi_evalf() called with empty format\n");
- return 0;
- }
-
- if (*fmt == 'q') {
- quiet = 1;
- fmt++;
- } else
- quiet = 0;
-
- res_type = *(fmt++);
-
- params.count = 0;
- params.pointer = &in_objs[0];
-
- va_start(ap, fmt);
- while (*fmt) {
- char c = *(fmt++);
- switch (c) {
- case 'd': /* int */
- in_objs[params.count].integer.value = va_arg(ap, int);
- in_objs[params.count++].type = ACPI_TYPE_INTEGER;
- break;
- /* add more types as needed */
- default:
- printk(IBM_ERR "acpi_evalf() called "
- "with invalid format character '%c'\n", c);
- return 0;
- }
- }
- va_end(ap);
-
- if (res_type != 'v') {
- result.length = sizeof(out_obj);
- result.pointer = &out_obj;
- resultp = &result;
- } else
- resultp = NULL;
-
- status = acpi_evaluate_object(handle, method, ¶ms, resultp);
-
- switch (res_type) {
- case 'd': /* int */
- if (res)
- *(int *)res = out_obj.integer.value;
- success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
- break;
- case 'v': /* void */
- success = status == AE_OK;
- break;
- /* add more types as needed */
- default:
- printk(IBM_ERR "acpi_evalf() called "
- "with invalid format character '%c'\n", res_type);
- return 0;
- }
-
- if (!success && !quiet)
- printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
- method, fmt0, status);
-
- return success;
-}
-
-static void __unused acpi_print_int(acpi_handle handle, char *method)
-{
- int i;
-
- if (acpi_evalf(handle, &i, method, "d"))
- printk(IBM_INFO "%s = 0x%x\n", method, i);
- else
- printk(IBM_ERR "error calling %s\n", method);
-}
-
-static char *next_cmd(char **cmds)
-{
- char *start = *cmds;
- char *end;
-
- while ((end = strchr(start, ',')) && end == start)
- start = end + 1;
-
- if (!end)
- return NULL;
-
- *end = 0;
- *cmds = end + 1;
- return start;
-}
-
-static int ibm_acpi_driver_init(void)
-{
- printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
- printk(IBM_INFO "%s\n", IBM_URL);
-
- if (ibm_thinkpad_ec_found)
- printk(IBM_INFO "ThinkPad EC firmware %s\n",
- ibm_thinkpad_ec_found);
-
- return 0;
-}
-
-static int driver_read(char *p)
-{
- int len = 0;
-
- len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC);
- len += sprintf(p + len, "version:\t%s\n", IBM_VERSION);
-
- return len;
-}
-
-static int hotkey_supported;
-static int hotkey_mask_supported;
-static int hotkey_orig_status;
-static int hotkey_orig_mask;
-
-static int hotkey_get(int *status, int *mask)
-{
- if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
- return 0;
-
- if (hotkey_mask_supported)
- if (!acpi_evalf(hkey_handle, mask, "DHKN", "d"))
- return 0;
-
- return 1;
-}
-
-static int hotkey_set(int status, int mask)
-{
- int i;
-
- if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
- return 0;
-
- if (hotkey_mask_supported)
- for (i = 0; i < 32; i++) {
- int bit = ((1 << i) & mask) != 0;
- if (!acpi_evalf(hkey_handle,
- NULL, "MHKM", "vdd", i + 1, bit))
- return 0;
- }
-
- return 1;
-}
-
-static int hotkey_init(void)
-{
- /* hotkey not supported on 570 */
- hotkey_supported = hkey_handle != NULL;
-
- if (hotkey_supported) {
- /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
- A30, R30, R31, T20-22, X20-21, X22-24 */
- hotkey_mask_supported =
- acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
-
- if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask))
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int hotkey_read(char *p)
-{
- int status, mask;
- int len = 0;
-
- if (!hotkey_supported) {
- len += sprintf(p + len, "status:\t\tnot supported\n");
- return len;
- }
-
- if (!hotkey_get(&status, &mask))
- return -EIO;
-
- len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
- if (hotkey_mask_supported) {
- len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
- len += sprintf(p + len,
- "commands:\tenable, disable, reset, <mask>\n");
- } else {
- len += sprintf(p + len, "mask:\t\tnot supported\n");
- len += sprintf(p + len, "commands:\tenable, disable, reset\n");
- }
-
- return len;
-}
-
-static int hotkey_write(char *buf)
-{
- int status, mask;
- char *cmd;
- int do_cmd = 0;
-
- if (!hotkey_supported)
- return -ENODEV;
-
- if (!hotkey_get(&status, &mask))
- return -EIO;
-
- while ((cmd = next_cmd(&buf))) {
- if (strlencmp(cmd, "enable") == 0) {
- status = 1;
- } else if (strlencmp(cmd, "disable") == 0) {
- status = 0;
- } else if (strlencmp(cmd, "reset") == 0) {
- status = hotkey_orig_status;
- mask = hotkey_orig_mask;
- } else if (sscanf(cmd, "0x%x", &mask) == 1) {
- /* mask set */
- } else if (sscanf(cmd, "%x", &mask) == 1) {
- /* mask set */
- } else
- return -EINVAL;
- do_cmd = 1;
- }
-
- if (do_cmd && !hotkey_set(status, mask))
- return -EIO;
-
- return 0;
-}
-
-static void hotkey_exit(void)
-{
- if (hotkey_supported)
- hotkey_set(hotkey_orig_status, hotkey_orig_mask);
-}
-
-static void hotkey_notify(struct ibm_struct *ibm, u32 event)
-{
- int hkey;
-
- if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d"))
- acpi_bus_generate_event(ibm->device, event, hkey);
- else {
- printk(IBM_ERR "unknown hotkey event %d\n", event);
- acpi_bus_generate_event(ibm->device, event, 0);
- }
-}
-
-static int bluetooth_supported;
-
-static int bluetooth_init(void)
-{
- /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
- G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
- bluetooth_supported = hkey_handle &&
- acpi_evalf(hkey_handle, NULL, "GBDC", "qv");
-
- return 0;
-}
-
-static int bluetooth_status(void)
-{
- int status;
-
- if (!bluetooth_supported ||
- !acpi_evalf(hkey_handle, &status, "GBDC", "d"))
- status = 0;
-
- return status;
-}
-
-static int bluetooth_read(char *p)
-{
- int len = 0;
- int status = bluetooth_status();
-
- if (!bluetooth_supported)
- len += sprintf(p + len, "status:\t\tnot supported\n");
- else if (!(status & 1))
- len += sprintf(p + len, "status:\t\tnot installed\n");
- else {
- len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
- len += sprintf(p + len, "commands:\tenable, disable\n");
- }
-
- return len;
-}
-
-static int bluetooth_write(char *buf)
-{
- int status = bluetooth_status();
- char *cmd;
- int do_cmd = 0;
-
- if (!bluetooth_supported)
- return -ENODEV;
-
- while ((cmd = next_cmd(&buf))) {
- if (strlencmp(cmd, "enable") == 0) {
- status |= 2;
- } else if (strlencmp(cmd, "disable") == 0) {
- status &= ~2;
- } else
- return -EINVAL;
- do_cmd = 1;
- }
-
- if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
- return -EIO;
-
- return 0;
-}
-
-static int wan_supported;
-
-static int wan_init(void)
-{
- wan_supported = hkey_handle &&
- acpi_evalf(hkey_handle, NULL, "GWAN", "qv");
-
- return 0;
-}
-
-static int wan_status(void)
-{
- int status;
-
- if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d"))
- status = 0;
-
- return status;
-}
-
-static int wan_read(char *p)
-{
- int len = 0;
- int status = wan_status();
-
- if (!wan_supported)
- len += sprintf(p + len, "status:\t\tnot supported\n");
- else if (!(status & 1))
- len += sprintf(p + len, "status:\t\tnot installed\n");
- else {
- len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
- len += sprintf(p + len, "commands:\tenable, disable\n");
- }
-
- return len;
-}
-
-static int wan_write(char *buf)
-{
- int status = wan_status();
- char *cmd;
- int do_cmd = 0;
-
- if (!wan_supported)
- return -ENODEV;
-
- while ((cmd = next_cmd(&buf))) {
- if (strlencmp(cmd, "enable") == 0) {
- status |= 2;
- } else if (strlencmp(cmd, "disable") == 0) {
- status &= ~2;
- } else
- return -EINVAL;
- do_cmd = 1;
- }
-
- if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
- return -EIO;
-
- return 0;
-}
-
-enum video_access_mode {
- IBMACPI_VIDEO_NONE = 0,
- IBMACPI_VIDEO_570, /* 570 */
- IBMACPI_VIDEO_770, /* 600e/x, 770e, 770x */
- IBMACPI_VIDEO_NEW, /* all others */
-};
-
-static enum video_access_mode video_supported;
-static int video_orig_autosw;
-
-static int video_init(void)
-{
- int ivga;
-
- if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
- /* G41, assume IVGA doesn't change */
- vid_handle = vid2_handle;
-
- if (!vid_handle)
- /* video switching not supported on R30, R31 */
- video_supported = IBMACPI_VIDEO_NONE;
- else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
- /* 570 */
- video_supported = IBMACPI_VIDEO_570;
- else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
- /* 600e/x, 770e, 770x */
- video_supported = IBMACPI_VIDEO_770;
- else
- /* all others */
- video_supported = IBMACPI_VIDEO_NEW;
-
- return 0;
-}
-
-static int video_status(void)
-{
- int status = 0;
- int i;
-
- if (video_supported == IBMACPI_VIDEO_570) {
- if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87))
- status = i & 3;
- } else if (video_supported == IBMACPI_VIDEO_770) {
- if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
- status |= 0x01 * i;
- if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
- status |= 0x02 * i;
- } else if (video_supported == IBMACPI_VIDEO_NEW) {
- acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1);
- if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
- status |= 0x02 * i;
-
- acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0);
- if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
- status |= 0x01 * i;
- if (acpi_evalf(NULL, &i, "\\VCDD", "d"))
- status |= 0x08 * i;
- }
-
- return status;
-}
-
-static int video_autosw(void)
-{
- int autosw = 0;
-
- if (video_supported == IBMACPI_VIDEO_570)
- acpi_evalf(vid_handle, &autosw, "SWIT", "d");
- else if (video_supported == IBMACPI_VIDEO_770 ||
- video_supported == IBMACPI_VIDEO_NEW)
- acpi_evalf(vid_handle, &autosw, "^VDEE", "d");
-
- return autosw & 1;
-}
-
-static int video_read(char *p)
-{
- int status = video_status();
- int autosw = video_autosw();
- int len = 0;
-
- if (!video_supported) {
- len += sprintf(p + len, "status:\t\tnot supported\n");
- return len;
- }
-
- len += sprintf(p + len, "status:\t\tsupported\n");
- len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
- len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
- if (video_supported == IBMACPI_VIDEO_NEW)
- len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
- len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
- len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
- len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
- if (video_supported == IBMACPI_VIDEO_NEW)
- len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
- len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
- len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
-
- return len;
-}
-
-static int video_switch(void)
-{
- int autosw = video_autosw();
- int ret;
-
- if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
- return -EIO;
- ret = video_supported == IBMACPI_VIDEO_570 ?
- acpi_evalf(ec_handle, NULL, "_Q16", "v") :
- acpi_evalf(vid_handle, NULL, "VSWT", "v");
- acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);
-
- return ret;
-}
-
-static int video_expand(void)
-{
- if (video_supported == IBMACPI_VIDEO_570)
- return acpi_evalf(ec_handle, NULL, "_Q17", "v");
- else if (video_supported == IBMACPI_VIDEO_770)
- return acpi_evalf(vid_handle, NULL, "VEXP", "v");
- else
- return acpi_evalf(NULL, NULL, "\\VEXP", "v");
-}
-
-static int video_switch2(int status)
-{
- int ret;
-
- if (video_supported == IBMACPI_VIDEO_570) {
- ret = acpi_evalf(NULL, NULL,
- "\\_SB.PHS2", "vdd", 0x8b, status | 0x80);
- } else if (video_supported == IBMACPI_VIDEO_770) {
- int autosw = video_autosw();
- if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
- return -EIO;
-
- ret = acpi_evalf(vid_handle, NULL,
- "ASWT", "vdd", status * 0x100, 0);
-
- acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);
- } else {
- ret = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
- acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
- }
-
- return ret;
-}
-
-static int video_write(char *buf)
-{
- char *cmd;
- int enable, disable, status;
-
- if (!video_supported)
- return -ENODEV;
-
- enable = disable = 0;
-
- while ((cmd = next_cmd(&buf))) {
- if (strlencmp(cmd, "lcd_enable") == 0) {
- enable |= 0x01;
- } else if (strlencmp(cmd, "lcd_disable") == 0) {
- disable |= 0x01;
- } else if (strlencmp(cmd, "crt_enable") == 0) {
- enable |= 0x02;
- } else if (strlencmp(cmd, "crt_disable") == 0) {
- disable |= 0x02;
- } else if (video_supported == IBMACPI_VIDEO_NEW &&
- strlencmp(cmd, "dvi_enable") == 0) {
- enable |= 0x08;
- } else if (video_supported == IBMACPI_VIDEO_NEW &&
- strlencmp(cmd, "dvi_disable") == 0) {
- disable |= 0x08;
- } else if (strlencmp(cmd, "auto_enable") == 0) {
- if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
- return -EIO;
- } else if (strlencmp(cmd, "auto_disable") == 0) {
- if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0))
- return -EIO;
- } else if (strlencmp(cmd, "video_switch") == 0) {
- if (!video_switch())
- return -EIO;
- } else if (strlencmp(cmd, "expand_toggle") == 0) {
- if (!video_expand())
- return -EIO;
- } else
- return -EINVAL;
- }
-
- if (enable || disable) {
- status = (video_status() & 0x0f & ~disable) | enable;
- if (!video_switch2(status))
- return -EIO;
- }
-
- return 0;
-}
-
-static void video_exit(void)
-{
- acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw);
-}
-
-static int light_supported;
-static int light_status_supported;
-
-static int light_init(void)
-{
- /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
- light_supported = (cmos_handle || lght_handle) && !ledb_handle;
-
- if (light_supported)
- /* light status not supported on
- 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
- light_status_supported = acpi_evalf(ec_handle, NULL,
- "KBLT", "qv");
-
- return 0;
-}
-
-static int light_read(char *p)
-{
- int len = 0;
- int status = 0;
-
- if (!light_supported) {
- len += sprintf(p + len, "status:\t\tnot supported\n");
- } else if (!light_status_supported) {
- len += sprintf(p + len, "status:\t\tunknown\n");
- len += sprintf(p + len, "commands:\ton, off\n");
- } else {
- if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
- return -EIO;
- len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
- len += sprintf(p + len, "commands:\ton, off\n");
- }
-
- return len;
-}
-
-static int light_write(char *buf)
-{
- int cmos_cmd, lght_cmd;
- char *cmd;
- int success;
-
- if (!light_supported)
- return -ENODEV;
-
- while ((cmd = next_cmd(&buf))) {
- if (strlencmp(cmd, "on") == 0) {
- cmos_cmd = 0x0c;
- lght_cmd = 1;
- } else if (strlencmp(cmd, "off") == 0) {
- cmos_cmd = 0x0d;
- lght_cmd = 0;
- } else
- return -EINVAL;
-
- success = cmos_handle ?
- acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
- acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
- if (!success)
- return -EIO;
- }
-
- return 0;
-}
-
-#if defined(CONFIG_ACPI_IBM_DOCK) || defined(CONFIG_ACPI_IBM_BAY)
-static int _sta(acpi_handle handle)
-{
- int status;
-
- if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
- status = 0;
-
- return status;
-}
-#endif
-
-#ifdef CONFIG_ACPI_IBM_DOCK
-#define dock_docked() (_sta(dock_handle) & 1)
-
-static int dock_read(char *p)
-{
- int len = 0;
- int docked = dock_docked();
-
- if (!dock_handle)
- len += sprintf(p + len, "status:\t\tnot supported\n");
- else if (!docked)
- len += sprintf(p + len, "status:\t\tundocked\n");
- else {
- len += sprintf(p + len, "status:\t\tdocked\n");
- len += sprintf(p + len, "commands:\tdock, undock\n");
- }
-
- return len;
-}
-
-static int dock_write(char *buf)
-{
- char *cmd;
-
- if (!dock_docked())
- return -ENODEV;
-
- while ((cmd = next_cmd(&buf))) {
- if (strlencmp(cmd, "undock") == 0) {
- if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
- !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
- return -EIO;
- } else if (strlencmp(cmd, "dock") == 0) {
- if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
- return -EIO;
- } else
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void dock_notify(struct ibm_struct *ibm, u32 event)
-{
- int docked = dock_docked();
- int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID);
-
- if (event == 1 && !pci) /* 570 */
- acpi_bus_generate_event(ibm->device, event, 1); /* button */
- else if (event == 1 && pci) /* 570 */
- acpi_bus_generate_event(ibm->device, event, 3); /* dock */
- else if (event == 3 && docked)
- acpi_bus_generate_event(ibm->device, event, 1); /* button */
- else if (event == 3 && !docked)
- acpi_bus_generate_event(ibm->device, event, 2); /* undock */
- else if (event == 0 && docked)
- acpi_bus_generate_event(ibm->device, event, 3); /* dock */
- else {
- printk(IBM_ERR "unknown dock event %d, status %d\n",
- event, _sta(dock_handle));
- acpi_bus_generate_event(ibm->device, event, 0); /* unknown */
- }
-}
-#endif
-
-#ifdef CONFIG_ACPI_IBM_BAY
-static int bay_status_supported;
-static int bay_status2_supported;
-static int bay_eject_supported;
-static int bay_eject2_supported;
-
-static int bay_init(void)
-{
- bay_status_supported = bay_handle &&
- acpi_evalf(bay_handle, NULL, "_STA", "qv");
- bay_status2_supported = bay2_handle &&
- acpi_evalf(bay2_handle, NULL, "_STA", "qv");
-
- bay_eject_supported = bay_handle && bay_ej_handle &&
- (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
- bay_eject2_supported = bay2_handle && bay2_ej_handle &&
- (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
-
- return 0;
-}
-
-#define bay_occupied(b) (_sta(b##_handle) & 1)
-
-static int bay_read(char *p)
-{
- int len = 0;
- int occupied = bay_occupied(bay);
- int occupied2 = bay_occupied(bay2);
- int eject, eject2;
-
- len += sprintf(p + len, "status:\t\t%s\n", bay_status_supported ?
- (occupied ? "occupied" : "unoccupied") :
- "not supported");
- if (bay_status2_supported)
- len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
- "occupied" : "unoccupied");
-
- eject = bay_eject_supported && occupied;
- eject2 = bay_eject2_supported && occupied2;
-
- if (eject && eject2)
- len += sprintf(p + len, "commands:\teject, eject2\n");
- else if (eject)
- len += sprintf(p + len, "commands:\teject\n");
- else if (eject2)
- len += sprintf(p + len, "commands:\teject2\n");
-
- return len;
-}
-
-static int bay_write(char *buf)
-{
- char *cmd;
-
- if (!bay_eject_supported && !bay_eject2_supported)
- return -ENODEV;
-
- while ((cmd = next_cmd(&buf))) {
- if (bay_eject_supported && strlencmp(cmd, "eject") == 0) {
- if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
- return -EIO;
- } else if (bay_eject2_supported &&
- strlencmp(cmd, "eject2") == 0) {
- if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
- return -EIO;
- } else
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void bay_notify(struct ibm_struct *ibm, u32 event)
-{
- acpi_bus_generate_event(ibm->device, event, 0);
-}
-#endif /* CONFIG_ACPI_IBM_BAY */
-
-static int cmos_read(char *p)
-{
- int len = 0;
-
- /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
- R30, R31, T20-22, X20-21 */
- if (!cmos_handle)
- len += sprintf(p + len, "status:\t\tnot supported\n");
- else {
- len += sprintf(p + len, "status:\t\tsupported\n");
- len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
- }
-
- return len;
-}
-
-static int cmos_eval(int cmos_cmd)
-{
- if (cmos_handle)
- return acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd);
- else
- return 1;
-}
-
-static int cmos_write(char *buf)
-{
- char *cmd;
- int cmos_cmd;
-
- if (!cmos_handle)
- return -EINVAL;
-
- while ((cmd = next_cmd(&buf))) {
- if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
- cmos_cmd >= 0 && cmos_cmd <= 21) {
- /* cmos_cmd set */
- } else
- return -EINVAL;
-
- if (!cmos_eval(cmos_cmd))
- return -EIO;
- }
-
- return 0;
-}
-
-enum led_access_mode {
- IBMACPI_LED_NONE = 0,
- IBMACPI_LED_570, /* 570 */
- IBMACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
- IBMACPI_LED_NEW, /* all others */
-};
-static enum led_access_mode led_supported;
-
-static int led_init(void)
-{
- if (!led_handle)
- /* led not supported on R30, R31 */
- led_supported = IBMACPI_LED_NONE;
- else if (strlencmp(led_path, "SLED") == 0)
- /* 570 */
- led_supported = IBMACPI_LED_570;
- else if (strlencmp(led_path, "SYSL") == 0)
- /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
- led_supported = IBMACPI_LED_OLD;
- else
- /* all others */
- led_supported = IBMACPI_LED_NEW;
-
- return 0;
-}
-
-#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking"))
-
-static int led_read(char *p)
-{
- int len = 0;
-
- if (!led_supported) {
- len += sprintf(p + len, "status:\t\tnot supported\n");
- return len;
- }
- len += sprintf(p + len, "status:\t\tsupported\n");
-
- if (led_supported == IBMACPI_LED_570) {
- /* 570 */
- int i, status;
- for (i = 0; i < 8; i++) {
- if (!acpi_evalf(ec_handle,
- &status, "GLED", "dd", 1 << i))
- return -EIO;
- len += sprintf(p + len, "%d:\t\t%s\n",
- i, led_status(status));
- }
- }
-
- len += sprintf(p + len, "commands:\t"
- "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
-
- return len;
-}
-
-/* off, on, blink */
-static const int led_sled_arg1[] = { 0, 1, 3 };
-static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
-static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
-static const int led_led_arg1[] = { 0, 0x80, 0xc0 };
-
-#define EC_HLCL 0x0c
-#define EC_HLBL 0x0d
-#define EC_HLMS 0x0e
-
-static int led_write(char *buf)
-{
- char *cmd;
- int led, ind, ret;
-
- if (!led_supported)
- return -ENODEV;
-
- while ((cmd = next_cmd(&buf))) {
- if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
- return -EINVAL;
-
- if (strstr(cmd, "off")) {
- ind = 0;
- } else if (strstr(cmd, "on")) {
- ind = 1;
- } else if (strstr(cmd, "blink")) {
- ind = 2;
- } else
- return -EINVAL;
-
- if (led_supported == IBMACPI_LED_570) {
- /* 570 */
- led = 1 << led;
- if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
- led, led_sled_arg1[ind]))
- return -EIO;
- } else if (led_supported == IBMACPI_LED_OLD) {
- /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
- led = 1 << led;
- ret = ec_write(EC_HLMS, led);
- if (ret >= 0)
- ret =
- ec_write(EC_HLBL, led * led_exp_hlbl[ind]);
- if (ret >= 0)
- ret =
- ec_write(EC_HLCL, led * led_exp_hlcl[ind]);
- if (ret < 0)
- return ret;
- } else {
- /* all others */
- if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
- led, led_led_arg1[ind]))
- return -EIO;
- }
- }
-
- return 0;
-}
-
-static int beep_read(char *p)
-{
- int len = 0;
-
- if (!beep_handle)
- len += sprintf(p + len, "status:\t\tnot supported\n");
- else {
- len += sprintf(p + len, "status:\t\tsupported\n");
- len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
- }
-
- return len;
-}
-
-static int beep_write(char *buf)
-{
- char *cmd;
- int beep_cmd;
-
- if (!beep_handle)
- return -ENODEV;
-
- while ((cmd = next_cmd(&buf))) {
- if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
- beep_cmd >= 0 && beep_cmd <= 17) {
- /* beep_cmd set */
- } else
- return -EINVAL;
- if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
- return -EIO;
- }
-
- return 0;
-}
-
-static int acpi_ec_read(int i, u8 * p)
-{
- int v;
-
- if (ecrd_handle) {
- if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
- return 0;
- *p = v;
- } else {
- if (ec_read(i, p) < 0)
- return 0;
- }
-
- return 1;
-}
-
-static int acpi_ec_write(int i, u8 v)
-{
- if (ecwr_handle) {
- if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
- return 0;
- } else {
- if (ec_write(i, v) < 0)
- return 0;
- }
-
- return 1;
-}
-
-static enum thermal_access_mode thermal_read_mode;
-
-static int thermal_init(void)
-{
- u8 t, ta1, ta2;
- int i;
- int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
-
- if (ibm_thinkpad_ec_found && experimental) {
- /*
- * Direct EC access mode: sensors at registers
- * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
- * non-implemented, thermal sensors return 0x80 when
- * not available
- */
-
- ta1 = ta2 = 0;
- for (i = 0; i < 8; i++) {
- if (likely(acpi_ec_read(0x78 + i, &t))) {
- ta1 |= t;
- } else {
- ta1 = 0;
- break;
- }
- if (likely(acpi_ec_read(0xC0 + i, &t))) {
- ta2 |= t;
- } else {
- ta1 = 0;
- break;
- }
- }
- if (ta1 == 0) {
- /* This is sheer paranoia, but we handle it anyway */
- if (acpi_tmp7) {
- printk(IBM_ERR
- "ThinkPad ACPI EC access misbehaving, "
- "falling back to ACPI TMPx access mode\n");
- thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07;
- } else {
- printk(IBM_ERR
- "ThinkPad ACPI EC access misbehaving, "
- "disabling thermal sensors access\n");
- thermal_read_mode = IBMACPI_THERMAL_NONE;
- }
- } else {
- thermal_read_mode =
- (ta2 != 0) ?
- IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8;
- }
- } else if (acpi_tmp7) {
- if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
- /* 600e/x, 770e, 770x */
- thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT;
- } else {
- /* Standard ACPI TMPx access, max 8 sensors */
- thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07;
- }
- } else {
- /* temperatures not supported on 570, G4x, R30, R31, R32 */
- thermal_read_mode = IBMACPI_THERMAL_NONE;
- }
-
- return 0;
-}
-
-static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
-{
- int i, t;
- s8 tmp;
- char tmpi[] = "TMPi";
-
- if (!s)
- return -EINVAL;
-
- switch (thermal_read_mode) {
-#if IBMACPI_MAX_THERMAL_SENSORS >= 16
- case IBMACPI_THERMAL_TPEC_16:
- for (i = 0; i < 8; i++) {
- if (!acpi_ec_read(0xC0 + i, &tmp))
- return -EIO;
- s->temp[i + 8] = tmp * 1000;
- }
- /* fallthrough */
-#endif
- case IBMACPI_THERMAL_TPEC_8:
- for (i = 0; i < 8; i++) {
- if (!acpi_ec_read(0x78 + i, &tmp))
- return -EIO;
- s->temp[i] = tmp * 1000;
- }
- return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8;
-
- case IBMACPI_THERMAL_ACPI_UPDT:
- if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
- return -EIO;
- for (i = 0; i < 8; i++) {
- tmpi[3] = '0' + i;
- if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
- return -EIO;
- s->temp[i] = (t - 2732) * 100;
- }
- return 8;
-
- case IBMACPI_THERMAL_ACPI_TMP07:
- for (i = 0; i < 8; i++) {
- tmpi[3] = '0' + i;
- if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
- return -EIO;
- s->temp[i] = t * 1000;
- }
- return 8;
-
- case IBMACPI_THERMAL_NONE:
- default:
- return 0;
- }
-}
-
-static int thermal_read(char *p)
-{
- int len = 0;
- int n, i;
- struct ibm_thermal_sensors_struct t;
-
- n = thermal_get_sensors(&t);
- if (unlikely(n < 0))
- return n;
-
- len += sprintf(p + len, "temperatures:\t");
-
- if (n > 0) {
- for (i = 0; i < (n - 1); i++)
- len += sprintf(p + len, "%d ", t.temp[i] / 1000);
- len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
- } else
- len += sprintf(p + len, "not supported\n");
-
- return len;
-}
-
-static u8 ecdump_regs[256];
-
-static int ecdump_read(char *p)
-{
- int len = 0;
- int i, j;
- u8 v;
-
- len += sprintf(p + len, "EC "
- " +00 +01 +02 +03 +04 +05 +06 +07"
- " +08 +09 +0a +0b +0c +0d +0e +0f\n");
- for (i = 0; i < 256; i += 16) {
- len += sprintf(p + len, "EC 0x%02x:", i);
- for (j = 0; j < 16; j++) {
- if (!acpi_ec_read(i + j, &v))
- break;
- if (v != ecdump_regs[i + j])
- len += sprintf(p + len, " *%02x", v);
- else
- len += sprintf(p + len, " %02x", v);
- ecdump_regs[i + j] = v;
- }
- len += sprintf(p + len, "\n");
- if (j != 16)
- break;
- }
-
- /* These are way too dangerous to advertise openly... */
-#if 0
- len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
- " (<offset> is 00-ff, <value> is 00-ff)\n");
- len += sprintf(p + len, "commands:\t0x<offset> <value> "
- " (<offset> is 00-ff, <value> is 0-255)\n");
-#endif
- return len;
-}
-
-static int ecdump_write(char *buf)
-{
- char *cmd;
- int i, v;
-
- while ((cmd = next_cmd(&buf))) {
- if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
- /* i and v set */
- } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
- /* i and v set */
- } else
- return -EINVAL;
- if (i >= 0 && i < 256 && v >= 0 && v < 256) {
- if (!acpi_ec_write(i, v))
- return -EIO;
- } else
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int brightness_offset = 0x31;
-
-static int brightness_get(struct backlight_device *bd)
-{
- u8 level;
- if (!acpi_ec_read(brightness_offset, &level))
- return -EIO;
-
- level &= 0x7;
-
- return level;
-}
-
-static int brightness_read(char *p)
-{
- int len = 0;
- int level;
-
- if ((level = brightness_get(NULL)) < 0) {
- len += sprintf(p + len, "level:\t\tunreadable\n");
- } else {
- len += sprintf(p + len, "level:\t\t%d\n", level & 0x7);
- len += sprintf(p + len, "commands:\tup, down\n");
- len += sprintf(p + len, "commands:\tlevel <level>"
- " (<level> is 0-7)\n");
- }
-
- return len;
-}
-
-#define BRIGHTNESS_UP 4
-#define BRIGHTNESS_DOWN 5
-
-static int brightness_set(int value)
-{
- int cmos_cmd, inc, i;
- int current_value = brightness_get(NULL);
-
- value &= 7;
-
- cmos_cmd = value > current_value ? BRIGHTNESS_UP : BRIGHTNESS_DOWN;
- inc = value > current_value ? 1 : -1;
- for (i = current_value; i != value; i += inc) {
- if (!cmos_eval(cmos_cmd))
- return -EIO;
- if (!acpi_ec_write(brightness_offset, i + inc))
- return -EIO;
- }
-
- return 0;
-}
-
-static int brightness_write(char *buf)
-{
- int level;
- int new_level;
- char *cmd;
-
- while ((cmd = next_cmd(&buf))) {
- if ((level = brightness_get(NULL)) < 0)
- return level;
- level &= 7;
-
- if (strlencmp(cmd, "up") == 0) {
- new_level = level == 7 ? 7 : level + 1;
- } else if (strlencmp(cmd, "down") == 0) {
- new_level = level == 0 ? 0 : level - 1;
- } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
- new_level >= 0 && new_level <= 7) {
- /* new_level set */
- } else
- return -EINVAL;
-
- brightness_set(new_level);
- }
-
- return 0;
-}
-
-static int brightness_update_status(struct backlight_device *bd)
-{
- return brightness_set(
- (bd->props.fb_blank == FB_BLANK_UNBLANK &&
- bd->props.power == FB_BLANK_UNBLANK) ?
- bd->props.brightness : 0);
-}
-
-static struct backlight_ops ibm_backlight_data = {
- .get_brightness = brightness_get,
- .update_status = brightness_update_status,
-};
-
-static int brightness_init(void)
-{
- int b;
-
- b = brightness_get(NULL);
- if (b < 0)
- return b;
-
- ibm_backlight_device = backlight_device_register("ibm", NULL, NULL,
- &ibm_backlight_data);
- if (IS_ERR(ibm_backlight_device)) {
- printk(IBM_ERR "Could not register backlight device\n");
- return PTR_ERR(ibm_backlight_device);
- }
-
- ibm_backlight_device->props.max_brightness = 7;
- ibm_backlight_device->props.brightness = b;
- backlight_update_status(ibm_backlight_device);
-
- return 0;
-}
-
-static void brightness_exit(void)
-{
- if (ibm_backlight_device) {
- backlight_device_unregister(ibm_backlight_device);
- ibm_backlight_device = NULL;
- }
-}
-
-static int volume_offset = 0x30;
-
-static int volume_read(char *p)
-{
- int len = 0;
- u8 level;
-
- if (!acpi_ec_read(volume_offset, &level)) {
- len += sprintf(p + len, "level:\t\tunreadable\n");
- } else {
- len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
- len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
- len += sprintf(p + len, "commands:\tup, down, mute\n");
- len += sprintf(p + len, "commands:\tlevel <level>"
- " (<level> is 0-15)\n");
- }
-
- return len;
-}
-
-#define VOLUME_DOWN 0
-#define VOLUME_UP 1
-#define VOLUME_MUTE 2
-
-static int volume_write(char *buf)
-{
- int cmos_cmd, inc, i;
- u8 level, mute;
- int new_level, new_mute;
- char *cmd;
-
- while ((cmd = next_cmd(&buf))) {
- if (!acpi_ec_read(volume_offset, &level))
- return -EIO;
- new_mute = mute = level & 0x40;
- new_level = level = level & 0xf;
-
- if (strlencmp(cmd, "up") == 0) {
- if (mute)
- new_mute = 0;
- else
- new_level = level == 15 ? 15 : level + 1;
- } else if (strlencmp(cmd, "down") == 0) {
- if (mute)
- new_mute = 0;
- else
- new_level = level == 0 ? 0 : level - 1;
- } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
- new_level >= 0 && new_level <= 15) {
- /* new_level set */
- } else if (strlencmp(cmd, "mute") == 0) {
- new_mute = 0x40;
- } else
- return -EINVAL;
-
- if (new_level != level) { /* mute doesn't change */
- cmos_cmd = new_level > level ? VOLUME_UP : VOLUME_DOWN;
- inc = new_level > level ? 1 : -1;
-
- if (mute && (!cmos_eval(cmos_cmd) ||
- !acpi_ec_write(volume_offset, level)))
- return -EIO;
-
- for (i = level; i != new_level; i += inc)
- if (!cmos_eval(cmos_cmd) ||
- !acpi_ec_write(volume_offset, i + inc))
- return -EIO;
-
- if (mute && (!cmos_eval(VOLUME_MUTE) ||
- !acpi_ec_write(volume_offset,
- new_level + mute)))
- return -EIO;
- }
-
- if (new_mute != mute) { /* level doesn't change */
- cmos_cmd = new_mute ? VOLUME_MUTE : VOLUME_UP;
-
- if (!cmos_eval(cmos_cmd) ||
- !acpi_ec_write(volume_offset, level + new_mute))
- return -EIO;
- }
- }
-
- return 0;
-}
-
-static enum fan_status_access_mode fan_status_access_mode;
-static enum fan_control_access_mode fan_control_access_mode;
-static enum fan_control_commands fan_control_commands;
-
-static int fan_control_status_known;
-static u8 fan_control_initial_status;
-
-static void fan_watchdog_fire(struct work_struct *ignored);
-static int fan_watchdog_maxinterval;
-static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
-
-static int fan_init(void)
-{
- fan_status_access_mode = IBMACPI_FAN_NONE;
- fan_control_access_mode = IBMACPI_FAN_WR_NONE;
- fan_control_commands = 0;
- fan_control_status_known = 1;
- fan_watchdog_maxinterval = 0;
-
- if (gfan_handle) {
- /* 570, 600e/x, 770e, 770x */
- fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN;
- } else {
- /* all other ThinkPads: note that even old-style
- * ThinkPad ECs supports the fan control register */
- if (likely(acpi_ec_read(fan_status_offset,
- &fan_control_initial_status))) {
- fan_status_access_mode = IBMACPI_FAN_RD_TPEC;
-
- /* In some ThinkPads, neither the EC nor the ACPI
- * DSDT initialize the fan status, and it ends up
- * being set to 0x07 when it *could* be either
- * 0x07 or 0x80.
- *
- * Enable for TP-1Y (T43), TP-78 (R51e),
- * TP-76 (R52), TP-70 (T43, R52), which are known
- * to be buggy. */
- if (fan_control_initial_status == 0x07 &&
- ibm_thinkpad_ec_found &&
- ((ibm_thinkpad_ec_found[0] == '1' &&
- ibm_thinkpad_ec_found[1] == 'Y') ||
- (ibm_thinkpad_ec_found[0] == '7' &&
- (ibm_thinkpad_ec_found[1] == '6' ||
- ibm_thinkpad_ec_found[1] == '8' ||
- ibm_thinkpad_ec_found[1] == '0'))
- )) {
- printk(IBM_NOTICE
- "fan_init: initial fan status is "
- "unknown, assuming it is in auto "
- "mode\n");
- fan_control_status_known = 0;
- }
- } else {
- printk(IBM_ERR
- "ThinkPad ACPI EC access misbehaving, "
- "fan status and control unavailable\n");
- return 0;
- }
- }
-
- if (sfan_handle) {
- /* 570, 770x-JL */
- fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN;
- fan_control_commands |=
- IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE;
- } else {
- if (!gfan_handle) {
- /* gfan without sfan means no fan control */
- /* all other models implement TP EC 0x2f control */
-
- if (fans_handle) {
- /* X31, X40, X41 */
- fan_control_access_mode =
- IBMACPI_FAN_WR_ACPI_FANS;
- fan_control_commands |=
- IBMACPI_FAN_CMD_SPEED |
- IBMACPI_FAN_CMD_LEVEL |
- IBMACPI_FAN_CMD_ENABLE;
- } else {
- fan_control_access_mode = IBMACPI_FAN_WR_TPEC;
- fan_control_commands |=
- IBMACPI_FAN_CMD_LEVEL |
- IBMACPI_FAN_CMD_ENABLE;
- }
- }
- }
-
- return 0;
-}
-
-static int fan_get_status(u8 *status)
-{
- u8 s;
-
- /* TODO:
- * Add IBMACPI_FAN_RD_ACPI_FANS ? */
-
- switch (fan_status_access_mode) {
- case IBMACPI_FAN_RD_ACPI_GFAN:
- /* 570, 600e/x, 770e, 770x */
-
- if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
- return -EIO;
-
- if (likely(status))
- *status = s & 0x07;
-
- break;
-
- case IBMACPI_FAN_RD_TPEC:
- /* all except 570, 600e/x, 770e, 770x */
- if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
- return -EIO;
-
- if (likely(status))
- *status = s;
-
- break;
-
- default:
- return -ENXIO;
- }
-
- return 0;
-}
-
-static int fan_get_speed(unsigned int *speed)
-{
- u8 hi, lo;
-
- switch (fan_status_access_mode) {
- case IBMACPI_FAN_RD_TPEC:
- /* all except 570, 600e/x, 770e, 770x */
- if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
- !acpi_ec_read(fan_rpm_offset + 1, &hi)))
- return -EIO;
-
- if (likely(speed))
- *speed = (hi << 8) | lo;
-
- break;
-
- default:
- return -ENXIO;
- }
-
- return 0;
-}
-
-static void fan_exit(void)
-{
- cancel_delayed_work(&fan_watchdog_task);
- flush_scheduled_work();
-}
-
-static void fan_watchdog_reset(void)
-{
- static int fan_watchdog_active = 0;
-
- if (fan_watchdog_active)
- cancel_delayed_work(&fan_watchdog_task);
-
- if (fan_watchdog_maxinterval > 0) {
- fan_watchdog_active = 1;
- if (!schedule_delayed_work(&fan_watchdog_task,
- msecs_to_jiffies(fan_watchdog_maxinterval
- * 1000))) {
- printk(IBM_ERR "failed to schedule the fan watchdog, "
- "watchdog will not trigger\n");
- }
- } else
- fan_watchdog_active = 0;
-}
-
-static int fan_read(char *p)
-{
- int len = 0;
- int rc;
- u8 status;
- unsigned int speed = 0;
-
- switch (fan_status_access_mode) {
- case IBMACPI_FAN_RD_ACPI_GFAN:
- /* 570, 600e/x, 770e, 770x */
- if ((rc = fan_get_status(&status)) < 0)
- return rc;
-
- len += sprintf(p + len, "status:\t\t%s\n"
- "level:\t\t%d\n",
- (status != 0) ? "enabled" : "disabled", status);
- break;
-
- case IBMACPI_FAN_RD_TPEC:
- /* all except 570, 600e/x, 770e, 770x */
- if ((rc = fan_get_status(&status)) < 0)
- return rc;
-
- if (unlikely(!fan_control_status_known)) {
- if (status != fan_control_initial_status)
- fan_control_status_known = 1;
- else
- /* Return most likely status. In fact, it
- * might be the only possible status */
- status = IBMACPI_FAN_EC_AUTO;
- }
-
- len += sprintf(p + len, "status:\t\t%s\n",
- (status != 0) ? "enabled" : "disabled");
-
- /* No ThinkPad boots on disengaged mode, we can safely
- * assume the tachometer is online if fan control status
- * was unknown */
- if ((rc = fan_get_speed(&speed)) < 0)
- return rc;
-
- len += sprintf(p + len, "speed:\t\t%d\n", speed);
-
- if (status & IBMACPI_FAN_EC_DISENGAGED)
- /* Disengaged mode takes precedence */
- len += sprintf(p + len, "level:\t\tdisengaged\n");
- else if (status & IBMACPI_FAN_EC_AUTO)
- len += sprintf(p + len, "level:\t\tauto\n");
- else
- len += sprintf(p + len, "level:\t\t%d\n", status);
- break;
-
- case IBMACPI_FAN_NONE:
- default:
- len += sprintf(p + len, "status:\t\tnot supported\n");
- }
-
- if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) {
- len += sprintf(p + len, "commands:\tlevel <level>");
-
- switch (fan_control_access_mode) {
- case IBMACPI_FAN_WR_ACPI_SFAN:
- len += sprintf(p + len, " (<level> is 0-7)\n");
- break;
-
- default:
- len += sprintf(p + len, " (<level> is 0-7, "
- "auto, disengaged)\n");
- break;
- }
- }
-
- if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE)
- len += sprintf(p + len, "commands:\tenable, disable\n"
- "commands:\twatchdog <timeout> (<timeout> is 0 (off), "
- "1-120 (seconds))\n");
-
- if (fan_control_commands & IBMACPI_FAN_CMD_SPEED)
- len += sprintf(p + len, "commands:\tspeed <speed>"
- " (<speed> is 0-65535)\n");
-
- return len;
-}
-
-static int fan_set_level(int level)
-{
- switch (fan_control_access_mode) {
- case IBMACPI_FAN_WR_ACPI_SFAN:
- if (level >= 0 && level <= 7) {
- if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
- return -EIO;
- } else
- return -EINVAL;
- break;
-
- case IBMACPI_FAN_WR_ACPI_FANS:
- case IBMACPI_FAN_WR_TPEC:
- if ((level != IBMACPI_FAN_EC_AUTO) &&
- (level != IBMACPI_FAN_EC_DISENGAGED) &&
- ((level < 0) || (level > 7)))
- return -EINVAL;
-
- if (!acpi_ec_write(fan_status_offset, level))
- return -EIO;
- else
- fan_control_status_known = 1;
- break;
-
- default:
- return -ENXIO;
- }
- return 0;
-}
-
-static int fan_set_enable(void)
-{
- u8 s;
- int rc;
-
- switch (fan_control_access_mode) {
- case IBMACPI_FAN_WR_ACPI_FANS:
- case IBMACPI_FAN_WR_TPEC:
- if ((rc = fan_get_status(&s)) < 0)
- return rc;
-
- /* Don't go out of emergency fan mode */
- if (s != 7)
- s = IBMACPI_FAN_EC_AUTO;
-
- if (!acpi_ec_write(fan_status_offset, s))
- return -EIO;
- else
- fan_control_status_known = 1;
- break;
-
- case IBMACPI_FAN_WR_ACPI_SFAN:
- if ((rc = fan_get_status(&s)) < 0)
- return rc;
-
- s &= 0x07;
-
- /* Set fan to at least level 4 */
- if (s < 4)
- s = 4;
-
- if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
- return -EIO;
- break;
-
- default:
- return -ENXIO;
- }
- return 0;
-}
-
-static int fan_set_disable(void)
-{
- switch (fan_control_access_mode) {
- case IBMACPI_FAN_WR_ACPI_FANS:
- case IBMACPI_FAN_WR_TPEC:
- if (!acpi_ec_write(fan_status_offset, 0x00))
- return -EIO;
- else
- fan_control_status_known = 1;
- break;
-
- case IBMACPI_FAN_WR_ACPI_SFAN:
- if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
- return -EIO;
- break;
-
- default:
- return -ENXIO;
- }
- return 0;
-}
-
-static int fan_set_speed(int speed)
-{
- switch (fan_control_access_mode) {
- case IBMACPI_FAN_WR_ACPI_FANS:
- if (speed >= 0 && speed <= 65535) {
- if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
- speed, speed, speed))
- return -EIO;
- } else
- return -EINVAL;
- break;
-
- default:
- return -ENXIO;
- }
- return 0;
-}
-
-static int fan_write_cmd_level(const char *cmd, int *rc)
-{
- int level;
-
- if (strlencmp(cmd, "level auto") == 0)
- level = IBMACPI_FAN_EC_AUTO;
- else if (strlencmp(cmd, "level disengaged") == 0)
- level = IBMACPI_FAN_EC_DISENGAGED;
- else if (sscanf(cmd, "level %d", &level) != 1)
- return 0;
-
- if ((*rc = fan_set_level(level)) == -ENXIO)
- printk(IBM_ERR "level command accepted for unsupported "
- "access mode %d", fan_control_access_mode);
-
- return 1;
-}
-
-static int fan_write_cmd_enable(const char *cmd, int *rc)
-{
- if (strlencmp(cmd, "enable") != 0)
- return 0;
-
- if ((*rc = fan_set_enable()) == -ENXIO)
- printk(IBM_ERR "enable command accepted for unsupported "
- "access mode %d", fan_control_access_mode);
-
- return 1;
-}
-
-static int fan_write_cmd_disable(const char *cmd, int *rc)
-{
- if (strlencmp(cmd, "disable") != 0)
- return 0;
-
- if ((*rc = fan_set_disable()) == -ENXIO)
- printk(IBM_ERR "disable command accepted for unsupported "
- "access mode %d", fan_control_access_mode);
-
- return 1;
-}
-
-static int fan_write_cmd_speed(const char *cmd, int *rc)
-{
- int speed;
-
- /* TODO:
- * Support speed <low> <medium> <high> ? */
-
- if (sscanf(cmd, "speed %d", &speed) != 1)
- return 0;
-
- if ((*rc = fan_set_speed(speed)) == -ENXIO)
- printk(IBM_ERR "speed command accepted for unsupported "
- "access mode %d", fan_control_access_mode);
-
- return 1;
-}
-
-static int fan_write_cmd_watchdog(const char *cmd, int *rc)
-{
- int interval;
-
- if (sscanf(cmd, "watchdog %d", &interval) != 1)
- return 0;
-
- if (interval < 0 || interval > 120)
- *rc = -EINVAL;
- else
- fan_watchdog_maxinterval = interval;
-
- return 1;
-}
-
-static int fan_write(char *buf)
-{
- char *cmd;
- int rc = 0;
-
- while (!rc && (cmd = next_cmd(&buf))) {
- if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) &&
- fan_write_cmd_level(cmd, &rc)) &&
- !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) &&
- (fan_write_cmd_enable(cmd, &rc) ||
- fan_write_cmd_disable(cmd, &rc) ||
- fan_write_cmd_watchdog(cmd, &rc))) &&
- !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) &&
- fan_write_cmd_speed(cmd, &rc))
- )
- rc = -EINVAL;
- else if (!rc)
- fan_watchdog_reset();
- }
-
- return rc;
-}
-
-static void fan_watchdog_fire(struct work_struct *ignored)
-{
- printk(IBM_NOTICE "fan watchdog: enabling fan\n");
- if (fan_set_enable()) {
- printk(IBM_ERR "fan watchdog: error while enabling fan\n");
- /* reschedule for later */
- fan_watchdog_reset();
- }
-}
-
-static struct ibm_struct ibms[] = {
- {
- .name = "driver",
- .init = ibm_acpi_driver_init,
- .read = driver_read,
- },
- {
- .name = "hotkey",
- .hid = IBM_HKEY_HID,
- .init = hotkey_init,
- .read = hotkey_read,
- .write = hotkey_write,
- .exit = hotkey_exit,
- .notify = hotkey_notify,
- .handle = &hkey_handle,
- .type = ACPI_DEVICE_NOTIFY,
- },
- {
- .name = "bluetooth",
- .init = bluetooth_init,
- .read = bluetooth_read,
- .write = bluetooth_write,
- },
- {
- .name = "wan",
- .init = wan_init,
- .read = wan_read,
- .write = wan_write,
- .experimental = 1,
- },
- {
- .name = "video",
- .init = video_init,
- .read = video_read,
- .write = video_write,
- .exit = video_exit,
- },
- {
- .name = "light",
- .init = light_init,
- .read = light_read,
- .write = light_write,
- },
-#ifdef CONFIG_ACPI_IBM_DOCK
- {
- .name = "dock",
- .read = dock_read,
- .write = dock_write,
- .notify = dock_notify,
- .handle = &dock_handle,
- .type = ACPI_SYSTEM_NOTIFY,
- },
- {
- .name = "dock",
- .hid = IBM_PCI_HID,
- .notify = dock_notify,
- .handle = &pci_handle,
- .type = ACPI_SYSTEM_NOTIFY,
- },
-#endif
-#ifdef CONFIG_ACPI_IBM_BAY
- {
- .name = "bay",
- .init = bay_init,
- .read = bay_read,
- .write = bay_write,
- .notify = bay_notify,
- .handle = &bay_handle,
- .type = ACPI_SYSTEM_NOTIFY,
- },
-#endif /* CONFIG_ACPI_IBM_BAY */
- {
- .name = "cmos",
- .read = cmos_read,
- .write = cmos_write,
- },
- {
- .name = "led",
- .init = led_init,
- .read = led_read,
- .write = led_write,
- },
- {
- .name = "beep",
- .read = beep_read,
- .write = beep_write,
- },
- {
- .name = "thermal",
- .init = thermal_init,
- .read = thermal_read,
- },
- {
- .name = "ecdump",
- .read = ecdump_read,
- .write = ecdump_write,
- .experimental = 1,
- },
- {
- .name = "brightness",
- .read = brightness_read,
- .write = brightness_write,
- .init = brightness_init,
- .exit = brightness_exit,
- },
- {
- .name = "volume",
- .read = volume_read,
- .write = volume_write,
- },
- {
- .name = "fan",
- .read = fan_read,
- .write = fan_write,
- .init = fan_init,
- .exit = fan_exit,
- .experimental = 1,
- },
-};
-
-static int dispatch_read(char *page, char **start, off_t off, int count,
- int *eof, void *data)
-{
- struct ibm_struct *ibm = data;
- int len;
-
- if (!ibm || !ibm->read)
- return -EINVAL;
-
- len = ibm->read(page);
- if (len < 0)
- return len;
-
- if (len <= off + count)
- *eof = 1;
- *start = page + off;
- len -= off;
- if (len > count)
- len = count;
- if (len < 0)
- len = 0;
-
- return len;
-}
-
-static int dispatch_write(struct file *file, const char __user * userbuf,
- unsigned long count, void *data)
-{
- struct ibm_struct *ibm = data;
- char *kernbuf;
- int ret;
-
- if (!ibm || !ibm->write)
- return -EINVAL;
-
- kernbuf = kmalloc(count + 2, GFP_KERNEL);
- if (!kernbuf)
- return -ENOMEM;
-
- if (copy_from_user(kernbuf, userbuf, count)) {
- kfree(kernbuf);
- return -EFAULT;
- }
-
- kernbuf[count] = 0;
- strcat(kernbuf, ",");
- ret = ibm->write(kernbuf);
- if (ret == 0)
- ret = count;
-
- kfree(kernbuf);
-
- return ret;
-}
-
-static void dispatch_notify(acpi_handle handle, u32 event, void *data)
-{
- struct ibm_struct *ibm = data;
-
- if (!ibm || !ibm->notify)
- return;
-
- ibm->notify(ibm, event);
-}
-
-static int __init setup_notify(struct ibm_struct *ibm)
-{
- acpi_status status;
- int ret;
-
- if (!*ibm->handle)
- return 0;
-
- ret = acpi_bus_get_device(*ibm->handle, &ibm->device);
- if (ret < 0) {
- printk(IBM_ERR "%s device not present\n", ibm->name);
- return -ENODEV;
- }
-
- acpi_driver_data(ibm->device) = ibm;
- sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name);
-
- status = acpi_install_notify_handler(*ibm->handle, ibm->type,
- dispatch_notify, ibm);
- if (ACPI_FAILURE(status)) {
- if (status == AE_ALREADY_EXISTS) {
- printk(IBM_NOTICE "another device driver is already handling %s events\n",
- ibm->name);
- } else {
- printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n",
- ibm->name, status);
- }
- return -ENODEV;
- }
- ibm->notify_installed = 1;
- return 0;
-}
-
-static int __init ibm_device_add(struct acpi_device *device)
-{
- return 0;
-}
-
-static int __init register_driver(struct ibm_struct *ibm)
-{
- int ret;
-
- ibm->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
- if (!ibm->driver) {
- printk(IBM_ERR "kmalloc(ibm->driver) failed\n");
- return -1;
- }
-
- sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name);
- ibm->driver->ids = ibm->hid;
- ibm->driver->ops.add = &ibm_device_add;
-
- ret = acpi_bus_register_driver(ibm->driver);
- if (ret < 0) {
- printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n",
- ibm->hid, ret);
- kfree(ibm->driver);
- }
-
- return ret;
-}
-
-static void ibm_exit(struct ibm_struct *ibm);
-
-static int __init ibm_init(struct ibm_struct *ibm)
-{
- int ret;
- struct proc_dir_entry *entry;
-
- if (ibm->experimental && !experimental)
- return 0;
-
- if (ibm->hid) {
- ret = register_driver(ibm);
- if (ret < 0)
- return ret;
- ibm->driver_registered = 1;
- }
-
- if (ibm->init) {
- ret = ibm->init();
- if (ret != 0)
- return ret;
- ibm->init_called = 1;
- }
-
- if (ibm->read) {
- entry = create_proc_entry(ibm->name,
- S_IFREG | S_IRUGO | S_IWUSR,
- proc_dir);
- if (!entry) {
- printk(IBM_ERR "unable to create proc entry %s\n",
- ibm->name);
- return -ENODEV;
- }
- entry->owner = THIS_MODULE;
- entry->data = ibm;
- entry->read_proc = &dispatch_read;
- if (ibm->write)
- entry->write_proc = &dispatch_write;
- ibm->proc_created = 1;
- }
-
- if (ibm->notify) {
- ret = setup_notify(ibm);
- if (ret == -ENODEV) {
- printk(IBM_NOTICE "disabling subdriver %s\n",
- ibm->name);
- ibm_exit(ibm);
- return 0;
- }
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
-static void ibm_exit(struct ibm_struct *ibm)
-{
- if (ibm->notify_installed)
- acpi_remove_notify_handler(*ibm->handle, ibm->type,
- dispatch_notify);
-
- if (ibm->proc_created)
- remove_proc_entry(ibm->name, proc_dir);
-
- if (ibm->init_called && ibm->exit)
- ibm->exit();
-
- if (ibm->driver_registered) {
- acpi_bus_unregister_driver(ibm->driver);
- kfree(ibm->driver);
- }
-}
-
-static void __init ibm_handle_init(char *name,
- acpi_handle * handle, acpi_handle parent,
- char **paths, int num_paths, char **path)
-{
- int i;
- acpi_status status;
-
- for (i = 0; i < num_paths; i++) {
- status = acpi_get_handle(parent, paths[i], handle);
- if (ACPI_SUCCESS(status)) {
- *path = paths[i];
- return;
- }
- }
-
- *handle = NULL;
-}
-
-#define IBM_HANDLE_INIT(object) \
- ibm_handle_init(#object, &object##_handle, *object##_parent, \
- object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
-
-static int __init set_ibm_param(const char *val, struct kernel_param *kp)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(ibms); i++)
- if (strcmp(ibms[i].name, kp->name) == 0 && ibms[i].write) {
- if (strlen(val) > sizeof(ibms[i].param) - 2)
- return -ENOSPC;
- strcpy(ibms[i].param, val);
- strcat(ibms[i].param, ",");
- return 0;
- }
-
- return -EINVAL;
-}
-
-#define IBM_PARAM(feature) \
- module_param_call(feature, set_ibm_param, NULL, NULL, 0)
-
-IBM_PARAM(hotkey);
-IBM_PARAM(bluetooth);
-IBM_PARAM(video);
-IBM_PARAM(light);
-#ifdef CONFIG_ACPI_IBM_DOCK
-IBM_PARAM(dock);
-#endif
-#ifdef CONFIG_ACPI_IBM_BAY
-IBM_PARAM(bay);
-#endif /* CONFIG_ACPI_IBM_BAY */
-IBM_PARAM(cmos);
-IBM_PARAM(led);
-IBM_PARAM(beep);
-IBM_PARAM(ecdump);
-IBM_PARAM(brightness);
-IBM_PARAM(volume);
-IBM_PARAM(fan);
-
-static void acpi_ibm_exit(void)
-{
- int i;
-
- for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--)
- ibm_exit(&ibms[i]);
-
- if (proc_dir)
- remove_proc_entry(IBM_DIR, acpi_root_dir);
-
- if (ibm_thinkpad_ec_found)
- kfree(ibm_thinkpad_ec_found);
-}
-
-static char* __init check_dmi_for_ec(void)
-{
- struct dmi_device *dev = NULL;
- char ec_fw_string[18];
-
- /*
- * ThinkPad T23 or newer, A31 or newer, R50e or newer,
- * X32 or newer, all Z series; Some models must have an
- * up-to-date BIOS or they will not be detected.
- *
- * See http://thinkwiki.org/wiki/List_of_DMI_IDs
- */
- while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
- if (sscanf(dev->name,
- "IBM ThinkPad Embedded Controller -[%17c",
- ec_fw_string) == 1) {
- ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
- ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
- return kstrdup(ec_fw_string, GFP_KERNEL);
- }
- }
- return NULL;
-}
-
-static int __init acpi_ibm_init(void)
-{
- int ret, i;
-
- if (acpi_disabled)
- return -ENODEV;
-
- /* ec is required because many other handles are relative to it */
- IBM_HANDLE_INIT(ec);
- if (!ec_handle) {
- printk(IBM_ERR "ec object not found\n");
- return -ENODEV;
- }
-
- /* Models with newer firmware report the EC in DMI */
- ibm_thinkpad_ec_found = check_dmi_for_ec();
-
- /* these handles are not required */
- IBM_HANDLE_INIT(vid);
- IBM_HANDLE_INIT(vid2);
- IBM_HANDLE_INIT(ledb);
- IBM_HANDLE_INIT(led);
- IBM_HANDLE_INIT(hkey);
- IBM_HANDLE_INIT(lght);
- IBM_HANDLE_INIT(cmos);
-#ifdef CONFIG_ACPI_IBM_DOCK
- IBM_HANDLE_INIT(dock);
-#endif
- IBM_HANDLE_INIT(pci);
-#ifdef CONFIG_ACPI_IBM_BAY
- IBM_HANDLE_INIT(bay);
- if (bay_handle)
- IBM_HANDLE_INIT(bay_ej);
- IBM_HANDLE_INIT(bay2);
- if (bay2_handle)
- IBM_HANDLE_INIT(bay2_ej);
-#endif /* CONFIG_ACPI_IBM_BAY */
- IBM_HANDLE_INIT(beep);
- IBM_HANDLE_INIT(ecrd);
- IBM_HANDLE_INIT(ecwr);
- IBM_HANDLE_INIT(fans);
- IBM_HANDLE_INIT(gfan);
- IBM_HANDLE_INIT(sfan);
-
- proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir);
- if (!proc_dir) {
- printk(IBM_ERR "unable to create proc dir %s", IBM_DIR);
- acpi_ibm_exit();
- return -ENODEV;
- }
- proc_dir->owner = THIS_MODULE;
-
- for (i = 0; i < ARRAY_SIZE(ibms); i++) {
- ret = ibm_init(&ibms[i]);
- if (ret >= 0 && *ibms[i].param)
- ret = ibms[i].write(ibms[i].param);
- if (ret < 0) {
- acpi_ibm_exit();
- return ret;
- }
- }
-
- return 0;
-}
-
-module_init(acpi_ibm_init);
-module_exit(acpi_ibm_exit);
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index 99d1516..f7de02a 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -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 @@
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;
}
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index cdf7894..ae0654c 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -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>
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index 59640d9..c1bae10 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -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 @@
#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 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_PRTCL 0x00 /* protocol, PEC */
-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_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 */
-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_STS_DONE 0x80
+#define ACPI_EC_SMB_STS_STATUS 0x1f
+
+#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
+
+#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 @@
.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 @@
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 {
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_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";
+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 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";
- break;
- case 0x0005:
- err_str = "overflow/underflow";
- break;
- case 0x0006:
- err_str = "bad size";
- break;
- case 0x0007:
- err_str = "unknown error";
+ 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;
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));
+
+ 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 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:
+ ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+ "unsupported transaction %d", size));
+ return (-1);
+ }
+
+ 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 @@
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 @@
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_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_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 @@
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 @@
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_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 @@
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_mode(struct acpi_battery *battery)
{
+ struct acpi_sbs *sbs = battery->sbs;
int result = 0;
s16 battery_mode;
@@ -732,12 +751,11 @@
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 @@
} 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 @@
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_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 @@
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 @@
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 @@
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 @@
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 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 @@
} 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 @@
end:
- up(&sbs_sem);
+ sbs_mutex_unlock(sbs);
return result;
}
@@ -996,26 +1014,29 @@
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 @@
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 @@
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_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 @@
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 @@
{
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 @@
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 @@
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 @@
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 @@
&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 @@
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 @@
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_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,81 +1480,92 @@
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) {
- 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;
- }
+ if (!new_battery_present)
+ goto event;
+ if (do_ac_init || do_battery_init) {
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"));
+ }
+ }
+ if (sbs_zombie(sbs)) {
+ goto end;
+ }
+
+ if ((data_type == DATA_TYPE_COMMON
+ || data_type == DATA_TYPE_INFO)
+ && new_battery_present) {
+ result = acpi_battery_get_info(battery);
+ if (result) {
+ 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) {
+ if (data_type == DATA_TYPE_STATE) {
+ goto event;
+ }
+ if (sbs_zombie(sbs)) {
goto end;
}
- if (data_type != DATA_TYPE_COMMON) {
+
+ if ((data_type == DATA_TYPE_COMMON
+ || data_type == DATA_TYPE_ALARM)
+ && new_battery_present) {
+ result = acpi_battery_get_alarm(battery);
+ if (result) {
+ ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+ "acpi_battery_get_alarm() "
+ "failed"));
+ }
+ }
+ if (data_type == DATA_TYPE_ALARM) {
continue;
}
+ if (sbs_zombie(sbs)) {
+ goto end;
+ }
- if (old_battery_present != new_battery_present) {
+ 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,
@@ -1489,151 +1573,120 @@
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 (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,
- new_battery_present,
- dir_name,
- ACPI_BATTERY_CLASS);
- if (result) {
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
- "acpi_sbs_generate_event() failed\n"));
- }
- }
-
- }
- 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;
- }
-
- cnt = 0;
- while (cnt < 10) {
- cnt++;
- ec_hc = acpi_get_ec_hc(device);
- if (ec_hc) {
- break;
- }
- msleep(1000);
- }
-
- if (!ec_hc) {
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
- "acpi_get_ec_hc() failed: "
- "NO driver found for EC HC SMBus\n"));
- result = -ENODEV;
+ ACPI_EXCEPTION((AE_INFO, AE_ERROR, "kzalloc() failed"));
+ result = -ENOMEM;
goto end;
}
+ mutex_init(&sbs->mutex);
+
+ 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 @@
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 @@
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 @@
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 @@
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 @@
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 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;
}
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index bb0e0da..d80dd84 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1068,7 +1068,9 @@
}
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;
}
diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c
index ccc11b3..2d912b7 100644
--- a/drivers/acpi/sleep/proc.c
+++ b/drivers/acpi/sleep/proc.c
@@ -350,21 +350,31 @@
{
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);
diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/tables/tbfadt.c
index 807c711..1db833e 100644
--- a/drivers/acpi/tables/tbfadt.c
+++ b/drivers/acpi/tables/tbfadt.c
@@ -347,6 +347,18 @@
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;
+ }
}
/******************************************************************************
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 7bdbe5a..365c306 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -156,11 +156,6 @@
help
This option enables support for Initio 162x Serial ATA.
-config SATA_INTEL_COMBINED
- bool
- depends on IDE=y && !BLK_DEV_IDE_SATA && (SATA_AHCI || ATA_PIIX)
- default y
-
config SATA_ACPI
bool
depends on ACPI && PCI
@@ -184,7 +179,7 @@
If unsure, say N.
config PATA_AMD
- tristate "AMD/NVidia PATA support (Experimental)"
+ tristate "AMD/NVidia PATA support"
depends on PCI
help
This option enables support for the AMD and NVidia PATA
@@ -209,6 +204,16 @@
If unsure, say N.
+config PATA_CMD640_PCI
+ tristate "CMD640 PCI PATA support (Very Experimental)"
+ depends on PCI && EXPERIMENTAL
+ help
+ This option enables support for the CMD640 PCI IDE
+ interface chip. Only the primary channel is currently
+ supported.
+
+ If unsure, say N.
+
config PATA_CMD64X
tristate "CMD64x PATA support (Very Experimental)"
depends on PCI&& EXPERIMENTAL
@@ -273,7 +278,7 @@
If unsure, say N.
config PATA_HPT366
- tristate "HPT 366/368 PATA support (Very Experimental)"
+ tristate "HPT 366/368 PATA support (Experimental)"
depends on PCI && EXPERIMENTAL
help
This option enables support for the HPT 366 and 368
@@ -282,7 +287,7 @@
If unsure, say N.
config PATA_HPT37X
- tristate "HPT 370/370A/371/372/374/302 PATA support (Very Experimental)"
+ tristate "HPT 370/370A/371/372/374/302 PATA support (Experimental)"
depends on PCI && EXPERIMENTAL
help
This option enables support for the majority of the later HPT
@@ -309,7 +314,7 @@
If unsure, say N.
config PATA_ISAPNP
- tristate "ISA Plug and Play PATA support (Very Experimental)"
+ tristate "ISA Plug and Play PATA support (Experimental)"
depends on EXPERIMENTAL && ISAPNP
help
This option enables support for ISA plug & play ATA
@@ -318,8 +323,8 @@
If unsure, say N.
config PATA_IT821X
- tristate "IT8211/2 PATA support (Experimental)"
- depends on PCI && EXPERIMENTAL
+ tristate "IT8211/2 PATA support"
+ depends on PCI
help
This option enables support for the ITE 8211 and 8212
PATA controllers via the new ATA layer, including RAID
@@ -390,10 +395,10 @@
If unsure, say N.
config PATA_OLDPIIX
- tristate "Intel PATA old PIIX support (Experimental)"
- depends on PCI && EXPERIMENTAL
+ tristate "Intel PATA old PIIX support"
+ depends on PCI
help
- This option enables support for old(?) PIIX PATA support.
+ This option enables support for early PIIX PATA support.
If unsure, say N.
@@ -444,7 +449,7 @@
If unsure, say N.
config PATA_PDC_OLD
- tristate "Older Promise PATA controller support (Very Experimental)"
+ tristate "Older Promise PATA controller support (Experimental)"
depends on PCI && EXPERIMENTAL
help
This option enables support for the Promise 20246, 20262, 20263,
@@ -459,7 +464,7 @@
Support for QDI 6500 and 6580 PATA controllers on VESA local bus.
config PATA_RADISYS
- tristate "RADISYS 82600 PATA support (Very experimental)"
+ tristate "RADISYS 82600 PATA support (Very Experimental)"
depends on PCI && EXPERIMENTAL
help
This option enables support for the RADISYS 82600
@@ -477,7 +482,7 @@
If unsure, say N.
config PATA_SC1200
- tristate "SC1200 PATA support (Raving Lunatic)"
+ tristate "SC1200 PATA support (Very Experimental)"
depends on PCI && EXPERIMENTAL
help
This option enables support for the NatSemi/AMD SC1200 SoC
@@ -486,8 +491,8 @@
If unsure, say N.
config PATA_SERVERWORKS
- tristate "SERVERWORKS OSB4/CSB5/CSB6/HT1000 PATA support (Experimental)"
- depends on PCI && EXPERIMENTAL
+ tristate "SERVERWORKS OSB4/CSB5/CSB6/HT1000 PATA support"
+ depends on PCI
help
This option enables support for the Serverworks OSB4/CSB5/CSB6 and
HT1000 PATA controllers, via the new ATA layer.
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 13d7397..b7055e3 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -22,6 +22,7 @@
obj-$(CONFIG_PATA_AMD) += pata_amd.o
obj-$(CONFIG_PATA_ARTOP) += pata_artop.o
obj-$(CONFIG_PATA_ATIIXP) += pata_atiixp.o
+obj-$(CONFIG_PATA_CMD640_PCI) += pata_cmd640.o
obj-$(CONFIG_PATA_CMD64X) += pata_cmd64x.o
obj-$(CONFIG_PATA_CS5520) += pata_cs5520.o
obj-$(CONFIG_PATA_CS5530) += pata_cs5530.o
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index fd27227..34c5534 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -170,6 +170,10 @@
AHCI_FLAG_IGN_IRQ_IF_ERR = (1 << 25), /* ignore IRQ_IF_ERR */
AHCI_FLAG_HONOR_PI = (1 << 26), /* honor PORTS_IMPL */
AHCI_FLAG_IGN_SERR_INTERNAL = (1 << 27), /* ignore SERR_INTERNAL */
+
+ AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
+ ATA_FLAG_SKIP_D2H_BSY,
};
struct ahci_cmd_hdr {
@@ -188,8 +192,10 @@
};
struct ahci_host_priv {
- u32 cap; /* cache of HOST_CAP register */
- u32 port_map; /* cache of HOST_PORTS_IMPL reg */
+ u32 cap; /* cap to use */
+ u32 port_map; /* port map to use */
+ u32 saved_cap; /* saved initial cap */
+ u32 saved_port_map; /* saved initial port_map */
};
struct ahci_port_priv {
@@ -209,7 +215,6 @@
static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc);
-static irqreturn_t ahci_interrupt (int irq, void *dev_instance);
static void ahci_irq_clear(struct ata_port *ap);
static int ahci_port_start(struct ata_port *ap);
static void ahci_port_stop(struct ata_port *ap);
@@ -263,7 +268,6 @@
.qc_prep = ahci_qc_prep,
.qc_issue = ahci_qc_issue,
- .irq_handler = ahci_interrupt,
.irq_clear = ahci_irq_clear,
.irq_on = ata_dummy_irq_on,
.irq_ack = ata_dummy_irq_ack,
@@ -298,7 +302,6 @@
.qc_prep = ahci_qc_prep,
.qc_issue = ahci_qc_issue,
- .irq_handler = ahci_interrupt,
.irq_clear = ahci_irq_clear,
.irq_on = ata_dummy_irq_on,
.irq_ack = ata_dummy_irq_ack,
@@ -324,58 +327,41 @@
static const struct ata_port_info ahci_port_info[] = {
/* board_ahci */
{
- .sht = &ahci_sht,
- .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
- ATA_FLAG_SKIP_D2H_BSY,
+ .flags = AHCI_FLAG_COMMON,
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
.port_ops = &ahci_ops,
},
/* board_ahci_pi */
{
- .sht = &ahci_sht,
- .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
- ATA_FLAG_SKIP_D2H_BSY | AHCI_FLAG_HONOR_PI,
+ .flags = AHCI_FLAG_COMMON | AHCI_FLAG_HONOR_PI,
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
.port_ops = &ahci_ops,
},
/* board_ahci_vt8251 */
{
- .sht = &ahci_sht,
- .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
- ATA_FLAG_SKIP_D2H_BSY |
- ATA_FLAG_HRST_TO_RESUME | AHCI_FLAG_NO_NCQ,
+ .flags = AHCI_FLAG_COMMON | ATA_FLAG_HRST_TO_RESUME |
+ AHCI_FLAG_NO_NCQ,
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
.port_ops = &ahci_vt8251_ops,
},
/* board_ahci_ign_iferr */
{
- .sht = &ahci_sht,
- .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
- ATA_FLAG_SKIP_D2H_BSY |
- AHCI_FLAG_IGN_IRQ_IF_ERR,
+ .flags = AHCI_FLAG_COMMON | AHCI_FLAG_IGN_IRQ_IF_ERR,
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
.port_ops = &ahci_ops,
},
/* board_ahci_sb600 */
{
- .sht = &ahci_sht,
- .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
- ATA_FLAG_SKIP_D2H_BSY |
+ .flags = AHCI_FLAG_COMMON |
AHCI_FLAG_IGN_SERR_INTERNAL,
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
.port_ops = &ahci_ops,
},
-
};
static const struct pci_device_id ahci_pci_tbl[] = {
@@ -413,11 +399,11 @@
PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr },
/* ATI */
- { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 non-raid */
- { PCI_VDEVICE(ATI, 0x4381), board_ahci }, /* ATI SB600 raid */
+ { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */
/* VIA */
{ PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */
+ { PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */
/* NVIDIA */
{ PCI_VDEVICE(NVIDIA, 0x044c), board_ahci }, /* MCP65 */
@@ -471,10 +457,100 @@
return (cap & 0x1f) + 1;
}
-static inline void __iomem *ahci_port_base(void __iomem *base,
- unsigned int port)
+static inline void __iomem *ahci_port_base(struct ata_port *ap)
{
- return base + 0x100 + (port * 0x80);
+ void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
+
+ return mmio + 0x100 + (ap->port_no * 0x80);
+}
+
+/**
+ * ahci_save_initial_config - Save and fixup initial config values
+ * @pdev: target PCI device
+ * @pi: associated ATA port info
+ * @hpriv: host private area to store config values
+ *
+ * Some registers containing configuration info might be setup by
+ * BIOS and might be cleared on reset. This function saves the
+ * initial values of those registers into @hpriv such that they
+ * can be restored after controller reset.
+ *
+ * If inconsistent, config values are fixed up by this function.
+ *
+ * LOCKING:
+ * None.
+ */
+static void ahci_save_initial_config(struct pci_dev *pdev,
+ const struct ata_port_info *pi,
+ struct ahci_host_priv *hpriv)
+{
+ void __iomem *mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR];
+ u32 cap, port_map;
+ int i;
+
+ /* Values prefixed with saved_ are written back to host after
+ * reset. Values without are used for driver operation.
+ */
+ hpriv->saved_cap = cap = readl(mmio + HOST_CAP);
+ hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL);
+
+ /* fixup zero port_map */
+ if (!port_map) {
+ port_map = (1 << ahci_nr_ports(hpriv->cap)) - 1;
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "PORTS_IMPL is zero, forcing 0x%x\n", port_map);
+
+ /* write the fixed up value to the PI register */
+ hpriv->saved_port_map = port_map;
+ }
+
+ /* cross check port_map and cap.n_ports */
+ if (pi->flags & AHCI_FLAG_HONOR_PI) {
+ u32 tmp_port_map = port_map;
+ int n_ports = ahci_nr_ports(cap);
+
+ for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) {
+ if (tmp_port_map & (1 << i)) {
+ n_ports--;
+ tmp_port_map &= ~(1 << i);
+ }
+ }
+
+ /* Whine if inconsistent. No need to update cap.
+ * port_map is used to determine number of ports.
+ */
+ if (n_ports || tmp_port_map)
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "nr_ports (%u) and implemented port map "
+ "(0x%x) don't match\n",
+ ahci_nr_ports(cap), port_map);
+ } else {
+ /* fabricate port_map from cap.nr_ports */
+ port_map = (1 << ahci_nr_ports(cap)) - 1;
+ }
+
+ /* record values to use during operation */
+ hpriv->cap = cap;
+ hpriv->port_map = port_map;
+}
+
+/**
+ * ahci_restore_initial_config - Restore initial config
+ * @host: target ATA host
+ *
+ * Restore initial config stored by ahci_save_initial_config().
+ *
+ * LOCKING:
+ * None.
+ */
+static void ahci_restore_initial_config(struct ata_host *host)
+{
+ struct ahci_host_priv *hpriv = host->private_data;
+ void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
+
+ writel(hpriv->saved_cap, mmio + HOST_CAP);
+ writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL);
+ (void) readl(mmio + HOST_PORTS_IMPL); /* flush */
}
static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in)
@@ -511,8 +587,9 @@
writel(val, ap->ioaddr.scr_addr + (sc_reg * 4));
}
-static void ahci_start_engine(void __iomem *port_mmio)
+static void ahci_start_engine(struct ata_port *ap)
{
+ void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
/* start DMA */
@@ -522,8 +599,9 @@
readl(port_mmio + PORT_CMD); /* flush */
}
-static int ahci_stop_engine(void __iomem *port_mmio)
+static int ahci_stop_engine(struct ata_port *ap)
{
+ void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
tmp = readl(port_mmio + PORT_CMD);
@@ -545,19 +623,23 @@
return 0;
}
-static void ahci_start_fis_rx(void __iomem *port_mmio, u32 cap,
- dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma)
+static void ahci_start_fis_rx(struct ata_port *ap)
{
+ void __iomem *port_mmio = ahci_port_base(ap);
+ struct ahci_host_priv *hpriv = ap->host->private_data;
+ struct ahci_port_priv *pp = ap->private_data;
u32 tmp;
/* set FIS registers */
- if (cap & HOST_CAP_64)
- writel((cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
- writel(cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
+ if (hpriv->cap & HOST_CAP_64)
+ writel((pp->cmd_slot_dma >> 16) >> 16,
+ port_mmio + PORT_LST_ADDR_HI);
+ writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
- if (cap & HOST_CAP_64)
- writel((rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI);
- writel(rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
+ if (hpriv->cap & HOST_CAP_64)
+ writel((pp->rx_fis_dma >> 16) >> 16,
+ port_mmio + PORT_FIS_ADDR_HI);
+ writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
/* enable FIS reception */
tmp = readl(port_mmio + PORT_CMD);
@@ -568,8 +650,9 @@
readl(port_mmio + PORT_CMD);
}
-static int ahci_stop_fis_rx(void __iomem *port_mmio)
+static int ahci_stop_fis_rx(struct ata_port *ap)
{
+ void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
/* disable FIS reception */
@@ -586,14 +669,16 @@
return 0;
}
-static void ahci_power_up(void __iomem *port_mmio, u32 cap)
+static void ahci_power_up(struct ata_port *ap)
{
+ struct ahci_host_priv *hpriv = ap->host->private_data;
+ void __iomem *port_mmio = ahci_port_base(ap);
u32 cmd;
cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
/* spin up device */
- if (cap & HOST_CAP_SSS) {
+ if (hpriv->cap & HOST_CAP_SSS) {
cmd |= PORT_CMD_SPIN_UP;
writel(cmd, port_mmio + PORT_CMD);
}
@@ -603,11 +688,13 @@
}
#ifdef CONFIG_PM
-static void ahci_power_down(void __iomem *port_mmio, u32 cap)
+static void ahci_power_down(struct ata_port *ap)
{
+ struct ahci_host_priv *hpriv = ap->host->private_data;
+ void __iomem *port_mmio = ahci_port_base(ap);
u32 cmd, scontrol;
- if (!(cap & HOST_CAP_SSS))
+ if (!(hpriv->cap & HOST_CAP_SSS))
return;
/* put device into listen mode, first set PxSCTL.DET to 0 */
@@ -622,29 +709,28 @@
}
#endif
-static void ahci_init_port(void __iomem *port_mmio, u32 cap,
- dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma)
+static void ahci_init_port(struct ata_port *ap)
{
/* enable FIS reception */
- ahci_start_fis_rx(port_mmio, cap, cmd_slot_dma, rx_fis_dma);
+ ahci_start_fis_rx(ap);
/* enable DMA */
- ahci_start_engine(port_mmio);
+ ahci_start_engine(ap);
}
-static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg)
+static int ahci_deinit_port(struct ata_port *ap, const char **emsg)
{
int rc;
/* disable DMA */
- rc = ahci_stop_engine(port_mmio);
+ rc = ahci_stop_engine(ap);
if (rc) {
*emsg = "failed to stop engine";
return rc;
}
/* disable FIS reception */
- rc = ahci_stop_fis_rx(port_mmio);
+ rc = ahci_stop_fis_rx(ap);
if (rc) {
*emsg = "failed stop FIS RX";
return rc;
@@ -653,12 +739,11 @@
return 0;
}
-static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
+static int ahci_reset_controller(struct ata_host *host)
{
- u32 cap_save, impl_save, tmp;
-
- cap_save = readl(mmio + HOST_CAP);
- impl_save = readl(mmio + HOST_PORTS_IMPL);
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
+ u32 tmp;
/* global controller reset */
tmp = readl(mmio + HOST_CTL);
@@ -674,7 +759,7 @@
tmp = readl(mmio + HOST_CTL);
if (tmp & HOST_RESET) {
- dev_printk(KERN_ERR, &pdev->dev,
+ dev_printk(KERN_ERR, host->dev,
"controller reset failed (0x%x)\n", tmp);
return -EIO;
}
@@ -683,18 +768,8 @@
writel(HOST_AHCI_EN, mmio + HOST_CTL);
(void) readl(mmio + HOST_CTL); /* flush */
- /* These write-once registers are normally cleared on reset.
- * Restore BIOS values... which we HOPE were present before
- * reset.
- */
- if (!impl_save) {
- impl_save = (1 << ahci_nr_ports(cap_save)) - 1;
- dev_printk(KERN_WARNING, &pdev->dev,
- "PORTS_IMPL is zero, forcing 0x%x\n", impl_save);
- }
- writel(cap_save, mmio + HOST_CAP);
- writel(impl_save, mmio + HOST_PORTS_IMPL);
- (void) readl(mmio + HOST_PORTS_IMPL); /* flush */
+ /* some registers might be cleared on reset. restore initial values */
+ ahci_restore_initial_config(host);
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
u16 tmp16;
@@ -708,23 +783,23 @@
return 0;
}
-static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev,
- int n_ports, unsigned int port_flags,
- struct ahci_host_priv *hpriv)
+static void ahci_init_controller(struct ata_host *host)
{
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
int i, rc;
u32 tmp;
- for (i = 0; i < n_ports; i++) {
- void __iomem *port_mmio = ahci_port_base(mmio, i);
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+ void __iomem *port_mmio = ahci_port_base(ap);
const char *emsg = NULL;
- if ((port_flags & AHCI_FLAG_HONOR_PI) &&
- !(hpriv->port_map & (1 << i)))
+ if (ata_port_is_dummy(ap))
continue;
/* make sure port is not active */
- rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
+ rc = ahci_deinit_port(ap, &emsg);
if (rc)
dev_printk(KERN_WARNING, &pdev->dev,
"%s (%d)\n", emsg, rc);
@@ -752,7 +827,7 @@
static unsigned int ahci_dev_classify(struct ata_port *ap)
{
- void __iomem *port_mmio = ap->ioaddr.cmd_addr;
+ void __iomem *port_mmio = ahci_port_base(ap);
struct ata_taskfile tf;
u32 tmp;
@@ -802,8 +877,7 @@
static int ahci_softreset(struct ata_port *ap, unsigned int *class)
{
struct ahci_port_priv *pp = ap->private_data;
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+ void __iomem *port_mmio = ahci_port_base(ap);
const u32 cmd_fis_len = 5; /* five dwords */
const char *reason = NULL;
struct ata_taskfile tf;
@@ -820,7 +894,7 @@
}
/* prepare for SRST (AHCI-1.1 10.4.1) */
- rc = ahci_stop_engine(port_mmio);
+ rc = ahci_stop_engine(ap);
if (rc) {
reason = "failed to stop engine";
goto fail_restart;
@@ -840,7 +914,7 @@
}
/* restart engine */
- ahci_start_engine(port_mmio);
+ ahci_start_engine(ap);
ata_tf_init(ap->device, &tf);
fis = pp->cmd_tbl;
@@ -899,7 +973,7 @@
return 0;
fail_restart:
- ahci_start_engine(port_mmio);
+ ahci_start_engine(ap);
fail:
ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason);
return rc;
@@ -910,13 +984,11 @@
struct ahci_port_priv *pp = ap->private_data;
u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
struct ata_taskfile tf;
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
int rc;
DPRINTK("ENTER\n");
- ahci_stop_engine(port_mmio);
+ ahci_stop_engine(ap);
/* clear D2H reception area to properly wait for D2H FIS */
ata_tf_init(ap->device, &tf);
@@ -925,7 +997,7 @@
rc = sata_std_hardreset(ap, class);
- ahci_start_engine(port_mmio);
+ ahci_start_engine(ap);
if (rc == 0 && ata_port_online(ap))
*class = ahci_dev_classify(ap);
@@ -938,20 +1010,18 @@
static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class)
{
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
int rc;
DPRINTK("ENTER\n");
- ahci_stop_engine(port_mmio);
+ ahci_stop_engine(ap);
rc = sata_port_hardreset(ap, sata_ehc_deb_timing(&ap->eh_context));
/* vt8251 needs SError cleared for the port to operate */
ahci_scr_write(ap, SCR_ERROR, ahci_scr_read(ap, SCR_ERROR));
- ahci_start_engine(port_mmio);
+ ahci_start_engine(ap);
DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
@@ -963,7 +1033,7 @@
static void ahci_postreset(struct ata_port *ap, unsigned int *class)
{
- void __iomem *port_mmio = ap->ioaddr.cmd_addr;
+ void __iomem *port_mmio = ahci_port_base(ap);
u32 new_tmp, tmp;
ata_std_postreset(ap, class);
@@ -1131,8 +1201,7 @@
static void ahci_host_intr(struct ata_port *ap)
{
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+ void __iomem *port_mmio = ap->ioaddr.cmd_addr;
struct ata_eh_info *ehi = &ap->eh_info;
struct ahci_port_priv *pp = ap->private_data;
u32 status, qc_active;
@@ -1283,7 +1352,7 @@
static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
- void __iomem *port_mmio = ap->ioaddr.cmd_addr;
+ void __iomem *port_mmio = ahci_port_base(ap);
if (qc->tf.protocol == ATA_PROT_NCQ)
writel(1 << qc->tag, port_mmio + PORT_SCR_ACT);
@@ -1295,8 +1364,7 @@
static void ahci_freeze(struct ata_port *ap)
{
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+ void __iomem *port_mmio = ahci_port_base(ap);
/* turn IRQ off */
writel(0, port_mmio + PORT_IRQ_MASK);
@@ -1305,7 +1373,7 @@
static void ahci_thaw(struct ata_port *ap)
{
void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+ void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
/* clear IRQ */
@@ -1319,13 +1387,10 @@
static void ahci_error_handler(struct ata_port *ap)
{
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-
if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
/* restart engine */
- ahci_stop_engine(port_mmio);
- ahci_start_engine(port_mmio);
+ ahci_stop_engine(ap);
+ ahci_start_engine(ap);
}
/* perform recovery */
@@ -1335,13 +1400,10 @@
static void ahci_vt8251_error_handler(struct ata_port *ap)
{
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-
if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
/* restart engine */
- ahci_stop_engine(port_mmio);
- ahci_start_engine(port_mmio);
+ ahci_stop_engine(ap);
+ ahci_start_engine(ap);
}
/* perform recovery */
@@ -1352,36 +1414,26 @@
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
- if (qc->flags & ATA_QCFLAG_FAILED)
- qc->err_mask |= AC_ERR_OTHER;
-
- if (qc->err_mask) {
+ if (qc->flags & ATA_QCFLAG_FAILED) {
/* make DMA engine forget about the failed command */
- ahci_stop_engine(port_mmio);
- ahci_start_engine(port_mmio);
+ ahci_stop_engine(ap);
+ ahci_start_engine(ap);
}
}
#ifdef CONFIG_PM
static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
{
- struct ahci_host_priv *hpriv = ap->host->private_data;
- struct ahci_port_priv *pp = ap->private_data;
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
const char *emsg = NULL;
int rc;
- rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
+ rc = ahci_deinit_port(ap, &emsg);
if (rc == 0)
- ahci_power_down(port_mmio, hpriv->cap);
+ ahci_power_down(ap);
else {
ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc);
- ahci_init_port(port_mmio, hpriv->cap,
- pp->cmd_slot_dma, pp->rx_fis_dma);
+ ahci_init_port(ap);
}
return rc;
@@ -1389,13 +1441,8 @@
static int ahci_port_resume(struct ata_port *ap)
{
- struct ahci_port_priv *pp = ap->private_data;
- struct ahci_host_priv *hpriv = ap->host->private_data;
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-
- ahci_power_up(port_mmio, hpriv->cap);
- ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma);
+ ahci_power_up(ap);
+ ahci_init_port(ap);
return 0;
}
@@ -1423,8 +1470,6 @@
static int ahci_pci_device_resume(struct pci_dev *pdev)
{
struct ata_host *host = dev_get_drvdata(&pdev->dev);
- struct ahci_host_priv *hpriv = host->private_data;
- void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
int rc;
rc = ata_pci_device_do_resume(pdev);
@@ -1432,12 +1477,11 @@
return rc;
if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
- rc = ahci_reset_controller(mmio, pdev);
+ rc = ahci_reset_controller(host);
if (rc)
return rc;
- ahci_init_controller(mmio, pdev, host->n_ports,
- host->ports[0]->flags, hpriv);
+ ahci_init_controller(host);
}
ata_host_resume(host);
@@ -1449,10 +1493,7 @@
static int ahci_port_start(struct ata_port *ap)
{
struct device *dev = ap->host->dev;
- struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp;
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
void *mem;
dma_addr_t mem_dma;
int rc;
@@ -1500,85 +1541,29 @@
ap->private_data = pp;
/* power up port */
- ahci_power_up(port_mmio, hpriv->cap);
+ ahci_power_up(ap);
/* initialize port */
- ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma);
+ ahci_init_port(ap);
return 0;
}
static void ahci_port_stop(struct ata_port *ap)
{
- struct ahci_host_priv *hpriv = ap->host->private_data;
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
- void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
const char *emsg = NULL;
int rc;
/* de-initialize port */
- rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg);
+ rc = ahci_deinit_port(ap, &emsg);
if (rc)
ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc);
}
-static void ahci_setup_port(struct ata_ioports *port, void __iomem *base,
- unsigned int port_idx)
+static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac)
{
- VPRINTK("ENTER, base==0x%lx, port_idx %u\n", base, port_idx);
- base = ahci_port_base(base, port_idx);
- VPRINTK("base now==0x%lx\n", base);
-
- port->cmd_addr = base;
- port->scr_addr = base + PORT_SCR;
-
- VPRINTK("EXIT\n");
-}
-
-static int ahci_host_init(struct ata_probe_ent *probe_ent)
-{
- struct ahci_host_priv *hpriv = probe_ent->private_data;
- struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
- void __iomem *mmio = probe_ent->iomap[AHCI_PCI_BAR];
- unsigned int i, cap_n_ports, using_dac;
int rc;
- rc = ahci_reset_controller(mmio, pdev);
- if (rc)
- return rc;
-
- hpriv->cap = readl(mmio + HOST_CAP);
- hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
- cap_n_ports = ahci_nr_ports(hpriv->cap);
-
- VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n",
- hpriv->cap, hpriv->port_map, cap_n_ports);
-
- if (probe_ent->port_flags & AHCI_FLAG_HONOR_PI) {
- unsigned int n_ports = cap_n_ports;
- u32 port_map = hpriv->port_map;
- int max_port = 0;
-
- for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) {
- if (port_map & (1 << i)) {
- n_ports--;
- port_map &= ~(1 << i);
- max_port = i;
- } else
- probe_ent->dummy_port_mask |= 1 << i;
- }
-
- if (n_ports || port_map)
- dev_printk(KERN_WARNING, &pdev->dev,
- "nr_ports (%u) and implemented port map "
- "(0x%x) don't match\n",
- cap_n_ports, hpriv->port_map);
-
- probe_ent->n_ports = max_port + 1;
- } else
- probe_ent->n_ports = cap_n_ports;
-
- using_dac = hpriv->cap & HOST_CAP_64;
if (using_dac &&
!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
@@ -1604,23 +1589,14 @@
return rc;
}
}
-
- for (i = 0; i < probe_ent->n_ports; i++)
- ahci_setup_port(&probe_ent->port[i], mmio, i);
-
- ahci_init_controller(mmio, pdev, probe_ent->n_ports,
- probe_ent->port_flags, hpriv);
-
- pci_set_master(pdev);
-
return 0;
}
-static void ahci_print_info(struct ata_probe_ent *probe_ent)
+static void ahci_print_info(struct ata_host *host)
{
- struct ahci_host_priv *hpriv = probe_ent->private_data;
- struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
- void __iomem *mmio = probe_ent->iomap[AHCI_PCI_BAR];
+ struct ahci_host_priv *hpriv = host->private_data;
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
u32 vers, cap, impl, speed;
const char *speed_s;
u16 cc;
@@ -1690,11 +1666,12 @@
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
- unsigned int board_idx = (unsigned int) ent->driver_data;
+ struct ata_port_info pi = ahci_port_info[ent->driver_data];
+ const struct ata_port_info *ppi[] = { &pi, NULL };
struct device *dev = &pdev->dev;
- struct ata_probe_ent *probe_ent;
struct ahci_host_priv *hpriv;
- int rc;
+ struct ata_host *host;
+ int i, rc;
VPRINTK("ENTER\n");
@@ -1703,6 +1680,7 @@
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ /* acquire resources */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
@@ -1716,44 +1694,49 @@
if (pci_enable_msi(pdev))
pci_intx(pdev, 1);
- probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
- if (probe_ent == NULL)
- return -ENOMEM;
-
- probe_ent->dev = pci_dev_to_dev(pdev);
- INIT_LIST_HEAD(&probe_ent->node);
-
hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
if (!hpriv)
return -ENOMEM;
- probe_ent->sht = ahci_port_info[board_idx].sht;
- probe_ent->port_flags = ahci_port_info[board_idx].flags;
- probe_ent->pio_mask = ahci_port_info[board_idx].pio_mask;
- probe_ent->udma_mask = ahci_port_info[board_idx].udma_mask;
- probe_ent->port_ops = ahci_port_info[board_idx].port_ops;
+ /* save initial config */
+ ahci_save_initial_config(pdev, &pi, hpriv);
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->iomap = pcim_iomap_table(pdev);
- probe_ent->private_data = hpriv;
+ /* prepare host */
+ if (!(pi.flags & AHCI_FLAG_NO_NCQ) && (hpriv->cap & HOST_CAP_NCQ))
+ pi.flags |= ATA_FLAG_NCQ;
+
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, fls(hpriv->port_map));
+ if (!host)
+ return -ENOMEM;
+ host->iomap = pcim_iomap_table(pdev);
+ host->private_data = hpriv;
+
+ for (i = 0; i < host->n_ports; i++) {
+ if (hpriv->port_map & (1 << i)) {
+ struct ata_port *ap = host->ports[i];
+ void __iomem *port_mmio = ahci_port_base(ap);
+
+ ap->ioaddr.cmd_addr = port_mmio;
+ ap->ioaddr.scr_addr = port_mmio + PORT_SCR;
+ } else
+ host->ports[i]->ops = &ata_dummy_port_ops;
+ }
/* initialize adapter */
- rc = ahci_host_init(probe_ent);
+ rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64);
if (rc)
return rc;
- if (!(probe_ent->port_flags & AHCI_FLAG_NO_NCQ) &&
- (hpriv->cap & HOST_CAP_NCQ))
- probe_ent->port_flags |= ATA_FLAG_NCQ;
+ rc = ahci_reset_controller(host);
+ if (rc)
+ return rc;
- ahci_print_info(probe_ent);
+ ahci_init_controller(host);
+ ahci_print_info(host);
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(dev, probe_ent);
- return 0;
+ pci_set_master(pdev);
+ return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
+ &ahci_sht);
}
static int __init ahci_init(void)
diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
index d8e7988..92a491d 100644
--- a/drivers/ata/ata_generic.c
+++ b/drivers/ata/ata_generic.c
@@ -33,35 +33,6 @@
*/
/**
- * generic_pre_reset - probe begin
- * @ap: ATA port
- *
- * Set up cable type and use generic probe init
- */
-
-static int generic_pre_reset(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_PATA80;
- return ata_std_prereset(ap);
-}
-
-
-/**
- * generic_error_handler - Probe specified port on PATA host controller
- * @ap: Port to probe
- * @classes:
- *
- * LOCKING:
- * None (inherited from caller).
- */
-
-
-static void generic_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, generic_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-/**
* generic_set_mode - mode setting
* @ap: interface to set up
* @unused: returned device on error
@@ -144,8 +115,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = generic_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_unknown,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index b952c58..55d306a 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -93,7 +93,7 @@
#include <linux/libata.h>
#define DRV_NAME "ata_piix"
-#define DRV_VERSION "2.10ac1"
+#define DRV_VERSION "2.11"
enum {
PIIX_IOCFG = 0x54, /* IDE I/O configuration register */
@@ -155,11 +155,11 @@
static int piix_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent);
static void piix_pata_error_handler(struct ata_port *ap);
-static void ich_pata_error_handler(struct ata_port *ap);
static void piix_sata_error_handler(struct ata_port *ap);
static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev);
static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev);
static void ich_set_dmamode (struct ata_port *ap, struct ata_device *adev);
+static int ich_pata_cable_detect(struct ata_port *ap);
static unsigned int in_module_init = 1;
@@ -305,6 +305,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = piix_pata_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -336,8 +337,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = ich_pata_error_handler,
+ .error_handler = piix_pata_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ich_pata_cable_detect,
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -580,12 +582,13 @@
/* devid, subvendor, subdev */
{ 0x27DF, 0x0005, 0x0280 }, /* ICH7 on Acer 5602WLMi */
{ 0x27DF, 0x1025, 0x0110 }, /* ICH7 on Acer 3682WLMi */
+ { 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */
/* end marker */
{ 0, }
};
/**
- * piix_pata_cbl_detect - Probe host controller cable detect info
+ * ich_pata_cable_detect - Probe host controller cable detect info
* @ap: Port for which cable detect info is desired
*
* Read 80c cable indicator from ATA PCI device's PCI config
@@ -595,23 +598,18 @@
* None (inherited from caller).
*/
-static void ich_pata_cbl_detect(struct ata_port *ap)
+static int ich_pata_cable_detect(struct ata_port *ap)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
const struct ich_laptop *lap = &ich_laptop[0];
u8 tmp, mask;
- /* no 80c support in host controller? */
- if ((ap->udma_mask & ~ATA_UDMA_MASK_40C) == 0)
- goto cbl40;
-
/* Check for specials - Acer Aspire 5602WLMi */
while (lap->device) {
if (lap->device == pdev->device &&
lap->subvendor == pdev->subsystem_vendor &&
lap->subdevice == pdev->subsystem_device) {
- ap->cbl = ATA_CBL_PATA40_SHORT;
- return;
+ return ATA_CBL_PATA40_SHORT;
}
lap++;
}
@@ -620,20 +618,14 @@
mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC;
pci_read_config_byte(pdev, PIIX_IOCFG, &tmp);
if ((tmp & mask) == 0)
- goto cbl40;
-
- ap->cbl = ATA_CBL_PATA80;
- return;
-
-cbl40:
- ap->cbl = ATA_CBL_PATA40;
+ return ATA_CBL_PATA40;
+ return ATA_CBL_PATA80;
}
/**
* piix_pata_prereset - prereset for PATA host controller
* @ap: Target port
*
- *
* LOCKING:
* None (inherited from caller).
*/
@@ -643,8 +635,6 @@
if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no]))
return -ENOENT;
-
- ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -655,30 +645,6 @@
}
-/**
- * ich_pata_prereset - prereset for PATA host controller
- * @ap: Target port
- *
- *
- * LOCKING:
- * None (inherited from caller).
- */
-static int ich_pata_prereset(struct ata_port *ap)
-{
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-
- if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no]))
- return -ENOENT;
- ich_pata_cbl_detect(ap);
- return ata_std_prereset(ap);
-}
-
-static void ich_pata_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, ich_pata_prereset, ata_std_softreset, NULL,
- ata_std_postreset);
-}
-
static void piix_sata_error_handler(struct ata_port *ap)
{
ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, NULL,
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 0abd72d..ca67484 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -72,7 +72,7 @@
static unsigned int ata_dev_set_xfermode(struct ata_device *dev);
static void ata_dev_xfermask(struct ata_device *dev);
-static unsigned int ata_print_id = 1;
+unsigned int ata_print_id = 1;
static struct workqueue_struct *ata_wq;
struct workqueue_struct *ata_aux_wq;
@@ -89,6 +89,10 @@
module_param_named(fua, libata_fua, int, 0444);
MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)");
+static int ata_ignore_hpa = 0;
+module_param_named(ignore_hpa, ata_ignore_hpa, int, 0644);
+MODULE_PARM_DESC(ignore_hpa, "Ignore HPA limit (0=keep BIOS limits, 1=ignore limits, using full disk)");
+
static int ata_probe_timeout = ATA_TMOUT_INTERNAL / HZ;
module_param(ata_probe_timeout, int, 0444);
MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)");
@@ -808,6 +812,205 @@
*p = '\0';
}
+static u64 ata_tf_to_lba48(struct ata_taskfile *tf)
+{
+ u64 sectors = 0;
+
+ sectors |= ((u64)(tf->hob_lbah & 0xff)) << 40;
+ sectors |= ((u64)(tf->hob_lbam & 0xff)) << 32;
+ sectors |= (tf->hob_lbal & 0xff) << 24;
+ sectors |= (tf->lbah & 0xff) << 16;
+ sectors |= (tf->lbam & 0xff) << 8;
+ sectors |= (tf->lbal & 0xff);
+
+ return ++sectors;
+}
+
+static u64 ata_tf_to_lba(struct ata_taskfile *tf)
+{
+ u64 sectors = 0;
+
+ sectors |= (tf->device & 0x0f) << 24;
+ sectors |= (tf->lbah & 0xff) << 16;
+ sectors |= (tf->lbam & 0xff) << 8;
+ sectors |= (tf->lbal & 0xff);
+
+ return ++sectors;
+}
+
+/**
+ * ata_read_native_max_address_ext - LBA48 native max query
+ * @dev: Device to query
+ *
+ * Perform an LBA48 size query upon the device in question. Return the
+ * actual LBA48 size or zero if the command fails.
+ */
+
+static u64 ata_read_native_max_address_ext(struct ata_device *dev)
+{
+ unsigned int err;
+ struct ata_taskfile tf;
+
+ ata_tf_init(dev, &tf);
+
+ tf.command = ATA_CMD_READ_NATIVE_MAX_EXT;
+ tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | ATA_TFLAG_ISADDR;
+ tf.protocol |= ATA_PROT_NODATA;
+ tf.device |= 0x40;
+
+ err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+ if (err)
+ return 0;
+
+ return ata_tf_to_lba48(&tf);
+}
+
+/**
+ * ata_read_native_max_address - LBA28 native max query
+ * @dev: Device to query
+ *
+ * Performa an LBA28 size query upon the device in question. Return the
+ * actual LBA28 size or zero if the command fails.
+ */
+
+static u64 ata_read_native_max_address(struct ata_device *dev)
+{
+ unsigned int err;
+ struct ata_taskfile tf;
+
+ ata_tf_init(dev, &tf);
+
+ tf.command = ATA_CMD_READ_NATIVE_MAX;
+ tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
+ tf.protocol |= ATA_PROT_NODATA;
+ tf.device |= 0x40;
+
+ err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+ if (err)
+ return 0;
+
+ return ata_tf_to_lba(&tf);
+}
+
+/**
+ * ata_set_native_max_address_ext - LBA48 native max set
+ * @dev: Device to query
+ *
+ * Perform an LBA48 size set max upon the device in question. Return the
+ * actual LBA48 size or zero if the command fails.
+ */
+
+static u64 ata_set_native_max_address_ext(struct ata_device *dev, u64 new_sectors)
+{
+ unsigned int err;
+ struct ata_taskfile tf;
+
+ new_sectors--;
+
+ ata_tf_init(dev, &tf);
+
+ tf.command = ATA_CMD_SET_MAX_EXT;
+ tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | ATA_TFLAG_ISADDR;
+ tf.protocol |= ATA_PROT_NODATA;
+ tf.device |= 0x40;
+
+ tf.lbal = (new_sectors >> 0) & 0xff;
+ tf.lbam = (new_sectors >> 8) & 0xff;
+ tf.lbah = (new_sectors >> 16) & 0xff;
+
+ tf.hob_lbal = (new_sectors >> 24) & 0xff;
+ tf.hob_lbam = (new_sectors >> 32) & 0xff;
+ tf.hob_lbah = (new_sectors >> 40) & 0xff;
+
+ err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+ if (err)
+ return 0;
+
+ return ata_tf_to_lba48(&tf);
+}
+
+/**
+ * ata_set_native_max_address - LBA28 native max set
+ * @dev: Device to query
+ *
+ * Perform an LBA28 size set max upon the device in question. Return the
+ * actual LBA28 size or zero if the command fails.
+ */
+
+static u64 ata_set_native_max_address(struct ata_device *dev, u64 new_sectors)
+{
+ unsigned int err;
+ struct ata_taskfile tf;
+
+ new_sectors--;
+
+ ata_tf_init(dev, &tf);
+
+ tf.command = ATA_CMD_SET_MAX;
+ tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
+ tf.protocol |= ATA_PROT_NODATA;
+
+ tf.lbal = (new_sectors >> 0) & 0xff;
+ tf.lbam = (new_sectors >> 8) & 0xff;
+ tf.lbah = (new_sectors >> 16) & 0xff;
+ tf.device |= ((new_sectors >> 24) & 0x0f) | 0x40;
+
+ err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+ if (err)
+ return 0;
+
+ return ata_tf_to_lba(&tf);
+}
+
+/**
+ * ata_hpa_resize - Resize a device with an HPA set
+ * @dev: Device to resize
+ *
+ * Read the size of an LBA28 or LBA48 disk with HPA features and resize
+ * it if required to the full size of the media. The caller must check
+ * the drive has the HPA feature set enabled.
+ */
+
+static u64 ata_hpa_resize(struct ata_device *dev)
+{
+ u64 sectors = dev->n_sectors;
+ u64 hpa_sectors;
+
+ if (ata_id_has_lba48(dev->id))
+ hpa_sectors = ata_read_native_max_address_ext(dev);
+ else
+ hpa_sectors = ata_read_native_max_address(dev);
+
+ /* if no hpa, both should be equal */
+ ata_dev_printk(dev, KERN_INFO, "%s 1: sectors = %lld, "
+ "hpa_sectors = %lld\n",
+ __FUNCTION__, (long long)sectors, (long long)hpa_sectors);
+
+ if (hpa_sectors > sectors) {
+ ata_dev_printk(dev, KERN_INFO,
+ "Host Protected Area detected:\n"
+ "\tcurrent size: %lld sectors\n"
+ "\tnative size: %lld sectors\n",
+ (long long)sectors, (long long)hpa_sectors);
+
+ if (ata_ignore_hpa) {
+ if (ata_id_has_lba48(dev->id))
+ hpa_sectors = ata_set_native_max_address_ext(dev, hpa_sectors);
+ else
+ hpa_sectors = ata_set_native_max_address(dev,
+ hpa_sectors);
+
+ if (hpa_sectors) {
+ ata_dev_printk(dev, KERN_INFO, "native size "
+ "increased to %lld sectors\n",
+ (long long)hpa_sectors);
+ return hpa_sectors;
+ }
+ }
+ }
+ return sectors;
+}
+
static u64 ata_id_n_sectors(const u16 *id)
{
if (ata_id_has_lba(id)) {
@@ -1270,12 +1473,16 @@
if (ap->ops->post_internal_cmd)
ap->ops->post_internal_cmd(qc);
- if ((qc->flags & ATA_QCFLAG_FAILED) && !qc->err_mask) {
- if (ata_msg_warn(ap))
- ata_dev_printk(dev, KERN_WARNING,
- "zero err_mask for failed "
- "internal command, assuming AC_ERR_OTHER\n");
- qc->err_mask |= AC_ERR_OTHER;
+ /* perform minimal error analysis */
+ if (qc->flags & ATA_QCFLAG_FAILED) {
+ if (qc->result_tf.command & (ATA_ERR | ATA_DF))
+ qc->err_mask |= AC_ERR_DEV;
+
+ if (!qc->err_mask)
+ qc->err_mask |= AC_ERR_OTHER;
+
+ if (qc->err_mask & ~AC_ERR_OTHER)
+ qc->err_mask &= ~AC_ERR_OTHER;
}
/* finish up */
@@ -1379,30 +1586,44 @@
* Check if the current speed of the device requires IORDY. Used
* by various controllers for chip configuration.
*/
-
+
unsigned int ata_pio_need_iordy(const struct ata_device *adev)
{
- int pio;
- int speed = adev->pio_mode - XFER_PIO_0;
-
- if (speed < 2)
+ /* Controller doesn't support IORDY. Probably a pointless check
+ as the caller should know this */
+ if (adev->ap->flags & ATA_FLAG_NO_IORDY)
return 0;
- if (speed > 2)
+ /* PIO3 and higher it is mandatory */
+ if (adev->pio_mode > XFER_PIO_2)
return 1;
+ /* We turn it on when possible */
+ if (ata_id_has_iordy(adev->id))
+ return 1;
+ return 0;
+}
+/**
+ * ata_pio_mask_no_iordy - Return the non IORDY mask
+ * @adev: ATA device
+ *
+ * Compute the highest mode possible if we are not using iordy. Return
+ * -1 if no iordy mode is available.
+ */
+
+static u32 ata_pio_mask_no_iordy(const struct ata_device *adev)
+{
/* If we have no drive specific rule, then PIO 2 is non IORDY */
-
if (adev->id[ATA_ID_FIELD_VALID] & 2) { /* EIDE */
- pio = adev->id[ATA_ID_EIDE_PIO];
+ u16 pio = adev->id[ATA_ID_EIDE_PIO];
/* Is the speed faster than the drive allows non IORDY ? */
if (pio) {
/* This is cycle times not frequency - watch the logic! */
if (pio > 240) /* PIO2 is 240nS per cycle */
- return 1;
- return 0;
+ return 3 << ATA_SHIFT_PIO;
+ return 7 << ATA_SHIFT_PIO;
}
}
- return 0;
+ return 3 << ATA_SHIFT_PIO;
}
/**
@@ -1431,13 +1652,13 @@
struct ata_taskfile tf;
unsigned int err_mask = 0;
const char *reason;
+ int tried_spinup = 0;
int rc;
if (ata_msg_ctl(ap))
ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __FUNCTION__);
ata_dev_select(ap, dev->devno, 1, 1); /* select device 0/1 */
-
retry:
ata_tf_init(dev, &tf);
@@ -1494,6 +1715,32 @@
goto err_out;
}
+ if (!tried_spinup && (id[2] == 0x37c8 || id[2] == 0x738c)) {
+ tried_spinup = 1;
+ /*
+ * Drive powered-up in standby mode, and requires a specific
+ * SET_FEATURES spin-up subcommand before it will accept
+ * anything other than the original IDENTIFY command.
+ */
+ ata_tf_init(dev, &tf);
+ tf.command = ATA_CMD_SET_FEATURES;
+ tf.feature = SETFEATURES_SPINUP;
+ tf.protocol = ATA_PROT_NODATA;
+ tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+ if (err_mask) {
+ rc = -EIO;
+ reason = "SPINUP failed";
+ goto err_out;
+ }
+ /*
+ * If the drive initially returned incomplete IDENTIFY info,
+ * we now must reissue the IDENTIFY command.
+ */
+ if (id[2] == 0x37c8)
+ goto retry;
+ }
+
if ((flags & ATA_READID_POSTRESET) && class == ATA_DEV_ATA) {
/*
* The exact sequence expected by certain pre-ATA4 drives is:
@@ -1560,20 +1807,6 @@
snprintf(desc, desc_sz, "NCQ (depth %d/%d)", hdepth, ddepth);
}
-static void ata_set_port_max_cmd_len(struct ata_port *ap)
-{
- int i;
-
- if (ap->scsi_host) {
- unsigned int len = 0;
-
- for (i = 0; i < ATA_MAX_DEVICES; i++)
- len = max(len, ap->device[i].cdb_len);
-
- ap->scsi_host->max_cmd_len = len;
- }
-}
-
/**
* ata_dev_configure - Configure the specified ATA/ATAPI device
* @dev: Target device to configure
@@ -1658,6 +1891,7 @@
snprintf(revbuf, 7, "ATA-%d", ata_id_major_version(id));
dev->n_sectors = ata_id_n_sectors(id);
+ dev->n_sectors_boot = dev->n_sectors;
/* SCSI only uses 4-char revisions, dump full 8 chars from ATA */
ata_id_c_string(dev->id, fwrevbuf, ATA_ID_FW_REV,
@@ -1684,6 +1918,9 @@
dev->flags |= ATA_DFLAG_FLUSH_EXT;
}
+ if (ata_id_hpa_enabled(dev->id))
+ dev->n_sectors = ata_hpa_resize(dev);
+
/* config NCQ */
ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc));
@@ -1773,8 +2010,6 @@
}
}
- ata_set_port_max_cmd_len(ap);
-
/* limit bridge transfers to udma5, 200 sectors */
if (ata_dev_knobble(dev)) {
if (ata_msg_drv(ap) && print_info)
@@ -1785,14 +2020,15 @@
}
if (ata_device_blacklisted(dev) & ATA_HORKAGE_MAX_SEC_128)
- dev->max_sectors = min(ATA_MAX_SECTORS_128, dev->max_sectors);
+ dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128,
+ dev->max_sectors);
/* limit ATAPI DMA to R/W commands only */
if (ata_device_blacklisted(dev) & ATA_HORKAGE_DMA_RW_ONLY)
dev->horkage |= ATA_HORKAGE_DMA_RW_ONLY;
if (ap->ops->dev_config)
- ap->ops->dev_config(ap, dev);
+ ap->ops->dev_config(dev);
if (ata_msg_probe(ap))
ata_dev_printk(dev, KERN_DEBUG, "%s: EXIT, drv_stat = 0x%x\n",
@@ -1807,6 +2043,56 @@
}
/**
+ * ata_cable_40wire - return 40 wire cable type
+ * @ap: port
+ *
+ * Helper method for drivers which want to hardwire 40 wire cable
+ * detection.
+ */
+
+int ata_cable_40wire(struct ata_port *ap)
+{
+ return ATA_CBL_PATA40;
+}
+
+/**
+ * ata_cable_80wire - return 80 wire cable type
+ * @ap: port
+ *
+ * Helper method for drivers which want to hardwire 80 wire cable
+ * detection.
+ */
+
+int ata_cable_80wire(struct ata_port *ap)
+{
+ return ATA_CBL_PATA80;
+}
+
+/**
+ * ata_cable_unknown - return unknown PATA cable.
+ * @ap: port
+ *
+ * Helper method for drivers which have no PATA cable detection.
+ */
+
+int ata_cable_unknown(struct ata_port *ap)
+{
+ return ATA_CBL_PATA_UNK;
+}
+
+/**
+ * ata_cable_sata - return SATA cable type
+ * @ap: port
+ *
+ * Helper method for drivers which have SATA cables
+ */
+
+int ata_cable_sata(struct ata_port *ap)
+{
+ return ATA_CBL_SATA;
+}
+
+/**
* ata_bus_probe - Reset and probe ATA bus
* @ap: Bus to probe
*
@@ -1876,6 +2162,10 @@
goto fail;
}
+ /* Now ask for the cable type as PDIAG- should have been released */
+ if (ap->ops->cable_detect)
+ ap->cbl = ap->ops->cable_detect(ap);
+
/* After the identify sequence we can now set up the devices. We do
this in the normal order so that the user doesn't get confused */
@@ -1958,7 +2248,7 @@
* LOCKING:
* None.
*/
-static void sata_print_link_status(struct ata_port *ap)
+void sata_print_link_status(struct ata_port *ap)
{
u32 sstatus, scontrol, tmp;
@@ -2352,6 +2642,12 @@
t->active += (t->cycle - (t->active + t->recover)) / 2;
t->recover = t->cycle - t->active;
}
+
+ /* In a few cases quantisation may produce enough errors to
+ leave t->cycle too low for the sum of active and recovery
+ if so we must correct this */
+ if (t->active + t->recover > t->cycle)
+ t->cycle = t->active + t->recover;
return 0;
}
@@ -2481,12 +2777,13 @@
}
/**
- * ata_set_mode - Program timings and issue SET FEATURES - XFER
+ * ata_do_set_mode - Program timings and issue SET FEATURES - XFER
* @ap: port on which timings will be programmed
* @r_failed_dev: out paramter for failed device
*
- * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If
- * ata_set_mode() fails, pointer to the failing device is
+ * Standard implementation of the function used to tune and set
+ * ATA device disk transfer mode (PIO3, UDMA6, etc.). If
+ * ata_dev_set_mode() fails, pointer to the failing device is
* returned in @r_failed_dev.
*
* LOCKING:
@@ -2495,14 +2792,12 @@
* RETURNS:
* 0 on success, negative errno otherwise
*/
-int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
+
+int ata_do_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
{
struct ata_device *dev;
int i, rc = 0, used_dma = 0, found = 0;
- /* has private set_mode? */
- if (ap->ops->set_mode)
- return ap->ops->set_mode(ap, r_failed_dev);
/* step 1: calculate xfer_mask */
for (i = 0; i < ATA_MAX_DEVICES; i++) {
@@ -2587,6 +2882,29 @@
}
/**
+ * ata_set_mode - Program timings and issue SET FEATURES - XFER
+ * @ap: port on which timings will be programmed
+ * @r_failed_dev: out paramter for failed device
+ *
+ * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If
+ * ata_set_mode() fails, pointer to the failing device is
+ * returned in @r_failed_dev.
+ *
+ * LOCKING:
+ * PCI/etc. bus probe sem.
+ *
+ * RETURNS:
+ * 0 on success, negative errno otherwise
+ */
+int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
+{
+ /* has private set_mode? */
+ if (ap->ops->set_mode)
+ return ap->ops->set_mode(ap, r_failed_dev);
+ return ata_do_set_mode(ap, r_failed_dev);
+}
+
+/**
* ata_tf_to_host - issue ATA taskfile to host controller
* @ap: port to which command is being issued
* @tf: ATA taskfile register set
@@ -3267,6 +3585,11 @@
"%llu != %llu\n",
(unsigned long long)dev->n_sectors,
(unsigned long long)new_n_sectors);
+ /* Are we the boot time size - if so we appear to be the
+ same disk at this point and our HPA got reapplied */
+ if (ata_ignore_hpa && dev->n_sectors_boot == new_n_sectors
+ && ata_id_hpa_enabled(new_id))
+ return 1;
return 0;
}
@@ -3441,19 +3764,7 @@
xfer_mask = ata_pack_xfermask(ap->pio_mask,
ap->mwdma_mask, ap->udma_mask);
- /* Apply cable rule here. Don't apply it early because when
- * we handle hot plug the cable type can itself change.
- */
- if (ap->cbl == ATA_CBL_PATA40)
- xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA);
- /* Apply drive side cable rule. Unknown or 80 pin cables reported
- * host side are checked drive side as well. Cases where we know a
- * 40wire cable is used safely for 80 are not checked here.
- */
- if (ata_drive_40wire(dev->id) && (ap->cbl == ATA_CBL_PATA_UNK || ap->cbl == ATA_CBL_PATA80))
- xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA);
-
-
+ /* drive modes available */
xfer_mask &= ata_pack_xfermask(dev->pio_mask,
dev->mwdma_mask, dev->udma_mask);
xfer_mask &= ata_id_xfermask(dev->id);
@@ -3482,8 +3793,30 @@
"other device, disabling DMA\n");
}
+ if (ap->flags & ATA_FLAG_NO_IORDY)
+ xfer_mask &= ata_pio_mask_no_iordy(dev);
+
if (ap->ops->mode_filter)
- xfer_mask = ap->ops->mode_filter(ap, dev, xfer_mask);
+ xfer_mask = ap->ops->mode_filter(dev, xfer_mask);
+
+ /* Apply cable rule here. Don't apply it early because when
+ * we handle hot plug the cable type can itself change.
+ * Check this last so that we know if the transfer rate was
+ * solely limited by the cable.
+ * Unknown or 80 wire cables reported host side are checked
+ * drive side as well. Cases where we know a 40wire cable
+ * is used safely for 80 are not checked here.
+ */
+ if (xfer_mask & (0xF8 << ATA_SHIFT_UDMA))
+ /* UDMA/44 or higher would be available */
+ if((ap->cbl == ATA_CBL_PATA40) ||
+ (ata_drive_40wire(dev->id) &&
+ (ap->cbl == ATA_CBL_PATA_UNK ||
+ ap->cbl == ATA_CBL_PATA80))) {
+ ata_dev_printk(dev, KERN_WARNING,
+ "limited to UDMA/33 due to 40-wire cable\n");
+ xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA);
+ }
ata_unpack_xfermask(xfer_mask, &dev->pio_mask,
&dev->mwdma_mask, &dev->udma_mask);
@@ -4022,10 +4355,10 @@
/**
- * ata_pio_sector - Transfer ATA_SECT_SIZE (512 bytes) of data.
+ * ata_pio_sector - Transfer a sector of data.
* @qc: Command on going
*
- * Transfer ATA_SECT_SIZE of data from/to the ATA device.
+ * Transfer qc->sect_size bytes of data from/to the ATA device.
*
* LOCKING:
* Inherited from caller.
@@ -4040,7 +4373,7 @@
unsigned int offset;
unsigned char *buf;
- if (qc->curbytes == qc->nbytes - ATA_SECT_SIZE)
+ if (qc->curbytes == qc->nbytes - qc->sect_size)
ap->hsm_task_state = HSM_ST_LAST;
page = sg[qc->cursg].page;
@@ -4060,17 +4393,17 @@
buf = kmap_atomic(page, KM_IRQ0);
/* do the actual data transfer */
- ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write);
+ ap->ops->data_xfer(qc->dev, buf + offset, qc->sect_size, do_write);
kunmap_atomic(buf, KM_IRQ0);
local_irq_restore(flags);
} else {
buf = page_address(page);
- ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write);
+ ap->ops->data_xfer(qc->dev, buf + offset, qc->sect_size, do_write);
}
- qc->curbytes += ATA_SECT_SIZE;
- qc->cursg_ofs += ATA_SECT_SIZE;
+ qc->curbytes += qc->sect_size;
+ qc->cursg_ofs += qc->sect_size;
if (qc->cursg_ofs == (&sg[qc->cursg])->length) {
qc->cursg++;
@@ -4079,10 +4412,10 @@
}
/**
- * ata_pio_sectors - Transfer one or many 512-byte sectors.
+ * ata_pio_sectors - Transfer one or many sectors.
* @qc: Command on going
*
- * Transfer one or many ATA_SECT_SIZE of data from/to the
+ * Transfer one or many sectors of data from/to the
* ATA device for the DRQ request.
*
* LOCKING:
@@ -4097,7 +4430,7 @@
WARN_ON(qc->dev->multi_count == 0);
- nsect = min((qc->nbytes - qc->curbytes) / ATA_SECT_SIZE,
+ nsect = min((qc->nbytes - qc->curbytes) / qc->sect_size,
qc->dev->multi_count);
while (nsect--)
ata_pio_sector(qc);
@@ -5577,42 +5910,35 @@
}
/**
- * ata_port_init - Initialize an ata_port structure
- * @ap: Structure to initialize
- * @host: Collection of hosts to which @ap belongs
- * @ent: Probe information provided by low-level driver
- * @port_no: Port number associated with this ata_port
+ * ata_port_alloc - allocate and initialize basic ATA port resources
+ * @host: ATA host this allocated port belongs to
*
- * Initialize a new ata_port structure.
+ * Allocate and initialize basic ATA port resources.
+ *
+ * RETURNS:
+ * Allocate ATA port on success, NULL on failure.
*
* LOCKING:
- * Inherited from caller.
+ * Inherited from calling layer (may sleep).
*/
-void ata_port_init(struct ata_port *ap, struct ata_host *host,
- const struct ata_probe_ent *ent, unsigned int port_no)
+struct ata_port *ata_port_alloc(struct ata_host *host)
{
+ struct ata_port *ap;
unsigned int i;
+ DPRINTK("ENTER\n");
+
+ ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+ if (!ap)
+ return NULL;
+
ap->lock = &host->lock;
ap->flags = ATA_FLAG_DISABLED;
- ap->print_id = ata_print_id++;
+ ap->print_id = -1;
ap->ctl = ATA_DEVCTL_OBS;
ap->host = host;
- ap->dev = ent->dev;
- ap->port_no = port_no;
- if (port_no == 1 && ent->pinfo2) {
- ap->pio_mask = ent->pinfo2->pio_mask;
- ap->mwdma_mask = ent->pinfo2->mwdma_mask;
- ap->udma_mask = ent->pinfo2->udma_mask;
- ap->flags |= ent->pinfo2->flags;
- ap->ops = ent->pinfo2->port_ops;
- } else {
- ap->pio_mask = ent->pio_mask;
- ap->mwdma_mask = ent->mwdma_mask;
- ap->udma_mask = ent->udma_mask;
- ap->flags |= ent->port_flags;
- ap->ops = ent->port_ops;
- }
+ ap->dev = host->dev;
+
ap->hw_sata_spd_limit = UINT_MAX;
ap->active_tag = ATA_TAG_POISON;
ap->last_ctl = 0xFF;
@@ -5632,10 +5958,7 @@
INIT_LIST_HEAD(&ap->eh_done_q);
init_waitqueue_head(&ap->eh_wait_q);
- /* set cable type */
ap->cbl = ATA_CBL_NONE;
- if (ap->flags & ATA_FLAG_SATA)
- ap->cbl = ATA_CBL_SATA;
for (i = 0; i < ATA_MAX_DEVICES; i++) {
struct ata_device *dev = &ap->device[i];
@@ -5648,72 +5971,6 @@
ap->stats.unhandled_irq = 1;
ap->stats.idle_irq = 1;
#endif
-
- memcpy(&ap->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports));
-}
-
-/**
- * ata_port_init_shost - Initialize SCSI host associated with ATA port
- * @ap: ATA port to initialize SCSI host for
- * @shost: SCSI host associated with @ap
- *
- * Initialize SCSI host @shost associated with ATA port @ap.
- *
- * LOCKING:
- * Inherited from caller.
- */
-static void ata_port_init_shost(struct ata_port *ap, struct Scsi_Host *shost)
-{
- ap->scsi_host = shost;
-
- shost->unique_id = ap->print_id;
- shost->max_id = 16;
- shost->max_lun = 1;
- shost->max_channel = 1;
- shost->max_cmd_len = 12;
-}
-
-/**
- * ata_port_add - Attach low-level ATA driver to system
- * @ent: Information provided by low-level driver
- * @host: Collections of ports to which we add
- * @port_no: Port number associated with this host
- *
- * Attach low-level ATA driver to system.
- *
- * LOCKING:
- * PCI/etc. bus probe sem.
- *
- * RETURNS:
- * New ata_port on success, for NULL on error.
- */
-static struct ata_port * ata_port_add(const struct ata_probe_ent *ent,
- struct ata_host *host,
- unsigned int port_no)
-{
- struct Scsi_Host *shost;
- struct ata_port *ap;
-
- DPRINTK("ENTER\n");
-
- if (!ent->port_ops->error_handler &&
- !(ent->port_flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST))) {
- printk(KERN_ERR "ata%u: no reset mechanism available\n",
- port_no);
- return NULL;
- }
-
- shost = scsi_host_alloc(ent->sht, sizeof(struct ata_port));
- if (!shost)
- return NULL;
-
- shost->transportt = &ata_scsi_transport_template;
-
- ap = ata_shost_to_port(shost);
-
- ata_port_init(ap, host, ent, port_no);
- ata_port_init_shost(ap, shost);
-
return ap;
}
@@ -5725,19 +5982,26 @@
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
- if (ap && ap->ops->port_stop)
+ if (!ap)
+ continue;
+
+ if ((host->flags & ATA_HOST_STARTED) && ap->ops->port_stop)
ap->ops->port_stop(ap);
}
- if (host->ops->host_stop)
+ if ((host->flags & ATA_HOST_STARTED) && host->ops->host_stop)
host->ops->host_stop(host);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
- if (ap)
+ if (!ap)
+ continue;
+
+ if (ap->scsi_host)
scsi_host_put(ap->scsi_host);
+ kfree(ap);
host->ports[i] = NULL;
}
@@ -5745,6 +6009,174 @@
}
/**
+ * ata_host_alloc - allocate and init basic ATA host resources
+ * @dev: generic device this host is associated with
+ * @max_ports: maximum number of ATA ports associated with this host
+ *
+ * Allocate and initialize basic ATA host resources. LLD calls
+ * this function to allocate a host, initializes it fully and
+ * attaches it using ata_host_register().
+ *
+ * @max_ports ports are allocated and host->n_ports is
+ * initialized to @max_ports. The caller is allowed to decrease
+ * host->n_ports before calling ata_host_register(). The unused
+ * ports will be automatically freed on registration.
+ *
+ * RETURNS:
+ * Allocate ATA host on success, NULL on failure.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ */
+struct ata_host *ata_host_alloc(struct device *dev, int max_ports)
+{
+ struct ata_host *host;
+ size_t sz;
+ int i;
+
+ DPRINTK("ENTER\n");
+
+ if (!devres_open_group(dev, NULL, GFP_KERNEL))
+ return NULL;
+
+ /* alloc a container for our list of ATA ports (buses) */
+ sz = sizeof(struct ata_host) + (max_ports + 1) * sizeof(void *);
+ /* alloc a container for our list of ATA ports (buses) */
+ host = devres_alloc(ata_host_release, sz, GFP_KERNEL);
+ if (!host)
+ goto err_out;
+
+ devres_add(dev, host);
+ dev_set_drvdata(dev, host);
+
+ spin_lock_init(&host->lock);
+ host->dev = dev;
+ host->n_ports = max_ports;
+
+ /* allocate ports bound to this host */
+ for (i = 0; i < max_ports; i++) {
+ struct ata_port *ap;
+
+ ap = ata_port_alloc(host);
+ if (!ap)
+ goto err_out;
+
+ ap->port_no = i;
+ host->ports[i] = ap;
+ }
+
+ devres_remove_group(dev, NULL);
+ return host;
+
+ err_out:
+ devres_release_group(dev, NULL);
+ return NULL;
+}
+
+/**
+ * ata_host_alloc_pinfo - alloc host and init with port_info array
+ * @dev: generic device this host is associated with
+ * @ppi: array of ATA port_info to initialize host with
+ * @n_ports: number of ATA ports attached to this host
+ *
+ * Allocate ATA host and initialize with info from @ppi. If NULL
+ * terminated, @ppi may contain fewer entries than @n_ports. The
+ * last entry will be used for the remaining ports.
+ *
+ * RETURNS:
+ * Allocate ATA host on success, NULL on failure.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ */
+struct ata_host *ata_host_alloc_pinfo(struct device *dev,
+ const struct ata_port_info * const * ppi,
+ int n_ports)
+{
+ const struct ata_port_info *pi;
+ struct ata_host *host;
+ int i, j;
+
+ host = ata_host_alloc(dev, n_ports);
+ if (!host)
+ return NULL;
+
+ for (i = 0, j = 0, pi = NULL; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+
+ if (ppi[j])
+ pi = ppi[j++];
+
+ ap->pio_mask = pi->pio_mask;
+ ap->mwdma_mask = pi->mwdma_mask;
+ ap->udma_mask = pi->udma_mask;
+ ap->flags |= pi->flags;
+ ap->ops = pi->port_ops;
+
+ if (!host->ops && (pi->port_ops != &ata_dummy_port_ops))
+ host->ops = pi->port_ops;
+ if (!host->private_data && pi->private_data)
+ host->private_data = pi->private_data;
+ }
+
+ return host;
+}
+
+/**
+ * ata_host_start - start and freeze ports of an ATA host
+ * @host: ATA host to start ports for
+ *
+ * Start and then freeze ports of @host. Started status is
+ * recorded in host->flags, so this function can be called
+ * multiple times. Ports are guaranteed to get started only
+ * once. If host->ops isn't initialized yet, its set to the
+ * first non-dummy port ops.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ *
+ * RETURNS:
+ * 0 if all ports are started successfully, -errno otherwise.
+ */
+int ata_host_start(struct ata_host *host)
+{
+ int i, rc;
+
+ if (host->flags & ATA_HOST_STARTED)
+ return 0;
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+
+ if (!host->ops && !ata_port_is_dummy(ap))
+ host->ops = ap->ops;
+
+ if (ap->ops->port_start) {
+ rc = ap->ops->port_start(ap);
+ if (rc) {
+ ata_port_printk(ap, KERN_ERR, "failed to "
+ "start port (errno=%d)\n", rc);
+ goto err_out;
+ }
+ }
+
+ ata_eh_freeze_port(ap);
+ }
+
+ host->flags |= ATA_HOST_STARTED;
+ return 0;
+
+ err_out:
+ while (--i >= 0) {
+ struct ata_port *ap = host->ports[i];
+
+ if (ap->ops->port_stop)
+ ap->ops->port_stop(ap);
+ }
+ return rc;
+}
+
+/**
* ata_sas_host_init - Initialize a host struct
* @host: host to initialize
* @dev: device host is attached to
@@ -5755,7 +6187,7 @@
* PCI/etc. bus probe sem.
*
*/
-
+/* KILLME - the only user left is ipr */
void ata_host_init(struct ata_host *host, struct device *dev,
unsigned long flags, const struct ata_port_operations *ops)
{
@@ -5766,137 +6198,58 @@
}
/**
- * ata_device_add - Register hardware device with ATA and SCSI layers
- * @ent: Probe information describing hardware device to be registered
+ * ata_host_register - register initialized ATA host
+ * @host: ATA host to register
+ * @sht: template for SCSI host
*
- * This function processes the information provided in the probe
- * information struct @ent, allocates the necessary ATA and SCSI
- * host information structures, initializes them, and registers
- * everything with requisite kernel subsystems.
- *
- * This function requests irqs, probes the ATA bus, and probes
- * the SCSI bus.
+ * Register initialized ATA host. @host is allocated using
+ * ata_host_alloc() and fully initialized by LLD. This function
+ * starts ports, registers @host with ATA and SCSI layers and
+ * probe registered devices.
*
* LOCKING:
- * PCI/etc. bus probe sem.
+ * Inherited from calling layer (may sleep).
*
* RETURNS:
- * Number of ports registered. Zero on error (no ports registered).
+ * 0 on success, -errno otherwise.
*/
-int ata_device_add(const struct ata_probe_ent *ent)
+int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
{
- unsigned int i;
- struct device *dev = ent->dev;
- struct ata_host *host;
- int rc;
+ int i, rc;
- DPRINTK("ENTER\n");
-
- if (ent->irq == 0) {
- dev_printk(KERN_ERR, dev, "is not available: No interrupt assigned.\n");
- return 0;
+ /* host must have been started */
+ if (!(host->flags & ATA_HOST_STARTED)) {
+ dev_printk(KERN_ERR, host->dev,
+ "BUG: trying to register unstarted host\n");
+ WARN_ON(1);
+ return -EINVAL;
}
- if (!devres_open_group(dev, ata_device_add, GFP_KERNEL))
- return 0;
+ /* Blow away unused ports. This happens when LLD can't
+ * determine the exact number of ports to allocate at
+ * allocation time.
+ */
+ for (i = host->n_ports; host->ports[i]; i++)
+ kfree(host->ports[i]);
- /* alloc a container for our list of ATA ports (buses) */
- host = devres_alloc(ata_host_release, sizeof(struct ata_host) +
- (ent->n_ports * sizeof(void *)), GFP_KERNEL);
- if (!host)
- goto err_out;
- devres_add(dev, host);
- dev_set_drvdata(dev, host);
+ /* give ports names and add SCSI hosts */
+ for (i = 0; i < host->n_ports; i++)
+ host->ports[i]->print_id = ata_print_id++;
- ata_host_init(host, dev, ent->_host_flags, ent->port_ops);
- host->n_ports = ent->n_ports;
- host->irq = ent->irq;
- host->irq2 = ent->irq2;
- host->iomap = ent->iomap;
- host->private_data = ent->private_data;
+ rc = ata_scsi_add_hosts(host, sht);
+ if (rc)
+ return rc;
- /* register each port bound to this device */
- for (i = 0; i < host->n_ports; i++) {
- struct ata_port *ap;
- unsigned long xfer_mode_mask;
- int irq_line = ent->irq;
-
- ap = ata_port_add(ent, host, i);
- host->ports[i] = ap;
- if (!ap)
- goto err_out;
-
- /* dummy? */
- if (ent->dummy_port_mask & (1 << i)) {
- ata_port_printk(ap, KERN_INFO, "DUMMY\n");
- ap->ops = &ata_dummy_port_ops;
- continue;
- }
-
- /* start port */
- rc = ap->ops->port_start(ap);
- if (rc) {
- host->ports[i] = NULL;
- scsi_host_put(ap->scsi_host);
- goto err_out;
- }
-
- /* Report the secondary IRQ for second channel legacy */
- if (i == 1 && ent->irq2)
- irq_line = ent->irq2;
-
- xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) |
- (ap->mwdma_mask << ATA_SHIFT_MWDMA) |
- (ap->pio_mask << ATA_SHIFT_PIO);
-
- /* print per-port info to dmesg */
- ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%p "
- "ctl 0x%p bmdma 0x%p irq %d\n",
- ap->flags & ATA_FLAG_SATA ? 'S' : 'P',
- ata_mode_string(xfer_mode_mask),
- ap->ioaddr.cmd_addr,
- ap->ioaddr.ctl_addr,
- ap->ioaddr.bmdma_addr,
- irq_line);
-
- /* freeze port before requesting IRQ */
- ata_eh_freeze_port(ap);
- }
-
- /* obtain irq, that may be shared between channels */
- rc = devm_request_irq(dev, ent->irq, ent->port_ops->irq_handler,
- ent->irq_flags, DRV_NAME, host);
- if (rc) {
- dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n",
- ent->irq, rc);
- goto err_out;
- }
-
- /* do we have a second IRQ for the other channel, eg legacy mode */
- if (ent->irq2) {
- /* We will get weird core code crashes later if this is true
- so trap it now */
- BUG_ON(ent->irq == ent->irq2);
-
- rc = devm_request_irq(dev, ent->irq2,
- ent->port_ops->irq_handler, ent->irq_flags,
- DRV_NAME, host);
- if (rc) {
- dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n",
- ent->irq2, rc);
- goto err_out;
- }
- }
-
- /* resource acquisition complete */
- devres_remove_group(dev, ata_device_add);
-
- /* perform each probe synchronously */
- DPRINTK("probe begin\n");
+ /* set cable, sata_spd_limit and report */
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
+ int irq_line;
u32 scontrol;
- int rc;
+ unsigned long xfer_mask;
+
+ /* set SATA cable type if still unset */
+ if (ap->cbl == ATA_CBL_NONE && (ap->flags & ATA_FLAG_SATA))
+ ap->cbl = ATA_CBL_SATA;
/* init sata_spd_limit to the current value */
if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) {
@@ -5905,16 +6258,35 @@
}
ap->sata_spd_limit = ap->hw_sata_spd_limit;
- rc = scsi_add_host(ap->scsi_host, dev);
- if (rc) {
- ata_port_printk(ap, KERN_ERR, "scsi_add_host failed\n");
- /* FIXME: do something useful here */
- /* FIXME: handle unconditional calls to
- * scsi_scan_host and ata_host_remove, below,
- * at the very least
- */
- }
+ /* report the secondary IRQ for second channel legacy */
+ irq_line = host->irq;
+ if (i == 1 && host->irq2)
+ irq_line = host->irq2;
+ xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask,
+ ap->udma_mask);
+
+ /* print per-port info to dmesg */
+ if (!ata_port_is_dummy(ap))
+ ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%p "
+ "ctl 0x%p bmdma 0x%p irq %d\n",
+ ap->cbl == ATA_CBL_SATA ? 'S' : 'P',
+ ata_mode_string(xfer_mask),
+ ap->ioaddr.cmd_addr,
+ ap->ioaddr.ctl_addr,
+ ap->ioaddr.bmdma_addr,
+ irq_line);
+ else
+ ata_port_printk(ap, KERN_INFO, "DUMMY\n");
+ }
+
+ /* perform each probe synchronously */
+ DPRINTK("probe begin\n");
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+ int rc;
+
+ /* probe */
if (ap->ops->error_handler) {
struct ata_eh_info *ehi = &ap->eh_info;
unsigned long flags;
@@ -5959,16 +6331,52 @@
ata_scsi_scan_host(ap);
}
- VPRINTK("EXIT, returning %u\n", ent->n_ports);
- return ent->n_ports; /* success */
-
- err_out:
- devres_release_group(dev, ata_device_add);
- VPRINTK("EXIT, returning %d\n", rc);
return 0;
}
/**
+ * ata_host_activate - start host, request IRQ and register it
+ * @host: target ATA host
+ * @irq: IRQ to request
+ * @irq_handler: irq_handler used when requesting IRQ
+ * @irq_flags: irq_flags used when requesting IRQ
+ * @sht: scsi_host_template to use when registering the host
+ *
+ * After allocating an ATA host and initializing it, most libata
+ * LLDs perform three steps to activate the host - start host,
+ * request IRQ and register it. This helper takes necessasry
+ * arguments and performs the three steps in one go.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+int ata_host_activate(struct ata_host *host, int irq,
+ irq_handler_t irq_handler, unsigned long irq_flags,
+ struct scsi_host_template *sht)
+{
+ int rc;
+
+ rc = ata_host_start(host);
+ if (rc)
+ return rc;
+
+ rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags,
+ dev_driver_string(host->dev), host);
+ if (rc)
+ return rc;
+
+ rc = ata_host_register(host, sht);
+ /* if failed, just free the IRQ and leave ports alone */
+ if (rc)
+ devm_free_irq(host->dev, irq, host);
+
+ return rc;
+}
+
+/**
* ata_port_detach - Detach ATA port in prepration of device removal
* @ap: ATA port to be detached
*
@@ -6043,32 +6451,6 @@
ata_port_detach(host->ports[i]);
}
-struct ata_probe_ent *
-ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port)
-{
- struct ata_probe_ent *probe_ent;
-
- probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
- if (!probe_ent) {
- printk(KERN_ERR DRV_NAME "(%s): out of memory\n",
- kobject_name(&(dev->kobj)));
- return NULL;
- }
-
- INIT_LIST_HEAD(&probe_ent->node);
- probe_ent->dev = dev;
-
- probe_ent->sht = port->sht;
- probe_ent->port_flags = port->flags;
- probe_ent->pio_mask = port->pio_mask;
- probe_ent->mwdma_mask = port->mwdma_mask;
- probe_ent->udma_mask = port->udma_mask;
- probe_ent->port_ops = port->port_ops;
- probe_ent->private_data = port->private_data;
-
- return probe_ent;
-}
-
/**
* ata_std_ports - initialize ioaddr with standard port offsets.
* @ioaddr: IO address structure to be initialized
@@ -6334,6 +6716,10 @@
.port_stop = ata_dummy_noret,
};
+const struct ata_port_info ata_dummy_port_info = {
+ .port_ops = &ata_dummy_port_ops,
+};
+
/*
* libata is essentially a library of internal helper functions for
* low-level ATA host controller drivers. As such, the API/ABI is
@@ -6345,10 +6731,15 @@
EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug);
EXPORT_SYMBOL_GPL(sata_deb_timing_long);
EXPORT_SYMBOL_GPL(ata_dummy_port_ops);
+EXPORT_SYMBOL_GPL(ata_dummy_port_info);
EXPORT_SYMBOL_GPL(ata_std_bios_param);
EXPORT_SYMBOL_GPL(ata_std_ports);
EXPORT_SYMBOL_GPL(ata_host_init);
-EXPORT_SYMBOL_GPL(ata_device_add);
+EXPORT_SYMBOL_GPL(ata_host_alloc);
+EXPORT_SYMBOL_GPL(ata_host_alloc_pinfo);
+EXPORT_SYMBOL_GPL(ata_host_start);
+EXPORT_SYMBOL_GPL(ata_host_register);
+EXPORT_SYMBOL_GPL(ata_host_activate);
EXPORT_SYMBOL_GPL(ata_host_detach);
EXPORT_SYMBOL_GPL(ata_sg_init);
EXPORT_SYMBOL_GPL(ata_sg_init_one);
@@ -6360,6 +6751,7 @@
EXPORT_SYMBOL_GPL(ata_tf_read);
EXPORT_SYMBOL_GPL(ata_noop_dev_select);
EXPORT_SYMBOL_GPL(ata_std_dev_select);
+EXPORT_SYMBOL_GPL(sata_print_link_status);
EXPORT_SYMBOL_GPL(ata_tf_to_fis);
EXPORT_SYMBOL_GPL(ata_tf_from_fis);
EXPORT_SYMBOL_GPL(ata_check_status);
@@ -6367,6 +6759,7 @@
EXPORT_SYMBOL_GPL(ata_exec_command);
EXPORT_SYMBOL_GPL(ata_port_start);
EXPORT_SYMBOL_GPL(ata_interrupt);
+EXPORT_SYMBOL_GPL(ata_do_set_mode);
EXPORT_SYMBOL_GPL(ata_data_xfer);
EXPORT_SYMBOL_GPL(ata_data_xfer_noirq);
EXPORT_SYMBOL_GPL(ata_qc_prep);
@@ -6429,7 +6822,8 @@
#ifdef CONFIG_PCI
EXPORT_SYMBOL_GPL(pci_test_config_bits);
-EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
+EXPORT_SYMBOL_GPL(ata_pci_init_native_host);
+EXPORT_SYMBOL_GPL(ata_pci_prepare_native_host);
EXPORT_SYMBOL_GPL(ata_pci_init_one);
EXPORT_SYMBOL_GPL(ata_pci_remove_one);
#ifdef CONFIG_PM
@@ -6461,3 +6855,8 @@
EXPORT_SYMBOL_GPL(ata_irq_ack);
EXPORT_SYMBOL_GPL(ata_dummy_irq_ack);
EXPORT_SYMBOL_GPL(ata_dev_try_classify);
+
+EXPORT_SYMBOL_GPL(ata_cable_40wire);
+EXPORT_SYMBOL_GPL(ata_cable_80wire);
+EXPORT_SYMBOL_GPL(ata_cable_unknown);
+EXPORT_SYMBOL_GPL(ata_cable_sata);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 39f556c..2bff9ad 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1056,7 +1056,7 @@
}
if (serror & SERR_INTERNAL) {
err_mask |= AC_ERR_SYSTEM;
- action |= ATA_EH_SOFTRESET;
+ action |= ATA_EH_HARDRESET;
}
if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG))
ata_ehi_hotplugged(&ehc->i);
@@ -1151,7 +1151,9 @@
return ATA_EH_SOFTRESET;
}
- if (!(qc->err_mask & AC_ERR_DEV))
+ if (stat & (ATA_ERR | ATA_DF))
+ qc->err_mask |= AC_ERR_DEV;
+ else
return 0;
switch (qc->dev->class) {
@@ -1669,7 +1671,10 @@
reset == softreset ? "soft" : "hard");
/* mark that this EH session started with reset */
- ehc->i.flags |= ATA_EHI_DID_RESET;
+ if (reset == hardreset)
+ ehc->i.flags |= ATA_EHI_DID_HARDRESET;
+ else
+ ehc->i.flags |= ATA_EHI_DID_SOFTRESET;
rc = ata_do_reset(ap, reset, classes);
@@ -1808,6 +1813,10 @@
}
}
+ /* PDIAG- should have been released, ask cable type if post-reset */
+ if ((ehc->i.flags & ATA_EHI_DID_RESET) && ap->ops->cable_detect)
+ ap->cbl = ap->ops->cable_detect(ap);
+
/* Configure new devices forward such that user doesn't see
* device detection messages backwards.
*/
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index e936443..9afba2b 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -104,7 +104,7 @@
* libata transport template. libata doesn't do real transport stuff.
* It just needs the eh_timed_out hook.
*/
-struct scsi_transport_template ata_scsi_transport_template = {
+static struct scsi_transport_template ata_scsi_transport_template = {
.eh_strategy_handler = ata_scsi_error,
.eh_timed_out = ata_scsi_timed_out,
.user_scan = ata_scsi_user_scan,
@@ -2678,6 +2678,18 @@
tf->device = qc->dev->devno ?
tf->device | ATA_DEV1 : tf->device & ~ATA_DEV1;
+ /* READ/WRITE LONG use a non-standard sect_size */
+ qc->sect_size = ATA_SECT_SIZE;
+ switch (tf->command) {
+ case ATA_CMD_READ_LONG:
+ case ATA_CMD_READ_LONG_ONCE:
+ case ATA_CMD_WRITE_LONG:
+ case ATA_CMD_WRITE_LONG_ONCE:
+ if (tf->protocol != ATA_PROT_PIO || tf->nsect != 1)
+ goto invalid_fld;
+ qc->sect_size = scmd->request_bufflen;
+ }
+
/*
* Filter SET_FEATURES - XFER MODE command -- otherwise,
* SET_FEATURES - XFER MODE must be preceded/succeeded
@@ -2792,8 +2804,9 @@
{
int rc = 0;
- if (unlikely(!scmd->cmd_len)) {
- ata_dev_printk(dev, KERN_WARNING, "WARNING: zero len CDB\n");
+ if (unlikely(!scmd->cmd_len || scmd->cmd_len > dev->cdb_len)) {
+ DPRINTK("bad CDB len=%u, max=%u\n",
+ scmd->cmd_len, dev->cdb_len);
scmd->result = DID_ERROR << 16;
done(scmd);
return 0;
@@ -2948,6 +2961,48 @@
}
}
+int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
+{
+ int i, rc;
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+ struct Scsi_Host *shost;
+
+ rc = -ENOMEM;
+ shost = scsi_host_alloc(sht, sizeof(struct ata_port *));
+ if (!shost)
+ goto err_alloc;
+
+ *(struct ata_port **)&shost->hostdata[0] = ap;
+ ap->scsi_host = shost;
+
+ shost->transportt = &ata_scsi_transport_template;
+ shost->unique_id = ap->print_id;
+ shost->max_id = 16;
+ shost->max_lun = 1;
+ shost->max_channel = 1;
+ shost->max_cmd_len = 16;
+
+ rc = scsi_add_host(ap->scsi_host, ap->host->dev);
+ if (rc)
+ goto err_add;
+ }
+
+ return 0;
+
+ err_add:
+ scsi_host_put(host->ports[i]->scsi_host);
+ err_alloc:
+ while (--i >= 0) {
+ struct Scsi_Host *shost = host->ports[i]->scsi_host;
+
+ scsi_remove_host(shost);
+ scsi_host_put(shost);
+ }
+ return rc;
+}
+
void ata_scsi_scan_host(struct ata_port *ap)
{
unsigned int i;
@@ -3224,21 +3279,21 @@
struct ata_port_info *port_info,
struct Scsi_Host *shost)
{
- struct ata_port *ap = kzalloc(sizeof(*ap), GFP_KERNEL);
- struct ata_probe_ent *ent;
+ struct ata_port *ap;
+ ap = ata_port_alloc(host);
if (!ap)
return NULL;
- ent = ata_probe_ent_alloc(host->dev, port_info);
- if (!ent) {
- kfree(ap);
- return NULL;
- }
-
- ata_port_init(ap, host, ent, 0);
+ ap->port_no = 0;
ap->lock = shost->host_lock;
- devm_kfree(host->dev, ent);
+ ap->pio_mask = port_info->pio_mask;
+ ap->mwdma_mask = port_info->mwdma_mask;
+ ap->udma_mask = port_info->udma_mask;
+ ap->flags |= port_info->flags;
+ ap->ops = port_info->port_ops;
+ ap->cbl = ATA_CBL_SATA;
+
return ap;
}
EXPORT_SYMBOL_GPL(ata_sas_port_alloc);
@@ -3294,8 +3349,10 @@
{
int rc = ap->ops->port_start(ap);
- if (!rc)
+ if (!rc) {
+ ap->print_id = ata_print_id++;
rc = ata_bus_probe(ap);
+ }
return rc;
}
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 2ffcca0..8af18ad 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -526,169 +526,400 @@
port = port * 2;
for (i = 0; i < 2; i ++) {
if (pci_resource_start(pdev, port + i) == 0 ||
- pci_resource_len(pdev, port + i) == 0)
- return 0;
+ pci_resource_len(pdev, port + i) == 0)
+ return 0;
}
return 1;
}
/**
- * ata_pci_init_native_mode - Initialize native-mode driver
- * @pdev: pci device to be initialized
- * @port: array[2] of pointers to port info structures.
- * @ports: bitmap of ports present
+ * ata_pci_init_bmdma - acquire PCI BMDMA resources and init ATA host
+ * @host: target ATA host
*
- * Utility function which allocates and initializes an
- * ata_probe_ent structure for a standard dual-port
- * PIO-based IDE controller. The returned ata_probe_ent
- * structure can be passed to ata_device_add(). The returned
- * ata_probe_ent structure should then be freed with kfree().
+ * Acquire PCI BMDMA resources and initialize @host accordingly.
*
- * The caller need only pass the address of the primary port, the
- * secondary will be deduced automatically. If the device has non
- * standard secondary port mappings this function can be called twice,
- * once for each interface.
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
*/
-
-struct ata_probe_ent *
-ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int ports)
+static int ata_pci_init_bmdma(struct ata_host *host)
{
- struct ata_probe_ent *probe_ent;
- int i, p = 0;
- void __iomem * const *iomap;
+ struct device *gdev = host->dev;
+ struct pci_dev *pdev = to_pci_dev(gdev);
+ int i, rc;
- /* iomap BARs */
- for (i = 0; i < 4; i++) {
- if (pcim_iomap(pdev, i, 0) == NULL) {
- dev_printk(KERN_ERR, &pdev->dev,
- "failed to iomap PCI BAR %d\n", i);
- return NULL;
- }
+ /* TODO: If we get no DMA mask we should fall back to PIO */
+ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ return rc;
+ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ return rc;
+
+ /* request and iomap DMA region */
+ rc = pcim_iomap_regions(pdev, 1 << 4, DRV_NAME);
+ if (rc) {
+ dev_printk(KERN_ERR, gdev, "failed to request/iomap BAR4\n");
+ return -ENOMEM;
+ }
+ host->iomap = pcim_iomap_table(pdev);
+
+ for (i = 0; i < 2; i++) {
+ struct ata_port *ap = host->ports[i];
+ void __iomem *bmdma = host->iomap[4] + 8 * i;
+
+ if (ata_port_is_dummy(ap))
+ continue;
+
+ ap->ioaddr.bmdma_addr = bmdma;
+ if ((!(ap->flags & ATA_FLAG_IGN_SIMPLEX)) &&
+ (ioread8(bmdma + 2) & 0x80))
+ host->flags |= ATA_HOST_SIMPLEX;
}
- pcim_iomap(pdev, 4, 0); /* may fail */
- iomap = pcim_iomap_table(pdev);
-
- /* alloc and init probe_ent */
- probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
- if (!probe_ent)
- return NULL;
-
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
-
- /* Discard disabled ports. Some controllers show their
- unused channels this way */
- if (ata_resources_present(pdev, 0) == 0)
- ports &= ~ATA_PORT_PRIMARY;
- if (ata_resources_present(pdev, 1) == 0)
- ports &= ~ATA_PORT_SECONDARY;
-
- if (ports & ATA_PORT_PRIMARY) {
- probe_ent->port[p].cmd_addr = iomap[0];
- probe_ent->port[p].altstatus_addr =
- probe_ent->port[p].ctl_addr = (void __iomem *)
- ((unsigned long)iomap[1] | ATA_PCI_CTL_OFS);
- if (iomap[4]) {
- if ((!(port[p]->flags & ATA_FLAG_IGN_SIMPLEX)) &&
- (ioread8(iomap[4] + 2) & 0x80))
- probe_ent->_host_flags |= ATA_HOST_SIMPLEX;
- probe_ent->port[p].bmdma_addr = iomap[4];
- }
- ata_std_ports(&probe_ent->port[p]);
- p++;
- }
-
- if (ports & ATA_PORT_SECONDARY) {
- probe_ent->port[p].cmd_addr = iomap[2];
- probe_ent->port[p].altstatus_addr =
- probe_ent->port[p].ctl_addr = (void __iomem *)
- ((unsigned long)iomap[3] | ATA_PCI_CTL_OFS);
- if (iomap[4]) {
- if ((!(port[p]->flags & ATA_FLAG_IGN_SIMPLEX)) &&
- (ioread8(iomap[4] + 10) & 0x80))
- probe_ent->_host_flags |= ATA_HOST_SIMPLEX;
- probe_ent->port[p].bmdma_addr = iomap[4] + 8;
- }
- ata_std_ports(&probe_ent->port[p]);
- probe_ent->pinfo2 = port[1];
- p++;
- }
-
- probe_ent->n_ports = p;
- return probe_ent;
+ return 0;
}
-static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev,
- struct ata_port_info **port, int port_mask)
+/**
+ * ata_pci_init_native_host - acquire native ATA resources and init host
+ * @host: target ATA host
+ * @port_mask: ports to consider
+ *
+ * Acquire native PCI ATA resources for @host and initialize
+ * @host accordoingly.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+int ata_pci_init_native_host(struct ata_host *host, unsigned int port_mask)
{
- struct ata_probe_ent *probe_ent;
- void __iomem *iomap[5] = { }, *bmdma;
+ struct device *gdev = host->dev;
+ struct pci_dev *pdev = to_pci_dev(gdev);
+ int i, rc;
- if (port_mask & ATA_PORT_PRIMARY) {
- iomap[0] = devm_ioport_map(&pdev->dev, ATA_PRIMARY_CMD, 8);
- iomap[1] = devm_ioport_map(&pdev->dev, ATA_PRIMARY_CTL, 1);
- if (!iomap[0] || !iomap[1])
- return NULL;
- }
-
- if (port_mask & ATA_PORT_SECONDARY) {
- iomap[2] = devm_ioport_map(&pdev->dev, ATA_SECONDARY_CMD, 8);
- iomap[3] = devm_ioport_map(&pdev->dev, ATA_SECONDARY_CTL, 1);
- if (!iomap[2] || !iomap[3])
- return NULL;
- }
-
- bmdma = pcim_iomap(pdev, 4, 16); /* may fail */
-
- /* alloc and init probe_ent */
- probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
- if (!probe_ent)
- return NULL;
-
- probe_ent->n_ports = 2;
- probe_ent->irq_flags = IRQF_SHARED;
-
- if (port_mask & ATA_PORT_PRIMARY) {
- probe_ent->irq = ATA_PRIMARY_IRQ(pdev);
- probe_ent->port[0].cmd_addr = iomap[0];
- probe_ent->port[0].altstatus_addr =
- probe_ent->port[0].ctl_addr = iomap[1];
- if (bmdma) {
- probe_ent->port[0].bmdma_addr = bmdma;
- if ((!(port[0]->flags & ATA_FLAG_IGN_SIMPLEX)) &&
- (ioread8(bmdma + 2) & 0x80))
- probe_ent->_host_flags |= ATA_HOST_SIMPLEX;
+ /* Discard disabled ports. Some controllers show their unused
+ * channels this way. Disabled ports are made dummy.
+ */
+ for (i = 0; i < 2; i++) {
+ if ((port_mask & (1 << i)) && !ata_resources_present(pdev, i)) {
+ host->ports[i]->ops = &ata_dummy_port_ops;
+ port_mask &= ~(1 << i);
}
- ata_std_ports(&probe_ent->port[0]);
- } else
- probe_ent->dummy_port_mask |= ATA_PORT_PRIMARY;
+ }
- if (port_mask & ATA_PORT_SECONDARY) {
- if (probe_ent->irq)
- probe_ent->irq2 = ATA_SECONDARY_IRQ(pdev);
+ if (!port_mask) {
+ dev_printk(KERN_ERR, gdev, "no available port\n");
+ return -ENODEV;
+ }
+
+ /* request, iomap BARs and init port addresses accordingly */
+ for (i = 0; i < 2; i++) {
+ struct ata_port *ap = host->ports[i];
+ int base = i * 2;
+ void __iomem * const *iomap;
+
+ if (!(port_mask & (1 << i)))
+ continue;
+
+ rc = pcim_iomap_regions(pdev, 0x3 << base, DRV_NAME);
+ if (rc) {
+ dev_printk(KERN_ERR, gdev, "failed to request/iomap "
+ "BARs for port %d (errno=%d)\n", i, rc);
+ if (rc == -EBUSY)
+ pcim_pin_device(pdev);
+ return rc;
+ }
+ host->iomap = iomap = pcim_iomap_table(pdev);
+
+ ap->ioaddr.cmd_addr = iomap[base];
+ ap->ioaddr.altstatus_addr =
+ ap->ioaddr.ctl_addr = (void __iomem *)
+ ((unsigned long)iomap[base + 1] | ATA_PCI_CTL_OFS);
+ ata_std_ports(&ap->ioaddr);
+ }
+
+ return 0;
+}
+
+/**
+ * ata_pci_prepare_native_host - helper to prepare native PCI ATA host
+ * @pdev: target PCI device
+ * @ppi: array of port_info
+ * @n_ports: number of ports to allocate
+ * @r_host: out argument for the initialized ATA host
+ *
+ * Helper to allocate ATA host for @pdev, acquire all native PCI
+ * resources and initialize it accordingly in one go.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+int ata_pci_prepare_native_host(struct pci_dev *pdev,
+ const struct ata_port_info * const * ppi,
+ int n_ports, struct ata_host **r_host)
+{
+ struct ata_host *host;
+ unsigned int port_mask;
+ int rc;
+
+ if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL))
+ return -ENOMEM;
+
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2);
+ if (!host) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "failed to allocate ATA host\n");
+ rc = -ENOMEM;
+ goto err_out;
+ }
+
+ port_mask = ATA_PORT_PRIMARY;
+ if (n_ports > 1)
+ port_mask |= ATA_PORT_SECONDARY;
+
+ rc = ata_pci_init_native_host(host, port_mask);
+ if (rc)
+ goto err_out;
+
+ /* init DMA related stuff */
+ rc = ata_pci_init_bmdma(host);
+ if (rc)
+ goto err_bmdma;
+
+ devres_remove_group(&pdev->dev, NULL);
+ *r_host = host;
+ return 0;
+
+ err_bmdma:
+ /* This is necessary because PCI and iomap resources are
+ * merged and releasing the top group won't release the
+ * acquired resources if some of those have been acquired
+ * before entering this function.
+ */
+ pcim_iounmap_regions(pdev, 0xf);
+ err_out:
+ devres_release_group(&pdev->dev, NULL);
+ return rc;
+}
+
+struct ata_legacy_devres {
+ unsigned int mask;
+ unsigned long cmd_port[2];
+ void __iomem * cmd_addr[2];
+ void __iomem * ctl_addr[2];
+ unsigned int irq[2];
+ void * irq_dev_id[2];
+};
+
+static void ata_legacy_free_irqs(struct ata_legacy_devres *legacy_dr)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (!legacy_dr->irq[i])
+ continue;
+
+ free_irq(legacy_dr->irq[i], legacy_dr->irq_dev_id[i]);
+ legacy_dr->irq[i] = 0;
+ legacy_dr->irq_dev_id[i] = NULL;
+ }
+}
+
+static void ata_legacy_release(struct device *gdev, void *res)
+{
+ struct ata_legacy_devres *this = res;
+ int i;
+
+ ata_legacy_free_irqs(this);
+
+ for (i = 0; i < 2; i++) {
+ if (this->cmd_addr[i])
+ ioport_unmap(this->cmd_addr[i]);
+ if (this->ctl_addr[i])
+ ioport_unmap(this->ctl_addr[i]);
+ if (this->cmd_port[i])
+ release_region(this->cmd_port[i], 8);
+ }
+}
+
+static int ata_init_legacy_port(struct ata_port *ap,
+ struct ata_legacy_devres *legacy_dr)
+{
+ struct ata_host *host = ap->host;
+ int port_no = ap->port_no;
+ unsigned long cmd_port, ctl_port;
+
+ if (port_no == 0) {
+ cmd_port = ATA_PRIMARY_CMD;
+ ctl_port = ATA_PRIMARY_CTL;
+ } else {
+ cmd_port = ATA_SECONDARY_CMD;
+ ctl_port = ATA_SECONDARY_CTL;
+ }
+
+ /* request cmd_port */
+ if (request_region(cmd_port, 8, "libata"))
+ legacy_dr->cmd_port[port_no] = cmd_port;
+ else {
+ dev_printk(KERN_WARNING, host->dev,
+ "0x%0lX IDE port busy\n", cmd_port);
+ return -EBUSY;
+ }
+
+ /* iomap cmd and ctl ports */
+ legacy_dr->cmd_addr[port_no] = ioport_map(cmd_port, 8);
+ legacy_dr->ctl_addr[port_no] = ioport_map(ctl_port, 1);
+ if (!legacy_dr->cmd_addr[port_no] || !legacy_dr->ctl_addr[port_no])
+ return -ENOMEM;
+
+ /* init IO addresses */
+ ap->ioaddr.cmd_addr = legacy_dr->cmd_addr[port_no];
+ ap->ioaddr.altstatus_addr = legacy_dr->ctl_addr[port_no];
+ ap->ioaddr.ctl_addr = legacy_dr->ctl_addr[port_no];
+ ata_std_ports(&ap->ioaddr);
+
+ return 0;
+}
+
+/**
+ * ata_init_legacy_host - acquire legacy ATA resources and init ATA host
+ * @host: target ATA host
+ * @legacy_mask: out parameter, mask indicating ports is in legacy mode
+ * @was_busy: out parameter, indicates whether any port was busy
+ *
+ * Acquire legacy ATA resources for ports.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+static int ata_init_legacy_host(struct ata_host *host,
+ unsigned int *legacy_mask, int *was_busy)
+{
+ struct device *gdev = host->dev;
+ struct ata_legacy_devres *legacy_dr;
+ int i, rc;
+
+ if (!devres_open_group(gdev, NULL, GFP_KERNEL))
+ return -ENOMEM;
+
+ rc = -ENOMEM;
+ legacy_dr = devres_alloc(ata_legacy_release, sizeof(*legacy_dr),
+ GFP_KERNEL);
+ if (!legacy_dr)
+ goto err_out;
+ devres_add(gdev, legacy_dr);
+
+ for (i = 0; i < 2; i++) {
+ *legacy_mask &= ~(1 << i);
+ rc = ata_init_legacy_port(host->ports[i], legacy_dr);
+ if (rc == 0)
+ legacy_dr->mask |= 1 << i;
+ else if (rc == -EBUSY)
+ (*was_busy)++;
+ }
+
+ if (!legacy_dr->mask)
+ return -EBUSY;
+
+ for (i = 0; i < 2; i++)
+ if (!(legacy_dr->mask & (1 << i)))
+ host->ports[i]->ops = &ata_dummy_port_ops;
+
+ *legacy_mask |= legacy_dr->mask;
+
+ devres_remove_group(gdev, NULL);
+ return 0;
+
+ err_out:
+ devres_release_group(gdev, NULL);
+ return rc;
+}
+
+/**
+ * ata_request_legacy_irqs - request legacy ATA IRQs
+ * @host: target ATA host
+ * @handler: array of IRQ handlers
+ * @irq_flags: array of IRQ flags
+ * @dev_id: array of IRQ dev_ids
+ *
+ * Request legacy IRQs for non-dummy legacy ports in @host. All
+ * IRQ parameters are passed as array to allow ports to have
+ * separate IRQ handlers.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+static int ata_request_legacy_irqs(struct ata_host *host,
+ irq_handler_t const *handler,
+ const unsigned int *irq_flags,
+ void * const *dev_id)
+{
+ struct device *gdev = host->dev;
+ struct ata_legacy_devres *legacy_dr;
+ int i, rc;
+
+ legacy_dr = devres_find(host->dev, ata_legacy_release, NULL, NULL);
+ BUG_ON(!legacy_dr);
+
+ for (i = 0; i < 2; i++) {
+ unsigned int irq;
+
+ /* FIXME: ATA_*_IRQ() should take generic device not pci_dev */
+ if (i == 0)
+ irq = ATA_PRIMARY_IRQ(to_pci_dev(gdev));
else
- probe_ent->irq = ATA_SECONDARY_IRQ(pdev);
- probe_ent->port[1].cmd_addr = iomap[2];
- probe_ent->port[1].altstatus_addr =
- probe_ent->port[1].ctl_addr = iomap[3];
- if (bmdma) {
- probe_ent->port[1].bmdma_addr = bmdma + 8;
- if ((!(port[1]->flags & ATA_FLAG_IGN_SIMPLEX)) &&
- (ioread8(bmdma + 10) & 0x80))
- probe_ent->_host_flags |= ATA_HOST_SIMPLEX;
+ irq = ATA_SECONDARY_IRQ(to_pci_dev(gdev));
+
+ if (!(legacy_dr->mask & (1 << i)))
+ continue;
+
+ if (!handler[i]) {
+ dev_printk(KERN_ERR, gdev,
+ "NULL handler specified for port %d\n", i);
+ rc = -EINVAL;
+ goto err_out;
}
- ata_std_ports(&probe_ent->port[1]);
- /* FIXME: could be pointing to stack area; must copy */
- probe_ent->pinfo2 = port[1];
- } else
- probe_ent->dummy_port_mask |= ATA_PORT_SECONDARY;
+ rc = request_irq(irq, handler[i], irq_flags[i], DRV_NAME,
+ dev_id[i]);
+ if (rc) {
+ dev_printk(KERN_ERR, gdev,
+ "irq %u request failed (errno=%d)\n", irq, rc);
+ goto err_out;
+ }
- return probe_ent;
+ /* record irq allocation in legacy_dr */
+ legacy_dr->irq[i] = irq;
+ legacy_dr->irq_dev_id[i] = dev_id[i];
+
+ /* only used to print info */
+ if (i == 0)
+ host->irq = irq;
+ else
+ host->irq2 = irq;
+ }
+
+ return 0;
+
+ err_out:
+ ata_legacy_free_irqs(legacy_dr);
+ return rc;
}
-
/**
* ata_pci_init_one - Initialize/register PCI IDE host controller
* @pdev: Controller to be initialized
@@ -718,8 +949,8 @@
unsigned int n_ports)
{
struct device *dev = &pdev->dev;
- struct ata_probe_ent *probe_ent = NULL;
- struct ata_port_info *port[2];
+ struct ata_host *host = NULL;
+ const struct ata_port_info *port[2];
u8 mask;
unsigned int legacy_mode = 0;
int rc;
@@ -743,7 +974,7 @@
Checking dev->is_enabled is insufficient as this is not set at
boot for the primary video which is BIOS enabled
- */
+ */
rc = pcim_enable_device(pdev);
if (rc)
@@ -769,96 +1000,68 @@
#endif
}
- if (!legacy_mode) {
- rc = pci_request_regions(pdev, DRV_NAME);
- if (rc) {
- pcim_pin_device(pdev);
- goto err_out;
- }
- } else {
- /* Deal with combined mode hack. This side of the logic all
- goes away once the combined mode hack is killed in 2.6.21 */
- if (!devm_request_region(dev, ATA_PRIMARY_CMD, 8, "libata")) {
- struct resource *conflict, res;
- res.start = ATA_PRIMARY_CMD;
- res.end = ATA_PRIMARY_CMD + 8 - 1;
- conflict = ____request_resource(&ioport_resource, &res);
- while (conflict->child)
- conflict = ____request_resource(conflict, &res);
- if (!strcmp(conflict->name, "libata"))
- legacy_mode |= ATA_PORT_PRIMARY;
- else {
- pcim_pin_device(pdev);
- printk(KERN_WARNING "ata: 0x%0X IDE port busy\n" \
- "ata: conflict with %s\n",
- ATA_PRIMARY_CMD,
- conflict->name);
- }
- } else
- legacy_mode |= ATA_PORT_PRIMARY;
-
- if (!devm_request_region(dev, ATA_SECONDARY_CMD, 8, "libata")) {
- struct resource *conflict, res;
- res.start = ATA_SECONDARY_CMD;
- res.end = ATA_SECONDARY_CMD + 8 - 1;
- conflict = ____request_resource(&ioport_resource, &res);
- while (conflict->child)
- conflict = ____request_resource(conflict, &res);
- if (!strcmp(conflict->name, "libata"))
- legacy_mode |= ATA_PORT_SECONDARY;
- else {
- pcim_pin_device(pdev);
- printk(KERN_WARNING "ata: 0x%X IDE port busy\n" \
- "ata: conflict with %s\n",
- ATA_SECONDARY_CMD,
- conflict->name);
- }
- } else
- legacy_mode |= ATA_PORT_SECONDARY;
-
- if (legacy_mode & ATA_PORT_PRIMARY)
- pci_request_region(pdev, 1, DRV_NAME);
- if (legacy_mode & ATA_PORT_SECONDARY)
- pci_request_region(pdev, 3, DRV_NAME);
- /* If there is a DMA resource, allocate it */
- pci_request_region(pdev, 4, DRV_NAME);
- }
-
- /* we have legacy mode, but all ports are unavailable */
- if (legacy_mode == (1 << 3)) {
- rc = -EBUSY;
- goto err_out;
- }
-
- /* TODO: If we get no DMA mask we should fall back to PIO */
- rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
- if (rc)
- goto err_out;
- rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
- if (rc)
- goto err_out;
-
- if (legacy_mode) {
- probe_ent = ata_pci_init_legacy_port(pdev, port, legacy_mode);
- } else {
- if (n_ports == 2)
- probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
- else
- probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY);
- }
- if (!probe_ent) {
+ /* alloc and init host */
+ host = ata_host_alloc_pinfo(dev, port, 2);
+ if (!host) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "failed to allocate ATA host\n");
rc = -ENOMEM;
goto err_out;
}
- pci_set_master(pdev);
+ if (!legacy_mode) {
+ unsigned int port_mask;
- if (!ata_device_add(probe_ent)) {
- rc = -ENODEV;
- goto err_out;
+ port_mask = ATA_PORT_PRIMARY;
+ if (n_ports > 1)
+ port_mask |= ATA_PORT_SECONDARY;
+
+ rc = ata_pci_init_native_host(host, port_mask);
+ if (rc)
+ goto err_out;
+ } else {
+ int was_busy = 0;
+
+ rc = ata_init_legacy_host(host, &legacy_mode, &was_busy);
+ if (was_busy)
+ pcim_pin_device(pdev);
+ if (rc)
+ goto err_out;
+
+ /* request respective PCI regions, may fail */
+ rc = pci_request_region(pdev, 1, DRV_NAME);
+ rc = pci_request_region(pdev, 3, DRV_NAME);
}
- devm_kfree(dev, probe_ent);
+ /* init BMDMA, may fail */
+ ata_pci_init_bmdma(host);
+ pci_set_master(pdev);
+
+ /* start host and request IRQ */
+ rc = ata_host_start(host);
+ if (rc)
+ goto err_out;
+
+ if (!legacy_mode)
+ rc = devm_request_irq(dev, pdev->irq,
+ port_info[0]->port_ops->irq_handler,
+ IRQF_SHARED, DRV_NAME, host);
+ else {
+ irq_handler_t handler[2] = { host->ops->irq_handler,
+ host->ops->irq_handler };
+ unsigned int irq_flags[2] = { IRQF_SHARED, IRQF_SHARED };
+ void *dev_id[2] = { host, host };
+
+ rc = ata_request_legacy_irqs(host, handler, irq_flags, dev_id);
+ }
+ if (rc)
+ goto err_out;
+
+ /* register */
+ rc = ata_host_register(host, port_info[0]->sht);
+ if (rc)
+ goto err_out;
+
devres_remove_group(dev, NULL);
return 0;
@@ -893,12 +1096,12 @@
return 0;
}
-unsigned long ata_pci_default_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long xfer_mask)
+unsigned long ata_pci_default_filter(struct ata_device *adev, unsigned long xfer_mask)
{
/* Filter out DMA modes if the device has been configured by
the BIOS as PIO only */
- if (ap->ioaddr.bmdma_addr == 0)
+ if (adev->ap->ioaddr.bmdma_addr == 0)
xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA);
return xfer_mask;
}
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 1f1e3a5..5f4d40c 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -52,6 +52,7 @@
ATA_DNXFER_QUIET = (1 << 31),
};
+extern unsigned int ata_print_id;
extern struct workqueue_struct *ata_aux_wq;
extern int atapi_enabled;
extern int atapi_dmadir;
@@ -92,10 +93,7 @@
extern void ata_dev_init(struct ata_device *dev);
extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg);
extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
-extern void ata_port_init(struct ata_port *ap, struct ata_host *host,
- const struct ata_probe_ent *ent, unsigned int port_no);
-extern struct ata_probe_ent *ata_probe_ent_alloc(struct device *dev,
- const struct ata_port_info *port);
+extern struct ata_port *ata_port_alloc(struct ata_host *host);
/* libata-acpi.c */
#ifdef CONFIG_SATA_ACPI
@@ -113,8 +111,8 @@
#endif
/* libata-scsi.c */
-extern struct scsi_transport_template ata_scsi_transport_template;
-
+extern int ata_scsi_add_hosts(struct ata_host *host,
+ struct scsi_host_template *sht);
extern void ata_scsi_scan_host(struct ata_port *ap);
extern int ata_scsi_offline_dev(struct ata_device *dev);
extern void ata_scsi_hotplug(struct work_struct *work);
diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c
index 11ea552..d40edeb 100644
--- a/drivers/ata/pata_ali.c
+++ b/drivers/ata/pata_ali.c
@@ -34,7 +34,7 @@
#include <linux/dmi.h>
#define DRV_NAME "pata_ali"
-#define DRV_VERSION "0.7.3"
+#define DRV_VERSION "0.7.4"
/*
* Cable special cases
@@ -90,59 +90,6 @@
}
/**
- * ali_early_error_handler - reset for eary chip
- * @ap: ATA port
- *
- * Handle the reset callback for the later chips with cable detect
- */
-
-static int ali_c2_pre_reset(struct ata_port *ap)
-{
- ap->cbl = ali_c2_cable_detect(ap);
- return ata_std_prereset(ap);
-}
-
-static void ali_c2_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, ali_c2_pre_reset,
- ata_std_softreset, NULL,
- ata_std_postreset);
-}
-
-/**
- * ali_early_cable_detect - cable detection
- * @ap: ATA port
- *
- * Perform cable detection for older chipsets. This turns out to be
- * rather easy to implement
- */
-
-static int ali_early_cable_detect(struct ata_port *ap)
-{
- return ATA_CBL_PATA40;
-}
-
-/**
- * ali_early_probe_init - reset for early chip
- * @ap: ATA port
- *
- * Handle the reset callback for the early (pre cable detect) chips.
- */
-
-static int ali_early_pre_reset(struct ata_port *ap)
-{
- ap->cbl = ali_early_cable_detect(ap);
- return ata_std_prereset(ap);
-}
-
-static void ali_early_error_handler(struct ata_port *ap)
-{
- return ata_bmdma_drive_eh(ap, ali_early_pre_reset,
- ata_std_softreset, NULL,
- ata_std_postreset);
-}
-
-/**
* ali_20_filter - filter for earlier ALI DMA
* @ap: ALi ATA port
* @adev: attached device
@@ -151,7 +98,7 @@
* fix that later on. Also ensure we do not do UDMA on WDC drives
*/
-static unsigned long ali_20_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long ali_20_filter(struct ata_device *adev, unsigned long mask)
{
char model_num[ATA_ID_PROD_LEN + 1];
/* No DMA on anything but a disk for now */
@@ -160,7 +107,7 @@
ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num));
if (strstr(model_num, "WDC"))
return mask &= ~ATA_MASK_UDMA;
- return ata_pci_default_filter(ap, adev, mask);
+ return ata_pci_default_filter(adev, mask);
}
/**
@@ -314,7 +261,6 @@
/**
* ali_lock_sectors - Keep older devices to 255 sector mode
- * @ap: ATA port
* @adev: Device
*
* Called during the bus probe for each device that is found. We use
@@ -324,7 +270,7 @@
* slower PIO methods
*/
-static void ali_lock_sectors(struct ata_port *ap, struct ata_device *adev)
+static void ali_lock_sectors(struct ata_device *adev)
{
adev->max_sectors = 255;
}
@@ -366,8 +312,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = ali_early_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
@@ -402,8 +349,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = ali_early_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -440,8 +388,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = ali_c2_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ali_c2_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -477,8 +426,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = ali_c2_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ali_c2_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c
index 1838176..536ee89 100644
--- a/drivers/ata/pata_amd.c
+++ b/drivers/ata/pata_amd.c
@@ -25,7 +25,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_amd"
-#define DRV_VERSION "0.2.8"
+#define DRV_VERSION "0.3.8"
/**
* timing_setup - shared timing computation and load
@@ -119,32 +119,25 @@
}
/**
- * amd_probe_init - cable detection
+ * amd_probe_init - perform reset handling
* @ap: ATA port
*
- * Perform cable detection. The BIOS stores this in PCI config
- * space for us.
+ * Reset sequence checking enable bits to see which ports are
+ * active.
*/
static int amd_pre_reset(struct ata_port *ap)
{
- static const u32 bitmask[2] = {0x03, 0x0C};
static const struct pci_bits amd_enable_bits[] = {
{ 0x40, 1, 0x02, 0x02 },
{ 0x40, 1, 0x01, 0x01 }
};
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- u8 ata66;
if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no]))
return -ENOENT;
- pci_read_config_byte(pdev, 0x42, &ata66);
- if (ata66 & bitmask[ap->port_no])
- ap->cbl = ATA_CBL_PATA80;
- else
- ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -156,28 +149,16 @@
ata_std_postreset);
}
-static int amd_early_pre_reset(struct ata_port *ap)
+static int amd_cable_detect(struct ata_port *ap)
{
+ static const u32 bitmask[2] = {0x03, 0x0C};
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- static struct pci_bits amd_enable_bits[] = {
- { 0x40, 1, 0x02, 0x02 },
- { 0x40, 1, 0x01, 0x01 }
- };
+ u8 ata66;
- if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no]))
- return -ENOENT;
-
- /* No host side cable detection */
- ap->cbl = ATA_CBL_PATA80;
- return ata_std_prereset(ap);
-
-}
-
-static void amd_early_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, amd_early_pre_reset,
- ata_std_softreset, NULL,
- ata_std_postreset);
+ pci_read_config_byte(pdev, 0x42, &ata66);
+ if (ata66 & bitmask[ap->port_no])
+ return ATA_CBL_PATA80;
+ return ATA_CBL_PATA40;
}
/**
@@ -247,31 +228,16 @@
*/
static int nv_pre_reset(struct ata_port *ap) {
- static const u8 bitmask[2] = {0x03, 0x0C};
static const struct pci_bits nv_enable_bits[] = {
{ 0x50, 1, 0x02, 0x02 },
{ 0x50, 1, 0x01, 0x01 }
};
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- u8 ata66;
- u16 udma;
if (!pci_test_config_bits(pdev, &nv_enable_bits[ap->port_no]))
return -ENOENT;
- pci_read_config_byte(pdev, 0x52, &ata66);
- if (ata66 & bitmask[ap->port_no])
- ap->cbl = ATA_CBL_PATA80;
- else
- ap->cbl = ATA_CBL_PATA40;
-
- /* We now have to double check because the Nvidia boxes BIOS
- doesn't always set the cable bits but does set mode bits */
-
- pci_read_config_word(pdev, 0x62 - 2 * ap->port_no, &udma);
- if ((udma & 0xC4) == 0xC4 || (udma & 0xC400) == 0xC400)
- ap->cbl = ATA_CBL_PATA80;
return ata_std_prereset(ap);
}
@@ -281,6 +247,29 @@
ata_std_softreset, NULL,
ata_std_postreset);
}
+
+static int nv_cable_detect(struct ata_port *ap)
+{
+ static const u8 bitmask[2] = {0x03, 0x0C};
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u8 ata66;
+ u16 udma;
+ int cbl;
+
+ pci_read_config_byte(pdev, 0x52, &ata66);
+ if (ata66 & bitmask[ap->port_no])
+ cbl = ATA_CBL_PATA80;
+ else
+ cbl = ATA_CBL_PATA40;
+
+ /* We now have to double check because the Nvidia boxes BIOS
+ doesn't always set the cable bits but does set mode bits */
+ pci_read_config_word(pdev, 0x62 - 2 * ap->port_no, &udma);
+ if ((udma & 0xC4) == 0xC4 || (udma & 0xC400) == 0xC400)
+ cbl = ATA_CBL_PATA80;
+ return cbl;
+}
+
/**
* nv100_set_piomode - set initial PIO mode data
* @ap: ATA interface
@@ -353,8 +342,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = amd_early_error_handler,
+ .error_handler = amd_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -387,8 +377,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = amd_early_error_handler,
+ .error_handler = amd_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_unknown,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -423,6 +414,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = amd_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_unknown,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -457,6 +449,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = amd_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = amd_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -491,6 +484,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = nv_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = nv_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -525,6 +519,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = nv_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = nv_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c
index 21c3028..00e9ec3 100644
--- a/drivers/ata/pata_artop.c
+++ b/drivers/ata/pata_artop.c
@@ -49,8 +49,6 @@
if (!pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no]))
return -ENOENT;
-
- ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -85,18 +83,28 @@
};
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- u8 tmp;
/* Odd numbered device ids are the units with enable bits (the -R cards) */
if (pdev->device % 1 && !pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no]))
return -ENOENT;
+ return ata_std_prereset(ap);
+}
+/**
+ * artop6260_cable_detect - identify cable type
+ * @ap: Port
+ *
+ * Identify the cable type for the ARTOp interface in question
+ */
+
+static int artop6260_cable_detect(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u8 tmp;
pci_read_config_byte(pdev, 0x49, &tmp);
if (tmp & (1 << ap->port_no))
- ap->cbl = ATA_CBL_PATA40;
- else
- ap->cbl = ATA_CBL_PATA80;
- return ata_std_prereset(ap);
+ return ATA_CBL_PATA40;
+ return ATA_CBL_PATA80;
}
/**
@@ -225,7 +233,7 @@
/**
* artop6210_set_dmamode - Initialize host controller PATA PIO timings
* @ap: Port whose timings we are configuring
- * @adev: um
+ * @adev: Device whose timings we are configuring
*
* Set DMA mode for device, in host controller PCI config space.
*
@@ -333,6 +341,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = artop6210_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -366,6 +375,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = artop6260_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = artop6260_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c
index 51d9923..39c871a 100644
--- a/drivers/ata/pata_atiixp.c
+++ b/drivers/ata/pata_atiixp.c
@@ -22,7 +22,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_atiixp"
-#define DRV_VERSION "0.4.4"
+#define DRV_VERSION "0.4.5"
enum {
ATIIXP_IDE_PIO_TIMING = 0x40,
@@ -35,23 +35,15 @@
static int atiixp_pre_reset(struct ata_port *ap)
{
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
static const struct pci_bits atiixp_enable_bits[] = {
{ 0x48, 1, 0x01, 0x00 },
{ 0x48, 1, 0x08, 0x00 }
};
- u8 udma;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
if (!pci_test_config_bits(pdev, &atiixp_enable_bits[ap->port_no]))
return -ENOENT;
- /* Hack from drivers/ide/pci. Really we want to know how to do the
- raw detection not play follow the bios mode guess */
- pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ap->port_no, &udma);
- if ((udma & 0x07) >= 0x04 || (udma & 0x70) >= 0x40)
- ap->cbl = ATA_CBL_PATA80;
- else
- ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -60,6 +52,19 @@
ata_bmdma_drive_eh(ap, atiixp_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
}
+static int atiixp_cable_detect(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u8 udma;
+
+ /* Hack from drivers/ide/pci. Really we want to know how to do the
+ raw detection not play follow the bios mode guess */
+ pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ap->port_no, &udma);
+ if ((udma & 0x07) >= 0x04 || (udma & 0x70) >= 0x40)
+ return ATA_CBL_PATA80;
+ return ATA_CBL_PATA40;
+}
+
/**
* atiixp_set_pio_timing - set initial PIO mode data
* @ap: ATA interface
@@ -245,6 +250,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = atiixp_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = atiixp_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = atiixp_bmdma_start,
diff --git a/drivers/ata/pata_cmd640.c b/drivers/ata/pata_cmd640.c
new file mode 100644
index 0000000..2105985
--- /dev/null
+++ b/drivers/ata/pata_cmd640.c
@@ -0,0 +1,312 @@
+/*
+ * pata_cmd640.c - CMD640 PCI PATA for new ATA layer
+ * (C) 2007 Red Hat Inc
+ * Alan Cox <alan@redhat.com>
+ *
+ * Based upon
+ * linux/drivers/ide/pci/cmd640.c Version 1.02 Sep 01, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & authors (see driver)
+ *
+ * This drives only the PCI version of the controller. If you have a
+ * VLB one then we have enough docs to support it but you can write
+ * your own code.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME "pata_cmd640"
+#define DRV_VERSION "0.0.5"
+
+struct cmd640_reg {
+ int last;
+ u8 reg58[ATA_MAX_DEVICES];
+};
+
+enum {
+ CFR = 0x50,
+ CNTRL = 0x51,
+ CMDTIM = 0x52,
+ ARTIM0 = 0x53,
+ DRWTIM0 = 0x54,
+ ARTIM23 = 0x57,
+ DRWTIM23 = 0x58,
+ BRST = 0x59
+};
+
+/**
+ * cmd640_set_piomode - set initial PIO mode data
+ * @ap: ATA port
+ * @adev: ATA device
+ *
+ * Called to do the PIO mode setup.
+ */
+
+static void cmd640_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+ struct cmd640_reg *timing = ap->private_data;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ struct ata_timing t;
+ const unsigned long T = 1000000 / 33;
+ const u8 setup_data[] = { 0x40, 0x40, 0x40, 0x80, 0x00 };
+ u8 reg;
+ int arttim = ARTIM0 + 2 * adev->devno;
+ struct ata_device *pair = ata_dev_pair(adev);
+
+ if (ata_timing_compute(adev, adev->pio_mode, &t, T, 0) < 0) {
+ printk(KERN_ERR DRV_NAME ": mode computation failed.\n");
+ return;
+ }
+
+ /* The second channel has shared timings and the setup timing is
+ messy to switch to merge it for worst case */
+ if (ap->port_no && pair) {
+ struct ata_timing p;
+ ata_timing_compute(pair, pair->pio_mode, &p, T, 1);
+ ata_timing_merge(&p, &t, &t, ATA_TIMING_SETUP);
+ }
+
+ /* Make the timings fit */
+ if (t.recover > 16) {
+ t.active += t.recover - 16;
+ t.recover = 16;
+ }
+ if (t.active > 16)
+ t.active = 16;
+
+ /* Now convert the clocks into values we can actually stuff into
+ the chip */
+
+ if (t.recover > 1)
+ t.recover--; /* 640B only */
+ else
+ t.recover = 15;
+
+ if (t.setup > 4)
+ t.setup = 0xC0;
+ else
+ t.setup = setup_data[t.setup];
+
+ if (ap->port_no == 0) {
+ t.active &= 0x0F; /* 0 = 16 */
+
+ /* Load setup timing */
+ pci_read_config_byte(pdev, arttim, ®);
+ reg &= 0x3F;
+ reg |= t.setup;
+ pci_write_config_byte(pdev, arttim, reg);
+
+ /* Load active/recovery */
+ pci_write_config_byte(pdev, arttim + 1, (t.active << 4) | t.recover);
+ } else {
+ /* Save the shared timings for channel, they will be loaded
+ by qc_issue_prot. Reloading the setup time is expensive
+ so we keep a merged one loaded */
+ pci_read_config_byte(pdev, ARTIM23, ®);
+ reg &= 0x3F;
+ reg |= t.setup;
+ pci_write_config_byte(pdev, ARTIM23, reg);
+ timing->reg58[adev->devno] = (t.active << 4) | t.recover;
+ }
+}
+
+
+/**
+ * cmd640_qc_issue_prot - command preparation hook
+ * @qc: Command to be issued
+ *
+ * Channel 1 has shared timings. We must reprogram the
+ * clock each drive 2/3 switch we do.
+ */
+
+static unsigned int cmd640_qc_issue_prot(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct ata_device *adev = qc->dev;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ struct cmd640_reg *timing = ap->private_data;
+
+ if (ap->port_no != 0 && adev->devno != timing->last) {
+ pci_write_config_byte(pdev, DRWTIM23, timing->reg58[adev->devno]);
+ timing->last = adev->devno;
+ }
+ return ata_qc_issue_prot(qc);
+}
+
+/**
+ * cmd640_port_start - port setup
+ * @ap: ATA port being set up
+ *
+ * The CMD640 needs to maintain private data structures so we
+ * allocate space here.
+ */
+
+static int cmd640_port_start(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ struct cmd640_reg *timing;
+
+ int ret = ata_port_start(ap);
+ if (ret < 0)
+ return ret;
+
+ timing = devm_kzalloc(&pdev->dev, sizeof(struct cmd640_reg), GFP_KERNEL);
+ if (timing == NULL)
+ return -ENOMEM;
+ timing->last = -1; /* Force a load */
+ ap->private_data = timing;
+ return ret;
+}
+
+static struct scsi_host_template cmd640_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .slave_destroy = ata_scsi_slave_destroy,
+ .bios_param = ata_std_bios_param,
+#ifdef CONFIG_PM
+ .resume = ata_scsi_device_resume,
+ .suspend = ata_scsi_device_suspend,
+#endif
+};
+
+static struct ata_port_operations cmd640_port_ops = {
+ .port_disable = ata_port_disable,
+ .set_piomode = cmd640_set_piomode,
+ .mode_filter = ata_pci_default_filter,
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .freeze = ata_bmdma_freeze,
+ .thaw = ata_bmdma_thaw,
+ .error_handler = ata_bmdma_error_handler,
+ .post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
+
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+
+ .qc_prep = ata_qc_prep,
+ .qc_issue = cmd640_qc_issue_prot,
+
+ /* In theory this is not needed once we kill the prefetcher */
+ .data_xfer = ata_data_xfer_noirq,
+
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+ .irq_on = ata_irq_on,
+ .irq_ack = ata_irq_ack,
+
+ .port_start = cmd640_port_start,
+};
+
+static void cmd640_hardware_init(struct pci_dev *pdev)
+{
+ u8 r;
+ u8 ctrl;
+
+ /* CMD640 detected, commiserations */
+ pci_write_config_byte(pdev, 0x5B, 0x00);
+ /* Get version info */
+ pci_read_config_byte(pdev, CFR, &r);
+ /* PIO0 command cycles */
+ pci_write_config_byte(pdev, CMDTIM, 0);
+ /* 512 byte bursts (sector) */
+ pci_write_config_byte(pdev, BRST, 0x40);
+ /*
+ * A reporter a long time ago
+ * Had problems with the data fifo
+ * So don't run the risk
+ * Of putting crap on the disk
+ * For its better just to go slow
+ */
+ /* Do channel 0 */
+ pci_read_config_byte(pdev, CNTRL, &ctrl);
+ pci_write_config_byte(pdev, CNTRL, ctrl | 0xC0);
+ /* Ditto for channel 1 */
+ pci_read_config_byte(pdev, ARTIM23, &ctrl);
+ ctrl |= 0x0C;
+ pci_write_config_byte(pdev, ARTIM23, ctrl);
+}
+
+static int cmd640_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ static struct ata_port_info info = {
+ .sht = &cmd640_sht,
+ .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST,
+ .pio_mask = 0x1f,
+ .port_ops = &cmd640_port_ops
+ };
+
+ static struct ata_port_info *port_info[2] = { &info, &info };
+
+ cmd640_hardware_init(pdev);
+ return ata_pci_init_one(pdev, port_info, 2);
+}
+
+static int cmd640_reinit_one(struct pci_dev *pdev)
+{
+ cmd640_hardware_init(pdev);
+#ifdef CONFIG_PM
+ return ata_pci_device_resume(pdev);
+#else
+ return 0;
+#endif
+}
+
+static const struct pci_device_id cmd640[] = {
+ { PCI_VDEVICE(CMD, 0x640), 0 },
+ { },
+};
+
+static struct pci_driver cmd640_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = cmd640,
+ .probe = cmd640_init_one,
+ .remove = ata_pci_remove_one,
+#ifdef CONFIG_PM
+ .suspend = ata_pci_device_suspend,
+#endif
+ .resume = cmd640_reinit_one,
+};
+
+static int __init cmd640_init(void)
+{
+ return pci_register_driver(&cmd640_pci_driver);
+}
+
+static void __exit cmd640_exit(void)
+{
+ pci_unregister_driver(&cmd640_pci_driver);
+}
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("low-level driver for CMD640 PATA controllers");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, cmd640);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(cmd640_init);
+module_exit(cmd640_exit);
diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c
index 5b13bdd..3989cc5 100644
--- a/drivers/ata/pata_cmd64x.c
+++ b/drivers/ata/pata_cmd64x.c
@@ -75,13 +75,7 @@
DTPR1 = 0x7C
};
-static int cmd64x_pre_reset(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_PATA40;
- return ata_std_prereset(ap);
-}
-
-static int cmd648_pre_reset(struct ata_port *ap)
+static int cmd648_cable_detect(struct ata_port *ap)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u8 r;
@@ -89,21 +83,8 @@
/* Check cable detect bits */
pci_read_config_byte(pdev, BMIDECSR, &r);
if (r & (1 << ap->port_no))
- ap->cbl = ATA_CBL_PATA80;
- else
- ap->cbl = ATA_CBL_PATA40;
-
- return ata_std_prereset(ap);
-}
-
-static void cmd64x_error_handler(struct ata_port *ap)
-{
- return ata_bmdma_drive_eh(ap, cmd64x_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-static void cmd648_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, cmd648_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
+ return ATA_CBL_PATA80;
+ return ATA_CBL_PATA40;
}
/**
@@ -304,8 +285,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = cmd64x_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -338,8 +320,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = cmd64x_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -372,8 +355,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = cmd648_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = cmd648_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c
index 55cc293..79bef0d 100644
--- a/drivers/ata/pata_cs5520.c
+++ b/drivers/ata/pata_cs5520.c
@@ -139,18 +139,6 @@
cs5520_set_timings(ap, adev, adev->pio_mode);
}
-
-static int cs5520_pre_reset(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_PATA40;
- return ata_std_prereset(ap);
-}
-
-static void cs5520_error_handler(struct ata_port *ap)
-{
- return ata_bmdma_drive_eh(ap, cs5520_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
static struct scsi_host_template cs5520_sht = {
.module = THIS_MODULE,
.name = DRV_NAME,
@@ -186,8 +174,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = cs5520_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -197,7 +186,6 @@
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_data_xfer,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -205,91 +193,104 @@
.port_start = ata_port_start,
};
-static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id)
+static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
+ struct ata_port_info pi = {
+ .flags = ATA_FLAG_SLAVE_POSS,
+ .pio_mask = 0x1f,
+ .port_ops = &cs5520_port_ops,
+ };
+ const struct ata_port_info *ppi[2];
u8 pcicfg;
- void __iomem *iomap[5];
- static struct ata_probe_ent probe[2];
- int ports = 0;
+ void *iomap[5];
+ struct ata_host *host;
+ struct ata_ioports *ioaddr;
+ int i, rc;
/* IDE port enable bits */
- pci_read_config_byte(dev, 0x60, &pcicfg);
+ pci_read_config_byte(pdev, 0x60, &pcicfg);
/* Check if the ATA ports are enabled */
if ((pcicfg & 3) == 0)
return -ENODEV;
+ ppi[0] = ppi[1] = &ata_dummy_port_info;
+ if (pcicfg & 1)
+ ppi[0] = π
+ if (pcicfg & 2)
+ ppi[1] = π
+
if ((pcicfg & 0x40) == 0) {
- printk(KERN_WARNING DRV_NAME ": DMA mode disabled. Enabling.\n");
- pci_write_config_byte(dev, 0x60, pcicfg | 0x40);
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "DMA mode disabled. Enabling.\n");
+ pci_write_config_byte(pdev, 0x60, pcicfg | 0x40);
}
+ pi.mwdma_mask = id->driver_data;
+
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2);
+ if (!host)
+ return -ENOMEM;
+
/* Perform set up for DMA */
- if (pci_enable_device_bars(dev, 1<<2)) {
+ if (pci_enable_device_bars(pdev, 1<<2)) {
printk(KERN_ERR DRV_NAME ": unable to configure BAR2.\n");
return -ENODEV;
}
- pci_set_master(dev);
- if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) {
+
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
printk(KERN_ERR DRV_NAME ": unable to configure DMA mask.\n");
return -ENODEV;
}
- if (pci_set_consistent_dma_mask(dev, DMA_32BIT_MASK)) {
+ if (pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) {
printk(KERN_ERR DRV_NAME ": unable to configure consistent DMA mask.\n");
return -ENODEV;
}
- /* Map IO ports */
- iomap[0] = devm_ioport_map(&dev->dev, 0x1F0, 8);
- iomap[1] = devm_ioport_map(&dev->dev, 0x3F6, 1);
- iomap[2] = devm_ioport_map(&dev->dev, 0x170, 8);
- iomap[3] = devm_ioport_map(&dev->dev, 0x376, 1);
- iomap[4] = pcim_iomap(dev, 2, 0);
+ /* Map IO ports and initialize host accordingly */
+ iomap[0] = devm_ioport_map(&pdev->dev, 0x1F0, 8);
+ iomap[1] = devm_ioport_map(&pdev->dev, 0x3F6, 1);
+ iomap[2] = devm_ioport_map(&pdev->dev, 0x170, 8);
+ iomap[3] = devm_ioport_map(&pdev->dev, 0x376, 1);
+ iomap[4] = pcim_iomap(pdev, 2, 0);
if (!iomap[0] || !iomap[1] || !iomap[2] || !iomap[3] || !iomap[4])
return -ENOMEM;
- /* We have to do our own plumbing as the PCI setup for this
- chipset is non-standard so we can't punt to the libata code */
+ ioaddr = &host->ports[0]->ioaddr;
+ ioaddr->cmd_addr = iomap[0];
+ ioaddr->ctl_addr = iomap[1];
+ ioaddr->altstatus_addr = iomap[1];
+ ioaddr->bmdma_addr = iomap[4];
+ ata_std_ports(ioaddr);
- INIT_LIST_HEAD(&probe[0].node);
- probe[0].dev = pci_dev_to_dev(dev);
- probe[0].port_ops = &cs5520_port_ops;
- probe[0].sht = &cs5520_sht;
- probe[0].pio_mask = 0x1F;
- probe[0].mwdma_mask = id->driver_data;
- probe[0].irq = 14;
- probe[0].irq_flags = 0;
- probe[0].port_flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST;
- probe[0].n_ports = 1;
- probe[0].port[0].cmd_addr = iomap[0];
- probe[0].port[0].ctl_addr = iomap[1];
- probe[0].port[0].altstatus_addr = iomap[1];
- probe[0].port[0].bmdma_addr = iomap[4];
+ ioaddr = &host->ports[1]->ioaddr;
+ ioaddr->cmd_addr = iomap[2];
+ ioaddr->ctl_addr = iomap[3];
+ ioaddr->altstatus_addr = iomap[3];
+ ioaddr->bmdma_addr = iomap[4] + 8;
+ ata_std_ports(ioaddr);
- /* The secondary lurks at different addresses but is otherwise
- the same beastie */
+ /* activate the host */
+ pci_set_master(pdev);
+ rc = ata_host_start(host);
+ if (rc)
+ return rc;
- probe[1] = probe[0];
- INIT_LIST_HEAD(&probe[1].node);
- probe[1].irq = 15;
- probe[1].port[0].cmd_addr = iomap[2];
- probe[1].port[0].ctl_addr = iomap[3];
- probe[1].port[0].altstatus_addr = iomap[3];
- probe[1].port[0].bmdma_addr = iomap[4] + 8;
+ for (i = 0; i < 2; i++) {
+ static const int irq[] = { 14, 15 };
+ struct ata_port *ap = host->ports[0];
- /* Let libata fill in the port details */
- ata_std_ports(&probe[0].port[0]);
- ata_std_ports(&probe[1].port[0]);
+ if (ata_port_is_dummy(ap))
+ continue;
- /* Now add the ports that are active */
- if (pcicfg & 1)
- ports += ata_device_add(&probe[0]);
- if (pcicfg & 2)
- ports += ata_device_add(&probe[1]);
- if (ports)
- return 0;
- return -ENODEV;
+ rc = devm_request_irq(&pdev->dev, irq[ap->port_no],
+ ata_interrupt, 0, DRV_NAME, host);
+ if (rc)
+ return rc;
+ }
+
+ return ata_host_register(host, &cs5520_sht);
}
/**
diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c
index db63e80..29642d5 100644
--- a/drivers/ata/pata_cs5530.c
+++ b/drivers/ata/pata_cs5530.c
@@ -160,18 +160,6 @@
return ata_qc_issue_prot(qc);
}
-static int cs5530_pre_reset(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_PATA40;
- return ata_std_prereset(ap);
-}
-
-static void cs5530_error_handler(struct ata_port *ap)
-{
- return ata_bmdma_drive_eh(ap, cs5530_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-
static struct scsi_host_template cs5530_sht = {
.module = THIS_MODULE,
.name = DRV_NAME,
@@ -213,8 +201,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = cs5530_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = cs5530_qc_issue_prot,
diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c
index 1572e5c..08cccc9 100644
--- a/drivers/ata/pata_cs5535.c
+++ b/drivers/ata/pata_cs5535.c
@@ -70,36 +70,23 @@
#define CS5535_BAD_PIO(timings) ( (timings&~0x80000000UL)==0x00009172 )
/**
- * cs5535_pre_reset - detect cable type
+ * cs5535_cable_detect - detect cable type
* @ap: Port to detect on
*
* Perform cable detection for ATA66 capable cable. Return a libata
* cable type.
*/
-static int cs5535_pre_reset(struct ata_port *ap)
+static int cs5535_cable_detect(struct ata_port *ap)
{
u8 cable;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
pci_read_config_byte(pdev, CS5535_CABLE_DETECT, &cable);
if (cable & 1)
- ap->cbl = ATA_CBL_PATA80;
+ return ATA_CBL_PATA80;
else
- ap->cbl = ATA_CBL_PATA40;
- return ata_std_prereset(ap);
-}
-
-/**
- * cs5535_error_handler - reset/probe
- * @ap: Port to reset
- *
- * Reset and configure a port
- */
-
-static void cs5535_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, cs5535_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
+ return ATA_CBL_PATA40;
}
/**
@@ -205,8 +192,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = cs5535_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = cs5535_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c
index f69dde5..6ec049c 100644
--- a/drivers/ata/pata_cypress.c
+++ b/drivers/ata/pata_cypress.c
@@ -41,17 +41,6 @@
CY82_INDEX_TIMEOUT = 0x32
};
-static int cy82c693_pre_reset(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_PATA40;
- return ata_std_prereset(ap);
-}
-
-static void cy82c693_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, cy82c693_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
/**
* cy82c693_set_piomode - set initial PIO mode data
* @ap: ATA interface
@@ -156,8 +145,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = cy82c693_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c
index dac7a65..a321685 100644
--- a/drivers/ata/pata_efar.c
+++ b/drivers/ata/pata_efar.c
@@ -22,10 +22,10 @@
#include <linux/ata.h>
#define DRV_NAME "pata_efar"
-#define DRV_VERSION "0.4.3"
+#define DRV_VERSION "0.4.4"
/**
- * efar_pre_reset - check for 40/80 pin
+ * efar_pre_reset - Enable bits
* @ap: Port
*
* Perform cable detection for the EFAR ATA interface. This is
@@ -38,18 +38,11 @@
{ 0x41U, 1U, 0x80UL, 0x80UL }, /* port 0 */
{ 0x43U, 1U, 0x80UL, 0x80UL }, /* port 1 */
};
-
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- u8 tmp;
if (!pci_test_config_bits(pdev, &efar_enable_bits[ap->port_no]))
return -ENOENT;
- pci_read_config_byte(pdev, 0x47, &tmp);
- if (tmp & (2 >> ap->port_no))
- ap->cbl = ATA_CBL_PATA40;
- else
- ap->cbl = ATA_CBL_PATA80;
return ata_std_prereset(ap);
}
@@ -67,6 +60,25 @@
}
/**
+ * efar_cable_detect - check for 40/80 pin
+ * @ap: Port
+ *
+ * Perform cable detection for the EFAR ATA interface. This is
+ * different to the PIIX arrangement
+ */
+
+static int efar_cable_detect(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u8 tmp;
+
+ pci_read_config_byte(pdev, 0x47, &tmp);
+ if (tmp & (2 >> ap->port_no))
+ return ATA_CBL_PATA40;
+ return ATA_CBL_PATA80;
+}
+
+/**
* efar_set_piomode - Initialize host controller PATA PIO timings
* @ap: Port whose timings we are configuring
* @adev: um
@@ -256,6 +268,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = efar_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = efar_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c
index baf35f8..93cfa6d 100644
--- a/drivers/ata/pata_hpt366.c
+++ b/drivers/ata/pata_hpt366.c
@@ -27,7 +27,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_hpt366"
-#define DRV_VERSION "0.6.0"
+#define DRV_VERSION "0.6.1"
struct hpt_clock {
u8 xfer_speed;
@@ -169,13 +169,12 @@
/**
* hpt366_filter - mode selection filter
- * @ap: ATA interface
* @adev: ATA device
*
* Block UDMA on devices that cause trouble with this controller.
*/
-static unsigned long hpt366_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long hpt366_filter(struct ata_device *adev, unsigned long mask)
{
if (adev->class == ATA_DEV_ATA) {
if (hpt_dma_blacklisted(adev, "UDMA", bad_ata33))
@@ -185,7 +184,7 @@
if (hpt_dma_blacklisted(adev, "UDMA4", bad_ata66_4))
mask &= ~(0x0F << ATA_SHIFT_UDMA);
}
- return ata_pci_default_filter(ap, adev, mask);
+ return ata_pci_default_filter(adev, mask);
}
/**
@@ -210,24 +209,28 @@
return 0xffffffffU; /* silence compiler warning */
}
+static int hpt36x_cable_detect(struct ata_port *ap)
+{
+ u8 ata66;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+
+ pci_read_config_byte(pdev, 0x5A, &ata66);
+ if (ata66 & (1 << ap->port_no))
+ return ATA_CBL_PATA40;
+ return ATA_CBL_PATA80;
+}
+
static int hpt36x_pre_reset(struct ata_port *ap)
{
static const struct pci_bits hpt36x_enable_bits[] = {
{ 0x50, 1, 0x04, 0x04 },
{ 0x54, 1, 0x04, 0x04 }
};
-
- u8 ata66;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
if (!pci_test_config_bits(pdev, &hpt36x_enable_bits[ap->port_no]))
return -ENOENT;
- pci_read_config_byte(pdev, 0x5A, &ata66);
- if (ata66 & (1 << ap->port_no))
- ap->cbl = ATA_CBL_PATA40;
- else
- ap->cbl = ATA_CBL_PATA80;
return ata_std_prereset(ap);
}
@@ -354,6 +357,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = hpt36x_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = hpt36x_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c
index f331eee..41d83129 100644
--- a/drivers/ata/pata_hpt37x.c
+++ b/drivers/ata/pata_hpt37x.c
@@ -8,6 +8,7 @@
* Copyright (C) 1999-2003 Andre Hedrick <andre@linux-ide.org>
* Portions Copyright (C) 2001 Sun Microsystems, Inc.
* Portions Copyright (C) 2003 Red Hat Inc
+ * Portions Copyright (C) 2005-2006 MontaVista Software, Inc.
*
* TODO
* PLL mode
@@ -25,7 +26,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_hpt37x"
-#define DRV_VERSION "0.6.0"
+#define DRV_VERSION "0.6.5"
struct hpt_clock {
u8 xfer_speed;
@@ -61,201 +62,75 @@
* 31 FIFO enable.
*/
-/* from highpoint documentation. these are old values */
-static const struct hpt_clock hpt370_timings_33[] = {
-/* { XFER_UDMA_5, 0x1A85F442, 0x16454e31 }, */
- { XFER_UDMA_5, 0x16454e31 },
- { XFER_UDMA_4, 0x16454e31 },
- { XFER_UDMA_3, 0x166d4e31 },
- { XFER_UDMA_2, 0x16494e31 },
- { XFER_UDMA_1, 0x164d4e31 },
- { XFER_UDMA_0, 0x16514e31 },
+static struct hpt_clock hpt37x_timings_33[] = {
+ { XFER_UDMA_6, 0x12446231 }, /* 0x12646231 ?? */
+ { XFER_UDMA_5, 0x12446231 },
+ { XFER_UDMA_4, 0x12446231 },
+ { XFER_UDMA_3, 0x126c6231 },
+ { XFER_UDMA_2, 0x12486231 },
+ { XFER_UDMA_1, 0x124c6233 },
+ { XFER_UDMA_0, 0x12506297 },
- { XFER_MW_DMA_2, 0x26514e21 },
- { XFER_MW_DMA_1, 0x26514e33 },
- { XFER_MW_DMA_0, 0x26514e97 },
+ { XFER_MW_DMA_2, 0x22406c31 },
+ { XFER_MW_DMA_1, 0x22406c33 },
+ { XFER_MW_DMA_0, 0x22406c97 },
- { XFER_PIO_4, 0x06514e21 },
- { XFER_PIO_3, 0x06514e22 },
- { XFER_PIO_2, 0x06514e33 },
- { XFER_PIO_1, 0x06914e43 },
- { XFER_PIO_0, 0x06914e57 },
- { 0, 0x06514e57 }
+ { XFER_PIO_4, 0x06414e31 },
+ { XFER_PIO_3, 0x06414e42 },
+ { XFER_PIO_2, 0x06414e53 },
+ { XFER_PIO_1, 0x06814e93 },
+ { XFER_PIO_0, 0x06814ea7 }
};
-static const struct hpt_clock hpt370_timings_66[] = {
- { XFER_UDMA_5, 0x14846231 },
- { XFER_UDMA_4, 0x14886231 },
- { XFER_UDMA_3, 0x148c6231 },
- { XFER_UDMA_2, 0x148c6231 },
- { XFER_UDMA_1, 0x14906231 },
- { XFER_UDMA_0, 0x14986231 },
+static struct hpt_clock hpt37x_timings_50[] = {
+ { XFER_UDMA_6, 0x12848242 },
+ { XFER_UDMA_5, 0x12848242 },
+ { XFER_UDMA_4, 0x12ac8242 },
+ { XFER_UDMA_3, 0x128c8242 },
+ { XFER_UDMA_2, 0x120c8242 },
+ { XFER_UDMA_1, 0x12148254 },
+ { XFER_UDMA_0, 0x121882ea },
- { XFER_MW_DMA_2, 0x26514e21 },
- { XFER_MW_DMA_1, 0x26514e33 },
- { XFER_MW_DMA_0, 0x26514e97 },
+ { XFER_MW_DMA_2, 0x22808242 },
+ { XFER_MW_DMA_1, 0x22808254 },
+ { XFER_MW_DMA_0, 0x228082ea },
- { XFER_PIO_4, 0x06514e21 },
- { XFER_PIO_3, 0x06514e22 },
- { XFER_PIO_2, 0x06514e33 },
- { XFER_PIO_1, 0x06914e43 },
- { XFER_PIO_0, 0x06914e57 },
- { 0, 0x06514e57 }
+ { XFER_PIO_4, 0x0a81f442 },
+ { XFER_PIO_3, 0x0a81f443 },
+ { XFER_PIO_2, 0x0a81f454 },
+ { XFER_PIO_1, 0x0ac1f465 },
+ { XFER_PIO_0, 0x0ac1f48a }
};
-/* these are the current (4 sep 2001) timings from highpoint */
-static const struct hpt_clock hpt370a_timings_33[] = {
- { XFER_UDMA_5, 0x12446231 },
- { XFER_UDMA_4, 0x12446231 },
- { XFER_UDMA_3, 0x126c6231 },
- { XFER_UDMA_2, 0x12486231 },
- { XFER_UDMA_1, 0x124c6233 },
- { XFER_UDMA_0, 0x12506297 },
+static struct hpt_clock hpt37x_timings_66[] = {
+ { XFER_UDMA_6, 0x1c869c62 },
+ { XFER_UDMA_5, 0x1cae9c62 }, /* 0x1c8a9c62 */
+ { XFER_UDMA_4, 0x1c8a9c62 },
+ { XFER_UDMA_3, 0x1c8e9c62 },
+ { XFER_UDMA_2, 0x1c929c62 },
+ { XFER_UDMA_1, 0x1c9a9c62 },
+ { XFER_UDMA_0, 0x1c829c62 },
- { XFER_MW_DMA_2, 0x22406c31 },
- { XFER_MW_DMA_1, 0x22406c33 },
- { XFER_MW_DMA_0, 0x22406c97 },
+ { XFER_MW_DMA_2, 0x2c829c62 },
+ { XFER_MW_DMA_1, 0x2c829c66 },
+ { XFER_MW_DMA_0, 0x2c829d2e },
- { XFER_PIO_4, 0x06414e31 },
- { XFER_PIO_3, 0x06414e42 },
- { XFER_PIO_2, 0x06414e53 },
- { XFER_PIO_1, 0x06814e93 },
- { XFER_PIO_0, 0x06814ea7 },
- { 0, 0x06814ea7 }
+ { XFER_PIO_4, 0x0c829c62 },
+ { XFER_PIO_3, 0x0c829c84 },
+ { XFER_PIO_2, 0x0c829ca6 },
+ { XFER_PIO_1, 0x0d029d26 },
+ { XFER_PIO_0, 0x0d029d5e }
};
-/* 2x 33MHz timings */
-static const struct hpt_clock hpt370a_timings_66[] = {
- { XFER_UDMA_5, 0x1488e673 },
- { XFER_UDMA_4, 0x1488e673 },
- { XFER_UDMA_3, 0x1498e673 },
- { XFER_UDMA_2, 0x1490e673 },
- { XFER_UDMA_1, 0x1498e677 },
- { XFER_UDMA_0, 0x14a0e73f },
-
- { XFER_MW_DMA_2, 0x2480fa73 },
- { XFER_MW_DMA_1, 0x2480fa77 },
- { XFER_MW_DMA_0, 0x2480fb3f },
-
- { XFER_PIO_4, 0x0c82be73 },
- { XFER_PIO_3, 0x0c82be95 },
- { XFER_PIO_2, 0x0c82beb7 },
- { XFER_PIO_1, 0x0d02bf37 },
- { XFER_PIO_0, 0x0d02bf5f },
- { 0, 0x0d02bf5f }
-};
-
-static const struct hpt_clock hpt370a_timings_50[] = {
- { XFER_UDMA_5, 0x12848242 },
- { XFER_UDMA_4, 0x12ac8242 },
- { XFER_UDMA_3, 0x128c8242 },
- { XFER_UDMA_2, 0x120c8242 },
- { XFER_UDMA_1, 0x12148254 },
- { XFER_UDMA_0, 0x121882ea },
-
- { XFER_MW_DMA_2, 0x22808242 },
- { XFER_MW_DMA_1, 0x22808254 },
- { XFER_MW_DMA_0, 0x228082ea },
-
- { XFER_PIO_4, 0x0a81f442 },
- { XFER_PIO_3, 0x0a81f443 },
- { XFER_PIO_2, 0x0a81f454 },
- { XFER_PIO_1, 0x0ac1f465 },
- { XFER_PIO_0, 0x0ac1f48a },
- { 0, 0x0ac1f48a }
-};
-
-static const struct hpt_clock hpt372_timings_33[] = {
- { XFER_UDMA_6, 0x1c81dc62 },
- { XFER_UDMA_5, 0x1c6ddc62 },
- { XFER_UDMA_4, 0x1c8ddc62 },
- { XFER_UDMA_3, 0x1c8edc62 }, /* checkme */
- { XFER_UDMA_2, 0x1c91dc62 },
- { XFER_UDMA_1, 0x1c9adc62 }, /* checkme */
- { XFER_UDMA_0, 0x1c82dc62 }, /* checkme */
-
- { XFER_MW_DMA_2, 0x2c829262 },
- { XFER_MW_DMA_1, 0x2c829266 }, /* checkme */
- { XFER_MW_DMA_0, 0x2c82922e }, /* checkme */
-
- { XFER_PIO_4, 0x0c829c62 },
- { XFER_PIO_3, 0x0c829c84 },
- { XFER_PIO_2, 0x0c829ca6 },
- { XFER_PIO_1, 0x0d029d26 },
- { XFER_PIO_0, 0x0d029d5e },
- { 0, 0x0d029d5e }
-};
-
-static const struct hpt_clock hpt372_timings_50[] = {
- { XFER_UDMA_5, 0x12848242 },
- { XFER_UDMA_4, 0x12ac8242 },
- { XFER_UDMA_3, 0x128c8242 },
- { XFER_UDMA_2, 0x120c8242 },
- { XFER_UDMA_1, 0x12148254 },
- { XFER_UDMA_0, 0x121882ea },
-
- { XFER_MW_DMA_2, 0x22808242 },
- { XFER_MW_DMA_1, 0x22808254 },
- { XFER_MW_DMA_0, 0x228082ea },
-
- { XFER_PIO_4, 0x0a81f442 },
- { XFER_PIO_3, 0x0a81f443 },
- { XFER_PIO_2, 0x0a81f454 },
- { XFER_PIO_1, 0x0ac1f465 },
- { XFER_PIO_0, 0x0ac1f48a },
- { 0, 0x0a81f443 }
-};
-
-static const struct hpt_clock hpt372_timings_66[] = {
- { XFER_UDMA_6, 0x1c869c62 },
- { XFER_UDMA_5, 0x1cae9c62 },
- { XFER_UDMA_4, 0x1c8a9c62 },
- { XFER_UDMA_3, 0x1c8e9c62 },
- { XFER_UDMA_2, 0x1c929c62 },
- { XFER_UDMA_1, 0x1c9a9c62 },
- { XFER_UDMA_0, 0x1c829c62 },
-
- { XFER_MW_DMA_2, 0x2c829c62 },
- { XFER_MW_DMA_1, 0x2c829c66 },
- { XFER_MW_DMA_0, 0x2c829d2e },
-
- { XFER_PIO_4, 0x0c829c62 },
- { XFER_PIO_3, 0x0c829c84 },
- { XFER_PIO_2, 0x0c829ca6 },
- { XFER_PIO_1, 0x0d029d26 },
- { XFER_PIO_0, 0x0d029d5e },
- { 0, 0x0d029d26 }
-};
-
-static const struct hpt_clock hpt374_timings_33[] = {
- { XFER_UDMA_6, 0x12808242 },
- { XFER_UDMA_5, 0x12848242 },
- { XFER_UDMA_4, 0x12ac8242 },
- { XFER_UDMA_3, 0x128c8242 },
- { XFER_UDMA_2, 0x120c8242 },
- { XFER_UDMA_1, 0x12148254 },
- { XFER_UDMA_0, 0x121882ea },
-
- { XFER_MW_DMA_2, 0x22808242 },
- { XFER_MW_DMA_1, 0x22808254 },
- { XFER_MW_DMA_0, 0x228082ea },
-
- { XFER_PIO_4, 0x0a81f442 },
- { XFER_PIO_3, 0x0a81f443 },
- { XFER_PIO_2, 0x0a81f454 },
- { XFER_PIO_1, 0x0ac1f465 },
- { XFER_PIO_0, 0x0ac1f48a },
- { 0, 0x06814e93 }
-};
static const struct hpt_chip hpt370 = {
"HPT370",
48,
{
- hpt370_timings_33,
+ hpt37x_timings_33,
NULL,
NULL,
- hpt370_timings_66
+ NULL
}
};
@@ -263,10 +138,10 @@
"HPT370A",
48,
{
- hpt370a_timings_33,
+ hpt37x_timings_33,
NULL,
- hpt370a_timings_50,
- hpt370a_timings_66
+ hpt37x_timings_50,
+ NULL
}
};
@@ -274,10 +149,10 @@
"HPT372",
55,
{
- hpt372_timings_33,
+ hpt37x_timings_33,
NULL,
- hpt372_timings_50,
- hpt372_timings_66
+ hpt37x_timings_50,
+ hpt37x_timings_66
}
};
@@ -285,10 +160,10 @@
"HPT302",
66,
{
- hpt372_timings_33,
+ hpt37x_timings_33,
NULL,
- hpt372_timings_50,
- hpt372_timings_66
+ hpt37x_timings_50,
+ hpt37x_timings_66
}
};
@@ -296,10 +171,10 @@
"HPT371",
66,
{
- hpt372_timings_33,
+ hpt37x_timings_33,
NULL,
- hpt372_timings_50,
- hpt372_timings_66
+ hpt37x_timings_50,
+ hpt37x_timings_66
}
};
@@ -307,10 +182,10 @@
"HPT372A",
66,
{
- hpt372_timings_33,
+ hpt37x_timings_33,
NULL,
- hpt372_timings_50,
- hpt372_timings_66
+ hpt37x_timings_50,
+ hpt37x_timings_66
}
};
@@ -318,7 +193,7 @@
"HPT374",
48,
{
- hpt374_timings_33,
+ hpt37x_timings_33,
NULL,
NULL,
NULL
@@ -397,13 +272,12 @@
/**
* hpt370_filter - mode selection filter
- * @ap: ATA interface
* @adev: ATA device
*
* Block UDMA on devices that cause trouble with this controller.
*/
-static unsigned long hpt370_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long hpt370_filter(struct ata_device *adev, unsigned long mask)
{
if (adev->class == ATA_DEV_ATA) {
if (hpt_dma_blacklisted(adev, "UDMA", bad_ata33))
@@ -411,24 +285,23 @@
if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5))
mask &= ~(0x1F << ATA_SHIFT_UDMA);
}
- return ata_pci_default_filter(ap, adev, mask);
+ return ata_pci_default_filter(adev, mask);
}
/**
* hpt370a_filter - mode selection filter
- * @ap: ATA interface
* @adev: ATA device
*
* Block UDMA on devices that cause trouble with this controller.
*/
-static unsigned long hpt370a_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long hpt370a_filter(struct ata_device *adev, unsigned long mask)
{
if (adev->class != ATA_DEV_ATA) {
if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5))
mask &= ~ (0x1F << ATA_SHIFT_UDMA);
}
- return ata_pci_default_filter(ap, adev, mask);
+ return ata_pci_default_filter(adev, mask);
}
/**
@@ -462,8 +335,7 @@
ap->cbl = ATA_CBL_PATA80;
/* Reset the state machine */
- pci_write_config_byte(pdev, 0x50, 0x37);
- pci_write_config_byte(pdev, 0x54, 0x37);
+ pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37);
udelay(100);
return ata_std_prereset(ap);
@@ -513,8 +385,7 @@
ap->cbl = ATA_CBL_PATA80;
/* Reset the state machine */
- pci_write_config_byte(pdev, 0x50, 0x37);
- pci_write_config_byte(pdev, 0x54, 0x37);
+ pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37);
udelay(100);
return ata_std_prereset(ap);
@@ -1032,6 +903,24 @@
.udma_mask = 0x3f,
.port_ops = &hpt370a_port_ops
};
+ /* HPT370 - UDMA100 */
+ static struct ata_port_info info_hpt370_33 = {
+ .sht = &hpt37x_sht,
+ .flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST,
+ .pio_mask = 0x1f,
+ .mwdma_mask = 0x07,
+ .udma_mask = 0x0f,
+ .port_ops = &hpt370_port_ops
+ };
+ /* HPT370A - UDMA100 */
+ static struct ata_port_info info_hpt370a_33 = {
+ .sht = &hpt37x_sht,
+ .flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST,
+ .pio_mask = 0x1f,
+ .mwdma_mask = 0x07,
+ .udma_mask = 0x0f,
+ .port_ops = &hpt370a_port_ops
+ };
/* HPT371, 372 and friends - UDMA133 */
static struct ata_port_info info_hpt372 = {
.sht = &hpt37x_sht,
@@ -1067,7 +956,11 @@
u8 irqmask;
u32 class_rev;
+ u8 mcr1;
u32 freq;
+ int prefer_dpll = 1;
+
+ unsigned long iobase = pci_resource_start(dev, 4);
const struct hpt_chip *chip_table;
int clock_slot;
@@ -1088,10 +981,12 @@
case 3:
port = &info_hpt370;
chip_table = &hpt370;
+ prefer_dpll = 0;
break;
case 4:
port = &info_hpt370a;
chip_table = &hpt370a;
+ prefer_dpll = 0;
break;
case 5:
port = &info_hpt372;
@@ -1119,8 +1014,16 @@
chip_table = &hpt302;
break;
case PCI_DEVICE_ID_TTI_HPT371:
+ if (class_rev > 1)
+ return -ENODEV;
port = &info_hpt372;
chip_table = &hpt371;
+ /* Single channel device, master is not present
+ but the BIOS (or us for non x86) must mark it
+ absent */
+ pci_read_config_byte(dev, 0x50, &mcr1);
+ mcr1 &= ~0x04;
+ pci_write_config_byte(dev, 0x50, mcr1);
break;
case PCI_DEVICE_ID_TTI_HPT374:
chip_table = &hpt374;
@@ -1150,8 +1053,18 @@
*/
pci_write_config_byte(dev, 0x5b, 0x23);
+
+ /*
+ * HighPoint does this for HPT372A.
+ * NOTE: This register is only writeable via I/O space.
+ */
+ if (chip_table == &hpt372a)
+ outb(0x0e, iobase + 0x9c);
- pci_read_config_dword(dev, 0x70, &freq);
+ /* Some devices do not let this value be accessed via PCI space
+ according to the old driver */
+
+ freq = inl(iobase + 0x90);
if ((freq >> 12) != 0xABCDE) {
int i;
u8 sr;
@@ -1162,7 +1075,7 @@
/* This is the process the HPT371 BIOS is reported to use */
for(i = 0; i < 128; i++) {
pci_read_config_byte(dev, 0x78, &sr);
- total += sr;
+ total += sr & 0x1FF;
udelay(15);
}
freq = total / 128;
@@ -1173,15 +1086,27 @@
* Turn the frequency check into a band and then find a timing
* table to match it.
*/
-
+
clock_slot = hpt37x_clock_slot(freq, chip_table->base);
- if (chip_table->clocks[clock_slot] == NULL) {
+ if (chip_table->clocks[clock_slot] == NULL || prefer_dpll) {
/*
* We need to try PLL mode instead
+ *
+ * For non UDMA133 capable devices we should
+ * use a 50MHz DPLL by choice
*/
- unsigned int f_low = (MHz[clock_slot] * chip_table->base) / 192;
- unsigned int f_high = f_low + 2;
+ unsigned int f_low, f_high;
int adjust;
+
+ clock_slot = 2;
+ if (port->udma_mask & 0xE0)
+ clock_slot = 3;
+
+ f_low = (MHz[clock_slot] * chip_table->base) / 192;
+ f_high = f_low + 2;
+
+ /* Select the DPLL clock. */
+ pci_write_config_byte(dev, 0x5b, 0x21);
for(adjust = 0; adjust < 8; adjust++) {
if (hpt37x_calibrate_dpll(dev))
@@ -1197,25 +1122,27 @@
printk(KERN_WARNING "hpt37x: DPLL did not stabilize.\n");
return -ENODEV;
}
- /* Check if this works for all cases */
- port->private_data = (void *)hpt370_timings_66;
+ if (clock_slot == 3)
+ port->private_data = (void *)hpt37x_timings_66;
+ else
+ port->private_data = (void *)hpt37x_timings_50;
printk(KERN_INFO "hpt37x: Bus clock %dMHz, using DPLL.\n", MHz[clock_slot]);
} else {
port->private_data = (void *)chip_table->clocks[clock_slot];
/*
- * Perform a final fixup. The 371 and 372 clock determines
- * if UDMA133 is available.
- */
+ * Perform a final fixup. Note that we will have used the
+ * DPLL on the HPT372 which means we don't have to worry
+ * about lack of UDMA133 support on lower clocks
+ */
- if (clock_slot == 2 && chip_table == &hpt372) { /* 50Mhz */
- printk(KERN_WARNING "pata_hpt37x: No UDMA133 support available with 50MHz bus clock.\n");
- if (port == &info_hpt372)
- port = &info_hpt372_50;
- else BUG();
- }
+ if (clock_slot < 2 && port == &info_hpt370)
+ port = &info_hpt370_33;
+ if (clock_slot < 2 && port == &info_hpt370a)
+ port = &info_hpt370a_33;
printk(KERN_INFO "hpt37x: %s: Bus clock %dMHz.\n", chip_table->name, MHz[clock_slot]);
}
+
port_info[0] = port_info[1] = port;
/* Now kick off ATA set up */
return ata_pci_init_one(dev, port_info, 2);
diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c
index 65f2e18..6a34521 100644
--- a/drivers/ata/pata_hpt3x2n.c
+++ b/drivers/ata/pata_hpt3x2n.c
@@ -8,10 +8,10 @@
* Copyright (C) 1999-2003 Andre Hedrick <andre@linux-ide.org>
* Portions Copyright (C) 2001 Sun Microsystems, Inc.
* Portions Copyright (C) 2003 Red Hat Inc
+ * Portions Copyright (C) 2005-2006 MontaVista Software, Inc.
*
*
* TODO
- * 371N
* Work out best PLL policy
*/
@@ -25,7 +25,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_hpt3x2n"
-#define DRV_VERSION "0.3.2"
+#define DRV_VERSION "0.3.3"
enum {
HPT_PCI_FAST = (1 << 31),
@@ -115,14 +115,13 @@
}
/**
- * hpt3x2n_pre_reset - reset the hpt3x2n bus
- * @ap: ATA port to reset
+ * hpt3x2n_cable_detect - Detect the cable type
+ * @ap: ATA port to detect on
*
- * Perform the initial reset handling for the 3x2n series controllers.
- * Reset the hardware and state machine, obtain the cable type.
+ * Return the cable type attached to this port
*/
-static int hpt3xn_pre_reset(struct ata_port *ap)
+static int hpt3x2n_cable_detect(struct ata_port *ap)
{
u8 scr2, ata66;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
@@ -135,15 +134,26 @@
pci_write_config_byte(pdev, 0x5B, scr2);
if (ata66 & (1 << ap->port_no))
- ap->cbl = ATA_CBL_PATA40;
+ return ATA_CBL_PATA40;
else
- ap->cbl = ATA_CBL_PATA80;
+ return ATA_CBL_PATA80;
+}
+/**
+ * hpt3x2n_pre_reset - reset the hpt3x2n bus
+ * @ap: ATA port to reset
+ * @deadline: deadline jiffies for the operation
+ *
+ * Perform the initial reset handling for the 3x2n series controllers.
+ * Reset the hardware and state machine,
+ */
+
+static int hpt3xn_pre_reset(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
/* Reset the state machine */
- pci_write_config_byte(pdev, 0x50, 0x37);
- pci_write_config_byte(pdev, 0x54, 0x37);
+ pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37);
udelay(100);
-
return ata_std_prereset(ap);
}
@@ -364,6 +374,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = hpt3x2n_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = hpt3x2n_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -422,8 +433,9 @@
{
unsigned long freq;
u32 fcnt;
+ unsigned long iobase = pci_resource_start(pdev, 4);
- pci_read_config_dword(pdev, 0x70/*CHECKME*/, &fcnt);
+ fcnt = inl(iobase + 0x90); /* Not PCI readable for some chips */
if ((fcnt >> 12) != 0xABCDE) {
printk(KERN_WARNING "hpt3xn: BIOS clock data not set.\n");
return 33; /* Not BIOS set */
@@ -492,6 +504,7 @@
unsigned int pci_mhz;
unsigned int f_low, f_high;
int adjust;
+ unsigned long iobase = pci_resource_start(dev, 4);
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
class_rev &= 0xFF;
@@ -501,6 +514,11 @@
if (class_rev < 6)
return -ENODEV;
break;
+ case PCI_DEVICE_ID_TTI_HPT371:
+ if (class_rev < 2)
+ return -ENODEV;
+ /* 371N if rev > 1 */
+ break;
case PCI_DEVICE_ID_TTI_HPT372:
/* 372N if rev >= 1*/
if (class_rev == 0)
@@ -528,6 +546,19 @@
irqmask &= ~0x10;
pci_write_config_byte(dev, 0x5a, irqmask);
+ /*
+ * HPT371 chips physically have only one channel, the secondary one,
+ * but the primary channel registers do exist! Go figure...
+ * So, we manually disable the non-existing channel here
+ * (if the BIOS hasn't done this already).
+ */
+ if (dev->device == PCI_DEVICE_ID_TTI_HPT371) {
+ u8 mcr1;
+ pci_read_config_byte(dev, 0x50, &mcr1);
+ mcr1 &= ~0x04;
+ pci_write_config_byte(dev, 0x50, mcr1);
+ }
+
/* Tune the PLL. HPT recommend using 75 for SATA, 66 for UDMA133 or
50 for UDMA100. Right now we always use 66 */
@@ -546,14 +577,24 @@
break;
pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low);
}
- if (adjust == 8)
- printk(KERN_WARNING "hpt3xn: DPLL did not stabilize.\n");
+ if (adjust == 8) {
+ printk(KERN_WARNING "hpt3x2n: DPLL did not stabilize.\n");
+ return -ENODEV;
+ }
/* Set our private data up. We only need a few flags so we use
it directly */
port->private_data = NULL;
- if (pci_mhz > 60)
+ if (pci_mhz > 60) {
port->private_data = (void *)PCI66;
+ /*
+ * On HPT371N, if ATA clock is 66 MHz we must set bit 2 in
+ * the MISC. register to stretch the UltraDMA Tss timing.
+ * NOTE: This register is only writeable via I/O space.
+ */
+ if (dev->device == PCI_DEVICE_ID_TTI_HPT371)
+ outb(inb(iobase + 0x9c) | 0x04, iobase + 0x9c);
+ }
/* Now kick off ATA set up */
port_info[0] = port_info[1] = port;
@@ -562,6 +603,7 @@
static const struct pci_device_id hpt3x2n[] = {
{ PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), },
+ { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT371), },
{ PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372), },
{ PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT302), },
{ PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372N), },
diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c
index 813485c..ac28ec8 100644
--- a/drivers/ata/pata_hpt3x3.c
+++ b/drivers/ata/pata_hpt3x3.c
@@ -25,25 +25,6 @@
#define DRV_NAME "pata_hpt3x3"
#define DRV_VERSION "0.4.2"
-static int hpt3x3_probe_init(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_PATA40;
- return ata_std_prereset(ap);
-}
-
-/**
- * hpt3x3_probe_reset - reset the hpt3x3 bus
- * @ap: ATA port to reset
- *
- * Perform the housekeeping when doing an ATA bus reeset. We just
- * need to force the cable type.
- */
-
-static void hpt3x3_error_handler(struct ata_port *ap)
-{
- return ata_bmdma_drive_eh(ap, hpt3x3_probe_init, ata_std_softreset, NULL, ata_std_postreset);
-}
-
/**
* hpt3x3_set_piomode - PIO setup
* @ap: ATA interface
@@ -139,8 +120,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = hpt3x3_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c
index 1a61cc8..d042efd 100644
--- a/drivers/ata/pata_isapnp.c
+++ b/drivers/ata/pata_isapnp.c
@@ -49,13 +49,13 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_data_xfer,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -74,8 +74,10 @@
static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev_id)
{
- struct ata_probe_ent ae;
+ struct ata_host *host;
+ struct ata_port *ap;
void __iomem *cmd_addr, *ctl_addr;
+ int rc;
if (pnp_port_valid(idev, 0) == 0)
return -ENODEV;
@@ -84,34 +86,36 @@
if (pnp_irq_valid(idev, 0) == 0)
return -ENODEV;
+ /* allocate host */
+ host = ata_host_alloc(&idev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+
+ /* acquire resources and fill host */
cmd_addr = devm_ioport_map(&idev->dev, pnp_port_start(idev, 0), 8);
if (!cmd_addr)
return -ENOMEM;
- memset(&ae, 0, sizeof(struct ata_probe_ent));
- INIT_LIST_HEAD(&ae.node);
- ae.dev = &idev->dev;
- ae.port_ops = &isapnp_port_ops;
- ae.sht = &isapnp_sht;
- ae.n_ports = 1;
- ae.pio_mask = 1; /* ISA so PIO 0 cycles */
- ae.irq = pnp_irq(idev, 0);
- ae.irq_flags = 0;
- ae.port_flags = ATA_FLAG_SLAVE_POSS;
- ae.port[0].cmd_addr = cmd_addr;
+ ap = host->ports[0];
+
+ ap->ops = &isapnp_port_ops;
+ ap->pio_mask = 1;
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
+
+ ap->ioaddr.cmd_addr = cmd_addr;
if (pnp_port_valid(idev, 1) == 0) {
ctl_addr = devm_ioport_map(&idev->dev,
pnp_port_start(idev, 1), 1);
- ae.port[0].altstatus_addr = ctl_addr;
- ae.port[0].ctl_addr = ctl_addr;
- ae.port_flags |= ATA_FLAG_SRST;
+ ap->ioaddr.altstatus_addr = ctl_addr;
+ ap->ioaddr.ctl_addr = ctl_addr;
}
- ata_std_ports(&ae.port[0]);
- if (ata_device_add(&ae) == 0)
- return -ENODEV;
- return 0;
+ ata_std_ports(&ap->ioaddr);
+
+ /* activate */
+ return ata_host_activate(host, pnp_irq(idev, 0), ata_interrupt, 0,
+ &isapnp_sht);
}
/**
diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c
index ea73470..011306e 100644
--- a/drivers/ata/pata_it8213.c
+++ b/drivers/ata/pata_it8213.c
@@ -25,8 +25,8 @@
* it8213_pre_reset - check for 40/80 pin
* @ap: Port
*
- * Perform cable detection for the 8213 ATA interface. This is
- * different to the PIIX arrangement
+ * Filter out ports by the enable bits before doing the normal reset
+ * and probe.
*/
static int it8213_pre_reset(struct ata_port *ap)
@@ -34,23 +34,14 @@
static const struct pci_bits it8213_enable_bits[] = {
{ 0x41U, 1U, 0x80UL, 0x80UL }, /* port 0 */
};
-
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- u8 tmp;
-
if (!pci_test_config_bits(pdev, &it8213_enable_bits[ap->port_no]))
return -ENOENT;
-
- pci_read_config_byte(pdev, 0x42, &tmp);
- if (tmp & 2) /* The initial docs are incorrect */
- ap->cbl = ATA_CBL_PATA40;
- else
- ap->cbl = ATA_CBL_PATA80;
return ata_std_prereset(ap);
}
/**
- * it8213_probe_reset - Probe specified port on PATA host controller
+ * it8213_error_handler - Probe specified port on PATA host controller
* @ap: Port to probe
*
* LOCKING:
@@ -63,9 +54,27 @@
}
/**
+ * it8213_cable_detect - check for 40/80 pin
+ * @ap: Port
+ *
+ * Perform cable detection for the 8213 ATA interface. This is
+ * different to the PIIX arrangement
+ */
+
+static int it8213_cable_detect(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u8 tmp;
+ pci_read_config_byte(pdev, 0x42, &tmp);
+ if (tmp & 2) /* The initial docs are incorrect */
+ return ATA_CBL_PATA40;
+ return ATA_CBL_PATA80;
+}
+
+/**
* it8213_set_piomode - Initialize host controller PATA PIO timings
* @ap: Port whose timings we are configuring
- * @adev: um
+ * @adev: Device whose timings we are configuring
*
* Set PIO mode for device, in host controller PCI config space.
*
@@ -268,6 +277,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = it8213_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = it8213_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
index 35ecb2b..f1f8cec 100644
--- a/drivers/ata/pata_it821x.c
+++ b/drivers/ata/pata_it821x.c
@@ -80,7 +80,7 @@
#define DRV_NAME "pata_it821x"
-#define DRV_VERSION "0.3.4"
+#define DRV_VERSION "0.3.6"
struct it821x_dev
{
@@ -113,31 +113,6 @@
static int it8212_noraid;
/**
- * it821x_pre_reset - probe
- * @ap: ATA port
- *
- * Set the cable type
- */
-
-static int it821x_pre_reset(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_PATA80;
- return ata_std_prereset(ap);
-}
-
-/**
- * it821x_error_handler - probe/reset
- * @ap: ATA port
- *
- * Set the cable type and trigger a probe
- */
-
-static void it821x_error_handler(struct ata_port *ap)
-{
- return ata_bmdma_drive_eh(ap, it821x_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-/**
* it821x_program - program the PIO/MWDMA registers
* @ap: ATA port
* @adev: Device to program
@@ -520,7 +495,6 @@
/**
* it821x_dev_config - Called each device identify
- * @ap: ATA port
* @adev: Device that has just been identified
*
* Perform the initial setup needed for each device that is chip
@@ -531,7 +505,7 @@
* basically we need to filter commands for this chip.
*/
-static void it821x_dev_config(struct ata_port *ap, struct ata_device *adev)
+static void it821x_dev_config(struct ata_device *adev)
{
unsigned char model_num[ATA_ID_PROD_LEN + 1];
@@ -667,8 +641,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = it821x_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_unknown,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -703,8 +678,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = it821x_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_unknown,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = it821x_passthru_bmdma_start,
diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c
index c6f0e19..420c343 100644
--- a/drivers/ata/pata_ixp4xx_cf.c
+++ b/drivers/ata/pata_ixp4xx_cf.c
@@ -129,8 +129,8 @@
.qc_issue = ata_qc_issue_prot,
.eng_timeout = ata_eng_timeout,
.data_xfer = ixp4xx_mmio_data_xfer,
+ .cable_detect = ata_cable_40wire,
- .irq_handler = ata_interrupt,
.irq_clear = ixp4xx_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -173,12 +173,12 @@
static __devinit int ixp4xx_pata_probe(struct platform_device *pdev)
{
- int ret;
unsigned int irq;
struct resource *cs0, *cs1;
- struct ata_probe_ent ae;
-
+ struct ata_host *host;
+ struct ata_port *ap;
struct ixp4xx_pata_data *data = pdev->dev.platform_data;
+ int rc;
cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
@@ -186,6 +186,12 @@
if (!cs0 || !cs1)
return -EINVAL;
+ /* allocate host */
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+
+ /* acquire resources and fill host */
pdev->dev.coherent_dma_mask = DMA_32BIT_MASK;
data->cs0 = devm_ioremap(&pdev->dev, cs0->start, 0x1000);
@@ -199,32 +205,22 @@
*data->cs0_cfg = data->cs0_bits;
*data->cs1_cfg = data->cs1_bits;
- memset(&ae, 0, sizeof(struct ata_probe_ent));
- INIT_LIST_HEAD(&ae.node);
+ ap = host->ports[0];
- ae.dev = &pdev->dev;
- ae.port_ops = &ixp4xx_port_ops;
- ae.sht = &ixp4xx_sht;
- ae.n_ports = 1;
- ae.pio_mask = 0x1f; /* PIO4 */
- ae.irq = irq;
- ae.irq_flags = 0;
- ae.port_flags = ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY
- | ATA_FLAG_NO_ATAPI | ATA_FLAG_SRST;
+ ap->ops = &ixp4xx_port_ops;
+ ap->pio_mask = 0x1f; /* PIO4 */
+ ap->flags |= ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY | ATA_FLAG_NO_ATAPI;
/* run in polling mode if no irq has been assigned */
if (!irq)
- ae.port_flags |= ATA_FLAG_PIO_POLLING;
+ ap->flags |= ATA_FLAG_PIO_POLLING;
- ixp4xx_setup_port(&ae.port[0], data);
+ ixp4xx_setup_port(&ap->ioaddr, data);
dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
- ret = ata_device_add(&ae);
- if (ret == 0)
- return -ENODEV;
-
- return 0;
+ /* activate host */
+ return ata_host_activate(host, irq, ata_interrupt, 0, &ixp4xx_sht);
}
static __devexit int ixp4xx_pata_remove(struct platform_device *dev)
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c
index 86fbcd6..7070992 100644
--- a/drivers/ata/pata_legacy.c
+++ b/drivers/ata/pata_legacy.c
@@ -162,6 +162,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
@@ -185,6 +186,7 @@
.check_status = ata_check_status,
.exec_command = ata_exec_command,
.dev_select = ata_std_dev_select,
+ .cable_detect = ata_cable_40wire,
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
@@ -305,6 +307,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
@@ -360,6 +363,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
@@ -426,6 +430,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
@@ -547,6 +552,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
@@ -680,6 +686,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = opti82c46x_qc_issue_prot,
@@ -709,7 +716,8 @@
static __init int legacy_init_one(int port, unsigned long io, unsigned long ctrl, int irq)
{
struct legacy_data *ld = &legacy_data[nr_legacy_host];
- struct ata_probe_ent ae;
+ struct ata_host *host;
+ struct ata_port *ap;
struct platform_device *pdev;
struct ata_port_operations *ops = &legacy_port_ops;
void __iomem *io_addr, *ctrl_addr;
@@ -791,24 +799,23 @@
if (ops == &legacy_port_ops && (autospeed & mask))
ops = &simple_port_ops;
- memset(&ae, 0, sizeof(struct ata_probe_ent));
- INIT_LIST_HEAD(&ae.node);
- ae.dev = &pdev->dev;
- ae.port_ops = ops;
- ae.sht = &legacy_sht;
- ae.n_ports = 1;
- ae.pio_mask = pio_modes;
- ae.irq = irq;
- ae.irq_flags = 0;
- ae.port_flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST|iordy;
- ae.port[0].cmd_addr = io_addr;
- ae.port[0].altstatus_addr = ctrl_addr;
- ae.port[0].ctl_addr = ctrl_addr;
- ata_std_ports(&ae.port[0]);
- ae.private_data = ld;
+ ret = -ENOMEM;
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ goto fail;
+ ap = host->ports[0];
- ret = -ENODEV;
- if (!ata_device_add(&ae))
+ ap->ops = ops;
+ ap->pio_mask = pio_modes;
+ ap->flags |= ATA_FLAG_SLAVE_POSS | iordy;
+ ap->ioaddr.cmd_addr = io_addr;
+ ap->ioaddr.altstatus_addr = ctrl_addr;
+ ap->ioaddr.ctl_addr = ctrl_addr;
+ ata_std_ports(&ap->ioaddr);
+ ap->private_data = ld;
+
+ ret = ata_host_activate(host, irq, ata_interrupt, 0, &legacy_sht);
+ if (ret)
goto fail;
legacy_host[nr_legacy_host++] = dev_get_drvdata(&pdev->dev);
diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c
index 6dd7c4e..d9b94a1 100644
--- a/drivers/ata/pata_marvell.c
+++ b/drivers/ata/pata_marvell.c
@@ -20,7 +20,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_marvell"
-#define DRV_VERSION "0.1.1"
+#define DRV_VERSION "0.1.4"
/**
* marvell_pre_reset - check for 40/80 pin
@@ -52,22 +52,23 @@
if ((pdev->device == 0x6145) && (ap->port_no == 0) &&
(!(devices & 0x10))) /* PATA enable ? */
return -ENOENT;
+ return ata_std_prereset(ap);
+}
+static int marvell_cable_detect(struct ata_port *ap)
+{
/* Cable type */
switch(ap->port_no)
{
case 0:
if (ioread8(ap->ioaddr.bmdma_addr + 1) & 1)
- ap->cbl = ATA_CBL_PATA40;
- else
- ap->cbl = ATA_CBL_PATA80;
- break;
-
+ return ATA_CBL_PATA40;
+ return ATA_CBL_PATA80;
case 1: /* Legacy SATA port */
- ap->cbl = ATA_CBL_SATA;
- break;
+ return ATA_CBL_SATA;
}
- return ata_std_prereset(ap);
+ BUG();
+ return 0; /* Our BUG macro needs the right markup */
}
/**
@@ -123,6 +124,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = marvell_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = marvell_cable_detect,
/* BMDMA handling is PCI ATA format, use helpers */
.bmdma_setup = ata_bmdma_setup,
diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c
index 882c36e..9587a89 100644
--- a/drivers/ata/pata_mpc52xx.c
+++ b/drivers/ata/pata_mpc52xx.c
@@ -24,7 +24,7 @@
#define DRV_NAME "mpc52xx_ata"
-#define DRV_VERSION "0.1.0"
+#define DRV_VERSION "0.1.0ac2"
/* Private structures used by the driver */
@@ -297,38 +297,37 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
.error_handler = mpc52xx_ata_error_handler,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_data_xfer,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
.port_start = ata_port_start,
};
-static struct ata_probe_ent mpc52xx_ata_probe_ent = {
- .port_ops = &mpc52xx_ata_port_ops,
- .sht = &mpc52xx_ata_sht,
- .n_ports = 1,
- .pio_mask = 0x1f, /* Up to PIO4 */
- .mwdma_mask = 0x00, /* No MWDMA */
- .udma_mask = 0x00, /* No UDMA */
- .port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST,
- .irq_flags = 0,
-};
-
static int __devinit
mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv)
{
- struct ata_probe_ent *ae = &mpc52xx_ata_probe_ent;
- struct ata_ioports *aio = &ae->port[0];
- int rv;
+ struct ata_host *host;
+ struct ata_port *ap;
+ struct ata_ioports *aio;
+ int rc;
- INIT_LIST_HEAD(&ae->node);
- ae->dev = dev;
- ae->irq = priv->ata_irq;
+ host = ata_host_alloc(dev, 1);
+ if (!host)
+ return -ENOMEM;
+ ap = host->ports[0];
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
+ ap->pio_mask = 0x1f; /* Up to PIO4 */
+ ap->mwdma_mask = 0x00; /* No MWDMA */
+ ap->udma_mask = 0x00; /* No UDMA */
+ ap->ops = &mpc52xx_ata_port_ops;
+ host->private_data = priv;
+
+ aio = &ap->ioaddr;
aio->cmd_addr = NULL; /* Don't have a classic reg block */
aio->altstatus_addr = &priv->ata_regs->tf_control;
aio->ctl_addr = &priv->ata_regs->tf_control;
@@ -343,11 +342,9 @@
aio->status_addr = &priv->ata_regs->tf_command;
aio->command_addr = &priv->ata_regs->tf_command;
- ae->private_data = priv;
-
- rv = ata_device_add(ae);
-
- return rv ? 0 : -EINVAL;
+ /* activate host */
+ return ata_host_activate(host, priv->ata_irq, ata_interrupt, 0,
+ &mpc52xx_ata_sht);
}
static struct mpc52xx_ata_priv *
diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c
index 4abe45a..987c5fa 100644
--- a/drivers/ata/pata_mpiix.c
+++ b/drivers/ata/pata_mpiix.c
@@ -35,7 +35,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_mpiix"
-#define DRV_VERSION "0.7.5"
+#define DRV_VERSION "0.7.6"
enum {
IDETIM = 0x6C, /* IDE control register */
@@ -53,7 +53,6 @@
if (!pci_test_config_bits(pdev, &mpiix_enable_bits))
return -ENOENT;
- ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -185,12 +184,12 @@
.thaw = ata_bmdma_thaw,
.error_handler = mpiix_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = mpiix_qc_issue_prot,
.data_xfer = ata_data_xfer,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -201,8 +200,9 @@
static int mpiix_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
/* Single threaded by the PCI probe logic */
- static struct ata_probe_ent probe;
static int printed_version;
+ struct ata_host *host;
+ struct ata_port *ap;
void __iomem *cmd_addr, *ctl_addr;
u16 idetim;
int irq;
@@ -210,6 +210,10 @@
if (!printed_version++)
dev_printk(KERN_DEBUG, &dev->dev, "version " DRV_VERSION "\n");
+ host = ata_host_alloc(&dev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+
/* MPIIX has many functions which can be turned on or off according
to other devices present. Make sure IDE is enabled before we try
and use it */
@@ -238,27 +242,21 @@
without BARs set fools the setup. #2 If you pci_disable_device
the MPIIX your box goes castors up */
- INIT_LIST_HEAD(&probe.node);
- probe.dev = pci_dev_to_dev(dev);
- probe.port_ops = &mpiix_port_ops;
- probe.sht = &mpiix_sht;
- probe.pio_mask = 0x1F;
- probe.irq_flags = IRQF_SHARED;
- probe.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
- probe.n_ports = 1;
+ ap = host->ports[0];
+ ap->ops = &mpiix_port_ops;
+ ap->pio_mask = 0x1F;
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
- probe.irq = irq;
- probe.port[0].cmd_addr = cmd_addr;
- probe.port[0].ctl_addr = ctl_addr;
- probe.port[0].altstatus_addr = ctl_addr;
+ ap->ioaddr.cmd_addr = cmd_addr;
+ ap->ioaddr.ctl_addr = ctl_addr;
+ ap->ioaddr.altstatus_addr = ctl_addr;
/* Let libata fill in the port details */
- ata_std_ports(&probe.port[0]);
+ ata_std_ports(&ap->ioaddr);
- /* Now add the port that is active */
- if (ata_device_add(&probe))
- return 0;
- return -ENODEV;
+ /* activate host */
+ return ata_host_activate(host, irq, ata_interrupt, IRQF_SHARED,
+ &mpiix_sht);
}
static const struct pci_device_id mpiix[] = {
diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c
index 38f99b3..dbba5b7 100644
--- a/drivers/ata/pata_netcell.c
+++ b/drivers/ata/pata_netcell.c
@@ -16,33 +16,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_netcell"
-#define DRV_VERSION "0.1.6"
-
-/**
- * netcell_probe_init - check for 40/80 pin
- * @ap: Port
- *
- * Cables are handled by the RAID controller. Report 80 pin.
- */
-
-static int netcell_pre_reset(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_PATA80;
- return ata_std_prereset(ap);
-}
-
-/**
- * netcell_probe_reset - Probe specified port on PATA host controller
- * @ap: Port to probe
- *
- * LOCKING:
- * None (inherited from caller).
- */
-
-static void netcell_error_handler(struct ata_port *ap)
-{
- return ata_bmdma_drive_eh(ap, netcell_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
+#define DRV_VERSION "0.1.7"
/* No PIO or DMA methods needed for this device */
@@ -81,8 +55,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = netcell_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_80wire,
/* BMDMA handling is PCI ATA format, use helpers */
.bmdma_setup = ata_bmdma_setup,
diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c
index 9944a28..078aeda 100644
--- a/drivers/ata/pata_ns87410.c
+++ b/drivers/ata/pata_ns87410.c
@@ -28,13 +28,13 @@
#include <linux/libata.h>
#define DRV_NAME "pata_ns87410"
-#define DRV_VERSION "0.4.3"
+#define DRV_VERSION "0.4.6"
/**
* ns87410_pre_reset - probe begin
* @ap: ATA port
*
- * Set up cable type and use generic probe init
+ * Check enabled ports
*/
static int ns87410_pre_reset(struct ata_port *ap)
@@ -47,7 +47,6 @@
if (!pci_test_config_bits(pdev, &ns87410_enable_bits[ap->port_no]))
return -ENOENT;
- ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -177,6 +176,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = ns87410_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = ns87410_qc_issue_prot,
diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c
index da68cd1..dea4690 100644
--- a/drivers/ata/pata_oldpiix.c
+++ b/drivers/ata/pata_oldpiix.c
@@ -25,7 +25,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_oldpiix"
-#define DRV_VERSION "0.5.4"
+#define DRV_VERSION "0.5.5"
/**
* oldpiix_pre_reset - probe begin
@@ -44,7 +44,6 @@
if (!pci_test_config_bits(pdev, &oldpiix_enable_bits[ap->port_no]))
return -ENOENT;
- ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -65,7 +64,7 @@
/**
* oldpiix_set_piomode - Initialize host controller PATA PIO timings
* @ap: Port whose timings we are configuring
- * @adev: um
+ * @adev: Device whose timings we are configuring
*
* Set PIO mode for device, in host controller PCI config space.
*
@@ -255,6 +254,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = oldpiix_pata_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c
index 3fd3a35..13b63e2 100644
--- a/drivers/ata/pata_opti.c
+++ b/drivers/ata/pata_opti.c
@@ -34,7 +34,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_opti"
-#define DRV_VERSION "0.2.8"
+#define DRV_VERSION "0.2.9"
enum {
READ_REG = 0, /* index of Read cycle timing register */
@@ -61,8 +61,6 @@
if (!pci_test_config_bits(pdev, &opti_enable_bits[ap->port_no]))
return -ENOENT;
-
- ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -198,6 +196,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = opti_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c
index 9764907..b70e04c 100644
--- a/drivers/ata/pata_optidma.c
+++ b/drivers/ata/pata_optidma.c
@@ -33,7 +33,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_optidma"
-#define DRV_VERSION "0.2.4"
+#define DRV_VERSION "0.3.2"
enum {
READ_REG = 0, /* index of Read cycle timing register */
@@ -62,7 +62,6 @@
if (ap->port_no && !pci_test_config_bits(pdev, &optidma_enable_bits))
return -ENOENT;
- ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -115,7 +114,7 @@
}
/**
- * optidma_set_mode - set mode data
+ * optidma_mode_setup - set mode data
* @ap: ATA interface
* @adev: ATA device
* @mode: Mode to set
@@ -128,7 +127,7 @@
* IRQ here we depend on the host set locking to avoid catastrophe.
*/
-static void optidma_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mode)
+static void optidma_mode_setup(struct ata_port *ap, struct ata_device *adev, u8 mode)
{
struct ata_device *pair = ata_dev_pair(adev);
int pio = adev->pio_mode - XFER_PIO_0;
@@ -202,7 +201,7 @@
}
/**
- * optiplus_set_mode - DMA setup for Firestar Plus
+ * optiplus_mode_setup - DMA setup for Firestar Plus
* @ap: ATA port
* @adev: device
* @mode: desired mode
@@ -213,7 +212,7 @@
* one
*/
-static void optiplus_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mode)
+static void optiplus_mode_setup(struct ata_port *ap, struct ata_device *adev, u8 mode)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u8 udcfg;
@@ -225,7 +224,7 @@
pci_read_config_byte(pdev, 0x44, &udcfg);
if (mode <= XFER_UDMA_0) {
udcfg &= ~(1 << unit);
- optidma_set_mode(ap, adev, adev->dma_mode);
+ optidma_mode_setup(ap, adev, adev->dma_mode);
} else {
udcfg |= (1 << unit);
if (ap->port_no) {
@@ -253,7 +252,7 @@
static void optidma_set_pio_mode(struct ata_port *ap, struct ata_device *adev)
{
- optidma_set_mode(ap, adev, adev->pio_mode);
+ optidma_mode_setup(ap, adev, adev->pio_mode);
}
/**
@@ -268,7 +267,7 @@
static void optidma_set_dma_mode(struct ata_port *ap, struct ata_device *adev)
{
- optidma_set_mode(ap, adev, adev->dma_mode);
+ optidma_mode_setup(ap, adev, adev->dma_mode);
}
/**
@@ -283,7 +282,7 @@
static void optiplus_set_pio_mode(struct ata_port *ap, struct ata_device *adev)
{
- optiplus_set_mode(ap, adev, adev->pio_mode);
+ optiplus_mode_setup(ap, adev, adev->pio_mode);
}
/**
@@ -298,7 +297,7 @@
static void optiplus_set_dma_mode(struct ata_port *ap, struct ata_device *adev)
{
- optiplus_set_mode(ap, adev, adev->dma_mode);
+ optiplus_mode_setup(ap, adev, adev->dma_mode);
}
/**
@@ -322,26 +321,29 @@
}
/**
- * optidma_post_set_mode - finalize PCI setup
+ * optidma_set_mode - mode setup
* @ap: port to set up
*
- * Finalise the configuration by writing the nibble of extra bits
- * of data into the chip.
+ * Use the standard setup to tune the chipset and then finalise the
+ * configuration by writing the nibble of extra bits of data into
+ * the chip.
*/
-static void optidma_post_set_mode(struct ata_port *ap)
+static int optidma_set_mode(struct ata_port *ap, struct ata_device **r_failed)
{
u8 r;
int nybble = 4 * ap->port_no;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ int rc = ata_do_set_mode(ap, r_failed);
+ if (rc == 0) {
+ pci_read_config_byte(pdev, 0x43, &r);
- pci_read_config_byte(pdev, 0x43, &r);
-
- r &= (0x0F << nybble);
- r |= (optidma_make_bits43(&ap->device[0]) +
- (optidma_make_bits43(&ap->device[0]) << 2)) << nybble;
-
- pci_write_config_byte(pdev, 0x43, r);
+ r &= (0x0F << nybble);
+ r |= (optidma_make_bits43(&ap->device[0]) +
+ (optidma_make_bits43(&ap->device[0]) << 2)) << nybble;
+ pci_write_config_byte(pdev, 0x43, r);
+ }
+ return rc;
}
static struct scsi_host_template optidma_sht = {
@@ -381,7 +383,8 @@
.thaw = ata_bmdma_thaw,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
.error_handler = optidma_error_handler,
- .post_set_mode = optidma_post_set_mode,
+ .set_mode = optidma_set_mode,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -416,7 +419,8 @@
.thaw = ata_bmdma_thaw,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
.error_handler = optidma_error_handler,
- .post_set_mode = optidma_post_set_mode,
+ .set_mode = optidma_set_mode,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c
index 103720f..75dc847 100644
--- a/drivers/ata/pata_pcmcia.c
+++ b/drivers/ata/pata_pcmcia.c
@@ -42,7 +42,7 @@
#define DRV_NAME "pata_pcmcia"
-#define DRV_VERSION "0.3.0"
+#define DRV_VERSION "0.3.1"
/*
* Private data structure to glue stuff together
@@ -54,6 +54,39 @@
dev_node_t node;
};
+/**
+ * pcmcia_set_mode - PCMCIA specific mode setup
+ * @ap: Port
+ * @r_failed_dev: Return pointer for failed device
+ *
+ * Perform the tuning and setup of the devices and timings, which
+ * for PCMCIA is the same as any other controller. We wrap it however
+ * as we need to spot hardware with incorrect or missing master/slave
+ * decode, which alas is embarrassingly common in the PC world
+ */
+
+static int pcmcia_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
+{
+ struct ata_device *master = &ap->device[0];
+ struct ata_device *slave = &ap->device[1];
+
+ if (!ata_dev_enabled(master) || !ata_dev_enabled(slave))
+ return ata_do_set_mode(ap, r_failed_dev);
+
+ if (memcmp(master->id + ATA_ID_FW_REV, slave->id + ATA_ID_FW_REV,
+ ATA_ID_FW_REV_LEN + ATA_ID_PROD_LEN) == 0)
+ {
+ /* Suspicious match, but could be two cards from
+ the same vendor - check serial */
+ if (memcmp(master->id + ATA_ID_SERNO, slave->id + ATA_ID_SERNO,
+ ATA_ID_SERNO_LEN) == 0 && master->id[ATA_ID_SERNO] >> 8) {
+ ata_dev_printk(slave, KERN_WARNING, "is a ghost device, ignoring.\n");
+ ata_dev_disable(slave);
+ }
+ }
+ return ata_do_set_mode(ap, r_failed_dev);
+}
+
static struct scsi_host_template pcmcia_sht = {
.module = THIS_MODULE,
.name = DRV_NAME,
@@ -73,6 +106,7 @@
};
static struct ata_port_operations pcmcia_port_ops = {
+ .set_mode = pcmcia_set_mode,
.port_disable = ata_port_disable,
.tf_load = ata_tf_load,
.tf_read = ata_tf_read,
@@ -84,13 +118,13 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_data_xfer_noirq,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -111,7 +145,8 @@
static int pcmcia_init_one(struct pcmcia_device *pdev)
{
- struct ata_probe_ent ae;
+ struct ata_host *host;
+ struct ata_port *ap;
struct ata_pcmcia_info *info;
tuple_t tuple;
struct {
@@ -255,24 +290,24 @@
* Having done the PCMCIA plumbing the ATA side is relatively
* sane.
*/
+ ret = -ENOMEM;
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ goto failed;
+ ap = host->ports[0];
- memset(&ae, 0, sizeof(struct ata_probe_ent));
- INIT_LIST_HEAD(&ae.node);
- ae.dev = &pdev->dev;
- ae.port_ops = &pcmcia_port_ops;
- ae.sht = &pcmcia_sht;
- ae.n_ports = 1;
- ae.pio_mask = 1; /* ISA so PIO 0 cycles */
- ae.irq = pdev->irq.AssignedIRQ;
- ae.irq_flags = IRQF_SHARED;
- ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
- ae.port[0].cmd_addr = io_addr;
- ae.port[0].altstatus_addr = ctl_addr;
- ae.port[0].ctl_addr = ctl_addr;
- ata_std_ports(&ae.port[0]);
+ ap->ops = &pcmcia_port_ops;
+ ap->pio_mask = 1; /* ISA so PIO 0 cycles */
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
+ ap->ioaddr.cmd_addr = io_addr;
+ ap->ioaddr.altstatus_addr = ctl_addr;
+ ap->ioaddr.ctl_addr = ctl_addr;
+ ata_std_ports(&ap->ioaddr);
- ret = -ENODEV;
- if (ata_device_add(&ae) == 0)
+ /* activate */
+ ret = ata_host_activate(host, pdev->irq.AssignedIRQ, ata_interrupt,
+ IRQF_SHARED, &pcmcia_sht);
+ if (ret)
goto failed;
info->ndev = 1;
diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c
index 93bcdad..a61cbc1 100644
--- a/drivers/ata/pata_pdc2027x.c
+++ b/drivers/ata/pata_pdc2027x.c
@@ -35,7 +35,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_pdc2027x"
-#define DRV_VERSION "0.8"
+#define DRV_VERSION "0.9"
#undef PDC_DEBUG
#ifdef PDC_DEBUG
@@ -66,8 +66,10 @@
static void pdc2027x_error_handler(struct ata_port *ap);
static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev);
static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev);
-static void pdc2027x_post_set_mode(struct ata_port *ap);
static int pdc2027x_check_atapi_dma(struct ata_queued_cmd *qc);
+static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long mask);
+static int pdc2027x_cable_detect(struct ata_port *ap);
+static int pdc2027x_set_mode(struct ata_port *ap, struct ata_device **r_failed);
/*
* ATA Timing Tables based on 133MHz controller clock.
@@ -146,6 +148,7 @@
static struct ata_port_operations pdc2027x_pata100_ops = {
.port_disable = ata_port_disable,
+ .mode_filter = ata_pci_default_filter,
.tf_load = ata_tf_load,
.tf_read = ata_tf_read,
@@ -166,8 +169,8 @@
.thaw = ata_bmdma_thaw,
.error_handler = pdc2027x_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = pdc2027x_cable_detect,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -179,7 +182,8 @@
.port_disable = ata_port_disable,
.set_piomode = pdc2027x_set_piomode,
.set_dmamode = pdc2027x_set_dmamode,
- .post_set_mode = pdc2027x_post_set_mode,
+ .set_mode = pdc2027x_set_mode,
+ .mode_filter = pdc2027x_mode_filter,
.tf_load = ata_tf_load,
.tf_read = ata_tf_read,
@@ -200,8 +204,8 @@
.thaw = ata_bmdma_thaw,
.error_handler = pdc2027x_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = pdc2027x_cable_detect,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -212,7 +216,6 @@
static struct ata_port_info pdc2027x_port_info[] = {
/* PDC_UDMA_100 */
{
- .sht = &pdc2027x_sht,
.flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS |
ATA_FLAG_MMIO,
.pio_mask = 0x1f, /* pio0-4 */
@@ -222,7 +225,6 @@
},
/* PDC_UDMA_133 */
{
- .sht = &pdc2027x_sht,
.flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS |
ATA_FLAG_MMIO,
.pio_mask = 0x1f, /* pio0-4 */
@@ -261,7 +263,7 @@
}
/**
- * pdc2027x_pata_cbl_detect - Probe host controller cable detect info
+ * pdc2027x_pata_cable_detect - Probe host controller cable detect info
* @ap: Port for which cable detect info is desired
*
* Read 80c cable indicator from Promise extended register.
@@ -270,7 +272,7 @@
* LOCKING:
* None (inherited from caller).
*/
-static void pdc2027x_cbl_detect(struct ata_port *ap)
+static int pdc2027x_cable_detect(struct ata_port *ap)
{
u32 cgcr;
@@ -281,13 +283,10 @@
PDPRINTK("No cable or 80-conductor cable on port %d\n", ap->port_no);
- ap->cbl = ATA_CBL_PATA80;
- return;
-
+ return ATA_CBL_PATA80;
cbl40:
printk(KERN_INFO DRV_NAME ": 40-conductor cable detected on port %d\n", ap->port_no);
- ap->cbl = ATA_CBL_PATA40;
- ap->udma_mask &= ATA_UDMA_MASK_40C;
+ return ATA_CBL_PATA40;
}
/**
@@ -314,7 +313,6 @@
/* Check whether port enabled */
if (!pdc2027x_port_enabled(ap))
return -ENOENT;
- pdc2027x_cbl_detect(ap);
return ata_std_prereset(ap);
}
@@ -334,6 +332,32 @@
}
/**
+ * pdc2720x_mode_filter - mode selection filter
+ * @adev: ATA device
+ * @mask: list of modes proposed
+ *
+ * Block UDMA on devices that cause trouble with this controller.
+ */
+
+static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long mask)
+{
+ unsigned char model_num[ATA_ID_PROD_LEN + 1];
+ struct ata_device *pair = ata_dev_pair(adev);
+
+ if (adev->class != ATA_DEV_ATA || adev->devno == 0 || pair == NULL)
+ return ata_pci_default_filter(adev, mask);
+
+ /* Check for slave of a Maxtor at UDMA6 */
+ ata_id_c_string(pair->id, model_num, ATA_ID_PROD,
+ ATA_ID_PROD_LEN + 1);
+ /* If the master is a maxtor in UDMA6 then the slave should not use UDMA 6 */
+ if(strstr(model_num, "Maxtor") == 0 && pair->dma_mode == XFER_UDMA_6)
+ mask &= ~ (1 << (6 + ATA_SHIFT_UDMA));
+
+ return ata_pci_default_filter(adev, mask);
+}
+
+/**
* pdc2027x_set_piomode - Initialize host controller PATA PIO timings
* @ap: Port to configure
* @adev: um
@@ -444,17 +468,22 @@
}
/**
- * pdc2027x_post_set_mode - Set the timing registers back to correct values.
+ * pdc2027x_set_mode - Set the timing registers back to correct values.
* @ap: Port to configure
+ * @r_failed: Returned device for failure
*
* The pdc2027x hardware will look at "SET FEATURES" and change the timing registers
* automatically. The values set by the hardware might be incorrect, under 133Mhz PLL.
* This function overwrites the possibly incorrect values set by the hardware to be correct.
*/
-static void pdc2027x_post_set_mode(struct ata_port *ap)
+static int pdc2027x_set_mode(struct ata_port *ap, struct ata_device **r_failed)
{
int i;
+ i = ata_do_set_mode(ap, r_failed);
+ if (i < 0)
+ return i;
+
for (i = 0; i < ATA_MAX_DEVICES; i++) {
struct ata_device *dev = &ap->device[i];
@@ -476,6 +505,7 @@
}
}
}
+ return 0;
}
/**
@@ -521,12 +551,12 @@
/**
* pdc_read_counter - Read the ctr counter
- * @probe_ent: for the port address
+ * @host: target ATA host
*/
-static long pdc_read_counter(struct ata_probe_ent *probe_ent)
+static long pdc_read_counter(struct ata_host *host)
{
- void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR];
+ void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR];
long counter;
int retry = 1;
u32 bccrl, bccrh, bccrlv, bccrhv;
@@ -564,12 +594,12 @@
* adjust_pll - Adjust the PLL input clock in Hz.
*
* @pdc_controller: controller specific information
- * @probe_ent: For the port address
+ * @host: target ATA host
* @pll_clock: The input of PLL in HZ
*/
-static void pdc_adjust_pll(struct ata_probe_ent *probe_ent, long pll_clock, unsigned int board_idx)
+static void pdc_adjust_pll(struct ata_host *host, long pll_clock, unsigned int board_idx)
{
- void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR];
+ void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR];
u16 pll_ctl;
long pll_clock_khz = pll_clock / 1000;
long pout_required = board_idx? PDC_133_MHZ:PDC_100_MHZ;
@@ -649,19 +679,19 @@
/**
* detect_pll_input_clock - Detect the PLL input clock in Hz.
- * @probe_ent: for the port address
+ * @host: target ATA host
* Ex. 16949000 on 33MHz PCI bus for pdc20275.
* Half of the PCI clock.
*/
-static long pdc_detect_pll_input_clock(struct ata_probe_ent *probe_ent)
+static long pdc_detect_pll_input_clock(struct ata_host *host)
{
- void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR];
+ void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR];
u32 scr;
long start_count, end_count;
long pll_clock;
/* Read current counter value */
- start_count = pdc_read_counter(probe_ent);
+ start_count = pdc_read_counter(host);
/* Start the test mode */
scr = readl(mmio_base + PDC_SYS_CTL);
@@ -673,7 +703,7 @@
mdelay(100);
/* Read the counter values again */
- end_count = pdc_read_counter(probe_ent);
+ end_count = pdc_read_counter(host);
/* Stop the test mode */
scr = readl(mmio_base + PDC_SYS_CTL);
@@ -692,11 +722,10 @@
/**
* pdc_hardware_init - Initialize the hardware.
- * @pdev: instance of pci_dev found
- * @pdc_controller: controller specific information
- * @pe: for the port address
+ * @host: target ATA host
+ * @board_idx: board identifier
*/
-static int pdc_hardware_init(struct pci_dev *pdev, struct ata_probe_ent *pe, unsigned int board_idx)
+static int pdc_hardware_init(struct ata_host *host, unsigned int board_idx)
{
long pll_clock;
@@ -706,15 +735,15 @@
* Ex. 25MHz or 40MHz, we have to adjust the cycle_time.
* The pdc20275 controller employs PLL circuit to help correct timing registers setting.
*/
- pll_clock = pdc_detect_pll_input_clock(pe);
+ pll_clock = pdc_detect_pll_input_clock(host);
if (pll_clock < 0) /* counter overflow? Try again. */
- pll_clock = pdc_detect_pll_input_clock(pe);
+ pll_clock = pdc_detect_pll_input_clock(host);
- dev_printk(KERN_INFO, &pdev->dev, "PLL input clock %ld kHz\n", pll_clock/1000);
+ dev_printk(KERN_INFO, host->dev, "PLL input clock %ld kHz\n", pll_clock/1000);
/* Adjust PLL control register */
- pdc_adjust_pll(pe, pll_clock, board_idx);
+ pdc_adjust_pll(host, pll_clock, board_idx);
return 0;
}
@@ -746,8 +775,7 @@
* Called when an instance of PCI adapter is inserted.
* This function checks whether the hardware is supported,
* initialize hardware and register an instance of ata_host to
- * libata by providing struct ata_probe_ent and ata_device_add().
- * (implements struct pci_driver.probe() )
+ * libata. (implements struct pci_driver.probe() )
*
* @pdev: instance of pci_dev found
* @ent: matching entry in the id_tbl[]
@@ -756,14 +784,21 @@
{
static int printed_version;
unsigned int board_idx = (unsigned int) ent->driver_data;
-
- struct ata_probe_ent *probe_ent;
+ const struct ata_port_info *ppi[] =
+ { &pdc2027x_port_info[board_idx], NULL };
+ struct ata_host *host;
void __iomem *mmio_base;
int rc;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ /* alloc host */
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2);
+ if (!host)
+ return -ENOMEM;
+
+ /* acquire resources and fill host */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
@@ -771,6 +806,7 @@
rc = pcim_iomap_regions(pdev, 1 << PDC_MMIO_BAR, DRV_NAME);
if (rc)
return rc;
+ host->iomap = pcim_iomap_table(pdev);
rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
if (rc)
@@ -780,46 +816,22 @@
if (rc)
return rc;
- /* Prepare the probe entry */
- probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
- if (probe_ent == NULL)
- return -ENOMEM;
+ mmio_base = host->iomap[PDC_MMIO_BAR];
- probe_ent->dev = pci_dev_to_dev(pdev);
- INIT_LIST_HEAD(&probe_ent->node);
+ pdc_ata_setup_port(&host->ports[0]->ioaddr, mmio_base + 0x17c0);
+ host->ports[0]->ioaddr.bmdma_addr = mmio_base + 0x1000;
+ pdc_ata_setup_port(&host->ports[1]->ioaddr, mmio_base + 0x15c0);
+ host->ports[1]->ioaddr.bmdma_addr = mmio_base + 0x1008;
- probe_ent->sht = pdc2027x_port_info[board_idx].sht;
- probe_ent->port_flags = pdc2027x_port_info[board_idx].flags;
- probe_ent->pio_mask = pdc2027x_port_info[board_idx].pio_mask;
- probe_ent->mwdma_mask = pdc2027x_port_info[board_idx].mwdma_mask;
- probe_ent->udma_mask = pdc2027x_port_info[board_idx].udma_mask;
- probe_ent->port_ops = pdc2027x_port_info[board_idx].port_ops;
-
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->iomap = pcim_iomap_table(pdev);
-
- mmio_base = probe_ent->iomap[PDC_MMIO_BAR];
-
- pdc_ata_setup_port(&probe_ent->port[0], mmio_base + 0x17c0);
- probe_ent->port[0].bmdma_addr = mmio_base + 0x1000;
- pdc_ata_setup_port(&probe_ent->port[1], mmio_base + 0x15c0);
- probe_ent->port[1].bmdma_addr = mmio_base + 0x1008;
-
- probe_ent->n_ports = 2;
-
- pci_set_master(pdev);
//pci_enable_intx(pdev);
/* initialize adapter */
- if (pdc_hardware_init(pdev, probe_ent, board_idx) != 0)
+ if (pdc_hardware_init(host, board_idx) != 0)
return -EIO;
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(&pdev->dev, probe_ent);
- return 0;
+ pci_set_master(pdev);
+ return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+ &pdc2027x_sht);
}
/**
diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c
index 0a14933..ee636be 100644
--- a/drivers/ata/pata_pdc202xx_old.c
+++ b/drivers/ata/pata_pdc202xx_old.c
@@ -22,45 +22,17 @@
#include <linux/libata.h>
#define DRV_NAME "pata_pdc202xx_old"
-#define DRV_VERSION "0.4.0"
+#define DRV_VERSION "0.4.2"
-/**
- * pdc2024x_pre_reset - probe begin
- * @ap: ATA port
- *
- * Set up cable type and use generic probe init
- */
-
-static int pdc2024x_pre_reset(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_PATA40;
- return ata_std_prereset(ap);
-}
-
-
-static void pdc2024x_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, pdc2024x_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-
-static int pdc2026x_pre_reset(struct ata_port *ap)
+static int pdc2026x_cable_detect(struct ata_port *ap)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u16 cis;
pci_read_config_word(pdev, 0x50, &cis);
if (cis & (1 << (10 + ap->port_no)))
- ap->cbl = ATA_CBL_PATA80;
- else
- ap->cbl = ATA_CBL_PATA40;
-
- return ata_std_prereset(ap);
-}
-
-static void pdc2026x_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, pdc2026x_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
+ return ATA_CBL_PATA80;
+ return ATA_CBL_PATA40;
}
/**
@@ -244,7 +216,6 @@
/**
* pdc2026x_dev_config - device setup hook
- * @ap: ATA port
* @adev: newly found device
*
* Perform chip specific early setup. We need to lock the transfer
@@ -252,7 +223,7 @@
* barf.
*/
-static void pdc2026x_dev_config(struct ata_port *ap, struct ata_device *adev)
+static void pdc2026x_dev_config(struct ata_device *adev)
{
adev->max_sectors = 256;
}
@@ -292,8 +263,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = pdc2024x_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -326,8 +298,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = pdc2026x_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = pdc2026x_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = pdc2026x_bmdma_start,
diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c
index 4b82a54..a0a650c 100644
--- a/drivers/ata/pata_platform.c
+++ b/drivers/ata/pata_platform.c
@@ -80,13 +80,13 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_unknown,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_data_xfer_noirq,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -135,7 +135,8 @@
static int __devinit pata_platform_probe(struct platform_device *pdev)
{
struct resource *io_res, *ctl_res;
- struct ata_probe_ent ae;
+ struct ata_host *host;
+ struct ata_port *ap;
unsigned int mmio;
/*
@@ -175,44 +176,41 @@
/*
* Now that that's out of the way, wire up the port..
*/
- memset(&ae, 0, sizeof(struct ata_probe_ent));
- INIT_LIST_HEAD(&ae.node);
- ae.dev = &pdev->dev;
- ae.port_ops = &pata_platform_port_ops;
- ae.sht = &pata_platform_sht;
- ae.n_ports = 1;
- ae.pio_mask = pio_mask;
- ae.irq = platform_get_irq(pdev, 0);
- ae.irq_flags = 0;
- ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+ ap = host->ports[0];
+
+ ap->ops = &pata_platform_port_ops;
+ ap->pio_mask = pio_mask;
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
/*
* Handle the MMIO case
*/
if (mmio) {
- ae.port[0].cmd_addr = devm_ioremap(&pdev->dev, io_res->start,
+ ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, io_res->start,
io_res->end - io_res->start + 1);
- ae.port[0].ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start,
+ ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start,
ctl_res->end - ctl_res->start + 1);
} else {
- ae.port[0].cmd_addr = devm_ioport_map(&pdev->dev, io_res->start,
+ ap->ioaddr.cmd_addr = devm_ioport_map(&pdev->dev, io_res->start,
io_res->end - io_res->start + 1);
- ae.port[0].ctl_addr = devm_ioport_map(&pdev->dev, ctl_res->start,
+ ap->ioaddr.ctl_addr = devm_ioport_map(&pdev->dev, ctl_res->start,
ctl_res->end - ctl_res->start + 1);
}
- if (!ae.port[0].cmd_addr || !ae.port[0].ctl_addr) {
+ if (!ap->ioaddr.cmd_addr || !ap->ioaddr.ctl_addr) {
dev_err(&pdev->dev, "failed to map IO/CTL base\n");
return -ENOMEM;
}
- ae.port[0].altstatus_addr = ae.port[0].ctl_addr;
+ ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
- pata_platform_setup_port(&ae.port[0], pdev->dev.platform_data);
+ pata_platform_setup_port(&ap->ioaddr, pdev->dev.platform_data);
- if (unlikely(ata_device_add(&ae) == 0))
- return -ENODEV;
-
- return 0;
+ /* activate */
+ return ata_host_activate(host, platform_get_irq(pdev, 0), ata_interrupt,
+ 0, &pata_platform_sht);
}
/**
diff --git a/drivers/ata/pata_qdi.c b/drivers/ata/pata_qdi.c
index c381001..27685ce 100644
--- a/drivers/ata/pata_qdi.c
+++ b/drivers/ata/pata_qdi.c
@@ -183,13 +183,13 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = qdi_qc_issue_prot,
.data_xfer = qdi_data_xfer,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -211,13 +211,13 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = qdi_qc_issue_prot,
.data_xfer = qdi_data_xfer,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -238,8 +238,9 @@
static __init int qdi_init_one(unsigned long port, int type, unsigned long io, int irq, int fast)
{
- struct ata_probe_ent ae;
struct platform_device *pdev;
+ struct ata_host *host;
+ struct ata_port *ap;
void __iomem *io_addr, *ctl_addr;
int ret;
@@ -257,34 +258,31 @@
if (!io_addr || !ctl_addr)
goto fail;
- memset(&ae, 0, sizeof(struct ata_probe_ent));
- INIT_LIST_HEAD(&ae.node);
- ae.dev = &pdev->dev;
+ ret = -ENOMEM;
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ goto fail;
+ ap = host->ports[0];
if (type == 6580) {
- ae.port_ops = &qdi6580_port_ops;
- ae.pio_mask = 0x1F;
- ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
+ ap->ops = &qdi6580_port_ops;
+ ap->pio_mask = 0x1F;
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
} else {
- ae.port_ops = &qdi6500_port_ops;
- ae.pio_mask = 0x07; /* Actually PIO3 !IORDY is possible */
- ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST |
- ATA_FLAG_NO_IORDY;
+ ap->ops = &qdi6500_port_ops;
+ ap->pio_mask = 0x07; /* Actually PIO3 !IORDY is possible */
+ ap->flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY;
}
- ae.sht = &qdi_sht;
- ae.n_ports = 1;
- ae.irq = irq;
- ae.irq_flags = 0;
- ae.port[0].cmd_addr = io_addr;
- ae.port[0].altstatus_addr = ctl_addr;
- ae.port[0].ctl_addr = ctl_addr;
- ata_std_ports(&ae.port[0]);
+ ap->ioaddr.cmd_addr = io_addr;
+ ap->ioaddr.altstatus_addr = ctl_addr;
+ ap->ioaddr.ctl_addr = ctl_addr;
+ ata_std_ports(&ap->ioaddr);
/*
* Hook in a private data structure per channel
*/
- ae.private_data = &qdi_data[nr_qdi_host];
+ ap->private_data = &qdi_data[nr_qdi_host];
qdi_data[nr_qdi_host].timing = port;
qdi_data[nr_qdi_host].fast = fast;
@@ -292,8 +290,9 @@
printk(KERN_INFO DRV_NAME": qd%d at 0x%lx.\n", type, io);
- ret = -ENODEV;
- if (!ata_device_add(&ae))
+ /* activate */
+ ret = ata_host_activate(host, irq, ata_interrupt, 0, &qdi_sht);
+ if (ret)
goto fail;
qdi_host[nr_qdi_host++] = dev_get_drvdata(&pdev->dev);
diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c
index 9a9132c..1c54673 100644
--- a/drivers/ata/pata_radisys.c
+++ b/drivers/ata/pata_radisys.c
@@ -24,40 +24,12 @@
#include <linux/ata.h>
#define DRV_NAME "pata_radisys"
-#define DRV_VERSION "0.4.1"
-
-/**
- * radisys_probe_init - probe begin
- * @ap: ATA port
- *
- * Set up cable type and use generic probe init
- */
-
-static int radisys_pre_reset(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_PATA80;
- return ata_std_prereset(ap);
-}
-
-
-/**
- * radisys_pata_error_handler - Probe specified port on PATA host controller
- * @ap: Port to probe
- * @classes:
- *
- * LOCKING:
- * None (inherited from caller).
- */
-
-static void radisys_pata_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, radisys_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
+#define DRV_VERSION "0.4.4"
/**
* radisys_set_piomode - Initialize host controller PATA PIO timings
- * @ap: Port whose timings we are configuring
- * @adev: um
+ * @ap: ATA port
+ * @adev: Device whose timings we are configuring
*
* Set PIO mode for device, in host controller PCI config space.
*
@@ -248,8 +220,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = radisys_pata_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_unknown,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c
index f522daa..85c4529 100644
--- a/drivers/ata/pata_rz1000.c
+++ b/drivers/ata/pata_rz1000.c
@@ -25,31 +25,6 @@
/**
- * rz1000_prereset - probe begin
- * @ap: ATA port
- *
- * Set up cable type and use generics
- */
-
-static int rz1000_prereset(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_PATA40;
- return ata_std_prereset(ap);
-}
-
-/**
- * rz1000_error_handler - probe reset
- * @ap: ATA port
- *
- * Perform the ATA standard reset sequence
- */
-
-static void rz1000_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, rz1000_prereset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-/**
* rz1000_set_mode - mode setting function
* @ap: ATA interface
* @unused: returned device on set_mode failure
@@ -122,8 +97,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = rz1000_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c
index 93b3ed0..66e8ff4 100644
--- a/drivers/ata/pata_sc1200.c
+++ b/drivers/ata/pata_sc1200.c
@@ -216,6 +216,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c
index f3ed141..5df354d 100644
--- a/drivers/ata/pata_scc.c
+++ b/drivers/ata/pata_scc.c
@@ -1016,7 +1016,6 @@
.error_handler = scc_error_handler,
.post_internal_cmd = scc_bmdma_stop,
- .irq_handler = ata_interrupt,
.irq_clear = scc_bmdma_irq_clear,
.irq_on = scc_irq_on,
.irq_ack = scc_irq_ack,
@@ -1027,7 +1026,6 @@
static struct ata_port_info scc_port_info[] = {
{
- .sht = &scc_sht,
.flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x00,
@@ -1040,10 +1038,10 @@
* scc_reset_controller - initialize SCC PATA controller.
*/
-static int scc_reset_controller(struct ata_probe_ent *probe_ent)
+static int scc_reset_controller(struct ata_host *host)
{
- void __iomem *ctrl_base = probe_ent->iomap[SCC_CTRL_BAR];
- void __iomem *bmid_base = probe_ent->iomap[SCC_BMID_BAR];
+ void __iomem *ctrl_base = host->iomap[SCC_CTRL_BAR];
+ void __iomem *bmid_base = host->iomap[SCC_BMID_BAR];
void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL;
void __iomem *mode_port = ctrl_base + SCC_CTL_MODEREG;
void __iomem *ecmode_port = ctrl_base + SCC_CTL_ECMODE;
@@ -1104,17 +1102,15 @@
ioaddr->command_addr = ioaddr->cmd_addr + SCC_REG_CMD;
}
-static int scc_host_init(struct ata_probe_ent *probe_ent)
+static int scc_host_init(struct ata_host *host)
{
- struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
+ struct pci_dev *pdev = to_pci_dev(host->dev);
int rc;
- rc = scc_reset_controller(probe_ent);
+ rc = scc_reset_controller(host);
if (rc)
return rc;
- probe_ent->n_ports = 1;
-
rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
if (rc)
return rc;
@@ -1122,7 +1118,7 @@
if (rc)
return rc;
- scc_setup_ports(&probe_ent->port[0], probe_ent->iomap[SCC_BMID_BAR]);
+ scc_setup_ports(&host->ports[0]->ioaddr, host->iomap[SCC_BMID_BAR]);
pci_set_master(pdev);
@@ -1145,14 +1141,18 @@
{
static int printed_version;
unsigned int board_idx = (unsigned int) ent->driver_data;
+ const struct ata_port_info *ppi[] = { &scc_port_info[board_idx], NULL };
struct device *dev = &pdev->dev;
- struct ata_probe_ent *probe_ent;
int rc;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev,
"version " DRV_VERSION "\n");
+ host = ata_port_alloc_pinfo(&pdev->dev, ppi, 1);
+ if (!host)
+ return -ENOMEM;
+
rc = pcim_enable_device(pdev);
if (rc)
return rc;
@@ -1162,33 +1162,14 @@
pcim_pin_device(pdev);
if (rc)
return rc;
+ host->iomap = pcim_iomap_table(pdev);
- probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
- if (!probe_ent)
- return -ENOMEM;
-
- probe_ent->dev = dev;
- INIT_LIST_HEAD(&probe_ent->node);
-
- probe_ent->sht = scc_port_info[board_idx].sht;
- probe_ent->port_flags = scc_port_info[board_idx].flags;
- probe_ent->pio_mask = scc_port_info[board_idx].pio_mask;
- probe_ent->udma_mask = scc_port_info[board_idx].udma_mask;
- probe_ent->port_ops = scc_port_info[board_idx].port_ops;
-
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->iomap = pcim_iomap_table(pdev);
-
- rc = scc_host_init(probe_ent);
+ rc = scc_host_init(host);
if (rc)
return rc;
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(dev, probe_ent);
- return 0;
+ return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+ &scc_sht);
}
static struct pci_driver scc_pci_driver = {
diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c
index 598eef8..3956ef2 100644
--- a/drivers/ata/pata_serverworks.c
+++ b/drivers/ata/pata_serverworks.c
@@ -1,5 +1,5 @@
/*
- * ata-serverworks.c - Serverworks PATA for new ATA layer
+ * pata_serverworks.c - Serverworks PATA for new ATA layer
* (C) 2005 Red Hat Inc
* Alan Cox <alan@redhat.com>
*
@@ -137,14 +137,14 @@
};
/**
- * serverworks_pre_reset - cable detection
+ * serverworks_cable_detect - cable detection
* @ap: ATA port
*
* Perform cable detection according to the device and subvendor
* identifications
*/
-static int serverworks_pre_reset(struct ata_port *ap) {
+static int serverworks_cable_detect(struct ata_port *ap) {
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
struct sv_cable_table *cb = cable_detect;
@@ -152,8 +152,7 @@
if (cb->device == pdev->device &&
(cb->subvendor == pdev->subsystem_vendor ||
cb->subvendor == PCI_ANY_ID)) {
- ap->cbl = cb->cable_detect(ap);
- return ata_std_prereset(ap);
+ return cb->cable_detect(ap);
}
cb++;
}
@@ -162,11 +161,6 @@
return -1; /* kill compiler warning */
}
-static void serverworks_error_handler(struct ata_port *ap)
-{
- return ata_bmdma_drive_eh(ap, serverworks_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
/**
* serverworks_is_csb - Check for CSB or OSB
* @pdev: PCI device to check
@@ -191,31 +185,31 @@
/**
* serverworks_osb4_filter - mode selection filter
- * @ap: ATA interface
* @adev: ATA device
+ * @mask: Mask of proposed modes
*
* Filter the offered modes for the device to apply controller
* specific rules. OSB4 requires no UDMA for disks due to a FIFO
* bug we hit.
*/
-static unsigned long serverworks_osb4_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long serverworks_osb4_filter(struct ata_device *adev, unsigned long mask)
{
if (adev->class == ATA_DEV_ATA)
mask &= ~ATA_MASK_UDMA;
- return ata_pci_default_filter(ap, adev, mask);
+ return ata_pci_default_filter(adev, mask);
}
/**
* serverworks_csb_filter - mode selection filter
- * @ap: ATA interface
* @adev: ATA device
+ * @mask: Mask of proposed modes
*
* Check the blacklist and disable UDMA5 if matched
*/
-static unsigned long serverworks_csb_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask)
+static unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned long mask)
{
const char *p;
char model_num[ATA_ID_PROD_LEN + 1];
@@ -223,7 +217,7 @@
/* Disk, UDMA */
if (adev->class != ATA_DEV_ATA)
- return ata_pci_default_filter(ap, adev, mask);
+ return ata_pci_default_filter(adev, mask);
/* Actually do need to check */
ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num));
@@ -232,7 +226,7 @@
if (!strcmp(p, model_num))
mask &= ~(0x1F << ATA_SHIFT_UDMA);
}
- return ata_pci_default_filter(ap, adev, mask);
+ return ata_pci_default_filter(adev, mask);
}
@@ -339,8 +333,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = serverworks_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = serverworks_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -374,8 +369,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = serverworks_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = serverworks_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c
index dab2889..6770820 100644
--- a/drivers/ata/pata_sil680.c
+++ b/drivers/ata/pata_sil680.c
@@ -33,7 +33,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_sil680"
-#define DRV_VERSION "0.4.5"
+#define DRV_VERSION "0.4.6"
/**
* sil680_selreg - return register base
@@ -91,12 +91,6 @@
return ATA_CBL_PATA40;
}
-static int sil680_pre_reset(struct ata_port *ap)
-{
- ap->cbl = sil680_cable_detect(ap);
- return ata_std_prereset(ap);
-}
-
/**
* sil680_bus_reset - reset the SIL680 bus
* @ap: ATA port to reset
@@ -119,7 +113,7 @@
static void sil680_error_handler(struct ata_port *ap)
{
- ata_bmdma_drive_eh(ap, sil680_pre_reset, sil680_bus_reset, NULL, ata_std_postreset);
+ ata_bmdma_drive_eh(ap, ata_std_prereset, sil680_bus_reset, NULL, ata_std_postreset);
}
/**
@@ -257,6 +251,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = sil680_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = sil680_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c
index 8dc3bc4..a3fbcee 100644
--- a/drivers/ata/pata_sis.c
+++ b/drivers/ata/pata_sis.c
@@ -35,7 +35,7 @@
#include "sis.h"
#define DRV_NAME "pata_sis"
-#define DRV_VERSION "0.5.0"
+#define DRV_VERSION "0.5.1"
struct sis_chipset {
u16 device; /* PCI host ID */
@@ -86,14 +86,55 @@
}
/**
- * sis_133_pre_reset - check for 40/80 pin
+ * sis_133_cable_detect - check for 40/80 pin
* @ap: Port
*
* Perform cable detection for the later UDMA133 capable
* SiS chipset.
*/
-static int sis_133_pre_reset(struct ata_port *ap)
+static int sis_133_cable_detect(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u16 tmp;
+
+ /* The top bit of this register is the cable detect bit */
+ pci_read_config_word(pdev, 0x50 + 2 * ap->port_no, &tmp);
+ if ((tmp & 0x8000) && !sis_short_ata40(pdev))
+ return ATA_CBL_PATA40;
+ return ATA_CBL_PATA80;
+}
+
+/**
+ * sis_66_cable_detect - check for 40/80 pin
+ * @ap: Port
+ *
+ * Perform cable detection on the UDMA66, UDMA100 and early UDMA133
+ * SiS IDE controllers.
+ */
+
+static int sis_66_cable_detect(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u8 tmp;
+
+ /* Older chips keep cable detect in bits 4/5 of reg 0x48 */
+ pci_read_config_byte(pdev, 0x48, &tmp);
+ tmp >>= ap->port_no;
+ if ((tmp & 0x10) && !sis_short_ata40(pdev))
+ return ATA_CBL_PATA40;
+ return ATA_CBL_PATA80;
+}
+
+
+/**
+ * sis_pre_reset - probe begin
+ * @ap: ATA port
+ *
+ * Set up cable type and use generic probe init
+ */
+
+static int sis_pre_reset(struct ata_port *ap)
{
static const struct pci_bits sis_enable_bits[] = {
{ 0x4aU, 1U, 0x02UL, 0x02UL }, /* port 0 */
@@ -101,21 +142,13 @@
};
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- u16 tmp;
if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no]))
return -ENOENT;
-
- /* The top bit of this register is the cable detect bit */
- pci_read_config_word(pdev, 0x50 + 2 * ap->port_no, &tmp);
- if ((tmp & 0x8000) && !sis_short_ata40(pdev))
- ap->cbl = ATA_CBL_PATA40;
- else
- ap->cbl = ATA_CBL_PATA80;
-
return ata_std_prereset(ap);
}
+
/**
* sis_error_handler - Probe specified port on PATA host controller
* @ap: Port to probe
@@ -124,97 +157,9 @@
* None (inherited from caller).
*/
-static void sis_133_error_handler(struct ata_port *ap)
+static void sis_error_handler(struct ata_port *ap)
{
- ata_bmdma_drive_eh(ap, sis_133_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-
-/**
- * sis_66_pre_reset - check for 40/80 pin
- * @ap: Port
- *
- * Perform cable detection on the UDMA66, UDMA100 and early UDMA133
- * SiS IDE controllers.
- */
-
-static int sis_66_pre_reset(struct ata_port *ap)
-{
- static const struct pci_bits sis_enable_bits[] = {
- { 0x4aU, 1U, 0x02UL, 0x02UL }, /* port 0 */
- { 0x4aU, 1U, 0x04UL, 0x04UL }, /* port 1 */
- };
-
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- u8 tmp;
-
- if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n");
- return 0;
- }
- /* Older chips keep cable detect in bits 4/5 of reg 0x48 */
- pci_read_config_byte(pdev, 0x48, &tmp);
- tmp >>= ap->port_no;
- if ((tmp & 0x10) && !sis_short_ata40(pdev))
- ap->cbl = ATA_CBL_PATA40;
- else
- ap->cbl = ATA_CBL_PATA80;
-
- return ata_std_prereset(ap);
-}
-
-/**
- * sis_66_error_handler - Probe specified port on PATA host controller
- * @ap: Port to probe
- * @classes:
- *
- * LOCKING:
- * None (inherited from caller).
- */
-
-static void sis_66_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, sis_66_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
-}
-
-/**
- * sis_old_pre_reset - probe begin
- * @ap: ATA port
- *
- * Set up cable type and use generic probe init
- */
-
-static int sis_old_pre_reset(struct ata_port *ap)
-{
- static const struct pci_bits sis_enable_bits[] = {
- { 0x4aU, 1U, 0x02UL, 0x02UL }, /* port 0 */
- { 0x4aU, 1U, 0x04UL, 0x04UL }, /* port 1 */
- };
-
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-
- if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n");
- return 0;
- }
- ap->cbl = ATA_CBL_PATA40;
- return ata_std_prereset(ap);
-}
-
-
-/**
- * sis_old_error_handler - Probe specified port on PATA host controller
- * @ap: Port to probe
- *
- * LOCKING:
- * None (inherited from caller).
- */
-
-static void sis_old_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, sis_old_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
+ ata_bmdma_drive_eh(ap, sis_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
}
/**
@@ -494,7 +439,7 @@
int drive_pci = sis_port_base(adev);
u16 timing;
- const u16 udma_bits[] = { 0x8F00, 0x8A00, 0x8700, 0x8500, 0x8300, 0x8200, 0x8100};
+ static const u16 udma_bits[] = { 0x8F00, 0x8A00, 0x8700, 0x8500, 0x8300, 0x8200, 0x8100};
pci_read_config_word(pdev, drive_pci, &timing);
@@ -531,8 +476,8 @@
u32 reg54;
/* bits 4- cycle time 8 - cvs time */
- const u32 timing_u100[] = { 0x6B0, 0x470, 0x350, 0x140, 0x120, 0x110, 0x000 };
- const u32 timing_u133[] = { 0x9F0, 0x6A0, 0x470, 0x250, 0x230, 0x220, 0x210 };
+ static const u32 timing_u100[] = { 0x6B0, 0x470, 0x350, 0x140, 0x120, 0x110, 0x000 };
+ static const u32 timing_u133[] = { 0x9F0, 0x6A0, 0x470, 0x250, 0x230, 0x220, 0x210 };
/* If bit 14 is set then the registers are mapped at 0x70 not 0x40 */
pci_read_config_dword(pdev, 0x54, ®54);
@@ -595,8 +540,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = sis_133_error_handler,
+ .error_handler = sis_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = sis_133_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -628,8 +574,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = sis_66_error_handler,
+ .error_handler = sis_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = sis_66_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -661,9 +608,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = sis_66_error_handler,
+ .error_handler = sis_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
-
+ .cable_detect = sis_66_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -692,10 +639,11 @@
.check_status = ata_check_status,
.exec_command = ata_exec_command,
.dev_select = ata_std_dev_select,
+ .cable_detect = sis_66_cable_detect,
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = sis_66_error_handler,
+ .error_handler = sis_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
.bmdma_setup = ata_bmdma_setup,
@@ -728,8 +676,9 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = sis_old_error_handler,
+ .error_handler = sis_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c
index b681441..da9e22b 100644
--- a/drivers/ata/pata_sl82c105.c
+++ b/drivers/ata/pata_sl82c105.c
@@ -58,7 +58,6 @@
if (ap->port_no && !pci_test_config_bits(pdev, &sl82c105_enable_bits[ap->port_no]))
return -ENOENT;
- ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -238,6 +237,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = sl82c105_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = sl82c105_bmdma_start,
diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c
index 71418f2..e618ffd 100644
--- a/drivers/ata/pata_triflex.c
+++ b/drivers/ata/pata_triflex.c
@@ -43,7 +43,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_triflex"
-#define DRV_VERSION "0.2.7"
+#define DRV_VERSION "0.2.8"
/**
* triflex_prereset - probe begin
@@ -63,7 +63,6 @@
if (!pci_test_config_bits(pdev, &triflex_enable_bits[ap->port_no]))
return -ENOENT;
- ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -214,6 +213,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = triflex_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = triflex_bmdma_start,
diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c
index 946ade0..96b7179 100644
--- a/drivers/ata/pata_via.c
+++ b/drivers/ata/pata_via.c
@@ -62,7 +62,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_via"
-#define DRV_VERSION "0.2.1"
+#define DRV_VERSION "0.3.1"
/*
* The following comes directly from Vojtech Pavlik's ide/pci/via82cxxx
@@ -135,16 +135,23 @@
*/
static int via_cable_detect(struct ata_port *ap) {
+ const struct via_isa_bridge *config = ap->host->private_data;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u32 ata66;
+ /* Early chips are 40 wire */
+ if ((config->flags & VIA_UDMA) < VIA_UDMA_66)
+ return ATA_CBL_PATA40;
+ /* UDMA 66 chips have only drive side logic */
+ else if((config->flags & VIA_UDMA) < VIA_UDMA_100)
+ return ATA_CBL_PATA_UNK;
+ /* UDMA 100 or later */
pci_read_config_dword(pdev, 0x50, &ata66);
/* Check both the drive cable reporting bits, we might not have
two drives */
if (ata66 & (0x10100000 >> (16 * ap->port_no)))
return ATA_CBL_PATA80;
- else
- return ATA_CBL_PATA40;
+ return ATA_CBL_PATA40;
}
static int via_pre_reset(struct ata_port *ap)
@@ -156,22 +163,10 @@
{ 0x40, 1, 0x02, 0x02 },
{ 0x40, 1, 0x01, 0x01 }
};
-
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
-
if (!pci_test_config_bits(pdev, &via_enable_bits[ap->port_no]))
return -ENOENT;
}
-
- if ((config->flags & VIA_UDMA) >= VIA_UDMA_100)
- ap->cbl = via_cable_detect(ap);
- /* The UDMA66 series has no cable detect so do drive side detect */
- else if ((config->flags & VIA_UDMA) < VIA_UDMA_66)
- ap->cbl = ATA_CBL_PATA40;
- else
- ap->cbl = ATA_CBL_PATA_UNK;
-
-
return ata_std_prereset(ap);
}
@@ -327,6 +322,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = via_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = via_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
@@ -362,6 +358,7 @@
.thaw = ata_bmdma_thaw,
.error_handler = via_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = via_cable_detect,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
diff --git a/drivers/ata/pata_winbond.c b/drivers/ata/pata_winbond.c
index 6c11103..cc4ad27 100644
--- a/drivers/ata/pata_winbond.c
+++ b/drivers/ata/pata_winbond.c
@@ -8,7 +8,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/pci.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
@@ -36,7 +35,7 @@
static int probe_winbond;
#endif
-static spinlock_t winbond_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(winbond_lock);
static void winbond_writecfg(unsigned long port, u8 reg, u8 val)
{
@@ -152,13 +151,13 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_40wire,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
.data_xfer = winbond_data_xfer,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -179,11 +178,9 @@
static __init int winbond_init_one(unsigned long port)
{
- struct ata_probe_ent ae;
struct platform_device *pdev;
- int ret;
u8 reg;
- int i;
+ int i, rc;
reg = winbond_readcfg(port, 0x81);
reg |= 0x80; /* jumpered mode off */
@@ -202,58 +199,56 @@
for (i = 0; i < 2 ; i ++) {
unsigned long cmd_port = 0x1F0 - (0x80 * i);
+ struct ata_host *host;
+ struct ata_port *ap;
void __iomem *cmd_addr, *ctl_addr;
- if (reg & (1 << i)) {
- /*
- * Fill in a probe structure first of all
- */
+ if (!(reg & (1 << i)))
+ continue;
- pdev = platform_device_register_simple(DRV_NAME, nr_winbond_host, NULL, 0);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
+ pdev = platform_device_register_simple(DRV_NAME, nr_winbond_host, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- cmd_addr = devm_ioport_map(&pdev->dev, cmd_port, 8);
- ctl_addr = devm_ioport_map(&pdev->dev, cmd_port + 0x0206, 1);
- if (!cmd_addr || !ctl_addr) {
- platform_device_unregister(pdev);
- return -ENOMEM;
- }
+ rc = -ENOMEM;
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ goto err_unregister;
- memset(&ae, 0, sizeof(struct ata_probe_ent));
- INIT_LIST_HEAD(&ae.node);
- ae.dev = &pdev->dev;
+ rc = -ENOMEM;
+ cmd_addr = devm_ioport_map(&pdev->dev, cmd_port, 8);
+ ctl_addr = devm_ioport_map(&pdev->dev, cmd_port + 0x0206, 1);
+ if (!cmd_addr || !ctl_addr)
+ goto err_unregister;
- ae.port_ops = &winbond_port_ops;
- ae.pio_mask = 0x1F;
+ ap = host->ports[0];
+ ap->ops = &winbond_port_ops;
+ ap->pio_mask = 0x1F;
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
+ ap->ioaddr.cmd_addr = cmd_addr;
+ ap->ioaddr.altstatus_addr = ctl_addr;
+ ap->ioaddr.ctl_addr = ctl_addr;
+ ata_std_ports(&ap->ioaddr);
- ae.sht = &winbond_sht;
+ /* hook in a private data structure per channel */
+ host->private_data = &winbond_data[nr_winbond_host];
+ winbond_data[nr_winbond_host].config = port;
+ winbond_data[nr_winbond_host].platform_dev = pdev;
- ae.n_ports = 1;
- ae.irq = 14 + i;
- ae.irq_flags = 0;
- ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
- ae.port[0].cmd_addr = cmd_addr;
- ae.port[0].altstatus_addr = ctl_addr;
- ae.port[0].ctl_addr = ctl_addr;
- ata_std_ports(&ae.port[0]);
- /*
- * Hook in a private data structure per channel
- */
- ae.private_data = &winbond_data[nr_winbond_host];
- winbond_data[nr_winbond_host].config = port;
- winbond_data[nr_winbond_host].platform_dev = pdev;
+ /* activate */
+ rc = ata_host_activate(host, 14 + i, ata_interrupt, 0,
+ &winbond_sht);
+ if (rc)
+ goto err_unregister;
- ret = ata_device_add(&ae);
- if (ret == 0) {
- platform_device_unregister(pdev);
- return -ENODEV;
- }
- winbond_host[nr_winbond_host++] = dev_get_drvdata(&pdev->dev);
- }
+ winbond_host[nr_winbond_host++] = dev_get_drvdata(&pdev->dev);
}
return 0;
+
+ err_unregister:
+ platform_device_unregister(pdev);
+ return rc;
}
/**
diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c
index 5dd3ca8..52b6953 100644
--- a/drivers/ata/pdc_adma.c
+++ b/drivers/ata/pdc_adma.c
@@ -52,9 +52,9 @@
/* macro to calculate base address for ADMA regs */
#define ADMA_REGS(base,port_no) ((base) + 0x80 + ((port_no) * 0x20))
-/* macro to obtain addresses from ata_host */
-#define ADMA_HOST_REGS(host,port_no) \
- ADMA_REGS((host)->iomap[ADMA_MMIO_BAR], port_no)
+/* macro to obtain addresses from ata_port */
+#define ADMA_PORT_REGS(ap) \
+ ADMA_REGS((ap)->host->iomap[ADMA_MMIO_BAR], ap->port_no)
enum {
ADMA_MMIO_BAR = 4,
@@ -128,7 +128,6 @@
static int adma_ata_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent);
-static irqreturn_t adma_intr (int irq, void *dev_instance);
static int adma_port_start(struct ata_port *ap);
static void adma_host_stop(struct ata_host *host);
static void adma_port_stop(struct ata_port *ap);
@@ -172,7 +171,6 @@
.qc_issue = adma_qc_issue,
.eng_timeout = adma_eng_timeout,
.data_xfer = ata_data_xfer,
- .irq_handler = adma_intr,
.irq_clear = adma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -186,7 +184,6 @@
static struct ata_port_info adma_port_info[] = {
/* board_1841_idx */
{
- .sht = &adma_ata_sht,
.flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST |
ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO |
ATA_FLAG_PIO_POLLING,
@@ -229,8 +226,10 @@
/* nothing */
}
-static void adma_reset_engine(void __iomem *chan)
+static void adma_reset_engine(struct ata_port *ap)
{
+ void __iomem *chan = ADMA_PORT_REGS(ap);
+
/* reset ADMA to idle state */
writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL);
udelay(2);
@@ -241,14 +240,14 @@
static void adma_reinit_engine(struct ata_port *ap)
{
struct adma_port_priv *pp = ap->private_data;
- void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no);
+ void __iomem *chan = ADMA_PORT_REGS(ap);
/* mask/clear ATA interrupts */
writeb(ATA_NIEN, ap->ioaddr.ctl_addr);
ata_check_status(ap);
/* reset the ADMA engine */
- adma_reset_engine(chan);
+ adma_reset_engine(ap);
/* set in-FIFO threshold to 0x100 */
writew(0x100, chan + ADMA_FIFO_IN);
@@ -268,7 +267,7 @@
static inline void adma_enter_reg_mode(struct ata_port *ap)
{
- void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no);
+ void __iomem *chan = ADMA_PORT_REGS(ap);
writew(aPIOMD4, chan + ADMA_CONTROL);
readb(chan + ADMA_STATUS); /* flush */
@@ -415,7 +414,7 @@
static inline void adma_packet_start(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
- void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no);
+ void __iomem *chan = ADMA_PORT_REGS(ap);
VPRINTK("ENTER, ap %p\n", ap);
@@ -453,7 +452,7 @@
struct ata_port *ap = host->ports[port_no];
struct adma_port_priv *pp;
struct ata_queued_cmd *qc;
- void __iomem *chan = ADMA_HOST_REGS(host, port_no);
+ void __iomem *chan = ADMA_PORT_REGS(ap);
u8 status = readb(chan + ADMA_STATUS);
if (status == 0)
@@ -575,7 +574,7 @@
static void adma_port_stop(struct ata_port *ap)
{
- adma_reset_engine(ADMA_HOST_REGS(ap->host, ap->port_no));
+ adma_reset_engine(ap);
}
static void adma_host_stop(struct ata_host *host)
@@ -583,21 +582,19 @@
unsigned int port_no;
for (port_no = 0; port_no < ADMA_PORTS; ++port_no)
- adma_reset_engine(ADMA_HOST_REGS(host, port_no));
+ adma_reset_engine(host->ports[port_no]);
}
-static void adma_host_init(unsigned int chip_id,
- struct ata_probe_ent *probe_ent)
+static void adma_host_init(struct ata_host *host, unsigned int chip_id)
{
unsigned int port_no;
- void __iomem *mmio_base = probe_ent->iomap[ADMA_MMIO_BAR];
/* enable/lock aGO operation */
- writeb(7, mmio_base + ADMA_MODE_LOCK);
+ writeb(7, host->iomap[ADMA_MMIO_BAR] + ADMA_MODE_LOCK);
/* reset the ADMA logic */
for (port_no = 0; port_no < ADMA_PORTS; ++port_no)
- adma_reset_engine(ADMA_REGS(mmio_base, port_no));
+ adma_reset_engine(host->ports[port_no]);
}
static int adma_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base)
@@ -623,14 +620,21 @@
const struct pci_device_id *ent)
{
static int printed_version;
- struct ata_probe_ent *probe_ent = NULL;
- void __iomem *mmio_base;
unsigned int board_idx = (unsigned int) ent->driver_data;
+ const struct ata_port_info *ppi[] = { &adma_port_info[board_idx], NULL };
+ struct ata_host *host;
+ void __iomem *mmio_base;
int rc, port_no;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ /* alloc host */
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, ADMA_PORTS);
+ if (!host)
+ return -ENOMEM;
+
+ /* acquire resources and fill host */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
@@ -641,46 +645,23 @@
rc = pcim_iomap_regions(pdev, 1 << ADMA_MMIO_BAR, DRV_NAME);
if (rc)
return rc;
- mmio_base = pcim_iomap_table(pdev)[ADMA_MMIO_BAR];
+ host->iomap = pcim_iomap_table(pdev);
+ mmio_base = host->iomap[ADMA_MMIO_BAR];
rc = adma_set_dma_masks(pdev, mmio_base);
if (rc)
return rc;
- probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
- if (probe_ent == NULL)
- return -ENOMEM;
-
- probe_ent->dev = pci_dev_to_dev(pdev);
- INIT_LIST_HEAD(&probe_ent->node);
-
- probe_ent->sht = adma_port_info[board_idx].sht;
- probe_ent->port_flags = adma_port_info[board_idx].flags;
- probe_ent->pio_mask = adma_port_info[board_idx].pio_mask;
- probe_ent->mwdma_mask = adma_port_info[board_idx].mwdma_mask;
- probe_ent->udma_mask = adma_port_info[board_idx].udma_mask;
- probe_ent->port_ops = adma_port_info[board_idx].port_ops;
-
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->n_ports = ADMA_PORTS;
- probe_ent->iomap = pcim_iomap_table(pdev);
-
- for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) {
- adma_ata_setup_port(&probe_ent->port[port_no],
+ for (port_no = 0; port_no < ADMA_PORTS; ++port_no)
+ adma_ata_setup_port(&host->ports[port_no]->ioaddr,
ADMA_ATA_REGS(mmio_base, port_no));
- }
-
- pci_set_master(pdev);
/* initialize adapter */
- adma_host_init(board_idx, probe_ent);
+ adma_host_init(host, board_idx);
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(&pdev->dev, probe_ent);
- return 0;
+ pci_set_master(pdev);
+ return ata_host_activate(host, pdev->irq, adma_intr, IRQF_SHARED,
+ &adma_ata_sht);
}
static int __init adma_ata_init(void)
diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c
index 1e21688..f099a1d 100644
--- a/drivers/ata/sata_inic162x.c
+++ b/drivers/ata/sata_inic162x.c
@@ -488,11 +488,11 @@
static void inic_post_internal_cmd(struct ata_queued_cmd *qc)
{
/* make DMA engine forget about the failed command */
- if (qc->err_mask)
+ if (qc->flags & ATA_QCFLAG_FAILED)
inic_reset_port(inic_port_base(qc->ap));
}
-static void inic_dev_config(struct ata_port *ap, struct ata_device *dev)
+static void inic_dev_config(struct ata_device *dev)
{
/* inic can only handle upto LBA28 max sectors */
if (dev->max_sectors > ATA_MAX_SECTORS)
@@ -559,7 +559,6 @@
.bmdma_stop = inic_bmdma_stop,
.bmdma_status = inic_bmdma_status,
- .irq_handler = inic_interrupt,
.irq_clear = inic_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -580,7 +579,6 @@
};
static struct ata_port_info inic_port_info = {
- .sht = &inic_sht,
/* For some reason, ATA_PROT_ATAPI is broken on this
* controller, and no, PIO_POLLING does't fix it. It somehow
* manages to report the wrong ireason and ignoring ireason
@@ -642,7 +640,9 @@
void __iomem *mmio_base = host->iomap[MMIO_BAR];
int rc;
- ata_pci_device_do_resume(pdev);
+ rc = ata_pci_device_do_resume(pdev);
+ if (rc)
+ return rc;
if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
rc = init_controller(mmio_base, hpriv->cached_hctl);
@@ -659,8 +659,8 @@
static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
- struct ata_port_info *pinfo = &inic_port_info;
- struct ata_probe_ent *probe_ent;
+ const struct ata_port_info *ppi[] = { &inic_port_info, NULL };
+ struct ata_host *host;
struct inic_host_priv *hpriv;
void __iomem * const *iomap;
int i, rc;
@@ -668,6 +668,15 @@
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ /* alloc host */
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, NR_PORTS);
+ hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
+ if (!host || !hpriv)
+ return -ENOMEM;
+
+ host->private_data = hpriv;
+
+ /* acquire resources and fill host */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
@@ -675,7 +684,22 @@
rc = pcim_iomap_regions(pdev, 0x3f, DRV_NAME);
if (rc)
return rc;
- iomap = pcim_iomap_table(pdev);
+ host->iomap = iomap = pcim_iomap_table(pdev);
+
+ for (i = 0; i < NR_PORTS; i++) {
+ struct ata_ioports *port = &host->ports[i]->ioaddr;
+ void __iomem *port_base = iomap[MMIO_BAR] + i * PORT_SIZE;
+
+ port->cmd_addr = iomap[2 * i];
+ port->altstatus_addr =
+ port->ctl_addr = (void __iomem *)
+ ((unsigned long)iomap[2 * i + 1] | ATA_PCI_CTL_OFS);
+ port->scr_addr = port_base + PORT_SCR;
+
+ ata_std_ports(port);
+ }
+
+ hpriv->cached_hctl = readw(iomap[MMIO_BAR] + HOST_CTL);
/* Set dma_mask. This devices doesn't support 64bit addressing. */
rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
@@ -692,43 +716,6 @@
return rc;
}
- probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
- hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
- if (!probe_ent || !hpriv)
- return -ENOMEM;
-
- probe_ent->dev = &pdev->dev;
- INIT_LIST_HEAD(&probe_ent->node);
-
- probe_ent->sht = pinfo->sht;
- probe_ent->port_flags = pinfo->flags;
- probe_ent->pio_mask = pinfo->pio_mask;
- probe_ent->mwdma_mask = pinfo->mwdma_mask;
- probe_ent->udma_mask = pinfo->udma_mask;
- probe_ent->port_ops = pinfo->port_ops;
- probe_ent->n_ports = NR_PORTS;
-
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
-
- probe_ent->iomap = iomap;
-
- for (i = 0; i < NR_PORTS; i++) {
- struct ata_ioports *port = &probe_ent->port[i];
- void __iomem *port_base = iomap[MMIO_BAR] + i * PORT_SIZE;
-
- port->cmd_addr = iomap[2 * i];
- port->altstatus_addr =
- port->ctl_addr = (void __iomem *)
- ((unsigned long)iomap[2 * i + 1] | ATA_PCI_CTL_OFS);
- port->scr_addr = port_base + PORT_SCR;
-
- ata_std_ports(port);
- }
-
- probe_ent->private_data = hpriv;
- hpriv->cached_hctl = readw(iomap[MMIO_BAR] + HOST_CTL);
-
rc = init_controller(iomap[MMIO_BAR], hpriv->cached_hctl);
if (rc) {
dev_printk(KERN_ERR, &pdev->dev,
@@ -737,13 +724,8 @@
}
pci_set_master(pdev);
-
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(&pdev->dev, probe_ent);
-
- return 0;
+ return ata_host_activate(host, pdev->irq, inic_interrupt, IRQF_SHARED,
+ &inic_sht);
}
static const struct pci_device_id inic_pci_tbl[] = {
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index a65ba63..cb9b9ac 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -253,10 +253,7 @@
#define IS_GEN_IIE(hpriv) ((hpriv)->hp_flags & MV_HP_GEN_IIE)
enum {
- /* Our DMA boundary is determined by an ePRD being unable to handle
- * anything larger than 64KB
- */
- MV_DMA_BOUNDARY = 0xffffU,
+ MV_DMA_BOUNDARY = 0xffffffffU,
EDMA_REQ_Q_BASE_LO_MASK = 0xfffffc00U,
@@ -350,7 +347,6 @@
static void mv_qc_prep(struct ata_queued_cmd *qc);
static void mv_qc_prep_iie(struct ata_queued_cmd *qc);
static unsigned int mv_qc_issue(struct ata_queued_cmd *qc);
-static irqreturn_t mv_interrupt(int irq, void *dev_instance);
static void mv_eng_timeout(struct ata_port *ap);
static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
@@ -384,10 +380,10 @@
.queuecommand = ata_scsi_queuecmd,
.can_queue = MV_USE_Q_DEPTH,
.this_id = ATA_SHT_THIS_ID,
- .sg_tablesize = MV_MAX_SG_CT / 2,
+ .sg_tablesize = MV_MAX_SG_CT,
.cmd_per_lun = ATA_SHT_CMD_PER_LUN,
.emulated = ATA_SHT_EMULATED,
- .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .use_clustering = 1,
.proc_name = DRV_NAME,
.dma_boundary = MV_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
@@ -405,6 +401,7 @@
.dev_select = ata_std_dev_select,
.phy_reset = mv_phy_reset,
+ .cable_detect = ata_cable_sata,
.qc_prep = mv_qc_prep,
.qc_issue = mv_qc_issue,
@@ -412,7 +409,6 @@
.eng_timeout = mv_eng_timeout,
- .irq_handler = mv_interrupt,
.irq_clear = mv_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -434,6 +430,7 @@
.dev_select = ata_std_dev_select,
.phy_reset = mv_phy_reset,
+ .cable_detect = ata_cable_sata,
.qc_prep = mv_qc_prep,
.qc_issue = mv_qc_issue,
@@ -441,7 +438,6 @@
.eng_timeout = mv_eng_timeout,
- .irq_handler = mv_interrupt,
.irq_clear = mv_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -463,6 +459,7 @@
.dev_select = ata_std_dev_select,
.phy_reset = mv_phy_reset,
+ .cable_detect = ata_cable_sata,
.qc_prep = mv_qc_prep_iie,
.qc_issue = mv_qc_issue,
@@ -470,7 +467,6 @@
.eng_timeout = mv_eng_timeout,
- .irq_handler = mv_interrupt,
.irq_clear = mv_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -484,35 +480,30 @@
static const struct ata_port_info mv_port_info[] = {
{ /* chip_504x */
- .sht = &mv_sht,
.flags = MV_COMMON_FLAGS,
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 */
.port_ops = &mv5_ops,
},
{ /* chip_508x */
- .sht = &mv_sht,
.flags = (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC),
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 */
.port_ops = &mv5_ops,
},
{ /* chip_5080 */
- .sht = &mv_sht,
.flags = (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC),
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 */
.port_ops = &mv5_ops,
},
{ /* chip_604x */
- .sht = &mv_sht,
.flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS),
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 */
.port_ops = &mv6_ops,
},
{ /* chip_608x */
- .sht = &mv_sht,
.flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS |
MV_FLAG_DUAL_HC),
.pio_mask = 0x1f, /* pio0-4 */
@@ -520,14 +511,12 @@
.port_ops = &mv6_ops,
},
{ /* chip_6042 */
- .sht = &mv_sht,
.flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS),
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 */
.port_ops = &mv_iie_ops,
},
{ /* chip_7042 */
- .sht = &mv_sht,
.flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS),
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 */
@@ -551,6 +540,9 @@
{ PCI_VDEVICE(TTI, 0x2310), chip_7042 },
+ /* add Marvell 7042 support */
+ { PCI_VDEVICE(MARVELL, 0x7042), chip_7042 },
+
{ } /* terminate list */
};
@@ -585,6 +577,39 @@
static int msi; /* Use PCI msi; either zero (off, default) or non-zero */
+/* move to PCI layer or libata core? */
+static int pci_go_64(struct pci_dev *pdev)
+{
+ int rc;
+
+ if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
+ rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
+ if (rc) {
+ rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "64-bit DMA enable failed\n");
+ return rc;
+ }
+ }
+ } else {
+ rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "32-bit DMA enable failed\n");
+ return rc;
+ }
+ rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "32-bit consistent DMA enable failed\n");
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
/*
* Functions
*/
@@ -798,20 +823,18 @@
{
unsigned int ofs = mv_scr_offset(sc_reg_in);
- if (0xffffffffU != ofs) {
+ if (0xffffffffU != ofs)
return readl(mv_ap_base(ap) + ofs);
- } else {
+ else
return (u32) ofs;
- }
}
static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val)
{
unsigned int ofs = mv_scr_offset(sc_reg_in);
- if (0xffffffffU != ofs) {
+ if (0xffffffffU != ofs)
writelfl(val, mv_ap_base(ap) + ofs);
- }
}
static void mv_edma_cfg(struct mv_host_priv *hpriv, void __iomem *port_mmio)
@@ -959,38 +982,30 @@
* LOCKING:
* Inherited from caller.
*/
-static void mv_fill_sg(struct ata_queued_cmd *qc)
+static unsigned int mv_fill_sg(struct ata_queued_cmd *qc)
{
struct mv_port_priv *pp = qc->ap->private_data;
- unsigned int i = 0;
+ unsigned int n_sg = 0;
struct scatterlist *sg;
+ struct mv_sg *mv_sg;
+ mv_sg = pp->sg_tbl;
ata_for_each_sg(sg, qc) {
- dma_addr_t addr;
- u32 sg_len, len, offset;
+ dma_addr_t addr = sg_dma_address(sg);
+ u32 sg_len = sg_dma_len(sg);
- addr = sg_dma_address(sg);
- sg_len = sg_dma_len(sg);
+ mv_sg->addr = cpu_to_le32(addr & 0xffffffff);
+ mv_sg->addr_hi = cpu_to_le32((addr >> 16) >> 16);
+ mv_sg->flags_size = cpu_to_le32(sg_len & 0xffff);
- while (sg_len) {
- offset = addr & MV_DMA_BOUNDARY;
- len = sg_len;
- if ((offset + sg_len) > 0x10000)
- len = 0x10000 - offset;
+ if (ata_sg_is_last(sg, qc))
+ mv_sg->flags_size |= cpu_to_le32(EPRD_FLAG_END_OF_TBL);
- pp->sg_tbl[i].addr = cpu_to_le32(addr & 0xffffffff);
- pp->sg_tbl[i].addr_hi = cpu_to_le32((addr >> 16) >> 16);
- pp->sg_tbl[i].flags_size = cpu_to_le32(len & 0xffff);
-
- sg_len -= len;
- addr += len;
-
- if (!sg_len && ata_sg_is_last(sg, qc))
- pp->sg_tbl[i].flags_size |= cpu_to_le32(EPRD_FLAG_END_OF_TBL);
-
- i++;
- }
+ mv_sg++;
+ n_sg++;
}
+
+ return n_sg;
}
static inline unsigned mv_inc_q_index(unsigned index)
@@ -1320,17 +1335,15 @@
int shift, port, port0, hard_port, handled;
unsigned int err_mask;
- if (hc == 0) {
+ if (hc == 0)
port0 = 0;
- } else {
+ else
port0 = MV_PORTS_PER_HC;
- }
/* we'll need the HC success int register in most cases */
hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS);
- if (hc_irq_cause) {
+ if (hc_irq_cause)
writelfl(~hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS);
- }
VPRINTK("ENTER, hc%u relevant=0x%08x HC IRQ cause=0x%08x\n",
hc,relevant,hc_irq_cause);
@@ -1425,9 +1438,8 @@
/* check the cases where we either have nothing pending or have read
* a bogus register value which can indicate HW removal or PCI fault
*/
- if (!irq_stat || (0xffffffffU == irq_stat)) {
+ if (!irq_stat || (0xffffffffU == irq_stat))
return IRQ_NONE;
- }
n_hcs = mv_get_hc_count(host->ports[0]->flags);
spin_lock(&host->lock);
@@ -1952,7 +1964,6 @@
ata_port_disable(ap);
return;
}
- ap->cbl = ATA_CBL_SATA;
/* even after SStatus reflects that device is ready,
* it seems to take a while for link to be fully
@@ -2077,9 +2088,10 @@
readl(port_mmio + EDMA_ERR_IRQ_MASK_OFS));
}
-static int mv_chip_id(struct pci_dev *pdev, struct mv_host_priv *hpriv,
- unsigned int board_idx)
+static int mv_chip_id(struct ata_host *host, unsigned int board_idx)
{
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ struct mv_host_priv *hpriv = host->private_data;
u8 rev_id;
u32 hp_flags = hpriv->hp_flags;
@@ -2177,8 +2189,8 @@
/**
* mv_init_host - Perform some early initialization of the host.
- * @pdev: host PCI device
- * @probe_ent: early data struct representing the host
+ * @host: ATA host to initialize
+ * @board_idx: controller index
*
* If possible, do an early global reset of the host. Then do
* our port init and clear/unmask all/relevant host interrupts.
@@ -2186,24 +2198,23 @@
* LOCKING:
* Inherited from caller.
*/
-static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent,
- unsigned int board_idx)
+static int mv_init_host(struct ata_host *host, unsigned int board_idx)
{
int rc = 0, n_hc, port, hc;
- void __iomem *mmio = probe_ent->iomap[MV_PRIMARY_BAR];
- struct mv_host_priv *hpriv = probe_ent->private_data;
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ void __iomem *mmio = host->iomap[MV_PRIMARY_BAR];
+ struct mv_host_priv *hpriv = host->private_data;
/* global interrupt mask */
writel(0, mmio + HC_MAIN_IRQ_MASK_OFS);
- rc = mv_chip_id(pdev, hpriv, board_idx);
+ rc = mv_chip_id(host, board_idx);
if (rc)
goto done;
- n_hc = mv_get_hc_count(probe_ent->port_flags);
- probe_ent->n_ports = MV_PORTS_PER_HC * n_hc;
+ n_hc = mv_get_hc_count(host->ports[0]->flags);
- for (port = 0; port < probe_ent->n_ports; port++)
+ for (port = 0; port < host->n_ports; port++)
hpriv->ops->read_preamp(hpriv, port, mmio);
rc = hpriv->ops->reset_hc(hpriv, mmio, n_hc);
@@ -2214,7 +2225,7 @@
hpriv->ops->reset_bus(pdev, mmio);
hpriv->ops->enable_leds(hpriv, mmio);
- for (port = 0; port < probe_ent->n_ports; port++) {
+ for (port = 0; port < host->n_ports; port++) {
if (IS_60XX(hpriv)) {
void __iomem *port_mmio = mv_port_base(mmio, port);
@@ -2227,9 +2238,9 @@
hpriv->ops->phy_errata(hpriv, mmio, port);
}
- for (port = 0; port < probe_ent->n_ports; port++) {
+ for (port = 0; port < host->n_ports; port++) {
void __iomem *port_mmio = mv_port_base(mmio, port);
- mv_port_init(&probe_ent->port[port], port_mmio);
+ mv_port_init(&host->ports[port]->ioaddr, port_mmio);
}
for (hc = 0; hc < n_hc; hc++) {
@@ -2268,17 +2279,17 @@
/**
* mv_print_info - Dump key info to kernel log for perusal.
- * @probe_ent: early data struct representing the host
+ * @host: ATA host to print info about
*
* FIXME: complete this.
*
* LOCKING:
* Inherited from caller.
*/
-static void mv_print_info(struct ata_probe_ent *probe_ent)
+static void mv_print_info(struct ata_host *host)
{
- struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
- struct mv_host_priv *hpriv = probe_ent->private_data;
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ struct mv_host_priv *hpriv = host->private_data;
u8 rev_id, scc;
const char *scc_s;
@@ -2297,7 +2308,7 @@
dev_printk(KERN_INFO, &pdev->dev,
"%u slots %u ports %s mode IRQ via %s\n",
- (unsigned)MV_MAX_Q_DEPTH, probe_ent->n_ports,
+ (unsigned)MV_MAX_Q_DEPTH, host->n_ports,
scc_s, (MV_HP_FLAG_MSI & hpriv->hp_flags) ? "MSI" : "INTx");
}
@@ -2312,50 +2323,42 @@
static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version = 0;
- struct device *dev = &pdev->dev;
- struct ata_probe_ent *probe_ent;
- struct mv_host_priv *hpriv;
unsigned int board_idx = (unsigned int)ent->driver_data;
- int rc;
+ const struct ata_port_info *ppi[] = { &mv_port_info[board_idx], NULL };
+ struct ata_host *host;
+ struct mv_host_priv *hpriv;
+ int n_ports, rc;
if (!printed_version++)
dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
+ /* allocate host */
+ n_ports = mv_get_hc_count(ppi[0]->flags) * MV_PORTS_PER_HC;
+
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
+ hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
+ if (!host || !hpriv)
+ return -ENOMEM;
+ host->private_data = hpriv;
+
+ /* acquire resources */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
- pci_set_master(pdev);
rc = pcim_iomap_regions(pdev, 1 << MV_PRIMARY_BAR, DRV_NAME);
if (rc == -EBUSY)
pcim_pin_device(pdev);
if (rc)
return rc;
+ host->iomap = pcim_iomap_table(pdev);
- probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
- if (probe_ent == NULL)
- return -ENOMEM;
-
- probe_ent->dev = pci_dev_to_dev(pdev);
- INIT_LIST_HEAD(&probe_ent->node);
-
- hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
- if (!hpriv)
- return -ENOMEM;
-
- probe_ent->sht = mv_port_info[board_idx].sht;
- probe_ent->port_flags = mv_port_info[board_idx].flags;
- probe_ent->pio_mask = mv_port_info[board_idx].pio_mask;
- probe_ent->udma_mask = mv_port_info[board_idx].udma_mask;
- probe_ent->port_ops = mv_port_info[board_idx].port_ops;
-
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->iomap = pcim_iomap_table(pdev);
- probe_ent->private_data = hpriv;
+ rc = pci_go_64(pdev);
+ if (rc)
+ return rc;
/* initialize adapter */
- rc = mv_init_host(pdev, probe_ent, board_idx);
+ rc = mv_init_host(host, board_idx);
if (rc)
return rc;
@@ -2364,13 +2367,11 @@
pci_intx(pdev, 1);
mv_dump_pci_cfg(pdev, 0x68);
- mv_print_info(probe_ent);
+ mv_print_info(host);
- if (ata_device_add(probe_ent) == 0)
- return -ENODEV;
-
- devm_kfree(dev, probe_ent);
- return 0;
+ pci_set_master(pdev);
+ return ata_host_activate(host, pdev->irq, mv_interrupt, IRQF_SHARED,
+ &mv_sht);
}
static int __init mv_init(void)
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 9d9670a..0216974 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -260,6 +260,7 @@
static void nv_adma_error_handler(struct ata_port *ap);
static void nv_adma_host_stop(struct ata_host *host);
static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc);
+static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
enum nv_host_type
{
@@ -368,7 +369,6 @@
.error_handler = nv_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
.data_xfer = ata_data_xfer,
- .irq_handler = nv_generic_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -395,7 +395,6 @@
.error_handler = nv_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
.data_xfer = ata_data_xfer,
- .irq_handler = nv_nf2_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -422,7 +421,6 @@
.error_handler = nv_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
.data_xfer = ata_data_xfer,
- .irq_handler = nv_ck804_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -435,7 +433,7 @@
static const struct ata_port_operations nv_adma_ops = {
.port_disable = ata_port_disable,
.tf_load = ata_tf_load,
- .tf_read = ata_tf_read,
+ .tf_read = nv_adma_tf_read,
.check_atapi_dma = nv_adma_check_atapi_dma,
.exec_command = ata_exec_command,
.check_status = ata_check_status,
@@ -451,7 +449,6 @@
.error_handler = nv_adma_error_handler,
.post_internal_cmd = nv_adma_post_internal_cmd,
.data_xfer = ata_data_xfer,
- .irq_handler = nv_adma_interrupt,
.irq_clear = nv_adma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -476,6 +473,7 @@
.mwdma_mask = NV_MWDMA_MASK,
.udma_mask = NV_UDMA_MASK,
.port_ops = &nv_generic_ops,
+ .irq_handler = nv_generic_interrupt,
},
/* nforce2/3 */
{
@@ -486,6 +484,7 @@
.mwdma_mask = NV_MWDMA_MASK,
.udma_mask = NV_UDMA_MASK,
.port_ops = &nv_nf2_ops,
+ .irq_handler = nv_nf2_interrupt,
},
/* ck804 */
{
@@ -496,6 +495,7 @@
.mwdma_mask = NV_MWDMA_MASK,
.udma_mask = NV_UDMA_MASK,
.port_ops = &nv_ck804_ops,
+ .irq_handler = nv_ck804_interrupt,
},
/* ADMA */
{
@@ -507,6 +507,7 @@
.mwdma_mask = NV_MWDMA_MASK,
.udma_mask = NV_UDMA_MASK,
.port_ops = &nv_adma_ops,
+ .irq_handler = nv_adma_interrupt,
},
};
@@ -667,6 +668,18 @@
return !(pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE);
}
+static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ /* Since commands where a result TF is requested are not
+ executed in ADMA mode, the only time this function will be called
+ in ADMA mode will be if a command fails. In this case we
+ don't care about going into register mode with ADMA commands
+ pending, as the commands will all shortly be aborted anyway. */
+ nv_adma_register_mode(ap);
+
+ ata_tf_read(ap, tf);
+}
+
static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, __le16 *cpb)
{
unsigned int idx = 0;
@@ -738,19 +751,11 @@
return 1;
}
- if (flags & NV_CPB_RESP_DONE) {
+ if (likely(flags & NV_CPB_RESP_DONE)) {
struct ata_queued_cmd *qc = ata_qc_from_tag(ap, cpb_num);
VPRINTK("CPB flags done, flags=0x%x\n", flags);
if (likely(qc)) {
- /* Grab the ATA port status for non-NCQ commands.
- For NCQ commands the current status may have nothing to do with
- the command just completed. */
- if (qc->tf.protocol != ATA_PROT_NCQ) {
- u8 ata_status = readb(pp->ctl_block + (ATA_REG_STATUS * 4));
- qc->err_mask |= ac_err_mask(ata_status);
- }
- DPRINTK("Completing qc from tag %d with err_mask %u\n",cpb_num,
- qc->err_mask);
+ DPRINTK("Completing qc from tag %d\n",cpb_num);
ata_qc_complete(qc);
} else {
struct ata_eh_info *ehi = &ap->eh_info;
@@ -1074,14 +1079,14 @@
}
#endif
-static void nv_adma_setup_port(struct ata_probe_ent *probe_ent, unsigned int port)
+static void nv_adma_setup_port(struct ata_port *ap)
{
- void __iomem *mmio = probe_ent->iomap[NV_MMIO_BAR];
- struct ata_ioports *ioport = &probe_ent->port[port];
+ void __iomem *mmio = ap->host->iomap[NV_MMIO_BAR];
+ struct ata_ioports *ioport = &ap->ioaddr;
VPRINTK("ENTER\n");
- mmio += NV_ADMA_PORT + port * NV_ADMA_PORT_SIZE;
+ mmio += NV_ADMA_PORT + ap->port_no * NV_ADMA_PORT_SIZE;
ioport->cmd_addr = mmio;
ioport->data_addr = mmio + (ATA_REG_DATA * 4);
@@ -1098,9 +1103,9 @@
ioport->ctl_addr = mmio + 0x20;
}
-static int nv_adma_host_init(struct ata_probe_ent *probe_ent)
+static int nv_adma_host_init(struct ata_host *host)
{
- struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
+ struct pci_dev *pdev = to_pci_dev(host->dev);
unsigned int i;
u32 tmp32;
@@ -1115,8 +1120,8 @@
pci_write_config_dword(pdev, NV_MCP_SATA_CFG_20, tmp32);
- for (i = 0; i < probe_ent->n_ports; i++)
- nv_adma_setup_port(probe_ent, i);
+ for (i = 0; i < host->n_ports; i++)
+ nv_adma_setup_port(host->ports[i]);
return 0;
}
@@ -1167,9 +1172,11 @@
struct nv_adma_port_priv *pp = qc->ap->private_data;
/* ADMA engine can only be used for non-ATAPI DMA commands,
- or interrupt-driven no-data commands. */
+ or interrupt-driven no-data commands, where a result taskfile
+ is not required. */
if((pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) ||
- (qc->tf.flags & ATA_TFLAG_POLLING))
+ (qc->tf.flags & ATA_TFLAG_POLLING) ||
+ (qc->flags & ATA_QCFLAG_RESULT_TF))
return 1;
if((qc->flags & ATA_QCFLAG_DMAMAP) ||
@@ -1473,14 +1480,13 @@
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version = 0;
- struct ata_port_info *ppi[2];
- struct ata_probe_ent *probe_ent;
+ const struct ata_port_info *ppi[2];
+ struct ata_host *host;
struct nv_host_priv *hpriv;
int rc;
u32 bar;
void __iomem *base;
unsigned long type = ent->driver_data;
- int mask_set = 0;
// Make sure this is a SATA controller by counting the number of bars
// (NVIDIA SATA controllers will always have six bars). Otherwise,
@@ -1496,50 +1502,38 @@
if (rc)
return rc;
- rc = pci_request_regions(pdev, DRV_NAME);
- if (rc) {
- pcim_pin_device(pdev);
- return rc;
- }
-
- if(type >= CK804 && adma_enabled) {
+ /* determine type and allocate host */
+ if (type >= CK804 && adma_enabled) {
dev_printk(KERN_NOTICE, &pdev->dev, "Using ADMA mode\n");
type = ADMA;
- if(!pci_set_dma_mask(pdev, DMA_64BIT_MASK) &&
- !pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK))
- mask_set = 1;
}
- if(!mask_set) {
- rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
- if (rc)
- return rc;
- rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
- if (rc)
- return rc;
- }
-
- rc = -ENOMEM;
+ ppi[0] = ppi[1] = &nv_port_info[type];
+ rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host);
+ if (rc)
+ return rc;
hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
if (!hpriv)
return -ENOMEM;
-
- ppi[0] = ppi[1] = &nv_port_info[type];
- probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
- if (!probe_ent)
- return -ENOMEM;
-
- if (!pcim_iomap(pdev, NV_MMIO_BAR, 0))
- return -EIO;
- probe_ent->iomap = pcim_iomap_table(pdev);
-
- probe_ent->private_data = hpriv;
hpriv->type = type;
+ host->private_data = hpriv;
- base = probe_ent->iomap[NV_MMIO_BAR];
- probe_ent->port[0].scr_addr = base + NV_PORT0_SCR_REG_OFFSET;
- probe_ent->port[1].scr_addr = base + NV_PORT1_SCR_REG_OFFSET;
+ /* set 64bit dma masks, may fail */
+ if (type == ADMA) {
+ if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0)
+ pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
+ }
+
+ /* request and iomap NV_MMIO_BAR */
+ rc = pcim_iomap_regions(pdev, 1 << NV_MMIO_BAR, DRV_NAME);
+ if (rc)
+ return rc;
+
+ /* configure SCR access */
+ base = host->iomap[NV_MMIO_BAR];
+ host->ports[0]->ioaddr.scr_addr = base + NV_PORT0_SCR_REG_OFFSET;
+ host->ports[1]->ioaddr.scr_addr = base + NV_PORT1_SCR_REG_OFFSET;
/* enable SATA space for CK804 */
if (type >= CK804) {
@@ -1550,20 +1544,16 @@
pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
}
- pci_set_master(pdev);
-
+ /* init ADMA */
if (type == ADMA) {
- rc = nv_adma_host_init(probe_ent);
+ rc = nv_adma_host_init(host);
if (rc)
return rc;
}
- rc = ata_device_add(probe_ent);
- if (rc != NV_PORTS)
- return -ENODEV;
-
- devm_kfree(&pdev->dev, probe_ent);
- return 0;
+ pci_set_master(pdev);
+ return ata_host_activate(host, pdev->irq, ppi[0]->irq_handler,
+ IRQF_SHARED, ppi[0]->sht);
}
static void nv_remove_one (struct pci_dev *pdev)
diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c
index 2339813..f56549b 100644
--- a/drivers/ata/sata_promise.c
+++ b/drivers/ata/sata_promise.c
@@ -45,10 +45,11 @@
#include "sata_promise.h"
#define DRV_NAME "sata_promise"
-#define DRV_VERSION "2.00"
+#define DRV_VERSION "2.05"
enum {
+ PDC_MAX_PORTS = 4,
PDC_MMIO_BAR = 3,
/* register offsets */
@@ -70,14 +71,31 @@
PDC_TBG_MODE = 0x41C, /* TBG mode (not SATAII) */
PDC_SLEW_CTL = 0x470, /* slew rate control reg (not SATAII) */
- PDC_ERR_MASK = (1<<19) | (1<<20) | (1<<21) | (1<<22) |
- (1<<8) | (1<<9) | (1<<10),
+ /* PDC_GLOBAL_CTL bit definitions */
+ PDC_PH_ERR = (1 << 8), /* PCI error while loading packet */
+ PDC_SH_ERR = (1 << 9), /* PCI error while loading S/G table */
+ PDC_DH_ERR = (1 << 10), /* PCI error while loading data */
+ PDC2_HTO_ERR = (1 << 12), /* host bus timeout */
+ PDC2_ATA_HBA_ERR = (1 << 13), /* error during SATA DATA FIS transmission */
+ PDC2_ATA_DMA_CNT_ERR = (1 << 14), /* DMA DATA FIS size differs from S/G count */
+ PDC_OVERRUN_ERR = (1 << 19), /* S/G byte count larger than HD requires */
+ PDC_UNDERRUN_ERR = (1 << 20), /* S/G byte count less than HD requires */
+ PDC_DRIVE_ERR = (1 << 21), /* drive error */
+ PDC_PCI_SYS_ERR = (1 << 22), /* PCI system error */
+ PDC1_PCI_PARITY_ERR = (1 << 23), /* PCI parity error (from SATA150 driver) */
+ PDC1_ERR_MASK = PDC1_PCI_PARITY_ERR,
+ PDC2_ERR_MASK = PDC2_HTO_ERR | PDC2_ATA_HBA_ERR | PDC2_ATA_DMA_CNT_ERR,
+ PDC_ERR_MASK = (PDC_PH_ERR | PDC_SH_ERR | PDC_DH_ERR | PDC_OVERRUN_ERR
+ | PDC_UNDERRUN_ERR | PDC_DRIVE_ERR | PDC_PCI_SYS_ERR
+ | PDC1_ERR_MASK | PDC2_ERR_MASK),
board_2037x = 0, /* FastTrak S150 TX2plus */
- board_20319 = 1, /* FastTrak S150 TX4 */
- board_20619 = 2, /* FastTrak TX4000 */
- board_2057x = 3, /* SATAII150 Tx2plus */
- board_40518 = 4, /* SATAII150 Tx4 */
+ board_2037x_pata = 1, /* FastTrak S150 TX2plus PATA port */
+ board_20319 = 2, /* FastTrak S150 TX4 */
+ board_20619 = 3, /* FastTrak TX4000 */
+ board_2057x = 4, /* SATAII150 Tx2plus */
+ board_2057x_pata = 5, /* SATAII150 Tx2plus */
+ board_40518 = 6, /* SATAII150 Tx4 */
PDC_HAS_PATA = (1 << 1), /* PDC20375/20575 has PATA */
@@ -100,8 +118,10 @@
ATA_FLAG_MMIO |
ATA_FLAG_PIO_POLLING,
- /* hp->flags bits */
- PDC_FLAG_GEN_II = (1 << 0),
+ /* ap->flags bits */
+ PDC_FLAG_GEN_II = (1 << 24),
+ PDC_FLAG_SATA_PATA = (1 << 25), /* supports SATA + PATA */
+ PDC_FLAG_4_PORTS = (1 << 26), /* 4 ports */
};
@@ -110,28 +130,25 @@
dma_addr_t pkt_dma;
};
-struct pdc_host_priv {
- unsigned long flags;
- unsigned long port_flags[ATA_MAX_PORTS];
-};
-
static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
-static irqreturn_t pdc_interrupt (int irq, void *dev_instance);
-static int pdc_port_start(struct ata_port *ap);
+static int pdc_common_port_start(struct ata_port *ap);
+static int pdc_sata_port_start(struct ata_port *ap);
static void pdc_qc_prep(struct ata_queued_cmd *qc);
static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
static int pdc_check_atapi_dma(struct ata_queued_cmd *qc);
-static int pdc_old_check_atapi_dma(struct ata_queued_cmd *qc);
+static int pdc_old_sata_check_atapi_dma(struct ata_queued_cmd *qc);
static void pdc_irq_clear(struct ata_port *ap);
static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc);
static void pdc_freeze(struct ata_port *ap);
static void pdc_thaw(struct ata_port *ap);
-static void pdc_error_handler(struct ata_port *ap);
+static void pdc_pata_error_handler(struct ata_port *ap);
+static void pdc_sata_error_handler(struct ata_port *ap);
static void pdc_post_internal_cmd(struct ata_queued_cmd *qc);
-
+static int pdc_pata_cable_detect(struct ata_port *ap);
+static int pdc_sata_cable_detect(struct ata_port *ap);
static struct scsi_host_template pdc_ata_sht = {
.module = THIS_MODULE,
@@ -164,17 +181,17 @@
.qc_issue = pdc_qc_issue_prot,
.freeze = pdc_freeze,
.thaw = pdc_thaw,
- .error_handler = pdc_error_handler,
+ .error_handler = pdc_sata_error_handler,
.post_internal_cmd = pdc_post_internal_cmd,
+ .cable_detect = pdc_sata_cable_detect,
.data_xfer = ata_data_xfer,
- .irq_handler = pdc_interrupt,
.irq_clear = pdc_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
.scr_read = pdc_sata_scr_read,
.scr_write = pdc_sata_scr_write,
- .port_start = pdc_port_start,
+ .port_start = pdc_sata_port_start,
};
/* First-generation chips need a more restrictive ->check_atapi_dma op */
@@ -185,23 +202,23 @@
.check_status = ata_check_status,
.exec_command = pdc_exec_command_mmio,
.dev_select = ata_std_dev_select,
- .check_atapi_dma = pdc_old_check_atapi_dma,
+ .check_atapi_dma = pdc_old_sata_check_atapi_dma,
.qc_prep = pdc_qc_prep,
.qc_issue = pdc_qc_issue_prot,
.freeze = pdc_freeze,
.thaw = pdc_thaw,
- .error_handler = pdc_error_handler,
+ .error_handler = pdc_sata_error_handler,
.post_internal_cmd = pdc_post_internal_cmd,
+ .cable_detect = pdc_sata_cable_detect,
.data_xfer = ata_data_xfer,
- .irq_handler = pdc_interrupt,
.irq_clear = pdc_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
.scr_read = pdc_sata_scr_read,
.scr_write = pdc_sata_scr_write,
- .port_start = pdc_port_start,
+ .port_start = pdc_sata_port_start,
};
static const struct ata_port_operations pdc_pata_ops = {
@@ -217,32 +234,41 @@
.qc_issue = pdc_qc_issue_prot,
.freeze = pdc_freeze,
.thaw = pdc_thaw,
- .error_handler = pdc_error_handler,
+ .error_handler = pdc_pata_error_handler,
.post_internal_cmd = pdc_post_internal_cmd,
+ .cable_detect = pdc_pata_cable_detect,
.data_xfer = ata_data_xfer,
- .irq_handler = pdc_interrupt,
.irq_clear = pdc_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
- .port_start = pdc_port_start,
+ .port_start = pdc_common_port_start,
};
static const struct ata_port_info pdc_port_info[] = {
/* board_2037x */
{
- .sht = &pdc_ata_sht,
- .flags = PDC_COMMON_FLAGS,
+ .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA |
+ PDC_FLAG_SATA_PATA,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
.port_ops = &pdc_old_sata_ops,
},
+ /* board_2037x_pata */
+ {
+ .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x7f, /* udma0-6 ; FIXME */
+ .port_ops = &pdc_pata_ops,
+ },
+
/* board_20319 */
{
- .sht = &pdc_ata_sht,
- .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA,
+ .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA |
+ PDC_FLAG_4_PORTS,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
@@ -251,8 +277,8 @@
/* board_20619 */
{
- .sht = &pdc_ata_sht,
- .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS,
+ .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS |
+ PDC_FLAG_4_PORTS,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
@@ -261,18 +287,28 @@
/* board_2057x */
{
- .sht = &pdc_ata_sht,
- .flags = PDC_COMMON_FLAGS,
+ .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA |
+ PDC_FLAG_GEN_II | PDC_FLAG_SATA_PATA,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
.port_ops = &pdc_sata_ops,
},
+ /* board_2057x_pata */
+ {
+ .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS,
+ PDC_FLAG_GEN_II,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x7f, /* udma0-6 ; FIXME */
+ .port_ops = &pdc_pata_ops,
+ },
+
/* board_40518 */
{
- .sht = &pdc_ata_sht,
- .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA,
+ .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA |
+ PDC_FLAG_GEN_II | PDC_FLAG_4_PORTS,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
@@ -313,18 +349,12 @@
};
-static int pdc_port_start(struct ata_port *ap)
+static int pdc_common_port_start(struct ata_port *ap)
{
struct device *dev = ap->host->dev;
- struct pdc_host_priv *hp = ap->host->private_data;
struct pdc_port_priv *pp;
int rc;
- /* fix up port flags and cable type for SATA+PATA chips */
- ap->flags |= hp->port_flags[ap->port_no];
- if (ap->flags & ATA_FLAG_SATA)
- ap->cbl = ATA_CBL_SATA;
-
rc = ata_port_start(ap);
if (rc)
return rc;
@@ -339,8 +369,19 @@
ap->private_data = pp;
+ return 0;
+}
+
+static int pdc_sata_port_start(struct ata_port *ap)
+{
+ int rc;
+
+ rc = pdc_common_port_start(ap);
+ if (rc)
+ return rc;
+
/* fix up PHYMODE4 align timing */
- if ((hp->flags & PDC_FLAG_GEN_II) && sata_scr_valid(ap)) {
+ if (ap->flags & PDC_FLAG_GEN_II) {
void __iomem *mmio = (void __iomem *) ap->ioaddr.scr_addr;
unsigned int tmp;
@@ -374,23 +415,25 @@
readl(mmio); /* flush */
}
-static void pdc_pata_cbl_detect(struct ata_port *ap)
+static int pdc_pata_cable_detect(struct ata_port *ap)
{
u8 tmp;
void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr + PDC_CTLSTAT + 0x03;
tmp = readb(mmio);
+ if (tmp & 0x01)
+ return ATA_CBL_PATA40;
+ return ATA_CBL_PATA80;
+}
- if (tmp & 0x01) {
- ap->cbl = ATA_CBL_PATA40;
- ap->udma_mask &= ATA_UDMA_MASK_40C;
- } else
- ap->cbl = ATA_CBL_PATA80;
+static int pdc_sata_cable_detect(struct ata_port *ap)
+{
+ return ATA_CBL_SATA;
}
static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg)
{
- if (sc_reg > SCR_CONTROL || ap->cbl != ATA_CBL_SATA)
+ if (sc_reg > SCR_CONTROL)
return 0xffffffffU;
return readl(ap->ioaddr.scr_addr + (sc_reg * 4));
}
@@ -399,7 +442,7 @@
static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg,
u32 val)
{
- if (sc_reg > SCR_CONTROL || ap->cbl != ATA_CBL_SATA)
+ if (sc_reg > SCR_CONTROL)
return;
writel(val, ap->ioaddr.scr_addr + (sc_reg * 4));
}
@@ -555,52 +598,79 @@
readl(mmio + PDC_CTLSTAT); /* flush */
}
-static int pdc_pre_reset(struct ata_port *ap)
+static void pdc_common_error_handler(struct ata_port *ap, ata_reset_fn_t hardreset)
{
- if (!sata_scr_valid(ap))
- pdc_pata_cbl_detect(ap);
- return ata_std_prereset(ap);
-}
-
-static void pdc_error_handler(struct ata_port *ap)
-{
- ata_reset_fn_t hardreset;
-
if (!(ap->pflags & ATA_PFLAG_FROZEN))
pdc_reset_port(ap);
- hardreset = NULL;
- if (sata_scr_valid(ap))
- hardreset = sata_std_hardreset;
-
/* perform recovery */
- ata_do_eh(ap, pdc_pre_reset, ata_std_softreset, hardreset,
+ ata_do_eh(ap, ata_std_prereset, ata_std_softreset, hardreset,
ata_std_postreset);
}
+static void pdc_pata_error_handler(struct ata_port *ap)
+{
+ pdc_common_error_handler(ap, NULL);
+}
+
+static void pdc_sata_error_handler(struct ata_port *ap)
+{
+ pdc_common_error_handler(ap, sata_std_hardreset);
+}
+
static void pdc_post_internal_cmd(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
- if (qc->flags & ATA_QCFLAG_FAILED)
- qc->err_mask |= AC_ERR_OTHER;
-
/* make DMA engine forget about the failed command */
- if (qc->err_mask)
+ if (qc->flags & ATA_QCFLAG_FAILED)
pdc_reset_port(ap);
}
+static void pdc_error_intr(struct ata_port *ap, struct ata_queued_cmd *qc,
+ u32 port_status, u32 err_mask)
+{
+ struct ata_eh_info *ehi = &ap->eh_info;
+ unsigned int ac_err_mask = 0;
+
+ ata_ehi_clear_desc(ehi);
+ ata_ehi_push_desc(ehi, "port_status 0x%08x", port_status);
+ port_status &= err_mask;
+
+ if (port_status & PDC_DRIVE_ERR)
+ ac_err_mask |= AC_ERR_DEV;
+ if (port_status & (PDC_OVERRUN_ERR | PDC_UNDERRUN_ERR))
+ ac_err_mask |= AC_ERR_HSM;
+ if (port_status & (PDC2_ATA_HBA_ERR | PDC2_ATA_DMA_CNT_ERR))
+ ac_err_mask |= AC_ERR_ATA_BUS;
+ if (port_status & (PDC_PH_ERR | PDC_SH_ERR | PDC_DH_ERR | PDC2_HTO_ERR
+ | PDC_PCI_SYS_ERR | PDC1_PCI_PARITY_ERR))
+ ac_err_mask |= AC_ERR_HOST_BUS;
+
+ if (sata_scr_valid(ap))
+ ehi->serror |= pdc_sata_scr_read(ap, SCR_ERROR);
+
+ qc->err_mask |= ac_err_mask;
+
+ pdc_reset_port(ap);
+}
+
static inline unsigned int pdc_host_intr( struct ata_port *ap,
struct ata_queued_cmd *qc)
{
unsigned int handled = 0;
- u32 tmp;
- void __iomem *mmio = ap->ioaddr.cmd_addr + PDC_GLOBAL_CTL;
+ void __iomem *port_mmio = ap->ioaddr.cmd_addr;
+ u32 port_status, err_mask;
- tmp = readl(mmio);
- if (tmp & PDC_ERR_MASK) {
- qc->err_mask |= AC_ERR_DEV;
- pdc_reset_port(ap);
+ err_mask = PDC_ERR_MASK;
+ if (ap->flags & PDC_FLAG_GEN_II)
+ err_mask &= ~PDC1_ERR_MASK;
+ else
+ err_mask &= ~PDC2_ERR_MASK;
+ port_status = readl(port_mmio + PDC_GLOBAL_CTL);
+ if (unlikely(port_status & err_mask)) {
+ pdc_error_intr(ap, qc, port_status, err_mask);
+ return 1;
}
switch (qc->tf.protocol) {
@@ -767,44 +837,40 @@
return pio;
}
-static int pdc_old_check_atapi_dma(struct ata_queued_cmd *qc)
+static int pdc_old_sata_check_atapi_dma(struct ata_queued_cmd *qc)
{
- struct ata_port *ap = qc->ap;
-
/* First generation chips cannot use ATAPI DMA on SATA ports */
- if (sata_scr_valid(ap))
- return 1;
- return pdc_check_atapi_dma(qc);
+ return 1;
}
-static void pdc_ata_setup_port(struct ata_ioports *port, void __iomem *base,
- void __iomem *scr_addr)
+static void pdc_ata_setup_port(struct ata_port *ap,
+ void __iomem *base, void __iomem *scr_addr)
{
- port->cmd_addr = base;
- port->data_addr = base;
- port->feature_addr =
- port->error_addr = base + 0x4;
- port->nsect_addr = base + 0x8;
- port->lbal_addr = base + 0xc;
- port->lbam_addr = base + 0x10;
- port->lbah_addr = base + 0x14;
- port->device_addr = base + 0x18;
- port->command_addr =
- port->status_addr = base + 0x1c;
- port->altstatus_addr =
- port->ctl_addr = base + 0x38;
- port->scr_addr = scr_addr;
+ ap->ioaddr.cmd_addr = base;
+ ap->ioaddr.data_addr = base;
+ ap->ioaddr.feature_addr =
+ ap->ioaddr.error_addr = base + 0x4;
+ ap->ioaddr.nsect_addr = base + 0x8;
+ ap->ioaddr.lbal_addr = base + 0xc;
+ ap->ioaddr.lbam_addr = base + 0x10;
+ ap->ioaddr.lbah_addr = base + 0x14;
+ ap->ioaddr.device_addr = base + 0x18;
+ ap->ioaddr.command_addr =
+ ap->ioaddr.status_addr = base + 0x1c;
+ ap->ioaddr.altstatus_addr =
+ ap->ioaddr.ctl_addr = base + 0x38;
+ ap->ioaddr.scr_addr = scr_addr;
}
-static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
+static void pdc_host_init(struct ata_host *host)
{
- void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
- struct pdc_host_priv *hp = pe->private_data;
+ void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
+ int is_gen2 = host->ports[0]->flags & PDC_FLAG_GEN_II;
int hotplug_offset;
u32 tmp;
- if (hp->flags & PDC_FLAG_GEN_II)
+ if (is_gen2)
hotplug_offset = PDC2_SATA_PLUG_CSR;
else
hotplug_offset = PDC_SATA_PLUG_CSR;
@@ -818,7 +884,7 @@
/* enable BMR_BURST, maybe change FIFO_SHD to 8 dwords */
tmp = readl(mmio + PDC_FLASH_CTL);
tmp |= 0x02000; /* bit 13 (enable bmr burst) */
- if (!(hp->flags & PDC_FLAG_GEN_II))
+ if (!is_gen2)
tmp |= 0x10000; /* bit 16 (fifo threshold at 8 dw) */
writel(tmp, mmio + PDC_FLASH_CTL);
@@ -831,7 +897,7 @@
writel(tmp | 0xff0000, mmio + hotplug_offset);
/* don't initialise TBG or SLEW on 2nd generation chips */
- if (hp->flags & PDC_FLAG_GEN_II)
+ if (is_gen2)
return;
/* reduce TBG clock to 133 Mhz. */
@@ -853,16 +919,16 @@
static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
- struct ata_probe_ent *probe_ent;
- struct pdc_host_priv *hp;
+ const struct ata_port_info *pi = &pdc_port_info[ent->driver_data];
+ const struct ata_port_info *ppi[PDC_MAX_PORTS];
+ struct ata_host *host;
void __iomem *base;
- unsigned int board_idx = (unsigned int) ent->driver_data;
- int rc;
- u8 tmp;
+ int n_ports, i, rc;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ /* enable and acquire resources */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
@@ -872,6 +938,37 @@
pcim_pin_device(pdev);
if (rc)
return rc;
+ base = pcim_iomap_table(pdev)[PDC_MMIO_BAR];
+
+ /* determine port configuration and setup host */
+ n_ports = 2;
+ if (pi->flags & PDC_FLAG_4_PORTS)
+ n_ports = 4;
+ for (i = 0; i < n_ports; i++)
+ ppi[i] = pi;
+
+ if (pi->flags & PDC_FLAG_SATA_PATA) {
+ u8 tmp = readb(base + PDC_FLASH_CTL+1);
+ if (!(tmp & 0x80)) {
+ ppi[n_ports++] = pi + 1;
+ dev_printk(KERN_INFO, &pdev->dev, "PATA port found\n");
+ }
+ }
+
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
+ if (!host) {
+ dev_printk(KERN_ERR, &pdev->dev, "failed to allocate host\n");
+ return -ENOMEM;
+ }
+ host->iomap = pcim_iomap_table(pdev);
+
+ for (i = 0; i < host->n_ports; i++)
+ pdc_ata_setup_port(host->ports[i],
+ base + 0x200 + i * 0x80,
+ base + 0x400 + i * 0x100);
+
+ /* initialize adapter */
+ pdc_host_init(host);
rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
if (rc)
@@ -880,81 +977,10 @@
if (rc)
return rc;
- probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
- if (probe_ent == NULL)
- return -ENOMEM;
-
- probe_ent->dev = pci_dev_to_dev(pdev);
- INIT_LIST_HEAD(&probe_ent->node);
-
- hp = devm_kzalloc(&pdev->dev, sizeof(*hp), GFP_KERNEL);
- if (hp == NULL)
- return -ENOMEM;
-
- probe_ent->private_data = hp;
-
- probe_ent->sht = pdc_port_info[board_idx].sht;
- probe_ent->port_flags = pdc_port_info[board_idx].flags;
- probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask;
- probe_ent->mwdma_mask = pdc_port_info[board_idx].mwdma_mask;
- probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask;
- probe_ent->port_ops = pdc_port_info[board_idx].port_ops;
-
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->iomap = pcim_iomap_table(pdev);
-
- base = probe_ent->iomap[PDC_MMIO_BAR];
-
- pdc_ata_setup_port(&probe_ent->port[0], base + 0x200, base + 0x400);
- pdc_ata_setup_port(&probe_ent->port[1], base + 0x280, base + 0x500);
-
- /* notice 4-port boards */
- switch (board_idx) {
- case board_40518:
- hp->flags |= PDC_FLAG_GEN_II;
- /* Fall through */
- case board_20319:
- probe_ent->n_ports = 4;
- pdc_ata_setup_port(&probe_ent->port[2], base + 0x300, base + 0x600);
- pdc_ata_setup_port(&probe_ent->port[3], base + 0x380, base + 0x700);
- break;
- case board_2057x:
- hp->flags |= PDC_FLAG_GEN_II;
- /* Fall through */
- case board_2037x:
- /* TX2plus boards also have a PATA port */
- tmp = readb(base + PDC_FLASH_CTL+1);
- if (!(tmp & 0x80)) {
- probe_ent->n_ports = 3;
- pdc_ata_setup_port(&probe_ent->port[2], base + 0x300, NULL);
- hp->port_flags[2] = ATA_FLAG_SLAVE_POSS;
- printk(KERN_INFO DRV_NAME " PATA port found\n");
- } else
- probe_ent->n_ports = 2;
- hp->port_flags[0] = ATA_FLAG_SATA;
- hp->port_flags[1] = ATA_FLAG_SATA;
- break;
- case board_20619:
- probe_ent->n_ports = 4;
- pdc_ata_setup_port(&probe_ent->port[2], base + 0x300, NULL);
- pdc_ata_setup_port(&probe_ent->port[3], base + 0x380, NULL);
- break;
- default:
- BUG();
- break;
- }
-
+ /* start host, request IRQ and attach */
pci_set_master(pdev);
-
- /* initialize adapter */
- pdc_host_init(board_idx, probe_ent);
-
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(&pdev->dev, probe_ent);
- return 0;
+ return ata_host_activate(host, pdev->irq, pdc_interrupt, IRQF_SHARED,
+ &pdc_ata_sht);
}
diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c
index 8786b45..f5a05de 100644
--- a/drivers/ata/sata_qstor.c
+++ b/drivers/ata/sata_qstor.c
@@ -114,7 +114,6 @@
static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
static int qs_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
-static irqreturn_t qs_intr (int irq, void *dev_instance);
static int qs_port_start(struct ata_port *ap);
static void qs_host_stop(struct ata_host *host);
static void qs_phy_reset(struct ata_port *ap);
@@ -158,7 +157,6 @@
.qc_issue = qs_qc_issue,
.data_xfer = ata_data_xfer,
.eng_timeout = qs_eng_timeout,
- .irq_handler = qs_intr,
.irq_clear = qs_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -173,7 +171,6 @@
static const struct ata_port_info qs_port_info[] = {
/* board_2068_idx */
{
- .sht = &qs_ata_sht,
.flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
ATA_FLAG_SATA_RESET |
//FIXME ATA_FLAG_SRST |
@@ -530,16 +527,16 @@
writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */
}
-static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
+static void qs_host_init(struct ata_host *host, unsigned int chip_id)
{
- void __iomem *mmio_base = pe->iomap[QS_MMIO_BAR];
+ void __iomem *mmio_base = host->iomap[QS_MMIO_BAR];
unsigned int port_no;
writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */
writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */
/* reset each channel in turn */
- for (port_no = 0; port_no < pe->n_ports; ++port_no) {
+ for (port_no = 0; port_no < host->n_ports; ++port_no) {
u8 __iomem *chan = mmio_base + (port_no * 0x4000);
writeb(QS_CTR1_RDEV|QS_CTR1_RCHN, chan + QS_CCT_CTR1);
writeb(QS_CTR0_REG, chan + QS_CCT_CTR0);
@@ -547,7 +544,7 @@
}
writeb(QS_SERD3_PHY_ENA, mmio_base + QS_HVS_SERD3); /* enable phy */
- for (port_no = 0; port_no < pe->n_ports; ++port_no) {
+ for (port_no = 0; port_no < host->n_ports; ++port_no) {
u8 __iomem *chan = mmio_base + (port_no * 0x4000);
/* set FIFO depths to same settings as Windows driver */
writew(32, chan + QS_CFC_HUFT);
@@ -607,14 +604,20 @@
const struct pci_device_id *ent)
{
static int printed_version;
- struct ata_probe_ent *probe_ent;
- void __iomem * const *iomap;
unsigned int board_idx = (unsigned int) ent->driver_data;
+ const struct ata_port_info *ppi[] = { &qs_port_info[board_idx], NULL };
+ struct ata_host *host;
int rc, port_no;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ /* alloc host */
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, QS_PORTS);
+ if (!host)
+ return -ENOMEM;
+
+ /* acquire resources and fill host */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
@@ -625,47 +628,24 @@
rc = pcim_iomap_regions(pdev, 1 << QS_MMIO_BAR, DRV_NAME);
if (rc)
return rc;
- iomap = pcim_iomap_table(pdev);
+ host->iomap = pcim_iomap_table(pdev);
- rc = qs_set_dma_masks(pdev, iomap[QS_MMIO_BAR]);
+ rc = qs_set_dma_masks(pdev, host->iomap[QS_MMIO_BAR]);
if (rc)
return rc;
- probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
- if (probe_ent == NULL)
- return -ENOMEM;
-
- probe_ent->dev = pci_dev_to_dev(pdev);
- INIT_LIST_HEAD(&probe_ent->node);
-
- probe_ent->sht = qs_port_info[board_idx].sht;
- probe_ent->port_flags = qs_port_info[board_idx].flags;
- probe_ent->pio_mask = qs_port_info[board_idx].pio_mask;
- probe_ent->mwdma_mask = qs_port_info[board_idx].mwdma_mask;
- probe_ent->udma_mask = qs_port_info[board_idx].udma_mask;
- probe_ent->port_ops = qs_port_info[board_idx].port_ops;
-
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->iomap = iomap;
- probe_ent->n_ports = QS_PORTS;
-
- for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) {
+ for (port_no = 0; port_no < host->n_ports; ++port_no) {
void __iomem *chan =
- probe_ent->iomap[QS_MMIO_BAR] + (port_no * 0x4000);
- qs_ata_setup_port(&probe_ent->port[port_no], chan);
+ host->iomap[QS_MMIO_BAR] + (port_no * 0x4000);
+ qs_ata_setup_port(&host->ports[port_no]->ioaddr, chan);
}
- pci_set_master(pdev);
-
/* initialize adapter */
- qs_host_init(board_idx, probe_ent);
+ qs_host_init(host, board_idx);
- if (ata_device_add(probe_ent) != QS_PORTS)
- return -EIO;
-
- devm_kfree(&pdev->dev, probe_ent);
- return 0;
+ pci_set_master(pdev);
+ return ata_host_activate(host, pdev->irq, qs_intr, IRQF_SHARED,
+ &qs_ata_sht);
}
static int __init qs_ata_init(void)
diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
index 917b7ea..0a1e417 100644
--- a/drivers/ata/sata_sil.c
+++ b/drivers/ata/sata_sil.c
@@ -46,7 +46,7 @@
#include <linux/libata.h>
#define DRV_NAME "sata_sil"
-#define DRV_VERSION "2.1"
+#define DRV_VERSION "2.2"
enum {
SIL_MMIO_BAR = 5,
@@ -114,11 +114,10 @@
#ifdef CONFIG_PM
static int sil_pci_device_resume(struct pci_dev *pdev);
#endif
-static void sil_dev_config(struct ata_port *ap, struct ata_device *dev);
+static void sil_dev_config(struct ata_device *dev);
static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
-static void sil_post_set_mode (struct ata_port *ap);
-static irqreturn_t sil_interrupt(int irq, void *dev_instance);
+static int sil_set_mode (struct ata_port *ap, struct ata_device **r_failed);
static void sil_freeze(struct ata_port *ap);
static void sil_thaw(struct ata_port *ap);
@@ -197,7 +196,7 @@
.check_status = ata_check_status,
.exec_command = ata_exec_command,
.dev_select = ata_std_dev_select,
- .post_set_mode = sil_post_set_mode,
+ .set_mode = sil_set_mode,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
.bmdma_stop = ata_bmdma_stop,
@@ -209,7 +208,6 @@
.thaw = sil_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
- .irq_handler = sil_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -221,7 +219,6 @@
static const struct ata_port_info sil_port_info[] = {
/* sil_3112 */
{
- .sht = &sil_sht,
.flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_MOD15WRITE,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
@@ -230,7 +227,6 @@
},
/* sil_3112_no_sata_irq */
{
- .sht = &sil_sht,
.flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_MOD15WRITE |
SIL_FLAG_NO_SATA_IRQ,
.pio_mask = 0x1f, /* pio0-4 */
@@ -240,7 +236,6 @@
},
/* sil_3512 */
{
- .sht = &sil_sht,
.flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_RERR_ON_DMA_ACT,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
@@ -249,7 +244,6 @@
},
/* sil_3114 */
{
- .sht = &sil_sht,
.flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_RERR_ON_DMA_ACT,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
@@ -297,7 +291,16 @@
return cache_line;
}
-static void sil_post_set_mode (struct ata_port *ap)
+/**
+ * sil_set_mode - wrap set_mode functions
+ * @ap: port to set up
+ * @r_failed: returned device when we fail
+ *
+ * Wrap the libata method for device setup as after the setup we need
+ * to inspect the results and do some configuration work
+ */
+
+static int sil_set_mode (struct ata_port *ap, struct ata_device **r_failed)
{
struct ata_host *host = ap->host;
struct ata_device *dev;
@@ -305,6 +308,11 @@
void __iomem *addr = mmio_base + sil_port[ap->port_no].xfer_mode;
u32 tmp, dev_mode[2];
unsigned int i;
+ int rc;
+
+ rc = ata_do_set_mode(ap, r_failed);
+ if (rc)
+ return rc;
for (i = 0; i < 2; i++) {
dev = &ap->device[i];
@@ -323,6 +331,7 @@
tmp |= (dev_mode[1] << 4);
writel(tmp, addr);
readl(addr); /* flush */
+ return 0;
}
static inline void __iomem *sil_scr_addr(struct ata_port *ap, unsigned int sc_reg)
@@ -521,7 +530,6 @@
/**
* sil_dev_config - Apply device/host-specific errata fixups
- * @ap: Port containing device to be examined
* @dev: Device to be examined
*
* After the IDENTIFY [PACKET] DEVICE step is complete, and a
@@ -548,8 +556,9 @@
* appreciated.
* - But then again UDMA5 is hardly anything to complain about
*/
-static void sil_dev_config(struct ata_port *ap, struct ata_device *dev)
+static void sil_dev_config(struct ata_device *dev)
{
+ struct ata_port *ap = dev->ap;
int print_info = ap->eh_context.i.flags & ATA_EHI_PRINTINFO;
unsigned int n, quirks = 0;
unsigned char model_num[ATA_ID_PROD_LEN + 1];
@@ -583,10 +592,10 @@
}
}
-static void sil_init_controller(struct pci_dev *pdev,
- int n_ports, unsigned long port_flags,
- void __iomem *mmio_base)
+static void sil_init_controller(struct ata_host *host)
{
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ void __iomem *mmio_base = host->iomap[SIL_MMIO_BAR];
u8 cls;
u32 tmp;
int i;
@@ -596,7 +605,7 @@
if (cls) {
cls >>= 3;
cls++; /* cls = (line_size/8)+1 */
- for (i = 0; i < n_ports; i++)
+ for (i = 0; i < host->n_ports; i++)
writew(cls << 8 | cls,
mmio_base + sil_port[i].fifo_cfg);
} else
@@ -604,10 +613,10 @@
"cache line size not set. Driver may not function\n");
/* Apply R_ERR on DMA activate FIS errata workaround */
- if (port_flags & SIL_FLAG_RERR_ON_DMA_ACT) {
+ if (host->ports[0]->flags & SIL_FLAG_RERR_ON_DMA_ACT) {
int cnt;
- for (i = 0, cnt = 0; i < n_ports; i++) {
+ for (i = 0, cnt = 0; i < host->n_ports; i++) {
tmp = readl(mmio_base + sil_port[i].sfis_cfg);
if ((tmp & 0x3) != 0x01)
continue;
@@ -620,7 +629,7 @@
}
}
- if (n_ports == 4) {
+ if (host->n_ports == 4) {
/* flip the magic "make 4 ports work" bit */
tmp = readl(mmio_base + sil_port[2].bmdma);
if ((tmp & SIL_INTR_STEERING) == 0)
@@ -632,15 +641,26 @@
static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
- struct device *dev = &pdev->dev;
- struct ata_probe_ent *probe_ent;
+ int board_id = ent->driver_data;
+ const struct ata_port_info *ppi[] = { &sil_port_info[board_id], NULL };
+ struct ata_host *host;
void __iomem *mmio_base;
- int rc;
+ int n_ports, rc;
unsigned int i;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ /* allocate host */
+ n_ports = 2;
+ if (board_id == sil_3114)
+ n_ports = 4;
+
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
+ if (!host)
+ return -ENOMEM;
+
+ /* acquire resources and fill host */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
@@ -650,6 +670,7 @@
pcim_pin_device(pdev);
if (rc)
return rc;
+ host->iomap = pcim_iomap_table(pdev);
rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
if (rc)
@@ -658,45 +679,25 @@
if (rc)
return rc;
- probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
- if (probe_ent == NULL)
- return -ENOMEM;
+ mmio_base = host->iomap[SIL_MMIO_BAR];
- INIT_LIST_HEAD(&probe_ent->node);
- probe_ent->dev = pci_dev_to_dev(pdev);
- probe_ent->port_ops = sil_port_info[ent->driver_data].port_ops;
- probe_ent->sht = sil_port_info[ent->driver_data].sht;
- probe_ent->n_ports = (ent->driver_data == sil_3114) ? 4 : 2;
- probe_ent->pio_mask = sil_port_info[ent->driver_data].pio_mask;
- probe_ent->mwdma_mask = sil_port_info[ent->driver_data].mwdma_mask;
- probe_ent->udma_mask = sil_port_info[ent->driver_data].udma_mask;
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->port_flags = sil_port_info[ent->driver_data].flags;
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_ioports *ioaddr = &host->ports[i]->ioaddr;
- probe_ent->iomap = pcim_iomap_table(pdev);
-
- mmio_base = probe_ent->iomap[SIL_MMIO_BAR];
-
- for (i = 0; i < probe_ent->n_ports; i++) {
- probe_ent->port[i].cmd_addr = mmio_base + sil_port[i].tf;
- probe_ent->port[i].altstatus_addr =
- probe_ent->port[i].ctl_addr = mmio_base + sil_port[i].ctl;
- probe_ent->port[i].bmdma_addr = mmio_base + sil_port[i].bmdma;
- probe_ent->port[i].scr_addr = mmio_base + sil_port[i].scr;
- ata_std_ports(&probe_ent->port[i]);
+ ioaddr->cmd_addr = mmio_base + sil_port[i].tf;
+ ioaddr->altstatus_addr =
+ ioaddr->ctl_addr = mmio_base + sil_port[i].ctl;
+ ioaddr->bmdma_addr = mmio_base + sil_port[i].bmdma;
+ ioaddr->scr_addr = mmio_base + sil_port[i].scr;
+ ata_std_ports(ioaddr);
}
- sil_init_controller(pdev, probe_ent->n_ports, probe_ent->port_flags,
- mmio_base);
+ /* initialize and activate */
+ sil_init_controller(host);
pci_set_master(pdev);
-
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(dev, probe_ent);
- return 0;
+ return ata_host_activate(host, pdev->irq, sil_interrupt, IRQF_SHARED,
+ &sil_sht);
}
#ifdef CONFIG_PM
@@ -709,8 +710,7 @@
if (rc)
return rc;
- sil_init_controller(pdev, host->n_ports, host->ports[0]->flags,
- host->iomap[SIL_MMIO_BAR]);
+ sil_init_controller(host);
ata_host_resume(host);
return 0;
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index 5614df8..e6223ba 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -323,7 +323,7 @@
struct ata_taskfile tf; /* Cached taskfile registers */
};
-static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev);
+static void sil24_dev_config(struct ata_device *dev);
static u8 sil24_check_status(struct ata_port *ap);
static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg);
static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val);
@@ -331,7 +331,6 @@
static void sil24_qc_prep(struct ata_queued_cmd *qc);
static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc);
static void sil24_irq_clear(struct ata_port *ap);
-static irqreturn_t sil24_interrupt(int irq, void *dev_instance);
static void sil24_freeze(struct ata_port *ap);
static void sil24_thaw(struct ata_port *ap);
static void sil24_error_handler(struct ata_port *ap);
@@ -401,7 +400,6 @@
.qc_prep = sil24_qc_prep,
.qc_issue = sil24_qc_issue,
- .irq_handler = sil24_interrupt,
.irq_clear = sil24_irq_clear,
.irq_on = ata_dummy_irq_on,
.irq_ack = ata_dummy_irq_ack,
@@ -424,10 +422,9 @@
#define SIL24_NPORTS2FLAG(nports) ((((unsigned)(nports) - 1) & 0x3) << 30)
#define SIL24_FLAG2NPORTS(flag) ((((flag) >> 30) & 0x3) + 1)
-static struct ata_port_info sil24_port_info[] = {
+static const struct ata_port_info sil24_port_info[] = {
/* sil_3124 */
{
- .sht = &sil24_sht,
.flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(4) |
SIL24_FLAG_PCIX_IRQ_WOC,
.pio_mask = 0x1f, /* pio0-4 */
@@ -437,7 +434,6 @@
},
/* sil_3132 */
{
- .sht = &sil24_sht,
.flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(2),
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
@@ -446,7 +442,6 @@
},
/* sil_3131/sil_3531 */
{
- .sht = &sil24_sht,
.flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(1),
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
@@ -462,9 +457,9 @@
return tag;
}
-static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev)
+static void sil24_dev_config(struct ata_device *dev)
{
- void __iomem *port = ap->ioaddr.cmd_addr;
+ void __iomem *port = dev->ap->ioaddr.cmd_addr;
if (dev->cdb_len == 16)
writel(PORT_CS_CDB16, port + PORT_CTRL_STAT);
@@ -924,11 +919,8 @@
{
struct ata_port *ap = qc->ap;
- if (qc->flags & ATA_QCFLAG_FAILED)
- qc->err_mask |= AC_ERR_OTHER;
-
/* make DMA engine forget about the failed command */
- if (qc->err_mask)
+ if (qc->flags & ATA_QCFLAG_FAILED)
sil24_init_port(ap);
}
@@ -964,11 +956,10 @@
return 0;
}
-static void sil24_init_controller(struct pci_dev *pdev, int n_ports,
- unsigned long port_flags,
- void __iomem *host_base,
- void __iomem *port_base)
+static void sil24_init_controller(struct ata_host *host)
{
+ void __iomem *host_base = host->iomap[SIL24_HOST_BAR];
+ void __iomem *port_base = host->iomap[SIL24_PORT_BAR];
u32 tmp;
int i;
@@ -979,7 +970,7 @@
writel(0, host_base + HOST_CTRL);
/* init ports */
- for (i = 0; i < n_ports; i++) {
+ for (i = 0; i < host->n_ports; i++) {
void __iomem *port = port_base + i * PORT_REGS_SIZE;
/* Initial PHY setting */
@@ -993,12 +984,12 @@
PORT_CS_PORT_RST,
PORT_CS_PORT_RST, 10, 100);
if (tmp & PORT_CS_PORT_RST)
- dev_printk(KERN_ERR, &pdev->dev,
+ dev_printk(KERN_ERR, host->dev,
"failed to clear port RST\n");
}
/* Configure IRQ WoC */
- if (port_flags & SIL24_FLAG_PCIX_IRQ_WOC)
+ if (host->ports[0]->flags & SIL24_FLAG_PCIX_IRQ_WOC)
writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT);
else
writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
@@ -1026,18 +1017,17 @@
static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version = 0;
- struct device *dev = &pdev->dev;
- unsigned int board_id = (unsigned int)ent->driver_data;
- struct ata_port_info *pinfo = &sil24_port_info[board_id];
- struct ata_probe_ent *probe_ent;
- void __iomem *host_base;
- void __iomem *port_base;
+ struct ata_port_info pi = sil24_port_info[ent->driver_data];
+ const struct ata_port_info *ppi[] = { &pi, NULL };
+ void __iomem * const *iomap;
+ struct ata_host *host;
int i, rc;
u32 tmp;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ /* acquire resources */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
@@ -1047,33 +1037,36 @@
DRV_NAME);
if (rc)
return rc;
+ iomap = pcim_iomap_table(pdev);
- /* allocate & init probe_ent */
- probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
- if (!probe_ent)
+ /* apply workaround for completion IRQ loss on PCI-X errata */
+ if (pi.flags & SIL24_FLAG_PCIX_IRQ_WOC) {
+ tmp = readl(iomap[SIL24_HOST_BAR] + HOST_CTRL);
+ if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL))
+ dev_printk(KERN_INFO, &pdev->dev,
+ "Applying completion IRQ loss on PCI-X "
+ "errata fix\n");
+ else
+ pi.flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;
+ }
+
+ /* allocate and fill host */
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi,
+ SIL24_FLAG2NPORTS(ppi[0]->flags));
+ if (!host)
return -ENOMEM;
+ host->iomap = iomap;
- probe_ent->dev = pci_dev_to_dev(pdev);
- INIT_LIST_HEAD(&probe_ent->node);
+ for (i = 0; i < host->n_ports; i++) {
+ void __iomem *port = iomap[SIL24_PORT_BAR] + i * PORT_REGS_SIZE;
- probe_ent->sht = pinfo->sht;
- probe_ent->port_flags = pinfo->flags;
- probe_ent->pio_mask = pinfo->pio_mask;
- probe_ent->mwdma_mask = pinfo->mwdma_mask;
- probe_ent->udma_mask = pinfo->udma_mask;
- probe_ent->port_ops = pinfo->port_ops;
- probe_ent->n_ports = SIL24_FLAG2NPORTS(pinfo->flags);
+ host->ports[i]->ioaddr.cmd_addr = port;
+ host->ports[i]->ioaddr.scr_addr = port + PORT_SCONTROL;
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->iomap = pcim_iomap_table(pdev);
+ ata_std_ports(&host->ports[i]->ioaddr);
+ }
- host_base = probe_ent->iomap[SIL24_HOST_BAR];
- port_base = probe_ent->iomap[SIL24_PORT_BAR];
-
- /*
- * Configure the device
- */
+ /* configure and activate the device */
if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
if (rc) {
@@ -1099,36 +1092,11 @@
}
}
- /* Apply workaround for completion IRQ loss on PCI-X errata */
- if (probe_ent->port_flags & SIL24_FLAG_PCIX_IRQ_WOC) {
- tmp = readl(host_base + HOST_CTRL);
- if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL))
- dev_printk(KERN_INFO, &pdev->dev,
- "Applying completion IRQ loss on PCI-X "
- "errata fix\n");
- else
- probe_ent->port_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;
- }
-
- for (i = 0; i < probe_ent->n_ports; i++) {
- void __iomem *port = port_base + i * PORT_REGS_SIZE;
-
- probe_ent->port[i].cmd_addr = port;
- probe_ent->port[i].scr_addr = port + PORT_SCONTROL;
-
- ata_std_ports(&probe_ent->port[i]);
- }
-
- sil24_init_controller(pdev, probe_ent->n_ports, probe_ent->port_flags,
- host_base, port_base);
+ sil24_init_controller(host);
pci_set_master(pdev);
-
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(dev, probe_ent);
- return 0;
+ return ata_host_activate(host, pdev->irq, sil24_interrupt, IRQF_SHARED,
+ &sil24_sht);
}
#ifdef CONFIG_PM
@@ -1136,7 +1104,6 @@
{
struct ata_host *host = dev_get_drvdata(&pdev->dev);
void __iomem *host_base = host->iomap[SIL24_HOST_BAR];
- void __iomem *port_base = host->iomap[SIL24_PORT_BAR];
int rc;
rc = ata_pci_device_do_resume(pdev);
@@ -1146,8 +1113,7 @@
if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND)
writel(HOST_CTRL_GLOBAL_RST, host_base + HOST_CTRL);
- sil24_init_controller(pdev, host->n_ports, host->ports[0]->flags,
- host_base, port_base);
+ sil24_init_controller(host);
ata_host_resume(host);
diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c
index a787f0d..d8ee062 100644
--- a/drivers/ata/sata_sis.c
+++ b/drivers/ata/sata_sis.c
@@ -121,7 +121,6 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -131,7 +130,6 @@
};
static struct ata_port_info sis_port_info = {
- .sht = &sis_sht,
.flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
.pio_mask = 0x1f,
.mwdma_mask = 0x7,
@@ -256,12 +254,13 @@
static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
- struct ata_probe_ent *probe_ent = NULL;
- int rc;
+ struct ata_port_info pi = sis_port_info;
+ const struct ata_port_info *ppi[2] = { &pi, &pi };
+ struct ata_host *host;
u32 genctl, val;
- struct ata_port_info pi = sis_port_info, *ppi[2] = { &pi, &pi };
u8 pmr;
u8 port2_start = 0x20;
+ int rc;
if (!printed_version++)
dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
@@ -270,19 +269,6 @@
if (rc)
return rc;
- rc = pci_request_regions(pdev, DRV_NAME);
- if (rc) {
- pcim_pin_device(pdev);
- return rc;
- }
-
- rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
- if (rc)
- return rc;
- rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
- if (rc)
- return rc;
-
/* check and see if the SCRs are in IO space or PCI cfg space */
pci_read_config_dword(pdev, SIS_GENCTL, &genctl);
if ((genctl & GENCTL_IOMAPPED_SCR) == 0)
@@ -349,30 +335,26 @@
break;
}
- probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
- if (!probe_ent)
- return -ENOMEM;
+ rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host);
+ if (rc)
+ return rc;
- if (!(probe_ent->port_flags & SIS_FLAG_CFGSCR)) {
+ if (!(pi.flags & SIS_FLAG_CFGSCR)) {
void __iomem *mmio;
- mmio = pcim_iomap(pdev, SIS_SCR_PCI_BAR, 0);
- if (!mmio)
- return -ENOMEM;
+ rc = pcim_iomap_regions(pdev, 1 << SIS_SCR_PCI_BAR, DRV_NAME);
+ if (rc)
+ return rc;
+ mmio = host->iomap[SIS_SCR_PCI_BAR];
- probe_ent->port[0].scr_addr = mmio;
- probe_ent->port[1].scr_addr = mmio + port2_start;
+ host->ports[0]->ioaddr.scr_addr = mmio;
+ host->ports[1]->ioaddr.scr_addr = mmio + port2_start;
}
pci_set_master(pdev);
pci_intx(pdev, 1);
-
- if (!ata_device_add(probe_ent))
- return -EIO;
-
- devm_kfree(&pdev->dev, probe_ent);
- return 0;
-
+ return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+ &sis_sht);
}
static int __init sis_init(void)
diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c
index b121195..cc07aac 100644
--- a/drivers/ata/sata_svw.c
+++ b/drivers/ata/sata_svw.c
@@ -56,7 +56,9 @@
#define DRV_VERSION "2.1"
enum {
- K2_FLAG_NO_ATAPI_DMA = (1 << 29),
+ /* ap->flags bits */
+ K2_FLAG_SATA_8_PORTS = (1 << 24),
+ K2_FLAG_NO_ATAPI_DMA = (1 << 25),
/* Taskfile registers offsets */
K2_SATA_TF_CMD_OFFSET = 0x00,
@@ -90,17 +92,6 @@
board_svw8 = 1,
};
-static const struct k2_board_info {
- unsigned int n_ports;
- unsigned long port_flags;
-} k2_board_info[] = {
- /* board_svw4 */
- { 4, K2_FLAG_NO_ATAPI_DMA },
-
- /* board_svw8 */
- { 8, K2_FLAG_NO_ATAPI_DMA },
-};
-
static u8 k2_stat_check_status(struct ata_port *ap);
@@ -354,7 +345,6 @@
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -363,6 +353,28 @@
.port_start = ata_port_start,
};
+static const struct ata_port_info k2_port_info[] = {
+ /* board_svw4 */
+ {
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_MMIO | K2_FLAG_NO_ATAPI_DMA,
+ .pio_mask = 0x1f,
+ .mwdma_mask = 0x07,
+ .udma_mask = 0x7f,
+ .port_ops = &k2_sata_ops,
+ },
+ /* board_svw8 */
+ {
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_MMIO | K2_FLAG_NO_ATAPI_DMA |
+ K2_FLAG_SATA_8_PORTS,
+ .pio_mask = 0x1f,
+ .mwdma_mask = 0x07,
+ .udma_mask = 0x7f,
+ .port_ops = &k2_sata_ops,
+ },
+};
+
static void k2_sata_setup_port(struct ata_ioports *port, void __iomem *base)
{
port->cmd_addr = base + K2_SATA_TF_CMD_OFFSET;
@@ -386,17 +398,24 @@
static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
- struct device *dev = &pdev->dev;
- struct ata_probe_ent *probe_ent;
+ const struct ata_port_info *ppi[] =
+ { &k2_port_info[ent->driver_data], NULL };
+ struct ata_host *host;
void __iomem *mmio_base;
- const struct k2_board_info *board_info =
- &k2_board_info[ent->driver_data];
- int rc;
- int i;
+ int n_ports, i, rc;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ /* allocate host */
+ n_ports = 4;
+ if (ppi[0]->flags & K2_FLAG_SATA_8_PORTS)
+ n_ports = 8;
+
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
+ if (!host)
+ return -ENOMEM;
+
/*
* If this driver happens to only be useful on Apple's K2, then
* we should check that here as it has a normal Serverworks ID
@@ -404,6 +423,7 @@
rc = pcim_enable_device(pdev);
if (rc)
return rc;
+
/*
* Check if we have resources mapped at all (second function may
* have been disabled by firmware)
@@ -417,6 +437,15 @@
pcim_pin_device(pdev);
if (rc)
return rc;
+ host->iomap = pcim_iomap_table(pdev);
+ mmio_base = host->iomap[5];
+
+ /* different controllers have different number of ports - currently 4 or 8 */
+ /* All ports are on the same function. Multi-function device is no
+ * longer available. This should not be seen in any system. */
+ for (i = 0; i < host->n_ports; i++)
+ k2_sata_setup_port(&host->ports[i]->ioaddr,
+ mmio_base + i * K2_SATA_PORT_OFFSET);
rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
if (rc)
@@ -425,38 +454,6 @@
if (rc)
return rc;
- probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL);
- if (probe_ent == NULL)
- return -ENOMEM;
-
- probe_ent->dev = pci_dev_to_dev(pdev);
- INIT_LIST_HEAD(&probe_ent->node);
-
- probe_ent->sht = &k2_sata_sht;
- probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO | board_info->port_flags;
- probe_ent->port_ops = &k2_sata_ops;
- probe_ent->n_ports = 4;
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->iomap = pcim_iomap_table(pdev);
-
- /* We don't care much about the PIO/UDMA masks, but the core won't like us
- * if we don't fill these
- */
- probe_ent->pio_mask = 0x1f;
- probe_ent->mwdma_mask = 0x7;
- probe_ent->udma_mask = 0x7f;
-
- mmio_base = probe_ent->iomap[5];
-
- /* different controllers have different number of ports - currently 4 or 8 */
- /* All ports are on the same function. Multi-function device is no
- * longer available. This should not be seen in any system. */
- for (i = 0; i < board_info->n_ports; i++)
- k2_sata_setup_port(&probe_ent->port[i],
- mmio_base + i * K2_SATA_PORT_OFFSET);
-
/* Clear a magic bit in SCR1 according to Darwin, those help
* some funky seagate drives (though so far, those were already
* set by the firmware on the machines I had access to)
@@ -469,12 +466,8 @@
writel(0x0, mmio_base + K2_SATA_SIM_OFFSET);
pci_set_master(pdev);
-
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(dev, probe_ent);
- return 0;
+ return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+ &k2_sata_sht);
}
/* 0x240 is device ID for Apple K2 device
diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c
index 1a081c3..3a4f445 100644
--- a/drivers/ata/sata_sx4.c
+++ b/drivers/ata/sata_sx4.c
@@ -151,24 +151,23 @@
static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
-static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance);
static void pdc_eng_timeout(struct ata_port *ap);
static void pdc_20621_phy_reset (struct ata_port *ap);
static int pdc_port_start(struct ata_port *ap);
static void pdc20621_qc_prep(struct ata_queued_cmd *qc);
static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
-static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe);
-static int pdc20621_detect_dimm(struct ata_probe_ent *pe);
-static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe,
+static unsigned int pdc20621_dimm_init(struct ata_host *host);
+static int pdc20621_detect_dimm(struct ata_host *host);
+static unsigned int pdc20621_i2c_read(struct ata_host *host,
u32 device, u32 subaddr, u32 *pdata);
-static int pdc20621_prog_dimm0(struct ata_probe_ent *pe);
-static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe);
+static int pdc20621_prog_dimm0(struct ata_host *host);
+static unsigned int pdc20621_prog_dimm_global(struct ata_host *host);
#ifdef ATA_VERBOSE_DEBUG
-static void pdc20621_get_from_dimm(struct ata_probe_ent *pe,
+static void pdc20621_get_from_dimm(struct ata_host *host,
void *psource, u32 offset, u32 size);
#endif
-static void pdc20621_put_to_dimm(struct ata_probe_ent *pe,
+static void pdc20621_put_to_dimm(struct ata_host *host,
void *psource, u32 offset, u32 size);
static void pdc20621_irq_clear(struct ata_port *ap);
static unsigned int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc);
@@ -204,7 +203,6 @@
.qc_issue = pdc20621_qc_issue_prot,
.data_xfer = ata_data_xfer,
.eng_timeout = pdc_eng_timeout,
- .irq_handler = pdc20621_interrupt,
.irq_clear = pdc20621_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -214,7 +212,6 @@
static const struct ata_port_info pdc_port_info[] = {
/* board_20621 */
{
- .sht = &pdc_sata_sht,
.flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
ATA_FLAG_SRST | ATA_FLAG_MMIO |
ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING,
@@ -882,15 +879,15 @@
#ifdef ATA_VERBOSE_DEBUG
-static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource,
+static void pdc20621_get_from_dimm(struct ata_host *host, void *psource,
u32 offset, u32 size)
{
u32 window_size;
u16 idx;
u8 page_mask;
long dist;
- void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
- void __iomem *dimm_mmio = pe->iomap[PDC_DIMM_BAR];
+ void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
+ void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR];
/* hard-code chip #0 */
mmio += PDC_CHIP0_OFS;
@@ -937,15 +934,15 @@
#endif
-static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource,
+static void pdc20621_put_to_dimm(struct ata_host *host, void *psource,
u32 offset, u32 size)
{
u32 window_size;
u16 idx;
u8 page_mask;
long dist;
- void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
- void __iomem *dimm_mmio = pe->iomap[PDC_DIMM_BAR];
+ void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
+ void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR];
/* hard-code chip #0 */
mmio += PDC_CHIP0_OFS;
@@ -987,10 +984,10 @@
}
-static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device,
+static unsigned int pdc20621_i2c_read(struct ata_host *host, u32 device,
u32 subaddr, u32 *pdata)
{
- void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
+ void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
u32 i2creg = 0;
u32 status;
u32 count =0;
@@ -1023,17 +1020,17 @@
}
-static int pdc20621_detect_dimm(struct ata_probe_ent *pe)
+static int pdc20621_detect_dimm(struct ata_host *host)
{
u32 data=0 ;
- if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+ if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
PDC_DIMM_SPD_SYSTEM_FREQ, &data)) {
if (data == 100)
return 100;
} else
return 0;
- if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) {
+ if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) {
if(data <= 0x75)
return 133;
} else
@@ -1043,13 +1040,13 @@
}
-static int pdc20621_prog_dimm0(struct ata_probe_ent *pe)
+static int pdc20621_prog_dimm0(struct ata_host *host)
{
u32 spd0[50];
u32 data = 0;
int size, i;
u8 bdimmsize;
- void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
+ void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
static const struct {
unsigned int reg;
unsigned int ofs;
@@ -1072,7 +1069,7 @@
mmio += PDC_CHIP0_OFS;
for(i=0; i<ARRAY_SIZE(pdc_i2c_read_data); i++)
- pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+ pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
pdc_i2c_read_data[i].reg,
&spd0[pdc_i2c_read_data[i].ofs]);
@@ -1108,11 +1105,11 @@
}
-static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe)
+static unsigned int pdc20621_prog_dimm_global(struct ata_host *host)
{
u32 data, spd0;
int error, i;
- void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
+ void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
/* hard-code chip #0 */
mmio += PDC_CHIP0_OFS;
@@ -1129,7 +1126,7 @@
readl(mmio + PDC_SDRAM_CONTROL_OFFSET);
/* Turn on for ECC */
- pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+ pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
PDC_DIMM_SPD_TYPE, &spd0);
if (spd0 == 0x02) {
data |= (0x01 << 16);
@@ -1156,7 +1153,7 @@
}
-static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe)
+static unsigned int pdc20621_dimm_init(struct ata_host *host)
{
int speed, size, length;
u32 addr,spd0,pci_status;
@@ -1166,7 +1163,7 @@
u32 ticks=0;
u32 clock=0;
u32 fparam=0;
- void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
+ void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
/* hard-code chip #0 */
mmio += PDC_CHIP0_OFS;
@@ -1225,18 +1222,18 @@
Read SPD of DIMM by I2C interface,
and program the DIMM Module Controller.
*/
- if (!(speed = pdc20621_detect_dimm(pe))) {
+ if (!(speed = pdc20621_detect_dimm(host))) {
printk(KERN_ERR "Detect Local DIMM Fail\n");
return 1; /* DIMM error */
}
VPRINTK("Local DIMM Speed = %d\n", speed);
/* Programming DIMM0 Module Control Register (index_CID0:80h) */
- size = pdc20621_prog_dimm0(pe);
+ size = pdc20621_prog_dimm0(host);
VPRINTK("Local DIMM Size = %dMB\n",size);
/* Programming DIMM Module Global Control Register (index_CID0:88h) */
- if (pdc20621_prog_dimm_global(pe)) {
+ if (pdc20621_prog_dimm_global(host)) {
printk(KERN_ERR "Programming DIMM Module Global Control Register Fail\n");
return 1;
}
@@ -1249,20 +1246,20 @@
'9','8','0','3','1','6','1','2',0,0};
u8 test_parttern2[40] = {0};
- pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x10040, 40);
- pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x40, 40);
+ pdc20621_put_to_dimm(host, (void *) test_parttern2, 0x10040, 40);
+ pdc20621_put_to_dimm(host, (void *) test_parttern2, 0x40, 40);
- pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x10040, 40);
- pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40);
+ pdc20621_put_to_dimm(host, (void *) test_parttern1, 0x10040, 40);
+ pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x40, 40);
printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
test_parttern2[1], &(test_parttern2[2]));
- pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x10040,
+ pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x10040,
40);
printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
test_parttern2[1], &(test_parttern2[2]));
- pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x40, 40);
- pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40);
+ pdc20621_put_to_dimm(host, (void *) test_parttern1, 0x40, 40);
+ pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x40, 40);
printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
test_parttern2[1], &(test_parttern2[2]));
}
@@ -1270,14 +1267,14 @@
/* ECC initiliazation. */
- pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+ pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
PDC_DIMM_SPD_TYPE, &spd0);
if (spd0 == 0x02) {
VPRINTK("Start ECC initialization\n");
addr = 0;
length = size * 1024 * 1024;
while (addr < length) {
- pdc20621_put_to_dimm(pe, (void *) &tmp, addr,
+ pdc20621_put_to_dimm(host, (void *) &tmp, addr,
sizeof(u32));
addr += sizeof(u32);
}
@@ -1287,10 +1284,10 @@
}
-static void pdc_20621_init(struct ata_probe_ent *pe)
+static void pdc_20621_init(struct ata_host *host)
{
u32 tmp;
- void __iomem *mmio = pe->iomap[PDC_MMIO_BAR];
+ void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
/* hard-code chip #0 */
mmio += PDC_CHIP0_OFS;
@@ -1321,15 +1318,25 @@
static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
- struct ata_probe_ent *probe_ent;
+ const struct ata_port_info *ppi[] =
+ { &pdc_port_info[ent->driver_data], NULL };
+ struct ata_host *host;
void __iomem *base;
struct pdc_host_priv *hpriv;
- unsigned int board_idx = (unsigned int) ent->driver_data;
int rc;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ /* allocate host */
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4);
+ hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
+ if (!host || !hpriv)
+ return -ENOMEM;
+
+ host->private_data = hpriv;
+
+ /* acquire resources and fill host */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
@@ -1340,7 +1347,15 @@
pcim_pin_device(pdev);
if (rc)
return rc;
+ host->iomap = pcim_iomap_table(pdev);
+ base = host->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS;
+ pdc_sata_setup_port(&host->ports[0]->ioaddr, base + 0x200);
+ pdc_sata_setup_port(&host->ports[1]->ioaddr, base + 0x280);
+ pdc_sata_setup_port(&host->ports[2]->ioaddr, base + 0x300);
+ pdc_sata_setup_port(&host->ports[3]->ioaddr, base + 0x380);
+
+ /* configure and activate */
rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
if (rc)
return rc;
@@ -1348,50 +1363,13 @@
if (rc)
return rc;
- probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
- if (probe_ent == NULL)
+ if (pdc20621_dimm_init(host))
return -ENOMEM;
-
- probe_ent->dev = pci_dev_to_dev(pdev);
- INIT_LIST_HEAD(&probe_ent->node);
-
- hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
- if (!hpriv)
- return -ENOMEM;
-
- probe_ent->sht = pdc_port_info[board_idx].sht;
- probe_ent->port_flags = pdc_port_info[board_idx].flags;
- probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask;
- probe_ent->mwdma_mask = pdc_port_info[board_idx].mwdma_mask;
- probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask;
- probe_ent->port_ops = pdc_port_info[board_idx].port_ops;
-
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->iomap = pcim_iomap_table(pdev);
-
- probe_ent->private_data = hpriv;
- base = probe_ent->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS;
-
- probe_ent->n_ports = 4;
- pdc_sata_setup_port(&probe_ent->port[0], base + 0x200);
- pdc_sata_setup_port(&probe_ent->port[1], base + 0x280);
- pdc_sata_setup_port(&probe_ent->port[2], base + 0x300);
- pdc_sata_setup_port(&probe_ent->port[3], base + 0x380);
+ pdc_20621_init(host);
pci_set_master(pdev);
-
- /* initialize adapter */
- /* initialize local dimm */
- if (pdc20621_dimm_init(probe_ent))
- return -ENOMEM;
- pdc_20621_init(probe_ent);
-
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(&pdev->dev, probe_ent);
- return 0;
+ return ata_host_activate(host, pdev->irq, pdc20621_interrupt,
+ IRQF_SHARED, &pdc_sata_sht);
}
diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c
index d659ace..f74e383 100644
--- a/drivers/ata/sata_uli.c
+++ b/drivers/ata/sata_uli.c
@@ -115,7 +115,6 @@
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -127,7 +126,6 @@
};
static struct ata_port_info uli_port_info = {
- .sht = &uli_sht,
.flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
ATA_FLAG_IGN_SIMPLEX,
.pio_mask = 0x1f, /* pio0-4 */
@@ -185,12 +183,13 @@
static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
- struct ata_probe_ent *probe_ent;
- struct ata_port_info *ppi[2];
- int rc;
+ const struct ata_port_info *ppi[] = { &uli_port_info, NULL };
unsigned int board_idx = (unsigned int) ent->driver_data;
+ struct ata_host *host;
struct uli_priv *hpriv;
void __iomem * const *iomap;
+ struct ata_ioports *ioaddr;
+ int n_ports, rc;
if (!printed_version++)
dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
@@ -199,54 +198,42 @@
if (rc)
return rc;
- rc = pci_request_regions(pdev, DRV_NAME);
- if (rc) {
- pcim_pin_device(pdev);
- return rc;
- }
-
- rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ n_ports = 2;
+ if (board_idx == uli_5287)
+ n_ports = 4;
+ rc = ata_pci_prepare_native_host(pdev, ppi, n_ports, &host);
if (rc)
return rc;
- rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
- if (rc)
- return rc;
-
- ppi[0] = ppi[1] = &uli_port_info;
- probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
- if (!probe_ent)
- return -ENOMEM;
hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
if (!hpriv)
return -ENOMEM;
+ host->private_data = hpriv;
- probe_ent->private_data = hpriv;
-
- iomap = pcim_iomap_table(pdev);
+ iomap = host->iomap;
switch (board_idx) {
case uli_5287:
hpriv->scr_cfg_addr[0] = ULI5287_BASE;
hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS;
- probe_ent->n_ports = 4;
- probe_ent->port[2].cmd_addr = iomap[0] + 8;
- probe_ent->port[2].altstatus_addr =
- probe_ent->port[2].ctl_addr = (void __iomem *)
+ ioaddr = &host->ports[2]->ioaddr;
+ ioaddr->cmd_addr = iomap[0] + 8;
+ ioaddr->altstatus_addr =
+ ioaddr->ctl_addr = (void __iomem *)
((unsigned long)iomap[1] | ATA_PCI_CTL_OFS) + 4;
- probe_ent->port[2].bmdma_addr = iomap[4] + 16;
+ ioaddr->bmdma_addr = iomap[4] + 16;
hpriv->scr_cfg_addr[2] = ULI5287_BASE + ULI5287_OFFS*4;
+ ata_std_ports(ioaddr);
- probe_ent->port[3].cmd_addr = iomap[2] + 8;
- probe_ent->port[3].altstatus_addr =
- probe_ent->port[3].ctl_addr = (void __iomem *)
+ ioaddr = &host->ports[3]->ioaddr;
+ ioaddr->cmd_addr = iomap[2] + 8;
+ ioaddr->altstatus_addr =
+ ioaddr->ctl_addr = (void __iomem *)
((unsigned long)iomap[3] | ATA_PCI_CTL_OFS) + 4;
- probe_ent->port[3].bmdma_addr = iomap[4] + 24;
+ ioaddr->bmdma_addr = iomap[4] + 24;
hpriv->scr_cfg_addr[3] = ULI5287_BASE + ULI5287_OFFS*5;
-
- ata_std_ports(&probe_ent->port[2]);
- ata_std_ports(&probe_ent->port[3]);
+ ata_std_ports(ioaddr);
break;
case uli_5289:
@@ -266,12 +253,8 @@
pci_set_master(pdev);
pci_intx(pdev, 1);
-
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(&pdev->dev, probe_ent);
- return 0;
+ return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+ &uli_sht);
}
static int __init uli_init(void)
diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c
index 598e6a2..1d855f5 100644
--- a/drivers/ata/sata_via.c
+++ b/drivers/ata/sata_via.c
@@ -64,8 +64,6 @@
PORT0 = (1 << 1),
PORT1 = (1 << 0),
ALL_PORTS = PORT0 | PORT1,
- PATA_PORT = 2, /* PATA is port 2 */
- N_PORTS = 3,
NATIVE_MODE_ALL = (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4),
@@ -78,11 +76,9 @@
static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
static void svia_noop_freeze(struct ata_port *ap);
static void vt6420_error_handler(struct ata_port *ap);
-static void vt6421_sata_error_handler(struct ata_port *ap);
-static void vt6421_pata_error_handler(struct ata_port *ap);
+static int vt6421_pata_cable_detect(struct ata_port *ap);
static void vt6421_set_pio_mode(struct ata_port *ap, struct ata_device *adev);
static void vt6421_set_dma_mode(struct ata_port *ap, struct ata_device *adev);
-static int vt6421_port_start(struct ata_port *ap);
static const struct pci_device_id svia_pci_tbl[] = {
{ PCI_VDEVICE(VIA, 0x5337), vt6420 },
@@ -141,7 +137,6 @@
.error_handler = vt6420_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -172,15 +167,15 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = vt6421_pata_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = vt6421_pata_cable_detect,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
- .port_start = vt6421_port_start,
+ .port_start = ata_port_start,
};
static const struct ata_port_operations vt6421_sata_ops = {
@@ -203,10 +198,10 @@
.freeze = ata_bmdma_freeze,
.thaw = ata_bmdma_thaw,
- .error_handler = vt6421_sata_error_handler,
+ .error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .cable_detect = ata_cable_sata,
- .irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -214,11 +209,10 @@
.scr_read = svia_scr_read,
.scr_write = svia_scr_write,
- .port_start = vt6421_port_start,
+ .port_start = ata_port_start,
};
-static struct ata_port_info vt6420_port_info = {
- .sht = &svia_sht,
+static const struct ata_port_info vt6420_port_info = {
.flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
.pio_mask = 0x1f,
.mwdma_mask = 0x07,
@@ -226,6 +220,22 @@
.port_ops = &vt6420_sata_ops,
};
+static struct ata_port_info vt6421_sport_info = {
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
+ .pio_mask = 0x1f,
+ .mwdma_mask = 0x07,
+ .udma_mask = 0x7f,
+ .port_ops = &vt6421_sata_ops,
+};
+
+static struct ata_port_info vt6421_pport_info = {
+ .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY,
+ .pio_mask = 0x1f,
+ .mwdma_mask = 0,
+ .udma_mask = 0x7f,
+ .port_ops = &vt6421_pata_ops,
+};
+
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("SCSI low-level driver for VIA SATA controllers");
MODULE_LICENSE("GPL");
@@ -330,35 +340,15 @@
NULL, ata_std_postreset);
}
-static int vt6421_pata_prereset(struct ata_port *ap)
+static int vt6421_pata_cable_detect(struct ata_port *ap)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u8 tmp;
pci_read_config_byte(pdev, PATA_UDMA_TIMING, &tmp);
if (tmp & 0x10)
- ap->cbl = ATA_CBL_PATA40;
- else
- ap->cbl = ATA_CBL_PATA80;
- return 0;
-}
-
-static void vt6421_pata_error_handler(struct ata_port *ap)
-{
- return ata_bmdma_drive_eh(ap, vt6421_pata_prereset, ata_std_softreset,
- NULL, ata_std_postreset);
-}
-
-static int vt6421_sata_prereset(struct ata_port *ap)
-{
- ap->cbl = ATA_CBL_SATA;
- return 0;
-}
-
-static void vt6421_sata_error_handler(struct ata_port *ap)
-{
- return ata_bmdma_drive_eh(ap, vt6421_sata_prereset, ata_std_softreset,
- NULL, ata_std_postreset);
+ return ATA_CBL_PATA40;
+ return ATA_CBL_PATA80;
}
static void vt6421_set_pio_mode(struct ata_port *ap, struct ata_device *adev)
@@ -375,16 +365,6 @@
pci_write_config_byte(pdev, PATA_UDMA_TIMING, udma_bits[adev->pio_mode - XFER_UDMA_0]);
}
-static int vt6421_port_start(struct ata_port *ap)
-{
- if (ap->port_no == PATA_PORT) {
- ap->ops = &vt6421_pata_ops;
- ap->mwdma_mask = 0;
- ap->flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY | ATA_FLAG_SRST;
- }
- return ata_port_start(ap);
-}
-
static const unsigned int svia_bar_sizes[] = {
8, 4, 8, 4, 16, 256
};
@@ -403,79 +383,78 @@
return addr + (port * 64);
}
-static void vt6421_init_addrs(struct ata_probe_ent *probe_ent,
- void __iomem * const *iomap, unsigned int port)
+static void vt6421_init_addrs(struct ata_port *ap)
{
- void __iomem *reg_addr = iomap[port];
- void __iomem *bmdma_addr = iomap[4] + (port * 8);
+ void __iomem * const * iomap = ap->host->iomap;
+ void __iomem *reg_addr = iomap[ap->port_no];
+ void __iomem *bmdma_addr = iomap[4] + (ap->port_no * 8);
+ struct ata_ioports *ioaddr = &ap->ioaddr;
- probe_ent->port[port].cmd_addr = reg_addr;
- probe_ent->port[port].altstatus_addr =
- probe_ent->port[port].ctl_addr = (void __iomem *)
+ ioaddr->cmd_addr = reg_addr;
+ ioaddr->altstatus_addr =
+ ioaddr->ctl_addr = (void __iomem *)
((unsigned long)(reg_addr + 8) | ATA_PCI_CTL_OFS);
- probe_ent->port[port].bmdma_addr = bmdma_addr;
- probe_ent->port[port].scr_addr = vt6421_scr_addr(iomap[5], port);
+ ioaddr->bmdma_addr = bmdma_addr;
+ ioaddr->scr_addr = vt6421_scr_addr(iomap[5], ap->port_no);
- ata_std_ports(&probe_ent->port[port]);
+ ata_std_ports(ioaddr);
}
-static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev)
+static int vt6420_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
{
- struct ata_probe_ent *probe_ent;
- struct ata_port_info *ppi[2];
- void __iomem *bar5;
+ const struct ata_port_info *ppi[] = { &vt6420_port_info, NULL };
+ struct ata_host *host;
+ int rc;
- ppi[0] = ppi[1] = &vt6420_port_info;
- probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
- if (!probe_ent)
- return NULL;
+ rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host);
+ if (rc)
+ return rc;
+ *r_host = host;
- bar5 = pcim_iomap(pdev, 5, 0);
- if (!bar5) {
+ rc = pcim_iomap_regions(pdev, 1 << 5, DRV_NAME);
+ if (rc) {
dev_printk(KERN_ERR, &pdev->dev, "failed to iomap PCI BAR 5\n");
- return NULL;
+ return rc;
}
- probe_ent->port[0].scr_addr = svia_scr_addr(bar5, 0);
- probe_ent->port[1].scr_addr = svia_scr_addr(bar5, 1);
+ host->ports[0]->ioaddr.scr_addr = svia_scr_addr(host->iomap[5], 0);
+ host->ports[1]->ioaddr.scr_addr = svia_scr_addr(host->iomap[5], 1);
- return probe_ent;
+ return 0;
}
-static struct ata_probe_ent *vt6421_init_probe_ent(struct pci_dev *pdev)
+static int vt6421_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
{
- struct ata_probe_ent *probe_ent;
- unsigned int i;
+ const struct ata_port_info *ppi[] =
+ { &vt6421_sport_info, &vt6421_sport_info, &vt6421_pport_info };
+ struct ata_host *host;
+ int i, rc;
- probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
- if (!probe_ent)
- return NULL;
+ *r_host = host = ata_host_alloc_pinfo(&pdev->dev, ppi, ARRAY_SIZE(ppi));
+ if (!host) {
+ dev_printk(KERN_ERR, &pdev->dev, "failed to allocate host\n");
+ return -ENOMEM;
+ }
- memset(probe_ent, 0, sizeof(*probe_ent));
- probe_ent->dev = pci_dev_to_dev(pdev);
- INIT_LIST_HEAD(&probe_ent->node);
+ rc = pcim_iomap_regions(pdev, 0x1f, DRV_NAME);
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev, "failed to request/iomap "
+ "PCI BARs (errno=%d)\n", rc);
+ return rc;
+ }
+ host->iomap = pcim_iomap_table(pdev);
- probe_ent->sht = &svia_sht;
- probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY;
- probe_ent->port_ops = &vt6421_sata_ops;
- probe_ent->n_ports = N_PORTS;
- probe_ent->irq = pdev->irq;
- probe_ent->irq_flags = IRQF_SHARED;
- probe_ent->pio_mask = 0x1f;
- probe_ent->mwdma_mask = 0x07;
- probe_ent->udma_mask = 0x7f;
+ for (i = 0; i < host->n_ports; i++)
+ vt6421_init_addrs(host->ports[i]);
- for (i = 0; i < 6; i++)
- if (!pcim_iomap(pdev, i, 0)) {
- dev_printk(KERN_ERR, &pdev->dev,
- "failed to iomap PCI BAR %d\n", i);
- return NULL;
- }
+ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ return rc;
+ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ return rc;
- for (i = 0; i < N_PORTS; i++)
- vt6421_init_addrs(probe_ent, pcim_iomap_table(pdev), i);
-
- return probe_ent;
+ return 0;
}
static void svia_configure(struct pci_dev *pdev)
@@ -522,7 +501,7 @@
static int printed_version;
unsigned int i;
int rc;
- struct ata_probe_ent *probe_ent;
+ struct ata_host *host;
int board_id = (int) ent->driver_data;
const int *bar_sizes;
u8 tmp8;
@@ -534,12 +513,6 @@
if (rc)
return rc;
- rc = pci_request_regions(pdev, DRV_NAME);
- if (rc) {
- pcim_pin_device(pdev);
- return rc;
- }
-
if (board_id == vt6420) {
pci_read_config_byte(pdev, SATA_PATA_SHARING, &tmp8);
if (tmp8 & SATA_2DEV) {
@@ -565,32 +538,18 @@
return -ENODEV;
}
- rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
- if (rc)
- return rc;
- rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
- if (rc)
- return rc;
-
if (board_id == vt6420)
- probe_ent = vt6420_init_probe_ent(pdev);
+ rc = vt6420_prepare_host(pdev, &host);
else
- probe_ent = vt6421_init_probe_ent(pdev);
-
- if (!probe_ent) {
- dev_printk(KERN_ERR, &pdev->dev, "out of memory\n");
- return -ENOMEM;
- }
+ rc = vt6421_prepare_host(pdev, &host);
+ if (rc)
+ return rc;
svia_configure(pdev);
pci_set_master(pdev);
-
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(&pdev->dev, probe_ent);
- return 0;
+ return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+ &svia_sht);
}
static int __init svia_init(void)
diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c
index 170bad1..80126f8 100644
--- a/drivers/ata/sata_vsc.c
+++ b/drivers/ata/sata_vsc.c
@@ -333,7 +333,6 @@
.thaw = vsc_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
- .irq_handler = vsc_sata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.irq_on = ata_irq_on,
.irq_ack = ata_irq_ack,
@@ -367,30 +366,50 @@
static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
+ static const struct ata_port_info pi = {
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_MMIO,
+ .pio_mask = 0x1f,
+ .mwdma_mask = 0x07,
+ .udma_mask = 0x7f,
+ .port_ops = &vsc_sata_ops,
+ };
+ const struct ata_port_info *ppi[] = { &pi, NULL };
static int printed_version;
- struct ata_probe_ent *probe_ent;
+ struct ata_host *host;
void __iomem *mmio_base;
- int rc;
+ int i, rc;
u8 cls;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
+ /* allocate host */
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4);
+ if (!host)
+ return -ENOMEM;
+
rc = pcim_enable_device(pdev);
if (rc)
return rc;
- /*
- * Check if we have needed resource mapped.
- */
+ /* check if we have needed resource mapped */
if (pci_resource_len(pdev, 0) == 0)
return -ENODEV;
+ /* map IO regions and intialize host accordingly */
rc = pcim_iomap_regions(pdev, 1 << VSC_MMIO_BAR, DRV_NAME);
if (rc == -EBUSY)
pcim_pin_device(pdev);
if (rc)
return rc;
+ host->iomap = pcim_iomap_table(pdev);
+
+ mmio_base = host->iomap[VSC_MMIO_BAR];
+
+ for (i = 0; i < host->n_ports; i++)
+ vsc_sata_setup_port(&host->ports[i]->ioaddr,
+ mmio_base + (i + 1) * VSC_SATA_PORT_OFFSET);
/*
* Use 32 bit DMA mask, because 64 bit address support is poor.
@@ -402,12 +421,6 @@
if (rc)
return rc;
- probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
- if (probe_ent == NULL)
- return -ENOMEM;
- probe_ent->dev = pci_dev_to_dev(pdev);
- INIT_LIST_HEAD(&probe_ent->node);
-
/*
* Due to a bug in the chip, the default cache line size can't be
* used (unless the default is non-zero).
@@ -418,33 +431,6 @@
if (pci_enable_msi(pdev) == 0)
pci_intx(pdev, 0);
- else
- probe_ent->irq_flags = IRQF_SHARED;
-
- probe_ent->sht = &vsc_sata_sht;
- probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO;
- probe_ent->port_ops = &vsc_sata_ops;
- probe_ent->n_ports = 4;
- probe_ent->irq = pdev->irq;
- probe_ent->iomap = pcim_iomap_table(pdev);
-
- /* We don't care much about the PIO/UDMA masks, but the core won't like us
- * if we don't fill these
- */
- probe_ent->pio_mask = 0x1f;
- probe_ent->mwdma_mask = 0x07;
- probe_ent->udma_mask = 0x7f;
-
- mmio_base = probe_ent->iomap[VSC_MMIO_BAR];
-
- /* We have 4 ports per PCI function */
- vsc_sata_setup_port(&probe_ent->port[0], mmio_base + 1 * VSC_SATA_PORT_OFFSET);
- vsc_sata_setup_port(&probe_ent->port[1], mmio_base + 2 * VSC_SATA_PORT_OFFSET);
- vsc_sata_setup_port(&probe_ent->port[2], mmio_base + 3 * VSC_SATA_PORT_OFFSET);
- vsc_sata_setup_port(&probe_ent->port[3], mmio_base + 4 * VSC_SATA_PORT_OFFSET);
-
- pci_set_master(pdev);
/*
* Config offset 0x98 is "Extended Control and Status Register 0"
@@ -454,11 +440,9 @@
*/
pci_write_config_dword(pdev, 0x98, 0);
- if (!ata_device_add(probe_ent))
- return -ENODEV;
-
- devm_kfree(&pdev->dev, probe_ent);
- return 0;
+ pci_set_master(pdev);
+ return ata_host_activate(host, pdev->irq, vsc_sata_interrupt,
+ IRQF_SHARED, &vsc_sata_sht);
}
static const struct pci_device_id vsc_sata_pci_tbl[] = {
diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c
index 7823757..3ef593a 100644
--- a/drivers/char/sonypi.c
+++ b/drivers/char/sonypi.c
@@ -1,6 +1,8 @@
/*
* 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_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 @@
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 @@
if (!camera)
return -EIO;
- down(&sonypi_device.lock);
+ mutex_lock(&sonypi_device.lock);
switch (command) {
case SONYPI_COMMAND_SETCAMERA:
@@ -919,7 +926,7 @@
command);
break;
}
- up(&sonypi_device.lock);
+ mutex_unlock(&sonypi_device.lock);
return 0;
}
@@ -938,20 +945,20 @@
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 @@
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 @@
default:
ret = -EINVAL;
}
- up(&sonypi_device.lock);
+ mutex_unlock(&sonypi_device.lock);
return ret;
}
@@ -1260,6 +1267,28 @@
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 @@
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 @@
}
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,
diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c
index 5ac309e..5cfcff5 100644
--- a/drivers/clocksource/acpi_pm.c
+++ b/drivers/clocksource/acpi_pm.c
@@ -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;
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 639e8b6..bc77378 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -577,14 +577,14 @@
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.
diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c
index 98681da..664aba8 100644
--- a/drivers/media/video/meye.c
+++ b/drivers/media/video/meye.c
@@ -925,13 +925,13 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h
index ea107cb..323d007 100644
--- a/drivers/media/video/meye.h
+++ b/drivers/media/video/meye.h
@@ -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>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 80b199f..a3c525b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -112,14 +112,70 @@
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
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7793ccd..e325164 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -12,3 +12,4 @@
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
diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c
index 4b23212..65c32a9 100644
--- a/drivers/misc/asus-laptop.c
+++ b/drivers/misc/asus-laptop.c
@@ -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_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(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(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 @@
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 @@
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 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 @@
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 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 @@
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 @@
* 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 @@
* 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 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 @@
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 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 @@
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 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 @@
* 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 @@
{
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 @@
}
/* 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 @@
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 @@
* 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 @@
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 @@
ASUS_LED_UNREGISTER(tled);
ASUS_LED_UNREGISTER(pled);
ASUS_LED_UNREGISTER(rled);
+ ASUS_LED_UNREGISTER(gled);
destroy_workqueue(led_workqueue);
}
@@ -1062,6 +1097,10 @@
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;
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c
index ac708bc..c15c1f6 100644
--- a/drivers/misc/sony-laptop.c
+++ b/drivers/misc/sony-laptop.c
@@ -1,5 +1,5 @@
/*
- * 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 Alcôve <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 *,
- char *);
-static ssize_t sony_acpi_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);
+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
-struct sony_acpi_value {
+static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *,
+ char *);
+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);
+
+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 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 @@
return 0;
}
- printk(LOG_PFX "acpi_callreadfunc failed\n");
+ printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n");
return -1;
}
@@ -179,7 +562,7 @@
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 @@
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 @@
}
/*
- * 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 @@
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 @@
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 @@
{
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 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 @@
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 @@
/*
* 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 @@
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;
- sony_acpi_acpi_device = device;
+ printk(KERN_INFO DRV_PFX "%s v%s.\n",
+ SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
- sony_acpi_handle = device->handle;
+ sony_nc_acpi_device = device;
+ strcpy(acpi_device_class(device), "sony/hotkey");
+
+ 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,
- ACPI_DEVICE_NOTIFY,
- sony_acpi_notify, NULL);
- if (ACPI_FAILURE(status)) {
- printk(LOG_PFX "unable to install notify handler\n");
- result = -ENODEV;
+ /* 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;
}
- if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle, "GBRT", &handle))) {
+ status = acpi_install_notify_handler(sony_nc_acpi_handle,
+ ACPI_DEVICE_NOTIFY,
+ sony_acpi_notify, NULL);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_WARNING DRV_PFX "unable to install notify handler\n");
+ result = -ENODEV;
+ goto outinput;
+ }
+
+ 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 @@
}
- 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)
+/*********** 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)
{
- return acpi_bus_register_driver(&sony_acpi_driver);
+ 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;
}
-static void __exit sony_acpi_exit(void)
-{
- acpi_bus_unregister_driver(&sony_acpi_driver);
+#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__); \
}
-module_init(sony_acpi_init);
-module_exit(sony_acpi_exit);
+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_laptop_exit(void)
+{
+ acpi_bus_unregister_driver(&sony_nc_driver);
+ if (!no_spic)
+ acpi_bus_unregister_driver(&sony_pic_driver);
+}
+
+module_init(sony_laptop_init);
+module_exit(sony_laptop_exit);
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
new file mode 100644
index 0000000..6c36a55
--- /dev/null
+++ b/drivers/misc/thinkpad_acpi.c
@@ -0,0 +1,4312 @@
+/*
+ * thinkpad_acpi.c - 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.
+ */
+
+#define IBM_VERSION "0.14"
+#define TPACPI_SYSFS_VERSION 0x000100
+
+/*
+ * Changelog:
+ * 2007-03-27 0.14 renamed to thinkpad_acpi and moved to
+ * drivers/misc.
+ *
+ * 2006-11-22 0.13 new maintainer
+ * changelog now lives in git commit history, and will
+ * not be updated further in-file.
+ *
+ * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels
+ * 2005-03-17 0.11 support for 600e, 770x
+ * thanks to Jamie Lentin <lentinj@dial.pipex.com>
+ * support for 770e, G41
+ * G40 and G41 don't have a thinklight
+ * temperatures no longer experimental
+ * experimental brightness control
+ * experimental volume control
+ * experimental fan enable/disable
+ * 2005-01-16 0.10 fix module loading on R30, R31
+ * 2005-01-16 0.9 support for 570, R30, R31
+ * ultrabay support on A22p, A3x
+ * limit arg for cmos, led, beep, drop experimental status
+ * more capable led control on A21e, A22p, T20-22, X20
+ * experimental temperatures and fan speed
+ * experimental embedded controller register dump
+ * mark more functions as __init, drop incorrect __exit
+ * use MODULE_VERSION
+ * thanks to Henrik Brix Andersen <brix@gentoo.org>
+ * fix parameter passing on module loading
+ * thanks to Rusty Russell <rusty@rustcorp.com.au>
+ * thanks to Jim Radford <radford@blackbean.org>
+ * 2004-11-08 0.8 fix init error case, don't return from a macro
+ * thanks to Chris Wright <chrisw@osdl.org>
+ * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20
+ * fix led control on A21e
+ * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device
+ * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20
+ * proc file format changed
+ * video_switch command
+ * experimental cmos control
+ * experimental led control
+ * experimental acpi sounds
+ * 2004-09-16 0.4 support for module parameters
+ * hotkey mask can be prefixed by 0x
+ * video output switching
+ * video expansion control
+ * ultrabay eject support
+ * removed lcd brightness/on/off control, didn't work
+ * 2004-08-17 0.3 support for R40
+ * lcd off, brightness control
+ * thinklight on/off
+ * 2004-08-14 0.2 support for T series, X20
+ * bluetooth enable/disable
+ * hotkey events disabled by default
+ * removed fan control, currently useless
+ * 2004-08-09 0.1 initial release, support for X series
+ */
+
+#include "thinkpad_acpi.h"
+
+MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
+MODULE_DESCRIPTION(IBM_DESC);
+MODULE_VERSION(IBM_VERSION);
+MODULE_LICENSE("GPL");
+
+/* Please remove this in year 2009 */
+MODULE_ALIAS("ibm_acpi");
+
+#define __unused __attribute__ ((unused))
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * ACPI Helpers and device model
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+/*************************************************************************
+ * ACPI basic handles
+ */
+
+static acpi_handle root_handle = NULL;
+
+#define IBM_HANDLE(object, parent, paths...) \
+ static acpi_handle object##_handle; \
+ static acpi_handle *object##_parent = &parent##_handle; \
+ static char *object##_path; \
+ static char *object##_paths[] = { paths }
+
+IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
+ "\\_SB.PCI.ISA.EC", /* 570 */
+ "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */
+ "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
+ "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */
+ "\\_SB.PCI0.ICH3.EC0", /* R31 */
+ "\\_SB.PCI0.LPC.EC", /* all others */
+ );
+
+IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */
+IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */
+
+
+/*************************************************************************
+ * Misc ACPI handles
+ */
+
+IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */
+ "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */
+ "\\CMS", /* R40, R40e */
+ ); /* all others */
+
+IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
+ "^HKEY", /* R30, R31 */
+ "HKEY", /* all others */
+ ); /* 570 */
+
+
+/*************************************************************************
+ * ACPI helpers
+ */
+
+static int acpi_evalf(acpi_handle handle,
+ void *res, char *method, char *fmt, ...)
+{
+ char *fmt0 = fmt;
+ struct acpi_object_list params;
+ union acpi_object in_objs[IBM_MAX_ACPI_ARGS];
+ struct acpi_buffer result, *resultp;
+ union acpi_object out_obj;
+ acpi_status status;
+ va_list ap;
+ char res_type;
+ int success;
+ int quiet;
+
+ if (!*fmt) {
+ printk(IBM_ERR "acpi_evalf() called with empty format\n");
+ return 0;
+ }
+
+ if (*fmt == 'q') {
+ quiet = 1;
+ fmt++;
+ } else
+ quiet = 0;
+
+ res_type = *(fmt++);
+
+ params.count = 0;
+ params.pointer = &in_objs[0];
+
+ va_start(ap, fmt);
+ while (*fmt) {
+ char c = *(fmt++);
+ switch (c) {
+ case 'd': /* int */
+ in_objs[params.count].integer.value = va_arg(ap, int);
+ in_objs[params.count++].type = ACPI_TYPE_INTEGER;
+ break;
+ /* add more types as needed */
+ default:
+ printk(IBM_ERR "acpi_evalf() called "
+ "with invalid format character '%c'\n", c);
+ return 0;
+ }
+ }
+ va_end(ap);
+
+ if (res_type != 'v') {
+ result.length = sizeof(out_obj);
+ result.pointer = &out_obj;
+ resultp = &result;
+ } else
+ resultp = NULL;
+
+ status = acpi_evaluate_object(handle, method, ¶ms, resultp);
+
+ switch (res_type) {
+ case 'd': /* int */
+ if (res)
+ *(int *)res = out_obj.integer.value;
+ success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
+ break;
+ case 'v': /* void */
+ success = status == AE_OK;
+ break;
+ /* add more types as needed */
+ default:
+ printk(IBM_ERR "acpi_evalf() called "
+ "with invalid format character '%c'\n", res_type);
+ return 0;
+ }
+
+ if (!success && !quiet)
+ printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
+ method, fmt0, status);
+
+ return success;
+}
+
+static void __unused acpi_print_int(acpi_handle handle, char *method)
+{
+ int i;
+
+ if (acpi_evalf(handle, &i, method, "d"))
+ printk(IBM_INFO "%s = 0x%x\n", method, i);
+ else
+ printk(IBM_ERR "error calling %s\n", method);
+}
+
+static int acpi_ec_read(int i, u8 * p)
+{
+ int v;
+
+ if (ecrd_handle) {
+ if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
+ return 0;
+ *p = v;
+ } else {
+ if (ec_read(i, p) < 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int acpi_ec_write(int i, u8 v)
+{
+ if (ecwr_handle) {
+ if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
+ return 0;
+ } else {
+ if (ec_write(i, v) < 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _sta(acpi_handle handle)
+{
+ int status;
+
+ if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
+ status = 0;
+
+ return status;
+}
+
+static int issue_thinkpad_cmos_command(int cmos_cmd)
+{
+ if (!cmos_handle)
+ return -ENXIO;
+
+ if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd))
+ return -EIO;
+
+ return 0;
+}
+
+/*************************************************************************
+ * ACPI device model
+ */
+
+static void drv_acpi_handle_init(char *name,
+ acpi_handle *handle, acpi_handle parent,
+ char **paths, int num_paths, char **path)
+{
+ int i;
+ acpi_status status;
+
+ vdbg_printk(TPACPI_DBG_INIT, "trying to locate ACPI handle for %s\n",
+ name);
+
+ for (i = 0; i < num_paths; i++) {
+ status = acpi_get_handle(parent, paths[i], handle);
+ if (ACPI_SUCCESS(status)) {
+ *path = paths[i];
+ dbg_printk(TPACPI_DBG_INIT,
+ "Found ACPI handle %s for %s\n",
+ *path, name);
+ return;
+ }
+ }
+
+ vdbg_printk(TPACPI_DBG_INIT, "ACPI handle for %s not found\n",
+ name);
+ *handle = NULL;
+}
+
+static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct ibm_struct *ibm = data;
+
+ if (!ibm || !ibm->acpi || !ibm->acpi->notify)
+ return;
+
+ ibm->acpi->notify(ibm, event);
+}
+
+static int __init setup_acpi_notify(struct ibm_struct *ibm)
+{
+ acpi_status status;
+ int rc;
+
+ BUG_ON(!ibm->acpi);
+
+ if (!*ibm->acpi->handle)
+ return 0;
+
+ vdbg_printk(TPACPI_DBG_INIT,
+ "setting up ACPI notify for %s\n", ibm->name);
+
+ rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device);
+ if (rc < 0) {
+ printk(IBM_ERR "acpi_bus_get_device(%s) failed: %d\n",
+ ibm->name, rc);
+ return -ENODEV;
+ }
+
+ acpi_driver_data(ibm->acpi->device) = ibm;
+ sprintf(acpi_device_class(ibm->acpi->device), "%s/%s",
+ IBM_ACPI_EVENT_PREFIX,
+ ibm->name);
+
+ status = acpi_install_notify_handler(*ibm->acpi->handle,
+ ibm->acpi->type, dispatch_acpi_notify, ibm);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_ALREADY_EXISTS) {
+ printk(IBM_NOTICE "another device driver is already handling %s events\n",
+ ibm->name);
+ } else {
+ printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n",
+ ibm->name, status);
+ }
+ return -ENODEV;
+ }
+ ibm->flags.acpi_notify_installed = 1;
+ return 0;
+}
+
+static int __init tpacpi_device_add(struct acpi_device *device)
+{
+ return 0;
+}
+
+static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
+{
+ int rc;
+
+ dbg_printk(TPACPI_DBG_INIT,
+ "registering %s as an ACPI driver\n", ibm->name);
+
+ BUG_ON(!ibm->acpi);
+
+ ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
+ if (!ibm->acpi->driver) {
+ printk(IBM_ERR "kzalloc(ibm->driver) failed\n");
+ return -ENOMEM;
+ }
+
+ sprintf(ibm->acpi->driver->name, "%s_%s", IBM_NAME, ibm->name);
+ ibm->acpi->driver->ids = ibm->acpi->hid;
+ ibm->acpi->driver->ops.add = &tpacpi_device_add;
+
+ rc = acpi_bus_register_driver(ibm->acpi->driver);
+ if (rc < 0) {
+ printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n",
+ ibm->acpi->hid, rc);
+ kfree(ibm->acpi->driver);
+ ibm->acpi->driver = NULL;
+ } else if (!rc)
+ ibm->flags.acpi_driver_registered = 1;
+
+ return rc;
+}
+
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Procfs Helpers
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+static int dispatch_procfs_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct ibm_struct *ibm = data;
+ int len;
+
+ if (!ibm || !ibm->read)
+ return -EINVAL;
+
+ len = ibm->read(page);
+ if (len < 0)
+ return len;
+
+ if (len <= off + count)
+ *eof = 1;
+ *start = page + off;
+ len -= off;
+ if (len > count)
+ len = count;
+ if (len < 0)
+ len = 0;
+
+ return len;
+}
+
+static int dispatch_procfs_write(struct file *file,
+ const char __user * userbuf,
+ unsigned long count, void *data)
+{
+ struct ibm_struct *ibm = data;
+ char *kernbuf;
+ int ret;
+
+ if (!ibm || !ibm->write)
+ return -EINVAL;
+
+ kernbuf = kmalloc(count + 2, GFP_KERNEL);
+ if (!kernbuf)
+ return -ENOMEM;
+
+ if (copy_from_user(kernbuf, userbuf, count)) {
+ kfree(kernbuf);
+ return -EFAULT;
+ }
+
+ kernbuf[count] = 0;
+ strcat(kernbuf, ",");
+ ret = ibm->write(kernbuf);
+ if (ret == 0)
+ ret = count;
+
+ kfree(kernbuf);
+
+ return ret;
+}
+
+static char *next_cmd(char **cmds)
+{
+ char *start = *cmds;
+ char *end;
+
+ while ((end = strchr(start, ',')) && end == start)
+ start = end + 1;
+
+ if (!end)
+ return NULL;
+
+ *end = 0;
+ *cmds = end + 1;
+ return start;
+}
+
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Device model: hwmon and platform
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+static struct platform_device *tpacpi_pdev = NULL;
+static struct class_device *tpacpi_hwmon = NULL;
+
+static struct platform_driver tpacpi_pdriver = {
+ .driver = {
+ .name = IBM_DRVR_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+
+/*************************************************************************
+ * thinkpad-acpi driver attributes
+ */
+
+/* interface_version --------------------------------------------------- */
+static ssize_t tpacpi_driver_interface_version_show(
+ struct device_driver *drv,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION);
+}
+
+static DRIVER_ATTR(interface_version, S_IRUGO,
+ tpacpi_driver_interface_version_show, NULL);
+
+/* debug_level --------------------------------------------------------- */
+static ssize_t tpacpi_driver_debug_show(struct device_driver *drv,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level);
+}
+
+static ssize_t tpacpi_driver_debug_store(struct device_driver *drv,
+ const char *buf, size_t count)
+{
+ unsigned long t;
+
+ if (parse_strtoul(buf, 0xffff, &t))
+ return -EINVAL;
+
+ dbg_level = t;
+
+ return count;
+}
+
+static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
+ tpacpi_driver_debug_show, tpacpi_driver_debug_store);
+
+/* version ------------------------------------------------------------- */
+static ssize_t tpacpi_driver_version_show(struct device_driver *drv,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s v%s\n", IBM_DESC, IBM_VERSION);
+}
+
+static DRIVER_ATTR(version, S_IRUGO,
+ tpacpi_driver_version_show, NULL);
+
+/* --------------------------------------------------------------------- */
+
+static struct driver_attribute* tpacpi_driver_attributes[] = {
+ &driver_attr_debug_level, &driver_attr_version,
+ &driver_attr_interface_version,
+};
+
+static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
+{
+ int i, res;
+
+ i = 0;
+ res = 0;
+ while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) {
+ res = driver_create_file(drv, tpacpi_driver_attributes[i]);
+ i++;
+ }
+
+ return res;
+}
+
+static void tpacpi_remove_driver_attributes(struct device_driver *drv)
+{
+ int i;
+
+ for(i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++)
+ driver_remove_file(drv, tpacpi_driver_attributes[i]);
+}
+
+/*************************************************************************
+ * sysfs support helpers
+ */
+
+struct attribute_set_obj {
+ struct attribute_set s;
+ struct attribute *a;
+} __attribute__((packed));
+
+static struct attribute_set *create_attr_set(unsigned int max_members,
+ const char* name)
+{
+ struct attribute_set_obj *sobj;
+
+ if (max_members == 0)
+ return NULL;
+
+ /* Allocates space for implicit NULL at the end too */
+ sobj = kzalloc(sizeof(struct attribute_set_obj) +
+ max_members * sizeof(struct attribute *),
+ GFP_KERNEL);
+ if (!sobj)
+ return NULL;
+ sobj->s.max_members = max_members;
+ sobj->s.group.attrs = &sobj->a;
+ sobj->s.group.name = name;
+
+ return &sobj->s;
+}
+
+/* not multi-threaded safe, use it in a single thread per set */
+static int add_to_attr_set(struct attribute_set* s, struct attribute *attr)
+{
+ if (!s || !attr)
+ return -EINVAL;
+
+ if (s->members >= s->max_members)
+ return -ENOMEM;
+
+ s->group.attrs[s->members] = attr;
+ s->members++;
+
+ return 0;
+}
+
+static int add_many_to_attr_set(struct attribute_set* s,
+ struct attribute **attr,
+ unsigned int count)
+{
+ int i, res;
+
+ for (i = 0; i < count; i++) {
+ res = add_to_attr_set(s, attr[i]);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static void delete_attr_set(struct attribute_set* s, struct kobject *kobj)
+{
+ sysfs_remove_group(kobj, &s->group);
+ destroy_attr_set(s);
+}
+
+static int parse_strtoul(const char *buf,
+ unsigned long max, unsigned long *value)
+{
+ char *endp;
+
+ *value = simple_strtoul(buf, &endp, 0);
+ while (*endp && isspace(*endp))
+ endp++;
+ if (*endp || *value > max)
+ return -EINVAL;
+
+ return 0;
+}
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Subdrivers
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+/*************************************************************************
+ * thinkpad-acpi init subdriver
+ */
+
+static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
+{
+ printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
+ printk(IBM_INFO "%s\n", IBM_URL);
+
+ if (ibm_thinkpad_ec_found)
+ printk(IBM_INFO "ThinkPad EC firmware %s\n",
+ ibm_thinkpad_ec_found);
+
+ return 0;
+}
+
+static int thinkpad_acpi_driver_read(char *p)
+{
+ int len = 0;
+
+ len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC);
+ len += sprintf(p + len, "version:\t%s\n", IBM_VERSION);
+
+ return len;
+}
+
+static struct ibm_struct thinkpad_acpi_driver_data = {
+ .name = "driver",
+ .read = thinkpad_acpi_driver_read,
+};
+
+/*************************************************************************
+ * Hotkey subdriver
+ */
+
+static int hotkey_orig_status;
+static int hotkey_orig_mask;
+
+static struct attribute_set *hotkey_dev_attributes = NULL;
+
+/* sysfs hotkey enable ------------------------------------------------- */
+static ssize_t hotkey_enable_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int res, status, mask;
+
+ res = hotkey_get(&status, &mask);
+ if (res)
+ return res;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", status);
+}
+
+static ssize_t hotkey_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long t;
+ int res, status, mask;
+
+ if (parse_strtoul(buf, 1, &t))
+ return -EINVAL;
+
+ res = hotkey_get(&status, &mask);
+ if (!res)
+ res = hotkey_set(t, mask);
+
+ return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_hotkey_enable =
+ __ATTR(enable, S_IWUSR | S_IRUGO,
+ hotkey_enable_show, hotkey_enable_store);
+
+/* sysfs hotkey mask --------------------------------------------------- */
+static ssize_t hotkey_mask_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int res, status, mask;
+
+ res = hotkey_get(&status, &mask);
+ if (res)
+ return res;
+
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n", mask);
+}
+
+static ssize_t hotkey_mask_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long t;
+ int res, status, mask;
+
+ if (parse_strtoul(buf, 0xffff, &t))
+ return -EINVAL;
+
+ res = hotkey_get(&status, &mask);
+ if (!res)
+ hotkey_set(status, t);
+
+ return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_hotkey_mask =
+ __ATTR(mask, S_IWUSR | S_IRUGO,
+ hotkey_mask_show, hotkey_mask_store);
+
+/* sysfs hotkey bios_enabled ------------------------------------------- */
+static ssize_t hotkey_bios_enabled_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_orig_status);
+}
+
+static struct device_attribute dev_attr_hotkey_bios_enabled =
+ __ATTR(bios_enabled, S_IRUGO, hotkey_bios_enabled_show, NULL);
+
+/* sysfs hotkey bios_mask ---------------------------------------------- */
+static ssize_t hotkey_bios_mask_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n", hotkey_orig_mask);
+}
+
+static struct device_attribute dev_attr_hotkey_bios_mask =
+ __ATTR(bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL);
+
+/* --------------------------------------------------------------------- */
+
+static struct attribute *hotkey_mask_attributes[] = {
+ &dev_attr_hotkey_mask.attr,
+ &dev_attr_hotkey_bios_enabled.attr,
+ &dev_attr_hotkey_bios_mask.attr,
+};
+
+static int __init hotkey_init(struct ibm_init_struct *iibm)
+{
+ int res;
+
+ vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n");
+
+ IBM_ACPIHANDLE_INIT(hkey);
+ mutex_init(&hotkey_mutex);
+
+ /* hotkey not supported on 570 */
+ tp_features.hotkey = hkey_handle != NULL;
+
+ vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n",
+ str_supported(tp_features.hotkey));
+
+ if (tp_features.hotkey) {
+ hotkey_dev_attributes = create_attr_set(4,
+ TPACPI_HOTKEY_SYSFS_GROUP);
+ if (!hotkey_dev_attributes)
+ return -ENOMEM;
+ res = add_to_attr_set(hotkey_dev_attributes,
+ &dev_attr_hotkey_enable.attr);
+ if (res)
+ return res;
+
+ /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
+ A30, R30, R31, T20-22, X20-21, X22-24 */
+ tp_features.hotkey_mask =
+ acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
+
+ vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
+ str_supported(tp_features.hotkey_mask));
+
+ res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask);
+ if (!res && tp_features.hotkey_mask) {
+ res = add_many_to_attr_set(hotkey_dev_attributes,
+ hotkey_mask_attributes,
+ ARRAY_SIZE(hotkey_mask_attributes));
+ }
+ if (!res)
+ res = register_attr_set_with_sysfs(
+ hotkey_dev_attributes,
+ &tpacpi_pdev->dev.kobj);
+
+ if (res)
+ return res;
+ }
+
+ return (tp_features.hotkey)? 0 : 1;
+}
+
+static void hotkey_exit(void)
+{
+ int res;
+
+ if (tp_features.hotkey) {
+ dbg_printk(TPACPI_DBG_EXIT, "restoring original hotkey mask\n");
+ res = hotkey_set(hotkey_orig_status, hotkey_orig_mask);
+ if (res)
+ printk(IBM_ERR "failed to restore hotkey to BIOS defaults\n");
+ }
+
+ if (hotkey_dev_attributes) {
+ delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
+ hotkey_dev_attributes = NULL;
+ }
+}
+
+static void hotkey_notify(struct ibm_struct *ibm, u32 event)
+{
+ int hkey;
+
+ if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d"))
+ acpi_bus_generate_event(ibm->acpi->device, event, hkey);
+ else {
+ printk(IBM_ERR "unknown hotkey event %d\n", event);
+ acpi_bus_generate_event(ibm->acpi->device, event, 0);
+ }
+}
+
+/*
+ * Call with hotkey_mutex held
+ */
+static int hotkey_get(int *status, int *mask)
+{
+ if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
+ return -EIO;
+
+ if (tp_features.hotkey_mask)
+ if (!acpi_evalf(hkey_handle, mask, "DHKN", "d"))
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Call with hotkey_mutex held
+ */
+static int hotkey_set(int status, int mask)
+{
+ int i;
+
+ if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
+ return -EIO;
+
+ if (tp_features.hotkey_mask)
+ for (i = 0; i < 32; i++) {
+ int bit = ((1 << i) & mask) != 0;
+ if (!acpi_evalf(hkey_handle,
+ NULL, "MHKM", "vdd", i + 1, bit))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* procfs -------------------------------------------------------------- */
+static int hotkey_read(char *p)
+{
+ int res, status, mask;
+ int len = 0;
+
+ if (!tp_features.hotkey) {
+ len += sprintf(p + len, "status:\t\tnot supported\n");
+ return len;
+ }
+
+ res = mutex_lock_interruptible(&hotkey_mutex);
+ if (res < 0)
+ return res;
+ res = hotkey_get(&status, &mask);
+ mutex_unlock(&hotkey_mutex);
+ if (res)
+ return res;
+
+ len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
+ if (tp_features.hotkey_mask) {
+ len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
+ len += sprintf(p + len,
+ "commands:\tenable, disable, reset, <mask>\n");
+ } else {
+ len += sprintf(p + len, "mask:\t\tnot supported\n");
+ len += sprintf(p + len, "commands:\tenable, disable, reset\n");
+ }
+
+ return len;
+}
+
+static int hotkey_write(char *buf)
+{
+ int res, status, mask;
+ char *cmd;
+ int do_cmd = 0;
+
+ if (!tp_features.hotkey)
+ return -ENODEV;
+
+ res = mutex_lock_interruptible(&hotkey_mutex);
+ if (res < 0)
+ return res;
+
+ res = hotkey_get(&status, &mask);
+ if (res)
+ goto errexit;
+
+ res = 0;
+ while ((cmd = next_cmd(&buf))) {
+ if (strlencmp(cmd, "enable") == 0) {
+ status = 1;
+ } else if (strlencmp(cmd, "disable") == 0) {
+ status = 0;
+ } else if (strlencmp(cmd, "reset") == 0) {
+ status = hotkey_orig_status;
+ mask = hotkey_orig_mask;
+ } else if (sscanf(cmd, "0x%x", &mask) == 1) {
+ /* mask set */
+ } else if (sscanf(cmd, "%x", &mask) == 1) {
+ /* mask set */
+ } else {
+ res = -EINVAL;
+ goto errexit;
+ }
+ do_cmd = 1;
+ }
+
+ if (do_cmd)
+ res = hotkey_set(status, mask);
+
+errexit:
+ mutex_unlock(&hotkey_mutex);
+ return res;
+}
+
+static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = {
+ .hid = IBM_HKEY_HID,
+ .notify = hotkey_notify,
+ .handle = &hkey_handle,
+ .type = ACPI_DEVICE_NOTIFY,
+};
+
+static struct ibm_struct hotkey_driver_data = {
+ .name = "hotkey",
+ .read = hotkey_read,
+ .write = hotkey_write,
+ .exit = hotkey_exit,
+ .acpi = &ibm_hotkey_acpidriver,
+};
+
+/*************************************************************************
+ * Bluetooth subdriver
+ */
+
+/* sysfs bluetooth enable ---------------------------------------------- */
+static ssize_t bluetooth_enable_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int status;
+
+ status = bluetooth_get_radiosw();
+ if (status < 0)
+ return status;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
+}
+
+static ssize_t bluetooth_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long t;
+ int res;
+
+ if (parse_strtoul(buf, 1, &t))
+ return -EINVAL;
+
+ res = bluetooth_set_radiosw(t);
+
+ return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_bluetooth_enable =
+ __ATTR(enable, S_IWUSR | S_IRUGO,
+ bluetooth_enable_show, bluetooth_enable_store);
+
+/* --------------------------------------------------------------------- */
+
+static struct attribute *bluetooth_attributes[] = {
+ &dev_attr_bluetooth_enable.attr,
+ NULL
+};
+
+static const struct attribute_group bluetooth_attr_group = {
+ .name = TPACPI_BLUETH_SYSFS_GROUP,
+ .attrs = bluetooth_attributes,
+};
+
+static int __init bluetooth_init(struct ibm_init_struct *iibm)
+{
+ int res;
+ int status = 0;
+
+ vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n");
+
+ IBM_ACPIHANDLE_INIT(hkey);
+
+ /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
+ G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
+ tp_features.bluetooth = hkey_handle &&
+ acpi_evalf(hkey_handle, &status, "GBDC", "qd");
+
+ vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n",
+ str_supported(tp_features.bluetooth),
+ status);
+
+ if (tp_features.bluetooth) {
+ if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
+ /* no bluetooth hardware present in system */
+ tp_features.bluetooth = 0;
+ dbg_printk(TPACPI_DBG_INIT,
+ "bluetooth hardware not installed\n");
+ } else {
+ res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ &bluetooth_attr_group);
+ if (res)
+ return res;
+ }
+ }
+
+ return (tp_features.bluetooth)? 0 : 1;
+}
+
+static void bluetooth_exit(void)
+{
+ sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+ &bluetooth_attr_group);
+}
+
+static int bluetooth_get_radiosw(void)
+{
+ int status;
+
+ if (!tp_features.bluetooth)
+ return -ENODEV;
+
+ if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+ return -EIO;
+
+ return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0);
+}
+
+static int bluetooth_set_radiosw(int radio_on)
+{
+ int status;
+
+ if (!tp_features.bluetooth)
+ return -ENODEV;
+
+ if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+ return -EIO;
+ if (radio_on)
+ status |= TP_ACPI_BLUETOOTH_RADIOSSW;
+ else
+ status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
+ if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
+ return -EIO;
+
+ return 0;
+}
+
+/* procfs -------------------------------------------------------------- */
+static int bluetooth_read(char *p)
+{
+ int len = 0;
+ int status = bluetooth_get_radiosw();
+
+ if (!tp_features.bluetooth)
+ len += sprintf(p + len, "status:\t\tnot supported\n");
+ else {
+ len += sprintf(p + len, "status:\t\t%s\n",
+ (status)? "enabled" : "disabled");
+ len += sprintf(p + len, "commands:\tenable, disable\n");
+ }
+
+ return len;
+}
+
+static int bluetooth_write(char *buf)
+{
+ char *cmd;
+
+ if (!tp_features.bluetooth)
+ return -ENODEV;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (strlencmp(cmd, "enable") == 0) {
+ bluetooth_set_radiosw(1);
+ } else if (strlencmp(cmd, "disable") == 0) {
+ bluetooth_set_radiosw(0);
+ } else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct ibm_struct bluetooth_driver_data = {
+ .name = "bluetooth",
+ .read = bluetooth_read,
+ .write = bluetooth_write,
+ .exit = bluetooth_exit,
+};
+
+/*************************************************************************
+ * Wan subdriver
+ */
+
+/* sysfs wan enable ---------------------------------------------------- */
+static ssize_t wan_enable_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int status;
+
+ status = wan_get_radiosw();
+ if (status < 0)
+ return status;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
+}
+
+static ssize_t wan_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long t;
+ int res;
+
+ if (parse_strtoul(buf, 1, &t))
+ return -EINVAL;
+
+ res = wan_set_radiosw(t);
+
+ return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_wan_enable =
+ __ATTR(enable, S_IWUSR | S_IRUGO,
+ wan_enable_show, wan_enable_store);
+
+/* --------------------------------------------------------------------- */
+
+static struct attribute *wan_attributes[] = {
+ &dev_attr_wan_enable.attr,
+ NULL
+};
+
+static const struct attribute_group wan_attr_group = {
+ .name = TPACPI_WAN_SYSFS_GROUP,
+ .attrs = wan_attributes,
+};
+
+static int __init wan_init(struct ibm_init_struct *iibm)
+{
+ int res;
+ int status = 0;
+
+ vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n");
+
+ IBM_ACPIHANDLE_INIT(hkey);
+
+ tp_features.wan = hkey_handle &&
+ acpi_evalf(hkey_handle, &status, "GWAN", "qd");
+
+ vdbg_printk(TPACPI_DBG_INIT, "wan is %s, status 0x%02x\n",
+ str_supported(tp_features.wan),
+ status);
+
+ if (tp_features.wan) {
+ if (!(status & TP_ACPI_WANCARD_HWPRESENT)) {
+ /* no wan hardware present in system */
+ tp_features.wan = 0;
+ dbg_printk(TPACPI_DBG_INIT,
+ "wan hardware not installed\n");
+ } else {
+ res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ &wan_attr_group);
+ if (res)
+ return res;
+ }
+ }
+
+ return (tp_features.wan)? 0 : 1;
+}
+
+static void wan_exit(void)
+{
+ sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+ &wan_attr_group);
+}
+
+static int wan_get_radiosw(void)
+{
+ int status;
+
+ if (!tp_features.wan)
+ return -ENODEV;
+
+ if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
+ return -EIO;
+
+ return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0);
+}
+
+static int wan_set_radiosw(int radio_on)
+{
+ int status;
+
+ if (!tp_features.wan)
+ return -ENODEV;
+
+ if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
+ return -EIO;
+ if (radio_on)
+ status |= TP_ACPI_WANCARD_RADIOSSW;
+ else
+ status &= ~TP_ACPI_WANCARD_RADIOSSW;
+ if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
+ return -EIO;
+
+ return 0;
+}
+
+/* procfs -------------------------------------------------------------- */
+static int wan_read(char *p)
+{
+ int len = 0;
+ int status = wan_get_radiosw();
+
+ if (!tp_features.wan)
+ len += sprintf(p + len, "status:\t\tnot supported\n");
+ else {
+ len += sprintf(p + len, "status:\t\t%s\n",
+ (status)? "enabled" : "disabled");
+ len += sprintf(p + len, "commands:\tenable, disable\n");
+ }
+
+ return len;
+}
+
+static int wan_write(char *buf)
+{
+ char *cmd;
+
+ if (!tp_features.wan)
+ return -ENODEV;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (strlencmp(cmd, "enable") == 0) {
+ wan_set_radiosw(1);
+ } else if (strlencmp(cmd, "disable") == 0) {
+ wan_set_radiosw(0);
+ } else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct ibm_struct wan_driver_data = {
+ .name = "wan",
+ .read = wan_read,
+ .write = wan_write,
+ .exit = wan_exit,
+ .flags.experimental = 1,
+};
+
+/*************************************************************************
+ * Video subdriver
+ */
+
+static enum video_access_mode video_supported;
+static int video_orig_autosw;
+
+IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
+ "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
+ "\\_SB.PCI0.VID0", /* 770e */
+ "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
+ "\\_SB.PCI0.AGP.VID", /* all others */
+ ); /* R30, R31 */
+
+IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */
+
+static int __init video_init(struct ibm_init_struct *iibm)
+{
+ int ivga;
+
+ vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n");
+
+ IBM_ACPIHANDLE_INIT(vid);
+ IBM_ACPIHANDLE_INIT(vid2);
+
+ if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
+ /* G41, assume IVGA doesn't change */
+ vid_handle = vid2_handle;
+
+ if (!vid_handle)
+ /* video switching not supported on R30, R31 */
+ video_supported = TPACPI_VIDEO_NONE;
+ else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
+ /* 570 */
+ video_supported = TPACPI_VIDEO_570;
+ else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
+ /* 600e/x, 770e, 770x */
+ video_supported = TPACPI_VIDEO_770;
+ else
+ /* all others */
+ video_supported = TPACPI_VIDEO_NEW;
+
+ vdbg_printk(TPACPI_DBG_INIT, "video is %s, mode %d\n",
+ str_supported(video_supported != TPACPI_VIDEO_NONE),
+ video_supported);
+
+ return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1;
+}
+
+static void video_exit(void)
+{
+ dbg_printk(TPACPI_DBG_EXIT,
+ "restoring original video autoswitch mode\n");
+ if (video_autosw_set(video_orig_autosw))
+ printk(IBM_ERR "error while trying to restore original "
+ "video autoswitch mode\n");
+}
+
+static int video_outputsw_get(void)
+{
+ int status = 0;
+ int i;
+
+ switch (video_supported) {
+ case TPACPI_VIDEO_570:
+ if (!acpi_evalf(NULL, &i, "\\_SB.PHS", "dd",
+ TP_ACPI_VIDEO_570_PHSCMD))
+ return -EIO;
+ status = i & TP_ACPI_VIDEO_570_PHSMASK;
+ break;
+ case TPACPI_VIDEO_770:
+ if (!acpi_evalf(NULL, &i, "\\VCDL", "d"))
+ return -EIO;
+ if (i)
+ status |= TP_ACPI_VIDEO_S_LCD;
+ if (!acpi_evalf(NULL, &i, "\\VCDC", "d"))
+ return -EIO;
+ if (i)
+ status |= TP_ACPI_VIDEO_S_CRT;
+ break;
+ case TPACPI_VIDEO_NEW:
+ if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1) ||
+ !acpi_evalf(NULL, &i, "\\VCDC", "d"))
+ return -EIO;
+ if (i)
+ status |= TP_ACPI_VIDEO_S_CRT;
+
+ if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0) ||
+ !acpi_evalf(NULL, &i, "\\VCDL", "d"))
+ return -EIO;
+ if (i)
+ status |= TP_ACPI_VIDEO_S_LCD;
+ if (!acpi_evalf(NULL, &i, "\\VCDD", "d"))
+ return -EIO;
+ if (i)
+ status |= TP_ACPI_VIDEO_S_DVI;
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return status;
+}
+
+static int video_outputsw_set(int status)
+{
+ int autosw;
+ int res = 0;
+
+ switch (video_supported) {
+ case TPACPI_VIDEO_570:
+ res = acpi_evalf(NULL, NULL,
+ "\\_SB.PHS2", "vdd",
+ TP_ACPI_VIDEO_570_PHS2CMD,
+ status | TP_ACPI_VIDEO_570_PHS2SET);
+ break;
+ case TPACPI_VIDEO_770:
+ autosw = video_autosw_get();
+ if (autosw < 0)
+ return autosw;
+
+ res = video_autosw_set(1);
+ if (res)
+ return res;
+ res = acpi_evalf(vid_handle, NULL,
+ "ASWT", "vdd", status * 0x100, 0);
+ if (!autosw && video_autosw_set(autosw)) {
+ printk(IBM_ERR "video auto-switch left enabled due to error\n");
+ return -EIO;
+ }
+ break;
+ case TPACPI_VIDEO_NEW:
+ res = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
+ acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return (res)? 0 : -EIO;
+}
+
+static int video_autosw_get(void)
+{
+ int autosw = 0;
+
+ switch (video_supported) {
+ case TPACPI_VIDEO_570:
+ if (!acpi_evalf(vid_handle, &autosw, "SWIT", "d"))
+ return -EIO;
+ break;
+ case TPACPI_VIDEO_770:
+ case TPACPI_VIDEO_NEW:
+ if (!acpi_evalf(vid_handle, &autosw, "^VDEE", "d"))
+ return -EIO;
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return autosw & 1;
+}
+
+static int video_autosw_set(int enable)
+{
+ if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable)? 1 : 0))
+ return -EIO;
+ return 0;
+}
+
+static int video_outputsw_cycle(void)
+{
+ int autosw = video_autosw_get();
+ int res;
+
+ if (autosw < 0)
+ return autosw;
+
+ switch (video_supported) {
+ case TPACPI_VIDEO_570:
+ res = video_autosw_set(1);
+ if (res)
+ return res;
+ res = acpi_evalf(ec_handle, NULL, "_Q16", "v");
+ break;
+ case TPACPI_VIDEO_770:
+ case TPACPI_VIDEO_NEW:
+ res = video_autosw_set(1);
+ if (res)
+ return res;
+ res = acpi_evalf(vid_handle, NULL, "VSWT", "v");
+ break;
+ default:
+ return -ENOSYS;
+ }
+ if (!autosw && video_autosw_set(autosw)) {
+ printk(IBM_ERR "video auto-switch left enabled due to error\n");
+ return -EIO;
+ }
+
+ return (res)? 0 : -EIO;
+}
+
+static int video_expand_toggle(void)
+{
+ switch (video_supported) {
+ case TPACPI_VIDEO_570:
+ return acpi_evalf(ec_handle, NULL, "_Q17", "v")?
+ 0 : -EIO;
+ case TPACPI_VIDEO_770:
+ return acpi_evalf(vid_handle, NULL, "VEXP", "v")?
+ 0 : -EIO;
+ case TPACPI_VIDEO_NEW:
+ return acpi_evalf(NULL, NULL, "\\VEXP", "v")?
+ 0 : -EIO;
+ default:
+ return -ENOSYS;
+ }
+ /* not reached */
+}
+
+static int video_read(char *p)
+{
+ int status, autosw;
+ int len = 0;
+
+ if (video_supported == TPACPI_VIDEO_NONE) {
+ len += sprintf(p + len, "status:\t\tnot supported\n");
+ return len;
+ }
+
+ status = video_outputsw_get();
+ if (status < 0)
+ return status;
+
+ autosw = video_autosw_get();
+ if (autosw < 0)
+ return autosw;
+
+ len += sprintf(p + len, "status:\t\tsupported\n");
+ len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
+ len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
+ if (video_supported == TPACPI_VIDEO_NEW)
+ len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
+ len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
+ len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
+ len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
+ if (video_supported == TPACPI_VIDEO_NEW)
+ len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
+ len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
+ len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
+
+ return len;
+}
+
+static int video_write(char *buf)
+{
+ char *cmd;
+ int enable, disable, status;
+ int res;
+
+ if (video_supported == TPACPI_VIDEO_NONE)
+ return -ENODEV;
+
+ enable = 0;
+ disable = 0;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (strlencmp(cmd, "lcd_enable") == 0) {
+ enable |= TP_ACPI_VIDEO_S_LCD;
+ } else if (strlencmp(cmd, "lcd_disable") == 0) {
+ disable |= TP_ACPI_VIDEO_S_LCD;
+ } else if (strlencmp(cmd, "crt_enable") == 0) {
+ enable |= TP_ACPI_VIDEO_S_CRT;
+ } else if (strlencmp(cmd, "crt_disable") == 0) {
+ disable |= TP_ACPI_VIDEO_S_CRT;
+ } else if (video_supported == TPACPI_VIDEO_NEW &&
+ strlencmp(cmd, "dvi_enable") == 0) {
+ enable |= TP_ACPI_VIDEO_S_DVI;
+ } else if (video_supported == TPACPI_VIDEO_NEW &&
+ strlencmp(cmd, "dvi_disable") == 0) {
+ disable |= TP_ACPI_VIDEO_S_DVI;
+ } else if (strlencmp(cmd, "auto_enable") == 0) {
+ res = video_autosw_set(1);
+ if (res)
+ return res;
+ } else if (strlencmp(cmd, "auto_disable") == 0) {
+ res = video_autosw_set(0);
+ if (res)
+ return res;
+ } else if (strlencmp(cmd, "video_switch") == 0) {
+ res = video_outputsw_cycle();
+ if (res)
+ return res;
+ } else if (strlencmp(cmd, "expand_toggle") == 0) {
+ res = video_expand_toggle();
+ if (res)
+ return res;
+ } else
+ return -EINVAL;
+ }
+
+ if (enable || disable) {
+ status = video_outputsw_get();
+ if (status < 0)
+ return status;
+ res = video_outputsw_set((status & ~disable) | enable);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static struct ibm_struct video_driver_data = {
+ .name = "video",
+ .read = video_read,
+ .write = video_write,
+ .exit = video_exit,
+};
+
+/*************************************************************************
+ * Light (thinklight) subdriver
+ */
+
+IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
+IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */
+
+static int __init light_init(struct ibm_init_struct *iibm)
+{
+ vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
+
+ IBM_ACPIHANDLE_INIT(ledb);
+ IBM_ACPIHANDLE_INIT(lght);
+ IBM_ACPIHANDLE_INIT(cmos);
+
+ /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
+ tp_features.light = (cmos_handle || lght_handle) && !ledb_handle;
+
+ if (tp_features.light)
+ /* light status not supported on
+ 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
+ tp_features.light_status =
+ acpi_evalf(ec_handle, NULL, "KBLT", "qv");
+
+ vdbg_printk(TPACPI_DBG_INIT, "light is %s\n",
+ str_supported(tp_features.light));
+
+ return (tp_features.light)? 0 : 1;
+}
+
+static int light_read(char *p)
+{
+ int len = 0;
+ int status = 0;
+
+ if (!tp_features.light) {
+ len += sprintf(p + len, "status:\t\tnot supported\n");
+ } else if (!tp_features.light_status) {
+ len += sprintf(p + len, "status:\t\tunknown\n");
+ len += sprintf(p + len, "commands:\ton, off\n");
+ } else {
+ if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
+ return -EIO;
+ len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
+ len += sprintf(p + len, "commands:\ton, off\n");
+ }
+
+ return len;
+}
+
+static int light_write(char *buf)
+{
+ int cmos_cmd, lght_cmd;
+ char *cmd;
+ int success;
+
+ if (!tp_features.light)
+ return -ENODEV;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (strlencmp(cmd, "on") == 0) {
+ cmos_cmd = 0x0c;
+ lght_cmd = 1;
+ } else if (strlencmp(cmd, "off") == 0) {
+ cmos_cmd = 0x0d;
+ lght_cmd = 0;
+ } else
+ return -EINVAL;
+
+ success = cmos_handle ?
+ acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
+ acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
+ if (!success)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static struct ibm_struct light_driver_data = {
+ .name = "light",
+ .read = light_read,
+ .write = light_write,
+};
+
+/*************************************************************************
+ * Dock subdriver
+ */
+
+#ifdef CONFIG_THINKPAD_ACPI_DOCK
+
+IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
+ "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
+ "\\_SB.PCI0.PCI1.DOCK", /* all others */
+ "\\_SB.PCI.ISA.SLCE", /* 570 */
+ ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
+
+/* don't list other alternatives as we install a notify handler on the 570 */
+IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
+
+static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
+ {
+ .notify = dock_notify,
+ .handle = &dock_handle,
+ .type = ACPI_SYSTEM_NOTIFY,
+ },
+ {
+ .hid = IBM_PCI_HID,
+ .notify = dock_notify,
+ .handle = &pci_handle,
+ .type = ACPI_SYSTEM_NOTIFY,
+ },
+};
+
+static struct ibm_struct dock_driver_data[2] = {
+ {
+ .name = "dock",
+ .read = dock_read,
+ .write = dock_write,
+ .acpi = &ibm_dock_acpidriver[0],
+ },
+ {
+ .name = "dock",
+ .acpi = &ibm_dock_acpidriver[1],
+ },
+};
+
+#define dock_docked() (_sta(dock_handle) & 1)
+
+static int __init dock_init(struct ibm_init_struct *iibm)
+{
+ vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n");
+
+ IBM_ACPIHANDLE_INIT(dock);
+
+ vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n",
+ str_supported(dock_handle != NULL));
+
+ return (dock_handle)? 0 : 1;
+}
+
+static int __init dock_init2(struct ibm_init_struct *iibm)
+{
+ int dock2_needed;
+
+ vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver part 2\n");
+
+ if (dock_driver_data[0].flags.acpi_driver_registered &&
+ dock_driver_data[0].flags.acpi_notify_installed) {
+ IBM_ACPIHANDLE_INIT(pci);
+ dock2_needed = (pci_handle != NULL);
+ vdbg_printk(TPACPI_DBG_INIT,
+ "dock PCI handler for the TP 570 is %s\n",
+ str_supported(dock2_needed));
+ } else {
+ vdbg_printk(TPACPI_DBG_INIT,
+ "dock subdriver part 2 not required\n");
+ dock2_needed = 0;
+ }
+
+ return (dock2_needed)? 0 : 1;
+}
+
+static void dock_notify(struct ibm_struct *ibm, u32 event)
+{
+ int docked = dock_docked();
+ int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, IBM_PCI_HID);
+
+ if (event == 1 && !pci) /* 570 */
+ acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */
+ else if (event == 1 && pci) /* 570 */
+ acpi_bus_generate_event(ibm->acpi->device, event, 3); /* dock */
+ else if (event == 3 && docked)
+ acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */
+ else if (event == 3 && !docked)
+ acpi_bus_generate_event(ibm->acpi->device, event, 2); /* undock */
+ else if (event == 0 && docked)
+ acpi_bus_generate_event(ibm->acpi->device, event, 3); /* dock */
+ else {
+ printk(IBM_ERR "unknown dock event %d, status %d\n",
+ event, _sta(dock_handle));
+ acpi_bus_generate_event(ibm->acpi->device, event, 0); /* unknown */
+ }
+}
+
+static int dock_read(char *p)
+{
+ int len = 0;
+ int docked = dock_docked();
+
+ if (!dock_handle)
+ len += sprintf(p + len, "status:\t\tnot supported\n");
+ else if (!docked)
+ len += sprintf(p + len, "status:\t\tundocked\n");
+ else {
+ len += sprintf(p + len, "status:\t\tdocked\n");
+ len += sprintf(p + len, "commands:\tdock, undock\n");
+ }
+
+ return len;
+}
+
+static int dock_write(char *buf)
+{
+ char *cmd;
+
+ if (!dock_docked())
+ return -ENODEV;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (strlencmp(cmd, "undock") == 0) {
+ if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
+ !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
+ return -EIO;
+ } else if (strlencmp(cmd, "dock") == 0) {
+ if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
+ return -EIO;
+ } else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_THINKPAD_ACPI_DOCK */
+
+/*************************************************************************
+ * Bay subdriver
+ */
+
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */
+ "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
+ "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */
+ "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
+ ); /* A21e, R30, R31 */
+IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */
+ "_EJ0", /* all others */
+ ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
+IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
+ "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
+ ); /* all others */
+IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */
+ "_EJ0", /* 770x */
+ ); /* all others */
+
+static int __init bay_init(struct ibm_init_struct *iibm)
+{
+ vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n");
+
+ IBM_ACPIHANDLE_INIT(bay);
+ if (bay_handle)
+ IBM_ACPIHANDLE_INIT(bay_ej);
+ IBM_ACPIHANDLE_INIT(bay2);
+ if (bay2_handle)
+ IBM_ACPIHANDLE_INIT(bay2_ej);
+
+ tp_features.bay_status = bay_handle &&
+ acpi_evalf(bay_handle, NULL, "_STA", "qv");
+ tp_features.bay_status2 = bay2_handle &&
+ acpi_evalf(bay2_handle, NULL, "_STA", "qv");
+
+ tp_features.bay_eject = bay_handle && bay_ej_handle &&
+ (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
+ tp_features.bay_eject2 = bay2_handle && bay2_ej_handle &&
+ (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
+
+ vdbg_printk(TPACPI_DBG_INIT,
+ "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n",
+ str_supported(tp_features.bay_status),
+ str_supported(tp_features.bay_eject),
+ str_supported(tp_features.bay_status2),
+ str_supported(tp_features.bay_eject2));
+
+ return (tp_features.bay_status || tp_features.bay_eject ||
+ tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1;
+}
+
+static void bay_notify(struct ibm_struct *ibm, u32 event)
+{
+ acpi_bus_generate_event(ibm->acpi->device, event, 0);
+}
+
+#define bay_occupied(b) (_sta(b##_handle) & 1)
+
+static int bay_read(char *p)
+{
+ int len = 0;
+ int occupied = bay_occupied(bay);
+ int occupied2 = bay_occupied(bay2);
+ int eject, eject2;
+
+ len += sprintf(p + len, "status:\t\t%s\n",
+ tp_features.bay_status ?
+ (occupied ? "occupied" : "unoccupied") :
+ "not supported");
+ if (tp_features.bay_status2)
+ len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
+ "occupied" : "unoccupied");
+
+ eject = tp_features.bay_eject && occupied;
+ eject2 = tp_features.bay_eject2 && occupied2;
+
+ if (eject && eject2)
+ len += sprintf(p + len, "commands:\teject, eject2\n");
+ else if (eject)
+ len += sprintf(p + len, "commands:\teject\n");
+ else if (eject2)
+ len += sprintf(p + len, "commands:\teject2\n");
+
+ return len;
+}
+
+static int bay_write(char *buf)
+{
+ char *cmd;
+
+ if (!tp_features.bay_eject && !tp_features.bay_eject2)
+ return -ENODEV;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) {
+ if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
+ return -EIO;
+ } else if (tp_features.bay_eject2 &&
+ strlencmp(cmd, "eject2") == 0) {
+ if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
+ return -EIO;
+ } else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct tp_acpi_drv_struct ibm_bay_acpidriver = {
+ .notify = bay_notify,
+ .handle = &bay_handle,
+ .type = ACPI_SYSTEM_NOTIFY,
+};
+
+static struct ibm_struct bay_driver_data = {
+ .name = "bay",
+ .read = bay_read,
+ .write = bay_write,
+ .acpi = &ibm_bay_acpidriver,
+};
+
+#endif /* CONFIG_THINKPAD_ACPI_BAY */
+
+/*************************************************************************
+ * CMOS subdriver
+ */
+
+/* sysfs cmos_command -------------------------------------------------- */
+static ssize_t cmos_command_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long cmos_cmd;
+ int res;
+
+ if (parse_strtoul(buf, 21, &cmos_cmd))
+ return -EINVAL;
+
+ res = issue_thinkpad_cmos_command(cmos_cmd);
+ return (res)? res : count;
+}
+
+static struct device_attribute dev_attr_cmos_command =
+ __ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store);
+
+/* --------------------------------------------------------------------- */
+
+static int __init cmos_init(struct ibm_init_struct *iibm)
+{
+ int res;
+
+ vdbg_printk(TPACPI_DBG_INIT,
+ "initializing cmos commands subdriver\n");
+
+ IBM_ACPIHANDLE_INIT(cmos);
+
+ vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n",
+ str_supported(cmos_handle != NULL));
+
+ res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
+ if (res)
+ return res;
+
+ return (cmos_handle)? 0 : 1;
+}
+
+static void cmos_exit(void)
+{
+ device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
+}
+
+static int cmos_read(char *p)
+{
+ int len = 0;
+
+ /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
+ R30, R31, T20-22, X20-21 */
+ if (!cmos_handle)
+ len += sprintf(p + len, "status:\t\tnot supported\n");
+ else {
+ len += sprintf(p + len, "status:\t\tsupported\n");
+ len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
+ }
+
+ return len;
+}
+
+static int cmos_write(char *buf)
+{
+ char *cmd;
+ int cmos_cmd, res;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
+ cmos_cmd >= 0 && cmos_cmd <= 21) {
+ /* cmos_cmd set */
+ } else
+ return -EINVAL;
+
+ res = issue_thinkpad_cmos_command(cmos_cmd);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static struct ibm_struct cmos_driver_data = {
+ .name = "cmos",
+ .read = cmos_read,
+ .write = cmos_write,
+ .exit = cmos_exit,
+};
+
+/*************************************************************************
+ * LED subdriver
+ */
+
+static enum led_access_mode led_supported;
+
+IBM_HANDLE(led, ec, "SLED", /* 570 */
+ "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
+ "LED", /* all others */
+ ); /* R30, R31 */
+
+static int __init led_init(struct ibm_init_struct *iibm)
+{
+ vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
+
+ IBM_ACPIHANDLE_INIT(led);
+
+ if (!led_handle)
+ /* led not supported on R30, R31 */
+ led_supported = TPACPI_LED_NONE;
+ else if (strlencmp(led_path, "SLED") == 0)
+ /* 570 */
+ led_supported = TPACPI_LED_570;
+ else if (strlencmp(led_path, "SYSL") == 0)
+ /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
+ led_supported = TPACPI_LED_OLD;
+ else
+ /* all others */
+ led_supported = TPACPI_LED_NEW;
+
+ vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
+ str_supported(led_supported), led_supported);
+
+ return (led_supported != TPACPI_LED_NONE)? 0 : 1;
+}
+
+#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking"))
+
+static int led_read(char *p)
+{
+ int len = 0;
+
+ if (!led_supported) {
+ len += sprintf(p + len, "status:\t\tnot supported\n");
+ return len;
+ }
+ len += sprintf(p + len, "status:\t\tsupported\n");
+
+ if (led_supported == TPACPI_LED_570) {
+ /* 570 */
+ int i, status;
+ for (i = 0; i < 8; i++) {
+ if (!acpi_evalf(ec_handle,
+ &status, "GLED", "dd", 1 << i))
+ return -EIO;
+ len += sprintf(p + len, "%d:\t\t%s\n",
+ i, led_status(status));
+ }
+ }
+
+ len += sprintf(p + len, "commands:\t"
+ "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
+
+ return len;
+}
+
+/* off, on, blink */
+static const int led_sled_arg1[] = { 0, 1, 3 };
+static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
+static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
+static const int led_led_arg1[] = { 0, 0x80, 0xc0 };
+
+static int led_write(char *buf)
+{
+ char *cmd;
+ int led, ind, ret;
+
+ if (!led_supported)
+ return -ENODEV;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
+ return -EINVAL;
+
+ if (strstr(cmd, "off")) {
+ ind = 0;
+ } else if (strstr(cmd, "on")) {
+ ind = 1;
+ } else if (strstr(cmd, "blink")) {
+ ind = 2;
+ } else
+ return -EINVAL;
+
+ if (led_supported == TPACPI_LED_570) {
+ /* 570 */
+ led = 1 << led;
+ if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
+ led, led_sled_arg1[ind]))
+ return -EIO;
+ } else if (led_supported == TPACPI_LED_OLD) {
+ /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
+ led = 1 << led;
+ ret = ec_write(TPACPI_LED_EC_HLMS, led);
+ if (ret >= 0)
+ ret =
+ ec_write(TPACPI_LED_EC_HLBL,
+ led * led_exp_hlbl[ind]);
+ if (ret >= 0)
+ ret =
+ ec_write(TPACPI_LED_EC_HLCL,
+ led * led_exp_hlcl[ind]);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* all others */
+ if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
+ led, led_led_arg1[ind]))
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static struct ibm_struct led_driver_data = {
+ .name = "led",
+ .read = led_read,
+ .write = led_write,
+};
+
+/*************************************************************************
+ * Beep subdriver
+ */
+
+IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
+
+static int __init beep_init(struct ibm_init_struct *iibm)
+{
+ vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n");
+
+ IBM_ACPIHANDLE_INIT(beep);
+
+ vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n",
+ str_supported(beep_handle != NULL));
+
+ return (beep_handle)? 0 : 1;
+}
+
+static int beep_read(char *p)
+{
+ int len = 0;
+
+ if (!beep_handle)
+ len += sprintf(p + len, "status:\t\tnot supported\n");
+ else {
+ len += sprintf(p + len, "status:\t\tsupported\n");
+ len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
+ }
+
+ return len;
+}
+
+static int beep_write(char *buf)
+{
+ char *cmd;
+ int beep_cmd;
+
+ if (!beep_handle)
+ return -ENODEV;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
+ beep_cmd >= 0 && beep_cmd <= 17) {
+ /* beep_cmd set */
+ } else
+ return -EINVAL;
+ if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static struct ibm_struct beep_driver_data = {
+ .name = "beep",
+ .read = beep_read,
+ .write = beep_write,
+};
+
+/*************************************************************************
+ * Thermal subdriver
+ */
+
+static enum thermal_access_mode thermal_read_mode;
+
+/* sysfs temp##_input -------------------------------------------------- */
+
+static ssize_t thermal_temp_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr =
+ to_sensor_dev_attr(attr);
+ int idx = sensor_attr->index;
+ s32 value;
+ int res;
+
+ res = thermal_get_sensor(idx, &value);
+ if (res)
+ return res;
+ if (value == TP_EC_THERMAL_TMP_NA * 1000)
+ return -ENXIO;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \
+ SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, thermal_temp_input_show, NULL, _idxB)
+
+static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = {
+ THERMAL_SENSOR_ATTR_TEMP(1, 0),
+ THERMAL_SENSOR_ATTR_TEMP(2, 1),
+ THERMAL_SENSOR_ATTR_TEMP(3, 2),
+ THERMAL_SENSOR_ATTR_TEMP(4, 3),
+ THERMAL_SENSOR_ATTR_TEMP(5, 4),
+ THERMAL_SENSOR_ATTR_TEMP(6, 5),
+ THERMAL_SENSOR_ATTR_TEMP(7, 6),
+ THERMAL_SENSOR_ATTR_TEMP(8, 7),
+ THERMAL_SENSOR_ATTR_TEMP(9, 8),
+ THERMAL_SENSOR_ATTR_TEMP(10, 9),
+ THERMAL_SENSOR_ATTR_TEMP(11, 10),
+ THERMAL_SENSOR_ATTR_TEMP(12, 11),
+ THERMAL_SENSOR_ATTR_TEMP(13, 12),
+ THERMAL_SENSOR_ATTR_TEMP(14, 13),
+ THERMAL_SENSOR_ATTR_TEMP(15, 14),
+ THERMAL_SENSOR_ATTR_TEMP(16, 15),
+};
+
+#define THERMAL_ATTRS(X) \
+ &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr
+
+static struct attribute *thermal_temp_input_attr[] = {
+ THERMAL_ATTRS(8),
+ THERMAL_ATTRS(9),
+ THERMAL_ATTRS(10),
+ THERMAL_ATTRS(11),
+ THERMAL_ATTRS(12),
+ THERMAL_ATTRS(13),
+ THERMAL_ATTRS(14),
+ THERMAL_ATTRS(15),
+ THERMAL_ATTRS(0),
+ THERMAL_ATTRS(1),
+ THERMAL_ATTRS(2),
+ THERMAL_ATTRS(3),
+ THERMAL_ATTRS(4),
+ THERMAL_ATTRS(5),
+ THERMAL_ATTRS(6),
+ THERMAL_ATTRS(7),
+ NULL
+};
+
+static const struct attribute_group thermal_temp_input16_group = {
+ .attrs = thermal_temp_input_attr
+};
+
+static const struct attribute_group thermal_temp_input8_group = {
+ .attrs = &thermal_temp_input_attr[8]
+};
+
+#undef THERMAL_SENSOR_ATTR_TEMP
+#undef THERMAL_ATTRS
+
+/* --------------------------------------------------------------------- */
+
+static int __init thermal_init(struct ibm_init_struct *iibm)
+{
+ u8 t, ta1, ta2;
+ int i;
+ int acpi_tmp7;
+ int res;
+
+ vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
+
+ acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
+
+ if (ibm_thinkpad_ec_found && experimental) {
+ /*
+ * Direct EC access mode: sensors at registers
+ * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
+ * non-implemented, thermal sensors return 0x80 when
+ * not available
+ */
+
+ ta1 = ta2 = 0;
+ for (i = 0; i < 8; i++) {
+ if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
+ ta1 |= t;
+ } else {
+ ta1 = 0;
+ break;
+ }
+ if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
+ ta2 |= t;
+ } else {
+ ta1 = 0;
+ break;
+ }
+ }
+ if (ta1 == 0) {
+ /* This is sheer paranoia, but we handle it anyway */
+ if (acpi_tmp7) {
+ printk(IBM_ERR
+ "ThinkPad ACPI EC access misbehaving, "
+ "falling back to ACPI TMPx access mode\n");
+ thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
+ } else {
+ printk(IBM_ERR
+ "ThinkPad ACPI EC access misbehaving, "
+ "disabling thermal sensors access\n");
+ thermal_read_mode = TPACPI_THERMAL_NONE;
+ }
+ } else {
+ thermal_read_mode =
+ (ta2 != 0) ?
+ TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
+ }
+ } else if (acpi_tmp7) {
+ if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
+ /* 600e/x, 770e, 770x */
+ thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
+ } else {
+ /* Standard ACPI TMPx access, max 8 sensors */
+ thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
+ }
+ } else {
+ /* temperatures not supported on 570, G4x, R30, R31, R32 */
+ thermal_read_mode = TPACPI_THERMAL_NONE;
+ }
+
+ vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n",
+ str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
+ thermal_read_mode);
+
+ switch(thermal_read_mode) {
+ case TPACPI_THERMAL_TPEC_16:
+ res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ &thermal_temp_input16_group);
+ if (res)
+ return res;
+ break;
+ case TPACPI_THERMAL_TPEC_8:
+ case TPACPI_THERMAL_ACPI_TMP07:
+ case TPACPI_THERMAL_ACPI_UPDT:
+ res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ &thermal_temp_input8_group);
+ if (res)
+ return res;
+ break;
+ case TPACPI_THERMAL_NONE:
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+static void thermal_exit(void)
+{
+ switch(thermal_read_mode) {
+ case TPACPI_THERMAL_TPEC_16:
+ sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+ &thermal_temp_input16_group);
+ break;
+ case TPACPI_THERMAL_TPEC_8:
+ case TPACPI_THERMAL_ACPI_TMP07:
+ case TPACPI_THERMAL_ACPI_UPDT:
+ sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+ &thermal_temp_input16_group);
+ break;
+ case TPACPI_THERMAL_NONE:
+ default:
+ break;
+ }
+}
+
+/* idx is zero-based */
+static int thermal_get_sensor(int idx, s32 *value)
+{
+ int t;
+ s8 tmp;
+ char tmpi[5];
+
+ t = TP_EC_THERMAL_TMP0;
+
+ switch (thermal_read_mode) {
+#if TPACPI_MAX_THERMAL_SENSORS >= 16
+ case TPACPI_THERMAL_TPEC_16:
+ if (idx >= 8 && idx <= 15) {
+ t = TP_EC_THERMAL_TMP8;
+ idx -= 8;
+ }
+ /* fallthrough */
+#endif
+ case TPACPI_THERMAL_TPEC_8:
+ if (idx <= 7) {
+ if (!acpi_ec_read(t + idx, &tmp))
+ return -EIO;
+ *value = tmp * 1000;
+ return 0;
+ }
+ break;
+
+ case TPACPI_THERMAL_ACPI_UPDT:
+ if (idx <= 7) {
+ snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
+ if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
+ return -EIO;
+ if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
+ return -EIO;
+ *value = (t - 2732) * 100;
+ return 0;
+ }
+ break;
+
+ case TPACPI_THERMAL_ACPI_TMP07:
+ if (idx <= 7) {
+ snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
+ if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
+ return -EIO;
+ *value = t * 1000;
+ return 0;
+ }
+ break;
+
+ case TPACPI_THERMAL_NONE:
+ default:
+ return -ENOSYS;
+ }
+
+ return -EINVAL;
+}
+
+static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
+{
+ int res, i;
+ int n;
+
+ n = 8;
+ i = 0;
+
+ if (!s)
+ return -EINVAL;
+
+ if (thermal_read_mode == TPACPI_THERMAL_TPEC_16)
+ n = 16;
+
+ for(i = 0 ; i < n; i++) {
+ res = thermal_get_sensor(i, &s->temp[i]);
+ if (res)
+ return res;
+ }
+
+ return n;
+}
+
+static int thermal_read(char *p)
+{
+ int len = 0;
+ int n, i;
+ struct ibm_thermal_sensors_struct t;
+
+ n = thermal_get_sensors(&t);
+ if (unlikely(n < 0))
+ return n;
+
+ len += sprintf(p + len, "temperatures:\t");
+
+ if (n > 0) {
+ for (i = 0; i < (n - 1); i++)
+ len += sprintf(p + len, "%d ", t.temp[i] / 1000);
+ len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
+ } else
+ len += sprintf(p + len, "not supported\n");
+
+ return len;
+}
+
+static struct ibm_struct thermal_driver_data = {
+ .name = "thermal",
+ .read = thermal_read,
+ .exit = thermal_exit,
+};
+
+/*************************************************************************
+ * EC Dump subdriver
+ */
+
+static u8 ecdump_regs[256];
+
+static int ecdump_read(char *p)
+{
+ int len = 0;
+ int i, j;
+ u8 v;
+
+ len += sprintf(p + len, "EC "
+ " +00 +01 +02 +03 +04 +05 +06 +07"
+ " +08 +09 +0a +0b +0c +0d +0e +0f\n");
+ for (i = 0; i < 256; i += 16) {
+ len += sprintf(p + len, "EC 0x%02x:", i);
+ for (j = 0; j < 16; j++) {
+ if (!acpi_ec_read(i + j, &v))
+ break;
+ if (v != ecdump_regs[i + j])
+ len += sprintf(p + len, " *%02x", v);
+ else
+ len += sprintf(p + len, " %02x", v);
+ ecdump_regs[i + j] = v;
+ }
+ len += sprintf(p + len, "\n");
+ if (j != 16)
+ break;
+ }
+
+ /* These are way too dangerous to advertise openly... */
+#if 0
+ len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
+ " (<offset> is 00-ff, <value> is 00-ff)\n");
+ len += sprintf(p + len, "commands:\t0x<offset> <value> "
+ " (<offset> is 00-ff, <value> is 0-255)\n");
+#endif
+ return len;
+}
+
+static int ecdump_write(char *buf)
+{
+ char *cmd;
+ int i, v;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
+ /* i and v set */
+ } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
+ /* i and v set */
+ } else
+ return -EINVAL;
+ if (i >= 0 && i < 256 && v >= 0 && v < 256) {
+ if (!acpi_ec_write(i, v))
+ return -EIO;
+ } else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct ibm_struct ecdump_driver_data = {
+ .name = "ecdump",
+ .read = ecdump_read,
+ .write = ecdump_write,
+ .flags.experimental = 1,
+};
+
+/*************************************************************************
+ * Backlight/brightness subdriver
+ */
+
+static struct backlight_device *ibm_backlight_device = NULL;
+
+static struct backlight_ops ibm_backlight_data = {
+ .get_brightness = brightness_get,
+ .update_status = brightness_update_status,
+};
+
+static int __init brightness_init(struct ibm_init_struct *iibm)
+{
+ int b;
+
+ vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
+
+ b = brightness_get(NULL);
+ if (b < 0)
+ return b;
+
+ ibm_backlight_device = backlight_device_register(
+ TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
+ &ibm_backlight_data);
+ if (IS_ERR(ibm_backlight_device)) {
+ printk(IBM_ERR "Could not register backlight device\n");
+ return PTR_ERR(ibm_backlight_device);
+ }
+ vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n");
+
+ ibm_backlight_device->props.max_brightness = 7;
+ ibm_backlight_device->props.brightness = b;
+ backlight_update_status(ibm_backlight_device);
+
+ return 0;
+}
+
+static void brightness_exit(void)
+{
+ if (ibm_backlight_device) {
+ vdbg_printk(TPACPI_DBG_EXIT,
+ "calling backlight_device_unregister()\n");
+ backlight_device_unregister(ibm_backlight_device);
+ ibm_backlight_device = NULL;
+ }
+}
+
+static int brightness_update_status(struct backlight_device *bd)
+{
+ return brightness_set(
+ (bd->props.fb_blank == FB_BLANK_UNBLANK &&
+ bd->props.power == FB_BLANK_UNBLANK) ?
+ bd->props.brightness : 0);
+}
+
+static int brightness_get(struct backlight_device *bd)
+{
+ u8 level;
+ if (!acpi_ec_read(brightness_offset, &level))
+ return -EIO;
+
+ level &= 0x7;
+
+ return level;
+}
+
+static int brightness_set(int value)
+{
+ int cmos_cmd, inc, i;
+ int current_value = brightness_get(NULL);
+
+ value &= 7;
+
+ cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN;
+ inc = value > current_value ? 1 : -1;
+ for (i = current_value; i != value; i += inc) {
+ if (issue_thinkpad_cmos_command(cmos_cmd))
+ return -EIO;
+ if (!acpi_ec_write(brightness_offset, i + inc))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int brightness_read(char *p)
+{
+ int len = 0;
+ int level;
+
+ if ((level = brightness_get(NULL)) < 0) {
+ len += sprintf(p + len, "level:\t\tunreadable\n");
+ } else {
+ len += sprintf(p + len, "level:\t\t%d\n", level & 0x7);
+ len += sprintf(p + len, "commands:\tup, down\n");
+ len += sprintf(p + len, "commands:\tlevel <level>"
+ " (<level> is 0-7)\n");
+ }
+
+ return len;
+}
+
+static int brightness_write(char *buf)
+{
+ int level;
+ int new_level;
+ char *cmd;
+
+ while ((cmd = next_cmd(&buf))) {
+ if ((level = brightness_get(NULL)) < 0)
+ return level;
+ level &= 7;
+
+ if (strlencmp(cmd, "up") == 0) {
+ new_level = level == 7 ? 7 : level + 1;
+ } else if (strlencmp(cmd, "down") == 0) {
+ new_level = level == 0 ? 0 : level - 1;
+ } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
+ new_level >= 0 && new_level <= 7) {
+ /* new_level set */
+ } else
+ return -EINVAL;
+
+ brightness_set(new_level);
+ }
+
+ return 0;
+}
+
+static struct ibm_struct brightness_driver_data = {
+ .name = "brightness",
+ .read = brightness_read,
+ .write = brightness_write,
+ .exit = brightness_exit,
+};
+
+/*************************************************************************
+ * Volume subdriver
+ */
+
+static int volume_read(char *p)
+{
+ int len = 0;
+ u8 level;
+
+ if (!acpi_ec_read(volume_offset, &level)) {
+ len += sprintf(p + len, "level:\t\tunreadable\n");
+ } else {
+ len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
+ len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
+ len += sprintf(p + len, "commands:\tup, down, mute\n");
+ len += sprintf(p + len, "commands:\tlevel <level>"
+ " (<level> is 0-15)\n");
+ }
+
+ return len;
+}
+
+static int volume_write(char *buf)
+{
+ int cmos_cmd, inc, i;
+ u8 level, mute;
+ int new_level, new_mute;
+ char *cmd;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (!acpi_ec_read(volume_offset, &level))
+ return -EIO;
+ new_mute = mute = level & 0x40;
+ new_level = level = level & 0xf;
+
+ if (strlencmp(cmd, "up") == 0) {
+ if (mute)
+ new_mute = 0;
+ else
+ new_level = level == 15 ? 15 : level + 1;
+ } else if (strlencmp(cmd, "down") == 0) {
+ if (mute)
+ new_mute = 0;
+ else
+ new_level = level == 0 ? 0 : level - 1;
+ } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
+ new_level >= 0 && new_level <= 15) {
+ /* new_level set */
+ } else if (strlencmp(cmd, "mute") == 0) {
+ new_mute = 0x40;
+ } else
+ return -EINVAL;
+
+ if (new_level != level) { /* mute doesn't change */
+ cmos_cmd = new_level > level ? TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
+ inc = new_level > level ? 1 : -1;
+
+ if (mute && (issue_thinkpad_cmos_command(cmos_cmd) ||
+ !acpi_ec_write(volume_offset, level)))
+ return -EIO;
+
+ for (i = level; i != new_level; i += inc)
+ if (issue_thinkpad_cmos_command(cmos_cmd) ||
+ !acpi_ec_write(volume_offset, i + inc))
+ return -EIO;
+
+ if (mute && (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) ||
+ !acpi_ec_write(volume_offset,
+ new_level + mute)))
+ return -EIO;
+ }
+
+ if (new_mute != mute) { /* level doesn't change */
+ cmos_cmd = new_mute ? TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP;
+
+ if (issue_thinkpad_cmos_command(cmos_cmd) ||
+ !acpi_ec_write(volume_offset, level + new_mute))
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static struct ibm_struct volume_driver_data = {
+ .name = "volume",
+ .read = volume_read,
+ .write = volume_write,
+};
+
+/*************************************************************************
+ * Fan subdriver
+ */
+
+/*
+ * FAN ACCESS MODES
+ *
+ * TPACPI_FAN_RD_ACPI_GFAN:
+ * ACPI GFAN method: returns fan level
+ *
+ * see TPACPI_FAN_WR_ACPI_SFAN
+ * EC 0x2f (HFSP) not available if GFAN exists
+ *
+ * TPACPI_FAN_WR_ACPI_SFAN:
+ * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
+ *
+ * EC 0x2f (HFSP) might be available *for reading*, but do not use
+ * it for writing.
+ *
+ * TPACPI_FAN_WR_TPEC:
+ * ThinkPad EC register 0x2f (HFSP): fan control loop mode
+ * Supported on almost all ThinkPads
+ *
+ * Fan speed changes of any sort (including those caused by the
+ * disengaged mode) are usually done slowly by the firmware as the
+ * maximum ammount of fan duty cycle change per second seems to be
+ * limited.
+ *
+ * Reading is not available if GFAN exists.
+ * Writing is not available if SFAN exists.
+ *
+ * Bits
+ * 7 automatic mode engaged;
+ * (default operation mode of the ThinkPad)
+ * fan level is ignored in this mode.
+ * 6 full speed mode (takes precedence over bit 7);
+ * not available on all thinkpads. May disable
+ * the tachometer while the fan controller ramps up
+ * the speed (which can take up to a few *minutes*).
+ * Speeds up fan to 100% duty-cycle, which is far above
+ * the standard RPM levels. It is not impossible that
+ * it could cause hardware damage.
+ * 5-3 unused in some models. Extra bits for fan level
+ * in others, but still useless as all values above
+ * 7 map to the same speed as level 7 in these models.
+ * 2-0 fan level (0..7 usually)
+ * 0x00 = stop
+ * 0x07 = max (set when temperatures critical)
+ * Some ThinkPads may have other levels, see
+ * TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
+ *
+ * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
+ * boot. Apparently the EC does not intialize it, so unless ACPI DSDT
+ * does so, its initial value is meaningless (0x07).
+ *
+ * For firmware bugs, refer to:
+ * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ *
+ * ----
+ *
+ * ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
+ * Main fan tachometer reading (in RPM)
+ *
+ * This register is present on all ThinkPads with a new-style EC, and
+ * it is known not to be present on the A21m/e, and T22, as there is
+ * something else in offset 0x84 according to the ACPI DSDT. Other
+ * ThinkPads from this same time period (and earlier) probably lack the
+ * tachometer as well.
+ *
+ * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
+ * was never fixed by IBM to report the EC firmware version string
+ * probably support the tachometer (like the early X models), so
+ * detecting it is quite hard. We need more data to know for sure.
+ *
+ * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
+ * might result.
+ *
+ * FIRMWARE BUG: may go stale while the EC is switching to full speed
+ * mode.
+ *
+ * For firmware bugs, refer to:
+ * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ *
+ * TPACPI_FAN_WR_ACPI_FANS:
+ * ThinkPad X31, X40, X41. Not available in the X60.
+ *
+ * FANS ACPI handle: takes three arguments: low speed, medium speed,
+ * high speed. ACPI DSDT seems to map these three speeds to levels
+ * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
+ * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
+ *
+ * The speeds are stored on handles
+ * (FANA:FAN9), (FANC:FANB), (FANE:FAND).
+ *
+ * There are three default speed sets, acessible as handles:
+ * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
+ *
+ * ACPI DSDT switches which set is in use depending on various
+ * factors.
+ *
+ * TPACPI_FAN_WR_TPEC is also available and should be used to
+ * command the fan. The X31/X40/X41 seems to have 8 fan levels,
+ * but the ACPI tables just mention level 7.
+ */
+
+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 void fan_watchdog_fire(struct work_struct *ignored);
+static int fan_watchdog_maxinterval;
+static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
+
+IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */
+IBM_HANDLE(gfan, ec, "GFAN", /* 570 */
+ "\\FSPD", /* 600e/x, 770e, 770x */
+ ); /* all others */
+IBM_HANDLE(sfan, ec, "SFAN", /* 570 */
+ "JFNS", /* 770x-JL */
+ ); /* all others */
+
+/*
+ * SYSFS fan layout: hwmon compatible (device)
+ *
+ * pwm*_enable:
+ * 0: "disengaged" mode
+ * 1: manual mode
+ * 2: native EC "auto" mode (recommended, hardware default)
+ *
+ * pwm*: set speed in manual mode, ignored otherwise.
+ * 0 is level 0; 255 is level 7. Intermediate points done with linear
+ * interpolation.
+ *
+ * fan*_input: tachometer reading, RPM
+ *
+ *
+ * SYSFS fan layout: extensions
+ *
+ * fan_watchdog (driver):
+ * fan watchdog interval in seconds, 0 disables (default), max 120
+ */
+
+/* sysfs fan pwm1_enable ----------------------------------------------- */
+static ssize_t fan_pwm1_enable_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int res, mode;
+ u8 status;
+
+ res = fan_get_status_safe(&status);
+ if (res)
+ return res;
+
+ if (unlikely(tp_features.fan_ctrl_status_undef)) {
+ if (status != fan_control_initial_status) {
+ tp_features.fan_ctrl_status_undef = 0;
+ } else {
+ /* Return most likely status. In fact, it
+ * might be the only possible status */
+ status = TP_EC_FAN_AUTO;
+ }
+ }
+
+ if (status & TP_EC_FAN_FULLSPEED) {
+ mode = 0;
+ } else if (status & TP_EC_FAN_AUTO) {
+ mode = 2;
+ } else
+ mode = 1;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", mode);
+}
+
+static ssize_t fan_pwm1_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long t;
+ int res, level;
+
+ if (parse_strtoul(buf, 2, &t))
+ return -EINVAL;
+
+ switch (t) {
+ case 0:
+ level = TP_EC_FAN_FULLSPEED;
+ break;
+ case 1:
+ level = TPACPI_FAN_LAST_LEVEL;
+ break;
+ case 2:
+ level = TP_EC_FAN_AUTO;
+ break;
+ case 3:
+ /* reserved for software-controlled auto mode */
+ return -ENOSYS;
+ default:
+ return -EINVAL;
+ }
+
+ res = fan_set_level_safe(level);
+ if (res == -ENXIO)
+ return -EINVAL;
+ else if (res < 0)
+ return res;
+
+ fan_watchdog_reset();
+
+ return count;
+}
+
+static struct device_attribute dev_attr_fan_pwm1_enable =
+ __ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+ fan_pwm1_enable_show, fan_pwm1_enable_store);
+
+/* sysfs fan pwm1 ------------------------------------------------------ */
+static ssize_t fan_pwm1_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int res;
+ u8 status;
+
+ res = fan_get_status_safe(&status);
+ if (res)
+ return res;
+
+ if (unlikely(tp_features.fan_ctrl_status_undef)) {
+ if (status != fan_control_initial_status) {
+ tp_features.fan_ctrl_status_undef = 0;
+ } else {
+ status = TP_EC_FAN_AUTO;
+ }
+ }
+
+ if ((status &
+ (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0)
+ status = fan_control_desired_level;
+
+ if (status > 7)
+ status = 7;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7);
+}
+
+static ssize_t fan_pwm1_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long s;
+ int rc;
+ u8 status, newlevel;
+
+ if (parse_strtoul(buf, 255, &s))
+ return -EINVAL;
+
+ /* scale down from 0-255 to 0-7 */
+ newlevel = (s >> 5) & 0x07;
+
+ rc = mutex_lock_interruptible(&fan_mutex);
+ if (rc < 0)
+ return rc;
+
+ rc = fan_get_status(&status);
+ if (!rc && (status &
+ (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
+ rc = fan_set_level(newlevel);
+ if (rc == -ENXIO)
+ rc = -EINVAL;
+ else if (!rc) {
+ fan_update_desired_level(newlevel);
+ fan_watchdog_reset();
+ }
+ }
+
+ mutex_unlock(&fan_mutex);
+ return (rc)? rc : count;
+}
+
+static struct device_attribute dev_attr_fan_pwm1 =
+ __ATTR(pwm1, S_IWUSR | S_IRUGO,
+ fan_pwm1_show, fan_pwm1_store);
+
+/* sysfs fan fan1_input ------------------------------------------------ */
+static ssize_t fan_fan1_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int res;
+ unsigned int speed;
+
+ res = fan_get_speed(&speed);
+ if (res < 0)
+ return res;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", speed);
+}
+
+static struct device_attribute dev_attr_fan_fan1_input =
+ __ATTR(fan1_input, S_IRUGO,
+ fan_fan1_input_show, NULL);
+
+/* sysfs fan fan_watchdog (driver) ------------------------------------- */
+static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval);
+}
+
+static ssize_t fan_fan_watchdog_store(struct device_driver *drv,
+ const char *buf, size_t count)
+{
+ unsigned long t;
+
+ if (parse_strtoul(buf, 120, &t))
+ return -EINVAL;
+
+ if (!fan_control_allowed)
+ return -EPERM;
+
+ fan_watchdog_maxinterval = t;
+ fan_watchdog_reset();
+
+ return count;
+}
+
+static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
+ fan_fan_watchdog_show, fan_fan_watchdog_store);
+
+/* --------------------------------------------------------------------- */
+static struct attribute *fan_attributes[] = {
+ &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr,
+ &dev_attr_fan_fan1_input.attr,
+ NULL
+};
+
+static const struct attribute_group fan_attr_group = {
+ .attrs = fan_attributes,
+};
+
+static int __init fan_init(struct ibm_init_struct *iibm)
+{
+ int rc;
+
+ vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n");
+
+ mutex_init(&fan_mutex);
+ fan_status_access_mode = TPACPI_FAN_NONE;
+ fan_control_access_mode = TPACPI_FAN_WR_NONE;
+ fan_control_commands = 0;
+ fan_watchdog_maxinterval = 0;
+ tp_features.fan_ctrl_status_undef = 0;
+ fan_control_desired_level = 7;
+
+ IBM_ACPIHANDLE_INIT(fans);
+ IBM_ACPIHANDLE_INIT(gfan);
+ IBM_ACPIHANDLE_INIT(sfan);
+
+ if (gfan_handle) {
+ /* 570, 600e/x, 770e, 770x */
+ fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
+ } else {
+ /* all other ThinkPads: note that even old-style
+ * ThinkPad ECs supports the fan control register */
+ if (likely(acpi_ec_read(fan_status_offset,
+ &fan_control_initial_status))) {
+ fan_status_access_mode = TPACPI_FAN_RD_TPEC;
+
+ /* In some ThinkPads, neither the EC nor the ACPI
+ * DSDT initialize the fan status, and it ends up
+ * being set to 0x07 when it *could* be either
+ * 0x07 or 0x80.
+ *
+ * Enable for TP-1Y (T43), TP-78 (R51e),
+ * TP-76 (R52), TP-70 (T43, R52), which are known
+ * to be buggy. */
+ if (fan_control_initial_status == 0x07 &&
+ ibm_thinkpad_ec_found &&
+ ((ibm_thinkpad_ec_found[0] == '1' &&
+ ibm_thinkpad_ec_found[1] == 'Y') ||
+ (ibm_thinkpad_ec_found[0] == '7' &&
+ (ibm_thinkpad_ec_found[1] == '6' ||
+ ibm_thinkpad_ec_found[1] == '8' ||
+ ibm_thinkpad_ec_found[1] == '0'))
+ )) {
+ printk(IBM_NOTICE
+ "fan_init: initial fan status is "
+ "unknown, assuming it is in auto "
+ "mode\n");
+ tp_features.fan_ctrl_status_undef = 1;
+ }
+ } else {
+ printk(IBM_ERR
+ "ThinkPad ACPI EC access misbehaving, "
+ "fan status and control unavailable\n");
+ return 1;
+ }
+ }
+
+ if (sfan_handle) {
+ /* 570, 770x-JL */
+ fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN;
+ fan_control_commands |=
+ TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE;
+ } else {
+ if (!gfan_handle) {
+ /* gfan without sfan means no fan control */
+ /* all other models implement TP EC 0x2f control */
+
+ if (fans_handle) {
+ /* X31, X40, X41 */
+ fan_control_access_mode =
+ TPACPI_FAN_WR_ACPI_FANS;
+ fan_control_commands |=
+ TPACPI_FAN_CMD_SPEED |
+ TPACPI_FAN_CMD_LEVEL |
+ TPACPI_FAN_CMD_ENABLE;
+ } else {
+ fan_control_access_mode = TPACPI_FAN_WR_TPEC;
+ fan_control_commands |=
+ TPACPI_FAN_CMD_LEVEL |
+ TPACPI_FAN_CMD_ENABLE;
+ }
+ }
+ }
+
+ vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n",
+ str_supported(fan_status_access_mode != TPACPI_FAN_NONE ||
+ fan_control_access_mode != TPACPI_FAN_WR_NONE),
+ fan_status_access_mode, fan_control_access_mode);
+
+ /* fan control master switch */
+ if (!fan_control_allowed) {
+ fan_control_access_mode = TPACPI_FAN_WR_NONE;
+ fan_control_commands = 0;
+ dbg_printk(TPACPI_DBG_INIT,
+ "fan control features disabled by parameter\n");
+ }
+
+ /* update fan_control_desired_level */
+ if (fan_status_access_mode != TPACPI_FAN_NONE)
+ fan_get_status_safe(NULL);
+
+ if (fan_status_access_mode != TPACPI_FAN_NONE ||
+ fan_control_access_mode != TPACPI_FAN_WR_NONE) {
+ rc = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ &fan_attr_group);
+ if (!(rc < 0))
+ rc = driver_create_file(&tpacpi_pdriver.driver,
+ &driver_attr_fan_watchdog);
+ if (rc < 0)
+ return rc;
+ return 0;
+ } else
+ return 1;
+}
+
+/*
+ * Call with fan_mutex held
+ */
+static void fan_update_desired_level(u8 status)
+{
+ if ((status &
+ (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
+ if (status > 7)
+ fan_control_desired_level = 7;
+ else
+ fan_control_desired_level = status;
+ }
+}
+
+static int fan_get_status(u8 *status)
+{
+ u8 s;
+
+ /* TODO:
+ * Add TPACPI_FAN_RD_ACPI_FANS ? */
+
+ switch (fan_status_access_mode) {
+ case TPACPI_FAN_RD_ACPI_GFAN:
+ /* 570, 600e/x, 770e, 770x */
+
+ if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
+ return -EIO;
+
+ if (likely(status))
+ *status = s & 0x07;
+
+ break;
+
+ case TPACPI_FAN_RD_TPEC:
+ /* all except 570, 600e/x, 770e, 770x */
+ if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
+ return -EIO;
+
+ if (likely(status))
+ *status = s;
+
+ break;
+
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int fan_get_status_safe(u8 *status)
+{
+ int rc;
+ u8 s;
+
+ rc = mutex_lock_interruptible(&fan_mutex);
+ if (rc < 0)
+ return rc;
+ rc = fan_get_status(&s);
+ if (!rc)
+ fan_update_desired_level(s);
+ mutex_unlock(&fan_mutex);
+
+ if (status)
+ *status = s;
+
+ return rc;
+}
+
+static void fan_exit(void)
+{
+ vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n");
+
+ /* FIXME: can we really do this unconditionally? */
+ sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group);
+ driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog);
+
+ cancel_delayed_work(&fan_watchdog_task);
+ flush_scheduled_work();
+}
+
+static int fan_get_speed(unsigned int *speed)
+{
+ u8 hi, lo;
+
+ switch (fan_status_access_mode) {
+ case TPACPI_FAN_RD_TPEC:
+ /* all except 570, 600e/x, 770e, 770x */
+ if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
+ !acpi_ec_read(fan_rpm_offset + 1, &hi)))
+ return -EIO;
+
+ if (likely(speed))
+ *speed = (hi << 8) | lo;
+
+ break;
+
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static void fan_watchdog_fire(struct work_struct *ignored)
+{
+ int rc;
+
+ printk(IBM_NOTICE "fan watchdog: enabling fan\n");
+ rc = fan_set_enable();
+ if (rc < 0) {
+ printk(IBM_ERR "fan watchdog: error %d while enabling fan, "
+ "will try again later...\n", -rc);
+ /* reschedule for later */
+ fan_watchdog_reset();
+ }
+}
+
+static void fan_watchdog_reset(void)
+{
+ static int fan_watchdog_active = 0;
+
+ if (fan_control_access_mode == TPACPI_FAN_WR_NONE)
+ return;
+
+ if (fan_watchdog_active)
+ cancel_delayed_work(&fan_watchdog_task);
+
+ if (fan_watchdog_maxinterval > 0) {
+ fan_watchdog_active = 1;
+ if (!schedule_delayed_work(&fan_watchdog_task,
+ msecs_to_jiffies(fan_watchdog_maxinterval
+ * 1000))) {
+ printk(IBM_ERR "failed to schedule the fan watchdog, "
+ "watchdog will not trigger\n");
+ }
+ } else
+ fan_watchdog_active = 0;
+}
+
+static int fan_set_level(int level)
+{
+ if (!fan_control_allowed)
+ return -EPERM;
+
+ switch (fan_control_access_mode) {
+ case TPACPI_FAN_WR_ACPI_SFAN:
+ if (level >= 0 && level <= 7) {
+ if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
+ return -EIO;
+ } else
+ return -EINVAL;
+ break;
+
+ case TPACPI_FAN_WR_ACPI_FANS:
+ case TPACPI_FAN_WR_TPEC:
+ if ((level != TP_EC_FAN_AUTO) &&
+ (level != TP_EC_FAN_FULLSPEED) &&
+ ((level < 0) || (level > 7)))
+ return -EINVAL;
+
+ /* safety net should the EC not support AUTO
+ * or FULLSPEED mode bits and just ignore them */
+ if (level & TP_EC_FAN_FULLSPEED)
+ level |= 7; /* safety min speed 7 */
+ else if (level & TP_EC_FAN_FULLSPEED)
+ level |= 4; /* safety min speed 4 */
+
+ if (!acpi_ec_write(fan_status_offset, level))
+ return -EIO;
+ else
+ tp_features.fan_ctrl_status_undef = 0;
+ break;
+
+ default:
+ return -ENXIO;
+ }
+ return 0;
+}
+
+static int fan_set_level_safe(int level)
+{
+ int rc;
+
+ if (!fan_control_allowed)
+ return -EPERM;
+
+ rc = mutex_lock_interruptible(&fan_mutex);
+ if (rc < 0)
+ return rc;
+
+ if (level == TPACPI_FAN_LAST_LEVEL)
+ level = fan_control_desired_level;
+
+ rc = fan_set_level(level);
+ if (!rc)
+ fan_update_desired_level(level);
+
+ mutex_unlock(&fan_mutex);
+ return rc;
+}
+
+static int fan_set_enable(void)
+{
+ u8 s;
+ int rc;
+
+ if (!fan_control_allowed)
+ return -EPERM;
+
+ rc = mutex_lock_interruptible(&fan_mutex);
+ if (rc < 0)
+ return rc;
+
+ switch (fan_control_access_mode) {
+ case TPACPI_FAN_WR_ACPI_FANS:
+ case TPACPI_FAN_WR_TPEC:
+ rc = fan_get_status(&s);
+ if (rc < 0)
+ break;
+
+ /* Don't go out of emergency fan mode */
+ if (s != 7) {
+ s &= 0x07;
+ s |= TP_EC_FAN_AUTO | 4; /* min fan speed 4 */
+ }
+
+ if (!acpi_ec_write(fan_status_offset, s))
+ rc = -EIO;
+ else {
+ tp_features.fan_ctrl_status_undef = 0;
+ rc = 0;
+ }
+ break;
+
+ case TPACPI_FAN_WR_ACPI_SFAN:
+ rc = fan_get_status(&s);
+ if (rc < 0)
+ break;
+
+ s &= 0x07;
+
+ /* Set fan to at least level 4 */
+ s |= 4;
+
+ if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
+ rc= -EIO;
+ else
+ rc = 0;
+ break;
+
+ default:
+ rc = -ENXIO;
+ }
+
+ mutex_unlock(&fan_mutex);
+ return rc;
+}
+
+static int fan_set_disable(void)
+{
+ int rc;
+
+ if (!fan_control_allowed)
+ return -EPERM;
+
+ rc = mutex_lock_interruptible(&fan_mutex);
+ if (rc < 0)
+ return rc;
+
+ rc = 0;
+ switch (fan_control_access_mode) {
+ case TPACPI_FAN_WR_ACPI_FANS:
+ case TPACPI_FAN_WR_TPEC:
+ if (!acpi_ec_write(fan_status_offset, 0x00))
+ rc = -EIO;
+ else {
+ fan_control_desired_level = 0;
+ tp_features.fan_ctrl_status_undef = 0;
+ }
+ break;
+
+ case TPACPI_FAN_WR_ACPI_SFAN:
+ if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
+ rc = -EIO;
+ else
+ fan_control_desired_level = 0;
+ break;
+
+ default:
+ rc = -ENXIO;
+ }
+
+
+ mutex_unlock(&fan_mutex);
+ return rc;
+}
+
+static int fan_set_speed(int speed)
+{
+ int rc;
+
+ if (!fan_control_allowed)
+ return -EPERM;
+
+ rc = mutex_lock_interruptible(&fan_mutex);
+ if (rc < 0)
+ return rc;
+
+ rc = 0;
+ switch (fan_control_access_mode) {
+ case TPACPI_FAN_WR_ACPI_FANS:
+ if (speed >= 0 && speed <= 65535) {
+ if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
+ speed, speed, speed))
+ rc = -EIO;
+ } else
+ rc = -EINVAL;
+ break;
+
+ default:
+ rc = -ENXIO;
+ }
+
+ mutex_unlock(&fan_mutex);
+ return rc;
+}
+
+static int fan_read(char *p)
+{
+ int len = 0;
+ int rc;
+ u8 status;
+ unsigned int speed = 0;
+
+ switch (fan_status_access_mode) {
+ case TPACPI_FAN_RD_ACPI_GFAN:
+ /* 570, 600e/x, 770e, 770x */
+ if ((rc = fan_get_status_safe(&status)) < 0)
+ return rc;
+
+ len += sprintf(p + len, "status:\t\t%s\n"
+ "level:\t\t%d\n",
+ (status != 0) ? "enabled" : "disabled", status);
+ break;
+
+ case TPACPI_FAN_RD_TPEC:
+ /* all except 570, 600e/x, 770e, 770x */
+ if ((rc = fan_get_status_safe(&status)) < 0)
+ return rc;
+
+ if (unlikely(tp_features.fan_ctrl_status_undef)) {
+ if (status != fan_control_initial_status)
+ tp_features.fan_ctrl_status_undef = 0;
+ else
+ /* Return most likely status. In fact, it
+ * might be the only possible status */
+ status = TP_EC_FAN_AUTO;
+ }
+
+ len += sprintf(p + len, "status:\t\t%s\n",
+ (status != 0) ? "enabled" : "disabled");
+
+ if ((rc = fan_get_speed(&speed)) < 0)
+ return rc;
+
+ len += sprintf(p + len, "speed:\t\t%d\n", speed);
+
+ if (status & TP_EC_FAN_FULLSPEED)
+ /* Disengaged mode takes precedence */
+ len += sprintf(p + len, "level:\t\tdisengaged\n");
+ else if (status & TP_EC_FAN_AUTO)
+ len += sprintf(p + len, "level:\t\tauto\n");
+ else
+ len += sprintf(p + len, "level:\t\t%d\n", status);
+ break;
+
+ case TPACPI_FAN_NONE:
+ default:
+ len += sprintf(p + len, "status:\t\tnot supported\n");
+ }
+
+ if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) {
+ len += sprintf(p + len, "commands:\tlevel <level>");
+
+ switch (fan_control_access_mode) {
+ case TPACPI_FAN_WR_ACPI_SFAN:
+ len += sprintf(p + len, " (<level> is 0-7)\n");
+ break;
+
+ default:
+ len += sprintf(p + len, " (<level> is 0-7, "
+ "auto, disengaged, full-speed)\n");
+ break;
+ }
+ }
+
+ if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
+ len += sprintf(p + len, "commands:\tenable, disable\n"
+ "commands:\twatchdog <timeout> (<timeout> is 0 (off), "
+ "1-120 (seconds))\n");
+
+ if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
+ len += sprintf(p + len, "commands:\tspeed <speed>"
+ " (<speed> is 0-65535)\n");
+
+ return len;
+}
+
+static int fan_write_cmd_level(const char *cmd, int *rc)
+{
+ int level;
+
+ if (strlencmp(cmd, "level auto") == 0)
+ level = TP_EC_FAN_AUTO;
+ else if ((strlencmp(cmd, "level disengaged") == 0) |
+ (strlencmp(cmd, "level full-speed") == 0))
+ level = TP_EC_FAN_FULLSPEED;
+ else if (sscanf(cmd, "level %d", &level) != 1)
+ return 0;
+
+ if ((*rc = fan_set_level_safe(level)) == -ENXIO)
+ printk(IBM_ERR "level command accepted for unsupported "
+ "access mode %d", fan_control_access_mode);
+
+ return 1;
+}
+
+static int fan_write_cmd_enable(const char *cmd, int *rc)
+{
+ if (strlencmp(cmd, "enable") != 0)
+ return 0;
+
+ if ((*rc = fan_set_enable()) == -ENXIO)
+ printk(IBM_ERR "enable command accepted for unsupported "
+ "access mode %d", fan_control_access_mode);
+
+ return 1;
+}
+
+static int fan_write_cmd_disable(const char *cmd, int *rc)
+{
+ if (strlencmp(cmd, "disable") != 0)
+ return 0;
+
+ if ((*rc = fan_set_disable()) == -ENXIO)
+ printk(IBM_ERR "disable command accepted for unsupported "
+ "access mode %d", fan_control_access_mode);
+
+ return 1;
+}
+
+static int fan_write_cmd_speed(const char *cmd, int *rc)
+{
+ int speed;
+
+ /* TODO:
+ * Support speed <low> <medium> <high> ? */
+
+ if (sscanf(cmd, "speed %d", &speed) != 1)
+ return 0;
+
+ if ((*rc = fan_set_speed(speed)) == -ENXIO)
+ printk(IBM_ERR "speed command accepted for unsupported "
+ "access mode %d", fan_control_access_mode);
+
+ return 1;
+}
+
+static int fan_write_cmd_watchdog(const char *cmd, int *rc)
+{
+ int interval;
+
+ if (sscanf(cmd, "watchdog %d", &interval) != 1)
+ return 0;
+
+ if (interval < 0 || interval > 120)
+ *rc = -EINVAL;
+ else
+ fan_watchdog_maxinterval = interval;
+
+ return 1;
+}
+
+static int fan_write(char *buf)
+{
+ char *cmd;
+ int rc = 0;
+
+ while (!rc && (cmd = next_cmd(&buf))) {
+ if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
+ fan_write_cmd_level(cmd, &rc)) &&
+ !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
+ (fan_write_cmd_enable(cmd, &rc) ||
+ fan_write_cmd_disable(cmd, &rc) ||
+ fan_write_cmd_watchdog(cmd, &rc))) &&
+ !((fan_control_commands & TPACPI_FAN_CMD_SPEED) &&
+ fan_write_cmd_speed(cmd, &rc))
+ )
+ rc = -EINVAL;
+ else if (!rc)
+ fan_watchdog_reset();
+ }
+
+ return rc;
+}
+
+static struct ibm_struct fan_driver_data = {
+ .name = "fan",
+ .read = fan_read,
+ .write = fan_write,
+ .exit = fan_exit,
+};
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Infrastructure
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+/* /proc support */
+static struct proc_dir_entry *proc_dir = NULL;
+
+/* Subdriver registry */
+static LIST_HEAD(tpacpi_all_drivers);
+
+
+/*
+ * Module and infrastructure proble, init and exit handling
+ */
+
+#ifdef CONFIG_THINKPAD_ACPI_DEBUG
+static const char * __init str_supported(int is_supported)
+{
+ static char text_unsupported[] __initdata = "not supported";
+
+ return (is_supported)? &text_unsupported[4] : &text_unsupported[0];
+}
+#endif /* CONFIG_THINKPAD_ACPI_DEBUG */
+
+static int __init ibm_init(struct ibm_init_struct *iibm)
+{
+ int ret;
+ struct ibm_struct *ibm = iibm->data;
+ struct proc_dir_entry *entry;
+
+ BUG_ON(ibm == NULL);
+
+ INIT_LIST_HEAD(&ibm->all_drivers);
+
+ if (ibm->flags.experimental && !experimental)
+ return 0;
+
+ dbg_printk(TPACPI_DBG_INIT,
+ "probing for %s\n", ibm->name);
+
+ if (iibm->init) {
+ ret = iibm->init(iibm);
+ if (ret > 0)
+ return 0; /* probe failed */
+ if (ret)
+ return ret;
+
+ ibm->flags.init_called = 1;
+ }
+
+ if (ibm->acpi) {
+ if (ibm->acpi->hid) {
+ ret = register_tpacpi_subdriver(ibm);
+ if (ret)
+ goto err_out;
+ }
+
+ if (ibm->acpi->notify) {
+ ret = setup_acpi_notify(ibm);
+ if (ret == -ENODEV) {
+ printk(IBM_NOTICE "disabling subdriver %s\n",
+ ibm->name);
+ ret = 0;
+ goto err_out;
+ }
+ if (ret < 0)
+ goto err_out;
+ }
+ }
+
+ dbg_printk(TPACPI_DBG_INIT,
+ "%s installed\n", ibm->name);
+
+ if (ibm->read) {
+ entry = create_proc_entry(ibm->name,
+ S_IFREG | S_IRUGO | S_IWUSR,
+ proc_dir);
+ if (!entry) {
+ printk(IBM_ERR "unable to create proc entry %s\n",
+ ibm->name);
+ ret = -ENODEV;
+ goto err_out;
+ }
+ entry->owner = THIS_MODULE;
+ entry->data = ibm;
+ entry->read_proc = &dispatch_procfs_read;
+ if (ibm->write)
+ entry->write_proc = &dispatch_procfs_write;
+ ibm->flags.proc_created = 1;
+ }
+
+ list_add_tail(&ibm->all_drivers, &tpacpi_all_drivers);
+
+ return 0;
+
+err_out:
+ dbg_printk(TPACPI_DBG_INIT,
+ "%s: at error exit path with result %d\n",
+ ibm->name, ret);
+
+ ibm_exit(ibm);
+ return (ret < 0)? ret : 0;
+}
+
+static void ibm_exit(struct ibm_struct *ibm)
+{
+ dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name);
+
+ list_del_init(&ibm->all_drivers);
+
+ if (ibm->flags.acpi_notify_installed) {
+ dbg_printk(TPACPI_DBG_EXIT,
+ "%s: acpi_remove_notify_handler\n", ibm->name);
+ BUG_ON(!ibm->acpi);
+ acpi_remove_notify_handler(*ibm->acpi->handle,
+ ibm->acpi->type,
+ dispatch_acpi_notify);
+ ibm->flags.acpi_notify_installed = 0;
+ ibm->flags.acpi_notify_installed = 0;
+ }
+
+ if (ibm->flags.proc_created) {
+ dbg_printk(TPACPI_DBG_EXIT,
+ "%s: remove_proc_entry\n", ibm->name);
+ remove_proc_entry(ibm->name, proc_dir);
+ ibm->flags.proc_created = 0;
+ }
+
+ if (ibm->flags.acpi_driver_registered) {
+ dbg_printk(TPACPI_DBG_EXIT,
+ "%s: acpi_bus_unregister_driver\n", ibm->name);
+ BUG_ON(!ibm->acpi);
+ acpi_bus_unregister_driver(ibm->acpi->driver);
+ kfree(ibm->acpi->driver);
+ ibm->acpi->driver = NULL;
+ ibm->flags.acpi_driver_registered = 0;
+ }
+
+ if (ibm->flags.init_called && ibm->exit) {
+ ibm->exit();
+ ibm->flags.init_called = 0;
+ }
+
+ dbg_printk(TPACPI_DBG_INIT, "finished removing %s\n", ibm->name);
+}
+
+/* Probing */
+
+static char *ibm_thinkpad_ec_found = NULL;
+
+static char* __init check_dmi_for_ec(void)
+{
+ struct dmi_device *dev = NULL;
+ char ec_fw_string[18];
+
+ /*
+ * ThinkPad T23 or newer, A31 or newer, R50e or newer,
+ * X32 or newer, all Z series; Some models must have an
+ * up-to-date BIOS or they will not be detected.
+ *
+ * See http://thinkwiki.org/wiki/List_of_DMI_IDs
+ */
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+ if (sscanf(dev->name,
+ "IBM ThinkPad Embedded Controller -[%17c",
+ ec_fw_string) == 1) {
+ ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
+ ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
+ return kstrdup(ec_fw_string, GFP_KERNEL);
+ }
+ }
+ return NULL;
+}
+
+static int __init probe_for_thinkpad(void)
+{
+ int is_thinkpad;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ /*
+ * Non-ancient models have better DMI tagging, but very old models
+ * don't.
+ */
+ is_thinkpad = dmi_name_in_vendors("ThinkPad");
+
+ /* ec is required because many other handles are relative to it */
+ IBM_ACPIHANDLE_INIT(ec);
+ if (!ec_handle) {
+ if (is_thinkpad)
+ printk(IBM_ERR
+ "Not yet supported ThinkPad detected!\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Risks a regression on very old machines, but reduces potential
+ * false positives a damn great deal
+ */
+ if (!is_thinkpad)
+ is_thinkpad = dmi_name_in_vendors("IBM");
+
+ if (!is_thinkpad && !force_load)
+ return -ENODEV;
+
+ return 0;
+}
+
+
+/* Module init, exit, parameters */
+
+static struct ibm_init_struct ibms_init[] __initdata = {
+ {
+ .init = thinkpad_acpi_driver_init,
+ .data = &thinkpad_acpi_driver_data,
+ },
+ {
+ .init = hotkey_init,
+ .data = &hotkey_driver_data,
+ },
+ {
+ .init = bluetooth_init,
+ .data = &bluetooth_driver_data,
+ },
+ {
+ .init = wan_init,
+ .data = &wan_driver_data,
+ },
+ {
+ .init = video_init,
+ .data = &video_driver_data,
+ },
+ {
+ .init = light_init,
+ .data = &light_driver_data,
+ },
+#ifdef CONFIG_THINKPAD_ACPI_DOCK
+ {
+ .init = dock_init,
+ .data = &dock_driver_data[0],
+ },
+ {
+ .init = dock_init2,
+ .data = &dock_driver_data[1],
+ },
+#endif
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+ {
+ .init = bay_init,
+ .data = &bay_driver_data,
+ },
+#endif
+ {
+ .init = cmos_init,
+ .data = &cmos_driver_data,
+ },
+ {
+ .init = led_init,
+ .data = &led_driver_data,
+ },
+ {
+ .init = beep_init,
+ .data = &beep_driver_data,
+ },
+ {
+ .init = thermal_init,
+ .data = &thermal_driver_data,
+ },
+ {
+ .data = &ecdump_driver_data,
+ },
+ {
+ .init = brightness_init,
+ .data = &brightness_driver_data,
+ },
+ {
+ .data = &volume_driver_data,
+ },
+ {
+ .init = fan_init,
+ .data = &fan_driver_data,
+ },
+};
+
+static int __init set_ibm_param(const char *val, struct kernel_param *kp)
+{
+ unsigned int i;
+ struct ibm_struct *ibm;
+
+ for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
+ ibm = ibms_init[i].data;
+ BUG_ON(ibm == NULL);
+
+ if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
+ if (strlen(val) > sizeof(ibms_init[i].param) - 2)
+ return -ENOSPC;
+ strcpy(ibms_init[i].param, val);
+ strcat(ibms_init[i].param, ",");
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int experimental;
+module_param(experimental, int, 0);
+
+static u32 dbg_level;
+module_param_named(debug, dbg_level, uint, 0);
+
+static int force_load;
+module_param(force_load, int, 0);
+
+static int fan_control_allowed;
+module_param_named(fan_control, fan_control_allowed, int, 0);
+
+#define IBM_PARAM(feature) \
+ module_param_call(feature, set_ibm_param, NULL, NULL, 0)
+
+IBM_PARAM(hotkey);
+IBM_PARAM(bluetooth);
+IBM_PARAM(video);
+IBM_PARAM(light);
+#ifdef CONFIG_THINKPAD_ACPI_DOCK
+IBM_PARAM(dock);
+#endif
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+IBM_PARAM(bay);
+#endif /* CONFIG_THINKPAD_ACPI_BAY */
+IBM_PARAM(cmos);
+IBM_PARAM(led);
+IBM_PARAM(beep);
+IBM_PARAM(ecdump);
+IBM_PARAM(brightness);
+IBM_PARAM(volume);
+IBM_PARAM(fan);
+
+static int __init thinkpad_acpi_module_init(void)
+{
+ int ret, i;
+
+ /* Driver-level probe */
+ ret = probe_for_thinkpad();
+ if (ret)
+ return ret;
+
+ /* Driver initialization */
+ ibm_thinkpad_ec_found = check_dmi_for_ec();
+ IBM_ACPIHANDLE_INIT(ecrd);
+ IBM_ACPIHANDLE_INIT(ecwr);
+
+ proc_dir = proc_mkdir(IBM_PROC_DIR, acpi_root_dir);
+ if (!proc_dir) {
+ printk(IBM_ERR "unable to create proc dir " IBM_PROC_DIR);
+ thinkpad_acpi_module_exit();
+ return -ENODEV;
+ }
+ proc_dir->owner = THIS_MODULE;
+
+ ret = platform_driver_register(&tpacpi_pdriver);
+ if (ret) {
+ printk(IBM_ERR "unable to register platform driver\n");
+ thinkpad_acpi_module_exit();
+ return ret;
+ }
+ ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver);
+ if (ret) {
+ printk(IBM_ERR "unable to create sysfs driver attributes\n");
+ thinkpad_acpi_module_exit();
+ return ret;
+ }
+
+
+ /* Device initialization */
+ tpacpi_pdev = platform_device_register_simple(IBM_DRVR_NAME, -1,
+ NULL, 0);
+ if (IS_ERR(tpacpi_pdev)) {
+ ret = PTR_ERR(tpacpi_pdev);
+ tpacpi_pdev = NULL;
+ printk(IBM_ERR "unable to register platform device\n");
+ thinkpad_acpi_module_exit();
+ return ret;
+ }
+ tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev);
+ if (IS_ERR(tpacpi_hwmon)) {
+ ret = PTR_ERR(tpacpi_hwmon);
+ tpacpi_hwmon = NULL;
+ printk(IBM_ERR "unable to register hwmon device\n");
+ thinkpad_acpi_module_exit();
+ return ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
+ ret = ibm_init(&ibms_init[i]);
+ if (ret >= 0 && *ibms_init[i].param)
+ ret = ibms_init[i].data->write(ibms_init[i].param);
+ if (ret < 0) {
+ thinkpad_acpi_module_exit();
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void thinkpad_acpi_module_exit(void)
+{
+ struct ibm_struct *ibm, *itmp;
+
+ list_for_each_entry_safe_reverse(ibm, itmp,
+ &tpacpi_all_drivers,
+ all_drivers) {
+ ibm_exit(ibm);
+ }
+
+ dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n");
+
+ if (tpacpi_hwmon)
+ hwmon_device_unregister(tpacpi_hwmon);
+
+ if (tpacpi_pdev)
+ platform_device_unregister(tpacpi_pdev);
+
+ tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);
+ platform_driver_unregister(&tpacpi_pdriver);
+
+ if (proc_dir)
+ remove_proc_entry(IBM_PROC_DIR, acpi_root_dir);
+
+ kfree(ibm_thinkpad_ec_found);
+}
+
+module_init(thinkpad_acpi_module_init);
+module_exit(thinkpad_acpi_module_exit);
diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h
new file mode 100644
index 0000000..440145a
--- /dev/null
+++ b/drivers/misc/thinkpad_acpi.h
@@ -0,0 +1,572 @@
+/*
+ * 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 */
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index a064f36..b5ac810 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -317,6 +317,10 @@
{
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;
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 65d6f23..3411483 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1303,119 +1303,6 @@
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EESSC, quirk_alder_ioapic );
#endif
-enum ide_combined_type { COMBINED = 0, IDE = 1, LIBATA = 2 };
-/* Defaults to combined */
-static enum ide_combined_type combined_mode;
-
-static int __init combined_setup(char *str)
-{
- if (!strncmp(str, "ide", 3))
- combined_mode = IDE;
- else if (!strncmp(str, "libata", 6))
- combined_mode = LIBATA;
- else /* "combined" or anything else defaults to old behavior */
- combined_mode = COMBINED;
-
- return 1;
-}
-__setup("combined_mode=", combined_setup);
-
-#ifdef CONFIG_SATA_INTEL_COMBINED
-static void __devinit quirk_intel_ide_combined(struct pci_dev *pdev)
-{
- u8 prog, comb, tmp;
- int ich = 0;
-
- /*
- * Narrow down to Intel SATA PCI devices.
- */
- switch (pdev->device) {
- /* PCI ids taken from drivers/scsi/ata_piix.c */
- case 0x24d1:
- case 0x24df:
- case 0x25a3:
- case 0x25b0:
- ich = 5;
- break;
- case 0x2651:
- case 0x2652:
- case 0x2653:
- case 0x2680: /* ESB2 */
- ich = 6;
- break;
- case 0x27c0:
- case 0x27c4:
- ich = 7;
- break;
- case 0x2828: /* ICH8M */
- ich = 8;
- break;
- default:
- /* we do not handle this PCI device */
- return;
- }
-
- /*
- * Read combined mode register.
- */
- pci_read_config_byte(pdev, 0x90, &tmp); /* combined mode reg */
-
- if (ich == 5) {
- tmp &= 0x6; /* interesting bits 2:1, PATA primary/secondary */
- if (tmp == 0x4) /* bits 10x */
- comb = (1 << 0); /* SATA port 0, PATA port 1 */
- else if (tmp == 0x6) /* bits 11x */
- comb = (1 << 2); /* PATA port 0, SATA port 1 */
- else
- return; /* not in combined mode */
- } else {
- WARN_ON((ich != 6) && (ich != 7) && (ich != 8));
- tmp &= 0x3; /* interesting bits 1:0 */
- if (tmp & (1 << 0))
- comb = (1 << 2); /* PATA port 0, SATA port 1 */
- else if (tmp & (1 << 1))
- comb = (1 << 0); /* SATA port 0, PATA port 1 */
- else
- return; /* not in combined mode */
- }
-
- /*
- * Read programming interface register.
- * (Tells us if it's legacy or native mode)
- */
- pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog);
-
- /* if SATA port is in native mode, we're ok. */
- if (prog & comb)
- return;
-
- /* Don't reserve any so the IDE driver can get them (but only if
- * combined_mode=ide).
- */
- if (combined_mode == IDE)
- return;
-
- /* Grab them both for libata if combined_mode=libata. */
- if (combined_mode == LIBATA) {
- request_region(0x1f0, 8, "libata"); /* port 0 */
- request_region(0x170, 8, "libata"); /* port 1 */
- return;
- }
-
- /* SATA port is in legacy mode. Reserve port so that
- * IDE driver does not attempt to use it. If request_region
- * fails, it will be obvious at boot time, so we don't bother
- * checking return values.
- */
- if (comb == (1 << 0))
- request_region(0x1f0, 8, "libata"); /* port 0 */
- else
- request_region(0x170, 8, "libata"); /* port 1 */
-}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_intel_ide_combined );
-#endif /* CONFIG_SATA_INTEL_COMBINED */
-
-
int pcie_mch_quirk;
EXPORT_SYMBOL(pcie_mch_quirk);
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 95045e3..e9bd299 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -3770,7 +3770,8 @@
* Return value:
* 0 on success / non-zero on failure
**/
-static int ipr_sata_reset(struct ata_port *ap, unsigned int *classes)
+static int ipr_sata_reset(struct ata_port *ap, unsigned int *classes,
+ unsigned long deadline)
{
struct ipr_sata_port *sata_port = ap->private_data;
struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h
index 09469e7..955adfb 100644
--- a/include/acpi/actbl.h
+++ b/include/acpi/actbl.h
@@ -276,6 +276,7 @@
#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
diff --git a/include/linux/ata.h b/include/linux/ata.h
index 6caeb98..edb31bf 100644
--- a/include/linux/ata.h
+++ b/include/linux/ata.h
@@ -159,11 +159,19 @@
ATA_CMD_INIT_DEV_PARAMS = 0x91,
ATA_CMD_READ_NATIVE_MAX = 0xF8,
ATA_CMD_READ_NATIVE_MAX_EXT = 0x27,
+ ATA_CMD_SET_MAX = 0xF9,
+ ATA_CMD_SET_MAX_EXT = 0x37,
ATA_CMD_READ_LOG_EXT = 0x2f,
/* READ_LOG_EXT pages */
ATA_LOG_SATA_NCQ = 0x10,
+ /* READ/WRITE LONG (obsolete) */
+ ATA_CMD_READ_LONG = 0x22,
+ ATA_CMD_READ_LONG_ONCE = 0x23,
+ ATA_CMD_WRITE_LONG = 0x32,
+ ATA_CMD_WRITE_LONG_ONCE = 0x33,
+
/* SETFEATURES stuff */
SETFEATURES_XFER = 0x03,
XFER_UDMA_7 = 0x47,
@@ -194,6 +202,8 @@
SETFEATURES_WC_ON = 0x02, /* Enable write cache */
SETFEATURES_WC_OFF = 0x82, /* Disable write cache */
+ SETFEATURES_SPINUP = 0x07, /* Spin-up drive */
+
/* ATAPI stuff */
ATAPI_PKT_DMA = (1 << 0),
ATAPI_DMADIR = (1 << 2), /* ATAPI data dir:
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 6859a3b..71ea9231 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -99,7 +99,6 @@
extern struct resource iomem_resource;
extern int request_resource(struct resource *root, struct resource *new);
-extern struct resource * ____request_resource(struct resource *root, struct resource *new);
extern int release_resource(struct resource *new);
extern int insert_resource(struct resource *parent, struct resource *new);
extern int allocate_resource(struct resource *root, struct resource *new,
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 0cfbcb6..d8cfc72 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -210,6 +210,7 @@
/* host set flags */
ATA_HOST_SIMPLEX = (1 << 0), /* Host is simplex, one DMA channel per host only */
+ ATA_HOST_STARTED = (1 << 1), /* Host started */
/* various lengths of time */
ATA_TMOUT_BOOT = 30 * HZ, /* heuristic */
@@ -281,11 +282,13 @@
ATA_EHI_NO_AUTOPSY = (1 << 2), /* no autopsy */
ATA_EHI_QUIET = (1 << 3), /* be quiet */
- ATA_EHI_DID_RESET = (1 << 16), /* already reset this port */
- ATA_EHI_PRINTINFO = (1 << 17), /* print configuration info */
- ATA_EHI_SETMODE = (1 << 18), /* configure transfer mode */
- ATA_EHI_POST_SETMODE = (1 << 19), /* revaildating after setmode */
+ ATA_EHI_DID_SOFTRESET = (1 << 16), /* already soft-reset this port */
+ ATA_EHI_DID_HARDRESET = (1 << 17), /* already soft-reset this port */
+ ATA_EHI_PRINTINFO = (1 << 18), /* print configuration info */
+ ATA_EHI_SETMODE = (1 << 19), /* configure transfer mode */
+ ATA_EHI_POST_SETMODE = (1 << 20), /* revaildating after setmode */
+ ATA_EHI_DID_RESET = ATA_EHI_DID_SOFTRESET | ATA_EHI_DID_HARDRESET,
ATA_EHI_RESET_MODIFIER_MASK = ATA_EHI_RESUME_LINK,
/* max repeat if error condition is still set after ->error_handler */
@@ -367,34 +370,6 @@
void __iomem *scr_addr;
};
-struct ata_probe_ent {
- struct list_head node;
- struct device *dev;
- const struct ata_port_operations *port_ops;
- struct scsi_host_template *sht;
- struct ata_ioports port[ATA_MAX_PORTS];
- unsigned int n_ports;
- unsigned int dummy_port_mask;
- unsigned int pio_mask;
- unsigned int mwdma_mask;
- unsigned int udma_mask;
- unsigned long irq;
- unsigned long irq2;
- unsigned int irq_flags;
- unsigned long port_flags;
- unsigned long _host_flags;
- void __iomem * const *iomap;
- void *private_data;
-
- /* port_info for the secondary port. Together with irq2, it's
- * used to implement non-uniform secondary port. Currently,
- * the only user is ata_piix combined mode. This workaround
- * will be removed together with ata_probe_ent when init model
- * is updated.
- */
- const struct ata_port_info *pinfo2;
-};
-
struct ata_host {
spinlock_t lock;
struct device *dev;
@@ -427,6 +402,7 @@
int dma_dir;
unsigned int pad_len;
+ unsigned int sect_size;
unsigned int nbytes;
unsigned int curbytes;
@@ -472,6 +448,7 @@
struct scsi_device *sdev; /* attached SCSI device */
/* n_sector is used as CLEAR_OFFSET, read comment above CLEAR_OFFSET */
u64 n_sectors; /* size of device, if ATA */
+ u64 n_sectors_boot; /* size of ATA device at startup */
unsigned int class; /* ATA_DEV_xxx */
u16 id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */
u8 pio_mode;
@@ -597,11 +574,11 @@
struct ata_port_operations {
void (*port_disable) (struct ata_port *);
- void (*dev_config) (struct ata_port *, struct ata_device *);
+ void (*dev_config) (struct ata_device *);
void (*set_piomode) (struct ata_port *, struct ata_device *);
void (*set_dmamode) (struct ata_port *, struct ata_device *);
- unsigned long (*mode_filter) (const struct ata_port *, struct ata_device *, unsigned long);
+ unsigned long (*mode_filter) (struct ata_device *, unsigned long);
void (*tf_load) (struct ata_port *ap, const struct ata_taskfile *tf);
void (*tf_read) (struct ata_port *ap, struct ata_taskfile *tf);
@@ -616,6 +593,8 @@
void (*post_set_mode) (struct ata_port *ap);
+ int (*cable_detect) (struct ata_port *ap);
+
int (*check_atapi_dma) (struct ata_queued_cmd *qc);
void (*bmdma_setup) (struct ata_queued_cmd *qc);
@@ -664,6 +643,7 @@
unsigned long mwdma_mask;
unsigned long udma_mask;
const struct ata_port_operations *port_ops;
+ irq_handler_t irq_handler;
void *private_data;
};
@@ -686,6 +666,7 @@
extern const unsigned long sata_deb_timing_long[];
extern const struct ata_port_operations ata_dummy_port_ops;
+extern const struct ata_port_info ata_dummy_port_info;
static inline const unsigned long *
sata_ehc_deb_timing(struct ata_eh_context *ehc)
@@ -701,6 +682,7 @@
return ap->ops == &ata_dummy_port_ops;
}
+extern void sata_print_link_status(struct ata_port *ap);
extern void ata_port_probe(struct ata_port *);
extern void __sata_phy_reset(struct ata_port *ap);
extern void sata_phy_reset(struct ata_port *ap);
@@ -728,7 +710,15 @@
#endif
extern int ata_pci_clear_simplex(struct pci_dev *pdev);
#endif /* CONFIG_PCI */
-extern int ata_device_add(const struct ata_probe_ent *ent);
+extern struct ata_host *ata_host_alloc(struct device *dev, int max_ports);
+extern struct ata_host *ata_host_alloc_pinfo(struct device *dev,
+ const struct ata_port_info * const * ppi, int n_ports);
+extern int ata_host_start(struct ata_host *host);
+extern int ata_host_register(struct ata_host *host,
+ struct scsi_host_template *sht);
+extern int ata_host_activate(struct ata_host *host, int irq,
+ irq_handler_t irq_handler, unsigned long irq_flags,
+ struct scsi_host_template *sht);
extern void ata_host_detach(struct ata_host *host);
extern void ata_host_init(struct ata_host *, struct device *,
unsigned long, const struct ata_port_operations *);
@@ -828,11 +818,17 @@
extern int ata_scsi_change_queue_depth(struct scsi_device *sdev,
int queue_depth);
extern struct ata_device *ata_dev_pair(struct ata_device *adev);
+extern int ata_do_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev);
extern u8 ata_irq_on(struct ata_port *ap);
extern u8 ata_dummy_irq_on(struct ata_port *ap);
extern u8 ata_irq_ack(struct ata_port *ap, unsigned int chk_drq);
extern u8 ata_dummy_irq_ack(struct ata_port *ap, unsigned int chk_drq);
+extern int ata_cable_40wire(struct ata_port *ap);
+extern int ata_cable_80wire(struct ata_port *ap);
+extern int ata_cable_sata(struct ata_port *ap);
+extern int ata_cable_unknown(struct ata_port *ap);
+
/*
* Timing helpers
*/
@@ -870,10 +866,13 @@
unsigned long val;
};
-extern struct ata_probe_ent *
-ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int portmask);
+extern int ata_pci_init_native_host(struct ata_host *host,
+ unsigned int port_mask);
+extern int ata_pci_prepare_native_host(struct pci_dev *pdev,
+ const struct ata_port_info * const * ppi,
+ int n_ports, struct ata_host **r_host);
extern int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits);
-extern unsigned long ata_pci_default_filter(const struct ata_port *, struct ata_device *, unsigned long);
+extern unsigned long ata_pci_default_filter(struct ata_device *, unsigned long);
#endif /* CONFIG_PCI */
/*
@@ -1173,6 +1172,7 @@
qc->n_elem = 0;
qc->err_mask = 0;
qc->pad_len = 0;
+ qc->sect_size = ATA_SECT_SIZE;
ata_tf_init(qc->dev, &qc->tf);
@@ -1220,7 +1220,7 @@
static inline struct ata_port *ata_shost_to_port(struct Scsi_Host *host)
{
- return (struct ata_port *) &host->hostdata[0];
+ return *(struct ata_port **)&host->hostdata[0];
}
#endif /* __LINUX_LIBATA_H__ */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index a3ad762..9724910 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -838,6 +838,7 @@
void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr);
void __iomem * const * pcim_iomap_table(struct pci_dev *pdev);
int pcim_iomap_regions(struct pci_dev *pdev, u16 mask, const char *name);
+void pcim_iounmap_regions(struct pci_dev *pdev, u16 mask);
extern int pci_pci_problems;
#define PCIPCI_FAIL 1 /* No PCI PCI DMA */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 247b5e6..1b0ddbb 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -368,7 +368,6 @@
#define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379
#define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a
#define PCI_DEVICE_ID_ATI_IXP600_SATA 0x4380
-#define PCI_DEVICE_ID_ATI_IXP600_SRAID 0x4381
#define PCI_DEVICE_ID_ATI_IXP600_SMBUS 0x4385
#define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c
diff --git a/include/linux/sony-laptop.h b/include/linux/sony-laptop.h
new file mode 100644
index 0000000..e2e036d
--- /dev/null
+++ b/include/linux/sony-laptop.h
@@ -0,0 +1,34 @@
+#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_ */
diff --git a/kernel/resource.c b/kernel/resource.c
index bdb55a3..9bd14fd 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -213,27 +213,6 @@
EXPORT_SYMBOL(request_resource);
/**
- * ____request_resource - reserve a resource, with resource conflict returned
- * @root: root resource descriptor
- * @new: resource descriptor desired by caller
- *
- * Returns:
- * On success, NULL is returned.
- * On error, a pointer to the conflicting resource is returned.
- */
-struct resource *____request_resource(struct resource *root, struct resource *new)
-{
- struct resource *conflict;
-
- write_lock(&resource_lock);
- conflict = __request_resource(root, new);
- write_unlock(&resource_lock);
- return conflict;
-}
-
-EXPORT_SYMBOL(____request_resource);
-
-/**
* release_resource - release a previously reserved resource
* @old: resource pointer
*/
diff --git a/lib/devres.c b/lib/devres.c
index eb38849..b1d336c 100644
--- a/lib/devres.c
+++ b/lib/devres.c
@@ -296,5 +296,31 @@
return rc;
}
EXPORT_SYMBOL(pcim_iomap_regions);
+
+/**
+ * pcim_iounmap_regions - Unmap and release PCI BARs
+ * @pdev: PCI device to map IO resources for
+ * @mask: Mask of BARs to unmap and release
+ *
+ * Unamp and release regions specified by @mask.
+ */
+void pcim_iounmap_regions(struct pci_dev *pdev, u16 mask)
+{
+ void __iomem * const *iomap;
+ int i;
+
+ iomap = pcim_iomap_table(pdev);
+ if (!iomap)
+ return;
+
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ pcim_iounmap(pdev, iomap[i]);
+ pci_release_region(pdev, i);
+ }
+}
+EXPORT_SYMBOL(pcim_iounmap_regions);
#endif
#endif