Prechádzať zdrojové kódy

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

* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6: (77 commits)
  ACPI: Populate /sys/firmware/acpi/tables/
  ACPI: create CONFIG_ACPI_DEBUG_FUNC_TRACE
  ACPI: update ACPI proc I/F removal schedule
  ACPI: update feature-removal-schedule.txt, /sys/firmware/acpi/namespace is gone
  ACPI: export ACPI events via acpi_mc_group multicast group
  ACPI: fix empty macros found by -Wextra
  ACPI: drivers/acpi/pci_link.c: lower printk severity
  sony-laptop: Fix event reading in sony-laptop
  sony-laptop: Add Vaio FE to the special init sequence
  sony-laptop: Make the driver use MSC_SCAN and a setkeycode and getkeycode key table.
  sony-laptop: Invoke _INI for SNC devices that provide it
  sony-laptop: Add support for recent Vaios Fn keys (C series for now)
  sony-laptop: map wireless switch events to KEY_WLAN
  sony-laptop: add new SNC handlers
  ACPI: thinkpad-acpi: add locking to brightness subdriver
  ACPI: thinkpad-acpi: bump up version to 0.15
  ACPI: thinkpad-acpi: make EC-based thermal readings non-experimental
  ACPI: thinkpad-acpi: make sure DSDT TMPx readings don't return +128
  ACPI: thinkpad-acpi: react to Lenovo ThinkPad differences in hot key
  ACPI: thinkpad-acpi: allow use of CMOS NVRAM for brightness control
  ...
Linus Torvalds 18 rokov pred
rodič
commit
39804b20f6

+ 4 - 17
Documentation/feature-removal-schedule.txt

@@ -180,24 +180,11 @@ Who:   Adrian Bunk <bunk@stusta.de>
 
 ---------------------------
 
-What:	/sys/firmware/acpi/namespace
-When:	2.6.21
-Why:	The ACPI namespace is effectively the symbol list for
-	the BIOS.  The device names are completely arbitrary
-	and have no place being exposed to user-space.
-
-	For those interested in the BIOS ACPI namespace,
-	the BIOS can be extracted and disassembled with acpidump
-	and iasl as documented in the pmtools package here:
-	http://ftp.kernel.org/pub/linux/kernel/people/lenb/acpi/utils
-Who:	Len Brown <len.brown@intel.com>
-
----------------------------
-
 What:	ACPI procfs interface
-When:	July 2007
-Why:	After ACPI sysfs conversion, ACPI attributes will be duplicated
-	in sysfs and the ACPI procfs interface should be removed.
+When:	July 2008
+Why:	ACPI sysfs conversion should be finished by January 2008.
+	ACPI procfs interface will be removed in July 2008 so that
+	there is enough time for the user space to catch up.
 Who:	Zhang Rui <rui.zhang@intel.com>
 
 ---------------------------

+ 276 - 77
Documentation/thinkpad-acpi.txt

@@ -1,11 +1,11 @@
 		     ThinkPad ACPI Extras Driver
 
-                            Version 0.14
-                          April 21st, 2007
+                            Version 0.15
+                           July 1st, 2007
 
                Borislav Deianov <borislav@users.sf.net>
-	     Henrique de Moraes Holschuh <hmh@hmh.eng.br>
-		      http://ibm-acpi.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
@@ -134,54 +134,68 @@ 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.
 
+Changes to the thinkpad-acpi sysfs interface are only considered
+non-experimental when they are submitted to Linux mainline, at which
+point the changes in this interface are documented and interface_version
+may be updated.  If you are using any thinkpad-acpi features not yet
+sent to mainline for merging, you do so on your own risk: these features
+may disappear, or be implemented in a different and incompatible way by
+the time they are merged in Linux mainline.
+
+Changes that are backwards-compatible by nature (e.g. the addition of
+attributes that do not change the way the other attributes work) do not
+always warrant an update of interface_version.  Therefore, one must
+expect that an attribute might not be there, and deal with it properly
+(an attribute not being there *is* a valid way to make it clear that a
+feature is not available in sysfs).
+
 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
+In a ThinkPad, the ACPI HKEY handler is responsible for comunicating
+some important events and also keyboard hot key presses to the operating
+system.  Enabling the hotkey functionality of thinkpad-acpi signals the
+firmware that such a driver is present, and modifies how the ThinkPad
+firmware will behave in many situations.
+
+When the hotkey feature is enabled and the hot key mask is set (see
+below), the various hot keys either 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/
+or events over the input layer.  The input layer support accepts the
+standard IOCTLs to remap the keycodes assigned to each hotkey.
+
+When the input device is open, the driver will suppress any ACPI hot key
+events that get translated into a meaningful input layer event, in order
+to avoid sending duplicate events to userspace.  Hot keys that are
+mapped to KEY_RESERVED in the keymap are not translated, and will always
+generate an ACPI ibm/hotkey HKEY event, and no input layer events.
+
+The hot key bit mask allows some control over which hot keys generate
+events.  If a key is "masked" (bit set to 0 in the mask), the firmware
+will handle it.  If it is "unmasked", it signals the firmware that
+thinkpad-acpi would prefer to handle it, if the firmware would be so
+kind to allow it (and it often doesn't!).
+
+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.  Some models do not support the mask at all, and in those
+models, hot keys cannot be controlled individually.  The behaviour of
+the mask is, therefore, higly dependent on the ThinkPad model.
+
+Note that unmasking some keys prevents their default behavior.  For
+example, if Fn+F5 is unmasked, that key will no longer enable/disable
+Bluetooth by itself.
+
+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:
 
@@ -189,9 +203,9 @@ 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 0xffffffff > /proc/acpi/ibm/hotkey -- enable all hot keys
+	echo 0 > /proc/acpi/ibm/hotkey -- disable all possible hot keys
+	... any other 8-hex-digit mask ...
 	echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
 
 sysfs notes:
@@ -202,7 +216,7 @@ sysfs notes:
 		key feature status will be restored to this value.
 
 		0: hot keys were disabled
-		1: hot keys were enabled
+		1: hot keys were enabled (unusual)
 
 	hotkey_bios_mask:
 		Returns the hot keys mask when thinkpad-acpi was loaded.
@@ -217,9 +231,182 @@ sysfs notes:
 		1: enables the hot keys feature / feature enabled
 
 	hotkey_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.
+		bit mask to enable driver-handling and ACPI event
+		generation for each hot key (see above).  Returns the
+		current status of the hot keys mask, and allows one to
+		modify it.
+
+	hotkey_all_mask:
+		bit mask that should enable event reporting for all
+		supported hot keys, when echoed to hotkey_mask above.
+		Unless you know which events need to be handled
+		passively (because the firmware *will* handle them
+		anyway), do *not* use hotkey_all_mask.  Use
+		hotkey_recommended_mask, instead. You have been warned.
+
+	hotkey_recommended_mask:
+		bit mask that should enable event reporting for all
+		supported hot keys, except those which are always
+		handled by the firmware anyway.  Echo it to
+		hotkey_mask above, to use.
+
+	hotkey_radio_sw:
+		if the ThinkPad has a hardware radio switch, this
+		attribute will read 0 if the switch is in the "radios
+		disabled" postition, and 1 if the switch is in the
+		"radios enabled" position.
+
+input layer notes:
+
+A Hot key is mapped to a single input layer EV_KEY event, possibly
+followed by an EV_MSC MSC_SCAN event that shall contain that key's scan
+code.  An EV_SYN event will always be generated to mark the end of the
+event block.
+
+Do not use the EV_MSC MSC_SCAN events to process keys.  They are to be
+used as a helper to remap keys, only.  They are particularly useful when
+remapping KEY_UNKNOWN keys.
+
+The events are available in an input device, with the following id:
+
+	Bus:		BUS_HOST
+	vendor:		0x1014 (PCI_VENDOR_ID_IBM)  or
+			0x17aa (PCI_VENDOR_ID_LENOVO)
+	product:	0x5054 ("TP")
+	version:	0x4101
+
+The version will have its LSB incremented if the keymap changes in a
+backwards-compatible way.  The MSB shall always be 0x41 for this input
+device.  If the MSB is not 0x41, do not use the device as described in
+this section, as it is either something else (e.g. another input device
+exported by a thinkpad driver, such as HDAPS) or its functionality has
+been changed in a non-backwards compatible way.
+
+Adding other event types for other functionalities shall be considered a
+backwards-compatible change for this input device.
+
+Thinkpad-acpi Hot Key event map (version 0x4101):
+
+ACPI	Scan
+event	code	Key		Notes
+
+0x1001	0x00	FN+F1		-
+0x1002	0x01	FN+F2		IBM: battery (rare)
+				Lenovo: Screen lock
+
+0x1003	0x02	FN+F3		Many IBM models always report
+				this hot key, even with hot keys
+				disabled or with Fn+F3 masked
+				off
+				IBM: screen lock
+				Lenovo: battery
+
+0x1004	0x03	FN+F4		Sleep button (ACPI sleep button
+				semanthics, i.e. sleep-to-RAM).
+				It is always generate some kind
+				of event, either the hot key
+				event or a ACPI sleep button
+				event. The firmware may
+				refuse to generate further FN+F4
+				key presses until a S3 or S4 ACPI
+				sleep cycle is performed or some
+				time passes.
+
+0x1005	0x04	FN+F5		Radio.  Enables/disables
+				the internal BlueTooth hardware
+				and W-WAN card if left in control
+				of the firmware.  Does not affect
+				the WLAN card.
+				Should be used to turn on/off all
+				radios (bluetooth+W-WAN+WLAN),
+				really.
+
+0x1006	0x05	FN+F6		-
+
+0x1007	0x06	FN+F7		Video output cycle.
+				Do you feel lucky today?
+
+0x1008	0x07	FN+F8		IBM: toggle screen expand
+				Lenovo: configure ultranav
+
+0x1009	0x08	FN+F9		-
+	..	..		..
+0x100B	0x0A	FN+F11		-
+
+0x100C	0x0B	FN+F12		Sleep to disk.  You are always
+				supposed to handle it yourself,
+				either through the ACPI event,
+				or through a hotkey event.
+				The firmware may refuse to
+				generate further FN+F4 key
+				press events until a S3 or S4
+				ACPI sleep cycle is performed,
+				or some time passes.
+
+0x100D	0x0C	FN+BACKSPACE	-
+0x100E	0x0D	FN+INSERT	-
+0x100F	0x0E	FN+DELETE	-
+
+0x1010	0x0F	FN+HOME		Brightness up.  This key is
+				always handled by the firmware
+				in IBM ThinkPads, even when
+				unmasked.  Just leave it alone.
+				For Lenovo ThinkPads with a new
+				BIOS, it has to be handled either
+				by the ACPI OSI, or by userspace.
+0x1011	0x10	FN+END		Brightness down.  See brightness
+				up for details.
+
+0x1012	0x11	FN+PGUP		Thinklight toggle.  This key is
+				always handled by the firmware,
+				even when unmasked.
+
+0x1013	0x12	FN+PGDOWN	-
+
+0x1014	0x13	FN+SPACE	Zoom key
+
+0x1015	0x14	VOLUME UP	Internal mixer volume up. This
+				key is always handled by the
+				firmware, even when unmasked.
+				NOTE: Lenovo seems to be changing
+				this.
+0x1016	0x15	VOLUME DOWN	Internal mixer volume up. This
+				key is always handled by the
+				firmware, even when unmasked.
+				NOTE: Lenovo seems to be changing
+				this.
+0x1017	0x16	MUTE		Mute internal mixer. This
+				key is always handled by the
+				firmware, even when unmasked.
+
+0x1018	0x17	THINKPAD	Thinkpad/Access IBM/Lenovo key
+
+0x1019	0x18	unknown
+..	..	..
+0x1020	0x1F	unknown
+
+The ThinkPad firmware does not allow one to differentiate when most hot
+keys are pressed or released (either that, or we don't know how to, yet).
+For these keys, the driver generates a set of events for a key press and
+immediately issues the same set of events for a key release.  It is
+unknown by the driver if the ThinkPad firmware triggered these events on
+hot key press or release, but the firmware will do it for either one, not
+both.
+
+If a key is mapped to KEY_RESERVED, it generates no input events at all,
+and it may generate a legacy thinkpad-acpi ACPI hotkey event.
+
+If a key is mapped to KEY_UNKNOWN, it generates an input event that
+includes an scan code, and it may also generate a legacy thinkpad-acpi
+ACPI hotkey event.
+
+If a key is mapped to anything else, it will only generate legacy
+thinkpad-acpi ACPI hotkey events if nobody has opened the input device.
+
+Non hot-key ACPI HKEY event map:
+0x5001		Lid closed
+0x5002		Lid opened
+0x7000		Radio Switch may have changed state
 
 
 Bluetooth
@@ -437,27 +624,34 @@ 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.
+This feature is mostly used internally by the ACPI firmware to keep the legacy
+CMOS NVRAM bits in sync with the current machine state, and to record this
+state so that the ThinkPad will retain such settings across reboots.
+
+Some of these commands actually perform actions in some ThinkPad models, but
+this is expected to disappear more and more in newer models.  As an example, in
+a T43 and in a X40, commands 12 and 13 still control the ThinkLight state for
+real, but commands 0 to 2 don't control the mixer anymore (they have been
+phased out) and just update the NVRAM.
 
 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
+	0 - Related to "Volume down" key press
+	1 - Related to "Volume up" key press
+	2 - Related to "Mute on" key press
+	3 - Related to "Access IBM" key press
+	4 - Related to "LCD brightness up" key pess
+	5 - Related to "LCD brightness down" key press
+	11 - Related to "toggle screen expansion" key press/function
+	12 - Related to "ThinkLight on"
+	13 - Related to "ThinkLight off"
+	14 - Related to "ThinkLight" key press (toggle thinklight)
 
 The cmos command interface is prone to firmware split-brain problems, as
-in newer ThinkPads it is just a compatibility layer.
+in newer ThinkPads it is just a compatibility layer.  Do not use it, it is
+exported just as a debug tool.
 
 LED control -- /proc/acpi/ibm/led
 ---------------------------------
@@ -516,23 +710,15 @@ 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.
+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 up to sixteen different sensors on newer ThinkPads.
 
 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:
+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
@@ -562,7 +748,8 @@ 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
+10: Clock-generator, mini-pci card and 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
@@ -681,6 +868,12 @@ cannot be controlled.
 The backlight control has eight levels, ranging from 0 to 7.  Some of the
 levels may not be distinct.
 
+There are two interfaces to the firmware for brightness control, EC and CMOS.
+To select which one should be used, use the brightness_mode module parameter:
+brightness_mode=1 selects EC mode, brightness_mode=2 selects CMOS mode,
+brightness_mode=3 selects both EC and CMOS.  The driver tries to autodetect
+which interface to use.
+
 Procfs notes:
 
 	The available commands are:
@@ -976,3 +1169,9 @@ Sysfs interface changelog:
 
 0x000100:	Initial sysfs support, as a single platform driver and
 		device.
+0x000200:	Hot key support for 32 hot keys, and radio slider switch
+		support.
+0x010000:	Hot keys are now handled by default over the input
+		layer, the radio switch generates input event EV_RADIO,
+		and the driver enables hot key handling by default in
+		the firmware.

+ 3 - 3
MAINTAINERS

@@ -225,15 +225,15 @@ T:	git kernel.org:/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6.git
 S:	Supported
 
 ACPI BATTERY DRIVERS
-P:	Vladimir P. Lebedev
-M:	vladimir.p.lebedev@intel.com
+P:	Alexey Starikovskiy
+M:	astarikovskiy@suse.de
 L:	linux-acpi@vger.kernel.org
 W:	http://acpi.sourceforge.net/
 S:	Supported
 
 ACPI EC DRIVER
 P:	Alexey Starikovskiy
-M:	alexey.y.starikovskiy@linux.intel.com
+M:	astarikovskiy@suse.de
 L:	linux-acpi@vger.kernel.org
 W:	http://acpi.sourceforge.net/
 S:	Supported

+ 0 - 8
arch/i386/kernel/acpi/boot.c

@@ -984,14 +984,6 @@ static struct dmi_system_id __initdata acpi_dmi_table[] = {
 		     DMI_MATCH(DMI_PRODUCT_NAME, "PRIMERGY T850"),
 		     },
 	 },
-	{
-	 .callback = force_acpi_ht,
-	 .ident = "DELL GX240",
-	 .matches = {
-		     DMI_MATCH(DMI_BOARD_VENDOR, "Dell Computer Corporation"),
-		     DMI_MATCH(DMI_BOARD_NAME, "OptiPlex GX240"),
-		     },
-	 },
 	{
 	 .callback = force_acpi_ht,
 	 .ident = "HP VISUALIZE NT Workstation",

+ 2 - 2
arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c

@@ -665,8 +665,8 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
 	data->max_freq = perf->states[0].core_frequency * 1000;
 	/* table init */
 	for (i=0; i<perf->state_count; i++) {
-		if (i>0 && perf->states[i].core_frequency ==
-		    perf->states[i-1].core_frequency)
+		if (i>0 && perf->states[i].core_frequency >=
+		    data->freq_table[valid_states-1].frequency / 1000)
 			continue;
 
 		data->freq_table[valid_states].index = i;

+ 11 - 12
drivers/acpi/Kconfig

@@ -2,16 +2,12 @@
 # ACPI Configuration
 #
 
-menu "ACPI (Advanced Configuration and Power Interface) Support"
+menuconfig ACPI
+	bool "ACPI Support (Advanced Configuration and Power Interface) Support"
 	depends on !X86_NUMAQ
 	depends on !X86_VISWS
 	depends on !IA64_HP_SIM
 	depends on IA64 || X86
-	depends on PM
-
-config ACPI
-	bool "ACPI Support"
-	depends on IA64 || X86
 	depends on PCI
 	depends on PM
 	select PNP
@@ -49,7 +45,6 @@ if ACPI
 config ACPI_SLEEP
 	bool "Sleep States"
 	depends on X86 && (!SMP || SUSPEND_SMP)
-	depends on PM
 	default y
 	---help---
 	  This option adds support for ACPI suspend states. 
@@ -82,7 +77,6 @@ config ACPI_SLEEP_PROC_SLEEP
 
 config ACPI_PROCFS
 	bool "Procfs interface (deprecated)"
-	depends on ACPI
 	default y
 	---help---
 	  The Procfs interface for ACPI is made optional for backward compatibility.
@@ -124,7 +118,7 @@ config ACPI_BUTTON
 
 config ACPI_VIDEO
 	tristate "Video"
-	depends on X86 && BACKLIGHT_CLASS_DEVICE
+	depends on X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL
 	help
 	  This driver implement the ACPI Extensions For Display Adapters
 	  for integrated graphics devices on motherboard, as specified in
@@ -280,6 +274,14 @@ config ACPI_DEBUG
 	  of verbosity. Saying Y enables these statements. This will increase
 	  your kernel size by around 50K.
 
+config ACPI_DEBUG_FUNC_TRACE
+	bool "Additionally enable ACPI function tracing"
+	default n
+	depends on ACPI_DEBUG
+	help
+	  ACPI Debug Statements slow down ACPI processing. Function trace
+	  is about half of the penalty and is rarely useful.
+
 config ACPI_EC
 	bool
 	default y
@@ -330,7 +332,6 @@ config ACPI_CONTAINER
 
 config ACPI_HOTPLUG_MEMORY
 	tristate "Memory Hotplug"
-	depends on ACPI
 	depends on MEMORY_HOTPLUG
 	default n
 	help
@@ -359,5 +360,3 @@ config ACPI_SBS
 	  to today's ACPI "Control Method" battery.
 
 endif	# ACPI
-
-endmenu

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 424 - 232
drivers/acpi/battery.c


+ 9 - 10
drivers/acpi/bay.c

@@ -288,6 +288,11 @@ static int bay_add(acpi_handle handle, int id)
 	new_bay->pdev = pdev;
 	platform_set_drvdata(pdev, new_bay);
 
+	/*
+	 * we want the bay driver to be able to send uevents
+	 */
+	pdev->dev.uevent_suppress = 0;
+
 	if (acpi_bay_add_fs(new_bay)) {
 		platform_device_unregister(new_bay->pdev);
 		goto bay_add_err;
@@ -328,18 +333,12 @@ static void bay_notify(acpi_handle handle, u32 event, void *data)
 {
 	struct bay *bay_dev = (struct bay *)data;
 	struct device *dev = &bay_dev->pdev->dev;
+	char event_string[12];
+	char *envp[] = { event_string, NULL };
 
 	bay_dprintk(handle, "Bay event");
-
-	switch(event) {
-	case ACPI_NOTIFY_BUS_CHECK:
-	case ACPI_NOTIFY_DEVICE_CHECK:
-	case ACPI_NOTIFY_EJECT_REQUEST:
-		kobject_uevent(&dev->kobj, KOBJ_CHANGE);
-		break;
-	default:
-		printk(KERN_ERR PREFIX "Bay: unknown event %d\n", event);
-	}
+	sprintf(event_string, "BAY_EVENT=%d\n", event);
+	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 }
 
 static acpi_status

+ 4 - 0
drivers/acpi/bus.c

@@ -292,6 +292,10 @@ int acpi_bus_generate_event(struct acpi_device *device, u8 type, int data)
 	if (!device)
 		return -EINVAL;
 
+	if (acpi_bus_generate_genetlink_event(device, type, data))
+		printk(KERN_WARNING PREFIX
+			"Failed to generate an ACPI event via genetlink!\n");
+
 	/* drop event on the floor if no one's listening */
 	if (!event_is_open)
 		return 0;

+ 97 - 30
drivers/acpi/dock.c

@@ -40,8 +40,15 @@ MODULE_AUTHOR("Kristen Carlson Accardi");
 MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION);
 MODULE_LICENSE("GPL");
 
+static int immediate_undock = 1;
+module_param(immediate_undock, bool, 0644);
+MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
+	"undock immediately when the undock button is pressed, 0 will cause"
+	" the driver to wait for userspace to write the undock sysfs file "
+	" before undocking");
+
 static struct atomic_notifier_head dock_notifier_list;
-static struct platform_device dock_device;
+static struct platform_device *dock_device;
 static char dock_device_name[] = "dock";
 
 struct dock_station {
@@ -63,6 +70,7 @@ struct dock_dependent_device {
 };
 
 #define DOCK_DOCKING	0x00000001
+#define DOCK_UNDOCKING  0x00000002
 #define DOCK_EVENT	3
 #define UNDOCK_EVENT	2
 
@@ -327,12 +335,20 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event)
 
 static void dock_event(struct dock_station *ds, u32 event, int num)
 {
-	struct device *dev = &dock_device.dev;
+	struct device *dev = &dock_device->dev;
+	char event_string[7];
+	char *envp[] = { event_string, NULL };
+
+	if (num == UNDOCK_EVENT)
+		sprintf(event_string, "UNDOCK");
+	else
+		sprintf(event_string, "DOCK");
+
 	/*
 	 * Indicate that the status of the dock station has
 	 * changed.
 	 */
-	kobject_uevent(&dev->kobj, KOBJ_CHANGE);
+	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 }
 
 /**
@@ -380,12 +396,11 @@ static void handle_dock(struct dock_station *ds, int dock)
 	union acpi_object arg;
 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 	struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-	union acpi_object *obj;
 
 	acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer);
-	obj = name_buffer.pointer;
 
-	printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking");
+	printk(KERN_INFO PREFIX "%s - %s\n",
+		(char *)name_buffer.pointer, dock ? "docking" : "undocking");
 
 	/* _DCK method has one argument */
 	arg_list.count = 1;
@@ -394,7 +409,8 @@ static void handle_dock(struct dock_station *ds, int dock)
 	arg.integer.value = dock;
 	status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer);
 	if (ACPI_FAILURE(status))
