]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
Merge branch 'audit.b63' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Jun 2009 17:22:57 +0000 (10:22 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Jun 2009 17:22:57 +0000 (10:22 -0700)
* 'audit.b63' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current:
  Fix rule eviction order for AUDIT_DIR
  Audit: clean up all op= output to include string quoting
  Audit: move audit_get_nd completely into audit_watch
  audit: seperate audit inode watches into a subfile
  Audit: clean up audit_receive_skb
  Audit: cleanup netlink mesg handling
  Audit: unify the printk of an skb when auditd not around
  Audit: dereferencing krule as if it were an audit_watch
  Audit: better estimation of execve record length
  Audit: fix audit watch use after free

176 files changed:
Documentation/filesystems/Locking
Documentation/kernel-parameters.txt
Documentation/laptops/thinkpad-acpi.txt
MAINTAINERS
arch/ia64/kernel/acpi-processor.c
arch/x86/include/asm/acpi.h
arch/x86/include/asm/pci_x86.h
arch/x86/kernel/acpi/boot.c
arch/x86/kernel/acpi/cstate.c
arch/x86/kernel/acpi/processor.c
arch/x86/kernel/apic/io_apic.c
arch/x86/pci/mmconfig-shared.c
drivers/acpi/ac.c
drivers/acpi/battery.c
drivers/acpi/blacklist.c
drivers/acpi/bus.c
drivers/acpi/glue.c
drivers/acpi/osl.c
drivers/acpi/pci_bind.c
drivers/acpi/pci_irq.c
drivers/acpi/pci_root.c
drivers/acpi/power.c
drivers/acpi/processor_core.c
drivers/acpi/processor_idle.c
drivers/acpi/scan.c
drivers/acpi/video.c
drivers/acpi/video_detect.c
drivers/char/mxser.c
drivers/char/nozomi.c
drivers/char/synclink_gt.c
drivers/char/tty_port.c
drivers/gpu/drm/i915/i915_opregion.c
drivers/i2c/busses/Kconfig
drivers/i2c/busses/Makefile
drivers/i2c/busses/i2c-designware.c [new file with mode: 0644]
drivers/net/Kconfig
drivers/net/bnx2.c
drivers/net/can/Kconfig
drivers/net/netxen/netxen_nic_init.c
drivers/net/netxen/netxen_nic_main.c
drivers/net/qla3xxx.c
drivers/pci/hotplug/acpi_pcihp.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/intel-iommu.c
drivers/pci/intr_remapping.c
drivers/pci/intr_remapping.h
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/acerhdf.c [new file with mode: 0644]
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus_acpi.c
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/thinkpad_acpi.c
drivers/pnp/pnpacpi/rsparser.c
drivers/serial/8250_pci.c
drivers/serial/icom.c
drivers/serial/jsm/jsm_tty.c
drivers/serial/serial_txx9.c
fs/btrfs/acl.c
fs/btrfs/btrfs_inode.h
fs/btrfs/ctree.h
fs/btrfs/inode.c
fs/compat_ioctl.c
fs/devpts/inode.c
fs/ext2/acl.c
fs/ext2/acl.h
fs/ext2/ext2.h
fs/ext2/inode.c
fs/ext2/super.c
fs/ext3/acl.c
fs/ext3/acl.h
fs/ext3/inode.c
fs/ext3/super.c
fs/ext4/acl.c
fs/ext4/acl.h
fs/ext4/ext4.h
fs/ext4/inode.c
fs/ext4/super.c
fs/fs-writeback.c
fs/inode.c
fs/ioctl.c
fs/jffs2/acl.c
fs/jffs2/acl.h
fs/jffs2/jffs2_fs_i.h
fs/jffs2/os-linux.h
fs/jffs2/readinode.c
fs/jfs/acl.c
fs/jfs/jfs_incore.h
fs/jfs/super.c
fs/jfs/xattr.c
fs/namei.c
fs/namespace.c
fs/nilfs2/inode.c
fs/nilfs2/nilfs.h
fs/nilfs2/super.c
fs/ocfs2/dlmglue.c
fs/ocfs2/dlmglue.h
fs/ocfs2/file.c
fs/ocfs2/inode.c
fs/ocfs2/journal.c
fs/ocfs2/journal.h
fs/ocfs2/namei.c
fs/ocfs2/ocfs2.h
fs/ocfs2/stack_o2cb.c
fs/ocfs2/stack_user.c
fs/ocfs2/stackglue.c
fs/ocfs2/stackglue.h
fs/ocfs2/suballoc.c
fs/ocfs2/super.c
fs/ocfs2/sysfile.c
fs/open.c
fs/reiserfs/inode.c
fs/reiserfs/resize.c
fs/reiserfs/super.c
fs/reiserfs/xattr_acl.c
fs/super.c
fs/ubifs/xattr.c
fs/udf/balloc.c
fs/udf/lowlevel.c
fs/xfs/linux-2.6/xfs_acl.c
fs/xfs/xfs_acl.h
fs/xfs/xfs_iget.c
fs/xfs/xfs_inode.h
include/acpi/acpi_bus.h
include/acpi/acpi_drivers.h
include/acpi/processor.h
include/acpi/video.h
include/linux/acpi.h
include/linux/dmar.h
include/linux/ext3_fs_i.h
include/linux/falloc.h
include/linux/fs.h
include/linux/icmpv6.h
include/linux/lockdep.h
include/linux/pci_hotplug.h
include/linux/posix_acl.h
include/linux/reiserfs_acl.h
include/linux/reiserfs_fs_i.h
include/linux/shmem_fs.h
include/net/protocol.h
include/net/rawv6.h
include/net/sctp/sctp.h
include/net/sock.h
include/net/xfrm.h
init/main.c
mm/shmem.c
mm/shmem_acl.c
net/ax25/ax25_in.c
net/core/dev.c
net/dccp/ipv6.c
net/ipv4/route.c
net/ipv6/ah6.c
net/ipv6/esp6.c
net/ipv6/icmp.c
net/ipv6/ip6_tunnel.c
net/ipv6/ipcomp6.c
net/ipv6/mip6.c
net/ipv6/raw.c
net/ipv6/route.c
net/ipv6/tcp_ipv6.c
net/ipv6/tunnel6.c
net/ipv6/udp.c
net/ipv6/udp_impl.h
net/ipv6/udplite.c
net/ipv6/xfrm6_tunnel.c
net/irda/af_irda.c
net/irda/ircomm/ircomm_lmp.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_log.c
net/netfilter/xt_NFQUEUE.c
net/netfilter/xt_cluster.c
net/netfilter/xt_quota.c
net/netfilter/xt_rateest.c
net/sctp/ipv6.c

index 229d7b7c50a350053170e5fba11ac9a0168c0e70..18b9d0ca0630e281bb20dc5c5991e4b6f06b4643 100644 (file)
@@ -109,27 +109,28 @@ prototypes:
 
 locking rules:
        All may block.
-                       BKL     s_lock  s_umount
-alloc_inode:           no      no      no
-destroy_inode:         no
-dirty_inode:           no                              (must not sleep)
-write_inode:           no
-drop_inode:            no                              !!!inode_lock!!!
-delete_inode:          no
-put_super:             yes     yes     no
-write_super:           no      yes     read
-sync_fs:               no      no      read
-freeze_fs:             ?
-unfreeze_fs:           ?
-statfs:                        no      no      no
-remount_fs:            yes     yes     maybe           (see below)
-clear_inode:           no
-umount_begin:          yes     no      no
-show_options:          no                              (vfsmount->sem)
-quota_read:            no      no      no              (see below)
-quota_write:           no      no      no              (see below)
-
-->remount_fs() will have the s_umount lock if it's already mounted.
+       None have BKL
+                       s_umount
+alloc_inode:
+destroy_inode:
+dirty_inode:                           (must not sleep)
+write_inode:
+drop_inode:                            !!!inode_lock!!!
+delete_inode:
+put_super:             write
+write_super:           read
+sync_fs:               read
+freeze_fs:             read
+unfreeze_fs:           read
+statfs:                        no
+remount_fs:            maybe           (see below)
+clear_inode:
+umount_begin:          no
+show_options:          no              (namespace_sem)
+quota_read:            no              (see below)
+quota_write:           no              (see below)
+
+->remount_fs() will have the s_umount exclusive lock if it's already mounted.
 When called from get_sb_single, it does NOT have the s_umount lock.
 ->quota_read() and ->quota_write() functions are both guaranteed to
 be the only ones operating on the quota file by the quota code (via
index 92e1ab8178a8693856c599dcc80c0e4f77a78390..040fee60728276610e54c0596b7127a2b11e81b8 100644 (file)
@@ -229,14 +229,6 @@ and is between 256 and 4096 characters. It is defined in the file
                        to assume that this machine's pmtimer latches its value
                        and always returns good values.
 
-       acpi.power_nocheck=     [HW,ACPI]
-                       Format: 1/0 enable/disable the check of power state.
-                       On some bogus BIOS the _PSC object/_STA object of
-                       power resource can't return the correct device power
-                       state. In such case it is unneccessary to check its
-                       power state again in power transition.
-                       1 : disable the power state check
-
        acpi_sci=       [HW,ACPI] ACPI System Control Interrupt trigger mode
                        Format: { level | edge | high | low }
 
index 78e354b42f6785cd07a0e12b56b482fde4b8d10b..f2296ecedb89d41e6afec1d3ea239256cb4fe768 100644 (file)
@@ -920,7 +920,7 @@ The available commands are:
        echo '<LED number> off' >/proc/acpi/ibm/led
        echo '<LED number> blink' >/proc/acpi/ibm/led
 
-The <LED number> range is 0 to 7. The set of LEDs that can be
+The <LED number> range is 0 to 15. The set of LEDs that can be
 controlled varies from model to model. Here is the common ThinkPad
 mapping:
 
@@ -932,6 +932,11 @@ mapping:
        5 - UltraBase battery slot
        6 - (unknown)
        7 - standby
+       8 - dock status 1
+       9 - dock status 2
+       10, 11 - (unknown)
+       12 - thinkvantage
+       13, 14, 15 - (unknown)
 
 All of the above can be turned on and off and can be made to blink.
 
@@ -940,10 +945,12 @@ sysfs notes:
 The ThinkPad LED sysfs interface is described in detail by the LED class
 documentation, in Documentation/leds-class.txt.
 
-The leds are named (in LED ID order, from 0 to 7):
+The LEDs are named (in LED ID order, from 0 to 12):
 "tpacpi::power", "tpacpi:orange:batt", "tpacpi:green:batt",
 "tpacpi::dock_active", "tpacpi::bay_active", "tpacpi::dock_batt",
-"tpacpi::unknown_led", "tpacpi::standby".
+"tpacpi::unknown_led", "tpacpi::standby", "tpacpi::dock_status1",
+"tpacpi::dock_status2", "tpacpi::unknown_led2", "tpacpi::unknown_led3",
+"tpacpi::thinkvantage".
 
 Due to limitations in the sysfs LED class, if the status of the LED
 indicators cannot be read due to an error, thinkpad-acpi will report it as
@@ -958,6 +965,12 @@ ThinkPad indicator LED should blink in hardware accelerated mode, use the
 "timer" trigger, and leave the delay_on and delay_off parameters set to
 zero (to request hardware acceleration autodetection).
 
+LEDs that are known not to exist in a given ThinkPad model are not
+made available through the sysfs interface.  If you have a dock and you
+notice there are LEDs listed for your ThinkPad that do not exist (and
+are not in the dock), or if you notice that there are missing LEDs,
+a report to ibm-acpi-devel@lists.sourceforge.net is appreciated.
+
 
 ACPI sounds -- /proc/acpi/ibm/beep
 ----------------------------------
@@ -1156,17 +1169,19 @@ may not be distinct.  Later Lenovo models that implement the ACPI
 display backlight brightness control methods have 16 levels, ranging
 from 0 to 15.
 
-There are two interfaces to the firmware for direct brightness control,
-EC and UCMS (or CMOS).  To select which one should be used, use the
-brightness_mode module parameter: brightness_mode=1 selects EC mode,
-brightness_mode=2 selects UCMS mode, brightness_mode=3 selects EC
-mode with NVRAM backing (so that brightness changes are remembered
-across shutdown/reboot).
+For IBM ThinkPads, there are two interfaces to the firmware for direct
+brightness control, EC and UCMS (or CMOS).  To select which one should be
+used, use the brightness_mode module parameter: brightness_mode=1 selects
+EC mode, brightness_mode=2 selects UCMS mode, brightness_mode=3 selects EC
+mode with NVRAM backing (so that brightness changes are remembered across
+shutdown/reboot).
 
 The driver tries to select which interface to use from a table of
 defaults for each ThinkPad model.  If it makes a wrong choice, please
 report this as a bug, so that we can fix it.
 
+Lenovo ThinkPads only support brightness_mode=2 (UCMS).
+
 When display backlight brightness controls are available through the
 standard ACPI interface, it is best to use it instead of this direct
 ThinkPad-specific interface.  The driver will disable its native
@@ -1254,7 +1269,7 @@ Fan control and monitoring: fan speed, fan enable/disable
 
 procfs: /proc/acpi/ibm/fan
 sysfs device attributes: (hwmon "thinkpad") fan1_input, pwm1,
-                         pwm1_enable
+                         pwm1_enable, fan2_input
 sysfs hwmon driver attributes: fan_watchdog
 
 NOTE NOTE NOTE: fan control operations are disabled by default for
@@ -1267,6 +1282,9 @@ from the hardware registers of the embedded controller.  This is known
 to work on later R, T, X and Z series ThinkPads but may show a bogus
 value on other models.
 
+Some Lenovo ThinkPads support a secondary fan.  This fan cannot be
+controlled separately, it shares the main fan control.
+
 Fan levels:
 
 Most ThinkPad fans work in "levels" at the firmware interface.  Level 0
@@ -1397,6 +1415,11 @@ hwmon device attribute fan1_input:
        which can take up to two minutes.  May return rubbish on older
        ThinkPads.
 
+hwmon device attribute fan2_input:
+       Fan tachometer reading, in RPM, for the secondary fan.
+       Available only on some ThinkPads.  If the secondary fan is
+       not installed, will always read 0.
+
 hwmon driver attribute fan_watchdog:
        Fan safety watchdog timer interval, in seconds.  Minimum is
        1 second, maximum is 120 seconds.  0 disables the watchdog.
@@ -1555,3 +1578,7 @@ Sysfs interface changelog:
 0x020300:      hotkey enable/disable support removed, attributes
                hotkey_bios_enabled and hotkey_enable deprecated and
                marked for removal.
+
+0x020400:      Marker for 16 LEDs support.  Also, LEDs that are known
+               to not exist in a given model are not registered with
+               the LED sysfs class anymore.
index 303129ab4b7509bff0ed0ce3960d5c6b7bc53ff9..fa2a16def17afb765ad447f10b5dde634f83ded9 100644 (file)
@@ -230,6 +230,13 @@ L: linux-acenic@sunsite.dk
 S:     Maintained
 F:     drivers/net/acenic*
 
+ACER ASPIRE ONE TEMPERATURE AND FAN DRIVER
+P: Peter Feuerer
+M: peter@piie.net
+W: http://piie.net/?section=acerhdf
+S: Maintained
+F: drivers/platform/x86/acerhdf.c
+
 ACER WMI LAPTOP EXTRAS
 P:     Carlos Corbacho
 M:     carlos@strangeworlds.co.uk
@@ -913,8 +920,7 @@ M:  corentincj@iksaif.net
 P:     Karol Kozimor
 M:     sziwan@users.sourceforge.net
 L:     acpi4asus-user@lists.sourceforge.net
-W:     http://sourceforge.net/projects/acpi4asus
-W:     http://xf.iksaif.net/acpi4asus
+W:     http://acpi4asus.sf.net
 S:     Maintained
 F:     arch/x86/kernel/acpi/boot.c
 F:     drivers/platform/x86/asus_acpi.c
@@ -930,8 +936,7 @@ ASUS LAPTOP EXTRAS DRIVER
 P:     Corentin Chary
 M:     corentincj@iksaif.net
 L:     acpi4asus-user@lists.sourceforge.net
-W:     http://sourceforge.net/projects/acpi4asus
-W:     http://xf.iksaif.net/acpi4asus
+W:     http://acpi4asus.sf.net
 S:     Maintained
 F:     drivers/platform/x86/asus-laptop.c
 
@@ -1636,7 +1641,7 @@ P:        Mikael Starvik
 M:     starvik@axis.com
 P:     Jesper Nilsson
 M:     jesper.nilsson@axis.com
-L:     dev-etrax@axis.com
+L:     linux-cris-kernel@axis.com
 W:     http://developer.axis.com
 S:     Maintained
 F:     arch/cris/
@@ -2110,7 +2115,7 @@ EEEPC LAPTOP EXTRAS DRIVER
 P:     Corentin Chary
 M:     corentincj@iksaif.net
 L:     acpi4asus-user@lists.sourceforge.net
-W:     http://sourceforge.net/projects/acpi4asus
+W:     http://acpi4asus.sf.net
 S:     Maintained
 F:     drivers/platform/x86/eeepc-laptop.c
 
index cbe6cee5a550327500d5497d6c445eb471714a12..dbda7bde61129d3403b189f5145618eae9f655e0 100644 (file)
@@ -71,3 +71,15 @@ void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
 }
 
 EXPORT_SYMBOL(arch_acpi_processor_init_pdc);
+
+void arch_acpi_processor_cleanup_pdc(struct acpi_processor *pr)
+{
+       if (pr->pdc) {
+               kfree(pr->pdc->pointer->buffer.pointer);
+               kfree(pr->pdc->pointer);
+               kfree(pr->pdc);
+               pr->pdc = NULL;
+       }
+}
+
+EXPORT_SYMBOL(arch_acpi_processor_cleanup_pdc);
index 4518dc50090380b6676074b3f2466709ddea5a4b..20d1465a2ab046d8956eec43d5f5d4d36a3bd6bb 100644 (file)
@@ -144,6 +144,7 @@ static inline unsigned int acpi_processor_cstate_check(unsigned int max_cstate)
 
 #else /* !CONFIG_ACPI */
 
+#define acpi_disabled 1
 #define acpi_lapic 0
 #define acpi_ioapic 0
 static inline void acpi_noirq_set(void) { }
index cb739cc0a08063729fb239d4d6a254f6aee08625..d419f5c02669467f07cfa5c2b8392f8186e1b716 100644 (file)
@@ -121,6 +121,9 @@ extern int __init pcibios_init(void);
 extern int __init pci_mmcfg_arch_init(void);
 extern void __init pci_mmcfg_arch_free(void);
 
+extern struct acpi_mcfg_allocation *pci_mmcfg_config;
+extern int pci_mmcfg_config_num;
+
 /*
  * AMD Fam10h CPUs are buggy, and cannot access MMIO config space
  * on their northbrige except through the * %eax register. As such, you MUST
index 631086159c53b0be5f28df92d3e2ed976cffa7a8..6b8ca3a0285d77512219724750bca449fa3a8a67 100644 (file)
 
 static int __initdata acpi_force = 0;
 u32 acpi_rsdt_forced;
-#ifdef CONFIG_ACPI
-int acpi_disabled = 0;
-#else
-int acpi_disabled = 1;
-#endif
+int acpi_disabled;
 EXPORT_SYMBOL(acpi_disabled);
 
 #ifdef CONFIG_X86_64
@@ -122,72 +118,6 @@ void __init __acpi_unmap_table(char *map, unsigned long size)
        early_iounmap(map, size);
 }
 
-#ifdef CONFIG_PCI_MMCONFIG
-
-static int acpi_mcfg_64bit_base_addr __initdata = FALSE;
-
-/* The physical address of the MMCONFIG aperture.  Set from ACPI tables. */
-struct acpi_mcfg_allocation *pci_mmcfg_config;
-int pci_mmcfg_config_num;
-
-static int __init acpi_mcfg_oem_check(struct acpi_table_mcfg *mcfg)
-{
-       if (!strcmp(mcfg->header.oem_id, "SGI"))
-               acpi_mcfg_64bit_base_addr = TRUE;
-
-       return 0;
-}
-
-int __init acpi_parse_mcfg(struct acpi_table_header *header)
-{
-       struct acpi_table_mcfg *mcfg;
-       unsigned long i;
-       int config_size;
-
-       if (!header)
-               return -EINVAL;
-
-       mcfg = (struct acpi_table_mcfg *)header;
-
-       /* how many config structures do we have */
-       pci_mmcfg_config_num = 0;
-       i = header->length - sizeof(struct acpi_table_mcfg);
-       while (i >= sizeof(struct acpi_mcfg_allocation)) {
-               ++pci_mmcfg_config_num;
-               i -= sizeof(struct acpi_mcfg_allocation);
-       };
-       if (pci_mmcfg_config_num == 0) {
-               printk(KERN_ERR PREFIX "MMCONFIG has no entries\n");
-               return -ENODEV;
-       }
-
-       config_size = pci_mmcfg_config_num * sizeof(*pci_mmcfg_config);
-       pci_mmcfg_config = kmalloc(config_size, GFP_KERNEL);
-       if (!pci_mmcfg_config) {
-               printk(KERN_WARNING PREFIX
-                      "No memory for MCFG config tables\n");
-               return -ENOMEM;
-       }
-
-       memcpy(pci_mmcfg_config, &mcfg[1], config_size);
-
-       acpi_mcfg_oem_check(mcfg);
-
-       for (i = 0; i < pci_mmcfg_config_num; ++i) {
-               if ((pci_mmcfg_config[i].address > 0xFFFFFFFF) &&
-                   !acpi_mcfg_64bit_base_addr) {
-                       printk(KERN_ERR PREFIX
-                              "MMCONFIG not in low 4GB of memory\n");
-                       kfree(pci_mmcfg_config);
-                       pci_mmcfg_config_num = 0;
-                       return -ENODEV;
-               }
-       }
-
-       return 0;
-}
-#endif                         /* CONFIG_PCI_MMCONFIG */
-
 #ifdef CONFIG_X86_LOCAL_APIC
 static int __init acpi_parse_madt(struct acpi_table_header *table)
 {
@@ -1517,14 +1447,6 @@ static struct dmi_system_id __initdata acpi_dmi_table[] = {
                     DMI_MATCH(DMI_PRODUCT_NAME, "Workstation W8000"),
                     },
         },
-       {
-        .callback = force_acpi_ht,
-        .ident = "ASUS P4B266",
-        .matches = {
-                    DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
-                    DMI_MATCH(DMI_BOARD_NAME, "P4B266"),
-                    },
-        },
        {
         .callback = force_acpi_ht,
         .ident = "ASUS P2B-DS",
index bbbe4bbb6f34b7ed8bf60cf849faa32963e93d90..8c44c232efcb9f2b1150b4f58a0942bf9dd3ac0a 100644 (file)
@@ -34,12 +34,22 @@ void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags,
                flags->bm_check = 1;
        else if (c->x86_vendor == X86_VENDOR_INTEL) {
                /*
-                * Today all CPUs that support C3 share cache.
-                * TBD: This needs to look at cache shared map, once
-                * multi-core detection patch makes to the base.
+                * Today all MP CPUs that support C3 share cache.
+                * And caches should not be flushed by software while
+                * entering C3 type state.
                 */
                flags->bm_check = 1;
        }
+
+       /*
+        * On all recent Intel platforms, ARB_DISABLE is a nop.
+        * So, set bm_control to zero to indicate that ARB_DISABLE
+        * is not required while entering C3 type state on
+        * P4, Core and beyond CPUs
+        */
+       if (c->x86_vendor == X86_VENDOR_INTEL &&
+           (c->x86 > 0x6 || (c->x86 == 6 && c->x86_model >= 14)))
+                       flags->bm_control = 0;
 }
 EXPORT_SYMBOL(acpi_processor_power_init_bm_check);
 
index 7c074eec39fb56ebff16c26f3351b6eae79cca50..d296f4a195c916d8d454c144396ecc1f71af91d2 100644 (file)
@@ -72,6 +72,7 @@ static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c)
        return;
 }
 
+
 /* Initialize _PDC data based on the CPU vendor */
 void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
 {
@@ -85,3 +86,15 @@ void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
 }
 
 EXPORT_SYMBOL(arch_acpi_processor_init_pdc);
