Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux...
Linus Torvalds [Thu, 7 Feb 2008 17:45:58 +0000 (09:45 -0800)]
* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6: (112 commits)
  ACPI: fix build warning
  Revert "cpuidle: build fix for non-x86"
  ACPI: update intrd DSDT override console messages
  ACPI: update DSDT override documentation
  ACPI: Add "acpi_no_initrd_override" kernel parameter
  ACPI: its a directory not a folder....
  ACPI: misc cleanups
  ACPI: add missing prink prefix strings
  ACPI: cleanup acpi.h
  ACPICA: fix CONFIG_ACPI_DEBUG_FUNC_TRACE build
  ACPI: video: Ignore ACPI video devices that aren't present in hardware
  ACPI: video: reset brightness on resume
  ACPI: video: call ACPI notifier chain for ACPI video notifications
  ACPI: create notifier chain to get hotkey events to graphics driver
  ACPI: video: delete unused display switch on hotkey event code
  ACPI: video: create "brightness_switch_enabled" modparam
  cpuidle: Add a poll_idle method
  ACPI: cpuidle: Support C1 idle time accounting
  ACPI: enable MWAIT for C1 idle
  ACPI: idle: Fix acpi_safe_halt usages and interrupt enabling/disabling
  ...

95 files changed:
Documentation/00-INDEX
Documentation/ABI/testing/sysfs-firmware-acpi [new file with mode: 0644]
Documentation/acpi/dsdt-override.txt [new file with mode: 0644]
Documentation/acpi/initramfs-add-dsdt.sh [new file with mode: 0755]
Documentation/acpi/method-tracing.txt [new file with mode: 0644]
Documentation/kernel-parameters.txt
Documentation/power/swsusp.txt
Documentation/thermal/sysfs-api.txt [new file with mode: 0644]
Documentation/thinkpad-acpi.txt
MAINTAINERS
arch/ia64/kernel/acpi-processor.c
arch/ia64/kernel/acpi.c
arch/x86/Kconfig
arch/x86/kernel/acpi/boot.c
arch/x86/kernel/acpi/processor.c
arch/x86/kernel/cpu/cpufreq/speedstep-lib.c
arch/x86/kernel/srat_32.c
drivers/Kconfig
drivers/Makefile
drivers/acpi/Kconfig
drivers/acpi/Makefile
drivers/acpi/asus_acpi.c
drivers/acpi/battery.c
drivers/acpi/bay.c
drivers/acpi/blacklist.c
drivers/acpi/bus.c
drivers/acpi/debug.c
drivers/acpi/dispatcher/dsopcode.c
drivers/acpi/dock.c
drivers/acpi/ec.c
drivers/acpi/event.c
drivers/acpi/events/evevent.c
drivers/acpi/events/evgpe.c
drivers/acpi/fan.c
drivers/acpi/glue.c
drivers/acpi/hardware/hwsleep.c
drivers/acpi/namespace/nsxfeval.c
drivers/acpi/numa.c
drivers/acpi/osl.c
drivers/acpi/pci_bind.c
drivers/acpi/pci_irq.c
drivers/acpi/pci_link.c
drivers/acpi/power.c
drivers/acpi/processor_core.c
drivers/acpi/processor_idle.c
drivers/acpi/processor_perflib.c
drivers/acpi/processor_thermal.c
drivers/acpi/processor_throttling.c
drivers/acpi/sbs.c
drivers/acpi/sbshc.c
drivers/acpi/scan.c
drivers/acpi/sleep/main.c
drivers/acpi/sleep/proc.c
drivers/acpi/system.c
drivers/acpi/tables/Makefile
drivers/acpi/tables/tbxfroot.c
drivers/acpi/thermal.c
drivers/acpi/utilities/utglobal.c
drivers/acpi/video.c
drivers/acpi/wmi.c [new file with mode: 0644]
drivers/cpuidle/Kconfig
drivers/cpuidle/cpuidle.c
drivers/firmware/dmi_scan.c
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/acer-wmi.c [new file with mode: 0644]
drivers/misc/asus-laptop.c
drivers/misc/intel_menlow.c [new file with mode: 0644]
drivers/misc/sony-laptop.c
drivers/misc/tc1100-wmi.c [new file with mode: 0644]
drivers/misc/thinkpad_acpi.c
drivers/misc/thinkpad_acpi.h [deleted file]
drivers/pnp/pnpacpi/core.c
drivers/power/power_supply_sysfs.c
drivers/thermal/Kconfig [new file with mode: 0644]
drivers/thermal/Makefile [new file with mode: 0644]
drivers/thermal/thermal.c [new file with mode: 0644]
include/acpi/acglobal.h
include/acpi/acmacros.h
include/acpi/acpi_bus.h
include/acpi/acpi_drivers.h
include/acpi/acpi_numa.h
include/acpi/acpiosxf.h
include/acpi/processor.h
include/linux/acpi.h
include/linux/cpuidle.h
include/linux/dmi.h
include/linux/kernel.h
include/linux/power_supply.h
include/linux/sonypi.h
include/linux/thermal.h [new file with mode: 0644]
init/initramfs.c
init/main.c
kernel/panic.c
kernel/power/Kconfig

index d273b55..6e9c405 100644 (file)
@@ -14,6 +14,7 @@ Following translations are available on the WWW:
        - this file.
 ABI/
        - info on kernel <-> userspace ABI and relative interface stability.
+
 BUG-HUNTING
        - brute force method of doing binary search of patches to find bug.
 Changes
@@ -66,6 +67,8 @@ VGA-softcursor.txt
        - how to change your VGA cursor from a blinking underscore.
 accounting/
        - documentation on accounting and taskstats.
+acpi/
+       - info on ACPI-specific hooks in the kernel.
 aoe/
        - description of AoE (ATA over Ethernet) along with config examples.
 applying-patches.txt
diff --git a/Documentation/ABI/testing/sysfs-firmware-acpi b/Documentation/ABI/testing/sysfs-firmware-acpi
new file mode 100644 (file)
index 0000000..9470ed9
--- /dev/null
@@ -0,0 +1,99 @@
+What:          /sys/firmware/acpi/interrupts/
+Date:          February 2008
+Contact:       Len Brown <lenb@kernel.org>
+Description:
+               All ACPI interrupts are handled via a single IRQ,
+               the System Control Interrupt (SCI), which appears
+               as "acpi" in /proc/interrupts.
+
+               However, one of the main functions of ACPI is to make
+               the platform understand random hardware without
+               special driver support.  So while the SCI handles a few
+               well known (fixed feature) interrupts sources, such
+               as the power button, it can also handle a variable
+               number of a "General Purpose Events" (GPE).
+
+               A GPE vectors to a specified handler in AML, which
+               can do a anything the BIOS writer wants from
+               OS context.  GPE 0x12, for example, would vector
+               to a level or edge handler called _L12 or _E12.
+               The handler may do its business and return.
+               Or the handler may send send a Notify event
+               to a Linux device driver registered on an ACPI device,
+               such as a battery, or a processor.
+
+               To figure out where all the SCI's are coming from,
+               /sys/firmware/acpi/interrupts contains a file listing
+               every possible source, and the count of how many
+               times it has triggered.
+
+               $ cd /sys/firmware/acpi/interrupts
+               $ grep . *
+               error:0
+               ff_gbl_lock:0
+               ff_pmtimer:0
+               ff_pwr_btn:0
+               ff_rt_clk:0
+               ff_slp_btn:0
+               gpe00:0
+               gpe01:0
+               gpe02:0
+               gpe03:0
+               gpe04:0
+               gpe05:0
+               gpe06:0
+               gpe07:0
+               gpe08:0
+               gpe09:174
+               gpe0A:0
+               gpe0B:0
+               gpe0C:0
+               gpe0D:0
+               gpe0E:0
+               gpe0F:0
+               gpe10:0
+               gpe11:60
+               gpe12:0
+               gpe13:0
+               gpe14:0
+               gpe15:0
+               gpe16:0
+               gpe17:0
+               gpe18:0
+               gpe19:7
+               gpe1A:0
+               gpe1B:0
+               gpe1C:0
+               gpe1D:0
+               gpe1E:0
+               gpe1F:0
+               gpe_all:241
+               sci:241
+
+               sci - The total number of times the ACPI SCI
+               has claimed an interrupt.
+
+               gpe_all - count of SCI caused by GPEs.
+
+               gpeXX - count for individual GPE source
+
+               ff_gbl_lock - Global Lock
+
+               ff_pmtimer - PM Timer
+
+               ff_pwr_btn - Power Button
+
+               ff_rt_clk - Real Time Clock
+
+               ff_slp_btn - Sleep Button
+
+               error - an interrupt that can't be accounted for above.
+
+               Root has permission to clear any of these counters.  Eg.
+               # echo 0 > gpe11
+
+               All counters can be cleared by clearing the total "sci":
+               # echo 0 > sci
+
+               None of these counters has an effect on the function
+               of the system, they are simply statistics.
diff --git a/Documentation/acpi/dsdt-override.txt b/Documentation/acpi/dsdt-override.txt
new file mode 100644 (file)
index 0000000..5008f25
--- /dev/null
@@ -0,0 +1,15 @@
+Linux supports two methods of overriding the BIOS DSDT:
+
+CONFIG_ACPI_CUSTOM_DSDT builds the image into the kernel.
+
+CONFIG_ACPI_CUSTOM_DSDT_INITRD adds the image to the initrd.
+
+When to use these methods is described in detail on the
+Linux/ACPI home page:
+http://www.lesswatts.org/projects/acpi/overridingDSDT.php
+
+Note that if both options are used, the DSDT supplied
+by the INITRD method takes precedence.
+
+Documentation/initramfs-add-dsdt.sh is provided for convenience
+for use with the CONFIG_ACPI_CUSTOM_DSDT_INITRD method.
diff --git a/Documentation/acpi/initramfs-add-dsdt.sh b/Documentation/acpi/initramfs-add-dsdt.sh
new file mode 100755 (executable)
index 0000000..17ef6e8
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/bash
+# Adds a DSDT file to the initrd (if it's an initramfs)
+# first argument is the name of archive
+# second argument is the name of the file to add
+# The file will be copied as /DSDT.aml
+
+# 20060126: fix "Premature end of file" with some old cpio (Roland Robic)
+# 20060205: this time it should really work
+
+# check the arguments
+if [ $# -ne 2 ]; then
+       program_name=$(basename $0)
+       echo "\
+$program_name: too few arguments
+Usage: $program_name initrd-name.img DSDT-to-add.aml
+Adds a DSDT file to an initrd (in initramfs format)
+
+  initrd-name.img: filename of the initrd in initramfs format
+  DSDT-to-add.aml: filename of the DSDT file to add
+  " 1>&2
+    exit 1
+fi
+
+# we should check it's an initramfs
+
+tempcpio=$(mktemp -d)
+# cleanup on exit, hangup, interrupt, quit, termination
+trap 'rm -rf $tempcpio' 0 1 2 3 15
+
+# extract the archive
+gunzip -c "$1" > "$tempcpio"/initramfs.cpio || exit 1
+
+# copy the DSDT file at the root of the directory so that we can call it "/DSDT.aml"
+cp -f "$2" "$tempcpio"/DSDT.aml
+
+# add the file
+cd "$tempcpio"
+(echo DSDT.aml | cpio --quiet -H newc -o -A -O "$tempcpio"/initramfs.cpio) || exit 1
+cd "$OLDPWD"
+
+# re-compress the archive
+gzip -c "$tempcpio"/initramfs.cpio > "$1"
+
diff --git a/Documentation/acpi/method-tracing.txt b/Documentation/acpi/method-tracing.txt
new file mode 100644 (file)
index 0000000..f6efb1e
--- /dev/null
@@ -0,0 +1,26 @@
+/sys/module/acpi/parameters/:
+
+trace_method_name
+       The AML method name that the user wants to trace
+
+trace_debug_layer
+       The temporary debug_layer used when tracing the method.
+       Using 0xffffffff by default if it is 0.
+
+trace_debug_level
+       The temporary debug_level used when tracing the method.
+       Using 0x00ffffff by default if it is 0.
+
+trace_state
+       The status of the tracing feature.
+
+       "enabled" means this feature is enabled
+       and the AML method is traced every time it's executed.
+
+       "1" means this feature is enabled and the AML method
+       will only be traced during the next execution.
+
+       "disabled" means this feature is disabled.
+       Users can enable/disable this debug tracing feature by
+       "echo string > /sys/module/acpi/parameters/trace_state".
+       "string" should be one of "enable", "disable" and "1".
index 8fd5aa4..0dcbd26 100644 (file)
@@ -147,8 +147,10 @@ and is between 256 and 4096 characters. It is defined in the file
                        default: 0
 
        acpi_sleep=     [HW,ACPI] Sleep options
-                       Format: { s3_bios, s3_mode }
-                       See Documentation/power/video.txt
+                       Format: { s3_bios, s3_mode, s3_beep }
+                       See Documentation/power/video.txt for s3_bios and s3_mode.
+                       s3_beep is for debugging; it makes the PC's speaker beep
+                       as soon as the kernel's real-mode entry point is called.
 
        acpi_sci=       [HW,ACPI] ACPI System Control Interrupt trigger mode
                        Format: { level | edge | high | low }
@@ -175,6 +177,9 @@ and is between 256 and 4096 characters. It is defined in the file
 
        acpi_no_auto_ssdt       [HW,ACPI] Disable automatic loading of SSDT
 
+       acpi_no_initrd_override [KNL,ACPI]
+                       Disable loading custom ACPI tables from the initramfs
+
        acpi_os_name=   [HW,ACPI] Tell ACPI BIOS the name of the OS
                        Format: To spoof as Windows 98: ="Microsoft Windows"
 
index aea7e92..9d60ab7 100644 (file)
@@ -386,6 +386,11 @@ before suspending; then remount them after resuming.
 There is a work-around for this problem.  For more information, see
 Documentation/usb/persist.txt.
 
+Q: Can I suspend-to-disk using a swap partition under LVM?
+
+A: No. You can suspend successfully, but you'll not be able to
+resume. uswsusp should be able to work with LVM. See suspend.sf.net.
+
 Q: I upgraded the kernel from 2.6.15 to 2.6.16. Both kernels were
 compiled with the similar configuration files. Anyway I found that
 suspend to disk (and resume) is much slower on 2.6.16 compared to
diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt
new file mode 100644 (file)
index 0000000..5776e09
--- /dev/null
@@ -0,0 +1,246 @@
+Generic Thermal Sysfs driver How To
+=========================
+
+Written by Sujith Thomas <sujith.thomas@intel.com>, Zhang Rui <rui.zhang@intel.com>
+
+Updated: 2 January 2008
+
+Copyright (c)  2008 Intel Corporation
+
+
+0. Introduction
+
+The generic thermal sysfs provides a set of interfaces for thermal zone devices (sensors)
+and thermal cooling devices (fan, processor...) to register with the thermal management
+solution and to be a part of it.
+
+This how-to focusses on enabling new thermal zone and cooling devices to participate
+in thermal management.
+This solution is platform independent and any type of thermal zone devices and
+cooling devices should be able to make use of the infrastructure.
+
+The main task of the thermal sysfs driver is to expose thermal zone attributes as well
+as cooling device attributes to the user space.
+An intelligent thermal management application can make decisions based on inputs
+from thermal zone attributes (the current temperature and trip point temperature)
+and throttle appropriate devices.
+
+[0-*]  denotes any positive number starting from 0
+[1-*]  denotes any positive number starting from 1
+
+1. thermal sysfs driver interface functions
+
+1.1 thermal zone device interface
+1.1.1 struct thermal_zone_device *thermal_zone_device_register(char *name, int trips,
+                               void *devdata, struct thermal_zone_device_ops *ops)
+
+       This interface function adds a new thermal zone device (sensor) to
+       /sys/class/thermal folder as thermal_zone[0-*].
+       It tries to bind all the thermal cooling devices registered at the same time.
+
+       name: the thermal zone name.
+       trips: the total number of trip points this thermal zone supports.
+       devdata: device private data
+       ops: thermal zone device callbacks.
+               .bind: bind the thermal zone device with a thermal cooling device.
+               .unbind: unbing the thermal zone device with a thermal cooling device.
+               .get_temp: get the current temperature of the thermal zone.
+               .get_mode: get the current mode (user/kernel) of the thermal zone.
+                          "kernel" means thermal management is done in kernel.
+                          "user" will prevent kernel thermal driver actions upon trip points
+                          so that user applications can take charge of thermal management.
+               .set_mode: set the mode (user/kernel) of the thermal zone.
+               .get_trip_type: get the type of certain trip point.
+               .get_trip_temp: get the temperature above which the certain trip point
+                               will be fired.
+
+1.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz)
+
+       This interface function removes the thermal zone device.
+       It deletes the corresponding entry form /sys/class/thermal folder and unbind all
+       the thermal cooling devices it uses.
+
+1.2 thermal cooling device interface
+1.2.1 struct thermal_cooling_device *thermal_cooling_device_register(char *name,
+                                       void *devdata, struct thermal_cooling_device_ops *)
+
+       This interface function adds a new thermal cooling device (fan/processor/...) to
+       /sys/class/thermal/ folder as cooling_device[0-*].
+       It tries to bind itself to all the thermal zone devices register at the same time.
+       name: the cooling device name.
+       devdata: device private data.
+       ops: thermal cooling devices callbacks.
+               .get_max_state: get the Maximum throttle state of the cooling device.
+               .get_cur_state: get the Current throttle state of the cooling device.
+               .set_cur_state: set the Current throttle state of the cooling device.
+
+1.2.2 void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
+
+       This interface function remove the thermal cooling device.
+       It deletes the corresponding entry form /sys/class/thermal folder and unbind
+       itself from all the thermal zone devices using it.
+
+1.3 interface for binding a thermal zone device with a thermal cooling device
+1.3.1 int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
+                       int trip, struct thermal_cooling_device *cdev);
+
+       This interface function bind a thermal cooling device to the certain trip point
+       of a thermal zone device.
+       This function is usually called in the thermal zone device .bind callback.
+       tz: the thermal zone device
+       cdev: thermal cooling device
+       trip: indicates which trip point the cooling devices is associated with
+                in this thermal zone.
+
+1.3.2 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
+                               int trip, struct thermal_cooling_device *cdev);
+
+       This interface function unbind a thermal cooling device from the certain trip point
+       of a thermal zone device.
+       This function is usually called in the thermal zone device .unbind callback.
+       tz: the thermal zone device
+       cdev: thermal cooling device
+       trip: indicates which trip point the cooling devices is associated with
+               in this thermal zone.
+
+2. sysfs attributes structure
+
+RO     read only value
+RW     read/write value
+
+All thermal sysfs attributes will be represented under /sys/class/thermal
+/sys/class/thermal/
+
+Thermal zone device sys I/F, created once it's registered:
+|thermal_zone[0-*]:
+       |-----type:                     Type of the thermal zone
+       |-----temp:                     Current temperature
+       |-----mode:                     Working mode of the thermal zone
+       |-----trip_point_[0-*]_temp:    Trip point temperature
+       |-----trip_point_[0-*]_type:    Trip point type
+
+Thermal cooling device sys I/F, created once it's registered:
+|cooling_device[0-*]:
+       |-----type :                    Type of the cooling device(processor/fan/...)
+       |-----max_state:                Maximum cooling state of the cooling device
+       |-----cur_state:                Current cooling state of the cooling device
+
+
+These two dynamic attributes are created/removed in pairs.
+They represent the relationship between a thermal zone and its associated cooling device.
+They are created/removed for each
+thermal_zone_bind_cooling_device/thermal_zone_unbind_cooling_device successful exection.
+
+|thermal_zone[0-*]
+       |-----cdev[0-*]:                The [0-*]th cooling device in the current thermal zone
+       |-----cdev[0-*]_trip_point:     Trip point that cdev[0-*] is associated with
+
+
+***************************
+* Thermal zone attributes *
+***************************
+
+type                           Strings which represent the thermal zone type.
+                               This is given by thermal zone driver as part of registration.
+                               Eg: "ACPI thermal zone" indicates it's a ACPI thermal device
+                               RO
+                               Optional
+
+temp                           Current temperature as reported by thermal zone (sensor)
+                               Unit: degree celsius
+                               RO
+                               Required
+
+mode                           One of the predifned values in [kernel, user]
+                               This file gives information about the algorithm
+                               that is currently managing the thermal zone.
+                               It can be either default kernel based algorithm
+                               or user space application.
+                               RW
+                               Optional
+                               kernel  = Thermal management in kernel thermal zone driver.
+                               user    = Preventing kernel thermal zone driver actions upon
+                                         trip points so that user application can take full
+                                         charge of the thermal management.
+
+trip_point_[0-*]_temp          The temperature above which trip point will be fired
+                               Unit: degree celsius
+                               RO
+                               Optional
+
+trip_point_[0-*]_type          Strings which indicate the type of the trip point
+                               Eg. it can be one of critical, hot, passive,
+                                   active[0-*] for ACPI thermal zone.
+                               RO
+                               Optional
+
+cdev[0-*]                      Sysfs link to the thermal cooling device node where the sys I/F
+                               for cooling device throttling control represents.
+                               RO
+                               Optional
+
+cdev[0-*]_trip_point           The trip point with which cdev[0-*] is assocated in this thermal zone
+                               -1 means the cooling device is not associated with any trip point.
+                               RO
+                               Optional
+
+******************************
+* Cooling device  attributes *
+******************************
+
+type                           String which represents the type of device
+                               eg: For generic ACPI: this should be "Fan",
+                               "Processor" or "LCD"
+                               eg. For memory controller device on intel_menlow platform:
+                               this should be "Memory controller"
+                               RO
+                               Optional
+
+max_state                      The maximum permissible cooling state of this cooling device.
+                               RO
+                               Required
+
+cur_state                      The current cooling state of this cooling device.
+                               the value can any integer numbers between 0 and max_state,
+                               cur_state == 0 means no cooling
+                               cur_state == max_state means the maximum cooling.
+                               RW
+                               Required
+
+3. A simple implementation
+
+ACPI thermal zone may support multiple trip points like critical/hot/passive/active.
+If an ACPI thermal zone supports critical, passive, active[0] and active[1] at the same time,
+it may register itself as a thermale_zone_device (thermal_zone1) with 4 trip points in all.
+It has one processor and one fan, which are both registered as thermal_cooling_device.
+If the processor is listed in _PSL method, and the fan is listed in _AL0 method,
+the sys I/F structure will be built like this:
+
+/sys/class/thermal:
+
+|thermal_zone1:
+       |-----type:                     ACPI thermal zone
+       |-----temp:                     37
+       |-----mode:                     kernel
+       |-----trip_point_0_temp:        100
+       |-----trip_point_0_type:        critical
+       |-----trip_point_1_temp:        80
+       |-----trip_point_1_type:        passive
+       |-----trip_point_2_temp:        70
+       |-----trip_point_2_type:        active[0]
+       |-----trip_point_3_temp:        60
+       |-----trip_point_3_type:        active[1]
+       |-----cdev0:                    --->/sys/class/thermal/cooling_device0
+       |-----cdev0_trip_point:         1       /* cdev0 can be used for passive */
+       |-----cdev1:                    --->/sys/class/thermal/cooling_device3
+       |-----cdev1_trip_point:         2       /* cdev1 can be used for active[0]*/
+
+|cooling_device0:
+       |-----type:                     Processor
+       |-----max_state:                8
+       |-----cur_state:                0
+
+|cooling_device3:
+       |-----type:                     Fan
+       |-----max_state:                2
+       |-----cur_state:                0
index 10c041c..6c24777 100644 (file)
@@ -1,7 +1,7 @@
                     ThinkPad ACPI Extras Driver
 
-                            Version 0.17
-                         October 04th, 2007
+                            Version 0.19
+                         January 06th, 2008
 
                Borislav Deianov <borislav@users.sf.net>
              Henrique de Moraes Holschuh <hmh@hmh.eng.br>
@@ -215,6 +215,11 @@ The following commands can be written to the /proc/acpi/ibm/hotkey file:
        ... any other 8-hex-digit mask ...
        echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
 
+The procfs interface does not support NVRAM polling control.  So as to
+maintain maximum bug-to-bug compatibility, it does not report any masks,
+nor does it allow one to manipulate the hot key mask when the firmware
+does not support masks at all, even if NVRAM polling is in use.
+
 sysfs notes:
 
        hotkey_bios_enabled:
@@ -231,17 +236,26 @@ sysfs notes:
                to this value.
 
        hotkey_enable:
-               Enables/disables the hot keys feature, and reports
-               current status of the hot keys feature.
+               Enables/disables the hot keys feature in the ACPI
+               firmware, and reports current status of the hot keys
+               feature.  Has no effect on the NVRAM hot key polling
+               functionality.
 
                0: disables the hot keys feature / feature disabled
                1: enables the hot keys feature / feature enabled
 
        hotkey_mask:
-               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.
+               bit mask to enable driver-handling (and depending on
+               the firmware, ACPI event generation) for each hot key
+               (see above).  Returns the current status of the hot keys
+               mask, and allows one to modify it.
+
+               Note: when NVRAM polling is active, the firmware mask
+               will be different from the value returned by
+               hotkey_mask.  The driver will retain enabled bits for
+               hotkeys that are under NVRAM polling even if the
+               firmware refuses them, and will not set these bits on
+               the firmware hot key mask.
 
        hotkey_all_mask:
                bit mask that should enable event reporting for all
@@ -257,12 +271,48 @@ sysfs notes:
                handled by the firmware anyway.  Echo it to
                hotkey_mask above, to use.
 
+       hotkey_source_mask:
+               bit mask that selects which hot keys will the driver
+               poll the NVRAM for.  This is auto-detected by the driver
+               based on the capabilities reported by the ACPI firmware,
+               but it can be overridden at runtime.
+
+               Hot keys whose bits are set in both hotkey_source_mask
+               and also on hotkey_mask are polled for in NVRAM.  Only a
+               few hot keys are available through CMOS NVRAM polling.
+
+               Warning: when in NVRAM mode, the volume up/down/mute
+               keys are synthesized according to changes in the mixer,
+               so you have to use volume up or volume down to unmute,
+               as per the ThinkPad volume mixer user interface.  When
+               in ACPI event mode, volume up/down/mute are reported as
+               separate events, but this behaviour may be corrected in
+               future releases of this driver, in which case the
+               ThinkPad volume mixer user interface semanthics will be
+               enforced.
+
+       hotkey_poll_freq:
+               frequency in Hz for hot key polling. It must be between
+               0 and 25 Hz.  Polling is only carried out when strictly
+               needed.
+
+               Setting hotkey_poll_freq to zero disables polling, and
+               will cause hot key presses that require NVRAM polling
+               to never be reported.
+
+               Setting hotkey_poll_freq too low will cause repeated
+               pressings of the same hot key to be misreported as a
+               single key press, or to not even be detected at all.
+               The recommended polling frequency is 10Hz.
+
        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.
 
+               This attribute has poll()/select() support.
+
        hotkey_report_mode:
                Returns the state of the procfs ACPI event report mode
                filter for hot keys.  If it is set to 1 (the default),
@@ -277,6 +327,25 @@ sysfs notes:
                May return -EPERM (write access locked out by module
                parameter) or -EACCES (read-only).
 
+       wakeup_reason:
+               Set to 1 if the system is waking up because the user
+               requested a bay ejection.  Set to 2 if the system is
+               waking up because the user requested the system to
+               undock.  Set to zero for normal wake-ups or wake-ups
+               due to unknown reasons.
+
+               This attribute has poll()/select() support.
+
+       wakeup_hotunplug_complete:
+               Set to 1 if the system was waken up because of an
+               undock or bay ejection request, and that request
+               was sucessfully completed.  At this point, it might
+               be useful to send the system back to sleep, at the
+               user's choice.  Refer to HKEY events 0x4003 and
+               0x3003, below.
+
+               This attribute has poll()/select() support.
+
 input layer notes:
 
 A Hot key is mapped to a single input layer EV_KEY event, possibly