-		pr_debug("%s: failed to execute _DCK\n", obj->string.pointer);
+		printk(KERN_ERR PREFIX "%s - failed to execute _DCK\n",
+			 (char *)name_buffer.pointer);
 	kfree(buffer.pointer);
 	kfree(name_buffer.pointer);
 }
@@ -420,6 +436,16 @@ static inline void complete_dock(struct dock_station *ds)
 	ds->last_dock_time = jiffies;
 }
 
+static inline void begin_undock(struct dock_station *ds)
+{
+	ds->flags |= DOCK_UNDOCKING;
+}
+
+static inline void complete_undock(struct dock_station *ds)
+{
+	ds->flags &= ~(DOCK_UNDOCKING);
+}
+
 /**
  * dock_in_progress - see if we are in the middle of handling a dock event
  * @ds: the dock station
@@ -550,7 +576,7 @@ static int handle_eject_request(struct dock_station *ds, u32 event)
 		printk(KERN_ERR PREFIX "Unable to undock!\n");
 		return -EBUSY;
 	}
-
+	complete_undock(ds);
 	return 0;
 }
 
@@ -594,7 +620,11 @@ static void dock_notify(acpi_handle handle, u32 event, void *data)
 	 * to the driver who wish to hotplug.
          */
 	case ACPI_NOTIFY_EJECT_REQUEST:
-		handle_eject_request(ds, event);
+		begin_undock(ds);
+		if (immediate_undock)
+			handle_eject_request(ds, event);
+		else
+			dock_event(ds, event, UNDOCK_EVENT);
 		break;
 	default:
 		printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
@@ -652,6 +682,17 @@ static ssize_t show_docked(struct device *dev,
 }
 DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
 
+/*
+ * show_flags - read method for flags file in sysfs
+ */
+static ssize_t show_flags(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
+
+}
+DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
+
 /*
  * write_undock - write method for "undock" file in sysfs
  */
@@ -675,16 +716,15 @@ 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)) {
+	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
@@ -711,33 +751,53 @@ static int dock_add(acpi_handle handle)
 	ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
 
 	/* initialize platform device stuff */
-	dock_device.name = dock_device_name;
-	ret = platform_device_register(&dock_device);
+	dock_device =
+		platform_device_register_simple(dock_device_name, 0, NULL, 0);
+	if (IS_ERR(dock_device)) {
+		kfree(dock_station);
+		dock_station = NULL;
+		return PTR_ERR(dock_device);
+	}
+
+	/* we want the dock device to send uevents */
+	dock_device->dev.uevent_suppress = 0;
+
+	ret = device_create_file(&dock_device->dev, &dev_attr_docked);
 	if (ret) {
-		printk(KERN_ERR PREFIX "Error %d registering dock device\n", ret);
+		printk("Error %d adding sysfs file\n", ret);
+		platform_device_unregister(dock_device);
 		kfree(dock_station);
+		dock_station = NULL;
 		return ret;
 	}
-	ret = device_create_file(&dock_device.dev, &dev_attr_docked);
+	ret = device_create_file(&dock_device->dev, &dev_attr_undock);
 	if (ret) {
 		printk("Error %d adding sysfs file\n", ret);
-		platform_device_unregister(&dock_device);
+		device_remove_file(&dock_device->dev, &dev_attr_docked);
+		platform_device_unregister(dock_device);
 		kfree(dock_station);
+		dock_station = NULL;
 		return ret;
 	}
-	ret = device_create_file(&dock_device.dev, &dev_attr_undock);
+	ret = device_create_file(&dock_device->dev, &dev_attr_uid);
 	if (ret) {
 		printk("Error %d adding sysfs file\n", ret);
-		device_remove_file(&dock_device.dev, &dev_attr_docked);
-		platform_device_unregister(&dock_device);
+		device_remove_file(&dock_device->dev, &dev_attr_docked);
+		device_remove_file(&dock_device->dev, &dev_attr_undock);
+		platform_device_unregister(dock_device);
 		kfree(dock_station);
+		dock_station = NULL;
 		return ret;
 	}
-	ret = device_create_file(&dock_device.dev, &dev_attr_uid);
+	ret = device_create_file(&dock_device->dev, &dev_attr_flags);
 	if (ret) {
 		printk("Error %d adding sysfs file\n", ret);
-		platform_device_unregister(&dock_device);
+		device_remove_file(&dock_device->dev, &dev_attr_docked);
+		device_remove_file(&dock_device->dev, &dev_attr_undock);
+		device_remove_file(&dock_device->dev, &dev_attr_uid);
+		platform_device_unregister(dock_device);
 		kfree(dock_station);
+		dock_station = NULL;
 		return ret;
 	}
 
@@ -750,6 +810,7 @@ static int dock_add(acpi_handle handle)
 	dd = alloc_dock_dependent_device(handle);
 	if (!dd) {
 		kfree(dock_station);
+		dock_station = NULL;
 		ret = -ENOMEM;
 		goto dock_add_err_unregister;
 	}
@@ -773,10 +834,13 @@ static int dock_add(acpi_handle handle)
 dock_add_err:
 	kfree(dd);
 dock_add_err_unregister:
-	device_remove_file(&dock_device.dev, &dev_attr_docked);
-	device_remove_file(&dock_device.dev, &dev_attr_undock);
-	platform_device_unregister(&dock_device);
+	device_remove_file(&dock_device->dev, &dev_attr_docked);
+	device_remove_file(&dock_device->dev, &dev_attr_undock);
+	device_remove_file(&dock_device->dev, &dev_attr_uid);
+	device_remove_file(&dock_device->dev, &dev_attr_flags);
+	platform_device_unregister(dock_device);
 	kfree(dock_station);
+	dock_station = NULL;
 	return ret;
 }
 
@@ -804,12 +868,15 @@ static int dock_remove(void)
 		printk(KERN_ERR "Error removing notify handler\n");
 
 	/* cleanup sysfs */
-	device_remove_file(&dock_device.dev, &dev_attr_docked);
-	device_remove_file(&dock_device.dev, &dev_attr_undock);
-	platform_device_unregister(&dock_device);
+	device_remove_file(&dock_device->dev, &dev_attr_docked);
+	device_remove_file(&dock_device->dev, &dev_attr_undock);
+	device_remove_file(&dock_device->dev, &dev_attr_uid);
+	device_remove_file(&dock_device->dev, &dev_attr_flags);
+	platform_device_unregister(dock_device);
 
 	/* free dock station memory */
 	kfree(dock_station);
+	dock_station = NULL;
 	return 0;
 }
 

+ 146 - 101
drivers/acpi/ec.c

@@ -34,25 +34,26 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/interrupt.h>
+#include <linux/list.h>
 #include <asm/io.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 #include <acpi/actypes.h>
 
-#define _COMPONENT		ACPI_EC_COMPONENT
-ACPI_MODULE_NAME("ec");
-#define ACPI_EC_COMPONENT		0x00100000
 #define ACPI_EC_CLASS			"embedded_controller"
 #define ACPI_EC_HID			"PNP0C09"
 #define ACPI_EC_DEVICE_NAME		"Embedded Controller"
 #define ACPI_EC_FILE_INFO		"info"
+
 #undef PREFIX
 #define PREFIX				"ACPI: EC: "
+
 /* EC status register */
 #define ACPI_EC_FLAG_OBF	0x01	/* Output buffer full */
 #define ACPI_EC_FLAG_IBF	0x02	/* Input buffer full */
 #define ACPI_EC_FLAG_BURST	0x10	/* burst mode */
 #define ACPI_EC_FLAG_SCI	0x20	/* EC-SCI occurred */
+
 /* EC commands */
 enum ec_command {
 	ACPI_EC_COMMAND_READ = 0x80,
@@ -61,6 +62,7 @@ enum ec_command {
 	ACPI_EC_BURST_DISABLE = 0x83,
 	ACPI_EC_COMMAND_QUERY = 0x84,
 };
+
 /* EC events */
 enum ec_event {
 	ACPI_EC_EVENT_OBF_1 = 1,	/* Output buffer full */
@@ -94,6 +96,16 @@ 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 */
+typedef int (*acpi_ec_query_func) (void *data);
+
+struct acpi_ec_query_handler {
+	struct list_head node;
+	acpi_ec_query_func func;
+	acpi_handle handle;
+	void *data;
+	u8 query_bit;
+};
+
 static struct acpi_ec {
 	acpi_handle handle;
 	unsigned long gpe;
@@ -104,6 +116,7 @@ static struct acpi_ec {
 	atomic_t query_pending;
 	atomic_t event_count;
 	wait_queue_head_t wait;
+	struct list_head list;
 } *boot_ec, *first_ec;
 
 /* --------------------------------------------------------------------------
@@ -245,7 +258,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command,
 
 	status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, 0, 0);
 	if (status) {
-		printk(KERN_DEBUG PREFIX
+		printk(KERN_ERR PREFIX
 		       "input buffer is not empty, aborting transaction\n");
 		goto end;
 	}
@@ -394,21 +407,67 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data)
 /* --------------------------------------------------------------------------
                                 Event Management
    -------------------------------------------------------------------------- */
+int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
+			      acpi_handle handle, acpi_ec_query_func func,
+			      void *data)
+{
+	struct acpi_ec_query_handler *handler =
+	    kzalloc(sizeof(struct acpi_ec_query_handler), GFP_KERNEL);
+	if (!handler)
+		return -ENOMEM;
+
+	handler->query_bit = query_bit;
+	handler->handle = handle;
+	handler->func = func;
+	handler->data = data;
+	mutex_lock(&ec->lock);
+	list_add_tail(&handler->node, &ec->list);
+	mutex_unlock(&ec->lock);
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler);
+
+void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit)
+{
+	struct acpi_ec_query_handler *handler;
+	mutex_lock(&ec->lock);
+	list_for_each_entry(handler, &ec->list, node) {
+		if (query_bit == handler->query_bit) {
+			list_del(&handler->node);
+			kfree(handler);
+			break;
+		}
+	}
+	mutex_unlock(&ec->lock);
+}
+
+EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler);
 
 static void acpi_ec_gpe_query(void *ec_cxt)
 {
 	struct acpi_ec *ec = ec_cxt;
 	u8 value = 0;
-	char object_name[8];
+	struct acpi_ec_query_handler *handler, copy;
 
 	if (!ec || acpi_ec_query(ec, &value))
 		return;
-
-	snprintf(object_name, 8, "_Q%2.2X", value);
-
-	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s", object_name));
-
-	acpi_evaluate_object(ec->handle, object_name, NULL, NULL);
+	mutex_lock(&ec->lock);
+	list_for_each_entry(handler, &ec->list, node) {
+		if (value == handler->query_bit) {
+			/* have custom handler for this bit */
+			memcpy(&copy, handler, sizeof(copy));
+			mutex_unlock(&ec->lock);
+			if (copy.func) {
+				copy.func(copy.data);
+			} else if (copy.handle) {
+				acpi_evaluate_object(copy.handle, NULL, NULL, NULL);
+			}
+			return;
+		}
+	}
+	mutex_unlock(&ec->lock);
+	printk(KERN_ERR PREFIX "Handler for query 0x%x is not found!\n", value);
 }
 
 static u32 acpi_ec_gpe_handler(void *data)
@@ -427,8 +486,7 @@ static u32 acpi_ec_gpe_handler(void *data)
 	if ((value & ACPI_EC_FLAG_SCI) && !atomic_read(&ec->query_pending)) {
 		atomic_set(&ec->query_pending, 1);
 		status =
-		    acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query,
-				    ec);
+		    acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec);
 	}
 
 	return status == AE_OK ?
@@ -454,57 +512,35 @@ acpi_ec_space_setup(acpi_handle region_handle,
 }
 
 static acpi_status
-acpi_ec_space_handler(u32 function,
-		      acpi_physical_address address,
-		      u32 bit_width,
-		      acpi_integer * value,
+acpi_ec_space_handler(u32 function, acpi_physical_address address,
+		      u32 bits, acpi_integer *value,
 		      void *handler_context, void *region_context)
 {
-	int result = 0;
 	struct acpi_ec *ec = handler_context;
-	u64 temp = *value;
-	acpi_integer f_v = 0;
-	int i = 0;
+	int result = 0, i = 0;
+	u8 temp = 0;
 
 	if ((address > 0xFF) || !value || !handler_context)
 		return AE_BAD_PARAMETER;
 
-	if (bit_width != 8 && acpi_strict) {
+	if (function != ACPI_READ && function != ACPI_WRITE)
 		return AE_BAD_PARAMETER;
-	}
-
-      next_byte:
-	switch (function) {
-	case ACPI_READ:
-		temp = 0;
-		result = acpi_ec_read(ec, (u8) address, (u8 *) & temp);
-		break;
-	case ACPI_WRITE:
-		result = acpi_ec_write(ec, (u8) address, (u8) temp);
-		break;
-	default:
-		result = -EINVAL;
-		goto out;
-		break;
-	}
 
-	bit_width -= 8;
-	if (bit_width) {
-		if (function == ACPI_READ)
-			f_v |= temp << 8 * i;
-		if (function == ACPI_WRITE)
-			temp >>= 8;
-		i++;
-		address++;
-		goto next_byte;
-	}
+	if (bits != 8 && acpi_strict)
+		return AE_BAD_PARAMETER;
 
-	if (function == ACPI_READ) {
-		f_v |= temp << 8 * i;
-		*value = f_v;
+	while (bits - i > 0) {
+		if (function == ACPI_READ) {
+			result = acpi_ec_read(ec, address, &temp);
+			(*value) |= ((acpi_integer)temp) << i;
+		} else {
+			temp = 0xff & ((*value) >> i);
+			result = acpi_ec_write(ec, address, temp);
+		}
+		i += 8;
+		++address;
 	}
 
-      out:
 	switch (result) {
 	case -EINVAL:
 		return AE_BAD_PARAMETER;
@@ -597,9 +633,6 @@ static int acpi_ec_remove_fs(struct acpi_device *device)
 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);
@@ -610,13 +643,52 @@ static struct acpi_ec *make_acpi_ec(void)
 	atomic_set(&ec->event_count, 1);
 	mutex_init(&ec->lock);
 	init_waitqueue_head(&ec->wait);
+	INIT_LIST_HEAD(&ec->list);
 
 	return ec;
 }
 
+static acpi_status
+acpi_ec_register_query_methods(acpi_handle handle, u32 level,
+			       void *context, void **return_value)
+{
+	struct acpi_namespace_node *node = handle;
+	struct acpi_ec *ec = context;
+	int value = 0;
+	if (sscanf(node->name.ascii, "_Q%x", &value) == 1) {
+		acpi_ec_add_query_handler(ec, value, handle, NULL, NULL);
+	}
+	return AE_OK;
+}
+
+static int ec_parse_device(struct acpi_ec *ec, acpi_handle handle)
+{
+	if (ACPI_FAILURE(acpi_walk_resources(handle, METHOD_NAME__CRS,
+				     ec_parse_io_ports, ec)))
+		return -EINVAL;
+
+	/* Get GPE bit assignment (EC events). */
+	/* TODO: Add support for _GPE returning a package */
+	if (ACPI_FAILURE(acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe)))
+		return -EINVAL;
+
+	/* Use the global lock for all EC transactions? */
+	acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock);
+
+	/* Find and register all query methods */
+	acpi_walk_namespace(ACPI_TYPE_METHOD, handle, 1,
+			    acpi_ec_register_query_methods, ec, NULL);
+
+	ec->handle = handle;
+
+	printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx",
+			  ec->gpe, ec->command_addr, ec->data_addr);
+
+	return 0;
+}
+
 static int acpi_ec_add(struct acpi_device *device)
 {
-	acpi_status status = AE_OK;
 	struct acpi_ec *ec = NULL;
 
 	if (!device)
@@ -629,8 +701,7 @@ static int acpi_ec_add(struct acpi_device *device)
 	if (!ec)
 		return -ENOMEM;
 
-	status = ec_parse_device(device->handle, 0, ec, NULL);
-	if (status != AE_CTRL_TERMINATE) {
+	if (ec_parse_device(ec, device->handle)) {
 		kfree(ec);
 		return -EINVAL;
 	}
@@ -641,6 +712,8 @@ static int acpi_ec_add(struct acpi_device *device)
 			/* We might have incorrect info for GL at boot time */
 			mutex_lock(&boot_ec->lock);
 			boot_ec->global_lock = ec->global_lock;
+			/* Copy handlers from new ec into boot ec */
+			list_splice(&ec->list, &boot_ec->list);
 			mutex_unlock(&boot_ec->lock);
 			kfree(ec);
 			ec = boot_ec;
@@ -651,22 +724,24 @@ static int acpi_ec_add(struct acpi_device *device)
 	acpi_driver_data(device) = ec;
 
 	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));
