Merge branch 'dmi-const' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik...
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Fri, 12 Oct 2007 02:18:45 +0000 (19:18 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Fri, 12 Oct 2007 02:18:45 +0000 (19:18 -0700)
* 'dmi-const' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/misc-2.6:
  drivers/firmware: const-ify DMI API and internals

1  2 
arch/x86/kernel/acpi/boot.c
arch/x86/kernel/acpi/sleep_32.c
arch/x86/kernel/apm_32.c
arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c
arch/x86/kernel/reboot_32.c
arch/x86/kernel/tsc_32.c
arch/x86/mach-generic/bigsmp.c
arch/x86/pci/common.c
arch/x86/pci/irq.c

index cacdd88,0000000..afd2afe
mode 100644,000000..100644
--- /dev/null
@@@ -1,1326 -1,0 +1,1326 @@@
- static int __init disable_acpi_irq(struct dmi_system_id *d)
 +/*
 + *  boot.c - Architecture-Specific Low-Level ACPI Boot Support
 + *
 + *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 + *  Copyright (C) 2001 Jun Nakajima <jun.nakajima@intel.com>
 + *
 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + *
 + *  This program is free software; you can redistribute it and/or modify
 + *  it under the terms of the GNU General Public License as published by
 + *  the Free Software Foundation; either version 2 of the License, or
 + *  (at your option) any later version.
 + *
 + *  This program is distributed in the hope that it will be useful,
 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + *  GNU General Public License for more details.
 + *
 + *  You should have received a copy of the GNU General Public License
 + *  along with this program; if not, write to the Free Software
 + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 + *
 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + */
 +
 +#include <linux/init.h>
 +#include <linux/acpi.h>
 +#include <linux/acpi_pmtmr.h>
 +#include <linux/efi.h>
 +#include <linux/cpumask.h>
 +#include <linux/module.h>
 +#include <linux/dmi.h>
 +#include <linux/irq.h>
 +#include <linux/bootmem.h>
 +#include <linux/ioport.h>
 +
 +#include <asm/pgtable.h>
 +#include <asm/io_apic.h>
 +#include <asm/apic.h>
 +#include <asm/io.h>
 +#include <asm/mpspec.h>
 +
 +static int __initdata acpi_force = 0;
 +
 +#ifdef        CONFIG_ACPI
 +int acpi_disabled = 0;
 +#else
 +int acpi_disabled = 1;
 +#endif
 +EXPORT_SYMBOL(acpi_disabled);
 +
 +#ifdef        CONFIG_X86_64
 +
 +#include <asm/proto.h>
 +
 +static inline int acpi_madt_oem_check(char *oem_id, char *oem_table_id) { return 0; }
 +
 +
 +#else                         /* X86 */
 +
 +#ifdef        CONFIG_X86_LOCAL_APIC
 +#include <mach_apic.h>
 +#include <mach_mpparse.h>
 +#endif                                /* CONFIG_X86_LOCAL_APIC */
 +
 +#endif                                /* X86 */
 +
 +#define BAD_MADT_ENTRY(entry, end) (                                      \
 +              (!entry) || (unsigned long)entry + sizeof(*entry) > end ||  \
 +              ((struct acpi_subtable_header *)entry)->length < sizeof(*entry))
 +
 +#define PREFIX                        "ACPI: "
 +
 +int acpi_noirq;                               /* skip ACPI IRQ initialization */
 +int acpi_pci_disabled __initdata;     /* skip ACPI PCI scan and IRQ initialization */
 +int acpi_ht __initdata = 1;   /* enable HT */
 +
 +int acpi_lapic;
 +int acpi_ioapic;
 +int acpi_strict;
 +EXPORT_SYMBOL(acpi_strict);
 +
 +u8 acpi_sci_flags __initdata;
 +int acpi_sci_override_gsi __initdata;
 +int acpi_skip_timer_override __initdata;
 +int acpi_use_timer_override __initdata;
 +
 +#ifdef CONFIG_X86_LOCAL_APIC
 +static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE;
 +#endif
 +
 +#ifndef __HAVE_ARCH_CMPXCHG
 +#warning ACPI uses CMPXCHG, i486 and later hardware
 +#endif
 +
 +/* --------------------------------------------------------------------------
 +                              Boot-time Configuration
 +   -------------------------------------------------------------------------- */
 +
 +/*
 + * The default interrupt routing model is PIC (8259).  This gets
 + * overriden if IOAPICs are enumerated (below).
 + */
 +enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PIC;
 +
 +#ifdef        CONFIG_X86_64
 +
 +/* rely on all ACPI tables being in the direct mapping */
 +char *__acpi_map_table(unsigned long phys_addr, unsigned long size)
 +{
 +      if (!phys_addr || !size)
 +              return NULL;
 +
 +      if (phys_addr+size <= (end_pfn_map << PAGE_SHIFT) + PAGE_SIZE)
 +              return __va(phys_addr);
 +
 +      return NULL;
 +}
 +
 +#else
 +
 +/*
 + * Temporarily use the virtual area starting from FIX_IO_APIC_BASE_END,
 + * to map the target physical address. The problem is that set_fixmap()
 + * provides a single page, and it is possible that the page is not
 + * sufficient.
 + * By using this area, we can map up to MAX_IO_APICS pages temporarily,
 + * i.e. until the next __va_range() call.
 + *
 + * Important Safety Note:  The fixed I/O APIC page numbers are *subtracted*
 + * from the fixed base.  That's why we start at FIX_IO_APIC_BASE_END and
 + * count idx down while incrementing the phys address.
 + */
 +char *__acpi_map_table(unsigned long phys, unsigned long size)
 +{
 +      unsigned long base, offset, mapped_size;
 +      int idx;
 +
 +      if (phys + size < 8 * 1024 * 1024)
 +              return __va(phys);
 +
 +      offset = phys & (PAGE_SIZE - 1);
 +      mapped_size = PAGE_SIZE - offset;
 +      set_fixmap(FIX_ACPI_END, phys);
 +      base = fix_to_virt(FIX_ACPI_END);
 +
 +      /*
 +       * Most cases can be covered by the below.
 +       */
 +      idx = FIX_ACPI_END;
 +      while (mapped_size < size) {
 +              if (--idx < FIX_ACPI_BEGIN)
 +                      return NULL;    /* cannot handle this */
 +              phys += PAGE_SIZE;
 +              set_fixmap(idx, phys);
 +              mapped_size += PAGE_SIZE;
 +      }
 +
 +      return ((unsigned char *)base + offset);
 +}
 +#endif
 +
 +#ifdef CONFIG_PCI_MMCONFIG
 +/* The physical address of the MMCONFIG aperture.  Set from ACPI tables. */
 +struct acpi_mcfg_allocation *pci_mmcfg_config;
 +int pci_mmcfg_config_num;
 +
 +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);
 +      for (i = 0; i < pci_mmcfg_config_num; ++i) {
 +              if (pci_mmcfg_config[i].address > 0xFFFFFFFF) {
 +                      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)
 +{
 +      struct acpi_table_madt *madt = NULL;
 +
 +      if (!cpu_has_apic)
 +              return -EINVAL;
 +
 +      madt = (struct acpi_table_madt *)table;
 +      if (!madt) {
 +              printk(KERN_WARNING PREFIX "Unable to map MADT\n");
 +              return -ENODEV;
 +      }
 +
 +      if (madt->address) {
 +              acpi_lapic_addr = (u64) madt->address;
 +
 +              printk(KERN_DEBUG PREFIX "Local APIC address 0x%08x\n",
 +                     madt->address);
 +      }
 +
 +      acpi_madt_oem_check(madt->header.oem_id, madt->header.oem_table_id);
 +
 +      return 0;
 +}
 +
 +static int __init
 +acpi_parse_lapic(struct acpi_subtable_header * header, const unsigned long end)
 +{
 +      struct acpi_madt_local_apic *processor = NULL;
 +
 +      processor = (struct acpi_madt_local_apic *)header;
 +
 +      if (BAD_MADT_ENTRY(processor, end))
 +              return -EINVAL;
 +
 +      acpi_table_print_madt_entry(header);
 +
 +      /*
 +       * We need to register disabled CPU as well to permit
 +       * counting disabled CPUs. This allows us to size
 +       * cpus_possible_map more accurately, to permit
 +       * to not preallocating memory for all NR_CPUS
 +       * when we use CPU hotplug.
 +       */
 +      mp_register_lapic(processor->id,        /* APIC ID */
 +                        processor->lapic_flags & ACPI_MADT_ENABLED);  /* Enabled? */
 +
 +      return 0;
 +}
 +
 +static int __init
 +acpi_parse_lapic_addr_ovr(struct acpi_subtable_header * header,
 +                        const unsigned long end)
 +{
 +      struct acpi_madt_local_apic_override *lapic_addr_ovr = NULL;
 +
 +      lapic_addr_ovr = (struct acpi_madt_local_apic_override *)header;
 +
 +      if (BAD_MADT_ENTRY(lapic_addr_ovr, end))
 +              return -EINVAL;
 +
 +      acpi_lapic_addr = lapic_addr_ovr->address;
 +
 +      return 0;
 +}
 +
 +static int __init
 +acpi_parse_lapic_nmi(struct acpi_subtable_header * header, const unsigned long end)
 +{
 +      struct acpi_madt_local_apic_nmi *lapic_nmi = NULL;
 +
 +      lapic_nmi = (struct acpi_madt_local_apic_nmi *)header;
 +
 +      if (BAD_MADT_ENTRY(lapic_nmi, end))
 +              return -EINVAL;
 +
 +      acpi_table_print_madt_entry(header);
 +
 +      if (lapic_nmi->lint != 1)
 +              printk(KERN_WARNING PREFIX "NMI not connected to LINT 1!\n");
 +
 +      return 0;
 +}
 +
 +#endif                                /*CONFIG_X86_LOCAL_APIC */
 +
 +#ifdef CONFIG_X86_IO_APIC
 +
 +static int __init
 +acpi_parse_ioapic(struct acpi_subtable_header * header, const unsigned long end)
 +{
 +      struct acpi_madt_io_apic *ioapic = NULL;
 +
 +      ioapic = (struct acpi_madt_io_apic *)header;
 +
 +      if (BAD_MADT_ENTRY(ioapic, end))
 +              return -EINVAL;
 +
 +      acpi_table_print_madt_entry(header);
 +
 +      mp_register_ioapic(ioapic->id,
 +                         ioapic->address, ioapic->global_irq_base);
 +
 +      return 0;
 +}
 +
 +/*
 + * Parse Interrupt Source Override for the ACPI SCI
 + */
 +static void __init acpi_sci_ioapic_setup(u32 gsi, u16 polarity, u16 trigger)
 +{
 +      if (trigger == 0)       /* compatible SCI trigger is level */
 +              trigger = 3;
 +
 +      if (polarity == 0)      /* compatible SCI polarity is low */
 +              polarity = 3;
 +
 +      /* Command-line over-ride via acpi_sci= */
 +      if (acpi_sci_flags & ACPI_MADT_TRIGGER_MASK)
 +              trigger = (acpi_sci_flags & ACPI_MADT_TRIGGER_MASK) >> 2;
 +
 +      if (acpi_sci_flags & ACPI_MADT_POLARITY_MASK)
 +              polarity = acpi_sci_flags & ACPI_MADT_POLARITY_MASK;
 +
 +      /*
 +       * mp_config_acpi_legacy_irqs() already setup IRQs < 16
 +       * If GSI is < 16, this will update its flags,
 +       * else it will create a new mp_irqs[] entry.
 +       */
 +      mp_override_legacy_irq(gsi, polarity, trigger, gsi);
 +
 +      /*
 +       * stash over-ride to indicate we've been here
 +       * and for later update of acpi_gbl_FADT
 +       */
 +      acpi_sci_override_gsi = gsi;
 +      return;
 +}
 +
 +static int __init
 +acpi_parse_int_src_ovr(struct acpi_subtable_header * header,
 +                     const unsigned long end)
 +{
 +      struct acpi_madt_interrupt_override *intsrc = NULL;
 +
 +      intsrc = (struct acpi_madt_interrupt_override *)header;
 +
 +      if (BAD_MADT_ENTRY(intsrc, end))
 +              return -EINVAL;
 +
 +      acpi_table_print_madt_entry(header);
 +
 +      if (intsrc->source_irq == acpi_gbl_FADT.sci_interrupt) {
 +              acpi_sci_ioapic_setup(intsrc->global_irq,
 +                                    intsrc->inti_flags & ACPI_MADT_POLARITY_MASK,
 +                                    (intsrc->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2);
 +              return 0;
 +      }
 +
 +      if (acpi_skip_timer_override &&
 +          intsrc->source_irq == 0 && intsrc->global_irq == 2) {
 +              printk(PREFIX "BIOS IRQ0 pin2 override ignored.\n");
 +              return 0;
 +      }
 +
 +      mp_override_legacy_irq(intsrc->source_irq,
 +                              intsrc->inti_flags & ACPI_MADT_POLARITY_MASK,
 +                              (intsrc->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2,
 +                              intsrc->global_irq);
 +
 +      return 0;
 +}
 +
 +static int __init
 +acpi_parse_nmi_src(struct acpi_subtable_header * header, const unsigned long end)
 +{
 +      struct acpi_madt_nmi_source *nmi_src = NULL;
 +
 +      nmi_src = (struct acpi_madt_nmi_source *)header;
 +
 +      if (BAD_MADT_ENTRY(nmi_src, end))
 +              return -EINVAL;
 +
 +      acpi_table_print_madt_entry(header);
 +
 +      /* TBD: Support nimsrc entries? */
 +
 +      return 0;
 +}
 +
 +#endif                                /* CONFIG_X86_IO_APIC */
 +
 +/*
 + * acpi_pic_sci_set_trigger()
 + *
 + * use ELCR to set PIC-mode trigger type for SCI
 + *
 + * If a PIC-mode SCI is not recognized or gives spurious IRQ7's
 + * it may require Edge Trigger -- use "acpi_sci=edge"
 + *
 + * Port 0x4d0-4d1 are ECLR1 and ECLR2, the Edge/Level Control Registers
 + * for the 8259 PIC.  bit[n] = 1 means irq[n] is Level, otherwise Edge.
 + * ECLR1 is IRQ's 0-7 (IRQ 0, 1, 2 must be 0)
 + * ECLR2 is IRQ's 8-15 (IRQ 8, 13 must be 0)
 + */
 +
 +void __init acpi_pic_sci_set_trigger(unsigned int irq, u16 trigger)
 +{
 +      unsigned int mask = 1 << irq;
 +      unsigned int old, new;
 +
 +      /* Real old ELCR mask */
 +      old = inb(0x4d0) | (inb(0x4d1) << 8);
 +
 +      /*
 +       * If we use ACPI to set PCI irq's, then we should clear ELCR
 +       * since we will set it correctly as we enable the PCI irq
 +       * routing.
 +       */
 +      new = acpi_noirq ? old : 0;
 +
 +      /*
 +       * Update SCI information in the ELCR, it isn't in the PCI
 +       * routing tables..
 +       */
 +      switch (trigger) {
 +      case 1:         /* Edge - clear */
 +              new &= ~mask;
 +              break;
 +      case 3:         /* Level - set */
 +              new |= mask;
 +              break;
 +      }
 +
 +      if (old == new)
 +              return;
 +
 +      printk(PREFIX "setting ELCR to %04x (from %04x)\n", new, old);
 +      outb(new, 0x4d0);
 +      outb(new >> 8, 0x4d1);
 +}
 +
 +int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
 +{
 +      *irq = gsi;
 +      return 0;
 +}
 +
 +/*
 + * success: return IRQ number (>=0)
 + * failure: return < 0
 + */
 +int acpi_register_gsi(u32 gsi, int triggering, int polarity)
 +{
 +      unsigned int irq;
 +      unsigned int plat_gsi = gsi;
 +
 +#ifdef CONFIG_PCI
 +      /*
 +       * Make sure all (legacy) PCI IRQs are set as level-triggered.
 +       */
 +      if (acpi_irq_model == ACPI_IRQ_MODEL_PIC) {
 +              extern void eisa_set_level_irq(unsigned int irq);
 +
 +              if (triggering == ACPI_LEVEL_SENSITIVE)
 +                      eisa_set_level_irq(gsi);
 +      }
 +#endif
 +
 +#ifdef CONFIG_X86_IO_APIC
 +      if (acpi_irq_model == ACPI_IRQ_MODEL_IOAPIC) {
 +              plat_gsi = mp_register_gsi(gsi, triggering, polarity);
 +      }
 +#endif
 +      acpi_gsi_to_irq(plat_gsi, &irq);
 +      return irq;
 +}
 +
 +EXPORT_SYMBOL(acpi_register_gsi);
 +
 +/*
 + *  ACPI based hotplug support for CPU
 + */
 +#ifdef CONFIG_ACPI_HOTPLUG_CPU
 +int acpi_map_lsapic(acpi_handle handle, int *pcpu)
 +{
 +      struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 +      union acpi_object *obj;
 +      struct acpi_madt_local_apic *lapic;
 +      cpumask_t tmp_map, new_map;
 +      u8 physid;
 +      int cpu;
 +
 +      if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
 +              return -EINVAL;
 +
 +      if (!buffer.length || !buffer.pointer)
 +              return -EINVAL;
 +
 +      obj = buffer.pointer;
 +      if (obj->type != ACPI_TYPE_BUFFER ||
 +          obj->buffer.length < sizeof(*lapic)) {
 +              kfree(buffer.pointer);
 +              return -EINVAL;
 +      }
 +
 +      lapic = (struct acpi_madt_local_apic *)obj->buffer.pointer;
 +
 +      if (lapic->header.type != ACPI_MADT_TYPE_LOCAL_APIC ||
 +          !(lapic->lapic_flags & ACPI_MADT_ENABLED)) {
 +              kfree(buffer.pointer);
 +              return -EINVAL;
 +      }
 +
 +      physid = lapic->id;
 +
 +      kfree(buffer.pointer);
 +      buffer.length = ACPI_ALLOCATE_BUFFER;
 +      buffer.pointer = NULL;
 +
 +      tmp_map = cpu_present_map;
 +      mp_register_lapic(physid, lapic->lapic_flags & ACPI_MADT_ENABLED);
 +
 +      /*
 +       * If mp_register_lapic successfully generates a new logical cpu
 +       * number, then the following will get us exactly what was mapped
 +       */
 +      cpus_andnot(new_map, cpu_present_map, tmp_map);
 +      if (cpus_empty(new_map)) {
 +              printk ("Unable to map lapic to logical cpu number\n");
 +              return -EINVAL;
 +      }
 +
 +      cpu = first_cpu(new_map);
 +
 +      *pcpu = cpu;
 +      return 0;
 +}
 +
 +EXPORT_SYMBOL(acpi_map_lsapic);
 +
 +int acpi_unmap_lsapic(int cpu)
 +{
 +      x86_cpu_to_apicid[cpu] = -1;
 +      cpu_clear(cpu, cpu_present_map);
 +      num_processors--;
 +
 +      return (0);
 +}
 +
 +EXPORT_SYMBOL(acpi_unmap_lsapic);
 +#endif                                /* CONFIG_ACPI_HOTPLUG_CPU */
 +
 +int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base)
 +{
 +      /* TBD */
 +      return -EINVAL;
 +}
 +
 +EXPORT_SYMBOL(acpi_register_ioapic);
 +
 +int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base)
 +{
 +      /* TBD */
 +      return -EINVAL;
 +}
 +
 +EXPORT_SYMBOL(acpi_unregister_ioapic);
 +
 +static unsigned long __init
 +acpi_scan_rsdp(unsigned long start, unsigned long length)
 +{
 +      unsigned long offset = 0;
 +      unsigned long sig_len = sizeof("RSD PTR ") - 1;
 +
 +      /*
 +       * Scan all 16-byte boundaries of the physical memory region for the
 +       * RSDP signature.
 +       */
 +      for (offset = 0; offset < length; offset += 16) {
 +              if (strncmp((char *)(phys_to_virt(start) + offset), "RSD PTR ", sig_len))
 +                      continue;
 +              return (start + offset);
 +      }
 +
 +      return 0;
 +}
 +
 +static int __init acpi_parse_sbf(struct acpi_table_header *table)
 +{
 +      struct acpi_table_boot *sb;
 +
 +      sb = (struct acpi_table_boot *)table;
 +      if (!sb) {
 +              printk(KERN_WARNING PREFIX "Unable to map SBF\n");
 +              return -ENODEV;
 +      }
 +
 +      sbf_port = sb->cmos_index;      /* Save CMOS port */
 +
 +      return 0;
 +}
 +
 +#ifdef CONFIG_HPET_TIMER
 +#include <asm/hpet.h>
 +
 +static struct __initdata resource *hpet_res;
 +
 +static int __init acpi_parse_hpet(struct acpi_table_header *table)
 +{
 +      struct acpi_table_hpet *hpet_tbl;
 +
 +      hpet_tbl = (struct acpi_table_hpet *)table;
 +      if (!hpet_tbl) {
 +              printk(KERN_WARNING PREFIX "Unable to map HPET\n");
 +              return -ENODEV;
 +      }
 +
 +      if (hpet_tbl->address.space_id != ACPI_SPACE_MEM) {
 +              printk(KERN_WARNING PREFIX "HPET timers must be located in "
 +                     "memory.\n");
 +              return -1;
 +      }
 +
 +      hpet_address = hpet_tbl->address.address;
 +      printk(KERN_INFO PREFIX "HPET id: %#x base: %#lx\n",
 +             hpet_tbl->id, hpet_address);
 +
 +      /*
 +       * Allocate and initialize the HPET firmware resource for adding into
 +       * the resource tree during the lateinit timeframe.
 +       */
 +#define HPET_RESOURCE_NAME_SIZE 9
 +      hpet_res = alloc_bootmem(sizeof(*hpet_res) + HPET_RESOURCE_NAME_SIZE);
 +
 +      if (!hpet_res)
 +              return 0;
 +
 +      memset(hpet_res, 0, sizeof(*hpet_res));
 +      hpet_res->name = (void *)&hpet_res[1];
 +      hpet_res->flags = IORESOURCE_MEM;
 +      snprintf((char *)hpet_res->name, HPET_RESOURCE_NAME_SIZE, "HPET %u",
 +               hpet_tbl->sequence);
 +
 +      hpet_res->start = hpet_address;
 +      hpet_res->end = hpet_address + (1 * 1024) - 1;
 +
 +      return 0;
 +}
 +
 +/*
 + * hpet_insert_resource inserts the HPET resources used into the resource
 + * tree.
 + */
 +static __init int hpet_insert_resource(void)
 +{
 +      if (!hpet_res)
 +              return 1;
 +
 +      return insert_resource(&iomem_resource, hpet_res);
 +}
 +
 +late_initcall(hpet_insert_resource);
 +
 +#else
 +#define       acpi_parse_hpet NULL
 +#endif
 +
 +static int __init acpi_parse_fadt(struct acpi_table_header *table)
 +{
 +
 +#ifdef CONFIG_X86_PM_TIMER
 +      /* detect the location of the ACPI PM Timer */
 +      if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID) {
 +              /* FADT rev. 2 */
 +              if (acpi_gbl_FADT.xpm_timer_block.space_id !=
 +                  ACPI_ADR_SPACE_SYSTEM_IO)
 +                      return 0;
 +
 +              pmtmr_ioport = acpi_gbl_FADT.xpm_timer_block.address;
 +              /*
 +               * "X" fields are optional extensions to the original V1.0
 +               * fields, so we must selectively expand V1.0 fields if the
 +               * corresponding X field is zero.
 +               */
 +              if (!pmtmr_ioport)
 +                      pmtmr_ioport = acpi_gbl_FADT.pm_timer_block;
 +      } else {
 +              /* FADT rev. 1 */
 +              pmtmr_ioport = acpi_gbl_FADT.pm_timer_block;
 +      }
 +      if (pmtmr_ioport)
 +              printk(KERN_INFO PREFIX "PM-Timer IO Port: %#x\n",
 +                     pmtmr_ioport);
 +#endif
 +      return 0;
 +}
 +
 +unsigned long __init acpi_find_rsdp(void)
 +{
 +      unsigned long rsdp_phys = 0;
 +
 +      if (efi_enabled) {
 +              if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
 +                      return efi.acpi20;
 +              else if (efi.acpi != EFI_INVALID_TABLE_ADDR)
 +                      return efi.acpi;
 +      }
 +      /*
 +       * Scan memory looking for the RSDP signature. First search EBDA (low
 +       * memory) paragraphs and then search upper memory (E0000-FFFFF).
 +       */
 +      rsdp_phys = acpi_scan_rsdp(0, 0x400);
 +      if (!rsdp_phys)
 +              rsdp_phys = acpi_scan_rsdp(0xE0000, 0x20000);
 +
 +      return rsdp_phys;
 +}
 +
 +#ifdef        CONFIG_X86_LOCAL_APIC
 +/*
 + * Parse LAPIC entries in MADT
 + * returns 0 on success, < 0 on error
 + */
 +static int __init acpi_parse_madt_lapic_entries(void)
 +{
 +      int count;
 +
 +      if (!cpu_has_apic)
 +              return -ENODEV;
 +
 +      /*
 +       * Note that the LAPIC address is obtained from the MADT (32-bit value)
 +       * and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value).
 +       */
 +
 +      count =
 +          acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE,
 +                                acpi_parse_lapic_addr_ovr, 0);
 +      if (count < 0) {
 +              printk(KERN_ERR PREFIX
 +                     "Error parsing LAPIC address override entry\n");
 +              return count;
 +      }
 +
 +      mp_register_lapic_address(acpi_lapic_addr);
 +
 +      count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC, acpi_parse_lapic,
 +                                    MAX_APICS);
 +      if (!count) {
 +              printk(KERN_ERR PREFIX "No LAPIC entries present\n");
 +              /* TBD: Cleanup to allow fallback to MPS */
 +              return -ENODEV;
 +      } else if (count < 0) {
 +              printk(KERN_ERR PREFIX "Error parsing LAPIC entry\n");
 +              /* TBD: Cleanup to allow fallback to MPS */
 +              return count;
 +      }
 +
 +      count =
 +          acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC_NMI, acpi_parse_lapic_nmi, 0);
 +      if (count < 0) {
 +              printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n");
 +              /* TBD: Cleanup to allow fallback to MPS */
 +              return count;
 +      }
 +      return 0;
 +}
 +#endif                                /* CONFIG_X86_LOCAL_APIC */
 +
 +#ifdef        CONFIG_X86_IO_APIC
 +/*
 + * Parse IOAPIC related entries in MADT
 + * returns 0 on success, < 0 on error
 + */
 +static int __init acpi_parse_madt_ioapic_entries(void)
 +{
 +      int count;
 +
 +      /*
 +       * ACPI interpreter is required to complete interrupt setup,
 +       * so if it is off, don't enumerate the io-apics with ACPI.
 +       * If MPS is present, it will handle them,
 +       * otherwise the system will stay in PIC mode
 +       */
 +      if (acpi_disabled || acpi_noirq) {
 +              return -ENODEV;
 +      }
 +
 +      if (!cpu_has_apic)
 +              return -ENODEV;
 +
 +      /*
 +       * if "noapic" boot option, don't look for IO-APICs
 +       */
 +      if (skip_ioapic_setup) {
 +              printk(KERN_INFO PREFIX "Skipping IOAPIC probe "
 +                     "due to 'noapic' option.\n");
 +              return -ENODEV;
 +      }
 +
 +      count =
 +          acpi_table_parse_madt(ACPI_MADT_TYPE_IO_APIC, acpi_parse_ioapic,
 +                                MAX_IO_APICS);
 +      if (!count) {
 +              printk(KERN_ERR PREFIX "No IOAPIC entries present\n");
 +              return -ENODEV;
 +      } else if (count < 0) {
 +              printk(KERN_ERR PREFIX "Error parsing IOAPIC entry\n");
 +              return count;
 +      }
 +
 +      count =
 +          acpi_table_parse_madt(ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, acpi_parse_int_src_ovr,
 +                                NR_IRQ_VECTORS);
 +      if (count < 0) {
 +              printk(KERN_ERR PREFIX
 +                     "Error parsing interrupt source overrides entry\n");
 +              /* TBD: Cleanup to allow fallback to MPS */
 +              return count;
 +      }
 +
 +      /*
 +       * If BIOS did not supply an INT_SRC_OVR for the SCI
 +       * pretend we got one so we can set the SCI flags.
 +       */
 +      if (!acpi_sci_override_gsi)
 +              acpi_sci_ioapic_setup(acpi_gbl_FADT.sci_interrupt, 0, 0);
 +
 +      /* Fill in identity legacy mapings where no override */
 +      mp_config_acpi_legacy_irqs();
 +
 +      count =
 +          acpi_table_parse_madt(ACPI_MADT_TYPE_NMI_SOURCE, acpi_parse_nmi_src,
 +                                NR_IRQ_VECTORS);
 +      if (count < 0) {
 +              printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n");
 +              /* TBD: Cleanup to allow fallback to MPS */
 +              return count;
 +      }
 +
 +      return 0;
 +}
 +#else
 +static inline int acpi_parse_madt_ioapic_entries(void)
 +{
 +      return -1;
 +}
 +#endif        /* !CONFIG_X86_IO_APIC */
 +
 +static void __init acpi_process_madt(void)
 +{
 +#ifdef CONFIG_X86_LOCAL_APIC
 +      int error;
 +
 +      if (!acpi_table_parse(ACPI_SIG_MADT, acpi_parse_madt)) {
 +
 +              /*
 +               * Parse MADT LAPIC entries
 +               */
 +              error = acpi_parse_madt_lapic_entries();
 +              if (!error) {
 +                      acpi_lapic = 1;
 +
 +#ifdef CONFIG_X86_GENERICARCH
 +                      generic_bigsmp_probe();
 +#endif
 +                      /*
 +                       * Parse MADT IO-APIC entries
 +                       */
 +                      error = acpi_parse_madt_ioapic_entries();
 +                      if (!error) {
 +                              acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC;
 +                              acpi_irq_balance_set(NULL);
 +                              acpi_ioapic = 1;
 +
 +                              smp_found_config = 1;
 +                              setup_apic_routing();
 +                      }
 +              }
 +              if (error == -EINVAL) {
 +                      /*
 +                       * Dell Precision Workstation 410, 610 come here.
 +                       */
 +                      printk(KERN_ERR PREFIX
 +                             "Invalid BIOS MADT, disabling ACPI\n");
 +                      disable_acpi();
 +              }
 +      }
 +#endif
 +      return;
 +}
 +
 +#ifdef __i386__
 +
- static int __init disable_acpi_pci(struct dmi_system_id *d)
++static int __init disable_acpi_irq(const struct dmi_system_id *d)
 +{
 +      if (!acpi_force) {
 +              printk(KERN_NOTICE "%s detected: force use of acpi=noirq\n",
 +                     d->ident);
 +              acpi_noirq_set();
 +      }
 +      return 0;
 +}
 +
