summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Makefile4
-rw-r--r--drivers/pci/access.c14
-rw-r--r--drivers/pci/dma_remapping.h157
-rw-r--r--drivers/pci/dmar.c401
-rw-r--r--drivers/pci/hotplug/acpi_pcihp.c99
-rw-r--r--drivers/pci/hotplug/acpiphp.h5
-rw-r--r--drivers/pci/hotplug/acpiphp_core.c25
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c40
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c6
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_core.c2
-rw-r--r--drivers/pci/hotplug/cpqphp_core.c4
-rw-r--r--drivers/pci/hotplug/fakephp.c86
-rw-r--r--drivers/pci/hotplug/ibmphp_ebda.c3
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c284
-rw-r--r--drivers/pci/hotplug/pciehp.h15
-rw-r--r--drivers/pci/hotplug/pciehp_core.c148
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c311
-rw-r--r--drivers/pci/hotplug/rpadlpar_sysfs.c5
-rw-r--r--drivers/pci/hotplug/rpaphp_slot.c44
-rw-r--r--drivers/pci/hotplug/sgi_hotplug.c12
-rw-r--r--drivers/pci/hotplug/shpchp.h14
-rw-r--r--drivers/pci/hotplug/shpchp_core.c69
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c1
-rw-r--r--drivers/pci/intel-iommu.c218
-rw-r--r--drivers/pci/intel-iommu.h233
-rw-r--r--drivers/pci/intr_remapping.c471
-rw-r--r--drivers/pci/intr_remapping.h8
-rw-r--r--drivers/pci/msi.c38
-rw-r--r--drivers/pci/pci-acpi.c284
-rw-r--r--drivers/pci/pci-driver.c388
-rw-r--r--drivers/pci/pci-sysfs.c23
-rw-r--r--drivers/pci/pci.c502
-rw-r--r--drivers/pci/pci.h50
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c9
-rw-r--r--drivers/pci/pcie/aer/aerdrv_acpi.c15
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c24
-rw-r--r--drivers/pci/pcie/aspm.c32
-rw-r--r--drivers/pci/pcie/portdrv_bus.c1
-rw-r--r--drivers/pci/pcie/portdrv_core.c22
-rw-r--r--drivers/pci/pcie/portdrv_pci.c5
-rw-r--r--drivers/pci/probe.c348
-rw-r--r--drivers/pci/proc.c22
-rw-r--r--drivers/pci/quirks.c195
-rw-r--r--drivers/pci/search.c8
-rw-r--r--drivers/pci/setup-bus.c85
-rw-r--r--drivers/pci/setup-irq.c3
-rw-r--r--drivers/pci/setup-res.c70
-rw-r--r--drivers/pci/slot.c233
48 files changed, 3406 insertions, 1630 deletions
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 4d1ce2e7361e..4b47f4ece5b7 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -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
@@ -26,6 +26,8 @@ obj-$(CONFIG_HT_IRQ) += htirq.o
# Build Intel IOMMU support
obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o
+obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o
+
#
# Some architectures use the generic PCI setup functions
#
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index ec8f7002b09d..39bb96b413ef 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -178,8 +178,7 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size,
int ret;
int begin, end, i;
- if (pos < 0 || pos > PCI_VPD_PCI22_SIZE ||
- size > PCI_VPD_PCI22_SIZE - pos)
+ if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos)
return -EINVAL;
if (size == 0)
return 0;
@@ -223,8 +222,8 @@ static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size,
u32 val;
int ret;
- if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || pos & 3 ||
- size > PCI_VPD_PCI22_SIZE - pos || size < 4)
+ if (pos < 0 || pos > vpd->base.len || pos & 3 ||
+ size > vpd->base.len - pos || size < 4)
return -EINVAL;
val = (u8) *buf++;
@@ -255,11 +254,6 @@ out:
return 4;
}
-static int pci_vpd_pci22_get_size(struct pci_dev *dev)
-{
- return PCI_VPD_PCI22_SIZE;
-}
-
static void pci_vpd_pci22_release(struct pci_dev *dev)
{
kfree(container_of(dev->vpd, struct pci_vpd_pci22, base));
@@ -268,7 +262,6 @@ static void pci_vpd_pci22_release(struct pci_dev *dev)
static struct pci_vpd_ops pci_vpd_pci22_ops = {
.read = pci_vpd_pci22_read,
.write = pci_vpd_pci22_write,
- .get_size = pci_vpd_pci22_get_size,
.release = pci_vpd_pci22_release,
};
@@ -284,6 +277,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev)
if (!vpd)
return -ENOMEM;
+ vpd->base.len = PCI_VPD_PCI22_SIZE;
vpd->base.ops = &pci_vpd_pci22_ops;
spin_lock_init(&vpd->lock);
vpd->cap = cap;
diff --git a/drivers/pci/dma_remapping.h b/drivers/pci/dma_remapping.h
new file mode 100644
index 000000000000..bff5c65f81dc
--- /dev/null
+++ b/drivers/pci/dma_remapping.h
@@ -0,0 +1,157 @@
+#ifndef _DMA_REMAPPING_H
+#define _DMA_REMAPPING_H
+
+/*
+ * We need a fixed PAGE_SIZE of 4K irrespective of
+ * arch PAGE_SIZE for IOMMU page tables.
+ */
+#define PAGE_SHIFT_4K (12)
+#define PAGE_SIZE_4K (1UL << PAGE_SHIFT_4K)
+#define PAGE_MASK_4K (((u64)-1) << PAGE_SHIFT_4K)
+#define PAGE_ALIGN_4K(addr) (((addr) + PAGE_SIZE_4K - 1) & PAGE_MASK_4K)
+
+#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT_4K)
+#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK)
+#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK)
+
+
+/*
+ * 0: Present
+ * 1-11: Reserved
+ * 12-63: Context Ptr (12 - (haw-1))
+ * 64-127: Reserved
+ */
+struct root_entry {
+ u64 val;
+ u64 rsvd1;
+};
+#define ROOT_ENTRY_NR (PAGE_SIZE_4K/sizeof(struct root_entry))
+static inline bool root_present(struct root_entry *root)
+{
+ return (root->val & 1);
+}
+static inline void set_root_present(struct root_entry *root)
+{
+ root->val |= 1;
+}
+static inline void set_root_value(struct root_entry *root, unsigned long value)
+{
+ root->val |= value & PAGE_MASK_4K;
+}
+
+struct context_entry;
+static inline struct context_entry *
+get_context_addr_from_root(struct root_entry *root)
+{
+ return (struct context_entry *)
+ (root_present(root)?phys_to_virt(
+ root->val & PAGE_MASK_4K):
+ NULL);
+}
+
+/*
+ * low 64 bits:
+ * 0: present
+ * 1: fault processing disable
+ * 2-3: translation type
+ * 12-63: address space root
+ * high 64 bits:
+ * 0-2: address width
+ * 3-6: aval
+ * 8-23: domain id
+ */
+struct context_entry {
+ u64 lo;
+ u64 hi;
+};
+#define context_present(c) ((c).lo & 1)
+#define context_fault_disable(c) (((c).lo >> 1) & 1)
+#define context_translation_type(c) (((c).lo >> 2) & 3)
+#define context_address_root(c) ((c).lo & PAGE_MASK_4K)
+#define context_address_width(c) ((c).hi & 7)
+#define context_domain_id(c) (((c).hi >> 8) & ((1 << 16) - 1))
+
+#define context_set_present(c) do {(c).lo |= 1;} while (0)
+#define context_set_fault_enable(c) \
+ do {(c).lo &= (((u64)-1) << 2) | 1;} while (0)
+#define context_set_translation_type(c, val) \
+ do { \
+ (c).lo &= (((u64)-1) << 4) | 3; \
+ (c).lo |= ((val) & 3) << 2; \
+ } while (0)
+#define CONTEXT_TT_MULTI_LEVEL 0
+#define context_set_address_root(c, val) \
+ do {(c).lo |= (val) & PAGE_MASK_4K;} while (0)
+#define context_set_address_width(c, val) do {(c).hi |= (val) & 7;} while (0)
+#define context_set_domain_id(c, val) \
+ do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0)
+#define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0)
+
+/*
+ * 0: readable
+ * 1: writable
+ * 2-6: reserved
+ * 7: super page
+ * 8-11: available
+ * 12-63: Host physcial address
+ */
+struct dma_pte {
+ u64 val;
+};
+#define dma_clear_pte(p) do {(p).val = 0;} while (0)
+
+#define DMA_PTE_READ (1)
+#define DMA_PTE_WRITE (2)
+
+#define dma_set_pte_readable(p) do {(p).val |= DMA_PTE_READ;} while (0)
+#define dma_set_pte_writable(p) do {(p).val |= DMA_PTE_WRITE;} while (0)
+#define dma_set_pte_prot(p, prot) \
+ do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0)
+#define dma_pte_addr(p) ((p).val & PAGE_MASK_4K)
+#define dma_set_pte_addr(p, addr) do {\
+ (p).val |= ((addr) & PAGE_MASK_4K); } while (0)
+#define dma_pte_present(p) (((p).val & 3) != 0)
+
+struct intel_iommu;
+
+struct dmar_domain {
+ int id; /* domain id */
+ struct intel_iommu *iommu; /* back pointer to owning iommu */
+
+ struct list_head devices; /* all devices' list */
+ struct iova_domain iovad; /* iova's that belong to this domain */
+
+ struct dma_pte *pgd; /* virtual address */
+ spinlock_t mapping_lock; /* page table lock */
+ int gaw; /* max guest address width */
+
+ /* adjusted guest address width, 0 is level 2 30-bit */
+ int agaw;
+
+#define DOMAIN_FLAG_MULTIPLE_DEVICES 1
+ int flags;
+};
+
+/* PCI domain-device relationship */
+struct device_domain_info {
+ struct list_head link; /* link to domain siblings */
+ struct list_head global; /* link to global list */
+ u8 bus; /* PCI bus numer */
+ u8 devfn; /* PCI devfn number */
+ struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */
+ struct dmar_domain *domain; /* pointer to domain */
+};
+
+extern int init_dmars(void);
+extern void free_dmar_iommu(struct intel_iommu *iommu);
+
+extern int dmar_disabled;
+
+#ifndef CONFIG_DMAR_GFX_WA
+static inline void iommu_prepare_gfx_mapping(void)
+{
+ return;
+}
+#endif /* !CONFIG_DMAR_GFX_WA */
+
+#endif
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c
index f941f609dbf3..bd2c01674f5e 100644
--- a/drivers/pci/dmar.c
+++ b/drivers/pci/dmar.c
@@ -19,13 +19,16 @@
* Author: Shaohua Li <shaohua.li@intel.com>
* Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
*
- * This file implements early detection/parsing of DMA Remapping Devices
+ * This file implements early detection/parsing of Remapping Devices
* reported to OS through BIOS via DMA remapping reporting (DMAR) ACPI
* tables.
+ *
+ * These routines are used by both DMA-remapping and Interrupt-remapping
*/
#include <linux/pci.h>
#include <linux/dmar.h>
+#include <linux/timer.h>
#include "iova.h"
#include "intel-iommu.h"
@@ -37,7 +40,6 @@
* these units are not supported by the architecture.
*/
LIST_HEAD(dmar_drhd_units);
-LIST_HEAD(dmar_rmrr_units);
static struct acpi_table_header * __initdata dmar_tbl;
@@ -53,11 +55,6 @@ static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
list_add(&drhd->list, &dmar_drhd_units);
}
-static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr)
-{
- list_add(&rmrr->list, &dmar_rmrr_units);
-}
-
static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
struct pci_dev **dev, u16 segment)
{
@@ -172,19 +169,37 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
struct acpi_dmar_hardware_unit *drhd;
struct dmar_drhd_unit *dmaru;
int ret = 0;
- static int include_all;
dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL);
if (!dmaru)
return -ENOMEM;
+ dmaru->hdr = header;
drhd = (struct acpi_dmar_hardware_unit *)header;
dmaru->reg_base_addr = drhd->address;
dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
+ ret = alloc_iommu(dmaru);
+ if (ret) {
+ kfree(dmaru);
+ return ret;
+ }
+ dmar_register_drhd_unit(dmaru);
+ return 0;
+}
+
+static int __init
+dmar_parse_dev(struct dmar_drhd_unit *dmaru)
+{
+ struct acpi_dmar_hardware_unit *drhd;
+ static int include_all;
+ int ret;
+
+ drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr;
+
if (!dmaru->include_all)
ret = dmar_parse_dev_scope((void *)(drhd + 1),
- ((void *)drhd) + header->length,
+ ((void *)drhd) + drhd->header.length,
&dmaru->devices_cnt, &dmaru->devices,
drhd->segment);
else {
@@ -197,37 +212,59 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
include_all = 1;
}
- if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all))
+ if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all)) {
+ list_del(&dmaru->list);
kfree(dmaru);
- else
- dmar_register_drhd_unit(dmaru);
+ }
return ret;
}
+#ifdef CONFIG_DMAR
+LIST_HEAD(dmar_rmrr_units);
+
+static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr)
+{
+ list_add(&rmrr->list, &dmar_rmrr_units);
+}
+
+
static int __init
dmar_parse_one_rmrr(struct acpi_dmar_header *header)
{
struct acpi_dmar_reserved_memory *rmrr;
struct dmar_rmrr_unit *rmrru;
- int ret = 0;
rmrru = kzalloc(sizeof(*rmrru), GFP_KERNEL);
if (!rmrru)
return -ENOMEM;
+ rmrru->hdr = header;
rmrr = (struct acpi_dmar_reserved_memory *)header;
rmrru->base_address = rmrr->base_address;
rmrru->end_address = rmrr->end_address;
+
+ dmar_register_rmrr_unit(rmrru);
+ return 0;
+}
+
+static int __init
+rmrr_parse_dev(struct dmar_rmrr_unit *rmrru)
+{
+ struct acpi_dmar_reserved_memory *rmrr;
+ int ret;
+
+ rmrr = (struct acpi_dmar_reserved_memory *) rmrru->hdr;
ret = dmar_parse_dev_scope((void *)(rmrr + 1),
- ((void *)rmrr) + header->length,
+ ((void *)rmrr) + rmrr->header.length,
&rmrru->devices_cnt, &rmrru->devices, rmrr->segment);
- if (ret || (rmrru->devices_cnt == 0))
+ if (ret || (rmrru->devices_cnt == 0)) {
+ list_del(&rmrru->list);
kfree(rmrru);
- else
- dmar_register_rmrr_unit(rmrru);
+ }
return ret;
}
+#endif
static void __init
dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
@@ -252,6 +289,7 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
}
}
+
/**
* parse_dmar_table - parses the DMA reporting table
*/
@@ -284,7 +322,9 @@ parse_dmar_table(void)
ret = dmar_parse_one_drhd(entry_header);
break;
case ACPI_DMAR_TYPE_RESERVED_MEMORY:
+#ifdef CONFIG_DMAR
ret = dmar_parse_one_rmrr(entry_header);
+#endif
break;
default:
printk(KERN_WARNING PREFIX
@@ -300,15 +340,77 @@ parse_dmar_table(void)
return ret;
}
+int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
+ struct pci_dev *dev)
+{
+ int index;
+
+ while (dev) {
+ for (index = 0; index < cnt; index++)
+ if (dev == devices[index])
+ return 1;
-int __init dmar_table_init(void)
+ /* Check our parent */
+ dev = dev->bus->self;
+ }
+
+ return 0;
+}
+
+struct dmar_drhd_unit *
+dmar_find_matched_drhd_unit(struct pci_dev *dev)
{
+ struct dmar_drhd_unit *drhd = NULL;
+ list_for_each_entry(drhd, &dmar_drhd_units, list) {
+ if (drhd->include_all || dmar_pci_device_match(drhd->devices,
+ drhd->devices_cnt, dev))
+ return drhd;
+ }
+
+ return NULL;
+}
+
+int __init dmar_dev_scope_init(void)
+{
+ struct dmar_drhd_unit *drhd;
+ int ret = -ENODEV;
+
+ for_each_drhd_unit(drhd) {
+ ret = dmar_parse_dev(drhd);
+ if (ret)
+ return ret;
+ }
+
+#ifdef CONFIG_DMAR
+ {
+ struct dmar_rmrr_unit *rmrr;
+ for_each_rmrr_units(rmrr) {
+ ret = rmrr_parse_dev(rmrr);
+ if (ret)
+ return ret;
+ }
+ }
+#endif
+
+ return ret;
+}
+
+
+int __init dmar_table_init(void)
+{
+ static int dmar_table_initialized;
int ret;
+ if (dmar_table_initialized)
+ return 0;
+
+ dmar_table_initialized = 1;
+
ret = parse_dmar_table();
if (ret) {
- printk(KERN_INFO PREFIX "parse DMAR table failure.\n");
+ if (ret != -ENODEV)
+ printk(KERN_INFO PREFIX "parse DMAR table failure.\n");
return ret;
}
@@ -317,11 +419,14 @@ int __init dmar_table_init(void)
return -ENODEV;
}
- if (list_empty(&dmar_rmrr_units)) {
+#ifdef CONFIG_DMAR
+ if (list_empty(&dmar_rmrr_units))
printk(KERN_INFO PREFIX "No RMRR found\n");
- return -ENODEV;
- }
+#endif
+#ifdef CONFIG_INTR_REMAP
+ parse_ioapics_under_ir();
+#endif
return 0;
}
@@ -343,3 +448,255 @@ int __init early_dmar_detect(void)
return (ACPI_SUCCESS(status) ? 1 : 0);
}
+
+void __init detect_intel_iommu(void)
+{
+ int ret;
+
+ ret = early_dmar_detect();
+
+#ifdef CONFIG_DMAR
+ {
+ struct acpi_table_dmar *dmar;
+ /*
+ * for now we will disable dma-remapping when interrupt
+ * remapping is enabled.
+ * When support for queued invalidation for IOTLB invalidation
+ * is added, we will not need this any more.
+ */
+ dmar = (struct acpi_table_dmar *) dmar_tbl;
+ if (ret && cpu_has_x2apic && dmar->flags & 0x1) {
+ printk(KERN_INFO
+ "Queued invalidation will be enabled to support "
+ "x2apic and Intr-remapping.\n");
+ printk(KERN_INFO
+ "Disabling IOMMU detection, because of missing "
+ "queued invalidation support for IOTLB "
+ "invalidation\n");
+ printk(KERN_INFO
+ "Use \"nox2apic\", if you want to use Intel "
+ " IOMMU for DMA-remapping and don't care about "
+ " x2apic support\n");
+
+ dmar_disabled = 1;
+ return;
+ }
+
+ if (ret && !no_iommu && !iommu_detected && !swiotlb &&
+ !dmar_disabled)
+ iommu_detected = 1;
+ }
+#endif
+}
+
+
+int alloc_iommu(struct dmar_drhd_unit *drhd)
+{
+ struct intel_iommu *iommu;
+ int map_size;
+ u32 ver;
+ static int iommu_allocated = 0;
+
+ iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
+ if (!iommu)
+ return -ENOMEM;
+
+ iommu->seq_id = iommu_allocated++;
+
+ iommu->reg = ioremap(drhd->reg_base_addr, PAGE_SIZE_4K);
+ if (!iommu->reg) {
+ printk(KERN_ERR "IOMMU: can't map the region\n");
+ goto error;
+ }
+ iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG);
+ iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG);
+
+ /* the registers might be more than one page */
+ map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap),
+ cap_max_fault_reg_offset(iommu->cap));
+ map_size = PAGE_ALIGN_4K(map_size);
+ if (map_size > PAGE_SIZE_4K) {
+ iounmap(iommu->reg);
+ iommu->reg = ioremap(drhd->reg_base_addr, map_size);
+ if (!iommu->reg) {
+ printk(KERN_ERR "IOMMU: can't map the region\n");
+ goto error;
+ }
+ }
+
+ ver = readl(iommu->reg + DMAR_VER_REG);
+ pr_debug("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n",
+ drhd->reg_base_addr, DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
+ iommu->cap, iommu->ecap);
+
+ spin_lock_init(&iommu->register_lock);
+
+ drhd->iommu = iommu;
+ return 0;
+error:
+ kfree(iommu);
+ return -1;
+}
+
+void free_iommu(struct intel_iommu *iommu)
+{
+ if (!iommu)
+ return;
+
+#ifdef CONFIG_DMAR
+ free_dmar_iommu(iommu);
+#endif
+
+ if (iommu->reg)
+ iounmap(iommu->reg);
+ kfree(iommu);
+}
+
+/*
+ * Reclaim all the submitted descriptors which have completed its work.
+ */
+static inline void reclaim_free_desc(struct q_inval *qi)
+{
+ while (qi->desc_status[qi->free_tail] == QI_DONE) {
+ qi->desc_status[qi->free_tail] = QI_FREE;
+ qi->free_tail = (qi->free_tail + 1) % QI_LENGTH;
+ qi->free_cnt++;
+ }
+}
+
+/*
+ * Submit the queued invalidation descriptor to the remapping
+ * hardware unit and wait for its completion.
+ */
+void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
+{
+ struct q_inval *qi = iommu->qi;
+ struct qi_desc *hw, wait_desc;
+ int wait_index, index;
+ unsigned long flags;
+
+ if (!qi)
+ return;
+
+ hw = qi->desc;
+
+ spin_lock(&qi->q_lock);
+ while (qi->free_cnt < 3) {
+ spin_unlock(&qi->q_lock);
+ cpu_relax();
+ spin_lock(&qi->q_lock);
+ }
+
+ index = qi->free_head;
+ wait_index = (index + 1) % QI_LENGTH;
+
+ qi->desc_status[index] = qi->desc_status[wait_index] = QI_IN_USE;
+
+ hw[index] = *desc;
+
+ wait_desc.low = QI_IWD_STATUS_DATA(2) | QI_IWD_STATUS_WRITE | QI_IWD_TYPE;
+ wait_desc.high = virt_to_phys(&qi->desc_status[wait_index]);
+
+ hw[wait_index] = wait_desc;
+
+ __iommu_flush_cache(iommu, &hw[index], sizeof(struct qi_desc));
+ __iommu_flush_cache(iommu, &hw[wait_index], sizeof(struct qi_desc));
+
+ qi->free_head = (qi->free_head + 2) % QI_LENGTH;
+ qi->free_cnt -= 2;
+
+ spin_lock_irqsave(&iommu->register_lock, flags);
+ /*
+ * update the HW tail register indicating the presence of
+ * new descriptors.
+ */
+ writel(qi->free_head << 4, iommu->reg + DMAR_IQT_REG);
+ spin_unlock_irqrestore(&iommu->register_lock, flags);
+
+ while (qi->desc_status[wait_index] != QI_DONE) {
+ spin_unlock(&qi->q_lock);
+ cpu_relax();
+ spin_lock(&qi->q_lock);
+ }
+
+ qi->desc_status[index] = QI_DONE;
+
+ reclaim_free_desc(qi);
+ spin_unlock(&qi->q_lock);
+}
+
+/*
+ * Flush the global interrupt entry cache.
+ */
+void qi_global_iec(struct intel_iommu *iommu)
+{
+ struct qi_desc desc;
+
+ desc.low = QI_IEC_TYPE;
+ desc.high = 0;
+
+ qi_submit_sync(&desc, iommu);
+}
+
+/*
+ * Enable Queued Invalidation interface. This is a must to support
+ * interrupt-remapping. Also used by DMA-remapping, which replaces
+ * register based IOTLB invalidation.
+ */
+int dmar_enable_qi(struct intel_iommu *iommu)
+{
+ u32 cmd, sts;
+ unsigned long flags;
+ struct q_inval *qi;
+
+ if (!ecap_qis(iommu->ecap))
+ return -ENOENT;
+
+ /*
+ * queued invalidation is already setup and enabled.
+ */
+ if (iommu->qi)
+ return 0;
+
+ iommu->qi = kmalloc(sizeof(*qi), GFP_KERNEL);
+ if (!iommu->qi)
+ return -ENOMEM;
+
+ qi = iommu->qi;
+
+ qi->desc = (void *)(get_zeroed_page(GFP_KERNEL));
+ if (!qi->desc) {
+ kfree(qi);
+ iommu->qi = 0;
+ return -ENOMEM;
+ }
+
+ qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_KERNEL);
+ if (!qi->desc_status) {
+ free_page((unsigned long) qi->desc);
+ kfree(qi);
+ iommu->qi = 0;
+ return -ENOMEM;
+ }
+
+ qi->free_head = qi->free_tail = 0;
+ qi->free_cnt = QI_LENGTH;
+
+ spin_lock_init(&qi->q_lock);
+
+ spin_lock_irqsave(&iommu->register_lock, flags);
+ /* write zero to the tail reg */
+ writel(0, iommu->reg + DMAR_IQT_REG);
+
+ dmar_writeq(iommu->reg + DMAR_IQA_REG, virt_to_phys(qi->desc));
+
+ cmd = iommu->gcmd | DMA_GCMD_QIE;
+ iommu->gcmd |= DMA_GCMD_QIE;
+ writel(cmd, iommu->reg + DMAR_GCMD_REG);
+
+ /* Make sure hardware complete it */
+ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_QIES), sts);
+ spin_unlock_irqrestore(&iommu->register_lock, flags);
+
+ return 0;
+}
diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c
index f8c187a763bd..e17ef54f0efc 100644
--- a/drivers/pci/hotplug/acpi_pcihp.c
+++ b/drivers/pci/hotplug/acpi_pcihp.c
@@ -30,6 +30,7 @@
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
+#include <linux/pci-acpi.h>
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/actypes.h>
@@ -299,7 +300,7 @@ free_and_return:
*
* @handle - the handle of the hotplug controller.
*/
-acpi_status acpi_run_oshp(acpi_handle handle)
+static acpi_status acpi_run_oshp(acpi_handle handle)
{
acpi_status status;
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -322,9 +323,6 @@ acpi_status acpi_run_oshp(acpi_handle handle)
kfree(string.pointer);
return status;
}
-EXPORT_SYMBOL_GPL(acpi_run_oshp);
-
-
/* acpi_get_hp_params_from_firmware
*
@@ -374,6 +372,99 @@ 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;
+ 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 on a root
+ * bridge preferentially (according to PCI fw spec). Later for
+ * OSHP within the scope of the hotplug controller and its parents,
+ * upto the host bridge under which this controller exists.
+ */
+ handle = acpi_find_root_bridge_handle(pdev);
+ if (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 (ACPI_SUCCESS(status))
+ goto got_one;
+ kfree(string.pointer);
+ string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
+ }
+
+ pdev = dev;
+ handle = DEVICE_ACPI_HANDLE(&dev->dev);
+ 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 = acpi_run_oshp(handle);
+ if (ACPI_SUCCESS(status))
+ goto got_one;
+ 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;
+got_one:
+ dbg("Gained control for hotplug HW for pci %s (%s)\n", pci_name(dev),
+ (char *)string.pointer);
+ kfree(string.pointer);
+ return 0;
+}
+EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
/* acpi_root_bridge - check to see if this acpi object is a root bridge
*
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index 7a29164d4b32..5a58b075dd8d 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -36,7 +36,7 @@
#define _ACPIPHP_H
#include <linux/acpi.h>
-#include <linux/kobject.h> /* for KOBJ_NAME_LEN */
+#include <linux/kobject.h>
#include <linux/mutex.h>
#include <linux/pci_hotplug.h>
@@ -51,7 +51,7 @@
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
/* name size which is used for entries in pcihpfs */
-#define SLOT_NAME_SIZE KOBJ_NAME_LEN /* {_SUN} */
+#define SLOT_NAME_SIZE 20 /* {_SUN} */
struct acpiphp_bridge;
struct acpiphp_slot;
@@ -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;
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
index 7af68ba27903..0e496e866a84 100644
--- a/drivers/pci/hotplug/acpiphp_core.c
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -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;
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 648596d469f6..a3e4705dd8f0 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -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;
}
}
@@ -700,9 +705,10 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
cleanup_p2p_bridge, NULL, NULL);
- if (!(bridge = acpiphp_handle_to_bridge(handle)))
- return AE_OK;
- cleanup_bridge(bridge);
+ bridge = acpiphp_handle_to_bridge(handle);
+ if (bridge)
+ cleanup_bridge(bridge);
+
return AE_OK;
}
@@ -715,9 +721,19 @@ static void remove_bridge(acpi_handle handle)
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
(u32)1, cleanup_p2p_bridge, NULL, NULL);
+ /*
+ * On root bridges with hotplug slots directly underneath (ie,
+ * no p2p bridge inbetween), we call cleanup_bridge().
+ *
+ * The else clause cleans up root bridges that either had no
+ * hotplug slots at all, or had a p2p bridge underneath.
+ */
bridge = acpiphp_handle_to_bridge(handle);
if (bridge)
cleanup_bridge(bridge);
+ else
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ handle_hotplug_event_bridge);
}
static struct pci_dev * get_apic_pci_info(acpi_handle handle)
@@ -1867,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;
-}
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index ede9051fdb5d..2b7c45e39370 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -33,8 +33,10 @@
#include <linux/kobject.h>
#include <asm/uaccess.h>
#include <linux/moduleparam.h>
+#include <linux/pci.h>
#include "acpiphp.h"
+#include "../pci.h"
#define DRIVER_VERSION "1.0.1"
#define DRIVER_AUTHOR "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
@@ -430,7 +432,7 @@ static int __init ibm_acpiphp_init(void)
int retval = 0;
acpi_status status;
struct acpi_device *device;
- struct kobject *sysdir = &pci_hotplug_slots_kset->kobj;
+ struct kobject *sysdir = &pci_slots_kset->kobj;
dbg("%s\n", __func__);
@@ -477,7 +479,7 @@ init_return:
static void __exit ibm_acpiphp_exit(void)
{
acpi_status status;
- struct kobject *sysdir = &pci_hotplug_slots_kset->kobj;
+ struct kobject *sysdir = &pci_slots_kset->kobj;
dbg("%s\n", __func__);
diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
index d8a6b80ab42a..935947991dc9 100644
--- a/drivers/pci/hotplug/cpci_hotplug_core.c
+++ b/drivers/pci/hotplug/cpci_hotplug_core.c
@@ -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;
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
index 36b115b27b0b..54defec51d08 100644
--- a/drivers/pci/hotplug/cpqphp_core.c
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -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;
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index 7e9a827c2687..146ca9cd1567 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -66,6 +66,7 @@ struct dummy_slot {
struct pci_dev *dev;
struct work_struct remove_work;
unsigned long removed;
+ char name[8];
};
static int debug;
@@ -100,6 +101,7 @@ static int add_slot(struct pci_dev *dev)
struct dummy_slot *dslot;
struct hotplug_slot *slot;
int retval = -ENOMEM;
+ static int count = 1;
slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
if (!slot)
@@ -113,18 +115,18 @@ static int add_slot(struct pci_dev *dev)
slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
- slot->name = &dev->dev.bus_id[0];
- dbg("slot->name = %s\n", slot->name);
-
dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL);
if (!dslot)
goto error_info;
+ slot->name = dslot->name;
+ snprintf(slot->name, sizeof(dslot->name), "fake%d", count++);
+ dbg("slot->name = %s\n", slot->name);
slot->ops = &dummy_hotplug_slot_ops;
slot->release = &dummy_release;
slot->private = dslot;
- retval = pci_hp_register(slot);
+ retval = pci_hp_register(slot, dev->bus, PCI_SLOT(dev->devfn));
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_dslot;
@@ -148,17 +150,17 @@ error:
static int __init pci_scan_buses(void)
{
struct pci_dev *dev = NULL;
- int retval = 0;
+ int lastslot = 0;
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
- retval = add_slot(dev);
- if (retval) {
- pci_dev_put(dev);
- break;
- }
+ if (PCI_FUNC(dev->devfn) > 0 &&
+ lastslot == PCI_SLOT(dev->devfn))
+ continue;
+ lastslot = PCI_SLOT(dev->devfn);
+ add_slot(dev);
}
- return retval;
+ return 0;
}
static void remove_slot(struct dummy_slot *dslot)
@@ -296,23 +298,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
return 0;
}
-/* find the hotplug_slot for the pci_dev */
-static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev)
-{
- struct dummy_slot *dslot;
-
- list_for_each_entry(dslot, &slot_list, node) {
- if (dslot->dev == dev)
- return dslot->slot;
- }
- return NULL;
-}
-
-
static int disable_slot(struct hotplug_slot *slot)
{
struct dummy_slot *dslot;
- struct hotplug_slot *hslot;
struct pci_dev *dev;
int func;
@@ -322,41 +310,27 @@ static int disable_slot(struct hotplug_slot *slot)
dbg("%s - physical_slot = %s\n", __func__, slot->name);
- /* don't disable bridged devices just yet, we can't handle them easily... */
- if (dslot->dev->subordinate) {
- err("Can't remove PCI devices with other PCI devices behind it yet.\n");
- return -ENODEV;
- }
- if (test_and_set_bit(0, &dslot->removed)) {
- dbg("Slot already scheduled for removal\n");
- return -ENODEV;
- }
- /* search for subfunctions and disable them first */
- if (!(dslot->dev->devfn & 7)) {
- for (func = 1; func < 8; func++) {
- dev = pci_get_slot(dslot->dev->bus,
- dslot->dev->devfn + func);
- if (dev) {
- hslot = get_slot_from_dev(dev);
- if (hslot)
- disable_slot(hslot);
- else {
- err("Hotplug slot not found for subfunction of PCI device\n");
- return -ENODEV;
- }
- pci_dev_put(dev);
- } else
- dbg("No device in slot found\n");
+ for (func = 7; func >= 0; func--) {
+ dev = pci_get_slot(dslot->dev->bus, dslot->dev->devfn + func);
+ if (!dev)
+ continue;
+
+ if (test_and_set_bit(0, &dslot->removed)) {
+ dbg("Slot already scheduled for removal\n");
+ return -ENODEV;
}
- }
- /* remove the device from the pci core */
- pci_remove_bus_device(dslot->dev);
+ /* remove the device from the pci core */
+ pci_remove_bus_device(dev);
- /* queue work item to blow away this sysfs entry and other parts. */
- INIT_WORK(&dslot->remove_work, remove_slot_worker);
- queue_work(dummyphp_wq, &dslot->remove_work);
+ /* queue work item to blow away this sysfs entry and other
+ * parts.
+ */
+ INIT_WORK(&dslot->remove_work, remove_slot_worker);
+ queue_work(dummyphp_wq, &dslot->remove_work);
+ pci_dev_put(dev);
+ }
return 0;
}
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c
index dca7efc14be2..8467d0287325 100644
--- a/drivers/pci/hotplug/ibmphp_ebda.c
+++ b/drivers/pci/hotplug/ibmphp_ebda.c
@@ -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 ();
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index a11021e8ce37..5f85b1b120e3 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -40,6 +40,7 @@
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <asm/uaccess.h>
+#include "../pci.h"
#define MY_NAME "pci_hotplug"
@@ -60,41 +61,7 @@ static int debug;
//////////////////////////////////////////////////////////////////
static LIST_HEAD(pci_hotplug_slot_list);
-
-struct kset *pci_hotplug_slots_kset;
-
-static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
- return attribute->show ? attribute->show(slot, buf) : -EIO;
-}
-
-static ssize_t hotplug_slot_attr_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t len)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
- return attribute->store ? attribute->store(slot, buf, len) : -EIO;
-}
-
-static struct sysfs_ops hotplug_slot_sysfs_ops = {
- .show = hotplug_slot_attr_show,
- .store = hotplug_slot_attr_store,
-};
-
-static void hotplug_slot_release(struct kobject *kobj)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- if (slot->release)
- slot->release(slot);
-}
-
-static struct kobj_type hotplug_slot_ktype = {
- .sysfs_ops = &hotplug_slot_sysfs_ops,
- .release = &hotplug_slot_release,
-};
+static DEFINE_SPINLOCK(pci_hotplug_slot_list_lock);
/* these strings match up with the values in pci_bus_speed */
static char *pci_bus_speed_strings[] = {
@@ -149,16 +116,15 @@ GET_STATUS(power_status, u8)
GET_STATUS(attention_status, u8)
GET_STATUS(latch_status, u8)
GET_STATUS(adapter_status, u8)
-GET_STATUS(address, u32)
GET_STATUS(max_bus_speed, enum pci_bus_speed)
GET_STATUS(cur_bus_speed, enum pci_bus_speed)
-static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t power_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;
- retval = get_power_status (slot, &value);
+ retval = get_power_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -166,9 +132,10 @@ exit:
return retval;
}
-static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
size_t count)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
unsigned long lpower;
u8 power;
int retval = 0;
@@ -204,29 +171,30 @@ exit:
return count;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_power = {
+static struct pci_slot_attribute hotplug_slot_attr_power = {
.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = power_read_file,
.store = power_write_file
};
-static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t attention_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;
- retval = get_attention_status (slot, &value);
+ retval = get_attention_status(slot->hotplug, &value);
if (retval)
goto exit;
- retval = sprintf (buf, "%d\n", value);
+ retval = sprintf(buf, "%d\n", value);
exit:
return retval;
}
-static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
size_t count)
{
+ struct hotplug_slot_ops *ops = slot->hotplug->ops;
unsigned long lattention;
u8 attention;
int retval = 0;
@@ -235,13 +203,13 @@ static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
attention = (u8)(lattention & 0xff);
dbg (" - attention = %d\n", attention);
- if (!try_module_get(slot->ops->owner)) {
+ if (!try_module_get(ops->owner)) {
retval = -ENODEV;
goto exit;
}
- if (slot->ops->set_attention_status)
- retval = slot->ops->set_attention_status(slot, attention);
- module_put(slot->ops->owner);
+ if (ops->set_attention_status)
+ retval = ops->set_attention_status(slot->hotplug, attention);
+ module_put(ops->owner);
exit:
if (retval)
@@ -249,18 +217,18 @@ exit:
return count;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_attention = {
+static struct pci_slot_attribute hotplug_slot_attr_attention = {
.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = attention_read_file,
.store = attention_write_file
};
-static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t latch_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;
- retval = get_latch_status (slot, &value);
+ retval = get_latch_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -269,17 +237,17 @@ exit:
return retval;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
+static struct pci_slot_attribute hotplug_slot_attr_latch = {
.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
.show = latch_read_file,
};
-static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t presence_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;
- retval = get_adapter_status (slot, &value);
+ retval = get_adapter_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -288,42 +256,20 @@ exit:
return retval;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
+static struct pci_slot_attribute hotplug_slot_attr_presence = {
.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
.show = presence_read_file,
};
-static ssize_t address_read_file (struct hotplug_slot *slot, char *buf)
-{
- int retval;
- u32 address;
-
- retval = get_address (slot, &address);
- if (retval)
- goto exit;
- retval = sprintf (buf, "%04x:%02x:%02x\n",
- (address >> 16) & 0xffff,
- (address >> 8) & 0xff,
- address & 0xff);
-
-exit:
- return retval;
-}
-
-static struct hotplug_slot_attribute hotplug_slot_attr_address = {
- .attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
- .show = address_read_file,
-};
-
static char *unknown_speed = "Unknown bus speed";
-static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;
- retval = get_max_bus_speed (slot, &value);
+ retval = get_max_bus_speed(slot->hotplug, &value);
if (retval)
goto exit;
@@ -338,18 +284,18 @@ exit:
return retval;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = {
.attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = max_bus_speed_read_file,
};
-static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;
- retval = get_cur_bus_speed (slot, &value);
+ retval = get_cur_bus_speed(slot->hotplug, &value);
if (retval)
goto exit;
@@ -364,14 +310,15 @@ exit:
return retval;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = {
.attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = cur_bus_speed_read_file,
};
-static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
size_t count)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
unsigned long ltest;
u32 test;
int retval = 0;
@@ -394,13 +341,14 @@ exit:
return count;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_test = {
+static struct pci_slot_attribute hotplug_slot_attr_test = {
.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.store = test_write_file
};
-static int has_power_file (struct hotplug_slot *slot)
+static int has_power_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->enable_slot) ||
@@ -410,8 +358,9 @@ static int has_power_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_attention_file (struct hotplug_slot *slot)
+static int has_attention_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->set_attention_status) ||
@@ -420,8 +369,9 @@ static int has_attention_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_latch_file (struct hotplug_slot *slot)
+static int has_latch_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_latch_status)
@@ -429,8 +379,9 @@ static int has_latch_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_adapter_file (struct hotplug_slot *slot)
+static int has_adapter_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_adapter_status)
@@ -438,17 +389,9 @@ static int has_adapter_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_address_file (struct hotplug_slot *slot)
-{
- if ((!slot) || (!slot->ops))
- return -ENODEV;
- if (slot->ops->get_address)
- return 0;
- return -ENOENT;
-}
-
-static int has_max_bus_speed_file (struct hotplug_slot *slot)
+static int has_max_bus_speed_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_max_bus_speed)
@@ -456,8 +399,9 @@ static int has_max_bus_speed_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_cur_bus_speed_file (struct hotplug_slot *slot)
+static int has_cur_bus_speed_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_cur_bus_speed)
@@ -465,8 +409,9 @@ static int has_cur_bus_speed_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_test_file (struct hotplug_slot *slot)
+static int has_test_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->hardware_test)
@@ -474,7 +419,7 @@ static int has_test_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int fs_add_slot (struct hotplug_slot *slot)
+static int fs_add_slot(struct pci_slot *slot)
{
int retval = 0;
@@ -505,13 +450,6 @@ static int fs_add_slot (struct hotplug_slot *slot)
goto exit_adapter;
}
- if (has_address_file(slot) == 0) {
- retval = sysfs_create_file(&slot->kobj,
- &hotplug_slot_attr_address.attr);
- if (retval)
- goto exit_address;
- }
-
if (has_max_bus_speed_file(slot) == 0) {
retval = sysfs_create_file(&slot->kobj,
&hotplug_slot_attr_max_bus_speed.attr);
@@ -544,10 +482,6 @@ exit_cur_speed:
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
exit_max_speed:
- if (has_address_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
-exit_address:
if (has_adapter_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
@@ -567,7 +501,7 @@ exit:
return retval;
}
-static void fs_remove_slot (struct hotplug_slot *slot)
+static void fs_remove_slot(struct pci_slot *slot)
{
if (has_power_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
@@ -581,9 +515,6 @@ static void fs_remove_slot (struct hotplug_slot *slot)
if (has_adapter_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
- if (has_address_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
if (has_max_bus_speed_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
@@ -599,27 +530,33 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
struct hotplug_slot *slot;
struct list_head *tmp;
+ spin_lock(&pci_hotplug_slot_list_lock);
list_for_each (tmp, &pci_hotplug_slot_list) {
slot = list_entry (tmp, struct hotplug_slot, slot_list);
if (strcmp(slot->name, name) == 0)
- return slot;
+ goto out;
}
- return NULL;
+ slot = NULL;
+out:
+ spin_unlock(&pci_hotplug_slot_list_lock);
+ return slot;
}
/**
* pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
+ * @bus: bus this slot is on
* @slot: pointer to the &struct hotplug_slot to register
+ * @slot_nr: slot number
*
* Registers a hotplug slot with the pci hotplug subsystem, which will allow
* userspace interaction to the slot.
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_register (struct hotplug_slot *slot)
+int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr)
{
int result;
- struct hotplug_slot *tmp;
+ struct pci_slot *pci_slot;
if (slot == NULL)
return -ENODEV;
@@ -632,57 +569,89 @@ int pci_hp_register (struct hotplug_slot *slot)
}
/* Check if we have already registered a slot with the same name. */
- tmp = get_slot_from_name(slot->name);
- if (tmp)
+ if (get_slot_from_name(slot->name))
return -EEXIST;
- slot->kobj.kset = pci_hotplug_slots_kset;
- result = kobject_init_and_add(&slot->kobj, &hotplug_slot_ktype, NULL,
- "%s", slot->name);
- if (result) {
- err("Unable to register kobject '%s'", slot->name);
- return -EINVAL;
+ /*
+ * No problems if we call this interface from both ACPI_PCI_SLOT
+ * driver and call it here again. If we've already created the
+ * pci_slot, the interface will simply bump the refcount.
+ */
+ pci_slot = pci_create_slot(bus, slot_nr, slot->name);
+ if (IS_ERR(pci_slot))
+ return PTR_ERR(pci_slot);
+
+ if (pci_slot->hotplug) {
+ dbg("%s: already claimed\n", __func__);
+ pci_destroy_slot(pci_slot);
+ return -EBUSY;
}
- list_add (&slot->slot_list, &pci_hotplug_slot_list);
+ slot->pci_slot = pci_slot;
+ pci_slot->hotplug = slot;
+
+ /*
+ * Allow pcihp drivers to override the ACPI_PCI_SLOT name.
+ */
+ if (strcmp(kobject_name(&pci_slot->kobj), slot->name)) {
+ result = kobject_rename(&pci_slot->kobj, slot->name);
+ if (result) {
+ pci_destroy_slot(pci_slot);
+ return result;
+ }
+ }
+
+ spin_lock(&pci_hotplug_slot_list_lock);
+ list_add(&slot->slot_list, &pci_hotplug_slot_list);
+ spin_unlock(&pci_hotplug_slot_list_lock);
+
+ result = fs_add_slot(pci_slot);
+ kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
+ dbg("Added slot %s to the list\n", slot->name);
+
- result = fs_add_slot (slot);
- kobject_uevent(&slot->kobj, KOBJ_ADD);
- dbg ("Added slot %s to the list\n", slot->name);
return result;
}
/**
* pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
- * @slot: pointer to the &struct hotplug_slot to deregister
+ * @hotplug: pointer to the &struct hotplug_slot to deregister
*
* The @slot must have been registered with the pci hotplug subsystem
* previously with a call to pci_hp_register().
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_deregister (struct hotplug_slot *slot)
+int pci_hp_deregister(struct hotplug_slot *hotplug)
{
struct hotplug_slot *temp;
+ struct pci_slot *slot;
- if (slot == NULL)
+ if (!hotplug)
return -ENODEV;
- temp = get_slot_from_name (slot->name);
- if (temp != slot) {
+ temp = get_slot_from_name(hotplug->name);
+ if (temp != hotplug)
return -ENODEV;
- }
- list_del (&slot->slot_list);
- fs_remove_slot (slot);
- dbg ("Removed slot %s from the list\n", slot->name);
- kobject_put(&slot->kobj);
+ spin_lock(&pci_hotplug_slot_list_lock);
+ list_del(&hotplug->slot_list);
+ spin_unlock(&pci_hotplug_slot_list_lock);
+
+ slot = hotplug->pci_slot;
+ fs_remove_slot(slot);
+ dbg("Removed slot %s from the list\n", hotplug->name);
+
+ hotplug->release(hotplug);
+ slot->hotplug = NULL;
+ pci_destroy_slot(slot);
+
return 0;
}
/**
* pci_hp_change_slot_info - changes the slot's information structure in the core
- * @slot: pointer to the slot whose info has changed
+ * @hotplug: pointer to the slot whose info has changed
* @info: pointer to the info copy into the slot's info structure
*
* @slot must have been registered with the pci
@@ -690,13 +659,15 @@ int pci_hp_deregister (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
+int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug,
struct hotplug_slot_info *info)
{
- if ((slot == NULL) || (info == NULL))
+ struct pci_slot *slot;
+ if (!hotplug || !info)
return -ENODEV;
+ slot = hotplug->pci_slot;
- memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
+ memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));
return 0;
}
@@ -704,36 +675,22 @@ int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
static int __init pci_hotplug_init (void)
{
int result;
- struct kset *pci_bus_kset;
- pci_bus_kset = bus_get_kset(&pci_bus_type);
-
- pci_hotplug_slots_kset = kset_create_and_add("slots", NULL,
- &pci_bus_kset->kobj);
- if (!pci_hotplug_slots_kset) {
- result = -ENOMEM;
- err("Register subsys error\n");
- goto exit;
- }
result = cpci_hotplug_init(debug);
if (result) {
err ("cpci_hotplug_init with error %d\n", result);
- goto err_subsys;
+ goto err_cpci;
}
info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
- goto exit;
-err_subsys:
- kset_unregister(pci_hotplug_slots_kset);
-exit:
+err_cpci:
return result;
}
static void __exit pci_hotplug_exit (void)
{
cpci_hotplug_exit();
- kset_unregister(pci_hotplug_slots_kset);
}
module_init(pci_hotplug_init);
@@ -745,7 +702,6 @@ MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
-EXPORT_SYMBOL_GPL(pci_hotplug_slots_kset);
EXPORT_SYMBOL_GPL(pci_hp_register);
EXPORT_SYMBOL_GPL(pci_hp_deregister);
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 79c9ddaad3fb..9e6cec67e1cc 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -96,7 +96,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 +156,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 +202,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)
{
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 48a2ed378914..4fd5355bc3b5 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -41,7 +41,6 @@ int pciehp_debug;
int pciehp_poll_mode;
int pciehp_poll_time;
int pciehp_force;
-int pciehp_slot_with_bus;
struct workqueue_struct *pciehp_wq;
#define DRIVER_VERSION "0.4"
@@ -56,12 +55,10 @@ module_param(pciehp_debug, bool, 0644);
module_param(pciehp_poll_mode, bool, 0644);
module_param(pciehp_poll_time, int, 0644);
module_param(pciehp_force, bool, 0644);
-module_param(pciehp_slot_with_bus, bool, 0644);
MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing");
-MODULE_PARM_DESC(pciehp_slot_with_bus, "Use bus number in the slot name");
#define PCIE_MODULE_NAME "pciehp"
@@ -72,7 +69,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 +81,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 +180,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)
@@ -209,61 +191,56 @@ static int init_slots(struct controller *ctrl)
struct slot *slot;
struct hotplug_slot *hotplug_slot;
struct hotplug_slot_info *info;
+ int len, dup = 1;
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);
+duplicate_name:
+ retval = pci_hp_register(hotplug_slot,
+ ctrl->pci_dev->subordinate,
+ slot->device);
if (retval) {
+ /*
+ * If slot N already exists, we'll try to create
+ * slot N-1, N-2 ... N-M, until we overflow.
+ */
+ if (retval == -EEXIST) {
+ len = snprintf(slot->name, SLOT_NAME_SIZE,
+ "%d-%d", slot->number, dup++);
+ if (len < SLOT_NAME_SIZE)
+ goto duplicate_name;
+ else
+ err("duplicate slot name overflow\n");
+ }
err("pci_hp_register failed with error %d\n", retval);
- if (retval == -EEXIST)
- err("Failed to register slot because of name "
- "collision. Try \'pciehp_slot_with_bus\' "
- "module option.\n");
goto error_info;
}
/* 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 +248,6 @@ static int init_slots(struct controller *ctrl)
goto error_info;
}
}
-
- list_add(&slot->slot_list, &ctrl->slot_list);
}
return 0;
@@ -280,27 +255,18 @@ error_info:
kfree(info);
error_hpslot:
kfree(hotplug_slot);
-error_slot:
- kfree(slot);
error:
return retval;
}
static void cleanup_slots(struct controller *ctrl)
{
- struct list_head *tmp;
- struct list_head *next;
struct slot *slot;
- list_for_each_safe(tmp, next, &ctrl->slot_list) {
- slot = list_entry(tmp, struct slot, slot_list);
- list_del(&slot->slot_list);
+ list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
if (EMI(ctrl))
- sysfs_remove_file(&slot->hotplug_slot->kobj,
+ sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj,
&hotplug_slot_attr_lock.attr);
- cancel_delayed_work(&slot->work);
- flush_scheduled_work();
- flush_workqueue(pciehp_wq);
pci_hp_deregister(slot->hotplug_slot);
}
}
@@ -398,19 +364,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 +399,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 +446,16 @@ err_out_free_ctrl_slot:
cleanup_slots(ctrl);
err_out_release_ctlr:
ctrl->hpc_ops->release_ctlr(ctrl);
-err_out_free_ctrl:
- kfree(ctrl);
err_out_none:
return -ENODEV;
}
static void pciehp_remove (struct pcie_device *dev)
{
- struct pci_dev *pdev = dev->port;
- struct controller *ctrl = pci_get_drvdata(pdev);
+ struct controller *ctrl = get_service_data(dev);
cleanup_slots(ctrl);
ctrl->hpc_ops->release_ctlr(ctrl);
- kfree(ctrl);
}
#ifdef CONFIG_PM
@@ -522,13 +469,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);
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 79f104963166..9d934ddee956 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -247,30 +247,32 @@ static inline void pciehp_free_irq(struct controller *ctrl)
free_irq(ctrl->pci_dev->irq, ctrl);
}
-static inline int pcie_poll_cmd(struct controller *ctrl)
+static int pcie_poll_cmd(struct controller *ctrl)
{
u16 slot_status;
int timeout = 1000;
- if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status))
- if (slot_status & CMD_COMPLETED)
- goto completed;
- for (timeout = 1000; timeout > 0; timeout -= 100) {
- msleep(100);
- if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status))
- if (slot_status & CMD_COMPLETED)
- goto completed;
+ if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) {
+ if (slot_status & CMD_COMPLETED) {
+ pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED);
+ return 1;
+ }
+ }
+ while (timeout > 0) {
+ msleep(10);
+ timeout -= 10;
+ if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) {
+ if (slot_status & CMD_COMPLETED) {
+ pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED);
+ return 1;
+ }
+ }
}
return 0; /* timeout */
-
-completed:
- pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED);
- return timeout;
}
-static inline int pcie_wait_cmd(struct controller *ctrl, int poll)
+static void pcie_wait_cmd(struct controller *ctrl, int poll)
{
- int retval = 0;
unsigned int msecs = pciehp_poll_mode ? 2500 : 1000;
unsigned long timeout = msecs_to_jiffies(msecs);
int rc;
@@ -278,16 +280,9 @@ static inline int pcie_wait_cmd(struct controller *ctrl, int poll)
if (poll)
rc = pcie_poll_cmd(ctrl);
else
- rc = wait_event_interruptible_timeout(ctrl->queue,
- !ctrl->cmd_busy, timeout);
+ rc = wait_event_timeout(ctrl->queue, !ctrl->cmd_busy, timeout);
if (!rc)
dbg("Command not completed in 1000 msec\n");
- else if (rc < 0) {
- retval = -EINTR;
- info("Command was interrupted by a signal\n");
- }
-
- return retval;
}
/**
@@ -342,10 +337,6 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
slot_ctrl &= ~mask;
slot_ctrl |= (cmd & mask);
- /* Don't enable command completed if caller is changing it. */
- if (!(mask & CMD_CMPL_INTR_ENABLE))
- slot_ctrl |= CMD_CMPL_INTR_ENABLE;
-
ctrl->cmd_busy = 1;
smp_mb();
retval = pciehp_writew(ctrl, SLOTCTRL, slot_ctrl);
@@ -365,7 +356,7 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
if (!(slot_ctrl & HP_INTR_ENABLE) ||
!(slot_ctrl & CMD_CMPL_INTR_ENABLE))
poll = 1;
- retval = pcie_wait_cmd(ctrl, poll);
+ pcie_wait_cmd(ctrl, poll);
}
out:
mutex_unlock(&ctrl->ctrl_lock);
@@ -614,23 +605,6 @@ static void hpc_set_green_led_blink(struct slot *slot)
__func__, ctrl->cap_base + SLOTCTRL, slot_cmd);
}
-static void hpc_release_ctlr(struct controller *ctrl)
-{
- /* Mask Hot-plug Interrupt Enable */
- if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE))
- err("%s: Cannot mask hotplut interrupt enable\n", __func__);
-
- /* Free interrupt handler or interrupt polling timer */
- pciehp_free_irq(ctrl);
-
- /*
- * If this is the last controller to be released, destroy the
- * pciehp work queue
- */
- if (atomic_dec_and_test(&pciehp_num_controllers))
- destroy_workqueue(pciehp_wq);
-}
-
static int hpc_power_on_slot(struct slot * slot)
{
struct controller *ctrl = slot->ctrl;
@@ -785,7 +759,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
intr_loc |= detected;
if (!intr_loc)
return IRQ_NONE;
- if (pciehp_writew(ctrl, SLOTSTATUS, detected)) {
+ if (detected && pciehp_writew(ctrl, SLOTSTATUS, detected)) {
err("%s: Cannot write to SLOTSTATUS\n", __func__);
return IRQ_NONE;
}
@@ -797,25 +771,13 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
if (intr_loc & CMD_COMPLETED) {
ctrl->cmd_busy = 0;
smp_mb();
- wake_up_interruptible(&ctrl->queue);
+ wake_up(&ctrl->queue);
}
if (!(intr_loc & ~CMD_COMPLETED))
return IRQ_HANDLED;
- /*
- * Return without handling events if this handler routine is
- * called before controller initialization is done. This may
- * happen if hotplug event or another interrupt that shares
- * the IRQ with pciehp arrives before slot initialization is
- * done after interrupt handler is registered.
- *
- * FIXME - Need more structural fixes. We need to be ready to
- * handle the event before installing interrupt handler.
- */
p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
- if (!p_slot || !p_slot->hpc_ops)
- return IRQ_HANDLED;
/* Check MRL Sensor Changed */
if (intr_loc & MRL_SENS_CHANGED)
@@ -992,6 +954,7 @@ static int hpc_get_cur_lnk_width(struct slot *slot,
return retval;
}
+static void pcie_release_ctrl(struct controller *ctrl);
static struct hpc_ops pciehp_hpc_ops = {
.power_on_slot = hpc_power_on_slot,
.power_off_slot = hpc_power_off_slot,
@@ -1013,97 +976,11 @@ static struct hpc_ops pciehp_hpc_ops = {
.green_led_off = hpc_set_green_led_off,
.green_led_blink = hpc_set_green_led_blink,
- .release_ctlr = hpc_release_ctlr,
+ .release_ctlr = pcie_release_ctrl,
.check_lnk_status = hpc_check_lnk_status,
};
-#ifdef CONFIG_ACPI
-static int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev)
-{
- acpi_status status;
- acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev));
- struct pci_dev *pdev = dev;
- struct pci_bus *parent;
- struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
-
- /*
- * Per PCI firmware specification, we should run the ACPI _OSC
- * method to get control of hotplug hardware before using it.
- * If an _OSC is missing, we look for an OSHP to do the same thing.
- * To handle different BIOS behavior, we look for _OSC and OSHP
- * within the scope of the hotplug controller and its parents, upto
- * the host bridge under which this controller exists.
- */
- while (!handle) {
- /*
- * This hotplug controller was not listed in the ACPI name
- * space at all. Try to get acpi handle of parent pci bus.
- */
- if (!pdev || !pdev->bus->parent)
- break;
- parent = pdev->bus->parent;
- dbg("Could not find %s in acpi namespace, trying parent\n",
- pci_name(pdev));
- if (!parent->self)
- /* Parent must be a host bridge */
- handle = acpi_get_pci_rootbridge_handle(
- pci_domain_nr(parent),
- parent->number);
- else
- handle = DEVICE_ACPI_HANDLE(
- &(parent->self->dev));
- pdev = parent->self;
- }
-
- while (handle) {
- acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
- dbg("Trying to get hotplug control for %s \n",
- (char *)string.pointer);
- status = pci_osc_control_set(handle,
- OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL |
- OSC_PCI_EXPRESS_NATIVE_HP_CONTROL);
- if (status == AE_NOT_FOUND)
- status = acpi_run_oshp(handle);
- if (ACPI_SUCCESS(status)) {
- dbg("Gained control for hotplug HW for pci %s (%s)\n",
- pci_name(dev), (char *)string.pointer);
- kfree(string.pointer);
- return 0;
- }
- if (acpi_root_bridge(handle))
- break;
- chandle = handle;
- status = acpi_get_parent(chandle, &handle);
- if (ACPI_FAILURE(status))
- break;
- }
-
- dbg("Cannot get control of hotplug hardware for pci %s\n",
- pci_name(dev));
-
- kfree(string.pointer);
- return -1;
-}
-#endif
-
-static int pcie_init_hardware_part1(struct controller *ctrl,
- struct pcie_device *dev)
-{
- /* Clear all remaining event bits in Slot Status register */
- if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) {
- err("%s: Cannot write to SLOTSTATUS register\n", __func__);
- return -1;
- }
-
- /* Mask Hot-plug Interrupt Enable */
- if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE)) {
- err("%s: Cannot mask hotplug interrupt enable\n", __func__);
- return -1;
- }
- return 0;
-}
-
-int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
+int pcie_enable_notification(struct controller *ctrl)
{
u16 cmd, mask;
@@ -1115,30 +992,74 @@ int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
if (MRL_SENS(ctrl))
cmd |= MRL_DETECT_ENABLE;
if (!pciehp_poll_mode)
- cmd |= HP_INTR_ENABLE;
+ cmd |= HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE;
- mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE |
- PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | HP_INTR_ENABLE;
+ mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE |
+ PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE;
if (pcie_write_cmd(ctrl, cmd, mask)) {
err("%s: Cannot enable software notification\n", __func__);
- goto abort;
+ return -1;
}
+ return 0;
+}
- if (pciehp_force)
- dbg("Bypassing BIOS check for pciehp use on %s\n",
- pci_name(ctrl->pci_dev));
- else if (pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev))
- goto abort_disable_intr;
+static void pcie_disable_notification(struct controller *ctrl)
+{
+ u16 mask;
+ mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE |
+ PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE;
+ if (pcie_write_cmd(ctrl, 0, mask))
+ warn("%s: Cannot disable software notification\n", __func__);
+}
+static int pcie_init_notification(struct controller *ctrl)
+{
+ if (pciehp_request_irq(ctrl))
+ return -1;
+ if (pcie_enable_notification(ctrl)) {
+ pciehp_free_irq(ctrl);
+ return -1;
+ }
return 0;
+}
- /* We end up here for the many possible ways to fail this API. */
-abort_disable_intr:
- if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE))
- err("%s : disabling interrupts failed\n", __func__);
-abort:
- return -1;
+static void pcie_shutdown_notification(struct controller *ctrl)
+{
+ pcie_disable_notification(ctrl);
+ pciehp_free_irq(ctrl);
+}
+
+static 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;
+ snprintf(slot->name, SLOT_NAME_SIZE, "%d", slot->number);
+ mutex_init(&slot->lock);
+ INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);
+ list_add(&slot->slot_list, &ctrl->slot_list);
+ return 0;
+}
+
+static void pcie_cleanup_slot(struct controller *ctrl)
+{
+ struct slot *slot;
+ slot = list_first_entry(&ctrl->slot_list, struct slot, slot_list);
+ list_del(&slot->slot_list);
+ cancel_delayed_work(&slot->work);
+ flush_scheduled_work();
+ flush_workqueue(pciehp_wq);
+ kfree(slot);
}
static inline void dbg_ctrl(struct controller *ctrl)
@@ -1173,18 +1094,26 @@ static inline void dbg_ctrl(struct controller *ctrl)
dbg(" Power Indicator : %3s\n", PWR_LED(ctrl) ? "yes" : "no");
dbg(" Hot-Plug Surprise : %3s\n", HP_SUPR_RM(ctrl) ? "yes" : "no");
dbg(" EMI Present : %3s\n", EMI(ctrl) ? "yes" : "no");
- dbg(" Comamnd Completed : %3s\n", NO_CMD_CMPL(ctrl)? "no" : "yes");
+ dbg(" Command 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 +1144,12 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev)
!(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl)))
ctrl->no_cmd_complete = 1;
- info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",
- pdev->vendor, pdev->device,
- pdev->subsystem_vendor, pdev->subsystem_device);
+ /* Clear all remaining event bits in Slot Status register */
+ if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f))
+ goto abort_ctrl;
- if (pcie_init_hardware_part1(ctrl, dev))
- goto abort;
-
- if (pciehp_request_irq(ctrl))
- goto abort;
+ /* Disable sotfware notification */
+ pcie_disable_notification(ctrl);
/*
* If this is the first controller to be initialized,
@@ -1231,18 +1157,39 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev)
*/
if (atomic_add_return(1, &pciehp_num_controllers) == 1) {
pciehp_wq = create_singlethread_workqueue("pciehpd");
- if (!pciehp_wq) {
- goto abort_free_irq;
- }
+ if (!pciehp_wq)
+ goto abort_ctrl;
}
- if (pcie_init_hardware_part2(ctrl, dev))
- goto abort_free_irq;
+ info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",
+ pdev->vendor, pdev->device,
+ pdev->subsystem_vendor, pdev->subsystem_device);
- return 0;
+ if (pcie_init_slot(ctrl))
+ goto abort_ctrl;
-abort_free_irq:
- pciehp_free_irq(ctrl);
+ if (pcie_init_notification(ctrl))
+ goto abort_slot;
+
+ return ctrl;
+
+abort_slot:
+ pcie_cleanup_slot(ctrl);
+abort_ctrl:
+ kfree(ctrl);
abort:
- return -1;
+ return NULL;
+}
+
+void pcie_release_ctrl(struct controller *ctrl)
+{
+ pcie_shutdown_notification(ctrl);
+ pcie_cleanup_slot(ctrl);
+ /*
+ * If this is the last controller to be released, destroy the
+ * pciehp work queue
+ */
+ if (atomic_dec_and_test(&pciehp_num_controllers))
+ destroy_workqueue(pciehp_wq);
+ kfree(ctrl);
}
diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c
index 779c5db71be4..a796301ea03f 100644
--- a/drivers/pci/hotplug/rpadlpar_sysfs.c
+++ b/drivers/pci/hotplug/rpadlpar_sysfs.c
@@ -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;
diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c
index 56197b600d36..9b714ea93d20 100644
--- a/drivers/pci/hotplug/rpaphp_slot.c
+++ b/drivers/pci/hotplug/rpaphp_slot.c
@@ -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);
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index 2fe37cd85b69..410fe0394a8e 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -197,13 +197,15 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
static struct hotplug_slot * sn_hp_destroy(void)
{
struct slot *slot;
+ struct pci_slot *pci_slot;
struct hotplug_slot *bss_hotplug_slot = NULL;
list_for_each_entry(slot, &sn_hp_list, hp_list) {
bss_hotplug_slot = slot->hotplug_slot;
+ pci_slot = bss_hotplug_slot->pci_slot;
list_del(&((struct slot *)bss_hotplug_slot->private)->
hp_list);
- sysfs_remove_file(&bss_hotplug_slot->kobj,
+ sysfs_remove_file(&pci_slot->kobj,
&sn_slot_path_attr.attr);
break;
}
@@ -614,6 +616,7 @@ static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot)
static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
{
int device;
+ struct pci_slot *pci_slot;
struct hotplug_slot *bss_hotplug_slot;
int rc = 0;
@@ -650,11 +653,12 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
bss_hotplug_slot->release = &sn_release_slot;
- rc = pci_hp_register(bss_hotplug_slot);
+ rc = pci_hp_register(bss_hotplug_slot, pci_bus, device);
if (rc)
goto register_err;
- rc = sysfs_create_file(&bss_hotplug_slot->kobj,
+ pci_slot = bss_hotplug_slot->pci_slot;
+ rc = sysfs_create_file(&pci_slot->kobj,
&sn_slot_path_attr.attr);
if (rc)
goto register_err;
@@ -664,7 +668,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
register_err:
dev_dbg(&pci_bus->self->dev, "bus failed to register with err = %d\n",
- rc);
+ rc);
alloc_err:
if (rc == -ENOMEM)
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
index f66e8d6315ab..8a026f750deb 100644
--- a/drivers/pci/hotplug/shpchp.h
+++ b/drivers/pci/hotplug/shpchp.h
@@ -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 {
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index 97848654652a..cc38615395f1 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -39,7 +39,6 @@
int shpchp_debug;
int shpchp_poll_mode;
int shpchp_poll_time;
-int shpchp_slot_with_bus;
struct workqueue_struct *shpchp_wq;
#define DRIVER_VERSION "0.4"
@@ -53,11 +52,9 @@ MODULE_LICENSE("GPL");
module_param(shpchp_debug, bool, 0644);
module_param(shpchp_poll_mode, bool, 0644);
module_param(shpchp_poll_time, int, 0644);
-module_param(shpchp_slot_with_bus, bool, 0644);
MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");
MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");
MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");
-MODULE_PARM_DESC(shpchp_slot_with_bus, "Use bus number in the slot name");
#define SHPC_MODULE_NAME "shpchp"
@@ -68,7 +65,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 +77,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,
};
@@ -101,23 +96,13 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
kfree(slot);
}
-static void make_slot_name(struct slot *slot)
-{
- if (shpchp_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);
-}
-
static int init_slots(struct controller *ctrl)
{
struct slot *slot;
struct hotplug_slot *hotplug_slot;
struct hotplug_slot_info *info;
int retval = -ENOMEM;
- int i;
+ int i, len, dup = 1;
for (i = 0; i < ctrl->num_slots; i++) {
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
@@ -148,7 +133,7 @@ static int init_slots(struct controller *ctrl)
/* register this slot with the hotplug pci core */
hotplug_slot->private = slot;
hotplug_slot->release = &release_slot;
- make_slot_name(slot);
+ snprintf(slot->name, SLOT_NAME_SIZE, "%d", slot->number);
hotplug_slot->ops = &shpchp_hotplug_slot_ops;
get_power_status(hotplug_slot, &info->power_status);
@@ -159,13 +144,23 @@ 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);
+duplicate_name:
+ retval = pci_hp_register(slot->hotplug_slot,
+ ctrl->pci_dev->subordinate, slot->device);
if (retval) {
+ /*
+ * If slot N already exists, we'll try to create
+ * slot N-1, N-2 ... N-M, until we overflow.
+ */
+ if (retval == -EEXIST) {
+ len = snprintf(slot->name, SLOT_NAME_SIZE,
+ "%d-%d", slot->number, dup++);
+ if (len < SLOT_NAME_SIZE)
+ goto duplicate_name;
+ else
+ err("duplicate slot name overflow\n");
+ }
err("pci_hp_register failed with error %d\n", retval);
- if (retval == -EEXIST)
- err("Failed to register slot because of name "
- "collision. Try \'shpchp_slot_with_bus\' "
- "module option.\n");
goto error_info;
}
@@ -288,19 +283,8 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}
-static int get_address (struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = get_slot(hotplug_slot);
- struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
-
- dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
-
- *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
-
- return 0;
-}
-
-static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+static int get_max_bus_speed(struct hotplug_slot *hotplug_slot,
+ enum pci_bus_speed *value)
{
struct slot *slot = get_slot(hotplug_slot);
int retval;
@@ -330,13 +314,14 @@ static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_sp
static int is_shpc_capable(struct pci_dev *dev)
{
- if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device ==
- PCI_DEVICE_ID_AMD_GOLAM_7450))
- return 1;
- if (pci_find_capability(dev, PCI_CAP_ID_SHPC))
- return 1;
-
- return 0;
+ if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device ==
+ PCI_DEVICE_ID_AMD_GOLAM_7450))
+ return 1;
+ if (!pci_find_capability(dev, PCI_CAP_ID_SHPC))
+ return 0;
+ if (get_hp_hw_control_from_firmware(dev))
+ return 0;
+ return 1;
}
static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index 7d770b2cd889..7a0bff364cd4 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -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,
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index 66c0fd21894b..389fdd6f4a9f 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -37,7 +37,7 @@
#include "intel-iommu.h"
#include <asm/proto.h> /* force_iommu in this header in x86-64*/
#include <asm/cacheflush.h>
-#include <asm/gart.h>
+#include <asm/iommu.h>
#include "pci.h"
#define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
@@ -49,8 +49,6 @@
#define DEFAULT_DOMAIN_ADDRESS_WIDTH 48
-#define DMAR_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) /* 10sec */
-
#define DOMAIN_MAX_ADDR(gaw) ((((u64)1) << gaw) - 1)
@@ -58,8 +56,6 @@ static void flush_unmaps_timeout(unsigned long data);
DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0);
-static struct intel_iommu *g_iommus;
-
#define HIGH_WATER_MARK 250
struct deferred_flush_tables {
int next;
@@ -80,7 +76,7 @@ static long list_size;
static void domain_remove_dev_info(struct dmar_domain *domain);
-static int dmar_disabled;
+int dmar_disabled;
static int __initdata dmar_map_gfx = 1;
static int dmar_forcedac;
static int intel_iommu_strict;
@@ -185,13 +181,6 @@ void free_iova_mem(struct iova *iova)
kmem_cache_free(iommu_iova_cache, iova);
}
-static inline void __iommu_flush_cache(
- struct intel_iommu *iommu, void *addr, int size)
-{
- if (!ecap_coherent(iommu->ecap))
- clflush_cache_range(addr, size);
-}
-
/* Gets context entry for a given bus and devfn */
static struct context_entry * device_to_context_entry(struct intel_iommu *iommu,
u8 bus, u8 devfn)
@@ -488,19 +477,6 @@ static int iommu_alloc_root_entry(struct intel_iommu *iommu)
return 0;
}
-#define IOMMU_WAIT_OP(iommu, offset, op, cond, sts) \
-{\
- cycles_t start_time = get_cycles();\
- while (1) {\
- sts = op (iommu->reg + offset);\
- if (cond)\
- break;\
- if (DMAR_OPERATION_TIMEOUT < (get_cycles() - start_time))\
- panic("DMAR hardware is malfunctioning\n");\
- cpu_relax();\
- }\
-}
-
static void iommu_set_root_entry(struct intel_iommu *iommu)
{
void *addr;
@@ -990,6 +966,8 @@ static int iommu_init_domains(struct intel_iommu *iommu)
return -ENOMEM;
}
+ spin_lock_init(&iommu->lock);
+
/*
* if Caching mode is set, then invalid translations are tagged
* with domainid 0. Hence we need to pre-allocate it.
@@ -998,62 +976,15 @@ static int iommu_init_domains(struct intel_iommu *iommu)
set_bit(0, iommu->domain_ids);
return 0;
}
-static struct intel_iommu *alloc_iommu(struct intel_iommu *iommu,
- struct dmar_drhd_unit *drhd)
-{
- int ret;
- int map_size;
- u32 ver;
- iommu->reg = ioremap(drhd->reg_base_addr, PAGE_SIZE_4K);
- if (!iommu->reg) {
- printk(KERN_ERR "IOMMU: can't map the region\n");
- goto error;
- }
- iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG);
- iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG);
-
- /* the registers might be more than one page */
- map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap),
- cap_max_fault_reg_offset(iommu->cap));
- map_size = PAGE_ALIGN_4K(map_size);
- if (map_size > PAGE_SIZE_4K) {
- iounmap(iommu->reg);
- iommu->reg = ioremap(drhd->reg_base_addr, map_size);
- if (!iommu->reg) {
- printk(KERN_ERR "IOMMU: can't map the region\n");
- goto error;
- }
- }
-
- ver = readl(iommu->reg + DMAR_VER_REG);
- pr_debug("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n",
- drhd->reg_base_addr, DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
- iommu->cap, iommu->ecap);
- ret = iommu_init_domains(iommu);
- if (ret)
- goto error_unmap;
- spin_lock_init(&iommu->lock);
- spin_lock_init(&iommu->register_lock);
-
- drhd->iommu = iommu;
- return iommu;
-error_unmap:
- iounmap(iommu->reg);
-error:
- kfree(iommu);
- return NULL;
-}
static void domain_exit(struct dmar_domain *domain);
-static void free_iommu(struct intel_iommu *iommu)
+
+void free_dmar_iommu(struct intel_iommu *iommu)
{
struct dmar_domain *domain;
int i;
- if (!iommu)
- return;
-
i = find_first_bit(iommu->domain_ids, cap_ndoms(iommu->cap));
for (; i < cap_ndoms(iommu->cap); ) {
domain = iommu->domains[i];
@@ -1078,10 +1009,6 @@ static void free_iommu(struct intel_iommu *iommu)
/* free context mapping */
free_context_table(iommu);
-
- if (iommu->reg)
- iounmap(iommu->reg);
- kfree(iommu);
}
static struct dmar_domain * iommu_alloc_domain(struct intel_iommu *iommu)
@@ -1426,37 +1353,6 @@ find_domain(struct pci_dev *pdev)
return NULL;
}
-static int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
- struct pci_dev *dev)
-{
- int index;
-
- while (dev) {
- for (index = 0; index < cnt; index++)
- if (dev == devices[index])
- return 1;
-
- /* Check our parent */
- dev = dev->bus->self;
- }
-
- return 0;
-}
-
-static struct dmar_drhd_unit *
-dmar_find_matched_drhd_unit(struct pci_dev *dev)
-{
- struct dmar_drhd_unit *drhd = NULL;
-
- list_for_each_entry(drhd, &dmar_drhd_units, list) {
- if (drhd->include_all || dmar_pci_device_match(drhd->devices,
- drhd->devices_cnt, dev))
- return drhd;
- }
-
- return NULL;
-}
-
/* domain is initialized */
static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
{
@@ -1637,12 +1533,43 @@ static inline int iommu_prepare_rmrr_dev(struct dmar_rmrr_unit *rmrr,
}
#ifdef CONFIG_DMAR_GFX_WA
-extern int arch_get_ram_range(int slot, u64 *addr, u64 *size);
+struct iommu_prepare_data {
+ struct pci_dev *pdev;
+ int ret;
+};
+
+static int __init iommu_prepare_work_fn(unsigned long start_pfn,
+ unsigned long end_pfn, void *datax)
+{
+ struct iommu_prepare_data *data;
+
+ data = (struct iommu_prepare_data *)datax;
+
+ data->ret = iommu_prepare_identity_map(data->pdev,
+ start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);
+ return data->ret;
+
+}
+
+static int __init iommu_prepare_with_active_regions(struct pci_dev *pdev)
+{
+ int nid;
+ struct iommu_prepare_data data;
+
+ data.pdev = pdev;
+ data.ret = 0;
+
+ for_each_online_node(nid) {
+ work_with_active_regions(nid, iommu_prepare_work_fn, &data);
+ if (data.ret)
+ return data.ret;
+ }
+ return data.ret;
+}
+
static void __init iommu_prepare_gfx_mapping(void)
{
struct pci_dev *pdev = NULL;
- u64 base, size;
- int slot;
int ret;
for_each_pci_dev(pdev) {
@@ -1651,17 +1578,9 @@ static void __init iommu_prepare_gfx_mapping(void)
continue;
printk(KERN_INFO "IOMMU: gfx device %s 1-1 mapping\n",
pci_name(pdev));
- slot = arch_get_ram_range(0, &base, &size);
- while (slot >= 0) {
- ret = iommu_prepare_identity_map(pdev,
- base, base + size);
- if (ret)
- goto error;
- slot = arch_get_ram_range(slot, &base, &size);
- }
- continue;
-error:
- printk(KERN_ERR "IOMMU: mapping reserved region failed\n");
+ ret = iommu_prepare_with_active_regions(pdev);
+ if (ret)
+ printk(KERN_ERR "IOMMU: mapping reserved region failed\n");
}
}
#endif
@@ -1706,8 +1625,6 @@ int __init init_dmars(void)
* endfor
*/
for_each_drhd_unit(drhd) {
- if (drhd->ignored)
- continue;
g_num_of_iommus++;
/*
* lock not needed as this is only incremented in the single
@@ -1716,30 +1633,22 @@ int __init init_dmars(void)
*/
}
- g_iommus = kzalloc(g_num_of_iommus * sizeof(*iommu), GFP_KERNEL);
- if (!g_iommus) {
- ret = -ENOMEM;
- goto error;
- }
-
deferred_flush = kzalloc(g_num_of_iommus *
sizeof(struct deferred_flush_tables), GFP_KERNEL);
if (!deferred_flush) {
- kfree(g_iommus);
ret = -ENOMEM;
goto error;
}
- i = 0;
for_each_drhd_unit(drhd) {
if (drhd->ignored)
continue;
- iommu = alloc_iommu(&g_iommus[i], drhd);
- i++;
- if (!iommu) {
- ret = -ENOMEM;
+
+ iommu = drhd->iommu;
+
+ ret = iommu_init_domains(iommu);
+ if (ret)
goto error;
- }
/*
* TBD:
@@ -1823,7 +1732,6 @@ error:
iommu = drhd->iommu;
free_iommu(iommu);
}
- kfree(g_iommus);
return ret;
}
@@ -1980,7 +1888,10 @@ static void flush_unmaps(void)
/* just flush them all */
for (i = 0; i < g_num_of_iommus; i++) {
if (deferred_flush[i].next) {
- iommu_flush_iotlb_global(&g_iommus[i], 0);
+ struct intel_iommu *iommu =
+ deferred_flush[i].domain[0]->iommu;
+
+ iommu_flush_iotlb_global(iommu, 0);
for (j = 0; j < deferred_flush[i].next; j++) {
__free_iova(&deferred_flush[i].domain[j]->iovad,
deferred_flush[i].iova[j]);
@@ -2010,7 +1921,8 @@ static void add_unmap(struct dmar_domain *dom, struct iova *iova)
if (list_size == HIGH_WATER_MARK)
flush_unmaps();
- iommu_id = dom->iommu - g_iommus;
+ iommu_id = dom->iommu->seq_id;
+
next = deferred_flush[iommu_id].next;
deferred_flush[iommu_id].domain[next] = dom;
deferred_flush[iommu_id].iova[next] = iova;
@@ -2326,15 +2238,6 @@ static void __init iommu_exit_mempool(void)
}
-void __init detect_intel_iommu(void)
-{
- if (swiotlb || no_iommu || iommu_detected || dmar_disabled)
- return;
- if (early_dmar_detect()) {
- iommu_detected = 1;
- }
-}
-
static void __init init_no_remapping_devices(void)
{
struct dmar_drhd_unit *drhd;
@@ -2381,12 +2284,19 @@ int __init intel_iommu_init(void)
{
int ret = 0;
- if (no_iommu || swiotlb || dmar_disabled)
- return -ENODEV;
-
if (dmar_table_init())
return -ENODEV;
+ if (dmar_dev_scope_init())
+ return -ENODEV;
+
+ /*
+ * Check the need for DMA-remapping initialization now.
+ * Above initialization will also be used by Interrupt-remapping.
+ */
+ if (no_iommu || swiotlb || dmar_disabled)
+ return -ENODEV;
+
iommu_init_mempool();
dmar_init_reserved_ranges();
diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h
index afc0ad96122e..2142c01e0143 100644
--- a/drivers/pci/intel-iommu.h
+++ b/drivers/pci/intel-iommu.h
@@ -27,19 +27,8 @@
#include <linux/sysdev.h>
#include "iova.h"
#include <linux/io.h>
-
-/*
- * We need a fixed PAGE_SIZE of 4K irrespective of
- * arch PAGE_SIZE for IOMMU page tables.
- */
-#define PAGE_SHIFT_4K (12)
-#define PAGE_SIZE_4K (1UL << PAGE_SHIFT_4K)
-#define PAGE_MASK_4K (((u64)-1) << PAGE_SHIFT_4K)
-#define PAGE_ALIGN_4K(addr) (((addr) + PAGE_SIZE_4K - 1) & PAGE_MASK_4K)
-
-#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT_4K)
-#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK)
-#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK)
+#include <asm/cacheflush.h>
+#include "dma_remapping.h"
/*
* Intel IOMMU register specification per version 1.0 public spec.
@@ -63,6 +52,11 @@
#define DMAR_PLMLIMIT_REG 0x6c /* PMRR low limit */
#define DMAR_PHMBASE_REG 0x70 /* pmrr high base addr */
#define DMAR_PHMLIMIT_REG 0x78 /* pmrr high limit */
+#define DMAR_IQH_REG 0x80 /* Invalidation queue head register */
+#define DMAR_IQT_REG 0x88 /* Invalidation queue tail register */
+#define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */
+#define DMAR_ICS_REG 0x98 /* Invalidation complete status register */
+#define DMAR_IRTA_REG 0xb8 /* Interrupt remapping table addr register */
#define OFFSET_STRIDE (9)
/*
@@ -126,6 +120,10 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
#define ecap_max_iotlb_offset(e) \
(ecap_iotlb_offset(e) + ecap_niotlb_iunits(e) * 16)
#define ecap_coherent(e) ((e) & 0x1)
+#define ecap_qis(e) ((e) & 0x2)
+#define ecap_eim_support(e) ((e >> 4) & 0x1)
+#define ecap_ir_support(e) ((e >> 3) & 0x1)
+#define ecap_max_handle_mask(e) ((e >> 20) & 0xf)
/* IOTLB_REG */
@@ -141,6 +139,17 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
#define DMA_TLB_IH_NONLEAF (((u64)1) << 6)
#define DMA_TLB_MAX_SIZE (0x3f)
+/* INVALID_DESC */
+#define DMA_ID_TLB_GLOBAL_FLUSH (((u64)1) << 3)
+#define DMA_ID_TLB_DSI_FLUSH (((u64)2) << 3)
+#define DMA_ID_TLB_PSI_FLUSH (((u64)3) << 3)
+#define DMA_ID_TLB_READ_DRAIN (((u64)1) << 7)
+#define DMA_ID_TLB_WRITE_DRAIN (((u64)1) << 6)
+#define DMA_ID_TLB_DID(id) (((u64)((id & 0xffff) << 16)))
+#define DMA_ID_TLB_IH_NONLEAF (((u64)1) << 6)
+#define DMA_ID_TLB_ADDR(addr) (addr)
+#define DMA_ID_TLB_ADDR_MASK(mask) (mask)
+
/* PMEN_REG */
#define DMA_PMEN_EPM (((u32)1)<<31)
#define DMA_PMEN_PRS (((u32)1)<<0)
@@ -151,6 +160,9 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
#define DMA_GCMD_SFL (((u32)1) << 29)
#define DMA_GCMD_EAFL (((u32)1) << 28)
#define DMA_GCMD_WBF (((u32)1) << 27)
+#define DMA_GCMD_QIE (((u32)1) << 26)
+#define DMA_GCMD_SIRTP (((u32)1) << 24)
+#define DMA_GCMD_IRE (((u32) 1) << 25)
/* GSTS_REG */
#define DMA_GSTS_TES (((u32)1) << 31)
@@ -158,6 +170,9 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
#define DMA_GSTS_FLS (((u32)1) << 29)
#define DMA_GSTS_AFLS (((u32)1) << 28)
#define DMA_GSTS_WBFS (((u32)1) << 27)
+#define DMA_GSTS_QIES (((u32)1) << 26)
+#define DMA_GSTS_IRTPS (((u32)1) << 24)
+#define DMA_GSTS_IRES (((u32)1) << 25)
/* CCMD_REG */
#define DMA_CCMD_ICC (((u64)1) << 63)
@@ -187,158 +202,106 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
#define dma_frcd_source_id(c) (c & 0xffff)
#define dma_frcd_page_addr(d) (d & (((u64)-1) << 12)) /* low 64 bit */
-/*
- * 0: Present
- * 1-11: Reserved
- * 12-63: Context Ptr (12 - (haw-1))
- * 64-127: Reserved
- */
-struct root_entry {
- u64 val;
- u64 rsvd1;
-};
-#define ROOT_ENTRY_NR (PAGE_SIZE_4K/sizeof(struct root_entry))
-static inline bool root_present(struct root_entry *root)
-{
- return (root->val & 1);
-}
-static inline void set_root_present(struct root_entry *root)
-{
- root->val |= 1;
-}
-static inline void set_root_value(struct root_entry *root, unsigned long value)
-{
- root->val |= value & PAGE_MASK_4K;
+#define DMAR_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) /* 10sec */
+
+#define IOMMU_WAIT_OP(iommu, offset, op, cond, sts) \
+{\
+ cycles_t start_time = get_cycles();\
+ while (1) {\
+ sts = op (iommu->reg + offset);\
+ if (cond)\
+ break;\
+ if (DMAR_OPERATION_TIMEOUT < (get_cycles() - start_time))\
+ panic("DMAR hardware is malfunctioning\n");\
+ cpu_relax();\
+ }\
}
-struct context_entry;
-static inline struct context_entry *
-get_context_addr_from_root(struct root_entry *root)
-{
- return (struct context_entry *)
- (root_present(root)?phys_to_virt(
- root->val & PAGE_MASK_4K):
- NULL);
-}
-
-/*
- * low 64 bits:
- * 0: present
- * 1: fault processing disable
- * 2-3: translation type
- * 12-63: address space root
- * high 64 bits:
- * 0-2: address width
- * 3-6: aval
- * 8-23: domain id
- */
-struct context_entry {
- u64 lo;
- u64 hi;
-};
-#define context_present(c) ((c).lo & 1)
-#define context_fault_disable(c) (((c).lo >> 1) & 1)
-#define context_translation_type(c) (((c).lo >> 2) & 3)
-#define context_address_root(c) ((c).lo & PAGE_MASK_4K)
-#define context_address_width(c) ((c).hi & 7)
-#define context_domain_id(c) (((c).hi >> 8) & ((1 << 16) - 1))
-
-#define context_set_present(c) do {(c).lo |= 1;} while (0)
-#define context_set_fault_enable(c) \
- do {(c).lo &= (((u64)-1) << 2) | 1;} while (0)
-#define context_set_translation_type(c, val) \
- do { \
- (c).lo &= (((u64)-1) << 4) | 3; \
- (c).lo |= ((val) & 3) << 2; \
- } while (0)
-#define CONTEXT_TT_MULTI_LEVEL 0
-#define context_set_address_root(c, val) \
- do {(c).lo |= (val) & PAGE_MASK_4K;} while (0)
-#define context_set_address_width(c, val) do {(c).hi |= (val) & 7;} while (0)
-#define context_set_domain_id(c, val) \
- do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0)
-#define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0)
+#define QI_LENGTH 256 /* queue length */
-/*
- * 0: readable
- * 1: writable
- * 2-6: reserved
- * 7: super page
- * 8-11: available
- * 12-63: Host physcial address
- */
-struct dma_pte {
- u64 val;
+enum {
+ QI_FREE,
+ QI_IN_USE,
+ QI_DONE
};
-#define dma_clear_pte(p) do {(p).val = 0;} while (0)
-
-#define DMA_PTE_READ (1)
-#define DMA_PTE_WRITE (2)
-#define dma_set_pte_readable(p) do {(p).val |= DMA_PTE_READ;} while (0)
-#define dma_set_pte_writable(p) do {(p).val |= DMA_PTE_WRITE;} while (0)
-#define dma_set_pte_prot(p, prot) \
- do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0)
-#define dma_pte_addr(p) ((p).val & PAGE_MASK_4K)
-#define dma_set_pte_addr(p, addr) do {\
- (p).val |= ((addr) & PAGE_MASK_4K); } while (0)
-#define dma_pte_present(p) (((p).val & 3) != 0)
+#define QI_CC_TYPE 0x1
+#define QI_IOTLB_TYPE 0x2
+#define QI_DIOTLB_TYPE 0x3
+#define QI_IEC_TYPE 0x4
+#define QI_IWD_TYPE 0x5
-struct intel_iommu;
+#define QI_IEC_SELECTIVE (((u64)1) << 4)
+#define QI_IEC_IIDEX(idx) (((u64)(idx & 0xffff) << 32))
+#define QI_IEC_IM(m) (((u64)(m & 0x1f) << 27))
-struct dmar_domain {
- int id; /* domain id */
- struct intel_iommu *iommu; /* back pointer to owning iommu */
+#define QI_IWD_STATUS_DATA(d) (((u64)d) << 32)
+#define QI_IWD_STATUS_WRITE (((u64)1) << 5)
- struct list_head devices; /* all devices' list */
- struct iova_domain iovad; /* iova's that belong to this domain */
+struct qi_desc {
+ u64 low, high;
+};
- struct dma_pte *pgd; /* virtual address */
- spinlock_t mapping_lock; /* page table lock */
- int gaw; /* max guest address width */
+struct q_inval {
+ spinlock_t q_lock;
+ struct qi_desc *desc; /* invalidation queue */
+ int *desc_status; /* desc status */
+ int free_head; /* first free entry */
+ int free_tail; /* last free entry */
+ int free_cnt;
+};
- /* adjusted guest address width, 0 is level 2 30-bit */
- int agaw;
+#ifdef CONFIG_INTR_REMAP
+/* 1MB - maximum possible interrupt remapping table size */
+#define INTR_REMAP_PAGE_ORDER 8
+#define INTR_REMAP_TABLE_REG_SIZE 0xf
-#define DOMAIN_FLAG_MULTIPLE_DEVICES 1
- int flags;
-};
+#define INTR_REMAP_TABLE_ENTRIES 65536
-/* PCI domain-device relationship */
-struct device_domain_info {
- struct list_head link; /* link to domain siblings */
- struct list_head global; /* link to global list */
- u8 bus; /* PCI bus numer */
- u8 devfn; /* PCI devfn number */
- struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */
- struct dmar_domain *domain; /* pointer to domain */
+struct ir_table {
+ struct irte *base;
};
-
-extern int init_dmars(void);
+#endif
struct intel_iommu {
void __iomem *reg; /* Pointer to hardware regs, virtual addr */
u64 cap;
u64 ecap;
- unsigned long *domain_ids; /* bitmap of domains */
- struct dmar_domain **domains; /* ptr to domains */
int seg;
u32 gcmd; /* Holds TE, EAFL. Don't need SRTP, SFL, WBF */
- spinlock_t lock; /* protect context, domain ids */
spinlock_t register_lock; /* protect register handling */
+ int seq_id; /* sequence id of the iommu */
+
+#ifdef CONFIG_DMAR
+ unsigned long *domain_ids; /* bitmap of domains */
+ struct dmar_domain **domains; /* ptr to domains */
+ spinlock_t lock; /* protect context, domain ids */
struct root_entry *root_entry; /* virtual address */
unsigned int irq;
unsigned char name[7]; /* Device Name */
struct msi_msg saved_msg;
struct sys_device sysdev;
+#endif
+ struct q_inval *qi; /* Queued invalidation info */
+#ifdef CONFIG_INTR_REMAP
+ struct ir_table *ir_table; /* Interrupt remapping info */
+#endif
};
-#ifndef CONFIG_DMAR_GFX_WA
-static inline void iommu_prepare_gfx_mapping(void)
+static inline void __iommu_flush_cache(
+ struct intel_iommu *iommu, void *addr, int size)
{
- return;
+ if (!ecap_coherent(iommu->ecap))
+ clflush_cache_range(addr, size);
}
-#endif /* !CONFIG_DMAR_GFX_WA */
+extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev);
+
+extern int alloc_iommu(struct dmar_drhd_unit *drhd);
+extern void free_iommu(struct intel_iommu *iommu);
+extern int dmar_enable_qi(struct intel_iommu *iommu);
+extern void qi_global_iec(struct intel_iommu *iommu);
+
+extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu);
#endif
diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c
new file mode 100644
index 000000000000..bb642cc5e18c
--- /dev/null
+++ b/drivers/pci/intr_remapping.c
@@ -0,0 +1,471 @@
+#include <linux/dmar.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+#include <linux/pci.h>
+#include <linux/irq.h>
+#include <asm/io_apic.h>
+#include "intel-iommu.h"
+#include "intr_remapping.h"
+
+static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
+static int ir_ioapic_num;
+int intr_remapping_enabled;
+
+static struct {
+ struct intel_iommu *iommu;
+ u16 irte_index;
+ u16 sub_handle;
+ u8 irte_mask;
+} irq_2_iommu[NR_IRQS];
+
+static DEFINE_SPINLOCK(irq_2_ir_lock);
+
+int irq_remapped(int irq)
+{
+ if (irq > NR_IRQS)
+ return 0;
+
+ if (!irq_2_iommu[irq].iommu)
+ return 0;
+
+ return 1;
+}
+
+int get_irte(int irq, struct irte *entry)
+{
+ int index;
+
+ if (!entry || irq > NR_IRQS)
+ return -1;
+
+ spin_lock(&irq_2_ir_lock);
+ if (!irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle;
+ *entry = *(irq_2_iommu[irq].iommu->ir_table->base + index);
+
+ spin_unlock(&irq_2_ir_lock);
+ return 0;
+}
+
+int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
+{
+ struct ir_table *table = iommu->ir_table;
+ u16 index, start_index;
+ unsigned int mask = 0;
+ int i;
+
+ if (!count)
+ return -1;
+
+ /*
+ * start the IRTE search from index 0.
+ */
+ index = start_index = 0;
+
+ if (count > 1) {
+ count = __roundup_pow_of_two(count);
+ mask = ilog2(count);
+ }
+
+ if (mask > ecap_max_handle_mask(iommu->ecap)) {
+ printk(KERN_ERR
+ "Requested mask %x exceeds the max invalidation handle"
+ " mask value %Lx\n", mask,
+ ecap_max_handle_mask(iommu->ecap));
+ return -1;
+ }
+
+ spin_lock(&irq_2_ir_lock);
+ do {
+ for (i = index; i < index + count; i++)
+ if (table->base[i].present)
+ break;
+ /* empty index found */
+ if (i == index + count)
+ break;
+
+ index = (index + count) % INTR_REMAP_TABLE_ENTRIES;
+
+ if (index == start_index) {
+ spin_unlock(&irq_2_ir_lock);
+ printk(KERN_ERR "can't allocate an IRTE\n");
+ return -1;
+ }
+ } while (1);
+
+ for (i = index; i < index + count; i++)
+ table->base[i].present = 1;
+
+ irq_2_iommu[irq].iommu = iommu;
+ irq_2_iommu[irq].irte_index = index;
+ irq_2_iommu[irq].sub_handle = 0;
+ irq_2_iommu[irq].irte_mask = mask;
+
+ spin_unlock(&irq_2_ir_lock);
+
+ return index;
+}
+
+static void qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
+{
+ struct qi_desc desc;
+
+ desc.low = QI_IEC_IIDEX(index) | QI_IEC_TYPE | QI_IEC_IM(mask)
+ | QI_IEC_SELECTIVE;
+ desc.high = 0;
+
+ qi_submit_sync(&desc, iommu);
+}
+
+int map_irq_to_irte_handle(int irq, u16 *sub_handle)
+{
+ int index;
+
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ *sub_handle = irq_2_iommu[irq].sub_handle;
+ index = irq_2_iommu[irq].irte_index;
+ spin_unlock(&irq_2_ir_lock);
+ return index;
+}
+
+int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle)
+{
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ irq_2_iommu[irq].iommu = iommu;
+ irq_2_iommu[irq].irte_index = index;
+ irq_2_iommu[irq].sub_handle = subhandle;
+ irq_2_iommu[irq].irte_mask = 0;
+
+ spin_unlock(&irq_2_ir_lock);
+
+ return 0;
+}
+
+int clear_irte_irq(int irq, struct intel_iommu *iommu, u16 index)
+{
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ irq_2_iommu[irq].iommu = NULL;
+ irq_2_iommu[irq].irte_index = 0;
+ irq_2_iommu[irq].sub_handle = 0;
+ irq_2_iommu[irq].irte_mask = 0;
+
+ spin_unlock(&irq_2_ir_lock);
+
+ return 0;
+}
+
+int modify_irte(int irq, struct irte *irte_modified)
+{
+ int index;
+ struct irte *irte;
+ struct intel_iommu *iommu;
+
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ iommu = irq_2_iommu[irq].iommu;
+
+ index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle;
+ irte = &iommu->ir_table->base[index];
+
+ set_64bit((unsigned long *)irte, irte_modified->low | (1 << 1));
+ __iommu_flush_cache(iommu, irte, sizeof(*irte));
+
+ qi_flush_iec(iommu, index, 0);
+
+ spin_unlock(&irq_2_ir_lock);
+ return 0;
+}
+
+int flush_irte(int irq)
+{
+ int index;
+ struct intel_iommu *iommu;
+
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ iommu = irq_2_iommu[irq].iommu;
+
+ index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle;
+
+ qi_flush_iec(iommu, index, irq_2_iommu[irq].irte_mask);
+ spin_unlock(&irq_2_ir_lock);
+
+ return 0;
+}
+
+struct intel_iommu *map_ioapic_to_ir(int apic)
+{
+ int i;
+
+ for (i = 0; i < MAX_IO_APICS; i++)
+ if (ir_ioapic[i].id == apic)
+ return ir_ioapic[i].iommu;
+ return NULL;
+}
+
+struct intel_iommu *map_dev_to_ir(struct pci_dev *dev)
+{
+ struct dmar_drhd_unit *drhd;
+
+ drhd = dmar_find_matched_drhd_unit(dev);
+ if (!drhd)
+ return NULL;
+
+ return drhd->iommu;
+}
+
+int free_irte(int irq)
+{
+ int index, i;
+ struct irte *irte;
+ struct intel_iommu *iommu;
+
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ iommu = irq_2_iommu[irq].iommu;
+
+ index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle;
+ irte = &iommu->ir_table->base[index];
+
+ if (!irq_2_iommu[irq].sub_handle) {
+ for (i = 0; i < (1 << irq_2_iommu[irq].irte_mask); i++)
+ set_64bit((unsigned long *)irte, 0);
+ qi_flush_iec(iommu, index, irq_2_iommu[irq].irte_mask);
+ }
+
+ irq_2_iommu[irq].iommu = NULL;
+ irq_2_iommu[irq].irte_index = 0;
+ irq_2_iommu[irq].sub_handle = 0;
+ irq_2_iommu[irq].irte_mask = 0;
+
+ spin_unlock(&irq_2_ir_lock);
+
+ return 0;
+}
+
+static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
+{
+ u64 addr;
+ u32 cmd, sts;
+ unsigned long flags;
+
+ addr = virt_to_phys((void *)iommu->ir_table->base);
+
+ spin_lock_irqsave(&iommu->register_lock, flags);
+
+ dmar_writeq(iommu->reg + DMAR_IRTA_REG,
+ (addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE);
+
+ /* Set interrupt-remapping table pointer */
+ cmd = iommu->gcmd | DMA_GCMD_SIRTP;
+ writel(cmd, iommu->reg + DMAR_GCMD_REG);
+
+ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
+ readl, (sts & DMA_GSTS_IRTPS), sts);
+ spin_unlock_irqrestore(&iommu->register_lock, flags);
+
+ /*
+ * global invalidation of interrupt entry cache before enabling
+ * interrupt-remapping.
+ */
+ qi_global_iec(iommu);
+
+ spin_lock_irqsave(&iommu->register_lock, flags);
+
+ /* Enable interrupt-remapping */
+ cmd = iommu->gcmd | DMA_GCMD_IRE;
+ iommu->gcmd |= DMA_GCMD_IRE;
+ writel(cmd, iommu->reg + DMAR_GCMD_REG);
+
+ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
+ readl, (sts & DMA_GSTS_IRES), sts);
+
+ spin_unlock_irqrestore(&iommu->register_lock, flags);
+}
+
+
+static int setup_intr_remapping(struct intel_iommu *iommu, int mode)
+{
+ struct ir_table *ir_table;
+ struct page *pages;
+
+ ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table),
+ GFP_KERNEL);
+
+ if (!iommu->ir_table)
+ return -ENOMEM;
+
+ pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, INTR_REMAP_PAGE_ORDER);
+
+ if (!pages) {
+ printk(KERN_ERR "failed to allocate pages of order %d\n",
+ INTR_REMAP_PAGE_ORDER);
+ kfree(iommu->ir_table);
+ return -ENOMEM;
+ }
+
+ ir_table->base = page_address(pages);
+
+ iommu_set_intr_remapping(iommu, mode);
+ return 0;
+}
+
+int __init enable_intr_remapping(int eim)
+{
+ struct dmar_drhd_unit *drhd;
+ int setup = 0;
+
+ /*
+ * check for the Interrupt-remapping support
+ */
+ for_each_drhd_unit(drhd) {
+ struct intel_iommu *iommu = drhd->iommu;
+
+ if (!ecap_ir_support(iommu->ecap))
+ continue;
+
+ if (eim && !ecap_eim_support(iommu->ecap)) {
+ printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, "
+ " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap);
+ return -1;
+ }
+ }
+
+ /*
+ * Enable queued invalidation for all the DRHD's.
+ */
+ for_each_drhd_unit(drhd) {
+ int ret;
+ struct intel_iommu *iommu = drhd->iommu;
+ ret = dmar_enable_qi(iommu);
+
+ if (ret) {
+ printk(KERN_ERR "DRHD %Lx: failed to enable queued, "
+ " invalidation, ecap %Lx, ret %d\n",
+ drhd->reg_base_addr, iommu->ecap, ret);
+ return -1;
+ }
+ }
+
+ /*
+ * Setup Interrupt-remapping for all the DRHD's now.
+ */
+ for_each_drhd_unit(drhd) {
+ struct intel_iommu *iommu = drhd->iommu;
+
+ if (!ecap_ir_support(iommu->ecap))
+ continue;
+
+ if (setup_intr_remapping(iommu, eim))
+ goto error;
+
+ setup = 1;
+ }
+
+ if (!setup)
+ goto error;
+
+ intr_remapping_enabled = 1;
+
+ return 0;
+
+error:
+ /*
+ * handle error condition gracefully here!
+ */
+ return -1;
+}
+
+static int ir_parse_ioapic_scope(struct acpi_dmar_header *header,
+ struct intel_iommu *iommu)
+{
+ struct acpi_dmar_hardware_unit *drhd;
+ struct acpi_dmar_device_scope *scope;
+ void *start, *end;
+
+ drhd = (struct acpi_dmar_hardware_unit *)header;
+
+ start = (void *)(drhd + 1);
+ end = ((void *)drhd) + header->length;
+
+ while (start < end) {
+ scope = start;
+ if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_IOAPIC) {
+ if (ir_ioapic_num == MAX_IO_APICS) {
+ printk(KERN_WARNING "Exceeded Max IO APICS\n");
+ return -1;
+ }
+
+ printk(KERN_INFO "IOAPIC id %d under DRHD base"
+ " 0x%Lx\n", scope->enumeration_id,
+ drhd->address);
+
+ ir_ioapic[ir_ioapic_num].iommu = iommu;
+ ir_ioapic[ir_ioapic_num].id = scope->enumeration_id;
+ ir_ioapic_num++;
+ }
+ start += scope->length;
+ }
+
+ return 0;
+}
+
+/*
+ * Finds the assocaition between IOAPIC's and its Interrupt-remapping
+ * hardware unit.
+ */
+int __init parse_ioapics_under_ir(void)
+{
+ struct dmar_drhd_unit *drhd;
+ int ir_supported = 0;
+
+ for_each_drhd_unit(drhd) {
+ struct intel_iommu *iommu = drhd->iommu;
+
+ if (ecap_ir_support(iommu->ecap)) {
+ if (ir_parse_ioapic_scope(drhd->hdr, iommu))
+ return -1;
+
+ ir_supported = 1;
+ }
+ }
+
+ if (ir_supported && ir_ioapic_num != nr_ioapics) {
+ printk(KERN_WARNING
+ "Not all IO-APIC's listed under remapping hardware\n");
+ return -1;
+ }
+
+ return ir_supported;
+}
diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h
new file mode 100644
index 000000000000..05f2635bbe4e
--- /dev/null
+++ b/drivers/pci/intr_remapping.h
@@ -0,0 +1,8 @@
+#include "intel-iommu.h"
+
+struct ioapic_scope {
+ struct intel_iommu *iommu;
+ unsigned int id;
+};
+
+#define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 8c61304cbb37..4a10b5624f72 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -70,12 +70,10 @@ arch_teardown_msi_irqs(struct pci_dev *dev)
}
}
-static void msi_set_enable(struct pci_dev *dev, int enable)
+static void __msi_set_enable(struct pci_dev *dev, int pos, int enable)
{
- int pos;
u16 control;
- pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
if (pos) {
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
control &= ~PCI_MSI_FLAGS_ENABLE;
@@ -85,6 +83,11 @@ static void msi_set_enable(struct pci_dev *dev, int enable)
}
}
+static void msi_set_enable(struct pci_dev *dev, int enable)
+{
+ __msi_set_enable(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), enable);
+}
+
static void msix_set_enable(struct pci_dev *dev, int enable)
{
int pos;
@@ -123,7 +126,16 @@ static void msix_flush_writes(unsigned int irq)
}
}
-static void msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag)
+/*
+ * PCI 2.3 does not specify mask bits for each MSI interrupt. Attempting to
+ * mask all MSI interrupts by clearing the MSI enable bit does not work
+ * reliably as devices without an INTx disable bit will then generate a
+ * level IRQ which will never be cleared.
+ *
+ * Returns 1 if it succeeded in masking the interrupt and 0 if the device
+ * doesn't support MSI masking.
+ */
+static int msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag)
{
struct msi_desc *entry;
@@ -141,7 +153,7 @@ static void msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag)
mask_bits |= flag & mask;
pci_write_config_dword(entry->dev, pos, mask_bits);
} else {
- msi_set_enable(entry->dev, !flag);
+ return 0;
}
break;
case PCI_CAP_ID_MSIX:
@@ -157,6 +169,7 @@ static void msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag)
break;
}
entry->msi_attrib.masked = !!flag;
+ return 1;
}
void read_msi_msg(unsigned int irq, struct msi_msg *msg)
@@ -295,9 +308,8 @@ static void __pci_restore_msi_state(struct pci_dev *dev)
entry->msi_attrib.masked);
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
- control &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
- if (entry->msi_attrib.maskbit || !entry->msi_attrib.masked)
- control |= PCI_MSI_FLAGS_ENABLE;
+ control &= ~PCI_MSI_FLAGS_QSIZE;
+ control |= PCI_MSI_FLAGS_ENABLE;
pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
}
@@ -561,9 +573,8 @@ int pci_enable_msi(struct pci_dev* dev)
/* Check whether driver already requested for MSI-X irqs */
if (dev->msix_enabled) {
- printk(KERN_INFO "PCI: %s: Can't enable MSI. "
- "Device already has MSI-X enabled\n",
- pci_name(dev));
+ dev_info(&dev->dev, "can't enable MSI "
+ "(MSI-X already enabled)\n");
return -EINVAL;
}
status = msi_capability_init(dev);
@@ -686,9 +697,8 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
/* Check whether driver already requested for MSI irq */
if (dev->msi_enabled) {
- printk(KERN_INFO "PCI: %s: Can't enable MSI-X. "
- "Device already has an MSI irq assigned\n",
- pci_name(dev));
+ dev_info(&dev->dev, "can't enable MSI-X "
+ "(MSI IRQ already assigned)\n");
return -EINVAL;
}
status = msix_capability_init(dev, entries, nvec);
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 9d6fc8e6285d..89a2f0fa10f9 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -11,6 +11,7 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/module.h>
+#include <linux/pci-aspm.h>
#include <acpi/acpi.h>
#include <acpi/acnamesp.h>
#include <acpi/acresrc.h>
@@ -21,12 +22,19 @@
struct acpi_osc_data {
acpi_handle handle;
- u32 ctrlset_buf[3];
- u32 global_ctrlsets;
+ u32 support_set;
+ u32 control_set;
+ int is_queried;
+ u32 query_result;
struct list_head sibiling;
};
static LIST_HEAD(acpi_osc_data_list);
+struct acpi_osc_args {
+ u32 capbuf[3];
+ u32 query_result;
+};
+
static struct acpi_osc_data *acpi_get_osc_data(acpi_handle handle)
{
struct acpi_osc_data *data;
@@ -44,42 +52,18 @@ static struct acpi_osc_data *acpi_get_osc_data(acpi_handle handle)
return data;
}
-static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
+static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40,
+ 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
-static acpi_status
-acpi_query_osc (
- acpi_handle handle,
- u32 level,
- void *context,
- void **retval )
+static acpi_status acpi_run_osc(acpi_handle handle,
+ struct acpi_osc_args *osc_args)
{
- acpi_status status;
- struct acpi_object_list input;
- union acpi_object in_params[4];
- struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
- union acpi_object *out_obj;
- u32 osc_dw0;
- acpi_status *ret_status = (acpi_status *)retval;
- struct acpi_osc_data *osc_data;
- u32 flags = (unsigned long)context, temp;
- acpi_handle tmp;
-
- status = acpi_get_handle(handle, "_OSC", &tmp);
- if (ACPI_FAILURE(status))
- return status;
-
- osc_data = acpi_get_osc_data(handle);
- if (!osc_data) {
- printk(KERN_ERR "acpi osc data array is full\n");
- return AE_ERROR;
- }
-
- osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] |= (flags & OSC_SUPPORT_MASKS);
-
- /* do _OSC query for all possible controls */
- temp = osc_data->ctrlset_buf[OSC_CONTROL_TYPE];
- osc_data->ctrlset_buf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
- osc_data->ctrlset_buf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
+ acpi_status status;
+ struct acpi_object_list input;
+ union acpi_object in_params[4];
+ struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *out_obj;
+ u32 osc_dw0, flags = osc_args->capbuf[OSC_QUERY_TYPE];
/* Setting up input parameters */
input.count = 4;
@@ -93,20 +77,19 @@ acpi_query_osc (
in_params[2].integer.value = 3;
in_params[3].type = ACPI_TYPE_BUFFER;
in_params[3].buffer.length = 12;
- in_params[3].buffer.pointer = (u8 *)osc_data->ctrlset_buf;
+ in_params[3].buffer.pointer = (u8 *)osc_args->capbuf;
status = acpi_evaluate_object(handle, "_OSC", &input, &output);
if (ACPI_FAILURE(status))
- goto out_nofree;
- out_obj = output.pointer;
+ return status;
+ out_obj = output.pointer;
if (out_obj->type != ACPI_TYPE_BUFFER) {
- printk(KERN_DEBUG
- "Evaluate _OSC returns wrong type\n");
+ printk(KERN_DEBUG "Evaluate _OSC returns wrong type\n");
status = AE_TYPE;
- goto query_osc_out;
+ goto out_kfree;
}
- osc_dw0 = *((u32 *) out_obj->buffer.pointer);
+ osc_dw0 = *((u32 *)out_obj->buffer.pointer);
if (osc_dw0) {
if (osc_dw0 & OSC_REQUEST_ERROR)
printk(KERN_DEBUG "_OSC request fails\n");
@@ -115,93 +98,58 @@ acpi_query_osc (
if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
printk(KERN_DEBUG "_OSC invalid revision\n");
if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
- /* Update Global Control Set */
- osc_data->global_ctrlsets =
- *((u32 *)(out_obj->buffer.pointer + 8));
- status = AE_OK;
- goto query_osc_out;
+ if (flags & OSC_QUERY_ENABLE)
+ goto out_success;
+ printk(KERN_DEBUG "_OSC FW not grant req. control\n");
+ status = AE_SUPPORT;
+ goto out_kfree;
}
status = AE_ERROR;
- goto query_osc_out;
+ goto out_kfree;
}
-
- /* Update Global Control Set */
- osc_data->global_ctrlsets = *((u32 *)(out_obj->buffer.pointer + 8));
+out_success:
+ if (flags & OSC_QUERY_ENABLE)
+ osc_args->query_result =
+ *((u32 *)(out_obj->buffer.pointer + 8));
status = AE_OK;
-query_osc_out:
+out_kfree:
kfree(output.pointer);
-out_nofree:
- *ret_status = status;
-
- osc_data->ctrlset_buf[OSC_QUERY_TYPE] = !OSC_QUERY_ENABLE;
- osc_data->ctrlset_buf[OSC_CONTROL_TYPE] = temp;
- if (ACPI_FAILURE(status)) {
- /* no osc support at all */
- osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] = 0;
- }
-
return status;
}
-
-static acpi_status
-acpi_run_osc (
- acpi_handle handle,
- void *context)
+static acpi_status acpi_query_osc(acpi_handle handle,
+ u32 level, void *context, void **retval)
{
- acpi_status status;
- struct acpi_object_list input;
- union acpi_object in_params[4];
- struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
- union acpi_object *out_obj;
- u32 osc_dw0;
-
- /* Setting up input parameters */
- input.count = 4;
- input.pointer = in_params;
- in_params[0].type = ACPI_TYPE_BUFFER;
- in_params[0].buffer.length = 16;
- in_params[0].buffer.pointer = OSC_UUID;
- in_params[1].type = ACPI_TYPE_INTEGER;
- in_params[1].integer.value = 1;
- in_params[2].type = ACPI_TYPE_INTEGER;
- in_params[2].integer.value = 3;
- in_params[3].type = ACPI_TYPE_BUFFER;
- in_params[3].buffer.length = 12;
- in_params[3].buffer.pointer = (u8 *)context;
+ acpi_status status;
+ struct acpi_osc_data *osc_data;
+ u32 flags = (unsigned long)context, support_set;
+ acpi_handle tmp;
+ struct acpi_osc_args osc_args;
- status = acpi_evaluate_object(handle, "_OSC", &input, &output);
- if (ACPI_FAILURE (status))
+ status = acpi_get_handle(handle, "_OSC", &tmp);
+ if (ACPI_FAILURE(status))
return status;
- out_obj = output.pointer;
- if (out_obj->type != ACPI_TYPE_BUFFER) {
- printk(KERN_DEBUG
- "Evaluate _OSC returns wrong type\n");
- status = AE_TYPE;
- goto run_osc_out;
+ osc_data = acpi_get_osc_data(handle);
+ if (!osc_data) {
+ printk(KERN_ERR "acpi osc data array is full\n");
+ return AE_ERROR;
}
- osc_dw0 = *((u32 *) out_obj->buffer.pointer);
- if (osc_dw0) {
- if (osc_dw0 & OSC_REQUEST_ERROR)
- printk(KERN_DEBUG "_OSC request fails\n");
- if (osc_dw0 & OSC_INVALID_UUID_ERROR)
- printk(KERN_DEBUG "_OSC invalid UUID\n");
- if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
- printk(KERN_DEBUG "_OSC invalid revision\n");
- if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
- printk(KERN_DEBUG "_OSC FW not grant req. control\n");
- status = AE_SUPPORT;
- goto run_osc_out;
- }
- status = AE_ERROR;
- goto run_osc_out;
+
+ /* do _OSC query for all possible controls */
+ support_set = osc_data->support_set | (flags & OSC_SUPPORT_MASKS);
+ osc_args.capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
+ osc_args.capbuf[OSC_SUPPORT_TYPE] = support_set;
+ osc_args.capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
+
+ status = acpi_run_osc(handle, &osc_args);
+ if (ACPI_SUCCESS(status)) {
+ osc_data->support_set = support_set;
+ osc_data->query_result = osc_args.query_result;
+ osc_data->is_queried = 1;
}
- status = AE_OK;
-run_osc_out:
- kfree(output.pointer);
return status;
}
@@ -215,15 +163,11 @@ run_osc_out:
**/
acpi_status __pci_osc_support_set(u32 flags, const char *hid)
{
- acpi_status retval = AE_NOT_FOUND;
-
- if (!(flags & OSC_SUPPORT_MASKS)) {
+ if (!(flags & OSC_SUPPORT_MASKS))
return AE_TYPE;
- }
- acpi_get_devices(hid,
- acpi_query_osc,
- (void *)(unsigned long)flags,
- (void **) &retval );
+
+ acpi_get_devices(hid, acpi_query_osc,
+ (void *)(unsigned long)flags, NULL);
return AE_OK;
}
@@ -236,10 +180,11 @@ acpi_status __pci_osc_support_set(u32 flags, const char *hid)
**/
acpi_status pci_osc_control_set(acpi_handle handle, u32 flags)
{
- acpi_status status;
- u32 ctrlset;
+ acpi_status status;
+ u32 ctrlset, control_set;
acpi_handle tmp;
struct acpi_osc_data *osc_data;
+ struct acpi_osc_args osc_args;
status = acpi_get_handle(handle, "_OSC", &tmp);
if (ACPI_FAILURE(status))
@@ -252,24 +197,25 @@ acpi_status pci_osc_control_set(acpi_handle handle, u32 flags)
}
ctrlset = (flags & OSC_CONTROL_MASKS);
- if (!ctrlset) {
+ if (!ctrlset)
return AE_TYPE;
- }
- if (osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] &&
- ((osc_data->global_ctrlsets & ctrlset) != ctrlset)) {
+
+ if (osc_data->is_queried &&
+ ((osc_data->query_result & ctrlset) != ctrlset))
return AE_SUPPORT;
- }
- osc_data->ctrlset_buf[OSC_CONTROL_TYPE] |= ctrlset;
- status = acpi_run_osc(handle, osc_data->ctrlset_buf);
- if (ACPI_FAILURE (status)) {
- osc_data->ctrlset_buf[OSC_CONTROL_TYPE] &= ~ctrlset;
- }
-
+
+ control_set = osc_data->control_set | ctrlset;
+ osc_args.capbuf[OSC_QUERY_TYPE] = 0;
+ osc_args.capbuf[OSC_SUPPORT_TYPE] = osc_data->support_set;
+ osc_args.capbuf[OSC_CONTROL_TYPE] = control_set;
+ status = acpi_run_osc(handle, &osc_args);
+ if (ACPI_SUCCESS(status))
+ osc_data->control_set = control_set;
+
return status;
}
EXPORT_SYMBOL(pci_osc_control_set);
-#ifdef CONFIG_ACPI_SLEEP
/*
* _SxD returns the D-state with the highest power
* (lowest D-state number) supported in the S-state "x".
@@ -293,13 +239,11 @@ EXPORT_SYMBOL(pci_osc_control_set);
* choose highest power _SxD or any lower power
*/
-static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev,
- pm_message_t state)
+static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
{
int acpi_state;
- acpi_state = acpi_pm_device_sleep_state(&pdev->dev,
- device_may_wakeup(&pdev->dev), NULL);
+ acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL);
if (acpi_state < 0)
return PCI_POWER_ERROR;
@@ -315,7 +259,13 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev,
}
return PCI_POWER_ERROR;
}
-#endif
+
+static bool acpi_pci_power_manageable(struct pci_dev *dev)
+{
+ acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+
+ return handle ? acpi_bus_power_manageable(handle) : false;
+}
static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
{
@@ -328,12 +278,11 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
[PCI_D3hot] = ACPI_STATE_D3,
[PCI_D3cold] = ACPI_STATE_D3
};
+ int error = -EINVAL;
- if (!handle)
- return -ENODEV;
/* If the ACPI device has _EJ0, ignore the device */
- if (ACPI_SUCCESS(acpi_get_handle(handle, "_EJ0", &tmp)))
- return 0;
+ if (!handle || ACPI_SUCCESS(acpi_get_handle(handle, "_EJ0", &tmp)))
+ return -ENODEV;
switch (state) {
case PCI_D0:
@@ -341,11 +290,41 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
case PCI_D2:
case PCI_D3hot:
case PCI_D3cold:
- return acpi_bus_set_power(handle, state_conv[state]);
+ error = acpi_bus_set_power(handle, state_conv[state]);
}
- return -EINVAL;
+
+ if (!error)
+ dev_printk(KERN_INFO, &dev->dev,
+ "power state changed by ACPI to D%d\n", state);
+
+ return error;
+}
+
+static bool acpi_pci_can_wakeup(struct pci_dev *dev)
+{
+ acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+
+ return handle ? acpi_bus_can_wakeup(handle) : false;
+}
+
+static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
+{
+ int error = acpi_pm_device_sleep_wake(&dev->dev, enable);
+
+ if (!error)
+ dev_printk(KERN_INFO, &dev->dev,
+ "wake-up capability %s by ACPI\n",
+ enable ? "enabled" : "disabled");
+ return error;
}
+static struct pci_platform_pm_ops acpi_pci_platform_pm = {
+ .is_manageable = acpi_pci_power_manageable,
+ .set_state = acpi_pci_set_power_state,
+ .choose_state = acpi_pci_choose_state,
+ .can_wakeup = acpi_pci_can_wakeup,
+ .sleep_wake = acpi_pci_sleep_wake,
+};
/* ACPI bus type */
static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
@@ -394,13 +373,16 @@ static int __init acpi_pci_init(void)
printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n");
pci_no_msi();
}
+
+ if (acpi_gbl_FADT.boot_flags & BAF_PCIE_ASPM_CONTROL) {
+ printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n");
+ pcie_no_aspm();
+ }
+
ret = register_acpi_bus_type(&acpi_pci_bus);
if (ret)
return 0;
-#ifdef CONFIG_ACPI_SLEEP
- platform_pci_choose_state = acpi_pci_choose_state;
-#endif
- platform_pci_set_power_state = acpi_pci_set_power_state;
+ pci_set_platform_pm(&acpi_pci_platform_pm);
return 0;
}
arch_initcall(acpi_pci_init);
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index e1637bd82b8e..a13f53486114 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -274,7 +274,57 @@ static int pci_device_remove(struct device * dev)
return 0;
}
-static int pci_device_suspend(struct device * dev, pm_message_t state)
+static void pci_device_shutdown(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+
+ if (drv && drv->shutdown)
+ drv->shutdown(pci_dev);
+ pci_msi_shutdown(pci_dev);
+ pci_msix_shutdown(pci_dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+/*
+ * Default "suspend" method for devices that have no driver provided suspend,
+ * or not even a driver at all.
+ */
+static void pci_default_pm_suspend(struct pci_dev *pci_dev)
+{
+ pci_save_state(pci_dev);
+ /*
+ * mark its power state as "unknown", since we don't know if
+ * e.g. the BIOS will change its device state when we suspend.
+ */
+ if (pci_dev->current_state == PCI_D0)
+ pci_dev->current_state = PCI_UNKNOWN;
+}
+
+/*
+ * Default "resume" method for devices that have no driver provided resume,
+ * or not even a driver at all.
+ */
+static int pci_default_pm_resume(struct pci_dev *pci_dev)
+{
+ int retval = 0;
+
+ /* restore the PCI config space */
+ pci_restore_state(pci_dev);
+ /* if the device was enabled before suspend, reenable */
+ retval = pci_reenable_device(pci_dev);
+ /*
+ * if the device was busmaster before the suspend, make it busmaster
+ * again
+ */
+ if (pci_dev->is_busmaster)
+ pci_set_master(pci_dev);
+
+ return retval;
+}
+
+static int pci_legacy_suspend(struct device *dev, pm_message_t state)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
@@ -284,18 +334,12 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
i = drv->suspend(pci_dev, state);
suspend_report_result(drv->suspend, i);
} else {
- pci_save_state(pci_dev);
- /*
- * mark its power state as "unknown", since we don't know if
- * e.g. the BIOS will change its device state when we suspend.
- */
- if (pci_dev->current_state == PCI_D0)
- pci_dev->current_state = PCI_UNKNOWN;
+ pci_default_pm_suspend(pci_dev);
}
return i;
}
-static int pci_device_suspend_late(struct device * dev, pm_message_t state)
+static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
@@ -308,26 +352,7 @@ static int pci_device_suspend_late(struct device * dev, pm_message_t state)
return i;
}
-/*
- * Default resume method for devices that have no driver provided resume,
- * or not even a driver at all.
- */
-static int pci_default_resume(struct pci_dev *pci_dev)
-{
- int retval = 0;
-
- /* restore the PCI config space */
- pci_restore_state(pci_dev);
- /* if the device was enabled before suspend, reenable */
- retval = pci_reenable_device(pci_dev);
- /* if the device was busmaster before the suspend, make it busmaster again */
- if (pci_dev->is_busmaster)
- pci_set_master(pci_dev);
-
- return retval;
-}
-
-static int pci_device_resume(struct device * dev)
+static int pci_legacy_resume(struct device *dev)
{
int error;
struct pci_dev * pci_dev = to_pci_dev(dev);
@@ -336,34 +361,313 @@ static int pci_device_resume(struct device * dev)
if (drv && drv->resume)
error = drv->resume(pci_dev);
else
- error = pci_default_resume(pci_dev);
+ error = pci_default_pm_resume(pci_dev);
return error;
}
-static int pci_device_resume_early(struct device * dev)
+static int pci_legacy_resume_early(struct device *dev)
{
int error = 0;
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
- pci_fixup_device(pci_fixup_resume, pci_dev);
-
if (drv && drv->resume_early)
error = drv->resume_early(pci_dev);
return error;
}
-static void pci_device_shutdown(struct device *dev)
+static int pci_pm_prepare(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm && drv->pm->prepare)
+ error = drv->pm->prepare(dev);
+
+ return error;
+}
+
+static void pci_pm_complete(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+
+ if (drv && drv->pm && drv->pm->complete)
+ drv->pm->complete(dev);
+}
+
+#ifdef CONFIG_SUSPEND
+
+static int pci_pm_suspend(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->suspend) {
+ error = drv->pm->suspend(dev);
+ suspend_report_result(drv->pm->suspend, error);
+ } else {
+ pci_default_pm_suspend(pci_dev);
+ }
+ } else {
+ error = pci_legacy_suspend(dev, PMSG_SUSPEND);
+ }
+ pci_fixup_device(pci_fixup_suspend, pci_dev);
+
+ return error;
+}
+
+static int pci_pm_suspend_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
- if (drv && drv->shutdown)
- drv->shutdown(pci_dev);
- pci_msi_shutdown(pci_dev);
- pci_msix_shutdown(pci_dev);
+ if (drv && drv->pm) {
+ if (drv->pm->suspend_noirq) {
+ error = drv->pm->suspend_noirq(dev);
+ suspend_report_result(drv->pm->suspend_noirq, error);
+ }
+ } else {
+ error = pci_legacy_suspend_late(dev, PMSG_SUSPEND);
+ }
+
+ return error;
}
+static int pci_pm_resume(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+ int error;
+
+ pci_fixup_device(pci_fixup_resume, pci_dev);
+
+ if (drv && drv->pm) {
+ error = drv->pm->resume ? drv->pm->resume(dev) :
+ pci_default_pm_resume(pci_dev);
+ } else {
+ error = pci_legacy_resume(dev);
+ }
+
+ return error;
+}
+
+static int pci_pm_resume_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
+
+ pci_fixup_device(pci_fixup_resume_early, pci_dev);
+
+ if (drv && drv->pm) {
+ if (drv->pm->resume_noirq)
+ error = drv->pm->resume_noirq(dev);
+ } else {
+ error = pci_legacy_resume_early(dev);
+ }
+
+ return error;
+}
+
+#else /* !CONFIG_SUSPEND */
+
+#define pci_pm_suspend NULL
+#define pci_pm_suspend_noirq NULL
+#define pci_pm_resume NULL
+#define pci_pm_resume_noirq NULL
+
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_HIBERNATION
+
+static int pci_pm_freeze(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->freeze) {
+ error = drv->pm->freeze(dev);
+ suspend_report_result(drv->pm->freeze, error);
+ } else {
+ pci_default_pm_suspend(pci_dev);
+ }
+ } else {
+ error = pci_legacy_suspend(dev, PMSG_FREEZE);
+ pci_fixup_device(pci_fixup_suspend, pci_dev);
+ }
+
+ return error;
+}
+
+static int pci_pm_freeze_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->freeze_noirq) {
+ error = drv->pm->freeze_noirq(dev);
+ suspend_report_result(drv->pm->freeze_noirq, error);
+ }
+ } else {
+ error = pci_legacy_suspend_late(dev, PMSG_FREEZE);
+ }
+
+ return error;
+}
+
+static int pci_pm_thaw(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->thaw)
+ error = drv->pm->thaw(dev);
+ } else {
+ pci_fixup_device(pci_fixup_resume, to_pci_dev(dev));
+ error = pci_legacy_resume(dev);
+ }
+
+ return error;
+}
+
+static int pci_pm_thaw_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->thaw_noirq)
+ error = drv->pm->thaw_noirq(dev);
+ } else {
+ pci_fixup_device(pci_fixup_resume_early, pci_dev);
+ error = pci_legacy_resume_early(dev);
+ }
+
+ return error;
+}
+
+static int pci_pm_poweroff(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
+
+ if (drv && drv->pm) {
+ if (drv->pm->poweroff) {
+ error = drv->pm->poweroff(dev);
+ suspend_report_result(drv->pm->poweroff, error);
+ }
+ } else {
+ error = pci_legacy_suspend(dev, PMSG_HIBERNATE);
+ }
+
+ return error;
+}
+
+static int pci_pm_poweroff_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->poweroff_noirq) {
+ error = drv->pm->poweroff_noirq(dev);
+ suspend_report_result(drv->pm->poweroff_noirq, error);
+ }
+ } else {
+ error = pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
+ }
+
+ return error;
+}
+
+static int pci_pm_restore(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+ int error;
+
+ if (drv && drv->pm) {
+ error = drv->pm->restore ? drv->pm->restore(dev) :
+ pci_default_pm_resume(pci_dev);
+ } else {
+ error = pci_legacy_resume(dev);
+ }
+ pci_fixup_device(pci_fixup_resume, pci_dev);
+
+ return error;
+}
+
+static int pci_pm_restore_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
+
+ pci_fixup_device(pci_fixup_resume, pci_dev);
+
+ if (drv && drv->pm) {
+ if (drv->pm->restore_noirq)
+ error = drv->pm->restore_noirq(dev);
+ } else {
+ error = pci_legacy_resume_early(dev);
+ }
+ pci_fixup_device(pci_fixup_resume_early, pci_dev);
+
+ return error;
+}
+
+#else /* !CONFIG_HIBERNATION */
+
+#define pci_pm_freeze NULL
+#define pci_pm_freeze_noirq NULL
+#define pci_pm_thaw NULL
+#define pci_pm_thaw_noirq NULL
+#define pci_pm_poweroff NULL
+#define pci_pm_poweroff_noirq NULL
+#define pci_pm_restore NULL
+#define pci_pm_restore_noirq NULL
+
+#endif /* !CONFIG_HIBERNATION */
+
+struct pm_ext_ops pci_pm_ops = {
+ .base = {
+ .prepare = pci_pm_prepare,
+ .complete = pci_pm_complete,
+ .suspend = pci_pm_suspend,
+ .resume = pci_pm_resume,
+ .freeze = pci_pm_freeze,
+ .thaw = pci_pm_thaw,
+ .poweroff = pci_pm_poweroff,
+ .restore = pci_pm_restore,
+ },
+ .suspend_noirq = pci_pm_suspend_noirq,
+ .resume_noirq = pci_pm_resume_noirq,
+ .freeze_noirq = pci_pm_freeze_noirq,
+ .thaw_noirq = pci_pm_thaw_noirq,
+ .poweroff_noirq = pci_pm_poweroff_noirq,
+ .restore_noirq = pci_pm_restore_noirq,
+};
+
+#define PCI_PM_OPS_PTR &pci_pm_ops
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define PCI_PM_OPS_PTR NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
/**
* __pci_register_driver - register a new pci driver
* @drv: the driver structure to register
@@ -386,6 +690,9 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
drv->driver.owner = owner;
drv->driver.mod_name = mod_name;
+ if (drv->pm)
+ drv->driver.pm = &drv->pm->base;
+
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
@@ -511,12 +818,9 @@ struct bus_type pci_bus_type = {
.uevent = pci_uevent,
.probe = pci_device_probe,
.remove = pci_device_remove,
- .suspend = pci_device_suspend,
- .suspend_late = pci_device_suspend_late,
- .resume_early = pci_device_resume_early,
- .resume = pci_device_resume,
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
+ .pm = PCI_PM_OPS_PTR,
};
static int __init pci_driver_init(void)
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 6f3c7446c329..77baff022f71 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/stat.h>
#include <linux/topology.h>
@@ -484,6 +485,21 @@ pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr,
#endif /* HAVE_PCI_LEGACY */
#ifdef HAVE_PCI_MMAP
+
+static int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma)
+{
+ unsigned long nr, start, size;
+
+ nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ start = vma->vm_pgoff;
+ size = pci_resource_len(pdev, resno) >> PAGE_SHIFT;
+ if (start < size && size - start >= nr)
+ return 1;
+ WARN(1, "process \"%s\" tried to map 0x%08lx-0x%08lx on %s BAR %d (size 0x%08lx)\n",
+ current->comm, start, start+nr, pci_name(pdev), resno, size);
+ return 0;
+}
+
/**
* pci_mmap_resource - map a PCI resource into user memory space
* @kobj: kobject for mapping
@@ -510,6 +526,9 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
if (i >= PCI_ROM_RESOURCE)
return -ENODEV;
+ if (!pci_mmap_fits(pdev, i, vma))
+ return -EINVAL;
+
/* pci_mmap_page_range() expects the same kind of entry as coming
* from /proc/bus/pci/ which is a "user visible" value. If this is
* different from the resource itself, arch will do necessary fixup.
@@ -736,9 +755,9 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
if (attr) {
pdev->vpd->attr = attr;
- attr->size = pdev->vpd->ops->get_size(pdev);
+ attr->size = pdev->vpd->len;
attr->attr.name = "vpd";
- attr->attr.mode = S_IRUGO | S_IWUSR;
+ attr->attr.mode = S_IRUSR | S_IWUSR;
attr->read = pci_read_vpd;
attr->write = pci_write_vpd;
retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index e4548ab2a93c..c9884bba22de 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1,6 +1,4 @@
/*
- * $Id: pci.c,v 1.91 1999/01/21 13:34:01 davem Exp $
- *
* PCI Bus Services, see include/linux/pci.h for further explanation.
*
* Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
@@ -19,6 +17,7 @@
#include <linux/string.h>
#include <linux/log2.h>
#include <linux/pci-aspm.h>
+#include <linux/pm_wakeup.h>
#include <asm/dma.h> /* isa_dma_bridge_buggy */
#include "pci.h"
@@ -378,74 +377,90 @@ pci_restore_bars(struct pci_dev *dev)
pci_update_resource(dev, &dev->resource[i], i);
}
-int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t t);
+static struct pci_platform_pm_ops *pci_platform_pm;
-/**
- * pci_set_power_state - Set the power state of a PCI device
- * @dev: PCI device to be suspended
- * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering
- *
- * Transition a device to a new power state, using the Power Management
- * Capabilities in the device's config space.
- *
- * RETURN VALUE:
- * -EINVAL if trying to enter a lower state than we're already in.
- * 0 if we're already in the requested state.
- * -EIO if device does not support PCI PM.
- * 0 if we can successfully change the power state.
- */
-int
-pci_set_power_state(struct pci_dev *dev, pci_power_t state)
+int pci_set_platform_pm(struct pci_platform_pm_ops *ops)
{
- int pm, need_restore = 0;
- u16 pmcsr, pmc;
+ if (!ops->is_manageable || !ops->set_state || !ops->choose_state
+ || !ops->sleep_wake || !ops->can_wakeup)
+ return -EINVAL;
+ pci_platform_pm = ops;
+ return 0;
+}
- /* bound the state we're entering */
- if (state > PCI_D3hot)
- state = PCI_D3hot;
+static inline bool platform_pci_power_manageable(struct pci_dev *dev)
+{
+ return pci_platform_pm ? pci_platform_pm->is_manageable(dev) : false;
+}
- /*
- * If the device or the parent bridge can't support PCI PM, ignore
- * the request if we're doing anything besides putting it into D0
- * (which would only happen on boot).
- */
- if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
- return 0;
+static inline int platform_pci_set_power_state(struct pci_dev *dev,
+ pci_power_t t)
+{
+ return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS;
+}
- /* find PCI PM capability in list */
- pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev)
+{
+ return pci_platform_pm ?
+ pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR;
+}
- /* abort if the device doesn't support PM capabilities */
- if (!pm)
+static inline bool platform_pci_can_wakeup(struct pci_dev *dev)
+{
+ return pci_platform_pm ? pci_platform_pm->can_wakeup(dev) : false;
+}
+
+static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable)
+{
+ return pci_platform_pm ?
+ pci_platform_pm->sleep_wake(dev, enable) : -ENODEV;
+}
+
+/**
+ * pci_raw_set_power_state - Use PCI PM registers to set the power state of
+ * given PCI device
+ * @dev: PCI device to handle.
+ * @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
+ *
+ * RETURN VALUE:
+ * -EINVAL if the requested state is invalid.
+ * -EIO if device does not support PCI PM or its PM capabilities register has a
+ * wrong version, or device doesn't support the requested state.
+ * 0 if device already is in the requested state.
+ * 0 if device's power state has been successfully changed.
+ */
+static int
+pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
+{
+ u16 pmcsr;
+ bool need_restore = false;
+
+ if (!dev->pm_cap)
return -EIO;
+ if (state < PCI_D0 || state > PCI_D3hot)
+ return -EINVAL;
+
/* Validate current state:
* Can enter D0 from any state, but if we can only go deeper
* to sleep if we're already in a low power state
*/
- if (state != PCI_D0 && dev->current_state > state) {
- printk(KERN_ERR "%s(): %s: state=%d, current state=%d\n",
- __func__, pci_name(dev), state, dev->current_state);
+ if (dev->current_state == state) {
+ /* we're already there */
+ return 0;
+ } else if (state != PCI_D0 && dev->current_state <= PCI_D3cold
+ && dev->current_state > state) {
+ dev_err(&dev->dev, "invalid power transition "
+ "(from state %d to %d)\n", dev->current_state, state);
return -EINVAL;
- } else if (dev->current_state == state)
- return 0; /* we're already there */
-
-
- pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
- if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
- printk(KERN_DEBUG
- "PCI: %s has unsupported PM cap regs version (%u)\n",
- pci_name(dev), pmc & PCI_PM_CAP_VER_MASK);
- return -EIO;
}
/* check if this device supports the desired state */
- if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
- return -EIO;
- else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))
+ if ((state == PCI_D1 && !dev->d1_support)
+ || (state == PCI_D2 && !dev->d2_support))
return -EIO;
- pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
+ pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
/* If we're (effectively) in D3, force entire word to 0.
* This doesn't affect PME_Status, disables PME_En, and
@@ -461,7 +476,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
case PCI_UNKNOWN: /* Boot-up */
if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
&& !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
- need_restore = 1;
+ need_restore = true;
/* Fall-through: force to D0 */
default:
pmcsr = 0;
@@ -469,7 +484,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
}
/* enter specified state */
- pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr);
+ pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
/* Mandatory power management transition delays */
/* see PCI PM 1.1 5.6.1 table 18 */
@@ -478,13 +493,6 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
else if (state == PCI_D2 || dev->current_state == PCI_D2)
udelay(200);
- /*
- * Give firmware a chance to be called, such as ACPI _PRx, _PSx
- * Firmware method after native method ?
- */
- if (platform_pci_set_power_state)
- platform_pci_set_power_state(dev, state);
-
dev->current_state = state;
/* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
@@ -508,8 +516,81 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
return 0;
}
-pci_power_t (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
-
+/**
+ * pci_update_current_state - Read PCI power state of given device from its
+ * PCI PM registers and cache it
+ * @dev: PCI device to handle.
+ */
+static void pci_update_current_state(struct pci_dev *dev)
+{
+ if (dev->pm_cap) {
+ u16 pmcsr;
+
+ pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
+ dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
+ }
+}
+
+/**
+ * pci_set_power_state - Set the power state of a PCI device
+ * @dev: PCI device to handle.
+ * @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
+ *
+ * Transition a device to a new power state, using the platform formware and/or
+ * the device's PCI PM registers.
+ *
+ * RETURN VALUE:
+ * -EINVAL if the requested state is invalid.
+ * -EIO if device does not support PCI PM or its PM capabilities register has a
+ * wrong version, or device doesn't support the requested state.
+ * 0 if device already is in the requested state.
+ * 0 if device's power state has been successfully changed.
+ */
+int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
+{
+ int error;
+
+ /* bound the state we're entering */
+ if (state > PCI_D3hot)
+ state = PCI_D3hot;
+ else if (state < PCI_D0)
+ state = PCI_D0;
+ else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
+ /*
+ * If the device or the parent bridge do not support PCI PM,
+ * ignore the request if we're doing anything other than putting
+ * it into D0 (which would only happen on boot).
+ */
+ return 0;
+
+ if (state == PCI_D0 && platform_pci_power_manageable(dev)) {
+ /*
+ * Allow the platform to change the state, for example via ACPI
+ * _PR0, _PS0 and some such, but do not trust it.
+ */
+ int ret = platform_pci_set_power_state(dev, PCI_D0);
+ if (!ret)
+ pci_update_current_state(dev);
+ }
+ /* This device is quirked not to be put into D3, so
+ don't put it in D3 */
+ if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3))
+ return 0;
+
+ error = pci_raw_set_power_state(dev, state);
+
+ if (state > PCI_D0 && platform_pci_power_manageable(dev)) {
+ /* Allow the platform to finalize the transition */
+ int ret = platform_pci_set_power_state(dev, state);
+ if (!ret) {
+ pci_update_current_state(dev);
+ error = 0;
+ }
+ }
+
+ return error;
+}
+
/**
* pci_choose_state - Choose the power state of a PCI device
* @dev: PCI device to be suspended
@@ -527,11 +608,9 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
if (!pci_find_capability(dev, PCI_CAP_ID_PM))
return PCI_D0;
- if (platform_pci_choose_state) {
- ret = platform_pci_choose_state(dev, state);
- if (ret != PCI_POWER_ERROR)
- return ret;
- }
+ ret = platform_pci_choose_state(dev);
+ if (ret != PCI_POWER_ERROR)
+ return ret;
switch (state.event) {
case PM_EVENT_ON:
@@ -543,7 +622,8 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
case PM_EVENT_HIBERNATE:
return PCI_D3hot;
default:
- printk("Unrecognized suspend event %d\n", state.event);
+ dev_info(&dev->dev, "unrecognized suspend event %d\n",
+ state.event);
BUG();
}
return PCI_D0;
@@ -568,7 +648,7 @@ static int pci_save_pcie_state(struct pci_dev *dev)
else
found = 1;
if (!save_state) {
- dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+ dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n");
return -ENOMEM;
}
cap = (u16 *)&save_state->data[0];
@@ -619,7 +699,7 @@ static int pci_save_pcix_state(struct pci_dev *dev)
else
found = 1;
if (!save_state) {
- dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+ dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n");
return -ENOMEM;
}
cap = (u16 *)&save_state->data[0];
@@ -685,10 +765,9 @@ pci_restore_state(struct pci_dev *dev)
for (i = 15; i >= 0; i--) {
pci_read_config_dword(dev, i * 4, &val);
if (val != dev->saved_config_space[i]) {
- printk(KERN_DEBUG "PM: Writing back config space on "
- "device %s at offset %x (was %x, writing %x)\n",
- pci_name(dev), i,
- val, (int)dev->saved_config_space[i]);
+ dev_printk(KERN_DEBUG, &dev->dev, "restoring config "
+ "space at offset %#x (was %#x, writing %#x)\n",
+ i, val, (int)dev->saved_config_space[i]);
pci_write_config_dword(dev,i * 4,
dev->saved_config_space[i]);
}
@@ -961,6 +1040,46 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
}
/**
+ * pci_pme_capable - check the capability of PCI device to generate PME#
+ * @dev: PCI device to handle.
+ * @state: PCI state from which device will issue PME#.
+ */
+bool pci_pme_capable(struct pci_dev *dev, pci_power_t state)
+{
+ if (!dev->pm_cap)
+ return false;
+
+ return !!(dev->pme_support & (1 << state));
+}
+
+/**
+ * pci_pme_active - enable or disable PCI device's PME# function
+ * @dev: PCI device to handle.
+ * @enable: 'true' to enable PME# generation; 'false' to disable it.
+ *
+ * The caller must verify that the device is capable of generating PME# before
+ * calling this function with @enable equal to 'true'.
+ */
+void pci_pme_active(struct pci_dev *dev, bool enable)
+{
+ u16 pmcsr;
+
+ if (!dev->pm_cap)
+ return;
+
+ pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
+ /* Clear PME_Status by writing 1 to it and enable PME# */
+ pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
+ if (!enable)
+ pmcsr &= ~PCI_PM_CTRL_PME_ENABLE;
+
+ pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
+
+ dev_printk(KERN_INFO, &dev->dev, "PME# %s\n",
+ enable ? "enabled" : "disabled");
+}
+
+/**
* pci_enable_wake - enable PCI device as wakeup event source
* @dev: PCI device affected
* @state: PCI state from which device will issue wakeup events
@@ -971,66 +1090,189 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
* called automatically by this routine.
*
* Devices with legacy power management (no standard PCI PM capabilities)
- * always require such platform hooks. Depending on the platform, devices
- * supporting the standard PCI PME# signal may require such platform hooks;
- * they always update bits in config space to allow PME# generation.
+ * always require such platform hooks.
*
- * -EIO is returned if the device can't ever be a wakeup event source.
- * -EINVAL is returned if the device can't generate wakeup events from
- * the specified PCI state. Returns zero if the operation is successful.
+ * RETURN VALUE:
+ * 0 is returned on success
+ * -EINVAL is returned if device is not supposed to wake up the system
+ * Error code depending on the platform is returned if both the platform and
+ * the native mechanism fail to enable the generation of wake-up events
*/
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
{
- int pm;
- int status;
- u16 value;
-
- /* Note that drivers should verify device_may_wakeup(&dev->dev)
- * before calling this function. Platform code should report
- * errors when drivers try to enable wakeup on devices that
- * can't issue wakeups, or on which wakeups were disabled by
- * userspace updating the /sys/devices.../power/wakeup file.
+ int error = 0;
+ bool pme_done = false;
+
+ if (!device_may_wakeup(&dev->dev))
+ return -EINVAL;
+
+ /*
+ * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don
+ * Anderson we should be doing PME# wake enable followed by ACPI wake
+ * enable. To disable wake-up we call the platform first, for symmetry.
*/
- status = call_platform_enable_wakeup(&dev->dev, enable);
+ if (!enable && platform_pci_can_wakeup(dev))
+ error = platform_pci_sleep_wake(dev, false);
- /* find PCI PM capability in list */
- pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+ if (!enable || pci_pme_capable(dev, state)) {
+ pci_pme_active(dev, enable);
+ pme_done = true;
+ }
- /* If device doesn't support PM Capabilities, but caller wants to
- * disable wake events, it's a NOP. Otherwise fail unless the
- * platform hooks handled this legacy device already.
- */
- if (!pm)
- return enable ? status : 0;
+ if (enable && platform_pci_can_wakeup(dev))
+ error = platform_pci_sleep_wake(dev, true);
- /* Check device's ability to generate PME# */
- pci_read_config_word(dev,pm+PCI_PM_PMC,&value);
+ return pme_done ? 0 : error;
+}
+
+/**
+ * pci_target_state - find an appropriate low power state for a given PCI dev
+ * @dev: PCI device
+ *
+ * Use underlying platform code to find a supported low power state for @dev.
+ * If the platform can't manage @dev, return the deepest state from which it
+ * can generate wake events, based on any available PME info.
+ */
+pci_power_t pci_target_state(struct pci_dev *dev)
+{
+ pci_power_t target_state = PCI_D3hot;
- value &= PCI_PM_CAP_PME_MASK;
- value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */
+ if (platform_pci_power_manageable(dev)) {
+ /*
+ * Call the platform to choose the target state of the device
+ * and enable wake-up from this state if supported.
+ */
+ pci_power_t state = platform_pci_choose_state(dev);
- /* Check if it can generate PME# from requested state. */
- if (!value || !(value & (1 << state))) {
- /* if it can't, revert what the platform hook changed,
- * always reporting the base "EINVAL, can't PME#" error
+ switch (state) {
+ case PCI_POWER_ERROR:
+ case PCI_UNKNOWN:
+ break;
+ case PCI_D1:
+ case PCI_D2:
+ if (pci_no_d1d2(dev))
+ break;
+ default:
+ target_state = state;
+ }
+ } else if (device_may_wakeup(&dev->dev)) {
+ /*
+ * Find the deepest state from which the device can generate
+ * wake-up events, make it the target state and enable device
+ * to generate PME#.
*/
- if (enable)
- call_platform_enable_wakeup(&dev->dev, 0);
- return enable ? -EINVAL : 0;
+ if (!dev->pm_cap)
+ return PCI_POWER_ERROR;
+
+ if (dev->pme_support) {
+ while (target_state
+ && !(dev->pme_support & (1 << target_state)))
+ target_state--;
+ }
}
- pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
+ return target_state;
+}
- /* Clear PME_Status by writing 1 to it and enable PME# */
- value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
+/**
+ * pci_prepare_to_sleep - prepare PCI device for system-wide transition into a sleep state
+ * @dev: Device to handle.
+ *
+ * Choose the power state appropriate for the device depending on whether
+ * it can wake up the system and/or is power manageable by the platform
+ * (PCI_D3hot is the default) and put the device into that state.
+ */
+int pci_prepare_to_sleep(struct pci_dev *dev)
+{
+ pci_power_t target_state = pci_target_state(dev);
+ int error;
- if (!enable)
- value &= ~PCI_PM_CTRL_PME_ENABLE;
+ if (target_state == PCI_POWER_ERROR)
+ return -EIO;
- pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
+ pci_enable_wake(dev, target_state, true);
- return 0;
+ error = pci_set_power_state(dev, target_state);
+
+ if (error)
+ pci_enable_wake(dev, target_state, false);
+
+ return error;
+}
+
+/**
+ * pci_back_from_sleep - turn PCI device on during system-wide transition into working state
+ * @dev: Device to handle.
+ *
+ * Disable device's sytem wake-up capability and put it into D0.
+ */
+int pci_back_from_sleep(struct pci_dev *dev)
+{
+ pci_enable_wake(dev, PCI_D0, false);
+ return pci_set_power_state(dev, PCI_D0);
+}
+
+/**
+ * pci_pm_init - Initialize PM functions of given PCI device
+ * @dev: PCI device to handle.
+ */
+void pci_pm_init(struct pci_dev *dev)
+{
+ int pm;
+ u16 pmc;
+
+ dev->pm_cap = 0;
+
+ /* find PCI PM capability in list */
+ pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+ if (!pm)
+ return;
+ /* Check device's ability to generate PME# */
+ pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
+
+ if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
+ dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n",
+ pmc & PCI_PM_CAP_VER_MASK);
+ return;
+ }
+
+ dev->pm_cap = pm;
+
+ dev->d1_support = false;
+ dev->d2_support = false;
+ if (!pci_no_d1d2(dev)) {
+ if (pmc & PCI_PM_CAP_D1) {
+ dev_printk(KERN_DEBUG, &dev->dev, "supports D1\n");
+ dev->d1_support = true;
+ }
+ if (pmc & PCI_PM_CAP_D2) {
+ dev_printk(KERN_DEBUG, &dev->dev, "supports D2\n");
+ dev->d2_support = true;
+ }
+ }
+
+ pmc &= PCI_PM_CAP_PME_MASK;
+ if (pmc) {
+ dev_printk(KERN_INFO, &dev->dev,
+ "PME# supported from%s%s%s%s%s\n",
+ (pmc & PCI_PM_CAP_PME_D0) ? " D0" : "",
+ (pmc & PCI_PM_CAP_PME_D1) ? " D1" : "",
+ (pmc & PCI_PM_CAP_PME_D2) ? " D2" : "",
+ (pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "",
+ (pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : "");
+ dev->pme_support = pmc >> PCI_PM_CAP_PME_SHIFT;
+ /*
+ * Make device's PM flags reflect the wake-up capability, but
+ * let the user space enable it to wake up the system as needed.
+ */
+ device_set_wakeup_capable(&dev->dev, true);
+ device_set_wakeup_enable(&dev->dev, false);
+ /* Disable the PME# generation functionality */
+ pci_pme_active(dev, false);
+ } else {
+ dev->pme_support = 0;
+ }
}
int
@@ -1116,13 +1358,11 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
return 0;
err_out:
- printk (KERN_WARNING "PCI: Unable to reserve %s region #%d:%llx@%llx "
- "for device %s\n",
- pci_resource_flags(pdev, bar) & IORESOURCE_IO ? "I/O" : "mem",
- bar + 1, /* PCI BAR # */
- (unsigned long long)pci_resource_len(pdev, bar),
- (unsigned long long)pci_resource_start(pdev, bar),
- pci_name(pdev));
+ dev_warn(&pdev->dev, "BAR %d: can't reserve %s region [%#llx-%#llx]\n",
+ bar,
+ pci_resource_flags(pdev, bar) & IORESOURCE_IO ? "I/O" : "mem",
+ (unsigned long long)pci_resource_start(pdev, bar),
+ (unsigned long long)pci_resource_end(pdev, bar));
return -EBUSY;
}
@@ -1214,7 +1454,7 @@ pci_set_master(struct pci_dev *dev)
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (! (cmd & PCI_COMMAND_MASTER)) {
- pr_debug("PCI: Enabling bus mastering for device %s\n", pci_name(dev));
+ dev_dbg(&dev->dev, "enabling bus mastering\n");
cmd |= PCI_COMMAND_MASTER;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
@@ -1279,8 +1519,8 @@ pci_set_cacheline_size(struct pci_dev *dev)
if (cacheline_size == pci_cache_line_size)
return 0;
- printk(KERN_DEBUG "PCI: cache line size of %d is not supported "
- "by device %s\n", pci_cache_line_size << 2, pci_name(dev));
+ dev_printk(KERN_DEBUG, &dev->dev, "cache line size of %d is not "
+ "supported\n", pci_cache_line_size << 2);
return -EINVAL;
}
@@ -1305,8 +1545,7 @@ pci_set_mwi(struct pci_dev *dev)
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (! (cmd & PCI_COMMAND_INVALIDATE)) {
- pr_debug("PCI: Enabling Mem-Wr-Inval for device %s\n",
- pci_name(dev));
+ dev_dbg(&dev->dev, "enabling Mem-Wr-Inval\n");
cmd |= PCI_COMMAND_INVALIDATE;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
@@ -1701,6 +1940,11 @@ EXPORT_SYMBOL(pci_select_bars);
EXPORT_SYMBOL(pci_set_power_state);
EXPORT_SYMBOL(pci_save_state);
EXPORT_SYMBOL(pci_restore_state);
+EXPORT_SYMBOL(pci_pme_capable);
+EXPORT_SYMBOL(pci_pme_active);
EXPORT_SYMBOL(pci_enable_wake);
+EXPORT_SYMBOL(pci_target_state);
+EXPORT_SYMBOL(pci_prepare_to_sleep);
+EXPORT_SYMBOL(pci_back_from_sleep);
EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 0a497c1b4227..d807cd786f20 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -5,11 +5,36 @@ extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
extern void pci_cleanup_rom(struct pci_dev *dev);
-/* Firmware callbacks */
-extern pci_power_t (*platform_pci_choose_state)(struct pci_dev *dev,
- pm_message_t state);
-extern int (*platform_pci_set_power_state)(struct pci_dev *dev,
- pci_power_t state);
+/**
+ * Firmware PM callbacks
+ *
+ * @is_manageable - returns 'true' if given device is power manageable by the
+ * platform firmware
+ *
+ * @set_state - invokes the platform firmware to set the device's power state
+ *
+ * @choose_state - returns PCI power state of given device preferred by the
+ * platform; to be used during system-wide transitions from a
+ * sleeping state to the working state and vice versa
+ *
+ * @can_wakeup - returns 'true' if given device is capable of waking up the
+ * system from a sleeping state
+ *
+ * @sleep_wake - enables/disables the system wake up capability of given device
+ *
+ * If given platform is generally capable of power managing PCI devices, all of
+ * these callbacks are mandatory.
+ */
+struct pci_platform_pm_ops {
+ bool (*is_manageable)(struct pci_dev *dev);
+ int (*set_state)(struct pci_dev *dev, pci_power_t state);
+ pci_power_t (*choose_state)(struct pci_dev *dev);
+ bool (*can_wakeup)(struct pci_dev *dev);
+ int (*sleep_wake)(struct pci_dev *dev, bool enable);
+};
+
+extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
+extern void pci_pm_init(struct pci_dev *dev);
extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val);
extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val);
@@ -21,11 +46,11 @@ extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);
struct pci_vpd_ops {
int (*read)(struct pci_dev *dev, int pos, int size, char *buf);
int (*write)(struct pci_dev *dev, int pos, int size, const char *buf);
- int (*get_size)(struct pci_dev *dev);
void (*release)(struct pci_dev *dev);
};
struct pci_vpd {
+ unsigned int len;
struct pci_vpd_ops *ops;
struct bin_attribute *attr; /* descriptor for sysfs VPD entry */
};
@@ -106,3 +131,16 @@ pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
}
struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev);
+
+/* PCI slot sysfs helper code */
+#define to_pci_slot(s) container_of(s, struct pci_slot, kobj)
+
+extern struct kset *pci_slots_kset;
+
+struct pci_slot_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct pci_slot *, char *);
+ ssize_t (*store)(struct pci_slot *, const char *, size_t);
+};
+#define to_pci_slot_attr(s) container_of(s, struct pci_slot_attribute, attr)
+
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index 07c3bdb6edc2..77036f46acfe 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -26,6 +26,7 @@
#include <linux/pcieport_if.h>
#include "aerdrv.h"
+#include "../../pci.h"
/*
* Version Information
@@ -219,8 +220,7 @@ static int __devinit aer_probe (struct pcie_device *dev,
/* Alloc rpc data structure */
if (!(rpc = aer_alloc_rpc(dev))) {
- printk(KERN_DEBUG "%s: Alloc rpc fails on PCIE device[%s]\n",
- __func__, device->bus_id);
+ dev_printk(KERN_DEBUG, device, "alloc rpc failed\n");
aer_remove(dev);
return -ENOMEM;
}
@@ -228,8 +228,7 @@ static int __devinit aer_probe (struct pcie_device *dev,
/* Request IRQ ISR */
if ((status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv",
dev))) {
- printk(KERN_DEBUG "%s: Request ISR fails on PCIE device[%s]\n",
- __func__, device->bus_id);
+ dev_printk(KERN_DEBUG, device, "request IRQ failed\n");
aer_remove(dev);
return status;
}
@@ -273,7 +272,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
* to issue Configuration Requests to those devices.
*/
msleep(200);
- printk(KERN_DEBUG "Complete link reset at Root[%s]\n", dev->dev.bus_id);
+ dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
/* Enable Root Port's interrupt in response to error messages */
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c
index d39a78dbd026..6dd7b13e9808 100644
--- a/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ b/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -36,12 +36,7 @@ int aer_osc_setup(struct pcie_device *pciedev)
if (acpi_pci_disabled)
return -1;
- /* Find root host bridge */
- while (pdev->bus->self)
- pdev = pdev->bus->self;
- handle = acpi_get_pci_rootbridge_handle(
- pci_domain_nr(pdev->bus), pdev->bus->number);
-
+ handle = acpi_find_root_bridge_handle(pdev);
if (handle) {
pcie_osc_support_set(OSC_EXT_PCI_CONFIG_SUPPORT);
status = pci_osc_control_set(handle,
@@ -50,10 +45,10 @@ int aer_osc_setup(struct pcie_device *pciedev)
}
if (ACPI_FAILURE(status)) {
- printk(KERN_DEBUG "AER service couldn't init device %s - %s\n",
- pciedev->device.bus_id,
- (status == AE_SUPPORT || status == AE_NOT_FOUND) ?
- "no _OSC support" : "Run ACPI _OSC fails");
+ dev_printk(KERN_DEBUG, &pciedev->device, "AER service couldn't "
+ "init device: %s\n",
+ (status == AE_SUPPORT || status == AE_NOT_FOUND) ?
+ "no _OSC support" : "_OSC failed");
return -1;
}
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index aaa82392d1dc..ee5e7b5176d0 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -221,9 +221,9 @@ static void report_error_detected(struct pci_dev *dev, void *data)
* of a driver for this device is unaware of
* its hw state.
*/
- printk(KERN_DEBUG "Device ID[%s] has %s\n",
- dev->dev.bus_id, (dev->driver) ?
- "no AER-aware driver" : "no driver");
+ dev_printk(KERN_DEBUG, &dev->dev, "device has %s\n",
+ dev->driver ?
+ "no AER-aware driver" : "no driver");
}
return;
}
@@ -304,7 +304,7 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
{
struct aer_broadcast_data result_data;
- printk(KERN_DEBUG "Broadcast %s message\n", error_mesg);
+ dev_printk(KERN_DEBUG, &dev->dev, "broadcast %s message\n", error_mesg);
result_data.state = state;
if (cb == report_error_detected)
result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
@@ -404,18 +404,16 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
data.aer_driver =
to_service_driver(aerdev->device.driver);
} else {
- printk(KERN_DEBUG "No link-reset support to Device ID"
- "[%s]\n",
- dev->dev.bus_id);
+ dev_printk(KERN_DEBUG, &dev->dev, "no link-reset "
+ "support\n");
return PCI_ERS_RESULT_DISCONNECT;
}
}
status = data.aer_driver->reset_link(udev);
if (status != PCI_ERS_RESULT_RECOVERED) {
- printk(KERN_DEBUG "Link reset at upstream Device ID"
- "[%s] failed\n",
- udev->dev.bus_id);
+ dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream "
+ "device %s failed\n", pci_name(udev));
return PCI_ERS_RESULT_DISCONNECT;
}
@@ -511,10 +509,12 @@ static void handle_error_source(struct pcie_device * aerdev,
} else {
status = do_recovery(aerdev, dev, info.severity);
if (status == PCI_ERS_RESULT_RECOVERED) {
- printk(KERN_DEBUG "AER driver successfully recovered\n");
+ dev_printk(KERN_DEBUG, &dev->dev, "AER driver "
+ "successfully recovered\n");
} else {
/* TODO: Should kernel panic here? */
- printk(KERN_DEBUG "AER driver didn't recover\n");
+ dev_printk(KERN_DEBUG, &dev->dev, "AER driver didn't "
+ "recover\n");
}
}
}
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index f82495583e63..851f5b83cdbc 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -55,7 +55,7 @@ struct pcie_link_state {
struct endpoint_state endpoints[8];
};
-static int aspm_disabled;
+static int aspm_disabled, aspm_force;
static DEFINE_MUTEX(aspm_lock);
static LIST_HEAD(link_list);
@@ -510,6 +510,7 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
{
struct pci_dev *child_dev;
int child_pos;
+ u32 reg32;
/*
* Some functions in a slot might not all be PCIE functions, very
@@ -519,6 +520,19 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP);
if (!child_pos)
return -EINVAL;
+
+ /*
+ * Disable ASPM for pre-1.1 PCIe device, we follow MS to use
+ * RBER bit to determine if a function is 1.1 version device
+ */
+ pci_read_config_dword(child_dev, child_pos + PCI_EXP_DEVCAP,
+ &reg32);
+ if (!(reg32 & PCI_EXP_DEVCAP_RBER) && !aspm_force) {
+ printk("Pre-1.1 PCIe device detected, "
+ "disable ASPM for %s. It can be enabled forcedly"
+ " with 'pcie_aspm=force'\n", pci_name(pdev));
+ return -EINVAL;
+ }
}
return 0;
}
@@ -802,11 +816,23 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
static int __init pcie_aspm_disable(char *str)
{
- aspm_disabled = 1;
+ if (!strcmp(str, "off")) {
+ aspm_disabled = 1;
+ printk(KERN_INFO "PCIe ASPM is disabled\n");
+ } else if (!strcmp(str, "force")) {
+ aspm_force = 1;
+ printk(KERN_INFO "PCIe ASPM is forcedly enabled\n");
+ }
return 1;
}
-__setup("pcie_noaspm", pcie_aspm_disable);
+__setup("pcie_aspm=", pcie_aspm_disable);
+
+void pcie_no_aspm(void)
+{
+ if (!aspm_force)
+ aspm_disabled = 1;
+}
#ifdef CONFIG_ACPI
#include <acpi/acpi_bus.h>
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
index 3f0976868eda..359fe5568df1 100644
--- a/drivers/pci/pcie/portdrv_bus.c
+++ b/drivers/pci/pcie/portdrv_bus.c
@@ -13,6 +13,7 @@
#include <linux/pm.h>
#include <linux/pcieport_if.h>
+#include "portdrv.h"
static int pcie_port_bus_match(struct device *dev, struct device_driver *drv);
static int pcie_port_bus_suspend(struct device *dev, pm_message_t state);
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index fb0abfa508dc..890f0d2b370a 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -23,20 +23,20 @@ static int pcie_port_probe_service(struct device *dev)
{
struct pcie_device *pciedev;
struct pcie_port_service_driver *driver;
- int status = -ENODEV;
+ int status;
if (!dev || !dev->driver)
- return status;
+ return -ENODEV;
driver = to_service_driver(dev->driver);
if (!driver || !driver->probe)
- return status;
+ return -ENODEV;
pciedev = to_pcie_device(dev);
status = driver->probe(pciedev, driver->id_table);
if (!status) {
- printk(KERN_DEBUG "Load service driver %s on pcie device %s\n",
- driver->name, dev->bus_id);
+ dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n",
+ driver->name);
get_device(dev);
}
return status;
@@ -53,8 +53,8 @@ static int pcie_port_remove_service(struct device *dev)
pciedev = to_pcie_device(dev);
driver = to_service_driver(dev->driver);
if (driver && driver->remove) {
- printk(KERN_DEBUG "Unload service driver %s on pcie device %s\n",
- driver->name, dev->bus_id);
+ dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n",
+ driver->name);
driver->remove(pciedev);
put_device(dev);
}
@@ -103,7 +103,7 @@ static int pcie_port_resume_service(struct device *dev)
*/
static void release_pcie_device(struct device *dev)
{
- printk(KERN_DEBUG "Free Port Service[%s]\n", dev->bus_id);
+ dev_printk(KERN_DEBUG, dev, "free port service\n");
kfree(to_pcie_device(dev));
}
@@ -150,7 +150,7 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
if (pos) {
struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] =
{{0, 0}, {0, 1}, {0, 2}, {0, 3}};
- printk("%s Found MSIX capability\n", __func__);
+ dev_info(&dev->dev, "found MSI-X capability\n");
status = pci_enable_msix(dev, msix_entries, nvec);
if (!status) {
int j = 0;
@@ -165,7 +165,7 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
if (status) {
pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
if (pos) {
- printk("%s Found MSI capability\n", __func__);
+ dev_info(&dev->dev, "found MSI capability\n");
status = pci_enable_msi(dev);
if (!status) {
interrupt_mode = PCIE_PORT_MSI_MODE;
@@ -252,7 +252,7 @@ static struct pcie_device* alloc_pcie_device(struct pci_dev *parent,
return NULL;
pcie_device_init(parent, device, port_type, service_type, irq,irq_mode);
- printk(KERN_DEBUG "Allocate Port Service[%s]\n", device->device.bus_id);
+ dev_printk(KERN_DEBUG, &device->device, "allocate port service\n");
return device;
}
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 51d163238d93..367c9c20000d 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -91,9 +91,8 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev,
pci_set_master(dev);
if (!dev->irq && dev->pin) {
- printk(KERN_WARNING
- "%s->Dev[%04x:%04x] has invalid IRQ. Check vendor BIOS\n",
- __func__, dev->vendor, dev->device);
+ dev_warn(&dev->dev, "device [%04x/%04x] has invalid IRQ; "
+ "check vendor BIOS\n", dev->vendor, dev->device);
}
if (pcie_port_device_register(dev)) {
pci_disable_device(dev);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 3706ce7972dd..36698e57b97f 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -52,27 +52,49 @@ EXPORT_SYMBOL(no_pci_devices);
* Some platforms allow access to legacy I/O port and ISA memory space on
* a per-bus basis. This routine creates the files and ties them into
* their associated read, write and mmap files from pci-sysfs.c
+ *
+ * On error unwind, but don't propogate the error to the caller
+ * as it is ok to set up the PCI bus without these files.
*/
static void pci_create_legacy_files(struct pci_bus *b)
{
+ int error;
+
b->legacy_io = kzalloc(sizeof(struct bin_attribute) * 2,
GFP_ATOMIC);
- if (b->legacy_io) {
- b->legacy_io->attr.name = "legacy_io";
- b->legacy_io->size = 0xffff;
- b->legacy_io->attr.mode = S_IRUSR | S_IWUSR;
- b->legacy_io->read = pci_read_legacy_io;
- b->legacy_io->write = pci_write_legacy_io;
- device_create_bin_file(&b->dev, b->legacy_io);
-
- /* Allocated above after the legacy_io struct */
- b->legacy_mem = b->legacy_io + 1;
- b->legacy_mem->attr.name = "legacy_mem";
- b->legacy_mem->size = 1024*1024;
- b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR;
- b->legacy_mem->mmap = pci_mmap_legacy_mem;
- device_create_bin_file(&b->dev, b->legacy_mem);
- }
+ if (!b->legacy_io)
+ goto kzalloc_err;
+
+ b->legacy_io->attr.name = "legacy_io";
+ b->legacy_io->size = 0xffff;
+ b->legacy_io->attr.mode = S_IRUSR | S_IWUSR;
+ b->legacy_io->read = pci_read_legacy_io;
+ b->legacy_io->write = pci_write_legacy_io;
+ error = device_create_bin_file(&b->dev, b->legacy_io);
+ if (error)
+ goto legacy_io_err;
+
+ /* Allocated above after the legacy_io struct */
+ b->legacy_mem = b->legacy_io + 1;
+ b->legacy_mem->attr.name = "legacy_mem";
+ b->legacy_mem->size = 1024*1024;
+ b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR;
+ b->legacy_mem->mmap = pci_mmap_legacy_mem;
+ error = device_create_bin_file(&b->dev, b->legacy_mem);
+ if (error)
+ goto legacy_mem_err;
+
+ return;
+
+legacy_mem_err:
+ device_remove_bin_file(&b->dev, b->legacy_io);
+legacy_io_err:
+ kfree(b->legacy_io);
+ b->legacy_io = NULL;
+kzalloc_err:
+ printk(KERN_WARNING "pci: warning: could not create legacy I/O port "
+ "and ISA memory resources to sysfs\n");
+ return;
}
void pci_remove_legacy_files(struct pci_bus *b)
@@ -163,12 +185,9 @@ static inline unsigned int pci_calc_resource_flags(unsigned int flags)
return IORESOURCE_MEM;
}
-/*
- * Find the extent of a PCI decode..
- */
-static u32 pci_size(u32 base, u32 maxbase, u32 mask)
+static u64 pci_size(u64 base, u64 maxbase, u64 mask)
{
- u32 size = mask & maxbase; /* Find the significant bits */
+ u64 size = mask & maxbase; /* Find the significant bits */
if (!size)
return 0;
@@ -184,135 +203,148 @@ static u32 pci_size(u32 base, u32 maxbase, u32 mask)
return size;
}
-static u64 pci_size64(u64 base, u64 maxbase, u64 mask)
+enum pci_bar_type {
+ pci_bar_unknown, /* Standard PCI BAR probe */
+ pci_bar_io, /* An io port BAR */
+ pci_bar_mem32, /* A 32-bit memory BAR */
+ pci_bar_mem64, /* A 64-bit memory BAR */
+};
+
+static inline enum pci_bar_type decode_bar(struct resource *res, u32 bar)
{
- u64 size = mask & maxbase; /* Find the significant bits */
- if (!size)
- return 0;
+ if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+ res->flags = bar & ~PCI_BASE_ADDRESS_IO_MASK;
+ return pci_bar_io;
+ }
- /* Get the lowest of them to find the decode size, and
- from that the extent. */
- size = (size & ~(size-1)) - 1;
+ res->flags = bar & ~PCI_BASE_ADDRESS_MEM_MASK;
- /* base == maxbase can be valid only if the BAR has
- already been programmed with all 1s. */
- if (base == maxbase && ((base | size) & mask) != mask)
- return 0;
-
- return size;
+ if (res->flags == PCI_BASE_ADDRESS_MEM_TYPE_64)
+ return pci_bar_mem64;
+ return pci_bar_mem32;
}
-static inline int is_64bit_memory(u32 mask)
+/*
+ * If the type is not unknown, we assume that the lowest bit is 'enable'.
+ * Returns 1 if the BAR was 64-bit and 0 if it was 32-bit.
+ */
+static int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
+ struct resource *res, unsigned int pos)
{
- if ((mask & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
- (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64))
- return 1;
- return 0;
-}
+ u32 l, sz, mask;
-static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
-{
- unsigned int pos, reg, next;
- u32 l, sz;
- struct resource *res;
+ mask = type ? ~PCI_ROM_ADDRESS_ENABLE : ~0;
- for(pos=0; pos<howmany; pos = next) {
- u64 l64;
- u64 sz64;
- u32 raw_sz;
+ res->name = pci_name(dev);
- next = pos+1;
- res = &dev->resource[pos];
- res->name = pci_name(dev);
- reg = PCI_BASE_ADDRESS_0 + (pos << 2);
- pci_read_config_dword(dev, reg, &l);
- pci_write_config_dword(dev, reg, ~0);
- pci_read_config_dword(dev, reg, &sz);
- pci_write_config_dword(dev, reg, l);
- if (!sz || sz == 0xffffffff)
- continue;
- if (l == 0xffffffff)
- l = 0;
- raw_sz = sz;
- if ((l & PCI_BASE_ADDRESS_SPACE) ==
- PCI_BASE_ADDRESS_SPACE_MEMORY) {
- sz = pci_size(l, sz, (u32)PCI_BASE_ADDRESS_MEM_MASK);
- /*
- * For 64bit prefetchable memory sz could be 0, if the
- * real size is bigger than 4G, so we need to check
- * szhi for that.
- */
- if (!is_64bit_memory(l) && !sz)
- continue;
- res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
- res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK;
+ pci_read_config_dword(dev, pos, &l);
+ pci_write_config_dword(dev, pos, mask);
+ pci_read_config_dword(dev, pos, &sz);
+ pci_write_config_dword(dev, pos, l);
+
+ /*
+ * All bits set in sz means the device isn't working properly.
+ * If the BAR isn't implemented, all bits must be 0. If it's a
+ * memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit
+ * 1 must be clear.
+ */
+ if (!sz || sz == 0xffffffff)
+ goto fail;
+
+ /*
+ * I don't know how l can have all bits set. Copied from old code.
+ * Maybe it fixes a bug on some ancient platform.
+ */
+ if (l == 0xffffffff)
+ l = 0;
+
+ if (type == pci_bar_unknown) {
+ type = decode_bar(res, l);
+ res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN;
+ if (type == pci_bar_io) {
+ l &= PCI_BASE_ADDRESS_IO_MASK;
+ mask = PCI_BASE_ADDRESS_IO_MASK & 0xffff;
} else {
- sz = pci_size(l, sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff);
- if (!sz)
- continue;
- res->start = l & PCI_BASE_ADDRESS_IO_MASK;
- res->flags |= l & ~PCI_BASE_ADDRESS_IO_MASK;
+ l &= PCI_BASE_ADDRESS_MEM_MASK;
+ mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
}
- res->end = res->start + (unsigned long) sz;
- res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN;
- if (is_64bit_memory(l)) {
- u32 szhi, lhi;
-
- pci_read_config_dword(dev, reg+4, &lhi);
- pci_write_config_dword(dev, reg+4, ~0);
- pci_read_config_dword(dev, reg+4, &szhi);
- pci_write_config_dword(dev, reg+4, lhi);
- sz64 = ((u64)szhi << 32) | raw_sz;
- l64 = ((u64)lhi << 32) | l;
- sz64 = pci_size64(l64, sz64, PCI_BASE_ADDRESS_MEM_MASK);
- next++;
-#if BITS_PER_LONG == 64
- if (!sz64) {
- res->start = 0;
- res->end = 0;
- res->flags = 0;
- continue;
- }
- res->start = l64 & PCI_BASE_ADDRESS_MEM_MASK;
- res->end = res->start + sz64;
-#else
- if (sz64 > 0x100000000ULL) {
- printk(KERN_ERR "PCI: Unable to handle 64-bit "
- "BAR for device %s\n", pci_name(dev));
- res->start = 0;
- res->flags = 0;
- } else if (lhi) {
- /* 64-bit wide address, treat as disabled */
- pci_write_config_dword(dev, reg,
- l & ~(u32)PCI_BASE_ADDRESS_MEM_MASK);
- pci_write_config_dword(dev, reg+4, 0);
- res->start = 0;
- res->end = sz;
- }
-#endif
+ } else {
+ res->flags |= (l & IORESOURCE_ROM_ENABLE);
+ l &= PCI_ROM_ADDRESS_MASK;
+ mask = (u32)PCI_ROM_ADDRESS_MASK;
+ }
+
+ if (type == pci_bar_mem64) {
+ u64 l64 = l;
+ u64 sz64 = sz;
+ u64 mask64 = mask | (u64)~0 << 32;
+
+ pci_read_config_dword(dev, pos + 4, &l);
+ pci_write_config_dword(dev, pos + 4, ~0);
+ pci_read_config_dword(dev, pos + 4, &sz);
+ pci_write_config_dword(dev, pos + 4, l);
+
+ l64 |= ((u64)l << 32);
+ sz64 |= ((u64)sz << 32);
+
+ sz64 = pci_size(l64, sz64, mask64);
+
+ if (!sz64)
+ goto fail;
+
+ if ((sizeof(resource_size_t) < 8) && (sz64 > 0x100000000ULL)) {
+ dev_err(&dev->dev, "can't handle 64-bit BAR\n");
+ goto fail;
+ } else if ((sizeof(resource_size_t) < 8) && l) {
+ /* Address above 32-bit boundary; disable the BAR */
+ pci_write_config_dword(dev, pos, 0);
+ pci_write_config_dword(dev, pos + 4, 0);
+ res->start = 0;
+ res->end = sz64;
+ } else {
+ res->start = l64;
+ res->end = l64 + sz64;
+ printk(KERN_DEBUG "PCI: %s reg %x 64bit mmio: [%llx, %llx]\n",
+ pci_name(dev), pos, (unsigned long long)res->start,
+ (unsigned long long)res->end);
}
+ } else {
+ sz = pci_size(l, sz, mask);
+
+ if (!sz)
+ goto fail;
+
+ res->start = l;
+ res->end = l + sz;
+ printk(KERN_DEBUG "PCI: %s reg %x %s: [%llx, %llx]\n", pci_name(dev),
+ pos, (res->flags & IORESOURCE_IO) ? "io port":"32bit mmio",
+ (unsigned long long)res->start, (unsigned long long)res->end);
}
+
+ out:
+ return (type == pci_bar_mem64) ? 1 : 0;
+ fail:
+ res->flags = 0;
+ goto out;
+}
+
+static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
+{
+ unsigned int pos, reg;
+
+ for (pos = 0; pos < howmany; pos++) {
+ struct resource *res = &dev->resource[pos];
+ reg = PCI_BASE_ADDRESS_0 + (pos << 2);
+ pos += __pci_read_base(dev, pci_bar_unknown, res, reg);
+ }
+
if (rom) {
+ struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
dev->rom_base_reg = rom;
- res = &dev->resource[PCI_ROM_RESOURCE];
- res->name = pci_name(dev);
- pci_read_config_dword(dev, rom, &l);
- pci_write_config_dword(dev, rom, ~PCI_ROM_ADDRESS_ENABLE);
- pci_read_config_dword(dev, rom, &sz);
- pci_write_config_dword(dev, rom, l);
- if (l == 0xffffffff)
- l = 0;
- if (sz && sz != 0xffffffff) {
- sz = pci_size(l, sz, (u32)PCI_ROM_ADDRESS_MASK);
- if (sz) {
- res->flags = (l & IORESOURCE_ROM_ENABLE) |
- IORESOURCE_MEM | IORESOURCE_PREFETCH |
- IORESOURCE_READONLY | IORESOURCE_CACHEABLE |
- IORESOURCE_SIZEALIGN;
- res->start = l & PCI_ROM_ADDRESS_MASK;
- res->end = res->start + (unsigned long) sz;
- }
- }
+ res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH |
+ IORESOURCE_READONLY | IORESOURCE_CACHEABLE |
+ IORESOURCE_SIZEALIGN;
+ __pci_read_base(dev, pci_bar_mem32, res, rom);
}
}
@@ -329,7 +361,7 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
return;
if (dev->transparent) {
- printk(KERN_INFO "PCI: Transparent bridge - %s\n", pci_name(dev));
+ dev_info(&dev->dev, "transparent bridge\n");
for(i = 3; i < PCI_BUS_NUM_RESOURCES; i++)
child->resource[i] = child->parent->resource[i - 3];
}
@@ -357,6 +389,9 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
res->start = base;
if (!res->end)
res->end = limit + 0xfff;
+ printk(KERN_DEBUG "PCI: bridge %s io port: [%llx, %llx]\n",
+ pci_name(dev), (unsigned long long) res->start,
+ (unsigned long long) res->end);
}
res = child->resource[1];
@@ -368,6 +403,9 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
res->start = base;
res->end = limit + 0xfffff;
+ printk(KERN_DEBUG "PCI: bridge %s 32bit mmio: [%llx, %llx]\n",
+ pci_name(dev), (unsigned long long) res->start,
+ (unsigned long long) res->end);
}
res = child->resource[2];
@@ -392,7 +430,8 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
limit |= ((long) mem_limit_hi) << 32;
#else
if (mem_base_hi || mem_limit_hi) {
- printk(KERN_ERR "PCI: Unable to handle 64-bit address space for bridge %s\n", pci_name(dev));
+ dev_err(&dev->dev, "can't handle 64-bit "
+ "address space for bridge\n");
return;
}
#endif
@@ -402,6 +441,9 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH;
res->start = base;
res->end = limit + 0xfffff;
+ printk(KERN_DEBUG "PCI: bridge %s %sbit mmio pref: [%llx, %llx]\n",
+ pci_name(dev), (res->flags & PCI_PREF_RANGE_TYPE_64) ? "64" : "32",
+ (unsigned long long) res->start, (unsigned long long) res->end);
}
}
@@ -414,6 +456,7 @@ static struct pci_bus * pci_alloc_bus(void)
INIT_LIST_HEAD(&b->node);
INIT_LIST_HEAD(&b->children);
INIT_LIST_HEAD(&b->devices);
+ INIT_LIST_HEAD(&b->slots);
}
return b;
}
@@ -511,8 +554,8 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);
- pr_debug("PCI: Scanning behind PCI bridge %s, config %06x, pass %d\n",
- pci_name(dev), buses & 0xffffff, pass);
+ dev_dbg(&dev->dev, "scanning behind bridge, config %06x, pass %d\n",
+ buses & 0xffffff, pass);
/* Disable MasterAbortMode during probing to avoid reporting
of bus errors (in some architectures) */
@@ -535,8 +578,8 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
* ignore it. This can happen with the i450NX chipset.
*/
if (pci_find_bus(pci_domain_nr(bus), busnr)) {
- printk(KERN_INFO "PCI: Bus %04x:%02x already known\n",
- pci_domain_nr(bus), busnr);
+ dev_info(&dev->dev, "bus %04x:%02x already known\n",
+ pci_domain_nr(bus), busnr);
goto out;
}
@@ -711,8 +754,9 @@ static int pci_setup_device(struct pci_dev * dev)
{
u32 class;
- sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
- dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
+ dev->bus->number, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
dev->revision = class & 0xff;
@@ -720,7 +764,7 @@ static int pci_setup_device(struct pci_dev * dev)
dev->class = class;
class >>= 8;
- pr_debug("PCI: Found %s [%04x/%04x] %06x %02x\n", pci_name(dev),
+ dev_dbg(&dev->dev, "found [%04x/%04x] class %06x header type %02x\n",
dev->vendor, dev->device, class, dev->hdr_type);
/* "Unknown power state" */
@@ -788,13 +832,13 @@ static int pci_setup_device(struct pci_dev * dev)
break;
default: /* unknown header */
- printk(KERN_ERR "PCI: device %s has unknown header type %02x, ignoring.\n",
- pci_name(dev), dev->hdr_type);
+ dev_err(&dev->dev, "unknown header type %02x, "
+ "ignoring device\n", dev->hdr_type);
return -1;
bad:
- printk(KERN_ERR "PCI: %s: class %x doesn't match header type %02x. Ignoring class.\n",
- pci_name(dev), class, dev->hdr_type);
+ dev_err(&dev->dev, "ignoring class %02x (doesn't match header "
+ "type %02x)\n", class, dev->hdr_type);
dev->class = PCI_CLASS_NOT_DEFINED;
}
@@ -927,7 +971,7 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
return NULL;
/* Card hasn't responded in 60 seconds? Must be stuck. */
if (delay > 60 * 1000) {
- printk(KERN_WARNING "Device %04x:%02x:%02x.%d not "
+ printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not "
"responding\n", pci_domain_nr(bus),
bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn));
@@ -984,6 +1028,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
/* Fix up broken headers */
pci_fixup_device(pci_fixup_header, dev);
+ /* Initialize power management of the device */
+ pci_pm_init(dev);
+
/*
* Add the device to our list of discovered devices
* and the bus list for fixup functions, etc.
@@ -1047,7 +1094,8 @@ int pci_scan_slot(struct pci_bus *bus, int devfn)
}
}
- if (bus->self)
+ /* only one slot has pcie device */
+ if (bus->self && nr)
pcie_aspm_init_link_state(bus->self);
return nr;
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 963a97642ae9..e1098c302c45 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -1,6 +1,4 @@
/*
- * $Id: proc.c,v 1.13 1998/05/12 07:36:07 mj Exp $
- *
* Procfs interface for the PCI bus.
*
* Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
@@ -90,7 +88,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
if ((pos & 3) && cnt > 2) {
unsigned short val;
pci_user_read_config_word(dev, pos, &val);
- __put_user(cpu_to_le16(val), (unsigned short __user *) buf);
+ __put_user(cpu_to_le16(val), (__le16 __user *) buf);
buf += 2;
pos += 2;
cnt -= 2;
@@ -99,7 +97,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
while (cnt >= 4) {
unsigned int val;
pci_user_read_config_dword(dev, pos, &val);
- __put_user(cpu_to_le32(val), (unsigned int __user *) buf);
+ __put_user(cpu_to_le32(val), (__le32 __user *) buf);
buf += 4;
pos += 4;
cnt -= 4;
@@ -108,7 +106,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
if (cnt >= 2) {
unsigned short val;
pci_user_read_config_word(dev, pos, &val);
- __put_user(cpu_to_le16(val), (unsigned short __user *) buf);
+ __put_user(cpu_to_le16(val), (__le16 __user *) buf);
buf += 2;
pos += 2;
cnt -= 2;
@@ -158,8 +156,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
}
if ((pos & 3) && cnt > 2) {
- unsigned short val;
- __get_user(val, (unsigned short __user *) buf);
+ __le16 val;
+ __get_user(val, (__le16 __user *) buf);
pci_user_write_config_word(dev, pos, le16_to_cpu(val));
buf += 2;
pos += 2;
@@ -167,8 +165,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
}
while (cnt >= 4) {
- unsigned int val;
- __get_user(val, (unsigned int __user *) buf);
+ __le32 val;
+ __get_user(val, (__le32 __user *) buf);
pci_user_write_config_dword(dev, pos, le32_to_cpu(val));
buf += 4;
pos += 4;
@@ -176,8 +174,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
}
if (cnt >= 2) {
- unsigned short val;
- __get_user(val, (unsigned short __user *) buf);
+ __le16 val;
+ __get_user(val, (__le16 __user *) buf);
pci_user_write_config_word(dev, pos, le16_to_cpu(val));
buf += 2;
pos += 2;
@@ -482,5 +480,5 @@ static int __init pci_proc_init(void)
return 0;
}
-__initcall(pci_proc_init);
+device_initcall(pci_proc_init);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index dabb563f51d9..9236e7f869c8 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -556,7 +556,7 @@ static void quirk_via_ioapic(struct pci_dev *dev)
pci_write_config_byte (dev, 0x58, tmp);
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic);
/*
* VIA 8237: Some BIOSs don't set the 'Bypass APIC De-Assert Message' Bit.
@@ -576,7 +576,7 @@ static void quirk_via_vt8237_bypass_apic_deassert(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert);
/*
* The AMD io apic can hang the box when an apic irq is masked.
@@ -622,7 +622,7 @@ static void quirk_amd_8131_ioapic(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
#endif /* CONFIG_X86_IO_APIC */
/*
@@ -774,7 +774,7 @@ static void quirk_cardbus_legacy(struct pci_dev *dev)
pci_write_config_dword(dev, PCI_CB_LEGACY_MODE_BASE, 0);
}
DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
-DECLARE_PCI_FIXUP_RESUME(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
/*
* Following the PCI ordering rules is optional on the AMD762. I'm not
@@ -797,7 +797,7 @@ static void quirk_amd_ordering(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering);
/*
* DreamWorks provided workaround for Dunord I-3000 problem
@@ -865,7 +865,7 @@ static void quirk_disable_pxb(struct pci_dev *pdev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb);
static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev)
{
@@ -885,9 +885,9 @@ static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev)
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode);
/*
* Serverworks CSB5 IDE does not fully support native mode
@@ -923,6 +923,19 @@ static void __init quirk_ide_samemode(struct pci_dev *pdev)
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, quirk_ide_samemode);
+/*
+ * Some ATA devices break if put into D3
+ */
+
+static void __devinit quirk_no_ata_d3(struct pci_dev *pdev)
+{
+ /* Quirk the legacy ATA devices only. The AHCI ones are ok */
+ if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE)
+ pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_ANY_ID, quirk_no_ata_d3);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, PCI_ANY_ID, quirk_no_ata_d3);
+
/* This was originally an Alpha specific thing, but it really fits here.
* The i82375 PCI/EISA bridge appears as non-classified. Fix that.
*/
@@ -1054,6 +1067,20 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
* its on-board VGA controller */
asus_hides_smbus = 1;
}
+ else if (dev->device == PCI_DEVICE_ID_INTEL_82845G_IG)
+ switch(dev->subsystem_device) {
+ case 0x00b8: /* Compaq Evo D510 CMT */
+ case 0x00b9: /* Compaq Evo D510 SFF */
+ asus_hides_smbus = 1;
+ }
+ else if (dev->device == PCI_DEVICE_ID_INTEL_82815_CGC)
+ switch (dev->subsystem_device) {
+ case 0x001A: /* Compaq Deskpro EN SSF P667 815E */
+ /* Motherboard doesn't have host bridge
+ * subvendor/subdevice IDs, therefore checking
+ * its on-board VGA controller */
+ asus_hides_smbus = 1;
+ }
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845_HB, asus_hides_smbus_hostbridge);
@@ -1068,6 +1095,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82855GM_HB, as
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82915GM_HB, asus_hides_smbus_hostbridge);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3, asus_hides_smbus_hostbridge);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845G_IG, asus_hides_smbus_hostbridge);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC, asus_hides_smbus_hostbridge);
static void asus_hides_smbus_lpc(struct pci_dev *dev)
{
@@ -1093,31 +1122,61 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asu
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc);
-static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev)
+/* It appears we just have one such device. If not, we have a warning */
+static void __iomem *asus_rcba_base;
+static void asus_hides_smbus_lpc_ich6_suspend(struct pci_dev *dev)
{
- u32 val, rcba;
- void __iomem *base;
+ u32 rcba;
if (likely(!asus_hides_smbus))
return;
+ WARN_ON(asus_rcba_base);
+
pci_read_config_dword(dev, 0xF0, &rcba);
- base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); /* use bits 31:14, 16 kB aligned */
- if (base == NULL) return;
- val=readl(base + 0x3418); /* read the Function Disable register, dword mode only */
- writel(val & 0xFFFFFFF7, base + 0x3418); /* enable the SMBus device */
- iounmap(base);
+ /* use bits 31:14, 16 kB aligned */
+ asus_rcba_base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000);
+ if (asus_rcba_base == NULL)
+ return;
+}
+
+static void asus_hides_smbus_lpc_ich6_resume_early(struct pci_dev *dev)
+{
+ u32 val;
+
+ if (likely(!asus_hides_smbus || !asus_rcba_base))
+ return;
+ /* read the Function Disable register, dword mode only */
+ val = readl(asus_rcba_base + 0x3418);
+ writel(val & 0xFFFFFFF7, asus_rcba_base + 0x3418); /* enable the SMBus device */
+}
+
+static void asus_hides_smbus_lpc_ich6_resume(struct pci_dev *dev)
+{
+ if (likely(!asus_hides_smbus || !asus_rcba_base))
+ return;
+ iounmap(asus_rcba_base);
+ asus_rcba_base = NULL;
dev_info(&dev->dev, "Enabled ICH6/i801 SMBus device\n");
}
+
+static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev)
+{
+ asus_hides_smbus_lpc_ich6_suspend(dev);
+ asus_hides_smbus_lpc_ich6_resume_early(dev);
+ asus_hides_smbus_lpc_ich6_resume(dev);
+}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6);
+DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_suspend);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume_early);
/*
* SiS 96x south bridge: BIOS typically hides SMBus device...
@@ -1135,10 +1194,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus);
/*
* ... This is further complicated by the fact that some SiS96x south
@@ -1172,7 +1231,7 @@ static void quirk_sis_503(struct pci_dev *dev)
quirk_sis_96x_smbus(dev);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503);
/*
@@ -1205,7 +1264,7 @@ static void asus_hides_ac97_lpc(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc);
#if defined(CONFIG_ATA) || defined(CONFIG_ATA_MODULE)
@@ -1270,12 +1329,12 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, qui
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata);
#endif
@@ -1521,6 +1580,10 @@ extern struct pci_fixup __start_pci_fixups_enable[];
extern struct pci_fixup __end_pci_fixups_enable[];
extern struct pci_fixup __start_pci_fixups_resume[];
extern struct pci_fixup __end_pci_fixups_resume[];
+extern struct pci_fixup __start_pci_fixups_resume_early[];
+extern struct pci_fixup __end_pci_fixups_resume_early[];
+extern struct pci_fixup __start_pci_fixups_suspend[];
+extern struct pci_fixup __end_pci_fixups_suspend[];
void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
@@ -1553,6 +1616,16 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
end = __end_pci_fixups_resume;
break;
+ case pci_fixup_resume_early:
+ start = __start_pci_fixups_resume_early;
+ end = __end_pci_fixups_resume_early;
+ break;
+
+ case pci_fixup_suspend:
+ start = __start_pci_fixups_suspend;
+ end = __end_pci_fixups_suspend;
+ break;
+
default:
/* stupid compiler warning, you would think with an enum... */
return;
@@ -1629,7 +1702,7 @@ static void quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
quirk_nvidia_ck804_pcie_aer_ext_cap);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
quirk_nvidia_ck804_pcie_aer_ext_cap);
static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
@@ -1670,6 +1743,53 @@ static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching);
+/*
+ * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the
+ * VPD end tag will hang the device. This problem was initially
+ * observed when a vpd entry was created in sysfs
+ * ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry
+ * will dump 32k of data. Reading a full 32k will cause an access
+ * beyond the VPD end tag causing the device to hang. Once the device
+ * is hung, the bnx2 driver will not be able to reset the device.
+ * We believe that it is legal to read beyond the end tag and
+ * therefore the solution is to limit the read/write length.
+ */
+static void __devinit quirk_brcm_570x_limit_vpd(struct pci_dev *dev)
+{
+ /*
+ * Only disable the VPD capability for 5706, 5706S, 5708,
+ * 5708S and 5709 rev. A
+ */
+ if ((dev->device == PCI_DEVICE_ID_NX2_5706) ||
+ (dev->device == PCI_DEVICE_ID_NX2_5706S) ||
+ (dev->device == PCI_DEVICE_ID_NX2_5708) ||
+ (dev->device == PCI_DEVICE_ID_NX2_5708S) ||
+ ((dev->device == PCI_DEVICE_ID_NX2_5709) &&
+ (dev->revision & 0xf0) == 0x0)) {
+ if (dev->vpd)
+ dev->vpd->len = 0x80;
+ }
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5706,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5706S,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5708,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5708S,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5709,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5709S,
+ quirk_brcm_570x_limit_vpd);
+
#ifdef CONFIG_PCI_MSI
/* Some chipsets do not support MSI. We cannot easily rely on setting
* PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually
@@ -1685,6 +1805,7 @@ static void __init quirk_disable_all_msi(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_disable_all_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS400_200, quirk_disable_all_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS480, quirk_disable_all_msi);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3336, quirk_disable_all_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3351, quirk_disable_all_msi);
/* Disable MSI on chipsets that are known to not support it */
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index 217814fef4ef..4edfc4731bd4 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -162,7 +162,7 @@ EXPORT_SYMBOL(pci_find_slot);
* time.
*/
struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device,
- const struct pci_dev *from)
+ struct pci_dev *from)
{
struct pci_dev *pdev;
@@ -263,7 +263,7 @@ static int match_pci_dev_by_id(struct device *dev, void *data)
* this file.
*/
static struct pci_dev *pci_get_dev_by_id(const struct pci_device_id *id,
- const struct pci_dev *from)
+ struct pci_dev *from)
{
struct device *dev;
struct device *dev_start = NULL;
@@ -280,6 +280,8 @@ static struct pci_dev *pci_get_dev_by_id(const struct pci_device_id *id,
match_pci_dev_by_id);
if (dev)
pdev = to_pci_dev(dev);
+ if (from)
+ pci_dev_put(from);
return pdev;
}
@@ -301,7 +303,7 @@ static struct pci_dev *pci_get_dev_by_id(const struct pci_device_id *id,
*/
struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,
unsigned int ss_vendor, unsigned int ss_device,
- const struct pci_dev *from)
+ struct pci_dev *from)
{
struct pci_dev *pdev;
struct pci_device_id *id;
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 8ddb918f5f57..3abbfad9ddab 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -27,13 +27,6 @@
#include <linux/slab.h>
-#define DEBUG_CONFIG 1
-#if DEBUG_CONFIG
-#define DBG(x...) printk(x)
-#else
-#define DBG(x...)
-#endif
-
static void pbus_assign_resources_sorted(struct pci_bus *bus)
{
struct pci_dev *dev;
@@ -81,8 +74,8 @@ void pci_setup_cardbus(struct pci_bus *bus)
struct pci_dev *bridge = bus->self;
struct pci_bus_region region;
- printk("PCI: Bus %d, cardbus bridge: %s\n",
- bus->number, pci_name(bridge));
+ dev_info(&bridge->dev, "CardBus bridge, secondary bus %04x:%02x\n",
+ pci_domain_nr(bus), bus->number);
pcibios_resource_to_bus(bridge, &region, bus->resource[0]);
if (bus->resource[0]->flags & IORESOURCE_IO) {
@@ -90,7 +83,7 @@ void pci_setup_cardbus(struct pci_bus *bus)
* The IO resource is allocated a range twice as large as it
* would normally need. This allows us to set both IO regs.
*/
- printk(KERN_INFO " IO window: 0x%08lx-0x%08lx\n",
+ dev_info(&bridge->dev, " IO window: %#08lx-%#08lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
pci_write_config_dword(bridge, PCI_CB_IO_BASE_0,
@@ -101,7 +94,7 @@ void pci_setup_cardbus(struct pci_bus *bus)
pcibios_resource_to_bus(bridge, &region, bus->resource[1]);
if (bus->resource[1]->flags & IORESOURCE_IO) {
- printk(KERN_INFO " IO window: 0x%08lx-0x%08lx\n",
+ dev_info(&bridge->dev, " IO window: %#08lx-%#08lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
pci_write_config_dword(bridge, PCI_CB_IO_BASE_1,
@@ -112,7 +105,7 @@ void pci_setup_cardbus(struct pci_bus *bus)
pcibios_resource_to_bus(bridge, &region, bus->resource[2]);
if (bus->resource[2]->flags & IORESOURCE_MEM) {
- printk(KERN_INFO " PREFETCH window: 0x%08lx-0x%08lx\n",
+ dev_info(&bridge->dev, " PREFETCH window: %#08lx-%#08lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0,
@@ -123,7 +116,7 @@ void pci_setup_cardbus(struct pci_bus *bus)
pcibios_resource_to_bus(bridge, &region, bus->resource[3]);
if (bus->resource[3]->flags & IORESOURCE_MEM) {
- printk(KERN_INFO " MEM window: 0x%08lx-0x%08lx\n",
+ dev_info(&bridge->dev, " MEM window: %#08lx-%#08lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1,
@@ -151,7 +144,8 @@ static void pci_setup_bridge(struct pci_bus *bus)
struct pci_bus_region region;
u32 l, bu, lu, io_upper16;
- DBG(KERN_INFO "PCI: Bridge: %s\n", pci_name(bridge));
+ dev_info(&bridge->dev, "PCI bridge, secondary bus %04x:%02x\n",
+ pci_domain_nr(bus), bus->number);
/* Set up the top and bottom of the PCI I/O segment for this bus. */
pcibios_resource_to_bus(bridge, &region, bus->resource[0]);
@@ -162,7 +156,7 @@ static void pci_setup_bridge(struct pci_bus *bus)
l |= region.end & 0xf000;
/* Set up upper 16 bits of I/O base/limit. */
io_upper16 = (region.end & 0xffff0000) | (region.start >> 16);
- DBG(KERN_INFO " IO window: %04lx-%04lx\n",
+ dev_info(&bridge->dev, " IO window: %#04lx-%#04lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
}
@@ -170,7 +164,7 @@ static void pci_setup_bridge(struct pci_bus *bus)
/* Clear upper 16 bits of I/O base/limit. */
io_upper16 = 0;
l = 0x00f0;
- DBG(KERN_INFO " IO window: disabled.\n");
+ dev_info(&bridge->dev, " IO window: disabled\n");
}
/* Temporarily disable the I/O range before updating PCI_IO_BASE. */
pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff);
@@ -185,13 +179,13 @@ static void pci_setup_bridge(struct pci_bus *bus)
if (bus->resource[1]->flags & IORESOURCE_MEM) {
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
- DBG(KERN_INFO " MEM window: 0x%08lx-0x%08lx\n",
+ dev_info(&bridge->dev, " MEM window: %#08lx-%#08lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
}
else {
l = 0x0000fff0;
- DBG(KERN_INFO " MEM window: disabled.\n");
+ dev_info(&bridge->dev, " MEM window: disabled\n");
}
pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
@@ -208,13 +202,13 @@ static void pci_setup_bridge(struct pci_bus *bus)
l |= region.end & 0xfff00000;
bu = upper_32_bits(region.start);
lu = upper_32_bits(region.end);
- DBG(KERN_INFO " PREFETCH window: 0x%016llx-0x%016llx\n",
+ dev_info(&bridge->dev, " PREFETCH window: %#016llx-%#016llx\n",
(unsigned long long)region.start,
(unsigned long long)region.end);
}
else {
l = 0x0000fff0;
- DBG(KERN_INFO " PREFETCH window: disabled.\n");
+ dev_info(&bridge->dev, " PREFETCH window: disabled\n");
}
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
@@ -358,12 +352,12 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
continue;
r_size = r->end - r->start + 1;
/* For bridges size != alignment */
- align = (i < PCI_BRIDGE_RESOURCES) ? r_size : r->start;
+ align = resource_alignment(r);
order = __ffs(align) - 20;
if (order > 11) {
- printk(KERN_WARNING "PCI: region %s/%d "
- "too large: 0x%016llx-0x%016llx\n",
- pci_name(dev), i,
+ dev_warn(&dev->dev, "BAR %d bad alignment %llx: "
+ "%#016llx-%#016llx\n", i,
+ (unsigned long long)align,
(unsigned long long)r->start,
(unsigned long long)r->end);
r->flags = 0;
@@ -529,14 +523,48 @@ void __ref pci_bus_assign_resources(struct pci_bus *bus)
break;
default:
- printk(KERN_INFO "PCI: not setting up bridge %s "
- "for bus %d\n", pci_name(dev), b->number);
+ dev_info(&dev->dev, "not setting up bridge for bus "
+ "%04x:%02x\n", pci_domain_nr(b), b->number);
break;
}
}
}
EXPORT_SYMBOL(pci_bus_assign_resources);
+static void pci_bus_dump_res(struct pci_bus *bus)
+{
+ int i;
+
+ for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
+ struct resource *res = bus->resource[i];
+ if (!res)
+ continue;
+
+ printk(KERN_INFO "bus: %02x index %x %s: [%llx, %llx]\n",
+ bus->number, i,
+ (res->flags & IORESOURCE_IO) ? "io port" : "mmio",
+ (unsigned long long) res->start,
+ (unsigned long long) res->end);
+ }
+}
+
+static void pci_bus_dump_resources(struct pci_bus *bus)
+{
+ struct pci_bus *b;
+ struct pci_dev *dev;
+
+
+ pci_bus_dump_res(bus);
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ b = dev->subordinate;
+ if (!b)
+ continue;
+
+ pci_bus_dump_resources(b);
+ }
+}
+
void __init
pci_assign_unassigned_resources(void)
{
@@ -552,4 +580,9 @@ pci_assign_unassigned_resources(void)
pci_bus_assign_resources(bus);
pci_enable_bridges(bus);
}
+
+ /* dump the resource on buses */
+ list_for_each_entry(bus, &pci_root_buses, node) {
+ pci_bus_dump_resources(bus);
+ }
}
diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c
index 05ca2ed9eb51..aa795fd428de 100644
--- a/drivers/pci/setup-irq.c
+++ b/drivers/pci/setup-irq.c
@@ -47,8 +47,7 @@ pdev_fixup_irq(struct pci_dev *dev,
}
dev->irq = irq;
- pr_debug("PCI: fixup irq: (%s) got %d\n",
- kobject_name(&dev->dev.kobj), dev->irq);
+ dev_dbg(&dev->dev, "fixup irq: got %d\n", dev->irq);
/* Always tell the device, so the driver knows what is
the real IRQ to use; the device does not use it. */
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 7d35cdf4579f..1a5fc83c71b3 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -26,8 +26,7 @@
#include "pci.h"
-void
-pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
+void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
{
struct pci_bus_region region;
u32 new, check, mask;
@@ -43,20 +42,20 @@ pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
/*
* Ignore non-moveable resources. This might be legacy resources for
* which no functional BAR register exists or another important
- * system resource we should better not move around in system address
- * space.
+ * system resource we shouldn't move around.
*/
if (res->flags & IORESOURCE_PCI_FIXED)
return;
pcibios_resource_to_bus(dev, &region, res);
- pr_debug(" got res [%llx:%llx] bus [%llx:%llx] flags %lx for "
- "BAR %d of %s\n", (unsigned long long)res->start,
+ dev_dbg(&dev->dev, "BAR %d: got res [%#llx-%#llx] bus [%#llx-%#llx] "
+ "flags %#lx\n", resno,
+ (unsigned long long)res->start,
(unsigned long long)res->end,
(unsigned long long)region.start,
(unsigned long long)region.end,
- (unsigned long)res->flags, resno, pci_name(dev));
+ (unsigned long)res->flags);
new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
if (res->flags & IORESOURCE_IO)
@@ -81,9 +80,8 @@ pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
pci_read_config_dword(dev, reg, &check);
if ((new ^ check) & mask) {
- printk(KERN_ERR "PCI: Error while updating region "
- "%s/%d (%08x != %08x)\n", pci_name(dev), resno,
- new, check);
+ dev_err(&dev->dev, "BAR %d: error updating (%#08x != %#08x)\n",
+ resno, new, check);
}
if ((new & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
@@ -92,15 +90,14 @@ pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
pci_write_config_dword(dev, reg + 4, new);
pci_read_config_dword(dev, reg + 4, &check);
if (check != new) {
- printk(KERN_ERR "PCI: Error updating region "
- "%s/%d (high %08x != %08x)\n",
- pci_name(dev), resno, new, check);
+ dev_err(&dev->dev, "BAR %d: error updating "
+ "(high %#08x != %#08x)\n", resno, new, check);
}
}
res->flags &= ~IORESOURCE_UNSET;
- pr_debug("PCI: moved device %s resource %d (%lx) to %x\n",
- pci_name(dev), resno, res->flags,
- new & ~PCI_REGION_FLAG_MASK);
+ dev_dbg(&dev->dev, "BAR %d: moved to bus [%#llx-%#llx] flags %#lx\n",
+ resno, (unsigned long long)region.start,
+ (unsigned long long)region.end, res->flags);
}
int pci_claim_resource(struct pci_dev *dev, int resource)
@@ -117,10 +114,11 @@ int pci_claim_resource(struct pci_dev *dev, int resource)
err = insert_resource(root, res);
if (err) {
- printk(KERN_ERR "PCI: %s region %d of %s %s [%llx:%llx]\n",
- root ? "Address space collision on" :
- "No parent found for",
- resource, dtype, pci_name(dev),
+ dev_err(&dev->dev, "BAR %d: %s of %s [%#llx-%#llx]\n",
+ resource,
+ root ? "address space collision on" :
+ "no parent found for",
+ dtype,
(unsigned long long)res->start,
(unsigned long long)res->end);
}
@@ -140,11 +138,10 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
align = resource_alignment(res);
if (!align) {
- printk(KERN_ERR "PCI: Cannot allocate resource (bogus "
- "alignment) %d [%llx:%llx] (flags %lx) of %s\n",
+ dev_err(&dev->dev, "BAR %d: can't allocate resource (bogus "
+ "alignment) [%#llx-%#llx] flags %#lx\n",
resno, (unsigned long long)res->start,
- (unsigned long long)res->end, res->flags,
- pci_name(dev));
+ (unsigned long long)res->end, res->flags);
return -EINVAL;
}
@@ -165,11 +162,11 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
}
if (ret) {
- printk(KERN_ERR "PCI: Failed to allocate %s resource "
- "#%d:%llx@%llx for %s\n",
+ dev_err(&dev->dev, "BAR %d: can't allocate %s resource "
+ "[%#llx-%#llx]\n", resno,
res->flags & IORESOURCE_IO ? "I/O" : "mem",
- resno, (unsigned long long)size,
- (unsigned long long)res->start, pci_name(dev));
+ (unsigned long long)res->start,
+ (unsigned long long)res->end);
} else {
res->flags &= ~IORESOURCE_STARTALIGN;
if (resno < PCI_BRIDGE_RESOURCES)
@@ -205,11 +202,11 @@ int pci_assign_resource_fixed(struct pci_dev *dev, int resno)
}
if (ret) {
- printk(KERN_ERR "PCI: Failed to allocate %s resource "
- "#%d:%llx@%llx for %s\n",
+ dev_err(&dev->dev, "BAR %d: can't allocate %s resource "
+ "[%#llx-%#llx\n]", resno,
res->flags & IORESOURCE_IO ? "I/O" : "mem",
- resno, (unsigned long long)(res->end - res->start + 1),
- (unsigned long long)res->start, pci_name(dev));
+ (unsigned long long)res->start,
+ (unsigned long long)res->end);
} else if (resno < PCI_BRIDGE_RESOURCES) {
pci_update_resource(dev, res, resno);
}
@@ -239,11 +236,10 @@ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
r_align = resource_alignment(r);
if (!r_align) {
- printk(KERN_WARNING "PCI: bogus alignment of resource "
- "%d [%llx:%llx] (flags %lx) of %s\n",
+ dev_warn(&dev->dev, "BAR %d: bogus alignment "
+ "[%#llx-%#llx] flags %#lx\n",
i, (unsigned long long)r->start,
- (unsigned long long)r->end, r->flags,
- pci_name(dev));
+ (unsigned long long)r->end, r->flags);
continue;
}
for (list = head; ; list = list->next) {
@@ -291,7 +287,7 @@ int pci_enable_resources(struct pci_dev *dev, int mask)
if (!r->parent) {
dev_err(&dev->dev, "device not available because of "
- "BAR %d [%llx:%llx] collisions\n", i,
+ "BAR %d [%#llx-%#llx] collisions\n", i,
(unsigned long long) r->start,
(unsigned long long) r->end);
return -EINVAL;
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
new file mode 100644
index 000000000000..7e5b85cbd948
--- /dev/null
+++ b/drivers/pci/slot.c
@@ -0,0 +1,233 @@
+/*
+ * drivers/pci/slot.c
+ * Copyright (C) 2006 Matthew Wilcox <matthew@wil.cx>
+ * Copyright (C) 2006-2008 Hewlett-Packard Development Company, L.P.
+ * Alex Chiang <achiang@hp.com>
+ */
+
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include <linux/err.h>
+#include "pci.h"
+
+struct kset *pci_slots_kset;
+EXPORT_SYMBOL_GPL(pci_slots_kset);
+
+static ssize_t pci_slot_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+ return attribute->show ? attribute->show(slot, buf) : -EIO;
+}
+
+static ssize_t pci_slot_attr_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t len)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+ return attribute->store ? attribute->store(slot, buf, len) : -EIO;
+}
+
+static struct sysfs_ops pci_slot_sysfs_ops = {
+ .show = pci_slot_attr_show,
+ .store = pci_slot_attr_store,
+};
+
+static ssize_t address_read_file(struct pci_slot *slot, char *buf)
+{
+ if (slot->number == 0xff)
+ return sprintf(buf, "%04x:%02x\n",
+ pci_domain_nr(slot->bus),
+ slot->bus->number);
+ else
+ return sprintf(buf, "%04x:%02x:%02x\n",
+ pci_domain_nr(slot->bus),
+ slot->bus->number,
+ slot->number);
+}
+
+static void pci_slot_release(struct kobject *kobj)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+
+ pr_debug("%s: releasing pci_slot on %x:%d\n", __func__,
+ slot->bus->number, slot->number);
+
+ list_del(&slot->list);
+
+ kfree(slot);
+}
+
+static struct pci_slot_attribute pci_slot_attr_address =
+ __ATTR(address, (S_IFREG | S_IRUGO), address_read_file, NULL);
+
+static struct attribute *pci_slot_default_attrs[] = {
+ &pci_slot_attr_address.attr,
+ NULL,
+};
+
+static struct kobj_type pci_slot_ktype = {
+ .sysfs_ops = &pci_slot_sysfs_ops,
+ .release = &pci_slot_release,
+ .default_attrs = pci_slot_default_attrs,
+};
+
+/**
+ * pci_create_slot - create or increment refcount for physical PCI slot
+ * @parent: struct pci_bus of parent bridge
+ * @slot_nr: PCI_SLOT(pci_dev->devfn) or -1 for placeholder
+ * @name: user visible string presented in /sys/bus/pci/slots/<name>
+ *
+ * PCI slots have first class attributes such as address, speed, width,
+ * and a &struct pci_slot is used to manage them. This interface will
+ * either return a new &struct pci_slot to the caller, or if the pci_slot
+ * already exists, its refcount will be incremented.
+ *
+ * Slots are uniquely identified by a @pci_bus, @slot_nr, @name tuple.
+ *
+ * Placeholder slots:
+ * In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify
+ * a slot. There is one notable exception - pSeries (rpaphp), where the
+ * @slot_nr cannot be determined until a device is actually inserted into
+ * the slot. In this scenario, the caller may pass -1 for @slot_nr.
+ *
+ * The following semantics are imposed when the caller passes @slot_nr ==
+ * -1. First, the check for existing %struct pci_slot is skipped, as the
+ * caller may know about several unpopulated slots on a given %struct
+ * pci_bus, and each slot would have a @slot_nr of -1. Uniqueness for
+ * these slots is then determined by the @name parameter. We expect
+ * kobject_init_and_add() to warn us if the caller attempts to create
+ * multiple slots with the same name. The other change in semantics is
+ * user-visible, which is the 'address' parameter presented in sysfs will
+ * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the
+ * %struct pci_bus and bb is the bus number. In other words, the devfn of
+ * the 'placeholder' slot will not be displayed.
+ */
+
+struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
+ const char *name)
+{
+ struct pci_slot *slot;
+ int err;
+
+ down_write(&pci_bus_sem);
+
+ if (slot_nr == -1)
+ goto placeholder;
+
+ /* If we've already created this slot, bump refcount and return. */
+ list_for_each_entry(slot, &parent->slots, list) {
+ if (slot->number == slot_nr) {
+ kobject_get(&slot->kobj);
+ pr_debug("%s: inc refcount to %d on %04x:%02x:%02x\n",
+ __func__,
+ atomic_read(&slot->kobj.kref.refcount),
+ pci_domain_nr(parent), parent->number,
+ slot_nr);
+ goto out;
+ }
+ }
+
+placeholder:
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot) {
+ slot = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ slot->bus = parent;
+ slot->number = slot_nr;
+
+ slot->kobj.kset = pci_slots_kset;
+ err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL,
+ "%s", name);
+ if (err) {
+ printk(KERN_ERR "Unable to register kobject %s\n", name);
+ goto err;
+ }
+
+ INIT_LIST_HEAD(&slot->list);
+ list_add(&slot->list, &parent->slots);
+
+ /* Don't care if debug printk has a -1 for slot_nr */
+ pr_debug("%s: created pci_slot on %04x:%02x:%02x\n",
+ __func__, pci_domain_nr(parent), parent->number, slot_nr);
+
+ out:
+ up_write(&pci_bus_sem);
+ return slot;
+ err:
+ kfree(slot);
+ slot = ERR_PTR(err);
+ goto out;
+}
+EXPORT_SYMBOL_GPL(pci_create_slot);
+
+/**
+ * pci_update_slot_number - update %struct pci_slot -> number
+ * @slot - %struct pci_slot to update
+ * @slot_nr - new number for slot
+ *
+ * The primary purpose of this interface is to allow callers who earlier
+ * created a placeholder slot in pci_create_slot() by passing a -1 as
+ * slot_nr, to update their %struct pci_slot with the correct @slot_nr.
+ */
+
+void pci_update_slot_number(struct pci_slot *slot, int slot_nr)
+{
+ int name_count = 0;
+ struct pci_slot *tmp;
+
+ down_write(&pci_bus_sem);
+
+ list_for_each_entry(tmp, &slot->bus->slots, list) {
+ WARN_ON(tmp->number == slot_nr);
+ if (!strcmp(kobject_name(&tmp->kobj), kobject_name(&slot->kobj)))
+ name_count++;
+ }
+
+ if (name_count > 1)
+ printk(KERN_WARNING "pci_update_slot_number found %d slots with the same name: %s\n", name_count, kobject_name(&slot->kobj));
+
+ slot->number = slot_nr;
+ up_write(&pci_bus_sem);
+}
+EXPORT_SYMBOL_GPL(pci_update_slot_number);
+
+/**
+ * pci_destroy_slot - decrement refcount for physical PCI slot
+ * @slot: struct pci_slot to decrement
+ *
+ * %struct pci_slot is refcounted, so destroying them is really easy; we
+ * just call kobject_put on its kobj and let our release methods do the
+ * rest.
+ */
+
+void pci_destroy_slot(struct pci_slot *slot)
+{
+ pr_debug("%s: dec refcount to %d on %04x:%02x:%02x\n", __func__,
+ atomic_read(&slot->kobj.kref.refcount) - 1,
+ pci_domain_nr(slot->bus), slot->bus->number, slot->number);
+
+ down_write(&pci_bus_sem);
+ kobject_put(&slot->kobj);
+ up_write(&pci_bus_sem);
+}
+EXPORT_SYMBOL_GPL(pci_destroy_slot);
+
+static int pci_slot_init(void)
+{
+ struct kset *pci_bus_kset;
+
+ pci_bus_kset = bus_get_kset(&pci_bus_type);
+ pci_slots_kset = kset_create_and_add("slots", NULL,
+ &pci_bus_kset->kobj);
+ if (!pci_slots_kset) {
+ printk(KERN_ERR "PCI: Slot initialization failure\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+subsys_initcall(pci_slot_init);