]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 17 Jul 2008 00:25:46 +0000 (17:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 17 Jul 2008 00:25:46 +0000 (17:25 -0700)
* 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6: (72 commits)
  Revert "x86/PCI: ACPI based PCI gap calculation"
  PCI: remove unnecessary volatile in PCIe hotplug struct controller
  x86/PCI: ACPI based PCI gap calculation
  PCI: include linux/pm_wakeup.h for device_set_wakeup_capable
  PCI PM: Fix pci_prepare_to_sleep
  x86/PCI: Fix PCI config space for domains > 0
  Fix acpi_pm_device_sleep_wake() by providing a stub for CONFIG_PM_SLEEP=n
  PCI: Simplify PCI device PM code
  PCI PM: Introduce pci_prepare_to_sleep and pci_back_from_sleep
  PCI ACPI: Rework PCI handling of wake-up
  ACPI: Introduce new device wakeup flag 'prepared'
  ACPI: Introduce acpi_device_sleep_wake function
  PCI: rework pci_set_power_state function to call platform first
  PCI: Introduce platform_pci_power_manageable function
  ACPI: Introduce acpi_bus_power_manageable function
  PCI: make pci_name use dev_name
  PCI: handle pci_name() being const
  PCI: add stub for pci_set_consistent_dma_mask()
  PCI: remove unused arch pcibios_update_resource() functions
  PCI: fix pci_setup_device()'s sprinting into a const buffer
  ...

Fixed up conflicts in various files (arch/x86/kernel/setup_64.c,
arch/x86/pci/irq.c, arch/x86/pci/pci.h, drivers/acpi/sleep/main.c,
drivers/pci/pci.c, drivers/pci/pci.h, include/acpi/acpi_bus.h) from x86
and ACPI updates manually.

84 files changed:
Documentation/kernel-parameters.txt
MAINTAINERS
arch/frv/mb93090-mb00/pci-frv.c
arch/m68knommu/kernel/comempci.c
arch/mips/pmc-sierra/yosemite/ht.c
arch/sh/drivers/pci/pci.c
arch/sparc64/kernel/pci.c
arch/x86/kernel/acpi/sleep.c
arch/x86/kernel/apm_32.c
arch/x86/kernel/early-quirks.c
arch/x86/kernel/setup.c
arch/x86/pci/common.c
arch/x86/pci/early.c
arch/x86/pci/irq.c
arch/x86/pci/pci.h
drivers/acpi/Kconfig
drivers/acpi/Makefile
drivers/acpi/bus.c
drivers/acpi/glue.c
drivers/acpi/pci_slot.c [new file with mode: 0644]
drivers/acpi/power.c
drivers/acpi/scan.c
drivers/acpi/sleep/main.c
drivers/acpi/sleep/wakeup.c
drivers/base/platform.c
drivers/base/power/main.c
drivers/base/power/power.h
drivers/base/power/sysfs.c
drivers/base/power/trace.c
drivers/pci/Makefile
drivers/pci/hotplug/acpi_pcihp.c
drivers/pci/hotplug/acpiphp.h
drivers/pci/hotplug/acpiphp_core.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/hotplug/acpiphp_ibm.c
drivers/pci/hotplug/cpci_hotplug_core.c
drivers/pci/hotplug/cpqphp_core.c
drivers/pci/hotplug/fakephp.c
drivers/pci/hotplug/ibmphp_ebda.c
drivers/pci/hotplug/pci_hotplug_core.c
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/hotplug/rpadlpar_sysfs.c
drivers/pci/hotplug/rpaphp_slot.c
drivers/pci/hotplug/sgi_hotplug.c
drivers/pci/hotplug/shpchp.h
drivers/pci/hotplug/shpchp_core.c
drivers/pci/hotplug/shpchp_hpc.c
drivers/pci/intel-iommu.c
drivers/pci/msi.c
drivers/pci/pci-acpi.c
drivers/pci/pci-driver.c
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/pcie/aer/aerdrv.c
drivers/pci/pcie/aer/aerdrv_acpi.c
drivers/pci/pcie/aer/aerdrv_core.c
drivers/pci/pcie/portdrv_bus.c
drivers/pci/pcie/portdrv_core.c
drivers/pci/pcie/portdrv_pci.c
drivers/pci/probe.c
drivers/pci/proc.c
drivers/pci/quirks.c
drivers/pci/setup-bus.c
drivers/pci/setup-irq.c
drivers/pci/setup-res.c
drivers/pci/slot.c [new file with mode: 0644]
include/acpi/acpi_bus.h
include/acpi/acpi_drivers.h
include/asm-generic/vmlinux.lds.h
include/asm-x86/pci-direct.h
include/linux/acpi.h
include/linux/device.h
include/linux/pci.h
include/linux/pci_hotplug.h
include/linux/pci_regs.h
include/linux/platform_device.h
include/linux/pm.h
include/linux/pm_wakeup.h
include/linux/suspend.h
kernel/power/disk.c
kernel/power/main.c
lib/kobject.c

index 5e497d16fb51a036e7b80af6b6151ddbe89603c9..09ad7450647bc81dff32a3eaf7ea3c0858f4a896 100644 (file)
@@ -147,10 +147,14 @@ and is between 256 and 4096 characters. It is defined in the file
                        default: 0
 
        acpi_sleep=     [HW,ACPI] Sleep options
-                       Format: { s3_bios, s3_mode, s3_beep }
+                       Format: { s3_bios, s3_mode, s3_beep, old_ordering }
                        See Documentation/power/video.txt for s3_bios and s3_mode.
                        s3_beep is for debugging; it makes the PC's speaker beep
                        as soon as the kernel's real-mode entry point is called.
+                       old_ordering causes the ACPI 1.0 ordering of the _PTS
+                       control method, wrt putting devices into low power
+                       states, to be enforced (the ACPI 2.0 ordering of _PTS is
+                       used by default).
 
        acpi_sci=       [HW,ACPI] ACPI System Control Interrupt trigger mode
                        Format: { level | edge | high | low }
@@ -1537,6 +1541,9 @@ and is between 256 and 4096 characters. It is defined in the file
                                Use with caution as certain devices share
                                address decoders between ROMs and other
                                resources.
+               norom           [X86-32,X86_64] Do not assign address space to
+                               expansion ROMs that do not already have
+                               BIOS assigned address ranges.
                irqmask=0xMMMM  [X86-32] Set a bit mask of IRQs allowed to be
                                assigned automatically to PCI devices. You can
                                make the kernel exclude IRQs of your ISA cards
index 2b9212f90446f288ab1d5f9bf7b1067946790626..93fd6b2efeee80c4948e0f33f02d8081a26c637c 100644 (file)
@@ -248,7 +248,7 @@ S:  Supported
 ACPI PCI HOTPLUG DRIVER
 P:     Kristen Carlson Accardi
 M:     kristen.c.accardi@intel.com
-L:     pcihpd-discuss@lists.sourceforge.net
+L:     linux-pci@vger.kernel.org
 S:     Supported
 
 ACPI THERMAL DRIVER
@@ -1145,21 +1145,21 @@ COMPACTPCI HOTPLUG CORE
 P:     Scott Murray
 M:     scottm@somanetworks.com
 M:     scott@spiteful.org
-L:     pcihpd-discuss@lists.sourceforge.net
+L:     linux-pci@vger.kernel.org
 S:     Supported
 
 COMPACTPCI HOTPLUG ZIATECH ZT5550 DRIVER
 P:     Scott Murray
 M:     scottm@somanetworks.com
 M:     scott@spiteful.org
-L:     pcihpd-discuss@lists.sourceforge.net
+L:     linux-pci@vger.kernel.org
 S:     Supported
 
 COMPACTPCI HOTPLUG GENERIC DRIVER
 P:     Scott Murray
 M:     scottm@somanetworks.com
 M:     scott@spiteful.org
-L:     pcihpd-discuss@lists.sourceforge.net
+L:     linux-pci@vger.kernel.org
 S:     Supported
 
 COMPAL LAPTOP SUPPORT
@@ -3219,7 +3219,7 @@ S:        Supported
 PCIE HOTPLUG DRIVER
 P:     Kristen Carlson Accardi
 M:     kristen.c.accardi@intel.com
-L:     pcihpd-discuss@lists.sourceforge.net
+L:     linux-pci@vger.kernel.org
 S:     Supported
 
 PCMCIA SUBSYSTEM
@@ -3865,7 +3865,7 @@ S:        Maintained
 SHPC HOTPLUG DRIVER
 P:     Kristen Carlson Accardi
 M:     kristen.c.accardi@intel.com
-L:     pcihpd-discuss@lists.sourceforge.net
+L:     linux-pci@vger.kernel.org
 S:     Supported
 
 SECURE DIGITAL HOST CONTROLLER INTERFACE DRIVER
index 4f165c93be424545ebcad65ee1d2b10be477f7fe..edae117fcc2bcc93ef81a8d1d8a167e6a2050a68 100644 (file)
 
 #include "pci-frv.h"
 
-#if 0
-void
-pcibios_update_resource(struct pci_dev *dev, struct resource *root,
-                       struct resource *res, int resource)
-{
-       u32 new, check;
-       int reg;
-
-       new = res->start | (res->flags & PCI_REGION_FLAG_MASK);
-       if (resource < 6) {
-               reg = PCI_BASE_ADDRESS_0 + 4*resource;
-       } else if (resource == PCI_ROM_RESOURCE) {
-               res->flags |= IORESOURCE_ROM_ENABLE;
-               new |= PCI_ROM_ADDRESS_ENABLE;
-               reg = dev->rom_base_reg;
-       } else {
-               /* Somebody might have asked allocation of a non-standard resource */
-               return;
-       }
-
-       pci_write_config_dword(dev, reg, new);
-       pci_read_config_dword(dev, reg, &check);
-       if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) {
-               printk(KERN_ERR "PCI: Error while updating region "
-                      "%s/%d (%08x != %08x)\n", pci_name(dev), resource,
-                      new, check);
-       }
-}
-#endif
-
 /*
  * We need to avoid collisions with `mirrored' VGA ports
  * and other strange ISA hardware, so we always want the
index 6ee00effbad28e4549383ff4ebb27afe440f37ee..0a68b5a85f86837b54b912328944c079a4222a24 100644 (file)
@@ -373,15 +373,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
        return(0);
 }
 
-/*****************************************************************************/
-
-void pcibios_update_resource(struct pci_dev *dev, struct resource *root, struct resource *r, int resource)
-{
-       printk(KERN_WARNING "%s(%d): no support for changing PCI resources...\n",
-               __FILE__, __LINE__);
-}
-
-
 /*****************************************************************************/
 
 /*
index 6380662bbf3ceeb29ac054446372293117751e51..678388fd34b1a9e521d812c307a0aec42478fbc4 100644 (file)
@@ -345,42 +345,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
         return pcibios_enable_resources(dev);
 }
 
-
-
-void pcibios_update_resource(struct pci_dev *dev, struct resource *root,
-                             struct resource *res, int resource)
-{
-        u32 new, check;
-        int reg;
-
-        return;
-
-        new = res->start | (res->flags & PCI_REGION_FLAG_MASK);
-        if (resource < 6) {
-                reg = PCI_BASE_ADDRESS_0 + 4 * resource;
-        } else if (resource == PCI_ROM_RESOURCE) {
-               res->flags |= IORESOURCE_ROM_ENABLE;
-                reg = dev->rom_base_reg;
-        } else {
-                /*
-                 * Somebody might have asked allocation of a non-standard
-                 * resource
-                 */
-                return;
-        }
-
-        pci_write_config_dword(dev, reg, new);
-        pci_read_config_dword(dev, reg, &check);
-        if ((new ^ check) &
-            ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK :
-             PCI_BASE_ADDRESS_MEM_MASK)) {
-                printk(KERN_ERR "PCI: Error while updating region "
-                       "%s/%d (%08x != %08x)\n", pci_name(dev), resource,
-                       new, check);
-        }
-}
-
-
 void pcibios_align_resource(void *data, struct resource *res,
                             resource_size_t size, resource_size_t align)
 {
index 08d2e7325252a19071fe446055417bd4d5368dd3..f57095a2617c064700fa0ec7123322eddcfadd80 100644 (file)
@@ -76,38 +76,6 @@ void __devinit __weak pcibios_fixup_bus(struct pci_bus *bus)
        pci_read_bridge_bases(bus);
 }
 
-void
-pcibios_update_resource(struct pci_dev *dev, struct resource *root,
-                       struct resource *res, int resource)
-{
-       u32 new, check;
-       int reg;
-
-       new = res->start | (res->flags & PCI_REGION_FLAG_MASK);
-       if (resource < 6) {
-               reg = PCI_BASE_ADDRESS_0 + 4*resource;
-       } else if (resource == PCI_ROM_RESOURCE) {
-               res->flags |= IORESOURCE_ROM_ENABLE;
-               new |= PCI_ROM_ADDRESS_ENABLE;
-               reg = dev->rom_base_reg;
-       } else {
-               /*
-                * Somebody might have asked allocation of a non-standard
-                * resource
-                */
-               return;
-       }
-
-       pci_write_config_dword(dev, reg, new);
-       pci_read_config_dword(dev, reg, &check);
-       if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ?
-               PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) {
-               printk(KERN_ERR "PCI: Error while updating region "
-                      "%s/%d (%08x != %08x)\n", pci_name(dev), resource,
-                      new, check);
-       }
-}
-
 void pcibios_align_resource(void *data, struct resource *res,
                            resource_size_t size, resource_size_t align)
                            __attribute__ ((weak));
index 112b09f16f367ffe788e2623a91debb118454c16..d00a3656c287fed07c13b4609997e1ff09f8e375 100644 (file)
@@ -408,7 +408,7 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
        dev->class = class >> 8;
        dev->revision = class & 0xff;
 
-       sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus),
+       sprintf(dev->dev.bus_id, "%04x:%02x:%02x.%d", pci_domain_nr(bus),
                dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn));
 
        if (ofpci_verbose)
index 793ad2045f585acceb8501d5c08c86f20803f871..868de3d5c39de9144bfc42574bcb8dd0d24cca3e 100644 (file)
@@ -158,6 +158,8 @@ static int __init acpi_sleep_setup(char *str)
                        acpi_realmode_flags |= 2;
                if (strncmp(str, "s3_beep", 7) == 0)
                        acpi_realmode_flags |= 4;
+               if (strncmp(str, "old_ordering", 12) == 0)
+                       acpi_old_suspend_ordering();
                str = strchr(str, ',');
                if (str != NULL)
                        str += strspn(str, ", \t");
index 75cb5da4ea0ad1880d6c25f02f8b510cb5d9ac97..bf9b441331e9fb2f42ec1a89fcc683d48c6af839 100644 (file)
@@ -1213,9 +1213,9 @@ static int suspend(int vetoable)
        if (err != APM_SUCCESS)
                apm_error("suspend", err);
        err = (err == APM_SUCCESS) ? 0 : -EIO;
-       device_power_up();
+       device_power_up(PMSG_RESUME);
        local_irq_enable();
-       device_resume();
+       device_resume(PMSG_RESUME);
        queue_event(APM_NORMAL_RESUME, NULL);
        spin_lock(&user_list_lock);
        for (as = user_list; as != NULL; as = as->next) {
@@ -1240,7 +1240,7 @@ static void standby(void)
                apm_error("standby", err);
 
        local_irq_disable();
-       device_power_up();
+       device_power_up(PMSG_RESUME);
        local_irq_enable();
 }
 
@@ -1326,7 +1326,7 @@ static void check_events(void)
                        ignore_bounce = 1;
                        if ((event != APM_NORMAL_RESUME)
                            || (ignore_normal_resume == 0)) {
-                               device_resume();
+                               device_resume(PMSG_RESUME);
                                queue_event(event, NULL);
                        }
                        ignore_normal_resume = 0;
index a4665f37cfc5dc9f3d3b7097c08166d061643de6..a0e11c0cc872f03b1ca9b85fd9521f19fa8a3269 100644 (file)
@@ -120,7 +120,18 @@ static struct chipset early_qrk[] __initdata = {
        {}
 };
 
-static void __init check_dev_quirk(int num, int slot, int func)
+/**
+ * check_dev_quirk - apply early quirks to a given PCI device
+ * @num: bus number
+ * @slot: slot number
+ * @func: PCI function
+ *
+ * Check the vendor & device ID against the early quirks table.
+ *
+ * If the device is single function, let early_quirks() know so we don't
+ * poke at this device again.
+ */
+static int __init check_dev_quirk(int num, int slot, int func)
 {
        u16 class;
        u16 vendor;
@@ -131,7 +142,7 @@ static void __init check_dev_quirk(int num, int slot, int func)
        class = read_pci_config_16(num, slot, func, PCI_CLASS_DEVICE);
 
        if (class == 0xffff)
-               return;
+               return -1; /* no class, treat as single function */
 
        vendor = read_pci_config_16(num, slot, func, PCI_VENDOR_ID);
 
@@ -154,7 +165,9 @@ static void __init check_dev_quirk(int num, int slot, int func)
        type = read_pci_config_byte(num, slot, func,
                                    PCI_HEADER_TYPE);
        if (!(type & 0x80))
-               return;
+               return -1;
+
+       return 0;
 }
 
 void __init early_quirks(void)
@@ -167,6 +180,9 @@ void __init early_quirks(void)
        /* Poor man's PCI discovery */
        for (num = 0; num < 32; num++)
                for (slot = 0; slot < 32; slot++)
-                       for (func = 0; func < 8; func++)
-                               check_dev_quirk(num, slot, func);
+                       for (func = 0; func < 8; func++) {
+                               /* Only probe function 0 on single fn devices */
+                               if (check_dev_quirk(num, slot, func))
+                                       break;
+                       }
 }
index 36c540d4ac4b6164dbd02d6b23951ea1315f134f..531b55b8e81a1de1827eac5691d5f8aef1d8d10c 100644 (file)
@@ -684,6 +684,11 @@ void __init setup_arch(char **cmdline_p)
                clear_cpu_cap(&boot_cpu_data, X86_FEATURE_APIC);
        }
 
+#ifdef CONFIG_PCI
+       if (pci_early_dump_regs)
+               early_dump_pci_devices();
+#endif
+
        finish_e820_parsing();
 
 #ifdef CONFIG_X86_32
index 20b9f59f95df85203009cce11d688f2510cbeab3..b67732bbb85a3a562063c36e579ee769ccdf86c1 100644 (file)
@@ -20,6 +20,7 @@
 unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2 |
                                PCI_PROBE_MMCONF;
 
+unsigned int pci_early_dump_regs;
 static int pci_bf_sort;
 int pci_routeirq;
 int pcibios_last_bus = -1;
@@ -31,7 +32,7 @@ struct pci_raw_ops *raw_pci_ext_ops;
 int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
                                                int reg, int len, u32 *val)
 {
-       if (reg < 256 && raw_pci_ops)
+       if (domain == 0 && reg < 256 && raw_pci_ops)
                return raw_pci_ops->read(domain, bus, devfn, reg, len, val);
        if (raw_pci_ext_ops)
                return raw_pci_ext_ops->read(domain, bus, devfn, reg, len, val);
@@ -41,7 +42,7 @@ int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
 int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
                                                int reg, int len, u32 val)
 {
-       if (reg < 256 && raw_pci_ops)
+       if (domain == 0 && reg < 256 && raw_pci_ops)
                return raw_pci_ops->write(domain, bus, devfn, reg, len, val);
        if (raw_pci_ext_ops)
                return raw_pci_ext_ops->write(domain, bus, devfn, reg, len, val);
@@ -121,6 +122,21 @@ void __init dmi_check_skip_isa_align(void)
        dmi_check_system(can_skip_pciprobe_dmi_table);
 }
 
+static void __devinit pcibios_fixup_device_resources(struct pci_dev *dev)
+{
+       struct resource *rom_r = &dev->resource[PCI_ROM_RESOURCE];
+
+       if (pci_probe & PCI_NOASSIGN_ROMS) {
+               if (rom_r->parent)
+                       return;
+               if (rom_r->start) {
+                       /* we deal with BIOS assigned ROM later */
+                       return;
+               }
+               rom_r->start = rom_r->end = rom_r->flags = 0;
+       }
+}
+
 /*
  *  Called after each bus is probed, but before its children
  *  are examined.
@@ -128,7 +144,11 @@ void __init dmi_check_skip_isa_align(void)
 
 void __devinit  pcibios_fixup_bus(struct pci_bus *b)
 {
+       struct pci_dev *dev;
+
        pci_read_bridge_bases(b);
+       list_for_each_entry(dev, &b->devices, bus_list)
+               pcibios_fixup_device_resources(dev);
 }
 
 /*
@@ -481,12 +501,18 @@ char * __devinit  pcibios_setup(char *str)
        else if (!strcmp(str, "rom")) {
                pci_probe |= PCI_ASSIGN_ROMS;
                return NULL;
+       } else if (!strcmp(str, "norom")) {
+               pci_probe |= PCI_NOASSIGN_ROMS;
+               return NULL;
        } else if (!strcmp(str, "assign-busses")) {
                pci_probe |= PCI_ASSIGN_ALL_BUSSES;
                return NULL;
        } else if (!strcmp(str, "use_crs")) {
                pci_probe |= PCI_USE__CRS;
                return NULL;
+       } else if (!strcmp(str, "earlydump")) {
+               pci_early_dump_regs = 1;
+               return NULL;
        } else if (!strcmp(str, "routeirq")) {
                pci_routeirq = 1;
                return NULL;
index 42df4b6606dfdbc6f21d2cce0cbb6e8ae3e3ea42..858dbe3399f9f79d5b318bef2767ffcb37153960 100644 (file)
@@ -49,7 +49,14 @@ void write_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset, u8 val)
 {
        PDprintk("%x writing to %x: %x\n", slot, offset, val);
        outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
-       outb(val, 0xcfc);
+       outb(val, 0xcfc + (offset&3));
+}
+
+void write_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset, u16 val)
+{
+       PDprintk("%x writing to %x: %x\n", slot, offset, val);
+       outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
+       outw(val, 0xcfc + (offset&2));
 }
 
 int early_pci_allowed(void)
@@ -57,3 +64,54 @@ int early_pci_allowed(void)
        return (pci_probe & (PCI_PROBE_CONF1|PCI_PROBE_NOEARLY)) ==
                        PCI_PROBE_CONF1;
 }
+
+void early_dump_pci_device(u8 bus, u8 slot, u8 func)
+{
+       int i;
+       int j;
+       u32 val;
+
+       printk("PCI: %02x:%02x:%02x", bus, slot, func);
+
+       for (i = 0; i < 256; i += 4) {
+               if (!(i & 0x0f))
+                       printk("\n%04x:",i);
+
+               val = read_pci_config(bus, slot, func, i);
+               for (j = 0; j < 4; j++) {
+                       printk(" %02x", val & 0xff);
+                       val >>= 8;
+               }
+       }
+       printk("\n");
+}
+
+void early_dump_pci_devices(void)
+{
+       unsigned bus, slot, func;
+
+       if (!early_pci_allowed())
+               return;
+
+       for (bus = 0; bus < 256; bus++) {
+               for (slot = 0; slot < 32; slot++) {
+                       for (func = 0; func < 8; func++) {
+                               u32 class;
+                               u8 type;
+                               class = read_pci_config(bus, slot, func,
+                                                       PCI_CLASS_REVISION);
+                               if (class == 0xffffffff)
+                                       break;
+
+                               early_dump_pci_device(bus, slot, func);
+
+                               /* No multi-function device? */
+                               type = read_pci_config_byte(bus, slot, func,
+                                                              PCI_HEADER_TYPE);
+                               if (!(type & 0x80))
+                                       break;
+                       }
+               }
+       }
+}
+
index dc568c6b83f8b5a5203bc0452c06427ae9b97b2a..6a06a2eb05973c5e2f129de7a9d1bbcdefc6564a 100644 (file)
@@ -45,7 +45,8 @@ 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);
+       int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq,
+               int new);
 };
 
 struct irq_router_handler {
@@ -77,7 +78,8 @@ static inline struct irq_routing_table *pirq_check_routing_table(u8 *addr)
        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);
+               DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n",
+                       rt);
                return rt;
        }
        return NULL;
@@ -183,7 +185,8 @@ static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset,
        return (nr & 1) ? (x >> 4) : (x & 0xf);
 }
 
-static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val)
+static void write_config_nybble(struct pci_dev *router, unsigned offset,
+       unsigned nr, unsigned int val)
 {
        u8 x;
        unsigned reg = offset + (nr >> 1);
@@ -467,7 +470,8 @@ static int pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int
        return inb(0xc01) & 0xf;
 }
 
-static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev,
+       int pirq, int irq)
 {
        outb(pirq, 0xc00);
        outb(irq, 0xc01);
@@ -660,7 +664,8 @@ static __init int vlsi_router_probe(struct irq_router *r, struct pci_dev *router
 }
 
 
-static __init int serverworks_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+static __init int serverworks_router_probe(struct irq_router *r,
+               struct pci_dev *router, u16 device)
 {
        switch (device) {
        case PCI_DEVICE_ID_SERVERWORKS_OSB4:
@@ -827,10 +832,12 @@ static void __init pirq_find_router(struct irq_router *r)
 
        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))
+               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))
+               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",
@@ -845,11 +852,13 @@ static void __init pirq_find_router(struct irq_router *r)
 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);
+       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))
+               if (info->bus == dev->bus->number &&
+                       PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn))
                        return info;
        return NULL;
 }
@@ -890,7 +899,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
                DBG(" -> not routed\n" KERN_DEBUG);
                return 0;
        }
-       DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs);
+       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
@@ -903,7 +913,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
        }
 
        /* 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) {
+       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);
@@ -920,15 +931,16 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
                        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));
+                               "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))
+                       if (pirq_penalty[i] < pirq_penalty[newirq] &&
+                               can_request_irq(i, IRQF_SHARED))
                                newirq = i;
                }
        }
@@ -944,7 +956,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
                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) {
+       } 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);
@@ -962,7 +975,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
                } else
                        return 0;
        }
-       printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, pci_name(dev));
+       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) {
@@ -974,7 +988,10 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
                if (!info)
                        continue;
                if (info->irq[pin].link == pirq) {
-                       /* We refuse to override the dev->irq information. Give a warning! */
+                       /*
+                        * 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))) {
@@ -987,7 +1004,9 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
                        dev2->irq = irq;
                        pirq_penalty[irq]++;
                        if (dev != dev2)
-                               printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, pci_name(dev2));
+                               printk(KERN_INFO
+                                       "PCI: Sharing IRQ %d with %s\n",
+                                       irq, pci_name(dev2));
                }
        }
        return 1;
@@ -1001,15 +1020,21 @@ static void __init pcibios_fixup_irqs(void)
        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 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);
+                       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)
+               /*
+                * 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]++;
        }
@@ -1025,8 +1050,13 @@ static void __init pcibios_fixup_irqs(void)
                        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);
+                               /*
+                                * interrupt pins are numbered starting
+                                * from 1
+                                */
+                               pin--;
+                               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,
@@ -1067,7 +1097,8 @@ 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);
+               printk(KERN_INFO "%s detected - fixing broken IRQ routing\n",
+                       d->ident);
        }
        return 0;
 }
@@ -1080,7 +1111,8 @@ 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);
+               printk(KERN_INFO "%s detected - fixing broken IRQ routing\n",
+                       d->ident);
        }
        return 0;
 }
@@ -1092,7 +1124,8 @@ static struct dmi_system_id __initdata pciirq_dmi_table[] = {
                .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_PRODUCT_VERSION,
+                               "HP Pavilion Notebook Model GE"),
                        DMI_MATCH(DMI_BOARD_VERSION, "OmniBook N32N-736"),
                },
        },
@@ -1131,7 +1164,10 @@ int __init pcibios_irq_init(void)
                                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 we're using the I/O APIC, avoid using the PCI IRQ
+                * routing table
+                */
                if (io_apic_assign_pci_irqs)
                        pirq_table = NULL;
        }
@@ -1175,7 +1211,7 @@ static int pirq_enable_irq(struct pci_dev *dev)
        if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) {
                char *msg = "";
 
-               pin--;          /* interrupt pins are numbered starting from 1 */
+               pin--; /* interrupt pins are numbered starting from 1 */
 
                if (io_apic_assign_pci_irqs) {
                        int irq;
@@ -1195,13 +1231,16 @@ static int pirq_enable_irq(struct pci_dev *dev)
                                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);
+                                       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",
+                               printk(KERN_INFO
+                                       "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n",
                                        pci_name(dev), 'A' + pin, irq);
                                dev->irq = irq;
                                return 0;
@@ -1212,12 +1251,17 @@ static int pirq_enable_irq(struct pci_dev *dev)
                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))
+               /*
+                * 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);
+               printk(KERN_WARNING
+                       "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
+                       'A' + pin, pci_name(dev), msg);
        }
        return 0;
 }
index b2270a55b0cf48ed934913d48a6c297cee38e1c8..3e25deb821ac9152feb484db2414565c541f18de 100644 (file)
@@ -28,6 +28,7 @@
 #define PCI_USE__CRS           0x10000
 #define PCI_CHECK_ENABLE_AMD_MMCONF    0x20000
 #define PCI_HAS_IO_ECS         0x40000
+#define PCI_NOASSIGN_ROMS      0x80000
 
 extern unsigned int pci_probe;
 extern unsigned long pirq_table_addr;
index bba867391a85bff4c08f9e61632026084cf59cea..735f5ea17473152fc9560a49f15de97cb1dd71b4 100644 (file)
@@ -336,6 +336,15 @@ config ACPI_EC
          the battery and thermal drivers.  If you are compiling for a 
          mobile system, say Y.
 
+config ACPI_PCI_SLOT
+       tristate "PCI slot detection driver"
+       default n
+       help
+         This driver will attempt to discover all PCI slots in your system,
+         and creates entries in /sys/bus/pci/slots/. This feature can
+         help you correlate PCI bus addresses with the physical geography
+         of your slots. If you are unsure, say N.
+
 config ACPI_POWER
        bool
        default y
index 4efbe598c817cfdeb58b8c41a4e67a33a08623c5..52a4cd4b81d0735e916f4ab6ce037265106c27d5 100644 (file)
@@ -48,6 +48,7 @@ obj-$(CONFIG_ACPI_DOCK)               += dock.o
 obj-$(CONFIG_ACPI_BAY)         += bay.o
 obj-$(CONFIG_ACPI_VIDEO)       += video.o
 obj-y                          += pci_root.o pci_link.o pci_irq.o pci_bind.o
+obj-$(CONFIG_ACPI_PCI_SLOT)    += pci_slot.o
 obj-$(CONFIG_ACPI_POWER)       += power.o
 obj-$(CONFIG_ACPI_PROCESSOR)   += processor.o
 obj-$(CONFIG_ACPI_CONTAINER)   += container.o
index afb34387d5f284b02709448b70beeb199587e1d4..ccae305ee55dcb554c441d908c20b43d509b3709 100644 (file)
@@ -295,6 +295,28 @@ int acpi_bus_set_power(acpi_handle handle, int state)
 
 EXPORT_SYMBOL(acpi_bus_set_power);
 
+bool acpi_bus_power_manageable(acpi_handle handle)
+{
+       struct acpi_device *device;
+       int result;
+
+       result = acpi_bus_get_device(handle, &device);
+       return result ? false : device->flags.power_manageable;
+}
+
+EXPORT_SYMBOL(acpi_bus_power_manageable);
+
+bool acpi_bus_can_wakeup(acpi_handle handle)
+{
+       struct acpi_device *device;
+       int result;
+
+       result = acpi_bus_get_device(handle, &device);
+       return result ? false : device->wakeup.flags.valid;
+}
+
+EXPORT_SYMBOL(acpi_bus_can_wakeup);
+
 /* --------------------------------------------------------------------------
                                 Event Management
    -------------------------------------------------------------------------- */
index 6d18ca34b6aa737f49deeca27982252ad1d3d076..0f2dd81736bda796c8ace56f1f216edca65f88fd 100644 (file)
@@ -166,6 +166,8 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle)
                                "firmware_node");
                ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
                                "physical_node");
+               if (acpi_dev->wakeup.flags.valid)
+                       device_set_wakeup_capable(dev, true);
        }
 
        return 0;
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
new file mode 100644 (file)
index 0000000..b9ab030
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ *  pci_slot.c - ACPI PCI Slot Driver
+ *
+ *  The code here is heavily leveraged from the acpiphp module.
+ *  Thanks to Matthew Wilcox <matthew@wil.cx> for much guidance.
+ *  Thanks to Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> for code
+ *  review and fixes.
+ *
+ *  Copyright (C) 2007 Alex Chiang <achiang@hp.com>
+ *  Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms and conditions of the GNU General Public License,
+ *  version 2, as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+static int debug;
+static int check_sta_before_sun;
+
+#define DRIVER_VERSION         "0.1"
+#define DRIVER_AUTHOR  "Alex Chiang <achiang@hp.com>"
+#define DRIVER_DESC    "ACPI PCI Slot Detection Driver"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+module_param(debug, bool, 0644);
+
+#define _COMPONENT             ACPI_PCI_COMPONENT
+ACPI_MODULE_NAME("pci_slot");
+
+#define MY_NAME "pci_slot"
+#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
+#define dbg(format, arg...)                                    \
+       do {                                                    \
+               if (debug)                                      \
+                       printk(KERN_DEBUG "%s: " format,        \
+                               MY_NAME , ## arg);              \
+       } while (0)
+
+#define SLOT_NAME_SIZE 20              /* Inspired by #define in acpiphp.h */
+
+struct acpi_pci_slot {
+       acpi_handle root_handle;        /* handle of the root bridge */
+       struct pci_slot *pci_slot;      /* corresponding pci_slot */
+       struct list_head list;          /* node in the list of slots */
+};
+
+static int acpi_pci_slot_add(acpi_handle handle);
+static void acpi_pci_slot_remove(acpi_handle handle);
+
+static LIST_HEAD(slot_list);
+static DEFINE_MUTEX(slot_list_lock);
+static struct acpi_pci_driver acpi_pci_slot_driver = {
+       .add = acpi_pci_slot_add,
+       .remove = acpi_pci_slot_remove,
+};
+
+static int
+check_slot(acpi_handle handle, int *device, unsigned long *sun)
+{
+       int retval = 0;
+       unsigned long adr, sta;
+       acpi_status status;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+       acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+       dbg("Checking slot on path: %s\n", (char *)buffer.pointer);
+
+       if (check_sta_before_sun) {
+               /* If SxFy doesn't have _STA, we just assume it's there */
+               status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+               if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT)) {
+                       retval = -1;
+                       goto out;
+               }
+       }
+
+       status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+       if (ACPI_FAILURE(status)) {
+               dbg("_ADR returned %d on %s\n", status, (char *)buffer.pointer);
+               retval = -1;
+               goto out;
+       }
+
+       *device = (adr >> 16) & 0xffff;
+
+       /* No _SUN == not a slot == bail */
+       status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
+       if (ACPI_FAILURE(status)) {
+               dbg("_SUN returned %d on %s\n", status, (char *)buffer.pointer);
+               retval = -1;
+               goto out;
+       }
+
+out:
+       kfree(buffer.pointer);
+       return retval;
+}
+
+struct callback_args {
+       acpi_walk_callback      user_function;  /* only for walk_p2p_bridge */
+       struct pci_bus          *pci_bus;
+       acpi_handle             root_handle;
+};
+
+/*
+ * register_slot
+ *
+ * Called once for each SxFy object in the namespace. Don't worry about
+ * calling pci_create_slot multiple times for the same pci_bus:device,
+ * since each subsequent call simply bumps the refcount on the pci_slot.
+ *
+ * The number of calls to pci_destroy_slot from unregister_slot is
+ * symmetrical.
+ */
+static acpi_status
+register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+       int device;
+       unsigned long sun;
+       char name[SLOT_NAME_SIZE];
+       struct acpi_pci_slot *slot;
+       struct pci_slot *pci_slot;
+       struct callback_args *parent_context = context;
+       struct pci_bus *pci_bus = parent_context->pci_bus;
+
+       if (check_slot(handle, &device, &sun))
+               return AE_OK;
+
+       slot = kmalloc(sizeof(*slot), GFP_KERNEL);
+       if (!slot) {
+               err("%s: cannot allocate memory\n", __func__);
+               return AE_OK;
+       }
+
+       snprintf(name, sizeof(name), "%u", (u32)sun);
+       pci_slot = pci_create_slot(pci_bus, device, name);
+       if (IS_ERR(pci_slot)) {
+               err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
+               kfree(slot);
+       }
+
+       slot->root_handle = parent_context->root_handle;
+       slot->pci_slot = pci_slot;
+       INIT_LIST_HEAD(&slot->list);
+       mutex_lock(&slot_list_lock);
+       list_add(&slot->list, &slot_list);
+       mutex_unlock(&slot_list_lock);
+
+       dbg("pci_slot: %p, pci_bus: %x, device: %d, name: %s\n",
+               pci_slot, pci_bus->number, device, name);
+
+       return AE_OK;
+}
+
+/*
+ * walk_p2p_bridge - discover and walk p2p bridges
+ * @handle: points to an acpi_pci_root
+ * @context: p2p_bridge_context pointer
+ *
+ * Note that when we call ourselves recursively, we pass a different
+ * value of pci_bus in the child_context.
+ */
+static acpi_status
+walk_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+       int device, function;
+       unsigned long adr;
+       acpi_status status;
+       acpi_handle dummy_handle;
+       acpi_walk_callback user_function;
+
+       struct pci_dev *dev;
+       struct pci_bus *pci_bus;
+       struct callback_args child_context;
+       struct callback_args *parent_context = context;
+
+       pci_bus = parent_context->pci_bus;
+       user_function = parent_context->user_function;
+
+       status = acpi_get_handle(handle, "_ADR", &dummy_handle);
+       if (ACPI_FAILURE(status))
+               return AE_OK;
+
+       status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+       if (ACPI_FAILURE(status))
+               return AE_OK;
+
+       device = (adr >> 16) & 0xffff;
+       function = adr & 0xffff;
+
+       dev = pci_get_slot(pci_bus, PCI_DEVFN(device, function));
+       if (!dev || !dev->subordinate)
+               goto out;
+
+       child_context.pci_bus = dev->subordinate;
+       child_context.user_function = user_function;
+       child_context.root_handle = parent_context->root_handle;
+
+       dbg("p2p bridge walk, pci_bus = %x\n", dev->subordinate->number);
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+                                    user_function, &child_context, NULL);
+       if (ACPI_FAILURE(status))
+               goto out;
+
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+                                    walk_p2p_bridge, &child_context, NULL);
+out:
+       pci_dev_put(dev);
+       return AE_OK;
+}
+
+/*
+ * walk_root_bridge - generic root bridge walker
+ * @handle: points to an acpi_pci_root
+ * @user_function: user callback for slot objects
+ *
+ * Call user_function for all objects underneath this root bridge.
+ * Walk p2p bridges underneath us and call user_function on those too.
+ */
+static int
+walk_root_bridge(acpi_handle handle, acpi_walk_callback user_function)
+{
+       int seg, bus;
+       unsigned long tmp;
+       acpi_status status;
+       acpi_handle dummy_handle;
+       struct pci_bus *pci_bus;
+       struct callback_args context;
+
+       /* If the bridge doesn't have _STA, we assume it is always there */
+       status = acpi_get_handle(handle, "_STA", &dummy_handle);
+       if (ACPI_SUCCESS(status)) {
+               status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp);
+               if (ACPI_FAILURE(status)) {
+                       info("%s: _STA evaluation failure\n", __func__);
+                       return 0;
+               }
+               if ((tmp & ACPI_STA_DEVICE_FUNCTIONING) == 0)
+                       /* don't register this object */
+                       return 0;
+       }
+
+       status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp);
+       seg = ACPI_SUCCESS(status) ? tmp : 0;
+
+       status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp);
+       bus = ACPI_SUCCESS(status) ? tmp : 0;
+
+       pci_bus = pci_find_bus(seg, bus);
+       if (!pci_bus)
+               return 0;
+
+       context.pci_bus = pci_bus;
+       context.user_function = user_function;
+       context.root_handle = handle;
+
+       dbg("root bridge walk, pci_bus = %x\n", pci_bus->number);
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+                                    user_function, &context, NULL);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+                                    walk_p2p_bridge, &context, NULL);
+       if (ACPI_FAILURE(status))
+               err("%s: walk_p2p_bridge failure - %d\n", __func__, status);
+
+       return status;
+}
+
+/*
+ * acpi_pci_slot_add
+ * @handle: points to an acpi_pci_root
+ */
+static int
+acpi_pci_slot_add(acpi_handle handle)
+{
+       acpi_status status;
+
+       status = walk_root_bridge(handle, register_slot);
+       if (ACPI_FAILURE(status))
+               err("%s: register_slot failure - %d\n", __func__, status);
+
+       return status;
+}
+
+/*
+ * acpi_pci_slot_remove
+ * @handle: points to an acpi_pci_root
+ */
+static void
+acpi_pci_slot_remove(acpi_handle handle)
+{
+       struct acpi_pci_slot *slot, *tmp;
+
+       mutex_lock(&slot_list_lock);
+       list_for_each_entry_safe(slot, tmp, &slot_list, list) {
+               if (slot->root_handle == handle) {
+                       list_del(&slot->list);
+                       pci_destroy_slot(slot->pci_slot);
+                       kfree(slot);
+               }
+       }
+       mutex_unlock(&slot_list_lock);
+}
+
+static int do_sta_before_sun(const struct dmi_system_id *d)
+{
+       info("%s detected: will evaluate _STA before calling _SUN\n", d->ident);
+       check_sta_before_sun = 1;
+       return 0;
+}
+
+static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = {
+       /*
+        * Fujitsu Primequest machines will return 1023 to indicate an
+        * error if the _SUN method is evaluated on SxFy objects that
+        * are not present (as indicated by _STA), so for those machines,
+        * we want to check _STA before evaluating _SUN.
+        */
+       {
+        .callback = do_sta_before_sun,
+        .ident = "Fujitsu PRIMEQUEST",
+        .matches = {
+               DMI_MATCH(DMI_BIOS_VENDOR, "FUJITSU LIMITED"),
+               DMI_MATCH(DMI_BIOS_VERSION, "PRIMEQUEST"),
+               },
+       },
+       {}
+};
+
+static int __init
+acpi_pci_slot_init(void)
+{
+       dmi_check_system(acpi_pci_slot_dmi_table);
+       acpi_pci_register_driver(&acpi_pci_slot_driver);
+       return 0;
+}
+
+static void __exit
+acpi_pci_slot_exit(void)
+{
+       acpi_pci_unregister_driver(&acpi_pci_slot_driver);
+}
+
+module_init(acpi_pci_slot_init);
+module_exit(acpi_pci_slot_exit);
index 81e4f081a4aefd2d53d3004fae15d0935610bb90..4ab21cb1c8c7ebde037b51256de4c326efcfd7f6 100644 (file)
@@ -292,69 +292,135 @@ static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
        return 0;
 }
 
+/**
+ * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in
+ *                          ACPI 3.0) _PSW (Power State Wake)
+ * @dev: Device to handle.
+ * @enable: 0 - disable, 1 - enable the wake capabilities of the device.
+ * @sleep_state: Target sleep state of the system.
+ * @dev_state: Target power state of the device.
+ *
+ * Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
+ * State Wake) for the device, if present.  On failure reset the device's
+ * wakeup.flags.valid flag.
+ *
+ * RETURN VALUE:
+ * 0 if either _DSW or _PSW has been successfully executed
+ * 0 if neither _DSW nor _PSW has been found
+ * -ENODEV if the execution of either _DSW or _PSW has failed
+ */
+int acpi_device_sleep_wake(struct acpi_device *dev,
+                           int enable, int sleep_state, int dev_state)
+{
+       union acpi_object in_arg[3];
+       struct acpi_object_list arg_list = { 3, in_arg };
+       acpi_status status = AE_OK;
+
+       /*
+        * Try to execute _DSW first.
+        *
+        * Three agruments are needed for the _DSW object:
+        * Argument 0: enable/disable the wake capabilities
+        * Argument 1: target system state
+        * Argument 2: target device state
+        * When _DSW object is called to disable the wake capabilities, maybe
+        * the first argument is filled. The values of the other two agruments
+        * are meaningless.
+        */
+       in_arg[0].type = ACPI_TYPE_INTEGER;
+       in_arg[0].integer.value = enable;
+       in_arg[1].type = ACPI_TYPE_INTEGER;
+       in_arg[1].integer.value = sleep_state;
+       in_arg[2].type = ACPI_TYPE_INTEGER;
+       in_arg[2].integer.value = dev_state;
+       status = acpi_evaluate_object(dev->handle, "_DSW", &arg_list, NULL);
+       if (ACPI_SUCCESS(status)) {
+               return 0;
+       } else if (status != AE_NOT_FOUND) {
+               printk(KERN_ERR PREFIX "_DSW execution failed\n");
+               dev->wakeup.flags.valid = 0;
+               return -ENODEV;
+       }
+
+       /* Execute _PSW */
+       arg_list.count = 1;
+       in_arg[0].integer.value = enable;
+       status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
+       if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
+               printk(KERN_ERR PREFIX "_PSW execution failed\n");
+               dev->wakeup.flags.valid = 0;
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
 /*
  * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
  * 1. Power on the power resources required for the wakeup device 
- * 2. Enable _PSW (power state wake) for the device if present
+ * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
+ *    State Wake) for the device, if present
  */
-int acpi_enable_wakeup_device_power(struct acpi_device *dev)
+int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
 {
-       union acpi_object arg = { ACPI_TYPE_INTEGER };
-       struct acpi_object_list arg_list = { 1, &arg };
-       acpi_status status = AE_OK;
-       int i;
-       int ret = 0;
+       int i, err;
 
        if (!dev || !dev->wakeup.flags.valid)
-               return -1;
+               return -EINVAL;
+
+       /*
+        * Do not execute the code below twice in a row without calling
+        * acpi_disable_wakeup_device_power() in between for the same device
+        */
+       if (dev->wakeup.flags.prepared)
+               return 0;
 
-       arg.integer.value = 1;
        /* Open power resource */
        for (i = 0; i < dev->wakeup.resources.count; i++) {
-               ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
+               int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
                if (ret) {
                        printk(KERN_ERR PREFIX "Transition power state\n");
                        dev->wakeup.flags.valid = 0;
-                       return -1;
+                       return -ENODEV;
                }
        }
 
-       /* Execute PSW */
-       status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
-       if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
-               printk(KERN_ERR PREFIX "Evaluate _PSW\n");
-               dev->wakeup.flags.valid = 0;
-               ret = -1;
-       }
+       /*
+        * Passing 3 as the third argument below means the device may be placed
+        * in arbitrary power state afterwards.
+        */
+       err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);
+       if (!err)
+               dev->wakeup.flags.prepared = 1;
 
-       return ret;
+       return err;
 }
 
 /*
  * Shutdown a wakeup device, counterpart of above method
- * 1. Disable _PSW (power state wake)
+ * 1. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
+ *    State Wake) for the device, if present
  * 2. Shutdown down the power resources
  */
 int acpi_disable_wakeup_device_power(struct acpi_device *dev)
 {
-       union acpi_object arg = { ACPI_TYPE_INTEGER };
-       struct acpi_object_list arg_list = { 1, &arg };
-       acpi_status status = AE_OK;
-       int i;
-       int ret = 0;
-
+       int i, ret;
 
        if (!dev || !dev->wakeup.flags.valid)
-               return -1;
+               return -EINVAL;
 
-       arg.integer.value = 0;
-       /* Execute PSW */
-       status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
-       if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
-               printk(KERN_ERR PREFIX "Evaluate _PSW\n");
-               dev->wakeup.flags.valid = 0;
-               return -1;
-       }
+       /*
+        * Do not execute the code below twice in a row without calling
+        * acpi_enable_wakeup_device_power() in between for the same device
+        */
+       if (!dev->wakeup.flags.prepared)
+               return 0;
+
+       dev->wakeup.flags.prepared = 0;
+
+       ret = acpi_device_sleep_wake(dev, 0, 0, 0);
+       if (ret)
+               return ret;
 
        /* Close power resource */
        for (i = 0; i < dev->wakeup.resources.count; i++) {
@@ -362,7 +428,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
                if (ret) {
                        printk(KERN_ERR PREFIX "Transition power state\n");
                        dev->wakeup.flags.valid = 0;
-                       return -1;
+                       return -ENODEV;
                }
        }
 
index 5b049cd795530a683393d1bca044ba6aa20498e4..f3132aa47a69d7b0656d9fe4fa40e9c5a9f8edcf 100644 (file)
@@ -703,9 +703,7 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
        acpi_status status = 0;
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
        union acpi_object *package = NULL;
-       union acpi_object in_arg[3];
-       struct acpi_object_list arg_list = { 3, in_arg };
-       acpi_status psw_status = AE_OK;
+       int psw_error;
 
        struct acpi_device_id button_device_ids[] = {
                {"PNP0C0D", 0},
@@ -737,39 +735,11 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
         * So it is necessary to call _DSW object first. Only when it is not
         * present will the _PSW object used.
         */
-       /*
-        * Three agruments are needed for the _DSW object.
-        * Argument 0: enable/disable the wake capabilities
-        * When _DSW object is called to disable the wake capabilities, maybe
-        * the first argument is filled. The value of the other two agruments
-        * is meaningless.
-        */
-       in_arg[0].type = ACPI_TYPE_INTEGER;
-       in_arg[0].integer.value = 0;
-       in_arg[1].type = ACPI_TYPE_INTEGER;
-       in_arg[1].integer.value = 0;
-       in_arg[2].type = ACPI_TYPE_INTEGER;
-       in_arg[2].integer.value = 0;
-       psw_status = acpi_evaluate_object(device->handle, "_DSW",
-                                               &arg_list, NULL);
-       if (ACPI_FAILURE(psw_status) && (psw_status != AE_NOT_FOUND))
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in evaluate _DSW\n"));
-       /*
-        * When the _DSW object is not present, OSPM will call _PSW object.
-        */
-       if (psw_status == AE_NOT_FOUND) {
-               /*
-                * Only one agruments is required for the _PSW object.
-                * agrument 0: enable/disable the wake capabilities
-                */
-               arg_list.count = 1;
-               in_arg[0].integer.value = 0;
-               psw_status = acpi_evaluate_object(device->handle, "_PSW",
-                                               &arg_list, NULL);
-               if (ACPI_FAILURE(psw_status) && (psw_status != AE_NOT_FOUND))
-                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in "
-                                               "evaluate _PSW\n"));
-       }
+       psw_error = acpi_device_sleep_wake(device, 0, 0, 0);
+       if (psw_error)
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                               "error in _DSW or _PSW evaluation\n"));
+
        /* Power button, Lid switch always enable wakeup */
        if (!acpi_match_device_ids(device, button_device_ids))
                device->wakeup.flags.run_wake = 1;
index 244e352f7661a6332ddaffa933d84c6b7edd2973..0489a7d1d42c91dcf404c50c56e07c722a41ea1f 100644 (file)
 
 u8 sleep_states[ACPI_S_STATE_COUNT];
 
-#ifdef CONFIG_PM_SLEEP
-static u32 acpi_target_sleep_state = ACPI_STATE_S0;
-#endif
-
 static int acpi_sleep_prepare(u32 acpi_state)
 {
 #ifdef CONFIG_ACPI_SLEEP
@@ -49,9 +45,96 @@ static int acpi_sleep_prepare(u32 acpi_state)
        return 0;
 }
 
-#ifdef CONFIG_SUSPEND
-static struct platform_suspend_ops acpi_suspend_ops;
+#ifdef CONFIG_PM_SLEEP
+static u32 acpi_target_sleep_state = ACPI_STATE_S0;
+
+/*
+ * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the
+ * user to request that behavior by using the 'acpi_old_suspend_ordering'
+ * kernel command line option that causes the following variable to be set.
+ */
+static bool old_suspend_ordering;
+
+void __init acpi_old_suspend_ordering(void)
+{
+       old_suspend_ordering = true;
+}
+
+/**
+ *     acpi_pm_disable_gpes - Disable the GPEs.
+ */
+static int acpi_pm_disable_gpes(void)
+{
+       acpi_hw_disable_all_gpes();
+       return 0;
+}
+
+/**
+ *     __acpi_pm_prepare - Prepare the platform to enter the target state.
+ *
+ *     If necessary, set the firmware waking vector and do arch-specific
+ *     nastiness to get the wakeup code to the waking vector.
+ */
+static int __acpi_pm_prepare(void)
+{
+       int error = acpi_sleep_prepare(acpi_target_sleep_state);
+
+       if (error)
+               acpi_target_sleep_state = ACPI_STATE_S0;
+       return error;
+}
+
+/**
+ *     acpi_pm_prepare - Prepare the platform to enter the target sleep
+ *             state and disable the GPEs.
+ */
+static int acpi_pm_prepare(void)
+{
+       int error = __acpi_pm_prepare();
+
+       if (!error)
+               acpi_hw_disable_all_gpes();
+       return error;
+}
+
+/**
+ *     acpi_pm_finish - Instruct the platform to leave a sleep state.
+ *
+ *     This is called after we wake back up (or if entering the sleep state
+ *     failed).
+ */
+static void acpi_pm_finish(void)
+{
+       u32 acpi_state = acpi_target_sleep_state;
+
+       if (acpi_state == ACPI_STATE_S0)
+               return;
+
+       printk(KERN_INFO PREFIX "Waking up from system sleep state S%d\n",
+               acpi_state);
+       acpi_disable_wakeup_device(acpi_state);
+       acpi_leave_sleep_state(acpi_state);
+
+       /* reset firmware waking vector */
+       acpi_set_firmware_waking_vector((acpi_physical_address) 0);
+
+       acpi_target_sleep_state = ACPI_STATE_S0;
+}
+
+/**
+ *     acpi_pm_end - Finish up suspend sequence.
+ */
+static void acpi_pm_end(void)
+{
+       /*
+        * This is necessary in case acpi_pm_finish() is not called during a
+        * failing transition to a sleep state.
+        */
+       acpi_target_sleep_state = ACPI_STATE_S0;
+}
+#endif /* CONFIG_PM_SLEEP */
 
+#ifdef CONFIG_SUSPEND
 extern void do_suspend_lowlevel(void);
 
 static u32 acpi_suspend_states[] = {
@@ -65,7 +148,6 @@ static u32 acpi_suspend_states[] = {
  *     acpi_suspend_begin - Set the target system sleep state to the state
  *             associated with given @pm_state, if supported.
  */
-
 static int acpi_suspend_begin(suspend_state_t pm_state)
 {
        u32 acpi_state = acpi_suspend_states[pm_state];
@@ -81,25 +163,6 @@ static int acpi_suspend_begin(suspend_state_t pm_state)
        return error;
 }
 
-/**
- *     acpi_suspend_prepare - Do preliminary suspend work.
- *
- *     If necessary, set the firmware waking vector and do arch-specific
- *     nastiness to get the wakeup code to the waking vector.
- */
-
-static int acpi_suspend_prepare(void)
-{
-       int error = acpi_sleep_prepare(acpi_target_sleep_state);
-
-       if (error) {
-               acpi_target_sleep_state = ACPI_STATE_S0;
-               return error;
-       }
-
-       return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
-}
-
 /**
  *     acpi_suspend_enter - Actually enter a sleep state.
  *     @pm_state: ignored
@@ -108,7 +171,6 @@ static int acpi_suspend_prepare(void)
  *     assembly, which in turn call acpi_enter_sleep_state().
  *     It's unfortunate, but it works. Please fix if you're feeling frisky.
  */
-
 static int acpi_suspend_enter(suspend_state_t pm_state)
 {
        acpi_status status = AE_OK;
@@ -165,39 +227,6 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
        return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
-/**
- *     acpi_suspend_finish - Instruct the platform to leave a sleep state.
- *
- *     This is called after we wake back up (or if entering the sleep state
- *     failed). 
- */
-
-static void acpi_suspend_finish(void)
-{
-       u32 acpi_state = acpi_target_sleep_state;
-
-       acpi_disable_wakeup_device(acpi_state);
-       acpi_leave_sleep_state(acpi_state);
-
-       /* reset firmware waking vector */
-       acpi_set_firmware_waking_vector((acpi_physical_address) 0);
-
-       acpi_target_sleep_state = ACPI_STATE_S0;
-}
-
-/**
- *     acpi_suspend_end - Finish up suspend sequence.
- */
-
-static void acpi_suspend_end(void)
-{
-       /*
-        * This is necessary in case acpi_suspend_finish() is not called during a
-        * failing transition to a sleep state.
-        */
-       acpi_target_sleep_state = ACPI_STATE_S0;
-}
-
 static int acpi_suspend_state_valid(suspend_state_t pm_state)
 {
        u32 acpi_state;
@@ -217,10 +246,39 @@ static int acpi_suspend_state_valid(suspend_state_t pm_state)
 static struct platform_suspend_ops acpi_suspend_ops = {
        .valid = acpi_suspend_state_valid,
        .begin = acpi_suspend_begin,
-       .prepare = acpi_suspend_prepare,
+       .prepare = acpi_pm_prepare,
        .enter = acpi_suspend_enter,
-       .finish = acpi_suspend_finish,
-       .end = acpi_suspend_end,
+       .finish = acpi_pm_finish,
+       .end = acpi_pm_end,
+};
+
+/**
+ *     acpi_suspend_begin_old - Set the target system sleep state to the
+ *             state associated with given @pm_state, if supported, and
+ *             execute the _PTS control method.  This function is used if the
+ *             pre-ACPI 2.0 suspend ordering has been requested.
+ */
+static int acpi_suspend_begin_old(suspend_state_t pm_state)
+{
+       int error = acpi_suspend_begin(pm_state);
+
+       if (!error)
+               error = __acpi_pm_prepare();
+       return error;
+}
+
+/*
+ * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
+ * been requested.
+ */
+static struct platform_suspend_ops acpi_suspend_ops_old = {
+       .valid = acpi_suspend_state_valid,
+       .begin = acpi_suspend_begin_old,
+       .prepare = acpi_pm_disable_gpes,
+       .enter = acpi_suspend_enter,
+       .finish = acpi_pm_finish,
+       .end = acpi_pm_end,
+       .recover = acpi_pm_finish,
 };
 #endif /* CONFIG_SUSPEND */
 
@@ -228,22 +286,9 @@ static struct platform_suspend_ops acpi_suspend_ops = {
 static int acpi_hibernation_begin(void)
 {
        acpi_target_sleep_state = ACPI_STATE_S4;
-
        return 0;
 }
 
-static int acpi_hibernation_prepare(void)
-{
-       int error = acpi_sleep_prepare(ACPI_STATE_S4);
-
-       if (error) {
-               acpi_target_sleep_state = ACPI_STATE_S0;
-               return error;
-       }
-
-       return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
-}
-
 static int acpi_hibernation_enter(void)
 {
        acpi_status status = AE_OK;
@@ -273,52 +318,55 @@ static void acpi_hibernation_leave(void)
        acpi_leave_sleep_state_prep(ACPI_STATE_S4);
 }
 
-static void acpi_hibernation_finish(void)
+static void acpi_pm_enable_gpes(void)
 {
-       acpi_disable_wakeup_device(ACPI_STATE_S4);
-       acpi_leave_sleep_state(ACPI_STATE_S4);
-
-       /* reset firmware waking vector */
-       acpi_set_firmware_waking_vector((acpi_physical_address) 0);
-
-       acpi_target_sleep_state = ACPI_STATE_S0;
+       acpi_hw_enable_all_runtime_gpes();
 }
 
-static void acpi_hibernation_end(void)
-{
-       /*
-        * This is necessary in case acpi_hibernation_finish() is not called
-        * during a failing transition to the sleep state.
-        */
-       acpi_target_sleep_state = ACPI_STATE_S0;
-}
+static struct platform_hibernation_ops acpi_hibernation_ops = {
+       .begin = acpi_hibernation_begin,
+       .end = acpi_pm_end,
+       .pre_snapshot = acpi_pm_prepare,
+       .finish = acpi_pm_finish,
+       .prepare = acpi_pm_prepare,
+       .enter = acpi_hibernation_enter,
+       .leave = acpi_hibernation_leave,
+       .pre_restore = acpi_pm_disable_gpes,
+       .restore_cleanup = acpi_pm_enable_gpes,
+};
 
-static int acpi_hibernation_pre_restore(void)
+/**
+ *     acpi_hibernation_begin_old - Set the target system sleep state to
+ *             ACPI_STATE_S4 and execute the _PTS control method.  This
+ *             function is used if the pre-ACPI 2.0 suspend ordering has been
+ *             requested.
+ */
+static int acpi_hibernation_begin_old(void)
 {
-       acpi_status status;
-
-       status = acpi_hw_disable_all_gpes();
-
-       return ACPI_SUCCESS(status) ? 0 : -EFAULT;
-}
+       int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
-static void acpi_hibernation_restore_cleanup(void)
-{
-       acpi_hw_enable_all_runtime_gpes();
+       if (!error)
+               acpi_target_sleep_state = ACPI_STATE_S4;
+       return error;
 }
 
-static struct platform_hibernation_ops acpi_hibernation_ops = {
-       .begin = acpi_hibernation_begin,
-       .end = acpi_hibernation_end,
-       .pre_snapshot = acpi_hibernation_prepare,
-       .finish = acpi_hibernation_finish,
-       .prepare = acpi_hibernation_prepare,
+/*
+ * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
+ * been requested.
+ */
+static struct platform_hibernation_ops acpi_hibernation_ops_old = {
+       .begin = acpi_hibernation_begin_old,
+       .end = acpi_pm_end,
+       .pre_snapshot = acpi_pm_disable_gpes,
+       .finish = acpi_pm_finish,
+       .prepare = acpi_pm_disable_gpes,
        .enter = acpi_hibernation_enter,
        .leave = acpi_hibernation_leave,
-       .pre_restore = acpi_hibernation_pre_restore,
-       .restore_cleanup = acpi_hibernation_restore_cleanup,
+       .pre_restore = acpi_pm_disable_gpes,
+       .restore_cleanup = acpi_pm_enable_gpes,
+       .recover = acpi_pm_finish,
 };
-#endif                         /* CONFIG_HIBERNATION */
+#endif /* CONFIG_HIBERNATION */
 
 int acpi_suspend(u32 acpi_state)
 {
@@ -419,6 +467,31 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
                *d_min_p = d_min;
        return d_max;
 }
+
+/**
+ *     acpi_pm_device_sleep_wake - enable or disable the system wake-up
+ *                                  capability of given device
+ *     @dev: device to handle
+ *     @enable: 'true' - enable, 'false' - disable the wake-up capability
+ */
+int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
+{
+       acpi_handle handle;
+       struct acpi_device *adev;
+
+       if (!device_may_wakeup(dev))
+               return -EINVAL;
+
+       handle = DEVICE_ACPI_HANDLE(dev);
+       if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) {
+               printk(KERN_DEBUG "ACPI handle has no context!\n");
+               return -ENODEV;
+       }
+
+       return enable ?
+               acpi_enable_wakeup_device_power(adev, acpi_target_sleep_state) :
+               acpi_disable_wakeup_device_power(adev);
+}
 #endif
 
 static void acpi_power_off_prepare(void)
@@ -460,13 +533,15 @@ int __init acpi_sleep_init(void)
                }
        }
 
-       suspend_set_ops(&acpi_suspend_ops);
+       suspend_set_ops(old_suspend_ordering ?
+               &acpi_suspend_ops_old : &acpi_suspend_ops);
 #endif
 
 #ifdef CONFIG_HIBERNATION
        status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b);
        if (ACPI_SUCCESS(status)) {
-               hibernation_set_ops(&acpi_hibernation_ops);
+               hibernation_set_ops(old_suspend_ordering ?
+                       &acpi_hibernation_ops_old : &acpi_hibernation_ops);
                sleep_states[ACPI_STATE_S4] = 1;
                printk(" S4");
        }
index ed8e41becf0c4997177697157ac8ee8a368d9ab1..38655eb132dcca3a42ac2db56f2daf79d3b10286 100644 (file)
@@ -42,7 +42,7 @@ void acpi_enable_wakeup_device_prep(u8 sleep_state)
                        continue;
 
                spin_unlock(&acpi_device_lock);
-               acpi_enable_wakeup_device_power(dev);
+               acpi_enable_wakeup_device_power(dev, sleep_state);
                spin_lock(&acpi_device_lock);
        }
        spin_unlock(&acpi_device_lock);
@@ -66,13 +66,15 @@ void acpi_enable_wakeup_device(u8 sleep_state)
        list_for_each_safe(node, next, &acpi_wakeup_device_list) {
                struct acpi_device *dev =
                        container_of(node, struct acpi_device, wakeup_list);
+
                if (!dev->wakeup.flags.valid)
                        continue;
+
                /* If users want to disable run-wake GPE,
                 * we only disable it for wake and leave it for runtime
                 */
-               if (!dev->wakeup.state.enabled ||
-                   sleep_state > (u32) dev->wakeup.sleep_state) {
+               if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared)
+                   || sleep_state > (u32) dev->wakeup.sleep_state) {
                        if (dev->wakeup.flags.run_wake) {
                                spin_unlock(&acpi_device_lock);
                                /* set_gpe_type will disable GPE, leave it like that */
@@ -110,8 +112,9 @@ void acpi_disable_wakeup_device(u8 sleep_state)
 
                if (!dev->wakeup.flags.valid)
                        continue;
-               if (!dev->wakeup.state.enabled ||
-                   sleep_state > (u32) dev->wakeup.sleep_state) {
+
+               if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared)
+                   || sleep_state > (u32) dev->wakeup.sleep_state) {
                        if (dev->wakeup.flags.run_wake) {
                                spin_unlock(&acpi_device_lock);
                                acpi_set_gpe_type(dev->wakeup.gpe_device,
index 911ec600fe71dec287cf922441a8cf53855ecf3f..3f940393d6c7904cdd68a5db804a3c0365ac696a 100644 (file)
@@ -453,6 +453,8 @@ int platform_driver_register(struct platform_driver *drv)
                drv->driver.suspend = platform_drv_suspend;
        if (drv->resume)
                drv->driver.resume = platform_drv_resume;
+       if (drv->pm)
+               drv->driver.pm = &drv->pm->base;
        return driver_register(&drv->driver);
 }
 EXPORT_SYMBOL_GPL(platform_driver_register);
@@ -560,7 +562,9 @@ static int platform_match(struct device *dev, struct device_driver *drv)
        return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
 }
 
-static int platform_suspend(struct device *dev, pm_message_t mesg)
+#ifdef CONFIG_PM_SLEEP
+
+static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)
 {
        int ret = 0;
 
@@ -570,7 +574,7 @@ static int platform_suspend(struct device *dev, pm_message_t mesg)
        return ret;
 }
 
-static int platform_suspend_late(struct device *dev, pm_message_t mesg)
+static int platform_legacy_suspend_late(struct device *dev, pm_message_t mesg)
 {
        struct platform_driver *drv = to_platform_driver(dev->driver);
        struct platform_device *pdev;
@@ -583,7 +587,7 @@ static int platform_suspend_late(struct device *dev, pm_message_t mesg)
        return ret;
 }
 
-static int platform_resume_early(struct device *dev)
+static int platform_legacy_resume_early(struct device *dev)
 {
        struct platform_driver *drv = to_platform_driver(dev->driver);
        struct platform_device *pdev;
@@ -596,7 +600,7 @@ static int platform_resume_early(struct device *dev)
        return ret;
 }
 
-static int platform_resume(struct device *dev)
+static int platform_legacy_resume(struct device *dev)
 {
        int ret = 0;
 
@@ -606,15 +610,291 @@ static int platform_resume(struct device *dev)
        return ret;
 }
 
+static int platform_pm_prepare(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+       int ret = 0;
+
+       if (drv && drv->pm && drv->pm->prepare)
+               ret = drv->pm->prepare(dev);
+
+       return ret;
+}
+
+static void platform_pm_complete(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+
+       if (drv && drv->pm && drv->pm->complete)
+               drv->pm->complete(dev);
+}
+
+#ifdef CONFIG_SUSPEND
+
+static int platform_pm_suspend(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+       int ret = 0;
+
+       if (drv && drv->pm) {
+               if (drv->pm->suspend)
+                       ret = drv->pm->suspend(dev);
+       } else {
+               ret = platform_legacy_suspend(dev, PMSG_SUSPEND);
+       }
+
+       return ret;
+}
+
+static int platform_pm_suspend_noirq(struct device *dev)
+{
+       struct platform_driver *pdrv;
+       int ret = 0;
+
+       if (!dev->driver)
+               return 0;
+
+       pdrv = to_platform_driver(dev->driver);
+       if (pdrv->pm) {
+               if (pdrv->pm->suspend_noirq)
+                       ret = pdrv->pm->suspend_noirq(dev);
+       } else {
+               ret = platform_legacy_suspend_late(dev, PMSG_SUSPEND);
+       }
+
+       return ret;
+}
+
+static int platform_pm_resume(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+       int ret = 0;
+
+       if (drv && drv->pm) {
+               if (drv->pm->resume)
+                       ret = drv->pm->resume(dev);
+       } else {
+               ret = platform_legacy_resume(dev);
+       }
+
+       return ret;
+}
+
+static int platform_pm_resume_noirq(struct device *dev)
+{
+       struct platform_driver *pdrv;
+       int ret = 0;
+
+       if (!dev->driver)
+               return 0;
+
+       pdrv = to_platform_driver(dev->driver);
+       if (pdrv->pm) {
+               if (pdrv->pm->resume_noirq)
+                       ret = pdrv->pm->resume_noirq(dev);
+       } else {
+               ret = platform_legacy_resume_early(dev);
+       }
+
+       return ret;
+}
+
+#else /* !CONFIG_SUSPEND */
+
+#define platform_pm_suspend            NULL
+#define platform_pm_resume             NULL
+#define platform_pm_suspend_noirq      NULL
+#define platform_pm_resume_noirq       NULL
+
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_HIBERNATION
+
+static int platform_pm_freeze(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+       int ret = 0;
+
+       if (!drv)
+               return 0;
+
+       if (drv->pm) {
+               if (drv->pm->freeze)
+                       ret = drv->pm->freeze(dev);
+       } else {
+               ret = platform_legacy_suspend(dev, PMSG_FREEZE);
+       }
+
+       return ret;
+}
+
+static int platform_pm_freeze_noirq(struct device *dev)
+{
+       struct platform_driver *pdrv;
+       int ret = 0;
+
+       if (!dev->driver)
+               return 0;
+
+       pdrv = to_platform_driver(dev->driver);
+       if (pdrv->pm) {
+               if (pdrv->pm->freeze_noirq)
+                       ret = pdrv->pm->freeze_noirq(dev);
+       } else {
+               ret = platform_legacy_suspend_late(dev, PMSG_FREEZE);
+       }
+
+       return ret;
+}
+
+static int platform_pm_thaw(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+       int ret = 0;
+
+       if (drv && drv->pm) {
+               if (drv->pm->thaw)
+                       ret = drv->pm->thaw(dev);
+       } else {
+               ret = platform_legacy_resume(dev);
+       }
+
+       return ret;
+}
+
+static int platform_pm_thaw_noirq(struct device *dev)
+{
+       struct platform_driver *pdrv;
+       int ret = 0;
+
+       if (!dev->driver)
+               return 0;
+
+       pdrv = to_platform_driver(dev->driver);
+       if (pdrv->pm) {
+               if (pdrv->pm->thaw_noirq)
+                       ret = pdrv->pm->thaw_noirq(dev);
+       } else {
+               ret = platform_legacy_resume_early(dev);
+       }
+
+       return ret;
+}
+
+static int platform_pm_poweroff(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+       int ret = 0;
+
+       if (drv && drv->pm) {
+               if (drv->pm->poweroff)
+                       ret = drv->pm->poweroff(dev);
+       } else {
+               ret = platform_legacy_suspend(dev, PMSG_HIBERNATE);
+       }
+
+       return ret;
+}
+
+static int platform_pm_poweroff_noirq(struct device *dev)
+{
+       struct platform_driver *pdrv;
+       int ret = 0;
+
+       if (!dev->driver)
+               return 0;
+
+       pdrv = to_platform_driver(dev->driver);
+       if (pdrv->pm) {
+               if (pdrv->pm->poweroff_noirq)
+                       ret = pdrv->pm->poweroff_noirq(dev);
+       } else {
+               ret = platform_legacy_suspend_late(dev, PMSG_HIBERNATE);
+       }
+
+       return ret;
+}
+
+static int platform_pm_restore(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+       int ret = 0;
+
+       if (drv && drv->pm) {
+               if (drv->pm->restore)
+                       ret = drv->pm->restore(dev);
+       } else {
+               ret = platform_legacy_resume(dev);
+       }
+
+       return ret;
+}
+
+static int platform_pm_restore_noirq(struct device *dev)
+{
+       struct platform_driver *pdrv;
+       int ret = 0;
+
+       if (!dev->driver)
+               return 0;
+
+       pdrv = to_platform_driver(dev->driver);
+       if (pdrv->pm) {
+               if (pdrv->pm->restore_noirq)
+                       ret = pdrv->pm->restore_noirq(dev);
+       } else {
+               ret = platform_legacy_resume_early(dev);
+       }
+
+       return ret;
+}
+
+#else /* !CONFIG_HIBERNATION */
+
+#define platform_pm_freeze             NULL
+#define platform_pm_thaw               NULL
+#define platform_pm_poweroff           NULL
+#define platform_pm_restore            NULL
+#define platform_pm_freeze_noirq       NULL
+#define platform_pm_thaw_noirq         NULL
+#define platform_pm_poweroff_noirq     NULL
+#define platform_pm_restore_noirq      NULL
+
+#endif /* !CONFIG_HIBERNATION */
+
+struct pm_ext_ops platform_pm_ops = {
+       .base = {
+               .prepare = platform_pm_prepare,
+               .complete = platform_pm_complete,
+               .suspend = platform_pm_suspend,
+               .resume = platform_pm_resume,
+               .freeze = platform_pm_freeze,
+               .thaw = platform_pm_thaw,
+               .poweroff = platform_pm_poweroff,
+               .restore = platform_pm_restore,
+       },
+       .suspend_noirq = platform_pm_suspend_noirq,
+       .resume_noirq = platform_pm_resume_noirq,
+       .freeze_noirq = platform_pm_freeze_noirq,
+       .thaw_noirq = platform_pm_thaw_noirq,
+       .poweroff_noirq = platform_pm_poweroff_noirq,
+       .restore_noirq = platform_pm_restore_noirq,
+};
+
+#define PLATFORM_PM_OPS_PTR    &platform_pm_ops
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define PLATFORM_PM_OPS_PTR    NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
 struct bus_type platform_bus_type = {
        .name           = "platform",
        .dev_attrs      = platform_dev_attrs,
        .match          = platform_match,
        .uevent         = platform_uevent,
-       .suspend        = platform_suspend,
-       .suspend_late   = platform_suspend_late,
-       .resume_early   = platform_resume_early,
-       .resume         = platform_resume,
+       .pm             = PLATFORM_PM_OPS_PTR,
 };
 EXPORT_SYMBOL_GPL(platform_bus_type);
 
index 45cc3d9eacb874db52f663fe4cc3dea83b41d92c..3250c5257b748286cfec992558a7590f0ab915e9 100644 (file)
  * and add it to the list of power-controlled devices. sysfs entries for
  * controlling device power management will also be added.
  *
- * A different set of lists than the global subsystem list are used to
- * keep track of power info because we use different lists to hold
- * devices based on what stage of the power management process they
- * are in. The power domain dependencies may also differ from the
- * ancestral dependencies that the subsystem list maintains.
+ * A separate list is used for keeping track of power info, because the power
+ * domain dependencies may differ from the ancestral dependencies that the
+ * subsystem list maintains.
  */
 
 #include <linux/device.h>
 #include "power.h"
 
 /*
- * The entries in the dpm_active list are in a depth first order, simply
+ * The entries in the dpm_list list are in a depth first order, simply
  * because children are guaranteed to be discovered after parents, and
  * are inserted at the back of the list on discovery.
  *
- * All the other lists are kept in the same order, for consistency.
- * However the lists aren't always traversed in the same order.
- * Semaphores must be acquired from the top (i.e., front) down
- * and released in the opposite order.  Devices must be suspended
- * from the bottom (i.e., end) up and resumed in the opposite order.
- * That way no parent will be suspended while it still has an active
- * child.
- *
  * Since device_pm_add() may be called with a device semaphore held,
  * we must never try to acquire a device semaphore while holding
  * dpm_list_mutex.
  */
 
-LIST_HEAD(dpm_active);
-static LIST_HEAD(dpm_off);
-static LIST_HEAD(dpm_off_irq);
+LIST_HEAD(dpm_list);
 
 static DEFINE_MUTEX(dpm_list_mtx);
 
-/* 'true' if all devices have been suspended, protected by dpm_list_mtx */
-static bool all_sleeping;
+/*
+ * Set once the preparation of devices for a PM transition has started, reset
+ * before starting to resume devices.  Protected by dpm_list_mtx.
+ */
+static bool transition_started;
+
+/**
+ *     device_pm_lock - lock the list of active devices used by the PM core
+ */
+void device_pm_lock(void)
+{
+       mutex_lock(&dpm_list_mtx);
+}
+
+/**
+ *     device_pm_unlock - unlock the list of active devices used by the PM core
+ */
+void device_pm_unlock(void)
+{
+       mutex_unlock(&dpm_list_mtx);
+}
 
 /**
  *     device_pm_add - add a device to the list of active devices
@@ -68,17 +75,25 @@ int device_pm_add(struct device *dev)
                 dev->bus ? dev->bus->name : "No Bus",
                 kobject_name(&dev->kobj));
        mutex_lock(&dpm_list_mtx);
-       if ((dev->parent && dev->parent->power.sleeping) || all_sleeping) {
-               if (dev->parent->power.sleeping)
-                       dev_warn(dev, "parent %s is sleeping\n",
+       if (dev->parent) {
+               if (dev->parent->power.status >= DPM_SUSPENDING) {
+                       dev_warn(dev, "parent %s is sleeping, will not add\n",
                                dev->parent->bus_id);
-               else
-                       dev_warn(dev, "all devices are sleeping\n");
+                       WARN_ON(true);
+               }
+       } else if (transition_started) {
+               /*
+                * We refuse to register parentless devices while a PM
+                * transition is in progress in order to avoid leaving them
+                * unhandled down the road
+                */
                WARN_ON(true);
        }
        error = dpm_sysfs_add(dev);
-       if (!error)
-               list_add_tail(&dev->power.entry, &dpm_active);
+       if (!error) {
+               dev->power.status = DPM_ON;
+               list_add_tail(&dev->power.entry, &dpm_list);
+       }
        mutex_unlock(&dpm_list_mtx);
        return error;
 }
@@ -100,73 +115,243 @@ void device_pm_remove(struct device *dev)
        mutex_unlock(&dpm_list_mtx);
 }
 
+/**
+ *     pm_op - execute the PM operation appropiate for given PM event
+ *     @dev:   Device.
+ *     @ops:   PM operations to choose from.
+ *     @state: PM transition of the system being carried out.
+ */
+static int pm_op(struct device *dev, struct pm_ops *ops, pm_message_t state)
+{
+       int error = 0;
+
+       switch (state.event) {
+#ifdef CONFIG_SUSPEND
+       case PM_EVENT_SUSPEND:
+               if (ops->suspend) {
+                       error = ops->suspend(dev);
+                       suspend_report_result(ops->suspend, error);
+               }
+               break;
+       case PM_EVENT_RESUME:
+               if (ops->resume) {
+                       error = ops->resume(dev);
+                       suspend_report_result(ops->resume, error);
+               }
+               break;
+#endif /* CONFIG_SUSPEND */
+#ifdef CONFIG_HIBERNATION
+       case PM_EVENT_FREEZE:
+       case PM_EVENT_QUIESCE:
+               if (ops->freeze) {
+                       error = ops->freeze(dev);
+                       suspend_report_result(ops->freeze, error);
+               }
+               break;
+       case PM_EVENT_HIBERNATE:
+               if (ops->poweroff) {
+                       error = ops->poweroff(dev);
+                       suspend_report_result(ops->poweroff, error);
+               }
+               break;
+       case PM_EVENT_THAW:
+       case PM_EVENT_RECOVER:
+               if (ops->thaw) {
+                       error = ops->thaw(dev);
+                       suspend_report_result(ops->thaw, error);
+               }
+               break;
+       case PM_EVENT_RESTORE:
+               if (ops->restore) {
+                       error = ops->restore(dev);
+                       suspend_report_result(ops->restore, error);
+               }
+               break;
+#endif /* CONFIG_HIBERNATION */
+       default:
+               error = -EINVAL;
+       }
+       return error;
+}
+
+/**
+ *     pm_noirq_op - execute the PM operation appropiate for given PM event
+ *     @dev:   Device.
+ *     @ops:   PM operations to choose from.
+ *     @state: PM transition of the system being carried out.
+ *
+ *     The operation is executed with interrupts disabled by the only remaining
+ *     functional CPU in the system.
+ */
+static int pm_noirq_op(struct device *dev, struct pm_ext_ops *ops,
+                       pm_message_t state)
+{
+       int error = 0;
+
+       switch (state.event) {
+#ifdef CONFIG_SUSPEND
+       case PM_EVENT_SUSPEND:
+               if (ops->suspend_noirq) {
+                       error = ops->suspend_noirq(dev);
+                       suspend_report_result(ops->suspend_noirq, error);
+               }
+               break;
+       case PM_EVENT_RESUME:
+               if (ops->resume_noirq) {
+                       error = ops->resume_noirq(dev);
+                       suspend_report_result(ops->resume_noirq, error);
+               }
+               break;
+#endif /* CONFIG_SUSPEND */
+#ifdef CONFIG_HIBERNATION
+       case PM_EVENT_FREEZE:
+       case PM_EVENT_QUIESCE:
+               if (ops->freeze_noirq) {
+                       error = ops->freeze_noirq(dev);
+                       suspend_report_result(ops->freeze_noirq, error);
+               }
+               break;
+       case PM_EVENT_HIBERNATE:
+               if (ops->poweroff_noirq) {
+                       error = ops->poweroff_noirq(dev);
+                       suspend_report_result(ops->poweroff_noirq, error);
+               }
+               break;
+       case PM_EVENT_THAW:
+       case PM_EVENT_RECOVER:
+               if (ops->thaw_noirq) {
+                       error = ops->thaw_noirq(dev);
+                       suspend_report_result(ops->thaw_noirq, error);
+               }
+               break;
+       case PM_EVENT_RESTORE:
+               if (ops->restore_noirq) {
+                       error = ops->restore_noirq(dev);
+                       suspend_report_result(ops->restore_noirq, error);
+               }
+               break;
+#endif /* CONFIG_HIBERNATION */
+       default:
+               error = -EINVAL;
+       }
+       return error;
+}
+
+static char *pm_verb(int event)
+{
+       switch (event) {
+       case PM_EVENT_SUSPEND:
+               return "suspend";
+       case PM_EVENT_RESUME:
+               return "resume";
+       case PM_EVENT_FREEZE:
+               return "freeze";
+       case PM_EVENT_QUIESCE:
+               return "quiesce";
+       case PM_EVENT_HIBERNATE:
+               return "hibernate";
+       case PM_EVENT_THAW:
+               return "thaw";
+       case PM_EVENT_RESTORE:
+               return "restore";
+       case PM_EVENT_RECOVER:
+               return "recover";
+       default:
+               return "(unknown PM event)";
+       }
+}
+
+static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info)
+{
+       dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event),
+               ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ?
+               ", may wakeup" : "");
+}
+
+static void pm_dev_err(struct device *dev, pm_message_t state, char *info,
+                       int error)
+{
+       printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n",
+               kobject_name(&dev->kobj), pm_verb(state.event), info, error);
+}
+
 /*------------------------- Resume routines -------------------------*/
 
 /**
- *     resume_device_early - Power on one device (early resume).
+ *     resume_device_noirq - Power on one device (early resume).
  *     @dev:   Device.
+ *     @state: PM transition of the system being carried out.
  *
  *     Must be called with interrupts disabled.
  */
-static int resume_device_early(struct device *dev)
+static int resume_device_noirq(struct device *dev, pm_message_t state)
 {
        int error = 0;
 
        TRACE_DEVICE(dev);
        TRACE_RESUME(0);
 
-       if (dev->bus && dev->bus->resume_early) {
-               dev_dbg(dev, "EARLY resume\n");
+       if (!dev->bus)
+               goto End;
+
+       if (dev->bus->pm) {
+               pm_dev_dbg(dev, state, "EARLY ");
+               error = pm_noirq_op(dev, dev->bus->pm, state);
+       } else if (dev->bus->resume_early) {
+               pm_dev_dbg(dev, state, "legacy EARLY ");
                error = dev->bus->resume_early(dev);
        }
-
+ End:
        TRACE_RESUME(error);
        return error;
 }
 
 /**
  *     dpm_power_up - Power on all regular (non-sysdev) devices.
+ *     @state: PM transition of the system being carried out.
  *
- *     Walk the dpm_off_irq list and power each device up. This
- *     is used for devices that required they be powered down with
- *     interrupts disabled. As devices are powered on, they are moved
- *     to the dpm_off list.
+ *     Execute the appropriate "noirq resume" callback for all devices marked
+ *     as DPM_OFF_IRQ.
  *
  *     Must be called with interrupts disabled and only one CPU running.
  */
-static void dpm_power_up(void)
+static void dpm_power_up(pm_message_t state)
 {
+       struct device *dev;
 
-       while (!list_empty(&dpm_off_irq)) {
-               struct list_head *entry = dpm_off_irq.next;
-               struct device *dev = to_device(entry);
+       list_for_each_entry(dev, &dpm_list, power.entry)
+               if (dev->power.status > DPM_OFF) {
+                       int error;
 
-               list_move_tail(entry, &dpm_off);
-               resume_device_early(dev);
-       }
+                       dev->power.status = DPM_OFF;
+                       error = resume_device_noirq(dev, state);
+                       if (error)
+                               pm_dev_err(dev, state, " early", error);
+               }
 }
 
 /**
  *     device_power_up - Turn on all devices that need special attention.
+ *     @state: PM transition of the system being carried out.
  *
  *     Power on system devices, then devices that required we shut them down
  *     with interrupts disabled.
  *
  *     Must be called with interrupts disabled.
  */
-void device_power_up(void)
+void device_power_up(pm_message_t state)
 {
        sysdev_resume();
-       dpm_power_up();
+       dpm_power_up(state);
 }
 EXPORT_SYMBOL_GPL(device_power_up);
 
 /**
  *     resume_device - Restore state for one device.
  *     @dev:   Device.
- *
+ *     @state: PM transition of the system being carried out.
  */
-static int resume_device(struct device *dev)
+static int resume_device(struct device *dev, pm_message_t state)
 {
        int error = 0;
 
@@ -175,21 +360,40 @@ static int resume_device(struct device *dev)
 
        down(&dev->sem);
 
-       if (dev->bus && dev->bus->resume) {
-               dev_dbg(dev,"resuming\n");
-               error = dev->bus->resume(dev);
+       if (dev->bus) {
+               if (dev->bus->pm) {
+                       pm_dev_dbg(dev, state, "");
+                       error = pm_op(dev, &dev->bus->pm->base, state);
+               } else if (dev->bus->resume) {
+                       pm_dev_dbg(dev, state, "legacy ");
+                       error = dev->bus->resume(dev);
+               }
+               if (error)
+                       goto End;
        }
 
-       if (!error && dev->type && dev->type->resume) {
-               dev_dbg(dev,"resuming\n");
-               error = dev->type->resume(dev);
+       if (dev->type) {
+               if (dev->type->pm) {
+                       pm_dev_dbg(dev, state, "type ");
+                       error = pm_op(dev, dev->type->pm, state);
+               } else if (dev->type->resume) {
+                       pm_dev_dbg(dev, state, "legacy type ");
+                       error = dev->type->resume(dev);
+               }
+               if (error)
+                       goto End;
        }
 
-       if (!error && dev->class && dev->class->resume) {
-               dev_dbg(dev,"class resume\n");
-               error = dev->class->resume(dev);
+       if (dev->class) {
+               if (dev->class->pm) {
+                       pm_dev_dbg(dev, state, "class ");
+                       error = pm_op(dev, dev->class->pm, state);
+               } else if (dev->class->resume) {
+                       pm_dev_dbg(dev, state, "legacy class ");
+                       error = dev->class->resume(dev);
+               }
        }
-
+ End:
        up(&dev->sem);
 
        TRACE_RESUME(error);
@@ -198,78 +402,161 @@ static int resume_device(struct device *dev)
 
 /**
  *     dpm_resume - Resume every device.
+ *     @state: PM transition of the system being carried out.
  *
- *     Resume the devices that have either not gone through
- *     the late suspend, or that did go through it but also
- *     went through the early resume.
+ *     Execute the appropriate "resume" callback for all devices the status of
+ *     which indicates that they are inactive.
+ */
+static void dpm_resume(pm_message_t state)
+{
+       struct list_head list;
+
+       INIT_LIST_HEAD(&list);
+       mutex_lock(&dpm_list_mtx);
+       transition_started = false;
+       while (!list_empty(&dpm_list)) {
+               struct device *dev = to_device(dpm_list.next);
+
+               get_device(dev);
+               if (dev->power.status >= DPM_OFF) {
+                       int error;
+
+                       dev->power.status = DPM_RESUMING;
+                       mutex_unlock(&dpm_list_mtx);
+
+                       error = resume_device(dev, state);
+
+                       mutex_lock(&dpm_list_mtx);
+                       if (error)
+                               pm_dev_err(dev, state, "", error);
+               } else if (dev->power.status == DPM_SUSPENDING) {
+                       /* Allow new children of the device to be registered */
+                       dev->power.status = DPM_RESUMING;
+               }
+               if (!list_empty(&dev->power.entry))
+                       list_move_tail(&dev->power.entry, &list);
+               put_device(dev);
+       }
+       list_splice(&list, &dpm_list);
+       mutex_unlock(&dpm_list_mtx);
+}
+
+/**
+ *     complete_device - Complete a PM transition for given device
+ *     @dev:   Device.
+ *     @state: PM transition of the system being carried out.
+ */
+static void complete_device(struct device *dev, pm_message_t state)
+{
+       down(&dev->sem);
+
+       if (dev->class && dev->class->pm && dev->class->pm->complete) {
+               pm_dev_dbg(dev, state, "completing class ");
+               dev->class->pm->complete(dev);
+       }
+
+       if (dev->type && dev->type->pm && dev->type->pm->complete) {
+               pm_dev_dbg(dev, state, "completing type ");
+               dev->type->pm->complete(dev);
+       }
+
+       if (dev->bus && dev->bus->pm && dev->bus->pm->base.complete) {
+               pm_dev_dbg(dev, state, "completing ");
+               dev->bus->pm->base.complete(dev);
+       }
+
+       up(&dev->sem);
+}
+
+/**
+ *     dpm_complete - Complete a PM transition for all devices.
+ *     @state: PM transition of the system being carried out.
  *
- *     Take devices from the dpm_off_list, resume them,
- *     and put them on the dpm_locked list.
+ *     Execute the ->complete() callbacks for all devices that are not marked
+ *     as DPM_ON.
  */
-static void dpm_resume(void)
+static void dpm_complete(pm_message_t state)
 {
+       struct list_head list;
+
+       INIT_LIST_HEAD(&list);
        mutex_lock(&dpm_list_mtx);
-       all_sleeping = false;
-       while(!list_empty(&dpm_off)) {
-               struct list_head *entry = dpm_off.next;
-               struct device *dev = to_device(entry);
+       while (!list_empty(&dpm_list)) {
+               struct device *dev = to_device(dpm_list.prev);
 
-               list_move_tail(entry, &dpm_active);
-               dev->power.sleeping = false;
-               mutex_unlock(&dpm_list_mtx);
-               resume_device(dev);
-               mutex_lock(&dpm_list_mtx);
+               get_device(dev);
+               if (dev->power.status > DPM_ON) {
+                       dev->power.status = DPM_ON;
+                       mutex_unlock(&dpm_list_mtx);
+
+                       complete_device(dev, state);
+
+                       mutex_lock(&dpm_list_mtx);
+               }
+               if (!list_empty(&dev->power.entry))
+                       list_move(&dev->power.entry, &list);
+               put_device(dev);
        }
+       list_splice(&list, &dpm_list);
        mutex_unlock(&dpm_list_mtx);
 }
 
 /**
  *     device_resume - Restore state of each device in system.
+ *     @state: PM transition of the system being carried out.
  *
  *     Resume all the devices, unlock them all, and allow new
  *     devices to be registered once again.
  */
-void device_resume(void)
+void device_resume(pm_message_t state)
 {
        might_sleep();
-       dpm_resume();
+       dpm_resume(state);
+       dpm_complete(state);
 }
 EXPORT_SYMBOL_GPL(device_resume);
 
 
 /*------------------------- Suspend routines -------------------------*/
 
-static inline char *suspend_verb(u32 event)
+/**
+ *     resume_event - return a PM message representing the resume event
+ *                    corresponding to given sleep state.
+ *     @sleep_state: PM message representing a sleep state.
+ */
+static pm_message_t resume_event(pm_message_t sleep_state)
 {
-       switch (event) {
-       case PM_EVENT_SUSPEND:  return "suspend";
-       case PM_EVENT_FREEZE:   return "freeze";
-       case PM_EVENT_PRETHAW:  return "prethaw";
-       default:                return "(unknown suspend event)";
+       switch (sleep_state.event) {
+       case PM_EVENT_SUSPEND:
+               return PMSG_RESUME;
+       case PM_EVENT_FREEZE:
+       case PM_EVENT_QUIESCE:
+               return PMSG_RECOVER;
+       case PM_EVENT_HIBERNATE:
+               return PMSG_RESTORE;
        }
-}
-
-static void
-suspend_device_dbg(struct device *dev, pm_message_t state, char *info)
-{
-       dev_dbg(dev, "%s%s%s\n", info, suspend_verb(state.event),
-               ((state.event == PM_EVENT_SUSPEND) && device_may_wakeup(dev)) ?
-               ", may wakeup" : "");
+       return PMSG_ON;
 }
 
 /**
- *     suspend_device_late - Shut down one device (late suspend).
+ *     suspend_device_noirq - Shut down one device (late suspend).
  *     @dev:   Device.
- *     @state: Power state device is entering.
+ *     @state: PM transition of the system being carried out.
  *
  *     This is called with interrupts off and only a single CPU running.
  */
-static int suspend_device_late(struct device *dev, pm_message_t state)
+static int suspend_device_noirq(struct device *dev, pm_message_t state)
 {
        int error = 0;
 
-       if (dev->bus && dev->bus->suspend_late) {
-               suspend_device_dbg(dev, state, "LATE ");
+       if (!dev->bus)
+               return 0;
+
+       if (dev->bus->pm) {
+               pm_dev_dbg(dev, state, "LATE ");
+               error = pm_noirq_op(dev, dev->bus->pm, state);
+       } else if (dev->bus->suspend_late) {
+               pm_dev_dbg(dev, state, "legacy LATE ");
                error = dev->bus->suspend_late(dev, state);
                suspend_report_result(dev->bus->suspend_late, error);
        }
@@ -278,37 +565,30 @@ static int suspend_device_late(struct device *dev, pm_message_t state)
 
 /**
  *     device_power_down - Shut down special devices.
- *     @state:         Power state to enter.
+ *     @state: PM transition of the system being carried out.
  *
- *     Power down devices that require interrupts to be disabled
- *     and move them from the dpm_off list to the dpm_off_irq list.
+ *     Power down devices that require interrupts to be disabled.
  *     Then power down system devices.
  *
  *     Must be called with interrupts disabled and only one CPU running.
  */
 int device_power_down(pm_message_t state)
 {
+       struct device *dev;
        int error = 0;
 
-       while (!list_empty(&dpm_off)) {
-               struct list_head *entry = dpm_off.prev;
-               struct device *dev = to_device(entry);
-
-               error = suspend_device_late(dev, state);
+       list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
+               error = suspend_device_noirq(dev, state);
                if (error) {
-                       printk(KERN_ERR "Could not power down device %s: "
-                                       "error %d\n",
-                                       kobject_name(&dev->kobj), error);
+                       pm_dev_err(dev, state, " late", error);
                        break;
                }
-               if (!list_empty(&dev->power.entry))
-                       list_move(&dev->power.entry, &dpm_off_irq);
+               dev->power.status = DPM_OFF_IRQ;
        }
-
        if (!error)
                error = sysdev_suspend(state);
        if (error)
-               dpm_power_up();
+               dpm_power_up(resume_event(state));
        return error;
 }
 EXPORT_SYMBOL_GPL(device_power_down);
@@ -316,7 +596,7 @@ EXPORT_SYMBOL_GPL(device_power_down);
 /**
  *     suspend_device - Save state of one device.
  *     @dev:   Device.
- *     @state: Power state device is entering.
+ *     @state: PM transition of the system being carried out.
  */
 static int suspend_device(struct device *dev, pm_message_t state)
 {
@@ -324,24 +604,43 @@ static int suspend_device(struct device *dev, pm_message_t state)
 
        down(&dev->sem);
 
-       if (dev->class && dev->class->suspend) {
-               suspend_device_dbg(dev, state, "class ");
-               error = dev->class->suspend(dev, state);
-               suspend_report_result(dev->class->suspend, error);
+       if (dev->class) {
+               if (dev->class->pm) {
+                       pm_dev_dbg(dev, state, "class ");
+                       error = pm_op(dev, dev->class->pm, state);
+               } else if (dev->class->suspend) {
+                       pm_dev_dbg(dev, state, "legacy class ");
+                       error = dev->class->suspend(dev, state);
+                       suspend_report_result(dev->class->suspend, error);
+               }
+               if (error)
+                       goto End;
        }
 
-       if (!error && dev->type && dev->type->suspend) {
-               suspend_device_dbg(dev, state, "type ");
-               error = dev->type->suspend(dev, state);
-               suspend_report_result(dev->type->suspend, error);
+       if (dev->type) {
+               if (dev->type->pm) {
+                       pm_dev_dbg(dev, state, "type ");
+                       error = pm_op(dev, dev->type->pm, state);
+               } else if (dev->type->suspend) {
+                       pm_dev_dbg(dev, state, "legacy type ");
+                       error = dev->type->suspend(dev, state);
+                       suspend_report_result(dev->type->suspend, error);
+               }
+               if (error)
+                       goto End;
        }
 
-       if (!error && dev->bus && dev->bus->suspend) {
-               suspend_device_dbg(dev, state, "");
-               error = dev->bus->suspend(dev, state);
-               suspend_report_result(dev->bus->suspend, error);
+       if (dev->bus) {
+               if (dev->bus->pm) {
+                       pm_dev_dbg(dev, state, "");
+                       error = pm_op(dev, &dev->bus->pm->base, state);
+               } else if (dev->bus->suspend) {
+                       pm_dev_dbg(dev, state, "legacy ");
+                       error = dev->bus->suspend(dev, state);
+                       suspend_report_result(dev->bus->suspend, error);
+               }
        }
-
+ End:
        up(&dev->sem);
 
        return error;
@@ -349,67 +648,139 @@ static int suspend_device(struct device *dev, pm_message_t state)
 
 /**
  *     dpm_suspend - Suspend every device.
- *     @state: Power state to put each device in.
- *
- *     Walk the dpm_locked list.  Suspend each device and move it
- *     to the dpm_off list.
+ *     @state: PM transition of the system being carried out.
  *
- *     (For historical reasons, if it returns -EAGAIN, that used to mean
- *     that the device would be called again with interrupts disabled.
- *     These days, we use the "suspend_late()" callback for that, so we
- *     print a warning and consider it an error).
+ *     Execute the appropriate "suspend" callbacks for all devices.
  */
 static int dpm_suspend(pm_message_t state)
 {
+       struct list_head list;
        int error = 0;
 
+       INIT_LIST_HEAD(&list);
        mutex_lock(&dpm_list_mtx);
-       while (!list_empty(&dpm_active)) {
-               struct list_head *entry = dpm_active.prev;
-               struct device *dev = to_device(entry);
+       while (!list_empty(&dpm_list)) {
+               struct device *dev = to_device(dpm_list.prev);
 
-               WARN_ON(dev->parent && dev->parent->power.sleeping);
-
-               dev->power.sleeping = true;
+               get_device(dev);
                mutex_unlock(&dpm_list_mtx);
+
                error = suspend_device(dev, state);
+
                mutex_lock(&dpm_list_mtx);
                if (error) {
-                       printk(KERN_ERR "Could not suspend device %s: "
-                                       "error %d%s\n",
-                                       kobject_name(&dev->kobj),
-                                       error,
-                                       (error == -EAGAIN ?
-                                       " (please convert to suspend_late)" :
-                                       ""));
-                       dev->power.sleeping = false;
+                       pm_dev_err(dev, state, "", error);
+                       put_device(dev);
                        break;
                }
+               dev->power.status = DPM_OFF;
                if (!list_empty(&dev->power.entry))
-                       list_move(&dev->power.entry, &dpm_off);
+                       list_move(&dev->power.entry, &list);
+               put_device(dev);
        }
-       if (!error)
-               all_sleeping = true;
+       list_splice(&list, dpm_list.prev);
        mutex_unlock(&dpm_list_mtx);
+       return error;
+}
+
+/**
+ *     prepare_device - Execute the ->prepare() callback(s) for given device.
+ *     @dev:   Device.
+ *     @state: PM transition of the system being carried out.
+ */
+static int prepare_device(struct device *dev, pm_message_t state)
+{
+       int error = 0;
+
+       down(&dev->sem);
+
+       if (dev->bus && dev->bus->pm && dev->bus->pm->base.prepare) {
+               pm_dev_dbg(dev, state, "preparing ");
+               error = dev->bus->pm->base.prepare(dev);
+               suspend_report_result(dev->bus->pm->base.prepare, error);
+               if (error)
+                       goto End;
+       }
+
+       if (dev->type && dev->type->pm && dev->type->pm->prepare) {
+               pm_dev_dbg(dev, state, "preparing type ");
+               error = dev->type->pm->prepare(dev);
+               suspend_report_result(dev->type->pm->prepare, error);
+               if (error)
+                       goto End;
+       }
+
+       if (dev->class && dev->class->pm && dev->class->pm->prepare) {
+               pm_dev_dbg(dev, state, "preparing class ");
+               error = dev->class->pm->prepare(dev);
+               suspend_report_result(dev->class->pm->prepare, error);
+       }
+ End:
+       up(&dev->sem);
+
+       return error;
+}
+
+/**
+ *     dpm_prepare - Prepare all devices for a PM transition.
+ *     @state: PM transition of the system being carried out.
+ *
+ *     Execute the ->prepare() callback for all devices.
+ */
+static int dpm_prepare(pm_message_t state)
+{
+       struct list_head list;
+       int error = 0;
+
+       INIT_LIST_HEAD(&list);
+       mutex_lock(&dpm_list_mtx);
+       transition_started = true;
+       while (!list_empty(&dpm_list)) {
+               struct device *dev = to_device(dpm_list.next);
+
+               get_device(dev);
+               dev->power.status = DPM_PREPARING;
+               mutex_unlock(&dpm_list_mtx);
 
+               error = prepare_device(dev, state);
+
+               mutex_lock(&dpm_list_mtx);
+               if (error) {
+                       dev->power.status = DPM_ON;
+                       if (error == -EAGAIN) {
+                               put_device(dev);
+                               continue;
+                       }
+                       printk(KERN_ERR "PM: Failed to prepare device %s "
+                               "for power transition: error %d\n",
+                               kobject_name(&dev->kobj), error);
+                       put_device(dev);
+                       break;
+               }
+               dev->power.status = DPM_SUSPENDING;
+               if (!list_empty(&dev->power.entry))
+                       list_move_tail(&dev->power.entry, &list);
+               put_device(dev);
+       }
+       list_splice(&list, &dpm_list);
+       mutex_unlock(&dpm_list_mtx);
        return error;
 }
 
 /**
  *     device_suspend - Save state and stop all devices in system.
- *     @state: new power management state
+ *     @state: PM transition of the system being carried out.
  *
- *     Prevent new devices from being registered, then lock all devices
- *     and suspend them.
+ *     Prepare and suspend all devices.
  */
 int device_suspend(pm_message_t state)
 {
        int error;
 
        might_sleep();
-       error = dpm_suspend(state);
-       if (error)
-               device_resume();
+       error = dpm_prepare(state);
+       if (!error)
+               error = dpm_suspend(state);
        return error;
 }
 EXPORT_SYMBOL_GPL(device_suspend);
index a6894f2a4b9960d1a19094fa8b4b7890b9bd0e6d..a3252c0e28878fde258ebac8c10ad7c10cafbb86 100644 (file)
@@ -4,7 +4,7 @@
  * main.c
  */
 
-extern struct list_head dpm_active;    /* The active device list */
+extern struct list_head dpm_list;      /* The active device list */
 
 static inline struct device *to_device(struct list_head *entry)
 {
index d11f74b038db2039026a6f4a6b1a490e994b51f6..596aeecfdffe4be62c7e6773046e9c23d4dd37bb 100644 (file)
@@ -6,9 +6,6 @@
 #include <linux/string.h>
 #include "power.h"
 
-int (*platform_enable_wakeup)(struct device *dev, int is_on);
-
-
 /*
  *     wakeup - Report/change current wakeup option for device
  *
index 87a7f1d02578f194c821eb26049cbaafcf46a54c..9b1b20b59e0a7b2dcc61bad05f078813e65f92fe 100644 (file)
@@ -188,9 +188,9 @@ static int show_file_hash(unsigned int value)
 static int show_dev_hash(unsigned int value)
 {
        int match = 0;
-       struct list_head * entry = dpm_active.prev;
+       struct list_head *entry = dpm_list.prev;
 
-       while (entry != &dpm_active) {
+       while (entry != &dpm_list) {
                struct device * dev = to_device(entry);
                unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH);
                if (hash == value) {
index 4d1ce2e7361e41cfee2b35b5da3025f7641e2e4b..7d63f8ced24b12568fc49ebfa06fd588e49b6fc9 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for the PCI bus specific drivers.
 #
 
-obj-y          += access.o bus.o probe.o remove.o pci.o quirks.o \
+obj-y          += access.o bus.o probe.o remove.o pci.o quirks.o slot.o \
                        pci-driver.o search.o pci-sysfs.o rom.o setup-res.o
 obj-$(CONFIG_PROC_FS) += proc.o
 
index f8c187a763bdcac12323191836079ea9943dde3e..93e37f0666ab5e2786484a775f107bbffd9b3ecd 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/types.h>
 #include <linux/pci.h>
 #include <linux/pci_hotplug.h>
+#include <linux/pci-acpi.h>
 #include <acpi/acpi.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/actypes.h>
@@ -299,7 +300,7 @@ free_and_return:
  *
  * @handle - the handle of the hotplug controller.
  */
-acpi_status acpi_run_oshp(acpi_handle handle)
+static acpi_status acpi_run_oshp(acpi_handle handle)
 {
        acpi_status             status;
        struct acpi_buffer      string = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -322,9 +323,6 @@ acpi_status acpi_run_oshp(acpi_handle handle)
        kfree(string.pointer);
        return status;
 }
-EXPORT_SYMBOL_GPL(acpi_run_oshp);
-
-
 
 /* acpi_get_hp_params_from_firmware
  *
@@ -374,6 +372,85 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
 }
 EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware);
 
+/**
+ * acpi_get_hp_hw_control_from_firmware
+ * @dev: the pci_dev of the bridge that has a hotplug controller
+ * @flags: requested control bits for _OSC
+ *
+ * Attempt to take hotplug control from firmware.
+ */
+int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags)
+{
+       acpi_status status;
+       acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev));
+       struct pci_dev *pdev = dev;
+       struct pci_bus *parent;
+       struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
+
+       flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
+                 OSC_SHPC_NATIVE_HP_CONTROL |
+                 OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+       if (!flags) {
+               err("Invalid flags %u specified!\n", flags);
+               return -EINVAL;
+       }
+
+       /*
+        * Per PCI firmware specification, we should run the ACPI _OSC
+        * method to get control of hotplug hardware before using it. If
+        * an _OSC is missing, we look for an OSHP to do the same thing.
+        * To handle different BIOS behavior, we look for _OSC and OSHP
+        * within the scope of the hotplug controller and its parents,
+        * upto the host bridge under which this controller exists.
+        */
+       while (!handle) {
+               /*
+                * This hotplug controller was not listed in the ACPI name
+                * space at all. Try to get acpi handle of parent pci bus.
+                */
+               if (!pdev || !pdev->bus->parent)
+                       break;
+               parent = pdev->bus->parent;
+               dbg("Could not find %s in acpi namespace, trying parent\n",
+                   pci_name(pdev));
+               if (!parent->self)
+                       /* Parent must be a host bridge */
+                       handle = acpi_get_pci_rootbridge_handle(
+                                       pci_domain_nr(parent),
+                                       parent->number);
+               else
+                       handle = DEVICE_ACPI_HANDLE(&(parent->self->dev));
+               pdev = parent->self;
+       }
+
+       while (handle) {
+               acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
+               dbg("Trying to get hotplug control for %s \n",
+                   (char *)string.pointer);
+               status = pci_osc_control_set(handle, flags);
+               if (status == AE_NOT_FOUND)
+                       status = acpi_run_oshp(handle);
+               if (ACPI_SUCCESS(status)) {
+                       dbg("Gained control for hotplug HW for pci %s (%s)\n",
+                           pci_name(dev), (char *)string.pointer);
+                       kfree(string.pointer);
+                       return 0;
+               }
+               if (acpi_root_bridge(handle))
+                       break;
+               chandle = handle;
+               status = acpi_get_parent(chandle, &handle);
+               if (ACPI_FAILURE(status))
+                       break;
+       }
+
+       dbg("Cannot get control of hotplug hardware for pci %s\n",
+           pci_name(dev));
+
+       kfree(string.pointer);
+       return -ENODEV;
+}
+EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
 
 /* acpi_root_bridge - check to see if this acpi object is a root bridge
  *
index 7a29164d4b325aa2ee4219398b679b066f0e27a6..eecf7cbf4139aca028fd6c57f49c075717bcf8da 100644 (file)
@@ -215,7 +215,6 @@ extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot);
 extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot);
 extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot);
 extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot);
-extern u32 acpiphp_get_address (struct acpiphp_slot *slot);
 
 /* variables */
 extern int acpiphp_debug;
index 7af68ba279036277a9f689683b9b00eaa98481be..0e496e866a84dc51480d4cb3194e1ea6b62023c8 100644 (file)
@@ -70,7 +70,6 @@ static int disable_slot               (struct hotplug_slot *slot);
 static int set_attention_status (struct hotplug_slot *slot, u8 value);
 static int get_power_status    (struct hotplug_slot *slot, u8 *value);
 static int get_attention_status (struct hotplug_slot *slot, u8 *value);
-static int get_address         (struct hotplug_slot *slot, u32 *value);
 static int get_latch_status    (struct hotplug_slot *slot, u8 *value);
 static int get_adapter_status  (struct hotplug_slot *slot, u8 *value);
 
@@ -83,7 +82,6 @@ static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
        .get_attention_status   = get_attention_status,
        .get_latch_status       = get_latch_status,
        .get_adapter_status     = get_adapter_status,
-       .get_address            = get_address,
 };
 
 
@@ -274,23 +272,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
        return 0;
 }
 
-
-/**
- * get_address - get pci address of a slot
- * @hotplug_slot: slot to get status
- * @value: pointer to struct pci_busdev (seg, bus, dev)
- */
-static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
-{
-       struct slot *slot = hotplug_slot->private;
-
-       dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
-
-       *value = acpiphp_get_address(slot->acpi_slot);
-
-       return 0;
-}
-
 static int __init init_acpi(void)
 {
        int retval;
@@ -357,7 +338,11 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
        acpiphp_slot->slot = slot;
        snprintf(slot->name, sizeof(slot->name), "%u", slot->acpi_slot->sun);
 
-       retval = pci_hp_register(slot->hotplug_slot);
+       retval = pci_hp_register(slot->hotplug_slot,
+                                       acpiphp_slot->bridge->pci_bus,
+                                       acpiphp_slot->device);
+       if (retval == -EBUSY)
+               goto error_hpslot;
        if (retval) {
                err("pci_hp_register failed with error %d\n", retval);
                goto error_hpslot;
index 91156f85a9267371c1c9354646bdcc6975e206af..a3e4705dd8f0f11ef477ddfeaec284990780c87f 100644 (file)
@@ -258,7 +258,12 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
                                bridge->pci_bus->number, slot->device);
                retval = acpiphp_register_hotplug_slot(slot);
                if (retval) {
-                       warn("acpiphp_register_hotplug_slot failed(err code = 0x%x)\n", retval);
+                       if (retval == -EBUSY)
+                               warn("Slot %d already registered by another "
+                                       "hotplug driver\n", slot->sun);
+                       else
+                               warn("acpiphp_register_hotplug_slot failed "
+                                       "(err code = 0x%x)\n", retval);
                        goto err_exit;
                }
        }
@@ -1878,19 +1883,3 @@ u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot)
 
        return (sta == 0) ? 0 : 1;
 }
-
-
-/*
- * pci address (seg/bus/dev)
- */
-u32 acpiphp_get_address(struct acpiphp_slot *slot)
-{
-       u32 address;
-       struct pci_bus *pci_bus = slot->bridge->pci_bus;
-
-       address = (pci_domain_nr(pci_bus) << 16) |
-                 (pci_bus->number << 8) |
-                 slot->device;
-
-       return address;
-}
index ede9051fdb5d848b3ee2dfe64d7cb7bccc18f2c2..2b7c45e39370f1e42fa0546f30cf6e6c7e9356f8 100644 (file)
 #include <linux/kobject.h>
 #include <asm/uaccess.h>
 #include <linux/moduleparam.h>
+#include <linux/pci.h>
 
 #include "acpiphp.h"
+#include "../pci.h"
 
 #define DRIVER_VERSION "1.0.1"
 #define DRIVER_AUTHOR  "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
@@ -430,7 +432,7 @@ static int __init ibm_acpiphp_init(void)
        int retval = 0;
        acpi_status status;
        struct acpi_device *device;
-       struct kobject *sysdir = &pci_hotplug_slots_kset->kobj;
+       struct kobject *sysdir = &pci_slots_kset->kobj;
 
        dbg("%s\n", __func__);
 
@@ -477,7 +479,7 @@ init_return:
 static void __exit ibm_acpiphp_exit(void)
 {
        acpi_status status;
-       struct kobject *sysdir = &pci_hotplug_slots_kset->kobj;
+       struct kobject *sysdir = &pci_slots_kset->kobj;
 
        dbg("%s\n", __func__);
 
index d8a6b80ab42aa92461ba513404a7a8fdabe5c95a..935947991dc98649dfaa8794fb55604ee12cb895 100644 (file)
@@ -285,7 +285,7 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
                info->attention_status = cpci_get_attention_status(slot);
 
                dbg("registering slot %s", slot->hotplug_slot->name);
-               status = pci_hp_register(slot->hotplug_slot);
+               status = pci_hp_register(slot->hotplug_slot, bus, i);
                if (status) {
                        err("pci_hp_register failed with error %d", status);
                        goto error_name;
index 36b115b27b0b812bebe943933c2aa58e9966789f..54defec51d0895f7dd2136230fa56e87e8feac3b 100644 (file)
@@ -434,7 +434,9 @@ static int ctrl_slot_setup(struct controller *ctrl,
                                slot->bus, slot->device,
                                slot->number, ctrl->slot_device_offset,
                                slot_number);
-               result = pci_hp_register(hotplug_slot);
+               result = pci_hp_register(hotplug_slot,
+                                        ctrl->pci_dev->subordinate,
+                                        slot->device);
                if (result) {
                        err("pci_hp_register failed with error %d\n", result);
                        goto error_name;
index 7e9a827c268799ed4c51b84b829fca03a3dfe5a7..40337a06c18ab7defa6160af363df122143545a9 100644 (file)
@@ -66,6 +66,7 @@ struct dummy_slot {
        struct pci_dev *dev;
        struct work_struct remove_work;
        unsigned long removed;
+       char name[8];
 };
 
 static int debug;
@@ -100,6 +101,7 @@ static int add_slot(struct pci_dev *dev)
        struct dummy_slot *dslot;
        struct hotplug_slot *slot;
        int retval = -ENOMEM;
+       static int count = 1;
 
        slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
        if (!slot)
@@ -113,18 +115,18 @@ static int add_slot(struct pci_dev *dev)
        slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
        slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
 
-       slot->name = &dev->dev.bus_id[0];
-       dbg("slot->name = %s\n", slot->name);
-
        dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL);
        if (!dslot)
                goto error_info;
 
+       slot->name = dslot->name;
+       snprintf(slot->name, sizeof(dslot->name), "fake%d", count++);
+       dbg("slot->name = %s\n", slot->name);
        slot->ops = &dummy_hotplug_slot_ops;
        slot->release = &dummy_release;
        slot->private = dslot;
 
-       retval = pci_hp_register(slot);
+       retval = pci_hp_register(slot, dev->bus, PCI_SLOT(dev->devfn));
        if (retval) {
                err("pci_hp_register failed with error %d\n", retval);
                goto error_dslot;
@@ -148,17 +150,17 @@ error:
 static int __init pci_scan_buses(void)
 {
        struct pci_dev *dev = NULL;
-       int retval = 0;
+       int lastslot = 0;
 
        while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
-               retval = add_slot(dev);
-               if (retval) {
-                       pci_dev_put(dev);
-                       break;
-               }
+               if (PCI_FUNC(dev->devfn) > 0 &&
+                               lastslot == PCI_SLOT(dev->devfn))
+                       continue;
+               lastslot = PCI_SLOT(dev->devfn);
+               add_slot(dev);
        }
 
-       return retval;
+       return 0;
 }
 
 static void remove_slot(struct dummy_slot *dslot)
@@ -296,23 +298,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
        return 0;
 }
 
-/* find the hotplug_slot for the pci_dev */
-static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev)
-{
-       struct dummy_slot *dslot;
-
-       list_for_each_entry(dslot, &slot_list, node) {
-               if (dslot->dev == dev)
-                       return dslot->slot;
-       }
-       return NULL;
-}
-
-
 static int disable_slot(struct hotplug_slot *slot)
 {
        struct dummy_slot *dslot;
-       struct hotplug_slot *hslot;
        struct pci_dev *dev;
        int func;
 
@@ -322,41 +310,27 @@ static int disable_slot(struct hotplug_slot *slot)
 
        dbg("%s - physical_slot = %s\n", __func__, slot->name);
 
-       /* don't disable bridged devices just yet, we can't handle them easily... */
-       if (dslot->dev->subordinate) {
-               err("Can't remove PCI devices with other PCI devices behind it yet.\n");
-               return -ENODEV;
-       }
-       if (test_and_set_bit(0, &dslot->removed)) {
-               dbg("Slot already scheduled for removal\n");
-               return -ENODEV;
-       }
-       /* search for subfunctions and disable them first */
-       if (!(dslot->dev->devfn & 7)) {
-               for (func = 1; func < 8; func++) {
-                       dev = pci_get_slot(dslot->dev->bus,
-                                       dslot->dev->devfn + func);
-                       if (dev) {
-                               hslot = get_slot_from_dev(dev);
-                               if (hslot)
-                                       disable_slot(hslot);
-                               else {
-                                       err("Hotplug slot not found for subfunction of PCI device\n");
-                                       return -ENODEV;
-                               }
-                               pci_dev_put(dev);
-                       } else
-                               dbg("No device in slot found\n");
+       for (func = 7; func >= 0; func--) {
+               dev = pci_get_slot(dslot->dev->bus, dslot->dev->devfn + func);
+               if (!dev)
+                       continue;
+
+               if (test_and_set_bit(0, &dslot->removed)) {
+                       dbg("Slot already scheduled for removal\n");
+                       return -ENODEV;
                }
-       }
 
-       /* remove the device from the pci core */
-       pci_remove_bus_device(dslot->dev);
+               /* queue work item to blow away this sysfs entry and other
+                * parts.
+                */
+               INIT_WORK(&dslot->remove_work, remove_slot_worker);
+               queue_work(dummyphp_wq, &dslot->remove_work);
 
-       /* queue work item to blow away this sysfs entry and other parts. */
-       INIT_WORK(&dslot->remove_work, remove_slot_worker);
-       queue_work(dummyphp_wq, &dslot->remove_work);
+               /* blow away this sysfs entry and other parts. */
+               remove_slot(dslot);
 
+               pci_dev_put(dev);
+       }
        return 0;
 }
 
index dca7efc14be27d12b6ea29ee387a32843e27af03..8467d028732514277a098815f3597ebfed1591ae 100644 (file)
@@ -1001,7 +1001,8 @@ static int __init ebda_rsrc_controller (void)
                tmp_slot = list_entry (list, struct slot, ibm_slot_list);
 
                snprintf (tmp_slot->hotplug_slot->name, 30, "%s", create_file_name (tmp_slot));
-               pci_hp_register (tmp_slot->hotplug_slot);
+               pci_hp_register(tmp_slot->hotplug_slot,
+                       pci_find_bus(0, tmp_slot->bus), tmp_slot->device);
        }
 
        print_ebda_hpc ();
index a11021e8ce37bb0cc3e76f182dfbd84027ff8d9f..5f85b1b120e3ddc12c350e2b821c18946ec90e27 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/pci.h>
 #include <linux/pci_hotplug.h>
 #include <asm/uaccess.h>
+#include "../pci.h"
 
 #define MY_NAME        "pci_hotplug"
 
@@ -60,41 +61,7 @@ static int debug;
 //////////////////////////////////////////////////////////////////
 
 static LIST_HEAD(pci_hotplug_slot_list);
-
-struct kset *pci_hotplug_slots_kset;
-
-static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
-               struct attribute *attr, char *buf)
-{
-       struct hotplug_slot *slot = to_hotplug_slot(kobj);
-       struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
-       return attribute->show ? attribute->show(slot, buf) : -EIO;
-}
-
-static ssize_t hotplug_slot_attr_store(struct kobject *kobj,
-               struct attribute *attr, const char *buf, size_t len)
-{
-       struct hotplug_slot *slot = to_hotplug_slot(kobj);
-       struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
-       return attribute->store ? attribute->store(slot, buf, len) : -EIO;
-}
-
-static struct sysfs_ops hotplug_slot_sysfs_ops = {
-       .show = hotplug_slot_attr_show,
-       .store = hotplug_slot_attr_store,
-};
-
-static void hotplug_slot_release(struct kobject *kobj)
-{
-       struct hotplug_slot *slot = to_hotplug_slot(kobj);
-       if (slot->release)
-               slot->release(slot);
-}
-
-static struct kobj_type hotplug_slot_ktype = {
-       .sysfs_ops = &hotplug_slot_sysfs_ops,
-       .release = &hotplug_slot_release,
-};
+static DEFINE_SPINLOCK(pci_hotplug_slot_list_lock);
 
 /* these strings match up with the values in pci_bus_speed */
 static char *pci_bus_speed_strings[] = {
@@ -149,16 +116,15 @@ GET_STATUS(power_status, u8)
 GET_STATUS(attention_status, u8)
 GET_STATUS(latch_status, u8)
 GET_STATUS(adapter_status, u8)
-GET_STATUS(address, u32)
 GET_STATUS(max_bus_speed, enum pci_bus_speed)
 GET_STATUS(cur_bus_speed, enum pci_bus_speed)
 
-static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t power_read_file(struct pci_slot *slot, char *buf)
 {
        int retval;
        u8 value;
 
-       retval = get_power_status (slot, &value);
+       retval = get_power_status(slot->hotplug, &value);
        if (retval)
                goto exit;
        retval = sprintf (buf, "%d\n", value);
@@ -166,9 +132,10 @@ exit:
        return retval;
 }
 
-static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
                size_t count)
 {
+       struct hotplug_slot *slot = pci_slot->hotplug;
        unsigned long lpower;
        u8 power;
        int retval = 0;
@@ -204,29 +171,30 @@ exit:
        return count;
 }
 
-static struct hotplug_slot_attribute hotplug_slot_attr_power = {
+static struct pci_slot_attribute hotplug_slot_attr_power = {
        .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
        .show = power_read_file,
        .store = power_write_file
 };
 
-static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t attention_read_file(struct pci_slot *slot, char *buf)
 {
        int retval;
        u8 value;
 
-       retval = get_attention_status (slot, &value);
+       retval = get_attention_status(slot->hotplug, &value);
        if (retval)
                goto exit;
-       retval = sprintf (buf, "%d\n", value);
+       retval = sprintf(buf, "%d\n", value);
 
 exit:
        return retval;
 }
 
-static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
                size_t count)
 {
+       struct hotplug_slot_ops *ops = slot->hotplug->ops;
        unsigned long lattention;
        u8 attention;
        int retval = 0;
@@ -235,13 +203,13 @@ static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
        attention = (u8)(lattention & 0xff);
        dbg (" - attention = %d\n", attention);
 
-       if (!try_module_get(slot->ops->owner)) {
+       if (!try_module_get(ops->owner)) {
                retval = -ENODEV;
                goto exit;
        }
-       if (slot->ops->set_attention_status)
-               retval = slot->ops->set_attention_status(slot, attention);
-       module_put(slot->ops->owner);
+       if (ops->set_attention_status)
+               retval = ops->set_attention_status(slot->hotplug, attention);
+       module_put(ops->owner);
 
 exit:  
        if (retval)
@@ -249,18 +217,18 @@ exit:
        return count;
 }
 
-static struct hotplug_slot_attribute hotplug_slot_attr_attention = {
+static struct pci_slot_attribute hotplug_slot_attr_attention = {
        .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
        .show = attention_read_file,
        .store = attention_write_file
 };
 
-static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t latch_read_file(struct pci_slot *slot, char *buf)
 {
        int retval;
        u8 value;
 
-       retval = get_latch_status (slot, &value);
+       retval = get_latch_status(slot->hotplug, &value);
        if (retval)
                goto exit;
        retval = sprintf (buf, "%d\n", value);
@@ -269,17 +237,17 @@ exit:
        return retval;
 }
 
-static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
+static struct pci_slot_attribute hotplug_slot_attr_latch = {
        .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
        .show = latch_read_file,
 };
 
-static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t presence_read_file(struct pci_slot *slot, char *buf)
 {
        int retval;
        u8 value;
 
-       retval = get_adapter_status (slot, &value);
+       retval = get_adapter_status(slot->hotplug, &value);
        if (retval)
                goto exit;
        retval = sprintf (buf, "%d\n", value);
@@ -288,42 +256,20 @@ exit:
        return retval;
 }
 
-static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
+static struct pci_slot_attribute hotplug_slot_attr_presence = {
        .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
        .show = presence_read_file,
 };
 
-static ssize_t address_read_file (struct hotplug_slot *slot, char *buf)
-{
-       int retval;
-       u32 address;
-
-       retval = get_address (slot, &address);
-       if (retval)
-               goto exit;
-       retval = sprintf (buf, "%04x:%02x:%02x\n",
-                         (address >> 16) & 0xffff,
-                         (address >> 8) & 0xff,
-                         address & 0xff);
-
-exit:
-       return retval;
-}
-
-static struct hotplug_slot_attribute hotplug_slot_attr_address = {
-       .attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
-       .show = address_read_file,
-};
-
 static char *unknown_speed = "Unknown bus speed";
 
-static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf)
 {
        char *speed_string;
        int retval;
        enum pci_bus_speed value;
        
-       retval = get_max_bus_speed (slot, &value);
+       retval = get_max_bus_speed(slot->hotplug, &value);
        if (retval)
                goto exit;
 
@@ -338,18 +284,18 @@ exit:
        return retval;
 }
 
-static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = {
        .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
        .show = max_bus_speed_read_file,
 };
 
-static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf)
 {
        char *speed_string;
        int retval;
        enum pci_bus_speed value;
 
-       retval = get_cur_bus_speed (slot, &value);
+       retval = get_cur_bus_speed(slot->hotplug, &value);
        if (retval)
                goto exit;
 
@@ -364,14 +310,15 @@ exit:
        return retval;
 }
 
-static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = {
        .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
        .show = cur_bus_speed_read_file,
 };
 
-static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
                size_t count)
 {
+       struct hotplug_slot *slot = pci_slot->hotplug;
        unsigned long ltest;
        u32 test;
        int retval = 0;
@@ -394,13 +341,14 @@ exit:
        return count;
 }
 
-static struct hotplug_slot_attribute hotplug_slot_attr_test = {
+static struct pci_slot_attribute hotplug_slot_attr_test = {
        .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
        .store = test_write_file
 };
 
-static int has_power_file (struct hotplug_slot *slot)
+static int has_power_file(struct pci_slot *pci_slot)
 {
+       struct hotplug_slot *slot = pci_slot->hotplug;
        if ((!slot) || (!slot->ops))
                return -ENODEV;
        if ((slot->ops->enable_slot) ||
@@ -410,8 +358,9 @@ static int has_power_file (struct hotplug_slot *slot)
        return -ENOENT;
 }
 
-static int has_attention_file (struct hotplug_slot *slot)
+static int has_attention_file(struct pci_slot *pci_slot)
 {
+       struct hotplug_slot *slot = pci_slot->hotplug;
        if ((!slot) || (!slot->ops))
                return -ENODEV;
        if ((slot->ops->set_attention_status) ||
@@ -420,8 +369,9 @@ static int has_attention_file (struct hotplug_slot *slot)
        return -ENOENT;
 }
 
-static int has_latch_file (struct hotplug_slot *slot)
+static int has_latch_file(struct pci_slot *pci_slot)
 {
+       struct hotplug_slot *slot = pci_slot->hotplug;
        if ((!slot) || (!slot->ops))
                return -ENODEV;
        if (slot->ops->get_latch_status)
@@ -429,8 +379,9 @@ static int has_latch_file (struct hotplug_slot *slot)
        return -ENOENT;
 }
 
-static int has_adapter_file (struct hotplug_slot *slot)
+static int has_adapter_file(struct pci_slot *pci_slot)
 {
+       struct hotplug_slot *slot = pci_slot->hotplug;
        if ((!slot) || (!slot->ops))
                return -ENODEV;
        if (slot->ops->get_adapter_status)
@@ -438,17 +389,9 @@ static int has_adapter_file (struct hotplug_slot *slot)
        return -ENOENT;
 }
 
-static int has_address_file (struct hotplug_slot *slot)
-{
-       if ((!slot) || (!slot->ops))
-               return -ENODEV;
-       if (slot->ops->get_address)
-               return 0;
-       return -ENOENT;
-}
-
-static int has_max_bus_speed_file (struct hotplug_slot *slot)
+static int has_max_bus_speed_file(struct pci_slot *pci_slot)
 {
+       struct hotplug_slot *slot = pci_slot->hotplug;
        if ((!slot) || (!slot->ops))
                return -ENODEV;
        if (slot->ops->get_max_bus_speed)
@@ -456,8 +399,9 @@ static int has_max_bus_speed_file (struct hotplug_slot *slot)
        return -ENOENT;
 }
 
-static int has_cur_bus_speed_file (struct hotplug_slot *slot)
+static int has_cur_bus_speed_file(struct pci_slot *pci_slot)
 {
+       struct hotplug_slot *slot = pci_slot->hotplug;
        if ((!slot) || (!slot->ops))
                return -ENODEV;
        if (slot->ops->get_cur_bus_speed)
@@ -465,8 +409,9 @@ static int has_cur_bus_speed_file (struct hotplug_slot *slot)
        return -ENOENT;
 }
 
-static int has_test_file (struct hotplug_slot *slot)
+static int has_test_file(struct pci_slot *pci_slot)
 {
+       struct hotplug_slot *slot = pci_slot->hotplug;
        if ((!slot) || (!slot->ops))
                return -ENODEV;
        if (slot->ops->hardware_test)
@@ -474,7 +419,7 @@ static int has_test_file (struct hotplug_slot *slot)
        return -ENOENT;
 }
 
-static int fs_add_slot (struct hotplug_slot *slot)
+static int fs_add_slot(struct pci_slot *slot)
 {
        int retval = 0;
 
@@ -505,13 +450,6 @@ static int fs_add_slot (struct hotplug_slot *slot)
                        goto exit_adapter;
        }
 
-       if (has_address_file(slot) == 0) {
-               retval = sysfs_create_file(&slot->kobj,
-                                          &hotplug_slot_attr_address.attr);
-               if (retval)
-                       goto exit_address;
-       }
-
        if (has_max_bus_speed_file(slot) == 0) {
                retval = sysfs_create_file(&slot->kobj,
                                           &hotplug_slot_attr_max_bus_speed.attr);
@@ -544,10 +482,6 @@ exit_cur_speed:
                sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
 
 exit_max_speed:
-       if (has_address_file(slot) == 0)
-               sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
-exit_address:
        if (has_adapter_file(slot) == 0)
                sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
 
@@ -567,7 +501,7 @@ exit:
        return retval;
 }
 
-static void fs_remove_slot (struct hotplug_slot *slot)
+static void fs_remove_slot(struct pci_slot *slot)
 {
        if (has_power_file(slot) == 0)
                sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
@@ -581,9 +515,6 @@ static void fs_remove_slot (struct hotplug_slot *slot)
        if (has_adapter_file(slot) == 0)
                sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
 
-       if (has_address_file(slot) == 0)
-               sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
        if (has_max_bus_speed_file(slot) == 0)
                sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
 
@@ -599,27 +530,33 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
        struct hotplug_slot *slot;
        struct list_head *tmp;
 
+       spin_lock(&pci_hotplug_slot_list_lock);
        list_for_each (tmp, &pci_hotplug_slot_list) {
                slot = list_entry (tmp, struct hotplug_slot, slot_list);
                if (strcmp(slot->name, name) == 0)
-                       return slot;
+                       goto out;
        }
-       return NULL;
+       slot = NULL;
+out:
+       spin_unlock(&pci_hotplug_slot_list_lock);
+       return slot;
 }
 
 /**
  * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
+ * @bus: bus this slot is on
  * @slot: pointer to the &struct hotplug_slot to register
+ * @slot_nr: slot number
  *
  * Registers a hotplug slot with the pci hotplug subsystem, which will allow
  * userspace interaction to the slot.
  *
  * Returns 0 if successful, anything else for an error.
  */
-int pci_hp_register (struct hotplug_slot *slot)
+int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr)
 {
        int result;
-       struct hotplug_slot *tmp;
+       struct pci_slot *pci_slot;
 
        if (slot == NULL)
                return -ENODEV;
@@ -632,57 +569,89 @@ int pci_hp_register (struct hotplug_slot *slot)
        }
 
        /* Check if we have already registered a slot with the same name. */
-       tmp = get_slot_from_name(slot->name);
-       if (tmp)
+       if (get_slot_from_name(slot->name))
                return -EEXIST;
 
-       slot->kobj.kset = pci_hotplug_slots_kset;
-       result = kobject_init_and_add(&slot->kobj, &hotplug_slot_ktype, NULL,
-                                     "%s", slot->name);
-       if (result) {
-               err("Unable to register kobject '%s'", slot->name);
-               return -EINVAL;
+       /*
+        * No problems if we call this interface from both ACPI_PCI_SLOT
+        * driver and call it here again. If we've already created the
+        * pci_slot, the interface will simply bump the refcount.
+        */
+       pci_slot = pci_create_slot(bus, slot_nr, slot->name);
+       if (IS_ERR(pci_slot))
+               return PTR_ERR(pci_slot);
+
+       if (pci_slot->hotplug) {
+               dbg("%s: already claimed\n", __func__);
+               pci_destroy_slot(pci_slot);
+               return -EBUSY;
        }
 
-       list_add (&slot->slot_list, &pci_hotplug_slot_list);
+       slot->pci_slot = pci_slot;
+       pci_slot->hotplug = slot;
+
+       /*
+        * Allow pcihp drivers to override the ACPI_PCI_SLOT name.
+        */
+       if (strcmp(kobject_name(&pci_slot->kobj), slot->name)) {
+               result = kobject_rename(&pci_slot->kobj, slot->name);
+               if (result) {
+                       pci_destroy_slot(pci_slot);
+                       return result;
+               }
+       }
+
+       spin_lock(&pci_hotplug_slot_list_lock);
+       list_add(&slot->slot_list, &pci_hotplug_slot_list);
+       spin_unlock(&pci_hotplug_slot_list_lock);
+
+       result = fs_add_slot(pci_slot);
+       kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
+       dbg("Added slot %s to the list\n", slot->name);
+
 
-       result = fs_add_slot (slot);
-       kobject_uevent(&slot->kobj, KOBJ_ADD);
-       dbg ("Added slot %s to the list\n", slot->name);
        return result;
 }
 
 /**
  * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
- * @slot: pointer to the &struct hotplug_slot to deregister
+ * @hotplug: pointer to the &struct hotplug_slot to deregister
  *
  * The @slot must have been registered with the pci hotplug subsystem
  * previously with a call to pci_hp_register().
  *
  * Returns 0 if successful, anything else for an error.
  */
-int pci_hp_deregister (struct hotplug_slot *slot)
+int pci_hp_deregister(struct hotplug_slot *hotplug)
 {
        struct hotplug_slot *temp;
+       struct pci_slot *slot;
 
-       if (slot == NULL)
+       if (!hotplug)
                return -ENODEV;
 
-       temp = get_slot_from_name (slot->name);
-       if (temp != slot) {
+       temp = get_slot_from_name(hotplug->name);
+       if (temp != hotplug)
                return -ENODEV;
-       }
-       list_del (&slot->slot_list);
 
-       fs_remove_slot (slot);
-       dbg ("Removed slot %s from the list\n", slot->name);
-       kobject_put(&slot->kobj);
+       spin_lock(&pci_hotplug_slot_list_lock);
+       list_del(&hotplug->slot_list);
+       spin_unlock(&pci_hotplug_slot_list_lock);
+
+       slot = hotplug->pci_slot;
+       fs_remove_slot(slot);
+       dbg("Removed slot %s from the list\n", hotplug->name);
+
+       hotplug->release(hotplug);
+       slot->hotplug = NULL;
+       pci_destroy_slot(slot);
+
        return 0;
 }
 
 /**
  * pci_hp_change_slot_info - changes the slot's information structure in the core
- * @slot: pointer to the slot whose info has changed
+ * @hotplug: pointer to the slot whose info has changed
  * @info: pointer to the info copy into the slot's info structure
  *
  * @slot must have been registered with the pci 
@@ -690,13 +659,15 @@ int pci_hp_deregister (struct hotplug_slot *slot)
  *
  * Returns 0 if successful, anything else for an error.
  */
-int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
+int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug,
                                         struct hotplug_slot_info *info)
 {
-       if ((slot == NULL) || (info == NULL))
+       struct pci_slot *slot;
+       if (!hotplug || !info)
                return -ENODEV;
+       slot = hotplug->pci_slot;
 
-       memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
+       memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));
 
        return 0;
 }
@@ -704,36 +675,22 @@ int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
 static int __init pci_hotplug_init (void)
 {
        int result;
-       struct kset *pci_bus_kset;
 
-       pci_bus_kset = bus_get_kset(&pci_bus_type);
-
-       pci_hotplug_slots_kset = kset_create_and_add("slots", NULL,
-                                                    &pci_bus_kset->kobj);
-       if (!pci_hotplug_slots_kset) {
-               result = -ENOMEM;
-               err("Register subsys error\n");
-               goto exit;
-       }
        result = cpci_hotplug_init(debug);
        if (result) {
                err ("cpci_hotplug_init with error %d\n", result);
-               goto err_subsys;
+               goto err_cpci;
        }
 
        info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
-       goto exit;
 
-err_subsys:
-       kset_unregister(pci_hotplug_slots_kset);
-exit:
+err_cpci:
        return result;
 }
 
 static void __exit pci_hotplug_exit (void)
 {
        cpci_hotplug_exit();
-       kset_unregister(pci_hotplug_slots_kset);
 }
 
 module_init(pci_hotplug_init);
@@ -745,7 +702,6 @@ MODULE_LICENSE("GPL");
 module_param(debug, bool, 0644);
 MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
 
-EXPORT_SYMBOL_GPL(pci_hotplug_slots_kset);
 EXPORT_SYMBOL_GPL(pci_hp_register);
 EXPORT_SYMBOL_GPL(pci_hp_deregister);
 EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
index 79c9ddaad3fb4e9b7754f951b9845476e0d1c5f3..e3a1e7e7dba246b6bc35a16dbd8a0a3f00ee885e 100644 (file)
@@ -43,6 +43,7 @@ extern int pciehp_poll_mode;
 extern int pciehp_poll_time;
 extern int pciehp_debug;
 extern int pciehp_force;
+extern int pciehp_slot_with_bus;
 extern struct workqueue_struct *pciehp_wq;
 
 #define dbg(format, arg...)                                            \
@@ -96,7 +97,7 @@ struct controller {
        u32 slot_cap;
        u8 cap_base;
        struct timer_list poll_timer;
-       volatile int cmd_busy;
+       int cmd_busy;
        unsigned int no_cmd_complete:1;
 };
 
@@ -156,10 +157,10 @@ extern u8 pciehp_handle_power_fault(struct slot *p_slot);
 extern int pciehp_configure_device(struct slot *p_slot);
 extern int pciehp_unconfigure_device(struct slot *p_slot);
 extern void pciehp_queue_pushbutton_work(struct work_struct *work);
-int pcie_init(struct controller *ctrl, struct pcie_device *dev);
+struct controller *pcie_init(struct pcie_device *dev);
 int pciehp_enable_slot(struct slot *p_slot);
 int pciehp_disable_slot(struct slot *p_slot);
-int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev);
+int pcie_enable_notification(struct controller *ctrl);
 
 static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device)
 {
@@ -202,8 +203,13 @@ struct hpc_ops {
 #include <acpi/actypes.h>
 #include <linux/pci-acpi.h>
 
-#define pciehp_get_hp_hw_control_from_firmware(dev)                    \
-       pciehp_acpi_get_hp_hw_control_from_firmware(dev)
+static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
+{
+       u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
+                    OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+       return acpi_get_hp_hw_control_from_firmware(dev, flags);
+}
+
 static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev,
                        struct hotplug_params *hpp)
 {
index 48a2ed378914f383056d7e803ad4c58b044b3ec4..3677495c4f91e2bf827c7d863d431be4272c3c5a 100644 (file)
@@ -72,7 +72,6 @@ static int get_power_status   (struct hotplug_slot *slot, u8 *value);
 static int get_attention_status        (struct hotplug_slot *slot, u8 *value);
 static int get_latch_status    (struct hotplug_slot *slot, u8 *value);
 static int get_adapter_status  (struct hotplug_slot *slot, u8 *value);
-static int get_address         (struct hotplug_slot *slot, u32 *value);
 static int get_max_bus_speed   (struct hotplug_slot *slot, enum pci_bus_speed *value);
 static int get_cur_bus_speed   (struct hotplug_slot *slot, enum pci_bus_speed *value);
 
@@ -85,7 +84,6 @@ static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
        .get_attention_status = get_attention_status,
        .get_latch_status =     get_latch_status,
        .get_adapter_status =   get_adapter_status,
-       .get_address =          get_address,
        .get_max_bus_speed =    get_max_bus_speed,
        .get_cur_bus_speed =    get_cur_bus_speed,
 };
@@ -185,23 +183,10 @@ static struct hotplug_slot_attribute hotplug_slot_attr_lock = {
  */
 static void release_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct slot *slot = hotplug_slot->private;
-
        dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
 
-       kfree(slot->hotplug_slot->info);
-       kfree(slot->hotplug_slot);
-       kfree(slot);
-}
-
-static void make_slot_name(struct slot *slot)
-{
-       if (pciehp_slot_with_bus)
-               snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%04d_%04d",
-                        slot->bus, slot->number);
-       else
-               snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d",
-                        slot->number);
+       kfree(hotplug_slot->info);
+       kfree(hotplug_slot);
 }
 
 static int init_slots(struct controller *ctrl)
@@ -210,49 +195,34 @@ static int init_slots(struct controller *ctrl)
        struct hotplug_slot *hotplug_slot;
        struct hotplug_slot_info *info;
        int retval = -ENOMEM;
-       int i;
-
-       for (i = 0; i < ctrl->num_slots; i++) {
-               slot = kzalloc(sizeof(*slot), GFP_KERNEL);
-               if (!slot)
-                       goto error;
 
+       list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
                hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
                if (!hotplug_slot)
-                       goto error_slot;
-               slot->hotplug_slot = hotplug_slot;
+                       goto error;
 
                info = kzalloc(sizeof(*info), GFP_KERNEL);
                if (!info)
                        goto error_hpslot;
-               hotplug_slot->info = info;
-
-               hotplug_slot->name = slot->name;
-
-               slot->hp_slot = i;
-               slot->ctrl = ctrl;
-               slot->bus = ctrl->pci_dev->subordinate->number;
-               slot->device = ctrl->slot_device_offset + i;
-               slot->hpc_ops = ctrl->hpc_ops;
-               slot->number = ctrl->first_slot;
-               mutex_init(&slot->lock);
-               INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);
 
                /* register this slot with the hotplug pci core */
+               hotplug_slot->info = info;
+               hotplug_slot->name = slot->name;
                hotplug_slot->private = slot;
                hotplug_slot->release = &release_slot;
-               make_slot_name(slot);
                hotplug_slot->ops = &pciehp_hotplug_slot_ops;
-
                get_power_status(hotplug_slot, &info->power_status);
                get_attention_status(hotplug_slot, &info->attention_status);
                get_latch_status(hotplug_slot, &info->latch_status);
                get_adapter_status(hotplug_slot, &info->adapter_status);
+               slot->hotplug_slot = hotplug_slot;
 
                dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
                    "slot_device_offset=%x\n", slot->bus, slot->device,
                    slot->hp_slot, slot->number, ctrl->slot_device_offset);
-               retval = pci_hp_register(hotplug_slot);
+               retval = pci_hp_register(hotplug_slot,
+                                        ctrl->pci_dev->subordinate,
+                                        slot->device);
                if (retval) {
                        err("pci_hp_register failed with error %d\n", retval);
                        if (retval == -EEXIST)
@@ -263,7 +233,7 @@ static int init_slots(struct controller *ctrl)
                }
                /* create additional sysfs entries */
                if (EMI(ctrl)) {
-                       retval = sysfs_create_file(&hotplug_slot->kobj,
+                       retval = sysfs_create_file(&hotplug_slot->pci_slot->kobj,
                                &hotplug_slot_attr_lock.attr);
                        if (retval) {
                                pci_hp_deregister(hotplug_slot);
@@ -271,8 +241,6 @@ static int init_slots(struct controller *ctrl)
                                goto error_info;
                        }
                }
-
-               list_add(&slot->slot_list, &ctrl->slot_list);
        }
 
        return 0;
@@ -280,27 +248,18 @@ error_info:
        kfree(info);
 error_hpslot:
        kfree(hotplug_slot);
-error_slot:
-       kfree(slot);
 error:
        return retval;
 }
 
 static void cleanup_slots(struct controller *ctrl)
 {
-       struct list_head *tmp;
-       struct list_head *next;
        struct slot *slot;
 
-       list_for_each_safe(tmp, next, &ctrl->slot_list) {
-               slot = list_entry(tmp, struct slot, slot_list);
-               list_del(&slot->slot_list);
+       list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
                if (EMI(ctrl))
-                       sysfs_remove_file(&slot->hotplug_slot->kobj,
+                       sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj,
                                &hotplug_slot_attr_lock.attr);
-               cancel_delayed_work(&slot->work);
-               flush_scheduled_work();
-               flush_workqueue(pciehp_wq);
                pci_hp_deregister(slot->hotplug_slot);
        }
 }
@@ -398,19 +357,8 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
        return 0;
 }
 
-static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
-{
-       struct slot *slot = hotplug_slot->private;
-       struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
-
-       dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
-
-       *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
-
-       return 0;
-}
-
-static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+static int get_max_bus_speed(struct hotplug_slot *hotplug_slot,
+                               enum pci_bus_speed *value)
 {
        struct slot *slot = hotplug_slot->private;
        int retval;
@@ -444,34 +392,30 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
        struct controller *ctrl;
        struct slot *t_slot;
        u8 value;
-       struct pci_dev *pdev;
+       struct pci_dev *pdev = dev->port;
 
-       ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
-       if (!ctrl) {
-               err("%s : out of memory\n", __func__);
+       if (pciehp_force)
+               dbg("Bypassing BIOS check for pciehp use on %s\n",
+                   pci_name(pdev));
+       else if (pciehp_get_hp_hw_control_from_firmware(pdev))
                goto err_out_none;
-       }
-       INIT_LIST_HEAD(&ctrl->slot_list);
-
-       pdev = dev->port;
-       ctrl->pci_dev = pdev;
 
-       rc = pcie_init(ctrl, dev);
-       if (rc) {
+       ctrl = pcie_init(dev);
+       if (!ctrl) {
                dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME);
-               goto err_out_free_ctrl;
+               goto err_out_none;
        }
-
-       pci_set_drvdata(pdev, ctrl);
-
-       dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n",
-           __func__, pdev->bus->number, PCI_SLOT(pdev->devfn),
-           PCI_FUNC(pdev->devfn), pdev->irq);
+       set_service_data(dev, ctrl);
 
        /* Setup the slot information structures */
        rc = init_slots(ctrl);
        if (rc) {
-               err("%s: slot initialization failed\n", PCIE_MODULE_NAME);
+               if (rc == -EBUSY)
+                       warn("%s: slot already registered by another "
+                               "hotplug driver\n", PCIE_MODULE_NAME);
+               else
+                       err("%s: slot initialization failed\n",
+                               PCIE_MODULE_NAME);
                goto err_out_release_ctlr;
        }
 
@@ -495,20 +439,16 @@ err_out_free_ctrl_slot:
        cleanup_slots(ctrl);
 err_out_release_ctlr:
        ctrl->hpc_ops->release_ctlr(ctrl);
-err_out_free_ctrl:
-       kfree(ctrl);
 err_out_none:
        return -ENODEV;
 }
 
 static void pciehp_remove (struct pcie_device *dev)
 {
-       struct pci_dev *pdev = dev->port;
-       struct controller *ctrl = pci_get_drvdata(pdev);
+       struct controller *ctrl = get_service_data(dev);
 
        cleanup_slots(ctrl);
        ctrl->hpc_ops->release_ctlr(ctrl);
-       kfree(ctrl);
 }
 
 #ifdef CONFIG_PM
@@ -522,13 +462,12 @@ static int pciehp_resume (struct pcie_device *dev)
 {
        printk("%s ENTRY\n", __func__);
        if (pciehp_force) {
-               struct pci_dev *pdev = dev->port;
-               struct controller *ctrl = pci_get_drvdata(pdev);
+               struct controller *ctrl = get_service_data(dev);
                struct slot *t_slot;
                u8 status;
 
                /* reinitialize the chipset's event detection logic */
-               pcie_init_hardware_part2(ctrl, dev);
+               pcie_enable_notification(ctrl);
 
                t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
 
index 79f104963166bdce55420df4005966df49e0dcc6..1323a43285d71efb7635e1114d939fef34618219 100644 (file)
@@ -247,30 +247,32 @@ static inline void pciehp_free_irq(struct controller *ctrl)
                free_irq(ctrl->pci_dev->irq, ctrl);
 }
 
-static inline int pcie_poll_cmd(struct controller *ctrl)
+static int pcie_poll_cmd(struct controller *ctrl)
 {
        u16 slot_status;
        int timeout = 1000;
 
-       if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status))
-               if (slot_status & CMD_COMPLETED)
-                       goto completed;
-       for (timeout = 1000; timeout > 0; timeout -= 100) {
-               msleep(100);
-               if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status))
-                       if (slot_status & CMD_COMPLETED)
-                               goto completed;
+       if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) {
+               if (slot_status & CMD_COMPLETED) {
+                       pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED);
+                       return 1;
+               }
+       }
+       while (timeout > 1000) {
+               msleep(10);
+               timeout -= 10;
+               if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) {
+                       if (slot_status & CMD_COMPLETED) {
+                               pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED);
+                               return 1;
+                       }
+               }
        }
        return 0;       /* timeout */
-
-completed:
-       pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED);
-       return timeout;
 }
 
-static inline int pcie_wait_cmd(struct controller *ctrl, int poll)
+static void pcie_wait_cmd(struct controller *ctrl, int poll)
 {
-       int retval = 0;
        unsigned int msecs = pciehp_poll_mode ? 2500 : 1000;
        unsigned long timeout = msecs_to_jiffies(msecs);
        int rc;
@@ -278,16 +280,9 @@ static inline int pcie_wait_cmd(struct controller *ctrl, int poll)
        if (poll)
                rc = pcie_poll_cmd(ctrl);
        else
-               rc = wait_event_interruptible_timeout(ctrl->queue,
-                                             !ctrl->cmd_busy, timeout);
+               rc = wait_event_timeout(ctrl->queue, !ctrl->cmd_busy, timeout);
        if (!rc)
                dbg("Command not completed in 1000 msec\n");
-       else if (rc < 0) {
-               retval = -EINTR;
-               info("Command was interrupted by a signal\n");
-       }
-
-       return retval;
 }
 
 /**
@@ -342,10 +337,6 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
 
        slot_ctrl &= ~mask;
        slot_ctrl |= (cmd & mask);
-       /* Don't enable command completed if caller is changing it. */
-       if (!(mask & CMD_CMPL_INTR_ENABLE))
-               slot_ctrl |= CMD_CMPL_INTR_ENABLE;
-
        ctrl->cmd_busy = 1;
        smp_mb();
        retval = pciehp_writew(ctrl, SLOTCTRL, slot_ctrl);
@@ -365,7 +356,7 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
                if (!(slot_ctrl & HP_INTR_ENABLE) ||
                    !(slot_ctrl & CMD_CMPL_INTR_ENABLE))
                        poll = 1;
-                retval = pcie_wait_cmd(ctrl, poll);
+                pcie_wait_cmd(ctrl, poll);
        }
  out:
        mutex_unlock(&ctrl->ctrl_lock);
@@ -614,23 +605,6 @@ static void hpc_set_green_led_blink(struct slot *slot)
            __func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
 }
 
-static void hpc_release_ctlr(struct controller *ctrl)
-{
-       /* Mask Hot-plug Interrupt Enable */
-       if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE))
-               err("%s: Cannot mask hotplut interrupt enable\n", __func__);
-
-       /* Free interrupt handler or interrupt polling timer */
-       pciehp_free_irq(ctrl);
-
-       /*
-        * If this is the last controller to be released, destroy the
-        * pciehp work queue
-        */
-       if (atomic_dec_and_test(&pciehp_num_controllers))
-               destroy_workqueue(pciehp_wq);
-}
-
 static int hpc_power_on_slot(struct slot * slot)
 {
        struct controller *ctrl = slot->ctrl;
@@ -785,7 +759,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
                intr_loc |= detected;
                if (!intr_loc)
                        return IRQ_NONE;
-               if (pciehp_writew(ctrl, SLOTSTATUS, detected)) {
+               if (detected && pciehp_writew(ctrl, SLOTSTATUS, detected)) {
                        err("%s: Cannot write to SLOTSTATUS\n", __func__);
                        return IRQ_NONE;
                }
@@ -797,25 +771,13 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
        if (intr_loc & CMD_COMPLETED) {
                ctrl->cmd_busy = 0;
                smp_mb();
-               wake_up_interruptible(&ctrl->queue);
+               wake_up(&ctrl->queue);
        }
 
        if (!(intr_loc & ~CMD_COMPLETED))
                return IRQ_HANDLED;
 
-       /*
-        * Return without handling events if this handler routine is
-        * called before controller initialization is done. This may
-        * happen if hotplug event or another interrupt that shares
-        * the IRQ with pciehp arrives before slot initialization is
-        * done after interrupt handler is registered.
-        *
-        * FIXME - Need more structural fixes. We need to be ready to
-        * handle the event before installing interrupt handler.
-        */
        p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
-       if (!p_slot || !p_slot->hpc_ops)
-               return IRQ_HANDLED;
 
        /* Check MRL Sensor Changed */
        if (intr_loc & MRL_SENS_CHANGED)
@@ -992,6 +954,7 @@ static int hpc_get_cur_lnk_width(struct slot *slot,
        return retval;
 }
 
+static void pcie_release_ctrl(struct controller *ctrl);
 static struct hpc_ops pciehp_hpc_ops = {
        .power_on_slot                  = hpc_power_on_slot,
        .power_off_slot                 = hpc_power_off_slot,
@@ -1013,97 +976,11 @@ static struct hpc_ops pciehp_hpc_ops = {
        .green_led_off                  = hpc_set_green_led_off,
        .green_led_blink                = hpc_set_green_led_blink,
 
-       .release_ctlr                   = hpc_release_ctlr,
+       .release_ctlr                   = pcie_release_ctrl,
        .check_lnk_status               = hpc_check_lnk_status,
 };
 
-#ifdef CONFIG_ACPI
-static int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev)
-{
-       acpi_status status;
-       acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev));
-       struct pci_dev *pdev = dev;
-       struct pci_bus *parent;
-       struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
-
-       /*
-        * Per PCI firmware specification, we should run the ACPI _OSC
-        * method to get control of hotplug hardware before using it.
-        * If an _OSC is missing, we look for an OSHP to do the same thing.
-        * To handle different BIOS behavior, we look for _OSC and OSHP
-        * within the scope of the hotplug controller and its parents, upto
-        * the host bridge under which this controller exists.
-        */
-       while (!handle) {
-               /*
-                * This hotplug controller was not listed in the ACPI name
-                * space at all. Try to get acpi handle of parent pci bus.
-                */
-               if (!pdev || !pdev->bus->parent)
-                       break;
-               parent = pdev->bus->parent;
-               dbg("Could not find %s in acpi namespace, trying parent\n",
-                               pci_name(pdev));
-               if (!parent->self)
-                       /* Parent must be a host bridge */
-                       handle = acpi_get_pci_rootbridge_handle(
-                                       pci_domain_nr(parent),
-                                       parent->number);
-               else
-                       handle = DEVICE_ACPI_HANDLE(
-                                       &(parent->self->dev));
-               pdev = parent->self;
-       }
-
-       while (handle) {
-               acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
-               dbg("Trying to get hotplug control for %s \n",
-                       (char *)string.pointer);
-               status = pci_osc_control_set(handle,
-                               OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL |
-                               OSC_PCI_EXPRESS_NATIVE_HP_CONTROL);
-               if (status == AE_NOT_FOUND)
-                       status = acpi_run_oshp(handle);
-               if (ACPI_SUCCESS(status)) {
-                       dbg("Gained control for hotplug HW for pci %s (%s)\n",
-                               pci_name(dev), (char *)string.pointer);
-                       kfree(string.pointer);
-                       return 0;
-               }
-               if (acpi_root_bridge(handle))
-                       break;
-               chandle = handle;
-               status = acpi_get_parent(chandle, &handle);
-               if (ACPI_FAILURE(status))
-                       break;
-       }
-
-       dbg("Cannot get control of hotplug hardware for pci %s\n",
-                       pci_name(dev));
-
-       kfree(string.pointer);
-       return -1;
-}
-#endif
-
-static int pcie_init_hardware_part1(struct controller *ctrl,
-                                   struct pcie_device *dev)
-{
-       /* Clear all remaining event bits in Slot Status register */
-       if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) {
-               err("%s: Cannot write to SLOTSTATUS register\n", __func__);
-               return -1;
-       }
-
-       /* Mask Hot-plug Interrupt Enable */
-       if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE)) {
-               err("%s: Cannot mask hotplug interrupt enable\n", __func__);
-               return -1;
-       }
-       return 0;
-}
-
-int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
+int pcie_enable_notification(struct controller *ctrl)
 {
        u16 cmd, mask;
 
@@ -1115,30 +992,83 @@ int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
        if (MRL_SENS(ctrl))
                cmd |= MRL_DETECT_ENABLE;
        if (!pciehp_poll_mode)
-               cmd |= HP_INTR_ENABLE;
+               cmd |= HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE;
 
-       mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE |
-               PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | HP_INTR_ENABLE;
+       mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE |
+              PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE;
 
        if (pcie_write_cmd(ctrl, cmd, mask)) {
                err("%s: Cannot enable software notification\n", __func__);
-               goto abort;
+               return -1;
        }
+       return 0;
+}
 
-       if (pciehp_force)
-               dbg("Bypassing BIOS check for pciehp use on %s\n",
-                               pci_name(ctrl->pci_dev));
-       else if (pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev))
-               goto abort_disable_intr;
+static void pcie_disable_notification(struct controller *ctrl)
+{
+       u16 mask;
+       mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE |
+              PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE;
+       if (pcie_write_cmd(ctrl, 0, mask))
+               warn("%s: Cannot disable software notification\n", __func__);
+}
 
+static int pcie_init_notification(struct controller *ctrl)
+{
+       if (pciehp_request_irq(ctrl))
+               return -1;
+       if (pcie_enable_notification(ctrl)) {
+               pciehp_free_irq(ctrl);
+               return -1;
+       }
        return 0;
+}
 
-       /* We end up here for the many possible ways to fail this API. */
-abort_disable_intr:
-       if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE))
-               err("%s : disabling interrupts failed\n", __func__);
-abort:
-       return -1;
+static void pcie_shutdown_notification(struct controller *ctrl)
+{
+       pcie_disable_notification(ctrl);
+       pciehp_free_irq(ctrl);
+}
+
+static void make_slot_name(struct slot *slot)
+{
+       if (pciehp_slot_with_bus)
+               snprintf(slot->name, SLOT_NAME_SIZE, "%04d_%04d",
+                        slot->bus, slot->number);
+       else
+               snprintf(slot->name, SLOT_NAME_SIZE, "%d", slot->number);
+}
+
+static int pcie_init_slot(struct controller *ctrl)
+{
+       struct slot *slot;
+
+       slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+       if (!slot)
+               return -ENOMEM;
+
+       slot->hp_slot = 0;
+       slot->ctrl = ctrl;
+       slot->bus = ctrl->pci_dev->subordinate->number;
+       slot->device = ctrl->slot_device_offset + slot->hp_slot;
+       slot->hpc_ops = ctrl->hpc_ops;
+       slot->number = ctrl->first_slot;
+       make_slot_name(slot);
+       mutex_init(&slot->lock);
+       INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);
+       list_add(&slot->slot_list, &ctrl->slot_list);
+       return 0;
+}
+
+static void pcie_cleanup_slot(struct controller *ctrl)
+{
+       struct slot *slot;
+       slot = list_first_entry(&ctrl->slot_list, struct slot, slot_list);
+       list_del(&slot->slot_list);
+       cancel_delayed_work(&slot->work);
+       flush_scheduled_work();
+       flush_workqueue(pciehp_wq);
+       kfree(slot);
 }
 
 static inline void dbg_ctrl(struct controller *ctrl)
@@ -1176,15 +1106,23 @@ static inline void dbg_ctrl(struct controller *ctrl)
        dbg("  Comamnd Completed    : %3s\n", NO_CMD_CMPL(ctrl)? "no" : "yes");
        pciehp_readw(ctrl, SLOTSTATUS, &reg16);
        dbg("Slot Status            : 0x%04x\n", reg16);
-       pciehp_readw(ctrl, SLOTSTATUS, &reg16);
+       pciehp_readw(ctrl, SLOTCTRL, &reg16);
        dbg("Slot Control           : 0x%04x\n", reg16);
 }
 
-int pcie_init(struct controller *ctrl, struct pcie_device *dev)
+struct controller *pcie_init(struct pcie_device *dev)
 {
+       struct controller *ctrl;
        u32 slot_cap;
        struct pci_dev *pdev = dev->port;
 
+       ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+       if (!ctrl) {
+               err("%s : out of memory\n", __func__);
+               goto abort;
+       }
+       INIT_LIST_HEAD(&ctrl->slot_list);
+
        ctrl->pci_dev = pdev;
        ctrl->cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP);
        if (!ctrl->cap_base) {
@@ -1215,15 +1153,12 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev)
            !(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl)))
            ctrl->no_cmd_complete = 1;
 
-       info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",
-            pdev->vendor, pdev->device,
-            pdev->subsystem_vendor, pdev->subsystem_device);
+       /* Clear all remaining event bits in Slot Status register */
+       if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f))
+               goto abort_ctrl;
 
-       if (pcie_init_hardware_part1(ctrl, dev))
-               goto abort;
-
-       if (pciehp_request_irq(ctrl))
-               goto abort;
+       /* Disable sotfware notification */
+       pcie_disable_notification(ctrl);
 
        /*
         * If this is the first controller to be initialized,
@@ -1231,18 +1166,39 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev)
         */
        if (atomic_add_return(1, &pciehp_num_controllers) == 1) {
                pciehp_wq = create_singlethread_workqueue("pciehpd");
-               if (!pciehp_wq) {
-                       goto abort_free_irq;
-               }
+               if (!pciehp_wq)
+                       goto abort_ctrl;
        }
 
-       if (pcie_init_hardware_part2(ctrl, dev))
-               goto abort_free_irq;
+       info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",
+            pdev->vendor, pdev->device,
+            pdev->subsystem_vendor, pdev->subsystem_device);
+
+       if (pcie_init_slot(ctrl))
+               goto abort_ctrl;
 
-       return 0;
+       if (pcie_init_notification(ctrl))
+               goto abort_slot;
 
-abort_free_irq:
-       pciehp_free_irq(ctrl);
+       return ctrl;
+
+abort_slot:
+       pcie_cleanup_slot(ctrl);
+abort_ctrl:
+       kfree(ctrl);
 abort:
-       return -1;
+       return NULL;
+}
+
+void pcie_release_ctrl(struct controller *ctrl)
+{
+       pcie_shutdown_notification(ctrl);
+       pcie_cleanup_slot(ctrl);
+       /*
+        * If this is the last controller to be released, destroy the
+        * pciehp work queue
+        */
+       if (atomic_dec_and_test(&pciehp_num_controllers))
+               destroy_workqueue(pciehp_wq);
+       kfree(ctrl);
 }
index 779c5db71be46150bc5c34896bea57dc23697b9e..a796301ea03fbd3709f15be52f78053bb12380f9 100644 (file)
  */
 #include <linux/kobject.h>
 #include <linux/string.h>
+#include <linux/pci.h>
 #include <linux/pci_hotplug.h>
 #include "rpadlpar.h"
+#include "../pci.h"
 
 #define DLPAR_KOBJ_NAME       "control"
 
@@ -27,7 +29,6 @@
 
 #define MAX_DRC_NAME_LEN 64
 
-
 static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr,
                              const char *buf, size_t nbytes)
 {
@@ -112,7 +113,7 @@ int dlpar_sysfs_init(void)
        int error;
 
        dlpar_kobj = kobject_create_and_add(DLPAR_KOBJ_NAME,
-                                           &pci_hotplug_slots_kset->kobj);
+                                           &pci_slots_kset->kobj);
        if (!dlpar_kobj)
                return -EINVAL;
 
index 56197b600d36d5fe079cfc746b387c1c44768c99..9b714ea93d207036fe9b51d6f3c2bbe0233c1c25 100644 (file)
 #include <asm/rtas.h>
 #include "rpaphp.h"
 
-static ssize_t address_read_file (struct hotplug_slot *php_slot, char *buf)
-{
-       int retval;
-       struct slot *slot = (struct slot *)php_slot->private;
-       struct pci_bus *bus;
-
-       if (!slot)
-               return -ENOENT;
-
-       bus = slot->bus;
-       if (!bus)
-               return -ENOENT;
-
-       if (bus->self)
-               retval = sprintf(buf, pci_name(bus->self));
-       else
-               retval = sprintf(buf, "%04x:%02x:00.0",
-                       pci_domain_nr(bus), bus->number);
-
-       return retval;
-}
-
-static struct hotplug_slot_attribute php_attr_address = {
-       .attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
-       .show = address_read_file,
-};
-
 /* free up the memory used by a slot */
 static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
 {
@@ -135,9 +108,6 @@ int rpaphp_deregister_slot(struct slot *slot)
 
        list_del(&slot->rpaphp_slot_list);
        
-       /* remove "address" file */
-       sysfs_remove_file(&php_slot->kobj, &php_attr_address.attr);
-
        retval = pci_hp_deregister(php_slot);
        if (retval)
                err("Problem unregistering a slot %s\n", slot->name);
@@ -151,6 +121,7 @@ int rpaphp_register_slot(struct slot *slot)
 {
        struct hotplug_slot *php_slot = slot->hotplug_slot;
        int retval;
+       int slotno;
 
        dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n", 
                __func__, slot->dn->full_name, slot->index, slot->name,
@@ -162,19 +133,16 @@ int rpaphp_register_slot(struct slot *slot)
                return -EAGAIN;
        }       
 
-       retval = pci_hp_register(php_slot);
+       if (slot->dn->child)
+               slotno = PCI_SLOT(PCI_DN(slot->dn->child)->devfn);
+       else
+               slotno = -1;
+       retval = pci_hp_register(php_slot, slot->bus, slotno);
        if (retval) {
                err("pci_hp_register failed with error %d\n", retval);
                return retval;
        }
 
-       /* create "address" file */
-       retval = sysfs_create_file(&php_slot->kobj, &php_attr_address.attr);
-       if (retval) {
-               err("sysfs_create_file failed with error %d\n", retval);
-               goto sysfs_fail;
-       }
-
        /* add slot to our internal list */
        list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
        info("Slot [%s] registered\n", slot->name);
index 2fe37cd85b69a77441625762c6007f8f473cf2e5..410fe0394a8ea63fb8de8542654d514d2ec36992 100644 (file)
@@ -197,13 +197,15 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
 static struct hotplug_slot * sn_hp_destroy(void)
 {
        struct slot *slot;
+       struct pci_slot *pci_slot;
        struct hotplug_slot *bss_hotplug_slot = NULL;
 
        list_for_each_entry(slot, &sn_hp_list, hp_list) {
                bss_hotplug_slot = slot->hotplug_slot;
+               pci_slot = bss_hotplug_slot->pci_slot;
                list_del(&((struct slot *)bss_hotplug_slot->private)->
                         hp_list);
-               sysfs_remove_file(&bss_hotplug_slot->kobj,
+               sysfs_remove_file(&pci_slot->kobj,
                                  &sn_slot_path_attr.attr);
                break;
        }
@@ -614,6 +616,7 @@ static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot)
 static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
 {
        int device;
+       struct pci_slot *pci_slot;
        struct hotplug_slot *bss_hotplug_slot;
        int rc = 0;
 
@@ -650,11 +653,12 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
                bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
                bss_hotplug_slot->release = &sn_release_slot;
 
-               rc = pci_hp_register(bss_hotplug_slot);
+               rc = pci_hp_register(bss_hotplug_slot, pci_bus, device);
                if (rc)
                        goto register_err;
 
-               rc = sysfs_create_file(&bss_hotplug_slot->kobj,
+               pci_slot = bss_hotplug_slot->pci_slot;
+               rc = sysfs_create_file(&pci_slot->kobj,
                                       &sn_slot_path_attr.attr);
                if (rc)
                        goto register_err;
@@ -664,7 +668,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
 
 register_err:
        dev_dbg(&pci_bus->self->dev, "bus failed to register with err = %d\n",
-               rc);
+               rc);
 
 alloc_err:
        if (rc == -ENOMEM)
index f66e8d6315abecab94d94aaffe2cf7d133a34d34..8a026f750deb65f64831f50a603c32d761e6c5ff 100644 (file)
@@ -170,6 +170,7 @@ extern void shpchp_queue_pushbutton_work(struct work_struct *work);
 extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev);
 
 #ifdef CONFIG_ACPI
+#include <linux/pci-acpi.h>
 static inline int get_hp_params_from_firmware(struct pci_dev *dev,
                                              struct hotplug_params *hpp)
 {
@@ -177,14 +178,15 @@ static inline int get_hp_params_from_firmware(struct pci_dev *dev,
                        return -ENODEV;
        return 0;
 }
-#define get_hp_hw_control_from_firmware(pdev)                          \
-       do {                                                            \
-               if (DEVICE_ACPI_HANDLE(&(pdev->dev)))                   \
-                       acpi_run_oshp(DEVICE_ACPI_HANDLE(&(pdev->dev)));\
-       } while (0)
+
+static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev)
+{
+       u32 flags = OSC_SHPC_NATIVE_HP_CONTROL;
+       return acpi_get_hp_hw_control_from_firmware(dev, flags);
+}
 #else
 #define get_hp_params_from_firmware(dev, hpp) (-ENODEV)
-#define get_hp_hw_control_from_firmware(dev) do { } while (0)
+#define get_hp_hw_control_from_firmware(dev) (0)
 #endif
 
 struct ctrl_reg {
index 97848654652a1d2e03e6f648c268012f9b0711d9..a8cbd039b85bfde22a4f6e019d8de62cb5714f9e 100644 (file)
@@ -39,7 +39,7 @@
 int shpchp_debug;
 int shpchp_poll_mode;
 int shpchp_poll_time;
-int shpchp_slot_with_bus;
+static int shpchp_slot_with_bus;
 struct workqueue_struct *shpchp_wq;
 
 #define DRIVER_VERSION "0.4"
@@ -68,7 +68,6 @@ static int get_power_status   (struct hotplug_slot *slot, u8 *value);
 static int get_attention_status        (struct hotplug_slot *slot, u8 *value);
 static int get_latch_status    (struct hotplug_slot *slot, u8 *value);
 static int get_adapter_status  (struct hotplug_slot *slot, u8 *value);
-static int get_address         (struct hotplug_slot *slot, u32 *value);
 static int get_max_bus_speed   (struct hotplug_slot *slot, enum pci_bus_speed *value);
 static int get_cur_bus_speed   (struct hotplug_slot *slot, enum pci_bus_speed *value);
 
@@ -81,7 +80,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
        .get_attention_status = get_attention_status,
        .get_latch_status =     get_latch_status,
        .get_adapter_status =   get_adapter_status,
-       .get_address =          get_address,
        .get_max_bus_speed =    get_max_bus_speed,
        .get_cur_bus_speed =    get_cur_bus_speed,
 };
@@ -159,7 +157,8 @@ static int init_slots(struct controller *ctrl)
                dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
                    "slot_device_offset=%x\n", slot->bus, slot->device,
                    slot->hp_slot, slot->number, ctrl->slot_device_offset);
-               retval = pci_hp_register(slot->hotplug_slot);
+               retval = pci_hp_register(slot->hotplug_slot,
+                               ctrl->pci_dev->subordinate, slot->device);
                if (retval) {
                        err("pci_hp_register failed with error %d\n", retval);
                        if (retval == -EEXIST)
@@ -288,19 +287,8 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
        return 0;
 }
 
-static int get_address (struct hotplug_slot *hotplug_slot, u32 *value)
-{
-       struct slot *slot = get_slot(hotplug_slot);
-       struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
-
-       dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
-
-       *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
-
-       return 0;
-}
-
-static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+static int get_max_bus_speed(struct hotplug_slot *hotplug_slot,
+                               enum pci_bus_speed *value)
 {
        struct slot *slot = get_slot(hotplug_slot);
        int retval;
@@ -330,13 +318,14 @@ static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_sp
 
 static int is_shpc_capable(struct pci_dev *dev)
 {
-       if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device ==
-                               PCI_DEVICE_ID_AMD_GOLAM_7450))
-               return 1;
-       if (pci_find_capability(dev, PCI_CAP_ID_SHPC))
-               return 1;
-
-       return 0;
+       if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device ==
+                                               PCI_DEVICE_ID_AMD_GOLAM_7450))
+               return 1;
+       if (!pci_find_capability(dev, PCI_CAP_ID_SHPC))
+               return 0;
+       if (get_hp_hw_control_from_firmware(dev))
+               return 0;
+       return 1;
 }
 
 static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
index 7d770b2cd889a961f2b9c46226b2e7fe59894fb9..7a0bff364cd4f816cf6475b0c80237f53b73f805 100644 (file)
@@ -1084,7 +1084,6 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
        dbg("%s: HPC at b:d:f:irq=0x%x:%x:%x:%x\n", __func__,
                        pdev->bus->number, PCI_SLOT(pdev->devfn),
                        PCI_FUNC(pdev->devfn), pdev->irq);
-       get_hp_hw_control_from_firmware(pdev);
 
        /*
         * If this is the first controller to be initialized,
index bb0642318a959453e9ef0c68190070ac7d9290a4..3f7b81c065d25188e17d82665b63575a91c92e19 100644 (file)
@@ -1748,7 +1748,6 @@ int __init init_dmars(void)
        deferred_flush = kzalloc(g_num_of_iommus *
                sizeof(struct deferred_flush_tables), GFP_KERNEL);
        if (!deferred_flush) {
-               kfree(g_iommus);
                ret = -ENOMEM;
                goto error;
        }
index 8c61304cbb37e8b9f32343c0fb93cd73bd276d27..15af618d36e20a0f64c704b67cc14e2c1dc2daf2 100644 (file)
@@ -70,12 +70,10 @@ arch_teardown_msi_irqs(struct pci_dev *dev)
        }
 }
 
-static void msi_set_enable(struct pci_dev *dev, int enable)
+static void __msi_set_enable(struct pci_dev *dev, int pos, int enable)
 {
-       int pos;
        u16 control;
 
-       pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
        if (pos) {
                pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
                control &= ~PCI_MSI_FLAGS_ENABLE;
@@ -85,6 +83,11 @@ static void msi_set_enable(struct pci_dev *dev, int enable)
        }
 }
 
+static void msi_set_enable(struct pci_dev *dev, int enable)
+{
+       __msi_set_enable(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), enable);
+}
+
 static void msix_set_enable(struct pci_dev *dev, int enable)
 {
        int pos;
@@ -141,7 +144,8 @@ static void msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag)
                        mask_bits |= flag & mask;
                        pci_write_config_dword(entry->dev, pos, mask_bits);
                } else {
-                       msi_set_enable(entry->dev, !flag);
+                       __msi_set_enable(entry->dev, entry->msi_attrib.pos,
+                                        !flag);
                }
                break;
        case PCI_CAP_ID_MSIX:
@@ -561,9 +565,8 @@ int pci_enable_msi(struct pci_dev* dev)
 
        /* Check whether driver already requested for MSI-X irqs */
        if (dev->msix_enabled) {
-               printk(KERN_INFO "PCI: %s: Can't enable MSI.  "
-                       "Device already has MSI-X enabled\n",
-                       pci_name(dev));
+               dev_info(&dev->dev, "can't enable MSI "
+                        "(MSI-X already enabled)\n");
                return -EINVAL;
        }
        status = msi_capability_init(dev);
@@ -686,9 +689,8 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
 
        /* Check whether driver already requested for MSI irq */
        if (dev->msi_enabled) {
-               printk(KERN_INFO "PCI: %s: Can't enable MSI-X.  "
-                      "Device already has an MSI irq assigned\n",
-                      pci_name(dev));
+               dev_info(&dev->dev, "can't enable MSI-X "
+                      "(MSI IRQ already assigned)\n");
                return -EINVAL;
        }
        status = msix_capability_init(dev, entries, nvec);
index dab9d471914c3dacd3fd6f165ed8bd1a6ca60c56..7764768b6a0e7dfc225c76693b3be997df4b926f 100644 (file)
 
 struct acpi_osc_data {
        acpi_handle handle;
-       u32 ctrlset_buf[3];
-       u32 global_ctrlsets;
+       u32 support_set;
+       u32 control_set;
+       int is_queried;
+       u32 query_result;
        struct list_head sibiling;
 };
 static LIST_HEAD(acpi_osc_data_list);
 
+struct acpi_osc_args {
+       u32 capbuf[3];
+       u32 query_result;
+};
+
 static struct acpi_osc_data *acpi_get_osc_data(acpi_handle handle)
 {
        struct acpi_osc_data *data;
@@ -44,42 +51,18 @@ static struct acpi_osc_data *acpi_get_osc_data(acpi_handle handle)
        return data;
 }
 
-static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
+static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40,
+                         0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
 
-static acpi_status  
-acpi_query_osc (
-       acpi_handle     handle,
-       u32             level,
-       void            *context,
-       void            **retval )
+static acpi_status acpi_run_osc(acpi_handle handle,
+                               struct acpi_osc_args *osc_args)
 {
-       acpi_status             status;
-       struct acpi_object_list input;
-       union acpi_object       in_params[4];
-       struct acpi_buffer      output = {ACPI_ALLOCATE_BUFFER, NULL};
-       union acpi_object       *out_obj;
-       u32                     osc_dw0;
-       acpi_status *ret_status = (acpi_status *)retval;
-       struct acpi_osc_data *osc_data;
-       u32 flags = (unsigned long)context, temp;
-       acpi_handle tmp;
-
-       status = acpi_get_handle(handle, "_OSC", &tmp);
-       if (ACPI_FAILURE(status))
-               return status;
-
-       osc_data = acpi_get_osc_data(handle);
-       if (!osc_data) {
-               printk(KERN_ERR "acpi osc data array is full\n");
-               return AE_ERROR;
-       }
-
-       osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] |= (flags & OSC_SUPPORT_MASKS);
-
-       /* do _OSC query for all possible controls */
-       temp = osc_data->ctrlset_buf[OSC_CONTROL_TYPE];
-       osc_data->ctrlset_buf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
-       osc_data->ctrlset_buf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
+       acpi_status status;
+       struct acpi_object_list input;
+       union acpi_object in_params[4];
+       struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object *out_obj;
+       u32 osc_dw0, flags = osc_args->capbuf[OSC_QUERY_TYPE];
 
        /* Setting up input parameters */
        input.count = 4;
@@ -93,20 +76,19 @@ acpi_query_osc (
        in_params[2].integer.value      = 3;
        in_params[3].type               = ACPI_TYPE_BUFFER;
        in_params[3].buffer.length      = 12;
-       in_params[3].buffer.pointer     = (u8 *)osc_data->ctrlset_buf;
+       in_params[3].buffer.pointer     = (u8 *)osc_args->capbuf;
 
        status = acpi_evaluate_object(handle, "_OSC", &input, &output);
        if (ACPI_FAILURE(status))
-               goto out_nofree;
-       out_obj = output.pointer;
+               return status;
 
+       out_obj = output.pointer;
        if (out_obj->type != ACPI_TYPE_BUFFER) {
-               printk(KERN_DEBUG  
-                       "Evaluate _OSC returns wrong type\n");
+               printk(KERN_DEBUG "Evaluate _OSC returns wrong type\n");
                status = AE_TYPE;
-               goto query_osc_out;
+               goto out_kfree;
        }
-       osc_dw0 = *((u32 *) out_obj->buffer.pointer);
+       osc_dw0 = *((u32 *)out_obj->buffer.pointer);
        if (osc_dw0) {
                if (osc_dw0 & OSC_REQUEST_ERROR)
                        printk(KERN_DEBUG "_OSC request fails\n"); 
@@ -115,93 +97,58 @@ acpi_query_osc (
                if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
                        printk(KERN_DEBUG "_OSC invalid revision\n"); 
                if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
-                       /* Update Global Control Set */
-                       osc_data->global_ctrlsets =
-                               *((u32 *)(out_obj->buffer.pointer + 8));
-                       status = AE_OK;
-                       goto query_osc_out;
+                       if (flags & OSC_QUERY_ENABLE)
+                               goto out_success;
+                       printk(KERN_DEBUG "_OSC FW not grant req. control\n");
+                       status = AE_SUPPORT;
+                       goto out_kfree;
                }
                status = AE_ERROR;
-               goto query_osc_out;
+               goto out_kfree;
        }
-
-       /* Update Global Control Set */
-       osc_data->global_ctrlsets = *((u32 *)(out_obj->buffer.pointer + 8));
+out_success:
+       if (flags & OSC_QUERY_ENABLE)
+               osc_args->query_result =
+                       *((u32 *)(out_obj->buffer.pointer + 8));
        status = AE_OK;
 
-query_osc_out:
+out_kfree:
        kfree(output.pointer);
-out_nofree:
-       *ret_status = status;
-
-       osc_data->ctrlset_buf[OSC_QUERY_TYPE] = !OSC_QUERY_ENABLE;
-       osc_data->ctrlset_buf[OSC_CONTROL_TYPE] = temp;
-       if (ACPI_FAILURE(status)) {
-               /* no osc support at all */
-               osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] = 0;
-       }
-
        return status;
 }
 
-
-static acpi_status  
-acpi_run_osc (
-       acpi_handle     handle,
-       void            *context)
+static acpi_status acpi_query_osc(acpi_handle handle,
+                                 u32 level, void *context, void **retval)
 {
-       acpi_status             status;
-       struct acpi_object_list input;
-       union acpi_object       in_params[4];
-       struct acpi_buffer      output = {ACPI_ALLOCATE_BUFFER, NULL};
-       union acpi_object       *out_obj;
-       u32                     osc_dw0;
-
-       /* Setting up input parameters */
-       input.count = 4;
-       input.pointer = in_params;
-       in_params[0].type               = ACPI_TYPE_BUFFER;
-       in_params[0].buffer.length      = 16;
-       in_params[0].buffer.pointer     = OSC_UUID;
-       in_params[1].type               = ACPI_TYPE_INTEGER;
-       in_params[1].integer.value      = 1;
-       in_params[2].type               = ACPI_TYPE_INTEGER;
-       in_params[2].integer.value      = 3;
-       in_params[3].type               = ACPI_TYPE_BUFFER;
-       in_params[3].buffer.length      = 12;
-       in_params[3].buffer.pointer     = (u8 *)context;
+       acpi_status status;
+       struct acpi_osc_data *osc_data;
+       u32 flags = (unsigned long)context, support_set;
+       acpi_handle tmp;
+       struct acpi_osc_args osc_args;
 
-       status = acpi_evaluate_object(handle, "_OSC", &input, &output);
-       if (ACPI_FAILURE (status))
+       status = acpi_get_handle(handle, "_OSC", &tmp);
+       if (ACPI_FAILURE(status))
                return status;
 
-       out_obj = output.pointer;
-       if (out_obj->type != ACPI_TYPE_BUFFER) {
-               printk(KERN_DEBUG  
-                       "Evaluate _OSC returns wrong type\n");
-               status = AE_TYPE;
-               goto run_osc_out;
+       osc_data = acpi_get_osc_data(handle);
+       if (!osc_data) {
+               printk(KERN_ERR "acpi osc data array is full\n");
+               return AE_ERROR;
        }
-       osc_dw0 = *((u32 *) out_obj->buffer.pointer);
-       if (osc_dw0) {
-               if (osc_dw0 & OSC_REQUEST_ERROR)
-                       printk(KERN_DEBUG "_OSC request fails\n"); 
-               if (osc_dw0 & OSC_INVALID_UUID_ERROR)
-                       printk(KERN_DEBUG "_OSC invalid UUID\n"); 
-               if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
-                       printk(KERN_DEBUG "_OSC invalid revision\n"); 
-               if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
-                       printk(KERN_DEBUG "_OSC FW not grant req. control\n");
-                       status = AE_SUPPORT;
-                       goto run_osc_out;
-               }
-               status = AE_ERROR;
-               goto run_osc_out;
+
+       /* do _OSC query for all possible controls */
+       support_set = osc_data->support_set | (flags & OSC_SUPPORT_MASKS);
+       osc_args.capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
+       osc_args.capbuf[OSC_SUPPORT_TYPE] = support_set;
+       osc_args.capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
+
+       status = acpi_run_osc(handle, &osc_args);
+       if (ACPI_SUCCESS(status)) {
+               osc_data->support_set = support_set;
+               osc_data->query_result = osc_args.query_result;
+               osc_data->is_queried = 1;
        }
-       status = AE_OK;
 
-run_osc_out:
-       kfree(output.pointer);
        return status;
 }
 
@@ -215,15 +162,11 @@ run_osc_out:
  **/
 acpi_status __pci_osc_support_set(u32 flags, const char *hid)
 {
-       acpi_status retval = AE_NOT_FOUND;
-
-       if (!(flags & OSC_SUPPORT_MASKS)) {
+       if (!(flags & OSC_SUPPORT_MASKS))
                return AE_TYPE;
-       }
-       acpi_get_devices(hid,
-                       acpi_query_osc,
-                       (void *)(unsigned long)flags,
-                       (void **) &retval );
+
+       acpi_get_devices(hid, acpi_query_osc,
+                        (void *)(unsigned long)flags, NULL);
        return AE_OK;
 }
 
@@ -236,10 +179,11 @@ acpi_status __pci_osc_support_set(u32 flags, const char *hid)
  **/
 acpi_status pci_osc_control_set(acpi_handle handle, u32 flags)
 {
-       acpi_status     status;
-       u32             ctrlset;
+       acpi_status status;
+       u32 ctrlset, control_set;
        acpi_handle tmp;
        struct acpi_osc_data *osc_data;
+       struct acpi_osc_args osc_args;
 
        status = acpi_get_handle(handle, "_OSC", &tmp);
        if (ACPI_FAILURE(status))
@@ -252,24 +196,25 @@ acpi_status pci_osc_control_set(acpi_handle handle, u32 flags)
        }
 
        ctrlset = (flags & OSC_CONTROL_MASKS);
-       if (!ctrlset) {
+       if (!ctrlset)
                return AE_TYPE;
-       }
-       if (osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] &&
-               ((osc_data->global_ctrlsets & ctrlset) != ctrlset)) {
+
+       if (osc_data->is_queried &&
+           ((osc_data->query_result & ctrlset) != ctrlset))
                return AE_SUPPORT;
-       }
-       osc_data->ctrlset_buf[OSC_CONTROL_TYPE] |= ctrlset;
-       status = acpi_run_osc(handle, osc_data->ctrlset_buf);
-       if (ACPI_FAILURE (status)) {
-               osc_data->ctrlset_buf[OSC_CONTROL_TYPE] &= ~ctrlset;
-       }
-       
+
+       control_set = osc_data->control_set | ctrlset;
+       osc_args.capbuf[OSC_QUERY_TYPE] = 0;
+       osc_args.capbuf[OSC_SUPPORT_TYPE] = osc_data->support_set;
+       osc_args.capbuf[OSC_CONTROL_TYPE] = control_set;
+       status = acpi_run_osc(handle, &osc_args);
+       if (ACPI_SUCCESS(status))
+               osc_data->control_set = control_set;
+
        return status;
 }
 EXPORT_SYMBOL(pci_osc_control_set);
 
-#ifdef CONFIG_ACPI_SLEEP
 /*
  * _SxD returns the D-state with the highest power
  * (lowest D-state number) supported in the S-state "x".
@@ -313,7 +258,13 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
        }
        return PCI_POWER_ERROR;
 }
-#endif
+
+static bool acpi_pci_power_manageable(struct pci_dev *dev)
+{
+       acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+
+       return handle ? acpi_bus_power_manageable(handle) : false;
+}
 
 static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
 {
@@ -326,12 +277,11 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
                [PCI_D3hot] = ACPI_STATE_D3,
                [PCI_D3cold] = ACPI_STATE_D3
        };
+       int error = -EINVAL;
 
-       if (!handle)
-               return -ENODEV;
        /* If the ACPI device has _EJ0, ignore the device */
-       if (ACPI_SUCCESS(acpi_get_handle(handle, "_EJ0", &tmp)))
-               return 0;
+       if (!handle || ACPI_SUCCESS(acpi_get_handle(handle, "_EJ0", &tmp)))
+               return -ENODEV;
 
        switch (state) {
        case PCI_D0:
@@ -339,11 +289,41 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
        case PCI_D2:
        case PCI_D3hot:
        case PCI_D3cold:
-               return acpi_bus_set_power(handle, state_conv[state]);
+               error = acpi_bus_set_power(handle, state_conv[state]);
        }
-       return -EINVAL;
+
+       if (!error)
+               dev_printk(KERN_INFO, &dev->dev,
+                               "power state changed by ACPI to D%d\n", state);
+
+       return error;
+}
+
+static bool acpi_pci_can_wakeup(struct pci_dev *dev)
+{
+       acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+
+       return handle ? acpi_bus_can_wakeup(handle) : false;
+}
+
+static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
+{
+       int error = acpi_pm_device_sleep_wake(&dev->dev, enable);
+
+       if (!error)
+               dev_printk(KERN_INFO, &dev->dev,
+                               "wake-up capability %s by ACPI\n",
+                               enable ? "enabled" : "disabled");
+       return error;
 }
 
+static struct pci_platform_pm_ops acpi_pci_platform_pm = {
+       .is_manageable = acpi_pci_power_manageable,
+       .set_state = acpi_pci_set_power_state,
+       .choose_state = acpi_pci_choose_state,
+       .can_wakeup = acpi_pci_can_wakeup,
+       .sleep_wake = acpi_pci_sleep_wake,
+};
 
 /* ACPI bus type */
 static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
@@ -395,10 +375,7 @@ static int __init acpi_pci_init(void)
        ret = register_acpi_bus_type(&acpi_pci_bus);
        if (ret)
                return 0;
-#ifdef CONFIG_ACPI_SLEEP
-       platform_pci_choose_state = acpi_pci_choose_state;
-#endif
-       platform_pci_set_power_state = acpi_pci_set_power_state;
+       pci_set_platform_pm(&acpi_pci_platform_pm);
        return 0;
 }
 arch_initcall(acpi_pci_init);
index e1637bd82b8e4cc28710ed4c2f02d581155764d6..a13f53486114fe27e665bfc7451933d823465ca1 100644 (file)
@@ -274,7 +274,57 @@ static int pci_device_remove(struct device * dev)
        return 0;
 }
 
-static int pci_device_suspend(struct device * dev, pm_message_t state)
+static void pci_device_shutdown(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct pci_driver *drv = pci_dev->driver;
+
+       if (drv && drv->shutdown)
+               drv->shutdown(pci_dev);
+       pci_msi_shutdown(pci_dev);
+       pci_msix_shutdown(pci_dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+/*
+ * Default "suspend" method for devices that have no driver provided suspend,
+ * or not even a driver at all.
+ */
+static void pci_default_pm_suspend(struct pci_dev *pci_dev)
+{
+       pci_save_state(pci_dev);
+       /*
+        * mark its power state as "unknown", since we don't know if
+        * e.g. the BIOS will change its device state when we suspend.
+        */
+       if (pci_dev->current_state == PCI_D0)
+               pci_dev->current_state = PCI_UNKNOWN;
+}
+
+/*
+ * Default "resume" method for devices that have no driver provided resume,
+ * or not even a driver at all.
+ */
+static int pci_default_pm_resume(struct pci_dev *pci_dev)
+{
+       int retval = 0;
+
+       /* restore the PCI config space */
+       pci_restore_state(pci_dev);
+       /* if the device was enabled before suspend, reenable */
+       retval = pci_reenable_device(pci_dev);
+       /*
+        * if the device was busmaster before the suspend, make it busmaster
+        * again
+        */
+       if (pci_dev->is_busmaster)
+               pci_set_master(pci_dev);
+
+       return retval;
+}
+
+static int pci_legacy_suspend(struct device *dev, pm_message_t state)
 {
        struct pci_dev * pci_dev = to_pci_dev(dev);
        struct pci_driver * drv = pci_dev->driver;
@@ -284,18 +334,12 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
                i = drv->suspend(pci_dev, state);
                suspend_report_result(drv->suspend, i);
        } else {
-               pci_save_state(pci_dev);
-               /*
-                * mark its power state as "unknown", since we don't know if
-                * e.g. the BIOS will change its device state when we suspend.
-                */
-               if (pci_dev->current_state == PCI_D0)
-                       pci_dev->current_state = PCI_UNKNOWN;
+               pci_default_pm_suspend(pci_dev);
        }
        return i;
 }
 
-static int pci_device_suspend_late(struct device * dev, pm_message_t state)
+static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
 {
        struct pci_dev * pci_dev = to_pci_dev(dev);
        struct pci_driver * drv = pci_dev->driver;
@@ -308,26 +352,7 @@ static int pci_device_suspend_late(struct device * dev, pm_message_t state)
        return i;
 }
 
-/*
- * Default resume method for devices that have no driver provided resume,
- * or not even a driver at all.
- */
-static int pci_default_resume(struct pci_dev *pci_dev)
-{
-       int retval = 0;
-
-       /* restore the PCI config space */
-       pci_restore_state(pci_dev);
-       /* if the device was enabled before suspend, reenable */
-       retval = pci_reenable_device(pci_dev);
-       /* if the device was busmaster before the suspend, make it busmaster again */
-       if (pci_dev->is_busmaster)
-               pci_set_master(pci_dev);
-
-       return retval;
-}
-
-static int pci_device_resume(struct device * dev)
+static int pci_legacy_resume(struct device *dev)
 {
        int error;
        struct pci_dev * pci_dev = to_pci_dev(dev);
@@ -336,34 +361,313 @@ static int pci_device_resume(struct device * dev)
        if (drv && drv->resume)
                error = drv->resume(pci_dev);
        else
-               error = pci_default_resume(pci_dev);
+               error = pci_default_pm_resume(pci_dev);
        return error;
 }
 
-static int pci_device_resume_early(struct device * dev)
+static int pci_legacy_resume_early(struct device *dev)
 {
        int error = 0;
        struct pci_dev * pci_dev = to_pci_dev(dev);
        struct pci_driver * drv = pci_dev->driver;
 
-       pci_fixup_device(pci_fixup_resume, pci_dev);
-
        if (drv && drv->resume_early)
                error = drv->resume_early(pci_dev);
        return error;
 }
 
-static void pci_device_shutdown(struct device *dev)
+static int pci_pm_prepare(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+       int error = 0;
+
+       if (drv && drv->pm && drv->pm->prepare)
+               error = drv->pm->prepare(dev);
+
+       return error;
+}
+
+static void pci_pm_complete(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+
+       if (drv && drv->pm && drv->pm->complete)
+               drv->pm->complete(dev);
+}
+
+#ifdef CONFIG_SUSPEND
+
+static int pci_pm_suspend(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct device_driver *drv = dev->driver;
+       int error = 0;
+
+       if (drv && drv->pm) {
+               if (drv->pm->suspend) {
+                       error = drv->pm->suspend(dev);
+                       suspend_report_result(drv->pm->suspend, error);
+               } else {
+                       pci_default_pm_suspend(pci_dev);
+               }
+       } else {
+               error = pci_legacy_suspend(dev, PMSG_SUSPEND);
+       }
+       pci_fixup_device(pci_fixup_suspend, pci_dev);
+
+       return error;
+}
+
+static int pci_pm_suspend_noirq(struct device *dev)
 {
        struct pci_dev *pci_dev = to_pci_dev(dev);
        struct pci_driver *drv = pci_dev->driver;
+       int error = 0;
 
-       if (drv && drv->shutdown)
-               drv->shutdown(pci_dev);
-       pci_msi_shutdown(pci_dev);
-       pci_msix_shutdown(pci_dev);
+       if (drv && drv->pm) {
+               if (drv->pm->suspend_noirq) {
+                       error = drv->pm->suspend_noirq(dev);
+                       suspend_report_result(drv->pm->suspend_noirq, error);
+               }
+       } else {
+               error = pci_legacy_suspend_late(dev, PMSG_SUSPEND);
+       }
+
+       return error;
 }
 
+static int pci_pm_resume(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct device_driver *drv = dev->driver;
+       int error;
+
+       pci_fixup_device(pci_fixup_resume, pci_dev);
+
+       if (drv && drv->pm) {
+               error = drv->pm->resume ? drv->pm->resume(dev) :
+                       pci_default_pm_resume(pci_dev);
+       } else {
+               error = pci_legacy_resume(dev);
+       }
+
+       return error;
+}
+
+static int pci_pm_resume_noirq(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct pci_driver *drv = pci_dev->driver;
+       int error = 0;
+
+       pci_fixup_device(pci_fixup_resume_early, pci_dev);
+
+       if (drv && drv->pm) {
+               if (drv->pm->resume_noirq)
+                       error = drv->pm->resume_noirq(dev);
+       } else {
+               error = pci_legacy_resume_early(dev);
+       }
+
+       return error;
+}
+
+#else /* !CONFIG_SUSPEND */
+
+#define pci_pm_suspend         NULL
+#define pci_pm_suspend_noirq   NULL
+#define pci_pm_resume          NULL
+#define pci_pm_resume_noirq    NULL
+
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_HIBERNATION
+
+static int pci_pm_freeze(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct device_driver *drv = dev->driver;
+       int error = 0;
+
+       if (drv && drv->pm) {
+               if (drv->pm->freeze) {
+                       error = drv->pm->freeze(dev);
+                       suspend_report_result(drv->pm->freeze, error);
+               } else {
+                       pci_default_pm_suspend(pci_dev);
+               }
+       } else {
+               error = pci_legacy_suspend(dev, PMSG_FREEZE);
+               pci_fixup_device(pci_fixup_suspend, pci_dev);
+       }
+
+       return error;
+}
+
+static int pci_pm_freeze_noirq(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct pci_driver *drv = pci_dev->driver;
+       int error = 0;
+
+       if (drv && drv->pm) {
+               if (drv->pm->freeze_noirq) {
+                       error = drv->pm->freeze_noirq(dev);
+                       suspend_report_result(drv->pm->freeze_noirq, error);
+               }
+       } else {
+               error = pci_legacy_suspend_late(dev, PMSG_FREEZE);
+       }
+
+       return error;
+}
+
+static int pci_pm_thaw(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+       int error = 0;
+
+       if (drv && drv->pm) {
+               if (drv->pm->thaw)
+                       error =  drv->pm->thaw(dev);
+       } else {
+               pci_fixup_device(pci_fixup_resume, to_pci_dev(dev));
+               error = pci_legacy_resume(dev);
+       }
+
+       return error;
+}
+
+static int pci_pm_thaw_noirq(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct pci_driver *drv = pci_dev->driver;
+       int error = 0;
+
+       if (drv && drv->pm) {
+               if (drv->pm->thaw_noirq)
+                       error = drv->pm->thaw_noirq(dev);
+       } else {
+               pci_fixup_device(pci_fixup_resume_early, pci_dev);
+               error = pci_legacy_resume_early(dev);
+       }
+
+       return error;
+}
+
+static int pci_pm_poweroff(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+       int error = 0;
+
+       pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
+
+       if (drv && drv->pm) {
+               if (drv->pm->poweroff) {
+                       error = drv->pm->poweroff(dev);
+                       suspend_report_result(drv->pm->poweroff, error);
+               }
+       } else {
+               error = pci_legacy_suspend(dev, PMSG_HIBERNATE);
+       }
+
+       return error;
+}
+
+static int pci_pm_poweroff_noirq(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct pci_driver *drv = pci_dev->driver;
+       int error = 0;
+
+       if (drv && drv->pm) {
+               if (drv->pm->poweroff_noirq) {
+                       error = drv->pm->poweroff_noirq(dev);
+                       suspend_report_result(drv->pm->poweroff_noirq, error);
+               }
+       } else {
+               error = pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
+       }
+
+       return error;
+}
+
+static int pci_pm_restore(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct device_driver *drv = dev->driver;
+       int error;
+
+       if (drv && drv->pm) {
+               error = drv->pm->restore ? drv->pm->restore(dev) :
+                       pci_default_pm_resume(pci_dev);
+       } else {
+               error = pci_legacy_resume(dev);
+       }
+       pci_fixup_device(pci_fixup_resume, pci_dev);
+
+       return error;
+}
+
+static int pci_pm_restore_noirq(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct pci_driver *drv = pci_dev->driver;
+       int error = 0;
+
+       pci_fixup_device(pci_fixup_resume, pci_dev);
+
+       if (drv && drv->pm) {
+               if (drv->pm->restore_noirq)
+                       error = drv->pm->restore_noirq(dev);
+       } else {
+               error = pci_legacy_resume_early(dev);
+       }
+       pci_fixup_device(pci_fixup_resume_early, pci_dev);
+
+       return error;
+}
+
+#else /* !CONFIG_HIBERNATION */
+
+#define pci_pm_freeze          NULL
+#define pci_pm_freeze_noirq    NULL
+#define pci_pm_thaw            NULL
+#define pci_pm_thaw_noirq      NULL
+#define pci_pm_poweroff                NULL
+#define pci_pm_poweroff_noirq  NULL
+#define pci_pm_restore         NULL
+#define pci_pm_restore_noirq   NULL
+
+#endif /* !CONFIG_HIBERNATION */
+
+struct pm_ext_ops pci_pm_ops = {
+       .base = {
+               .prepare = pci_pm_prepare,
+               .complete = pci_pm_complete,
+               .suspend = pci_pm_suspend,
+               .resume = pci_pm_resume,
+               .freeze = pci_pm_freeze,
+               .thaw = pci_pm_thaw,
+               .poweroff = pci_pm_poweroff,
+               .restore = pci_pm_restore,
+       },
+       .suspend_noirq = pci_pm_suspend_noirq,
+       .resume_noirq = pci_pm_resume_noirq,
+       .freeze_noirq = pci_pm_freeze_noirq,
+       .thaw_noirq = pci_pm_thaw_noirq,
+       .poweroff_noirq = pci_pm_poweroff_noirq,
+       .restore_noirq = pci_pm_restore_noirq,
+};
+
+#define PCI_PM_OPS_PTR &pci_pm_ops
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define PCI_PM_OPS_PTR NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
 /**
  * __pci_register_driver - register a new pci driver
  * @drv: the driver structure to register
@@ -386,6 +690,9 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
        drv->driver.owner = owner;
        drv->driver.mod_name = mod_name;
 
+       if (drv->pm)
+               drv->driver.pm = &drv->pm->base;
+
        spin_lock_init(&drv->dynids.lock);
        INIT_LIST_HEAD(&drv->dynids.list);
 
@@ -511,12 +818,9 @@ struct bus_type pci_bus_type = {
        .uevent         = pci_uevent,
        .probe          = pci_device_probe,
        .remove         = pci_device_remove,
-       .suspend        = pci_device_suspend,
-       .suspend_late   = pci_device_suspend_late,
-       .resume_early   = pci_device_resume_early,
-       .resume         = pci_device_resume,
        .shutdown       = pci_device_shutdown,
        .dev_attrs      = pci_dev_attrs,
+       .pm             = PCI_PM_OPS_PTR,
 };
 
 static int __init pci_driver_init(void)
index 75c60239cadd035d967677d60e662b5a23e360fe..44a46c92b721a2c6d97fe1b795b6e4c0b1656ad6 100644 (file)
@@ -1,6 +1,4 @@
 /*
- *     $Id: pci.c,v 1.91 1999/01/21 13:34:01 davem Exp $
- *
  *     PCI Bus Services, see include/linux/pci.h for further explanation.
  *
  *     Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
@@ -19,6 +17,7 @@
 #include <linux/string.h>
 #include <linux/log2.h>
 #include <linux/pci-aspm.h>
+#include <linux/pm_wakeup.h>
 #include <asm/dma.h>   /* isa_dma_bridge_buggy */
 #include "pci.h"
 
@@ -378,74 +377,90 @@ pci_restore_bars(struct pci_dev *dev)
                pci_update_resource(dev, &dev->resource[i], i);
 }
 
-int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t t);
+static struct pci_platform_pm_ops *pci_platform_pm;
 
-/**
- * pci_set_power_state - Set the power state of a PCI device
- * @dev: PCI device to be suspended
- * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering
- *
- * Transition a device to a new power state, using the Power Management 
- * Capabilities in the device's config space.
- *
- * RETURN VALUE: 
- * -EINVAL if trying to enter a lower state than we're already in.
- * 0 if we're already in the requested state.
- * -EIO if device does not support PCI PM.
- * 0 if we can successfully change the power state.
- */
-int
-pci_set_power_state(struct pci_dev *dev, pci_power_t state)
+int pci_set_platform_pm(struct pci_platform_pm_ops *ops)
 {
-       int pm, need_restore = 0;
-       u16 pmcsr, pmc;
+       if (!ops->is_manageable || !ops->set_state || !ops->choose_state
+           || !ops->sleep_wake || !ops->can_wakeup)
+               return -EINVAL;
+       pci_platform_pm = ops;
+       return 0;
+}
 
-       /* bound the state we're entering */
-       if (state > PCI_D3hot)
-               state = PCI_D3hot;
+static inline bool platform_pci_power_manageable(struct pci_dev *dev)
+{
+       return pci_platform_pm ? pci_platform_pm->is_manageable(dev) : false;
+}
 
-       /*
-        * If the device or the parent bridge can't support PCI PM, ignore
-        * the request if we're doing anything besides putting it into D0
-        * (which would only happen on boot).
-        */
-       if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
-               return 0;
+static inline int platform_pci_set_power_state(struct pci_dev *dev,
+                                                pci_power_t t)
+{
+       return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS;
+}
 
-       /* find PCI PM capability in list */
-       pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev)
+{
+       return pci_platform_pm ?
+                       pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR;
+}
 
-       /* abort if the device doesn't support PM capabilities */
-       if (!pm)
+static inline bool platform_pci_can_wakeup(struct pci_dev *dev)
+{
+       return pci_platform_pm ? pci_platform_pm->can_wakeup(dev) : false;
+}
+
+static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable)
+{
+       return pci_platform_pm ?
+                       pci_platform_pm->sleep_wake(dev, enable) : -ENODEV;
+}
+
+/**
+ * pci_raw_set_power_state - Use PCI PM registers to set the power state of
+ *                           given PCI device
+ * @dev: PCI device to handle.
+ * @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
+ *
+ * RETURN VALUE:
+ * -EINVAL if the requested state is invalid.
+ * -EIO if device does not support PCI PM or its PM capabilities register has a
+ * wrong version, or device doesn't support the requested state.
+ * 0 if device already is in the requested state.
+ * 0 if device's power state has been successfully changed.
+ */
+static int
+pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
+{
+       u16 pmcsr;
+       bool need_restore = false;
+
+       if (!dev->pm_cap)
                return -EIO;
 
+       if (state < PCI_D0 || state > PCI_D3hot)
+               return -EINVAL;
+
        /* Validate current state:
         * Can enter D0 from any state, but if we can only go deeper 
         * to sleep if we're already in a low power state
         */
-       if (state != PCI_D0 && dev->current_state > state) {
-               printk(KERN_ERR "%s(): %s: state=%d, current state=%d\n",
-                       __func__, pci_name(dev), state, dev->current_state);
+       if (dev->current_state == state) {
+               /* we're already there */
+               return 0;
+       } else if (state != PCI_D0 && dev->current_state <= PCI_D3cold
+           && dev->current_state > state) {
+               dev_err(&dev->dev, "invalid power transition "
+                       "(from state %d to %d)\n", dev->current_state, state);
                return -EINVAL;
-       } else if (dev->current_state == state)
-               return 0;        /* we're already there */
-
-
-       pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
-       if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
-               printk(KERN_DEBUG
-                      "PCI: %s has unsupported PM cap regs version (%u)\n",
-                      pci_name(dev), pmc & PCI_PM_CAP_VER_MASK);
-               return -EIO;
        }
 
        /* check if this device supports the desired state */
-       if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
-               return -EIO;
-       else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))
+       if ((state == PCI_D1 && !dev->d1_support)
+          || (state == PCI_D2 && !dev->d2_support))
                return -EIO;
 
-       pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
+       pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
 
        /* If we're (effectively) in D3, force entire word to 0.
         * This doesn't affect PME_Status, disables PME_En, and
@@ -461,7 +476,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
        case PCI_UNKNOWN: /* Boot-up */
                if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
                 && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
-                       need_restore = 1;
+                       need_restore = true;
                /* Fall-through: force to D0 */
        default:
                pmcsr = 0;
@@ -469,7 +484,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
        }
 
        /* enter specified state */
-       pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr);
+       pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
 
        /* Mandatory power management transition delays */
        /* see PCI PM 1.1 5.6.1 table 18 */
@@ -478,13 +493,6 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
        else if (state == PCI_D2 || dev->current_state == PCI_D2)
                udelay(200);
 
-       /*
-        * Give firmware a chance to be called, such as ACPI _PRx, _PSx
-        * Firmware method after native method ?
-        */
-       if (platform_pci_set_power_state)
-               platform_pci_set_power_state(dev, state);
-
        dev->current_state = state;
 
        /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
@@ -508,8 +516,77 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
        return 0;
 }
 
-pci_power_t (*platform_pci_choose_state)(struct pci_dev *dev);
+/**
+ * pci_update_current_state - Read PCI power state of given device from its
+ *                            PCI PM registers and cache it
+ * @dev: PCI device to handle.
+ */
+static void pci_update_current_state(struct pci_dev *dev)
+{
+       if (dev->pm_cap) {
+               u16 pmcsr;
+
+               pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
+               dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
+       }
+}
+
+/**
+ * pci_set_power_state - Set the power state of a PCI device
+ * @dev: PCI device to handle.
+ * @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
+ *
+ * Transition a device to a new power state, using the platform formware and/or
+ * the device's PCI PM registers.
+ *
+ * RETURN VALUE:
+ * -EINVAL if the requested state is invalid.
+ * -EIO if device does not support PCI PM or its PM capabilities register has a
+ * wrong version, or device doesn't support the requested state.
+ * 0 if device already is in the requested state.
+ * 0 if device's power state has been successfully changed.
+ */
+int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
+{
+       int error;
+
+       /* bound the state we're entering */
+       if (state > PCI_D3hot)
+               state = PCI_D3hot;
+       else if (state < PCI_D0)
+               state = PCI_D0;
+       else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
+               /*
+                * If the device or the parent bridge do not support PCI PM,
+                * ignore the request if we're doing anything other than putting
+                * it into D0 (which would only happen on boot).
+                */
+               return 0;
+
+       if (state == PCI_D0 && platform_pci_power_manageable(dev)) {
+               /*
+                * Allow the platform to change the state, for example via ACPI
+                * _PR0, _PS0 and some such, but do not trust it.
+                */
+               int ret = platform_pci_set_power_state(dev, PCI_D0);
+               if (!ret)
+                       pci_update_current_state(dev);
+       }
+
+       error = pci_raw_set_power_state(dev, state);
+
+       if (state > PCI_D0 && platform_pci_power_manageable(dev)) {
+               /* Allow the platform to finalize the transition */
+               int ret = platform_pci_set_power_state(dev, state);
+               if (!ret) {
+                       pci_update_current_state(dev);
+                       error = 0;
+               }
+       }
+
+       return error;
+}
+
 /**
  * pci_choose_state - Choose the power state of a PCI device
  * @dev: PCI device to be suspended
@@ -527,11 +604,9 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
        if (!pci_find_capability(dev, PCI_CAP_ID_PM))
                return PCI_D0;
 
-       if (platform_pci_choose_state) {
-               ret = platform_pci_choose_state(dev);
-               if (ret != PCI_POWER_ERROR)
-                       return ret;
-       }
+       ret = platform_pci_choose_state(dev);
+       if (ret != PCI_POWER_ERROR)
+               return ret;
 
        switch (state.event) {
        case PM_EVENT_ON:
@@ -543,7 +618,8 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
        case PM_EVENT_HIBERNATE:
                return PCI_D3hot;
        default:
-               printk("Unrecognized suspend event %d\n", state.event);
+               dev_info(&dev->dev, "unrecognized suspend event %d\n",
+                        state.event);
                BUG();
        }
        return PCI_D0;
@@ -568,7 +644,7 @@ static int pci_save_pcie_state(struct pci_dev *dev)
        else
                found = 1;
        if (!save_state) {
-               dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+               dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n");
                return -ENOMEM;
        }
        cap = (u16 *)&save_state->data[0];
@@ -619,7 +695,7 @@ static int pci_save_pcix_state(struct pci_dev *dev)
        else
                found = 1;
        if (!save_state) {
-               dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+               dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n");
                return -ENOMEM;
        }
        cap = (u16 *)&save_state->data[0];
@@ -685,10 +761,9 @@ pci_restore_state(struct pci_dev *dev)
        for (i = 15; i >= 0; i--) {
                pci_read_config_dword(dev, i * 4, &val);
                if (val != dev->saved_config_space[i]) {
-                       printk(KERN_DEBUG "PM: Writing back config space on "
-                               "device %s at offset %x (was %x, writing %x)\n",
-                               pci_name(dev), i,
-                               val, (int)dev->saved_config_space[i]);
+                       dev_printk(KERN_DEBUG, &dev->dev, "restoring config "
+                               "space at offset %#x (was %#x, writing %#x)\n",
+                               i, val, (int)dev->saved_config_space[i]);
                        pci_write_config_dword(dev,i * 4,
                                dev->saved_config_space[i]);
                }
@@ -960,6 +1035,46 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
        return pcibios_set_pcie_reset_state(dev, state);
 }
 
+/**
+ * pci_pme_capable - check the capability of PCI device to generate PME#
+ * @dev: PCI device to handle.
+ * @state: PCI state from which device will issue PME#.
+ */
+static bool pci_pme_capable(struct pci_dev *dev, pci_power_t state)
+{
+       if (!dev->pm_cap)
+               return false;
+
+       return !!(dev->pme_support & (1 << state));
+}
+
+/**
+ * pci_pme_active - enable or disable PCI device's PME# function
+ * @dev: PCI device to handle.
+ * @enable: 'true' to enable PME# generation; 'false' to disable it.
+ *
+ * The caller must verify that the device is capable of generating PME# before
+ * calling this function with @enable equal to 'true'.
+ */
+static void pci_pme_active(struct pci_dev *dev, bool enable)
+{
+       u16 pmcsr;
+
+       if (!dev->pm_cap)
+               return;
+
+       pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
+       /* Clear PME_Status by writing 1 to it and enable PME# */
+       pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
+       if (!enable)
+               pmcsr &= ~PCI_PM_CTRL_PME_ENABLE;
+
+       pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
+
+       dev_printk(KERN_INFO, &dev->dev, "PME# %s\n",
+                       enable ? "enabled" : "disabled");
+}
+
 /**
  * pci_enable_wake - enable PCI device as wakeup event source
  * @dev: PCI device affected
@@ -971,66 +1086,173 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
  * called automatically by this routine.
  *
  * Devices with legacy power management (no standard PCI PM capabilities)
- * always require such platform hooks.  Depending on the platform, devices
- * supporting the standard PCI PME# signal may require such platform hooks;
- * they always update bits in config space to allow PME# generation.
+ * always require such platform hooks.
  *
- * -EIO is returned if the device can't ever be a wakeup event source.
- * -EINVAL is returned if the device can't generate wakeup events from
- * the specified PCI state.  Returns zero if the operation is successful.
+ * RETURN VALUE:
+ * 0 is returned on success
+ * -EINVAL is returned if device is not supposed to wake up the system
+ * Error code depending on the platform is returned if both the platform and
+ * the native mechanism fail to enable the generation of wake-up events
  */
 int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
 {
-       int pm;
-       int status;
-       u16 value;
-
-       /* Note that drivers should verify device_may_wakeup(&dev->dev)
-        * before calling this function.  Platform code should report
-        * errors when drivers try to enable wakeup on devices that
-        * can't issue wakeups, or on which wakeups were disabled by
-        * userspace updating the /sys/devices.../power/wakeup file.
+       int error = 0;
+       bool pme_done = false;
+
+       if (!device_may_wakeup(&dev->dev))
+               return -EINVAL;
+
+       /*
+        * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don
+        * Anderson we should be doing PME# wake enable followed by ACPI wake
+        * enable.  To disable wake-up we call the platform first, for symmetry.
         */
 
-       status = call_platform_enable_wakeup(&dev->dev, enable);
+       if (!enable && platform_pci_can_wakeup(dev))
+               error = platform_pci_sleep_wake(dev, false);
 
-       /* find PCI PM capability in list */
-       pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+       if (!enable || pci_pme_capable(dev, state)) {
+               pci_pme_active(dev, enable);
+               pme_done = true;
+       }
 
-       /* If device doesn't support PM Capabilities, but caller wants to
-        * disable wake events, it's a NOP.  Otherwise fail unless the
-        * platform hooks handled this legacy device already.
-        */
-       if (!pm)
-               return enable ? status : 0;
+       if (enable && platform_pci_can_wakeup(dev))
+               error = platform_pci_sleep_wake(dev, true);
 
-       /* Check device's ability to generate PME# */
-       pci_read_config_word(dev,pm+PCI_PM_PMC,&value);
+       return pme_done ? 0 : error;
+}
 
-       value &= PCI_PM_CAP_PME_MASK;
-       value >>= ffs(PCI_PM_CAP_PME_MASK) - 1;   /* First bit of mask */
+/**
+ * pci_prepare_to_sleep - prepare PCI device for system-wide transition into
+ *                        a sleep state
+ * @dev: Device to handle.
+ *
+ * Choose the power state appropriate for the device depending on whether
+ * it can wake up the system and/or is power manageable by the platform
+ * (PCI_D3hot is the default) and put the device into that state.
+ */
+int pci_prepare_to_sleep(struct pci_dev *dev)
+{
+       pci_power_t target_state = PCI_D3hot;
+       int error;
 
-       /* Check if it can generate PME# from requested state. */
-       if (!value || !(value & (1 << state))) {
-               /* if it can't, revert what the platform hook changed,
-                * always reporting the base "EINVAL, can't PME#" error
+       if (platform_pci_power_manageable(dev)) {
+               /*
+                * Call the platform to choose the target state of the device
+                * and enable wake-up from this state if supported.
                 */
-               if (enable)
-                       call_platform_enable_wakeup(&dev->dev, 0);
-               return enable ? -EINVAL : 0;
+               pci_power_t state = platform_pci_choose_state(dev);
+
+               switch (state) {
+               case PCI_POWER_ERROR:
+               case PCI_UNKNOWN:
+                       break;
+               case PCI_D1:
+               case PCI_D2:
+                       if (pci_no_d1d2(dev))
+                               break;
+               default:
+                       target_state = state;
+               }
+       } else if (device_may_wakeup(&dev->dev)) {
+               /*
+                * Find the deepest state from which the device can generate
+                * wake-up events, make it the target state and enable device
+                * to generate PME#.
+                */
+               if (!dev->pm_cap)
+                       return -EIO;
+
+               if (dev->pme_support) {
+                       while (target_state
+                             && !(dev->pme_support & (1 << target_state)))
+                               target_state--;
+               }
        }
 
-       pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
+       pci_enable_wake(dev, target_state, true);
 
-       /* Clear PME_Status by writing 1 to it and enable PME# */
-       value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
+       error = pci_set_power_state(dev, target_state);
 
-       if (!enable)
-               value &= ~PCI_PM_CTRL_PME_ENABLE;
+       if (error)
+               pci_enable_wake(dev, target_state, false);
 
-       pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
+       return error;
+}
 
-       return 0;
+/**
+ * pci_back_from_sleep - turn PCI device on during system-wide transition into
+ *                       the working state a sleep state
+ * @dev: Device to handle.
+ *
+ * Disable device's sytem wake-up capability and put it into D0.
+ */
+int pci_back_from_sleep(struct pci_dev *dev)
+{
+       pci_enable_wake(dev, PCI_D0, false);
+       return pci_set_power_state(dev, PCI_D0);
+}
+
+/**
+ * pci_pm_init - Initialize PM functions of given PCI device
+ * @dev: PCI device to handle.
+ */
+void pci_pm_init(struct pci_dev *dev)
+{
+       int pm;
+       u16 pmc;
+
+       dev->pm_cap = 0;
+
+       /* find PCI PM capability in list */
+       pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+       if (!pm)
+               return;
+       /* Check device's ability to generate PME# */
+       pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
+
+       if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
+               dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n",
+                       pmc & PCI_PM_CAP_VER_MASK);
+               return;
+       }
+
+       dev->pm_cap = pm;
+
+       dev->d1_support = false;
+       dev->d2_support = false;
+       if (!pci_no_d1d2(dev)) {
+               if (pmc & PCI_PM_CAP_D1) {
+                       dev_printk(KERN_DEBUG, &dev->dev, "supports D1\n");
+                       dev->d1_support = true;
+               }
+               if (pmc & PCI_PM_CAP_D2) {
+                       dev_printk(KERN_DEBUG, &dev->dev, "supports D2\n");
+                       dev->d2_support = true;
+               }
+       }
+
+       pmc &= PCI_PM_CAP_PME_MASK;
+       if (pmc) {
+               dev_printk(KERN_INFO, &dev->dev,
+                       "PME# supported from%s%s%s%s%s\n",
+                       (pmc & PCI_PM_CAP_PME_D0) ? " D0" : "",
+                       (pmc & PCI_PM_CAP_PME_D1) ? " D1" : "",
+                       (pmc & PCI_PM_CAP_PME_D2) ? " D2" : "",
+                       (pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "",
+                       (pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : "");
+               dev->pme_support = pmc >> PCI_PM_CAP_PME_SHIFT;
+               /*
+                * Make device's PM flags reflect the wake-up capability, but
+                * let the user space enable it to wake up the system as needed.
+                */
+               device_set_wakeup_capable(&dev->dev, true);
+               device_set_wakeup_enable(&dev->dev, false);
+               /* Disable the PME# generation functionality */
+               pci_pme_active(dev, false);
+       } else {
+               dev->pme_support = 0;
+       }
 }
 
 int
@@ -1116,13 +1338,11 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
        return 0;
 
 err_out:
-       printk (KERN_WARNING "PCI: Unable to reserve %s region #%d:%llx@%llx "
-               "for device %s\n",
-               pci_resource_flags(pdev, bar) & IORESOURCE_IO ? "I/O" : "mem",
-               bar + 1, /* PCI BAR # */
-               (unsigned long long)pci_resource_len(pdev, bar),
-               (unsigned long long)pci_resource_start(pdev, bar),
-               pci_name(pdev));
+       dev_warn(&pdev->dev, "BAR %d: can't reserve %s region [%#llx-%#llx]\n",
+                bar,
+                pci_resource_flags(pdev, bar) & IORESOURCE_IO ? "I/O" : "mem",
+                (unsigned long long)pci_resource_start(pdev, bar),
+                (unsigned long long)pci_resource_end(pdev, bar));
        return -EBUSY;
 }
 
@@ -1214,7 +1434,7 @@ pci_set_master(struct pci_dev *dev)
 
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
        if (! (cmd & PCI_COMMAND_MASTER)) {
-               pr_debug("PCI: Enabling bus mastering for device %s\n", pci_name(dev));
+               dev_dbg(&dev->dev, "enabling bus mastering\n");
                cmd |= PCI_COMMAND_MASTER;
                pci_write_config_word(dev, PCI_COMMAND, cmd);
        }
@@ -1279,8 +1499,8 @@ pci_set_cacheline_size(struct pci_dev *dev)
        if (cacheline_size == pci_cache_line_size)
                return 0;
 
-       printk(KERN_DEBUG "PCI: cache line size of %d is not supported "
-              "by device %s\n", pci_cache_line_size << 2, pci_name(dev));
+       dev_printk(KERN_DEBUG, &dev->dev, "cache line size of %d is not "
+                  "supported\n", pci_cache_line_size << 2);
 
        return -EINVAL;
 }
@@ -1305,8 +1525,7 @@ pci_set_mwi(struct pci_dev *dev)
 
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
        if (! (cmd & PCI_COMMAND_INVALIDATE)) {
-               pr_debug("PCI: Enabling Mem-Wr-Inval for device %s\n",
-                       pci_name(dev));
+               dev_dbg(&dev->dev, "enabling Mem-Wr-Inval\n");
                cmd |= PCI_COMMAND_INVALIDATE;
                pci_write_config_word(dev, PCI_COMMAND, cmd);
        }
@@ -1702,5 +1921,7 @@ EXPORT_SYMBOL(pci_set_power_state);
 EXPORT_SYMBOL(pci_save_state);
 EXPORT_SYMBOL(pci_restore_state);
 EXPORT_SYMBOL(pci_enable_wake);
+EXPORT_SYMBOL(pci_prepare_to_sleep);
+EXPORT_SYMBOL(pci_back_from_sleep);
 EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
 
index 312daff834b62c86387ad8043640e0cb306b602f..d807cd786f20a18419105df88d8cf816019cab48 100644 (file)
@@ -5,10 +5,36 @@ extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
 extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
 extern void pci_cleanup_rom(struct pci_dev *dev);
 
-/* Firmware callbacks */
-extern pci_power_t (*platform_pci_choose_state)(struct pci_dev *dev);
-extern int (*platform_pci_set_power_state)(struct pci_dev *dev,
-                                               pci_power_t state);
+/**
+ * Firmware PM callbacks
+ *
+ * @is_manageable - returns 'true' if given device is power manageable by the
+ *                  platform firmware
+ *
+ * @set_state - invokes the platform firmware to set the device's power state
+ *
+ * @choose_state - returns PCI power state of given device preferred by the
+ *                 platform; to be used during system-wide transitions from a
+ *                 sleeping state to the working state and vice versa
+ *
+ * @can_wakeup - returns 'true' if given device is capable of waking up the
+ *               system from a sleeping state
+ *
+ * @sleep_wake - enables/disables the system wake up capability of given device
+ *
+ * If given platform is generally capable of power managing PCI devices, all of
+ * these callbacks are mandatory.
+ */
+struct pci_platform_pm_ops {
+       bool (*is_manageable)(struct pci_dev *dev);
+       int (*set_state)(struct pci_dev *dev, pci_power_t state);
+       pci_power_t (*choose_state)(struct pci_dev *dev);
+       bool (*can_wakeup)(struct pci_dev *dev);
+       int (*sleep_wake)(struct pci_dev *dev, bool enable);
+};
+
+extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
+extern void pci_pm_init(struct pci_dev *dev);
 
 extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val);
 extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val);
@@ -105,3 +131,16 @@ pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
 }
 
 struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev);
+
+/* PCI slot sysfs helper code */
+#define to_pci_slot(s) container_of(s, struct pci_slot, kobj)
+
+extern struct kset *pci_slots_kset;
+
+struct pci_slot_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct pci_slot *, char *);
+       ssize_t (*store)(struct pci_slot *, const char *, size_t);
+};
+#define to_pci_slot_attr(s) container_of(s, struct pci_slot_attribute, attr)
+
index 07c3bdb6edc2ad5b261f3877eed725466f4d895a..77036f46acfe22e1cfdebeb3613cb6f3527b6df2 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/pcieport_if.h>
 
 #include "aerdrv.h"
+#include "../../pci.h"
 
 /*
  * Version Information
@@ -219,8 +220,7 @@ static int __devinit aer_probe (struct pcie_device *dev,
 
        /* Alloc rpc data structure */
        if (!(rpc = aer_alloc_rpc(dev))) {
-               printk(KERN_DEBUG "%s: Alloc rpc fails on PCIE device[%s]\n",
-                       __func__, device->bus_id);
+               dev_printk(KERN_DEBUG, device, "alloc rpc failed\n");
                aer_remove(dev);
                return -ENOMEM;
        }
@@ -228,8 +228,7 @@ static int __devinit aer_probe (struct pcie_device *dev,
        /* Request IRQ ISR */
        if ((status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv",
                                dev))) {
-               printk(KERN_DEBUG "%s: Request ISR fails on PCIE device[%s]\n",
-                       __func__, device->bus_id);
+               dev_printk(KERN_DEBUG, device, "request IRQ failed\n");
                aer_remove(dev);
                return status;
        }
@@ -273,7 +272,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
         * to issue Configuration Requests to those devices.
         */
        msleep(200);
-       printk(KERN_DEBUG "Complete link reset at Root[%s]\n", dev->dev.bus_id);
+       dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
 
        /* Enable Root Port's interrupt in response to error messages */
        pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
index d39a78dbd026b03b4d58d450d6efc11ccd3ca652..30f581b8791f5f84c6ab72a1f76bf1de8a5a2c87 100644 (file)
@@ -50,10 +50,10 @@ int aer_osc_setup(struct pcie_device *pciedev)
        }
 
        if (ACPI_FAILURE(status)) {
-               printk(KERN_DEBUG "AER service couldn't init device %s - %s\n",
-                   pciedev->device.bus_id,
-                   (status == AE_SUPPORT || status == AE_NOT_FOUND) ?
-                   "no _OSC support" : "Run ACPI _OSC fails");
+               dev_printk(KERN_DEBUG, &pciedev->device, "AER service couldn't "
+                          "init device: %s\n",
+                          (status == AE_SUPPORT || status == AE_NOT_FOUND) ?
+                          "no _OSC support" : "_OSC failed");
                return -1;
        }
 
index aaa82392d1dcc4c79e79b9432e68f66ab6b93be6..ee5e7b5176d0f5d0986fbcb04050e37ff8b324ee 100644 (file)
@@ -221,9 +221,9 @@ static void report_error_detected(struct pci_dev *dev, void *data)
                         * of a driver for this device is unaware of
                         * its hw state.
                         */
-                       printk(KERN_DEBUG "Device ID[%s] has %s\n",
-                                       dev->dev.bus_id, (dev->driver) ?
-                                       "no AER-aware driver" : "no driver");
+                       dev_printk(KERN_DEBUG, &dev->dev, "device has %s\n",
+                                  dev->driver ?
+                                  "no AER-aware driver" : "no driver");
                }
                return;
        }
@@ -304,7 +304,7 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
 {
        struct aer_broadcast_data result_data;
 
-       printk(KERN_DEBUG "Broadcast %s message\n", error_mesg);
+       dev_printk(KERN_DEBUG, &dev->dev, "broadcast %s message\n", error_mesg);
        result_data.state = state;
        if (cb == report_error_detected)
                result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
@@ -404,18 +404,16 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
                        data.aer_driver =
                                to_service_driver(aerdev->device.driver);
                } else {
-                       printk(KERN_DEBUG "No link-reset support to Device ID"
-                               "[%s]\n",
-                               dev->dev.bus_id);
+                       dev_printk(KERN_DEBUG, &dev->dev, "no link-reset "
+                                  "support\n");
                        return PCI_ERS_RESULT_DISCONNECT;
                }
        }
 
        status = data.aer_driver->reset_link(udev);
        if (status != PCI_ERS_RESULT_RECOVERED) {
-               printk(KERN_DEBUG "Link reset at upstream Device ID"
-                       "[%s] failed\n",
-                       udev->dev.bus_id);
+               dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream "
+                          "device %s failed\n", pci_name(udev));
                return PCI_ERS_RESULT_DISCONNECT;
        }
 
@@ -511,10 +509,12 @@ static void handle_error_source(struct pcie_device * aerdev,
        } else {
                status = do_recovery(aerdev, dev, info.severity);
                if (status == PCI_ERS_RESULT_RECOVERED) {
-                       printk(KERN_DEBUG "AER driver successfully recovered\n");
+                       dev_printk(KERN_DEBUG, &dev->dev, "AER driver "
+                                  "successfully recovered\n");
                } else {
                        /* TODO: Should kernel panic here? */
-                       printk(KERN_DEBUG "AER driver didn't recover\n");
+                       dev_printk(KERN_DEBUG, &dev->dev, "AER driver didn't "
+                                  "recover\n");
                }
        }
 }
index 3f0976868eda656c9a2441caae804c96f9cc3fac..359fe5568df10263cd9c0620c42e7f9c8ffef1bd 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/pm.h>
 
 #include <linux/pcieport_if.h>
+#include "portdrv.h"
 
 static int pcie_port_bus_match(struct device *dev, struct device_driver *drv);
 static int pcie_port_bus_suspend(struct device *dev, pm_message_t state);
index fb0abfa508dce74cce8efe89982e0ae9ccafc879..890f0d2b370af7003625326075073f7641e549ce 100644 (file)
@@ -23,20 +23,20 @@ static int pcie_port_probe_service(struct device *dev)
 {
        struct pcie_device *pciedev;
        struct pcie_port_service_driver *driver;
-       int status = -ENODEV;
+       int status;
 
        if (!dev || !dev->driver)
-               return status;
+               return -ENODEV;
 
        driver = to_service_driver(dev->driver);
        if (!driver || !driver->probe)
-               return status;
+               return -ENODEV;
 
        pciedev = to_pcie_device(dev);
        status = driver->probe(pciedev, driver->id_table);
        if (!status) {
-               printk(KERN_DEBUG "Load service driver %s on pcie device %s\n",
-                       driver->name, dev->bus_id);
+               dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n",
+                       driver->name);
                get_device(dev);
        }
        return status;
@@ -53,8 +53,8 @@ static int pcie_port_remove_service(struct device *dev)
        pciedev = to_pcie_device(dev);
        driver = to_service_driver(dev->driver);
        if (driver && driver->remove) { 
-               printk(KERN_DEBUG "Unload service driver %s on pcie device %s\n",
-                       driver->name, dev->bus_id);
+               dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n",
+                       driver->name);
                driver->remove(pciedev);
                put_device(dev);
        }
@@ -103,7 +103,7 @@ static int pcie_port_resume_service(struct device *dev)
  */
 static void release_pcie_device(struct device *dev)
 {
-       printk(KERN_DEBUG "Free Port Service[%s]\n", dev->bus_id);
+       dev_printk(KERN_DEBUG, dev, "free port service\n");
        kfree(to_pcie_device(dev));                     
 }
 
@@ -150,7 +150,7 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
        if (pos) {
                struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] = 
                        {{0, 0}, {0, 1}, {0, 2}, {0, 3}};
-               printk("%s Found MSIX capability\n", __func__);
+               dev_info(&dev->dev, "found MSI-X capability\n");
                status = pci_enable_msix(dev, msix_entries, nvec);
                if (!status) {
                        int j = 0;
@@ -165,7 +165,7 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
        if (status) {
                pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
                if (pos) {
-                       printk("%s Found MSI capability\n", __func__);
+                       dev_info(&dev->dev, "found MSI capability\n");
                        status = pci_enable_msi(dev);
                        if (!status) {
                                interrupt_mode = PCIE_PORT_MSI_MODE;
@@ -252,7 +252,7 @@ static struct pcie_device* alloc_pcie_device(struct pci_dev *parent,
                return NULL;
 
        pcie_device_init(parent, device, port_type, service_type, irq,irq_mode);
-       printk(KERN_DEBUG "Allocate Port Service[%s]\n", device->device.bus_id);
+       dev_printk(KERN_DEBUG, &device->device, "allocate port service\n");
        return device;
 }
 
index 51d163238d93e63429bd6d04446c1563aaa8be64..367c9c20000dd59dfdd8e1130be182ed2b3fde51 100644 (file)
@@ -91,9 +91,8 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev,
        
        pci_set_master(dev);
         if (!dev->irq && dev->pin) {
-               printk(KERN_WARNING 
-               "%s->Dev[%04x:%04x] has invalid IRQ. Check vendor BIOS\n", 
-               __func__, dev->vendor, dev->device);
+               dev_warn(&dev->dev, "device [%04x/%04x] has invalid IRQ; "
+                        "check vendor BIOS\n", dev->vendor, dev->device);
        }
        if (pcie_port_device_register(dev)) {
                pci_disable_device(dev);
index 3706ce7972dddc0c389f828a3ec0c4635d1d8efe..b1724cf31b669ef459d3250aa8fae48b38e4c7fa 100644 (file)
@@ -277,8 +277,8 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
                        res->end = res->start + sz64;
 #else
                        if (sz64 > 0x100000000ULL) {
-                               printk(KERN_ERR "PCI: Unable to handle 64-bit "
-                                       "BAR for device %s\n", pci_name(dev));
+                               dev_err(&dev->dev, "BAR %d: can't handle 64-bit"
+                                       " BAR\n", pos);
                                res->start = 0;
                                res->flags = 0;
                        } else if (lhi) {
@@ -329,7 +329,7 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
                return;
 
        if (dev->transparent) {
-               printk(KERN_INFO "PCI: Transparent bridge - %s\n", pci_name(dev));
+               dev_info(&dev->dev, "transparent bridge\n");
                for(i = 3; i < PCI_BUS_NUM_RESOURCES; i++)
                        child->resource[i] = child->parent->resource[i - 3];
        }
@@ -392,7 +392,8 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
                        limit |= ((long) mem_limit_hi) << 32;
 #else
                        if (mem_base_hi || mem_limit_hi) {
-                               printk(KERN_ERR "PCI: Unable to handle 64-bit address space for bridge %s\n", pci_name(dev));
+                               dev_err(&dev->dev, "can't handle 64-bit "
+                                       "address space for bridge\n");
                                return;
                        }
 #endif
@@ -414,6 +415,7 @@ static struct pci_bus * pci_alloc_bus(void)
                INIT_LIST_HEAD(&b->node);
                INIT_LIST_HEAD(&b->children);
                INIT_LIST_HEAD(&b->devices);
+               INIT_LIST_HEAD(&b->slots);
        }
        return b;
 }
@@ -511,8 +513,8 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
 
        pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);
 
-       pr_debug("PCI: Scanning behind PCI bridge %s, config %06x, pass %d\n",
-                pci_name(dev), buses & 0xffffff, pass);
+       dev_dbg(&dev->dev, "scanning behind bridge, config %06x, pass %d\n",
+               buses & 0xffffff, pass);
 
        /* Disable MasterAbortMode during probing to avoid reporting
           of bus errors (in some architectures) */ 
@@ -535,8 +537,8 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
                 * ignore it.  This can happen with the i450NX chipset.
                 */
                if (pci_find_bus(pci_domain_nr(bus), busnr)) {
-                       printk(KERN_INFO "PCI: Bus %04x:%02x already known\n",
-                                       pci_domain_nr(bus), busnr);
+                       dev_info(&dev->dev, "bus %04x:%02x already known\n",
+                                pci_domain_nr(bus), busnr);
                        goto out;
                }
 
@@ -711,8 +713,9 @@ static int pci_setup_device(struct pci_dev * dev)
 {
        u32 class;
 
-       sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
-               dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+       dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
+                    dev->bus->number, PCI_SLOT(dev->devfn),
+                    PCI_FUNC(dev->devfn));
 
        pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
        dev->revision = class & 0xff;
@@ -720,7 +723,7 @@ static int pci_setup_device(struct pci_dev * dev)
        dev->class = class;
        class >>= 8;
 
-       pr_debug("PCI: Found %s [%04x/%04x] %06x %02x\n", pci_name(dev),
+       dev_dbg(&dev->dev, "found [%04x/%04x] class %06x header type %02x\n",
                 dev->vendor, dev->device, class, dev->hdr_type);
 
        /* "Unknown power state" */
@@ -788,13 +791,13 @@ static int pci_setup_device(struct pci_dev * dev)
                break;
 
        default:                                    /* unknown header */
-               printk(KERN_ERR "PCI: device %s has unknown header type %02x, ignoring.\n",
-                       pci_name(dev), dev->hdr_type);
+               dev_err(&dev->dev, "unknown header type %02x, "
+                       "ignoring device\n", dev->hdr_type);
                return -1;
 
        bad:
-               printk(KERN_ERR "PCI: %s: class %x doesn't match header type %02x. Ignoring class.\n",
-                      pci_name(dev), class, dev->hdr_type);
+               dev_err(&dev->dev, "ignoring class %02x (doesn't match header "
+                       "type %02x)\n", class, dev->hdr_type);
                dev->class = PCI_CLASS_NOT_DEFINED;
        }
 
@@ -927,7 +930,7 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
                        return NULL;
                /* Card hasn't responded in 60 seconds?  Must be stuck. */
                if (delay > 60 * 1000) {
-                       printk(KERN_WARNING "Device %04x:%02x:%02x.%d not "
+                       printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not "
                                        "responding\n", pci_domain_nr(bus),
                                        bus->number, PCI_SLOT(devfn),
                                        PCI_FUNC(devfn));
@@ -984,6 +987,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
        /* Fix up broken headers */
        pci_fixup_device(pci_fixup_header, dev);
 
+       /* Initialize power management of the device */
+       pci_pm_init(dev);
+
        /*
         * Add the device to our list of discovered devices
         * and the bus list for fixup functions, etc.
index 963a97642ae9101a7454c7b0b3c0dbcc95f8240e..4400dffbd93a853310855cd441cf7f5293e8c091 100644 (file)
@@ -1,6 +1,4 @@
 /*
- *     $Id: proc.c,v 1.13 1998/05/12 07:36:07 mj Exp $
- *
  *     Procfs interface for the PCI bus.
  *
  *     Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
@@ -482,5 +480,5 @@ static int __init pci_proc_init(void)
        return 0;
 }
 
-__initcall(pci_proc_init);
+device_initcall(pci_proc_init);
 
index 338a3f94b4d43bdf3d44dd0548e018199a49774a..12d489395fad0496d7953a78a30d6237ae9018ac 100644 (file)
@@ -556,7 +556,7 @@ static void quirk_via_ioapic(struct pci_dev *dev)
        pci_write_config_byte (dev, 0x58, tmp);
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C686,       quirk_via_ioapic);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA,    PCI_DEVICE_ID_VIA_82C686,       quirk_via_ioapic);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA,      PCI_DEVICE_ID_VIA_82C686,       quirk_via_ioapic);
 
 /*
  * VIA 8237: Some BIOSs don't set the 'Bypass APIC De-Assert Message' Bit.
@@ -576,7 +576,7 @@ static void quirk_via_vt8237_bypass_apic_deassert(struct pci_dev *dev)
        }
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_8237,         quirk_via_vt8237_bypass_apic_deassert);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA,    PCI_DEVICE_ID_VIA_8237,         quirk_via_vt8237_bypass_apic_deassert);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA,      PCI_DEVICE_ID_VIA_8237,         quirk_via_vt8237_bypass_apic_deassert);
 
 /*
  * The AMD io apic can hang the box when an apic irq is masked.
@@ -622,7 +622,7 @@ static void quirk_amd_8131_ioapic(struct pci_dev *dev)
         }
 } 
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
 #endif /* CONFIG_X86_IO_APIC */
 
 /*
@@ -774,7 +774,7 @@ static void quirk_cardbus_legacy(struct pci_dev *dev)
        pci_write_config_dword(dev, PCI_CB_LEGACY_MODE_BASE, 0);
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
-DECLARE_PCI_FIXUP_RESUME(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
 
 /*
  * Following the PCI ordering rules is optional on the AMD762. I'm not
@@ -797,7 +797,7 @@ static void quirk_amd_ordering(struct pci_dev *dev)
        }
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD,     PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD,    PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD,      PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering);
 
 /*
  *     DreamWorks provided workaround for Dunord I-3000 problem
@@ -865,7 +865,7 @@ static void quirk_disable_pxb(struct pci_dev *pdev)
        }
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82454NX,    quirk_disable_pxb);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82454NX,    quirk_disable_pxb);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82454NX,    quirk_disable_pxb);
 
 static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev)
 {
@@ -885,9 +885,9 @@ static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev)
        }
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode);
 
 /*
  *     Serverworks CSB5 IDE does not fully support native mode
@@ -1054,6 +1054,20 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
                                 * its on-board VGA controller */
                                asus_hides_smbus = 1;
                        }
+               else if (dev->device == PCI_DEVICE_ID_INTEL_82845G_IG)
+                       switch(dev->subsystem_device) {
+                       case 0x00b8: /* Compaq Evo D510 CMT */
+                       case 0x00b9: /* Compaq Evo D510 SFF */
+                               asus_hides_smbus = 1;
+                       }
+               else if (dev->device == PCI_DEVICE_ID_INTEL_82815_CGC)
+                       switch (dev->subsystem_device) {
+                       case 0x001A: /* Compaq Deskpro EN SSF P667 815E */
+                               /* Motherboard doesn't have host bridge
+                                * subvendor/subdevice IDs, therefore checking
+                                * its on-board VGA controller */
+                               asus_hides_smbus = 1;
+                       }
        }
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82845_HB,   asus_hides_smbus_hostbridge);
@@ -1068,6 +1082,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,     PCI_DEVICE_ID_INTEL_82855GM_HB, as
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82915GM_HB, asus_hides_smbus_hostbridge);
 
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82810_IG3,  asus_hides_smbus_hostbridge);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82845G_IG,  asus_hides_smbus_hostbridge);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82815_CGC,  asus_hides_smbus_hostbridge);
 
 static void asus_hides_smbus_lpc(struct pci_dev *dev)
 {
@@ -1093,31 +1109,61 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82801CA_0,  asu
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82801EB_0,  asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82801AA_0,  asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82801DB_0,  asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82801BA_0,  asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82801CA_0,  asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_82801EB_0,  asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801AA_0,  asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801DB_0,  asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801BA_0,  asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801CA_0,  asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82801EB_0,  asus_hides_smbus_lpc);
 
-static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev)
+/* It appears we just have one such device. If not, we have a warning */
+static void __iomem *asus_rcba_base;
+static void asus_hides_smbus_lpc_ich6_suspend(struct pci_dev *dev)
 {
-       u32 val, rcba;
-       void __iomem *base;
+       u32 rcba;
 
        if (likely(!asus_hides_smbus))
                return;
+       WARN_ON(asus_rcba_base);
+
        pci_read_config_dword(dev, 0xF0, &rcba);
-       base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); /* use bits 31:14, 16 kB aligned */
-       if (base == NULL) return;
-       val=readl(base + 0x3418); /* read the Function Disable register, dword mode only */
-       writel(val & 0xFFFFFFF7, base + 0x3418); /* enable the SMBus device */
-       iounmap(base);
+       /* use bits 31:14, 16 kB aligned */
+       asus_rcba_base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000);
+       if (asus_rcba_base == NULL)
+               return;
+}
+
+static void asus_hides_smbus_lpc_ich6_resume_early(struct pci_dev *dev)
+{
+       u32 val;
+
+       if (likely(!asus_hides_smbus || !asus_rcba_base))
+               return;
+       /* read the Function Disable register, dword mode only */
+       val = readl(asus_rcba_base + 0x3418);
+       writel(val & 0xFFFFFFF7, asus_rcba_base + 0x3418); /* enable the SMBus device */
+}
+
+static void asus_hides_smbus_lpc_ich6_resume(struct pci_dev *dev)
+{
+       if (likely(!asus_hides_smbus || !asus_rcba_base))
+               return;
+       iounmap(asus_rcba_base);
+       asus_rcba_base = NULL;
        dev_info(&dev->dev, "Enabled ICH6/i801 SMBus device\n");
 }
+
+static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev)
+{
+       asus_hides_smbus_lpc_ich6_suspend(dev);
+       asus_hides_smbus_lpc_ich6_resume_early(dev);
+       asus_hides_smbus_lpc_ich6_resume(dev);
+}
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_ICH6_1,     asus_hides_smbus_lpc_ich6);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_ICH6_1,     asus_hides_smbus_lpc_ich6);
+DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1,     asus_hides_smbus_lpc_ich6_suspend);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_ICH6_1,     asus_hides_smbus_lpc_ich6_resume);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_ICH6_1,     asus_hides_smbus_lpc_ich6_resume_early);
 
 /*
  * SiS 96x south bridge: BIOS typically hides SMBus device...
@@ -1135,10 +1181,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,      PCI_DEVICE_ID_SI_961,           quirk_sis_96x_
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,     PCI_DEVICE_ID_SI_962,           quirk_sis_96x_smbus);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,     PCI_DEVICE_ID_SI_963,           quirk_sis_96x_smbus);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,     PCI_DEVICE_ID_SI_LPC,           quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI,     PCI_DEVICE_ID_SI_961,           quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI,     PCI_DEVICE_ID_SI_962,           quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI,     PCI_DEVICE_ID_SI_963,           quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI,     PCI_DEVICE_ID_SI_LPC,           quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI,       PCI_DEVICE_ID_SI_961,           quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI,       PCI_DEVICE_ID_SI_962,           quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI,       PCI_DEVICE_ID_SI_963,           quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI,       PCI_DEVICE_ID_SI_LPC,           quirk_sis_96x_smbus);
 
 /*
  * ... This is further complicated by the fact that some SiS96x south
@@ -1172,7 +1218,7 @@ static void quirk_sis_503(struct pci_dev *dev)
        quirk_sis_96x_smbus(dev);
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI,     PCI_DEVICE_ID_SI_503,           quirk_sis_503);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI,     PCI_DEVICE_ID_SI_503,           quirk_sis_503);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI,       PCI_DEVICE_ID_SI_503,           quirk_sis_503);
 
 
 /*
@@ -1205,7 +1251,7 @@ static void asus_hides_ac97_lpc(struct pci_dev *dev)
        }
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA,    PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA,    PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA,      PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc);
 
 #if defined(CONFIG_ATA) || defined(CONFIG_ATA_MODULE)
 
@@ -1270,12 +1316,12 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, qui
 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata);
 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata);
 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata);
 
 #endif
 
@@ -1521,6 +1567,10 @@ extern struct pci_fixup __start_pci_fixups_enable[];
 extern struct pci_fixup __end_pci_fixups_enable[];
 extern struct pci_fixup __start_pci_fixups_resume[];
 extern struct pci_fixup __end_pci_fixups_resume[];
+extern struct pci_fixup __start_pci_fixups_resume_early[];
+extern struct pci_fixup __end_pci_fixups_resume_early[];
+extern struct pci_fixup __start_pci_fixups_suspend[];
+extern struct pci_fixup __end_pci_fixups_suspend[];
 
 
 void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
@@ -1553,6 +1603,16 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
                end = __end_pci_fixups_resume;
                break;
 
+       case pci_fixup_resume_early:
+               start = __start_pci_fixups_resume_early;
+               end = __end_pci_fixups_resume_early;
+               break;
+
+       case pci_fixup_suspend:
+               start = __start_pci_fixups_suspend;
+               end = __end_pci_fixups_suspend;
+               break;
+
        default:
                /* stupid compiler warning, you would think with an enum... */
                return;
@@ -1629,7 +1689,7 @@ static void quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev)
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA,  PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
                        quirk_nvidia_ck804_pcie_aer_ext_cap);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_NVIDIA,  PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA,  PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
                        quirk_nvidia_ck804_pcie_aer_ext_cap);
 
 static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
index 8ddb918f5f57dfc0bdda3e4a287abe4c08bae365..827c0a520e2b3aec87bcc2a160720fbd48e836b2 100644 (file)
 #include <linux/slab.h>
 
 
-#define DEBUG_CONFIG 1
-#if DEBUG_CONFIG
-#define DBG(x...)     printk(x)
-#else
-#define DBG(x...)
-#endif
-
 static void pbus_assign_resources_sorted(struct pci_bus *bus)
 {
        struct pci_dev *dev;
@@ -81,8 +74,8 @@ void pci_setup_cardbus(struct pci_bus *bus)
        struct pci_dev *bridge = bus->self;
        struct pci_bus_region region;
 
-       printk("PCI: Bus %d, cardbus bridge: %s\n",
-               bus->number, pci_name(bridge));
+       dev_info(&bridge->dev, "CardBus bridge, secondary bus %04x:%02x\n",
+                pci_domain_nr(bus), bus->number);
 
        pcibios_resource_to_bus(bridge, &region, bus->resource[0]);
        if (bus->resource[0]->flags & IORESOURCE_IO) {
@@ -90,7 +83,7 @@ void pci_setup_cardbus(struct pci_bus *bus)
                 * The IO resource is allocated a range twice as large as it
                 * would normally need.  This allows us to set both IO regs.
                 */
-               printk(KERN_INFO "  IO window: 0x%08lx-0x%08lx\n",
+               dev_info(&bridge->dev, "  IO window: %#08lx-%#08lx\n",
                       (unsigned long)region.start,
                       (unsigned long)region.end);
                pci_write_config_dword(bridge, PCI_CB_IO_BASE_0,
@@ -101,7 +94,7 @@ void pci_setup_cardbus(struct pci_bus *bus)
 
        pcibios_resource_to_bus(bridge, &region, bus->resource[1]);
        if (bus->resource[1]->flags & IORESOURCE_IO) {
-               printk(KERN_INFO "  IO window: 0x%08lx-0x%08lx\n",
+               dev_info(&bridge->dev, "  IO window: %#08lx-%#08lx\n",
                       (unsigned long)region.start,
                       (unsigned long)region.end);
                pci_write_config_dword(bridge, PCI_CB_IO_BASE_1,
@@ -112,7 +105,7 @@ void pci_setup_cardbus(struct pci_bus *bus)
 
        pcibios_resource_to_bus(bridge, &region, bus->resource[2]);
        if (bus->resource[2]->flags & IORESOURCE_MEM) {
-               printk(KERN_INFO "  PREFETCH window: 0x%08lx-0x%08lx\n",
+               dev_info(&bridge->dev, "  PREFETCH window: %#08lx-%#08lx\n",
                       (unsigned long)region.start,
                       (unsigned long)region.end);
                pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0,
@@ -123,7 +116,7 @@ void pci_setup_cardbus(struct pci_bus *bus)
 
        pcibios_resource_to_bus(bridge, &region, bus->resource[3]);
        if (bus->resource[3]->flags & IORESOURCE_MEM) {
-               printk(KERN_INFO "  MEM window: 0x%08lx-0x%08lx\n",
+               dev_info(&bridge->dev, "  MEM window: %#08lx-%#08lx\n",
                       (unsigned long)region.start,
                       (unsigned long)region.end);
                pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1,
@@ -151,7 +144,8 @@ static void pci_setup_bridge(struct pci_bus *bus)
        struct pci_bus_region region;
        u32 l, bu, lu, io_upper16;
 
-       DBG(KERN_INFO "PCI: Bridge: %s\n", pci_name(bridge));
+       dev_info(&bridge->dev, "PCI bridge, secondary bus %04x:%02x\n",
+                pci_domain_nr(bus), bus->number);
 
        /* Set up the top and bottom of the PCI I/O segment for this bus. */
        pcibios_resource_to_bus(bridge, &region, bus->resource[0]);
@@ -162,7 +156,7 @@ static void pci_setup_bridge(struct pci_bus *bus)
                l |= region.end & 0xf000;
                /* Set up upper 16 bits of I/O base/limit. */
                io_upper16 = (region.end & 0xffff0000) | (region.start >> 16);
-               DBG(KERN_INFO "  IO window: %04lx-%04lx\n",
+               dev_info(&bridge->dev, "  IO window: %#04lx-%#04lx\n",
                    (unsigned long)region.start,
                    (unsigned long)region.end);
        }
@@ -170,7 +164,7 @@ static void pci_setup_bridge(struct pci_bus *bus)
                /* Clear upper 16 bits of I/O base/limit. */
                io_upper16 = 0;
                l = 0x00f0;
-               DBG(KERN_INFO "  IO window: disabled.\n");
+               dev_info(&bridge->dev, "  IO window: disabled\n");
        }
        /* Temporarily disable the I/O range before updating PCI_IO_BASE. */
        pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff);
@@ -185,13 +179,13 @@ static void pci_setup_bridge(struct pci_bus *bus)
        if (bus->resource[1]->flags & IORESOURCE_MEM) {
                l = (region.start >> 16) & 0xfff0;
                l |= region.end & 0xfff00000;
-               DBG(KERN_INFO "  MEM window: 0x%08lx-0x%08lx\n",
+               dev_info(&bridge->dev, "  MEM window: %#08lx-%#08lx\n",
                    (unsigned long)region.start,
                    (unsigned long)region.end);
        }
        else {
                l = 0x0000fff0;
-               DBG(KERN_INFO "  MEM window: disabled.\n");
+               dev_info(&bridge->dev, "  MEM window: disabled\n");
        }
        pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
 
@@ -208,13 +202,13 @@ static void pci_setup_bridge(struct pci_bus *bus)
                l |= region.end & 0xfff00000;
                bu = upper_32_bits(region.start);
                lu = upper_32_bits(region.end);
-               DBG(KERN_INFO "  PREFETCH window: 0x%016llx-0x%016llx\n",
+               dev_info(&bridge->dev, "  PREFETCH window: %#016llx-%#016llx\n",
                    (unsigned long long)region.start,
                    (unsigned long long)region.end);
        }
        else {
                l = 0x0000fff0;
-               DBG(KERN_INFO "  PREFETCH window: disabled.\n");
+               dev_info(&bridge->dev, "  PREFETCH window: disabled\n");
        }
        pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
 
@@ -361,9 +355,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
                        align = (i < PCI_BRIDGE_RESOURCES) ? r_size : r->start;
                        order = __ffs(align) - 20;
                        if (order > 11) {
-                               printk(KERN_WARNING "PCI: region %s/%d "
-                                      "too large: 0x%016llx-0x%016llx\n",
-                                       pci_name(dev), i,
+                               dev_warn(&dev->dev, "BAR %d too large: "
+                                      "%#016llx-%#016llx\n", i,
                                       (unsigned long long)r->start,
                                       (unsigned long long)r->end);
                                r->flags = 0;
@@ -529,8 +522,8 @@ void __ref pci_bus_assign_resources(struct pci_bus *bus)
                        break;
 
                default:
-                       printk(KERN_INFO "PCI: not setting up bridge %s "
-                              "for bus %d\n", pci_name(dev), b->number);
+                       dev_info(&dev->dev, "not setting up bridge for bus "
+                                "%04x:%02x\n", pci_domain_nr(b), b->number);
                        break;
                }
        }
index 05ca2ed9eb51fdc24340a3244a087e4680ae86ce..aa795fd428ded07fd01d25755fe6680e6091041c 100644 (file)
@@ -47,8 +47,7 @@ pdev_fixup_irq(struct pci_dev *dev,
        }
        dev->irq = irq;
 
-       pr_debug("PCI: fixup irq: (%s) got %d\n",
-               kobject_name(&dev->dev.kobj), dev->irq);
+       dev_dbg(&dev->dev, "fixup irq: got %d\n", dev->irq);
 
        /* Always tell the device, so the driver knows what is
           the real IRQ to use; the device does not use it. */
index 7d35cdf4579f2f92ca8c5293053dad43511042ea..1a5fc83c71b36fec69fe45a30006a27db9bc1300 100644 (file)
@@ -26,8 +26,7 @@
 #include "pci.h"
 
 
-void
-pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
+void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
 {
        struct pci_bus_region region;
        u32 new, check, mask;
@@ -43,20 +42,20 @@ pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
        /*
         * Ignore non-moveable resources.  This might be legacy resources for
         * which no functional BAR register exists or another important
-        * system resource we should better not move around in system address
-        * space.
+        * system resource we shouldn't move around.
         */
        if (res->flags & IORESOURCE_PCI_FIXED)
                return;
 
        pcibios_resource_to_bus(dev, &region, res);
 
-       pr_debug("  got res [%llx:%llx] bus [%llx:%llx] flags %lx for "
-                "BAR %d of %s\n", (unsigned long long)res->start,
+       dev_dbg(&dev->dev, "BAR %d: got res [%#llx-%#llx] bus [%#llx-%#llx] "
+               "flags %#lx\n", resno,
+                (unsigned long long)res->start,
                 (unsigned long long)res->end,
                 (unsigned long long)region.start,
                 (unsigned long long)region.end,
-                (unsigned long)res->flags, resno, pci_name(dev));
+                (unsigned long)res->flags);
 
        new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
        if (res->flags & IORESOURCE_IO)
@@ -81,9 +80,8 @@ pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
        pci_read_config_dword(dev, reg, &check);
 
        if ((new ^ check) & mask) {
-               printk(KERN_ERR "PCI: Error while updating region "
-                      "%s/%d (%08x != %08x)\n", pci_name(dev), resno,
-                      new, check);
+               dev_err(&dev->dev, "BAR %d: error updating (%#08x != %#08x)\n",
+                       resno, new, check);
        }
 
        if ((new & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
@@ -92,15 +90,14 @@ pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
                pci_write_config_dword(dev, reg + 4, new);
                pci_read_config_dword(dev, reg + 4, &check);
                if (check != new) {
-                       printk(KERN_ERR "PCI: Error updating region "
-                              "%s/%d (high %08x != %08x)\n",
-                              pci_name(dev), resno, new, check);
+                       dev_err(&dev->dev, "BAR %d: error updating "
+                              "(high %#08x != %#08x)\n", resno, new, check);
                }
        }
        res->flags &= ~IORESOURCE_UNSET;
-       pr_debug("PCI: moved device %s resource %d (%lx) to %x\n",
-               pci_name(dev), resno, res->flags,
-               new & ~PCI_REGION_FLAG_MASK);
+       dev_dbg(&dev->dev, "BAR %d: moved to bus [%#llx-%#llx] flags %#lx\n",
+               resno, (unsigned long long)region.start,
+               (unsigned long long)region.end, res->flags);
 }
 
 int pci_claim_resource(struct pci_dev *dev, int resource)
@@ -117,10 +114,11 @@ int pci_claim_resource(struct pci_dev *dev, int resource)
                err = insert_resource(root, res);
 
        if (err) {
-               printk(KERN_ERR "PCI: %s region %d of %s %s [%llx:%llx]\n",
-                       root ? "Address space collision on" :
-                               "No parent found for",
-                       resource, dtype, pci_name(dev),
+               dev_err(&dev->dev, "BAR %d: %s of %s [%#llx-%#llx]\n",
+                       resource,
+                       root ? "address space collision on" :
+                               "no parent found for",
+                       dtype,
                        (unsigned long long)res->start,
                        (unsigned long long)res->end);
        }
@@ -140,11 +138,10 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
 
        align = resource_alignment(res);
        if (!align) {
-               printk(KERN_ERR "PCI: Cannot allocate resource (bogus "
-                       "alignment) %d [%llx:%llx] (flags %lx) of %s\n",
+               dev_err(&dev->dev, "BAR %d: can't allocate resource (bogus "
+                       "alignment) [%#llx-%#llx] flags %#lx\n",
                        resno, (unsigned long long)res->start,
-                       (unsigned long long)res->end, res->flags,
-                       pci_name(dev));
+                       (unsigned long long)res->end, res->flags);
                return -EINVAL;
        }
 
@@ -165,11 +162,11 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
        }
 
        if (ret) {
-               printk(KERN_ERR "PCI: Failed to allocate %s resource "
-                       "#%d:%llx@%llx for %s\n",
+               dev_err(&dev->dev, "BAR %d: can't allocate %s resource "
+                       "[%#llx-%#llx]\n", resno,
                        res->flags & IORESOURCE_IO ? "I/O" : "mem",
-                       resno, (unsigned long long)size,
-                       (unsigned long long)res->start, pci_name(dev));
+                       (unsigned long long)res->start,
+                       (unsigned long long)res->end);
        } else {
                res->flags &= ~IORESOURCE_STARTALIGN;
                if (resno < PCI_BRIDGE_RESOURCES)
@@ -205,11 +202,11 @@ int pci_assign_resource_fixed(struct pci_dev *dev, int resno)
        }
 
        if (ret) {
-               printk(KERN_ERR "PCI: Failed to allocate %s resource "
-                               "#%d:%llx@%llx for %s\n",
+               dev_err(&dev->dev, "BAR %d: can't allocate %s resource "
+                       "[%#llx-%#llx\n]", resno,
                        res->flags & IORESOURCE_IO ? "I/O" : "mem",
-                       resno, (unsigned long long)(res->end - res->start + 1),
-                       (unsigned long long)res->start, pci_name(dev));
+                       (unsigned long long)res->start,
+                       (unsigned long long)res->end);
        } else if (resno < PCI_BRIDGE_RESOURCES) {
                pci_update_resource(dev, res, resno);
        }
@@ -239,11 +236,10 @@ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
 
                r_align = resource_alignment(r);
                if (!r_align) {
-                       printk(KERN_WARNING "PCI: bogus alignment of resource "
-                               "%d [%llx:%llx] (flags %lx) of %s\n",
+                       dev_warn(&dev->dev, "BAR %d: bogus alignment "
+                               "[%#llx-%#llx] flags %#lx\n",
                                i, (unsigned long long)r->start,
-                               (unsigned long long)r->end, r->flags,
-                               pci_name(dev));
+                               (unsigned long long)r->end, r->flags);
                        continue;
                }
                for (list = head; ; list = list->next) {
@@ -291,7 +287,7 @@ int pci_enable_resources(struct pci_dev *dev, int mask)
 
                if (!r->parent) {
                        dev_err(&dev->dev, "device not available because of "
-                               "BAR %d [%llx:%llx] collisions\n", i,
+                               "BAR %d [%#llx-%#llx] collisions\n", i,
                                (unsigned long long) r->start,
                                (unsigned long long) r->end);
                        return -EINVAL;
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
new file mode 100644 (file)
index 0000000..7e5b85c
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * drivers/pci/slot.c
+ * Copyright (C) 2006 Matthew Wilcox <matthew@wil.cx>
+ * Copyright (C) 2006-2008 Hewlett-Packard Development Company, L.P.
+ *     Alex Chiang <achiang@hp.com>
+ */
+
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include <linux/err.h>
+#include "pci.h"
+
+struct kset *pci_slots_kset;
+EXPORT_SYMBOL_GPL(pci_slots_kset);
+
+static ssize_t pci_slot_attr_show(struct kobject *kobj,
+                                       struct attribute *attr, char *buf)
+{
+       struct pci_slot *slot = to_pci_slot(kobj);
+       struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+       return attribute->show ? attribute->show(slot, buf) : -EIO;
+}
+
+static ssize_t pci_slot_attr_store(struct kobject *kobj,
+                       struct attribute *attr, const char *buf, size_t len)
+{
+       struct pci_slot *slot = to_pci_slot(kobj);
+       struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+       return attribute->store ? attribute->store(slot, buf, len) : -EIO;
+}
+
+static struct sysfs_ops pci_slot_sysfs_ops = {
+       .show = pci_slot_attr_show,
+       .store = pci_slot_attr_store,
+};
+
+static ssize_t address_read_file(struct pci_slot *slot, char *buf)
+{
+       if (slot->number == 0xff)
+               return sprintf(buf, "%04x:%02x\n",
+                               pci_domain_nr(slot->bus),
+                               slot->bus->number);
+       else
+               return sprintf(buf, "%04x:%02x:%02x\n",
+                               pci_domain_nr(slot->bus),
+                               slot->bus->number,
+                               slot->number);
+}
+
+static void pci_slot_release(struct kobject *kobj)
+{
+       struct pci_slot *slot = to_pci_slot(kobj);
+
+       pr_debug("%s: releasing pci_slot on %x:%d\n", __func__,
+                slot->bus->number, slot->number);
+
+       list_del(&slot->list);
+
+       kfree(slot);
+}
+
+static struct pci_slot_attribute pci_slot_attr_address =
+       __ATTR(address, (S_IFREG | S_IRUGO), address_read_file, NULL);
+
+static struct attribute *pci_slot_default_attrs[] = {
+       &pci_slot_attr_address.attr,
+       NULL,
+};
+
+static struct kobj_type pci_slot_ktype = {
+       .sysfs_ops = &pci_slot_sysfs_ops,
+       .release = &pci_slot_release,
+       .default_attrs = pci_slot_default_attrs,
+};
+
+/**
+ * pci_create_slot - create or increment refcount for physical PCI slot
+ * @parent: struct pci_bus of parent bridge
+ * @slot_nr: PCI_SLOT(pci_dev->devfn) or -1 for placeholder
+ * @name: user visible string presented in /sys/bus/pci/slots/<name>
+ *
+ * PCI slots have first class attributes such as address, speed, width,
+ * and a &struct pci_slot is used to manage them. This interface will
+ * either return a new &struct pci_slot to the caller, or if the pci_slot
+ * already exists, its refcount will be incremented.
+ *
+ * Slots are uniquely identified by a @pci_bus, @slot_nr, @name tuple.
+ *
+ * Placeholder slots:
+ * In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify
+ * a slot. There is one notable exception - pSeries (rpaphp), where the
+ * @slot_nr cannot be determined until a device is actually inserted into
+ * the slot. In this scenario, the caller may pass -1 for @slot_nr.
+ *
+ * The following semantics are imposed when the caller passes @slot_nr ==
+ * -1. First, the check for existing %struct pci_slot is skipped, as the
+ * caller may know about several unpopulated slots on a given %struct
+ * pci_bus, and each slot would have a @slot_nr of -1.  Uniqueness for
+ * these slots is then determined by the @name parameter. We expect
+ * kobject_init_and_add() to warn us if the caller attempts to create
+ * multiple slots with the same name. The other change in semantics is
+ * user-visible, which is the 'address' parameter presented in sysfs will
+ * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the
+ * %struct pci_bus and bb is the bus number. In other words, the devfn of
+ * the 'placeholder' slot will not be displayed.
+ */
+
+struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
+                                const char *name)
+{
+       struct pci_slot *slot;
+       int err;
+
+       down_write(&pci_bus_sem);
+
+       if (slot_nr == -1)
+               goto placeholder;
+
+       /* If we've already created this slot, bump refcount and return. */
+       list_for_each_entry(slot, &parent->slots, list) {
+               if (slot->number == slot_nr) {
+                       kobject_get(&slot->kobj);
+                       pr_debug("%s: inc refcount to %d on %04x:%02x:%02x\n",
+                                __func__,
+                                atomic_read(&slot->kobj.kref.refcount),
+                                pci_domain_nr(parent), parent->number,
+                                slot_nr);
+                       goto out;
+               }
+       }
+
+placeholder:
+       slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+       if (!slot) {
+               slot = ERR_PTR(-ENOMEM);
+               goto out;
+       }
+
+       slot->bus = parent;
+       slot->number = slot_nr;
+
+       slot->kobj.kset = pci_slots_kset;
+       err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL,
+                                  "%s", name);
+       if (err) {
+               printk(KERN_ERR "Unable to register kobject %s\n", name);
+               goto err;
+       }
+
+       INIT_LIST_HEAD(&slot->list);
+       list_add(&slot->list, &parent->slots);
+
+       /* Don't care if debug printk has a -1 for slot_nr */
+       pr_debug("%s: created pci_slot on %04x:%02x:%02x\n",
+                __func__, pci_domain_nr(parent), parent->number, slot_nr);
+
+ out:
+       up_write(&pci_bus_sem);
+       return slot;
+ err:
+       kfree(slot);
+       slot = ERR_PTR(err);
+       goto out;
+}
+EXPORT_SYMBOL_GPL(pci_create_slot);
+
+/**
+ * pci_update_slot_number - update %struct pci_slot -> number
+ * @slot - %struct pci_slot to update
+ * @slot_nr - new number for slot
+ *
+ * The primary purpose of this interface is to allow callers who earlier
+ * created a placeholder slot in pci_create_slot() by passing a -1 as
+ * slot_nr, to update their %struct pci_slot with the correct @slot_nr.
+ */
+
+void pci_update_slot_number(struct pci_slot *slot, int slot_nr)
+{
+       int name_count = 0;
+       struct pci_slot *tmp;
+
+       down_write(&pci_bus_sem);
+
+       list_for_each_entry(tmp, &slot->bus->slots, list) {
+               WARN_ON(tmp->number == slot_nr);
+               if (!strcmp(kobject_name(&tmp->kobj), kobject_name(&slot->kobj)))
+                       name_count++;
+       }
+
+       if (name_count > 1)
+               printk(KERN_WARNING "pci_update_slot_number found %d slots with the same name: %s\n", name_count, kobject_name(&slot->kobj));
+
+       slot->number = slot_nr;
+       up_write(&pci_bus_sem);
+}
+EXPORT_SYMBOL_GPL(pci_update_slot_number);
+
+/**
+ * pci_destroy_slot - decrement refcount for physical PCI slot
+ * @slot: struct pci_slot to decrement
+ *
+ * %struct pci_slot is refcounted, so destroying them is really easy; we
+ * just call kobject_put on its kobj and let our release methods do the
+ * rest.
+ */
+
+void pci_destroy_slot(struct pci_slot *slot)
+{
+       pr_debug("%s: dec refcount to %d on %04x:%02x:%02x\n", __func__,
+                atomic_read(&slot->kobj.kref.refcount) - 1,
+                pci_domain_nr(slot->bus), slot->bus->number, slot->number);
+
+       down_write(&pci_bus_sem);
+       kobject_put(&slot->kobj);
+       up_write(&pci_bus_sem);
+}
+EXPORT_SYMBOL_GPL(pci_destroy_slot);
+
+static int pci_slot_init(void)
+{
+       struct kset *pci_bus_kset;
+
+       pci_bus_kset = bus_get_kset(&pci_bus_type);
+       pci_slots_kset = kset_create_and_add("slots", NULL,
+                                               &pci_bus_kset->kobj);
+       if (!pci_slots_kset) {
+               printk(KERN_ERR "PCI: Slot initialization failure\n");
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+subsys_initcall(pci_slot_init);
index db90a74f8714052fadfc424aff91c862b328a80d..a5ac0bc7f52eb1cc1849fb29afda135fb9dda6c1 100644 (file)
@@ -259,6 +259,7 @@ struct acpi_device_perf {
 /* Wakeup Management */
 struct acpi_device_wakeup_flags {
        u8 valid:1;             /* Can successfully enable wakeup? */
+       u8 prepared:1;          /* Has the wake-up capability been enabled? */
        u8 run_wake:1;          /* Run-Wake GPE devices */
 };
 
@@ -335,6 +336,8 @@ void acpi_bus_data_handler(acpi_handle handle, u32 function, void *context);
 int acpi_bus_get_status(struct acpi_device *device);
 int acpi_bus_get_power(acpi_handle handle, int *state);
 int acpi_bus_set_power(acpi_handle handle, int state);
+bool acpi_bus_power_manageable(acpi_handle handle);
+bool acpi_bus_can_wakeup(acpi_handle handle);
 #ifdef CONFIG_ACPI_PROC_EVENT
 int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data);
 int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data);
@@ -377,6 +380,7 @@ acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int);
 
 #ifdef CONFIG_PM_SLEEP
 int acpi_pm_device_sleep_state(struct device *, int *);
+int acpi_pm_device_sleep_wake(struct device *, bool);
 #else /* !CONFIG_PM_SLEEP */
 static inline int acpi_pm_device_sleep_state(struct device *d, int *p)
 {
@@ -384,6 +388,10 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p)
                *p = ACPI_STATE_D0;
        return ACPI_STATE_D3;
 }
+static inline int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
+{
+       return -ENODEV;
+}
 #endif /* !CONFIG_PM_SLEEP */
 
 #endif                         /* CONFIG_ACPI */
index 9757a040a5057e82678490bd35713488a6eab893..e5f38e5ce86fc5a4e28ec624e529ee1db59c16c0 100644 (file)
@@ -87,7 +87,9 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_device *device, int domain,
    -------------------------------------------------------------------------- */
 
 #ifdef CONFIG_ACPI_POWER
-int acpi_enable_wakeup_device_power(struct acpi_device *dev);
+int acpi_device_sleep_wake(struct acpi_device *dev,
+                           int enable, int sleep_state, int dev_state);
+int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state);
 int acpi_disable_wakeup_device_power(struct acpi_device *dev);
 int acpi_power_get_inferred_state(struct acpi_device *device);
 int acpi_power_transition(struct acpi_device *device, int state);
index b20409404c7dac724761f2e4df39d228f25b9026..729f6b0a60e9d1655cfa5005236c5bf11fb83d5c 100644 (file)
                VMLINUX_SYMBOL(__start_pci_fixups_resume) = .;          \
                *(.pci_fixup_resume)                                    \
                VMLINUX_SYMBOL(__end_pci_fixups_resume) = .;            \
+               VMLINUX_SYMBOL(__start_pci_fixups_resume_early) = .;    \
+               *(.pci_fixup_resume_early)                              \
+               VMLINUX_SYMBOL(__end_pci_fixups_resume_early) = .;      \
+               VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .;         \
+               *(.pci_fixup_suspend)                                   \
+               VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .;           \
        }                                                               \
                                                                        \
        /* Built-in firmware blobs */                                   \
index 5b21485be573e12eb845a845fdffc463506b9c98..80c775d9fe205810257fb8dc0013b997b5d61969 100644 (file)
@@ -11,7 +11,11 @@ extern u8 read_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset);
 extern u16 read_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset);
 extern void write_pci_config(u8 bus, u8 slot, u8 func, u8 offset, u32 val);
 extern void write_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset, u8 val);
+extern void write_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset, u16 val);
 
 extern int early_pci_allowed(void);
 
+extern unsigned int pci_early_dump_regs;
+extern void early_dump_pci_device(u8 bus, u8 slot, u8 func);
+extern void early_dump_pci_devices(void);
 #endif
index 0601075d09a13565d0b1ea94e9d0bba1947e95df..a171776393767ab0e23411c4ff2d8a89f15676e2 100644 (file)
@@ -235,6 +235,9 @@ int acpi_check_region(resource_size_t start, resource_size_t n,
 int acpi_check_mem_region(resource_size_t start, resource_size_t n,
                      const char *name);
 
+#ifdef CONFIG_PM_SLEEP
+void __init acpi_old_suspend_ordering(void);
+#endif /* CONFIG_PM_SLEEP */
 #else  /* CONFIG_ACPI */
 
 static inline int early_acpi_boot_init(void)
index 6a2d04c011bc1850e4f8191cc3749699952e2b7c..f71a78d123aef9d7b3b03276921d10286e1eceed 100644 (file)
@@ -68,6 +68,8 @@ struct bus_type {
        int (*resume_early)(struct device *dev);
        int (*resume)(struct device *dev);
 
+       struct pm_ext_ops *pm;
+
        struct bus_type_private *p;
 };
 
@@ -131,6 +133,8 @@ struct device_driver {
        int (*resume) (struct device *dev);
        struct attribute_group **groups;
 
+       struct pm_ops *pm;
+
        struct driver_private *p;
 };
 
@@ -197,6 +201,8 @@ struct class {
 
        int (*suspend)(struct device *dev, pm_message_t state);
        int (*resume)(struct device *dev);
+
+       struct pm_ops *pm;
 };
 
 extern int __must_check class_register(struct class *class);
@@ -248,8 +254,11 @@ struct device_type {
        struct attribute_group **groups;
        int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
        void (*release)(struct device *dev);
+
        int (*suspend)(struct device *dev, pm_message_t state);
        int (*resume)(struct device *dev);
+
+       struct pm_ops *pm;
 };
 
 /* interface for exporting device attributes */
index d18b1dd49faba947acd56a77e1ccbb55df562b0f..a6a088e1a8049bf7b15fe5dac91b87f9a938133a 100644 (file)
@@ -17,8 +17,7 @@
 #ifndef LINUX_PCI_H
 #define LINUX_PCI_H
 
-/* Include the pci register defines */
-#include <linux/pci_regs.h>
+#include <linux/pci_regs.h>    /* The pci register defines */
 
 /*
  * The PCI interface treats multi-function devices as independent
 #include <linux/list.h>
 #include <linux/compiler.h>
 #include <linux/errno.h>
+#include <linux/kobject.h>
 #include <asm/atomic.h>
 #include <linux/device.h>
 
 /* Include the ID list */
 #include <linux/pci_ids.h>
 
+/* pci_slot represents a physical slot */
+struct pci_slot {
+       struct pci_bus *bus;            /* The bus this slot is on */
+       struct list_head list;          /* node in list of slots on this bus */
+       struct hotplug_slot *hotplug;   /* Hotplug info (migrate over time) */
+       unsigned char number;           /* PCI_SLOT(pci_dev->devfn) */
+       struct kobject kobj;
+};
+
 /* File state for mmap()s on /proc/bus/pci/X/Y */
 enum pci_mmap_state {
        pci_mmap_io,
@@ -142,6 +151,7 @@ struct pci_dev {
 
        void            *sysdata;       /* hook for sys-specific extension */
        struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */
+       struct pci_slot *slot;          /* Physical slot this device is in */
 
        unsigned int    devfn;          /* encoded device & function index */
        unsigned short  vendor;
@@ -167,6 +177,13 @@ struct pci_dev {
        pci_power_t     current_state;  /* Current operating state. In ACPI-speak,
                                           this is D0-D3, D0 being fully functional,
                                           and D3 being off. */
+       int             pm_cap;         /* PM capability offset in the
+                                          configuration space */
+       unsigned int    pme_support:5;  /* Bitmask of states from which PME#
+                                          can be generated */
+       unsigned int    d1_support:1;   /* Low power state D1 is supported */
+       unsigned int    d2_support:1;   /* Low power state D2 is supported */
+       unsigned int    no_d1d2:1;      /* Only allow D0 and D3 */
 
 #ifdef CONFIG_PCIEASPM
        struct pcie_link_state  *link_state;    /* ASPM link state. */
@@ -191,7 +208,6 @@ struct pci_dev {
        unsigned int    is_added:1;
        unsigned int    is_busmaster:1; /* device is busmaster */
        unsigned int    no_msi:1;       /* device may not use msi */
-       unsigned int    no_d1d2:1;   /* only allow d0 or d3 */
        unsigned int    block_ucfg_access:1;    /* userspace config space access is blocked */
        unsigned int    broken_parity_status:1; /* Device generates false positive parity */
        unsigned int    msi_enabled:1;
@@ -267,6 +283,7 @@ struct pci_bus {
        struct list_head children;      /* list of child buses */
        struct list_head devices;       /* list of devices on this bus */
        struct pci_dev  *self;          /* bridge device as seen by parent */
+       struct list_head slots;         /* list of slots on this bus */
        struct resource *resource[PCI_BUS_NUM_RESOURCES];
                                        /* address space routed to this bus */
 
@@ -328,7 +345,7 @@ struct pci_bus_region {
 struct pci_dynids {
        spinlock_t lock;            /* protects list, index */
        struct list_head list;      /* for IDs added at runtime */
-       unsigned int use_driver_data:1; /* pci_driver->driver_data is used */
+       unsigned int use_driver_data:1; /* pci_device_id->driver_data is used */
 };
 
 /* ---------------------------------------------------------------- */
@@ -390,7 +407,7 @@ struct pci_driver {
        int  (*resume_early) (struct pci_dev *dev);
        int  (*resume) (struct pci_dev *dev);                   /* Device woken up */
        void (*shutdown) (struct pci_dev *dev);
-
+       struct pm_ext_ops *pm;
        struct pci_error_handlers *err_handler;
        struct device_driver    driver;
        struct pci_dynids dynids;
@@ -489,6 +506,10 @@ struct pci_bus *pci_create_bus(struct device *parent, int bus,
                               struct pci_ops *ops, void *sysdata);
 struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev,
                                int busnr);
+struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
+                                const char *name);
+void pci_destroy_slot(struct pci_slot *slot);
+void pci_update_slot_number(struct pci_slot *slot, int slot_nr);
 int pci_scan_slot(struct pci_bus *bus, int devfn);
 struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn);
 void pci_device_add(struct pci_dev *dev, struct pci_bus *bus);
@@ -618,6 +639,8 @@ int pci_restore_state(struct pci_dev *dev);
 int pci_set_power_state(struct pci_dev *dev, pci_power_t state);
 pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
 int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable);
+int pci_prepare_to_sleep(struct pci_dev *dev);
+int pci_back_from_sleep(struct pci_dev *dev);
 
 /* Functions for PCI Hotplug drivers to use */
 int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);
@@ -839,6 +862,11 @@ static inline int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
        return -EIO;
 }
 
+static inline int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
+{
+       return -EIO;
+}
+
 static inline int pci_set_dma_max_seg_size(struct pci_dev *dev,
                                        unsigned int size)
 {
@@ -977,9 +1005,9 @@ static inline void pci_set_drvdata(struct pci_dev *pdev, void *data)
 /* If you want to know what to call your pci_dev, ask this function.
  * Again, it's a wrapper around the generic device.
  */
-static inline char *pci_name(struct pci_dev *pdev)
+static inline const char *pci_name(struct pci_dev *pdev)
 {
-       return pdev->dev.bus_id;
+       return dev_name(&pdev->dev);
 }
 
 
@@ -1014,7 +1042,9 @@ enum pci_fixup_pass {
        pci_fixup_header,       /* After reading configuration header */
        pci_fixup_final,        /* Final phase of device fixups */
        pci_fixup_enable,       /* pci_enable_device() time */
-       pci_fixup_resume,       /* pci_enable_device() time */
+       pci_fixup_resume,       /* pci_device_resume() */
+       pci_fixup_suspend,      /* pci_device_suspend */
+       pci_fixup_resume_early, /* pci_device_resume_early() */
 };
 
 /* Anonymous variables would be nice... */
@@ -1036,6 +1066,12 @@ enum pci_fixup_pass {
 #define DECLARE_PCI_FIXUP_RESUME(vendor, device, hook)                 \
        DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume,                    \
                        resume##vendor##device##hook, vendor, device, hook)
+#define DECLARE_PCI_FIXUP_RESUME_EARLY(vendor, device, hook)           \
+       DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume_early,              \
+                       resume_early##vendor##device##hook, vendor, device, hook)
+#define DECLARE_PCI_FIXUP_SUSPEND(vendor, device, hook)                        \
+       DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend,                   \
+                       suspend##vendor##device##hook, vendor, device, hook)
 
 
 void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
@@ -1060,7 +1096,10 @@ extern int pci_pci_problems;
 extern unsigned long pci_cardbus_io_size;
 extern unsigned long pci_cardbus_mem_size;
 
-extern int pcibios_add_platform_entries(struct pci_dev *dev);
+int pcibios_add_platform_entries(struct pci_dev *dev);
+void pcibios_disable_device(struct pci_dev *dev);
+int pcibios_set_pcie_reset_state(struct pci_dev *dev,
+                                enum pcie_reset_state state);
 
 #ifdef CONFIG_PCI_MMCONFIG
 extern void __init pci_mmcfg_early_init(void);
index 8f67e8f2a3cc0c0e24d32ebd77cde24bb94281a5..a08cd06b541a190975d04ee7d1cdd8af73e0a9f5 100644 (file)
@@ -95,9 +95,6 @@ struct hotplug_slot_attribute {
  * @get_adapter_status: Called to get see if an adapter is present in the slot or not.
  *     If this field is NULL, the value passed in the struct hotplug_slot_info
  *     will be used when this value is requested by a user.
- * @get_address: Called to get pci address of a slot.
- *     If this field is NULL, the value passed in the struct hotplug_slot_info
- *     will be used when this value is requested by a user.
  * @get_max_bus_speed: Called to get the max bus speed for a slot.
  *     If this field is NULL, the value passed in the struct hotplug_slot_info
  *     will be used when this value is requested by a user.
@@ -120,7 +117,6 @@ struct hotplug_slot_ops {
        int (*get_attention_status)     (struct hotplug_slot *slot, u8 *value);
        int (*get_latch_status)         (struct hotplug_slot *slot, u8 *value);
        int (*get_adapter_status)       (struct hotplug_slot *slot, u8 *value);
-       int (*get_address)              (struct hotplug_slot *slot, u32 *value);
        int (*get_max_bus_speed)        (struct hotplug_slot *slot, enum pci_bus_speed *value);
        int (*get_cur_bus_speed)        (struct hotplug_slot *slot, enum pci_bus_speed *value);
 };
@@ -140,7 +136,6 @@ struct hotplug_slot_info {
        u8      attention_status;
        u8      latch_status;
        u8      adapter_status;
-       u32     address;
        enum pci_bus_speed      max_bus_speed;
        enum pci_bus_speed      cur_bus_speed;
 };
@@ -166,15 +161,14 @@ struct hotplug_slot {
 
        /* Variables below this are for use only by the hotplug pci core. */
        struct list_head                slot_list;
-       struct kobject                  kobj;
+       struct pci_slot                 *pci_slot;
 };
 #define to_hotplug_slot(n) container_of(n, struct hotplug_slot, kobj)
 
-extern int pci_hp_register             (struct hotplug_slot *slot);
-extern int pci_hp_deregister           (struct hotplug_slot *slot);
+extern int pci_hp_register(struct hotplug_slot *, struct pci_bus *, int nr);
+extern int pci_hp_deregister(struct hotplug_slot *slot);
 extern int __must_check pci_hp_change_slot_info        (struct hotplug_slot *slot,
                                                 struct hotplug_slot_info *info);
-extern struct kset *pci_hotplug_slots_kset;
 
 /* PCI Setting Record (Type 0) */
 struct hpp_type0 {
@@ -227,9 +221,9 @@ struct hotplug_params {
 #include <acpi/acpi.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/actypes.h>
-extern acpi_status acpi_run_oshp(acpi_handle handle);
 extern acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
                                struct hotplug_params *hpp);
+int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags);
 int acpi_root_bridge(acpi_handle handle);
 #endif
 #endif
index c0c1223c91945111ca4ebbfe8d022de8461ab0ad..19958b929905beb2af8a0a6ca6802510379c232a 100644 (file)
 #define  PCI_PM_CAP_PME_D2     0x2000  /* PME# from D2 */
 #define  PCI_PM_CAP_PME_D3     0x4000  /* PME# from D3 (hot) */
 #define  PCI_PM_CAP_PME_D3cold 0x8000  /* PME# from D3 (cold) */
+#define  PCI_PM_CAP_PME_SHIFT  11      /* Start of the PME Mask in PMC */
 #define PCI_PM_CTRL            4       /* PM control and status register */
 #define  PCI_PM_CTRL_STATE_MASK        0x0003  /* Current power state (D0 to D3) */
 #define  PCI_PM_CTRL_NO_SOFT_RESET     0x0004  /* No reset for D3hot->D0 */
index 3261681c82a45bb4ec68551bd6bb40bbef1935e3..95ac21ab3a092369f5f41537614eb9890161d70e 100644 (file)
@@ -53,6 +53,7 @@ struct platform_driver {
        int (*suspend_late)(struct platform_device *, pm_message_t state);
        int (*resume_early)(struct platform_device *);
        int (*resume)(struct platform_device *);
+       struct pm_ext_ops *pm;
        struct device_driver driver;
 };
 
index 39a7ee859b671cfc57f5fcd9e063fc3031e96f84..4ad9de94449a67d598ea51bbb9a322fdfbce8b11 100644 (file)
@@ -112,7 +112,9 @@ typedef struct pm_message {
        int event;
 } pm_message_t;
 
-/*
+/**
+ * struct pm_ops - device PM callbacks
+ *
  * Several driver power state transitions are externally visible, affecting
  * the state of pending I/O queues and (for drivers that touch hardware)
  * interrupts, wakeups, DMA, and other hardware state.  There may also be
@@ -120,6 +122,284 @@ typedef struct pm_message {
  * to the rest of the driver stack (such as a driver that's ON gating off
  * clocks which are not in active use).
  *
+ * The externally visible transitions are handled with the help of the following
+ * callbacks included in this structure:
+ *
+ * @prepare: Prepare the device for the upcoming transition, but do NOT change
+ *     its hardware state.  Prevent new children of the device from being
+ *     registered after @prepare() returns (the driver's subsystem and
+ *     generally the rest of the kernel is supposed to prevent new calls to the
+ *     probe method from being made too once @prepare() has succeeded).  If
+ *     @prepare() detects a situation it cannot handle (e.g. registration of a
+ *     child already in progress), it may return -EAGAIN, so that the PM core
+ *     can execute it once again (e.g. after the new child has been registered)
+ *     to recover from the race condition.  This method is executed for all
+ *     kinds of suspend transitions and is followed by one of the suspend
+ *     callbacks: @suspend(), @freeze(), or @poweroff().
+ *     The PM core executes @prepare() for all devices before starting to
+ *     execute suspend callbacks for any of them, so drivers may assume all of
+ *     the other devices to be present and functional while @prepare() is being
+ *     executed.  In particular, it is safe to make GFP_KERNEL memory
+ *     allocations from within @prepare().  However, drivers may NOT assume
+ *     anything about the availability of the user space at that time and it
+ *     is not correct to request firmware from within @prepare() (it's too
+ *     late to do that).  [To work around this limitation, drivers may
+ *     register suspend and hibernation notifiers that are executed before the
+ *     freezing of tasks.]
+ *
+ * @complete: Undo the changes made by @prepare().  This method is executed for
+ *     all kinds of resume transitions, following one of the resume callbacks:
+ *     @resume(), @thaw(), @restore().  Also called if the state transition
+ *     fails before the driver's suspend callback (@suspend(), @freeze(),
+ *     @poweroff()) can be executed (e.g. if the suspend callback fails for one
+ *     of the other devices that the PM core has unsuccessfully attempted to
+ *     suspend earlier).
+ *     The PM core executes @complete() after it has executed the appropriate
+ *     resume callback for all devices.
+ *
+ * @suspend: Executed before putting the system into a sleep state in which the
+ *     contents of main memory are preserved.  Quiesce the device, put it into
+ *     a low power state appropriate for the upcoming system state (such as
+ *     PCI_D3hot), and enable wakeup events as appropriate.
+ *
+ * @resume: Executed after waking the system up from a sleep state in which the
+ *     contents of main memory were preserved.  Put the device into the
+ *     appropriate state, according to the information saved in memory by the
+ *     preceding @suspend().  The driver starts working again, responding to
+ *     hardware events and software requests.  The hardware may have gone
+ *     through a power-off reset, or it may have maintained state from the
+ *     previous suspend() which the driver may rely on while resuming.  On most
+ *     platforms, there are no restrictions on availability of resources like
+ *     clocks during @resume().
+ *
+ * @freeze: Hibernation-specific, executed before creating a hibernation image.
+ *     Quiesce operations so that a consistent image can be created, but do NOT
+ *     otherwise put the device into a low power device state and do NOT emit
+ *     system wakeup events.  Save in main memory the device settings to be
+ *     used by @restore() during the subsequent resume from hibernation or by
+ *     the subsequent @thaw(), if the creation of the image or the restoration
+ *     of main memory contents from it fails.
+ *
+ * @thaw: Hibernation-specific, executed after creating a hibernation image OR
+ *     if the creation of the image fails.  Also executed after a failing
+ *     attempt to restore the contents of main memory from such an image.
+ *     Undo the changes made by the preceding @freeze(), so the device can be
+ *     operated in the same way as immediately before the call to @freeze().
+ *
+ * @poweroff: Hibernation-specific, executed after saving a hibernation image.
+ *     Quiesce the device, put it into a low power state appropriate for the
+ *     upcoming system state (such as PCI_D3hot), and enable wakeup events as
+ *     appropriate.
+ *
+ * @restore: Hibernation-specific, executed after restoring the contents of main
+ *     memory from a hibernation image.  Driver starts working again,
+ *     responding to hardware events and software requests.  Drivers may NOT
+ *     make ANY assumptions about the hardware state right prior to @restore().
+ *     On most platforms, there are no restrictions on availability of
+ *     resources like clocks during @restore().
+ *
+ * All of the above callbacks, except for @complete(), return error codes.
+ * However, the error codes returned by the resume operations, @resume(),
+ * @thaw(), and @restore(), do not cause the PM core to abort the resume
+ * transition during which they are returned.  The error codes returned in
+ * that cases are only printed by the PM core to the system logs for debugging
+ * purposes.  Still, it is recommended that drivers only return error codes
+ * from their resume methods in case of an unrecoverable failure (i.e. when the
+ * device being handled refuses to resume and becomes unusable) to allow us to
+ * modify the PM core in the future, so that it can avoid attempting to handle
+ * devices that failed to resume and their children.
+ *
+ * It is allowed to unregister devices while the above callbacks are being
+ * executed.  However, it is not allowed to unregister a device from within any
+ * of its own callbacks.
+ */
+
+struct pm_ops {
+       int (*prepare)(struct device *dev);
+       void (*complete)(struct device *dev);
+       int (*suspend)(struct device *dev);
+       int (*resume)(struct device *dev);
+       int (*freeze)(struct device *dev);
+       int (*thaw)(struct device *dev);
+       int (*poweroff)(struct device *dev);
+       int (*restore)(struct device *dev);
+};
+
+/**
+ * struct pm_ext_ops - extended device PM callbacks
+ *
+ * Some devices require certain operations related to suspend and hibernation
+ * to be carried out with interrupts disabled.  Thus, 'struct pm_ext_ops' below
+ * is defined, adding callbacks to be executed with interrupts disabled to
+ * 'struct pm_ops'.
+ *
+ * The following callbacks included in 'struct pm_ext_ops' are executed with
+ * the nonboot CPUs switched off and with interrupts disabled on the only
+ * functional CPU.  They also are executed with the PM core list of devices
+ * locked, so they must NOT unregister any devices.
+ *
+ * @suspend_noirq: Complete the operations of ->suspend() by carrying out any
+ *     actions required for suspending the device that need interrupts to be
+ *     disabled
+ *
+ * @resume_noirq: Prepare for the execution of ->resume() by carrying out any
+ *     actions required for resuming the device that need interrupts to be
+ *     disabled
+ *
+ * @freeze_noirq: Complete the operations of ->freeze() by carrying out any
+ *     actions required for freezing the device that need interrupts to be
+ *     disabled
+ *
+ * @thaw_noirq: Prepare for the execution of ->thaw() by carrying out any
+ *     actions required for thawing the device that need interrupts to be
+ *     disabled
+ *
+ * @poweroff_noirq: Complete the operations of ->poweroff() by carrying out any
+ *     actions required for handling the device that need interrupts to be
+ *     disabled
+ *
+ * @restore_noirq: Prepare for the execution of ->restore() by carrying out any
+ *     actions required for restoring the operations of the device that need
+ *     interrupts to be disabled
+ *
+ * All of the above callbacks return error codes, but the error codes returned
+ * by the resume operations, @resume_noirq(), @thaw_noirq(), and
+ * @restore_noirq(), do not cause the PM core to abort the resume transition
+ * during which they are returned.  The error codes returned in that cases are
+ * only printed by the PM core to the system logs for debugging purposes.
+ * Still, as stated above, it is recommended that drivers only return error
+ * codes from their resume methods if the device being handled fails to resume
+ * and is not usable any more.
+ */
+
+struct pm_ext_ops {
+       struct pm_ops base;
+       int (*suspend_noirq)(struct device *dev);
+       int (*resume_noirq)(struct device *dev);
+       int (*freeze_noirq)(struct device *dev);
+       int (*thaw_noirq)(struct device *dev);
+       int (*poweroff_noirq)(struct device *dev);
+       int (*restore_noirq)(struct device *dev);
+};
+
+/**
+ * PM_EVENT_ messages
+ *
+ * The following PM_EVENT_ messages are defined for the internal use of the PM
+ * core, in order to provide a mechanism allowing the high level suspend and
+ * hibernation code to convey the necessary information to the device PM core
+ * code:
+ *
+ * ON          No transition.
+ *
+ * FREEZE      System is going to hibernate, call ->prepare() and ->freeze()
+ *             for all devices.
+ *
+ * SUSPEND     System is going to suspend, call ->prepare() and ->suspend()
+ *             for all devices.
+ *
+ * HIBERNATE   Hibernation image has been saved, call ->prepare() and
+ *             ->poweroff() for all devices.
+ *
+ * QUIESCE     Contents of main memory are going to be restored from a (loaded)
+ *             hibernation image, call ->prepare() and ->freeze() for all
+ *             devices.
+ *
+ * RESUME      System is resuming, call ->resume() and ->complete() for all
+ *             devices.
+ *
+ * THAW                Hibernation image has been created, call ->thaw() and
+ *             ->complete() for all devices.
+ *
+ * RESTORE     Contents of main memory have been restored from a hibernation
+ *             image, call ->restore() and ->complete() for all devices.
+ *
+ * RECOVER     Creation of a hibernation image or restoration of the main
+ *             memory contents from a hibernation image has failed, call
+ *             ->thaw() and ->complete() for all devices.
+ */
+
+#define PM_EVENT_ON            0x0000
+#define PM_EVENT_FREEZE        0x0001
+#define PM_EVENT_SUSPEND       0x0002
+#define PM_EVENT_HIBERNATE     0x0004
+#define PM_EVENT_QUIESCE       0x0008
+#define PM_EVENT_RESUME                0x0010
+#define PM_EVENT_THAW          0x0020
+#define PM_EVENT_RESTORE       0x0040
+#define PM_EVENT_RECOVER       0x0080
+
+#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE)
+
+#define PMSG_FREEZE    ((struct pm_message){ .event = PM_EVENT_FREEZE, })
+#define PMSG_QUIESCE   ((struct pm_message){ .event = PM_EVENT_QUIESCE, })
+#define PMSG_SUSPEND   ((struct pm_message){ .event = PM_EVENT_SUSPEND, })
+#define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, })
+#define PMSG_RESUME    ((struct pm_message){ .event = PM_EVENT_RESUME, })
+#define PMSG_THAW      ((struct pm_message){ .event = PM_EVENT_THAW, })
+#define PMSG_RESTORE   ((struct pm_message){ .event = PM_EVENT_RESTORE, })
+#define PMSG_RECOVER   ((struct pm_message){ .event = PM_EVENT_RECOVER, })
+#define PMSG_ON                ((struct pm_message){ .event = PM_EVENT_ON, })
+
+/**
+ * Device power management states
+ *
+ * These state labels are used internally by the PM core to indicate the current
+ * status of a device with respect to the PM core operations.
+ *
+ * DPM_ON              Device is regarded as operational.  Set this way
+ *                     initially and when ->complete() is about to be called.
+ *                     Also set when ->prepare() fails.
+ *
+ * DPM_PREPARING       Device is going to be prepared for a PM transition.  Set
+ *                     when ->prepare() is about to be called.
+ *
+ * DPM_RESUMING                Device is going to be resumed.  Set when ->resume(),
+ *                     ->thaw(), or ->restore() is about to be called.
+ *
+ * DPM_SUSPENDING      Device has been prepared for a power transition.  Set
+ *                     when ->prepare() has just succeeded.
+ *
+ * DPM_OFF             Device is regarded as inactive.  Set immediately after
+ *                     ->suspend(), ->freeze(), or ->poweroff() has succeeded.
+ *                     Also set when ->resume()_noirq, ->thaw_noirq(), or
+ *                     ->restore_noirq() is about to be called.
+ *
+ * DPM_OFF_IRQ         Device is in a "deep sleep".  Set immediately after
+ *                     ->suspend_noirq(), ->freeze_noirq(), or
+ *                     ->poweroff_noirq() has just succeeded.
+ */
+
+enum dpm_state {
+       DPM_INVALID,
+       DPM_ON,
+       DPM_PREPARING,
+       DPM_RESUMING,
+       DPM_SUSPENDING,
+       DPM_OFF,
+       DPM_OFF_IRQ,
+};
+
+struct dev_pm_info {
+       pm_message_t            power_state;
+       unsigned                can_wakeup:1;
+       unsigned                should_wakeup:1;
+       enum dpm_state          status;         /* Owned by the PM core */
+#ifdef CONFIG_PM_SLEEP
+       struct list_head        entry;
+#endif
+};
+
+/*
+ * The PM_EVENT_ messages are also used by drivers implementing the legacy
+ * suspend framework, based on the ->suspend() and ->resume() callbacks common
+ * for suspend and hibernation transitions, according to the rules below.
+ */
+
+/* Necessary, because several drivers use PM_EVENT_PRETHAW */
+#define PM_EVENT_PRETHAW PM_EVENT_QUIESCE
+
+/*
  * One transition is triggered by resume(), after a suspend() call; the
  * message is implicit:
  *
@@ -164,35 +444,13 @@ typedef struct pm_message {
  * or from system low-power states such as standby or suspend-to-RAM.
  */
 
-#define PM_EVENT_ON 0
-#define PM_EVENT_FREEZE 1
-#define PM_EVENT_SUSPEND 2
-#define PM_EVENT_HIBERNATE 4
-#define PM_EVENT_PRETHAW 8
-
-#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE)
-
-#define PMSG_FREEZE    ((struct pm_message){ .event = PM_EVENT_FREEZE, })
-#define PMSG_PRETHAW   ((struct pm_message){ .event = PM_EVENT_PRETHAW, })
-#define PMSG_SUSPEND   ((struct pm_message){ .event = PM_EVENT_SUSPEND, })
-#define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, })
-#define PMSG_ON                ((struct pm_message){ .event = PM_EVENT_ON, })
-
-struct dev_pm_info {
-       pm_message_t            power_state;
-       unsigned                can_wakeup:1;
-       unsigned                should_wakeup:1;
-       bool                    sleeping:1;     /* Owned by the PM core */
-#ifdef CONFIG_PM_SLEEP
-       struct list_head        entry;
-#endif
-};
+#ifdef CONFIG_PM_SLEEP
+extern void device_pm_lock(void);
+extern void device_power_up(pm_message_t state);
+extern void device_resume(pm_message_t state);
 
+extern void device_pm_unlock(void);
 extern int device_power_down(pm_message_t state);
-extern void device_power_up(void);
-extern void device_resume(void);
-
-#ifdef CONFIG_PM_SLEEP
 extern int device_suspend(pm_message_t state);
 extern int device_prepare_suspend(pm_message_t state);
 
index f0d0b2cb8d205ce6de169cae5c60c7a3166ef11a..0aae7776185e63523a3eac6535adfbd8f9524e28 100644 (file)
@@ -35,6 +35,11 @@ static inline void device_init_wakeup(struct device *dev, int val)
        dev->power.can_wakeup = dev->power.should_wakeup = !!val;
 }
 
+static inline void device_set_wakeup_capable(struct device *dev, int val)
+{
+       dev->power.can_wakeup = !!val;
+}
+
 static inline int device_can_wakeup(struct device *dev)
 {
        return dev->power.can_wakeup;
@@ -47,21 +52,7 @@ static inline void device_set_wakeup_enable(struct device *dev, int val)
 
 static inline int device_may_wakeup(struct device *dev)
 {
-       return dev->power.can_wakeup & dev->power.should_wakeup;
-}
-
-/*
- * Platform hook to activate device wakeup capability, if that's not already
- * handled by enable_irq_wake() etc.
- * Returns zero on success, else negative errno
- */
-extern int (*platform_enable_wakeup)(struct device *dev, int is_on);
-
-static inline int call_platform_enable_wakeup(struct device *dev, int is_on)
-{
-       if (platform_enable_wakeup)
-               return (*platform_enable_wakeup)(dev, is_on);
-       return 0;
+       return dev->power.can_wakeup && dev->power.should_wakeup;
 }
 
 #else /* !CONFIG_PM */
@@ -72,6 +63,8 @@ static inline void device_init_wakeup(struct device *dev, int val)
        dev->power.can_wakeup = !!val;
 }
 
+static inline void device_set_wakeup_capable(struct device *dev, int val) { }
+
 static inline int device_can_wakeup(struct device *dev)
 {
        return dev->power.can_wakeup;
@@ -80,11 +73,6 @@ static inline int device_can_wakeup(struct device *dev)
 #define device_set_wakeup_enable(dev, val)     do {} while (0)
 #define device_may_wakeup(dev)                 0
 
-static inline int call_platform_enable_wakeup(struct device *dev, int is_on)
-{
-       return 0;
-}
-
 #endif /* !CONFIG_PM */
 
 #endif /* _LINUX_PM_WAKEUP_H */
index a6977423baf737ed67d4847cdce4c89fc24452e0..e8e69159af71c2ed546fb97f25316bcd5de1ea0f 100644 (file)
@@ -86,6 +86,11 @@ typedef int __bitwise suspend_state_t;
  *     that implement @begin(), but platforms implementing @begin() should
  *     also provide a @end() which cleans up transitions aborted before
  *     @enter().
+ *
+ * @recover: Recover the platform from a suspend failure.
+ *     Called by the PM core if the suspending of devices fails.
+ *     This callback is optional and should only be implemented by platforms
+ *     which require special recovery actions in that situation.
  */
 struct platform_suspend_ops {
        int (*valid)(suspend_state_t state);
@@ -94,6 +99,7 @@ struct platform_suspend_ops {
        int (*enter)(suspend_state_t state);
        void (*finish)(void);
        void (*end)(void);
+       void (*recover)(void);
 };
 
 #ifdef CONFIG_SUSPEND
@@ -149,7 +155,7 @@ extern void mark_free_pages(struct zone *zone);
  * The methods in this structure allow a platform to carry out special
  * operations required by it during a hibernation transition.
  *
- * All the methods below must be implemented.
+ * All the methods below, except for @recover(), must be implemented.
  *
  * @begin: Tell the platform driver that we're starting hibernation.
  *     Called right after shrinking memory and before freezing devices.
@@ -189,6 +195,11 @@ extern void mark_free_pages(struct zone *zone);
  * @restore_cleanup: Clean up after a failing image restoration.
  *     Called right after the nonboot CPUs have been enabled and before
  *     thawing devices (runs with IRQs on).
+ *
+ * @recover: Recover the platform from a failure to suspend devices.
+ *     Called by the PM core if the suspending of devices during hibernation
+ *     fails.  This callback is optional and should only be implemented by
+ *     platforms which require special recovery actions in that situation.
  */
 struct platform_hibernation_ops {
        int (*begin)(void);
@@ -200,6 +211,7 @@ struct platform_hibernation_ops {
        void (*leave)(void);
        int (*pre_restore)(void);
        void (*restore_cleanup)(void);
+       void (*recover)(void);
 };
 
 #ifdef CONFIG_HIBERNATION
index 14a656cdc6523bf64d9c5b64c3a628529b56bab5..f011e0870b52b316032295bc9b6ffcc45a4358ea 100644 (file)
@@ -179,6 +179,17 @@ static void platform_restore_cleanup(int platform_mode)
                hibernation_ops->restore_cleanup();
 }
 
+/**
+ *     platform_recover - recover the platform from a failure to suspend
+ *     devices.
+ */
+
+static void platform_recover(int platform_mode)
+{
+       if (platform_mode && hibernation_ops && hibernation_ops->recover)
+               hibernation_ops->recover();
+}
+
 /**
  *     create_image - freeze devices that need to be frozen with interrupts
  *     off, create the hibernation image and thaw those devices.  Control
@@ -193,6 +204,7 @@ static int create_image(int platform_mode)
        if (error)
                return error;
 
+       device_pm_lock();
        local_irq_disable();
        /* At this point, device_suspend() has been called, but *not*
         * device_power_down(). We *must* call device_power_down() now.
@@ -224,9 +236,11 @@ static int create_image(int platform_mode)
        /* NOTE:  device_power_up() is just a resume() for devices
         * that suspended with irqs off ... no overall powerup.
         */
-       device_power_up();
+       device_power_up(in_suspend ?
+               (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
  Enable_irqs:
        local_irq_enable();
+       device_pm_unlock();
        return error;
 }
 
@@ -255,10 +269,10 @@ int hibernation_snapshot(int platform_mode)
        suspend_console();
        error = device_suspend(PMSG_FREEZE);
        if (error)
-               goto Resume_console;
+               goto Recover_platform;
 
        if (hibernation_test(TEST_DEVICES))
-               goto Resume_devices;
+               goto Recover_platform;
 
        error = platform_pre_snapshot(platform_mode);
        if (error || hibernation_test(TEST_PLATFORM))
@@ -280,12 +294,16 @@ int hibernation_snapshot(int platform_mode)
  Finish:
        platform_finish(platform_mode);
  Resume_devices:
-       device_resume();
- Resume_console:
+       device_resume(in_suspend ?
+               (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
        resume_console();
  Close:
        platform_end(platform_mode);
        return error;
+
+ Recover_platform:
+       platform_recover(platform_mode);
+       goto Resume_devices;
 }
 
 /**
@@ -300,8 +318,9 @@ static int resume_target_kernel(void)
 {
        int error;
 
+       device_pm_lock();
        local_irq_disable();
-       error = device_power_down(PMSG_PRETHAW);
+       error = device_power_down(PMSG_QUIESCE);
        if (error) {
                printk(KERN_ERR "PM: Some devices failed to power down, "
                        "aborting resume\n");
@@ -329,9 +348,10 @@ static int resume_target_kernel(void)
        swsusp_free();
        restore_processor_state();
        touch_softlockup_watchdog();
-       device_power_up();
+       device_power_up(PMSG_RECOVER);
  Enable_irqs:
        local_irq_enable();
+       device_pm_unlock();
        return error;
 }
 
@@ -350,7 +370,7 @@ int hibernation_restore(int platform_mode)
 
        pm_prepare_console();
        suspend_console();
-       error = device_suspend(PMSG_PRETHAW);
+       error = device_suspend(PMSG_QUIESCE);
        if (error)
                goto Finish;
 
@@ -362,7 +382,7 @@ int hibernation_restore(int platform_mode)
                enable_nonboot_cpus();
        }
        platform_restore_cleanup(platform_mode);
-       device_resume();
+       device_resume(PMSG_RECOVER);
  Finish:
        resume_console();
        pm_restore_console();
@@ -392,8 +412,11 @@ int hibernation_platform_enter(void)
 
        suspend_console();
        error = device_suspend(PMSG_HIBERNATE);
-       if (error)
-               goto Resume_console;
+       if (error) {
+               if (hibernation_ops->recover)
+                       hibernation_ops->recover();
+               goto Resume_devices;
+       }
 
        error = hibernation_ops->prepare();
        if (error)
@@ -403,6 +426,7 @@ int hibernation_platform_enter(void)
        if (error)
                goto Finish;
 
+       device_pm_lock();
        local_irq_disable();
        error = device_power_down(PMSG_HIBERNATE);
        if (!error) {
@@ -411,6 +435,7 @@ int hibernation_platform_enter(void)
                while (1);
        }
        local_irq_enable();
+       device_pm_unlock();
 
        /*
         * We don't need to reenable the nonboot CPUs or resume consoles, since
@@ -419,8 +444,7 @@ int hibernation_platform_enter(void)
  Finish:
        hibernation_ops->finish();
  Resume_devices:
-       device_resume();
- Resume_console:
+       device_resume(PMSG_RESTORE);
        resume_console();
  Close:
        hibernation_ops->end();
index 6a6d5eb3524e7d2f219aeaa894e70981b3a706fb..3398f4651aa150b370a6eeef95305ca56ff39a0b 100644 (file)
@@ -228,6 +228,7 @@ static int suspend_enter(suspend_state_t state)
 {
        int error = 0;
 
+       device_pm_lock();
        arch_suspend_disable_irqs();
        BUG_ON(!irqs_disabled());
 
@@ -239,10 +240,11 @@ static int suspend_enter(suspend_state_t state)
        if (!suspend_test(TEST_CORE))
                error = suspend_ops->enter(state);
 
-       device_power_up();
+       device_power_up(PMSG_RESUME);
  Done:
        arch_suspend_enable_irqs();
        BUG_ON(irqs_disabled());
+       device_pm_unlock();
        return error;
 }
 
@@ -267,11 +269,11 @@ int suspend_devices_and_enter(suspend_state_t state)
        error = device_suspend(PMSG_SUSPEND);
        if (error) {
                printk(KERN_ERR "PM: Some devices failed to suspend\n");
-               goto Resume_console;
+               goto Recover_platform;
        }
 
        if (suspend_test(TEST_DEVICES))
-               goto Resume_devices;
+               goto Recover_platform;
 
        if (suspend_ops->prepare) {
                error = suspend_ops->prepare();
@@ -291,13 +293,17 @@ int suspend_devices_and_enter(suspend_state_t state)
        if (suspend_ops->finish)
                suspend_ops->finish();
  Resume_devices:
-       device_resume();
- Resume_console:
+       device_resume(PMSG_RESUME);
        resume_console();
  Close:
        if (suspend_ops->end)
                suspend_ops->end();
        return error;
+
+ Recover_platform:
+       if (suspend_ops->recover)
+               suspend_ops->recover();
+       goto Resume_devices;
 }
 
 /**
index 718e5101c263596224f7f35b523cea27e093ecd6..dcade0543bd2a2395ede8f91bf33438348e54f7e 100644 (file)
@@ -439,6 +439,7 @@ out:
 
        return error;
 }
+EXPORT_SYMBOL_GPL(kobject_rename);
 
 /**
  * kobject_move - move object to another parent