Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Sun, 29 Apr 2007 17:48:48 +0000 (10:48 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Sun, 29 Apr 2007 17:48:48 +0000 (10:48 -0700)
* '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
  ...

117 files changed:
Documentation/ibm-acpi.txt [deleted file]
Documentation/kernel-parameters.txt
Documentation/sony-laptop.txt
Documentation/thinkpad-acpi.txt [new file with mode: 0644]
Documentation/video4linux/meye.txt
MAINTAINERS
arch/i386/defconfig
arch/i386/kernel/acpi/earlyquirk.c
arch/parisc/configs/c3000_defconfig
arch/x86_64/defconfig
drivers/acpi/Kconfig
drivers/acpi/Makefile
drivers/acpi/acpi_memhotplug.c
drivers/acpi/bus.c
drivers/acpi/container.c
drivers/acpi/dock.c
drivers/acpi/ec.c
drivers/acpi/i2c_ec.c [deleted file]
drivers/acpi/i2c_ec.h [deleted file]
drivers/acpi/ibm_acpi.c [deleted file]
drivers/acpi/processor_core.c
drivers/acpi/processor_idle.c
drivers/acpi/sbs.c
drivers/acpi/scan.c
drivers/acpi/sleep/proc.c
drivers/acpi/tables/tbfadt.c
drivers/ata/Kconfig
drivers/ata/Makefile
drivers/ata/ahci.c
drivers/ata/ata_generic.c
drivers/ata/ata_piix.c
drivers/ata/libata-core.c
drivers/ata/libata-eh.c
drivers/ata/libata-scsi.c
drivers/ata/libata-sff.c
drivers/ata/libata.h
drivers/ata/pata_ali.c
drivers/ata/pata_amd.c
drivers/ata/pata_artop.c
drivers/ata/pata_atiixp.c
drivers/ata/pata_cmd640.c [new file with mode: 0644]
drivers/ata/pata_cmd64x.c
drivers/ata/pata_cs5520.c
drivers/ata/pata_cs5530.c
drivers/ata/pata_cs5535.c
drivers/ata/pata_cypress.c
drivers/ata/pata_efar.c
drivers/ata/pata_hpt366.c
drivers/ata/pata_hpt37x.c
drivers/ata/pata_hpt3x2n.c
drivers/ata/pata_hpt3x3.c
drivers/ata/pata_isapnp.c
drivers/ata/pata_it8213.c
drivers/ata/pata_it821x.c
drivers/ata/pata_ixp4xx_cf.c
drivers/ata/pata_legacy.c
drivers/ata/pata_marvell.c
drivers/ata/pata_mpc52xx.c
drivers/ata/pata_mpiix.c
drivers/ata/pata_netcell.c
drivers/ata/pata_ns87410.c
drivers/ata/pata_oldpiix.c
drivers/ata/pata_opti.c
drivers/ata/pata_optidma.c
drivers/ata/pata_pcmcia.c
drivers/ata/pata_pdc2027x.c
drivers/ata/pata_pdc202xx_old.c
drivers/ata/pata_platform.c
drivers/ata/pata_qdi.c
drivers/ata/pata_radisys.c
drivers/ata/pata_rz1000.c
drivers/ata/pata_sc1200.c
drivers/ata/pata_scc.c
drivers/ata/pata_serverworks.c
drivers/ata/pata_sil680.c
drivers/ata/pata_sis.c
drivers/ata/pata_sl82c105.c
drivers/ata/pata_triflex.c
drivers/ata/pata_via.c
drivers/ata/pata_winbond.c
drivers/ata/pdc_adma.c
drivers/ata/sata_inic162x.c
drivers/ata/sata_mv.c
drivers/ata/sata_nv.c
drivers/ata/sata_promise.c
drivers/ata/sata_qstor.c
drivers/ata/sata_sil.c
drivers/ata/sata_sil24.c
drivers/ata/sata_sis.c
drivers/ata/sata_svw.c
drivers/ata/sata_sx4.c
drivers/ata/sata_uli.c
drivers/ata/sata_via.c
drivers/ata/sata_vsc.c
drivers/char/sonypi.c
drivers/clocksource/acpi_pm.c
drivers/media/video/Kconfig
drivers/media/video/meye.c
drivers/media/video/meye.h
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/asus-laptop.c
drivers/misc/sony-laptop.c
drivers/misc/thinkpad_acpi.c [new file with mode: 0644]
drivers/misc/thinkpad_acpi.h [new file with mode: 0644]
drivers/pci/pci-acpi.c
drivers/pci/quirks.c
drivers/scsi/ipr.c
include/acpi/actbl.h
include/linux/ata.h
include/linux/ioport.h
include/linux/libata.h
include/linux/pci.h
include/linux/pci_ids.h
include/linux/sony-laptop.h [new file with mode: 0644]
kernel/resource.c
lib/devres.c

diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt
deleted file mode 100644 (file)
index 0132d36..0000000
+++ /dev/null
@@ -1,728 +0,0 @@
-                   IBM ThinkPad ACPI Extras Driver
-
-                            Version 0.12
-                           17 August 2005
-
-               Borislav Deianov <borislav@users.sf.net>
-                     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.
-
-
-Status
-------
-
-The features currently supported are the following (see below for
-detailed description):
-
-       - Fn key combinations
-       - Bluetooth enable and disable
-       - video output switching, expansion control     
-       - ThinkLight on and off
-       - limited docking and undocking
-       - UltraBay eject
-       - CMOS control
-       - LED control
-       - ACPI sounds
-       - temperature sensors
-       - Experimental: embedded controller register dump
-       - LCD brightness control
-       - Volume control
-       - Experimental: fan speed, fan enable/disable
-       - Experimental: WAN enable and disable
-
-A compatibility table by model and feature is maintained on the web
-site, http://ibm-acpi.sf.net/. I appreciate any success or failure
-reports, especially if they add to or correct the compatibility table.
-Please include the following information in your report:
-
-       - ThinkPad model name
-       - a copy of your DSDT, from /proc/acpi/dsdt
-       - which driver features work and which don't
-       - the observed behavior of non-working features
-
-Any other comments or patches are also more than welcome.
-
-
-Installation
-------------
-
-If you are compiling this driver as included in the Linux kernel
-sources, simply enable the CONFIG_ACPI_IBM option (Power Management /
-ACPI / IBM ThinkPad Laptop Extras).
-
-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.
-
-Driver version -- /proc/acpi/ibm/driver
----------------------------------------
-
-The driver name and version. No commands can be written to this file.
-
-Hot keys -- /proc/acpi/ibm/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
-mask set (see below), the various hot keys generate ACPI events in the
-following format:
-
-       ibm/hotkey HKEY 00000080 0000xxxx
-
-The last four digits vary depending on the key combination pressed.
-All labeled Fn-Fx key combinations generate distinct events. In
-addition, the lid microswitch and some docking station buttons may
-also generate such events.
-
-The following commands can be written to this file:
-
-       echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature
-       echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature
-       echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys
-       echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys
-       ... any other 4-hex-digit mask ...
-       echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
-
-The bit mask allows some control over which hot keys generate ACPI
-events. Not all bits in the mask can be modified. Not all bits that
-can be modified do anything. Not all hot keys can be individually
-controlled by the mask. Most recent ThinkPad models honor the
-following bits (assuming the hot keys feature has been enabled):
-
-       key     bit     behavior when set       behavior when unset
-
-       Fn-F3                   always generates ACPI event
-       Fn-F4                   always generates ACPI event
-       Fn-F5   0010    generate ACPI event     enable/disable Bluetooth
-       Fn-F7   0040    generate ACPI event     switch LCD and external display
-       Fn-F8   0080    generate ACPI event     expand screen or none
-       Fn-F9   0100    generate ACPI event     none
-       Fn-F12                  always generates ACPI event
-
-Some models do not support all of the above. For example, the T30 does
-not support Fn-F5 and Fn-F9. Other models do not support the mask at
-all. On those models, hot keys cannot be controlled individually.
-
-Note that enabling ACPI events for some keys prevents their default
-behavior. For example, if events for Fn-F5 are enabled, that key will
-no longer enable/disable Bluetooth by itself. This can still be done
-from an acpid handler for the ibm/hotkey event.
-
-Note also that not all Fn key combinations are supported through
-ACPI. For example, on the X40, the brightness, volume and "Access IBM"
-buttons do not generate ACPI events even with this driver. They *can*
-be used through the "ThinkPad Buttons" utility, see
-http://www.nongnu.org/tpb/
-
-Bluetooth -- /proc/acpi/ibm/bluetooth
--------------------------------------
-
-This feature shows the presence and current state of a Bluetooth
-device. If Bluetooth is installed, the following commands can be used:
-
-       echo enable > /proc/acpi/ibm/bluetooth
-       echo disable > /proc/acpi/ibm/bluetooth
-
-Video output control -- /proc/acpi/ibm/video
---------------------------------------------
-
-This feature allows control over the devices used for video output -
-LCD, CRT or DVI (if available). The following commands are available:
-
-       echo lcd_enable > /proc/acpi/ibm/video
-       echo lcd_disable > /proc/acpi/ibm/video
-       echo crt_enable > /proc/acpi/ibm/video
-       echo crt_disable > /proc/acpi/ibm/video
-       echo dvi_enable > /proc/acpi/ibm/video
-       echo dvi_disable > /proc/acpi/ibm/video
-       echo auto_enable > /proc/acpi/ibm/video
-       echo auto_disable > /proc/acpi/ibm/video
-       echo expand_toggle > /proc/acpi/ibm/video
-       echo video_switch > /proc/acpi/ibm/video
-
-Each video output device can be enabled or disabled individually.
-Reading /proc/acpi/ibm/video shows the status of each device.
-
-Automatic video switching can be enabled or disabled.  When automatic
-video switching is enabled, certain events (e.g. opening the lid,
-docking or undocking) cause the video output device to change
-automatically. While this can be useful, it also causes flickering
-and, on the X40, video corruption. By disabling automatic switching,
-the flickering or video corruption can be avoided.
-
-The video_switch command cycles through the available video outputs
-(it simulates the behavior of Fn-F7).
-
-Video expansion can be toggled through this feature. This controls
-whether the display is expanded to fill the entire LCD screen when a
-mode with less than full resolution is used. Note that the current
-video expansion status cannot be determined through this feature.
-
-Note that on many models (particularly those using Radeon graphics
-chips) the X driver configures the video card in a way which prevents
-Fn-F7 from working. This also disables the video output switching
-features of this driver, as it uses the same ACPI methods as
-Fn-F7. Video switching on the console should still work.
-
-UPDATE: There's now a patch for the X.org Radeon driver which
-addresses this issue. Some people are reporting success with the patch
-while others are still having problems. For more information:
-
-https://bugs.freedesktop.org/show_bug.cgi?id=2000
-
-ThinkLight control -- /proc/acpi/ibm/light
-------------------------------------------
-
-The current status of the ThinkLight can be found in this file. A few
-models which do not make the status available will show it as
-"unknown". The available commands are:
-
-       echo on  > /proc/acpi/ibm/light
-       echo off > /proc/acpi/ibm/light
-
-Docking / undocking -- /proc/acpi/ibm/dock
-------------------------------------------
-
-Docking and undocking (e.g. with the X4 UltraBase) requires some
-actions to be taken by the operating system to safely make or break
-the electrical connections with the dock.
-
-The docking feature of this driver generates the following ACPI events:
-
-       ibm/dock GDCK 00000003 00000001 -- eject request
-       ibm/dock GDCK 00000003 00000002 -- undocked
-       ibm/dock GDCK 00000000 00000003 -- docked
-
-NOTE: These events will only be generated if the laptop was docked
-when originally booted. This is due to the current lack of support for
-hot plugging of devices in the Linux ACPI framework. If the laptop was
-booted while not in the dock, the following message is shown in the
-logs:
-
-       Mar 17 01:42:34 aero kernel: ibm_acpi: dock device not present
-
-In this case, no dock-related events are generated but the dock and
-undock commands described below still work. They can be executed
-manually or triggered by Fn key combinations (see the example acpid
-configuration files included in the driver tarball package available
-on the web site).
-
-When the eject request button on the dock is pressed, the first event
-above is generated. The handler for this event should issue the
-following command:
-
-       echo undock > /proc/acpi/ibm/dock
-
-After the LED on the dock goes off, it is safe to eject the laptop.
-Note: if you pressed this key by mistake, go ahead and eject the
-laptop, then dock it back in. Otherwise, the dock may not function as
-expected.
-
-When the laptop is docked, the third event above is generated. The
-handler for this event should issue the following command to fully
-enable the dock:
-
-       echo dock > /proc/acpi/ibm/dock
-
-The contents of the /proc/acpi/ibm/dock file shows the current status
-of the dock, as provided by the ACPI framework.
-
-The docking support in this driver does not take care of enabling or
-disabling any other devices you may have attached to the dock. For
-example, a CD drive plugged into the UltraBase needs to be disabled or
-enabled separately. See the provided example acpid configuration files
-for how this can be accomplished.
-
-There is no support yet for PCI devices that may be attached to a
-docking station, e.g. in the ThinkPad Dock II. The driver currently
-does not recognize, enable or disable such devices. This means that
-the only docking stations currently supported are the X-series
-UltraBase docks and "dumb" port replicators like the Mini Dock (the
-latter don't need any ACPI support, actually).
-
-UltraBay eject -- /proc/acpi/ibm/bay
-------------------------------------
-
-Inserting or ejecting an UltraBay device requires some actions to be
-taken by the operating system to safely make or break the electrical
-connections with the device.
-
-This feature generates the following ACPI events:
-
-       ibm/bay MSTR 00000003 00000000 -- eject request
-       ibm/bay MSTR 00000001 00000000 -- eject lever inserted
-
-NOTE: These events will only be generated if the UltraBay was present
-when the laptop was originally booted (on the X series, the UltraBay
-is in the dock, so it may not be present if the laptop was undocked).
-This is due to the current lack of support for hot plugging of devices
-in the Linux ACPI framework. If the laptop was booted without the
-UltraBay, the following message is shown in the logs:
-
-       Mar 17 01:42:34 aero kernel: ibm_acpi: bay device not present
-
-In this case, no bay-related events are generated but the eject
-command described below still works. It can be executed manually or
-triggered by a hot key combination.
-
-Sliding the eject lever generates the first event shown above. The
-handler for this event should take whatever actions are necessary to
-shut down the device in the UltraBay (e.g. call idectl), then issue
-the following command:
-
-       echo eject > /proc/acpi/ibm/bay
-
-After the LED on the UltraBay goes off, it is safe to pull out the
-device.
-
-When the eject lever is inserted, the second event above is
-generated. The handler for this event should take whatever actions are
-necessary to enable the UltraBay device (e.g. call idectl).
-
-The contents of the /proc/acpi/ibm/bay file shows the current status
-of the UltraBay, as provided by the ACPI framework.
-
-EXPERIMENTAL warm eject support on the 600e/x, A22p and A3x (To use
-this feature, you need to supply the experimental=1 parameter when
-loading the module):
-
-These models do not have a button near the UltraBay device to request
-a hot eject but rather require the laptop to be put to sleep
-(suspend-to-ram) before the bay device is ejected or inserted).
-The sequence of steps to eject the device is as follows:
-
-       echo eject > /proc/acpi/ibm/bay
-       put the ThinkPad to sleep
-       remove the drive
-       resume from sleep
-       cat /proc/acpi/ibm/bay should show that the drive was removed
-
-On the A3x, both the UltraBay 2000 and UltraBay Plus devices are
-supported. Use "eject2" instead of "eject" for the second bay.
-
-Note: the UltraBay eject support on the 600e/x, A22p and A3x is
-EXPERIMENTAL and may not work as expected. USE WITH CAUTION!
-
-CMOS control -- /proc/acpi/ibm/cmos
------------------------------------
-
-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):
-
-       0 - no effect but tpb reports "Volume down"
-       1 - no effect but tpb reports "Volume up"
-       2 - no effect but tpb reports "Mute on"
-       3 - simulate pressing the "Access IBM" button
-       4 - LCD brightness up
-       5 - LCD brightness down
-       11 - toggle screen expansion
-       12 - ThinkLight on
-       13 - ThinkLight off
-       14 - no effect but tpb reports ThinkLight status change
-
-LED control -- /proc/acpi/ibm/led
----------------------------------
-
-Some of the LED indicators can be controlled through this feature. The
-available commands are:
-
-       echo '<led number> on' >/proc/acpi/ibm/led
-       echo '<led number> off' >/proc/acpi/ibm/led
-       echo '<led number> blink' >/proc/acpi/ibm/led
-
-The <led number> range is 0 to 7. The set of LEDs that can be
-controlled varies from model to model. Here is the mapping on the X40:
-
-       0 - power
-       1 - battery (orange)
-       2 - battery (green)
-       3 - UltraBase
-       4 - UltraBay
-       7 - standby
-
-All of the above can be turned on and off and can be made to blink.
-
-ACPI sounds -- /proc/acpi/ibm/beep
-----------------------------------
-
-The BEEP method is used internally by the ACPI firmware to provide
-audible alerts in various situations. This feature allows the same
-sounds to be triggered manually.
-
-The commands are non-negative integer numbers:
-
-       echo <number> >/proc/acpi/ibm/beep
-
-The valid <number> range is 0 to 17. Not all numbers trigger sounds
-and the sounds vary from model to model. Here is the behavior on the
-X40:
-
-       0 - stop a sound in progress (but use 17 to stop 16)
-       2 - two beeps, pause, third beep ("low battery")
-       3 - single beep
-       4 - high, followed by low-pitched beep ("unable")
-       5 - single beep
-       6 - very high, followed by high-pitched beep ("AC/DC")
-       7 - high-pitched beep
-       9 - three short beeps
-       10 - very long beep
-       12 - low-pitched beep
-       15 - three high-pitched beeps repeating constantly, stop with 0
-       16 - one medium-pitched beep repeating constantly, stop with 17
-       17 - stop 16
-
-Temperature sensors -- /proc/acpi/ibm/thermal
----------------------------------------------
-
-Most ThinkPads include six or more separate temperature sensors but
-only expose the CPU temperature through the standard ACPI methods.
-This feature shows readings from up to eight different sensors on older
-ThinkPads, and it has experimental support for up to sixteen different
-sensors on newer ThinkPads.  Readings from sensors that are not available
-return -128.
-
-No commands can be written to this file.
-
-EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the
-implementation directly accesses hardware registers and may not work as
-expected. USE WITH CAUTION! To use this feature, you need to supply the
-experimental=1 parameter when loading the module.  When EXPERIMENTAL
-mode is enabled, reading the first 8 sensors on newer ThinkPads will
-also use an new experimental thermal sensor access mode.
-
-For example, on the X40, a typical output may be:
-temperatures:   42 42 45 41 36 -128 33 -128
-
-EXPERIMENTAL: On the T43/p, a typical output may be:
-temperatures:   48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128
-
-The mapping of thermal sensors to physical locations varies depending on
-system-board model (and thus, on ThinkPad model).
-
-http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that
-tries to track down these locations for various models.
-
-Most (newer?) models seem to follow this pattern:
-
-1:  CPU
-2:  (depends on model)
-3:  (depends on model)
-4:  GPU
-5:  Main battery: main sensor
-6:  Bay battery: main sensor
-7:  Main battery: secondary sensor
-8:  Bay battery: secondary sensor
-9-15: (depends on model)
-
-For the R51 (source: Thomas Gruber):
-2:  Mini-PCI
-3:  Internal HDD
-
-For the T43, T43/p (source: Shmidoax/Thinkwiki.org)
-http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p
-2:  System board, left side (near PCMCIA slot), reported as HDAPS temp
-3:  PCMCIA slot
-9:  MCH (northbridge) to DRAM Bus
-10: ICH (southbridge), under Mini-PCI card, under touchpad
-11: Power regulator, underside of system board, below F2 key
-
-The A31 has a very atypical layout for the thermal sensors
-(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31)
-1:  CPU
-2:  Main Battery: main sensor
-3:  Power Converter
-4:  Bay Battery: main sensor
-5:  MCH (northbridge)
-6:  PCMCIA/ambient
-7:  Main Battery: secondary sensor
-8:  Bay Battery: secondary sensor
-
-
-EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump
-------------------------------------------------------------------------
-
-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 dumps the values of 256 embedded controller
-registers. Values which have changed since the last time the registers
-were dumped are marked with a star:
-
-[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump 
-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
-EC 0x20:  00  00  00  00  00  00  00  00  00  00  00  03  43  00  00  80
-EC 0x30:  01  07  1a  00  30  04  00  00 *85  00  00  10  00  50  00  00
-EC 0x40:  00  00  00  00  00  00  14  01  00  04  00  00  00  00  00  00
-EC 0x50:  00  c0  02  0d  00  01  01  02  02  03  03  03  03 *bc *02 *bc
-EC 0x60: *02 *bc *02  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0x70:  00  00  00  00  00  12  30  40 *24 *26 *2c *27 *20  80 *1f  80
-EC 0x80:  00  00  00  06 *37 *0e  03  00  00  00  0e  07  00  00  00  00
-EC 0x90:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xa0: *ff  09  ff  09  ff  ff *64  00 *00 *00 *a2  41 *ff *ff *e0  00
-EC 0xb0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xc0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xd0:  03  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xe0:  00  00  00  00  00  00  00  00  11  20  49  04  24  06  55  03
-EC 0xf0:  31  55  48  54  35  38  57  57  08  2f  45  73  07  65  6c  1a
-
-This feature can be used to determine the register holding the fan
-speed on some models. To do that, do the following:
-
-       - make sure the battery is fully charged
-       - make sure the fan is running
-       - run 'cat /proc/acpi/ibm/ecdump' several times, once per second or so
-
-The first step makes sure various charging-related values don't
-vary. The second ensures that the fan-related values do vary, since
-the fan speed fluctuates a bit. The third will (hopefully) mark the
-fan register with a star:
-
-[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump 
-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
-EC 0x20:  00  00  00  00  00  00  00  00  00  00  00  03  43  00  00  80
-EC 0x30:  01  07  1a  00  30  04  00  00  85  00  00  10  00  50  00  00
-EC 0x40:  00  00  00  00  00  00  14  01  00  04  00  00  00  00  00  00
-EC 0x50:  00  c0  02  0d  00  01  01  02  02  03  03  03  03  bc  02  bc
-EC 0x60:  02  bc  02  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0x70:  00  00  00  00  00  12  30  40  24  27  2c  27  21  80  1f  80
-EC 0x80:  00  00  00  06 *be  0d  03  00  00  00  0e  07  00  00  00  00
-EC 0x90:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xa0:  ff  09  ff  09  ff  ff  64  00  00  00  a2  41  ff  ff  e0  00
-EC 0xb0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xc0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xd0:  03  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xe0:  00  00  00  00  00  00  00  00  11  20  49  04  24  06  55  03
-EC 0xf0:  31  55  48  54  35  38  57  57  08  2f  45  73  07  65  6c  1a
-
-Another set of values that varies often is the temperature
-readings. Since temperatures don't change vary fast, you can take
-several quick dumps to eliminate them.
-
-You can use a similar method to figure out the meaning of other
-embedded controller registers - e.g. make sure nothing else changes
-except the charging or discharging battery to determine which
-registers contain the current battery capacity, etc. If you experiment
-with this, do send me your results (including some complete dumps with
-a description of the conditions when they were taken.)
-
-LCD brightness control -- /proc/acpi/ibm/brightness
----------------------------------------------------
-
-This feature allows software control of the LCD brightness on ThinkPad
-models which don't have a hardware brightness slider. 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.
-
-Volume control -- /proc/acpi/ibm/volume
----------------------------------------
-
-This feature allows volume control on ThinkPad models which don't have
-a hardware volume knob. The available commands are:
-
-       echo up   >/proc/acpi/ibm/volume
-       echo down >/proc/acpi/ibm/volume
-       echo mute >/proc/acpi/ibm/volume
-       echo 'level <level>' >/proc/acpi/ibm/volume
-
-The <level> number range is 0 to 15 although not all of them may be
-distinct. The unmute the volume after the mute command, use either the
-up or down command (the level command will not unmute the volume).
-The current volume level and mute state is shown in the file.
-
-EXPERIMENTAL: fan speed, fan enable/disable -- /proc/acpi/ibm/fan
------------------------------------------------------------------
-
-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 attempts to show the current fan speed, control mode and
-other fan data that might be available.  The speed is read directly
-from the hardware registers of the embedded controller.  This is known
-to work on later R, T and X series ThinkPads but may show a bogus
-value on other models.
-
-Most ThinkPad fans work in "levels".  Level 0 stops the fan.  The higher
-the level, the higher the fan speed, although adjacent levels often map
-to the same fan speed.  7 is the highest level, where the fan reaches
-the maximum recommended speed.  Level "auto" means the EC changes the
-fan level according to some internal algorithm, usually based on
-readings from the thermal sensors.  Level "disengaged" means the EC
-disables the speed-locked closed-loop fan control, and drives the fan as
-fast as it can go, which might exceed hardware limits, so use this level
-with caution.
-
-The fan usually ramps up or down slowly from one speed to another,
-and it is normal for the EC to take several seconds to react to fan
-commands.
-
-The fan may be enabled or disabled with the following commands:
-
-       echo enable  >/proc/acpi/ibm/fan
-       echo disable >/proc/acpi/ibm/fan
-
-Placing a fan on level 0 is the same as disabling it.  Enabling a fan
-will try to place it in a safe level if it is too slow or disabled.
-
-WARNING WARNING WARNING: do not leave the fan disabled unless you are
-monitoring all of the temperature sensor readings and you are ready to
-enable it if necessary to avoid overheating.
-
-An enabled fan in level "auto" may stop spinning if the EC decides the
-ThinkPad is cool enough and doesn't need the extra airflow.  This is
-normal, and the EC will spin the fan up if the varios thermal readings
-rise too much.
-
-On the X40, this seems to depend on the CPU and HDD temperatures.
-Specifically, the fan is turned on when either the CPU temperature
-climbs to 56 degrees or the HDD temperature climbs to 46 degrees.  The
-fan is turned off when the CPU temperature drops to 49 degrees and the
-HDD temperature drops to 41 degrees.  These thresholds cannot
-currently be controlled.
-
-The fan level can be controlled with the command:
-
-       echo 'level <level>' > /proc/acpi/ibm/thermal
-
-Where <level> is an integer from 0 to 7, or one of the words "auto"
-or "disengaged" (without the quotes).  Not all ThinkPads support the
-"auto" and "disengaged" levels.
-
-On the X31 and X40 (and ONLY on those models), the fan speed can be
-controlled to a certain degree. Once the fan is running, it can be
-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.
-
-EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan
----------------------------------------
-
-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:
-
-       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.
-
-Multiple Commands, Module Parameters
-------------------------------------
-
-Multiple commands can be written to the proc files in one shot by
-separating them with commas, for example:
-
-       echo enable,0xffff > /proc/acpi/ibm/hotkey
-       echo lcd_disable,crt_enable > /proc/acpi/ibm/video
-
-Commands can also be specified when loading the ibm_acpi module, for
-example:
-
-       modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable
-
-The ibm-acpi kernel driver can be programmed to revert the fan level
-to a safe setting if userspace does not issue one of the fan commands:
-"enable", "disable", "level" or "watchdog" within a configurable
-ammount of time.  To do this, use the "watchdog" command.
-
-       echo 'watchdog <interval>' > /proc/acpi/ibm/fan
-
-Interval is the ammount of time in seconds to wait for one of the
-above mentioned fan commands before reseting the fan level to a safe
-one.  If set to zero, the watchdog is disabled (default).  When the
-watchdog timer runs out, it does the exact equivalent of the "enable"
-fan command.
-
-Note that the watchdog timer stops after it enables the fan.  It will
-be rearmed again automatically (using the same interval) when one of
-the above mentioned fan commands is received.  The fan watchdog is,
-therefore, not suitable to protect against fan mode changes made
-through means other than the "enable", "disable", and "level" fan
-commands.
-
-
-Example Configuration
----------------------
-
-The ACPI support in the kernel is intended to be used in conjunction
-with a user-space daemon, acpid. The configuration files for this
-daemon control what actions are taken in response to various ACPI
-events. An example set of configuration files are included in the
-config/ directory of the tarball package available on the web
-site. Note that these are provided for illustration purposes only and
-may need to be adapted to your particular setup.
-
-The following utility scripts are used by the example action
-scripts (included with ibm-acpi for completeness):
-
-       /usr/local/sbin/idectl -- from the hdparm source distribution,
-               see http://www.ibiblio.org/pub/Linux/system/hardware
-       /usr/local/sbin/laptop_mode -- from the Linux kernel source
-               distribution, see Documentation/laptop-mode.txt
-       /sbin/service -- comes with Redhat/Fedora distributions
-       /usr/sbin/hibernate -- from the Software Suspend 2 distribution,
-               see http://softwaresuspend.berlios.de/
-
-Toan T Nguyen <ntt@physics.ucla.edu> notes that Suse uses the
-powersave program to suspend ('powersave --suspend-to-ram') or
-hibernate ('powersave --suspend-to-disk'). This means that the
-hibernate script is not needed on that distribution.
-
-Henrik Brix Andersen <brix@gentoo.org> has written a Gentoo ACPI event
-handler script for the X31. You can get the latest version from
-http://dev.gentoo.org/~brix/files/x31.sh
-
-David Schweikert <dws@ee.eth.ch> has written an alternative blank.sh
-script which works on Debian systems. This scripts has now been
-extended to also work on Fedora systems and included as the default
-blank.sh in the distribution.
index 2017942e09664832589f4148066570e418489e00..84c3bd05c639df4510ae484f900f23865b2a46d8 100644 (file)
@@ -181,19 +181,41 @@ and is between 256 and 4096 characters. It is defined in the file
                        that require a timer override, but don't have
                        HPET
 
-       acpi_dbg_layer= [HW,ACPI]
+       acpi.debug_layer=       [HW,ACPI]
                        Format: <int>
                        Each bit of the <int> indicates an ACPI debug layer,
                        1: enable, 0: disable. It is useful for boot time
                        debugging. After system has booted up, it can be set
-                       via /proc/acpi/debug_layer.
-
-       acpi_dbg_level= [HW,ACPI]
+                       via /sys/module/acpi/parameters/debug_layer.
+                       CONFIG_ACPI_DEBUG must be enabled for this to produce any output.
+                       Available bits (add the numbers together) to enable debug output
+                       for specific parts of the ACPI subsystem:
+                       0x01 utilities 0x02 hardware 0x04 events 0x08 tables
+                       0x10 namespace 0x20 parser 0x40 dispatcher
+                       0x80 executer 0x100 resources 0x200 acpica debugger
+                       0x400 os services 0x800 acpica disassembler.
+                       The number can be in decimal or prefixed with 0x in hex.
+                       Warning: Many of these options can produce a lot of
+                       output and make your system unusable. Be very careful.
+
+       acpi.debug_level=       [HW,ACPI]
                        Format: <int>
                        Each bit of the <int> indicates an ACPI debug level,
                        1: enable, 0: disable. It is useful for boot time
                        debugging. After system has booted up, it can be set
-                       via /proc/acpi/debug_level.
+                       via /sys/module/acpi/parameters/debug_level.
+                       CONFIG_ACPI_DEBUG must be enabled for this to produce any output.
+                       Available bits (add the numbers together) to enable different
+                       debug output levels of the ACPI subsystem:
+                       0x01 error 0x02 warn 0x04 init 0x08 debug object
+                       0x10 info 0x20 init names 0x40 parse 0x80 load
+                       0x100 dispatch 0x200 execute 0x400 names 0x800 operation region
+                       0x1000 bfield 0x2000 tables 0x4000 values 0x8000 objects
+                       0x10000 resources 0x20000 user requests 0x40000 package.
+                       The number can be in decimal or prefixed with 0x in hex.
+                       Warning: Many of these options can produce a lot of
+                       output and make your system unusable. Be very careful.
+
 
        acpi_fake_ecdt  [HW,ACPI] Workaround failure due to BIOS lacking ECDT
 
index dfd26df056f4039fcf35c87548424ee301c21850..7a5c1a81905c66940fe2c3a2d516b237f957b970 100644 (file)
@@ -3,12 +3,18 @@ Sony Notebook Control Driver (SNC) Readme
        Copyright (C) 2004- 2005 Stelian Pop <stelian@popies.net>
        Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
 
-This mini-driver drives the SNC device present in the ACPI BIOS of
-the Sony Vaio laptops.
+This mini-driver drives the SNC and SPIC device present in the ACPI BIOS of the
+Sony Vaio laptops. This driver mixes both devices functions under the same
+(hopefully consistent) interface. This also means that the sonypi driver is
+obsoleted by sony-laptop now.
 
-It gives access to some extra laptop functionalities. In its current
-form, this driver let the user set or query the screen brightness
-through the backlight subsystem and remove/apply power to some devices.
+Fn keys (hotkeys):
+------------------
+Some models report hotkeys through the SNC or SPIC devices, such events are
+reported both through the ACPI subsystem as acpi events and through the INPUT
+subsystem. See the logs of acpid or /proc/acpi/event and
+/proc/bus/input/devices to find out what those events are and which input
+devices are created by the driver.
 
 Backlight control:
 ------------------
@@ -39,6 +45,8 @@ The files are:
        audiopower              power on/off the internal sound card
        lanpower                power on/off the internal ethernet card
                                (only in debug mode)
+       bluetoothpower          power on/off the internal bluetooth device
+       fanspeed                get/set the fan speed
 
 Note that some files may be missing if they are not supported
 by your particular laptop model.
@@ -76,9 +84,9 @@ The sony-laptop driver creates, for some of those methods (the most
 current ones found on several Vaio models), an entry under
 /sys/devices/platform/sony-laptop, just like the 'cdpower' one.
 You can create other entries corresponding to your own laptop methods by
-further editing the source (see the 'sony_acpi_values' table, and add a new
+further editing the source (see the 'sony_nc_values' table, and add a new
 entry to this table with your get/set method names using the
-HANDLE_NAMES macro).
+SNC_HANDLE_NAMES macro).
 
 Your mission, should you accept it, is to try finding out what
 those entries are for, by reading/writing random values from/to those
@@ -87,6 +95,9 @@ files and find out what is the impact on your laptop.
 Should you find anything interesting, please report it back to me,
 I will not disavow all knowledge of your actions :)
 
+See also http://www.linux.it/~malattia/wiki/index.php/Sony_drivers for other
+useful info.
+
 Bugs/Limitations:
 -----------------
 
diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt
new file mode 100644 (file)
index 0000000..2d48033
--- /dev/null
@@ -0,0 +1,981 @@
+                    ThinkPad ACPI Extras Driver
+
+                            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 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
+------
+
+The features currently supported are the following (see below for
+detailed description):
+
+       - Fn key combinations
+       - Bluetooth enable and disable
+       - video output switching, expansion control
+       - ThinkLight on and off
+       - limited docking and undocking
+       - UltraBay eject
+       - CMOS control
+       - LED control
+       - ACPI sounds
+       - temperature sensors
+       - Experimental: embedded controller register dump
+       - LCD brightness control
+       - Volume control
+       - 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
+site, http://ibm-acpi.sf.net/. I appreciate any success or failure
+reports, especially if they add to or correct the compatibility table.
+Please include the following information in your report:
+
+       - ThinkPad model name
+       - a copy of your DSDT, from /proc/acpi/dsdt
+       - a copy of the output of dmidecode, with serial numbers
+         and UUIDs masked off
+       - which driver features work and which don't
+       - the observed behavior of non-working features
+
+Any other comments or patches are also more than welcome.
+
+
+Installation
+------------
+
+If you are compiling this driver as included in the Linux kernel
+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 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.
+
+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.
+
+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
+mask set (see below), the various hot keys generate ACPI events in the
+following format:
+
+       ibm/hotkey HKEY 00000080 0000xxxx
+
+The last four digits vary depending on the key combination pressed.
+All labeled Fn-Fx key combinations generate distinct events. In
+addition, the lid microswitch and some docking station buttons may
+also generate such events.
+
+The 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
+controlled by the mask. Most recent ThinkPad models honor the
+following bits (assuming the hot keys feature has been enabled):
+
+       key     bit     behavior when set       behavior when unset
+
+       Fn-F3                   always generates ACPI event
+       Fn-F4                   always generates ACPI event
+       Fn-F5   0010    generate ACPI event     enable/disable Bluetooth
+       Fn-F7   0040    generate ACPI event     switch LCD and external display
+       Fn-F8   0080    generate ACPI event     expand screen or none
+       Fn-F9   0100    generate ACPI event     none
+       Fn-F12                  always generates ACPI event
+
+Some models do not support all of the above. For example, the T30 does
+not support Fn-F5 and Fn-F9. Other models do not support the mask at
+all. On those models, hot keys cannot be controlled individually.
+
+Note that enabling ACPI events for some keys prevents their default
+behavior. For example, if events for Fn-F5 are enabled, that key will
+no longer enable/disable Bluetooth by itself. This can still be done
+from an acpid handler for the ibm/hotkey event.
+
+Note also that not all Fn key combinations are supported through
+ACPI. For example, on the X40, the brightness, volume and "Access IBM"
+buttons do not generate ACPI events even with this driver. They *can*
+be used through the "ThinkPad Buttons" utility, see
+http://www.nongnu.org/tpb/
+
+procfs notes:
+
+The following commands can be written to the /proc/acpi/ibm/hotkey file:
+
+       echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature
+       echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature
+       echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys
+       echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys
+       ... any other 4-hex-digit mask ...
+       echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
+
+sysfs notes:
+
+       The hot keys attributes are in a hotkey/ subdirectory off the
+       thinkpad device.
+
+       bios_enabled:
+               Returns the status of the hot keys feature when
+               thinkpad-acpi was loaded.  Upon module unload, the hot
+               key feature status will be restored to this value.
+
+               0: hot keys were disabled
+               1: hot keys were enabled
+
+       bios_mask:
+               Returns the hot keys mask when thinkpad-acpi was loaded.
+               Upon module unload, the hot keys mask will be restored
+               to this value.
+
+       enable:
+               Enables/disables the hot keys feature, and reports
+               current status of the hot keys feature.
+
+               0: disables the hot keys feature / feature disabled
+               1: enables the hot keys feature / feature enabled
+
+       mask:
+               bit mask to enable ACPI event generation for each hot
+               key (see above).  Returns the current status of the hot
+               keys mask, and allows one to modify it.
+
+
+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
+--------------------------------------------
+
+This feature allows control over the devices used for video output -
+LCD, CRT or DVI (if available). The following commands are available:
+
+       echo lcd_enable > /proc/acpi/ibm/video
+       echo lcd_disable > /proc/acpi/ibm/video
+       echo crt_enable > /proc/acpi/ibm/video
+       echo crt_disable > /proc/acpi/ibm/video
+       echo dvi_enable > /proc/acpi/ibm/video
+       echo dvi_disable > /proc/acpi/ibm/video
+       echo auto_enable > /proc/acpi/ibm/video
+       echo auto_disable > /proc/acpi/ibm/video
+       echo expand_toggle > /proc/acpi/ibm/video
+       echo video_switch > /proc/acpi/ibm/video
+
+Each video output device can be enabled or disabled individually.
+Reading /proc/acpi/ibm/video shows the status of each device.
+
+Automatic video switching can be enabled or disabled.  When automatic
+video switching is enabled, certain events (e.g. opening the lid,
+docking or undocking) cause the video output device to change
+automatically. While this can be useful, it also causes flickering
+and, on the X40, video corruption. By disabling automatic switching,
+the flickering or video corruption can be avoided.
+
+The video_switch command cycles through the available video outputs
+(it simulates the behavior of Fn-F7).
+
+Video expansion can be toggled through this feature. This controls
+whether the display is expanded to fill the entire LCD screen when a
+mode with less than full resolution is used. Note that the current
+video expansion status cannot be determined through this feature.
+
+Note that on many models (particularly those using Radeon graphics
+chips) the X driver configures the video card in a way which prevents
+Fn-F7 from working. This also disables the video output switching
+features of this driver, as it uses the same ACPI methods as
+Fn-F7. Video switching on the console should still work.
+
+UPDATE: There's now a patch for the X.org Radeon driver which
+addresses this issue. Some people are reporting success with the patch
+while others are still having problems. For more information:
+
+https://bugs.freedesktop.org/show_bug.cgi?id=2000
+
+ThinkLight control -- /proc/acpi/ibm/light
+------------------------------------------
+
+The current status of the ThinkLight can be found in this file. A few
+models which do not make the status available will show it as
+"unknown". The available commands are:
+
+       echo on  > /proc/acpi/ibm/light
+       echo off > /proc/acpi/ibm/light
+
+Docking / undocking -- /proc/acpi/ibm/dock
+------------------------------------------
+
+Docking and undocking (e.g. with the X4 UltraBase) requires some
+actions to be taken by the operating system to safely make or break
+the electrical connections with the dock.
+
+The docking feature of this driver generates the following ACPI events:
+
+       ibm/dock GDCK 00000003 00000001 -- eject request
+       ibm/dock GDCK 00000003 00000002 -- undocked
+       ibm/dock GDCK 00000000 00000003 -- docked
+
+NOTE: These events will only be generated if the laptop was docked
+when originally booted. This is due to the current lack of support for
+hot plugging of devices in the Linux ACPI framework. If the laptop was
+booted while not in the dock, the following message is shown in the
+logs:
+
+       Mar 17 01:42:34 aero kernel: 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
+manually or triggered by Fn key combinations (see the example acpid
+configuration files included in the driver tarball package available
+on the web site).
+
+When the eject request button on the dock is pressed, the first event
+above is generated. The handler for this event should issue the
+following command:
+
+       echo undock > /proc/acpi/ibm/dock
+
+After the LED on the dock goes off, it is safe to eject the laptop.
+Note: if you pressed this key by mistake, go ahead and eject the
+laptop, then dock it back in. Otherwise, the dock may not function as
+expected.
+
+When the laptop is docked, the third event above is generated. The
+handler for this event should issue the following command to fully
+enable the dock:
+
+       echo dock > /proc/acpi/ibm/dock
+
+The contents of the /proc/acpi/ibm/dock file shows the current status
+of the dock, as provided by the ACPI framework.
+
+The docking support in this driver does not take care of enabling or
+disabling any other devices you may have attached to the dock. For
+example, a CD drive plugged into the UltraBase needs to be disabled or
+enabled separately. See the provided example acpid configuration files
+for how this can be accomplished.
+
+There is no support yet for PCI devices that may be attached to a
+docking station, e.g. in the ThinkPad Dock II. The driver currently
+does not recognize, enable or disable such devices. This means that
+the only docking stations currently supported are the X-series
+UltraBase docks and "dumb" port replicators like the Mini Dock (the
+latter don't need any ACPI support, actually).
+
+UltraBay eject -- /proc/acpi/ibm/bay
+------------------------------------
+
+Inserting or ejecting an UltraBay device requires some actions to be
+taken by the operating system to safely make or break the electrical
+connections with the device.
+
+This feature generates the following ACPI events:
+
+       ibm/bay MSTR 00000003 00000000 -- eject request
+       ibm/bay MSTR 00000001 00000000 -- eject lever inserted
+
+NOTE: These events will only be generated if the UltraBay was present
+when the laptop was originally booted (on the X series, the UltraBay
+is in the dock, so it may not be present if the laptop was undocked).
+This is due to the current lack of support for hot plugging of devices
+in the Linux ACPI framework. If the laptop was booted without the
+UltraBay, the following message is shown in the logs:
+
+       Mar 17 01:42:34 aero kernel: 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
+triggered by a hot key combination.
+
+Sliding the eject lever generates the first event shown above. The
+handler for this event should take whatever actions are necessary to
+shut down the device in the UltraBay (e.g. call idectl), then issue
+the following command:
+
+       echo eject > /proc/acpi/ibm/bay
+
+After the LED on the UltraBay goes off, it is safe to pull out the
+device.
+
+When the eject lever is inserted, the second event above is
+generated. The handler for this event should take whatever actions are
+necessary to enable the UltraBay device (e.g. call idectl).
+
+The contents of the /proc/acpi/ibm/bay file shows the current status
+of the UltraBay, as provided by the ACPI framework.
+
+EXPERIMENTAL warm eject support on the 600e/x, A22p and A3x (To use
+this feature, you need to supply the experimental=1 parameter when
+loading the module):
+
+These models do not have a button near the UltraBay device to request
+a hot eject but rather require the laptop to be put to sleep
+(suspend-to-ram) before the bay device is ejected or inserted).
+The sequence of steps to eject the device is as follows:
+
+       echo eject > /proc/acpi/ibm/bay
+       put the ThinkPad to sleep
+       remove the drive
+       resume from sleep
+       cat /proc/acpi/ibm/bay should show that the drive was removed
+
+On the A3x, both the UltraBay 2000 and UltraBay Plus devices are
+supported. Use "eject2" instead of "eject" for the second bay.
+
+Note: the UltraBay eject support on the 600e/x, A22p and A3x is
+EXPERIMENTAL and may not work as expected. USE WITH CAUTION!
+
+CMOS control
+------------
+
+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 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"
+       2 - no effect but tpb reports "Mute on"
+       3 - simulate pressing the "Access IBM" button
+       4 - LCD brightness up
+       5 - LCD brightness down
+       11 - toggle screen expansion
+       12 - ThinkLight on
+       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
+---------------------------------
+
+Some of the LED indicators can be controlled through this feature. The
+available commands are:
+
+       echo '<led number> on' >/proc/acpi/ibm/led
+       echo '<led number> off' >/proc/acpi/ibm/led
+       echo '<led number> blink' >/proc/acpi/ibm/led
+
+The <led number> range is 0 to 7. The set of LEDs that can be
+controlled varies from model to model. Here is the mapping on the X40:
+
+       0 - power
+       1 - battery (orange)
+       2 - battery (green)
+       3 - UltraBase
+       4 - UltraBay
+       7 - standby
+
+All of the above can be turned on and off and can be made to blink.
+
+ACPI sounds -- /proc/acpi/ibm/beep
+----------------------------------
+
+The BEEP method is used internally by the ACPI firmware to provide
+audible alerts in various situations. This feature allows the same
+sounds to be triggered manually.
+
+The commands are non-negative integer numbers:
+
+       echo <number> >/proc/acpi/ibm/beep
+
+The valid <number> range is 0 to 17. Not all numbers trigger sounds
+and the sounds vary from model to model. Here is the behavior on the
+X40:
+
+       0 - stop a sound in progress (but use 17 to stop 16)
+       2 - two beeps, pause, third beep ("low battery")
+       3 - single beep
+       4 - high, followed by low-pitched beep ("unable")
+       5 - single beep
+       6 - very high, followed by high-pitched beep ("AC/DC")
+       7 - high-pitched beep
+       9 - three short beeps
+       10 - very long beep
+       12 - low-pitched beep
+       15 - three high-pitched beeps repeating constantly, stop with 0
+       16 - one medium-pitched beep repeating constantly, stop with 17
+       17 - stop 16
+
+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.
+
+EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the
+implementation directly accesses hardware registers and may not work as
+expected. USE WITH CAUTION! To use this feature, you need to supply the
+experimental=1 parameter when loading the module.  When EXPERIMENTAL
+mode is enabled, reading the first 8 sensors on newer ThinkPads will
+also use an new experimental thermal sensor access mode.
+
+For example, on the X40, a typical output may be:
+temperatures:   42 42 45 41 36 -128 33 -128
+
+EXPERIMENTAL: On the T43/p, a typical output may be:
+temperatures:   48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128
+
+The mapping of thermal sensors to physical locations varies depending on
+system-board model (and thus, on ThinkPad model).
+
+http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that
+tries to track down these locations for various models.
+
+Most (newer?) models seem to follow this pattern:
+
+1:  CPU
+2:  (depends on model)
+3:  (depends on model)
+4:  GPU
+5:  Main battery: main sensor
+6:  Bay battery: main sensor
+7:  Main battery: secondary sensor
+8:  Bay battery: secondary sensor
+9-15: (depends on model)
+
+For the R51 (source: Thomas Gruber):
+2:  Mini-PCI
+3:  Internal HDD
+
+For the T43, T43/p (source: Shmidoax/Thinkwiki.org)
+http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p
+2:  System board, left side (near PCMCIA slot), reported as HDAPS temp
+3:  PCMCIA slot
+9:  MCH (northbridge) to DRAM Bus
+10: ICH (southbridge), under Mini-PCI card, under touchpad
+11: Power regulator, underside of system board, below F2 key
+
+The A31 has a very atypical layout for the thermal sensors
+(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31)
+1:  CPU
+2:  Main Battery: main sensor
+3:  Power Converter
+4:  Bay Battery: main sensor
+5:  MCH (northbridge)
+6:  PCMCIA/ambient
+7:  Main Battery: secondary sensor
+8:  Bay Battery: secondary sensor
+
+
+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
+------------------------------------------------------------------------
+
+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 dumps the values of 256 embedded controller
+registers. Values which have changed since the last time the registers
+were dumped are marked with a star:
+
+[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
+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
+EC 0x20:  00  00  00  00  00  00  00  00  00  00  00  03  43  00  00  80
+EC 0x30:  01  07  1a  00  30  04  00  00 *85  00  00  10  00  50  00  00
+EC 0x40:  00  00  00  00  00  00  14  01  00  04  00  00  00  00  00  00
+EC 0x50:  00  c0  02  0d  00  01  01  02  02  03  03  03  03 *bc *02 *bc
+EC 0x60: *02 *bc *02  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0x70:  00  00  00  00  00  12  30  40 *24 *26 *2c *27 *20  80 *1f  80
+EC 0x80:  00  00  00  06 *37 *0e  03  00  00  00  0e  07  00  00  00  00
+EC 0x90:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xa0: *ff  09  ff  09  ff  ff *64  00 *00 *00 *a2  41 *ff *ff *e0  00
+EC 0xb0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xc0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xd0:  03  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xe0:  00  00  00  00  00  00  00  00  11  20  49  04  24  06  55  03
+EC 0xf0:  31  55  48  54  35  38  57  57  08  2f  45  73  07  65  6c  1a
+
+This feature can be used to determine the register holding the fan
+speed on some models. To do that, do the following:
+
+       - make sure the battery is fully charged
+       - make sure the fan is running
+       - run 'cat /proc/acpi/ibm/ecdump' several times, once per second or so
+
+The first step makes sure various charging-related values don't
+vary. The second ensures that the fan-related values do vary, since
+the fan speed fluctuates a bit. The third will (hopefully) mark the
+fan register with a star:
+
+[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
+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
+EC 0x20:  00  00  00  00  00  00  00  00  00  00  00  03  43  00  00  80
+EC 0x30:  01  07  1a  00  30  04  00  00  85  00  00  10  00  50  00  00
+EC 0x40:  00  00  00  00  00  00  14  01  00  04  00  00  00  00  00  00
+EC 0x50:  00  c0  02  0d  00  01  01  02  02  03  03  03  03  bc  02  bc
+EC 0x60:  02  bc  02  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0x70:  00  00  00  00  00  12  30  40  24  27  2c  27  21  80  1f  80
+EC 0x80:  00  00  00  06 *be  0d  03  00  00  00  0e  07  00  00  00  00
+EC 0x90:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xa0:  ff  09  ff  09  ff  ff  64  00  00  00  a2  41  ff  ff  e0  00
+EC 0xb0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xc0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xd0:  03  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xe0:  00  00  00  00  00  00  00  00  11  20  49  04  24  06  55  03
+EC 0xf0:  31  55  48  54  35  38  57  57  08  2f  45  73  07  65  6c  1a
+
+Another set of values that varies often is the temperature
+readings. Since temperatures don't change vary fast, you can take
+several quick dumps to eliminate them.
+
+You can use a similar method to figure out the meaning of other
+embedded controller registers - e.g. make sure nothing else changes
+except the charging or discharging battery to determine which
+registers contain the current battery capacity, etc. If you experiment
+with this, do send me your results (including some complete dumps with
+a description of the conditions when they were taken.)
+
+LCD brightness control
+----------------------
+
+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.
+
+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
+
+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
+---------------------------------------
+
+This feature allows volume control on ThinkPad models which don't have
+a hardware volume knob. The available commands are:
+
+       echo up   >/proc/acpi/ibm/volume
+       echo down >/proc/acpi/ibm/volume
+       echo mute >/proc/acpi/ibm/volume
+       echo 'level <level>' >/proc/acpi/ibm/volume
+
+The <level> number range is 0 to 15 although not all of them may be
+distinct. The unmute the volume after the mute command, use either the
+up or down command (the level command will not unmute the volume).
+The current volume level and mute state is shown in the file.
+
+Fan control and monitoring: fan speed, fan enable/disable
+---------------------------------------------------------
+
+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, X and Z series ThinkPads but may show a bogus
+value on other models.
+
+Fan levels:
+
+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.
+
+Level "auto" means the EC changes the fan level according to some
+internal algorithm, usually based on readings from the thermal sensors.
+
+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.
+
+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
+enable it if necessary to avoid overheating.
+
+An enabled fan in level "auto" may stop spinning if the EC decides the
+ThinkPad is cool enough and doesn't need the extra airflow.  This is
+normal, and the EC will spin the fan up if the varios thermal readings
+rise too much.
+
+On the X40, this seems to depend on the CPU and HDD temperatures.
+Specifically, the fan is turned on when either the CPU temperature
+climbs to 56 degrees or the HDD temperature climbs to 46 degrees.  The
+fan is turned off when the CPU temperature drops to 49 degrees and the
+HDD temperature drops to 41 degrees.  These thresholds cannot
+currently be controlled.
+
+The ThinkPad's ACPI DSDT code will reprogram the fan on its own when
+certain conditions are met.  It will override any fan programming done
+through thinkpad-acpi.
+
+The thinkpad-acpi kernel driver can be programmed to revert the fan
+level to a safe setting if userspace does not issue one of the procfs
+fan commands: "enable", "disable", "level" or "watchdog", or if there
+are no writes to pwm1_enable (or to pwm1 *if and only if* pwm1_enable is
+set to 1, manual mode) within a configurable amount of time of up to
+120 seconds.  This functionality is called fan safety watchdog.
+
+Note that the watchdog timer stops after it enables the fan.  It will be
+rearmed again automatically (using the same interval) when one of the
+above mentioned fan commands is received.  The fan watchdog is,
+therefore, not suitable to protect against fan mode changes made through
+means other than the "enable", "disable", and "level" procfs fan
+commands, or the hwmon fan control sysfs interface.
+
+Procfs notes:
+
+The fan may be enabled or disabled with the following commands:
+
+       echo enable  >/proc/acpi/ibm/fan
+       echo disable >/proc/acpi/ibm/fan
+
+Placing a fan on level 0 is the same as disabling it.  Enabling a fan
+will try to place it in a safe level if it is too slow or disabled.
+
+The fan level can be controlled with the command:
+
+       echo 'level <level>' > /proc/acpi/ibm/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 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
+
+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
+------------------------------------
+
+Multiple commands can be written to the proc files in one shot by
+separating them with commas, for example:
+
+       echo enable,0xffff > /proc/acpi/ibm/hotkey
+       echo lcd_disable,crt_enable > /proc/acpi/ibm/video
+
+Commands can also be specified when loading the thinkpad-acpi module,
+for example:
+
+       modprobe thinkpad_acpi hotkey=enable,0xffff video=auto_disable
+
+Enabling debugging output
+-------------------------
+
+The module takes a debug paramater which can be used to selectively
+enable various classes of debugging output, for example:
+
+        modprobe ibm_acpi debug=0xffff
+
+will enable all debugging output classes.  It takes a bitmask, so
+to enable more than one output class, just add their values.
+
+       Debug bitmask           Description
+       0x0001                  Initialization and probing
+       0x0002                  Removal
+
+There is also a kernel build option to enable more debugging
+information, which may be necessary to debug driver problems.
+
+The level of debugging information output by the driver can be changed
+at runtime through sysfs, using the driver attribute debug_level.  The
+attribute takes the same bitmask as the debug module parameter above.
+
+Force loading of module
+-----------------------
+
+If thinkpad-acpi refuses to detect your ThinkPad, you can try to specify
+the module parameter force_load=1.  Regardless of whether this works or
+not, please contact ibm-acpi-devel@lists.sourceforge.net with a report.
+
+
+Sysfs interface changelog:
+
+0x000100:      Initial sysfs support, as a single platform driver and
+               device.
index ecb34160e61d5ca2fafad4bba259b782199cc66e..5e51c59bf2b024e2915d747e76f63d00e11d53f9 100644 (file)
@@ -5,10 +5,9 @@ Vaio Picturebook Motion Eye Camera Driver Readme
        Copyright (C) 2000 Andrew Tridgell <tridge@samba.org>
 
 This driver enable the use of video4linux compatible applications with the
-Motion Eye camera. This driver requires the "Sony Vaio Programmable I/O
-Control Device" driver (which can be found in the "Character drivers"
-section of the kernel configuration utility) to be compiled and installed
-(using its "camera=1" parameter).
+Motion Eye camera. This driver requires the "Sony Laptop Extras" driver (which
+can be found in the "Misc devices" section of the kernel configuration utility)
+to be compiled and installed (using its "camera=1" parameter).
 
 It can do at maximum 30 fps @ 320x240 or 15 fps @ 640x480.
 
index d4743b4c3f4fd87ca8df1317079b0f42e073f460..af1c7926c1530e16da6e2bb4d196707d4d5c77cb 100644 (file)
@@ -1658,15 +1658,6 @@ W:       http://www.ia64-linux.org/
 T:     git kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
 S:     Maintained
 
-IBM ACPI EXTRAS DRIVER
-P:     Henrique de Moraes Holschuh
-M:     ibm-acpi@hmh.eng.br
-L:     ibm-acpi-devel@lists.sourceforge.net
-W:     http://ibm-acpi.sourceforge.net
-W:     http://thinkwiki.org/wiki/Ibm-acpi
-T:     git repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
-S:     Maintained
-
 SN-IA64 (Itanium) SUB-PLATFORM
 P:     Jes Sorensen
 M:     jes@sgi.com
@@ -3182,6 +3173,15 @@ P:       Chris Zankel
 M:     chris@zankel.net
 S:     Maintained
 
+THINKPAD ACPI EXTRAS DRIVER
+P:     Henrique de Moraes Holschuh
+M:     ibm-acpi@hmh.eng.br
+L:     ibm-acpi-devel@lists.sourceforge.net
+W:     http://ibm-acpi.sourceforge.net
+W:     http://thinkwiki.org/wiki/Ibm-acpi
+T:     git repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
+S:     Maintained
+
 UltraSPARC (sparc64):
 P:     David S. Miller
 M:     davem@davemloft.net
index f4efd66e1ee502a401c720085d8f81fcfe6c803e..c96911c37aea28b7141c7ee26d43b49e4982f902 100644 (file)
@@ -692,7 +692,6 @@ CONFIG_SATA_SIL=y
 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
index a7d22d9f3d7e50d13ae2c4d6a43aba093a920b8a..8f7efd38254d47afd953054fa8cbf42e47d1dff8 100644 (file)
@@ -23,10 +23,13 @@ static int __init nvidia_hpet_check(struct acpi_table_header *header)
 static int __init check_bridge(int vendor, int device)
 {
 #ifdef CONFIG_ACPI
+       static int warned;
        /* According to Nvidia all timer overrides are bogus unless HPET
           is enabled. */
        if (!acpi_use_timer_override && vendor == PCI_VENDOR_ID_NVIDIA) {
-               if (acpi_table_parse(ACPI_SIG_HPET, nvidia_hpet_check)) {
+               if (!warned && acpi_table_parse(ACPI_SIG_HPET,
+                                               nvidia_hpet_check)) {
+                       warned = 1;
                        acpi_skip_timer_override = 1;
                          printk(KERN_INFO "Nvidia board "
                        "detected. Ignoring ACPI "
index 782906b644ddd46253f231bfd692153f4ef973e0..eb2f9a3d515c4ec6925b30eb034888a67688541b 100644 (file)
@@ -435,7 +435,6 @@ CONFIG_SCSI_SATA_SIL=m
 # 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
index 7a1e251e333ddfc505dcc3e68cfa10acb19a46dd..b26378815b9145ead9183f55113395d7271bfee0 100644 (file)
@@ -631,7 +631,6 @@ CONFIG_SATA_SIL=y
 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
index e2ce4a9c1c9259adf6d16da21b0a8b313f15b906..139f41f033d8bae09144493eb77c1ac2b09d3d41 100644 (file)
@@ -85,8 +85,8 @@ config ACPI_PROCFS
        depends on ACPI
        default y
        ---help---
-         Procfs interface for ACPI is made optional for back-compatible.
-         As the same functions are duplicated in sysfs interface
+         The Procfs interface for ACPI is made optional for backward compatibility.
+         As the same functions are duplicated in the sysfs interface
          and this proc interface will be removed some time later,
          it's marked as deprecated.
          ( /proc/acpi/debug_layer && debug_level are deprecated by
@@ -218,43 +218,6 @@ config ACPI_ASUS
          NOTE: This driver is deprecated and will probably be removed soon,
          use asus-laptop instead.
 
-config ACPI_IBM
-       tristate "IBM ThinkPad Laptop Extras"
-       depends on X86
-       select BACKLIGHT_CLASS_DEVICE
-       ---help---
-         This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds
-         support for Fn-Fx key combinations, Bluetooth control, video
-         output switching, ThinkLight control, UltraBay eject and more.
-         For more information about this driver see <file:Documentation/ibm-acpi.txt>
-         and <http://ibm-acpi.sf.net/> .
-
-         If you have an IBM ThinkPad laptop, say Y or M here.
-
-config ACPI_IBM_DOCK
-       bool "Legacy Docking Station Support"
-       depends on ACPI_IBM
-       depends on ACPI_DOCK=n
-       default n
-       ---help---
-         Allows the ibm_acpi driver to handle docking station events.
-         This support is obsoleted by CONFIG_HOTPLUG_PCI_ACPI.  It will
-         allow locking and removing the laptop from the docking station,
-         but will not properly connect PCI devices.
-
-         If you are not sure, say N here.
-
-config ACPI_IBM_BAY
-       bool "Legacy Removable Bay Support"
-       depends on ACPI_IBM
-       default y
-       ---help---
-         Allows the ibm_acpi driver to handle removable bays.  It will allow
-         disabling the device in the bay, and also generate notifications when
-         the bay lever is ejected or inserted.
-
-         If you are not sure, say Y here.
-
 config ACPI_TOSHIBA
        tristate "Toshiba Laptop Extras"
        depends on X86
@@ -388,11 +351,10 @@ config ACPI_HOTPLUG_MEMORY
 
 config ACPI_SBS
        tristate "Smart Battery System (EXPERIMENTAL)"
-       depends on X86 && I2C
+       depends on X86
        depends on EXPERIMENTAL
        help
          This driver adds support for the Smart Battery System.
-         Depends on I2C (Device Drivers ---> I2C support)
          A "Smart Battery" is quite old and quite rare compared
          to today's ACPI "Control Method" battery.
 
index 5956e9f64a8bfd682754999267f99f80ba9a7558..d4336f1730e9a0324ff61cfed04bd7215e03a816 100644 (file)
@@ -1,6 +1,6 @@
 #
 # Makefile for the Linux ACPI interpreter
-# 
+#
 
 export ACPI_CFLAGS
 
@@ -32,16 +32,17 @@ obj-y                               += osl.o utils.o \
 processor-objs += processor_core.o processor_throttling.o \
                                processor_idle.o processor_thermal.o
 ifdef CONFIG_CPU_FREQ
-processor-objs += processor_perflib.o                  
+processor-objs += processor_perflib.o
 endif
 
 obj-y                          += sleep/
 obj-y                          += bus.o glue.o
 obj-y                          += scan.o
+# Keep EC driver first. Initialization of others depend on it.
+obj-$(CONFIG_ACPI_EC)          += ec.o
 obj-$(CONFIG_ACPI_AC)          += ac.o
 obj-$(CONFIG_ACPI_BATTERY)     += battery.o
 obj-$(CONFIG_ACPI_BUTTON)      += button.o
-obj-$(CONFIG_ACPI_EC)          += ec.o
 obj-$(CONFIG_ACPI_FAN)         += fan.o
 obj-$(CONFIG_ACPI_DOCK)                += dock.o
 obj-$(CONFIG_ACPI_BAY)         += bay.o
@@ -55,8 +56,7 @@ obj-$(CONFIG_ACPI_SYSTEM)     += system.o event.o
 obj-$(CONFIG_ACPI_DEBUG)       += debug.o
 obj-$(CONFIG_ACPI_NUMA)                += numa.o
 obj-$(CONFIG_ACPI_ASUS)                += asus_acpi.o
-obj-$(CONFIG_ACPI_IBM)         += ibm_acpi.o
 obj-$(CONFIG_ACPI_TOSHIBA)     += toshiba_acpi.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)      += acpi_memhotplug.o
 obj-y                          += cm_sbs.o
-obj-$(CONFIG_ACPI_SBS)         += i2c_ec.o sbs.o
+obj-$(CONFIG_ACPI_SBS)         += sbs.o
index c26172671fd882a175eb04cba40d6a8d1db8c304..e65628a030852a736ab40a59407d1af14a883888 100644 (file)
@@ -44,11 +44,6 @@ MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
 MODULE_DESCRIPTION("Hotplug Mem Driver");
 MODULE_LICENSE("GPL");
 
-/* ACPI _STA method values */
-#define ACPI_MEMORY_STA_PRESENT                (0x00000001UL)
-#define ACPI_MEMORY_STA_ENABLED                (0x00000002UL)
-#define ACPI_MEMORY_STA_FUNCTIONAL     (0x00000008UL)
-
 /* Memory Device States */
 #define MEMORY_INVALID_STATE   0
 #define MEMORY_POWER_ON_STATE  1
@@ -204,9 +199,9 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
         * Check for device status. Device should be
         * present/enabled/functioning.
         */
-       if (!((current_status & ACPI_MEMORY_STA_PRESENT)
-             && (current_status & ACPI_MEMORY_STA_ENABLED)
-             && (current_status & ACPI_MEMORY_STA_FUNCTIONAL)))
+       if (!((current_status & ACPI_STA_DEVICE_PRESENT)
+             && (current_status & ACPI_STA_DEVICE_ENABLED)
+             && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
                return -ENODEV;
 
        return 0;
@@ -286,7 +281,7 @@ static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
                return -ENODEV;
 
        /* Check for device status.  Device should be disabled */
-       if (current_status & ACPI_MEMORY_STA_ENABLED)
+       if (current_status & ACPI_STA_DEVICE_ENABLED)
                return -EINVAL;
 
        return 0;
index dd49ea0d0ed3606a69dfc2a9580b64768ced7cb9..e5084ececb6ff7f004326a3fcdf24828c45e2f0a 100644 (file)
@@ -103,7 +103,9 @@ int acpi_bus_get_status(struct acpi_device *device)
        else if (device->parent)
                device->status = device->parent->status;
        else
-               STRUCT_TO_INT(device->status) = 0x0F;
+               STRUCT_TO_INT(device->status) =
+                   ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
+                   ACPI_STA_DEVICE_UI      | ACPI_STA_DEVICE_FUNCTIONING;
 
        if (device->status.functional && !device->status.present) {
                printk(KERN_WARNING PREFIX "Device [%s] status [%08x]: "
index 0930d9413dfa31768e889e7150e9dd55a0691670..0dd3bf7c0ed1f7c281dc576ac50fa436e97f2118 100644 (file)
@@ -49,8 +49,6 @@ MODULE_AUTHOR("Anil S Keshavamurthy");
 MODULE_DESCRIPTION("ACPI container driver");
 MODULE_LICENSE("GPL");
 
-#define ACPI_STA_PRESENT               (0x00000001)
-
 static int acpi_container_add(struct acpi_device *device);
 static int acpi_container_remove(struct acpi_device *device, int type);
 
@@ -75,13 +73,13 @@ static int is_device_present(acpi_handle handle)
 
        status = acpi_get_handle(handle, "_STA", &temp);
        if (ACPI_FAILURE(status))
-               return 1;       /* _STA not found, assmue device present */
+               return 1;       /* _STA not found, assume device present */
 
        status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
        if (ACPI_FAILURE(status))
                return 0;       /* Firmware error */
 
-       return ((sta & ACPI_STA_PRESENT) == ACPI_STA_PRESENT);
+       return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT);
 }
 
 /*******************************************************************/
index 54a697f9aa1873400cd2ad1237b786e4f3f9550e..4546bf873aea273c2a0993fb3fbb465d801543ed 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/notifier.h>
 #include <linux/platform_device.h>
 #include <linux/jiffies.h>
+#include <linux/stddef.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 
@@ -667,6 +668,23 @@ static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
 }
 DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
 
+/*
+ * show_dock_uid - read method for "uid" file in sysfs
+ */
+static ssize_t show_dock_uid(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       unsigned long lbuf;
+       acpi_status status = acpi_evaluate_integer(dock_station->handle, "_UID", NULL, &lbuf);
+       if(ACPI_FAILURE(status)) {
+           return 0;
+       }
+       return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf);
+}
+DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
+
+
+
 /**
  * dock_add - add a new dock station
  * @handle: the dock station handle
@@ -715,6 +733,13 @@ static int dock_add(acpi_handle handle)
                kfree(dock_station);
                return ret;
        }
+       ret = device_create_file(&dock_device.dev, &dev_attr_uid);
+       if (ret) {
+               printk("Error %d adding sysfs file\n", ret);
+               platform_device_unregister(&dock_device);
+               kfree(dock_station);
+               return ret;
+       }
 
        /* Find dependent devices */
        acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
index a802962ff2b499fc2cc3489a5e28a02e58d2465d..e08cf98f504f12583174d23468a60bf4f9a28c14 100644 (file)
@@ -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 @@ static struct acpi_driver acpi_ec_driver = {
 };
 
 /* If we find an EC via the ECDT, we need to keep a ptr to its context */
+/* External interfaces use first EC only, so remember */
 static struct acpi_ec {
        acpi_handle handle;
-       unsigned long uid;
        unsigned long gpe;
        unsigned long command_addr;
        unsigned long data_addr;
@@ -101,12 +103,8 @@ static struct acpi_ec {
        struct mutex lock;
        atomic_t query_pending;
        atomic_t event_count;
-       atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */
        wait_queue_head_t wait;
-} *ec_ecdt;
-
-/* External interfaces use first EC only, so remember */
-static struct acpi_device *first_ec;
+} *boot_ec, *first_ec;
 
 /* --------------------------------------------------------------------------
                              Transaction Management
@@ -173,56 +171,6 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, unsigned count)
        return -ETIME;
 }
 
-#ifdef ACPI_FUTURE_USAGE
-/*
- * Note: samsung nv5000 doesn't work with ec burst mode.
- * http://bugzilla.kernel.org/show_bug.cgi?id=4980
- */
-int acpi_ec_enter_burst_mode(struct acpi_ec *ec)
-{
-       u8 tmp = 0;
-       u8 status = 0;
-
-       status = acpi_ec_read_status(ec);
-       if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) {
-               status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
-               if (status)
-                       goto end;
-               acpi_ec_write_cmd(ec, ACPI_EC_BURST_ENABLE);
-               status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1);
-               tmp = acpi_ec_read_data(ec);
-               if (tmp != 0x90) {      /* Burst ACK byte */
-                       return -EINVAL;
-               }
-       }
-
-       atomic_set(&ec->leaving_burst, 0);
-       return 0;
-      end:
-       ACPI_EXCEPTION((AE_INFO, status, "EC wait, burst mode"));
-       return -1;
-}
-
-int acpi_ec_leave_burst_mode(struct acpi_ec *ec)
-{
-       u8 status = 0;
-
-       status = acpi_ec_read_status(ec);
-       if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)) {
-               status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
-               if (status)
-                       goto end;
-               acpi_ec_write_cmd(ec, ACPI_EC_BURST_DISABLE);
-               acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
-       }
-       atomic_set(&ec->leaving_burst, 1);
-       return 0;
-      end:
-       ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode"));
-       return -1;
-}
-#endif                         /* ACPI_FUTURE_USAGE */
-
 static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
                                        const u8 * wdata, unsigned wdata_len,
                                        u8 * rdata, unsigned rdata_len)
@@ -312,6 +260,21 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command,
        return status;
 }
 
+/*
+ * Note: samsung nv5000 doesn't work with ec burst mode.
+ * http://bugzilla.kernel.org/show_bug.cgi?id=4980
+ */
+int acpi_ec_burst_enable(struct acpi_ec *ec)
+{
+       u8 d;
+       return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1);
+}
+
+int acpi_ec_burst_disable(struct acpi_ec *ec)
+{
+       return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0);
+}
+
 static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data)
 {
        int result;
@@ -333,18 +296,33 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data)
 /*
  * Externally callable EC access functions. For now, assume 1 EC only
  */
+int ec_burst_enable(void)
+{
+       if (!first_ec)
+               return -ENODEV;
+       return acpi_ec_burst_enable(first_ec);
+}
+
+EXPORT_SYMBOL(ec_burst_enable);
+
+int ec_burst_disable(void)
+{
+       if (!first_ec)
+               return -ENODEV;
+       return acpi_ec_burst_disable(first_ec);
+}
+
+EXPORT_SYMBOL(ec_burst_disable);
+
 int ec_read(u8 addr, u8 * val)
 {
-       struct acpi_ec *ec;
        int err;
        u8 temp_data;
 
        if (!first_ec)
                return -ENODEV;
 
-       ec = acpi_driver_data(first_ec);
-
-       err = acpi_ec_read(ec, addr, &temp_data);
+       err = acpi_ec_read(first_ec, addr, &temp_data);
 
        if (!err) {
                *val = temp_data;
@@ -357,15 +335,12 @@ EXPORT_SYMBOL(ec_read);
 
 int ec_write(u8 addr, u8 val)
 {
-       struct acpi_ec *ec;
        int err;
 
        if (!first_ec)
                return -ENODEV;
 
-       ec = acpi_driver_data(first_ec);
-
-       err = acpi_ec_write(ec, addr, val);
+       err = acpi_ec_write(first_ec, addr, val);
 
        return err;
 }
@@ -376,14 +351,10 @@ int ec_transaction(u8 command,
                   const u8 * wdata, unsigned wdata_len,
                   u8 * rdata, unsigned rdata_len)
 {
-       struct acpi_ec *ec;
-
        if (!first_ec)
                return -ENODEV;
 
-       ec = acpi_driver_data(first_ec);
-
-       return acpi_ec_transaction(ec, command, wdata,
+       return acpi_ec_transaction(first_ec, command, wdata,
                                   wdata_len, rdata, rdata_len);
 }
 
@@ -420,7 +391,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data)
 
 static void acpi_ec_gpe_query(void *ec_cxt)
 {
-       struct acpi_ec *ec = (struct acpi_ec *)ec_cxt;
+       struct acpi_ec *ec = ec_cxt;
        u8 value = 0;
        char object_name[8];
 
@@ -438,8 +409,9 @@ static u32 acpi_ec_gpe_handler(void *data)
 {
        acpi_status status = AE_OK;
        u8 value;
-       struct acpi_ec *ec = (struct acpi_ec *)data;
+       struct acpi_ec *ec = data;
        atomic_inc(&ec->event_count);
+
        if (acpi_ec_mode == EC_INTR) {
                wake_up(&ec->wait);
        }
@@ -482,7 +454,7 @@ acpi_ec_space_handler(u32 function,
                      void *handler_context, void *region_context)
 {
        int result = 0;
-       struct acpi_ec *ec = NULL;
+       struct acpi_ec *ec = handler_context;
        u64 temp = *value;
        acpi_integer f_v = 0;
        int i = 0;
@@ -494,8 +466,6 @@ acpi_ec_space_handler(u32 function,
                return AE_BAD_PARAMETER;
        }
 
-       ec = (struct acpi_ec *)handler_context;
-
       next_byte:
        switch (function) {
        case ACPI_READ:
@@ -551,18 +521,16 @@ static struct proc_dir_entry *acpi_ec_dir;
 
 static int acpi_ec_read_info(struct seq_file *seq, void *offset)
 {
-       struct acpi_ec *ec = (struct acpi_ec *)seq->private;
+       struct acpi_ec *ec = seq->private;
 
        if (!ec)
                goto end;
 
-       seq_printf(seq, "gpe:                 0x%02x\n", (u32) ec->gpe);
-       seq_printf(seq, "ports:                   0x%02x, 0x%02x\n",
-                  (u32) ec->command_addr, (u32) ec->data_addr);
-       seq_printf(seq, "use global lock:         %s\n",
+       seq_printf(seq, "gpe:\t\t\t0x%02x\n", (u32) ec->gpe);
+       seq_printf(seq, "ports:\t\t\t0x%02x, 0x%02x\n",
+                  (unsigned)ec->command_addr, (unsigned)ec->data_addr);
+       seq_printf(seq, "use global lock:\t%s\n",
                   ec->global_lock ? "yes" : "no");
-       acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
-
       end:
        return 0;
 }
@@ -619,154 +587,122 @@ static int acpi_ec_remove_fs(struct acpi_device *device)
 /* --------------------------------------------------------------------------
                                Driver Interface
    -------------------------------------------------------------------------- */
+static acpi_status
+ec_parse_io_ports(struct acpi_resource *resource, void *context);
+
+static acpi_status
+ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval);
+
+static struct acpi_ec *make_acpi_ec(void)
+{
+       struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
+       if (!ec)
+               return NULL;
+
+       atomic_set(&ec->query_pending, 1);
+       atomic_set(&ec->event_count, 1);
+       mutex_init(&ec->lock);
+       init_waitqueue_head(&ec->wait);
+
+       return ec;
+}
 
 static int acpi_ec_add(struct acpi_device *device)
 {
-       int result = 0;
        acpi_status status = AE_OK;
        struct acpi_ec *ec = NULL;
 
        if (!device)
                return -EINVAL;
 
-       ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
-       if (!ec)
-               return -ENOMEM;
-
-       ec->handle = device->handle;
-       ec->uid = -1;
-       mutex_init(&ec->lock);
-       atomic_set(&ec->query_pending, 0);
-       atomic_set(&ec->event_count, 1);
-       if (acpi_ec_mode == EC_INTR) {
-               atomic_set(&ec->leaving_burst, 1);
-               init_waitqueue_head(&ec->wait);
-       }
        strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
        strcpy(acpi_device_class(device), ACPI_EC_CLASS);
-       acpi_driver_data(device) = ec;
-
-       /* Use the global lock for all EC transactions? */
-       acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock);
-
-       /* XXX we don't test uids, because on some boxes ecdt uid = 0, see:
-          http://bugzilla.kernel.org/show_bug.cgi?id=6111 */
-       if (ec_ecdt) {
-               acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
-                                                 ACPI_ADR_SPACE_EC,
-                                                 &acpi_ec_space_handler);
 
-               acpi_remove_gpe_handler(NULL, ec_ecdt->gpe,
-                                       &acpi_ec_gpe_handler);
+       ec = make_acpi_ec();
+       if (!ec)
+               return -ENOMEM;
 
-               kfree(ec_ecdt);
+       status = ec_parse_device(device->handle, 0, ec, NULL);
+       if (status != AE_CTRL_TERMINATE) {
+               kfree(ec);
+               return -EINVAL;
        }
 
-       /* Get GPE bit assignment (EC events). */
-       /* TODO: Add support for _GPE returning a package */
-       status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe);
-       if (ACPI_FAILURE(status)) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Obtaining GPE bit assignment"));
-               result = -ENODEV;
-               goto end;
-       }
+       /* Check if we found the boot EC */
+       if (boot_ec) {
+               if (boot_ec->gpe == ec->gpe) {
+                       /* We might have incorrect info for GL at boot time */
+                       mutex_lock(&boot_ec->lock);
+                       boot_ec->global_lock = ec->global_lock;
+                       mutex_unlock(&boot_ec->lock);
+                       kfree(ec);
+                       ec = boot_ec;
+               }
+       } else
+               first_ec = ec;
+       ec->handle = device->handle;
+       acpi_driver_data(device) = ec;
 
-       result = acpi_ec_add_fs(device);
-       if (result)
-               goto end;
+       acpi_ec_add_fs(device);
 
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.",
                          acpi_device_name(device), acpi_device_bid(device),
                          (u32) ec->gpe));
 
-       if (!first_ec)
-               first_ec = device;
-
-      end:
-       if (result)
-               kfree(ec);
-
-       return result;
+       return 0;
 }
 
 static int acpi_ec_remove(struct acpi_device *device, int type)
 {
-       struct acpi_ec *ec = NULL;
+       struct acpi_ec *ec;
 
        if (!device)
                return -EINVAL;
 
        ec = acpi_driver_data(device);
-
        acpi_ec_remove_fs(device);
+       acpi_driver_data(device) = NULL;
+       if (ec == first_ec)
+               first_ec = NULL;
 
-       kfree(ec);
-
+       /* Don't touch boot EC */
+       if (boot_ec != ec)
+               kfree(ec);
        return 0;
 }
 
 static acpi_status
-acpi_ec_io_ports(struct acpi_resource *resource, void *context)
+ec_parse_io_ports(struct acpi_resource *resource, void *context)
 {
-       struct acpi_ec *ec = (struct acpi_ec *)context;
+       struct acpi_ec *ec = context;
 
-       if (resource->type != ACPI_RESOURCE_TYPE_IO) {
+       if (resource->type != ACPI_RESOURCE_TYPE_IO)
                return AE_OK;
-       }
 
        /*
         * The first address region returned is the data port, and
         * the second address region returned is the status/command
         * port.
         */
-       if (ec->data_addr == 0) {
+       if (ec->data_addr == 0)
                ec->data_addr = resource->data.io.minimum;
-       } else if (ec->command_addr == 0) {
+       else if (ec->command_addr == 0)
                ec->command_addr = resource->data.io.minimum;
-       } else {
+       else
                return AE_CTRL_TERMINATE;
-       }
 
        return AE_OK;
 }
 
-static int acpi_ec_start(struct acpi_device *device)
+static int ec_install_handlers(struct acpi_ec *ec)
 {
-       acpi_status status = AE_OK;
-       struct acpi_ec *ec = NULL;
-
-       if (!device)
-               return -EINVAL;
-
-       ec = acpi_driver_data(device);
-
-       if (!ec)
-               return -EINVAL;
-
-       /*
-        * Get I/O port addresses. Convert to GAS format.
-        */
-       status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS,
-                                    acpi_ec_io_ports, ec);
-       if (ACPI_FAILURE(status) || ec->command_addr == 0) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Error getting I/O port addresses"));
-               return -ENODEV;
-       }
-
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx",
-                         ec->gpe, ec->command_addr, ec->data_addr));
-
-       /*
-        * Install GPE handler
-        */
+       acpi_status status;
        status = acpi_install_gpe_handler(NULL, ec->gpe,
                                          ACPI_GPE_EDGE_TRIGGERED,
                                          &acpi_ec_gpe_handler, ec);
-       if (ACPI_FAILURE(status)) {
+       if (ACPI_FAILURE(status))
                return -ENODEV;
-       }
+
        acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME);
        acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
 
@@ -779,18 +715,49 @@ static int acpi_ec_start(struct acpi_device *device)
                return -ENODEV;
        }
 
-       return AE_OK;
+       /* EC is fully operational, allow queries */
+       atomic_set(&ec->query_pending, 0);
+
+       return 0;
+}
+
+static int acpi_ec_start(struct acpi_device *device)
+{
+       struct acpi_ec *ec;
+
+       if (!device)
+               return -EINVAL;
+
+       ec = acpi_driver_data(device);
+
+       if (!ec)
+               return -EINVAL;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx",
+                         ec->gpe, ec->command_addr, ec->data_addr));
+
+       /* Boot EC is already working */
+       if (ec == boot_ec)
+               return 0;
+
+       return ec_install_handlers(ec);
 }
 
 static int acpi_ec_stop(struct acpi_device *device, int type)
 {
-       acpi_status status = AE_OK;
-       struct acpi_ec *ec = NULL;
+       acpi_status status;
+       struct acpi_ec *ec;
 
        if (!device)
                return -EINVAL;
 
        ec = acpi_driver_data(device);
+       if (!ec)
+               return -EINVAL;
+
+       /* Don't touch boot EC */
+       if (ec == boot_ec)
+               return 0;
 
        status = acpi_remove_address_space_handler(ec->handle,
                                                   ACPI_ADR_SPACE_EC,
@@ -805,164 +772,67 @@ static int acpi_ec_stop(struct acpi_device *device, int type)
        return 0;
 }
 
-static acpi_status __init
-acpi_fake_ecdt_callback(acpi_handle handle,
-                       u32 Level, void *context, void **retval)
+static acpi_status
+ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
 {
        acpi_status status;
 
-       mutex_init(&ec_ecdt->lock);
-       atomic_set(&ec_ecdt->event_count, 1);
-       if (acpi_ec_mode == EC_INTR) {
-               init_waitqueue_head(&ec_ecdt->wait);
-       }
+       struct acpi_ec *ec = context;
        status = acpi_walk_resources(handle, METHOD_NAME__CRS,
-                                    acpi_ec_io_ports, ec_ecdt);
+                                    ec_parse_io_ports, ec);
        if (ACPI_FAILURE(status))
                return status;
 
-       ec_ecdt->uid = -1;
-       acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid);
-
-       status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe);
+       /* Get GPE bit assignment (EC events). */
+       /* TODO: Add support for _GPE returning a package */
+       status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe);
        if (ACPI_FAILURE(status))
                return status;
-       ec_ecdt->global_lock = TRUE;
-       ec_ecdt->handle = handle;
-
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx",
-                         ec_ecdt->gpe, ec_ecdt->command_addr,
-                         ec_ecdt->data_addr));
 
-       return AE_CTRL_TERMINATE;
-}
-
-/*
- * Some BIOS (such as some from Gateway laptops) access EC region very early
- * such as in BAT0._INI or EC._INI before an EC device is found and
- * do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily
- * required, but if EC regison is accessed early, it is required.
- * The routine tries to workaround the BIOS bug by pre-scan EC device
- * It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any
- * op region (since _REG isn't invoked yet). The assumption is true for
- * all systems found.
- */
-static int __init acpi_ec_fake_ecdt(void)
-{
-       acpi_status status;
-       int ret = 0;
+       /* Use the global lock for all EC transactions? */
+       acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock);
 
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Try to make an fake ECDT"));
+       ec->handle = handle;
 
-       ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
-       if (!ec_ecdt) {
-               ret = -ENOMEM;
-               goto error;
-       }
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx",
+                         ec->gpe, ec->command_addr, ec->data_addr));
 
-       status = acpi_get_devices(ACPI_EC_HID,
-                                 acpi_fake_ecdt_callback, NULL, NULL);
-       if (ACPI_FAILURE(status)) {
-               kfree(ec_ecdt);
-               ec_ecdt = NULL;
-               ret = -ENODEV;
-               ACPI_EXCEPTION((AE_INFO, status, "Can't make an fake ECDT"));
-               goto error;
-       }
-       return 0;
-      error:
-       return ret;
+       return AE_CTRL_TERMINATE;
 }
 
-static int __init acpi_ec_get_real_ecdt(void)
+int __init acpi_ec_ecdt_probe(void)
 {
+       int ret;
        acpi_status status;
        struct acpi_table_ecdt *ecdt_ptr;
 
-       status = acpi_get_table(ACPI_SIG_ECDT, 1,
-                               (struct acpi_table_header **)&ecdt_ptr);
-       if (ACPI_FAILURE(status))
-               return -ENODEV;
-
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT"));
-
+       boot_ec = make_acpi_ec();
+       if (!boot_ec)
+               return -ENOMEM;
        /*
-        * Generate a temporary ec context to use until the namespace is scanned
+        * Generate a boot ec context
         */
-       ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
-       if (!ec_ecdt)
-               return -ENOMEM;
 
-       mutex_init(&ec_ecdt->lock);
-       atomic_set(&ec_ecdt->event_count, 1);
-       if (acpi_ec_mode == EC_INTR) {
-               init_waitqueue_head(&ec_ecdt->wait);
-       }
-       ec_ecdt->command_addr = ecdt_ptr->control.address;
-       ec_ecdt->data_addr = ecdt_ptr->data.address;
-       ec_ecdt->gpe = ecdt_ptr->gpe;
-       /* use the GL just to be safe */
-       ec_ecdt->global_lock = TRUE;
-       ec_ecdt->uid = ecdt_ptr->uid;
-
-       status = acpi_get_handle(NULL, ecdt_ptr->id, &ec_ecdt->handle);
-       if (ACPI_FAILURE(status)) {
+       status = acpi_get_table(ACPI_SIG_ECDT, 1,
+                               (struct acpi_table_header **)&ecdt_ptr);
+       if (ACPI_FAILURE(status))
                goto error;
-       }
-
-       return 0;
-      error:
-       ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT"));
-       kfree(ec_ecdt);
-       ec_ecdt = NULL;
 
-       return -ENODEV;
-}
-
-static int __initdata acpi_fake_ecdt_enabled;
-int __init acpi_ec_ecdt_probe(void)
-{
-       acpi_status status;
-       int ret;
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT"));
 
-       ret = acpi_ec_get_real_ecdt();
-       /* Try to make a fake ECDT */
-       if (ret && acpi_fake_ecdt_enabled) {
-               ret = acpi_ec_fake_ecdt();
-       }
+       boot_ec->command_addr = ecdt_ptr->control.address;
+       boot_ec->data_addr = ecdt_ptr->data.address;
+       boot_ec->gpe = ecdt_ptr->gpe;
+       boot_ec->handle = ACPI_ROOT_OBJECT;
 
-       if (ret)
+       ret = ec_install_handlers(boot_ec);
+       if (!ret) {
+               first_ec = boot_ec;
                return 0;
-
-       /*
-        * Install GPE handler
-        */
-       status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe,
-                                         ACPI_GPE_EDGE_TRIGGERED,
-                                         &acpi_ec_gpe_handler, ec_ecdt);
-       if (ACPI_FAILURE(status)) {
-               goto error;
        }
-       acpi_set_gpe_type(NULL, ec_ecdt->gpe, ACPI_GPE_TYPE_RUNTIME);
-       acpi_enable_gpe(NULL, ec_ecdt->gpe, ACPI_NOT_ISR);
-
-       status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
-                                                   ACPI_ADR_SPACE_EC,
-                                                   &acpi_ec_space_handler,
-                                                   &acpi_ec_space_setup,
-                                                   ec_ecdt);
-       if (ACPI_FAILURE(status)) {
-               acpi_remove_gpe_handler(NULL, ec_ecdt->gpe,
-                                       &acpi_ec_gpe_handler);
-               goto error;
-       }
-
-       return 0;
-
       error:
-       ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT"));
-       kfree(ec_ecdt);
-       ec_ecdt = NULL;
+       kfree(boot_ec);
+       boot_ec = NULL;
 
        return -ENODEV;
 }
@@ -1003,13 +873,6 @@ static void __exit acpi_ec_exit(void)
 }
 #endif                         /* 0 */
 
-static int __init acpi_fake_ecdt_setup(char *str)
-{
-       acpi_fake_ecdt_enabled = 1;
-       return 1;
-}
-
-__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup);
 static int __init acpi_ec_set_intr_mode(char *str)
 {
        int intr;
@@ -1017,12 +880,8 @@ static int __init acpi_ec_set_intr_mode(char *str)
        if (!get_option(&str, &intr))
                return 0;
 
-       if (intr) {
-               acpi_ec_mode = EC_INTR;
-       } else {
-               acpi_ec_mode = EC_POLL;
-       }
-       acpi_ec_driver.ops.add = acpi_ec_add;
+       acpi_ec_mode = (intr) ? EC_INTR : EC_POLL;
+
        printk(KERN_NOTICE PREFIX "%s mode.\n", intr ? "interrupt" : "polling");
 
        return 1;
diff --git a/drivers/acpi/i2c_ec.c b/drivers/acpi/i2c_ec.c
deleted file mode 100644 (file)
index acab4a4..0000000
+++ /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 (file)
index 7c53fb7..0000000
+++ /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 (file)
index dc10966..0000000
+++ /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, &params, 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);
index 99d1516d1e7023c067b8485272440dab43111485..f7de02a6f497a5d61752dff2a413a7c7b4c9f43a 100644 (file)
@@ -70,8 +70,6 @@
 #define ACPI_PROCESSOR_LIMIT_USER      0
 #define ACPI_PROCESSOR_LIMIT_THERMAL   1
 
-#define ACPI_STA_PRESENT 0x00000001
-
 #define _COMPONENT             ACPI_PROCESSOR_COMPONENT
 ACPI_MODULE_NAME("processor_core");
 
@@ -779,7 +777,7 @@ static int is_processor_present(acpi_handle handle)
 
 
        status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
-       if (ACPI_FAILURE(status) || !(sta & ACPI_STA_PRESENT)) {
+       if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) {
                ACPI_EXCEPTION((AE_INFO, status, "Processor Device is not present"));
                return 0;
        }
index cdf78943af4db915f1a54da4285cbca2cc1c42ac..ae0654cd11eaa58bcf3fff763e2cd3d341004a44 100644 (file)
 #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>
 
index 59640d9a0acceefe1ee8a4e6d9495edb4f5c5431..c1bae106833cef03d6f7faee83e5d42c9fda36a9 100644 (file)
 #include <linux/seq_file.h>
 #include <asm/uaccess.h>
 #include <linux/acpi.h>
-#include <linux/i2c.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
 #include <linux/delay.h>
 
-#include "i2c_ec.h"
-
-#define        DEF_CAPACITY_UNIT       3
-#define        MAH_CAPACITY_UNIT       1
-#define        MWH_CAPACITY_UNIT       2
-#define        CAPACITY_UNIT           DEF_CAPACITY_UNIT
-
-#define        REQUEST_UPDATE_MODE     1
-#define        QUEUE_UPDATE_MODE       2
-
-#define        DATA_TYPE_COMMON        0
-#define        DATA_TYPE_INFO          1
-#define        DATA_TYPE_STATE         2
-#define        DATA_TYPE_ALARM         3
-#define        DATA_TYPE_AC_STATE      4
-
-extern struct proc_dir_entry *acpi_lock_ac_dir(void);
-extern struct proc_dir_entry *acpi_lock_battery_dir(void);
-extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
-extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
-
 #define ACPI_SBS_COMPONENT             0x00080000
 #define ACPI_SBS_CLASS                 "sbs"
 #define ACPI_AC_CLASS                  "ac_adapter"
@@ -74,39 +54,75 @@ extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
 
 #define _COMPONENT                     ACPI_SBS_COMPONENT
 
-#define        MAX_SBS_BAT                     4
-#define        MAX_SMBUS_ERR                   1
-
 ACPI_MODULE_NAME("sbs");
 
 MODULE_AUTHOR("Rich Townsend");
 MODULE_DESCRIPTION("Smart Battery System ACPI interface driver");
 MODULE_LICENSE("GPL");
 
-static struct semaphore sbs_sem;
+#define        xmsleep(t)      msleep(t)
+
+#define ACPI_EC_SMB_PRTCL      0x00    /* protocol, PEC */
+
+#define ACPI_EC_SMB_STS                0x01    /* status */
+#define ACPI_EC_SMB_ADDR       0x02    /* address */
+#define ACPI_EC_SMB_CMD                0x03    /* command */
+#define ACPI_EC_SMB_DATA       0x04    /* 32 data registers */
+#define ACPI_EC_SMB_BCNT       0x24    /* number of data bytes */
 
-#define        UPDATE_MODE             QUEUE_UPDATE_MODE
-/* REQUEST_UPDATE_MODE  QUEUE_UPDATE_MODE */
-#define        UPDATE_INFO_MODE        0
-#define        UPDATE_TIME             60
-#define        UPDATE_TIME2            0
+#define ACPI_EC_SMB_STS_DONE   0x80
+#define ACPI_EC_SMB_STS_STATUS 0x1f
 
-static int capacity_mode = CAPACITY_UNIT;
-static int update_mode = UPDATE_MODE;
-static int update_info_mode = UPDATE_INFO_MODE;
-static int update_time = UPDATE_TIME;
-static int update_time2 = UPDATE_TIME2;
+#define ACPI_EC_SMB_PRTCL_WRITE                0x00
+#define ACPI_EC_SMB_PRTCL_READ         0x01
+#define ACPI_EC_SMB_PRTCL_WORD_DATA    0x08
+#define ACPI_EC_SMB_PRTCL_BLOCK_DATA   0x0a
 
-module_param(capacity_mode, int, 0);
-module_param(update_mode, int, 0);
-module_param(update_info_mode, int, 0);
-module_param(update_time, int, 0);
-module_param(update_time2, int, 0);
+#define ACPI_EC_SMB_TRANSACTION_SLEEP  1
+#define ACPI_EC_SMB_ACCESS_SLEEP1      1
+#define ACPI_EC_SMB_ACCESS_SLEEP2      10
+
+#define        DEF_CAPACITY_UNIT       3
+#define        MAH_CAPACITY_UNIT       1
+#define        MWH_CAPACITY_UNIT       2
+#define        CAPACITY_UNIT           DEF_CAPACITY_UNIT
+
+#define        REQUEST_UPDATE_MODE     1
+#define        QUEUE_UPDATE_MODE       2
+
+#define        DATA_TYPE_COMMON        0
+#define        DATA_TYPE_INFO          1
+#define        DATA_TYPE_STATE         2
+#define        DATA_TYPE_ALARM         3
+#define        DATA_TYPE_AC_STATE      4
+
+extern struct proc_dir_entry *acpi_lock_ac_dir(void);
+extern struct proc_dir_entry *acpi_lock_battery_dir(void);
+extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
+extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
+
+#define        MAX_SBS_BAT                     4
+#define ACPI_SBS_BLOCK_MAX             32
+
+#define ACPI_SBS_SMBUS_READ            1
+#define ACPI_SBS_SMBUS_WRITE           2
+
+#define ACPI_SBS_WORD_DATA             1
+#define ACPI_SBS_BLOCK_DATA            2
+
+#define        UPDATE_DELAY    10
+
+/* 0 - every time, > 0 - by update_time */
+static unsigned int update_time = 120;
+
+static unsigned int capacity_mode = CAPACITY_UNIT;
+
+module_param(update_time, uint, 0644);
+module_param(capacity_mode, uint, 0444);
 
 static int acpi_sbs_add(struct acpi_device *device);
 static int acpi_sbs_remove(struct acpi_device *device, int type);
-static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus);
-static void acpi_sbs_update_queue(void *data);
+static int acpi_sbs_resume(struct acpi_device *device);
 
 static struct acpi_driver acpi_sbs_driver = {
        .name = "sbs",
@@ -115,9 +131,14 @@ static struct acpi_driver acpi_sbs_driver = {
        .ops = {
                .add = acpi_sbs_add,
                .remove = acpi_sbs_remove,
+               .resume = acpi_sbs_resume,
                },
 };
 
+struct acpi_ac {
+       int ac_present;
+};
+
 struct acpi_battery_info {
        int capacity_mode;
        s16 full_charge_capacity;
@@ -126,18 +147,16 @@ struct acpi_battery_info {
        int vscale;
        int ipscale;
        s16 serial_number;
-       char manufacturer_name[I2C_SMBUS_BLOCK_MAX + 3];
-       char device_name[I2C_SMBUS_BLOCK_MAX + 3];
-       char device_chemistry[I2C_SMBUS_BLOCK_MAX + 3];
+       char manufacturer_name[ACPI_SBS_BLOCK_MAX + 3];
+       char device_name[ACPI_SBS_BLOCK_MAX + 3];
+       char device_chemistry[ACPI_SBS_BLOCK_MAX + 3];
 };
 
 struct acpi_battery_state {
        s16 voltage;
        s16 amperage;
        s16 remaining_capacity;
-       s16 average_time_to_empty;
-       s16 average_time_to_full;
-       s16 battery_status;
+       s16 battery_state;
 };
 
 struct acpi_battery_alarm {
@@ -146,9 +165,9 @@ struct acpi_battery_alarm {
 
 struct acpi_battery {
        int alive;
-       int battery_present;
        int id;
        int init_state;
+       int battery_present;
        struct acpi_sbs *sbs;
        struct acpi_battery_info info;
        struct acpi_battery_state state;
@@ -158,186 +177,251 @@ struct acpi_battery {
 
 struct acpi_sbs {
        acpi_handle handle;
+       int base;
        struct acpi_device *device;
        struct acpi_ec_smbus *smbus;
+       struct mutex mutex;
        int sbsm_present;
        int sbsm_batteries_supported;
-       int ac_present;
        struct proc_dir_entry *ac_entry;
+       struct acpi_ac ac;
        struct acpi_battery battery[MAX_SBS_BAT];
-       int update_info_mode;
        int zombie;
-       int update_time;
-       int update_time2;
        struct timer_list update_timer;
+       int run_cnt;
+       int update_proc_flg;
 };
 
-static void acpi_update_delay(struct acpi_sbs *sbs);
-static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type);
+static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type);
+static void acpi_sbs_update_time(void *data);
+
+union sbs_rw_data {
+       u16 word;
+       u8 block[ACPI_SBS_BLOCK_MAX + 2];
+};
+
+static int acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr,
+                             char read_write, u8 command, int size,
+                             union sbs_rw_data *data);
 
 /* --------------------------------------------------------------------------
                                SMBus Communication
    -------------------------------------------------------------------------- */
 
-static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus)
+static int acpi_ec_sbs_read(struct acpi_sbs *sbs, u8 address, u8 * data)
 {
-       union i2c_smbus_data data;
-       int result = 0;
-       char *err_str;
-       int err_number;
+       u8 val;
+       int err;
 
-       data.word = 0;
+       err = ec_read(sbs->base + address, &val);
+       if (!err) {
+               *data = val;
+       }
+       xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP);
+       return (err);
+}
 
-       result = smbus->adapter.algo->
-           smbus_xfer(&smbus->adapter,
-                      ACPI_SB_SMBUS_ADDR,
-                      0, I2C_SMBUS_READ, 0x16, I2C_SMBUS_BLOCK_DATA, &data);
+static int acpi_ec_sbs_write(struct acpi_sbs *sbs, u8 address, u8 data)
+{
+       int err;
 
-       err_number = (data.word & 0x000f);
+       err = ec_write(sbs->base + address, data);
+       return (err);
+}
 
-       switch (data.word & 0x000f) {
-       case 0x0000:
-               err_str = "unexpected bus error";
-               break;
-       case 0x0001:
-               err_str = "busy";
-               break;
-       case 0x0002:
-               err_str = "reserved command";
-               break;
-       case 0x0003:
-               err_str = "unsupported command";
-               break;
-       case 0x0004:
-               err_str = "access denied";
+static int
+acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr,
+                  char read_write, u8 command, int size,
+                  union sbs_rw_data *data)
+{
+       unsigned char protocol, len = 0, temp[2] = { 0, 0 };
+       int i;
+
+       if (read_write == ACPI_SBS_SMBUS_READ) {
+               protocol = ACPI_EC_SMB_PRTCL_READ;
+       } else {
+               protocol = ACPI_EC_SMB_PRTCL_WRITE;
+       }
+
+       switch (size) {
+
+       case ACPI_SBS_WORD_DATA:
+               acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command);
+               if (read_write == ACPI_SBS_SMBUS_WRITE) {
+                       acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA, data->word);
+                       acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + 1,
+                                         data->word >> 8);
+               }
+               protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA;
                break;
-       case 0x0005:
-               err_str = "overflow/underflow";
+       case ACPI_SBS_BLOCK_DATA:
+               acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command);
+               if (read_write == ACPI_SBS_SMBUS_WRITE) {
+                       len = min_t(u8, data->block[0], 32);
+                       acpi_ec_sbs_write(sbs, ACPI_EC_SMB_BCNT, len);
+                       for (i = 0; i < len; i++)
+                               acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + i,
+                                                 data->block[i + 1]);
+               }
+               protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA;
                break;
-       case 0x0006:
-               err_str = "bad size";
+       default:
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "unsupported transaction %d", size));
+               return (-1);
+       }
+
+       acpi_ec_sbs_write(sbs, ACPI_EC_SMB_ADDR, addr << 1);
+       acpi_ec_sbs_write(sbs, ACPI_EC_SMB_PRTCL, protocol);
+
+       acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
+
+       if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
+               xmsleep(ACPI_EC_SMB_ACCESS_SLEEP1);
+               acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
+       }
+       if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
+               xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2);
+               acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
+       }
+       if ((~temp[0] & ACPI_EC_SMB_STS_DONE)
+           || (temp[0] & ACPI_EC_SMB_STS_STATUS)) {
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "transaction %d error", size));
+               return (-1);
+       }
+
+       if (read_write == ACPI_SBS_SMBUS_WRITE) {
+               return (0);
+       }
+
+       switch (size) {
+
+       case ACPI_SBS_WORD_DATA:
+               acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA, temp);
+               acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + 1, temp + 1);
+               data->word = (temp[1] << 8) | temp[0];
                break;
-       case 0x0007:
-               err_str = "unknown error";
+
+       case ACPI_SBS_BLOCK_DATA:
+               len = 0;
+               acpi_ec_sbs_read(sbs, ACPI_EC_SMB_BCNT, &len);
+               len = min_t(u8, len, 32);
+               for (i = 0; i < len; i++)
+                       acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + i,
+                                        data->block + i + 1);
+               data->block[0] = len;
                break;
        default:
-               err_str = "unrecognized error";
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "unsupported transaction %d", size));
+               return (-1);
        }
-       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                         "%s: ret %i, err %i\n", err_str, result, err_number));
+
+       return (0);
 }
 
 static int
-acpi_sbs_smbus_read_word(struct acpi_ec_smbus *smbus, int addr, int func,
-                        u16 * word,
-                        void (*err_handler) (struct acpi_ec_smbus * smbus))
+acpi_sbs_read_word(struct acpi_sbs *sbs, int addr, int func, u16 * word)
 {
-       union i2c_smbus_data data;
+       union sbs_rw_data data;
        int result = 0;
-       int i;
 
-       if (err_handler == NULL) {
-               err_handler = acpi_battery_smbus_err_handler;
-       }
-
-       for (i = 0; i < MAX_SMBUS_ERR; i++) {
-               result =
-                   smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0,
-                                                   I2C_SMBUS_READ, func,
-                                                   I2C_SMBUS_WORD_DATA, &data);
-               if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "try %i: smbus->adapter.algo->smbus_xfer() failed\n",
-                                         i));
-                       if (err_handler) {
-                               err_handler(smbus);
-                       }
-               } else {
-                       *word = data.word;
-                       break;
-               }
+       result = acpi_ec_sbs_access(sbs, addr,
+                                   ACPI_SBS_SMBUS_READ, func,
+                                   ACPI_SBS_WORD_DATA, &data);
+       if (result) {
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_ec_sbs_access() failed"));
+       } else {
+               *word = data.word;
        }
 
        return result;
 }
 
 static int
-acpi_sbs_smbus_read_str(struct acpi_ec_smbus *smbus, int addr, int func,
-                       char *str,
-                       void (*err_handler) (struct acpi_ec_smbus * smbus))
+acpi_sbs_read_str(struct acpi_sbs *sbs, int addr, int func, char *str)
 {
-       union i2c_smbus_data data;
+       union sbs_rw_data data;
        int result = 0;
-       int i;
-
-       if (err_handler == NULL) {
-               err_handler = acpi_battery_smbus_err_handler;
-       }
 
-       for (i = 0; i < MAX_SMBUS_ERR; i++) {
-               result =
-                   smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0,
-                                                   I2C_SMBUS_READ, func,
-                                                   I2C_SMBUS_BLOCK_DATA,
-                                                   &data);
-               if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "try %i: smbus->adapter.algo->smbus_xfer() failed\n",
-                                         i));
-                       if (err_handler) {
-                               err_handler(smbus);
-                       }
-               } else {
-                       strncpy(str, (const char *)data.block + 1,
-                               data.block[0]);
-                       str[data.block[0]] = 0;
-                       break;
-               }
+       result = acpi_ec_sbs_access(sbs, addr,
+                                   ACPI_SBS_SMBUS_READ, func,
+                                   ACPI_SBS_BLOCK_DATA, &data);
+       if (result) {
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_ec_sbs_access() failed"));
+       } else {
+               strncpy(str, (const char *)data.block + 1, data.block[0]);
+               str[data.block[0]] = 0;
        }
 
        return result;
 }
 
 static int
-acpi_sbs_smbus_write_word(struct acpi_ec_smbus *smbus, int addr, int func,
-                         int word,
-                         void (*err_handler) (struct acpi_ec_smbus * smbus))
+acpi_sbs_write_word(struct acpi_sbs *sbs, int addr, int func, int word)
 {
-       union i2c_smbus_data data;
+       union sbs_rw_data data;
        int result = 0;
-       int i;
-
-       if (err_handler == NULL) {
-               err_handler = acpi_battery_smbus_err_handler;
-       }
 
        data.word = word;
 
-       for (i = 0; i < MAX_SMBUS_ERR; i++) {
-               result =
-                   smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0,
-                                                   I2C_SMBUS_WRITE, func,
-                                                   I2C_SMBUS_WORD_DATA, &data);
-               if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "try %i: smbus->adapter.algo"
-                                         "->smbus_xfer() failed\n", i));
-                       if (err_handler) {
-                               err_handler(smbus);
-                       }
-               } else {
-                       break;
-               }
+       result = acpi_ec_sbs_access(sbs, addr,
+                                   ACPI_SBS_SMBUS_WRITE, func,
+                                   ACPI_SBS_WORD_DATA, &data);
+       if (result) {
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_ec_sbs_access() failed"));
        }
 
        return result;
 }
 
+static int sbs_zombie(struct acpi_sbs *sbs)
+{
+       return (sbs->zombie);
+}
+
+static int sbs_mutex_lock(struct acpi_sbs *sbs)
+{
+       if (sbs_zombie(sbs)) {
+               return -ENODEV;
+       }
+       mutex_lock(&sbs->mutex);
+       return 0;
+}
+
+static void sbs_mutex_unlock(struct acpi_sbs *sbs)
+{
+       mutex_unlock(&sbs->mutex);
+}
+
 /* --------------------------------------------------------------------------
                             Smart Battery System Management
    -------------------------------------------------------------------------- */
 
-/* Smart Battery */
+static int acpi_check_update_proc(struct acpi_sbs *sbs)
+{
+       acpi_status status = AE_OK;
+
+       if (update_time == 0) {
+               sbs->update_proc_flg = 0;
+               return 0;
+       }
+       if (sbs->update_proc_flg == 0) {
+               status = acpi_os_execute(OSL_GPE_HANDLER,
+                                        acpi_sbs_update_time, sbs);
+               if (status != AE_OK) {
+                       ACPI_EXCEPTION((AE_INFO, status,
+                                       "acpi_os_execute() failed"));
+                       return 1;
+               }
+               sbs->update_proc_flg = 1;
+       }
+       return 0;
+}
 
 static int acpi_sbs_generate_event(struct acpi_device *device,
                                   int event, int state, char *bid, char *class)
@@ -366,12 +450,11 @@ static int acpi_battery_get_present(struct acpi_battery *battery)
        int result = 0;
        int is_present = 0;
 
-       result = acpi_sbs_smbus_read_word(battery->sbs->smbus,
-                                         ACPI_SBSM_SMBUS_ADDR, 0x01,
-                                         &state, NULL);
+       result = acpi_sbs_read_word(battery->sbs,
+                                   ACPI_SBSM_SMBUS_ADDR, 0x01, &state);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
        }
        if (!result) {
                is_present = (state & 0x000f) & (1 << battery->id);
@@ -381,45 +464,33 @@ static int acpi_battery_get_present(struct acpi_battery *battery)
        return result;
 }
 
-static int acpi_battery_is_present(struct acpi_battery *battery)
-{
-       return (battery->battery_present);
-}
-
-static int acpi_ac_is_present(struct acpi_sbs *sbs)
-{
-       return (sbs->ac_present);
-}
-
 static int acpi_battery_select(struct acpi_battery *battery)
 {
-       struct acpi_ec_smbus *smbus = battery->sbs->smbus;
+       struct acpi_sbs *sbs = battery->sbs;
        int result = 0;
        s16 state;
        int foo;
 
-       if (battery->sbs->sbsm_present) {
+       if (sbs->sbsm_present) {
 
                /* Take special care not to knobble other nibbles of
                 * state (aka selector_state), since
                 * it causes charging to halt on SBSELs */
 
                result =
-                   acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01,
-                                            &state, NULL);
+                   acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, &state);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_sbs_smbus_read_word() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_sbs_read_word() failed"));
                        goto end;
                }
 
                foo = (state & 0x0fff) | (1 << (battery->id + 12));
                result =
-                   acpi_sbs_smbus_write_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01,
-                                             foo, NULL);
+                   acpi_sbs_write_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, foo);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_sbs_smbus_write_word() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_sbs_write_word() failed"));
                        goto end;
                }
        }
@@ -430,15 +501,14 @@ static int acpi_battery_select(struct acpi_battery *battery)
 
 static int acpi_sbsm_get_info(struct acpi_sbs *sbs)
 {
-       struct acpi_ec_smbus *smbus = sbs->smbus;
        int result = 0;
        s16 battery_system_info;
 
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x04,
-                                         &battery_system_info, NULL);
+       result = acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x04,
+                                   &battery_system_info);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
@@ -451,53 +521,50 @@ static int acpi_sbsm_get_info(struct acpi_sbs *sbs)
 
 static int acpi_battery_get_info(struct acpi_battery *battery)
 {
-       struct acpi_ec_smbus *smbus = battery->sbs->smbus;
+       struct acpi_sbs *sbs = battery->sbs;
        int result = 0;
        s16 battery_mode;
        s16 specification_info;
 
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03,
-                                         &battery_mode,
-                                         &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03,
+                                   &battery_mode);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
        battery->info.capacity_mode = (battery_mode & 0x8000) >> 15;
 
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x10,
-                                         &battery->info.full_charge_capacity,
-                                         &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x10,
+                                   &battery->info.full_charge_capacity);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x18,
-                                         &battery->info.design_capacity,
-                                         &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x18,
+                                   &battery->info.design_capacity);
 
        if (result) {
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x19,
-                                         &battery->info.design_voltage,
-                                         &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x19,
+                                   &battery->info.design_voltage);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1a,
-                                         &specification_info,
-                                         &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1a,
+                                   &specification_info);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
@@ -529,37 +596,35 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
                battery->info.ipscale = 1;
        }
 
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1c,
-                                         &battery->info.serial_number,
-                                         &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1c,
+                                   &battery->info.serial_number);
        if (result) {
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
-       result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x20,
-                                        battery->info.manufacturer_name,
-                                        &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x20,
+                                  battery->info.manufacturer_name);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_str() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_str() failed"));
                goto end;
        }
 
-       result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x21,
-                                        battery->info.device_name,
-                                        &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x21,
+                                  battery->info.device_name);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_str() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_str() failed"));
                goto end;
        }
 
-       result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x22,
-                                        battery->info.device_chemistry,
-                                        &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x22,
+                                  battery->info.device_chemistry);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_str() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_str() failed"));
                goto end;
        }
 
@@ -567,103 +632,60 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
        return result;
 }
 
-static void acpi_update_delay(struct acpi_sbs *sbs)
-{
-       if (sbs->zombie) {
-               return;
-       }
-       if (sbs->update_time2 > 0) {
-               msleep(sbs->update_time2 * 1000);
-       }
-}
-
 static int acpi_battery_get_state(struct acpi_battery *battery)
 {
-       struct acpi_ec_smbus *smbus = battery->sbs->smbus;
+       struct acpi_sbs *sbs = battery->sbs;
        int result = 0;
 
-       acpi_update_delay(battery->sbs);
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x09,
-                                         &battery->state.voltage,
-                                         &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x09,
+                                   &battery->state.voltage);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
-       acpi_update_delay(battery->sbs);
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0a,
-                                         &battery->state.amperage,
-                                         &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0a,
+                                   &battery->state.amperage);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
-       acpi_update_delay(battery->sbs);
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0f,
-                                         &battery->state.remaining_capacity,
-                                         &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0f,
+                                   &battery->state.remaining_capacity);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
-       acpi_update_delay(battery->sbs);
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x12,
-                                         &battery->state.average_time_to_empty,
-                                         &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x16,
+                                   &battery->state.battery_state);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
-       acpi_update_delay(battery->sbs);
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x13,
-                                         &battery->state.average_time_to_full,
-                                         &acpi_battery_smbus_err_handler);
-       if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
-               goto end;
-       }
-
-       acpi_update_delay(battery->sbs);
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x16,
-                                         &battery->state.battery_status,
-                                         &acpi_battery_smbus_err_handler);
-       if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
-               goto end;
-       }
-
-       acpi_update_delay(battery->sbs);
-
       end:
        return result;
 }
 
 static int acpi_battery_get_alarm(struct acpi_battery *battery)
 {
-       struct acpi_ec_smbus *smbus = battery->sbs->smbus;
+       struct acpi_sbs *sbs = battery->sbs;
        int result = 0;
 
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01,
-                                         &battery->alarm.remaining_capacity,
-                                         &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01,
+                                   &battery->alarm.remaining_capacity);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
-       acpi_update_delay(battery->sbs);
-
       end:
 
        return result;
@@ -672,15 +694,15 @@ static int acpi_battery_get_alarm(struct acpi_battery *battery)
 static int acpi_battery_set_alarm(struct acpi_battery *battery,
                                  unsigned long alarm)
 {
-       struct acpi_ec_smbus *smbus = battery->sbs->smbus;
+       struct acpi_sbs *sbs = battery->sbs;
        int result = 0;
        s16 battery_mode;
        int foo;
 
        result = acpi_battery_select(battery);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_battery_select() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_battery_select() failed"));
                goto end;
        }
 
@@ -688,33 +710,29 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery,
 
        if (alarm > 0) {
                result =
-                   acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03,
-                                            &battery_mode,
-                                            &acpi_battery_smbus_err_handler);
+                   acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03,
+                                      &battery_mode);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_sbs_smbus_read_word() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_sbs_read_word() failed"));
                        goto end;
                }
 
                result =
-                   acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01,
-                                             battery_mode & 0xbfff,
-                                             &acpi_battery_smbus_err_handler);
+                   acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01,
+                                       battery_mode & 0xbfff);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_sbs_smbus_write_word() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_sbs_write_word() failed"));
                        goto end;
                }
        }
 
        foo = alarm / (battery->info.capacity_mode ? 10 : 1);
-       result = acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01,
-                                          foo,
-                                          &acpi_battery_smbus_err_handler);
+       result = acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, foo);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_write_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_write_word() failed"));
                goto end;
        }
 
@@ -725,6 +743,7 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery,
 
 static int acpi_battery_set_mode(struct acpi_battery *battery)
 {
+       struct acpi_sbs *sbs = battery->sbs;
        int result = 0;
        s16 battery_mode;
 
@@ -732,12 +751,11 @@ static int acpi_battery_set_mode(struct acpi_battery *battery)
                goto end;
        }
 
-       result = acpi_sbs_smbus_read_word(battery->sbs->smbus,
-                                         ACPI_SB_SMBUS_ADDR, 0x03,
-                                         &battery_mode, NULL);
+       result = acpi_sbs_read_word(sbs,
+                                   ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
@@ -746,21 +764,19 @@ static int acpi_battery_set_mode(struct acpi_battery *battery)
        } else {
                battery_mode |= 0x8000;
        }
-       result = acpi_sbs_smbus_write_word(battery->sbs->smbus,
-                                          ACPI_SB_SMBUS_ADDR, 0x03,
-                                          battery_mode, NULL);
+       result = acpi_sbs_write_word(sbs,
+                                    ACPI_SB_SMBUS_ADDR, 0x03, battery_mode);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_write_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_write_word() failed"));
                goto end;
        }
 
-       result = acpi_sbs_smbus_read_word(battery->sbs->smbus,
-                                         ACPI_SB_SMBUS_ADDR, 0x03,
-                                         &battery_mode, NULL);
+       result = acpi_sbs_read_word(sbs,
+                                   ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
@@ -774,36 +790,36 @@ static int acpi_battery_init(struct acpi_battery *battery)
 
        result = acpi_battery_select(battery);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_battery_init() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_battery_select() failed"));
                goto end;
        }
 
        result = acpi_battery_set_mode(battery);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_battery_set_mode() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_battery_set_mode() failed"));
                goto end;
        }
 
        result = acpi_battery_get_info(battery);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_battery_get_info() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_battery_get_info() failed"));
                goto end;
        }
 
        result = acpi_battery_get_state(battery);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_battery_get_state() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_battery_get_state() failed"));
                goto end;
        }
 
        result = acpi_battery_get_alarm(battery);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_battery_get_alarm() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_battery_get_alarm() failed"));
                goto end;
        }
 
@@ -813,20 +829,19 @@ static int acpi_battery_init(struct acpi_battery *battery)
 
 static int acpi_ac_get_present(struct acpi_sbs *sbs)
 {
-       struct acpi_ec_smbus *smbus = sbs->smbus;
        int result = 0;
        s16 charger_status;
 
-       result = acpi_sbs_smbus_read_word(smbus, ACPI_SBC_SMBUS_ADDR, 0x13,
-                                         &charger_status, NULL);
+       result = acpi_sbs_read_word(sbs, ACPI_SBC_SMBUS_ADDR, 0x13,
+                                   &charger_status);
 
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_smbus_read_word() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_read_word() failed"));
                goto end;
        }
 
-       sbs->ac_present = (charger_status & 0x8000) >> 15;
+       sbs->ac.ac_present = (charger_status & 0x8000) >> 15;
 
       end:
 
@@ -852,8 +867,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir,
        if (!*dir) {
                *dir = proc_mkdir(dir_name, parent_dir);
                if (!*dir) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "proc_mkdir() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "proc_mkdir() failed"));
                        return -ENODEV;
                }
                (*dir)->owner = THIS_MODULE;
@@ -863,8 +878,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir,
        if (info_fops) {
                entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir);
                if (!entry) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "create_proc_entry() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "create_proc_entry() failed"));
                } else {
                        entry->proc_fops = info_fops;
                        entry->data = data;
@@ -876,8 +891,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir,
        if (state_fops) {
                entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir);
                if (!entry) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "create_proc_entry() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "create_proc_entry() failed"));
                } else {
                        entry->proc_fops = state_fops;
                        entry->data = data;
@@ -889,8 +904,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir,
        if (alarm_fops) {
                entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IRUGO, *dir);
                if (!entry) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "create_proc_entry() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "create_proc_entry() failed"));
                } else {
                        entry->proc_fops = alarm_fops;
                        entry->data = data;
@@ -923,24 +938,27 @@ static struct proc_dir_entry *acpi_battery_dir = NULL;
 static int acpi_battery_read_info(struct seq_file *seq, void *offset)
 {
        struct acpi_battery *battery = seq->private;
+       struct acpi_sbs *sbs = battery->sbs;
        int cscale;
        int result = 0;
 
-       if (battery->sbs->zombie) {
+       if (sbs_mutex_lock(sbs)) {
                return -ENODEV;
        }
 
-       down(&sbs_sem);
+       result = acpi_check_update_proc(sbs);
+       if (result)
+               goto end;
 
-       if (update_mode == REQUEST_UPDATE_MODE) {
-               result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_INFO);
+       if (update_time == 0) {
+               result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_INFO);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_sbs_update_run() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_sbs_update_run() failed"));
                }
        }
 
-       if (acpi_battery_is_present(battery)) {
+       if (battery->battery_present) {
                seq_printf(seq, "present:                 yes\n");
        } else {
                seq_printf(seq, "present:                 no\n");
@@ -952,13 +970,13 @@ static int acpi_battery_read_info(struct seq_file *seq, void *offset)
        } else {
                cscale = battery->info.ipscale;
        }
-       seq_printf(seq, "design capacity:         %i%s",
+       seq_printf(seq, "design capacity:         %i%s\n",
                   battery->info.design_capacity * cscale,
-                  battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
+                  battery->info.capacity_mode ? "0 mWh" : " mAh");
 
-       seq_printf(seq, "last full capacity:      %i%s",
+       seq_printf(seq, "last full capacity:      %i%s\n",
                   battery->info.full_charge_capacity * cscale,
-                  battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
+                  battery->info.capacity_mode ? "0 mWh" : " mAh");
 
        seq_printf(seq, "battery technology:      rechargeable\n");
 
@@ -984,7 +1002,7 @@ static int acpi_battery_read_info(struct seq_file *seq, void *offset)
 
       end:
 
-       up(&sbs_sem);
+       sbs_mutex_unlock(sbs);
 
        return result;
 }
@@ -996,26 +1014,29 @@ static int acpi_battery_info_open_fs(struct inode *inode, struct file *file)
 
 static int acpi_battery_read_state(struct seq_file *seq, void *offset)
 {
-       struct acpi_battery *battery = (struct acpi_battery *)seq->private;
+       struct acpi_battery *battery = seq->private;
+       struct acpi_sbs *sbs = battery->sbs;
        int result = 0;
        int cscale;
        int foo;
 
-       if (battery->sbs->zombie) {
+       if (sbs_mutex_lock(sbs)) {
                return -ENODEV;
        }
 
-       down(&sbs_sem);
+       result = acpi_check_update_proc(sbs);
+       if (result)
+               goto end;
 
-       if (update_mode == REQUEST_UPDATE_MODE) {
-               result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_STATE);
+       if (update_time == 0) {
+               result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_STATE);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_sbs_update_run() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_sbs_update_run() failed"));
                }
        }
 
-       if (acpi_battery_is_present(battery)) {
+       if (battery->battery_present) {
                seq_printf(seq, "present:                 yes\n");
        } else {
                seq_printf(seq, "present:                 no\n");
@@ -1028,7 +1049,7 @@ static int acpi_battery_read_state(struct seq_file *seq, void *offset)
                cscale = battery->info.ipscale;
        }
 
-       if (battery->state.battery_status & 0x0010) {
+       if (battery->state.battery_state & 0x0010) {
                seq_printf(seq, "capacity state:          critical\n");
        } else {
                seq_printf(seq, "capacity state:          ok\n");
@@ -1052,16 +1073,16 @@ static int acpi_battery_read_state(struct seq_file *seq, void *offset)
                           battery->info.capacity_mode ? "mW" : "mA");
        }
 
-       seq_printf(seq, "remaining capacity:      %i%s",
+       seq_printf(seq, "remaining capacity:      %i%s\n",
                   battery->state.remaining_capacity * cscale,
-                  battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
+                  battery->info.capacity_mode ? "0 mWh" : " mAh");
 
        seq_printf(seq, "present voltage:         %i mV\n",
                   battery->state.voltage * battery->info.vscale);
 
       end:
 
-       up(&sbs_sem);
+       sbs_mutex_unlock(sbs);
 
        return result;
 }
@@ -1074,24 +1095,27 @@ static int acpi_battery_state_open_fs(struct inode *inode, struct file *file)
 static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
 {
        struct acpi_battery *battery = seq->private;
+       struct acpi_sbs *sbs = battery->sbs;
        int result = 0;
        int cscale;
 
-       if (battery->sbs->zombie) {
+       if (sbs_mutex_lock(sbs)) {
                return -ENODEV;
        }
 
-       down(&sbs_sem);
+       result = acpi_check_update_proc(sbs);
+       if (result)
+               goto end;
 
-       if (update_mode == REQUEST_UPDATE_MODE) {
-               result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_ALARM);
+       if (update_time == 0) {
+               result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_ALARM);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_sbs_update_run() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_sbs_update_run() failed"));
                }
        }
 
-       if (!acpi_battery_is_present(battery)) {
+       if (!battery->battery_present) {
                seq_printf(seq, "present:                 no\n");
                goto end;
        }
@@ -1104,16 +1128,16 @@ static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
 
        seq_printf(seq, "alarm:                   ");
        if (battery->alarm.remaining_capacity) {
-               seq_printf(seq, "%i%s",
+               seq_printf(seq, "%i%s\n",
                           battery->alarm.remaining_capacity * cscale,
-                          battery->info.capacity_mode ? "0 mWh\n" : " mAh\n");
+                          battery->info.capacity_mode ? "0 mWh" : " mAh");
        } else {
                seq_printf(seq, "disabled\n");
        }
 
       end:
 
-       up(&sbs_sem);
+       sbs_mutex_unlock(sbs);
 
        return result;
 }
@@ -1124,16 +1148,19 @@ acpi_battery_write_alarm(struct file *file, const char __user * buffer,
 {
        struct seq_file *seq = file->private_data;
        struct acpi_battery *battery = seq->private;
+       struct acpi_sbs *sbs = battery->sbs;
        char alarm_string[12] = { '\0' };
        int result, old_alarm, new_alarm;
 
-       if (battery->sbs->zombie) {
+       if (sbs_mutex_lock(sbs)) {
                return -ENODEV;
        }
 
-       down(&sbs_sem);
+       result = acpi_check_update_proc(sbs);
+       if (result)
+               goto end;
 
-       if (!acpi_battery_is_present(battery)) {
+       if (!battery->battery_present) {
                result = -ENODEV;
                goto end;
        }
@@ -1155,21 +1182,21 @@ acpi_battery_write_alarm(struct file *file, const char __user * buffer,
 
        result = acpi_battery_set_alarm(battery, new_alarm);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_battery_set_alarm() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_battery_set_alarm() failed"));
                acpi_battery_set_alarm(battery, old_alarm);
                goto end;
        }
        result = acpi_battery_get_alarm(battery);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_battery_get_alarm() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_battery_get_alarm() failed"));
                acpi_battery_set_alarm(battery, old_alarm);
                goto end;
        }
 
       end:
-       up(&sbs_sem);
+       sbs_mutex_unlock(sbs);
 
        if (result) {
                return result;
@@ -1217,24 +1244,22 @@ static int acpi_ac_read_state(struct seq_file *seq, void *offset)
        struct acpi_sbs *sbs = seq->private;
        int result;
 
-       if (sbs->zombie) {
+       if (sbs_mutex_lock(sbs)) {
                return -ENODEV;
        }
 
-       down(&sbs_sem);
-
-       if (update_mode == REQUEST_UPDATE_MODE) {
-               result = acpi_sbs_update_run(sbs, DATA_TYPE_AC_STATE);
+       if (update_time == 0) {
+               result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_AC_STATE);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_sbs_update_run() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_sbs_update_run() failed"));
                }
        }
 
        seq_printf(seq, "state:                   %s\n",
-                  sbs->ac_present ? "on-line" : "off-line");
+                  sbs->ac.ac_present ? "on-line" : "off-line");
 
-       up(&sbs_sem);
+       sbs_mutex_unlock(sbs);
 
        return 0;
 }
@@ -1275,25 +1300,25 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
 
        result = acpi_battery_select(battery);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_battery_select() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_battery_select() failed"));
                goto end;
        }
 
        result = acpi_battery_get_present(battery);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_battery_get_present() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_battery_get_present() failed"));
                goto end;
        }
 
-       is_present = acpi_battery_is_present(battery);
+       is_present = battery->battery_present;
 
        if (is_present) {
                result = acpi_battery_init(battery);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_battery_init() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_battery_init() failed"));
                        goto end;
                }
                battery->init_state = 1;
@@ -1308,12 +1333,16 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
                                         &acpi_battery_state_fops,
                                         &acpi_battery_alarm_fops, battery);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_generic_add_fs() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_generic_add_fs() failed"));
                goto end;
        }
        battery->alive = 1;
 
+       printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n",
+              ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), dir_name,
+              sbs->battery->battery_present ? "present" : "absent");
+
       end:
        return result;
 }
@@ -1333,8 +1362,8 @@ static int acpi_ac_add(struct acpi_sbs *sbs)
 
        result = acpi_ac_get_present(sbs);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_ac_get_present() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_ac_get_present() failed"));
                goto end;
        }
 
@@ -1343,11 +1372,15 @@ static int acpi_ac_add(struct acpi_sbs *sbs)
                                         ACPI_AC_DIR_NAME,
                                         NULL, &acpi_ac_state_fops, NULL, sbs);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_generic_add_fs() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_generic_add_fs() failed"));
                goto end;
        }
 
+       printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n",
+              ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
+              ACPI_AC_DIR_NAME, sbs->ac.ac_present ? "on-line" : "off-line");
+
       end:
 
        return result;
@@ -1361,45 +1394,85 @@ static void acpi_ac_remove(struct acpi_sbs *sbs)
        }
 }
 
-static void acpi_sbs_update_queue_run(unsigned long data)
+static void acpi_sbs_update_time_run(unsigned long data)
 {
-       acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_queue, (void *)data);
+       acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_time, (void *)data);
 }
 
-static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type)
+static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type)
 {
        struct acpi_battery *battery;
-       int result = 0;
-       int old_ac_present;
-       int old_battery_present;
-       int new_ac_present;
-       int new_battery_present;
-       int id;
+       int result = 0, cnt;
+       int old_ac_present = -1;
+       int old_battery_present = -1;
+       int new_ac_present = -1;
+       int new_battery_present = -1;
+       int id_min = 0, id_max = MAX_SBS_BAT - 1;
        char dir_name[32];
-       int do_battery_init, do_ac_init;
-       s16 old_remaining_capacity;
+       int do_battery_init = 0, do_ac_init = 0;
+       int old_remaining_capacity = 0;
+       int update_ac = 1, update_battery = 1;
+       int up_tm = update_time;
 
-       if (sbs->zombie) {
+       if (sbs_zombie(sbs)) {
                goto end;
        }
 
-       old_ac_present = acpi_ac_is_present(sbs);
+       if (id >= 0) {
+               id_min = id_max = id;
+       }
+
+       if (data_type == DATA_TYPE_COMMON && up_tm > 0) {
+               cnt = up_tm / (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
+               if (sbs->run_cnt % cnt != 0) {
+                       update_battery = 0;
+               }
+       }
+
+       sbs->run_cnt++;
+
+       if (!update_ac && !update_battery) {
+               goto end;
+       }
+
+       old_ac_present = sbs->ac.ac_present;
 
        result = acpi_ac_get_present(sbs);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_ac_get_present() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_ac_get_present() failed"));
        }
 
-       new_ac_present = acpi_ac_is_present(sbs);
+       new_ac_present = sbs->ac.ac_present;
 
        do_ac_init = (old_ac_present != new_ac_present);
+       if (sbs->run_cnt == 1 && data_type == DATA_TYPE_COMMON) {
+               do_ac_init = 1;
+       }
 
-       if (data_type == DATA_TYPE_AC_STATE) {
+       if (do_ac_init) {
+               result = acpi_sbs_generate_event(sbs->device,
+                                                ACPI_SBS_AC_NOTIFY_STATUS,
+                                                new_ac_present,
+                                                ACPI_AC_DIR_NAME,
+                                                ACPI_AC_CLASS);
+               if (result) {
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_sbs_generate_event() failed"));
+               }
+       }
+
+       if (data_type == DATA_TYPE_COMMON) {
+               if (!do_ac_init && !update_battery) {
+                       goto end;
+               }
+       }
+
+       if (data_type == DATA_TYPE_AC_STATE && !do_ac_init) {
                goto end;
        }
 
-       for (id = 0; id < MAX_SBS_BAT; id++) {
+       for (id = id_min; id <= id_max; id++) {
                battery = &sbs->battery[id];
                if (battery->alive == 0) {
                        continue;
@@ -1407,94 +1480,92 @@ static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type)
 
                old_remaining_capacity = battery->state.remaining_capacity;
 
-               old_battery_present = acpi_battery_is_present(battery);
+               old_battery_present = battery->battery_present;
 
                result = acpi_battery_select(battery);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_battery_select() failed\n"));
-               }
-               if (sbs->zombie) {
-                       goto end;
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_battery_select() failed"));
                }
 
                result = acpi_battery_get_present(battery);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_battery_get_present() failed\n"));
-               }
-               if (sbs->zombie) {
-                       goto end;
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_battery_get_present() failed"));
                }
 
-               new_battery_present = acpi_battery_is_present(battery);
+               new_battery_present = battery->battery_present;
 
                do_battery_init = ((old_battery_present != new_battery_present)
                                   && new_battery_present);
-
-               if (sbs->zombie) {
+               if (!new_battery_present)
+                       goto event;
+               if (do_ac_init || do_battery_init) {
+                       result = acpi_battery_init(battery);
+                       if (result) {
+                               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                               "acpi_battery_init() "
+                                               "failed"));
+                       }
+               }
+               if (sbs_zombie(sbs)) {
                        goto end;
                }
-               if (do_ac_init || do_battery_init ||
-                   update_info_mode || sbs->update_info_mode) {
-                       if (sbs->update_info_mode) {
-                               sbs->update_info_mode = 0;
-                       } else {
-                               sbs->update_info_mode = 1;
-                       }
-                       result = acpi_battery_init(battery);
+
+               if ((data_type == DATA_TYPE_COMMON
+                    || data_type == DATA_TYPE_INFO)
+                   && new_battery_present) {
+                       result = acpi_battery_get_info(battery);
                        if (result) {
-                               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                                 "acpi_battery_init() "
-                                                 "failed\n"));
+                               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                               "acpi_battery_get_info() failed"));
                        }
                }
                if (data_type == DATA_TYPE_INFO) {
                        continue;
                }
-
-               if (sbs->zombie) {
+               if (sbs_zombie(sbs)) {
                        goto end;
                }
-               if (new_battery_present) {
-                       result = acpi_battery_get_alarm(battery);
-                       if (result) {
-                               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                                 "acpi_battery_get_alarm() "
-                                                 "failed\n"));
-                       }
-                       if (data_type == DATA_TYPE_ALARM) {
-                               continue;
-                       }
 
+               if ((data_type == DATA_TYPE_COMMON
+                    || data_type == DATA_TYPE_STATE)
+                   && new_battery_present) {
                        result = acpi_battery_get_state(battery);
                        if (result) {
-                               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                                 "acpi_battery_get_state() "
-                                                 "failed\n"));
+                               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                               "acpi_battery_get_state() failed"));
                        }
                }
-               if (sbs->zombie) {
-                       goto end;
+               if (data_type == DATA_TYPE_STATE) {
+                       goto event;
                }
-               if (data_type != DATA_TYPE_COMMON) {
-                       continue;
+               if (sbs_zombie(sbs)) {
+                       goto end;
                }
 
-               if (old_battery_present != new_battery_present) {
-                       sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
-                       result = acpi_sbs_generate_event(sbs->device,
-                                                        ACPI_SBS_BATTERY_NOTIFY_STATUS,
-                                                        new_battery_present,
-                                                        dir_name,
-                                                        ACPI_BATTERY_CLASS);
+               if ((data_type == DATA_TYPE_COMMON
+                    || data_type == DATA_TYPE_ALARM)
+                   && new_battery_present) {
+                       result = acpi_battery_get_alarm(battery);
                        if (result) {
-                               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                                 "acpi_sbs_generate_event() "
-                                                 "failed\n"));
+                               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                               "acpi_battery_get_alarm() "
+                                               "failed"));
                        }
                }
-               if (old_remaining_capacity != battery->state.remaining_capacity) {
+               if (data_type == DATA_TYPE_ALARM) {
+                       continue;
+               }
+               if (sbs_zombie(sbs)) {
+                       goto end;
+               }
+
+             event:
+
+               if (old_battery_present != new_battery_present || do_ac_init ||
+                   old_remaining_capacity !=
+                   battery->state.remaining_capacity) {
                        sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
                        result = acpi_sbs_generate_event(sbs->device,
                                                         ACPI_SBS_BATTERY_NOTIFY_STATUS,
@@ -1502,138 +1573,120 @@ static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type)
                                                         dir_name,
                                                         ACPI_BATTERY_CLASS);
                        if (result) {
-                               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                                 "acpi_sbs_generate_event() failed\n"));
+                               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                               "acpi_sbs_generate_event() "
+                                               "failed"));
                        }
                }
-
-       }
-       if (sbs->zombie) {
-               goto end;
-       }
-       if (data_type != DATA_TYPE_COMMON) {
-               goto end;
-       }
-
-       if (old_ac_present != new_ac_present) {
-               result = acpi_sbs_generate_event(sbs->device,
-                                                ACPI_SBS_AC_NOTIFY_STATUS,
-                                                new_ac_present,
-                                                ACPI_AC_DIR_NAME,
-                                                ACPI_AC_CLASS);
-               if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_sbs_generate_event() failed\n"));
-               }
        }
 
       end:
+
        return result;
 }
 
-static void acpi_sbs_update_queue(void *data)
+static void acpi_sbs_update_time(void *data)
 {
        struct acpi_sbs *sbs = data;
        unsigned long delay = -1;
        int result;
+       unsigned int up_tm = update_time;
 
-       if (sbs->zombie) {
-               goto end;
-       }
+       if (sbs_mutex_lock(sbs))
+               return;
 
-       result = acpi_sbs_update_run(sbs, DATA_TYPE_COMMON);
+       result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_COMMON);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_sbs_update_run() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_sbs_update_run() failed"));
        }
 
-       if (sbs->zombie) {
+       if (sbs_zombie(sbs)) {
                goto end;
        }
 
-       if (update_mode == REQUEST_UPDATE_MODE) {
-               goto end;
+       if (!up_tm) {
+               if (timer_pending(&sbs->update_timer))
+                       del_timer(&sbs->update_timer);
+       } else {
+               delay = (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
+               delay = jiffies + HZ * delay;
+               if (timer_pending(&sbs->update_timer)) {
+                       mod_timer(&sbs->update_timer, delay);
+               } else {
+                       sbs->update_timer.data = (unsigned long)data;
+                       sbs->update_timer.function = acpi_sbs_update_time_run;
+                       sbs->update_timer.expires = delay;
+                       add_timer(&sbs->update_timer);
+               }
        }
 
-       delay = jiffies + HZ * update_time;
-       sbs->update_timer.data = (unsigned long)data;
-       sbs->update_timer.function = acpi_sbs_update_queue_run;
-       sbs->update_timer.expires = delay;
-       add_timer(&sbs->update_timer);
       end:
-       ;
+
+       sbs_mutex_unlock(sbs);
 }
 
 static int acpi_sbs_add(struct acpi_device *device)
 {
        struct acpi_sbs *sbs = NULL;
-       struct acpi_ec_hc *ec_hc = NULL;
-       int result, remove_result = 0;
+       int result = 0, remove_result = 0;
        unsigned long sbs_obj;
-       int id, cnt;
+       int id;
        acpi_status status = AE_OK;
+       unsigned long val;
+
+       status =
+           acpi_evaluate_integer(device->parent->handle, "_EC", NULL, &val);
+       if (ACPI_FAILURE(status)) {
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Error obtaining _EC"));
+               return -EIO;
+       }
 
        sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
        if (!sbs) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "kmalloc() failed\n"));
-               return -ENOMEM;
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR, "kzalloc() failed"));
+               result = -ENOMEM;
+               goto end;
        }
 
-       cnt = 0;
-       while (cnt < 10) {
-               cnt++;
-               ec_hc = acpi_get_ec_hc(device);
-               if (ec_hc) {
-                       break;
-               }
-               msleep(1000);
-       }
+       mutex_init(&sbs->mutex);
 
-       if (!ec_hc) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_get_ec_hc() failed: "
-                                 "NO driver found for EC HC SMBus\n"));
-               result = -ENODEV;
-               goto end;
-       }
+       sbs_mutex_lock(sbs);
 
+       sbs->base = (val & 0xff00ull) >> 8;
        sbs->device = device;
-       sbs->smbus = ec_hc->smbus;
 
        strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME);
        strcpy(acpi_device_class(device), ACPI_SBS_CLASS);
        acpi_driver_data(device) = sbs;
 
-       sbs->update_time = 0;
-       sbs->update_time2 = 0;
-
        result = acpi_ac_add(sbs);
        if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_ac_add() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR, "acpi_ac_add() failed"));
                goto end;
        }
-       result = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj);
-       if (ACPI_FAILURE(result)) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_evaluate_integer() failed\n"));
+       status = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj);
+       if (status) {
+               ACPI_EXCEPTION((AE_INFO, status,
+                               "acpi_evaluate_integer() failed"));
                result = -EIO;
                goto end;
        }
-
        if (sbs_obj > 0) {
                result = acpi_sbsm_get_info(sbs);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_sbsm_get_info() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_sbsm_get_info() failed"));
                        goto end;
                }
                sbs->sbsm_present = 1;
        }
+
        if (sbs->sbsm_present == 0) {
                result = acpi_battery_add(sbs, 0);
                if (result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_battery_add() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_battery_add() failed"));
                        goto end;
                }
        } else {
@@ -1641,9 +1694,8 @@ static int acpi_sbs_add(struct acpi_device *device)
                        if ((sbs->sbsm_batteries_supported & (1 << id))) {
                                result = acpi_battery_add(sbs, id);
                                if (result) {
-                                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                                         "acpi_battery_add() "
-                                                         "failed\n"));
+                                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                                       "acpi_battery_add() failed"));
                                        goto end;
                                }
                        }
@@ -1653,33 +1705,26 @@ static int acpi_sbs_add(struct acpi_device *device)
        sbs->handle = device->handle;
 
        init_timer(&sbs->update_timer);
-       if (update_mode == QUEUE_UPDATE_MODE) {
-               status = acpi_os_execute(OSL_GPE_HANDLER,
-                                        acpi_sbs_update_queue, sbs);
-               if (status != AE_OK) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_os_execute() failed\n"));
-               }
-       }
-       sbs->update_time = update_time;
-       sbs->update_time2 = update_time2;
-
-       printk(KERN_INFO PREFIX "%s [%s]\n",
-              acpi_device_name(device), acpi_device_bid(device));
+       result = acpi_check_update_proc(sbs);
+       if (result)
+               goto end;
 
       end:
+
+       sbs_mutex_unlock(sbs);
+
        if (result) {
                remove_result = acpi_sbs_remove(device, 0);
                if (remove_result) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                         "acpi_sbs_remove() failed\n"));
+                       ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                                       "acpi_sbs_remove() failed"));
                }
        }
 
        return result;
 }
 
-int acpi_sbs_remove(struct acpi_device *device, int type)
+static int acpi_sbs_remove(struct acpi_device *device, int type)
 {
        struct acpi_sbs *sbs;
        int id;
@@ -1688,15 +1733,14 @@ int acpi_sbs_remove(struct acpi_device *device, int type)
                return -EINVAL;
        }
 
-       sbs = (struct acpi_sbs *)acpi_driver_data(device);
-
+       sbs = acpi_driver_data(device);
        if (!sbs) {
                return -EINVAL;
        }
 
+       sbs_mutex_lock(sbs);
+
        sbs->zombie = 1;
-       sbs->update_time = 0;
-       sbs->update_time2 = 0;
        del_timer_sync(&sbs->update_timer);
        acpi_os_wait_events_complete(NULL);
        del_timer_sync(&sbs->update_timer);
@@ -1707,11 +1751,41 @@ int acpi_sbs_remove(struct acpi_device *device, int type)
 
        acpi_ac_remove(sbs);
 
+       sbs_mutex_unlock(sbs);
+
+       mutex_destroy(&sbs->mutex);
+
        kfree(sbs);
 
        return 0;
 }
 
+static void acpi_sbs_rmdirs(void)
+{
+       if (acpi_ac_dir) {
+               acpi_unlock_ac_dir(acpi_ac_dir);
+               acpi_ac_dir = NULL;
+       }
+       if (acpi_battery_dir) {
+               acpi_unlock_battery_dir(acpi_battery_dir);
+               acpi_battery_dir = NULL;
+       }
+}
+
+static int acpi_sbs_resume(struct acpi_device *device)
+{
+       struct acpi_sbs *sbs;
+
+       if (!device)
+               return -EINVAL;
+
+       sbs = device->driver_data;
+
+       sbs->run_cnt = 0;
+
+       return 0;
+}
+
 static int __init acpi_sbs_init(void)
 {
        int result = 0;
@@ -1719,35 +1793,34 @@ static int __init acpi_sbs_init(void)
        if (acpi_disabled)
                return -ENODEV;
 
-       init_MUTEX(&sbs_sem);
-
        if (capacity_mode != DEF_CAPACITY_UNIT
            && capacity_mode != MAH_CAPACITY_UNIT
            && capacity_mode != MWH_CAPACITY_UNIT) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_sbs_init: "
-                                 "invalid capacity_mode = %d\n",
-                                 capacity_mode));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "invalid capacity_mode = %d", capacity_mode));
                return -EINVAL;
        }
 
        acpi_ac_dir = acpi_lock_ac_dir();
        if (!acpi_ac_dir) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_lock_ac_dir() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_lock_ac_dir() failed"));
                return -ENODEV;
        }
 
        acpi_battery_dir = acpi_lock_battery_dir();
        if (!acpi_battery_dir) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_lock_battery_dir() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_lock_battery_dir() failed"));
+               acpi_sbs_rmdirs();
                return -ENODEV;
        }
 
        result = acpi_bus_register_driver(&acpi_sbs_driver);
        if (result < 0) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "acpi_bus_register_driver() failed\n"));
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,
+                               "acpi_bus_register_driver() failed"));
+               acpi_sbs_rmdirs();
                return -ENODEV;
        }
 
@@ -1756,13 +1829,9 @@ static int __init acpi_sbs_init(void)
 
 static void __exit acpi_sbs_exit(void)
 {
-
        acpi_bus_unregister_driver(&acpi_sbs_driver);
 
-       acpi_unlock_ac_dir(acpi_ac_dir);
-       acpi_ac_dir = NULL;
-       acpi_unlock_battery_dir(acpi_battery_dir);
-       acpi_battery_dir = NULL;
+       acpi_sbs_rmdirs();
 
        return;
 }
index bb0e0da39fb15d355be664e5ae23f1dc5e632441..d80dd84e5bfdaab0713177c5cb21db15aac07996 100644 (file)
@@ -1068,7 +1068,9 @@ acpi_add_single_object(struct acpi_device **child,
                }
                break;
        default:
-               STRUCT_TO_INT(device->status) = 0x0F;
+               STRUCT_TO_INT(device->status) =
+                   ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
+                   ACPI_STA_DEVICE_UI      | ACPI_STA_DEVICE_FUNCTIONING;
                break;
        }
 
index ccc11b33d89cd713036ae2d0f5ad98bb9a4bd3f3..2d912b71e5435651226cc1278b9d01e8ea55b3e4 100644 (file)
@@ -350,21 +350,31 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
 {
        struct list_head *node, *next;
 
-       seq_printf(seq, "Device Sleep state     Status\n");
+       seq_printf(seq, "Device\tS-state\t  Status   Sysfs node\n");
 
        spin_lock(&acpi_device_lock);
        list_for_each_safe(node, next, &acpi_wakeup_device_list) {
                struct acpi_device *dev =
                    container_of(node, struct acpi_device, wakeup_list);
+               struct device *ldev;
 
                if (!dev->wakeup.flags.valid)
                        continue;
                spin_unlock(&acpi_device_lock);
-               seq_printf(seq, "%4s    %4d             %s%8s\n",
+
+               ldev = acpi_get_physical_device(dev->handle);
+               seq_printf(seq, "%s\t  S%d\t%c%-8s  ",
                           dev->pnp.bus_id,
                           (u32) dev->wakeup.sleep_state,
-                          dev->wakeup.flags.run_wake ? "*" : "",
+                          dev->wakeup.flags.run_wake ? '*' : ' ',
                           dev->wakeup.state.enabled ? "enabled" : "disabled");
+               if (ldev)
+                       seq_printf(seq, "%s:%s",
+                               ldev->bus ? ldev->bus->name : "no-bus",
+                               ldev->bus_id);
+               seq_printf(seq, "\n");
+               put_device(ldev);
+
                spin_lock(&acpi_device_lock);
        }
        spin_unlock(&acpi_device_lock);
index 807c7116e94b8eaa19471ce66ab4196e0ef66c06..1db833eb2417c5988890dc8ce4b62bed764e0d45 100644 (file)
@@ -347,6 +347,18 @@ static void acpi_tb_convert_fadt(void)
                acpi_gbl_xpm1b_enable.space_id = acpi_gbl_FADT.xpm1a_event_block.space_id;
 
        }
+
+       /*
+        * For ACPI 1.0 FADTs, ensure that reserved fields (which should be zero)
+        * are indeed zero. This will workaround BIOSs that inadvertently placed
+        * values in these fields.
+        */
+       if (acpi_gbl_FADT.header.revision < 3) {
+               acpi_gbl_FADT.preferred_profile = 0;
+               acpi_gbl_FADT.pstate_control = 0;
+               acpi_gbl_FADT.cst_control = 0;
+               acpi_gbl_FADT.boot_flags = 0;
+       }
 }
 
 /******************************************************************************
index 7bdbe5a914d0ce78f34d10325ce0e9eba31fb126..365c306c7cf81e2fcfd75d4be9c3fb1e2c39e303 100644 (file)
@@ -156,11 +156,6 @@ config SATA_INIC162X
        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 @@ config PATA_ALI
          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 @@ config PATA_ATIIXP
 
          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 @@ config ATA_GENERIC
          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 @@ config PATA_HPT366
          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 @@ config PATA_HPT3X3
          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 @@ config PATA_ISAPNP
          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 @@ config PATA_MPIIX
          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 @@ config PATA_PCMCIA
          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 @@ config PATA_QDI
          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 @@ config PATA_RZ1000
          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 @@ config PATA_SC1200
          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.
index 13d7397e0008080fe84dc020f69e7f10a5e2b870..b7055e302650dc4d6dea4e81d6346dc2457208b1 100644 (file)
@@ -22,6 +22,7 @@ obj-$(CONFIG_PATA_ALI)                += pata_ali.o
 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
index fd27227771b48a8038ec867c62f9804fc34aa9af..34c5534ed64c22c65779b418a57793739b98f16f 100644 (file)
@@ -170,6 +170,10 @@ enum {
        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_sg {
 };
 
 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 u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg);
 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 @@ static const struct ata_port_operations ahci_ops = {
        .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 @@ static const struct ata_port_operations ahci_vt8251_ops = {
        .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_operations ahci_vt8251_ops = {
 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 @@ static const struct pci_device_id ahci_pci_tbl[] = {
          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 @@ static inline int ahci_nr_ports(u32 cap)
        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)
+{
+       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)
 {
-       return base + 0x100 + (port * 0x80);
+       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 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in,
        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 @@ static void ahci_start_engine(void __iomem *port_mmio)
        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 @@ static int ahci_stop_engine(void __iomem *port_mmio)
        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 @@ static void ahci_start_fis_rx(void __iomem *port_mmio, u32 cap,
        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 @@ static int ahci_stop_fis_rx(void __iomem *port_mmio)
        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 @@ static void ahci_power_up(void __iomem *port_mmio, u32 cap)
 }
 
 #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 @@ static void ahci_power_down(void __iomem *port_mmio, u32 cap)
 }
 #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 @@ static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg)
        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 @@ static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
 
        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 @@ static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
        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 @@ static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
        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 void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev,
 
 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_clo(struct ata_port *ap)
 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 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class)
        }
 
        /* 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 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class)
        }
 
        /* 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 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class)
        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 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class)
        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 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class)
 
        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_hardreset(struct ata_port *ap, unsigned int *class)
 
 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 int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class)
 
 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_error_intr(struct ata_port *ap, u32 irq_stat)
 
 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 irqreturn_t ahci_interrupt(int irq, void *dev_instance)
 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 unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
 
 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_freeze(struct ata_port *ap)
 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_thaw(struct ata_port *ap)
 
 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_error_handler(struct ata_port *ap)
 
 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_vt8251_error_handler(struct ata_port *ap)
 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_suspend(struct ata_port *ap, pm_message_t mesg)
 
 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_suspend(struct pci_dev *pdev, pm_message_t mesg)
 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 @@ static int ahci_pci_device_resume(struct pci_dev *pdev)
                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_pci_device_resume(struct pci_dev *pdev)
 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 @@ static int ahci_port_start(struct ata_port *ap)
        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 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
                        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 void ahci_print_info(struct ata_probe_ent *probe_ent)
 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 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        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 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        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;
-
-       ahci_print_info(probe_ent);
+       rc = ahci_reset_controller(host);
+       if (rc)
+               return rc;
 
-       if (!ata_device_add(probe_ent))
-               return -ENODEV;
+       ahci_init_controller(host);
+       ahci_print_info(host);
 
-       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)
index d8e79882b8809a97df8eff73efd87f9e228d1bc7..92a491ddd03055555c2926ecf16f23c6a4526749 100644 (file)
  *     A generic parallel ATA driver using libata
  */
 
-/**
- *     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
@@ -144,8 +115,9 @@ static struct ata_port_operations generic_port_ops = {
 
        .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,
index b952c584338fb4fe3f797435a1f720c8b6070ef4..55d306a3e5386e779e12d040a9156040d93a5c69 100644 (file)
@@ -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 @@ struct piix_host_priv {
 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 @@ static const struct ata_port_operations piix_pata_ops = {
        .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 @@ static const struct ata_port_operations ich_pata_ops = {
 
        .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 @@ static const struct ich_laptop ich_laptop[] = {
        /* 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 @@ static const struct ich_laptop ich_laptop[] = {
  *     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 @@ static void ich_pata_cbl_detect(struct ata_port *ap)
        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 @@ static int piix_pata_prereset(struct ata_port *ap)
 
        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 @@ static void piix_pata_error_handler(struct ata_port *ap)
 }
 
 
-/**
- *     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,
index 0abd72d0dec215bb2fa529c4c84b3c8abfe81646..ca67484af1ebe2259164f5b3531d6ee8056ac89f 100644 (file)
@@ -72,7 +72,7 @@ static unsigned int ata_dev_init_params(struct ata_device *dev,
 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 @@ int libata_fua = 0;
 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 @@ void ata_id_c_string(const u16 *id, unsigned char *s,
        *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 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
        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 @@ unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd)
  *     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 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class,
        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 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class,
                        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 @@ static void ata_dev_config_ncq(struct ata_device *dev,
                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 @@ int ata_dev_configure(struct ata_device *dev)
                        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 @@ int ata_dev_configure(struct ata_device *dev)
                                        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 @@ int ata_dev_configure(struct ata_device *dev)
                }
        }
 
-       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 @@ int ata_dev_configure(struct ata_device *dev)
        }
 
        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",
@@ -1806,6 +2042,56 @@ err_out_nosup:
        return rc;
 }
 
+/**
+ *     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 @@ int ata_bus_probe(struct ata_port *ap)
                        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 @@ void ata_port_probe(struct ata_port *ap)
  *     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 @@ int ata_timing_compute(struct ata_device *adev, unsigned short speed,
                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 @@ static int ata_dev_set_mode(struct ata_device *dev)
 }
 
 /**
- *     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 @@ static int ata_dev_set_mode(struct ata_device *dev)
  *     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++) {
@@ -2586,6 +2881,29 @@ int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
        return rc;
 }
 
+/**
+ *     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
@@ -3267,6 +3585,11 @@ static int ata_dev_same_device(struct ata_device *dev, unsigned int new_class,
                               "%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 @@ static void ata_dev_xfermask(struct ata_device *dev)
        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 @@ static void ata_dev_xfermask(struct ata_device *dev)
                               "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 @@ void ata_data_xfer_noirq(struct ata_device *adev, unsigned char *buf,
 
 
 /**
- *     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 @@ static void ata_pio_sector(struct ata_queued_cmd *qc)
        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 @@ static void ata_pio_sector(struct ata_queued_cmd *qc)
                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 @@ static void ata_pio_sector(struct ata_queued_cmd *qc)
 }
 
 /**
- *     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 @@ static void ata_pio_sectors(struct ata_queued_cmd *qc)
 
                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 @@ void ata_dev_init(struct ata_device *dev)
 }
 
 /**
- *     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 @@ void ata_port_init(struct ata_port *ap, struct ata_host *host,
        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,100 +5971,209 @@ void ata_port_init(struct ata_port *ap, struct ata_host *host,
        ap->stats.unhandled_irq = 1;
        ap->stats.idle_irq = 1;
 #endif
+       return ap;
+}
 
-       memcpy(&ap->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports));
+static void ata_host_release(struct device *gendev, void *res)
+{
+       struct ata_host *host = dev_get_drvdata(gendev);
+       int i;
+
+       for (i = 0; i < host->n_ports; i++) {
+               struct ata_port *ap = host->ports[i];
+
+               if (!ap)
+                       continue;
+
+               if ((host->flags & ATA_HOST_STARTED) && ap->ops->port_stop)
+                       ap->ops->port_stop(ap);
+       }
+
+       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)
+                       continue;
+
+               if (ap->scsi_host)
+                       scsi_host_put(ap->scsi_host);
+
+               kfree(ap);
+               host->ports[i] = NULL;
+       }
+
+       dev_set_drvdata(gendev, NULL);
 }
 
 /**
- *     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
+ *     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
  *
- *     Initialize SCSI host @shost associated with ATA port @ap.
+ *     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 caller.
+ *     Inherited from calling layer (may sleep).
  */
-static void ata_port_init_shost(struct ata_port *ap, struct Scsi_Host *shost)
+struct ata_host *ata_host_alloc(struct device *dev, int max_ports)
 {
-       ap->scsi_host = shost;
+       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);
 
-       shost->unique_id = ap->print_id;
-       shost->max_id = 16;
-       shost->max_lun = 1;
-       shost->max_channel = 1;
-       shost->max_cmd_len = 12;
+       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_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
+ *     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
  *
- *     Attach low-level ATA driver to system.
- *
- *     LOCKING:
- *     PCI/etc. bus probe sem.
+ *     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:
- *     New ata_port on success, for NULL on error.
+ *     Allocate ATA host on success, NULL on failure.
+ *
+ *     LOCKING:
+ *     Inherited from calling layer (may sleep).
  */
-static struct ata_port * ata_port_add(const struct ata_probe_ent *ent,
-                                     struct ata_host *host,
-                                     unsigned int port_no)
+struct ata_host *ata_host_alloc_pinfo(struct device *dev,
+                                     const struct ata_port_info * const * ppi,
+                                     int n_ports)
 {
-       struct Scsi_Host *shost;
-       struct ata_port *ap;
-
-       DPRINTK("ENTER\n");
+       const struct ata_port_info *pi;
+       struct ata_host *host;
+       int i, j;
 
-       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);
+       host = ata_host_alloc(dev, n_ports);
+       if (!host)
                return NULL;
-       }
 
-       shost = scsi_host_alloc(ent->sht, sizeof(struct ata_port));
-       if (!shost)
-               return NULL;
+       for (i = 0, j = 0, pi = NULL; i < host->n_ports; i++) {
+               struct ata_port *ap = host->ports[i];
 
-       shost->transportt = &ata_scsi_transport_template;
+               if (ppi[j])
+                       pi = ppi[j++];
 
-       ap = ata_shost_to_port(shost);
+               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;
 
-       ata_port_init(ap, host, ent, port_no);
-       ata_port_init_shost(ap, shost);
+               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 ap;
+       return host;
 }
 
-static void ata_host_release(struct device *gendev, void *res)
+/**
+ *     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)
 {
-       struct ata_host *host = dev_get_drvdata(gendev);
-       int i;
+       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 (ap && ap->ops->port_stop)
-                       ap->ops->port_stop(ap);
+               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);
        }
 
-       if (host->ops->host_stop)
-               host->ops->host_stop(host);
+       host->flags |= ATA_HOST_STARTED;
+       return 0;
 
-       for (i = 0; i < host->n_ports; i++) {
+ err_out:
+       while (--i >= 0) {
                struct ata_port *ap = host->ports[i];
 
-               if (ap)
-                       scsi_host_put(ap->scsi_host);
-
-               host->ports[i] = NULL;
+               if (ap->ops->port_stop)
+                       ap->ops->port_stop(ap);
        }
-
-       dev_set_drvdata(gendev, NULL);
+       return rc;
 }
 
 /**
@@ -5755,7 +6187,7 @@ static void ata_host_release(struct device *gendev, void *res)
  *     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,155 +6198,95 @@ void ata_host_init(struct ata_host *host, struct device *dev,
 }
 
 /**
- *     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;
-
-       DPRINTK("ENTER\n");
+       int i, rc;
 
-       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 */
+       /* set cable, sata_spd_limit and report */
        for (i = 0; i < host->n_ports; i++) {
-               struct ata_port *ap;
-               unsigned long xfer_mode_mask;
-               int irq_line = ent->irq;
+               struct ata_port *ap = host->ports[i];
+               int irq_line;
+               u32 scontrol;
+               unsigned long xfer_mask;
 
-               ap = ata_port_add(ent, host, i);
-               host->ports[i] = ap;
-               if (!ap)
-                       goto err_out;
+               /* set SATA cable type if still unset */
+               if (ap->cbl == ATA_CBL_NONE && (ap->flags & ATA_FLAG_SATA))
+                       ap->cbl = ATA_CBL_SATA;
 
-               /* 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;
+               /* init sata_spd_limit to the current value */
+               if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) {
+                       int spd = (scontrol >> 4) & 0xf;
+                       ap->hw_sata_spd_limit &= (1 << spd) - 1;
                }
+               ap->sata_spd_limit = ap->hw_sata_spd_limit;
 
-               /* Report the secondary IRQ for second channel legacy */
-               if (i == 1 && ent->irq2)
-                       irq_line = ent->irq2;
+               /* report the secondary IRQ for second channel legacy */
+               irq_line = host->irq;
+               if (i == 1 && host->irq2)
+                       irq_line = host->irq2;
 
-               xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) |
-                               (ap->mwdma_mask << ATA_SHIFT_MWDMA) |
-                               (ap->pio_mask << ATA_SHIFT_PIO);
+               xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask,
+                                             ap->udma_mask);
 
                /* 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;
-               }
+               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");
        }
 
-       /* resource acquisition complete */
-       devres_remove_group(dev, ata_device_add);
-
        /* perform each probe synchronously */
        DPRINTK("probe begin\n");
        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap = host->ports[i];
-               u32 scontrol;
                int rc;
 
-               /* init sata_spd_limit to the current value */
-               if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) {
-                       int spd = (scontrol >> 4) & 0xf;
-                       ap->hw_sata_spd_limit &= (1 << spd) - 1;
-               }
-               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
-                        */
-               }
-
+               /* probe */
                if (ap->ops->error_handler) {
                        struct ata_eh_info *ehi = &ap->eh_info;
                        unsigned long flags;
@@ -5959,15 +6331,51 @@ int ata_device_add(const struct ata_probe_ent *ent)
                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 @@ void ata_host_detach(struct ata_host *host)
                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 @@ const struct ata_port_operations ata_dummy_port_ops = {
        .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_normal);
 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_load);
 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_altstatus);
 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 @@ EXPORT_SYMBOL_GPL(ata_timing_merge);
 
 #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_dummy_irq_on);
 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);
index 39f556c0299265ffb99a4430588f7aa9cac194ff..2bff9adcacf15de02b7f0f6c35f83116410df606 100644 (file)
@@ -1056,7 +1056,7 @@ static void ata_eh_analyze_serror(struct ata_port *ap)
        }
        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 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc,
                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 @@ static int ata_eh_reset(struct ata_port *ap, int classify,
                                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 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap,
                }
        }
 
+       /* 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.
         */
index e9364434182c437fe8e0cb73e38d60a73803ed29..9afba2ba489e52097bc5fca2fe8fd98747b6b887 100644 (file)
@@ -104,7 +104,7 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = {
  * 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 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
                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 @@ static inline int __ata_scsi_queuecmd(struct scsi_cmnd *scmd,
 {
        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 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd,
        }
 }
 
+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 *ata_sas_port_alloc(struct ata_host *host,
                                    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 ata_sas_port_init(struct ata_port *ap)
 {
        int rc = ap->ops->port_start(ap);
 
-       if (!rc)
+       if (!rc) {
+               ap->print_id = ata_print_id++;
                rc = ata_bus_probe(ap);
+       }
 
        return rc;
 }
index 2ffcca063d80adc3405ab16c484ab7da8788a5d5..8af18ad1ca7f4de2856d6a21b7b495771305120f 100644 (file)
@@ -526,168 +526,399 @@ static int ata_resources_present(struct pci_dev *pdev, int port)
        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
- *
- *     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().
- *
- *     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.
+ *     ata_pci_init_bmdma - acquire PCI BMDMA resources and init ATA host
+ *     @host: target ATA host
+ *
+ *     Acquire PCI BMDMA resources and initialize @host accordingly.
+ *
+ *     LOCKING:
+ *     Inherited from calling layer (may sleep).
+ *
+ *     RETURNS:
+ *     0 on success, -errno otherwise.
  */
+static int ata_pci_init_bmdma(struct ata_host *host)
+{
+       struct device *gdev = host->dev;
+       struct pci_dev *pdev = to_pci_dev(gdev);
+       int i, rc;
+
+       /* 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;
+       }
+
+       return 0;
+}
 
-struct ata_probe_ent *
-ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int ports)
+/**
+ *     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;
-       int i, p = 0;
-       void __iomem * const *iomap;
-
-       /* 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;
+       struct device *gdev = host->dev;
+       struct pci_dev *pdev = to_pci_dev(gdev);
+       int i, rc;
+
+       /* 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);
                }
        }
 
-       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 (!port_mask) {
+               dev_printk(KERN_ERR, gdev, "no available port\n");
+               return -ENODEV;
        }
 
-       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;
+       /* 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;
                }
-               ata_std_ports(&probe_ent->port[p]);
-               probe_ent->pinfo2 = port[1];
-               p++;
+               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);
        }
 
-       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_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)
 {
-       struct ata_probe_ent *probe_ent;
-       void __iomem *iomap[5] = { }, *bmdma;
-
-       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;
+       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;
 
-       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;
+       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);
        }
+}
 
-       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;
-               }
-               ata_std_ports(&probe_ent->port[0]);
-       } else
-               probe_ent->dummy_port_mask |= ATA_PORT_PRIMARY;
+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;
+       }
 
-       if (port_mask & ATA_PORT_SECONDARY) {
-               if (probe_ent->irq)
-                       probe_ent->irq2 = ATA_SECONDARY_IRQ(pdev);
+       /* 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;
+               }
+
+               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;
                }
-               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;
+               /* record irq allocation in legacy_dr */
+               legacy_dr->irq[i] = irq;
+               legacy_dr->irq_dev_id[i] = dev_id[i];
 
-       return probe_ent;
-}
+               /* 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
@@ -718,8 +949,8 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
                      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 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
 
           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 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
 #endif
        }
 
+       /* 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;
+       }
+
        if (!legacy_mode) {
-               rc = pci_request_regions(pdev, DRV_NAME);
-               if (rc) {
-                       pcim_pin_device(pdev);
+               unsigned int port_mask;
+
+               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 {
-               /* 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);
-       }
+               int was_busy = 0;
 
-       /* we have legacy mode, but all ports are unavailable */
-       if (legacy_mode == (1 << 3)) {
-               rc = -EBUSY;
-               goto err_out;
+               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);
        }
 
-       /* 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);
+       /* 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) {
-               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 (!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 (!probe_ent) {
-               rc = -ENOMEM;
+       if (rc)
                goto err_out;
-       }
-
-       pci_set_master(pdev);
 
-       if (!ata_device_add(probe_ent)) {
-               rc = -ENODEV;
+       /* register */
+       rc = ata_host_register(host, port_info[0]->sht);
+       if (rc)
                goto err_out;
-       }
 
-       devm_kfree(dev, probe_ent);
        devres_remove_group(dev, NULL);
        return 0;
 
@@ -893,12 +1096,12 @@ int ata_pci_clear_simplex(struct pci_dev *pdev)
        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;
 }
index 1f1e3a51f85965f7cebadb3f6b10cfbdb07afb29..5f4d40cd32888a9aaae5c3dc76a2bb3d84bd69aa 100644 (file)
@@ -52,6 +52,7 @@ enum {
        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 int ata_flush_cache(struct ata_device *dev);
 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 @@ static inline int ata_acpi_push_id(struct ata_port *ap, unsigned int ix)
 #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);
index 11ea552a58cac00507f7853e1e53a598ec5afe41..d40edebb510a59ba41b6fbbd1600a8d9530a19a8 100644 (file)
@@ -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
@@ -89,59 +89,6 @@ static int ali_c2_cable_detect(struct ata_port *ap)
                return ATA_CBL_PATA80;
 }
 
-/**
- *     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
@@ -151,7 +98,7 @@ static void ali_early_error_handler(struct ata_port *ap)
  *     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 @@ static unsigned long ali_20_filter(const struct ata_port *ap, struct ata_device
        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 @@ static void ali_set_dmamode(struct ata_port *ap, struct ata_device *adev)
 
 /**
  *     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 @@ static void ali_set_dmamode(struct ata_port *ap, struct ata_device *adev)
  *     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 @@ static struct ata_port_operations ali_early_port_ops = {
 
        .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 @@ static struct ata_port_operations ali_20_port_ops = {
 
        .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 @@ static struct ata_port_operations ali_c2_port_ops = {
 
        .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 @@ static struct ata_port_operations ali_c5_port_ops = {
 
        .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,
index 18381762908bf8b474918bc5c4f93c5fcca7026c..536ee892ab72da7aa2b789010c1bf60381de893d 100644 (file)
@@ -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 @@ static void timing_setup(struct ata_port *ap, struct ata_device *adev, int offse
 }
 
 /**
- *     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 @@ static void amd_error_handler(struct ata_port *ap)
                                      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 }
-       };
-
-       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);
-
-}
+       u8 ata66;
 
-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 void amd133_set_dmamode(struct ata_port *ap, struct ata_device *adev)
  */
 
 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 @@ static void nv_error_handler(struct ata_port *ap)
                               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 @@ static struct ata_port_operations amd33_port_ops = {
 
        .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 @@ static struct ata_port_operations amd66_port_ops = {
 
        .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 @@ static struct ata_port_operations amd100_port_ops = {
        .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 @@ static struct ata_port_operations amd133_port_ops = {
        .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 @@ static struct ata_port_operations nv100_port_ops = {
        .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 @@ static struct ata_port_operations nv133_port_ops = {
        .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,
index 21c30282717ccafea9a541f9173552467bec932a..00e9ec342db001cda8e2ff32e9e29435f10fb92e 100644 (file)
@@ -49,8 +49,6 @@ static int artop6210_pre_reset(struct ata_port *ap)
 
        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 @@ static int artop6260_pre_reset(struct ata_port *ap)
        };
 
        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 @@ static void artop6260_set_piomode(struct ata_port *ap, struct ata_device *adev)
 /**
  *     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 @@ static const struct ata_port_operations artop6210_ops = {
        .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 @@ static const struct ata_port_operations artop6260_ops = {
        .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,
index 51d9923be02efe9e03f75b107f7ba97581f4db60..39c871a3ddac9578fb8a3be5437eb2ecc69667b4 100644 (file)
@@ -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 @@ enum {
 
 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 @@ static void atiixp_error_handler(struct ata_port *ap)
        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 @@ static struct ata_port_operations atiixp_port_ops = {
        .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 (file)
index 0000000..2105985
--- /dev/null
@@ -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);
+               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);
+               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);
index 5b13bdd1edc0863049143034b7b23b8d1ef60b3a..3989cc577fcd503b6e073a001f89eb72d935b6f0 100644 (file)
@@ -75,13 +75,7 @@ enum {
        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 @@ static int cmd648_pre_reset(struct ata_port *ap)
        /* 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 @@ static struct ata_port_operations cmd64x_port_ops = {
 
        .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 @@ static struct ata_port_operations cmd646r1_port_ops = {
 
        .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 @@ static struct ata_port_operations cmd648_port_ops = {
 
        .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,
index 55cc293e74873d176aa78606f16253c0d5b82422..79bef0d1fad337106eb77d419bf192090834fc80 100644 (file)
@@ -139,18 +139,6 @@ static void cs5520_set_piomode(struct ata_port *ap, struct ata_device *adev)
        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 @@ static struct ata_port_operations cs5520_port_ops = {
 
        .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 @@ static struct ata_port_operations cs5520_port_ops = {
        .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 @@ static struct ata_port_operations cs5520_port_ops = {
        .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] = &pi;
+       if (pcicfg & 2)
+               ppi[1] = &pi;
+
        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 */
-
-       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];
-
-       /* The secondary lurks at different addresses but is otherwise
-          the same beastie */
-
-       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;
-
-       /* Let libata fill in the port details */
-       ata_std_ports(&probe[0].port[0]);
-       ata_std_ports(&probe[1].port[0]);
-
-       /* 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;
+       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);
+
+       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);
+
+       /* activate the host */
+       pci_set_master(pdev);
+       rc = ata_host_start(host);
+       if (rc)
+               return rc;
+
+       for (i = 0; i < 2; i++) {
+               static const int irq[] = { 14, 15 };
+               struct ata_port *ap = host->ports[0];
+
+               if (ata_port_is_dummy(ap))
+                       continue;
+
+               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);
 }
 
 /**
index db63e80e608b3b65baefaba969c2706baf92d802..29642d5ee1891ee3d3aef8e40d51008f986b2fbc 100644 (file)
@@ -160,18 +160,6 @@ static unsigned int cs5530_qc_issue_prot(struct ata_queued_cmd *qc)
        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 @@ static struct ata_port_operations cs5530_port_ops = {
 
        .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,
index 1572e5c9031af86c58c5d8a009854144e5fecc59..08cccc9c659b9cebbbe7ff13fe2c1a1cdd292d2c 100644 (file)
 #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 @@ static struct ata_port_operations cs5535_port_ops = {
 
        .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,
index f69dde5f70665a37e2a019a23f7563177f1cbabf..6ec049c3b1dcf4e0fbe03b374a0d17f69672a405 100644 (file)
@@ -41,17 +41,6 @@ enum {
        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 @@ static struct ata_port_operations cy82c693_port_ops = {
 
        .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,
index dac7a6554f6c28c0febe60cb9320c61130b33bd0..a3216850bba15e10938da4f2335f50e863b516e5 100644 (file)
 #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 @@ static int efar_pre_reset(struct ata_port *ap)
                { 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);
 }
 
@@ -66,6 +59,25 @@ static void efar_error_handler(struct ata_port *ap)
        ata_bmdma_drive_eh(ap, efar_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
 }
 
+/**
+ *     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
@@ -256,6 +268,7 @@ static const struct ata_port_operations efar_ops = {
        .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,
index baf35f87603061220fb7da4930fa0de0e7e967e3..93cfa6d300a5a08196d789817a4c93938b7b1da6 100644 (file)
@@ -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 @@ static int hpt_dma_blacklisted(const struct ata_device *dev, char *modestr, cons
 
 /**
  *     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 @@ static unsigned long hpt366_filter(const struct ata_port *ap, struct ata_device
                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 @@ static u32 hpt36x_find_mode(struct ata_port *ap, int speed)
        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 @@ static struct ata_port_operations hpt366_port_ops = {
        .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,
index f331eeeafa0f39d75302023953c676910d446e64..41d8312963474447feb7faf6284a80efaa00cc53 100644 (file)
@@ -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 @@ struct hpt_chip {
  * 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      },
-
-       {       XFER_MW_DMA_2,  0x26514e21      },
-       {       XFER_MW_DMA_1,  0x26514e33      },
-       {       XFER_MW_DMA_0,  0x26514e97      },
-
-       {       XFER_PIO_4,     0x06514e21      },
-       {       XFER_PIO_3,     0x06514e22      },
-       {       XFER_PIO_2,     0x06514e33      },
-       {       XFER_PIO_1,     0x06914e43      },
-       {       XFER_PIO_0,     0x06914e57      },
-       {       0,              0x06514e57      }
+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,        0x22406c31 },
+       { XFER_MW_DMA_1,        0x22406c33 },
+       { XFER_MW_DMA_0,        0x22406c97 },
+
+       { 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      },
-
-       {       XFER_MW_DMA_2,  0x26514e21      },
-       {       XFER_MW_DMA_1,  0x26514e33      },
-       {       XFER_MW_DMA_0,  0x26514e97      },
-
-       {       XFER_PIO_4,     0x06514e21      },
-       {       XFER_PIO_3,     0x06514e22      },
-       {       XFER_PIO_2,     0x06514e33      },
-       {       XFER_PIO_1,     0x06914e43      },
-       {       XFER_PIO_0,     0x06914e57      },
-       {       0,              0x06514e57      }
+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,        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 }
 };
 
-/* 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      },
-
-       {       XFER_MW_DMA_2,  0x22406c31      },
-       {       XFER_MW_DMA_1,  0x22406c33      },
-       {       XFER_MW_DMA_0,  0x22406c97      },
-
-       {       XFER_PIO_4,     0x06414e31      },
-       {       XFER_PIO_3,     0x06414e42      },
-       {       XFER_PIO_2,     0x06414e53      },
-       {       XFER_PIO_1,     0x06814e93      },
-       {       XFER_PIO_0,     0x06814ea7      },
-       {       0,              0x06814ea7      }
+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,        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 }
 };
 
-/* 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 @@ static const struct hpt_chip hpt370a = {
        "HPT370A",
        48,
        {
-               hpt370a_timings_33,
+               hpt37x_timings_33,
                NULL,
-               hpt370a_timings_50,
-               hpt370a_timings_66
+               hpt37x_timings_50,
+               NULL
        }
 };
 
@@ -274,10 +149,10 @@ static const struct hpt_chip hpt372 = {
        "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 @@ static const struct hpt_chip hpt302 = {
        "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 @@ static const struct hpt_chip hpt371 = {
        "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 @@ static const struct hpt_chip hpt372a = {
        "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 @@ static const struct hpt_chip hpt374 = {
        "HPT374",
        48,
        {
-               hpt374_timings_33,
+               hpt37x_timings_33,
                NULL,
                NULL,
                NULL
@@ -397,13 +272,12 @@ static const char *bad_ata100_5[] = {
 
 /**
  *     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 @@ static unsigned long hpt370_filter(const struct ata_port *ap, struct ata_device
                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 @@ static int hpt37x_pre_reset(struct ata_port *ap)
                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 @@ static int hpt374_pre_reset(struct ata_port *ap)
                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 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
                .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 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
 
        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 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
                        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 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
                                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 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
         */
 
        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 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
                /* 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 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
         *      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 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
                        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.
-                */
-
-               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();
-               }
+                *      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 && 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);
index 65f2e180e7fa285644e9bf38522d39846da446b5..6a34521b9e01a7ded70309d3b528c0d7b4f1410e 100644 (file)
@@ -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 @@ static u32 hpt3x2n_find_mode(struct ata_port *ap, int speed)
 }
 
 /**
- *     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 @@ static int hpt3xn_pre_reset(struct ata_port *ap)
        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 @@ static struct ata_port_operations hpt3x2n_port_ops = {
        .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 @@ static int hpt3x2n_pci_clock(struct pci_dev *pdev)
 {
        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 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id)
        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 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id)
                        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 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id)
        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 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id)
                        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 int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id)
 
 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), },
index 813485c8526c433a0d7931ea429351680ef7aeb5..ac28ec8c50aa61a19f03c704c843f50b8709ba05 100644 (file)
 #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 @@ static struct ata_port_operations hpt3x3_port_ops = {
 
        .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,
index 1a61cc89174191cb65e9e8ef6474b6cf74b633d2..d042efdfbac4d5483f277306146dbd9384b32b7e 100644 (file)
@@ -49,13 +49,13 @@ static struct ata_port_operations isapnp_port_ops = {
        .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 struct ata_port_operations isapnp_port_ops = {
 
 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 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev
        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);
 }
 
 /**
index ea734701555ee8d0f1ecb9810fdf561a755b33ed..011306ef8334f8d7b701d6748190cb858fd79602 100644 (file)
@@ -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 int it8213_pre_reset(struct ata_port *ap)
        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:
@@ -62,10 +53,28 @@ static void it8213_error_handler(struct ata_port *ap)
        ata_bmdma_drive_eh(ap, it8213_pre_reset, ata_std_softreset, NULL, ata_std_postreset);
 }
 
+/**
+ *     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 @@ static const struct ata_port_operations it8213_ops = {
        .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,
index 35ecb2ba067b8b09453d0236071c4bde42a51214..f1f8cec8c224aad4ae1375eb0ea551a810652966 100644 (file)
@@ -80,7 +80,7 @@
 
 
 #define DRV_NAME "pata_it821x"
-#define DRV_VERSION "0.3.4"
+#define DRV_VERSION "0.3.6"
 
 struct it821x_dev
 {
@@ -112,31 +112,6 @@ struct it821x_dev
 
 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
@@ -520,7 +495,6 @@ static int it821x_smart_set_mode(struct ata_port *ap, struct ata_device **unused
 
 /**
  *     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 @@ static int it821x_smart_set_mode(struct ata_port *ap, struct ata_device **unused
  *     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 @@ static struct ata_port_operations it821x_smart_port_ops = {
 
        .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 @@ static struct ata_port_operations it821x_passthru_port_ops = {
 
        .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,
index c6f0e192755156523ccb3cb93cf84e656c2833fe..420c343e57111107d88684557e8ea753f485fb39 100644 (file)
@@ -129,8 +129,8 @@ static struct ata_port_operations ixp4xx_port_ops = {
        .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 void ixp4xx_setup_port(struct ata_ioports *ioaddr,
 
 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 @@ static __devinit int ixp4xx_pata_probe(struct platform_device *pdev)
        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 @@ static __devinit int ixp4xx_pata_probe(struct platform_device *pdev)
        *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)
index 86fbcd6a742b5dbb486b471806e496ef8ebf6712..707099291e017ae5beca51a8f866a6b1df99ebd1 100644 (file)
@@ -162,6 +162,7 @@ static struct ata_port_operations simple_port_ops = {
        .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 @@ static struct ata_port_operations legacy_port_ops = {
        .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 @@ static struct ata_port_operations pdc20230_port_ops = {
        .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 @@ static struct ata_port_operations ht6560a_port_ops = {
        .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 @@ static struct ata_port_operations ht6560b_port_ops = {
        .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 @@ static struct ata_port_operations opti82c611a_port_ops = {
        .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 @@ static struct ata_port_operations opti82c46x_port_ops = {
        .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 struct ata_port_operations opti82c46x_port_ops = {
 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 @@ static __init int legacy_init_one(int port, unsigned long io, unsigned long ctrl
        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 = -ENODEV;
-       if (!ata_device_add(&ae))
+       ret = -ENOMEM;
+       host = ata_host_alloc(&pdev->dev, 1);
+       if (!host)
+               goto fail;
+       ap = host->ports[0];
+
+       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);
index 6dd7c4ef3e66bbd93832a2b4e36398a36f5756e2..d9b94a1b6954ed384b3d351858cd91f4a7baed70 100644 (file)
@@ -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 @@ static int marvell_pre_reset(struct ata_port *ap)
        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 @@ static const struct ata_port_operations marvell_ops = {
        .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,
index 882c36eaf2935ce8e1184c56cc915b699e2aee61..9587a89f9683d98d46b4e359441b35dc598c11a9 100644 (file)
@@ -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 @@ static struct ata_port_operations mpc52xx_ata_port_ops = {
        .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;
-
-       INIT_LIST_HEAD(&ae->node);
-       ae->dev = dev;
-       ae->irq = priv->ata_irq;
-
+       struct ata_host *host;
+       struct ata_port *ap;
+       struct ata_ioports *aio;
+       int rc;
+
+       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 @@ mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv)
        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 *
index 4abe45ac19a2c121c03655875dd63b951c2c6d35..987c5fafab087e72b1490c30ca9983d0bb506f3b 100644 (file)
@@ -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 @@ static int mpiix_pre_reset(struct ata_port *ap)
 
        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 @@ static struct ata_port_operations mpiix_port_ops = {
        .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 struct ata_port_operations mpiix_port_ops = {
 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 @@ static int mpiix_init_one(struct pci_dev *dev, const struct pci_device_id *id)
        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 @@ static int mpiix_init_one(struct pci_dev *dev, const struct pci_device_id *id)
           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[] = {
index 38f99b38a5ea97a861e1a8cb2d1d27f324ea38cc..dbba5b77d79c907e0444a89a1732848cab277891 100644 (file)
 #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 @@ static const struct ata_port_operations netcell_ops = {
 
        .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,
index 9944a28daa9c681874413ea23f8d95a938dc678a..078aeda9cf8d81eacea9e824adec6f199d683a52 100644 (file)
 #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 @@ static int ns87410_pre_reset(struct ata_port *ap)
 
        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 @@ static struct ata_port_operations ns87410_port_ops = {
        .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,
index da68cd19efd612cfc5f5bde902b98605ab23683f..dea4690340d1ed291cfd58c011d4f40ab55c11a1 100644 (file)
@@ -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 @@ static int oldpiix_pre_reset(struct ata_port *ap)
 
        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 @@ static void oldpiix_pata_error_handler(struct ata_port *ap)
 /**
  *     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 @@ static const struct ata_port_operations oldpiix_pata_ops = {
        .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,
index 3fd3a35c2241bf1015189e25db264389480ef128..13b63e21838d9ff94c0be60051c5a6e1190e78ee 100644 (file)
@@ -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 @@ static int opti_pre_reset(struct ata_port *ap)
 
        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 @@ static struct ata_port_operations opti_port_ops = {
        .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,
index 9764907e8a134e20a238478bab1f932d9f224c0e..b70e04c144dfa591ec6afe925b9b9acaa756b118 100644 (file)
@@ -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 @@ static int optidma_pre_reset(struct ata_port *ap)
        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 @@ static void optidma_lock(struct ata_port *ap)
 }
 
 /**
- *     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 @@ static void optidma_lock(struct ata_port *ap)
  *     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 @@ static void optidma_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mo
 }
 
 /**
- *     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 @@ static void optidma_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mo
  *     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 @@ static void optiplus_set_mode(struct ata_port *ap, struct ata_device *adev, u8 m
        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 optiplus_set_mode(struct ata_port *ap, struct ata_device *adev, u8 m
 
 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_pio_mode(struct ata_port *ap, struct ata_device *adev)
 
 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 optidma_set_dma_mode(struct ata_port *ap, struct ata_device *adev)
 
 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_pio_mode(struct ata_port *ap, struct ata_device *adev)
 
 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 @@ static u8 optidma_make_bits43(struct ata_device *adev)
 }
 
 /**
- *     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);
-
-       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);
+       int rc  = ata_do_set_mode(ap, r_failed);
+       if (rc == 0) {
+               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);
+       }
+       return rc;
 }
 
 static struct scsi_host_template optidma_sht = {
@@ -381,7 +383,8 @@ static struct ata_port_operations optidma_port_ops = {
        .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 @@ static struct ata_port_operations optiplus_port_ops = {
        .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,
index 103720f873c8f31d8384b0f54b5b97975c2ad3bb..75dc84797ff322eb9b9ffce033c2581dc5f5f4c2 100644 (file)
@@ -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 @@ struct ata_pcmcia_info {
        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 scsi_host_template pcmcia_sht = {
 };
 
 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 @@ static struct ata_port_operations pcmcia_port_ops = {
        .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 @@ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
 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 @@ next_entry:
         *      Having done the PCMCIA plumbing the ATA side is relatively
         *      sane.
         */
-
-       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]);
-
-       ret = -ENODEV;
-       if (ata_device_add(&ae) == 0)
+       ret = -ENOMEM;
+       host = ata_host_alloc(&pdev->dev, 1);
+       if (!host)
+               goto failed;
+       ap = host->ports[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);
+
+       /* activate */
+       ret = ata_host_activate(host, pdev->irq.AssignedIRQ, ata_interrupt,
+                               IRQF_SHARED, &pcmcia_sht);
+       if (ret)
                goto failed;
 
        info->ndev = 1;
index 93bcdadb7be3a17c8ff82f00dc5a7425cf3c9d26..a61cbc110688d1bf97859666aaa5fc7462cf7d2c 100644 (file)
@@ -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 int pdc2027x_init_one(struct pci_dev *pdev, const struct pci_device_id *e
 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 scsi_host_template pdc2027x_sht = {
 
 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 @@ static struct ata_port_operations pdc2027x_pata100_ops = {
        .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 @@ static struct ata_port_operations pdc2027x_pata133_ops = {
        .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 @@ static struct ata_port_operations pdc2027x_pata133_ops = {
        .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_operations pdc2027x_pata133_ops = {
 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 @@ static struct ata_port_info pdc2027x_port_info[] = {
        },
        /* 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 @@ static inline void __iomem *dev_mmio(struct ata_port *ap, struct ata_device *ade
 }
 
 /**
- *     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 @@ static inline void __iomem *dev_mmio(struct ata_port *ap, struct ata_device *ade
  *     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 @@ static void pdc2027x_cbl_detect(struct ata_port *ap)
 
        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 @@ static int pdc2027x_prereset(struct ata_port *ap)
        /* Check whether port enabled */
        if (!pdc2027x_port_enabled(ap))
                return -ENOENT;
-       pdc2027x_cbl_detect(ap);
        return ata_std_prereset(ap);
 }
 
@@ -333,6 +331,32 @@ static void pdc2027x_error_handler(struct ata_port *ap)
        ata_bmdma_drive_eh(ap, pdc2027x_prereset, ata_std_softreset, NULL, ata_std_postreset);
 }
 
+/**
+ *     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
@@ -444,17 +468,22 @@ static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev)
 }
 
 /**
- *     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 @@ static void pdc2027x_post_set_mode(struct ata_port *ap)
                        }
                }
        }
+       return 0;
 }
 
 /**
@@ -521,12 +551,12 @@ static int pdc2027x_check_atapi_dma(struct ata_queued_cmd *qc)
 
 /**
  * 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 @@ retry:
  * 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 @@ static void pdc_adjust_pll(struct ata_probe_ent *probe_ent, long pll_clock, unsi
 
 /**
  * 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 @@ static long pdc_detect_pll_input_clock(struct ata_probe_ent *probe_ent)
        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 @@ static long pdc_detect_pll_input_clock(struct ata_probe_ent *probe_ent)
 
 /**
  * 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 @@ static int pdc_hardware_init(struct pci_dev *pdev, struct ata_probe_ent *pe, uns
         * 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 @@ static void pdc_ata_setup_port(struct ata_ioports *port, void __iomem *base)
  * 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 __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_de
 {
        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 @@ static int __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_de
        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 @@ static int __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_de
        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;
-
-       probe_ent->dev = pci_dev_to_dev(pdev);
-       INIT_LIST_HEAD(&probe_ent->node);
-
-       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;
+       mmio_base = host->iomap[PDC_MMIO_BAR];
 
-               probe_ent->irq = pdev->irq;
-               probe_ent->irq_flags = IRQF_SHARED;
-       probe_ent->iomap = pcim_iomap_table(pdev);
+       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;
 
-       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);
 }
 
 /**
index 0a149339891395d6264f7c3f1b01be7dde85cb84..ee636beb05e132f7da4ca127d569702c5eb4d6fc 100644 (file)
 #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 @@ static void pdc2026x_bmdma_stop(struct ata_queued_cmd *qc)
 
 /**
  *     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 @@ static void pdc2026x_bmdma_stop(struct ata_queued_cmd *qc)
  *     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 @@ static struct ata_port_operations pdc2024x_port_ops = {
 
        .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 @@ static struct ata_port_operations pdc2026x_port_ops = {
 
        .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,
index 4b82a5435a4ebfc1f558afa89bf088bb9d4dc565..a0a650c7f272042ae71458259fc1566aa66e91a9 100644 (file)
@@ -80,13 +80,13 @@ static struct ata_port_operations pata_platform_port_ops = {
        .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 void pata_platform_setup_port(struct ata_ioports *ioaddr,
 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 @@ static int __devinit pata_platform_probe(struct platform_device *pdev)
        /*
         * 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);
 }
 
 /**
index c3810012f3f4af7d873ecd120fda194974528dae..27685ce63ceb27a92f8780c6c1c6aca48aa5745d 100644 (file)
@@ -183,13 +183,13 @@ static struct ata_port_operations qdi6500_port_ops = {
        .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 @@ static struct ata_port_operations qdi6580_port_ops = {
        .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 struct ata_port_operations qdi6580_port_ops = {
 
 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 @@ static __init int qdi_init_one(unsigned long port, int type, unsigned long io, i
        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 @@ static __init int qdi_init_one(unsigned long port, int type, unsigned long io, i
 
        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);
index 9a9132c9e3317a034ab0f402cc45ae1f6289ac39..1c54673e008d0a0d2f5e7c3f821ac260d162522d 100644 (file)
 #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 @@ static const struct ata_port_operations radisys_pata_ops = {
 
        .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,
index f522daa2a6aa3c274befa168bca47fd2cd620643..85c45290eeee9972c1c59b27737d43f03a6b9b5b 100644 (file)
 #define DRV_VERSION    "0.2.3"
 
 
-/**
- *     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
@@ -122,8 +97,9 @@ static struct ata_port_operations rz1000_port_ops = {
 
        .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,
index 93b3ed0f9e8ac9a448102073535e51f04cdceacb..66e8ff467c8d1b7771ce282627070dc0a2e4d05f 100644 (file)
@@ -216,6 +216,7 @@ static struct ata_port_operations sc1200_port_ops = {
        .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,
index f3ed141fdc0ea59554473ea2030bdb5fd9160f1d..5df354d573e8183c5174b8049bbf69b9860b7fb8 100644 (file)
@@ -1016,7 +1016,6 @@ static const struct ata_port_operations scc_pata_ops = {
        .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 const struct ata_port_operations scc_pata_ops = {
 
 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 @@ static struct ata_port_info scc_port_info[] = {
  *     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 @@ static void scc_setup_ports (struct ata_ioports *ioaddr, void __iomem *base)
        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 @@ static int scc_host_init(struct ata_probe_ent *probe_ent)
        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 scc_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;
+       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 @@ static int scc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
                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 = {
index 598eef810a74ab605001bb48e1191d42b7a99e4a..3956ef26936d3005d601f9ad231effb3be89b83a 100644 (file)
@@ -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 @@ static struct sv_cable_table cable_detect[] = {
 };
 
 /**
- *     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 @@ static int serverworks_pre_reset(struct ata_port *ap) {
                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 @@ static int serverworks_pre_reset(struct ata_port *ap) {
        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 @@ static u8 serverworks_is_csb(struct pci_dev *pdev)
 
 /**
  *     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 @@ static unsigned long serverworks_csb_filter(const struct ata_port *ap, struct at
 
        /* 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 @@ static unsigned long serverworks_csb_filter(const struct ata_port *ap, struct at
                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 @@ static struct ata_port_operations serverworks_osb4_port_ops = {
 
        .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 @@ static struct ata_port_operations serverworks_csb_port_ops = {
 
        .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,
index dab2889a556f4ab28305bfaf985f7cf7107c5f6a..6770820cfca9c74c08f8e7e9eb8759429be6b624 100644 (file)
@@ -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 @@ static int sil680_cable_detect(struct ata_port *ap) {
                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 int sil680_bus_reset(struct ata_port *ap,unsigned int *classes)
 
 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 @@ static struct ata_port_operations sil680_port_ops = {
        .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,
index 8dc3bc4f5863dc33ea1832083ef3a6325d896b1f..a3fbcee6fb330185ebf97ba10ecf9cb711862530 100644 (file)
@@ -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,106 +86,55 @@ static int sis_port_base(struct ata_device *adev)
 }
 
 /**
- *     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)
 {
-       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);
        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);
+               return ATA_CBL_PATA40;
+       return ATA_CBL_PATA80;
 }
 
 /**
- *     sis_error_handler - Probe specified port on PATA host controller
- *     @ap: Port to probe
- *
- *     LOCKING:
- *     None (inherited from caller).
- */
-
-static void sis_133_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
+ *     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_pre_reset(struct ata_port *ap)
+static int sis_66_cable_detect(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);
+               return ATA_CBL_PATA40;
+       return ATA_CBL_PATA80;
 }
 
-/**
- *     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
+ *     sis_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 int sis_pre_reset(struct ata_port *ap)
 {
        static const struct pci_bits sis_enable_bits[] = {
                { 0x4aU, 1U, 0x02UL, 0x02UL },  /* port 0 */
@@ -194,27 +143,23 @@ static int sis_old_pre_reset(struct ata_port *ap)
 
        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;
+       if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no]))
+               return -ENOENT;
        return ata_std_prereset(ap);
 }
 
 
 /**
- *     sis_old_error_handler - Probe specified port on PATA host controller
+ *     sis_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)
+static void sis_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 @@ static void sis_133_early_set_dmamode (struct ata_port *ap, struct ata_device *a
        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 @@ static void sis_133_set_dmamode (struct ata_port *ap, struct ata_device *adev)
        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, &reg54);
@@ -595,8 +540,9 @@ static const struct ata_port_operations sis_133_ops = {
 
        .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 @@ static const struct ata_port_operations sis_133_early_ops = {
 
        .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 @@ static const struct ata_port_operations sis_100_ops = {
 
        .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 @@ static const struct ata_port_operations sis_66_ops = {
        .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 @@ static const struct ata_port_operations sis_old_ops = {
 
        .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,
index b681441cfcb96a96a17ed5492d6c985961427d07..da9e22b257532f10fb7e930117b379426b956fea 100644 (file)
@@ -58,7 +58,6 @@ static int sl82c105_pre_reset(struct ata_port *ap)
 
        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 @@ static struct ata_port_operations sl82c105_port_ops = {
        .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,
index 71418f2a0cdb702408fde99a2203ddb1441106c9..e618ffd6e944b392ac35458ee5cd3dc7c73fc264 100644 (file)
@@ -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 @@ static int triflex_prereset(struct ata_port *ap)
 
        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 @@ static struct ata_port_operations triflex_port_ops = {
        .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,
index 946ade0e1f1b3e7a43a7a09ec5a2406afde85834..96b71791d2f48edc8109d606a7a7d76c074df51b 100644 (file)
@@ -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 const struct via_isa_bridge {
  */
 
 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 @@ static int via_pre_reset(struct ata_port *ap)
                        { 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 @@ static struct ata_port_operations via_port_ops = {
        .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 @@ static struct ata_port_operations via_port_ops_noirq = {
        .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,
index 6c111035fc84da35b2ffa7e4bcf42fcb4951a960..cc4ad271afb58be4b21010216efd3146c8ddb9df 100644 (file)
@@ -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 = 1;
 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 @@ static struct ata_port_operations winbond_port_ops = {
        .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 struct ata_port_operations winbond_port_ops = {
 
 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 @@ static __init int winbond_init_one(unsigned long port)
 
        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
-                        */
-
-                       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;
-                       }
-
-                       memset(&ae, 0, sizeof(struct ata_probe_ent));
-                       INIT_LIST_HEAD(&ae.node);
-                       ae.dev = &pdev->dev;
-
-                       ae.port_ops = &winbond_port_ops;
-                       ae.pio_mask = 0x1F;
-
-                       ae.sht = &winbond_sht;
-
-                       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;
-
-                       ret = ata_device_add(&ae);
-                       if (ret == 0) {
-                               platform_device_unregister(pdev);
-                               return -ENODEV;
-                       }
-                       winbond_host[nr_winbond_host++] = dev_get_drvdata(&pdev->dev);
-               }
+               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);
+
+               rc = -ENOMEM;
+               host = ata_host_alloc(&pdev->dev, 1);
+               if (!host)
+                       goto err_unregister;
+
+               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;
+
+               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);
+
+               /* 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;
+
+               /* activate */
+               rc = ata_host_activate(host, 14 + i, ata_interrupt, 0,
+                                      &winbond_sht);
+               if (rc)
+                       goto err_unregister;
+
+               winbond_host[nr_winbond_host++] = dev_get_drvdata(&pdev->dev);
        }
 
        return 0;
+
+ err_unregister:
+       platform_device_unregister(pdev);
+       return rc;
 }
 
 /**
index 5dd3ca8b5f29fb273303810d488c318d80ce7287..52b69530ab29d768b8d71cebc15e5a5fa2b61341 100644 (file)
@@ -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 @@ struct adma_port_priv {
 
 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 @@ static const struct ata_port_operations adma_ata_ops = {
        .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 const struct ata_port_operations adma_ata_ops = {
 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 @@ static void adma_irq_clear(struct ata_port *ap)
        /* 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_reset_engine(void __iomem *chan)
 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 void adma_reinit_engine(struct ata_port *ap)
 
 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 void adma_qc_prep(struct ata_queued_cmd *qc)
 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 @@ static inline unsigned int adma_intr_pkt(struct ata_host *host)
                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 int adma_port_start(struct ata_port *ap)
 
 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 @@ static void adma_host_stop(struct ata_host *host)
        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 @@ static int adma_ata_init_one(struct pci_dev *pdev,
                             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 @@ static int adma_ata_init_one(struct pci_dev *pdev,
        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)
index 1e21688bfcf2f4992fd493de5299e5beb679b7c7..f099a1d83a000b7bb02dacc145259c59334c765e 100644 (file)
@@ -488,11 +488,11 @@ static void inic_error_handler(struct ata_port *ap)
 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 @@ static struct ata_port_operations inic_port_ops = {
        .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_operations inic_port_ops = {
 };
 
 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 @@ static int inic_pci_device_resume(struct pci_dev *pdev)
        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_pci_device_resume(struct pci_dev *pdev)
 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 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        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 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        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 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                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 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
 
        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[] = {
index a65ba636aaa8eeb0ddb66b160f4a5749409c1106..cb9b9ac12b4c1cf3689757bafe9be68f46f59ab5 100644 (file)
@@ -253,10 +253,7 @@ enum {
 #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_port_stop(struct ata_port *ap);
 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 @@ static struct scsi_host_template mv_sht = {
        .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 @@ static const struct ata_port_operations mv5_ops = {
        .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 @@ static const struct ata_port_operations mv5_ops = {
 
        .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 @@ static const struct ata_port_operations mv6_ops = {
        .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 @@ static const struct ata_port_operations mv6_ops = {
 
        .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 @@ static const struct ata_port_operations mv_iie_ops = {
        .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 @@ static const struct ata_port_operations mv_iie_ops = {
 
        .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_operations mv_iie_ops = {
 
 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 @@ static const struct ata_port_info mv_port_info[] = {
                .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 @@ static const struct pci_device_id mv_pci_tbl[] = {
 
        { PCI_VDEVICE(TTI, 0x2310), chip_7042 },
 
+       /* add Marvell 7042 support */
+       { PCI_VDEVICE(MARVELL, 0x7042), chip_7042 },
+
        { }                     /* terminate list */
 };
 
@@ -585,6 +577,39 @@ static const struct mv_hw_ops mv6xxx_ops = {
 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 @@ static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in)
 {
        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 @@ static void mv_port_stop(struct ata_port *ap)
  *      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;
-
-               addr = sg_dma_address(sg);
-               sg_len = sg_dma_len(sg);
-
-               while (sg_len) {
-                       offset = addr & MV_DMA_BOUNDARY;
-                       len = sg_len;
-                       if ((offset + sg_len) > 0x10000)
-                               len = 0x10000 - offset;
+               dma_addr_t addr = sg_dma_address(sg);
+               u32 sg_len = sg_dma_len(sg);
 
-                       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);
+               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);
 
-                       sg_len -= len;
-                       addr += len;
+               if (ata_sg_is_last(sg, qc))
+                       mv_sg->flags_size |= cpu_to_le32(EPRD_FLAG_END_OF_TBL);
 
-                       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 @@ static void mv_host_intr(struct ata_host *host, u32 relevant, unsigned int hc)
        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 @@ static irqreturn_t mv_interrupt(int irq, void *dev_instance)
        /* 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 @@ comreset_retry:
                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 @@ static void mv_port_init(struct ata_ioports *port,  void __iomem *port_mmio)
                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 @@ static int mv_chip_id(struct pci_dev *pdev, struct mv_host_priv *hpriv,
 
 /**
  *      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 @@ static int mv_chip_id(struct pci_dev *pdev, struct mv_host_priv *hpriv,
  *      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 @@ static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent,
        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 @@ static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent,
                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 @@ done:
 
 /**
  *      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 @@ static void mv_print_info(struct ata_probe_ent *probe_ent)
 
        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 void mv_print_info(struct ata_probe_ent *probe_ent)
 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 @@ static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                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)
index 9d9670a9b117e67d3689b8f5b2a1aa4b586f9ac6..02169740ed245dd2bddc51f9297edf790b07768e 100644 (file)
@@ -260,6 +260,7 @@ static int nv_adma_port_resume(struct ata_port *ap);
 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 @@ static const struct ata_port_operations nv_generic_ops = {
        .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 @@ static const struct ata_port_operations nv_nf2_ops = {
        .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 @@ static const struct ata_port_operations nv_ck804_ops = {
        .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_ck804_ops = {
 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 @@ static const struct ata_port_operations nv_adma_ops = {
        .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 @@ static struct ata_port_info nv_port_info[] = {
                .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 @@ static struct ata_port_info nv_port_info[] = {
                .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 @@ static struct ata_port_info nv_port_info[] = {
                .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 @@ static struct ata_port_info nv_port_info[] = {
                .mwdma_mask     = NV_MWDMA_MASK,
                .udma_mask      = NV_UDMA_MASK,
                .port_ops       = &nv_adma_ops,
+               .irq_handler    = nv_adma_interrupt,
        },
 };
 
@@ -667,6 +668,18 @@ static int nv_adma_check_atapi_dma(struct ata_queued_cmd *qc)
        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 @@ static int nv_adma_check_cpb(struct ata_port *ap, int cpb_num, int force_err)
                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 @@ static int nv_adma_port_resume(struct ata_port *ap)
 }
 #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 @@ static void nv_adma_setup_port(struct ata_probe_ent *probe_ent, unsigned int por
        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 @@ static int nv_adma_host_init(struct ata_probe_ent *probe_ent)
 
        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 @@ static int nv_adma_use_reg_mode(struct ata_queued_cmd *qc)
        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 void nv_adma_error_handler(struct ata_port *ap)
 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 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
        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;
+       hpriv->type = type;
+       host->private_data = hpriv;
 
-       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);
+       /* 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);
+       }
 
-       probe_ent->private_data = hpriv;
-       hpriv->type = type;
+       /* request and iomap NV_MMIO_BAR */
+       rc = pcim_iomap_regions(pdev, 1 << NV_MMIO_BAR, DRV_NAME);
+       if (rc)
+               return rc;
 
-       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;
+       /* 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 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
                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)
index 2339813ce9f646caf678172bbb85e045d157f2c7..f56549b90aa63e3d4849dcef9e491dc519ebacfb 100644 (file)
 #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 @@ enum {
        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 @@ enum {
                                  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 @@ struct pdc_port_priv {
        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 @@ static const struct ata_port_operations pdc_sata_ops = {
        .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 @@ static const struct ata_port_operations pdc_old_sata_ops = {
        .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 @@ static const struct ata_port_operations pdc_pata_ops = {
        .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 @@ static const struct ata_port_info pdc_port_info[] = {
 
        /* 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 @@ static const struct ata_port_info pdc_port_info[] = {
 
        /* 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 struct pci_driver pdc_ata_pci_driver = {
 };
 
 
-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 @@ static int pdc_port_start(struct ata_port *ap)
 
        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 @@ static void pdc_reset_port(struct ata_port *ap)
        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 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)
 {
-       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 @@ static void pdc_thaw(struct ata_port *ap)
        readl(mmio + PDC_CTLSTAT); /* flush */
 }
 
-static int pdc_pre_reset(struct ata_port *ap)
-{
-       if (!sata_scr_valid(ap))
-               pdc_pata_cbl_detect(ap);
-       return ata_std_prereset(ap);
-}
-
-static void pdc_error_handler(struct ata_port *ap)
+static void pdc_common_error_handler(struct ata_port *ap, ata_reset_fn_t hardreset)
 {
-       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 @@ static int pdc_check_atapi_dma(struct ata_queued_cmd *qc)
        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 @@ static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
        /* 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 @@ static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
        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 void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
 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,89 +938,49 @@ static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *e
                pcim_pin_device(pdev);
        if (rc)
                return rc;
+       base = pcim_iomap_table(pdev)[PDC_MMIO_BAR];
 
-       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;
-
-       probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
-       if (probe_ent == NULL)
-               return -ENOMEM;
+       /* 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;
 
-       probe_ent->dev = pci_dev_to_dev(pdev);
-       INIT_LIST_HEAD(&probe_ent->node);
+       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");
+               }
+       }
 
-       hp = devm_kzalloc(&pdev->dev, sizeof(*hp), GFP_KERNEL);
-       if (hp == NULL)
+       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;
-
-       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;
        }
+       host->iomap = pcim_iomap_table(pdev);
 
-       pci_set_master(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(board_idx, probe_ent);
+       pdc_host_init(host);
 
-       if (!ata_device_add(probe_ent))
-               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;
 
-       devm_kfree(&pdev->dev, probe_ent);
-       return 0;
+       /* start host, request IRQ and attach */
+       pci_set_master(pdev);
+       return ata_host_activate(host, pdev->irq, pdc_interrupt, IRQF_SHARED,
+                                &pdc_ata_sht);
 }
 
 
index 8786b45f291b8d62af9ddb6fd3bc585d7c659201..f5a05de0093de45156a02c01542fa97b592f6f4c 100644 (file)
@@ -114,7 +114,6 @@ struct qs_port_priv {
 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 @@ static const struct ata_port_operations qs_ata_ops = {
        .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_operations qs_ata_ops = {
 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 @@ static void qs_host_stop(struct ata_host *host)
        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 @@ static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
        }
        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 @@ static int qs_ata_init_one(struct pci_dev *pdev,
                                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 @@ static int qs_ata_init_one(struct pci_dev *pdev,
        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)
index 917b7ea4ef7c203b0bd4ebc5abd83aad5ceb4420..0a1e417f309c5342aaaf763fa2ddc595e414fed7 100644 (file)
@@ -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 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
 #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 @@ static const struct ata_port_operations sil_ops = {
        .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 @@ static const struct ata_port_operations sil_ops = {
        .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_operations sil_ops = {
 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 @@ static const struct ata_port_info sil_port_info[] = {
        },
        /* 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 @@ static const struct ata_port_info sil_port_info[] = {
        },
        /* 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 @@ static const struct ata_port_info sil_port_info[] = {
        },
        /* 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 @@ static unsigned char sil_get_device_cache_line(struct pci_dev *pdev)
        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 @@ static void sil_post_set_mode (struct ata_port *ap)
        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 @@ static void sil_post_set_mode (struct ata_port *ap)
        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 @@ static void sil_thaw(struct ata_port *ap)
 
 /**
  *     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 @@ static void sil_thaw(struct ata_port *ap)
  *     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_dev_config(struct ata_port *ap, struct ata_device *dev)
        }
 }
 
-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 @@ static void sil_init_controller(struct pci_dev *pdev,
        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 @@ static void sil_init_controller(struct pci_dev *pdev,
                           "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 @@ static void sil_init_controller(struct pci_dev *pdev,
                }
        }
 
-       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 void sil_init_controller(struct pci_dev *pdev,
 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 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
                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 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
        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;
-
-       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]);
+       for (i = 0; i < host->n_ports; i++) {
+               struct ata_ioports *ioaddr = &host->ports[i]->ioaddr;
+
+               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 @@ static int sil_pci_device_resume(struct pci_dev *pdev)
        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;
index 5614df8c1ce2809bed166aa3b0f70d63e112819f..e6223ba667daca271a19e1c541470d80463c2a9a 100644 (file)
@@ -323,7 +323,7 @@ struct sil24_port_priv {
        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_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
 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 @@ static const struct ata_port_operations sil24_ops = {
        .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 @@ static const struct ata_port_operations sil24_ops = {
 #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 @@ static struct ata_port_info sil24_port_info[] = {
        },
        /* 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 @@ static struct ata_port_info sil24_port_info[] = {
        },
        /* 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 @@ static int sil24_tag(int tag)
        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 @@ static void sil24_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)
                sil24_init_port(ap);
 }
 
@@ -964,11 +956,10 @@ static int sil24_port_start(struct ata_port *ap)
        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 @@ static void sil24_init_controller(struct pci_dev *pdev, int n_ports,
        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 @@ static void sil24_init_controller(struct pci_dev *pdev, int n_ports,
                                                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 void sil24_init_controller(struct pci_dev *pdev, int n_ports,
 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 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                                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)
-               return -ENOMEM;
+       /* 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;
+       }
 
-       probe_ent->dev = pci_dev_to_dev(pdev);
-       INIT_LIST_HEAD(&probe_ent->node);
+       /* 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->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);
+       for (i = 0; i < host->n_ports; i++) {
+               void __iomem *port = iomap[SIL24_PORT_BAR] + i * PORT_REGS_SIZE;
 
-       probe_ent->irq = pdev->irq;
-       probe_ent->irq_flags = IRQF_SHARED;
-       probe_ent->iomap = pcim_iomap_table(pdev);
+               host->ports[i]->ioaddr.cmd_addr = port;
+               host->ports[i]->ioaddr.scr_addr = port + PORT_SCONTROL;
 
-       host_base = probe_ent->iomap[SIL24_HOST_BAR];
-       port_base = probe_ent->iomap[SIL24_PORT_BAR];
+               ata_std_ports(&host->ports[i]->ioaddr);
+       }
 
-       /*
-        * 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 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                }
        }
 
-       /* 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 @@ static int sil24_pci_device_resume(struct pci_dev *pdev)
 {
        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 @@ static int sil24_pci_device_resume(struct pci_dev *pdev)
        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);
 
index a787f0d4a5baeea3d9da9f2c63dd025e9f078d92..d8ee062e82fce91fafc91348296bc3ec75e930b9 100644 (file)
@@ -121,7 +121,6 @@ static const struct ata_port_operations sis_ops = {
        .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 const struct ata_port_operations sis_ops = {
 };
 
 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 void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
 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 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
        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 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
                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)
index b121195cc598c6a9f4dccd6bbe21580e187ad7c9..cc07aac10e8ce37a0ee818f5ec982b3fc602c509 100644 (file)
@@ -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 @@ enum {
        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 @@ static const struct ata_port_operations k2_sata_ops = {
        .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 @@ static const struct ata_port_operations k2_sata_ops = {
        .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 void k2_sata_setup_port(struct ata_ioports *port, void __iomem *base)
 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 @@ static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *e
        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 @@ static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *e
                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 @@ static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *e
        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 @@ static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *e
        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
index 1a081c3a8c06a24b3cd4ba51e6d643435ed2ca21..3a4f44559d0ae556176ac908c89989487ba75654 100644 (file)
@@ -151,24 +151,23 @@ struct pdc_host_priv {
 
 
 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 @@ static const struct ata_port_operations pdc_20621_ops = {
        .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_operations pdc_20621_ops = {
 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 @@ static void pdc_sata_setup_port(struct ata_ioports *port, void __iomem *base)
 
 
 #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 @@ static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource,
 #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 void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource,
 }
 
 
-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 unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device,
 }
 
 
-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_detect_dimm(struct ata_probe_ent *pe)
 }
 
 
-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 @@ static int pdc20621_prog_dimm0(struct ata_probe_ent *pe)
        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 int pdc20621_prog_dimm0(struct ata_probe_ent *pe)
 }
 
 
-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 @@ static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe)
        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_prog_dimm_global(struct ata_probe_ent *pe)
 }
 
 
-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 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe)
        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 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe)
           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 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe)
                                '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 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe)
 
        /* 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 unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe)
 }
 
 
-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 void pdc_20621_init(struct ata_probe_ent *pe)
 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 @@ static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *
                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 @@ static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *
        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);
 }
 
 
index d659ace80f4f6c985f6da071a61b9d42ff93b8a5..f74e383de08303546259f0d1f817c77043788000 100644 (file)
@@ -115,7 +115,6 @@ static const struct ata_port_operations uli_ops = {
        .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 const struct ata_port_operations uli_ops = {
 };
 
 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 void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
 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 @@ static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
        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);
+       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;
 
-       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 @@ static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 
        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)
index 598e6a26a481a7934d8ae14121e395844c1f22dd..1d855f55f5f710edd378110740b76042afe37005 100644 (file)
@@ -64,8 +64,6 @@ enum {
        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 u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg);
 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 @@ static const struct ata_port_operations vt6420_sata_ops = {
        .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 @@ static const struct ata_port_operations vt6421_pata_ops = {
 
        .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 @@ static const struct ata_port_operations vt6421_sata_ops = {
 
        .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 @@ static const struct ata_port_operations vt6421_sata_ops = {
        .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 @@ static struct ata_port_info vt6420_port_info = {
        .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 @@ static void vt6420_error_handler(struct ata_port *ap)
                                  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 @@ static void vt6421_set_dma_mode(struct ata_port *ap, struct ata_device *adev)
        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 @@ static void __iomem * vt6421_scr_addr(void __iomem *addr, unsigned int port)
        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);
-
-       probe_ent->port[port].cmd_addr = reg_addr;
-       probe_ent->port[port].altstatus_addr =
-       probe_ent->port[port].ctl_addr = (void __iomem *)
+       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;
+
+       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;
+
+       *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;
+       }
 
-       probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL);
-       if (!probe_ent)
-               return NULL;
-
-       memset(probe_ent, 0, sizeof(*probe_ent));
-       probe_ent->dev = pci_dev_to_dev(pdev);
-       INIT_LIST_HEAD(&probe_ent->node);
-
-       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 < 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 = 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);
 
-       for (i = 0; i < N_PORTS; i++)
-               vt6421_init_addrs(probe_ent, pcim_iomap_table(pdev), i);
+       for (i = 0; i < host->n_ports; i++)
+               vt6421_init_addrs(host->ports[i]);
 
-       return probe_ent;
+       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;
+
+       return 0;
 }
 
 static void svia_configure(struct pci_dev *pdev)
@@ -522,7 +501,7 @@ static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
        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 @@ static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
        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 @@ static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
                        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)
index 170bad1b415b01765276b7578f6675804e7afb6d..80126f835d323897ec8ace75110cc3dbbc78d133 100644 (file)
@@ -333,7 +333,6 @@ static const struct ata_port_operations vsc_sata_ops = {
        .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 void __devinit vsc_sata_setup_port(struct ata_ioports *port,
 
 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 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d
        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 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d
 
        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 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d
         */
        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[] = {
index 78237577b05ac57790ac34aee0f601b8aae23aa7..3ef593a9015f1d298348cc44040000182ef7b63e 100644 (file)
@@ -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_param(useinput, int, 0444);
 MODULE_PARM_DESC(useinput,
                 "set this if you would like sonypi to feed events to the input subsystem");
 
+static int check_ioport = 1;
+module_param(check_ioport, int, 0444);
+MODULE_PARM_DESC(check_ioport,
+                "set this to 0 if you think the automatic ioport check for sony-laptop is wrong");
+
 #define SONYPI_DEVICE_MODEL_TYPE1      1
 #define SONYPI_DEVICE_MODEL_TYPE2      2
 #define SONYPI_DEVICE_MODEL_TYPE3      3
@@ -477,7 +484,7 @@ static struct sonypi_device {
        u16 evtype_offset;
        int camera_power;
        int bluetooth_power;
-       struct semaphore lock;
+       struct mutex lock;
        struct kfifo *fifo;
        spinlock_t fifo_lock;
        wait_queue_head_t fifo_proc_list;
@@ -884,7 +891,7 @@ int sonypi_camera_command(int command, u8 value)
        if (!camera)
                return -EIO;
 
-       down(&sonypi_device.lock);
+       mutex_lock(&sonypi_device.lock);
 
        switch (command) {
        case SONYPI_COMMAND_SETCAMERA:
@@ -919,7 +926,7 @@ int sonypi_camera_command(int command, u8 value)
                       command);
                break;
        }
-       up(&sonypi_device.lock);
+       mutex_unlock(&sonypi_device.lock);
        return 0;
 }
 
@@ -938,20 +945,20 @@ static int sonypi_misc_fasync(int fd, struct file *filp, int on)
 static int sonypi_misc_release(struct inode *inode, struct file *file)
 {
        sonypi_misc_fasync(-1, file, 0);
-       down(&sonypi_device.lock);
+       mutex_lock(&sonypi_device.lock);
        sonypi_device.open_count--;
-       up(&sonypi_device.lock);
+       mutex_unlock(&sonypi_device.lock);
        return 0;
 }
 
 static int sonypi_misc_open(struct inode *inode, struct file *file)
 {
-       down(&sonypi_device.lock);
+       mutex_lock(&sonypi_device.lock);
        /* Flush input queue on first open */
        if (!sonypi_device.open_count)
                kfifo_reset(sonypi_device.fifo);
        sonypi_device.open_count++;
-       up(&sonypi_device.lock);
+       mutex_unlock(&sonypi_device.lock);
        return 0;
 }
 
@@ -1001,7 +1008,7 @@ static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
        u8 val8;
        u16 val16;
 
-       down(&sonypi_device.lock);
+       mutex_lock(&sonypi_device.lock);
        switch (cmd) {
        case SONYPI_IOCGBRT:
                if (sonypi_ec_read(SONYPI_LCD_LIGHT, &val8)) {
@@ -1101,7 +1108,7 @@ static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
        default:
                ret = -EINVAL;
        }
-       up(&sonypi_device.lock);
+       mutex_unlock(&sonypi_device.lock);
        return ret;
 }
 
@@ -1260,6 +1267,28 @@ static int __devinit sonypi_create_input_devices(void)
 static int __devinit sonypi_setup_ioports(struct sonypi_device *dev,
                                const struct sonypi_ioport_list *ioport_list)
 {
+       /* try to detect if sony-laptop is being used and thus
+        * has already requested one of the known ioports.
+        * As in the deprecated check_region this is racy has we have
+        * multiple ioports available and one of them can be requested
+        * between this check and the subsequent request. Anyway, as an
+        * attempt to be some more user-friendly as we currently are,
+        * this is enough.
+        */
+       const struct sonypi_ioport_list *check = ioport_list;
+       while (check_ioport && check->port1) {
+               if (!request_region(check->port1,
+                                  sonypi_device.region_size,
+                                  "Sony Programable I/O Device Check")) {
+                       printk(KERN_ERR "sonypi: ioport 0x%.4x busy, using sony-laptop? "
+                                       "if not use check_ioport=0\n",
+                                       check->port1);
+                       return -EBUSY;
+               }
+               release_region(check->port1, sonypi_device.region_size);
+               check++;
+       }
+
        while (ioport_list->port1) {
 
                if (request_region(ioport_list->port1,
@@ -1321,6 +1350,10 @@ static int __devinit sonypi_probe(struct platform_device *dev)
        struct pci_dev *pcidev;
        int error;
 
+       printk(KERN_WARNING "sonypi: please try the sony-laptop module instead "
+                       "and report failures, see also "
+                       "http://www.linux.it/~malattia/wiki/index.php/Sony_drivers\n");
+
        spin_lock_init(&sonypi_device.fifo_lock);
        sonypi_device.fifo = kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL,
                                         &sonypi_device.fifo_lock);
@@ -1330,7 +1363,7 @@ static int __devinit sonypi_probe(struct platform_device *dev)
        }
 
        init_waitqueue_head(&sonypi_device.fifo_proc_list);
-       init_MUTEX(&sonypi_device.lock);
+       mutex_init(&sonypi_device.lock);
        sonypi_device.bluetooth_power = -1;
 
        if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
index 5ac309ee7f05c39d97f22c562bc3318aee85215c..5cfcff532545b019e3f984dbf3de5273b91d464b 100644 (file)
@@ -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;
 
index 639e8b6c35b11dd03b72bef6a26be7b0521553b6..bc773781993a5b75113388a623f12f64fa697556 100644 (file)
@@ -577,14 +577,14 @@ config VIDEO_ZORAN_AVS6EYES
 
 config VIDEO_MEYE
        tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
-       depends on PCI && SONYPI && VIDEO_V4L1
+       depends on PCI && SONY_LAPTOP && VIDEO_V4L1
        ---help---
          This is the video4linux driver for the Motion Eye camera found
          in the Vaio Picturebook laptops. Please read the material in
          <file:Documentation/video4linux/meye.txt> for more information.
 
-         If you say Y or M here, you need to say Y or M to "Sony Programmable
-         I/O Control Device" in the character device section.
+         If you say Y or M here, you need to say Y or M to "Sony Laptop
+         Extras" in the misc device section.
 
          To compile this driver as a module, choose M here: the
          module will be called meye.
index 98681da5e3b91fbbd72d9c665c277eaf22e48cea..664aba8b4d85231895954ac48df4447212796d48 100644 (file)
@@ -925,13 +925,13 @@ static int meye_do_ioctl(struct inode *inode, struct file *file,
                if (p->palette != VIDEO_PALETTE_YUV422 && p->palette != VIDEO_PALETTE_YUYV)
                        return -EINVAL;
                mutex_lock(&meye.lock);
-               sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS,
+               sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS,
                                      p->brightness >> 10);
-               sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE,
+               sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE,
                                      p->hue >> 10);
-               sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR,
+               sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR,
                                      p->colour >> 10);
-               sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST,
+               sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST,
                                      p->contrast >> 10);
                meye.picture = *p;
                mutex_unlock(&meye.lock);
@@ -1043,11 +1043,11 @@ static int meye_do_ioctl(struct inode *inode, struct file *file,
                    meye.params.quality != jp->quality)
                        mchip_hic_stop();       /* need restart */
                meye.params = *jp;
-               sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS,
+               sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS,
                                      meye.params.sharpness);
-               sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC,
+               sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC,
                                      meye.params.agc);
-               sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE,
+               sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE,
                                      meye.params.picture);
                mutex_unlock(&meye.lock);
                break;
@@ -1287,38 +1287,38 @@ static int meye_do_ioctl(struct inode *inode, struct file *file,
                mutex_lock(&meye.lock);
                switch (c->id) {
                case V4L2_CID_BRIGHTNESS:
-                       sonypi_camera_command(
-                               SONYPI_COMMAND_SETCAMERABRIGHTNESS, c->value);
+                       sony_pic_camera_command(
+                               SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value);
                        meye.picture.brightness = c->value << 10;
                        break;
                case V4L2_CID_HUE:
-                       sonypi_camera_command(
-                               SONYPI_COMMAND_SETCAMERAHUE, c->value);
+                       sony_pic_camera_command(
+                               SONY_PIC_COMMAND_SETCAMERAHUE, c->value);
                        meye.picture.hue = c->value << 10;
                        break;
                case V4L2_CID_CONTRAST:
-                       sonypi_camera_command(
-                               SONYPI_COMMAND_SETCAMERACONTRAST, c->value);
+                       sony_pic_camera_command(
+                               SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value);
                        meye.picture.contrast = c->value << 10;
                        break;
                case V4L2_CID_SATURATION:
-                       sonypi_camera_command(
-                               SONYPI_COMMAND_SETCAMERACOLOR, c->value);
+                       sony_pic_camera_command(
+                               SONY_PIC_COMMAND_SETCAMERACOLOR, c->value);
                        meye.picture.colour = c->value << 10;
                        break;
                case V4L2_CID_AGC:
-                       sonypi_camera_command(
-                               SONYPI_COMMAND_SETCAMERAAGC, c->value);
+                       sony_pic_camera_command(
+                               SONY_PIC_COMMAND_SETCAMERAAGC, c->value);
                        meye.params.agc = c->value;
                        break;
                case V4L2_CID_SHARPNESS:
-                       sonypi_camera_command(
-                               SONYPI_COMMAND_SETCAMERASHARPNESS, c->value);
+                       sony_pic_camera_command(
+                               SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value);
                        meye.params.sharpness = c->value;
                        break;
                case V4L2_CID_PICTURE:
-                       sonypi_camera_command(
-                               SONYPI_COMMAND_SETCAMERAPICTURE, c->value);
+                       sony_pic_camera_command(
+                               SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value);
                        meye.params.picture = c->value;
                        break;
                case V4L2_CID_JPEGQUAL:
@@ -1848,7 +1848,7 @@ static int __devinit meye_probe(struct pci_dev *pcidev,
        memcpy(meye.video_dev, &meye_template, sizeof(meye_template));
        meye.video_dev->dev = &meye.mchip_dev->dev;
 
-       if ((ret = sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 1))) {
+       if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) {
                printk(KERN_ERR "meye: unable to power on the camera\n");
                printk(KERN_ERR "meye: did you enable the camera in "
                                "sonypi using the module options ?\n");
@@ -1928,13 +1928,13 @@ static int __devinit meye_probe(struct pci_dev *pcidev,
        meye.params.picture = 0;
        meye.params.framerate = 0;
 
-       sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, 32);
-       sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, 32);
-       sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, 32);
-       sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, 32);
-       sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, 32);
-       sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, 0);
-       sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, 48);
+       sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, 32);
+       sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, 32);
+       sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, 32);
+       sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, 32);
+       sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, 32);
+       sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0);
+       sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48);
 
        printk(KERN_INFO "meye: Motion Eye Camera Driver v%s.\n",
               MEYE_DRIVER_VERSION);
@@ -1953,7 +1953,7 @@ outremap:
 outregions:
        pci_disable_device(meye.mchip_dev);
 outenabledev:
-       sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0);
+       sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0);
 outsonypienable:
        kfifo_free(meye.doneq);
 outkfifoalloc2:
@@ -1986,7 +1986,7 @@ static void __devexit meye_remove(struct pci_dev *pcidev)
 
        pci_disable_device(meye.mchip_dev);
 
-       sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0);
+       sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0);
 
        kfifo_free(meye.doneq);
        kfifo_free(meye.grabq);
index ea107cb5c845baf46b928bf880e1b54944257077..323d0074120da4aa5ad7f0c87413abd72212e219 100644 (file)
 /****************************************************************************/
 
 /* 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>
index 80b199fa0aa9b7173fc684b1866de7c1c2e46b2f..a3c525b2616aafeaa855b03377e0f4ee4937cdc5 100644 (file)
@@ -112,14 +112,70 @@ config SONY_LAPTOP
        depends on X86 && ACPI
        select BACKLIGHT_CLASS_DEVICE
          ---help---
-         This mini-driver drives the SNC device present in the ACPI BIOS of
-         the Sony Vaio laptops.
+         This mini-driver drives the SNC and SPIC devices present in the ACPI
+         BIOS of the Sony Vaio laptops.
 
-         It gives access to some extra laptop functionalities. In its current
-         form, this driver let the user set or query the screen brightness
-         through the backlight subsystem and remove/apply power to some
+         It gives access to some extra laptop functionalities like Bluetooth,
+         screen brightness control, Fn keys and allows powering on/off some
          devices.
 
          Read <file:Documentation/sony-laptop.txt> for more information.
 
+config SONY_LAPTOP_OLD
+       bool "Sonypi compatibility"
+       depends on SONY_LAPTOP
+         ---help---
+         Build the sonypi driver compatibility code into the sony-laptop driver.
+
+config THINKPAD_ACPI
+       tristate "ThinkPad ACPI Laptop Extras"
+       depends on X86 && ACPI
+       select BACKLIGHT_CLASS_DEVICE
+       select HWMON
+       ---help---
+         This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
+         support for Fn-Fx key combinations, Bluetooth control, video
+         output switching, ThinkLight control, UltraBay eject and more.
+         For more information about this driver see 
+         <file:Documentation/thinkpad-acpi.txt> and <http://ibm-acpi.sf.net/> .
+
+         This driver was formely known as ibm-acpi.
+
+         If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
+
+config THINKPAD_ACPI_DEBUG
+       bool "Verbose debug mode"
+       depends on THINKPAD_ACPI
+       default n
+       ---help---
+         Enables extra debugging information, at the expense of a slightly
+         increase in driver size.
+
+         If you are not sure, say N here.
+
+config THINKPAD_ACPI_DOCK
+       bool "Legacy Docking Station Support"
+       depends on THINKPAD_ACPI
+       depends on ACPI_DOCK=n
+       default n
+       ---help---
+         Allows the thinkpad_acpi driver to handle docking station events.
+         This support was made obsolete by the generic ACPI docking station
+         support (CONFIG_ACPI_DOCK).  It will allow locking and removing the
+         laptop from the docking station, but will not properly connect PCI
+         devices.
+
+         If you are not sure, say N here.
+
+config THINKPAD_ACPI_BAY
+       bool "Legacy Removable Bay Support"
+       depends on THINKPAD_ACPI
+       default y
+       ---help---
+         Allows the thinkpad_acpi driver to handle removable bays.  It will
+         eletrically disable the device in the bay, and also generate
+         notifications when the bay lever is ejected or inserted.
+
+         If you are not sure, say Y here.
+
 endmenu
index 7793ccd7904962c699340c53e8ef1cfa6389e9b1..e325164591380bd93011840757808b3927e26eb1 100644 (file)
@@ -12,3 +12,4 @@ obj-$(CONFIG_TIFM_CORE)               += tifm_core.o
 obj-$(CONFIG_TIFM_7XX1)        += tifm_7xx1.o
 obj-$(CONFIG_SGI_IOC4)         += ioc4.o
 obj-$(CONFIG_SONY_LAPTOP)      += sony-laptop.o
+obj-$(CONFIG_THINKPAD_ACPI)    += thinkpad_acpi.o
index 4b232124a1abb0dea3b9e737062250e3988b968c..65c32a95e121360d3288e95fc5b96792538084ad 100644 (file)
@@ -3,7 +3,7 @@
  *
  *
  *  Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
- *  Copyright (C) 2006 Corentin Chary
+ *  Copyright (C) 2006-2007 Corentin Chary
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -48,7 +48,7 @@
 #include <acpi/acpi_bus.h>
 #include <asm/uaccess.h>
 
-#define ASUS_LAPTOP_VERSION "0.40"
+#define ASUS_LAPTOP_VERSION "0.41"
 
 #define ASUS_HOTK_NAME          "Asus Laptop Support"
 #define ASUS_HOTK_CLASS         "hotkey"
@@ -81,7 +81,8 @@
 #define TLED_ON     0x08       //touchpad LED
 #define RLED_ON     0x10       //Record LED
 #define PLED_ON     0x20       //Phone LED
-#define LCD_ON      0x40       //LCD backlight
+#define GLED_ON     0x40       //Gaming LED
+#define LCD_ON      0x80       //LCD backlight
 
 #define ASUS_LOG    ASUS_HOTK_FILE ": "
 #define ASUS_ERR    KERN_ERR    ASUS_LOG
@@ -94,6 +95,19 @@ MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
 MODULE_DESCRIPTION(ASUS_HOTK_NAME);
 MODULE_LICENSE("GPL");
 
+/* WAPF defines the behavior of the Fn+Fx wlan key
+ * The significance of values is yet to be found, but
+ * most of the time:
+ * 0x0 will do nothing
+ * 0x1 will allow to control the device with Fn+Fx key.
+ * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
+ * 0x5 like 0x1 or 0x4
+ * So, if something doesn't work as you want, just try other values =)
+ */
+static uint wapf = 1;
+module_param(wapf, uint, 0644);
+MODULE_PARM_DESC(wapf, "WAPF value");
+
 #define ASUS_HANDLE(object, paths...)                                  \
        static acpi_handle  object##_handle = NULL;                     \
        static char *object##_paths[] = { paths }
@@ -103,6 +117,7 @@ ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED");
 ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED");
 ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED");        /* W1JC */
 ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED");        /* A7J */
+ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED");        /* G1, G2 (probably) */
 
 /* LEDD */
 ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM");
@@ -221,6 +236,7 @@ ASUS_LED(mled, "mail");
 ASUS_LED(tled, "touchpad");
 ASUS_LED(rled, "record");
 ASUS_LED(pled, "phone");
+ASUS_LED(gled, "gaming");
 
 /*
  * This function evaluates an ACPI method, given an int as parameter, the
@@ -245,32 +261,19 @@ static int write_acpi_int(acpi_handle handle, const char *method, int val,
        return (status == AE_OK);
 }
 
-static int read_acpi_int(acpi_handle handle, const char *method, int *val,
-                        struct acpi_object_list *params)
-{
-       struct acpi_buffer output;
-       union acpi_object out_obj;
-       acpi_status status;
-
-       output.length = sizeof(out_obj);
-       output.pointer = &out_obj;
-
-       status = acpi_evaluate_object(handle, (char *)method, params, &output);
-       *val = out_obj.integer.value;
-       return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
-}
-
 static int read_wireless_status(int mask)
 {
-       int status;
+       ulong status;
+       acpi_status rv = AE_OK;
 
        if (!wireless_status_handle)
                return (hotk->status & mask) ? 1 : 0;
 
-       if (read_acpi_int(wireless_status_handle, NULL, &status, NULL)) {
-               return (status & mask) ? 1 : 0;
-       } else
+       rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
+       if (ACPI_FAILURE(rv))
                printk(ASUS_WARNING "Error reading Wireless status\n");
+       else
+               return (status & mask) ? 1 : 0;
 
        return (hotk->status & mask) ? 1 : 0;
 }
@@ -285,19 +288,28 @@ static int read_status(int mask)
        return (hotk->status & mask) ? 1 : 0;
 }
 
-static void write_status(acpi_handle handle, int out, int mask, int invert)
+static void write_status(acpi_handle handle, int out, int mask)
 {
        hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask);
 
-       if (invert)             /* invert target value */
+       switch (mask) {
+       case MLED_ON:
                out = !out & 0x1;
+               break;
+       case GLED_ON:
+               out = (out & 0x1) + 1;
+               break;
+       default:
+               out &= 0x1;
+               break;
+       }
 
        if (handle && !write_acpi_int(handle, NULL, out, NULL))
-               printk(ASUS_WARNING " write failed\n");
+               printk(ASUS_WARNING " write failed %x\n", mask);
 }
 
 /* /sys/class/led handlers */
-#define ASUS_LED_HANDLER(object, mask, invert)                         \
+#define ASUS_LED_HANDLER(object, mask)                                 \
        static void object##_led_set(struct led_classdev *led_cdev,     \
                                     enum led_brightness value)         \
        {                                                               \
@@ -307,13 +319,14 @@ static void write_status(acpi_handle handle, int out, int mask, int invert)
        static void object##_led_update(struct work_struct *ignored)    \
        {                                                               \
                int value = object##_led_wk;                            \
-               write_status(object##_set_handle, value, (mask), (invert)); \
+               write_status(object##_set_handle, value, (mask));       \
        }
 
-ASUS_LED_HANDLER(mled, MLED_ON, 1);
-ASUS_LED_HANDLER(pled, PLED_ON, 0);
-ASUS_LED_HANDLER(rled, RLED_ON, 0);
-ASUS_LED_HANDLER(tled, TLED_ON, 0);
+ASUS_LED_HANDLER(mled, MLED_ON);
+ASUS_LED_HANDLER(pled, PLED_ON);
+ASUS_LED_HANDLER(rled, RLED_ON);
+ASUS_LED_HANDLER(tled, TLED_ON);
+ASUS_LED_HANDLER(gled, GLED_ON);
 
 static int get_lcd_state(void)
 {
@@ -338,7 +351,7 @@ static int set_lcd_state(int value)
                        printk(ASUS_WARNING "Error switching LCD\n");
        }
 
-       write_status(NULL, lcd, LCD_ON, 0);
+       write_status(NULL, lcd, LCD_ON);
        return 0;
 }
 
@@ -354,9 +367,11 @@ static void lcd_blank(int blank)
 
 static int read_brightness(struct backlight_device *bd)
 {
-       int value;
+       ulong value;
+       acpi_status rv = AE_OK;
 
-       if (!read_acpi_int(brightness_get_handle, NULL, &value, NULL))
+       rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
+       if (ACPI_FAILURE(rv))
                printk(ASUS_WARNING "Error reading brightness\n");
 
        return value;
@@ -403,8 +418,10 @@ static ssize_t show_infos(struct device *dev,
                          struct device_attribute *attr, char *page)
 {
        int len = 0;
-       int temp;
+       ulong temp;
        char buf[16];           //enough for all info
+       acpi_status rv = AE_OK;
+
        /*
         * We use the easy way, we don't care of off and count, so we don't set eof
         * to 1
@@ -418,9 +435,10 @@ static ssize_t show_infos(struct device *dev,
         * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
         * The significance of others is yet to be found.
         */
-       if (read_acpi_int(hotk->handle, "SFUN", &temp, NULL))
-               len +=
-                   sprintf(page + len, "SFUN value         : 0x%04x\n", temp);
+       rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
+       if (!ACPI_FAILURE(rv))
+               len += sprintf(page + len, "SFUN value         : 0x%04x\n",
+                              (uint) temp);
        /*
         * Another value for userspace: the ASYM method returns 0x02 for
         * battery low and 0x04 for battery critical, its readings tend to be
@@ -428,9 +446,10 @@ static ssize_t show_infos(struct device *dev,
         * Note: since not all the laptops provide this method, errors are
         * silently ignored.
         */
-       if (read_acpi_int(hotk->handle, "ASYM", &temp, NULL))
-               len +=
-                   sprintf(page + len, "ASYM value         : 0x%04x\n", temp);
+       rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
+       if (!ACPI_FAILURE(rv))
+               len += sprintf(page + len, "ASYM value         : 0x%04x\n",
+                              (uint) temp);
        if (asus_info) {
                snprintf(buf, 16, "%d", asus_info->length);
                len += sprintf(page + len, "DSDT length        : %s\n", buf);
@@ -465,7 +484,7 @@ static int parse_arg(const char *buf, unsigned long count, int *val)
 }
 
 static ssize_t store_status(const char *buf, size_t count,
-                           acpi_handle handle, int mask, int invert)
+                           acpi_handle handle, int mask)
 {
        int rv, value;
        int out = 0;
@@ -474,7 +493,7 @@ static ssize_t store_status(const char *buf, size_t count,
        if (rv > 0)
                out = value ? 1 : 0;
 
-       write_status(handle, out, mask, invert);
+       write_status(handle, out, mask);
 
        return rv;
 }
@@ -515,7 +534,7 @@ static ssize_t show_wlan(struct device *dev,
 static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
                          const char *buf, size_t count)
 {
-       return store_status(buf, count, wl_switch_handle, WL_ON, 0);
+       return store_status(buf, count, wl_switch_handle, WL_ON);
 }
 
 /*
@@ -531,7 +550,7 @@ static ssize_t store_bluetooth(struct device *dev,
                               struct device_attribute *attr, const char *buf,
                               size_t count)
 {
-       return store_status(buf, count, bt_switch_handle, BT_ON, 0);
+       return store_status(buf, count, bt_switch_handle, BT_ON);
 }
 
 /*
@@ -547,12 +566,15 @@ static void set_display(int value)
 
 static int read_display(void)
 {
-       int value = 0;
+       ulong value = 0;
+       acpi_status rv = AE_OK;
 
        /* In most of the case, we know how to set the display, but sometime
           we can't read it */
        if (display_get_handle) {
-               if (!read_acpi_int(display_get_handle, NULL, &value, NULL))
+               rv = acpi_evaluate_integer(display_get_handle, NULL,
+                                          NULL, &value);
+               if (ACPI_FAILURE(rv))
                        printk(ASUS_WARNING "Error reading display status\n");
        }
 
@@ -656,10 +678,10 @@ static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
         * switched
         */
        if (event == ATKD_LCD_ON) {
-               write_status(NULL, 1, LCD_ON, 0);
+               write_status(NULL, 1, LCD_ON);
                lcd_blank(FB_BLANK_UNBLANK);
        } else if (event == ATKD_LCD_OFF) {
-               write_status(NULL, 0, LCD_ON, 0);
+               write_status(NULL, 0, LCD_ON);
                lcd_blank(FB_BLANK_POWERDOWN);
        }
 
@@ -771,7 +793,7 @@ static int asus_hotk_get_info(void)
 {
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
        union acpi_object *model = NULL;
-       int bsts_result, hwrs_result;
+       ulong bsts_result, hwrs_result;
        char *string = NULL;
        acpi_status status;
 
@@ -794,11 +816,16 @@ static int asus_hotk_get_info(void)
        }
 
        /* This needs to be called for some laptops to init properly */
-       if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result, NULL))
+       status =
+           acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
+       if (ACPI_FAILURE(status))
                printk(ASUS_WARNING "Error calling BSTS\n");
        else if (bsts_result)
                printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n",
-                      bsts_result);
+                      (uint) bsts_result);
+
+       /* This too ... */
+       write_acpi_int(hotk->handle, "CWAP", wapf, NULL);
 
        /*
         * Try to match the object returned by INIT to the specific model.
@@ -831,6 +858,7 @@ static int asus_hotk_get_info(void)
        ASUS_HANDLE_INIT(tled_set);
        ASUS_HANDLE_INIT(rled_set);
        ASUS_HANDLE_INIT(pled_set);
+       ASUS_HANDLE_INIT(gled_set);
 
        ASUS_HANDLE_INIT(ledd_set);
 
@@ -840,7 +868,9 @@ static int asus_hotk_get_info(void)
         * The significance of others is yet to be found.
         * If we don't find the method, we assume the device are present.
         */
-       if (!read_acpi_int(hotk->handle, "HRWS", &hwrs_result, NULL))
+       status =
+           acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result);
+       if (ACPI_FAILURE(status))
                hwrs_result = WL_HWRS | BT_HWRS;
 
        if (hwrs_result & WL_HWRS)
@@ -928,11 +958,15 @@ static int asus_hotk_add(struct acpi_device *device)
        asus_hotk_found = 1;
 
        /* WLED and BLED are on by default */
-       write_status(bt_switch_handle, 1, BT_ON, 0);
-       write_status(wl_switch_handle, 1, WL_ON, 0);
+       write_status(bt_switch_handle, 1, BT_ON);
+       write_status(wl_switch_handle, 1, WL_ON);
+
+       /* If the h/w switch is off, we need to check the real status */
+       write_status(NULL, read_status(BT_ON), BT_ON);
+       write_status(NULL, read_status(WL_ON), WL_ON);
 
        /* LCD Backlight is on by default */
-       write_status(NULL, 1, LCD_ON, 0);
+       write_status(NULL, 1, LCD_ON);
 
        /* LED display is off by default */
        hotk->ledd_status = 0xFFF;
@@ -991,6 +1025,7 @@ static void asus_led_exit(void)
        ASUS_LED_UNREGISTER(tled);
        ASUS_LED_UNREGISTER(pled);
        ASUS_LED_UNREGISTER(rled);
+       ASUS_LED_UNREGISTER(gled);
 
        destroy_workqueue(led_workqueue);
 }
@@ -1062,6 +1097,10 @@ static int asus_led_init(struct device *dev)
        if (rv)
                return rv;
 
+       rv = ASUS_LED_REGISTER(gled, dev);
+       if (rv)
+               return rv;
+
        led_workqueue = create_singlethread_workqueue("led_workqueue");
        if (!led_workqueue)
                return -ENOMEM;
index ac708bc2f9f3ce3bf158c74cf376072d7ac8726b..c15c1f61bd1bfad853b8c6ea343d6cdd47e98135 100644 (file)
@@ -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
 #include <linux/backlight.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/kfifo.h>
+#include <linux/workqueue.h>
+#include <linux/acpi.h>
 #include <acpi/acpi_drivers.h>
 #include <acpi/acpi_bus.h>
 #include <asm/uaccess.h>
+#include <linux/sonypi.h>
+#include <linux/sony-laptop.h>
+#ifdef CONFIG_SONY_LAPTOP_OLD
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#endif
 
-#define ACPI_SNC_CLASS         "sony"
-#define ACPI_SNC_HID           "SNY5001"
-#define ACPI_SNC_DRIVER_NAME   "ACPI Sony Notebook Control Driver v0.4"
+#define DRV_PFX                        "sony-laptop: "
+#define dprintk(msg...)                do {                    \
+       if (debug) printk(KERN_WARNING DRV_PFX  msg);   \
+} while (0)
 
-/* the device uses 1-based values, while the backlight subsystem uses
-   0-based values */
-#define SONY_MAX_BRIGHTNESS    8
+#define SONY_LAPTOP_DRIVER_VERSION     "0.5"
 
-#define LOG_PFX                        KERN_WARNING "sony-laptop: "
+#define SONY_NC_CLASS          "sony-nc"
+#define SONY_NC_HID            "SNY5001"
+#define SONY_NC_DRIVER_NAME    "Sony Notebook Control Driver"
+
+#define SONY_PIC_CLASS         "sony-pic"
+#define SONY_PIC_HID           "SNY6001"
+#define SONY_PIC_DRIVER_NAME   "Sony Programmable IO Control Driver"
 
 MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
-MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME);
+MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");
 MODULE_LICENSE("GPL");
+MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);
 
 static int debug;
 module_param(debug, int, 0);
 MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
                 "the development of this driver");
 
-static ssize_t sony_acpi_show(struct device *, struct device_attribute *,
+static int no_spic;            /* = 0 */
+module_param(no_spic, int, 0444);
+MODULE_PARM_DESC(no_spic,
+                "set this if you don't want to enable the SPIC device");
+
+static int compat;             /* = 0 */
+module_param(compat, int, 0444);
+MODULE_PARM_DESC(compat,
+                "set this if you want to enable backward compatibility mode");
+
+static unsigned long mask = 0xffffffff;
+module_param(mask, ulong, 0644);
+MODULE_PARM_DESC(mask,
+                "set this to the mask of event you want to enable (see doc)");
+
+static int camera;             /* = 0 */
+module_param(camera, int, 0444);
+MODULE_PARM_DESC(camera,
+                "set this to 1 to enable Motion Eye camera controls "
+                "(only use it if you have a C1VE or C1VN model)");
+
+#ifdef CONFIG_SONY_LAPTOP_OLD
+static int minor = -1;
+module_param(minor, int, 0);
+MODULE_PARM_DESC(minor,
+                "minor number of the misc device for the SPIC compatibility code, "
+                "default is -1 (automatic)");
+#endif
+
+/*********** Input Devices ***********/
+
+#define SONY_LAPTOP_BUF_SIZE   128
+struct sony_laptop_input_s {
+       atomic_t                users;
+       struct input_dev        *jog_dev;
+       struct input_dev        *key_dev;
+       struct kfifo            *fifo;
+       spinlock_t              fifo_lock;
+       struct workqueue_struct *wq;
+};
+static struct sony_laptop_input_s sony_laptop_input = {
+       .users = ATOMIC_INIT(0),
+};
+
+struct sony_laptop_keypress {
+       struct input_dev *dev;
+       int key;
+};
+
+/* Correspondance table between sonypi events and input layer events */
+static struct {
+       int sonypiev;
+       int inputev;
+} sony_laptop_inputkeys[] = {
+       { SONYPI_EVENT_CAPTURE_PRESSED,         KEY_CAMERA },
+       { SONYPI_EVENT_FNKEY_ONLY,              KEY_FN },
+       { SONYPI_EVENT_FNKEY_ESC,               KEY_FN_ESC },
+       { SONYPI_EVENT_FNKEY_F1,                KEY_FN_F1 },
+       { SONYPI_EVENT_FNKEY_F2,                KEY_FN_F2 },
+       { SONYPI_EVENT_FNKEY_F3,                KEY_FN_F3 },
+       { SONYPI_EVENT_FNKEY_F4,                KEY_FN_F4 },
+       { SONYPI_EVENT_FNKEY_F5,                KEY_FN_F5 },
+       { SONYPI_EVENT_FNKEY_F6,                KEY_FN_F6 },
+       { SONYPI_EVENT_FNKEY_F7,                KEY_FN_F7 },
+       { SONYPI_EVENT_FNKEY_F8,                KEY_FN_F8 },
+       { SONYPI_EVENT_FNKEY_F9,                KEY_FN_F9 },
+       { SONYPI_EVENT_FNKEY_F10,               KEY_FN_F10 },
+       { SONYPI_EVENT_FNKEY_F11,               KEY_FN_F11 },
+       { SONYPI_EVENT_FNKEY_F12,               KEY_FN_F12 },
+       { SONYPI_EVENT_FNKEY_1,                 KEY_FN_1 },
+       { SONYPI_EVENT_FNKEY_2,                 KEY_FN_2 },
+       { SONYPI_EVENT_FNKEY_D,                 KEY_FN_D },
+       { SONYPI_EVENT_FNKEY_E,                 KEY_FN_E },
+       { SONYPI_EVENT_FNKEY_F,                 KEY_FN_F },
+       { SONYPI_EVENT_FNKEY_S,                 KEY_FN_S },
+       { SONYPI_EVENT_FNKEY_B,                 KEY_FN_B },
+       { SONYPI_EVENT_BLUETOOTH_PRESSED,       KEY_BLUE },
+       { SONYPI_EVENT_BLUETOOTH_ON,            KEY_BLUE },
+       { SONYPI_EVENT_PKEY_P1,                 KEY_PROG1 },
+       { SONYPI_EVENT_PKEY_P2,                 KEY_PROG2 },
+       { SONYPI_EVENT_PKEY_P3,                 KEY_PROG3 },
+       { SONYPI_EVENT_BACK_PRESSED,            KEY_BACK },
+       { SONYPI_EVENT_HELP_PRESSED,            KEY_HELP },
+       { SONYPI_EVENT_ZOOM_PRESSED,            KEY_ZOOM },
+       { SONYPI_EVENT_THUMBPHRASE_PRESSED,     BTN_THUMB },
+       { 0, 0 },
+};
+
+/* release buttons after a short delay if pressed */
+static void do_sony_laptop_release_key(struct work_struct *work)
+{
+       struct sony_laptop_keypress kp;
+
+       while (kfifo_get(sony_laptop_input.fifo, (unsigned char *)&kp,
+                        sizeof(kp)) == sizeof(kp)) {
+               msleep(10);
+               input_report_key(kp.dev, kp.key, 0);
+               input_sync(kp.dev);
+       }
+}
+static DECLARE_WORK(sony_laptop_release_key_work,
+               do_sony_laptop_release_key);
+
+/* forward event to the input subsytem */
+static void sony_laptop_report_input_event(u8 event)
+{
+       struct input_dev *jog_dev = sony_laptop_input.jog_dev;
+       struct input_dev *key_dev = sony_laptop_input.key_dev;
+       struct sony_laptop_keypress kp = { NULL };
+       int i;
+
+       if (event == SONYPI_EVENT_FNKEY_RELEASED) {
+               /* Nothing, not all VAIOs generate this event */
+               return;
+       }
+
+       /* report events */
+       switch (event) {
+       /* jog_dev events */
+       case SONYPI_EVENT_JOGDIAL_UP:
+       case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
+               input_report_rel(jog_dev, REL_WHEEL, 1);
+               input_sync(jog_dev);
+               return;
+
+       case SONYPI_EVENT_JOGDIAL_DOWN:
+       case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
+               input_report_rel(jog_dev, REL_WHEEL, -1);
+               input_sync(jog_dev);
+               return;
+
+       /* key_dev events */
+       case SONYPI_EVENT_JOGDIAL_PRESSED:
+               kp.key = BTN_MIDDLE;
+               kp.dev = jog_dev;
+               break;
+
+       default:
+               for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++)
+                       if (event == sony_laptop_inputkeys[i].sonypiev) {
+                               kp.dev = key_dev;
+                               kp.key = sony_laptop_inputkeys[i].inputev;
+                               break;
+                       }
+               break;
+       }
+
+       if (kp.dev) {
+               input_report_key(kp.dev, kp.key, 1);
+               input_sync(kp.dev);
+               kfifo_put(sony_laptop_input.fifo,
+                         (unsigned char *)&kp, sizeof(kp));
+
+               if (!work_pending(&sony_laptop_release_key_work))
+                       queue_work(sony_laptop_input.wq,
+                                       &sony_laptop_release_key_work);
+       } else
+               dprintk("unknown input event %.2x\n", event);
+}
+
+static int sony_laptop_setup_input(void)
+{
+       struct input_dev *jog_dev;
+       struct input_dev *key_dev;
+       int i;
+       int error;
+
+       /* don't run again if already initialized */
+       if (atomic_add_return(1, &sony_laptop_input.users) > 1)
+               return 0;
+
+       /* kfifo */
+       spin_lock_init(&sony_laptop_input.fifo_lock);
+       sony_laptop_input.fifo =
+               kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
+                           &sony_laptop_input.fifo_lock);
+       if (IS_ERR(sony_laptop_input.fifo)) {
+               printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
+               error = PTR_ERR(sony_laptop_input.fifo);
+               goto err_dec_users;
+       }
+
+       /* init workqueue */
+       sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop");
+       if (!sony_laptop_input.wq) {
+               printk(KERN_ERR DRV_PFX
+                               "Unabe to create workqueue.\n");
+               error = -ENXIO;
+               goto err_free_kfifo;
+       }
+
+       /* input keys */
+       key_dev = input_allocate_device();
+       if (!key_dev) {
+               error = -ENOMEM;
+               goto err_destroy_wq;
+       }
+
+       key_dev->name = "Sony Vaio Keys";
+       key_dev->id.bustype = BUS_ISA;
+       key_dev->id.vendor = PCI_VENDOR_ID_SONY;
+
+       /* Initialize the Input Drivers: special keys */
+       key_dev->evbit[0] = BIT(EV_KEY);
+       for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++)
+               if (sony_laptop_inputkeys[i].inputev)
+                       set_bit(sony_laptop_inputkeys[i].inputev,
+                                       key_dev->keybit);
+
+       error = input_register_device(key_dev);
+       if (error)
+               goto err_free_keydev;
+
+       sony_laptop_input.key_dev = key_dev;
+
+       /* jogdial */
+       jog_dev = input_allocate_device();
+       if (!jog_dev) {
+               error = -ENOMEM;
+               goto err_unregister_keydev;
+       }
+
+       jog_dev->name = "Sony Vaio Jogdial";
+       jog_dev->id.bustype = BUS_ISA;
+       jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
+
+       jog_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+       jog_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_MIDDLE);
+       jog_dev->relbit[0] = BIT(REL_WHEEL);
+
+       error = input_register_device(jog_dev);
+       if (error)
+               goto err_free_jogdev;
+
+       sony_laptop_input.jog_dev = jog_dev;
+
+       return 0;
+
+err_free_jogdev:
+       input_free_device(jog_dev);
+
+err_unregister_keydev:
+       input_unregister_device(key_dev);
+       /* to avoid kref underflow below at input_free_device */
+       key_dev = NULL;
+
+err_free_keydev:
+       input_free_device(key_dev);
+
+err_destroy_wq:
+       destroy_workqueue(sony_laptop_input.wq);
+
+err_free_kfifo:
+       kfifo_free(sony_laptop_input.fifo);
+
+err_dec_users:
+       atomic_dec(&sony_laptop_input.users);
+       return error;
+}
+
+static void sony_laptop_remove_input(void)
+{
+       /* cleanup only after the last user has gone */
+       if (!atomic_dec_and_test(&sony_laptop_input.users))
+               return;
+
+       /* flush workqueue first */
+       flush_workqueue(sony_laptop_input.wq);
+
+       /* destroy input devs */
+       input_unregister_device(sony_laptop_input.key_dev);
+       sony_laptop_input.key_dev = NULL;
+
+       if (sony_laptop_input.jog_dev) {
+               input_unregister_device(sony_laptop_input.jog_dev);
+               sony_laptop_input.jog_dev = NULL;
+       }
+
+       destroy_workqueue(sony_laptop_input.wq);
+       kfifo_free(sony_laptop_input.fifo);
+}
+
+/*********** Platform Device ***********/
+
+static atomic_t sony_pf_users = ATOMIC_INIT(0);
+static struct platform_driver sony_pf_driver = {
+       .driver = {
+                  .name = "sony-laptop",
+                  .owner = THIS_MODULE,
+                  }
+};
+static struct platform_device *sony_pf_device;
+
+static int sony_pf_add(void)
+{
+       int ret = 0;
+
+       /* don't run again if already initialized */
+       if (atomic_add_return(1, &sony_pf_users) > 1)
+               return 0;
+
+       ret = platform_driver_register(&sony_pf_driver);
+       if (ret)
+               goto out;
+
+       sony_pf_device = platform_device_alloc("sony-laptop", -1);
+       if (!sony_pf_device) {
+               ret = -ENOMEM;
+               goto out_platform_registered;
+       }
+
+       ret = platform_device_add(sony_pf_device);
+       if (ret)
+               goto out_platform_alloced;
+
+       return 0;
+
+      out_platform_alloced:
+       platform_device_put(sony_pf_device);
+       sony_pf_device = NULL;
+      out_platform_registered:
+       platform_driver_unregister(&sony_pf_driver);
+      out:
+       atomic_dec(&sony_pf_users);
+       return ret;
+}
+
+static void sony_pf_remove(void)
+{
+       /* deregister only after the last user has gone */
+       if (!atomic_dec_and_test(&sony_pf_users))
+               return;
+
+       platform_device_del(sony_pf_device);
+       platform_device_put(sony_pf_device);
+       platform_driver_unregister(&sony_pf_driver);
+}
+
+/*********** SNC (SNY5001) Device ***********/
+
+/* the device uses 1-based values, while the backlight subsystem uses
+   0-based values */
+#define SONY_MAX_BRIGHTNESS    8
+
+#define SNC_VALIDATE_IN                0
+#define SNC_VALIDATE_OUT       1
+
+static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *,
                              char *);
-static ssize_t sony_acpi_store(struct device *, struct device_attribute *,
+static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *,
                               const char *, size_t);
 static int boolean_validate(const int, const int);
 static int brightness_default_validate(const int, const int);
 
-#define SNC_VALIDATE_IN                0
-#define SNC_VALIDATE_OUT       1
-
-struct sony_acpi_value {
+struct sony_nc_value {
        char *name;             /* name of the entry */
        char **acpiget;         /* names of the ACPI get function */
        char **acpiset;         /* names of the ACPI set function */
@@ -75,65 +458,65 @@ struct sony_acpi_value {
        struct device_attribute devattr;        /* sysfs atribute */
 };
 
-#define HANDLE_NAMES(_name, _values...) \
+#define SNC_HANDLE_NAMES(_name, _values...) \
        static char *snc_##_name[] = { _values, NULL }
 
-#define SONY_ACPI_VALUE(_name, _getters, _setters, _validate, _debug) \
+#define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \
        { \
                .name           = __stringify(_name), \
                .acpiget        = _getters, \
                .acpiset        = _setters, \
                .validate       = _validate, \
                .debug          = _debug, \
-               .devattr        = __ATTR(_name, 0, sony_acpi_show, sony_acpi_store), \
+               .devattr        = __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \
        }
 
-#define SONY_ACPI_VALUE_NULL   { .name = NULL }
+#define SNC_HANDLE_NULL        { .name = NULL }
 
-HANDLE_NAMES(fnkey_get, "GHKE");
+SNC_HANDLE_NAMES(fnkey_get, "GHKE");
 
-HANDLE_NAMES(brightness_def_get, "GPBR");
-HANDLE_NAMES(brightness_def_set, "SPBR");
+SNC_HANDLE_NAMES(brightness_def_get, "GPBR");
+SNC_HANDLE_NAMES(brightness_def_set, "SPBR");
 
-HANDLE_NAMES(cdpower_get, "GCDP");
-HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
+SNC_HANDLE_NAMES(cdpower_get, "GCDP");
+SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
 
-HANDLE_NAMES(audiopower_get, "GAZP");
-HANDLE_NAMES(audiopower_set, "AZPW");
+SNC_HANDLE_NAMES(audiopower_get, "GAZP");
+SNC_HANDLE_NAMES(audiopower_set, "AZPW");
 
-HANDLE_NAMES(lanpower_get, "GLNP");
-HANDLE_NAMES(lanpower_set, "LNPW");
+SNC_HANDLE_NAMES(lanpower_get, "GLNP");
+SNC_HANDLE_NAMES(lanpower_set, "LNPW");
 
-HANDLE_NAMES(PID_get, "GPID");
+SNC_HANDLE_NAMES(PID_get, "GPID");
 
-HANDLE_NAMES(CTR_get, "GCTR");
-HANDLE_NAMES(CTR_set, "SCTR");
+SNC_HANDLE_NAMES(CTR_get, "GCTR");
+SNC_HANDLE_NAMES(CTR_set, "SCTR");
 
-HANDLE_NAMES(PCR_get, "GPCR");
-HANDLE_NAMES(PCR_set, "SPCR");
+SNC_HANDLE_NAMES(PCR_get, "GPCR");
+SNC_HANDLE_NAMES(PCR_set, "SPCR");
 
-HANDLE_NAMES(CMI_get, "GCMI");
-HANDLE_NAMES(CMI_set, "SCMI");
+SNC_HANDLE_NAMES(CMI_get, "GCMI");
+SNC_HANDLE_NAMES(CMI_set, "SCMI");
 
-static struct sony_acpi_value sony_acpi_values[] = {
-       SONY_ACPI_VALUE(brightness_default, snc_brightness_def_get,
+static struct sony_nc_value sony_nc_values[] = {
+       SNC_HANDLE(brightness_default, snc_brightness_def_get,
                        snc_brightness_def_set, brightness_default_validate, 0),
-       SONY_ACPI_VALUE(fnkey, snc_fnkey_get, NULL, NULL, 0),
-       SONY_ACPI_VALUE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
-       SONY_ACPI_VALUE(audiopower, snc_audiopower_get, snc_audiopower_set,
+       SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0),
+       SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
+       SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set,
                        boolean_validate, 0),
-       SONY_ACPI_VALUE(lanpower, snc_lanpower_get, snc_lanpower_set,
+       SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set,
                        boolean_validate, 1),
        /* unknown methods */
-       SONY_ACPI_VALUE(PID, snc_PID_get, NULL, NULL, 1),
-       SONY_ACPI_VALUE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
-       SONY_ACPI_VALUE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
-       SONY_ACPI_VALUE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
-       SONY_ACPI_VALUE_NULL
+       SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1),
+       SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
+       SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
+       SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
+       SNC_HANDLE_NULL
 };
 
-static acpi_handle sony_acpi_handle;
-static struct acpi_device *sony_acpi_acpi_device = NULL;
+static acpi_handle sony_nc_acpi_handle;
+static struct acpi_device *sony_nc_acpi_device = NULL;
 
 /*
  * acpi_evaluate_object wrappers
@@ -153,7 +536,7 @@ static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
                return 0;
        }
 
-       printk(LOG_PFX "acpi_callreadfunc failed\n");
+       printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n");
 
        return -1;
 }
@@ -179,7 +562,7 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
        if (status == AE_OK) {
                if (result != NULL) {
                        if (out_obj.type != ACPI_TYPE_INTEGER) {
-                               printk(LOG_PFX "acpi_evaluate_object bad "
+                               printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad "
                                       "return type\n");
                                return -1;
                        }
@@ -188,13 +571,13 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
                return 0;
        }
 
-       printk(LOG_PFX "acpi_evaluate_object failed\n");
+       printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n");
 
        return -1;
 }
 
 /*
- * sony_acpi_values input/output validate functions
+ * sony_nc_values input/output validate functions
  */
 
 /* brightness_default_validate:
@@ -229,19 +612,19 @@ static int boolean_validate(const int direction, const int value)
 }
 
 /*
- * Sysfs show/store common to all sony_acpi_values
+ * Sysfs show/store common to all sony_nc_values
  */
-static ssize_t sony_acpi_show(struct device *dev, struct device_attribute *attr,
+static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr,
                              char *buffer)
 {
        int value;
-       struct sony_acpi_value *item =
-           container_of(attr, struct sony_acpi_value, devattr);
+       struct sony_nc_value *item =
+           container_of(attr, struct sony_nc_value, devattr);
 
        if (!*item->acpiget)
                return -EIO;
 
-       if (acpi_callgetfunc(sony_acpi_handle, *item->acpiget, &value) < 0)
+       if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0)
                return -EIO;
 
        if (item->validate)
@@ -250,13 +633,13 @@ static ssize_t sony_acpi_show(struct device *dev, struct device_attribute *attr,
        return snprintf(buffer, PAGE_SIZE, "%d\n", value);
 }
 
-static ssize_t sony_acpi_store(struct device *dev,
+static ssize_t sony_nc_sysfs_store(struct device *dev,
                               struct device_attribute *attr,
                               const char *buffer, size_t count)
 {
        int value;
-       struct sony_acpi_value *item =
-           container_of(attr, struct sony_acpi_value, devattr);
+       struct sony_nc_value *item =
+           container_of(attr, struct sony_nc_value, devattr);
 
        if (!item->acpiset)
                return -EIO;
@@ -272,118 +655,20 @@ static ssize_t sony_acpi_store(struct device *dev,
        if (value < 0)
                return value;
 
-       if (acpi_callsetfunc(sony_acpi_handle, *item->acpiset, value, NULL) < 0)
+       if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0)
                return -EIO;
        item->value = value;
        item->valid = 1;
        return count;
 }
 
-/*
- * Platform device
- */
-static struct platform_driver sncpf_driver = {
-       .driver = {
-                  .name = "sony-laptop",
-                  .owner = THIS_MODULE,
-                  }
-};
-static struct platform_device *sncpf_device;
-
-static int sony_snc_pf_add(void)
-{
-       acpi_handle handle;
-       struct sony_acpi_value *item;
-       int ret = 0;
-
-       ret = platform_driver_register(&sncpf_driver);
-       if (ret)
-               goto out;
-
-       sncpf_device = platform_device_alloc("sony-laptop", -1);
-       if (!sncpf_device) {
-               ret = -ENOMEM;
-               goto out_platform_registered;
-       }
-
-       ret = platform_device_add(sncpf_device);
-       if (ret)
-               goto out_platform_alloced;
-
-       for (item = sony_acpi_values; item->name; ++item) {
-
-               if (!debug && item->debug)
-                       continue;
-
-               /* find the available acpiget as described in the DSDT */
-               for (; item->acpiget && *item->acpiget; ++item->acpiget) {
-                       if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle,
-                                                        *item->acpiget,
-                                                        &handle))) {
-                               if (debug)
-                                       printk(LOG_PFX "Found %s getter: %s\n",
-                                              item->name, *item->acpiget);
-                               item->devattr.attr.mode |= S_IRUGO;
-                               break;
-                       }
-               }
-
-               /* find the available acpiset as described in the DSDT */
-               for (; item->acpiset && *item->acpiset; ++item->acpiset) {
-                       if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle,
-                                                        *item->acpiset,
-                                                        &handle))) {
-                               if (debug)
-                                       printk(LOG_PFX "Found %s setter: %s\n",
-                                              item->name, *item->acpiset);
-                               item->devattr.attr.mode |= S_IWUSR;
-                               break;
-                       }
-               }
-
-               if (item->devattr.attr.mode != 0) {
-                       ret =
-                           device_create_file(&sncpf_device->dev,
-                                              &item->devattr);
-                       if (ret)
-                               goto out_sysfs;
-               }
-       }
-
-       return 0;
-
-      out_sysfs:
-       for (item = sony_acpi_values; item->name; ++item) {
-               device_remove_file(&sncpf_device->dev, &item->devattr);
-       }
-       platform_device_del(sncpf_device);
-      out_platform_alloced:
-       platform_device_put(sncpf_device);
-      out_platform_registered:
-       platform_driver_unregister(&sncpf_driver);
-      out:
-       return ret;
-}
-
-static void sony_snc_pf_remove(void)
-{
-       struct sony_acpi_value *item;
-
-       for (item = sony_acpi_values; item->name; ++item) {
-               device_remove_file(&sncpf_device->dev, &item->devattr);
-       }
-
-       platform_device_del(sncpf_device);
-       platform_device_put(sncpf_device);
-       platform_driver_unregister(&sncpf_driver);
-}
 
 /*
  * Backlight device
  */
 static int sony_backlight_update_status(struct backlight_device *bd)
 {
-       return acpi_callsetfunc(sony_acpi_handle, "SBRT",
+       return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
                                bd->props.brightness + 1, NULL);
 }
 
@@ -391,7 +676,7 @@ static int sony_backlight_get_brightness(struct backlight_device *bd)
 {
        int value;
 
-       if (acpi_callgetfunc(sony_acpi_handle, "GBRT", &value))
+       if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value))
                return 0;
        /* brightness levels are 1-based, while backlight ones are 0-based */
        return value - 1;
@@ -408,9 +693,9 @@ static struct backlight_ops sony_backlight_ops = {
  */
 static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
 {
-       if (debug)
-               printk(LOG_PFX "sony_acpi_notify, event: %d\n", event);
-       acpi_bus_generate_event(sony_acpi_acpi_device, 1, event);
+       dprintk("sony_acpi_notify, event: %d\n", event);
+       sony_laptop_report_input_event(event);
+       acpi_bus_generate_event(sony_nc_acpi_device, 1, event);
 }
 
 static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
@@ -422,7 +707,7 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
        node = (struct acpi_namespace_node *)handle;
        operand = (union acpi_operand_object *)node->object;
 
-       printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
+       printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
               (u32) operand->method.param_count);
 
        return AE_OK;
@@ -431,16 +716,16 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
 /*
  * ACPI device
  */
-static int sony_acpi_resume(struct acpi_device *device)
+static int sony_nc_resume(struct acpi_device *device)
 {
-       struct sony_acpi_value *item;
+       struct sony_nc_value *item;
 
-       for (item = sony_acpi_values; item->name; item++) {
+       for (item = sony_nc_values; item->name; item++) {
                int ret;
 
                if (!item->valid)
                        continue;
-               ret = acpi_callsetfunc(sony_acpi_handle, *item->acpiset,
+               ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
                                       item->value, NULL);
                if (ret < 0) {
                        printk("%s: %d\n", __FUNCTION__, ret);
@@ -450,42 +735,55 @@ static int sony_acpi_resume(struct acpi_device *device)
        return 0;
 }
 
-static int sony_acpi_add(struct acpi_device *device)
+static int sony_nc_add(struct acpi_device *device)
 {
        acpi_status status;
        int result = 0;
        acpi_handle handle;
+       struct sony_nc_value *item;
+
+       printk(KERN_INFO DRV_PFX "%s v%s.\n",
+               SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
 
-       sony_acpi_acpi_device = device;
+       sony_nc_acpi_device = device;
+       strcpy(acpi_device_class(device), "sony/hotkey");
 
-       sony_acpi_handle = device->handle;
+       sony_nc_acpi_handle = device->handle;
 
        if (debug) {
-               status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_acpi_handle,
+               status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
                                             1, sony_walk_callback, NULL, NULL);
                if (ACPI_FAILURE(status)) {
-                       printk(LOG_PFX "unable to walk acpi resources\n");
+                       printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n");
                        result = -ENODEV;
                        goto outwalk;
                }
        }
 
-       status = acpi_install_notify_handler(sony_acpi_handle,
+       /* setup input devices and helper fifo */
+       result = sony_laptop_setup_input();
+       if (result) {
+               printk(KERN_ERR DRV_PFX
+                               "Unabe to create input devices.\n");
+               goto outwalk;
+       }
+
+       status = acpi_install_notify_handler(sony_nc_acpi_handle,
                                             ACPI_DEVICE_NOTIFY,
                                             sony_acpi_notify, NULL);
        if (ACPI_FAILURE(status)) {
-               printk(LOG_PFX "unable to install notify handler\n");
+               printk(KERN_WARNING DRV_PFX "unable to install notify handler\n");
                result = -ENODEV;
-               goto outwalk;
+               goto outinput;
        }
 
-       if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle, "GBRT", &handle))) {
+       if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) {
                sony_backlight_device = backlight_device_register("sony", NULL,
                                                                  NULL,
                                                                  &sony_backlight_ops);
 
                if (IS_ERR(sony_backlight_device)) {
-                       printk(LOG_PFX "unable to register backlight device\n");
+                       printk(KERN_WARNING DRV_PFX "unable to register backlight device\n");
                        sony_backlight_device = NULL;
                } else {
                        sony_backlight_device->props.brightness =
@@ -497,68 +795,1497 @@ static int sony_acpi_add(struct acpi_device *device)
 
        }
 
-       if (sony_snc_pf_add())
+       result = sony_pf_add();
+       if (result)
                goto outbacklight;
 
-       printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
+       /* create sony_pf sysfs attributes related to the SNC device */
+       for (item = sony_nc_values; item->name; ++item) {
+
+               if (!debug && item->debug)
+                       continue;
+
+               /* find the available acpiget as described in the DSDT */
+               for (; item->acpiget && *item->acpiget; ++item->acpiget) {
+                       if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
+                                                        *item->acpiget,
+                                                        &handle))) {
+                               dprintk("Found %s getter: %s\n",
+                                               item->name, *item->acpiget);
+                               item->devattr.attr.mode |= S_IRUGO;
+                               break;
+                       }
+               }
+
+               /* find the available acpiset as described in the DSDT */
+               for (; item->acpiset && *item->acpiset; ++item->acpiset) {
+                       if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
+                                                        *item->acpiset,
+                                                        &handle))) {
+                               dprintk("Found %s setter: %s\n",
+                                               item->name, *item->acpiset);
+                               item->devattr.attr.mode |= S_IWUSR;
+                               break;
+                       }
+               }
+
+               if (item->devattr.attr.mode != 0) {
+                       result =
+                           device_create_file(&sony_pf_device->dev,
+                                              &item->devattr);
+                       if (result)
+                               goto out_sysfs;
+               }
+       }
 
        return 0;
 
+      out_sysfs:
+       for (item = sony_nc_values; item->name; ++item) {
+               device_remove_file(&sony_pf_device->dev, &item->devattr);
+       }
+       sony_pf_remove();
+
       outbacklight:
        if (sony_backlight_device)
                backlight_device_unregister(sony_backlight_device);
 
-       status = acpi_remove_notify_handler(sony_acpi_handle,
+       status = acpi_remove_notify_handler(sony_nc_acpi_handle,
                                            ACPI_DEVICE_NOTIFY,
                                            sony_acpi_notify);
        if (ACPI_FAILURE(status))
-               printk(LOG_PFX "unable to remove notify handler\n");
+               printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
+
+      outinput:
+       sony_laptop_remove_input();
+
       outwalk:
        return result;
 }
 
-static int sony_acpi_remove(struct acpi_device *device, int type)
+static int sony_nc_remove(struct acpi_device *device, int type)
 {
        acpi_status status;
+       struct sony_nc_value *item;
 
        if (sony_backlight_device)
                backlight_device_unregister(sony_backlight_device);
 
-       sony_acpi_acpi_device = NULL;
+       sony_nc_acpi_device = NULL;
 
-       status = acpi_remove_notify_handler(sony_acpi_handle,
+       status = acpi_remove_notify_handler(sony_nc_acpi_handle,
                                            ACPI_DEVICE_NOTIFY,
                                            sony_acpi_notify);
        if (ACPI_FAILURE(status))
-               printk(LOG_PFX "unable to remove notify handler\n");
+               printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
 
-       sony_snc_pf_remove();
+       for (item = sony_nc_values; item->name; ++item) {
+               device_remove_file(&sony_pf_device->dev, &item->devattr);
+       }
 
-       printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
+       sony_pf_remove();
+       sony_laptop_remove_input();
+       dprintk(SONY_NC_DRIVER_NAME " removed.\n");
 
        return 0;
 }
 
-static struct acpi_driver sony_acpi_driver = {
-       .name = ACPI_SNC_DRIVER_NAME,
-       .class = ACPI_SNC_CLASS,
-       .ids = ACPI_SNC_HID,
+static struct acpi_driver sony_nc_driver = {
+       .name = SONY_NC_DRIVER_NAME,
+       .class = SONY_NC_CLASS,
+       .ids = SONY_NC_HID,
+       .owner = THIS_MODULE,
        .ops = {
-               .add = sony_acpi_add,
-               .remove = sony_acpi_remove,
-               .resume = sony_acpi_resume,
+               .add = sony_nc_add,
+               .remove = sony_nc_remove,
+               .resume = sony_nc_resume,
                },
 };
 
-static int __init sony_acpi_init(void)
-{
-       return acpi_bus_register_driver(&sony_acpi_driver);
+/*********** SPIC (SNY6001) Device ***********/
+
+#define SONYPI_DEVICE_TYPE1    0x00000001
+#define SONYPI_DEVICE_TYPE2    0x00000002
+#define SONYPI_DEVICE_TYPE3    0x00000004
+
+#define SONY_PIC_EV_MASK       0xff
+
+struct sony_pic_ioport {
+       struct acpi_resource_io io;
+       struct list_head        list;
+};
+
+struct sony_pic_irq {
+       struct acpi_resource_irq        irq;
+       struct list_head                list;
+};
+
+struct sony_pic_dev {
+       int                     model;
+       u8                      camera_power;
+       u8                      bluetooth_power;
+       u8                      wwan_power;
+       struct acpi_device      *acpi_dev;
+       struct sony_pic_irq     *cur_irq;
+       struct sony_pic_ioport  *cur_ioport;
+       struct list_head        interrupts;
+       struct list_head        ioports;
+       struct mutex            lock;
+};
+
+static struct sony_pic_dev spic_dev = {
+       .interrupts     = LIST_HEAD_INIT(spic_dev.interrupts),
+       .ioports        = LIST_HEAD_INIT(spic_dev.ioports),
+};
+
+/* Event masks */
+#define SONYPI_JOGGER_MASK                     0x00000001
+#define SONYPI_CAPTURE_MASK                    0x00000002
+#define SONYPI_FNKEY_MASK                      0x00000004
+#define SONYPI_BLUETOOTH_MASK                  0x00000008
+#define SONYPI_PKEY_MASK                       0x00000010
+#define SONYPI_BACK_MASK                       0x00000020
+#define SONYPI_HELP_MASK                       0x00000040
+#define SONYPI_LID_MASK                                0x00000080
+#define SONYPI_ZOOM_MASK                       0x00000100
+#define SONYPI_THUMBPHRASE_MASK                        0x00000200
+#define SONYPI_MEYE_MASK                       0x00000400
+#define SONYPI_MEMORYSTICK_MASK                        0x00000800
+#define SONYPI_BATTERY_MASK                    0x00001000
+#define SONYPI_WIRELESS_MASK                   0x00002000
+
+struct sonypi_event {
+       u8      data;
+       u8      event;
+};
+
+/* The set of possible button release events */
+static struct sonypi_event sonypi_releaseev[] = {
+       { 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0, 0 }
+};
+
+/* The set of possible jogger events  */
+static struct sonypi_event sonypi_joggerev[] = {
+       { 0x1f, SONYPI_EVENT_JOGDIAL_UP },
+       { 0x01, SONYPI_EVENT_JOGDIAL_DOWN },
+       { 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED },
+       { 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED },
+       { 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP },
+       { 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN },
+       { 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED },
+       { 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED },
+       { 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP },
+       { 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN },
+       { 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED },
+       { 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED },
+       { 0x40, SONYPI_EVENT_JOGDIAL_PRESSED },
+       { 0, 0 }
+};
+
+/* The set of possible capture button events */
+static struct sonypi_event sonypi_captureev[] = {
+       { 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED },
+       { 0x07, SONYPI_EVENT_CAPTURE_PRESSED },
+       { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED },
+       { 0, 0 }
+};
+
+/* The set of possible fnkeys events */
+static struct sonypi_event sonypi_fnkeyev[] = {
+       { 0x10, SONYPI_EVENT_FNKEY_ESC },
+       { 0x11, SONYPI_EVENT_FNKEY_F1 },
+       { 0x12, SONYPI_EVENT_FNKEY_F2 },
+       { 0x13, SONYPI_EVENT_FNKEY_F3 },
+       { 0x14, SONYPI_EVENT_FNKEY_F4 },
+       { 0x15, SONYPI_EVENT_FNKEY_F5 },
+       { 0x16, SONYPI_EVENT_FNKEY_F6 },
+       { 0x17, SONYPI_EVENT_FNKEY_F7 },
+       { 0x18, SONYPI_EVENT_FNKEY_F8 },
+       { 0x19, SONYPI_EVENT_FNKEY_F9 },
+       { 0x1a, SONYPI_EVENT_FNKEY_F10 },
+       { 0x1b, SONYPI_EVENT_FNKEY_F11 },
+       { 0x1c, SONYPI_EVENT_FNKEY_F12 },
+       { 0x1f, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x21, SONYPI_EVENT_FNKEY_1 },
+       { 0x22, SONYPI_EVENT_FNKEY_2 },
+       { 0x31, SONYPI_EVENT_FNKEY_D },
+       { 0x32, SONYPI_EVENT_FNKEY_E },
+       { 0x33, SONYPI_EVENT_FNKEY_F },
+       { 0x34, SONYPI_EVENT_FNKEY_S },
+       { 0x35, SONYPI_EVENT_FNKEY_B },
+       { 0x36, SONYPI_EVENT_FNKEY_ONLY },
+       { 0, 0 }
+};
+
+/* The set of possible program key events */
+static struct sonypi_event sonypi_pkeyev[] = {
+       { 0x01, SONYPI_EVENT_PKEY_P1 },
+       { 0x02, SONYPI_EVENT_PKEY_P2 },
+       { 0x04, SONYPI_EVENT_PKEY_P3 },
+       { 0x5c, SONYPI_EVENT_PKEY_P1 },
+       { 0, 0 }
+};
+
+/* The set of possible bluetooth events */
+static struct sonypi_event sonypi_blueev[] = {
+       { 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED },
+       { 0x59, SONYPI_EVENT_BLUETOOTH_ON },
+       { 0x5a, SONYPI_EVENT_BLUETOOTH_OFF },
+       { 0, 0 }
+};
+
+/* The set of possible wireless events */
+static struct sonypi_event sonypi_wlessev[] = {
+       { 0x59, SONYPI_EVENT_WIRELESS_ON },
+       { 0x5a, SONYPI_EVENT_WIRELESS_OFF },
+       { 0, 0 }
+};
+
+/* The set of possible back button events */
+static struct sonypi_event sonypi_backev[] = {
+       { 0x20, SONYPI_EVENT_BACK_PRESSED },
+       { 0, 0 }
+};
+
+/* The set of possible help button events */
+static struct sonypi_event sonypi_helpev[] = {
+       { 0x3b, SONYPI_EVENT_HELP_PRESSED },
+       { 0, 0 }
+};
+
+
+/* The set of possible lid events */
+static struct sonypi_event sonypi_lidev[] = {
+       { 0x51, SONYPI_EVENT_LID_CLOSED },
+       { 0x50, SONYPI_EVENT_LID_OPENED },
+       { 0, 0 }
+};
+
+/* The set of possible zoom events */
+static struct sonypi_event sonypi_zoomev[] = {
+       { 0x39, SONYPI_EVENT_ZOOM_PRESSED },
+       { 0, 0 }
+};
+
+/* The set of possible thumbphrase events */
+static struct sonypi_event sonypi_thumbphraseev[] = {
+       { 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED },
+       { 0, 0 }
+};
+
+/* The set of possible motioneye camera events */
+static struct sonypi_event sonypi_meyeev[] = {
+       { 0x00, SONYPI_EVENT_MEYE_FACE },
+       { 0x01, SONYPI_EVENT_MEYE_OPPOSITE },
+       { 0, 0 }
+};
+
+/* The set of possible memorystick events */
+static struct sonypi_event sonypi_memorystickev[] = {
+       { 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT },
+       { 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT },
+       { 0, 0 }
+};
+
+/* The set of possible battery events */
+static struct sonypi_event sonypi_batteryev[] = {
+       { 0x20, SONYPI_EVENT_BATTERY_INSERT },
+       { 0x30, SONYPI_EVENT_BATTERY_REMOVE },
+       { 0, 0 }
+};
+
+static struct sonypi_eventtypes {
+       int                     model;
+       u8                      data;
+       unsigned long           mask;
+       struct sonypi_event *   events;
+} sony_pic_eventtypes[] = {
+       { SONYPI_DEVICE_TYPE1, 0, 0xffffffff, sonypi_releaseev },
+       { SONYPI_DEVICE_TYPE1, 0x70, SONYPI_MEYE_MASK, sonypi_meyeev },
+       { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_LID_MASK, sonypi_lidev },
+       { SONYPI_DEVICE_TYPE1, 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev },
+       { SONYPI_DEVICE_TYPE1, 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev },
+       { SONYPI_DEVICE_TYPE1, 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+       { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
+       { SONYPI_DEVICE_TYPE1, 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev },
+       { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+       { SONYPI_DEVICE_TYPE1, 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev },
+
+       { SONYPI_DEVICE_TYPE2, 0, 0xffffffff, sonypi_releaseev },
+       { SONYPI_DEVICE_TYPE2, 0x38, SONYPI_LID_MASK, sonypi_lidev },
+       { SONYPI_DEVICE_TYPE2, 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev },
+       { SONYPI_DEVICE_TYPE2, 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev },
+       { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+       { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
+       { SONYPI_DEVICE_TYPE2, 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev },
+       { SONYPI_DEVICE_TYPE2, 0x11, SONYPI_BACK_MASK, sonypi_backev },
+       { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_HELP_MASK, sonypi_helpev },
+       { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev },
+       { SONYPI_DEVICE_TYPE2, 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev },
+       { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+       { SONYPI_DEVICE_TYPE2, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
+       { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
+
+       { SONYPI_DEVICE_TYPE3, 0, 0xffffffff, sonypi_releaseev },
+       { SONYPI_DEVICE_TYPE3, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+       { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
+       { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+       { SONYPI_DEVICE_TYPE3, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
+       { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
+       { 0 }
+};
+
+static int sony_pic_detect_device_type(void)
+{
+       struct pci_dev *pcidev;
+       int model = 0;
+
+       if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                                    PCI_DEVICE_ID_INTEL_82371AB_3, NULL)))
+               model = SONYPI_DEVICE_TYPE1;
+
+       else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                                         PCI_DEVICE_ID_INTEL_ICH6_1, NULL)))
+               model = SONYPI_DEVICE_TYPE3;
+
+       else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                                         PCI_DEVICE_ID_INTEL_ICH7_1, NULL)))
+               model = SONYPI_DEVICE_TYPE3;
+
+       else
+               model = SONYPI_DEVICE_TYPE2;
+
+       if (pcidev)
+               pci_dev_put(pcidev);
+
+       printk(KERN_INFO DRV_PFX "detected Type%d model\n",
+                       model == SONYPI_DEVICE_TYPE1 ? 1 :
+                       model == SONYPI_DEVICE_TYPE2 ? 2 : 3);
+       return model;
+}
+
+#define ITERATIONS_LONG                10000
+#define ITERATIONS_SHORT       10
+#define wait_on_command(command, iterations) {                         \
+       unsigned int n = iterations;                                    \
+       while (--n && (command))                                        \
+               udelay(1);                                              \
+       if (!n)                                                         \
+               dprintk("command failed at %s : %s (line %d)\n",        \
+                               __FILE__, __FUNCTION__, __LINE__);      \
+}
+
+static u8 sony_pic_call1(u8 dev)
+{
+       u8 v1, v2;
+
+       wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
+                       ITERATIONS_LONG);
+       outb(dev, spic_dev.cur_ioport->io.minimum + 4);
+       v1 = inb_p(spic_dev.cur_ioport->io.minimum + 4);
+       v2 = inb_p(spic_dev.cur_ioport->io.minimum);
+       dprintk("sony_pic_call1: 0x%.4x\n", (v2 << 8) | v1);
+       return v2;
+}
+
+static u8 sony_pic_call2(u8 dev, u8 fn)
+{
+       u8 v1;
+
+       wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
+                       ITERATIONS_LONG);
+       outb(dev, spic_dev.cur_ioport->io.minimum + 4);
+       wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
+                       ITERATIONS_LONG);
+       outb(fn, spic_dev.cur_ioport->io.minimum);
+       v1 = inb_p(spic_dev.cur_ioport->io.minimum);
+       dprintk("sony_pic_call2: 0x%.4x\n", v1);
+       return v1;
+}
+
+static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
+{
+       u8 v1;
+
+       wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
+       outb(dev, spic_dev.cur_ioport->io.minimum + 4);
+       wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
+       outb(fn, spic_dev.cur_ioport->io.minimum);
+       wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
+       outb(v, spic_dev.cur_ioport->io.minimum);
+       v1 = inb_p(spic_dev.cur_ioport->io.minimum);
+       dprintk("sony_pic_call3: 0x%.4x\n", v1);
+       return v1;
+}
+
+/* camera tests and poweron/poweroff */
+#define SONYPI_CAMERA_PICTURE          5
+#define SONYPI_CAMERA_CONTROL          0x10
+
+#define SONYPI_CAMERA_BRIGHTNESS               0
+#define SONYPI_CAMERA_CONTRAST                 1
+#define SONYPI_CAMERA_HUE                      2
+#define SONYPI_CAMERA_COLOR                    3
+#define SONYPI_CAMERA_SHARPNESS                        4
+
+#define SONYPI_CAMERA_EXPOSURE_MASK            0xC
+#define SONYPI_CAMERA_WHITE_BALANCE_MASK       0x3
+#define SONYPI_CAMERA_PICTURE_MODE_MASK                0x30
+#define SONYPI_CAMERA_MUTE_MASK                        0x40
+
+/* the rest don't need a loop until not 0xff */
+#define SONYPI_CAMERA_AGC                      6
+#define SONYPI_CAMERA_AGC_MASK                 0x30
+#define SONYPI_CAMERA_SHUTTER_MASK             0x7
+
+#define SONYPI_CAMERA_SHUTDOWN_REQUEST         7
+#define SONYPI_CAMERA_CONTROL                  0x10
+
+#define SONYPI_CAMERA_STATUS                   7
+#define SONYPI_CAMERA_STATUS_READY             0x2
+#define SONYPI_CAMERA_STATUS_POSITION          0x4
+
+#define SONYPI_DIRECTION_BACKWARDS             0x4
+
+#define SONYPI_CAMERA_REVISION                         8
+#define SONYPI_CAMERA_ROMVERSION               9
+
+static int __sony_pic_camera_ready(void)
+{
+       u8 v;
+
+       v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS);
+       return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY));
+}
+
+static int __sony_pic_camera_off(void)
+{
+       if (!camera) {
+               printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
+               return -ENODEV;
+       }
+
+       wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE,
+                               SONYPI_CAMERA_MUTE_MASK),
+                       ITERATIONS_SHORT);
+
+       if (spic_dev.camera_power) {
+               sony_pic_call2(0x91, 0);
+               spic_dev.camera_power = 0;
+       }
+       return 0;
+}
+
+static int __sony_pic_camera_on(void)
+{
+       int i, j, x;
+
+       if (!camera) {
+               printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
+               return -ENODEV;
+       }
+
+       if (spic_dev.camera_power)
+               return 0;
+
+       for (j = 5; j > 0; j--) {
+
+               for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++)
+                       msleep(10);
+               sony_pic_call1(0x93);
+
+               for (i = 400; i > 0; i--) {
+                       if (__sony_pic_camera_ready())
+                               break;
+                       msleep(10);
+               }
+               if (i)
+                       break;
+       }
+
+       if (j == 0) {
+               printk(KERN_WARNING DRV_PFX "failed to power on camera\n");
+               return -ENODEV;
+       }
+
+       wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL,
+                               0x5a),
+                       ITERATIONS_SHORT);
+
+       spic_dev.camera_power = 1;
+       return 0;
+}
+
+/* External camera command (exported to the motion eye v4l driver) */
+int sony_pic_camera_command(int command, u8 value)
+{
+       if (!camera)
+               return -EIO;
+
+       mutex_lock(&spic_dev.lock);
+
+       switch (command) {
+       case SONY_PIC_COMMAND_SETCAMERA:
+               if (value)
+                       __sony_pic_camera_on();
+               else
+                       __sony_pic_camera_off();
+               break;
+       case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERACONTRAST:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERAHUE:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERACOLOR:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERASHARPNESS:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERAPICTURE:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERAAGC:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value),
+                               ITERATIONS_SHORT);
+               break;
+       default:
+               printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n",
+                      command);
+               break;
+       }
+       mutex_unlock(&spic_dev.lock);
+       return 0;
+}
+EXPORT_SYMBOL(sony_pic_camera_command);
+
+/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */
+static void sony_pic_set_wwanpower(u8 state)
+{
+       state = !!state;
+       mutex_lock(&spic_dev.lock);
+       if (spic_dev.wwan_power == state) {
+               mutex_unlock(&spic_dev.lock);
+               return;
+       }
+       sony_pic_call2(0xB0, state);
+       spic_dev.wwan_power = state;
+       mutex_unlock(&spic_dev.lock);
+}
+
+static ssize_t sony_pic_wwanpower_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buffer, size_t count)
+{
+       unsigned long value;
+       if (count > 31)
+               return -EINVAL;
+
+       value = simple_strtoul(buffer, NULL, 10);
+       sony_pic_set_wwanpower(value);
+
+       return count;
+}
+
+static ssize_t sony_pic_wwanpower_show(struct device *dev,
+               struct device_attribute *attr, char *buffer)
+{
+       ssize_t count;
+       mutex_lock(&spic_dev.lock);
+       count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power);
+       mutex_unlock(&spic_dev.lock);
+       return count;
+}
+
+/* bluetooth subsystem power state */
+static void __sony_pic_set_bluetoothpower(u8 state)
+{
+       state = !!state;
+       if (spic_dev.bluetooth_power == state)
+               return;
+       sony_pic_call2(0x96, state);
+       sony_pic_call1(0x82);
+       spic_dev.bluetooth_power = state;
+}
+
+static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buffer, size_t count)
+{
+       unsigned long value;
+       if (count > 31)
+               return -EINVAL;
+
+       value = simple_strtoul(buffer, NULL, 10);
+       mutex_lock(&spic_dev.lock);
+       __sony_pic_set_bluetoothpower(value);
+       mutex_unlock(&spic_dev.lock);
+
+       return count;
+}
+
+static ssize_t sony_pic_bluetoothpower_show(struct device *dev,
+               struct device_attribute *attr, char *buffer)
+{
+       ssize_t count = 0;
+       mutex_lock(&spic_dev.lock);
+       count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power);
+       mutex_unlock(&spic_dev.lock);
+       return count;
+}
+
+/* fan speed */
+/* FAN0 information (reverse engineered from ACPI tables) */
+#define SONY_PIC_FAN0_STATUS   0x93
+static int sony_pic_set_fanspeed(unsigned long value)
+{
+       return ec_write(SONY_PIC_FAN0_STATUS, value);
+}
+
+static int sony_pic_get_fanspeed(u8 *value)
+{
+       return ec_read(SONY_PIC_FAN0_STATUS, value);
+}
+
+static ssize_t sony_pic_fanspeed_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buffer, size_t count)
+{
+       unsigned long value;
+       if (count > 31)
+               return -EINVAL;
+
+       value = simple_strtoul(buffer, NULL, 10);
+       if (sony_pic_set_fanspeed(value))
+               return -EIO;
+
+       return count;
+}
+
+static ssize_t sony_pic_fanspeed_show(struct device *dev,
+               struct device_attribute *attr, char *buffer)
+{
+       u8 value = 0;
+       if (sony_pic_get_fanspeed(&value))
+               return -EIO;
+
+       return snprintf(buffer, PAGE_SIZE, "%d\n", value);
+}
+
+#define SPIC_ATTR(_name, _mode)                                        \
+struct device_attribute spic_attr_##_name = __ATTR(_name,      \
+               _mode, sony_pic_## _name ##_show,               \
+               sony_pic_## _name ##_store)
+
+static SPIC_ATTR(bluetoothpower, 0644);
+static SPIC_ATTR(wwanpower, 0644);
+static SPIC_ATTR(fanspeed, 0644);
+
+static struct attribute *spic_attributes[] = {
+       &spic_attr_bluetoothpower.attr,
+       &spic_attr_wwanpower.attr,
+       &spic_attr_fanspeed.attr,
+       NULL
+};
+
+static struct attribute_group spic_attribute_group = {
+       .attrs = spic_attributes
+};
+
+/******** SONYPI compatibility **********/
+#ifdef CONFIG_SONY_LAPTOP_OLD
+
+/* battery / brightness / temperature  addresses */
+#define SONYPI_BAT_FLAGS       0x81
+#define SONYPI_LCD_LIGHT       0x96
+#define SONYPI_BAT1_PCTRM      0xa0
+#define SONYPI_BAT1_LEFT       0xa2
+#define SONYPI_BAT1_MAXRT      0xa4
+#define SONYPI_BAT2_PCTRM      0xa8
+#define SONYPI_BAT2_LEFT       0xaa
+#define SONYPI_BAT2_MAXRT      0xac
+#define SONYPI_BAT1_MAXTK      0xb0
+#define SONYPI_BAT1_FULL       0xb2
+#define SONYPI_BAT2_MAXTK      0xb8
+#define SONYPI_BAT2_FULL       0xba
+#define SONYPI_TEMP_STATUS     0xC1
+
+struct sonypi_compat_s {
+       struct fasync_struct    *fifo_async;
+       struct kfifo            *fifo;
+       spinlock_t              fifo_lock;
+       wait_queue_head_t       fifo_proc_list;
+       atomic_t                open_count;
+};
+static struct sonypi_compat_s sonypi_compat = {
+       .open_count = ATOMIC_INIT(0),
+};
+
+static int sonypi_misc_fasync(int fd, struct file *filp, int on)
+{
+       int retval;
+
+       retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
+       if (retval < 0)
+               return retval;
+       return 0;
+}
+
+static int sonypi_misc_release(struct inode *inode, struct file *file)
+{
+       sonypi_misc_fasync(-1, file, 0);
+       atomic_dec(&sonypi_compat.open_count);
+       return 0;
+}
+
+static int sonypi_misc_open(struct inode *inode, struct file *file)
+{
+       /* Flush input queue on first open */
+       if (atomic_inc_return(&sonypi_compat.open_count) == 1)
+               kfifo_reset(sonypi_compat.fifo);
+       return 0;
+}
+
+static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
+                               size_t count, loff_t *pos)
+{
+       ssize_t ret;
+       unsigned char c;
+
+       if ((kfifo_len(sonypi_compat.fifo) == 0) &&
+           (file->f_flags & O_NONBLOCK))
+               return -EAGAIN;
+
+       ret = wait_event_interruptible(sonypi_compat.fifo_proc_list,
+                                      kfifo_len(sonypi_compat.fifo) != 0);
+       if (ret)
+               return ret;
+
+       while (ret < count &&
+              (kfifo_get(sonypi_compat.fifo, &c, sizeof(c)) == sizeof(c))) {
+               if (put_user(c, buf++))
+                       return -EFAULT;
+               ret++;
+       }
+
+       if (ret > 0) {
+               struct inode *inode = file->f_path.dentry->d_inode;
+               inode->i_atime = current_fs_time(inode->i_sb);
+       }
+
+       return ret;
+}
+
+static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait)
+{
+       poll_wait(file, &sonypi_compat.fifo_proc_list, wait);
+       if (kfifo_len(sonypi_compat.fifo))
+               return POLLIN | POLLRDNORM;
+       return 0;
+}
+
+static int ec_read16(u8 addr, u16 *value)
+{
+       u8 val_lb, val_hb;
+       if (ec_read(addr, &val_lb))
+               return -1;
+       if (ec_read(addr + 1, &val_hb))
+               return -1;
+       *value = val_lb | (val_hb << 8);
+       return 0;
+}
+
+static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
+                            unsigned int cmd, unsigned long arg)
+{
+       int ret = 0;
+       void __user *argp = (void __user *)arg;
+       u8 val8;
+       u16 val16;
+       int value;
+
+       mutex_lock(&spic_dev.lock);
+       switch (cmd) {
+       case SONYPI_IOCGBRT:
+               if (sony_backlight_device == NULL) {
+                       ret = -EIO;
+                       break;
+               }
+               if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) {
+                       ret = -EIO;
+                       break;
+               }
+               val8 = ((value & 0xff) - 1) << 5;
+               if (copy_to_user(argp, &val8, sizeof(val8)))
+                               ret = -EFAULT;
+               break;
+       case SONYPI_IOCSBRT:
+               if (sony_backlight_device == NULL) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_from_user(&val8, argp, sizeof(val8))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
+                               (val8 >> 5) + 1, NULL)) {
+                       ret = -EIO;
+                       break;
+               }
+               /* sync the backlight device status */
+               sony_backlight_device->props.brightness =
+                   sony_backlight_get_brightness(sony_backlight_device);
+               break;
+       case SONYPI_IOCGBAT1CAP:
+               if (ec_read16(SONYPI_BAT1_FULL, &val16)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val16, sizeof(val16)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCGBAT1REM:
+               if (ec_read16(SONYPI_BAT1_LEFT, &val16)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val16, sizeof(val16)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCGBAT2CAP:
+               if (ec_read16(SONYPI_BAT2_FULL, &val16)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val16, sizeof(val16)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCGBAT2REM:
+               if (ec_read16(SONYPI_BAT2_LEFT, &val16)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val16, sizeof(val16)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCGBATFLAGS:
+               if (ec_read(SONYPI_BAT_FLAGS, &val8)) {
+                       ret = -EIO;
+                       break;
+               }
+               val8 &= 0x07;
+               if (copy_to_user(argp, &val8, sizeof(val8)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCGBLUE:
+               val8 = spic_dev.bluetooth_power;
+               if (copy_to_user(argp, &val8, sizeof(val8)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCSBLUE:
+               if (copy_from_user(&val8, argp, sizeof(val8))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               __sony_pic_set_bluetoothpower(val8);
+               break;
+       /* FAN Controls */
+       case SONYPI_IOCGFAN:
+               if (sony_pic_get_fanspeed(&val8)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val8, sizeof(val8)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCSFAN:
+               if (copy_from_user(&val8, argp, sizeof(val8))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               if (sony_pic_set_fanspeed(val8))
+                       ret = -EIO;
+               break;
+       /* GET Temperature (useful under APM) */
+       case SONYPI_IOCGTEMP:
+               if (ec_read(SONYPI_TEMP_STATUS, &val8)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val8, sizeof(val8)))
+                       ret = -EFAULT;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       mutex_unlock(&spic_dev.lock);
+       return ret;
+}
+
+static const struct file_operations sonypi_misc_fops = {
+       .owner          = THIS_MODULE,
+       .read           = sonypi_misc_read,
+       .poll           = sonypi_misc_poll,
+       .open           = sonypi_misc_open,
+       .release        = sonypi_misc_release,
+       .fasync         = sonypi_misc_fasync,
+       .ioctl          = sonypi_misc_ioctl,
+};
+
+static struct miscdevice sonypi_misc_device = {
+       .minor          = MISC_DYNAMIC_MINOR,
+       .name           = "sonypi",
+       .fops           = &sonypi_misc_fops,
+};
+
+static void sonypi_compat_report_event(u8 event)
+{
+       kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event));
+       kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN);
+       wake_up_interruptible(&sonypi_compat.fifo_proc_list);
+}
+
+static int sonypi_compat_init(void)
+{
+       int error;
+
+       spin_lock_init(&sonypi_compat.fifo_lock);
+       sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
+                                        &sonypi_compat.fifo_lock);
+       if (IS_ERR(sonypi_compat.fifo)) {
+               printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
+               return PTR_ERR(sonypi_compat.fifo);
+       }
+
+       init_waitqueue_head(&sonypi_compat.fifo_proc_list);
+
+       if (minor != -1)
+               sonypi_misc_device.minor = minor;
+       error = misc_register(&sonypi_misc_device);
+       if (error) {
+               printk(KERN_ERR DRV_PFX "misc_register failed\n");
+               goto err_free_kfifo;
+       }
+       if (minor == -1)
+               printk(KERN_INFO DRV_PFX "device allocated minor is %d\n",
+                      sonypi_misc_device.minor);
+
+       return 0;
+
+err_free_kfifo:
+       kfifo_free(sonypi_compat.fifo);
+       return error;
+}
+
+static void sonypi_compat_exit(void)
+{
+       misc_deregister(&sonypi_misc_device);
+       kfifo_free(sonypi_compat.fifo);
+}
+#else
+static int sonypi_compat_init(void) { return 0; }
+static void sonypi_compat_exit(void) { }
+static void sonypi_compat_report_event(u8 event) { }
+#endif /* CONFIG_SONY_LAPTOP_OLD */
+
+/*
+ * ACPI callbacks
+ */
+static acpi_status
+sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
+{
+       u32 i;
+       struct sony_pic_dev *dev = (struct sony_pic_dev *)context;
+
+       switch (resource->type) {
+       case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+       case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+               return AE_OK;
+
+       case ACPI_RESOURCE_TYPE_IRQ:
+               {
+                       struct acpi_resource_irq *p = &resource->data.irq;
+                       struct sony_pic_irq *interrupt = NULL;
+                       if (!p || !p->interrupt_count) {
+                               /*
+                                * IRQ descriptors may have no IRQ# bits set,
+                                * particularly those those w/ _STA disabled
+                                */
+                               dprintk("Blank IRQ resource\n");
+                               return AE_OK;
+                       }
+                       for (i = 0; i < p->interrupt_count; i++) {
+                               if (!p->interrupts[i]) {
+                                       printk(KERN_WARNING DRV_PFX
+                                                       "Invalid IRQ %d\n",
+                                                       p->interrupts[i]);
+                                       continue;
+                               }
+                               interrupt = kzalloc(sizeof(*interrupt),
+                                               GFP_KERNEL);
+                               if (!interrupt)
+                                       return AE_ERROR;
+
+                               list_add_tail(&interrupt->list, &dev->interrupts);
+                               interrupt->irq.triggering = p->triggering;
+                               interrupt->irq.polarity = p->polarity;
+                               interrupt->irq.sharable = p->sharable;
+                               interrupt->irq.interrupt_count = 1;
+                               interrupt->irq.interrupts[0] = p->interrupts[i];
+                       }
+                       return AE_OK;
+               }
+       case ACPI_RESOURCE_TYPE_IO:
+               {
+                       struct acpi_resource_io *io = &resource->data.io;
+                       struct sony_pic_ioport *ioport = NULL;
+                       if (!io) {
+                               dprintk("Blank IO resource\n");
+                               return AE_OK;
+                       }
+
+                       ioport = kzalloc(sizeof(*ioport), GFP_KERNEL);
+                       if (!ioport)
+                               return AE_ERROR;
+
+                       list_add_tail(&ioport->list, &dev->ioports);
+                       memcpy(&ioport->io, io, sizeof(*io));
+                       return AE_OK;
+               }
+       default:
+               dprintk("Resource %d isn't an IRQ nor an IO port\n",
+                               resource->type);
+
+       case ACPI_RESOURCE_TYPE_END_TAG:
+               return AE_OK;
+       }
+       return AE_CTRL_TERMINATE;
+}
+
+static int sony_pic_possible_resources(struct acpi_device *device)
+{
+       int result = 0;
+       acpi_status status = AE_OK;
+
+       if (!device)
+               return -EINVAL;
+
+       /* get device status */
+       /* see acpi_pci_link_get_current acpi_pci_link_get_possible */
+       dprintk("Evaluating _STA\n");
+       result = acpi_bus_get_status(device);
+       if (result) {
+               printk(KERN_WARNING DRV_PFX "Unable to read status\n");
+               goto end;
+       }
+
+       if (!device->status.enabled)
+               dprintk("Device disabled\n");
+       else
+               dprintk("Device enabled\n");
+
+       /*
+        * Query and parse 'method'
+        */
+       dprintk("Evaluating %s\n", METHOD_NAME__PRS);
+       status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,
+                       sony_pic_read_possible_resource, &spic_dev);
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_WARNING DRV_PFX
+                               "Failure evaluating %s\n",
+                               METHOD_NAME__PRS);
+               result = -ENODEV;
+       }
+end:
+       return result;
+}
+
+/*
+ *  Disable the spic device by calling its _DIS method
+ */
+static int sony_pic_disable(struct acpi_device *device)
+{
+       if (ACPI_FAILURE(acpi_evaluate_object(device->handle, "_DIS", 0, NULL)))
+               return -ENXIO;
+
+       dprintk("Device disabled\n");
+       return 0;
+}
+
+
+/*
+ *  Based on drivers/acpi/pci_link.c:acpi_pci_link_set
+ *
+ *  Call _SRS to set current resources
+ */
+static int sony_pic_enable(struct acpi_device *device,
+               struct sony_pic_ioport *ioport, struct sony_pic_irq *irq)
+{
+       acpi_status status;
+       int result = 0;
+       struct {
+               struct acpi_resource io_res;
+               struct acpi_resource irq_res;
+               struct acpi_resource end;
+       } *resource;
+       struct acpi_buffer buffer = { 0, NULL };
+
+       if (!ioport || !irq)
+               return -EINVAL;
+
+       /* init acpi_buffer */
+       resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL);
+       if (!resource)
+               return -ENOMEM;
+
+       buffer.length = sizeof(*resource) + 1;
+       buffer.pointer = resource;
+
+       /* setup io resource */
+       resource->io_res.type = ACPI_RESOURCE_TYPE_IO;
+       resource->io_res.length = sizeof(struct acpi_resource);
+       memcpy(&resource->io_res.data.io, &ioport->io,
+                       sizeof(struct acpi_resource_io));
+
+       /* setup irq resource */
+       resource->irq_res.type = ACPI_RESOURCE_TYPE_IRQ;
+       resource->irq_res.length = sizeof(struct acpi_resource);
+       memcpy(&resource->irq_res.data.irq, &irq->irq,
+                       sizeof(struct acpi_resource_irq));
+       /* we requested a shared irq */
+       resource->irq_res.data.irq.sharable = ACPI_SHARED;
+
+       resource->end.type = ACPI_RESOURCE_TYPE_END_TAG;
+
+       /* Attempt to set the resource */
+       dprintk("Evaluating _SRS\n");
+       status = acpi_set_current_resources(device->handle, &buffer);
+
+       /* check for total failure */
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR DRV_PFX "Error evaluating _SRS");
+               result = -ENODEV;
+               goto end;
+       }
+
+       /* Necessary device initializations calls (from sonypi) */
+       sony_pic_call1(0x82);
+       sony_pic_call2(0x81, 0xff);
+       sony_pic_call1(compat ? 0x92 : 0x82);
+
+end:
+       kfree(resource);
+       return result;
+}
+
+/*****************
+ *
+ * ISR: some event is available
+ *
+ *****************/
+static irqreturn_t sony_pic_irq(int irq, void *dev_id)
+{
+       int i, j;
+       u32 port_val = 0;
+       u8 ev = 0;
+       u8 data_mask = 0;
+       u8 device_event = 0;
+
+       struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id;
+
+       acpi_os_read_port(dev->cur_ioport->io.minimum, &port_val,
+                       dev->cur_ioport->io.address_length);
+       ev = port_val & SONY_PIC_EV_MASK;
+       data_mask = 0xff & (port_val >> (dev->cur_ioport->io.address_length - 8));
+
+       dprintk("event (0x%.8x [%.2x] [%.2x]) at port 0x%.4x\n",
+                       port_val, ev, data_mask, dev->cur_ioport->io.minimum);
+
+       if (ev == 0x00 || ev == 0xff)
+               return IRQ_HANDLED;
+
+       for (i = 0; sony_pic_eventtypes[i].model; i++) {
+
+               if (spic_dev.model != sony_pic_eventtypes[i].model)
+                       continue;
+
+               if ((data_mask & sony_pic_eventtypes[i].data) !=
+                   sony_pic_eventtypes[i].data)
+                       continue;
+
+               if (!(mask & sony_pic_eventtypes[i].mask))
+                       continue;
+
+               for (j = 0; sony_pic_eventtypes[i].events[j].event; j++) {
+                       if (ev == sony_pic_eventtypes[i].events[j].data) {
+                               device_event =
+                                       sony_pic_eventtypes[i].events[j].event;
+                               goto found;
+                       }
+               }
+       }
+       return IRQ_HANDLED;
+
+found:
+       sony_laptop_report_input_event(device_event);
+       acpi_bus_generate_event(spic_dev.acpi_dev, 1, device_event);
+       sonypi_compat_report_event(device_event);
+
+       return IRQ_HANDLED;
+}
+
+/*****************
+ *
+ *  ACPI driver
+ *
+ *****************/
+static int sony_pic_remove(struct acpi_device *device, int type)
+{
+       struct sony_pic_ioport *io, *tmp_io;
+       struct sony_pic_irq *irq, *tmp_irq;
+
+       sonypi_compat_exit();
+
+       if (sony_pic_disable(device)) {
+               printk(KERN_ERR DRV_PFX "Couldn't disable device.\n");
+               return -ENXIO;
+       }
+
+       free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
+       release_region(spic_dev.cur_ioport->io.minimum,
+                       spic_dev.cur_ioport->io.address_length);
+
+       sony_laptop_remove_input();
+
+       /* pf attrs */
+       sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
+       sony_pf_remove();
+
+       list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
+               list_del(&io->list);
+               kfree(io);
+       }
+       list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
+               list_del(&irq->list);
+               kfree(irq);
+       }
+       spic_dev.cur_ioport = NULL;
+       spic_dev.cur_irq = NULL;
+
+       dprintk(SONY_PIC_DRIVER_NAME " removed.\n");
+       return 0;
+}
+
+static int sony_pic_add(struct acpi_device *device)
+{
+       int result;
+       struct sony_pic_ioport *io, *tmp_io;
+       struct sony_pic_irq *irq, *tmp_irq;
+
+       printk(KERN_INFO DRV_PFX "%s v%s.\n",
+               SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
+
+       spic_dev.acpi_dev = device;
+       strcpy(acpi_device_class(device), "sony/hotkey");
+       spic_dev.model = sony_pic_detect_device_type();
+       mutex_init(&spic_dev.lock);
+
+       /* read _PRS resources */
+       result = sony_pic_possible_resources(device);
+       if (result) {
+               printk(KERN_ERR DRV_PFX
+                               "Unabe to read possible resources.\n");
+               goto err_free_resources;
+       }
+
+       /* setup input devices and helper fifo */
+       result = sony_laptop_setup_input();
+       if (result) {
+               printk(KERN_ERR DRV_PFX
+                               "Unabe to create input devices.\n");
+               goto err_free_resources;
+       }
+
+       /* request io port */
+       list_for_each_entry(io, &spic_dev.ioports, list) {
+               if (request_region(io->io.minimum, io->io.address_length,
+                                       "Sony Programable I/O Device")) {
+                       dprintk("I/O port: 0x%.4x (0x%.4x) + 0x%.2x\n",
+                                       io->io.minimum, io->io.maximum,
+                                       io->io.address_length);
+                       spic_dev.cur_ioport = io;
+                       break;
+               }
+       }
+       if (!spic_dev.cur_ioport) {
+               printk(KERN_ERR DRV_PFX "Failed to request_region.\n");
+               result = -ENODEV;
+               goto err_remove_input;
+       }
+
+       /* request IRQ */
+       list_for_each_entry(irq, &spic_dev.interrupts, list) {
+               if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
+                                       IRQF_SHARED, "sony-laptop", &spic_dev)) {
+                       dprintk("IRQ: %d - triggering: %d - "
+                                       "polarity: %d - shr: %d\n",
+                                       irq->irq.interrupts[0],
+                                       irq->irq.triggering,
+                                       irq->irq.polarity,
+                                       irq->irq.sharable);
+                       spic_dev.cur_irq = irq;
+                       break;
+               }
+       }
+       if (!spic_dev.cur_irq) {
+               printk(KERN_ERR DRV_PFX "Failed to request_irq.\n");
+               result = -ENODEV;
+               goto err_release_region;
+       }
+
+       /* set resource status _SRS */
+       result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
+       if (result) {
+               printk(KERN_ERR DRV_PFX "Couldn't enable device.\n");
+               goto err_free_irq;
+       }
+
+       spic_dev.bluetooth_power = -1;
+       /* create device attributes */
+       result = sony_pf_add();
+       if (result)
+               goto err_disable_device;
+
+       result = sysfs_create_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
+       if (result)
+               goto err_remove_pf;
+
+       if (sonypi_compat_init())
+               goto err_remove_pf;
+
+       return 0;
+
+err_remove_pf:
+       sony_pf_remove();
+
+err_disable_device:
+       sony_pic_disable(device);
+
+err_free_irq:
+       free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
+
+err_release_region:
+       release_region(spic_dev.cur_ioport->io.minimum,
+                       spic_dev.cur_ioport->io.address_length);
+
+err_remove_input:
+       sony_laptop_remove_input();
+
+err_free_resources:
+       list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
+               list_del(&io->list);
+               kfree(io);
+       }
+       list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
+               list_del(&irq->list);
+               kfree(irq);
+       }
+       spic_dev.cur_ioport = NULL;
+       spic_dev.cur_irq = NULL;
+
+       return result;
+}
+
+static int sony_pic_suspend(struct acpi_device *device, pm_message_t state)
+{
+       if (sony_pic_disable(device))
+               return -ENXIO;
+       return 0;
+}
+
+static int sony_pic_resume(struct acpi_device *device)
+{
+       sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
+       return 0;
+}
+
+static struct acpi_driver sony_pic_driver = {
+       .name = SONY_PIC_DRIVER_NAME,
+       .class = SONY_PIC_CLASS,
+       .ids = SONY_PIC_HID,
+       .owner = THIS_MODULE,
+       .ops = {
+               .add = sony_pic_add,
+               .remove = sony_pic_remove,
+               .suspend = sony_pic_suspend,
+               .resume = sony_pic_resume,
+               },
+};
+
+static struct dmi_system_id __initdata sonypi_dmi_table[] = {
+       {
+               .ident = "Sony Vaio",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"),
+               },
+       },
+       {
+               .ident = "Sony Vaio",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"),
+               },
+       },
+       { }
+};
+
+static int __init sony_laptop_init(void)
+{
+       int result;
+
+       if (!no_spic && dmi_check_system(sonypi_dmi_table)) {
+               result = acpi_bus_register_driver(&sony_pic_driver);
+               if (result) {
+                       printk(KERN_ERR DRV_PFX
+                                       "Unable to register SPIC driver.");
+                       goto out;
+               }
+       }
+
+       result = acpi_bus_register_driver(&sony_nc_driver);
+       if (result) {
+               printk(KERN_ERR DRV_PFX "Unable to register SNC driver.");
+               goto out_unregister_pic;
+       }
+
+       return 0;
+
+out_unregister_pic:
+       if (!no_spic)
+               acpi_bus_unregister_driver(&sony_pic_driver);
+out:
+       return result;
 }
 
-static void __exit sony_acpi_exit(void)
+static void __exit sony_laptop_exit(void)
 {
-       acpi_bus_unregister_driver(&sony_acpi_driver);
+       acpi_bus_unregister_driver(&sony_nc_driver);
+       if (!no_spic)
+               acpi_bus_unregister_driver(&sony_pic_driver);
 }
 
-module_init(sony_acpi_init);
-module_exit(sony_acpi_exit);
+module_init(sony_laptop_init);
+module_exit(sony_laptop_exit);
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
new file mode 100644 (file)
index 0000000..6c36a55
--- /dev/null
@@ -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, &params, 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 (file)
index 0000000..440145a
--- /dev/null
@@ -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 */
index a064f36a0805d605e3e3edbf5ed87be5c7ee3819..b5ac810404c0f19dc98318dfe8dde6212d04c2bb 100644 (file)
@@ -317,6 +317,10 @@ static int __init acpi_pci_init(void)
 {
        int ret;
 
+       if (acpi_gbl_FADT.boot_flags & BAF_MSI_NOT_SUPPORTED) {
+               printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n");
+               pci_no_msi();
+       }
        ret = register_acpi_bus_type(&acpi_pci_bus);
        if (ret)
                return 0;
index 65d6f23ead4151ae6953477f6eeed653df0cdd06..3411483240cd7166db36d84c8760b18c67b0cf59 100644 (file)
@@ -1303,119 +1303,6 @@ static void __init quirk_alder_ioapic(struct pci_dev *pdev)
 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);
 
index 95045e33710d488b758374d17956f0f4074b837a..e9bd29975db4361913161ee5b1d24b88ff5c0ef9 100644 (file)
@@ -3770,7 +3770,8 @@ static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg,
  * 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;
index 09469e7db6a542e6ae7ebbc8d1125b9d5e1c59e7..955adfb8d64c2358d0c7f449a51adc93ecd964bd 100644 (file)
@@ -276,6 +276,7 @@ enum acpi_prefered_pm_profiles {
 
 #define BAF_LEGACY_DEVICES              0x0001
 #define BAF_8042_KEYBOARD_CONTROLLER    0x0002
+#define BAF_MSI_NOT_SUPPORTED           0x0008
 
 #define FADT2_REVISION_ID               3
 #define FADT2_MINUS_REVISION_ID         2
index 6caeb98e29ddd942c6defabc3eace48702f63b52..edb31bfff68fb3586097173f5dc61b7b215333ab 100644 (file)
@@ -159,11 +159,19 @@ enum {
        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 @@ enum {
        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:
index 6859a3b14088459fef9de4e373f36ea0009a0f3a..71ea92319241ef55579609a081a6ff60bc403bf8 100644 (file)
@@ -99,7 +99,6 @@ extern struct resource ioport_resource;
 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,
index 0cfbcb6f08eb79a1841ddb7cc7ccabc7370188d2..d8cfc72ea9c110937161c910813a5f59d447b3b6 100644 (file)
@@ -210,6 +210,7 @@ enum {
 
        /* 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 @@ enum {
        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 @@ struct ata_ioports {
        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 @@ struct ata_queued_cmd {
        int                     dma_dir;
 
        unsigned int            pad_len;
+       unsigned int            sect_size;
 
        unsigned int            nbytes;
        unsigned int            curbytes;
@@ -472,6 +448,7 @@ struct ata_device {
        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 {
 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 @@ struct ata_port_operations {
 
        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 @@ struct ata_port_info {
        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_hotplug[];
 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 @@ static inline int ata_port_is_dummy(struct ata_port *ap)
        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 @@ extern int ata_pci_device_resume(struct pci_dev *pdev);
 #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 void ata_scsi_slave_destroy(struct scsi_device *sdev);
 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 @@ struct pci_bits {
        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 @@ static inline void ata_qc_reinit(struct ata_queued_cmd *qc)
        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 void ata_pad_free(struct ata_port *ap, struct device *dev)
 
 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__ */
index a3ad76221c6ff23d2e6b3c07d874f37aeac79354..972491089ac9622884a4cfb1a4c6d5861754a6c2 100644 (file)
@@ -838,6 +838,7 @@ void __iomem * pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen);
 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 */
index 247b5e63f6c2a686037709dc3b0dea04c6892a1e..1b0ddbb8a8045ea85fc8fb028b81919dc7ca6baf 100644 (file)
 #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 (file)
index 0000000..e2e036d
--- /dev/null
@@ -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_ */
index bdb55a33f9691e2f79765eeba502d075f71d067c..9bd14fd3e6de5bcb5be171a8d3e8772a3a0cf91a 100644 (file)
@@ -212,27 +212,6 @@ int request_resource(struct resource *root, struct resource *new)
 
 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
index eb38849aa71702445e7e8e42744ad384fe512f48..b1d336ce7f3d0c34f694844c8bd7dfd0f1dc687d 100644 (file)
@@ -296,5 +296,31 @@ int pcim_iomap_regions(struct pci_dev *pdev, u16 mask, const char *name)
        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