-
 	return 0;
 }
 
 static int acpi_ec_remove(struct acpi_device *device, int type)
 {
 	struct acpi_ec *ec;
+	struct acpi_ec_query_handler *handler;
 
 	if (!device)
 		return -EINVAL;
 
 	ec = acpi_driver_data(device);
+	mutex_lock(&ec->lock);
+	list_for_each_entry(handler, &ec->list, node) {
+		list_del(&handler->node);
+		kfree(handler);
+	}
+	mutex_unlock(&ec->lock);
 	acpi_ec_remove_fs(device);
 	acpi_driver_data(device) = NULL;
 	if (ec == first_ec)
@@ -722,15 +797,13 @@ static int ec_install_handlers(struct acpi_ec *ec)
 		return -ENODEV;
 	}
 
-	/* 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;
+	int ret = 0;
 
 	if (!device)
 		return -EINVAL;
@@ -740,14 +813,14 @@ static int acpi_ec_start(struct acpi_device *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;
+	if (ec != boot_ec)
+		ret = ec_install_handlers(ec);
+
+	/* EC is fully operational, allow queries */
+	atomic_set(&ec->query_pending, 0);
 
-	return ec_install_handlers(ec);
+	return ret;
 }
 
 static int acpi_ec_stop(struct acpi_device *device, int type)
@@ -779,34 +852,6 @@ static int acpi_ec_stop(struct acpi_device *device, int type)
 	return 0;
 }
 
-static acpi_status
-ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
-{
-	acpi_status status;
-
-	struct acpi_ec *ec = context;
-	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
-				     ec_parse_io_ports, ec);
-	if (ACPI_FAILURE(status))
-		return status;
-
-	/* 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;
-
-	/* Use the global lock for all EC transactions? */
-	acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock);
-
-	ec->handle = handle;
-
-	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx",
-			  ec->gpe, ec->command_addr, ec->data_addr));
-
-	return AE_CTRL_TERMINATE;
-}
-
 int __init acpi_ec_ecdt_probe(void)
 {
 	int ret;
@@ -825,7 +870,7 @@ int __init acpi_ec_ecdt_probe(void)
 	if (ACPI_FAILURE(status))
 		goto error;
 
-	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT"));
+	printk(KERN_INFO PREFIX "EC description table is found, configuring boot EC\n");
 
 	boot_ec->command_addr = ecdt_ptr->control.address;
 	boot_ec->data_addr = ecdt_ptr->data.address;

+ 146 - 7
drivers/acpi/event.c

@@ -11,6 +11,8 @@
 #include <linux/init.h>
 #include <linux/poll.h>
 #include <acpi/acpi_drivers.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
 
 #define _COMPONENT		ACPI_SYSTEM_COMPONENT
 ACPI_MODULE_NAME("event");
@@ -48,7 +50,6 @@ acpi_system_read_event(struct file *file, char __user * buffer, size_t count,
 	static int chars_remaining = 0;
 	static char *ptr;
 
-
 	if (!chars_remaining) {
 		memset(&event, 0, sizeof(struct acpi_bus_event));
 
@@ -106,23 +107,161 @@ static const struct file_operations acpi_system_event_ops = {
 	.poll = acpi_system_poll_event,
 };
 
+#ifdef CONFIG_NET
+unsigned int acpi_event_seqnum;
+struct acpi_genl_event {
+	acpi_device_class device_class;
+	char bus_id[15];
+	u32 type;
+	u32 data;
+};
+
+/* attributes of acpi_genl_family */
+enum {
+	ACPI_GENL_ATTR_UNSPEC,
+	ACPI_GENL_ATTR_EVENT,	/* ACPI event info needed by user space */
+	__ACPI_GENL_ATTR_MAX,
+};
+#define ACPI_GENL_ATTR_MAX (__ACPI_GENL_ATTR_MAX - 1)
+
+/* commands supported by the acpi_genl_family */
+enum {
+	ACPI_GENL_CMD_UNSPEC,
+	ACPI_GENL_CMD_EVENT,	/* kernel->user notifications for ACPI events */
+	__ACPI_GENL_CMD_MAX,
+};
+#define ACPI_GENL_CMD_MAX (__ACPI_GENL_CMD_MAX - 1)
+
+#define ACPI_GENL_FAMILY_NAME		"acpi_event"
+#define ACPI_GENL_VERSION		0x01
+#define ACPI_GENL_MCAST_GROUP_NAME 	"acpi_mc_group"
+
+static struct genl_family acpi_event_genl_family = {
+	.id = GENL_ID_GENERATE,
+	.name = ACPI_GENL_FAMILY_NAME,
+	.version = ACPI_GENL_VERSION,
+	.maxattr = ACPI_GENL_ATTR_MAX,
+};
+
+static struct genl_multicast_group acpi_event_mcgrp = {
+	.name = ACPI_GENL_MCAST_GROUP_NAME,
+};
+
+int acpi_bus_generate_genetlink_event(struct acpi_device *device,
+				      u8 type, int data)
+{
+	struct sk_buff *skb;
+	struct nlattr *attr;
+	struct acpi_genl_event *event;
+	void *msg_header;
+	int size;
+	int result;
+
+	/* allocate memory */
+	size = nla_total_size(sizeof(struct acpi_genl_event)) +
+	    nla_total_size(0);
+
+	skb = genlmsg_new(size, GFP_ATOMIC);
+	if (!skb)
+		return -ENOMEM;
+
+	/* add the genetlink message header */
+	msg_header = genlmsg_put(skb, 0, acpi_event_seqnum++,
+				 &acpi_event_genl_family, 0,
+				 ACPI_GENL_CMD_EVENT);
+	if (!msg_header) {
+		nlmsg_free(skb);
+		return -ENOMEM;
+	}
+
+	/* fill the data */
+	attr =
+	    nla_reserve(skb, ACPI_GENL_ATTR_EVENT,
+			sizeof(struct acpi_genl_event));
+	if (!attr) {
+		nlmsg_free(skb);
+		return -EINVAL;
+	}
+
+	event = nla_data(attr);
+	if (!event) {
+		nlmsg_free(skb);
+		return -EINVAL;
+	}
+
+	memset(event, 0, sizeof(struct acpi_genl_event));
+
+	strcpy(event->device_class, device->pnp.device_class);
+	strcpy(event->bus_id, device->dev.bus_id);
+	event->type = type;
+	event->data = data;
+
+	/* send multicast genetlink message */
+	result = genlmsg_end(skb, msg_header);
+	if (result < 0) {
+		nlmsg_free(skb);
+		return result;
+	}
+
+	result =
+	    genlmsg_multicast(skb, 0, acpi_event_mcgrp.id, GFP_ATOMIC);
+	if (result)
+		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+				  "Failed to send a Genetlink message!\n"));
+	return 0;
+}
+
+static int acpi_event_genetlink_init(void)
+{
+	int result;
+
+	result = genl_register_family(&acpi_event_genl_family);
+	if (result)
+		return result;
+
+	result = genl_register_mc_group(&acpi_event_genl_family,
+					&acpi_event_mcgrp);
+	if (result)
+		genl_unregister_family(&acpi_event_genl_family);
+
+	return result;
+}
+
+#else
+int acpi_bus_generate_genetlink_event(struct acpi_device *device, u8 type,
+				      int data)
+{
+	return 0;
+}
+
+static int acpi_event_genetlink_init(void)
+{
+	return -ENODEV;
+}
+#endif
+
 static int __init acpi_event_init(void)
 {
 	struct proc_dir_entry *entry;
 	int error = 0;
 
-
 	if (acpi_disabled)
 		return 0;
 
+	/* create genetlink for acpi event */
+	error = acpi_event_genetlink_init();
+	if (error)
+		printk(KERN_WARNING PREFIX
+		       "Failed to create genetlink family for ACPI event\n");
+
 	/* 'event' [R] */
 	entry = create_proc_entry("event", S_IRUSR, acpi_root_dir);
 	if (entry)
 		entry->proc_fops = &acpi_system_event_ops;
-	else {
-		error = -ENODEV;
-	}
-	return error;
+	else
+		return -ENODEV;
+
+	return 0;
 }
 
-subsys_initcall(acpi_event_init);
+fs_initcall(acpi_event_init);

+ 4 - 0
drivers/acpi/events/evgpeblk.c

@@ -586,6 +586,10 @@ acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt)
 	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
 	if (gpe_xrupt->previous) {
 		gpe_xrupt->previous->next = gpe_xrupt->next;
+	} else {
+		/* No previous, update list head */
+
+		acpi_gbl_gpe_xrupt_list_head = gpe_xrupt->next;
 	}
 
 	if (gpe_xrupt->next) {

+ 1 - 0
drivers/acpi/events/evrgnini.c

@@ -284,6 +284,7 @@ acpi_ev_pci_config_region_setup(acpi_handle handle,
 	}
 
 	if (!pci_device_node) {
+		ACPI_FREE(pci_id);
 		return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
 	}
 

+ 1 - 1
drivers/acpi/glue.c

@@ -16,7 +16,7 @@
 #if ACPI_GLUE_DEBUG
 #define DBG(x...) printk(PREFIX x)
 #else
-#define DBG(x...)
+#define DBG(x...) do { } while(0)
 #endif
 static LIST_HEAD(bus_type_list);
 static DECLARE_RWSEM(bus_type_sem);

+ 2 - 38
drivers/acpi/osl.c

@@ -77,13 +77,7 @@ static struct workqueue_struct *kacpi_notify_wq;
 #define	OSI_STRING_LENGTH_MAX 64	/* arbitrary */
 static char osi_additional_string[OSI_STRING_LENGTH_MAX];
 
-#define OSI_LINUX_ENABLED
-#ifdef	OSI_LINUX_ENABLED
-int osi_linux = 1;	/* enable _OSI(Linux) by default */
-#else
-int osi_linux;		/* disable _OSI(Linux) by default */
-#endif
-
+static int osi_linux;		/* disable _OSI(Linux) by default */
 
 #ifdef CONFIG_DMI
 static struct __initdata dmi_system_id acpi_osl_dmi_table[];
@@ -1183,17 +1177,10 @@ acpi_os_validate_interface (char *interface)
 	if (!strcmp("Linux", interface)) {
 		printk(KERN_WARNING PREFIX
 			"System BIOS is requesting _OSI(Linux)\n");
-#ifdef	OSI_LINUX_ENABLED
-		printk(KERN_WARNING PREFIX
-			"Please test with \"acpi_osi=!Linux\"\n"
-			"Please send dmidecode "
-			"to linux-acpi@vger.kernel.org\n");
-#else
 		printk(KERN_WARNING PREFIX
 			"If \"acpi_osi=Linux\" works better,\n"
 			"Please send dmidecode "
 			"to linux-acpi@vger.kernel.org\n");
-#endif
 		if(osi_linux)
 			return AE_OK;
 	}
@@ -1227,36 +1214,14 @@ acpi_os_validate_address (
 }
 
 #ifdef CONFIG_DMI
-#ifdef	OSI_LINUX_ENABLED
-static int dmi_osi_not_linux(struct dmi_system_id *d)
-{
-	printk(KERN_NOTICE "%s detected: requires not _OSI(Linux)\n", d->ident);
-	enable_osi_linux(0);
-	return 0;
-}
-#else
 static int dmi_osi_linux(struct dmi_system_id *d)
 {
-	printk(KERN_NOTICE "%s detected: requires _OSI(Linux)\n", d->ident);
+	printk(KERN_NOTICE "%s detected: enabling _OSI(Linux)\n", d->ident);
 	enable_osi_linux(1);
 	return 0;
 }
-#endif
 
 static struct dmi_system_id acpi_osl_dmi_table[] __initdata = {
-#ifdef	OSI_LINUX_ENABLED
-	/*
-	 * Boxes that need NOT _OSI(Linux)
-	 */
-	{
-	 .callback = dmi_osi_not_linux,
-	 .ident = "Toshiba Satellite P100",
-	 .matches = {
-		     DMI_MATCH(DMI_BOARD_VENDOR, "TOSHIBA"),
-		     DMI_MATCH(DMI_BOARD_NAME, "Satellite P100"),
-		     },
-	 },
-#else
 	/*
 	 * Boxes that need _OSI(Linux)
 	 */
@@ -1268,7 +1233,6 @@ static struct dmi_system_id acpi_osl_dmi_table[] __initdata = {
 		     DMI_MATCH(DMI_BOARD_NAME, "MPAD-MSAE Customer Reference Boards"),
 		     },
 	 },
-#endif
 	{}
 };
 #endif /* CONFIG_DMI */

+ 1 - 1
drivers/acpi/pci_link.c

@@ -733,7 +733,7 @@ static int acpi_pci_link_add(struct acpi_device *device)
 	/* query and set link->irq.active */
 	acpi_pci_link_get_current(link);
 
-	printk(PREFIX "%s [%s] (IRQs", acpi_device_name(device),
+	printk(KERN_INFO PREFIX "%s [%s] (IRQs", acpi_device_name(device),
 	       acpi_device_bid(device));
 	for (i = 0; i < link->irq.possible_count; i++) {
 		if (link->irq.active == link->irq.possible[i]) {

+ 6 - 0
drivers/acpi/processor_core.c

@@ -66,6 +66,7 @@
 #define ACPI_PROCESSOR_FILE_LIMIT	"limit"
 #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
 #define ACPI_PROCESSOR_NOTIFY_POWER	0x81
+#define ACPI_PROCESSOR_NOTIFY_THROTTLING	0x82
 
 #define ACPI_PROCESSOR_LIMIT_USER	0
 #define ACPI_PROCESSOR_LIMIT_THERMAL	1
@@ -84,6 +85,8 @@ static int acpi_processor_info_open_fs(struct inode *inode, struct file *file);
 static void acpi_processor_notify(acpi_handle handle, u32 event, void *data);
 static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu);
 static int acpi_processor_handle_eject(struct acpi_processor *pr);
+extern int acpi_processor_tstate_has_changed(struct acpi_processor *pr);
+
 
 static struct acpi_driver acpi_processor_driver = {
 	.name = "processor",
@@ -696,6 +699,9 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data)
 		acpi_processor_cst_has_changed(pr);
 		acpi_bus_generate_event(device, event, 0);
 		break;
+	case ACPI_PROCESSOR_NOTIFY_THROTTLING:
+		acpi_processor_tstate_has_changed(pr);
+		acpi_bus_generate_event(device, event, 0);
 	default:
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 				  "Unsupported event [0x%x]\n", event));

+ 15 - 5
drivers/acpi/processor_idle.c

@@ -490,7 +490,17 @@ static void acpi_processor_idle(void)
 
 	case ACPI_STATE_C3:
 
-		if (pr->flags.bm_check) {
+		/*
+		 * disable bus master
+		 * bm_check implies we need ARB_DIS
+		 * !bm_check implies we need cache flush
+		 * bm_control implies whether we can do ARB_DIS
+		 *
+		 * That leaves a case where bm_check is set and bm_control is
+		 * not set. In that case we cannot do much, we enter C3
+		 * without doing anything.
+		 */
+		if (pr->flags.bm_check && pr->flags.bm_control) {
 			if (atomic_inc_return(&c3_cpu_count) ==
 			    num_online_cpus()) {
 				/*
@@ -499,7 +509,7 @@ static void acpi_processor_idle(void)
 				 */
 				acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1);
 			}
-		} else {
+		} else if (!pr->flags.bm_check) {
 			/* SMP with no shared cache... Invalidate cache  */
 			ACPI_FLUSH_CPU_CACHE();
 		}
@@ -511,7 +521,7 @@ static void acpi_processor_idle(void)
 		acpi_cstate_enter(cx);
 		/* Get end time (ticks) */
 		t2 = inl(acpi_gbl_FADT.xpm_timer_block.address);
-		if (pr->flags.bm_check) {
+		if (pr->flags.bm_check && pr->flags.bm_control) {
 			/* Enable bus master arbitration */
 			atomic_dec(&c3_cpu_count);
 			acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0);
@@ -961,9 +971,9 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
 	if (pr->flags.bm_check) {
 		/* bus mastering control is necessary */
 		if (!pr->flags.bm_control) {
+			/* In this case we enter C3 without bus mastering */
 			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-					  "C3 support requires bus mastering control\n"));
-			return;
+				"C3 support without bus mastering control\n"));
 		}
 	} else {
 		/*

+ 395 - 15
drivers/acpi/processor_throttling.c

@@ -44,17 +44,231 @@
 #define _COMPONENT              ACPI_PROCESSOR_COMPONENT
 ACPI_MODULE_NAME("processor_throttling");
 
+static int acpi_processor_get_throttling(struct acpi_processor *pr);
+int acpi_processor_set_throttling(struct acpi_processor *pr, int state);
+
+static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
+{
+	acpi_status status = 0;
+	unsigned long tpc = 0;
+
+	if (!pr)
+		return -EINVAL;
+	status = acpi_evaluate_integer(pr->handle, "_TPC", NULL, &tpc);
+	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _TPC"));
+		return -ENODEV;
+	}
+	pr->throttling_platform_limit = (int)tpc;
+	return 0;
+}
+
+int acpi_processor_tstate_has_changed(struct acpi_processor *pr)
+{
+	return acpi_processor_get_platform_limit(pr);
+}
+
+/* --------------------------------------------------------------------------
+                             _PTC, _TSS, _TSD support 
+   -------------------------------------------------------------------------- */
+static int acpi_processor_get_throttling_control(struct acpi_processor *pr)
+{
+	int result = 0;
+	acpi_status status = 0;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *ptc = NULL;
+	union acpi_object obj = { 0 };
+
+	status = acpi_evaluate_object(pr->handle, "_PTC", NULL, &buffer);
+	if (ACPI_FAILURE(status)) {
+		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PTC"));
+		return -ENODEV;
+	}
+
+	ptc = (union acpi_object *)buffer.pointer;
+	if (!ptc || (ptc->type != ACPI_TYPE_PACKAGE)
+	    || (ptc->package.count != 2)) {
+		printk(KERN_ERR PREFIX "Invalid _PTC data\n");
+		result = -EFAULT;
+		goto end;
+	}
+
+	/*
+	 * control_register
+	 */
+
+	obj = ptc->package.elements[0];
+
+	if ((obj.type != ACPI_TYPE_BUFFER)
+	    || (obj.buffer.length < sizeof(struct acpi_ptc_register))
+	    || (obj.buffer.pointer == NULL)) {
+		printk(KERN_ERR PREFIX
+		       "Invalid _PTC data (control_register)\n");
+		result = -EFAULT;
+		goto end;
+	}
+	memcpy(&pr->throttling.control_register, obj.buffer.pointer,
+	       sizeof(struct acpi_ptc_register));
+
+	/*
+	 * status_register
+	 */
+
+	obj = ptc->package.elements[1];
+
+	if ((obj.type != ACPI_TYPE_BUFFER)
+	    || (obj.buffer.length < sizeof(struct acpi_ptc_register))
+	    || (obj.buffer.pointer == NULL)) {
+		printk(KERN_ERR PREFIX "Invalid _PTC data (status_register)\n");
+		result = -EFAULT;
+		goto end;
+	}
+
+	memcpy(&pr->throttling.status_register, obj.buffer.pointer,
+	       sizeof(struct acpi_ptc_register));
+
+      end:
+	kfree(buffer.pointer);
+
+	return result;
+}
+static int acpi_processor_get_throttling_states(struct acpi_processor *pr)
+{
+	int result = 0;
+	acpi_status status = AE_OK;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
+	struct acpi_buffer state = { 0, NULL };
+	union acpi_object *tss = NULL;
+	int i;
+
+	status = acpi_evaluate_object(pr->handle, "_TSS", NULL, &buffer);
+	if (ACPI_FAILURE(status)) {
+		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _TSS"));
+		return -ENODEV;
+	}
+
+	tss = buffer.pointer;
+	if (!tss || (tss->type != ACPI_TYPE_PACKAGE)) {
+		printk(KERN_ERR PREFIX "Invalid _TSS data\n");
+		result = -EFAULT;
+		goto end;
+	}
+
+	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d throttling states\n",
+			  tss->package.count));
+
+	pr->throttling.state_count = tss->package.count;
+	pr->throttling.states_tss =
+	    kmalloc(sizeof(struct acpi_processor_tx_tss) * tss->package.count,
+		    GFP_KERNEL);
+	if (!pr->throttling.states_tss) {
+		result = -ENOMEM;
+		goto end;
+	}
+
+	for (i = 0; i < pr->throttling.state_count; i++) {
+
+		struct acpi_processor_tx_tss *tx =
+		    (struct acpi_processor_tx_tss *)&(pr->throttling.
+						      states_tss[i]);
+
+		state.length = sizeof(struct acpi_processor_tx_tss);
+		state.pointer = tx;
+
+		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i));
+
+		status = acpi_extract_package(&(tss->package.elements[i]),
+					      &format, &state);
+		if (ACPI_FAILURE(status)) {
+			ACPI_EXCEPTION((AE_INFO, status, "Invalid _TSS data"));
+			result = -EFAULT;
+			kfree(pr->throttling.states_tss);
+			goto end;
+		}
+
+		if (!tx->freqpercentage) {
+			printk(KERN_ERR PREFIX
+			       "Invalid _TSS data: freq is zero\n");
+			result = -EFAULT;
+			kfree(pr->throttling.states_tss);
+			goto end;
+		}
+	}
+
+      end:
+	kfree(buffer.pointer);
+
+	return result;
+}
+static int acpi_processor_get_tsd(struct acpi_processor *pr)
+{
+	int result = 0;
+	acpi_status status = AE_OK;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
+	struct acpi_buffer state = { 0, NULL };
+	union acpi_object *tsd = NULL;
+	struct acpi_tsd_package *pdomain;
+
+	status = acpi_evaluate_object(pr->handle, "_TSD", NULL, &buffer);
+	if (ACPI_FAILURE(status)) {
+		return -ENODEV;
+	}
+
+	tsd = buffer.pointer;
+	if (!tsd || (tsd->type != ACPI_TYPE_PACKAGE)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _TSD data\n"));
+		result = -EFAULT;
+		goto end;
+	}
+
+	if (tsd->package.count != 1) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _TSD data\n"));
+		result = -EFAULT;
+		goto end;
+	}
+
+	pdomain = &(pr->throttling.domain_info);
+
+	state.length = sizeof(struct acpi_tsd_package);
+	state.pointer = pdomain;
+
+	status = acpi_extract_package(&(tsd->package.elements[0]),
+				      &format, &state);
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _TSD data\n"));
+		result = -EFAULT;
+		goto end;
+	}
+
+	if (pdomain->num_entries != ACPI_TSD_REV0_ENTRIES) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _TSD:num_entries\n"));
+		result = -EFAULT;
+		goto end;
+	}
+
+	if (pdomain->revision != ACPI_TSD_REV0_REVISION) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _TSD:revision\n"));
+		result = -EFAULT;
+		goto end;
+	}
+
+      end:
+	kfree(buffer.pointer);
+	return result;
+}
+
 /* --------------------------------------------------------------------------
                               Throttling Control
    -------------------------------------------------------------------------- */