+
+void arch_acpi_processor_cleanup_pdc(struct acpi_processor *pr)
+{
+       if (pr->pdc) {
+               kfree(pr->pdc->pointer->buffer.pointer);
+               kfree(pr->pdc->pointer);
+               kfree(pr->pdc);
+               pr->pdc = NULL;
+       }
+}
+
+EXPORT_SYMBOL(arch_acpi_processor_cleanup_pdc);
index b7a79207295ee05e0f4ecb34e3863d96e330cd1d..4d0216fcb36c4356d4f135fee9c585fac7f42c3a 100644 (file)
@@ -1414,6 +1414,9 @@ int setup_ioapic_entry(int apic_id, int irq,
                irte.vector = vector;
                irte.dest_id = IRTE_DEST(destination);
 
+               /* Set source-id of interrupt request */
+               set_ioapic_sid(&irte, apic_id);
+
                modify_irte(irq, &irte);
 
                ir_entry->index2 = (index >> 15) & 0x1;
@@ -3290,6 +3293,9 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms
                irte.vector = cfg->vector;
                irte.dest_id = IRTE_DEST(dest);
 
+               /* Set source-id of interrupt request */
+               set_msi_sid(&irte, pdev);
+
                modify_irte(irq, &irte);
 
                msg->address_hi = MSI_ADDR_BASE_HI;
index 8766b0e216c5f1a139a7094630933670dd3f1cc4..712443ec6d43b46aaf05d75e04f2c249f44d20da 100644 (file)
@@ -523,6 +523,69 @@ reject:
 
 static int __initdata known_bridge;
 
+static int acpi_mcfg_64bit_base_addr __initdata = FALSE;
+
+/* The physical address of the MMCONFIG aperture.  Set from ACPI tables. */
+struct acpi_mcfg_allocation *pci_mmcfg_config;
+int pci_mmcfg_config_num;
+
+static int __init acpi_mcfg_oem_check(struct acpi_table_mcfg *mcfg)
+{
+       if (!strcmp(mcfg->header.oem_id, "SGI"))
+               acpi_mcfg_64bit_base_addr = TRUE;
+
+       return 0;
+}
+
+static int __init pci_parse_mcfg(struct acpi_table_header *header)
+{
+       struct acpi_table_mcfg *mcfg;
+       unsigned long i;
+       int config_size;
+
+       if (!header)
+               return -EINVAL;
+
+       mcfg = (struct acpi_table_mcfg *)header;
+
+       /* how many config structures do we have */
+       pci_mmcfg_config_num = 0;
+       i = header->length - sizeof(struct acpi_table_mcfg);
+       while (i >= sizeof(struct acpi_mcfg_allocation)) {
+               ++pci_mmcfg_config_num;
+               i -= sizeof(struct acpi_mcfg_allocation);
+       };
+       if (pci_mmcfg_config_num == 0) {
+               printk(KERN_ERR PREFIX "MMCONFIG has no entries\n");
+               return -ENODEV;
+       }
+
+       config_size = pci_mmcfg_config_num * sizeof(*pci_mmcfg_config);
+       pci_mmcfg_config = kmalloc(config_size, GFP_KERNEL);
+       if (!pci_mmcfg_config) {
+               printk(KERN_WARNING PREFIX
+                      "No memory for MCFG config tables\n");
+               return -ENOMEM;
+       }
+
+       memcpy(pci_mmcfg_config, &mcfg[1], config_size);
+
+       acpi_mcfg_oem_check(mcfg);
+
+       for (i = 0; i < pci_mmcfg_config_num; ++i) {
+               if ((pci_mmcfg_config[i].address > 0xFFFFFFFF) &&
+                   !acpi_mcfg_64bit_base_addr) {
+                       printk(KERN_ERR PREFIX
+                              "MMCONFIG not in low 4GB of memory\n");
+                       kfree(pci_mmcfg_config);
+                       pci_mmcfg_config_num = 0;
+                       return -ENODEV;
+               }
+       }
+
+       return 0;
+}
+
 static void __init __pci_mmcfg_init(int early)
 {
        /* MMCONFIG disabled */
@@ -543,7 +606,7 @@ static void __init __pci_mmcfg_init(int early)
        }
 
        if (!known_bridge)
-               acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
+               acpi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
 
        pci_mmcfg_reject_broken(early);
 
index 88e42abf5d881b8bf443ecc4f58e3b4e76b3ecd3..0df8fcb687d6f00ce400c1b4677019a34af236a1 100644 (file)
@@ -61,6 +61,7 @@ static int acpi_ac_open_fs(struct inode *inode, struct file *file);
 static int acpi_ac_add(struct acpi_device *device);
 static int acpi_ac_remove(struct acpi_device *device, int type);
 static int acpi_ac_resume(struct acpi_device *device);
+static void acpi_ac_notify(struct acpi_device *device, u32 event);
 
 static const struct acpi_device_id ac_device_ids[] = {
        {"ACPI0003", 0},
@@ -72,10 +73,12 @@ static struct acpi_driver acpi_ac_driver = {
        .name = "ac",
        .class = ACPI_AC_CLASS,
        .ids = ac_device_ids,
+       .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
        .ops = {
                .add = acpi_ac_add,
                .remove = acpi_ac_remove,
                .resume = acpi_ac_resume,
+               .notify = acpi_ac_notify,
                },
 };
 
@@ -220,16 +223,14 @@ static int acpi_ac_remove_fs(struct acpi_device *device)
                                    Driver Model
    -------------------------------------------------------------------------- */
 
-static void acpi_ac_notify(acpi_handle handle, u32 event, void *data)
+static void acpi_ac_notify(struct acpi_device *device, u32 event)
 {
-       struct acpi_ac *ac = data;
-       struct acpi_device *device = NULL;
+       struct acpi_ac *ac = acpi_driver_data(device);
 
 
        if (!ac)
                return;
 
-       device = ac->device;
        switch (event) {
        default:
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
@@ -253,7 +254,6 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data)
 static int acpi_ac_add(struct acpi_device *device)
 {
        int result = 0;
-       acpi_status status = AE_OK;
        struct acpi_ac *ac = NULL;
 
 
@@ -286,13 +286,6 @@ static int acpi_ac_add(struct acpi_device *device)
        ac->charger.get_property = get_ac_property;
        power_supply_register(&ac->device->dev, &ac->charger);
 #endif
-       status = acpi_install_notify_handler(device->handle,
-                                            ACPI_ALL_NOTIFY, acpi_ac_notify,
-                                            ac);
-       if (ACPI_FAILURE(status)) {
-               result = -ENODEV;
-               goto end;
-       }
 
        printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
               acpi_device_name(device), acpi_device_bid(device),
@@ -328,7 +321,6 @@ static int acpi_ac_resume(struct acpi_device *device)
 
 static int acpi_ac_remove(struct acpi_device *device, int type)
 {
-       acpi_status status = AE_OK;
        struct acpi_ac *ac = NULL;
 
 
@@ -337,8 +329,6 @@ static int acpi_ac_remove(struct acpi_device *device, int type)
 
        ac = acpi_driver_data(device);
 
-       status = acpi_remove_notify_handler(device->handle,
-                                           ACPI_ALL_NOTIFY, acpi_ac_notify);
 #ifdef CONFIG_ACPI_SYSFS_POWER
        if (ac->charger.dev)
                power_supply_unregister(&ac->charger);
index b0de6312919a82a455f20f8d06dddd8f1b672d80..58b4517ce71277b2d7c533d0cc43b0052a58f975 100644 (file)
@@ -796,13 +796,12 @@ static void acpi_battery_remove_fs(struct acpi_device *device)
                                  Driver Interface
    -------------------------------------------------------------------------- */
 
-static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
+static void acpi_battery_notify(struct acpi_device *device, u32 event)
 {
-       struct acpi_battery *battery = data;
-       struct acpi_device *device;
+       struct acpi_battery *battery = acpi_driver_data(device);
+
        if (!battery)
                return;
-       device = battery->device;
        acpi_battery_update(battery);
        acpi_bus_generate_proc_event(device, event,
                                     acpi_battery_present(battery));
@@ -819,7 +818,6 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
 static int acpi_battery_add(struct acpi_device *device)
 {
        int result = 0;
-       acpi_status status = 0;
        struct acpi_battery *battery = NULL;
        if (!device)
                return -EINVAL;
@@ -834,22 +832,12 @@ static int acpi_battery_add(struct acpi_device *device)
        acpi_battery_update(battery);
 #ifdef CONFIG_ACPI_PROCFS_POWER
        result = acpi_battery_add_fs(device);
-       if (result)
-               goto end;
 #endif
-       status = acpi_install_notify_handler(device->handle,
-                                            ACPI_ALL_NOTIFY,
-                                            acpi_battery_notify, battery);
-       if (ACPI_FAILURE(status)) {
-               ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler"));
-               result = -ENODEV;
-               goto end;
-       }
-       printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
-              ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
-              device->status.battery_present ? "present" : "absent");
-      end:
-       if (result) {
+       if (!result) {
+               printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
+                       ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
+                       device->status.battery_present ? "present" : "absent");
+       } else {
 #ifdef CONFIG_ACPI_PROCFS_POWER
                acpi_battery_remove_fs(device);
 #endif
@@ -860,15 +848,11 @@ static int acpi_battery_add(struct acpi_device *device)
 
 static int acpi_battery_remove(struct acpi_device *device, int type)
 {
-       acpi_status status = 0;
        struct acpi_battery *battery = NULL;
 
        if (!device || !acpi_driver_data(device))
                return -EINVAL;
        battery = acpi_driver_data(device);
-       status = acpi_remove_notify_handler(device->handle,
-                                           ACPI_ALL_NOTIFY,
-                                           acpi_battery_notify);
 #ifdef CONFIG_ACPI_PROCFS_POWER
        acpi_battery_remove_fs(device);
 #endif
@@ -896,10 +880,12 @@ static struct acpi_driver acpi_battery_driver = {
        .name = "battery",
        .class = ACPI_BATTERY_CLASS,
        .ids = battery_device_ids,
+       .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
        .ops = {
                .add = acpi_battery_add,
                .resume = acpi_battery_resume,
                .remove = acpi_battery_remove,
+               .notify = acpi_battery_notify,
                },
 };
 
index 09c69806c1fc7693f91268ce86d0a8030dc2df59..f6baa77deefbd6d0743e92fc4a593b66a93c64c5 100644 (file)
@@ -192,6 +192,22 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
                     DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
                },
        },
+       {
+       .callback = dmi_disable_osi_vista,
+       .ident = "Sony VGN-NS10J_S",
+       .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"),
+               },
+       },
+       {
+       .callback = dmi_disable_osi_vista,
+       .ident = "Sony VGN-SR290J",
+       .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "Sony VGN-SR290J"),
+               },
+       },
 
        /*
         * BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
index ae862f1798dc9d59d1b1e7479a6252dec22a185c..2876fc70c3a9b71915ef016f1ade1fa235c8db3f 100644 (file)
@@ -450,18 +450,16 @@ int acpi_bus_receive_event(struct acpi_bus_event *event)
                              Notification Handling
    -------------------------------------------------------------------------- */
 
-static int
-acpi_bus_check_device(struct acpi_device *device, int *status_changed)
+static void acpi_bus_check_device(acpi_handle handle)
 {
-       acpi_status status = 0;
+       struct acpi_device *device;
+       acpi_status status;
        struct acpi_device_status old_status;
 
-
+       if (acpi_bus_get_device(handle, &device))
+               return;
        if (!device)
-               return -EINVAL;
-
-       if (status_changed)
-               *status_changed = 0;
+               return;
 
        old_status = device->status;
 
@@ -471,22 +469,15 @@ acpi_bus_check_device(struct acpi_device *device, int *status_changed)
         */
        if (device->parent && !device->parent->status.present) {
                device->status = device->parent->status;
-               if (STRUCT_TO_INT(old_status) != STRUCT_TO_INT(device->status)) {
-                       if (status_changed)
-                               *status_changed = 1;
-               }
-               return 0;
+               return;
        }
 
        status = acpi_bus_get_status(device);
        if (ACPI_FAILURE(status))
-               return -ENODEV;
+               return;
 
        if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status))
-               return 0;
-
-       if (status_changed)
-               *status_changed = 1;
+               return;
 
        /*
         * Device Insertion/Removal
@@ -498,33 +489,17 @@ acpi_bus_check_device(struct acpi_device *device, int *status_changed)
                ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n"));
                /* TBD: Handle device removal */
        }
-
-       return 0;
 }
 
-static int acpi_bus_check_scope(struct acpi_device *device)
+static void acpi_bus_check_scope(acpi_handle handle)
 {
-       int result = 0;
-       int status_changed = 0;
-
-
-       if (!device)
-               return -EINVAL;
-
        /* Status Change? */
-       result = acpi_bus_check_device(device, &status_changed);
-       if (result)
-               return result;
-
-       if (!status_changed)
-               return 0;
+       acpi_bus_check_device(handle);
 
        /*
         * TBD: Enumerate child devices within this device's scope and
         *       run acpi_bus_check_device()'s on them.
         */
-
-       return 0;
 }
 
 static BLOCKING_NOTIFIER_HEAD(acpi_bus_notify_list);
@@ -547,22 +522,19 @@ EXPORT_SYMBOL_GPL(unregister_acpi_bus_notifier);
  */
 static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
 {
-       int result = 0;
        struct acpi_device *device = NULL;
+       struct acpi_driver *driver;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n",
+                         type, handle));
 
        blocking_notifier_call_chain(&acpi_bus_notify_list,
                type, (void *)handle);
 
-       if (acpi_bus_get_device(handle, &device))
-               return;
-
        switch (type) {
 
        case ACPI_NOTIFY_BUS_CHECK:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received BUS CHECK notification for device [%s]\n",
-                                 device->pnp.bus_id));
-               result = acpi_bus_check_scope(device);
+               acpi_bus_check_scope(handle);
                /*
                 * TBD: We'll need to outsource certain events to non-ACPI
                 *      drivers via the device manager (device.c).
@@ -570,10 +542,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
                break;
 
        case ACPI_NOTIFY_DEVICE_CHECK:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received DEVICE CHECK notification for device [%s]\n",
-                                 device->pnp.bus_id));
-               result = acpi_bus_check_device(device, NULL);
+               acpi_bus_check_device(handle);
                /*
                 * TBD: We'll need to outsource certain events to non-ACPI
                 *      drivers via the device manager (device.c).
@@ -581,44 +550,26 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
                break;
 
        case ACPI_NOTIFY_DEVICE_WAKE:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received DEVICE WAKE notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD */
                break;
 
        case ACPI_NOTIFY_EJECT_REQUEST:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received EJECT REQUEST notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD */
                break;
 
        case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received DEVICE CHECK LIGHT notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD: Exactly what does 'light' mean? */
                break;
 
        case ACPI_NOTIFY_FREQUENCY_MISMATCH:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received FREQUENCY MISMATCH notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD */
                break;
 
        case ACPI_NOTIFY_BUS_MODE_MISMATCH:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received BUS MODE MISMATCH notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD */
                break;
 
        case ACPI_NOTIFY_POWER_FAULT:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received POWER FAULT notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD */
                break;
 
@@ -629,7 +580,13 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
                break;
        }
 
-       return;
+       acpi_bus_get_device(handle, &device);
+       if (device) {
+               driver = device->driver;
+               if (driver && driver->ops.notify &&
+                   (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
+                       driver->ops.notify(device, type);
+       }
 }
 
 /* --------------------------------------------------------------------------
index 8bd2c2a6884d5ce550ff336775e87844331e0e69..a8a5c29958c81faaea6b007626e40ee225ec0275 100644 (file)
@@ -140,46 +140,6 @@ struct device *acpi_get_physical_device(acpi_handle handle)
 
 EXPORT_SYMBOL(acpi_get_physical_device);
 
-/* ToDo: When a PCI bridge is found, return the PCI device behind the bridge
- *       This should work in general, but did not on a Lenovo T61 for the
- *      graphics card. But this must be fixed when the PCI device is
- *       bound and the kernel device struct is attached to the acpi device
- * Note: A success call will increase reference count by one
- *       Do call put_device(dev) on the returned device then
- */
-struct device *acpi_get_physical_pci_device(acpi_handle handle)
-{
-       struct device *dev;
-       long long device_id;
-       acpi_status status;
-
-       status =
-               acpi_evaluate_integer(handle, "_ADR", NULL, &device_id);
-
-       if (ACPI_FAILURE(status))
-               return NULL;
-
-       /* 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 device in the host bridge */
-       if (device_id >= 0x10000) {
-               /* It looks like a PCI device. Does it exist? */
-               dev = acpi_get_physical_device(handle);
-       } else {
-               /* It doesn't look like a PCI device. Does its parent
-                  exist? */
-               acpi_handle phandle;
-               if (acpi_get_parent(handle, &phandle))
-                       return NULL;
-               dev = acpi_get_physical_device(phandle);
-       }
-       if (!dev)
-               return NULL;
-       return dev;
-}
-EXPORT_SYMBOL(acpi_get_physical_pci_device);
-
 static int acpi_bind_one(struct device *dev, acpi_handle handle)
 {
        struct acpi_device *acpi_dev;
index d916bea729f1dfac4e81f020d581eee296ab42e0..71670719d61a6fec442792189fa0beac85e0324d 100644 (file)
@@ -79,6 +79,7 @@ static acpi_osd_handler acpi_irq_handler;
 static void *acpi_irq_context;
 static struct workqueue_struct *kacpid_wq;
 static struct workqueue_struct *kacpi_notify_wq;
+static struct workqueue_struct *kacpi_hotplug_wq;
 
 struct acpi_res_list {
        resource_size_t start;
@@ -192,8 +193,10 @@ acpi_status acpi_os_initialize1(void)
 {
        kacpid_wq = create_singlethread_workqueue("kacpid");
        kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify");
+       kacpi_hotplug_wq = create_singlethread_workqueue("kacpi_hotplug");
        BUG_ON(!kacpid_wq);
        BUG_ON(!kacpi_notify_wq);
+       BUG_ON(!kacpi_hotplug_wq);
        return AE_OK;
 }
 
@@ -206,6 +209,7 @@ acpi_status acpi_os_terminate(void)
 
        destroy_workqueue(kacpid_wq);
        destroy_workqueue(kacpi_notify_wq);
+       destroy_workqueue(kacpi_hotplug_wq);
 
        return AE_OK;
 }
@@ -716,6 +720,7 @@ static acpi_status __acpi_os_execute(acpi_execute_type type,
        acpi_status status = AE_OK;
        struct acpi_os_dpc *dpc;
        struct workqueue_struct *queue;
+       work_func_t func;
        int ret;
        ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
                          "Scheduling function [%p(%p)] for deferred execution.\n",
@@ -740,15 +745,17 @@ static acpi_status __acpi_os_execute(acpi_execute_type type,
        dpc->function = function;
        dpc->context = context;
 
-       if (!hp) {
-               INIT_WORK(&dpc->work, acpi_os_execute_deferred);
-               queue = (type == OSL_NOTIFY_HANDLER) ?
-                       kacpi_notify_wq : kacpid_wq;
-               ret = queue_work(queue, &dpc->work);
-       } else {
-               INIT_WORK(&dpc->work, acpi_os_execute_hp_deferred);
-               ret = schedule_work(&dpc->work);
-       }
+       /*
+        * We can't run hotplug code in keventd_wq/kacpid_wq/kacpid_notify_wq
+        * because the hotplug code may call driver .remove() functions,
+        * which invoke flush_scheduled_work/acpi_os_wait_events_complete
+        * to flush these workqueues.
+        */
+       queue = hp ? kacpi_hotplug_wq :
+               (type == OSL_NOTIFY_HANDLER ? kacpi_notify_wq : kacpid_wq);
+       func = hp ? acpi_os_execute_hp_deferred : acpi_os_execute_deferred;
+       INIT_WORK(&dpc->work, func);
+       ret = queue_work(queue, &dpc->work);
 
        if (!ret) {
                printk(KERN_ERR PREFIX
index bc46de3d967f62904962e06bf9beef26095a9a78..a5a77b78a7237cbbb6e77a707f73a0861426718d 100644 (file)
  */
 
 #include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
 #include <linux/types.h>
-#include <linux/proc_fs.h>
-#include <linux/spinlock.h>
-#include <linux/pm.h>
 #include <linux/pci.h>
 #include <linux/acpi.h>
 #include <acpi/acpi_bus.h>
 #define _COMPONENT             ACPI_PCI_COMPONENT
 ACPI_MODULE_NAME("pci_bind");
 
-struct acpi_pci_data {
-       struct acpi_pci_id id;
-       struct pci_bus *bus;
-       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)
-{
-
-       /* TBD: Anything we need to do here? */
-
-       return;
-}
-
-/**
- * acpi_get_pci_id
- * ------------------
- * This function is used by the ACPI Interpreter (a.k.a. Core Subsystem)
- * to resolve PCI information for ACPI-PCI devices defined in the namespace.
- * This typically occurs when resolving PCI operation region information.
- */
-acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id)
+static int acpi_pci_unbind(struct acpi_device *device)
 {
-       int result = 0;
-       acpi_status status = AE_OK;
-       struct acpi_device *device = NULL;
-       struct acpi_pci_data *data = NULL;
-
-
-       if (!id)
-               return AE_BAD_PARAMETER;
-
-       result = acpi_bus_get_device(handle, &device);
-       if (result) {
-               printk(KERN_ERR PREFIX
-                           "Invalid ACPI Bus context for device %s\n",
-                           acpi_device_bid(device));
-               return AE_NOT_EXIST;
-       }
-
-       status = acpi_get_data(handle, acpi_pci_data_handler, (void **)&data);
-       if (ACPI_FAILURE(status) || !data) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Invalid ACPI-PCI context for device %s",
-                               acpi_device_bid(device)));
-               return status;
-       }
+       struct pci_dev *dev;
 
-       *id = data->id;
+       dev = acpi_get_pci_dev(device->handle);
+       if (!dev || !dev->subordinate)
+               goto out;
 
-       /*
-          id->segment = data->id.segment;
-          id->bus = data->id.bus;
-          id->device = data->id.device;
-          id->function = data->id.function;
-        */
+       acpi_pci_irq_del_prt(dev->subordinate);
 
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                         "Device %s has PCI address %04x:%02x:%02x.%d\n",
-                         acpi_device_bid(device), id->segment, id->bus,
-                         id->device, id->function));
+       device->ops.bind = NULL;
+       device->ops.unbind = NULL;
 
-       return AE_OK;
+out:
+       pci_dev_put(dev);
+       return 0;
 }
 
-EXPORT_SYMBOL(acpi_get_pci_id);
-
-int acpi_pci_bind(struct acpi_device *device)
+static int acpi_pci_bind(struct acpi_device *device)
 {
-       int result = 0;
        acpi_status status;
-       struct acpi_pci_data *data;
-       struct acpi_pci_data *pdata;
-       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
        acpi_handle handle;
+       struct pci_bus *bus;
+       struct pci_dev *dev;
 
-       if (!device || !device->parent)
-               return -EINVAL;
-
-       data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer);
-       if (ACPI_FAILURE(status)) {
-               kfree(data);
-               return -ENODEV;
-       }
-
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI device [%s]...\n",
-                         (char *)buffer.pointer));
-
-       /* 
-        * Segment & Bus
-        * -------------
-        * These are obtained via the parent device's ACPI-PCI context.
-        */
-       status = acpi_get_data(device->parent->handle, acpi_pci_data_handler,
-                              (void **)&pdata);
-       if (ACPI_FAILURE(status) || !pdata || !pdata->bus) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Invalid ACPI-PCI context for parent device %s",
-                               acpi_device_bid(device->parent)));
-               result = -ENODEV;
-               goto end;
-       }
-       data->id.segment = pdata->id.segment;
-       data->id.bus = pdata->bus->number;
-
-       /*
-        * Device & Function
-        * -----------------
-        * These are simply obtained from the device's _ADR method.  Note
-        * that a value of zero is valid.
-        */
-       data->id.device = device->pnp.bus_address >> 16;
-       data->id.function = device->pnp.bus_address & 0xFFFF;
-
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "...to %04x:%02x:%02x.%d\n",
-                         data->id.segment, data->id.bus, data->id.device,
-                         data->id.function));
-
-       /*
-        * TBD: Support slot devices (e.g. function=0xFFFF).
-        */
-
-       /* 
-        * Locate PCI Device
-        * -----------------
-        * Locate matching device in PCI namespace.  If it doesn't exist
-        * this typically means that the device isn't currently inserted
-        * (e.g. docking station, port replicator, etc.).
-        */
-       data->dev = pci_get_slot(pdata->bus,
-                               PCI_DEVFN(data->id.device, data->id.function));
-       if (!data->dev) {
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Device %04x:%02x:%02x.%d not present in PCI namespace\n",
-                                 data->id.segment, data->id.bus,
-                                 data->id.device, data->id.function));
-               result = -ENODEV;
-               goto end;
-       }
-       if (!data->dev->bus) {
-               printk(KERN_ERR PREFIX
-                           "Device %04x:%02x:%02x.%d has invalid 'bus' field\n",
-                           data->id.segment, data->id.bus,
-                           data->id.device, data->id.function);
-               result = -ENODEV;
-               goto end;
-       }
+       dev = acpi_get_pci_dev(device->handle);
+       if (!dev)
+               return 0;
 
        /*
-        * PCI Bridge?
-        * -----------
-        * If so, set the 'bus' field and install the 'bind' function to 
-        * facilitate callbacks for all of its children.
+        * Install the 'bind' function to facilitate callbacks for
+        * children of the P2P bridge.
         */
-       if (data->dev->subordinate) {
+       if (dev->subordinate) {
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                                  "Device %04x:%02x:%02x.%d is a PCI bridge\n",
-                                 data->id.segment, data->id.bus,
-                                 data->id.device, data->id.function));
-               data->bus = data->dev->subordinate;
+                                 pci_domain_nr(dev->bus), dev->bus->number,
+                                 PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)));
                device->ops.bind = acpi_pci_bind;
                device->ops.unbind = acpi_pci_unbind;
        }
 
        /*
-        * Attach ACPI-PCI Context
-        * -----------------------
-        * Thus binding the ACPI and PCI devices.
-        */
-       status = acpi_attach_data(device->handle, acpi_pci_data_handler, data);
-       if (ACPI_FAILURE(status)) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Unable to attach ACPI-PCI context to device %s",
-                               acpi_device_bid(device)));
-               result = -ENODEV;
-               goto end;
-       }
-
-       /*
-        * PCI Routing Table
-        * -----------------
-        * Evaluate and parse _PRT, if exists.  This code is independent of 
-        * PCI bridges (above) to allow parsing of _PRT objects within the
-        * scope of non-bridge devices.  Note that _PRTs within the scope of
-        * a PCI bridge assume the bridge's subordinate bus number.
+        * Evaluate and parse _PRT, if exists.  This code allows parsing of
+        * _PRT objects within the scope of non-bridge devices.  Note that
+        * _PRTs within the scope of a PCI bridge assume the bridge's
+        * subordinate bus number.
         *
         * TBD: Can _PRTs exist within the scope of non-bridge PCI devices?
         */
        status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
-       if (ACPI_SUCCESS(status)) {
-               if (data->bus)  /* PCI-PCI bridge */
-                       acpi_pci_irq_add_prt(device->handle, data->id.segment,
-                                            data->bus->number);
-               else            /* non-bridge PCI device */
-                       acpi_pci_irq_add_prt(device->handle, data->id.segment,
-                                            data->id.bus);
-       }
-
-      end:
-       kfree(buffer.pointer);
-       if (result) {
-               pci_dev_put(data->dev);
-               kfree(data);
-       }
-       return result;
-}
-
-static int acpi_pci_unbind(struct acpi_device *device)
-{
-       int result = 0;
-       acpi_status status;
-       struct acpi_pci_data *data;
-       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-
-
-       if (!device || !device->parent)
-               return -EINVAL;
-
-       status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer);
        if (ACPI_FAILURE(status))
-               return -ENODEV;
+               goto out;
 
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unbinding PCI device [%s]...\n",
-                         (char *) buffer.pointer));
-       kfree(buffer.pointer);
+       if (dev->subordinate)
+               bus = dev->subordinate;
+       else
+               bus = dev->bus;
 
-       status =
-           acpi_get_data(device->handle, acpi_pci_data_handler,
-                         (void **)&data);
-       if (ACPI_FAILURE(status)) {
-               result = -ENODEV;
-               goto end;
-       }
+       acpi_pci_irq_add_prt(device->handle, bus);
 
-       status = acpi_detach_data(device->handle, acpi_pci_data_handler);
-       if (ACPI_FAILURE(status)) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Unable to detach data from device %s",
-                               acpi_device_bid(device)));
-               result = -ENODEV;
-               goto end;
-       }
-       if (data->dev->subordinate) {
-               acpi_pci_irq_del_prt(data->id.segment, data->bus->number);
-       }
-       pci_dev_put(data->dev);
-       kfree(data);
-
-      end:
-       return result;
+out:
+       pci_dev_put(dev);
+       return 0;
 }
 
-int
-acpi_pci_bind_root(struct acpi_device *device,
-                  struct acpi_pci_id *id, struct pci_bus *bus)
+int acpi_pci_bind_root(struct acpi_device *device)
 {
-       int result = 0;
-       acpi_status status;
-       struct acpi_pci_data *data = NULL;
-       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-
-       if (!device || !id || !bus) {
-               return -EINVAL;
-       }
-
-       data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       data->id = *id;
-       data->bus = bus;
        device->ops.bind = acpi_pci_bind;
        device->ops.unbind = acpi_pci_unbind;
 
-       status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer);
-       if (ACPI_FAILURE(status)) {
-               kfree (data);
-               return -ENODEV;
-       }
-
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI root bridge [%s] to "
-                       "%04x:%02x\n", (char *)buffer.pointer,
-                       id->segment, id->bus));
-
-       status = acpi_attach_data(device->handle, acpi_pci_data_handler, data);
-       if (ACPI_FAILURE(status)) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Unable to attach ACPI-PCI context to device %s",
-                               (char *)buffer.pointer));
-               result = -ENODEV;
-               goto end;
-       }
-
-      end:
-       kfree(buffer.pointer);
-       if (result != 0)
-               kfree(data);
-
-       return result;
+       return 0;
 }
index 2faa9e2ac89331b9c46c1de0de560a0225ce88e2..b794eb88ab9030506873ab6879ab05860cc083b8 100644 (file)
@@ -182,7 +182,7 @@ static void do_prt_fixups(struct acpi_prt_entry *entry,
        }
 }
 
-static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus,
+static int acpi_pci_irq_add_entry(acpi_handle handle, struct pci_bus *bus,
                                  struct acpi_pci_routing_table *prt)
 {
        struct acpi_prt_entry *entry;
@@ -196,8 +196,8 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus,
         * 1=INTA, 2=INTB.  We use the PCI encoding throughout, so convert
         * it here.
         */
-       entry->id.segment = segment;
-       entry->id.bus = bus;
+       entry->id.segment = pci_domain_nr(bus);
+       entry->id.bus = bus->number;
        entry->id.device = (prt->address >> 16) & 0xFFFF;
        entry->pin = prt->pin + 1;
 
@@ -242,7 +242,7 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus,
        return 0;
 }
 