- static int __init dmi_disable_acpi(struct dmi_system_id *d)
++static int __init disable_acpi_pci(const struct dmi_system_id *d)
 +{
 +      if (!acpi_force) {
 +              printk(KERN_NOTICE "%s detected: force use of pci=noacpi\n",
 +                     d->ident);
 +              acpi_disable_pci();
 +      }
 +      return 0;
 +}
 +
- static int __init force_acpi_ht(struct dmi_system_id *d)
++static int __init dmi_disable_acpi(const struct dmi_system_id *d)
 +{
 +      if (!acpi_force) {
 +              printk(KERN_NOTICE "%s detected: acpi off\n", d->ident);
 +              disable_acpi();
 +      } else {
 +              printk(KERN_NOTICE
 +                     "Warning: DMI blacklist says broken, but acpi forced\n");
 +      }
 +      return 0;
 +}
 +
 +/*
 + * Limit ACPI to CPU enumeration for HT
 + */
++static int __init force_acpi_ht(const struct dmi_system_id *d)
 +{
 +      if (!acpi_force) {
 +              printk(KERN_NOTICE "%s detected: force use of acpi=ht\n",
 +                     d->ident);
 +              disable_acpi();
 +              acpi_ht = 1;
 +      } else {
 +              printk(KERN_NOTICE
 +                     "Warning: acpi=force overrules DMI blacklist: acpi=ht\n");
 +      }
 +      return 0;
 +}
 +
 +/*
 + * If your system is blacklisted here, but you find that acpi=force
 + * works for you, please contact acpi-devel@sourceforge.net
 + */
 +static struct dmi_system_id __initdata acpi_dmi_table[] = {
 +      /*
 +       * Boxes that need ACPI disabled
 +       */
 +      {
 +       .callback = dmi_disable_acpi,
 +       .ident = "IBM Thinkpad",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "IBM"),
 +                   DMI_MATCH(DMI_BOARD_NAME, "2629H1G"),
 +                   },
 +       },
 +
 +      /*
 +       * Boxes that need acpi=ht
 +       */
 +      {
 +       .callback = force_acpi_ht,
 +       .ident = "FSC Primergy T850",
 +       .matches = {
 +                   DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 +                   DMI_MATCH(DMI_PRODUCT_NAME, "PRIMERGY T850"),
 +                   },
 +       },
 +      {
 +       .callback = force_acpi_ht,
 +       .ident = "HP VISUALIZE NT Workstation",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
 +                   DMI_MATCH(DMI_PRODUCT_NAME, "HP VISUALIZE NT Workstation"),
 +                   },
 +       },
 +      {
 +       .callback = force_acpi_ht,
 +       .ident = "Compaq Workstation W8000",
 +       .matches = {
 +                   DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
 +                   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",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
 +                   DMI_MATCH(DMI_BOARD_NAME, "P2B-DS"),
 +                   },
 +       },
 +      {
 +       .callback = force_acpi_ht,
 +       .ident = "ASUS CUR-DLS",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
 +                   DMI_MATCH(DMI_BOARD_NAME, "CUR-DLS"),
 +                   },
 +       },
 +      {
 +       .callback = force_acpi_ht,
 +       .ident = "ABIT i440BX-W83977",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "ABIT <http://www.abit.com>"),
 +                   DMI_MATCH(DMI_BOARD_NAME, "i440BX-W83977 (BP6)"),
 +                   },
 +       },
 +      {
 +       .callback = force_acpi_ht,
 +       .ident = "IBM Bladecenter",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "IBM"),
 +                   DMI_MATCH(DMI_BOARD_NAME, "IBM eServer BladeCenter HS20"),
 +                   },
 +       },
 +      {
 +       .callback = force_acpi_ht,
 +       .ident = "IBM eServer xSeries 360",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "IBM"),
 +                   DMI_MATCH(DMI_BOARD_NAME, "eServer xSeries 360"),
 +                   },
 +       },
 +      {
 +       .callback = force_acpi_ht,
 +       .ident = "IBM eserver xSeries 330",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "IBM"),
 +                   DMI_MATCH(DMI_BOARD_NAME, "eserver xSeries 330"),
 +                   },
 +       },
 +      {
 +       .callback = force_acpi_ht,
 +       .ident = "IBM eserver xSeries 440",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "IBM"),
 +                   DMI_MATCH(DMI_PRODUCT_NAME, "eserver xSeries 440"),
 +                   },
 +       },
 +
 +      /*
 +       * Boxes that need ACPI PCI IRQ routing disabled
 +       */
 +      {
 +       .callback = disable_acpi_irq,
 +       .ident = "ASUS A7V",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC"),
 +                   DMI_MATCH(DMI_BOARD_NAME, "<A7V>"),
 +                   /* newer BIOS, Revision 1011, does work */
 +                   DMI_MATCH(DMI_BIOS_VERSION,
 +                             "ASUS A7V ACPI BIOS Revision 1007"),
 +                   },
 +       },
 +      {
 +              /*
 +               * Latest BIOS for IBM 600E (1.16) has bad pcinum
 +               * for LPC bridge, which is needed for the PCI
 +               * interrupt links to work. DSDT fix is in bug 5966.
 +               * 2645, 2646 model numbers are shared with 600/600E/600X
 +               */
 +       .callback = disable_acpi_irq,
 +       .ident = "IBM Thinkpad 600 Series 2645",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "IBM"),
 +                   DMI_MATCH(DMI_BOARD_NAME, "2645"),
 +                   },
 +       },
 +      {
 +       .callback = disable_acpi_irq,
 +       .ident = "IBM Thinkpad 600 Series 2646",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "IBM"),
 +                   DMI_MATCH(DMI_BOARD_NAME, "2646"),
 +                   },
 +       },
 +      /*
 +       * Boxes that need ACPI PCI IRQ routing and PCI scan disabled
 +       */
 +      {                       /* _BBN 0 bug */
 +       .callback = disable_acpi_pci,
 +       .ident = "ASUS PR-DLS",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
 +                   DMI_MATCH(DMI_BOARD_NAME, "PR-DLS"),
 +                   DMI_MATCH(DMI_BIOS_VERSION,
 +                             "ASUS PR-DLS ACPI BIOS Revision 1010"),
 +                   DMI_MATCH(DMI_BIOS_DATE, "03/21/2003")
 +                   },
 +       },
 +      {
 +       .callback = disable_acpi_pci,
 +       .ident = "Acer TravelMate 36x Laptop",
 +       .matches = {
 +                   DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 +                   DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),
 +                   },
 +       },
 +      {}
 +};
 +
 +#endif                                /* __i386__ */
 +
 +/*
 + * acpi_boot_table_init() and acpi_boot_init()
 + *  called from setup_arch(), always.
 + *    1. checksums all tables
 + *    2. enumerates lapics
 + *    3. enumerates io-apics
 + *
 + * acpi_table_init() is separate to allow reading SRAT without
 + * other side effects.
 + *
 + * side effects of acpi_boot_init:
 + *    acpi_lapic = 1 if LAPIC found
 + *    acpi_ioapic = 1 if IOAPIC found
 + *    if (acpi_lapic && acpi_ioapic) smp_found_config = 1;
 + *    if acpi_blacklisted() acpi_disabled = 1;
 + *    acpi_irq_model=...
 + *    ...
 + *
 + * return value: (currently ignored)
 + *    0: success
 + *    !0: failure
 + */
 +
 +int __init acpi_boot_table_init(void)
 +{
 +      int error;
 +
 +#ifdef __i386__
 +      dmi_check_system(acpi_dmi_table);
 +#endif
 +
 +      /*
 +       * If acpi_disabled, bail out
 +       * One exception: acpi=ht continues far enough to enumerate LAPICs
 +       */
 +      if (acpi_disabled && !acpi_ht)
 +              return 1;
 +
 +      /*
 +       * Initialize the ACPI boot-time table parser.
 +       */
 +      error = acpi_table_init();
 +      if (error) {
 +              disable_acpi();
 +              return error;
 +      }
 +
 +      acpi_table_parse(ACPI_SIG_BOOT, acpi_parse_sbf);
 +
 +      /*
 +       * blacklist may disable ACPI entirely
 +       */
 +      error = acpi_blacklisted();
 +      if (error) {
 +              if (acpi_force) {
 +                      printk(KERN_WARNING PREFIX "acpi=force override\n");
 +              } else {
 +                      printk(KERN_WARNING PREFIX "Disabling ACPI support\n");
 +                      disable_acpi();
 +                      return error;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +int __init acpi_boot_init(void)
 +{
 +      /*
 +       * If acpi_disabled, bail out
 +       * One exception: acpi=ht continues far enough to enumerate LAPICs
 +       */
 +      if (acpi_disabled && !acpi_ht)
 +              return 1;
 +
 +      acpi_table_parse(ACPI_SIG_BOOT, acpi_parse_sbf);
 +
 +      /*
 +       * set sci_int and PM timer address
 +       */
 +      acpi_table_parse(ACPI_SIG_FADT, acpi_parse_fadt);
 +
 +      /*
 +       * Process the Multiple APIC Description Table (MADT), if present
 +       */
 +      acpi_process_madt();
 +
 +      acpi_table_parse(ACPI_SIG_HPET, acpi_parse_hpet);
 +
 +      return 0;
 +}
 +
 +static int __init parse_acpi(char *arg)
 +{
 +      if (!arg)
 +              return -EINVAL;
 +
 +      /* "acpi=off" disables both ACPI table parsing and interpreter */
 +      if (strcmp(arg, "off") == 0) {
 +              disable_acpi();
 +      }
 +      /* acpi=force to over-ride black-list */
 +      else if (strcmp(arg, "force") == 0) {
 +              acpi_force = 1;
 +              acpi_ht = 1;
 +              acpi_disabled = 0;
 +      }
 +      /* acpi=strict disables out-of-spec workarounds */
 +      else if (strcmp(arg, "strict") == 0) {
 +              acpi_strict = 1;
 +      }
 +      /* Limit ACPI just to boot-time to enable HT */
 +      else if (strcmp(arg, "ht") == 0) {
 +              if (!acpi_force)
 +                      disable_acpi();
 +              acpi_ht = 1;
 +      }
 +      /* "acpi=noirq" disables ACPI interrupt routing */
 +      else if (strcmp(arg, "noirq") == 0) {
 +              acpi_noirq_set();
 +      } else {
 +              /* Core will printk when we return error. */
 +              return -EINVAL;
 +      }
 +      return 0;
 +}
 +early_param("acpi", parse_acpi);
 +
 +/* FIXME: Using pci= for an ACPI parameter is a travesty. */
 +static int __init parse_pci(char *arg)
 +{
 +      if (arg && strcmp(arg, "noacpi") == 0)
 +              acpi_disable_pci();
 +      return 0;
 +}
 +early_param("pci", parse_pci);
 +
 +#ifdef CONFIG_X86_IO_APIC
 +static int __init parse_acpi_skip_timer_override(char *arg)
 +{
 +      acpi_skip_timer_override = 1;
 +      return 0;
 +}
 +early_param("acpi_skip_timer_override", parse_acpi_skip_timer_override);
 +
 +static int __init parse_acpi_use_timer_override(char *arg)
 +{
 +      acpi_use_timer_override = 1;
 +      return 0;
 +}
 +early_param("acpi_use_timer_override", parse_acpi_use_timer_override);
 +#endif /* CONFIG_X86_IO_APIC */
 +
 +static int __init setup_acpi_sci(char *s)
 +{
 +      if (!s)
 +              return -EINVAL;
 +      if (!strcmp(s, "edge"))
 +              acpi_sci_flags =  ACPI_MADT_TRIGGER_EDGE |
 +                      (acpi_sci_flags & ~ACPI_MADT_TRIGGER_MASK);
 +      else if (!strcmp(s, "level"))
 +              acpi_sci_flags = ACPI_MADT_TRIGGER_LEVEL |
 +                      (acpi_sci_flags & ~ACPI_MADT_TRIGGER_MASK);
 +      else if (!strcmp(s, "high"))
 +              acpi_sci_flags = ACPI_MADT_POLARITY_ACTIVE_HIGH |
 +                      (acpi_sci_flags & ~ACPI_MADT_POLARITY_MASK);
 +      else if (!strcmp(s, "low"))
 +              acpi_sci_flags = ACPI_MADT_POLARITY_ACTIVE_LOW |
 +                      (acpi_sci_flags & ~ACPI_MADT_POLARITY_MASK);
 +      else
 +              return -EINVAL;
 +      return 0;
 +}
 +early_param("acpi_sci", setup_acpi_sci);
 +
 +int __acpi_acquire_global_lock(unsigned int *lock)
 +{
 +      unsigned int old, new, val;
 +      do {
 +              old = *lock;
 +              new = (((old & ~0x3) + 2) + ((old >> 1) & 0x1));
 +              val = cmpxchg(lock, old, new);
 +      } while (unlikely (val != old));
 +      return (new < 3) ? -1 : 0;
 +}
 +
 +int __acpi_release_global_lock(unsigned int *lock)
 +{
 +      unsigned int old, new, val;
 +      do {
 +              old = *lock;
 +              new = old & ~0x3;
 +              val = cmpxchg(lock, old, new);
 +      } while (unlikely (val != old));
 +      return old & 0x1;
 +}
index c42b5ab,0000000..1069948
mode 100644,000000..100644
--- /dev/null
@@@ -1,110 -1,0 +1,110 @@@
- static __init int reset_videomode_after_s3(struct dmi_system_id *d)
 +/*
 + * sleep.c - x86-specific ACPI sleep support.
 + *
 + *  Copyright (C) 2001-2003 Patrick Mochel
 + *  Copyright (C) 2001-2003 Pavel Machek <pavel@suse.cz>
 + */
 +
 +#include <linux/acpi.h>
 +#include <linux/bootmem.h>
 +#include <linux/dmi.h>
 +#include <linux/cpumask.h>
 +
 +#include <asm/smp.h>
 +
 +/* address in low memory of the wakeup routine. */
 +unsigned long acpi_wakeup_address = 0;
 +unsigned long acpi_realmode_flags;
 +extern char wakeup_start, wakeup_end;
 +
 +extern unsigned long FASTCALL(acpi_copy_wakeup_routine(unsigned long));
 +
 +/**
 + * acpi_save_state_mem - save kernel state
 + *
 + * Create an identity mapped page table and copy the wakeup routine to
 + * low memory.
 + */
 +int acpi_save_state_mem(void)
 +{
 +      if (!acpi_wakeup_address)
 +              return 1;
 +      memcpy((void *)acpi_wakeup_address, &wakeup_start,
 +             &wakeup_end - &wakeup_start);
 +      acpi_copy_wakeup_routine(acpi_wakeup_address);
 +
 +      return 0;
 +}
 +
 +/*
 + * acpi_restore_state - undo effects of acpi_save_state_mem
 + */
 +void acpi_restore_state_mem(void)
 +{
 +}
 +
 +/**
 + * acpi_reserve_bootmem - do _very_ early ACPI initialisation
 + *
 + * We allocate a page from the first 1MB of memory for the wakeup
 + * routine for when we come back from a sleep state. The
 + * runtime allocator allows specification of <16MB pages, but not
 + * <1MB pages.
 + */
 +void __init acpi_reserve_bootmem(void)
 +{
 +      if ((&wakeup_end - &wakeup_start) > PAGE_SIZE) {
 +              printk(KERN_ERR
 +                     "ACPI: Wakeup code way too big, S3 disabled.\n");
 +              return;
 +      }
 +
 +      acpi_wakeup_address = (unsigned long)alloc_bootmem_low(PAGE_SIZE);
 +      if (!acpi_wakeup_address)
 +              printk(KERN_ERR "ACPI: Cannot allocate lowmem, S3 disabled.\n");
 +}
 +
 +static int __init acpi_sleep_setup(char *str)
 +{
 +      while ((str != NULL) && (*str != '\0')) {
 +              if (strncmp(str, "s3_bios", 7) == 0)
 +                      acpi_realmode_flags |= 1;
 +              if (strncmp(str, "s3_mode", 7) == 0)
 +                      acpi_realmode_flags |= 2;
 +              if (strncmp(str, "s3_beep", 7) == 0)
 +                      acpi_realmode_flags |= 4;
 +              str = strchr(str, ',');
 +              if (str != NULL)
 +                      str += strspn(str, ", \t");
 +      }
 +      return 1;
 +}
 +
 +__setup("acpi_sleep=", acpi_sleep_setup);
 +
 +/* Ouch, we want to delete this. We already have better version in userspace, in
 +   s2ram from suspend.sf.net project */
++static __init int reset_videomode_after_s3(const struct dmi_system_id *d)
 +{
 +      acpi_realmode_flags |= 2;
 +      return 0;
 +}
 +
 +static __initdata struct dmi_system_id acpisleep_dmi_table[] = {
 +      {                       /* Reset video mode after returning from ACPI S3 sleep */
 +       .callback = reset_videomode_after_s3,
 +       .ident = "Toshiba Satellite 4030cdt",
 +       .matches = {
 +                   DMI_MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"),
 +                   },
 +       },
 +      {}
 +};
 +
 +static int __init acpisleep_dmi_init(void)
 +{
 +      dmi_check_system(acpisleep_dmi_table);
 +      return 0;
 +}
 +
 +core_initcall(acpisleep_dmi_init);
index f02a8ac,0000000..32f2365
mode 100644,000000..100644
--- /dev/null
@@@ -1,2403 -1,0 +1,2403 @@@
- static int __init print_if_true(struct dmi_system_id *d)
 +/* -*- linux-c -*-
 + * APM BIOS driver for Linux
 + * Copyright 1994-2001 Stephen Rothwell (sfr@canb.auug.org.au)
 + *
 + * Initial development of this driver was funded by NEC Australia P/L
 + *    and NEC Corporation
 + *
 + * 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, 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.
 + *
 + * October 1995, Rik Faith (faith@cs.unc.edu):
 + *    Minor enhancements and updates (to the patch set) for 1.3.x
 + *    Documentation
 + * January 1996, Rik Faith (faith@cs.unc.edu):
 + *    Make /proc/apm easy to format (bump driver version)
 + * March 1996, Rik Faith (faith@cs.unc.edu):
 + *    Prohibit APM BIOS calls unless apm_enabled.
 + *    (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>)
 + * April 1996, Stephen Rothwell (sfr@canb.auug.org.au)
 + *    Version 1.0 and 1.1
 + * May 1996, Version 1.2
 + * Feb 1998, Version 1.3
 + * Feb 1998, Version 1.4
 + * Aug 1998, Version 1.5
 + * Sep 1998, Version 1.6
 + * Nov 1998, Version 1.7
 + * Jan 1999, Version 1.8
 + * Jan 1999, Version 1.9
 + * Oct 1999, Version 1.10
 + * Nov 1999, Version 1.11
 + * Jan 2000, Version 1.12
 + * Feb 2000, Version 1.13
 + * Nov 2000, Version 1.14
 + * Oct 2001, Version 1.15
 + * Jan 2002, Version 1.16
 + * Oct 2002, Version 1.16ac
 + *
 + * History:
 + *    0.6b: first version in official kernel, Linux 1.3.46
 + *    0.7: changed /proc/apm format, Linux 1.3.58
 + *    0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
 + *    0.9: only call bios if bios is present, Linux 1.3.72
 + *    1.0: use fixed device number, consolidate /proc/apm into this file,
 + *         Linux 1.3.85
 + *    1.1: support user-space standby and suspend, power off after system
 + *         halted, Linux 1.3.98
 + *    1.2: When resetting RTC after resume, take care so that the time
 + *         is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
 + *         <jtoth@princeton.edu>); improve interaction between
 + *         screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
 + *    1.2a:Simple change to stop mysterious bug reports with SMP also added
 + *       levels to the printk calls. APM is not defined for SMP machines.
 + *         The new replacment for it is, but Linux doesn't yet support this.
 + *         Alan Cox Linux 2.1.55
 + *    1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
 + *    1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by
 + *         Dean Gaudet <dgaudet@arctic.org>.
 + *         C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
 + *    1.5: Fix segment register reloading (in case of bad segments saved
 + *         across BIOS call).
 + *         Stephen Rothwell
 + *    1.6: Cope with complier/assembler differences.
 + *         Only try to turn off the first display device.
 + *         Fix OOPS at power off with no APM BIOS by Jan Echternach
 + *                   <echter@informatik.uni-rostock.de>
 + *         Stephen Rothwell
 + *    1.7: Modify driver's cached copy of the disabled/disengaged flags
 + *         to reflect current state of APM BIOS.
 + *         Chris Rankin <rankinc@bellsouth.net>
 + *         Reset interrupt 0 timer to 100Hz after suspend
 + *         Chad Miller <cmiller@surfsouth.com>
 + *         Add CONFIG_APM_IGNORE_SUSPEND_BOUNCE
 + *         Richard Gooch <rgooch@atnf.csiro.au>
 + *         Allow boot time disabling of APM
 + *         Make boot messages far less verbose by default
 + *         Make asm safer
 + *         Stephen Rothwell
 + *    1.8: Add CONFIG_APM_RTC_IS_GMT
 + *         Richard Gooch <rgooch@atnf.csiro.au>
 + *         change APM_NOINTS to CONFIG_APM_ALLOW_INTS
 + *         remove dependency on CONFIG_PROC_FS
 + *         Stephen Rothwell
 + *    1.9: Fix small typo.  <laslo@wodip.opole.pl>
 + *         Try to cope with BIOS's that need to have all display
 + *         devices blanked and not just the first one.
 + *         Ross Paterson <ross@soi.city.ac.uk>
 + *         Fix segment limit setting it has always been wrong as
 + *         the segments needed to have byte granularity.
 + *         Mark a few things __init.
 + *         Add hack to allow power off of SMP systems by popular request.
 + *         Use CONFIG_SMP instead of __SMP__
 + *         Ignore BOUNCES for three seconds.
 + *         Stephen Rothwell
 + *   1.10: Fix for Thinkpad return code.
 + *         Merge 2.2 and 2.3 drivers.
 + *         Remove APM dependencies in arch/i386/kernel/process.c
 + *         Remove APM dependencies in drivers/char/sysrq.c
 + *         Reset time across standby.
 + *         Allow more inititialisation on SMP.
 + *         Remove CONFIG_APM_POWER_OFF and make it boot time
 + *         configurable (default on).
 + *         Make debug only a boot time parameter (remove APM_DEBUG).
 + *         Try to blank all devices on any error.
 + *   1.11: Remove APM dependencies in drivers/char/console.c
 + *         Check nr_running to detect if we are idle (from
 + *         Borislav Deianov <borislav@lix.polytechnique.fr>)
 + *         Fix for bioses that don't zero the top part of the
 + *         entrypoint offset (Mario Sitta <sitta@al.unipmn.it>)
 + *         (reported by Panos Katsaloulis <teras@writeme.com>).
 + *         Real mode power off patch (Walter Hofmann
 + *         <Walter.Hofmann@physik.stud.uni-erlangen.de>).
 + *   1.12: Remove CONFIG_SMP as the compiler will optimize
 + *         the code away anyway (smp_num_cpus == 1 in UP)
 + *         noted by Artur Skawina <skawina@geocities.com>.
 + *         Make power off under SMP work again.
 + *         Fix thinko with initial engaging of BIOS.
 + *         Make sure power off only happens on CPU 0
 + *         (Paul "Rusty" Russell <rusty@rustcorp.com.au>).
 + *         Do error notification to user mode if BIOS calls fail.
 + *         Move entrypoint offset fix to ...boot/setup.S
 + *         where it belongs (Cosmos <gis88564@cis.nctu.edu.tw>).
 + *         Remove smp-power-off. SMP users must now specify
 + *         "apm=power-off" on the kernel command line. Suggested
 + *         by Jim Avera <jima@hal.com>, modified by Alan Cox
 + *         <alan@lxorguk.ukuu.org.uk>.
 + *         Register the /proc/apm entry even on SMP so that
 + *         scripts that check for it before doing power off
 + *         work (Jim Avera <jima@hal.com>).
 + *   1.13: Changes for new pm_ interfaces (Andy Henroid
 + *         <andy_henroid@yahoo.com>).
 + *         Modularize the code.
 + *         Fix the Thinkpad (again) :-( (CONFIG_APM_IGNORE_MULTIPLE_SUSPENDS
 + *         is now the way life works).
 + *         Fix thinko in suspend() (wrong return).
 + *         Notify drivers on critical suspend.
 + *         Make kapmd absorb more idle time (Pavel Machek <pavel@suse.cz>
 + *         modified by sfr).
 + *         Disable interrupts while we are suspended (Andy Henroid
 + *         <andy_henroid@yahoo.com> fixed by sfr).
 + *         Make power off work on SMP again (Tony Hoyle
 + *         <tmh@magenta-logic.com> and <zlatko@iskon.hr>) modified by sfr.
 + *         Remove CONFIG_APM_SUSPEND_BOUNCE.  The bounce ignore
 + *         interval is now configurable.
 + *   1.14: Make connection version persist across module unload/load.
 + *         Enable and engage power management earlier.
 + *         Disengage power management on module unload.
 + *         Changed to use the sysrq-register hack for registering the
 + *         power off function called by magic sysrq based upon discussions
 + *         in irc://irc.openprojects.net/#kernelnewbies
 + *         (Crutcher Dunnavant <crutcher+kernel@datastacks.com>).
 + *         Make CONFIG_APM_REAL_MODE_POWER_OFF run time configurable.
 + *         (Arjan van de Ven <arjanv@redhat.com>) modified by sfr.
 + *         Work around byte swap bug in one of the Vaio's BIOS's
 + *         (Marc Boucher <marc@mbsi.ca>).
 + *         Exposed the disable flag to dmi so that we can handle known
 + *         broken APM (Alan Cox <alan@redhat.com>).
 + *   1.14ac: If the BIOS says "I slowed the CPU down" then don't spin
 + *         calling it - instead idle. (Alan Cox <alan@redhat.com>)
 + *         If an APM idle fails log it and idle sensibly
 + *   1.15: Don't queue events to clients who open the device O_WRONLY.
 + *         Don't expect replies from clients who open the device O_RDONLY.
 + *         (Idea from Thomas Hood)
 + *         Minor waitqueue cleanups. (John Fremlin <chief@bandits.org>)
 + *   1.16: Fix idle calling. (Andreas Steinmetz <ast@domdv.de> et al.)
 + *         Notify listeners of standby or suspend events before notifying
 + *         drivers. Return EBUSY to ioctl() if suspend is rejected.
 + *         (Russell King <rmk@arm.linux.org.uk> and Thomas Hood)
 + *         Ignore first resume after we generate our own resume event
 + *         after a suspend (Thomas Hood)
 + *         Daemonize now gets rid of our controlling terminal (sfr).
 + *         CONFIG_APM_CPU_IDLE now just affects the default value of
 + *         idle_threshold (sfr).
 + *         Change name of kernel apm daemon (as it no longer idles) (sfr).
 + *   1.16ac: Fix up SMP support somewhat. You can now force SMP on and we
 + *       make _all_ APM calls on the CPU#0. Fix unsafe sign bug.
 + *       TODO: determine if its "boot CPU" or "CPU0" we want to lock to.
 + *
 + * APM 1.1 Reference:
 + *
 + *   Intel Corporation, Microsoft Corporation. Advanced Power Management
 + *   (APM) BIOS Interface Specification, Revision 1.1, September 1993.
 + *   Intel Order Number 241704-001.  Microsoft Part Number 781-110-X01.
 + *
 + * [This document is available free from Intel by calling 800.628.8686 (fax
 + * 916.356.6100) or 800.548.4725; or via anonymous ftp from
 + * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc.  It is also
 + * available from Microsoft by calling 206.882.8080.]
 + *
 + * APM 1.2 Reference:
 + *   Intel Corporation, Microsoft Corporation. Advanced Power Management
 + *   (APM) BIOS Interface Specification, Revision 1.2, February 1996.
 + *
 + * [This document is available from Microsoft at:
 + *    http://www.microsoft.com/whdc/archive/amp_12.mspx]
 + */
 +
 +#include <linux/module.h>
 +
 +#include <linux/poll.h>
 +#include <linux/types.h>
 +#include <linux/stddef.h>
 +#include <linux/timer.h>
 +#include <linux/fcntl.h>
 +#include <linux/slab.h>
 +#include <linux/stat.h>
 +#include <linux/proc_fs.h>
 +#include <linux/seq_file.h>
 +#include <linux/miscdevice.h>
 +#include <linux/apm_bios.h>
 +#include <linux/init.h>
 +#include <linux/time.h>
 +#include <linux/sched.h>
 +#include <linux/pm.h>
 +#include <linux/pm_legacy.h>
 +#include <linux/capability.h>
 +#include <linux/device.h>
 +#include <linux/kernel.h>
 +#include <linux/freezer.h>
 +#include <linux/smp.h>
 +#include <linux/dmi.h>
 +#include <linux/suspend.h>
 +#include <linux/kthread.h>
 +
 +#include <asm/system.h>
 +#include <asm/uaccess.h>
 +#include <asm/desc.h>
 +#include <asm/i8253.h>
 +#include <asm/paravirt.h>
 +#include <asm/reboot.h>
 +
 +#include "io_ports.h"
 +
 +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
 +extern int (*console_blank_hook)(int);
 +#endif
 +
 +/*
 + * The apm_bios device is one of the misc char devices.
 + * This is its minor number.
 + */
 +#define       APM_MINOR_DEV   134
 +
 +/*
 + * See Documentation/Config.help for the configuration options.
 + *
 + * Various options can be changed at boot time as follows:
 + * (We allow underscores for compatibility with the modules code)
 + *    apm=on/off                      enable/disable APM
 + *        [no-]allow[-_]ints          allow interrupts during BIOS calls
 + *        [no-]broken[-_]psr          BIOS has a broken GetPowerStatus call
 + *        [no-]realmode[-_]power[-_]off       switch to real mode before
 + *                                            powering off
 + *        [no-]debug                  log some debugging messages
 + *        [no-]power[-_]off           power off on shutdown
 + *        [no-]smp                    Use apm even on an SMP box
 + *        bounce[-_]interval=<n>      number of ticks to ignore suspend
 + *                                    bounces
 + *          idle[-_]threshold=<n>       System idle percentage above which to
 + *                                      make APM BIOS idle calls. Set it to
 + *                                      100 to disable.
 + *          idle[-_]period=<n>          Period (in 1/100s of a second) over
 + *                                      which the idle percentage is
 + *                                      calculated.
 + */
 +
 +/* KNOWN PROBLEM MACHINES:
 + *
 + * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
 + *                         [Confirmed by TI representative]
 + * ?: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
 + *                    [Confirmed by BIOS disassembly]
 + *                    [This may work now ...]
 + * P: Toshiba 1950S: battery life information only gets updated after resume
 + * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
 + *    broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
 + * ?: AcerNote-950: oops on reading /proc/apm - workaround is a WIP
 + *    Neale Banks <neale@lowendale.com.au> December 2000
 + *
 + * Legend: U = unusable with APM patches
 + *         P = partially usable with APM patches
 + */
 +
 +/*
 + * Define as 1 to make the driver always call the APM BIOS busy
 + * routine even if the clock was not reported as slowed by the
 + * idle routine.  Otherwise, define as 0.
 + */
 +#define ALWAYS_CALL_BUSY   1
 +
 +/*
 + * Define to make the APM BIOS calls zero all data segment registers (so
 + * that an incorrect BIOS implementation will cause a kernel panic if it
 + * tries to write to arbitrary memory).
 + */
 +#define APM_ZERO_SEGS
 +
 +#include "apm.h"
 +
 +/*
 + * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend.
 + * This patched by Chad Miller <cmiller@surfsouth.com>, original code by
 + * David Chen <chen@ctpa04.mit.edu>
 + */
 +#undef INIT_TIMER_AFTER_SUSPEND
 +
 +#ifdef INIT_TIMER_AFTER_SUSPEND
 +#include <linux/timex.h>
 +#include <asm/io.h>
 +#include <linux/delay.h>
 +#endif
 +
 +/*
 + * Need to poll the APM BIOS every second
 + */
 +#define APM_CHECK_TIMEOUT     (HZ)
 +
 +/*
 + * Ignore suspend events for this amount of time after a resume
 + */
 +#define DEFAULT_BOUNCE_INTERVAL               (3 * HZ)
 +
 +/*
 + * Maximum number of events stored
 + */
 +#define APM_MAX_EVENTS                20
 +
 +/*
 + * The per-file APM data
 + */
 +struct apm_user {
 +      int             magic;
 +      struct apm_user *       next;
 +      unsigned int    suser: 1;
 +      unsigned int    writer: 1;
 +      unsigned int    reader: 1;
 +      unsigned int    suspend_wait: 1;
 +      int             suspend_result;
 +      int             suspends_pending;
 +      int             standbys_pending;
 +      int             suspends_read;
 +      int             standbys_read;
 +      int             event_head;
 +      int             event_tail;
 +      apm_event_t     events[APM_MAX_EVENTS];
 +};
 +
 +/*
 + * The magic number in apm_user
 + */
 +#define APM_BIOS_MAGIC                0x4101
 +
 +/*
 + * idle percentage above which bios idle calls are done
 + */
 +#ifdef CONFIG_APM_CPU_IDLE
 +#define DEFAULT_IDLE_THRESHOLD        95
 +#else
 +#define DEFAULT_IDLE_THRESHOLD        100
 +#endif
 +#define DEFAULT_IDLE_PERIOD   (100 / 3)
 +
 +/*
 + * Local variables
 + */
 +static struct {
 +      unsigned long   offset;
 +      unsigned short  segment;
 +}                             apm_bios_entry;
 +static int                    clock_slowed;
 +static int                    idle_threshold __read_mostly = DEFAULT_IDLE_THRESHOLD;
 +static int                    idle_period __read_mostly = DEFAULT_IDLE_PERIOD;
 +static int                    set_pm_idle;
 +static int                    suspends_pending;
 +static int                    standbys_pending;
 +static int                    ignore_sys_suspend;
 +static int                    ignore_normal_resume;
 +static int                    bounce_interval __read_mostly = DEFAULT_BOUNCE_INTERVAL;
 +
 +static int                    debug __read_mostly;
 +static int                    smp __read_mostly;
 +static int                    apm_disabled = -1;
 +#ifdef CONFIG_SMP
 +static int                    power_off;
 +#else
 +static int                    power_off = 1;
 +#endif
 +#ifdef CONFIG_APM_REAL_MODE_POWER_OFF
 +static int                    realmode_power_off = 1;
 +#else
 +static int                    realmode_power_off;
 +#endif
 +#ifdef CONFIG_APM_ALLOW_INTS
 +static int                    allow_ints = 1;
 +#else
 +static int                    allow_ints;
 +#endif
 +static int                    broken_psr;
 +
 +static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
 +static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
 +static struct apm_user *      user_list;
 +static DEFINE_SPINLOCK(user_list_lock);
 +static const struct desc_struct       bad_bios_desc = { 0, 0x00409200 };
 +
 +static const char             driver_version[] = "1.16ac";    /* no spaces */
 +
 +static struct task_struct *kapmd_task;
 +
 +/*
 + *    APM event names taken from the APM 1.2 specification. These are
 + *    the message codes that the BIOS uses to tell us about events
 + */
 +static const char *   const apm_event_name[] = {
 +      "system standby",
 +      "system suspend",
 +      "normal resume",
 +      "critical resume",
 +      "low battery",
 +      "power status change",
 +      "update time",
 +      "critical suspend",
 +      "user standby",
 +      "user suspend",
 +      "system standby resume",
 +      "capabilities change"
 +};
 +#define NR_APM_EVENT_NAME ARRAY_SIZE(apm_event_name)
 +
 +typedef struct lookup_t {
 +      int     key;
 +      char *  msg;
 +} lookup_t;
 +
 +/*
 + *    The BIOS returns a set of standard error codes in AX when the
 + *    carry flag is set.
 + */
 + 
 +static const lookup_t error_table[] = {
 +/* N/A        { APM_SUCCESS,          "Operation succeeded" }, */
 +      { APM_DISABLED,         "Power management disabled" },
 +      { APM_CONNECTED,        "Real mode interface already connected" },
 +      { APM_NOT_CONNECTED,    "Interface not connected" },
 +      { APM_16_CONNECTED,     "16 bit interface already connected" },
 +/* N/A        { APM_16_UNSUPPORTED,   "16 bit interface not supported" }, */
 +      { APM_32_CONNECTED,     "32 bit interface already connected" },
 +      { APM_32_UNSUPPORTED,   "32 bit interface not supported" },
 +      { APM_BAD_DEVICE,       "Unrecognized device ID" },
 +      { APM_BAD_PARAM,        "Parameter out of range" },
 +      { APM_NOT_ENGAGED,      "Interface not engaged" },
 +      { APM_BAD_FUNCTION,     "Function not supported" },
 +      { APM_RESUME_DISABLED,  "Resume timer disabled" },
 +      { APM_BAD_STATE,        "Unable to enter requested state" },
 +/* N/A        { APM_NO_EVENTS,        "No events pending" }, */
 +      { APM_NO_ERROR,         "BIOS did not set a return code" },
 +      { APM_NOT_PRESENT,      "No APM present" }
 +};
 +#define ERROR_COUNT   ARRAY_SIZE(error_table)
 +
 +/**
 + *    apm_error       -       display an APM error
 + *    @str: information string
 + *    @err: APM BIOS return code
 + *
 + *    Write a meaningful log entry to the kernel log in the event of
 + *    an APM error.
 + */
 + 
 +static void apm_error(char *str, int err)
 +{
 +      int     i;
 +
 +      for (i = 0; i < ERROR_COUNT; i++)
 +              if (error_table[i].key == err) break;
 +      if (i < ERROR_COUNT)
 +              printk(KERN_NOTICE "apm: %s: %s\n", str, error_table[i].msg);
 +      else
 +              printk(KERN_NOTICE "apm: %s: unknown error code %#2.2x\n",
 +                      str, err);
 +}
 +
 +/*
 + * Lock APM functionality to physical CPU 0
 + */
 + 
 +#ifdef CONFIG_SMP
 +
 +static cpumask_t apm_save_cpus(void)
 +{
 +      cpumask_t x = current->cpus_allowed;
 +      /* Some bioses don't like being called from CPU != 0 */
 +      set_cpus_allowed(current, cpumask_of_cpu(0));
 +      BUG_ON(smp_processor_id() != 0);
 +      return x;
 +}
 +
 +static inline void apm_restore_cpus(cpumask_t mask)
 +{
 +      set_cpus_allowed(current, mask);
 +}
 +
 +#else
 +
 +/*
 + *    No CPU lockdown needed on a uniprocessor
 + */
 + 
 +#define apm_save_cpus()               (current->cpus_allowed)
 +#define apm_restore_cpus(x)   (void)(x)
 +
 +#endif
 +
 +/*
 + * These are the actual BIOS calls.  Depending on APM_ZERO_SEGS and
 + * apm_info.allow_ints, we are being really paranoid here!  Not only
 + * are interrupts disabled, but all the segment registers (except SS)
 + * are saved and zeroed this means that if the BIOS tries to reference
 + * any data without explicitly loading the segment registers, the kernel
 + * will fault immediately rather than have some unforeseen circumstances
 + * for the rest of the kernel.  And it will be very obvious!  :-) Doing
 + * this depends on CS referring to the same physical memory as DS so that
 + * DS can be zeroed before the call. Unfortunately, we can't do anything
 + * about the stack segment/pointer.  Also, we tell the compiler that
 + * everything could change.
 + *
 + * Also, we KNOW that for the non error case of apm_bios_call, there
 + * is no useful data returned in the low order 8 bits of eax.
 + */
 +
 +static inline unsigned long __apm_irq_save(void)
 +{
 +      unsigned long flags;
 +      local_save_flags(flags);
 +      if (apm_info.allow_ints) {
 +              if (irqs_disabled_flags(flags))
 +                      local_irq_enable();
 +      } else
 +              local_irq_disable();
 +
 +      return flags;
 +}
 +
 +#define apm_irq_save(flags) \
 +      do { flags = __apm_irq_save(); } while (0)
 +
 +static inline void apm_irq_restore(unsigned long flags)
 +{
 +      if (irqs_disabled_flags(flags))
 +              local_irq_disable();
 +      else if (irqs_disabled())
 +              local_irq_enable();
 +}
 +
 +#ifdef APM_ZERO_SEGS
 +#     define APM_DECL_SEGS \
 +              unsigned int saved_fs; unsigned int saved_gs;
 +#     define APM_DO_SAVE_SEGS \
 +              savesegment(fs, saved_fs); savesegment(gs, saved_gs)
 +#     define APM_DO_RESTORE_SEGS \
 +              loadsegment(fs, saved_fs); loadsegment(gs, saved_gs)
 +#else
 +#     define APM_DECL_SEGS
 +#     define APM_DO_SAVE_SEGS
 +#     define APM_DO_RESTORE_SEGS
 +#endif
 +
 +/**
 + *    apm_bios_call   -       Make an APM BIOS 32bit call
 + *    @func: APM function to execute
 + *    @ebx_in: EBX register for call entry
 + *    @ecx_in: ECX register for call entry
 + *    @eax: EAX register return
 + *    @ebx: EBX register return
 + *    @ecx: ECX register return
 + *    @edx: EDX register return
 + *    @esi: ESI register return
 + *
 + *    Make an APM call using the 32bit protected mode interface. The
 + *    caller is responsible for knowing if APM BIOS is configured and
 + *    enabled. This call can disable interrupts for a long period of
 + *    time on some laptops.  The return value is in AH and the carry
 + *    flag is loaded into AL.  If there is an error, then the error
 + *    code is returned in AH (bits 8-15 of eax) and this function
 + *    returns non-zero.
 + */
 + 
 +static u8 apm_bios_call(u32 func, u32 ebx_in, u32 ecx_in,
 +      u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
 +{
 +      APM_DECL_SEGS
 +      unsigned long           flags;
 +      cpumask_t               cpus;
 +      int                     cpu;
 +      struct desc_struct      save_desc_40;
 +      struct desc_struct      *gdt;
 +
 +      cpus = apm_save_cpus();
 +      
 +      cpu = get_cpu();
 +      gdt = get_cpu_gdt_table(cpu);
 +      save_desc_40 = gdt[0x40 / 8];
 +      gdt[0x40 / 8] = bad_bios_desc;
 +
 +      apm_irq_save(flags);
 +      APM_DO_SAVE_SEGS;
 +      apm_bios_call_asm(func, ebx_in, ecx_in, eax, ebx, ecx, edx, esi);
 +      APM_DO_RESTORE_SEGS;
 +      apm_irq_restore(flags);
 +      gdt[0x40 / 8] = save_desc_40;
 +      put_cpu();
 +      apm_restore_cpus(cpus);
 +      
 +      return *eax & 0xff;
 +}
 +
 +/**
 + *    apm_bios_call_simple    -       make a simple APM BIOS 32bit call
 + *    @func: APM function to invoke
 + *    @ebx_in: EBX register value for BIOS call
 + *    @ecx_in: ECX register value for BIOS call
 + *    @eax: EAX register on return from the BIOS call
 + *
 + *    Make a BIOS call that returns one value only, or just status.
 + *    If there is an error, then the error code is returned in AH
 + *    (bits 8-15 of eax) and this function returns non-zero. This is
 + *    used for simpler BIOS operations. This call may hold interrupts
 + *    off for a long time on some laptops.
 + */
 +
 +static u8 apm_bios_call_simple(u32 func, u32 ebx_in, u32 ecx_in, u32 *eax)
 +{
 +      u8                      error;
 +      APM_DECL_SEGS
 +      unsigned long           flags;
 +      cpumask_t               cpus;
 +      int                     cpu;
 +      struct desc_struct      save_desc_40;
 +      struct desc_struct      *gdt;
 +
 +      cpus = apm_save_cpus();
 +      
 +      cpu = get_cpu();
 +      gdt = get_cpu_gdt_table(cpu);
 +      save_desc_40 = gdt[0x40 / 8];
 +      gdt[0x40 / 8] = bad_bios_desc;
 +
 +      apm_irq_save(flags);
 +      APM_DO_SAVE_SEGS;
 +      error = apm_bios_call_simple_asm(func, ebx_in, ecx_in, eax);
 +      APM_DO_RESTORE_SEGS;
 +      apm_irq_restore(flags);
 +      gdt[0x40 / 8] = save_desc_40;
 +      put_cpu();
 +      apm_restore_cpus(cpus);
 +      return error;
 +}
 +
 +/**
 + *    apm_driver_version      -       APM driver version
 + *    @val:   loaded with the APM version on return
 + *
 + *    Retrieve the APM version supported by the BIOS. This is only
 + *    supported for APM 1.1 or higher. An error indicates APM 1.0 is
 + *    probably present.
 + *
 + *    On entry val should point to a value indicating the APM driver
 + *    version with the high byte being the major and the low byte the
 + *    minor number both in BCD
 + *
 + *    On return it will hold the BIOS revision supported in the
 + *    same format.
 + */
 +
 +static int apm_driver_version(u_short *val)
 +{
 +      u32     eax;
 +
 +      if (apm_bios_call_simple(APM_FUNC_VERSION, 0, *val, &eax))
 +              return (eax >> 8) & 0xff;
 +      *val = eax;
 +      return APM_SUCCESS;
 +}
 +
 +/**
 + *    apm_get_event   -       get an APM event from the BIOS
 + *    @event: pointer to the event
 + *    @info: point to the event information
 + *
 + *    The APM BIOS provides a polled information for event
 + *    reporting. The BIOS expects to be polled at least every second
 + *    when events are pending. When a message is found the caller should
 + *    poll until no more messages are present.  However, this causes
 + *    problems on some laptops where a suspend event notification is
 + *    not cleared until it is acknowledged.
 + *
 + *    Additional information is returned in the info pointer, providing
 + *    that APM 1.2 is in use. If no messges are pending the value 0x80
 + *    is returned (No power management events pending).
 + */
 + 
 +static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
 +{
 +      u32     eax;
 +      u32     ebx;
 +      u32     ecx;
 +      u32     dummy;
 +
 +      if (apm_bios_call(APM_FUNC_GET_EVENT, 0, 0, &eax, &ebx, &ecx,
 +                      &dummy, &dummy))
 +              return (eax >> 8) & 0xff;
 +      *event = ebx;
 +      if (apm_info.connection_version < 0x0102)
 +              *info = ~0; /* indicate info not valid */
 +      else
 +              *info = ecx;
 +      return APM_SUCCESS;
 +}
 +
 +/**
 + *    set_power_state -       set the power management state
 + *    @what: which items to transition
 + *    @state: state to transition to
 + *
 + *    Request an APM change of state for one or more system devices. The
 + *    processor state must be transitioned last of all. what holds the
 + *    class of device in the upper byte and the device number (0xFF for
 + *    all) for the object to be transitioned.
 + *
 + *    The state holds the state to transition to, which may in fact
 + *    be an acceptance of a BIOS requested state change.
 + */
 + 
 +static int set_power_state(u_short what, u_short state)
 +{
 +      u32     eax;
 +
 +      if (apm_bios_call_simple(APM_FUNC_SET_STATE, what, state, &eax))
 +              return (eax >> 8) & 0xff;
 +      return APM_SUCCESS;
 +}
 +
 +/**
 + *    set_system_power_state - set system wide power state
 + *    @state: which state to enter
 + *
 + *    Transition the entire system into a new APM power state.
 + */
 + 
 +static int set_system_power_state(u_short state)
 +{
 +      return set_power_state(APM_DEVICE_ALL, state);
 +}
 +
 +/**
 + *    apm_do_idle     -       perform power saving
 + *
 + *    This function notifies the BIOS that the processor is (in the view
 + *    of the OS) idle. It returns -1 in the event that the BIOS refuses
 + *    to handle the idle request. On a success the function returns 1
 + *    if the BIOS did clock slowing or 0 otherwise.
 + */
 + 
 +static int apm_do_idle(void)
 +{
 +      u32     eax;
 +      u8      ret = 0;
 +      int     idled = 0;
 +      int     polling;
 +
 +      polling = !!(current_thread_info()->status & TS_POLLING);
 +      if (polling) {
 +              current_thread_info()->status &= ~TS_POLLING;
 +              /*
 +               * TS_POLLING-cleared state must be visible before we
 +               * test NEED_RESCHED:
 +               */
 +              smp_mb();
 +      }
 +      if (!need_resched()) {
 +              idled = 1;
 +              ret = apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &eax);
 +      }
 +      if (polling)
 +              current_thread_info()->status |= TS_POLLING;
 +
 +      if (!idled)
 +              return 0;
 +
 +      if (ret) {
 +              static unsigned long t;
 +
 +              /* This always fails on some SMP boards running UP kernels.
 +               * Only report the failure the first 5 times.
 +               */
 +              if (++t < 5)
 +              {
 +                      printk(KERN_DEBUG "apm_do_idle failed (%d)\n",
 +                                      (eax >> 8) & 0xff);
 +                      t = jiffies;
 +              }
 +              return -1;
 +      }
 +      clock_slowed = (apm_info.bios.flags & APM_IDLE_SLOWS_CLOCK) != 0;
 +      return clock_slowed;
 +}
 +
 +/**
 + *    apm_do_busy     -       inform the BIOS the CPU is busy
 + *
 + *    Request that the BIOS brings the CPU back to full performance. 
 + */
 + 
 +static void apm_do_busy(void)
 +{
 +      u32     dummy;
 +
 +      if (clock_slowed || ALWAYS_CALL_BUSY) {
 +              (void) apm_bios_call_simple(APM_FUNC_BUSY, 0, 0, &dummy);
 +              clock_slowed = 0;
 +      }
 +}
 +
 +/*
 + * If no process has really been interested in
 + * the CPU for some time, we want to call BIOS
 + * power management - we probably want
 + * to conserve power.
 + */
 +#define IDLE_CALC_LIMIT   (HZ * 100)
 +#define IDLE_LEAKY_MAX    16
 +
 +static void (*original_pm_idle)(void) __read_mostly;
 +
 +/**
 + * apm_cpu_idle               -       cpu idling for APM capable Linux
 + *
 + * This is the idling function the kernel executes when APM is available. It 
 + * tries to do BIOS powermanagement based on the average system idle time.
 + * Furthermore it calls the system default idle routine.
 + */
 +
 +static void apm_cpu_idle(void)
 +{
 +      static int use_apm_idle; /* = 0 */
 +      static unsigned int last_jiffies; /* = 0 */
 +      static unsigned int last_stime; /* = 0 */
 +
 +      int apm_idle_done = 0;
 +      unsigned int jiffies_since_last_check = jiffies - last_jiffies;
 +      unsigned int bucket;
 +
 +recalc:
 +      if (jiffies_since_last_check > IDLE_CALC_LIMIT) {
 +              use_apm_idle = 0;
 +              last_jiffies = jiffies;
 +              last_stime = current->stime;
 +      } else if (jiffies_since_last_check > idle_period) {
 +              unsigned int idle_percentage;
 +
 +              idle_percentage = current->stime - last_stime;
 +              idle_percentage *= 100;
 +              idle_percentage /= jiffies_since_last_check;
 +              use_apm_idle = (idle_percentage > idle_threshold);
 +              if (apm_info.forbid_idle)
 +                      use_apm_idle = 0;
 +              last_jiffies = jiffies;
 +              last_stime = current->stime;
 +      }
 +
 +      bucket = IDLE_LEAKY_MAX;
 +
 +      while (!need_resched()) {
 +              if (use_apm_idle) {
 +                      unsigned int t;
 +
 +                      t = jiffies;
 +                      switch (apm_do_idle()) {
 +                      case 0: apm_idle_done = 1;
 +                              if (t != jiffies) {
 +                                      if (bucket) {
 +                                              bucket = IDLE_LEAKY_MAX;
 +                                              continue;
 +                                      }
 +                              } else if (bucket) {
 +                                      bucket--;
 +                                      continue;
 +                              }
 +                              break;
 +                      case 1: apm_idle_done = 1;
 +                              break;
 +                      default: /* BIOS refused */
 +                              break;
 +                      }
 +              }
 +              if (original_pm_idle)
 +                      original_pm_idle();
 +              else
 +                      default_idle();
 +              jiffies_since_last_check = jiffies - last_jiffies;
 +              if (jiffies_since_last_check > idle_period)
 +                      goto recalc;
 +      }
 +
 +      if (apm_idle_done)
 +              apm_do_busy();
 +}
 +
 +/**
 + *    apm_power_off   -       ask the BIOS to power off
 + *
 + *    Handle the power off sequence. This is the one piece of code we
 + *    will execute even on SMP machines. In order to deal with BIOS
 + *    bugs we support real mode APM BIOS power off calls. We also make
 + *    the SMP call on CPU0 as some systems will only honour this call
 + *    on their first cpu.
 + */
 + 
 +static void apm_power_off(void)
 +{
 +      unsigned char   po_bios_call[] = {
 +              0xb8, 0x00, 0x10,       /* movw  $0x1000,ax  */
 +              0x8e, 0xd0,             /* movw  ax,ss       */
 +              0xbc, 0x00, 0xf0,       /* movw  $0xf000,sp  */
 +              0xb8, 0x07, 0x53,       /* movw  $0x5307,ax  */
 +              0xbb, 0x01, 0x00,       /* movw  $0x0001,bx  */
 +              0xb9, 0x03, 0x00,       /* movw  $0x0003,cx  */
 +              0xcd, 0x15              /* int   $0x15       */
 +      };
 +
 +      /* Some bioses don't like being called from CPU != 0 */
 +      if (apm_info.realmode_power_off)
 +      {
 +              (void)apm_save_cpus();
 +              machine_real_restart(po_bios_call, sizeof(po_bios_call));
 +      }
 +      else
 +              (void) set_system_power_state(APM_STATE_OFF);
 +}
 +
 +#ifdef CONFIG_APM_DO_ENABLE
 +
 +/**
 + *    apm_enable_power_management - enable BIOS APM power management
 + *    @enable: enable yes/no
 + *
 + *    Enable or disable the APM BIOS power services. 
 + */
 + 
 +static int apm_enable_power_management(int enable)
 +{
 +      u32     eax;
 +
 +      if ((enable == 0) && (apm_info.bios.flags & APM_BIOS_DISENGAGED))
 +              return APM_NOT_ENGAGED;
 +      if (apm_bios_call_simple(APM_FUNC_ENABLE_PM, APM_DEVICE_BALL,
 +                      enable, &eax))
 +              return (eax >> 8) & 0xff;
 +      if (enable)
 +              apm_info.bios.flags &= ~APM_BIOS_DISABLED;
 +      else
 +              apm_info.bios.flags |= APM_BIOS_DISABLED;
 +      return APM_SUCCESS;
 +}
 +#endif
 +
 +/**
 + *    apm_get_power_status    -       get current power state
 + *    @status: returned status
 + *    @bat: battery info
 + *    @life: estimated life
 + *
 + *    Obtain the current power status from the APM BIOS. We return a
 + *    status which gives the rough battery status, and current power
 + *    source. The bat value returned give an estimate as a percentage
 + *    of life and a status value for the battery. The estimated life
 + *    if reported is a lifetime in secodnds/minutes at current powwer
 + *    consumption.
 + */
 + 
 +static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
 +{
 +      u32     eax;
 +      u32     ebx;
 +      u32     ecx;
 +      u32     edx;
 +      u32     dummy;
 +
 +      if (apm_info.get_power_status_broken)
 +              return APM_32_UNSUPPORTED;
 +      if (apm_bios_call(APM_FUNC_GET_STATUS, APM_DEVICE_ALL, 0,
 +                      &eax, &ebx, &ecx, &edx, &dummy))
 +              return (eax >> 8) & 0xff;
 +      *status = ebx;
 +      *bat = ecx;
 +      if (apm_info.get_power_status_swabinminutes) {
 +              *life = swab16((u16)edx);
 +              *life |= 0x8000;
 +      } else
 +              *life = edx;
 +      return APM_SUCCESS;
 +}
 +
 +#if 0
 +static int apm_get_battery_status(u_short which, u_short *status,
 +                                u_short *bat, u_short *life, u_short *nbat)
 +{
 +      u32     eax;
 +      u32     ebx;
 +      u32     ecx;
 +      u32     edx;
 +      u32     esi;
 +
 +      if (apm_info.connection_version < 0x0102) {
 +              /* pretend we only have one battery. */
 +              if (which != 1)
 +                      return APM_BAD_DEVICE;
 +              *nbat = 1;
 +              return apm_get_power_status(status, bat, life);
 +      }
 +
 +      if (apm_bios_call(APM_FUNC_GET_STATUS, (0x8000 | (which)), 0, &eax,
 +                      &ebx, &ecx, &edx, &esi))
 +              return (eax >> 8) & 0xff;
 +      *status = ebx;
 +      *bat = ecx;
 +      *life = edx;
 +      *nbat = esi;
 +      return APM_SUCCESS;
 +}
 +#endif
 +
 +/**
 + *    apm_engage_power_management     -       enable PM on a device
 + *    @device: identity of device
 + *    @enable: on/off
 + *
 + *    Activate or deactive power management on either a specific device
 + *    or the entire system (%APM_DEVICE_ALL).
 + */
 + 
 +static int apm_engage_power_management(u_short device, int enable)
 +{
 +      u32     eax;
 +
 +      if ((enable == 0) && (device == APM_DEVICE_ALL)
 +          && (apm_info.bios.flags & APM_BIOS_DISABLED))
 +              return APM_DISABLED;
 +      if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, enable, &eax))
 +              return (eax >> 8) & 0xff;
 +      if (device == APM_DEVICE_ALL) {
 +              if (enable)
 +                      apm_info.bios.flags &= ~APM_BIOS_DISENGAGED;
 +              else
 +                      apm_info.bios.flags |= APM_BIOS_DISENGAGED;
 +      }
 +      return APM_SUCCESS;
 +}
 +
 +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
 +
 +/**
 + *    apm_console_blank       -       blank the display
 + *    @blank: on/off
 + *
 + *    Attempt to blank the console, firstly by blanking just video device
 + *    zero, and if that fails (some BIOSes don't support it) then it blanks
 + *    all video devices. Typically the BIOS will do laptop backlight and
 + *    monitor powerdown for us.
 + */
 + 
 +static int apm_console_blank(int blank)
 +{
 +      int error = APM_NOT_ENGAGED; /* silence gcc */
 +      int i;
 +      u_short state;
 +      static const u_short dev[3] = { 0x100, 0x1FF, 0x101 };
 +
 +      state = blank ? APM_STATE_STANDBY : APM_STATE_READY;
 +
 +      for (i = 0; i < ARRAY_SIZE(dev); i++) {
 +              error = set_power_state(dev[i], state);
 +
 +              if ((error == APM_SUCCESS) || (error == APM_NO_ERROR))
 +                      return 1;
 +
 +              if (error == APM_NOT_ENGAGED)
 +                      break;
 +      }
 +
 +      if (error == APM_NOT_ENGAGED) {
 +              static int tried;
 +              int eng_error;
 +              if (tried++ == 0) {
 +                      eng_error = apm_engage_power_management(APM_DEVICE_ALL, 1);
 +                      if (eng_error) {
 +                              apm_error("set display", error);
 +                              apm_error("engage interface", eng_error);
 +                              return 0;
 +                      } else
 +                              return apm_console_blank(blank);
 +              }
 +      }
 +      apm_error("set display", error);
 +      return 0;
 +}
 +#endif
 +
 +static int queue_empty(struct apm_user *as)
 +{
 +      return as->event_head == as->event_tail;
 +}
 +
 +static apm_event_t get_queued_event(struct apm_user *as)
 +{
 +      if (++as->event_tail >= APM_MAX_EVENTS)
 +              as->event_tail = 0;
 +      return as->events[as->event_tail];
 +}
 +
 +static void queue_event(apm_event_t event, struct apm_user *sender)
 +{
 +      struct apm_user *       as;
 +
 +      spin_lock(&user_list_lock);
 +      if (user_list == NULL)
 +              goto out;
 +      for (as = user_list; as != NULL; as = as->next) {
 +              if ((as == sender) || (!as->reader))
 +                      continue;
 +              if (++as->event_head >= APM_MAX_EVENTS)
 +                      as->event_head = 0;
 +
 +              if (as->event_head == as->event_tail) {
 +                      static int notified;
 +
 +                      if (notified++ == 0)
 +                          printk(KERN_ERR "apm: an event queue overflowed\n");
 +                      if (++as->event_tail >= APM_MAX_EVENTS)
 +                              as->event_tail = 0;
 +              }
 +              as->events[as->event_head] = event;
 +              if ((!as->suser) || (!as->writer))
 +                      continue;
 +              switch (event) {
 +              case APM_SYS_SUSPEND:
 +              case APM_USER_SUSPEND:
 +                      as->suspends_pending++;
 +                      suspends_pending++;
 +                      break;
 +
 +              case APM_SYS_STANDBY:
 +              case APM_USER_STANDBY:
 +                      as->standbys_pending++;
 +                      standbys_pending++;
 +                      break;
 +              }
 +      }
 +      wake_up_interruptible(&apm_waitqueue);
 +out:
 +      spin_unlock(&user_list_lock);
 +}
 +
 +static void reinit_timer(void)
 +{
 +#ifdef INIT_TIMER_AFTER_SUSPEND
 +      unsigned long flags;
 +
 +      spin_lock_irqsave(&i8253_lock, flags);
 +      /* set the clock to HZ */
 +      outb_p(0x34, PIT_MODE);         /* binary, mode 2, LSB/MSB, ch 0 */
 +      udelay(10);
 +      outb_p(LATCH & 0xff, PIT_CH0);  /* LSB */
 +      udelay(10);
 +      outb(LATCH >> 8, PIT_CH0);      /* MSB */
 +      udelay(10);
 +      spin_unlock_irqrestore(&i8253_lock, flags);
 +#endif
 +}
 +
 +static int suspend(int vetoable)
 +{
 +      int             err;
 +      struct apm_user *as;
 +
 +      if (pm_send_all(PM_SUSPEND, (void *)3)) {
 +              /* Vetoed */
 +              if (vetoable) {
 +                      if (apm_info.connection_version > 0x100)
 +                              set_system_power_state(APM_STATE_REJECT);
 +                      err = -EBUSY;
 +                      ignore_sys_suspend = 0;
 +                      printk(KERN_WARNING "apm: suspend was vetoed.\n");
 +                      goto out;
 +              }
 +              printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n");
 +      }
 +
 +      device_suspend(PMSG_SUSPEND);
 +      local_irq_disable();
 +      device_power_down(PMSG_SUSPEND);
 +
 +      local_irq_enable();
 +
 +      save_processor_state();
 +      err = set_system_power_state(APM_STATE_SUSPEND);
 +      ignore_normal_resume = 1;
 +      restore_processor_state();
 +
 +      local_irq_disable();
 +      reinit_timer();
 +
 +      if (err == APM_NO_ERROR)
 +              err = APM_SUCCESS;
 +      if (err != APM_SUCCESS)
 +              apm_error("suspend", err);
 +      err = (err == APM_SUCCESS) ? 0 : -EIO;
 +      device_power_up();
 +      local_irq_enable();
 +      device_resume();
 +      pm_send_all(PM_RESUME, (void *)0);
 +      queue_event(APM_NORMAL_RESUME, NULL);
 + out:
 +      spin_lock(&user_list_lock);
 +      for (as = user_list; as != NULL; as = as->next) {
 +              as->suspend_wait = 0;
 +              as->suspend_result = err;
 +      }
 +      spin_unlock(&user_list_lock);
 +      wake_up_interruptible(&apm_suspend_waitqueue);
 +      return err;
 +}
 +
 +static void standby(void)
 +{
 +      int     err;
 +
 +      local_irq_disable();
 +      device_power_down(PMSG_SUSPEND);
 +      local_irq_enable();
 +
 +      err = set_system_power_state(APM_STATE_STANDBY);
 +      if ((err != APM_SUCCESS) && (err != APM_NO_ERROR))
 +              apm_error("standby", err);
 +
 +      local_irq_disable();
 +      device_power_up();
 +      local_irq_enable();
 +}
 +
 +static apm_event_t get_event(void)
 +{
 +      int             error;
 +      apm_event_t     event = APM_NO_EVENTS; /* silence gcc */
 +      apm_eventinfo_t info;
 +
 +      static int notified;
 +
 +      /* we don't use the eventinfo */
 +      error = apm_get_event(&event, &info);
 +      if (error == APM_SUCCESS)
 +              return event;
 +
 +      if ((error != APM_NO_EVENTS) && (notified++ == 0))
 +              apm_error("get_event", error);
 +
 +      return 0;
 +}
 +
 +static void check_events(void)
 +{
 +      apm_event_t             event;
 +      static unsigned long    last_resume;
 +      static int              ignore_bounce;
 +
 +      while ((event = get_event()) != 0) {
 +              if (debug) {
 +                      if (event <= NR_APM_EVENT_NAME)
 +                              printk(KERN_DEBUG "apm: received %s notify\n",
 +                                     apm_event_name[event - 1]);
 +                      else
 +                              printk(KERN_DEBUG "apm: received unknown "
 +                                     "event 0x%02x\n", event);
 +              }
 +              if (ignore_bounce
 +                  && ((jiffies - last_resume) > bounce_interval))
 +                      ignore_bounce = 0;
 +
 +              switch (event) {
 +              case APM_SYS_STANDBY:
 +              case APM_USER_STANDBY:
 +                      queue_event(event, NULL);
 +                      if (standbys_pending <= 0)
 +                              standby();
 +                      break;
 +
 +              case APM_USER_SUSPEND:
 +#ifdef CONFIG_APM_IGNORE_USER_SUSPEND
 +                      if (apm_info.connection_version > 0x100)
 +                              set_system_power_state(APM_STATE_REJECT);
 +                      break;
 +#endif
 +              case APM_SYS_SUSPEND:
 +                      if (ignore_bounce) {
 +                              if (apm_info.connection_version > 0x100)
 +                                      set_system_power_state(APM_STATE_REJECT);
 +                              break;
 +                      }
 +                      /*
 +                       * If we are already processing a SUSPEND,
 +                       * then further SUSPEND events from the BIOS
 +                       * will be ignored.  We also return here to
 +                       * cope with the fact that the Thinkpads keep
 +                       * sending a SUSPEND event until something else
 +                       * happens!
 +                       */
 +                      if (ignore_sys_suspend)
 +                              return;
 +                      ignore_sys_suspend = 1;
 +                      queue_event(event, NULL);
 +                      if (suspends_pending <= 0)
 +                              (void) suspend(1);
 +                      break;
 +
 +              case APM_NORMAL_RESUME:
 +              case APM_CRITICAL_RESUME:
 +              case APM_STANDBY_RESUME:
 +                      ignore_sys_suspend = 0;
 +                      last_resume = jiffies;
 +                      ignore_bounce = 1;
 +                      if ((event != APM_NORMAL_RESUME)
 +                          || (ignore_normal_resume == 0)) {
 +                              device_resume();
 +                              pm_send_all(PM_RESUME, (void *)0);
 +                              queue_event(event, NULL);
 +                      }
 +                      ignore_normal_resume = 0;
 +                      break;
 +
 +              case APM_CAPABILITY_CHANGE:
 +              case APM_LOW_BATTERY:
 +              case APM_POWER_STATUS_CHANGE:
 +                      queue_event(event, NULL);
 +                      /* If needed, notify drivers here */
 +                      break;
 +
 +              case APM_UPDATE_TIME:
 +                      break;
 +
 +              case APM_CRITICAL_SUSPEND:
 +                      /*
 +                       * We are not allowed to reject a critical suspend.
 +                       */
 +                      (void) suspend(0);
 +                      break;
 +              }
 +      }
 +}
 +
 +static void apm_event_handler(void)
 +{
 +      static int      pending_count = 4;
 +      int             err;
 +
 +      if ((standbys_pending > 0) || (suspends_pending > 0)) {
 +              if ((apm_info.connection_version > 0x100) &&
 +                              (pending_count-- <= 0)) {
 +                      pending_count = 4;
 +                      if (debug)
 +                              printk(KERN_DEBUG "apm: setting state busy\n");
 +                      err = set_system_power_state(APM_STATE_BUSY);
 +                      if (err)
 +                              apm_error("busy", err);
 +              }
 +      } else
 +              pending_count = 4;
 +      check_events();
 +}
 +
 +/*
 + * This is the APM thread main loop.
 + */
 +
 +static void apm_mainloop(void)
 +{
 +      DECLARE_WAITQUEUE(wait, current);
 +
 +      add_wait_queue(&apm_waitqueue, &wait);
 +      set_current_state(TASK_INTERRUPTIBLE);
 +      for (;;) {
 +              schedule_timeout(APM_CHECK_TIMEOUT);
 +              if (kthread_should_stop())
 +                      break;
 +              /*
 +               * Ok, check all events, check for idle (and mark us sleeping
 +               * so as not to count towards the load average)..
 +               */
 +              set_current_state(TASK_INTERRUPTIBLE);
 +              apm_event_handler();
 +      }
 +      remove_wait_queue(&apm_waitqueue, &wait);
 +}
 +
 +static int check_apm_user(struct apm_user *as, const char *func)
 +{
 +      if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
 +              printk(KERN_ERR "apm: %s passed bad filp\n", func);
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +static ssize_t do_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
 +{
 +      struct apm_user *       as;
 +      int                     i;
 +      apm_event_t             event;
 +
 +      as = fp->private_data;
 +      if (check_apm_user(as, "read"))
 +              return -EIO;
 +      if ((int)count < sizeof(apm_event_t))
 +              return -EINVAL;
 +      if ((queue_empty(as)) && (fp->f_flags & O_NONBLOCK))
 +              return -EAGAIN;
 +      wait_event_interruptible(apm_waitqueue, !queue_empty(as));
 +      i = count;
 +      while ((i >= sizeof(event)) && !queue_empty(as)) {
 +              event = get_queued_event(as);
 +              if (copy_to_user(buf, &event, sizeof(event))) {
 +                      if (i < count)
 +                              break;
 +                      return -EFAULT;
 +              }
 +              switch (event) {
 +              case APM_SYS_SUSPEND:
 +              case APM_USER_SUSPEND:
 +                      as->suspends_read++;
 +                      break;
 +
 +              case APM_SYS_STANDBY:
 +              case APM_USER_STANDBY:
 +                      as->standbys_read++;
 +                      break;
 +              }
 +              buf += sizeof(event);
 +              i -= sizeof(event);
 +      }
 +      if (i < count)
 +              return count - i;
 +      if (signal_pending(current))
 +              return -ERESTARTSYS;
 +      return 0;
 +}
 +
 +static unsigned int do_poll(struct file *fp, poll_table * wait)
 +{
 +      struct apm_user * as;
 +
 +      as = fp->private_data;
 +      if (check_apm_user(as, "poll"))
 +              return 0;
 +      poll_wait(fp, &apm_waitqueue, wait);
 +      if (!queue_empty(as))
 +              return POLLIN | POLLRDNORM;
 +      return 0;
 +}
 +
 +static int do_ioctl(struct inode * inode, struct file *filp,
 +                  u_int cmd, u_long arg)
 +{
 +      struct apm_user *       as;
 +
 +      as = filp->private_data;
 +      if (check_apm_user(as, "ioctl"))
 +              return -EIO;
 +      if ((!as->suser) || (!as->writer))
 +              return -EPERM;
 +      switch (cmd) {
 +      case APM_IOC_STANDBY:
 +              if (as->standbys_read > 0) {
 +                      as->standbys_read--;
 +                      as->standbys_pending--;
 +                      standbys_pending--;
 +              } else
 +                      queue_event(APM_USER_STANDBY, as);
 +              if (standbys_pending <= 0)
 +                      standby();
 +              break;
 +      case APM_IOC_SUSPEND:
 +              if (as->suspends_read > 0) {
 +                      as->suspends_read--;
 +                      as->suspends_pending--;
 +                      suspends_pending--;
 +              } else
 +                      queue_event(APM_USER_SUSPEND, as);
 +              if (suspends_pending <= 0) {
 +                      return suspend(1);
 +              } else {
 +                      as->suspend_wait = 1;
 +                      wait_event_interruptible(apm_suspend_waitqueue,
 +                                      as->suspend_wait == 0);
 +                      return as->suspend_result;
 +              }
 +              break;
 +      default:
 +              return -EINVAL;
 +      }
 +      return 0;
 +}
 +
 +static int do_release(struct inode * inode, struct file * filp)
 +{
 +      struct apm_user *       as;
 +
 +      as = filp->private_data;
 +      if (check_apm_user(as, "release"))
 +              return 0;
 +      filp->private_data = NULL;
 +      if (as->standbys_pending > 0) {
 +              standbys_pending -= as->standbys_pending;
 +              if (standbys_pending <= 0)
 +                      standby();
 +      }
 +      if (as->suspends_pending > 0) {
 +              suspends_pending -= as->suspends_pending;
 +              if (suspends_pending <= 0)
 +                      (void) suspend(1);
 +      }
 +      spin_lock(&user_list_lock);
 +      if (user_list == as)
 +              user_list = as->next;
 +      else {
 +              struct apm_user *       as1;
 +
 +              for (as1 = user_list;
 +                   (as1 != NULL) && (as1->next != as);
 +                   as1 = as1->next)
 +                      ;
 +              if (as1 == NULL)
 +                      printk(KERN_ERR "apm: filp not in user list\n");
 +              else
 +                      as1->next = as->next;
 +      }
 +      spin_unlock(&user_list_lock);
 +      kfree(as);
 +      return 0;
 +}
 +
 +static int do_open(struct inode * inode, struct file * filp)
 +{
 +      struct apm_user *       as;
 +
 +      as = kmalloc(sizeof(*as), GFP_KERNEL);
 +      if (as == NULL) {
 +              printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
 +                     sizeof(*as));
 +              return -ENOMEM;
 +      }
 +      as->magic = APM_BIOS_MAGIC;
 +      as->event_tail = as->event_head = 0;
 +      as->suspends_pending = as->standbys_pending = 0;
 +      as->suspends_read = as->standbys_read = 0;
 +      /*
 +       * XXX - this is a tiny bit broken, when we consider BSD
 +         * process accounting. If the device is opened by root, we
 +       * instantly flag that we used superuser privs. Who knows,
 +       * we might close the device immediately without doing a
 +       * privileged operation -- cevans
 +       */
 +      as->suser = capable(CAP_SYS_ADMIN);
 +      as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
 +      as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
 +      spin_lock(&user_list_lock);
 +      as->next = user_list;
 +      user_list = as;
 +      spin_unlock(&user_list_lock);
 +      filp->private_data = as;
 +      return 0;
 +}
 +
 +static int proc_apm_show(struct seq_file *m, void *v)
 +{
 +      unsigned short  bx;
 +      unsigned short  cx;
 +      unsigned short  dx;
 +      int             error;
 +      unsigned short  ac_line_status = 0xff;
 +      unsigned short  battery_status = 0xff;
 +      unsigned short  battery_flag   = 0xff;
 +      int             percentage     = -1;
 +      int             time_units     = -1;
 +      char            *units         = "?";
 +
 +      if ((num_online_cpus() == 1) &&
 +          !(error = apm_get_power_status(&bx, &cx, &dx))) {
 +              ac_line_status = (bx >> 8) & 0xff;
 +              battery_status = bx & 0xff;
 +              if ((cx & 0xff) != 0xff)
 +                      percentage = cx & 0xff;
 +
 +              if (apm_info.connection_version > 0x100) {
 +                      battery_flag = (cx >> 8) & 0xff;
 +                      if (dx != 0xffff) {
 +                              units = (dx & 0x8000) ? "min" : "sec";
 +                              time_units = dx & 0x7fff;
 +                      }
 +              }
 +      }
 +      /* Arguments, with symbols from linux/apm_bios.h.  Information is
 +         from the Get Power Status (0x0a) call unless otherwise noted.
 +
 +         0) Linux driver version (this will change if format changes)
 +         1) APM BIOS Version.  Usually 1.0, 1.1 or 1.2.
 +         2) APM flags from APM Installation Check (0x00):
 +            bit 0: APM_16_BIT_SUPPORT
 +            bit 1: APM_32_BIT_SUPPORT
 +            bit 2: APM_IDLE_SLOWS_CLOCK
 +            bit 3: APM_BIOS_DISABLED
 +            bit 4: APM_BIOS_DISENGAGED
 +         3) AC line status
 +            0x00: Off-line
 +            0x01: On-line
 +            0x02: On backup power (BIOS >= 1.1 only)
 +            0xff: Unknown
 +         4) Battery status
 +            0x00: High
 +            0x01: Low
 +            0x02: Critical
 +            0x03: Charging
 +            0x04: Selected battery not present (BIOS >= 1.2 only)
 +            0xff: Unknown
 +         5) Battery flag
 +            bit 0: High
 +            bit 1: Low
 +            bit 2: Critical
 +            bit 3: Charging
 +            bit 7: No system battery
 +            0xff: Unknown
 +         6) Remaining battery life (percentage of charge):
 +            0-100: valid
 +            -1: Unknown
 +         7) Remaining battery life (time units):
 +            Number of remaining minutes or seconds
 +            -1: Unknown
 +         8) min = minutes; sec = seconds */
 +
 +      seq_printf(m, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
 +                   driver_version,
 +                   (apm_info.bios.version >> 8) & 0xff,
 +                   apm_info.bios.version & 0xff,
 +                   apm_info.bios.flags,
 +                   ac_line_status,
 +                   battery_status,
 +                   battery_flag,
 +                   percentage,
 +                   time_units,
 +                   units);
 +      return 0;
 +}
 +
 +static int proc_apm_open(struct inode *inode, struct file *file)
 +{
 +      return single_open(file, proc_apm_show, NULL);
 +}
 +
 +static const struct file_operations apm_file_ops = {
 +      .owner          = THIS_MODULE,
 +      .open           = proc_apm_open,
 +      .read           = seq_read,
 +      .llseek         = seq_lseek,
 +      .release        = single_release,
 +};
 +
 +static int apm(void *unused)
 +{
 +      unsigned short  bx;
 +      unsigned short  cx;
 +      unsigned short  dx;
 +      int             error;
 +      char *          power_stat;
 +      char *          bat_stat;
 +
 +#ifdef CONFIG_SMP
 +      /* 2002/08/01 - WT
 +       * This is to avoid random crashes at boot time during initialization
 +       * on SMP systems in case of "apm=power-off" mode. Seen on ASUS A7M266D.
 +       * Some bioses don't like being called from CPU != 0.
 +       * Method suggested by Ingo Molnar.
 +       */
 +      set_cpus_allowed(current, cpumask_of_cpu(0));
 +      BUG_ON(smp_processor_id() != 0);
 +#endif
 +
 +      if (apm_info.connection_version == 0) {
 +              apm_info.connection_version = apm_info.bios.version;
 +              if (apm_info.connection_version > 0x100) {
 +                      /*
 +                       * We only support BIOSs up to version 1.2
 +                       */
 +                      if (apm_info.connection_version > 0x0102)
 +                              apm_info.connection_version = 0x0102;
 +                      error = apm_driver_version(&apm_info.connection_version);
 +                      if (error != APM_SUCCESS) {
 +                              apm_error("driver version", error);
 +                              /* Fall back to an APM 1.0 connection. */
 +                              apm_info.connection_version = 0x100;
 +                      }
 +              }
 +      }
 +
 +      if (debug)
 +              printk(KERN_INFO "apm: Connection version %d.%d\n",
 +                      (apm_info.connection_version >> 8) & 0xff,
 +                      apm_info.connection_version & 0xff);
 +
 +#ifdef CONFIG_APM_DO_ENABLE
 +      if (apm_info.bios.flags & APM_BIOS_DISABLED) {
 +              /*
 +               * This call causes my NEC UltraLite Versa 33/C to hang if it
 +               * is booted with PM disabled but not in the docking station.
 +               * Unfortunate ...
 +               */
 +              error = apm_enable_power_management(1);
 +              if (error) {
 +                      apm_error("enable power management", error);
 +                      return -1;
 +              }
 +      }
 +#endif
 +
 +      if ((apm_info.bios.flags & APM_BIOS_DISENGAGED)
 +          && (apm_info.connection_version > 0x0100)) {
 +              error = apm_engage_power_management(APM_DEVICE_ALL, 1);
 +              if (error) {
 +                      apm_error("engage power management", error);
 +                      return -1;
 +              }
 +      }
 +
 +      if (debug && (num_online_cpus() == 1 || smp )) {
 +              error = apm_get_power_status(&bx, &cx, &dx);
 +              if (error)
 +                      printk(KERN_INFO "apm: power status not available\n");
 +              else {
 +                      switch ((bx >> 8) & 0xff) {
 +                      case 0: power_stat = "off line"; break;
 +                      case 1: power_stat = "on line"; break;
 +                      case 2: power_stat = "on backup power"; break;
 +                      default: power_stat = "unknown"; break;
 +                      }
 +                      switch (bx & 0xff) {
 +                      case 0: bat_stat = "high"; break;
 +                      case 1: bat_stat = "low"; break;
 +                      case 2: bat_stat = "critical"; break;
 +                      case 3: bat_stat = "charging"; break;
 +                      default: bat_stat = "unknown"; break;
 +                      }
 +                      printk(KERN_INFO
 +                             "apm: AC %s, battery status %s, battery life ",
 +                             power_stat, bat_stat);
 +                      if ((cx & 0xff) == 0xff)
 +                              printk("unknown\n");
 +                      else
 +                              printk("%d%%\n", cx & 0xff);
 +                      if (apm_info.connection_version > 0x100) {
 +                              printk(KERN_INFO
 +                                     "apm: battery flag 0x%02x, battery life ",
 +                                     (cx >> 8) & 0xff);
 +                              if (dx == 0xffff)
 +                                      printk("unknown\n");
 +                              else
 +                                      printk("%d %s\n", dx & 0x7fff,
 +                                              (dx & 0x8000) ?
 +                                              "minutes" : "seconds");
 +                      }
 +              }
 +      }
 +
 +      /* Install our power off handler.. */
 +      if (power_off)
 +              pm_power_off = apm_power_off;
 +
 +      if (num_online_cpus() == 1 || smp) {
 +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
 +              console_blank_hook = apm_console_blank;
 +#endif
 +              apm_mainloop();
 +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
 +              console_blank_hook = NULL;
 +#endif
 +      }
 +
 +      return 0;
 +}
 +
 +#ifndef MODULE
 +static int __init apm_setup(char *str)
 +{
 +      int     invert;
 +
 +      while ((str != NULL) && (*str != '\0')) {
 +              if (strncmp(str, "off", 3) == 0)
 +                      apm_disabled = 1;
 +              if (strncmp(str, "on", 2) == 0)
 +                      apm_disabled = 0;
 +              if ((strncmp(str, "bounce-interval=", 16) == 0) ||
 +                  (strncmp(str, "bounce_interval=", 16) == 0))
 +                      bounce_interval = simple_strtol(str + 16, NULL, 0);
 +              if ((strncmp(str, "idle-threshold=", 15) == 0) ||
 +                  (strncmp(str, "idle_threshold=", 15) == 0))
 +                      idle_threshold = simple_strtol(str + 15, NULL, 0);
 +              if ((strncmp(str, "idle-period=", 12) == 0) ||
 +                  (strncmp(str, "idle_period=", 12) == 0))
 +                      idle_period = simple_strtol(str + 12, NULL, 0);
 +              invert = (strncmp(str, "no-", 3) == 0) ||
 +                      (strncmp(str, "no_", 3) == 0);
 +              if (invert)
 +                      str += 3;
 +              if (strncmp(str, "debug", 5) == 0)
 +                      debug = !invert;
 +              if ((strncmp(str, "power-off", 9) == 0) ||
 +                  (strncmp(str, "power_off", 9) == 0))
 +                      power_off = !invert;
 +              if (strncmp(str, "smp", 3) == 0)
 +              {
 +                      smp = !invert;
 +                      idle_threshold = 100;
 +              }
 +              if ((strncmp(str, "allow-ints", 10) == 0) ||
 +                  (strncmp(str, "allow_ints", 10) == 0))
 +                      apm_info.allow_ints = !invert;
 +              if ((strncmp(str, "broken-psr", 10) == 0) ||
 +                  (strncmp(str, "broken_psr", 10) == 0))
 +                      apm_info.get_power_status_broken = !invert;
 +              if ((strncmp(str, "realmode-power-off", 18) == 0) ||
 +                  (strncmp(str, "realmode_power_off", 18) == 0))
 +                      apm_info.realmode_power_off = !invert;
 +              str = strchr(str, ',');
 +              if (str != NULL)
 +                      str += strspn(str, ", \t");
 +      }
 +      return 1;
 +}
 +
 +__setup("apm=", apm_setup);
 +#endif
 +
 +static const struct file_operations apm_bios_fops = {
 +      .owner          = THIS_MODULE,
 +      .read           = do_read,
 +      .poll           = do_poll,
 +      .ioctl          = do_ioctl,
 +      .open           = do_open,
 +      .release        = do_release,
 +};
 +
 +static struct miscdevice apm_device = {
 +      APM_MINOR_DEV,
 +      "apm_bios",
 +      &apm_bios_fops
 +};
 +
 +
 +/* Simple "print if true" callback */
- static int __init broken_ps2_resume(struct dmi_system_id *d)
++static int __init print_if_true(const struct dmi_system_id *d)
 +{
 +      printk("%s\n", d->ident);
 +      return 0;
 +}
 +
 +/*
 + * Some Bioses enable the PS/2 mouse (touchpad) at resume, even if it was
 + * disabled before the suspend. Linux used to get terribly confused by that.
 + */
- static int __init set_realmode_power_off(struct dmi_system_id *d)
++static int __init broken_ps2_resume(const struct dmi_system_id *d)
 +{
 +      printk(KERN_INFO "%s machine detected. Mousepad Resume Bug workaround hopefully not needed.\n", d->ident);
 +      return 0;
 +}
 +
 +/* Some bioses have a broken protected mode poweroff and need to use realmode */
- static int __init set_apm_ints(struct dmi_system_id *d)
++static int __init set_realmode_power_off(const struct dmi_system_id *d)
 +{
 +      if (apm_info.realmode_power_off == 0) {
 +              apm_info.realmode_power_off = 1;
 +              printk(KERN_INFO "%s bios detected. Using realmode poweroff only.\n", d->ident);
 +      }
 +      return 0;
 +}
 +
 +/* Some laptops require interrupts to be enabled during APM calls */
- static int __init apm_is_horked(struct dmi_system_id *d)
++static int __init set_apm_ints(const struct dmi_system_id *d)
 +{
 +      if (apm_info.allow_ints == 0) {
 +              apm_info.allow_ints = 1;
 +              printk(KERN_INFO "%s machine detected. Enabling interrupts during APM calls.\n", d->ident);
 +      }
 +      return 0;
 +}
 +
 +/* Some APM bioses corrupt memory or just plain do not work */
- static int __init apm_is_horked_d850md(struct dmi_system_id *d)
++static int __init apm_is_horked(const struct dmi_system_id *d)
 +{
 +      if (apm_info.disabled == 0) {
 +              apm_info.disabled = 1;
 +              printk(KERN_INFO "%s machine detected. Disabling APM.\n", d->ident);
 +      }
 +      return 0;
 +}
 +
- static int __init apm_likes_to_melt(struct dmi_system_id *d)
++static int __init apm_is_horked_d850md(const struct dmi_system_id *d)
 +{
 +      if (apm_info.disabled == 0) {
 +              apm_info.disabled = 1;
 +              printk(KERN_INFO "%s machine detected. Disabling APM.\n", d->ident);
 +              printk(KERN_INFO "This bug is fixed in bios P15 which is available for \n");
 +              printk(KERN_INFO "download from support.intel.com \n");
 +      }
 +      return 0;
 +}
 +
 +/* Some APM bioses hang on APM idle calls */
- static int __init broken_apm_power(struct dmi_system_id *d)
++static int __init apm_likes_to_melt(const struct dmi_system_id *d)
 +{
 +      if (apm_info.forbid_idle == 0) {
 +              apm_info.forbid_idle = 1;
 +              printk(KERN_INFO "%s machine detected. Disabling APM idle calls.\n", d->ident);
 +      }
 +      return 0;
 +}
 +
 +/*
 + *  Check for clue free BIOS implementations who use
 + *  the following QA technique
 + *
 + *      [ Write BIOS Code ]<------
 + *               |                ^
 + *      < Does it Compile >----N--
 + *               |Y               ^
 + *    < Does it Boot Win98 >-N--
 + *               |Y
 + *           [Ship It]
 + *
 + *    Phoenix A04  08/24/2000 is known bad (Dell Inspiron 5000e)
 + *    Phoenix A07  09/29/2000 is known good (Dell Inspiron 5000)
 + */
- static int __init swab_apm_power_in_minutes(struct dmi_system_id *d)
++static int __init broken_apm_power(const struct dmi_system_id *d)
 +{
 +      apm_info.get_power_status_broken = 1;
 +      printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n");
 +      return 0;
 +}
 +
 +/*
 + * This bios swaps the APM minute reporting bytes over (Many sony laptops
 + * have this problem).
 + */
++static int __init swab_apm_power_in_minutes(const struct dmi_system_id *d)
 +{
 +      apm_info.get_power_status_swabinminutes = 1;
 +      printk(KERN_WARNING "BIOS strings suggest APM reports battery life in minutes and wrong byte order.\n");
 +      return 0;
 +}
 +
 +static struct dmi_system_id __initdata apm_dmi_table[] = {
 +      {
 +              print_if_true,
 +              KERN_WARNING "IBM T23 - BIOS 1.03b+ and controller firmware 1.02+ may be needed for Linux APM.",
 +              {       DMI_MATCH(DMI_SYS_VENDOR, "IBM"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "1AET38WW (1.01b)"), },
 +      },
 +      {       /* Handle problems with APM on the C600 */
 +              broken_ps2_resume, "Dell Latitude C600",
 +              {       DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "Latitude C600"), },
 +      },
 +      {       /* Allow interrupts during suspend on Dell Latitude laptops*/
 +              set_apm_ints, "Dell Latitude",
 +              {       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "Latitude C510"), }
 +      },
 +      {       /* APM crashes */
 +              apm_is_horked, "Dell Inspiron 2500",
 +              {       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 2500"),
 +                      DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
 +      },
 +      {       /* Allow interrupts during suspend on Dell Inspiron laptops*/
 +              set_apm_ints, "Dell Inspiron", {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 4000"), },
 +      },
 +      {       /* Handle problems with APM on Inspiron 5000e */
 +              broken_apm_power, "Dell Inspiron 5000e",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "A04"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "08/24/2000"), },
 +      },
 +      {       /* Handle problems with APM on Inspiron 2500 */
 +              broken_apm_power, "Dell Inspiron 2500",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "A12"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "02/04/2002"), },
 +      },
 +      {       /* APM crashes */
 +              apm_is_horked, "Dell Dimension 4100",
 +              {       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "XPS-Z"),
 +                      DMI_MATCH(DMI_BIOS_VENDOR,"Intel Corp."),
 +                      DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
 +      },
 +      {       /* Allow interrupts during suspend on Compaq Laptops*/
 +              set_apm_ints, "Compaq 12XL125",
 +              {       DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "Compaq PC"),
 +                      DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION,"4.06"), },
 +      },
 +      {       /* Allow interrupts during APM or the clock goes slow */
 +              set_apm_ints, "ASUSTeK",
 +              {       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "L8400K series Notebook PC"), },
 +      },
 +      {       /* APM blows on shutdown */
 +              apm_is_horked, "ABIT KX7-333[R]",
 +              {       DMI_MATCH(DMI_BOARD_VENDOR, "ABIT"),
 +                      DMI_MATCH(DMI_BOARD_NAME, "VT8367-8233A (KX7-333[R])"), },
 +      },
 +      {       /* APM crashes */
 +              apm_is_horked, "Trigem Delhi3",
 +              {       DMI_MATCH(DMI_SYS_VENDOR, "TriGem Computer, Inc"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "Delhi3"), },
 +      },
 +      {       /* APM crashes */
 +              apm_is_horked, "Fujitsu-Siemens",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "hoenix/FUJITSU SIEMENS"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "Version1.01"), },
 +      },
 +      {       /* APM crashes */
 +              apm_is_horked_d850md, "Intel D850MD",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "MV85010A.86A.0016.P07.0201251536"), },
 +      },
 +      {       /* APM crashes */
 +              apm_is_horked, "Intel D810EMO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "MO81010A.86A.0008.P04.0004170800"), },
 +      },
 +      {       /* APM crashes */
 +              apm_is_horked, "Dell XPS-Z",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "A11"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "XPS-Z"), },
 +      },
 +      {       /* APM crashes */
 +              apm_is_horked, "Sharp PC-PJ/AX",
 +              {       DMI_MATCH(DMI_SYS_VENDOR, "SHARP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "PC-PJ/AX"),
 +                      DMI_MATCH(DMI_BIOS_VENDOR,"SystemSoft"),
 +                      DMI_MATCH(DMI_BIOS_VERSION,"Version R2.08"), },
 +      },
 +      {       /* APM crashes */
 +              apm_is_horked, "Dell Inspiron 2500",
 +              {       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 2500"),
 +                      DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
 +      },
 +      {       /* APM idle hangs */
 +              apm_likes_to_melt, "Jabil AMD",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "0AASNP06"), },
 +      },
 +      {       /* APM idle hangs */
 +              apm_likes_to_melt, "AMI Bios",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "0AASNP05"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-N505X(DE) */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "R0206H"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "08/23/99"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-N505VX */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "W2K06H0"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "02/03/00"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-XG29 */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "R0117A0"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "04/25/00"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-Z600NE */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "R0121Z1"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "05/11/00"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-Z600NE */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "WME01Z1"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "08/11/00"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-Z600LEK(DE) */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "R0206Z3"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "12/25/00"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-Z505LS */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "R0203D0"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "05/12/00"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-Z505LS */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "R0203Z3"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "08/25/00"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-Z505LS (with updated BIOS) */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "R0209Z3"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "05/12/01"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-F104K */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "R0204K2"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "08/28/00"), },
 +      },
 +
 +      {       /* Handle problems with APM on Sony Vaio PCG-C1VN/C1VE */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "R0208P1"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "11/09/00"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-C1VE */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "R0204P1"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "09/12/00"), },
 +      },
 +      {       /* Handle problems with APM on Sony Vaio PCG-C1VE */
 +              swab_apm_power_in_minutes, "Sony VAIO",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "WXPO1Z3"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "10/26/01"), },
 +      },
 +      {       /* broken PM poweroff bios */
 +              set_realmode_power_off, "Award Software v4.60 PGMA",
 +              {       DMI_MATCH(DMI_BIOS_VENDOR, "Award Software International, Inc."),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "4.60 PGMA"),
 +                      DMI_MATCH(DMI_BIOS_DATE, "134526184"), },
 +      },
 +
 +      /* Generic per vendor APM settings  */
 +
 +      {       /* Allow interrupts during suspend on IBM laptops */
 +              set_apm_ints, "IBM",
 +              {       DMI_MATCH(DMI_SYS_VENDOR, "IBM"), },
 +      },
 +
 +      { }
 +};
 +
 +/*
 + * Just start the APM thread. We do NOT want to do APM BIOS
 + * calls from anything but the APM thread, if for no other reason
 + * than the fact that we don't trust the APM BIOS. This way,
 + * most common APM BIOS problems that lead to protection errors
 + * etc will have at least some level of being contained...
 + *
 + * In short, if something bad happens, at least we have a choice
 + * of just killing the apm thread..
 + */
 +static int __init apm_init(void)
 +{
 +      struct proc_dir_entry *apm_proc;
 +      struct desc_struct *gdt;
 +      int err;
 +
 +      dmi_check_system(apm_dmi_table);
 +
 +      if (apm_info.bios.version == 0 || paravirt_enabled()) {
 +              printk(KERN_INFO "apm: BIOS not found.\n");
 +              return -ENODEV;
 +      }
 +      printk(KERN_INFO
 +              "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
 +              ((apm_info.bios.version >> 8) & 0xff),
 +              (apm_info.bios.version & 0xff),
 +              apm_info.bios.flags,
 +              driver_version);
 +      if ((apm_info.bios.flags & APM_32_BIT_SUPPORT) == 0) {
 +              printk(KERN_INFO "apm: no 32 bit BIOS support\n");
 +              return -ENODEV;
 +      }
 +
 +      if (allow_ints)
 +              apm_info.allow_ints = 1;
 +      if (broken_psr)
 +              apm_info.get_power_status_broken = 1;
 +      if (realmode_power_off)
 +              apm_info.realmode_power_off = 1;
 +      /* User can override, but default is to trust DMI */
 +      if (apm_disabled != -1)
 +              apm_info.disabled = apm_disabled;
 +
 +      /*
 +       * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
 +       * but is reportedly a 1.0 BIOS.
 +       */
 +      if (apm_info.bios.version == 0x001)
 +              apm_info.bios.version = 0x100;
 +
 +      /* BIOS < 1.2 doesn't set cseg_16_len */
 +      if (apm_info.bios.version < 0x102)
 +              apm_info.bios.cseg_16_len = 0; /* 64k */
 +
 +      if (debug) {
 +              printk(KERN_INFO "apm: entry %x:%x cseg16 %x dseg %x",
 +                      apm_info.bios.cseg, apm_info.bios.offset,
 +                      apm_info.bios.cseg_16, apm_info.bios.dseg);
 +              if (apm_info.bios.version > 0x100)
 +                      printk(" cseg len %x, dseg len %x",
 +                              apm_info.bios.cseg_len,
 +                              apm_info.bios.dseg_len);
 +              if (apm_info.bios.version > 0x101)
 +                      printk(" cseg16 len %x", apm_info.bios.cseg_16_len);
 +              printk("\n");
 +      }
 +
 +      if (apm_info.disabled) {
 +              printk(KERN_NOTICE "apm: disabled on user request.\n");
 +              return -ENODEV;
 +      }
 +      if ((num_online_cpus() > 1) && !power_off && !smp) {
 +              printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
 +              apm_info.disabled = 1;
 +              return -ENODEV;
 +      }
 +      if (PM_IS_ACTIVE()) {
 +              printk(KERN_NOTICE "apm: overridden by ACPI.\n");
 +              apm_info.disabled = 1;
 +              return -ENODEV;
 +      }
 +#ifdef CONFIG_PM_LEGACY
 +      pm_active = 1;
 +#endif
 +
 +      /*
 +       * Set up a segment that references the real mode segment 0x40
 +       * that extends up to the end of page zero (that we have reserved).
 +       * This is for buggy BIOS's that refer to (real mode) segment 0x40
 +       * even though they are called in protected mode.
 +       */
 +      set_base(bad_bios_desc, __va((unsigned long)0x40 << 4));
 +      _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4));
 +
 +      /*
 +       * Set up the long jump entry point to the APM BIOS, which is called
 +       * from inline assembly.
 +       */
 +      apm_bios_entry.offset = apm_info.bios.offset;
 +      apm_bios_entry.segment = APM_CS;
 +
 +      /*
 +       * The APM 1.1 BIOS is supposed to provide limit information that it
 +       * recognizes.  Many machines do this correctly, but many others do
 +       * not restrict themselves to their claimed limit.  When this happens,
 +       * they will cause a segmentation violation in the kernel at boot time.
 +       * Most BIOS's, however, will respect a 64k limit, so we use that.
 +       *
 +       * Note we only set APM segments on CPU zero, since we pin the APM
 +       * code to that CPU.
 +       */
 +      gdt = get_cpu_gdt_table(0);
 +      set_base(gdt[APM_CS >> 3],
 +               __va((unsigned long)apm_info.bios.cseg << 4));
 +      set_base(gdt[APM_CS_16 >> 3],
 +               __va((unsigned long)apm_info.bios.cseg_16 << 4));
 +      set_base(gdt[APM_DS >> 3],
 +               __va((unsigned long)apm_info.bios.dseg << 4));
 +
 +      apm_proc = create_proc_entry("apm", 0, NULL);
 +      if (apm_proc)
 +              apm_proc->proc_fops = &apm_file_ops;
 +
 +      kapmd_task = kthread_create(apm, NULL, "kapmd");
 +      if (IS_ERR(kapmd_task)) {
 +              printk(KERN_ERR "apm: disabled - Unable to start kernel "
 +                              "thread.\n");
 +              err = PTR_ERR(kapmd_task);
 +              kapmd_task = NULL;
 +              remove_proc_entry("apm", NULL);
 +              return err;
 +      }
 +      wake_up_process(kapmd_task);
 +
 +      if (num_online_cpus() > 1 && !smp ) {
 +              printk(KERN_NOTICE
 +                 "apm: disabled - APM is not SMP safe (power off active).\n");
 +              return 0;
 +      }
 +
 +      /*
 +       * Note we don't actually care if the misc_device cannot be registered.
 +       * this driver can do its job without it, even if userspace can't
 +       * control it.  just log the error
 +       */
 +      if (misc_register(&apm_device))
 +              printk(KERN_WARNING "apm: Could not register misc device.\n");
 +
 +      if (HZ != 100)
 +              idle_period = (idle_period * HZ) / 100;
 +      if (idle_threshold < 100) {
 +              original_pm_idle = pm_idle;
 +              pm_idle  = apm_cpu_idle;
 +              set_pm_idle = 1;
 +      }
 +
 +      return 0;
 +}
 +
 +static void __exit apm_exit(void)
 +{
 +      int     error;
 +
 +      if (set_pm_idle) {
 +              pm_idle = original_pm_idle;
 +              /*
 +               * We are about to unload the current idle thread pm callback
 +               * (pm_idle), Wait for all processors to update cached/local
 +               * copies of pm_idle before proceeding.
 +               */
 +              cpu_idle_wait();
 +      }
 +      if (((apm_info.bios.flags & APM_BIOS_DISENGAGED) == 0)
 +          && (apm_info.connection_version > 0x0100)) {
 +              error = apm_engage_power_management(APM_DEVICE_ALL, 0);
 +              if (error)
 +                      apm_error("disengage power management", error);
 +      }
 +      misc_deregister(&apm_device);
 +      remove_proc_entry("apm", NULL);
 +      if (power_off)
 +              pm_power_off = NULL;
 +      if (kapmd_task) {
 +              kthread_stop(kapmd_task);
 +              kapmd_task = NULL;
 +      }
 +#ifdef CONFIG_PM_LEGACY
 +      pm_active = 0;
 +#endif
 +}
 +
 +module_init(apm_init);
 +module_exit(apm_exit);
 +
 +MODULE_AUTHOR("Stephen Rothwell");
 +MODULE_DESCRIPTION("Advanced Power Management");
 +MODULE_LICENSE("GPL");
 +module_param(debug, bool, 0644);
 +MODULE_PARM_DESC(debug, "Enable debug mode");
 +module_param(power_off, bool, 0444);
 +MODULE_PARM_DESC(power_off, "Enable power off");
 +module_param(bounce_interval, int, 0444);
 +MODULE_PARM_DESC(bounce_interval,
 +              "Set the number of ticks to ignore suspend bounces");
 +module_param(allow_ints, bool, 0444);
 +MODULE_PARM_DESC(allow_ints, "Allow interrupts during BIOS calls");
 +module_param(broken_psr, bool, 0444);
 +MODULE_PARM_DESC(broken_psr, "BIOS has a broken GetPowerStatus call");
 +module_param(realmode_power_off, bool, 0444);
 +MODULE_PARM_DESC(realmode_power_off,
 +              "Switch to real mode before powering off");
 +module_param(idle_threshold, int, 0444);
 +MODULE_PARM_DESC(idle_threshold,
 +      "System idle percentage above which to make APM BIOS idle calls");
 +module_param(idle_period, int, 0444);
 +MODULE_PARM_DESC(idle_period,
 +      "Period (in sec/100) over which to caculate the idle percentage");
 +module_param(smp, bool, 0444);
 +MODULE_PARM_DESC(smp,
 +      "Set this to enable APM use on an SMP platform. Use with caution on older systems");
 +MODULE_ALIAS_MISCDEV(APM_MINOR_DEV);