-static int acpi_processor_get_throttling(struct acpi_processor *pr)
+static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr)
 {
 	int state = 0;
 	u32 value = 0;
 	u32 duty_mask = 0;
 	u32 duty_value = 0;
 
-
 	if (!pr)
 		return -EINVAL;
 
@@ -94,13 +308,115 @@ static int acpi_processor_get_throttling(struct acpi_processor *pr)
 	return 0;
 }
 
-int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
+static int acpi_read_throttling_status(struct acpi_processor_throttling
+				       *throttling)
+{
+	int value = -1;
+	switch (throttling->status_register.space_id) {
+	case ACPI_ADR_SPACE_SYSTEM_IO:
+		acpi_os_read_port((acpi_io_address) throttling->status_register.
+				  address, &value,
+				  (u32) throttling->status_register.bit_width *
+				  8);
+		break;
+	case ACPI_ADR_SPACE_FIXED_HARDWARE:
+		printk(KERN_ERR PREFIX
+		       "HARDWARE addr space,NOT supported yet\n");
+		break;
+	default:
+		printk(KERN_ERR PREFIX "Unknown addr space %d\n",
+		       (u32) (throttling->status_register.space_id));
+	}
+	return value;
+}
+
+static int acpi_write_throttling_state(struct acpi_processor_throttling
+				       *throttling, int value)
+{
+	int ret = -1;
+
+	switch (throttling->control_register.space_id) {
+	case ACPI_ADR_SPACE_SYSTEM_IO:
+		acpi_os_write_port((acpi_io_address) throttling->
+				   control_register.address, value,
+				   (u32) throttling->control_register.
+				   bit_width * 8);
+		ret = 0;
+		break;
+	case ACPI_ADR_SPACE_FIXED_HARDWARE:
+		printk(KERN_ERR PREFIX
+		       "HARDWARE addr space,NOT supported yet\n");
+		break;
+	default:
+		printk(KERN_ERR PREFIX "Unknown addr space %d\n",
+		       (u32) (throttling->control_register.space_id));
+	}
+	return ret;
+}
+
+static int acpi_get_throttling_state(struct acpi_processor *pr, int value)
+{
+	int i;
+
+	for (i = 0; i < pr->throttling.state_count; i++) {
+		struct acpi_processor_tx_tss *tx =
+		    (struct acpi_processor_tx_tss *)&(pr->throttling.
+						      states_tss[i]);
+		if (tx->control == value)
+			break;
+	}
+	if (i > pr->throttling.state_count)
+		i = -1;
+	return i;
+}
+
+static int acpi_get_throttling_value(struct acpi_processor *pr, int state)
+{
+	int value = -1;
+	if (state >= 0 && state <= pr->throttling.state_count) {
+		struct acpi_processor_tx_tss *tx =
+		    (struct acpi_processor_tx_tss *)&(pr->throttling.
+						      states_tss[state]);
+		value = tx->control;
+	}
+	return value;
+}
+
+static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr)
+{
+	int state = 0;
+	u32 value = 0;
+
+	if (!pr)
+		return -EINVAL;
+
+	if (!pr->flags.throttling)
+		return -ENODEV;
+
+	pr->throttling.state = 0;
+	local_irq_disable();
+	value = acpi_read_throttling_status(&pr->throttling);
+	if (value >= 0) {
+		state = acpi_get_throttling_state(pr, value);
+		pr->throttling.state = state;
+	}
+	local_irq_enable();
+
+	return 0;
+}
+
+static int acpi_processor_get_throttling(struct acpi_processor *pr)
+{
+	return pr->throttling.acpi_processor_get_throttling(pr);
+}
+
+static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr,
+					      int state)
 {
 	u32 value = 0;
 	u32 duty_mask = 0;
 	u32 duty_value = 0;
 
-
 	if (!pr)
 		return -EINVAL;
 
@@ -113,6 +429,8 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
 	if (state == pr->throttling.state)
 		return 0;
 
+	if (state < pr->throttling_platform_limit)
+		return -EPERM;
 	/*
 	 * Calculate the duty_value and duty_mask.
 	 */
@@ -165,12 +483,51 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
 	return 0;
 }
 
+static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr,
+					     int state)
+{
+	u32 value = 0;
+
+	if (!pr)
+		return -EINVAL;
+
+	if ((state < 0) || (state > (pr->throttling.state_count - 1)))
+		return -EINVAL;
+
+	if (!pr->flags.throttling)
+		return -ENODEV;
+
+	if (state == pr->throttling.state)
+		return 0;
+
+	if (state < pr->throttling_platform_limit)
+		return -EPERM;
+
+	local_irq_disable();
+
+	value = acpi_get_throttling_value(pr, state);
+	if (value >= 0) {
+		acpi_write_throttling_state(&pr->throttling, value);
+		pr->throttling.state = state;
+	}
+	local_irq_enable();
+
+	return 0;
+}
+
+int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
+{
+	return pr->throttling.acpi_processor_set_throttling(pr, state);
+}
+
 int acpi_processor_get_throttling_info(struct acpi_processor *pr)
 {
 	int result = 0;
 	int step = 0;
 	int i = 0;
-
+	int no_ptc = 0;
+	int no_tss = 0;
+	int no_tsd = 0;
 
 	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 			  "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n",
@@ -182,6 +539,21 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr)
 		return -EINVAL;
 
 	/* TBD: Support ACPI 2.0 objects */
+	no_ptc = acpi_processor_get_throttling_control(pr);
+	no_tss = acpi_processor_get_throttling_states(pr);
+	no_tsd = acpi_processor_get_tsd(pr);
+
+	if (no_ptc || no_tss) {
+		pr->throttling.acpi_processor_get_throttling =
+		    &acpi_processor_get_throttling_fadt;
+		pr->throttling.acpi_processor_set_throttling =
+		    &acpi_processor_set_throttling_fadt;
+	} else {
+		pr->throttling.acpi_processor_get_throttling =
+		    &acpi_processor_get_throttling_ptc;
+		pr->throttling.acpi_processor_set_throttling =
+		    &acpi_processor_set_throttling_ptc;
+	}
 
 	if (!pr->throttling.address) {
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling register\n"));
@@ -262,7 +634,6 @@ static int acpi_processor_throttling_seq_show(struct seq_file *seq,
 	int i = 0;
 	int result = 0;
 
-
 	if (!pr)
 		goto end;
 
@@ -280,15 +651,25 @@ static int acpi_processor_throttling_seq_show(struct seq_file *seq,
 	}
 
 	seq_printf(seq, "state count:             %d\n"
-		   "active state:            T%d\n",
-		   pr->throttling.state_count, pr->throttling.state);
+		   "active state:            T%d\n"
+		   "state available: T%d to T%d\n",
+		   pr->throttling.state_count, pr->throttling.state,
+		   pr->throttling_platform_limit,
+		   pr->throttling.state_count - 1);
 
 	seq_puts(seq, "states:\n");
-	for (i = 0; i < pr->throttling.state_count; i++)
-		seq_printf(seq, "   %cT%d:                  %02d%%\n",
-			   (i == pr->throttling.state ? '*' : ' '), i,
-			   (pr->throttling.states[i].performance ? pr->
-			    throttling.states[i].performance / 10 : 0));
+	if (acpi_processor_get_throttling == acpi_processor_get_throttling_fadt)
+		for (i = 0; i < pr->throttling.state_count; i++)
+			seq_printf(seq, "   %cT%d:                  %02d%%\n",
+				   (i == pr->throttling.state ? '*' : ' '), i,
+				   (pr->throttling.states[i].performance ? pr->
+				    throttling.states[i].performance / 10 : 0));
+	else
+		for (i = 0; i < pr->throttling.state_count; i++)
+			seq_printf(seq, "   %cT%d:                  %02d%%\n",
+				   (i == pr->throttling.state ? '*' : ' '), i,
+				   (int)pr->throttling.states_tss[i].
+				   freqpercentage);
 
       end:
 	return 0;
@@ -301,7 +682,7 @@ static int acpi_processor_throttling_open_fs(struct inode *inode,
 			   PDE(inode)->data);
 }
 
-static ssize_t acpi_processor_write_throttling(struct file * file,
+static ssize_t acpi_processor_write_throttling(struct file *file,
 					       const char __user * buffer,
 					       size_t count, loff_t * data)
 {
@@ -310,7 +691,6 @@ static ssize_t acpi_processor_write_throttling(struct file * file,
 	struct acpi_processor *pr = m->private;
 	char state_string[12] = { '\0' };
 
-
 	if (!pr || (count > sizeof(state_string) - 1))
 		return -EINVAL;
 

+ 7 - 26
drivers/acpi/sbs.c

@@ -127,7 +127,7 @@ static int acpi_sbs_resume(struct acpi_device *device);
 static struct acpi_driver acpi_sbs_driver = {
 	.name = "sbs",
 	.class = ACPI_SBS_CLASS,
-	.ids = ACPI_SBS_HID,
+	.ids = "ACPI0001,ACPI0005",
 	.ops = {
 		.add = acpi_sbs_add,
 		.remove = acpi_sbs_remove,
@@ -176,10 +176,8 @@ 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;
@@ -511,7 +509,7 @@ static int acpi_sbsm_get_info(struct acpi_sbs *sbs)
 				"acpi_sbs_read_word() failed"));
 		goto end;
 	}
-
+	sbs->sbsm_present = 1;
 	sbs->sbsm_batteries_supported = battery_system_info & 0x000f;
 
       end:
@@ -1630,13 +1628,12 @@ static int acpi_sbs_add(struct acpi_device *device)
 {
 	struct acpi_sbs *sbs = NULL;
 	int result = 0, remove_result = 0;
-	unsigned long sbs_obj;
 	int id;
 	acpi_status status = AE_OK;
 	unsigned long val;
 
 	status =
-	    acpi_evaluate_integer(device->parent->handle, "_EC", NULL, &val);
+	    acpi_evaluate_integer(device->handle, "_EC", NULL, &val);
 	if (ACPI_FAILURE(status)) {
 		ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Error obtaining _EC"));
 		return -EIO;
@@ -1653,7 +1650,7 @@ static int acpi_sbs_add(struct acpi_device *device)
 
 	sbs_mutex_lock(sbs);
 
-	sbs->base = (val & 0xff00ull) >> 8;
+	sbs->base = 0xff & (val >> 8);
 	sbs->device = device;
 
 	strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME);
@@ -1665,24 +1662,10 @@ static int acpi_sbs_add(struct acpi_device *device)
 		ACPI_EXCEPTION((AE_INFO, AE_ERROR, "acpi_ac_add() failed"));
 		goto end;
 	}
-	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_EXCEPTION((AE_INFO, AE_ERROR,
-					"acpi_sbsm_get_info() failed"));
-			goto end;
-		}
-		sbs->sbsm_present = 1;
-	}
 
-	if (sbs->sbsm_present == 0) {
+	acpi_sbsm_get_info(sbs);
+
+	if (!sbs->sbsm_present) {
 		result = acpi_battery_add(sbs, 0);
 		if (result) {
 			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
@@ -1702,8 +1685,6 @@ static int acpi_sbs_add(struct acpi_device *device)
 		}
 	}
 
-	sbs->handle = device->handle;
-
 	init_timer(&sbs->update_timer);
 	result = acpi_check_update_proc(sbs);
 	if (result)

+ 0 - 5
drivers/acpi/sleep/main.c

@@ -210,11 +210,6 @@ static void acpi_hibernation_finish(void)
 
 	/* reset firmware waking vector */
 	acpi_set_firmware_waking_vector((acpi_physical_address) 0);
-
-	if (init_8259A_after_S1) {
-		printk("Broken toshiba laptop -> kicking interrupts\n");
-		init_8259A(0);
-	}
 }
 
 static int acpi_hibernation_pre_restore(void)

+ 144 - 21
drivers/acpi/system.c

@@ -39,15 +39,12 @@ ACPI_MODULE_NAME("system");
 
 #define ACPI_SYSTEM_CLASS		"system"
 #define ACPI_SYSTEM_DEVICE_NAME		"System"
-#define ACPI_SYSTEM_FILE_INFO		"info"
-#define ACPI_SYSTEM_FILE_EVENT		"event"
-#define ACPI_SYSTEM_FILE_DSDT		"dsdt"
-#define ACPI_SYSTEM_FILE_FADT		"fadt"
 
 /*
  * Make ACPICA version work as module param
  */
-static int param_get_acpica_version(char *buffer, struct kernel_param *kp) {
+static int param_get_acpica_version(char *buffer, struct kernel_param *kp)
+{
 	int result;
 
 	result = sprintf(buffer, "%x", ACPI_CA_VERSION);
@@ -57,10 +54,127 @@ static int param_get_acpica_version(char *buffer, struct kernel_param *kp) {
 
 module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444);
 
+/* --------------------------------------------------------------------------
+                              FS Interface (/sys)
+   -------------------------------------------------------------------------- */
+static LIST_HEAD(acpi_table_attr_list);
+static struct kobject tables_kobj;
+
+struct acpi_table_attr {
+	struct bin_attribute attr;
+	char name[8];
+	int instance;
+	struct list_head node;
+};
+
+static ssize_t acpi_table_show(struct kobject *kobj,
+			       struct bin_attribute *bin_attr, char *buf,
+			       loff_t offset, size_t count)
+{
+	struct acpi_table_attr *table_attr =
+	    container_of(bin_attr, struct acpi_table_attr, attr);
+	struct acpi_table_header *table_header = NULL;
+	acpi_status status;
+	ssize_t ret_count = count;
+
+	status =
+	    acpi_get_table(table_attr->name, table_attr->instance,
+			   &table_header);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	if (offset >= table_header->length) {
+		ret_count = 0;
+		goto end;
+	}
+
+	if (offset + ret_count > table_header->length)
+		ret_count = table_header->length - offset;
+
+	memcpy(buf, ((char *)table_header) + offset, ret_count);
+
+      end:
+	return ret_count;
+}
+
+static void acpi_table_attr_init(struct acpi_table_attr *table_attr,
+				 struct acpi_table_header *table_header)
+{
+	struct acpi_table_header *header = NULL;
+	struct acpi_table_attr *attr = NULL;
+
+	memcpy(table_attr->name, table_header->signature, ACPI_NAME_SIZE);
+
+	list_for_each_entry(attr, &acpi_table_attr_list, node) {
+		if (!memcmp(table_header->signature, attr->name,
+			    ACPI_NAME_SIZE))
+			if (table_attr->instance < attr->instance)
+				table_attr->instance = attr->instance;
+	}
+	table_attr->instance++;
+
+	if (table_attr->instance > 1 || (table_attr->instance == 1 &&
+					 !acpi_get_table(table_header->
+							 signature, 2,
+							 &header)))
+		sprintf(table_attr->name + 4, "%d", table_attr->instance);
+
+	table_attr->attr.size = 0;
+	table_attr->attr.read = acpi_table_show;
+	table_attr->attr.attr.name = table_attr->name;
+	table_attr->attr.attr.mode = 0444;
+	table_attr->attr.attr.owner = THIS_MODULE;
+
+	return;
+}
+
+static int acpi_system_sysfs_init(void)
+{
+	struct acpi_table_attr *table_attr;
+	struct acpi_table_header *table_header = NULL;
+	int table_index = 0;
+	int result;
+
+	tables_kobj.parent = &acpi_subsys.kobj;
+	kobject_set_name(&tables_kobj, "tables");
+	result = kobject_register(&tables_kobj);
+	if (result)
+		return result;
+
+	do {
+		result = acpi_get_table_by_index(table_index, &table_header);
+		if (!result) {
+			table_index++;
+			table_attr = NULL;
+			table_attr =
+			    kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL);
+			if (!table_attr)
+				return -ENOMEM;
+
+			acpi_table_attr_init(table_attr, table_header);
+			result =
+			    sysfs_create_bin_file(&tables_kobj,
+						  &table_attr->attr);
+			if (result) {
+				kfree(table_attr);
+				return result;
+			} else
+				list_add_tail(&table_attr->node,
+					      &acpi_table_attr_list);
+		}
+	} while (!result);
+
+	return 0;
+}
+
 /* --------------------------------------------------------------------------
                               FS Interface (/proc)
    -------------------------------------------------------------------------- */
 #ifdef CONFIG_ACPI_PROCFS
+#define ACPI_SYSTEM_FILE_INFO		"info"
+#define ACPI_SYSTEM_FILE_EVENT		"event"
+#define ACPI_SYSTEM_FILE_DSDT		"dsdt"
+#define ACPI_SYSTEM_FILE_FADT		"fadt"
 
 static int acpi_system_read_info(struct seq_file *seq, void *offset)
 {
@@ -80,7 +194,6 @@ static const struct file_operations acpi_system_info_ops = {
 	.llseek = seq_lseek,
 	.release = single_release,
 };
-#endif
 
 static ssize_t acpi_system_read_dsdt(struct file *, char __user *, size_t,
 				     loff_t *);
@@ -97,13 +210,11 @@ acpi_system_read_dsdt(struct file *file,
 	struct acpi_table_header *dsdt = NULL;
 	ssize_t res;
 
-
 	status = acpi_get_table(ACPI_SIG_DSDT, 1, &dsdt);
 	if (ACPI_FAILURE(status))
 		return -ENODEV;
 
-	res = simple_read_from_buffer(buffer, count, ppos,
-				      dsdt, dsdt->length);
+	res = simple_read_from_buffer(buffer, count, ppos, dsdt, dsdt->length);
 
 	return res;
 }
@@ -123,28 +234,21 @@ acpi_system_read_fadt(struct file *file,
 	struct acpi_table_header *fadt = NULL;
 	ssize_t res;
 
-
 	status = acpi_get_table(ACPI_SIG_FADT, 1, &fadt);
 	if (ACPI_FAILURE(status))
 		return -ENODEV;
 
-	res = simple_read_from_buffer(buffer, count, ppos,
-				      fadt, fadt->length);
+	res = simple_read_from_buffer(buffer, count, ppos, fadt, fadt->length);
 
 	return res;
 }
 
-static int __init acpi_system_init(void)
+static int acpi_system_procfs_init(void)
 {
 	struct proc_dir_entry *entry;
 	int error = 0;
 	char *name;
 
-
-	if (acpi_disabled)
-		return 0;
-
-#ifdef CONFIG_ACPI_PROCFS
 	/* 'info' [R] */
 	name = ACPI_SYSTEM_FILE_INFO;
 	entry = create_proc_entry(name, S_IRUGO, acpi_root_dir);
@@ -153,7 +257,6 @@ static int __init acpi_system_init(void)
 	else {
 		entry->proc_fops = &acpi_system_info_ops;
 	}
-#endif
 
 	/* 'dsdt' [R] */
 	name = ACPI_SYSTEM_FILE_DSDT;
@@ -177,12 +280,32 @@ static int __init acpi_system_init(void)
       Error:
 	remove_proc_entry(ACPI_SYSTEM_FILE_FADT, acpi_root_dir);
 	remove_proc_entry(ACPI_SYSTEM_FILE_DSDT, acpi_root_dir);
-#ifdef CONFIG_ACPI_PROCFS
 	remove_proc_entry(ACPI_SYSTEM_FILE_INFO, acpi_root_dir);
-#endif
 
 	error = -EFAULT;
 	goto Done;
 }
+#else
+static int acpi_system_procfs_init(void)
+{
+	return 0;
+}
+#endif
+
+static int __init acpi_system_init(void)
+{
+	int result = 0;
+
+	if (acpi_disabled)
+		return 0;
+
+	result = acpi_system_procfs_init();
+	if (result)
+		return result;
+
+	result = acpi_system_sysfs_init();
+
+	return result;
+}
 
 subsys_initcall(acpi_system_init);

+ 26 - 18
drivers/acpi/tables/tbfadt.c

@@ -211,14 +211,17 @@ void acpi_tb_parse_fadt(acpi_native_uint table_index, u8 flags)
  * DESCRIPTION: Get a local copy of the FADT and convert it to a common format.
  *              Performs validation on some important FADT fields.
  *
+ * NOTE:        We create a local copy of the FADT regardless of the version.
+ *
  ******************************************************************************/
 
 void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length)
 {
 
 	/*
-	 * Check if the FADT is larger than what we know about (ACPI 2.0 version).
-	 * Truncate the table, but make some noise.
+	 * Check if the FADT is larger than the largest table that we expect
+	 * (the ACPI 2.0/3.0 version). If so, truncate the table, and issue
+	 * a warning.
 	 */
 	if (length > sizeof(struct acpi_table_fadt)) {
 		ACPI_WARNING((AE_INFO,
@@ -227,10 +230,12 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length)
 			      sizeof(struct acpi_table_fadt)));
 	}
 
-	/* Copy the entire FADT locally. Zero first for tb_convert_fadt */
+	/* Clear the entire local FADT */
 
 	ACPI_MEMSET(&acpi_gbl_FADT, 0, sizeof(struct acpi_table_fadt));
 
+	/* Copy the original FADT, up to sizeof (struct acpi_table_fadt) */
+
 	ACPI_MEMCPY(&acpi_gbl_FADT, table,
 		    ACPI_MIN(length, sizeof(struct acpi_table_fadt)));
 
@@ -251,7 +256,7 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length)
  * RETURN:      None
  *
  * DESCRIPTION: Converts all versions of the FADT to a common internal format.