@@ -427,6 +496,23 @@ Non hot-key ACPI HKEY event map:
 The above events are not propagated by the driver, except for legacy
 compatibility purposes when hotkey_report_mode is set to 1.
 
+0x2304         System is waking up from suspend to undock
+0x2305         System is waking up from suspend to eject bay
+0x2404         System is waking up from hibernation to undock
+0x2405         System is waking up from hibernation to eject bay
+
+The above events are never propagated by the driver.
+
+0x3003         Bay ejection (see 0x2x05) complete, can sleep again
+0x4003         Undocked (see 0x2x04), can sleep again
+0x5009         Tablet swivel: switched to tablet mode
+0x500A         Tablet swivel: switched to normal mode
+0x500B         Tablet pen insterted into its storage bay
+0x500C         Tablet pen removed from its storage bay
+0x5010         Brightness level changed (newer Lenovo BIOSes)
+
+The above events are propagated by the driver.
+
 Compatibility notes:
 
 ibm-acpi and thinkpad-acpi 0.15 (mainline kernels before 2.6.23) never
@@ -1263,3 +1349,17 @@ Sysfs interface changelog:
                and the hwmon class for libsensors4 (lm-sensors 3)
                compatibility.  Moved all hwmon attributes to this
                new platform device.
+
+0x020100:      Marker for thinkpad-acpi with hot key NVRAM polling
+               support.  If you must, use it to know you should not
+               start an userspace NVRAM poller (allows to detect when
+               NVRAM is compiled out by the user because it is
+               unneeded/undesired in the first place).
+0x020101:      Marker for thinkpad-acpi with hot key NVRAM polling
+               and proper hotkey_mask semanthics (version 8 of the
+               NVRAM polling patch).  Some development snapshots of
+               0.18 had an earlier version that did strange things
+               to hotkey_mask.
+
+0x020200:      Add poll()/select() support to the following attributes:
+               hotkey_radio_sw, wakeup_hotunplug_complete, wakeup_reason
index a372c86..aefd23f 100644 (file)
@@ -202,6 +202,13 @@ L: linux-scsi@vger.kernel.org
 W:     http://www.adaptec.com/
 S:     Supported
 
+ACER WMI LAPTOP EXTRAS
+P:     Carlos Corbacho
+M:     carlos@strangeworlds.co.uk
+L:     aceracpi@googlegroups.com (subscribers-only)
+W:     http://code.google.com/p/aceracpi
+S:     Maintained
+
 ACPI
 P:     Len Brown
 M:     len.brown@intel.com
@@ -252,6 +259,13 @@ L: linux-acpi@vger.kernel.org
 W:     http://acpi.sourceforge.net/
 S:     Supported
 
+ACPI WMI DRIVER
+P:      Carlos Corbacho
+M:      carlos@strangeworlds.co.uk
+L:      linux-acpi@vger.kernel.org
+W:      http://www.lesswatts.org/projects/acpi/
+S:      Maintained
+
 ADM1025 HARDWARE MONITOR DRIVER
 P:     Jean Delvare
 M:     khali@linux-fr.org
@@ -1790,6 +1804,11 @@ P:       Jaroslav Kysela
 M:     perex@perex.cz
 S:     Maintained
 
+HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER
+P:     Carlos Corbacho
+M:     carlos@strangeworlds.co.uk
+S:     Odd Fixes
+
 HPET:  High Precision Event Timers driver (hpet.c)
 P:     Clemens Ladisch
 M:     clemens@ladisch.de
index 5a216c0..cbe6cee 100644 (file)
@@ -45,6 +45,12 @@ static void init_intel_pdc(struct acpi_processor *pr)
        buf[0] = ACPI_PDC_REVISION_ID;
        buf[1] = 1;
        buf[2] = ACPI_PDC_EST_CAPABILITY_SMP;
+       /*
+        * The default of PDC_SMP_T_SWCOORD bit is set for IA64 cpu so
+        * that OSPM is capable of native ACPI throttling software
+        * coordination using BIOS supplied _TSD info.
+        */
+       buf[2] |= ACPI_PDC_SMP_T_SWCOORD;
 
        obj->type = ACPI_TYPE_BUFFER;
        obj->buffer.length = 12;
index 00b5d08..78f28d8 100644 (file)
@@ -69,6 +69,20 @@ unsigned int acpi_cpei_phys_cpuid;
 
 unsigned long acpi_wakeup_address = 0;
 
+#ifdef CONFIG_IA64_GENERIC
+static unsigned long __init acpi_find_rsdp(void)
+{
+       unsigned long rsdp_phys = 0;
+
+       if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
+               rsdp_phys = efi.acpi20;
+       else if (efi.acpi != EFI_INVALID_TABLE_ADDR)
+               printk(KERN_WARNING PREFIX
+                      "v1.0/r0.71 tables no longer supported\n");
+       return rsdp_phys;
+}
+#endif
+
 const char __init *
 acpi_get_sysname(void)
 {
@@ -152,7 +166,7 @@ int acpi_request_vector(u32 int_type)
        return vector;
 }
 
-char *__acpi_map_table(unsigned long phys_addr, unsigned long size)
+char *__init __acpi_map_table(unsigned long phys_addr, unsigned long size)
 {
        return __va(phys_addr);
 }
@@ -601,8 +615,6 @@ int acpi_register_gsi(u32 gsi, int triggering, int polarity)
                                     IOSAPIC_LEVEL);
 }
 
-EXPORT_SYMBOL(acpi_register_gsi);
-
 void acpi_unregister_gsi(u32 gsi)
 {
        if (acpi_irq_model == ACPI_IRQ_MODEL_PLATFORM)
@@ -611,8 +623,6 @@ void acpi_unregister_gsi(u32 gsi)
        iosapic_unregister_intr(gsi);
 }
 
-EXPORT_SYMBOL(acpi_unregister_gsi);
-
 static int __init acpi_parse_fadt(struct acpi_table_header *table)
 {
        struct acpi_table_header *fadt_header;
@@ -631,18 +641,6 @@ static int __init acpi_parse_fadt(struct acpi_table_header *table)
        return 0;
 }
 
-unsigned long __init acpi_find_rsdp(void)
-{
-       unsigned long rsdp_phys = 0;
-
-       if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
-               rsdp_phys = efi.acpi20;
-       else if (efi.acpi != EFI_INVALID_TABLE_ADDR)
-               printk(KERN_WARNING PREFIX
-                      "v1.0/r0.71 tables no longer supported\n");
-       return rsdp_phys;
-}
-
 int __init acpi_boot_init(void)
 {
 
index 923c3ba..c95482b 100644 (file)
@@ -105,6 +105,9 @@ config GENERIC_TIME_VSYSCALL
        bool
        default X86_64
 
+config ARCH_HAS_CPU_RELAX
+       def_bool y
+
 config HAVE_SETUP_PER_CPU_AREA
        def_bool X86_64
 
index d2a5843..680b730 100644 (file)
@@ -78,7 +78,6 @@ int acpi_ht __initdata = 1;   /* enable HT */
 int acpi_lapic;
 int acpi_ioapic;
 int acpi_strict;
-EXPORT_SYMBOL(acpi_strict);
 
 u8 acpi_sci_flags __initdata;
 int acpi_sci_override_gsi __initdata;
@@ -106,7 +105,7 @@ enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PIC;
 #ifdef CONFIG_X86_64
 
 /* rely on all ACPI tables being in the direct mapping */
-char *__acpi_map_table(unsigned long phys_addr, unsigned long size)
+char *__init __acpi_map_table(unsigned long phys_addr, unsigned long size)
 {
        if (!phys_addr || !size)
                return NULL;
@@ -131,7 +130,7 @@ char *__acpi_map_table(unsigned long phys_addr, unsigned long size)
  * from the fixed base.  That's why we start at FIX_IO_APIC_BASE_END and
  * count idx down while incrementing the phys address.
  */
-char *__acpi_map_table(unsigned long phys, unsigned long size)
+char *__init __acpi_map_table(unsigned long phys, unsigned long size)
 {
        unsigned long base, offset, mapped_size;
        int idx;
@@ -490,8 +489,6 @@ int acpi_register_gsi(u32 gsi, int triggering, int polarity)
        return irq;
 }
 
-EXPORT_SYMBOL(acpi_register_gsi);
-
 /*
  *  ACPI based hotplug support for CPU
  */
@@ -587,25 +584,6 @@ int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base)
 
 EXPORT_SYMBOL(acpi_unregister_ioapic);
 
-static unsigned long __init
-acpi_scan_rsdp(unsigned long start, unsigned long length)
-{
-       unsigned long offset = 0;
-       unsigned long sig_len = sizeof("RSD PTR ") - 1;
-
-       /*
-        * Scan all 16-byte boundaries of the physical memory region for the
-        * RSDP signature.
-        */
-       for (offset = 0; offset < length; offset += 16) {
-               if (strncmp((char *)(phys_to_virt(start) + offset), "RSD PTR ", sig_len))
-                       continue;
-               return (start + offset);
-       }
-
-       return 0;
-}
-
 static int __init acpi_parse_sbf(struct acpi_table_header *table)
 {
        struct acpi_table_boot *sb;
@@ -748,27 +726,6 @@ static int __init acpi_parse_fadt(struct acpi_table_header *table)
        return 0;
 }
 
-unsigned long __init acpi_find_rsdp(void)
-{
-       unsigned long rsdp_phys = 0;
-
-       if (efi_enabled) {
-               if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
-                       return efi.acpi20;
-               else if (efi.acpi != EFI_INVALID_TABLE_ADDR)
-                       return efi.acpi;
-       }
-       /*
-        * Scan memory looking for the RSDP signature. First search EBDA (low
-        * memory) paragraphs and then search upper memory (E0000-FFFFF).
-        */
-       rsdp_phys = acpi_scan_rsdp(0, 0x400);
-       if (!rsdp_phys)
-               rsdp_phys = acpi_scan_rsdp(0xE0000, 0x20000);
-
-       return rsdp_phys;
-}
-
 #ifdef CONFIG_X86_LOCAL_APIC
 /*
  * Parse LAPIC entries in MADT
index a25db51..324eb0c 100644 (file)
@@ -46,6 +46,12 @@ static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c)
        buf[1] = 1;
        buf[2] = ACPI_PDC_C_CAPABILITY_SMP;
 
+       /*
+        * The default of PDC_SMP_T_SWCOORD bit is set for intel x86 cpu so
+        * that OSPM is capable of native ACPI throttling software
+        * coordination using BIOS supplied _TSD info.
+        */
+       buf[2] |= ACPI_PDC_SMP_T_SWCOORD;
        if (cpu_has(c, X86_FEATURE_EST))
                buf[2] |= ACPI_PDC_EST_CAPABILITY_SWSMP;
 
index 76c3ab0..98d4fdb 100644 (file)
@@ -189,10 +189,7 @@ static unsigned int pentium4_get_frequency(void)
                printk(KERN_DEBUG "speedstep-lib: couldn't detect FSB speed. Please send an e-mail to <linux@brodo.de>\n");
 
        /* Multiplier. */
-       if (c->x86_model < 2)
-               mult = msr_lo >> 27;
-       else
-               mult = msr_lo >> 24;
+       mult = msr_lo >> 24;
 
        dprintk("P4 - FSB %u kHz; Multiplier %u; Speed %u kHz\n", fsb, mult, (fsb * mult));
 
index 2bf6903..b72e613 100644 (file)
@@ -274,7 +274,7 @@ int __init get_memcfg_from_srat(void)
        int tables = 0;
        int i = 0;
 
-       rsdp_address = acpi_find_rsdp();
+       rsdp_address = acpi_os_get_root_pointer();
        if (!rsdp_address) {
                printk("%s: System description tables not found\n",
                       __FUNCTION__);
index d74d9fb..b86877b 100644 (file)
@@ -60,6 +60,8 @@ source "drivers/power/Kconfig"
 
 source "drivers/hwmon/Kconfig"
 
+source "drivers/thermal/Kconfig"
+
 source "drivers/watchdog/Kconfig"
 
 source "drivers/ssb/Kconfig"
index f1c11db..30ba97e 100644 (file)
@@ -65,6 +65,7 @@ obj-y                         += i2c/
 obj-$(CONFIG_W1)               += w1/
 obj-$(CONFIG_POWER_SUPPLY)     += power/
 obj-$(CONFIG_HWMON)            += hwmon/
+obj-$(CONFIG_THERMAL)          += thermal/
 obj-$(CONFIG_WATCHDOG)         += watchdog/
 obj-$(CONFIG_PHONE)            += telephony/
 obj-$(CONFIG_MD)               += md/
index ccf6ea9..7ef172c 100644 (file)
@@ -68,26 +68,28 @@ config ACPI_PROCFS
 
          Say N to delete /proc/acpi/ files that have moved to /sys/
 config ACPI_PROCFS_POWER
-       bool "Deprecated power /proc/acpi folders"
+       bool "Deprecated power /proc/acpi directories"
        depends on PROC_FS
        default y
        ---help---
          For backwards compatibility, this option allows
-          deprecated power /proc/acpi/ folders to exist, even when
+          deprecated power /proc/acpi/ directories to exist, even when
           they have been replaced by functions in /sys.
-          The deprecated folders (and their replacements) include:
+          The deprecated directories (and their replacements) include:
          /proc/acpi/battery/* (/sys/class/power_supply/*)
          /proc/acpi/ac_adapter/* (sys/class/power_supply/*)
-         This option has no effect on /proc/acpi/ folders
+         This option has no effect on /proc/acpi/ directories
          and functions, which do not yet exist in /sys
 
-         Say N to delete power /proc/acpi/ folders that have moved to /sys/
+         Say N to delete power /proc/acpi/ directories that have moved to /sys/
+
 config ACPI_SYSFS_POWER
        bool "Future power /sys interface"
        select POWER_SUPPLY
        default y
        ---help---
          Say N to disable power /sys interface
+
 config ACPI_PROC_EVENT
        bool "Deprecated /proc/acpi/event support"
        depends on PROC_FS
@@ -186,6 +188,7 @@ config ACPI_HOTPLUG_CPU
 config ACPI_THERMAL
        tristate "Thermal Zone"
        depends on ACPI_PROCESSOR
+       select THERMAL
        default y
        help
          This driver adds support for ACPI thermal zones.  Most mobile and
@@ -199,6 +202,16 @@ config ACPI_NUMA
        depends on (X86 || IA64)
        default y if IA64_GENERIC || IA64_SGI_SN2
 
+config ACPI_WMI
+       tristate "WMI (EXPERIMENTAL)"
+       depends on EXPERIMENTAL
+       help
+         This driver adds support for the ACPI-WMI mapper device (PNP0C14)
+         found on some systems.
+
+         NOTE: You will need another driver or userspace application on top of
+         this to actually use anything defined in the ACPI-WMI mapper.
+
 config ACPI_ASUS
         tristate "ASUS/Medion Laptop Extras"
        depends on X86
@@ -263,8 +276,10 @@ config ACPI_CUSTOM_DSDT
        depends on !STANDALONE
        default n 
        help
-         This option is to load a custom ACPI DSDT
-         If you don't know what that is, say N.
+         This option supports a custom DSDT by linking it into the kernel.
+         See Documentation/acpi/dsdt-override.txt
+
+         If unsure, say N.
 
 config ACPI_CUSTOM_DSDT_FILE
        string "Custom DSDT Table file to include"
@@ -274,6 +289,17 @@ config ACPI_CUSTOM_DSDT_FILE
          Enter the full path name to the file which includes the AmlCode
          declaration.
 
+config ACPI_CUSTOM_DSDT_INITRD
+       bool "Read Custom DSDT from initramfs"
+       depends on BLK_DEV_INITRD
+       default n
+       help
+         This option supports a custom DSDT by optionally loading it from initrd.
+         See Documentation/acpi/dsdt-override.txt
+
+         If you are not using this feature now, but may use it later,
+         it is safe to say Y here.
+
 config ACPI_BLACKLIST_YEAR
        int "Disable ACPI for systems before Jan 1st this year" if X86_32
        default 0
index 456446f..f29812a 100644 (file)
@@ -55,6 +55,7 @@ obj-$(CONFIG_ACPI_THERMAL)    += thermal.o
 obj-$(CONFIG_ACPI_SYSTEM)      += system.o event.o
 obj-$(CONFIG_ACPI_DEBUG)       += debug.o
 obj-$(CONFIG_ACPI_NUMA)                += numa.o
+obj-$(CONFIG_ACPI_WMI)         += wmi.o
 obj-$(CONFIG_ACPI_ASUS)                += asus_acpi.o
 obj-$(CONFIG_ACPI_TOSHIBA)     += toshiba_acpi.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)      += acpi_memhotplug.o
index d915fec..d25ef96 100644 (file)
@@ -142,6 +142,7 @@ struct asus_hotk {
                xxN,            //M2400N, M3700N, M5200N, M6800N, S1300N, S5200N
                A4S,            //Z81sp
                //(Centrino)
+               F3Sa,
                END_MODEL
        } model;                //Models currently supported
        u16 event_count[128];   //count for each event TODO make this better
@@ -405,7 +406,20 @@ static struct model_data model_conf[END_MODEL] = {
                .brightness_get    = "GPLV",
                .mt_bt_switch      = "BLED",
                .mt_wled           = "WLED"
-       }
+       },
+
+       {
+               .name           = "F3Sa",
+               .mt_bt_switch   = "BLED",
+               .mt_wled        = "WLED",
+               .mt_mled        = "MLED",
+               .brightness_get = "GPLV",
+               .brightness_set = "SPLV",
+               .mt_lcd_switch  = "\\_SB.PCI0.SBRG.EC0._Q10",
+               .lcd_status     = "\\_SB.PCI0.SBRG.EC0.RPIN",
+               .display_get    = "\\ADVG",
+               .display_set    = "SDSP",
+       },
 
 };
 
@@ -710,15 +724,8 @@ static int get_lcd_state(void)
 {
        int lcd = 0;
 
-       if (hotk->model != L3H) {
-               /* We don't have to check anything if we are here */
-               if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd))
-                       printk(KERN_WARNING
-                              "Asus ACPI: Error reading LCD status\n");
-
-               if (hotk->model == L2D)
-                       lcd = ~lcd;
-       } else {                /* L3H and the like have to be handled differently */
+       if (hotk->model == L3H) {
+               /* L3H and the like have to be handled differently */
                acpi_status status = 0;
                struct acpi_object_list input;
                union acpi_object mt_params[2];
@@ -745,6 +752,32 @@ static int get_lcd_state(void)
                if (out_obj.type == ACPI_TYPE_INTEGER)
                        /* That's what the AML code does */
                        lcd = out_obj.integer.value >> 8;
+       } else if (hotk->model == F3Sa) {
+               unsigned long tmp;
+               union acpi_object param;
+               struct acpi_object_list input;
+               acpi_status status;
+
+               /* Read pin 11 */
+               param.type = ACPI_TYPE_INTEGER;
+               param.integer.value = 0x11;
+               input.count = 1;
+               input.pointer = &param;
+
+               status = acpi_evaluate_integer(NULL, hotk->methods->lcd_status,
+                                               &input, &tmp);
+               if (status != AE_OK)
+                       return -1;
+
+               lcd = tmp;
+       } else {
+               /* We don't have to check anything if we are here */
+               if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd))
+                       printk(KERN_WARNING
+                              "Asus ACPI: Error reading LCD status\n");
+
+               if (hotk->model == L2D)
+                       lcd = ~lcd;
        }
 
        return (lcd & 1);
@@ -1134,6 +1167,8 @@ static int asus_model_match(char *model)
                return W5A;
        else if (strncmp(model, "A4S", 3) == 0)
                return A4S;
+       else if (strncmp(model, "F3Sa", 4) == 0)
+               return F3Sa;
        else
                return END_MODEL;
 }
index c4a769d..f6215e8 100644 (file)
@@ -194,6 +194,9 @@ static int acpi_battery_get_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_MANUFACTURER:
                val->strval = battery->oem_info;
                break;
+       case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+               val->strval = battery->serial_number;
+               break;
        default:
                return -EINVAL;
        }
@@ -212,6 +215,7 @@ static enum power_supply_property charge_battery_props[] = {
        POWER_SUPPLY_PROP_CHARGE_NOW,
        POWER_SUPPLY_PROP_MODEL_NAME,
        POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_SERIAL_NUMBER,
 };
 
 static enum power_supply_property energy_battery_props[] = {
@@ -226,6 +230,7 @@ static enum power_supply_property energy_battery_props[] = {
        POWER_SUPPLY_PROP_ENERGY_NOW,
        POWER_SUPPLY_PROP_MODEL_NAME,
        POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_SERIAL_NUMBER,
 };
 #endif
 
index 6daf608..1fa8681 100644 (file)
@@ -46,6 +46,12 @@ MODULE_LICENSE("GPL");
        printk(KERN_DEBUG PREFIX "%s: %s\n", prefix, s); }
 static void bay_notify(acpi_handle handle, u32 event, void *data);
 
+static const struct acpi_device_id bay_device_ids[] = {
+       {"LNXIOBAY", 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, bay_device_ids);
+
 struct bay {
        acpi_handle handle;
        char *name;
@@ -128,7 +134,7 @@ static ssize_t show_present(struct device *dev,
        return snprintf(buf, PAGE_SIZE, "%d\n", bay_present(bay));
 
 }
-DEVICE_ATTR(present, S_IRUGO, show_present, NULL);
+static DEVICE_ATTR(present, S_IRUGO, show_present, NULL);
 
 /*
  * write_eject - write method for "eject" file in sysfs
@@ -144,7 +150,7 @@ static ssize_t write_eject(struct device *dev, struct device_attribute *attr,
        eject_device(bay->handle);
        return count;
 }
-DEVICE_ATTR(eject, S_IWUSR, NULL, write_eject);
+static DEVICE_ATTR(eject, S_IWUSR, NULL, write_eject);
 
 /**
  * is_ata - see if a device is an ata device
index 8809654..6dbaa2d 100644 (file)
@@ -70,8 +70,6 @@ static struct acpi_blacklist_item acpi_blacklist[] __initdata = {
        /* IBM 600E - _ADR should return 7, but it returns 1 */
        {"IBM   ", "TP600E  ", 0x00000105, ACPI_SIG_DSDT, less_than_or_equal,
         "Incorrect _ADR", 1},
-       {"ASUS\0\0", "P2B-S   ", 0, ACPI_SIG_DSDT, all_versions,
-        "Bogus PCI routing", 1},
 
        {""}
 };
@@ -208,33 +206,35 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
         * Disable OSI(Linux) warnings on all "Acer, inc."
         *
         * _OSI(Linux) disables the latest Windows BIOS code:
+        * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5050"),
+        * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5580"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 3010"),
         * _OSI(Linux) effect unknown:
         * DMI_MATCH(DMI_PRODUCT_NAME, "Ferrari 5000"),
         */
-       {
-       .callback = dmi_disable_osi_linux,
-       .ident = "Acer, inc.",
-       .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "Acer, inc."),
-               },
-       },
+       /*
+        * note that dmi_check_system() uses strstr()
+        * to match sub-strings rather than !strcmp(),
+        * so "Acer" below matches "Acer, inc." above.
+        */
        /*
         * Disable OSI(Linux) warnings on all "Acer"
         *
         * _OSI(Linux) effect unknown:
-        * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720Z"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5520"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 6460"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 7510"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5220"),
+        *
+        * _OSI(Linux) is a NOP:
+        * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"),
         */
        {
-       .callback = dmi_unknown_osi_linux,
+       .callback = dmi_disable_osi_linux,
        .ident = "Acer",
        .matches = {
                     DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
@@ -242,21 +242,22 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
        },
        /*
         * Disable OSI(Linux) warnings on all "Apple Computer, Inc."
+        * Disable OSI(Linux) warnings on all "Apple Inc."
         *
         * _OSI(Linux) confirmed to be a NOP:
         * DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "MacBook2,1"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2"),
+        * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"),
         * _OSI(Linux) effect unknown:
         * DMI_MATCH(DMI_PRODUCT_NAME, "MacPro2,1"),
         * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro1,1"),
-        * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"),
         */
        {
        .callback = dmi_disable_osi_linux,
        .ident = "Apple",
        .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."),
+                    DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
                },
        },
        /*
@@ -294,13 +295,13 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
         * DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
         */
        {
-       .callback = dmi_unknown_osi_linux,
+       .callback = dmi_disable_osi_linux,
        .ident = "Compal",
        .matches = {
                     DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"),
                },
        },