index 705e13a,0000000..b6434a7
mode 100644,000000..100644
--- /dev/null
@@@ -1,799 -1,0 +1,799 @@@
- static int sw_any_bug_found(struct dmi_system_id *d)
 +/*
 + * acpi-cpufreq.c - ACPI Processor P-States Driver ($Revision: 1.4 $)
 + *
 + *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 + *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 + *  Copyright (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de>
 + *  Copyright (C) 2006       Denis Sadykov <denis.m.sadykov@intel.com>
 + *
 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + *
 + *  This program is free software; you can redistribute it and/or modify
 + *  it under the terms of the GNU General Public License as published by
 + *  the Free Software Foundation; either version 2 of the License, or (at
 + *  your option) any later version.
 + *
 + *  This program is distributed in the hope that it will be useful, but
 + *  WITHOUT ANY WARRANTY; without even the implied warranty of
 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + *  General Public License for more details.
 + *
 + *  You should have received a copy of the GNU General Public License along
 + *  with this program; if not, write to the Free Software Foundation, Inc.,
 + *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 + *
 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +#include <linux/smp.h>
 +#include <linux/sched.h>
 +#include <linux/cpufreq.h>
 +#include <linux/compiler.h>
 +#include <linux/dmi.h>
 +
 +#include <linux/acpi.h>
 +#include <acpi/processor.h>
 +
 +#include <asm/io.h>
 +#include <asm/msr.h>
 +#include <asm/processor.h>
 +#include <asm/cpufeature.h>
 +#include <asm/delay.h>
 +#include <asm/uaccess.h>
 +
 +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg)
 +
 +MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski");
 +MODULE_DESCRIPTION("ACPI Processor P-States Driver");
 +MODULE_LICENSE("GPL");
 +
 +enum {
 +      UNDEFINED_CAPABLE = 0,
 +      SYSTEM_INTEL_MSR_CAPABLE,
 +      SYSTEM_IO_CAPABLE,
 +};
 +
 +#define INTEL_MSR_RANGE               (0xffff)
 +#define CPUID_6_ECX_APERFMPERF_CAPABILITY     (0x1)
 +
 +struct acpi_cpufreq_data {
 +      struct acpi_processor_performance *acpi_data;
 +      struct cpufreq_frequency_table *freq_table;
 +      unsigned int max_freq;
 +      unsigned int resume;
 +      unsigned int cpu_feature;
 +};
 +
 +static struct acpi_cpufreq_data *drv_data[NR_CPUS];
 +/* acpi_perf_data is a pointer to percpu data. */
 +static struct acpi_processor_performance *acpi_perf_data;
 +
 +static struct cpufreq_driver acpi_cpufreq_driver;
 +
 +static unsigned int acpi_pstate_strict;
 +
 +static int check_est_cpu(unsigned int cpuid)
 +{
 +      struct cpuinfo_x86 *cpu = &cpu_data[cpuid];
 +
 +      if (cpu->x86_vendor != X86_VENDOR_INTEL ||
 +          !cpu_has(cpu, X86_FEATURE_EST))
 +              return 0;
 +
 +      return 1;
 +}
 +
 +static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data)
 +{
 +      struct acpi_processor_performance *perf;
 +      int i;
 +
 +      perf = data->acpi_data;
 +
 +      for (i=0; i<perf->state_count; i++) {
 +              if (value == perf->states[i].status)
 +                      return data->freq_table[i].frequency;
 +      }
 +      return 0;
 +}
 +
 +static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
 +{
 +      int i;
 +      struct acpi_processor_performance *perf;
 +
 +      msr &= INTEL_MSR_RANGE;
 +      perf = data->acpi_data;
 +
 +      for (i=0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
 +              if (msr == perf->states[data->freq_table[i].index].status)
 +                      return data->freq_table[i].frequency;
 +      }
 +      return data->freq_table[0].frequency;
 +}
 +
 +static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data)
 +{
 +      switch (data->cpu_feature) {
 +      case SYSTEM_INTEL_MSR_CAPABLE:
 +              return extract_msr(val, data);
 +      case SYSTEM_IO_CAPABLE:
 +              return extract_io(val, data);
 +      default:
 +              return 0;
 +      }
 +}
 +
 +struct msr_addr {
 +      u32 reg;
 +};
 +
 +struct io_addr {
 +      u16 port;
 +      u8 bit_width;
 +};
 +
 +typedef union {
 +      struct msr_addr msr;
 +      struct io_addr io;
 +} drv_addr_union;
 +
 +struct drv_cmd {
 +      unsigned int type;
 +      cpumask_t mask;
 +      drv_addr_union addr;
 +      u32 val;
 +};
 +
 +static void do_drv_read(struct drv_cmd *cmd)
 +{
 +      u32 h;
 +
 +      switch (cmd->type) {
 +      case SYSTEM_INTEL_MSR_CAPABLE:
 +              rdmsr(cmd->addr.msr.reg, cmd->val, h);
 +              break;
 +      case SYSTEM_IO_CAPABLE:
 +              acpi_os_read_port((acpi_io_address)cmd->addr.io.port,
 +                              &cmd->val,
 +                              (u32)cmd->addr.io.bit_width);
 +              break;
 +      default:
 +              break;
 +      }
 +}
 +
 +static void do_drv_write(struct drv_cmd *cmd)
 +{
 +      u32 lo, hi;
 +
 +      switch (cmd->type) {
 +      case SYSTEM_INTEL_MSR_CAPABLE:
 +              rdmsr(cmd->addr.msr.reg, lo, hi);
 +              lo = (lo & ~INTEL_MSR_RANGE) | (cmd->val & INTEL_MSR_RANGE);
 +              wrmsr(cmd->addr.msr.reg, lo, hi);
 +              break;
 +      case SYSTEM_IO_CAPABLE:
 +              acpi_os_write_port((acpi_io_address)cmd->addr.io.port,
 +                              cmd->val,
 +                              (u32)cmd->addr.io.bit_width);
 +              break;
 +      default:
 +              break;
 +      }
 +}
 +
 +static void drv_read(struct drv_cmd *cmd)
 +{
 +      cpumask_t saved_mask = current->cpus_allowed;
 +      cmd->val = 0;
 +
 +      set_cpus_allowed(current, cmd->mask);
 +      do_drv_read(cmd);
 +      set_cpus_allowed(current, saved_mask);
 +}
 +
 +static void drv_write(struct drv_cmd *cmd)
 +{
 +      cpumask_t saved_mask = current->cpus_allowed;
 +      unsigned int i;
 +
 +      for_each_cpu_mask(i, cmd->mask) {
 +              set_cpus_allowed(current, cpumask_of_cpu(i));
 +              do_drv_write(cmd);
 +      }
 +
 +      set_cpus_allowed(current, saved_mask);
 +      return;
 +}
 +
 +static u32 get_cur_val(cpumask_t mask)
 +{
 +      struct acpi_processor_performance *perf;
 +      struct drv_cmd cmd;
 +
 +      if (unlikely(cpus_empty(mask)))
 +              return 0;
 +
 +      switch (drv_data[first_cpu(mask)]->cpu_feature) {
 +      case SYSTEM_INTEL_MSR_CAPABLE:
 +              cmd.type = SYSTEM_INTEL_MSR_CAPABLE;
 +              cmd.addr.msr.reg = MSR_IA32_PERF_STATUS;
 +              break;
 +      case SYSTEM_IO_CAPABLE:
 +              cmd.type = SYSTEM_IO_CAPABLE;
 +              perf = drv_data[first_cpu(mask)]->acpi_data;
 +              cmd.addr.io.port = perf->control_register.address;
 +              cmd.addr.io.bit_width = perf->control_register.bit_width;
 +              break;
 +      default:
 +              return 0;
 +      }
 +
 +      cmd.mask = mask;
 +
 +      drv_read(&cmd);
 +
 +      dprintk("get_cur_val = %u\n", cmd.val);
 +
 +      return cmd.val;
 +}
 +
 +/*
 + * Return the measured active (C0) frequency on this CPU since last call
 + * to this function.
 + * Input: cpu number
 + * Return: Average CPU frequency in terms of max frequency (zero on error)
 + *
 + * We use IA32_MPERF and IA32_APERF MSRs to get the measured performance
 + * over a period of time, while CPU is in C0 state.
 + * IA32_MPERF counts at the rate of max advertised frequency
 + * IA32_APERF counts at the rate of actual CPU frequency
 + * Only IA32_APERF/IA32_MPERF ratio is architecturally defined and
 + * no meaning should be associated with absolute values of these MSRs.
 + */
 +static unsigned int get_measured_perf(unsigned int cpu)
 +{
 +      union {
 +              struct {
 +                      u32 lo;
 +                      u32 hi;
 +              } split;
 +              u64 whole;
 +      } aperf_cur, mperf_cur;
 +
 +      cpumask_t saved_mask;
 +      unsigned int perf_percent;
 +      unsigned int retval;
 +
 +      saved_mask = current->cpus_allowed;
 +      set_cpus_allowed(current, cpumask_of_cpu(cpu));
 +      if (get_cpu() != cpu) {
 +              /* We were not able to run on requested processor */
 +              put_cpu();
 +              return 0;
 +      }
 +
 +      rdmsr(MSR_IA32_APERF, aperf_cur.split.lo, aperf_cur.split.hi);
 +      rdmsr(MSR_IA32_MPERF, mperf_cur.split.lo, mperf_cur.split.hi);
 +
 +      wrmsr(MSR_IA32_APERF, 0,0);
 +      wrmsr(MSR_IA32_MPERF, 0,0);
 +
 +#ifdef __i386__
 +      /*
 +       * We dont want to do 64 bit divide with 32 bit kernel
 +       * Get an approximate value. Return failure in case we cannot get
 +       * an approximate value.
 +       */
 +      if (unlikely(aperf_cur.split.hi || mperf_cur.split.hi)) {
 +              int shift_count;
 +              u32 h;
 +
 +              h = max_t(u32, aperf_cur.split.hi, mperf_cur.split.hi);
 +              shift_count = fls(h);
 +
 +              aperf_cur.whole >>= shift_count;
 +              mperf_cur.whole >>= shift_count;
 +      }
 +
 +      if (((unsigned long)(-1) / 100) < aperf_cur.split.lo) {
 +              int shift_count = 7;
 +              aperf_cur.split.lo >>= shift_count;
 +              mperf_cur.split.lo >>= shift_count;
 +      }
 +
 +      if (aperf_cur.split.lo && mperf_cur.split.lo)
 +              perf_percent = (aperf_cur.split.lo * 100) / mperf_cur.split.lo;
 +      else
 +              perf_percent = 0;
 +
 +#else
 +      if (unlikely(((unsigned long)(-1) / 100) < aperf_cur.whole)) {
 +              int shift_count = 7;
 +              aperf_cur.whole >>= shift_count;
 +              mperf_cur.whole >>= shift_count;
 +      }
 +
 +      if (aperf_cur.whole && mperf_cur.whole)
 +              perf_percent = (aperf_cur.whole * 100) / mperf_cur.whole;
 +      else
 +              perf_percent = 0;
 +
 +#endif
 +
 +      retval = drv_data[cpu]->max_freq * perf_percent / 100;
 +
 +      put_cpu();
 +      set_cpus_allowed(current, saved_mask);
 +
 +      dprintk("cpu %d: performance percent %d\n", cpu, perf_percent);
 +      return retval;
 +}
 +
 +static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
 +{
 +      struct acpi_cpufreq_data *data = drv_data[cpu];
 +      unsigned int freq;
 +
 +      dprintk("get_cur_freq_on_cpu (%d)\n", cpu);
 +
 +      if (unlikely(data == NULL ||
 +                   data->acpi_data == NULL || data->freq_table == NULL)) {
 +              return 0;
 +      }
 +
 +      freq = extract_freq(get_cur_val(cpumask_of_cpu(cpu)), data);
 +      dprintk("cur freq = %u\n", freq);
 +
 +      return freq;
 +}
 +
 +static unsigned int check_freqs(cpumask_t mask, unsigned int freq,
 +                              struct acpi_cpufreq_data *data)
 +{
 +      unsigned int cur_freq;
 +      unsigned int i;
 +
 +      for (i=0; i<100; i++) {
 +              cur_freq = extract_freq(get_cur_val(mask), data);
 +              if (cur_freq == freq)
 +                      return 1;
 +              udelay(10);
 +      }
 +      return 0;
 +}
 +
 +static int acpi_cpufreq_target(struct cpufreq_policy *policy,
 +                             unsigned int target_freq, unsigned int relation)
 +{
 +      struct acpi_cpufreq_data *data = drv_data[policy->cpu];
 +      struct acpi_processor_performance *perf;
 +      struct cpufreq_freqs freqs;
 +      cpumask_t online_policy_cpus;
 +      struct drv_cmd cmd;
 +      unsigned int next_state = 0; /* Index into freq_table */
 +      unsigned int next_perf_state = 0; /* Index into perf table */
 +      unsigned int i;
 +      int result = 0;
 +
 +      dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu);
 +
 +      if (unlikely(data == NULL ||
 +           data->acpi_data == NULL || data->freq_table == NULL)) {
 +              return -ENODEV;
 +      }
 +
 +      perf = data->acpi_data;
 +      result = cpufreq_frequency_table_target(policy,
 +                                              data->freq_table,
 +                                              target_freq,
 +                                              relation, &next_state);
 +      if (unlikely(result))
 +              return -ENODEV;
 +
 +#ifdef CONFIG_HOTPLUG_CPU
 +      /* cpufreq holds the hotplug lock, so we are safe from here on */
 +      cpus_and(online_policy_cpus, cpu_online_map, policy->cpus);
 +#else
 +      online_policy_cpus = policy->cpus;
 +#endif
 +
 +      next_perf_state = data->freq_table[next_state].index;
 +      if (perf->state == next_perf_state) {
 +              if (unlikely(data->resume)) {
 +                      dprintk("Called after resume, resetting to P%d\n",
 +                              next_perf_state);
 +                      data->resume = 0;
 +              } else {
 +                      dprintk("Already at target state (P%d)\n",
 +                              next_perf_state);
 +                      return 0;
 +              }
 +      }
 +
 +      switch (data->cpu_feature) {
 +      case SYSTEM_INTEL_MSR_CAPABLE:
 +              cmd.type = SYSTEM_INTEL_MSR_CAPABLE;
 +              cmd.addr.msr.reg = MSR_IA32_PERF_CTL;
 +              cmd.val = (u32) perf->states[next_perf_state].control;
 +              break;
 +      case SYSTEM_IO_CAPABLE:
 +              cmd.type = SYSTEM_IO_CAPABLE;
 +              cmd.addr.io.port = perf->control_register.address;
 +              cmd.addr.io.bit_width = perf->control_register.bit_width;
 +              cmd.val = (u32) perf->states[next_perf_state].control;
 +              break;
 +      default:
 +              return -ENODEV;
 +      }
 +
 +      cpus_clear(cmd.mask);
 +
 +      if (policy->shared_type != CPUFREQ_SHARED_TYPE_ANY)
 +              cmd.mask = online_policy_cpus;
 +      else
 +              cpu_set(policy->cpu, cmd.mask);
 +
 +      freqs.old = perf->states[perf->state].core_frequency * 1000;
 +      freqs.new = data->freq_table[next_state].frequency;
 +      for_each_cpu_mask(i, cmd.mask) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 +      }
 +
 +      drv_write(&cmd);
 +
 +      if (acpi_pstate_strict) {
 +              if (!check_freqs(cmd.mask, freqs.new, data)) {
 +                      dprintk("acpi_cpufreq_target failed (%d)\n",
 +                              policy->cpu);
 +                      return -EAGAIN;
 +              }
 +      }
 +
 +      for_each_cpu_mask(i, cmd.mask) {
 +              freqs.cpu = i;
 +              cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 +      }
 +      perf->state = next_perf_state;
 +
 +      return result;
 +}
 +
 +static int acpi_cpufreq_verify(struct cpufreq_policy *policy)
 +{
 +      struct acpi_cpufreq_data *data = drv_data[policy->cpu];
 +
 +      dprintk("acpi_cpufreq_verify\n");
 +
 +      return cpufreq_frequency_table_verify(policy, data->freq_table);
 +}
 +
 +static unsigned long
 +acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu)
 +{
 +      struct acpi_processor_performance *perf = data->acpi_data;
 +
 +      if (cpu_khz) {
 +              /* search the closest match to cpu_khz */
 +              unsigned int i;
 +              unsigned long freq;
 +              unsigned long freqn = perf->states[0].core_frequency * 1000;
 +
 +              for (i=0; i<(perf->state_count-1); i++) {
 +                      freq = freqn;
 +                      freqn = perf->states[i+1].core_frequency * 1000;
 +                      if ((2 * cpu_khz) > (freqn + freq)) {
 +                              perf->state = i;
 +                              return freq;
 +                      }
 +              }
 +              perf->state = perf->state_count-1;
 +              return freqn;
 +      } else {
 +              /* assume CPU is at P0... */
 +              perf->state = 0;
 +              return perf->states[0].core_frequency * 1000;
 +      }
 +}
 +
 +/*
 + * acpi_cpufreq_early_init - initialize ACPI P-States library
 + *
 + * Initialize the ACPI P-States library (drivers/acpi/processor_perflib.c)
 + * in order to determine correct frequency and voltage pairings. We can
 + * do _PDC and _PSD and find out the processor dependency for the
 + * actual init that will happen later...
 + */
 +static int __init acpi_cpufreq_early_init(void)
 +{
 +      dprintk("acpi_cpufreq_early_init\n");
 +
 +      acpi_perf_data = alloc_percpu(struct acpi_processor_performance);
 +      if (!acpi_perf_data) {
 +              dprintk("Memory allocation error for acpi_perf_data.\n");
 +              return -ENOMEM;
 +      }
 +
 +      /* Do initialization in ACPI core */
 +      acpi_processor_preregister_performance(acpi_perf_data);
 +      return 0;
 +}
 +
 +#ifdef CONFIG_SMP
 +/*
 + * Some BIOSes do SW_ANY coordination internally, either set it up in hw
 + * or do it in BIOS firmware and won't inform about it to OS. If not
 + * detected, this has a side effect of making CPU run at a different speed
 + * than OS intended it to run at. Detect it and handle it cleanly.
 + */
 +static int bios_with_sw_any_bug;
 +
