提交 dc7c65db 编写于 作者: L Linus Torvalds

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.
......@@ -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
......
......@@ -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: 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;
}
......@@ -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,11 +1251,16 @@ 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",
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;
......
/*
* 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,50 +318,53 @@ static void acpi_hibernation_leave(void)
acpi_leave_sleep_state_prep(ACPI_STATE_S4);
}
static void acpi_hibernation_finish(void)
static void acpi_pm_enable_gpes(void)
{
acpi_disable_wakeup_device(ACPI_STATE_S4);
acpi_leave_sleep_state(ACPI_STATE_S4);
/* reset firmware waking vector */
acpi_set_firmware_waking_vector((acpi_physical_address) 0);
acpi_target_sleep_state = ACPI_STATE_S0;
acpi_hw_enable_all_runtime_gpes();
}
static void acpi_hibernation_end(void)
{
/*
* This is necessary in case acpi_hibernation_finish() is not called
* during a failing transition to the sleep state.
*/
acpi_target_sleep_state = ACPI_STATE_S0;
}
static struct platform_hibernation_ops acpi_hibernation_ops = {
.begin = acpi_hibernation_begin,
.end = acpi_pm_end,
.pre_snapshot = acpi_pm_prepare,
.finish = acpi_pm_finish,
.prepare = acpi_pm_prepare,
.enter = acpi_hibernation_enter,
.leave = acpi_hibernation_leave,
.pre_restore = acpi_pm_disable_gpes,
.restore_cleanup = acpi_pm_enable_gpes,
};
static int acpi_hibernation_pre_restore(void)
/**
* acpi_hibernation_begin_old - Set the target system sleep state to
* ACPI_STATE_S4 and execute the _PTS control method. This
* function is used if the pre-ACPI 2.0 suspend ordering has been
* requested.
*/
static int acpi_hibernation_begin_old(void)
{
acpi_status status;
status = acpi_hw_disable_all_gpes();
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
}
int error = acpi_sleep_prepare(ACPI_STATE_S4);
static void acpi_hibernation_restore_cleanup(void)
{
acpi_hw_enable_all_runtime_gpes();
if (!error)
acpi_target_sleep_state = ACPI_STATE_S4;
return error;
}
static struct platform_hibernation_ops acpi_hibernation_ops = {
.begin = acpi_hibernation_begin,
.end = acpi_hibernation_end,
.pre_snapshot = acpi_hibernation_prepare,
.finish = acpi_hibernation_finish,
.prepare = acpi_hibernation_prepare,
/*
* The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
* been requested.
*/
static struct platform_hibernation_ops acpi_hibernation_ops_old = {
.begin = acpi_hibernation_begin_old,
.end = acpi_pm_end,
.pre_snapshot = acpi_pm_disable_gpes,
.finish = acpi_pm_finish,
.prepare = acpi_pm_disable_gpes,
.enter = acpi_hibernation_enter,
.leave = acpi_hibernation_leave,
.pre_restore = acpi_hibernation_pre_restore,
.restore_cleanup = acpi_hibernation_restore_cleanup,
.pre_restore = acpi_pm_disable_gpes,
.restore_cleanup = acpi_pm_enable_gpes,
.recover = acpi_pm_finish,
};
#endif /* CONFIG_HIBERNATION */
......@@ -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);
......
此差异已折叠。
......@@ -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 @@ acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
*
* @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 @@ static int __init ibm_acpiphp_init(void)
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 @@ static int add_slot(struct pci_dev *dev)
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;
}
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;
}
/* 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");
}
}
/* 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. */
/* queue work item to blow away this sysfs entry and other
* parts.
*/
INIT_WORK(&dslot->remove_work, remove_slot_worker);
queue_work(dummyphp_wq, &dslot->remove_work);
/* blow away this sysfs entry and other parts. */
remove_slot(dslot);
pci_dev_put(dev);
}
return 0;
}
......
......@@ -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 @@ static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
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 @@ static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
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 @@ static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
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 @@ static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
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 @@ static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
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 @@ static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
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 @@ static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
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 @@ static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
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 @@ static int fs_add_slot (struct hotplug_slot *slot)
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 @@ static int fs_add_slot (struct hotplug_slot *slot)
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);
/*
* 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;
}
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) {
err("Unable to register kobject '%s'", slot->name);
return -EINVAL;
pci_destroy_slot(pci_slot);
return result;
}
}
list_add (&slot->slot_list, &pci_hotplug_slot_list);
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 @@ static int init_slots(struct controller *ctrl)
kfree(info);
error_hpslot:
kfree(hotplug_slot);
error_slot:
kfree(slot);
error:
return retval;
}
static void cleanup_slots(struct controller *ctrl)
{
struct list_head *tmp;
struct list_head *next;
struct slot *slot;
list_for_each_safe(tmp, next, &ctrl->slot_list) {
slot = list_entry(tmp, struct slot, slot_list);
list_del(&slot->slot_list);
list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
if (EMI(ctrl))
sysfs_remove_file(&slot->hotplug_slot->kobj,
sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj,
&hotplug_slot_attr_lock.attr);
cancel_delayed_work(&slot->work);
flush_scheduled_work();
flush_workqueue(pciehp_wq);
pci_hp_deregister(slot->hotplug_slot);
}
}
......@@ -398,19 +357,8 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}
static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
{
struct slot *slot = hotplug_slot->private;
struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
*value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
return 0;
}
static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
static int get_max_bus_speed(struct hotplug_slot *hotplug_slot,
enum pci_bus_speed *value)
{
struct slot *slot = hotplug_slot->private;
int retval;
......@@ -444,34 +392,30 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
struct controller *ctrl;
struct slot *t_slot;
u8 value;
struct pci_dev *pdev;
struct pci_dev *pdev = dev->port;
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
if (!ctrl) {
err("%s : out of memory\n", __func__);
if (pciehp_force)
dbg("Bypassing BIOS check for pciehp use on %s\n",
pci_name(pdev));
else if (pciehp_get_hp_hw_control_from_firmware(pdev))
goto err_out_none;
}
INIT_LIST_HEAD(&ctrl->slot_list);
pdev = dev->port;
ctrl->pci_dev = pdev;
rc = pcie_init(ctrl, dev);
if (rc) {
ctrl = pcie_init(dev);
if (!ctrl) {
dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME);
goto err_out_free_ctrl;
goto err_out_none;
}
pci_set_drvdata(pdev, ctrl);
dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n",
__func__, pdev->bus->number, PCI_SLOT(pdev->devfn),
PCI_FUNC(pdev->devfn), pdev->irq);
set_service_data(dev, ctrl);
/* Setup the slot information structures */
rc = init_slots(ctrl);
if (rc) {
err("%s: slot initialization failed\n", PCIE_MODULE_NAME);
if (rc == -EBUSY)
warn("%s: slot already registered by another "
"hotplug driver\n", PCIE_MODULE_NAME);
else
err("%s: slot initialization failed\n",
PCIE_MODULE_NAME);
goto err_out_release_ctlr;
}
......@@ -495,20 +439,16 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
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;
}
return 0; /* timeout */
completed:
}
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 timeout;
return 1;
}
}
}
return 0; /* timeout */
}
static inline int pcie_wait_cmd(struct controller *ctrl, int poll)
static void pcie_wait_cmd(struct controller *ctrl, int poll)
{
int retval = 0;
unsigned int msecs = pciehp_poll_mode ? 2500 : 1000;
unsigned long timeout = msecs_to_jiffies(msecs);
int rc;
......@@ -278,16 +280,9 @@ static inline int pcie_wait_cmd(struct controller *ctrl, int poll)
if (poll)
rc = pcie_poll_cmd(ctrl);
else
rc = wait_event_interruptible_timeout(ctrl->queue,
!ctrl->cmd_busy, timeout);
rc = wait_event_timeout(ctrl->queue, !ctrl->cmd_busy, timeout);
if (!rc)
dbg("Command not completed in 1000 msec\n");
else if (rc < 0) {
retval = -EINTR;
info("Command was interrupted by a signal\n");
}
return retval;
}
/**
......@@ -342,10 +337,6 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
slot_ctrl &= ~mask;
slot_ctrl |= (cmd & mask);
/* Don't enable command completed if caller is changing it. */
if (!(mask & CMD_CMPL_INTR_ENABLE))
slot_ctrl |= CMD_CMPL_INTR_ENABLE;
ctrl->cmd_busy = 1;
smp_mb();
retval = pciehp_writew(ctrl, SLOTCTRL, slot_ctrl);
......@@ -365,7 +356,7 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
if (!(slot_ctrl & HP_INTR_ENABLE) ||
!(slot_ctrl & CMD_CMPL_INTR_ENABLE))
poll = 1;
retval = pcie_wait_cmd(ctrl, poll);
pcie_wait_cmd(ctrl, poll);
}
out:
mutex_unlock(&ctrl->ctrl_lock);
......@@ -614,23 +605,6 @@ static void hpc_set_green_led_blink(struct slot *slot)
__func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
}
static void hpc_release_ctlr(struct controller *ctrl)
{
/* Mask Hot-plug Interrupt Enable */
if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE))
err("%s: Cannot mask hotplut interrupt enable\n", __func__);
/* Free interrupt handler or interrupt polling timer */
pciehp_free_irq(ctrl);
/*
* If this is the last controller to be released, destroy the
* pciehp work queue
*/
if (atomic_dec_and_test(&pciehp_num_controllers))
destroy_workqueue(pciehp_wq);
}
static int hpc_power_on_slot(struct slot * slot)
{
struct controller *ctrl = slot->ctrl;
......@@ -785,7 +759,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
intr_loc |= detected;
if (!intr_loc)
return IRQ_NONE;
if (pciehp_writew(ctrl, SLOTSTATUS, detected)) {
if (detected && pciehp_writew(ctrl, SLOTSTATUS, detected)) {
err("%s: Cannot write to SLOTSTATUS\n", __func__);
return IRQ_NONE;
}
......@@ -797,25 +771,13 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
if (intr_loc & CMD_COMPLETED) {
ctrl->cmd_busy = 0;
smp_mb();
wake_up_interruptible(&ctrl->queue);
wake_up(&ctrl->queue);
}
if (!(intr_loc & ~CMD_COMPLETED))
return IRQ_HANDLED;
/*
* Return without handling events if this handler routine is
* called before controller initialization is done. This may
* happen if hotplug event or another interrupt that shares
* the IRQ with pciehp arrives before slot initialization is
* done after interrupt handler is registered.
*
* FIXME - Need more structural fixes. We need to be ready to
* handle the event before installing interrupt handler.
*/
p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
if (!p_slot || !p_slot->hpc_ops)
return IRQ_HANDLED;
/* Check MRL Sensor Changed */
if (intr_loc & MRL_SENS_CHANGED)
......@@ -992,6 +954,7 @@ static int hpc_get_cur_lnk_width(struct slot *slot,
return retval;
}
static void pcie_release_ctrl(struct controller *ctrl);
static struct hpc_ops pciehp_hpc_ops = {
.power_on_slot = hpc_power_on_slot,
.power_off_slot = hpc_power_off_slot,
......@@ -1013,97 +976,11 @@ static struct hpc_ops pciehp_hpc_ops = {
.green_led_off = hpc_set_green_led_off,
.green_led_blink = hpc_set_green_led_blink,
.release_ctlr = hpc_release_ctlr,
.release_ctlr = pcie_release_ctrl,
.check_lnk_status = hpc_check_lnk_status,
};
#ifdef CONFIG_ACPI
static int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev)
{
acpi_status status;
acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev));
struct pci_dev *pdev = dev;
struct pci_bus *parent;
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
/*
* Per PCI firmware specification, we should run the ACPI _OSC
* method to get control of hotplug hardware before using it.
* If an _OSC is missing, we look for an OSHP to do the same thing.
* To handle different BIOS behavior, we look for _OSC and OSHP
* within the scope of the hotplug controller and its parents, upto
* the host bridge under which this controller exists.
*/
while (!handle) {
/*
* This hotplug controller was not listed in the ACPI name
* space at all. Try to get acpi handle of parent pci bus.
*/
if (!pdev || !pdev->bus->parent)
break;
parent = pdev->bus->parent;
dbg("Could not find %s in acpi namespace, trying parent\n",
pci_name(pdev));
if (!parent->self)
/* Parent must be a host bridge */
handle = acpi_get_pci_rootbridge_handle(
pci_domain_nr(parent),
parent->number);
else
handle = DEVICE_ACPI_HANDLE(
&(parent->self->dev));
pdev = parent->self;
}
while (handle) {
acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
dbg("Trying to get hotplug control for %s \n",
(char *)string.pointer);
status = pci_osc_control_set(handle,
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL |
OSC_PCI_EXPRESS_NATIVE_HP_CONTROL);
if (status == AE_NOT_FOUND)
status = acpi_run_oshp(handle);
if (ACPI_SUCCESS(status)) {
dbg("Gained control for hotplug HW for pci %s (%s)\n",
pci_name(dev), (char *)string.pointer);
kfree(string.pointer);
return 0;
}
if (acpi_root_bridge(handle))
break;
chandle = handle;
status = acpi_get_parent(chandle, &handle);
if (ACPI_FAILURE(status))
break;
}
dbg("Cannot get control of hotplug hardware for pci %s\n",
pci_name(dev));
kfree(string.pointer);
return -1;
}
#endif
static int pcie_init_hardware_part1(struct controller *ctrl,
struct pcie_device *dev)
{
/* Clear all remaining event bits in Slot Status register */
if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) {
err("%s: Cannot write to SLOTSTATUS register\n", __func__);
return -1;
}
/* Mask Hot-plug Interrupt Enable */
if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE)) {
err("%s: Cannot mask hotplug interrupt enable\n", __func__);
return -1;
}
return 0;
}
int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
int pcie_enable_notification(struct controller *ctrl)
{
u16 cmd, mask;
......@@ -1115,30 +992,83 @@ int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
if (MRL_SENS(ctrl))
cmd |= MRL_DETECT_ENABLE;
if (!pciehp_poll_mode)
cmd |= HP_INTR_ENABLE;
cmd |= HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE;
mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE |
PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | HP_INTR_ENABLE;
mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE |
PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE;
if (pcie_write_cmd(ctrl, cmd, mask)) {
err("%s: Cannot enable software notification\n", __func__);
goto abort;
return -1;
}
return 0;
}
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;
}
if (pciehp_force)
dbg("Bypassing BIOS check for pciehp use on %s\n",
pci_name(ctrl->pci_dev));
else if (pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev))
goto abort_disable_intr;
static void pcie_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;
}
/* 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_cleanup_slot(struct controller *ctrl)
{
struct slot *slot;
slot = list_first_entry(&ctrl->slot_list, struct slot, slot_list);
list_del(&slot->slot_list);
cancel_delayed_work(&slot->work);
flush_scheduled_work();
flush_workqueue(pciehp_wq);
kfree(slot);
}
static inline void dbg_ctrl(struct controller *ctrl)
......@@ -1176,15 +1106,23 @@ static inline void dbg_ctrl(struct controller *ctrl)
dbg(" Comamnd Completed : %3s\n", NO_CMD_CMPL(ctrl)? "no" : "yes");
pciehp_readw(ctrl, SLOTSTATUS, &reg16);
dbg("Slot Status : 0x%04x\n", reg16);
pciehp_readw(ctrl, SLOTSTATUS, &reg16);
pciehp_readw(ctrl, SLOTCTRL, &reg16);
dbg("Slot Control : 0x%04x\n", reg16);
}
int pcie_init(struct controller *ctrl, struct pcie_device *dev)
struct controller *pcie_init(struct pcie_device *dev)
{
struct controller *ctrl;
u32 slot_cap;
struct pci_dev *pdev = dev->port;
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
if (!ctrl) {
err("%s : out of memory\n", __func__);
goto abort;
}
INIT_LIST_HEAD(&ctrl->slot_list);
ctrl->pci_dev = pdev;
ctrl->cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP);
if (!ctrl->cap_base) {
......@@ -1215,15 +1153,12 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev)
!(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl)))
ctrl->no_cmd_complete = 1;
info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",
pdev->vendor, pdev->device,
pdev->subsystem_vendor, pdev->subsystem_device);
if (pcie_init_hardware_part1(ctrl, dev))
goto abort;
/* Clear all remaining event bits in Slot Status register */
if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f))
goto abort_ctrl;
if (pciehp_request_irq(ctrl))
goto abort;
/* Disable sotfware notification */
pcie_disable_notification(ctrl);
/*
* If this is the first controller to be initialized,
......@@ -1231,18 +1166,39 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev)
*/
if (atomic_add_return(1, &pciehp_num_controllers) == 1) {
pciehp_wq = create_singlethread_workqueue("pciehpd");
if (!pciehp_wq) {
goto abort_free_irq;
}
if (!pciehp_wq)
goto abort_ctrl;
}
if (pcie_init_hardware_part2(ctrl, dev))
goto abort_free_irq;
info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",
pdev->vendor, pdev->device,
pdev->subsystem_vendor, pdev->subsystem_device);
if (pcie_init_slot(ctrl))
goto abort_ctrl;
return 0;
if (pcie_init_notification(ctrl))
goto abort_slot;
abort_free_irq:
pciehp_free_irq(ctrl);
return ctrl;
abort_slot:
pcie_cleanup_slot(ctrl);
abort_ctrl:
kfree(ctrl);
abort:
return -1;
return NULL;
}
void pcie_release_ctrl(struct controller *ctrl)
{
pcie_shutdown_notification(ctrl);
pcie_cleanup_slot(ctrl);
/*
* If this is the last controller to be released, destroy the
* pciehp work queue
*/
if (atomic_dec_and_test(&pciehp_num_controllers))
destroy_workqueue(pciehp_wq);
kfree(ctrl);
}
......@@ -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;
......
......@@ -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;
......@@ -333,10 +321,11 @@ 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;
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,
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册