-int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus)
+int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus)
 {
        acpi_status status;
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -271,7 +271,7 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus)
 
        entry = buffer.pointer;
        while (entry && (entry->length > 0)) {
-               acpi_pci_irq_add_entry(handle, segment, bus, entry);
+               acpi_pci_irq_add_entry(handle, bus, entry);
                entry = (struct acpi_pci_routing_table *)
                    ((unsigned long)entry + entry->length);
        }
@@ -280,16 +280,17 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus)
        return 0;
 }
 
-void acpi_pci_irq_del_prt(int segment, int bus)
+void acpi_pci_irq_del_prt(struct pci_bus *bus)
 {
        struct acpi_prt_entry *entry, *tmp;
 
        printk(KERN_DEBUG
               "ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n",
-              segment, bus);
+              pci_domain_nr(bus), bus->number);
        spin_lock(&acpi_prt_lock);
        list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) {
-               if (segment == entry->id.segment && bus == entry->id.bus) {
+               if (pci_domain_nr(bus) == entry->id.segment
+                       && bus->number == entry->id.bus) {
                        list_del(&entry->list);
                        kfree(entry);
                }
index 196f97d00956aa379b95b8fbe8e5a6cb6ddd10eb..8a5bf3b356faa2d60c40f0c1f6aef6279d9d57c8 100644 (file)
@@ -63,9 +63,10 @@ static struct acpi_driver acpi_pci_root_driver = {
 
 struct acpi_pci_root {
        struct list_head node;
-       struct acpi_device * device;
-       struct acpi_pci_id id;
+       struct acpi_device *device;
        struct pci_bus *bus;
+       u16 segment;
+       u8 bus_nr;
 
        u32 osc_support_set;    /* _OSC state of support bits */
        u32 osc_control_set;    /* _OSC state of control bits */
@@ -82,7 +83,7 @@ static DEFINE_MUTEX(osc_lock);
 int acpi_pci_register_driver(struct acpi_pci_driver *driver)
 {
        int n = 0;
-       struct list_head *entry;
+       struct acpi_pci_root *root;
 
        struct acpi_pci_driver **pptr = &sub_driver;
        while (*pptr)
@@ -92,9 +93,7 @@ int acpi_pci_register_driver(struct acpi_pci_driver *driver)
        if (!driver->add)
                return 0;
 
-       list_for_each(entry, &acpi_pci_roots) {
-               struct acpi_pci_root *root;
-               root = list_entry(entry, struct acpi_pci_root, node);
+       list_for_each_entry(root, &acpi_pci_roots, node) {
                driver->add(root->device->handle);
                n++;
        }
@@ -106,7 +105,7 @@ EXPORT_SYMBOL(acpi_pci_register_driver);
 
 void acpi_pci_unregister_driver(struct acpi_pci_driver *driver)
 {
-       struct list_head *entry;
+       struct acpi_pci_root *root;
 
        struct acpi_pci_driver **pptr = &sub_driver;
        while (*pptr) {
@@ -120,28 +119,48 @@ void acpi_pci_unregister_driver(struct acpi_pci_driver *driver)
        if (!driver->remove)
                return;
 
-       list_for_each(entry, &acpi_pci_roots) {
-               struct acpi_pci_root *root;
-               root = list_entry(entry, struct acpi_pci_root, node);
+       list_for_each_entry(root, &acpi_pci_roots, node)
                driver->remove(root->device->handle);
-       }
 }
 
 EXPORT_SYMBOL(acpi_pci_unregister_driver);
 
 acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus)
 {
-       struct acpi_pci_root *tmp;
+       struct acpi_pci_root *root;
        
-       list_for_each_entry(tmp, &acpi_pci_roots, node) {
-               if ((tmp->id.segment == (u16) seg) && (tmp->id.bus == (u16) bus))
-                       return tmp->device->handle;
-       }
+       list_for_each_entry(root, &acpi_pci_roots, node)
+               if ((root->segment == (u16) seg) && (root->bus_nr == (u16) bus))
+                       return root->device->handle;
        return NULL;            
 }
 
 EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle);
 
+/**
+ * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge
+ * @handle - the ACPI CA node in question.
+ *
+ * Note: we could make this API take a struct acpi_device * instead, but
+ * for now, it's more convenient to operate on an acpi_handle.
+ */
+int acpi_is_root_bridge(acpi_handle handle)
+{
+       int ret;
+       struct acpi_device *device;
+
+       ret = acpi_bus_get_device(handle, &device);
+       if (ret)
+               return 0;
+
+       ret = acpi_match_device_ids(device, root_device_ids);
+       if (ret)
+               return 0;
+       else
+               return 1;
+}
+EXPORT_SYMBOL_GPL(acpi_is_root_bridge);
+
 static acpi_status
 get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data)
 {
@@ -161,19 +180,22 @@ get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data)
        return AE_OK;
 }
 
-static acpi_status try_get_root_bridge_busnr(acpi_handle handle, int *busnum)
+static acpi_status try_get_root_bridge_busnr(acpi_handle handle,
+                                            unsigned long long *bus)
 {
        acpi_status status;
+       int busnum;
 
-       *busnum = -1;
+       busnum = -1;
        status =
            acpi_walk_resources(handle, METHOD_NAME__CRS,
-                               get_root_bridge_busnr_callback, busnum);
+                               get_root_bridge_busnr_callback, &busnum);
        if (ACPI_FAILURE(status))
                return status;
        /* Check if we really get a bus number from _CRS */
-       if (*busnum == -1)
+       if (busnum == -1)
                return AE_ERROR;
+       *bus = busnum;
        return AE_OK;
 }
 
@@ -298,6 +320,7 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags)
 static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
 {
        struct acpi_pci_root *root;
+
        list_for_each_entry(root, &acpi_pci_roots, node) {
                if (root->device->handle == handle)
                        return root;
@@ -305,6 +328,87 @@ static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
        return NULL;
 }
 
+struct acpi_handle_node {
+       struct list_head node;
+       acpi_handle handle;
+};
+
+/**
+ * acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev
+ * @handle: the handle in question
+ *
+ * Given an ACPI CA handle, the desired PCI device is located in the
+ * list of PCI devices.
+ *
+ * If the device is found, its reference count is increased and this
+ * function returns a pointer to its data structure.  The caller must
+ * decrement the reference count by calling pci_dev_put().
+ * If no device is found, %NULL is returned.
+ */
+struct pci_dev *acpi_get_pci_dev(acpi_handle handle)
+{
+       int dev, fn;
+       unsigned long long adr;
+       acpi_status status;
+       acpi_handle phandle;
+       struct pci_bus *pbus;
+       struct pci_dev *pdev = NULL;
+       struct acpi_handle_node *node, *tmp;
+       struct acpi_pci_root *root;
+       LIST_HEAD(device_list);
+
+       /*
+        * Walk up the ACPI CA namespace until we reach a PCI root bridge.
+        */
+       phandle = handle;
+       while (!acpi_is_root_bridge(phandle)) {
+               node = kzalloc(sizeof(struct acpi_handle_node), GFP_KERNEL);
+               if (!node)
+                       goto out;
+
+               INIT_LIST_HEAD(&node->node);
+               node->handle = phandle;
+               list_add(&node->node, &device_list);
+
+               status = acpi_get_parent(phandle, &phandle);
+               if (ACPI_FAILURE(status))
+                       goto out;
+       }
+
+       root = acpi_pci_find_root(phandle);
+       if (!root)
+               goto out;
+
+       pbus = root->bus;
+
+       /*
+        * Now, walk back down the PCI device tree until we return to our
+        * original handle. Assumes that everything between the PCI root
+        * bridge and the device we're looking for must be a P2P bridge.
+        */
+       list_for_each_entry(node, &device_list, node) {
+               acpi_handle hnd = node->handle;
+               status = acpi_evaluate_integer(hnd, "_ADR", NULL, &adr);
+               if (ACPI_FAILURE(status))
+                       goto out;
+               dev = (adr >> 16) & 0xffff;
+               fn  = adr & 0xffff;
+
+               pdev = pci_get_slot(pbus, PCI_DEVFN(dev, fn));
+               if (hnd == handle)
+                       break;
+
+               pbus = pdev->subordinate;
+               pci_dev_put(pdev);
+       }
+out:
+       list_for_each_entry_safe(node, tmp, &device_list, node)
+               kfree(node);
+
+       return pdev;
+}
+EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
+
 /**
  * acpi_pci_osc_control_set - commit requested control to Firmware
  * @handle: acpi_handle for the target ACPI object
@@ -363,31 +467,46 @@ EXPORT_SYMBOL(acpi_pci_osc_control_set);
 
 static int __devinit acpi_pci_root_add(struct acpi_device *device)
 {
-       int result = 0;
-       struct acpi_pci_root *root = NULL;
-       struct acpi_pci_root *tmp;
-       acpi_status status = AE_OK;
-       unsigned long long value = 0;
-       acpi_handle handle = NULL;
+       unsigned long long segment, bus;
+       acpi_status status;
+       int result;
+       struct acpi_pci_root *root;
+       acpi_handle handle;
        struct acpi_device *child;
        u32 flags, base_flags;
 
+       segment = 0;
+       status = acpi_evaluate_integer(device->handle, METHOD_NAME__SEG, NULL,
+                                      &segment);
+       if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+               printk(KERN_ERR PREFIX "can't evaluate _SEG\n");
+               return -ENODEV;
+       }
 
-       if (!device)
-               return -EINVAL;
+       /* Check _CRS first, then _BBN.  If no _BBN, default to zero. */
+       bus = 0;
+       status = try_get_root_bridge_busnr(device->handle, &bus);
+       if (ACPI_FAILURE(status)) {
+               status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN,                                               NULL, &bus);
+               if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+                       printk(KERN_ERR PREFIX
+                            "no bus number in _CRS and can't evaluate _BBN\n");
+                       return -ENODEV;
+               }
+       }
 
        root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL);
        if (!root)
                return -ENOMEM;
-       INIT_LIST_HEAD(&root->node);
 
+       INIT_LIST_HEAD(&root->node);
        root->device = device;
+       root->segment = segment & 0xFFFF;
+       root->bus_nr = bus & 0xFF;
        strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME);
        strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);
        device->driver_data = root;
 
-       device->ops.bind = acpi_pci_bind;
-
        /*
         * All supported architectures that use ACPI have support for
         * PCI domains, so we indicate this in _OSC support capabilities.
@@ -395,79 +514,6 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
        flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT;
        acpi_pci_osc_support(root, flags);
 
-       /* 
-        * Segment
-        * -------
-        * Obtained via _SEG, if exists, otherwise assumed to be zero (0).
-        */
-       status = acpi_evaluate_integer(device->handle, METHOD_NAME__SEG, NULL,
-                                      &value);
-       switch (status) {
-       case AE_OK:
-               root->id.segment = (u16) value;
-               break;
-       case AE_NOT_FOUND:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Assuming segment 0 (no _SEG)\n"));
-               root->id.segment = 0;
-               break;
-       default:
-               ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SEG"));
-               result = -ENODEV;
-               goto end;
-       }
-
-       /* 
-        * Bus
-        * ---
-        * Obtained via _BBN, if exists, otherwise assumed to be zero (0).
-        */
-       status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN, NULL,
-                                      &value);
-       switch (status) {
-       case AE_OK:
-               root->id.bus = (u16) value;
-               break;
-       case AE_NOT_FOUND:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Assuming bus 0 (no _BBN)\n"));
-               root->id.bus = 0;
-               break;
-       default:
-               ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BBN"));
-               result = -ENODEV;
-               goto end;
-       }
-
-       /* Some systems have wrong _BBN */
-       list_for_each_entry(tmp, &acpi_pci_roots, node) {
-               if ((tmp->id.segment == root->id.segment)
-                   && (tmp->id.bus == root->id.bus)) {
-                       int bus = 0;
-                       acpi_status status;
-
-                       printk(KERN_ERR PREFIX
-                                   "Wrong _BBN value, reboot"
-                                   " and use option 'pci=noacpi'\n");
-
-                       status = try_get_root_bridge_busnr(device->handle, &bus);
-                       if (ACPI_FAILURE(status))
-                               break;
-                       if (bus != root->id.bus) {
-                               printk(KERN_INFO PREFIX
-                                      "PCI _CRS %d overrides _BBN 0\n", bus);
-                               root->id.bus = bus;
-                       }
-                       break;
-               }
-       }
-       /*
-        * Device & Function
-        * -----------------
-        * Obtained from _ADR (which has already been evaluated for us).
-        */
-       root->id.device = device->pnp.bus_address >> 16;
-       root->id.function = device->pnp.bus_address & 0xFFFF;
-
        /*
         * TBD: Need PCI interface for enumeration/configuration of roots.
         */
@@ -477,7 +523,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
 
        printk(KERN_INFO PREFIX "%s [%s] (%04x:%02x)\n",
               acpi_device_name(device), acpi_device_bid(device),
-              root->id.segment, root->id.bus);
+              root->segment, root->bus_nr);
 
        /*
         * Scan the Root Bridge
@@ -486,11 +532,11 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
         * PCI namespace does not get created until this call is made (and 
         * thus the root bridge's pci_dev does not exist).
         */
-       root->bus = pci_acpi_scan_root(device, root->id.segment, root->id.bus);
+       root->bus = pci_acpi_scan_root(device, segment, bus);
        if (!root->bus) {
                printk(KERN_ERR PREFIX
                            "Bus %04x:%02x not present in PCI namespace\n",
-                           root->id.segment, root->id.bus);
+                           root->segment, root->bus_nr);
                result = -ENODEV;
                goto end;
        }
@@ -500,7 +546,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
         * -----------------------
         * Thus binding the ACPI and PCI devices.
         */
-       result = acpi_pci_bind_root(device, &root->id, root->bus);
+       result = acpi_pci_bind_root(device);
        if (result)
                goto end;
 
@@ -511,8 +557,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
         */
        status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
        if (ACPI_SUCCESS(status))
-               result = acpi_pci_irq_add_prt(device->handle, root->id.segment,
-                                             root->id.bus);
+               result = acpi_pci_irq_add_prt(device->handle, root->bus);
 
        /*
         * Scan and bind all _ADR-Based Devices
@@ -531,42 +576,28 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
        if (flags != base_flags)
                acpi_pci_osc_support(root, flags);
 
-      end:
-       if (result) {
-               if (!list_empty(&root->node))
-                       list_del(&root->node);
-               kfree(root);
-       }
+       return 0;
 
+end:
+       if (!list_empty(&root->node))
+               list_del(&root->node);
+       kfree(root);
        return result;
 }
 
 static int acpi_pci_root_start(struct acpi_device *device)
 {
-       struct acpi_pci_root *root;
+       struct acpi_pci_root *root = acpi_driver_data(device);
 
-
-       list_for_each_entry(root, &acpi_pci_roots, node) {
-               if (root->device == device) {
-                       pci_bus_add_devices(root->bus);
-                       return 0;
-               }
-       }
-       return -ENODEV;
+       pci_bus_add_devices(root->bus);
+       return 0;
 }
 
 static int acpi_pci_root_remove(struct acpi_device *device, int type)
 {
-       struct acpi_pci_root *root = NULL;
-
-
-       if (!device || !acpi_driver_data(device))
-               return -EINVAL;
-
-       root = acpi_driver_data(device);
+       struct acpi_pci_root *root = acpi_driver_data(device);
 
        kfree(root);
-
        return 0;
 }
 
index 56665a63bf19b29f86c4088f02cd0ff8c7ad9faa..d74365d4a6e7d03f781954663301c50d30ac77da 100644 (file)
@@ -194,7 +194,7 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
 
 static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
 {
-       int result = 0, state;
+       int result = 0;
        int found = 0;
        acpi_status status = AE_OK;
        struct acpi_power_resource *resource = NULL;
@@ -236,18 +236,6 @@ static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
        if (ACPI_FAILURE(status))
                return -ENODEV;
 
-       if (!acpi_power_nocheck) {
-               /*
-                * If acpi_power_nocheck is set, it is unnecessary to check
-                * the power state after power transition.
-                */
-               result = acpi_power_get_state(resource->device->handle,
-                               &state);
-               if (result)
-                       return result;
-               if (state != ACPI_POWER_RESOURCE_STATE_ON)
-                       return -ENOEXEC;
-       }
        /* Update the power resource's _device_ power state */
        resource->device->power.state = ACPI_STATE_D0;
 
@@ -258,7 +246,7 @@ static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
 
 static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
 {
-       int result = 0, state;
+       int result = 0;
        acpi_status status = AE_OK;
        struct acpi_power_resource *resource = NULL;
        struct list_head *node, *next;
@@ -293,18 +281,6 @@ static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
        if (ACPI_FAILURE(status))
                return -ENODEV;
 
-       if (!acpi_power_nocheck) {
-               /*
-                * If acpi_power_nocheck is set, it is unnecessary to check
-                * the power state after power transition.
-                */
-               result = acpi_power_get_state(handle, &state);
-               if (result)
-                       return result;
-               if (state != ACPI_POWER_RESOURCE_STATE_OFF)
-                       return -ENOEXEC;
-       }
-
        /* Update the power resource's _device_ power state */
        resource->device->power.state = ACPI_STATE_D3;
 
index 23f0fb84f1c1104538efe624c5824937e069c191..84e0f3c0744270b4c8474f92239b64255a27985b 100644 (file)
@@ -89,7 +89,7 @@ static int acpi_processor_handle_eject(struct acpi_processor *pr);
 
 static const struct acpi_device_id processor_device_ids[] = {
        {ACPI_PROCESSOR_OBJECT_HID, 0},
-       {ACPI_PROCESSOR_HID, 0},
+       {"ACPI0007", 0},
        {"", 0},
 };
 MODULE_DEVICE_TABLE(acpi, processor_device_ids);
@@ -596,7 +596,21 @@ static int acpi_processor_get_info(struct acpi_device *device)
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                                  "No bus mastering arbitration control\n"));
 
-       if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_HID)) {
+       if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) {
+               /* Declared with "Processor" statement; match ProcessorID */
+               status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
+               if (ACPI_FAILURE(status)) {
+                       printk(KERN_ERR PREFIX "Evaluating processor object\n");
+                       return -ENODEV;
+               }
+
+               /*
+                * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP.
+                *      >>> 'acpi_get_processor_id(acpi_id, &id)' in
+                *      arch/xxx/acpi.c
+                */
+               pr->acpi_id = object.processor.proc_id;
+       } else {
                /*
                 * Declared with "Device" statement; match _UID.
                 * Note that we don't handle string _UIDs yet.
@@ -611,20 +625,6 @@ static int acpi_processor_get_info(struct acpi_device *device)
                }
                device_declaration = 1;
                pr->acpi_id = value;
-       } else {
-               /* Declared with "Processor" statement; match ProcessorID */
-               status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
-               if (ACPI_FAILURE(status)) {
-                       printk(KERN_ERR PREFIX "Evaluating processor object\n");
-                       return -ENODEV;
-               }
-
-               /*
-                * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP.
-                *      >>> 'acpi_get_processor_id(acpi_id, &id)' in
-                *      arch/xxx/acpi.c
-                */
-               pr->acpi_id = object.processor.proc_id;
        }
        cpu_index = get_cpu_id(pr->handle, device_declaration, pr->acpi_id);
 
@@ -649,7 +649,16 @@ static int acpi_processor_get_info(struct acpi_device *device)
                        return -ENODEV;
                }
        }
-
+       /*
+        * On some boxes several processors use the same processor bus id.
+        * But they are located in different scope. For example:
+        * \_SB.SCK0.CPU0
+        * \_SB.SCK1.CPU0
+        * Rename the processor device bus id. And the new bus id will be
+        * generated as the following format:
+        * CPU+CPU ID.
+        */
+       sprintf(acpi_device_bid(device), "CPU%X", pr->id);
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id,
                          pr->acpi_id));
 
@@ -731,6 +740,8 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device)
        /* _PDC call should be done before doing anything else (if reqd.). */
        arch_acpi_processor_init_pdc(pr);
        acpi_processor_set_pdc(pr);
+       arch_acpi_processor_cleanup_pdc(pr);
+
 #ifdef CONFIG_CPU_FREQ
        acpi_processor_ppc_has_changed(pr);
 #endif
index 10a2d913635a99d76502309efb55895d7a92c5e4..0efa59e7e3afd16095fa538f333f11f37ca7a9d4 100644 (file)
@@ -139,7 +139,7 @@ static void acpi_safe_halt(void)
  * are affected too. We pick the most conservative approach: we assume
  * that the local APIC stops in both C2 and C3.
  */
-static void acpi_timer_check_state(int state, struct acpi_processor *pr,
+static void lapic_timer_check_state(int state, struct acpi_processor *pr,
                                   struct acpi_processor_cx *cx)
 {
        struct acpi_processor_power *pwr = &pr->power;
@@ -162,7 +162,7 @@ static void acpi_timer_check_state(int state, struct acpi_processor *pr,
                pr->power.timer_broadcast_on_state = state;
 }
 
-static void acpi_propagate_timer_broadcast(struct acpi_processor *pr)
+static void lapic_timer_propagate_broadcast(struct acpi_processor *pr)
 {
        unsigned long reason;
 
@@ -173,7 +173,7 @@ static void acpi_propagate_timer_broadcast(struct acpi_processor *pr)
 }
 
 /* Power(C) State timer broadcast control */
-static void acpi_state_timer_broadcast(struct acpi_processor *pr,
+static void lapic_timer_state_broadcast(struct acpi_processor *pr,
                                       struct acpi_processor_cx *cx,
                                       int broadcast)
 {
@@ -190,10 +190,10 @@ static void acpi_state_timer_broadcast(struct acpi_processor *pr,
 
 #else
 
-static void acpi_timer_check_state(int state, struct acpi_processor *pr,
+static void lapic_timer_check_state(int state, struct acpi_processor *pr,
                                   struct acpi_processor_cx *cstate) { }
-static void acpi_propagate_timer_broadcast(struct acpi_processor *pr) { }
-static void acpi_state_timer_broadcast(struct acpi_processor *pr,
+static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { }
+static void lapic_timer_state_broadcast(struct acpi_processor *pr,
                                       struct acpi_processor_cx *cx,
                                       int broadcast)
 {
@@ -515,7 +515,8 @@ static void acpi_processor_power_verify_c2(struct acpi_processor_cx *cx)
 static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
                                           struct acpi_processor_cx *cx)
 {
-       static int bm_check_flag;
+       static int bm_check_flag = -1;
+       static int bm_control_flag = -1;
 
 
        if (!cx->address)
@@ -545,12 +546,14 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
        }
 
        /* All the logic here assumes flags.bm_check is same across all CPUs */
-       if (!bm_check_flag) {
+       if (bm_check_flag == -1) {
                /* Determine whether bm_check is needed based on CPU  */
                acpi_processor_power_init_bm_check(&(pr->flags), pr->id);
                bm_check_flag = pr->flags.bm_check;
+               bm_control_flag = pr->flags.bm_control;
        } else {
                pr->flags.bm_check = bm_check_flag;
+               pr->flags.bm_control = bm_control_flag;
        }
 
        if (pr->flags.bm_check) {
@@ -614,29 +617,25 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
                switch (cx->type) {
                case ACPI_STATE_C1:
                        cx->valid = 1;
-                       acpi_timer_check_state(i, pr, cx);
                        break;
 
                case ACPI_STATE_C2:
                        acpi_processor_power_verify_c2(cx);
-                       if (cx->valid)
-                               acpi_timer_check_state(i, pr, cx);
                        break;
 
                case ACPI_STATE_C3:
                        acpi_processor_power_verify_c3(pr, cx);
-                       if (cx->valid)
-                               acpi_timer_check_state(i, pr, cx);
                        break;
                }
-               if (cx->valid)
-                       tsc_check_state(cx->type);
+               if (!cx->valid)
+                       continue;
 
-               if (cx->valid)
-                       working++;
+               lapic_timer_check_state(i, pr, cx);
+               tsc_check_state(cx->type);
+               working++;
        }
 
-       acpi_propagate_timer_broadcast(pr);
+       lapic_timer_propagate_broadcast(pr);
 
        return (working);
 }
@@ -839,7 +838,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
                return 0;
        }
 
-       acpi_state_timer_broadcast(pr, cx, 1);
+       lapic_timer_state_broadcast(pr, cx, 1);
        kt1 = ktime_get_real();
        acpi_idle_do_entry(cx);
        kt2 = ktime_get_real();
@@ -847,7 +846,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
 
        local_irq_enable();
        cx->usage++;
-       acpi_state_timer_broadcast(pr, cx, 0);
+       lapic_timer_state_broadcast(pr, cx, 0);
 
        return idle_time;
 }
@@ -892,7 +891,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
         * Must be done before busmaster disable as we might need to
         * access HPET !
         */
-       acpi_state_timer_broadcast(pr, cx, 1);
+       lapic_timer_state_broadcast(pr, cx, 1);
 
        if (cx->type == ACPI_STATE_C3)
                ACPI_FLUSH_CPU_CACHE();
@@ -914,7 +913,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
 
        cx->usage++;
 
-       acpi_state_timer_broadcast(pr, cx, 0);
+       lapic_timer_state_broadcast(pr, cx, 0);
        cx->time += sleep_ticks;
        return idle_time;
 }
@@ -981,7 +980,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
         * Must be done before busmaster disable as we might need to
         * access HPET !
         */
-       acpi_state_timer_broadcast(pr, cx, 1);
+       lapic_timer_state_broadcast(pr, cx, 1);
 
        kt1 = ktime_get_real();
        /*
@@ -1026,7 +1025,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
 
        cx->usage++;
 
-       acpi_state_timer_broadcast(pr, cx, 0);
+       lapic_timer_state_broadcast(pr, cx, 0);
        cx->time += sleep_ticks;
        return idle_time;
 }
index 8ff510b91d88f4f38e473afab76425a9cf392477..781435d7e3692026d81cb6f112e7e422aa00a7dc 100644 (file)
@@ -95,7 +95,7 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
 }
 static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
 
-static int acpi_bus_hot_remove_device(void *context)
+static void acpi_bus_hot_remove_device(void *context)
 {
        struct acpi_device *device;
        acpi_handle handle = context;
@@ -104,10 +104,10 @@ static int acpi_bus_hot_remove_device(void *context)
        acpi_status status = AE_OK;
 
        if (acpi_bus_get_device(handle, &device))
-               return 0;
+               return;
 
        if (!device)
-               return 0;
+               return;
 
        ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                "Hot-removing device %s...\n", dev_name(&device->dev)));
@@ -115,7 +115,7 @@ static int acpi_bus_hot_remove_device(void *context)
        if (acpi_bus_trim(device, 1)) {
                printk(KERN_ERR PREFIX
                                "Removing device failed\n");
-               return -1;
+               return;
        }
 
        /* power off device */
@@ -142,9 +142,10 @@ static int acpi_bus_hot_remove_device(void *context)
         */
        status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);
        if (ACPI_FAILURE(status))
-               return -ENODEV;
+               printk(KERN_WARNING PREFIX
+                               "Eject device failed\n");
 
-       return 0;
+       return;
 }
 
 static ssize_t
@@ -155,7 +156,6 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
        acpi_status status;
        acpi_object_type type = 0;
        struct acpi_device *acpi_device = to_acpi_device(d);
-       struct task_struct *task;
 
        if ((!count) || (buf[0] != '1')) {
                return -EINVAL;
@@ -172,11 +172,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
                goto err;
        }
 
-       /* remove the device in another thread to fix the deadlock issue */
-       task = kthread_run(acpi_bus_hot_remove_device,
-                               acpi_device->handle, "acpi_hot_remove_device");
-       if (IS_ERR(task))
-               ret = PTR_ERR(task);
+       acpi_os_hotplug_execute(acpi_bus_hot_remove_device, acpi_device->handle);
 err:
        return ret;
 }
@@ -198,12 +194,12 @@ acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *b
        int result;
 
        result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path);