- static struct dmi_system_id sw_any_bug_dmi_table[] = {
++static int sw_any_bug_found(const struct dmi_system_id *d)
 +{
 +      bios_with_sw_any_bug = 1;
 +      return 0;
 +}
 +
++static const struct dmi_system_id sw_any_bug_dmi_table[] = {
 +      {
 +              .callback = sw_any_bug_found,
 +              .ident = "Supermicro Server X6DLP",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "080010"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "X6DLP"),
 +              },
 +      },
 +      { }
 +};
 +#endif
 +
 +static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
 +{
 +      unsigned int i;
 +      unsigned int valid_states = 0;
 +      unsigned int cpu = policy->cpu;
 +      struct acpi_cpufreq_data *data;
 +      unsigned int result = 0;
 +      struct cpuinfo_x86 *c = &cpu_data[policy->cpu];
 +      struct acpi_processor_performance *perf;
 +
 +      dprintk("acpi_cpufreq_cpu_init\n");
 +
 +      data = kzalloc(sizeof(struct acpi_cpufreq_data), GFP_KERNEL);
 +      if (!data)
 +              return -ENOMEM;
 +
 +      data->acpi_data = percpu_ptr(acpi_perf_data, cpu);
 +      drv_data[cpu] = data;
 +
 +      if (cpu_has(c, X86_FEATURE_CONSTANT_TSC))
 +              acpi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS;
 +
 +      result = acpi_processor_register_performance(data->acpi_data, cpu);
 +      if (result)
 +              goto err_free;
 +
 +      perf = data->acpi_data;
 +      policy->shared_type = perf->shared_type;
 +
 +      /*
 +       * Will let policy->cpus know about dependency only when software
 +       * coordination is required.
 +       */
 +      if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL ||
 +          policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) {
 +              policy->cpus = perf->shared_cpu_map;
 +      }
 +
 +#ifdef CONFIG_SMP
 +      dmi_check_system(sw_any_bug_dmi_table);
 +      if (bios_with_sw_any_bug && cpus_weight(policy->cpus) == 1) {
 +              policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
 +              policy->cpus = cpu_core_map[cpu];
 +      }
 +#endif
 +
 +      /* capability check */
 +      if (perf->state_count <= 1) {
 +              dprintk("No P-States\n");
 +              result = -ENODEV;
 +              goto err_unreg;
 +      }
 +
 +      if (perf->control_register.space_id != perf->status_register.space_id) {
 +              result = -ENODEV;
 +              goto err_unreg;
 +      }
 +
 +      switch (perf->control_register.space_id) {
 +      case ACPI_ADR_SPACE_SYSTEM_IO:
 +              dprintk("SYSTEM IO addr space\n");
 +              data->cpu_feature = SYSTEM_IO_CAPABLE;
 +              break;
 +      case ACPI_ADR_SPACE_FIXED_HARDWARE:
 +              dprintk("HARDWARE addr space\n");
 +              if (!check_est_cpu(cpu)) {
 +                      result = -ENODEV;
 +                      goto err_unreg;
 +              }
 +              data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE;
 +              break;
 +      default:
 +              dprintk("Unknown addr space %d\n",
 +                      (u32) (perf->control_register.space_id));
 +              result = -ENODEV;
 +              goto err_unreg;
 +      }
 +
 +      data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) *
 +                  (perf->state_count+1), GFP_KERNEL);
 +      if (!data->freq_table) {
 +              result = -ENOMEM;
 +              goto err_unreg;
 +      }
 +
 +      /* detect transition latency */
 +      policy->cpuinfo.transition_latency = 0;
 +      for (i=0; i<perf->state_count; i++) {
 +              if ((perf->states[i].transition_latency * 1000) >
 +                  policy->cpuinfo.transition_latency)
 +                      policy->cpuinfo.transition_latency =
 +                          perf->states[i].transition_latency * 1000;
 +      }
 +      policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 +
 +      data->max_freq = perf->states[0].core_frequency * 1000;
 +      /* table init */
 +      for (i=0; i<perf->state_count; i++) {
 +              if (i>0 && perf->states[i].core_frequency >=
 +                  data->freq_table[valid_states-1].frequency / 1000)
 +                      continue;
 +
 +              data->freq_table[valid_states].index = i;
 +              data->freq_table[valid_states].frequency =
 +                  perf->states[i].core_frequency * 1000;
 +              valid_states++;
 +      }
 +      data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END;
 +      perf->state = 0;
 +
 +      result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
 +      if (result)
 +              goto err_freqfree;
 +
 +      switch (perf->control_register.space_id) {
 +      case ACPI_ADR_SPACE_SYSTEM_IO:
 +              /* Current speed is unknown and not detectable by IO port */
 +              policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu);
 +              break;
 +      case ACPI_ADR_SPACE_FIXED_HARDWARE:
 +              acpi_cpufreq_driver.get = get_cur_freq_on_cpu;
 +              policy->cur = get_cur_freq_on_cpu(cpu);
 +              break;
 +      default:
 +              break;
 +      }
 +
 +      /* notify BIOS that we exist */
 +      acpi_processor_notify_smm(THIS_MODULE);
 +
 +      /* Check for APERF/MPERF support in hardware */
 +      if (c->x86_vendor == X86_VENDOR_INTEL && c->cpuid_level >= 6) {
 +              unsigned int ecx;
 +              ecx = cpuid_ecx(6);
 +              if (ecx & CPUID_6_ECX_APERFMPERF_CAPABILITY)
 +                      acpi_cpufreq_driver.getavg = get_measured_perf;
 +      }
 +
 +      dprintk("CPU%u - ACPI performance management activated.\n", cpu);
 +      for (i = 0; i < perf->state_count; i++)
 +              dprintk("     %cP%d: %d MHz, %d mW, %d uS\n",
 +                      (i == perf->state ? '*' : ' '), i,
 +                      (u32) perf->states[i].core_frequency,
 +                      (u32) perf->states[i].power,
 +                      (u32) perf->states[i].transition_latency);
 +
 +      cpufreq_frequency_table_get_attr(data->freq_table, policy->cpu);
 +
 +      /*
 +       * the first call to ->target() should result in us actually
 +       * writing something to the appropriate registers.
 +       */
 +      data->resume = 1;
 +
 +      return result;
 +
 +err_freqfree:
 +      kfree(data->freq_table);
 +err_unreg:
 +      acpi_processor_unregister_performance(perf, cpu);
 +err_free:
 +      kfree(data);
 +      drv_data[cpu] = NULL;
 +
 +      return result;
 +}
 +
 +static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 +{
 +      struct acpi_cpufreq_data *data = drv_data[policy->cpu];
 +
 +      dprintk("acpi_cpufreq_cpu_exit\n");
 +
 +      if (data) {
 +              cpufreq_frequency_table_put_attr(policy->cpu);
 +              drv_data[policy->cpu] = NULL;
 +              acpi_processor_unregister_performance(data->acpi_data,
 +                                                    policy->cpu);
 +              kfree(data);
 +      }
 +
 +      return 0;
 +}
 +
 +static int acpi_cpufreq_resume(struct cpufreq_policy *policy)
 +{
 +      struct acpi_cpufreq_data *data = drv_data[policy->cpu];
 +
 +      dprintk("acpi_cpufreq_resume\n");
 +
 +      data->resume = 1;
 +
 +      return 0;
 +}
 +
 +static struct freq_attr *acpi_cpufreq_attr[] = {
 +      &cpufreq_freq_attr_scaling_available_freqs,
 +      NULL,
 +};
 +
 +static struct cpufreq_driver acpi_cpufreq_driver = {
 +      .verify = acpi_cpufreq_verify,
 +      .target = acpi_cpufreq_target,
 +      .init = acpi_cpufreq_cpu_init,
 +      .exit = acpi_cpufreq_cpu_exit,
 +      .resume = acpi_cpufreq_resume,
 +      .name = "acpi-cpufreq",
 +      .owner = THIS_MODULE,
 +      .attr = acpi_cpufreq_attr,
 +};
 +
 +static int __init acpi_cpufreq_init(void)
 +{
 +      int ret;
 +
 +      dprintk("acpi_cpufreq_init\n");
 +
 +      ret = acpi_cpufreq_early_init();
 +      if (ret)
 +              return ret;
 +
 +      return cpufreq_register_driver(&acpi_cpufreq_driver);
 +}
 +
 +static void __exit acpi_cpufreq_exit(void)
 +{
 +      dprintk("acpi_cpufreq_exit\n");
 +
 +      cpufreq_unregister_driver(&acpi_cpufreq_driver);
 +
 +      free_percpu(acpi_perf_data);
 +
 +      return;
 +}
 +
 +module_param(acpi_pstate_strict, uint, 0644);
 +MODULE_PARM_DESC(acpi_pstate_strict,
 +      "value 0 or non-zero. non-zero -> strict ACPI checks are "
 +      "performed during frequency changes.");
 +
 +late_initcall(acpi_cpufreq_init);
 +module_exit(acpi_cpufreq_exit);
 +
 +MODULE_ALIAS("acpi");