- *              -> Expand all 32-bit addresses to 64-bit.
+ *              Expand all 32-bit addresses to 64-bit.
  *
  * NOTE:        acpi_gbl_FADT must be of size (struct acpi_table_fadt),
  *              and must contain a copy of the actual FADT.
@@ -292,8 +297,23 @@ static void acpi_tb_convert_fadt(void)
 	}
 
 	/*
-	 * Expand the 32-bit V1.0 addresses to the 64-bit "X" generic address
-	 * structures as necessary.
+	 * For ACPI 1.0 FADTs (revision 1 or 2), ensure that reserved fields which
+	 * should be zero are indeed zero. This will workaround BIOSs that
+	 * inadvertently place values in these fields.
+	 *
+	 * The ACPI 1.0 reserved fields that will be zeroed are the bytes located at
+	 * offset 45, 55, 95, and the word located at offset 109, 110.
+	 */
+	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;
+	}
+
+	/*
+	 * Expand the ACPI 1.0 32-bit V1.0 addresses to the ACPI 2.0 64-bit "X"
+	 * generic address structures as necessary.
 	 */
 	for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) {
 		target =
@@ -349,18 +369,6 @@ static void acpi_tb_convert_fadt(void)
 		    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;
-	}
 }
 
 /******************************************************************************

+ 7 - 10
drivers/acpi/utilities/uteval.c

@@ -62,16 +62,13 @@ acpi_ut_translate_one_cid(union acpi_operand_object *obj_desc,
 static char *acpi_interfaces_supported[] = {
 	/* Operating System Vendor Strings */
 
-	"Windows 2000",
-	"Windows 2001",
-	"Windows 2001 SP0",
-	"Windows 2001 SP1",
-	"Windows 2001 SP2",
-	"Windows 2001 SP3",
-	"Windows 2001 SP4",
-	"Windows 2001.1",
-	"Windows 2001.1 SP1",	/* Added 03/2006 */
-	"Windows 2006",		/* Added 03/2006 */
+	"Windows 2000",		/* Windows 2000 */
+	"Windows 2001",		/* Windows XP */
+	"Windows 2001 SP1",	/* Windows XP SP1 */
+	"Windows 2001 SP2",	/* Windows XP SP2 */
+	"Windows 2001.1",	/* Windows Server 2003 */
+	"Windows 2001.1 SP1",	/* Windows Server 2003 SP1 - Added 03/2006 */
+	"Windows 2006",		/* Windows Vista - Added 03/2006 */
 
 	/* Feature Group Strings */
 

+ 82 - 38
drivers/acpi/video.c

@@ -33,6 +33,7 @@
 #include <linux/seq_file.h>
 
 #include <linux/backlight.h>
+#include <linux/video_output.h>
 #include <asm/uaccess.h>
 
 #include <acpi/acpi_bus.h>
@@ -169,6 +170,7 @@ struct acpi_video_device {
 	struct acpi_device *dev;
 	struct acpi_video_device_brightness *brightness;
 	struct backlight_device *backlight;
+	struct output_device *output_dev;
 };
 
 /* bus */
@@ -272,6 +274,10 @@ static int acpi_video_get_next_level(struct acpi_video_device *device,
 				     u32 level_current, u32 event);
 static void acpi_video_switch_brightness(struct acpi_video_device *device,
 					 int event);
+static int acpi_video_device_get_state(struct acpi_video_device *device,
+			    unsigned long *state);
+static int acpi_video_output_get(struct output_device *od);
+static int acpi_video_device_set_state(struct acpi_video_device *device, int state);
 
 /*backlight device sysfs support*/
 static int acpi_video_get_brightness(struct backlight_device *bd)
@@ -297,6 +303,28 @@ static struct backlight_ops acpi_backlight_ops = {
 	.update_status  = acpi_video_set_brightness,
 };
 
+/*video output device sysfs support*/
+static int acpi_video_output_get(struct output_device *od)
+{
+	unsigned long state;
+	struct acpi_video_device *vd =
+		(struct acpi_video_device *)class_get_devdata(&od->class_dev);
+	acpi_video_device_get_state(vd, &state);
+	return (int)state;
+}
+
+static int acpi_video_output_set(struct output_device *od)
+{
+	unsigned long state = od->request_state;
+	struct acpi_video_device *vd=
+		(struct acpi_video_device *)class_get_devdata(&od->class_dev);
+	return acpi_video_device_set_state(vd, state);
+}
+
+static struct output_properties acpi_output_properties = {
+	.set_state = acpi_video_output_set,
+	.get_status = acpi_video_output_get,
+};
 /* --------------------------------------------------------------------------
                                Video Management
    -------------------------------------------------------------------------- */
@@ -531,7 +559,6 @@ acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
 
 static void acpi_video_device_find_cap(struct acpi_video_device *device)
 {
-	acpi_integer status;
 	acpi_handle h_dummy1;
 	int i;
 	u32 max_level = 0;
@@ -565,50 +592,55 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
 		device->cap._DSS = 1;
 	}
 
-	status = acpi_video_device_lcd_query_levels(device, &obj);
-
-	if (obj && obj->type == ACPI_TYPE_PACKAGE && obj->package.count >= 2) {
-		int count = 0;
-		union acpi_object *o;
-
-		br = kzalloc(sizeof(*br), GFP_KERNEL);
-		if (!br) {
-			printk(KERN_ERR "can't allocate memory\n");
-		} else {
-			br->levels = kmalloc(obj->package.count *
-					     sizeof *(br->levels), GFP_KERNEL);
-			if (!br->levels)
-				goto out;
-
-			for (i = 0; i < obj->package.count; i++) {
-				o = (union acpi_object *)&obj->package.
-				    elements[i];
-				if (o->type != ACPI_TYPE_INTEGER) {
-					printk(KERN_ERR PREFIX "Invalid data\n");
-					continue;
-				}
-				br->levels[count] = (u32) o->integer.value;
-				if (br->levels[count] > max_level)
-					max_level = br->levels[count];
-				count++;
-			}
-		      out:
-			if (count < 2) {
-				kfree(br->levels);
-				kfree(br);
+	if (ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) {
+
+		if (obj->package.count >= 2) {
+			int count = 0;
+			union acpi_object *o;
+
+			br = kzalloc(sizeof(*br), GFP_KERNEL);
+			if (!br) {
+				printk(KERN_ERR "can't allocate memory\n");
 			} else {
-				br->count = count;
-				device->brightness = br;
-				ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-						  "found %d brightness levels\n",
-						  count));
+				br->levels = kmalloc(obj->package.count *
+						     sizeof *(br->levels), GFP_KERNEL);
+				if (!br->levels)
+					goto out;
+
+				for (i = 0; i < obj->package.count; i++) {
+					o = (union acpi_object *)&obj->package.
+					    elements[i];
+					if (o->type != ACPI_TYPE_INTEGER) {
+						printk(KERN_ERR PREFIX "Invalid data\n");
+						continue;
+					}
+					br->levels[count] = (u32) o->integer.value;
+
+					if (br->levels[count] > max_level)
+						max_level = br->levels[count];
+					count++;
+				}
+			      out:
+				if (count < 2) {
+					kfree(br->levels);
+					kfree(br);
+				} else {
+					br->count = count;
+					device->brightness = br;
+					ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+							  "found %d brightness levels\n",
+							  count));
+				}
 			}
 		}
+
+	} else {
+		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available LCD brightness level\n"));
 	}
 
 	kfree(obj);
 
-	if (device->cap._BCL && device->cap._BCM && device->cap._BQC){
+	if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){
 		unsigned long tmp;
 		static int count = 0;
 		char *name;
@@ -626,6 +658,17 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
 
 		kfree(name);
 	}
+	if (device->cap._DCS && device->cap._DSS){
+		static int count = 0;
+		char *name;
+		name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
+		if (!name)
+			return;
+		sprintf(name, "acpi_video%d", count++);
+		device->output_dev = video_output_register(name,
+				NULL, device, &acpi_output_properties);
+		kfree(name);
+	}
 	return;
 }
 
@@ -1669,6 +1712,7 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
 					    ACPI_DEVICE_NOTIFY,
 					    acpi_video_device_notify);
 	backlight_device_unregister(device->backlight);
+	video_output_unregister(device->output_dev);
 	return 0;
 }
 

+ 14 - 0
drivers/misc/Kconfig

@@ -150,6 +150,7 @@ config THINKPAD_ACPI
 	depends on X86 && ACPI
 	select BACKLIGHT_CLASS_DEVICE
 	select HWMON
+	select NVRAM
 	---help---
 	  This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
 	  support for Fn-Fx key combinations, Bluetooth control, video
@@ -196,4 +197,17 @@ config THINKPAD_ACPI_BAY
 
 	  If you are not sure, say Y here.
 
+config THINKPAD_ACPI_INPUT_ENABLED
+	bool "Enable input layer support by default"
+	depends on THINKPAD_ACPI
+	default y
+	---help---
+	  Enables hot key handling over the input layer by default.  If unset,
+	  the driver does not enable any hot key handling by default, and also
+	  starts up with a mostly empty keymap.
+
+	  If you are not sure, say Y here.  Say N to retain the deprecated
+	  behavior of ibm-acpi, and thinkpad-acpi for kernels up to 2.6.21.
+
+
 endif # MISC_DEVICES

+ 308 - 60
drivers/misc/sony-laptop.c

@@ -142,43 +142,124 @@ struct sony_laptop_keypress {
 	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 },
+/* Correspondance table between sonypi events
+ * and input layer indexes in the keymap
+ */
+static int sony_laptop_input_index[] = {
+	-1,	/* no event */
+	-1,	/* SONYPI_EVENT_JOGDIAL_DOWN */
+	-1,	/* SONYPI_EVENT_JOGDIAL_UP */
+	-1,	/* SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */
+	-1,	/* SONYPI_EVENT_JOGDIAL_UP_PRESSED */
+	-1,	/* SONYPI_EVENT_JOGDIAL_PRESSED */
+	-1,	/* SONYPI_EVENT_JOGDIAL_RELEASED */
+	 0,	/* SONYPI_EVENT_CAPTURE_PRESSED */
+	 1,	/* SONYPI_EVENT_CAPTURE_RELEASED */
+	 2,	/* SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
+	 3,	/* SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
+	 4,	/* SONYPI_EVENT_FNKEY_ESC */
+	 5,	/* SONYPI_EVENT_FNKEY_F1 */
+	 6,	/* SONYPI_EVENT_FNKEY_F2 */
+	 7,	/* SONYPI_EVENT_FNKEY_F3 */
+	 8,	/* SONYPI_EVENT_FNKEY_F4 */
+	 9,	/* SONYPI_EVENT_FNKEY_F5 */
+	10,	/* SONYPI_EVENT_FNKEY_F6 */
+	11,	/* SONYPI_EVENT_FNKEY_F7 */
+	12,	/* SONYPI_EVENT_FNKEY_F8 */
+	13,	/* SONYPI_EVENT_FNKEY_F9 */
+	14,	/* SONYPI_EVENT_FNKEY_F10 */
+	15,	/* SONYPI_EVENT_FNKEY_F11 */
+	16,	/* SONYPI_EVENT_FNKEY_F12 */
+	17,	/* SONYPI_EVENT_FNKEY_1 */
+	18,	/* SONYPI_EVENT_FNKEY_2 */
+	19,	/* SONYPI_EVENT_FNKEY_D */
+	20,	/* SONYPI_EVENT_FNKEY_E */
+	21,	/* SONYPI_EVENT_FNKEY_F */
+	22,	/* SONYPI_EVENT_FNKEY_S */
+	23,	/* SONYPI_EVENT_FNKEY_B */
+	24,	/* SONYPI_EVENT_BLUETOOTH_PRESSED */
+	25,	/* SONYPI_EVENT_PKEY_P1 */
+	26,	/* SONYPI_EVENT_PKEY_P2 */
+	27,	/* SONYPI_EVENT_PKEY_P3 */
+	28,	/* SONYPI_EVENT_BACK_PRESSED */
+	-1,	/* SONYPI_EVENT_LID_CLOSED */
+	-1,	/* SONYPI_EVENT_LID_OPENED */
+	29,	/* SONYPI_EVENT_BLUETOOTH_ON */
+	30,	/* SONYPI_EVENT_BLUETOOTH_OFF */
+	31,	/* SONYPI_EVENT_HELP_PRESSED */
+	32,	/* SONYPI_EVENT_FNKEY_ONLY */
+	33,	/* SONYPI_EVENT_JOGDIAL_FAST_DOWN */
+	34,	/* SONYPI_EVENT_JOGDIAL_FAST_UP */
+	35,	/* SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
+	36,	/* SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
+	37,	/* SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
+	38,	/* SONYPI_EVENT_JOGDIAL_VFAST_UP */
+	39,	/* SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
+	40,	/* SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
+	41,	/* SONYPI_EVENT_ZOOM_PRESSED */
+	42,	/* SONYPI_EVENT_THUMBPHRASE_PRESSED */
+	43,	/* SONYPI_EVENT_MEYE_FACE */
+	44,	/* SONYPI_EVENT_MEYE_OPPOSITE */
+	45,	/* SONYPI_EVENT_MEMORYSTICK_INSERT */
+	46,	/* SONYPI_EVENT_MEMORYSTICK_EJECT */
+	-1,	/* SONYPI_EVENT_ANYBUTTON_RELEASED */
+	-1,	/* SONYPI_EVENT_BATTERY_INSERT */
+	-1,	/* SONYPI_EVENT_BATTERY_REMOVE */
+	-1,	/* SONYPI_EVENT_FNKEY_RELEASED */
+	47,	/* SONYPI_EVENT_WIRELESS_ON */
+	48,	/* SONYPI_EVENT_WIRELESS_OFF */
+};
+
+static int sony_laptop_input_keycode_map[] = {
+	KEY_CAMERA,	/*  0 SONYPI_EVENT_CAPTURE_PRESSED */
+	KEY_RESERVED,	/*  1 SONYPI_EVENT_CAPTURE_RELEASED */
+	KEY_RESERVED,	/*  2 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
+	KEY_RESERVED,	/*  3 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
+	KEY_FN_ESC,	/*  4 SONYPI_EVENT_FNKEY_ESC */
+	KEY_FN_F1,	/*  5 SONYPI_EVENT_FNKEY_F1 */
+	KEY_FN_F2,	/*  6 SONYPI_EVENT_FNKEY_F2 */
+	KEY_FN_F3,	/*  7 SONYPI_EVENT_FNKEY_F3 */
+	KEY_FN_F4,	/*  8 SONYPI_EVENT_FNKEY_F4 */
+	KEY_FN_F5,	/*  9 SONYPI_EVENT_FNKEY_F5 */
+	KEY_FN_F6,	/* 10 SONYPI_EVENT_FNKEY_F6 */
+	KEY_FN_F7,	/* 11 SONYPI_EVENT_FNKEY_F7 */
+	KEY_FN_F8,	/* 12 SONYPI_EVENT_FNKEY_F8 */
+	KEY_FN_F9,	/* 13 SONYPI_EVENT_FNKEY_F9 */
+	KEY_FN_F10,	/* 14 SONYPI_EVENT_FNKEY_F10 */
+	KEY_FN_F11,	/* 15 SONYPI_EVENT_FNKEY_F11 */
+	KEY_FN_F12,	/* 16 SONYPI_EVENT_FNKEY_F12 */
+	KEY_FN_F1,	/* 17 SONYPI_EVENT_FNKEY_1 */
+	KEY_FN_F2,	/* 18 SONYPI_EVENT_FNKEY_2 */
+	KEY_FN_D,	/* 19 SONYPI_EVENT_FNKEY_D */
+	KEY_FN_E,	/* 20 SONYPI_EVENT_FNKEY_E */
+	KEY_FN_F,	/* 21 SONYPI_EVENT_FNKEY_F */
+	KEY_FN_S,	/* 22 SONYPI_EVENT_FNKEY_S */
+	KEY_FN_B,	/* 23 SONYPI_EVENT_FNKEY_B */
+	KEY_BLUETOOTH,	/* 24 SONYPI_EVENT_BLUETOOTH_PRESSED */
+	KEY_PROG1,	/* 25 SONYPI_EVENT_PKEY_P1 */
+	KEY_PROG2,	/* 26 SONYPI_EVENT_PKEY_P2 */
+	KEY_PROG3,	/* 27 SONYPI_EVENT_PKEY_P3 */
+	KEY_BACK,	/* 28 SONYPI_EVENT_BACK_PRESSED */
+	KEY_BLUETOOTH,	/* 29 SONYPI_EVENT_BLUETOOTH_ON */
+	KEY_BLUETOOTH,	/* 30 SONYPI_EVENT_BLUETOOTH_OFF */
+	KEY_HELP,	/* 31 SONYPI_EVENT_HELP_PRESSED */
+	KEY_FN,		/* 32 SONYPI_EVENT_FNKEY_ONLY */
+	KEY_RESERVED,	/* 33 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
+	KEY_RESERVED,	/* 34 SONYPI_EVENT_JOGDIAL_FAST_UP */
+	KEY_RESERVED,	/* 35 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
+	KEY_RESERVED,	/* 36 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
+	KEY_RESERVED,	/* 37 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
+	KEY_RESERVED,	/* 38 SONYPI_EVENT_JOGDIAL_VFAST_UP */
+	KEY_RESERVED,	/* 39 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
+	KEY_RESERVED,	/* 40 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
+	KEY_ZOOM,	/* 41 SONYPI_EVENT_ZOOM_PRESSED */
+	BTN_THUMB,	/* 42 SONYPI_EVENT_THUMBPHRASE_PRESSED */
+	KEY_RESERVED,	/* 43 SONYPI_EVENT_MEYE_FACE */
+	KEY_RESERVED,	/* 44 SONYPI_EVENT_MEYE_OPPOSITE */
+	KEY_RESERVED,	/* 45 SONYPI_EVENT_MEMORYSTICK_INSERT */
+	KEY_RESERVED,	/* 46 SONYPI_EVENT_MEMORYSTICK_EJECT */
+	KEY_WLAN,	/* 47 SONYPI_EVENT_WIRELESS_ON */
+	KEY_WLAN,	/* 48 SONYPI_EVENT_WIRELESS_OFF */
 };
 
 /* release buttons after a short delay if pressed */