-       if(result)
+       if (result)
                goto end;
 
        result = sprintf(buf, "%s\n", (char*)path.pointer);
        kfree(path.pointer);
-  end:
+end:
        return result;
 }
 static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL);
@@ -217,21 +213,21 @@ static int acpi_device_setup_files(struct acpi_device *dev)
        /*
         * Devices gotten from FADT don't have a "path" attribute
         */
-       if(dev->handle) {
+       if (dev->handle) {
                result = device_create_file(&dev->dev, &dev_attr_path);
-               if(result)
+               if (result)
                        goto end;
        }
 
-       if(dev->flags.hardware_id) {
+       if (dev->flags.hardware_id) {
                result = device_create_file(&dev->dev, &dev_attr_hid);
-               if(result)
+               if (result)
                        goto end;
        }
 
-       if (dev->flags.hardware_id || dev->flags.compatible_ids){
+       if (dev->flags.hardware_id || dev->flags.compatible_ids) {
                result = device_create_file(&dev->dev, &dev_attr_modalias);
-               if(result)
+               if (result)
                        goto end;
        }
 
@@ -242,7 +238,7 @@ static int acpi_device_setup_files(struct acpi_device *dev)
        status = acpi_get_handle(dev->handle, "_EJ0", &temp);
        if (ACPI_SUCCESS(status))
                result = device_create_file(&dev->dev, &dev_attr_eject);
-  end:
+end:
        return result;
 }
 
@@ -262,9 +258,9 @@ static void acpi_device_remove_files(struct acpi_device *dev)
        if (dev->flags.hardware_id || dev->flags.compatible_ids)
                device_remove_file(&dev->dev, &dev_attr_modalias);
 
-       if(dev->flags.hardware_id)
+       if (dev->flags.hardware_id)
                device_remove_file(&dev->dev, &dev_attr_hid);
-       if(dev->handle)
+       if (dev->handle)
                device_remove_file(&dev->dev, &dev_attr_path);
 }
 /* --------------------------------------------------------------------------
@@ -512,7 +508,7 @@ static int acpi_device_register(struct acpi_device *device,
                        break;
                }
        }
-       if(!found) {
+       if (!found) {
                acpi_device_bus_id = new_bus_id;
                strcpy(acpi_device_bus_id->bus_id, device->flags.hardware_id ? device->pnp.hardware_id : "device");
                acpi_device_bus_id->instance_no = 0;
@@ -530,22 +526,21 @@ static int acpi_device_register(struct acpi_device *device,
        if (device->parent)
                device->dev.parent = &parent->dev;
        device->dev.bus = &acpi_bus_type;
-       device_initialize(&device->dev);
        device->dev.release = &acpi_device_release;
-       result = device_add(&device->dev);
-       if(result) {
-               dev_err(&device->dev, "Error adding device\n");
+       result = device_register(&device->dev);
+       if (result) {
+               dev_err(&device->dev, "Error registering device\n");
                goto end;
        }
 
        result = acpi_device_setup_files(device);
-       if(result)
+       if (result)
                printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n",
                       dev_name(&device->dev));
 
        device->removal_type = ACPI_BUS_REMOVAL_NORMAL;
        return 0;
-  end:
+end:
        mutex_lock(&acpi_device_lock);
        if (device->parent)
                list_del(&device->node);
@@ -577,7 +572,7 @@ static void acpi_device_unregister(struct acpi_device *device, int type)
  * @device: the device to add and initialize
  * @driver: driver for the device
  *
- * Used to initialize a device via its device driver.  Called whenever a 
+ * Used to initialize a device via its device driver.  Called whenever a
  * driver is bound to a device.  Invokes the driver's add() ops.
  */
 static int
@@ -585,7 +580,6 @@ acpi_bus_driver_init(struct acpi_device *device, struct acpi_driver *driver)
 {
        int result = 0;
 
-
        if (!device || !driver)
                return -EINVAL;
 
@@ -802,7 +796,7 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
        if (!acpi_match_device_ids(device, button_device_ids))
                device->wakeup.flags.run_wake = 1;
 
-      end:
+end:
        if (ACPI_FAILURE(status))
                device->flags.wake_capable = 0;
        return 0;
@@ -1070,7 +1064,7 @@ static void acpi_device_set_id(struct acpi_device *device,
                break;
        }
 
-       /* 
+       /*
         * \_SB
         * ----
         * Fix for the system root bus device -- the only root-level device.
@@ -1320,7 +1314,7 @@ acpi_add_single_object(struct acpi_device **child,
                        device->parent->ops.bind(device);
        }
 
-      end:
+end:
        if (!result)
                *child = device;
        else {
@@ -1464,7 +1458,6 @@ acpi_bus_add(struct acpi_device **child,
 
        return result;
 }
-
 EXPORT_SYMBOL(acpi_bus_add);
 
 int acpi_bus_start(struct acpi_device *device)
@@ -1484,7 +1477,6 @@ int acpi_bus_start(struct acpi_device *device)
        }
        return result;
 }
-
 EXPORT_SYMBOL(acpi_bus_start);
 
 int acpi_bus_trim(struct acpi_device *start, int rmdevice)
@@ -1542,7 +1534,6 @@ int acpi_bus_trim(struct acpi_device *start, int rmdevice)
 }
 EXPORT_SYMBOL_GPL(acpi_bus_trim);
 
-
 static int acpi_bus_scan_fixed(struct acpi_device *root)
 {
        int result = 0;
@@ -1610,6 +1601,6 @@ int __init acpi_scan_init(void)
        if (result)
                acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
 
-      Done:
+Done:
        return result;
 }
index 1bdfb37377e32d58bcd1f3e35e858c1cc681aa5c..8851315ce858a2e2eb1e4ffd2d2b6b695fa8bfb8 100644 (file)
@@ -76,6 +76,7 @@ MODULE_LICENSE("GPL");
 static int brightness_switch_enabled = 1;
 module_param(brightness_switch_enabled, bool, 0644);
 
+static int register_count = 0;
 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);
@@ -586,6 +587,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"),
                },
        },
+       {
+        .callback = video_set_bqc_offset,
+        .ident = "Acer Aspire 7720",
+        .matches = {
+               DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),
+               },
+       },
        {}
 };
 
@@ -976,6 +985,11 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                device->backlight->props.max_brightness = device->brightness->count-3;
                kfree(name);
 
+               result = sysfs_create_link(&device->backlight->dev.kobj,
+                                          &device->dev->dev.kobj, "device");
+               if (result)
+                       printk(KERN_ERR PREFIX "Create sysfs link\n");
+
                device->cdev = thermal_cooling_device_register("LCD",
                                        device->dev, &video_cooling_ops);
                if (IS_ERR(device->cdev))
@@ -1054,15 +1068,15 @@ 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;
-       struct device *dev;
+       struct pci_dev *dev;
 
        if (!video)
                return -EINVAL;
 
-       dev = acpi_get_physical_pci_device(video->device->handle);
+       dev = acpi_get_pci_dev(video->device->handle);
        if (!dev)
                return -ENODEV;
-       put_device(dev);
+       pci_dev_put(dev);
 
        /* Since there is no HID, CID and so on for VGA driver, we have
         * to check well known required nodes.
@@ -1990,6 +2004,7 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
        status = acpi_remove_notify_handler(device->dev->handle,
                                            ACPI_DEVICE_NOTIFY,
                                            acpi_video_device_notify);
+       sysfs_remove_link(&device->backlight->dev.kobj, "device");
        backlight_device_unregister(device->backlight);
        if (device->cdev) {
                sysfs_remove_link(&device->dev->dev.kobj,
@@ -2318,6 +2333,13 @@ static int __init intel_opregion_present(void)
 int acpi_video_register(void)
 {
        int result = 0;
+       if (register_count) {
+               /*
+                * if the function of acpi_video_register is already called,
+                * don't register the acpi_vide_bus again and return no error.
+                */
+               return 0;
+       }
 
        acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
        if (!acpi_video_dir)
@@ -2329,10 +2351,35 @@ int acpi_video_register(void)
                return -ENODEV;
        }
 
+       /*
+        * When the acpi_video_bus is loaded successfully, increase
+        * the counter reference.
+        */
+       register_count = 1;
+
        return 0;
 }
 EXPORT_SYMBOL(acpi_video_register);
 
+void acpi_video_unregister(void)
+{
+       if (!register_count) {
+               /*
+                * If the acpi video bus is already unloaded, don't
+                * unload it again and return directly.
+                */
+               return;
+       }
+       acpi_bus_unregister_driver(&acpi_video_bus);
+
+       remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
+
+       register_count = 0;
+
+       return;
+}
+EXPORT_SYMBOL(acpi_video_unregister);
+
 /*
  * This is kind of nasty. Hardware using Intel chipsets may require
  * the video opregion code to be run first in order to initialise
@@ -2350,16 +2397,12 @@ static int __init acpi_video_init(void)
        return acpi_video_register();
 }
 
-void acpi_video_exit(void)
+static void __exit acpi_video_exit(void)
 {
-
-       acpi_bus_unregister_driver(&acpi_video_bus);
-
-       remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
+       acpi_video_unregister();
 
        return;
 }
-EXPORT_SYMBOL(acpi_video_exit);
 
 module_init(acpi_video_init);
 module_exit(acpi_video_exit);
index 09737275e25fd94990d7e250f7acfcc09d2e10af..7cd2b63435ea1bf1464d2a8dbac17650cbd12dc4 100644 (file)
@@ -10,7 +10,7 @@
  * assinged
  *
  * After PCI devices are glued with ACPI devices
- * acpi_get_physical_pci_device() can be called to identify ACPI graphics
+ * acpi_get_pci_dev() can be called to identify ACPI graphics
  * devices for which a real graphics card is plugged in
  *
  * Now acpi_video_get_capabilities() can be called to check which
@@ -36,6 +36,7 @@
 
 #include <linux/acpi.h>
 #include <linux/dmi.h>
+#include <linux/pci.h>
 
 ACPI_MODULE_NAME("video");
 #define _COMPONENT             ACPI_VIDEO_COMPONENT
@@ -109,7 +110,7 @@ static acpi_status
 find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
 {
        long *cap = context;
-       struct device *dev;
+       struct pci_dev *dev;
        struct acpi_device *acpi_dev;
 
        const struct acpi_device_id video_ids[] = {
@@ -120,10 +121,10 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
                return AE_OK;
 
        if (!acpi_match_device_ids(acpi_dev, video_ids)) {
-               dev = acpi_get_physical_pci_device(handle);
+               dev = acpi_get_pci_dev(handle);
                if (!dev)
                        return AE_OK;
-               put_device(dev);
+               pci_dev_put(dev);
                *cap |= acpi_is_video_device(acpi_dev);
        }
        return AE_OK;
index 9533f43a30bb0e7bad9873acb85d25cf39e44625..52d953eb30c3d6872eb6e4d3b3710ba7578e272c 100644 (file)
@@ -1048,8 +1048,6 @@ static int mxser_open(struct tty_struct *tty, struct file *filp)
        if (retval)
                return retval;
 
-       /* unmark here for very high baud rate (ex. 921600 bps) used */
-       tty->low_latency = 1;
        return 0;
 }
 
index d6102b644b550d0f9a306b015dd985f642d7e974..574f1c79b6e637ededd5a4708651bc7902aea960 100644 (file)
@@ -1591,8 +1591,6 @@ static int ntty_open(struct tty_struct *tty, struct file *file)
 
        /* Enable interrupt downlink for channel */
        if (port->port.count == 1) {
-               /* FIXME: is this needed now ? */
-               tty->low_latency = 1;
                tty->driver_data = port;
                tty_port_tty_set(&port->port, tty);
                DBG1("open: %d", port->token_dl);
index 1386625fc4caae4bdf6a39e6835d0026b0e27ca7..a2e67e6df3a1406697e3482c93689e314bfe0f12 100644 (file)
@@ -467,7 +467,6 @@ static unsigned int free_tbuf_count(struct slgt_info *info);
 static unsigned int tbuf_bytes(struct slgt_info *info);
 static void reset_tbufs(struct slgt_info *info);
 static void tdma_reset(struct slgt_info *info);
-static void tdma_start(struct slgt_info *info);
 static void tx_load(struct slgt_info *info, const char *buf, unsigned int count);
 
 static void get_signals(struct slgt_info *info);
@@ -795,6 +794,18 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
        }
 }
 
+static void update_tx_timer(struct slgt_info *info)
+{
+       /*
+        * use worst case speed of 1200bps to calculate transmit timeout
+        * based on data in buffers (tbuf_bytes) and FIFO (128 bytes)
+        */
+       if (info->params.mode == MGSL_MODE_HDLC) {
+               int timeout  = (tbuf_bytes(info) * 7) + 1000;
+               mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout));
+       }
+}
+
 static int write(struct tty_struct *tty,
                 const unsigned char *buf, int count)
 {
@@ -838,8 +849,18 @@ start:
                spin_lock_irqsave(&info->lock,flags);
                if (!info->tx_active)
                        tx_start(info);
-               else
-                       tdma_start(info);
+               else if (!(rd_reg32(info, TDCSR) & BIT0)) {
+                       /* transmit still active but transmit DMA stopped */
+                       unsigned int i = info->tbuf_current;
+                       if (!i)
+                               i = info->tbuf_count;
+                       i--;
+                       /* if DMA buf unsent must try later after tx idle */
+                       if (desc_count(info->tbufs[i]))
+                               ret = 0;
+               }
+               if (ret > 0)
+                       update_tx_timer(info);
                spin_unlock_irqrestore(&info->lock,flags);
        }
 
@@ -1502,10 +1523,9 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
        /* save start time for transmit timeout detection */
        dev->trans_start = jiffies;
 
-       /* start hardware transmitter if necessary */
        spin_lock_irqsave(&info->lock,flags);
-       if (!info->tx_active)
-               tx_start(info);
+       tx_start(info);
+       update_tx_timer(info);
        spin_unlock_irqrestore(&info->lock,flags);
 
        return 0;
@@ -3946,50 +3966,19 @@ static void tx_start(struct slgt_info *info)
                        slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE);
                        /* clear tx idle and underrun status bits */
                        wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
-                       if (info->params.mode == MGSL_MODE_HDLC)
-                               mod_timer(&info->tx_timer, jiffies +
-                                               msecs_to_jiffies(5000));
                } else {
                        slgt_irq_off(info, IRQ_TXDATA);
                        slgt_irq_on(info, IRQ_TXIDLE);
                        /* clear tx idle status bit */
                        wr_reg16(info, SSR, IRQ_TXIDLE);
                }
-               tdma_start(info);
+               /* set 1st descriptor address and start DMA */
+               wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
+               wr_reg32(info, TDCSR, BIT2 + BIT0);
                info->tx_active = true;
        }
 }
 
-/*
- * start transmit DMA if inactive and there are unsent buffers
- */
-static void tdma_start(struct slgt_info *info)
-{
-       unsigned int i;
-
-       if (rd_reg32(info, TDCSR) & BIT0)
-               return;
-
-       /* transmit DMA inactive, check for unsent buffers */
-       i = info->tbuf_start;
-       while (!desc_count(info->tbufs[i])) {
-               if (++i == info->tbuf_count)
-                       i = 0;
-               if (i == info->tbuf_current)
-                       return;
-       }
-       info->tbuf_start = i;
-
-       /* there are unsent buffers, start transmit DMA */
-
-       /* reset needed if previous error condition */
-       tdma_reset(info);
-
-       /* set 1st descriptor address */
-       wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
-       wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
-}
-
 static void tx_stop(struct slgt_info *info)
 {
        unsigned short val;
@@ -5004,8 +4993,7 @@ static void tx_timeout(unsigned long context)
                info->icount.txtimeout++;
        }
        spin_lock_irqsave(&info->lock,flags);
-       info->tx_active = false;
-       info->tx_count = 0;
+       tx_stop(info);
        spin_unlock_irqrestore(&info->lock,flags);
 
 #if SYNCLINK_GENERIC_HDLC