index 0d79624,0000000..b37ed22
mode 100644,000000..100644
--- /dev/null
@@@ -1,413 -1,0 +1,413 @@@
- static int __init set_bios_reboot(struct dmi_system_id *d)
 +/*
 + *  linux/arch/i386/kernel/reboot.c
 + */
 +
 +#include <linux/mm.h>
 +#include <linux/module.h>
 +#include <linux/delay.h>
 +#include <linux/init.h>
 +#include <linux/interrupt.h>
 +#include <linux/mc146818rtc.h>
 +#include <linux/efi.h>
 +#include <linux/dmi.h>
 +#include <linux/ctype.h>
 +#include <linux/pm.h>
 +#include <linux/reboot.h>
 +#include <asm/uaccess.h>
 +#include <asm/apic.h>
 +#include <asm/desc.h>
 +#include "mach_reboot.h"
 +#include <asm/reboot_fixups.h>
 +#include <asm/reboot.h>
 +
 +/*
 + * Power off function, if any
 + */
 +void (*pm_power_off)(void);
 +EXPORT_SYMBOL(pm_power_off);
 +
 +static int reboot_mode;
 +static int reboot_thru_bios;
 +
 +#ifdef CONFIG_SMP
 +static int reboot_cpu = -1;
 +#endif
 +static int __init reboot_setup(char *str)
 +{
 +      while(1) {
 +              switch (*str) {
 +              case 'w': /* "warm" reboot (no memory testing etc) */
 +                      reboot_mode = 0x1234;
 +                      break;
 +              case 'c': /* "cold" reboot (with memory testing etc) */
 +                      reboot_mode = 0x0;
 +                      break;
 +              case 'b': /* "bios" reboot by jumping through the BIOS */
 +                      reboot_thru_bios = 1;
 +                      break;
 +              case 'h': /* "hard" reboot by toggling RESET and/or crashing the CPU */
 +                      reboot_thru_bios = 0;
 +                      break;
 +#ifdef CONFIG_SMP
 +              case 's': /* "smp" reboot by executing reset on BSP or other CPU*/
 +                      if (isdigit(*(str+1))) {
 +                              reboot_cpu = (int) (*(str+1) - '0');
 +                              if (isdigit(*(str+2)))
 +                                      reboot_cpu = reboot_cpu*10 + (int)(*(str+2) - '0');
 +                      }
 +                              /* we will leave sorting out the final value 
 +                              when we are ready to reboot, since we might not
 +                              have set up boot_cpu_id or smp_num_cpu */
 +                      break;
 +#endif
 +              }
 +              if((str = strchr(str,',')) != NULL)
 +                      str++;
 +              else
 +                      break;
 +      }
 +      return 1;
 +}
 +
 +__setup("reboot=", reboot_setup);
 +
 +/*
 + * Reboot options and system auto-detection code provided by
 + * Dell Inc. so their systems "just work". :-)
 + */
 +
 +/*
 + * Some machines require the "reboot=b"  commandline option, this quirk makes that automatic.
 + */
