mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 04:34:08 +08:00
Merge branch 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6
* '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.
This commit is contained in:
commit
dc7c65db28
@ -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
|
||||
|
12
MAINTAINERS
12
MAINTAINERS
@ -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
|
||||
|
@ -19,36 +19,6 @@
|
||||
|
||||
#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
|
||||
|
@ -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__);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
-------------------------------------------------------------------------- */
|
||||
|
@ -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;
|
||||
|
368
drivers/acpi/pci_slot.c
Normal file
368
drivers/acpi/pci_slot.c
Normal file
@ -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);
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -24,10 +24,6 @@
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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 int acpi_hibernation_pre_restore(void)
|
||||
{
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_hw_disable_all_gpes();
|
||||
|
||||
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
|
||||
}
|
||||
|
||||
static void acpi_hibernation_restore_cleanup(void)
|
||||
static void acpi_pm_enable_gpes(void)
|
||||
{
|
||||
acpi_hw_enable_all_runtime_gpes();
|
||||
}
|
||||
|
||||
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,
|
||||
.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_hibernation_pre_restore,
|
||||
.restore_cleanup = acpi_hibernation_restore_cleanup,
|
||||
.pre_restore = acpi_pm_disable_gpes,
|
||||
.restore_cleanup = acpi_pm_enable_gpes,
|
||||
};
|
||||
#endif /* CONFIG_HIBERNATION */
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
int error = acpi_sleep_prepare(ACPI_STATE_S4);
|
||||
|
||||
if (!error)
|
||||
acpi_target_sleep_state = ACPI_STATE_S4;
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_pm_disable_gpes,
|
||||
.restore_cleanup = acpi_pm_enable_gpes,
|
||||
.recover = acpi_pm_finish,
|
||||
};
|
||||
#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");
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -12,11 +12,9 @@
|
||||
* 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>
|
||||
@ -30,31 +28,40 @@
|
||||
#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.
|
||||
*
|
||||
* Take devices from the dpm_off_list, resume them,
|
||||
* and put them on the dpm_locked list.
|
||||
* Execute the appropriate "resume" callback for all devices the status of
|
||||
* which indicates that they are inactive.
|
||||
*/
|
||||
static void dpm_resume(void)
|
||||
static void dpm_resume(pm_message_t state)
|
||||
{
|
||||
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);
|
||||
struct list_head list;
|
||||
|
||||
list_move_tail(entry, &dpm_active);
|
||||
dev->power.sleeping = false;
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
resume_device(dev);
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
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.
|
||||
*
|
||||
* Execute the ->complete() callbacks for all devices that are not marked
|
||||
* as DPM_ON.
|
||||
*/
|
||||
static void dpm_complete(pm_message_t state)
|
||||
{
|
||||
struct list_head list;
|
||||
|
||||
INIT_LIST_HEAD(&list);
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
while (!list_empty(&dpm_list)) {
|
||||
struct device *dev = to_device(dpm_list.prev);
|
||||
|
||||
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.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Walk the dpm_locked list. Suspend each device and move it
|
||||
* to the dpm_off list.
|
||||
*
|
||||
* (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;
|
||||
}
|
||||
|
||||
/**
|
||||
* device_suspend - Save state and stop all devices in system.
|
||||
* @state: new power management state
|
||||
* dpm_prepare - Prepare all devices for a PM transition.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Prevent new devices from being registered, then lock all devices
|
||||
* and suspend them.
|
||||
* 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: PM transition of the system being carried out.
|
||||
*
|
||||
* 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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -33,8 +33,10 @@
|
||||
#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__);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -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 ();
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 (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;
|
||||
|
||||
ctrl = pcie_init(dev);
|
||||
if (!ctrl) {
|
||||
err("%s : out of memory\n", __func__);
|
||||
dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME);
|
||||
goto err_out_none;
|
||||
}
|
||||
INIT_LIST_HEAD(&ctrl->slot_list);
|
||||
|
||||
pdev = dev->port;
|
||||
ctrl->pci_dev = pdev;
|
||||
|
||||
rc = pcie_init(ctrl, dev);
|
||||
if (rc) {
|
||||
dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME);
|
||||
goto err_out_free_ctrl;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
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, ®16);
|
||||
dbg("Slot Status : 0x%04x\n", reg16);
|
||||
pciehp_readw(ctrl, SLOTSTATUS, ®16);
|
||||
pciehp_readw(ctrl, SLOTCTRL, ®16);
|
||||
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);
|
||||
|
||||
return 0;
|
||||
if (pcie_init_slot(ctrl))
|
||||
goto abort_ctrl;
|
||||
|
||||
abort_free_irq:
|
||||
pciehp_free_irq(ctrl);
|
||||
if (pcie_init_notification(ctrl))
|
||||
goto abort_slot;
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -14,8 +14,10 @@
|
||||
*/
|
||||
#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;
|
||||
|
||||
|
@ -33,33 +33,6 @@
|
||||
#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);
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -21,12 +21,19 @@
|
||||
|
||||
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,25 +51,80 @@ 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;
|
||||
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;
|
||||
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 *)osc_args->capbuf;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_OSC", &input, &output);
|
||||
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 out_kfree;
|
||||
}
|
||||
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) {
|
||||
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 out_kfree;
|
||||
}
|
||||
out_success:
|
||||
if (flags & OSC_QUERY_ENABLE)
|
||||
osc_args->query_result =
|
||||
*((u32 *)(out_obj->buffer.pointer + 8));
|
||||
status = AE_OK;
|
||||
|
||||
out_kfree:
|
||||
kfree(output.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static acpi_status acpi_query_osc(acpi_handle handle,
|
||||
u32 level, void *context, void **retval)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_osc_data *osc_data;
|
||||
u32 flags = (unsigned long)context, temp;
|
||||
u32 flags = (unsigned long)context, support_set;
|
||||
acpi_handle tmp;
|
||||
struct acpi_osc_args osc_args;
|
||||
|
||||
status = acpi_get_handle(handle, "_OSC", &tmp);
|
||||
if (ACPI_FAILURE(status))
|
||||
@ -74,134 +136,19 @@ acpi_query_osc (
|
||||
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;
|
||||
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;
|
||||
|
||||
/* 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 *)osc_data->ctrlset_buf;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_OSC", &input, &output);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto out_nofree;
|
||||
out_obj = output.pointer;
|
||||
|
||||
if (out_obj->type != ACPI_TYPE_BUFFER) {
|
||||
printk(KERN_DEBUG
|
||||
"Evaluate _OSC returns wrong type\n");
|
||||
status = AE_TYPE;
|
||||
goto query_osc_out;
|
||||
}
|
||||
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) {
|
||||
/* Update Global Control Set */
|
||||
osc_data->global_ctrlsets =
|
||||
*((u32 *)(out_obj->buffer.pointer + 8));
|
||||
status = AE_OK;
|
||||
goto query_osc_out;
|
||||
}
|
||||
status = AE_ERROR;
|
||||
goto query_osc_out;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Update Global Control Set */
|
||||
osc_data->global_ctrlsets = *((u32 *)(out_obj->buffer.pointer + 8));
|
||||
status = AE_OK;
|
||||
|
||||
query_osc_out:
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_OSC", &input, &output);
|
||||
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_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;
|
||||
}
|
||||
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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
int pci_set_platform_pm(struct pci_platform_pm_ops *ops)
|
||||
{
|
||||
if (!ops->is_manageable || !ops->set_state || !ops->choose_state
|
||||
|| !ops->sleep_wake || !ops->can_wakeup)
|
||||
return -EINVAL;
|
||||
pci_platform_pm = ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool platform_pci_power_manageable(struct pci_dev *dev)
|
||||
{
|
||||
return pci_platform_pm ? pci_platform_pm->is_manageable(dev) : false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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)
|
||||
static int
|
||||
pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||
{
|
||||
int pm, need_restore = 0;
|
||||
u16 pmcsr, pmc;
|
||||
u16 pmcsr;
|
||||
bool need_restore = false;
|
||||
|
||||
/* bound the state we're entering */
|
||||
if (state > PCI_D3hot)
|
||||
state = PCI_D3hot;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/* find PCI PM capability in list */
|
||||
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
||||
|
||||
/* abort if the device doesn't support PM capabilities */
|
||||
if (!pm)
|
||||
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;
|
||||
int error = 0;
|
||||
bool pme_done = false;
|
||||
|
||||
/* 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.
|
||||
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);
|
||||
|
||||
if (!enable || pci_pme_capable(dev, state)) {
|
||||
pci_pme_active(dev, enable);
|
||||
pme_done = true;
|
||||
}
|
||||
|
||||
if (enable && platform_pci_can_wakeup(dev))
|
||||
error = platform_pci_sleep_wake(dev, true);
|
||||
|
||||
return pme_done ? 0 : error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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.
|
||||
*/
|
||||
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_enable_wake(dev, target_state, true);
|
||||
|
||||
error = pci_set_power_state(dev, target_state);
|
||||
|
||||
if (error)
|
||||
pci_enable_wake(dev, target_state, false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
|
||||
return;
|
||||
/* Check device's ability to generate PME# */
|
||||
pci_read_config_word(dev,pm+PCI_PM_PMC,&value);
|
||||
pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
|
||||
|
||||
value &= PCI_PM_CAP_PME_MASK;
|
||||
value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */
|
||||
|
||||
/* 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 (enable)
|
||||
call_platform_enable_wakeup(&dev->dev, 0);
|
||||
return enable ? -EINVAL : 0;
|
||||
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;
|
||||
}
|
||||
|
||||
pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
|
||||
dev->pm_cap = pm;
|
||||
|
||||
/* Clear PME_Status by writing 1 to it and enable PME# */
|
||||
value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enable)
|
||||
value &= ~PCI_PM_CTRL_PME_ENABLE;
|
||||
|
||||
pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
|
||||
|
||||
return 0;
|
||||
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);
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -27,13 +27,6 @@
|
||||
#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, ®ion, 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, ®ion, 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, ®ion, 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, ®ion, 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, ®ion, 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;
|
||||
}
|
||||
}
|
||||
|
@ -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. */
|
||||
|
@ -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, ®ion, 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;
|
||||
|
233
drivers/pci/slot.c
Normal file
233
drivers/pci/slot.c
Normal file
@ -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);
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -86,6 +86,12 @@
|
||||
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 */ \
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
@ -49,12 +48,22 @@
|
||||
#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);
|
||||
|
@ -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
|
||||
|
@ -231,6 +231,7 @@
|
||||
#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 */
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
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 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 int device_suspend(pm_message_t state);
|
||||
extern int device_prepare_suspend(pm_message_t state);
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -439,6 +439,7 @@ out:
|
||||
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kobject_rename);
|
||||
|
||||
/**
|
||||
* kobject_move - move object to another parent
|
||||
|
Loading…
Reference in New Issue
Block a user