index 62dadfc95e341078ef42a284366cef37dbac6432..4e862a75f7ff213bd62022e852a9d485b65adbbf 100644 (file)
@@ -193,7 +193,7 @@ int tty_port_block_til_ready(struct tty_port *port,
 {
        int do_clocal = 0, retval;
        unsigned long flags;
-       DECLARE_WAITQUEUE(wait, current);
+       DEFINE_WAIT(wait);
        int cd;
 
        /* block if port is in the process of being closed */
index dc425e74a268e0c59d9f008cb3d791e1b83c8751..e4b4e8898e39a20a451cd97a5b57b13d9e63561b 100644 (file)
@@ -419,7 +419,7 @@ void intel_opregion_free(struct drm_device *dev, int suspend)
                return;
 
        if (!suspend)
-               acpi_video_exit();
+               acpi_video_unregister();
 
        opregion->acpi->drdy = 0;
 
index 3c259ee7ddda01d61b9462dae9d9d9bfc7625178..aa87b6a3bbef0b1f4b7e086ebc55b3fd638ef885 100644 (file)
@@ -326,6 +326,15 @@ config I2C_DAVINCI
          devices such as DaVinci NIC.
          For details please see http://www.ti.com/davinci
 
+config I2C_DESIGNWARE
+       tristate "Synopsys DesignWare"
+       help
+         If you say yes to this option, support will be included for the
+         Synopsys DesignWare I2C adapter. Only master mode is supported.
+
+         This driver can also be built as a module.  If so, the module
+         will be called i2c-designware.
+
 config I2C_GPIO
        tristate "GPIO-based bitbanging I2C"
        depends on GENERIC_GPIO
index edeabf00310600830caa27dd96e88e038762f078..e654263bfc01df6442f52b5c39df95589203b698 100644 (file)
@@ -30,6 +30,7 @@ obj-$(CONFIG_I2C_AU1550)      += i2c-au1550.o
 obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
 obj-$(CONFIG_I2C_CPM)          += i2c-cpm.o
 obj-$(CONFIG_I2C_DAVINCI)      += i2c-davinci.o
+obj-$(CONFIG_I2C_DESIGNWARE)   += i2c-designware.o
 obj-$(CONFIG_I2C_GPIO)         += i2c-gpio.o
 obj-$(CONFIG_I2C_HIGHLANDER)   += i2c-highlander.o
 obj-$(CONFIG_I2C_IBM_IIC)      += i2c-ibm_iic.o
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c
new file mode 100644 (file)
index 0000000..b444762
--- /dev/null
@@ -0,0 +1,624 @@
+/*
+ * Synopsys Designware I2C adapter driver (master only).
+ *
+ * Based on the TI DAVINCI I2C adapter driver.
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2007 MontaVista Software Inc.
+ * Copyright (C) 2009 Provigent Ltd.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ----------------------------------------------------------------------------
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+/*
+ * Registers offset
+ */
+#define DW_IC_CON              0x0
+#define DW_IC_TAR              0x4
+#define DW_IC_DATA_CMD         0x10
+#define DW_IC_SS_SCL_HCNT      0x14
+#define DW_IC_SS_SCL_LCNT      0x18
+#define DW_IC_FS_SCL_HCNT      0x1c
+#define DW_IC_FS_SCL_LCNT      0x20
+#define DW_IC_INTR_STAT                0x2c
+#define DW_IC_INTR_MASK                0x30
+#define DW_IC_CLR_INTR         0x40
+#define DW_IC_ENABLE           0x6c
+#define DW_IC_STATUS           0x70
+#define DW_IC_TXFLR            0x74
+#define DW_IC_RXFLR            0x78
+#define DW_IC_COMP_PARAM_1     0xf4
+#define DW_IC_TX_ABRT_SOURCE   0x80
+
+#define DW_IC_CON_MASTER               0x1
+#define DW_IC_CON_SPEED_STD            0x2
+#define DW_IC_CON_SPEED_FAST           0x4
+#define DW_IC_CON_10BITADDR_MASTER     0x10
+#define DW_IC_CON_RESTART_EN           0x20
+#define DW_IC_CON_SLAVE_DISABLE                0x40
+
+#define DW_IC_INTR_TX_EMPTY    0x10
+#define DW_IC_INTR_TX_ABRT     0x40
+#define DW_IC_INTR_STOP_DET    0x200
+
+#define DW_IC_STATUS_ACTIVITY  0x1
+
+#define DW_IC_ERR_TX_ABRT      0x1
+
+/*
+ * status codes
+ */
+#define STATUS_IDLE                    0x0
+#define STATUS_WRITE_IN_PROGRESS       0x1
+#define STATUS_READ_IN_PROGRESS                0x2
+
+#define TIMEOUT                        20 /* ms */
+
+/*
+ * hardware abort codes from the DW_IC_TX_ABRT_SOURCE register
+ *
+ * only expected abort codes are listed here
+ * refer to the datasheet for the full list
+ */
+#define ABRT_7B_ADDR_NOACK     0
+#define ABRT_10ADDR1_NOACK     1
+#define ABRT_10ADDR2_NOACK     2
+#define ABRT_TXDATA_NOACK      3
+#define ABRT_GCALL_NOACK       4
+#define ABRT_GCALL_READ                5
+#define ABRT_SBYTE_ACKDET      7
+#define ABRT_SBYTE_NORSTRT     9
+#define ABRT_10B_RD_NORSTRT    10
+#define ARB_MASTER_DIS         11
+#define ARB_LOST               12
+
+static char *abort_sources[] = {
+       [ABRT_7B_ADDR_NOACK]    =
+               "slave address not acknowledged (7bit mode)",
+       [ABRT_10ADDR1_NOACK]    =
+               "first address byte not acknowledged (10bit mode)",
+       [ABRT_10ADDR2_NOACK]    =
+               "second address byte not acknowledged (10bit mode)",
+       [ABRT_TXDATA_NOACK]             =
+               "data not acknowledged",
+       [ABRT_GCALL_NOACK]              =
+               "no acknowledgement for a general call",
+       [ABRT_GCALL_READ]               =
+               "read after general call",
+       [ABRT_SBYTE_ACKDET]             =
+               "start byte acknowledged",
+       [ABRT_SBYTE_NORSTRT]    =
+               "trying to send start byte when restart is disabled",
+       [ABRT_10B_RD_NORSTRT]   =
+               "trying to read when restart is disabled (10bit mode)",
+       [ARB_MASTER_DIS]                =
+               "trying to use disabled adapter",
+       [ARB_LOST]                      =
+               "lost arbitration",
+};
+
+/**
+ * struct dw_i2c_dev - private i2c-designware data
+ * @dev: driver model device node
+ * @base: IO registers pointer
+ * @cmd_complete: tx completion indicator
+ * @pump_msg: continue in progress transfers
+ * @lock: protect this struct and IO registers
+ * @clk: input reference clock
+ * @cmd_err: run time hadware error code
+ * @msgs: points to an array of messages currently being transfered
+ * @msgs_num: the number of elements in msgs
+ * @msg_write_idx: the element index of the current tx message in the msgs
+ *     array
+ * @tx_buf_len: the length of the current tx buffer
+ * @tx_buf: the current tx buffer
+ * @msg_read_idx: the element index of the current rx message in the msgs
+ *     array
+ * @rx_buf_len: the length of the current rx buffer
+ * @rx_buf: the current rx buffer
+ * @msg_err: error status of the current transfer
+ * @status: i2c master status, one of STATUS_*
+ * @abort_source: copy of the TX_ABRT_SOURCE register
+ * @irq: interrupt number for the i2c master
+ * @adapter: i2c subsystem adapter node
+ * @tx_fifo_depth: depth of the hardware tx fifo
+ * @rx_fifo_depth: depth of the hardware rx fifo
+ */
+struct dw_i2c_dev {
+       struct device           *dev;
+       void __iomem            *base;
+       struct completion       cmd_complete;
+       struct tasklet_struct   pump_msg;
+       struct mutex            lock;
+       struct clk              *clk;
+       int                     cmd_err;
+       struct i2c_msg          *msgs;
+       int                     msgs_num;
+       int                     msg_write_idx;
+       u16                     tx_buf_len;
+       u8                      *tx_buf;
+       int                     msg_read_idx;
+       u16                     rx_buf_len;
+       u8                      *rx_buf;
+       int                     msg_err;
+       unsigned int            status;
+       u16                     abort_source;
+       int                     irq;
+       struct i2c_adapter      adapter;
+       unsigned int            tx_fifo_depth;
+       unsigned int            rx_fifo_depth;
+};
+
+/**
+ * i2c_dw_init() - initialize the designware i2c master hardware
+ * @dev: device private data
+ *
+ * This functions configures and enables the I2C master.
+ * This function is called during I2C init function, and in case of timeout at
+ * run time.
+ */
+static void i2c_dw_init(struct dw_i2c_dev *dev)
+{
+       u32 input_clock_khz = clk_get_rate(dev->clk) / 1000;
+       u16 ic_con;
+
+       /* Disable the adapter */
+       writeb(0, dev->base + DW_IC_ENABLE);
+
+       /* set standard and fast speed deviders for high/low periods */
+       writew((input_clock_khz * 40 / 10000)+1, /* std speed high, 4us */
+                       dev->base + DW_IC_SS_SCL_HCNT);
+       writew((input_clock_khz * 47 / 10000)+1, /* std speed low, 4.7us */
+                       dev->base + DW_IC_SS_SCL_LCNT);
+       writew((input_clock_khz *  6 / 10000)+1, /* fast speed high, 0.6us */
+                       dev->base + DW_IC_FS_SCL_HCNT);
+       writew((input_clock_khz * 13 / 10000)+1, /* fast speed low, 1.3us */
+                       dev->base + DW_IC_FS_SCL_LCNT);
+
+       /* configure the i2c master */
+       ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
+               DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
+       writew(ic_con, dev->base + DW_IC_CON);
+}
+
+/*
+ * Waiting for bus not busy
+ */
+static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
+{
+       int timeout = TIMEOUT;
+
+       while (readb(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
+               if (timeout <= 0) {
+                       dev_warn(dev->dev, "timeout waiting for bus ready\n");
+                       return -ETIMEDOUT;
+               }
+               timeout--;
+               mdelay(1);
+       }
+
+       return 0;
+}
+
+/*
+ * Initiate low level master read/write transaction.
+ * This function is called from i2c_dw_xfer when starting a transfer.
+ * This function is also called from dw_i2c_pump_msg to continue a transfer
+ * that is longer than the size of the TX FIFO.
+ */
+static void
+i2c_dw_xfer_msg(struct i2c_adapter *adap)
+{
+       struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+       struct i2c_msg *msgs = dev->msgs;
+       int num = dev->msgs_num;
+       u16 ic_con, intr_mask;
+       int tx_limit = dev->tx_fifo_depth - readb(dev->base + DW_IC_TXFLR);
+       int rx_limit = dev->rx_fifo_depth - readb(dev->base + DW_IC_RXFLR);
+       u16 addr = msgs[dev->msg_write_idx].addr;
+       u16 buf_len = dev->tx_buf_len;
+
+       if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
+               /* Disable the adapter */
+               writeb(0, dev->base + DW_IC_ENABLE);
+
+               /* set the slave (target) address */
+               writew(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
+
+               /* if the slave address is ten bit address, enable 10BITADDR */
+               ic_con = readw(dev->base + DW_IC_CON);
+               if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
+                       ic_con |= DW_IC_CON_10BITADDR_MASTER;
+               else
+                       ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
+               writew(ic_con, dev->base + DW_IC_CON);
+
+               /* Enable the adapter */
+               writeb(1, dev->base + DW_IC_ENABLE);
+       }
+
+       for (; dev->msg_write_idx < num; dev->msg_write_idx++) {
+               /* if target address has changed, we need to
+                * reprogram the target address in the i2c
+                * adapter when we are done with this transfer
+                */
+               if (msgs[dev->msg_write_idx].addr != addr)
+                       return;
+
+               if (msgs[dev->msg_write_idx].len == 0) {
+                       dev_err(dev->dev,
+                               "%s: invalid message length\n", __func__);
+                       dev->msg_err = -EINVAL;
+                       return;
+               }
+
+               if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
+                       /* new i2c_msg */
+                       dev->tx_buf = msgs[dev->msg_write_idx].buf;
+                       buf_len = msgs[dev->msg_write_idx].len;
+               }
+
+               while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
+                       if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
+                               writew(0x100, dev->base + DW_IC_DATA_CMD);
+                               rx_limit--;
+                       } else
+                               writew(*(dev->tx_buf++),
+                                               dev->base + DW_IC_DATA_CMD);
+                       tx_limit--; buf_len--;
+               }
+       }
+
+       intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
+       if (buf_len > 0) { /* more bytes to be written */
+               intr_mask |= DW_IC_INTR_TX_EMPTY;
+               dev->status |= STATUS_WRITE_IN_PROGRESS;
+       } else
+               dev->status &= ~STATUS_WRITE_IN_PROGRESS;
+       writew(intr_mask, dev->base + DW_IC_INTR_MASK);
+
+       dev->tx_buf_len = buf_len;
+}
+
+static void
+i2c_dw_read(struct i2c_adapter *adap)
+{
+       struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+       struct i2c_msg *msgs = dev->msgs;
+       int num = dev->msgs_num;
+       u16 addr = msgs[dev->msg_read_idx].addr;
+       int rx_valid = readw(dev->base + DW_IC_RXFLR);
+
+       for (; dev->msg_read_idx < num; dev->msg_read_idx++) {
+               u16 len;
+               u8 *buf;
+
+               if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))
+                       continue;
+
+               /* different i2c client, reprogram the i2c adapter */
+               if (msgs[dev->msg_read_idx].addr != addr)
+                       return;
+
+               if (!(dev->status & STATUS_READ_IN_PROGRESS)) {
+                       len = msgs[dev->msg_read_idx].len;
+                       buf = msgs[dev->msg_read_idx].buf;
+               } else {
+                       len = dev->rx_buf_len;
+                       buf = dev->rx_buf;
+               }
+
+               for (; len > 0 && rx_valid > 0; len--, rx_valid--)
+                       *buf++ = readb(dev->base + DW_IC_DATA_CMD);
+
+               if (len > 0) {
+                       dev->status |= STATUS_READ_IN_PROGRESS;
+                       dev->rx_buf_len = len;
+                       dev->rx_buf = buf;
+                       return;
+               } else
+                       dev->status &= ~STATUS_READ_IN_PROGRESS;
+       }
+}
+
+/*
+ * Prepare controller for a transaction and call i2c_dw_xfer_msg
+ */
+static int
+i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+       struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+       int ret;
+
+       dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
+
+       mutex_lock(&dev->lock);
+
+       INIT_COMPLETION(dev->cmd_complete);
+       dev->msgs = msgs;
+       dev->msgs_num = num;
+       dev->cmd_err = 0;
+       dev->msg_write_idx = 0;
+       dev->msg_read_idx = 0;
+       dev->msg_err = 0;
+       dev->status = STATUS_IDLE;
+
+       ret = i2c_dw_wait_bus_not_busy(dev);
+       if (ret < 0)
+               goto done;
+
+       /* start the transfers */
+       i2c_dw_xfer_msg(adap);
+
+       /* wait for tx to complete */
+       ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ);
+       if (ret == 0) {
+               dev_err(dev->dev, "controller timed out\n");
+               i2c_dw_init(dev);
+               ret = -ETIMEDOUT;
+               goto done;
+       } else if (ret < 0)
+               goto done;
+
+       if (dev->msg_err) {
+               ret = dev->msg_err;
+               goto done;
+       }
+
+       /* no error */
+       if (likely(!dev->cmd_err)) {
+               /* read rx fifo, and disable the adapter */
+               do {
+                       i2c_dw_read(adap);
+               } while (dev->status & STATUS_READ_IN_PROGRESS);
+               writeb(0, dev->base + DW_IC_ENABLE);
+               ret = num;
+               goto done;
+       }
+
+       /* We have an error */
+       if (dev->cmd_err == DW_IC_ERR_TX_ABRT) {
+               unsigned long abort_source = dev->abort_source;
+               int i;
+
+               for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) {
+                   dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
+               }
+       }
+       ret = -EIO;
+
+done:
+       mutex_unlock(&dev->lock);
+
+       return ret;
+}
+
+static u32 i2c_dw_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
+}
+
+static void dw_i2c_pump_msg(unsigned long data)
+{
+       struct dw_i2c_dev *dev = (struct dw_i2c_dev *) data;
+       u16 intr_mask;
+
+       i2c_dw_read(&dev->adapter);
+       i2c_dw_xfer_msg(&dev->adapter);
+
+       intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
+       if (dev->status & STATUS_WRITE_IN_PROGRESS)
+               intr_mask |= DW_IC_INTR_TX_EMPTY;
+       writew(intr_mask, dev->base + DW_IC_INTR_MASK);
+}
+
+/*
+ * Interrupt service routine. This gets called whenever an I2C interrupt
+ * occurs.
+ */
+static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
+{
+       struct dw_i2c_dev *dev = dev_id;
+       u16 stat;
+
+       stat = readw(dev->base + DW_IC_INTR_STAT);
+       dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
+       if (stat & DW_IC_INTR_TX_ABRT) {
+               dev->abort_source = readw(dev->base + DW_IC_TX_ABRT_SOURCE);
+               dev->cmd_err |= DW_IC_ERR_TX_ABRT;
+               dev->status = STATUS_IDLE;
+       } else if (stat & DW_IC_INTR_TX_EMPTY)
+               tasklet_schedule(&dev->pump_msg);
+
+       readb(dev->base + DW_IC_CLR_INTR);      /* clear interrupts */
+       writew(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */
+       if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
+               complete(&dev->cmd_complete);
+
+       return IRQ_HANDLED;
+}
+
+static struct i2c_algorithm i2c_dw_algo = {
+       .master_xfer    = i2c_dw_xfer,
+       .functionality  = i2c_dw_func,
+};
+
+static int __devinit dw_i2c_probe(struct platform_device *pdev)
+{
+       struct dw_i2c_dev *dev;
+       struct i2c_adapter *adap;
+       struct resource *mem, *irq, *ioarea;
+       int r;
+
+       /* NOTE: driver uses the static register mapping */
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(&pdev->dev, "no mem resource?\n");
+               return -EINVAL;
+       }
+
+       irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!irq) {
+               dev_err(&pdev->dev, "no irq resource?\n");
+               return -EINVAL;
+       }
+
+       ioarea = request_mem_region(mem->start, resource_size(mem),
+                       pdev->name);
+       if (!ioarea) {
+               dev_err(&pdev->dev, "I2C region already claimed\n");
+               return -EBUSY;
+       }
+
+       dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
+       if (!dev) {
+               r = -ENOMEM;
+               goto err_release_region;
+       }
+
+       init_completion(&dev->cmd_complete);
+       tasklet_init(&dev->pump_msg, dw_i2c_pump_msg, (unsigned long) dev);
+       mutex_init(&dev->lock);
+       dev->dev = get_device(&pdev->dev);
+       dev->irq = irq->start;
+       platform_set_drvdata(pdev, dev);
+
+       dev->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(dev->clk)) {
+               r = -ENODEV;
+               goto err_free_mem;
+       }
+       clk_enable(dev->clk);
+
+       dev->base = ioremap(mem->start, resource_size(mem));
+       if (dev->base == NULL) {
+               dev_err(&pdev->dev, "failure mapping io resources\n");
+               r = -EBUSY;
+               goto err_unuse_clocks;
+       }
+       {
+               u32 param1 = readl(dev->base + DW_IC_COMP_PARAM_1);
+
+               dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1;
+               dev->rx_fifo_depth = ((param1 >> 8)  & 0xff) + 1;
+       }
+       i2c_dw_init(dev);
+
+       writew(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */
+       r = request_irq(dev->irq, i2c_dw_isr, 0, pdev->name, dev);
+       if (r) {
+               dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
+               goto err_iounmap;
+       }
+
+       adap = &dev->adapter;
+       i2c_set_adapdata(adap, dev);
+       adap->owner = THIS_MODULE;
+       adap->class = I2C_CLASS_HWMON;
+       strlcpy(adap->name, "Synopsys DesignWare I2C adapter",
+                       sizeof(adap->name));
+       adap->algo = &i2c_dw_algo;
+       adap->dev.parent = &pdev->dev;
+
+       adap->nr = pdev->id;
+       r = i2c_add_numbered_adapter(adap);
+       if (r) {
+               dev_err(&pdev->dev, "failure adding adapter\n");
+               goto err_free_irq;
+       }
+
+       return 0;
+
+err_free_irq:
+       free_irq(dev->irq, dev);
+err_iounmap:
+       iounmap(dev->base);
+err_unuse_clocks:
+       clk_disable(dev->clk);
+       clk_put(dev->clk);
+       dev->clk = NULL;
+err_free_mem:
+       platform_set_drvdata(pdev, NULL);
+       put_device(&pdev->dev);
+       kfree(dev);
+err_release_region:
+       release_mem_region(mem->start, resource_size(mem));
+
+       return r;
+}
+
+static int __devexit dw_i2c_remove(struct platform_device *pdev)
+{
+       struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
+       struct resource *mem;
+
+       platform_set_drvdata(pdev, NULL);
+       i2c_del_adapter(&dev->adapter);
+       put_device(&pdev->dev);
+
+       clk_disable(dev->clk);
+       clk_put(dev->clk);
+       dev->clk = NULL;
+
+       writeb(0, dev->base + DW_IC_ENABLE);
+       free_irq(dev->irq, dev);
+       kfree(dev);
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(mem->start, resource_size(mem));
+       return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:i2c_designware");
+
+static struct platform_driver dw_i2c_driver = {
+       .remove         = __devexit_p(dw_i2c_remove),
+       .driver         = {
+               .name   = "i2c_designware",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init dw_i2c_init_driver(void)
+{
+       return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
+}
+module_init(dw_i2c_init_driver);
+
+static void __exit dw_i2c_exit_driver(void)
+{
+       platform_driver_unregister(&dw_i2c_driver);
+}
+module_exit(dw_i2c_exit_driver);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");
+MODULE_LICENSE("GPL");
index 1dc721517e4cdf30db36ed726230d74b83e55501..c155bd3ec9f1a47640d4706626f544afe2e335bb 100644 (file)
@@ -1725,6 +1725,7 @@ config TLAN
 
 config KS8842
        tristate "Micrel KSZ8842"
+       depends on HAS_IOMEM
        help
          This platform driver is for Micrel KSZ8842 chip.
 
index 38f1c3375d7f64eb7a0e50e59feef331b72cf16c..b70cc99962fcfcf6ccb655213ccf0f1f0225fa89 100644 (file)
@@ -6825,6 +6825,14 @@ bnx2_nway_reset(struct net_device *dev)
        return 0;
 }
 
+static u32
+bnx2_get_link(struct net_device *dev)
+{
+       struct bnx2 *bp = netdev_priv(dev);
+
+       return bp->link_up;
+}
+
 static int
 bnx2_get_eeprom_len(struct net_device *dev)
 {
@@ -7392,7 +7400,7 @@ static const struct ethtool_ops bnx2_ethtool_ops = {
        .get_wol                = bnx2_get_wol,
        .set_wol                = bnx2_set_wol,
        .nway_reset             = bnx2_nway_reset,
-       .get_link               = ethtool_op_get_link,
+       .get_link               = bnx2_get_link,
        .get_eeprom_len         = bnx2_get_eeprom_len,
        .get_eeprom             = bnx2_get_eeprom,
        .set_eeprom             = bnx2_set_eeprom,
index d5e18812bf497f615465a6f1a5c9f7708976fefe..33821a81cbf85113336ab1b2ec37015291efcc4b 100644 (file)
@@ -36,7 +36,7 @@ config CAN_CALC_BITTIMING
          If unsure, say Y.
 
 config CAN_SJA1000
-       depends on CAN_DEV
+       depends on CAN_DEV && HAS_IOMEM
        tristate "Philips SJA1000"
        ---help---
          Driver for the SJA1000 CAN controllers from Philips or NXP
index bdb143d2b5c7a4f2032d19d746875dc3dfef01db..055bb61d6e7773b5177e342ebfcc952f4f0a008b 100644 (file)
@@ -944,28 +944,31 @@ int netxen_phantom_init(struct netxen_adapter *adapter, int pegtune_val)
        u32 val = 0;
        int retries = 60;
 
-       if (!pegtune_val) {
-               do {
-                       val = NXRD32(adapter, CRB_CMDPEG_STATE);
+       if (pegtune_val)
+               return 0;
 
-                       if (val == PHAN_INITIALIZE_COMPLETE ||
-                               val == PHAN_INITIALIZE_ACK)
-                               return 0;
+       do {
+               val = NXRD32(adapter, CRB_CMDPEG_STATE);
 
-                       msleep(500);
+               switch (val) {
+               case PHAN_INITIALIZE_COMPLETE:
+               case PHAN_INITIALIZE_ACK:
+                       return 0;
+               case PHAN_INITIALIZE_FAILED:
+                       goto out_err;
+               default:
+                       break;
+               }
 
-               } while (--retries);
+               msleep(500);
 
-               if (!retries) {
-                       pegtune_val = NXRD32(adapter,
-                                       NETXEN_ROMUSB_GLB_PEGTUNE_DONE);
-                       printk(KERN_WARNING "netxen_phantom_init: init failed, "
-                                       "pegtune_val=%x\n", pegtune_val);
-                       return -1;
-               }
-       }
+       } while (--retries);
 
-       return 0;
+       NXWR32(adapter, CRB_CMDPEG_STATE, PHAN_INITIALIZE_FAILED);
+
+out_err:
+       dev_warn(&adapter->pdev->dev, "firmware init failed\n");
+       return -EIO;
 }
 
 static int
index 71daa3d5f114174f7ec13015b49fa8d45cb9d2a6..2919a2d12bf40c5327f4979059eed3ea1de78878 100644 (file)
@@ -705,7 +705,7 @@ netxen_start_firmware(struct netxen_adapter *adapter, int request_fw)
                first_driver = (adapter->ahw.pci_func == 0);
 
        if (!first_driver)
-               return 0;
+               goto wait_init;
 
        first_boot = NXRD32(adapter, NETXEN_CAM_RAM(0x1fc));
 
@@ -752,6 +752,7 @@ netxen_start_firmware(struct netxen_adapter *adapter, int request_fw)
                | (_NETXEN_NIC_LINUX_SUBVERSION);
        NXWR32(adapter, CRB_DRIVER_VERSION, val);
 
+wait_init:
        /* Handshake with the card before we register the devices. */
        err = netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE);
        if (err) {
@@ -1178,6 +1179,7 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev)
        free_netdev(netdev);
 }
 
+#ifdef CONFIG_PM
 static int
 netxen_nic_suspend(struct pci_dev *pdev, pm_message_t state)
 {
@@ -1242,6 +1244,7 @@ netxen_nic_resume(struct pci_dev *pdev)
 
        return 0;
 }
+#endif
 
 static int netxen_nic_open(struct net_device *netdev)
 {
@@ -1771,8 +1774,10 @@ static struct pci_driver netxen_driver = {
        .id_table = netxen_pci_tbl,
        .probe = netxen_nic_probe,
        .remove = __devexit_p(netxen_nic_remove),
+#ifdef CONFIG_PM
        .suspend = netxen_nic_suspend,
        .resume = netxen_nic_resume
+#endif
 };
 
 /* Driver Registration on NetXen card    */
index bbc6d4d3cc945b1cdf9f42f1872d7cef231012a6..3e4b67aaa6ea5bddeee06f372d5925bf394466ce 100644 (file)
@@ -3142,6 +3142,7 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev)
                                                (void __iomem *)port_regs;
        u32 delay = 10;
        int status = 0;
+       unsigned long hw_flags = 0;
 
        if(ql_mii_setup(qdev))
                return -1;
@@ -3150,7 +3151,8 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev)
        ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg,
                            (ISP_SERIAL_PORT_IF_WE |
                             (ISP_SERIAL_PORT_IF_WE << 16)));
-
+       /* Give the PHY time to come out of reset. */
+       mdelay(100);
        qdev->port_link_state = LS_DOWN;
        netif_carrier_off(qdev->ndev);
 
@@ -3350,7 +3352,9 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev)
                value = ql_read_page0_reg(qdev, &port_regs->portStatus);
                if (value & PORT_STATUS_IC)
                        break;
+               spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
                msleep(500);
+               spin_lock_irqsave(&qdev->hw_lock, hw_flags);
        } while (--delay);
 
        if (delay == 0) {
index fbc63d5e459fd4182f35f5beffc9bee1a875c8d8..eb159587d0bfa5701c2d9476c8b606625d761af9 100644 (file)
@@ -354,7 +354,7 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
                status = acpi_run_hpp(handle, hpp);
                if (ACPI_SUCCESS(status))
                        break;
-               if (acpi_root_bridge(handle))
+               if (acpi_is_root_bridge(handle))
                        break;
                status = acpi_get_parent(handle, &phandle);
                if (ACPI_FAILURE(status))
@@ -428,7 +428,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
                status = acpi_run_oshp(handle);
                if (ACPI_SUCCESS(status))
                        goto got_one;
-               if (acpi_root_bridge(handle))
+               if (acpi_is_root_bridge(handle))
                        break;
                chandle = handle;
                status = acpi_get_parent(chandle, &handle);
@@ -449,42 +449,6 @@ got_one:
 }
 EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
 