++static int __init set_bios_reboot(const struct dmi_system_id *d)
 +{
 +      if (!reboot_thru_bios) {
 +              reboot_thru_bios = 1;
 +              printk(KERN_INFO "%s series board detected. Selecting BIOS-method for reboots.\n", d->ident);
 +      }
 +      return 0;
 +}
 +
 +static struct dmi_system_id __initdata reboot_dmi_table[] = {
 +      {       /* Handle problems with rebooting on Dell E520's */
 +              .callback = set_bios_reboot,
 +              .ident = "Dell E520",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM061"),
 +              },
 +      },
 +      {       /* Handle problems with rebooting on Dell 1300's */
 +              .callback = set_bios_reboot,
 +              .ident = "Dell PowerEdge 1300",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/"),
 +              },
 +      },
 +      {       /* Handle problems with rebooting on Dell 300's */
 +              .callback = set_bios_reboot,
 +              .ident = "Dell PowerEdge 300",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"),
 +              },
 +      },
 +      {       /* Handle problems with rebooting on Dell Optiplex 745's SFF*/
 +              .callback = set_bios_reboot,
 +              .ident = "Dell OptiPlex 745",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
 +                      DMI_MATCH(DMI_BOARD_NAME, "0WF810"),
 +              },
 +      },
 +      {       /* Handle problems with rebooting on Dell 2400's */
 +              .callback = set_bios_reboot,
 +              .ident = "Dell PowerEdge 2400",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"),
 +              },
 +      },
 +      {       /* Handle problems with rebooting on HP laptops */
 +              .callback = set_bios_reboot,
 +              .ident = "HP Compaq Laptop",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq"),
 +              },
 +      },
 +      { }
 +};
 +
 +static int __init reboot_init(void)
 +{
 +      dmi_check_system(reboot_dmi_table);
 +      return 0;
 +}
 +
 +core_initcall(reboot_init);
 +
 +/* The following code and data reboots the machine by switching to real
 +   mode and jumping to the BIOS reset entry point, as if the CPU has
 +   really been reset.  The previous version asked the keyboard
 +   controller to pulse the CPU reset line, which is more thorough, but
 +   doesn't work with at least one type of 486 motherboard.  It is easy
 +   to stop this code working; hence the copious comments. */
 +
 +static unsigned long long
 +real_mode_gdt_entries [3] =
 +{
 +      0x0000000000000000ULL,  /* Null descriptor */
 +      0x00009a000000ffffULL,  /* 16-bit real-mode 64k code at 0x00000000 */
 +      0x000092000100ffffULL   /* 16-bit real-mode 64k data at 0x00000100 */
 +};
 +
 +static struct Xgt_desc_struct
 +real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
 +real_mode_idt = { 0x3ff, 0 },
 +no_idt = { 0, 0 };
 +
 +
 +/* This is 16-bit protected mode code to disable paging and the cache,
 +   switch to real mode and jump to the BIOS reset code.
 +
 +   The instruction that switches to real mode by writing to CR0 must be
 +   followed immediately by a far jump instruction, which set CS to a
 +   valid value for real mode, and flushes the prefetch queue to avoid
 +   running instructions that have already been decoded in protected
 +   mode.
 +
 +   Clears all the flags except ET, especially PG (paging), PE
 +   (protected-mode enable) and TS (task switch for coprocessor state
 +   save).  Flushes the TLB after paging has been disabled.  Sets CD and
 +   NW, to disable the cache on a 486, and invalidates the cache.  This
 +   is more like the state of a 486 after reset.  I don't know if
 +   something else should be done for other chips.
 +
 +   More could be done here to set up the registers as if a CPU reset had
 +   occurred; hopefully real BIOSs don't assume much. */
 +
 +static unsigned char real_mode_switch [] =
 +{
 +      0x66, 0x0f, 0x20, 0xc0,                 /*    movl  %cr0,%eax        */
 +      0x66, 0x83, 0xe0, 0x11,                 /*    andl  $0x00000011,%eax */
 +      0x66, 0x0d, 0x00, 0x00, 0x00, 0x60,     /*    orl   $0x60000000,%eax */
 +      0x66, 0x0f, 0x22, 0xc0,                 /*    movl  %eax,%cr0        */
 +      0x66, 0x0f, 0x22, 0xd8,                 /*    movl  %eax,%cr3        */
 +      0x66, 0x0f, 0x20, 0xc3,                 /*    movl  %cr0,%ebx        */
 +      0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60,       /*    andl  $0x60000000,%ebx */
 +      0x74, 0x02,                             /*    jz    f                */
 +      0x0f, 0x09,                             /*    wbinvd                 */
 +      0x24, 0x10,                             /* f: andb  $0x10,al         */
 +      0x66, 0x0f, 0x22, 0xc0                  /*    movl  %eax,%cr0        */
 +};
 +static unsigned char jump_to_bios [] =
 +{
 +      0xea, 0x00, 0x00, 0xff, 0xff            /*    ljmp  $0xffff,$0x0000  */
 +};
 +
 +/*
 + * Switch to real mode and then execute the code
 + * specified by the code and length parameters.
 + * We assume that length will aways be less that 100!
 + */
 +void machine_real_restart(unsigned char *code, int length)
 +{
 +      local_irq_disable();
 +
 +      /* Write zero to CMOS register number 0x0f, which the BIOS POST
 +         routine will recognize as telling it to do a proper reboot.  (Well
 +         that's what this book in front of me says -- it may only apply to
 +         the Phoenix BIOS though, it's not clear).  At the same time,
 +         disable NMIs by setting the top bit in the CMOS address register,
 +         as we're about to do peculiar things to the CPU.  I'm not sure if
 +         `outb_p' is needed instead of just `outb'.  Use it to be on the
 +         safe side.  (Yes, CMOS_WRITE does outb_p's. -  Paul G.)
 +       */
 +
 +      spin_lock(&rtc_lock);
 +      CMOS_WRITE(0x00, 0x8f);
 +      spin_unlock(&rtc_lock);
 +
 +      /* Remap the kernel at virtual address zero, as well as offset zero
 +         from the kernel segment.  This assumes the kernel segment starts at
 +         virtual address PAGE_OFFSET. */
 +
 +      memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS,
 +              sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
 +
 +      /*
 +       * Use `swapper_pg_dir' as our page directory.
 +       */
 +      load_cr3(swapper_pg_dir);
 +
 +      /* Write 0x1234 to absolute memory location 0x472.  The BIOS reads
 +         this on booting to tell it to "Bypass memory test (also warm
 +         boot)".  This seems like a fairly standard thing that gets set by
 +         REBOOT.COM programs, and the previous reset routine did this
 +         too. */
 +
 +      *((unsigned short *)0x472) = reboot_mode;
 +
 +      /* For the switch to real mode, copy some code to low memory.  It has
 +         to be in the first 64k because it is running in 16-bit mode, and it
 +         has to have the same physical and virtual address, because it turns
 +         off paging.  Copy it near the end of the first page, out of the way
 +         of BIOS variables. */
 +
 +      memcpy ((void *) (0x1000 - sizeof (real_mode_switch) - 100),
 +              real_mode_switch, sizeof (real_mode_switch));
 +      memcpy ((void *) (0x1000 - 100), code, length);
 +
 +      /* Set up the IDT for real mode. */
 +
 +      load_idt(&real_mode_idt);
 +
 +      /* Set up a GDT from which we can load segment descriptors for real
 +         mode.  The GDT is not used in real mode; it is just needed here to
 +         prepare the descriptors. */
 +
 +      load_gdt(&real_mode_gdt);
 +
 +      /* Load the data segment registers, and thus the descriptors ready for
 +         real mode.  The base address of each segment is 0x100, 16 times the
 +         selector value being loaded here.  This is so that the segment
 +         registers don't have to be reloaded after switching to real mode:
 +         the values are consistent for real mode operation already. */
 +
 +      __asm__ __volatile__ ("movl $0x0010,%%eax\n"
 +                              "\tmovl %%eax,%%ds\n"
 +                              "\tmovl %%eax,%%es\n"
 +                              "\tmovl %%eax,%%fs\n"
 +                              "\tmovl %%eax,%%gs\n"
 +                              "\tmovl %%eax,%%ss" : : : "eax");
 +
 +      /* Jump to the 16-bit code that we copied earlier.  It disables paging
 +         and the cache, switches to real mode, and jumps to the BIOS reset
 +         entry point. */
 +
 +      __asm__ __volatile__ ("ljmp $0x0008,%0"
 +                              :
 +                              : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100)));
 +}
 +#ifdef CONFIG_APM_MODULE
 +EXPORT_SYMBOL(machine_real_restart);
 +#endif
 +
 +static void native_machine_shutdown(void)
 +{
 +#ifdef CONFIG_SMP
 +      int reboot_cpu_id;
 +
 +      /* The boot cpu is always logical cpu 0 */
 +      reboot_cpu_id = 0;
 +
 +      /* See if there has been given a command line override */
 +      if ((reboot_cpu != -1) && (reboot_cpu < NR_CPUS) &&
 +              cpu_isset(reboot_cpu, cpu_online_map)) {
 +              reboot_cpu_id = reboot_cpu;
 +      }
 +
 +      /* Make certain the cpu I'm rebooting on is online */
 +      if (!cpu_isset(reboot_cpu_id, cpu_online_map)) {
 +              reboot_cpu_id = smp_processor_id();
 +      }
 +
 +      /* Make certain I only run on the appropriate processor */
 +      set_cpus_allowed(current, cpumask_of_cpu(reboot_cpu_id));
 +
 +      /* O.K. Now that I'm on the appropriate processor, stop
 +       * all of the others, and disable their local APICs.
 +       */
 +
 +      smp_send_stop();
 +#endif /* CONFIG_SMP */
 +
 +      lapic_shutdown();
 +
 +#ifdef CONFIG_X86_IO_APIC
 +      disable_IO_APIC();
 +#endif
 +}
 +
 +void __attribute__((weak)) mach_reboot_fixups(void)
 +{
 +}
 +
 +static void native_machine_emergency_restart(void)
 +{
 +      if (!reboot_thru_bios) {
 +              if (efi_enabled) {
 +                      efi.reset_system(EFI_RESET_COLD, EFI_SUCCESS, 0, NULL);
 +                      load_idt(&no_idt);
 +                      __asm__ __volatile__("int3");
 +              }
 +              /* rebooting needs to touch the page at absolute addr 0 */
 +              *((unsigned short *)__va(0x472)) = reboot_mode;
 +              for (;;) {
 +                      mach_reboot_fixups(); /* for board specific fixups */
 +                      mach_reboot();
 +                      /* That didn't work - force a triple fault.. */
 +                      load_idt(&no_idt);
 +                      __asm__ __volatile__("int3");
 +              }
 +      }
 +      if (efi_enabled)
 +              efi.reset_system(EFI_RESET_WARM, EFI_SUCCESS, 0, NULL);
 +
 +      machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
 +}
 +
 +static void native_machine_restart(char * __unused)
 +{
 +      machine_shutdown();
 +      machine_emergency_restart();
 +}
 +
 +static void native_machine_halt(void)
 +{
 +}
 +
 +static void native_machine_power_off(void)
 +{
 +      if (pm_power_off) {
 +              machine_shutdown();
 +              pm_power_off();
 +      }
 +}
 +
 +
 +struct machine_ops machine_ops = {
 +      .power_off = native_machine_power_off,
 +      .shutdown = native_machine_shutdown,
 +      .emergency_restart = native_machine_emergency_restart,
 +      .restart = native_machine_restart,
 +      .halt = native_machine_halt,
 +};
 +
 +void machine_power_off(void)
 +{
 +      machine_ops.power_off();
 +}
 +
 +void machine_shutdown(void)
 +{
 +      machine_ops.shutdown();
 +}
 +
 +void machine_emergency_restart(void)
 +{
 +      machine_ops.emergency_restart();
 +}
 +
 +void machine_restart(char *cmd)
 +{
 +      machine_ops.restart(cmd);
 +}
 +
 +void machine_halt(void)
 +{
 +      machine_ops.halt();
 +}