-       { /* OSI(Linux) touches USB, breaks suspend to disk */
+       { /* OSI(Linux) touches USB, unknown side-effect */
        .callback = dmi_disable_osi_linux,
        .ident = "Dell Dimension 5150",
        .matches = {
@@ -310,7 +311,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
        },
        { /* OSI(Linux) is a NOP */
        .callback = dmi_disable_osi_linux,
-       .ident = "Dell",
+       .ident = "Dell i1501",
        .matches = {
                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1501"),
@@ -318,7 +319,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
        },
        { /* OSI(Linux) effect unknown */
        .callback = dmi_unknown_osi_linux,
-       .ident = "Dell",
+       .ident = "Dell Latitude D830",
        .matches = {
                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                     DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D830"),
@@ -326,7 +327,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
        },
        { /* OSI(Linux) effect unknown */
        .callback = dmi_unknown_osi_linux,
-       .ident = "Dell",
+       .ident = "Dell OP GX620",
        .matches = {
                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                     DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX620"),
@@ -334,15 +335,23 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
        },
        { /* OSI(Linux) effect unknown */
        .callback = dmi_unknown_osi_linux,
-       .ident = "Dell",
+       .ident = "Dell PE 1900",
        .matches = {
                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                     DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1900"),
                },
        },
+       { /* OSI(Linux) is a NOP */
+       .callback = dmi_disable_osi_linux,
+       .ident = "Dell PE R200",
+       .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge R200"),
+               },
+       },
        { /* OSI(Linux) touches USB */
        .callback = dmi_disable_osi_linux,
-       .ident = "Dell",
+       .ident = "Dell PR 390",
        .matches = {
                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                     DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation 390"),
@@ -358,7 +367,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
        },
        { /* OSI(Linux) effect unknown */
        .callback = dmi_unknown_osi_linux,
-       .ident = "Dell",
+       .ident = "Dell PE SC440",
        .matches = {
                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                     DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge SC440"),
@@ -474,6 +483,11 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
         *
         * _OSI(Linux) confirmed to be a NOP:
         * DMI_MATCH(DMI_PRODUCT_NAME, "P1-J150B"),
+        * with DMI_MATCH(DMI_BOARD_NAME, "ROCKY"),
+        *
+        * unknown:
+        * DMI_MATCH(DMI_PRODUCT_NAME, "S1-MDGDG"),
+        * with DMI_MATCH(DMI_BOARD_NAME, "ROCKY"),
         */
        {
        .callback = dmi_disable_osi_linux,
@@ -516,7 +530,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
         * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ11M"),
         */
        {
-       .callback = dmi_unknown_osi_linux,
+       .callback = dmi_disable_osi_linux,
        .ident = "Sony",
        .matches = {
                     DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
index 1b4cf98..8b0d4b7 100644 (file)
@@ -122,6 +122,31 @@ int acpi_bus_get_status(struct acpi_device *device)
 
 EXPORT_SYMBOL(acpi_bus_get_status);
 
+void acpi_bus_private_data_handler(acpi_handle handle,
+                                  u32 function, void *context)
+{
+       return;
+}
+EXPORT_SYMBOL(acpi_bus_private_data_handler);
+
+int acpi_bus_get_private_data(acpi_handle handle, void **data)
+{
+       acpi_status status = AE_OK;
+
+       if (!*data)
+               return -EINVAL;
+
+       status = acpi_get_data(handle, acpi_bus_private_data_handler, data);
+       if (ACPI_FAILURE(status) || !*data) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
+                               handle));
+               return -ENODEV;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(acpi_bus_get_private_data);
+
 /* --------------------------------------------------------------------------
                                  Power Management
    -------------------------------------------------------------------------- */
@@ -366,7 +391,6 @@ int acpi_bus_receive_event(struct acpi_bus_event *event)
        return 0;
 }
 
-EXPORT_SYMBOL(acpi_bus_receive_event);
 #endif /* CONFIG_ACPI_PROC_EVENT */
 
 /* --------------------------------------------------------------------------
index bf513e0..6df564f 100644 (file)
@@ -130,6 +130,63 @@ static int param_get_debug_level(char *buffer, struct kernel_param *kp) {
 module_param_call(debug_layer, param_set_uint, param_get_debug_layer, &acpi_dbg_layer, 0644);
 module_param_call(debug_level, param_set_uint, param_get_debug_level, &acpi_dbg_level, 0644);
 
+static char trace_method_name[6];
+module_param_string(trace_method_name, trace_method_name, 6, 0644);
+static unsigned int trace_debug_layer;
+module_param(trace_debug_layer, uint, 0644);
+static unsigned int trace_debug_level;
+module_param(trace_debug_level, uint, 0644);
+
+static int param_set_trace_state(const char *val, struct kernel_param *kp)
+{
+       int result = 0;
+
+       if (!strncmp(val, "enable", strlen("enable") - 1)) {
+               result = acpi_debug_trace(trace_method_name, trace_debug_level,
+                                         trace_debug_layer, 0);
+               if (result)
+                       result = -EBUSY;
+               goto exit;
+       }
+
+       if (!strncmp(val, "disable", strlen("disable") - 1)) {
+               int name = 0;
+               result = acpi_debug_trace((char *)&name, trace_debug_level,
+                                         trace_debug_layer, 0);
+               if (result)
+                       result = -EBUSY;
+               goto exit;
+       }
+
+       if (!strncmp(val, "1", 1)) {
+               result = acpi_debug_trace(trace_method_name, trace_debug_level,
+                                         trace_debug_layer, 1);
+               if (result)
+                       result = -EBUSY;
+               goto exit;
+       }
+
+       result = -EINVAL;
+exit:
+       return result;
+}
+
+static int param_get_trace_state(char *buffer, struct kernel_param *kp)
+{
+       if (!acpi_gbl_trace_method_name)
+               return sprintf(buffer, "disable");
+       else {
+               if (acpi_gbl_trace_flags & 1)
+                       return sprintf(buffer, "1");
+               else
+                       return sprintf(buffer, "enable");
+       }
+       return 0;
+}
+
+module_param_call(trace_state, param_set_trace_state, param_get_trace_state,
+                 NULL, 0644);
+
 /* --------------------------------------------------------------------------
                               FS Interface (/proc)
    -------------------------------------------------------------------------- */
index fc9da48..f501e08 100644 (file)
@@ -359,7 +359,9 @@ acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc)
 
        status = acpi_os_validate_address(obj_desc->region.space_id,
                                          obj_desc->region.address,
-                                         (acpi_size) obj_desc->region.length);
+                                         (acpi_size) obj_desc->region.length,
+                                         acpi_ut_get_node_name(node));
+
        if (ACPI_FAILURE(status)) {
                /*
                 * Invalid address/length. We will emit an error message and mark
index 1dabdf4..307cef6 100644 (file)
@@ -51,6 +51,12 @@ static struct atomic_notifier_head dock_notifier_list;
 static struct platform_device *dock_device;
 static char dock_device_name[] = "dock";
 
+static const struct acpi_device_id dock_device_ids[] = {
+       {"LNXDOCK", 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, dock_device_ids);
+
 struct dock_station {
        acpi_handle handle;
        unsigned long last_dock_time;
@@ -680,7 +686,7 @@ static ssize_t show_docked(struct device *dev,
        return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station));
 
 }
-DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
+static DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
 
 /*
  * show_flags - read method for flags file in sysfs
@@ -691,7 +697,7 @@ static ssize_t show_flags(struct device *dev,
        return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
 
 }
-DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
+static DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
 
 /*
  * write_undock - write method for "undock" file in sysfs
@@ -707,7 +713,7 @@ static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
        ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
        return ret ? ret: count;
 }
-DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
+static DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
 
 /*
  * show_dock_uid - read method for "uid" file in sysfs
@@ -723,7 +729,7 @@ static ssize_t show_dock_uid(struct device *dev,
 
        return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf);
 }
-DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
+static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
 
 /**
  * dock_add - add a new dock station
index 987b967..7222a18 100644 (file)
@@ -573,7 +573,7 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
                      void *handler_context, void *region_context)
 {
        struct acpi_ec *ec = handler_context;
-       int result = 0, i = 0;
+       int result = 0, i;
        u8 temp = 0;
 
        if ((address > 0xFF) || !value || !handler_context)
@@ -585,7 +585,18 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
        if (bits != 8 && acpi_strict)
                return AE_BAD_PARAMETER;
 
-       while (bits - i > 0) {
+       acpi_ec_burst_enable(ec);
+
+       if (function == ACPI_READ) {
+               result = acpi_ec_read(ec, address, &temp);
+               *value = temp;
+       } else {
+               temp = 0xff & (*value);
+               result = acpi_ec_write(ec, address, temp);
+       }
+
+       for (i = 8; unlikely(bits - i > 0); i += 8) {
+               ++address;
                if (function == ACPI_READ) {
                        result = acpi_ec_read(ec, address, &temp);
                        (*value) |= ((acpi_integer)temp) << i;
@@ -593,10 +604,10 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
                        temp = 0xff & ((*value) >> i);
                        result = acpi_ec_write(ec, address, temp);
                }
-               i += 8;
-               ++address;
        }
 
+       acpi_ec_burst_disable(ec);
+
        switch (result) {
        case -EINVAL:
                return AE_BAD_PARAMETER;
index 5c95863..5479dc0 100644 (file)
@@ -109,6 +109,34 @@ static const struct file_operations acpi_system_event_ops = {
 };
 #endif /* CONFIG_ACPI_PROC_EVENT */
 
+/* ACPI notifier chain */
+BLOCKING_NOTIFIER_HEAD(acpi_chain_head);
+
+int acpi_notifier_call_chain(struct acpi_device *dev, u32 type, u32 data)
+{
+       struct acpi_bus_event event;
+
+       strcpy(event.device_class, dev->pnp.device_class);
+       strcpy(event.bus_id, dev->pnp.bus_id);
+       event.type = type;
+       event.data = data;
+       return (blocking_notifier_call_chain(&acpi_chain_head, 0, (void *)&event)
+                        == NOTIFY_BAD) ? -EINVAL : 0;
+}
+EXPORT_SYMBOL(acpi_notifier_call_chain);
+
+int register_acpi_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&acpi_chain_head, nb);
+}
+EXPORT_SYMBOL(register_acpi_notifier);
+
+int unregister_acpi_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&acpi_chain_head, nb);
+}
+EXPORT_SYMBOL(unregister_acpi_notifier);
+
 #ifdef CONFIG_NET
 static unsigned int acpi_event_seqnum;
 struct acpi_genl_event {
index e412878..3048801 100644 (file)
@@ -259,7 +259,7 @@ u32 acpi_ev_fixed_event_detect(void)
                        enable_bit_mask)) {
 
                        /* Found an active (signalled) event */
-
+                       acpi_os_fixed_event_count(i);
                        int_status |= acpi_ev_fixed_event_dispatch((u32) i);
                }
        }
index e22f4a9..0dadd2a 100644 (file)
@@ -270,18 +270,18 @@ acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)
        case ACPI_GPE_TYPE_WAKE_RUN:
                ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);
 
-               /*lint -fallthrough */
+               /* fallthrough */
 
        case ACPI_GPE_TYPE_RUNTIME:
 
                /* Disable the requested runtime GPE */
 
                ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_RUN_ENABLED);
-               status = acpi_hw_write_gpe_enable_reg(gpe_event_info);
-               break;
+
+               /* fallthrough */
 
        default:
-               return_ACPI_STATUS(AE_BAD_PARAMETER);
+               acpi_hw_write_gpe_enable_reg(gpe_event_info);
        }
 
        return_ACPI_STATUS(AE_OK);
@@ -501,6 +501,7 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list)
  *              an interrupt handler.
  *
  ******************************************************************************/
+static void acpi_ev_asynch_enable_gpe(void *context);
 
 static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
 {
@@ -576,22 +577,30 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
                                         method_node)));
                }
        }
+       /* Defer enabling of GPE until all notify handlers are done */
+       acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ev_asynch_enable_gpe,
+                               gpe_event_info);
+       return_VOID;
+}
 
-       if ((local_gpe_event_info.flags & ACPI_GPE_XRUPT_TYPE_MASK) ==
+static void acpi_ev_asynch_enable_gpe(void *context)
+{
+       struct acpi_gpe_event_info *gpe_event_info = context;
+       acpi_status status;
+       if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) ==
            ACPI_GPE_LEVEL_TRIGGERED) {
                /*
                 * GPE is level-triggered, we clear the GPE status bit after
                 * handling the event.
                 */
-               status = acpi_hw_clear_gpe(&local_gpe_event_info);
+               status = acpi_hw_clear_gpe(gpe_event_info);
                if (ACPI_FAILURE(status)) {
                        return_VOID;
                }
        }
 
        /* Enable this GPE */
-
-       (void)acpi_hw_write_gpe_enable_reg(&local_gpe_event_info);
+       (void)acpi_hw_write_gpe_enable_reg(gpe_event_info);
        return_VOID;
 }
 
@@ -618,7 +627,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number)
 
        ACPI_FUNCTION_TRACE(ev_gpe_dispatch);
 
-       acpi_gpe_count++;
+       acpi_os_gpe_count(gpe_number);
 
        /*
         * If edge-triggered, clear the GPE status bit now.  Note that
index a6e149d..48cb705 100644 (file)
@@ -30,7 +30,7 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <asm/uaccess.h>
-
+#include <linux/thermal.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 
@@ -68,9 +68,55 @@ static struct acpi_driver acpi_fan_driver = {
                },
 };
 
+/* thermal cooling device callbacks */
+static int fan_get_max_state(struct thermal_cooling_device *cdev, char *buf)
+{
+       /* ACPI fan device only support two states: ON/OFF */
+       return sprintf(buf, "1\n");
+}
+
+static int fan_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
+{
+       struct acpi_device *device = cdev->devdata;
+       int state;
+       int result;
+
+       if (!device)
+               return -EINVAL;
+
+       result = acpi_bus_get_power(device->handle, &state);
+       if (result)
+               return result;
+
+       return sprintf(buf, "%s\n", state == ACPI_STATE_D3 ? "0" :
+                        (state == ACPI_STATE_D0 ? "1" : "unknown"));
+}
+
+static int
+fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
+{
+       struct acpi_device *device = cdev->devdata;
+       int result;
+
+       if (!device || (state != 0 && state != 1))
+               return -EINVAL;
+
+       result = acpi_bus_set_power(device->handle,
+                               state ? ACPI_STATE_D0 : ACPI_STATE_D3);
+
+       return result;
+}
+
+static struct thermal_cooling_device_ops fan_cooling_ops = {
+       .get_max_state = fan_get_max_state,
+       .get_cur_state = fan_get_cur_state,
+       .set_cur_state = fan_set_cur_state,
+};
+
 /* --------------------------------------------------------------------------
                               FS Interface (/proc)
    -------------------------------------------------------------------------- */
+#ifdef CONFIG_ACPI_PROCFS
 
 static struct proc_dir_entry *acpi_fan_dir;
 
@@ -171,7 +217,17 @@ static int acpi_fan_remove_fs(struct acpi_device *device)
 
        return 0;
 }
+#else
+static int acpi_fan_add_fs(struct acpi_device *device)
+{
+       return 0;
+}
 
+static int acpi_fan_remove_fs(struct acpi_device *device)
+{
+       return 0;
+}
+#endif
 /* --------------------------------------------------------------------------
                                  Driver Interface
    -------------------------------------------------------------------------- */
@@ -179,9 +235,8 @@ static int acpi_fan_remove_fs(struct acpi_device *device)
 static int acpi_fan_add(struct acpi_device *device)
 {
        int result = 0;
-       struct acpi_fan *fan = NULL;
        int state = 0;
-
+       struct thermal_cooling_device *cdev;
 
        if (!device)
                return -EINVAL;
@@ -199,6 +254,25 @@ static int acpi_fan_add(struct acpi_device *device)
        acpi_bus_set_power(device->handle, state);
        device->flags.force_power_state = 0;
 
+       cdev = thermal_cooling_device_register("Fan", device,
+                                               &fan_cooling_ops);
+       if (cdev)
+               printk(KERN_INFO PREFIX
+                       "%s is registered as cooling_device%d\n",
+                       device->dev.bus_id, cdev->id);
+       else
+               goto end;
+       acpi_driver_data(device) = cdev;
+       result = sysfs_create_link(&device->dev.kobj, &cdev->device.kobj,
+                                       "thermal_cooling");
+       if (result)
+               return result;
+
+       result = sysfs_create_link(&cdev->device.kobj, &device->dev.kobj,
+                                       "device");
+        if (result)
+                return result;
+
        result = acpi_fan_add_fs(device);
        if (result)
                goto end;
@@ -208,18 +282,20 @@ static int acpi_fan_add(struct acpi_device *device)
               !device->power.state ? "on" : "off");
 
       end:
-       if (result)
-               kfree(fan);
-
        return result;
 }
 
 static int acpi_fan_remove(struct acpi_device *device, int type)
 {
-       if (!device || !acpi_driver_data(device))
+       struct thermal_cooling_device *cdev = acpi_driver_data(device);
+
+       if (!device || !cdev)
                return -EINVAL;
 
        acpi_fan_remove_fs(device);
+       sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
+       sysfs_remove_link(&cdev->device.kobj, "device");
+       thermal_cooling_device_unregister(cdev);
 
        return 0;
 }
@@ -261,10 +337,12 @@ static int __init acpi_fan_init(void)
        int result = 0;
 
 
+#ifdef CONFIG_ACPI_PROCFS
        acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir);
        if (!acpi_fan_dir)
                return -ENODEV;
        acpi_fan_dir->owner = THIS_MODULE;
+#endif
 
        result = acpi_bus_register_driver(&acpi_fan_driver);
        if (result < 0) {
index 4893e25..eda0978 100644 (file)
@@ -36,8 +36,6 @@ int register_acpi_bus_type(struct acpi_bus_type *type)
        return -ENODEV;
 }
 
-EXPORT_SYMBOL(register_acpi_bus_type);
-
 int unregister_acpi_bus_type(struct acpi_bus_type *type)
 {
        if (acpi_disabled)
@@ -53,8 +51,6 @@ int unregister_acpi_bus_type(struct acpi_bus_type *type)
        return -ENODEV;
 }
 
-EXPORT_SYMBOL(unregister_acpi_bus_type);
-
 static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type)
 {
        struct acpi_bus_type *tmp, *ret = NULL;
index fd1c4ba..058d0be 100644 (file)
@@ -286,13 +286,13 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
        }
 
        /*
+        * 1) Disable/Clear all GPEs
         * 2) Enable all wakeup GPEs
         */
        status = acpi_hw_disable_all_gpes();
        if (ACPI_FAILURE(status)) {
                return_ACPI_STATUS(status);
        }
-
        acpi_gbl_system_awake_and_running = FALSE;
 
        status = acpi_hw_enable_all_wakeup_gpes();
index f39fbc6..b92133f 100644 (file)
@@ -443,6 +443,7 @@ acpi_ns_get_device_callback(acpi_handle obj_handle,
        struct acpica_device_id hid;
        struct acpi_compatible_id_list *cid;
        acpi_native_uint i;
+       int found;
 
        status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
        if (ACPI_FAILURE(status)) {
@@ -496,16 +497,19 @@ acpi_ns_get_device_callback(acpi_handle obj_handle,
 
                        /* Walk the CID list */
 
+                       found = 0;
                        for (i = 0; i < cid->count; i++) {
                                if (ACPI_STRNCMP(cid->id[i].value, info->hid,
                                                 sizeof(struct
-                                                       acpi_compatible_id)) !=
+                                                       acpi_compatible_id)) ==
                                    0) {
-                                       ACPI_FREE(cid);
-                                       return (AE_OK);
+                                       found = 1;
+                                       break;
                                }
                        }
                        ACPI_FREE(cid);
+                       if (!found)
+                               return (AE_OK);
                }
        }
 
index 0822d9f..5d59cb3 100644 (file)
@@ -78,6 +78,7 @@ int acpi_map_pxm_to_node(int pxm)
        return node;
 }
 
+#if 0
 void __cpuinit acpi_unmap_pxm_to_node(int node)
 {
        int pxm = node_to_pxm_map[node];
@@ -85,6 +86,7 @@ void __cpuinit acpi_unmap_pxm_to_node(int node)
        node_to_pxm_map[node] = PXM_INVAL;
        node_clear(node, nodes_found_map);
 }
+#endif  /*  0  */
 
 static void __init
 acpi_table_print_srat_entry(struct acpi_subtable_header *header)
@@ -247,7 +249,6 @@ int acpi_get_pxm(acpi_handle h)
        } while (ACPI_SUCCESS(status));
        return -1;
 }
-EXPORT_SYMBOL(acpi_get_pxm);
 
 int acpi_get_node(acpi_handle *handle)
 {
index e53fb51..27ccd68 100644 (file)
@@ -44,6 +44,8 @@
 #include <asm/uaccess.h>
 
 #include <linux/efi.h>
+#include <linux/ioport.h>
+#include <linux/list.h>
 
 #define _COMPONENT             ACPI_OS_SERVICES
 ACPI_MODULE_NAME("osl");
@@ -74,9 +76,25 @@ static void *acpi_irq_context;
 static struct workqueue_struct *kacpid_wq;
 static struct workqueue_struct *kacpi_notify_wq;
 
+struct acpi_res_list {
+       resource_size_t start;
+       resource_size_t end;
+       acpi_adr_space_type resource_type; /* IO port, System memory, ...*/
+       char name[5];   /* only can have a length of 4 chars, make use of this
+                          one instead of res->name, no need to kalloc then */
+       struct list_head resource_list;
+};
+
+static LIST_HEAD(resource_list_head);
+static DEFINE_SPINLOCK(acpi_res_lock);
+
 #define        OSI_STRING_LENGTH_MAX 64        /* arbitrary */
 static char osi_additional_string[OSI_STRING_LENGTH_MAX];
 
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+static int acpi_no_initrd_override;
+#endif
+
 /*
  * "Ode to _OSI(Linux)"
  *
@@ -120,7 +138,7 @@ static char osi_additional_string[OSI_STRING_LENGTH_MAX];
  */
 #define OSI_LINUX_ENABLE 0
 
-struct osi_linux {
+static struct osi_linux {
        unsigned int    enable:1;
        unsigned int    dmi:1;
        unsigned int    cmdline:1;
@@ -219,8 +237,6 @@ void acpi_os_printf(const char *fmt, ...)
        va_end(args);
 }
 
-EXPORT_SYMBOL(acpi_os_printf);
-
 void acpi_os_vprintf(const char *fmt, va_list args)
 {
        static char buffer[512];
@@ -250,11 +266,16 @@ acpi_physical_address __init acpi_os_get_root_pointer(void)
                               "System description tables not found\n");
                        return 0;
                }
-       } else
-               return acpi_find_rsdp();
+       } else {
+               acpi_physical_address pa = 0;
+
+               acpi_find_root_pointer(&pa);
+               return pa;
+       }
 }
 
-void __iomem *acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
+void __iomem *__init_refok
+acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
 {
        if (phys > ULONG_MAX) {
                printk(KERN_ERR PREFIX "Cannot map memory that high\n");
@@ -312,6 +333,67 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
        return AE_OK;
 }
 
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+struct acpi_table_header *acpi_find_dsdt_initrd(void)
+{
+       struct file *firmware_file;
+       mm_segment_t oldfs;
+       unsigned long len, len2;
+       struct acpi_table_header *dsdt_buffer, *ret = NULL;
+       struct kstat stat;
+       char *ramfs_dsdt_name = "/DSDT.aml";
+
+       printk(KERN_INFO PREFIX "Checking initramfs for custom DSDT");
+
+       /*
+        * Never do this at home, only the user-space is allowed to open a file.
+        * The clean way would be to use the firmware loader.
+        * But this code must be run before there is any userspace available.
+        * A static/init firmware infrastructure doesn't exist yet...
+        */
+       if (vfs_stat(ramfs_dsdt_name, &stat) < 0)
+               return ret;
+
+       len = stat.size;
+       /* check especially against empty files */
+       if (len <= 4) {
+               printk(KERN_ERR PREFIX "Failed: DSDT only %lu bytes.\n", len);
+               return ret;
+       }
+
+       firmware_file = filp_open(ramfs_dsdt_name, O_RDONLY, 0);
+       if (IS_ERR(firmware_file)) {
+               printk(KERN_ERR PREFIX "Failed to open %s.\n", ramfs_dsdt_name);
+               return ret;
+       }
+
+       dsdt_buffer = kmalloc(len, GFP_ATOMIC);
+       if (!dsdt_buffer) {
+               printk(KERN_ERR PREFIX "Failed to allocate %lu bytes.\n", len);
+               goto err;
+       }
+
+       oldfs = get_fs();
+       set_fs(KERNEL_DS);
+       len2 = vfs_read(firmware_file, (char __user *)dsdt_buffer, len,
+               &firmware_file->f_pos);
+       set_fs(oldfs);
+       if (len2 < len) {
+               printk(KERN_ERR PREFIX "Failed to read %lu bytes from %s.\n",
+                       len, ramfs_dsdt_name);
+               ACPI_FREE(dsdt_buffer);
+               goto err;
+       }
+
+       printk(KERN_INFO PREFIX "Found %lu byte DSDT in %s.\n",
+                       len, ramfs_dsdt_name);
+       ret = dsdt_buffer;
+err:
+       filp_close(firmware_file, NULL);
+       return ret;
+}
+#endif
+
 acpi_status
 acpi_os_table_override(struct acpi_table_header * existing_table,
                       struct acpi_table_header ** new_table)
@@ -319,20 +401,52 @@ acpi_os_table_override(struct acpi_table_header * existing_table,
        if (!existing_table || !new_table)
                return AE_BAD_PARAMETER;
 
+       *new_table = NULL;
+
 #ifdef CONFIG_ACPI_CUSTOM_DSDT
        if (strncmp(existing_table->signature, "DSDT", 4) == 0)
                *new_table = (struct acpi_table_header *)AmlCode;
-       else
-               *new_table = NULL;
-#else
-       *new_table = NULL;
 #endif
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+       if ((strncmp(existing_table->signature, "DSDT", 4) == 0) &&
+           !acpi_no_initrd_override) {
+               struct acpi_table_header *initrd_table;
+
+               initrd_table = acpi_find_dsdt_initrd();
+               if (initrd_table)
+                       *new_table = initrd_table;
+       }
+#endif
+       if (*new_table != NULL) {
+               printk(KERN_WARNING PREFIX "Override [%4.4s-%8.8s], "
+                          "this is unsafe: tainting kernel\n",
+                      existing_table->signature,
+                      existing_table->oem_table_id);
+               add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
+       }
        return AE_OK;
 }
 
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+int __init acpi_no_initrd_override_setup(char *s)
+{
+       acpi_no_initrd_override = 1;
+       return 1;
+}
+__setup("acpi_no_initrd_override", acpi_no_initrd_override_setup);
+#endif
+
 static irqreturn_t acpi_irq(int irq, void *dev_id)
 {
-       return (*acpi_irq_handler) (acpi_irq_context) ? IRQ_HANDLED : IRQ_NONE;
+       u32 handled;
+
+       handled = (*acpi_irq_handler) (acpi_irq_context);
+
+       if (handled) {
+               acpi_irq_handled++;
+               return IRQ_HANDLED;
+       } else
+               return IRQ_NONE;
 }
 
 acpi_status
@@ -341,6 +455,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
 {
        unsigned int irq;
 
+       acpi_irq_stats_init();
+
        /*
         * Ignore the GSI from the core, and use the value in our copy of the
         * FADT. It may not be the same if an interrupt source override exists
@@ -384,8 +500,6 @@ void acpi_os_sleep(acpi_integer ms)
        schedule_timeout_interruptible(msecs_to_jiffies(ms));
 }
 
-EXPORT_SYMBOL(acpi_os_sleep);
-
 void acpi_os_stall(u32 us)
 {
        while (us) {
@@ -399,8 +513,6 @@ void acpi_os_stall(u32 us)
        }
 }
 
-EXPORT_SYMBOL(acpi_os_stall);
-
 /*
  * Support ACPI 3.0 AML Timer operand
  * Returns 64-bit free-running, monotonically increasing timer
@@ -550,8 +662,6 @@ acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
        return (result ? AE_ERROR : AE_OK);
 }
 
-EXPORT_SYMBOL(acpi_os_read_pci_configuration);
-
 acpi_status
 acpi_os_write_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
                                acpi_integer value, u32 width)
@@ -661,25 +771,6 @@ static void acpi_os_execute_deferred(struct work_struct *work)
        dpc->function(dpc->context);
        kfree(dpc);
 
-       /* Yield cpu to notify thread */
-       cond_resched();
-
-       return;
-}
-
-static void acpi_os_execute_notify(struct work_struct *work)
-{
-       struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);
-
-       if (!dpc) {
-               printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
-               return;
-       }
-
-       dpc->function(dpc->context);
-
-       kfree(dpc);
-
        return;
 }
 
@@ -703,7 +794,7 @@ acpi_status acpi_os_execute(acpi_execute_type type,
 {
        acpi_status status = AE_OK;
        struct acpi_os_dpc *dpc;
-
+       struct workqueue_struct *queue;
        ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
                          "Scheduling function [%p(%p)] for deferred execution.\n",
                          function, context));
@@ -727,20 +818,13 @@ acpi_status acpi_os_execute(acpi_execute_type type,
        dpc->function = function;
        dpc->context = context;
 
-       if (type == OSL_NOTIFY_HANDLER) {
-               INIT_WORK(&dpc->work, acpi_os_execute_notify);
-               if (!queue_work(kacpi_notify_wq, &dpc->work)) {
-                       status = AE_ERROR;
-                       kfree(dpc);
-               }
-       } else {
-               INIT_WORK(&dpc->work, acpi_os_execute_deferred);
-               if (!queue_work(kacpid_wq, &dpc->work)) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "Call to queue_work() failed.\n"));
-                       status = AE_ERROR;
-                       kfree(dpc);
-               }
+       INIT_WORK(&dpc->work, acpi_os_execute_deferred);
+       queue = (type == OSL_NOTIFY_HANDLER) ? kacpi_notify_wq : kacpid_wq;
+       if (!queue_work(queue, &dpc->work)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                         "Call to queue_work() failed.\n"));
+               status = AE_ERROR;
+               kfree(dpc);
        }
        return_ACPI_STATUS(status);
 }
@@ -793,8 +877,6 @@ acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle)
        return AE_OK;
 }
 