-/* acpi_root_bridge - check to see if this acpi object is a root bridge
- *
- * @handle - the acpi object in question.
- */
-int acpi_root_bridge(acpi_handle handle)
-{
-       acpi_status status;
-       struct acpi_device_info *info;
-       struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
-       int i;
-
-       status = acpi_get_object_info(handle, &buffer);
-       if (ACPI_SUCCESS(status)) {
-               info = buffer.pointer;
-               if ((info->valid & ACPI_VALID_HID) &&
-                       !strcmp(PCI_ROOT_HID_STRING,
-                                       info->hardware_id.value)) {
-                       kfree(buffer.pointer);
-                       return 1;
-               }
-               if (info->valid & ACPI_VALID_CID) {
-                       for (i=0; i < info->compatibility_id.count; i++) {
-                               if (!strcmp(PCI_ROOT_HID_STRING,
-                                       info->compatibility_id.id[i].value)) {
-                                       kfree(buffer.pointer);
-                                       return 1;
-                               }
-                       }
-               }
-               kfree(buffer.pointer);
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(acpi_root_bridge);
-
-
 static int is_ejectable(acpi_handle handle)
 {
        acpi_status status;
index 3a6064bce5614a84ba16e1411a50bcfc7afddce3..0cb0f830a99307539d780e25e54ead35ed43cf02 100644 (file)
@@ -678,18 +678,9 @@ static void remove_bridge(acpi_handle handle)
 
 static struct pci_dev * get_apic_pci_info(acpi_handle handle)
 {
-       struct acpi_pci_id id;
-       struct pci_bus *bus;
        struct pci_dev *dev;
 
-       if (ACPI_FAILURE(acpi_get_pci_id(handle, &id)))
-               return NULL;
-
-       bus = pci_find_bus(id.segment, id.bus);
-       if (!bus)
-               return NULL;
-
-       dev = pci_get_slot(bus, PCI_DEVFN(id.device, id.function));
+       dev = acpi_get_pci_dev(handle);
        if (!dev)
                return NULL;
 
@@ -1396,19 +1387,16 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus)
 /* Program resources in newly inserted bridge */
 static int acpiphp_configure_bridge (acpi_handle handle)
 {
-       struct acpi_pci_id pci_id;
+       struct pci_dev *dev;
        struct pci_bus *bus;
 
-       if (ACPI_FAILURE(acpi_get_pci_id(handle, &pci_id))) {
+       dev = acpi_get_pci_dev(handle);
+       if (!dev) {
                err("cannot get PCI domain and bus number for bridge\n");
                return -EINVAL;
        }
-       bus = pci_find_bus(pci_id.segment, pci_id.bus);
-       if (!bus) {
-               err("cannot find bus %d:%d\n",
-                               pci_id.segment, pci_id.bus);
-               return -EINVAL;
-       }
+
+       bus = dev->bus;
 
        pci_bus_size_bridges(bus);
        pci_bus_assign_resources(bus);
@@ -1416,6 +1404,7 @@ static int acpiphp_configure_bridge (acpi_handle handle)
        acpiphp_set_hpp_values(handle, bus);
        pci_enable_bridges(bus);
        acpiphp_configure_ioapics(handle);
+       pci_dev_put(dev);
        return 0;
 }
 
@@ -1631,7 +1620,7 @@ find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
 {
        int *count = (int *)context;
 
-       if (acpi_root_bridge(handle)) {
+       if (acpi_is_root_bridge(handle)) {
                acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
                                handle_hotplug_event_bridge, NULL);
                        (*count)++;
index 178853a074405d746d6c0932030aa22daf2b17ca..e53eacd75c8daf5d6096947ef646a75162fb6bf1 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/sysdev.h>
 #include <asm/cacheflush.h>
 #include <asm/iommu.h>
+#include <asm/e820.h>
 #include "pci.h"
 
 #define ROOT_SIZE              VTD_PAGE_SIZE
@@ -217,6 +218,14 @@ static inline bool dma_pte_present(struct dma_pte *pte)
        return (pte->val & 3) != 0;
 }
 
+/*
+ * This domain is a statically identity mapping domain.
+ *     1. This domain creats a static 1:1 mapping to all usable memory.
+ *     2. It maps to each iommu if successful.
+ *     3. Each iommu mapps to this domain if successful.
+ */
+struct dmar_domain *si_domain;
+
 /* devices under the same p2p bridge are owned in one domain */
 #define DOMAIN_FLAG_P2P_MULTIPLE_DEVICES (1 << 0)
 
@@ -225,6 +234,9 @@ static inline bool dma_pte_present(struct dma_pte *pte)
  */
 #define DOMAIN_FLAG_VIRTUAL_MACHINE    (1 << 1)
 
+/* si_domain contains mulitple devices */
+#define DOMAIN_FLAG_STATIC_IDENTITY    (1 << 2)
+
 struct dmar_domain {
        int     id;                     /* domain id */
        unsigned long iommu_bmp;        /* bitmap of iommus this domain uses*/
@@ -435,12 +447,14 @@ int iommu_calculate_agaw(struct intel_iommu *iommu)
        return __iommu_calculate_agaw(iommu, DEFAULT_DOMAIN_ADDRESS_WIDTH);
 }
 
-/* in native case, each domain is related to only one iommu */
+/* This functionin only returns single iommu in a domain */
 static struct intel_iommu *domain_get_iommu(struct dmar_domain *domain)
 {
        int iommu_id;
 
+       /* si_domain and vm domain should not get here. */
        BUG_ON(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE);
+       BUG_ON(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY);
 
        iommu_id = find_first_bit(&domain->iommu_bmp, g_num_of_iommus);
        if (iommu_id < 0 || iommu_id >= g_num_of_iommus)
@@ -1189,48 +1203,71 @@ void free_dmar_iommu(struct intel_iommu *iommu)
        free_context_table(iommu);
 }
 
-static struct dmar_domain * iommu_alloc_domain(struct intel_iommu *iommu)
+static struct dmar_domain *alloc_domain(void)
 {
-       unsigned long num;
-       unsigned long ndomains;
        struct dmar_domain *domain;
-       unsigned long flags;
 
        domain = alloc_domain_mem();
        if (!domain)
                return NULL;
 
+       memset(&domain->iommu_bmp, 0, sizeof(unsigned long));
+       domain->flags = 0;
+
+       return domain;
+}
+
+static int iommu_attach_domain(struct dmar_domain *domain,
+                              struct intel_iommu *iommu)
+{
+       int num;
+       unsigned long ndomains;
+       unsigned long flags;
+
        ndomains = cap_ndoms(iommu->cap);
 
        spin_lock_irqsave(&iommu->lock, flags);
+
        num = find_first_zero_bit(iommu->domain_ids, ndomains);
        if (num >= ndomains) {
                spin_unlock_irqrestore(&iommu->lock, flags);
-               free_domain_mem(domain);
                printk(KERN_ERR "IOMMU: no free domain ids\n");
-               return NULL;
+               return -ENOMEM;
        }
 
-       set_bit(num, iommu->domain_ids);
        domain->id = num;
-       memset(&domain->iommu_bmp, 0, sizeof(unsigned long));
+       set_bit(num, iommu->domain_ids);
        set_bit(iommu->seq_id, &domain->iommu_bmp);
-       domain->flags = 0;
        iommu->domains[num] = domain;
        spin_unlock_irqrestore(&iommu->lock, flags);
 
-       return domain;
+       return 0;
 }
 
-static void iommu_free_domain(struct dmar_domain *domain)
+static void iommu_detach_domain(struct dmar_domain *domain,
+                               struct intel_iommu *iommu)
 {
        unsigned long flags;
-       struct intel_iommu *iommu;
-
-       iommu = domain_get_iommu(domain);
+       int num, ndomains;
+       int found = 0;
 
        spin_lock_irqsave(&iommu->lock, flags);
-       clear_bit(domain->id, iommu->domain_ids);
+       ndomains = cap_ndoms(iommu->cap);
+       num = find_first_bit(iommu->domain_ids, ndomains);
+       for (; num < ndomains; ) {
+               if (iommu->domains[num] == domain) {
+                       found = 1;
+                       break;
+               }
+               num = find_next_bit(iommu->domain_ids,
+                                   cap_ndoms(iommu->cap), num+1);
+       }
+
+       if (found) {
+               clear_bit(num, iommu->domain_ids);
+               clear_bit(iommu->seq_id, &domain->iommu_bmp);
+               iommu->domains[num] = NULL;
+       }
        spin_unlock_irqrestore(&iommu->lock, flags);
 }
 
@@ -1350,6 +1387,8 @@ static int domain_init(struct dmar_domain *domain, int guest_width)
 
 static void domain_exit(struct dmar_domain *domain)
 {
+       struct dmar_drhd_unit *drhd;
+       struct intel_iommu *iommu;
        u64 end;
 
        /* Domain 0 is reserved, so dont process it */
@@ -1368,7 +1407,10 @@ static void domain_exit(struct dmar_domain *domain)
        /* free page tables */
        dma_pte_free_pagetable(domain, 0, end);
 
-       iommu_free_domain(domain);
+       for_each_active_iommu(iommu, drhd)
+               if (test_bit(iommu->seq_id, &domain->iommu_bmp))
+                       iommu_detach_domain(domain, iommu);
+
        free_domain_mem(domain);
 }
 
@@ -1408,7 +1450,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
        id = domain->id;
        pgd = domain->pgd;
 
-       if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) {
+       if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE ||
+           domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) {
                int found = 0;
 
                /* find an available domain id for this device in iommu */
@@ -1433,6 +1476,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
                        }
 
                        set_bit(num, iommu->domain_ids);
+                       set_bit(iommu->seq_id, &domain->iommu_bmp);
                        iommu->domains[num] = domain;
                        id = num;
                }
@@ -1675,6 +1719,7 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
        unsigned long flags;
        int bus = 0, devfn = 0;
        int segment;
+       int ret;
 
        domain = find_domain(pdev);
        if (domain)
@@ -1707,6 +1752,10 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
                }
        }
 
+       domain = alloc_domain();
+       if (!domain)
+               goto error;
+
        /* Allocate new domain for the device */
        drhd = dmar_find_matched_drhd_unit(pdev);
        if (!drhd) {
@@ -1716,9 +1765,11 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
        }
        iommu = drhd->iommu;
 
-       domain = iommu_alloc_domain(iommu);
-       if (!domain)
+       ret = iommu_attach_domain(domain, iommu);
+       if (ret) {
+               domain_exit(domain);
                goto error;
+       }
 
        if (domain_init(domain, gaw)) {
                domain_exit(domain);
@@ -1792,6 +1843,8 @@ error:
        return find_domain(pdev);
 }
 
+static int iommu_identity_mapping;
+
 static int iommu_prepare_identity_map(struct pci_dev *pdev,
                                      unsigned long long start,
                                      unsigned long long end)
@@ -1804,8 +1857,11 @@ static int iommu_prepare_identity_map(struct pci_dev *pdev,
        printk(KERN_INFO
                "IOMMU: Setting identity map for device %s [0x%Lx - 0x%Lx]\n",
                pci_name(pdev), start, end);
-       /* page table init */
-       domain = get_domain_for_dev(pdev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
+       if (iommu_identity_mapping)
+               domain = si_domain;
+       else
+               /* page table init */
+               domain = get_domain_for_dev(pdev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
        if (!domain)
                return -ENOMEM;
 
@@ -1952,7 +2008,110 @@ static int __init init_context_pass_through(void)
        return 0;
 }
 
-static int __init init_dmars(void)
+static int md_domain_init(struct dmar_domain *domain, int guest_width);
+static int si_domain_init(void)
+{
+       struct dmar_drhd_unit *drhd;
+       struct intel_iommu *iommu;
+       int ret = 0;
+
+       si_domain = alloc_domain();
+       if (!si_domain)
+               return -EFAULT;
+
+
+       for_each_active_iommu(iommu, drhd) {
+               ret = iommu_attach_domain(si_domain, iommu);
+               if (ret) {
+                       domain_exit(si_domain);
+                       return -EFAULT;
+               }
+       }
+
+       if (md_domain_init(si_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) {
+               domain_exit(si_domain);
+               return -EFAULT;
+       }
+
+       si_domain->flags = DOMAIN_FLAG_STATIC_IDENTITY;
+
+       return 0;
+}
+
+static void domain_remove_one_dev_info(struct dmar_domain *domain,
+                                         struct pci_dev *pdev);
+static int identity_mapping(struct pci_dev *pdev)
+{
+       struct device_domain_info *info;
+
+       if (likely(!iommu_identity_mapping))
+               return 0;
+
+
+       list_for_each_entry(info, &si_domain->devices, link)
+               if (info->dev == pdev)
+                       return 1;
+       return 0;
+}
+
+static int domain_add_dev_info(struct dmar_domain *domain,
+                                 struct pci_dev *pdev)
+{
+       struct device_domain_info *info;
+       unsigned long flags;
+
+       info = alloc_devinfo_mem();
+       if (!info)
+               return -ENOMEM;
+
+       info->segment = pci_domain_nr(pdev->bus);
+       info->bus = pdev->bus->number;
+       info->devfn = pdev->devfn;
+       info->dev = pdev;
+       info->domain = domain;
+
+       spin_lock_irqsave(&device_domain_lock, flags);
+       list_add(&info->link, &domain->devices);
+       list_add(&info->global, &device_domain_list);
+       pdev->dev.archdata.iommu = info;
+       spin_unlock_irqrestore(&device_domain_lock, flags);
+
+       return 0;
+}
+
+static int iommu_prepare_static_identity_mapping(void)
+{
+       int i;
+       struct pci_dev *pdev = NULL;
+       int ret;
+
+       ret = si_domain_init();
+       if (ret)
+               return -EFAULT;
+
+       printk(KERN_INFO "IOMMU: Setting identity map:\n");
+       for_each_pci_dev(pdev) {
+               for (i = 0; i < e820.nr_map; i++) {
+                       struct e820entry *ei = &e820.map[i];
+
+                       if (ei->type == E820_RAM) {
+                               ret = iommu_prepare_identity_map(pdev,
+                                       ei->addr, ei->addr + ei->size);
+                               if (ret)  {
+                                       printk(KERN_INFO "1:1 mapping to one domain failed.\n");
+                                       return -EFAULT;
+                               }
+                       }
+               }
+               ret = domain_add_dev_info(si_domain, pdev);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int __init init_dmars(void)
 {
        struct dmar_drhd_unit *drhd;
        struct dmar_rmrr_unit *rmrr;
@@ -1961,6 +2120,13 @@ static int __init init_dmars(void)
        int i, ret;
        int pass_through = 1;
 
+       /*
+        * In case pass through can not be enabled, iommu tries to use identity
+        * mapping.
+        */
+       if (iommu_pass_through)
+               iommu_identity_mapping = 1;
+
        /*
         * for each drhd
         *    allocate root
@@ -2090,9 +2256,12 @@ static int __init init_dmars(void)
 
        /*
         * If pass through is not set or not enabled, setup context entries for
-        * identity mappings for rmrr, gfx, and isa.
+        * identity mappings for rmrr, gfx, and isa and may fall back to static
+        * identity mapping if iommu_identity_mapping is set.
         */
        if (!iommu_pass_through) {
+               if (iommu_identity_mapping)
+                       iommu_prepare_static_identity_mapping();
                /*
                 * For each rmrr
                 *   for each dev attached to rmrr
@@ -2107,6 +2276,7 @@ static int __init init_dmars(void)
                 *    endfor
                 * endfor
                 */
+               printk(KERN_INFO "IOMMU: Setting RMRR:\n");
                for_each_rmrr_units(rmrr) {
                        for (i = 0; i < rmrr->devices_cnt; i++) {
                                pdev = rmrr->devices[i];
@@ -2248,6 +2418,52 @@ get_valid_domain_for_dev(struct pci_dev *pdev)
        return domain;
 }
 
+static int iommu_dummy(struct pci_dev *pdev)
+{
+       return pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO;
+}
+
+/* Check if the pdev needs to go through non-identity map and unmap process.*/
+static int iommu_no_mapping(struct pci_dev *pdev)
+{
+       int found;
+
+       if (!iommu_identity_mapping)
+               return iommu_dummy(pdev);
+
+       found = identity_mapping(pdev);
+       if (found) {
+               if (pdev->dma_mask > DMA_BIT_MASK(32))
+                       return 1;
+               else {
+                       /*
+                        * 32 bit DMA is removed from si_domain and fall back
+                        * to non-identity mapping.
+                        */
+                       domain_remove_one_dev_info(si_domain, pdev);
+                       printk(KERN_INFO "32bit %s uses non-identity mapping\n",
+                              pci_name(pdev));
+                       return 0;
+               }
+       } else {
+               /*
+                * In case of a detached 64 bit DMA device from vm, the device
+                * is put into si_domain for identity mapping.
+                */
+               if (pdev->dma_mask > DMA_BIT_MASK(32)) {
+                       int ret;
+                       ret = domain_add_dev_info(si_domain, pdev);
+                       if (!ret) {
+                               printk(KERN_INFO "64bit %s uses identity mapping\n",
+                                      pci_name(pdev));
+                               return 1;
+                       }
+               }
+       }
+
+       return iommu_dummy(pdev);
+}
+
 static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
                                     size_t size, int dir, u64 dma_mask)
 {
@@ -2260,7 +2476,8 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
        struct intel_iommu *iommu;
 
        BUG_ON(dir == DMA_NONE);
-       if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
+
+       if (iommu_no_mapping(pdev))
                return paddr;
 
        domain = get_valid_domain_for_dev(pdev);
@@ -2401,8 +2618,9 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
        struct iova *iova;
        struct intel_iommu *iommu;
 
-       if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
+       if (iommu_no_mapping(pdev))
                return;
+
        domain = find_domain(pdev);
        BUG_ON(!domain);
 
@@ -2492,7 +2710,7 @@ static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist,
        struct scatterlist *sg;
        struct intel_iommu *iommu;
 
-       if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
+       if (iommu_no_mapping(pdev))
                return;
 
        domain = find_domain(pdev);
@@ -2553,7 +2771,7 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
        struct intel_iommu *iommu;
 
        BUG_ON(dir == DMA_NONE);
-       if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
+       if (iommu_no_mapping(pdev))
                return intel_nontranslate_map_sg(hwdev, sglist, nelems, dir);
 
        domain = get_valid_domain_for_dev(pdev);
@@ -2951,31 +3169,6 @@ int __init intel_iommu_init(void)
        return 0;
 }
 
-static int vm_domain_add_dev_info(struct dmar_domain *domain,
-                                 struct pci_dev *pdev)
-{
-       struct device_domain_info *info;
-       unsigned long flags;
-
-       info = alloc_devinfo_mem();
-       if (!info)
-               return -ENOMEM;
-
-       info->segment = pci_domain_nr(pdev->bus);
-       info->bus = pdev->bus->number;
-       info->devfn = pdev->devfn;
-       info->dev = pdev;
-       info->domain = domain;
-
-       spin_lock_irqsave(&device_domain_lock, flags);
-       list_add(&info->link, &domain->devices);
-       list_add(&info->global, &device_domain_list);
-       pdev->dev.archdata.iommu = info;
-       spin_unlock_irqrestore(&device_domain_lock, flags);
-
-       return 0;
-}
-
 static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
                                           struct pci_dev *pdev)
 {
@@ -3003,7 +3196,7 @@ static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
        }
 }
 
-static void vm_domain_remove_one_dev_info(struct dmar_domain *domain,
+static void domain_remove_one_dev_info(struct dmar_domain *domain,
                                          struct pci_dev *pdev)
 {
        struct device_domain_info *info;
@@ -3136,7 +3329,7 @@ static struct dmar_domain *iommu_alloc_vm_domain(void)
        return domain;
 }
 
-static int vm_domain_init(struct dmar_domain *domain, int guest_width)
+static int md_domain_init(struct dmar_domain *domain, int guest_width)
 {
        int adjust_width;
 
@@ -3227,7 +3420,7 @@ static int intel_iommu_domain_init(struct iommu_domain *domain)
                        "intel_iommu_domain_init: dmar_domain == NULL\n");
                return -ENOMEM;
        }
-       if (vm_domain_init(dmar_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) {
+       if (md_domain_init(dmar_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) {
                printk(KERN_ERR
                        "intel_iommu_domain_init() failed\n");
                vm_domain_exit(dmar_domain);
@@ -3262,8 +3455,9 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
 
                old_domain = find_domain(pdev);
                if (old_domain) {
-                       if (dmar_domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE)
-                               vm_domain_remove_one_dev_info(old_domain, pdev);
+                       if (dmar_domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE ||
+                           dmar_domain->flags & DOMAIN_FLAG_STATIC_IDENTITY)
+                               domain_remove_one_dev_info(old_domain, pdev);
                        else
                                domain_remove_dev_info(old_domain);
                }
@@ -3285,7 +3479,7 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
                return -EFAULT;
        }
 
-       ret = vm_domain_add_dev_info(dmar_domain, pdev);
+       ret = domain_add_dev_info(dmar_domain, pdev);
        if (ret)
                return ret;
 
@@ -3299,7 +3493,7 @@ static void intel_iommu_detach_device(struct iommu_domain *domain,
        struct dmar_domain *dmar_domain = domain->priv;
        struct pci_dev *pdev = to_pci_dev(dev);
 
-       vm_domain_remove_one_dev_info(dmar_domain, pdev);
+       domain_remove_one_dev_info(dmar_domain, pdev);
 }
 
 static int intel_iommu_map_range(struct iommu_domain *domain,
index 1e83c8c5f9859d29229bec9225205eb5e39e5253..4f5b8712931f0d34c1702440a80ff0bb71f46964 100644 (file)
@@ -10,6 +10,8 @@
 #include <linux/intel-iommu.h>
 #include "intr_remapping.h"
 #include <acpi/acpi.h>
+#include <asm/pci-direct.h>
+#include "pci.h"
 
 static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
 static int ir_ioapic_num;
@@ -314,7 +316,8 @@ int modify_irte(int irq, struct irte *irte_modified)
        index = irq_iommu->irte_index + irq_iommu->sub_handle;
        irte = &iommu->ir_table->base[index];
 
-       set_64bit((unsigned long *)irte, irte_modified->low);
+       set_64bit((unsigned long *)&irte->low, irte_modified->low);
+       set_64bit((unsigned long *)&irte->high, irte_modified->high);
        __iommu_flush_cache(iommu, irte, sizeof(*irte));
 
        rc = qi_flush_iec(iommu, index, 0);
@@ -369,12 +372,32 @@ struct intel_iommu *map_dev_to_ir(struct pci_dev *dev)
        return drhd->iommu;
 }
 
+static int clear_entries(struct irq_2_iommu *irq_iommu)
+{
+       struct irte *start, *entry, *end;
+       struct intel_iommu *iommu;
+       int index;
+
+       if (irq_iommu->sub_handle)
+               return 0;
+
+       iommu = irq_iommu->iommu;
+       index = irq_iommu->irte_index + irq_iommu->sub_handle;
+
+       start = iommu->ir_table->base + index;
+       end = start + (1 << irq_iommu->irte_mask);
+
+       for (entry = start; entry < end; entry++) {
+               set_64bit((unsigned long *)&entry->low, 0);
+               set_64bit((unsigned long *)&entry->high, 0);
+       }
+
+       return qi_flush_iec(iommu, index, irq_iommu->irte_mask);
+}
+
 int free_irte(int irq)
 {
        int rc = 0;
-       int index, i;
-       struct irte *irte;
-       struct intel_iommu *iommu;
        struct irq_2_iommu *irq_iommu;
        unsigned long flags;
 
@@ -385,16 +408,7 @@ int free_irte(int irq)
                return -1;
        }
 
-       iommu = irq_iommu->iommu;
-
-       index = irq_iommu->irte_index + irq_iommu->sub_handle;
-       irte = &iommu->ir_table->base[index];
-
-       if (!irq_iommu->sub_handle) {
-               for (i = 0; i < (1 << irq_iommu->irte_mask); i++)
-                       set_64bit((unsigned long *)(irte + i), 0);
-               rc = qi_flush_iec(iommu, index, irq_iommu->irte_mask);
-       }
+       rc = clear_entries(irq_iommu);
 
        irq_iommu->iommu = NULL;
        irq_iommu->irte_index = 0;
@@ -406,6 +420,91 @@ int free_irte(int irq)
        return rc;
 }
 
+/*
+ * source validation type
+ */
+#define SVT_NO_VERIFY          0x0  /* no verification is required */
+#define SVT_VERIFY_SID_SQ      0x1  /* verify using SID and SQ fiels */
+#define SVT_VERIFY_BUS         0x2  /* verify bus of request-id */
+
+/*
+ * source-id qualifier
+ */
+#define SQ_ALL_16      0x0  /* verify all 16 bits of request-id */
+#define SQ_13_IGNORE_1 0x1  /* verify most significant 13 bits, ignore
+                             * the third least significant bit
+                             */
+#define SQ_13_IGNORE_2 0x2  /* verify most significant 13 bits, ignore
+                             * the second and third least significant bits
+                             */
+#define SQ_13_IGNORE_3 0x3  /* verify most significant 13 bits, ignore
+                             * the least three significant bits
+                             */
+
+/*
+ * set SVT, SQ and SID fields of irte to verify
+ * source ids of interrupt requests
+ */
+static void set_irte_sid(struct irte *irte, unsigned int svt,
+                        unsigned int sq, unsigned int sid)
+{
+       irte->svt = svt;
+       irte->sq = sq;
+       irte->sid = sid;
+}
+
+int set_ioapic_sid(struct irte *irte, int apic)
+{
+       int i;
+       u16 sid = 0;
+
+       if (!irte)
+               return -1;
+
+       for (i = 0; i < MAX_IO_APICS; i++) {
+               if (ir_ioapic[i].id == apic) {
+                       sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn;
+                       break;
+               }
+       }
+
+       if (sid == 0) {
+               pr_warning("Failed to set source-id of IOAPIC (%d)\n", apic);
+               return -1;
+       }
+
+       set_irte_sid(irte, 1, 0, sid);
+
+       return 0;
+}
+
+int set_msi_sid(struct irte *irte, struct pci_dev *dev)
+{
+       struct pci_dev *bridge;
+
+       if (!irte || !dev)
+               return -1;
+
+       /* PCIe device or Root Complex integrated PCI device */
+       if (dev->is_pcie || !dev->bus->parent) {
+               set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
+                            (dev->bus->number << 8) | dev->devfn);
+               return 0;
+       }
+
+       bridge = pci_find_upstream_pcie_bridge(dev);
+       if (bridge) {
+               if (bridge->is_pcie) /* this is a PCIE-to-PCI/PCIX bridge */
+                       set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
+                               (bridge->bus->number << 8) | dev->bus->number);
+               else /* this is a legacy PCI bridge */
+                       set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
+                               (bridge->bus->number << 8) | bridge->devfn);
+       }
+
+       return 0;
+}
+
 static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
 {
        u64 addr;
@@ -612,6 +711,35 @@ error:
        return -1;
 }
 
+static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
+                                     struct intel_iommu *iommu)
+{
+       struct acpi_dmar_pci_path *path;
+       u8 bus;
+       int count;
+
+       bus = scope->bus;
+       path = (struct acpi_dmar_pci_path *)(scope + 1);
+       count = (scope->length - sizeof(struct acpi_dmar_device_scope))
+               / sizeof(struct acpi_dmar_pci_path);
+
+       while (--count > 0) {
+               /*
+                * Access PCI directly due to the PCI
+                * subsystem isn't initialized yet.
+                */
+               bus = read_pci_config_byte(bus, path->dev, path->fn,
+                                          PCI_SECONDARY_BUS);
+               path++;
+       }
+
+       ir_ioapic[ir_ioapic_num].bus   = bus;
+       ir_ioapic[ir_ioapic_num].devfn = PCI_DEVFN(path->dev, path->fn);
+       ir_ioapic[ir_ioapic_num].iommu = iommu;
+       ir_ioapic[ir_ioapic_num].id    = scope->enumeration_id;
+       ir_ioapic_num++;
+}
+
 static int ir_parse_ioapic_scope(struct acpi_dmar_header *header,
                                 struct intel_iommu *iommu)
 {
@@ -636,9 +764,7 @@ static int ir_parse_ioapic_scope(struct acpi_dmar_header *header,
                               " 0x%Lx\n", scope->enumeration_id,
                               drhd->address);
 
-                       ir_ioapic[ir_ioapic_num].iommu = iommu;
-                       ir_ioapic[ir_ioapic_num].id = scope->enumeration_id;
-                       ir_ioapic_num++;
+                       ir_parse_one_ioapic_scope(scope, iommu);
                }
                start += scope->length;
        }
index ca48f0df8ac989bb41889e1efd87db7d5ee5fe10..63a263c18415f8d2f61ce13c6b1918d95c65d19a 100644 (file)
@@ -3,6 +3,8 @@
 struct ioapic_scope {
        struct intel_iommu *iommu;
        unsigned int id;
+       unsigned int bus;       /* PCI bus number */
+       unsigned int devfn;     /* PCI devfn number */
 };
 
 #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
index c682ac53641554a82c3b47ef91a71204530d393e..7232fe7104aa140c20b2b97b9a6bdc5717044747 100644 (file)
@@ -34,10 +34,27 @@ config ACER_WMI
          If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
          here.
 
+config ACERHDF
+       tristate "Acer Aspire One temperature and fan driver"
+       depends on THERMAL && THERMAL_HWMON && ACPI
+       ---help---
+         This is a driver for Acer Aspire One netbooks. It allows to access
+         the temperature sensor and to control the fan.
+
+         After loading this driver the BIOS is still in control of the fan.
+         To let the kernel handle the fan, do:
+         echo -n enabled > /sys/class/thermal/thermal_zone0/mode
+
+         For more information about this driver see
+         <http://piie.net/files/acerhdf_README.txt>
+
+         If you have an Acer Aspire One netbook, say Y or M
+         here.
+
 config ASUS_LAPTOP
-       tristate "Asus Laptop Extras (EXPERIMENTAL)"
+       tristate "Asus Laptop Extras"
        depends on ACPI
-       depends on EXPERIMENTAL && !ACPI_ASUS
+       depends on !ACPI_ASUS
        select LEDS_CLASS
        select NEW_LEDS
        select BACKLIGHT_CLASS_DEVICE
@@ -45,12 +62,12 @@ config ASUS_LAPTOP
        ---help---
          This is the new Linux driver for Asus laptops. It may also support some
          MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate
-         standard ACPI events that go through /proc/acpi/events. It also adds
+         standard ACPI events and input events. It also adds
          support for video output switching, LCD backlight control, Bluetooth and
          Wlan control, and most importantly, allows you to blink those fancy LEDs.
 
          For more information and a userspace daemon for handling the extra
-         buttons see <http://acpi4asus.sf.net/>.
+         buttons see <http://acpi4asus.sf.net>.
 
          If you have an ACPI-compatible ASUS laptop, say Y or M here.
 
@@ -342,7 +359,10 @@ config EEEPC_LAPTOP
        select HWMON
        ---help---
          This driver supports the Fn-Fx keys on Eee PC laptops.
-         It also adds the ability to switch camera/wlan on/off.
+
+         It  also gives access to some extra laptop functionalities like
+         Bluetooth, backlight and allows powering on/off some other
+         devices.
 
          If you have an Eee PC laptop, say Y or M here.
 
@@ -369,7 +389,7 @@ config ACPI_WMI
          any ACPI-WMI devices.
 
 config ACPI_ASUS
-       tristate "ASUS/Medion Laptop Extras"
+       tristate "ASUS/Medion Laptop Extras (DEPRECATED)"
        depends on ACPI
        select BACKLIGHT_CLASS_DEVICE
        ---help---
@@ -390,7 +410,7 @@ config ACPI_ASUS
          parameters.
 
          More information and a userspace daemon for handling the extra buttons
-         at <http://sourceforge.net/projects/acpi4asus/>.
+         at <http://acpi4asus.sf.net>.
 
          If you have an ACPI-compatible ASUS laptop, say Y or M here. This
          driver is still under development, so if your laptop is unsupported or
index e40c7bd1b87e55358a4bb02235e5e6ca7720bfdf..641b8bfa5538a7ca3ed0ebfc62410552bbd8dfb1 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_COMPAL_LAPTOP)     += compal-laptop.o
 obj-$(CONFIG_DELL_LAPTOP)      += dell-laptop.o
 obj-$(CONFIG_DELL_WMI)         += dell-wmi.o
 obj-$(CONFIG_ACER_WMI)         += acer-wmi.o
+obj-$(CONFIG_ACERHDF)          += acerhdf.o
 obj-$(CONFIG_HP_WMI)           += hp-wmi.o
 obj-$(CONFIG_TC1100_WMI)       += tc1100-wmi.o
 obj-$(CONFIG_SONY_LAPTOP)      += sony-laptop.o
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
new file mode 100644 (file)
index 0000000..bdfee17
--- /dev/null
@@ -0,0 +1,602 @@
+/*
+ * acerhdf - A driver which monitors the temperature
+ *           of the aspire one netbook, turns on/off the fan
+ *           as soon as the upper/lower threshold is reached.
+ *
+ * (C) 2009 - Peter Feuerer     peter (a) piie.net
+ *                              http://piie.net
+ *     2009 Borislav Petkov <petkovbb@gmail.com>
+ *
+ * Inspired by and many thanks to:
+ *  o acerfand   - Rachel Greenham
+ *  o acer_ec.pl - Michael Kurz     michi.kurz (at) googlemail.com
+ *               - Petr Tomasek     tomasek (#) etf,cuni,cz
+ *               - Carlos Corbacho  cathectic (at) gmail.com
+ *  o lkml       - Matthew Garrett
+ *               - Borislav Petkov
+ *               - Andreas Mohr
+ *
+ *  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 pr_fmt(fmt) "acerhdf: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/dmi.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/sched.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+
+/*
+ * The driver is started with "kernel mode off" by default. That means, the BIOS
+ * is still in control of the fan. In this mode the driver allows to read the
+ * temperature of the cpu and a userspace tool may take over control of the fan.
+ * If the driver is switched to "kernel mode" (e.g. via module parameter) the
+ * driver is in full control of the fan. If you want the module to be started in
+ * kernel mode by default, define the following:
+ */
+#undef START_IN_KERNEL_MODE
+
+#define DRV_VER "0.5.13"
+
+/*
+ * According to the Atom N270 datasheet,
+ * (http://download.intel.com/design/processor/datashts/320032.pdf) the
+ * CPU's optimal operating limits denoted in junction temperature as
+ * measured by the on-die thermal monitor are within 0 <= Tj <= 90. So,
+ * assume 89°C is critical temperature.
+ */
+#define ACERHDF_TEMP_CRIT 89
+#define ACERHDF_FAN_OFF 0
+#define ACERHDF_FAN_AUTO 1
+
+/*
+ * No matter what value the user puts into the fanon variable, turn on the fan
+ * at 80 degree Celsius to prevent hardware damage
+ */
+#define ACERHDF_MAX_FANON 80
+
+/*
+ * Maximum interval between two temperature checks is 15 seconds, as the die
+ * can get hot really fast under heavy load (plus we shouldn't forget about
+ * possible impact of _external_ aggressive sources such as heaters, sun etc.)
+ */
+#define ACERHDF_MAX_INTERVAL 15
+
+#ifdef START_IN_KERNEL_MODE
+static int kernelmode = 1;
+#else
+static int kernelmode;
+#endif
+
+static unsigned int interval = 10;
+static unsigned int fanon = 63;
+static unsigned int fanoff = 58;
+static unsigned int verbose;
+static unsigned int fanstate = ACERHDF_FAN_AUTO;
+static char force_bios[16];
+static unsigned int prev_interval;
+struct thermal_zone_device *thz_dev;
+struct thermal_cooling_device *cl_dev;
+struct platform_device *acerhdf_dev;
+
+module_param(kernelmode, uint, 0);
+MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off");
+module_param(interval, uint, 0600);
+MODULE_PARM_DESC(interval, "Polling interval of temperature check");
+module_param(fanon, uint, 0600);
+MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature");
+module_param(fanoff, uint, 0600);
+MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature");
+module_param(verbose, uint, 0600);
+MODULE_PARM_DESC(verbose, "Enable verbose dmesg output");
+module_param_string(force_bios, force_bios, 16, 0);
+MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check");
+
+/* BIOS settings */
+struct bios_settings_t {
+       const char *vendor;
+       const char *version;
+       unsigned char fanreg;
+       unsigned char tempreg;
+       unsigned char fancmd[2]; /* fan off and auto commands */
+};
+
+/* Register addresses and values for different BIOS versions */
+static const struct bios_settings_t bios_tbl[] = {
+       {"Acer", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
+       {"Acer", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
+       {"Acer", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
+       {"Acer", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
+       {"Acer", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
+       {"Acer", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
+       {"Acer", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
+       {"Acer", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
+       {"Gateway", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
+       {"Packard Bell", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
+       {"", "", 0, 0, {0, 0} }
+};
+
+static const struct bios_settings_t *bios_cfg __read_mostly;
+
+
+static int acerhdf_get_temp(int *temp)
+{
+       u8 read_temp;
+
+       if (ec_read(bios_cfg->tempreg, &read_temp))
+               return -EINVAL;
+
+       *temp = read_temp;
+
+       return 0;
+}
+
+static int acerhdf_get_fanstate(int *state)
+{
+       u8 fan;
+       bool tmp;
+
+       if (ec_read(bios_cfg->fanreg, &fan))
+               return -EINVAL;
+
+       tmp = (fan == bios_cfg->fancmd[ACERHDF_FAN_OFF]);
+       *state = tmp ? ACERHDF_FAN_OFF : ACERHDF_FAN_AUTO;
+
+       return 0;
+}
+
+static void acerhdf_change_fanstate(int state)
+{
+       unsigned char cmd;
+
+       if (verbose)
+               pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ?
+                               "OFF" : "ON");
+
+       if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) {
+               pr_err("invalid fan state %d requested, setting to auto!\n",
+                       state);
+               state = ACERHDF_FAN_AUTO;
+       }
+
+       cmd = bios_cfg->fancmd[state];
+       fanstate = state;
+
+       ec_write(bios_cfg->fanreg, cmd);
+}
+
+static void acerhdf_check_param(struct thermal_zone_device *thermal)
+{
+       if (fanon > ACERHDF_MAX_FANON) {
+               pr_err("fanon temperature too high, set to %d\n",
+                               ACERHDF_MAX_FANON);
+               fanon = ACERHDF_MAX_FANON;
+       }
+
+       if (kernelmode && prev_interval != interval) {
+               if (interval > ACERHDF_MAX_INTERVAL) {
+                       pr_err("interval too high, set to %d\n",
+                               ACERHDF_MAX_INTERVAL);
+                       interval = ACERHDF_MAX_INTERVAL;
+               }
+               if (verbose)
+                       pr_notice("interval changed to: %d\n",
+                                       interval);
+               thermal->polling_delay = interval*1000;
+               prev_interval = interval;
+       }
+}
+
+/*
+ * This is the thermal zone callback which does the delayed polling of the fan
+ * state. We do check /sysfs-originating settings here in acerhdf_check_param()
+ * as late as the polling interval is since we can't do that in the respective
+ * accessors of the module parameters.
+ */
+static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal,
+                              unsigned long *t)
+{
+       int temp, err = 0;
+
+       acerhdf_check_param(thermal);
+
+       err = acerhdf_get_temp(&temp);
+       if (err)
+               return err;
+
+       if (verbose)
+               pr_notice("temp %d\n", temp);
+
+       *t = temp;
+       return 0;
+}
+
+static int acerhdf_bind(struct thermal_zone_device *thermal,
+                       struct thermal_cooling_device *cdev)
+{
+       /* if the cooling device is the one from acerhdf bind it */
+       if (cdev != cl_dev)
+               return 0;
+
+       if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
+               pr_err("error binding cooling dev\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int acerhdf_unbind(struct thermal_zone_device *thermal,
+                         struct thermal_cooling_device *cdev)
+{
+       if (cdev != cl_dev)
+               return 0;
+
+       if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
+               pr_err("error unbinding cooling dev\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static inline void acerhdf_revert_to_bios_mode(void)
+{
+       acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
+       kernelmode = 0;
+       if (thz_dev)
+               thz_dev->polling_delay = 0;
+       pr_notice("kernel mode fan control OFF\n");
+}
+static inline void acerhdf_enable_kernelmode(void)
+{
+       kernelmode = 1;
+
+       thz_dev->polling_delay = interval*1000;
+       thermal_zone_device_update(thz_dev);
+       pr_notice("kernel mode fan control ON\n");
+}
+
+static int acerhdf_get_mode(struct thermal_zone_device *thermal,
+                           enum thermal_device_mode *mode)
+{
+       if (verbose)
+               pr_notice("kernel mode fan control %d\n", kernelmode);
+
+       *mode = (kernelmode) ? THERMAL_DEVICE_ENABLED
+                            : THERMAL_DEVICE_DISABLED;
+
+       return 0;
+}
+
+/*
+ * set operation mode;
+ * enabled: the thermal layer of the kernel takes care about
+ *          the temperature and the fan.
+ * disabled: the BIOS takes control of the fan.
+ */
+static int acerhdf_set_mode(struct thermal_zone_device *thermal,
+                           enum thermal_device_mode mode)
+{
+       if (mode == THERMAL_DEVICE_DISABLED && kernelmode)
+               acerhdf_revert_to_bios_mode();
+       else if (mode == THERMAL_DEVICE_ENABLED && !kernelmode)
+               acerhdf_enable_kernelmode();
+
+       return 0;
+}
+
+static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip,
+                                enum thermal_trip_type *type)
+{
+       if (trip == 0)
+               *type = THERMAL_TRIP_ACTIVE;
+
+       return 0;
+}
+
+static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
+                                unsigned long *temp)
+{
+       if (trip == 0)
+               *temp = fanon;
+
+       return 0;
+}
+
+static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal,
+                                unsigned long *temperature)
+{
+       *temperature = ACERHDF_TEMP_CRIT;
+       return 0;
+}
+
+/* bind callback functions to thermalzone */
+struct thermal_zone_device_ops acerhdf_dev_ops = {
+       .bind = acerhdf_bind,
+       .unbind = acerhdf_unbind,
+       .get_temp = acerhdf_get_ec_temp,
+       .get_mode = acerhdf_get_mode,
+       .set_mode = acerhdf_set_mode,
+       .get_trip_type = acerhdf_get_trip_type,
+       .get_trip_temp = acerhdf_get_trip_temp,
+       .get_crit_temp = acerhdf_get_crit_temp,
+};
+
+
+/*
+ * cooling device callback functions
+ * get maximal fan cooling state
+ */
+static int acerhdf_get_max_state(struct thermal_cooling_device *cdev,
+                                unsigned long *state)
+{
+       *state = 1;
+
+       return 0;
+}
+
+static int acerhdf_get_cur_state(struct thermal_cooling_device *cdev,
+                                unsigned long *state)
+{
+       int err = 0, tmp;
+
+       err = acerhdf_get_fanstate(&tmp);
+       if (err)
+               return err;
+
+       *state = (tmp == ACERHDF_FAN_AUTO) ? 1 : 0;
+       return 0;
+}
+
+/* change current fan state - is overwritten when running in kernel mode */
+static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev,
+                                unsigned long state)
+{
+       int cur_temp, cur_state, err = 0;
+
+       if (!kernelmode)
+               return 0;
+
+       err = acerhdf_get_temp(&cur_temp);
+       if (err) {
+               pr_err("error reading temperature, hand off control to BIOS\n");
+               goto err_out;
+       }
+
+       err = acerhdf_get_fanstate(&cur_state);
+       if (err) {
+               pr_err("error reading fan state, hand off control to BIOS\n");
+               goto err_out;
+       }
+
+       if (state == 0) {
+               /* turn fan off only if below fanoff temperature */
+               if ((cur_state == ACERHDF_FAN_AUTO) &&
+                   (cur_temp < fanoff))
+                       acerhdf_change_fanstate(ACERHDF_FAN_OFF);
+       } else {
+               if (cur_state == ACERHDF_FAN_OFF)
+                       acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
+       }
+       return 0;
+
+err_out:
+       acerhdf_revert_to_bios_mode();
+       return -EINVAL;
+}
+
+/* bind fan callbacks to fan device */
+struct thermal_cooling_device_ops acerhdf_cooling_ops = {
+       .get_max_state = acerhdf_get_max_state,
+       .get_cur_state = acerhdf_get_cur_state,
+       .set_cur_state = acerhdf_set_cur_state,
+};
+
+/* suspend / resume functionality */
+static int acerhdf_suspend(struct platform_device *dev, pm_message_t state)
+{
+       if (kernelmode)
+               acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
+
+       if (verbose)
+               pr_notice("going suspend\n");
+
+       return 0;
+}
+
+static int acerhdf_resume(struct platform_device *device)
+{
+       if (verbose)
+               pr_notice("resuming\n");
+
+       return 0;
+}
+
+static int __devinit acerhdf_probe(struct platform_device *device)
+{
+       return 0;
+}
+
+static int acerhdf_remove(struct platform_device *device)
+{
+       return 0;
+}
+
+struct platform_driver acerhdf_drv = {
+       .driver = {
+               .name = "acerhdf",
+               .owner = THIS_MODULE,
+       },
+       .probe = acerhdf_probe,
+       .remove = acerhdf_remove,
+       .suspend = acerhdf_suspend,
+       .resume = acerhdf_resume,
+};
+
+
+/* check hardware */
+static int acerhdf_check_hardware(void)
+{
+       char const *vendor, *version, *product;
+       int i;
+
+       /* get BIOS data */
+       vendor  = dmi_get_system_info(DMI_SYS_VENDOR);
+       version = dmi_get_system_info(DMI_BIOS_VERSION);
+       product = dmi_get_system_info(DMI_PRODUCT_NAME);
+
+       pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER);
+
+       if (!force_bios[0]) {
+               if (strncmp(product, "AO", 2)) {
+                       pr_err("no Aspire One hardware found\n");
+                       return -EINVAL;
+               }
+       } else {
+               pr_info("forcing BIOS version: %s\n", version);
+               version = force_bios;
+               kernelmode = 0;
+       }
+
+       if (verbose)
+               pr_info("BIOS info: %s %s, product: %s\n",
+                       vendor, version, product);
+
+       /* search BIOS version and vendor in BIOS settings table */
+       for (i = 0; bios_tbl[i].version[0]; i++) {
+               if (!strcmp(bios_tbl[i].vendor, vendor) &&
+                   !strcmp(bios_tbl[i].version, version)) {
+                       bios_cfg = &bios_tbl[i];
+                       break;
+               }
+       }
+
+       if (!bios_cfg) {
+               pr_err("unknown (unsupported) BIOS version %s/%s, "
+                       "please report, aborting!\n", vendor, version);
+               return -EINVAL;
+       }
+
+       /*
+        * if started with kernel mode off, prevent the kernel from switching
+        * off the fan
+        */
+       if (!kernelmode) {
+               pr_notice("Fan control off, to enable do:\n");
+               pr_notice("echo -n \"enabled\" > "
+                       "/sys/class/thermal/thermal_zone0/mode\n");
+       }
+
+       return 0;
+}
+
+static int acerhdf_register_platform(void)
+{
+       int err = 0;
+
+       err = platform_driver_register(&acerhdf_drv);
+       if (err)
+               return err;
+
+       acerhdf_dev = platform_device_alloc("acerhdf", -1);
+       platform_device_add(acerhdf_dev);
+
+       return 0;
+}
+
+static void acerhdf_unregister_platform(void)
+{
+       if (!acerhdf_dev)
+               return;
+
+       platform_device_del(acerhdf_dev);
+       platform_driver_unregister(&acerhdf_drv);
+}
+
+static int acerhdf_register_thermal(void)
+{
+       cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL,
+                                                &acerhdf_cooling_ops);
+
+       if (IS_ERR(cl_dev))
+               return -EINVAL;
+
+       thz_dev = thermal_zone_device_register("acerhdf", 1, NULL,
+                                             &acerhdf_dev_ops, 0, 0, 0,
+                                             (kernelmode) ? interval*1000 : 0);
+       if (IS_ERR(thz_dev))
+               return -EINVAL;
+
+       return 0;
+}
+
+static void acerhdf_unregister_thermal(void)
+{
+       if (cl_dev) {
+               thermal_cooling_device_unregister(cl_dev);
+               cl_dev = NULL;
+       }
+
+       if (thz_dev) {
+               thermal_zone_device_unregister(thz_dev);
+               thz_dev = NULL;
+       }
+}
+
+static int __init acerhdf_init(void)
+{
+       int err = 0;
+
+       err = acerhdf_check_hardware();
+       if (err)
+               goto out_err;
+
+       err = acerhdf_register_platform();
+       if (err)
+               goto err_unreg;
+
+       err = acerhdf_register_thermal();
+       if (err)
+               goto err_unreg;
+
+       return 0;
+
+err_unreg:
+       acerhdf_unregister_thermal();
+       acerhdf_unregister_platform();
+
+out_err:
+       return -ENODEV;
+}
+
+static void __exit acerhdf_exit(void)
+{
+       acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
+       acerhdf_unregister_thermal();
+       acerhdf_unregister_platform();
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter Feuerer");
+MODULE_DESCRIPTION("Aspire One temperature and fan driver");
+MODULE_ALIAS("dmi:*:*Acer*:*:");
+MODULE_ALIAS("dmi:*:*Gateway*:*:");
+MODULE_ALIAS("dmi:*:*Packard Bell*:*:");
+
+module_init(acerhdf_init);
+module_exit(acerhdf_exit);
index bfc1a8892a32f5eb03c8c2752ffb2daff977488c..db657bbeec908e456facb8271fa8624fe0f0a515 100644 (file)
@@ -33,6 +33,8 @@
  *  Sam Lin        - GPS support
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #define ASUS_HOTK_NAME          "Asus Laptop Support"
 #define ASUS_HOTK_CLASS         "hotkey"
 #define ASUS_HOTK_DEVICE_NAME   "Hotkey"
-#define ASUS_HOTK_FILE          "asus-laptop"
+#define ASUS_HOTK_FILE          KBUILD_MODNAME
 #define ASUS_HOTK_PREFIX        "\\_SB.ATKD."
 
+
 /*
  * Some events we use, same for all Asus
  */
@@ -207,13 +210,17 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids);
 
 static int asus_hotk_add(struct acpi_device *device);
 static int asus_hotk_remove(struct acpi_device *device, int type);
+static void asus_hotk_notify(struct acpi_device *device, u32 event);
+
 static struct acpi_driver asus_hotk_driver = {
        .name = ASUS_HOTK_NAME,
        .class = ASUS_HOTK_CLASS,
        .ids = asus_device_ids,
+       .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
        .ops = {
                .add = asus_hotk_add,
                .remove = asus_hotk_remove,
+               .notify = asus_hotk_notify,
                },
 };
 
@@ -323,7 +330,7 @@ static int read_wireless_status(int mask)
 
        rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
        if (ACPI_FAILURE(rv))
-               printk(ASUS_WARNING "Error reading Wireless status\n");
+               pr_warning("Error reading Wireless status\n");
        else
                return (status & mask) ? 1 : 0;
 
@@ -337,7 +344,7 @@ static int read_gps_status(void)
 
        rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status);
        if (ACPI_FAILURE(rv))
-               printk(ASUS_WARNING "Error reading GPS status\n");
+               pr_warning("Error reading GPS status\n");
        else
                return status ? 1 : 0;
 
@@ -377,7 +384,7 @@ static void write_status(acpi_handle handle, int out, int mask)
        }
 
        if (write_acpi_int(handle, NULL, out, NULL))
-               printk(ASUS_WARNING " write failed %x\n", mask);
+               pr_warning(" write failed %x\n", mask);
 }
 
 /* /sys/class/led handlers */
@@ -420,7 +427,7 @@ static int set_lcd_state(int value)
                                              NULL, NULL, NULL);
 
                if (ACPI_FAILURE(status))
-                       printk(ASUS_WARNING "Error switching LCD\n");
+                       pr_warning("Error switching LCD\n");
        }
 
        write_status(NULL, lcd, LCD_ON);
@@ -444,7 +451,7 @@ static int read_brightness(struct backlight_device *bd)
 
        rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
        if (ACPI_FAILURE(rv))
-               printk(ASUS_WARNING "Error reading brightness\n");
+               pr_warning("Error reading brightness\n");
 
        return value;
 }