@@ -202,7 +283,6 @@ 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 */
@@ -231,17 +311,22 @@ static void sony_laptop_report_input_event(u8 event)
 		break;
 
 	default:
-		for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++)
-			if (event == sony_laptop_inputkeys[i].sonypiev) {
+		if (event > ARRAY_SIZE (sony_laptop_input_keycode_map)) {
+			dprintk("sony_laptop_report_input_event, event not known: %d\n", event);
+			break;
+		}
+		if (sony_laptop_input_index[event] != -1) {
+			kp.key = sony_laptop_input_keycode_map[sony_laptop_input_index[event]];
+			if (kp.key != KEY_UNKNOWN)
 				kp.dev = key_dev;
-				kp.key = sony_laptop_inputkeys[i].inputev;
-				break;
-			}
+		}
 		break;
 	}
 
 	if (kp.dev) {
 		input_report_key(kp.dev, kp.key, 1);
+		/* we emit the scancode so we can always remap the key */
+		input_event(kp.dev, EV_MSC, MSC_SCAN, event);
 		input_sync(kp.dev);
 		kfifo_put(sony_laptop_input.fifo,
 			  (unsigned char *)&kp, sizeof(kp));
@@ -296,11 +381,18 @@ static int sony_laptop_setup_input(void)
 	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);
+	set_bit(EV_KEY, key_dev->evbit);
+	set_bit(EV_MSC, key_dev->evbit);
+	set_bit(MSC_SCAN, key_dev->mscbit);
+	key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]);
+	key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map);
+	key_dev->keycode = &sony_laptop_input_keycode_map;
+	for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) {
+		if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) {
+			set_bit(sony_laptop_input_keycode_map[i],
+				key_dev->keybit);
+		}
+	}
 
 	error = input_register_device(key_dev);
 	if (error)
@@ -487,6 +579,14 @@ SNC_HANDLE_NAMES(audiopower_set, "AZPW");
 SNC_HANDLE_NAMES(lanpower_get, "GLNP");
 SNC_HANDLE_NAMES(lanpower_set, "LNPW");
 
+SNC_HANDLE_NAMES(lidstate_get, "GLID");
+
+SNC_HANDLE_NAMES(indicatorlamp_get, "GILS");
+SNC_HANDLE_NAMES(indicatorlamp_set, "SILS");
+
+SNC_HANDLE_NAMES(gainbass_get, "GMGB");
+SNC_HANDLE_NAMES(gainbass_set, "CMGB");
+
 SNC_HANDLE_NAMES(PID_get, "GPID");
 
 SNC_HANDLE_NAMES(CTR_get, "GCTR");
@@ -507,6 +607,12 @@ static struct sony_nc_value sony_nc_values[] = {
 			boolean_validate, 0),
 	SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set,
 			boolean_validate, 1),
+	SNC_HANDLE(lidstate, snc_lidstate_get, NULL,
+			boolean_validate, 0),
+	SNC_HANDLE(indicatorlamp, snc_indicatorlamp_get, snc_indicatorlamp_set,
+			boolean_validate, 0),
+	SNC_HANDLE(gainbass, snc_gainbass_get, snc_gainbass_set,
+			boolean_validate, 0),
 	/* unknown methods */
 	SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1),
 	SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
@@ -688,14 +794,117 @@ static struct backlight_ops sony_backlight_ops = {
 	.get_brightness = sony_backlight_get_brightness,
 };
 
+/*
+ * New SNC-only Vaios event mapping to driver known keys
+ */
+struct sony_nc_event {
+	u8	data;
+	u8	event;
+};
+
+static struct sony_nc_event *sony_nc_events;
+
+/* Vaio C* --maybe also FE*, N* and AR* ?-- special init sequence
+ * for Fn keys
+ */
+static int sony_nc_C_enable(struct dmi_system_id *id)
+{
+	int result = 0;
+
+	printk(KERN_NOTICE DRV_PFX "detected %s\n", id->ident);
+
+	sony_nc_events = id->driver_data;
+
+	if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x4, &result) < 0
+			|| acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x2, &result) < 0
+			|| acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x10, &result) < 0
+			|| acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x0, &result) < 0
+			|| acpi_callsetfunc(sony_nc_acpi_handle, "SN03", 0x2, &result) < 0
+			|| acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x101, &result) < 0) {
+		printk(KERN_WARNING DRV_PFX "failed to initialize SNC, some "
+				"functionalities may be missing\n");
+		return 1;
+	}
+	return 0;
+}
+
+static struct sony_nc_event sony_C_events[] = {
+	{ 0x81, SONYPI_EVENT_FNKEY_F1 },
+	{ 0x01, SONYPI_EVENT_FNKEY_RELEASED },
+	{ 0x85, SONYPI_EVENT_FNKEY_F5 },
+	{ 0x05, SONYPI_EVENT_FNKEY_RELEASED },
+	{ 0x86, SONYPI_EVENT_FNKEY_F6 },
+	{ 0x06, SONYPI_EVENT_FNKEY_RELEASED },
+	{ 0x87, SONYPI_EVENT_FNKEY_F7 },
+	{ 0x07, SONYPI_EVENT_FNKEY_RELEASED },
+	{ 0x8A, SONYPI_EVENT_FNKEY_F10 },
+	{ 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
+	{ 0x8C, SONYPI_EVENT_FNKEY_F12 },
+	{ 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
+	{ 0, 0 },
+};
+
+/* SNC-only model map */
+struct dmi_system_id sony_nc_ids[] = {
+		{
+			.ident = "Sony Vaio FE Series",
+			.callback = sony_nc_C_enable,
+			.driver_data = sony_C_events,
+			.matches = {
+				DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+				DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FE"),
+			},
+		},
+		{
+			.ident = "Sony Vaio C Series",
+			.callback = sony_nc_C_enable,
+			.driver_data = sony_C_events,
+			.matches = {
+				DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+				DMI_MATCH(DMI_PRODUCT_NAME, "VGN-C"),
+			},
+		},
+		{ }
+};
+
 /*
  * ACPI callbacks
  */
 static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
 {
-	dprintk("sony_acpi_notify, event: %d\n", event);
-	sony_laptop_report_input_event(event);
-	acpi_bus_generate_event(sony_nc_acpi_device, 1, event);
+	struct sony_nc_event *evmap;
+	u32 ev = event;
+	int result;
+
+	if (ev == 0x92) {
+		/* read the key pressed from EC.GECR
+		 * A call to SN07 with 0x0202 will do it as well respecting
+		 * the current protocol on different OSes
+		 *
+		 * Note: the path for GECR may be
+		 *   \_SB.PCI0.LPCB.EC (C, FE, AR, N and friends)
+		 *   \_SB.PCI0.PIB.EC0 (VGN-FR notifications are sent directly, no GECR)
+		 *
+		 * TODO: we may want to do the same for the older GHKE -need
+		 *       dmi list- so this snippet may become one more callback.
+		 */
+		if (acpi_callsetfunc(handle, "SN07", 0x0202, &result) < 0)
+			dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev);
+		else
+			ev = result & 0xFF;
+	}
+
+	if (sony_nc_events)
+		for (evmap = sony_nc_events; evmap->event; evmap++) {
+			if (evmap->data == ev) {
+				ev = evmap->event;
+				break;
+			}
+		}
+
+	dprintk("sony_acpi_notify, event: 0x%.2x\n", ev);
+	sony_laptop_report_input_event(ev);
+	acpi_bus_generate_event(sony_nc_acpi_device, 1, ev);
 }
 
 static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
@@ -732,6 +941,10 @@ static int sony_nc_resume(struct acpi_device *device)
 			break;
 		}
 	}
+
+	/* re-initialize models with specific requirements */
+	dmi_check_system(sony_nc_ids);
+
 	return 0;
 }
 
@@ -750,6 +963,15 @@ static int sony_nc_add(struct acpi_device *device)
 
 	sony_nc_acpi_handle = device->handle;
 
+	/* read device status */
+	result = acpi_bus_get_status(device);
+	/* bail IFF the above call was successful and the device is not present */
+	if (!result && !device->status.present) {
+		dprintk("Device not present\n");
+		result = -ENODEV;
+		goto outwalk;
+	}
+
 	if (debug) {
 		status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
 					     1, sony_walk_callback, NULL, NULL);
@@ -760,6 +982,15 @@ static int sony_nc_add(struct acpi_device *device)
 		}
 	}
 
+	/* try to _INI the device if such method exists (ACPI spec 3.0-6.5.1
+	 * should be respected as we already checked for the device presence above */
+	if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, METHOD_NAME__INI, &handle))) {
+		dprintk("Invoking _INI\n");
+		if (ACPI_FAILURE(acpi_evaluate_object(sony_nc_acpi_handle, METHOD_NAME__INI,
+						NULL, NULL)))
+			dprintk("_INI Method failed\n");
+	}
+
 	/* setup input devices and helper fifo */
 	result = sony_laptop_setup_input();
 	if (result) {
@@ -772,7 +1003,7 @@ static int sony_nc_add(struct acpi_device *device)
 					     ACPI_DEVICE_NOTIFY,
 					     sony_acpi_notify, NULL);
 	if (ACPI_FAILURE(status)) {
-		printk(KERN_WARNING DRV_PFX "unable to install notify handler\n");
+		printk(KERN_WARNING DRV_PFX "unable to install notify handler (%u)\n", status);
 		result = -ENODEV;
 		goto outinput;
 	}
@@ -795,6 +1026,9 @@ static int sony_nc_add(struct acpi_device *device)
 
 	}
 
+	/* initialize models with specific requirements */
+	dmi_check_system(sony_nc_ids);
+
 	result = sony_pf_add();
 	if (result)
 		goto outbacklight;
@@ -908,7 +1142,9 @@ static struct acpi_driver sony_nc_driver = {
 #define SONYPI_DEVICE_TYPE2	0x00000002
 #define SONYPI_DEVICE_TYPE3	0x00000004
 
-#define SONY_PIC_EV_MASK	0xff
+#define SONYPI_TYPE1_OFFSET	0x04
+#define SONYPI_TYPE2_OFFSET	0x12
+#define SONYPI_TYPE3_OFFSET	0x12
 
 struct sony_pic_ioport {
 	struct acpi_resource_io	io;
@@ -922,6 +1158,7 @@ struct sony_pic_irq {
 
 struct sony_pic_dev {
 	int			model;
+	u16			evport_offset;
 	u8			camera_power;
 	u8			bluetooth_power;
 	u8			wwan_power;
@@ -1999,20 +2236,17 @@ end:
 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));
+	ev = inb_p(dev->cur_ioport->io.minimum);
+	data_mask = inb_p(dev->cur_ioport->io.minimum + dev->evport_offset);
 
-	dprintk("event (0x%.8x [%.2x] [%.2x]) at port 0x%.4x\n",
-			port_val, ev, data_mask, dev->cur_ioport->io.minimum);
+	dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
+			ev, data_mask, dev->cur_ioport->io.minimum, dev->evport_offset);
 
 	if (ev == 0x00 || ev == 0xff)
 		return IRQ_HANDLED;
@@ -2103,6 +2337,20 @@ static int sony_pic_add(struct acpi_device *device)
 	spic_dev.model = sony_pic_detect_device_type();
 	mutex_init(&spic_dev.lock);
 
+	/* model specific characteristics */
+	switch(spic_dev.model) {
+		case SONYPI_DEVICE_TYPE1:
+			spic_dev.evport_offset = SONYPI_TYPE1_OFFSET;
+			break;
+		case SONYPI_DEVICE_TYPE3:
+			spic_dev.evport_offset = SONYPI_TYPE3_OFFSET;
+			break;
+		case SONYPI_DEVICE_TYPE2:
+		default:
+			spic_dev.evport_offset = SONYPI_TYPE2_OFFSET;
+			break;
+	}
+
 	/* read _PRS resources */
 	result = sony_pic_possible_resources(device);
 	if (result) {

+ 526 - 76
drivers/misc/thinkpad_acpi.c

@@ -21,8 +21,8 @@
  *  02110-1301, USA.
  */
 
-#define IBM_VERSION "0.14"
-#define TPACPI_SYSFS_VERSION 0x000100
+#define IBM_VERSION "0.15"
+#define TPACPI_SYSFS_VERSION 0x010000
 
 /*
  *  Changelog:
@@ -92,6 +92,29 @@ MODULE_LICENSE("GPL");
 /* Please remove this in year 2009 */
 MODULE_ALIAS("ibm_acpi");
 
+/*
+ * DMI matching for module autoloading
+ *
+ * See http://thinkwiki.org/wiki/List_of_DMI_IDs
+ * See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads
+ *
+ * Only models listed in thinkwiki will be supported, so add yours
+ * if it is not there yet.
+ */
+#define IBM_BIOS_MODULE_ALIAS(__type) \
+	MODULE_ALIAS("dmi:bvnIBM:bvr" __type "ET??WW")
+
+/* Non-ancient thinkpads */
+MODULE_ALIAS("dmi:bvnIBM:*:svnIBM:*:pvrThinkPad*:rvnIBM:*");
+MODULE_ALIAS("dmi:bvnLENOVO:*:svnLENOVO:*:pvrThinkPad*:rvnLENOVO:*");
+
+/* Ancient thinkpad BIOSes have to be identified by
+ * BIOS type or model number, and there are far less
+ * BIOS types than model numbers... */
+IBM_BIOS_MODULE_ALIAS("I[B,D,H,I,M,N,O,T,W,V,Y,Z]");
+IBM_BIOS_MODULE_ALIAS("1[0,3,6,8,A-G,I,K,M-P,S,T]");
+IBM_BIOS_MODULE_ALIAS("K[U,X-Z]");
+
 #define __unused __attribute__ ((unused))
 
 /****************************************************************************
@@ -106,7 +129,7 @@ MODULE_ALIAS("ibm_acpi");
  * ACPI basic handles
  */
 
-static acpi_handle root_handle = NULL;
+static acpi_handle root_handle;
 
 #define IBM_HANDLE(object, parent, paths...)			\
 	static acpi_handle  object##_handle;			\
@@ -487,19 +510,36 @@ static char *next_cmd(char **cmds)
 /****************************************************************************
  ****************************************************************************
  *
- * Device model: hwmon and platform
+ * Device model: input, hwmon and platform
  *
  ****************************************************************************
  ****************************************************************************/
 
-static struct platform_device *tpacpi_pdev = NULL;
-static struct class_device *tpacpi_hwmon = NULL;
+static struct platform_device *tpacpi_pdev;
+static struct class_device *tpacpi_hwmon;
+static struct input_dev *tpacpi_inputdev;
+
+
+static int tpacpi_resume_handler(struct platform_device *pdev)
+{
+	struct ibm_struct *ibm, *itmp;
+
+	list_for_each_entry_safe(ibm, itmp,
+				 &tpacpi_all_drivers,
+				 all_drivers) {
+		if (ibm->resume)
+			(ibm->resume)();
+	}
+
+	return 0;
+}
 
 static struct platform_driver tpacpi_pdriver = {
 	.driver = {
 		.name = IBM_DRVR_NAME,
 		.owner = THIS_MODULE,
 	},
+	.resume = tpacpi_resume_handler,
 };
 
 
@@ -677,9 +717,19 @@ 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);
+	printk(IBM_INFO "ThinkPad BIOS %s, EC %s\n",
+		(thinkpad_id.bios_version_str) ?
+			thinkpad_id.bios_version_str : "unknown",
+		(thinkpad_id.ec_version_str) ?
+			thinkpad_id.ec_version_str : "unknown");
+
+	if (thinkpad_id.vendor && thinkpad_id.model_str)
+		printk(IBM_INFO "%s %s\n",
+			(thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ?
+				"IBM" : ((thinkpad_id.vendor ==
+						PCI_VENDOR_ID_LENOVO) ?
+					"Lenovo" : "Unknown vendor"),
+			thinkpad_id.model_str);
 
 	return 0;
 }
@@ -704,16 +754,28 @@ static struct ibm_struct thinkpad_acpi_driver_data = {
  */
 
 static int hotkey_orig_status;
-static int hotkey_orig_mask;
+static u32 hotkey_orig_mask;
+static u32 hotkey_all_mask;
+static u32 hotkey_reserved_mask;
+
+static u16 *hotkey_keycode_map;
 
-static struct attribute_set *hotkey_dev_attributes = NULL;
+static struct attribute_set *hotkey_dev_attributes;
+
+static int hotkey_get_wlsw(int *status)
+{
+	if (!acpi_evalf(hkey_handle, status, "WLSW", "d"))
+		return -EIO;
+	return 0;
+}
 
 /* sysfs hotkey enable ------------------------------------------------- */
 static ssize_t hotkey_enable_show(struct device *dev,
 			   struct device_attribute *attr,
 			   char *buf)
 {
-	int res, status, mask;
+	int res, status;
+	u32 mask;
 
 	res = hotkey_get(&status, &mask);
 	if (res)
@@ -727,7 +789,8 @@ static ssize_t hotkey_enable_store(struct device *dev,
 			    const char *buf, size_t count)
 {
 	unsigned long t;
-	int res, status, mask;
+	int res, status;
+	u32 mask;
 
 	if (parse_strtoul(buf, 1, &t))
 		return -EINVAL;
@@ -748,13 +811,14 @@ static ssize_t hotkey_mask_show(struct device *dev,
 			   struct device_attribute *attr,
 			   char *buf)
 {
-	int res, status, mask;
+	int res, status;
+	u32 mask;
 
 	res = hotkey_get(&status, &mask);
 	if (res)
 		return res;
 
-	return snprintf(buf, PAGE_SIZE, "0x%04x\n", mask);
+	return snprintf(buf, PAGE_SIZE, "0x%08x\n", mask);
 }
 
 static ssize_t hotkey_mask_store(struct device *dev,
@@ -762,9 +826,10 @@ static ssize_t hotkey_mask_store(struct device *dev,
 			    const char *buf, size_t count)
 {
 	unsigned long t;
-	int res, status, mask;
+	int res, status;
+	u32 mask;
 
-	if (parse_strtoul(buf, 0xffff, &t))
+	if (parse_strtoul(buf, 0xffffffffUL, &t))
 		return -EINVAL;
 
 	res = hotkey_get(&status, &mask);
@@ -794,26 +859,123 @@ 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);
+	return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask);
 }
 
 static struct device_attribute dev_attr_hotkey_bios_mask =
 	__ATTR(hotkey_bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL);
 
+/* sysfs hotkey all_mask ----------------------------------------------- */
+static ssize_t hotkey_all_mask_show(struct device *dev,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_all_mask);
+}
+
+static struct device_attribute dev_attr_hotkey_all_mask =
+	__ATTR(hotkey_all_mask, S_IRUGO, hotkey_all_mask_show, NULL);
+
+/* sysfs hotkey recommended_mask --------------------------------------- */
+static ssize_t hotkey_recommended_mask_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%08x\n",
+			hotkey_all_mask & ~hotkey_reserved_mask);
+}
+
+static struct device_attribute dev_attr_hotkey_recommended_mask =
+	__ATTR(hotkey_recommended_mask, S_IRUGO,
+		hotkey_recommended_mask_show, NULL);
+
+/* sysfs hotkey radio_sw ----------------------------------------------- */
+static ssize_t hotkey_radio_sw_show(struct device *dev,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	int res, s;
+	res = hotkey_get_wlsw(&s);
+	if (res < 0)
+		return res;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", !!s);
+}
+
+static struct device_attribute dev_attr_hotkey_radio_sw =
+	__ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_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,
+	&dev_attr_hotkey_all_mask.attr,
+	&dev_attr_hotkey_recommended_mask.attr,
 };
 
 static int __init hotkey_init(struct ibm_init_struct *iibm)
 {
-	int res;
+
+	static u16 ibm_keycode_map[] __initdata = {
+		/* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
+		KEY_FN_F1,	KEY_FN_F2,	KEY_COFFEE,	KEY_SLEEP,
+		KEY_WLAN,	KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
+		KEY_FN_F9,	KEY_FN_F10,	KEY_FN_F11,	KEY_SUSPEND,
+		/* Scan codes 0x0C to 0x0F: Other ACPI HKEY hot keys */
+		KEY_UNKNOWN,	/* 0x0C: FN+BACKSPACE */
+		KEY_UNKNOWN,	/* 0x0D: FN+INSERT */
+		KEY_UNKNOWN,	/* 0x0E: FN+DELETE */
+		KEY_RESERVED,	/* 0x0F: FN+HOME (brightness up) */
+		/* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */
+		KEY_RESERVED,	/* 0x10: FN+END (brightness down) */
+		KEY_RESERVED,	/* 0x11: FN+PGUP (thinklight toggle) */
+		KEY_UNKNOWN,	/* 0x12: FN+PGDOWN */
+		KEY_ZOOM,	/* 0x13: FN+SPACE (zoom) */
+		KEY_RESERVED,	/* 0x14: VOLUME UP */
+		KEY_RESERVED,	/* 0x15: VOLUME DOWN */
+		KEY_RESERVED,	/* 0x16: MUTE */
+		KEY_VENDOR,	/* 0x17: Thinkpad/AccessIBM/Lenovo */
+		/* (assignments unknown, please report if found) */
+		KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+		KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+	};
+	static u16 lenovo_keycode_map[] __initdata = {
+		/* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
+		KEY_FN_F1,	KEY_COFFEE,	KEY_BATTERY,	KEY_SLEEP,
+		KEY_WLAN,	KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
+		KEY_FN_F9,	KEY_FN_F10,	KEY_FN_F11,	KEY_SUSPEND,
+		/* Scan codes 0x0C to 0x0F: Other ACPI HKEY hot keys */
+		KEY_UNKNOWN,	/* 0x0C: FN+BACKSPACE */
+		KEY_UNKNOWN,	/* 0x0D: FN+INSERT */
+		KEY_UNKNOWN,	/* 0x0E: FN+DELETE */
+		KEY_BRIGHTNESSUP,	/* 0x0F: FN+HOME (brightness up) */
+		/* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */
+		KEY_BRIGHTNESSDOWN,	/* 0x10: FN+END (brightness down) */
+		KEY_RESERVED,	/* 0x11: FN+PGUP (thinklight toggle) */
+		KEY_UNKNOWN,	/* 0x12: FN+PGDOWN */
+		KEY_ZOOM,	/* 0x13: FN+SPACE (zoom) */
+		KEY_RESERVED,	/* 0x14: VOLUME UP */
+		KEY_RESERVED,	/* 0x15: VOLUME DOWN */
+		KEY_RESERVED,	/* 0x16: MUTE */
+		KEY_VENDOR,	/* 0x17: Thinkpad/AccessIBM/Lenovo */
+		/* (assignments unknown, please report if found) */
+		KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+		KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+	};
+
+#define TPACPI_HOTKEY_MAP_LEN		ARRAY_SIZE(ibm_keycode_map)
+#define TPACPI_HOTKEY_MAP_SIZE		sizeof(ibm_keycode_map)
+#define TPACPI_HOTKEY_MAP_TYPESIZE	sizeof(ibm_keycode_map[0])
+
+	int res, i;
+	int status;
 
 	vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n");
 
+	BUG_ON(!tpacpi_inputdev);
+
 	IBM_ACPIHANDLE_INIT(hkey);
 	mutex_init(&hotkey_mutex);
 
@@ -824,7 +986,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
 		str_supported(tp_features.hotkey));
 
 	if (tp_features.hotkey) {
-		hotkey_dev_attributes = create_attr_set(4, NULL);
+		hotkey_dev_attributes = create_attr_set(7, NULL);
 		if (!hotkey_dev_attributes)
 			return -ENOMEM;
 		res = add_to_attr_set(hotkey_dev_attributes,
@@ -840,19 +1002,92 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
 		vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
 			str_supported(tp_features.hotkey_mask));
 
+		if (tp_features.hotkey_mask) {
+			/* MHKA available in A31, R40, R40e, T4x, X31, and later */
+			if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
+					"MHKA", "qd"))
+				hotkey_all_mask = 0x080cU; /* FN+F12, FN+F4, FN+F3 */
+		}
+
 		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));
 		}