-EXPORT_SYMBOL(acpi_os_create_semaphore);
-
 /*
  * TODO: A better way to delete semaphores?  Linux doesn't have a
  * 'delete_semaphore()' function -- may result in an invalid
@@ -818,8 +900,6 @@ acpi_status acpi_os_delete_semaphore(acpi_handle handle)
        return AE_OK;
 }
 
-EXPORT_SYMBOL(acpi_os_delete_semaphore);
-
 /*
  * TODO: The kernel doesn't have a 'down_timeout' function -- had to
  * improvise.  The process is to sleep for one scheduler quantum
@@ -912,8 +992,6 @@ acpi_status acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 timeout)
        return status;
 }
 
-EXPORT_SYMBOL(acpi_os_wait_semaphore);
-
 /*
  * TODO: Support for units > 1?
  */
@@ -936,8 +1014,6 @@ acpi_status acpi_os_signal_semaphore(acpi_handle handle, u32 units)
        return AE_OK;
 }
 
-EXPORT_SYMBOL(acpi_os_signal_semaphore);
-
 #ifdef ACPI_FUTURE_USAGE
 u32 acpi_os_get_line(char *buffer)
 {
@@ -981,8 +1057,6 @@ acpi_status acpi_os_signal(u32 function, void *info)
        return AE_OK;
 }
 
-EXPORT_SYMBOL(acpi_os_signal);
-
 static int __init acpi_os_name_setup(char *str)
 {
        char *p = acpi_os_name;
@@ -1102,6 +1176,128 @@ static int __init acpi_wake_gpes_always_on_setup(char *str)
 
 __setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup);
 
+/* Check of resource interference between native drivers and ACPI
+ * OperationRegions (SystemIO and System Memory only).
+ * IO ports and memory declared in ACPI might be used by the ACPI subsystem
+ * in arbitrary AML code and can interfere with legacy drivers.
+ * acpi_enforce_resources= can be set to:
+ *
+ *   - strict           (2)
+ *     -> further driver trying to access the resources will not load
+ *   - lax (default)    (1)
+ *     -> further driver trying to access the resources will load, but you
+ *     get a system message that something might go wrong...
+ *
+ *   - no               (0)
+ *     -> ACPI Operation Region resources will not be registered
+ *
+ */
+#define ENFORCE_RESOURCES_STRICT 2
+#define ENFORCE_RESOURCES_LAX    1
+#define ENFORCE_RESOURCES_NO     0
+
+static unsigned int acpi_enforce_resources = ENFORCE_RESOURCES_LAX;
+
+static int __init acpi_enforce_resources_setup(char *str)
+{
+       if (str == NULL || *str == '\0')
+               return 0;
+
+       if (!strcmp("strict", str))
+               acpi_enforce_resources = ENFORCE_RESOURCES_STRICT;
+       else if (!strcmp("lax", str))
+               acpi_enforce_resources = ENFORCE_RESOURCES_LAX;
+       else if (!strcmp("no", str))
+               acpi_enforce_resources = ENFORCE_RESOURCES_NO;
+
+       return 1;
+}
+
+__setup("acpi_enforce_resources=", acpi_enforce_resources_setup);
+
+/* Check for resource conflicts between ACPI OperationRegions and native
+ * drivers */
+int acpi_check_resource_conflict(struct resource *res)
+{
+       struct acpi_res_list *res_list_elem;
+       int ioport;
+       int clash = 0;
+
+       if (acpi_enforce_resources == ENFORCE_RESOURCES_NO)
+               return 0;
+       if (!(res->flags & IORESOURCE_IO) && !(res->flags & IORESOURCE_MEM))
+               return 0;
+
+       ioport = res->flags & IORESOURCE_IO;
+
+       spin_lock(&acpi_res_lock);
+       list_for_each_entry(res_list_elem, &resource_list_head,
+                           resource_list) {
+               if (ioport && (res_list_elem->resource_type
+                              != ACPI_ADR_SPACE_SYSTEM_IO))
+                       continue;
+               if (!ioport && (res_list_elem->resource_type
+                               != ACPI_ADR_SPACE_SYSTEM_MEMORY))
+                       continue;
+
+               if (res->end < res_list_elem->start
+                   || res_list_elem->end < res->start)
+                       continue;
+               clash = 1;
+               break;
+       }
+       spin_unlock(&acpi_res_lock);
+
+       if (clash) {
+               if (acpi_enforce_resources != ENFORCE_RESOURCES_NO) {
+                       printk(KERN_INFO "%sACPI: %s resource %s [0x%llx-0x%llx]"
+                              " conflicts with ACPI region %s"
+                              " [0x%llx-0x%llx]\n",
+                              acpi_enforce_resources == ENFORCE_RESOURCES_LAX
+                              ? KERN_WARNING : KERN_ERR,
+                              ioport ? "I/O" : "Memory", res->name,
+                              (long long) res->start, (long long) res->end,
+                              res_list_elem->name,
+                              (long long) res_list_elem->start,
+                              (long long) res_list_elem->end);
+                       printk(KERN_INFO "ACPI: Device needs an ACPI driver\n");
+               }
+               if (acpi_enforce_resources == ENFORCE_RESOURCES_STRICT)
+                       return -EBUSY;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(acpi_check_resource_conflict);
+
+int acpi_check_region(resource_size_t start, resource_size_t n,
+                     const char *name)
+{
+       struct resource res = {
+               .start = start,
+               .end   = start + n - 1,
+               .name  = name,
+               .flags = IORESOURCE_IO,
+       };
+
+       return acpi_check_resource_conflict(&res);
+}
+EXPORT_SYMBOL(acpi_check_region);
+
+int acpi_check_mem_region(resource_size_t start, resource_size_t n,
+                     const char *name)
+{
+       struct resource res = {
+               .start = start,
+               .end   = start + n - 1,
+               .name  = name,
+               .flags = IORESOURCE_MEM,
+       };
+
+       return acpi_check_resource_conflict(&res);
+
+}
+EXPORT_SYMBOL(acpi_check_mem_region);
+
 /*
  * Acquire a spinlock.
  *
@@ -1213,24 +1409,24 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object)
  *
  *     Returns 0 on success
  */
-int acpi_dmi_dump(void)
+static int acpi_dmi_dump(void)
 {
 
        if (!dmi_available)
                return -1;
 
        printk(KERN_NOTICE PREFIX "DMI System Vendor: %s\n",
-               dmi_get_slot(DMI_SYS_VENDOR));
+               dmi_get_system_info(DMI_SYS_VENDOR));
        printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n",
-               dmi_get_slot(DMI_PRODUCT_NAME));
+               dmi_get_system_info(DMI_PRODUCT_NAME));
        printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n",
-               dmi_get_slot(DMI_PRODUCT_VERSION));
+               dmi_get_system_info(DMI_PRODUCT_VERSION));
        printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n",
-               dmi_get_slot(DMI_BOARD_NAME));
+               dmi_get_system_info(DMI_BOARD_NAME));
        printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n",
-               dmi_get_slot(DMI_BIOS_VENDOR));
+               dmi_get_system_info(DMI_BIOS_VENDOR));
        printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n",
-               dmi_get_slot(DMI_BIOS_DATE));
+               dmi_get_system_info(DMI_BIOS_DATE));
 
        return 0;
 }
@@ -1303,10 +1499,46 @@ acpi_status
 acpi_os_validate_address (
     u8                   space_id,
     acpi_physical_address   address,
-    acpi_size               length)
+    acpi_size               length,
+    char *name)
 {
+       struct acpi_res_list *res;
+       if (acpi_enforce_resources == ENFORCE_RESOURCES_NO)
+               return AE_OK;
 
-    return AE_OK;
+       switch (space_id) {
+       case ACPI_ADR_SPACE_SYSTEM_IO:
+       case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+               /* Only interference checks against SystemIO and SytemMemory
+                  are needed */
+               res = kzalloc(sizeof(struct acpi_res_list), GFP_KERNEL);
+               if (!res)
+                       return AE_OK;
+               /* ACPI names are fixed to 4 bytes, still better use strlcpy */
+               strlcpy(res->name, name, 5);
+               res->start = address;
+               res->end = address + length - 1;
+               res->resource_type = space_id;
+               spin_lock(&acpi_res_lock);
+               list_add(&res->resource_list, &resource_list_head);
+               spin_unlock(&acpi_res_lock);
+               pr_debug("Added %s resource: start: 0x%llx, end: 0x%llx, "
+                        "name: %s\n", (space_id == ACPI_ADR_SPACE_SYSTEM_IO)
+                        ? "SystemIO" : "System Memory",
+                        (unsigned long long)res->start,
+                        (unsigned long long)res->end,
+                        res->name);
+               break;
+       case ACPI_ADR_SPACE_PCI_CONFIG:
+       case ACPI_ADR_SPACE_EC:
+       case ACPI_ADR_SPACE_SMBUS:
+       case ACPI_ADR_SPACE_CMOS:
+       case ACPI_ADR_SPACE_PCI_BAR_TARGET:
+       case ACPI_ADR_SPACE_DATA_TABLE:
+       case ACPI_ADR_SPACE_FIXED_HARDWARE:
+               break;
+       }
+       return AE_OK;
 }
 
 #endif
index 388300d..4b252ea 100644 (file)
@@ -44,6 +44,8 @@ struct acpi_pci_data {
        struct pci_dev *dev;
 };
 
+static int acpi_pci_unbind(struct acpi_device *device);
+
 static void acpi_pci_data_handler(acpi_handle handle, u32 function,
                                  void *context)
 {
@@ -267,7 +269,7 @@ int acpi_pci_bind(struct acpi_device *device)
        return result;
 }
 
-int acpi_pci_unbind(struct acpi_device *device)
+static int acpi_pci_unbind(struct acpi_device *device)
 {
        int result = 0;
        acpi_status status = AE_OK;
index 62010c2..7f19859 100644 (file)
@@ -51,10 +51,8 @@ static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(int segment,
                                                          int bus,
                                                          int device, int pin)
 {
-       struct list_head *node = NULL;
        struct acpi_prt_entry *entry = NULL;
 
-
        if (!acpi_prt.count)
                return NULL;
 
@@ -64,8 +62,7 @@ static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(int segment,
         *
         */
        spin_lock(&acpi_prt_lock);
-       list_for_each(node, &acpi_prt.entries) {
-               entry = list_entry(node, struct acpi_prt_entry, node);
+       list_for_each_entry(entry, &acpi_prt.entries, node) {
                if ((segment == entry->id.segment)
                    && (bus == entry->id.bus)
                    && (device == entry->id.device)
@@ -478,8 +475,6 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
        return 0;
 }
 
-EXPORT_SYMBOL(acpi_pci_irq_enable);
-
 /* FIXME: implement x86/x86_64 version */
 void __attribute__ ((weak)) acpi_unregister_gsi(u32 i)
 {
index 5400ea1..233c40c 100644 (file)
@@ -95,7 +95,7 @@ static struct {
        int count;
        struct list_head entries;
 } acpi_link;
-DEFINE_MUTEX(acpi_link_lock);
+static DEFINE_MUTEX(acpi_link_lock);
 
 /* --------------------------------------------------------------------------
                             PCI Link Device Management
index af1769a..76bf6d9 100644 (file)
@@ -458,11 +458,9 @@ int acpi_power_transition(struct acpi_device *device, int state)
        }
 
      end:
-       if (result) {
+       if (result)
                device->power.state = ACPI_STATE_UNKNOWN;
-               printk(KERN_WARNING PREFIX "Transitioning device [%s] to D%d\n",
-                             device->pnp.bus_id, state);
-       } else {
+       else {
        /* We shouldn't change the state till all above operations succeed */
                device->power.state = state;
        }
index e48ee4f..75ccf5d 100644 (file)
@@ -668,6 +668,24 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device)
 
        acpi_processor_power_init(pr, device);
 
+       pr->cdev = thermal_cooling_device_register("Processor", device,
+                                               &processor_cooling_ops);
+       if (pr->cdev)
+               printk(KERN_INFO PREFIX
+                       "%s is registered as cooling_device%d\n",
+                       device->dev.bus_id, pr->cdev->id);
+       else
+               goto end;
+
+       result = sysfs_create_link(&device->dev.kobj, &pr->cdev->device.kobj,
+                                       "thermal_cooling");
+       if (result)
+               return result;
+       result = sysfs_create_link(&pr->cdev->device.kobj, &device->dev.kobj,
+                                       "device");
+       if (result)
+               return result;
+
        if (pr->flags.throttling) {
                printk(KERN_INFO PREFIX "%s [%s] (supports",
                       acpi_device_name(device), acpi_device_bid(device));
@@ -791,6 +809,11 @@ static int acpi_processor_remove(struct acpi_device *device, int type)
 
        acpi_processor_remove_fs(device);
 
+       sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
+       sysfs_remove_link(&pr->cdev->device.kobj, "device");
+       thermal_cooling_device_unregister(pr->cdev);
+       pr->cdev = NULL;
+
        processors[pr->id] = NULL;
 
        kfree(pr);
@@ -812,11 +835,18 @@ static int is_processor_present(acpi_handle handle)
 
 
        status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
-       if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) {
-               ACPI_EXCEPTION((AE_INFO, status, "Processor Device is not present"));
-               return 0;
-       }
-       return 1;
+       /*
+        * if a processor object does not have an _STA object,
+        * OSPM assumes that the processor is present.
+        */
+       if (status == AE_NOT_FOUND)
+               return 1;
+
+       if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_PRESENT))
+               return 1;
+
+       ACPI_EXCEPTION((AE_INFO, status, "Processor Device is not present"));
+       return 0;
 }
 
 static
@@ -1061,6 +1091,8 @@ static int __init acpi_processor_init(void)
 
        acpi_processor_ppc_init();
 
+       acpi_processor_throttling_init();
+
        return 0;
 
 out_cpuidle:
index 199ea21..32003fd 100644 (file)
@@ -98,6 +98,9 @@ module_param(bm_history, uint, 0644);
 
 static int acpi_processor_set_power_policy(struct acpi_processor *pr);
 
+#else  /* CONFIG_CPU_IDLE */
+static unsigned int latency_factor __read_mostly = 2;
+module_param(latency_factor, uint, 0644);
 #endif
 
 /*
@@ -201,6 +204,10 @@ static inline u32 ticks_elapsed_in_us(u32 t1, u32 t2)
                return PM_TIMER_TICKS_TO_US((0xFFFFFFFF - t1) + t2);
 }
 
+/*
+ * Callers should disable interrupts before the call and enable
+ * interrupts after return.
+ */
 static void acpi_safe_halt(void)
 {
        current_thread_info()->status &= ~TS_POLLING;
@@ -261,7 +268,7 @@ static atomic_t c3_cpu_count;
 /* Common C-state entry for C2, C3, .. */
 static void acpi_cstate_enter(struct acpi_processor_cx *cstate)
 {
-       if (cstate->space_id == ACPI_CSTATE_FFH) {
+       if (cstate->entry_method == ACPI_CSTATE_FFH) {
                /* Call into architectural FFH based C-state */
                acpi_processor_ffh_cstate_enter(cstate);
        } else {
@@ -413,6 +420,8 @@ static void acpi_processor_idle(void)
                        pm_idle_save();
                else
                        acpi_safe_halt();
+
+               local_irq_enable();
                return;
        }
 
@@ -521,6 +530,7 @@ static void acpi_processor_idle(void)
                 *       skew otherwise.
                 */
                sleep_ticks = 0xFFFFFFFF;
+               local_irq_enable();
                break;
 
        case ACPI_STATE_C2:
@@ -922,20 +932,20 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr)
                cx.address = reg->address;
                cx.index = current_count + 1;
 
-               cx.space_id = ACPI_CSTATE_SYSTEMIO;
+               cx.entry_method = ACPI_CSTATE_SYSTEMIO;
                if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
                        if (acpi_processor_ffh_cstate_probe
                                        (pr->id, &cx, reg) == 0) {
-                               cx.space_id = ACPI_CSTATE_FFH;
-                       } else if (cx.type != ACPI_STATE_C1) {
+                               cx.entry_method = ACPI_CSTATE_FFH;
+                       } else if (cx.type == ACPI_STATE_C1) {
                                /*
                                 * C1 is a special case where FIXED_HARDWARE
                                 * can be handled in non-MWAIT way as well.
                                 * In that case, save this _CST entry info.
-                                * That is, we retain space_id of SYSTEM_IO for
-                                * halt based C1.
                                 * Otherwise, ignore this info and continue.
                                 */
+                               cx.entry_method = ACPI_CSTATE_HALT;
+                       } else {
                                continue;
                        }
                }
@@ -1369,12 +1379,16 @@ static inline void acpi_idle_update_bm_rld(struct acpi_processor *pr,
 /**
  * acpi_idle_do_entry - a helper function that does C2 and C3 type entry
  * @cx: cstate data
+ *
+ * Caller disables interrupt before call and enables interrupt after return.
  */
 static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx)
 {
-       if (cx->space_id == ACPI_CSTATE_FFH) {
+       if (cx->entry_method == ACPI_CSTATE_FFH) {
                /* Call into architectural FFH based C-state */
                acpi_processor_ffh_cstate_enter(cx);
+       } else if (cx->entry_method == ACPI_CSTATE_HALT) {
+               acpi_safe_halt();
        } else {
                int unused;
                /* IO port based C-state */
@@ -1396,21 +1410,27 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx)
 static int acpi_idle_enter_c1(struct cpuidle_device *dev,
                              struct cpuidle_state *state)
 {
+       u32 t1, t2;
        struct acpi_processor *pr;
        struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
+
        pr = processors[smp_processor_id()];
 
        if (unlikely(!pr))
                return 0;
 
+       local_irq_disable();
        if (pr->flags.bm_check)
                acpi_idle_update_bm_rld(pr, cx);
 
-       acpi_safe_halt();
+       t1 = inl(acpi_gbl_FADT.xpm_timer_block.address);
+       acpi_idle_do_entry(cx);
+       t2 = inl(acpi_gbl_FADT.xpm_timer_block.address);
 
+       local_irq_enable();
        cx->usage++;
 
-       return 0;
+       return ticks_elapsed_in_us(t1, t2);
 }
 
 /**
@@ -1517,7 +1537,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
                if (dev->safe_state) {
                        return dev->safe_state->enter(dev, dev->safe_state);
                } else {
+                       local_irq_disable();
                        acpi_safe_halt();
+                       local_irq_enable();
                        return 0;
                }
        }
@@ -1609,7 +1631,7 @@ struct cpuidle_driver acpi_idle_driver = {
  */
 static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 {
-       int i, count = 0;
+       int i, count = CPUIDLE_DRIVER_STATE_START;
        struct acpi_processor_cx *cx;
        struct cpuidle_state *state;
        struct cpuidle_device *dev = &pr->power.dev;
@@ -1638,13 +1660,14 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 
                snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i);
                state->exit_latency = cx->latency;
-               state->target_residency = cx->latency * 6;
+               state->target_residency = cx->latency * latency_factor;
                state->power_usage = cx->power;
 
                state->flags = 0;
                switch (cx->type) {
                        case ACPI_STATE_C1:
                        state->flags |= CPUIDLE_FLAG_SHALLOW;
+                       state->flags |= CPUIDLE_FLAG_TIME_VALID;
                        state->enter = acpi_idle_enter_c1;
                        dev->safe_state = state;
                        break;
@@ -1667,6 +1690,8 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
                }
 
                count++;
+               if (count == CPUIDLE_STATE_MAX)
+                       break;
        }
 
        dev->state_count = count;
index 463b024..f32010b 100644 (file)
@@ -60,6 +60,11 @@ static DEFINE_MUTEX(performance_mutex);
  * policy is adjusted accordingly.
  */
 
+static unsigned int ignore_ppc = 0;
+module_param(ignore_ppc, uint, 0644);
+MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \
+                "limited by BIOS, this should help");
+
 #define PPC_REGISTERED   1
 #define PPC_IN_USE       2
 
@@ -72,6 +77,9 @@ static int acpi_processor_ppc_notifier(struct notifier_block *nb,
        struct acpi_processor *pr;
        unsigned int ppc = 0;
 
+       if (ignore_ppc)
+               return 0;
+
        mutex_lock(&performance_mutex);
 
        if (event != CPUFREQ_INCOMPATIBLE)
@@ -130,7 +138,13 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
 
 int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
 {
-       int ret = acpi_processor_get_platform_limit(pr);
+       int ret;
+
+       if (ignore_ppc)
+               return 0;
+
+       ret = acpi_processor_get_platform_limit(pr);
+
        if (ret < 0)
                return (ret);
        else
index 06e6f3f..9cb43f5 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/cpufreq.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
+#include <linux/sysdev.h>
 
 #include <asm/uaccess.h>
 
@@ -93,6 +94,9 @@ static int acpi_processor_apply_limit(struct acpi_processor *pr)
  * _any_ cpufreq driver and not only the acpi-cpufreq driver.
  */
 
+#define CPUFREQ_THERMAL_MIN_STEP 0
+#define CPUFREQ_THERMAL_MAX_STEP 3
+
 static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS];
 static unsigned int acpi_thermal_cpufreq_is_init = 0;
 
@@ -109,8 +113,9 @@ static int acpi_thermal_cpufreq_increase(unsigned int cpu)
        if (!cpu_has_cpufreq(cpu))
                return -ENODEV;
 
-       if (cpufreq_thermal_reduction_pctg[cpu] < 60) {
-               cpufreq_thermal_reduction_pctg[cpu] += 20;
+       if (cpufreq_thermal_reduction_pctg[cpu] <
+               CPUFREQ_THERMAL_MAX_STEP) {
+               cpufreq_thermal_reduction_pctg[cpu]++;
                cpufreq_update_policy(cpu);
                return 0;
        }
@@ -123,8 +128,9 @@ static int acpi_thermal_cpufreq_decrease(unsigned int cpu)
        if (!cpu_has_cpufreq(cpu))
                return -ENODEV;
 
-       if (cpufreq_thermal_reduction_pctg[cpu] > 20)
-               cpufreq_thermal_reduction_pctg[cpu] -= 20;
+       if (cpufreq_thermal_reduction_pctg[cpu] >
+               (CPUFREQ_THERMAL_MIN_STEP + 1))
+               cpufreq_thermal_reduction_pctg[cpu]--;
        else
                cpufreq_thermal_reduction_pctg[cpu] = 0;
        cpufreq_update_policy(cpu);
@@ -143,7 +149,7 @@ static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb,
 
        max_freq =
            (policy->cpuinfo.max_freq *
-            (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100;
+            (100 - cpufreq_thermal_reduction_pctg[policy->cpu] * 20)) / 100;
 
        cpufreq_verify_within_limits(policy, 0, max_freq);
 
@@ -155,6 +161,32 @@ static struct notifier_block acpi_thermal_cpufreq_notifier_block = {
        .notifier_call = acpi_thermal_cpufreq_notifier,
 };
 
+static int cpufreq_get_max_state(unsigned int cpu)
+{
+       if (!cpu_has_cpufreq(cpu))
+               return 0;
+
+       return CPUFREQ_THERMAL_MAX_STEP;
+}
+
+static int cpufreq_get_cur_state(unsigned int cpu)
+{
+       if (!cpu_has_cpufreq(cpu))
+               return 0;
+
+       return cpufreq_thermal_reduction_pctg[cpu];
+}
+
+static int cpufreq_set_cur_state(unsigned int cpu, int state)
+{
+       if (!cpu_has_cpufreq(cpu))
+               return 0;
+
+       cpufreq_thermal_reduction_pctg[cpu] = state;
+       cpufreq_update_policy(cpu);
+       return 0;
+}
+
 void acpi_thermal_cpufreq_init(void)
 {
        int i;
@@ -179,6 +211,20 @@ void acpi_thermal_cpufreq_exit(void)
 }
 
 #else                          /* ! CONFIG_CPU_FREQ */
+static int cpufreq_get_max_state(unsigned int cpu)
+{
+       return 0;
+}
+
+static int cpufreq_get_cur_state(unsigned int cpu)
+{
+       return 0;
+}
+
+static int cpufreq_set_cur_state(unsigned int cpu, int state)
+{
+       return 0;
+}
 
 static int acpi_thermal_cpufreq_increase(unsigned int cpu)
 {
@@ -310,6 +356,84 @@ int acpi_processor_get_limit_info(struct acpi_processor *pr)
        return 0;
 }
 
+/* thermal coolign device callbacks */
+static int acpi_processor_max_state(struct acpi_processor *pr)
+{
+       int max_state = 0;
+
+       /*
+        * There exists four states according to
+        * cpufreq_thermal_reduction_ptg. 0, 1, 2, 3
+        */
+       max_state += cpufreq_get_max_state(pr->id);
+       if (pr->flags.throttling)
+               max_state += (pr->throttling.state_count -1);
+
+       return max_state;
+}
+static int
+processor_get_max_state(struct thermal_cooling_device *cdev, char *buf)
+{
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_processor *pr = acpi_driver_data(device);
+
+       if (!device || !pr)
+               return -EINVAL;
+
+       return sprintf(buf, "%d\n", acpi_processor_max_state(pr));
+}
+
+static int
+processor_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
+{
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_processor *pr = acpi_driver_data(device);
+       int cur_state;
+
+       if (!device || !pr)
+               return -EINVAL;
+
+       cur_state = cpufreq_get_cur_state(pr->id);
+       if (pr->flags.throttling)
+               cur_state += pr->throttling.state;
+
+       return sprintf(buf, "%d\n", cur_state);
+}
+
+static int
+processor_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
+{
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_processor *pr = acpi_driver_data(device);
+       int result = 0;
+       int max_pstate;
+
+       if (!device || !pr)
+               return -EINVAL;
+
+       max_pstate = cpufreq_get_max_state(pr->id);
+
+       if (state > acpi_processor_max_state(pr))
+               return -EINVAL;
+
+       if (state <= max_pstate) {
+               if (pr->flags.throttling && pr->throttling.state)
+                       result = acpi_processor_set_throttling(pr, 0);
+               cpufreq_set_cur_state(pr->id, state);
+       } else {
+               cpufreq_set_cur_state(pr->id, max_pstate);
+               result = acpi_processor_set_throttling(pr,
+                               state - max_pstate);
+       }
+       return result;
+}
+
+struct thermal_cooling_device_ops processor_cooling_ops = {
+       .get_max_state = processor_get_max_state,
+       .get_cur_state = processor_get_cur_state,
+       .set_cur_state = processor_set_cur_state,
+};
+
 /* /proc interface */
 
 static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset)
index 1685b40..1b8e592 100644 (file)
 #define _COMPONENT              ACPI_PROCESSOR_COMPONENT
 ACPI_MODULE_NAME("processor_throttling");
 
+struct throttling_tstate {
+       unsigned int cpu;               /* cpu nr */
+       int target_state;               /* target T-state */
+};
+
+#define THROTTLING_PRECHANGE       (1)
+#define THROTTLING_POSTCHANGE      (2)
+
 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_update_tsd_coord(void)
+{
+       int count, count_target;
+       int retval = 0;
+       unsigned int i, j;
+       cpumask_t covered_cpus;
+       struct acpi_processor *pr, *match_pr;
+       struct acpi_tsd_package *pdomain, *match_pdomain;
+       struct acpi_processor_throttling *pthrottling, *match_pthrottling;
+
+       /*
+        * Now that we have _TSD data from all CPUs, lets setup T-state
+        * coordination between all CPUs.
+        */
+       for_each_possible_cpu(i) {
+               pr = processors[i];
+               if (!pr)
+                       continue;
+
+               /* Basic validity check for domain info */
+               pthrottling = &(pr->throttling);
+
+               /*
+                * If tsd package for one cpu is invalid, the coordination
+                * among all CPUs is thought as invalid.
+                * Maybe it is ugly.
+                */
+               if (!pthrottling->tsd_valid_flag) {
+                       retval = -EINVAL;
+                       break;
+               }
+       }
+       if (retval)
+               goto err_ret;
+
+       cpus_clear(covered_cpus);
+       for_each_possible_cpu(i) {
+               pr = processors[i];
+               if (!pr)
+                       continue;
+
+               if (cpu_isset(i, covered_cpus))
+                       continue;
+               pthrottling = &pr->throttling;
+
+               pdomain = &(pthrottling->domain_info);
+               cpu_set(i, pthrottling->shared_cpu_map);
+               cpu_set(i, covered_cpus);
+               /*
+                * If the number of processor in the TSD domain is 1, it is
+                * unnecessary to parse the coordination for this CPU.
+                */
+               if (pdomain->num_processors <= 1)
+                       continue;
+
+               /* Validate the Domain info */
+               count_target = pdomain->num_processors;
+               count = 1;
+
+               for_each_possible_cpu(j) {
+                       if (i == j)
+                               continue;
+
+                       match_pr = processors[j];
+                       if (!match_pr)
+                               continue;
+
+                       match_pthrottling = &(match_pr->throttling);
+                       match_pdomain = &(match_pthrottling->domain_info);
+                       if (match_pdomain->domain != pdomain->domain)
+                               continue;
+
+                       /* Here i and j are in the same domain.
+                        * If two TSD packages have the same domain, they
+                        * should have the same num_porcessors and
+                        * coordination type. Otherwise it will be regarded
+                        * as illegal.
+                        */
+                       if (match_pdomain->num_processors != count_target) {
+                               retval = -EINVAL;
+                               goto err_ret;
+                       }
+
+                       if (pdomain->coord_type != match_pdomain->coord_type) {
+                               retval = -EINVAL;
+                               goto err_ret;
+                       }
+
+                       cpu_set(j, covered_cpus);
+                       cpu_set(j, pthrottling->shared_cpu_map);
+                       count++;
+               }
+               for_each_possible_cpu(j) {
+                       if (i == j)
+                               continue;
+
+                       match_pr = processors[j];
+                       if (!match_pr)
+                               continue;
+
+                       match_pthrottling = &(match_pr->throttling);
+                       match_pdomain = &(match_pthrottling->domain_info);
+                       if (match_pdomain->domain != pdomain->domain)
+                               continue;
+
+                       /*
+                        * If some CPUS have the same domain, they
+                        * will have the same shared_cpu_map.
+                        */
+                       match_pthrottling->shared_cpu_map =
+                               pthrottling->shared_cpu_map;
+               }
+       }
+
+err_ret:
+       for_each_possible_cpu(i) {
+               pr = processors[i];
+               if (!pr)
+                       continue;
+
+               /*
+                * Assume no coordination on any error parsing domain info.
+                * The coordination type will be forced as SW_ALL.
+                */
+               if (retval) {
+                       pthrottling = &(pr->throttling);
+                       cpus_clear(pthrottling->shared_cpu_map);
+                       cpu_set(i, pthrottling->shared_cpu_map);
+                       pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL;
+               }
+       }
+
+       return retval;
+}
+
+/*
+ * Update the T-state coordination after the _TSD
+ * data for all cpus is obtained.
+ */
+void acpi_processor_throttling_init(void)
+{
+       if (acpi_processor_update_tsd_coord())
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Assume no T-state coordination\n"));
+
+       return;
+}
+
+static int acpi_processor_throttling_notifier(unsigned long event, void *data)
+{
+       struct throttling_tstate *p_tstate = data;
+       struct acpi_processor *pr;
+       unsigned int cpu ;
+       int target_state;
+       struct acpi_processor_limit *p_limit;
+       struct acpi_processor_throttling *p_throttling;
+
+       cpu = p_tstate->cpu;
+       pr = processors[cpu];
+       if (!pr) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid pr pointer\n"));
+               return 0;
+       }
+       if (!pr->flags.throttling) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Throttling control is "
+                               "unsupported on CPU %d\n", cpu));
+               return 0;
+       }
+       target_state = p_tstate->target_state;
+       p_throttling = &(pr->throttling);
+       switch (event) {
+       case THROTTLING_PRECHANGE:
+               /*
+                * Prechange event is used to choose one proper t-state,
+                * which meets the limits of thermal, user and _TPC.
+                */
+               p_limit = &pr->limit;
+               if (p_limit->thermal.tx > target_state)
+                       target_state = p_limit->thermal.tx;
+               if (p_limit->user.tx > target_state)
+                       target_state = p_limit->user.tx;
+               if (pr->throttling_platform_limit > target_state)
+                       target_state = pr->throttling_platform_limit;
+               if (target_state >= p_throttling->state_count) {
+                       printk(KERN_WARNING
+                               "Exceed the limit of T-state \n");
+                       target_state = p_throttling->state_count - 1;
+               }
+               p_tstate->target_state = target_state;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PreChange Event:"
+                               "target T-state of CPU %d is T%d\n",
+                               cpu, target_state));
+               break;
+       case THROTTLING_POSTCHANGE:
+               /*
+                * Postchange event is only used to update the
+                * T-state flag of acpi_processor_throttling.
+                */
+               p_throttling->state = target_state;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PostChange Event:"
+                               "CPU %d is switched to T%d\n",
+                               cpu, target_state));
+               break;
+       default:
+               printk(KERN_WARNING
+                       "Unsupported Throttling notifier event\n");
+               break;
+       }
+
+       return 0;
+}
+
 /*
  * _TPC - Throttling Present Capabilities
  */
@@ -293,6 +513,10 @@ static int acpi_processor_get_tsd(struct acpi_processor *pr)
        struct acpi_buffer state = { 0, NULL };
        union acpi_object *tsd = NULL;
        struct acpi_tsd_package *pdomain;
+       struct acpi_processor_throttling *pthrottling;
+
+       pthrottling = &pr->throttling;
+       pthrottling->tsd_valid_flag = 0;
 
        status = acpi_evaluate_object(pr->handle, "_TSD", NULL, &buffer);
        if (ACPI_FAILURE(status)) {
@@ -340,6 +564,22 @@ static int acpi_processor_get_tsd(struct acpi_processor *pr)
                goto end;
        }
 
+       pthrottling = &pr->throttling;
+       pthrottling->tsd_valid_flag = 1;
+       pthrottling->shared_type = pdomain->coord_type;
+       cpu_set(pr->id, pthrottling->shared_cpu_map);
+       /*
+        * If the coordination type is not defined in ACPI spec,
+        * the tsd_valid_flag will be clear and coordination type
+        * will be forecd as DOMAIN_COORD_TYPE_SW_ALL.
+        */
+       if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
+               pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
+               pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
+               pthrottling->tsd_valid_flag = 0;
+               pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL;
+       }
+
       end:
        kfree(buffer.pointer);
        return result;
@@ -589,6 +829,11 @@ static int acpi_processor_get_throttling(struct acpi_processor *pr)
        cpumask_t saved_mask;
        int ret;
 
+       if (!pr)
+               return -EINVAL;
+
+       if (!pr->flags.throttling)
+               return -ENODEV;
        /*
         * Migrate task to the cpu pointed by pr.
         */
@@ -742,13 +987,92 @@ static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr,
 int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
 {
        cpumask_t saved_mask;
-       int ret;
+       int ret = 0;
+       unsigned int i;
+       struct acpi_processor *match_pr;
+       struct acpi_processor_throttling *p_throttling;
+       struct throttling_tstate t_state;
+       cpumask_t online_throttling_cpus;
+
+       if (!pr)
+               return -EINVAL;
+
+       if (!pr->flags.throttling)
+               return -ENODEV;
+
+       if ((state < 0) || (state > (pr->throttling.state_count - 1)))
+               return -EINVAL;
+
+       saved_mask = current->cpus_allowed;
+       t_state.target_state = state;
+       p_throttling = &(pr->throttling);
+       cpus_and(online_throttling_cpus, cpu_online_map,
+                       p_throttling->shared_cpu_map);
        /*
-        * Migrate task to the cpu pointed by pr.
+        * The throttling notifier will be called for every
+        * affected cpu in order to get one proper T-state.
+        * The notifier event is THROTTLING_PRECHANGE.
         */
-       saved_mask = current->cpus_allowed;
-       set_cpus_allowed(current, cpumask_of_cpu(pr->id));
-       ret = pr->throttling.acpi_processor_set_throttling(pr, state);
+       for_each_cpu_mask(i, online_throttling_cpus) {
+               t_state.cpu = i;
+               acpi_processor_throttling_notifier(THROTTLING_PRECHANGE,
+                                                       &t_state);
+       }
+       /*
+        * The function of acpi_processor_set_throttling will be called
+        * to switch T-state. If the coordination type is SW_ALL or HW_ALL,
+        * it is necessary to call it for every affected cpu. Otherwise
+        * it can be called only for the cpu pointed by pr.
+        */
+       if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) {
+               set_cpus_allowed(current, cpumask_of_cpu(pr->id));
+               ret = p_throttling->acpi_processor_set_throttling(pr,
+                                               t_state.target_state);
+       } else {
+               /*
+                * When the T-state coordination is SW_ALL or HW_ALL,
+                * it is necessary to set T-state for every affected
+                * cpus.
+                */
+               for_each_cpu_mask(i, online_throttling_cpus) {
+                       match_pr = processors[i];
+                       /*
+                        * If the pointer is invalid, we will report the
+                        * error message and continue.
+                        */
+                       if (!match_pr) {
+                               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                       "Invalid Pointer for CPU %d\n", i));
+                               continue;
+                       }
+                       /*
+                        * If the throttling control is unsupported on CPU i,
+                        * we will report the error message and continue.
+                        */
+                       if (!match_pr->flags.throttling) {
+                               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                       "Throttling Controll is unsupported "
+                                       "on CPU %d\n", i));
+                               continue;
+                       }
+                       t_state.cpu = i;
+                       set_cpus_allowed(current, cpumask_of_cpu(i));
+                       ret = match_pr->throttling.
+                               acpi_processor_set_throttling(
+                               match_pr, t_state.target_state);
+               }
+       }
+       /*
+        * After the set_throttling is called, the
+        * throttling notifier is called for every
+        * affected cpu to update the T-states.
+        * The notifier event is THROTTLING_POSTCHANGE
+        */
+       for_each_cpu_mask(i, online_throttling_cpus) {
+               t_state.cpu = i;
+               acpi_processor_throttling_notifier(THROTTLING_POSTCHANGE,
+                                                       &t_state);
+       }
        /* restore the previous state */
        set_cpus_allowed(current, saved_mask);
        return ret;
@@ -757,6 +1081,7 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
 int acpi_processor_get_throttling_info(struct acpi_processor *pr)
 {
        int result = 0;
+       struct acpi_processor_throttling *pthrottling;
 
        ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                          "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n",
@@ -788,7 +1113,16 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr)
                    &acpi_processor_set_throttling_ptc;
        }
 
-       acpi_processor_get_tsd(pr);
+       /*
+        * If TSD package for one CPU can't be parsed successfully, it means
+        * that this CPU will have no coordination with other CPUs.
+        */
+       if (acpi_processor_get_tsd(pr)) {
+               pthrottling = &pr->throttling;
+               pthrottling->tsd_valid_flag = 0;
+               cpu_set(pr->id, pthrottling->shared_cpu_map);
+               pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL;
+       }
 
        /*
         * PIIX4 Errata: We don't support throttling on the original PIIX4.
index f136c7d..1194105 100644 (file)
@@ -888,7 +888,7 @@ static void acpi_charger_remove(struct acpi_sbs *sbs)
 #endif
 }
 
-void acpi_sbs_callback(void *context)
+static void acpi_sbs_callback(void *context)
 {
        int id;
        struct acpi_sbs *sbs = context;
index fd40b6a..ae9a904 100644 (file)
@@ -111,8 +111,8 @@ static int wait_transaction_complete(struct acpi_smb_hc *hc, int timeout)
                return -ETIME;
 }
 
-int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol, u8 address,
-                   u8 command, u8 *data, u8 length)
+static int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol,
+                                 u8 address, u8 command, u8 *data, u8 length)
 {
        int ret = -EFAULT, i;
        u8 temp, sz = 0;
index d9d531c..3fac011 100644 (file)
@@ -59,7 +59,7 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
                        count = snprintf(&modalias[len], size, "%s:",
                                         cid_list->id[i].value);
                        if (count < 0 || count >= size) {
-                               printk(KERN_ERR "acpi: %s cid[%i] exceeds event buffer size",
+                               printk(KERN_ERR PREFIX "%s cid[%i] exceeds event buffer size",
                                       acpi_dev->pnp.device_name, i);
                                break;
                        }
@@ -453,7 +453,7 @@ static int acpi_device_register(struct acpi_device *device,
        device->dev.release = &acpi_device_release;
        result = device_add(&device->dev);
        if(result) {
-               printk("Error adding device %s", device->dev.bus_id);
+               printk(KERN_ERR PREFIX "Error adding device %s", device->dev.bus_id);
                goto end;
        }
 
@@ -941,6 +941,15 @@ static int acpi_bay_match(struct acpi_device *device){
        return -ENODEV;
 }
 
+/*
+ * acpi_dock_match - see if a device has a _DCK method
+ */
+static int acpi_dock_match(struct acpi_device *device)
+{
+       acpi_handle tmp;
+       return acpi_get_handle(device->handle, "_DCK", &tmp);
+}
+
 static void acpi_device_set_id(struct acpi_device *device,
                               struct acpi_device *parent, acpi_handle handle,
                               int type)
@@ -950,13 +959,14 @@ static void acpi_device_set_id(struct acpi_device *device,
        char *hid = NULL;
        char *uid = NULL;
        struct acpi_compatible_id_list *cid_list = NULL;
+       const char *cid_add = NULL;
        acpi_status status;
 
        switch (type) {
        case ACPI_BUS_TYPE_DEVICE:
                status = acpi_get_object_info(handle, &buffer);
                if (ACPI_FAILURE(status)) {
-                       printk("%s: Error reading device info\n", __FUNCTION__);
+                       printk(KERN_ERR PREFIX "%s: Error reading device info\n", __FUNCTION__);
                        return;
                }
 
@@ -972,15 +982,18 @@ static void acpi_device_set_id(struct acpi_device *device,
                        device->flags.bus_address = 1;
                }
 
-               if(!(info->valid & (ACPI_VALID_HID | ACPI_VALID_CID))){
-                       status = acpi_video_bus_match(device);
-                       if(ACPI_SUCCESS(status))
-                               hid = ACPI_VIDEO_HID;
+               /* If we have a video/bay/dock device, add our selfdefined
+                  HID to the CID list. Like that the video/bay/dock drivers
+                  will get autoloaded and the device might still match
+                  against another driver.
+               */
+               if (ACPI_SUCCESS(acpi_video_bus_match(device)))
+                       cid_add = ACPI_VIDEO_HID;
+               else if (ACPI_SUCCESS(acpi_bay_match(device)))
+                       cid_add = ACPI_BAY_HID;
+               else if (ACPI_SUCCESS(acpi_dock_match(device)))
+                       cid_add = ACPI_DOCK_HID;
 
-                       status = acpi_bay_match(device);
-                       if (ACPI_SUCCESS(status))
-                               hid = ACPI_BAY_HID;
-               }
                break;
        case ACPI_BUS_TYPE_POWER:
                hid = ACPI_POWER_HID;
@@ -1021,12 +1034,45 @@ static void acpi_device_set_id(struct acpi_device *device,
                strcpy(device->pnp.unique_id, uid);
                device->flags.unique_id = 1;
        }
-       if (cid_list) {
-               device->pnp.cid_list = kmalloc(cid_list->size, GFP_KERNEL);
-               if (device->pnp.cid_list)
-                       memcpy(device->pnp.cid_list, cid_list, cid_list->size);
-               else
-                       printk(KERN_ERR "Memory allocation error\n");
+       if (cid_list || cid_add) {
+               struct  acpi_compatible_id_list *list;
+               int size = 0;
+               int count = 0;
+
+               if (cid_list) {
+                       size = cid_list->size;
+               } else if (cid_add) {
+                       size = sizeof(struct acpi_compatible_id_list);
+                       cid_list = ACPI_ALLOCATE_ZEROED((acpi_size) size);
+                       if (!cid_list) {
+                               printk(KERN_ERR "Memory allocation error\n");
+                               kfree(buffer.pointer);
+                               return;
+                       } else {
+                               cid_list->count = 0;
+                               cid_list->size = size;
+                       }
+               }
+               if (cid_add)
+                       size += sizeof(struct acpi_compatible_id);
+               list = kmalloc(size, GFP_KERNEL);
+
+               if (list) {
+                       if (cid_list) {
+                               memcpy(list, cid_list, cid_list->size);
+                               count = cid_list->count;
+                       }
+                       if (cid_add) {
+                               strncpy(list->id[count].value, cid_add,
+                                       ACPI_MAX_CID_LENGTH);
+                               count++;
+                               device->flags.compatible_ids = 1;
+                       }
+                       list->size = size;
+                       list->count = count;
+                       device->pnp.cid_list = list;
+               } else
+                       printk(KERN_ERR PREFIX "Memory allocation error\n");
        }
 
        kfree(buffer.pointer);
@@ -1050,7 +1096,7 @@ static int acpi_device_set_context(struct acpi_device *device, int type)
                                          acpi_bus_data_handler, device);
 
                if (ACPI_FAILURE(status)) {
-                       printk("Error attaching device data\n");
+                       printk(KERN_ERR PREFIX "Error attaching device data\n");
                        result = -ENODEV;
                }
        }
@@ -1081,6 +1127,20 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice)
 }
 
 static int
+acpi_is_child_device(struct acpi_device *device,
+                       int (*matcher)(struct acpi_device *))
+{
+       int result = -ENODEV;
+
+       do {
+               if (ACPI_SUCCESS(matcher(device)))
+                       return AE_OK;
+       } while ((device = device->parent));
+
+       return result;
+}
+
+static int
 acpi_add_single_object(struct acpi_device **child,
                       struct acpi_device *parent, acpi_handle handle, int type,
                        struct acpi_bus_ops *ops)
@@ -1131,10 +1191,20 @@ acpi_add_single_object(struct acpi_device **child,
        case ACPI_BUS_TYPE_PROCESSOR:
        case ACPI_BUS_TYPE_DEVICE:
                result = acpi_bus_get_status(device);
-               if (ACPI_FAILURE(result) || !device->status.present) {
-                       result = -ENOENT;
+               if (ACPI_FAILURE(result)) {
+                       result = -ENODEV;
                        goto end;
                }
+               if (!device->status.present) {
+                       /* Bay and dock should be handled even if absent */
+                       if (!ACPI_SUCCESS(
+                            acpi_is_child_device(device, acpi_bay_match)) &&
+                           !ACPI_SUCCESS(
+                            acpi_is_child_device(device, acpi_dock_match))) {
+                                       result = -ENODEV;
+                                       goto end;
+                       }
+               }
                break;
        default:
                STRUCT_TO_INT(device->status) =
index 485de13..293a1cb 100644 (file)
@@ -170,7 +170,7 @@ static int acpi_pm_enter(suspend_state_t pm_state)
        /* Reprogram control registers and execute _BFS */
        acpi_leave_sleep_state_prep(acpi_state);
 
-       /* ACPI 3.0 specs (P62) says that it's the responsabilty
+       /* ACPI 3.0 specs (P62) says that it's the responsibility
         * of the OSPM to clear the status bit [ implying that the
         * POWER_BUTTON event should not reach userspace ]
         */
@@ -472,11 +472,20 @@ int acpi_pm_device_sleep_state(struct device *dev, int wake, int *d_min_p)
        if (acpi_target_sleep_state == ACPI_STATE_S0 ||
            (wake && adev->wakeup.state.enabled &&
             adev->wakeup.sleep_state <= acpi_target_sleep_state)) {
+               acpi_status status;
+
                acpi_method[3] = 'W';
-               acpi_evaluate_integer(handle, acpi_method, NULL, &d_max);
-               /* Sanity check */
-               if (d_max < d_min)
+               status = acpi_evaluate_integer(handle, acpi_method, NULL,
+                                               &d_max);
+               if (ACPI_FAILURE(status)) {
+                       d_max = d_min;
+               } else if (d_max < d_min) {
+                       /* Warn the user of the broken DSDT */
+                       printk(KERN_WARNING "ACPI: Wrong value from %s\n",
+                               acpi_method);
+                       /* Sanitize it */
                        d_min = d_max;
+               }
        }
 
        if (d_min_p)
index 1538355..f8df521 100644 (file)
@@ -178,6 +178,9 @@ static int get_date_field(char **p, u32 * value)
         * Try to find delimeter, only to insert null.  The end of the
         * string won't have one, but is still valid.
         */
+       if (*p == NULL)
+               return result;
+
        next = strpbrk(*p, "- :");
        if (next)
                *next++ = '\0';
@@ -190,6 +193,8 @@ static int get_date_field(char **p, u32 * value)
 
        if (next)
                *p = next;
+       else
+               *p = NULL;
 
        return result;
 }
@@ -251,27 +256,6 @@ acpi_system_write_alarm(struct file *file,
        if ((result = get_date_field(&p, &sec)))
                goto end;
 
-       if (sec > 59) {
-               min += 1;
-               sec -= 60;
-       }
-       if (min > 59) {
-               hr += 1;
-               min -= 60;
-       }
-       if (hr > 23) {
-               day += 1;
-               hr -= 24;
-       }
-       if (day > 31) {
-               mo += 1;
-               day -= 31;
-       }
-       if (mo > 12) {
-               yr += 1;
-               mo -= 12;
-       }
-
        spin_lock_irq(&rtc_lock);
 
        rtc_control = CMOS_READ(RTC_CONTROL);
@@ -288,24 +272,24 @@ acpi_system_write_alarm(struct file *file,
        spin_unlock_irq(&rtc_lock);
 
        if (sec > 59) {
-               min++;
-               sec -= 60;
+               min += sec/60;
+               sec = sec%60;
        }
        if (min > 59) {
-               hr++;
-               min -= 60;
+               hr += min/60;
+               min = min%60;
        }
        if (hr > 23) {
-               day++;
-               hr -= 24;
+               day += hr/24;
+               hr = hr%24;
        }
        if (day > 31) {
-               mo++;
-               day -= 31;
+               mo += day/32;
+               day = day%32;
        }
        if (mo > 12) {
-               yr++;
-               mo -= 12;
+               yr += mo/13;
+               mo = mo%13;
        }
 
        spin_lock_irq(&rtc_lock);
index 5ffe0ea..55cf4c0 100644 (file)
@@ -40,6 +40,8 @@ ACPI_MODULE_NAME("system");
 #define ACPI_SYSTEM_CLASS              "system"
 #define ACPI_SYSTEM_DEVICE_NAME                "System"
 
+u32 acpi_irq_handled;
+
 /*
  * Make ACPICA version work as module param
  */
@@ -166,6 +168,212 @@ static int acpi_system_sysfs_init(void)
        return 0;
 }
 
+/*
+ * Detailed ACPI IRQ counters in /sys/firmware/acpi/interrupts/
+ * See Documentation/ABI/testing/sysfs-firmware-acpi
+ */
+
+#define COUNT_GPE 0
+#define COUNT_SCI 1    /* acpi_irq_handled */
+#define COUNT_ERROR 2  /* other */
+#define NUM_COUNTERS_EXTRA 3
+
+static u32 *all_counters;
+static u32 num_gpes;
+static u32 num_counters;
+static struct attribute **all_attrs;
+static u32 acpi_gpe_count;
+
+static struct attribute_group interrupt_stats_attr_group = {
+       .name = "interrupts",
+};
+static struct kobj_attribute *counter_attrs;
+
+static int count_num_gpes(void)
+{
+       int count = 0;
+       struct acpi_gpe_xrupt_info *gpe_xrupt_info;
+       struct acpi_gpe_block_info *gpe_block;
+       acpi_cpu_flags flags;
+
+       flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+       gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head;
+       while (gpe_xrupt_info) {
+               gpe_block = gpe_xrupt_info->gpe_block_list_head;
+               while (gpe_block) {
+                       count += gpe_block->register_count *
+                           ACPI_GPE_REGISTER_WIDTH;
+                       gpe_block = gpe_block->next;
+               }
+               gpe_xrupt_info = gpe_xrupt_info->next;
+       }
+       acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+
+       return count;
+}
+
+static void delete_gpe_attr_array(void)
+{
+       u32 *tmp = all_counters;
+
+       all_counters = NULL;
+       kfree(tmp);
+
+       if (counter_attrs) {
+               int i;
+
+               for (i = 0; i < num_gpes; i++)
+                       kfree(counter_attrs[i].attr.name);
+
+               kfree(counter_attrs);
+       }
+       kfree(all_attrs);
+
+       return;
+}
+
+void acpi_os_gpe_count(u32 gpe_number)
+{
+       acpi_gpe_count++;
+
+       if (!all_counters)
+               return;
+
+       if (gpe_number < num_gpes)
+               all_counters[gpe_number]++;
+       else
+               all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]++;
+
+       return;
+}
+
+void acpi_os_fixed_event_count(u32 event_number)
+{
+       if (!all_counters)
+               return;
+
+       if (event_number < ACPI_NUM_FIXED_EVENTS)
+               all_counters[num_gpes + event_number]++;
+       else
+               all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]++;
+
+       return;
+}
+
+static ssize_t counter_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI] =
+               acpi_irq_handled;
+       all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE] =
+               acpi_gpe_count;
+
+       return sprintf(buf, "%d\n", all_counters[attr - counter_attrs]);
+}
+
+/*
+ * counter_set() sets the specified counter.
+ * setting the total "sci" file to any value clears all counters.
+ */
+static ssize_t counter_set(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t size)
+{
+       int index = attr - counter_attrs;
+
+       if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) {
+               int i;
+               for (i = 0; i < num_counters; ++i)
+                       all_counters[i] = 0;
+               acpi_gpe_count = 0;
+               acpi_irq_handled = 0;
+
+       } else
+               all_counters[index] = strtoul(buf, NULL, 0);
+
+       return size;
+}
+
+void acpi_irq_stats_init(void)
+{
+       int i;
+
+       if (all_counters)
+               return;
+
+       num_gpes = count_num_gpes();
+       num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA;
+
+       all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1),
+                       GFP_KERNEL);
+       if (all_attrs == NULL)
+               return;
+
+       all_counters = kzalloc(sizeof(u32) * (num_counters), GFP_KERNEL);
+       if (all_counters == NULL)
+               goto fail;
+
+       counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters),
+                       GFP_KERNEL);
+       if (counter_attrs == NULL)
+               goto fail;
+
+       for (i = 0; i < num_counters; ++i) {
+               char buffer[10];
+               char *name;
+
+               if (i < num_gpes)
+                       sprintf(buffer, "gpe%02X", i);
+               else if (i == num_gpes + ACPI_EVENT_PMTIMER)
+                       sprintf(buffer, "ff_pmtimer");
+               else if (i == num_gpes + ACPI_EVENT_GLOBAL)
+                       sprintf(buffer, "ff_gbl_lock");
+               else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON)
+                       sprintf(buffer, "ff_pwr_btn");
+               else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON)
+                       sprintf(buffer, "ff_slp_btn");
+               else if (i == num_gpes + ACPI_EVENT_RTC)
+                       sprintf(buffer, "ff_rt_clk");
+               else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE)
+                       sprintf(buffer, "gpe_all");
+               else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI)
+                       sprintf(buffer, "sci");
+               else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR)
+                       sprintf(buffer, "error");
+               else
+                       sprintf(buffer, "bug%02X", i);
+
+               name = kzalloc(strlen(buffer) + 1, GFP_KERNEL);
+               if (name == NULL)
+                       goto fail;
+               strncpy(name, buffer, strlen(buffer) + 1);
+
+               counter_attrs[i].attr.name = name;
+               counter_attrs[i].attr.mode = 0644;
+               counter_attrs[i].show = counter_show;
+               counter_attrs[i].store = counter_set;
+
+               all_attrs[i] = &counter_attrs[i].attr;
+       }
+
+       interrupt_stats_attr_group.attrs = all_attrs;
+       if (!sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group))
+               return;
+
+fail:
+       delete_gpe_attr_array();
+       return;
+}
+
+static void __exit interrupt_stats_exit(void)
+{
+       sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group);
+
+       delete_gpe_attr_array();
+
+       return;
+}
+
 /* --------------------------------------------------------------------------
                               FS Interface (/proc)
    -------------------------------------------------------------------------- */