index a39280b,0000000..3ed0ae8
mode 100644,000000..100644
--- /dev/null
@@@ -1,413 -1,0 +1,413 @@@
- static int __init dmi_mark_tsc_unstable(struct dmi_system_id *d)
 +/*
 + * This code largely moved from arch/i386/kernel/timer/timer_tsc.c
 + * which was originally moved from arch/i386/kernel/time.c.
 + * See comments there for proper credits.
 + */
 +
 +#include <linux/sched.h>
 +#include <linux/clocksource.h>
 +#include <linux/workqueue.h>
 +#include <linux/cpufreq.h>
 +#include <linux/jiffies.h>
 +#include <linux/init.h>
 +#include <linux/dmi.h>
 +
 +#include <asm/delay.h>
 +#include <asm/tsc.h>
 +#include <asm/io.h>
 +#include <asm/timer.h>
 +
 +#include "mach_timer.h"
 +
 +static int tsc_enabled;
 +
 +/*
 + * On some systems the TSC frequency does not
 + * change with the cpu frequency. So we need
 + * an extra value to store the TSC freq
 + */
 +unsigned int tsc_khz;
 +EXPORT_SYMBOL_GPL(tsc_khz);
 +
 +int tsc_disable;
 +
 +#ifdef CONFIG_X86_TSC
 +static int __init tsc_setup(char *str)
 +{
 +      printk(KERN_WARNING "notsc: Kernel compiled with CONFIG_X86_TSC, "
 +                              "cannot disable TSC.\n");
 +      return 1;
 +}
 +#else
 +/*
 + * disable flag for tsc. Takes effect by clearing the TSC cpu flag
 + * in cpu/common.c
 + */
 +static int __init tsc_setup(char *str)
 +{
 +      tsc_disable = 1;
 +
 +      return 1;
 +}
 +#endif
 +
 +__setup("notsc", tsc_setup);
 +
 +/*
 + * code to mark and check if the TSC is unstable
 + * due to cpufreq or due to unsynced TSCs
 + */
 +static int tsc_unstable;
 +
 +int check_tsc_unstable(void)
 +{
 +      return tsc_unstable;
 +}
 +EXPORT_SYMBOL_GPL(check_tsc_unstable);
 +
 +/* Accellerators for sched_clock()
 + * convert from cycles(64bits) => nanoseconds (64bits)
 + *  basic equation:
 + *            ns = cycles / (freq / ns_per_sec)
 + *            ns = cycles * (ns_per_sec / freq)
 + *            ns = cycles * (10^9 / (cpu_khz * 10^3))
 + *            ns = cycles * (10^6 / cpu_khz)
 + *
 + *    Then we use scaling math (suggested by george@mvista.com) to get:
 + *            ns = cycles * (10^6 * SC / cpu_khz) / SC
 + *            ns = cycles * cyc2ns_scale / SC
 + *
 + *    And since SC is a constant power of two, we can convert the div
 + *  into a shift.
 + *
 + *  We can use khz divisor instead of mhz to keep a better percision, since
 + *  cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits.
 + *  (mathieu.desnoyers@polymtl.ca)
 + *
 + *                    -johnstul@us.ibm.com "math is hard, lets go shopping!"
 + */
 +unsigned long cyc2ns_scale __read_mostly;
 +
 +#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
 +
 +static inline void set_cyc2ns_scale(unsigned long cpu_khz)
 +{
 +      cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz;
 +}
 +
 +/*
 + * Scheduler clock - returns current time in nanosec units.
 + */
 +unsigned long long native_sched_clock(void)
 +{
 +      unsigned long long this_offset;
 +
 +      /*
 +       * Fall back to jiffies if there's no TSC available:
 +       * ( But note that we still use it if the TSC is marked
 +       *   unstable. We do this because unlike Time Of Day,
 +       *   the scheduler clock tolerates small errors and it's
 +       *   very important for it to be as fast as the platform
 +       *   can achive it. )
 +       */
 +      if (unlikely(!tsc_enabled && !tsc_unstable))
 +              /* No locking but a rare wrong value is not a big deal: */
 +              return (jiffies_64 - INITIAL_JIFFIES) * (1000000000 / HZ);
 +
 +      /* read the Time Stamp Counter: */
 +      rdtscll(this_offset);
 +
 +      /* return the value in ns */
 +      return cycles_2_ns(this_offset);
 +}
 +
 +/* We need to define a real function for sched_clock, to override the
 +   weak default version */
 +#ifdef CONFIG_PARAVIRT
 +unsigned long long sched_clock(void)
 +{
 +      return paravirt_sched_clock();
 +}
 +#else
 +unsigned long long sched_clock(void)
 +      __attribute__((alias("native_sched_clock")));
 +#endif
 +
 +unsigned long native_calculate_cpu_khz(void)
 +{
 +      unsigned long long start, end;
 +      unsigned long count;
 +      u64 delta64;
 +      int i;
 +      unsigned long flags;
 +
 +      local_irq_save(flags);
 +
 +      /* run 3 times to ensure the cache is warm */
 +      for (i = 0; i < 3; i++) {
 +              mach_prepare_counter();
 +              rdtscll(start);
 +              mach_countup(&count);
 +              rdtscll(end);
 +      }
 +      /*
 +       * Error: ECTCNEVERSET
 +       * The CTC wasn't reliable: we got a hit on the very first read,
 +       * or the CPU was so fast/slow that the quotient wouldn't fit in
 +       * 32 bits..
 +       */
 +      if (count <= 1)
 +              goto err;
 +
 +      delta64 = end - start;
 +
 +      /* cpu freq too fast: */
 +      if (delta64 > (1ULL<<32))
 +              goto err;
 +
 +      /* cpu freq too slow: */
 +      if (delta64 <= CALIBRATE_TIME_MSEC)
 +              goto err;
 +
 +      delta64 += CALIBRATE_TIME_MSEC/2; /* round for do_div */
 +      do_div(delta64,CALIBRATE_TIME_MSEC);
 +
 +      local_irq_restore(flags);
 +      return (unsigned long)delta64;
 +err:
 +      local_irq_restore(flags);
 +      return 0;
 +}
 +
 +int recalibrate_cpu_khz(void)
 +{
 +#ifndef CONFIG_SMP
 +      unsigned long cpu_khz_old = cpu_khz;
 +
 +      if (cpu_has_tsc) {
 +              cpu_khz = calculate_cpu_khz();
 +              tsc_khz = cpu_khz;
 +              cpu_data[0].loops_per_jiffy =
 +                      cpufreq_scale(cpu_data[0].loops_per_jiffy,
 +                                      cpu_khz_old, cpu_khz);
 +              return 0;
 +      } else
 +              return -ENODEV;
 +#else
 +      return -ENODEV;
 +#endif
 +}
 +
 +EXPORT_SYMBOL(recalibrate_cpu_khz);
 +
 +#ifdef CONFIG_CPU_FREQ
 +
 +/*
 + * if the CPU frequency is scaled, TSC-based delays will need a different
 + * loops_per_jiffy value to function properly.
 + */
 +static unsigned int ref_freq = 0;
 +static unsigned long loops_per_jiffy_ref = 0;
 +static unsigned long cpu_khz_ref = 0;
 +
 +static int
 +time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, void *data)
 +{
 +      struct cpufreq_freqs *freq = data;
 +
 +      if (!ref_freq) {
 +              if (!freq->old){
 +                      ref_freq = freq->new;
 +                      return 0;
 +              }
 +              ref_freq = freq->old;
 +              loops_per_jiffy_ref = cpu_data[freq->cpu].loops_per_jiffy;
 +              cpu_khz_ref = cpu_khz;
 +      }
 +
 +      if ((val == CPUFREQ_PRECHANGE  && freq->old < freq->new) ||
 +          (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) ||
 +          (val == CPUFREQ_RESUMECHANGE)) {
 +              if (!(freq->flags & CPUFREQ_CONST_LOOPS))
 +                      cpu_data[freq->cpu].loops_per_jiffy =
 +                              cpufreq_scale(loops_per_jiffy_ref,
 +                                              ref_freq, freq->new);
 +
 +              if (cpu_khz) {
 +
 +                      if (num_online_cpus() == 1)
 +                              cpu_khz = cpufreq_scale(cpu_khz_ref,
 +                                              ref_freq, freq->new);
 +                      if (!(freq->flags & CPUFREQ_CONST_LOOPS)) {
 +                              tsc_khz = cpu_khz;
 +                              set_cyc2ns_scale(cpu_khz);
 +                              /*
 +                               * TSC based sched_clock turns
 +                               * to junk w/ cpufreq
 +                               */
 +                              mark_tsc_unstable("cpufreq changes");
 +                      }
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +static struct notifier_block time_cpufreq_notifier_block = {
 +      .notifier_call  = time_cpufreq_notifier
 +};
 +
 +static int __init cpufreq_tsc(void)
 +{
 +      return cpufreq_register_notifier(&time_cpufreq_notifier_block,
 +                                       CPUFREQ_TRANSITION_NOTIFIER);
 +}
 +core_initcall(cpufreq_tsc);
 +
 +#endif
 +
 +/* clock source code */
 +
 +static unsigned long current_tsc_khz = 0;
 +
 +static cycle_t read_tsc(void)
 +{
 +      cycle_t ret;
 +
 +      rdtscll(ret);
 +
 +      return ret;
 +}
 +
 +static struct clocksource clocksource_tsc = {
 +      .name                   = "tsc",
 +      .rating                 = 300,
 +      .read                   = read_tsc,
 +      .mask                   = CLOCKSOURCE_MASK(64),
 +      .mult                   = 0, /* to be set */
 +      .shift                  = 22,
 +      .flags                  = CLOCK_SOURCE_IS_CONTINUOUS |
 +                                CLOCK_SOURCE_MUST_VERIFY,
 +};
 +
 +void mark_tsc_unstable(char *reason)
 +{
 +      if (!tsc_unstable) {
 +              tsc_unstable = 1;
 +              tsc_enabled = 0;
 +              printk("Marking TSC unstable due to: %s.\n", reason);
 +              /* Can be called before registration */
 +              if (clocksource_tsc.mult)
 +                      clocksource_change_rating(&clocksource_tsc, 0);
 +              else
 +                      clocksource_tsc.rating = 0;
 +      }
 +}
 +EXPORT_SYMBOL_GPL(mark_tsc_unstable);
 +
++static int __init dmi_mark_tsc_unstable(const struct dmi_system_id *d)
 +{
 +      printk(KERN_NOTICE "%s detected: marking TSC unstable.\n",
 +                     d->ident);
 +      tsc_unstable = 1;
 +      return 0;
 +}
 +
 +/* List of systems that have known TSC problems */
 +static struct dmi_system_id __initdata bad_tsc_dmi_table[] = {
 +      {
 +       .callback = dmi_mark_tsc_unstable,
 +       .ident = "IBM Thinkpad 380XD",
 +       .matches = {
 +                   DMI_MATCH(DMI_BOARD_VENDOR, "IBM"),
 +                   DMI_MATCH(DMI_BOARD_NAME, "2635FA0"),
 +                   },
 +       },
 +       {}
 +};
 +
 +/*
 + * Make an educated guess if the TSC is trustworthy and synchronized
 + * over all CPUs.
 + */
 +__cpuinit int unsynchronized_tsc(void)
 +{
 +      if (!cpu_has_tsc || tsc_unstable)
 +              return 1;
 +      /*
 +       * Intel systems are normally all synchronized.
 +       * Exceptions must mark TSC as unstable:
 +       */
 +      if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) {
 +              /* assume multi socket systems are not synchronized: */
 +              if (num_possible_cpus() > 1)
 +                      tsc_unstable = 1;
 +      }
 +      return tsc_unstable;
 +}
 +
 +/*
 + * Geode_LX - the OLPC CPU has a possibly a very reliable TSC
 + */
 +#ifdef CONFIG_MGEODE_LX
 +/* RTSC counts during suspend */
 +#define RTSC_SUSP 0x100
 +
 +static void __init check_geode_tsc_reliable(void)
 +{
 +      unsigned long val;
 +
 +      rdmsrl(MSR_GEODE_BUSCONT_CONF0, val);
 +      if ((val & RTSC_SUSP))
 +              clocksource_tsc.flags &= ~CLOCK_SOURCE_MUST_VERIFY;
 +}
 +#else
 +static inline void check_geode_tsc_reliable(void) { }
 +#endif
 +
 +
 +void __init tsc_init(void)
 +{
 +      if (!cpu_has_tsc || tsc_disable)
 +              goto out_no_tsc;
 +
 +      cpu_khz = calculate_cpu_khz();
 +      tsc_khz = cpu_khz;
 +
 +      if (!cpu_khz)
 +              goto out_no_tsc;
 +
 +      printk("Detected %lu.%03lu MHz processor.\n",
 +                              (unsigned long)cpu_khz / 1000,
 +                              (unsigned long)cpu_khz % 1000);
 +
 +      set_cyc2ns_scale(cpu_khz);
 +      use_tsc_delay();
 +
 +      /* Check and install the TSC clocksource */
 +      dmi_check_system(bad_tsc_dmi_table);
 +
 +      unsynchronized_tsc();
 +      check_geode_tsc_reliable();
 +      current_tsc_khz = tsc_khz;
 +      clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,
 +                                                      clocksource_tsc.shift);
 +      /* lower the rating if we already know its unstable: */
 +      if (check_tsc_unstable()) {
 +              clocksource_tsc.rating = 0;
 +              clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS;
 +      } else
 +              tsc_enabled = 1;
 +
 +      clocksource_register(&clocksource_tsc);
 +
 +      return;
 +
 +out_no_tsc:
 +      /*
 +       * Set the tsc_disable flag if there's no TSC support, this
 +       * makes it a fast flag for the kernel to see whether it
 +       * should be using the TSC.
 +       */
 +      tsc_disable = 1;
 +}
index 58a477b,0000000..292a225
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,57 @@@
- static int hp_ht_bigsmp(struct dmi_system_id *d)
 +/* 
 + * APIC driver for "bigsmp" XAPIC machines with more than 8 virtual CPUs.
 + * Drives the local APIC in "clustered mode".
 + */
 +#define APIC_DEFINITION 1
 +#include <linux/threads.h>
 +#include <linux/cpumask.h>
 +#include <asm/smp.h>
 +#include <asm/mpspec.h>
 +#include <asm/genapic.h>
 +#include <asm/fixmap.h>
 +#include <asm/apicdef.h>
 +#include <linux/kernel.h>
 +#include <linux/smp.h>
 +#include <linux/init.h>
 +#include <linux/dmi.h>
 +#include <asm/mach-bigsmp/mach_apic.h>
 +#include <asm/mach-bigsmp/mach_apicdef.h>
 +#include <asm/mach-bigsmp/mach_ipi.h>
 +#include <asm/mach-default/mach_mpparse.h>
 +
 +static int dmi_bigsmp; /* can be set by dmi scanners */
 +
- static struct dmi_system_id bigsmp_dmi_table[] = {
++static int hp_ht_bigsmp(const struct dmi_system_id *d)
 +{
 +#ifdef CONFIG_X86_GENERICARCH
 +      printk(KERN_NOTICE "%s detected: force use of apic=bigsmp\n", d->ident);
 +      dmi_bigsmp = 1;
 +#endif
 +      return 0;
 +}
 +
 +
++static const struct dmi_system_id bigsmp_dmi_table[] = {
 +      { hp_ht_bigsmp, "HP ProLiant DL760 G2", {
 +              DMI_MATCH(DMI_BIOS_VENDOR, "HP"),
 +              DMI_MATCH(DMI_BIOS_VERSION, "P44-"),
 +      }},
 +
 +      { hp_ht_bigsmp, "HP ProLiant DL740", {
 +              DMI_MATCH(DMI_BIOS_VENDOR, "HP"),
 +              DMI_MATCH(DMI_BIOS_VERSION, "P47-"),
 +       }},
 +       { }
 +};
 +
 +
 +static int probe_bigsmp(void)
 +{ 
 +      if (def_to_bigsmp)
 +              dmi_bigsmp = 1;
 +      else
 +              dmi_check_system(bigsmp_dmi_table);
 +      return dmi_bigsmp; 
 +} 
 +
 +struct genapic apic_bigsmp = APIC_INIT("bigsmp", probe_bigsmp); 
index ebc6f3c,0000000..07d5223
mode 100644,000000..100644
--- /dev/null
@@@ -1,480 -1,0 +1,480 @@@
- static int __devinit set_bf_sort(struct dmi_system_id *d)
 +/*
 + *    Low-Level PCI Support for PC
 + *
 + *    (c) 1999--2000 Martin Mares <mj@ucw.cz>
 + */
 +
 +#include <linux/sched.h>
 +#include <linux/pci.h>
 +#include <linux/ioport.h>
 +#include <linux/init.h>
 +#include <linux/dmi.h>
 +
 +#include <asm/acpi.h>
 +#include <asm/segment.h>
 +#include <asm/io.h>
 +#include <asm/smp.h>
 +
 +#include "pci.h"
 +
 +unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2 |
 +                              PCI_PROBE_MMCONF;
 +
 +static int pci_bf_sort;
 +int pci_routeirq;
 +int pcibios_last_bus = -1;
 +unsigned long pirq_table_addr;
 +struct pci_bus *pci_root_bus;
 +struct pci_raw_ops *raw_pci_ops;
 +
 +static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value)
 +{
 +      return raw_pci_ops->read(0, bus->number, devfn, where, size, value);
 +}
 +
 +static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value)
 +{
 +      return raw_pci_ops->write(0, bus->number, devfn, where, size, value);
 +}
 +
 +struct pci_ops pci_root_ops = {
 +      .read = pci_read,
 +      .write = pci_write,
 +};
 +
 +/*
 + * legacy, numa, and acpi all want to call pcibios_scan_root
 + * from their initcalls. This flag prevents that.
 + */
 +int pcibios_scanned;
 +
 +/*
 + * This interrupt-safe spinlock protects all accesses to PCI
 + * configuration space.
 + */
 +DEFINE_SPINLOCK(pci_config_lock);
 +
 +/*
 + * Several buggy motherboards address only 16 devices and mirror
 + * them to next 16 IDs. We try to detect this `feature' on all
 + * primary buses (those containing host bridges as they are
 + * expected to be unique) and remove the ghost devices.
 + */
 +
 +static void __devinit pcibios_fixup_ghosts(struct pci_bus *b)
 +{
 +      struct list_head *ln, *mn;
 +      struct pci_dev *d, *e;
 +      int mirror = PCI_DEVFN(16,0);
 +      int seen_host_bridge = 0;
 +      int i;
 +
 +      DBG("PCI: Scanning for ghost devices on bus %d\n", b->number);
 +      list_for_each(ln, &b->devices) {
 +              d = pci_dev_b(ln);
 +              if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST)
 +                      seen_host_bridge++;
 +              for (mn=ln->next; mn != &b->devices; mn=mn->next) {
 +                      e = pci_dev_b(mn);
 +                      if (e->devfn != d->devfn + mirror ||
 +                          e->vendor != d->vendor ||
 +                          e->device != d->device ||
 +                          e->class != d->class)
 +                              continue;
 +                      for(i=0; i<PCI_NUM_RESOURCES; i++)
 +                              if (e->resource[i].start != d->resource[i].start ||
 +                                  e->resource[i].end != d->resource[i].end ||
 +                                  e->resource[i].flags != d->resource[i].flags)
 +                                      continue;
 +                      break;
 +              }
 +              if (mn == &b->devices)
 +                      return;
 +      }
 +      if (!seen_host_bridge)
 +              return;
 +      printk(KERN_WARNING "PCI: Ignoring ghost devices on bus %02x\n", b->number);
 +
 +      ln = &b->devices;
 +      while (ln->next != &b->devices) {
 +              d = pci_dev_b(ln->next);
 +              if (d->devfn >= mirror) {
 +                      list_del(&d->global_list);
 +                      list_del(&d->bus_list);
 +                      kfree(d);
 +              } else
 +                      ln = ln->next;
 +      }
 +}
 +
 +/*
 + *  Called after each bus is probed, but before its children
 + *  are examined.
 + */
 +
 +void __devinit  pcibios_fixup_bus(struct pci_bus *b)
 +{
 +      pcibios_fixup_ghosts(b);
 +      pci_read_bridge_bases(b);
 +}
 +
 +/*
 + * Only use DMI information to set this if nothing was passed
 + * on the kernel command line (which was parsed earlier).
 + */
 +
- static int __devinit assign_all_busses(struct dmi_system_id *d)
++static int __devinit set_bf_sort(const struct dmi_system_id *d)
 +{
 +      if (pci_bf_sort == pci_bf_sort_default) {
 +              pci_bf_sort = pci_dmi_bf;
 +              printk(KERN_INFO "PCI: %s detected, enabling pci=bfsort.\n", d->ident);
 +      }
 +      return 0;
 +}
 +
 +/*
 + * Enable renumbering of PCI bus# ranges to reach all PCI busses (Cardbus)
 + */
 +#ifdef __i386__
++static int __devinit assign_all_busses(const struct dmi_system_id *d)
 +{
 +      pci_probe |= PCI_ASSIGN_ALL_BUSSES;
 +      printk(KERN_INFO "%s detected: enabling PCI bus# renumbering"
 +                      " (pci=assign-busses)\n", d->ident);
 +      return 0;
 +}
 +#endif
 +
 +static struct dmi_system_id __devinitdata pciprobe_dmi_table[] = {
 +#ifdef __i386__
 +/*
 + * Laptops which need pci=assign-busses to see Cardbus cards
 + */
 +      {
 +              .callback = assign_all_busses,
 +              .ident = "Samsung X20 Laptop",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Samsung Electronics"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "SX20S"),
 +              },
 +      },
 +#endif                /* __i386__ */
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "Dell PowerEdge 1950",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1950"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "Dell PowerEdge 1955",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1955"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "Dell PowerEdge 2900",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2900"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "Dell PowerEdge 2950",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2950"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "Dell PowerEdge R900",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge R900"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "HP ProLiant BL20p G3",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "HP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL20p G3"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "HP ProLiant BL20p G4",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "HP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL20p G4"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "HP ProLiant BL30p G1",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "HP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL30p G1"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "HP ProLiant BL25p G1",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "HP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL25p G1"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "HP ProLiant BL35p G1",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "HP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL35p G1"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "HP ProLiant BL45p G1",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "HP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL45p G1"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "HP ProLiant BL45p G2",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "HP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL45p G2"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "HP ProLiant BL460c G1",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "HP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL460c G1"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "HP ProLiant BL465c G1",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "HP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL465c G1"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "HP ProLiant BL480c G1",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "HP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL480c G1"),
 +              },
 +      },
 +      {
 +              .callback = set_bf_sort,
 +              .ident = "HP ProLiant BL685c G1",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "HP"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL685c G1"),
 +              },
 +      },
 +      {}
 +};
 +
 +struct pci_bus * __devinit pcibios_scan_root(int busnum)
 +{
 +      struct pci_bus *bus = NULL;
 +      struct pci_sysdata *sd;
 +
 +      dmi_check_system(pciprobe_dmi_table);
 +
 +      while ((bus = pci_find_next_bus(bus)) != NULL) {
 +              if (bus->number == busnum) {
 +                      /* Already scanned */
 +                      return bus;
 +              }
 +      }
 +
 +      /* Allocate per-root-bus (not per bus) arch-specific data.
 +       * TODO: leak; this memory is never freed.
 +       * It's arguable whether it's worth the trouble to care.
 +       */
 +      sd = kzalloc(sizeof(*sd), GFP_KERNEL);
 +      if (!sd) {
 +              printk(KERN_ERR "PCI: OOM, not probing PCI bus %02x\n", busnum);
 +              return NULL;
 +      }
 +
 +      printk(KERN_DEBUG "PCI: Probing PCI hardware (bus %02x)\n", busnum);
 +
 +      return pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd);
 +}
 +
 +extern u8 pci_cache_line_size;
 +
 +static int __init pcibios_init(void)
 +{
 +      struct cpuinfo_x86 *c = &boot_cpu_data;
 +
 +      if (!raw_pci_ops) {
 +              printk(KERN_WARNING "PCI: System does not support PCI\n");
 +              return 0;
 +      }
 +
 +      /*
 +       * Assume PCI cacheline size of 32 bytes for all x86s except K7/K8
 +       * and P4. It's also good for 386/486s (which actually have 16)
 +       * as quite a few PCI devices do not support smaller values.
 +       */
 +      pci_cache_line_size = 32 >> 2;
 +      if (c->x86 >= 6 && c->x86_vendor == X86_VENDOR_AMD)
 +              pci_cache_line_size = 64 >> 2;  /* K7 & K8 */
 +      else if (c->x86 > 6 && c->x86_vendor == X86_VENDOR_INTEL)
 +              pci_cache_line_size = 128 >> 2; /* P4 */
 +
 +      pcibios_resource_survey();
 +
 +      if (pci_bf_sort >= pci_force_bf)
 +              pci_sort_breadthfirst();
 +#ifdef CONFIG_PCI_BIOS
 +      if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT))
 +              pcibios_sort();
 +#endif
 +      return 0;
 +}
 +
 +subsys_initcall(pcibios_init);
 +
 +char * __devinit  pcibios_setup(char *str)
 +{
 +      if (!strcmp(str, "off")) {
 +              pci_probe = 0;
 +              return NULL;
 +      } else if (!strcmp(str, "bfsort")) {
 +              pci_bf_sort = pci_force_bf;
 +              return NULL;
 +      } else if (!strcmp(str, "nobfsort")) {
 +              pci_bf_sort = pci_force_nobf;
 +              return NULL;
 +      }
 +#ifdef CONFIG_PCI_BIOS
 +      else if (!strcmp(str, "bios")) {
 +              pci_probe = PCI_PROBE_BIOS;
 +              return NULL;
 +      } else if (!strcmp(str, "nobios")) {
 +              pci_probe &= ~PCI_PROBE_BIOS;
 +              return NULL;
 +      } else if (!strcmp(str, "nosort")) {
 +              pci_probe |= PCI_NO_SORT;
 +              return NULL;
 +      } else if (!strcmp(str, "biosirq")) {
 +              pci_probe |= PCI_BIOS_IRQ_SCAN;
 +              return NULL;
 +      } else if (!strncmp(str, "pirqaddr=", 9)) {
 +              pirq_table_addr = simple_strtoul(str+9, NULL, 0);
 +              return NULL;
 +      }
 +#endif
 +#ifdef CONFIG_PCI_DIRECT
 +      else if (!strcmp(str, "conf1")) {
 +              pci_probe = PCI_PROBE_CONF1 | PCI_NO_CHECKS;
 +              return NULL;
 +      }
 +      else if (!strcmp(str, "conf2")) {
 +              pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS;
 +              return NULL;
 +      }
 +#endif
 +#ifdef CONFIG_PCI_MMCONFIG
 +      else if (!strcmp(str, "nommconf")) {
 +              pci_probe &= ~PCI_PROBE_MMCONF;
 +              return NULL;
 +      }
 +#endif
 +      else if (!strcmp(str, "noacpi")) {
 +              acpi_noirq_set();
 +              return NULL;
 +      }
 +      else if (!strcmp(str, "noearly")) {
 +              pci_probe |= PCI_PROBE_NOEARLY;
 +              return NULL;
 +      }
 +#ifndef CONFIG_X86_VISWS
 +      else if (!strcmp(str, "usepirqmask")) {
 +              pci_probe |= PCI_USE_PIRQ_MASK;
 +              return NULL;
 +      } else if (!strncmp(str, "irqmask=", 8)) {
 +              pcibios_irq_mask = simple_strtol(str+8, NULL, 0);
 +              return NULL;
 +      } else if (!strncmp(str, "lastbus=", 8)) {
 +              pcibios_last_bus = simple_strtol(str+8, NULL, 0);
 +              return NULL;
 +      }
 +#endif
 +      else if (!strcmp(str, "rom")) {
 +              pci_probe |= PCI_ASSIGN_ROMS;
 +              return NULL;
 +      } else if (!strcmp(str, "assign-busses")) {
 +              pci_probe |= PCI_ASSIGN_ALL_BUSSES;
 +              return NULL;
 +      } else if (!strcmp(str, "routeirq")) {
 +              pci_routeirq = 1;
 +              return NULL;
 +      }
 +      return str;
 +}
 +
 +unsigned int pcibios_assign_all_busses(void)
 +{
 +      return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0;
 +}
 +
 +int pcibios_enable_device(struct pci_dev *dev, int mask)
 +{
 +      int err;
 +
 +      if ((err = pcibios_enable_resources(dev, mask)) < 0)
 +              return err;
 +
 +      if (!dev->msi_enabled)
 +              return pcibios_enable_irq(dev);
 +      return 0;
 +}
 +
 +void pcibios_disable_device (struct pci_dev *dev)
 +{
 +      if (!dev->msi_enabled && pcibios_disable_irq)
 +              pcibios_disable_irq(dev);
 +}
 +
 +struct pci_bus *pci_scan_bus_with_sysdata(int busno)
 +{
 +      struct pci_bus *bus = NULL;
 +      struct pci_sysdata *sd;
 +
 +      /*
 +       * Allocate per-root-bus (not per bus) arch-specific data.
 +       * TODO: leak; this memory is never freed.
 +       * It's arguable whether it's worth the trouble to care.
 +       */
 +      sd = kzalloc(sizeof(*sd), GFP_KERNEL);
 +      if (!sd) {
 +              printk(KERN_ERR "PCI: OOM, skipping PCI bus %02x\n", busno);
 +              return NULL;
 +      }
 +      sd->node = -1;
 +      bus = pci_scan_bus(busno, &pci_root_ops, sd);
 +      if (!bus)
 +              kfree(sd);
 +
 +      return bus;
 +}