@@ -457,7 +464,7 @@ static int set_brightness(struct backlight_device *bd, int value)
        /* 0 <= value <= 15 */
 
        if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) {
-               printk(ASUS_WARNING "Error changing brightness\n");
+               pr_warning("Error changing brightness\n");
                ret = -EIO;
        }
 
@@ -587,7 +594,7 @@ static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
        rv = parse_arg(buf, count, &value);
        if (rv > 0) {
                if (write_acpi_int(ledd_set_handle, NULL, value, NULL))
-                       printk(ASUS_WARNING "LED display write failed\n");
+                       pr_warning("LED display write failed\n");
                else
                        hotk->ledd_status = (u32) value;
        }
@@ -632,7 +639,7 @@ static void set_display(int value)
 {
        /* no sanity check needed for now */
        if (write_acpi_int(display_set_handle, NULL, value, NULL))
-               printk(ASUS_WARNING "Error setting display\n");
+               pr_warning("Error setting display\n");
        return;
 }
 
@@ -647,7 +654,7 @@ static int read_display(void)
                rv = acpi_evaluate_integer(display_get_handle, NULL,
                                           NULL, &value);
                if (ACPI_FAILURE(rv))
-                       printk(ASUS_WARNING "Error reading display status\n");
+                       pr_warning("Error reading display status\n");
        }
 
        value &= 0x0F;          /* needed for some models, shouldn't hurt others */
@@ -689,7 +696,7 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
 static void set_light_sens_switch(int value)
 {
        if (write_acpi_int(ls_switch_handle, NULL, value, NULL))
-               printk(ASUS_WARNING "Error setting light sensor switch\n");
+               pr_warning("Error setting light sensor switch\n");
        hotk->light_switch = value;
 }
 
@@ -714,7 +721,7 @@ static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
 static void set_light_sens_level(int value)
 {
        if (write_acpi_int(ls_level_handle, NULL, value, NULL))
-               printk(ASUS_WARNING "Error setting light sensor level\n");
+               pr_warning("Error setting light sensor level\n");
        hotk->light_level = value;
 }
 
@@ -812,7 +819,7 @@ static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode)
        return -EINVAL;
 }
 
-static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
+static void asus_hotk_notify(struct acpi_device *device, u32 event)
 {
        static struct key_entry *key;
        u16 count;
@@ -975,11 +982,11 @@ static int asus_hotk_get_info(void)
         */
        status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
        if (ACPI_FAILURE(status))
-               printk(ASUS_WARNING "Couldn't get the DSDT table header\n");
+               pr_warning("Couldn't get the DSDT table header\n");
 
        /* We have to write 0 on init this far for all ASUS models */
        if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
-               printk(ASUS_ERR "Hotkey initialization failed\n");
+               pr_err("Hotkey initialization failed\n");
                return -ENODEV;
        }
 
@@ -987,9 +994,9 @@ static int asus_hotk_get_info(void)
        status =
            acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
        if (ACPI_FAILURE(status))
-               printk(ASUS_WARNING "Error calling BSTS\n");
+               pr_warning("Error calling BSTS\n");
        else if (bsts_result)
-               printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n",
+               pr_notice("BSTS called, 0x%02x returned\n",
                       (uint) bsts_result);
 
        /* This too ... */
@@ -1020,7 +1027,7 @@ static int asus_hotk_get_info(void)
                return -ENOMEM;
 
        if (*string)
-               printk(ASUS_NOTICE "  %s model detected\n", string);
+               pr_notice("  %s model detected\n", string);
 
        ASUS_HANDLE_INIT(mled_set);
        ASUS_HANDLE_INIT(tled_set);
@@ -1077,7 +1084,7 @@ static int asus_input_init(void)
 
        hotk->inputdev = input_allocate_device();
        if (!hotk->inputdev) {
-               printk(ASUS_INFO "Unable to allocate input device\n");
+               pr_info("Unable to allocate input device\n");
                return 0;
        }
        hotk->inputdev->name = "Asus Laptop extra buttons";
@@ -1096,7 +1103,7 @@ static int asus_input_init(void)
        }
        result = input_register_device(hotk->inputdev);
        if (result) {
-               printk(ASUS_INFO "Unable to register input device\n");
+               pr_info("Unable to register input device\n");
                input_free_device(hotk->inputdev);
        }
        return result;
@@ -1113,7 +1120,7 @@ static int asus_hotk_check(void)
        if (hotk->device->status.present) {
                result = asus_hotk_get_info();
        } else {
-               printk(ASUS_ERR "Hotkey device not present, aborting\n");
+               pr_err("Hotkey device not present, aborting\n");
                return -EINVAL;
        }
 
@@ -1124,13 +1131,12 @@ static int asus_hotk_found;
 
 static int asus_hotk_add(struct acpi_device *device)
 {
-       acpi_status status = AE_OK;
        int result;
 
        if (!device)
                return -EINVAL;
 
-       printk(ASUS_NOTICE "Asus Laptop Support version %s\n",
+       pr_notice("Asus Laptop Support version %s\n",
               ASUS_LAPTOP_VERSION);
 
        hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
@@ -1149,15 +1155,6 @@ static int asus_hotk_add(struct acpi_device *device)
 
        asus_hotk_add_fs();
 
-       /*
-        * We install the handler, it will receive the hotk in parameter, so, we
-        * could add other data to the hotk struct
-        */
-       status = acpi_install_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
-                                            asus_hotk_notify, hotk);
-       if (ACPI_FAILURE(status))
-               printk(ASUS_ERR "Error installing notify handler\n");
-
        asus_hotk_found = 1;
 
        /* WLED and BLED are on by default */
@@ -1198,16 +1195,9 @@ end:
 
 static int asus_hotk_remove(struct acpi_device *device, int type)
 {
-       acpi_status status = 0;
-
        if (!device || !acpi_driver_data(device))
                return -EINVAL;
 
-       status = acpi_remove_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
-                                           asus_hotk_notify);
-       if (ACPI_FAILURE(status))
-               printk(ASUS_ERR "Error removing notify handler\n");
-
        kfree(hotk->name);
        kfree(hotk);
 
@@ -1260,8 +1250,7 @@ static int asus_backlight_init(struct device *dev)
                bd = backlight_device_register(ASUS_HOTK_FILE, dev,
                                               NULL, &asusbl_ops);
                if (IS_ERR(bd)) {
-                       printk(ASUS_ERR
-                              "Could not register asus backlight device\n");
+                       pr_err("Could not register asus backlight device\n");
                        asus_backlight_device = NULL;
                        return PTR_ERR(bd);
                }
@@ -1334,7 +1323,6 @@ out:
 
 static int __init asus_laptop_init(void)
 {
-       struct device *dev;
        int result;
 
        if (acpi_disabled)
@@ -1356,24 +1344,10 @@ static int __init asus_laptop_init(void)
                return -ENODEV;
        }
 
-       dev = acpi_get_physical_device(hotk->device->handle);
-
-       if (!acpi_video_backlight_support()) {
-               result = asus_backlight_init(dev);
-               if (result)
-                       goto fail_backlight;
-       } else
-               printk(ASUS_INFO "Brightness ignored, must be controlled by "
-                      "ACPI video driver\n");
-
        result = asus_input_init();
        if (result)
                goto fail_input;
 
-       result = asus_led_init(dev);
-       if (result)
-               goto fail_led;
-
        /* Register platform stuff */
        result = platform_driver_register(&asuspf_driver);
        if (result)
@@ -1394,8 +1368,27 @@ static int __init asus_laptop_init(void)
        if (result)
                goto fail_sysfs;
 
+       result = asus_led_init(&asuspf_device->dev);
+       if (result)
+               goto fail_led;
+
+       if (!acpi_video_backlight_support()) {
+               result = asus_backlight_init(&asuspf_device->dev);
+               if (result)
+                       goto fail_backlight;
+       } else
+               pr_info("Brightness ignored, must be controlled by "
+                      "ACPI video driver\n");
+
        return 0;
 
+fail_backlight:
+       asus_led_exit();
+
+fail_led:
+       sysfs_remove_group(&asuspf_device->dev.kobj,
+                         &asuspf_attribute_group);
+
 fail_sysfs:
        platform_device_del(asuspf_device);
 
@@ -1406,15 +1399,9 @@ fail_platform_device1:
        platform_driver_unregister(&asuspf_driver);
 
 fail_platform_driver:
-       asus_led_exit();
-
-fail_led:
        asus_input_exit();
 
 fail_input:
-       asus_backlight_exit();
-
-fail_backlight:
 
        return result;
 }
index ba1f7497e4b9a99f5be8d9d9d406ff1f6b170dbd..ddf5240ade8cc0e10358553382ffa68716933ce0 100644 (file)
@@ -455,6 +455,8 @@ static struct asus_hotk *hotk;
  */
 static int asus_hotk_add(struct acpi_device *device);
 static int asus_hotk_remove(struct acpi_device *device, int type);
+static void asus_hotk_notify(struct acpi_device *device, u32 event);
+
 static const struct acpi_device_id asus_device_ids[] = {
        {"ATK0100", 0},
        {"", 0},
@@ -465,9 +467,11 @@ static struct acpi_driver asus_hotk_driver = {
        .name = "asus_acpi",
        .class = ACPI_HOTK_CLASS,
        .ids = asus_device_ids,
+       .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
        .ops = {
                .add = asus_hotk_add,
                .remove = asus_hotk_remove,
+               .notify = asus_hotk_notify,
                },
 };
 
@@ -1101,12 +1105,20 @@ static int asus_hotk_remove_fs(struct acpi_device *device)
        return 0;
 }
 
-static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
+static void asus_hotk_notify(struct acpi_device *device, u32 event)
 {
        /* TODO Find a better way to handle events count. */
        if (!hotk)
                return;
 
+       /*
+        * The BIOS *should* be sending us device events, but apparently
+        * Asus uses system events instead, so just ignore any device
+        * events we get.
+        */
+       if (event > ACPI_MAX_SYS_NOTIFY)
+               return;
+
        if ((event & ~((u32) BR_UP)) < 16)
                hotk->brightness = (event & ~((u32) BR_UP));
        else if ((event & ~((u32) BR_DOWN)) < 16)
@@ -1346,15 +1358,6 @@ static int asus_hotk_add(struct acpi_device *device)
        if (result)
                goto end;
 
-       /*
-        * We install the handler, it will receive the hotk in parameter, so, we
-        * could add other data to the hotk struct
-        */
-       status = acpi_install_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
-                                            asus_hotk_notify, hotk);
-       if (ACPI_FAILURE(status))
-               printk(KERN_ERR "  Error installing notify handler\n");
-
        /* For laptops without GPLV: init the hotk->brightness value */
        if ((!hotk->methods->brightness_get)
            && (!hotk->methods->brightness_status)
@@ -1389,16 +1392,9 @@ end:
 
 static int asus_hotk_remove(struct acpi_device *device, int type)
 {
-       acpi_status status = 0;
-
        if (!device || !acpi_driver_data(device))
                return -EINVAL;
 
-       status = acpi_remove_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
-                                           asus_hotk_notify);
-       if (ACPI_FAILURE(status))
-               printk(KERN_ERR "Asus ACPI: Error removing notify handler\n");
-
        asus_hotk_remove_fs(device);
 
        kfree(hotk);
index 2fab94162147e2b2bc0206bcad3e6b4b76764587..0f900cc9fa7a5131283c14b2aae8e11d7667b949 100644 (file)
@@ -46,10 +46,53 @@ struct key_entry {
        u16 keycode;
 };
 
-enum { KE_KEY, KE_SW, KE_END };
+enum { KE_KEY, KE_SW, KE_IGNORE, KE_END };
+
+/*
+ * Certain keys are flagged as KE_IGNORE. All of these are either
+ * notifications (rather than requests for change) or are also sent
+ * via the keyboard controller so should not be sent again.
+ */
 
 static struct key_entry dell_wmi_keymap[] = {
        {KE_KEY, 0xe045, KEY_PROG1},
+       {KE_KEY, 0xe009, KEY_EJECTCD},
+
+       /* These also contain the brightness level at offset 6 */
+       {KE_KEY, 0xe006, KEY_BRIGHTNESSUP},
+       {KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN},
+
+       /* Battery health status button */
+       {KE_KEY, 0xe007, KEY_BATTERY},
+
+       /* This is actually for all radios. Although physically a
+        * switch, the notification does not provide an indication of
+        * state and so it should be reported as a key */
+       {KE_KEY, 0xe008, KEY_WLAN},
+
+       /* The next device is at offset 6, the active devices are at
+          offset 8 and the attached devices at offset 10 */
+       {KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE},
+
+       {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE},
+
+       /* BIOS error detected */
+       {KE_IGNORE, 0xe00d, KEY_RESERVED},
+
+       /* Wifi Catcher */
+       {KE_KEY, 0xe011, KEY_PROG2},
+
+       /* Ambient light sensor toggle */
+       {KE_IGNORE, 0xe013, KEY_RESERVED},
+
+       {KE_IGNORE, 0xe020, KEY_MUTE},
+       {KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN},
+       {KE_IGNORE, 0xe030, KEY_VOLUMEUP},
+       {KE_IGNORE, 0xe033, KEY_KBDILLUMUP},
+       {KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN},
+       {KE_IGNORE, 0xe03a, KEY_CAPSLOCK},
+       {KE_IGNORE, 0xe045, KEY_NUMLOCK},
+       {KE_IGNORE, 0xe046, KEY_SCROLLLOCK},
        {KE_END, 0}
 };
 
@@ -122,15 +165,20 @@ static void dell_wmi_notify(u32 value, void *context)
 
        if (obj && obj->type == ACPI_TYPE_BUFFER) {
                int *buffer = (int *)obj->buffer.pointer;
-               key = dell_wmi_get_entry_by_scancode(buffer[1]);
+               /*
+                *  The upper bytes of the event may contain
+                *  additional information, so mask them off for the
+                *  scancode lookup
+                */
+               key = dell_wmi_get_entry_by_scancode(buffer[1] & 0xFFFF);
                if (key) {
                        input_report_key(dell_wmi_input_dev, key->keycode, 1);
                        input_sync(dell_wmi_input_dev);
                        input_report_key(dell_wmi_input_dev, key->keycode, 0);
                        input_sync(dell_wmi_input_dev);
-               } else
+               } else if (buffer[1] & 0xFFFF)
                        printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
-                              buffer[1]);
+                              buffer[1] & 0xFFFF);
        }
 }
 
index 8153b3e5918967bb1662c44523d1ab7eab233d69..4207b26ff990634d3a75d35394f20b4c80afba21 100644 (file)
@@ -62,7 +62,10 @@ enum {
        DISABLE_ASL_GPS = 0x0020,
        DISABLE_ASL_DISPLAYSWITCH = 0x0040,
        DISABLE_ASL_MODEM = 0x0080,
-       DISABLE_ASL_CARDREADER = 0x0100
+       DISABLE_ASL_CARDREADER = 0x0100,
+       DISABLE_ASL_3G = 0x0200,
+       DISABLE_ASL_WIMAX = 0x0400,
+       DISABLE_ASL_HWCF = 0x0800
 };
 
 enum {
@@ -87,7 +90,13 @@ enum {
        CM_ASL_USBPORT3,
        CM_ASL_MODEM,
        CM_ASL_CARDREADER,
-       CM_ASL_LID
+       CM_ASL_3G,
+       CM_ASL_WIMAX,
+       CM_ASL_HWCF,
+       CM_ASL_LID,
+       CM_ASL_TYPE,
+       CM_ASL_PANELPOWER,      /*P901*/
+       CM_ASL_TPD
 };
 
 static const char *cm_getv[] = {
@@ -96,7 +105,8 @@ static const char *cm_getv[] = {
        NULL, "PBLG", NULL, NULL,
        "CFVG", NULL, NULL, NULL,
        "USBG", NULL, NULL, "MODG",
-       "CRDG", "LIDG"
+       "CRDG", "M3GG", "WIMG", "HWCF",
+       "LIDG", "TYPE", "PBPG", "TPDG"
 };
 
 static const char *cm_setv[] = {
@@ -105,7 +115,8 @@ static const char *cm_setv[] = {
        "SDSP", "PBLS", "HDPS", NULL,
        "CFVS", NULL, NULL, NULL,
        "USBG", NULL, NULL, "MODS",
-       "CRDS", NULL
+       "CRDS", "M3GS", "WIMS", NULL,
+       NULL, NULL, "PBPS", "TPDS"
 };
 
 #define EEEPC_EC       "\\_SB.PCI0.SBRG.EC0."
@@ -181,6 +192,7 @@ static struct key_entry eeepc_keymap[] = {
 static int eeepc_hotk_add(struct acpi_device *device);
 static int eeepc_hotk_remove(struct acpi_device *device, int type);
 static int eeepc_hotk_resume(struct acpi_device *device);
+static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
 
 static const struct acpi_device_id eeepc_device_ids[] = {
        {EEEPC_HOTK_HID, 0},
@@ -192,10 +204,12 @@ static struct acpi_driver eeepc_hotk_driver = {
        .name = EEEPC_HOTK_NAME,
        .class = EEEPC_HOTK_CLASS,
        .ids = eeepc_device_ids,
+       .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
        .ops = {
                .add = eeepc_hotk_add,
                .remove = eeepc_hotk_remove,
                .resume = eeepc_hotk_resume,
+               .notify = eeepc_hotk_notify,
        },
 };
 
@@ -318,6 +332,15 @@ static const struct rfkill_ops eeepc_rfkill_ops = {
        .set_block = eeepc_rfkill_set,
 };
 
+static void __init eeepc_enable_camera(void)
+{
+       /*
+        * If the following call to set_acpi() fails, it's because there's no
+        * camera so we can ignore the error.
+        */
+       set_acpi(CM_ASL_CAMERA, 1);
+}
+
 /*
  * Sys helpers
  */
@@ -369,13 +392,88 @@ static ssize_t show_sys_acpi(int cm, char *buf)
 EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
 EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
 EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
-EEEPC_CREATE_DEVICE_ATTR(cpufv, CM_ASL_CPUFV);
+
+struct eeepc_cpufv {
+       int num;
+       int cur;
+};
+
+static int get_cpufv(struct eeepc_cpufv *c)
+{
+       c->cur = get_acpi(CM_ASL_CPUFV);
+       c->num = (c->cur >> 8) & 0xff;
+       c->cur &= 0xff;
+       if (c->cur < 0 || c->num <= 0 || c->num > 12)
+               return -ENODEV;
+       return 0;
+}
+
+static ssize_t show_available_cpufv(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       struct eeepc_cpufv c;
+       int i;
+       ssize_t len = 0;
+
+       if (get_cpufv(&c))
+               return -ENODEV;
+       for (i = 0; i < c.num; i++)
+               len += sprintf(buf + len, "%d ", i);
+       len += sprintf(buf + len, "\n");
+       return len;
+}
+
+static ssize_t show_cpufv(struct device *dev,
+                         struct device_attribute *attr,
+                         char *buf)
+{
+       struct eeepc_cpufv c;
+
+       if (get_cpufv(&c))
+               return -ENODEV;
+       return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
+}
+
+static ssize_t store_cpufv(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       struct eeepc_cpufv c;
+       int rv, value;
+
+       if (get_cpufv(&c))
+               return -ENODEV;
+       rv = parse_arg(buf, count, &value);
+       if (rv < 0)
+               return rv;
+       if (!rv || value < 0 || value >= c.num)
+               return -EINVAL;
+       set_acpi(CM_ASL_CPUFV, value);
+       return rv;
+}
+
+static struct device_attribute dev_attr_cpufv = {
+       .attr = {
+               .name = "cpufv",
+               .mode = 0644 },
+       .show   = show_cpufv,
+       .store  = store_cpufv
+};
+
+static struct device_attribute dev_attr_available_cpufv = {
+       .attr = {
+               .name = "available_cpufv",
+               .mode = 0444 },
+       .show   = show_available_cpufv
+};
 
 static struct attribute *platform_attributes[] = {
        &dev_attr_camera.attr,
        &dev_attr_cardr.attr,
        &dev_attr_disp.attr,
        &dev_attr_cpufv.attr,
+       &dev_attr_available_cpufv.attr,
        NULL
 };
 
@@ -558,7 +656,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
        eeepc_rfkill_hotplug();
 }
 
-static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
+static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
 {
        static struct key_entry *key;
        u16 count;
@@ -566,6 +664,8 @@ static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
 
        if (!ehotk)
                return;
+       if (event > ACPI_MAX_SYS_NOTIFY)
+               return;
        if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
                brn = notify_brn();
        count = ehotk->event_count[event % 128]++;
@@ -646,7 +746,6 @@ static void eeepc_unregister_rfkill_notifier(char *node)
 
 static int eeepc_hotk_add(struct acpi_device *device)
 {
-       acpi_status status = AE_OK;
        int result;
 
        if (!device)
@@ -664,10 +763,6 @@ static int eeepc_hotk_add(struct acpi_device *device)
        result = eeepc_hotk_check();
        if (result)
                goto ehotk_fail;
-       status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
-                                            eeepc_hotk_notify, ehotk);
-       if (ACPI_FAILURE(status))
-               printk(EEEPC_ERR "Error installing notify handler\n");
 
        eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
        eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
@@ -725,14 +820,8 @@ static int eeepc_hotk_add(struct acpi_device *device)
 
 static int eeepc_hotk_remove(struct acpi_device *device, int type)
 {
-       acpi_status status = 0;
-
        if (!device || !acpi_driver_data(device))
                 return -EINVAL;
-       status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
-                                           eeepc_hotk_notify);
-       if (ACPI_FAILURE(status))
-               printk(EEEPC_ERR "Error removing notify handler\n");
 
        eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
        eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
@@ -989,6 +1078,9 @@ static int __init eeepc_laptop_init(void)
        result = eeepc_hwmon_init(dev);
        if (result)
                goto fail_hwmon;
+
+       eeepc_enable_camera();
+
        /* Register platform stuff */
        result = platform_driver_register(&platform_driver);
        if (result)
index 16fffe44e3335ee34b4053e7b309b6e4dd3a631b..4ac2311c00afe9fae55fcf1334522a564fd943e5 100644 (file)
@@ -47,7 +47,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
 #define HPWMI_DISPLAY_QUERY 0x1
 #define HPWMI_HDDTEMP_QUERY 0x2
 #define HPWMI_ALS_QUERY 0x3
-#define HPWMI_DOCK_QUERY 0x4
+#define HPWMI_HARDWARE_QUERY 0x4
 #define HPWMI_WIRELESS_QUERY 0x5
 #define HPWMI_HOTKEY_QUERY 0xc
 
@@ -75,10 +75,9 @@ struct key_entry {
        u16 keycode;
 };
 
-enum { KE_KEY, KE_SW, KE_END };
+enum { KE_KEY, KE_END };
 
 static struct key_entry hp_wmi_keymap[] = {
-       {KE_SW, 0x01, SW_DOCK},
        {KE_KEY, 0x02, KEY_BRIGHTNESSUP},
        {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
        {KE_KEY, 0x20e6, KEY_PROG1},
@@ -151,7 +150,22 @@ static int hp_wmi_als_state(void)
 
 static int hp_wmi_dock_state(void)
 {
-       return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
+       int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0);
+
+       if (ret < 0)
+               return ret;
+
+       return ret & 0x1;
+}
+
+static int hp_wmi_tablet_state(void)
+{
+       int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0);
+
+       if (ret < 0)
+               return ret;
+
+       return (ret & 0x4) ? 1 : 0;
 }
 
 static int hp_wmi_set_block(void *data, bool blocked)
@@ -232,6 +246,15 @@ static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
        return sprintf(buf, "%d\n", value);
 }
 
+static ssize_t show_tablet(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       int value = hp_wmi_tablet_state();
+       if (value < 0)
+               return -EINVAL;
+       return sprintf(buf, "%d\n", value);
+}
+
 static ssize_t set_als(struct device *dev, struct device_attribute *attr,
                       const char *buf, size_t count)
 {
@@ -244,6 +267,7 @@ static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
 static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
 static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
 static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
+static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
 
 static struct key_entry *hp_wmi_get_entry_by_scancode(int code)
 {
@@ -326,13 +350,13 @@ static void hp_wmi_notify(u32 value, void *context)
                                                 key->keycode, 0);
                                input_sync(hp_wmi_input_dev);
                                break;
-                       case KE_SW:
-                               input_report_switch(hp_wmi_input_dev,
-                                                   key->keycode,
-                                                   hp_wmi_dock_state());
-                               input_sync(hp_wmi_input_dev);
-                               break;
                        }
+               } else if (eventcode == 0x1) {
+                       input_report_switch(hp_wmi_input_dev, SW_DOCK,
+                                           hp_wmi_dock_state());
+                       input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
+                                           hp_wmi_tablet_state());
+                       input_sync(hp_wmi_input_dev);
                } else if (eventcode == 0x5) {
                        if (wifi_rfkill)
                                rfkill_set_sw_state(wifi_rfkill,
@@ -369,18 +393,19 @@ static int __init hp_wmi_input_setup(void)
                        set_bit(EV_KEY, hp_wmi_input_dev->evbit);
                        set_bit(key->keycode, hp_wmi_input_dev->keybit);
                        break;
-               case KE_SW:
-                       set_bit(EV_SW, hp_wmi_input_dev->evbit);
-                       set_bit(key->keycode, hp_wmi_input_dev->swbit);
-
-                       /* Set initial dock state */
-                       input_report_switch(hp_wmi_input_dev, key->keycode,
-                                           hp_wmi_dock_state());
-                       input_sync(hp_wmi_input_dev);
-                       break;
                }
        }
 
+       set_bit(EV_SW, hp_wmi_input_dev->evbit);
+       set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
+       set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
+
+       /* Set initial hardware state */
+       input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
+       input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
+                           hp_wmi_tablet_state());
+       input_sync(hp_wmi_input_dev);
+
        err = input_register_device(hp_wmi_input_dev);
 
        if (err) {
@@ -397,6 +422,7 @@ static void cleanup_sysfs(struct platform_device *device)
        device_remove_file(&device->dev, &dev_attr_hddtemp);
        device_remove_file(&device->dev, &dev_attr_als);
        device_remove_file(&device->dev, &dev_attr_dock);
+       device_remove_file(&device->dev, &dev_attr_tablet);
 }
 
 static int __init hp_wmi_bios_setup(struct platform_device *device)
@@ -414,6 +440,9 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
        if (err)
                goto add_sysfs_error;
        err = device_create_file(&device->dev, &dev_attr_dock);
+       if (err)
+               goto add_sysfs_error;
+       err = device_create_file(&device->dev, &dev_attr_tablet);
        if (err)
                goto add_sysfs_error;
 
@@ -485,23 +514,17 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
 
 static int hp_wmi_resume_handler(struct platform_device *device)
 {
-       struct key_entry *key;
-
        /*
-        * Docking state may have changed while suspended, so trigger
-        * an input event for the current state. As this is a switch,
+        * Hardware state may have changed while suspended, so trigger
+        * input events for the current state. As this is a switch,
         * the input layer will only actually pass it on if the state
         * changed.
         */
-       for (key = hp_wmi_keymap; key->type != KE_END; key++) {
-               switch (key->type) {
-               case KE_SW:
-                       input_report_switch(hp_wmi_input_dev, key->keycode,
-                                           hp_wmi_dock_state());
-                       input_sync(hp_wmi_input_dev);
-                       break;
-               }
-       }
+
+       input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
+       input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
+                           hp_wmi_tablet_state());
+       input_sync(hp_wmi_input_dev);
 
        return 0;
 }
index 40d64c03278ca4bd18db3cf8214e11a9412cc0e7..a463fd72c49560a7c5d7fe2f8744a614519f7f34 100644 (file)
@@ -22,7 +22,7 @@
  */
 
 #define TPACPI_VERSION "0.23"
-#define TPACPI_SYSFS_VERSION 0x020300
+#define TPACPI_SYSFS_VERSION 0x020400
 
 /*
  *  Changelog:
@@ -257,6 +257,8 @@ static struct {
        u32 wan:1;
        u32 uwb:1;
        u32 fan_ctrl_status_undef:1;
+       u32 second_fan:1;
+       u32 beep_needs_two_args:1;
        u32 input_device_registered:1;
        u32 platform_drv_registered:1;
        u32 platform_drv_attrs_registered:1;
@@ -277,8 +279,10 @@ struct thinkpad_id_data {
        char *bios_version_str; /* Something like 1ZET51WW (1.03z) */
        char *ec_version_str;   /* Something like 1ZHT51WW-1.04a */
 
-       u16 bios_model;         /* Big Endian, TP-1Y = 0x5931, 0 = unknown */
+       u16 bios_model;         /* 1Y = 0x5931, 0 = unknown */
        u16 ec_model;
+       u16 bios_release;       /* 1ZETK1WW = 0x314b, 0 = unknown */
+       u16 ec_release;
 
        char *model_str;        /* ThinkPad T43 */
        char *nummodel_str;     /* 9384A9C for a 9384-A9C model */
@@ -355,6 +359,73 @@ static void tpacpi_log_usertask(const char * const what)
                } \
        } while (0)
 
+/*
+ * Quirk handling helpers
+ *
+ * ThinkPad IDs and versions seen in the field so far
+ * are two-characters from the set [0-9A-Z], i.e. base 36.
+ *
+ * We use values well outside that range as specials.
+ */
+
+#define TPACPI_MATCH_ANY               0xffffU
+#define TPACPI_MATCH_UNKNOWN           0U
+
+/* TPID('1', 'Y') == 0x5931 */
+#define TPID(__c1, __c2) (((__c2) << 8) | (__c1))
+
+#define TPACPI_Q_IBM(__id1, __id2, __quirk)    \
+       { .vendor = PCI_VENDOR_ID_IBM,          \
+         .bios = TPID(__id1, __id2),           \
+         .ec = TPACPI_MATCH_ANY,               \
+         .quirks = (__quirk) }
+
+#define TPACPI_Q_LNV(__id1, __id2, __quirk)    \
+       { .vendor = PCI_VENDOR_ID_LENOVO,       \
+         .bios = TPID(__id1, __id2),           \
+         .ec = TPACPI_MATCH_ANY,               \
+         .quirks = (__quirk) }
+
+struct tpacpi_quirk {
+       unsigned int vendor;
+       u16 bios;
+       u16 ec;
+       unsigned long quirks;
+};
+
+/**
+ * tpacpi_check_quirks() - search BIOS/EC version on a list
+ * @qlist:             array of &struct tpacpi_quirk
+ * @qlist_size:                number of elements in @qlist
+ *
+ * Iterates over a quirks list until one is found that matches the
+ * ThinkPad's vendor, BIOS and EC model.
+ *
+ * Returns 0 if nothing matches, otherwise returns the quirks field of
+ * the matching &struct tpacpi_quirk entry.
+ *
+ * The match criteria is: vendor, ec and bios much match.
+ */
+static unsigned long __init tpacpi_check_quirks(
+                       const struct tpacpi_quirk *qlist,
+                       unsigned int qlist_size)
+{
+       while (qlist_size) {
+               if ((qlist->vendor == thinkpad_id.vendor ||
+                               qlist->vendor == TPACPI_MATCH_ANY) &&
+                   (qlist->bios == thinkpad_id.bios_model ||
+                               qlist->bios == TPACPI_MATCH_ANY) &&
+                   (qlist->ec == thinkpad_id.ec_model ||
+                               qlist->ec == TPACPI_MATCH_ANY))
+                       return qlist->quirks;
+
+               qlist_size--;
+               qlist++;
+       }
+       return 0;
+}
+
+
 /****************************************************************************
  ****************************************************************************
  *
@@ -2880,7 +2951,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                /* update bright_acpimode... */
                tpacpi_check_std_acpi_brightness_support();
 
-       if (tp_features.bright_acpimode) {
+       if (tp_features.bright_acpimode && acpi_video_backlight_support()) {
                printk(TPACPI_INFO
                       "This ThinkPad has standard ACPI backlight "
                       "brightness control, supported by the ACPI "
@@ -4773,7 +4844,7 @@ TPACPI_HANDLE(led, ec, "SLED",    /* 570 */
           "LED",               /* all others */
           );                   /* R30, R31 */
 
-#define TPACPI_LED_NUMLEDS 8
+#define TPACPI_LED_NUMLEDS 16
 static struct tpacpi_led_classdev *tpacpi_leds;
 static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
 static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
@@ -4786,15 +4857,20 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
        "tpacpi::dock_batt",
        "tpacpi::unknown_led",
        "tpacpi::standby",
+       "tpacpi::dock_status1",
+       "tpacpi::dock_status2",
+       "tpacpi::unknown_led2",
+       "tpacpi::unknown_led3",
+       "tpacpi::thinkvantage",
 };
-#define TPACPI_SAFE_LEDS       0x0081U
+#define TPACPI_SAFE_LEDS       0x1081U
 
 static inline bool tpacpi_is_led_restricted(const unsigned int led)
 {
 #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
        return false;
 #else
-       return (TPACPI_SAFE_LEDS & (1 << led)) == 0;
+       return (1U & (TPACPI_SAFE_LEDS >> led)) == 0;
 #endif
 }
 
@@ -4956,6 +5032,10 @@ static int __init tpacpi_init_led(unsigned int led)
 
        tpacpi_leds[led].led = led;
 
+       /* LEDs with no name don't get registered */
+       if (!tpacpi_led_names[led])
+               return 0;
+
        tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set;
        tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
        if (led_supported == TPACPI_LED_570)
@@ -4974,10 +5054,59 @@ static int __init tpacpi_init_led(unsigned int led)
        return rc;
 }
 
+static const struct tpacpi_quirk led_useful_qtable[] __initconst = {
+       TPACPI_Q_IBM('1', 'E', 0x009f), /* A30 */
+       TPACPI_Q_IBM('1', 'N', 0x009f), /* A31 */
+       TPACPI_Q_IBM('1', 'G', 0x009f), /* A31 */
+
+       TPACPI_Q_IBM('1', 'I', 0x0097), /* T30 */
+       TPACPI_Q_IBM('1', 'R', 0x0097), /* T40, T41, T42, R50, R51 */
+       TPACPI_Q_IBM('7', '0', 0x0097), /* T43, R52 */
+       TPACPI_Q_IBM('1', 'Y', 0x0097), /* T43 */
+       TPACPI_Q_IBM('1', 'W', 0x0097), /* R50e */
+       TPACPI_Q_IBM('1', 'V', 0x0097), /* R51 */
+       TPACPI_Q_IBM('7', '8', 0x0097), /* R51e */
+       TPACPI_Q_IBM('7', '6', 0x0097), /* R52 */
+
+       TPACPI_Q_IBM('1', 'K', 0x00bf), /* X30 */
+       TPACPI_Q_IBM('1', 'Q', 0x00bf), /* X31, X32 */
+       TPACPI_Q_IBM('1', 'U', 0x00bf), /* X40 */
+       TPACPI_Q_IBM('7', '4', 0x00bf), /* X41 */
+       TPACPI_Q_IBM('7', '5', 0x00bf), /* X41t */
+
+       TPACPI_Q_IBM('7', '9', 0x1f97), /* T60 (1) */
+       TPACPI_Q_IBM('7', '7', 0x1f97), /* Z60* (1) */
+       TPACPI_Q_IBM('7', 'F', 0x1f97), /* Z61* (1) */
+       TPACPI_Q_IBM('7', 'B', 0x1fb7), /* X60 (1) */
+
+       /* (1) - may have excess leds enabled on MSB */
+
+       /* Defaults (order matters, keep last, don't reorder!) */
+       { /* Lenovo */
+         .vendor = PCI_VENDOR_ID_LENOVO,
+         .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY,
+         .quirks = 0x1fffU,
+       },
+       { /* IBM ThinkPads with no EC version string */
+         .vendor = PCI_VENDOR_ID_IBM,
+         .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_UNKNOWN,
+         .quirks = 0x00ffU,
+       },
+       { /* IBM ThinkPads with EC version string */
+         .vendor = PCI_VENDOR_ID_IBM,
+         .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY,
+         .quirks = 0x00bfU,
+       },
+};
+
+#undef TPACPI_LEDQ_IBM
+#undef TPACPI_LEDQ_LNV
+
 static int __init led_init(struct ibm_init_struct *iibm)
 {
        unsigned int i;
        int rc;
+       unsigned long useful_leds;
 
        vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
 
@@ -4999,6 +5128,9 @@ static int __init led_init(struct ibm_init_struct *iibm)
        vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
                str_supported(led_supported), led_supported);
 
+       if (led_supported == TPACPI_LED_NONE)
+               return 1;
+
        tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
                              GFP_KERNEL);
        if (!tpacpi_leds) {
@@ -5006,8 +5138,12 @@ static int __init led_init(struct ibm_init_struct *iibm)
                return -ENOMEM;
        }
 
+       useful_leds = tpacpi_check_quirks(led_useful_qtable,
+                                         ARRAY_SIZE(led_useful_qtable));
+
        for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
-               if (!tpacpi_is_led_restricted(i)) {
+               if (!tpacpi_is_led_restricted(i) &&
+                   test_bit(i, &useful_leds)) {
                        rc = tpacpi_init_led(i);
                        if (rc < 0) {
                                led_exit();
@@ -5017,12 +5153,11 @@ static int __init led_init(struct ibm_init_struct *iibm)
        }
 
 #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
-       if (led_supported != TPACPI_LED_NONE)
-               printk(TPACPI_NOTICE
-                       "warning: userspace override of important "
-                       "firmware LEDs is enabled\n");
+       printk(TPACPI_NOTICE
+               "warning: userspace override of important "
+               "firmware LEDs is enabled\n");
 #endif
-       return (led_supported != TPACPI_LED_NONE)? 0 : 1;
+       return 0;
 }
 
 #define str_led_status(s) \
@@ -5052,7 +5187,7 @@ static int led_read(char *p)
        }
 
        len += sprintf(p + len, "commands:\t"
-                      "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
+                      "<led> on, <led> off, <led> blink (<led> is 0-15)\n");
 
        return len;
 }
@@ -5067,7 +5202,7 @@ static int led_write(char *buf)
                return -ENODEV;
 
        while ((cmd = next_cmd(&buf))) {
-               if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
+               if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 15)
                        return -EINVAL;
 
                if (strstr(cmd, "off")) {
@@ -5101,8 +5236,17 @@ static struct ibm_struct led_driver_data = {
 
 TPACPI_HANDLE(beep, ec, "BEEP");       /* all except R30, R31 */
 
+#define TPACPI_BEEP_Q1 0x0001
+
+static const struct tpacpi_quirk beep_quirk_table[] __initconst = {
+       TPACPI_Q_IBM('I', 'M', TPACPI_BEEP_Q1), /* 570 */
+       TPACPI_Q_IBM('I', 'U', TPACPI_BEEP_Q1), /* 570E - unverified */
+};
+
 static int __init beep_init(struct ibm_init_struct *iibm)
 {
+       unsigned long quirks;
+
        vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n");
 
        TPACPI_ACPIHANDLE_INIT(beep);
@@ -5110,6 +5254,11 @@ static int __init beep_init(struct ibm_init_struct *iibm)
        vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n",
                str_supported(beep_handle != NULL));
 
+       quirks = tpacpi_check_quirks(beep_quirk_table,
+                                    ARRAY_SIZE(beep_quirk_table));
+
+       tp_features.beep_needs_two_args = !!(quirks & TPACPI_BEEP_Q1);
+
        return (beep_handle)? 0 : 1;
 }
 
@@ -5141,8 +5290,15 @@ static int beep_write(char *buf)
                        /* beep_cmd set */
                } else