+
+		/* Not all thinkpads have a hardware radio switch */
+		if (!res && acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
+			tp_features.hotkey_wlsw = 1;
+			printk(IBM_INFO
+				"radio switch found; radios are %s\n",
+				enabled(status, 0));
+			res = add_to_attr_set(hotkey_dev_attributes,
+					&dev_attr_hotkey_radio_sw.attr);
+		}
+
 		if (!res)
 			res = register_attr_set_with_sysfs(
 					hotkey_dev_attributes,
 					&tpacpi_pdev->dev.kobj);
+		if (res)
+			return res;
+
+		/* Set up key map */
+
+		hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,
+						GFP_KERNEL);
+		if (!hotkey_keycode_map) {
+			printk(IBM_ERR "failed to allocate memory for key map\n");
+			return -ENOMEM;
+		}
+
+		if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
+			dbg_printk(TPACPI_DBG_INIT,
+				   "using Lenovo default hot key map\n");
+			memcpy(hotkey_keycode_map, &lenovo_keycode_map,
+				TPACPI_HOTKEY_MAP_SIZE);
+		} else {
+			dbg_printk(TPACPI_DBG_INIT,
+				   "using IBM default hot key map\n");
+			memcpy(hotkey_keycode_map, &ibm_keycode_map,
+				TPACPI_HOTKEY_MAP_SIZE);
+		}
 
+#ifndef CONFIG_THINKPAD_ACPI_INPUT_ENABLED
+		for (i = 0; i < 12; i++)
+			hotkey_keycode_map[i] = KEY_UNKNOWN;
+#endif /* ! CONFIG_THINKPAD_ACPI_INPUT_ENABLED */
+
+		set_bit(EV_KEY, tpacpi_inputdev->evbit);
+		set_bit(EV_MSC, tpacpi_inputdev->evbit);
+		set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
+		tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
+		tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
+		tpacpi_inputdev->keycode = hotkey_keycode_map;
+		for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
+			if (hotkey_keycode_map[i] != KEY_RESERVED) {
+				set_bit(hotkey_keycode_map[i],
+					tpacpi_inputdev->keybit);
+			} else {
+				if (i < sizeof(hotkey_reserved_mask)*8)
+					hotkey_reserved_mask |= 1 << i;
+			}
+		}
+
+		if (tp_features.hotkey_wlsw) {
+			set_bit(EV_SW, tpacpi_inputdev->evbit);
+			set_bit(SW_RADIO, tpacpi_inputdev->swbit);
+		}
+
+#ifdef CONFIG_THINKPAD_ACPI_INPUT_ENABLED
+		dbg_printk(TPACPI_DBG_INIT,
+				"enabling hot key handling\n");
+		res = hotkey_set(1, (hotkey_all_mask & ~hotkey_reserved_mask)
+					| hotkey_orig_mask);
 		if (res)
 			return res;
+#endif /* CONFIG_THINKPAD_ACPI_INPUT_ENABLED */
 	}
 
 	return (tp_features.hotkey)? 0 : 1;
@@ -875,22 +1110,101 @@ static void hotkey_exit(void)
 	}
 }
 