index 8434f23,0000000..d98c6b0
mode 100644,000000..100644
--- /dev/null
@@@ -1,1173 -1,0 +1,1173 @@@
- static int __init fix_broken_hp_bios_irq9(struct dmi_system_id *d)
 +/*
 + *    Low-Level PCI Support for PC -- Routing of Interrupts
 + *
 + *    (c) 1999--2000 Martin Mares <mj@ucw.cz>
 + */
 +
 +#include <linux/types.h>
 +#include <linux/kernel.h>
 +#include <linux/pci.h>
 +#include <linux/init.h>
 +#include <linux/slab.h>
 +#include <linux/interrupt.h>
 +#include <linux/dmi.h>
 +#include <asm/io.h>
 +#include <asm/smp.h>
 +#include <asm/io_apic.h>
 +#include <linux/irq.h>
 +#include <linux/acpi.h>
 +
 +#include "pci.h"
 +
 +#define PIRQ_SIGNATURE        (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
 +#define PIRQ_VERSION 0x0100
 +
 +static int broken_hp_bios_irq9;
 +static int acer_tm360_irqrouting;
 +
 +static struct irq_routing_table *pirq_table;
 +
 +static int pirq_enable_irq(struct pci_dev *dev);
 +
 +/*
 + * Never use: 0, 1, 2 (timer, keyboard, and cascade)
 + * Avoid using: 13, 14 and 15 (FP error and IDE).
 + * Penalize: 3, 4, 6, 7, 12 (known ISA uses: serial, floppy, parallel and mouse)
 + */
 +unsigned int pcibios_irq_mask = 0xfff8;
 +
 +static int pirq_penalty[16] = {
 +      1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000,
 +      0, 0, 0, 0, 1000, 100000, 100000, 100000
 +};
 +
 +struct irq_router {
 +      char *name;
 +      u16 vendor, device;
 +      int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq);
 +      int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new);
 +};
 +
 +struct irq_router_handler {
 +      u16 vendor;
 +      int (*probe)(struct irq_router *r, struct pci_dev *router, u16 device);
 +};
 +
 +int (*pcibios_enable_irq)(struct pci_dev *dev) = NULL;
 +void (*pcibios_disable_irq)(struct pci_dev *dev) = NULL;
 +
 +/*
 + *  Check passed address for the PCI IRQ Routing Table signature
 + *  and perform checksum verification.
 + */
 +
 +static inline struct irq_routing_table * pirq_check_routing_table(u8 *addr)
 +{
 +      struct irq_routing_table *rt;
 +      int i;
 +      u8 sum;
 +
 +      rt = (struct irq_routing_table *) addr;
 +      if (rt->signature != PIRQ_SIGNATURE ||
 +          rt->version != PIRQ_VERSION ||
 +          rt->size % 16 ||
 +          rt->size < sizeof(struct irq_routing_table))
 +              return NULL;
 +      sum = 0;
 +      for (i=0; i < rt->size; i++)
 +              sum += addr[i];
 +      if (!sum) {
 +              DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n", rt);
 +              return rt;
 +      }
 +      return NULL;
 +}
 +
 +
 +
 +/*
 + *  Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table.
 + */
 +
 +static struct irq_routing_table * __init pirq_find_routing_table(void)
 +{
 +      u8 *addr;
 +      struct irq_routing_table *rt;
 +
 +      if (pirq_table_addr) {
 +              rt = pirq_check_routing_table((u8 *) __va(pirq_table_addr));
 +              if (rt)
 +                      return rt;
 +              printk(KERN_WARNING "PCI: PIRQ table NOT found at pirqaddr\n");
 +      }
 +      for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) {
 +              rt = pirq_check_routing_table(addr);
 +              if (rt)
 +                      return rt;
 +      }
 +      return NULL;
 +}
 +
 +/*
 + *  If we have a IRQ routing table, use it to search for peer host
 + *  bridges.  It's a gross hack, but since there are no other known
 + *  ways how to get a list of buses, we have to go this way.
 + */
 +
 +static void __init pirq_peer_trick(void)
 +{
 +      struct irq_routing_table *rt = pirq_table;
 +      u8 busmap[256];
 +      int i;
 +      struct irq_info *e;
 +
 +      memset(busmap, 0, sizeof(busmap));
 +      for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) {
 +              e = &rt->slots[i];
 +#ifdef DEBUG
 +              {
 +                      int j;
 +                      DBG(KERN_DEBUG "%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot);
 +                      for(j=0; j<4; j++)
 +                              DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap);
 +                      DBG("\n");
 +              }
 +#endif
 +              busmap[e->bus] = 1;
 +      }
 +      for(i = 1; i < 256; i++) {
 +              if (!busmap[i] || pci_find_bus(0, i))
 +                      continue;
 +              if (pci_scan_bus_with_sysdata(i))
 +                      printk(KERN_INFO "PCI: Discovered primary peer "
 +                             "bus %02x [IRQ]\n", i);
 +      }
 +      pcibios_last_bus = -1;
 +}
 +
 +/*
 + *  Code for querying and setting of IRQ routes on various interrupt routers.
 + */
 +
 +void eisa_set_level_irq(unsigned int irq)
 +{
 +      unsigned char mask = 1 << (irq & 7);
 +      unsigned int port = 0x4d0 + (irq >> 3);
 +      unsigned char val;
 +      static u16 eisa_irq_mask;
 +
 +      if (irq >= 16 || (1 << irq) & eisa_irq_mask)
 +              return;
 +
 +      eisa_irq_mask |= (1 << irq);
 +      printk(KERN_DEBUG "PCI: setting IRQ %u as level-triggered\n", irq);
 +      val = inb(port);
 +      if (!(val & mask)) {
 +              DBG(KERN_DEBUG " -> edge");
 +              outb(val | mask, port);
 +      }
 +}
 +
 +/*
 + * Common IRQ routing practice: nybbles in config space,
 + * offset by some magic constant.
 + */
 +static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr)
 +{
 +      u8 x;
 +      unsigned reg = offset + (nr >> 1);
 +
 +      pci_read_config_byte(router, reg, &x);
 +      return (nr & 1) ? (x >> 4) : (x & 0xf);
 +}
 +
 +static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val)
 +{
 +      u8 x;
 +      unsigned reg = offset + (nr >> 1);
 +
 +      pci_read_config_byte(router, reg, &x);
 +      x = (nr & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val);
 +      pci_write_config_byte(router, reg, x);
 +}
 +
 +/*
 + * ALI pirq entries are damn ugly, and completely undocumented.
 + * This has been figured out from pirq tables, and it's not a pretty
 + * picture.
 + */
 +static int pirq_ali_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 +{
 +      static const unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
 +
 +      return irqmap[read_config_nybble(router, 0x48, pirq-1)];
 +}
 +
 +static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      static const unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
 +      unsigned int val = irqmap[irq];
 +              
 +      if (val) {
 +              write_config_nybble(router, 0x48, pirq-1, val);
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +/*
 + * The Intel PIIX4 pirq rules are fairly simple: "pirq" is
 + * just a pointer to the config space.
 + */
 +static int pirq_piix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 +{
 +      u8 x;
 +
 +      pci_read_config_byte(router, pirq, &x);
 +      return (x < 16) ? x : 0;
 +}
 +
 +static int pirq_piix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      pci_write_config_byte(router, pirq, irq);
 +      return 1;
 +}
 +
 +/*
 + * The VIA pirq rules are nibble-based, like ALI,
 + * but without the ugly irq number munging.
 + * However, PIRQD is in the upper instead of lower 4 bits.
 + */
 +static int pirq_via_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 +{
 +      return read_config_nybble(router, 0x55, pirq == 4 ? 5 : pirq);
 +}
 +
 +static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      write_config_nybble(router, 0x55, pirq == 4 ? 5 : pirq, irq);
 +      return 1;
 +}
 +
 +/*
 + * The VIA pirq rules are nibble-based, like ALI,
 + * but without the ugly irq number munging.
 + * However, for 82C586, nibble map is different .
 + */
 +static int pirq_via586_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 +{
 +      static const unsigned int pirqmap[5] = { 3, 2, 5, 1, 1 };
 +      return read_config_nybble(router, 0x55, pirqmap[pirq-1]);
 +}
 +
 +static int pirq_via586_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      static const unsigned int pirqmap[5] = { 3, 2, 5, 1, 1 };
 +      write_config_nybble(router, 0x55, pirqmap[pirq-1], irq);
 +      return 1;
 +}
 +
 +/*
 + * ITE 8330G pirq rules are nibble-based
 + * FIXME: pirqmap may be { 1, 0, 3, 2 },
 + *      2+3 are both mapped to irq 9 on my system
 + */
 +static int pirq_ite_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 +{
 +      static const unsigned char pirqmap[4] = { 1, 0, 2, 3 };
 +      return read_config_nybble(router,0x43, pirqmap[pirq-1]);
 +}
 +
 +static int pirq_ite_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      static const unsigned char pirqmap[4] = { 1, 0, 2, 3 };
 +      write_config_nybble(router, 0x43, pirqmap[pirq-1], irq);
 +      return 1;
 +}
 +
 +/*
 + * OPTI: high four bits are nibble pointer..
 + * I wonder what the low bits do?
 + */
 +static int pirq_opti_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 +{
 +      return read_config_nybble(router, 0xb8, pirq >> 4);
 +}
 +
 +static int pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      write_config_nybble(router, 0xb8, pirq >> 4, irq);
 +      return 1;
 +}
 +
 +/*
 + * Cyrix: nibble offset 0x5C
 + * 0x5C bits 7:4 is INTB bits 3:0 is INTA 
 + * 0x5D bits 7:4 is INTD bits 3:0 is INTC
 + */
 +static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 +{
 +      return read_config_nybble(router, 0x5C, (pirq-1)^1);
 +}
 +
 +static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      write_config_nybble(router, 0x5C, (pirq-1)^1, irq);
 +      return 1;
 +}
 +
 +/*
 + *    PIRQ routing for SiS 85C503 router used in several SiS chipsets.
 + *    We have to deal with the following issues here:
 + *    - vendors have different ideas about the meaning of link values
 + *    - some onboard devices (integrated in the chipset) have special
 + *      links and are thus routed differently (i.e. not via PCI INTA-INTD)
 + *    - different revision of the router have a different layout for
 + *      the routing registers, particularly for the onchip devices
 + *
 + *    For all routing registers the common thing is we have one byte
 + *    per routeable link which is defined as:
 + *             bit 7      IRQ mapping enabled (0) or disabled (1)
 + *             bits [6:4] reserved (sometimes used for onchip devices)
 + *             bits [3:0] IRQ to map to
 + *                 allowed: 3-7, 9-12, 14-15
 + *                 reserved: 0, 1, 2, 8, 13
 + *
 + *    The config-space registers located at 0x41/0x42/0x43/0x44 are
 + *    always used to route the normal PCI INT A/B/C/D respectively.
 + *    Apparently there are systems implementing PCI routing table using
 + *    link values 0x01-0x04 and others using 0x41-0x44 for PCI INTA..D.
 + *    We try our best to handle both link mappings.
 + *    
 + *    Currently (2003-05-21) it appears most SiS chipsets follow the
 + *    definition of routing registers from the SiS-5595 southbridge.
 + *    According to the SiS 5595 datasheets the revision id's of the
 + *    router (ISA-bridge) should be 0x01 or 0xb0.
 + *
 + *    Furthermore we've also seen lspci dumps with revision 0x00 and 0xb1.
 + *    Looks like these are used in a number of SiS 5xx/6xx/7xx chipsets.
 + *    They seem to work with the current routing code. However there is
 + *    some concern because of the two USB-OHCI HCs (original SiS 5595
 + *    had only one). YMMV.
 + *
 + *    Onchip routing for router rev-id 0x01/0xb0 and probably 0x00/0xb1:
 + *
 + *    0x61:   IDEIRQ:
 + *            bits [6:5] must be written 01
 + *            bit 4 channel-select primary (0), secondary (1)
 + *
 + *    0x62:   USBIRQ:
 + *            bit 6 OHCI function disabled (0), enabled (1)
 + *    
 + *    0x6a:   ACPI/SCI IRQ: bits 4-6 reserved
 + *
 + *    0x7e:   Data Acq. Module IRQ - bits 4-6 reserved
 + *
 + *    We support USBIRQ (in addition to INTA-INTD) and keep the
 + *    IDE, ACPI and DAQ routing untouched as set by the BIOS.
 + *
 + *    Currently the only reported exception is the new SiS 65x chipset
 + *    which includes the SiS 69x southbridge. Here we have the 85C503
 + *    router revision 0x04 and there are changes in the register layout
 + *    mostly related to the different USB HCs with USB 2.0 support.
 + *
 + *    Onchip routing for router rev-id 0x04 (try-and-error observation)
 + *
 + *    0x60/0x61/0x62/0x63:    1xEHCI and 3xOHCI (companion) USB-HCs
 + *                            bit 6-4 are probably unused, not like 5595
 + */
 +
 +#define PIRQ_SIS_IRQ_MASK     0x0f
 +#define PIRQ_SIS_IRQ_DISABLE  0x80
 +#define PIRQ_SIS_USB_ENABLE   0x40
 +
 +static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 +{
 +      u8 x;
 +      int reg;
 +
 +      reg = pirq;
 +      if (reg >= 0x01 && reg <= 0x04)
 +              reg += 0x40;
 +      pci_read_config_byte(router, reg, &x);
 +      return (x & PIRQ_SIS_IRQ_DISABLE) ? 0 : (x & PIRQ_SIS_IRQ_MASK);
 +}
 +
 +static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      u8 x;
 +      int reg;
 +
 +      reg = pirq;
 +      if (reg >= 0x01 && reg <= 0x04)
 +              reg += 0x40;
 +      pci_read_config_byte(router, reg, &x);
 +      x &= ~(PIRQ_SIS_IRQ_MASK | PIRQ_SIS_IRQ_DISABLE);
 +      x |= irq ? irq: PIRQ_SIS_IRQ_DISABLE;
 +      pci_write_config_byte(router, reg, x);
 +      return 1;
 +}
 +
 +
 +/*
 + * VLSI: nibble offset 0x74 - educated guess due to routing table and
 + *       config space of VLSI 82C534 PCI-bridge/router (1004:0102)
 + *       Tested on HP OmniBook 800 covering PIRQ 1, 2, 4, 8 for onboard
 + *       devices, PIRQ 3 for non-pci(!) soundchip and (untested) PIRQ 6
 + *       for the busbridge to the docking station.
 + */
 +
 +static int pirq_vlsi_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 +{
 +      if (pirq > 8) {
 +              printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq);
 +              return 0;
 +      }
 +      return read_config_nybble(router, 0x74, pirq-1);
 +}
 +
 +static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      if (pirq > 8) {
 +              printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq);
 +              return 0;
 +      }
 +      write_config_nybble(router, 0x74, pirq-1, irq);
 +      return 1;
 +}
 +
 +/*
 + * ServerWorks: PCI interrupts mapped to system IRQ lines through Index
 + * and Redirect I/O registers (0x0c00 and 0x0c01).  The Index register
 + * format is (PCIIRQ## | 0x10), e.g.: PCIIRQ10=0x1a.  The Redirect
 + * register is a straight binary coding of desired PIC IRQ (low nibble).
 + *
 + * The 'link' value in the PIRQ table is already in the correct format
 + * for the Index register.  There are some special index values:
 + * 0x00 for ACPI (SCI), 0x01 for USB, 0x02 for IDE0, 0x04 for IDE1,
 + * and 0x03 for SMBus.
 + */
 +static int pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 +{
 +      outb_p(pirq, 0xc00);
 +      return inb(0xc01) & 0xf;
 +}
 +
 +static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      outb_p(pirq, 0xc00);
 +      outb_p(irq, 0xc01);
 +      return 1;
 +}
 +
 +/* Support for AMD756 PCI IRQ Routing
 + * Jhon H. Caicedo <jhcaiced@osso.org.co>
 + * Jun/21/2001 0.2.0 Release, fixed to use "nybble" functions... (jhcaiced)
 + * Jun/19/2001 Alpha Release 0.1.0 (jhcaiced)
 + * The AMD756 pirq rules are nibble-based
 + * offset 0x56 0-3 PIRQA  4-7  PIRQB
 + * offset 0x57 0-3 PIRQC  4-7  PIRQD
 + */
 +static int pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 +{
 +      u8 irq;
 +      irq = 0;
 +      if (pirq <= 4)
 +      {
 +              irq = read_config_nybble(router, 0x56, pirq - 1);
 +      }
 +      printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n",
 +              dev->vendor, dev->device, pirq, irq);
 +      return irq;
 +}
 +
 +static int pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n", 
 +              dev->vendor, dev->device, pirq, irq);
 +      if (pirq <= 4)
 +      {
 +              write_config_nybble(router, 0x56, pirq - 1, irq);
 +      }
 +      return 1;
 +}
 +
 +#ifdef CONFIG_PCI_BIOS
 +
 +static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 +{
 +      struct pci_dev *bridge;
 +      int pin = pci_get_interrupt_pin(dev, &bridge);
 +      return pcibios_set_irq_routing(bridge, pin, irq);
 +}
 +
 +#endif
 +
 +static __init int intel_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
 +{
 +      static struct pci_device_id __initdata pirq_440gx[] = {
 +              { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_0) },
 +              { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_2) },
 +              { },
 +      };
 +
 +      /* 440GX has a proprietary PIRQ router -- don't use it */
 +      if (pci_dev_present(pirq_440gx))
 +              return 0;
 +
 +      switch(device)
 +      {
 +              case PCI_DEVICE_ID_INTEL_82371FB_0:
 +              case PCI_DEVICE_ID_INTEL_82371SB_0:
 +              case PCI_DEVICE_ID_INTEL_82371AB_0:
 +              case PCI_DEVICE_ID_INTEL_82371MX:
 +              case PCI_DEVICE_ID_INTEL_82443MX_0:
 +              case PCI_DEVICE_ID_INTEL_82801AA_0:
 +              case PCI_DEVICE_ID_INTEL_82801AB_0:
 +              case PCI_DEVICE_ID_INTEL_82801BA_0:
 +              case PCI_DEVICE_ID_INTEL_82801BA_10:
 +              case PCI_DEVICE_ID_INTEL_82801CA_0:
 +              case PCI_DEVICE_ID_INTEL_82801CA_12:
 +              case PCI_DEVICE_ID_INTEL_82801DB_0:
 +              case PCI_DEVICE_ID_INTEL_82801E_0:
 +              case PCI_DEVICE_ID_INTEL_82801EB_0:
 +              case PCI_DEVICE_ID_INTEL_ESB_1:
 +              case PCI_DEVICE_ID_INTEL_ICH6_0:
 +              case PCI_DEVICE_ID_INTEL_ICH6_1:
 +              case PCI_DEVICE_ID_INTEL_ICH7_0:
 +              case PCI_DEVICE_ID_INTEL_ICH7_1:
 +              case PCI_DEVICE_ID_INTEL_ICH7_30:
 +              case PCI_DEVICE_ID_INTEL_ICH7_31:
 +              case PCI_DEVICE_ID_INTEL_ESB2_0:
 +              case PCI_DEVICE_ID_INTEL_ICH8_0:
 +              case PCI_DEVICE_ID_INTEL_ICH8_1:
 +              case PCI_DEVICE_ID_INTEL_ICH8_2:
 +              case PCI_DEVICE_ID_INTEL_ICH8_3:
 +              case PCI_DEVICE_ID_INTEL_ICH8_4:
 +              case PCI_DEVICE_ID_INTEL_ICH9_0:
 +              case PCI_DEVICE_ID_INTEL_ICH9_1:
 +              case PCI_DEVICE_ID_INTEL_ICH9_2:
 +              case PCI_DEVICE_ID_INTEL_ICH9_3:
 +              case PCI_DEVICE_ID_INTEL_ICH9_4:
 +              case PCI_DEVICE_ID_INTEL_ICH9_5:
 +              case PCI_DEVICE_ID_INTEL_TOLAPAI_0:
 +                      r->name = "PIIX/ICH";
 +                      r->get = pirq_piix_get;
 +                      r->set = pirq_piix_set;
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +static __init int via_router_probe(struct irq_router *r,
 +                              struct pci_dev *router, u16 device)
 +{
 +      /* FIXME: We should move some of the quirk fixup stuff here */
 +
 +      /*
 +       * work arounds for some buggy BIOSes
 +       */
 +      if (device == PCI_DEVICE_ID_VIA_82C586_0) {
 +              switch(router->device) {
 +              case PCI_DEVICE_ID_VIA_82C686:
 +                      /*
 +                       * Asus k7m bios wrongly reports 82C686A
 +                       * as 586-compatible
 +                       */
 +                      device = PCI_DEVICE_ID_VIA_82C686;
 +                      break;
 +              case PCI_DEVICE_ID_VIA_8235:
 +                      /**
 +                       * Asus a7v-x bios wrongly reports 8235
 +                       * as 586-compatible
 +                       */
 +                      device = PCI_DEVICE_ID_VIA_8235;
 +                      break;
 +              }
 +      }
 +
 +      switch(device) {
 +      case PCI_DEVICE_ID_VIA_82C586_0:
 +              r->name = "VIA";
 +              r->get = pirq_via586_get;
 +              r->set = pirq_via586_set;
 +              return 1;
 +      case PCI_DEVICE_ID_VIA_82C596:
 +      case PCI_DEVICE_ID_VIA_82C686:
 +      case PCI_DEVICE_ID_VIA_8231:
 +      case PCI_DEVICE_ID_VIA_8233A:
 +      case PCI_DEVICE_ID_VIA_8235:
 +      case PCI_DEVICE_ID_VIA_8237:
 +              /* FIXME: add new ones for 8233/5 */
 +              r->name = "VIA";
 +              r->get = pirq_via_get;
 +              r->set = pirq_via_set;
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +static __init int vlsi_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
 +{
 +      switch(device)
 +      {
 +              case PCI_DEVICE_ID_VLSI_82C534:
 +                      r->name = "VLSI 82C534";
 +                      r->get = pirq_vlsi_get;
 +                      r->set = pirq_vlsi_set;
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +static __init int serverworks_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
 +{
 +      switch(device)
 +      {
 +              case PCI_DEVICE_ID_SERVERWORKS_OSB4:
 +              case PCI_DEVICE_ID_SERVERWORKS_CSB5:
 +                      r->name = "ServerWorks";
 +                      r->get = pirq_serverworks_get;
 +                      r->set = pirq_serverworks_set;
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +static __init int sis_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
 +{
 +      if (device != PCI_DEVICE_ID_SI_503)
 +              return 0;
 +              
 +      r->name = "SIS";
 +      r->get = pirq_sis_get;
 +      r->set = pirq_sis_set;
 +      return 1;
 +}
 +
 +static __init int cyrix_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
 +{
 +      switch(device)
 +      {
 +              case PCI_DEVICE_ID_CYRIX_5520:
 +                      r->name = "NatSemi";
 +                      r->get = pirq_cyrix_get;
 +                      r->set = pirq_cyrix_set;
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +static __init int opti_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
 +{
 +      switch(device)
 +      {
 +              case PCI_DEVICE_ID_OPTI_82C700:
 +                      r->name = "OPTI";
 +                      r->get = pirq_opti_get;
 +                      r->set = pirq_opti_set;
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +static __init int ite_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
 +{
 +      switch(device)
 +      {
 +              case PCI_DEVICE_ID_ITE_IT8330G_0:
 +                      r->name = "ITE";
 +                      r->get = pirq_ite_get;
 +                      r->set = pirq_ite_set;
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +static __init int ali_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
 +{
 +      switch(device)
 +      {
 +      case PCI_DEVICE_ID_AL_M1533:
 +      case PCI_DEVICE_ID_AL_M1563:
 +              printk(KERN_DEBUG "PCI: Using ALI IRQ Router\n");
 +              r->name = "ALI";
 +              r->get = pirq_ali_get;
 +              r->set = pirq_ali_set;
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +static __init int amd_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
 +{
 +      switch(device)
 +      {
 +              case PCI_DEVICE_ID_AMD_VIPER_740B:
 +                      r->name = "AMD756";
 +                      break;
 +              case PCI_DEVICE_ID_AMD_VIPER_7413:
 +                      r->name = "AMD766";
 +                      break;
 +              case PCI_DEVICE_ID_AMD_VIPER_7443:
 +                      r->name = "AMD768";
 +                      break;
 +              default:
 +                      return 0;
 +      }
 +      r->get = pirq_amd756_get;
 +      r->set = pirq_amd756_set;
 +      return 1;
 +}
 +              
 +static __initdata struct irq_router_handler pirq_routers[] = {
 +      { PCI_VENDOR_ID_INTEL, intel_router_probe },
 +      { PCI_VENDOR_ID_AL, ali_router_probe },
 +      { PCI_VENDOR_ID_ITE, ite_router_probe },
 +      { PCI_VENDOR_ID_VIA, via_router_probe },
 +      { PCI_VENDOR_ID_OPTI, opti_router_probe },
 +      { PCI_VENDOR_ID_SI, sis_router_probe },
 +      { PCI_VENDOR_ID_CYRIX, cyrix_router_probe },
 +      { PCI_VENDOR_ID_VLSI, vlsi_router_probe },
 +      { PCI_VENDOR_ID_SERVERWORKS, serverworks_router_probe },
 +      { PCI_VENDOR_ID_AMD, amd_router_probe },
 +      /* Someone with docs needs to add the ATI Radeon IGP */
 +      { 0, NULL }
 +};
 +static struct irq_router pirq_router;
 +static struct pci_dev *pirq_router_dev;
 +
 +
 +/*
 + *    FIXME: should we have an option to say "generic for
 + *    chipset" ?
 + */
 + 
 +static void __init pirq_find_router(struct irq_router *r)
 +{
 +      struct irq_routing_table *rt = pirq_table;
 +      struct irq_router_handler *h;
 +
 +#ifdef CONFIG_PCI_BIOS
 +      if (!rt->signature) {
 +              printk(KERN_INFO "PCI: Using BIOS for IRQ routing\n");
 +              r->set = pirq_bios_set;
 +              r->name = "BIOS";
 +              return;
 +      }
 +#endif
 +
 +      /* Default unless a driver reloads it */
 +      r->name = "default";
 +      r->get = NULL;
 +      r->set = NULL;
 +      
 +      DBG(KERN_DEBUG "PCI: Attempting to find IRQ router for %04x:%04x\n",
 +          rt->rtr_vendor, rt->rtr_device);
 +
 +      pirq_router_dev = pci_get_bus_and_slot(rt->rtr_bus, rt->rtr_devfn);
 +      if (!pirq_router_dev) {
 +              DBG(KERN_DEBUG "PCI: Interrupt router not found at "
 +                      "%02x:%02x\n", rt->rtr_bus, rt->rtr_devfn);
 +              return;
 +      }
 +
 +      for( h = pirq_routers; h->vendor; h++) {
 +              /* First look for a router match */
 +              if (rt->rtr_vendor == h->vendor && h->probe(r, pirq_router_dev, rt->rtr_device))
 +                      break;
 +              /* Fall back to a device match */
 +              if (pirq_router_dev->vendor == h->vendor && h->probe(r, pirq_router_dev, pirq_router_dev->device))
 +                      break;
 +      }
 +      printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n",
 +              pirq_router.name,
 +              pirq_router_dev->vendor,
 +              pirq_router_dev->device,
 +              pci_name(pirq_router_dev));
 +
 +      /* The device remains referenced for the kernel lifetime */
 +}
 +
 +static struct irq_info *pirq_get_info(struct pci_dev *dev)
 +{
 +      struct irq_routing_table *rt = pirq_table;
 +      int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
 +      struct irq_info *info;
 +
 +      for (info = rt->slots; entries--; info++)
 +              if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn))
 +                      return info;
 +      return NULL;
 +}
 +
 +static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
 +{
 +      u8 pin;
 +      struct irq_info *info;
 +      int i, pirq, newirq;
 +      int irq = 0;
 +      u32 mask;
 +      struct irq_router *r = &pirq_router;
 +      struct pci_dev *dev2 = NULL;
 +      char *msg = NULL;
 +
 +      /* Find IRQ pin */
 +      pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
 +      if (!pin) {
 +              DBG(KERN_DEBUG " -> no interrupt pin\n");
 +              return 0;
 +      }
 +      pin = pin - 1;
 +
 +      /* Find IRQ routing entry */
 +
 +      if (!pirq_table)
 +              return 0;
 +      
 +      DBG(KERN_DEBUG "IRQ for %s[%c]", pci_name(dev), 'A' + pin);
 +      info = pirq_get_info(dev);
 +      if (!info) {
 +              DBG(" -> not found in routing table\n" KERN_DEBUG);
 +              return 0;
 +      }
 +      pirq = info->irq[pin].link;
 +      mask = info->irq[pin].bitmap;
 +      if (!pirq) {
 +              DBG(" -> not routed\n" KERN_DEBUG);
 +              return 0;
 +      }
 +      DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs);
 +      mask &= pcibios_irq_mask;
 +
 +      /* Work around broken HP Pavilion Notebooks which assign USB to
 +         IRQ 9 even though it is actually wired to IRQ 11 */
 +
 +      if (broken_hp_bios_irq9 && pirq == 0x59 && dev->irq == 9) {
 +              dev->irq = 11;
 +              pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 11);
 +              r->set(pirq_router_dev, dev, pirq, 11);
 +      }
 +
 +      /* same for Acer Travelmate 360, but with CB and irq 11 -> 10 */
 +      if (acer_tm360_irqrouting && dev->irq == 11 && dev->vendor == PCI_VENDOR_ID_O2) {
 +              pirq = 0x68;
 +              mask = 0x400;
 +              dev->irq = r->get(pirq_router_dev, dev, pirq);
 +              pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
 +      }
 +
 +      /*
 +       * Find the best IRQ to assign: use the one
 +       * reported by the device if possible.
 +       */
 +      newirq = dev->irq;
 +      if (newirq && !((1 << newirq) & mask)) {
 +              if ( pci_probe & PCI_USE_PIRQ_MASK) newirq = 0;
 +              else printk("\n" KERN_WARNING
 +                      "PCI: IRQ %i for device %s doesn't match PIRQ mask "
 +                      "- try pci=usepirqmask\n" KERN_DEBUG, newirq,
 +                      pci_name(dev));
 +      }
 +      if (!newirq && assign) {
 +              for (i = 0; i < 16; i++) {
 +                      if (!(mask & (1 << i)))
 +                              continue;
 +                      if (pirq_penalty[i] < pirq_penalty[newirq] && can_request_irq(i, IRQF_SHARED))
 +                              newirq = i;
 +              }
 +      }
 +      DBG(" -> newirq=%d", newirq);
 +
 +      /* Check if it is hardcoded */
 +      if ((pirq & 0xf0) == 0xf0) {
 +              irq = pirq & 0xf;
 +              DBG(" -> hardcoded IRQ %d\n", irq);
 +              msg = "Hardcoded";
 +      } else if ( r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \
 +      ((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask)) ) {
 +              DBG(" -> got IRQ %d\n", irq);
 +              msg = "Found";
 +              eisa_set_level_irq(irq);
 +      } else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
 +              DBG(" -> assigning IRQ %d", newirq);
 +              if (r->set(pirq_router_dev, dev, pirq, newirq)) {
 +                      eisa_set_level_irq(newirq);
 +                      DBG(" ... OK\n");
 +                      msg = "Assigned";
 +                      irq = newirq;
 +              }
 +      }
 +
 +      if (!irq) {
 +              DBG(" ... failed\n");
 +              if (newirq && mask == (1 << newirq)) {
 +                      msg = "Guessed";
 +                      irq = newirq;
 +              } else
 +                      return 0;
 +      }
 +      printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, pci_name(dev));
 +
 +      /* Update IRQ for all devices with the same pirq value */
 +      while ((dev2 = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev2)) != NULL) {
 +              pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin);
 +              if (!pin)
 +                      continue;
 +              pin--;
 +              info = pirq_get_info(dev2);
 +              if (!info)
 +                      continue;
 +              if (info->irq[pin].link == pirq) {
 +                      /* We refuse to override the dev->irq information. Give a warning! */
 +                      if ( dev2->irq && dev2->irq != irq && \
 +                      (!(pci_probe & PCI_USE_PIRQ_MASK) || \
 +                      ((1 << dev2->irq) & mask)) ) {
 +#ifndef CONFIG_PCI_MSI
 +                              printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n",
 +                                     pci_name(dev2), dev2->irq, irq);
 +#endif
 +                              continue;
 +                      }
 +                      dev2->irq = irq;
 +                      pirq_penalty[irq]++;
 +                      if (dev != dev2)
 +                              printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, pci_name(dev2));
 +              }
 +      }
 +      return 1;
 +}
 +
 +static void __init pcibios_fixup_irqs(void)
 +{
 +      struct pci_dev *dev = NULL;
 +      u8 pin;
 +
 +      DBG(KERN_DEBUG "PCI: IRQ fixup\n");
 +      while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
 +              /*
 +               * If the BIOS has set an out of range IRQ number, just ignore it.
 +               * Also keep track of which IRQ's are already in use.
 +               */
 +              if (dev->irq >= 16) {
 +                      DBG(KERN_DEBUG "%s: ignoring bogus IRQ %d\n", pci_name(dev), dev->irq);
 +                      dev->irq = 0;
 +              }
 +              /* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */
 +              if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000)
 +                      pirq_penalty[dev->irq] = 0;
 +              pirq_penalty[dev->irq]++;
 +      }
 +
 +      dev = NULL;
 +      while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
 +              pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
 +#ifdef CONFIG_X86_IO_APIC
 +              /*
 +               * Recalculate IRQ numbers if we use the I/O APIC.
 +               */
 +              if (io_apic_assign_pci_irqs)
 +              {
 +                      int irq;
 +
 +                      if (pin) {
 +                              pin--;          /* interrupt pins are numbered starting from 1 */
 +                              irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
 +      /*
 +       * Busses behind bridges are typically not listed in the MP-table.
 +       * In this case we have to look up the IRQ based on the parent bus,
 +       * parent slot, and pin number. The SMP code detects such bridged
 +       * busses itself so we should get into this branch reliably.
 +       */
 +                              if (irq < 0 && dev->bus->parent) { /* go back to the bridge */
 +                                      struct pci_dev * bridge = dev->bus->self;
 +
 +                                      pin = (pin + PCI_SLOT(dev->devfn)) % 4;
 +                                      irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, 
 +                                                      PCI_SLOT(bridge->devfn), pin);
 +                                      if (irq >= 0)
 +                                              printk(KERN_WARNING "PCI: using PPB %s[%c] to get irq %d\n",
 +                                                      pci_name(bridge), 'A' + pin, irq);
 +                              }
 +                              if (irq >= 0) {
 +                                      printk(KERN_INFO "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n",
 +                                              pci_name(dev), 'A' + pin, irq);
 +                                      dev->irq = irq;
 +                              }
 +                      }
 +              }
 +#endif
 +              /*
 +               * Still no IRQ? Try to lookup one...
 +               */
 +              if (pin && !dev->irq)
 +                      pcibios_lookup_irq(dev, 0);
 +      }
 +}
 +
 +/*
 + * Work around broken HP Pavilion Notebooks which assign USB to
 + * IRQ 9 even though it is actually wired to IRQ 11
 + */
- static int __init fix_acer_tm360_irqrouting(struct dmi_system_id *d)
++static int __init fix_broken_hp_bios_irq9(const struct dmi_system_id *d)
 +{
 +      if (!broken_hp_bios_irq9) {
 +              broken_hp_bios_irq9 = 1;
 +              printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident);
 +      }
 +      return 0;
 +}
 +
 +/*
 + * Work around broken Acer TravelMate 360 Notebooks which assign
 + * Cardbus to IRQ 11 even though it is actually wired to IRQ 10
 + */
++static int __init fix_acer_tm360_irqrouting(const struct dmi_system_id *d)
 +{
 +      if (!acer_tm360_irqrouting) {
 +              acer_tm360_irqrouting = 1;
 +              printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident);
 +      }
 +      return 0;
 +}
 +
 +static struct dmi_system_id __initdata pciirq_dmi_table[] = {
 +      {
 +              .callback = fix_broken_hp_bios_irq9,
 +              .ident = "HP Pavilion N5400 Series Laptop",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
 +                      DMI_MATCH(DMI_BIOS_VERSION, "GE.M1.03"),
 +                      DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook Model GE"),
 +                      DMI_MATCH(DMI_BOARD_VERSION, "OmniBook N32N-736"),
 +              },
 +      },
 +      {
 +              .callback = fix_acer_tm360_irqrouting,
 +              .ident = "Acer TravelMate 36x Laptop",
 +              .matches = {
 +                      DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 +                      DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),
 +              },
 +      },
 +      { }
 +};
 +
 +static int __init pcibios_irq_init(void)
 +{
 +      DBG(KERN_DEBUG "PCI: IRQ init\n");
 +
 +      if (pcibios_enable_irq || raw_pci_ops == NULL)
 +              return 0;
 +
 +      dmi_check_system(pciirq_dmi_table);
 +
 +      pirq_table = pirq_find_routing_table();
 +
 +#ifdef CONFIG_PCI_BIOS
 +      if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN))
 +              pirq_table = pcibios_get_irq_routing_table();
 +#endif
 +      if (pirq_table) {
 +              pirq_peer_trick();
 +              pirq_find_router(&pirq_router);
 +              if (pirq_table->exclusive_irqs) {
 +                      int i;
 +                      for (i=0; i<16; i++)
 +                              if (!(pirq_table->exclusive_irqs & (1 << i)))
 +                                      pirq_penalty[i] += 100;
 +              }
 +              /* If we're using the I/O APIC, avoid using the PCI IRQ routing table */
 +              if (io_apic_assign_pci_irqs)
 +                      pirq_table = NULL;
 +      }
 +
 +      pcibios_enable_irq = pirq_enable_irq;
 +
 +      pcibios_fixup_irqs();
 +      return 0;
 +}
 +
 +subsys_initcall(pcibios_irq_init);
 +
 +
 +static void pirq_penalize_isa_irq(int irq, int active)
 +{
 +      /*
 +       *  If any ISAPnP device reports an IRQ in its list of possible
 +       *  IRQ's, we try to avoid assigning it to PCI devices.
 +       */
 +      if (irq < 16) {
 +              if (active)
 +                      pirq_penalty[irq] += 1000;
 +              else
 +                      pirq_penalty[irq] += 100;
 +      }
 +}
 +
 +void pcibios_penalize_isa_irq(int irq, int active)
 +{
 +#ifdef CONFIG_ACPI
 +      if (!acpi_noirq)
 +              acpi_penalize_isa_irq(irq, active);
 +      else
 +#endif
 +              pirq_penalize_isa_irq(irq, active);
 +}
 +
 +static int pirq_enable_irq(struct pci_dev *dev)
 +{
 +      u8 pin;
 +      struct pci_dev *temp_dev;
 +
 +      pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
 +      if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) {
 +              char *msg = "";
 +
 +              pin--;          /* interrupt pins are numbered starting from 1 */
 +
 +              if (io_apic_assign_pci_irqs) {
 +                      int irq;
 +
 +                      irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
 +                      /*
 +                       * Busses behind bridges are typically not listed in the MP-table.
 +                       * In this case we have to look up the IRQ based on the parent bus,
 +                       * parent slot, and pin number. The SMP code detects such bridged
 +                       * busses itself so we should get into this branch reliably.
 +                       */
 +                      temp_dev = dev;
 +                      while (irq < 0 && dev->bus->parent) { /* go back to the bridge */
 +                              struct pci_dev * bridge = dev->bus->self;
 +
 +                              pin = (pin + PCI_SLOT(dev->devfn)) % 4;
 +                              irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, 
 +                                              PCI_SLOT(bridge->devfn), pin);
 +                              if (irq >= 0)
 +                                      printk(KERN_WARNING "PCI: using PPB %s[%c] to get irq %d\n",
 +                                              pci_name(bridge), 'A' + pin, irq);
 +                              dev = bridge;
 +                      }
 +                      dev = temp_dev;
 +                      if (irq >= 0) {
 +                              printk(KERN_INFO "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n",
 +                                      pci_name(dev), 'A' + pin, irq);
 +                              dev->irq = irq;
 +                              return 0;
 +                      } else
 +                              msg = " Probably buggy MP table.";
 +              } else if (pci_probe & PCI_BIOS_IRQ_SCAN)
 +                      msg = "";
 +              else
 +                      msg = " Please try using pci=biosirq.";
 +
 +              /* With IDE legacy devices the IRQ lookup failure is not a problem.. */
 +              if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && !(dev->class & 0x5))
 +                      return 0;
 +
 +              printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
 +                     'A' + pin, pci_name(dev), msg);
 +      }
 +      return 0;
 +}