index 0a7d7af..7385efa 100644 (file)
@@ -2,6 +2,6 @@
 # Makefile for all Linux ACPI interpreter subdirectories
 #
 
-obj-y := tbxface.o tbinstal.o  tbutils.o tbfind.o tbfadt.o
+obj-y := tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o
 
 EXTRA_CFLAGS += $(ACPI_CFLAGS)
index cf8fa51..9ecb4b6 100644 (file)
@@ -100,7 +100,7 @@ static acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp)
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_tb_find_rsdp
+ * FUNCTION:    acpi_find_root_pointer
  *
  * PARAMETERS:  table_address           - Where the table pointer is returned
  *
@@ -219,8 +219,6 @@ acpi_status acpi_find_root_pointer(acpi_native_uint * table_address)
        return_ACPI_STATUS(AE_NOT_FOUND);
 }
 
-ACPI_EXPORT_SYMBOL(acpi_find_root_pointer)
-
 /*******************************************************************************
  *
  * FUNCTION:    acpi_tb_scan_memory_for_rsdp
index 5f79b44..8d4b79b 100644 (file)
@@ -43,7 +43,7 @@
 #include <linux/seq_file.h>
 #include <linux/reboot.h>
 #include <asm/uaccess.h>
-
+#include <linux/thermal.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 
@@ -65,9 +65,6 @@
 #define ACPI_THERMAL_MAX_ACTIVE        10
 #define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
 
-#define KELVIN_TO_CELSIUS(t)    (long)(((long)t-2732>=0) ? ((long)t-2732+5)/10 : ((long)t-2732-5)/10)
-#define CELSIUS_TO_KELVIN(t)   ((t+273)*10)
-
 #define _COMPONENT             ACPI_THERMAL_COMPONENT
 ACPI_MODULE_NAME("thermal");
 
@@ -195,6 +192,8 @@ struct acpi_thermal {
        struct acpi_thermal_trips trips;
        struct acpi_handle_list devices;
        struct timer_list timer;
+       struct thermal_zone_device *thermal_zone;
+       int tz_enabled;
        struct mutex lock;
 };
 
@@ -321,178 +320,226 @@ static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode)
        return 0;
 }
 
-static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
-{
-       acpi_status status = AE_OK;
-       int i = 0;
+#define ACPI_TRIPS_CRITICAL    0x01
+#define ACPI_TRIPS_HOT         0x02
+#define ACPI_TRIPS_PASSIVE     0x04
+#define ACPI_TRIPS_ACTIVE      0x08
+#define ACPI_TRIPS_DEVICES     0x10
 
+#define ACPI_TRIPS_REFRESH_THRESHOLDS  (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
+#define ACPI_TRIPS_REFRESH_DEVICES     ACPI_TRIPS_DEVICES
 
-       if (!tz)
-               return -EINVAL;
+#define ACPI_TRIPS_INIT      (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT |   \
+                             ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE |  \
+                             ACPI_TRIPS_DEVICES)
 
-       /* Critical Shutdown (required) */
-
-       status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL,
-                                      &tz->trips.critical.temperature);
-       if (ACPI_FAILURE(status)) {
-               tz->trips.critical.flags.valid = 0;
-               ACPI_EXCEPTION((AE_INFO, status, "No critical threshold"));
-               return -ENODEV;
-       } else {
-               tz->trips.critical.flags.valid = 1;
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Found critical threshold [%lu]\n",
-                                 tz->trips.critical.temperature));
-       }
+/*
+ * This exception is thrown out in two cases:
+ * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
+ *   when re-evaluating the AML code.
+ * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
+ *   We need to re-bind the cooling devices of a thermal zone when this occurs.
+ */
+#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str)       \
+do {   \
+       if (flags != ACPI_TRIPS_INIT)   \
+               ACPI_EXCEPTION((AE_INFO, AE_ERROR,      \
+               "ACPI thermal trip point %s changed\n"  \
+               "Please send acpidump to linux-acpi@vger.kernel.org\n", str)); \
+} while (0)
+
+static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
+{
+       acpi_status status = AE_OK;
+       struct acpi_handle_list devices;
+       int valid = 0;
+       int i;
 
-       if (tz->trips.critical.flags.valid == 1) {
-               if (crt == -1) {
+       /* Critical Shutdown (required) */
+       if (flag & ACPI_TRIPS_CRITICAL) {
+               status = acpi_evaluate_integer(tz->device->handle,
+                               "_CRT", NULL, &tz->trips.critical.temperature);
+               if (ACPI_FAILURE(status)) {
                        tz->trips.critical.flags.valid = 0;
-               } else if (crt > 0) {
-                       unsigned long crt_k = CELSIUS_TO_KELVIN(crt);
-
-                       /*
-                        * Allow override to lower critical threshold
-                        */
-                       if (crt_k < tz->trips.critical.temperature)
-                               tz->trips.critical.temperature = crt_k;
+                       ACPI_EXCEPTION((AE_INFO, status,
+                                       "No critical threshold"));
+                       return -ENODEV;
+               } else {
+                       tz->trips.critical.flags.valid = 1;
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                       "Found critical threshold [%lu]\n",
+                                       tz->trips.critical.temperature));
+               }
+               if (tz->trips.critical.flags.valid == 1) {
+                       if (crt == -1) {
+                               tz->trips.critical.flags.valid = 0;
+                       } else if (crt > 0) {
+                               unsigned long crt_k = CELSIUS_TO_KELVIN(crt);
+                               /*
+                                * Allow override to lower critical threshold
+                                */
+                               if (crt_k < tz->trips.critical.temperature)
+                                       tz->trips.critical.temperature = crt_k;
+                       }
                }
        }
 
        /* Critical Sleep (optional) */
-
-       status =
-           acpi_evaluate_integer(tz->device->handle, "_HOT", NULL,
-                                 &tz->trips.hot.temperature);
-       if (ACPI_FAILURE(status)) {
-               tz->trips.hot.flags.valid = 0;
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n"));
-       } else {
-               tz->trips.hot.flags.valid = 1;
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n",
-                                 tz->trips.hot.temperature));
-       }
-
-       /* Passive: Processors (optional) */
-
-       if (psv == -1) {
-               status = AE_SUPPORT;
-       } else if (psv > 0) {
-               tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv);
-               status = AE_OK;
-       } else {
+       if (flag & ACPI_TRIPS_HOT) {
                status = acpi_evaluate_integer(tz->device->handle,
-                       "_PSV", NULL, &tz->trips.passive.temperature);
+                               "_HOT", NULL, &tz->trips.hot.temperature);
+               if (ACPI_FAILURE(status)) {
+                       tz->trips.hot.flags.valid = 0;
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                       "No hot threshold\n"));
+               } else {
+                       tz->trips.hot.flags.valid = 1;
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                       "Found hot threshold [%lu]\n",
+                                       tz->trips.critical.temperature));
+               }
        }
 
-       if (ACPI_FAILURE(status)) {
-               tz->trips.passive.flags.valid = 0;
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n"));
-       } else {
-               tz->trips.passive.flags.valid = 1;
-
-               status =
-                   acpi_evaluate_integer(tz->device->handle, "_TC1", NULL,
-                                         &tz->trips.passive.tc1);
-               if (ACPI_FAILURE(status))
-                       tz->trips.passive.flags.valid = 0;
-
-               status =
-                   acpi_evaluate_integer(tz->device->handle, "_TC2", NULL,
-                                         &tz->trips.passive.tc2);
-               if (ACPI_FAILURE(status))
-                       tz->trips.passive.flags.valid = 0;
+       /* Passive (optional) */
+       if (flag & ACPI_TRIPS_PASSIVE) {
+               valid = tz->trips.passive.flags.valid;
+               if (psv == -1) {
+                       status = AE_SUPPORT;
+               } else if (psv > 0) {
+                       tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv);
+                       status = AE_OK;
+               } else {
+                       status = acpi_evaluate_integer(tz->device->handle,
+                               "_PSV", NULL, &tz->trips.passive.temperature);
+               }
 
-               status =
-                   acpi_evaluate_integer(tz->device->handle, "_TSP", NULL,
-                                         &tz->trips.passive.tsp);
                if (ACPI_FAILURE(status))
                        tz->trips.passive.flags.valid = 0;
-
-               status =
-                   acpi_evaluate_reference(tz->device->handle, "_PSL", NULL,
-                                           &tz->trips.passive.devices);
+               else {
+                       tz->trips.passive.flags.valid = 1;
+                       if (flag == ACPI_TRIPS_INIT) {
+                               status = acpi_evaluate_integer(
+                                               tz->device->handle, "_TC1",
+                                               NULL, &tz->trips.passive.tc1);
+                               if (ACPI_FAILURE(status))
+                                       tz->trips.passive.flags.valid = 0;
+                               status = acpi_evaluate_integer(
+                                               tz->device->handle, "_TC2",
+                                               NULL, &tz->trips.passive.tc2);
+                               if (ACPI_FAILURE(status))
+                                       tz->trips.passive.flags.valid = 0;
+                               status = acpi_evaluate_integer(
+                                               tz->device->handle, "_TSP",
+                                               NULL, &tz->trips.passive.tsp);
+                               if (ACPI_FAILURE(status))
+                                       tz->trips.passive.flags.valid = 0;
+                       }
+               }
+       }
+       if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) {
+               memset(&devices, 0, sizeof(struct acpi_handle_list));
+               status = acpi_evaluate_reference(tz->device->handle, "_PSL",
+                                                       NULL, &devices);
                if (ACPI_FAILURE(status))
                        tz->trips.passive.flags.valid = 0;
-
-               if (!tz->trips.passive.flags.valid)
-                       printk(KERN_WARNING PREFIX "Invalid passive threshold\n");
                else
-                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                         "Found passive threshold [%lu]\n",
-                                         tz->trips.passive.temperature));
-       }
+                       tz->trips.passive.flags.valid = 1;
 
-       /* Active: Fans, etc. (optional) */
+               if (memcmp(&tz->trips.passive.devices, &devices,
+                               sizeof(struct acpi_handle_list))) {
+                       memcpy(&tz->trips.passive.devices, &devices,
+                               sizeof(struct acpi_handle_list));
+                       ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
+               }
+       }
+       if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) {
+               if (valid != tz->trips.passive.flags.valid)
+                               ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
+       }
 
+       /* Active (optional) */
        for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
-
                char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
+               valid = tz->trips.active[i].flags.valid;
 
                if (act == -1)
-                       break;  /* disable all active trip points */
-
-               status = acpi_evaluate_integer(tz->device->handle,
-                       name, NULL, &tz->trips.active[i].temperature);
-
-               if (ACPI_FAILURE(status)) {
-                       if (i == 0)     /* no active trip points */
+                       break; /* disable all active trip points */
+
+               if (flag & ACPI_TRIPS_ACTIVE) {
+                       status = acpi_evaluate_integer(tz->device->handle,
+                               name, NULL, &tz->trips.active[i].temperature);
+                       if (ACPI_FAILURE(status)) {
+                               tz->trips.active[i].flags.valid = 0;
+                               if (i == 0)
+                                       break;
+                               if (act <= 0)
+                                       break;
+                               if (i == 1)
+                                       tz->trips.active[0].temperature =
+                                               CELSIUS_TO_KELVIN(act);
+                               else
+                                       /*
+                                        * Don't allow override higher than
+                                        * the next higher trip point
+                                        */
+                                       tz->trips.active[i - 1].temperature =
+                                               (tz->trips.active[i - 2].temperature <
+                                               CELSIUS_TO_KELVIN(act) ?
+                                               tz->trips.active[i - 2].temperature :
+                                               CELSIUS_TO_KELVIN(act));
                                break;
-                       if (act <= 0)   /* no override requested */
-                               break;
-                       if (i == 1) {   /* 1 trip point */
-                               tz->trips.active[0].temperature =
-                                       CELSIUS_TO_KELVIN(act);
-                       } else {        /* multiple trips */
-                               /*
-                                * Don't allow override higher than
-                                * the next higher trip point
-                                */
-                               tz->trips.active[i - 1].temperature =
-                                   (tz->trips.active[i - 2].temperature <
-                                       CELSIUS_TO_KELVIN(act) ?
-                                       tz->trips.active[i - 2].temperature :
-                                       CELSIUS_TO_KELVIN(act));
-                       }
-                       break;
+                       } else
+                               tz->trips.active[i].flags.valid = 1;
                }
 
                name[2] = 'L';
-               status =
-                   acpi_evaluate_reference(tz->device->handle, name, NULL,
-                                           &tz->trips.active[i].devices);
-               if (ACPI_SUCCESS(status)) {
-                       tz->trips.active[i].flags.valid = 1;
-                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                         "Found active threshold [%d]:[%lu]\n",
-                                         i, tz->trips.active[i].temperature));
-               } else
-                       ACPI_EXCEPTION((AE_INFO, status,
-                                       "Invalid active threshold [%d]", i));
+               if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) {
+                       memset(&devices, 0, sizeof(struct acpi_handle_list));
+                       status = acpi_evaluate_reference(tz->device->handle,
+                                               name, NULL, &devices);
+                       if (ACPI_FAILURE(status))
+                               tz->trips.active[i].flags.valid = 0;
+                       else
+                               tz->trips.active[i].flags.valid = 1;
+
+                       if (memcmp(&tz->trips.active[i].devices, &devices,
+                                       sizeof(struct acpi_handle_list))) {
+                               memcpy(&tz->trips.active[i].devices, &devices,
+                                       sizeof(struct acpi_handle_list));
+                               ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
+                       }
+               }
+               if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES))
+                       if (valid != tz->trips.active[i].flags.valid)
+                               ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
+
+               if (!tz->trips.active[i].flags.valid)
+                       break;
+       }
+
+       if (flag & ACPI_TRIPS_DEVICES) {
+               memset(&devices, 0, sizeof(struct acpi_handle_list));
+               status = acpi_evaluate_reference(tz->device->handle, "_TZD",
+                                               NULL, &devices);
+               if (memcmp(&tz->devices, &devices,
+                               sizeof(struct acpi_handle_list))) {
+                       memcpy(&tz->devices, &devices,
+                               sizeof(struct acpi_handle_list));
+                       ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
+               }
        }
 
        return 0;
 }
 
-static int acpi_thermal_get_devices(struct acpi_thermal *tz)
+static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
 {
-       acpi_status status = AE_OK;
-
-
-       if (!tz)
-               return -EINVAL;
-
-       status =
-           acpi_evaluate_reference(tz->device->handle, "_TZD", NULL, &tz->devices);
-       if (ACPI_FAILURE(status))
-               return -ENODEV;
-
-       return 0;
+       return acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
 }
 
 static int acpi_thermal_critical(struct acpi_thermal *tz)
 {
-       if (!tz || !tz->trips.critical.flags.valid || nocrt)
+       if (!tz || !tz->trips.critical.flags.valid)
                return -EINVAL;
 
        if (tz->temperature >= tz->trips.critical.temperature) {
@@ -501,9 +548,6 @@ static int acpi_thermal_critical(struct acpi_thermal *tz)
        } else if (tz->trips.critical.flags.enabled)
                tz->trips.critical.flags.enabled = 0;
 
-       printk(KERN_EMERG
-              "Critical temperature reached (%ld C), shutting down.\n",
-              KELVIN_TO_CELSIUS(tz->temperature));
        acpi_bus_generate_proc_event(tz->device, ACPI_THERMAL_NOTIFY_CRITICAL,
                                tz->trips.critical.flags.enabled);
        acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
@@ -511,14 +555,20 @@ static int acpi_thermal_critical(struct acpi_thermal *tz)
                                          ACPI_THERMAL_NOTIFY_CRITICAL,
                                          tz->trips.critical.flags.enabled);
 
-       orderly_poweroff(true);
+       /* take no action if nocrt is set */
+       if(!nocrt) {
+               printk(KERN_EMERG
+                       "Critical temperature reached (%ld C), shutting down.\n",
+                       KELVIN_TO_CELSIUS(tz->temperature));
+               orderly_poweroff(true);
+       }
 
        return 0;
 }
 
 static int acpi_thermal_hot(struct acpi_thermal *tz)
 {
-       if (!tz || !tz->trips.hot.flags.valid || nocrt)
+       if (!tz || !tz->trips.hot.flags.valid)
                return -EINVAL;
 
        if (tz->temperature >= tz->trips.hot.temperature) {
@@ -534,7 +584,7 @@ static int acpi_thermal_hot(struct acpi_thermal *tz)
                                          ACPI_THERMAL_NOTIFY_HOT,
                                          tz->trips.hot.flags.enabled);
 
-       /* TBD: Call user-mode "sleep(S4)" function */
+       /* TBD: Call user-mode "sleep(S4)" function if nocrt is cleared */
 
        return 0;
 }
@@ -732,6 +782,9 @@ static void acpi_thermal_check(void *data)
        if (result)
                goto unlock;
 
+       if (!tz->tz_enabled)
+               goto unlock;
+
        memset(&tz->state, 0, sizeof(tz->state));
 
        /*
@@ -825,6 +878,290 @@ static void acpi_thermal_check(void *data)
        mutex_unlock(&tz->lock);
 }
 
+/* sys I/F for generic thermal sysfs support */
+static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf)
+{
+       struct acpi_thermal *tz = thermal->devdata;
+
+       if (!tz)
+               return -EINVAL;
+
+       return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(tz->temperature));
+}
+
+static const char enabled[] = "kernel";
+static const char disabled[] = "user";
+static int thermal_get_mode(struct thermal_zone_device *thermal,
+                               char *buf)
+{
+       struct acpi_thermal *tz = thermal->devdata;
+
+       if (!tz)
+               return -EINVAL;
+
+       return sprintf(buf, "%s\n", tz->tz_enabled ?
+                       enabled : disabled);
+}
+
+static int thermal_set_mode(struct thermal_zone_device *thermal,
+                               const char *buf)
+{
+       struct acpi_thermal *tz = thermal->devdata;
+       int enable;
+
+       if (!tz)
+               return -EINVAL;
+
+       /*
+        * enable/disable thermal management from ACPI thermal driver
+        */
+       if (!strncmp(buf, enabled, sizeof enabled - 1))
+               enable = 1;
+       else if (!strncmp(buf, disabled, sizeof disabled - 1))
+               enable = 0;
+       else
+               return -EINVAL;
+
+       if (enable != tz->tz_enabled) {
+               tz->tz_enabled = enable;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "%s ACPI thermal control\n",
+                       tz->tz_enabled ? enabled : disabled));
+               acpi_thermal_check(tz);
+       }
+       return 0;
+}
+
+static int thermal_get_trip_type(struct thermal_zone_device *thermal,
+                                int trip, char *buf)
+{
+       struct acpi_thermal *tz = thermal->devdata;
+       int i;
+
+       if (!tz || trip < 0)
+               return -EINVAL;
+
+       if (tz->trips.critical.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "critical\n");
+               trip--;
+       }
+
+       if (tz->trips.hot.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "hot\n");
+               trip--;
+       }
+
+       if (tz->trips.passive.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "passive\n");
+               trip--;
+       }
+
+       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
+               tz->trips.active[i].flags.valid; i++) {
+               if (!trip)
+                       return sprintf(buf, "active%d\n", i);
+               trip--;
+       }
+
+       return -EINVAL;
+}
+
+static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
+                                int trip, char *buf)
+{
+       struct acpi_thermal *tz = thermal->devdata;
+       int i;
+
+       if (!tz || trip < 0)
+               return -EINVAL;
+
+       if (tz->trips.critical.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
+                               tz->trips.critical.temperature));
+               trip--;
+       }
+
+       if (tz->trips.hot.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
+                                       tz->trips.hot.temperature));
+               trip--;
+       }
+
+       if (tz->trips.passive.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
+                                       tz->trips.passive.temperature));
+               trip--;
+       }
+
+       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
+               tz->trips.active[i].flags.valid; i++) {
+               if (!trip)
+                       return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
+                                       tz->trips.active[i].temperature));
+               trip--;
+       }
+
+       return -EINVAL;
+}
+
+typedef int (*cb)(struct thermal_zone_device *, int,
+                 struct thermal_cooling_device *);
+static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
+                                       struct thermal_cooling_device *cdev,
+                                       cb action)
+{
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_thermal *tz = thermal->devdata;
+       struct acpi_device *dev;
+       acpi_status status;
+       acpi_handle handle;
+       int i;
+       int j;
+       int trip = -1;
+       int result = 0;
+
+       if (tz->trips.critical.flags.valid)
+               trip++;
+
+       if (tz->trips.hot.flags.valid)
+               trip++;
+
+       if (tz->trips.passive.flags.valid) {
+               trip++;
+               for (i = 0; i < tz->trips.passive.devices.count;
+                   i++) {
+                       handle = tz->trips.passive.devices.handles[i];
+                       status = acpi_bus_get_device(handle, &dev);
+                       if (ACPI_SUCCESS(status) && (dev == device)) {
+                               result = action(thermal, trip, cdev);
+                               if (result)
+                                       goto failed;
+                       }
+               }
+       }
+
+       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
+               if (!tz->trips.active[i].flags.valid)
+                       break;
+               trip++;
+               for (j = 0;
+                   j < tz->trips.active[i].devices.count;
+                   j++) {
+                       handle = tz->trips.active[i].devices.handles[j];
+                       status = acpi_bus_get_device(handle, &dev);
+                       if (ACPI_SUCCESS(status) && (dev == device)) {
+                               result = action(thermal, trip, cdev);
+                               if (result)
+                                       goto failed;
+                       }
+               }
+       }
+
+       for (i = 0; i < tz->devices.count; i++) {
+               handle = tz->devices.handles[i];
+               status = acpi_bus_get_device(handle, &dev);
+               if (ACPI_SUCCESS(status) && (dev == device)) {
+                       result = action(thermal, -1, cdev);
+                       if (result)
+                               goto failed;
+               }
+       }
+
+failed:
+       return result;
+}
+
+static int
+acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,
+                                       struct thermal_cooling_device *cdev)
+{
+       return acpi_thermal_cooling_device_cb(thermal, cdev,
+                               thermal_zone_bind_cooling_device);
+}
+
+static int
+acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
+                                       struct thermal_cooling_device *cdev)
+{
+       return acpi_thermal_cooling_device_cb(thermal, cdev,
+                               thermal_zone_unbind_cooling_device);
+}
+
+static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
+       .bind = acpi_thermal_bind_cooling_device,
+       .unbind = acpi_thermal_unbind_cooling_device,
+       .get_temp = thermal_get_temp,
+       .get_mode = thermal_get_mode,
+       .set_mode = thermal_set_mode,
+       .get_trip_type = thermal_get_trip_type,
+       .get_trip_temp = thermal_get_trip_temp,
+};
+
+static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
+{
+       int trips = 0;
+       int result;
+       acpi_status status;
+       int i;
+
+       if (tz->trips.critical.flags.valid)
+               trips++;
+
+       if (tz->trips.hot.flags.valid)
+               trips++;
+
+       if (tz->trips.passive.flags.valid)
+               trips++;
+
+       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
+                       tz->trips.active[i].flags.valid; i++, trips++);
+       tz->thermal_zone = thermal_zone_device_register("ACPI thermal zone",
+                                       trips, tz, &acpi_thermal_zone_ops);
+       if (!tz->thermal_zone)
+               return -ENODEV;
+
+       result = sysfs_create_link(&tz->device->dev.kobj,
+                                  &tz->thermal_zone->device.kobj, "thermal_zone");
+       if (result)
+               return result;
+
+       result = sysfs_create_link(&tz->thermal_zone->device.kobj,
+                                  &tz->device->dev.kobj, "device");
+       if (result)
+               return result;
+
+       status = acpi_attach_data(tz->device->handle,
+                                 acpi_bus_private_data_handler,
+                                 tz->thermal_zone);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                               "Error attaching device data\n"));
+               return -ENODEV;
+       }
+
+       tz->tz_enabled = 1;
+
+       printk(KERN_INFO PREFIX "%s is registered as thermal_zone%d\n",
+                       tz->device->dev.bus_id, tz->thermal_zone->id);
+       return 0;
+}
+
+static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
+{
+       sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
+       sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
+       thermal_zone_device_unregister(tz->thermal_zone);
+       tz->thermal_zone = NULL;
+       acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler);
+}
+
+
 /* --------------------------------------------------------------------------
                               FS Interface (/proc)
    -------------------------------------------------------------------------- */
@@ -1181,15 +1518,15 @@ static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data)
                acpi_thermal_check(tz);
                break;
        case ACPI_THERMAL_NOTIFY_THRESHOLDS:
-               acpi_thermal_get_trip_points(tz);
+               acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);
                acpi_thermal_check(tz);
                acpi_bus_generate_proc_event(device, event, 0);
                acpi_bus_generate_netlink_event(device->pnp.device_class,
                                                  device->dev.bus_id, event, 0);
                break;
        case ACPI_THERMAL_NOTIFY_DEVICES:
-               if (tz->flags.devices)
-                       acpi_thermal_get_devices(tz);
+               acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);
+               acpi_thermal_check(tz);
                acpi_bus_generate_proc_event(device, event, 0);
                acpi_bus_generate_netlink_event(device->pnp.device_class,
                                                  device->dev.bus_id, event, 0);
@@ -1232,11 +1569,6 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz)
        else
                acpi_thermal_get_polling_frequency(tz);
 
-       /* Get devices in this thermal zone [_TZD] (optional) */
-       result = acpi_thermal_get_devices(tz);
-       if (!result)
-               tz->flags.devices = 1;
-
        return 0;
 }
 
@@ -1260,13 +1592,19 @@ static int acpi_thermal_add(struct acpi_device *device)
        strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
        acpi_driver_data(device) = tz;
        mutex_init(&tz->lock);
+
+
        result = acpi_thermal_get_info(tz);
        if (result)
-               goto end;
+               goto free_memory;
+
+       result = acpi_thermal_register_thermal_zone(tz);
+       if (result)
+               goto free_memory;
 
        result = acpi_thermal_add_fs(device);
        if (result)
-               goto end;
+               goto unregister_thermal_zone;
 
        init_timer(&tz->timer);
 
@@ -1277,19 +1615,21 @@ static int acpi_thermal_add(struct acpi_device *device)
                                             acpi_thermal_notify, tz);
        if (ACPI_FAILURE(status)) {
                result = -ENODEV;
-               goto end;
+               goto remove_fs;
        }
 
        printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n",
               acpi_device_name(device), acpi_device_bid(device),
               KELVIN_TO_CELSIUS(tz->temperature));
+       goto end;
 
-      end:
-       if (result) {
-               acpi_thermal_remove_fs(device);
-               kfree(tz);
-       }
-
+remove_fs:
+       acpi_thermal_remove_fs(device);
+unregister_thermal_zone:
+       thermal_zone_device_unregister(tz->thermal_zone);
+free_memory:
+       kfree(tz);
+end:
        return result;
 }
 
@@ -1329,6 +1669,7 @@ static int acpi_thermal_remove(struct acpi_device *device, int type)
        }
 
        acpi_thermal_remove_fs(device);
+       acpi_thermal_unregister_thermal_zone(tz);
        mutex_destroy(&tz->lock);
        kfree(tz);
        return 0;
index 93ea829..630c9a2 100644 (file)
@@ -671,7 +671,6 @@ void acpi_ut_init_globals(void)
 
        /* GPE support */
 
-       acpi_gpe_count = 0;
        acpi_gbl_gpe_xrupt_list_head = NULL;
        acpi_gbl_gpe_fadt_blocks[0] = NULL;
        acpi_gbl_gpe_fadt_blocks[1] = NULL;
@@ -735,4 +734,3 @@ void acpi_ut_init_globals(void)
 
 ACPI_EXPORT_SYMBOL(acpi_dbg_level)
     ACPI_EXPORT_SYMBOL(acpi_dbg_layer)
-    ACPI_EXPORT_SYMBOL(acpi_gpe_count)
index bd77e81..7f714fa 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/seq_file.h>
 #include <linux/input.h>
 #include <linux/backlight.h>
+#include <linux/thermal.h>
 #include <linux/video_output.h>
 #include <asm/uaccess.h>
 
@@ -72,8 +73,12 @@ MODULE_AUTHOR("Bruno Ducrot");
 MODULE_DESCRIPTION("ACPI Video Driver");
 MODULE_LICENSE("GPL");
 
+static int brightness_switch_enabled = 1;
+module_param(brightness_switch_enabled, bool, 0644);
+
 static int acpi_video_bus_add(struct acpi_device *device);
 static int acpi_video_bus_remove(struct acpi_device *device, int type);
+static int acpi_video_resume(struct acpi_device *device);
 
 static const struct acpi_device_id video_device_ids[] = {
        {ACPI_VIDEO_HID, 0},
@@ -88,6 +93,7 @@ static struct acpi_driver acpi_video_bus = {
        .ops = {
                .add = acpi_video_bus_add,
                .remove = acpi_video_bus_remove,
+               .resume = acpi_video_resume,
                },
 };
 
@@ -179,6 +185,7 @@ struct acpi_video_device {
        struct acpi_device *dev;
        struct acpi_video_device_brightness *brightness;
        struct backlight_device *backlight;
+       struct thermal_cooling_device *cdev;
        struct output_device *output_dev;
 };
 
@@ -273,7 +280,6 @@ static void acpi_video_device_rebind(struct acpi_video_bus *video);
 static void acpi_video_device_bind(struct acpi_video_bus *video,
                                   struct acpi_video_device *device);
 static int acpi_video_device_enumerate(struct acpi_video_bus *video);
-static int acpi_video_switch_output(struct acpi_video_bus *video, int event);
 static int acpi_video_device_lcd_set_level(struct acpi_video_device *device,
                        int level);
 static int acpi_video_device_lcd_get_level_current(
@@ -292,18 +298,26 @@ static int acpi_video_device_set_state(struct acpi_video_device *device, int sta
 static int acpi_video_get_brightness(struct backlight_device *bd)
 {
        unsigned long cur_level;
+       int i;
        struct acpi_video_device *vd =
                (struct acpi_video_device *)bl_get_data(bd);
        acpi_video_device_lcd_get_level_current(vd, &cur_level);
-       return (int) cur_level;
+       for (i = 2; i < vd->brightness->count; i++) {
+               if (vd->brightness->levels[i] == cur_level)
+                       /* The first two entries are special - see page 575
+                          of the ACPI spec 3.0 */
+                       return i-2;
+       }
+       return 0;
 }
 
 static int acpi_video_set_brightness(struct backlight_device *bd)
 {
-       int request_level = bd->props.brightness;
+       int request_level = bd->props.brightness+2;
        struct acpi_video_device *vd =
                (struct acpi_video_device *)bl_get_data(bd);
-       acpi_video_device_lcd_set_level(vd, request_level);
+       acpi_video_device_lcd_set_level(vd,
+                                       vd->brightness->levels[request_level]);
        return 0;
 }
 
@@ -334,6 +348,54 @@ static struct output_properties acpi_output_properties = {
        .set_state = acpi_video_output_set,
        .get_status = acpi_video_output_get,
 };
+
+
+/* thermal cooling device callbacks */
+static int video_get_max_state(struct thermal_cooling_device *cdev, char *buf)
+{
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_video_device *video = acpi_driver_data(device);
+
+       return sprintf(buf, "%d\n", video->brightness->count - 3);
+}
+
+static int video_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
+{
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_video_device *video = acpi_driver_data(device);
+       unsigned long level;
+       int state;
+
+       acpi_video_device_lcd_get_level_current(video, &level);
+       for (state = 2; state < video->brightness->count; state++)
+               if (level == video->brightness->levels[state])
+                       return sprintf(buf, "%d\n",
+                                      video->brightness->count - state - 1);
+
+       return -EINVAL;
+}
+
+static int
+video_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
+{
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_video_device *video = acpi_driver_data(device);
+       int level;
+
+       if ( state >= video->brightness->count - 2)
+               return -EINVAL;
+
+       state = video->brightness->count - state;
+       level = video->brightness->levels[state -1];
+       return acpi_video_device_lcd_set_level(video, level);
+}
+
+static struct thermal_cooling_device_ops video_cooling_ops = {
+       .get_max_state = video_get_max_state,
+       .get_cur_state = video_get_cur_state,
+       .set_cur_state = video_set_cur_state,
+};
+
 /* --------------------------------------------------------------------------
                                Video Management
    -------------------------------------------------------------------------- */
@@ -652,7 +714,7 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
        kfree(obj);
 
        if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){
-               unsigned long tmp;
+               int result;
                static int count = 0;
                char *name;
                name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
@@ -660,14 +722,30 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                        return;
 
                sprintf(name, "acpi_video%d", count++);
-               acpi_video_device_lcd_get_level_current(device, &tmp);
                device->backlight = backlight_device_register(name,
                        NULL, device, &acpi_backlight_ops);
-               device->backlight->props.max_brightness = max_level;
-               device->backlight->props.brightness = (int)tmp;
+               device->backlight->props.max_brightness = device->brightness->count-3;
+               device->backlight->props.brightness = acpi_video_get_brightness(device->backlight);
                backlight_update_status(device->backlight);
-
                kfree(name);
+
+               device->cdev = thermal_cooling_device_register("LCD",
+                                       device->dev, &video_cooling_ops);
+               if (device->cdev) {
+                       printk(KERN_INFO PREFIX
+                               "%s is registered as cooling_device%d\n",
+                               device->dev->dev.bus_id, device->cdev->id);
+                       result = sysfs_create_link(&device->dev->dev.kobj,
+                                         &device->cdev->device.kobj,
+                                         "thermal_cooling");
+                       if (result)
+                               printk(KERN_ERR PREFIX "Create sysfs link\n");
+                       result = sysfs_create_link(&device->cdev->device.kobj,
+                                         &device->dev->dev.kobj,
+                                         "device");
+                        if (result)
+                               printk(KERN_ERR PREFIX "Create sysfs link\n");
+               }
        }
        if (device->cap._DCS && device->cap._DSS){
                static int count = 0;
@@ -726,11 +804,40 @@ static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
 static int acpi_video_bus_check(struct acpi_video_bus *video)
 {
        acpi_status status = -ENOENT;
-
+       long device_id;
+       struct device *dev;
+       struct acpi_device *device;
 
        if (!video)
                return -EINVAL;
 
+       device = video->device;
+
+       status =
+           acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
+
+       if (!ACPI_SUCCESS(status))
+               return -ENODEV;
+
+       /* We need to attempt to determine whether the _ADR refers to a
+          PCI device or not. There's no terribly good way to do this,
+          so the best we can hope for is to assume that there'll never
+          be a video device in the host bridge */
+       if (device_id >= 0x10000) {
+               /* It looks like a PCI device. Does it exist? */
+               dev = acpi_get_physical_device(device->handle);
+       } else {
+               /* It doesn't look like a PCI device. Does its parent
+                  exist? */
+               acpi_handle phandle;
+               if (acpi_get_parent(device->handle, &phandle))
+                       return -ENODEV;
+               dev = acpi_get_physical_device(phandle);
+       }
+       if (!dev)
+               return -ENODEV;
+       put_device(dev);
+
        /* Since there is no HID, CID and so on for VGA driver, we have
         * to check well known required nodes.
         */
@@ -1256,8 +1363,37 @@ acpi_video_bus_write_DOS(struct file *file,
 
 static int acpi_video_bus_add_fs(struct acpi_device *device)
 {
+       long device_id;
+       int status;
        struct proc_dir_entry *entry = NULL;
        struct acpi_video_bus *video;
+       struct device *dev;
+
+       status =
+           acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
+
+       if (!ACPI_SUCCESS(status))
+               return -ENODEV;
+
+       /* We need to attempt to determine whether the _ADR refers to a
+          PCI device or not. There's no terribly good way to do this,
+          so the best we can hope for is to assume that there'll never
+          be a video device in the host bridge */
+       if (device_id >= 0x10000) {
+               /* It looks like a PCI device. Does it exist? */
+               dev = acpi_get_physical_device(device->handle);
+       } else {
+               /* It doesn't look like a PCI device. Does its parent
+                  exist? */
+               acpi_handle phandle;
+               if (acpi_get_parent(device->handle, &phandle))
+                       return -ENODEV;
+               dev = acpi_get_physical_device(phandle);
+       }
+       if (!dev)
+               return -ENODEV;
+       put_device(dev);
+
 
 
        video = acpi_driver_data(device);
@@ -1580,64 +1716,6 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video)
        return status;
 }
 
-/*
- *  Arg:
- *     video   : video bus device 
- *     event   : notify event
- *
- *  Return:
- *     < 0     : error
- *  
- *     1. Find out the current active output device.
- *     2. Identify the next output device to switch to.
- *     3. call _DSS to do actual switch.
- */
-
-static int acpi_video_switch_output(struct acpi_video_bus *video, int event)
-{
-       struct list_head *node;
-       struct acpi_video_device *dev = NULL;
-       struct acpi_video_device *dev_next = NULL;
-       struct acpi_video_device *dev_prev = NULL;
-       unsigned long state;
-       int status = 0;
-
-       mutex_lock(&video->device_list_lock);
-
-       list_for_each(node, &video->video_device_list) {
-               dev = container_of(node, struct acpi_video_device, entry);
-               status = acpi_video_device_get_state(dev, &state);
-               if (state & 0x2) {
-                       dev_next = container_of(node->next,
-                                       struct acpi_video_device, entry);
-                       dev_prev = container_of(node->prev,
-                                       struct acpi_video_device, entry);
-                       goto out;
-               }
-       }
-
-       dev_next = container_of(node->next, struct acpi_video_device, entry);
-       dev_prev = container_of(node->prev, struct acpi_video_device, entry);
-
- out:
-       mutex_unlock(&video->device_list_lock);
-
-       switch (event) {
-       case ACPI_VIDEO_NOTIFY_CYCLE:
-       case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:
-               acpi_video_device_set_state(dev, 0);
-               acpi_video_device_set_state(dev_next, 0x80000001);
-               break;
-       case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:
-               acpi_video_device_set_state(dev, 0);
-               acpi_video_device_set_state(dev_prev, 0x80000001);
-       default:
-               break;
-       }
-
-       return status;
-}
-
 static int
 acpi_video_get_next_level(struct acpi_video_device *device,
                          u32 level_current, u32 event)
@@ -1729,6 +1807,14 @@ 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);
+       if (device->cdev) {
+               sysfs_remove_link(&device->dev->dev.kobj,
+                                 "thermal_cooling");
+               sysfs_remove_link(&device->cdev->device.kobj,
+                                 "device");
+               thermal_cooling_device_unregister(device->cdev);
+               device->cdev = NULL;
+       }
        video_output_unregister(device->output_dev);
 
        return 0;
@@ -1797,23 +1883,19 @@ static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
                                         * connector. */
                acpi_video_device_enumerate(video);
                acpi_video_device_rebind(video);
-               acpi_video_switch_output(video, event);
                acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_SWITCHVIDEOMODE;
                break;
 
        case ACPI_VIDEO_NOTIFY_CYCLE:   /* Cycle Display output hotkey pressed. */
-               acpi_video_switch_output(video, event);
                acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_SWITCHVIDEOMODE;
                break;
        case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:     /* Next Display output hotkey pressed. */
-               acpi_video_switch_output(video, event);
                acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_VIDEO_NEXT;
                break;
        case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:     /* previous Display output hotkey pressed. */
-               acpi_video_switch_output(video, event);
                acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_VIDEO_PREV;
                break;
@@ -1825,6 +1907,7 @@ static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
                break;
        }
 
+       acpi_notifier_call_chain(device, event, 0);
        input_report_key(input, keycode, 1);
        input_sync(input);
        input_report_key(input, keycode, 0);
@@ -1850,27 +1933,32 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
 
        switch (event) {
        case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:        /* Cycle brightness */
-               acpi_video_switch_brightness(video_device, event);
+               if (brightness_switch_enabled)
+                       acpi_video_switch_brightness(video_device, event);
                acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_BRIGHTNESS_CYCLE;
                break;
        case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:  /* Increase brightness */
-               acpi_video_switch_brightness(video_device, event);
+               if (brightness_switch_enabled)
+                       acpi_video_switch_brightness(video_device, event);
                acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_BRIGHTNESSUP;
                break;
        case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:  /* Decrease brightness */
-               acpi_video_switch_brightness(video_device, event);
+               if (brightness_switch_enabled)
+                       acpi_video_switch_brightness(video_device, event);
                acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_BRIGHTNESSDOWN;
                break;
        case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightnesss */
-               acpi_video_switch_brightness(video_device, event);
+               if (brightness_switch_enabled)
+                       acpi_video_switch_brightness(video_device, event);
                acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_BRIGHTNESS_ZERO;
                break;
        case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:     /* display device off */
-               acpi_video_switch_brightness(video_device, event);
+               if (brightness_switch_enabled)
+                       acpi_video_switch_brightness(video_device, event);
                acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_DISPLAY_OFF;
                break;
@@ -1881,6 +1969,7 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
                break;
        }
 
+       acpi_notifier_call_chain(device, event, 0);
        input_report_key(input, keycode, 1);
        input_sync(input);
        input_report_key(input, keycode, 0);
@@ -1890,6 +1979,25 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
 }
 
 static int instance;
+static int acpi_video_resume(struct acpi_device *device)
+{
+       struct acpi_video_bus *video;
+       struct acpi_video_device *video_device;
+       int i;
+
+       if (!device || !acpi_driver_data(device))
+               return -EINVAL;
+
+       video = acpi_driver_data(device);
+
+       for (i = 0; i < video->attached_count; i++) {
+               video_device = video->attached_array[i].bind_info;
+               if (video_device && video_device->backlight)
+                       acpi_video_set_brightness(video_device->backlight);
+       }
+       return AE_OK;
+}
+
 static int acpi_video_bus_add(struct acpi_device *device)
 {
        acpi_status status;
diff --git a/drivers/acpi/wmi.c b/drivers/acpi/wmi.c
new file mode 100644 (file)
index 0000000..36b84ab
--- /dev/null
@@ -0,0 +1,710 @@
+/*
+ *  ACPI-WMI mapping driver
+ *
+ *  Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
+ *
+ *  GUID parsing code from ldm.c is:
+ *   Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
+ *   Copyright (c) 2001-2007 Anton Altaparmakov
+ *   Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+ACPI_MODULE_NAME("wmi");
+MODULE_AUTHOR("Carlos Corbacho");
+MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
+MODULE_LICENSE("GPL");
+
+#define ACPI_WMI_CLASS "wmi"
+
+#undef PREFIX
+#define PREFIX "ACPI: WMI: "
+
+static DEFINE_MUTEX(wmi_data_lock);
+
+struct guid_block {
+       char guid[16];
+       union {
+               char object_id[2];
+               struct {
+                       unsigned char notify_id;
+                       unsigned char reserved;
+               };
+       };
+       u8 instance_count;
+       u8 flags;
+};
+
+struct wmi_block {
+       struct list_head list;
+       struct guid_block gblock;
+       acpi_handle handle;
+       wmi_notify_handler handler;
+       void *handler_data;
+};
+
+static struct wmi_block wmi_blocks;
+
+/*
+ * If the GUID data block is marked as expensive, we must enable and
+ * explicitily disable data collection.
+ */
+#define ACPI_WMI_EXPENSIVE   0x1
+#define ACPI_WMI_METHOD      0x2       /* GUID is a method */
+#define ACPI_WMI_STRING      0x4       /* GUID takes & returns a string */
+#define ACPI_WMI_EVENT       0x8       /* GUID is an event */
+
+static int acpi_wmi_remove(struct acpi_device *device, int type);
+static int acpi_wmi_add(struct acpi_device *device);
+
+static const struct acpi_device_id wmi_device_ids[] = {
+       {"PNP0C14", 0},
+       {"pnp0c14", 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
+
+static struct acpi_driver acpi_wmi_driver = {
+       .name = "wmi",
+       .class = ACPI_WMI_CLASS,
+       .ids = wmi_device_ids,
+       .ops = {
+               .add = acpi_wmi_add,
+               .remove = acpi_wmi_remove,
+               },
+};
+
+/*
+ * GUID parsing functions
+ */
+
+/**
+ * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
+ * @src:  Pointer to at least 2 characters to convert.
+ *
+ * Convert a two character ASCII hex string to a number.
+ *
+ * Return:  0-255  Success, the byte was parsed correctly
+ *          -1     Error, an invalid character was supplied
+ */
+static int wmi_parse_hexbyte(const u8 *src)
+{
+       unsigned int x; /* For correct wrapping */
+       int h;
+
+       /* high part */
+       x = src[0];
+       if (x - '0' <= '9' - '0') {
+               h = x - '0';
+       } else if (x - 'a' <= 'f' - 'a') {
+               h = x - 'a' + 10;
+       } else if (x - 'A' <= 'F' - 'A') {
+               h = x - 'A' + 10;
+       } else {
+               return -1;
+       }
+       h <<= 4;
+
+       /* low part */
+       x = src[1];
+       if (x - '0' <= '9' - '0')
+               return h | (x - '0');
+       if (x - 'a' <= 'f' - 'a')
+               return h | (x - 'a' + 10);
+       if (x - 'A' <= 'F' - 'A')
+               return h | (x - 'A' + 10);
+       return -1;
+}
+
+/**
+ * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
+ * @src:   Memory block holding binary GUID (16 bytes)
+ * @dest:  Memory block to hold byte swapped binary GUID (16 bytes)
+ *
+ * Byte swap a binary GUID to match it's real GUID value
+ */
+static void wmi_swap_bytes(u8 *src, u8 *dest)
+{
+       int i;
+
+       for (i = 0; i <= 3; i++)
+               memcpy(dest + i, src + (3 - i), 1);
+
+       for (i = 0; i <= 1; i++)
+               memcpy(dest + 4 + i, src + (5 - i), 1);
+
+       for (i = 0; i <= 1; i++)
+               memcpy(dest + 6 + i, src + (7 - i), 1);
+
+       memcpy(dest + 8, src + 8, 8);
+}
+
+/**
+ * wmi_parse_guid - Convert GUID from ASCII to binary
+ * @src:   36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
+ * @dest:  Memory block to hold binary GUID (16 bytes)
+ *
+ * N.B. The GUID need not be NULL terminated.
+ *
+ * Return:  'true'   @dest contains binary GUID
+ *          'false'  @dest contents are undefined
+ */
+static bool wmi_parse_guid(const u8 *src, u8 *dest)
+{
+       static const int size[] = { 4, 2, 2, 2, 6 };
+       int i, j, v;
+
+       if (src[8]  != '-' || src[13] != '-' ||
+               src[18] != '-' || src[23] != '-')
+               return false;
+
+       for (j = 0; j < 5; j++, src++) {
+               for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
+                       v = wmi_parse_hexbyte(src);
+                       if (v < 0)
+                               return false;
+               }
+       }
+
+       return true;
+}
+
+static bool find_guid(const char *guid_string, struct wmi_block **out)
+{
+       char tmp[16], guid_input[16];
+       struct wmi_block *wblock;
+       struct guid_block *block;
+       struct list_head *p;
+
+       wmi_parse_guid(guid_string, tmp);
+       wmi_swap_bytes(tmp, guid_input);
+
+       list_for_each(p, &wmi_blocks.list) {
+               wblock = list_entry(p, struct wmi_block, list);
+               block = &wblock->gblock;
+
+               if (memcmp(block->guid, guid_input, 16) == 0) {
+                       if (out)
+                               *out = wblock;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Exported WMI functions
+ */
+/**
+ * wmi_evaluate_method - Evaluate a WMI method
+ * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
+ * @instance: Instance index
+ * @method_id: Method ID to call
+ * &in: Buffer containing input for the method call
+ * &out: Empty buffer to return the method results
+ *
+ * Call an ACPI-WMI method
+ */
+acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
+u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
+{
+       struct guid_block *block = NULL;
+       struct wmi_block *wblock = NULL;
+       acpi_handle handle;
+       acpi_status status;
+       struct acpi_object_list input;
+       union acpi_object params[3];
+       char method[4] = "WM";
+
+       if (!find_guid(guid_string, &wblock))
+               return AE_BAD_ADDRESS;
+
+       block = &wblock->gblock;
+       handle = wblock->handle;
+
+       if (!block->flags & ACPI_WMI_METHOD)
+               return AE_BAD_DATA;
+
+       if (block->instance_count < instance)
+               return AE_BAD_PARAMETER;
+
+       input.count = 2;
+       input.pointer = params;
+       params[0].type = ACPI_TYPE_INTEGER;
+       params[0].integer.value = instance;
+       params[1].type = ACPI_TYPE_INTEGER;
+       params[1].integer.value = method_id;
+
+       if (in) {
+               input.count = 3;
+
+               if (block->flags & ACPI_WMI_STRING) {
+                       params[2].type = ACPI_TYPE_STRING;
+               } else {
+                       params[2].type = ACPI_TYPE_BUFFER;
+               }
+               params[2].buffer.length = in->length;
+               params[2].buffer.pointer = in->pointer;
+       }
+
+       strncat(method, block->object_id, 2);
+
+       status = acpi_evaluate_object(handle, method, &input, out);
+
+       return status;
+}
+EXPORT_SYMBOL_GPL(wmi_evaluate_method);
+
+/**
+ * wmi_query_block - Return contents of a WMI block
+ * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
+ * @instance: Instance index
+ * &out: Empty buffer to return the contents of the data block to
+ *
+ * Return the contents of an ACPI-WMI data block to a buffer
+ */
+acpi_status wmi_query_block(const char *guid_string, u8 instance,
+struct acpi_buffer *out)
+{
+       struct guid_block *block = NULL;
+       struct wmi_block *wblock = NULL;
+       acpi_handle handle;
+       acpi_status status, wc_status = AE_ERROR;
+       struct acpi_object_list input, wc_input;
+       union acpi_object wc_params[1], wq_params[1];
+       char method[4];
+       char wc_method[4] = "WC";
+
+       if (!guid_string || !out)
+               return AE_BAD_PARAMETER;
+
+       if (!find_guid(guid_string, &wblock))
+               return AE_BAD_ADDRESS;
+
+       block = &wblock->gblock;
+       handle = wblock->handle;
+
+       if (block->instance_count < instance)
+               return AE_BAD_PARAMETER;
+
+       /* Check GUID is a data block */
+       if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
+               return AE_BAD_ADDRESS;
+
+       input.count = 1;
+       input.pointer = wq_params;
+       wq_params[0].type = ACPI_TYPE_INTEGER;
+       wq_params[0].integer.value = instance;
+
+       /*
+        * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
+        * enable collection.
+        */
+       if (block->flags & ACPI_WMI_EXPENSIVE) {
+               wc_input.count = 1;
+               wc_input.pointer = wc_params;
+               wc_params[0].type = ACPI_TYPE_INTEGER;
+               wc_params[0].integer.value = 1;
+
+               strncat(wc_method, block->object_id, 2);
+
+               /*
+                * Some GUIDs break the specification by declaring themselves
+                * expensive, but have no corresponding WCxx method. So we
+                * should not fail if this happens.
+                */
+               wc_status = acpi_evaluate_object(handle, wc_method,
+                       &wc_input, NULL);
+       }
+
+       strcpy(method, "WQ");
+       strncat(method, block->object_id, 2);
+
+       status = acpi_evaluate_object(handle, method, NULL, out);
+
+       /*
+        * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
+        * the WQxx method failed - we should disable collection anyway.
+        */
+       if ((block->flags & ACPI_WMI_EXPENSIVE) && wc_status) {
+               wc_params[0].integer.value = 0;
+               status = acpi_evaluate_object(handle,
+               wc_method, &wc_input, NULL);
+       }
+
+       return status;
+}
+EXPORT_SYMBOL_GPL(wmi_query_block);
+
+/**
+ * wmi_set_block - Write to a WMI block
+ * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
+ * @instance: Instance index
+ * &in: Buffer containing new values for the data block
+ *
+ * Write the contents of the input buffer to an ACPI-WMI data block
+ */
+acpi_status wmi_set_block(const char *guid_string, u8 instance,
+const struct acpi_buffer *in)
+{
+       struct guid_block *block = NULL;
+       struct wmi_block *wblock = NULL;
+       acpi_handle handle;
+       struct acpi_object_list input;
+       union acpi_object params[2];
+       char method[4] = "WS";
+
+       if (!guid_string || !in)
+               return AE_BAD_DATA;
+
+       if (!find_guid(guid_string, &wblock))
+               return AE_BAD_ADDRESS;
+
+       block = &wblock->gblock;
+       handle = wblock->handle;
+
+       if (block->instance_count < instance)
+               return AE_BAD_PARAMETER;
+
+       /* Check GUID is a data block */
+       if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
+               return AE_BAD_ADDRESS;
+
+       input.count = 2;
+       input.pointer = params;
+       params[0].type = ACPI_TYPE_INTEGER;
+       params[0].integer.value = instance;
+
+       if (block->flags & ACPI_WMI_STRING) {
+               params[1].type = ACPI_TYPE_STRING;
+       } else {
+               params[1].type = ACPI_TYPE_BUFFER;
+       }
+       params[1].buffer.length = in->length;
+       params[1].buffer.pointer = in->pointer;
+
+       strncat(method, block->object_id, 2);
+
+       return acpi_evaluate_object(handle, method, &input, NULL);
+}
+EXPORT_SYMBOL_GPL(wmi_set_block);
+
+/**
+ * wmi_install_notify_handler - Register handler for WMI events
+ * @handler: Function to handle notifications
+ * @data: Data to be returned to handler when event is fired
+ *
+ * Register a handler for events sent to the ACPI-WMI mapper device.
+ */
+acpi_status wmi_install_notify_handler(const char *guid,
+wmi_notify_handler handler, void *data)
+{
+       struct wmi_block *block;
+
+       if (!guid || !handler)
+               return AE_BAD_PARAMETER;
+
+       find_guid(guid, &block);
+       if (!block)
+               return AE_NOT_EXIST;
+
+       if (block->handler)
+               return AE_ALREADY_ACQUIRED;
+
+       block->handler = handler;
+       block->handler_data = data;
+
+       return AE_OK;
+}
+EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
+
+/**
+ * wmi_uninstall_notify_handler - Unregister handler for WMI events
+ *
+ * Unregister handler for events sent to the ACPI-WMI mapper device.
+ */
+acpi_status wmi_remove_notify_handler(const char *guid)
+{
+       struct wmi_block *block;
+
+       if (!guid)
+               return AE_BAD_PARAMETER;
+
+       find_guid(guid, &block);
+       if (!block)
+               return AE_NOT_EXIST;
+
+       if (!block->handler)
+               return AE_NULL_ENTRY;
+
+       block->handler = NULL;
+       block->handler_data = NULL;
+
+       return AE_OK;
+}
+EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
+
+/**
+ * wmi_get_event_data - Get WMI data associated with an event
+ *
+ * @event - Event to find
+ * &out - Buffer to hold event data
+ *
+ * Returns extra data associated with an event in WMI.
+ */
+acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
+{
+       struct acpi_object_list input;
+       union acpi_object params[1];
+       struct guid_block *gblock;
+       struct wmi_block *wblock;
+       struct list_head *p;
+
+       input.count = 1;
+       input.pointer = params;
+       params[0].type = ACPI_TYPE_INTEGER;
+       params[0].integer.value = event;
+
+       list_for_each(p, &wmi_blocks.list) {
+               wblock = list_entry(p, struct wmi_block, list);
+               gblock = &wblock->gblock;
+
+               if ((gblock->flags & ACPI_WMI_EVENT) &&
+                       (gblock->notify_id == event))
+                       return acpi_evaluate_object(wblock->handle, "_WED",
+                               &input, out);
+       }
+
+       return AE_NOT_FOUND;
+}
+EXPORT_SYMBOL_GPL(wmi_get_event_data);
+
+/**
+ * wmi_has_guid - Check if a GUID is available
+ * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
+ *
+ * Check if a given GUID is defined by _WDG
+ */
+bool wmi_has_guid(const char *guid_string)
+{
+       return find_guid(guid_string, NULL);
+}
+EXPORT_SYMBOL_GPL(wmi_has_guid);
+
+/*
+ * Parse the _WDG method for the GUID data blocks
+ */
+static __init acpi_status parse_wdg(acpi_handle handle)
+{
+       struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object *obj;
+       struct guid_block *gblock;
+       struct wmi_block *wblock;
+       acpi_status status;
+       u32 i, total;
+
+       status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
+
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = (union acpi_object *) out.pointer;
+
+       if (obj->type != ACPI_TYPE_BUFFER)
+               return AE_ERROR;
+
+       total = obj->buffer.length / sizeof(struct guid_block);
+
+       gblock = kzalloc(obj->buffer.length, GFP_KERNEL);
+       if (!gblock)
+               return AE_NO_MEMORY;
+
+       memcpy(gblock, obj->buffer.pointer, obj->buffer.length);
+
+       for (i = 0; i < total; i++) {
+               wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
+               if (!wblock)
+                       return AE_NO_MEMORY;
+
+               wblock->gblock = gblock[i];
+               wblock->handle = handle;
+               list_add_tail(&wblock->list, &wmi_blocks.list);
+       }
+
+       kfree(out.pointer);
+       kfree(gblock);
+
+       return status;
+}
+
+/*
+ * WMI can have EmbeddedControl access regions. In which case, we just want to
+ * hand these off to the EC driver.
+ */
+static acpi_status
+acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
+                     u32 bits, acpi_integer * value,
+                     void *handler_context, void *region_context)
+{
+       int result = 0, i = 0;
+       u8 temp = 0;
+
+       if ((address > 0xFF) || !value)
+               return AE_BAD_PARAMETER;
+
+       if (function != ACPI_READ && function != ACPI_WRITE)
+               return AE_BAD_PARAMETER;
+
+       if (bits != 8)
+               return AE_BAD_PARAMETER;
+
+       if (function == ACPI_READ) {
+               result = ec_read(address, &temp);
+               (*value) |= ((acpi_integer)temp) << i;
+       } else {
+               temp = 0xff & ((*value) >> i);
+               result = ec_write(address, temp);
+       }
+
+       switch (result) {
+       case -EINVAL:
+               return AE_BAD_PARAMETER;
+               break;
+       case -ENODEV:
+               return AE_NOT_FOUND;
+               break;
+       case -ETIME:
+               return AE_TIME;
+               break;
+       default:
+               return AE_OK;
+       }
+}
+
+static void acpi_wmi_notify(acpi_handle handle, u32 event, void *data)
+{
+       struct guid_block *block;
+       struct wmi_block *wblock;
+       struct list_head *p;
+       struct acpi_device *device = data;
+
+       list_for_each(p, &wmi_blocks.list) {
+               wblock = list_entry(p, struct wmi_block, list);
+               block = &wblock->gblock;
+
+               if ((block->flags & ACPI_WMI_EVENT) &&
+                       (block->notify_id == event)) {
+                       if (wblock->handler)
+                               wblock->handler(event, wblock->handler_data);
+
+                       acpi_bus_generate_netlink_event(
+                               device->pnp.device_class, device->dev.bus_id,
+                               event, 0);
+                       break;
+               }
+       }
+}
+
+static int acpi_wmi_remove(struct acpi_device *device, int type)
+{
+       acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+               acpi_wmi_notify);
+
+       acpi_remove_address_space_handler(device->handle,
+                               ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
+
+       return 0;
+}
+
+static int __init acpi_wmi_add(struct acpi_device *device)
+{
+       acpi_status status;
+       int result = 0;
+
+       status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+               acpi_wmi_notify, device);
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR PREFIX "Error installing notify handler\n");
+               return -ENODEV;
+       }
+
+       status = acpi_install_address_space_handler(device->handle,
+                                                   ACPI_ADR_SPACE_EC,
+                                                   &acpi_wmi_ec_space_handler,
+                                                   NULL, NULL);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       status = parse_wdg(device->handle);
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR PREFIX "Error installing EC region handler\n");
+               return -ENODEV;
+       }
+
+       return result;
+}
+
+static int __init acpi_wmi_init(void)
+{
+       acpi_status result;
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       INIT_LIST_HEAD(&wmi_blocks.list);
+
+       result = acpi_bus_register_driver(&acpi_wmi_driver);
+
+       if (result < 0) {
+               printk(KERN_INFO PREFIX "Error loading mapper\n");
+       } else {
+               printk(KERN_INFO PREFIX "Mapper loaded\n");
+       }
+
+       return result;
+}
+
+static void __exit acpi_wmi_exit(void)
+{
+       struct list_head *p, *tmp;
+       struct wmi_block *wblock;
+
+       acpi_bus_unregister_driver(&acpi_wmi_driver);
+
+       list_for_each_safe(p, tmp, &wmi_blocks.list) {
+               wblock = list_entry(p, struct wmi_block, list);
+
+               list_del(p);
+               kfree(wblock);
+       }
+
+       printk(KERN_INFO PREFIX "Mapper unloaded\n");
+}
+
+subsys_initcall(acpi_wmi_init);
+module_exit(acpi_wmi_exit);
index 3bed412..7dbc4a8 100644 (file)
@@ -1,13 +1,13 @@
 
 config CPU_IDLE
        bool "CPU idle PM support"
+       default ACPI
        help
          CPU idle is a generic framework for supporting software-controlled
          idle processor power management.  It includes modular cross-platform
          governors that can be swapped during runtime.
 
-         If you're using a mobile platform that supports CPU idle PM (e.g.
-         an ACPI-capable notebook), you should say Y here.
+         If you're using an ACPI-enabled platform, you should say Y here.
 
 config CPU_IDLE_GOV_LADDER
        bool
index 2a98d99..2c4b2d4 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/pm_qos_params.h>
 #include <linux/cpu.h>
 #include <linux/cpuidle.h>
+#include <linux/ktime.h>
 
 #include "cpuidle.h"
 
@@ -180,6 +181,44 @@ void cpuidle_disable_device(struct cpuidle_device *dev)
 
 EXPORT_SYMBOL_GPL(cpuidle_disable_device);
 
+#ifdef CONFIG_ARCH_HAS_CPU_RELAX
+static int poll_idle(struct cpuidle_device *dev, struct cpuidle_state *st)
+{
+       ktime_t t1, t2;
+       s64 diff;
+       int ret;
+
+       t1 = ktime_get();
+       local_irq_enable();
+       while (!need_resched())
+               cpu_relax();
+
+       t2 = ktime_get();
+       diff = ktime_to_us(ktime_sub(t2, t1));
+       if (diff > INT_MAX)
+               diff = INT_MAX;
+
+       ret = (int) diff;
+       return ret;
+}
+
+static void poll_idle_init(struct cpuidle_device *dev)
+{
+       struct cpuidle_state *state = &dev->states[0];
+
+       cpuidle_set_statedata(state, NULL);
+
+       snprintf(state->name, CPUIDLE_NAME_LEN, "C0 (poll idle)");
+       state->exit_latency = 0;
+       state->target_residency = 0;
+       state->power_usage = -1;
+       state->flags = CPUIDLE_FLAG_POLL | CPUIDLE_FLAG_TIME_VALID;
+       state->enter = poll_idle;
+}
+#else
+static void poll_idle_init(struct cpuidle_device *dev) {}
+#endif /* CONFIG_ARCH_HAS_CPU_RELAX */
+
 /**
  * cpuidle_register_device - registers a CPU's idle PM feature
  * @dev: the cpu
@@ -198,6 +237,8 @@ int cpuidle_register_device(struct cpuidle_device *dev)
 
        mutex_lock(&cpuidle_lock);
 
+       poll_idle_init(dev);
+
        per_cpu(cpuidle_devices, dev->cpu) = dev;
        list_add(&dev->device_list, &cpuidle_detected_devices);
        if ((ret = cpuidle_add_sysfs(sys_dev))) {
index 9008ed5..e0bade7 100644 (file)
@@ -489,12 +489,3 @@ int dmi_get_year(int field)
 
        return year;
 }
-
-/**
- *     dmi_get_slot - return dmi_ident[slot]
- *     @slot:  index into dmi_ident[]
- */
-char *dmi_get_slot(int slot)
-{
-       return(dmi_ident[slot]);
-}
index b5e67c0..78cd338 100644 (file)
@@ -92,6 +92,22 @@ config TIFM_7XX1
           To compile this driver as a module, choose M here: the module will
          be called tifm_7xx1.
 
+config ACER_WMI
+        tristate "Acer WMI Laptop Extras (EXPERIMENTAL)"
+       depends on X86
+       depends on EXPERIMENTAL
+       depends on ACPI
+       depends on ACPI_WMI
+       depends on LEDS_CLASS
+       depends on BACKLIGHT_CLASS_DEVICE
+       ---help---
+         This is a driver for newer Acer (and Wistron) laptops. It adds
+         wireless radio and bluetooth control, and on some laptops,
+         exposes the mail LED and LCD backlight.
+
+         If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
+         here.
+
 config ASUS_LAPTOP
         tristate "Asus Laptop Extras (EXPERIMENTAL)"
         depends on X86
@@ -126,6 +142,15 @@ config FUJITSU_LAPTOP
 
          If you have a Fujitsu laptop, say Y or M here.
 
+config TC1100_WMI
+       tristate "HP Compaq TC1100 Tablet WMI Extras"
+       depends on X86 && !X86_64
+       depends on ACPI
+       depends on ACPI_WMI
+       ---help---
+         This is a driver for the WMI extensions (wireless and bluetooth power
+         control) of the HP Compaq TC1100 tablet.
+
 config MSI_LAPTOP
         tristate "MSI Laptop Extras"
         depends on X86
@@ -219,6 +244,25 @@ config THINKPAD_ACPI_BAY
 
          If you are not sure, say Y here.
 
+config THINKPAD_ACPI_HOTKEY_POLL
+       bool "Suport NVRAM polling for hot keys"
+       depends on THINKPAD_ACPI
+       default y
+       ---help---
+         Some thinkpad models benefit from NVRAM polling to detect a few of
+         the hot key press events.  If you know your ThinkPad model does not
+         need to do NVRAM polling to support any of the hot keys you use,
+         unselecting this option will save about 1kB of memory.
+
+         ThinkPads T40 and newer, R52 and newer, and X31 and newer are
+         unlikely to need NVRAM polling in their latest BIOS versions.
+
+         NVRAM polling can detect at most the following keys: ThinkPad/Access
+         IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute,
+         Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12).
+
+         If you are not sure, say Y here.  The driver enables polling only if
+         it is strictly necessary to do so.
 
 config ATMEL_SSC
        tristate "Device driver for Atmel SSC peripheral"
@@ -232,4 +276,13 @@ config ATMEL_SSC
 
          If unsure, say N.
 
+config INTEL_MENLOW
+       tristate "Thermal Management driver for Intel menlow platform"
+       depends on ACPI_THERMAL
+       ---help---
+         ACPI thermal management enhancement driver on
+         Intel Menlow platform.
+
+         If unsure, say N.
+
 endif # MISC_DEVICES
index 87f2685..1f41654 100644 (file)
@@ -6,8 +6,10 @@ obj- := misc.o # Dummy rule to force built-in.o to be made
 obj-$(CONFIG_IBM_ASM)          += ibmasm/
 obj-$(CONFIG_HDPU_FEATURES)    += hdpuftrs/
 obj-$(CONFIG_MSI_LAPTOP)     += msi-laptop.o
+obj-$(CONFIG_ACER_WMI)     += acer-wmi.o
 obj-$(CONFIG_ASUS_LAPTOP)     += asus-laptop.o
 obj-$(CONFIG_ATMEL_SSC)                += atmel-ssc.o
+obj-$(CONFIG_TC1100_WMI)       += tc1100-wmi.o
 obj-$(CONFIG_LKDTM)            += lkdtm.o
 obj-$(CONFIG_TIFM_CORE)        += tifm_core.o
 obj-$(CONFIG_TIFM_7XX1)        += tifm_7xx1.o
@@ -17,3 +19,4 @@ obj-$(CONFIG_SONY_LAPTOP)     += sony-laptop.o
 obj-$(CONFIG_THINKPAD_ACPI)    += thinkpad_acpi.o
 obj-$(CONFIG_FUJITSU_LAPTOP)   += fujitsu-laptop.o
 obj-$(CONFIG_EEPROM_93CX6)     += eeprom_93cx6.o
+obj-$(CONFIG_INTEL_MENLOW)     += intel_menlow.o
diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c
new file mode 100644 (file)
index 0000000..a4d6775
--- /dev/null
@@ -0,0 +1,1109 @@
+/*
+ *  Acer WMI Laptop Extras
+ *
+ *  Copyright (C) 2007-2008    Carlos Corbacho <carlos@strangeworlds.co.uk>
+ *
+ *  Based on acer_acpi:
+ *    Copyright (C) 2005-2007  E.M. Smith
+ *    Copyright (C) 2007-2008  Carlos Corbacho <cathectic@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define ACER_WMI_VERSION       "0.1"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/dmi.h>
+#include <linux/backlight.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/i8042.h>
+
+#include <acpi/acpi_drivers.h>
+
+MODULE_AUTHOR("Carlos Corbacho");
+MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
+MODULE_LICENSE("GPL");
+
+#define ACER_LOGPREFIX "acer-wmi: "
+#define ACER_ERR KERN_ERR ACER_LOGPREFIX
+#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
+#define ACER_INFO KERN_INFO ACER_LOGPREFIX
+
+/*
+ * The following defines quirks to get some specific functions to work
+ * which are known to not be supported over ACPI-WMI (such as the mail LED
+ * on WMID based Acer's)
+ */
+struct acer_quirks {
+       const char *vendor;
+       const char *model;
+       u16 quirks;
+};
+
+/*
+ * Magic Number
+ * Meaning is unknown - this number is required for writing to ACPI for AMW0
+ * (it's also used in acerhk when directly accessing the BIOS)
+ */
+#define ACER_AMW0_WRITE        0x9610
+
+/*
+ * Bit masks for the AMW0 interface
+ */
+#define ACER_AMW0_WIRELESS_MASK  0x35
+#define ACER_AMW0_BLUETOOTH_MASK 0x34
+#define ACER_AMW0_MAILLED_MASK   0x31
+
+/*
+ * Method IDs for WMID interface
+ */
+#define ACER_WMID_GET_WIRELESS_METHODID                1
+#define ACER_WMID_GET_BLUETOOTH_METHODID       2
+#define ACER_WMID_GET_BRIGHTNESS_METHODID      3
+#define ACER_WMID_SET_WIRELESS_METHODID                4
+#define ACER_WMID_SET_BLUETOOTH_METHODID       5
+#define ACER_WMID_SET_BRIGHTNESS_METHODID      6
+#define ACER_WMID_GET_THREEG_METHODID          10
+#define ACER_WMID_SET_THREEG_METHODID          11
+
+/*
+ * Acer ACPI method GUIDs
+ */
+#define AMW0_GUID1             "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
+#define WMID_GUID1             "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
+#define WMID_GUID2             "95764E09-FB56-4e83-B31A-37761F60994A"
+
+MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
+MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
+
+/* Temporary workaround until the WMI sysfs interface goes in */
+MODULE_ALIAS("dmi:*:*Acer*:*:");
+
+/*
+ * Interface capability flags
+ */
+#define ACER_CAP_MAILLED               (1<<0)
+#define ACER_CAP_WIRELESS              (1<<1)
+#define ACER_CAP_BLUETOOTH             (1<<2)
+#define ACER_CAP_BRIGHTNESS            (1<<3)
+#define ACER_CAP_THREEG                        (1<<4)
+#define ACER_CAP_ANY                   (0xFFFFFFFF)
+
+/*
+ * Interface type flags
+ */
+enum interface_flags {
+       ACER_AMW0,
+       ACER_AMW0_V2,
+       ACER_WMID,
+};
+
+#define ACER_DEFAULT_WIRELESS  0
+#define ACER_DEFAULT_BLUETOOTH 0
+#define ACER_DEFAULT_MAILLED   0
+#define ACER_DEFAULT_THREEG    0
+
+static int max_brightness = 0xF;
+
+static int wireless = -1;
+static int bluetooth = -1;
+static int mailled = -1;
+static int brightness = -1;
+static int threeg = -1;
+static int force_series;
+
+module_param(mailled, int, 0444);
+module_param(wireless, int, 0444);
+module_param(bluetooth, int, 0444);
+module_param(brightness, int, 0444);
+module_param(threeg, int, 0444);
+module_param(force_series, int, 0444);
+MODULE_PARM_DESC(wireless, "Set initial state of Wireless hardware");
+MODULE_PARM_DESC(bluetooth, "Set initial state of Bluetooth hardware");
+MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
+MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
+MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
+MODULE_PARM_DESC(force_series, "Force a different laptop series");
+
+struct acer_data {
+       int mailled;
+       int wireless;
+       int bluetooth;
+       int threeg;
+       int brightness;
+};
+
+/* Each low-level interface must define at least some of the following */
+struct wmi_interface {
+       /* The WMI device type */
+       u32 type;
+
+       /* The capabilities this interface provides */
+       u32 capability;
+
+       /* Private data for the current interface */
+       struct acer_data data;
+};
+
+/* The static interface pointer, points to the currently detected interface */
+static struct wmi_interface *interface;
+
+/*
+ * Embedded Controller quirks
+ * Some laptops require us to directly access the EC to either enable or query
+ * features that are not available through WMI.
+ */
+
+struct quirk_entry {
+       u8 wireless;
+       u8 mailled;
+       u8 brightness;
+       u8 bluetooth;
+};
+
+static struct quirk_entry *quirks;
+
+static void set_quirks(void)
+{
+       if (quirks->mailled)
+               interface->capability |= ACER_CAP_MAILLED;
+
+       if (quirks->brightness)
+               interface->capability |= ACER_CAP_BRIGHTNESS;
+}
+
+static int dmi_matched(const struct dmi_system_id *dmi)
+{
+       quirks = dmi->driver_data;
+       return 0;
+}
+
+static struct quirk_entry quirk_unknown = {
+};
+
+static struct quirk_entry quirk_acer_travelmate_2490 = {
+       .mailled = 1,
+};
+
+/* This AMW0 laptop has no bluetooth */
+static struct quirk_entry quirk_medion_md_98300 = {
+       .wireless = 1,
+};
+
+static struct dmi_system_id acer_quirks[] = {
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 3100",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 5100",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 5630",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 5650",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 5680",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 9110",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer TravelMate 2490",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Medion MD 98300",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
+               },
+               .driver_data = &quirk_medion_md_98300,
+       },
+       {}
+};
+
+/* Find which quirks are needed for a particular vendor/ model pair */
+static void find_quirks(void)
+{
+       if (!force_series) {
+               dmi_check_system(acer_quirks);
+       } else if (force_series == 2490) {
+               quirks = &quirk_acer_travelmate_2490;
+       }
+
+       if (quirks == NULL)
+               quirks = &quirk_unknown;
+
+       set_quirks();
+}
+
+/*
+ * General interface convenience methods
+ */
+
+static bool has_cap(u32 cap)
+{
+       if ((interface->capability & cap) != 0)
+               return 1;
+
+       return 0;
+}
+
+/*
+ * AMW0 (V1) interface
+ */
+struct wmab_args {
+       u32 eax;
+       u32 ebx;
+       u32 ecx;
+       u32 edx;
+};
+
+struct wmab_ret {
+       u32 eax;
+       u32 ebx;
+       u32 ecx;
+       u32 edx;
+       u32 eex;
+};
+
+static acpi_status wmab_execute(struct wmab_args *regbuf,
+struct acpi_buffer *result)
+{
+       struct acpi_buffer input;
+       acpi_status status;
+       input.length = sizeof(struct wmab_args);
+       input.pointer = (u8 *)regbuf;
+
+       status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
+
+       return status;
+}
+
+static acpi_status AMW0_get_u32(u32 *value, u32 cap,
+struct wmi_interface *iface)
+{
+       int err;
+       u8 result;
+
+       switch (cap) {
+       case ACER_CAP_MAILLED:
+               switch (quirks->mailled) {
+               default:
+                       err = ec_read(0xA, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = (result >> 7) & 0x1;
+                       return AE_OK;
+               }
+               break;
+       case ACER_CAP_WIRELESS:
+               switch (quirks->wireless) {
+               case 1:
+                       err = ec_read(0x7B, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = result & 0x1;
+                       return AE_OK;
+               default:
+                       err = ec_read(0xA, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = (result >> 2) & 0x1;
+                       return AE_OK;
+               }
+               break;
+       case ACER_CAP_BLUETOOTH:
+               switch (quirks->bluetooth) {
+               default:
+                       err = ec_read(0xA, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = (result >> 4) & 0x1;
+                       return AE_OK;
+               }
+               break;
+       case ACER_CAP_BRIGHTNESS:
+               switch (quirks->brightness) {
+               default:
+                       err = ec_read(0x83, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = result;
+                       return AE_OK;
+               }
+               break;
+       default:
+               return AE_BAD_ADDRESS;
+       }
+       return AE_OK;
+}
+
+static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
+{
+       struct wmab_args args;
+
+       args.eax = ACER_AMW0_WRITE;
+       args.ebx = value ? (1<<8) : 0;
+       args.ecx = args.edx = 0;
+
+       switch (cap) {
+       case ACER_CAP_MAILLED:
+               if (value > 1)
+                       return AE_BAD_PARAMETER;
+               args.ebx |= ACER_AMW0_MAILLED_MASK;
+               break;
+       case ACER_CAP_WIRELESS:
+               if (value > 1)
+                       return AE_BAD_PARAMETER;
+               args.ebx |= ACER_AMW0_WIRELESS_MASK;
+               break;
+       case ACER_CAP_BLUETOOTH:
+               if (value > 1)
+                       return AE_BAD_PARAMETER;
+               args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
+               break;
+       case ACER_CAP_BRIGHTNESS:
+               if (value > max_brightness)
+                       return AE_BAD_PARAMETER;
+               switch (quirks->brightness) {
+               case 1:
+                       return ec_write(0x83, value);
+               default:
+                       return AE_BAD_ADDRESS;
+               break;
+               }
+       default:
+               return AE_BAD_ADDRESS;
+       }
+
+       /* Actually do the set */
+       return wmab_execute(&args, NULL);
+}
+
+static acpi_status AMW0_find_mailled(void)
+{
+       struct wmab_args args;
+       struct wmab_ret ret;
+       acpi_status status = AE_OK;
+       struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+
+       args.eax = 0x86;
+       args.ebx = args.ecx = args.edx = 0;
+
+       status = wmab_execute(&args, &out);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = (union acpi_object *) out.pointer;
+       if (obj && obj->type == ACPI_TYPE_BUFFER &&
+       obj->buffer.length == sizeof(struct wmab_ret)) {
+               ret = *((struct wmab_ret *) obj->buffer.pointer);
+       } else {
+               return AE_ERROR;
+       }
+
+       if (ret.eex & 0x1)
+               interface->capability |= ACER_CAP_MAILLED;
+
+       kfree(out.pointer);
+
+       return AE_OK;
+}
+
+static acpi_status AMW0_set_capabilities(void)
+{
+       struct wmab_args args;
+       struct wmab_ret ret;
+       acpi_status status = AE_OK;
+       struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+
+       args.eax = ACER_AMW0_WRITE;
+       args.ecx = args.edx = 0;
+
+       args.ebx = 0xa2 << 8;
+       args.ebx |= ACER_AMW0_WIRELESS_MASK;
+
+       status = wmab_execute(&args, &out);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = (union acpi_object *) out.pointer;
+       if (obj && obj->type == ACPI_TYPE_BUFFER &&
+       obj->buffer.length == sizeof(struct wmab_ret)) {
+               ret = *((struct wmab_ret *) obj->buffer.pointer);
+       } else {
+               return AE_ERROR;
+       }
+
+       if (ret.eax & 0x1)
+               interface->capability |= ACER_CAP_WIRELESS;
+
+       args.ebx = 2 << 8;
+       args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
+
+       status = wmab_execute(&args, &out);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = (union acpi_object *) out.pointer;
+       if (obj && obj->type == ACPI_TYPE_BUFFER
+       && obj->buffer.length == sizeof(struct wmab_ret)) {
+               ret = *((struct wmab_ret *) obj->buffer.pointer);
+       } else {
+               return AE_ERROR;
+       }
+
+       if (ret.eax & 0x1)
+               interface->capability |= ACER_CAP_BLUETOOTH;
+
+       kfree(out.pointer);
+
+       /*
+        * This appears to be safe to enable, since all Wistron based laptops
+        * appear to use the same EC register for brightness, even if they
+        * differ for wireless, etc
+        */
+       interface->capability |= ACER_CAP_BRIGHTNESS;
+
+       return AE_OK;
+}
+
+static struct wmi_interface AMW0_interface = {
+       .type = ACER_AMW0,
+};
+
+static struct wmi_interface AMW0_V2_interface = {
+       .type = ACER_AMW0_V2,
+};
+
+/*
+ * New interface (The WMID interface)
+ */
+static acpi_status
+WMI_execute_u32(u32 method_id, u32 in, u32 *out)
+{
+       struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
+       struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+       u32 tmp;
+       acpi_status status;
+
+       status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
+
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = (union acpi_object *) result.pointer;
+       if (obj && obj->type == ACPI_TYPE_BUFFER &&
+               obj->buffer.length == sizeof(u32)) {
+               tmp = *((u32 *) obj->buffer.pointer);
+       } else {
+               tmp = 0;
+       }
+
+       if (out)
+               *out = tmp;
+
+       kfree(result.pointer);
+
+       return status;
+}
+
+static acpi_status WMID_get_u32(u32 *value, u32 cap,
+struct wmi_interface *iface)
+{
+       acpi_status status;
+       u8 tmp;
+       u32 result, method_id = 0;
+
+       switch (cap) {
+       case ACER_CAP_WIRELESS:
+               method_id = ACER_WMID_GET_WIRELESS_METHODID;
+               break;
+       case ACER_CAP_BLUETOOTH:
+               method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
+               break;
+       case ACER_CAP_BRIGHTNESS:
+               method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
+               break;
+       case ACER_CAP_THREEG:
+               method_id = ACER_WMID_GET_THREEG_METHODID;
+               break;
+       case ACER_CAP_MAILLED:
+               if (quirks->mailled == 1) {
+                       ec_read(0x9f, &tmp);
+                       *value = tmp & 0x1;
+                       return 0;
+               }
+       default:
+               return AE_BAD_ADDRESS;
+       }
+       status = WMI_execute_u32(method_id, 0, &result);
+
+       if (ACPI_SUCCESS(status))
+               *value = (u8)result;
+
+       return status;
+}
+
+static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
+{
+       u32 method_id = 0;
+       char param;
+
<