+static void tpacpi_input_send_key(unsigned int scancode,
+				  unsigned int keycode)
+{
+	if (keycode != KEY_RESERVED) {
+		input_report_key(tpacpi_inputdev, keycode, 1);
+		if (keycode == KEY_UNKNOWN)
+			input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
+				    scancode);
+		input_sync(tpacpi_inputdev);
+
+		input_report_key(tpacpi_inputdev, keycode, 0);
+		if (keycode == KEY_UNKNOWN)
+			input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
+				    scancode);
+		input_sync(tpacpi_inputdev);
+	}
+}
+
+static void tpacpi_input_send_radiosw(void)
+{
+	int wlsw;
+
+	if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw))
+		input_report_switch(tpacpi_inputdev,
+				    SW_RADIO, !!wlsw);
+}
+
 static void hotkey_notify(struct ibm_struct *ibm, u32 event)
 {
-	int hkey;
+	u32 hkey;
+	unsigned int keycode, scancode;
+	int sendacpi = 1;
+
+	if (event == 0x80 && acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {
+		if (tpacpi_inputdev->users > 0) {
+			switch (hkey >> 12) {
+			case 1:
+				/* 0x1000-0x1FFF: key presses */
+				scancode = hkey & 0xfff;
+				if (scancode > 0 && scancode < 0x21) {
+					scancode--;
+					keycode = hotkey_keycode_map[scancode];
+					tpacpi_input_send_key(scancode, keycode);
+					sendacpi = (keycode == KEY_RESERVED
+						|| keycode == KEY_UNKNOWN);
+				} else {
+					printk(IBM_ERR
+					       "hotkey 0x%04x out of range for keyboard map\n",
+					       hkey);
+				}
+				break;
+			case 5:
+				/* 0x5000-0x5FFF: LID */
+				/* we don't handle it through this path, just
+				 * eat up known LID events */
+				if (hkey != 0x5001 && hkey != 0x5002) {
+					printk(IBM_ERR
+						"unknown LID-related hotkey event: 0x%04x\n",
+						hkey);
+				}
+				break;
+			case 7:
+				/* 0x7000-0x7FFF: misc */
+				if (tp_features.hotkey_wlsw && hkey == 0x7000) {
+						tpacpi_input_send_radiosw();
+						sendacpi = 0;
+					break;
+				}
+				/* fallthrough to default */
+			default:
+				/* case 2: dock-related */
+				/*	0x2305 - T43 waking up due to bay lever eject while aslept */
+				/* case 3: ultra-bay related. maybe bay in dock? */
+				/*	0x3003 - T43 after wake up by bay lever eject (0x2305) */
+				printk(IBM_NOTICE "unhandled hotkey event 0x%04x\n", 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);
+		if (sendacpi)
+			acpi_bus_generate_event(ibm->acpi->device, event, hkey);
+	} else {
+		printk(IBM_ERR "unknown hotkey notification event %d\n", event);
 		acpi_bus_generate_event(ibm->acpi->device, event, 0);
 	}
 }
 
+static void hotkey_resume(void)
+{
+	tpacpi_input_send_radiosw();
+}
+
 /*
  * Call with hotkey_mutex held
  */
-static int hotkey_get(int *status, int *mask)
+static int hotkey_get(int *status, u32 *mask)
 {
 	if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
 		return -EIO;
@@ -905,7 +1219,7 @@ static int hotkey_get(int *status, int *mask)
 /*
  * Call with hotkey_mutex held
  */
-static int hotkey_set(int status, int mask)
+static int hotkey_set(int status, u32 mask)
 {
 	int i;
 
@@ -926,7 +1240,8 @@ static int hotkey_set(int status, int mask)
 /* procfs -------------------------------------------------------------- */
 static int hotkey_read(char *p)
 {
-	int res, status, mask;
+	int res, status;
+	u32 mask;
 	int len = 0;
 
 	if (!tp_features.hotkey) {
@@ -944,7 +1259,7 @@ static int hotkey_read(char *p)
 
 	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, "mask:\t\t0x%08x\n", mask);
 		len += sprintf(p + len,
 			       "commands:\tenable, disable, reset, <mask>\n");
 	} else {
@@ -957,7 +1272,8 @@ static int hotkey_read(char *p)
 
 static int hotkey_write(char *buf)
 {
-	int res, status, mask;
+	int res, status;
+	u32 mask;
 	char *cmd;
 	int do_cmd = 0;
 
@@ -1012,6 +1328,7 @@ static struct ibm_struct hotkey_driver_data = {
 	.read = hotkey_read,
 	.write = hotkey_write,
 	.exit = hotkey_exit,
+	.resume = hotkey_resume,
 	.acpi = &ibm_hotkey_acpidriver,
 };
 
@@ -1770,7 +2087,10 @@ static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
 	 .type = ACPI_SYSTEM_NOTIFY,
 	},
 	{
-	 .hid = IBM_PCI_HID,
+	/* THIS ONE MUST NEVER BE USED FOR DRIVER AUTOLOADING.
+	 * We just use it to get notifications of dock hotplug
+	 * in very old thinkpads */
+	 .hid = PCI_ROOT_HID_STRING,
 	 .notify = dock_notify,
 	 .handle = &pci_handle,
 	 .type = ACPI_SYSTEM_NOTIFY,
@@ -1829,7 +2149,7 @@ static int __init dock_init2(struct ibm_init_struct *iibm)
 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);
+	int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, PCI_ROOT_HID_STRING);
 
 	if (event == 1 && !pci)	/* 570 */
 		acpi_bus_generate_event(ibm->acpi->device, event, 1);	/* button */
@@ -2389,7 +2709,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
 
 	acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
 
-	if (ibm_thinkpad_ec_found && experimental) {
+	if (thinkpad_id.ec_model) {
 		/*
 		 * Direct EC access mode: sensors at registers
 		 * 0x78-0x7F, 0xC0-0xC7.  Registers return 0x00 for
@@ -2533,6 +2853,8 @@ static int thermal_get_sensor(int idx, s32 *value)
 			snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
 			if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
 				return -EIO;
+			if (t > 127 || t < -127)
+				t = TP_EC_THERMAL_TMP_NA;
 			*value = t * 1000;
 			return 0;
 		}
@@ -2671,22 +2993,39 @@ static struct ibm_struct ecdump_driver_data = {
  * Backlight/brightness subdriver
  */
 
-static struct backlight_device *ibm_backlight_device = NULL;
+static struct backlight_device *ibm_backlight_device;
 
 static struct backlight_ops ibm_backlight_data = {
         .get_brightness = brightness_get,
         .update_status  = brightness_update_status,
 };
 
+static struct mutex brightness_mutex;
+
 static int __init brightness_init(struct ibm_init_struct *iibm)
 {
 	int b;
 
 	vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
 
+	mutex_init(&brightness_mutex);
+
+	if (!brightness_mode) {
+		if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO)
+			brightness_mode = 2;
+		else
+			brightness_mode = 3;
+
+		dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n",
+			brightness_mode);
+	}
+
+	if (brightness_mode > 3)
+		return -EINVAL;
+
 	b = brightness_get(NULL);
 	if (b < 0)
-		return b;
+		return 1;
 
 	ibm_backlight_device = backlight_device_register(
 					TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
@@ -2722,34 +3061,79 @@ static int brightness_update_status(struct backlight_device *bd)
 				bd->props.brightness : 0);
 }
 
+/*
+ * ThinkPads can read brightness from two places: EC 0x31, or
+ * CMOS NVRAM byte 0x5E, bits 0-3.
+ */
 static int brightness_get(struct backlight_device *bd)
 {
-	u8 level;
-	if (!acpi_ec_read(brightness_offset, &level))
-		return -EIO;
+	u8 lec = 0, lcmos = 0, level = 0;
 
-	level &= 0x7;
+	if (brightness_mode & 1) {
+		if (!acpi_ec_read(brightness_offset, &lec))
+			return -EIO;
+		lec &= 7;
+		level = lec;
+	};
+	if (brightness_mode & 2) {
+		lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
+			 & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
+			>> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
+		level = lcmos;
+	}
+
+	if (brightness_mode == 3 && lec != lcmos) {
+		printk(IBM_ERR
+			"CMOS NVRAM (%u) and EC (%u) do not agree "
+			"on display brightness level\n",
+			(unsigned int) lcmos,
+			(unsigned int) lec);
+		return -EIO;
+	}
 
 	return level;
 }
 
 static int brightness_set(int value)
 {
-	int cmos_cmd, inc, i;
-	int current_value = brightness_get(NULL);
+	int cmos_cmd, inc, i, res;
+	int current_value;
+
+	if (value > 7)
+		return -EINVAL;
 
-	value &= 7;
+	res = mutex_lock_interruptible(&brightness_mutex);
+	if (res < 0)
+		return res;
+
+	current_value = brightness_get(NULL);
+	if (current_value < 0) {
+		res = current_value;
+		goto errout;
+	}
 
-	cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN;
+	cmos_cmd = value > current_value ?
+			TP_CMOS_BRIGHTNESS_UP :
+			TP_CMOS_BRIGHTNESS_DOWN;
 	inc = value > current_value ? 1 : -1;
+
+	res = 0;
 	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;
+		if ((brightness_mode & 2) &&
+		    issue_thinkpad_cmos_command(cmos_cmd)) {
+			res = -EIO;
+			goto errout;
+		}
+		if ((brightness_mode & 1) &&
+		    !acpi_ec_write(brightness_offset, i + inc)) {
+			res = -EIO;
+			goto errout;;
+		}
 	}
 
-	return 0;
+errout:
+	mutex_unlock(&brightness_mutex);
+	return res;
 }
 
 static int brightness_read(char *p)
@@ -3273,20 +3657,19 @@ static int __init fan_init(struct ibm_init_struct *iibm)
 			 * 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;
+			if (fan_control_initial_status == 0x07) {
+				switch (thinkpad_id.ec_model) {
+				case 0x5931: /* TP-1Y */
+				case 0x3837: /* TP-78 */
+				case 0x3637: /* TP-76 */
+				case 0x3037: /* TP-70 */
+					printk(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
@@ -3474,7 +3857,7 @@ static void fan_watchdog_fire(struct work_struct *ignored)
 
 static void fan_watchdog_reset(void)
 {
-	static int fan_watchdog_active = 0;
+	static int fan_watchdog_active;
 
 	if (fan_control_access_mode == TPACPI_FAN_WR_NONE)
 		return;
@@ -3877,7 +4260,7 @@ static struct ibm_struct fan_driver_data = {
  ****************************************************************************/
 
 /* /proc support */
-static struct proc_dir_entry *proc_dir = NULL;
+static struct proc_dir_entry *proc_dir;
 
 /* Subdriver registry */
 static LIST_HEAD(tpacpi_all_drivers);
@@ -4020,13 +4403,30 @@ static void ibm_exit(struct ibm_struct *ibm)
 
 /* Probing */
 
-static char *ibm_thinkpad_ec_found = NULL;
-
-static char* __init check_dmi_for_ec(void)
+static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
 {
 	struct dmi_device *dev = NULL;
 	char ec_fw_string[18];
 
+	if (!tp)
+		return;
+
+	memset(tp, 0, sizeof(*tp));
+
+	if (dmi_name_in_vendors("IBM"))
+		tp->vendor = PCI_VENDOR_ID_IBM;
+	else if (dmi_name_in_vendors("LENOVO"))
+		tp->vendor = PCI_VENDOR_ID_LENOVO;
+	else
+		return;
+
+	tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION),
+					GFP_KERNEL);
+	if (!tp->bios_version_str)
+		return;
+	tp->bios_model = tp->bios_version_str[0]
+			 | (tp->bios_version_str[1] << 8);
+
 	/*
 	 * ThinkPad T23 or newer, A31 or newer, R50e or newer,
 	 * X32 or newer, all Z series;  Some models must have an
@@ -4040,10 +4440,20 @@ static char* __init check_dmi_for_ec(void)
 			   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);
+
+			tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
+			tp->ec_model = ec_fw_string[0]
+					| (ec_fw_string[1] << 8);
+			break;
 		}
 	}
-	return NULL;
+
+	tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION),
+					GFP_KERNEL);
+	if (strnicmp(tp->model_str, "ThinkPad", 8) != 0) {
+		kfree(tp->model_str);
+		tp->model_str = NULL;
+	}
 }
 
 static int __init probe_for_thinkpad(void)
@@ -4057,7 +4467,7 @@ static int __init probe_for_thinkpad(void)
 	 * Non-ancient models have better DMI tagging, but very old models
 	 * don't.
 	 */
-	is_thinkpad = dmi_name_in_vendors("ThinkPad");
+	is_thinkpad = (thinkpad_id.model_str != NULL);
 
 	/* ec is required because many other handles are relative to it */
 	IBM_ACPIHANDLE_INIT(ec);
@@ -4073,7 +4483,7 @@ static int __init probe_for_thinkpad(void)
 	 * false positives a damn great deal
 	 */
 	if (!is_thinkpad)
-		is_thinkpad = dmi_name_in_vendors("IBM");
+		is_thinkpad = (thinkpad_id.vendor == PCI_VENDOR_ID_IBM);
 
 	if (!is_thinkpad && !force_load)
 		return -ENODEV;
@@ -4185,10 +4595,13 @@ static u32 dbg_level;
 module_param_named(debug, dbg_level, uint, 0);
 
 static int force_load;
-module_param(force_load, int, 0);
+module_param(force_load, bool, 0);
 
 static int fan_control_allowed;
-module_param_named(fan_control, fan_control_allowed, int, 0);
+module_param_named(fan_control, fan_control_allowed, bool, 0);
+
+static int brightness_mode;
+module_param_named(brightness_mode, brightness_mode, int, 0);
 
 #define IBM_PARAM(feature) \
 	module_param_call(feature, set_ibm_param, NULL, NULL, 0)
@@ -4216,12 +4629,16 @@ static int __init thinkpad_acpi_module_init(void)
 	int ret, i;
 
 	/* Driver-level probe */
+
+	get_thinkpad_model_data(&thinkpad_id);
 	ret = probe_for_thinkpad();
-	if (ret)
+	if (ret) {
+		thinkpad_acpi_module_exit();
 		return ret;
+	}
 
 	/* Driver initialization */
-	ibm_thinkpad_ec_found = check_dmi_for_ec();
+
 	IBM_ACPIHANDLE_INIT(ecrd);
 	IBM_ACPIHANDLE_INIT(ecwr);
 
@@ -4265,6 +4682,22 @@ static int __init thinkpad_acpi_module_init(void)
 		thinkpad_acpi_module_exit();
 		return ret;
 	}
+	tpacpi_inputdev = input_allocate_device();
+	if (!tpacpi_inputdev) {
+		printk(IBM_ERR "unable to allocate input device\n");
+		thinkpad_acpi_module_exit();
+		return -ENOMEM;
+	} else {
+		/* Prepare input device, but don't register */
+		tpacpi_inputdev->name = "ThinkPad Extra Buttons";
+		tpacpi_inputdev->phys = IBM_DRVR_NAME "/input0";
+		tpacpi_inputdev->id.bustype = BUS_HOST;
+		tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ?
+						thinkpad_id.vendor :
+						PCI_VENDOR_ID_IBM;
+		tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
+		tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
+	}
 	for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
 		ret = ibm_init(&ibms_init[i]);
 		if (ret >= 0 && *ibms_init[i].param)
@@ -4274,6 +4707,14 @@ static int __init thinkpad_acpi_module_init(void)
 			return ret;
 		}
 	}
+	ret = input_register_device(tpacpi_inputdev);
+	if (ret < 0) {
+		printk(IBM_ERR "unable to register input device\n");
+		thinkpad_acpi_module_exit();
+		return ret;
+	} else {
+		tp_features.input_device_registered = 1;
+	}
 
 	return 0;
 }
@@ -4290,6 +4731,13 @@ static void thinkpad_acpi_module_exit(void)
 
 	dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n");
 
+	if (tpacpi_inputdev) {
+		if (tp_features.input_device_registered)
+			input_unregister_device(tpacpi_inputdev);
+		else
+			input_free_device(tpacpi_inputdev);
+	}
+
 	if (tpacpi_hwmon)
 		hwmon_device_unregister(tpacpi_hwmon);
 
@@ -4302,7 +4750,9 @@ static void thinkpad_acpi_module_exit(void)
 	if (proc_dir)
 		remove_proc_entry(IBM_PROC_DIR, acpi_root_dir);
 
-	kfree(ibm_thinkpad_ec_found);
+	kfree(thinkpad_id.bios_version_str);
+	kfree(thinkpad_id.ec_version_str);
+	kfree(thinkpad_id.model_str);
 }
 
 module_init(thinkpad_acpi_module_init);

+ 36 - 6
drivers/misc/thinkpad_acpi.h

@@ -32,6 +32,7 @@
 #include <linux/list.h>
 #include <linux/mutex.h>
 
+#include <linux/nvram.h>
 #include <linux/proc_fs.h>
 #include <linux/sysfs.h>
 #include <linux/backlight.h>
@@ -39,6 +40,7 @@
 #include <linux/platform_device.h>
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
+#include <linux/input.h>
 #include <asm/uaccess.h>
 
 #include <linux/dmi.h>
@@ -48,6 +50,7 @@
 #include <acpi/acpi_drivers.h>
 #include <acpi/acnamesp.h>
 
+#include <linux/pci_ids.h>
 
 /****************************************************************************
  * Main driver
@@ -78,6 +81,11 @@
 #define TP_CMOS_BRIGHTNESS_UP	4
 #define TP_CMOS_BRIGHTNESS_DOWN	5
 
+/* ThinkPad CMOS NVRAM constants */
+#define TP_NVRAM_ADDR_BRIGHTNESS       0x5e
+#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x07
+#define TP_NVRAM_POS_LEVEL_BRIGHTNESS 0
+
 #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)))
@@ -98,9 +106,13 @@ static const char *str_supported(int is_supported);
 #define vdbg_printk(a_dbg_level, format, arg...)
 #endif
 
+/* Input IDs */
+#define TPACPI_HKEY_INPUT_VENDOR	PCI_VENDOR_ID_IBM
+#define TPACPI_HKEY_INPUT_PRODUCT	0x5054 /* "TP" */
+#define TPACPI_HKEY_INPUT_VERSION	0x4101
+
 /* ACPI HIDs */
 #define IBM_HKEY_HID    "IBM0068"
-#define IBM_PCI_HID     "PNP0A03"
 
 /* ACPI helpers */
 static int __must_check acpi_evalf(acpi_handle handle,
@@ -161,6 +173,7 @@ static int parse_strtoul(const char *buf, unsigned long max,
 static struct platform_device *tpacpi_pdev;
 static struct class_device *tpacpi_hwmon;
 static struct platform_driver tpacpi_pdriver;
+static struct input_dev *tpacpi_inputdev;
 static int tpacpi_create_driver_attributes(struct device_driver *drv);
 static void tpacpi_remove_driver_attributes(struct device_driver *drv);
 
@@ -168,9 +181,7 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv);
 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);
 
@@ -197,6 +208,7 @@ struct ibm_struct {
 	int (*read) (char *);
 	int (*write) (char *);
 	void (*exit) (void);
+	void (*resume) (void);
 
 	struct list_head all_drivers;
 
@@ -228,12 +240,29 @@ static struct {
 	u16 bluetooth:1;
 	u16 hotkey:1;
 	u16 hotkey_mask:1;
+	u16 hotkey_wlsw:1;
 	u16 light:1;
 	u16 light_status:1;
 	u16 wan:1;
 	u16 fan_ctrl_status_undef:1;
+	u16 input_device_registered:1;
 } tp_features;
 
+struct thinkpad_id_data {
+	unsigned int vendor;	/* ThinkPad vendor:
+				 * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */
+
+	char *bios_version_str;	/* Something like 1ZET51WW (1.03z) */
+	char *ec_version_str;	/* Something like 1ZHT51WW-1.04a */
+
+	u16 bios_model;		/* Big Endian, TP-1Y = 0x5931, 0 = unknown */
+	u16 ec_model;
+
+	char *model_str;
+};
+
+static struct thinkpad_id_data thinkpad_id;
+
 static struct list_head tpacpi_all_drivers;
 
 static struct ibm_init_struct ibms_init[];
@@ -300,6 +329,7 @@ static int bluetooth_write(char *buf);
 
 static struct backlight_device *ibm_backlight_device;
 static int brightness_offset = 0x31;
+static int brightness_mode;
 
 static int brightness_init(struct ibm_init_struct *iibm);
 static void brightness_exit(void);
@@ -415,14 +445,14 @@ static int fan_write_cmd_watchdog(const char *cmd, int *rc);
  */
 
 static int hotkey_orig_status;
-static int hotkey_orig_mask;
+static u32 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 int hotkey_get(int *status, u32 *mask);
+static int hotkey_set(int status, u32 mask);
 static void hotkey_notify(struct ibm_struct *ibm, u32 event);
 static int hotkey_read(char *p);
 static int hotkey_write(char *buf);

+ 7 - 0
drivers/video/Kconfig

@@ -12,6 +12,13 @@ config VGASTATE
        tristate
        default n
 
+config VIDEO_OUTPUT_CONTROL
+	tristate "Lowlevel video output switch controls"
+	default m
+	help
+	  This framework adds support for low-level control of the video 
+	  output switch.
+
 config FB
 	tristate "Support for frame buffer devices"
 	---help---

+ 3 - 0
drivers/video/Makefile

@@ -123,3 +123,6 @@ obj-$(CONFIG_FB_OF)               += offb.o
 
 # the test framebuffer is last
 obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
+
+#video output switch sysfs driver
+obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o

+ 43 - 20
include/acpi/acmacros.h

@@ -486,6 +486,8 @@
 #define ACPI_FUNCTION_NAME(name)
 #endif
 
+#ifdef DEBUG_FUNC_TRACE
+
 #define ACPI_FUNCTION_TRACE(a)          ACPI_FUNCTION_NAME(a) \
 			  acpi_ut_trace(ACPI_DEBUG_PARAMETERS)
 #define ACPI_FUNCTION_TRACE_PTR(a,b)    ACPI_FUNCTION_NAME(a) \
@@ -563,6 +565,27 @@
 
 #endif				/* ACPI_SIMPLE_RETURN_MACROS */
 
+#else /* !DEBUG_FUNC_TRACE */
+
+#define ACPI_FUNCTION_TRACE(a)
+#define ACPI_FUNCTION_TRACE_PTR(a,b)
+#define ACPI_FUNCTION_TRACE_U32(a,b)
+#define ACPI_FUNCTION_TRACE_STR(a,b)
+#define ACPI_FUNCTION_EXIT
+#define ACPI_FUNCTION_STATUS_EXIT(s)
+#define ACPI_FUNCTION_VALUE_EXIT(s)
+#define ACPI_FUNCTION_TRACE(a)
+#define ACPI_FUNCTION_ENTRY()
+
+#define return_VOID                     return
+#define return_ACPI_STATUS(s)           return(s)
+#define return_VALUE(s)                 return(s)
+#define return_UINT8(s)                 return(s)
+#define return_UINT32(s)                return(s)
+#define return_PTR(s)                   return(s)
+
+#endif /* DEBUG_FUNC_TRACE */
+
 /* Conditional execution */
 
 #define ACPI_DEBUG_EXEC(a)              a
@@ -599,26 +622,26 @@
 #define ACPI_DEBUG_EXEC(a)
 #define ACPI_NORMAL_EXEC(a)             a;
 
-#define ACPI_DEBUG_DEFINE(a)
-#define ACPI_DEBUG_ONLY_MEMBERS(a)
-#define ACPI_FUNCTION_NAME(a)
-#define ACPI_FUNCTION_TRACE(a)
-#define ACPI_FUNCTION_TRACE_PTR(a,b)
-#define ACPI_FUNCTION_TRACE_U32(a,b)
-#define ACPI_FUNCTION_TRACE_STR(a,b)
-#define ACPI_FUNCTION_EXIT
-#define ACPI_FUNCTION_STATUS_EXIT(s)
-#define ACPI_FUNCTION_VALUE_EXIT(s)
-#define ACPI_FUNCTION_ENTRY()
-#define ACPI_DUMP_STACK_ENTRY(a)
-#define ACPI_DUMP_OPERANDS(a,b,c,d,e)
-#define ACPI_DUMP_ENTRY(a,b)
-#define ACPI_DUMP_TABLES(a,b)
-#define ACPI_DUMP_PATHNAME(a,b,c,d)
-#define ACPI_DUMP_RESOURCE_LIST(a)
-#define ACPI_DUMP_BUFFER(a,b)
-#define ACPI_DEBUG_PRINT(pl)
-#define ACPI_DEBUG_PRINT_RAW(pl)
+#define ACPI_DEBUG_DEFINE(a)		do { } while(0)
+#define ACPI_DEBUG_ONLY_MEMBERS(a)	do { } while(0)
+#define ACPI_FUNCTION_NAME(a)		do { } while(0)
+#define ACPI_FUNCTION_TRACE(a)		do { } while(0)
+#define ACPI_FUNCTION_TRACE_PTR(a,b)	do { } while(0)
+#define ACPI_FUNCTION_TRACE_U32(a,b)	do { } while(0)
+#define ACPI_FUNCTION_TRACE_STR(a,b)	do { } while(0)
+#define ACPI_FUNCTION_EXIT		do { } while(0)
+#define ACPI_FUNCTION_STATUS_EXIT(s)	do { } while(0)
+#define ACPI_FUNCTION_VALUE_EXIT(s)	do { } while(0)
+#define ACPI_FUNCTION_ENTRY()		do { } while(0)
+#define ACPI_DUMP_STACK_ENTRY(a)	do { } while(0)
+#define ACPI_DUMP_OPERANDS(a,b,c,d,e)	do { } while(0)
+#define ACPI_DUMP_ENTRY(a,b)		do { } while(0)
+#define ACPI_DUMP_TABLES(a,b)		do { } while(0)
+#define ACPI_DUMP_PATHNAME(a,b,c,d)	do { } while(0)
+#define ACPI_DUMP_RESOURCE_LIST(a)	do { } while(0)
+#define ACPI_DUMP_BUFFER(a,b)		do { } while(0)
+#define ACPI_DEBUG_PRINT(pl)		do { } while(0)
+#define ACPI_DEBUG_PRINT_RAW(pl)	do { } while(0)
 
 #define return_VOID                     return
 #define return_ACPI_STATUS(s)           return(s)

+ 2 - 2
include/acpi/acoutput.h

@@ -178,8 +178,8 @@
 
 /* Defaults for debug_level, debug and normal */
 
-#define ACPI_DEBUG_DEFAULT          (ACPI_LV_INIT | ACPI_LV_WARN | ACPI_LV_ERROR | ACPI_LV_DEBUG_OBJECT)
-#define ACPI_NORMAL_DEFAULT         (ACPI_LV_INIT | ACPI_LV_WARN | ACPI_LV_ERROR | ACPI_LV_DEBUG_OBJECT)
+#define ACPI_DEBUG_DEFAULT          (ACPI_LV_INIT | ACPI_LV_WARN | ACPI_LV_ERROR)
+#define ACPI_NORMAL_DEFAULT         (ACPI_LV_INIT | ACPI_LV_WARN | ACPI_LV_ERROR)
 #define ACPI_DEBUG_ALL              (ACPI_LV_AML_DISASSEMBLE | ACPI_LV_ALL_EXCEPTIONS | ACPI_LV_ALL)
 
 #endif				/* __ACOUTPUT_H__ */

+ 2 - 1
include/acpi/acpi_bus.h

@@ -321,7 +321,8 @@ struct acpi_bus_event {
 };
 
 extern struct kset acpi_subsys;
-
+extern int acpi_bus_generate_genetlink_event(struct acpi_device *device,
+						u8 type, int data);
 /*
  * External Functions
  */

+ 1 - 1
include/acpi/platform/acenv.h

@@ -136,7 +136,7 @@
 
 /*! [Begin] no source code translation */
 
-#if defined(__linux__)
+#if defined(_LINUX) || defined(__linux__)
 #include "aclinux.h"
 
 #elif defined(_AED_EFI)

+ 3 - 0
include/acpi/platform/aclinux.h

@@ -91,7 +91,10 @@
 #define ACPI_USE_NATIVE_DIVIDE
 #endif
 
+#ifndef __cdecl
 #define __cdecl
+#endif
+
 #define ACPI_FLUSH_CPU_CACHE()
 #endif				/* __KERNEL__ */
 

+ 44 - 3
include/acpi/processor.h

@@ -21,6 +21,8 @@
 #define ACPI_PSD_REV0_REVISION		0	/* Support for _PSD as in ACPI 3.0 */
 #define ACPI_PSD_REV0_ENTRIES		5
 
+#define ACPI_TSD_REV0_REVISION		0	/* Support for _PSD as in ACPI 3.0 */
+#define ACPI_TSD_REV0_ENTRIES		5
 /*
  * Types of coordination defined in ACPI 3.0. Same macros can be used across
  * P, C and T states
@@ -125,17 +127,53 @@ struct acpi_processor_performance {
 
 /* Throttling Control */
 
+struct acpi_tsd_package {
+	acpi_integer num_entries;
+	acpi_integer revision;
+	acpi_integer domain;
+	acpi_integer coord_type;
+	acpi_integer num_processors;
+} __attribute__ ((packed));
+
+struct acpi_ptc_register {
+	u8 descriptor;
+	u16 length;
+	u8 space_id;
+	u8 bit_width;
+	u8 bit_offset;
+	u8 reserved;
+	u64 address;
+} __attribute__ ((packed));
+
+struct acpi_processor_tx_tss {
+	acpi_integer freqpercentage;	/* */
+	acpi_integer power;	/* milliWatts */
+	acpi_integer transition_latency;	/* microseconds */
+	acpi_integer control;	/* control value */
+	acpi_integer status;	/* success indicator */
+};
 struct acpi_processor_tx {
 	u16 power;
 	u16 performance;
 };
 
+struct acpi_processor;
 struct acpi_processor_throttling {
-	int state;
+	unsigned int state;
+	unsigned int platform_limit;
+	struct acpi_pct_register control_register;
+	struct acpi_pct_register status_register;
+	unsigned int state_count;
+	struct acpi_processor_tx_tss *states_tss;
+	struct acpi_tsd_package domain_info;
+	cpumask_t shared_cpu_map;
+	int (*acpi_processor_get_throttling) (struct acpi_processor * pr);
+	int (*acpi_processor_set_throttling) (struct acpi_processor * pr,
+					      int state);
+
 	u32 address;
 	u8 duty_offset;
 	u8 duty_width;
-	int state_count;
 	struct acpi_processor_tx states[ACPI_PROCESSOR_MAX_THROTTLING];
 };
 
@@ -169,6 +207,9 @@ struct acpi_processor {
 	u32 id;
 	u32 pblk;
 	int performance_platform_limit;
+	int throttling_platform_limit;
+	/* 0 - states 0..n-th state available */
+
 	struct acpi_processor_flags flags;
 	struct acpi_processor_power power;
 	struct acpi_processor_performance *performance;
@@ -270,7 +311,7 @@ static inline int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
 
 /* in processor_throttling.c */
 int acpi_processor_get_throttling_info(struct acpi_processor *pr);
-int acpi_processor_set_throttling(struct acpi_processor *pr, int state);
+extern int acpi_processor_set_throttling(struct acpi_processor *pr, int state);
 extern struct file_operations acpi_processor_throttling_fops;
 
 /* in processor_idle.c */

+ 2 - 0
include/linux/pci_ids.h

@@ -2040,6 +2040,8 @@
 #define PCI_DEVICE_ID_ALTIMA_AC9100	0x03ea
 #define PCI_DEVICE_ID_ALTIMA_AC1003	0x03eb
 
+#define PCI_VENDOR_ID_LENOVO		0x17aa
+
 #define PCI_VENDOR_ID_ARECA		0x17d3
 #define PCI_DEVICE_ID_ARECA_1110	0x1110
 #define PCI_DEVICE_ID_ARECA_1120	0x1120

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov