aboutsummaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/.gitignore4
-rw-r--r--drivers/pci/Kconfig100
-rw-r--r--drivers/pci/Makefile50
-rw-r--r--drivers/pci/access.c563
-rw-r--r--drivers/pci/ats.c375
-rw-r--r--drivers/pci/bus.c365
-rw-r--r--drivers/pci/dmar.c345
-rw-r--r--drivers/pci/host-bridge.c84
-rw-r--r--drivers/pci/host/Kconfig49
-rw-r--r--drivers/pci/host/Makefile8
-rw-r--r--drivers/pci/host/pci-exynos.c667
-rw-r--r--drivers/pci/host/pci-host-generic.c388
-rw-r--r--drivers/pci/host/pci-imx6.c616
-rw-r--r--drivers/pci/host/pci-mvebu.c1097
-rw-r--r--drivers/pci/host/pci-rcar-gen2.c426
-rw-r--r--drivers/pci/host/pci-tegra.c1719
-rw-r--r--drivers/pci/host/pcie-designware.c835
-rw-r--r--drivers/pci/host/pcie-designware.h76
-rw-r--r--drivers/pci/host/pcie-rcar.c1006
-rw-r--r--drivers/pci/hotplug-pci.c31
-rw-r--r--drivers/pci/hotplug.c37
-rw-r--r--drivers/pci/hotplug/Kconfig59
-rw-r--r--drivers/pci/hotplug/Makefile23
-rw-r--r--drivers/pci/hotplug/acpi_pcihp.c317
-rw-r--r--drivers/pci/hotplug/acpiphp.h151
-rw-r--r--drivers/pci/hotplug/acpiphp_core.c149
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c2083
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c111
-rw-r--r--drivers/pci/hotplug/cpci_hotplug.h56
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_core.c150
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_pci.c111
-rw-r--r--drivers/pci/hotplug/cpcihp_generic.c35
-rw-r--r--drivers/pci/hotplug/cpcihp_zt5550.c32
-rw-r--r--drivers/pci/hotplug/cpcihp_zt5550.h18
-rw-r--r--drivers/pci/hotplug/cpqphp.h187
-rw-r--r--drivers/pci/hotplug/cpqphp_core.c1155
-rw-r--r--drivers/pci/hotplug/cpqphp_ctrl.c584
-rw-r--r--drivers/pci/hotplug/cpqphp_nvram.c132
-rw-r--r--drivers/pci/hotplug/cpqphp_nvram.h12
-rw-r--r--drivers/pci/hotplug/cpqphp_pci.c652
-rw-r--r--drivers/pci/hotplug/cpqphp_sysfs.c34
-rw-r--r--drivers/pci/hotplug/fakephp.c402
-rw-r--r--drivers/pci/hotplug/ibmphp.h83
-rw-r--r--drivers/pci/hotplug/ibmphp_core.c361
-rw-r--r--drivers/pci/hotplug/ibmphp_ebda.c344
-rw-r--r--drivers/pci/hotplug/ibmphp_hpc.c106
-rw-r--r--drivers/pci/hotplug/ibmphp_pci.c127
-rw-r--r--drivers/pci/hotplug/ibmphp_res.c117
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c595
-rw-r--r--drivers/pci/hotplug/pciehp.h200
-rw-r--r--drivers/pci/hotplug/pciehp_acpi.c137
-rw-r--r--drivers/pci/hotplug/pciehp_core.c518
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c516
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c1685
-rw-r--r--drivers/pci/hotplug/pciehp_pci.c279
-rw-r--r--drivers/pci/hotplug/pcihp_skeleton.c81
-rw-r--r--drivers/pci/hotplug/pcihp_slot.c180
-rw-r--r--drivers/pci/hotplug/rpadlpar.h8
-rw-r--r--drivers/pci/hotplug/rpadlpar_core.c118
-rw-r--r--drivers/pci/hotplug/rpadlpar_sysfs.c13
-rw-r--r--drivers/pci/hotplug/rpaphp.h24
-rw-r--r--drivers/pci/hotplug/rpaphp_core.c81
-rw-r--r--drivers/pci/hotplug/rpaphp_pci.c19
-rw-r--r--drivers/pci/hotplug/rpaphp_slot.c80
-rw-r--r--drivers/pci/hotplug/s390_pci_hpc.c214
-rw-r--r--drivers/pci/hotplug/sgi_hotplug.c175
-rw-r--r--drivers/pci/hotplug/shpchp.h103
-rw-r--r--drivers/pci/hotplug/shpchp_core.c163
-rw-r--r--drivers/pci/hotplug/shpchp_ctrl.c179
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c298
-rw-r--r--drivers/pci/hotplug/shpchp_pci.c181
-rw-r--r--drivers/pci/hotplug/shpchp_sysfs.c40
-rw-r--r--drivers/pci/htirq.c54
-rw-r--r--drivers/pci/intel-iommu.c2296
-rw-r--r--drivers/pci/intel-iommu.h344
-rw-r--r--drivers/pci/ioapic.c121
-rw-r--r--drivers/pci/iov.c694
-rw-r--r--drivers/pci/iova.c395
-rw-r--r--drivers/pci/iova.h52
-rw-r--r--drivers/pci/irq.c61
-rw-r--r--drivers/pci/msi.c1183
-rw-r--r--drivers/pci/msi.h42
-rw-r--r--drivers/pci/of.c61
-rw-r--r--drivers/pci/pci-acpi.c501
-rw-r--r--drivers/pci/pci-driver.c1253
-rw-r--r--drivers/pci/pci-label.c307
-rw-r--r--drivers/pci/pci-stub.c97
-rw-r--r--drivers/pci/pci-sysfs.c1268
-rw-r--r--drivers/pci/pci.c3752
-rw-r--r--drivers/pci/pci.h288
-rw-r--r--drivers/pci/pcie/Kconfig64
-rw-r--r--drivers/pci/pcie/Makefile6
-rw-r--r--drivers/pci/pcie/aer/Kconfig15
-rw-r--r--drivers/pci/pcie/aer/Kconfig.debug18
-rw-r--r--drivers/pci/pcie/aer/Makefile3
-rw-r--r--drivers/pci/pcie/aer/aer_inject.c536
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c253
-rw-r--r--drivers/pci/pcie/aer/aerdrv.h78
-rw-r--r--drivers/pci/pcie/aer/aerdrv_acpi.c147
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c814
-rw-r--r--drivers/pci/pcie/aer/aerdrv_errprint.c321
-rw-r--r--drivers/pci/pcie/aer/ecrc.c131
-rw-r--r--drivers/pci/pcie/aspm.c991
-rw-r--r--drivers/pci/pcie/pme.c438
-rw-r--r--drivers/pci/pcie/portdrv.h88
-rw-r--r--drivers/pci/pcie/portdrv_acpi.c63
-rw-r--r--drivers/pci/pcie/portdrv_bus.c52
-rw-r--r--drivers/pci/pcie/portdrv_core.c737
-rw-r--r--drivers/pci/pcie/portdrv_pci.c273
-rw-r--r--drivers/pci/probe.c2069
-rw-r--r--drivers/pci/proc.c171
-rw-r--r--drivers/pci/quirks.c2719
-rw-r--r--drivers/pci/remove.c184
-rw-r--r--drivers/pci/rom.c105
-rw-r--r--drivers/pci/search.c486
-rw-r--r--drivers/pci/setup-bus.c1568
-rw-r--r--drivers/pci/setup-irq.c25
-rw-r--r--drivers/pci/setup-res.c408
-rw-r--r--drivers/pci/slot.c397
-rw-r--r--drivers/pci/syscall.c17
-rw-r--r--drivers/pci/vc.c434
-rw-r--r--drivers/pci/vpd.c62
-rw-r--r--drivers/pci/xen-pcifront.c1168
123 files changed, 33726 insertions, 16203 deletions
diff --git a/drivers/pci/.gitignore b/drivers/pci/.gitignore
deleted file mode 100644
index f297ca8d313..00000000000
--- a/drivers/pci/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-classlist.h
-devlist.h
-gen-devlist
-
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index e1ca42591ac..893503fa178 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -1,14 +1,9 @@
#
# PCI configuration
#
-config ARCH_SUPPORTS_MSI
- bool
- default n
-
config PCI_MSI
bool "Message Signaled Interrupts (MSI and MSI-X)"
depends on PCI
- depends on ARCH_SUPPORTS_MSI
help
This allows device drivers to enable MSI (Message Signaled
Interrupts). Message Signaled Interrupts enable a device to
@@ -19,18 +14,7 @@ config PCI_MSI
by using the 'pci=nomsi' option. This disables MSI for the
entire system.
- If you don't know what to do here, say N.
-
-config PCI_LEGACY
- bool "Enable deprecated pci_find_* API"
- depends on PCI
- default y
- help
- Say Y here if you want to include support for the deprecated
- pci_find_slot() and pci_find_device() APIs. Most drivers have
- been converted over to using the proper hotplug APIs, so this
- option serves to include/exclude only a few drivers that are
- still using this API.
+ If you don't know what to do here, say Y.
config PCI_DEBUG
bool "PCI Debugging"
@@ -42,6 +26,38 @@ config PCI_DEBUG
When in doubt, say N.
+config PCI_REALLOC_ENABLE_AUTO
+ bool "Enable PCI resource re-allocation detection"
+ depends on PCI
+ help
+ Say Y here if you want the PCI core to detect if PCI resource
+ re-allocation needs to be enabled. You can always use pci=realloc=on
+ or pci=realloc=off to override it. Note this feature is a no-op
+ unless PCI_IOV support is also enabled; in that case it will
+ automatically re-allocate PCI resources if SR-IOV BARs have not
+ been allocated by the BIOS.
+
+ When in doubt, say N.
+
+config PCI_STUB
+ tristate "PCI Stub driver"
+ depends on PCI
+ help
+ Say Y or M here if you want be able to reserve a PCI device
+ when it is going to be assigned to a guest operating system.
+
+ When in doubt, say N.
+
+config XEN_PCIDEV_FRONTEND
+ tristate "Xen PCI Frontend"
+ depends on PCI && X86 && XEN
+ select PCI_XEN
+ select XEN_XENBUS_FRONTEND
+ default y
+ help
+ The PCI device frontend driver allows the kernel to import arbitrary
+ PCI devices from a PCI backend to support PCI driver domains.
+
config HT_IRQ
bool "Interrupts on hypertransport devices"
default y
@@ -50,3 +66,53 @@ config HT_IRQ
This allows native hypertransport devices to use interrupts.
If unsure say Y.
+
+config PCI_ATS
+ bool
+
+config PCI_IOV
+ bool "PCI IOV support"
+ depends on PCI
+ select PCI_ATS
+ help
+ I/O Virtualization is a PCI feature supported by some devices
+ which allows them to create virtual devices which share their
+ physical resources.
+
+ If unsure, say N.
+
+config PCI_PRI
+ bool "PCI PRI support"
+ depends on PCI
+ select PCI_ATS
+ help
+ PRI is the PCI Page Request Interface. It allows PCI devices that are
+ behind an IOMMU to recover from page faults.
+
+ If unsure, say N.
+
+config PCI_PASID
+ bool "PCI PASID support"
+ depends on PCI
+ select PCI_ATS
+ help
+ Process Address Space Identifiers (PASIDs) can be used by PCI devices
+ to access more than one IO address space at the same time. To make
+ use of this feature an IOMMU is required which also supports PASIDs.
+ Select this option if you have such an IOMMU and want to compile the
+ driver for it into your kernel.
+
+ If unsure, say N.
+
+config PCI_IOAPIC
+ bool "PCI IO-APIC hotplug support" if X86
+ depends on PCI
+ depends on ACPI
+ depends on X86_IO_APIC
+ default !X86
+
+config PCI_LABEL
+ def_bool y if (DMI || ACPI)
+ select NLS
+
+source "drivers/pci/host/Kconfig"
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 4d1ce2e7361..e04fe2d9df3 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -2,14 +2,18 @@
# Makefile for the PCI bus specific drivers.
#
-obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \
- pci-driver.o search.o pci-sysfs.o rom.o setup-res.o
+obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \
+ pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
+ irq.o vpd.o setup-bus.o vc.o
obj-$(CONFIG_PROC_FS) += proc.o
+obj-$(CONFIG_SYSFS) += slot.o
+
+obj-$(CONFIG_PCI_QUIRKS) += quirks.o
# Build PCI Express stuff if needed
obj-$(CONFIG_PCIEPORTBUS) += pcie/
-obj-$(CONFIG_HOTPLUG) += hotplug.o
+obj-$(CONFIG_PCI_IOAPIC) += ioapic.o
# Build the PCI Hotplug drivers if we were asked to
obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
@@ -23,33 +27,39 @@ obj-$(CONFIG_PCI_MSI) += msi.o
# Build the Hypertransport interrupt support
obj-$(CONFIG_HT_IRQ) += htirq.o
-# Build Intel IOMMU support
-obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o
+obj-$(CONFIG_PCI_ATS) += ats.o
+obj-$(CONFIG_PCI_IOV) += iov.o
#
# Some architectures use the generic PCI setup functions
#
-obj-$(CONFIG_X86) += setup-bus.o
-obj-$(CONFIG_ALPHA) += setup-bus.o setup-irq.o
-obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o
-obj-$(CONFIG_PARISC) += setup-bus.o
-obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o
-obj-$(CONFIG_PPC32) += setup-irq.o
-obj-$(CONFIG_PPC) += setup-bus.o
-obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
-obj-$(CONFIG_X86_VISWS) += setup-irq.o
-obj-$(CONFIG_MN10300) += setup-bus.o
+obj-$(CONFIG_ALPHA) += setup-irq.o
+obj-$(CONFIG_ARM) += setup-irq.o
+obj-$(CONFIG_UNICORE32) += setup-irq.o
+obj-$(CONFIG_SUPERH) += setup-irq.o
+obj-$(CONFIG_MIPS) += setup-irq.o
+obj-$(CONFIG_TILE) += setup-irq.o
+obj-$(CONFIG_SPARC_LEON) += setup-irq.o
+obj-$(CONFIG_M68K) += setup-irq.o
#
# ACPI Related PCI FW Functions
+# ACPI _DSM provided firmware instance and string name
#
obj-$(CONFIG_ACPI) += pci-acpi.o
-# Cardbus & CompactPCI use setup-bus
-obj-$(CONFIG_HOTPLUG) += setup-bus.o
+# SMBIOS provided firmware instance and labels
+obj-$(CONFIG_PCI_LABEL) += pci-label.o
obj-$(CONFIG_PCI_SYSCALL) += syscall.o
-ifeq ($(CONFIG_PCI_DEBUG),y)
-EXTRA_CFLAGS += -DDEBUG
-endif
+obj-$(CONFIG_PCI_STUB) += pci-stub.o
+
+obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
+
+obj-$(CONFIG_OF) += of.o
+
+ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
+
+# PCI host controller drivers
+obj-y += host/
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index fc405f0165d..d292d7cb341 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -1,6 +1,8 @@
+#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/sched.h>
+#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/wait.h>
@@ -11,7 +13,7 @@
* configuration space.
*/
-static DEFINE_SPINLOCK(pci_lock);
+DEFINE_RAW_SPINLOCK(pci_lock);
/*
* Wrappers for all PCI configuration access functions. They just check
@@ -31,10 +33,10 @@ int pci_bus_read_config_##size \
unsigned long flags; \
u32 data = 0; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
- spin_lock_irqsave(&pci_lock, flags); \
+ raw_spin_lock_irqsave(&pci_lock, flags); \
res = bus->ops->read(bus, devfn, pos, len, &data); \
*value = (type)data; \
- spin_unlock_irqrestore(&pci_lock, flags); \
+ raw_spin_unlock_irqrestore(&pci_lock, flags); \
return res; \
}
@@ -45,9 +47,9 @@ int pci_bus_write_config_##size \
int res; \
unsigned long flags; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
- spin_lock_irqsave(&pci_lock, flags); \
+ raw_spin_lock_irqsave(&pci_lock, flags); \
res = bus->ops->write(bus, devfn, pos, len, value); \
- spin_unlock_irqrestore(&pci_lock, flags); \
+ raw_spin_unlock_irqrestore(&pci_lock, flags); \
return res; \
}
@@ -65,6 +67,58 @@ EXPORT_SYMBOL(pci_bus_write_config_byte);
EXPORT_SYMBOL(pci_bus_write_config_word);
EXPORT_SYMBOL(pci_bus_write_config_dword);
+/**
+ * pci_bus_set_ops - Set raw operations of pci bus
+ * @bus: pci bus struct
+ * @ops: new raw operations
+ *
+ * Return previous raw operations
+ */
+struct pci_ops *pci_bus_set_ops(struct pci_bus *bus, struct pci_ops *ops)
+{
+ struct pci_ops *old_ops;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&pci_lock, flags);
+ old_ops = bus->ops;
+ bus->ops = ops;
+ raw_spin_unlock_irqrestore(&pci_lock, flags);
+ return old_ops;
+}
+EXPORT_SYMBOL(pci_bus_set_ops);
+
+/**
+ * pci_read_vpd - Read one entry from Vital Product Data
+ * @dev: pci device struct
+ * @pos: offset in vpd space
+ * @count: number of bytes to read
+ * @buf: pointer to where to store result
+ *
+ */
+ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf)
+{
+ if (!dev->vpd || !dev->vpd->ops)
+ return -ENODEV;
+ return dev->vpd->ops->read(dev, pos, count, buf);
+}
+EXPORT_SYMBOL(pci_read_vpd);
+
+/**
+ * pci_write_vpd - Write entry to Vital Product Data
+ * @dev: pci device struct
+ * @pos: offset in vpd space
+ * @count: number of bytes to write
+ * @buf: buffer containing write data
+ *
+ */
+ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf)
+{
+ if (!dev->vpd || !dev->vpd->ops)
+ return -ENODEV;
+ return dev->vpd->ops->write(dev, pos, count, buf);
+}
+EXPORT_SYMBOL(pci_write_vpd);
+
/*
* The following routines are to prevent the user from accessing PCI config
* space when it's unsafe to do so. Some devices require this during BIST and
@@ -73,51 +127,59 @@ EXPORT_SYMBOL(pci_bus_write_config_dword);
* We have a bit per device to indicate it's blocked and a global wait queue
* for callers to sleep on until devices are unblocked.
*/
-static DECLARE_WAIT_QUEUE_HEAD(pci_ucfg_wait);
+static DECLARE_WAIT_QUEUE_HEAD(pci_cfg_wait);
-static noinline void pci_wait_ucfg(struct pci_dev *dev)
+static noinline void pci_wait_cfg(struct pci_dev *dev)
{
DECLARE_WAITQUEUE(wait, current);
- __add_wait_queue(&pci_ucfg_wait, &wait);
+ __add_wait_queue(&pci_cfg_wait, &wait);
do {
set_current_state(TASK_UNINTERRUPTIBLE);
- spin_unlock_irq(&pci_lock);
+ raw_spin_unlock_irq(&pci_lock);
schedule();
- spin_lock_irq(&pci_lock);
- } while (dev->block_ucfg_access);
- __remove_wait_queue(&pci_ucfg_wait, &wait);
+ raw_spin_lock_irq(&pci_lock);
+ } while (dev->block_cfg_access);
+ __remove_wait_queue(&pci_cfg_wait, &wait);
}
+/* Returns 0 on success, negative values indicate error. */
#define PCI_USER_READ_CONFIG(size,type) \
int pci_user_read_config_##size \
(struct pci_dev *dev, int pos, type *val) \
{ \
- int ret = 0; \
+ int ret = PCIBIOS_SUCCESSFUL; \
u32 data = -1; \
- if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
- spin_lock_irq(&pci_lock); \
- if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \
+ if (PCI_##size##_BAD) \
+ return -EINVAL; \
+ raw_spin_lock_irq(&pci_lock); \
+ if (unlikely(dev->block_cfg_access)) \
+ pci_wait_cfg(dev); \
ret = dev->bus->ops->read(dev->bus, dev->devfn, \
pos, sizeof(type), &data); \
- spin_unlock_irq(&pci_lock); \
+ raw_spin_unlock_irq(&pci_lock); \
*val = (type)data; \
- return ret; \
-}
+ return pcibios_err_to_errno(ret); \
+} \
+EXPORT_SYMBOL_GPL(pci_user_read_config_##size);
+/* Returns 0 on success, negative values indicate error. */
#define PCI_USER_WRITE_CONFIG(size,type) \
int pci_user_write_config_##size \
(struct pci_dev *dev, int pos, type val) \
{ \
- int ret = -EIO; \
- if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
- spin_lock_irq(&pci_lock); \
- if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \
+ int ret = PCIBIOS_SUCCESSFUL; \
+ if (PCI_##size##_BAD) \
+ return -EINVAL; \
+ raw_spin_lock_irq(&pci_lock); \
+ if (unlikely(dev->block_cfg_access)) \
+ pci_wait_cfg(dev); \
ret = dev->bus->ops->write(dev->bus, dev->devfn, \
pos, sizeof(type), val); \
- spin_unlock_irq(&pci_lock); \
- return ret; \
-}
+ raw_spin_unlock_irq(&pci_lock); \
+ return pcibios_err_to_errno(ret); \
+} \
+EXPORT_SYMBOL_GPL(pci_user_write_config_##size);
PCI_USER_READ_CONFIG(byte, u8)
PCI_USER_READ_CONFIG(word, u16)
@@ -126,48 +188,451 @@ PCI_USER_WRITE_CONFIG(byte, u8)
PCI_USER_WRITE_CONFIG(word, u16)
PCI_USER_WRITE_CONFIG(dword, u32)
+/* VPD access through PCI 2.2+ VPD capability */
+
+#define PCI_VPD_PCI22_SIZE (PCI_VPD_ADDR_MASK + 1)
+
+struct pci_vpd_pci22 {
+ struct pci_vpd base;
+ struct mutex lock;
+ u16 flag;
+ bool busy;
+ u8 cap;
+};
+
+/*
+ * Wait for last operation to complete.
+ * This code has to spin since there is no other notification from the PCI
+ * hardware. Since the VPD is often implemented by serial attachment to an
+ * EEPROM, it may take many milliseconds to complete.
+ *
+ * Returns 0 on success, negative values indicate error.
+ */
+static int pci_vpd_pci22_wait(struct pci_dev *dev)
+{
+ struct pci_vpd_pci22 *vpd =
+ container_of(dev->vpd, struct pci_vpd_pci22, base);
+ unsigned long timeout = jiffies + HZ/20 + 2;
+ u16 status;
+ int ret;
+
+ if (!vpd->busy)
+ return 0;
+
+ for (;;) {
+ ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR,
+ &status);
+ if (ret < 0)
+ return ret;
+
+ if ((status & PCI_VPD_ADDR_F) == vpd->flag) {
+ vpd->busy = false;
+ return 0;
+ }
+
+ if (time_after(jiffies, timeout)) {
+ dev_printk(KERN_DEBUG, &dev->dev, "vpd r/w failed. This is likely a firmware bug on this device. Contact the card vendor for a firmware update\n");
+ return -ETIMEDOUT;
+ }
+ if (fatal_signal_pending(current))
+ return -EINTR;
+ if (!cond_resched())
+ udelay(10);
+ }
+}
+
+static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count,
+ void *arg)
+{
+ struct pci_vpd_pci22 *vpd =
+ container_of(dev->vpd, struct pci_vpd_pci22, base);
+ int ret;
+ loff_t end = pos + count;
+ u8 *buf = arg;
+
+ if (pos < 0 || pos > vpd->base.len || end > vpd->base.len)
+ return -EINVAL;
+
+ if (mutex_lock_killable(&vpd->lock))
+ return -EINTR;
+
+ ret = pci_vpd_pci22_wait(dev);
+ if (ret < 0)
+ goto out;
+
+ while (pos < end) {
+ u32 val;
+ unsigned int i, skip;
+
+ ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
+ pos & ~3);
+ if (ret < 0)
+ break;
+ vpd->busy = true;
+ vpd->flag = PCI_VPD_ADDR_F;
+ ret = pci_vpd_pci22_wait(dev);
+ if (ret < 0)
+ break;
+
+ ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val);
+ if (ret < 0)
+ break;
+
+ skip = pos & 3;
+ for (i = 0; i < sizeof(u32); i++) {
+ if (i >= skip) {
+ *buf++ = val;
+ if (++pos == end)
+ break;
+ }
+ val >>= 8;
+ }
+ }
+out:
+ mutex_unlock(&vpd->lock);
+ return ret ? ret : count;
+}
+
+static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count,
+ const void *arg)
+{
+ struct pci_vpd_pci22 *vpd =
+ container_of(dev->vpd, struct pci_vpd_pci22, base);
+ const u8 *buf = arg;
+ loff_t end = pos + count;
+ int ret = 0;
+
+ if (pos < 0 || (pos & 3) || (count & 3) || end > vpd->base.len)
+ return -EINVAL;
+
+ if (mutex_lock_killable(&vpd->lock))
+ return -EINTR;
+
+ ret = pci_vpd_pci22_wait(dev);
+ if (ret < 0)
+ goto out;
+
+ while (pos < end) {
+ u32 val;
+
+ val = *buf++;
+ val |= *buf++ << 8;
+ val |= *buf++ << 16;
+ val |= *buf++ << 24;
+
+ ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val);
+ if (ret < 0)
+ break;
+ ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
+ pos | PCI_VPD_ADDR_F);
+ if (ret < 0)
+ break;
+
+ vpd->busy = true;
+ vpd->flag = 0;
+ ret = pci_vpd_pci22_wait(dev);
+ if (ret < 0)
+ break;
+
+ pos += sizeof(u32);
+ }
+out:
+ mutex_unlock(&vpd->lock);
+ return ret ? ret : count;
+}
+
+static void pci_vpd_pci22_release(struct pci_dev *dev)
+{
+ kfree(container_of(dev->vpd, struct pci_vpd_pci22, base));
+}
+
+static const struct pci_vpd_ops pci_vpd_pci22_ops = {
+ .read = pci_vpd_pci22_read,
+ .write = pci_vpd_pci22_write,
+ .release = pci_vpd_pci22_release,
+};
+
+int pci_vpd_pci22_init(struct pci_dev *dev)
+{
+ struct pci_vpd_pci22 *vpd;
+ u8 cap;
+
+ cap = pci_find_capability(dev, PCI_CAP_ID_VPD);
+ if (!cap)
+ return -ENODEV;
+ vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC);
+ if (!vpd)
+ return -ENOMEM;
+
+ vpd->base.len = PCI_VPD_PCI22_SIZE;
+ vpd->base.ops = &pci_vpd_pci22_ops;
+ mutex_init(&vpd->lock);
+ vpd->cap = cap;
+ vpd->busy = false;
+ dev->vpd = &vpd->base;
+ return 0;
+}
+
/**
- * pci_block_user_cfg_access - Block userspace PCI config reads/writes
+ * pci_cfg_access_lock - Lock PCI config reads/writes
* @dev: pci device struct
*
- * When user access is blocked, any reads or writes to config space will
- * sleep until access is unblocked again. We don't allow nesting of
- * block/unblock calls.
+ * When access is locked, any userspace reads or writes to config
+ * space and concurrent lock requests will sleep until access is
+ * allowed via pci_cfg_access_unlocked again.
*/
-void pci_block_user_cfg_access(struct pci_dev *dev)
+void pci_cfg_access_lock(struct pci_dev *dev)
+{
+ might_sleep();
+
+ raw_spin_lock_irq(&pci_lock);
+ if (dev->block_cfg_access)
+ pci_wait_cfg(dev);
+ dev->block_cfg_access = 1;
+ raw_spin_unlock_irq(&pci_lock);
+}
+EXPORT_SYMBOL_GPL(pci_cfg_access_lock);
+
+/**
+ * pci_cfg_access_trylock - try to lock PCI config reads/writes
+ * @dev: pci device struct
+ *
+ * Same as pci_cfg_access_lock, but will return 0 if access is
+ * already locked, 1 otherwise. This function can be used from
+ * atomic contexts.
+ */
+bool pci_cfg_access_trylock(struct pci_dev *dev)
{
unsigned long flags;
- int was_blocked;
+ bool locked = true;
- spin_lock_irqsave(&pci_lock, flags);
- was_blocked = dev->block_ucfg_access;
- dev->block_ucfg_access = 1;
- spin_unlock_irqrestore(&pci_lock, flags);
+ raw_spin_lock_irqsave(&pci_lock, flags);
+ if (dev->block_cfg_access)
+ locked = false;
+ else
+ dev->block_cfg_access = 1;
+ raw_spin_unlock_irqrestore(&pci_lock, flags);
- /* If we BUG() inside the pci_lock, we're guaranteed to hose
- * the machine */
- BUG_ON(was_blocked);
+ return locked;
}
-EXPORT_SYMBOL_GPL(pci_block_user_cfg_access);
+EXPORT_SYMBOL_GPL(pci_cfg_access_trylock);
/**
- * pci_unblock_user_cfg_access - Unblock userspace PCI config reads/writes
+ * pci_cfg_access_unlock - Unlock PCI config reads/writes
* @dev: pci device struct
*
- * This function allows userspace PCI config accesses to resume.
+ * This function allows PCI config accesses to resume.
*/
-void pci_unblock_user_cfg_access(struct pci_dev *dev)
+void pci_cfg_access_unlock(struct pci_dev *dev)
{
unsigned long flags;
- spin_lock_irqsave(&pci_lock, flags);
+ raw_spin_lock_irqsave(&pci_lock, flags);
/* This indicates a problem in the caller, but we don't need
* to kill them, unlike a double-block above. */
- WARN_ON(!dev->block_ucfg_access);
+ WARN_ON(!dev->block_cfg_access);
+
+ dev->block_cfg_access = 0;
+ wake_up_all(&pci_cfg_wait);
+ raw_spin_unlock_irqrestore(&pci_lock, flags);
+}
+EXPORT_SYMBOL_GPL(pci_cfg_access_unlock);
+
+static inline int pcie_cap_version(const struct pci_dev *dev)
+{
+ return pcie_caps_reg(dev) & PCI_EXP_FLAGS_VERS;
+}
+
+static inline bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return type == PCI_EXP_TYPE_ENDPOINT ||
+ type == PCI_EXP_TYPE_LEG_END ||
+ type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_UPSTREAM ||
+ type == PCI_EXP_TYPE_DOWNSTREAM ||
+ type == PCI_EXP_TYPE_PCI_BRIDGE ||
+ type == PCI_EXP_TYPE_PCIE_BRIDGE;
+}
+
+static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return (type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_DOWNSTREAM) &&
+ pcie_caps_reg(dev) & PCI_EXP_FLAGS_SLOT;
+}
+
+static inline bool pcie_cap_has_rtctl(const struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_RC_EC;
+}
+
+static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos)
+{
+ if (!pci_is_pcie(dev))
+ return false;
+
+ switch (pos) {
+ case PCI_EXP_FLAGS:
+ return true;
+ case PCI_EXP_DEVCAP:
+ case PCI_EXP_DEVCTL:
+ case PCI_EXP_DEVSTA:
+ return true;
+ case PCI_EXP_LNKCAP:
+ case PCI_EXP_LNKCTL:
+ case PCI_EXP_LNKSTA:
+ return pcie_cap_has_lnkctl(dev);
+ case PCI_EXP_SLTCAP:
+ case PCI_EXP_SLTCTL:
+ case PCI_EXP_SLTSTA:
+ return pcie_cap_has_sltctl(dev);
+ case PCI_EXP_RTCTL:
+ case PCI_EXP_RTCAP:
+ case PCI_EXP_RTSTA:
+ return pcie_cap_has_rtctl(dev);
+ case PCI_EXP_DEVCAP2:
+ case PCI_EXP_DEVCTL2:
+ case PCI_EXP_LNKCAP2:
+ case PCI_EXP_LNKCTL2:
+ case PCI_EXP_LNKSTA2:
+ return pcie_cap_version(dev) > 1;
+ default:
+ return false;
+ }
+}
+
+/*
+ * Note that these accessor functions are only for the "PCI Express
+ * Capability" (see PCIe spec r3.0, sec 7.8). They do not apply to the
+ * other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.)
+ */
+int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val)
+{
+ int ret;
+
+ *val = 0;
+ if (pos & 1)
+ return -EINVAL;
+
+ if (pcie_capability_reg_implemented(dev, pos)) {
+ ret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val);
+ /*
+ * Reset *val to 0 if pci_read_config_word() fails, it may
+ * have been written as 0xFFFF if hardware error happens
+ * during pci_read_config_word().
+ */
+ if (ret)
+ *val = 0;
+ return ret;
+ }
+
+ /*
+ * For Functions that do not implement the Slot Capabilities,
+ * Slot Status, and Slot Control registers, these spaces must
+ * be hardwired to 0b, with the exception of the Presence Detect
+ * State bit in the Slot Status register of Downstream Ports,
+ * which must be hardwired to 1b. (PCIe Base Spec 3.0, sec 7.8)
+ */
+ if (pci_is_pcie(dev) && pos == PCI_EXP_SLTSTA &&
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
+ *val = PCI_EXP_SLTSTA_PDS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pcie_capability_read_word);
+
+int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val)
+{
+ int ret;
+
+ *val = 0;
+ if (pos & 3)
+ return -EINVAL;
+
+ if (pcie_capability_reg_implemented(dev, pos)) {
+ ret = pci_read_config_dword(dev, pci_pcie_cap(dev) + pos, val);
+ /*
+ * Reset *val to 0 if pci_read_config_dword() fails, it may
+ * have been written as 0xFFFFFFFF if hardware error happens
+ * during pci_read_config_dword().
+ */
+ if (ret)
+ *val = 0;
+ return ret;
+ }
+
+ if (pci_is_pcie(dev) && pos == PCI_EXP_SLTCTL &&
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
+ *val = PCI_EXP_SLTSTA_PDS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pcie_capability_read_dword);
+
+int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val)
+{
+ if (pos & 1)
+ return -EINVAL;
+
+ if (!pcie_capability_reg_implemented(dev, pos))
+ return 0;
+
+ return pci_write_config_word(dev, pci_pcie_cap(dev) + pos, val);
+}
+EXPORT_SYMBOL(pcie_capability_write_word);
+
+int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val)
+{
+ if (pos & 3)
+ return -EINVAL;
+
+ if (!pcie_capability_reg_implemented(dev, pos))
+ return 0;
+
+ return pci_write_config_dword(dev, pci_pcie_cap(dev) + pos, val);
+}
+EXPORT_SYMBOL(pcie_capability_write_dword);
+
+int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos,
+ u16 clear, u16 set)
+{
+ int ret;
+ u16 val;
+
+ ret = pcie_capability_read_word(dev, pos, &val);
+ if (!ret) {
+ val &= ~clear;
+ val |= set;
+ ret = pcie_capability_write_word(dev, pos, val);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(pcie_capability_clear_and_set_word);
+
+int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos,
+ u32 clear, u32 set)
+{
+ int ret;
+ u32 val;
+
+ ret = pcie_capability_read_dword(dev, pos, &val);
+ if (!ret) {
+ val &= ~clear;
+ val |= set;
+ ret = pcie_capability_write_dword(dev, pos, val);
+ }
- dev->block_ucfg_access = 0;
- wake_up_all(&pci_ucfg_wait);
- spin_unlock_irqrestore(&pci_lock, flags);
+ return ret;
}
-EXPORT_SYMBOL_GPL(pci_unblock_user_cfg_access);
+EXPORT_SYMBOL(pcie_capability_clear_and_set_dword);
diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
new file mode 100644
index 00000000000..a8099d4d0c9
--- /dev/null
+++ b/drivers/pci/ats.c
@@ -0,0 +1,375 @@
+/*
+ * drivers/pci/ats.c
+ *
+ * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
+ * Copyright (C) 2011 Advanced Micro Devices,
+ *
+ * PCI Express I/O Virtualization (IOV) support.
+ * Address Translation Service 1.0
+ * Page Request Interface added by Joerg Roedel <joerg.roedel@amd.com>
+ * PASID support added by Joerg Roedel <joerg.roedel@amd.com>
+ */
+
+#include <linux/export.h>
+#include <linux/pci-ats.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "pci.h"
+
+static int ats_alloc_one(struct pci_dev *dev, int ps)
+{
+ int pos;
+ u16 cap;
+ struct pci_ats *ats;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS);
+ if (!pos)
+ return -ENODEV;
+
+ ats = kzalloc(sizeof(*ats), GFP_KERNEL);
+ if (!ats)
+ return -ENOMEM;
+
+ ats->pos = pos;
+ ats->stu = ps;
+ pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap);
+ ats->qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) :
+ PCI_ATS_MAX_QDEP;
+ dev->ats = ats;
+
+ return 0;
+}
+
+static void ats_free_one(struct pci_dev *dev)
+{
+ kfree(dev->ats);
+ dev->ats = NULL;
+}
+
+/**
+ * pci_enable_ats - enable the ATS capability
+ * @dev: the PCI device
+ * @ps: the IOMMU page shift
+ *
+ * Returns 0 on success, or negative on failure.
+ */
+int pci_enable_ats(struct pci_dev *dev, int ps)
+{
+ int rc;
+ u16 ctrl;
+
+ BUG_ON(dev->ats && dev->ats->is_enabled);
+
+ if (ps < PCI_ATS_MIN_STU)
+ return -EINVAL;
+
+ if (dev->is_physfn || dev->is_virtfn) {
+ struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn;
+
+ mutex_lock(&pdev->sriov->lock);
+ if (pdev->ats)
+ rc = pdev->ats->stu == ps ? 0 : -EINVAL;
+ else
+ rc = ats_alloc_one(pdev, ps);
+
+ if (!rc)
+ pdev->ats->ref_cnt++;
+ mutex_unlock(&pdev->sriov->lock);
+ if (rc)
+ return rc;
+ }
+
+ if (!dev->is_physfn) {
+ rc = ats_alloc_one(dev, ps);
+ if (rc)
+ return rc;
+ }
+
+ ctrl = PCI_ATS_CTRL_ENABLE;
+ if (!dev->is_virtfn)
+ ctrl |= PCI_ATS_CTRL_STU(ps - PCI_ATS_MIN_STU);
+ pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl);
+
+ dev->ats->is_enabled = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_enable_ats);
+
+/**
+ * pci_disable_ats - disable the ATS capability
+ * @dev: the PCI device
+ */
+void pci_disable_ats(struct pci_dev *dev)
+{
+ u16 ctrl;
+
+ BUG_ON(!dev->ats || !dev->ats->is_enabled);
+
+ pci_read_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, &ctrl);
+ ctrl &= ~PCI_ATS_CTRL_ENABLE;
+ pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl);
+
+ dev->ats->is_enabled = 0;
+
+ if (dev->is_physfn || dev->is_virtfn) {
+ struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn;
+
+ mutex_lock(&pdev->sriov->lock);
+ pdev->ats->ref_cnt--;
+ if (!pdev->ats->ref_cnt)
+ ats_free_one(pdev);
+ mutex_unlock(&pdev->sriov->lock);
+ }
+
+ if (!dev->is_physfn)
+ ats_free_one(dev);
+}
+EXPORT_SYMBOL_GPL(pci_disable_ats);
+
+void pci_restore_ats_state(struct pci_dev *dev)
+{
+ u16 ctrl;
+
+ if (!pci_ats_enabled(dev))
+ return;
+ if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS))
+ BUG();
+
+ ctrl = PCI_ATS_CTRL_ENABLE;
+ if (!dev->is_virtfn)
+ ctrl |= PCI_ATS_CTRL_STU(dev->ats->stu - PCI_ATS_MIN_STU);
+
+ pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl);
+}
+EXPORT_SYMBOL_GPL(pci_restore_ats_state);
+
+/**
+ * pci_ats_queue_depth - query the ATS Invalidate Queue Depth
+ * @dev: the PCI device
+ *
+ * Returns the queue depth on success, or negative on failure.
+ *
+ * The ATS spec uses 0 in the Invalidate Queue Depth field to
+ * indicate that the function can accept 32 Invalidate Request.
+ * But here we use the `real' values (i.e. 1~32) for the Queue
+ * Depth; and 0 indicates the function shares the Queue with
+ * other functions (doesn't exclusively own a Queue).
+ */
+int pci_ats_queue_depth(struct pci_dev *dev)
+{
+ int pos;
+ u16 cap;
+
+ if (dev->is_virtfn)
+ return 0;
+
+ if (dev->ats)
+ return dev->ats->qdep;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS);
+ if (!pos)
+ return -ENODEV;
+
+ pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap);
+
+ return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) :
+ PCI_ATS_MAX_QDEP;
+}
+EXPORT_SYMBOL_GPL(pci_ats_queue_depth);
+
+#ifdef CONFIG_PCI_PRI
+/**
+ * pci_enable_pri - Enable PRI capability
+ * @ pdev: PCI device structure
+ *
+ * Returns 0 on success, negative value on error
+ */
+int pci_enable_pri(struct pci_dev *pdev, u32 reqs)
+{
+ u16 control, status;
+ u32 max_requests;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
+ if (!pos)
+ return -EINVAL;
+
+ pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
+ pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
+ if ((control & PCI_PRI_CTRL_ENABLE) ||
+ !(status & PCI_PRI_STATUS_STOPPED))
+ return -EBUSY;
+
+ pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ, &max_requests);
+ reqs = min(max_requests, reqs);
+ pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs);
+
+ control |= PCI_PRI_CTRL_ENABLE;
+ pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_enable_pri);
+
+/**
+ * pci_disable_pri - Disable PRI capability
+ * @pdev: PCI device structure
+ *
+ * Only clears the enabled-bit, regardless of its former value
+ */
+void pci_disable_pri(struct pci_dev *pdev)
+{
+ u16 control;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
+ if (!pos)
+ return;
+
+ pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
+ control &= ~PCI_PRI_CTRL_ENABLE;
+ pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
+}
+EXPORT_SYMBOL_GPL(pci_disable_pri);
+
+/**
+ * pci_reset_pri - Resets device's PRI state
+ * @pdev: PCI device structure
+ *
+ * The PRI capability must be disabled before this function is called.
+ * Returns 0 on success, negative value on error.
+ */
+int pci_reset_pri(struct pci_dev *pdev)
+{
+ u16 control;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
+ if (!pos)
+ return -EINVAL;
+
+ pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
+ if (control & PCI_PRI_CTRL_ENABLE)
+ return -EBUSY;
+
+ control |= PCI_PRI_CTRL_RESET;
+
+ pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_reset_pri);
+#endif /* CONFIG_PCI_PRI */
+
+#ifdef CONFIG_PCI_PASID
+/**
+ * pci_enable_pasid - Enable the PASID capability
+ * @pdev: PCI device structure
+ * @features: Features to enable
+ *
+ * Returns 0 on success, negative value on error. This function checks
+ * whether the features are actually supported by the device and returns
+ * an error if not.
+ */
+int pci_enable_pasid(struct pci_dev *pdev, int features)
+{
+ u16 control, supported;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
+ if (!pos)
+ return -EINVAL;
+
+ pci_read_config_word(pdev, pos + PCI_PASID_CTRL, &control);
+ pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
+
+ if (control & PCI_PASID_CTRL_ENABLE)
+ return -EINVAL;
+
+ supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
+
+ /* User wants to enable anything unsupported? */
+ if ((supported & features) != features)
+ return -EINVAL;
+
+ control = PCI_PASID_CTRL_ENABLE | features;
+
+ pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_enable_pasid);
+
+/**
+ * pci_disable_pasid - Disable the PASID capability
+ * @pdev: PCI device structure
+ *
+ */
+void pci_disable_pasid(struct pci_dev *pdev)
+{
+ u16 control = 0;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
+ if (!pos)
+ return;
+
+ pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
+}
+EXPORT_SYMBOL_GPL(pci_disable_pasid);
+
+/**
+ * pci_pasid_features - Check which PASID features are supported
+ * @pdev: PCI device structure
+ *
+ * Returns a negative value when no PASI capability is present.
+ * Otherwise is returns a bitmask with supported features. Current
+ * features reported are:
+ * PCI_PASID_CAP_EXEC - Execute permission supported
+ * PCI_PASID_CAP_PRIV - Privileged mode supported
+ */
+int pci_pasid_features(struct pci_dev *pdev)
+{
+ u16 supported;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
+ if (!pos)
+ return -EINVAL;
+
+ pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
+
+ supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
+
+ return supported;
+}
+EXPORT_SYMBOL_GPL(pci_pasid_features);
+
+#define PASID_NUMBER_SHIFT 8
+#define PASID_NUMBER_MASK (0x1f << PASID_NUMBER_SHIFT)
+/**
+ * pci_max_pasid - Get maximum number of PASIDs supported by device
+ * @pdev: PCI device structure
+ *
+ * Returns negative value when PASID capability is not present.
+ * Otherwise it returns the numer of supported PASIDs.
+ */
+int pci_max_pasids(struct pci_dev *pdev)
+{
+ u16 supported;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
+ if (!pos)
+ return -EINVAL;
+
+ pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
+
+ supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT;
+
+ return (1 << supported);
+}
+EXPORT_SYMBOL_GPL(pci_max_pasids);
+#endif /* CONFIG_PCI_PASID */
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index d708358326e..73aef51a28f 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -13,39 +13,139 @@
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/proc_fs.h>
-#include <linux/init.h>
+#include <linux/slab.h>
#include "pci.h"
-/**
- * pci_bus_alloc_resource - allocate a resource from a parent bus
- * @bus: PCI bus
- * @res: resource to allocate
- * @size: size of resource to allocate
- * @align: alignment of resource to allocate
- * @min: minimum /proc/iomem address to allocate
- * @type_mask: IORESOURCE_* type flags
- * @alignf: resource alignment function
- * @alignf_data: data argument for resource alignment function
- *
- * Given the PCI bus a device resides on, the size, minimum address,
- * alignment and type, try to find an acceptable resource allocation
- * for a specific device resource.
+void pci_add_resource_offset(struct list_head *resources, struct resource *res,
+ resource_size_t offset)
+{
+ struct pci_host_bridge_window *window;
+
+ window = kzalloc(sizeof(struct pci_host_bridge_window), GFP_KERNEL);
+ if (!window) {
+ printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res);
+ return;
+ }
+
+ window->res = res;
+ window->offset = offset;
+ list_add_tail(&window->list, resources);
+}
+EXPORT_SYMBOL(pci_add_resource_offset);
+
+void pci_add_resource(struct list_head *resources, struct resource *res)
+{
+ pci_add_resource_offset(resources, res, 0);
+}
+EXPORT_SYMBOL(pci_add_resource);
+
+void pci_free_resource_list(struct list_head *resources)
+{
+ struct pci_host_bridge_window *window, *tmp;
+
+ list_for_each_entry_safe(window, tmp, resources, list) {
+ list_del(&window->list);
+ kfree(window);
+ }
+}
+EXPORT_SYMBOL(pci_free_resource_list);
+
+void pci_bus_add_resource(struct pci_bus *bus, struct resource *res,
+ unsigned int flags)
+{
+ struct pci_bus_resource *bus_res;
+
+ bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL);
+ if (!bus_res) {
+ dev_err(&bus->dev, "can't add %pR resource\n", res);
+ return;
+ }
+
+ bus_res->res = res;
+ bus_res->flags = flags;
+ list_add_tail(&bus_res->list, &bus->resources);
+}
+
+struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n)
+{
+ struct pci_bus_resource *bus_res;
+
+ if (n < PCI_BRIDGE_RESOURCE_NUM)
+ return bus->resource[n];
+
+ n -= PCI_BRIDGE_RESOURCE_NUM;
+ list_for_each_entry(bus_res, &bus->resources, list) {
+ if (n-- == 0)
+ return bus_res->res;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(pci_bus_resource_n);
+
+void pci_bus_remove_resources(struct pci_bus *bus)
+{
+ int i;
+ struct pci_bus_resource *bus_res, *tmp;
+
+ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
+ bus->resource[i] = NULL;
+
+ list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
+ list_del(&bus_res->list);
+ kfree(bus_res);
+ }
+}
+
+static struct pci_bus_region pci_32_bit = {0, 0xffffffffULL};
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+static struct pci_bus_region pci_64_bit = {0,
+ (dma_addr_t) 0xffffffffffffffffULL};
+static struct pci_bus_region pci_high = {(dma_addr_t) 0x100000000ULL,
+ (dma_addr_t) 0xffffffffffffffffULL};
+#endif
+
+/*
+ * @res contains CPU addresses. Clip it so the corresponding bus addresses
+ * on @bus are entirely within @region. This is used to control the bus
+ * addresses of resources we allocate, e.g., we may need a resource that
+ * can be mapped by a 32-bit BAR.
*/
-int
-pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
+static void pci_clip_resource_to_region(struct pci_bus *bus,
+ struct resource *res,
+ struct pci_bus_region *region)
+{
+ struct pci_bus_region r;
+
+ pcibios_resource_to_bus(bus, &r, res);
+ if (r.start < region->start)
+ r.start = region->start;
+ if (r.end > region->end)
+ r.end = region->end;
+
+ if (r.end < r.start)
+ res->end = res->start - 1;
+ else
+ pcibios_bus_to_resource(bus, res, &r);
+}
+
+static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
resource_size_t size, resource_size_t align,
- resource_size_t min, unsigned int type_mask,
- void (*alignf)(void *, struct resource *, resource_size_t,
- resource_size_t),
- void *alignf_data)
+ resource_size_t min, unsigned long type_mask,
+ resource_size_t (*alignf)(void *,
+ const struct resource *,
+ resource_size_t,
+ resource_size_t),
+ void *alignf_data,
+ struct pci_bus_region *region)
{
- int i, ret = -ENOMEM;
+ int i, ret;
+ struct resource *r, avail;
+ resource_size_t max;
- type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
+ type_mask |= IORESOURCE_TYPE_BITS;
- for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
- struct resource *r = bus->resource[i];
+ pci_bus_for_each_resource(bus, r, i) {
if (!r)
continue;
@@ -59,122 +159,129 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
!(res->flags & IORESOURCE_PREFETCH))
continue;
+ avail = *r;
+ pci_clip_resource_to_region(bus, &avail, region);
+
+ /*
+ * "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to
+ * protect badly documented motherboard resources, but if
+ * this is an already-configured bridge window, its start
+ * overrides "min".
+ */
+ if (avail.start)
+ min = avail.start;
+
+ max = avail.end;
+
/* Ok, try it out.. */
- ret = allocate_resource(r, res, size,
- r->start ? : min,
- -1, align,
- alignf, alignf_data);
+ ret = allocate_resource(r, res, size, min, max,
+ align, alignf, alignf_data);
if (ret == 0)
- break;
+ return 0;
}
- return ret;
+ return -ENOMEM;
}
/**
- * add a single device
- * @dev: device to add
+ * pci_bus_alloc_resource - allocate a resource from a parent bus
+ * @bus: PCI bus
+ * @res: resource to allocate
+ * @size: size of resource to allocate
+ * @align: alignment of resource to allocate
+ * @min: minimum /proc/iomem address to allocate
+ * @type_mask: IORESOURCE_* type flags
+ * @alignf: resource alignment function
+ * @alignf_data: data argument for resource alignment function
*
- * This adds a single pci device to the global
- * device list and adds sysfs and procfs entries
+ * Given the PCI bus a device resides on, the size, minimum address,
+ * alignment and type, try to find an acceptable resource allocation
+ * for a specific device resource.
*/
-int pci_bus_add_device(struct pci_dev *dev)
+int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
+ resource_size_t size, resource_size_t align,
+ resource_size_t min, unsigned long type_mask,
+ resource_size_t (*alignf)(void *,
+ const struct resource *,
+ resource_size_t,
+ resource_size_t),
+ void *alignf_data)
{
- int retval;
- retval = device_add(&dev->dev);
- if (retval)
- return retval;
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ int rc;
- down_write(&pci_bus_sem);
- list_add_tail(&dev->global_list, &pci_devices);
- up_write(&pci_bus_sem);
+ if (res->flags & IORESOURCE_MEM_64) {
+ rc = pci_bus_alloc_from_region(bus, res, size, align, min,
+ type_mask, alignf, alignf_data,
+ &pci_high);
+ if (rc == 0)
+ return 0;
- pci_proc_attach_device(dev);
- pci_create_sysfs_dev_files(dev);
- return 0;
+ return pci_bus_alloc_from_region(bus, res, size, align, min,
+ type_mask, alignf, alignf_data,
+ &pci_64_bit);
+ }
+#endif
+
+ return pci_bus_alloc_from_region(bus, res, size, align, min,
+ type_mask, alignf, alignf_data,
+ &pci_32_bit);
}
+EXPORT_SYMBOL(pci_bus_alloc_resource);
+
+void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { }
/**
- * pci_bus_add_devices - insert newly discovered PCI devices
- * @bus: bus to check for new devices
- *
- * Add newly discovered PCI devices (which are on the bus->devices
- * list) to the global PCI device list, add the sysfs and procfs
- * entries. Where a bridge is found, add the discovered bus to
- * the parents list of child buses, and recurse (breadth-first
- * to be compatible with 2.4)
+ * pci_bus_add_device - start driver for a single device
+ * @dev: device to add
*
- * Call hotplug for each new devices.
+ * This adds add sysfs entries and start device drivers
*/
-void pci_bus_add_devices(struct pci_bus *bus)
+void pci_bus_add_device(struct pci_dev *dev)
{
- struct pci_dev *dev;
- struct pci_bus *child_bus;
int retval;
- list_for_each_entry(dev, &bus->devices, bus_list) {
- /*
- * Skip already-present devices (which are on the
- * global device list.)
- */
- if (!list_empty(&dev->global_list))
- continue;
- retval = pci_bus_add_device(dev);
- if (retval)
- dev_err(&dev->dev, "Error adding device, continuing\n");
- }
-
- list_for_each_entry(dev, &bus->devices, bus_list) {
+ /*
+ * Can not put in pci_device_add yet because resources
+ * are not assigned yet for some devices.
+ */
+ pci_fixup_device(pci_fixup_final, dev);
+ pci_create_sysfs_dev_files(dev);
+ pci_proc_attach_device(dev);
- BUG_ON(list_empty(&dev->global_list));
+ dev->match_driver = true;
+ retval = device_attach(&dev->dev);
+ WARN_ON(retval < 0);
- /*
- * If there is an unattached subordinate bus, attach
- * it and then scan for unattached PCI devices.
- */
- if (dev->subordinate) {
- if (list_empty(&dev->subordinate->node)) {
- down_write(&pci_bus_sem);
- list_add_tail(&dev->subordinate->node,
- &dev->bus->children);
- up_write(&pci_bus_sem);
- }
- pci_bus_add_devices(dev->subordinate);
-
- /* register the bus with sysfs as the parent is now
- * properly registered. */
- child_bus = dev->subordinate;
- if (child_bus->is_added)
- continue;
- child_bus->dev.parent = child_bus->bridge;
- retval = device_register(&child_bus->dev);
- if (retval)
- dev_err(&dev->dev, "Error registering pci_bus,"
- " continuing...\n");
- else {
- child_bus->is_added = 1;
- retval = device_create_file(&child_bus->dev,
- &dev_attr_cpuaffinity);
- }
- if (retval)
- dev_err(&dev->dev, "Error creating cpuaffinity"
- " file, continuing...\n");
- }
- }
+ dev->is_added = 1;
}
+EXPORT_SYMBOL_GPL(pci_bus_add_device);
-void pci_enable_bridges(struct pci_bus *bus)
+/**
+ * pci_bus_add_devices - start driver for PCI devices
+ * @bus: bus to check for new devices
+ *
+ * Start driver for PCI devices and add some sysfs entries.
+ */
+void pci_bus_add_devices(const struct pci_bus *bus)
{
struct pci_dev *dev;
- int retval;
+ struct pci_bus *child;
list_for_each_entry(dev, &bus->devices, bus_list) {
- if (dev->subordinate) {
- retval = pci_enable_device(dev);
- pci_set_master(dev);
- pci_enable_bridges(dev->subordinate);
- }
+ /* Skip already-added devices */
+ if (dev->is_added)
+ continue;
+ pci_bus_add_device(dev);
+ }
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ BUG_ON(!dev->is_added);
+ child = dev->subordinate;
+ if (child)
+ pci_bus_add_devices(child);
}
}
+EXPORT_SYMBOL(pci_bus_add_devices);
/** pci_walk_bus - walk devices on/under bus, calling callback.
* @top bus whose devices should be walked
@@ -184,13 +291,18 @@ void pci_enable_bridges(struct pci_bus *bus)
* Walk the given bus, including any bridged devices
* on buses under this bus. Call the provided callback
* on each device found.
+ *
+ * We check the return of @cb each time. If it returns anything
+ * other than 0, we break out.
+ *
*/
-void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *),
+void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
void *userdata)
{
struct pci_dev *dev;
struct pci_bus *bus;
struct list_head *next;
+ int retval;
bus = top;
down_read(&pci_bus_sem);
@@ -212,15 +324,26 @@ void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *),
} else
next = dev->bus_list.next;
- /* Run device routines with the device locked */
- down(&dev->dev.sem);
- cb(dev, userdata);
- up(&dev->dev.sem);
+ retval = cb(dev, userdata);
+ if (retval)
+ break;
}
up_read(&pci_bus_sem);
}
+EXPORT_SYMBOL_GPL(pci_walk_bus);
+
+struct pci_bus *pci_bus_get(struct pci_bus *bus)
+{
+ if (bus)
+ get_device(&bus->dev);
+ return bus;
+}
+EXPORT_SYMBOL(pci_bus_get);
+
+void pci_bus_put(struct pci_bus *bus)
+{
+ if (bus)
+ put_device(&bus->dev);
+}
+EXPORT_SYMBOL(pci_bus_put);
-EXPORT_SYMBOL(pci_bus_alloc_resource);
-EXPORT_SYMBOL_GPL(pci_bus_add_device);
-EXPORT_SYMBOL(pci_bus_add_devices);
-EXPORT_SYMBOL(pci_enable_bridges);
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c
deleted file mode 100644
index f941f609dbf..00000000000
--- a/drivers/pci/dmar.c
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright (c) 2006, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place - Suite 330, Boston, MA 02111-1307 USA.
- *
- * Copyright (C) 2006-2008 Intel Corporation
- * Author: Ashok Raj <ashok.raj@intel.com>
- * 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
- * reported to OS through BIOS via DMA remapping reporting (DMAR) ACPI
- * tables.
- */
-
-#include <linux/pci.h>
-#include <linux/dmar.h>
-#include "iova.h"
-#include "intel-iommu.h"
-
-#undef PREFIX
-#define PREFIX "DMAR:"
-
-/* No locks are needed as DMA remapping hardware unit
- * list is constructed at boot time and hotplug of
- * 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;
-
-static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
-{
- /*
- * add INCLUDE_ALL at the tail, so scan the list will find it at
- * the very end.
- */
- if (drhd->include_all)
- list_add_tail(&drhd->list, &dmar_drhd_units);
- else
- 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)
-{
- struct pci_bus *bus;
- struct pci_dev *pdev = NULL;
- struct acpi_dmar_pci_path *path;
- int count;
-
- bus = pci_find_bus(segment, scope->bus);
- path = (struct acpi_dmar_pci_path *)(scope + 1);
- count = (scope->length - sizeof(struct acpi_dmar_device_scope))
- / sizeof(struct acpi_dmar_pci_path);
-
- while (count) {
- if (pdev)
- pci_dev_put(pdev);
- /*
- * Some BIOSes list non-exist devices in DMAR table, just
- * ignore it
- */
- if (!bus) {
- printk(KERN_WARNING
- PREFIX "Device scope bus [%d] not found\n",
- scope->bus);
- break;
- }
- pdev = pci_get_slot(bus, PCI_DEVFN(path->dev, path->fn));
- if (!pdev) {
- printk(KERN_WARNING PREFIX
- "Device scope device [%04x:%02x:%02x.%02x] not found\n",
- segment, bus->number, path->dev, path->fn);
- break;
- }
- path ++;
- count --;
- bus = pdev->subordinate;
- }
- if (!pdev) {
- printk(KERN_WARNING PREFIX
- "Device scope device [%04x:%02x:%02x.%02x] not found\n",
- segment, scope->bus, path->dev, path->fn);
- *dev = NULL;
- return 0;
- }
- if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT && \
- pdev->subordinate) || (scope->entry_type == \
- ACPI_DMAR_SCOPE_TYPE_BRIDGE && !pdev->subordinate)) {
- pci_dev_put(pdev);
- printk(KERN_WARNING PREFIX
- "Device scope type does not match for %s\n",
- pci_name(pdev));
- return -EINVAL;
- }
- *dev = pdev;
- return 0;
-}
-
-static int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
- struct pci_dev ***devices, u16 segment)
-{
- struct acpi_dmar_device_scope *scope;
- void * tmp = start;
- int index;
- int ret;
-
- *cnt = 0;
- while (start < end) {
- scope = start;
- if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
- scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE)
- (*cnt)++;
- else
- printk(KERN_WARNING PREFIX
- "Unsupported device scope\n");
- start += scope->length;
- }
- if (*cnt == 0)
- return 0;
-
- *devices = kcalloc(*cnt, sizeof(struct pci_dev *), GFP_KERNEL);
- if (!*devices)
- return -ENOMEM;
-
- start = tmp;
- index = 0;
- while (start < end) {
- scope = start;
- if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
- scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) {
- ret = dmar_parse_one_dev_scope(scope,
- &(*devices)[index], segment);
- if (ret) {
- kfree(*devices);
- return ret;
- }
- index ++;
- }
- start += scope->length;
- }
-
- return 0;
-}
-
-/**
- * dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition
- * structure which uniquely represent one DMA remapping hardware unit
- * present in the platform
- */
-static int __init
-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;
-
- drhd = (struct acpi_dmar_hardware_unit *)header;
- dmaru->reg_base_addr = drhd->address;
- dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
-
- if (!dmaru->include_all)
- ret = dmar_parse_dev_scope((void *)(drhd + 1),
- ((void *)drhd) + header->length,
- &dmaru->devices_cnt, &dmaru->devices,
- drhd->segment);
- else {
- /* Only allow one INCLUDE_ALL */
- if (include_all) {
- printk(KERN_WARNING PREFIX "Only one INCLUDE_ALL "
- "device scope is allowed\n");
- ret = -EINVAL;
- }
- include_all = 1;
- }
-
- if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all))
- kfree(dmaru);
- else
- dmar_register_drhd_unit(dmaru);
- return ret;
-}
-
-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;
-
- rmrr = (struct acpi_dmar_reserved_memory *)header;
- rmrru->base_address = rmrr->base_address;
- rmrru->end_address = rmrr->end_address;
- ret = dmar_parse_dev_scope((void *)(rmrr + 1),
- ((void *)rmrr) + header->length,
- &rmrru->devices_cnt, &rmrru->devices, rmrr->segment);
-
- if (ret || (rmrru->devices_cnt == 0))
- kfree(rmrru);
- else
- dmar_register_rmrr_unit(rmrru);
- return ret;
-}
-
-static void __init
-dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
-{
- struct acpi_dmar_hardware_unit *drhd;
- struct acpi_dmar_reserved_memory *rmrr;
-
- switch (header->type) {
- case ACPI_DMAR_TYPE_HARDWARE_UNIT:
- drhd = (struct acpi_dmar_hardware_unit *)header;
- printk (KERN_INFO PREFIX
- "DRHD (flags: 0x%08x)base: 0x%016Lx\n",
- drhd->flags, drhd->address);
- break;
- case ACPI_DMAR_TYPE_RESERVED_MEMORY:
- rmrr = (struct acpi_dmar_reserved_memory *)header;
-
- printk (KERN_INFO PREFIX
- "RMRR base: 0x%016Lx end: 0x%016Lx\n",
- rmrr->base_address, rmrr->end_address);
- break;
- }
-}
-
-/**
- * parse_dmar_table - parses the DMA reporting table
- */
-static int __init
-parse_dmar_table(void)
-{
- struct acpi_table_dmar *dmar;
- struct acpi_dmar_header *entry_header;
- int ret = 0;
-
- dmar = (struct acpi_table_dmar *)dmar_tbl;
- if (!dmar)
- return -ENODEV;
-
- if (dmar->width < PAGE_SHIFT_4K - 1) {
- printk(KERN_WARNING PREFIX "Invalid DMAR haw\n");
- return -EINVAL;
- }
-
- printk (KERN_INFO PREFIX "Host address width %d\n",
- dmar->width + 1);
-
- entry_header = (struct acpi_dmar_header *)(dmar + 1);
- while (((unsigned long)entry_header) <
- (((unsigned long)dmar) + dmar_tbl->length)) {
- dmar_table_print_dmar_entry(entry_header);
-
- switch (entry_header->type) {
- case ACPI_DMAR_TYPE_HARDWARE_UNIT:
- ret = dmar_parse_one_drhd(entry_header);
- break;
- case ACPI_DMAR_TYPE_RESERVED_MEMORY:
- ret = dmar_parse_one_rmrr(entry_header);
- break;
- default:
- printk(KERN_WARNING PREFIX
- "Unknown DMAR structure type\n");
- ret = 0; /* for forward compatibility */
- break;
- }
- if (ret)
- break;
-
- entry_header = ((void *)entry_header + entry_header->length);
- }
- return ret;
-}
-
-
-int __init dmar_table_init(void)
-{
-
- int ret;
-
- ret = parse_dmar_table();
- if (ret) {
- printk(KERN_INFO PREFIX "parse DMAR table failure.\n");
- return ret;
- }
-
- if (list_empty(&dmar_drhd_units)) {
- printk(KERN_INFO PREFIX "No DMAR devices found\n");
- return -ENODEV;
- }
-
- if (list_empty(&dmar_rmrr_units)) {
- printk(KERN_INFO PREFIX "No RMRR found\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-/**
- * early_dmar_detect - checks to see if the platform supports DMAR devices
- */
-int __init early_dmar_detect(void)
-{
- acpi_status status = AE_OK;
-
- /* if we could find DMAR table, then there are DMAR devices */
- status = acpi_get_table(ACPI_SIG_DMAR, 0,
- (struct acpi_table_header **)&dmar_tbl);
-
- if (ACPI_SUCCESS(status) && !dmar_tbl) {
- printk (KERN_WARNING PREFIX "Unable to map DMAR\n");
- status = AE_NOT_FOUND;
- }
-
- return (ACPI_SUCCESS(status) ? 1 : 0);
-}
diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c
new file mode 100644
index 00000000000..0e5f3c95af5
--- /dev/null
+++ b/drivers/pci/host-bridge.c
@@ -0,0 +1,84 @@
+/*
+ * host bridge related code
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+
+#include "pci.h"
+
+static struct pci_bus *find_pci_root_bus(struct pci_bus *bus)
+{
+ while (bus->parent)
+ bus = bus->parent;
+
+ return bus;
+}
+
+static struct pci_host_bridge *find_pci_host_bridge(struct pci_bus *bus)
+{
+ struct pci_bus *root_bus = find_pci_root_bus(bus);
+
+ return to_pci_host_bridge(root_bus->bridge);
+}
+
+void pci_set_host_bridge_release(struct pci_host_bridge *bridge,
+ void (*release_fn)(struct pci_host_bridge *),
+ void *release_data)
+{
+ bridge->release_fn = release_fn;
+ bridge->release_data = release_data;
+}
+
+void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region,
+ struct resource *res)
+{
+ struct pci_host_bridge *bridge = find_pci_host_bridge(bus);
+ struct pci_host_bridge_window *window;
+ resource_size_t offset = 0;
+
+ list_for_each_entry(window, &bridge->windows, list) {
+ if (resource_contains(window->res, res)) {
+ offset = window->offset;
+ break;
+ }
+ }
+
+ region->start = res->start - offset;
+ region->end = res->end - offset;
+}
+EXPORT_SYMBOL(pcibios_resource_to_bus);
+
+static bool region_contains(struct pci_bus_region *region1,
+ struct pci_bus_region *region2)
+{
+ return region1->start <= region2->start && region1->end >= region2->end;
+}
+
+void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res,
+ struct pci_bus_region *region)
+{
+ struct pci_host_bridge *bridge = find_pci_host_bridge(bus);
+ struct pci_host_bridge_window *window;
+ resource_size_t offset = 0;
+
+ list_for_each_entry(window, &bridge->windows, list) {
+ struct pci_bus_region bus_region;
+
+ if (resource_type(res) != resource_type(window->res))
+ continue;
+
+ bus_region.start = window->res->start - window->offset;
+ bus_region.end = window->res->end - window->offset;
+
+ if (region_contains(&bus_region, region)) {
+ offset = window->offset;
+ break;
+ }
+ }
+
+ res->start = region->start + offset;
+ res->end = region->end + offset;
+}
+EXPORT_SYMBOL(pcibios_bus_to_resource);
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
new file mode 100644
index 00000000000..21df477be0c
--- /dev/null
+++ b/drivers/pci/host/Kconfig
@@ -0,0 +1,49 @@
+menu "PCI host controller drivers"
+ depends on PCI
+
+config PCI_MVEBU
+ bool "Marvell EBU PCIe controller"
+ depends on ARCH_MVEBU || ARCH_DOVE || ARCH_KIRKWOOD
+ depends on OF
+
+config PCIE_DW
+ bool
+
+config PCI_EXYNOS
+ bool "Samsung Exynos PCIe controller"
+ depends on SOC_EXYNOS5440
+ select PCIEPORTBUS
+ select PCIE_DW
+
+config PCI_IMX6
+ bool "Freescale i.MX6 PCIe controller"
+ depends on SOC_IMX6Q
+ select PCIEPORTBUS
+ select PCIE_DW
+
+config PCI_TEGRA
+ bool "NVIDIA Tegra PCIe controller"
+ depends on ARCH_TEGRA
+
+config PCI_RCAR_GEN2
+ bool "Renesas R-Car Gen2 Internal PCI controller"
+ depends on ARCH_SHMOBILE || (ARM && COMPILE_TEST)
+ help
+ Say Y here if you want internal PCI support on R-Car Gen2 SoC.
+ There are 3 internal PCI controllers available with a single
+ built-in EHCI/OHCI host controller present on each one.
+
+config PCI_RCAR_GEN2_PCIE
+ bool "Renesas R-Car PCIe controller"
+ depends on ARCH_SHMOBILE || (ARM && COMPILE_TEST)
+ help
+ Say Y here if you want PCIe controller support on R-Car Gen2 SoCs.
+
+config PCI_HOST_GENERIC
+ bool "Generic PCI host controller"
+ depends on ARM && OF
+ help
+ Say Y here if you want to support a simple generic PCI host
+ controller, such as the one emulated by kvmtool.
+
+endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
new file mode 100644
index 00000000000..611ba4b48c9
--- /dev/null
+++ b/drivers/pci/host/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_PCIE_DW) += pcie-designware.o
+obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
+obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
+obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
+obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
+obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
+obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
+obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
new file mode 100644
index 00000000000..c5d0ca38450
--- /dev/null
+++ b/drivers/pci/host/pci-exynos.c
@@ -0,0 +1,667 @@
+/*
+ * PCIe host controller driver for Samsung EXYNOS SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+#define to_exynos_pcie(x) container_of(x, struct exynos_pcie, pp)
+
+struct exynos_pcie {
+ void __iomem *elbi_base;
+ void __iomem *phy_base;
+ void __iomem *block_base;
+ int reset_gpio;
+ struct clk *clk;
+ struct clk *bus_clk;
+ struct pcie_port pp;
+};
+
+/* PCIe ELBI registers */
+#define PCIE_IRQ_PULSE 0x000
+#define IRQ_INTA_ASSERT (0x1 << 0)
+#define IRQ_INTB_ASSERT (0x1 << 2)
+#define IRQ_INTC_ASSERT (0x1 << 4)
+#define IRQ_INTD_ASSERT (0x1 << 6)
+#define PCIE_IRQ_LEVEL 0x004
+#define PCIE_IRQ_SPECIAL 0x008
+#define PCIE_IRQ_EN_PULSE 0x00c
+#define PCIE_IRQ_EN_LEVEL 0x010
+#define IRQ_MSI_ENABLE (0x1 << 2)
+#define PCIE_IRQ_EN_SPECIAL 0x014
+#define PCIE_PWR_RESET 0x018
+#define PCIE_CORE_RESET 0x01c
+#define PCIE_CORE_RESET_ENABLE (0x1 << 0)
+#define PCIE_STICKY_RESET 0x020
+#define PCIE_NONSTICKY_RESET 0x024
+#define PCIE_APP_INIT_RESET 0x028
+#define PCIE_APP_LTSSM_ENABLE 0x02c
+#define PCIE_ELBI_RDLH_LINKUP 0x064
+#define PCIE_ELBI_LTSSM_ENABLE 0x1
+#define PCIE_ELBI_SLV_AWMISC 0x11c
+#define PCIE_ELBI_SLV_ARMISC 0x120
+#define PCIE_ELBI_SLV_DBI_ENABLE (0x1 << 21)
+
+/* PCIe Purple registers */
+#define PCIE_PHY_GLOBAL_RESET 0x000
+#define PCIE_PHY_COMMON_RESET 0x004
+#define PCIE_PHY_CMN_REG 0x008
+#define PCIE_PHY_MAC_RESET 0x00c
+#define PCIE_PHY_PLL_LOCKED 0x010
+#define PCIE_PHY_TRSVREG_RESET 0x020
+#define PCIE_PHY_TRSV_RESET 0x024
+
+/* PCIe PHY registers */
+#define PCIE_PHY_IMPEDANCE 0x004
+#define PCIE_PHY_PLL_DIV_0 0x008
+#define PCIE_PHY_PLL_BIAS 0x00c
+#define PCIE_PHY_DCC_FEEDBACK 0x014
+#define PCIE_PHY_PLL_DIV_1 0x05c
+#define PCIE_PHY_COMMON_POWER 0x064
+#define PCIE_PHY_COMMON_PD_CMN (0x1 << 3)
+#define PCIE_PHY_TRSV0_EMP_LVL 0x084
+#define PCIE_PHY_TRSV0_DRV_LVL 0x088
+#define PCIE_PHY_TRSV0_RXCDR 0x0ac
+#define PCIE_PHY_TRSV0_POWER 0x0c4
+#define PCIE_PHY_TRSV0_PD_TSV (0x1 << 7)
+#define PCIE_PHY_TRSV0_LVCC 0x0dc
+#define PCIE_PHY_TRSV1_EMP_LVL 0x144
+#define PCIE_PHY_TRSV1_RXCDR 0x16c
+#define PCIE_PHY_TRSV1_POWER 0x184
+#define PCIE_PHY_TRSV1_PD_TSV (0x1 << 7)
+#define PCIE_PHY_TRSV1_LVCC 0x19c
+#define PCIE_PHY_TRSV2_EMP_LVL 0x204
+#define PCIE_PHY_TRSV2_RXCDR 0x22c
+#define PCIE_PHY_TRSV2_POWER 0x244
+#define PCIE_PHY_TRSV2_PD_TSV (0x1 << 7)
+#define PCIE_PHY_TRSV2_LVCC 0x25c
+#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4
+#define PCIE_PHY_TRSV3_RXCDR 0x2ec
+#define PCIE_PHY_TRSV3_POWER 0x304
+#define PCIE_PHY_TRSV3_PD_TSV (0x1 << 7)
+#define PCIE_PHY_TRSV3_LVCC 0x31c
+
+static inline void exynos_elb_writel(struct exynos_pcie *pcie, u32 val, u32 reg)
+{
+ writel(val, pcie->elbi_base + reg);
+}
+
+static inline u32 exynos_elb_readl(struct exynos_pcie *pcie, u32 reg)
+{
+ return readl(pcie->elbi_base + reg);
+}
+
+static inline void exynos_phy_writel(struct exynos_pcie *pcie, u32 val, u32 reg)
+{
+ writel(val, pcie->phy_base + reg);
+}
+
+static inline u32 exynos_phy_readl(struct exynos_pcie *pcie, u32 reg)
+{
+ return readl(pcie->phy_base + reg);
+}
+
+static inline void exynos_blk_writel(struct exynos_pcie *pcie, u32 val, u32 reg)
+{
+ writel(val, pcie->block_base + reg);
+}
+
+static inline u32 exynos_blk_readl(struct exynos_pcie *pcie, u32 reg)
+{
+ return readl(pcie->block_base + reg);
+}
+
+static void exynos_pcie_sideband_dbi_w_mode(struct pcie_port *pp, bool on)
+{
+ u32 val;
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ if (on) {
+ val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_AWMISC);
+ val |= PCIE_ELBI_SLV_DBI_ENABLE;
+ exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_AWMISC);
+ } else {
+ val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_AWMISC);
+ val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
+ exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_AWMISC);
+ }
+}
+
+static void exynos_pcie_sideband_dbi_r_mode(struct pcie_port *pp, bool on)
+{
+ u32 val;
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ if (on) {
+ val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_ARMISC);
+ val |= PCIE_ELBI_SLV_DBI_ENABLE;
+ exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_ARMISC);
+ } else {
+ val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_ARMISC);
+ val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
+ exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_ARMISC);
+ }
+}
+
+static void exynos_pcie_assert_core_reset(struct pcie_port *pp)
+{
+ u32 val;
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET);
+ val &= ~PCIE_CORE_RESET_ENABLE;
+ exynos_elb_writel(exynos_pcie, val, PCIE_CORE_RESET);
+ exynos_elb_writel(exynos_pcie, 0, PCIE_PWR_RESET);
+ exynos_elb_writel(exynos_pcie, 0, PCIE_STICKY_RESET);
+ exynos_elb_writel(exynos_pcie, 0, PCIE_NONSTICKY_RESET);
+}
+
+static void exynos_pcie_deassert_core_reset(struct pcie_port *pp)
+{
+ u32 val;
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET);
+ val |= PCIE_CORE_RESET_ENABLE;
+
+ exynos_elb_writel(exynos_pcie, val, PCIE_CORE_RESET);
+ exynos_elb_writel(exynos_pcie, 1, PCIE_STICKY_RESET);
+ exynos_elb_writel(exynos_pcie, 1, PCIE_NONSTICKY_RESET);
+ exynos_elb_writel(exynos_pcie, 1, PCIE_APP_INIT_RESET);
+ exynos_elb_writel(exynos_pcie, 0, PCIE_APP_INIT_RESET);
+ exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_MAC_RESET);
+}
+
+static void exynos_pcie_assert_phy_reset(struct pcie_port *pp)
+{
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_MAC_RESET);
+ exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_GLOBAL_RESET);
+}
+
+static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp)
+{
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_GLOBAL_RESET);
+ exynos_elb_writel(exynos_pcie, 1, PCIE_PWR_RESET);
+ exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
+ exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_CMN_REG);
+ exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSVREG_RESET);
+ exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSV_RESET);
+}
+
+static void exynos_pcie_power_on_phy(struct pcie_port *pp)
+{
+ u32 val;
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER);
+ val &= ~PCIE_PHY_COMMON_PD_CMN;
+ exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER);
+
+ val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER);
+ val &= ~PCIE_PHY_TRSV0_PD_TSV;
+ exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER);
+
+ val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER);
+ val &= ~PCIE_PHY_TRSV1_PD_TSV;
+ exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER);
+
+ val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER);
+ val &= ~PCIE_PHY_TRSV2_PD_TSV;
+ exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER);
+
+ val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER);
+ val &= ~PCIE_PHY_TRSV3_PD_TSV;
+ exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER);
+}
+
+static void exynos_pcie_power_off_phy(struct pcie_port *pp)
+{
+ u32 val;
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER);
+ val |= PCIE_PHY_COMMON_PD_CMN;
+ exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER);
+
+ val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER);
+ val |= PCIE_PHY_TRSV0_PD_TSV;
+ exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER);
+
+ val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER);
+ val |= PCIE_PHY_TRSV1_PD_TSV;
+ exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER);
+
+ val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER);
+ val |= PCIE_PHY_TRSV2_PD_TSV;
+ exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER);
+
+ val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER);
+ val |= PCIE_PHY_TRSV3_PD_TSV;
+ exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER);
+}
+
+static void exynos_pcie_init_phy(struct pcie_port *pp)
+{
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ /* DCC feedback control off */
+ exynos_phy_writel(exynos_pcie, 0x29, PCIE_PHY_DCC_FEEDBACK);
+
+ /* set TX/RX impedance */
+ exynos_phy_writel(exynos_pcie, 0xd5, PCIE_PHY_IMPEDANCE);
+
+ /* set 50Mhz PHY clock */
+ exynos_phy_writel(exynos_pcie, 0x14, PCIE_PHY_PLL_DIV_0);
+ exynos_phy_writel(exynos_pcie, 0x12, PCIE_PHY_PLL_DIV_1);
+
+ /* set TX Differential output for lane 0 */
+ exynos_phy_writel(exynos_pcie, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
+
+ /* set TX Pre-emphasis Level Control for lane 0 to minimum */
+ exynos_phy_writel(exynos_pcie, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
+
+ /* set RX clock and data recovery bandwidth */
+ exynos_phy_writel(exynos_pcie, 0xe7, PCIE_PHY_PLL_BIAS);
+ exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV0_RXCDR);
+ exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV1_RXCDR);
+ exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV2_RXCDR);
+ exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV3_RXCDR);
+
+ /* change TX Pre-emphasis Level Control for lanes */
+ exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
+ exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
+ exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
+ exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
+
+ /* set LVCC */
+ exynos_phy_writel(exynos_pcie, 0x20, PCIE_PHY_TRSV0_LVCC);
+ exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV1_LVCC);
+ exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV2_LVCC);
+ exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV3_LVCC);
+}
+
+static void exynos_pcie_assert_reset(struct pcie_port *pp)
+{
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ if (exynos_pcie->reset_gpio >= 0)
+ devm_gpio_request_one(pp->dev, exynos_pcie->reset_gpio,
+ GPIOF_OUT_INIT_HIGH, "RESET");
+ return;
+}
+
+static int exynos_pcie_establish_link(struct pcie_port *pp)
+{
+ u32 val;
+ int count = 0;
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ if (dw_pcie_link_up(pp)) {
+ dev_err(pp->dev, "Link already up\n");
+ return 0;
+ }
+
+ /* assert reset signals */
+ exynos_pcie_assert_core_reset(pp);
+ exynos_pcie_assert_phy_reset(pp);
+
+ /* de-assert phy reset */
+ exynos_pcie_deassert_phy_reset(pp);
+
+ /* power on phy */
+ exynos_pcie_power_on_phy(pp);
+
+ /* initialize phy */
+ exynos_pcie_init_phy(pp);
+
+ /* pulse for common reset */
+ exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_COMMON_RESET);
+ udelay(500);
+ exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
+
+ /* de-assert core reset */
+ exynos_pcie_deassert_core_reset(pp);
+
+ /* setup root complex */
+ dw_pcie_setup_rc(pp);
+
+ /* assert reset signal */
+ exynos_pcie_assert_reset(pp);
+
+ /* assert LTSSM enable */
+ exynos_elb_writel(exynos_pcie, PCIE_ELBI_LTSSM_ENABLE,
+ PCIE_APP_LTSSM_ENABLE);
+
+ /* check if the link is up or not */
+ while (!dw_pcie_link_up(pp)) {
+ mdelay(100);
+ count++;
+ if (count == 10) {
+ while (exynos_phy_readl(exynos_pcie,
+ PCIE_PHY_PLL_LOCKED) == 0) {
+ val = exynos_blk_readl(exynos_pcie,
+ PCIE_PHY_PLL_LOCKED);
+ dev_info(pp->dev, "PLL Locked: 0x%x\n", val);
+ }
+ /* power off phy */
+ exynos_pcie_power_off_phy(pp);
+
+ dev_err(pp->dev, "PCIe Link Fail\n");
+ return -EINVAL;
+ }
+ }
+
+ dev_info(pp->dev, "Link up\n");
+
+ return 0;
+}
+
+static void exynos_pcie_clear_irq_pulse(struct pcie_port *pp)
+{
+ u32 val;
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_PULSE);
+ exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_PULSE);
+ return;
+}
+
+static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp)
+{
+ u32 val;
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ /* enable INTX interrupt */
+ val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
+ IRQ_INTC_ASSERT | IRQ_INTD_ASSERT,
+ exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_PULSE);
+ return;
+}
+
+static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
+{
+ struct pcie_port *pp = arg;
+
+ exynos_pcie_clear_irq_pulse(pp);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg)
+{
+ struct pcie_port *pp = arg;
+
+ return dw_handle_msi_irq(pp);
+}
+
+static void exynos_pcie_msi_init(struct pcie_port *pp)
+{
+ u32 val;
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+ dw_pcie_msi_init(pp);
+
+ /* enable MSI interrupt */
+ val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_EN_LEVEL);
+ val |= IRQ_MSI_ENABLE;
+ exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_LEVEL);
+ return;
+}
+
+static void exynos_pcie_enable_interrupts(struct pcie_port *pp)
+{
+ exynos_pcie_enable_irq_pulse(pp);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ exynos_pcie_msi_init(pp);
+
+ return;
+}
+
+static inline void exynos_pcie_readl_rc(struct pcie_port *pp,
+ void __iomem *dbi_base, u32 *val)
+{
+ exynos_pcie_sideband_dbi_r_mode(pp, true);
+ *val = readl(dbi_base);
+ exynos_pcie_sideband_dbi_r_mode(pp, false);
+ return;
+}
+
+static inline void exynos_pcie_writel_rc(struct pcie_port *pp,
+ u32 val, void __iomem *dbi_base)
+{
+ exynos_pcie_sideband_dbi_w_mode(pp, true);
+ writel(val, dbi_base);
+ exynos_pcie_sideband_dbi_w_mode(pp, false);
+ return;
+}
+
+static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
+ u32 *val)
+{
+ int ret;
+
+ exynos_pcie_sideband_dbi_r_mode(pp, true);
+ ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, size, val);
+ exynos_pcie_sideband_dbi_r_mode(pp, false);
+ return ret;
+}
+
+static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
+ u32 val)
+{
+ int ret;
+
+ exynos_pcie_sideband_dbi_w_mode(pp, true);
+ ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3),
+ where, size, val);
+ exynos_pcie_sideband_dbi_w_mode(pp, false);
+ return ret;
+}
+
+static int exynos_pcie_link_up(struct pcie_port *pp)
+{
+ struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+ u32 val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_RDLH_LINKUP);
+
+ if (val == PCIE_ELBI_LTSSM_ENABLE)
+ return 1;
+
+ return 0;
+}
+
+static void exynos_pcie_host_init(struct pcie_port *pp)
+{
+ exynos_pcie_establish_link(pp);
+ exynos_pcie_enable_interrupts(pp);
+}
+
+static struct pcie_host_ops exynos_pcie_host_ops = {
+ .readl_rc = exynos_pcie_readl_rc,
+ .writel_rc = exynos_pcie_writel_rc,
+ .rd_own_conf = exynos_pcie_rd_own_conf,
+ .wr_own_conf = exynos_pcie_wr_own_conf,
+ .link_up = exynos_pcie_link_up,
+ .host_init = exynos_pcie_host_init,
+};
+
+static int __init add_pcie_port(struct pcie_port *pp,
+ struct platform_device *pdev)
+{
+ int ret;
+
+ pp->irq = platform_get_irq(pdev, 1);
+ if (!pp->irq) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return -ENODEV;
+ }
+ ret = devm_request_irq(&pdev->dev, pp->irq, exynos_pcie_irq_handler,
+ IRQF_SHARED, "exynos-pcie", pp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ return ret;
+ }
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ pp->msi_irq = platform_get_irq(pdev, 0);
+ if (!pp->msi_irq) {
+ dev_err(&pdev->dev, "failed to get msi irq\n");
+ return -ENODEV;
+ }
+
+ ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+ exynos_pcie_msi_irq_handler,
+ IRQF_SHARED, "exynos-pcie", pp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request msi irq\n");
+ return ret;
+ }
+ }
+
+ pp->root_bus_nr = -1;
+ pp->ops = &exynos_pcie_host_ops;
+
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init exynos_pcie_probe(struct platform_device *pdev)
+{
+ struct exynos_pcie *exynos_pcie;
+ struct pcie_port *pp;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *elbi_base;
+ struct resource *phy_base;
+ struct resource *block_base;
+ int ret;
+
+ exynos_pcie = devm_kzalloc(&pdev->dev, sizeof(*exynos_pcie),
+ GFP_KERNEL);
+ if (!exynos_pcie)
+ return -ENOMEM;
+
+ pp = &exynos_pcie->pp;
+
+ pp->dev = &pdev->dev;
+
+ exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+
+ exynos_pcie->clk = devm_clk_get(&pdev->dev, "pcie");
+ if (IS_ERR(exynos_pcie->clk)) {
+ dev_err(&pdev->dev, "Failed to get pcie rc clock\n");
+ return PTR_ERR(exynos_pcie->clk);
+ }
+ ret = clk_prepare_enable(exynos_pcie->clk);
+ if (ret)
+ return ret;
+
+ exynos_pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus");
+ if (IS_ERR(exynos_pcie->bus_clk)) {
+ dev_err(&pdev->dev, "Failed to get pcie bus clock\n");
+ ret = PTR_ERR(exynos_pcie->bus_clk);
+ goto fail_clk;
+ }
+ ret = clk_prepare_enable(exynos_pcie->bus_clk);
+ if (ret)
+ goto fail_clk;
+
+ elbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ exynos_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base);
+ if (IS_ERR(exynos_pcie->elbi_base)) {
+ ret = PTR_ERR(exynos_pcie->elbi_base);
+ goto fail_bus_clk;
+ }
+
+ phy_base = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ exynos_pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy_base);
+ if (IS_ERR(exynos_pcie->phy_base)) {
+ ret = PTR_ERR(exynos_pcie->phy_base);
+ goto fail_bus_clk;
+ }
+
+ block_base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ exynos_pcie->block_base = devm_ioremap_resource(&pdev->dev, block_base);
+ if (IS_ERR(exynos_pcie->block_base)) {
+ ret = PTR_ERR(exynos_pcie->block_base);
+ goto fail_bus_clk;
+ }
+
+ ret = add_pcie_port(pp, pdev);
+ if (ret < 0)
+ goto fail_bus_clk;
+
+ platform_set_drvdata(pdev, exynos_pcie);
+ return 0;
+
+fail_bus_clk:
+ clk_disable_unprepare(exynos_pcie->bus_clk);
+fail_clk:
+ clk_disable_unprepare(exynos_pcie->clk);
+ return ret;
+}
+
+static int __exit exynos_pcie_remove(struct platform_device *pdev)
+{
+ struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(exynos_pcie->bus_clk);
+ clk_disable_unprepare(exynos_pcie->clk);
+
+ return 0;
+}
+
+static const struct of_device_id exynos_pcie_of_match[] = {
+ { .compatible = "samsung,exynos5440-pcie", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_pcie_of_match);
+
+static struct platform_driver exynos_pcie_driver = {
+ .remove = __exit_p(exynos_pcie_remove),
+ .driver = {
+ .name = "exynos-pcie",
+ .owner = THIS_MODULE,
+ .of_match_table = exynos_pcie_of_match,
+ },
+};
+
+/* Exynos PCIe driver does not allow module unload */
+
+static int __init pcie_init(void)
+{
+ return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe);
+}
+subsys_initcall(pcie_init);
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("Samsung PCIe host controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c
new file mode 100644
index 00000000000..44fe6aa6a43
--- /dev/null
+++ b/drivers/pci/host/pci-host-generic.c
@@ -0,0 +1,388 @@
+/*
+ * Simple, generic PCI host controller driver targetting firmware-initialised
+ * systems and virtual machines (e.g. the PCI emulation provided by kvmtool).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2014 ARM Limited
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/platform_device.h>
+
+struct gen_pci_cfg_bus_ops {
+ u32 bus_shift;
+ void __iomem *(*map_bus)(struct pci_bus *, unsigned int, int);
+};
+
+struct gen_pci_cfg_windows {
+ struct resource res;
+ struct resource bus_range;
+ void __iomem **win;
+
+ const struct gen_pci_cfg_bus_ops *ops;
+};
+
+struct gen_pci {
+ struct pci_host_bridge host;
+ struct gen_pci_cfg_windows cfg;
+ struct list_head resources;
+};
+
+static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus,
+ unsigned int devfn,
+ int where)
+{
+ struct pci_sys_data *sys = bus->sysdata;
+ struct gen_pci *pci = sys->private_data;
+ resource_size_t idx = bus->number - pci->cfg.bus_range.start;
+
+ return pci->cfg.win[idx] + ((devfn << 8) | where);
+}
+
+static struct gen_pci_cfg_bus_ops gen_pci_cfg_cam_bus_ops = {
+ .bus_shift = 16,
+ .map_bus = gen_pci_map_cfg_bus_cam,
+};
+
+static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
+ unsigned int devfn,
+ int where)
+{
+ struct pci_sys_data *sys = bus->sysdata;
+ struct gen_pci *pci = sys->private_data;
+ resource_size_t idx = bus->number - pci->cfg.bus_range.start;
+
+ return pci->cfg.win[idx] + ((devfn << 12) | where);
+}
+
+static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = {
+ .bus_shift = 20,
+ .map_bus = gen_pci_map_cfg_bus_ecam,
+};
+
+static int gen_pci_config_read(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ void __iomem *addr;
+ struct pci_sys_data *sys = bus->sysdata;
+ struct gen_pci *pci = sys->private_data;
+
+ addr = pci->cfg.ops->map_bus(bus, devfn, where);
+
+ switch (size) {
+ case 1:
+ *val = readb(addr);
+ break;
+ case 2:
+ *val = readw(addr);
+ break;
+ default:
+ *val = readl(addr);
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int gen_pci_config_write(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ void __iomem *addr;
+ struct pci_sys_data *sys = bus->sysdata;
+ struct gen_pci *pci = sys->private_data;
+
+ addr = pci->cfg.ops->map_bus(bus, devfn, where);
+
+ switch (size) {
+ case 1:
+ writeb(val, addr);
+ break;
+ case 2:
+ writew(val, addr);
+ break;
+ default:
+ writel(val, addr);
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops gen_pci_ops = {
+ .read = gen_pci_config_read,
+ .write = gen_pci_config_write,
+};
+
+static const struct of_device_id gen_pci_of_match[] = {
+ { .compatible = "pci-host-cam-generic",
+ .data = &gen_pci_cfg_cam_bus_ops },
+
+ { .compatible = "pci-host-ecam-generic",
+ .data = &gen_pci_cfg_ecam_bus_ops },
+
+ { },
+};
+MODULE_DEVICE_TABLE(of, gen_pci_of_match);
+
+static int gen_pci_calc_io_offset(struct device *dev,
+ struct of_pci_range *range,
+ struct resource *res,
+ resource_size_t *offset)
+{
+ static atomic_t wins = ATOMIC_INIT(0);
+ int err, idx, max_win;
+ unsigned int window;
+
+ if (!PAGE_ALIGNED(range->cpu_addr))
+ return -EINVAL;
+
+ max_win = (IO_SPACE_LIMIT + 1) / SZ_64K;
+ idx = atomic_inc_return(&wins);
+ if (idx > max_win)
+ return -ENOSPC;
+
+ window = (idx - 1) * SZ_64K;
+ err = pci_ioremap_io(window, range->cpu_addr);
+ if (err)
+ return err;
+
+ of_pci_range_to_resource(range, dev->of_node, res);
+ res->start = window;
+ res->end = res->start + range->size - 1;
+ *offset = window - range->pci_addr;
+ return 0;
+}
+
+static int gen_pci_calc_mem_offset(struct device *dev,
+ struct of_pci_range *range,
+ struct resource *res,
+ resource_size_t *offset)
+{
+ of_pci_range_to_resource(range, dev->of_node, res);
+ *offset = range->cpu_addr - range->pci_addr;
+ return 0;
+}
+
+static void gen_pci_release_of_pci_ranges(struct gen_pci *pci)
+{
+ struct pci_host_bridge_window *win;
+
+ list_for_each_entry(win, &pci->resources, list)
+ release_resource(win->res);
+
+ pci_free_resource_list(&pci->resources);
+}
+
+static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
+{
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ int err, res_valid = 0;
+ struct device *dev = pci->host.dev.parent;
+ struct device_node *np = dev->of_node;
+
+ if (of_pci_range_parser_init(&parser, np)) {
+ dev_err(dev, "missing \"ranges\" property\n");
+ return -EINVAL;
+ }
+
+ for_each_of_pci_range(&parser, &range) {
+ struct resource *parent, *res;
+ resource_size_t offset;
+ u32 restype = range.flags & IORESOURCE_TYPE_BITS;
+
+ res = devm_kmalloc(dev, sizeof(*res), GFP_KERNEL);
+ if (!res) {
+ err = -ENOMEM;
+ goto out_release_res;
+ }
+
+ switch (restype) {
+ case IORESOURCE_IO:
+ parent = &ioport_resource;
+ err = gen_pci_calc_io_offset(dev, &range, res, &offset);
+ break;
+ case IORESOURCE_MEM:
+ parent = &iomem_resource;
+ err = gen_pci_calc_mem_offset(dev, &range, res, &offset);
+ res_valid |= !(res->flags & IORESOURCE_PREFETCH || err);
+ break;
+ default:
+ err = -EINVAL;
+ continue;
+ }
+
+ if (err) {
+ dev_warn(dev,
+ "error %d: failed to add resource [type 0x%x, %lld bytes]\n",
+ err, restype, range.size);
+ continue;
+ }
+
+ err = request_resource(parent, res);
+ if (err)
+ goto out_release_res;
+
+ pci_add_resource_offset(&pci->resources, res, offset);
+ }
+
+ if (!res_valid) {
+ dev_err(dev, "non-prefetchable memory resource required\n");
+ err = -EINVAL;
+ goto out_release_res;
+ }
+
+ return 0;
+
+out_release_res:
+ gen_pci_release_of_pci_ranges(pci);
+ return err;
+}
+
+static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
+{
+ int err;
+ u8 bus_max;
+ resource_size_t busn;
+ struct resource *bus_range;
+ struct device *dev = pci->host.dev.parent;
+ struct device_node *np = dev->of_node;
+
+ if (of_pci_parse_bus_range(np, &pci->cfg.bus_range))
+ pci->cfg.bus_range = (struct resource) {
+ .name = np->name,
+ .start = 0,
+ .end = 0xff,
+ .flags = IORESOURCE_BUS,
+ };
+
+ err = of_address_to_resource(np, 0, &pci->cfg.res);
+ if (err) {
+ dev_err(dev, "missing \"reg\" property\n");
+ return err;
+ }
+
+ pci->cfg.win = devm_kcalloc(dev, resource_size(&pci->cfg.bus_range),
+ sizeof(*pci->cfg.win), GFP_KERNEL);
+ if (!pci->cfg.win)
+ return -ENOMEM;
+
+ /* Limit the bus-range to fit within reg */
+ bus_max = pci->cfg.bus_range.start +
+ (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
+ pci->cfg.bus_range.end = min_t(resource_size_t, pci->cfg.bus_range.end,
+ bus_max);
+
+ /* Map our Configuration Space windows */
+ if (!devm_request_mem_region(dev, pci->cfg.res.start,
+ resource_size(&pci->cfg.res),
+ "Configuration Space"))
+ return -ENOMEM;
+
+ bus_range = &pci->cfg.bus_range;
+ for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
+ u32 idx = busn - bus_range->start;
+ u32 sz = 1 << pci->cfg.ops->bus_shift;
+
+ pci->cfg.win[idx] = devm_ioremap(dev,
+ pci->cfg.res.start + busn * sz,
+ sz);
+ if (!pci->cfg.win[idx])
+ return -ENOMEM;
+ }
+
+ /* Register bus resource */
+ pci_add_resource(&pci->resources, bus_range);
+ return 0;
+}
+
+static int gen_pci_setup(int nr, struct pci_sys_data *sys)
+{
+ struct gen_pci *pci = sys->private_data;
+ list_splice_init(&pci->resources, &sys->resources);
+ return 1;
+}
+
+static int gen_pci_probe(struct platform_device *pdev)
+{
+ int err;
+ const char *type;
+ const struct of_device_id *of_id;
+ const int *prop;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ struct hw_pci hw = {
+ .nr_controllers = 1,
+ .private_data = (void **)&pci,
+ .setup = gen_pci_setup,
+ .map_irq = of_irq_parse_and_map_pci,
+ .ops = &gen_pci_ops,
+ };
+
+ if (!pci)
+ return -ENOMEM;
+
+ type = of_get_property(np, "device_type", NULL);
+ if (!type || strcmp(type, "pci")) {
+ dev_err(dev, "invalid \"device_type\" %s\n", type);
+ return -EINVAL;
+ }
+
+ prop = of_get_property(of_chosen, "linux,pci-probe-only", NULL);
+ if (prop) {
+ if (*prop)
+ pci_add_flags(PCI_PROBE_ONLY);
+ else
+ pci_clear_flags(PCI_PROBE_ONLY);
+ }
+
+ of_id = of_match_node(gen_pci_of_match, np);
+ pci->cfg.ops = of_id->data;
+ pci->host.dev.parent = dev;
+ INIT_LIST_HEAD(&pci->host.windows);
+ INIT_LIST_HEAD(&pci->resources);
+
+ /* Parse our PCI ranges and request their resources */
+ err = gen_pci_parse_request_of_pci_ranges(pci);
+ if (err)
+ return err;
+
+ /* Parse and map our Configuration Space windows */
+ err = gen_pci_parse_map_cfg_windows(pci);
+ if (err) {
+ gen_pci_release_of_pci_ranges(pci);
+ return err;
+ }
+
+ pci_common_init_dev(dev, &hw);
+ return 0;
+}
+
+static struct platform_driver gen_pci_driver = {
+ .driver = {
+ .name = "pci-host-generic",
+ .owner = THIS_MODULE,
+ .of_match_table = gen_pci_of_match,
+ },
+ .probe = gen_pci_probe,
+};
+module_platform_driver(gen_pci_driver);
+
+MODULE_DESCRIPTION("Generic PCI host driver");
+MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
new file mode 100644
index 00000000000..a568efaa331
--- /dev/null
+++ b/drivers/pci/host/pci-imx6.c
@@ -0,0 +1,616 @@
+/*
+ * PCIe host controller driver for Freescale i.MX6 SoCs
+ *
+ * Copyright (C) 2013 Kosagi
+ * http://www.kosagi.com
+ *
+ * Author: Sean Cross <xobs@kosagi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+
+#include "pcie-designware.h"
+
+#define to_imx6_pcie(x) container_of(x, struct imx6_pcie, pp)
+
+struct imx6_pcie {
+ int reset_gpio;
+ struct clk *pcie_bus;
+ struct clk *pcie_phy;
+ struct clk *pcie;
+ struct pcie_port pp;
+ struct regmap *iomuxc_gpr;
+ void __iomem *mem_base;
+};
+
+/* PCIe Root Complex registers (memory-mapped) */
+#define PCIE_RC_LCR 0x7c
+#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1
+#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2 0x2
+#define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK 0xf
+
+/* PCIe Port Logic registers (memory-mapped) */
+#define PL_OFFSET 0x700
+#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28)
+#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
+#define PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING (1 << 29)
+#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP (1 << 4)
+
+#define PCIE_PHY_CTRL (PL_OFFSET + 0x114)
+#define PCIE_PHY_CTRL_DATA_LOC 0
+#define PCIE_PHY_CTRL_CAP_ADR_LOC 16
+#define PCIE_PHY_CTRL_CAP_DAT_LOC 17
+#define PCIE_PHY_CTRL_WR_LOC 18
+#define PCIE_PHY_CTRL_RD_LOC 19
+
+#define PCIE_PHY_STAT (PL_OFFSET + 0x110)
+#define PCIE_PHY_STAT_ACK_LOC 16
+
+#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
+#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
+
+/* PHY registers (not memory-mapped) */
+#define PCIE_PHY_RX_ASIC_OUT 0x100D
+
+#define PHY_RX_OVRD_IN_LO 0x1005
+#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
+#define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
+
+static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val)
+{
+ u32 val;
+ u32 max_iterations = 10;
+ u32 wait_counter = 0;
+
+ do {
+ val = readl(dbi_base + PCIE_PHY_STAT);
+ val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1;
+ wait_counter++;
+
+ if (val == exp_val)
+ return 0;
+
+ udelay(1);
+ } while (wait_counter < max_iterations);
+
+ return -ETIMEDOUT;
+}
+
+static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr)
+{
+ u32 val;
+ int ret;
+
+ val = addr << PCIE_PHY_CTRL_DATA_LOC;
+ writel(val, dbi_base + PCIE_PHY_CTRL);
+
+ val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC);
+ writel(val, dbi_base + PCIE_PHY_CTRL);
+
+ ret = pcie_phy_poll_ack(dbi_base, 1);
+ if (ret)
+ return ret;
+
+ val = addr << PCIE_PHY_CTRL_DATA_LOC;
+ writel(val, dbi_base + PCIE_PHY_CTRL);
+
+ ret = pcie_phy_poll_ack(dbi_base, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */
+static int pcie_phy_read(void __iomem *dbi_base, int addr , int *data)
+{
+ u32 val, phy_ctl;
+ int ret;
+
+ ret = pcie_phy_wait_ack(dbi_base, addr);
+ if (ret)
+ return ret;
+
+ /* assert Read signal */
+ phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC;
+ writel(phy_ctl, dbi_base + PCIE_PHY_CTRL);
+
+ ret = pcie_phy_poll_ack(dbi_base, 1);
+ if (ret)
+ return ret;
+
+ val = readl(dbi_base + PCIE_PHY_STAT);
+ *data = val & 0xffff;
+
+ /* deassert Read signal */
+ writel(0x00, dbi_base + PCIE_PHY_CTRL);
+
+ ret = pcie_phy_poll_ack(dbi_base, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int pcie_phy_write(void __iomem *dbi_base, int addr, int data)
+{
+ u32 var;
+ int ret;
+
+ /* write addr */
+ /* cap addr */
+ ret = pcie_phy_wait_ack(dbi_base, addr);
+ if (ret)
+ return ret;
+
+ var = data << PCIE_PHY_CTRL_DATA_LOC;
+ writel(var, dbi_base + PCIE_PHY_CTRL);
+
+ /* capture data */
+ var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC);
+ writel(var, dbi_base + PCIE_PHY_CTRL);
+
+ ret = pcie_phy_poll_ack(dbi_base, 1);
+ if (ret)
+ return ret;
+
+ /* deassert cap data */
+ var = data << PCIE_PHY_CTRL_DATA_LOC;
+ writel(var, dbi_base + PCIE_PHY_CTRL);
+
+ /* wait for ack de-assertion */
+ ret = pcie_phy_poll_ack(dbi_base, 0);
+ if (ret)
+ return ret;
+
+ /* assert wr signal */
+ var = 0x1 << PCIE_PHY_CTRL_WR_LOC;
+ writel(var, dbi_base + PCIE_PHY_CTRL);
+
+ /* wait for ack */
+ ret = pcie_phy_poll_ack(dbi_base, 1);
+ if (ret)
+ return ret;
+
+ /* deassert wr signal */
+ var = data << PCIE_PHY_CTRL_DATA_LOC;
+ writel(var, dbi_base + PCIE_PHY_CTRL);
+
+ /* wait for ack de-assertion */
+ ret = pcie_phy_poll_ack(dbi_base, 0);
+ if (ret)
+ return ret;
+
+ writel(0x0, dbi_base + PCIE_PHY_CTRL);
+
+ return 0;
+}
+
+/* Added for PCI abort handling */
+static int imx6q_pcie_abort_handler(unsigned long addr,
+ unsigned int fsr, struct pt_regs *regs)
+{
+ return 0;
+}
+
+static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
+{
+ struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
+
+ return 0;
+}
+
+static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
+{
+ struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+ int ret;
+
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
+
+ ret = clk_prepare_enable(imx6_pcie->pcie_phy);
+ if (ret) {
+ dev_err(pp->dev, "unable to enable pcie_phy clock\n");
+ goto err_pcie_phy;
+ }
+
+ ret = clk_prepare_enable(imx6_pcie->pcie_bus);
+ if (ret) {
+ dev_err(pp->dev, "unable to enable pcie_bus clock\n");
+ goto err_pcie_bus;
+ }
+
+ ret = clk_prepare_enable(imx6_pcie->pcie);
+ if (ret) {
+ dev_err(pp->dev, "unable to enable pcie clock\n");
+ goto err_pcie;
+ }
+
+ /* allow the clocks to stabilize */
+ usleep_range(200, 500);
+
+ /* Some boards don't have PCIe reset GPIO. */
+ if (gpio_is_valid(imx6_pcie->reset_gpio)) {
+ gpio_set_value(imx6_pcie->reset_gpio, 0);
+ msleep(100);
+ gpio_set_value(imx6_pcie->reset_gpio, 1);
+ }
+ return 0;
+
+err_pcie:
+ clk_disable_unprepare(imx6_pcie->pcie_bus);
+err_pcie_bus:
+ clk_disable_unprepare(imx6_pcie->pcie_phy);
+err_pcie_phy:
+ return ret;
+
+}
+
+static void imx6_pcie_init_phy(struct pcie_port *pp)
+{
+ struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+ IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
+
+ /* configure constant input signal to the pcie ctrl and phy */
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+ IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+ IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
+
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+ IMX6Q_GPR8_TX_DEEMPH_GEN1, 0 << 0);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+ IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, 0 << 6);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+ IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, 20 << 12);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+ IMX6Q_GPR8_TX_SWING_FULL, 127 << 18);
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+ IMX6Q_GPR8_TX_SWING_LOW, 127 << 25);
+}
+
+static int imx6_pcie_wait_for_link(struct pcie_port *pp)
+{
+ int count = 200;
+
+ while (!dw_pcie_link_up(pp)) {
+ usleep_range(100, 1000);
+ if (--count)
+ continue;
+
+ dev_err(pp->dev, "phy link never came up\n");
+ dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
+ readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
+ readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg)
+{
+ struct pcie_port *pp = arg;
+
+ return dw_handle_msi_irq(pp);
+}
+
+static int imx6_pcie_start_link(struct pcie_port *pp)
+{
+ struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+ uint32_t tmp;
+ int ret, count;
+
+ /*
+ * Force Gen1 operation when starting the link. In case the link is
+ * started in Gen2 mode, there is a possibility the devices on the
+ * bus will not be detected at all. This happens with PCIe switches.
+ */
+ tmp = readl(pp->dbi_base + PCIE_RC_LCR);
+ tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
+ tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1;
+ writel(tmp, pp->dbi_base + PCIE_RC_LCR);
+
+ /* Start LTSSM. */
+ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+ IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
+
+ ret = imx6_pcie_wait_for_link(pp);
+ if (ret)
+ return ret;
+
+ /* Allow Gen2 mode after the link is up. */
+ tmp = readl(pp->dbi_base + PCIE_RC_LCR);
+ tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
+ tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
+ writel(tmp, pp->dbi_base + PCIE_RC_LCR);
+
+ /*
+ * Start Directed Speed Change so the best possible speed both link
+ * partners support can be negotiated.
+ */
+ tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+ tmp |= PORT_LOGIC_SPEED_CHANGE;
+ writel(tmp, pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+
+ count = 200;
+ while (count--) {
+ tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+ /* Test if the speed change finished. */
+ if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
+ break;
+ usleep_range(100, 1000);
+ }
+
+ /* Make sure link training is finished as well! */
+ if (count)
+ ret = imx6_pcie_wait_for_link(pp);
+ else
+ ret = -EINVAL;
+
+ if (ret) {
+ dev_err(pp->dev, "Failed to bring link up!\n");
+ } else {
+ tmp = readl(pp->dbi_base + 0x80);
+ dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf);
+ }
+
+ return ret;
+}
+
+static void imx6_pcie_host_init(struct pcie_port *pp)
+{
+ imx6_pcie_assert_core_reset(pp);
+
+ imx6_pcie_init_phy(pp);
+
+ imx6_pcie_deassert_core_reset(pp);
+
+ dw_pcie_setup_rc(pp);
+
+ imx6_pcie_start_link(pp);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ dw_pcie_msi_init(pp);
+}
+
+static void imx6_pcie_reset_phy(struct pcie_port *pp)
+{
+ uint32_t temp;
+
+ pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
+ temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
+ PHY_RX_OVRD_IN_LO_RX_PLL_EN);
+ pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, temp);
+
+ usleep_range(2000, 3000);
+
+ pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
+ temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN |
+ PHY_RX_OVRD_IN_LO_RX_PLL_EN);
+ pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, temp);
+}
+
+static int imx6_pcie_link_up(struct pcie_port *pp)
+{
+ u32 rc, debug_r0, rx_valid;
+ int count = 5;
+
+ /*
+ * Test if the PHY reports that the link is up and also that the LTSSM
+ * training finished. There are three possible states of the link when
+ * this code is called:
+ * 1) The link is DOWN (unlikely)
+ * The link didn't come up yet for some reason. This usually means
+ * we have a real problem somewhere. Reset the PHY and exit. This
+ * state calls for inspection of the DEBUG registers.
+ * 2) The link is UP, but still in LTSSM training
+ * Wait for the training to finish, which should take a very short
+ * time. If the training does not finish, we have a problem and we
+ * need to inspect the DEBUG registers. If the training does finish,
+ * the link is up and operating correctly.
+ * 3) The link is UP and no longer in LTSSM training
+ * The link is up and operating correctly.
+ */
+ while (1) {
+ rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
+ if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP))
+ break;
+ if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING))
+ return 1;
+ if (!count--)
+ break;
+ dev_dbg(pp->dev, "Link is up, but still in training\n");
+ /*
+ * Wait a little bit, then re-check if the link finished
+ * the training.
+ */
+ usleep_range(1000, 2000);
+ }
+ /*
+ * From L0, initiate MAC entry to gen2 if EP/RC supports gen2.
+ * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2).
+ * If (MAC/LTSSM.state == Recovery.RcvrLock)
+ * && (PHY/rx_valid==0) then pulse PHY/rx_reset. Transition
+ * to gen2 is stuck
+ */
+ pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid);
+ debug_r0 = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0);
+
+ if (rx_valid & 0x01)
+ return 0;
+
+ if ((debug_r0 & 0x3f) != 0x0d)
+ return 0;
+
+ dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n");
+ dev_dbg(pp->dev, "debug_r0=%08x debug_r1=%08x\n", debug_r0, rc);
+
+ imx6_pcie_reset_phy(pp);
+
+ return 0;
+}
+
+static struct pcie_host_ops imx6_pcie_host_ops = {
+ .link_up = imx6_pcie_link_up,
+ .host_init = imx6_pcie_host_init,
+};
+
+static int __init imx6_add_pcie_port(struct pcie_port *pp,
+ struct platform_device *pdev)
+{
+ int ret;
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ pp->msi_irq = platform_get_irq_byname(pdev, "msi");
+ if (pp->msi_irq <= 0) {
+ dev_err(&pdev->dev, "failed to get MSI irq\n");
+ return -ENODEV;
+ }
+
+ ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+ imx6_pcie_msi_handler,
+ IRQF_SHARED, "mx6-pcie-msi", pp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request MSI irq\n");
+ return -ENODEV;
+ }
+ }
+
+ pp->root_bus_nr = -1;
+ pp->ops = &imx6_pcie_host_ops;
+
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init imx6_pcie_probe(struct platform_device *pdev)
+{
+ struct imx6_pcie *imx6_pcie;
+ struct pcie_port *pp;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *dbi_base;
+ int ret;
+
+ imx6_pcie = devm_kzalloc(&pdev->dev, sizeof(*imx6_pcie), GFP_KERNEL);
+ if (!imx6_pcie)
+ return -ENOMEM;
+
+ pp = &imx6_pcie->pp;
+ pp->dev = &pdev->dev;
+
+ /* Added for PCI abort handling */
+ hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
+ "imprecise external abort");
+
+ dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base);
+ if (IS_ERR(pp->dbi_base))
+ return PTR_ERR(pp->dbi_base);
+
+ /* Fetch GPIOs */
+ imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+ if (gpio_is_valid(imx6_pcie->reset_gpio)) {
+ ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio,
+ GPIOF_OUT_INIT_LOW, "PCIe reset");
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get reset gpio\n");
+ return ret;
+ }
+ }
+
+ /* Fetch clocks */
+ imx6_pcie->pcie_phy = devm_clk_get(&pdev->dev, "pcie_phy");
+ if (IS_ERR(imx6_pcie->pcie_phy)) {
+ dev_err(&pdev->dev,
+ "pcie_phy clock source missing or invalid\n");
+ return PTR_ERR(imx6_pcie->pcie_phy);
+ }
+
+ imx6_pcie->pcie_bus = devm_clk_get(&pdev->dev, "pcie_bus");
+ if (IS_ERR(imx6_pcie->pcie_bus)) {
+ dev_err(&pdev->dev,
+ "pcie_bus clock source missing or invalid\n");
+ return PTR_ERR(imx6_pcie->pcie_bus);
+ }
+
+ imx6_pcie->pcie = devm_clk_get(&pdev->dev, "pcie");
+ if (IS_ERR(imx6_pcie->pcie)) {
+ dev_err(&pdev->dev,
+ "pcie clock source missing or invalid\n");
+ return PTR_ERR(imx6_pcie->pcie);
+ }
+
+ /* Grab GPR config register range */
+ imx6_pcie->iomuxc_gpr =
+ syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
+ if (IS_ERR(imx6_pcie->iomuxc_gpr)) {
+ dev_err(&pdev->dev, "unable to find iomuxc registers\n");
+ return PTR_ERR(imx6_pcie->iomuxc_gpr);
+ }
+
+ ret = imx6_add_pcie_port(pp, pdev);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, imx6_pcie);
+ return 0;
+}
+
+static const struct of_device_id imx6_pcie_of_match[] = {
+ { .compatible = "fsl,imx6q-pcie", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, imx6_pcie_of_match);
+
+static struct platform_driver imx6_pcie_driver = {
+ .driver = {
+ .name = "imx6q-pcie",
+ .owner = THIS_MODULE,
+ .of_match_table = imx6_pcie_of_match,
+ },
+};
+
+/* Freescale PCIe driver does not allow module unload */
+
+static int __init imx6_pcie_init(void)
+{
+ return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe);
+}
+fs_initcall(imx6_pcie_init);
+
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_DESCRIPTION("Freescale i.MX6 PCIe host controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c
new file mode 100644
index 00000000000..ce23e0f076b
--- /dev/null
+++ b/drivers/pci/host/pci-mvebu.c
@@ -0,0 +1,1097 @@
+/*
+ * PCIe driver for Marvell Armada 370 and Armada XP SoCs
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/mbus.h>
+#include <linux/msi.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+
+/*
+ * PCIe unit register offsets.
+ */
+#define PCIE_DEV_ID_OFF 0x0000
+#define PCIE_CMD_OFF 0x0004
+#define PCIE_DEV_REV_OFF 0x0008
+#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3))
+#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3))
+#define PCIE_HEADER_LOG_4_OFF 0x0128
+#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4))
+#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4))
+#define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4))
+#define PCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4))
+#define PCIE_WIN5_CTRL_OFF 0x1880
+#define PCIE_WIN5_BASE_OFF 0x1884
+#define PCIE_WIN5_REMAP_OFF 0x188c
+#define PCIE_CONF_ADDR_OFF 0x18f8
+#define PCIE_CONF_ADDR_EN 0x80000000
+#define PCIE_CONF_REG(r) ((((r) & 0xf00) << 16) | ((r) & 0xfc))
+#define PCIE_CONF_BUS(b) (((b) & 0xff) << 16)
+#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 11)
+#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 8)
+#define PCIE_CONF_ADDR(bus, devfn, where) \
+ (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \
+ PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where) | \
+ PCIE_CONF_ADDR_EN)
+#define PCIE_CONF_DATA_OFF 0x18fc
+#define PCIE_MASK_OFF 0x1910
+#define PCIE_MASK_ENABLE_INTS 0x0f000000
+#define PCIE_CTRL_OFF 0x1a00
+#define PCIE_CTRL_X1_MODE 0x0001
+#define PCIE_STAT_OFF 0x1a04
+#define PCIE_STAT_BUS 0xff00
+#define PCIE_STAT_DEV 0x1f0000
+#define PCIE_STAT_LINK_DOWN BIT(0)
+#define PCIE_DEBUG_CTRL 0x1a60
+#define PCIE_DEBUG_SOFT_RESET BIT(20)
+
+/* PCI configuration space of a PCI-to-PCI bridge */
+struct mvebu_sw_pci_bridge {
+ u16 vendor;
+ u16 device;
+ u16 command;
+ u16 class;
+ u8 interface;
+ u8 revision;
+ u8 bist;
+ u8 header_type;
+ u8 latency_timer;
+ u8 cache_line_size;
+ u32 bar[2];
+ u8 primary_bus;
+ u8 secondary_bus;
+ u8 subordinate_bus;
+ u8 secondary_latency_timer;
+ u8 iobase;
+ u8 iolimit;
+ u16 secondary_status;
+ u16 membase;
+ u16 memlimit;
+ u16 iobaseupper;
+ u16 iolimitupper;
+ u8 cappointer;
+ u8 reserved1;
+ u16 reserved2;
+ u32 romaddr;
+ u8 intline;
+ u8 intpin;
+ u16 bridgectrl;
+};
+
+struct mvebu_pcie_port;
+
+/* Structure representing all PCIe interfaces */
+struct mvebu_pcie {
+ struct platform_device *pdev;
+ struct mvebu_pcie_port *ports;
+ struct msi_chip *msi;
+ struct resource io;
+ char io_name[30];
+ struct resource realio;
+ char mem_name[30];
+ struct resource mem;
+ struct resource busn;
+ int nports;
+};
+
+/* Structure representing one PCIe interface */
+struct mvebu_pcie_port {
+ char *name;
+ void __iomem *base;
+ u32 port;
+ u32 lane;
+ int devfn;
+ unsigned int mem_target;
+ unsigned int mem_attr;
+ unsigned int io_target;
+ unsigned int io_attr;
+ struct clk *clk;
+ int reset_gpio;
+ int reset_active_low;
+ char *reset_name;
+ struct mvebu_sw_pci_bridge bridge;
+ struct device_node *dn;
+ struct mvebu_pcie *pcie;
+ phys_addr_t memwin_base;
+ size_t memwin_size;
+ phys_addr_t iowin_base;
+ size_t iowin_size;
+};
+
+static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg)
+{
+ writel(val, port->base + reg);
+}
+
+static inline u32 mvebu_readl(struct mvebu_pcie_port *port, u32 reg)
+{
+ return readl(port->base + reg);
+}
+
+static inline bool mvebu_has_ioport(struct mvebu_pcie_port *port)
+{
+ return port->io_target != -1 && port->io_attr != -1;
+}
+
+static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port)
+{
+ return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN);
+}
+
+static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie_port *port, int nr)
+{
+ u32 stat;
+
+ stat = mvebu_readl(port, PCIE_STAT_OFF);
+ stat &= ~PCIE_STAT_BUS;
+ stat |= nr << 8;
+ mvebu_writel(port, stat, PCIE_STAT_OFF);
+}
+
+static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr)
+{
+ u32 stat;
+
+ stat = mvebu_readl(port, PCIE_STAT_OFF);
+ stat &= ~PCIE_STAT_DEV;
+ stat |= nr << 16;
+ mvebu_writel(port, stat, PCIE_STAT_OFF);
+}
+
+/*
+ * Setup PCIE BARs and Address Decode Wins:
+ * BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks
+ * WIN[0-3] -> DRAM bank[0-3]
+ */
+static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
+{
+ const struct mbus_dram_target_info *dram;
+ u32 size;
+ int i;
+
+ dram = mv_mbus_dram_info();
+
+ /* First, disable and clear BARs and windows. */
+ for (i = 1; i < 3; i++) {
+ mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i));
+ mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i));
+ mvebu_writel(port, 0, PCIE_BAR_HI_OFF(i));
+ }
+
+ for (i = 0; i < 5; i++) {
+ mvebu_writel(port, 0, PCIE_WIN04_CTRL_OFF(i));
+ mvebu_writel(port, 0, PCIE_WIN04_BASE_OFF(i));
+ mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i));
+ }
+
+ mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF);
+ mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF);
+ mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF);
+
+ /* Setup windows for DDR banks. Count total DDR size on the fly. */
+ size = 0;
+ for (i = 0; i < dram->num_cs; i++) {
+ const struct mbus_dram_window *cs = dram->cs + i;
+
+ mvebu_writel(port, cs->base & 0xffff0000,
+ PCIE_WIN04_BASE_OFF(i));
+ mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i));
+ mvebu_writel(port,
+ ((cs->size - 1) & 0xffff0000) |
+ (cs->mbus_attr << 8) |
+ (dram->mbus_dram_target_id << 4) | 1,
+ PCIE_WIN04_CTRL_OFF(i));
+
+ size += cs->size;
+ }
+
+ /* Round up 'size' to the nearest power of two. */
+ if ((size & (size - 1)) != 0)
+ size = 1 << fls(size);
+
+ /* Setup BAR[1] to all DRAM banks. */
+ mvebu_writel(port, dram->cs[0].base, PCIE_BAR_LO_OFF(1));
+ mvebu_writel(port, 0, PCIE_BAR_HI_OFF(1));
+ mvebu_writel(port, ((size - 1) & 0xffff0000) | 1,
+ PCIE_BAR_CTRL_OFF(1));
+}
+
+static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+{
+ u32 cmd, mask;
+
+ /* Point PCIe unit MBUS decode windows to DRAM space. */
+ mvebu_pcie_setup_wins(port);
+
+ /* Master + slave enable. */
+ cmd = mvebu_readl(port, PCIE_CMD_OFF);
+ cmd |= PCI_COMMAND_IO;
+ cmd |= PCI_COMMAND_MEMORY;
+ cmd |= PCI_COMMAND_MASTER;
+ mvebu_writel(port, cmd, PCIE_CMD_OFF);
+
+ /* Enable interrupt lines A-D. */
+ mask = mvebu_readl(port, PCIE_MASK_OFF);
+ mask |= PCIE_MASK_ENABLE_INTS;
+ mvebu_writel(port, mask, PCIE_MASK_OFF);
+}
+
+static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,
+ struct pci_bus *bus,
+ u32 devfn, int where, int size, u32 *val)
+{
+ mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
+ PCIE_CONF_ADDR_OFF);
+
+ *val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
+
+ if (size == 1)
+ *val = (*val >> (8 * (where & 3))) & 0xff;
+ else if (size == 2)
+ *val = (*val >> (8 * (where & 3))) & 0xffff;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,
+ struct pci_bus *bus,
+ u32 devfn, int where, int size, u32 val)
+{
+ u32 _val, shift = 8 * (where & 3);
+
+ mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
+ PCIE_CONF_ADDR_OFF);
+ _val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
+
+ if (size == 4)
+ _val = val;
+ else if (size == 2)
+ _val = (_val & ~(0xffff << shift)) | ((val & 0xffff) << shift);
+ else if (size == 1)
+ _val = (_val & ~(0xff << shift)) | ((val & 0xff) << shift);
+ else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ mvebu_writel(port, _val, PCIE_CONF_DATA_OFF);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+/*
+ * Remove windows, starting from the largest ones to the smallest
+ * ones.
+ */
+static void mvebu_pcie_del_windows(struct mvebu_pcie_port *port,
+ phys_addr_t base, size_t size)
+{
+ while (size) {
+ size_t sz = 1 << (fls(size) - 1);
+
+ mvebu_mbus_del_window(base, sz);
+ base += sz;
+ size -= sz;
+ }
+}
+
+/*
+ * MBus windows can only have a power of two size, but PCI BARs do not
+ * have this constraint. Therefore, we have to split the PCI BAR into
+ * areas each having a power of two size. We start from the largest
+ * one (i.e highest order bit set in the size).
+ */
+static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port,
+ unsigned int target, unsigned int attribute,
+ phys_addr_t base, size_t size,
+ phys_addr_t remap)
+{
+ size_t size_mapped = 0;
+
+ while (size) {
+ size_t sz = 1 << (fls(size) - 1);
+ int ret;
+
+ ret = mvebu_mbus_add_window_remap_by_id(target, attribute, base,
+ sz, remap);
+ if (ret) {
+ phys_addr_t end = base + sz - 1;
+
+ dev_err(&port->pcie->pdev->dev,
+ "Could not create MBus window at [mem %pa-%pa]: %d\n",
+ &base, &end, ret);
+ mvebu_pcie_del_windows(port, base - size_mapped,
+ size_mapped);
+ return;
+ }
+
+ size -= sz;
+ size_mapped += sz;
+ base += sz;
+ if (remap != MVEBU_MBUS_NO_REMAP)
+ remap += sz;
+ }
+}
+
+static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
+{
+ phys_addr_t iobase;
+
+ /* Are the new iobase/iolimit values invalid? */
+ if (port->bridge.iolimit < port->bridge.iobase ||
+ port->bridge.iolimitupper < port->bridge.iobaseupper ||
+ !(port->bridge.command & PCI_COMMAND_IO)) {
+
+ /* If a window was configured, remove it */
+ if (port->iowin_base) {
+ mvebu_pcie_del_windows(port, port->iowin_base,
+ port->iowin_size);
+ port->iowin_base = 0;
+ port->iowin_size = 0;
+ }
+
+ return;
+ }
+
+ if (!mvebu_has_ioport(port)) {
+ dev_WARN(&port->pcie->pdev->dev,
+ "Attempt to set IO when IO is disabled\n");
+ return;
+ }
+
+ /*
+ * We read the PCI-to-PCI bridge emulated registers, and
+ * calculate the base address and size of the address decoding
+ * window to setup, according to the PCI-to-PCI bridge
+ * specifications. iobase is the bus address, port->iowin_base
+ * is the CPU address.
+ */
+ iobase = ((port->bridge.iobase & 0xF0) << 8) |
+ (port->bridge.iobaseupper << 16);
+ port->iowin_base = port->pcie->io.start + iobase;
+ port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
+ (port->bridge.iolimitupper << 16)) -
+ iobase) + 1;
+
+ mvebu_pcie_add_windows(port, port->io_target, port->io_attr,
+ port->iowin_base, port->iowin_size,
+ iobase);
+}
+
+static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
+{
+ /* Are the new membase/memlimit values invalid? */
+ if (port->bridge.memlimit < port->bridge.membase ||
+ !(port->bridge.command & PCI_COMMAND_MEMORY)) {
+
+ /* If a window was configured, remove it */
+ if (port->memwin_base) {
+ mvebu_pcie_del_windows(port, port->memwin_base,
+ port->memwin_size);
+ port->memwin_base = 0;
+ port->memwin_size = 0;
+ }
+
+ return;
+ }
+
+ /*
+ * We read the PCI-to-PCI bridge emulated registers, and
+ * calculate the base address and size of the address decoding
+ * window to setup, according to the PCI-to-PCI bridge
+ * specifications.
+ */
+ port->memwin_base = ((port->bridge.membase & 0xFFF0) << 16);
+ port->memwin_size =
+ (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) -
+ port->memwin_base + 1;
+
+ mvebu_pcie_add_windows(port, port->mem_target, port->mem_attr,
+ port->memwin_base, port->memwin_size,
+ MVEBU_MBUS_NO_REMAP);
+}
+
+/*
+ * Initialize the configuration space of the PCI-to-PCI bridge
+ * associated with the given PCIe interface.
+ */
+static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)
+{
+ struct mvebu_sw_pci_bridge *bridge = &port->bridge;
+
+ memset(bridge, 0, sizeof(struct mvebu_sw_pci_bridge));
+
+ bridge->class = PCI_CLASS_BRIDGE_PCI;
+ bridge->vendor = PCI_VENDOR_ID_MARVELL;
+ bridge->device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16;
+ bridge->revision = mvebu_readl(port, PCIE_DEV_REV_OFF) & 0xff;
+ bridge->header_type = PCI_HEADER_TYPE_BRIDGE;
+ bridge->cache_line_size = 0x10;
+
+ /* We support 32 bits I/O addressing */
+ bridge->iobase = PCI_IO_RANGE_TYPE_32;
+ bridge->iolimit = PCI_IO_RANGE_TYPE_32;
+}
+
+/*
+ * Read the configuration space of the PCI-to-PCI bridge associated to
+ * the given PCIe interface.
+ */
+static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
+ unsigned int where, int size, u32 *value)
+{
+ struct mvebu_sw_pci_bridge *bridge = &port->bridge;
+
+ switch (where & ~3) {
+ case PCI_VENDOR_ID:
+ *value = bridge->device << 16 | bridge->vendor;
+ break;
+
+ case PCI_COMMAND:
+ *value = bridge->command;
+ break;
+
+ case PCI_CLASS_REVISION:
+ *value = bridge->class << 16 | bridge->interface << 8 |
+ bridge->revision;
+ break;
+
+ case PCI_CACHE_LINE_SIZE:
+ *value = bridge->bist << 24 | bridge->header_type << 16 |
+ bridge->latency_timer << 8 | bridge->cache_line_size;
+ break;
+
+ case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1:
+ *value = bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4];
+ break;
+
+ case PCI_PRIMARY_BUS:
+ *value = (bridge->secondary_latency_timer << 24 |
+ bridge->subordinate_bus << 16 |
+ bridge->secondary_bus << 8 |
+ bridge->primary_bus);
+ break;
+
+ case PCI_IO_BASE:
+ if (!mvebu_has_ioport(port))
+ *value = bridge->secondary_status << 16;
+ else
+ *value = (bridge->secondary_status << 16 |
+ bridge->iolimit << 8 |
+ bridge->iobase);
+ break;
+
+ case PCI_MEMORY_BASE:
+ *value = (bridge->memlimit << 16 | bridge->membase);
+ break;
+
+ case PCI_PREF_MEMORY_BASE:
+ *value = 0;
+ break;
+
+ case PCI_IO_BASE_UPPER16:
+ *value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
+ break;
+
+ case PCI_ROM_ADDRESS1:
+ *value = 0;
+ break;
+
+ case PCI_INTERRUPT_LINE:
+ /* LINE PIN MIN_GNT MAX_LAT */
+ *value = 0;
+ break;
+
+ default:
+ *value = 0xffffffff;
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+
+ if (size == 2)
+ *value = (*value >> (8 * (where & 3))) & 0xffff;
+ else if (size == 1)
+ *value = (*value >> (8 * (where & 3))) & 0xff;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+/* Write to the PCI-to-PCI bridge configuration space */
+static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
+ unsigned int where, int size, u32 value)
+{
+ struct mvebu_sw_pci_bridge *bridge = &port->bridge;
+ u32 mask, reg;
+ int err;
+
+ if (size == 4)
+ mask = 0x0;
+ else if (size == 2)
+ mask = ~(0xffff << ((where & 3) * 8));
+ else if (size == 1)
+ mask = ~(0xff << ((where & 3) * 8));
+ else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ err = mvebu_sw_pci_bridge_read(port, where & ~3, 4, &reg);
+ if (err)
+ return err;
+
+ value = (reg & mask) | value << ((where & 3) * 8);
+
+ switch (where & ~3) {
+ case PCI_COMMAND:
+ {
+ u32 old = bridge->command;
+
+ if (!mvebu_has_ioport(port))
+ value &= ~PCI_COMMAND_IO;
+
+ bridge->command = value & 0xffff;
+ if ((old ^ bridge->command) & PCI_COMMAND_IO)
+ mvebu_pcie_handle_iobase_change(port);
+ if ((old ^ bridge->command) & PCI_COMMAND_MEMORY)
+ mvebu_pcie_handle_membase_change(port);
+ break;
+ }
+
+ case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1:
+ bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4] = value;
+ break;
+
+ case PCI_IO_BASE:
+ /*
+ * We also keep bit 1 set, it is a read-only bit that
+ * indicates we support 32 bits addressing for the
+ * I/O
+ */
+ bridge->iobase = (value & 0xff) | PCI_IO_RANGE_TYPE_32;
+ bridge->iolimit = ((value >> 8) & 0xff) | PCI_IO_RANGE_TYPE_32;
+ mvebu_pcie_handle_iobase_change(port);
+ break;
+
+ case PCI_MEMORY_BASE:
+ bridge->membase = value & 0xffff;
+ bridge->memlimit = value >> 16;
+ mvebu_pcie_handle_membase_change(port);
+ break;
+
+ case PCI_IO_BASE_UPPER16:
+ bridge->iobaseupper = value & 0xffff;
+ bridge->iolimitupper = value >> 16;
+ mvebu_pcie_handle_iobase_change(port);
+ break;
+
+ case PCI_PRIMARY_BUS:
+ bridge->primary_bus = value & 0xff;
+ bridge->secondary_bus = (value >> 8) & 0xff;
+ bridge->subordinate_bus = (value >> 16) & 0xff;
+ bridge->secondary_latency_timer = (value >> 24) & 0xff;
+ mvebu_pcie_set_local_bus_nr(port, bridge->secondary_bus);
+ break;
+
+ default:
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
+{
+ return sys->private_data;
+}
+
+static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
+ struct pci_bus *bus,
+ int devfn)
+{
+ int i;
+
+ for (i = 0; i < pcie->nports; i++) {
+ struct mvebu_pcie_port *port = &pcie->ports[i];
+ if (bus->number == 0 && port->devfn == devfn)
+ return port;
+ if (bus->number != 0 &&
+ bus->number >= port->bridge.secondary_bus &&
+ bus->number <= port->bridge.subordinate_bus)
+ return port;
+ }
+
+ return NULL;
+}
+
+/* PCI configuration space write function */
+static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+ int where, int size, u32 val)
+{
+ struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata);
+ struct mvebu_pcie_port *port;
+ int ret;
+
+ port = mvebu_pcie_find_port(pcie, bus, devfn);
+ if (!port)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ /* Access the emulated PCI-to-PCI bridge */
+ if (bus->number == 0)
+ return mvebu_sw_pci_bridge_write(port, where, size, val);
+
+ if (!mvebu_pcie_link_up(port))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ /*
+ * On the secondary bus, we don't want to expose any other
+ * device than the device physically connected in the PCIe
+ * slot, visible in slot 0. In slot 1, there's a special
+ * Marvell device that only makes sense when the Armada is
+ * used as a PCIe endpoint.
+ */
+ if (bus->number == port->bridge.secondary_bus &&
+ PCI_SLOT(devfn) != 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ /* Access the real PCIe interface */
+ ret = mvebu_pcie_hw_wr_conf(port, bus, devfn,
+ where, size, val);
+
+ return ret;
+}
+
+/* PCI configuration space read function */
+static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+ int size, u32 *val)
+{
+ struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata);
+ struct mvebu_pcie_port *port;
+ int ret;
+
+ port = mvebu_pcie_find_port(pcie, bus, devfn);
+ if (!port) {
+ *val = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ /* Access the emulated PCI-to-PCI bridge */
+ if (bus->number == 0)
+ return mvebu_sw_pci_bridge_read(port, where, size, val);
+
+ if (!mvebu_pcie_link_up(port)) {
+ *val = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ /*
+ * On the secondary bus, we don't want to expose any other
+ * device than the device physically connected in the PCIe
+ * slot, visible in slot 0. In slot 1, there's a special
+ * Marvell device that only makes sense when the Armada is
+ * used as a PCIe endpoint.
+ */
+ if (bus->number == port->bridge.secondary_bus &&
+ PCI_SLOT(devfn) != 0) {
+ *val = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ /* Access the real PCIe interface */
+ ret = mvebu_pcie_hw_rd_conf(port, bus, devfn,
+ where, size, val);
+
+ return ret;
+}
+
+static struct pci_ops mvebu_pcie_ops = {
+ .read = mvebu_pcie_rd_conf,
+ .write = mvebu_pcie_wr_conf,
+};
+
+static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+ struct mvebu_pcie *pcie = sys_to_pcie(sys);
+ int i;
+ int domain = 0;
+
+#ifdef CONFIG_PCI_DOMAINS
+ domain = sys->domain;
+#endif
+
+ snprintf(pcie->mem_name, sizeof(pcie->mem_name), "PCI MEM %04x",
+ domain);
+ pcie->mem.name = pcie->mem_name;
+
+ snprintf(pcie->io_name, sizeof(pcie->io_name), "PCI I/O %04x", domain);
+ pcie->realio.name = pcie->io_name;
+
+ if (request_resource(&iomem_resource, &pcie->mem))
+ return 0;
+
+ if (resource_size(&pcie->realio) != 0) {
+ if (request_resource(&ioport_resource, &pcie->realio)) {
+ release_resource(&pcie->mem);
+ return 0;
+ }
+ pci_add_resource_offset(&sys->resources, &pcie->realio,
+ sys->io_offset);
+ }
+ pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
+ pci_add_resource(&sys->resources, &pcie->busn);
+
+ for (i = 0; i < pcie->nports; i++) {
+ struct mvebu_pcie_port *port = &pcie->ports[i];
+ if (!port->base)
+ continue;
+ mvebu_pcie_setup_hw(port);
+ }
+
+ return 1;
+}
+
+static struct pci_bus *mvebu_pcie_scan_bus(int nr, struct pci_sys_data *sys)
+{
+ struct mvebu_pcie *pcie = sys_to_pcie(sys);
+ struct pci_bus *bus;
+
+ bus = pci_create_root_bus(&pcie->pdev->dev, sys->busnr,
+ &mvebu_pcie_ops, sys, &sys->resources);
+ if (!bus)
+ return NULL;
+
+ pci_scan_child_bus(bus);
+
+ return bus;
+}
+
+static void mvebu_pcie_add_bus(struct pci_bus *bus)
+{
+ struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata);
+ bus->msi = pcie->msi;
+}
+
+static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
+ const struct resource *res,
+ resource_size_t start,
+ resource_size_t size,
+ resource_size_t align)
+{
+ if (dev->bus->number != 0)
+ return start;
+
+ /*
+ * On the PCI-to-PCI bridge side, the I/O windows must have at
+ * least a 64 KB size and the memory windows must have at
+ * least a 1 MB size. Moreover, MBus windows need to have a
+ * base address aligned on their size, and their size must be
+ * a power of two. This means that if the BAR doesn't have a
+ * power of two size, several MBus windows will actually be
+ * created. We need to ensure that the biggest MBus window
+ * (which will be the first one) is aligned on its size, which
+ * explains the rounddown_pow_of_two() being done here.
+ */
+ if (res->flags & IORESOURCE_IO)
+ return round_up(start, max_t(resource_size_t, SZ_64K,
+ rounddown_pow_of_two(size)));
+ else if (res->flags & IORESOURCE_MEM)
+ return round_up(start, max_t(resource_size_t, SZ_1M,
+ rounddown_pow_of_two(size)));
+ else
+ return start;
+}
+
+static void mvebu_pcie_enable(struct mvebu_pcie *pcie)
+{
+ struct hw_pci hw;
+
+ memset(&hw, 0, sizeof(hw));
+
+ hw.nr_controllers = 1;
+ hw.private_data = (void **)&pcie;
+ hw.setup = mvebu_pcie_setup;
+ hw.scan = mvebu_pcie_scan_bus;
+ hw.map_irq = of_irq_parse_and_map_pci;
+ hw.ops = &mvebu_pcie_ops;
+ hw.align_resource = mvebu_pcie_align_resource;
+ hw.add_bus = mvebu_pcie_add_bus;
+
+ pci_common_init(&hw);
+}
+
+/*
+ * Looks up the list of register addresses encoded into the reg =
+ * <...> property for one that matches the given port/lane. Once
+ * found, maps it.
+ */
+static void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev,
+ struct device_node *np,
+ struct mvebu_pcie_port *port)
+{
+ struct resource regs;
+ int ret = 0;
+
+ ret = of_address_to_resource(np, 0, &regs);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return devm_ioremap_resource(&pdev->dev, &regs);
+}
+
+#define DT_FLAGS_TO_TYPE(flags) (((flags) >> 24) & 0x03)
+#define DT_TYPE_IO 0x1
+#define DT_TYPE_MEM32 0x2
+#define DT_CPUADDR_TO_TARGET(cpuaddr) (((cpuaddr) >> 56) & 0xFF)
+#define DT_CPUADDR_TO_ATTR(cpuaddr) (((cpuaddr) >> 48) & 0xFF)
+
+static int mvebu_get_tgt_attr(struct device_node *np, int devfn,
+ unsigned long type,
+ unsigned int *tgt,
+ unsigned int *attr)
+{
+ const int na = 3, ns = 2;
+ const __be32 *range;
+ int rlen, nranges, rangesz, pna, i;
+
+ *tgt = -1;
+ *attr = -1;
+
+ range = of_get_property(np, "ranges", &rlen);
+ if (!range)
+ return -EINVAL;
+
+ pna = of_n_addr_cells(np);
+ rangesz = pna + na + ns;
+ nranges = rlen / sizeof(__be32) / rangesz;
+
+ for (i = 0; i < nranges; i++) {
+ u32 flags = of_read_number(range, 1);
+ u32 slot = of_read_number(range + 1, 1);
+ u64 cpuaddr = of_read_number(range + na, pna);
+ unsigned long rtype;
+
+ if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_IO)
+ rtype = IORESOURCE_IO;
+ else if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_MEM32)
+ rtype = IORESOURCE_MEM;
+
+ if (slot == PCI_SLOT(devfn) && type == rtype) {
+ *tgt = DT_CPUADDR_TO_TARGET(cpuaddr);
+ *attr = DT_CPUADDR_TO_ATTR(cpuaddr);
+ return 0;
+ }
+
+ range += rangesz;
+ }
+
+ return -ENOENT;
+}
+
+static void mvebu_pcie_msi_enable(struct mvebu_pcie *pcie)
+{
+ struct device_node *msi_node;
+
+ msi_node = of_parse_phandle(pcie->pdev->dev.of_node,
+ "msi-parent", 0);
+ if (!msi_node)
+ return;
+
+ pcie->msi = of_pci_find_msi_chip_by_node(msi_node);
+
+ if (pcie->msi)
+ pcie->msi->dev = &pcie->pdev->dev;
+}
+
+static int mvebu_pcie_probe(struct platform_device *pdev)
+{
+ struct mvebu_pcie *pcie;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child;
+ int i, ret;
+
+ pcie = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pcie),
+ GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ pcie->pdev = pdev;
+ platform_set_drvdata(pdev, pcie);
+
+ /* Get the PCIe memory and I/O aperture */
+ mvebu_mbus_get_pcie_mem_aperture(&pcie->mem);
+ if (resource_size(&pcie->mem) == 0) {
+ dev_err(&pdev->dev, "invalid memory aperture size\n");
+ return -EINVAL;
+ }
+
+ mvebu_mbus_get_pcie_io_aperture(&pcie->io);
+
+ if (resource_size(&pcie->io) != 0) {
+ pcie->realio.flags = pcie->io.flags;
+ pcie->realio.start = PCIBIOS_MIN_IO;
+ pcie->realio.end = min_t(resource_size_t,
+ IO_SPACE_LIMIT,
+ resource_size(&pcie->io));
+ } else
+ pcie->realio = pcie->io;
+
+ /* Get the bus range */
+ ret = of_pci_parse_bus_range(np, &pcie->busn);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse bus-range property: %d\n",
+ ret);
+ return ret;
+ }
+
+ i = 0;
+ for_each_child_of_node(pdev->dev.of_node, child) {
+ if (!of_device_is_available(child))
+ continue;
+ i++;
+ }
+
+ pcie->ports = devm_kzalloc(&pdev->dev, i *
+ sizeof(struct mvebu_pcie_port),
+ GFP_KERNEL);
+ if (!pcie->ports)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_child_of_node(pdev->dev.of_node, child) {
+ struct mvebu_pcie_port *port = &pcie->ports[i];
+ enum of_gpio_flags flags;
+
+ if (!of_device_is_available(child))
+ continue;
+
+ port->pcie = pcie;
+
+ if (of_property_read_u32(child, "marvell,pcie-port",
+ &port->port)) {
+ dev_warn(&pdev->dev,
+ "ignoring PCIe DT node, missing pcie-port property\n");
+ continue;
+ }
+
+ if (of_property_read_u32(child, "marvell,pcie-lane",
+ &port->lane))
+ port->lane = 0;
+
+ port->name = kasprintf(GFP_KERNEL, "pcie%d.%d",
+ port->port, port->lane);
+
+ port->devfn = of_pci_get_devfn(child);
+ if (port->devfn < 0)
+ continue;
+
+ ret = mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_MEM,
+ &port->mem_target, &port->mem_attr);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "PCIe%d.%d: cannot get tgt/attr for mem window\n",
+ port->port, port->lane);
+ continue;
+ }
+
+ if (resource_size(&pcie->io) != 0)
+ mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO,
+ &port->io_target, &port->io_attr);
+ else {
+ port->io_target = -1;
+ port->io_attr = -1;
+ }
+
+ port->reset_gpio = of_get_named_gpio_flags(child,
+ "reset-gpios", 0, &flags);
+ if (gpio_is_valid(port->reset_gpio)) {
+ u32 reset_udelay = 20000;
+
+ port->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
+ port->reset_name = kasprintf(GFP_KERNEL,
+ "pcie%d.%d-reset", port->port, port->lane);
+ of_property_read_u32(child, "reset-delay-us",
+ &reset_udelay);
+
+ ret = devm_gpio_request_one(&pdev->dev,
+ port->reset_gpio, GPIOF_DIR_OUT, port->reset_name);
+ if (ret) {
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ continue;
+ }
+
+ gpio_set_value(port->reset_gpio,
+ (port->reset_active_low) ? 1 : 0);
+ msleep(reset_udelay/1000);
+ }
+
+ port->clk = of_clk_get_by_name(child, NULL);
+ if (IS_ERR(port->clk)) {
+ dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n",
+ port->port, port->lane);
+ continue;
+ }
+
+ ret = clk_prepare_enable(port->clk);
+ if (ret)
+ continue;
+
+ port->base = mvebu_pcie_map_registers(pdev, child, port);
+ if (IS_ERR(port->base)) {
+ dev_err(&pdev->dev, "PCIe%d.%d: cannot map registers\n",
+ port->port, port->lane);
+ port->base = NULL;
+ clk_disable_unprepare(port->clk);
+ continue;
+ }
+
+ mvebu_pcie_set_local_dev_nr(port, 1);
+
+ port->dn = child;
+ mvebu_sw_pci_bridge_init(port);
+ i++;
+ }
+
+ pcie->nports = i;
+
+ for (i = 0; i < (IO_SPACE_LIMIT - SZ_64K); i += SZ_64K)
+ pci_ioremap_io(i, pcie->io.start + i);
+
+ mvebu_pcie_msi_enable(pcie);
+ mvebu_pcie_enable(pcie);
+
+ return 0;
+}
+
+static const struct of_device_id mvebu_pcie_of_match_table[] = {
+ { .compatible = "marvell,armada-xp-pcie", },
+ { .compatible = "marvell,armada-370-pcie", },
+ { .compatible = "marvell,dove-pcie", },
+ { .compatible = "marvell,kirkwood-pcie", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table);
+
+static struct platform_driver mvebu_pcie_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mvebu-pcie",
+ .of_match_table = mvebu_pcie_of_match_table,
+ /* driver unloading/unbinding currently not supported */
+ .suppress_bind_attrs = true,
+ },
+ .probe = mvebu_pcie_probe,
+};
+module_platform_driver(mvebu_pcie_driver);
+
+MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
+MODULE_DESCRIPTION("Marvell EBU PCIe driver");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c
new file mode 100644
index 00000000000..3ef854f5a5b
--- /dev/null
+++ b/drivers/pci/host/pci-rcar-gen2.c
@@ -0,0 +1,426 @@
+/*
+ * pci-rcar-gen2: internal PCI bus support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+/* AHB-PCI Bridge PCI communication registers */
+#define RCAR_AHBPCI_PCICOM_OFFSET 0x800
+
+#define RCAR_PCIAHB_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x00)
+#define RCAR_PCIAHB_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x04)
+#define RCAR_PCIAHB_PREFETCH0 0x0
+#define RCAR_PCIAHB_PREFETCH4 0x1
+#define RCAR_PCIAHB_PREFETCH8 0x2
+#define RCAR_PCIAHB_PREFETCH16 0x3
+
+#define RCAR_AHBPCI_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x10)
+#define RCAR_AHBPCI_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x14)
+#define RCAR_AHBPCI_WIN_CTR_MEM (3 << 1)
+#define RCAR_AHBPCI_WIN_CTR_CFG (5 << 1)
+#define RCAR_AHBPCI_WIN1_HOST (1 << 30)
+#define RCAR_AHBPCI_WIN1_DEVICE (1 << 31)
+
+#define RCAR_PCI_INT_ENABLE_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x20)
+#define RCAR_PCI_INT_STATUS_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x24)
+#define RCAR_PCI_INT_SIGTABORT (1 << 0)
+#define RCAR_PCI_INT_SIGRETABORT (1 << 1)
+#define RCAR_PCI_INT_REMABORT (1 << 2)
+#define RCAR_PCI_INT_PERR (1 << 3)
+#define RCAR_PCI_INT_SIGSERR (1 << 4)
+#define RCAR_PCI_INT_RESERR (1 << 5)
+#define RCAR_PCI_INT_WIN1ERR (1 << 12)
+#define RCAR_PCI_INT_WIN2ERR (1 << 13)
+#define RCAR_PCI_INT_A (1 << 16)
+#define RCAR_PCI_INT_B (1 << 17)
+#define RCAR_PCI_INT_PME (1 << 19)
+#define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \
+ RCAR_PCI_INT_SIGRETABORT | \
+ RCAR_PCI_INT_SIGRETABORT | \
+ RCAR_PCI_INT_REMABORT | \
+ RCAR_PCI_INT_PERR | \
+ RCAR_PCI_INT_SIGSERR | \
+ RCAR_PCI_INT_RESERR | \
+ RCAR_PCI_INT_WIN1ERR | \
+ RCAR_PCI_INT_WIN2ERR)
+
+#define RCAR_AHB_BUS_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x30)
+#define RCAR_AHB_BUS_MMODE_HTRANS (1 << 0)
+#define RCAR_AHB_BUS_MMODE_BYTE_BURST (1 << 1)
+#define RCAR_AHB_BUS_MMODE_WR_INCR (1 << 2)
+#define RCAR_AHB_BUS_MMODE_HBUS_REQ (1 << 7)
+#define RCAR_AHB_BUS_SMODE_READYCTR (1 << 17)
+#define RCAR_AHB_BUS_MODE (RCAR_AHB_BUS_MMODE_HTRANS | \
+ RCAR_AHB_BUS_MMODE_BYTE_BURST | \
+ RCAR_AHB_BUS_MMODE_WR_INCR | \
+ RCAR_AHB_BUS_MMODE_HBUS_REQ | \
+ RCAR_AHB_BUS_SMODE_READYCTR)
+
+#define RCAR_USBCTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x34)
+#define RCAR_USBCTR_USBH_RST (1 << 0)
+#define RCAR_USBCTR_PCICLK_MASK (1 << 1)
+#define RCAR_USBCTR_PLL_RST (1 << 2)
+#define RCAR_USBCTR_DIRPD (1 << 8)
+#define RCAR_USBCTR_PCIAHB_WIN2_EN (1 << 9)
+#define RCAR_USBCTR_PCIAHB_WIN1_256M (0 << 10)
+#define RCAR_USBCTR_PCIAHB_WIN1_512M (1 << 10)
+#define RCAR_USBCTR_PCIAHB_WIN1_1G (2 << 10)
+#define RCAR_USBCTR_PCIAHB_WIN1_2G (3 << 10)
+#define RCAR_USBCTR_PCIAHB_WIN1_MASK (3 << 10)
+
+#define RCAR_PCI_ARBITER_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x40)
+#define RCAR_PCI_ARBITER_PCIREQ0 (1 << 0)
+#define RCAR_PCI_ARBITER_PCIREQ1 (1 << 1)
+#define RCAR_PCI_ARBITER_PCIBP_MODE (1 << 12)
+
+#define RCAR_PCI_UNIT_REV_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x48)
+
+struct rcar_pci_priv {
+ struct device *dev;
+ void __iomem *reg;
+ struct resource io_res;
+ struct resource mem_res;
+ struct resource *cfg_res;
+ unsigned busnr;
+ int irq;
+ unsigned long window_size;
+};
+
+/* PCI configuration space operations */
+static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn,
+ int where)
+{
+ struct pci_sys_data *sys = bus->sysdata;
+ struct rcar_pci_priv *priv = sys->private_data;
+ int slot, val;
+
+ if (sys->busnr != bus->number || PCI_FUNC(devfn))
+ return NULL;
+
+ /* Only one EHCI/OHCI device built-in */
+ slot = PCI_SLOT(devfn);
+ if (slot > 2)
+ return NULL;
+
+ /* bridge logic only has registers to 0x40 */
+ if (slot == 0x0 && where >= 0x40)
+ return NULL;
+
+ val = slot ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG :
+ RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG;
+
+ iowrite32(val, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG);
+ return priv->reg + (slot >> 1) * 0x100 + where;
+}
+
+static int rcar_pci_read_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ void __iomem *reg = rcar_pci_cfg_base(bus, devfn, where);
+
+ if (!reg)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ switch (size) {
+ case 1:
+ *val = ioread8(reg);
+ break;
+ case 2:
+ *val = ioread16(reg);
+ break;
+ default:
+ *val = ioread32(reg);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int rcar_pci_write_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ void __iomem *reg = rcar_pci_cfg_base(bus, devfn, where);
+
+ if (!reg)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ switch (size) {
+ case 1:
+ iowrite8(val, reg);
+ break;
+ case 2:
+ iowrite16(val, reg);
+ break;
+ default:
+ iowrite32(val, reg);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+/* PCI interrupt mapping */
+static int rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct pci_sys_data *sys = dev->bus->sysdata;
+ struct rcar_pci_priv *priv = sys->private_data;
+ int irq;
+
+ irq = of_irq_parse_and_map_pci(dev, slot, pin);
+ if (!irq)
+ irq = priv->irq;
+
+ return irq;
+}
+
+#ifdef CONFIG_PCI_DEBUG
+/* if debug enabled, then attach an error handler irq to the bridge */
+
+static irqreturn_t rcar_pci_err_irq(int irq, void *pw)
+{
+ struct rcar_pci_priv *priv = pw;
+ u32 status = ioread32(priv->reg + RCAR_PCI_INT_STATUS_REG);
+
+ if (status & RCAR_PCI_INT_ALLERRORS) {
+ dev_err(priv->dev, "error irq: status %08x\n", status);
+
+ /* clear the error(s) */
+ iowrite32(status & RCAR_PCI_INT_ALLERRORS,
+ priv->reg + RCAR_PCI_INT_STATUS_REG);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static void rcar_pci_setup_errirq(struct rcar_pci_priv *priv)
+{
+ int ret;
+ u32 val;
+
+ ret = devm_request_irq(priv->dev, priv->irq, rcar_pci_err_irq,
+ IRQF_SHARED, "error irq", priv);
+ if (ret) {
+ dev_err(priv->dev, "cannot claim IRQ for error handling\n");
+ return;
+ }
+
+ val = ioread32(priv->reg + RCAR_PCI_INT_ENABLE_REG);
+ val |= RCAR_PCI_INT_ALLERRORS;
+ iowrite32(val, priv->reg + RCAR_PCI_INT_ENABLE_REG);
+}
+#else
+static inline void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { }
+#endif
+
+/* PCI host controller setup */
+static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
+{
+ struct rcar_pci_priv *priv = sys->private_data;
+ void __iomem *reg = priv->reg;
+ u32 val;
+
+ pm_runtime_enable(priv->dev);
+ pm_runtime_get_sync(priv->dev);
+
+ val = ioread32(reg + RCAR_PCI_UNIT_REV_REG);
+ dev_info(priv->dev, "PCI: bus%u revision %x\n", sys->busnr, val);
+
+ /* Disable Direct Power Down State and assert reset */
+ val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD;
+ val |= RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST;
+ iowrite32(val, reg + RCAR_USBCTR_REG);
+ udelay(4);
+
+ /* De-assert reset and reset PCIAHB window1 size */
+ val &= ~(RCAR_USBCTR_PCIAHB_WIN1_MASK | RCAR_USBCTR_PCICLK_MASK |
+ RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST);
+
+ /* Setup PCIAHB window1 size */
+ switch (priv->window_size) {
+ case SZ_2G:
+ val |= RCAR_USBCTR_PCIAHB_WIN1_2G;
+ break;
+ case SZ_1G:
+ val |= RCAR_USBCTR_PCIAHB_WIN1_1G;
+ break;
+ case SZ_512M:
+ val |= RCAR_USBCTR_PCIAHB_WIN1_512M;
+ break;
+ default:
+ pr_warn("unknown window size %ld - defaulting to 256M\n",
+ priv->window_size);
+ priv->window_size = SZ_256M;
+ /* fall-through */
+ case SZ_256M:
+ val |= RCAR_USBCTR_PCIAHB_WIN1_256M;
+ break;
+ }
+ iowrite32(val, reg + RCAR_USBCTR_REG);
+
+ /* Configure AHB master and slave modes */
+ iowrite32(RCAR_AHB_BUS_MODE, reg + RCAR_AHB_BUS_CTR_REG);
+
+ /* Configure PCI arbiter */
+ val = ioread32(reg + RCAR_PCI_ARBITER_CTR_REG);
+ val |= RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 |
+ RCAR_PCI_ARBITER_PCIBP_MODE;
+ iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG);
+
+ /* PCI-AHB mapping: 0x40000000 base */
+ iowrite32(0x40000000 | RCAR_PCIAHB_PREFETCH16,
+ reg + RCAR_PCIAHB_WIN1_CTR_REG);
+
+ /* AHB-PCI mapping: OHCI/EHCI registers */
+ val = priv->mem_res.start | RCAR_AHBPCI_WIN_CTR_MEM;
+ iowrite32(val, reg + RCAR_AHBPCI_WIN2_CTR_REG);
+
+ /* Enable AHB-PCI bridge PCI configuration access */
+ iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG,
+ reg + RCAR_AHBPCI_WIN1_CTR_REG);
+ /* Set PCI-AHB Window1 address */
+ iowrite32(0x40000000 | PCI_BASE_ADDRESS_MEM_PREFETCH,
+ reg + PCI_BASE_ADDRESS_1);
+ /* Set AHB-PCI bridge PCI communication area address */
+ val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET;
+ iowrite32(val, reg + PCI_BASE_ADDRESS_0);
+
+ val = ioread32(reg + PCI_COMMAND);
+ val |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+ PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+ iowrite32(val, reg + PCI_COMMAND);
+
+ /* Enable PCI interrupts */
+ iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME,
+ reg + RCAR_PCI_INT_ENABLE_REG);
+
+ if (priv->irq > 0)
+ rcar_pci_setup_errirq(priv);
+
+ /* Add PCI resources */
+ pci_add_resource(&sys->resources, &priv->io_res);
+ pci_add_resource(&sys->resources, &priv->mem_res);
+
+ /* Setup bus number based on platform device id / of bus-range */
+ sys->busnr = priv->busnr;
+ return 1;
+}
+
+static struct pci_ops rcar_pci_ops = {
+ .read = rcar_pci_read_config,
+ .write = rcar_pci_write_config,
+};
+
+static int rcar_pci_probe(struct platform_device *pdev)
+{
+ struct resource *cfg_res, *mem_res;
+ struct rcar_pci_priv *priv;
+ void __iomem *reg;
+ struct hw_pci hw;
+ void *hw_private[1];
+
+ cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ reg = devm_ioremap_resource(&pdev->dev, cfg_res);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!mem_res || !mem_res->start)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct rcar_pci_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mem_res = *mem_res;
+ /*
+ * The controller does not support/use port I/O,
+ * so setup a dummy port I/O region here.
+ */
+ priv->io_res.start = priv->mem_res.start;
+ priv->io_res.end = priv->mem_res.end;
+ priv->io_res.flags = IORESOURCE_IO;
+
+ priv->cfg_res = cfg_res;
+
+ priv->irq = platform_get_irq(pdev, 0);
+ priv->reg = reg;
+ priv->dev = &pdev->dev;
+
+ if (priv->irq < 0) {
+ dev_err(&pdev->dev, "no valid irq found\n");
+ return priv->irq;
+ }
+
+ priv->window_size = SZ_1G;
+
+ if (pdev->dev.of_node) {
+ struct resource busnr;
+ int ret;
+
+ ret = of_pci_parse_bus_range(pdev->dev.of_node, &busnr);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to parse bus-range\n");
+ return ret;
+ }
+
+ priv->busnr = busnr.start;
+ if (busnr.end != busnr.start)
+ dev_warn(&pdev->dev, "only one bus number supported\n");
+ } else {
+ priv->busnr = pdev->id;
+ }
+
+ hw_private[0] = priv;
+ memset(&hw, 0, sizeof(hw));
+ hw.nr_controllers = ARRAY_SIZE(hw_private);
+ hw.private_data = hw_private;
+ hw.map_irq = rcar_pci_map_irq;
+ hw.ops = &rcar_pci_ops;
+ hw.setup = rcar_pci_setup;
+ pci_common_init_dev(&pdev->dev, &hw);
+ return 0;
+}
+
+static struct of_device_id rcar_pci_of_match[] = {
+ { .compatible = "renesas,pci-r8a7790", },
+ { .compatible = "renesas,pci-r8a7791", },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, rcar_pci_of_match);
+
+static struct platform_driver rcar_pci_driver = {
+ .driver = {
+ .name = "pci-rcar-gen2",
+ .owner = THIS_MODULE,
+ .suppress_bind_attrs = true,
+ .of_match_table = rcar_pci_of_match,
+ },
+ .probe = rcar_pci_probe,
+};
+
+module_platform_driver(rcar_pci_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Renesas R-Car Gen2 internal PCI");
+MODULE_AUTHOR("Valentine Barshak <valentine.barshak@cogentembedded.com>");
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
new file mode 100644
index 00000000000..083cf37ca04
--- /dev/null
+++ b/drivers/pci/host/pci-tegra.c
@@ -0,0 +1,1719 @@
+/*
+ * PCIe host controller driver for Tegra SoCs
+ *
+ * Copyright (c) 2010, CompuLab, Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on NVIDIA PCIe driver
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * Bits taken from arch/arm/mach-dove/pcie.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/tegra-cpuidle.h>
+#include <linux/tegra-powergate.h>
+#include <linux/vmalloc.h>
+#include <linux/regulator/consumer.h>
+
+#include <asm/mach/irq.h>
+#include <asm/mach/map.h>
+#include <asm/mach/pci.h>
+
+#define INT_PCI_MSI_NR (8 * 32)
+
+/* register definitions */
+
+#define AFI_AXI_BAR0_SZ 0x00
+#define AFI_AXI_BAR1_SZ 0x04
+#define AFI_AXI_BAR2_SZ 0x08
+#define AFI_AXI_BAR3_SZ 0x0c
+#define AFI_AXI_BAR4_SZ 0x10
+#define AFI_AXI_BAR5_SZ 0x14
+
+#define AFI_AXI_BAR0_START 0x18
+#define AFI_AXI_BAR1_START 0x1c
+#define AFI_AXI_BAR2_START 0x20
+#define AFI_AXI_BAR3_START 0x24
+#define AFI_AXI_BAR4_START 0x28
+#define AFI_AXI_BAR5_START 0x2c
+
+#define AFI_FPCI_BAR0 0x30
+#define AFI_FPCI_BAR1 0x34
+#define AFI_FPCI_BAR2 0x38
+#define AFI_FPCI_BAR3 0x3c
+#define AFI_FPCI_BAR4 0x40
+#define AFI_FPCI_BAR5 0x44
+
+#define AFI_CACHE_BAR0_SZ 0x48
+#define AFI_CACHE_BAR0_ST 0x4c
+#define AFI_CACHE_BAR1_SZ 0x50
+#define AFI_CACHE_BAR1_ST 0x54
+
+#define AFI_MSI_BAR_SZ 0x60
+#define AFI_MSI_FPCI_BAR_ST 0x64
+#define AFI_MSI_AXI_BAR_ST 0x68
+
+#define AFI_MSI_VEC0 0x6c
+#define AFI_MSI_VEC1 0x70
+#define AFI_MSI_VEC2 0x74
+#define AFI_MSI_VEC3 0x78
+#define AFI_MSI_VEC4 0x7c
+#define AFI_MSI_VEC5 0x80
+#define AFI_MSI_VEC6 0x84
+#define AFI_MSI_VEC7 0x88
+
+#define AFI_MSI_EN_VEC0 0x8c
+#define AFI_MSI_EN_VEC1 0x90
+#define AFI_MSI_EN_VEC2 0x94
+#define AFI_MSI_EN_VEC3 0x98
+#define AFI_MSI_EN_VEC4 0x9c
+#define AFI_MSI_EN_VEC5 0xa0
+#define AFI_MSI_EN_VEC6 0xa4
+#define AFI_MSI_EN_VEC7 0xa8
+
+#define AFI_CONFIGURATION 0xac
+#define AFI_CONFIGURATION_EN_FPCI (1 << 0)
+
+#define AFI_FPCI_ERROR_MASKS 0xb0
+
+#define AFI_INTR_MASK 0xb4
+#define AFI_INTR_MASK_INT_MASK (1 << 0)
+#define AFI_INTR_MASK_MSI_MASK (1 << 8)
+
+#define AFI_INTR_CODE 0xb8
+#define AFI_INTR_CODE_MASK 0xf
+#define AFI_INTR_AXI_SLAVE_ERROR 1
+#define AFI_INTR_AXI_DECODE_ERROR 2
+#define AFI_INTR_TARGET_ABORT 3
+#define AFI_INTR_MASTER_ABORT 4
+#define AFI_INTR_INVALID_WRITE 5
+#define AFI_INTR_LEGACY 6
+#define AFI_INTR_FPCI_DECODE_ERROR 7
+
+#define AFI_INTR_SIGNATURE 0xbc
+#define AFI_UPPER_FPCI_ADDRESS 0xc0
+#define AFI_SM_INTR_ENABLE 0xc4
+#define AFI_SM_INTR_INTA_ASSERT (1 << 0)
+#define AFI_SM_INTR_INTB_ASSERT (1 << 1)
+#define AFI_SM_INTR_INTC_ASSERT (1 << 2)
+#define AFI_SM_INTR_INTD_ASSERT (1 << 3)
+#define AFI_SM_INTR_INTA_DEASSERT (1 << 4)
+#define AFI_SM_INTR_INTB_DEASSERT (1 << 5)
+#define AFI_SM_INTR_INTC_DEASSERT (1 << 6)
+#define AFI_SM_INTR_INTD_DEASSERT (1 << 7)
+
+#define AFI_AFI_INTR_ENABLE 0xc8
+#define AFI_INTR_EN_INI_SLVERR (1 << 0)
+#define AFI_INTR_EN_INI_DECERR (1 << 1)
+#define AFI_INTR_EN_TGT_SLVERR (1 << 2)
+#define AFI_INTR_EN_TGT_DECERR (1 << 3)
+#define AFI_INTR_EN_TGT_WRERR (1 << 4)
+#define AFI_INTR_EN_DFPCI_DECERR (1 << 5)
+#define AFI_INTR_EN_AXI_DECERR (1 << 6)
+#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7)
+#define AFI_INTR_EN_PRSNT_SENSE (1 << 8)
+
+#define AFI_PCIE_CONFIG 0x0f8
+#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1))
+#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20)
+
+#define AFI_FUSE 0x104
+#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2)
+
+#define AFI_PEX0_CTRL 0x110
+#define AFI_PEX1_CTRL 0x118
+#define AFI_PEX2_CTRL 0x128
+#define AFI_PEX_CTRL_RST (1 << 0)
+#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1)
+#define AFI_PEX_CTRL_REFCLK_EN (1 << 3)
+
+#define AFI_PEXBIAS_CTRL_0 0x168
+
+#define RP_VEND_XP 0x00000F00
+#define RP_VEND_XP_DL_UP (1 << 30)
+
+#define RP_LINK_CONTROL_STATUS 0x00000090
+#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000
+#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000
+
+#define PADS_CTL_SEL 0x0000009C
+
+#define PADS_CTL 0x000000A0
+#define PADS_CTL_IDDQ_1L (1 << 0)
+#define PADS_CTL_TX_DATA_EN_1L (1 << 6)
+#define PADS_CTL_RX_DATA_EN_1L (1 << 10)
+
+#define PADS_PLL_CTL_TEGRA20 0x000000B8
+#define PADS_PLL_CTL_TEGRA30 0x000000B4
+#define PADS_PLL_CTL_RST_B4SM (1 << 1)
+#define PADS_PLL_CTL_LOCKDET (1 << 8)
+#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16)
+#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0 << 16)
+#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (1 << 16)
+#define PADS_PLL_CTL_REFCLK_EXTERNAL (2 << 16)
+#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20)
+#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20)
+#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20)
+#define PADS_PLL_CTL_TXCLKREF_BUF_EN (1 << 22)
+
+#define PADS_REFCLK_CFG0 0x000000C8
+#define PADS_REFCLK_CFG1 0x000000CC
+
+/*
+ * Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit
+ * entries, one entry per PCIe port. These field definitions and desired
+ * values aren't in the TRM, but do come from NVIDIA.
+ */
+#define PADS_REFCLK_CFG_TERM_SHIFT 2 /* 6:2 */
+#define PADS_REFCLK_CFG_E_TERM_SHIFT 7
+#define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */
+#define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */
+
+/* Default value provided by HW engineering is 0xfa5c */
+#define PADS_REFCLK_CFG_VALUE \
+ ( \
+ (0x17 << PADS_REFCLK_CFG_TERM_SHIFT) | \
+ (0 << PADS_REFCLK_CFG_E_TERM_SHIFT) | \
+ (0xa << PADS_REFCLK_CFG_PREDI_SHIFT) | \
+ (0xf << PADS_REFCLK_CFG_DRVI_SHIFT) \
+ )
+
+struct tegra_msi {
+ struct msi_chip chip;
+ DECLARE_BITMAP(used, INT_PCI_MSI_NR);
+ struct irq_domain *domain;
+ unsigned long pages;
+ struct mutex lock;
+ int irq;
+};
+
+/* used to differentiate between Tegra SoC generations */
+struct tegra_pcie_soc_data {
+ unsigned int num_ports;
+ unsigned int msi_base_shift;
+ u32 pads_pll_ctl;
+ u32 tx_ref_sel;
+ bool has_pex_clkreq_en;
+ bool has_pex_bias_ctrl;
+ bool has_intr_prsnt_sense;
+ bool has_avdd_supply;
+ bool has_cml_clk;
+};
+
+static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip)
+{
+ return container_of(chip, struct tegra_msi, chip);
+}
+
+struct tegra_pcie {
+ struct device *dev;
+
+ void __iomem *pads;
+ void __iomem *afi;
+ int irq;
+
+ struct list_head buses;
+ struct resource *cs;
+
+ struct resource io;
+ struct resource mem;
+ struct resource prefetch;
+ struct resource busn;
+
+ struct clk *pex_clk;
+ struct clk *afi_clk;
+ struct clk *pll_e;
+ struct clk *cml_clk;
+
+ struct reset_control *pex_rst;
+ struct reset_control *afi_rst;
+ struct reset_control *pcie_xrst;
+
+ struct tegra_msi msi;
+
+ struct list_head ports;
+ unsigned int num_ports;
+ u32 xbar_config;
+
+ struct regulator *pex_clk_supply;
+ struct regulator *vdd_supply;
+ struct regulator *avdd_supply;
+
+ const struct tegra_pcie_soc_data *soc_data;
+};
+
+struct tegra_pcie_port {
+ struct tegra_pcie *pcie;
+ struct list_head list;
+ struct resource regs;
+ void __iomem *base;
+ unsigned int index;
+ unsigned int lanes;
+};
+
+struct tegra_pcie_bus {
+ struct vm_struct *area;
+ struct list_head list;
+ unsigned int nr;
+};
+
+static inline struct tegra_pcie *sys_to_pcie(struct pci_sys_data *sys)
+{
+ return sys->private_data;
+}
+
+static inline void afi_writel(struct tegra_pcie *pcie, u32 value,
+ unsigned long offset)
+{
+ writel(value, pcie->afi + offset);
+}
+
+static inline u32 afi_readl(struct tegra_pcie *pcie, unsigned long offset)
+{
+ return readl(pcie->afi + offset);
+}
+
+static inline void pads_writel(struct tegra_pcie *pcie, u32 value,
+ unsigned long offset)
+{
+ writel(value, pcie->pads + offset);
+}
+
+static inline u32 pads_readl(struct tegra_pcie *pcie, unsigned long offset)
+{
+ return readl(pcie->pads + offset);
+}
+
+/*
+ * The configuration space mapping on Tegra is somewhat similar to the ECAM
+ * defined by PCIe. However it deviates a bit in how the 4 bits for extended
+ * register accesses are mapped:
+ *
+ * [27:24] extended register number
+ * [23:16] bus number
+ * [15:11] device number
+ * [10: 8] function number
+ * [ 7: 0] register number
+ *
+ * Mapping the whole extended configuration space would require 256 MiB of
+ * virtual address space, only a small part of which will actually be used.
+ * To work around this, a 1 MiB of virtual addresses are allocated per bus
+ * when the bus is first accessed. When the physical range is mapped, the
+ * the bus number bits are hidden so that the extended register number bits
+ * appear as bits [19:16]. Therefore the virtual mapping looks like this:
+ *
+ * [19:16] extended register number
+ * [15:11] device number
+ * [10: 8] function number
+ * [ 7: 0] register number
+ *
+ * This is achieved by stitching together 16 chunks of 64 KiB of physical
+ * address space via the MMU.
+ */
+static unsigned long tegra_pcie_conf_offset(unsigned int devfn, int where)
+{
+ return ((where & 0xf00) << 8) | (PCI_SLOT(devfn) << 11) |
+ (PCI_FUNC(devfn) << 8) | (where & 0xfc);
+}
+
+static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
+ unsigned int busnr)
+{
+ pgprot_t prot = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_XN |
+ L_PTE_MT_DEV_SHARED | L_PTE_SHARED;
+ phys_addr_t cs = pcie->cs->start;
+ struct tegra_pcie_bus *bus;
+ unsigned int i;
+ int err;
+
+ bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&bus->list);
+ bus->nr = busnr;
+
+ /* allocate 1 MiB of virtual addresses */
+ bus->area = get_vm_area(SZ_1M, VM_IOREMAP);
+ if (!bus->area) {
+ err = -ENOMEM;
+ goto free;
+ }
+
+ /* map each of the 16 chunks of 64 KiB each */
+ for (i = 0; i < 16; i++) {
+ unsigned long virt = (unsigned long)bus->area->addr +
+ i * SZ_64K;
+ phys_addr_t phys = cs + i * SZ_1M + busnr * SZ_64K;
+
+ err = ioremap_page_range(virt, virt + SZ_64K, phys, prot);
+ if (err < 0) {
+ dev_err(pcie->dev, "ioremap_page_range() failed: %d\n",
+ err);
+ goto unmap;
+ }
+ }
+
+ return bus;
+
+unmap:
+ vunmap(bus->area->addr);
+free:
+ kfree(bus);
+ return ERR_PTR(err);
+}
+
+/*
+ * Look up a virtual address mapping for the specified bus number. If no such
+ * mapping exists, try to create one.
+ */
+static void __iomem *tegra_pcie_bus_map(struct tegra_pcie *pcie,
+ unsigned int busnr)
+{
+ struct tegra_pcie_bus *bus;
+
+ list_for_each_entry(bus, &pcie->buses, list)
+ if (bus->nr == busnr)
+ return (void __iomem *)bus->area->addr;
+
+ bus = tegra_pcie_bus_alloc(pcie, busnr);
+ if (IS_ERR(bus))
+ return NULL;
+
+ list_add_tail(&bus->list, &pcie->buses);
+
+ return (void __iomem *)bus->area->addr;
+}
+
+static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus,
+ unsigned int devfn,
+ int where)
+{
+ struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata);
+ void __iomem *addr = NULL;
+
+ if (bus->number == 0) {
+ unsigned int slot = PCI_SLOT(devfn);
+ struct tegra_pcie_port *port;
+
+ list_for_each_entry(port, &pcie->ports, list) {
+ if (port->index + 1 == slot) {
+ addr = port->base + (where & ~3);
+ break;
+ }
+ }
+ } else {
+ addr = tegra_pcie_bus_map(pcie, bus->number);
+ if (!addr) {
+ dev_err(pcie->dev,
+ "failed to map cfg. space for bus %u\n",
+ bus->number);
+ return NULL;
+ }
+
+ addr += tegra_pcie_conf_offset(devfn, where);
+ }
+
+ return addr;
+}
+
+static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *value)
+{
+ void __iomem *addr;
+
+ addr = tegra_pcie_conf_address(bus, devfn, where);
+ if (!addr) {
+ *value = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ *value = readl(addr);
+
+ if (size == 1)
+ *value = (*value >> (8 * (where & 3))) & 0xff;
+ else if (size == 2)
+ *value = (*value >> (8 * (where & 3))) & 0xffff;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 value)
+{
+ void __iomem *addr;
+ u32 mask, tmp;
+
+ addr = tegra_pcie_conf_address(bus, devfn, where);
+ if (!addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (size == 4) {
+ writel(value, addr);
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ if (size == 2)
+ mask = ~(0xffff << ((where & 0x3) * 8));
+ else if (size == 1)
+ mask = ~(0xff << ((where & 0x3) * 8));
+ else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ tmp = readl(addr) & mask;
+ tmp |= value << ((where & 0x3) * 8);
+ writel(tmp, addr);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops tegra_pcie_ops = {
+ .read = tegra_pcie_read_conf,
+ .write = tegra_pcie_write_conf,
+};
+
+static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port)
+{
+ unsigned long ret = 0;
+
+ switch (port->index) {
+ case 0:
+ ret = AFI_PEX0_CTRL;
+ break;
+
+ case 1:
+ ret = AFI_PEX1_CTRL;
+ break;
+
+ case 2:
+ ret = AFI_PEX2_CTRL;
+ break;
+ }
+
+ return ret;
+}
+
+static void tegra_pcie_port_reset(struct tegra_pcie_port *port)
+{
+ unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
+ unsigned long value;
+
+ /* pulse reset signal */
+ value = afi_readl(port->pcie, ctrl);
+ value &= ~AFI_PEX_CTRL_RST;
+ afi_writel(port->pcie, value, ctrl);
+
+ usleep_range(1000, 2000);
+
+ value = afi_readl(port->pcie, ctrl);
+ value |= AFI_PEX_CTRL_RST;
+ afi_writel(port->pcie, value, ctrl);
+}
+
+static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
+{
+ const struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
+ unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
+ unsigned long value;
+
+ /* enable reference clock */
+ value = afi_readl(port->pcie, ctrl);
+ value |= AFI_PEX_CTRL_REFCLK_EN;
+
+ if (soc->has_pex_clkreq_en)
+ value |= AFI_PEX_CTRL_CLKREQ_EN;
+
+ afi_writel(port->pcie, value, ctrl);
+
+ tegra_pcie_port_reset(port);
+}
+
+static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
+{
+ unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
+ unsigned long value;
+
+ /* assert port reset */
+ value = afi_readl(port->pcie, ctrl);
+ value &= ~AFI_PEX_CTRL_RST;
+ afi_writel(port->pcie, value, ctrl);
+
+ /* disable reference clock */
+ value = afi_readl(port->pcie, ctrl);
+ value &= ~AFI_PEX_CTRL_REFCLK_EN;
+ afi_writel(port->pcie, value, ctrl);
+}
+
+static void tegra_pcie_port_free(struct tegra_pcie_port *port)
+{
+ struct tegra_pcie *pcie = port->pcie;
+
+ devm_iounmap(pcie->dev, port->base);
+ devm_release_mem_region(pcie->dev, port->regs.start,
+ resource_size(&port->regs));
+ list_del(&port->list);
+ devm_kfree(pcie->dev, port);
+}
+
+static void tegra_pcie_fixup_bridge(struct pci_dev *dev)
+{
+ u16 reg;
+
+ if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) {
+ pci_read_config_word(dev, PCI_COMMAND, &reg);
+ reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_SERR);
+ pci_write_config_word(dev, PCI_COMMAND, reg);
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge);
+
+/* Tegra PCIE root complex wrongly reports device class */
+static void tegra_pcie_fixup_class(struct pci_dev *dev)
+{
+ dev->class = PCI_CLASS_BRIDGE_PCI << 8;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1c, tegra_pcie_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1d, tegra_pcie_fixup_class);
+
+/* Tegra PCIE requires relaxed ordering */
+static void tegra_pcie_relax_enable(struct pci_dev *dev)
+{
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
+
+static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+ struct tegra_pcie *pcie = sys_to_pcie(sys);
+
+ pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
+ pci_add_resource_offset(&sys->resources, &pcie->prefetch,
+ sys->mem_offset);
+ pci_add_resource(&sys->resources, &pcie->busn);
+
+ pci_ioremap_io(nr * SZ_64K, pcie->io.start);
+
+ return 1;
+}
+
+static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
+{
+ struct tegra_pcie *pcie = sys_to_pcie(pdev->bus->sysdata);
+ int irq;
+
+ tegra_cpuidle_pcie_irqs_in_use();
+
+ irq = of_irq_parse_and_map_pci(pdev, slot, pin);
+ if (!irq)
+ irq = pcie->irq;
+
+ return irq;
+}
+
+static void tegra_pcie_add_bus(struct pci_bus *bus)
+{
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata);
+
+ bus->msi = &pcie->msi.chip;
+ }
+}
+
+static struct pci_bus *tegra_pcie_scan_bus(int nr, struct pci_sys_data *sys)
+{
+ struct tegra_pcie *pcie = sys_to_pcie(sys);
+ struct pci_bus *bus;
+
+ bus = pci_create_root_bus(pcie->dev, sys->busnr, &tegra_pcie_ops, sys,
+ &sys->resources);
+ if (!bus)
+ return NULL;
+
+ pci_scan_child_bus(bus);
+
+ return bus;
+}
+
+static irqreturn_t tegra_pcie_isr(int irq, void *arg)
+{
+ const char *err_msg[] = {
+ "Unknown",
+ "AXI slave error",
+ "AXI decode error",
+ "Target abort",
+ "Master abort",
+ "Invalid write",
+ "Response decoding error",
+ "AXI response decoding error",
+ "Transaction timeout",
+ };
+ struct tegra_pcie *pcie = arg;
+ u32 code, signature;
+
+ code = afi_readl(pcie, AFI_INTR_CODE) & AFI_INTR_CODE_MASK;
+ signature = afi_readl(pcie, AFI_INTR_SIGNATURE);
+ afi_writel(pcie, 0, AFI_INTR_CODE);
+
+ if (code == AFI_INTR_LEGACY)
+ return IRQ_NONE;
+
+ if (code >= ARRAY_SIZE(err_msg))
+ code = 0;
+
+ /*
+ * do not pollute kernel log with master abort reports since they
+ * happen a lot during enumeration
+ */
+ if (code == AFI_INTR_MASTER_ABORT)
+ dev_dbg(pcie->dev, "%s, signature: %08x\n", err_msg[code],
+ signature);
+ else
+ dev_err(pcie->dev, "%s, signature: %08x\n", err_msg[code],
+ signature);
+
+ if (code == AFI_INTR_TARGET_ABORT || code == AFI_INTR_MASTER_ABORT ||
+ code == AFI_INTR_FPCI_DECODE_ERROR) {
+ u32 fpci = afi_readl(pcie, AFI_UPPER_FPCI_ADDRESS) & 0xff;
+ u64 address = (u64)fpci << 32 | (signature & 0xfffffffc);
+
+ if (code == AFI_INTR_MASTER_ABORT)
+ dev_dbg(pcie->dev, " FPCI address: %10llx\n", address);
+ else
+ dev_err(pcie->dev, " FPCI address: %10llx\n", address);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * FPCI map is as follows:
+ * - 0xfdfc000000: I/O space
+ * - 0xfdfe000000: type 0 configuration space
+ * - 0xfdff000000: type 1 configuration space
+ * - 0xfe00000000: type 0 extended configuration space
+ * - 0xfe10000000: type 1 extended configuration space
+ */
+static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
+{
+ u32 fpci_bar, size, axi_address;
+
+ /* Bar 0: type 1 extended configuration space */
+ fpci_bar = 0xfe100000;
+ size = resource_size(pcie->cs);
+ axi_address = pcie->cs->start;
+ afi_writel(pcie, axi_address, AFI_AXI_BAR0_START);
+ afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ);
+ afi_writel(pcie, fpci_bar, AFI_FPCI_BAR0);
+
+ /* Bar 1: downstream IO bar */
+ fpci_bar = 0xfdfc0000;
+ size = resource_size(&pcie->io);
+ axi_address = pcie->io.start;
+ afi_writel(pcie, axi_address, AFI_AXI_BAR1_START);
+ afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ);
+ afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1);
+
+ /* Bar 2: prefetchable memory BAR */
+ fpci_bar = (((pcie->prefetch.start >> 12) & 0x0fffffff) << 4) | 0x1;
+ size = resource_size(&pcie->prefetch);
+ axi_address = pcie->prefetch.start;
+ afi_writel(pcie, axi_address, AFI_AXI_BAR2_START);
+ afi_writel(pcie, size >> 12, AFI_AXI_BAR2_SZ);
+ afi_writel(pcie, fpci_bar, AFI_FPCI_BAR2);
+
+ /* Bar 3: non prefetchable memory BAR */
+ fpci_bar = (((pcie->mem.start >> 12) & 0x0fffffff) << 4) | 0x1;
+ size = resource_size(&pcie->mem);
+ axi_address = pcie->mem.start;
+ afi_writel(pcie, axi_address, AFI_AXI_BAR3_START);
+ afi_writel(pcie, size >> 12, AFI_AXI_BAR3_SZ);
+ afi_writel(pcie, fpci_bar, AFI_FPCI_BAR3);
+
+ /* NULL out the remaining BARs as they are not used */
+ afi_writel(pcie, 0, AFI_AXI_BAR4_START);
+ afi_writel(pcie, 0, AFI_AXI_BAR4_SZ);
+ afi_writel(pcie, 0, AFI_FPCI_BAR4);
+
+ afi_writel(pcie, 0, AFI_AXI_BAR5_START);
+ afi_writel(pcie, 0, AFI_AXI_BAR5_SZ);
+ afi_writel(pcie, 0, AFI_FPCI_BAR5);
+
+ /* map all upstream transactions as uncached */
+ afi_writel(pcie, PHYS_OFFSET, AFI_CACHE_BAR0_ST);
+ afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ);
+ afi_writel(pcie, 0, AFI_CACHE_BAR1_ST);
+ afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ);
+
+ /* MSI translations are setup only when needed */
+ afi_writel(pcie, 0, AFI_MSI_FPCI_BAR_ST);
+ afi_writel(pcie, 0, AFI_MSI_BAR_SZ);
+ afi_writel(pcie, 0, AFI_MSI_AXI_BAR_ST);
+ afi_writel(pcie, 0, AFI_MSI_BAR_SZ);
+}
+
+static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ struct tegra_pcie_port *port;
+ unsigned int timeout;
+ unsigned long value;
+
+ /* power down PCIe slot clock bias pad */
+ if (soc->has_pex_bias_ctrl)
+ afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
+
+ /* configure mode and disable all ports */
+ value = afi_readl(pcie, AFI_PCIE_CONFIG);
+ value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
+ value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config;
+
+ list_for_each_entry(port, &pcie->ports, list)
+ value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index);
+
+ afi_writel(pcie, value, AFI_PCIE_CONFIG);
+
+ value = afi_readl(pcie, AFI_FUSE);
+ value |= AFI_FUSE_PCIE_T0_GEN2_DIS;
+ afi_writel(pcie, value, AFI_FUSE);
+
+ /* initialize internal PHY, enable up to 16 PCIE lanes */
+ pads_writel(pcie, 0x0, PADS_CTL_SEL);
+
+ /* override IDDQ to 1 on all 4 lanes */
+ value = pads_readl(pcie, PADS_CTL);
+ value |= PADS_CTL_IDDQ_1L;
+ pads_writel(pcie, value, PADS_CTL);
+
+ /*
+ * Set up PHY PLL inputs select PLLE output as refclock,
+ * set TX ref sel to div10 (not div5).
+ */
+ value = pads_readl(pcie, soc->pads_pll_ctl);
+ value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK);
+ value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel;
+ pads_writel(pcie, value, soc->pads_pll_ctl);
+
+ /* take PLL out of reset */
+ value = pads_readl(pcie, soc->pads_pll_ctl);
+ value |= PADS_PLL_CTL_RST_B4SM;
+ pads_writel(pcie, value, soc->pads_pll_ctl);
+
+ /* Configure the reference clock driver */
+ value = PADS_REFCLK_CFG_VALUE | (PADS_REFCLK_CFG_VALUE << 16);
+ pads_writel(pcie, value, PADS_REFCLK_CFG0);
+ if (soc->num_ports > 2)
+ pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1);
+
+ /* wait for the PLL to lock */
+ timeout = 300;
+ do {
+ value = pads_readl(pcie, soc->pads_pll_ctl);
+ usleep_range(1000, 2000);
+ if (--timeout == 0) {
+ pr_err("Tegra PCIe error: timeout waiting for PLL\n");
+ return -EBUSY;
+ }
+ } while (!(value & PADS_PLL_CTL_LOCKDET));
+
+ /* turn off IDDQ override */
+ value = pads_readl(pcie, PADS_CTL);
+ value &= ~PADS_CTL_IDDQ_1L;
+ pads_writel(pcie, value, PADS_CTL);
+
+ /* enable TX/RX data */
+ value = pads_readl(pcie, PADS_CTL);
+ value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L;
+ pads_writel(pcie, value, PADS_CTL);
+
+ /* take the PCIe interface module out of reset */
+ reset_control_deassert(pcie->pcie_xrst);
+
+ /* finally enable PCIe */
+ value = afi_readl(pcie, AFI_CONFIGURATION);
+ value |= AFI_CONFIGURATION_EN_FPCI;
+ afi_writel(pcie, value, AFI_CONFIGURATION);
+
+ value = AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR |
+ AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR |
+ AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR;
+
+ if (soc->has_intr_prsnt_sense)
+ value |= AFI_INTR_EN_PRSNT_SENSE;
+
+ afi_writel(pcie, value, AFI_AFI_INTR_ENABLE);
+ afi_writel(pcie, 0xffffffff, AFI_SM_INTR_ENABLE);
+
+ /* don't enable MSI for now, only when needed */
+ afi_writel(pcie, AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK);
+
+ /* disable all exceptions */
+ afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS);
+
+ return 0;
+}
+
+static void tegra_pcie_power_off(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ int err;
+
+ /* TODO: disable and unprepare clocks? */
+
+ reset_control_assert(pcie->pcie_xrst);
+ reset_control_assert(pcie->afi_rst);
+ reset_control_assert(pcie->pex_rst);
+
+ tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
+
+ if (soc->has_avdd_supply) {
+ err = regulator_disable(pcie->avdd_supply);
+ if (err < 0)
+ dev_warn(pcie->dev,
+ "failed to disable AVDD regulator: %d\n",
+ err);
+ }
+
+ err = regulator_disable(pcie->pex_clk_supply);
+ if (err < 0)
+ dev_warn(pcie->dev, "failed to disable pex-clk regulator: %d\n",
+ err);
+
+ err = regulator_disable(pcie->vdd_supply);
+ if (err < 0)
+ dev_warn(pcie->dev, "failed to disable VDD regulator: %d\n",
+ err);
+}
+
+static int tegra_pcie_power_on(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ int err;
+
+ reset_control_assert(pcie->pcie_xrst);
+ reset_control_assert(pcie->afi_rst);
+ reset_control_assert(pcie->pex_rst);
+
+ tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
+
+ /* enable regulators */
+ err = regulator_enable(pcie->vdd_supply);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to enable VDD regulator: %d\n", err);
+ return err;
+ }
+
+ err = regulator_enable(pcie->pex_clk_supply);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to enable pex-clk regulator: %d\n",
+ err);
+ return err;
+ }
+
+ if (soc->has_avdd_supply) {
+ err = regulator_enable(pcie->avdd_supply);
+ if (err < 0) {
+ dev_err(pcie->dev,
+ "failed to enable AVDD regulator: %d\n",
+ err);
+ return err;
+ }
+ }
+
+ err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
+ pcie->pex_clk,
+ pcie->pex_rst);
+ if (err) {
+ dev_err(pcie->dev, "powerup sequence failed: %d\n", err);
+ return err;
+ }
+
+ reset_control_deassert(pcie->afi_rst);
+
+ err = clk_prepare_enable(pcie->afi_clk);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to enable AFI clock: %d\n", err);
+ return err;
+ }
+
+ if (soc->has_cml_clk) {
+ err = clk_prepare_enable(pcie->cml_clk);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to enable CML clock: %d\n",
+ err);
+ return err;
+ }
+ }
+
+ err = clk_prepare_enable(pcie->pll_e);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to enable PLLE clock: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+
+ pcie->pex_clk = devm_clk_get(pcie->dev, "pex");
+ if (IS_ERR(pcie->pex_clk))
+ return PTR_ERR(pcie->pex_clk);
+
+ pcie->afi_clk = devm_clk_get(pcie->dev, "afi");
+ if (IS_ERR(pcie->afi_clk))
+ return PTR_ERR(pcie->afi_clk);
+
+ pcie->pll_e = devm_clk_get(pcie->dev, "pll_e");
+ if (IS_ERR(pcie->pll_e))
+ return PTR_ERR(pcie->pll_e);
+
+ if (soc->has_cml_clk) {
+ pcie->cml_clk = devm_clk_get(pcie->dev, "cml");
+ if (IS_ERR(pcie->cml_clk))
+ return PTR_ERR(pcie->cml_clk);
+ }
+
+ return 0;
+}
+
+static int tegra_pcie_resets_get(struct tegra_pcie *pcie)
+{
+ pcie->pex_rst = devm_reset_control_get(pcie->dev, "pex");
+ if (IS_ERR(pcie->pex_rst))
+ return PTR_ERR(pcie->pex_rst);
+
+ pcie->afi_rst = devm_reset_control_get(pcie->dev, "afi");
+ if (IS_ERR(pcie->afi_rst))
+ return PTR_ERR(pcie->afi_rst);
+
+ pcie->pcie_xrst = devm_reset_control_get(pcie->dev, "pcie_x");
+ if (IS_ERR(pcie->pcie_xrst))
+ return PTR_ERR(pcie->pcie_xrst);
+
+ return 0;
+}
+
+static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
+{
+ struct platform_device *pdev = to_platform_device(pcie->dev);
+ struct resource *pads, *afi, *res;
+ int err;
+
+ err = tegra_pcie_clocks_get(pcie);
+ if (err) {
+ dev_err(&pdev->dev, "failed to get clocks: %d\n", err);
+ return err;
+ }
+
+ err = tegra_pcie_resets_get(pcie);
+ if (err) {
+ dev_err(&pdev->dev, "failed to get resets: %d\n", err);
+ return err;
+ }
+
+ err = tegra_pcie_power_on(pcie);
+ if (err) {
+ dev_err(&pdev->dev, "failed to power up: %d\n", err);
+ return err;
+ }
+
+ pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads");
+ pcie->pads = devm_ioremap_resource(&pdev->dev, pads);
+ if (IS_ERR(pcie->pads)) {
+ err = PTR_ERR(pcie->pads);
+ goto poweroff;
+ }
+
+ afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi");
+ pcie->afi = devm_ioremap_resource(&pdev->dev, afi);
+ if (IS_ERR(pcie->afi)) {
+ err = PTR_ERR(pcie->afi);
+ goto poweroff;
+ }
+
+ /* request configuration space, but remap later, on demand */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs");
+ if (!res) {
+ err = -EADDRNOTAVAIL;
+ goto poweroff;
+ }
+
+ pcie->cs = devm_request_mem_region(pcie->dev, res->start,
+ resource_size(res), res->name);
+ if (!pcie->cs) {
+ err = -EADDRNOTAVAIL;
+ goto poweroff;
+ }
+
+ /* request interrupt */
+ err = platform_get_irq_byname(pdev, "intr");
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ: %d\n", err);
+ goto poweroff;
+ }
+
+ pcie->irq = err;
+
+ err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register IRQ: %d\n", err);
+ goto poweroff;
+ }
+
+ return 0;
+
+poweroff:
+ tegra_pcie_power_off(pcie);
+ return err;
+}
+
+static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
+{
+ if (pcie->irq > 0)
+ free_irq(pcie->irq, pcie);
+
+ tegra_pcie_power_off(pcie);
+ return 0;
+}
+
+static int tegra_msi_alloc(struct tegra_msi *chip)
+{
+ int msi;
+
+ mutex_lock(&chip->lock);
+
+ msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR);
+ if (msi < INT_PCI_MSI_NR)
+ set_bit(msi, chip->used);
+ else
+ msi = -ENOSPC;
+
+ mutex_unlock(&chip->lock);
+
+ return msi;
+}
+
+static void tegra_msi_free(struct tegra_msi *chip, unsigned long irq)
+{
+ struct device *dev = chip->chip.dev;
+
+ mutex_lock(&chip->lock);
+
+ if (!test_bit(irq, chip->used))
+ dev_err(dev, "trying to free unused MSI#%lu\n", irq);
+ else
+ clear_bit(irq, chip->used);
+
+ mutex_unlock(&chip->lock);
+}
+
+static irqreturn_t tegra_pcie_msi_irq(int irq, void *data)
+{
+ struct tegra_pcie *pcie = data;
+ struct tegra_msi *msi = &pcie->msi;
+ unsigned int i, processed = 0;
+
+ for (i = 0; i < 8; i++) {
+ unsigned long reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4);
+
+ while (reg) {
+ unsigned int offset = find_first_bit(&reg, 32);
+ unsigned int index = i * 32 + offset;
+ unsigned int irq;
+
+ /* clear the interrupt */
+ afi_writel(pcie, 1 << offset, AFI_MSI_VEC0 + i * 4);
+
+ irq = irq_find_mapping(msi->domain, index);
+ if (irq) {
+ if (test_bit(index, msi->used))
+ generic_handle_irq(irq);
+ else
+ dev_info(pcie->dev, "unhandled MSI\n");
+ } else {
+ /*
+ * that's weird who triggered this?
+ * just clear it
+ */
+ dev_info(pcie->dev, "unexpected MSI\n");
+ }
+
+ /* see if there's any more pending in this vector */
+ reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4);
+
+ processed++;
+ }
+ }
+
+ return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
+ struct msi_desc *desc)
+{
+ struct tegra_msi *msi = to_tegra_msi(chip);
+ struct msi_msg msg;
+ unsigned int irq;
+ int hwirq;
+
+ hwirq = tegra_msi_alloc(msi);
+ if (hwirq < 0)
+ return hwirq;
+
+ irq = irq_create_mapping(msi->domain, hwirq);
+ if (!irq)
+ return -EINVAL;
+
+ irq_set_msi_desc(irq, desc);
+
+ msg.address_lo = virt_to_phys((void *)msi->pages);
+ /* 32 bit address only */
+ msg.address_hi = 0;
+ msg.data = hwirq;
+
+ write_msi_msg(irq, &msg);
+
+ return 0;
+}
+
+static void tegra_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
+{
+ struct tegra_msi *msi = to_tegra_msi(chip);
+ struct irq_data *d = irq_get_irq_data(irq);
+
+ tegra_msi_free(msi, d->hwirq);
+}
+
+static struct irq_chip tegra_msi_irq_chip = {
+ .name = "Tegra PCIe MSI",
+ .irq_enable = unmask_msi_irq,
+ .irq_disable = mask_msi_irq,
+ .irq_mask = mask_msi_irq,
+ .irq_unmask = unmask_msi_irq,
+};
+
+static int tegra_msi_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &tegra_msi_irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, domain->host_data);
+ set_irq_flags(irq, IRQF_VALID);
+
+ tegra_cpuidle_pcie_irqs_in_use();
+
+ return 0;
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+ .map = tegra_msi_map,
+};
+
+static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
+{
+ struct platform_device *pdev = to_platform_device(pcie->dev);
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ struct tegra_msi *msi = &pcie->msi;
+ unsigned long base;
+ int err;
+ u32 reg;
+
+ mutex_init(&msi->lock);
+
+ msi->chip.dev = pcie->dev;
+ msi->chip.setup_irq = tegra_msi_setup_irq;
+ msi->chip.teardown_irq = tegra_msi_teardown_irq;
+
+ msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR,
+ &msi_domain_ops, &msi->chip);
+ if (!msi->domain) {
+ dev_err(&pdev->dev, "failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ err = platform_get_irq_byname(pdev, "msi");
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ: %d\n", err);
+ goto err;
+ }
+
+ msi->irq = err;
+
+ err = request_irq(msi->irq, tegra_pcie_msi_irq, 0,
+ tegra_msi_irq_chip.name, pcie);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
+ goto err;
+ }
+
+ /* setup AFI/FPCI range */
+ msi->pages = __get_free_pages(GFP_KERNEL, 0);
+ base = virt_to_phys((void *)msi->pages);
+
+ afi_writel(pcie, base >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST);
+ afi_writel(pcie, base, AFI_MSI_AXI_BAR_ST);
+ /* this register is in 4K increments */
+ afi_writel(pcie, 1, AFI_MSI_BAR_SZ);
+
+ /* enable all MSI vectors */
+ afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC0);
+ afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC1);
+ afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC2);
+ afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC3);
+ afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC4);
+ afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC5);
+ afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC6);
+ afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC7);
+
+ /* and unmask the MSI interrupt */
+ reg = afi_readl(pcie, AFI_INTR_MASK);
+ reg |= AFI_INTR_MASK_MSI_MASK;
+ afi_writel(pcie, reg, AFI_INTR_MASK);
+
+ return 0;
+
+err:
+ irq_domain_remove(msi->domain);
+ return err;
+}
+
+static int tegra_pcie_disable_msi(struct tegra_pcie *pcie)
+{
+ struct tegra_msi *msi = &pcie->msi;
+ unsigned int i, irq;
+ u32 value;
+
+ /* mask the MSI interrupt */
+ value = afi_readl(pcie, AFI_INTR_MASK);
+ value &= ~AFI_INTR_MASK_MSI_MASK;
+ afi_writel(pcie, value, AFI_INTR_MASK);
+
+ /* disable all MSI vectors */
+ afi_writel(pcie, 0, AFI_MSI_EN_VEC0);
+ afi_writel(pcie, 0, AFI_MSI_EN_VEC1);
+ afi_writel(pcie, 0, AFI_MSI_EN_VEC2);
+ afi_writel(pcie, 0, AFI_MSI_EN_VEC3);
+ afi_writel(pcie, 0, AFI_MSI_EN_VEC4);
+ afi_writel(pcie, 0, AFI_MSI_EN_VEC5);
+ afi_writel(pcie, 0, AFI_MSI_EN_VEC6);
+ afi_writel(pcie, 0, AFI_MSI_EN_VEC7);
+
+ free_pages(msi->pages, 0);
+
+ if (msi->irq > 0)
+ free_irq(msi->irq, pcie);
+
+ for (i = 0; i < INT_PCI_MSI_NR; i++) {
+ irq = irq_find_mapping(msi->domain, i);
+ if (irq > 0)
+ irq_dispose_mapping(irq);
+ }
+
+ irq_domain_remove(msi->domain);
+
+ return 0;
+}
+
+static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
+ u32 *xbar)
+{
+ struct device_node *np = pcie->dev->of_node;
+
+ if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
+ switch (lanes) {
+ case 0x00000204:
+ dev_info(pcie->dev, "4x1, 2x1 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420;
+ return 0;
+
+ case 0x00020202:
+ dev_info(pcie->dev, "2x3 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222;
+ return 0;
+
+ case 0x00010104:
+ dev_info(pcie->dev, "4x1, 1x2 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411;
+ return 0;
+ }
+ } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
+ switch (lanes) {
+ case 0x00000004:
+ dev_info(pcie->dev, "single-mode configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE;
+ return 0;
+
+ case 0x00000202:
+ dev_info(pcie->dev, "dual-mode configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ struct device_node *np = pcie->dev->of_node, *port;
+ struct of_pci_range_parser parser;
+ struct of_pci_range range;
+ struct resource res;
+ u32 lanes = 0;
+ int err;
+
+ if (of_pci_range_parser_init(&parser, np)) {
+ dev_err(pcie->dev, "missing \"ranges\" property\n");
+ return -EINVAL;
+ }
+
+ pcie->vdd_supply = devm_regulator_get(pcie->dev, "vdd");
+ if (IS_ERR(pcie->vdd_supply))
+ return PTR_ERR(pcie->vdd_supply);
+
+ pcie->pex_clk_supply = devm_regulator_get(pcie->dev, "pex-clk");
+ if (IS_ERR(pcie->pex_clk_supply))
+ return PTR_ERR(pcie->pex_clk_supply);
+
+ if (soc->has_avdd_supply) {
+ pcie->avdd_supply = devm_regulator_get(pcie->dev, "avdd");
+ if (IS_ERR(pcie->avdd_supply))
+ return PTR_ERR(pcie->avdd_supply);
+ }
+
+ for_each_of_pci_range(&parser, &range) {
+ of_pci_range_to_resource(&range, np, &res);
+
+ switch (res.flags & IORESOURCE_TYPE_BITS) {
+ case IORESOURCE_IO:
+ memcpy(&pcie->io, &res, sizeof(res));
+ pcie->io.name = "I/O";
+ break;
+
+ case IORESOURCE_MEM:
+ if (res.flags & IORESOURCE_PREFETCH) {
+ memcpy(&pcie->prefetch, &res, sizeof(res));
+ pcie->prefetch.name = "PREFETCH";
+ } else {
+ memcpy(&pcie->mem, &res, sizeof(res));
+ pcie->mem.name = "MEM";
+ }
+ break;
+ }
+ }
+
+ err = of_pci_parse_bus_range(np, &pcie->busn);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to parse ranges property: %d\n",
+ err);
+ pcie->busn.name = np->name;
+ pcie->busn.start = 0;
+ pcie->busn.end = 0xff;
+ pcie->busn.flags = IORESOURCE_BUS;
+ }
+
+ /* parse root ports */
+ for_each_child_of_node(np, port) {
+ struct tegra_pcie_port *rp;
+ unsigned int index;
+ u32 value;
+
+ err = of_pci_get_devfn(port);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to parse address: %d\n",
+ err);
+ return err;
+ }
+
+ index = PCI_SLOT(err);
+
+ if (index < 1 || index > soc->num_ports) {
+ dev_err(pcie->dev, "invalid port number: %d\n", index);
+ return -EINVAL;
+ }
+
+ index--;
+
+ err = of_property_read_u32(port, "nvidia,num-lanes", &value);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to parse # of lanes: %d\n",
+ err);
+ return err;
+ }
+
+ if (value > 16) {
+ dev_err(pcie->dev, "invalid # of lanes: %u\n", value);
+ return -EINVAL;
+ }
+
+ lanes |= value << (index << 3);
+
+ if (!of_device_is_available(port))
+ continue;
+
+ rp = devm_kzalloc(pcie->dev, sizeof(*rp), GFP_KERNEL);
+ if (!rp)
+ return -ENOMEM;
+
+ err = of_address_to_resource(port, 0, &rp->regs);
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to parse address: %d\n",
+ err);
+ return err;
+ }
+
+ INIT_LIST_HEAD(&rp->list);
+ rp->index = index;
+ rp->lanes = value;
+ rp->pcie = pcie;
+
+ rp->base = devm_ioremap_resource(pcie->dev, &rp->regs);
+ if (IS_ERR(rp->base))
+ return PTR_ERR(rp->base);
+
+ list_add_tail(&rp->list, &pcie->ports);
+ }
+
+ err = tegra_pcie_get_xbar_config(pcie, lanes, &pcie->xbar_config);
+ if (err < 0) {
+ dev_err(pcie->dev, "invalid lane configuration\n");
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * FIXME: If there are no PCIe cards attached, then calling this function
+ * can result in the increase of the bootup time as there are big timeout
+ * loops.
+ */
+#define TEGRA_PCIE_LINKUP_TIMEOUT 200 /* up to 1.2 seconds */
+static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
+{
+ unsigned int retries = 3;
+ unsigned long value;
+
+ do {
+ unsigned int timeout = TEGRA_PCIE_LINKUP_TIMEOUT;
+
+ do {
+ value = readl(port->base + RP_VEND_XP);
+
+ if (value & RP_VEND_XP_DL_UP)
+ break;
+
+ usleep_range(1000, 2000);
+ } while (--timeout);
+
+ if (!timeout) {
+ dev_err(port->pcie->dev, "link %u down, retrying\n",
+ port->index);
+ goto retry;
+ }
+
+ timeout = TEGRA_PCIE_LINKUP_TIMEOUT;
+
+ do {
+ value = readl(port->base + RP_LINK_CONTROL_STATUS);
+
+ if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE)
+ return true;
+
+ usleep_range(1000, 2000);
+ } while (--timeout);
+
+retry:
+ tegra_pcie_port_reset(port);
+ } while (--retries);
+
+ return false;
+}
+
+static int tegra_pcie_enable(struct tegra_pcie *pcie)
+{
+ struct tegra_pcie_port *port, *tmp;
+ struct hw_pci hw;
+
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ dev_info(pcie->dev, "probing port %u, using %u lanes\n",
+ port->index, port->lanes);
+
+ tegra_pcie_port_enable(port);
+
+ if (tegra_pcie_port_check_link(port))
+ continue;
+
+ dev_info(pcie->dev, "link %u down, ignoring\n", port->index);
+
+ tegra_pcie_port_disable(port);
+ tegra_pcie_port_free(port);
+ }
+
+ memset(&hw, 0, sizeof(hw));
+
+ hw.nr_controllers = 1;
+ hw.private_data = (void **)&pcie;
+ hw.setup = tegra_pcie_setup;
+ hw.map_irq = tegra_pcie_map_irq;
+ hw.add_bus = tegra_pcie_add_bus;
+ hw.scan = tegra_pcie_scan_bus;
+ hw.ops = &tegra_pcie_ops;
+
+ pci_common_init_dev(pcie->dev, &hw);
+
+ return 0;
+}
+
+static const struct tegra_pcie_soc_data tegra20_pcie_data = {
+ .num_ports = 2,
+ .msi_base_shift = 0,
+ .pads_pll_ctl = PADS_PLL_CTL_TEGRA20,
+ .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10,
+ .has_pex_clkreq_en = false,
+ .has_pex_bias_ctrl = false,
+ .has_intr_prsnt_sense = false,
+ .has_avdd_supply = false,
+ .has_cml_clk = false,
+};
+
+static const struct tegra_pcie_soc_data tegra30_pcie_data = {
+ .num_ports = 3,
+ .msi_base_shift = 8,
+ .pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
+ .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
+ .has_pex_clkreq_en = true,
+ .has_pex_bias_ctrl = true,
+ .has_intr_prsnt_sense = true,
+ .has_avdd_supply = true,
+ .has_cml_clk = true,
+};
+
+static const struct of_device_id tegra_pcie_of_match[] = {
+ { .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie_data },
+ { .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie_data },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra_pcie_of_match);
+
+static int tegra_pcie_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct tegra_pcie *pcie;
+ int err;
+
+ match = of_match_device(tegra_pcie_of_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&pcie->buses);
+ INIT_LIST_HEAD(&pcie->ports);
+ pcie->soc_data = match->data;
+ pcie->dev = &pdev->dev;
+
+ err = tegra_pcie_parse_dt(pcie);
+ if (err < 0)
+ return err;
+
+ pcibios_min_mem = 0;
+
+ err = tegra_pcie_get_resources(pcie);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request resources: %d\n", err);
+ return err;
+ }
+
+ err = tegra_pcie_enable_controller(pcie);
+ if (err)
+ goto put_resources;
+
+ /* setup the AFI address translations */
+ tegra_pcie_setup_translations(pcie);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ err = tegra_pcie_enable_msi(pcie);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "failed to enable MSI support: %d\n",
+ err);
+ goto put_resources;
+ }
+ }
+
+ err = tegra_pcie_enable(pcie);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to enable PCIe ports: %d\n", err);
+ goto disable_msi;
+ }
+
+ platform_set_drvdata(pdev, pcie);
+ return 0;
+
+disable_msi:
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ tegra_pcie_disable_msi(pcie);
+put_resources:
+ tegra_pcie_put_resources(pcie);
+ return err;
+}
+
+static struct platform_driver tegra_pcie_driver = {
+ .driver = {
+ .name = "tegra-pcie",
+ .owner = THIS_MODULE,
+ .of_match_table = tegra_pcie_of_match,
+ .suppress_bind_attrs = true,
+ },
+ .probe = tegra_pcie_probe,
+};
+module_platform_driver(tegra_pcie_driver);
+
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
+MODULE_DESCRIPTION("NVIDIA Tegra PCIe driver");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
new file mode 100644
index 00000000000..1eaf4df3618
--- /dev/null
+++ b/drivers/pci/host/pcie-designware.c
@@ -0,0 +1,835 @@
+/*
+ * Synopsys Designware PCIe host controller driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+/* Synopsis specific PCIE configuration registers */
+#define PCIE_PORT_LINK_CONTROL 0x710
+#define PORT_LINK_MODE_MASK (0x3f << 16)
+#define PORT_LINK_MODE_1_LANES (0x1 << 16)
+#define PORT_LINK_MODE_2_LANES (0x3 << 16)
+#define PORT_LINK_MODE_4_LANES (0x7 << 16)
+
+#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
+#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
+#define PORT_LOGIC_LINK_WIDTH_MASK (0x1ff << 8)
+#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8)
+#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8)
+#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8)
+
+#define PCIE_MSI_ADDR_LO 0x820
+#define PCIE_MSI_ADDR_HI 0x824
+#define PCIE_MSI_INTR0_ENABLE 0x828
+#define PCIE_MSI_INTR0_MASK 0x82C
+#define PCIE_MSI_INTR0_STATUS 0x830
+
+#define PCIE_ATU_VIEWPORT 0x900
+#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
+#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
+#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
+#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
+#define PCIE_ATU_CR1 0x904
+#define PCIE_ATU_TYPE_MEM (0x0 << 0)
+#define PCIE_ATU_TYPE_IO (0x2 << 0)
+#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
+#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
+#define PCIE_ATU_CR2 0x908
+#define PCIE_ATU_ENABLE (0x1 << 31)
+#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
+#define PCIE_ATU_LOWER_BASE 0x90C
+#define PCIE_ATU_UPPER_BASE 0x910
+#define PCIE_ATU_LIMIT 0x914
+#define PCIE_ATU_LOWER_TARGET 0x918
+#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
+#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
+#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
+#define PCIE_ATU_UPPER_TARGET 0x91C
+
+static struct hw_pci dw_pci;
+
+static unsigned long global_io_offset;
+
+static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys)
+{
+ return sys->private_data;
+}
+
+int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val)
+{
+ *val = readl(addr);
+
+ if (size == 1)
+ *val = (*val >> (8 * (where & 3))) & 0xff;
+ else if (size == 2)
+ *val = (*val >> (8 * (where & 3))) & 0xffff;
+ else if (size != 4)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val)
+{
+ if (size == 4)
+ writel(val, addr);
+ else if (size == 2)
+ writew(val, addr + (where & 2));
+ else if (size == 1)
+ writeb(val, addr + (where & 3));
+ else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static inline void dw_pcie_readl_rc(struct pcie_port *pp, u32 reg, u32 *val)
+{
+ if (pp->ops->readl_rc)
+ pp->ops->readl_rc(pp, pp->dbi_base + reg, val);
+ else
+ *val = readl(pp->dbi_base + reg);
+}
+
+static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
+{
+ if (pp->ops->writel_rc)
+ pp->ops->writel_rc(pp, val, pp->dbi_base + reg);
+ else
+ writel(val, pp->dbi_base + reg);
+}
+
+static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
+ u32 *val)
+{
+ int ret;
+
+ if (pp->ops->rd_own_conf)
+ ret = pp->ops->rd_own_conf(pp, where, size, val);
+ else
+ ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where,
+ size, val);
+
+ return ret;
+}
+
+static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
+ u32 val)
+{
+ int ret;
+
+ if (pp->ops->wr_own_conf)
+ ret = pp->ops->wr_own_conf(pp, where, size, val);
+ else
+ ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where,
+ size, val);
+
+ return ret;
+}
+
+static struct irq_chip dw_msi_irq_chip = {
+ .name = "PCI-MSI",
+ .irq_enable = unmask_msi_irq,
+ .irq_disable = mask_msi_irq,
+ .irq_mask = mask_msi_irq,
+ .irq_unmask = unmask_msi_irq,
+};
+
+/* MSI int handler */
+irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
+{
+ unsigned long val;
+ int i, pos, irq;
+ irqreturn_t ret = IRQ_NONE;
+
+ for (i = 0; i < MAX_MSI_CTRLS; i++) {
+ dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4,
+ (u32 *)&val);
+ if (val) {
+ ret = IRQ_HANDLED;
+ pos = 0;
+ while ((pos = find_next_bit(&val, 32, pos)) != 32) {
+ irq = irq_find_mapping(pp->irq_domain,
+ i * 32 + pos);
+ dw_pcie_wr_own_conf(pp,
+ PCIE_MSI_INTR0_STATUS + i * 12,
+ 4, 1 << pos);
+ generic_handle_irq(irq);
+ pos++;
+ }
+ }
+ }
+
+ return ret;
+}
+
+void dw_pcie_msi_init(struct pcie_port *pp)
+{
+ pp->msi_data = __get_free_pages(GFP_KERNEL, 0);
+
+ /* program the msi_data */
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
+ virt_to_phys((void *)pp->msi_data));
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
+}
+
+static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0)
+{
+ int flag = 1;
+
+ do {
+ pos = find_next_zero_bit(pp->msi_irq_in_use,
+ MAX_MSI_IRQS, pos);
+ /*if you have reached to the end then get out from here.*/
+ if (pos == MAX_MSI_IRQS)
+ return -ENOSPC;
+ /*
+ * Check if this position is at correct offset.nvec is always a
+ * power of two. pos0 must be nvec bit aligned.
+ */
+ if (pos % msgvec)
+ pos += msgvec - (pos % msgvec);
+ else
+ flag = 0;
+ } while (flag);
+
+ *pos0 = pos;
+ return 0;
+}
+
+static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base,
+ unsigned int nvec, unsigned int pos)
+{
+ unsigned int i, res, bit, val;
+
+ for (i = 0; i < nvec; i++) {
+ irq_set_msi_desc_off(irq_base, i, NULL);
+ clear_bit(pos + i, pp->msi_irq_in_use);
+ /* Disable corresponding interrupt on MSI controller */
+ res = ((pos + i) / 32) * 12;
+ bit = (pos + i) % 32;
+ dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
+ val &= ~(1 << bit);
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
+ }
+}
+
+static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
+{
+ int res, bit, irq, pos0, pos1, i;
+ u32 val;
+ struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata);
+
+ if (!pp) {
+ BUG();
+ return -EINVAL;
+ }
+
+ pos0 = find_first_zero_bit(pp->msi_irq_in_use,
+ MAX_MSI_IRQS);
+ if (pos0 % no_irqs) {
+ if (find_valid_pos0(pp, no_irqs, pos0, &pos0))
+ goto no_valid_irq;
+ }
+ if (no_irqs > 1) {
+ pos1 = find_next_bit(pp->msi_irq_in_use,
+ MAX_MSI_IRQS, pos0);
+ /* there must be nvec number of consecutive free bits */
+ while ((pos1 - pos0) < no_irqs) {
+ if (find_valid_pos0(pp, no_irqs, pos1, &pos0))
+ goto no_valid_irq;
+ pos1 = find_next_bit(pp->msi_irq_in_use,
+ MAX_MSI_IRQS, pos0);
+ }
+ }
+
+ irq = irq_find_mapping(pp->irq_domain, pos0);
+ if (!irq)
+ goto no_valid_irq;
+
+ /*
+ * irq_create_mapping (called from dw_pcie_host_init) pre-allocates
+ * descs so there is no need to allocate descs here. We can therefore
+ * assume that if irq_find_mapping above returns non-zero, then the
+ * descs are also successfully allocated.
+ */
+
+ for (i = 0; i < no_irqs; i++) {
+ if (irq_set_msi_desc_off(irq, i, desc) != 0) {
+ clear_irq_range(pp, irq, i, pos0);
+ goto no_valid_irq;
+ }
+ set_bit(pos0 + i, pp->msi_irq_in_use);
+ /*Enable corresponding interrupt in MSI interrupt controller */
+ res = ((pos0 + i) / 32) * 12;
+ bit = (pos0 + i) % 32;
+ dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
+ val |= 1 << bit;
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
+ }
+
+ *pos = pos0;
+ return irq;
+
+no_valid_irq:
+ *pos = pos0;
+ return -ENOSPC;
+}
+
+static void clear_irq(unsigned int irq)
+{
+ unsigned int pos, nvec;
+ struct msi_desc *msi;
+ struct pcie_port *pp;
+ struct irq_data *data = irq_get_irq_data(irq);
+
+ /* get the port structure */
+ msi = irq_data_get_msi(data);
+ pp = sys_to_pcie(msi->dev->bus->sysdata);
+ if (!pp) {
+ BUG();
+ return;
+ }
+
+ /* undo what was done in assign_irq */
+ pos = data->hwirq;
+ nvec = 1 << msi->msi_attrib.multiple;
+
+ clear_irq_range(pp, irq, nvec, pos);
+
+ /* all irqs cleared; reset attributes */
+ msi->irq = 0;
+ msi->msi_attrib.multiple = 0;
+}
+
+static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
+ struct msi_desc *desc)
+{
+ int irq, pos, msgvec;
+ u16 msg_ctr;
+ struct msi_msg msg;
+ struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata);
+
+ if (!pp) {
+ BUG();
+ return -EINVAL;
+ }
+
+ pci_read_config_word(pdev, desc->msi_attrib.pos+PCI_MSI_FLAGS,
+ &msg_ctr);
+ msgvec = (msg_ctr&PCI_MSI_FLAGS_QSIZE) >> 4;
+ if (msgvec == 0)
+ msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1;
+ if (msgvec > 5)
+ msgvec = 0;
+
+ irq = assign_irq((1 << msgvec), desc, &pos);
+ if (irq < 0)
+ return irq;
+
+ /*
+ * write_msi_msg() will update PCI_MSI_FLAGS so there is
+ * no need to explicitly call pci_write_config_word().
+ */
+ desc->msi_attrib.multiple = msgvec;
+
+ msg.address_lo = virt_to_phys((void *)pp->msi_data);
+ msg.address_hi = 0x0;
+ msg.data = pos;
+ write_msi_msg(irq, &msg);
+
+ return 0;
+}
+
+static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
+{
+ clear_irq(irq);
+}
+
+static struct msi_chip dw_pcie_msi_chip = {
+ .setup_irq = dw_msi_setup_irq,
+ .teardown_irq = dw_msi_teardown_irq,
+};
+
+int dw_pcie_link_up(struct pcie_port *pp)
+{
+ if (pp->ops->link_up)
+ return pp->ops->link_up(pp);
+ else
+ return 0;
+}
+
+static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, domain->host_data);
+ set_irq_flags(irq, IRQF_VALID);
+
+ return 0;
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+ .map = dw_pcie_msi_map,
+};
+
+int __init dw_pcie_host_init(struct pcie_port *pp)
+{
+ struct device_node *np = pp->dev->of_node;
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ u32 val;
+ int i;
+
+ if (of_pci_range_parser_init(&parser, np)) {
+ dev_err(pp->dev, "missing ranges property\n");
+ return -EINVAL;
+ }
+
+ /* Get the I/O and memory ranges from DT */
+ for_each_of_pci_range(&parser, &range) {
+ unsigned long restype = range.flags & IORESOURCE_TYPE_BITS;
+ if (restype == IORESOURCE_IO) {
+ of_pci_range_to_resource(&range, np, &pp->io);
+ pp->io.name = "I/O";
+ pp->io.start = max_t(resource_size_t,
+ PCIBIOS_MIN_IO,
+ range.pci_addr + global_io_offset);
+ pp->io.end = min_t(resource_size_t,
+ IO_SPACE_LIMIT,
+ range.pci_addr + range.size
+ + global_io_offset);
+ pp->config.io_size = resource_size(&pp->io);
+ pp->config.io_bus_addr = range.pci_addr;
+ pp->io_base = range.cpu_addr;
+ }
+ if (restype == IORESOURCE_MEM) {
+ of_pci_range_to_resource(&range, np, &pp->mem);
+ pp->mem.name = "MEM";
+ pp->config.mem_size = resource_size(&pp->mem);
+ pp->config.mem_bus_addr = range.pci_addr;
+ }
+ if (restype == 0) {
+ of_pci_range_to_resource(&range, np, &pp->cfg);
+ pp->config.cfg0_size = resource_size(&pp->cfg)/2;
+ pp->config.cfg1_size = resource_size(&pp->cfg)/2;
+ }
+ }
+
+ if (!pp->dbi_base) {
+ pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start,
+ resource_size(&pp->cfg));
+ if (!pp->dbi_base) {
+ dev_err(pp->dev, "error with ioremap\n");
+ return -ENOMEM;
+ }
+ }
+
+ pp->cfg0_base = pp->cfg.start;
+ pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size;
+ pp->mem_base = pp->mem.start;
+
+ pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base,
+ pp->config.cfg0_size);
+ if (!pp->va_cfg0_base) {
+ dev_err(pp->dev, "error with ioremap in function\n");
+ return -ENOMEM;
+ }
+ pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base,
+ pp->config.cfg1_size);
+ if (!pp->va_cfg1_base) {
+ dev_err(pp->dev, "error with ioremap\n");
+ return -ENOMEM;
+ }
+
+ if (of_property_read_u32(np, "num-lanes", &pp->lanes)) {
+ dev_err(pp->dev, "Failed to parse the number of lanes\n");
+ return -EINVAL;
+ }
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ pp->irq_domain = irq_domain_add_linear(pp->dev->of_node,
+ MAX_MSI_IRQS, &msi_domain_ops,
+ &dw_pcie_msi_chip);
+ if (!pp->irq_domain) {
+ dev_err(pp->dev, "irq domain init failed\n");
+ return -ENXIO;
+ }
+
+ for (i = 0; i < MAX_MSI_IRQS; i++)
+ irq_create_mapping(pp->irq_domain, i);
+ }
+
+ if (pp->ops->host_init)
+ pp->ops->host_init(pp);
+
+ dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
+
+ /* program correct class for RC */
+ dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
+
+ dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
+ val |= PORT_LOGIC_SPEED_CHANGE;
+ dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
+
+ dw_pci.nr_controllers = 1;
+ dw_pci.private_data = (void **)&pp;
+
+ pci_common_init_dev(pp->dev, &dw_pci);
+ pci_assign_unassigned_resources();
+#ifdef CONFIG_PCI_DOMAINS
+ dw_pci.domain++;
+#endif
+
+ return 0;
+}
+
+static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev)
+{
+ /* Program viewport 0 : OUTBOUND : CFG0 */
+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0,
+ PCIE_ATU_VIEWPORT);
+ dw_pcie_writel_rc(pp, pp->cfg0_base, PCIE_ATU_LOWER_BASE);
+ dw_pcie_writel_rc(pp, (pp->cfg0_base >> 32), PCIE_ATU_UPPER_BASE);
+ dw_pcie_writel_rc(pp, pp->cfg0_base + pp->config.cfg0_size - 1,
+ PCIE_ATU_LIMIT);
+ dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
+ dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
+ dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG0, PCIE_ATU_CR1);
+ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+}
+
+static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev)
+{
+ /* Program viewport 1 : OUTBOUND : CFG1 */
+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1,
+ PCIE_ATU_VIEWPORT);
+ dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1);
+ dw_pcie_writel_rc(pp, pp->cfg1_base, PCIE_ATU_LOWER_BASE);
+ dw_pcie_writel_rc(pp, (pp->cfg1_base >> 32), PCIE_ATU_UPPER_BASE);
+ dw_pcie_writel_rc(pp, pp->cfg1_base + pp->config.cfg1_size - 1,
+ PCIE_ATU_LIMIT);
+ dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
+ dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
+ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+}
+
+static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp)
+{
+ /* Program viewport 0 : OUTBOUND : MEM */
+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0,
+ PCIE_ATU_VIEWPORT);
+ dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1);
+ dw_pcie_writel_rc(pp, pp->mem_base, PCIE_ATU_LOWER_BASE);
+ dw_pcie_writel_rc(pp, (pp->mem_base >> 32), PCIE_ATU_UPPER_BASE);
+ dw_pcie_writel_rc(pp, pp->mem_base + pp->config.mem_size - 1,
+ PCIE_ATU_LIMIT);
+ dw_pcie_writel_rc(pp, pp->config.mem_bus_addr, PCIE_ATU_LOWER_TARGET);
+ dw_pcie_writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr),
+ PCIE_ATU_UPPER_TARGET);
+ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+}
+
+static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp)
+{
+ /* Program viewport 1 : OUTBOUND : IO */
+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1,
+ PCIE_ATU_VIEWPORT);
+ dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1);
+ dw_pcie_writel_rc(pp, pp->io_base, PCIE_ATU_LOWER_BASE);
+ dw_pcie_writel_rc(pp, (pp->io_base >> 32), PCIE_ATU_UPPER_BASE);
+ dw_pcie_writel_rc(pp, pp->io_base + pp->config.io_size - 1,
+ PCIE_ATU_LIMIT);
+ dw_pcie_writel_rc(pp, pp->config.io_bus_addr, PCIE_ATU_LOWER_TARGET);
+ dw_pcie_writel_rc(pp, upper_32_bits(pp->config.io_bus_addr),
+ PCIE_ATU_UPPER_TARGET);
+ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+}
+
+static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+ u32 devfn, int where, int size, u32 *val)
+{
+ int ret = PCIBIOS_SUCCESSFUL;
+ u32 address, busdev;
+
+ busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
+ PCIE_ATU_FUNC(PCI_FUNC(devfn));
+ address = where & ~0x3;
+
+ if (bus->parent->number == pp->root_bus_nr) {
+ dw_pcie_prog_viewport_cfg0(pp, busdev);
+ ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size,
+ val);
+ dw_pcie_prog_viewport_mem_outbound(pp);
+ } else {
+ dw_pcie_prog_viewport_cfg1(pp, busdev);
+ ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size,
+ val);
+ dw_pcie_prog_viewport_io_outbound(pp);
+ }
+
+ return ret;
+}
+
+static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+ u32 devfn, int where, int size, u32 val)
+{
+ int ret = PCIBIOS_SUCCESSFUL;
+ u32 address, busdev;
+
+ busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
+ PCIE_ATU_FUNC(PCI_FUNC(devfn));
+ address = where & ~0x3;
+
+ if (bus->parent->number == pp->root_bus_nr) {
+ dw_pcie_prog_viewport_cfg0(pp, busdev);
+ ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size,
+ val);
+ dw_pcie_prog_viewport_mem_outbound(pp);
+ } else {
+ dw_pcie_prog_viewport_cfg1(pp, busdev);
+ ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size,
+ val);
+ dw_pcie_prog_viewport_io_outbound(pp);
+ }
+
+ return ret;
+}
+
+static int dw_pcie_valid_config(struct pcie_port *pp,
+ struct pci_bus *bus, int dev)
+{
+ /* If there is no link, then there is no device */
+ if (bus->number != pp->root_bus_nr) {
+ if (!dw_pcie_link_up(pp))
+ return 0;
+ }
+
+ /* access only one slot on each root port */
+ if (bus->number == pp->root_bus_nr && dev > 0)
+ return 0;
+
+ /*
+ * do not read more than one device on the bus directly attached
+ * to RC's (Virtual Bridge's) DS side.
+ */
+ if (bus->primary == pp->root_bus_nr && dev > 0)
+ return 0;
+
+ return 1;
+}
+
+static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+ int size, u32 *val)
+{
+ struct pcie_port *pp = sys_to_pcie(bus->sysdata);
+ int ret;
+
+ if (!pp) {
+ BUG();
+ return -EINVAL;
+ }
+
+ if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
+ *val = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ if (bus->number != pp->root_bus_nr)
+ ret = dw_pcie_rd_other_conf(pp, bus, devfn,
+ where, size, val);
+ else
+ ret = dw_pcie_rd_own_conf(pp, where, size, val);
+
+ return ret;
+}
+
+static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+ int where, int size, u32 val)
+{
+ struct pcie_port *pp = sys_to_pcie(bus->sysdata);
+ int ret;
+
+ if (!pp) {
+ BUG();
+ return -EINVAL;
+ }
+
+ if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (bus->number != pp->root_bus_nr)
+ ret = dw_pcie_wr_other_conf(pp, bus, devfn,
+ where, size, val);
+ else
+ ret = dw_pcie_wr_own_conf(pp, where, size, val);
+
+ return ret;
+}
+
+static struct pci_ops dw_pcie_ops = {
+ .read = dw_pcie_rd_conf,
+ .write = dw_pcie_wr_conf,
+};
+
+static int dw_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+ struct pcie_port *pp;
+
+ pp = sys_to_pcie(sys);
+
+ if (!pp)
+ return 0;
+
+ if (global_io_offset < SZ_1M && pp->config.io_size > 0) {
+ sys->io_offset = global_io_offset - pp->config.io_bus_addr;
+ pci_ioremap_io(global_io_offset, pp->io_base);
+ global_io_offset += SZ_64K;
+ pci_add_resource_offset(&sys->resources, &pp->io,
+ sys->io_offset);
+ }
+
+ sys->mem_offset = pp->mem.start - pp->config.mem_bus_addr;
+ pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset);
+
+ return 1;
+}
+
+static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys)
+{
+ struct pci_bus *bus;
+ struct pcie_port *pp = sys_to_pcie(sys);
+
+ if (pp) {
+ pp->root_bus_nr = sys->busnr;
+ bus = pci_scan_root_bus(pp->dev, sys->busnr, &dw_pcie_ops,
+ sys, &sys->resources);
+ } else {
+ bus = NULL;
+ BUG();
+ }
+
+ return bus;
+}
+
+static int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata);
+ int irq;
+
+ irq = of_irq_parse_and_map_pci(dev, slot, pin);
+ if (!irq)
+ irq = pp->irq;
+
+ return irq;
+}
+
+static void dw_pcie_add_bus(struct pci_bus *bus)
+{
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ struct pcie_port *pp = sys_to_pcie(bus->sysdata);
+
+ dw_pcie_msi_chip.dev = pp->dev;
+ bus->msi = &dw_pcie_msi_chip;
+ }
+}
+
+static struct hw_pci dw_pci = {
+ .setup = dw_pcie_setup,
+ .scan = dw_pcie_scan_bus,
+ .map_irq = dw_pcie_map_irq,
+ .add_bus = dw_pcie_add_bus,
+};
+
+void dw_pcie_setup_rc(struct pcie_port *pp)
+{
+ struct pcie_port_info *config = &pp->config;
+ u32 val;
+ u32 membase;
+ u32 memlimit;
+
+ /* set the number of lanes */
+ dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val);
+ val &= ~PORT_LINK_MODE_MASK;
+ switch (pp->lanes) {
+ case 1:
+ val |= PORT_LINK_MODE_1_LANES;
+ break;
+ case 2:
+ val |= PORT_LINK_MODE_2_LANES;
+ break;
+ case 4:
+ val |= PORT_LINK_MODE_4_LANES;
+ break;
+ }
+ dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
+
+ /* set link width speed control register */
+ dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, &val);
+ val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
+ switch (pp->lanes) {
+ case 1:
+ val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
+ break;
+ case 2:
+ val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
+ break;
+ case 4:
+ val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
+ break;
+ }
+ dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL);
+
+ /* setup RC BARs */
+ dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0);
+ dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1);
+
+ /* setup interrupt pins */
+ dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE, &val);
+ val &= 0xffff00ff;
+ val |= 0x00000100;
+ dw_pcie_writel_rc(pp, val, PCI_INTERRUPT_LINE);
+
+ /* setup bus numbers */
+ dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS, &val);
+ val &= 0xff000000;
+ val |= 0x00010100;
+ dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS);
+
+ /* setup memory base, memory limit */
+ membase = ((u32)pp->mem_base & 0xfff00000) >> 16;
+ memlimit = (config->mem_size + (u32)pp->mem_base) & 0xfff00000;
+ val = memlimit | membase;
+ dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE);
+
+ /* setup command register */
+ dw_pcie_readl_rc(pp, PCI_COMMAND, &val);
+ val &= 0xffff0000;
+ val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
+ dw_pcie_writel_rc(pp, val, PCI_COMMAND);
+}
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("Designware PCIe host controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
new file mode 100644
index 00000000000..77f592faa7b
--- /dev/null
+++ b/drivers/pci/host/pcie-designware.h
@@ -0,0 +1,76 @@
+/*
+ * Synopsys Designware PCIe host controller driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _PCIE_DESIGNWARE_H
+#define _PCIE_DESIGNWARE_H
+
+struct pcie_port_info {
+ u32 cfg0_size;
+ u32 cfg1_size;
+ u32 io_size;
+ u32 mem_size;
+ phys_addr_t io_bus_addr;
+ phys_addr_t mem_bus_addr;
+};
+
+/*
+ * Maximum number of MSI IRQs can be 256 per controller. But keep
+ * it 32 as of now. Probably we will never need more than 32. If needed,
+ * then increment it in multiple of 32.
+ */
+#define MAX_MSI_IRQS 32
+#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32)
+
+struct pcie_port {
+ struct device *dev;
+ u8 root_bus_nr;
+ void __iomem *dbi_base;
+ u64 cfg0_base;
+ void __iomem *va_cfg0_base;
+ u64 cfg1_base;
+ void __iomem *va_cfg1_base;
+ u64 io_base;
+ u64 mem_base;
+ struct resource cfg;
+ struct resource io;
+ struct resource mem;
+ struct pcie_port_info config;
+ int irq;
+ u32 lanes;
+ struct pcie_host_ops *ops;
+ int msi_irq;
+ struct irq_domain *irq_domain;
+ unsigned long msi_data;
+ DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
+};
+
+struct pcie_host_ops {
+ void (*readl_rc)(struct pcie_port *pp,
+ void __iomem *dbi_base, u32 *val);
+ void (*writel_rc)(struct pcie_port *pp,
+ u32 val, void __iomem *dbi_base);
+ int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val);
+ int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val);
+ int (*link_up)(struct pcie_port *pp);
+ void (*host_init)(struct pcie_port *pp);
+};
+
+int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val);
+int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val);
+irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
+void dw_pcie_msi_init(struct pcie_port *pp);
+int dw_pcie_link_up(struct pcie_port *pp);
+void dw_pcie_setup_rc(struct pcie_port *pp);
+int dw_pcie_host_init(struct pcie_port *pp);
+
+#endif /* _PCIE_DESIGNWARE_H */
diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c
new file mode 100644
index 00000000000..f7d3de32c9a
--- /dev/null
+++ b/drivers/pci/host/pcie-rcar.c
@@ -0,0 +1,1006 @@
+/*
+ * PCIe driver for Renesas R-Car SoCs
+ * Copyright (C) 2014 Renesas Electronics Europe Ltd
+ *
+ * Based on:
+ * arch/sh/drivers/pci/pcie-sh7786.c
+ * arch/sh/drivers/pci/ops-sh7786.c
+ * Copyright (C) 2009 - 2011 Paul Mundt
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define DRV_NAME "rcar-pcie"
+
+#define PCIECAR 0x000010
+#define PCIECCTLR 0x000018
+#define CONFIG_SEND_ENABLE (1 << 31)
+#define TYPE0 (0 << 8)
+#define TYPE1 (1 << 8)
+#define PCIECDR 0x000020
+#define PCIEMSR 0x000028
+#define PCIEINTXR 0x000400
+#define PCIEMSITXR 0x000840
+
+/* Transfer control */
+#define PCIETCTLR 0x02000
+#define CFINIT 1
+#define PCIETSTR 0x02004
+#define DATA_LINK_ACTIVE 1
+#define PCIEERRFR 0x02020
+#define UNSUPPORTED_REQUEST (1 << 4)
+#define PCIEMSIFR 0x02044
+#define PCIEMSIALR 0x02048
+#define MSIFE 1
+#define PCIEMSIAUR 0x0204c
+#define PCIEMSIIER 0x02050
+
+/* root port address */
+#define PCIEPRAR(x) (0x02080 + ((x) * 0x4))
+
+/* local address reg & mask */
+#define PCIELAR(x) (0x02200 + ((x) * 0x20))
+#define PCIELAMR(x) (0x02208 + ((x) * 0x20))
+#define LAM_PREFETCH (1 << 3)
+#define LAM_64BIT (1 << 2)
+#define LAR_ENABLE (1 << 1)
+
+/* PCIe address reg & mask */
+#define PCIEPARL(x) (0x03400 + ((x) * 0x20))
+#define PCIEPARH(x) (0x03404 + ((x) * 0x20))
+#define PCIEPAMR(x) (0x03408 + ((x) * 0x20))
+#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20))
+#define PAR_ENABLE (1 << 31)
+#define IO_SPACE (1 << 8)
+
+/* Configuration */
+#define PCICONF(x) (0x010000 + ((x) * 0x4))
+#define PMCAP(x) (0x010040 + ((x) * 0x4))
+#define EXPCAP(x) (0x010070 + ((x) * 0x4))
+#define VCCAP(x) (0x010100 + ((x) * 0x4))
+
+/* link layer */
+#define IDSETR1 0x011004
+#define TLCTLR 0x011048
+#define MACSR 0x011054
+#define MACCTLR 0x011058
+#define SCRAMBLE_DISABLE (1 << 27)
+
+/* R-Car H1 PHY */
+#define H1_PCIEPHYADRR 0x04000c
+#define WRITE_CMD (1 << 16)
+#define PHY_ACK (1 << 24)
+#define RATE_POS 12
+#define LANE_POS 8
+#define ADR_POS 0
+#define H1_PCIEPHYDOUTR 0x040014
+#define H1_PCIEPHYSR 0x040018
+
+#define INT_PCI_MSI_NR 32
+
+#define RCONF(x) (PCICONF(0)+(x))
+#define RPMCAP(x) (PMCAP(0)+(x))
+#define REXPCAP(x) (EXPCAP(0)+(x))
+#define RVCCAP(x) (VCCAP(0)+(x))
+
+#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24)
+#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19)
+#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16)
+
+#define PCI_MAX_RESOURCES 4
+#define MAX_NR_INBOUND_MAPS 6
+
+struct rcar_msi {
+ DECLARE_BITMAP(used, INT_PCI_MSI_NR);
+ struct irq_domain *domain;
+ struct msi_chip chip;
+ unsigned long pages;
+ struct mutex lock;
+ int irq1;
+ int irq2;
+};
+
+static inline struct rcar_msi *to_rcar_msi(struct msi_chip *chip)
+{
+ return container_of(chip, struct rcar_msi, chip);
+}
+
+/* Structure representing the PCIe interface */
+struct rcar_pcie {
+ struct device *dev;
+ void __iomem *base;
+ struct resource res[PCI_MAX_RESOURCES];
+ struct resource busn;
+ int root_bus_nr;
+ struct clk *clk;
+ struct clk *bus_clk;
+ struct rcar_msi msi;
+};
+
+static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys)
+{
+ return sys->private_data;
+}
+
+static void pci_write_reg(struct rcar_pcie *pcie, unsigned long val,
+ unsigned long reg)
+{
+ writel(val, pcie->base + reg);
+}
+
+static unsigned long pci_read_reg(struct rcar_pcie *pcie, unsigned long reg)
+{
+ return readl(pcie->base + reg);
+}
+
+enum {
+ PCI_ACCESS_READ,
+ PCI_ACCESS_WRITE,
+};
+
+static void rcar_rmw32(struct rcar_pcie *pcie, int where, u32 mask, u32 data)
+{
+ int shift = 8 * (where & 3);
+ u32 val = pci_read_reg(pcie, where & ~3);
+
+ val &= ~(mask << shift);
+ val |= data << shift;
+ pci_write_reg(pcie, val, where & ~3);
+}
+
+static u32 rcar_read_conf(struct rcar_pcie *pcie, int where)
+{
+ int shift = 8 * (where & 3);
+ u32 val = pci_read_reg(pcie, where & ~3);
+
+ return val >> shift;
+}
+
+/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
+static int rcar_pcie_config_access(struct rcar_pcie *pcie,
+ unsigned char access_type, struct pci_bus *bus,
+ unsigned int devfn, int where, u32 *data)
+{
+ int dev, func, reg, index;
+
+ dev = PCI_SLOT(devfn);
+ func = PCI_FUNC(devfn);
+ reg = where & ~3;
+ index = reg / 4;
+
+ /*
+ * While each channel has its own memory-mapped extended config
+ * space, it's generally only accessible when in endpoint mode.
+ * When in root complex mode, the controller is unable to target
+ * itself with either type 0 or type 1 accesses, and indeed, any
+ * controller initiated target transfer to its own config space
+ * result in a completer abort.
+ *
+ * Each channel effectively only supports a single device, but as
+ * the same channel <-> device access works for any PCI_SLOT()
+ * value, we cheat a bit here and bind the controller's config
+ * space to devfn 0 in order to enable self-enumeration. In this
+ * case the regular ECAR/ECDR path is sidelined and the mangled
+ * config access itself is initiated as an internal bus transaction.
+ */
+ if (pci_is_root_bus(bus)) {
+ if (dev != 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (access_type == PCI_ACCESS_READ) {
+ *data = pci_read_reg(pcie, PCICONF(index));
+ } else {
+ /* Keep an eye out for changes to the root bus number */
+ if (pci_is_root_bus(bus) && (reg == PCI_PRIMARY_BUS))
+ pcie->root_bus_nr = *data & 0xff;
+
+ pci_write_reg(pcie, *data, PCICONF(index));
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ if (pcie->root_bus_nr < 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ /* Clear errors */
+ pci_write_reg(pcie, pci_read_reg(pcie, PCIEERRFR), PCIEERRFR);
+
+ /* Set the PIO address */
+ pci_write_reg(pcie, PCIE_CONF_BUS(bus->number) | PCIE_CONF_DEV(dev) |
+ PCIE_CONF_FUNC(func) | reg, PCIECAR);
+
+ /* Enable the configuration access */
+ if (bus->parent->number == pcie->root_bus_nr)
+ pci_write_reg(pcie, CONFIG_SEND_ENABLE | TYPE0, PCIECCTLR);
+ else
+ pci_write_reg(pcie, CONFIG_SEND_ENABLE | TYPE1, PCIECCTLR);
+
+ /* Check for errors */
+ if (pci_read_reg(pcie, PCIEERRFR) & UNSUPPORTED_REQUEST)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ /* Check for master and target aborts */
+ if (rcar_read_conf(pcie, RCONF(PCI_STATUS)) &
+ (PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (access_type == PCI_ACCESS_READ)
+ *data = pci_read_reg(pcie, PCIECDR);
+ else
+ pci_write_reg(pcie, *data, PCIECDR);
+
+ /* Disable the configuration access */
+ pci_write_reg(pcie, 0, PCIECCTLR);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ struct rcar_pcie *pcie = sys_to_pcie(bus->sysdata);
+ int ret;
+
+ if ((size == 2) && (where & 1))
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ else if ((size == 4) && (where & 3))
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ ret = rcar_pcie_config_access(pcie, PCI_ACCESS_READ,
+ bus, devfn, where, val);
+ if (ret != PCIBIOS_SUCCESSFUL) {
+ *val = 0xffffffff;
+ return ret;
+ }
+
+ if (size == 1)
+ *val = (*val >> (8 * (where & 3))) & 0xff;
+ else if (size == 2)
+ *val = (*val >> (8 * (where & 2))) & 0xffff;
+
+ dev_dbg(&bus->dev, "pcie-config-read: bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08lx\n",
+ bus->number, devfn, where, size, (unsigned long)*val);
+
+ return ret;
+}
+
+/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
+static int rcar_pcie_write_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ struct rcar_pcie *pcie = sys_to_pcie(bus->sysdata);
+ int shift, ret;
+ u32 data;
+
+ if ((size == 2) && (where & 1))
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ else if ((size == 4) && (where & 3))
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ ret = rcar_pcie_config_access(pcie, PCI_ACCESS_READ,
+ bus, devfn, where, &data);
+ if (ret != PCIBIOS_SUCCESSFUL)
+ return ret;
+
+ dev_dbg(&bus->dev, "pcie-config-write: bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08lx\n",
+ bus->number, devfn, where, size, (unsigned long)val);
+
+ if (size == 1) {
+ shift = 8 * (where & 3);
+ data &= ~(0xff << shift);
+ data |= ((val & 0xff) << shift);
+ } else if (size == 2) {
+ shift = 8 * (where & 2);
+ data &= ~(0xffff << shift);
+ data |= ((val & 0xffff) << shift);
+ } else
+ data = val;
+
+ ret = rcar_pcie_config_access(pcie, PCI_ACCESS_WRITE,
+ bus, devfn, where, &data);
+
+ return ret;
+}
+
+static struct pci_ops rcar_pcie_ops = {
+ .read = rcar_pcie_read_conf,
+ .write = rcar_pcie_write_conf,
+};
+
+static void rcar_pcie_setup_window(int win, struct resource *res,
+ struct rcar_pcie *pcie)
+{
+ /* Setup PCIe address space mappings for each resource */
+ resource_size_t size;
+ u32 mask;
+
+ pci_write_reg(pcie, 0x00000000, PCIEPTCTLR(win));
+
+ /*
+ * The PAMR mask is calculated in units of 128Bytes, which
+ * keeps things pretty simple.
+ */
+ size = resource_size(res);
+ mask = (roundup_pow_of_two(size) / SZ_128) - 1;
+ pci_write_reg(pcie, mask << 7, PCIEPAMR(win));
+
+ pci_write_reg(pcie, upper_32_bits(res->start), PCIEPARH(win));
+ pci_write_reg(pcie, lower_32_bits(res->start), PCIEPARL(win));
+
+ /* First resource is for IO */
+ mask = PAR_ENABLE;
+ if (res->flags & IORESOURCE_IO)
+ mask |= IO_SPACE;
+
+ pci_write_reg(pcie, mask, PCIEPTCTLR(win));
+}
+
+static int rcar_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+ struct rcar_pcie *pcie = sys_to_pcie(sys);
+ struct resource *res;
+ int i;
+
+ pcie->root_bus_nr = -1;
+
+ /* Setup PCI resources */
+ for (i = 0; i < PCI_MAX_RESOURCES; i++) {
+
+ res = &pcie->res[i];
+ if (!res->flags)
+ continue;
+
+ rcar_pcie_setup_window(i, res, pcie);
+
+ if (res->flags & IORESOURCE_IO)
+ pci_ioremap_io(nr * SZ_64K, res->start);
+ else
+ pci_add_resource(&sys->resources, res);
+ }
+ pci_add_resource(&sys->resources, &pcie->busn);
+
+ return 1;
+}
+
+static void rcar_pcie_add_bus(struct pci_bus *bus)
+{
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ struct rcar_pcie *pcie = sys_to_pcie(bus->sysdata);
+
+ bus->msi = &pcie->msi.chip;
+ }
+}
+
+struct hw_pci rcar_pci = {
+ .setup = rcar_pcie_setup,
+ .map_irq = of_irq_parse_and_map_pci,
+ .ops = &rcar_pcie_ops,
+ .add_bus = rcar_pcie_add_bus,
+};
+
+static void rcar_pcie_enable(struct rcar_pcie *pcie)
+{
+ struct platform_device *pdev = to_platform_device(pcie->dev);
+
+ rcar_pci.nr_controllers = 1;
+ rcar_pci.private_data = (void **)&pcie;
+
+ pci_common_init_dev(&pdev->dev, &rcar_pci);
+#ifdef CONFIG_PCI_DOMAINS
+ rcar_pci.domain++;
+#endif
+}
+
+static int phy_wait_for_ack(struct rcar_pcie *pcie)
+{
+ unsigned int timeout = 100;
+
+ while (timeout--) {
+ if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK)
+ return 0;
+
+ udelay(100);
+ }
+
+ dev_err(pcie->dev, "Access to PCIe phy timed out\n");
+
+ return -ETIMEDOUT;
+}
+
+static void phy_write_reg(struct rcar_pcie *pcie,
+ unsigned int rate, unsigned int addr,
+ unsigned int lane, unsigned int data)
+{
+ unsigned long phyaddr;
+
+ phyaddr = WRITE_CMD |
+ ((rate & 1) << RATE_POS) |
+ ((lane & 0xf) << LANE_POS) |
+ ((addr & 0xff) << ADR_POS);
+
+ /* Set write data */
+ pci_write_reg(pcie, data, H1_PCIEPHYDOUTR);
+ pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR);
+
+ /* Ignore errors as they will be dealt with if the data link is down */
+ phy_wait_for_ack(pcie);
+
+ /* Clear command */
+ pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR);
+ pci_write_reg(pcie, 0, H1_PCIEPHYADRR);
+
+ /* Ignore errors as they will be dealt with if the data link is down */
+ phy_wait_for_ack(pcie);
+}
+
+static int rcar_pcie_wait_for_dl(struct rcar_pcie *pcie)
+{
+ unsigned int timeout = 10;
+
+ while (timeout--) {
+ if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE))
+ return 0;
+
+ msleep(5);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int rcar_pcie_hw_init(struct rcar_pcie *pcie)
+{
+ int err;
+
+ /* Begin initialization */
+ pci_write_reg(pcie, 0, PCIETCTLR);
+
+ /* Set mode */
+ pci_write_reg(pcie, 1, PCIEMSR);
+
+ /*
+ * Initial header for port config space is type 1, set the device
+ * class to match. Hardware takes care of propagating the IDSETR
+ * settings, so there is no need to bother with a quirk.
+ */
+ pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1);
+
+ /*
+ * Setup Secondary Bus Number & Subordinate Bus Number, even though
+ * they aren't used, to avoid bridge being detected as broken.
+ */
+ rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1);
+ rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1);
+
+ /* Initialize default capabilities. */
+ rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP);
+ rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS),
+ PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4);
+ rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f,
+ PCI_HEADER_TYPE_BRIDGE);
+
+ /* Enable data link layer active state reporting */
+ rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, PCI_EXP_LNKCAP_DLLLARC);
+
+ /* Write out the physical slot number = 0 */
+ rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0);
+
+ /* Set the completion timer timeout to the maximum 50ms. */
+ rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50);
+
+ /* Terminate list of capabilities (Next Capability Offset=0) */
+ rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0);
+
+ /* Enable MAC data scrambling. */
+ rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0);
+
+ /* Enable MSI */
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ pci_write_reg(pcie, 0x101f0000, PCIEMSITXR);
+
+ /* Finish initialization - establish a PCI Express link */
+ pci_write_reg(pcie, CFINIT, PCIETCTLR);
+
+ /* This will timeout if we don't have a link. */
+ err = rcar_pcie_wait_for_dl(pcie);
+ if (err)
+ return err;
+
+ /* Enable INTx interrupts */
+ rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8);
+
+ /* Enable slave Bus Mastering */
+ rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK,
+ PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+ PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST);
+
+ wmb();
+
+ return 0;
+}
+
+static int rcar_pcie_hw_init_h1(struct rcar_pcie *pcie)
+{
+ unsigned int timeout = 10;
+
+ /* Initialize the phy */
+ phy_write_reg(pcie, 0, 0x42, 0x1, 0x0EC34191);
+ phy_write_reg(pcie, 1, 0x42, 0x1, 0x0EC34180);
+ phy_write_reg(pcie, 0, 0x43, 0x1, 0x00210188);
+ phy_write_reg(pcie, 1, 0x43, 0x1, 0x00210188);
+ phy_write_reg(pcie, 0, 0x44, 0x1, 0x015C0014);
+ phy_write_reg(pcie, 1, 0x44, 0x1, 0x015C0014);
+ phy_write_reg(pcie, 1, 0x4C, 0x1, 0x786174A0);
+ phy_write_reg(pcie, 1, 0x4D, 0x1, 0x048000BB);
+ phy_write_reg(pcie, 0, 0x51, 0x1, 0x079EC062);
+ phy_write_reg(pcie, 0, 0x52, 0x1, 0x20000000);
+ phy_write_reg(pcie, 1, 0x52, 0x1, 0x20000000);
+ phy_write_reg(pcie, 1, 0x56, 0x1, 0x00003806);
+
+ phy_write_reg(pcie, 0, 0x60, 0x1, 0x004B03A5);
+ phy_write_reg(pcie, 0, 0x64, 0x1, 0x3F0F1F0F);
+ phy_write_reg(pcie, 0, 0x66, 0x1, 0x00008000);
+
+ while (timeout--) {
+ if (pci_read_reg(pcie, H1_PCIEPHYSR))
+ return rcar_pcie_hw_init(pcie);
+
+ msleep(5);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int rcar_msi_alloc(struct rcar_msi *chip)
+{
+ int msi;
+
+ mutex_lock(&chip->lock);
+
+ msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR);
+ if (msi < INT_PCI_MSI_NR)
+ set_bit(msi, chip->used);
+ else
+ msi = -ENOSPC;
+
+ mutex_unlock(&chip->lock);
+
+ return msi;
+}
+
+static void rcar_msi_free(struct rcar_msi *chip, unsigned long irq)
+{
+ mutex_lock(&chip->lock);
+ clear_bit(irq, chip->used);
+ mutex_unlock(&chip->lock);
+}
+
+static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)
+{
+ struct rcar_pcie *pcie = data;
+ struct rcar_msi *msi = &pcie->msi;
+ unsigned long reg;
+
+ reg = pci_read_reg(pcie, PCIEMSIFR);
+
+ /* MSI & INTx share an interrupt - we only handle MSI here */
+ if (!reg)
+ return IRQ_NONE;
+
+ while (reg) {
+ unsigned int index = find_first_bit(&reg, 32);
+ unsigned int irq;
+
+ /* clear the interrupt */
+ pci_write_reg(pcie, 1 << index, PCIEMSIFR);
+
+ irq = irq_find_mapping(msi->domain, index);
+ if (irq) {
+ if (test_bit(index, msi->used))
+ generic_handle_irq(irq);
+ else
+ dev_info(pcie->dev, "unhandled MSI\n");
+ } else {
+ /* Unknown MSI, just clear it */
+ dev_dbg(pcie->dev, "unexpected MSI\n");
+ }
+
+ /* see if there's any more pending in this vector */
+ reg = pci_read_reg(pcie, PCIEMSIFR);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rcar_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
+ struct msi_desc *desc)
+{
+ struct rcar_msi *msi = to_rcar_msi(chip);
+ struct rcar_pcie *pcie = container_of(chip, struct rcar_pcie, msi.chip);
+ struct msi_msg msg;
+ unsigned int irq;
+ int hwirq;
+
+ hwirq = rcar_msi_alloc(msi);
+ if (hwirq < 0)
+ return hwirq;
+
+ irq = irq_create_mapping(msi->domain, hwirq);
+ if (!irq) {
+ rcar_msi_free(msi, hwirq);
+ return -EINVAL;
+ }
+
+ irq_set_msi_desc(irq, desc);
+
+ msg.address_lo = pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE;
+ msg.address_hi = pci_read_reg(pcie, PCIEMSIAUR);
+ msg.data = hwirq;
+
+ write_msi_msg(irq, &msg);
+
+ return 0;
+}
+
+static void rcar_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
+{
+ struct rcar_msi *msi = to_rcar_msi(chip);
+ struct irq_data *d = irq_get_irq_data(irq);
+
+ rcar_msi_free(msi, d->hwirq);
+}
+
+static struct irq_chip rcar_msi_irq_chip = {
+ .name = "R-Car PCIe MSI",
+ .irq_enable = unmask_msi_irq,
+ .irq_disable = mask_msi_irq,
+ .irq_mask = mask_msi_irq,
+ .irq_unmask = unmask_msi_irq,
+};
+
+static int rcar_msi_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &rcar_msi_irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, domain->host_data);
+ set_irq_flags(irq, IRQF_VALID);
+
+ return 0;
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+ .map = rcar_msi_map,
+};
+
+static int rcar_pcie_enable_msi(struct rcar_pcie *pcie)
+{
+ struct platform_device *pdev = to_platform_device(pcie->dev);
+ struct rcar_msi *msi = &pcie->msi;
+ unsigned long base;
+ int err;
+
+ mutex_init(&msi->lock);
+
+ msi->chip.dev = pcie->dev;
+ msi->chip.setup_irq = rcar_msi_setup_irq;
+ msi->chip.teardown_irq = rcar_msi_teardown_irq;
+
+ msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR,
+ &msi_domain_ops, &msi->chip);
+ if (!msi->domain) {
+ dev_err(&pdev->dev, "failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ /* Two irqs are for MSI, but they are also used for non-MSI irqs */
+ err = devm_request_irq(&pdev->dev, msi->irq1, rcar_pcie_msi_irq,
+ IRQF_SHARED, rcar_msi_irq_chip.name, pcie);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
+ goto err;
+ }
+
+ err = devm_request_irq(&pdev->dev, msi->irq2, rcar_pcie_msi_irq,
+ IRQF_SHARED, rcar_msi_irq_chip.name, pcie);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
+ goto err;
+ }
+
+ /* setup MSI data target */
+ msi->pages = __get_free_pages(GFP_KERNEL, 0);
+ base = virt_to_phys((void *)msi->pages);
+
+ pci_write_reg(pcie, base | MSIFE, PCIEMSIALR);
+ pci_write_reg(pcie, 0, PCIEMSIAUR);
+
+ /* enable all MSI interrupts */
+ pci_write_reg(pcie, 0xffffffff, PCIEMSIIER);
+
+ return 0;
+
+err:
+ irq_domain_remove(msi->domain);
+ return err;
+}
+
+static int rcar_pcie_get_resources(struct platform_device *pdev,
+ struct rcar_pcie *pcie)
+{
+ struct resource res;
+ int err, i;
+
+ err = of_address_to_resource(pdev->dev.of_node, 0, &res);
+ if (err)
+ return err;
+
+ pcie->clk = devm_clk_get(&pdev->dev, "pcie");
+ if (IS_ERR(pcie->clk)) {
+ dev_err(pcie->dev, "cannot get platform clock\n");
+ return PTR_ERR(pcie->clk);
+ }
+ err = clk_prepare_enable(pcie->clk);
+ if (err)
+ goto fail_clk;
+
+ pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus");
+ if (IS_ERR(pcie->bus_clk)) {
+ dev_err(pcie->dev, "cannot get pcie bus clock\n");
+ err = PTR_ERR(pcie->bus_clk);
+ goto fail_clk;
+ }
+ err = clk_prepare_enable(pcie->bus_clk);
+ if (err)
+ goto err_map_reg;
+
+ i = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ if (i < 0) {
+ dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n");
+ err = -ENOENT;
+ goto err_map_reg;
+ }
+ pcie->msi.irq1 = i;
+
+ i = irq_of_parse_and_map(pdev->dev.of_node, 1);
+ if (i < 0) {
+ dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n");
+ err = -ENOENT;
+ goto err_map_reg;
+ }
+ pcie->msi.irq2 = i;
+
+ pcie->base = devm_ioremap_resource(&pdev->dev, &res);
+ if (IS_ERR(pcie->base)) {
+ err = PTR_ERR(pcie->base);
+ goto err_map_reg;
+ }
+
+ return 0;
+
+err_map_reg:
+ clk_disable_unprepare(pcie->bus_clk);
+fail_clk:
+ clk_disable_unprepare(pcie->clk);
+
+ return err;
+}
+
+static int rcar_pcie_inbound_ranges(struct rcar_pcie *pcie,
+ struct of_pci_range *range,
+ int *index)
+{
+ u64 restype = range->flags;
+ u64 cpu_addr = range->cpu_addr;
+ u64 cpu_end = range->cpu_addr + range->size;
+ u64 pci_addr = range->pci_addr;
+ u32 flags = LAM_64BIT | LAR_ENABLE;
+ u64 mask;
+ u64 size;
+ int idx = *index;
+
+ if (restype & IORESOURCE_PREFETCH)
+ flags |= LAM_PREFETCH;
+
+ /*
+ * If the size of the range is larger than the alignment of the start
+ * address, we have to use multiple entries to perform the mapping.
+ */
+ if (cpu_addr > 0) {
+ unsigned long nr_zeros = __ffs64(cpu_addr);
+ u64 alignment = 1ULL << nr_zeros;
+ size = min(range->size, alignment);
+ } else {
+ size = range->size;
+ }
+ /* Hardware supports max 4GiB inbound region */
+ size = min(size, 1ULL << 32);
+
+ mask = roundup_pow_of_two(size) - 1;
+ mask &= ~0xf;
+
+ while (cpu_addr < cpu_end) {
+ /*
+ * Set up 64-bit inbound regions as the range parser doesn't
+ * distinguish between 32 and 64-bit types.
+ */
+ pci_write_reg(pcie, lower_32_bits(pci_addr), PCIEPRAR(idx));
+ pci_write_reg(pcie, lower_32_bits(cpu_addr), PCIELAR(idx));
+ pci_write_reg(pcie, lower_32_bits(mask) | flags, PCIELAMR(idx));
+
+ pci_write_reg(pcie, upper_32_bits(pci_addr), PCIEPRAR(idx+1));
+ pci_write_reg(pcie, upper_32_bits(cpu_addr), PCIELAR(idx+1));
+ pci_write_reg(pcie, 0, PCIELAMR(idx+1));
+
+ pci_addr += size;
+ cpu_addr += size;
+ idx += 2;
+
+ if (idx > MAX_NR_INBOUND_MAPS) {
+ dev_err(pcie->dev, "Failed to map inbound regions!\n");
+ return -EINVAL;
+ }
+ }
+ *index = idx;
+
+ return 0;
+}
+
+static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
+ struct device_node *node)
+{
+ const int na = 3, ns = 2;
+ int rlen;
+
+ parser->node = node;
+ parser->pna = of_n_addr_cells(node);
+ parser->np = parser->pna + na + ns;
+
+ parser->range = of_get_property(node, "dma-ranges", &rlen);
+ if (!parser->range)
+ return -ENOENT;
+
+ parser->end = parser->range + rlen / sizeof(__be32);
+ return 0;
+}
+
+static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie,
+ struct device_node *np)
+{
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ int index = 0;
+ int err;
+
+ if (pci_dma_range_parser_init(&parser, np))
+ return -EINVAL;
+
+ /* Get the dma-ranges from DT */
+ for_each_of_pci_range(&parser, &range) {
+ u64 end = range.cpu_addr + range.size - 1;
+ dev_dbg(pcie->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
+ range.flags, range.cpu_addr, end, range.pci_addr);
+
+ err = rcar_pcie_inbound_ranges(pcie, &range, &index);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id rcar_pcie_of_match[] = {
+ { .compatible = "renesas,pcie-r8a7779", .data = rcar_pcie_hw_init_h1 },
+ { .compatible = "renesas,pcie-r8a7790", .data = rcar_pcie_hw_init },
+ { .compatible = "renesas,pcie-r8a7791", .data = rcar_pcie_hw_init },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rcar_pcie_of_match);
+
+static int rcar_pcie_probe(struct platform_device *pdev)
+{
+ struct rcar_pcie *pcie;
+ unsigned int data;
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ const struct of_device_id *of_id;
+ int err, win = 0;
+ int (*hw_init_fn)(struct rcar_pcie *);
+
+ pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ pcie->dev = &pdev->dev;
+ platform_set_drvdata(pdev, pcie);
+
+ /* Get the bus range */
+ if (of_pci_parse_bus_range(pdev->dev.of_node, &pcie->busn)) {
+ dev_err(&pdev->dev, "failed to parse bus-range property\n");
+ return -EINVAL;
+ }
+
+ if (of_pci_range_parser_init(&parser, pdev->dev.of_node)) {
+ dev_err(&pdev->dev, "missing ranges property\n");
+ return -EINVAL;
+ }
+
+ err = rcar_pcie_get_resources(pdev, pcie);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request resources: %d\n", err);
+ return err;
+ }
+
+ for_each_of_pci_range(&parser, &range) {
+ of_pci_range_to_resource(&range, pdev->dev.of_node,
+ &pcie->res[win++]);
+
+ if (win > PCI_MAX_RESOURCES)
+ break;
+ }
+
+ err = rcar_pcie_parse_map_dma_ranges(pcie, pdev->dev.of_node);
+ if (err)
+ return err;
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ err = rcar_pcie_enable_msi(pcie);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "failed to enable MSI support: %d\n",
+ err);
+ return err;
+ }
+ }
+
+ of_id = of_match_device(rcar_pcie_of_match, pcie->dev);
+ if (!of_id || !of_id->data)
+ return -EINVAL;
+ hw_init_fn = of_id->data;
+
+ /* Failure to get a link might just be that no cards are inserted */
+ err = hw_init_fn(pcie);
+ if (err) {
+ dev_info(&pdev->dev, "PCIe link down\n");
+ return 0;
+ }
+
+ data = pci_read_reg(pcie, MACSR);
+ dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);
+
+ rcar_pcie_enable(pcie);
+
+ return 0;
+}
+
+static struct platform_driver rcar_pcie_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = rcar_pcie_of_match,
+ .suppress_bind_attrs = true,
+ },
+ .probe = rcar_pcie_probe,
+};
+module_platform_driver(rcar_pcie_driver);
+
+MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>");
+MODULE_DESCRIPTION("Renesas R-Car PCIe driver");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/pci/hotplug-pci.c b/drivers/pci/hotplug-pci.c
index 4d4a6447840..c68366cee6b 100644
--- a/drivers/pci/hotplug-pci.c
+++ b/drivers/pci/hotplug-pci.c
@@ -1,20 +1,29 @@
/* Core PCI functionality used only by PCI hotplug */
#include <linux/pci.h>
+#include <linux/export.h>
#include "pci.h"
-
-unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus)
+int pci_hp_add_bridge(struct pci_dev *dev)
{
- unsigned int max;
-
- max = pci_scan_child_bus(bus);
+ struct pci_bus *parent = dev->bus;
+ int pass, busnr, start = parent->busn_res.start;
+ int end = parent->busn_res.end;
- /*
- * Make the discovered devices available.
- */
- pci_bus_add_devices(bus);
+ for (busnr = start; busnr <= end; busnr++) {
+ if (!pci_find_bus(pci_domain_nr(parent), busnr))
+ break;
+ }
+ if (busnr-- > end) {
+ printk(KERN_ERR "No bus number available for hot-added bridge %s\n",
+ pci_name(dev));
+ return -1;
+ }
+ for (pass = 0; pass < 2; pass++)
+ busnr = pci_scan_bridge(parent, dev, busnr, pass);
+ if (!dev->subordinate)
+ return -1;
- return max;
+ return 0;
}
-EXPORT_SYMBOL(pci_do_scan_bus);
+EXPORT_SYMBOL_GPL(pci_hp_add_bridge);
diff --git a/drivers/pci/hotplug.c b/drivers/pci/hotplug.c
deleted file mode 100644
index 2b5352a7dff..00000000000
--- a/drivers/pci/hotplug.c
+++ /dev/null
@@ -1,37 +0,0 @@
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/module.h>
-#include "pci.h"
-
-int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- struct pci_dev *pdev;
-
- if (!dev)
- return -ENODEV;
-
- pdev = to_pci_dev(dev);
- if (!pdev)
- return -ENODEV;
-
- if (add_uevent_var(env, "PCI_CLASS=%04X", pdev->class))
- return -ENOMEM;
-
- if (add_uevent_var(env, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device))
- return -ENOMEM;
-
- if (add_uevent_var(env, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor,
- pdev->subsystem_device))
- return -ENOMEM;
-
- if (add_uevent_var(env, "PCI_SLOT_NAME=%s", pci_name(pdev)))
- return -ENOMEM;
-
- if (add_uevent_var(env, "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x",
- pdev->vendor, pdev->device,
- pdev->subsystem_vendor, pdev->subsystem_device,
- (u8)(pdev->class >> 16), (u8)(pdev->class >> 8),
- (u8)(pdev->class)))
- return -ENOMEM;
- return 0;
-}
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
index 2cdd8326f13..df8caec5978 100644
--- a/drivers/pci/hotplug/Kconfig
+++ b/drivers/pci/hotplug/Kconfig
@@ -3,45 +3,20 @@
#
menuconfig HOTPLUG_PCI
- tristate "Support for PCI Hotplug"
- depends on PCI && HOTPLUG
+ bool "Support for PCI Hotplug"
+ depends on PCI && SYSFS
---help---
Say Y here if you have a motherboard with a PCI Hotplug controller.
This allows you to add and remove PCI cards while the machine is
powered up and running.
- To compile this driver as a module, choose M here: the
- module will be called pci_hotplug.
-
When in doubt, say N.
if HOTPLUG_PCI
-config HOTPLUG_PCI_FAKE
- tristate "Fake PCI Hotplug driver"
- help
- Say Y here if you want to use the fake PCI hotplug driver. It can
- be used to simulate PCI hotplug events if even if your system is
- not PCI hotplug capable.
-
- This driver will "emulate" removing PCI devices from the system.
- If the "power" file is written to with "0" then the specified PCI
- device will be completely removed from the kernel.
-
- WARNING, this does NOT turn off the power to the PCI device.
- This is a "logical" removal, not a physical or electrical
- removal.
-
- Use this module at your own risk. You have been warned!
-
- To compile this driver as a module, choose M here: the
- module will be called fakephp.
-
- When in doubt, say N.
-
config HOTPLUG_PCI_COMPAQ
tristate "Compaq PCI Hotplug driver"
- depends on X86 && PCI_BIOS && PCI_LEGACY
+ depends on X86 && PCI_BIOS
help
Say Y here if you have a motherboard with a Compaq PCI Hotplug
controller.
@@ -63,7 +38,7 @@ config HOTPLUG_PCI_COMPAQ_NVRAM
config HOTPLUG_PCI_IBM
tristate "IBM PCI Hotplug driver"
- depends on X86_IO_APIC && X86 && PCI_BIOS && PCI_LEGACY
+ depends on X86_IO_APIC && X86 && PCI_BIOS
help
Say Y here if you have a motherboard with a IBM PCI Hotplug
controller.
@@ -74,15 +49,12 @@ config HOTPLUG_PCI_IBM
When in doubt, say N.
config HOTPLUG_PCI_ACPI
- tristate "ACPI PCI Hotplug driver"
- depends on (!ACPI_DOCK && ACPI) || (ACPI_DOCK)
+ bool "ACPI PCI Hotplug driver"
+ depends on HOTPLUG_PCI=y && ((!ACPI_DOCK && ACPI) || (ACPI_DOCK))
help
Say Y here if you have a system that supports PCI Hotplug using
ACPI.
- To compile this driver as a module, choose M here: the
- module will be called acpiphp.
-
When in doubt, say N.
config HOTPLUG_PCI_ACPI_IBM
@@ -119,7 +91,7 @@ config HOTPLUG_PCI_CPCI_ZT5550
config HOTPLUG_PCI_CPCI_GENERIC
tristate "Generic port I/O CompactPCI Hotplug driver"
- depends on HOTPLUG_PCI_CPCI && X86 && PCI_LEGACY
+ depends on HOTPLUG_PCI_CPCI && X86
help
Say Y here if you have a CompactPCI system card that exposes the #ENUM
hotswap signal as a bit in a system register that can be read through
@@ -143,7 +115,7 @@ config HOTPLUG_PCI_SHPC
config HOTPLUG_PCI_RPA
tristate "RPA PCI Hotplug driver"
- depends on PPC_PSERIES && PPC64 && !HOTPLUG_PCI_FAKE
+ depends on PPC_PSERIES && EEH
help
Say Y here if you have a RPA system that supports PCI Hotplug.
@@ -161,8 +133,8 @@ config HOTPLUG_PCI_RPA_DLPAR
To compile this driver as a module, choose M here: the
module will be called rpadlpar_io.
-
- When in doubt, say N.
+
+ When in doubt, say N.
config HOTPLUG_PCI_SGI
tristate "SGI PCI Hotplug Support"
@@ -173,4 +145,15 @@ config HOTPLUG_PCI_SGI
When in doubt, say N.
+config HOTPLUG_PCI_S390
+ bool "System z PCI Hotplug Support"
+ depends on S390 && 64BIT
+ help
+ Say Y here if you want to use the System z PCI Hotplug
+ driver for PCI devices. Without this driver it is not
+ possible to access stand-by PCI functions nor to deconfigure
+ PCI functions.
+
+ When in doubt, say Y.
+
endif # HOTPLUG_PCI
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
index 9bdbe1a6688..3e6532b945c 100644
--- a/drivers/pci/hotplug/Makefile
+++ b/drivers/pci/hotplug/Makefile
@@ -5,27 +5,33 @@
obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o
obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o
obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o
-obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o
-obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o
+
+# native drivers should be linked before acpiphp in order to allow the
+# native driver to attempt to bind first. We can then fall back to
+# generic support.
+
+obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o
obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o
obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o
-obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o
obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o
obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o
obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o
obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o
+obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o
+obj-$(CONFIG_HOTPLUG_PCI_S390) += s390_pci_hpc.o
+
+# acpiphp_ibm extends acpiphp, so should be linked afterwards.
-# Link this last so it doesn't claim devices that have a real hotplug driver
-obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o
+obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o
-pci_hotplug-objs := pci_hotplug_core.o
+pci_hotplug-objs := pci_hotplug_core.o pcihp_slot.o
ifdef CONFIG_HOTPLUG_PCI_CPCI
pci_hotplug-objs += cpci_hotplug_core.o \
cpci_hotplug_pci.o
endif
ifdef CONFIG_ACPI
-pci_hotplug-objs += acpi_pcihp.o
+pci_hotplug-objs += acpi_pcihp.o
endif
cpqphp-objs := cpqphp_core.o \
@@ -55,6 +61,9 @@ pciehp-objs := pciehp_core.o \
pciehp_ctrl.o \
pciehp_pci.o \
pciehp_hpc.o
+ifdef CONFIG_ACPI
+pciehp-objs += pciehp_acpi.o
+endif
shpchp-objs := shpchp_core.o \
shpchp_ctrl.o \
diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c
index 270a33cc08f..a94d850ae22 100644
--- a/drivers/pci/hotplug/acpi_pcihp.c
+++ b/drivers/pci/hotplug/acpi_pcihp.c
@@ -30,22 +30,21 @@
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
-#include <acpi/acpi.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/actypes.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/slab.h>
#define MY_NAME "acpi_pcihp"
-#define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0)
+#define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
#define METHOD_NAME__SUN "_SUN"
-#define METHOD_NAME__HPP "_HPP"
#define METHOD_NAME_OSHP "OSHP"
-static int debug_acpi;
+static bool debug_acpi;
static acpi_status
decode_type0_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
@@ -71,7 +70,7 @@ decode_type0_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
default:
printk(KERN_WARNING
"%s: Type 0 Revision %d record not supported\n",
- __FUNCTION__, revision);
+ __func__, revision);
return AE_ERROR;
}
return AE_OK;
@@ -100,7 +99,7 @@ decode_type1_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
default:
printk(KERN_WARNING
"%s: Type 1 Revision %d record not supported\n",
- __FUNCTION__, revision);
+ __func__, revision);
return AE_ERROR;
}
return AE_OK;
@@ -142,7 +141,7 @@ decode_type2_hpx_record(union acpi_object *record, struct hotplug_params *hpx)
default:
printk(KERN_WARNING
"%s: Type 2 Revision %d record not supported\n",
- __FUNCTION__, revision);
+ __func__, revision);
return AE_ERROR;
}
return AE_OK;
@@ -203,7 +202,7 @@ acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
break;
default:
printk(KERN_ERR "%s: Type %d record not supported\n",
- __FUNCTION__, type);
+ __func__, type);
status = AE_ERROR;
goto exit;
}
@@ -216,80 +215,41 @@ acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
static acpi_status
acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
{
- acpi_status status;
- u8 nui[4];
- struct acpi_buffer ret_buf = { 0, NULL};
- struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object *ext_obj, *package;
- int i, len = 0;
-
- acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
+ acpi_status status;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *package, *fields;
+ int i;
- /* Clear the return buffer with zeros */
memset(hpp, 0, sizeof(struct hotplug_params));
- /* get _hpp */
- status = acpi_evaluate_object(handle, METHOD_NAME__HPP, NULL, &ret_buf);
- switch (status) {
- case AE_BUFFER_OVERFLOW:
- ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL);
- if (!ret_buf.pointer) {
- printk(KERN_ERR "%s:%s alloc for _HPP fail\n",
- __FUNCTION__, (char *)string.pointer);
- kfree(string.pointer);
- return AE_NO_MEMORY;
- }
- status = acpi_evaluate_object(handle, METHOD_NAME__HPP,
- NULL, &ret_buf);
- if (ACPI_SUCCESS(status))
- break;
- default:
- if (ACPI_FAILURE(status)) {
- pr_debug("%s:%s _HPP fail=0x%x\n", __FUNCTION__,
- (char *)string.pointer, status);
- kfree(string.pointer);
- return status;
- }
- }
+ status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return status;
- ext_obj = (union acpi_object *) ret_buf.pointer;
- if (ext_obj->type != ACPI_TYPE_PACKAGE) {
- printk(KERN_ERR "%s:%s _HPP obj not a package\n", __FUNCTION__,
- (char *)string.pointer);
+ package = (union acpi_object *) buffer.pointer;
+ if (package->type != ACPI_TYPE_PACKAGE ||
+ package->package.count != 4) {
status = AE_ERROR;
- goto free_and_return;
+ goto exit;
}
- len = ext_obj->package.count;
- package = (union acpi_object *) ret_buf.pointer;
- for ( i = 0; (i < len) || (i < 4); i++) {
- ext_obj = (union acpi_object *) &package->package.elements[i];
- switch (ext_obj->type) {
- case ACPI_TYPE_INTEGER:
- nui[i] = (u8)ext_obj->integer.value;
- break;
- default:
- printk(KERN_ERR "%s:%s _HPP obj type incorrect\n",
- __FUNCTION__, (char *)string.pointer);
+ fields = package->package.elements;
+ for (i = 0; i < 4; i++) {
+ if (fields[i].type != ACPI_TYPE_INTEGER) {
status = AE_ERROR;
- goto free_and_return;
+ goto exit;
}
}
hpp->t0 = &hpp->type0_data;
- hpp->t0->cache_line_size = nui[0];
- hpp->t0->latency_timer = nui[1];
- hpp->t0->enable_serr = nui[2];
- hpp->t0->enable_perr = nui[3];
-
- pr_debug(" _HPP: cache_line_size=0x%x\n", hpp->t0->cache_line_size);
- pr_debug(" _HPP: latency timer =0x%x\n", hpp->t0->latency_timer);
- pr_debug(" _HPP: enable SERR =0x%x\n", hpp->t0->enable_serr);
- pr_debug(" _HPP: enable PERR =0x%x\n", hpp->t0->enable_perr);
+ hpp->t0->revision = 1;
+ hpp->t0->cache_line_size = fields[0].integer.value;
+ hpp->t0->latency_timer = fields[1].integer.value;
+ hpp->t0->enable_serr = fields[2].integer.value;
+ hpp->t0->enable_perr = fields[3].integer.value;
-free_and_return:
- kfree(string.pointer);
- kfree(ret_buf.pointer);
+exit:
+ kfree(buffer.pointer);
return status;
}
@@ -299,7 +259,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 };
@@ -311,104 +271,207 @@ acpi_status acpi_run_oshp(acpi_handle handle)
if (ACPI_FAILURE(status))
if (status != AE_NOT_FOUND)
printk(KERN_ERR "%s:%s OSHP fails=0x%x\n",
- __FUNCTION__, (char *)string.pointer, status);
+ __func__, (char *)string.pointer, status);
else
dbg("%s:%s OSHP not found\n",
- __FUNCTION__, (char *)string.pointer);
+ __func__, (char *)string.pointer);
else
- pr_debug("%s:%s OSHP passes\n", __FUNCTION__,
+ pr_debug("%s:%s OSHP passes\n", __func__,
(char *)string.pointer);
kfree(string.pointer);
return status;
}
-EXPORT_SYMBOL_GPL(acpi_run_oshp);
-
-
-/* acpi_get_hp_params_from_firmware
+/* pci_get_hp_params
*
- * @bus - the pci_bus of the bus on which the device is newly added
+ * @dev - the pci_dev for which we want parameters
* @hpp - allocated by the caller
*/
-acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
- struct hotplug_params *hpp)
+int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
{
- acpi_status status = AE_NOT_FOUND;
+ acpi_status status;
acpi_handle handle, phandle;
- struct pci_bus *pbus = bus;
- struct pci_dev *pdev;
-
- do {
- pdev = pbus->self;
- if (!pdev) {
- handle = acpi_get_pci_rootbridge_handle(
- pci_domain_nr(pbus), pbus->number);
+ struct pci_bus *pbus;
+
+ handle = NULL;
+ for (pbus = dev->bus; pbus; pbus = pbus->parent) {
+ handle = acpi_pci_get_bridge_handle(pbus);
+ if (handle)
break;
- }
- handle = DEVICE_ACPI_HANDLE(&(pdev->dev));
- pbus = pbus->parent;
- } while (!handle);
+ }
/*
* _HPP settings apply to all child buses, until another _HPP is
* encountered. If we don't find an _HPP for the input pci dev,
* look for it in the parent device scope since that would apply to
- * this pci dev. If we don't find any _HPP, use hardcoded defaults
+ * this pci dev.
*/
while (handle) {
status = acpi_run_hpx(handle, hpp);
if (ACPI_SUCCESS(status))
- break;
+ return 0;
status = acpi_run_hpp(handle, hpp);
if (ACPI_SUCCESS(status))
- break;
- if (acpi_root_bridge(handle))
+ return 0;
+ if (acpi_is_root_bridge(handle))
break;
status = acpi_get_parent(handle, &phandle);
if (ACPI_FAILURE(status))
break;
handle = phandle;
}
- return status;
+ return -ENODEV;
}
-EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware);
+EXPORT_SYMBOL_GPL(pci_get_hp_params);
-
-/* acpi_root_bridge - check to see if this acpi object is a root bridge
+/**
+ * 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
*
- * @handle - the acpi object in question.
+ * Attempt to take hotplug control from firmware.
*/
-int acpi_root_bridge(acpi_handle handle)
+int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
{
acpi_status status;
- struct acpi_device_info *info;
- struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
- int i;
+ acpi_handle chandle, handle;
+ struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
- status = acpi_get_object_info(handle, &buffer);
- if (ACPI_SUCCESS(status)) {
- info = buffer.pointer;
- if ((info->valid & ACPI_VALID_HID) &&
- !strcmp(PCI_ROOT_HID_STRING,
- info->hardware_id.value)) {
- kfree(buffer.pointer);
- return 1;
- }
- if (info->valid & ACPI_VALID_CID) {
- for (i=0; i < info->compatibility_id.count; i++) {
- if (!strcmp(PCI_ROOT_HID_STRING,
- info->compatibility_id.id[i].value)) {
- kfree(buffer.pointer);
- return 1;
- }
- }
+ flags &= OSC_PCI_SHPC_NATIVE_HP_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,
+ * up to 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 = acpi_pci_osc_control_set(handle, &flags, flags);
+ if (ACPI_SUCCESS(status))
+ goto got_one;
+ if (status == AE_SUPPORT)
+ goto no_control;
+ kfree(string.pointer);
+ string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
+ }
+
+ handle = ACPI_HANDLE(&pdev->dev);
+ if (!handle) {
+ /*
+ * This hotplug controller was not listed in the ACPI name
+ * space at all. Try to get acpi handle of parent pci bus.
+ */
+ struct pci_bus *pbus;
+ for (pbus = pdev->bus; pbus; pbus = pbus->parent) {
+ handle = acpi_pci_get_bridge_handle(pbus);
+ if (handle)
+ break;
}
- kfree(buffer.pointer);
}
+
+ 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_is_root_bridge(handle))
+ break;
+ chandle = handle;
+ status = acpi_get_parent(chandle, &handle);
+ if (ACPI_FAILURE(status))
+ break;
+ }
+no_control:
+ dbg("Cannot get control of hotplug hardware for pci %s\n",
+ pci_name(pdev));
+ kfree(string.pointer);
+ return -ENODEV;
+got_one:
+ dbg("Gained control for hotplug HW for pci %s (%s)\n",
+ pci_name(pdev), (char *)string.pointer);
+ kfree(string.pointer);
+ return 0;
+}
+EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
+
+static int pcihp_is_ejectable(acpi_handle handle)
+{
+ acpi_status status;
+ unsigned long long removable;
+ if (!acpi_has_method(handle, "_ADR"))
+ return 0;
+ if (acpi_has_method(handle, "_EJ0"))
+ return 1;
+ status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable);
+ if (ACPI_SUCCESS(status) && removable)
+ return 1;
return 0;
}
-EXPORT_SYMBOL_GPL(acpi_root_bridge);
+
+/**
+ * acpi_pcihp_check_ejectable - check if handle is ejectable ACPI PCI slot
+ * @pbus: the PCI bus of the PCI slot corresponding to 'handle'
+ * @handle: ACPI handle to check
+ *
+ * Return 1 if handle is ejectable PCI slot, 0 otherwise.
+ */
+int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle)
+{
+ acpi_handle bridge_handle, parent_handle;
+
+ if (!(bridge_handle = acpi_pci_get_bridge_handle(pbus)))
+ return 0;
+ if ((ACPI_FAILURE(acpi_get_parent(handle, &parent_handle))))
+ return 0;
+ if (bridge_handle != parent_handle)
+ return 0;
+ return pcihp_is_ejectable(handle);
+}
+EXPORT_SYMBOL_GPL(acpi_pci_check_ejectable);
+
+static acpi_status
+check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ int *found = (int *)context;
+ if (pcihp_is_ejectable(handle)) {
+ *found = 1;
+ return AE_CTRL_TERMINATE;
+ }
+ return AE_OK;
+}
+
+/**
+ * acpi_pci_detect_ejectable - check if the PCI bus has ejectable slots
+ * @handle - handle of the PCI bus to scan
+ *
+ * Returns 1 if the PCI bus has ACPI based ejectable slots, 0 otherwise.
+ */
+int acpi_pci_detect_ejectable(acpi_handle handle)
+{
+ int found = 0;
+
+ if (!handle)
+ return found;
+
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+ check_hotplug, NULL, (void *)&found, NULL);
+ return found;
+}
+EXPORT_SYMBOL_GPL(acpi_pci_detect_ejectable);
module_param(debug_acpi, bool, 0644);
MODULE_PARM_DESC(debug_acpi, "Debugging mode for ACPI enabled or not");
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index 7a29164d4b3..b0e61bf261a 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -36,23 +36,10 @@
#define _ACPIPHP_H
#include <linux/acpi.h>
-#include <linux/kobject.h> /* for KOBJ_NAME_LEN */
#include <linux/mutex.h>
#include <linux/pci_hotplug.h>
-#define dbg(format, arg...) \
- do { \
- if (acpiphp_debug) \
- printk(KERN_DEBUG "%s: " format, \
- MY_NAME , ## arg); \
- } while (0)
-#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
-#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
-#define 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} */
-
+struct acpiphp_context;
struct acpiphp_bridge;
struct acpiphp_slot;
@@ -63,9 +50,14 @@ struct slot {
struct hotplug_slot *hotplug_slot;
struct acpiphp_slot *acpi_slot;
struct hotplug_slot_info info;
- char name[SLOT_NAME_SIZE];
+ unsigned int sun; /* ACPI _SUN (Slot User Number) value */
};
+static inline const char *slot_name(struct slot *slot)
+{
+ return hotplug_slot_name(slot->hotplug_slot);
+}
+
/*
* struct acpiphp_bridge - PCI bridge information
*
@@ -73,27 +65,20 @@ struct slot {
*/
struct acpiphp_bridge {
struct list_head list;
- acpi_handle handle;
- struct acpiphp_slot *slots;
+ struct list_head slots;
+ struct kref ref;
- /* Ejectable PCI-to-PCI bridge (PCI bridge and PCI function) */
- struct acpiphp_func *func;
+ struct acpiphp_context *context;
- int type;
int nr_slots;
- u32 flags;
-
/* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */
struct pci_bus *pci_bus;
/* PCI-to-PCI bridge device */
struct pci_dev *pci_dev;
- /* ACPI 2.0 _HPP parameters */
- struct hotplug_params hpp;
-
- spinlock_t res_lock;
+ bool is_going_away;
};
@@ -103,16 +88,13 @@ struct acpiphp_bridge {
* PCI slot information for each *physical* PCI slot
*/
struct acpiphp_slot {
- struct acpiphp_slot *next;
- struct acpiphp_bridge *bridge; /* parent */
+ struct list_head node;
+ struct pci_bus *bus;
struct list_head funcs; /* one slot may have different
objects (i.e. for each function) */
struct slot *slot;
- struct mutex crit_sect;
u8 device; /* pci device# */
-
- u32 sun; /* ACPI _SUN (slot unique number) */
u32 flags; /* see below */
};
@@ -124,18 +106,52 @@ struct acpiphp_slot {
* typically 8 objects per slot (i.e. for each PCI function)
*/
struct acpiphp_func {
- struct acpiphp_slot *slot; /* parent */
- struct acpiphp_bridge *bridge; /* Ejectable PCI-to-PCI bridge */
+ struct acpiphp_bridge *parent;
+ struct acpiphp_slot *slot;
struct list_head sibling;
- struct pci_dev *pci_dev;
- struct notifier_block nb;
- acpi_handle handle;
u8 function; /* pci function# */
u32 flags; /* see below */
};
+struct acpiphp_context {
+ struct acpi_hotplug_context hp;
+ struct acpiphp_func func;
+ struct acpiphp_bridge *bridge;
+ unsigned int refcount;
+};
+
+static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp)
+{
+ return container_of(hp, struct acpiphp_context, hp);
+}
+
+static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
+{
+ return container_of(func, struct acpiphp_context, func);
+}
+
+static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
+{
+ return func_to_context(func)->hp.self;
+}
+
+static inline acpi_handle func_to_handle(struct acpiphp_func *func)
+{
+ return func_to_acpi_device(func)->handle;
+}
+
+struct acpiphp_root_context {
+ struct acpi_hotplug_context hp;
+ struct acpiphp_bridge *root_bridge;
+};
+
+static inline struct acpiphp_root_context *to_acpiphp_root_context(struct acpi_hotplug_context *hp)
+{
+ return container_of(hp, struct acpiphp_root_context, hp);
+}
+
/*
* struct acpiphp_attention_info - device specific attention registration
*
@@ -149,75 +165,38 @@ struct acpiphp_attention_info
struct module *owner;
};
-struct acpiphp_ioapic {
- struct pci_dev *dev;
- u32 gsi_base;
- struct list_head list;
-};
-
-/* PCI bus bridge HID */
-#define ACPI_PCI_HOST_HID "PNP0A03"
-
-/* PCI BRIDGE type */
-#define BRIDGE_TYPE_HOST 0
-#define BRIDGE_TYPE_P2P 1
-
/* ACPI _STA method value (ignore bit 4; battery present) */
-#define ACPI_STA_PRESENT (0x00000001)
-#define ACPI_STA_ENABLED (0x00000002)
-#define ACPI_STA_SHOW_IN_UI (0x00000004)
-#define ACPI_STA_FUNCTIONING (0x00000008)
#define ACPI_STA_ALL (0x0000000f)
-/* bridge flags */
-#define BRIDGE_HAS_STA (0x00000001)
-#define BRIDGE_HAS_EJ0 (0x00000002)
-#define BRIDGE_HAS_HPP (0x00000004)
-#define BRIDGE_HAS_PS0 (0x00000010)
-#define BRIDGE_HAS_PS1 (0x00000020)
-#define BRIDGE_HAS_PS2 (0x00000040)
-#define BRIDGE_HAS_PS3 (0x00000080)
-
/* slot flags */
-#define SLOT_POWEREDON (0x00000001)
-#define SLOT_ENABLED (0x00000002)
-#define SLOT_MULTIFUNCTION (0x00000004)
+#define SLOT_ENABLED (0x00000001)
+#define SLOT_IS_GOING_AWAY (0x00000002)
/* function flags */
#define FUNC_HAS_STA (0x00000001)
#define FUNC_HAS_EJ0 (0x00000002)
-#define FUNC_HAS_PS0 (0x00000010)
-#define FUNC_HAS_PS1 (0x00000020)
-#define FUNC_HAS_PS2 (0x00000040)
-#define FUNC_HAS_PS3 (0x00000080)
-#define FUNC_HAS_DCK (0x00000100)
/* function prototypes */
/* acpiphp_core.c */
-extern int acpiphp_register_attention(struct acpiphp_attention_info*info);
-extern int acpiphp_unregister_attention(struct acpiphp_attention_info *info);
-extern int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot);
-extern void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);
+int acpiphp_register_attention(struct acpiphp_attention_info*info);
+int acpiphp_unregister_attention(struct acpiphp_attention_info *info);
+int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot, unsigned int sun);
+void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);
/* acpiphp_glue.c */
-extern int acpiphp_glue_init (void);
-extern void acpiphp_glue_exit (void);
-extern int acpiphp_get_num_slots (void);
typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);
-extern int acpiphp_enable_slot (struct acpiphp_slot *slot);
-extern int acpiphp_disable_slot (struct acpiphp_slot *slot);
-extern int acpiphp_eject_slot (struct acpiphp_slot *slot);
-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);
+int acpiphp_enable_slot(struct acpiphp_slot *slot);
+int acpiphp_disable_slot(struct acpiphp_slot *slot);
+u8 acpiphp_get_power_status(struct acpiphp_slot *slot);
+u8 acpiphp_get_attention_status(struct acpiphp_slot *slot);
+u8 acpiphp_get_latch_status(struct acpiphp_slot *slot);
+u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot);
/* variables */
-extern int acpiphp_debug;
+extern bool acpiphp_disabled;
#endif /* _ACPIPHP_H */
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
index 9279d5ba62e..e291efcd02a 100644
--- a/drivers/pci/hotplug/acpiphp_core.c
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -31,24 +31,26 @@
*
*/
+#define pr_fmt(fmt) "acpiphp: " fmt
+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/pci.h>
+#include <linux/pci-acpi.h>
#include <linux/pci_hotplug.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include "acpiphp.h"
-#define MY_NAME "acpiphp"
+/* name size which is used for entries in pcihpfs */
+#define SLOT_NAME_SIZE 21 /* {_SUN} */
-static int debug;
-int acpiphp_debug;
+bool acpiphp_disabled;
/* local variables */
-static int num_slots;
static struct acpiphp_attention_info *attention_info;
#define DRIVER_VERSION "0.5"
@@ -58,24 +60,18 @@ static struct acpiphp_attention_info *attention_info;
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
-module_param(debug, bool, 0644);
-
-/* export the attention callback registration methods */
-EXPORT_SYMBOL_GPL(acpiphp_register_attention);
-EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
+MODULE_PARM_DESC(disable, "disable acpiphp driver");
+module_param_named(disable, acpiphp_disabled, bool, 0444);
static int enable_slot (struct hotplug_slot *slot);
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);
static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
- .owner = THIS_MODULE,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.set_attention_status = set_attention_status,
@@ -83,10 +79,8 @@ 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,
};
-
/**
* acpiphp_register_attention - set attention LED callback
* @info: must be completely filled with LED callbacks
@@ -106,6 +100,7 @@ int acpiphp_register_attention(struct acpiphp_attention_info *info)
}
return retval;
}
+EXPORT_SYMBOL_GPL(acpiphp_register_attention);
/**
@@ -113,7 +108,7 @@ int acpiphp_register_attention(struct acpiphp_attention_info *info)
* @info: must match the pointer used to register
*
* Description: This is used to un-register a hardware specific acpi
- * driver that manipulates the attention LED. The pointer to the
+ * driver that manipulates the attention LED. The pointer to the
* info struct must be the same as the one used to set it.
*/
int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
@@ -126,6 +121,7 @@ int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
}
return retval;
}
+EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
/**
@@ -138,7 +134,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
/* enable the specified slot */
return acpiphp_enable_slot(slot->acpi_slot);
@@ -154,15 +150,11 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
static int disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
- int retval;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
/* disable the specified slot */
- retval = acpiphp_disable_slot(slot->acpi_slot);
- if (!retval)
- retval = acpiphp_eject_slot(slot->acpi_slot);
- return retval;
+ return acpiphp_disable_slot(slot->acpi_slot);
}
@@ -175,20 +167,21 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
* was registered with us. This allows hardware specific
* ACPI implementations to blink the light for us.
*/
- static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
- {
+static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
+{
int retval = -ENODEV;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
+ pr_debug("%s - physical_slot = %s\n", __func__,
+ hotplug_slot_name(hotplug_slot));
+
if (attention_info && try_module_get(attention_info->owner)) {
retval = attention_info->set_attn(hotplug_slot, status);
module_put(attention_info->owner);
} else
attention_info = NULL;
return retval;
- }
-
+}
+
/**
* get_power_status - get power status of a slot
@@ -202,7 +195,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
*value = acpiphp_get_power_status(slot->acpi_slot);
@@ -224,7 +217,8 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
int retval = -EINVAL;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ pr_debug("%s - physical_slot = %s\n", __func__,
+ hotplug_slot_name(hotplug_slot));
if (attention_info && try_module_get(attention_info->owner)) {
retval = attention_info->get_attn(hotplug_slot, value);
@@ -247,7 +241,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
*value = acpiphp_get_latch_status(slot->acpi_slot);
@@ -267,49 +261,13 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
*value = acpiphp_get_adapter_status(slot->acpi_slot);
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", __FUNCTION__, hotplug_slot->name);
-
- *value = acpiphp_get_address(slot->acpi_slot);
-
- return 0;
-}
-
-static int __init init_acpi(void)
-{
- int retval;
-
- /* initialize internal data structure etc. */
- retval = acpiphp_glue_init();
-
- /* read initial number of slots */
- if (!retval) {
- num_slots = acpiphp_get_num_slots();
- if (num_slots == 0) {
- acpiphp_glue_exit();
- retval = -ENODEV;
- }
- }
-
- return retval;
-}
-
/**
* release_slot - free up the memory used by a slot
* @hotplug_slot: slot to free
@@ -318,17 +276,19 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
kfree(slot->hotplug_slot);
kfree(slot);
}
/* callback routine to initialize 'struct slot' for each slot */
-int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
+int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
+ unsigned int sun)
{
struct slot *slot;
int retval = -ENOMEM;
+ char name[SLOT_NAME_SIZE];
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
if (!slot)
@@ -340,8 +300,6 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
slot->hotplug_slot->info = &slot->info;
- slot->hotplug_slot->name = slot->name;
-
slot->hotplug_slot->private = slot;
slot->hotplug_slot->release = &release_slot;
slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
@@ -351,19 +309,21 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
slot->hotplug_slot->info->attention_status = 0;
slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
- slot->hotplug_slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
- slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
acpiphp_slot->slot = slot;
- snprintf(slot->name, sizeof(slot->name), "%u", slot->acpi_slot->sun);
+ slot->sun = sun;
+ snprintf(name, SLOT_NAME_SIZE, "%u", sun);
- retval = pci_hp_register(slot->hotplug_slot);
+ retval = pci_hp_register(slot->hotplug_slot, acpiphp_slot->bus,
+ acpiphp_slot->device, name);
+ if (retval == -EBUSY)
+ goto error_hpslot;
if (retval) {
- err("pci_hp_register failed with error %d\n", retval);
+ pr_err("pci_hp_register failed with error %d\n", retval);
goto error_hpslot;
- }
+ }
- info("Slot [%s] registered\n", slot->hotplug_slot->name);
+ pr_info("Slot [%s] registered\n", slot_name(slot));
return 0;
error_hpslot:
@@ -380,36 +340,17 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
struct slot *slot = acpiphp_slot->slot;
int retval = 0;
- info ("Slot [%s] unregistered\n", slot->hotplug_slot->name);
+ pr_info("Slot [%s] unregistered\n", slot_name(slot));
retval = pci_hp_deregister(slot->hotplug_slot);
if (retval)
- err("pci_hp_deregister failed with error %d\n", retval);
-}
-
-
-static int __init acpiphp_init(void)
-{
- info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
-
- if (acpi_pci_disabled)
- return 0;
-
- acpiphp_debug = debug;
-
- /* read all the ACPI info from the system */
- return init_acpi();
+ pr_err("pci_hp_deregister failed with error %d\n", retval);
}
-static void __exit acpiphp_exit(void)
+void __init acpiphp_init(void)
{
- if (acpi_pci_disabled)
- return;
-
- /* deallocate internal data structures etc. */
- acpiphp_glue_exit();
+ pr_info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n",
+ acpiphp_disabled ? ", disabled by user; please report a bug"
+ : "");
}
-
-module_init(acpiphp_init);
-module_exit(acpiphp_exit);
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 5e50008d118..602d153c705 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -32,965 +32,392 @@
/*
* Lifetime rules for pci_dev:
- * - The one in acpiphp_func has its refcount elevated by pci_get_slot()
- * when the driver is loaded or when an insertion event occurs. It loses
- * a refcount when its ejected or the driver unloads.
* - The one in acpiphp_bridge has its refcount elevated by pci_get_slot()
* when the bridge is scanned and it loses a refcount when the bridge
* is removed.
+ * - When a P2P bridge is present, we elevate the refcount on the subordinate
+ * bus. It loses the refcount when the the driver unloads.
*/
-#include <linux/init.h>
+#define pr_fmt(fmt) "acpiphp_glue: " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
+#include <linux/pci-acpi.h>
+#include <linux/pm_runtime.h>
#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
#include "../pci.h"
#include "acpiphp.h"
static LIST_HEAD(bridge_list);
-static LIST_HEAD(ioapic_list);
-static DEFINE_SPINLOCK(ioapic_list_lock);
-
-#define MY_NAME "acpiphp_glue"
+static DEFINE_MUTEX(bridge_mutex);
-static void handle_hotplug_event_bridge (acpi_handle, u32, void *);
+static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type);
+static void acpiphp_post_dock_fixup(struct acpi_device *adev);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
-static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus);
-static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context);
-
-
-/*
- * initialization & terminatation routines
- */
+static void acpiphp_set_hpp_values(struct pci_bus *bus);
+static void hotplug_event(u32 type, struct acpiphp_context *context);
+static void free_bridge(struct kref *kref);
/**
- * is_ejectable - determine if a slot is ejectable
- * @handle: handle to acpi namespace
- *
- * Ejectable slot should satisfy at least these conditions:
- *
- * 1. has _ADR method
- * 2. has _EJ0 method
+ * acpiphp_init_context - Create hotplug context and grab a reference to it.
+ * @adev: ACPI device object to create the context for.
*
- * optionally
- *
- * 1. has _STA method
- * 2. has _PS0 method
- * 3. has _PS3 method
- * 4. ..
+ * Call under acpi_hp_context_lock.
*/
-static int is_ejectable(acpi_handle handle)
+static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
- acpi_status status;
- acpi_handle tmp;
+ struct acpiphp_context *context;
- status = acpi_get_handle(handle, "_ADR", &tmp);
- if (ACPI_FAILURE(status)) {
- return 0;
- }
-
- status = acpi_get_handle(handle, "_EJ0", &tmp);
- if (ACPI_FAILURE(status)) {
- return 0;
- }
+ context = kzalloc(sizeof(*context), GFP_KERNEL);
+ if (!context)
+ return NULL;
- return 1;
+ context->refcount = 1;
+ acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_notify, NULL,
+ acpiphp_post_dock_fixup);
+ return context;
}
-
-/* callback routine to check for the existence of ejectable slots */
-static acpi_status
-is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
+/**
+ * acpiphp_get_context - Get hotplug context and grab a reference to it.
+ * @adev: ACPI device object to get the context for.
+ *
+ * Call under acpi_hp_context_lock.
+ */
+static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev)
{
- int *count = (int *)context;
-
- if (is_ejectable(handle)) {
- (*count)++;
- /* only one ejectable slot is enough */
- return AE_CTRL_TERMINATE;
- } else {
- return AE_OK;
- }
-}
+ struct acpiphp_context *context;
-/* callback routine to check for the existence of a pci dock device */
-static acpi_status
-is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
- int *count = (int *)context;
+ if (!adev->hp)
+ return NULL;
- if (is_dock_device(handle)) {
- (*count)++;
- return AE_CTRL_TERMINATE;
- } else {
- return AE_OK;
- }
+ context = to_acpiphp_context(adev->hp);
+ context->refcount++;
+ return context;
}
-
-
-
-/*
- * the _DCK method can do funny things... and sometimes not
- * hah-hah funny.
+/**
+ * acpiphp_put_context - Drop a reference to ACPI hotplug context.
+ * @context: ACPI hotplug context to drop a reference to.
+ *
+ * The context object is removed if there are no more references to it.
*
- * TBD - figure out a way to only call fixups for
- * systems that require them.
+ * Call under acpi_hp_context_lock.
*/
-static int post_dock_fixups(struct notifier_block *nb, unsigned long val,
- void *v)
+static void acpiphp_put_context(struct acpiphp_context *context)
{
- struct acpiphp_func *func = container_of(nb, struct acpiphp_func, nb);
- struct pci_bus *bus = func->slot->bridge->pci_bus;
- u32 buses;
-
- if (!bus->self)
- return NOTIFY_OK;
-
- /* fixup bad _DCK function that rewrites
- * secondary bridge on slot
- */
- pci_read_config_dword(bus->self,
- PCI_PRIMARY_BUS,
- &buses);
+ if (--context->refcount)
+ return;
- if (((buses >> 8) & 0xff) != bus->secondary) {
- buses = (buses & 0xff000000)
- | ((unsigned int)(bus->primary) << 0)
- | ((unsigned int)(bus->secondary) << 8)
- | ((unsigned int)(bus->subordinate) << 16);
- pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
- }
- return NOTIFY_OK;
+ WARN_ON(context->bridge);
+ context->hp.self->hp = NULL;
+ kfree(context);
}
-
-
-
-/* callback routine to register each ACPI PCI slot object */
-static acpi_status
-register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
+static inline void get_bridge(struct acpiphp_bridge *bridge)
{
- struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context;
- struct acpiphp_slot *slot;
- struct acpiphp_func *newfunc;
- acpi_handle tmp;
- acpi_status status = AE_OK;
- unsigned long adr, sun;
- int device, function, retval;
-
- status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
-
- if (ACPI_FAILURE(status))
- return AE_OK;
-
- status = acpi_get_handle(handle, "_EJ0", &tmp);
-
- if (ACPI_FAILURE(status) && !(is_dock_device(handle)))
- return AE_OK;
-
- device = (adr >> 16) & 0xffff;
- function = adr & 0xffff;
-
- newfunc = kzalloc(sizeof(struct acpiphp_func), GFP_KERNEL);
- if (!newfunc)
- return AE_NO_MEMORY;
-
- INIT_LIST_HEAD(&newfunc->sibling);
- newfunc->handle = handle;
- newfunc->function = function;
- if (ACPI_SUCCESS(status))
- newfunc->flags = FUNC_HAS_EJ0;
-
- if (ACPI_SUCCESS(acpi_get_handle(handle, "_STA", &tmp)))
- newfunc->flags |= FUNC_HAS_STA;
-
- if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS0", &tmp)))
- newfunc->flags |= FUNC_HAS_PS0;
-
- if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp)))
- newfunc->flags |= FUNC_HAS_PS3;
-
- if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp)))
- newfunc->flags |= FUNC_HAS_DCK;
-
- status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
- if (ACPI_FAILURE(status)) {
- /*
- * use the count of the number of slots we've found
- * for the number of the slot
- */
- sun = bridge->nr_slots+1;
- }
-
- /* search for objects that share the same slot */
- for (slot = bridge->slots; slot; slot = slot->next)
- if (slot->device == device) {
- if (slot->sun != sun)
- warn("sibling found, but _SUN doesn't match!\n");
- break;
- }
-
- if (!slot) {
- slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
- if (!slot) {
- kfree(newfunc);
- return AE_NO_MEMORY;
- }
-
- slot->bridge = bridge;
- slot->device = device;
- slot->sun = sun;
- INIT_LIST_HEAD(&slot->funcs);
- mutex_init(&slot->crit_sect);
-
- slot->next = bridge->slots;
- bridge->slots = slot;
-
- bridge->nr_slots++;
-
- dbg("found ACPI PCI Hotplug slot %d at PCI %04x:%02x:%02x\n",
- slot->sun, pci_domain_nr(bridge->pci_bus),
- 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);
- goto err_exit;
- }
- }
-
- newfunc->slot = slot;
- list_add_tail(&newfunc->sibling, &slot->funcs);
-
- /* associate corresponding pci_dev */
- newfunc->pci_dev = pci_get_slot(bridge->pci_bus,
- PCI_DEVFN(device, function));
- if (newfunc->pci_dev) {
- slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
- }
-
- if (is_dock_device(handle)) {
- /* we don't want to call this device's _EJ0
- * because we want the dock notify handler
- * to call it after it calls _DCK
- */
- newfunc->flags &= ~FUNC_HAS_EJ0;
- if (register_hotplug_dock_device(handle,
- handle_hotplug_event_func, newfunc))
- dbg("failed to register dock device\n");
-
- /* we need to be notified when dock events happen
- * outside of the hotplug operation, since we may
- * need to do fixups before we can hotplug.
- */
- newfunc->nb.notifier_call = post_dock_fixups;
- if (register_dock_notifier(&newfunc->nb))
- dbg("failed to register a dock notifier");
- }
-
- /* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK)) {
- status = acpi_install_notify_handler(handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event_func,
- newfunc);
-
- if (ACPI_FAILURE(status))
- err("failed to register interrupt notify handler\n");
- } else
- status = AE_OK;
-
- return status;
-
- err_exit:
- bridge->nr_slots--;
- bridge->slots = slot->next;
- kfree(slot);
- kfree(newfunc);
-
- return AE_OK;
+ kref_get(&bridge->ref);
}
-
-/* see if it's worth looking at this bridge */
-static int detect_ejectable_slots(acpi_handle *bridge_handle)
+static inline void put_bridge(struct acpiphp_bridge *bridge)
{
- acpi_status status;
- int count;
-
- count = 0;
-
- /* only check slots defined directly below bridge object */
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1,
- is_ejectable_slot, (void *)&count, NULL);
-
- /*
- * we also need to add this bridge if there is a dock bridge or
- * other pci device on a dock station (removable)
- */
- if (!count)
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,
- (u32)1, is_pci_dock_device, (void *)&count,
- NULL);
-
- return count;
+ kref_put(&bridge->ref, free_bridge);
}
-
-/* decode ACPI 2.0 _HPP hot plug parameters */
-static void decode_hpp(struct acpiphp_bridge *bridge)
+static struct acpiphp_context *acpiphp_grab_context(struct acpi_device *adev)
{
- acpi_status status;
+ struct acpiphp_context *context;
- status = acpi_get_hp_params_from_firmware(bridge->pci_bus, &bridge->hpp);
- if (ACPI_FAILURE(status) ||
- !bridge->hpp.t0 || (bridge->hpp.t0->revision > 1)) {
- /* use default numbers */
- printk(KERN_WARNING
- "%s: Could not get hotplug parameters. Use defaults\n",
- __FUNCTION__);
- bridge->hpp.t0 = &bridge->hpp.type0_data;
- bridge->hpp.t0->revision = 0;
- bridge->hpp.t0->cache_line_size = 0x10;
- bridge->hpp.t0->latency_timer = 0x40;
- bridge->hpp.t0->enable_serr = 0;
- bridge->hpp.t0->enable_perr = 0;
+ acpi_lock_hp_context();
+ context = acpiphp_get_context(adev);
+ if (!context || context->func.parent->is_going_away) {
+ acpi_unlock_hp_context();
+ return NULL;
}
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
+ acpi_unlock_hp_context();
+ return context;
}
-
-
-/* initialize miscellaneous stuff for both root and PCI-to-PCI bridge */
-static void init_bridge_misc(struct acpiphp_bridge *bridge)
+static void acpiphp_let_context_go(struct acpiphp_context *context)
{
- acpi_status status;
-
- /* decode ACPI 2.0 _HPP (hot plug parameters) */
- decode_hpp(bridge);
-
- /* must be added to the list prior to calling register_slot */
- list_add(&bridge->list, &bridge_list);
-
- /* register all slot objects under this bridge */
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1,
- register_slot, bridge, NULL);
- if (ACPI_FAILURE(status)) {
- list_del(&bridge->list);
- return;
- }
-
- /* install notify handler */
- if (bridge->type != BRIDGE_TYPE_HOST) {
- if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {
- status = acpi_remove_notify_handler(bridge->func->handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event_func);
- if (ACPI_FAILURE(status))
- err("failed to remove notify handler\n");
- }
- status = acpi_install_notify_handler(bridge->handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event_bridge,
- bridge);
-
- if (ACPI_FAILURE(status)) {
- err("failed to register interrupt notify handler\n");
- }
- }
+ put_bridge(context->func.parent);
}
-
-/* find acpiphp_func from acpiphp_bridge */
-static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle)
+static void free_bridge(struct kref *kref)
{
- struct list_head *node, *l;
+ struct acpiphp_context *context;
struct acpiphp_bridge *bridge;
- struct acpiphp_slot *slot;
- struct acpiphp_func *func;
-
- list_for_each(node, &bridge_list) {
- bridge = list_entry(node, struct acpiphp_bridge, list);
- for (slot = bridge->slots; slot; slot = slot->next) {
- list_for_each(l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func,
- sibling);
- if (func->handle == handle)
- return func;
- }
- }
- }
-
- return NULL;
-}
+ struct acpiphp_slot *slot, *next;
+ struct acpiphp_func *func, *tmp;
+ acpi_lock_hp_context();
-static inline void config_p2p_bridge_flags(struct acpiphp_bridge *bridge)
-{
- acpi_handle dummy_handle;
-
- if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
- "_STA", &dummy_handle)))
- bridge->flags |= BRIDGE_HAS_STA;
-
- if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
- "_EJ0", &dummy_handle)))
- bridge->flags |= BRIDGE_HAS_EJ0;
-
- if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
- "_PS0", &dummy_handle)))
- bridge->flags |= BRIDGE_HAS_PS0;
-
- if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
- "_PS3", &dummy_handle)))
- bridge->flags |= BRIDGE_HAS_PS3;
-
- /* is this ejectable p2p bridge? */
- if (bridge->flags & BRIDGE_HAS_EJ0) {
- struct acpiphp_func *func;
-
- dbg("found ejectable p2p bridge\n");
-
- /* make link between PCI bridge and PCI function */
- func = acpiphp_bridge_handle_to_function(bridge->handle);
- if (!func)
- return;
- bridge->func = func;
- func->bridge = bridge;
- }
-}
+ bridge = container_of(kref, struct acpiphp_bridge, ref);
+ list_for_each_entry_safe(slot, next, &bridge->slots, node) {
+ list_for_each_entry_safe(func, tmp, &slot->funcs, sibling)
+ acpiphp_put_context(func_to_context(func));
-/* allocate and initialize host bridge data structure */
-static void add_host_bridge(acpi_handle *handle, struct pci_bus *pci_bus)
-{
- struct acpiphp_bridge *bridge;
-
- bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
- if (bridge == NULL)
- return;
-
- bridge->type = BRIDGE_TYPE_HOST;
- bridge->handle = handle;
-
- bridge->pci_bus = pci_bus;
-
- spin_lock_init(&bridge->res_lock);
-
- init_bridge_misc(bridge);
-}
-
-
-/* allocate and initialize PCI-to-PCI bridge data structure */
-static void add_p2p_bridge(acpi_handle *handle, struct pci_dev *pci_dev)
-{
- struct acpiphp_bridge *bridge;
-
- bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
- if (bridge == NULL) {
- err("out of memory\n");
- return;
+ kfree(slot);
}
- bridge->type = BRIDGE_TYPE_P2P;
- bridge->handle = handle;
- config_p2p_bridge_flags(bridge);
-
- bridge->pci_dev = pci_dev_get(pci_dev);
- bridge->pci_bus = pci_dev->subordinate;
- if (!bridge->pci_bus) {
- err("This is not a PCI-to-PCI bridge!\n");
- goto err;
+ context = bridge->context;
+ /* Root bridges will not have hotplug context. */
+ if (context) {
+ /* Release the reference taken by acpiphp_enumerate_slots(). */
+ put_bridge(context->func.parent);
+ context->bridge = NULL;
+ acpiphp_put_context(context);
}
- spin_lock_init(&bridge->res_lock);
-
- init_bridge_misc(bridge);
- return;
- err:
- pci_dev_put(pci_dev);
+ put_device(&bridge->pci_bus->dev);
+ pci_dev_put(bridge->pci_dev);
kfree(bridge);
- return;
-}
+ acpi_unlock_hp_context();
+}
-/* callback routine to find P2P bridges */
-static acpi_status
-find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
+/**
+ * acpiphp_post_dock_fixup - Post-dock fixups for PCI devices.
+ * @adev: ACPI device object corresponding to a PCI device.
+ *
+ * TBD - figure out a way to only call fixups for systems that require them.
+ */
+static void acpiphp_post_dock_fixup(struct acpi_device *adev)
{
- acpi_status status;
- acpi_handle dummy_handle;
- unsigned long tmp;
- int device, function;
- struct pci_dev *dev;
- struct pci_bus *pci_bus = context;
-
- status = acpi_get_handle(handle, "_ADR", &dummy_handle);
- if (ACPI_FAILURE(status))
- return AE_OK; /* continue */
-
- status = acpi_evaluate_integer(handle, "_ADR", NULL, &tmp);
- if (ACPI_FAILURE(status)) {
- dbg("%s: _ADR evaluation failure\n", __FUNCTION__);
- return AE_OK;
- }
-
- device = (tmp >> 16) & 0xffff;
- function = tmp & 0xffff;
+ struct acpiphp_context *context = acpiphp_grab_context(adev);
+ struct pci_bus *bus;
+ u32 buses;
- dev = pci_get_slot(pci_bus, PCI_DEVFN(device, function));
+ if (!context)
+ return;
- if (!dev || !dev->subordinate)
+ bus = context->func.slot->bus;
+ if (!bus->self)
goto out;
- /* check if this bridge has ejectable slots */
- if ((detect_ejectable_slots(handle) > 0)) {
- dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev));
- add_p2p_bridge(handle, dev);
- }
+ /* fixup bad _DCK function that rewrites
+ * secondary bridge on slot
+ */
+ pci_read_config_dword(bus->self, PCI_PRIMARY_BUS, &buses);
- /* search P2P bridges under this p2p bridge */
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- find_p2p_bridge, dev->subordinate, NULL);
- if (ACPI_FAILURE(status))
- warn("find_p2p_bridge failed (error code = 0x%x)\n", status);
+ if (((buses >> 8) & 0xff) != bus->busn_res.start) {
+ buses = (buses & 0xff000000)
+ | ((unsigned int)(bus->primary) << 0)
+ | ((unsigned int)(bus->busn_res.start) << 8)
+ | ((unsigned int)(bus->busn_res.end) << 16);
+ pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
+ }
out:
- pci_dev_put(dev);
- return AE_OK;
+ acpiphp_let_context_go(context);
}
-
-/* find hot-pluggable slots, and then find P2P bridge */
-static int add_bridge(acpi_handle handle)
+/* Check whether the PCI device is managed by native PCIe hotplug driver */
+static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
{
- acpi_status status;
- unsigned long tmp;
- int seg, bus;
- acpi_handle dummy_handle;
- struct pci_bus *pci_bus;
-
- /* if the bridge doesn't have _STA, we assume it is always there */
- status = acpi_get_handle(handle, "_STA", &dummy_handle);
- if (ACPI_SUCCESS(status)) {
- status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp);
- if (ACPI_FAILURE(status)) {
- dbg("%s: _STA evaluation failure\n", __FUNCTION__);
- return 0;
- }
- if ((tmp & ACPI_STA_FUNCTIONING) == 0)
- /* don't register this object */
- return 0;
- }
-
- /* get PCI segment number */
- status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp);
-
- seg = ACPI_SUCCESS(status) ? tmp : 0;
-
- /* get PCI bus number */
- status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp);
-
- if (ACPI_SUCCESS(status)) {
- bus = tmp;
- } else {
- warn("can't get bus number, assuming 0\n");
- bus = 0;
- }
-
- pci_bus = pci_find_bus(seg, bus);
- if (!pci_bus) {
- err("Can't find bus %04x:%02x\n", seg, bus);
- return 0;
- }
-
- /* check if this bridge has ejectable slots */
- if (detect_ejectable_slots(handle) > 0) {
- dbg("found PCI host-bus bridge with hot-pluggable slots\n");
- add_host_bridge(handle, pci_bus);
- }
-
- /* search P2P bridges under this host bridge */
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- find_p2p_bridge, pci_bus, NULL);
+ u32 reg32;
+ acpi_handle tmp;
+ struct acpi_pci_root *root;
- if (ACPI_FAILURE(status))
- warn("find_p2p_bridge failed (error code = 0x%x)\n", status);
+ /* Check whether the PCIe port supports native PCIe hotplug */
+ if (pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32))
+ return false;
+ if (!(reg32 & PCI_EXP_SLTCAP_HPC))
+ return false;
- return 0;
-}
-
-static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)
-{
- struct list_head *head;
- list_for_each(head, &bridge_list) {
- struct acpiphp_bridge *bridge = list_entry(head,
- struct acpiphp_bridge, list);
- if (bridge->handle == handle)
- return bridge;
- }
-
- return NULL;
+ /*
+ * Check whether native PCIe hotplug has been enabled for
+ * this PCIe hierarchy.
+ */
+ tmp = acpi_find_root_bridge_handle(pdev);
+ if (!tmp)
+ return false;
+ root = acpi_pci_find_root(tmp);
+ if (!root)
+ return false;
+ if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
+ return false;
+
+ return true;
}
-static void cleanup_bridge(struct acpiphp_bridge *bridge)
+/**
+ * acpiphp_add_context - Add ACPIPHP context to an ACPI device object.
+ * @handle: ACPI handle of the object to add a context to.
+ * @lvl: Not used.
+ * @data: The object's parent ACPIPHP bridge.
+ * @rv: Not used.
+ */
+static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
+ void **rv)
{
- struct list_head *list, *tmp;
+ struct acpiphp_bridge *bridge = data;
+ struct acpiphp_context *context;
+ struct acpi_device *adev;
struct acpiphp_slot *slot;
- acpi_status status;
- acpi_handle handle = bridge->handle;
-
- status = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event_bridge);
- if (ACPI_FAILURE(status))
- err("failed to remove notify handler\n");
-
- if ((bridge->type != BRIDGE_TYPE_HOST) &&
- ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func)) {
- status = acpi_install_notify_handler(bridge->func->handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event_func,
- bridge->func);
- if (ACPI_FAILURE(status))
- err("failed to install interrupt notify handler\n");
- }
-
- slot = bridge->slots;
- while (slot) {
- struct acpiphp_slot *next = slot->next;
- list_for_each_safe (list, tmp, &slot->funcs) {
- struct acpiphp_func *func;
- func = list_entry(list, struct acpiphp_func, sibling);
- if (is_dock_device(func->handle)) {
- unregister_hotplug_dock_device(func->handle);
- unregister_dock_notifier(&func->nb);
- }
- if (!(func->flags & FUNC_HAS_DCK)) {
- status = acpi_remove_notify_handler(func->handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event_func);
- if (ACPI_FAILURE(status))
- err("failed to remove notify handler\n");
- }
- pci_dev_put(func->pci_dev);
- list_del(list);
- kfree(func);
- }
- acpiphp_unregister_hotplug_slot(slot);
- list_del(&slot->funcs);
- kfree(slot);
- slot = next;
- }
-
- pci_dev_put(bridge->pci_dev);
- list_del(&bridge->list);
- kfree(bridge);
-}
-
-static acpi_status
-cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
- struct acpiphp_bridge *bridge;
-
- /* cleanup p2p bridges under this P2P bridge
- in a depth-first manner */
- acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- cleanup_p2p_bridge, NULL, NULL);
+ struct acpiphp_func *newfunc;
+ acpi_status status = AE_OK;
+ unsigned long long adr;
+ int device, function;
+ struct pci_bus *pbus = bridge->pci_bus;
+ struct pci_dev *pdev = bridge->pci_dev;
+ u32 val;
- if (!(bridge = acpiphp_handle_to_bridge(handle)))
+ status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND)
+ acpi_handle_warn(handle,
+ "can't evaluate _ADR (%#x)\n", status);
return AE_OK;
- cleanup_bridge(bridge);
- return AE_OK;
-}
-
-static void remove_bridge(acpi_handle handle)
-{
- struct acpiphp_bridge *bridge;
-
- /* cleanup p2p bridges under this host bridge
- in a depth-first manner */
- acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
- (u32)1, cleanup_p2p_bridge, NULL, NULL);
-
- bridge = acpiphp_handle_to_bridge(handle);
- if (bridge)
- cleanup_bridge(bridge);
-}
-
-static struct pci_dev * get_apic_pci_info(acpi_handle handle)
-{
- struct acpi_pci_id id;
- struct pci_bus *bus;
- struct pci_dev *dev;
-
- if (ACPI_FAILURE(acpi_get_pci_id(handle, &id)))
- return NULL;
-
- bus = pci_find_bus(id.segment, id.bus);
- if (!bus)
- return NULL;
-
- dev = pci_get_slot(bus, PCI_DEVFN(id.device, id.function));
- if (!dev)
- return NULL;
-
- if ((dev->class != PCI_CLASS_SYSTEM_PIC_IOAPIC) &&
- (dev->class != PCI_CLASS_SYSTEM_PIC_IOXAPIC))
- {
- pci_dev_put(dev);
- return NULL;
}
+ if (acpi_bus_get_device(handle, &adev))
+ return AE_OK;
- return dev;
-}
+ device = (adr >> 16) & 0xffff;
+ function = adr & 0xffff;
-static int get_gsi_base(acpi_handle handle, u32 *gsi_base)
-{
- acpi_status status;
- int result = -1;
- unsigned long gsb;
- struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
- union acpi_object *obj;
- void *table;
-
- status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsb);
- if (ACPI_SUCCESS(status)) {
- *gsi_base = (u32)gsb;
- return 0;
+ acpi_lock_hp_context();
+ context = acpiphp_init_context(adev);
+ if (!context) {
+ acpi_unlock_hp_context();
+ acpi_handle_err(handle, "No hotplug context\n");
+ return AE_NOT_EXIST;
}
+ newfunc = &context->func;
+ newfunc->function = function;
+ newfunc->parent = bridge;
+ acpi_unlock_hp_context();
- status = acpi_evaluate_object(handle, "_MAT", NULL, &buffer);
- if (ACPI_FAILURE(status) || !buffer.length || !buffer.pointer)
- return -1;
-
- obj = buffer.pointer;
- if (obj->type != ACPI_TYPE_BUFFER)
- goto out;
+ /*
+ * If this is a dock device, its _EJ0 should be executed by the dock
+ * notify handler after calling _DCK.
+ */
+ if (!is_dock_device(adev) && acpi_has_method(handle, "_EJ0"))
+ newfunc->flags = FUNC_HAS_EJ0;
- table = obj->buffer.pointer;
- switch (((struct acpi_subtable_header *)table)->type) {
- case ACPI_MADT_TYPE_IO_SAPIC:
- *gsi_base = ((struct acpi_madt_io_sapic *)table)->global_irq_base;
- result = 0;
- break;
- case ACPI_MADT_TYPE_IO_APIC:
- *gsi_base = ((struct acpi_madt_io_apic *)table)->global_irq_base;
- result = 0;
- break;
- default:
- break;
- }
- out:
- kfree(buffer.pointer);
- return result;
-}
+ if (acpi_has_method(handle, "_STA"))
+ newfunc->flags |= FUNC_HAS_STA;
-static acpi_status
-ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
- acpi_status status;
- unsigned long sta;
- acpi_handle tmp;
- struct pci_dev *pdev;
- u32 gsi_base;
- u64 phys_addr;
- struct acpiphp_ioapic *ioapic;
-
- /* Evaluate _STA if present */
- status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
- if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)
- return AE_CTRL_DEPTH;
-
- /* Scan only PCI bus scope */
- status = acpi_get_handle(handle, "_HID", &tmp);
- if (ACPI_SUCCESS(status))
- return AE_CTRL_DEPTH;
-
- if (get_gsi_base(handle, &gsi_base))
- return AE_OK;
+ /* search for objects that share the same slot */
+ list_for_each_entry(slot, &bridge->slots, node)
+ if (slot->device == device)
+ goto slot_found;
- ioapic = kmalloc(sizeof(*ioapic), GFP_KERNEL);
- if (!ioapic)
+ slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
+ if (!slot) {
+ acpi_lock_hp_context();
+ acpiphp_put_context(context);
+ acpi_unlock_hp_context();
return AE_NO_MEMORY;
+ }
- pdev = get_apic_pci_info(handle);
- if (!pdev)
- goto exit_kfree;
-
- if (pci_enable_device(pdev))
- goto exit_pci_dev_put;
-
- pci_set_master(pdev);
-
- if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)"))
- goto exit_pci_disable_device;
-
- phys_addr = pci_resource_start(pdev, 0);
- if (acpi_register_ioapic(handle, phys_addr, gsi_base))
- goto exit_pci_release_region;
-
- ioapic->gsi_base = gsi_base;
- ioapic->dev = pdev;
- spin_lock(&ioapic_list_lock);
- list_add_tail(&ioapic->list, &ioapic_list);
- spin_unlock(&ioapic_list_lock);
-
- return AE_OK;
-
- exit_pci_release_region:
- pci_release_region(pdev, 0);
- exit_pci_disable_device:
- pci_disable_device(pdev);
- exit_pci_dev_put:
- pci_dev_put(pdev);
- exit_kfree:
- kfree(ioapic);
-
- return AE_OK;
-}
-
-static acpi_status
-ioapic_remove(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
- acpi_status status;
- unsigned long sta;
- acpi_handle tmp;
- u32 gsi_base;
- struct acpiphp_ioapic *pos, *n, *ioapic = NULL;
+ slot->bus = bridge->pci_bus;
+ slot->device = device;
+ INIT_LIST_HEAD(&slot->funcs);
- /* Evaluate _STA if present */
- status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
- if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)
- return AE_CTRL_DEPTH;
+ list_add_tail(&slot->node, &bridge->slots);
- /* Scan only PCI bus scope */
- status = acpi_get_handle(handle, "_HID", &tmp);
- if (ACPI_SUCCESS(status))
- return AE_CTRL_DEPTH;
+ /*
+ * Expose slots to user space for functions that have _EJ0 or _RMV or
+ * are located in dock stations. Do not expose them for devices handled
+ * by the native PCIe hotplug (PCIeHP), becuase that code is supposed to
+ * expose slots to user space in those cases.
+ */
+ if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(adev))
+ && !(pdev && device_is_managed_by_native_pciehp(pdev))) {
+ unsigned long long sun;
+ int retval;
- if (get_gsi_base(handle, &gsi_base))
- return AE_OK;
+ bridge->nr_slots++;
+ status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
+ if (ACPI_FAILURE(status))
+ sun = bridge->nr_slots;
- acpi_unregister_ioapic(handle, gsi_base);
+ pr_debug("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n",
+ sun, pci_domain_nr(pbus), pbus->number, device);
- spin_lock(&ioapic_list_lock);
- list_for_each_entry_safe(pos, n, &ioapic_list, list) {
- if (pos->gsi_base != gsi_base)
- continue;
- ioapic = pos;
- list_del(&ioapic->list);
- break;
+ retval = acpiphp_register_hotplug_slot(slot, sun);
+ if (retval) {
+ slot->slot = NULL;
+ bridge->nr_slots--;
+ if (retval == -EBUSY)
+ pr_warn("Slot %llu already registered by another hotplug driver\n", sun);
+ else
+ pr_warn("acpiphp_register_hotplug_slot failed (err code = 0x%x)\n", retval);
+ }
+ /* Even if the slot registration fails, we can still use it. */
}
- spin_unlock(&ioapic_list_lock);
- if (!ioapic)
- return AE_OK;
+ slot_found:
+ newfunc->slot = slot;
+ list_add_tail(&newfunc->sibling, &slot->funcs);
- pci_release_region(ioapic->dev, 0);
- pci_disable_device(ioapic->dev);
- pci_dev_put(ioapic->dev);
- kfree(ioapic);
+ if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function),
+ &val, 60*1000))
+ slot->flags |= SLOT_ENABLED;
return AE_OK;
}
-static int acpiphp_configure_ioapics(acpi_handle handle)
-{
- ioapic_add(handle, 0, NULL, NULL);
- acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
- ACPI_UINT32_MAX, ioapic_add, NULL, NULL);
- return 0;
-}
-
-static int acpiphp_unconfigure_ioapics(acpi_handle handle)
+static struct acpiphp_bridge *acpiphp_dev_to_bridge(struct acpi_device *adev)
{
- ioapic_remove(handle, 0, NULL, NULL);
- acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
- ACPI_UINT32_MAX, ioapic_remove, NULL, NULL);
- return 0;
-}
+ struct acpiphp_bridge *bridge = NULL;
-static int power_on_slot(struct acpiphp_slot *slot)
-{
- acpi_status status;
- struct acpiphp_func *func;
- struct list_head *l;
- int retval = 0;
-
- /* if already enabled, just skip */
- if (slot->flags & SLOT_POWEREDON)
- goto err_exit;
-
- list_for_each (l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func, sibling);
-
- if (func->flags & FUNC_HAS_PS0) {
- dbg("%s: executing _PS0\n", __FUNCTION__);
- status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL);
- if (ACPI_FAILURE(status)) {
- warn("%s: _PS0 failed\n", __FUNCTION__);
- retval = -1;
- goto err_exit;
- } else
- break;
- }
+ acpi_lock_hp_context();
+ if (adev->hp) {
+ bridge = to_acpiphp_root_context(adev->hp)->root_bridge;
+ if (bridge)
+ get_bridge(bridge);
}
-
- /* TBD: evaluate _STA to check if the slot is enabled */
-
- slot->flags |= SLOT_POWEREDON;
-
- err_exit:
- return retval;
+ acpi_unlock_hp_context();
+ return bridge;
}
-
-static int power_off_slot(struct acpiphp_slot *slot)
+static void cleanup_bridge(struct acpiphp_bridge *bridge)
{
- acpi_status status;
+ struct acpiphp_slot *slot;
struct acpiphp_func *func;
- struct list_head *l;
-
- int retval = 0;
- /* if already disabled, just skip */
- if ((slot->flags & SLOT_POWEREDON) == 0)
- goto err_exit;
+ list_for_each_entry(slot, &bridge->slots, node) {
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ struct acpi_device *adev = func_to_acpi_device(func);
- list_for_each (l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func, sibling);
-
- if (func->flags & FUNC_HAS_PS3) {
- status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL);
- if (ACPI_FAILURE(status)) {
- warn("%s: _PS3 failed\n", __FUNCTION__);
- retval = -1;
- goto err_exit;
- } else
- break;
+ acpi_lock_hp_context();
+ adev->hp->notify = NULL;
+ adev->hp->fixup = NULL;
+ acpi_unlock_hp_context();
}
+ slot->flags |= SLOT_IS_GOING_AWAY;
+ if (slot->slot)
+ acpiphp_unregister_hotplug_slot(slot);
}
- /* TBD: evaluate _STA to check if the slot is disabled */
-
- slot->flags &= (~SLOT_POWEREDON);
+ mutex_lock(&bridge_mutex);
+ list_del(&bridge->list);
+ mutex_unlock(&bridge_mutex);
- err_exit:
- return retval;
+ acpi_lock_hp_context();
+ bridge->is_going_away = true;
+ acpi_unlock_hp_context();
}
-
-
/**
* acpiphp_max_busnr - return the highest reserved bus number under the given bus.
* @bus: bus to start search with
*/
static unsigned char acpiphp_max_busnr(struct pci_bus *bus)
{
- struct list_head *tmp;
+ struct pci_bus *tmp;
unsigned char max, n;
/*
@@ -1001,238 +428,167 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus)
* bus->subordinate value because it could have
* padding in it.
*/
- max = bus->secondary;
+ max = bus->busn_res.start;
- list_for_each(tmp, &bus->children) {
- n = pci_bus_max_busnr(pci_bus_b(tmp));
+ list_for_each_entry(tmp, &bus->children, node) {
+ n = pci_bus_max_busnr(tmp);
if (n > max)
max = n;
}
return max;
}
-
-/**
- * acpiphp_bus_add - add a new bus to acpi subsystem
- * @func: acpiphp_func of the bridge
- */
-static int acpiphp_bus_add(struct acpiphp_func *func)
+static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
{
- acpi_handle phandle;
- struct acpi_device *device, *pdevice;
- int ret_val;
-
- acpi_get_parent(func->handle, &phandle);
- if (acpi_bus_get_device(phandle, &pdevice)) {
- dbg("no parent device, assuming NULL\n");
- pdevice = NULL;
- }
- if (!acpi_bus_get_device(func->handle, &device)) {
- dbg("bus exists... trim\n");
- /* this shouldn't be in here, so remove
- * the bus then re-add it...
- */
- ret_val = acpi_bus_trim(device, 1);
- dbg("acpi_bus_trim return %x\n", ret_val);
- }
+ struct acpiphp_func *func;
+ union acpi_object params[2];
+ struct acpi_object_list arg_list;
- ret_val = acpi_bus_add(&device, pdevice, func->handle,
- ACPI_BUS_TYPE_DEVICE);
- if (ret_val) {
- dbg("error adding bus, %x\n",
- -ret_val);
- goto acpiphp_bus_add_out;
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ arg_list.count = 2;
+ arg_list.pointer = params;
+ params[0].type = ACPI_TYPE_INTEGER;
+ params[0].integer.value = ACPI_ADR_SPACE_PCI_CONFIG;
+ params[1].type = ACPI_TYPE_INTEGER;
+ params[1].integer.value = 1;
+ /* _REG is optional, we don't care about if there is failure */
+ acpi_evaluate_object(func_to_handle(func), "_REG", &arg_list,
+ NULL);
}
- /*
- * try to start anyway. We could have failed to add
- * simply because this bus had previously been added
- * on another add. Don't bother with the return value
- * we just keep going.
- */
- ret_val = acpi_bus_start(device);
-
-acpiphp_bus_add_out:
- return ret_val;
}
-
-/**
- * acpiphp_bus_trim - trim a bus from acpi subsystem
- * @handle: handle to acpi namespace
- */
-static int acpiphp_bus_trim(acpi_handle handle)
+static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev)
{
- struct acpi_device *device;
- int retval;
+ struct acpiphp_func *func;
- retval = acpi_bus_get_device(handle, &device);
- if (retval) {
- dbg("acpi_device not found\n");
- return retval;
+ /* quirk, or pcie could set it already */
+ if (dev->is_hotplug_bridge)
+ return;
+
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ if (PCI_FUNC(dev->devfn) == func->function) {
+ dev->is_hotplug_bridge = 1;
+ break;
+ }
}
+}
+
+static int acpiphp_rescan_slot(struct acpiphp_slot *slot)
+{
+ struct acpiphp_func *func;
- retval = acpi_bus_trim(device, 1);
- if (retval)
- err("cannot remove from acpi list\n");
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ struct acpi_device *adev = func_to_acpi_device(func);
- return retval;
+ acpi_bus_scan(adev->handle);
+ if (acpi_device_enumerated(adev))
+ acpi_device_set_power(adev, ACPI_STATE_D0);
+ }
+ return pci_scan_slot(slot->bus, PCI_DEVFN(slot->device, 0));
}
/**
- * enable_device - enable, configure a slot
+ * enable_slot - enable, configure a slot
* @slot: slot to be enabled
*
* This function should be called per *physical slot*,
* not per each slot object in ACPI namespace.
*/
-static int __ref enable_device(struct acpiphp_slot *slot)
+static void enable_slot(struct acpiphp_slot *slot)
{
struct pci_dev *dev;
- struct pci_bus *bus = slot->bridge->pci_bus;
- struct list_head *l;
+ struct pci_bus *bus = slot->bus;
struct acpiphp_func *func;
- int retval = 0;
- int num, max, pass;
- acpi_status status;
-
- if (slot->flags & SLOT_ENABLED)
- goto err_exit;
-
- /* sanity check: dev should be NULL when hot-plugged in */
- dev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0));
- if (dev) {
- /* This case shouldn't happen */
- err("pci_dev structure already exists.\n");
- pci_dev_put(dev);
- retval = -1;
- goto err_exit;
- }
-
- num = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));
- if (num == 0) {
- err("No new device found\n");
- retval = -1;
- goto err_exit;
- }
+ int max, pass;
+ LIST_HEAD(add_list);
+ acpiphp_rescan_slot(slot);
max = acpiphp_max_busnr(bus);
for (pass = 0; pass < 2; pass++) {
list_for_each_entry(dev, &bus->devices, bus_list) {
if (PCI_SLOT(dev->devfn) != slot->device)
continue;
- if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
- dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
+
+ if (pci_is_bridge(dev)) {
max = pci_scan_bridge(bus, dev, max, pass);
- if (pass && dev->subordinate)
- pci_bus_size_bridges(dev->subordinate);
+ if (pass && dev->subordinate) {
+ check_hotplug_bridge(slot, dev);
+ pcibios_resource_survey_bus(dev->subordinate);
+ __pci_bus_size_bridges(dev->subordinate,
+ &add_list);
+ }
}
}
}
+ __pci_bus_assign_resources(bus, &add_list, NULL);
- list_for_each (l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func, sibling);
- acpiphp_bus_add(func);
- }
-
- pci_bus_assign_resources(bus);
acpiphp_sanitize_bus(bus);
- acpiphp_set_hpp_values(slot->bridge->handle, bus);
- list_for_each_entry(func, &slot->funcs, sibling)
- acpiphp_configure_ioapics(func->handle);
- pci_enable_bridges(bus);
- pci_bus_add_devices(bus);
+ acpiphp_set_hpp_values(bus);
+ acpiphp_set_acpi_region(slot);
- /* associate pci_dev to our representation */
- list_for_each (l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func, sibling);
- func->pci_dev = pci_get_slot(bus, PCI_DEVFN(slot->device,
- func->function));
- if (!func->pci_dev)
- continue;
-
- if (func->pci_dev->hdr_type != PCI_HEADER_TYPE_BRIDGE &&
- func->pci_dev->hdr_type != PCI_HEADER_TYPE_CARDBUS)
- continue;
-
- status = find_p2p_bridge(func->handle, (u32)1, bus, NULL);
- if (ACPI_FAILURE(status))
- warn("find_p2p_bridge failed (error code = 0x%x)\n",
- status);
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ /* Assume that newly added devices are powered on already. */
+ if (!dev->is_added)
+ dev->current_state = PCI_D0;
}
- slot->flags |= SLOT_ENABLED;
-
- err_exit:
- return retval;
-}
+ pci_bus_add_devices(bus);
-static void disable_bridges(struct pci_bus *bus)
-{
- struct pci_dev *dev;
- list_for_each_entry(dev, &bus->devices, bus_list) {
- if (dev->subordinate) {
- disable_bridges(dev->subordinate);
- pci_disable_device(dev);
+ slot->flags |= SLOT_ENABLED;
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ dev = pci_get_slot(bus, PCI_DEVFN(slot->device,
+ func->function));
+ if (!dev) {
+ /* Do not set SLOT_ENABLED flag if some funcs
+ are not added. */
+ slot->flags &= (~SLOT_ENABLED);
+ continue;
}
}
}
/**
- * disable_device - disable a slot
+ * disable_slot - disable a slot
* @slot: ACPI PHP slot
*/
-static int disable_device(struct acpiphp_slot *slot)
+static void disable_slot(struct acpiphp_slot *slot)
{
- int retval = 0;
+ struct pci_bus *bus = slot->bus;
+ struct pci_dev *dev, *prev;
struct acpiphp_func *func;
- struct list_head *l;
-
- /* is this slot already disabled? */
- if (!(slot->flags & SLOT_ENABLED))
- goto err_exit;
-
- list_for_each (l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func, sibling);
- if (func->bridge) {
- /* cleanup p2p bridges under this P2P bridge */
- cleanup_p2p_bridge(func->bridge->handle,
- (u32)1, NULL, NULL);
- func->bridge = NULL;
- }
-
- if (func->pci_dev) {
- pci_stop_bus_device(func->pci_dev);
- if (func->pci_dev->subordinate) {
- disable_bridges(func->pci_dev->subordinate);
- pci_disable_device(func->pci_dev);
- }
- }
- }
+ /*
+ * enable_slot() enumerates all functions in this device via
+ * pci_scan_slot(), whether they have associated ACPI hotplug
+ * methods (_EJ0, etc.) or not. Therefore, we remove all functions
+ * here.
+ */
+ list_for_each_entry_safe_reverse(dev, prev, &bus->devices, bus_list)
+ if (PCI_SLOT(dev->devfn) == slot->device)
+ pci_stop_and_remove_bus_device(dev);
- list_for_each (l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func, sibling);
+ list_for_each_entry(func, &slot->funcs, sibling)
+ acpi_bus_trim(func_to_acpi_device(func));
- acpiphp_unconfigure_ioapics(func->handle);
- acpiphp_bus_trim(func->handle);
- /* try to remove anyway.
- * acpiphp_bus_add might have been failed */
+ slot->flags &= (~SLOT_ENABLED);
+}
- if (!func->pci_dev)
- continue;
+static bool acpiphp_no_hotplug(struct acpi_device *adev)
+{
+ return adev && adev->flags.no_hotplug;
+}
- pci_remove_bus_device(func->pci_dev);
- pci_dev_put(func->pci_dev);
- func->pci_dev = NULL;
- }
+static bool slot_no_hotplug(struct acpiphp_slot *slot)
+{
+ struct acpiphp_func *func;
- slot->flags &= (~SLOT_ENABLED);
+ list_for_each_entry(func, &slot->funcs, sibling)
+ if (acpiphp_no_hotplug(func_to_acpi_device(func)))
+ return true;
- err_exit:
- return retval;
+ return false;
}
-
/**
* get_slot_status - get ACPI slot status
* @slot: ACPI PHP slot
@@ -1247,21 +603,21 @@ static int disable_device(struct acpiphp_slot *slot)
*/
static unsigned int get_slot_status(struct acpiphp_slot *slot)
{
- acpi_status status;
- unsigned long sta = 0;
- u32 dvid;
- struct list_head *l;
+ unsigned long long sta = 0;
struct acpiphp_func *func;
- list_for_each (l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func, sibling);
-
+ list_for_each_entry(func, &slot->funcs, sibling) {
if (func->flags & FUNC_HAS_STA) {
- status = acpi_evaluate_integer(func->handle, "_STA", NULL, &sta);
+ acpi_status status;
+
+ status = acpi_evaluate_integer(func_to_handle(func),
+ "_STA", NULL, &sta);
if (ACPI_SUCCESS(status) && sta)
break;
} else {
- pci_bus_read_config_dword(slot->bridge->pci_bus,
+ u32 dvid;
+
+ pci_bus_read_config_dword(slot->bus,
PCI_DEVFN(slot->device,
func->function),
PCI_VENDOR_ID, &dvid);
@@ -1275,38 +631,52 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
return (unsigned int)sta;
}
+static inline bool device_status_valid(unsigned int sta)
+{
+ /*
+ * ACPI spec says that _STA may return bit 0 clear with bit 3 set
+ * if the device is valid but does not require a device driver to be
+ * loaded (Section 6.3.7 of ACPI 5.0A).
+ */
+ unsigned int mask = ACPI_STA_DEVICE_ENABLED | ACPI_STA_DEVICE_FUNCTIONING;
+ return (sta & mask) == mask;
+}
+
/**
- * acpiphp_eject_slot - physically eject the slot
- * @slot: ACPI PHP slot
+ * trim_stale_devices - remove PCI devices that are not responding.
+ * @dev: PCI device to start walking the hierarchy from.
*/
-int acpiphp_eject_slot(struct acpiphp_slot *slot)
+static void trim_stale_devices(struct pci_dev *dev)
{
- acpi_status status;
- struct acpiphp_func *func;
- struct list_head *l;
- struct acpi_object_list arg_list;
- union acpi_object arg;
-
- list_for_each (l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func, sibling);
-
- /* We don't want to call _EJ0 on non-existing functions. */
- if ((func->flags & FUNC_HAS_EJ0)) {
- /* _EJ0 method take one argument */
- arg_list.count = 1;
- arg_list.pointer = &arg;
- arg.type = ACPI_TYPE_INTEGER;
- arg.integer.value = 1;
-
- status = acpi_evaluate_object(func->handle, "_EJ0", &arg_list, NULL);
- if (ACPI_FAILURE(status)) {
- warn("%s: _EJ0 failed\n", __FUNCTION__);
- return -1;
- } else
- break;
- }
+ struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
+ struct pci_bus *bus = dev->subordinate;
+ bool alive = false;
+
+ if (adev) {
+ acpi_status status;
+ unsigned long long sta;
+
+ status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
+ alive = (ACPI_SUCCESS(status) && device_status_valid(sta))
+ || acpiphp_no_hotplug(adev);
+ }
+ if (!alive)
+ alive = pci_device_is_present(dev);
+
+ if (!alive) {
+ pci_stop_and_remove_bus_device(dev);
+ if (adev)
+ acpi_bus_trim(adev);
+ } else if (bus) {
+ struct pci_dev *child, *tmp;
+
+ /* The device is a bridge. so check the bus below it. */
+ pm_runtime_get_sync(&dev->dev);
+ list_for_each_entry_safe_reverse(child, tmp, &bus->devices, bus_list)
+ trim_stale_devices(child);
+
+ pm_runtime_put(&dev->dev);
}
- return 0;
}
/**
@@ -1316,109 +686,41 @@ int acpiphp_eject_slot(struct acpiphp_slot *slot)
* Iterate over all slots under this bridge and make sure that if a
* card is present they are enabled, and if not they are disabled.
*/
-static int acpiphp_check_bridge(struct acpiphp_bridge *bridge)
+static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)
{
struct acpiphp_slot *slot;
- int retval = 0;
- int enabled, disabled;
-
- enabled = disabled = 0;
- for (slot = bridge->slots; slot; slot = slot->next) {
- unsigned int status = get_slot_status(slot);
- if (slot->flags & SLOT_ENABLED) {
- if (status == ACPI_STA_ALL)
- continue;
- retval = acpiphp_disable_slot(slot);
- if (retval) {
- err("Error occurred in disabling\n");
- goto err_exit;
- } else {
- acpiphp_eject_slot(slot);
- }
- disabled++;
- } else {
- if (status != ACPI_STA_ALL)
- continue;
- retval = acpiphp_enable_slot(slot);
- if (retval) {
- err("Error occurred in enabling\n");
- goto err_exit;
- }
- enabled++;
- }
- }
-
- dbg("%s: %d enabled, %d disabled\n", __FUNCTION__, enabled, disabled);
-
- err_exit:
- return retval;
-}
-
-static void program_hpp(struct pci_dev *dev, struct acpiphp_bridge *bridge)
-{
- u16 pci_cmd, pci_bctl;
- struct pci_dev *cdev;
-
- /* Program hpp values for this device */
- if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL ||
- (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
- (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
- return;
-
- if ((dev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
+ /* Bail out if the bridge is going away. */
+ if (bridge->is_going_away)
return;
- pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
- bridge->hpp.t0->cache_line_size);
- pci_write_config_byte(dev, PCI_LATENCY_TIMER,
- bridge->hpp.t0->latency_timer);
- pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
- if (bridge->hpp.t0->enable_serr)
- pci_cmd |= PCI_COMMAND_SERR;
- else
- pci_cmd &= ~PCI_COMMAND_SERR;
- if (bridge->hpp.t0->enable_perr)
- pci_cmd |= PCI_COMMAND_PARITY;
- else
- pci_cmd &= ~PCI_COMMAND_PARITY;
- pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
-
- /* Program bridge control value and child devices */
- if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
- pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
- bridge->hpp.t0->latency_timer);
- pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
- if (bridge->hpp.t0->enable_serr)
- pci_bctl |= PCI_BRIDGE_CTL_SERR;
- else
- pci_bctl &= ~PCI_BRIDGE_CTL_SERR;
- if (bridge->hpp.t0->enable_perr)
- pci_bctl |= PCI_BRIDGE_CTL_PARITY;
- else
- pci_bctl &= ~PCI_BRIDGE_CTL_PARITY;
- pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
- if (dev->subordinate) {
- list_for_each_entry(cdev, &dev->subordinate->devices,
- bus_list)
- program_hpp(cdev, bridge);
+ list_for_each_entry(slot, &bridge->slots, node) {
+ struct pci_bus *bus = slot->bus;
+ struct pci_dev *dev, *tmp;
+
+ if (slot_no_hotplug(slot)) {
+ ; /* do nothing */
+ } else if (device_status_valid(get_slot_status(slot))) {
+ /* remove stale devices if any */
+ list_for_each_entry_safe_reverse(dev, tmp,
+ &bus->devices, bus_list)
+ if (PCI_SLOT(dev->devfn) == slot->device)
+ trim_stale_devices(dev);
+
+ /* configure all functions */
+ enable_slot(slot);
+ } else {
+ disable_slot(slot);
}
}
}
-static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus)
+static void acpiphp_set_hpp_values(struct pci_bus *bus)
{
- struct acpiphp_bridge bridge;
struct pci_dev *dev;
- memset(&bridge, 0, sizeof(bridge));
- bridge.handle = handle;
- bridge.pci_bus = bus;
- bridge.pci_dev = bus->self;
- decode_hpp(&bridge);
list_for_each_entry(dev, &bus->devices, bus_list)
- program_hpp(dev, &bridge);
-
+ pci_configure_slot(dev);
}
/*
@@ -1427,353 +729,242 @@ static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus)
*/
static void acpiphp_sanitize_bus(struct pci_bus *bus)
{
- struct pci_dev *dev;
+ struct pci_dev *dev, *tmp;
int i;
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM;
- list_for_each_entry(dev, &bus->devices, bus_list) {
+ list_for_each_entry_safe_reverse(dev, tmp, &bus->devices, bus_list) {
for (i=0; i<PCI_BRIDGE_RESOURCES; i++) {
struct resource *res = &dev->resource[i];
if ((res->flags & type_mask) && !res->start &&
res->end) {
/* Could not assign a required resources
* for this device, remove it */
- pci_remove_bus_device(dev);
+ pci_stop_and_remove_bus_device(dev);
break;
}
}
}
}
-/* Program resources in newly inserted bridge */
-static int acpiphp_configure_bridge (acpi_handle handle)
-{
- struct acpi_pci_id pci_id;
- struct pci_bus *bus;
-
- if (ACPI_FAILURE(acpi_get_pci_id(handle, &pci_id))) {
- err("cannot get PCI domain and bus number for bridge\n");
- return -EINVAL;
- }
- bus = pci_find_bus(pci_id.segment, pci_id.bus);
- if (!bus) {
- err("cannot find bus %d:%d\n",
- pci_id.segment, pci_id.bus);
- return -EINVAL;
- }
-
- pci_bus_size_bridges(bus);
- pci_bus_assign_resources(bus);
- acpiphp_sanitize_bus(bus);
- acpiphp_set_hpp_values(handle, bus);
- pci_enable_bridges(bus);
- acpiphp_configure_ioapics(handle);
- return 0;
-}
-
-static void handle_bridge_insertion(acpi_handle handle, u32 type)
-{
- struct acpi_device *device, *pdevice;
- acpi_handle phandle;
-
- if ((type != ACPI_NOTIFY_BUS_CHECK) &&
- (type != ACPI_NOTIFY_DEVICE_CHECK)) {
- err("unexpected notification type %d\n", type);
- return;
- }
-
- acpi_get_parent(handle, &phandle);
- if (acpi_bus_get_device(phandle, &pdevice)) {
- dbg("no parent device, assuming NULL\n");
- pdevice = NULL;
- }
- if (acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE)) {
- err("cannot add bridge to acpi list\n");
- return;
- }
- if (!acpiphp_configure_bridge(handle) &&
- !acpi_bus_start(device))
- add_bridge(handle);
- else
- err("cannot configure and start bridge\n");
-
-}
-
/*
* ACPI event handlers
*/
-static acpi_status
-count_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
- int *count = (int *)context;
- struct acpiphp_bridge *bridge;
-
- bridge = acpiphp_handle_to_bridge(handle);
- if (bridge)
- (*count)++;
- return AE_OK ;
-}
-
-static acpi_status
-check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
+void acpiphp_check_host_bridge(struct acpi_device *adev)
{
struct acpiphp_bridge *bridge;
- char objname[64];
- struct acpi_buffer buffer = { .length = sizeof(objname),
- .pointer = objname };
- bridge = acpiphp_handle_to_bridge(handle);
+ bridge = acpiphp_dev_to_bridge(adev);
if (bridge) {
- acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
- dbg("%s: re-enumerating slots under %s\n",
- __FUNCTION__, objname);
+ pci_lock_rescan_remove();
+
acpiphp_check_bridge(bridge);
+
+ pci_unlock_rescan_remove();
+ put_bridge(bridge);
}
- return AE_OK ;
}
-/**
- * handle_hotplug_event_bridge - handle ACPI event on bridges
- * @handle: Notify()'ed acpi_handle
- * @type: Notify code
- * @context: pointer to acpiphp_bridge structure
- *
- * Handles ACPI event notification on {host,p2p} bridges.
- */
-static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *context)
+static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot);
+
+static void hotplug_event(u32 type, struct acpiphp_context *context)
{
+ acpi_handle handle = context->hp.self->handle;
+ struct acpiphp_func *func = &context->func;
+ struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;
- char objname[64];
- struct acpi_buffer buffer = { .length = sizeof(objname),
- .pointer = objname };
- struct acpi_device *device;
- int num_sub_bridges = 0;
-
- if (acpi_bus_get_device(handle, &device)) {
- /* This bridge must have just been physically inserted */
- handle_bridge_insertion(handle, type);
- return;
- }
- bridge = acpiphp_handle_to_bridge(handle);
- if (type == ACPI_NOTIFY_BUS_CHECK) {
- acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX,
- count_sub_bridges, &num_sub_bridges, NULL);
- }
+ acpi_lock_hp_context();
+ bridge = context->bridge;
+ if (bridge)
+ get_bridge(bridge);
- if (!bridge && !num_sub_bridges) {
- err("cannot get bridge info\n");
- return;
- }
+ acpi_unlock_hp_context();
- acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+ pci_lock_rescan_remove();
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
/* bus re-enumerate */
- dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname);
- if (bridge) {
- dbg("%s: re-enumerating slots under %s\n",
- __FUNCTION__, objname);
+ acpi_handle_debug(handle, "Bus check in %s()\n", __func__);
+ if (bridge)
acpiphp_check_bridge(bridge);
- }
- if (num_sub_bridges)
- acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
- ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL);
+ else if (!(slot->flags & SLOT_IS_GOING_AWAY))
+ enable_slot(slot);
+
break;
case ACPI_NOTIFY_DEVICE_CHECK:
/* device check */
- dbg("%s: Device check notify on %s\n", __FUNCTION__, objname);
- acpiphp_check_bridge(bridge);
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- /* wake event */
- dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname);
+ acpi_handle_debug(handle, "Device check in %s()\n", __func__);
+ if (bridge) {
+ acpiphp_check_bridge(bridge);
+ } else if (!(slot->flags & SLOT_IS_GOING_AWAY)) {
+ /*
+ * Check if anything has changed in the slot and rescan
+ * from the parent if that's the case.
+ */
+ if (acpiphp_rescan_slot(slot))
+ acpiphp_check_bridge(func->parent);
+ }
break;
case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
- dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname);
- if ((bridge->type != BRIDGE_TYPE_HOST) &&
- (bridge->flags & BRIDGE_HAS_EJ0)) {
- struct acpiphp_slot *slot;
- slot = bridge->func->slot;
- if (!acpiphp_disable_slot(slot))
- acpiphp_eject_slot(slot);
- }
+ acpi_handle_debug(handle, "Eject request in %s()\n", __func__);
+ acpiphp_disable_and_eject_slot(slot);
break;
+ }
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- printk(KERN_ERR "Device %s cannot be configured due"
- " to a frequency mismatch\n", objname);
- break;
+ pci_unlock_rescan_remove();
+ if (bridge)
+ put_bridge(bridge);
+}
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- printk(KERN_ERR "Device %s cannot be configured due"
- " to a bus mode mismatch\n", objname);
- break;
+static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type)
+{
+ struct acpiphp_context *context;
- case ACPI_NOTIFY_POWER_FAULT:
- printk(KERN_ERR "Device %s has suffered a power fault\n",
- objname);
- break;
+ context = acpiphp_grab_context(adev);
+ if (!context)
+ return -ENODATA;
- default:
- warn("notify_handler: unknown event type 0x%x for %s\n", type, objname);
- break;
- }
+ hotplug_event(type, context);
+ acpiphp_let_context_go(context);
+ return 0;
}
/**
- * handle_hotplug_event_func - handle ACPI event on functions (i.e. slots)
- * @handle: Notify()'ed acpi_handle
- * @type: Notify code
- * @context: pointer to acpiphp_func structure
+ * acpiphp_enumerate_slots - Enumerate PCI slots for a given bus.
+ * @bus: PCI bus to enumerate the slots for.
*
- * Handles ACPI event notification on slots.
+ * A "slot" is an object associated with a PCI device number. All functions
+ * (PCI devices) with the same bus and device number belong to the same slot.
*/
-static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context)
+void acpiphp_enumerate_slots(struct pci_bus *bus)
{
- struct acpiphp_func *func;
- char objname[64];
- struct acpi_buffer buffer = { .length = sizeof(objname),
- .pointer = objname };
+ struct acpiphp_bridge *bridge;
+ struct acpi_device *adev;
+ acpi_handle handle;
+ acpi_status status;
- acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+ if (acpiphp_disabled)
+ return;
- func = (struct acpiphp_func *)context;
+ adev = ACPI_COMPANION(bus->bridge);
+ if (!adev)
+ return;
- switch (type) {
- case ACPI_NOTIFY_BUS_CHECK:
- /* bus re-enumerate */
- dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname);
- acpiphp_enable_slot(func->slot);
- break;
+ handle = adev->handle;
+ bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
+ if (!bridge) {
+ acpi_handle_err(handle, "No memory for bridge object\n");
+ return;
+ }
- case ACPI_NOTIFY_DEVICE_CHECK:
- /* device check : re-enumerate from parent bus */
- dbg("%s: Device check notify on %s\n", __FUNCTION__, objname);
- acpiphp_check_bridge(func->slot->bridge);
- break;
+ INIT_LIST_HEAD(&bridge->slots);
+ kref_init(&bridge->ref);
+ bridge->pci_dev = pci_dev_get(bus->self);
+ bridge->pci_bus = bus;
- case ACPI_NOTIFY_DEVICE_WAKE:
- /* wake event */
- dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname);
- break;
+ /*
+ * Grab a ref to the subordinate PCI bus in case the bus is
+ * removed via PCI core logical hotplug. The ref pins the bus
+ * (which we access during module unload).
+ */
+ get_device(&bus->dev);
- case ACPI_NOTIFY_EJECT_REQUEST:
- /* request device eject */
- dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname);
- if (!(acpiphp_disable_slot(func->slot)))
- acpiphp_eject_slot(func->slot);
- break;
+ acpi_lock_hp_context();
+ if (pci_is_root_bus(bridge->pci_bus)) {
+ struct acpiphp_root_context *root_context;
- default:
- warn("notify_handler: unknown event type 0x%x for %s\n", type, objname);
- break;
- }
-}
+ root_context = kzalloc(sizeof(*root_context), GFP_KERNEL);
+ if (!root_context)
+ goto err;
+ root_context->root_bridge = bridge;
+ acpi_set_hp_context(adev, &root_context->hp, NULL, NULL, NULL);
+ } else {
+ struct acpiphp_context *context;
-static acpi_status
-find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
- int *count = (int *)context;
+ /*
+ * This bridge should have been registered as a hotplug function
+ * under its parent, so the context should be there, unless the
+ * parent is going to be handled by pciehp, in which case this
+ * bridge is not interesting to us either.
+ */
+ context = acpiphp_get_context(adev);
+ if (!context)
+ goto err;
- if (acpi_root_bridge(handle)) {
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event_bridge, NULL);
- (*count)++;
+ bridge->context = context;
+ context->bridge = bridge;
+ /* Get a reference to the parent bridge. */
+ get_bridge(context->func.parent);
}
- return AE_OK ;
-}
-
-static struct acpi_pci_driver acpi_pci_hp_driver = {
- .add = add_bridge,
- .remove = remove_bridge,
-};
-
-/**
- * acpiphp_glue_init - initializes all PCI hotplug - ACPI glue data structures
- */
-int __init acpiphp_glue_init(void)
-{
- int num = 0;
+ acpi_unlock_hp_context();
- acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, find_root_bridges, &num, NULL);
+ /* Must be added to the list prior to calling acpiphp_add_context(). */
+ mutex_lock(&bridge_mutex);
+ list_add(&bridge->list, &bridge_list);
+ mutex_unlock(&bridge_mutex);
- if (num <= 0)
- return -1;
- else
- acpi_pci_register_driver(&acpi_pci_hp_driver);
+ /* register all slot objects under this bridge */
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+ acpiphp_add_context, NULL, bridge, NULL);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_err(handle, "failed to register slots\n");
+ cleanup_bridge(bridge);
+ put_bridge(bridge);
+ }
+ return;
- return 0;
+ err:
+ acpi_unlock_hp_context();
+ put_device(&bus->dev);
+ pci_dev_put(bridge->pci_dev);
+ kfree(bridge);
}
-
-/**
- * acpiphp_glue_exit - terminates all PCI hotplug - ACPI glue data structures
- *
- * This function frees all data allocated in acpiphp_glue_init().
- */
-void acpiphp_glue_exit(void)
+void acpiphp_drop_bridge(struct acpiphp_bridge *bridge)
{
- acpi_pci_unregister_driver(&acpi_pci_hp_driver);
-}
+ if (pci_is_root_bus(bridge->pci_bus)) {
+ struct acpiphp_root_context *root_context;
+ struct acpi_device *adev;
-
-/**
- * acpiphp_get_num_slots - count number of slots in a system
- */
-int __init acpiphp_get_num_slots(void)
-{
- struct acpiphp_bridge *bridge;
- int num_slots = 0;
-
- list_for_each_entry (bridge, &bridge_list, list) {
- dbg("Bus %04x:%02x has %d slot%s\n",
- pci_domain_nr(bridge->pci_bus),
- bridge->pci_bus->number, bridge->nr_slots,
- bridge->nr_slots == 1 ? "" : "s");
- num_slots += bridge->nr_slots;
+ acpi_lock_hp_context();
+ adev = ACPI_COMPANION(bridge->pci_bus->bridge);
+ root_context = to_acpiphp_root_context(adev->hp);
+ adev->hp = NULL;
+ acpi_unlock_hp_context();
+ kfree(root_context);
}
-
- dbg("Total %d slots\n", num_slots);
- return num_slots;
+ cleanup_bridge(bridge);
+ put_bridge(bridge);
}
-
-#if 0
/**
- * acpiphp_for_each_slot - call function for each slot
- * @fn: callback function
- * @data: context to be passed to callback function
+ * acpiphp_remove_slots - Remove slot objects associated with a given bus.
+ * @bus: PCI bus to remove the slot objects for.
*/
-static int acpiphp_for_each_slot(acpiphp_callback fn, void *data)
+void acpiphp_remove_slots(struct pci_bus *bus)
{
- struct list_head *node;
struct acpiphp_bridge *bridge;
- struct acpiphp_slot *slot;
- int retval = 0;
-
- list_for_each (node, &bridge_list) {
- bridge = (struct acpiphp_bridge *)node;
- for (slot = bridge->slots; slot; slot = slot->next) {
- retval = fn(slot, data);
- if (!retval)
- goto err_exit;
+
+ if (acpiphp_disabled)
+ return;
+
+ mutex_lock(&bridge_mutex);
+ list_for_each_entry(bridge, &bridge_list, list)
+ if (bridge->pci_bus == bus) {
+ mutex_unlock(&bridge_mutex);
+ acpiphp_drop_bridge(bridge);
+ return;
}
- }
- err_exit:
- return retval;
+ mutex_unlock(&bridge_mutex);
}
-#endif
-
/**
* acpiphp_enable_slot - power on slot
@@ -1781,55 +972,61 @@ static int acpiphp_for_each_slot(acpiphp_callback fn, void *data)
*/
int acpiphp_enable_slot(struct acpiphp_slot *slot)
{
- int retval;
-
- mutex_lock(&slot->crit_sect);
+ pci_lock_rescan_remove();
- /* wake up all functions */
- retval = power_on_slot(slot);
- if (retval)
- goto err_exit;
+ if (slot->flags & SLOT_IS_GOING_AWAY)
+ return -ENODEV;
- if (get_slot_status(slot) == ACPI_STA_ALL) {
- /* configure all functions */
- retval = enable_device(slot);
- if (retval)
- power_off_slot(slot);
- } else {
- dbg("%s: Slot status is not ACPI_STA_ALL\n", __FUNCTION__);
- power_off_slot(slot);
- }
+ /* configure all functions */
+ if (!(slot->flags & SLOT_ENABLED))
+ enable_slot(slot);
- err_exit:
- mutex_unlock(&slot->crit_sect);
- return retval;
+ pci_unlock_rescan_remove();
+ return 0;
}
/**
- * acpiphp_disable_slot - power off slot
+ * acpiphp_disable_and_eject_slot - power off and eject slot
* @slot: ACPI PHP slot
*/
-int acpiphp_disable_slot(struct acpiphp_slot *slot)
+static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
{
- int retval = 0;
+ struct acpiphp_func *func;
- mutex_lock(&slot->crit_sect);
+ if (slot->flags & SLOT_IS_GOING_AWAY)
+ return -ENODEV;
/* unconfigure all functions */
- retval = disable_device(slot);
- if (retval)
- goto err_exit;
-
- /* power off all functions */
- retval = power_off_slot(slot);
- if (retval)
- goto err_exit;
-
- err_exit:
- mutex_unlock(&slot->crit_sect);
- return retval;
+ disable_slot(slot);
+
+ list_for_each_entry(func, &slot->funcs, sibling)
+ if (func->flags & FUNC_HAS_EJ0) {
+ acpi_handle handle = func_to_handle(func);
+
+ if (ACPI_FAILURE(acpi_evaluate_ej0(handle)))
+ acpi_handle_err(handle, "_EJ0 failed\n");
+
+ break;
+ }
+
+ return 0;
}
+int acpiphp_disable_slot(struct acpiphp_slot *slot)
+{
+ int ret;
+
+ /*
+ * Acquire acpi_scan_lock to ensure that the execution of _EJ0 in
+ * acpiphp_disable_and_eject_slot() will be synchronized properly.
+ */
+ acpi_scan_lock_acquire();
+ pci_lock_rescan_remove();
+ ret = acpiphp_disable_and_eject_slot(slot);
+ pci_unlock_rescan_remove();
+ acpi_scan_lock_release();
+ return ret;
+}
/*
* slot enabled: 1
@@ -1837,49 +1034,23 @@ int acpiphp_disable_slot(struct acpiphp_slot *slot)
*/
u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
{
- return (slot->flags & SLOT_POWEREDON);
+ return (slot->flags & SLOT_ENABLED);
}
-
/*
* latch open: 1
* latch closed: 0
*/
u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
{
- unsigned int sta;
-
- sta = get_slot_status(slot);
-
- return (sta & ACPI_STA_SHOW_IN_UI) ? 0 : 1;
+ return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI);
}
-
/*
* adapter presence : 1
* absence : 0
*/
u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot)
{
- unsigned int sta;
-
- sta = get_slot_status(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;
+ return !!get_slot_status(slot);
}
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index b0a22b92717..8dcccffd6e2 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -25,45 +25,37 @@
*
*/
+#define pr_fmt(fmt) "acpiphp_ibm: " fmt
+
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <acpi/acpi_bus.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
-#include <asm/uaccess.h>
#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <asm/uaccess.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>"
#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver IBM extension"
-static int debug;
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);
-module_param(debug, bool, 0644);
-MODULE_PARM_DESC(debug, " Debugging mode enabled or not");
-#define MY_NAME "acpiphp_ibm"
-
-#undef dbg
-#define dbg(format, arg...) \
-do { \
- if (debug) \
- printk(KERN_DEBUG "%s: " format, \
- MY_NAME , ## arg); \
-} while (0)
#define FOUND_APCI 0x61504349
/* these are the names for the IBM ACPI pseudo-device */
#define IBM_HARDWARE_ID1 "IBM37D0"
#define IBM_HARDWARE_ID2 "IBM37D4"
-#define hpslot_to_sun(A) (((struct slot *)((A)->private))->acpi_slot->sun)
+#define hpslot_to_sun(A) (((struct slot *)((A)->private))->sun)
/* union apci_descriptor - allows access to the
* various device descriptors that are embedded in the
@@ -105,7 +97,7 @@ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status);
static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status);
static void ibm_handle_events(acpi_handle handle, u32 event, void *context);
static int ibm_get_table_from_acpi(char **bufp);
-static ssize_t ibm_read_apci_table(struct kobject *kobj,
+static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t size);
static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
@@ -123,7 +115,7 @@ static struct bin_attribute ibm_apci_table_attr = {
.read = ibm_read_apci_table,
.write = NULL,
};
-static struct acpiphp_attention_info ibm_attention_info =
+static struct acpiphp_attention_info ibm_attention_info =
{
.set_attn = ibm_set_attention_status,
.get_attn = ibm_get_attention_status,
@@ -178,15 +170,15 @@ ibm_slot_done:
*/
static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
{
- union acpi_object args[2];
+ union acpi_object args[2];
struct acpi_object_list params = { .pointer = args, .count = 2 };
- acpi_status stat;
- unsigned long rc;
+ acpi_status stat;
+ unsigned long long rc;
union apci_descriptor *ibm_slot;
ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
- dbg("%s: set slot %d (%d) attention status to %d\n", __FUNCTION__,
+ pr_debug("%s: set slot %d (%d) attention status to %d\n", __func__,
ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
(status ? 1 : 0));
@@ -199,10 +191,10 @@ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", &params, &rc);
if (ACPI_FAILURE(stat)) {
- err("APLS evaluation failed: 0x%08x\n", stat);
+ pr_err("APLS evaluation failed: 0x%08x\n", stat);
return -ENODEV;
} else if (!rc) {
- err("APLS method failed: 0x%08lx\n", rc);
+ pr_err("APLS method failed: 0x%08llx\n", rc);
return -ERANGE;
}
return 0;
@@ -215,7 +207,7 @@ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
*
* Description: This method is registered with the acpiphp module as a
* callback to do the device specific task of getting the LED status.
- *
+ *
* Because there is no direct method of getting the LED status directly
* from an ACPI call, we read the aPCI table and parse out our
* slot descriptor to read the status from that.
@@ -231,7 +223,7 @@ static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
else
*status = 0;
- dbg("%s: get slot %d (%d) attention status is %d\n", __FUNCTION__,
+ pr_debug("%s: get slot %d (%d) attention status is %d\n", __func__,
ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
*status);
@@ -263,13 +255,12 @@ static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
u8 subevent = event & 0xf0;
struct notification *note = context;
- dbg("%s: Received notification %02x\n", __FUNCTION__, event);
+ pr_debug("%s: Received notification %02x\n", __func__, event);
if (subevent == 0x80) {
- dbg("%s: generationg bus event\n", __FUNCTION__);
- acpi_bus_generate_proc_event(note->device, note->event, detail);
+ pr_debug("%s: generating bus event\n", __func__);
acpi_bus_generate_netlink_event(note->device->pnp.device_class,
- note->device->dev.bus_id,
+ dev_name(&note->device->dev),
note->event, detail);
} else
note->event = event;
@@ -299,7 +290,7 @@ static int ibm_get_table_from_acpi(char **bufp)
status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer);
if (ACPI_FAILURE(status)) {
- err("%s: APCI evaluation failed\n", __FUNCTION__);
+ pr_err("%s: APCI evaluation failed\n", __func__);
return -ENODEV;
}
@@ -307,13 +298,13 @@ static int ibm_get_table_from_acpi(char **bufp)
if (!(package) ||
(package->type != ACPI_TYPE_PACKAGE) ||
!(package->package.elements)) {
- err("%s: Invalid APCI object\n", __FUNCTION__);
+ pr_err("%s: Invalid APCI object\n", __func__);
goto read_table_done;
}
for(size = 0, i = 0; i < package->package.count; i++) {
if (package->package.elements[i].type != ACPI_TYPE_BUFFER) {
- err("%s: Invalid APCI element %d\n", __FUNCTION__, i);
+ pr_err("%s: Invalid APCI element %d\n", __func__, i);
goto read_table_done;
}
size += package->package.elements[i].buffer.length;
@@ -323,8 +314,8 @@ static int ibm_get_table_from_acpi(char **bufp)
goto read_table_done;
lbuf = kzalloc(size, GFP_KERNEL);
- dbg("%s: element count: %i, ASL table size: %i, &table = 0x%p\n",
- __FUNCTION__, package->package.count, size, lbuf);
+ pr_debug("%s: element count: %i, ASL table size: %i, &table = 0x%p\n",
+ __func__, package->package.count, size, lbuf);
if (lbuf) {
*bufp = lbuf;
@@ -348,6 +339,7 @@ read_table_done:
/**
* ibm_read_apci_table - callback for the sysfs apci_table file
+ * @filp: the open sysfs file
* @kobj: the kobject this binary attribute is a part of
* @bin_attr: struct bin_attribute for this file
* @buffer: the kernel space buffer to fill
@@ -361,14 +353,14 @@ read_table_done:
* things get really tricky here...
* our solution is to only allow reading the table in all at once.
*/
-static ssize_t ibm_read_apci_table(struct kobject *kobj,
+static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t size)
{
int bytes_read = -EINVAL;
char *table = NULL;
-
- dbg("%s: pos = %d, size = %zd\n", __FUNCTION__, (int)pos, size);
+
+ pr_debug("%s: pos = %d, size = %zd\n", __func__, (int)pos, size);
if (pos == 0) {
bytes_read = ibm_get_table_from_acpi(&table);
@@ -394,28 +386,25 @@ static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
u32 lvl, void *context, void **rv)
{
acpi_handle *phandle = (acpi_handle *)context;
- acpi_status status;
+ acpi_status status;
struct acpi_device_info *info;
- struct acpi_buffer info_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
int retval = 0;
- status = acpi_get_object_info(handle, &info_buffer);
+ status = acpi_get_object_info(handle, &info);
if (ACPI_FAILURE(status)) {
- err("%s: Failed to get device information status=0x%x\n",
- __FUNCTION__, status);
+ pr_err("%s: Failed to get device information status=0x%x\n",
+ __func__, status);
return retval;
}
- info = info_buffer.pointer;
- info->hardware_id.value[sizeof(info->hardware_id.value) - 1] = '\0';
if (info->current_status && (info->valid & ACPI_VALID_HID) &&
- (!strcmp(info->hardware_id.value, IBM_HARDWARE_ID1) ||
- !strcmp(info->hardware_id.value, IBM_HARDWARE_ID2))) {
- dbg("found hardware: %s, handle: %p\n",
- info->hardware_id.value, handle);
+ (!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) ||
+ !strcmp(info->hardware_id.string, IBM_HARDWARE_ID2))) {
+ pr_debug("found hardware: %s, handle: %p\n",
+ info->hardware_id.string, handle);
*phandle = handle;
/* returning non-zero causes the search to stop
- * and returns this value to the caller of
+ * and returns this value to the caller of
* acpi_walk_namespace, but it also causes some warnings
* in the acpi debug code to print...
*/
@@ -430,20 +419,20 @@ 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", __FUNCTION__);
+ pr_debug("%s\n", __func__);
if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, ibm_find_acpi_device,
+ ACPI_UINT32_MAX, ibm_find_acpi_device, NULL,
&ibm_acpi_handle, NULL) != FOUND_APCI) {
- err("%s: acpi_walk_namespace failed\n", __FUNCTION__);
+ pr_err("%s: acpi_walk_namespace failed\n", __func__);
retval = -ENODEV;
goto init_return;
}
- dbg("%s: found IBM aPCI device\n", __FUNCTION__);
+ pr_debug("%s: found IBM aPCI device\n", __func__);
if (acpi_bus_get_device(ibm_acpi_handle, &device)) {
- err("%s: acpi_bus_get_device failed\n", __FUNCTION__);
+ pr_err("%s: acpi_bus_get_device failed\n", __func__);
retval = -ENODEV;
goto init_return;
}
@@ -457,8 +446,8 @@ static int __init ibm_acpiphp_init(void)
ACPI_DEVICE_NOTIFY, ibm_handle_events,
&ibm_note);
if (ACPI_FAILURE(status)) {
- err("%s: Failed to register notification handler\n",
- __FUNCTION__);
+ pr_err("%s: Failed to register notification handler\n",
+ __func__);
retval = -EBUSY;
goto init_cleanup;
}
@@ -477,19 +466,19 @@ 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", __FUNCTION__);
+ pr_debug("%s\n", __func__);
if (acpiphp_unregister_attention(&ibm_attention_info))
- err("%s: attention info deregistration failed", __FUNCTION__);
+ pr_err("%s: attention info deregistration failed", __func__);
status = acpi_remove_notify_handler(
ibm_acpi_handle,
ACPI_DEVICE_NOTIFY,
ibm_handle_events);
if (ACPI_FAILURE(status))
- err("%s: Notification handler removal failed\n", __FUNCTION__);
+ pr_err("%s: Notification handler removal failed\n", __func__);
/* remove the /sys entries */
sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
}
diff --git a/drivers/pci/hotplug/cpci_hotplug.h b/drivers/pci/hotplug/cpci_hotplug.h
index d9769b30be9..6a0ddf75734 100644
--- a/drivers/pci/hotplug/cpci_hotplug.h
+++ b/drivers/pci/hotplug/cpci_hotplug.h
@@ -30,6 +30,7 @@
#include <linux/types.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
/* PICMG 2.1 R2.0 HS CSR bits: */
#define HS_CSR_INS 0x0080
@@ -55,9 +56,9 @@ struct cpci_hp_controller_ops {
int (*enable_irq) (void);
int (*disable_irq) (void);
int (*check_irq) (void *dev_id);
- int (*hardware_test) (struct slot* slot, u32 value);
- u8 (*get_power) (struct slot* slot);
- int (*set_power) (struct slot* slot, int value);
+ int (*hardware_test) (struct slot *slot, u32 value);
+ u8 (*get_power) (struct slot *slot);
+ int (*set_power) (struct slot *slot, int value);
};
struct cpci_hp_controller {
@@ -69,28 +70,41 @@ struct cpci_hp_controller {
struct cpci_hp_controller_ops *ops;
};
-extern int cpci_hp_register_controller(struct cpci_hp_controller *controller);
-extern int cpci_hp_unregister_controller(struct cpci_hp_controller *controller);
-extern int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last);
-extern int cpci_hp_unregister_bus(struct pci_bus *bus);
-extern int cpci_hp_start(void);
-extern int cpci_hp_stop(void);
+static inline const char *slot_name(struct slot *slot)
+{
+ return hotplug_slot_name(slot->hotplug_slot);
+}
+
+int cpci_hp_register_controller(struct cpci_hp_controller *controller);
+int cpci_hp_unregister_controller(struct cpci_hp_controller *controller);
+int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last);
+int cpci_hp_unregister_bus(struct pci_bus *bus);
+int cpci_hp_start(void);
+int cpci_hp_stop(void);
/*
* Internal function prototypes, these functions should not be used by
* board/chassis drivers.
*/
-extern u8 cpci_get_attention_status(struct slot *slot);
-extern u8 cpci_get_latch_status(struct slot *slot);
-extern u8 cpci_get_adapter_status(struct slot *slot);
-extern u16 cpci_get_hs_csr(struct slot * slot);
-extern int cpci_set_attention_status(struct slot *slot, int status);
-extern int cpci_check_and_clear_ins(struct slot * slot);
-extern int cpci_check_ext(struct slot * slot);
-extern int cpci_clear_ext(struct slot * slot);
-extern int cpci_led_on(struct slot * slot);
-extern int cpci_led_off(struct slot * slot);
-extern int cpci_configure_slot(struct slot *slot);
-extern int cpci_unconfigure_slot(struct slot *slot);
+u8 cpci_get_attention_status(struct slot *slot);
+u8 cpci_get_latch_status(struct slot *slot);
+u8 cpci_get_adapter_status(struct slot *slot);
+u16 cpci_get_hs_csr(struct slot *slot);
+int cpci_set_attention_status(struct slot *slot, int status);
+int cpci_check_and_clear_ins(struct slot *slot);
+int cpci_check_ext(struct slot *slot);
+int cpci_clear_ext(struct slot *slot);
+int cpci_led_on(struct slot *slot);
+int cpci_led_off(struct slot *slot);
+int cpci_configure_slot(struct slot *slot);
+int cpci_unconfigure_slot(struct slot *slot);
+
+#ifdef CONFIG_HOTPLUG_PCI_CPCI
+int cpci_hotplug_init(int debug);
+void cpci_hotplug_exit(void);
+#else
+static inline int cpci_hotplug_init(int debug) { return 0; }
+static inline void cpci_hotplug_exit(void) { }
+#endif
#endif /* _CPCI_HOTPLUG_H */
diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
index ed4d44e3332..e09cf7827d6 100644
--- a/drivers/pci/hotplug/cpci_hotplug_core.c
+++ b/drivers/pci/hotplug/cpci_hotplug_core.c
@@ -32,8 +32,7 @@
#include <linux/pci_hotplug.h>
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/smp_lock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include "cpci_hotplug.h"
@@ -47,7 +46,7 @@
do { \
if (cpci_debug) \
printk (KERN_DEBUG "%s: " format "\n", \
- MY_NAME , ## arg); \
+ MY_NAME , ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
@@ -66,13 +65,12 @@ static int thread_finished;
static int enable_slot(struct hotplug_slot *slot);
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_adapter_status(struct hotplug_slot *slot, u8 * value);
-static int get_latch_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_adapter_status(struct hotplug_slot *slot, u8 *value);
+static int get_latch_status(struct hotplug_slot *slot, u8 *value);
static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
- .owner = THIS_MODULE,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.set_attention_status = set_attention_status,
@@ -108,7 +106,7 @@ enable_slot(struct hotplug_slot *hotplug_slot)
struct slot *slot = hotplug_slot->private;
int retval = 0;
- dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s", __func__, slot_name(slot));
if (controller->ops->set_power)
retval = controller->ops->set_power(slot, 1);
@@ -121,25 +119,23 @@ disable_slot(struct hotplug_slot *hotplug_slot)
struct slot *slot = hotplug_slot->private;
int retval = 0;
- dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s", __func__, slot_name(slot));
down_write(&list_rwsem);
/* Unconfigure device */
- dbg("%s - unconfiguring slot %s",
- __FUNCTION__, slot->hotplug_slot->name);
+ dbg("%s - unconfiguring slot %s", __func__, slot_name(slot));
if ((retval = cpci_unconfigure_slot(slot))) {
err("%s - could not unconfigure slot %s",
- __FUNCTION__, slot->hotplug_slot->name);
+ __func__, slot_name(slot));
goto disable_error;
}
- dbg("%s - finished unconfiguring slot %s",
- __FUNCTION__, slot->hotplug_slot->name);
+ dbg("%s - finished unconfiguring slot %s", __func__, slot_name(slot));
/* Clear EXT (by setting it) */
if (cpci_clear_ext(slot)) {
err("%s - could not clear EXT for slot %s",
- __FUNCTION__, slot->hotplug_slot->name);
+ __func__, slot_name(slot));
retval = -ENODEV;
goto disable_error;
}
@@ -172,7 +168,7 @@ cpci_get_power_status(struct slot *slot)
}
static int
-get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
+get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
@@ -181,7 +177,7 @@ get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
}
static int
-get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
+get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
@@ -196,14 +192,14 @@ set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
}
static int
-get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
+get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
*value = hotplug_slot->info->adapter_status;
return 0;
}
static int
-get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value)
+get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
*value = hotplug_slot->info->latch_status;
return 0;
@@ -214,7 +210,6 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
struct slot *slot = hotplug_slot->private;
kfree(slot->hotplug_slot->info);
- kfree(slot->hotplug_slot->name);
kfree(slot->hotplug_slot);
if (slot->dev)
pci_dev_put(slot->dev);
@@ -222,12 +217,6 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
}
#define SLOT_NAME_SIZE 6
-static void
-make_slot_name(struct slot *slot)
-{
- snprintf(slot->hotplug_slot->name,
- SLOT_NAME_SIZE, "%02x:%02x", slot->bus->number, slot->number);
-}
int
cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
@@ -235,8 +224,8 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
struct slot *slot;
struct hotplug_slot *hotplug_slot;
struct hotplug_slot_info *info;
- char *name;
- int status = -ENOMEM;
+ char name[SLOT_NAME_SIZE];
+ int status;
int i;
if (!(controller && bus))
@@ -248,48 +237,51 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
*/
for (i = first; i <= last; ++i) {
slot = kzalloc(sizeof (struct slot), GFP_KERNEL);
- if (!slot)
+ if (!slot) {
+ status = -ENOMEM;
goto error;
+ }
hotplug_slot =
kzalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
- if (!hotplug_slot)
+ if (!hotplug_slot) {
+ status = -ENOMEM;
goto error_slot;
+ }
slot->hotplug_slot = hotplug_slot;
info = kzalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL);
- if (!info)
+ if (!info) {
+ status = -ENOMEM;
goto error_hpslot;
+ }
hotplug_slot->info = info;
- name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
- if (!name)
- goto error_info;
- hotplug_slot->name = name;
-
slot->bus = bus;
slot->number = i;
slot->devfn = PCI_DEVFN(i, 0);
+ snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i);
+
hotplug_slot->private = slot;
hotplug_slot->release = &release_slot;
- make_slot_name(slot);
hotplug_slot->ops = &cpci_hotplug_slot_ops;
/*
* Initialize the slot info structure with some known
* good values.
*/
- dbg("initializing slot %s", slot->hotplug_slot->name);
+ dbg("initializing slot %s", name);
info->power_status = cpci_get_power_status(slot);
info->attention_status = cpci_get_attention_status(slot);
- dbg("registering slot %s", slot->hotplug_slot->name);
- status = pci_hp_register(slot->hotplug_slot);
+ dbg("registering slot %s", name);
+ status = pci_hp_register(slot->hotplug_slot, bus, i, name);
if (status) {
err("pci_hp_register failed with error %d", status);
- goto error_name;
+ goto error_info;
}
+ dbg("slot registered with name: %s", slot_name(slot));
/* Add slot to our internal list */
down_write(&list_rwsem);
@@ -298,8 +290,6 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
up_write(&list_rwsem);
}
return 0;
-error_name:
- kfree(name);
error_info:
kfree(info);
error_hpslot:
@@ -309,6 +299,7 @@ error_slot:
error:
return status;
}
+EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
int
cpci_hp_unregister_bus(struct pci_bus *bus)
@@ -327,7 +318,7 @@ cpci_hp_unregister_bus(struct pci_bus *bus)
list_del(&slot->slot_list);
slots--;
- dbg("deregistering slot %s", slot->hotplug_slot->name);
+ dbg("deregistering slot %s", slot_name(slot));
status = pci_hp_deregister(slot->hotplug_slot);
if (status) {
err("pci_hp_deregister failed with error %d",
@@ -339,6 +330,7 @@ cpci_hp_unregister_bus(struct pci_bus *bus)
up_write(&list_rwsem);
return status;
}
+EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
/* This is the interrupt mode interrupt handler */
static irqreturn_t
@@ -370,20 +362,19 @@ static int
init_slots(int clear_ins)
{
struct slot *slot;
- struct pci_dev* dev;
+ struct pci_dev *dev;
- dbg("%s - enter", __FUNCTION__);
+ dbg("%s - enter", __func__);
down_read(&list_rwsem);
if (!slots) {
up_read(&list_rwsem);
return -1;
}
list_for_each_entry(slot, &slot_list, slot_list) {
- dbg("%s - looking at slot %s",
- __FUNCTION__, slot->hotplug_slot->name);
+ dbg("%s - looking at slot %s", __func__, slot_name(slot));
if (clear_ins && cpci_check_and_clear_ins(slot))
dbg("%s - cleared INS for slot %s",
- __FUNCTION__, slot->hotplug_slot->name);
+ __func__, slot_name(slot));
dev = pci_get_slot(slot->bus, PCI_DEVFN(slot->number, 0));
if (dev) {
if (update_adapter_status(slot->hotplug_slot, 1))
@@ -394,7 +385,7 @@ init_slots(int clear_ins)
}
}
up_read(&list_rwsem);
- dbg("%s - exit", __FUNCTION__);
+ dbg("%s - exit", __func__);
return 0;
}
@@ -414,8 +405,7 @@ check_slots(void)
}
extracted = inserted = 0;
list_for_each_entry(slot, &slot_list, slot_list) {
- dbg("%s - looking at slot %s",
- __FUNCTION__, slot->hotplug_slot->name);
+ dbg("%s - looking at slot %s", __func__, slot_name(slot));
if (cpci_check_and_clear_ins(slot)) {
/*
* Some broken hardware (e.g. PLX 9054AB) asserts
@@ -423,35 +413,34 @@ check_slots(void)
*/
if (slot->dev) {
warn("slot %s already inserted",
- slot->hotplug_slot->name);
+ slot_name(slot));
inserted++;
continue;
}
/* Process insertion */
- dbg("%s - slot %s inserted",
- __FUNCTION__, slot->hotplug_slot->name);
+ dbg("%s - slot %s inserted", __func__, slot_name(slot));
/* GSM, debug */
hs_csr = cpci_get_hs_csr(slot);
dbg("%s - slot %s HS_CSR (1) = %04x",
- __FUNCTION__, slot->hotplug_slot->name, hs_csr);
+ __func__, slot_name(slot), hs_csr);
/* Configure device */
dbg("%s - configuring slot %s",
- __FUNCTION__, slot->hotplug_slot->name);
+ __func__, slot_name(slot));
if (cpci_configure_slot(slot)) {
err("%s - could not configure slot %s",
- __FUNCTION__, slot->hotplug_slot->name);
+ __func__, slot_name(slot));
continue;
}
dbg("%s - finished configuring slot %s",
- __FUNCTION__, slot->hotplug_slot->name);
+ __func__, slot_name(slot));
/* GSM, debug */
hs_csr = cpci_get_hs_csr(slot);
dbg("%s - slot %s HS_CSR (2) = %04x",
- __FUNCTION__, slot->hotplug_slot->name, hs_csr);
+ __func__, slot_name(slot), hs_csr);
if (update_latch_status(slot->hotplug_slot, 1))
warn("failure to update latch file");
@@ -464,18 +453,18 @@ check_slots(void)
/* GSM, debug */
hs_csr = cpci_get_hs_csr(slot);
dbg("%s - slot %s HS_CSR (3) = %04x",
- __FUNCTION__, slot->hotplug_slot->name, hs_csr);
+ __func__, slot_name(slot), hs_csr);
inserted++;
} else if (cpci_check_ext(slot)) {
/* Process extraction request */
dbg("%s - slot %s extracted",
- __FUNCTION__, slot->hotplug_slot->name);
+ __func__, slot_name(slot));
/* GSM, debug */
hs_csr = cpci_get_hs_csr(slot);
dbg("%s - slot %s HS_CSR = %04x",
- __FUNCTION__, slot->hotplug_slot->name, hs_csr);
+ __func__, slot_name(slot), hs_csr);
if (!slot->extracting) {
if (update_latch_status(slot->hotplug_slot, 0)) {
@@ -493,7 +482,7 @@ check_slots(void)
* bother trying to tell the driver or not?
*/
err("card in slot %s was improperly removed",
- slot->hotplug_slot->name);
+ slot_name(slot));
if (update_adapter_status(slot->hotplug_slot, 0))
warn("failure to update adapter file");
slot->extracting = 0;
@@ -519,7 +508,7 @@ event_thread(void *data)
{
int rc;
- dbg("%s - event thread started", __FUNCTION__);
+ dbg("%s - event thread started", __func__);
while (1) {
dbg("event thread sleeping");
set_current_state(TASK_INTERRUPTIBLE);
@@ -532,7 +521,7 @@ event_thread(void *data)
/* Give userspace a chance to handle extraction */
msleep(500);
} else if (rc < 0) {
- dbg("%s - error checking slots", __FUNCTION__);
+ dbg("%s - error checking slots", __func__);
thread_finished = 1;
goto out;
}
@@ -541,7 +530,7 @@ event_thread(void *data)
break;
/* Re-enable ENUM# interrupt */
- dbg("%s - re-enabling irq", __FUNCTION__);
+ dbg("%s - re-enabling irq", __func__);
controller->ops->enable_irq();
}
out:
@@ -564,7 +553,7 @@ poll_thread(void *data)
/* Give userspace a chance to handle extraction */
msleep(500);
} else if (rc < 0) {
- dbg("%s - error checking slots", __FUNCTION__);
+ dbg("%s - error checking slots", __func__);
thread_finished = 1;
goto out;
}
@@ -621,12 +610,13 @@ cpci_hp_register_controller(struct cpci_hp_controller *new_controller)
status = -ENODEV;
}
dbg("%s - acquired controller irq %d",
- __FUNCTION__, new_controller->irq);
+ __func__, new_controller->irq);
}
if (!status)
controller = new_controller;
return status;
}
+EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
static void
cleanup_slots(void)
@@ -666,6 +656,7 @@ cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
status = -ENODEV;
return status;
}
+EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
int
cpci_hp_start(void)
@@ -673,7 +664,7 @@ cpci_hp_start(void)
static int first = 1;
int status;
- dbg("%s - enter", __FUNCTION__);
+ dbg("%s - enter", __func__);
if (!controller)
return -ENODEV;
@@ -693,16 +684,17 @@ cpci_hp_start(void)
status = cpci_start_thread();
if (status)
return status;
- dbg("%s - thread started", __FUNCTION__);
+ dbg("%s - thread started", __func__);
if (controller->irq) {
/* Start enum interrupt processing */
- dbg("%s - enabling irq", __FUNCTION__);
+ dbg("%s - enabling irq", __func__);
controller->ops->enable_irq();
}
- dbg("%s - exit", __FUNCTION__);
+ dbg("%s - exit", __func__);
return 0;
}
+EXPORT_SYMBOL_GPL(cpci_hp_start);
int
cpci_hp_stop(void)
@@ -711,12 +703,13 @@ cpci_hp_stop(void)
return -ENODEV;
if (controller->irq) {
/* Stop enum interrupt processing */
- dbg("%s - disabling irq", __FUNCTION__);
+ dbg("%s - disabling irq", __func__);
controller->ops->disable_irq();
}
cpci_stop_thread();
return 0;
}
+EXPORT_SYMBOL_GPL(cpci_hp_stop);
int __init
cpci_hotplug_init(int debug)
@@ -734,10 +727,3 @@ cpci_hotplug_exit(void)
cpci_hp_stop();
cpci_hp_unregister_controller(controller);
}
-
-EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
-EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
-EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
-EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
-EXPORT_SYMBOL_GPL(cpci_hp_start);
-EXPORT_SYMBOL_GPL(cpci_hp_stop);
diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c
index b3515fc4cd3..7d48ecae669 100644
--- a/drivers/pci/hotplug/cpci_hotplug_pci.c
+++ b/drivers/pci/hotplug/cpci_hotplug_pci.c
@@ -39,14 +39,14 @@ extern int cpci_debug;
do { \
if (cpci_debug) \
printk (KERN_DEBUG "%s: " format "\n", \
- MY_NAME , ## arg); \
+ MY_NAME , ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
-u8 cpci_get_attention_status(struct slot* slot)
+u8 cpci_get_attention_status(struct slot *slot)
{
int hs_cap;
u16 hs_csr;
@@ -66,7 +66,7 @@ u8 cpci_get_attention_status(struct slot* slot)
return hs_csr & 0x0008 ? 1 : 0;
}
-int cpci_set_attention_status(struct slot* slot, int status)
+int cpci_set_attention_status(struct slot *slot, int status)
{
int hs_cap;
u16 hs_csr;
@@ -93,7 +93,7 @@ int cpci_set_attention_status(struct slot* slot, int status)
return 1;
}
-u16 cpci_get_hs_csr(struct slot* slot)
+u16 cpci_get_hs_csr(struct slot *slot)
{
int hs_cap;
u16 hs_csr;
@@ -111,7 +111,7 @@ u16 cpci_get_hs_csr(struct slot* slot)
return hs_csr;
}
-int cpci_check_and_clear_ins(struct slot* slot)
+int cpci_check_and_clear_ins(struct slot *slot)
{
int hs_cap;
u16 hs_csr;
@@ -140,7 +140,7 @@ int cpci_check_and_clear_ins(struct slot* slot)
return ins;
}
-int cpci_check_ext(struct slot* slot)
+int cpci_check_ext(struct slot *slot)
{
int hs_cap;
u16 hs_csr;
@@ -161,7 +161,7 @@ int cpci_check_ext(struct slot* slot)
return ext;
}
-int cpci_clear_ext(struct slot* slot)
+int cpci_clear_ext(struct slot *slot)
{
int hs_cap;
u16 hs_csr;
@@ -187,7 +187,7 @@ int cpci_clear_ext(struct slot* slot)
return 0;
}
-int cpci_led_on(struct slot* slot)
+int cpci_led_on(struct slot *slot)
{
int hs_cap;
u16 hs_csr;
@@ -209,14 +209,14 @@ int cpci_led_on(struct slot* slot)
hs_cap + 2,
hs_csr)) {
err("Could not set LOO for slot %s",
- slot->hotplug_slot->name);
+ hotplug_slot_name(slot->hotplug_slot));
return -ENODEV;
}
}
return 0;
}
-int cpci_led_off(struct slot* slot)
+int cpci_led_off(struct slot *slot)
{
int hs_cap;
u16 hs_csr;
@@ -238,7 +238,7 @@ int cpci_led_off(struct slot* slot)
hs_cap + 2,
hs_csr)) {
err("Could not clear LOO for slot %s",
- slot->hotplug_slot->name);
+ hotplug_slot_name(slot->hotplug_slot));
return -ENODEV;
}
}
@@ -250,12 +250,15 @@ int cpci_led_off(struct slot* slot)
* Device configuration functions
*/
-int __ref cpci_configure_slot(struct slot *slot)
+int cpci_configure_slot(struct slot *slot)
{
+ struct pci_dev *dev;
struct pci_bus *parent;
- int fn;
+ int ret = 0;
+
+ dbg("%s - enter", __func__);
- dbg("%s - enter", __FUNCTION__);
+ pci_lock_rescan_remove();
if (slot->dev == NULL) {
dbg("pci_dev null, finding %02x:%02x:%x",
@@ -273,81 +276,57 @@ int __ref cpci_configure_slot(struct slot *slot)
* we will only call this case when lookup fails.
*/
n = pci_scan_slot(slot->bus, slot->devfn);
- dbg("%s: pci_scan_slot returned %d", __FUNCTION__, n);
+ dbg("%s: pci_scan_slot returned %d", __func__, n);
slot->dev = pci_get_slot(slot->bus, slot->devfn);
if (slot->dev == NULL) {
err("Could not find PCI device for slot %02x", slot->number);
- return -ENODEV;
+ ret = -ENODEV;
+ goto out;
}
}
parent = slot->dev->bus;
- for (fn = 0; fn < 8; fn++) {
- struct pci_dev *dev;
-
- dev = pci_get_slot(parent, PCI_DEVFN(PCI_SLOT(slot->devfn), fn));
- if (!dev)
+ list_for_each_entry(dev, &parent->devices, bus_list)
+ if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn))
continue;
- if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) ||
- (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) {
- /* Find an unused bus number for the new bridge */
- struct pci_bus *child;
- unsigned char busnr, start = parent->secondary;
- unsigned char end = parent->subordinate;
-
- for (busnr = start; busnr <= end; busnr++) {
- if (!pci_find_bus(pci_domain_nr(parent),
- busnr))
- break;
- }
- if (busnr >= end) {
- err("No free bus for hot-added bridge\n");
- pci_dev_put(dev);
- continue;
- }
- child = pci_add_new_bus(parent, dev, busnr);
- if (!child) {
- err("Cannot add new bus for %s\n",
- pci_name(dev));
- pci_dev_put(dev);
- continue;
- }
- child->subordinate = pci_do_scan_bus(child);
- pci_bus_size_bridges(child);
- }
- pci_dev_put(dev);
- }
+ if (pci_is_bridge(dev))
+ pci_hp_add_bridge(dev);
+
+
+ pci_assign_unassigned_bridge_resources(parent->self);
- pci_bus_assign_resources(parent);
pci_bus_add_devices(parent);
- pci_enable_bridges(parent);
- dbg("%s - exit", __FUNCTION__);
- return 0;
+ out:
+ pci_unlock_rescan_remove();
+ dbg("%s - exit", __func__);
+ return ret;
}
-int cpci_unconfigure_slot(struct slot* slot)
+int cpci_unconfigure_slot(struct slot *slot)
{
- int i;
- struct pci_dev *dev;
+ struct pci_dev *dev, *temp;
- dbg("%s - enter", __FUNCTION__);
+ dbg("%s - enter", __func__);
if (!slot->dev) {
err("No device for slot %02x\n", slot->number);
return -ENODEV;
}
- for (i = 0; i < 8; i++) {
- dev = pci_get_slot(slot->bus,
- PCI_DEVFN(PCI_SLOT(slot->devfn), i));
- if (dev) {
- pci_remove_bus_device(dev);
- pci_dev_put(dev);
- }
+ pci_lock_rescan_remove();
+
+ list_for_each_entry_safe(dev, temp, &slot->bus->devices, bus_list) {
+ if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn))
+ continue;
+ pci_dev_get(dev);
+ pci_stop_and_remove_bus_device(dev);
+ pci_dev_put(dev);
}
pci_dev_put(slot->dev);
slot->dev = NULL;
- dbg("%s - exit", __FUNCTION__);
+ pci_unlock_rescan_remove();
+
+ dbg("%s - exit", __func__);
return 0;
}
diff --git a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c
index f3852a6b74e..04fcd781140 100644
--- a/drivers/pci/hotplug/cpcihp_generic.c
+++ b/drivers/pci/hotplug/cpcihp_generic.c
@@ -13,14 +13,14 @@
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
@@ -53,16 +53,16 @@
#define dbg(format, arg...) \
do { \
- if(debug) \
+ if (debug) \
printk (KERN_DEBUG "%s: " format "\n", \
- MY_NAME , ## arg); \
+ MY_NAME , ## arg); \
} while(0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
/* local variables */
-static int debug;
+static bool debug;
static char *bridge;
static u8 bridge_busnr;
static u8 bridge_slot;
@@ -78,8 +78,8 @@ static struct cpci_hp_controller generic_hpc;
static int __init validate_parameters(void)
{
- char* str;
- char* p;
+ char *str;
+ char *p;
unsigned long tmp;
if(!bridge) {
@@ -142,8 +142,8 @@ static int query_enum(void)
static int __init cpcihp_generic_init(void)
{
int status;
- struct resource* r;
- struct pci_dev* dev;
+ struct resource *r;
+ struct pci_dev *dev;
info(DRIVER_DESC " version: " DRIVER_VERSION);
status = validate_parameters();
@@ -154,12 +154,15 @@ static int __init cpcihp_generic_init(void)
if(!r)
return -EBUSY;
- dev = pci_find_slot(bridge_busnr, PCI_DEVFN(bridge_slot, 0));
+ dev = pci_get_domain_bus_and_slot(0, bridge_busnr,
+ PCI_DEVFN(bridge_slot, 0));
if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
err("Invalid bridge device %s", bridge);
+ pci_dev_put(dev);
return -EINVAL;
}
bus = dev->subordinate;
+ pci_dev_put(dev);
memset(&generic_hpc, 0, sizeof (struct cpci_hp_controller));
generic_hpc_ops.query_enum = query_enum;
diff --git a/drivers/pci/hotplug/cpcihp_zt5550.c b/drivers/pci/hotplug/cpcihp_zt5550.c
index 41f6a8d79c8..6757b3ef7e1 100644
--- a/drivers/pci/hotplug/cpcihp_zt5550.c
+++ b/drivers/pci/hotplug/cpcihp_zt5550.c
@@ -13,14 +13,14 @@
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
@@ -48,17 +48,17 @@
#define dbg(format, arg...) \
do { \
- if(debug) \
+ if (debug) \
printk (KERN_DEBUG "%s: " format "\n", \
- MY_NAME , ## arg); \
+ MY_NAME , ## arg); \
} while(0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
/* local variables */
-static int debug;
-static int poll;
+static bool debug;
+static bool poll;
static struct cpci_hp_controller_ops zt5550_hpc_ops;
static struct cpci_hp_controller zt5550_hpc;
@@ -271,7 +271,7 @@ init_hc_error:
}
-static void __devexit zt5550_hc_remove_one(struct pci_dev *pdev)
+static void zt5550_hc_remove_one(struct pci_dev *pdev)
{
cpci_hp_stop();
cpci_hp_unregister_bus(bus0);
@@ -285,17 +285,17 @@ static struct pci_device_id zt5550_hc_pci_tbl[] = {
{ 0, }
};
MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl);
-
+
static struct pci_driver zt5550_hc_driver = {
.name = "zt5550_hc",
.id_table = zt5550_hc_pci_tbl,
.probe = zt5550_hc_init_one,
- .remove = __devexit_p(zt5550_hc_remove_one),
+ .remove = zt5550_hc_remove_one,
};
static int __init zt5550_init(void)
{
- struct resource* r;
+ struct resource *r;
int rc;
info(DRIVER_DESC " version: " DRIVER_VERSION);
diff --git a/drivers/pci/hotplug/cpcihp_zt5550.h b/drivers/pci/hotplug/cpcihp_zt5550.h
index bebc6060a55..9a57fda5348 100644
--- a/drivers/pci/hotplug/cpcihp_zt5550.h
+++ b/drivers/pci/hotplug/cpcihp_zt5550.h
@@ -13,14 +13,14 @@
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
@@ -55,7 +55,7 @@
#define HC_CMD_REG 0x0C
#define ARB_CONFIG_GNT_REG 0x10
#define ARB_CONFIG_CFG_REG 0x12
-#define ARB_CONFIG_REG 0x10
+#define ARB_CONFIG_REG 0x10
#define ISOL_CONFIG_REG 0x18
#define FAULT_STATUS_REG 0x20
#define FAULT_CONFIG_REG 0x24
diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h
index 298ad7f3f4f..0450f405807 100644
--- a/drivers/pci/hotplug/cpqphp.h
+++ b/drivers/pci/hotplug/cpqphp.h
@@ -32,6 +32,7 @@
#include <asm/io.h> /* for read? and write? functions */
#include <linux/delay.h> /* for delays */
#include <linux/mutex.h>
+#include <linux/sched.h> /* for signal_pending() */
#define MY_NAME "cpqphp"
@@ -150,25 +151,25 @@ struct ctrl_reg { /* offset */
/* offsets to the controller registers based on the above structure layout */
enum ctrl_offsets {
- SLOT_RST = offsetof(struct ctrl_reg, slot_RST),
+ SLOT_RST = offsetof(struct ctrl_reg, slot_RST),
SLOT_ENABLE = offsetof(struct ctrl_reg, slot_enable),
MISC = offsetof(struct ctrl_reg, misc),
LED_CONTROL = offsetof(struct ctrl_reg, led_control),
INT_INPUT_CLEAR = offsetof(struct ctrl_reg, int_input_clear),
- INT_MASK = offsetof(struct ctrl_reg, int_mask),
- CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0),
+ INT_MASK = offsetof(struct ctrl_reg, int_mask),
+ CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0),
CTRL_RESERVED1 = offsetof(struct ctrl_reg, reserved1),
CTRL_RESERVED2 = offsetof(struct ctrl_reg, reserved1),
- GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB),
- NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input),
+ GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB),
+ NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input),
CTRL_RESERVED3 = offsetof(struct ctrl_reg, reserved3),
CTRL_RESERVED4 = offsetof(struct ctrl_reg, reserved4),
CTRL_RESERVED5 = offsetof(struct ctrl_reg, reserved5),
CTRL_RESERVED6 = offsetof(struct ctrl_reg, reserved6),
CTRL_RESERVED7 = offsetof(struct ctrl_reg, reserved7),
CTRL_RESERVED8 = offsetof(struct ctrl_reg, reserved8),
- SLOT_MASK = offsetof(struct ctrl_reg, slot_mask),
- CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9),
+ SLOT_MASK = offsetof(struct ctrl_reg, slot_mask),
+ CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9),
CTRL_RESERVED10 = offsetof(struct ctrl_reg, reserved10),
CTRL_RESERVED11 = offsetof(struct ctrl_reg, reserved11),
SLOT_SERR = offsetof(struct ctrl_reg, slot_SERR),
@@ -190,7 +191,9 @@ struct hrt {
u32 reserved2;
} __attribute__ ((packed));
-/* offsets to the hotplug resource table registers based on the above structure layout */
+/* offsets to the hotplug resource table registers based on the above
+ * structure layout
+ */
enum hrt_offsets {
SIG0 = offsetof(struct hrt, sig0),
SIG1 = offsetof(struct hrt, sig1),
@@ -217,18 +220,20 @@ struct slot_rt {
u16 pre_mem_length;
} __attribute__ ((packed));
-/* offsets to the hotplug slot resource table registers based on the above structure layout */
+/* offsets to the hotplug slot resource table registers based on the above
+ * structure layout
+ */
enum slot_rt_offsets {
DEV_FUNC = offsetof(struct slot_rt, dev_func),
- PRIMARY_BUS = offsetof(struct slot_rt, primary_bus),
- SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
- MAX_BUS = offsetof(struct slot_rt, max_bus),
- IO_BASE = offsetof(struct slot_rt, io_base),
- IO_LENGTH = offsetof(struct slot_rt, io_length),
- MEM_BASE = offsetof(struct slot_rt, mem_base),
- MEM_LENGTH = offsetof(struct slot_rt, mem_length),
- PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base),
- PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
+ PRIMARY_BUS = offsetof(struct slot_rt, primary_bus),
+ SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
+ MAX_BUS = offsetof(struct slot_rt, max_bus),
+ IO_BASE = offsetof(struct slot_rt, io_base),
+ IO_LENGTH = offsetof(struct slot_rt, io_length),
+ MEM_BASE = offsetof(struct slot_rt, mem_base),
+ MEM_LENGTH = offsetof(struct slot_rt, mem_length),
+ PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base),
+ PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
};
struct pci_func {
@@ -250,7 +255,7 @@ struct pci_func {
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct timer_list *p_task_event;
- struct pci_dev* pci_dev;
+ struct pci_dev *pci_dev;
};
struct slot {
@@ -273,7 +278,7 @@ struct slot {
};
struct pci_resource {
- struct pci_resource * next;
+ struct pci_resource *next;
u32 base;
u32 length;
};
@@ -286,8 +291,8 @@ struct event_info {
struct controller {
struct controller *next;
u32 ctrl_int_comp;
- struct mutex crit_sect; /* critical section mutex */
- void __iomem *hpc_reg; /* cookie for our pci controller location */
+ struct mutex crit_sect; /* critical section mutex */
+ void __iomem *hpc_reg; /* cookie for our pci controller location */
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
@@ -299,14 +304,12 @@ struct controller {
u8 next_event;
u8 interrupt;
u8 cfgspc_irq;
- u8 bus; /* bus number for the pci hotplug controller */
+ u8 bus; /* bus number for the pci hotplug controller */
u8 rev;
u8 slot_device_offset;
u8 first_slot;
u8 add_support;
u8 push_flag;
- enum pci_bus_speed speed;
- enum pci_bus_speed speed_capability;
u8 push_button; /* 0 = no pushbutton, 1 = pushbutton present */
u8 slot_switch_type; /* 0 = no switch, 1 = switch present */
u8 defeature_PHP; /* 0 = PHP not supported, 1 = PHP supported */
@@ -401,46 +404,51 @@ struct resource_lists {
/* debugfs functions for the hotplug controller info */
-extern void cpqhp_initialize_debugfs (void);
-extern void cpqhp_shutdown_debugfs (void);
-extern void cpqhp_create_debugfs_files (struct controller *ctrl);
-extern void cpqhp_remove_debugfs_files (struct controller *ctrl);
+void cpqhp_initialize_debugfs(void);
+void cpqhp_shutdown_debugfs(void);
+void cpqhp_create_debugfs_files(struct controller *ctrl);
+void cpqhp_remove_debugfs_files(struct controller *ctrl);
/* controller functions */
-extern void cpqhp_pushbutton_thread (unsigned long event_pointer);
-extern irqreturn_t cpqhp_ctrl_intr (int IRQ, void *data);
-extern int cpqhp_find_available_resources (struct controller *ctrl, void __iomem *rom_start);
-extern int cpqhp_event_start_thread (void);
-extern void cpqhp_event_stop_thread (void);
-extern struct pci_func *cpqhp_slot_create (unsigned char busnumber);
-extern struct pci_func *cpqhp_slot_find (unsigned char bus, unsigned char device, unsigned char index);
-extern int cpqhp_process_SI (struct controller *ctrl, struct pci_func *func);
-extern int cpqhp_process_SS (struct controller *ctrl, struct pci_func *func);
-extern int cpqhp_hardware_test (struct controller *ctrl, int test_num);
+void cpqhp_pushbutton_thread(unsigned long event_pointer);
+irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data);
+int cpqhp_find_available_resources(struct controller *ctrl,
+ void __iomem *rom_start);
+int cpqhp_event_start_thread(void);
+void cpqhp_event_stop_thread(void);
+struct pci_func *cpqhp_slot_create(unsigned char busnumber);
+struct pci_func *cpqhp_slot_find(unsigned char bus, unsigned char device,
+ unsigned char index);
+int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func);
+int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func);
+int cpqhp_hardware_test(struct controller *ctrl, int test_num);
/* resource functions */
-extern int cpqhp_resource_sort_and_combine (struct pci_resource **head);
+int cpqhp_resource_sort_and_combine (struct pci_resource **head);
/* pci functions */
-extern int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
-extern int cpqhp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot);
-extern int cpqhp_save_config (struct controller *ctrl, int busnumber, int is_hot_plug);
-extern int cpqhp_save_base_addr_length (struct controller *ctrl, struct pci_func * func);
-extern int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func);
-extern int cpqhp_configure_board (struct controller *ctrl, struct pci_func * func);
-extern int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot);
-extern int cpqhp_valid_replace (struct controller *ctrl, struct pci_func * func);
-extern void cpqhp_destroy_board_resources (struct pci_func * func);
-extern int cpqhp_return_board_resources (struct pci_func * func, struct resource_lists * resources);
-extern void cpqhp_destroy_resource_list (struct resource_lists * resources);
-extern int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func);
-extern int cpqhp_unconfigure_device (struct pci_func* func);
+int cpqhp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
+int cpqhp_get_bus_dev(struct controller *ctrl, u8 *bus_num, u8 *dev_num,
+ u8 slot);
+int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug);
+int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func *func);
+int cpqhp_save_used_resources(struct controller *ctrl, struct pci_func *func);
+int cpqhp_configure_board(struct controller *ctrl, struct pci_func *func);
+int cpqhp_save_slot_config(struct controller *ctrl, struct pci_func *new_slot);
+int cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func);
+void cpqhp_destroy_board_resources(struct pci_func *func);
+int cpqhp_return_board_resources(struct pci_func *func,
+ struct resource_lists *resources);
+void cpqhp_destroy_resource_list(struct resource_lists *resources);
+int cpqhp_configure_device(struct controller *ctrl, struct pci_func *func);
+int cpqhp_unconfigure_device(struct pci_func *func);
/* Global variables */
extern int cpqhp_debug;
extern int cpqhp_legacy_mode;
extern struct controller *cpqhp_ctrl_list;
extern struct pci_func *cpqhp_slot_list[256];
+extern struct irq_routing_table *cpqhp_routing_table;
/* these can be gotten rid of, but for debugging they are purty */
extern u8 cpqhp_nic_irq;
@@ -449,13 +457,18 @@ extern u8 cpqhp_disk_irq;
/* inline functions */
+static inline const char *slot_name(struct slot *slot)
+{
+ return hotplug_slot_name(slot->hotplug_slot);
+}
+
/*
* return_resource
*
* Puts node back in the resource list pointed to by head
- *
*/
-static inline void return_resource(struct pci_resource **head, struct pci_resource *node)
+static inline void return_resource(struct pci_resource **head,
+ struct pci_resource *node)
{
if (!node || !head)
return;
@@ -466,7 +479,7 @@ static inline void return_resource(struct pci_resource **head, struct pci_resour
static inline void set_SOGO(struct controller *ctrl)
{
u16 misc;
-
+
misc = readw(ctrl->hpc_reg + MISC);
misc = (misc | 0x0001) & 0xFFFB;
writew(misc, ctrl->hpc_reg + MISC);
@@ -476,7 +489,7 @@ static inline void set_SOGO(struct controller *ctrl)
static inline void amber_LED_on(struct controller *ctrl, u8 slot)
{
u32 led_control;
-
+
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control |= (0x01010000L << slot);
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
@@ -486,7 +499,7 @@ static inline void amber_LED_on(struct controller *ctrl, u8 slot)
static inline void amber_LED_off(struct controller *ctrl, u8 slot)
{
u32 led_control;
-
+
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control &= ~(0x01010000L << slot);
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
@@ -499,7 +512,7 @@ static inline int read_amber_LED(struct controller *ctrl, u8 slot)
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control &= (0x01010000L << slot);
-
+
return led_control ? 1 : 0;
}
@@ -507,7 +520,7 @@ static inline int read_amber_LED(struct controller *ctrl, u8 slot)
static inline void green_LED_on(struct controller *ctrl, u8 slot)
{
u32 led_control;
-
+
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control |= 0x0101L << slot;
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
@@ -516,7 +529,7 @@ static inline void green_LED_on(struct controller *ctrl, u8 slot)
static inline void green_LED_off(struct controller *ctrl, u8 slot)
{
u32 led_control;
-
+
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control &= ~(0x0101L << slot);
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
@@ -526,7 +539,7 @@ static inline void green_LED_off(struct controller *ctrl, u8 slot)
static inline void green_LED_blink(struct controller *ctrl, u8 slot)
{
u32 led_control;
-
+
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control &= ~(0x0101L << slot);
led_control |= (0x0001L << slot);
@@ -570,22 +583,21 @@ static inline u8 read_slot_enable(struct controller *ctrl)
}
-/*
+/**
* get_controller_speed - find the current frequency/mode of controller.
*
* @ctrl: controller to get frequency/mode for.
*
* Returns controller speed.
- *
*/
static inline u8 get_controller_speed(struct controller *ctrl)
{
u8 curr_freq;
- u16 misc;
-
+ u16 misc;
+
if (ctrl->pcix_support) {
curr_freq = readb(ctrl->hpc_reg + NEXT_CURR_FREQ);
- if ((curr_freq & 0xB0) == 0xB0)
+ if ((curr_freq & 0xB0) == 0xB0)
return PCI_SPEED_133MHz_PCIX;
if ((curr_freq & 0xA0) == 0xA0)
return PCI_SPEED_100MHz_PCIX;
@@ -597,19 +609,18 @@ static inline u8 get_controller_speed(struct controller *ctrl)
return PCI_SPEED_33MHz;
}
- misc = readw(ctrl->hpc_reg + MISC);
- return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
+ misc = readw(ctrl->hpc_reg + MISC);
+ return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
}
-
-/*
+
+/**
* get_adapter_speed - find the max supported frequency/mode of adapter.
*
* @ctrl: hotplug controller.
* @hp_slot: hotplug slot where adapter is installed.
*
* Returns adapter speed.
- *
*/
static inline u8 get_adapter_speed(struct controller *ctrl, u8 hp_slot)
{
@@ -667,14 +678,15 @@ static inline int get_slot_enabled(struct controller *ctrl, struct slot *slot)
}
-static inline int cpq_get_latch_status(struct controller *ctrl, struct slot *slot)
+static inline int cpq_get_latch_status(struct controller *ctrl,
+ struct slot *slot)
{
u32 status;
u8 hp_slot;
hp_slot = slot->device - ctrl->slot_device_offset;
dbg("%s: slot->device = %d, ctrl->slot_device_offset = %d \n",
- __FUNCTION__, slot->device, ctrl->slot_device_offset);
+ __func__, slot->device, ctrl->slot_device_offset);
status = (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot));
@@ -682,7 +694,8 @@ static inline int cpq_get_latch_status(struct controller *ctrl, struct slot *slo
}
-static inline int get_presence_status(struct controller *ctrl, struct slot *slot)
+static inline int get_presence_status(struct controller *ctrl,
+ struct slot *slot)
{
int presence_save = 0;
u8 hp_slot;
@@ -691,25 +704,18 @@ static inline int get_presence_status(struct controller *ctrl, struct slot *slot
hp_slot = slot->device - ctrl->slot_device_offset;
tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
- presence_save = (int) ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> hp_slot) & 0x02;
+ presence_save = (int) ((((~tempdword) >> 23) | ((~tempdword) >> 15))
+ >> hp_slot) & 0x02;
return presence_save;
}
-#define SLOT_NAME_SIZE 10
-
-static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot)
-{
- snprintf(buffer, buffer_size, "%d", slot->number);
-}
-
-
static inline int wait_for_ctrl_irq(struct controller *ctrl)
{
DECLARE_WAITQUEUE(wait, current);
int retval = 0;
- dbg("%s - start\n", __FUNCTION__);
+ dbg("%s - start\n", __func__);
add_wait_queue(&ctrl->queue, &wait);
/* Sleep for up to 1 second to wait for the LED to change. */
msleep_interruptible(1000);
@@ -717,9 +723,16 @@ static inline int wait_for_ctrl_irq(struct controller *ctrl)
if (signal_pending(current))
retval = -EINTR;
- dbg("%s - end\n", __FUNCTION__);
+ dbg("%s - end\n", __func__);
return retval;
}
-#endif
+#include <asm/pci_x86.h>
+static inline int cpqhp_routing_table_length(void)
+{
+ BUG_ON(cpqhp_routing_table == NULL);
+ return ((cpqhp_routing_table->size - sizeof(struct irq_routing_table)) /
+ sizeof(struct irq_info));
+}
+#endif
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
index 74178875b94..4aaee746df8 100644
--- a/drivers/pci/hotplug/cpqphp_core.c
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -25,8 +25,7 @@
* Send feedback to <greg@kroah.com>
*
* Jan 12, 2003 - Added 66/100/133MHz PCI-X support,
- * Torben Mathiasen <torben.mathiasen@hp.com>
- *
+ * Torben Mathiasen <torben.mathiasen@hp.com>
*/
#include <linux/module.h>
@@ -45,7 +44,6 @@
#include "cpqphp.h"
#include "cpqphp_nvram.h"
-#include "../../../arch/x86/pci/pci.h" /* horrible hack showing how processor dependent we are... */
/* Global variables */
@@ -53,13 +51,14 @@ int cpqhp_debug;
int cpqhp_legacy_mode;
struct controller *cpqhp_ctrl_list; /* = NULL */
struct pci_func *cpqhp_slot_list[256];
+struct irq_routing_table *cpqhp_routing_table;
/* local variables */
static void __iomem *smbios_table;
static void __iomem *smbios_start;
static void __iomem *cpqhp_rom_start;
-static int power_mode;
-static int debug;
+static bool power_mode;
+static bool debug;
static int initialized;
#define DRIVER_VERSION "0.9.8"
@@ -78,33 +77,6 @@ MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
#define CPQHPC_MODULE_MINOR 208
-static int one_time_init (void);
-static int set_attention_status (struct hotplug_slot *slot, u8 value);
-static int process_SI (struct hotplug_slot *slot);
-static int process_SS (struct hotplug_slot *slot);
-static int hardware_test (struct hotplug_slot *slot, u32 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_latch_status (struct hotplug_slot *slot, u8 *value);
-static int get_adapter_status (struct hotplug_slot *slot, u8 *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);
-
-static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
- .owner = THIS_MODULE,
- .set_attention_status = set_attention_status,
- .enable_slot = process_SI,
- .disable_slot = process_SS,
- .hardware_test = hardware_test,
- .get_power_status = get_power_status,
- .get_attention_status = get_attention_status,
- .get_latch_status = get_latch_status,
- .get_adapter_status = get_adapter_status,
- .get_max_bus_speed = get_max_bus_speed,
- .get_cur_bus_speed = get_cur_bus_speed,
-};
-
-
static inline int is_slot64bit(struct slot *slot)
{
return (readb(slot->p_sm_slot + SMBIOS_SLOT_WIDTH) == 0x06) ? 1 : 0;
@@ -122,7 +94,7 @@ static inline int is_slot66mhz(struct slot *slot)
*
* Returns pointer to the head of the SMBIOS tables (or %NULL).
*/
-static void __iomem * detect_SMBIOS_pointer(void __iomem *begin, void __iomem *end)
+static void __iomem *detect_SMBIOS_pointer(void __iomem *begin, void __iomem *end)
{
void __iomem *fp;
void __iomem *endp;
@@ -144,7 +116,7 @@ static void __iomem * detect_SMBIOS_pointer(void __iomem *begin, void __iomem *e
break;
}
}
-
+
if (!status)
fp = NULL;
@@ -159,7 +131,7 @@ static void __iomem * detect_SMBIOS_pointer(void __iomem *begin, void __iomem *e
*
* For unexpected switch opens
*/
-static int init_SERR(struct controller * ctrl)
+static int init_SERR(struct controller *ctrl)
{
u32 tempdword;
u32 number_of_slots;
@@ -171,7 +143,7 @@ static int init_SERR(struct controller * ctrl)
tempdword = ctrl->first_slot;
number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
- // Loop through slots
+ /* Loop through slots */
while (number_of_slots) {
physical_slot = tempdword;
writeb(0, ctrl->hpc_reg + SLOT_SERR);
@@ -182,41 +154,42 @@ static int init_SERR(struct controller * ctrl)
return 0;
}
-
-/* nice debugging output */
-static int pci_print_IRQ_route (void)
+static int init_cpqhp_routing_table(void)
{
- struct irq_routing_table *routing_table;
int len;
- int loop;
-
- u8 tbus, tdevice, tslot;
- routing_table = pcibios_get_irq_routing_table();
- if (routing_table == NULL) {
- err("No BIOS Routing Table??? Not good\n");
+ cpqhp_routing_table = pcibios_get_irq_routing_table();
+ if (cpqhp_routing_table == NULL)
return -ENOMEM;
- }
- len = (routing_table->size - sizeof(struct irq_routing_table)) /
- sizeof(struct irq_info);
- // Make sure I got at least one entry
+ len = cpqhp_routing_table_length();
if (len == 0) {
- kfree(routing_table);
+ kfree(cpqhp_routing_table);
+ cpqhp_routing_table = NULL;
return -1;
}
- dbg("bus dev func slot\n");
+ return 0;
+}
+
+/* nice debugging output */
+static void pci_print_IRQ_route(void)
+{
+ int len;
+ int loop;
+ u8 tbus, tdevice, tslot;
+
+ len = cpqhp_routing_table_length();
+ dbg("bus dev func slot\n");
for (loop = 0; loop < len; ++loop) {
- tbus = routing_table->slots[loop].bus;
- tdevice = routing_table->slots[loop].devfn;
- tslot = routing_table->slots[loop].slot;
+ tbus = cpqhp_routing_table->slots[loop].bus;
+ tdevice = cpqhp_routing_table->slots[loop].devfn;
+ tslot = cpqhp_routing_table->slots[loop].slot;
dbg("%d %d %d %d\n", tbus, tdevice >> 3, tdevice & 0x7, tslot);
}
- kfree(routing_table);
- return 0;
+ return;
}
@@ -242,9 +215,9 @@ static void __iomem *get_subsequent_smbios_entry(void __iomem *smbios_start,
void __iomem *p_max;
if (!smbios_table || !curr)
- return(NULL);
+ return NULL;
- // set p_max to the end of the table
+ /* set p_max to the end of the table */
p_max = smbios_start + readw(smbios_table + ST_LENGTH);
p_temp = curr;
@@ -253,20 +226,19 @@ static void __iomem *get_subsequent_smbios_entry(void __iomem *smbios_start,
while ((p_temp < p_max) && !bail) {
/* Look for the double NULL terminator
* The first condition is the previous byte
- * and the second is the curr */
- if (!previous_byte && !(readb(p_temp))) {
+ * and the second is the curr
+ */
+ if (!previous_byte && !(readb(p_temp)))
bail = 1;
- }
previous_byte = readb(p_temp);
p_temp++;
}
- if (p_temp < p_max) {
+ if (p_temp < p_max)
return p_temp;
- } else {
+ else
return NULL;
- }
}
@@ -292,21 +264,18 @@ static void __iomem *get_SMBIOS_entry(void __iomem *smbios_start,
if (!smbios_table)
return NULL;
- if (!previous) {
+ if (!previous)
previous = smbios_start;
- } else {
+ else
previous = get_subsequent_smbios_entry(smbios_start,
smbios_table, previous);
- }
- while (previous) {
- if (readb(previous + SMBIOS_GENERIC_TYPE) != type) {
+ while (previous)
+ if (readb(previous + SMBIOS_GENERIC_TYPE) != type)
previous = get_subsequent_smbios_entry(smbios_start,
smbios_table, previous);
- } else {
+ else
break;
- }
- }
return previous;
}
@@ -315,153 +284,14 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
kfree(slot->hotplug_slot->info);
- kfree(slot->hotplug_slot->name);
kfree(slot->hotplug_slot);
kfree(slot);
}
-static int ctrl_slot_setup(struct controller *ctrl,
- void __iomem *smbios_start,
- void __iomem *smbios_table)
-{
- struct slot *slot;
- struct hotplug_slot *hotplug_slot;
- struct hotplug_slot_info *hotplug_slot_info;
- u8 number_of_slots;
- u8 slot_device;
- u8 slot_number;
- u8 ctrl_slot;
- u32 tempdword;
- void __iomem *slot_entry= NULL;
- int result = -ENOMEM;
-
- dbg("%s\n", __FUNCTION__);
-
- tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
-
- number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
- slot_device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
- slot_number = ctrl->first_slot;
-
- while (number_of_slots) {
- slot = kzalloc(sizeof(*slot), GFP_KERNEL);
- if (!slot)
- goto error;
-
- slot->hotplug_slot = kzalloc(sizeof(*(slot->hotplug_slot)),
- GFP_KERNEL);
- if (!slot->hotplug_slot)
- goto error_slot;
- hotplug_slot = slot->hotplug_slot;
-
- hotplug_slot->info =
- kzalloc(sizeof(*(hotplug_slot->info)),
- GFP_KERNEL);
- if (!hotplug_slot->info)
- goto error_hpslot;
- hotplug_slot_info = hotplug_slot->info;
- hotplug_slot->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
-
- if (!hotplug_slot->name)
- goto error_info;
-
- slot->ctrl = ctrl;
- slot->bus = ctrl->bus;
- slot->device = slot_device;
- slot->number = slot_number;
- dbg("slot->number = %d\n", slot->number);
-
- slot_entry = get_SMBIOS_entry(smbios_start, smbios_table, 9,
- slot_entry);
-
- while (slot_entry && (readw(slot_entry + SMBIOS_SLOT_NUMBER) !=
- slot->number)) {
- slot_entry = get_SMBIOS_entry(smbios_start,
- smbios_table, 9, slot_entry);
- }
-
- slot->p_sm_slot = slot_entry;
-
- init_timer(&slot->task_event);
- slot->task_event.expires = jiffies + 5 * HZ;
- slot->task_event.function = cpqhp_pushbutton_thread;
-
- //FIXME: these capabilities aren't used but if they are
- // they need to be correctly implemented
- slot->capabilities |= PCISLOT_REPLACE_SUPPORTED;
- slot->capabilities |= PCISLOT_INTERLOCK_SUPPORTED;
-
- if (is_slot64bit(slot))
- slot->capabilities |= PCISLOT_64_BIT_SUPPORTED;
- if (is_slot66mhz(slot))
- slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED;
- if (ctrl->speed == PCI_SPEED_66MHz)
- slot->capabilities |= PCISLOT_66_MHZ_OPERATION;
-
- ctrl_slot =
- slot_device - (readb(ctrl->hpc_reg + SLOT_MASK) >> 4);
-
- // Check presence
- slot->capabilities |=
- ((((~tempdword) >> 23) |
- ((~tempdword) >> 15)) >> ctrl_slot) & 0x02;
- // Check the switch state
- slot->capabilities |=
- ((~tempdword & 0xFF) >> ctrl_slot) & 0x01;
- // Check the slot enable
- slot->capabilities |=
- ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04;
-
- /* register this slot with the hotplug pci core */
- hotplug_slot->release = &release_slot;
- hotplug_slot->private = slot;
- make_slot_name(hotplug_slot->name, SLOT_NAME_SIZE, slot);
- hotplug_slot->ops = &cpqphp_hotplug_slot_ops;
-
- hotplug_slot_info->power_status = get_slot_enabled(ctrl, slot);
- hotplug_slot_info->attention_status =
- cpq_get_attention_status(ctrl, slot);
- hotplug_slot_info->latch_status =
- cpq_get_latch_status(ctrl, slot);
- hotplug_slot_info->adapter_status =
- get_presence_status(ctrl, slot);
-
- dbg("registering bus %d, dev %d, number %d, "
- "ctrl->slot_device_offset %d, slot %d\n",
- slot->bus, slot->device,
- slot->number, ctrl->slot_device_offset,
- slot_number);
- result = pci_hp_register(hotplug_slot);
- if (result) {
- err("pci_hp_register failed with error %d\n", result);
- goto error_name;
- }
-
- slot->next = ctrl->slot;
- ctrl->slot = slot;
-
- number_of_slots--;
- slot_device++;
- slot_number++;
- }
-
- return 0;
-error_name:
- kfree(hotplug_slot->name);
-error_info:
- kfree(hotplug_slot_info);
-error_hpslot:
- kfree(hotplug_slot);
-error_slot:
- kfree(slot);
-error:
- return result;
-}
-
-static int ctrl_slot_cleanup (struct controller * ctrl)
+static int ctrl_slot_cleanup (struct controller *ctrl)
{
struct slot *old_slot, *next_slot;
@@ -477,76 +307,61 @@ static int ctrl_slot_cleanup (struct controller * ctrl)
cpqhp_remove_debugfs_files(ctrl);
- //Free IRQ associated with hot plug device
+ /* Free IRQ associated with hot plug device */
free_irq(ctrl->interrupt, ctrl);
- //Unmap the memory
+ /* Unmap the memory */
iounmap(ctrl->hpc_reg);
- //Finally reclaim PCI mem
+ /* Finally reclaim PCI mem */
release_mem_region(pci_resource_start(ctrl->pci_dev, 0),
pci_resource_len(ctrl->pci_dev, 0));
- return(0);
+ return 0;
}
-//============================================================================
-// function: get_slot_mapping
-//
-// Description: Attempts to determine a logical slot mapping for a PCI
-// device. Won't work for more than one PCI-PCI bridge
-// in a slot.
-//
-// Input: u8 bus_num - bus number of PCI device
-// u8 dev_num - device number of PCI device
-// u8 *slot - Pointer to u8 where slot number will
-// be returned
-//
-// Output: SUCCESS or FAILURE
-//=============================================================================
+/**
+ * get_slot_mapping - determine logical slot mapping for PCI device
+ *
+ * Won't work for more than one PCI-PCI bridge in a slot.
+ *
+ * @bus_num - bus number of PCI device
+ * @dev_num - device number of PCI device
+ * @slot - Pointer to u8 where slot number will be returned
+ *
+ * Output: SUCCESS or FAILURE
+ */
static int
get_slot_mapping(struct pci_bus *bus, u8 bus_num, u8 dev_num, u8 *slot)
{
- struct irq_routing_table *PCIIRQRoutingInfoLength;
u32 work;
long len;
long loop;
u8 tbus, tdevice, tslot, bridgeSlot;
- dbg("%s: %p, %d, %d, %p\n", __FUNCTION__, bus, bus_num, dev_num, slot);
+ dbg("%s: %p, %d, %d, %p\n", __func__, bus, bus_num, dev_num, slot);
bridgeSlot = 0xFF;
- PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table();
- if (!PCIIRQRoutingInfoLength)
- return -1;
-
- len = (PCIIRQRoutingInfoLength->size -
- sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
- // Make sure I got at least one entry
- if (len == 0) {
- kfree(PCIIRQRoutingInfoLength);
- return -1;
- }
-
+ len = cpqhp_routing_table_length();
for (loop = 0; loop < len; ++loop) {
- tbus = PCIIRQRoutingInfoLength->slots[loop].bus;
- tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn >> 3;
- tslot = PCIIRQRoutingInfoLength->slots[loop].slot;
+ tbus = cpqhp_routing_table->slots[loop].bus;
+ tdevice = cpqhp_routing_table->slots[loop].devfn >> 3;
+ tslot = cpqhp_routing_table->slots[loop].slot;
if ((tbus == bus_num) && (tdevice == dev_num)) {
*slot = tslot;
- kfree(PCIIRQRoutingInfoLength);
return 0;
} else {
/* Did not get a match on the target PCI device. Check
- * if the current IRQ table entry is a PCI-to-PCI bridge
- * device. If so, and it's secondary bus matches the
- * bus number for the target device, I need to save the
- * bridge's slot number. If I can not find an entry for
- * the target device, I will have to assume it's on the
- * other side of the bridge, and assign it the bridge's
- * slot. */
+ * if the current IRQ table entry is a PCI-to-PCI
+ * bridge device. If so, and it's secondary bus
+ * matches the bus number for the target device, I need
+ * to save the bridge's slot number. If I can not find
+ * an entry for the target device, I will have to
+ * assume it's on the other side of the bridge, and
+ * assign it the bridge's slot.
+ */
bus->number = tbus;
pci_bus_read_config_dword(bus, PCI_DEVFN(tdevice, 0),
PCI_CLASS_REVISION, &work);
@@ -556,25 +371,23 @@ get_slot_mapping(struct pci_bus *bus, u8 bus_num, u8 dev_num, u8 *slot)
PCI_DEVFN(tdevice, 0),
PCI_PRIMARY_BUS, &work);
// See if bridge's secondary bus matches target bus.
- if (((work >> 8) & 0x000000FF) == (long) bus_num) {
+ if (((work >> 8) & 0x000000FF) == (long) bus_num)
bridgeSlot = tslot;
- }
}
}
}
- // If we got here, we didn't find an entry in the IRQ mapping table
- // for the target PCI device. If we did determine that the target
- // device is on the other side of a PCI-to-PCI bridge, return the
- // slot number for the bridge.
+ /* If we got here, we didn't find an entry in the IRQ mapping table for
+ * the target PCI device. If we did determine that the target device
+ * is on the other side of a PCI-to-PCI bridge, return the slot number
+ * for the bridge.
+ */
if (bridgeSlot != 0xFF) {
*slot = bridgeSlot;
- kfree(PCIIRQRoutingInfoLength);
return 0;
}
- kfree(PCIIRQRoutingInfoLength);
- // Couldn't find an entry in the routing table for this PCI device
+ /* Couldn't find an entry in the routing table for this PCI device */
return -1;
}
@@ -592,32 +405,32 @@ cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func,
u8 hp_slot;
if (func == NULL)
- return(1);
+ return 1;
hp_slot = func->device - ctrl->slot_device_offset;
- // Wait for exclusive access to hardware
+ /* Wait for exclusive access to hardware */
mutex_lock(&ctrl->crit_sect);
- if (status == 1) {
+ if (status == 1)
amber_LED_on (ctrl, hp_slot);
- } else if (status == 0) {
+ else if (status == 0)
amber_LED_off (ctrl, hp_slot);
- } else {
- // Done with exclusive hardware access
+ else {
+ /* Done with exclusive hardware access */
mutex_unlock(&ctrl->crit_sect);
- return(1);
+ return 1;
}
set_SOGO(ctrl);
- // Wait for SOBS to be unset
+ /* Wait for SOBS to be unset */
wait_for_ctrl_irq (ctrl);
- // Done with exclusive hardware access
+ /* Done with exclusive hardware access */
mutex_unlock(&ctrl->crit_sect);
- return(0);
+ return 0;
}
@@ -636,7 +449,7 @@ static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
u8 device;
u8 function;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
return -ENODEV;
@@ -663,7 +476,7 @@ static int process_SI(struct hotplug_slot *hotplug_slot)
u8 device;
u8 function;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
return -ENODEV;
@@ -695,7 +508,7 @@ static int process_SS(struct hotplug_slot *hotplug_slot)
u8 device;
u8 function;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
return -ENODEV;
@@ -708,7 +521,7 @@ static int process_SS(struct hotplug_slot *hotplug_slot)
if (!slot_func)
return -ENODEV;
- dbg("In %s, slot_func = %p, ctrl = %p\n", __FUNCTION__, slot_func, ctrl);
+ dbg("In %s, slot_func = %p, ctrl = %p\n", __func__, slot_func, ctrl);
return cpqhp_process_SS(ctrl, slot_func);
}
@@ -718,9 +531,9 @@ static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
struct slot *slot = hotplug_slot->private;
struct controller *ctrl = slot->ctrl;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
- return cpqhp_hardware_test(ctrl, value);
+ return cpqhp_hardware_test(ctrl, value);
}
@@ -729,7 +542,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
struct slot *slot = hotplug_slot->private;
struct controller *ctrl = slot->ctrl;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
*value = get_slot_enabled(ctrl, slot);
return 0;
@@ -739,8 +552,8 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
struct controller *ctrl = slot->ctrl;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
*value = cpq_get_attention_status(ctrl, slot);
return 0;
@@ -751,7 +564,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
struct slot *slot = hotplug_slot->private;
struct controller *ctrl = slot->ctrl;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
*value = cpq_get_latch_status(ctrl, slot);
@@ -763,35 +576,239 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
struct slot *slot = hotplug_slot->private;
struct controller *ctrl = slot->ctrl;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
*value = get_presence_status(ctrl, slot);
return 0;
}
-static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
+ .set_attention_status = set_attention_status,
+ .enable_slot = process_SI,
+ .disable_slot = process_SS,
+ .hardware_test = hardware_test,
+ .get_power_status = get_power_status,
+ .get_attention_status = get_attention_status,
+ .get_latch_status = get_latch_status,
+ .get_adapter_status = get_adapter_status,
+};
+
+#define SLOT_NAME_SIZE 10
+
+static int ctrl_slot_setup(struct controller *ctrl,
+ void __iomem *smbios_start,
+ void __iomem *smbios_table)
{
- struct slot *slot = hotplug_slot->private;
- struct controller *ctrl = slot->ctrl;
+ struct slot *slot;
+ struct hotplug_slot *hotplug_slot;
+ struct hotplug_slot_info *hotplug_slot_info;
+ struct pci_bus *bus = ctrl->pci_bus;
+ u8 number_of_slots;
+ u8 slot_device;
+ u8 slot_number;
+ u8 ctrl_slot;
+ u32 tempdword;
+ char name[SLOT_NAME_SIZE];
+ void __iomem *slot_entry= NULL;
+ int result;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s\n", __func__);
- *value = ctrl->speed_capability;
+ tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+ number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
+ slot_device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
+ slot_number = ctrl->first_slot;
+
+ while (number_of_slots) {
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot) {
+ result = -ENOMEM;
+ goto error;
+ }
+
+ slot->hotplug_slot = kzalloc(sizeof(*(slot->hotplug_slot)),
+ GFP_KERNEL);
+ if (!slot->hotplug_slot) {
+ result = -ENOMEM;
+ goto error_slot;
+ }
+ hotplug_slot = slot->hotplug_slot;
+
+ hotplug_slot->info = kzalloc(sizeof(*(hotplug_slot->info)),
+ GFP_KERNEL);
+ if (!hotplug_slot->info) {
+ result = -ENOMEM;
+ goto error_hpslot;
+ }
+ hotplug_slot_info = hotplug_slot->info;
+
+ slot->ctrl = ctrl;
+ slot->bus = ctrl->bus;
+ slot->device = slot_device;
+ slot->number = slot_number;
+ dbg("slot->number = %u\n", slot->number);
+
+ slot_entry = get_SMBIOS_entry(smbios_start, smbios_table, 9,
+ slot_entry);
+
+ while (slot_entry && (readw(slot_entry + SMBIOS_SLOT_NUMBER) !=
+ slot->number)) {
+ slot_entry = get_SMBIOS_entry(smbios_start,
+ smbios_table, 9, slot_entry);
+ }
+
+ slot->p_sm_slot = slot_entry;
+
+ init_timer(&slot->task_event);
+ slot->task_event.expires = jiffies + 5 * HZ;
+ slot->task_event.function = cpqhp_pushbutton_thread;
+
+ /*FIXME: these capabilities aren't used but if they are
+ * they need to be correctly implemented
+ */
+ slot->capabilities |= PCISLOT_REPLACE_SUPPORTED;
+ slot->capabilities |= PCISLOT_INTERLOCK_SUPPORTED;
+
+ if (is_slot64bit(slot))
+ slot->capabilities |= PCISLOT_64_BIT_SUPPORTED;
+ if (is_slot66mhz(slot))
+ slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED;
+ if (bus->cur_bus_speed == PCI_SPEED_66MHz)
+ slot->capabilities |= PCISLOT_66_MHZ_OPERATION;
+
+ ctrl_slot =
+ slot_device - (readb(ctrl->hpc_reg + SLOT_MASK) >> 4);
+
+ /* Check presence */
+ slot->capabilities |=
+ ((((~tempdword) >> 23) |
+ ((~tempdword) >> 15)) >> ctrl_slot) & 0x02;
+ /* Check the switch state */
+ slot->capabilities |=
+ ((~tempdword & 0xFF) >> ctrl_slot) & 0x01;
+ /* Check the slot enable */
+ slot->capabilities |=
+ ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04;
+
+ /* register this slot with the hotplug pci core */
+ hotplug_slot->release = &release_slot;
+ hotplug_slot->private = slot;
+ snprintf(name, SLOT_NAME_SIZE, "%u", slot->number);
+ hotplug_slot->ops = &cpqphp_hotplug_slot_ops;
+
+ hotplug_slot_info->power_status = get_slot_enabled(ctrl, slot);
+ hotplug_slot_info->attention_status =
+ cpq_get_attention_status(ctrl, slot);
+ hotplug_slot_info->latch_status =
+ cpq_get_latch_status(ctrl, slot);
+ hotplug_slot_info->adapter_status =
+ get_presence_status(ctrl, slot);
+
+ dbg("registering bus %d, dev %d, number %d, ctrl->slot_device_offset %d, slot %d\n",
+ slot->bus, slot->device,
+ slot->number, ctrl->slot_device_offset,
+ slot_number);
+ result = pci_hp_register(hotplug_slot,
+ ctrl->pci_dev->bus,
+ slot->device,
+ name);
+ if (result) {
+ err("pci_hp_register failed with error %d\n", result);
+ goto error_info;
+ }
+
+ slot->next = ctrl->slot;
+ ctrl->slot = slot;
+
+ number_of_slots--;
+ slot_device++;
+ slot_number++;
+ }
return 0;
+error_info:
+ kfree(hotplug_slot_info);
+error_hpslot:
+ kfree(hotplug_slot);
+error_slot:
+ kfree(slot);
+error:
+ return result;
}
-static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+static int one_time_init(void)
{
- struct slot *slot = hotplug_slot->private;
- struct controller *ctrl = slot->ctrl;
+ int loop;
+ int retval = 0;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ if (initialized)
+ return 0;
- *value = ctrl->speed;
+ power_mode = 0;
- return 0;
+ retval = init_cpqhp_routing_table();
+ if (retval)
+ goto error;
+
+ if (cpqhp_debug)
+ pci_print_IRQ_route();
+
+ dbg("Initialize + Start the notification mechanism \n");
+
+ retval = cpqhp_event_start_thread();
+ if (retval)
+ goto error;
+
+ dbg("Initialize slot lists\n");
+ for (loop = 0; loop < 256; loop++)
+ cpqhp_slot_list[loop] = NULL;
+
+ /* FIXME: We also need to hook the NMI handler eventually.
+ * this also needs to be worked with Christoph
+ * register_NMI_handler();
+ */
+ /* Map rom address */
+ cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN);
+ if (!cpqhp_rom_start) {
+ err ("Could not ioremap memory region for ROM\n");
+ retval = -EIO;
+ goto error;
+ }
+
+ /* Now, map the int15 entry point if we are on compaq specific
+ * hardware
+ */
+ compaq_nvram_init(cpqhp_rom_start);
+
+ /* Map smbios table entry point structure */
+ smbios_table = detect_SMBIOS_pointer(cpqhp_rom_start,
+ cpqhp_rom_start + ROM_PHY_LEN);
+ if (!smbios_table) {
+ err ("Could not find the SMBIOS pointer in memory\n");
+ retval = -EIO;
+ goto error_rom_start;
+ }
+
+ smbios_start = ioremap(readl(smbios_table + ST_ADDRESS),
+ readw(smbios_table + ST_LENGTH));
+ if (!smbios_start) {
+ err ("Could not ioremap memory region taken from SMBIOS values\n");
+ retval = -EIO;
+ goto error_smbios_start;
+ }
+
+ initialized = 1;
+
+ return retval;
+
+error_smbios_start:
+ iounmap(smbios_start);
+error_rom_start:
+ iounmap(cpqhp_rom_start);
+error:
+ return retval;
}
static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -807,6 +824,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
u32 rc;
struct controller *ctrl;
struct pci_func *func;
+ struct pci_bus *bus;
int err;
err = pci_enable_device(pdev);
@@ -816,9 +834,19 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return err;
}
- // Need to read VID early b/c it's used to differentiate CPQ and INTC discovery
- rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id);
- if (rc || ((vendor_id != PCI_VENDOR_ID_COMPAQ) && (vendor_id != PCI_VENDOR_ID_INTEL))) {
+ bus = pdev->subordinate;
+ if (!bus) {
+ dev_notice(&pdev->dev, "the device is not a bridge, skipping\n");
+ rc = -ENODEV;
+ goto err_disable_device;
+ }
+
+ /* Need to read VID early b/c it's used to differentiate CPQ and INTC
+ * discovery
+ */
+ vendor_id = pdev->vendor;
+ if ((vendor_id != PCI_VENDOR_ID_COMPAQ) &&
+ (vendor_id != PCI_VENDOR_ID_INTEL)) {
err(msg_HPC_non_compaq_or_intel);
rc = -ENODEV;
goto err_disable_device;
@@ -832,223 +860,207 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_disable_device;
}
- /* Check for the proper subsytem ID's
- * Intel uses a different SSID programming model than Compaq.
+ /* Check for the proper subsystem IDs
+ * Intel uses a different SSID programming model than Compaq.
* For Intel, each SSID bit identifies a PHP capability.
- * Also Intel HPC's may have RID=0.
+ * Also Intel HPCs may have RID=0.
*/
- if ((pdev->revision > 2) || (vendor_id == PCI_VENDOR_ID_INTEL)) {
- // TODO: This code can be made to support non-Compaq or Intel subsystem IDs
- rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vid);
- if (rc) {
- err("%s : pci_read_config_word failed\n", __FUNCTION__);
- goto err_disable_device;
- }
- dbg("Subsystem Vendor ID: %x\n", subsystem_vid);
- if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) {
- err(msg_HPC_non_compaq_or_intel);
- rc = -ENODEV;
- goto err_disable_device;
- }
+ if ((pdev->revision <= 2) && (vendor_id != PCI_VENDOR_ID_INTEL)) {
+ err(msg_HPC_not_supported);
+ return -ENODEV;
+ }
- ctrl = kzalloc(sizeof(struct controller), GFP_KERNEL);
- if (!ctrl) {
- err("%s : out of memory\n", __FUNCTION__);
- rc = -ENOMEM;
- goto err_disable_device;
- }
+ /* TODO: This code can be made to support non-Compaq or Intel
+ * subsystem IDs
+ */
+ subsystem_vid = pdev->subsystem_vendor;
+ dbg("Subsystem Vendor ID: %x\n", subsystem_vid);
+ if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) {
+ err(msg_HPC_non_compaq_or_intel);
+ rc = -ENODEV;
+ goto err_disable_device;
+ }
- rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsystem_deviceid);
- if (rc) {
- err("%s : pci_read_config_word failed\n", __FUNCTION__);
- goto err_free_ctrl;
- }
+ ctrl = kzalloc(sizeof(struct controller), GFP_KERNEL);
+ if (!ctrl) {
+ err("%s : out of memory\n", __func__);
+ rc = -ENOMEM;
+ goto err_disable_device;
+ }
- info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid);
-
- /* Set Vendor ID, so it can be accessed later from other functions */
- ctrl->vendor_id = vendor_id;
-
- switch (subsystem_vid) {
- case PCI_VENDOR_ID_COMPAQ:
- if (pdev->revision >= 0x13) { /* CIOBX */
- ctrl->push_flag = 1;
- ctrl->slot_switch_type = 1;
- ctrl->push_button = 1;
- ctrl->pci_config_space = 1;
- ctrl->defeature_PHP = 1;
- ctrl->pcix_support = 1;
- ctrl->pcix_speed_capability = 1;
- pci_read_config_byte(pdev, 0x41, &bus_cap);
- if (bus_cap & 0x80) {
- dbg("bus max supports 133MHz PCI-X\n");
- ctrl->speed_capability = PCI_SPEED_133MHz_PCIX;
- break;
- }
- if (bus_cap & 0x40) {
- dbg("bus max supports 100MHz PCI-X\n");
- ctrl->speed_capability = PCI_SPEED_100MHz_PCIX;
- break;
- }
- if (bus_cap & 20) {
- dbg("bus max supports 66MHz PCI-X\n");
- ctrl->speed_capability = PCI_SPEED_66MHz_PCIX;
- break;
- }
- if (bus_cap & 10) {
- dbg("bus max supports 66MHz PCI\n");
- ctrl->speed_capability = PCI_SPEED_66MHz;
- break;
- }
-
- break;
- }
-
- switch (subsystem_deviceid) {
- case PCI_SUB_HPC_ID:
- /* Original 6500/7000 implementation */
- ctrl->slot_switch_type = 1;
- ctrl->speed_capability = PCI_SPEED_33MHz;
- ctrl->push_button = 0;
- ctrl->pci_config_space = 1;
- ctrl->defeature_PHP = 1;
- ctrl->pcix_support = 0;
- ctrl->pcix_speed_capability = 0;
- break;
- case PCI_SUB_HPC_ID2:
- /* First Pushbutton implementation */
- ctrl->push_flag = 1;
- ctrl->slot_switch_type = 1;
- ctrl->speed_capability = PCI_SPEED_33MHz;
- ctrl->push_button = 1;
- ctrl->pci_config_space = 1;
- ctrl->defeature_PHP = 1;
- ctrl->pcix_support = 0;
- ctrl->pcix_speed_capability = 0;
- break;
- case PCI_SUB_HPC_ID_INTC:
- /* Third party (6500/7000) */
- ctrl->slot_switch_type = 1;
- ctrl->speed_capability = PCI_SPEED_33MHz;
- ctrl->push_button = 0;
- ctrl->pci_config_space = 1;
- ctrl->defeature_PHP = 1;
- ctrl->pcix_support = 0;
- ctrl->pcix_speed_capability = 0;
- break;
- case PCI_SUB_HPC_ID3:
- /* First 66 Mhz implementation */
- ctrl->push_flag = 1;
- ctrl->slot_switch_type = 1;
- ctrl->speed_capability = PCI_SPEED_66MHz;
- ctrl->push_button = 1;
- ctrl->pci_config_space = 1;
- ctrl->defeature_PHP = 1;
- ctrl->pcix_support = 0;
- ctrl->pcix_speed_capability = 0;
- break;
- case PCI_SUB_HPC_ID4:
- /* First PCI-X implementation, 100MHz */
- ctrl->push_flag = 1;
- ctrl->slot_switch_type = 1;
- ctrl->speed_capability = PCI_SPEED_100MHz_PCIX;
- ctrl->push_button = 1;
- ctrl->pci_config_space = 1;
- ctrl->defeature_PHP = 1;
- ctrl->pcix_support = 1;
- ctrl->pcix_speed_capability = 0;
- break;
- default:
- err(msg_HPC_not_supported);
- rc = -ENODEV;
- goto err_free_ctrl;
- }
- break;
+ subsystem_deviceid = pdev->subsystem_device;
+
+ info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid);
- case PCI_VENDOR_ID_INTEL:
- /* Check for speed capability (0=33, 1=66) */
- if (subsystem_deviceid & 0x0001) {
- ctrl->speed_capability = PCI_SPEED_66MHz;
- } else {
- ctrl->speed_capability = PCI_SPEED_33MHz;
- }
-
- /* Check for push button */
- if (subsystem_deviceid & 0x0002) {
- /* no push button */
- ctrl->push_button = 0;
- } else {
- /* push button supported */
- ctrl->push_button = 1;
- }
-
- /* Check for slot switch type (0=mechanical, 1=not mechanical) */
- if (subsystem_deviceid & 0x0004) {
- /* no switch */
- ctrl->slot_switch_type = 0;
- } else {
- /* switch */
- ctrl->slot_switch_type = 1;
- }
-
- /* PHP Status (0=De-feature PHP, 1=Normal operation) */
- if (subsystem_deviceid & 0x0008) {
- ctrl->defeature_PHP = 1; // PHP supported
- } else {
- ctrl->defeature_PHP = 0; // PHP not supported
- }
-
- /* Alternate Base Address Register Interface (0=not supported, 1=supported) */
- if (subsystem_deviceid & 0x0010) {
- ctrl->alternate_base_address = 1; // supported
- } else {
- ctrl->alternate_base_address = 0; // not supported
- }
-
- /* PCI Config Space Index (0=not supported, 1=supported) */
- if (subsystem_deviceid & 0x0020) {
- ctrl->pci_config_space = 1; // supported
- } else {
- ctrl->pci_config_space = 0; // not supported
- }
-
- /* PCI-X support */
- if (subsystem_deviceid & 0x0080) {
- /* PCI-X capable */
- ctrl->pcix_support = 1;
- /* Frequency of operation in PCI-X mode */
- if (subsystem_deviceid & 0x0040) {
- /* 133MHz PCI-X if bit 7 is 1 */
- ctrl->pcix_speed_capability = 1;
- } else {
- /* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */
- /* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */
- ctrl->pcix_speed_capability = 0;
- }
- } else {
- /* Conventional PCI */
- ctrl->pcix_support = 0;
- ctrl->pcix_speed_capability = 0;
- }
+ /* Set Vendor ID, so it can be accessed later from other
+ * functions
+ */
+ ctrl->vendor_id = vendor_id;
+
+ switch (subsystem_vid) {
+ case PCI_VENDOR_ID_COMPAQ:
+ if (pdev->revision >= 0x13) { /* CIOBX */
+ ctrl->push_flag = 1;
+ ctrl->slot_switch_type = 1;
+ ctrl->push_button = 1;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 1;
+ ctrl->pcix_speed_capability = 1;
+ pci_read_config_byte(pdev, 0x41, &bus_cap);
+ if (bus_cap & 0x80) {
+ dbg("bus max supports 133MHz PCI-X\n");
+ bus->max_bus_speed = PCI_SPEED_133MHz_PCIX;
break;
+ }
+ if (bus_cap & 0x40) {
+ dbg("bus max supports 100MHz PCI-X\n");
+ bus->max_bus_speed = PCI_SPEED_100MHz_PCIX;
+ break;
+ }
+ if (bus_cap & 0x20) {
+ dbg("bus max supports 66MHz PCI-X\n");
+ bus->max_bus_speed = PCI_SPEED_66MHz_PCIX;
+ break;
+ }
+ if (bus_cap & 0x10) {
+ dbg("bus max supports 66MHz PCI\n");
+ bus->max_bus_speed = PCI_SPEED_66MHz;
+ break;
+ }
- default:
- err(msg_HPC_not_supported);
- rc = -ENODEV;
- goto err_free_ctrl;
+ break;
}
- } else {
+ switch (subsystem_deviceid) {
+ case PCI_SUB_HPC_ID:
+ /* Original 6500/7000 implementation */
+ ctrl->slot_switch_type = 1;
+ bus->max_bus_speed = PCI_SPEED_33MHz;
+ ctrl->push_button = 0;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 0;
+ ctrl->pcix_speed_capability = 0;
+ break;
+ case PCI_SUB_HPC_ID2:
+ /* First Pushbutton implementation */
+ ctrl->push_flag = 1;
+ ctrl->slot_switch_type = 1;
+ bus->max_bus_speed = PCI_SPEED_33MHz;
+ ctrl->push_button = 1;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 0;
+ ctrl->pcix_speed_capability = 0;
+ break;
+ case PCI_SUB_HPC_ID_INTC:
+ /* Third party (6500/7000) */
+ ctrl->slot_switch_type = 1;
+ bus->max_bus_speed = PCI_SPEED_33MHz;
+ ctrl->push_button = 0;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 0;
+ ctrl->pcix_speed_capability = 0;
+ break;
+ case PCI_SUB_HPC_ID3:
+ /* First 66 Mhz implementation */
+ ctrl->push_flag = 1;
+ ctrl->slot_switch_type = 1;
+ bus->max_bus_speed = PCI_SPEED_66MHz;
+ ctrl->push_button = 1;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 0;
+ ctrl->pcix_speed_capability = 0;
+ break;
+ case PCI_SUB_HPC_ID4:
+ /* First PCI-X implementation, 100MHz */
+ ctrl->push_flag = 1;
+ ctrl->slot_switch_type = 1;
+ bus->max_bus_speed = PCI_SPEED_100MHz_PCIX;
+ ctrl->push_button = 1;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 1;
+ ctrl->pcix_speed_capability = 0;
+ break;
+ default:
+ err(msg_HPC_not_supported);
+ rc = -ENODEV;
+ goto err_free_ctrl;
+ }
+ break;
+
+ case PCI_VENDOR_ID_INTEL:
+ /* Check for speed capability (0=33, 1=66) */
+ if (subsystem_deviceid & 0x0001)
+ bus->max_bus_speed = PCI_SPEED_66MHz;
+ else
+ bus->max_bus_speed = PCI_SPEED_33MHz;
+
+ /* Check for push button */
+ if (subsystem_deviceid & 0x0002)
+ ctrl->push_button = 0;
+ else
+ ctrl->push_button = 1;
+
+ /* Check for slot switch type (0=mechanical, 1=not mechanical) */
+ if (subsystem_deviceid & 0x0004)
+ ctrl->slot_switch_type = 0;
+ else
+ ctrl->slot_switch_type = 1;
+
+ /* PHP Status (0=De-feature PHP, 1=Normal operation) */
+ if (subsystem_deviceid & 0x0008)
+ ctrl->defeature_PHP = 1; /* PHP supported */
+ else
+ ctrl->defeature_PHP = 0; /* PHP not supported */
+
+ /* Alternate Base Address Register Interface
+ * (0=not supported, 1=supported)
+ */
+ if (subsystem_deviceid & 0x0010)
+ ctrl->alternate_base_address = 1;
+ else
+ ctrl->alternate_base_address = 0;
+
+ /* PCI Config Space Index (0=not supported, 1=supported) */
+ if (subsystem_deviceid & 0x0020)
+ ctrl->pci_config_space = 1;
+ else
+ ctrl->pci_config_space = 0;
+
+ /* PCI-X support */
+ if (subsystem_deviceid & 0x0080) {
+ ctrl->pcix_support = 1;
+ if (subsystem_deviceid & 0x0040)
+ /* 133MHz PCI-X if bit 7 is 1 */
+ ctrl->pcix_speed_capability = 1;
+ else
+ /* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */
+ /* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */
+ ctrl->pcix_speed_capability = 0;
+ } else {
+ /* Conventional PCI */
+ ctrl->pcix_support = 0;
+ ctrl->pcix_speed_capability = 0;
+ }
+ break;
+
+ default:
err(msg_HPC_not_supported);
- return -ENODEV;
+ rc = -ENODEV;
+ goto err_free_ctrl;
}
- // Tell the user that we found one.
+ /* Tell the user that we found one. */
info("Initializing the PCI hot plug controller residing on PCI bus %d\n",
pdev->bus->number);
dbg("Hotplug controller capabilities:\n");
- dbg(" speed_capability %d\n", ctrl->speed_capability);
+ dbg(" speed_capability %d\n", bus->max_bus_speed);
dbg(" slot_switch_type %s\n", ctrl->slot_switch_type ?
"switch present" : "no switch");
dbg(" defeature_PHP %s\n", ctrl->defeature_PHP ?
@@ -1067,13 +1079,12 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* make our own copy of the pci bus structure,
* as we like tweaking it a lot */
- ctrl->pci_bus = kmalloc(sizeof(*ctrl->pci_bus), GFP_KERNEL);
+ ctrl->pci_bus = kmemdup(pdev->bus, sizeof(*ctrl->pci_bus), GFP_KERNEL);
if (!ctrl->pci_bus) {
err("out of memory\n");
rc = -ENOMEM;
goto err_free_ctrl;
}
- memcpy(ctrl->pci_bus, pdev->bus, sizeof(*ctrl->pci_bus));
ctrl->bus = pdev->bus->number;
ctrl->rev = pdev->revision;
@@ -1088,7 +1099,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc) {
goto err_free_bus;
}
-
+
dbg("pdev = %p\n", pdev);
dbg("pci resource start %llx\n", (unsigned long long)pci_resource_start(pdev, 0));
dbg("pci resource len %llx\n", (unsigned long long)pci_resource_len(pdev, 0));
@@ -1110,8 +1121,8 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_free_mem_region;
}
- // Check for 66Mhz operation
- ctrl->speed = get_controller_speed(ctrl);
+ /* Check for 66Mhz operation */
+ bus->cur_bus_speed = get_controller_speed(ctrl);
/********************************************************
@@ -1121,7 +1132,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
*
********************************************************/
- // find the physical slot number of the first hot plug slot
+ /* find the physical slot number of the first hot plug slot */
/* Get slot won't work for devices behind bridges, but
* in this case it will always be called for the "base"
@@ -1138,18 +1149,18 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_iounmap;
}
- // Store PCI Config Space for all devices on this bus
+ /* Store PCI Config Space for all devices on this bus */
rc = cpqhp_save_config(ctrl, ctrl->bus, readb(ctrl->hpc_reg + SLOT_MASK));
if (rc) {
err("%s: unable to save PCI configuration data, error %d\n",
- __FUNCTION__, rc);
+ __func__, rc);
goto err_iounmap;
}
/*
* Get IO, memory, and IRQ resources for new devices
*/
- // The next line is required for cpqhp_find_available_resources
+ /* The next line is required for cpqhp_find_available_resources */
ctrl->interrupt = pdev->irq;
if (ctrl->interrupt < 0x10) {
cpqhp_legacy_mode = 1;
@@ -1180,10 +1191,10 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc) {
err(msg_initialization_err, 6);
err("%s: unable to save PCI configuration data, error %d\n",
- __FUNCTION__, rc);
+ __func__, rc);
goto err_iounmap;
}
-
+
/* Mask all general input interrupts */
writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_MASK);
@@ -1197,12 +1208,14 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_iounmap;
}
- /* Enable Shift Out interrupt and clear it, also enable SERR on power fault */
+ /* Enable Shift Out interrupt and clear it, also enable SERR on power
+ * fault
+ */
temp_word = readw(ctrl->hpc_reg + MISC);
temp_word |= 0x4006;
writew(temp_word, ctrl->hpc_reg + MISC);
- // Changed 05/05/97 to clear all interrupts at start
+ /* Changed 05/05/97 to clear all interrupts at start */
writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_INPUT_CLEAR);
ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
@@ -1217,13 +1230,14 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
cpqhp_ctrl_list = ctrl;
}
- // turn off empty slots here unless command line option "ON" set
- // Wait for exclusive access to hardware
+ /* turn off empty slots here unless command line option "ON" set
+ * Wait for exclusive access to hardware
+ */
mutex_lock(&ctrl->crit_sect);
num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
- // find first device number for the ctrl
+ /* find first device number for the ctrl */
device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
while (num_of_slots) {
@@ -1235,23 +1249,21 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
hp_slot = func->device - ctrl->slot_device_offset;
dbg("hp_slot: %d\n", hp_slot);
- // We have to save the presence info for these slots
+ /* We have to save the presence info for these slots */
temp_word = ctrl->ctrl_int_comp >> 16;
func->presence_save = (temp_word >> hp_slot) & 0x01;
func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
- if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+ if (ctrl->ctrl_int_comp & (0x1L << hp_slot))
func->switch_save = 0;
- } else {
+ else
func->switch_save = 0x10;
- }
- if (!power_mode) {
+ if (!power_mode)
if (!func->is_a_board) {
green_LED_off(ctrl, hp_slot);
slot_disable(ctrl, hp_slot);
}
- }
device++;
num_of_slots--;
@@ -1259,7 +1271,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!power_mode) {
set_SOGO(ctrl);
- // Wait for SOBS to be unset
+ /* Wait for SOBS to be unset */
wait_for_ctrl_irq(ctrl);
}
@@ -1270,7 +1282,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_free_irq;
}
- // Done with exclusive hardware access
+ /* Done with exclusive hardware access */
mutex_unlock(&ctrl->crit_sect);
cpqhp_create_debugfs_files(ctrl);
@@ -1292,77 +1304,6 @@ err_disable_device:
return rc;
}
-
-static int one_time_init(void)
-{
- int loop;
- int retval = 0;
-
- if (initialized)
- return 0;
-
- power_mode = 0;
-
- retval = pci_print_IRQ_route();
- if (retval)
- goto error;
-
- dbg("Initialize + Start the notification mechanism \n");
-
- retval = cpqhp_event_start_thread();
- if (retval)
- goto error;
-
- dbg("Initialize slot lists\n");
- for (loop = 0; loop < 256; loop++) {
- cpqhp_slot_list[loop] = NULL;
- }
-
- // FIXME: We also need to hook the NMI handler eventually.
- // this also needs to be worked with Christoph
- // register_NMI_handler();
-
- // Map rom address
- cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN);
- if (!cpqhp_rom_start) {
- err ("Could not ioremap memory region for ROM\n");
- retval = -EIO;
- goto error;
- }
-
- /* Now, map the int15 entry point if we are on compaq specific hardware */
- compaq_nvram_init(cpqhp_rom_start);
-
- /* Map smbios table entry point structure */
- smbios_table = detect_SMBIOS_pointer(cpqhp_rom_start,
- cpqhp_rom_start + ROM_PHY_LEN);
- if (!smbios_table) {
- err ("Could not find the SMBIOS pointer in memory\n");
- retval = -EIO;
- goto error_rom_start;
- }
-
- smbios_start = ioremap(readl(smbios_table + ST_ADDRESS),
- readw(smbios_table + ST_LENGTH));
- if (!smbios_start) {
- err ("Could not ioremap memory region taken from SMBIOS values\n");
- retval = -EIO;
- goto error_smbios_start;
- }
-
- initialized = 1;
-
- return retval;
-
-error_smbios_start:
- iounmap(smbios_start);
-error_rom_start:
- iounmap(cpqhp_rom_start);
-error:
- return retval;
-}
-
-
static void __exit unload_cpqphpd(void)
{
struct pci_func *next;
@@ -1382,10 +1323,10 @@ static void __exit unload_cpqphpd(void)
if (ctrl->hpc_reg) {
u16 misc;
rc = read_slot_enable (ctrl);
-
+
writeb(0, ctrl->hpc_reg + SLOT_SERR);
writel(0xFFFFFFC0L | ~rc, ctrl->hpc_reg + INT_MASK);
-
+
misc = readw(ctrl->hpc_reg + MISC);
misc &= 0xFFFD;
writew(misc, ctrl->hpc_reg + MISC);
@@ -1465,38 +1406,34 @@ static void __exit unload_cpqphpd(void)
}
}
- // Stop the notification mechanism
+ /* Stop the notification mechanism */
if (initialized)
cpqhp_event_stop_thread();
- //unmap the rom address
+ /* unmap the rom address */
if (cpqhp_rom_start)
iounmap(cpqhp_rom_start);
if (smbios_start)
iounmap(smbios_start);
}
-
-
static struct pci_device_id hpcd_pci_tbl[] = {
{
/* handle any PCI Hotplug controller */
.class = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00),
.class_mask = ~0,
-
+
/* no matter who makes it */
.vendor = PCI_ANY_ID,
.device = PCI_ANY_ID,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
-
+
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, hpcd_pci_tbl);
-
-
static struct pci_driver cpqhpc_driver = {
.name = "compaq_pci_hotplug",
.id_table = hpcd_pci_tbl,
@@ -1504,8 +1441,6 @@ static struct pci_driver cpqhpc_driver = {
/* remove: cpqhpc_remove_one, */
};
-
-
static int __init cpqhpc_init(void)
{
int result;
@@ -1519,7 +1454,6 @@ static int __init cpqhpc_init(void)
return result;
}
-
static void __exit cpqhpc_cleanup(void)
{
dbg("unload_cpqphpd()\n");
@@ -1530,8 +1464,5 @@ static void __exit cpqhpc_cleanup(void)
cpqhp_shutdown_debugfs();
}
-
module_init(cpqhpc_init);
module_exit(cpqhpc_cleanup);
-
-
diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c
index 4018420c6f9..bde47fce324 100644
--- a/drivers/pci/hotplug/cpqphp_ctrl.c
+++ b/drivers/pci/hotplug/cpqphp_ctrl.c
@@ -34,15 +34,14 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/wait.h>
-#include <linux/smp_lock.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/kthread.h>
#include "cpqphp.h"
-static u32 configure_new_device(struct controller* ctrl, struct pci_func *func,
+static u32 configure_new_device(struct controller *ctrl, struct pci_func *func,
u8 behind_bridge, struct resource_lists *resources);
-static int configure_new_function(struct controller* ctrl, struct pci_func *func,
+static int configure_new_function(struct controller *ctrl, struct pci_func *func,
u8 behind_bridge, struct resource_lists *resources);
static void interrupt_event_handler(struct controller *ctrl);
@@ -65,7 +64,7 @@ static void long_delay(int delay)
/* FIXME: The following line needs to be somewhere else... */
#define WRONG_BUS_FREQUENCY 0x07
-static u8 handle_switch_change(u8 change, struct controller * ctrl)
+static u8 handle_switch_change(u8 change, struct controller *ctrl)
{
int hp_slot;
u8 rc = 0;
@@ -81,14 +80,15 @@ static u8 handle_switch_change(u8 change, struct controller * ctrl)
for (hp_slot = 0; hp_slot < 6; hp_slot++) {
if (change & (0x1L << hp_slot)) {
- /**********************************
+ /*
* this one changed.
- **********************************/
+ */
func = cpqhp_slot_find(ctrl->bus,
(hp_slot + ctrl->slot_device_offset), 0);
/* this is the structure that tells the worker thread
- *what to do */
+ * what to do
+ */
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
@@ -100,17 +100,17 @@ static u8 handle_switch_change(u8 change, struct controller * ctrl)
func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
- /**********************************
+ /*
* Switch opened
- **********************************/
+ */
func->switch_save = 0;
taskInfo->event_type = INT_SWITCH_OPEN;
} else {
- /**********************************
+ /*
* Switch closed
- **********************************/
+ */
func->switch_save = 0x10;
@@ -131,15 +131,14 @@ static struct slot *cpqhp_find_slot(struct controller *ctrl, u8 device)
{
struct slot *slot = ctrl->slot;
- while (slot && (slot->device != device)) {
+ while (slot && (slot->device != device))
slot = slot->next;
- }
return slot;
}
-static u8 handle_presence_change(u16 change, struct controller * ctrl)
+static u8 handle_presence_change(u16 change, struct controller *ctrl)
{
int hp_slot;
u8 rc = 0;
@@ -152,17 +151,17 @@ static u8 handle_presence_change(u16 change, struct controller * ctrl)
if (!change)
return 0;
- /**********************************
+ /*
* Presence Change
- **********************************/
+ */
dbg("cpqsbd: Presence/Notify input change.\n");
dbg(" Changed bits are 0x%4.4x\n", change );
for (hp_slot = 0; hp_slot < 6; hp_slot++) {
if (change & (0x0101 << hp_slot)) {
- /**********************************
+ /*
* this one changed.
- **********************************/
+ */
func = cpqhp_slot_find(ctrl->bus,
(hp_slot + ctrl->slot_device_offset), 0);
@@ -177,22 +176,23 @@ static u8 handle_presence_change(u16 change, struct controller * ctrl)
return 0;
/* If the switch closed, must be a button
- * If not in button mode, nevermind */
+ * If not in button mode, nevermind
+ */
if (func->switch_save && (ctrl->push_button == 1)) {
temp_word = ctrl->ctrl_int_comp >> 16;
temp_byte = (temp_word >> hp_slot) & 0x01;
temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02;
if (temp_byte != func->presence_save) {
- /**************************************
+ /*
* button Pressed (doesn't do anything)
- **************************************/
+ */
dbg("hp_slot %d button pressed\n", hp_slot);
taskInfo->event_type = INT_BUTTON_PRESS;
} else {
- /**********************************
+ /*
* button Released - TAKE ACTION!!!!
- **********************************/
+ */
dbg("hp_slot %d button released\n", hp_slot);
taskInfo->event_type = INT_BUTTON_RELEASE;
@@ -210,7 +210,8 @@ static u8 handle_presence_change(u16 change, struct controller * ctrl)
}
} else {
/* Switch is open, assume a presence change
- * Save the presence state */
+ * Save the presence state
+ */
temp_word = ctrl->ctrl_int_comp >> 16;
func->presence_save = (temp_word >> hp_slot) & 0x01;
func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
@@ -231,7 +232,7 @@ static u8 handle_presence_change(u16 change, struct controller * ctrl)
}
-static u8 handle_power_fault(u8 change, struct controller * ctrl)
+static u8 handle_power_fault(u8 change, struct controller *ctrl)
{
int hp_slot;
u8 rc = 0;
@@ -241,17 +242,17 @@ static u8 handle_power_fault(u8 change, struct controller * ctrl)
if (!change)
return 0;
- /**********************************
+ /*
* power fault
- **********************************/
+ */
info("power fault interrupt\n");
for (hp_slot = 0; hp_slot < 6; hp_slot++) {
if (change & (0x01 << hp_slot)) {
- /**********************************
+ /*
* this one changed.
- **********************************/
+ */
func = cpqhp_slot_find(ctrl->bus,
(hp_slot + ctrl->slot_device_offset), 0);
@@ -262,16 +263,16 @@ static u8 handle_power_fault(u8 change, struct controller * ctrl)
rc++;
if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) {
- /**********************************
+ /*
* power fault Cleared
- **********************************/
+ */
func->status = 0x00;
taskInfo->event_type = INT_POWER_FAULT_CLEAR;
} else {
- /**********************************
+ /*
* power fault
- **********************************/
+ */
taskInfo->event_type = INT_POWER_FAULT;
if (ctrl->rev < 4) {
@@ -432,13 +433,15 @@ static struct pci_resource *do_pre_bridge_resource_split(struct pci_resource **h
/* If we got here, there the bridge requires some of the resource, but
- * we may be able to split some off of the front */
+ * we may be able to split some off of the front
+ */
node = *head;
if (node->length & (alignment -1)) {
/* this one isn't an aligned length, so we'll make a new entry
- * and split it up. */
+ * and split it up.
+ */
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
if (!split_node)
@@ -544,10 +547,10 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size
if (!(*head))
return NULL;
- if ( cpqhp_resource_sort_and_combine(head) )
+ if (cpqhp_resource_sort_and_combine(head))
return NULL;
- if ( sort_by_size(head) )
+ if (sort_by_size(head))
return NULL;
for (node = *head; node; node = node->next) {
@@ -556,7 +559,8 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size
if (node->base & (size - 1)) {
/* this one isn't base aligned properly
- * so we'll make a new entry and split it up */
+ * so we'll make a new entry and split it up
+ */
temp_dword = (node->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
@@ -581,7 +585,8 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size
/* Don't need to check if too small since we already did */
if (node->length > size) {
/* this one is longer than we need
- * so we'll make a new entry and split it up */
+ * so we'll make a new entry and split it up
+ */
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
if (!split_node)
@@ -601,7 +606,8 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size
continue;
/* If we got here, then it is the right size
- * Now take it out of the list and break */
+ * Now take it out of the list and break
+ */
if (*head == node) {
*head = node->next;
} else {
@@ -642,14 +648,16 @@ static struct pci_resource *get_max_resource(struct pci_resource **head, u32 siz
return NULL;
for (max = *head; max; max = max->next) {
- /* If not big enough we could probably just bail,
- * instead we'll continue to the next. */
+ /* If not big enough we could probably just bail,
+ * instead we'll continue to the next.
+ */
if (max->length < size)
continue;
if (max->base & (size - 1)) {
/* this one isn't base aligned properly
- * so we'll make a new entry and split it up */
+ * so we'll make a new entry and split it up
+ */
temp_dword = (max->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
@@ -672,7 +680,8 @@ static struct pci_resource *get_max_resource(struct pci_resource **head, u32 siz
if ((max->base + max->length) & (size - 1)) {
/* this one isn't end aligned properly at the top
- * so we'll make a new entry and split it up */
+ * so we'll make a new entry and split it up
+ */
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
if (!split_node)
@@ -700,7 +709,8 @@ static struct pci_resource *get_max_resource(struct pci_resource **head, u32 siz
temp = temp->next;
}
- temp->next = max->next;
+ if (temp)
+ temp->next = max->next;
}
max->next = NULL;
@@ -737,14 +747,15 @@ static struct pci_resource *get_resource(struct pci_resource **head, u32 size)
for (node = *head; node; node = node->next) {
dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",
- __FUNCTION__, size, node, node->base, node->length);
+ __func__, size, node, node->base, node->length);
if (node->length < size)
continue;
if (node->base & (size - 1)) {
- dbg("%s: not aligned\n", __FUNCTION__);
+ dbg("%s: not aligned\n", __func__);
/* this one isn't base aligned properly
- * so we'll make a new entry and split it up */
+ * so we'll make a new entry and split it up
+ */
temp_dword = (node->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
@@ -767,9 +778,10 @@ static struct pci_resource *get_resource(struct pci_resource **head, u32 size)
/* Don't need to check if too small since we already did */
if (node->length > size) {
- dbg("%s: too big\n", __FUNCTION__);
+ dbg("%s: too big\n", __func__);
/* this one is longer than we need
- * so we'll make a new entry and split it up */
+ * so we'll make a new entry and split it up
+ */
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
if (!split_node)
@@ -784,7 +796,7 @@ static struct pci_resource *get_resource(struct pci_resource **head, u32 size)
node->next = split_node;
} /* End of too big on top end */
- dbg("%s: got one!!!\n", __FUNCTION__);
+ dbg("%s: got one!!!\n", __func__);
/* If we got here, then it is the right size
* Now take it out of the list */
if (*head == node) {
@@ -819,7 +831,7 @@ int cpqhp_resource_sort_and_combine(struct pci_resource **head)
struct pci_resource *node2;
int out_of_order = 1;
- dbg("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head);
+ dbg("%s: head = %p, *head = %p\n", __func__, head, *head);
if (!(*head))
return 1;
@@ -886,19 +898,19 @@ irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data)
u32 Diff;
u32 temp_dword;
-
+
misc = readw(ctrl->hpc_reg + MISC);
- /***************************************
+ /*
* Check to see if it was our interrupt
- ***************************************/
+ */
if (!(misc & 0x000C)) {
return IRQ_NONE;
}
if (misc & 0x0004) {
- /**********************************
+ /*
* Serial Output interrupt Pending
- **********************************/
+ */
/* Clear the interrupt */
misc |= 0x0004;
@@ -907,7 +919,7 @@ irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data)
/* Read to clear posted writes */
misc = readw(ctrl->hpc_reg + MISC);
- dbg ("%s - waking up\n", __FUNCTION__);
+ dbg ("%s - waking up\n", __func__);
wake_up_interruptible(&ctrl->queue);
}
@@ -961,11 +973,8 @@ struct pci_func *cpqhp_slot_create(u8 busnumber)
struct pci_func *next;
new_slot = kzalloc(sizeof(*new_slot), GFP_KERNEL);
- if (new_slot == NULL) {
- /* I'm not dead yet!
- * You will be. */
+ if (new_slot == NULL)
return new_slot;
- }
new_slot->next = NULL;
new_slot->configured = 1;
@@ -988,7 +997,7 @@ struct pci_func *cpqhp_slot_create(u8 busnumber)
*
* Returns %0 if successful, !0 otherwise.
*/
-static int slot_remove(struct pci_func * old_slot)
+static int slot_remove(struct pci_func *old_slot)
{
struct pci_func *next;
@@ -996,10 +1005,8 @@ static int slot_remove(struct pci_func * old_slot)
return 1;
next = cpqhp_slot_list[old_slot->bus];
-
- if (next == NULL) {
+ if (next == NULL)
return 1;
- }
if (next == old_slot) {
cpqhp_slot_list[old_slot->bus] = old_slot->next;
@@ -1008,9 +1015,8 @@ static int slot_remove(struct pci_func * old_slot)
return 0;
}
- while ((next->next != old_slot) && (next->next != NULL)) {
+ while ((next->next != old_slot) && (next->next != NULL))
next = next->next;
- }
if (next->next == old_slot) {
next->next = old_slot->next;
@@ -1040,9 +1046,8 @@ static int bridge_slot_remove(struct pci_func *bridge)
for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
next = cpqhp_slot_list[tempBus];
- while (!slot_remove(next)) {
+ while (!slot_remove(next))
next = cpqhp_slot_list[tempBus];
- }
}
next = cpqhp_slot_list[bridge->bus];
@@ -1104,7 +1109,7 @@ struct pci_func *cpqhp_slot_find(u8 bus, u8 device, u8 index)
/* DJZ: I don't think is_bridge will work as is.
* FIXME */
-static int is_bridge(struct pci_func * func)
+static int is_bridge(struct pci_func *func)
{
/* Check the header type */
if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
@@ -1126,67 +1131,72 @@ static int is_bridge(struct pci_func * func)
static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot)
{
struct slot *slot;
+ struct pci_bus *bus = ctrl->pci_bus;
u8 reg;
u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
u16 reg16;
u32 leds = readl(ctrl->hpc_reg + LED_CONTROL);
-
- if (ctrl->speed == adapter_speed)
+
+ if (bus->cur_bus_speed == adapter_speed)
return 0;
-
+
/* We don't allow freq/mode changes if we find another adapter running
- * in another slot on this controller */
+ * in another slot on this controller
+ */
for(slot = ctrl->slot; slot; slot = slot->next) {
- if (slot->device == (hp_slot + ctrl->slot_device_offset))
+ if (slot->device == (hp_slot + ctrl->slot_device_offset))
continue;
- if (!slot->hotplug_slot && !slot->hotplug_slot->info)
+ if (!slot->hotplug_slot || !slot->hotplug_slot->info)
continue;
- if (slot->hotplug_slot->info->adapter_status == 0)
+ if (slot->hotplug_slot->info->adapter_status == 0)
continue;
/* If another adapter is running on the same segment but at a
* lower speed/mode, we allow the new adapter to function at
- * this rate if supported */
- if (ctrl->speed < adapter_speed)
+ * this rate if supported
+ */
+ if (bus->cur_bus_speed < adapter_speed)
return 0;
return 1;
}
-
+
/* If the controller doesn't support freq/mode changes and the
- * controller is running at a higher mode, we bail */
- if ((ctrl->speed > adapter_speed) && (!ctrl->pcix_speed_capability))
+ * controller is running at a higher mode, we bail
+ */
+ if ((bus->cur_bus_speed > adapter_speed) && (!ctrl->pcix_speed_capability))
return 1;
-
+
/* But we allow the adapter to run at a lower rate if possible */
- if ((ctrl->speed < adapter_speed) && (!ctrl->pcix_speed_capability))
+ if ((bus->cur_bus_speed < adapter_speed) && (!ctrl->pcix_speed_capability))
return 0;
/* We try to set the max speed supported by both the adapter and
- * controller */
- if (ctrl->speed_capability < adapter_speed) {
- if (ctrl->speed == ctrl->speed_capability)
+ * controller
+ */
+ if (bus->max_bus_speed < adapter_speed) {
+ if (bus->cur_bus_speed == bus->max_bus_speed)
return 0;
- adapter_speed = ctrl->speed_capability;
+ adapter_speed = bus->max_bus_speed;
}
writel(0x0L, ctrl->hpc_reg + LED_CONTROL);
writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE);
-
- set_SOGO(ctrl);
+
+ set_SOGO(ctrl);
wait_for_ctrl_irq(ctrl);
-
+
if (adapter_speed != PCI_SPEED_133MHz_PCIX)
reg = 0xF5;
else
- reg = 0xF4;
+ reg = 0xF4;
pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
-
+
reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ);
reg16 &= ~0x000F;
switch(adapter_speed) {
- case(PCI_SPEED_133MHz_PCIX):
+ case(PCI_SPEED_133MHz_PCIX):
reg = 0x75;
- reg16 |= 0xB;
+ reg16 |= 0xB;
break;
case(PCI_SPEED_100MHz_PCIX):
reg = 0x74;
@@ -1203,48 +1213,48 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
default: /* 33MHz PCI 2.2 */
reg = 0x71;
break;
-
+
}
reg16 |= 0xB << 12;
writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ);
-
- mdelay(5);
-
+
+ mdelay(5);
+
/* Reenable interrupts */
writel(0, ctrl->hpc_reg + INT_MASK);
- pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
-
+ pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
+
/* Restart state machine */
reg = ~0xF;
pci_read_config_byte(ctrl->pci_dev, 0x43, &reg);
pci_write_config_byte(ctrl->pci_dev, 0x43, reg);
-
+
/* Only if mode change...*/
- if (((ctrl->speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) ||
- ((ctrl->speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz)))
+ if (((bus->cur_bus_speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) ||
+ ((bus->cur_bus_speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz)))
set_SOGO(ctrl);
-
+
wait_for_ctrl_irq(ctrl);
mdelay(1100);
-
+
/* Restore LED/Slot state */
writel(leds, ctrl->hpc_reg + LED_CONTROL);
writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE);
-
+
set_SOGO(ctrl);
wait_for_ctrl_irq(ctrl);
- ctrl->speed = adapter_speed;
+ bus->cur_bus_speed = adapter_speed;
slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
- info("Successfully changed frequency/mode for adapter in slot %d\n",
+ info("Successfully changed frequency/mode for adapter in slot %d\n",
slot->number);
return 0;
}
-/* the following routines constitute the bulk of the
- hotplug controller logic
+/* the following routines constitute the bulk of the
+ * hotplug controller logic
*/
@@ -1261,6 +1271,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
*/
static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
{
+ struct pci_bus *bus = ctrl->pci_bus;
u8 hp_slot;
u8 temp_byte;
u8 adapter_speed;
@@ -1268,17 +1279,17 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
hp_slot = func->device - ctrl->slot_device_offset;
- if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)) {
- /**********************************
- * The switch is open.
- **********************************/
+ /*
+ * The switch is open.
+ */
+ if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot))
rc = INTERLOCK_OPEN;
- } else if (is_slot_enabled (ctrl, hp_slot)) {
- /**********************************
- * The board is already on
- **********************************/
+ /*
+ * The board is already on
+ */
+ else if (is_slot_enabled (ctrl, hp_slot))
rc = CARD_FUNCTIONING;
- } else {
+ else {
mutex_lock(&ctrl->crit_sect);
/* turn on board without attaching to the bus */
@@ -1299,9 +1310,9 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
/* Wait for SOBS to be unset */
wait_for_ctrl_irq (ctrl);
-
+
adapter_speed = get_adapter_speed(ctrl, hp_slot);
- if (ctrl->speed != adapter_speed)
+ if (bus->cur_bus_speed != adapter_speed)
if (set_controller_speed(ctrl, adapter_speed, hp_slot))
rc = WRONG_BUS_FREQUENCY;
@@ -1352,7 +1363,8 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
* Get slot won't work for devices behind
* bridges, but in this case it will always be
* called for the "base" bus/dev/func of an
- * adapter. */
+ * adapter.
+ */
mutex_lock(&ctrl->crit_sect);
@@ -1377,7 +1389,8 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
* Get slot won't work for devices behind bridges, but
* in this case it will always be called for the "base"
- * bus/dev/func of an adapter. */
+ * bus/dev/func of an adapter.
+ */
mutex_lock(&ctrl->crit_sect);
@@ -1416,12 +1429,13 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
u32 temp_register = 0xFFFFFFFF;
u32 rc = 0;
struct pci_func *new_slot = NULL;
+ struct pci_bus *bus = ctrl->pci_bus;
struct slot *p_slot;
struct resource_lists res_lists;
hp_slot = func->device - ctrl->slot_device_offset;
dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n",
- __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot);
+ __func__, func->device, ctrl->slot_device_offset, hp_slot);
mutex_lock(&ctrl->crit_sect);
@@ -1434,7 +1448,8 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
wait_for_ctrl_irq (ctrl);
/* Change bits in slot power register to force another shift out
- * NOTE: this is to work around the timer bug */
+ * NOTE: this is to work around the timer bug
+ */
temp_byte = readb(ctrl->hpc_reg + SLOT_POWER);
writeb(0x00, ctrl->hpc_reg + SLOT_POWER);
writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER);
@@ -1443,12 +1458,12 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
/* Wait for SOBS to be unset */
wait_for_ctrl_irq (ctrl);
-
+
adapter_speed = get_adapter_speed(ctrl, hp_slot);
- if (ctrl->speed != adapter_speed)
+ if (bus->cur_bus_speed != adapter_speed)
if (set_controller_speed(ctrl, adapter_speed, hp_slot))
rc = WRONG_BUS_FREQUENCY;
-
+
/* turn off board without attaching to the bus */
disable_slot_power (ctrl, hp_slot);
@@ -1461,67 +1476,67 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
if (rc)
return rc;
-
+
p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
/* turn on board and blink green LED */
- dbg("%s: before down\n", __FUNCTION__);
+ dbg("%s: before down\n", __func__);
mutex_lock(&ctrl->crit_sect);
- dbg("%s: after down\n", __FUNCTION__);
+ dbg("%s: after down\n", __func__);
- dbg("%s: before slot_enable\n", __FUNCTION__);
+ dbg("%s: before slot_enable\n", __func__);
slot_enable (ctrl, hp_slot);
- dbg("%s: before green_LED_blink\n", __FUNCTION__);
+ dbg("%s: before green_LED_blink\n", __func__);
green_LED_blink (ctrl, hp_slot);
- dbg("%s: before amber_LED_blink\n", __FUNCTION__);
+ dbg("%s: before amber_LED_blink\n", __func__);
amber_LED_off (ctrl, hp_slot);
- dbg("%s: before set_SOGO\n", __FUNCTION__);
+ dbg("%s: before set_SOGO\n", __func__);
set_SOGO(ctrl);
/* Wait for SOBS to be unset */
- dbg("%s: before wait_for_ctrl_irq\n", __FUNCTION__);
+ dbg("%s: before wait_for_ctrl_irq\n", __func__);
wait_for_ctrl_irq (ctrl);
- dbg("%s: after wait_for_ctrl_irq\n", __FUNCTION__);
+ dbg("%s: after wait_for_ctrl_irq\n", __func__);
- dbg("%s: before up\n", __FUNCTION__);
+ dbg("%s: before up\n", __func__);
mutex_unlock(&ctrl->crit_sect);
- dbg("%s: after up\n", __FUNCTION__);
+ dbg("%s: after up\n", __func__);
/* Wait for ~1 second because of hot plug spec */
- dbg("%s: before long_delay\n", __FUNCTION__);
+ dbg("%s: before long_delay\n", __func__);
long_delay(1*HZ);
- dbg("%s: after long_delay\n", __FUNCTION__);
+ dbg("%s: after long_delay\n", __func__);
- dbg("%s: func status = %x\n", __FUNCTION__, func->status);
+ dbg("%s: func status = %x\n", __func__, func->status);
/* Check for a power fault */
if (func->status == 0xFF) {
/* power fault occurred, but it was benign */
temp_register = 0xFFFFFFFF;
- dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register);
+ dbg("%s: temp register set to %x by power fault\n", __func__, temp_register);
rc = POWER_FAILURE;
func->status = 0;
} else {
/* Get vendor/device ID u32 */
ctrl->pci_bus->number = func->bus;
rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register);
- dbg("%s: pci_read_config_dword returns %d\n", __FUNCTION__, rc);
- dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register);
+ dbg("%s: pci_read_config_dword returns %d\n", __func__, rc);
+ dbg("%s: temp_register is %x\n", __func__, temp_register);
if (rc != 0) {
/* Something's wrong here */
temp_register = 0xFFFFFFFF;
- dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register);
+ dbg("%s: temp register set to %x by error\n", __func__, temp_register);
}
/* Preset return code. It will be changed later if things go okay. */
rc = NO_ADAPTER_PRESENT;
}
/* All F's is an empty slot or an invalid board */
- if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */
+ if (temp_register != 0xFFFFFFFF) {
res_lists.io_head = ctrl->io_head;
res_lists.mem_head = ctrl->mem_head;
res_lists.p_mem_head = ctrl->p_mem_head;
@@ -1530,7 +1545,7 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
rc = configure_new_device(ctrl, func, 0, &res_lists);
- dbg("%s: back from configure_new_device\n", __FUNCTION__);
+ dbg("%s: back from configure_new_device\n", __func__);
ctrl->io_head = res_lists.io_head;
ctrl->mem_head = res_lists.mem_head;
ctrl->p_mem_head = res_lists.p_mem_head;
@@ -1566,13 +1581,12 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
/* next, we will instantiate the linux pci_dev structures (with
* appropriate driver notification, if already present) */
- dbg("%s: configure linux pci_dev structure\n", __FUNCTION__);
+ dbg("%s: configure linux pci_dev structure\n", __func__);
index = 0;
do {
new_slot = cpqhp_slot_find(ctrl->bus, func->device, index++);
- if (new_slot && !new_slot->pci_dev) {
+ if (new_slot && !new_slot->pci_dev)
cpqhp_configure_device(ctrl, new_slot);
- }
} while (new_slot);
mutex_lock(&ctrl->crit_sect);
@@ -1611,7 +1625,7 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
* @replace_flag: whether replacing or adding a new device
* @ctrl: target controller
*/
-static u32 remove_board(struct pci_func * func, u32 replace_flag, struct controller * ctrl)
+static u32 remove_board(struct pci_func *func, u32 replace_flag, struct controller *ctrl)
{
int index;
u8 skip = 0;
@@ -1628,7 +1642,7 @@ static u32 remove_board(struct pci_func * func, u32 replace_flag, struct control
device = func->device;
hp_slot = func->device - ctrl->slot_device_offset;
- dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
+ dbg("In %s, hp_slot = %d\n", __func__, hp_slot);
/* When we get here, it is safe to change base address registers.
* We will attempt to save the base address register lengths */
@@ -1728,7 +1742,7 @@ static void pushbutton_helper_thread(unsigned long data)
/* this is the main worker thread */
-static int event_thread(void* data)
+static int event_thread(void *data)
{
struct controller *ctrl;
@@ -1815,7 +1829,7 @@ static void interrupt_event_handler(struct controller *ctrl)
if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
dbg("button pressed\n");
- } else if (ctrl->event_queue[loop].event_type ==
+ } else if (ctrl->event_queue[loop].event_type ==
INT_BUTTON_CANCEL) {
dbg("button cancel\n");
del_timer(&p_slot->task_event);
@@ -1859,12 +1873,12 @@ static void interrupt_event_handler(struct controller *ctrl)
info(msg_button_on, p_slot->number);
}
mutex_lock(&ctrl->crit_sect);
-
+
dbg("blink green LED and turn off amber\n");
-
+
amber_LED_off (ctrl, hp_slot);
green_LED_blink (ctrl, hp_slot);
-
+
set_SOGO(ctrl);
/* Wait for SOBS to be unset */
@@ -1887,8 +1901,7 @@ static void interrupt_event_handler(struct controller *ctrl)
dbg("power fault\n");
} else {
/* refresh notification */
- if (p_slot)
- update_slot_info(ctrl, p_slot);
+ update_slot_info(ctrl, p_slot);
}
ctrl->event_queue[loop].event_type = 0;
@@ -1928,7 +1941,7 @@ void cpqhp_pushbutton_thread(unsigned long slot)
func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0);
dbg("In power_down_board, func = %p, ctrl = %p\n", func, ctrl);
if (!func) {
- dbg("Error! func NULL in %s\n", __FUNCTION__);
+ dbg("Error! func NULL in %s\n", __func__);
return ;
}
@@ -1950,15 +1963,15 @@ void cpqhp_pushbutton_thread(unsigned long slot)
func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0);
dbg("In add_board, func = %p, ctrl = %p\n", func, ctrl);
if (!func) {
- dbg("Error! func NULL in %s\n", __FUNCTION__);
+ dbg("Error! func NULL in %s\n", __func__);
return ;
}
- if (func != NULL && ctrl != NULL) {
+ if (ctrl != NULL) {
if (cpqhp_process_SI(ctrl, func) != 0) {
amber_LED_on(ctrl, hp_slot);
green_LED_off(ctrl, hp_slot);
-
+
set_SOGO(ctrl);
/* Wait for SOBS to be unset */
@@ -1979,7 +1992,7 @@ int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
u16 temp_word;
u32 tempdword;
int rc;
- struct slot* p_slot;
+ struct slot *p_slot;
int physical_slot = 0;
tempdword = 0;
@@ -2058,7 +2071,7 @@ int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
}
if (rc) {
- dbg("%s: rc = %d\n", __FUNCTION__, rc);
+ dbg("%s: rc = %d\n", __func__, rc);
}
if (p_slot)
@@ -2075,11 +2088,11 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
u8 replace_flag;
u32 rc = 0;
unsigned int devfn;
- struct slot* p_slot;
+ struct slot *p_slot;
struct pci_bus *pci_bus = ctrl->pci_bus;
int physical_slot=0;
- device = func->device;
+ device = func->device;
func = cpqhp_slot_find(ctrl->bus, device, index++);
p_slot = cpqhp_find_slot(ctrl, device);
if (p_slot) {
@@ -2113,9 +2126,8 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
/* If the VGA Enable bit is set, remove isn't
* supported */
- if (BCR & PCI_BRIDGE_CTL_VGA) {
+ if (BCR & PCI_BRIDGE_CTL_VGA)
rc = REMOVE_NOT_SUPPORTED;
- }
}
}
@@ -2183,67 +2195,67 @@ int cpqhp_hardware_test(struct controller *ctrl, int test_num)
num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0f;
switch (test_num) {
- case 1:
- /* Do stuff here! */
-
- /* Do that funky LED thing */
- /* so we can restore them later */
- save_LED = readl(ctrl->hpc_reg + LED_CONTROL);
- work_LED = 0x01010101;
- switch_leds(ctrl, num_of_slots, &work_LED, 0);
- switch_leds(ctrl, num_of_slots, &work_LED, 1);
- switch_leds(ctrl, num_of_slots, &work_LED, 0);
- switch_leds(ctrl, num_of_slots, &work_LED, 1);
-
- work_LED = 0x01010000;
- writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
- switch_leds(ctrl, num_of_slots, &work_LED, 0);
- switch_leds(ctrl, num_of_slots, &work_LED, 1);
- work_LED = 0x00000101;
- writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
- switch_leds(ctrl, num_of_slots, &work_LED, 0);
- switch_leds(ctrl, num_of_slots, &work_LED, 1);
+ case 1:
+ /* Do stuff here! */
+
+ /* Do that funky LED thing */
+ /* so we can restore them later */
+ save_LED = readl(ctrl->hpc_reg + LED_CONTROL);
+ work_LED = 0x01010101;
+ switch_leds(ctrl, num_of_slots, &work_LED, 0);
+ switch_leds(ctrl, num_of_slots, &work_LED, 1);
+ switch_leds(ctrl, num_of_slots, &work_LED, 0);
+ switch_leds(ctrl, num_of_slots, &work_LED, 1);
+
+ work_LED = 0x01010000;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ switch_leds(ctrl, num_of_slots, &work_LED, 0);
+ switch_leds(ctrl, num_of_slots, &work_LED, 1);
+ work_LED = 0x00000101;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ switch_leds(ctrl, num_of_slots, &work_LED, 0);
+ switch_leds(ctrl, num_of_slots, &work_LED, 1);
+
+ work_LED = 0x01010000;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ for (loop = 0; loop < num_of_slots; loop++) {
+ set_SOGO(ctrl);
- work_LED = 0x01010000;
- writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
- for (loop = 0; loop < num_of_slots; loop++) {
- set_SOGO(ctrl);
+ /* Wait for SOGO interrupt */
+ wait_for_ctrl_irq (ctrl);
- /* Wait for SOGO interrupt */
- wait_for_ctrl_irq (ctrl);
+ /* Get ready for next iteration */
+ long_delay((3*HZ)/10);
+ work_LED = work_LED >> 16;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
- /* Get ready for next iteration */
- long_delay((3*HZ)/10);
- work_LED = work_LED >> 16;
- writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
-
- set_SOGO(ctrl);
+ set_SOGO(ctrl);
- /* Wait for SOGO interrupt */
- wait_for_ctrl_irq (ctrl);
+ /* Wait for SOGO interrupt */
+ wait_for_ctrl_irq (ctrl);
- /* Get ready for next iteration */
- long_delay((3*HZ)/10);
- work_LED = work_LED << 16;
- writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
- work_LED = work_LED << 1;
- writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
- }
+ /* Get ready for next iteration */
+ long_delay((3*HZ)/10);
+ work_LED = work_LED << 16;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ work_LED = work_LED << 1;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ }
- /* put it back the way it was */
- writel(save_LED, ctrl->hpc_reg + LED_CONTROL);
+ /* put it back the way it was */
+ writel(save_LED, ctrl->hpc_reg + LED_CONTROL);
- set_SOGO(ctrl);
+ set_SOGO(ctrl);
- /* Wait for SOBS to be unset */
- wait_for_ctrl_irq (ctrl);
- break;
- case 2:
- /* Do other stuff here! */
- break;
- case 3:
- /* and more... */
- break;
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+ break;
+ case 2:
+ /* Do other stuff here! */
+ break;
+ case 3:
+ /* and more... */
+ break;
}
return 0;
}
@@ -2258,8 +2270,8 @@ int cpqhp_hardware_test(struct controller *ctrl, int test_num)
*
* Returns 0 if success.
*/
-static u32 configure_new_device(struct controller * ctrl, struct pci_func * func,
- u8 behind_bridge, struct resource_lists * resources)
+static u32 configure_new_device(struct controller *ctrl, struct pci_func *func,
+ u8 behind_bridge, struct resource_lists *resources)
{
u8 temp_byte, function, max_functions, stop_it;
int rc;
@@ -2269,12 +2281,12 @@ static u32 configure_new_device(struct controller * ctrl, struct pci_func * func
new_slot = func;
- dbg("%s\n", __FUNCTION__);
+ dbg("%s\n", __func__);
/* Check for Multi-function device */
ctrl->pci_bus->number = func->bus;
rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte);
if (rc) {
- dbg("%s: rc = %d\n", __FUNCTION__, rc);
+ dbg("%s: rc = %d\n", __func__, rc);
return rc;
}
@@ -2312,9 +2324,9 @@ static u32 configure_new_device(struct controller * ctrl, struct pci_func * func
while ((function < max_functions) && (!stop_it)) {
pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
- if (ID == 0xFFFFFFFF) { /* There's nothing there. */
+ if (ID == 0xFFFFFFFF) {
function++;
- } else { /* There's something there */
+ } else {
/* Setup slot structure. */
new_slot = cpqhp_slot_create(func->bus);
@@ -2339,8 +2351,8 @@ static u32 configure_new_device(struct controller * ctrl, struct pci_func * func
/*
- Configuration logic that involves the hotplug data structures and
- their bookkeeping
+ * Configuration logic that involves the hotplug data structures and
+ * their bookkeeping
*/
@@ -2393,18 +2405,18 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
if (rc)
return rc;
- if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
+ if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
/* set Primary bus */
dbg("set Primary bus = %d\n", func->bus);
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
if (rc)
return rc;
- /* find range of busses to use */
+ /* find range of buses to use */
dbg("find ranges of buses to use\n");
bus_node = get_max_resource(&(resources->bus_head), 1);
- /* If we don't have any busses to allocate, we can't continue */
+ /* If we don't have any buses to allocate, we can't continue */
if (!bus_node)
return -ENOMEM;
@@ -2484,7 +2496,8 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
temp_resources.irqs = &irqs;
/* Make copies of the nodes we are going to pass down so that
- * if there is a problem,we can just use these to free resources */
+ * if there is a problem,we can just use these to free resources
+ */
hold_bus_node = kmalloc(sizeof(*hold_bus_node), GFP_KERNEL);
hold_IO_node = kmalloc(sizeof(*hold_IO_node), GFP_KERNEL);
hold_mem_node = kmalloc(sizeof(*hold_mem_node), GFP_KERNEL);
@@ -2507,44 +2520,28 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
/* If we have IO resources copy them and fill in the bridge's
* IO range registers */
- if (io_node) {
- memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
- io_node->next = NULL;
-
- /* set IO base and Limit registers */
- temp_byte = io_node->base >> 8;
- rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte);
-
- temp_byte = (io_node->base + io_node->length - 1) >> 8;
- rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
- } else {
- kfree(hold_IO_node);
- hold_IO_node = NULL;
- }
+ memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
+ io_node->next = NULL;
- /* If we have memory resources copy them and fill in the
- * bridge's memory range registers. Otherwise, fill in the
- * range registers with values that disable them. */
- if (mem_node) {
- memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource));
- mem_node->next = NULL;
+ /* set IO base and Limit registers */
+ temp_byte = io_node->base >> 8;
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte);
- /* set Mem base and Limit registers */
- temp_word = mem_node->base >> 16;
- rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+ temp_byte = (io_node->base + io_node->length - 1) >> 8;
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
- temp_word = (mem_node->base + mem_node->length - 1) >> 16;
- rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
- } else {
- temp_word = 0xFFFF;
- rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+ /* Copy the memory resources and fill in the bridge's memory
+ * range registers.
+ */
+ memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource));
+ mem_node->next = NULL;
- temp_word = 0x0000;
- rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+ /* set Mem base and Limit registers */
+ temp_word = mem_node->base >> 16;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
- kfree(hold_mem_node);
- hold_mem_node = NULL;
- }
+ temp_word = (mem_node->base + mem_node->length - 1) >> 16;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource));
p_mem_node->next = NULL;
@@ -2556,7 +2553,8 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
- /* Adjust this to compensate for extra adjustment in first loop */
+ /* Adjust this to compensate for extra adjustment in first loop
+ */
irqs.barber_pole--;
rc = 0;
@@ -2604,7 +2602,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
for (cloop = 0; cloop < 4; cloop++) {
if (irqs.valid_INT & (0x01 << cloop)) {
rc = cpqhp_set_irq(func->bus, func->device,
- 0x0A + cloop, irqs.interrupt[cloop]);
+ cloop + 1, irqs.interrupt[cloop]);
if (rc)
goto free_and_out;
}
@@ -2613,7 +2611,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
/* Return unused bus resources
* First use the temporary node to store information for
* the board */
- if (hold_bus_node && bus_node && temp_resources.bus_head) {
+ if (bus_node && temp_resources.bus_head) {
hold_bus_node->length = bus_node->base - hold_bus_node->base;
hold_bus_node->next = func->bus_head;
@@ -2737,7 +2735,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
}
/* If we have prefetchable memory space available and there
* is some left at the end, return the unused portion */
- if (hold_p_mem_node && temp_resources.p_mem_head) {
+ if (temp_resources.p_mem_head) {
p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head),
&hold_p_mem_node, 0x100000);
@@ -2876,27 +2874,8 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
func->mem_head = mem_node;
} else
return -ENOMEM;
- } else if ((temp_register & 0x0BL) == 0x04) {
- /* Map memory */
- base = temp_register & 0xFFFFFFF0;
- base = ~base + 1;
-
- dbg("CND: length = 0x%x\n", base);
- mem_node = get_resource(&(resources->mem_head), base);
-
- /* allocate the resource to the board */
- if (mem_node) {
- base = mem_node->base;
-
- mem_node->next = func->mem_head;
- func->mem_head = mem_node;
- } else
- return -ENOMEM;
- } else if ((temp_register & 0x0BL) == 0x06) {
- /* Those bits are reserved, we can't handle this */
- return 1;
} else {
- /* Requesting space below 1M */
+ /* Reserved bits or requesting space below 1M */
return NOT_ENOUGH_RESOURCES;
}
@@ -2917,27 +2896,26 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
} /* End of base register loop */
if (cpqhp_legacy_mode) {
/* Figure out which interrupt pin this function uses */
- rc = pci_bus_read_config_byte (pci_bus, devfn,
+ rc = pci_bus_read_config_byte (pci_bus, devfn,
PCI_INTERRUPT_PIN, &temp_byte);
/* If this function needs an interrupt and we are behind
* a bridge and the pin is tied to something that's
- * alread mapped, set this one the same */
- if (temp_byte && resources->irqs &&
- (resources->irqs->valid_INT &
+ * already mapped, set this one the same */
+ if (temp_byte && resources->irqs &&
+ (resources->irqs->valid_INT &
(0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) {
/* We have to share with something already set up */
- IRQ = resources->irqs->interrupt[(temp_byte +
+ IRQ = resources->irqs->interrupt[(temp_byte +
resources->irqs->barber_pole - 1) & 0x03];
} else {
/* Program IRQ based on card type */
rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
- if (class_code == PCI_BASE_CLASS_STORAGE) {
+ if (class_code == PCI_BASE_CLASS_STORAGE)
IRQ = cpqhp_disk_irq;
- } else {
+ else
IRQ = cpqhp_nic_irq;
- }
}
/* IRQ Line */
@@ -2945,7 +2923,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
}
if (!behind_bridge) {
- rc = cpqhp_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ);
+ rc = cpqhp_set_irq(func->bus, func->device, temp_byte, IRQ);
if (rc)
return 1;
} else {
diff --git a/drivers/pci/hotplug/cpqphp_nvram.c b/drivers/pci/hotplug/cpqphp_nvram.c
index ae5e974c45a..0968a9bcb34 100644
--- a/drivers/pci/hotplug/cpqphp_nvram.c
+++ b/drivers/pci/hotplug/cpqphp_nvram.c
@@ -34,7 +34,6 @@
#include <linux/workqueue.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
-#include <linux/init.h>
#include <asm/uaccess.h>
#include "cpqphp.h"
#include "cpqphp_nvram.h"
@@ -94,12 +93,13 @@ static u8 evbuffer[1024];
static void __iomem *compaq_int15_entry_point;
-static spinlock_t int15_lock; /* lock for ordering int15_bios_call() */
+/* lock for ordering int15_bios_call() */
+static spinlock_t int15_lock;
/* This is a series of function that deals with
- setting & getting the hotplug resource table in some environment variable.
-*/
+ * setting & getting the hotplug resource table in some environment variable.
+ */
/*
* We really shouldn't be doing this unless there is a _very_ good reason to!!!
@@ -107,13 +107,13 @@ static spinlock_t int15_lock; /* lock for ordering int15_bios_call() */
*/
-static u32 add_byte( u32 **p_buffer, u8 value, u32 *used, u32 *avail)
+static u32 add_byte(u32 **p_buffer, u8 value, u32 *used, u32 *avail)
{
u8 **tByte;
if ((*used + 1) > *avail)
return(1);
-
+
*((u8*)*p_buffer) = value;
tByte = (u8**)p_buffer;
(*tByte)++;
@@ -122,7 +122,7 @@ static u32 add_byte( u32 **p_buffer, u8 value, u32 *used, u32 *avail)
}
-static u32 add_dword( u32 **p_buffer, u32 value, u32 *used, u32 *avail)
+static u32 add_dword(u32 **p_buffer, u32 value, u32 *used, u32 *avail)
{
if ((*used + 4) > *avail)
return(1);
@@ -160,7 +160,7 @@ static int check_for_compaq_ROM (void __iomem *rom_start)
(temp6 == 'Q')) {
result = 1;
}
- dbg ("%s - returned %d\n", __FUNCTION__, result);
+ dbg ("%s - returned %d\n", __func__, result);
return result;
}
@@ -170,10 +170,10 @@ static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
unsigned long flags;
int op = operation;
int ret_val;
-
+
if (!compaq_int15_entry_point)
return -ENODEV;
-
+
spin_lock_irqsave(&int15_lock, flags);
__asm__ (
"xorl %%ebx,%%ebx\n" \
@@ -187,7 +187,7 @@ static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
"D" (buffer), "m" (compaq_int15_entry_point)
: "%ebx", "%edx");
spin_unlock_irqrestore(&int15_lock, flags);
-
+
return((ret_val & 0xFF00) >> 8);
}
@@ -210,14 +210,16 @@ static int load_HRT (void __iomem *rom_start)
available = 1024;
- // Now load the EV
+ /* Now load the EV */
temp_dword = available;
rc = access_EV(READ_EV, "CQTHPS", evbuffer, &temp_dword);
evbuffer_length = temp_dword;
- // We're maintaining the resource lists so write FF to invalidate old info
+ /* We're maintaining the resource lists so write FF to invalidate old
+ * info
+ */
temp_dword = 1;
rc = access_EV(WRITE_EV, "CQTHPS", &temp_byte, &temp_dword);
@@ -263,14 +265,14 @@ static u32 store_HRT (void __iomem *rom_start)
p_EV_header = (struct ev_hrt_header *) pFill;
ctrl = cpqhp_ctrl_list;
-
- // The revision of this structure
- rc = add_byte( &pFill, 1 + ctrl->push_flag, &usedbytes, &available);
+
+ /* The revision of this structure */
+ rc = add_byte(&pFill, 1 + ctrl->push_flag, &usedbytes, &available);
if (rc)
return(rc);
- // The number of controllers
- rc = add_byte( &pFill, 1, &usedbytes, &available);
+ /* The number of controllers */
+ rc = add_byte(&pFill, 1, &usedbytes, &available);
if (rc)
return(rc);
@@ -279,27 +281,27 @@ static u32 store_HRT (void __iomem *rom_start)
numCtrl++;
- // The bus number
- rc = add_byte( &pFill, ctrl->bus, &usedbytes, &available);
+ /* The bus number */
+ rc = add_byte(&pFill, ctrl->bus, &usedbytes, &available);
if (rc)
return(rc);
- // The device Number
- rc = add_byte( &pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available);
+ /* The device Number */
+ rc = add_byte(&pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available);
if (rc)
return(rc);
- // The function Number
- rc = add_byte( &pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available);
+ /* The function Number */
+ rc = add_byte(&pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available);
if (rc)
return(rc);
- // Skip the number of available entries
- rc = add_dword( &pFill, 0, &usedbytes, &available);
+ /* Skip the number of available entries */
+ rc = add_dword(&pFill, 0, &usedbytes, &available);
if (rc)
return(rc);
- // Figure out memory Available
+ /* Figure out memory Available */
resNode = ctrl->mem_head;
@@ -308,23 +310,23 @@ static u32 store_HRT (void __iomem *rom_start)
while (resNode) {
loop ++;
- // base
- rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+ /* base */
+ rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
if (rc)
return(rc);
- // length
- rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+ /* length */
+ rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
if (rc)
return(rc);
resNode = resNode->next;
}
- // Fill in the number of entries
+ /* Fill in the number of entries */
p_ev_ctrl->mem_avail = loop;
- // Figure out prefetchable memory Available
+ /* Figure out prefetchable memory Available */
resNode = ctrl->p_mem_head;
@@ -333,23 +335,23 @@ static u32 store_HRT (void __iomem *rom_start)
while (resNode) {
loop ++;
- // base
- rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+ /* base */
+ rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
if (rc)
return(rc);
- // length
- rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+ /* length */
+ rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
if (rc)
return(rc);
resNode = resNode->next;
}
- // Fill in the number of entries
+ /* Fill in the number of entries */
p_ev_ctrl->p_mem_avail = loop;
- // Figure out IO Available
+ /* Figure out IO Available */
resNode = ctrl->io_head;
@@ -358,23 +360,23 @@ static u32 store_HRT (void __iomem *rom_start)
while (resNode) {
loop ++;
- // base
- rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+ /* base */
+ rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
if (rc)
return(rc);
- // length
- rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+ /* length */
+ rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
if (rc)
return(rc);
resNode = resNode->next;
}
- // Fill in the number of entries
+ /* Fill in the number of entries */
p_ev_ctrl->io_avail = loop;
- // Figure out bus Available
+ /* Figure out bus Available */
resNode = ctrl->bus_head;
@@ -383,28 +385,28 @@ static u32 store_HRT (void __iomem *rom_start)
while (resNode) {
loop ++;
- // base
- rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+ /* base */
+ rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
if (rc)
return(rc);
- // length
- rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+ /* length */
+ rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
if (rc)
return(rc);
resNode = resNode->next;
}
- // Fill in the number of entries
+ /* Fill in the number of entries */
p_ev_ctrl->bus_avail = loop;
ctrl = ctrl->next;
}
-
+
p_EV_header->num_of_ctrl = numCtrl;
- // Now store the EV
+ /* Now store the EV */
temp_dword = usedbytes;
@@ -449,20 +451,21 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
struct ev_hrt_header *p_EV_header;
if (!evbuffer_init) {
- // Read the resource list information in from NVRAM
+ /* Read the resource list information in from NVRAM */
if (load_HRT(rom_start))
memset (evbuffer, 0, 1024);
evbuffer_init = 1;
}
- // If we saved information in NVRAM, use it now
+ /* If we saved information in NVRAM, use it now */
p_EV_header = (struct ev_hrt_header *) evbuffer;
- // The following code is for systems where version 1.0 of this
- // driver has been loaded, but doesn't support the hardware.
- // In that case, the driver would incorrectly store something
- // in NVRAM.
+ /* The following code is for systems where version 1.0 of this
+ * driver has been loaded, but doesn't support the hardware.
+ * In that case, the driver would incorrectly store something
+ * in NVRAM.
+ */
if ((p_EV_header->Version == 2) ||
((p_EV_header->Version == 1) && !ctrl->push_flag)) {
p_byte = &(p_EV_header->next);
@@ -479,7 +482,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
function = p_ev_ctrl->function;
while ((bus != ctrl->bus) ||
- (device != PCI_SLOT(ctrl->pci_dev->devfn)) ||
+ (device != PCI_SLOT(ctrl->pci_dev->devfn)) ||
(function != PCI_FUNC(ctrl->pci_dev->devfn))) {
nummem = p_ev_ctrl->mem_avail;
numpmem = p_ev_ctrl->p_mem_avail;
@@ -491,7 +494,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
return 2;
- // Skip forward to the next entry
+ /* Skip forward to the next entry */
p_byte += (nummem + numpmem + numio + numbus) * 8;
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
@@ -629,8 +632,9 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
ctrl->bus_head = bus_node;
}
- // If all of the following fail, we don't have any resources for
- // hot plug add
+ /* If all of the following fail, we don't have any resources for
+ * hot plug add
+ */
rc = 1;
rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
@@ -640,14 +644,14 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
if (rc)
return(rc);
} else {
- if ((evbuffer[0] != 0) && (!ctrl->push_flag))
+ if ((evbuffer[0] != 0) && (!ctrl->push_flag))
return 1;
}
return 0;
}
-
+
int compaq_nvram_store (void __iomem *rom_start)
{
int rc = 1;
diff --git a/drivers/pci/hotplug/cpqphp_nvram.h b/drivers/pci/hotplug/cpqphp_nvram.h
index e89c0702119..34e4e54fcf1 100644
--- a/drivers/pci/hotplug/cpqphp_nvram.h
+++ b/drivers/pci/hotplug/cpqphp_nvram.h
@@ -30,26 +30,26 @@
#ifndef CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM
-static inline void compaq_nvram_init (void __iomem *rom_start)
+static inline void compaq_nvram_init(void __iomem *rom_start)
{
return;
}
-static inline int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
+static inline int compaq_nvram_load(void __iomem *rom_start, struct controller *ctrl)
{
return 0;
}
-static inline int compaq_nvram_store (void __iomem *rom_start)
+static inline int compaq_nvram_store(void __iomem *rom_start)
{
return 0;
}
#else
-extern void compaq_nvram_init (void __iomem *rom_start);
-extern int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl);
-extern int compaq_nvram_store (void __iomem *rom_start);
+void compaq_nvram_init(void __iomem *rom_start);
+int compaq_nvram_load(void __iomem *rom_start, struct controller *ctrl);
+int compaq_nvram_store(void __iomem *rom_start);
#endif
diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c
index 3f6cd20e95d..1c8c2f130d3 100644
--- a/drivers/pci/hotplug/cpqphp_pci.c
+++ b/drivers/pci/hotplug/cpqphp_pci.c
@@ -37,7 +37,6 @@
#include "../pci.h"
#include "cpqphp.h"
#include "cpqphp_nvram.h"
-#include "../../../arch/x86/pci/pci.h" /* horrible hack showing how processor dependent we are... */
u8 cpqhp_nic_irq;
@@ -82,14 +81,15 @@ static void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iom
}
-int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)
+int cpqhp_configure_device (struct controller *ctrl, struct pci_func *func)
{
- unsigned char bus;
struct pci_bus *child;
int num;
+ pci_lock_rescan_remove();
+
if (func->pci_dev == NULL)
- func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+ func->pci_dev = pci_get_bus_and_slot(func->bus,PCI_DEVFN(func->device, func->function));
/* No pci device, we need to create it then */
if (func->pci_dev == NULL) {
@@ -99,34 +99,43 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)
if (num)
pci_bus_add_devices(ctrl->pci_dev->bus);
- func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+ func->pci_dev = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, func->function));
if (func->pci_dev == NULL) {
dbg("ERROR: pci_dev still null\n");
- return 0;
+ goto out;
}
}
if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
- pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus);
- child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus);
- pci_do_scan_bus(child);
+ pci_hp_add_bridge(func->pci_dev);
+ child = func->pci_dev->subordinate;
+ if (child)
+ pci_bus_add_devices(child);
}
+ pci_dev_put(func->pci_dev);
+
+ out:
+ pci_unlock_rescan_remove();
return 0;
}
-int cpqhp_unconfigure_device(struct pci_func* func)
+int cpqhp_unconfigure_device(struct pci_func *func)
{
int j;
-
- dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, func->device, func->function);
+ dbg("%s: bus/dev/func = %x/%x/%x\n", __func__, func->bus, func->device, func->function);
+
+ pci_lock_rescan_remove();
for (j=0; j<8 ; j++) {
- struct pci_dev* temp = pci_find_slot(func->bus, PCI_DEVFN(func->device, j));
- if (temp)
- pci_remove_bus_device(temp);
+ struct pci_dev *temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j));
+ if (temp) {
+ pci_dev_put(temp);
+ pci_stop_and_remove_bus_device(temp);
+ }
}
+ pci_unlock_rescan_remove();
return 0;
}
@@ -170,41 +179,31 @@ int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
fakedev->bus = fakebus;
fakebus->number = bus_num;
dbg("%s: dev %d, bus %d, pin %d, num %d\n",
- __FUNCTION__, dev_num, bus_num, int_pin, irq_num);
- rc = pcibios_set_irq_routing(fakedev, int_pin - 0x0a, irq_num);
+ __func__, dev_num, bus_num, int_pin, irq_num);
+ rc = pcibios_set_irq_routing(fakedev, int_pin - 1, irq_num);
kfree(fakedev);
kfree(fakebus);
- dbg("%s: rc %d\n", __FUNCTION__, rc);
+ dbg("%s: rc %d\n", __func__, rc);
if (!rc)
return !rc;
- // set the Edge Level Control Register (ELCR)
+ /* set the Edge Level Control Register (ELCR) */
temp_word = inb(0x4d0);
temp_word |= inb(0x4d1) << 8;
temp_word |= 0x01 << irq_num;
- // This should only be for x86 as it sets the Edge Level Control Register
- outb((u8) (temp_word & 0xFF), 0x4d0);
- outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1);
- rc = 0;
- }
+ /* This should only be for x86 as it sets the Edge Level
+ * Control Register
+ */
+ outb((u8) (temp_word & 0xFF), 0x4d0); outb((u8) ((temp_word &
+ 0xFF00) >> 8), 0x4d1); rc = 0; }
return rc;
}
-/*
- * WTF??? This function isn't in the code, yet a function calls it, but the
- * compiler optimizes it away? strange. Here as a placeholder to keep the
- * compiler happy.
- */
-static int PCI_ScanBusNonBridge (u8 bus, u8 device)
-{
- return 0;
-}
-
-static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev_num)
+static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 *dev_num)
{
u16 tdevice;
u32 work;
@@ -213,11 +212,11 @@ static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev
ctrl->pci_bus->number = bus_num;
for (tdevice = 0; tdevice < 0xFF; tdevice++) {
- //Scan for access first
+ /* Scan for access first */
if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1)
continue;
dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice);
- //Yep we got one. Not a bridge ?
+ /* Yep we got one. Not a bridge ? */
if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) {
*dev_num = tdevice;
dbg("found it !\n");
@@ -225,16 +224,16 @@ static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev
}
}
for (tdevice = 0; tdevice < 0xFF; tdevice++) {
- //Scan for access first
+ /* Scan for access first */
if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1)
continue;
dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice);
- //Yep we got one. bridge ?
+ /* Yep we got one. bridge ? */
if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(tdevice, 0), PCI_SECONDARY_BUS, &tbus);
+ /* XXX: no recursion, wtf? */
dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice);
- if (PCI_ScanBusNonBridge(tbus, tdevice) == 0)
- return 0;
+ return 0;
}
}
@@ -244,39 +243,23 @@ static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev
static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge)
{
- struct irq_routing_table *PCIIRQRoutingInfoLength;
- long len;
- long loop;
+ int loop, len;
u32 work;
-
u8 tbus, tdevice, tslot;
- PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table();
- if (!PCIIRQRoutingInfoLength)
- return -1;
-
- len = (PCIIRQRoutingInfoLength->size -
- sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
- // Make sure I got at least one entry
- if (len == 0) {
- kfree(PCIIRQRoutingInfoLength );
- return -1;
- }
-
+ len = cpqhp_routing_table_length();
for (loop = 0; loop < len; ++loop) {
- tbus = PCIIRQRoutingInfoLength->slots[loop].bus;
- tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn;
- tslot = PCIIRQRoutingInfoLength->slots[loop].slot;
+ tbus = cpqhp_routing_table->slots[loop].bus;
+ tdevice = cpqhp_routing_table->slots[loop].devfn;
+ tslot = cpqhp_routing_table->slots[loop].slot;
if (tslot == slot) {
*bus_num = tbus;
*dev_num = tdevice;
ctrl->pci_bus->number = tbus;
pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_VENDOR_ID, &work);
- if (!nobridge || (work == 0xffffffff)) {
- kfree(PCIIRQRoutingInfoLength );
+ if (!nobridge || (work == 0xffffffff))
return 0;
- }
dbg("bus_num %d devfn %d\n", *bus_num, *dev_num);
pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_CLASS_REVISION, &work);
@@ -287,28 +270,26 @@ static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num
dbg("Scan bus for Non Bridge: bus %d\n", tbus);
if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) {
*bus_num = tbus;
- kfree(PCIIRQRoutingInfoLength );
return 0;
}
- } else {
- kfree(PCIIRQRoutingInfoLength );
+ } else
return 0;
- }
-
}
}
- kfree(PCIIRQRoutingInfoLength );
return -1;
}
-int cpqhp_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot)
+int cpqhp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot)
{
- return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0); //plain (bridges allowed)
+ /* plain (bridges allowed) */
+ return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0);
}
-/* More PCI configuration routines; this time centered around hotplug controller */
+/* More PCI configuration routines; this time centered around hotplug
+ * controller
+ */
/*
@@ -316,7 +297,7 @@ int cpqhp_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 s
*
* Reads configuration for all slots in a PCI bus and saves info.
*
- * Note: For non-hot plug busses, the slot # saved is the device #
+ * Note: For non-hot plug buses, the slot # saved is the device #
*
* returns 0 if success
*/
@@ -339,12 +320,12 @@ int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug)
int stop_it;
int index;
- // Decide which slots are supported
+ /* Decide which slots are supported */
if (is_hot_plug) {
- //*********************************
- // is_hot_plug is the slot mask
- //*********************************
+ /*
+ * is_hot_plug is the slot mask
+ */
FirstSupported = is_hot_plug >> 4;
LastSupported = FirstSupported + (is_hot_plug & 0x0F) - 1;
} else {
@@ -352,123 +333,127 @@ int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug)
LastSupported = 0x1F;
}
- // Save PCI configuration space for all devices in supported slots
+ /* Save PCI configuration space for all devices in supported slots */
ctrl->pci_bus->number = busnumber;
for (device = FirstSupported; device <= LastSupported; device++) {
ID = 0xFFFFFFFF;
- rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID);
+ rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID);
+
+ if (ID == 0xFFFFFFFF) {
+ if (is_hot_plug) {
+ /* Setup slot structure with entry for empty
+ * slot
+ */
+ new_slot = cpqhp_slot_create(busnumber);
+ if (new_slot == NULL)
+ return 1;
- if (ID != 0xFFFFFFFF) { // device in slot
- rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code);
- if (rc)
- return rc;
+ new_slot->bus = (u8) busnumber;
+ new_slot->device = (u8) device;
+ new_slot->function = 0;
+ new_slot->is_a_board = 0;
+ new_slot->presence_save = 0;
+ new_slot->switch_save = 0;
+ }
+ continue;
+ }
- rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type);
- if (rc)
- return rc;
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code);
+ if (rc)
+ return rc;
- // If multi-function device, set max_functions to 8
- if (header_type & 0x80)
- max_functions = 8;
- else
- max_functions = 1;
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
- function = 0;
+ /* If multi-function device, set max_functions to 8 */
+ if (header_type & 0x80)
+ max_functions = 8;
+ else
+ max_functions = 1;
- do {
- DevError = 0;
+ function = 0;
- if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // P-P Bridge
- // Recurse the subordinate bus
- // get the subordinate bus number
- rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus);
- if (rc) {
+ do {
+ DevError = 0;
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+ /* Recurse the subordinate bus
+ * get the subordinate bus number
+ */
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus);
+ if (rc) {
+ return rc;
+ } else {
+ sub_bus = (int) secondary_bus;
+
+ /* Save secondary bus cfg spc
+ * with this recursive call.
+ */
+ rc = cpqhp_save_config(ctrl, sub_bus, 0);
+ if (rc)
return rc;
- } else {
- sub_bus = (int) secondary_bus;
-
- // Save secondary bus cfg spc
- // with this recursive call.
- rc = cpqhp_save_config(ctrl, sub_bus, 0);
- if (rc)
- return rc;
- ctrl->pci_bus->number = busnumber;
- }
+ ctrl->pci_bus->number = busnumber;
}
+ }
- index = 0;
+ index = 0;
+ new_slot = cpqhp_slot_find(busnumber, device, index++);
+ while (new_slot &&
+ (new_slot->function != (u8) function))
new_slot = cpqhp_slot_find(busnumber, device, index++);
- while (new_slot &&
- (new_slot->function != (u8) function))
- new_slot = cpqhp_slot_find(busnumber, device, index++);
-
- if (!new_slot) {
- // Setup slot structure.
- new_slot = cpqhp_slot_create(busnumber);
-
- if (new_slot == NULL)
- return(1);
- }
- new_slot->bus = (u8) busnumber;
- new_slot->device = (u8) device;
- new_slot->function = (u8) function;
- new_slot->is_a_board = 1;
- new_slot->switch_save = 0x10;
- // In case of unsupported board
- new_slot->status = DevError;
- new_slot->pci_dev = pci_find_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function);
-
- for (cloop = 0; cloop < 0x20; cloop++) {
- rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
- if (rc)
- return rc;
- }
+ if (!new_slot) {
+ /* Setup slot structure. */
+ new_slot = cpqhp_slot_create(busnumber);
+ if (new_slot == NULL)
+ return 1;
+ }
- function++;
+ new_slot->bus = (u8) busnumber;
+ new_slot->device = (u8) device;
+ new_slot->function = (u8) function;
+ new_slot->is_a_board = 1;
+ new_slot->switch_save = 0x10;
+ /* In case of unsupported board */
+ new_slot->status = DevError;
+ new_slot->pci_dev = pci_get_bus_and_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function);
- stop_it = 0;
+ for (cloop = 0; cloop < 0x20; cloop++) {
+ rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
+ if (rc)
+ return rc;
+ }
- // this loop skips to the next present function
- // reading in Class Code and Header type.
+ pci_dev_put(new_slot->pci_dev);
- while ((function < max_functions)&&(!stop_it)) {
- rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID);
- if (ID == 0xFFFFFFFF) { // nothing there.
- function++;
- } else { // Something there
- rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code);
- if (rc)
- return rc;
+ function++;
- rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type);
- if (rc)
- return rc;
+ stop_it = 0;
- stop_it++;
- }
+ /* this loop skips to the next present function
+ * reading in Class Code and Header type.
+ */
+ while ((function < max_functions) && (!stop_it)) {
+ rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID);
+ if (ID == 0xFFFFFFFF) {
+ function++;
+ continue;
}
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code);
+ if (rc)
+ return rc;
- } while (function < max_functions);
- } // End of IF (device in slot?)
- else if (is_hot_plug) {
- // Setup slot structure with entry for empty slot
- new_slot = cpqhp_slot_create(busnumber);
+ rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
- if (new_slot == NULL) {
- return(1);
+ stop_it++;
}
- new_slot->bus = (u8) busnumber;
- new_slot->device = (u8) device;
- new_slot->function = 0;
- new_slot->is_a_board = 0;
- new_slot->presence_save = 0;
- new_slot->switch_save = 0;
- }
- } // End of FOR loop
+ } while (function < max_functions);
+ } /* End of FOR loop */
- return(0);
+ return 0;
}
@@ -476,11 +461,11 @@ int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug)
* cpqhp_save_slot_config
*
* Saves configuration info for all PCI devices in a given slot
- * including subordinate busses.
+ * including subordinate buses.
*
* returns 0 if success
*/
-int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot)
+int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func *new_slot)
{
long rc;
u8 class_code;
@@ -489,7 +474,7 @@ int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot)
u8 secondary_bus;
int sub_bus;
int max_functions;
- int function;
+ int function = 0;
int cloop = 0;
int stop_it;
@@ -498,63 +483,58 @@ int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot)
ctrl->pci_bus->number = new_slot->bus;
pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID);
- if (ID != 0xFFFFFFFF) { // device in slot
- pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code);
- pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type);
-
- if (header_type & 0x80) // Multi-function device
- max_functions = 8;
- else
- max_functions = 1;
-
- function = 0;
-
- do {
- if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
- // Recurse the subordinate bus
- pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus);
+ if (ID == 0xFFFFFFFF)
+ return 2;
- sub_bus = (int) secondary_bus;
+ pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code);
+ pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type);
- // Save the config headers for the secondary bus.
- rc = cpqhp_save_config(ctrl, sub_bus, 0);
- if (rc)
- return(rc);
- ctrl->pci_bus->number = new_slot->bus;
+ if (header_type & 0x80) /* Multi-function device */
+ max_functions = 8;
+ else
+ max_functions = 1;
- } // End of IF
+ while (function < max_functions) {
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+ /* Recurse the subordinate bus */
+ pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus);
- new_slot->status = 0;
+ sub_bus = (int) secondary_bus;
- for (cloop = 0; cloop < 0x20; cloop++) {
- pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
- }
+ /* Save the config headers for the secondary
+ * bus.
+ */
+ rc = cpqhp_save_config(ctrl, sub_bus, 0);
+ if (rc)
+ return(rc);
+ ctrl->pci_bus->number = new_slot->bus;
- function++;
+ }
- stop_it = 0;
+ new_slot->status = 0;
- // this loop skips to the next present function
- // reading in the Class Code and the Header type.
+ for (cloop = 0; cloop < 0x20; cloop++)
+ pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
- while ((function < max_functions) && (!stop_it)) {
- pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID);
+ function++;
- if (ID == 0xFFFFFFFF) { // nothing there.
- function++;
- } else { // Something there
- pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code);
+ stop_it = 0;
- pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type);
+ /* this loop skips to the next present function
+ * reading in the Class Code and the Header type.
+ */
+ while ((function < max_functions) && (!stop_it)) {
+ pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID);
- stop_it++;
- }
+ if (ID == 0xFFFFFFFF)
+ function++;
+ else {
+ pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code);
+ pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type);
+ stop_it++;
}
+ }
- } while (function < max_functions);
- } // End of IF (device in slot?)
- else {
- return 2;
}
return 0;
@@ -569,7 +549,7 @@ int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot)
*
* returns 0 if success
*/
-int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func)
+int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func *func)
{
u8 cloop;
u8 header_type;
@@ -590,11 +570,10 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func)
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
- // Check for Bridge
+ /* Check for Bridge */
pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
- // PCI-PCI Bridge
pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
sub_bus = (int) secondary_bus;
@@ -610,23 +589,27 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func)
}
pci_bus->number = func->bus;
- //FIXME: this loop is duplicated in the non-bridge case. The two could be rolled together
- // Figure out IO and memory base lengths
+ /* FIXME: this loop is duplicated in the non-bridge
+ * case. The two could be rolled together Figure out
+ * IO and memory base lengths
+ */
for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
temp_register = 0xFFFFFFFF;
pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
-
- if (base) { // If this register is implemented
+ /* If this register is implemented */
+ if (base) {
if (base & 0x01L) {
- // IO base
- // set base = amount of IO space requested
+ /* IO base
+ * set base = amount of IO space
+ * requested
+ */
base = base & 0xFFFFFFFE;
base = (~base) + 1;
type = 1;
} else {
- // memory base
+ /* memory base */
base = base & 0xFFFFFFF0;
base = (~base) + 1;
@@ -637,32 +620,36 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func)
type = 0;
}
- // Save information in slot structure
+ /* Save information in slot structure */
func->base_length[(cloop - 0x10) >> 2] =
base;
func->base_type[(cloop - 0x10) >> 2] = type;
- } // End of base register loop
+ } /* End of base register loop */
-
- } else if ((header_type & 0x7F) == 0x00) { // PCI-PCI Bridge
- // Figure out IO and memory base lengths
+ } else if ((header_type & 0x7F) == 0x00) {
+ /* Figure out IO and memory base lengths */
for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
temp_register = 0xFFFFFFFF;
pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
- if (base) { // If this register is implemented
+ /* If this register is implemented */
+ if (base) {
if (base & 0x01L) {
- // IO base
- // base = amount of IO space requested
+ /* IO base
+ * base = amount of IO space
+ * requested
+ */
base = base & 0xFFFFFFFE;
base = (~base) + 1;
type = 1;
} else {
- // memory base
- // base = amount of memory space requested
+ /* memory base
+ * base = amount of memory
+ * space requested
+ */
base = base & 0xFFFFFFF0;
base = (~base) + 1;
@@ -673,16 +660,16 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func)
type = 0;
}
- // Save information in slot structure
+ /* Save information in slot structure */
func->base_length[(cloop - 0x10) >> 2] = base;
func->base_type[(cloop - 0x10) >> 2] = type;
- } // End of base register loop
+ } /* End of base register loop */
- } else { // Some other unknown header type
+ } else { /* Some other unknown header type */
}
- // find the next device in this slot
+ /* find the next device in this slot */
func = cpqhp_slot_find(func->bus, func->device, index++);
}
@@ -699,7 +686,7 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func)
*
* returns 0 if success
*/
-int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
+int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func *func)
{
u8 cloop;
u8 header_type;
@@ -728,18 +715,18 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
- // Save the command register
+ /* Save the command register */
pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command);
- // disable card
+ /* disable card */
command = 0x00;
pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
- // Check for Bridge
+ /* Check for Bridge */
pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
- if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
- // Clear Bridge Control Register
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+ /* Clear Bridge Control Register */
command = 0x00;
pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
@@ -755,7 +742,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
bus_node->next = func->bus_head;
func->bus_head = bus_node;
- // Save IO base and Limit registers
+ /* Save IO base and Limit registers */
pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &b_base);
pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &b_length);
@@ -771,7 +758,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
func->io_head = io_node;
}
- // Save memory base and Limit registers
+ /* Save memory base and Limit registers */
pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base);
pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length);
@@ -787,7 +774,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
func->mem_head = mem_node;
}
- // Save prefetchable memory base and Limit registers
+ /* Save prefetchable memory base and Limit registers */
pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base);
pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length);
@@ -802,7 +789,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
}
- // Figure out IO and memory base lengths
+ /* Figure out IO and memory base lengths */
for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
pci_bus_read_config_dword (pci_bus, devfn, cloop, &save_base);
@@ -812,11 +799,14 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
temp_register = base;
- if (base) { // If this register is implemented
+ /* If this register is implemented */
+ if (base) {
if (((base & 0x03L) == 0x01)
&& (save_command & 0x01)) {
- // IO base
- // set temp_register = amount of IO space requested
+ /* IO base
+ * set temp_register = amount
+ * of IO space requested
+ */
temp_register = base & 0xFFFFFFFE;
temp_register = (~temp_register) + 1;
@@ -834,7 +824,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
} else
if (((base & 0x0BL) == 0x08)
&& (save_command & 0x02)) {
- // prefetchable memory base
+ /* prefetchable memory base */
temp_register = base & 0xFFFFFFF0;
temp_register = (~temp_register) + 1;
@@ -851,7 +841,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
} else
if (((base & 0x0BL) == 0x00)
&& (save_command & 0x02)) {
- // prefetchable memory base
+ /* prefetchable memory base */
temp_register = base & 0xFFFFFFF0;
temp_register = (~temp_register) + 1;
@@ -868,9 +858,10 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
} else
return(1);
}
- } // End of base register loop
- } else if ((header_type & 0x7F) == 0x00) { // Standard header
- // Figure out IO and memory base lengths
+ } /* End of base register loop */
+ /* Standard header */
+ } else if ((header_type & 0x7F) == 0x00) {
+ /* Figure out IO and memory base lengths */
for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base);
@@ -880,11 +871,14 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
temp_register = base;
- if (base) { // If this register is implemented
+ /* If this register is implemented */
+ if (base) {
if (((base & 0x03L) == 0x01)
&& (save_command & 0x01)) {
- // IO base
- // set temp_register = amount of IO space requested
+ /* IO base
+ * set temp_register = amount
+ * of IO space requested
+ */
temp_register = base & 0xFFFFFFFE;
temp_register = (~temp_register) + 1;
@@ -901,7 +895,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
} else
if (((base & 0x0BL) == 0x08)
&& (save_command & 0x02)) {
- // prefetchable memory base
+ /* prefetchable memory base */
temp_register = base & 0xFFFFFFF0;
temp_register = (~temp_register) + 1;
@@ -918,7 +912,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
} else
if (((base & 0x0BL) == 0x00)
&& (save_command & 0x02)) {
- // prefetchable memory base
+ /* prefetchable memory base */
temp_register = base & 0xFFFFFFF0;
temp_register = (~temp_register) + 1;
@@ -935,15 +929,14 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
} else
return(1);
}
- } // End of base register loop
- } else { // Some other unknown header type
+ } /* End of base register loop */
}
- // find the next device in this slot
+ /* find the next device in this slot */
func = cpqhp_slot_find(func->bus, func->device, index++);
}
- return(0);
+ return 0;
}
@@ -956,7 +949,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
*
* returns 0 if success
*/
-int cpqhp_configure_board(struct controller *ctrl, struct pci_func * func)
+int cpqhp_configure_board(struct controller *ctrl, struct pci_func *func)
{
int cloop;
u8 header_type;
@@ -975,16 +968,16 @@ int cpqhp_configure_board(struct controller *ctrl, struct pci_func * func)
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
- // Start at the top of config space so that the control
- // registers are programmed last
- for (cloop = 0x3C; cloop > 0; cloop -= 4) {
+ /* Start at the top of config space so that the control
+ * registers are programmed last
+ */
+ for (cloop = 0x3C; cloop > 0; cloop -= 4)
pci_bus_write_config_dword (pci_bus, devfn, cloop, func->config_space[cloop >> 2]);
- }
pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
- // If this is a bridge device, restore subordinate devices
- if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
+ /* If this is a bridge device, restore subordinate devices */
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
sub_bus = (int) secondary_bus;
@@ -1000,8 +993,9 @@ int cpqhp_configure_board(struct controller *ctrl, struct pci_func * func)
}
} else {
- // Check all the base Address Registers to make sure
- // they are the same. If not, the board is different.
+ /* Check all the base Address Registers to make sure
+ * they are the same. If not, the board is different.
+ */
for (cloop = 16; cloop < 40; cloop += 4) {
pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp);
@@ -1033,7 +1027,7 @@ int cpqhp_configure_board(struct controller *ctrl, struct pci_func * func)
*
* returns 0 if the board is the same nonzero otherwise
*/
-int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func)
+int cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func)
{
u8 cloop;
u8 header_type;
@@ -1058,27 +1052,28 @@ int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func)
pci_bus_read_config_dword (pci_bus, devfn, PCI_VENDOR_ID, &temp_register);
- // No adapter present
+ /* No adapter present */
if (temp_register == 0xFFFFFFFF)
return(NO_ADAPTER_PRESENT);
if (temp_register != func->config_space[0])
return(ADAPTER_NOT_SAME);
- // Check for same revision number and class code
+ /* Check for same revision number and class code */
pci_bus_read_config_dword (pci_bus, devfn, PCI_CLASS_REVISION, &temp_register);
- // Adapter not the same
+ /* Adapter not the same */
if (temp_register != func->config_space[0x08 >> 2])
return(ADAPTER_NOT_SAME);
- // Check for Bridge
+ /* Check for Bridge */
pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
- if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
- // In order to continue checking, we must program the
- // bus registers in the bridge to respond to accesses
- // for it's subordinate bus(es)
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+ /* In order to continue checking, we must program the
+ * bus registers in the bridge to respond to accesses
+ * for its subordinate bus(es)
+ */
temp_register = func->config_space[0x18 >> 2];
pci_bus_write_config_dword (pci_bus, devfn, PCI_PRIMARY_BUS, temp_register);
@@ -1096,35 +1091,39 @@ int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func)
}
}
- // Check to see if it is a standard config header
+ /* Check to see if it is a standard config header */
else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
- // Check subsystem vendor and ID
+ /* Check subsystem vendor and ID */
pci_bus_read_config_dword (pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register);
if (temp_register != func->config_space[0x2C >> 2]) {
- // If it's a SMART-2 and the register isn't filled
- // in, ignore the difference because
- // they just have an old rev of the firmware
-
+ /* If it's a SMART-2 and the register isn't
+ * filled in, ignore the difference because
+ * they just have an old rev of the firmware
+ */
if (!((func->config_space[0] == 0xAE100E11)
&& (temp_register == 0x00L)))
return(ADAPTER_NOT_SAME);
}
- // Figure out IO and memory base lengths
+ /* Figure out IO and memory base lengths */
for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
temp_register = 0xFFFFFFFF;
pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
- if (base) { // If this register is implemented
+
+ /* If this register is implemented */
+ if (base) {
if (base & 0x01L) {
- // IO base
- // set base = amount of IO space requested
+ /* IO base
+ * set base = amount of IO
+ * space requested
+ */
base = base & 0xFFFFFFFE;
base = (~base) + 1;
type = 1;
} else {
- // memory base
+ /* memory base */
base = base & 0xFFFFFFF0;
base = (~base) + 1;
@@ -1135,23 +1134,24 @@ int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func)
type = 0;
}
- // Check information in slot structure
+ /* Check information in slot structure */
if (func->base_length[(cloop - 0x10) >> 2] != base)
return(ADAPTER_NOT_SAME);
if (func->base_type[(cloop - 0x10) >> 2] != type)
return(ADAPTER_NOT_SAME);
- } // End of base register loop
+ } /* End of base register loop */
- } // End of (type 0 config space) else
+ } /* End of (type 0 config space) else */
else {
- // this is not a type 0 or 1 config space header so
- // we don't know how to do it
+ /* this is not a type 0 or 1 config space header so
+ * we don't know how to do it
+ */
return(DEVICE_TYPE_NOT_SUPPORTED);
}
- // Get the next function
+ /* Get the next function */
func = cpqhp_slot_find(func->bus, func->device, index++);
}
@@ -1168,7 +1168,7 @@ int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func)
* this function is for hot plug ADD!
*
* returns 0 if success
- */
+ */
int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_start)
{
u8 temp;
@@ -1187,10 +1187,10 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st
rom_resource_table = detect_HRT_floating_pointer(rom_start, rom_start+0xffff);
dbg("rom_resource_table = %p\n", rom_resource_table);
- if (rom_resource_table == NULL) {
+ if (rom_resource_table == NULL)
return -ENODEV;
- }
- // Sum all resources and setup resource maps
+
+ /* Sum all resources and setup resource maps */
unused_IRQ = readl(rom_resource_table + UNUSED_IRQ);
dbg("unused_IRQ = %x\n", unused_IRQ);
@@ -1222,13 +1222,11 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st
temp = 0;
- if (!cpqhp_nic_irq) {
+ if (!cpqhp_nic_irq)
cpqhp_nic_irq = ctrl->cfgspc_irq;
- }
- if (!cpqhp_disk_irq) {
+ if (!cpqhp_disk_irq)
cpqhp_disk_irq = ctrl->cfgspc_irq;
- }
dbg("cpqhp_disk_irq, cpqhp_nic_irq= %d, %d\n", cpqhp_disk_irq, cpqhp_nic_irq);
@@ -1262,13 +1260,13 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st
dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length,
primary_bus, secondary_bus, max_bus);
- // If this entry isn't for our controller's bus, ignore it
+ /* If this entry isn't for our controller's bus, ignore it */
if (primary_bus != ctrl->bus) {
i--;
one_slot += sizeof (struct slot_rt);
continue;
}
- // find out if this entry is for an occupied slot
+ /* find out if this entry is for an occupied slot */
ctrl->pci_bus->number = primary_bus;
pci_bus_read_config_dword (ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword);
dbg("temp_D_word = %x\n", temp_dword);
@@ -1282,13 +1280,13 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st
func = cpqhp_slot_find(primary_bus, dev_func >> 3, index++);
}
- // If we can't find a match, skip this table entry
+ /* If we can't find a match, skip this table entry */
if (!func) {
i--;
one_slot += sizeof (struct slot_rt);
continue;
}
- // this may not work and shouldn't be used
+ /* this may not work and shouldn't be used */
if (secondary_bus != primary_bus)
bridged_slot = 1;
else
@@ -1301,7 +1299,7 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st
}
- // If we've got a valid IO base, use it
+ /* If we've got a valid IO base, use it */
temp_dword = io_base + io_length;
@@ -1325,7 +1323,7 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st
}
}
- // If we've got a valid memory base, use it
+ /* If we've got a valid memory base, use it */
temp_dword = mem_base + mem_length;
if ((mem_base) && (temp_dword < 0x10000)) {
mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL);
@@ -1348,8 +1346,9 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st
}
}
- // If we've got a valid prefetchable memory base, and
- // the base + length isn't greater than 0xFFFF
+ /* If we've got a valid prefetchable memory base, and
+ * the base + length isn't greater than 0xFFFF
+ */
temp_dword = pre_mem_base + pre_mem_length;
if ((pre_mem_base) && (temp_dword < 0x10000)) {
p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL);
@@ -1372,9 +1371,10 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st
}
}
- // If we've got a valid bus number, use it
- // The second condition is to ignore bus numbers on
- // populated slots that don't have PCI-PCI bridges
+ /* If we've got a valid bus number, use it
+ * The second condition is to ignore bus numbers on
+ * populated slots that don't have PCI-PCI bridges
+ */
if (secondary_bus && (secondary_bus != primary_bus)) {
bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL);
if (!bus_node)
@@ -1398,8 +1398,9 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st
one_slot += sizeof (struct slot_rt);
}
- // If all of the following fail, we don't have any resources for
- // hot plug add
+ /* If all of the following fail, we don't have any resources for
+ * hot plug add
+ */
rc = 1;
rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
@@ -1418,12 +1419,12 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st
*
* returns 0 if success
*/
-int cpqhp_return_board_resources(struct pci_func * func, struct resource_lists * resources)
+int cpqhp_return_board_resources(struct pci_func *func, struct resource_lists *resources)
{
int rc = 0;
struct pci_resource *node;
struct pci_resource *t_node;
- dbg("%s\n", __FUNCTION__);
+ dbg("%s\n", __func__);
if (!func)
return 1;
@@ -1474,7 +1475,7 @@ int cpqhp_return_board_resources(struct pci_func * func, struct resource_lists *
*
* Puts node back in the resource list pointed to by head
*/
-void cpqhp_destroy_resource_list (struct resource_lists * resources)
+void cpqhp_destroy_resource_list (struct resource_lists *resources)
{
struct pci_resource *res, *tres;
@@ -1521,7 +1522,7 @@ void cpqhp_destroy_resource_list (struct resource_lists * resources)
*
* Puts node back in the resource list pointed to by head
*/
-void cpqhp_destroy_board_resources (struct pci_func * func)
+void cpqhp_destroy_board_resources (struct pci_func *func)
{
struct pci_resource *res, *tres;
@@ -1561,4 +1562,3 @@ void cpqhp_destroy_board_resources (struct pci_func * func)
kfree(tres);
}
}
-
diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c
index a13abf55d78..4a392c44e3d 100644
--- a/drivers/pci/hotplug/cpqphp_sysfs.c
+++ b/drivers/pci/hotplug/cpqphp_sysfs.c
@@ -28,14 +28,17 @@
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
+#include <linux/mutex.h>
#include <linux/debugfs.h>
#include "cpqphp.h"
+static DEFINE_MUTEX(cpqphp_mutex);
static int show_ctrl (struct controller *ctrl, char *buf)
{
char *out = buf;
@@ -76,7 +79,7 @@ static int show_ctrl (struct controller *ctrl, char *buf)
static int show_dev (struct controller *ctrl, char *buf)
{
- char * out = buf;
+ char *out = buf;
int index;
struct pci_resource *res;
struct pci_func *new_slot;
@@ -145,7 +148,7 @@ static int open(struct inode *inode, struct file *file)
struct ctrl_dbg *dbg;
int retval = -ENOMEM;
- lock_kernel();
+ mutex_lock(&cpqphp_mutex);
dbg = kmalloc(sizeof(*dbg), GFP_KERNEL);
if (!dbg)
goto exit;
@@ -158,32 +161,14 @@ static int open(struct inode *inode, struct file *file)
file->private_data = dbg;
retval = 0;
exit:
- unlock_kernel();
+ mutex_unlock(&cpqphp_mutex);
return retval;
}
static loff_t lseek(struct file *file, loff_t off, int whence)
{
- struct ctrl_dbg *dbg;
- loff_t new = -1;
-
- lock_kernel();
- dbg = file->private_data;
-
- switch (whence) {
- case 0:
- new = off;
- break;
- case 1:
- new = file->f_pos + off;
- break;
- }
- if (new < 0 || new > dbg->size) {
- unlock_kernel();
- return -EINVAL;
- }
- unlock_kernel();
- return (file->f_pos = new);
+ struct ctrl_dbg *dbg = file->private_data;
+ return fixed_size_llseek(file, off, whence, dbg->size);
}
static ssize_t read(struct file *file, char __user *buf,
@@ -225,7 +210,8 @@ void cpqhp_shutdown_debugfs(void)
void cpqhp_create_debugfs_files(struct controller *ctrl)
{
- ctrl->dentry = debugfs_create_file(ctrl->pci_dev->dev.bus_id, S_IRUGO, root, ctrl, &debug_ops);
+ ctrl->dentry = debugfs_create_file(dev_name(&ctrl->pci_dev->dev),
+ S_IRUGO, root, ctrl, &debug_ops);
}
void cpqhp_remove_debugfs_files(struct controller *ctrl)
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
deleted file mode 100644
index 94b640146d4..00000000000
--- a/drivers/pci/hotplug/fakephp.c
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Fake PCI Hot Plug Controller Driver
- *
- * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
- * Copyright (C) 2003 IBM Corp.
- * Copyright (C) 2003 Rolf Eike Beer <eike-kernel@sf-tec.de>
- *
- * Based on ideas and code from:
- * Vladimir Kondratiev <vladimir.kondratiev@intel.com>
- * Rolf Eike Beer <eike-kernel@sf-tec.de>
- *
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 2 of the License.
- *
- * Send feedback to <greg@kroah.com>
- */
-
-/*
- *
- * This driver will "emulate" removing PCI devices from the system. If
- * the "power" file is written to with "0" then the specified PCI device
- * will be completely removed from the kernel.
- *
- * WARNING, this does NOT turn off the power to the PCI device. This is
- * a "logical" removal, not a physical or electrical removal.
- *
- * Use this module at your own risk, you have been warned!
- *
- * Enabling PCI devices is left as an exercise for the reader...
- *
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/pci_hotplug.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/workqueue.h>
-#include "../pci.h"
-
-#if !defined(MODULE)
- #define MY_NAME "fakephp"
-#else
- #define MY_NAME THIS_MODULE->name
-#endif
-
-#define dbg(format, arg...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG "%s: " format, \
- MY_NAME , ## arg); \
- } while (0)
-#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
-#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
-
-#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
-#define DRIVER_DESC "Fake PCI Hot Plug Controller Driver"
-
-struct dummy_slot {
- struct list_head node;
- struct hotplug_slot *slot;
- struct pci_dev *dev;
- struct work_struct remove_work;
- unsigned long removed;
-};
-
-static int debug;
-static LIST_HEAD(slot_list);
-static struct workqueue_struct *dummyphp_wq;
-
-static void pci_rescan_worker(struct work_struct *work);
-static DECLARE_WORK(pci_rescan_work, pci_rescan_worker);
-
-static int enable_slot (struct hotplug_slot *slot);
-static int disable_slot (struct hotplug_slot *slot);
-
-static struct hotplug_slot_ops dummy_hotplug_slot_ops = {
- .owner = THIS_MODULE,
- .enable_slot = enable_slot,
- .disable_slot = disable_slot,
-};
-
-static void dummy_release(struct hotplug_slot *slot)
-{
- struct dummy_slot *dslot = slot->private;
-
- list_del(&dslot->node);
- kfree(dslot->slot->info);
- kfree(dslot->slot);
- pci_dev_put(dslot->dev);
- kfree(dslot);
-}
-
-static int add_slot(struct pci_dev *dev)
-{
- struct dummy_slot *dslot;
- struct hotplug_slot *slot;
- int retval = -ENOMEM;
-
- slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
- if (!slot)
- goto error;
-
- slot->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
- if (!slot->info)
- goto error_slot;
-
- slot->info->power_status = 1;
- 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->ops = &dummy_hotplug_slot_ops;
- slot->release = &dummy_release;
- slot->private = dslot;
-
- retval = pci_hp_register(slot);
- if (retval) {
- err("pci_hp_register failed with error %d\n", retval);
- goto error_dslot;
- }
-
- dslot->slot = slot;
- dslot->dev = pci_dev_get(dev);
- list_add (&dslot->node, &slot_list);
- return retval;
-
-error_dslot:
- kfree(dslot);
-error_info:
- kfree(slot->info);
-error_slot:
- kfree(slot);
-error:
- return retval;
-}
-
-static int __init pci_scan_buses(void)
-{
- struct pci_dev *dev = NULL;
- int retval = 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;
- }
- }
-
- return retval;
-}
-
-static void remove_slot(struct dummy_slot *dslot)
-{
- int retval;
-
- dbg("removing slot %s\n", dslot->slot->name);
- retval = pci_hp_deregister(dslot->slot);
- if (retval)
- err("Problem unregistering a slot %s\n", dslot->slot->name);
-}
-
-/* called from the single-threaded workqueue handler to remove a slot */
-static void remove_slot_worker(struct work_struct *work)
-{
- struct dummy_slot *dslot =
- container_of(work, struct dummy_slot, remove_work);
- remove_slot(dslot);
-}
-
-/**
- * pci_rescan_slot - Rescan slot
- * @temp: Device template. Should be set: bus and devfn.
- *
- * Tries hard not to re-enable already existing devices;
- * also handles scanning of subfunctions.
- */
-static void pci_rescan_slot(struct pci_dev *temp)
-{
- struct pci_bus *bus = temp->bus;
- struct pci_dev *dev;
- int func;
- int retval;
- u8 hdr_type;
-
- if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
- temp->hdr_type = hdr_type & 0x7f;
- if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
- pci_dev_put(dev);
- else {
- dev = pci_scan_single_device(bus, temp->devfn);
- if (dev) {
- dbg("New device on %s function %x:%x\n",
- bus->name, temp->devfn >> 3,
- temp->devfn & 7);
- retval = pci_bus_add_device(dev);
- if (retval)
- dev_err(&dev->dev, "error adding "
- "device, continuing.\n");
- else
- add_slot(dev);
- }
- }
- /* multifunction device? */
- if (!(hdr_type & 0x80))
- return;
-
- /* continue scanning for other functions */
- for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) {
- if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type))
- continue;
- temp->hdr_type = hdr_type & 0x7f;
-
- if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
- pci_dev_put(dev);
- else {
- dev = pci_scan_single_device(bus, temp->devfn);
- if (dev) {
- dbg("New device on %s function %x:%x\n",
- bus->name, temp->devfn >> 3,
- temp->devfn & 7);
- retval = pci_bus_add_device(dev);
- if (retval)
- dev_err(&dev->dev, "error adding "
- "device, continuing.\n");
- else
- add_slot(dev);
- }
- }
- }
- }
-}
-
-
-/**
- * pci_rescan_bus - Rescan PCI bus
- * @bus: the PCI bus to rescan
- *
- * Call pci_rescan_slot for each possible function of the bus.
- */
-static void pci_rescan_bus(const struct pci_bus *bus)
-{
- unsigned int devfn;
- struct pci_dev *dev;
- dev = alloc_pci_dev();
- if (!dev)
- return;
-
- dev->bus = (struct pci_bus*)bus;
- dev->sysdata = bus->sysdata;
- for (devfn = 0; devfn < 0x100; devfn += 8) {
- dev->devfn = devfn;
- pci_rescan_slot(dev);
- }
- kfree(dev);
-}
-
-/* recursively scan all buses */
-static void pci_rescan_buses(const struct list_head *list)
-{
- const struct list_head *l;
- list_for_each(l,list) {
- const struct pci_bus *b = pci_bus_b(l);
- pci_rescan_bus(b);
- pci_rescan_buses(&b->children);
- }
-}
-
-/* initiate rescan of all pci buses */
-static inline void pci_rescan(void) {
- pci_rescan_buses(&pci_root_buses);
-}
-
-/* called from the single-threaded workqueue handler to rescan all pci buses */
-static void pci_rescan_worker(struct work_struct *work)
-{
- pci_rescan();
-}
-
-static int enable_slot(struct hotplug_slot *hotplug_slot)
-{
- /* mis-use enable_slot for rescanning of the pci bus */
- cancel_work_sync(&pci_rescan_work);
- queue_work(dummyphp_wq, &pci_rescan_work);
- return -ENODEV;
-}
-
-/* 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;
-
- if (!slot)
- return -ENODEV;
- dslot = slot->private;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, 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");
- }
- }
-
- /* remove the device from the pci core */
- pci_remove_bus_device(dslot->dev);
-
- /* queue work item to blow away this sysfs entry and other parts. */
- INIT_WORK(&dslot->remove_work, remove_slot_worker);
- queue_work(dummyphp_wq, &dslot->remove_work);
-
- return 0;
-}
-
-static void cleanup_slots (void)
-{
- struct list_head *tmp;
- struct list_head *next;
- struct dummy_slot *dslot;
-
- destroy_workqueue(dummyphp_wq);
- list_for_each_safe (tmp, next, &slot_list) {
- dslot = list_entry (tmp, struct dummy_slot, node);
- remove_slot(dslot);
- }
-
-}
-
-static int __init dummyphp_init(void)
-{
- info(DRIVER_DESC "\n");
-
- dummyphp_wq = create_singlethread_workqueue(MY_NAME);
- if (!dummyphp_wq)
- return -ENOMEM;
-
- return pci_scan_buses();
-}
-
-
-static void __exit dummyphp_exit(void)
-{
- cleanup_slots();
-}
-
-module_init(dummyphp_init);
-module_exit(dummyphp_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
-
diff --git a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h
index 612d9630150..e3e46a7b3ee 100644
--- a/drivers/pci/hotplug/ibmphp.h
+++ b/drivers/pci/hotplug/ibmphp.h
@@ -59,7 +59,7 @@ extern int ibmphp_debug;
/************************************************************
-* RESOURE TYPE *
+* RESOURCE TYPE *
************************************************************/
#define EBDA_RSRC_TYPE_MASK 0x03
@@ -103,7 +103,7 @@ extern int ibmphp_debug;
//--------------------------------------------------------------
struct rio_table_hdr {
- u8 ver_num;
+ u8 ver_num;
u8 scal_count;
u8 riodev_count;
u16 offset;
@@ -127,7 +127,7 @@ struct scal_detail {
};
//--------------------------------------------------------------
-// RIO DETAIL
+// RIO DETAIL
//--------------------------------------------------------------
struct rio_detail {
@@ -152,7 +152,7 @@ struct opt_rio {
u8 first_slot_num;
u8 middle_num;
struct list_head opt_rio_list;
-};
+};
struct opt_rio_lo {
u8 rio_type;
@@ -161,7 +161,7 @@ struct opt_rio_lo {
u8 middle_num;
u8 pack_count;
struct list_head opt_rio_lo_list;
-};
+};
/****************************************************************
* HPC DESCRIPTOR NODE *
@@ -275,17 +275,17 @@ extern struct list_head ibmphp_slot_head;
* FUNCTION PROTOTYPES *
***********************************************************/
-extern void ibmphp_free_ebda_hpc_queue (void);
-extern int ibmphp_access_ebda (void);
-extern struct slot *ibmphp_get_slot_from_physical_num (u8);
-extern int ibmphp_get_total_hp_slots (void);
-extern void ibmphp_free_ibm_slot (struct slot *);
-extern void ibmphp_free_bus_info_queue (void);
-extern void ibmphp_free_ebda_pci_rsrc_queue (void);
-extern struct bus_info *ibmphp_find_same_bus_num (u32);
-extern int ibmphp_get_bus_index (u8);
-extern u16 ibmphp_get_total_controllers (void);
-extern int ibmphp_register_pci (void);
+void ibmphp_free_ebda_hpc_queue(void);
+int ibmphp_access_ebda(void);
+struct slot *ibmphp_get_slot_from_physical_num(u8);
+int ibmphp_get_total_hp_slots(void);
+void ibmphp_free_ibm_slot(struct slot *);
+void ibmphp_free_bus_info_queue(void);
+void ibmphp_free_ebda_pci_rsrc_queue(void);
+struct bus_info *ibmphp_find_same_bus_num(u32);
+int ibmphp_get_bus_index(u8);
+u16 ibmphp_get_total_controllers(void);
+int ibmphp_register_pci(void);
/* passed parameters */
#define MEM 0
@@ -381,24 +381,24 @@ struct res_needed {
/* functions */
-extern int ibmphp_rsrc_init (void);
-extern int ibmphp_add_resource (struct resource_node *);
-extern int ibmphp_remove_resource (struct resource_node *);
-extern int ibmphp_find_resource (struct bus_node *, u32, struct resource_node **, int);
-extern int ibmphp_check_resource (struct resource_node *, u8);
-extern int ibmphp_remove_bus (struct bus_node *, u8);
-extern void ibmphp_free_resources (void);
-extern int ibmphp_add_pfmem_from_mem (struct resource_node *);
-extern struct bus_node *ibmphp_find_res_bus (u8);
-extern void ibmphp_print_test (void); /* for debugging purposes */
+int ibmphp_rsrc_init(void);
+int ibmphp_add_resource(struct resource_node *);
+int ibmphp_remove_resource(struct resource_node *);
+int ibmphp_find_resource(struct bus_node *, u32, struct resource_node **, int);
+int ibmphp_check_resource(struct resource_node *, u8);
+int ibmphp_remove_bus(struct bus_node *, u8);
+void ibmphp_free_resources(void);
+int ibmphp_add_pfmem_from_mem(struct resource_node *);
+struct bus_node *ibmphp_find_res_bus(u8);
+void ibmphp_print_test(void); /* for debugging purposes */
-extern void ibmphp_hpc_initvars (void);
-extern int ibmphp_hpc_readslot (struct slot *, u8, u8 *);
-extern int ibmphp_hpc_writeslot (struct slot *, u8);
-extern void ibmphp_lock_operations (void);
-extern void ibmphp_unlock_operations (void);
-extern int ibmphp_hpc_start_poll_thread (void);
-extern void ibmphp_hpc_stop_poll_thread (void);
+void ibmphp_hpc_initvars(void);
+int ibmphp_hpc_readslot(struct slot *, u8, u8 *);
+int ibmphp_hpc_writeslot(struct slot *, u8);
+void ibmphp_lock_operations(void);
+void ibmphp_unlock_operations(void);
+int ibmphp_hpc_start_poll_thread(void);
+void ibmphp_hpc_stop_poll_thread(void);
//----------------------------------------------------------------------------
@@ -574,7 +574,7 @@ extern void ibmphp_hpc_stop_poll_thread (void);
#define HPC_CTLR_IRQ_PENDG 0x80
//----------------------------------------------------------------------------
-// HPC_CTLR_WROKING status return codes
+// HPC_CTLR_WORKING status return codes
//----------------------------------------------------------------------------
#define HPC_CTLR_WORKING_NO 0x00
#define HPC_CTLR_WORKING_YES 0x01
@@ -707,17 +707,16 @@ struct slot {
u8 device;
u8 number;
u8 real_physical_slot_num;
- char name[100];
u32 capabilities;
u8 supported_speed;
u8 supported_bus_mode;
+ u8 flag; /* this is for disable slot and polling */
+ u8 ctlr_index;
struct hotplug_slot *hotplug_slot;
struct controller *ctrl;
struct pci_func *func;
u8 irq[4];
- u8 flag; /* this is for disable slot and polling */
int bit_mode; /* 0 = 32, 1 = 64 */
- u8 ctlr_index;
struct bus_info *bus_on;
struct list_head ibm_slot_list;
u8 status;
@@ -750,11 +749,11 @@ struct controller {
/* Functions */
-extern int ibmphp_init_devno (struct slot **); /* This function is called from EBDA, so we need it not be static */
-extern int ibmphp_do_disable_slot (struct slot *slot_cur);
-extern int ibmphp_update_slot_info (struct slot *); /* This function is called from HPC, so we need it to not be be static */
-extern int ibmphp_configure_card (struct pci_func *, u8);
-extern int ibmphp_unconfigure_card (struct slot **, int);
+int ibmphp_init_devno(struct slot **); /* This function is called from EBDA, so we need it not be static */
+int ibmphp_do_disable_slot(struct slot *slot_cur);
+int ibmphp_update_slot_info(struct slot *); /* This function is called from HPC, so we need it to not be be static */
+int ibmphp_configure_card(struct pci_func *, u8);
+int ibmphp_unconfigure_card(struct slot **, int);
extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops;
#endif //__IBMPHP_H
diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c
index 87b6b8b280e..f7b8684a773 100644
--- a/drivers/pci/hotplug/ibmphp_core.c
+++ b/drivers/pci/hotplug/ibmphp_core.c
@@ -35,7 +35,7 @@
#include <linux/delay.h>
#include <linux/wait.h>
#include "../pci.h"
-#include "../../../arch/x86/pci/pci.h" /* for struct irq_routing_table */
+#include <asm/pci_x86.h> /* for struct irq_routing_table */
#include "ibmphp.h"
#define attn_on(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNON)
@@ -49,7 +49,7 @@
int ibmphp_debug;
-static int debug;
+static bool debug;
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC (debug, "Debugging mode enabled or not");
MODULE_LICENSE ("GPL");
@@ -58,7 +58,7 @@ MODULE_DESCRIPTION (DRIVER_DESC);
struct pci_bus *ibmphp_pci_bus;
static int max_slots;
-static int irqs[16]; /* PIC mode IRQ's we're using so far (in case MPS
+static int irqs[16]; /* PIC mode IRQs we're using so far (in case MPS
* tables don't provide default info for empty slots */
static int init_flag;
@@ -71,20 +71,20 @@ static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value)
return get_max_adapter_speed_1 (hs, value, 1);
}
*/
-static inline int get_cur_bus_info(struct slot **sl)
+static inline int get_cur_bus_info(struct slot **sl)
{
int rc = 1;
- struct slot * slot_cur = *sl;
+ struct slot *slot_cur = *sl;
debug("options = %x\n", slot_cur->ctrl->options);
- debug("revision = %x\n", slot_cur->ctrl->revision);
+ debug("revision = %x\n", slot_cur->ctrl->revision);
- if (READ_BUS_STATUS(slot_cur->ctrl))
+ if (READ_BUS_STATUS(slot_cur->ctrl))
rc = ibmphp_hpc_readslot(slot_cur, READ_BUSSTATUS, NULL);
-
- if (rc)
+
+ if (rc)
return rc;
-
+
slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED(slot_cur->busstatus);
if (READ_BUS_MODE(slot_cur->ctrl))
slot_cur->bus_on->current_bus_mode =
@@ -96,7 +96,7 @@ static inline int get_cur_bus_info(struct slot **sl)
slot_cur->busstatus,
slot_cur->bus_on->current_speed,
slot_cur->bus_on->current_bus_mode);
-
+
*sl = slot_cur;
return 0;
}
@@ -104,8 +104,8 @@ static inline int get_cur_bus_info(struct slot **sl)
static inline int slot_update(struct slot **sl)
{
int rc;
- rc = ibmphp_hpc_readslot(*sl, READ_ALLSTAT, NULL);
- if (rc)
+ rc = ibmphp_hpc_readslot(*sl, READ_ALLSTAT, NULL);
+ if (rc)
return rc;
if (!init_flag)
rc = get_cur_bus_info(sl);
@@ -114,8 +114,8 @@ static inline int slot_update(struct slot **sl)
static int __init get_max_slots (void)
{
- struct slot * slot_cur;
- struct list_head * tmp;
+ struct slot *slot_cur;
+ struct list_head *tmp;
u8 slot_count = 0;
list_for_each(tmp, &ibmphp_slot_head) {
@@ -148,50 +148,56 @@ int ibmphp_init_devno(struct slot **cur_slot)
len = (rtable->size - sizeof(struct irq_routing_table)) /
sizeof(struct irq_info);
- if (!len)
+ if (!len) {
+ kfree(rtable);
return -1;
+ }
for (loop = 0; loop < len; loop++) {
- if ((*cur_slot)->number == rtable->slots[loop].slot) {
- if ((*cur_slot)->bus == rtable->slots[loop].bus) {
+ if ((*cur_slot)->number == rtable->slots[loop].slot &&
+ (*cur_slot)->bus == rtable->slots[loop].bus) {
+ struct io_apic_irq_attr irq_attr;
+
(*cur_slot)->device = PCI_SLOT(rtable->slots[loop].devfn);
for (i = 0; i < 4; i++)
(*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector((int) (*cur_slot)->bus,
- (int) (*cur_slot)->device, i);
-
- debug("(*cur_slot)->irq[0] = %x\n",
- (*cur_slot)->irq[0]);
- debug("(*cur_slot)->irq[1] = %x\n",
- (*cur_slot)->irq[1]);
- debug("(*cur_slot)->irq[2] = %x\n",
- (*cur_slot)->irq[2]);
- debug("(*cur_slot)->irq[3] = %x\n",
- (*cur_slot)->irq[3]);
-
- debug("rtable->exlusive_irqs = %x\n",
+ (int) (*cur_slot)->device, i,
+ &irq_attr);
+
+ debug("(*cur_slot)->irq[0] = %x\n",
+ (*cur_slot)->irq[0]);
+ debug("(*cur_slot)->irq[1] = %x\n",
+ (*cur_slot)->irq[1]);
+ debug("(*cur_slot)->irq[2] = %x\n",
+ (*cur_slot)->irq[2]);
+ debug("(*cur_slot)->irq[3] = %x\n",
+ (*cur_slot)->irq[3]);
+
+ debug("rtable->exclusive_irqs = %x\n",
rtable->exclusive_irqs);
- debug("rtable->slots[loop].irq[0].bitmap = %x\n",
+ debug("rtable->slots[loop].irq[0].bitmap = %x\n",
rtable->slots[loop].irq[0].bitmap);
- debug("rtable->slots[loop].irq[1].bitmap = %x\n",
+ debug("rtable->slots[loop].irq[1].bitmap = %x\n",
rtable->slots[loop].irq[1].bitmap);
- debug("rtable->slots[loop].irq[2].bitmap = %x\n",
+ debug("rtable->slots[loop].irq[2].bitmap = %x\n",
rtable->slots[loop].irq[2].bitmap);
- debug("rtable->slots[loop].irq[3].bitmap = %x\n",
+ debug("rtable->slots[loop].irq[3].bitmap = %x\n",
rtable->slots[loop].irq[3].bitmap);
- debug("rtable->slots[loop].irq[0].link = %x\n",
+ debug("rtable->slots[loop].irq[0].link = %x\n",
rtable->slots[loop].irq[0].link);
- debug("rtable->slots[loop].irq[1].link = %x\n",
+ debug("rtable->slots[loop].irq[1].link = %x\n",
rtable->slots[loop].irq[1].link);
- debug("rtable->slots[loop].irq[2].link = %x\n",
+ debug("rtable->slots[loop].irq[2].link = %x\n",
rtable->slots[loop].irq[2].link);
- debug("rtable->slots[loop].irq[3].link = %x\n",
+ debug("rtable->slots[loop].irq[3].link = %x\n",
rtable->slots[loop].irq[3].link);
- debug("end of init_devno\n");
- return 0;
- }
+ debug("end of init_devno\n");
+ kfree(rtable);
+ return 0;
}
}
+ kfree(rtable);
return -1;
}
@@ -265,7 +271,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
else
rc = -ENODEV;
}
- } else
+ } else
rc = -ENODEV;
ibmphp_unlock_operations();
@@ -274,7 +280,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
return rc;
}
-static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
int rc = -ENODEV;
struct slot *pslot;
@@ -282,7 +288,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
debug("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n",
(ulong) hotplug_slot, (ulong) value);
-
+
ibmphp_lock_operations();
if (hotplug_slot) {
pslot = hotplug_slot->private;
@@ -305,7 +311,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
return rc;
}
-static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
int rc = -ENODEV;
struct slot *pslot;
@@ -332,7 +338,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value)
}
-static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
int rc = -ENODEV;
struct slot *pslot;
@@ -358,7 +364,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
return rc;
}
-static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 *value)
{
int rc = -ENODEV;
struct slot *pslot;
@@ -389,94 +395,45 @@ static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 * value)
return rc;
}
-static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+static int get_max_bus_speed(struct slot *slot)
{
- int rc = -ENODEV;
- struct slot *pslot;
+ int rc;
u8 mode = 0;
+ enum pci_bus_speed speed;
+ struct pci_bus *bus = slot->hotplug_slot->pci_slot->bus;
- debug("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__,
- hotplug_slot, value);
+ debug("%s - Entry slot[%p]\n", __func__, slot);
ibmphp_lock_operations();
-
- if (hotplug_slot) {
- pslot = hotplug_slot->private;
- if (pslot) {
- rc = 0;
- mode = pslot->supported_bus_mode;
- *value = pslot->supported_speed;
- switch (*value) {
- case BUS_SPEED_33:
- break;
- case BUS_SPEED_66:
- if (mode == BUS_MODE_PCIX)
- *value += 0x01;
- break;
- case BUS_SPEED_100:
- case BUS_SPEED_133:
- *value = pslot->supported_speed + 0x01;
- break;
- default:
- /* Note (will need to change): there would be soon 256, 512 also */
- rc = -ENODEV;
- }
- }
- }
-
+ mode = slot->supported_bus_mode;
+ speed = slot->supported_speed;
ibmphp_unlock_operations();
- debug("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, rc, *value);
- return rc;
-}
-
-static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
-{
- int rc = -ENODEV;
- struct slot *pslot;
- u8 mode = 0;
-
- debug("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__,
- hotplug_slot, value);
-
- ibmphp_lock_operations();
- if (hotplug_slot) {
- pslot = hotplug_slot->private;
- if (pslot) {
- rc = get_cur_bus_info(&pslot);
- if (!rc) {
- mode = pslot->bus_on->current_bus_mode;
- *value = pslot->bus_on->current_speed;
- switch (*value) {
- case BUS_SPEED_33:
- break;
- case BUS_SPEED_66:
- if (mode == BUS_MODE_PCIX)
- *value += 0x01;
- else if (mode == BUS_MODE_PCI)
- ;
- else
- *value = PCI_SPEED_UNKNOWN;
- break;
- case BUS_SPEED_100:
- case BUS_SPEED_133:
- *value += 0x01;
- break;
- default:
- /* Note of change: there would also be 256, 512 soon */
- rc = -ENODEV;
- }
- }
- }
+ switch (speed) {
+ case BUS_SPEED_33:
+ break;
+ case BUS_SPEED_66:
+ if (mode == BUS_MODE_PCIX)
+ speed += 0x01;
+ break;
+ case BUS_SPEED_100:
+ case BUS_SPEED_133:
+ speed += 0x01;
+ break;
+ default:
+ /* Note (will need to change): there would be soon 256, 512 also */
+ rc = -ENODEV;
}
- ibmphp_unlock_operations();
- debug("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, rc, *value);
+ if (!rc)
+ bus->max_bus_speed = speed;
+
+ debug("%s - Exit rc[%d] speed[%x]\n", __func__, rc, speed);
return rc;
}
/*
-static int get_max_adapter_speed_1(struct hotplug_slot *hotplug_slot, u8 * value, u8 flag)
+static int get_max_adapter_speed_1(struct hotplug_slot *hotplug_slot, u8 *value, u8 flag)
{
int rc = -ENODEV;
struct slot *pslot;
@@ -514,7 +471,7 @@ static int get_max_adapter_speed_1(struct hotplug_slot *hotplug_slot, u8 * value
return rc;
}
-static int get_bus_name(struct hotplug_slot *hotplug_slot, char * value)
+static int get_bus_name(struct hotplug_slot *hotplug_slot, char *value)
{
int rc = -ENODEV;
struct slot *pslot = NULL;
@@ -558,14 +515,15 @@ static int __init init_ops(void)
debug("BEFORE GETTING SLOT STATUS, slot # %x\n",
slot_cur->number);
- if (slot_cur->ctrl->revision == 0xFF)
+ if (slot_cur->ctrl->revision == 0xFF)
if (get_ctrl_revision(slot_cur,
&slot_cur->ctrl->revision))
return -1;
- if (slot_cur->bus_on->current_speed == 0xFF)
- if (get_cur_bus_info(&slot_cur))
+ if (slot_cur->bus_on->current_speed == 0xFF)
+ if (get_cur_bus_info(&slot_cur))
return -1;
+ get_max_bus_speed(slot_cur);
if (slot_cur->ctrl->options == 0xFF)
if (get_hpc_options(slot_cur, &slot_cur->ctrl->options))
@@ -581,8 +539,8 @@ static int __init init_ops(void)
debug("SLOT_PRESENT = %x\n", SLOT_PRESENT(slot_cur->status));
debug("SLOT_LATCH = %x\n", SLOT_LATCH(slot_cur->status));
- if ((SLOT_PWRGD(slot_cur->status)) &&
- !(SLOT_PRESENT(slot_cur->status)) &&
+ if ((SLOT_PWRGD(slot_cur->status)) &&
+ !(SLOT_PRESENT(slot_cur->status)) &&
!(SLOT_LATCH(slot_cur->status))) {
debug("BEFORE POWER OFF COMMAND\n");
rc = power_off(slot_cur);
@@ -623,13 +581,13 @@ static int validate(struct slot *slot_cur, int opn)
switch (opn) {
case ENABLE:
- if (!(SLOT_PWRGD(slot_cur->status)) &&
- (SLOT_PRESENT(slot_cur->status)) &&
+ if (!(SLOT_PWRGD(slot_cur->status)) &&
+ (SLOT_PRESENT(slot_cur->status)) &&
!(SLOT_LATCH(slot_cur->status)))
return 0;
break;
case DISABLE:
- if ((SLOT_PWRGD(slot_cur->status)) &&
+ if ((SLOT_PWRGD(slot_cur->status)) &&
(SLOT_PRESENT(slot_cur->status)) &&
!(SLOT_LATCH(slot_cur->status)))
return 0;
@@ -649,6 +607,7 @@ static int validate(struct slot *slot_cur, int opn)
int ibmphp_update_slot_info(struct slot *slot_cur)
{
struct hotplug_slot_info *info;
+ struct pci_bus *bus = slot_cur->hotplug_slot->pci_slot->bus;
int rc;
u8 bus_speed;
u8 mode;
@@ -658,7 +617,7 @@ int ibmphp_update_slot_info(struct slot *slot_cur)
err("out of system memory\n");
return -ENOMEM;
}
-
+
info->power_status = SLOT_PWRGD(slot_cur->status);
info->attention_status = SLOT_ATTN(slot_cur->status,
slot_cur->ext_status);
@@ -679,7 +638,7 @@ int ibmphp_update_slot_info(struct slot *slot_cur)
case BUS_SPEED_33:
break;
case BUS_SPEED_66:
- if (mode == BUS_MODE_PCIX)
+ if (mode == BUS_MODE_PCIX)
bus_speed += 0x01;
else if (mode == BUS_MODE_PCI)
;
@@ -694,10 +653,9 @@ int ibmphp_update_slot_info(struct slot *slot_cur)
bus_speed = PCI_SPEED_UNKNOWN;
}
- info->cur_bus_speed = bus_speed;
- info->max_bus_speed = slot_cur->hotplug_slot->info->max_bus_speed;
- // To do: bus_names
-
+ bus->cur_bus_speed = bus_speed;
+ // To do: bus_names
+
rc = pci_hp_change_slot_info(slot_cur->hotplug_slot, info);
kfree(info);
return rc;
@@ -713,7 +671,7 @@ static struct pci_func *ibm_slot_find(u8 busno, u8 device, u8 function)
{
struct pci_func *func_cur;
struct slot *slot_cur;
- struct list_head * tmp;
+ struct list_head *tmp;
list_for_each(tmp, &ibmphp_slot_head) {
slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
if (slot_cur->func) {
@@ -738,16 +696,16 @@ static struct pci_func *ibm_slot_find(u8 busno, u8 device, u8 function)
static void free_slots(void)
{
struct slot *slot_cur;
- struct list_head * tmp;
- struct list_head * next;
+ struct list_head *tmp;
+ struct list_head *next;
- debug("%s -- enter\n", __FUNCTION__);
+ debug("%s -- enter\n", __func__);
list_for_each_safe(tmp, next, &ibmphp_slot_head) {
slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
pci_hp_deregister(slot_cur->hotplug_slot);
}
- debug("%s -- exit\n", __FUNCTION__);
+ debug("%s -- exit\n", __func__);
}
static void ibm_unconfigure_device(struct pci_func *func)
@@ -755,24 +713,29 @@ static void ibm_unconfigure_device(struct pci_func *func)
struct pci_dev *temp;
u8 j;
- debug("inside %s\n", __FUNCTION__);
+ debug("inside %s\n", __func__);
debug("func->device = %x, func->function = %x\n",
func->device, func->function);
debug("func->device << 3 | 0x0 = %x\n", func->device << 3 | 0x0);
+ pci_lock_rescan_remove();
+
for (j = 0; j < 0x08; j++) {
temp = pci_get_bus_and_slot(func->busno, (func->device << 3) | j);
if (temp) {
- pci_remove_bus_device(temp);
+ pci_stop_and_remove_bus_device(temp);
pci_dev_put(temp);
}
}
+
pci_dev_put(func->dev);
+
+ pci_unlock_rescan_remove();
}
/*
- * The following function is to fix kernel bug regarding
- * getting bus entries, here we manually add those primary
+ * The following function is to fix kernel bug regarding
+ * getting bus entries, here we manually add those primary
* bus entries to kernel bus structure whenever apply
*/
static u8 bus_structure_fixup(u8 busno)
@@ -786,13 +749,13 @@ static u8 bus_structure_fixup(u8 busno)
bus = kmalloc(sizeof(*bus), GFP_KERNEL);
if (!bus) {
- err("%s - out of memory\n", __FUNCTION__);
+ err("%s - out of memory\n", __func__);
return 1;
}
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
kfree(bus);
- err("%s - out of memory\n", __FUNCTION__);
+ err("%s - out of memory\n", __func__);
return 1;
}
@@ -802,8 +765,8 @@ static u8 bus_structure_fixup(u8 busno)
for (dev->devfn = 0; dev->devfn < 256; dev->devfn += 8) {
if (!pci_read_config_word(dev, PCI_VENDOR_ID, &l) &&
(l != 0x0000) && (l != 0xffff)) {
- debug("%s - Inside bus_struture_fixup()\n",
- __FUNCTION__);
+ debug("%s - Inside bus_structure_fixup()\n",
+ __func__);
pci_scan_bus(busno, ibmphp_pci_bus->ops, NULL);
break;
}
@@ -817,12 +780,13 @@ static u8 bus_structure_fixup(u8 busno)
static int ibm_configure_device(struct pci_func *func)
{
- unsigned char bus;
struct pci_bus *child;
int num;
int flag = 0; /* this is to make sure we don't double scan the bus,
for bridged devices primarily */
+ pci_lock_rescan_remove();
+
if (!(bus_structure_fixup(func->busno)))
flag = 1;
if (func->dev == NULL)
@@ -832,7 +796,7 @@ static int ibm_configure_device(struct pci_func *func)
if (func->dev == NULL) {
struct pci_bus *bus = pci_find_bus(0, func->busno);
if (!bus)
- return 0;
+ goto out;
num = pci_scan_slot(bus,
PCI_DEVFN(func->device, func->function));
@@ -843,25 +807,28 @@ static int ibm_configure_device(struct pci_func *func)
PCI_DEVFN(func->device, func->function));
if (func->dev == NULL) {
err("ERROR... : pci_dev still NULL\n");
- return 0;
+ goto out;
}
}
if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) {
- pci_read_config_byte(func->dev, PCI_SECONDARY_BUS, &bus);
- child = pci_add_new_bus(func->dev->bus, func->dev, bus);
- pci_do_scan_bus(child);
+ pci_hp_add_bridge(func->dev);
+ child = func->dev->subordinate;
+ if (child)
+ pci_bus_add_devices(child);
}
+ out:
+ pci_unlock_rescan_remove();
return 0;
}
/*******************************************************
- * Returns whether the bus is empty or not
+ * Returns whether the bus is empty or not
*******************************************************/
-static int is_bus_empty(struct slot * slot_cur)
+static int is_bus_empty(struct slot *slot_cur)
{
int rc;
- struct slot * tmp_slot;
+ struct slot *tmp_slot;
u8 i = slot_cur->bus_on->slot_min;
while (i <= slot_cur->bus_on->slot_max) {
@@ -884,12 +851,12 @@ static int is_bus_empty(struct slot * slot_cur)
}
/***********************************************************
- * If the HPC permits and the bus currently empty, tries to set the
+ * If the HPC permits and the bus currently empty, tries to set the
* bus speed and mode at the maximum card and bus capability
* Parameters: slot
* Returns: bus is set (0) or error code
***********************************************************/
-static int set_bus(struct slot * slot_cur)
+static int set_bus(struct slot *slot_cur)
{
int rc;
u8 speed;
@@ -898,9 +865,9 @@ static int set_bus(struct slot * slot_cur)
static struct pci_device_id ciobx[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 0x0101) },
{ },
- };
+ };
- debug("%s - entry slot # %d\n", __FUNCTION__, slot_cur->number);
+ debug("%s - entry slot # %d\n", __func__, slot_cur->number);
if (SET_BUS_STATUS(slot_cur->ctrl) && is_bus_empty(slot_cur)) {
rc = slot_update(&slot_cur);
if (rc)
@@ -919,7 +886,7 @@ static int set_bus(struct slot * slot_cur)
else if (!SLOT_BUS_MODE(slot_cur->ext_status))
/* if max slot/bus capability is 66 pci
and there's no bus mode mismatch, then
- the adapter supports 66 pci */
+ the adapter supports 66 pci */
cmd = HPC_BUS_66CONVMODE;
else
cmd = HPC_BUS_33CONVMODE;
@@ -972,24 +939,24 @@ static int set_bus(struct slot * slot_cur)
return -EIO;
}
}
- /* This is for x440, once Brandon fixes the firmware,
+ /* This is for x440, once Brandon fixes the firmware,
will not need this delay */
msleep(1000);
- debug("%s -Exit\n", __FUNCTION__);
+ debug("%s -Exit\n", __func__);
return 0;
}
/* This routine checks the bus limitations that the slot is on from the BIOS.
- * This is used in deciding whether or not to power up the slot.
+ * This is used in deciding whether or not to power up the slot.
* (electrical/spec limitations. For example, >1 133 MHz or >2 66 PCI cards on
- * same bus)
+ * same bus)
* Parameters: slot
* Returns: 0 = no limitations, -EINVAL = exceeded limitations on the bus
*/
static int check_limitations(struct slot *slot_cur)
{
u8 i;
- struct slot * tmp_slot;
+ struct slot *tmp_slot;
u8 count = 0;
u8 limitation = 0;
@@ -1028,7 +995,7 @@ static int check_limitations(struct slot *slot_cur)
static inline void print_card_capability(struct slot *slot_cur)
{
info("capability of the card is ");
- if ((slot_cur->ext_status & CARD_INFO) == PCIX133)
+ if ((slot_cur->ext_status & CARD_INFO) == PCIX133)
info(" 133 MHz PCI-X\n");
else if ((slot_cur->ext_status & CARD_INFO) == PCIX66)
info(" 66 MHz PCI-X\n");
@@ -1062,7 +1029,7 @@ static int enable_slot(struct hotplug_slot *hs)
}
attn_LED_blink(slot_cur);
-
+
rc = set_bus(slot_cur);
if (rc) {
err("was not able to set the bus\n");
@@ -1078,8 +1045,7 @@ static int enable_slot(struct hotplug_slot *hs)
rc = check_limitations(slot_cur);
if (rc) {
err("Adding this card exceeds the limitations of this bus.\n");
- err("(i.e., >1 133MHz cards running on same bus, or "
- ">2 66 PCI cards running on same bus.\n");
+ err("(i.e., >1 133MHz cards running on same bus, or >2 66 PCI cards running on same bus.\n");
err("Try hot-adding into another bus\n");
rc = -EINVAL;
goto error_nopower;
@@ -1103,12 +1069,10 @@ static int enable_slot(struct hotplug_slot *hs)
!(SLOT_PWRGD(slot_cur->status)))
err("power fault occurred trying to power up\n");
else if (SLOT_BUS_SPEED(slot_cur->status)) {
- err("bus speed mismatch occurred. please check "
- "current bus speed and card capability\n");
+ err("bus speed mismatch occurred. please check current bus speed and card capability\n");
print_card_capability(slot_cur);
} else if (SLOT_BUS_MODE(slot_cur->ext_status)) {
- err("bus mode mismatch occurred. please check "
- "current bus mode and card capability\n");
+ err("bus mode mismatch occurred. please check current bus mode and card capability\n");
print_card_capability(slot_cur);
}
ibmphp_update_slot_info(slot_cur);
@@ -1124,18 +1088,17 @@ static int enable_slot(struct hotplug_slot *hs)
rc = slot_update(&slot_cur);
if (rc)
goto error_power;
-
+
rc = -EINVAL;
if (SLOT_POWER(slot_cur->status) && !(SLOT_PWRGD(slot_cur->status))) {
err("power fault occurred trying to power up...\n");
goto error_power;
}
if (SLOT_POWER(slot_cur->status) && (SLOT_BUS_SPEED(slot_cur->status))) {
- err("bus speed mismatch occurred. please check current bus "
- "speed and card capability\n");
+ err("bus speed mismatch occurred. please check current bus speed and card capability\n");
print_card_capability(slot_cur);
goto error_power;
- }
+ }
/* Don't think this case will happen after above checks...
* but just in case, for paranoia sake */
if (!(SLOT_POWER(slot_cur->status))) {
@@ -1186,7 +1149,7 @@ static int enable_slot(struct hotplug_slot *hs)
ibmphp_print_test();
rc = ibmphp_update_slot_info(slot_cur);
exit:
- ibmphp_unlock_operations();
+ ibmphp_unlock_operations();
return rc;
error_nopower:
@@ -1222,7 +1185,7 @@ static int ibmphp_disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
int rc;
-
+
ibmphp_lock_operations();
rc = ibmphp_do_disable_slot(slot);
ibmphp_unlock_operations();
@@ -1234,12 +1197,12 @@ int ibmphp_do_disable_slot(struct slot *slot_cur)
int rc;
u8 flag;
- debug("DISABLING SLOT...\n");
-
+ debug("DISABLING SLOT...\n");
+
if ((slot_cur == NULL) || (slot_cur->ctrl == NULL)) {
return -ENODEV;
}
-
+
flag = slot_cur->flag;
slot_cur->flag = 1;
@@ -1252,7 +1215,7 @@ int ibmphp_do_disable_slot(struct slot *slot_cur)
attn_LED_blink(slot_cur);
if (slot_cur->func == NULL) {
- /* We need this for fncs's that were there on bootup */
+ /* We need this for functions that were there on bootup */
slot_cur->func = kzalloc(sizeof(struct pci_func), GFP_KERNEL);
if (!slot_cur->func) {
err("out of system memory\n");
@@ -1264,12 +1227,13 @@ int ibmphp_do_disable_slot(struct slot *slot_cur)
}
ibm_unconfigure_device(slot_cur->func);
-
- /* If we got here from latch suddenly opening on operating card or
- a power fault, there's no power to the card, so cannot
- read from it to determine what resources it occupied. This operation
- is forbidden anyhow. The best we can do is remove it from kernel
- lists at least */
+
+ /*
+ * If we got here from latch suddenly opening on operating card or
+ * a power fault, there's no power to the card, so cannot
+ * read from it to determine what resources it occupied. This operation
+ * is forbidden anyhow. The best we can do is remove it from kernel
+ * lists at least */
if (!flag) {
attn_off(slot_cur);
@@ -1306,13 +1270,12 @@ error:
rc = -EFAULT;
goto exit;
}
- if (flag)
+ if (flag)
ibmphp_update_slot_info(slot_cur);
goto exit;
}
struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
- .owner = THIS_MODULE,
.set_attention_status = set_attention_status,
.enable_slot = enable_slot,
.disable_slot = ibmphp_disable_slot,
@@ -1321,8 +1284,6 @@ struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_present,
- .get_max_bus_speed = get_max_bus_speed,
- .get_cur_bus_speed = get_cur_bus_speed,
/* .get_max_adapter_speed = get_max_adapter_speed,
.get_bus_name_status = get_bus_name,
*/
@@ -1384,7 +1345,7 @@ static int __init ibmphp_init(void)
debug("AFTER Resource & EBDA INITIALIZATIONS\n");
max_slots = get_max_slots();
-
+
if ((rc = ibmphp_register_pci()))
goto error;
@@ -1398,10 +1359,6 @@ static int __init ibmphp_init(void)
goto error;
}
- /* lock ourselves into memory with a module
- * count of -1 so that no one can unload us. */
- module_put(THIS_MODULE);
-
exit:
return rc;
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c
index bbccde9f228..0f65ac55543 100644
--- a/drivers/pci/hotplug/ibmphp_ebda.c
+++ b/drivers/pci/hotplug/ibmphp_ebda.c
@@ -123,22 +123,20 @@ static struct ebda_pci_rsrc *alloc_ebda_pci_rsrc (void)
static void __init print_bus_info (void)
{
struct bus_info *ptr;
- struct list_head *ptr1;
-
- list_for_each (ptr1, &bus_info_head) {
- ptr = list_entry (ptr1, struct bus_info, bus_info_list);
- debug ("%s - slot_min = %x\n", __FUNCTION__, ptr->slot_min);
- debug ("%s - slot_max = %x\n", __FUNCTION__, ptr->slot_max);
- debug ("%s - slot_count = %x\n", __FUNCTION__, ptr->slot_count);
- debug ("%s - bus# = %x\n", __FUNCTION__, ptr->busno);
- debug ("%s - current_speed = %x\n", __FUNCTION__, ptr->current_speed);
- debug ("%s - controller_id = %x\n", __FUNCTION__, ptr->controller_id);
-
- debug ("%s - slots_at_33_conv = %x\n", __FUNCTION__, ptr->slots_at_33_conv);
- debug ("%s - slots_at_66_conv = %x\n", __FUNCTION__, ptr->slots_at_66_conv);
- debug ("%s - slots_at_66_pcix = %x\n", __FUNCTION__, ptr->slots_at_66_pcix);
- debug ("%s - slots_at_100_pcix = %x\n", __FUNCTION__, ptr->slots_at_100_pcix);
- debug ("%s - slots_at_133_pcix = %x\n", __FUNCTION__, ptr->slots_at_133_pcix);
+
+ list_for_each_entry(ptr, &bus_info_head, bus_info_list) {
+ debug ("%s - slot_min = %x\n", __func__, ptr->slot_min);
+ debug ("%s - slot_max = %x\n", __func__, ptr->slot_max);
+ debug ("%s - slot_count = %x\n", __func__, ptr->slot_count);
+ debug ("%s - bus# = %x\n", __func__, ptr->busno);
+ debug ("%s - current_speed = %x\n", __func__, ptr->current_speed);
+ debug ("%s - controller_id = %x\n", __func__, ptr->controller_id);
+
+ debug ("%s - slots_at_33_conv = %x\n", __func__, ptr->slots_at_33_conv);
+ debug ("%s - slots_at_66_conv = %x\n", __func__, ptr->slots_at_66_conv);
+ debug ("%s - slots_at_66_pcix = %x\n", __func__, ptr->slots_at_66_pcix);
+ debug ("%s - slots_at_100_pcix = %x\n", __func__, ptr->slots_at_100_pcix);
+ debug ("%s - slots_at_133_pcix = %x\n", __func__, ptr->slots_at_133_pcix);
}
}
@@ -146,16 +144,14 @@ static void __init print_bus_info (void)
static void print_lo_info (void)
{
struct rio_detail *ptr;
- struct list_head *ptr1;
- debug ("print_lo_info ----\n");
- list_for_each (ptr1, &rio_lo_head) {
- ptr = list_entry (ptr1, struct rio_detail, rio_detail_list);
- debug ("%s - rio_node_id = %x\n", __FUNCTION__, ptr->rio_node_id);
- debug ("%s - rio_type = %x\n", __FUNCTION__, ptr->rio_type);
- debug ("%s - owner_id = %x\n", __FUNCTION__, ptr->owner_id);
- debug ("%s - first_slot_num = %x\n", __FUNCTION__, ptr->first_slot_num);
- debug ("%s - wpindex = %x\n", __FUNCTION__, ptr->wpindex);
- debug ("%s - chassis_num = %x\n", __FUNCTION__, ptr->chassis_num);
+ debug ("print_lo_info ----\n");
+ list_for_each_entry(ptr, &rio_lo_head, rio_detail_list) {
+ debug ("%s - rio_node_id = %x\n", __func__, ptr->rio_node_id);
+ debug ("%s - rio_type = %x\n", __func__, ptr->rio_type);
+ debug ("%s - owner_id = %x\n", __func__, ptr->owner_id);
+ debug ("%s - first_slot_num = %x\n", __func__, ptr->first_slot_num);
+ debug ("%s - wpindex = %x\n", __func__, ptr->wpindex);
+ debug ("%s - chassis_num = %x\n", __func__, ptr->chassis_num);
}
}
@@ -163,16 +159,14 @@ static void print_lo_info (void)
static void print_vg_info (void)
{
struct rio_detail *ptr;
- struct list_head *ptr1;
- debug ("%s ---\n", __FUNCTION__);
- list_for_each (ptr1, &rio_vg_head) {
- ptr = list_entry (ptr1, struct rio_detail, rio_detail_list);
- debug ("%s - rio_node_id = %x\n", __FUNCTION__, ptr->rio_node_id);
- debug ("%s - rio_type = %x\n", __FUNCTION__, ptr->rio_type);
- debug ("%s - owner_id = %x\n", __FUNCTION__, ptr->owner_id);
- debug ("%s - first_slot_num = %x\n", __FUNCTION__, ptr->first_slot_num);
- debug ("%s - wpindex = %x\n", __FUNCTION__, ptr->wpindex);
- debug ("%s - chassis_num = %x\n", __FUNCTION__, ptr->chassis_num);
+ debug ("%s ---\n", __func__);
+ list_for_each_entry(ptr, &rio_vg_head, rio_detail_list) {
+ debug ("%s - rio_node_id = %x\n", __func__, ptr->rio_node_id);
+ debug ("%s - rio_type = %x\n", __func__, ptr->rio_type);
+ debug ("%s - owner_id = %x\n", __func__, ptr->owner_id);
+ debug ("%s - first_slot_num = %x\n", __func__, ptr->first_slot_num);
+ debug ("%s - wpindex = %x\n", __func__, ptr->wpindex);
+ debug ("%s - chassis_num = %x\n", __func__, ptr->chassis_num);
}
}
@@ -180,80 +174,70 @@ static void print_vg_info (void)
static void __init print_ebda_pci_rsrc (void)
{
struct ebda_pci_rsrc *ptr;
- struct list_head *ptr1;
- list_for_each (ptr1, &ibmphp_ebda_pci_rsrc_head) {
- ptr = list_entry (ptr1, struct ebda_pci_rsrc, ebda_pci_rsrc_list);
- debug ("%s - rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n",
- __FUNCTION__, ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr);
+ list_for_each_entry(ptr, &ibmphp_ebda_pci_rsrc_head, ebda_pci_rsrc_list) {
+ debug ("%s - rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n",
+ __func__, ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr);
}
}
static void __init print_ibm_slot (void)
{
struct slot *ptr;
- struct list_head *ptr1;
- list_for_each (ptr1, &ibmphp_slot_head) {
- ptr = list_entry (ptr1, struct slot, ibm_slot_list);
- debug ("%s - slot_number: %x\n", __FUNCTION__, ptr->number);
+ list_for_each_entry(ptr, &ibmphp_slot_head, ibm_slot_list) {
+ debug ("%s - slot_number: %x\n", __func__, ptr->number);
}
}
static void __init print_opt_vg (void)
{
struct opt_rio *ptr;
- struct list_head *ptr1;
- debug ("%s ---\n", __FUNCTION__);
- list_for_each (ptr1, &opt_vg_head) {
- ptr = list_entry (ptr1, struct opt_rio, opt_rio_list);
- debug ("%s - rio_type %x\n", __FUNCTION__, ptr->rio_type);
- debug ("%s - chassis_num: %x\n", __FUNCTION__, ptr->chassis_num);
- debug ("%s - first_slot_num: %x\n", __FUNCTION__, ptr->first_slot_num);
- debug ("%s - middle_num: %x\n", __FUNCTION__, ptr->middle_num);
+ debug ("%s ---\n", __func__);
+ list_for_each_entry(ptr, &opt_vg_head, opt_rio_list) {
+ debug ("%s - rio_type %x\n", __func__, ptr->rio_type);
+ debug ("%s - chassis_num: %x\n", __func__, ptr->chassis_num);
+ debug ("%s - first_slot_num: %x\n", __func__, ptr->first_slot_num);
+ debug ("%s - middle_num: %x\n", __func__, ptr->middle_num);
}
}
static void __init print_ebda_hpc (void)
{
struct controller *hpc_ptr;
- struct list_head *ptr1;
u16 index;
- list_for_each (ptr1, &ebda_hpc_head) {
-
- hpc_ptr = list_entry (ptr1, struct controller, ebda_hpc_list);
-
+ list_for_each_entry(hpc_ptr, &ebda_hpc_head, ebda_hpc_list) {
for (index = 0; index < hpc_ptr->slot_count; index++) {
- debug ("%s - physical slot#: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_num);
- debug ("%s - pci bus# of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_bus_num);
- debug ("%s - index into ctlr addr: %x\n", __FUNCTION__, hpc_ptr->slots[index].ctl_index);
- debug ("%s - cap of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_cap);
+ debug ("%s - physical slot#: %x\n", __func__, hpc_ptr->slots[index].slot_num);
+ debug ("%s - pci bus# of the slot: %x\n", __func__, hpc_ptr->slots[index].slot_bus_num);
+ debug ("%s - index into ctlr addr: %x\n", __func__, hpc_ptr->slots[index].ctl_index);
+ debug ("%s - cap of the slot: %x\n", __func__, hpc_ptr->slots[index].slot_cap);
}
for (index = 0; index < hpc_ptr->bus_count; index++) {
- debug ("%s - bus# of each bus controlled by this ctlr: %x\n", __FUNCTION__, hpc_ptr->buses[index].bus_num);
+ debug ("%s - bus# of each bus controlled by this ctlr: %x\n", __func__, hpc_ptr->buses[index].bus_num);
}
- debug ("%s - type of hpc: %x\n", __FUNCTION__, hpc_ptr->ctlr_type);
+ debug ("%s - type of hpc: %x\n", __func__, hpc_ptr->ctlr_type);
switch (hpc_ptr->ctlr_type) {
case 1:
- debug ("%s - bus: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.bus);
- debug ("%s - dev_fun: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.dev_fun);
- debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq);
+ debug ("%s - bus: %x\n", __func__, hpc_ptr->u.pci_ctlr.bus);
+ debug ("%s - dev_fun: %x\n", __func__, hpc_ptr->u.pci_ctlr.dev_fun);
+ debug ("%s - irq: %x\n", __func__, hpc_ptr->irq);
break;
case 0:
- debug ("%s - io_start: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_start);
- debug ("%s - io_end: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_end);
- debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq);
+ debug ("%s - io_start: %x\n", __func__, hpc_ptr->u.isa_ctlr.io_start);
+ debug ("%s - io_end: %x\n", __func__, hpc_ptr->u.isa_ctlr.io_end);
+ debug ("%s - irq: %x\n", __func__, hpc_ptr->irq);
break;
case 2:
case 4:
- debug ("%s - wpegbbar: %lx\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.wpegbbar);
- debug ("%s - i2c_addr: %x\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.i2c_addr);
- debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq);
+ debug ("%s - wpegbbar: %lx\n", __func__, hpc_ptr->u.wpeg_ctlr.wpegbbar);
+ debug ("%s - i2c_addr: %x\n", __func__, hpc_ptr->u.wpeg_ctlr.i2c_addr);
+ debug ("%s - irq: %x\n", __func__, hpc_ptr->irq);
break;
}
}
@@ -261,7 +245,7 @@ static void __init print_ebda_hpc (void)
int __init ibmphp_access_ebda (void)
{
- u8 format, num_ctlrs, rio_complete, hs_complete;
+ u8 format, num_ctlrs, rio_complete, hs_complete, ebda_sz;
u16 ebda_seg, num_entries, next_offset, offset, blk_id, sub_addr, re, rc_id, re_id, base;
int rc = 0;
@@ -275,14 +259,29 @@ int __init ibmphp_access_ebda (void)
ebda_seg = readw (io_mem);
iounmap (io_mem);
debug ("returned ebda segment: %x\n", ebda_seg);
-
- io_mem = ioremap (ebda_seg<<4, 65000);
+
+ io_mem = ioremap(ebda_seg<<4, 1);
+ if (!io_mem)
+ return -ENOMEM;
+ ebda_sz = readb(io_mem);
+ iounmap(io_mem);
+ debug("ebda size: %d(KiB)\n", ebda_sz);
+ if (ebda_sz == 0)
+ return -ENOMEM;
+
+ io_mem = ioremap(ebda_seg<<4, (ebda_sz * 1024));
if (!io_mem )
return -ENOMEM;
next_offset = 0x180;
for (;;) {
offset = next_offset;
+
+ /* Make sure what we read is still in the mapped section */
+ if (WARN(offset > (ebda_sz * 1024 - 4),
+ "ibmphp_ebda: next read is beyond ebda_sz\n"))
+ break;
+
next_offset = readw (io_mem + offset); /* offset of next blk */
offset += 2;
@@ -311,7 +310,7 @@ int __init ibmphp_access_ebda (void)
re = readw (io_mem + sub_addr); /* next sub blk */
sub_addr += 2;
- rc_id = readw (io_mem + sub_addr); /* sub blk id */
+ rc_id = readw (io_mem + sub_addr); /* sub blk id */
sub_addr += 2;
if (rc_id != 0x5243)
@@ -331,7 +330,7 @@ int __init ibmphp_access_ebda (void)
debug ("info about hpc descriptor---\n");
debug ("hot blk format: %x\n", format);
debug ("num of controller: %x\n", num_ctlrs);
- debug ("offset of hpc data structure enteries: %x\n ", sub_addr);
+ debug ("offset of hpc data structure entries: %x\n ", sub_addr);
sub_addr = base + re; /* re sub blk */
/* FIXME: rc is never used/checked */
@@ -360,7 +359,7 @@ int __init ibmphp_access_ebda (void)
debug ("info about rsrc descriptor---\n");
debug ("format: %x\n", format);
debug ("num of rsrc: %x\n", num_entries);
- debug ("offset of rsrc data structure enteries: %x\n ", sub_addr);
+ debug ("offset of rsrc data structure entries: %x\n ", sub_addr);
hs_complete = 1;
} else {
@@ -369,13 +368,15 @@ int __init ibmphp_access_ebda (void)
debug ("rio blk id: %x\n", blk_id);
rio_table_ptr = kzalloc(sizeof(struct rio_table_hdr), GFP_KERNEL);
- if (!rio_table_ptr)
- return -ENOMEM;
+ if (!rio_table_ptr) {
+ rc = -ENOMEM;
+ goto out;
+ }
rio_table_ptr->ver_num = readb (io_mem + offset);
rio_table_ptr->scal_count = readb (io_mem + offset + 1);
rio_table_ptr->riodev_count = readb (io_mem + offset + 2);
rio_table_ptr->offset = offset +3 ;
-
+
debug("info about rio table hdr ---\n");
debug("ver_num: %x\nscal_count: %x\nriodev_count: %x\noffset of rio table: %x\n ",
rio_table_ptr->ver_num, rio_table_ptr->scal_count,
@@ -439,12 +440,12 @@ static int __init ebda_rio_table (void)
rio_detail_ptr->chassis_num = readb (io_mem + offset + 14);
// debug ("rio_node_id: %x\nbbar: %x\nrio_type: %x\nowner_id: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nfirst_slot_num: %x\nstatus: %x\n", rio_detail_ptr->rio_node_id, rio_detail_ptr->bbar, rio_detail_ptr->rio_type, rio_detail_ptr->owner_id, rio_detail_ptr->port0_node_connect, rio_detail_ptr->port0_port_connect, rio_detail_ptr->port1_node_connect, rio_detail_ptr->port1_port_connect, rio_detail_ptr->first_slot_num, rio_detail_ptr->status);
//create linked list of chassis
- if (rio_detail_ptr->rio_type == 4 || rio_detail_ptr->rio_type == 5)
+ if (rio_detail_ptr->rio_type == 4 || rio_detail_ptr->rio_type == 5)
list_add (&rio_detail_ptr->rio_detail_list, &rio_vg_head);
- //create linked list of expansion box
- else if (rio_detail_ptr->rio_type == 6 || rio_detail_ptr->rio_type == 7)
+ //create linked list of expansion box
+ else if (rio_detail_ptr->rio_type == 6 || rio_detail_ptr->rio_type == 7)
list_add (&rio_detail_ptr->rio_detail_list, &rio_lo_head);
- else
+ else
// not in my concern
kfree (rio_detail_ptr);
offset += 15;
@@ -455,17 +456,15 @@ static int __init ebda_rio_table (void)
}
/*
- * reorganizing linked list of chassis
+ * reorganizing linked list of chassis
*/
static struct opt_rio *search_opt_vg (u8 chassis_num)
{
struct opt_rio *ptr;
- struct list_head *ptr1;
- list_for_each (ptr1, &opt_vg_head) {
- ptr = list_entry (ptr1, struct opt_rio, opt_rio_list);
+ list_for_each_entry(ptr, &opt_vg_head, opt_rio_list) {
if (ptr->chassis_num == chassis_num)
return ptr;
- }
+ }
return NULL;
}
@@ -473,10 +472,8 @@ static int __init combine_wpg_for_chassis (void)
{
struct opt_rio *opt_rio_ptr = NULL;
struct rio_detail *rio_detail_ptr = NULL;
- struct list_head *list_head_ptr = NULL;
-
- list_for_each (list_head_ptr, &rio_vg_head) {
- rio_detail_ptr = list_entry (list_head_ptr, struct rio_detail, rio_detail_list);
+
+ list_for_each_entry(rio_detail_ptr, &rio_vg_head, rio_detail_list) {
opt_rio_ptr = search_opt_vg (rio_detail_ptr->chassis_num);
if (!opt_rio_ptr) {
opt_rio_ptr = kzalloc(sizeof(struct opt_rio), GFP_KERNEL);
@@ -487,27 +484,25 @@ static int __init combine_wpg_for_chassis (void)
opt_rio_ptr->first_slot_num = rio_detail_ptr->first_slot_num;
opt_rio_ptr->middle_num = rio_detail_ptr->first_slot_num;
list_add (&opt_rio_ptr->opt_rio_list, &opt_vg_head);
- } else {
+ } else {
opt_rio_ptr->first_slot_num = min (opt_rio_ptr->first_slot_num, rio_detail_ptr->first_slot_num);
opt_rio_ptr->middle_num = max (opt_rio_ptr->middle_num, rio_detail_ptr->first_slot_num);
- }
+ }
}
print_opt_vg ();
- return 0;
-}
+ return 0;
+}
/*
- * reorgnizing linked list of expansion box
+ * reorganizing linked list of expansion box
*/
static struct opt_rio_lo *search_opt_lo (u8 chassis_num)
{
struct opt_rio_lo *ptr;
- struct list_head *ptr1;
- list_for_each (ptr1, &opt_lo_head) {
- ptr = list_entry (ptr1, struct opt_rio_lo, opt_rio_lo_list);
+ list_for_each_entry(ptr, &opt_lo_head, opt_rio_lo_list) {
if (ptr->chassis_num == chassis_num)
return ptr;
- }
+ }
return NULL;
}
@@ -515,10 +510,8 @@ static int combine_wpg_for_expansion (void)
{
struct opt_rio_lo *opt_rio_lo_ptr = NULL;
struct rio_detail *rio_detail_ptr = NULL;
- struct list_head *list_head_ptr = NULL;
-
- list_for_each (list_head_ptr, &rio_lo_head) {
- rio_detail_ptr = list_entry (list_head_ptr, struct rio_detail, rio_detail_list);
+
+ list_for_each_entry(rio_detail_ptr, &rio_lo_head, rio_detail_list) {
opt_rio_lo_ptr = search_opt_lo (rio_detail_ptr->chassis_num);
if (!opt_rio_lo_ptr) {
opt_rio_lo_ptr = kzalloc(sizeof(struct opt_rio_lo), GFP_KERNEL);
@@ -529,41 +522,38 @@ static int combine_wpg_for_expansion (void)
opt_rio_lo_ptr->first_slot_num = rio_detail_ptr->first_slot_num;
opt_rio_lo_ptr->middle_num = rio_detail_ptr->first_slot_num;
opt_rio_lo_ptr->pack_count = 1;
-
+
list_add (&opt_rio_lo_ptr->opt_rio_lo_list, &opt_lo_head);
- } else {
+ } else {
opt_rio_lo_ptr->first_slot_num = min (opt_rio_lo_ptr->first_slot_num, rio_detail_ptr->first_slot_num);
opt_rio_lo_ptr->middle_num = max (opt_rio_lo_ptr->middle_num, rio_detail_ptr->first_slot_num);
opt_rio_lo_ptr->pack_count = 2;
- }
+ }
}
- return 0;
+ return 0;
}
-
+
/* Since we don't know the max slot number per each chassis, hence go
* through the list of all chassis to find out the range
- * Arguments: slot_num, 1st slot number of the chassis we think we are on,
- * var (0 = chassis, 1 = expansion box)
+ * Arguments: slot_num, 1st slot number of the chassis we think we are on,
+ * var (0 = chassis, 1 = expansion box)
*/
static int first_slot_num (u8 slot_num, u8 first_slot, u8 var)
{
struct opt_rio *opt_vg_ptr = NULL;
struct opt_rio_lo *opt_lo_ptr = NULL;
- struct list_head *ptr = NULL;
int rc = 0;
if (!var) {
- list_for_each (ptr, &opt_vg_head) {
- opt_vg_ptr = list_entry (ptr, struct opt_rio, opt_rio_list);
- if ((first_slot < opt_vg_ptr->first_slot_num) && (slot_num >= opt_vg_ptr->first_slot_num)) {
+ list_for_each_entry(opt_vg_ptr, &opt_vg_head, opt_rio_list) {
+ if ((first_slot < opt_vg_ptr->first_slot_num) && (slot_num >= opt_vg_ptr->first_slot_num)) {
rc = -ENODEV;
break;
}
}
} else {
- list_for_each (ptr, &opt_lo_head) {
- opt_lo_ptr = list_entry (ptr, struct opt_rio_lo, opt_rio_lo_list);
+ list_for_each_entry(opt_lo_ptr, &opt_lo_head, opt_rio_lo_list) {
if ((first_slot < opt_lo_ptr->first_slot_num) && (slot_num >= opt_lo_ptr->first_slot_num)) {
rc = -ENODEV;
break;
@@ -573,29 +563,25 @@ static int first_slot_num (u8 slot_num, u8 first_slot, u8 var)
return rc;
}
-static struct opt_rio_lo * find_rxe_num (u8 slot_num)
+static struct opt_rio_lo *find_rxe_num (u8 slot_num)
{
struct opt_rio_lo *opt_lo_ptr;
- struct list_head *ptr;
- list_for_each (ptr, &opt_lo_head) {
- opt_lo_ptr = list_entry (ptr, struct opt_rio_lo, opt_rio_lo_list);
+ list_for_each_entry(opt_lo_ptr, &opt_lo_head, opt_rio_lo_list) {
//check to see if this slot_num belongs to expansion box
- if ((slot_num >= opt_lo_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_lo_ptr->first_slot_num, 1)))
+ if ((slot_num >= opt_lo_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_lo_ptr->first_slot_num, 1)))
return opt_lo_ptr;
}
return NULL;
}
-static struct opt_rio * find_chassis_num (u8 slot_num)
+static struct opt_rio *find_chassis_num (u8 slot_num)
{
struct opt_rio *opt_vg_ptr;
- struct list_head *ptr;
- list_for_each (ptr, &opt_vg_head) {
- opt_vg_ptr = list_entry (ptr, struct opt_rio, opt_rio_list);
- //check to see if this slot_num belongs to chassis
- if ((slot_num >= opt_vg_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_vg_ptr->first_slot_num, 0)))
+ list_for_each_entry(opt_vg_ptr, &opt_vg_head, opt_rio_list) {
+ //check to see if this slot_num belongs to chassis
+ if ((slot_num >= opt_vg_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_vg_ptr->first_slot_num, 0)))
return opt_vg_ptr;
}
return NULL;
@@ -607,24 +593,25 @@ static struct opt_rio * find_chassis_num (u8 slot_num)
static u8 calculate_first_slot (u8 slot_num)
{
u8 first_slot = 1;
- struct list_head * list;
- struct slot * slot_cur;
-
- list_for_each (list, &ibmphp_slot_head) {
- slot_cur = list_entry (list, struct slot, ibm_slot_list);
+ struct slot *slot_cur;
+
+ list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) {
if (slot_cur->ctrl) {
- if ((slot_cur->ctrl->ctlr_type != 4) && (slot_cur->ctrl->ending_slot_num > first_slot) && (slot_num > slot_cur->ctrl->ending_slot_num))
+ if ((slot_cur->ctrl->ctlr_type != 4) && (slot_cur->ctrl->ending_slot_num > first_slot) && (slot_num > slot_cur->ctrl->ending_slot_num))
first_slot = slot_cur->ctrl->ending_slot_num;
}
- }
+ }
return first_slot + 1;
}
-static char *create_file_name (struct slot * slot_cur)
+
+#define SLOT_NAME_SIZE 30
+
+static char *create_file_name (struct slot *slot_cur)
{
struct opt_rio *opt_vg_ptr = NULL;
struct opt_rio_lo *opt_lo_ptr = NULL;
- static char str[30];
+ static char str[SLOT_NAME_SIZE];
int which = 0; /* rxe = 1, chassis = 0 */
u8 number = 1; /* either chassis or rxe # */
u8 first_slot = 1;
@@ -635,11 +622,11 @@ static char *create_file_name (struct slot * slot_cur)
err ("Structure passed is empty\n");
return NULL;
}
-
+
slot_num = slot_cur->number;
memset (str, 0, sizeof(str));
-
+
if (rio_table_ptr) {
if (rio_table_ptr->ver_num == 3) {
opt_vg_ptr = find_chassis_num (slot_num);
@@ -673,7 +660,7 @@ static char *create_file_name (struct slot * slot_cur)
/* if both NULL and we DO have correct RIO table in BIOS */
return NULL;
}
- }
+ }
if (!flag) {
if (slot_cur->ctrl->ctlr_type == 4) {
first_slot = calculate_first_slot (slot_num);
@@ -736,7 +723,6 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
slot = hotplug_slot->private;
kfree(slot->hotplug_slot->info);
- kfree(slot->hotplug_slot->name);
kfree(slot->hotplug_slot);
slot->ctrl = NULL;
slot->bus_on = NULL;
@@ -767,7 +753,7 @@ static int __init ebda_rsrc_controller (void)
struct bus_info *bus_info_ptr1, *bus_info_ptr2;
int rc;
struct slot *tmp_slot;
- struct list_head *list;
+ char name[SLOT_NAME_SIZE];
addr = hpc_list_ptr->phys_addr;
for (ctlr = 0; ctlr < hpc_list_ptr->num_ctlrs; ctlr++) {
@@ -798,7 +784,7 @@ static int __init ebda_rsrc_controller (void)
hpc_ptr->ctlr_relative_id = ctlr;
hpc_ptr->slot_count = slot_num;
hpc_ptr->bus_count = bus_num;
- debug ("now enter ctlr data struture ---\n");
+ debug ("now enter ctlr data structure ---\n");
debug ("ctlr id: %x\n", ctlr_id);
debug ("ctlr_relative_id: %x\n", hpc_ptr->ctlr_relative_id);
debug ("count of slots controlled by this ctlr: %x\n", slot_num);
@@ -812,7 +798,7 @@ static int __init ebda_rsrc_controller (void)
slot_ptr->ctl_index = readb (io_mem + addr_slot + 2*slot_num);
slot_ptr->slot_cap = readb (io_mem + addr_slot + 3*slot_num);
- // create bus_info lined list --- if only one slot per bus: slot_min = slot_max
+ // create bus_info lined list --- if only one slot per bus: slot_min = slot_max
bus_info_ptr2 = ibmphp_find_same_bus_num (slot_ptr->slot_bus_num);
if (!bus_info_ptr2) {
@@ -828,9 +814,9 @@ static int __init ebda_rsrc_controller (void)
bus_info_ptr1->index = bus_index++;
bus_info_ptr1->current_speed = 0xff;
bus_info_ptr1->current_bus_mode = 0xff;
-
+
bus_info_ptr1->controller_id = hpc_ptr->ctlr_id;
-
+
list_add_tail (&bus_info_ptr1->bus_info_list, &bus_info_head);
} else {
@@ -865,7 +851,7 @@ static int __init ebda_rsrc_controller (void)
bus_info_ptr2->slots_at_66_conv = bus_ptr->slots_at_66_conv;
bus_info_ptr2->slots_at_66_pcix = bus_ptr->slots_at_66_pcix;
bus_info_ptr2->slots_at_100_pcix = bus_ptr->slots_at_100_pcix;
- bus_info_ptr2->slots_at_133_pcix = bus_ptr->slots_at_133_pcix;
+ bus_info_ptr2->slots_at_133_pcix = bus_ptr->slots_at_133_pcix;
}
bus_ptr++;
}
@@ -878,7 +864,7 @@ static int __init ebda_rsrc_controller (void)
hpc_ptr->u.pci_ctlr.dev_fun = readb (io_mem + addr + 1);
hpc_ptr->irq = readb (io_mem + addr + 2);
addr += 3;
- debug ("ctrl bus = %x, ctlr devfun = %x, irq = %x\n",
+ debug ("ctrl bus = %x, ctlr devfun = %x, irq = %x\n",
hpc_ptr->u.pci_ctlr.bus,
hpc_ptr->u.pci_ctlr.dev_fun, hpc_ptr->irq);
break;
@@ -931,12 +917,6 @@ static int __init ebda_rsrc_controller (void)
goto error_no_hp_info;
}
- hp_slot_ptr->name = kmalloc(30, GFP_KERNEL);
- if (!hp_slot_ptr->name) {
- rc = -ENOMEM;
- goto error_no_hp_name;
- }
-
tmp_slot = kzalloc(sizeof(*tmp_slot), GFP_KERNEL);
if (!tmp_slot) {
rc = -ENOMEM;
@@ -952,7 +932,7 @@ static int __init ebda_rsrc_controller (void)
tmp_slot->supported_speed = 2;
else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_66_MAX) == EBDA_SLOT_66_MAX)
tmp_slot->supported_speed = 1;
-
+
if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_PCIX_CAP) == EBDA_SLOT_PCIX_CAP)
tmp_slot->supported_bus_mode = 1;
else
@@ -997,11 +977,10 @@ static int __init ebda_rsrc_controller (void)
} /* each hpc */
- list_for_each (list, &ibmphp_slot_head) {
- 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);
+ list_for_each_entry(tmp_slot, &ibmphp_slot_head, ibm_slot_list) {
+ snprintf(name, SLOT_NAME_SIZE, "%s", create_file_name(tmp_slot));
+ pci_hp_register(tmp_slot->hotplug_slot,
+ pci_find_bus(0, tmp_slot->bus), tmp_slot->device, name);
}
print_ebda_hpc ();
@@ -1011,8 +990,6 @@ static int __init ebda_rsrc_controller (void)
error:
kfree (hp_slot_ptr->private);
error_no_slot:
- kfree (hp_slot_ptr->name);
-error_no_hp_name:
kfree (hp_slot_ptr->info);
error_no_hp_info:
kfree (hp_slot_ptr);
@@ -1023,7 +1000,7 @@ error_no_hpc:
return rc;
}
-/*
+/*
* map info (bus, devfun, start addr, end addr..) of i/o, memory,
* pfm from the physical addr to a list of resource.
*/
@@ -1080,7 +1057,7 @@ static int __init ebda_rsrc_rsrc (void)
addr += 10;
debug ("rsrc from mem or pfm ---\n");
- debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n",
+ debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n",
rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr);
list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head);
@@ -1100,10 +1077,8 @@ u16 ibmphp_get_total_controllers (void)
struct slot *ibmphp_get_slot_from_physical_num (u8 physical_num)
{
struct slot *slot;
- struct list_head *list;
- list_for_each (list, &ibmphp_slot_head) {
- slot = list_entry (list, struct slot, ibm_slot_list);
+ list_for_each_entry(slot, &ibmphp_slot_head, ibm_slot_list) {
if (slot->number == physical_num)
return slot;
}
@@ -1119,11 +1094,9 @@ struct slot *ibmphp_get_slot_from_physical_num (u8 physical_num)
struct bus_info *ibmphp_find_same_bus_num (u32 num)
{
struct bus_info *ptr;
- struct list_head *ptr1;
- list_for_each (ptr1, &bus_info_head) {
- ptr = list_entry (ptr1, struct bus_info, bus_info_list);
- if (ptr->busno == num)
+ list_for_each_entry(ptr, &bus_info_head, bus_info_list) {
+ if (ptr->busno == num)
return ptr;
}
return NULL;
@@ -1135,11 +1108,9 @@ struct bus_info *ibmphp_find_same_bus_num (u32 num)
int ibmphp_get_bus_index (u8 num)
{
struct bus_info *ptr;
- struct list_head *ptr1;
- list_for_each (ptr1, &bus_info_head) {
- ptr = list_entry (ptr1, struct bus_info, bus_info_list);
- if (ptr->busno == num)
+ list_for_each_entry(ptr, &bus_info_head, bus_info_list) {
+ if (ptr->busno == num)
return ptr->index;
}
return -ENODEV;
@@ -1197,7 +1168,7 @@ static struct pci_device_id id_table[] = {
.subdevice = HPC_SUBSYSTEM_ID,
.class = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00),
}, {}
-};
+};
MODULE_DEVICE_TABLE(pci, id_table);
@@ -1211,11 +1182,9 @@ static struct pci_driver ibmphp_driver = {
int ibmphp_register_pci (void)
{
struct controller *ctrl;
- struct list_head *tmp;
int rc = 0;
- list_for_each (tmp, &ebda_hpc_head) {
- ctrl = list_entry (tmp, struct controller, ebda_hpc_list);
+ list_for_each_entry(ctrl, &ebda_hpc_head, ebda_hpc_list) {
if (ctrl->ctlr_type == 1) {
rc = pci_register_driver(&ibmphp_driver);
break;
@@ -1223,15 +1192,13 @@ int ibmphp_register_pci (void)
}
return rc;
}
-static int ibmphp_probe (struct pci_dev * dev, const struct pci_device_id *ids)
+static int ibmphp_probe (struct pci_dev *dev, const struct pci_device_id *ids)
{
struct controller *ctrl;
- struct list_head *tmp;
debug ("inside ibmphp_probe\n");
-
- list_for_each (tmp, &ebda_hpc_head) {
- ctrl = list_entry (tmp, struct controller, ebda_hpc_list);
+
+ list_for_each_entry(ctrl, &ebda_hpc_head, ebda_hpc_list) {
if (ctrl->ctlr_type == 1) {
if ((dev->devfn == ctrl->u.pci_ctlr.dev_fun) && (dev->bus->number == ctrl->u.pci_ctlr.bus)) {
ctrl->ctrl_dev = dev;
@@ -1243,4 +1210,3 @@ static int ibmphp_probe (struct pci_dev * dev, const struct pci_device_id *ids)
}
return -ENODEV;
}
-
diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c
index c31e7bf3450..a936022956e 100644
--- a/drivers/pci/hotplug/ibmphp_hpc.c
+++ b/drivers/pci/hotplug/ibmphp_hpc.c
@@ -35,6 +35,7 @@
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/sched.h>
+#include <linux/semaphore.h>
#include <linux/kthread.h>
#include "ibmphp.h"
@@ -129,14 +130,14 @@ static int hpc_wait_ctlr_notworking (int, struct controller *, void __iomem *, u
*---------------------------------------------------------------------*/
void __init ibmphp_hpc_initvars (void)
{
- debug ("%s - Entry\n", __FUNCTION__);
+ debug ("%s - Entry\n", __func__);
mutex_init(&sem_hpcaccess);
- init_MUTEX (&semOperations);
- init_MUTEX_LOCKED (&sem_exit);
+ sema_init(&semOperations, 1);
+ sema_init(&sem_exit, 0);
to_debug = 0;
- debug ("%s - Exit\n", __FUNCTION__);
+ debug ("%s - Exit\n", __func__);
}
/*----------------------------------------------------------------------
@@ -154,7 +155,7 @@ static u8 i2c_ctrl_read (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8
unsigned long ultemp;
unsigned long data; // actual data HILO format
- debug_polling ("%s - Entry WPGBbar[%p] index[%x] \n", __FUNCTION__, WPGBbar, index);
+ debug_polling ("%s - Entry WPGBbar[%p] index[%x] \n", __func__, WPGBbar, index);
//--------------------------------------------------------------------
// READ - step 1
@@ -213,7 +214,7 @@ static u8 i2c_ctrl_read (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8
i--;
}
if (i == 0) {
- debug ("%s - Error : WPG timeout\n", __FUNCTION__);
+ debug ("%s - Error : WPG timeout\n", __func__);
return HPC_ERROR;
}
//--------------------------------------------------------------------
@@ -241,7 +242,7 @@ static u8 i2c_ctrl_read (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8
status = (u8) data;
- debug_polling ("%s - Exit index[%x] status[%x]\n", __FUNCTION__, index, status);
+ debug_polling ("%s - Exit index[%x] status[%x]\n", __func__, index, status);
return (status);
}
@@ -257,12 +258,12 @@ static u8 i2c_ctrl_write (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8
{
u8 rc;
void __iomem *wpg_addr; // base addr + offset
- unsigned long wpg_data; // data to/from WPG LOHI format
+ unsigned long wpg_data; // data to/from WPG LOHI format
unsigned long ultemp;
unsigned long data; // actual data HILO format
int i;
- debug_polling ("%s - Entry WPGBbar[%p] index[%x] cmd[%x]\n", __FUNCTION__, WPGBbar, index, cmd);
+ debug_polling ("%s - Entry WPGBbar[%p] index[%x] cmd[%x]\n", __func__, WPGBbar, index, cmd);
rc = 0;
//--------------------------------------------------------------------
@@ -324,7 +325,7 @@ static u8 i2c_ctrl_write (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8
i--;
}
if (i == 0) {
- debug ("%s - Exit Error:WPG timeout\n", __FUNCTION__);
+ debug ("%s - Exit Error:WPG timeout\n", __func__);
rc = HPC_ERROR;
}
@@ -345,12 +346,12 @@ static u8 i2c_ctrl_write (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8
rc = HPC_ERROR;
}
- debug_polling ("%s Exit rc[%x]\n", __FUNCTION__, rc);
+ debug_polling ("%s Exit rc[%x]\n", __func__, rc);
return (rc);
}
//------------------------------------------------------------
-// Read from ISA type HPC
+// Read from ISA type HPC
//------------------------------------------------------------
static u8 isa_ctrl_read (struct controller *ctlr_ptr, u8 offset)
{
@@ -371,7 +372,7 @@ static void isa_ctrl_write (struct controller *ctlr_ptr, u8 offset, u8 data)
{
u16 start_address;
u16 port_address;
-
+
start_address = ctlr_ptr->u.isa_ctlr.io_start;
port_address = start_address + (u16) offset;
outb (data, port_address);
@@ -532,7 +533,7 @@ static u8 hpc_readcmdtoindex (u8 cmd, u8 index)
*
* Return 0 or error codes
*---------------------------------------------------------------------*/
-int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
+int ibmphp_hpc_readslot (struct slot *pslot, u8 cmd, u8 *pstatus)
{
void __iomem *wpg_bbar = NULL;
struct controller *ctlr_ptr;
@@ -541,12 +542,12 @@ int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
int rc = 0;
int busindex;
- debug_polling ("%s - Entry pslot[%p] cmd[%x] pstatus[%p]\n", __FUNCTION__, pslot, cmd, pstatus);
+ debug_polling ("%s - Entry pslot[%p] cmd[%x] pstatus[%p]\n", __func__, pslot, cmd, pstatus);
if ((pslot == NULL)
|| ((pstatus == NULL) && (cmd != READ_ALLSTAT) && (cmd != READ_BUSSTATUS))) {
rc = -EINVAL;
- err ("%s - Error invalid pointer, rc[%d]\n", __FUNCTION__, rc);
+ err ("%s - Error invalid pointer, rc[%d]\n", __func__, rc);
return rc;
}
@@ -554,7 +555,7 @@ int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
busindex = ibmphp_get_bus_index (pslot->bus);
if (busindex < 0) {
rc = -EINVAL;
- err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc);
+ err ("%s - Exit Error:invalid bus, rc[%d]\n", __func__, rc);
return rc;
} else
index = (u8) busindex;
@@ -565,7 +566,7 @@ int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
if (index == HPC_ERROR) {
rc = -EINVAL;
- err ("%s - Exit Error:invalid index, rc[%d]\n", __FUNCTION__, rc);
+ err ("%s - Exit Error:invalid index, rc[%d]\n", __func__, rc);
return rc;
}
@@ -641,7 +642,7 @@ int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
ctrl_read (ctlr_ptr, wpg_bbar,
index + WPG_1ST_EXTSLOT_INDEX);
} else {
- err ("%s - Error ctrl_read failed\n", __FUNCTION__);
+ err ("%s - Error ctrl_read failed\n", __func__);
rc = -EINVAL;
break;
}
@@ -655,14 +656,14 @@ int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
//--------------------------------------------------------------------
// cleanup
//--------------------------------------------------------------------
-
+
// remove physical to logical address mapping
if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4))
iounmap (wpg_bbar);
-
+
free_hpc_access ();
- debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc);
+ debug_polling ("%s - Exit rc[%d]\n", __func__, rc);
return rc;
}
@@ -671,7 +672,7 @@ int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
*
* Action: issue a WRITE command to HPC
*---------------------------------------------------------------------*/
-int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd)
+int ibmphp_hpc_writeslot (struct slot *pslot, u8 cmd)
{
void __iomem *wpg_bbar = NULL;
struct controller *ctlr_ptr;
@@ -681,10 +682,10 @@ int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd)
int rc = 0;
int timeout;
- debug_polling ("%s - Entry pslot[%p] cmd[%x]\n", __FUNCTION__, pslot, cmd);
+ debug_polling ("%s - Entry pslot[%p] cmd[%x]\n", __func__, pslot, cmd);
if (pslot == NULL) {
rc = -EINVAL;
- err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc);
+ err ("%s - Error Exit rc[%d]\n", __func__, rc);
return rc;
}
@@ -694,7 +695,7 @@ int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd)
busindex = ibmphp_get_bus_index (pslot->bus);
if (busindex < 0) {
rc = -EINVAL;
- err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc);
+ err ("%s - Exit Error:invalid bus, rc[%d]\n", __func__, rc);
return rc;
} else
index = (u8) busindex;
@@ -705,7 +706,7 @@ int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd)
if (index == HPC_ERROR) {
rc = -EINVAL;
- err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc);
+ err ("%s - Error Exit rc[%d]\n", __func__, rc);
return rc;
}
@@ -719,7 +720,7 @@ int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd)
if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) {
wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE);
- debug ("%s - ctlr id[%x] physical[%lx] logical[%lx] i2c[%x]\n", __FUNCTION__,
+ debug ("%s - ctlr id[%x] physical[%lx] logical[%lx] i2c[%x]\n", __func__,
ctlr_ptr->ctlr_id, (ulong) (ctlr_ptr->u.wpeg_ctlr.wpegbbar), (ulong) wpg_bbar,
ctlr_ptr->u.wpeg_ctlr.i2c_addr);
}
@@ -750,7 +751,7 @@ int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd)
msleep(1000);
if (timeout < 1) {
done = 1;
- err ("%s - Error command complete timeout\n", __FUNCTION__);
+ err ("%s - Error command complete timeout\n", __func__);
rc = -EFAULT;
} else
timeout--;
@@ -765,7 +766,7 @@ int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd)
iounmap (wpg_bbar);
free_hpc_access ();
- debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc);
+ debug_polling ("%s - Exit rc[%d]\n", __func__, rc);
return rc;
}
@@ -803,10 +804,10 @@ void ibmphp_lock_operations (void)
*---------------------------------------------------------------------*/
void ibmphp_unlock_operations (void)
{
- debug ("%s - Entry\n", __FUNCTION__);
+ debug ("%s - Entry\n", __func__);
up (&semOperations);
to_debug = 0;
- debug ("%s - Exit\n", __FUNCTION__);
+ debug ("%s - Exit\n", __func__);
}
/*----------------------------------------------------------------------
@@ -827,14 +828,14 @@ static int poll_hpc(void *data)
int poll_count = 0;
u8 ctrl_count = 0x00;
- debug ("%s - Entry\n", __FUNCTION__);
+ debug ("%s - Entry\n", __func__);
while (!kthread_should_stop()) {
/* try to get the lock to do some kind of hardware access */
down (&semOperations);
switch (poll_state) {
- case POLL_LATCH_REGISTER:
+ case POLL_LATCH_REGISTER:
oldlatchlow = curlatchlow;
ctrl_count = 0x00;
list_for_each (pslotlist, &ibmphp_slot_head) {
@@ -890,24 +891,25 @@ static int poll_hpc(void *data)
msleep(POLL_INTERVAL_SEC * 1000);
if (kthread_should_stop())
- break;
-
+ goto out_sleep;
+
down (&semOperations);
-
+
if (poll_count >= POLL_LATCH_CNT) {
poll_count = 0;
poll_state = POLL_SLOTS;
} else
poll_state = POLL_LATCH_REGISTER;
break;
- }
+ }
/* give up the hardware semaphore */
up (&semOperations);
/* sleep for a short time just for good measure */
+out_sleep:
msleep(100);
}
up (&sem_exit);
- debug ("%s - Exit\n", __FUNCTION__);
+ debug ("%s - Exit\n", __func__);
return 0;
}
@@ -956,7 +958,7 @@ static int process_changeinstatus (struct slot *pslot, struct slot *poldslot)
// bit 5 - HPC_SLOT_PWRGD
if ((pslot->status & 0x20) != (poldslot->status & 0x20))
// OFF -> ON: ignore, ON -> OFF: disable slot
- if ((poldslot->status & 0x20) && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status)))
+ if ((poldslot->status & 0x20) && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status)))
disable = 1;
// bit 6 - HPC_SLOT_BUS_SPEED
@@ -978,7 +980,7 @@ static int process_changeinstatus (struct slot *pslot, struct slot *poldslot)
pslot->status &= ~HPC_SLOT_POWER;
}
}
- // CLOSE -> OPEN
+ // CLOSE -> OPEN
else if ((SLOT_PWRGD (poldslot->status) == HPC_SLOT_PWRGD_GOOD)
&& (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status))) {
disable = 1;
@@ -999,7 +1001,7 @@ static int process_changeinstatus (struct slot *pslot, struct slot *poldslot)
ibmphp_update_slot_info (pslot);
}
- debug ("%s - Exit rc[%d] disable[%x] update[%x]\n", __FUNCTION__, rc, disable, update);
+ debug ("%s - Exit rc[%d] disable[%x] update[%x]\n", __func__, rc, disable, update);
return rc;
}
@@ -1021,7 +1023,7 @@ static int process_changeinlatch (u8 old, u8 new, struct controller *ctrl)
u8 mask;
int rc = 0;
- debug ("%s - Entry old[%x], new[%x]\n", __FUNCTION__, old, new);
+ debug ("%s - Entry old[%x], new[%x]\n", __func__, old, new);
// bit 0 reserved, 0 is LSB, check bit 1-6 for 6 slots
for (i = ctrl->starting_slot_num; i <= ctrl->ending_slot_num; i++) {
@@ -1031,15 +1033,15 @@ static int process_changeinlatch (u8 old, u8 new, struct controller *ctrl)
if (pslot) {
memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL);
- debug ("%s - call process_changeinstatus for slot[%d]\n", __FUNCTION__, i);
+ debug ("%s - call process_changeinstatus for slot[%d]\n", __func__, i);
process_changeinstatus (pslot, &myslot);
} else {
rc = -EINVAL;
- err ("%s - Error bad pointer for slot[%d]\n", __FUNCTION__, i);
+ err ("%s - Error bad pointer for slot[%d]\n", __func__, i);
}
}
}
- debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc);
+ debug ("%s - Exit rc[%d]\n", __func__, rc);
return rc;
}
@@ -1050,11 +1052,11 @@ static int process_changeinlatch (u8 old, u8 new, struct controller *ctrl)
*---------------------------------------------------------------------*/
int __init ibmphp_hpc_start_poll_thread (void)
{
- debug ("%s - Entry\n", __FUNCTION__);
+ debug ("%s - Entry\n", __func__);
ibmphp_poll_thread = kthread_run(poll_hpc, NULL, "hpc_poll");
if (IS_ERR(ibmphp_poll_thread)) {
- err ("%s - Error, thread not started\n", __FUNCTION__);
+ err ("%s - Error, thread not started\n", __func__);
return PTR_ERR(ibmphp_poll_thread);
}
return 0;
@@ -1067,13 +1069,13 @@ int __init ibmphp_hpc_start_poll_thread (void)
*---------------------------------------------------------------------*/
void __exit ibmphp_hpc_stop_poll_thread (void)
{
- debug ("%s - Entry\n", __FUNCTION__);
+ debug ("%s - Entry\n", __func__);
kthread_stop(ibmphp_poll_thread);
debug ("before locking operations \n");
ibmphp_lock_operations ();
debug ("after locking operations \n");
-
+
// wait for poll thread to exit
debug ("before sem_exit down \n");
down (&sem_exit);
@@ -1088,7 +1090,7 @@ void __exit ibmphp_hpc_stop_poll_thread (void)
up (&sem_exit);
debug ("after sem exit up\n");
- debug ("%s - Exit\n", __FUNCTION__);
+ debug ("%s - Exit\n", __func__);
}
/*----------------------------------------------------------------------
@@ -1100,7 +1102,7 @@ void __exit ibmphp_hpc_stop_poll_thread (void)
* Value:
*---------------------------------------------------------------------*/
static int hpc_wait_ctlr_notworking (int timeout, struct controller *ctlr_ptr, void __iomem *wpg_bbar,
- u8 * pstatus)
+ u8 *pstatus)
{
int rc = 0;
u8 done = 0;
diff --git a/drivers/pci/hotplug/ibmphp_pci.c b/drivers/pci/hotplug/ibmphp_pci.c
index d8f05d7a3c7..2fd296706ce 100644
--- a/drivers/pci/hotplug/ibmphp_pci.c
+++ b/drivers/pci/hotplug/ibmphp_pci.c
@@ -1,8 +1,8 @@
/*
* IBM Hot Plug Controller Driver
- *
+ *
* Written By: Irene Zubarev, IBM Corporation
- *
+ *
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001,2002 IBM Corp.
*
@@ -42,12 +42,12 @@ static u8 find_sec_number (u8 primary_busno, u8 slotno);
/*
* NOTE..... If BIOS doesn't provide default routing, we assign:
- * 9 for SCSI, 10 for LAN adapters, and 11 for everything else.
+ * 9 for SCSI, 10 for LAN adapters, and 11 for everything else.
* If adapter is bridged, then we assign 11 to it and devices behind it.
* We also assign the same irq numbers for multi function devices.
* These are PIC mode, so shouldn't matter n.e.ways (hopefully)
*/
-static void assign_alt_irq (struct pci_func * cur_func, u8 class_code)
+static void assign_alt_irq (struct pci_func *cur_func, u8 class_code)
{
int j;
for (j = 0; j < 4; j++) {
@@ -71,11 +71,11 @@ static void assign_alt_irq (struct pci_func * cur_func, u8 class_code)
* Configures the device to be added (will allocate needed resources if it
* can), the device can be a bridge or a regular pci device, can also be
* multi-functional
- *
+ *
* Input: function to be added
- *
+ *
* TO DO: The error case with Multifunction device or multi function bridge,
- * if there is an error, will need to go through all previous functions and
+ * if there is an error, will need to go through all previous functions and
* unconfigure....or can add some code into unconfigure_card....
*/
int ibmphp_configure_card (struct pci_func *func, u8 slotno)
@@ -98,7 +98,7 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
cur_func = func;
/* We only get bus and device from IRQ routing table. So at this point,
- * func->busno is correct, and func->device contains only device (at the 5
+ * func->busno is correct, and func->device contains only device (at the 5
* highest bits)
*/
@@ -109,7 +109,7 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
cur_func->function = function;
- debug ("inside the loop, cur_func->busno = %x, cur_func->device = %x, cur_func->funcion = %x\n",
+ debug ("inside the loop, cur_func->busno = %x, cur_func->device = %x, cur_func->function = %x\n",
cur_func->busno, cur_func->device, cur_func->function);
pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id);
@@ -137,8 +137,8 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
"Please choose another device.\n", cur_func->device);
return -ENODEV;
} else if (class == PCI_CLASS_DISPLAY_VGA) {
- err ("The device %x is not supported for hot plugging. "
- "Please choose another device.\n", cur_func->device);
+ err ("The device %x is not supported for hot plugging. Please choose another device.\n",
+ cur_func->device);
return -ENODEV;
}
switch (hdr_type) {
@@ -151,7 +151,7 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
cur_func->device, cur_func->busno);
cleanup_count = 6;
goto error;
- }
+ }
cur_func->next = NULL;
function = 0x8;
break;
@@ -179,8 +179,8 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
case PCI_HEADER_TYPE_MULTIBRIDGE:
class >>= 8;
if (class != PCI_CLASS_BRIDGE_PCI) {
- err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. "
- "Please insert another card.\n", cur_func->device);
+ err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. Please insert another card.\n",
+ cur_func->device);
return -ENODEV;
}
assign_alt_irq (cur_func, class_code);
@@ -247,8 +247,8 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
class >>= 8;
debug ("class now is %x\n", class);
if (class != PCI_CLASS_BRIDGE_PCI) {
- err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. "
- "Please insert another card.\n", cur_func->device);
+ err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. Please insert another card.\n",
+ cur_func->device);
return -ENODEV;
}
@@ -339,7 +339,7 @@ error:
}
/*
- * This function configures the pci BARs of a single device.
+ * This function configures the pci BARs of a single device.
* Input: pointer to the pci_func
* Output: configured PCI, 0, or error
*/
@@ -364,24 +364,24 @@ static int configure_device (struct pci_func *func)
struct resource_node *pfmem[6];
unsigned int devfn;
- debug ("%s - inside\n", __FUNCTION__);
+ debug ("%s - inside\n", __func__);
devfn = PCI_DEVFN(func->device, func->function);
ibmphp_pci_bus->number = func->busno;
for (count = 0; address[count]; count++) { /* for 6 BARs */
- /* not sure if i need this. per scott, said maybe need smth like this
+ /* not sure if i need this. per scott, said maybe need * something like this
if devices don't adhere 100% to the spec, so don't want to write
to the reserved bits
- pcibios_read_config_byte(cur_func->busno, cur_func->device,
+ pcibios_read_config_byte(cur_func->busno, cur_func->device,
PCI_BASE_ADDRESS_0 + 4 * count, &tmp);
if (tmp & 0x01) // IO
- pcibios_write_config_dword(cur_func->busno, cur_func->device,
+ pcibios_write_config_dword(cur_func->busno, cur_func->device,
PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFD);
else // Memory
- pcibios_write_config_dword(cur_func->busno, cur_func->device,
+ pcibios_write_config_dword(cur_func->busno, cur_func->device,
PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFF);
*/
pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
@@ -421,8 +421,8 @@ static int configure_device (struct pci_func *func)
return -EIO;
}
pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->io[count]->start);
-
- /* _______________This is for debugging purposes only_____________________ */
+
+ /* _______________This is for debugging purposes only_____________________ */
debug ("b4 writing, the IO address is %x\n", func->io[count]->start);
pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]);
debug ("after writing.... the start address is %x\n", bar[count]);
@@ -484,7 +484,7 @@ static int configure_device (struct pci_func *func)
pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start);
- /*_______________This is for debugging purposes only______________________________*/
+ /*_______________This is for debugging purposes only______________________________*/
debug ("b4 writing, start address is %x\n", func->pfmem[count]->start);
pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]);
debug ("after writing, start address is %x\n", bar[count]);
@@ -559,7 +559,7 @@ static int configure_device (struct pci_func *func)
/******************************************************************************
* This routine configures a PCI-2-PCI bridge and the functions behind it
* Parameters: pci_func
- * Returns:
+ * Returns:
******************************************************************************/
static int configure_bridge (struct pci_func **func_passed, u8 slotno)
{
@@ -595,7 +595,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
u8 irq;
int retval;
- debug ("%s - enter\n", __FUNCTION__);
+ debug ("%s - enter\n", __func__);
devfn = PCI_DEVFN(func->function, func->device);
ibmphp_pci_bus->number = func->busno;
@@ -622,7 +622,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
debug ("AFTER FIND_SEC_NUMBER, func->busno IS %x\n", func->busno);
pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, sec_number);
-
+
/* __________________For debugging purposes only __________________________________
pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number);
debug ("sec_number after write/read is %x\n", sec_number);
@@ -644,7 +644,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !!!!!!!!!!!!!!!NEED TO ADD!!! FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!!
+ !!!!!!!!!!!!!!!NEED TO ADD!!! FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
@@ -670,7 +670,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
debug ("len[count] in IO = %x\n", len[count]);
bus_io[count] = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
-
+
if (!bus_io[count]) {
err ("out of system memory\n");
retval = -ENOMEM;
@@ -735,7 +735,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
ibmphp_add_pfmem_from_mem (bus_pfmem[count]);
func->pfmem[count] = bus_pfmem[count];
} else {
- err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n",
+ err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n",
func->busno, func->device, len[count]);
kfree (mem_tmp);
kfree (bus_pfmem[count]);
@@ -805,7 +805,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
debug ("amount_needed->mem = %x\n", amount_needed->mem);
debug ("amount_needed->pfmem = %x\n", amount_needed->pfmem);
- if (amount_needed->not_correct) {
+ if (amount_needed->not_correct) {
debug ("amount_needed is not correct\n");
for (count = 0; address[count]; count++) {
/* for 2 BARs */
@@ -830,7 +830,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
} else {
debug ("it wants %x IO behind the bridge\n", amount_needed->io);
io = kzalloc(sizeof(*io), GFP_KERNEL);
-
+
if (!io) {
err ("out of system memory\n");
retval = -ENOMEM;
@@ -959,7 +959,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
if (bus->noIORanges) {
pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00 | bus->rangeIO->start >> 8);
- pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8);
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8);
/* _______________This is for debugging purposes only ____________________
pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &temp);
@@ -980,7 +980,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
if (bus->noMemRanges) {
pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0x0000 | bus->rangeMem->start >> 16);
pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000 | bus->rangeMem->end >> 16);
-
+
/* ____________________This is for debugging purposes only ________________________
pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &temp);
debug ("mem_base = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
@@ -1017,7 +1017,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq);
if ((irq > 0x00) && (irq < 0x05))
pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]);
- /*
+ /*
pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, ctrl);
pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY);
pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR);
@@ -1071,9 +1071,9 @@ error:
* This function adds up the amount of resources needed behind the PPB bridge
* and passes it to the configure_bridge function
* Input: bridge function
- * Ouput: amount of resources needed
+ * Output: amount of resources needed
*****************************************************************************/
-static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno)
+static struct res_needed *scan_behind_bridge (struct pci_func *func, u8 busno)
{
int count, len[6];
u16 vendor_id;
@@ -1125,13 +1125,11 @@ static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno)
class >>= 8; /* to take revision out, class = class.subclass.prog i/f */
if (class == PCI_CLASS_NOT_DEFINED_VGA) {
- err ("The device %x is VGA compatible and as is not supported for hot plugging. "
- "Please choose another device.\n", device);
+ err ("The device %x is VGA compatible and as is not supported for hot plugging. Please choose another device.\n", device);
amount->not_correct = 1;
return amount;
} else if (class == PCI_CLASS_DISPLAY_VGA) {
- err ("The device %x is not supported for hot plugging. "
- "Please choose another device.\n", device);
+ err ("The device %x is not supported for hot plugging. Please choose another device.\n", device);
amount->not_correct = 1;
return amount;
}
@@ -1204,9 +1202,9 @@ static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno)
return amount;
}
-/* The following 3 unconfigure_boot_ routines deal with the case when we had the card
- * upon bootup in the system, since we don't allocate func to such case, we need to read
- * the start addresses from pci config space and then find the corresponding entries in
+/* The following 3 unconfigure_boot_ routines deal with the case when we had the card
+ * upon bootup in the system, since we don't allocate func to such case, we need to read
+ * the start addresses from pci config space and then find the corresponding entries in
* our resource lists. The functions return either 0, -ENODEV, or -1 (general failure)
* Change: we also call these functions even if we configured the card ourselves (i.e., not
* the bootup case), since it should work same way
@@ -1234,7 +1232,7 @@ static int unconfigure_boot_device (u8 busno, u8 device, u8 function)
u32 tmp_address;
unsigned int devfn;
- debug ("%s - enter\n", __FUNCTION__);
+ debug ("%s - enter\n", __func__);
bus = ibmphp_find_res_bus (busno);
if (!bus) {
@@ -1351,7 +1349,7 @@ static int unconfigure_boot_bridge (u8 busno, u8 device, u8 function)
bus_no = (int) busno;
debug ("busno is %x\n", busno);
pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, &pri_number);
- debug ("%s - busno = %x, primary_number = %x\n", __FUNCTION__, busno, pri_number);
+ debug ("%s - busno = %x, primary_number = %x\n", __func__, busno, pri_number);
pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number);
debug ("sec_number is %x\n", sec_number);
@@ -1437,7 +1435,7 @@ static int unconfigure_boot_bridge (u8 busno, u8 device, u8 function)
}
} /* end of mem */
} /* end of for */
- debug ("%s - exiting, returning success\n", __FUNCTION__);
+ debug ("%s - exiting, returning success\n", __func__);
return 0;
}
@@ -1453,7 +1451,7 @@ static int unconfigure_boot_card (struct slot *slot_cur)
unsigned int devfn;
u8 valid_device = 0x00; /* To see if we are ever able to find valid device and read it */
- debug ("%s - enter\n", __FUNCTION__);
+ debug ("%s - enter\n", __func__);
device = slot_cur->device;
busno = slot_cur->bus;
@@ -1470,7 +1468,7 @@ static int unconfigure_boot_card (struct slot *slot_cur)
/* found correct device!!! */
++valid_device;
- debug ("%s - found correct device\n", __FUNCTION__);
+ debug ("%s - found correct device\n", __func__);
/* header: x x x x x x x x
* | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge
@@ -1483,12 +1481,10 @@ static int unconfigure_boot_card (struct slot *slot_cur)
debug ("hdr_type %x, class %x\n", hdr_type, class);
class >>= 8; /* to take revision out, class = class.subclass.prog i/f */
if (class == PCI_CLASS_NOT_DEFINED_VGA) {
- err ("The device %x function %x is VGA compatible and is not supported for hot removing. "
- "Please choose another device.\n", device, function);
+ err ("The device %x function %x is VGA compatible and is not supported for hot removing. Please choose another device.\n", device, function);
return -ENODEV;
} else if (class == PCI_CLASS_DISPLAY_VGA) {
- err ("The device %x function %x is not supported for hot removing. "
- "Please choose another device.\n", device, function);
+ err ("The device %x function %x is not supported for hot removing. Please choose another device.\n", device, function);
return -ENODEV;
}
@@ -1513,9 +1509,7 @@ static int unconfigure_boot_card (struct slot *slot_cur)
case PCI_HEADER_TYPE_BRIDGE:
class >>= 8;
if (class != PCI_CLASS_BRIDGE_PCI) {
- err ("This device %x function %x is not PCI-to-PCI bridge, "
- "and is not supported for hot-removing. "
- "Please try another card.\n", device, function);
+ err ("This device %x function %x is not PCI-to-PCI bridge, and is not supported for hot-removing. Please try another card.\n", device, function);
return -ENODEV;
}
rc = unconfigure_boot_bridge (busno, device, function);
@@ -1529,9 +1523,7 @@ static int unconfigure_boot_card (struct slot *slot_cur)
case PCI_HEADER_TYPE_MULTIBRIDGE:
class >>= 8;
if (class != PCI_CLASS_BRIDGE_PCI) {
- err ("This device %x function %x is not PCI-to-PCI bridge, "
- "and is not supported for hot-removing. "
- "Please try another card.\n", device, function);
+ err ("This device %x function %x is not PCI-to-PCI bridge, and is not supported for hot-removing. Please try another card.\n", device, function);
return -ENODEV;
}
rc = unconfigure_boot_bridge (busno, device, function);
@@ -1561,8 +1553,8 @@ static int unconfigure_boot_card (struct slot *slot_cur)
* unconfiguring the device
* TO DO: will probably need to add some code in case there was some resource,
* to remove it... this is from when we have errors in the configure_card...
- * !!!!!!!!!!!!!!!!!!!!!!!!!FOR BUSES!!!!!!!!!!!!
- * Returns: 0, -1, -ENODEV
+ * !!!!!!!!!!!!!!!!!!!!!!!!!FOR BUSES!!!!!!!!!!!!
+ * Returns: 0, -1, -ENODEV
*/
int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end)
{
@@ -1573,7 +1565,7 @@ int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end)
struct pci_func *cur_func = NULL;
struct pci_func *temp_func;
- debug ("%s - enter\n", __FUNCTION__);
+ debug ("%s - enter\n", __func__);
if (!the_end) {
/* Need to unconfigure the card */
@@ -1624,7 +1616,7 @@ int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end)
sl->func = NULL;
*slot_cur = sl;
- debug ("%s - exit\n", __FUNCTION__);
+ debug ("%s - exit\n", __func__);
return 0;
}
@@ -1634,7 +1626,7 @@ int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end)
* Input: bus and the amount of resources needed (we know we can assign those,
* since they've been checked already
* Output: bus added to the correct spot
- * 0, -1, error
+ * 0, -1, error
*/
static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct resource_node *mem, struct resource_node *pfmem, u8 parent_busno)
{
@@ -1650,7 +1642,7 @@ static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct r
err ("strange, cannot find bus which is supposed to be at the system... something is terribly wrong...\n");
return -ENODEV;
}
-
+
list_add (&bus->bus_list, &cur_bus->bus_list);
}
if (io) {
@@ -1679,7 +1671,7 @@ static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct r
}
if (pfmem) {
pfmem_range = kzalloc(sizeof(*pfmem_range), GFP_KERNEL);
- if (!pfmem_range) {
+ if (!pfmem_range) {
err ("out of system memory\n");
return -ENOMEM;
}
@@ -1726,4 +1718,3 @@ static u8 find_sec_number (u8 primary_busno, u8 slotno)
return busno;
return 0xff;
}
-
diff --git a/drivers/pci/hotplug/ibmphp_res.c b/drivers/pci/hotplug/ibmphp_res.c
index 5636b1ac2a2..f34745abd5b 100644
--- a/drivers/pci/hotplug/ibmphp_res.c
+++ b/drivers/pci/hotplug/ibmphp_res.c
@@ -40,15 +40,15 @@ static void update_resources (struct bus_node *bus_cur, int type, int rangeno);
static int once_over (void);
static int remove_ranges (struct bus_node *, struct bus_node *);
static int update_bridge_ranges (struct bus_node **);
-static int add_range (int type, struct range_node *, struct bus_node *);
+static int add_bus_range (int type, struct range_node *, struct bus_node *);
static void fix_resources (struct bus_node *);
static struct bus_node *find_bus_wprev (u8, struct bus_node **, u8);
static LIST_HEAD(gbuses);
-static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc * curr, u8 busno, int flag)
+static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc *curr, u8 busno, int flag)
{
- struct bus_node * newbus;
+ struct bus_node *newbus;
if (!(curr) && !(flag)) {
err ("NULL pointer passed\n");
@@ -69,10 +69,10 @@ static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc * curr, u8
return newbus;
}
-static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc * curr)
+static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc *curr)
{
struct resource_node *rs;
-
+
if (!curr) {
err ("NULL passed to allocate\n");
return NULL;
@@ -93,7 +93,7 @@ static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc * cur
static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node **new_range, struct ebda_pci_rsrc *curr, int flag, u8 first_bus)
{
- struct bus_node * newbus;
+ struct bus_node *newbus;
struct range_node *newrange;
u8 num_ranges = 0;
@@ -128,12 +128,12 @@ static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node
}
newrange->start = curr->start_addr;
newrange->end = curr->end_addr;
-
+
if (first_bus || (!num_ranges))
newrange->rangeno = 1;
else {
/* need to insert our range */
- add_range (flag, newrange, newbus);
+ add_bus_range (flag, newrange, newbus);
debug ("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end);
}
@@ -162,7 +162,7 @@ static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node
newbus->rangePFMem = newrange;
if (first_bus)
newbus->noPFMemRanges = 1;
- else {
+ else {
debug ("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
++newbus->noPFMemRanges;
fix_resources (newbus);
@@ -190,7 +190,7 @@ static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node
* This is the Resource Management initialization function. It will go through
* the Resource list taken from EBDA and fill in this module's data structures
*
- * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES,
+ * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES,
* SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW
*
* Input: ptr to the head of the resource list from EBDA
@@ -382,9 +382,9 @@ int __init ibmphp_rsrc_init (void)
* pci devices' resources for the appropriate resource
*
* Input: type of the resource, range to add, current bus
- * Output: 0 or -1, bus and range ptrs
+ * Output: 0 or -1, bus and range ptrs
********************************************************************************/
-static int add_range (int type, struct range_node *range, struct bus_node *bus_cur)
+static int add_bus_range (int type, struct range_node *range, struct bus_node *bus_cur)
{
struct range_node *range_cur = NULL;
struct range_node *range_prev;
@@ -455,7 +455,7 @@ static int add_range (int type, struct range_node *range, struct bus_node *bus_c
/*******************************************************************************
* This routine goes through the list of resources of type 'type' and updates
- * the range numbers that they correspond to. It was called from add_range fnc
+ * the range numbers that they correspond to. It was called from add_bus_range fnc
*
* Input: bus, type of the resource, the rangeno starting from which to update
******************************************************************************/
@@ -466,7 +466,7 @@ static void update_resources (struct bus_node *bus_cur, int type, int rangeno)
switch (type) {
case MEM:
- if (bus_cur->firstMem)
+ if (bus_cur->firstMem)
res = bus_cur->firstMem;
break;
case PFMEM:
@@ -563,7 +563,7 @@ static void fix_resources (struct bus_node *bus_cur)
struct range_node *range;
struct resource_node *res;
- debug ("%s - bus_cur->busno = %d\n", __FUNCTION__, bus_cur->busno);
+ debug ("%s - bus_cur->busno = %d\n", __func__, bus_cur->busno);
if (bus_cur->needIOUpdate) {
res = bus_cur->firstIO;
@@ -583,7 +583,7 @@ static void fix_resources (struct bus_node *bus_cur)
}
/*******************************************************************************
- * This routine adds a resource to the list of resources to the appropriate bus
+ * This routine adds a resource to the list of resources to the appropriate bus
* based on their resource type and sorted by their starting addresses. It assigns
* the ptrs to next and nextRange if needed.
*
@@ -599,17 +599,17 @@ int ibmphp_add_resource (struct resource_node *res)
struct range_node *range_cur = NULL;
struct resource_node *res_start = NULL;
- debug ("%s - enter\n", __FUNCTION__);
+ debug ("%s - enter\n", __func__);
if (!res) {
err ("NULL passed to add\n");
return -ENODEV;
}
-
+
bus_cur = find_bus_wprev (res->busno, NULL, 0);
-
+
if (!bus_cur) {
- /* didn't find a bus, smth's wrong!!! */
+ /* didn't find a bus, something's wrong!!! */
debug ("no bus in the system, either pci_dev's wrong or allocation failed\n");
return -ENODEV;
}
@@ -648,7 +648,7 @@ int ibmphp_add_resource (struct resource_node *res)
if (!range_cur) {
switch (res->type) {
case IO:
- ++bus_cur->needIOUpdate;
+ ++bus_cur->needIOUpdate;
break;
case MEM:
++bus_cur->needMemUpdate;
@@ -659,13 +659,13 @@ int ibmphp_add_resource (struct resource_node *res)
}
res->rangeno = -1;
}
-
+
debug ("The range is %d\n", res->rangeno);
if (!res_start) {
/* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */
switch (res->type) {
case IO:
- bus_cur->firstIO = res;
+ bus_cur->firstIO = res;
break;
case MEM:
bus_cur->firstMem = res;
@@ -673,7 +673,7 @@ int ibmphp_add_resource (struct resource_node *res)
case PFMEM:
bus_cur->firstPFMem = res;
break;
- }
+ }
res->next = NULL;
res->nextRange = NULL;
} else {
@@ -762,7 +762,7 @@ int ibmphp_add_resource (struct resource_node *res)
}
}
- debug ("%s - exit\n", __FUNCTION__);
+ debug ("%s - exit\n", __func__);
return 0;
}
@@ -770,7 +770,7 @@ int ibmphp_add_resource (struct resource_node *res)
* This routine will remove the resource from the list of resources
*
* Input: io, mem, and/or pfmem resource to be deleted
- * Ouput: modified resource list
+ * Output: modified resource list
* 0 or error code
****************************************************************************/
int ibmphp_remove_resource (struct resource_node *res)
@@ -789,8 +789,7 @@ int ibmphp_remove_resource (struct resource_node *res)
bus_cur = find_bus_wprev (res->busno, NULL, 0);
if (!bus_cur) {
- err ("cannot find corresponding bus of the io resource to remove "
- "bailing out...\n");
+ err ("cannot find corresponding bus of the io resource to remove bailing out...\n");
return -ENODEV;
}
@@ -825,7 +824,7 @@ int ibmphp_remove_resource (struct resource_node *res)
if (!res_cur) {
if (res->type == PFMEM) {
- /*
+ /*
* case where pfmem might be in the PFMemFromMem list
* so will also need to remove the corresponding mem
* entry
@@ -934,9 +933,9 @@ int ibmphp_remove_resource (struct resource_node *res)
return 0;
}
-static struct range_node * find_range (struct bus_node *bus_cur, struct resource_node * res)
+static struct range_node *find_range (struct bus_node *bus_cur, struct resource_node *res)
{
- struct range_node * range = NULL;
+ struct range_node *range = NULL;
switch (res->type) {
case IO:
@@ -961,12 +960,12 @@ static struct range_node * find_range (struct bus_node *bus_cur, struct resource
}
/*****************************************************************************
- * This routine will check to make sure the io/mem/pfmem->len that the device asked for
+ * This routine will check to make sure the io/mem/pfmem->len that the device asked for
* can fit w/i our list of available IO/MEM/PFMEM resources. If cannot, returns -EINVAL,
* otherwise, returns 0
*
* Input: resource
- * Ouput: the correct start and end address are inputted into the resource node,
+ * Output: the correct start and end address are inputted into the resource node,
* 0 or -EINVAL
*****************************************************************************/
int ibmphp_check_resource (struct resource_node *res, u8 bridge)
@@ -996,12 +995,12 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
bus_cur = find_bus_wprev (res->busno, NULL, 0);
if (!bus_cur) {
- /* didn't find a bus, smth's wrong!!! */
+ /* didn't find a bus, something's wrong!!! */
debug ("no bus in the system, either pci_dev's wrong or allocation failed\n");
return -EINVAL;
}
- debug ("%s - enter\n", __FUNCTION__);
+ debug ("%s - enter\n", __func__);
debug ("bus_cur->busno is %d\n", bus_cur->busno);
/* This is a quick fix to not mess up with the code very much. i.e.,
@@ -1029,7 +1028,7 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
while (res_cur) {
range = find_range (bus_cur, res_cur);
- debug ("%s - rangeno = %d\n", __FUNCTION__, res_cur->rangeno);
+ debug ("%s - rangeno = %d\n", __func__, res_cur->rangeno);
if (!range) {
err ("no range for the device exists... bailing out...\n");
@@ -1066,7 +1065,7 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
break;
}
}
-
+
if (flag && len_cur == res->len) {
debug ("but we are not here, right?\n");
res->start = start_cur;
@@ -1118,10 +1117,10 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
if (res_prev) {
if (res_prev->rangeno != res_cur->rangeno) {
/* 1st device on this range */
- if ((res_cur->start != range->start) &&
+ if ((res_cur->start != range->start) &&
((len_tmp = res_cur->start - 1 - range->start) >= res->len)) {
if ((len_tmp < len_cur) || (len_cur == 0)) {
- if ((range->start % tmp_divide) == 0) {
+ if ((range->start % tmp_divide) == 0) {
/* just perfect, starting address is divisible by length */
flag = 1;
len_cur = len_tmp;
@@ -1344,7 +1343,7 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
* This routine is called from remove_card if the card contained PPB.
* It will remove all the resources on the bus as well as the bus itself
* Input: Bus
- * Ouput: 0, -ENODEV
+ * Output: 0, -ENODEV
********************************************************************************/
int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno)
{
@@ -1353,7 +1352,7 @@ int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno)
struct bus_node *prev_bus;
int rc;
- prev_bus = find_bus_wprev (parent_busno, NULL, 0);
+ prev_bus = find_bus_wprev (parent_busno, NULL, 0);
if (!prev_bus) {
debug ("something terribly wrong. Cannot find parent bus to the one to remove\n");
@@ -1424,7 +1423,7 @@ int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno)
}
/******************************************************************************
- * This routine deletes the ranges from a given bus, and the entries from the
+ * This routine deletes the ranges from a given bus, and the entries from the
* parent's bus in the resources
* Input: current bus, previous bus
* Output: 0, -EINVAL
@@ -1453,7 +1452,7 @@ static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev)
if (bus_cur->noMemRanges) {
range_cur = bus_cur->rangeMem;
for (i = 0; i < bus_cur->noMemRanges; i++) {
- if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0)
+ if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0)
return -EINVAL;
ibmphp_remove_resource (res);
@@ -1467,7 +1466,7 @@ static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev)
if (bus_cur->noPFMemRanges) {
range_cur = bus_cur->rangePFMem;
for (i = 0; i < bus_cur->noPFMemRanges; i++) {
- if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0)
+ if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0)
return -EINVAL;
ibmphp_remove_resource (res);
@@ -1482,7 +1481,7 @@ static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev)
}
/*
- * find the resource node in the bus
+ * find the resource node in the bus
* Input: Resource needed, start address of the resource, type of resource
*/
int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resource_node **res, int flag)
@@ -1512,7 +1511,7 @@ int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resour
err ("wrong type of flag\n");
return -EINVAL;
}
-
+
while (res_cur) {
if (res_cur->start == start_address) {
*res = res_cur;
@@ -1718,7 +1717,7 @@ static int __init once_over (void)
} /* end for pfmem */
} /* end if */
} /* end list_for_each bus */
- return 0;
+ return 0;
}
int ibmphp_add_pfmem_from_mem (struct resource_node *pfmem)
@@ -1760,9 +1759,9 @@ static struct bus_node *find_bus_wprev (u8 bus_number, struct bus_node **prev, u
list_for_each (tmp, &gbuses) {
tmp_prev = tmp->prev;
bus_cur = list_entry (tmp, struct bus_node, bus_list);
- if (flag)
+ if (flag)
*prev = list_entry (tmp_prev, struct bus_node, bus_list);
- if (bus_cur->busno == bus_number)
+ if (bus_cur->busno == bus_number)
return bus_cur;
}
@@ -1776,7 +1775,7 @@ void ibmphp_print_test (void)
struct range_node *range;
struct resource_node *res;
struct list_head *tmp;
-
+
debug_pci ("*****************START**********************\n");
if ((!list_empty(&gbuses)) && flags) {
@@ -1906,7 +1905,7 @@ static int range_exists_already (struct range_node * range, struct bus_node * bu
return 1;
range_cur = range_cur->next;
}
-
+
return 0;
}
@@ -1920,7 +1919,7 @@ static int range_exists_already (struct range_node * range, struct bus_node * bu
* Returns: none
* Note: this function doesn't take into account IO restrictions etc,
* so will only work for bridges with no video/ISA devices behind them It
- * also will not work for onboard PPB's that can have more than 1 *bus
+ * also will not work for onboard PPBs that can have more than 1 *bus
* behind them All these are TO DO.
* Also need to add more error checkings... (from fnc returns etc)
*/
@@ -1942,7 +1941,7 @@ static int __init update_bridge_ranges (struct bus_node **bus)
return -ENODEV;
ibmphp_pci_bus->number = bus_cur->busno;
- debug ("inside %s\n", __FUNCTION__);
+ debug ("inside %s\n", __func__);
debug ("bus_cur->busno = %x\n", bus_cur->busno);
for (device = 0; device < 32; device++) {
@@ -1963,7 +1962,7 @@ static int __init update_bridge_ranges (struct bus_node **bus)
case PCI_HEADER_TYPE_BRIDGE:
function = 0x8;
case PCI_HEADER_TYPE_MULTIBRIDGE:
- /* We assume here that only 1 bus behind the bridge
+ /* We assume here that only 1 bus behind the bridge
TO DO: add functionality for several:
temp = secondary;
while (temp < subordinate) {
@@ -1972,7 +1971,7 @@ static int __init update_bridge_ranges (struct bus_node **bus)
}
*/
pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_busno);
- bus_sec = find_bus_wprev (sec_busno, NULL, 0);
+ bus_sec = find_bus_wprev (sec_busno, NULL, 0);
/* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */
if (!bus_sec) {
bus_sec = alloc_error_bus (NULL, sec_busno, 1);
@@ -1999,7 +1998,7 @@ static int __init update_bridge_ranges (struct bus_node **bus)
if (bus_sec->noIORanges > 0) {
if (!range_exists_already (range, bus_sec, IO)) {
- add_range (IO, range, bus_sec);
+ add_bus_range (IO, range, bus_sec);
++bus_sec->noIORanges;
} else {
kfree (range);
@@ -2028,7 +2027,7 @@ static int __init update_bridge_ranges (struct bus_node **bus)
io->len = io->end - io->start + 1;
ibmphp_add_resource (io);
}
- }
+ }
pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &start_mem_address);
pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &end_mem_address);
@@ -2048,7 +2047,7 @@ static int __init update_bridge_ranges (struct bus_node **bus)
if (bus_sec->noMemRanges > 0) {
if (!range_exists_already (range, bus_sec, MEM)) {
- add_range (MEM, range, bus_sec);
+ add_bus_range (MEM, range, bus_sec);
++bus_sec->noMemRanges;
} else {
kfree (range);
@@ -2102,7 +2101,7 @@ static int __init update_bridge_ranges (struct bus_node **bus)
if (bus_sec->noPFMemRanges > 0) {
if (!range_exists_already (range, bus_sec, PFMEM)) {
- add_range (PFMEM, range, bus_sec);
+ add_bus_range (PFMEM, range, bus_sec);
++bus_sec->noPFMemRanges;
} else {
kfree (range);
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index dd59a050260..56d8486dc16 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -33,115 +33,48 @@
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/pagemap.h>
-#include <linux/slab.h>
#include <linux/init.h>
#include <linux/mount.h>
#include <linux/namei.h>
+#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <asm/uaccess.h>
+#include "../pci.h"
+#include "cpci_hotplug.h"
#define MY_NAME "pci_hotplug"
-#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0)
+#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
/* local variables */
-static int debug;
+static bool debug;
#define DRIVER_VERSION "0.5"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>"
#define DRIVER_DESC "PCI Hot Plug PCI Core"
-//////////////////////////////////////////////////////////////////
-
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,
-};
-
-/* these strings match up with the values in pci_bus_speed */
-static char *pci_bus_speed_strings[] = {
- "33 MHz PCI", /* 0x00 */
- "66 MHz PCI", /* 0x01 */
- "66 MHz PCIX", /* 0x02 */
- "100 MHz PCIX", /* 0x03 */
- "133 MHz PCIX", /* 0x04 */
- NULL, /* 0x05 */
- NULL, /* 0x06 */
- NULL, /* 0x07 */
- NULL, /* 0x08 */
- "66 MHz PCIX 266", /* 0x09 */
- "100 MHz PCIX 266", /* 0x0a */
- "133 MHz PCIX 266", /* 0x0b */
- NULL, /* 0x0c */
- NULL, /* 0x0d */
- NULL, /* 0x0e */
- NULL, /* 0x0f */
- NULL, /* 0x10 */
- "66 MHz PCIX 533", /* 0x11 */
- "100 MHz PCIX 533", /* 0x12 */
- "133 MHz PCIX 533", /* 0x13 */
- "25 GBps PCI-E", /* 0x14 */
-};
-
-#ifdef CONFIG_HOTPLUG_PCI_CPCI
-extern int cpci_hotplug_init(int debug);
-extern void cpci_hotplug_exit(void);
-#else
-static inline int cpci_hotplug_init(int debug) { return 0; }
-static inline void cpci_hotplug_exit(void) { }
-#endif
+static DEFINE_MUTEX(pci_hp_mutex);
/* Weee, fun with macros... */
-#define GET_STATUS(name,type) \
-static int get_##name (struct hotplug_slot *slot, type *value) \
+#define GET_STATUS(name, type) \
+static int get_##name(struct hotplug_slot *slot, type *value) \
{ \
struct hotplug_slot_ops *ops = slot->ops; \
int retval = 0; \
- if (try_module_get(ops->owner)) { \
- if (ops->get_##name) \
- retval = ops->get_##name(slot, value); \
- else \
- *value = slot->info->name; \
- module_put(ops->owner); \
- } \
+ if (!try_module_get(ops->owner)) \
+ return -ENODEV; \
+ if (ops->get_##name) \
+ retval = ops->get_##name(slot, value); \
+ else \
+ *value = slot->info->name; \
+ module_put(ops->owner); \
return retval; \
}
@@ -149,236 +82,153 @@ 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);
-exit:
- return retval;
+ return retval;
+
+ return sprintf(buf, "%d\n", value);
}
-static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
- size_t count)
+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;
- lpower = simple_strtoul (buf, NULL, 10);
+ lpower = simple_strtoul(buf, NULL, 10);
power = (u8)(lpower & 0xff);
- dbg ("power = %d\n", power);
+ dbg("power = %d\n", power);
if (!try_module_get(slot->ops->owner)) {
retval = -ENODEV;
goto exit;
}
switch (power) {
- case 0:
- if (slot->ops->disable_slot)
- retval = slot->ops->disable_slot(slot);
- break;
-
- case 1:
- if (slot->ops->enable_slot)
- retval = slot->ops->enable_slot(slot);
- break;
-
- default:
- err ("Illegal value specified for power\n");
- retval = -EINVAL;
+ case 0:
+ if (slot->ops->disable_slot)
+ retval = slot->ops->disable_slot(slot);
+ break;
+
+ case 1:
+ if (slot->ops->enable_slot)
+ retval = slot->ops->enable_slot(slot);
+ break;
+
+ default:
+ err("Illegal value specified for power\n");
+ retval = -EINVAL;
}
module_put(slot->ops->owner);
-exit:
+exit:
if (retval)
return retval;
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);
+ return retval;
-exit:
- return retval;
+ return sprintf(buf, "%d\n", value);
}
-static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
- size_t count)
+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;
- lattention = simple_strtoul (buf, NULL, 10);
+ lattention = simple_strtoul(buf, NULL, 10);
attention = (u8)(lattention & 0xff);
- dbg (" - attention = %d\n", attention);
+ 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:
+exit:
if (retval)
return retval;
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);
+ return retval;
-exit:
- return retval;
+ return sprintf(buf, "%d\n", value);
}
-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);
+ return retval;
-exit:
- return retval;
+ return sprintf(buf, "%d\n", value);
}
-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)
-{
- char *speed_string;
- int retval;
- enum pci_bus_speed value;
-
- retval = get_max_bus_speed (slot, &value);
- if (retval)
- goto exit;
-
- if (value == PCI_SPEED_UNKNOWN)
- speed_string = unknown_speed;
- else
- speed_string = pci_bus_speed_strings[value];
-
- retval = sprintf (buf, "%s\n", speed_string);
-
-exit:
- return retval;
-}
-
-static struct hotplug_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)
-{
- char *speed_string;
- int retval;
- enum pci_bus_speed value;
-
- retval = get_cur_bus_speed (slot, &value);
- if (retval)
- goto exit;
-
- if (value == PCI_SPEED_UNKNOWN)
- speed_string = unknown_speed;
- else
- speed_string = pci_bus_speed_strings[value];
-
- retval = sprintf (buf, "%s\n", speed_string);
-
-exit:
- return retval;
-}
-
-static struct hotplug_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,
- size_t count)
+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;
ltest = simple_strtoul (buf, NULL, 10);
test = (u32)(ltest & 0xffffffff);
- dbg ("test = %d\n", test);
+ dbg("test = %d\n", test);
if (!try_module_get(slot->ops->owner)) {
retval = -ENODEV;
@@ -388,145 +238,111 @@ static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
retval = slot->ops->hardware_test(slot, test);
module_put(slot->ops->owner);
-exit:
+exit:
if (retval)
return retval;
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 bool has_power_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
+
if ((!slot) || (!slot->ops))
- return -ENODEV;
+ return false;
if ((slot->ops->enable_slot) ||
(slot->ops->disable_slot) ||
(slot->ops->get_power_status))
- return 0;
- return -ENOENT;
+ return true;
+ return false;
}
-static int has_attention_file (struct hotplug_slot *slot)
+static bool has_attention_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
+
if ((!slot) || (!slot->ops))
- return -ENODEV;
+ return false;
if ((slot->ops->set_attention_status) ||
(slot->ops->get_attention_status))
- return 0;
- return -ENOENT;
+ return true;
+ return false;
}
-static int has_latch_file (struct hotplug_slot *slot)
+static bool has_latch_file(struct pci_slot *pci_slot)
{
- if ((!slot) || (!slot->ops))
- return -ENODEV;
- if (slot->ops->get_latch_status)
- return 0;
- return -ENOENT;
-}
+ struct hotplug_slot *slot = pci_slot->hotplug;
-static int has_adapter_file (struct hotplug_slot *slot)
-{
if ((!slot) || (!slot->ops))
- return -ENODEV;
- if (slot->ops->get_adapter_status)
- return 0;
- return -ENOENT;
+ return false;
+ if (slot->ops->get_latch_status)
+ return true;
+ return false;
}
-static int has_address_file (struct hotplug_slot *slot)
+static bool has_adapter_file(struct pci_slot *pci_slot)
{
- if ((!slot) || (!slot->ops))
- return -ENODEV;
- if (slot->ops->get_address)
- return 0;
- return -ENOENT;
-}
+ struct hotplug_slot *slot = pci_slot->hotplug;
-static int has_max_bus_speed_file (struct hotplug_slot *slot)
-{
if ((!slot) || (!slot->ops))
- return -ENODEV;
- if (slot->ops->get_max_bus_speed)
- return 0;
- return -ENOENT;
+ return false;
+ if (slot->ops->get_adapter_status)
+ return true;
+ return false;
}
-static int has_cur_bus_speed_file (struct hotplug_slot *slot)
+static bool has_test_file(struct pci_slot *pci_slot)
{
- if ((!slot) || (!slot->ops))
- return -ENODEV;
- if (slot->ops->get_cur_bus_speed)
- return 0;
- return -ENOENT;
-}
+ struct hotplug_slot *slot = pci_slot->hotplug;
-static int has_test_file (struct hotplug_slot *slot)
-{
if ((!slot) || (!slot->ops))
- return -ENODEV;
+ return false;
if (slot->ops->hardware_test)
- return 0;
- return -ENOENT;
+ return true;
+ return false;
}
-static int fs_add_slot (struct hotplug_slot *slot)
+static int fs_add_slot(struct pci_slot *slot)
{
int retval = 0;
- if (has_power_file(slot) == 0) {
- retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+ /* Create symbolic link to the hotplug driver module */
+ pci_hp_create_module_link(slot);
+
+ if (has_power_file(slot)) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_power.attr);
if (retval)
goto exit_power;
}
- if (has_attention_file(slot) == 0) {
+ if (has_attention_file(slot)) {
retval = sysfs_create_file(&slot->kobj,
&hotplug_slot_attr_attention.attr);
if (retval)
goto exit_attention;
}
- if (has_latch_file(slot) == 0) {
+ if (has_latch_file(slot)) {
retval = sysfs_create_file(&slot->kobj,
&hotplug_slot_attr_latch.attr);
if (retval)
goto exit_latch;
}
- if (has_adapter_file(slot) == 0) {
+ if (has_adapter_file(slot)) {
retval = sysfs_create_file(&slot->kobj,
&hotplug_slot_attr_presence.attr);
if (retval)
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);
- if (retval)
- goto exit_max_speed;
- }
-
- if (has_cur_bus_speed_file(slot) == 0) {
- retval = sysfs_create_file(&slot->kobj,
- &hotplug_slot_attr_cur_bus_speed.attr);
- if (retval)
- goto exit_cur_speed;
- }
-
- if (has_test_file(slot) == 0) {
+ if (has_test_file(slot)) {
retval = sysfs_create_file(&slot->kobj,
&hotplug_slot_attr_test.attr);
if (retval)
@@ -536,199 +352,197 @@ static int fs_add_slot (struct hotplug_slot *slot)
goto exit;
exit_test:
- if (has_cur_bus_speed_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
-
-exit_cur_speed:
- if (has_max_bus_speed_file(slot) == 0)
- 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);
-
+ if (has_adapter_file(slot))
+ sysfs_remove_file(&slot->kobj,
+ &hotplug_slot_attr_presence.attr);
exit_adapter:
- if (has_latch_file(slot) == 0)
+ if (has_latch_file(slot))
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
-
exit_latch:
- if (has_attention_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
-
+ if (has_attention_file(slot))
+ sysfs_remove_file(&slot->kobj,
+ &hotplug_slot_attr_attention.attr);
exit_attention:
- if (has_power_file(slot) == 0)
+ if (has_power_file(slot))
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
exit_power:
+ pci_hp_remove_module_link(slot);
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)
+ if (has_power_file(slot))
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
- if (has_attention_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+ if (has_attention_file(slot))
+ sysfs_remove_file(&slot->kobj,
+ &hotplug_slot_attr_attention.attr);
- if (has_latch_file(slot) == 0)
+ if (has_latch_file(slot))
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
- 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_adapter_file(slot))
+ sysfs_remove_file(&slot->kobj,
+ &hotplug_slot_attr_presence.attr);
- if (has_max_bus_speed_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
-
- if (has_cur_bus_speed_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
-
- if (has_test_file(slot) == 0)
+ if (has_test_file(slot))
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
+
+ pci_hp_remove_module_link(slot);
}
-static struct hotplug_slot *get_slot_from_name (const char *name)
+static struct hotplug_slot *get_slot_from_name(const char *name)
{
struct hotplug_slot *slot;
struct list_head *tmp;
- list_for_each (tmp, &pci_hotplug_slot_list) {
- slot = list_entry (tmp, struct hotplug_slot, slot_list);
- if (strcmp(slot->name, name) == 0)
+ list_for_each(tmp, &pci_hotplug_slot_list) {
+ slot = list_entry(tmp, struct hotplug_slot, slot_list);
+ if (strcmp(hotplug_slot_name(slot), name) == 0)
return slot;
}
return NULL;
}
/**
- * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
+ * __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
+ * @devnr: device number
+ * @name: name registered with kobject core
+ * @owner: caller module owner
+ * @mod_name: caller module name
*
* 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 devnr, const char *name,
+ struct module *owner, const char *mod_name)
{
int result;
+ struct pci_slot *pci_slot;
if (slot == NULL)
return -ENODEV;
if ((slot->info == NULL) || (slot->ops == NULL))
return -EINVAL;
if (slot->release == NULL) {
- dbg("Why are you trying to register a hotplug slot "
- "without a proper release function?\n");
+ dbg("Why are you trying to register a hotplug slot without a proper release function?\n");
return -EINVAL;
}
- /* this can fail if we have already registered a slot with the same name */
- 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;
+ slot->ops->owner = owner;
+ slot->ops->mod_name = mod_name;
+
+ mutex_lock(&pci_hp_mutex);
+ /*
+ * 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, devnr, name, slot);
+ if (IS_ERR(pci_slot)) {
+ result = PTR_ERR(pci_slot);
+ goto out;
}
- list_add (&slot->slot_list, &pci_hotplug_slot_list);
+ slot->pci_slot = pci_slot;
+ pci_slot->hotplug = slot;
+
+ list_add(&slot->slot_list, &pci_hotplug_slot_list);
- result = fs_add_slot (slot);
- kobject_uevent(&slot->kobj, KOBJ_ADD);
- dbg ("Added slot %s to the list\n", slot->name);
+ result = fs_add_slot(pci_slot);
+ kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
+ dbg("Added slot %s to the list\n", name);
+out:
+ mutex_unlock(&pci_hp_mutex);
return result;
}
+EXPORT_SYMBOL_GPL(__pci_hp_register);
/**
* 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) {
+ mutex_lock(&pci_hp_mutex);
+ temp = get_slot_from_name(hotplug_slot_name(hotplug));
+ if (temp != hotplug) {
+ mutex_unlock(&pci_hp_mutex);
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);
+ list_del(&hotplug->slot_list);
+
+ slot = hotplug->pci_slot;
+ fs_remove_slot(slot);
+ dbg("Removed slot %s from the list\n", hotplug_slot_name(hotplug));
+
+ hotplug->release(hotplug);
+ slot->hotplug = NULL;
+ pci_destroy_slot(slot);
+ mutex_unlock(&pci_hp_mutex);
+
return 0;
}
+EXPORT_SYMBOL_GPL(pci_hp_deregister);
/**
* 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
+ * @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 __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
- struct hotplug_slot_info *info)
+int pci_hp_change_slot_info(struct hotplug_slot *hotplug,
+ struct hotplug_slot_info *info)
{
- if ((slot == NULL) || (info == NULL))
+ if (!hotplug || !info)
return -ENODEV;
- memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
+ memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));
return 0;
}
+EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
-static int __init pci_hotplug_init (void)
+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;
+ err("cpci_hotplug_init with error %d\n", result);
+ return result;
}
- info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
- goto exit;
-
-err_subsys:
- kset_unregister(pci_hotplug_slots_kset);
-exit:
+ info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
return result;
}
-static void __exit pci_hotplug_exit (void)
+static void __exit pci_hotplug_exit(void)
{
cpci_hotplug_exit();
- kset_unregister(pci_hotplug_slots_kset);
}
module_init(pci_hotplug_init);
@@ -739,8 +553,3 @@ MODULE_DESCRIPTION(DRIVER_DESC);
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 ca656b27a50..8e9012dca45 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -36,20 +36,19 @@
#include <linux/sched.h> /* signal_pending() */
#include <linux/pcieport_if.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
#define MY_NAME "pciehp"
-extern int pciehp_poll_mode;
+extern bool pciehp_poll_mode;
extern int pciehp_poll_time;
-extern int pciehp_debug;
-extern int pciehp_force;
-extern struct workqueue_struct *pciehp_wq;
+extern bool pciehp_debug;
#define dbg(format, arg...) \
- do { \
- if (pciehp_debug) \
- printk("%s: " format, MY_NAME , ## arg); \
- } while (0)
+do { \
+ if (pciehp_debug) \
+ printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); \
+} while (0)
#define err(format, arg...) \
printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) \
@@ -57,22 +56,28 @@ extern struct workqueue_struct *pciehp_wq;
#define warn(format, arg...) \
printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
+#define ctrl_dbg(ctrl, format, arg...) \
+ do { \
+ if (pciehp_debug) \
+ dev_printk(KERN_DEBUG, &ctrl->pcie->device, \
+ format, ## arg); \
+ } while (0)
+#define ctrl_err(ctrl, format, arg...) \
+ dev_err(&ctrl->pcie->device, format, ## arg)
+#define ctrl_info(ctrl, format, arg...) \
+ dev_info(&ctrl->pcie->device, format, ## arg)
+#define ctrl_warn(ctrl, format, arg...) \
+ dev_warn(&ctrl->pcie->device, format, ## arg)
+
#define SLOT_NAME_SIZE 10
struct slot {
- u8 bus;
- u8 device;
- u32 number;
u8 state;
- struct timer_list task_event;
- u8 hp_slot;
struct controller *ctrl;
- struct hpc_ops *hpc_ops;
struct hotplug_slot *hotplug_slot;
- struct list_head slot_list;
- char name[SLOT_NAME_SIZE];
- unsigned long last_emi_toggle;
struct delayed_work work; /* work for button event */
struct mutex lock;
+ struct mutex hotplug_lock;
+ struct workqueue_struct *wq;
};
struct event_info {
@@ -82,22 +87,17 @@ struct event_info {
};
struct controller {
- struct mutex crit_sect; /* critical section mutex */
struct mutex ctrl_lock; /* controller lock */
- int num_slots; /* Number of slots on ctlr */
- int slot_num_inc; /* 1 or -1 */
- struct pci_dev *pci_dev;
- struct list_head slot_list;
- struct hpc_ops *hpc_ops;
+ struct pcie_device *pcie; /* PCI Express port service */
+ struct slot *slot;
wait_queue_head_t queue; /* sleep & wake process */
- u8 slot_device_offset;
- u32 first_slot; /* First physical slot number */ /* PCIE only has 1 slot */
- u8 slot_bus; /* Bus where the slots handled by this controller sit */
- u8 ctrlcap;
- u8 cap_base;
+ u32 slot_cap;
struct timer_list poll_timer;
- volatile int cmd_busy;
- spinlock_t lock;
+ unsigned int cmd_busy:1;
+ unsigned int no_cmd_complete:1;
+ unsigned int link_active_reporting:1;
+ unsigned int notification_enabled:1;
+ unsigned int power_fault_detected;
};
#define INT_BUTTON_IGNORE 0
@@ -110,6 +110,8 @@ struct controller {
#define INT_BUTTON_PRESS 7
#define INT_BUTTON_RELEASE 8
#define INT_BUTTON_CANCEL 9
+#define INT_LINK_UP 10
+#define INT_LINK_DOWN 11
#define STATIC_STATE 0
#define BLINKINGON_STATE 1
@@ -117,100 +119,68 @@ struct controller {
#define POWERON_STATE 3
#define POWEROFF_STATE 4
-/* Error messages */
-#define INTERLOCK_OPEN 0x00000002
-#define ADD_NOT_SUPPORTED 0x00000003
-#define CARD_FUNCTIONING 0x00000005
-#define ADAPTER_NOT_SAME 0x00000006
-#define NO_ADAPTER_PRESENT 0x00000009
-#define NOT_ENOUGH_RESOURCES 0x0000000B
-#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
-#define WRONG_BUS_FREQUENCY 0x0000000D
-#define POWER_FAILURE 0x0000000E
-
-/* Field definitions in Slot Capabilities Register */
-#define ATTN_BUTTN_PRSN 0x00000001
-#define PWR_CTRL_PRSN 0x00000002
-#define MRL_SENS_PRSN 0x00000004
-#define ATTN_LED_PRSN 0x00000008
-#define PWR_LED_PRSN 0x00000010
-#define HP_SUPR_RM_SUP 0x00000020
-#define EMI_PRSN 0x00020000
-
-#define ATTN_BUTTN(cap) (cap & ATTN_BUTTN_PRSN)
-#define POWER_CTRL(cap) (cap & PWR_CTRL_PRSN)
-#define MRL_SENS(cap) (cap & MRL_SENS_PRSN)
-#define ATTN_LED(cap) (cap & ATTN_LED_PRSN)
-#define PWR_LED(cap) (cap & PWR_LED_PRSN)
-#define HP_SUPR_RM(cap) (cap & HP_SUPR_RM_SUP)
-#define EMI(cap) (cap & EMI_PRSN)
-
-extern int pciehp_sysfs_enable_slot(struct slot *slot);
-extern int pciehp_sysfs_disable_slot(struct slot *slot);
-extern u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl);
-extern u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl);
-extern u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl);
-extern u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl);
-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);
+#define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP)
+#define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP)
+#define MRL_SENS(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_MRLSP)
+#define ATTN_LED(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_AIP)
+#define PWR_LED(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PIP)
+#define HP_SUPR_RM(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_HPS)
+#define EMI(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_EIP)
+#define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS)
+#define PSN(ctrl) (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19)
+
+int pciehp_sysfs_enable_slot(struct slot *slot);
+int pciehp_sysfs_disable_slot(struct slot *slot);
+u8 pciehp_handle_attention_button(struct slot *p_slot);
+u8 pciehp_handle_switch_change(struct slot *p_slot);
+u8 pciehp_handle_presence_change(struct slot *p_slot);
+u8 pciehp_handle_power_fault(struct slot *p_slot);
+void pciehp_handle_linkstate_change(struct slot *p_slot);
+int pciehp_configure_device(struct slot *p_slot);
+int pciehp_unconfigure_device(struct slot *p_slot);
+void pciehp_queue_pushbutton_work(struct work_struct *work);
+struct controller *pcie_init(struct pcie_device *dev);
+int pcie_init_notification(struct controller *ctrl);
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);
-
-static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device)
+void pcie_enable_notification(struct controller *ctrl);
+int pciehp_power_on_slot(struct slot *slot);
+void pciehp_power_off_slot(struct slot *slot);
+void pciehp_get_power_status(struct slot *slot, u8 *status);
+void pciehp_get_attention_status(struct slot *slot, u8 *status);
+
+void pciehp_set_attention_status(struct slot *slot, u8 status);
+void pciehp_get_latch_status(struct slot *slot, u8 *status);
+void pciehp_get_adapter_status(struct slot *slot, u8 *status);
+int pciehp_query_power_fault(struct slot *slot);
+void pciehp_green_led_on(struct slot *slot);
+void pciehp_green_led_off(struct slot *slot);
+void pciehp_green_led_blink(struct slot *slot);
+int pciehp_check_link_status(struct controller *ctrl);
+bool pciehp_check_link_active(struct controller *ctrl);
+void pciehp_release_ctrl(struct controller *ctrl);
+int pciehp_reset_slot(struct slot *slot, int probe);
+
+static inline const char *slot_name(struct slot *slot)
{
- struct slot *slot;
-
- list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
- if (slot->device == device)
- return slot;
- }
-
- err("%s: slot (device=0x%x) not found\n", __FUNCTION__, device);
- return NULL;
+ return hotplug_slot_name(slot->hotplug_slot);
}
-struct hpc_ops {
- int (*power_on_slot)(struct slot *slot);
- int (*power_off_slot)(struct slot *slot);
- int (*get_power_status)(struct slot *slot, u8 *status);
- int (*get_attention_status)(struct slot *slot, u8 *status);
- int (*set_attention_status)(struct slot *slot, u8 status);
- int (*get_latch_status)(struct slot *slot, u8 *status);
- int (*get_adapter_status)(struct slot *slot, u8 *status);
- int (*get_emi_status)(struct slot *slot, u8 *status);
- int (*toggle_emi)(struct slot *slot);
- int (*get_max_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
- int (*get_cur_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
- int (*get_max_lnk_width)(struct slot *slot, enum pcie_link_width *val);
- int (*get_cur_lnk_width)(struct slot *slot, enum pcie_link_width *val);
- int (*query_power_fault)(struct slot *slot);
- void (*green_led_on)(struct slot *slot);
- void (*green_led_off)(struct slot *slot);
- void (*green_led_blink)(struct slot *slot);
- void (*release_ctlr)(struct controller *ctrl);
- int (*check_lnk_status)(struct controller *ctrl);
-};
-
#ifdef CONFIG_ACPI
-#include <acpi/acpi.h>
-#include <acpi/acpi_bus.h>
-#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_params_from_firmware(struct pci_dev *dev,
- struct hotplug_params *hpp)
+void __init pciehp_acpi_slot_detection_init(void);
+int pciehp_acpi_slot_detection_check(struct pci_dev *dev);
+
+static inline void pciehp_firmware_init(void)
{
- if (ACPI_FAILURE(acpi_get_hp_params_from_firmware(dev->bus, hpp)))
- return -ENODEV;
- return 0;
+ pciehp_acpi_slot_detection_init();
}
#else
-#define pciehp_get_hp_hw_control_from_firmware(dev) 0
-#define pciehp_get_hp_params_from_firmware(dev, hpp) (-ENODEV)
-#endif /* CONFIG_ACPI */
+#define pciehp_firmware_init() do {} while (0)
+static inline int pciehp_acpi_slot_detection_check(struct pci_dev *dev)
+{
+ return 0;
+}
+#endif /* CONFIG_ACPI */
#endif /* _PCIEHP_H */
diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c
new file mode 100644
index 00000000000..93cc9266e8c
--- /dev/null
+++ b/drivers/pci/hotplug/pciehp_acpi.c
@@ -0,0 +1,137 @@
+/*
+ * ACPI related functions for PCI Express Hot Plug driver.
+ *
+ * Copyright (C) 2008 Kenji Kaneshige
+ * Copyright (C) 2008 Fujitsu Limited.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include "pciehp.h"
+
+#define PCIEHP_DETECT_PCIE (0)
+#define PCIEHP_DETECT_ACPI (1)
+#define PCIEHP_DETECT_AUTO (2)
+#define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_AUTO
+
+struct dummy_slot {
+ u32 number;
+ struct list_head list;
+};
+
+static int slot_detection_mode;
+static char *pciehp_detect_mode;
+module_param(pciehp_detect_mode, charp, 0444);
+MODULE_PARM_DESC(pciehp_detect_mode,
+ "Slot detection mode: pcie, acpi, auto\n"
+ " pcie - Use PCIe based slot detection\n"
+ " acpi - Use ACPI for slot detection\n"
+ " auto(default) - Auto select mode. Use acpi option if duplicate\n"
+ " slot ids are found. Otherwise, use pcie option\n");
+
+int pciehp_acpi_slot_detection_check(struct pci_dev *dev)
+{
+ if (slot_detection_mode != PCIEHP_DETECT_ACPI)
+ return 0;
+ if (acpi_pci_detect_ejectable(ACPI_HANDLE(&dev->dev)))
+ return 0;
+ return -ENODEV;
+}
+
+static int __init parse_detect_mode(void)
+{
+ if (!pciehp_detect_mode)
+ return PCIEHP_DETECT_DEFAULT;
+ if (!strcmp(pciehp_detect_mode, "pcie"))
+ return PCIEHP_DETECT_PCIE;
+ if (!strcmp(pciehp_detect_mode, "acpi"))
+ return PCIEHP_DETECT_ACPI;
+ if (!strcmp(pciehp_detect_mode, "auto"))
+ return PCIEHP_DETECT_AUTO;
+ warn("bad specifier '%s' for pciehp_detect_mode. Use default\n",
+ pciehp_detect_mode);
+ return PCIEHP_DETECT_DEFAULT;
+}
+
+static int __initdata dup_slot_id;
+static int __initdata acpi_slot_detected;
+static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots);
+
+/* Dummy driver for duplicate name detection */
+static int __init dummy_probe(struct pcie_device *dev)
+{
+ u32 slot_cap;
+ acpi_handle handle;
+ struct dummy_slot *slot, *tmp;
+ struct pci_dev *pdev = dev->port;
+
+ pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot)
+ return -ENOMEM;
+ slot->number = (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19;
+ list_for_each_entry(tmp, &dummy_slots, list) {
+ if (tmp->number == slot->number)
+ dup_slot_id++;
+ }
+ list_add_tail(&slot->list, &dummy_slots);
+ handle = ACPI_HANDLE(&pdev->dev);
+ if (!acpi_slot_detected && acpi_pci_detect_ejectable(handle))
+ acpi_slot_detected = 1;
+ return -ENODEV; /* dummy driver always returns error */
+}
+
+static struct pcie_port_service_driver __initdata dummy_driver = {
+ .name = "pciehp_dummy",
+ .port_type = PCIE_ANY_PORT,
+ .service = PCIE_PORT_SERVICE_HP,
+ .probe = dummy_probe,
+};
+
+static int __init select_detection_mode(void)
+{
+ struct dummy_slot *slot, *tmp;
+
+ if (pcie_port_service_register(&dummy_driver))
+ return PCIEHP_DETECT_ACPI;
+ pcie_port_service_unregister(&dummy_driver);
+ list_for_each_entry_safe(slot, tmp, &dummy_slots, list) {
+ list_del(&slot->list);
+ kfree(slot);
+ }
+ if (acpi_slot_detected && dup_slot_id)
+ return PCIEHP_DETECT_ACPI;
+ return PCIEHP_DETECT_PCIE;
+}
+
+void __init pciehp_acpi_slot_detection_init(void)
+{
+ slot_detection_mode = parse_detect_mode();
+ if (slot_detection_mode != PCIEHP_DETECT_AUTO)
+ goto out;
+ slot_detection_mode = select_detection_mode();
+out:
+ if (slot_detection_mode == PCIEHP_DETECT_ACPI)
+ info("Using ACPI for slot detection.\n");
+}
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 5fa4ba0d993..a2297db8081 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -30,6 +30,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pci.h>
#include "pciehp.h"
@@ -37,11 +38,10 @@
#include <linux/time.h>
/* Global variables */
-int pciehp_debug;
-int pciehp_poll_mode;
+bool pciehp_debug;
+bool pciehp_poll_mode;
int pciehp_poll_time;
-int pciehp_force;
-struct workqueue_struct *pciehp_wq;
+static bool pciehp_force;
#define DRIVER_VERSION "0.4"
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
@@ -58,7 +58,7 @@ module_param(pciehp_force, 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_force, "Force pciehp, even if OSHP is missing");
#define PCIE_MODULE_NAME "pciehp"
@@ -69,112 +69,7 @@ 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);
-
-static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
- .owner = THIS_MODULE,
- .set_attention_status = set_attention_status,
- .enable_slot = enable_slot,
- .disable_slot = disable_slot,
- .get_power_status = get_power_status,
- .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,
-};
-
-/*
- * Check the status of the Electro Mechanical Interlock (EMI)
- */
-static int get_lock_status(struct hotplug_slot *hotplug_slot, u8 *value)
-{
- struct slot *slot = hotplug_slot->private;
- return (slot->hpc_ops->get_emi_status(slot, value));
-}
-
-/*
- * sysfs interface for the Electro Mechanical Interlock (EMI)
- * 1 == locked, 0 == unlocked
- */
-static ssize_t lock_read_file(struct hotplug_slot *slot, char *buf)
-{
- int retval;
- u8 value;
-
- retval = get_lock_status(slot, &value);
- if (retval)
- goto lock_read_exit;
- retval = sprintf (buf, "%d\n", value);
-
-lock_read_exit:
- return retval;
-}
-
-/*
- * Change the status of the Electro Mechanical Interlock (EMI)
- * This is a toggle - in addition there must be at least 1 second
- * in between toggles.
- */
-static int set_lock_status(struct hotplug_slot *hotplug_slot, u8 status)
-{
- struct slot *slot = hotplug_slot->private;
- int retval;
- u8 value;
-
- mutex_lock(&slot->ctrl->crit_sect);
-
- /* has it been >1 sec since our last toggle? */
- if ((get_seconds() - slot->last_emi_toggle) < 1)
- return -EINVAL;
-
- /* see what our current state is */
- retval = get_lock_status(hotplug_slot, &value);
- if (retval || (value == status))
- goto set_lock_exit;
-
- slot->hpc_ops->toggle_emi(slot);
-set_lock_exit:
- mutex_unlock(&slot->ctrl->crit_sect);
- return 0;
-}
-
-/*
- * sysfs interface which allows the user to toggle the Electro Mechanical
- * Interlock. Valid values are either 0 or 1. 0 == unlock, 1 == lock
- */
-static ssize_t lock_write_file(struct hotplug_slot *slot, const char *buf,
- size_t count)
-{
- unsigned long llock;
- u8 lock;
- int retval = 0;
-
- llock = simple_strtoul(buf, NULL, 10);
- lock = (u8)(llock & 0xff);
-
- switch (lock) {
- case 0:
- case 1:
- retval = set_lock_status(slot, lock);
- break;
- default:
- err ("%d is an invalid lock value\n", lock);
- retval = -EINVAL;
- }
- if (retval)
- return retval;
- return count;
-}
-
-static struct hotplug_slot_attribute hotplug_slot_attr_lock = {
- .attr = {.name = "lock", .mode = S_IFREG | S_IRUGO | S_IWUSR},
- .show = lock_read_file,
- .store = lock_write_file
-};
+static int reset_slot (struct hotplug_slot *slot, int probe);
/**
* release_slot - free up the memory used by a slot
@@ -184,114 +79,76 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, hotplug_slot_name(hotplug_slot));
- kfree(slot->hotplug_slot->info);
- kfree(slot->hotplug_slot);
- kfree(slot);
-}
-
-static void make_slot_name(struct slot *slot)
-{
- snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%04d_%04d",
- slot->bus, slot->number);
+ kfree(hotplug_slot->ops);
+ kfree(hotplug_slot->info);
+ kfree(hotplug_slot);
}
-static int init_slots(struct controller *ctrl)
+static int init_slot(struct controller *ctrl)
{
- struct slot *slot;
- struct hotplug_slot *hotplug_slot;
- struct hotplug_slot_info *info;
+ struct slot *slot = ctrl->slot;
+ struct hotplug_slot *hotplug = NULL;
+ struct hotplug_slot_info *info = NULL;
+ struct hotplug_slot_ops *ops = NULL;
+ char name[SLOT_NAME_SIZE];
int retval = -ENOMEM;
- int i;
-
- for (i = 0; i < ctrl->num_slots; i++) {
- slot = kzalloc(sizeof(*slot), GFP_KERNEL);
- if (!slot)
- goto error;
-
- hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
- if (!hotplug_slot)
- goto error_slot;
- slot->hotplug_slot = hotplug_slot;
-
- 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->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);
-
- 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);
- if (retval) {
- err ("pci_hp_register failed with error %d\n", retval);
- goto error_info;
- }
- /* create additional sysfs entries */
- if (EMI(ctrl->ctrlcap)) {
- retval = sysfs_create_file(&hotplug_slot->kobj,
- &hotplug_slot_attr_lock.attr);
- if (retval) {
- pci_hp_deregister(hotplug_slot);
- err("cannot create additional sysfs entries\n");
- goto error_info;
- }
- }
-
- list_add(&slot->slot_list, &ctrl->slot_list);
+
+ hotplug = kzalloc(sizeof(*hotplug), GFP_KERNEL);
+ if (!hotplug)
+ goto out;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ goto out;
+
+ /* Setup hotplug slot ops */
+ ops = kzalloc(sizeof(*ops), GFP_KERNEL);
+ if (!ops)
+ goto out;
+
+ ops->enable_slot = enable_slot;
+ ops->disable_slot = disable_slot;
+ ops->get_power_status = get_power_status;
+ ops->get_adapter_status = get_adapter_status;
+ ops->reset_slot = reset_slot;
+ if (MRL_SENS(ctrl))
+ ops->get_latch_status = get_latch_status;
+ if (ATTN_LED(ctrl)) {
+ ops->get_attention_status = get_attention_status;
+ ops->set_attention_status = set_attention_status;
}
- return 0;
-error_info:
- kfree(info);
-error_hpslot:
- kfree(hotplug_slot);
-error_slot:
- kfree(slot);
-error:
+ /* register this slot with the hotplug pci core */
+ hotplug->info = info;
+ hotplug->private = slot;
+ hotplug->release = &release_slot;
+ hotplug->ops = ops;
+ slot->hotplug_slot = hotplug;
+ snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
+
+ ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:00 sun=%x\n",
+ pci_domain_nr(ctrl->pcie->port->subordinate),
+ ctrl->pcie->port->subordinate->number, PSN(ctrl));
+ retval = pci_hp_register(hotplug,
+ ctrl->pcie->port->subordinate, 0, name);
+ if (retval)
+ ctrl_err(ctrl,
+ "pci_hp_register failed with error %d\n", retval);
+out:
+ if (retval) {
+ kfree(ops);
+ kfree(info);
+ kfree(hotplug);
+ }
return retval;
}
-static void cleanup_slots(struct controller *ctrl)
+static void cleanup_slot(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);
- if (EMI(ctrl->ctrlcap))
- sysfs_remove_file(&slot->hotplug_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);
- }
+ pci_hp_deregister(ctrl->slot->hotplug_slot);
}
/*
@@ -301,13 +158,10 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- hotplug_slot->info->attention_status = status;
-
- if (ATTN_LED(slot->ctrl->ctrlcap))
- slot->hpc_ops->set_attention_status(slot, status);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
+ pciehp_set_attention_status(slot, status);
return 0;
}
@@ -316,7 +170,8 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
return pciehp_sysfs_enable_slot(slot);
}
@@ -326,7 +181,8 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
return pciehp_sysfs_disable_slot(slot);
}
@@ -334,217 +190,161 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
- int retval;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
- retval = slot->hpc_ops->get_power_status(slot, value);
- if (retval < 0)
- *value = hotplug_slot->info->power_status;
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
+ pciehp_get_power_status(slot, value);
return 0;
}
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
- int retval;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- retval = slot->hpc_ops->get_attention_status(slot, value);
- if (retval < 0)
- *value = hotplug_slot->info->attention_status;
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
+ pciehp_get_attention_status(slot, value);
return 0;
}
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
- int retval;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
- retval = slot->hpc_ops->get_latch_status(slot, value);
- if (retval < 0)
- *value = hotplug_slot->info->latch_status;
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
+ pciehp_get_latch_status(slot, value);
return 0;
}
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
- int retval;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
- retval = slot->hpc_ops->get_adapter_status(slot, value);
- if (retval < 0)
- *value = hotplug_slot->info->adapter_status;
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
+ pciehp_get_adapter_status(slot, value);
return 0;
}
-static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
+static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
{
struct slot *slot = hotplug_slot->private;
- struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
- *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)
-{
- struct slot *slot = hotplug_slot->private;
- int retval;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- retval = slot->hpc_ops->get_max_bus_speed(slot, value);
- if (retval < 0)
- *value = PCI_SPEED_UNKNOWN;
-
- return 0;
-}
-
-static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
-{
- struct slot *slot = hotplug_slot->private;
- int retval;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- retval = slot->hpc_ops->get_cur_bus_speed(slot, value);
- if (retval < 0)
- *value = PCI_SPEED_UNKNOWN;
-
- return 0;
+ return pciehp_reset_slot(slot, probe);
}
-static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_id *id)
+static int pciehp_probe(struct pcie_device *dev)
{
int rc;
struct controller *ctrl;
- struct slot *t_slot;
- u8 value;
- struct pci_dev *pdev;
+ struct slot *slot;
+ u8 occupied, poweron;
- ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
- if (!ctrl) {
- err("%s : out of memory\n", __FUNCTION__);
+ if (pciehp_force)
+ dev_info(&dev->device,
+ "Bypassing BIOS check for pciehp use on %s\n",
+ pci_name(dev->port));
+ else if (pciehp_acpi_slot_detection_check(dev->port))
goto err_out_none;
- }
- INIT_LIST_HEAD(&ctrl->slot_list);
- pdev = dev->port;
- ctrl->pci_dev = pdev;
-
- rc = pcie_init(ctrl, dev);
- if (rc) {
- dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME);
- goto err_out_free_ctrl;
+ ctrl = pcie_init(dev);
+ if (!ctrl) {
+ dev_err(&dev->device, "Controller initialization failed\n");
+ goto err_out_none;
}
-
- pci_set_drvdata(pdev, ctrl);
-
- dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n",
- __FUNCTION__, 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);
+ rc = init_slot(ctrl);
if (rc) {
- err("%s: slot initialization failed\n", PCIE_MODULE_NAME);
+ if (rc == -EBUSY)
+ ctrl_warn(ctrl, "Slot already registered by another hotplug driver\n");
+ else
+ ctrl_err(ctrl, "Slot initialization failed\n");
goto err_out_release_ctlr;
}
- t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
-
- t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */
- if (value && pciehp_force) {
- rc = pciehp_enable_slot(t_slot);
- if (rc) /* -ENODEV: shouldn't happen, but deal with it */
- value = 0;
+ /* Enable events after we have setup the data structures */
+ rc = pcie_init_notification(ctrl);
+ if (rc) {
+ ctrl_err(ctrl, "Notification initialization failed\n");
+ goto err_out_free_ctrl_slot;
}
- if ((POWER_CTRL(ctrl->ctrlcap)) && !value) {
- rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/
- if (rc)
- goto err_out_free_ctrl_slot;
+
+ /* Check if slot is occupied */
+ slot = ctrl->slot;
+ pciehp_get_adapter_status(slot, &occupied);
+ pciehp_get_power_status(slot, &poweron);
+ if (occupied && pciehp_force) {
+ mutex_lock(&slot->hotplug_lock);
+ pciehp_enable_slot(slot);
+ mutex_unlock(&slot->hotplug_lock);
}
+ /* If empty slot's power status is on, turn power off */
+ if (!occupied && poweron && POWER_CTRL(ctrl))
+ pciehp_power_off_slot(slot);
return 0;
err_out_free_ctrl_slot:
- cleanup_slots(ctrl);
+ cleanup_slot(ctrl);
err_out_release_ctlr:
- ctrl->hpc_ops->release_ctlr(ctrl);
-err_out_free_ctrl:
- kfree(ctrl);
+ pciehp_release_ctrl(ctrl);
err_out_none:
return -ENODEV;
}
-static void pciehp_remove (struct pcie_device *dev)
+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);
+ cleanup_slot(ctrl);
+ pciehp_release_ctrl(ctrl);
}
#ifdef CONFIG_PM
-static int pciehp_suspend (struct pcie_device *dev, pm_message_t state)
+static int pciehp_suspend(struct pcie_device *dev)
{
- printk("%s ENTRY\n", __FUNCTION__);
return 0;
}
-static int pciehp_resume (struct pcie_device *dev)
+static int pciehp_resume(struct pcie_device *dev)
{
- printk("%s ENTRY\n", __FUNCTION__);
- if (pciehp_force) {
- struct pci_dev *pdev = dev->port;
- struct controller *ctrl = pci_get_drvdata(pdev);
- struct slot *t_slot;
- u8 status;
-
- /* reinitialize the chipset's event detection logic */
- pcie_init_hardware_part2(ctrl, dev);
-
- t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
-
- /* Check if slot is occupied */
- t_slot->hpc_ops->get_adapter_status(t_slot, &status);
- if (status)
- pciehp_enable_slot(t_slot);
- else
- pciehp_disable_slot(t_slot);
- }
+ struct controller *ctrl;
+ struct slot *slot;
+ u8 status;
+
+ ctrl = get_service_data(dev);
+
+ /* reinitialize the chipset's event detection logic */
+ pcie_enable_notification(ctrl);
+
+ slot = ctrl->slot;
+
+ /* Check if slot is occupied */
+ pciehp_get_adapter_status(slot, &status);
+ mutex_lock(&slot->hotplug_lock);
+ if (status)
+ pciehp_enable_slot(slot);
+ else
+ pciehp_disable_slot(slot);
+ mutex_unlock(&slot->hotplug_lock);
return 0;
}
-#endif
-
-static struct pcie_port_service_id port_pci_ids[] = { {
- .vendor = PCI_ANY_ID,
- .device = PCI_ANY_ID,
- .port_type = PCIE_ANY_PORT,
- .service_type = PCIE_PORT_SERVICE_HP,
- .driver_data = 0,
- }, { /* end: all zeroes */ }
-};
-static const char device_name[] = "hpdriver";
+#endif /* PM */
static struct pcie_port_service_driver hpdriver_portdrv = {
- .name = (char *)device_name,
- .id_table = &port_pci_ids[0],
+ .name = PCIE_MODULE_NAME,
+ .port_type = PCIE_ANY_PORT,
+ .service = PCIE_PORT_SERVICE_HP,
.probe = pciehp_probe,
.remove = pciehp_remove,
@@ -559,11 +359,13 @@ static int __init pcied_init(void)
{
int retval = 0;
+ pciehp_firmware_init();
retval = pcie_port_service_register(&hpdriver_portdrv);
- dbg("pcie_port_service_register = %d\n", retval);
- info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
- if (retval)
- dbg("%s: Failure to register service\n", __FUNCTION__);
+ dbg("pcie_port_service_register = %d\n", retval);
+ info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+ if (retval)
+ dbg("Failure to register service\n");
+
return retval;
}
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index b23061c5611..ff32e85e1de 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -30,9 +30,8 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
-#include <linux/smp_lock.h>
+#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/workqueue.h>
#include "../pci.h"
#include "pciehp.h"
@@ -50,25 +49,23 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
info->p_slot = p_slot;
INIT_WORK(&info->work, interrupt_event_handler);
- schedule_work(&info->work);
+ queue_work(p_slot->wq, &info->work);
return 0;
}
-u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
+u8 pciehp_handle_attention_button(struct slot *p_slot)
{
- struct slot *p_slot;
u32 event_type;
+ struct controller *ctrl = p_slot->ctrl;
/* Attention Button Change */
- dbg("pciehp: Attention button interrupt received.\n");
-
- p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+ ctrl_dbg(ctrl, "Attention button interrupt received\n");
/*
* Button pressed - See if need to TAKE ACTION!!!
*/
- info("Button pressed on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot));
event_type = INT_BUTTON_PRESS;
queue_interrupt_event(p_slot, event_type);
@@ -76,29 +73,27 @@ u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
return 0;
}
-u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
+u8 pciehp_handle_switch_change(struct slot *p_slot)
{
- struct slot *p_slot;
u8 getstatus;
u32 event_type;
+ struct controller *ctrl = p_slot->ctrl;
/* Switch Change */
- dbg("pciehp: Switch interrupt received.\n");
-
- p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
- p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+ ctrl_dbg(ctrl, "Switch interrupt received\n");
+ pciehp_get_latch_status(p_slot, &getstatus);
if (getstatus) {
/*
* Switch opened
*/
- info("Latch open on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot));
event_type = INT_SWITCH_OPEN;
} else {
/*
* Switch closed
*/
- info("Latch close on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot));
event_type = INT_SWITCH_CLOSE;
}
@@ -107,32 +102,31 @@ u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
return 1;
}
-u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
+u8 pciehp_handle_presence_change(struct slot *p_slot)
{
- struct slot *p_slot;
u32 event_type;
u8 presence_save;
+ struct controller *ctrl = p_slot->ctrl;
/* Presence Change */
- dbg("pciehp: Presence/Notify input change.\n");
-
- p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+ ctrl_dbg(ctrl, "Presence/Notify input change\n");
/* Switch is open, assume a presence change
* Save the presence state
*/
- p_slot->hpc_ops->get_adapter_status(p_slot, &presence_save);
+ pciehp_get_adapter_status(p_slot, &presence_save);
if (presence_save) {
/*
* Card Present
*/
- info("Card present on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Card present on Slot(%s)\n", slot_name(p_slot));
event_type = INT_PRESENCE_ON;
} else {
/*
* Not Present
*/
- info("Card not present on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Card not present on Slot(%s)\n",
+ slot_name(p_slot));
event_type = INT_PRESENCE_OFF;
}
@@ -141,61 +135,62 @@ u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
return 1;
}
-u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
+u8 pciehp_handle_power_fault(struct slot *p_slot)
{
- struct slot *p_slot;
u32 event_type;
+ struct controller *ctrl = p_slot->ctrl;
/* power fault */
- dbg("pciehp: Power fault interrupt received.\n");
+ ctrl_dbg(ctrl, "Power fault interrupt received\n");
+ ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
+ event_type = INT_POWER_FAULT;
+ ctrl_info(ctrl, "Power fault bit %x set\n", 0);
+ queue_interrupt_event(p_slot, event_type);
- p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+ return 1;
+}
- if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
- /*
- * power fault Cleared
- */
- info("Power fault cleared on Slot(%s)\n", p_slot->name);
- event_type = INT_POWER_FAULT_CLEAR;
+void pciehp_handle_linkstate_change(struct slot *p_slot)
+{
+ u32 event_type;
+ struct controller *ctrl = p_slot->ctrl;
+
+ /* Link Status Change */
+ ctrl_dbg(ctrl, "Data Link Layer State change\n");
+
+ if (pciehp_check_link_active(ctrl)) {
+ ctrl_info(ctrl, "slot(%s): Link Up event\n",
+ slot_name(p_slot));
+ event_type = INT_LINK_UP;
} else {
- /*
- * power fault
- */
- info("Power fault on Slot(%s)\n", p_slot->name);
- event_type = INT_POWER_FAULT;
- info("power fault bit %x set\n", hp_slot);
+ ctrl_info(ctrl, "slot(%s): Link Down event\n",
+ slot_name(p_slot));
+ event_type = INT_LINK_DOWN;
}
queue_interrupt_event(p_slot, event_type);
-
- return 1;
}
/* The following routines constitute the bulk of the
hotplug controller logic
*/
-static void set_slot_off(struct controller *ctrl, struct slot * pslot)
+static void set_slot_off(struct controller *ctrl, struct slot *pslot)
{
/* turn off slot, turn on Amber LED, turn off Green LED if supported*/
- if (POWER_CTRL(ctrl->ctrlcap)) {
- if (pslot->hpc_ops->power_off_slot(pslot)) {
- err("%s: Issue of Slot Power Off command failed\n",
- __FUNCTION__);
- return;
- }
- }
-
- if (PWR_LED(ctrl->ctrlcap))
- pslot->hpc_ops->green_led_off(pslot);
+ if (POWER_CTRL(ctrl)) {
+ pciehp_power_off_slot(pslot);
- if (ATTN_LED(ctrl->ctrlcap)) {
- if (pslot->hpc_ops->set_attention_status(pslot, 1)) {
- err("%s: Issue of Set Attention Led command failed\n",
- __FUNCTION__);
- return;
- }
+ /*
+ * After turning power off, we must wait for at least 1 second
+ * before taking any action that relies on power having been
+ * removed from the slot/adapter.
+ */
+ msleep(1000);
}
+
+ pciehp_green_led_off(pslot);
+ pciehp_set_attention_status(pslot, 1);
}
/**
@@ -209,54 +204,40 @@ static int board_added(struct slot *p_slot)
{
int retval = 0;
struct controller *ctrl = p_slot->ctrl;
+ struct pci_bus *parent = ctrl->pcie->port->subordinate;
- dbg("%s: slot device, slot offset, hp slot = %d, %d ,%d\n",
- __FUNCTION__, p_slot->device,
- ctrl->slot_device_offset, p_slot->hp_slot);
-
- if (POWER_CTRL(ctrl->ctrlcap)) {
+ if (POWER_CTRL(ctrl)) {
/* Power on slot */
- retval = p_slot->hpc_ops->power_on_slot(p_slot);
+ retval = pciehp_power_on_slot(p_slot);
if (retval)
return retval;
}
- if (PWR_LED(ctrl->ctrlcap))
- p_slot->hpc_ops->green_led_blink(p_slot);
-
- /* Wait for ~1 second */
- msleep(1000);
+ pciehp_green_led_blink(p_slot);
/* Check link training status */
- retval = p_slot->hpc_ops->check_lnk_status(ctrl);
+ retval = pciehp_check_link_status(ctrl);
if (retval) {
- err("%s: Failed to check link status\n", __FUNCTION__);
- set_slot_off(ctrl, p_slot);
- return retval;
+ ctrl_err(ctrl, "Failed to check link status\n");
+ goto err_exit;
}
/* Check for a power fault */
- if (p_slot->hpc_ops->query_power_fault(p_slot)) {
- dbg("%s: power fault detected\n", __FUNCTION__);
- retval = POWER_FAILURE;
+ if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
+ ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
+ retval = -EIO;
goto err_exit;
}
retval = pciehp_configure_device(p_slot);
if (retval) {
- err("Cannot add device 0x%x:%x\n", p_slot->bus,
- p_slot->device);
- goto err_exit;
+ ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
+ pci_domain_nr(parent), parent->number);
+ if (retval != -EEXIST)
+ goto err_exit;
}
- /*
- * Some PCI Express root ports require fixup after hot-plug operation.
- */
- if (pcie_mch_quirk)
- pci_fixup_device(pci_fixup_final, ctrl->pci_dev);
- if (PWR_LED(ctrl->ctrlcap))
- p_slot->hpc_ops->green_led_on(p_slot);
-
+ pciehp_green_led_on(p_slot);
return 0;
err_exit:
@@ -270,35 +251,35 @@ err_exit:
*/
static int remove_board(struct slot *p_slot)
{
- int retval = 0;
+ int retval;
struct controller *ctrl = p_slot->ctrl;
retval = pciehp_unconfigure_device(p_slot);
if (retval)
return retval;
- dbg("In %s, hp_slot = %d\n", __FUNCTION__, p_slot->hp_slot);
+ if (POWER_CTRL(ctrl)) {
+ pciehp_power_off_slot(p_slot);
- if (POWER_CTRL(ctrl->ctrlcap)) {
- /* power off slot */
- retval = p_slot->hpc_ops->power_off_slot(p_slot);
- if (retval) {
- err("%s: Issue of Slot Disable command failed\n",
- __FUNCTION__);
- return retval;
- }
+ /*
+ * After turning power off, we must wait for at least 1 second
+ * before taking any action that relies on power having been
+ * removed from the slot/adapter.
+ */
+ msleep(1000);
}
- if (PWR_LED(ctrl->ctrlcap))
- /* turn off Green LED */
- p_slot->hpc_ops->green_led_off(p_slot);
-
+ /* turn off Green LED */
+ pciehp_green_led_off(p_slot);
return 0;
}
struct power_work_info {
struct slot *p_slot;
struct work_struct work;
+ unsigned int req;
+#define DISABLE_REQ 0
+#define ENABLE_REQ 1
};
/**
@@ -313,29 +294,38 @@ static void pciehp_power_thread(struct work_struct *work)
struct power_work_info *info =
container_of(work, struct power_work_info, work);
struct slot *p_slot = info->p_slot;
-
- mutex_lock(&p_slot->lock);
- switch (p_slot->state) {
- case POWEROFF_STATE:
- mutex_unlock(&p_slot->lock);
- dbg("%s: disabling bus:device(%x:%x)\n",
- __FUNCTION__, p_slot->bus, p_slot->device);
+ int ret;
+
+ switch (info->req) {
+ case DISABLE_REQ:
+ ctrl_dbg(p_slot->ctrl,
+ "Disabling domain:bus:device=%04x:%02x:00\n",
+ pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
+ p_slot->ctrl->pcie->port->subordinate->number);
+ mutex_lock(&p_slot->hotplug_lock);
pciehp_disable_slot(p_slot);
+ mutex_unlock(&p_slot->hotplug_lock);
mutex_lock(&p_slot->lock);
p_slot->state = STATIC_STATE;
- break;
- case POWERON_STATE:
mutex_unlock(&p_slot->lock);
- if (pciehp_enable_slot(p_slot) &&
- PWR_LED(p_slot->ctrl->ctrlcap))
- p_slot->hpc_ops->green_led_off(p_slot);
+ break;
+ case ENABLE_REQ:
+ ctrl_dbg(p_slot->ctrl,
+ "Enabling domain:bus:device=%04x:%02x:00\n",
+ pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
+ p_slot->ctrl->pcie->port->subordinate->number);
+ mutex_lock(&p_slot->hotplug_lock);
+ ret = pciehp_enable_slot(p_slot);
+ mutex_unlock(&p_slot->hotplug_lock);
+ if (ret)
+ pciehp_green_led_off(p_slot);
mutex_lock(&p_slot->lock);
p_slot->state = STATIC_STATE;
+ mutex_unlock(&p_slot->lock);
break;
default:
break;
}
- mutex_unlock(&p_slot->lock);
kfree(info);
}
@@ -347,7 +337,8 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
- err("%s: Cannot allocate memory\n", __FUNCTION__);
+ ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
+ __func__);
return;
}
info->p_slot = p_slot;
@@ -357,37 +348,21 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
switch (p_slot->state) {
case BLINKINGOFF_STATE:
p_slot->state = POWEROFF_STATE;
+ info->req = DISABLE_REQ;
break;
case BLINKINGON_STATE:
p_slot->state = POWERON_STATE;
+ info->req = ENABLE_REQ;
break;
default:
+ kfree(info);
goto out;
}
- queue_work(pciehp_wq, &info->work);
+ queue_work(p_slot->wq, &info->work);
out:
mutex_unlock(&p_slot->lock);
}
-static int update_slot_info(struct slot *slot)
-{
- struct hotplug_slot_info *info;
- int result;
-
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- slot->hpc_ops->get_power_status(slot, &(info->power_status));
- slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
- slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
- slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
-
- result = pci_hp_change_slot_info(slot->hotplug_slot, info);
- kfree (info);
- return result;
-}
-
/*
* Note: This function must be called with slot->lock held
*/
@@ -398,23 +373,20 @@ static void handle_button_press_event(struct slot *p_slot)
switch (p_slot->state) {
case STATIC_STATE:
- p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+ pciehp_get_power_status(p_slot, &getstatus);
if (getstatus) {
p_slot->state = BLINKINGOFF_STATE;
- info("PCI slot #%s - powering off due to button "
- "press.\n", p_slot->name);
+ ctrl_info(ctrl, "PCI slot #%s - powering off due to button press\n",
+ slot_name(p_slot));
} else {
p_slot->state = BLINKINGON_STATE;
- info("PCI slot #%s - powering on due to button "
- "press.\n", p_slot->name);
+ ctrl_info(ctrl, "PCI slot #%s - powering on due to button press\n",
+ slot_name(p_slot));
}
/* blink green LED and turn off amber */
- if (PWR_LED(ctrl->ctrlcap))
- p_slot->hpc_ops->green_led_blink(p_slot);
- if (ATTN_LED(ctrl->ctrlcap))
- p_slot->hpc_ops->set_attention_status(p_slot, 0);
-
- schedule_delayed_work(&p_slot->work, 5*HZ);
+ pciehp_green_led_blink(p_slot);
+ pciehp_set_attention_status(p_slot, 0);
+ queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
break;
case BLINKINGOFF_STATE:
case BLINKINGON_STATE:
@@ -423,20 +395,15 @@ static void handle_button_press_event(struct slot *p_slot)
* press the attention again before the 5 sec. limit
* expires to cancel hot-add or hot-remove
*/
- info("Button cancel on Slot(%s)\n", p_slot->name);
- dbg("%s: button cancel\n", __FUNCTION__);
+ ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot));
cancel_delayed_work(&p_slot->work);
- if (p_slot->state == BLINKINGOFF_STATE) {
- if (PWR_LED(ctrl->ctrlcap))
- p_slot->hpc_ops->green_led_on(p_slot);
- } else {
- if (PWR_LED(ctrl->ctrlcap))
- p_slot->hpc_ops->green_led_off(p_slot);
- }
- if (ATTN_LED(ctrl->ctrlcap))
- p_slot->hpc_ops->set_attention_status(p_slot, 0);
- info("PCI slot #%s - action canceled due to button press\n",
- p_slot->name);
+ if (p_slot->state == BLINKINGOFF_STATE)
+ pciehp_green_led_on(p_slot);
+ else
+ pciehp_green_led_off(p_slot);
+ pciehp_set_attention_status(p_slot, 0);
+ ctrl_info(ctrl, "PCI slot #%s - action canceled due to button press\n",
+ slot_name(p_slot));
p_slot->state = STATIC_STATE;
break;
case POWEROFF_STATE:
@@ -446,11 +413,10 @@ static void handle_button_press_event(struct slot *p_slot)
* this means that the previous attention button action
* to hot-add or hot-remove is undergoing
*/
- info("Button ignore on Slot(%s)\n", p_slot->name);
- update_slot_info(p_slot);
+ ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot));
break;
default:
- warn("Not a valid state\n");
+ ctrl_warn(ctrl, "Not a valid state\n");
break;
}
}
@@ -465,19 +431,87 @@ static void handle_surprise_event(struct slot *p_slot)
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
- err("%s: Cannot allocate memory\n", __FUNCTION__);
+ ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
+ __func__);
return;
}
info->p_slot = p_slot;
INIT_WORK(&info->work, pciehp_power_thread);
- p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
- if (!getstatus)
+ pciehp_get_adapter_status(p_slot, &getstatus);
+ if (!getstatus) {
p_slot->state = POWEROFF_STATE;
- else
+ info->req = DISABLE_REQ;
+ } else {
p_slot->state = POWERON_STATE;
+ info->req = ENABLE_REQ;
+ }
- queue_work(pciehp_wq, &info->work);
+ queue_work(p_slot->wq, &info->work);
+}
+
+/*
+ * Note: This function must be called with slot->lock held
+ */
+static void handle_link_event(struct slot *p_slot, u32 event)
+{
+ struct controller *ctrl = p_slot->ctrl;
+ struct power_work_info *info;
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
+ __func__);
+ return;
+ }
+ info->p_slot = p_slot;
+ info->req = event == INT_LINK_UP ? ENABLE_REQ : DISABLE_REQ;
+ INIT_WORK(&info->work, pciehp_power_thread);
+
+ switch (p_slot->state) {
+ case BLINKINGON_STATE:
+ case BLINKINGOFF_STATE:
+ cancel_delayed_work(&p_slot->work);
+ /* Fall through */
+ case STATIC_STATE:
+ p_slot->state = event == INT_LINK_UP ?
+ POWERON_STATE : POWEROFF_STATE;
+ queue_work(p_slot->wq, &info->work);
+ break;
+ case POWERON_STATE:
+ if (event == INT_LINK_UP) {
+ ctrl_info(ctrl,
+ "Link Up event ignored on slot(%s): already powering on\n",
+ slot_name(p_slot));
+ kfree(info);
+ } else {
+ ctrl_info(ctrl,
+ "Link Down event queued on slot(%s): currently getting powered on\n",
+ slot_name(p_slot));
+ p_slot->state = POWEROFF_STATE;
+ queue_work(p_slot->wq, &info->work);
+ }
+ break;
+ case POWEROFF_STATE:
+ if (event == INT_LINK_UP) {
+ ctrl_info(ctrl,
+ "Link Up event queued on slot(%s): currently getting powered off\n",
+ slot_name(p_slot));
+ p_slot->state = POWERON_STATE;
+ queue_work(p_slot->wq, &info->work);
+ } else {
+ ctrl_info(ctrl,
+ "Link Down event ignored on slot(%s): already powering off\n",
+ slot_name(p_slot));
+ kfree(info);
+ }
+ break;
+ default:
+ ctrl_err(ctrl, "Not a valid state on slot(%s)\n",
+ slot_name(p_slot));
+ kfree(info);
+ break;
+ }
}
static void interrupt_event_handler(struct work_struct *work)
@@ -492,23 +526,30 @@ static void interrupt_event_handler(struct work_struct *work)
handle_button_press_event(p_slot);
break;
case INT_POWER_FAULT:
- if (!POWER_CTRL(ctrl->ctrlcap))
+ if (!POWER_CTRL(ctrl))
break;
- if (ATTN_LED(ctrl->ctrlcap))
- p_slot->hpc_ops->set_attention_status(p_slot, 1);
- if (PWR_LED(ctrl->ctrlcap))
- p_slot->hpc_ops->green_led_off(p_slot);
+ pciehp_set_attention_status(p_slot, 1);
+ pciehp_green_led_off(p_slot);
break;
case INT_PRESENCE_ON:
- case INT_PRESENCE_OFF:
- if (!HP_SUPR_RM(ctrl->ctrlcap))
+ if (!HP_SUPR_RM(ctrl))
break;
- dbg("Surprise Removal\n");
- update_slot_info(p_slot);
+ ctrl_dbg(ctrl, "Surprise Insertion\n");
handle_surprise_event(p_slot);
break;
+ case INT_PRESENCE_OFF:
+ /*
+ * Regardless of surprise capability, we need to
+ * definitely remove a card that has been pulled out!
+ */
+ ctrl_dbg(ctrl, "Surprise Removal\n");
+ handle_surprise_event(p_slot);
+ break;
+ case INT_LINK_UP:
+ case INT_LINK_DOWN:
+ handle_link_event(p_slot, info->event_type);
+ break;
default:
- update_slot_info(p_slot);
break;
}
mutex_unlock(&p_slot->lock);
@@ -516,106 +557,74 @@ static void interrupt_event_handler(struct work_struct *work)
kfree(info);
}
+/*
+ * Note: This function must be called with slot->hotplug_lock held
+ */
int pciehp_enable_slot(struct slot *p_slot)
{
u8 getstatus = 0;
int rc;
+ struct controller *ctrl = p_slot->ctrl;
- /* Check to see if (latch closed, card present, power off) */
- mutex_lock(&p_slot->ctrl->crit_sect);
-
- rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
- if (rc || !getstatus) {
- info("%s: no adapter on slot(%s)\n", __FUNCTION__,
- p_slot->name);
- mutex_unlock(&p_slot->ctrl->crit_sect);
+ pciehp_get_adapter_status(p_slot, &getstatus);
+ if (!getstatus) {
+ ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
return -ENODEV;
}
- if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
- rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
- if (rc || getstatus) {
- info("%s: latch open on slot(%s)\n", __FUNCTION__,
- p_slot->name);
- mutex_unlock(&p_slot->ctrl->crit_sect);
+ if (MRL_SENS(p_slot->ctrl)) {
+ pciehp_get_latch_status(p_slot, &getstatus);
+ if (getstatus) {
+ ctrl_info(ctrl, "Latch open on slot(%s)\n",
+ slot_name(p_slot));
return -ENODEV;
}
}
- if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
- rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
- if (rc || getstatus) {
- info("%s: already enabled on slot(%s)\n", __FUNCTION__,
- p_slot->name);
- mutex_unlock(&p_slot->ctrl->crit_sect);
+ if (POWER_CTRL(p_slot->ctrl)) {
+ pciehp_get_power_status(p_slot, &getstatus);
+ if (getstatus) {
+ ctrl_info(ctrl, "Already enabled on slot(%s)\n",
+ slot_name(p_slot));
return -EINVAL;
}
}
- p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+ pciehp_get_latch_status(p_slot, &getstatus);
rc = board_added(p_slot);
- if (rc) {
- p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
- }
-
- update_slot_info(p_slot);
+ if (rc)
+ pciehp_get_latch_status(p_slot, &getstatus);
- mutex_unlock(&p_slot->ctrl->crit_sect);
return rc;
}
-
+/*
+ * Note: This function must be called with slot->hotplug_lock held
+ */
int pciehp_disable_slot(struct slot *p_slot)
{
u8 getstatus = 0;
- int ret = 0;
+ struct controller *ctrl = p_slot->ctrl;
if (!p_slot->ctrl)
return 1;
- /* Check to see if (latch closed, card present, power on) */
- mutex_lock(&p_slot->ctrl->crit_sect);
-
- if (!HP_SUPR_RM(p_slot->ctrl->ctrlcap)) {
- ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
- if (ret || !getstatus) {
- info("%s: no adapter on slot(%s)\n", __FUNCTION__,
- p_slot->name);
- mutex_unlock(&p_slot->ctrl->crit_sect);
- return -ENODEV;
- }
- }
-
- if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
- ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
- if (ret || getstatus) {
- info("%s: latch open on slot(%s)\n", __FUNCTION__,
- p_slot->name);
- mutex_unlock(&p_slot->ctrl->crit_sect);
- return -ENODEV;
- }
- }
-
- if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
- ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
- if (ret || !getstatus) {
- info("%s: already disabled slot(%s)\n", __FUNCTION__,
- p_slot->name);
- mutex_unlock(&p_slot->ctrl->crit_sect);
+ if (POWER_CTRL(p_slot->ctrl)) {
+ pciehp_get_power_status(p_slot, &getstatus);
+ if (!getstatus) {
+ ctrl_info(ctrl, "Already disabled on slot(%s)\n",
+ slot_name(p_slot));
return -EINVAL;
}
}
- ret = remove_board(p_slot);
- update_slot_info(p_slot);
-
- mutex_unlock(&p_slot->ctrl->crit_sect);
- return ret;
+ return remove_board(p_slot);
}
int pciehp_sysfs_enable_slot(struct slot *p_slot)
{
int retval = -ENODEV;
+ struct controller *ctrl = p_slot->ctrl;
mutex_lock(&p_slot->lock);
switch (p_slot->state) {
@@ -624,20 +633,24 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
case STATIC_STATE:
p_slot->state = POWERON_STATE;
mutex_unlock(&p_slot->lock);
+ mutex_lock(&p_slot->hotplug_lock);
retval = pciehp_enable_slot(p_slot);
+ mutex_unlock(&p_slot->hotplug_lock);
mutex_lock(&p_slot->lock);
p_slot->state = STATIC_STATE;
break;
case POWERON_STATE:
- info("Slot %s is already in powering on state\n",
- p_slot->name);
+ ctrl_info(ctrl, "Slot %s is already in powering on state\n",
+ slot_name(p_slot));
break;
case BLINKINGOFF_STATE:
case POWEROFF_STATE:
- info("Already enabled on slot %s\n", p_slot->name);
+ ctrl_info(ctrl, "Already enabled on slot %s\n",
+ slot_name(p_slot));
break;
default:
- err("Not a valid state on slot %s\n", p_slot->name);
+ ctrl_err(ctrl, "Not a valid state on slot %s\n",
+ slot_name(p_slot));
break;
}
mutex_unlock(&p_slot->lock);
@@ -648,6 +661,7 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
int pciehp_sysfs_disable_slot(struct slot *p_slot)
{
int retval = -ENODEV;
+ struct controller *ctrl = p_slot->ctrl;
mutex_lock(&p_slot->lock);
switch (p_slot->state) {
@@ -661,15 +675,17 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
p_slot->state = STATIC_STATE;
break;
case POWEROFF_STATE:
- info("Slot %s is already in powering off state\n",
- p_slot->name);
+ ctrl_info(ctrl, "Slot %s is already in powering off state\n",
+ slot_name(p_slot));
break;
case BLINKINGON_STATE:
case POWERON_STATE:
- info("Already disabled on slot %s\n", p_slot->name);
+ ctrl_info(ctrl, "Already disabled on slot %s\n",
+ slot_name(p_slot));
break;
default:
- err("Not a valid state on slot %s\n", p_slot->name);
+ ctrl_err(ctrl, "Not a valid state on slot %s\n",
+ slot_name(p_slot));
break;
}
mutex_unlock(&p_slot->lock);
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 698975a6a21..42914e04d11 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -36,160 +36,16 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/time.h>
+#include <linux/slab.h>
#include "../pci.h"
#include "pciehp.h"
-static atomic_t pciehp_num_controllers = ATOMIC_INIT(0);
-
-struct ctrl_reg {
- u8 cap_id;
- u8 nxt_ptr;
- u16 cap_reg;
- u32 dev_cap;
- u16 dev_ctrl;
- u16 dev_status;
- u32 lnk_cap;
- u16 lnk_ctrl;
- u16 lnk_status;
- u32 slot_cap;
- u16 slot_ctrl;
- u16 slot_status;
- u16 root_ctrl;
- u16 rsvp;
- u32 root_status;
-} __attribute__ ((packed));
-
-/* offsets to the controller registers based on the above structure layout */
-enum ctrl_offsets {
- PCIECAPID = offsetof(struct ctrl_reg, cap_id),
- NXTCAPPTR = offsetof(struct ctrl_reg, nxt_ptr),
- CAPREG = offsetof(struct ctrl_reg, cap_reg),
- DEVCAP = offsetof(struct ctrl_reg, dev_cap),
- DEVCTRL = offsetof(struct ctrl_reg, dev_ctrl),
- DEVSTATUS = offsetof(struct ctrl_reg, dev_status),
- LNKCAP = offsetof(struct ctrl_reg, lnk_cap),
- LNKCTRL = offsetof(struct ctrl_reg, lnk_ctrl),
- LNKSTATUS = offsetof(struct ctrl_reg, lnk_status),
- SLOTCAP = offsetof(struct ctrl_reg, slot_cap),
- SLOTCTRL = offsetof(struct ctrl_reg, slot_ctrl),
- SLOTSTATUS = offsetof(struct ctrl_reg, slot_status),
- ROOTCTRL = offsetof(struct ctrl_reg, root_ctrl),
- ROOTSTATUS = offsetof(struct ctrl_reg, root_status),
-};
-
-static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value)
-{
- struct pci_dev *dev = ctrl->pci_dev;
- return pci_read_config_word(dev, ctrl->cap_base + reg, value);
-}
-
-static inline int pciehp_readl(struct controller *ctrl, int reg, u32 *value)
-{
- struct pci_dev *dev = ctrl->pci_dev;
- return pci_read_config_dword(dev, ctrl->cap_base + reg, value);
-}
-
-static inline int pciehp_writew(struct controller *ctrl, int reg, u16 value)
-{
- struct pci_dev *dev = ctrl->pci_dev;
- return pci_write_config_word(dev, ctrl->cap_base + reg, value);
-}
-
-static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value)
+static inline struct pci_dev *ctrl_dev(struct controller *ctrl)
{
- struct pci_dev *dev = ctrl->pci_dev;
- return pci_write_config_dword(dev, ctrl->cap_base + reg, value);
+ return ctrl->pcie->port;
}
-/* Field definitions in PCI Express Capabilities Register */
-#define CAP_VER 0x000F
-#define DEV_PORT_TYPE 0x00F0
-#define SLOT_IMPL 0x0100
-#define MSG_NUM 0x3E00
-
-/* Device or Port Type */
-#define NAT_ENDPT 0x00
-#define LEG_ENDPT 0x01
-#define ROOT_PORT 0x04
-#define UP_STREAM 0x05
-#define DN_STREAM 0x06
-#define PCIE_PCI_BRDG 0x07
-#define PCI_PCIE_BRDG 0x10
-
-/* Field definitions in Device Capabilities Register */
-#define DATTN_BUTTN_PRSN 0x1000
-#define DATTN_LED_PRSN 0x2000
-#define DPWR_LED_PRSN 0x4000
-
-/* Field definitions in Link Capabilities Register */
-#define MAX_LNK_SPEED 0x000F
-#define MAX_LNK_WIDTH 0x03F0
-
-/* Link Width Encoding */
-#define LNK_X1 0x01
-#define LNK_X2 0x02
-#define LNK_X4 0x04
-#define LNK_X8 0x08
-#define LNK_X12 0x0C
-#define LNK_X16 0x10
-#define LNK_X32 0x20
-
-/*Field definitions of Link Status Register */
-#define LNK_SPEED 0x000F
-#define NEG_LINK_WD 0x03F0
-#define LNK_TRN_ERR 0x0400
-#define LNK_TRN 0x0800
-#define SLOT_CLK_CONF 0x1000
-
-/* Field definitions in Slot Capabilities Register */
-#define ATTN_BUTTN_PRSN 0x00000001
-#define PWR_CTRL_PRSN 0x00000002
-#define MRL_SENS_PRSN 0x00000004
-#define ATTN_LED_PRSN 0x00000008
-#define PWR_LED_PRSN 0x00000010
-#define HP_SUPR_RM_SUP 0x00000020
-#define HP_CAP 0x00000040
-#define SLOT_PWR_VALUE 0x000003F8
-#define SLOT_PWR_LIMIT 0x00000C00
-#define PSN 0xFFF80000 /* PSN: Physical Slot Number */
-
-/* Field definitions in Slot Control Register */
-#define ATTN_BUTTN_ENABLE 0x0001
-#define PWR_FAULT_DETECT_ENABLE 0x0002
-#define MRL_DETECT_ENABLE 0x0004
-#define PRSN_DETECT_ENABLE 0x0008
-#define CMD_CMPL_INTR_ENABLE 0x0010
-#define HP_INTR_ENABLE 0x0020
-#define ATTN_LED_CTRL 0x00C0
-#define PWR_LED_CTRL 0x0300
-#define PWR_CTRL 0x0400
-#define EMI_CTRL 0x0800
-
-/* Attention indicator and Power indicator states */
-#define LED_ON 0x01
-#define LED_BLINK 0x10
-#define LED_OFF 0x11
-
-/* Power Control Command */
-#define POWER_ON 0
-#define POWER_OFF 0x0400
-
-/* EMI Status defines */
-#define EMI_DISENGAGED 0
-#define EMI_ENGAGED 1
-
-/* Field definitions in Slot Status Register */
-#define ATTN_BUTTN_PRESSED 0x0001
-#define PWR_FAULT_DETECTED 0x0002
-#define MRL_SENS_CHANGED 0x0004
-#define PRSN_DETECT_CHANGED 0x0008
-#define CMD_COMPLETED 0x0010
-#define MRL_STATE 0x0020
-#define PRSN_STATE 0x0040
-#define EMI_STATE 0x0080
-#define EMI_STATUS_BIT 7
-
static irqreturn_t pcie_isr(int irq, void *dev_id);
static void start_int_poll_timer(struct controller *ctrl, int sec);
@@ -213,7 +69,7 @@ static void start_int_poll_timer(struct controller *ctrl, int sec)
{
/* Clamp to sane value */
if ((sec <= 0) || (sec > 60))
- sec = 2;
+ sec = 2;
ctrl->poll_timer.function = &int_poll_timeout;
ctrl->poll_timer.data = (unsigned long)ctrl;
@@ -221,1176 +77,753 @@ static void start_int_poll_timer(struct controller *ctrl, int sec)
add_timer(&ctrl->poll_timer);
}
-static inline int pcie_wait_cmd(struct controller *ctrl)
+static inline int pciehp_request_irq(struct controller *ctrl)
+{
+ int retval, irq = ctrl->pcie->irq;
+
+ /* Install interrupt polling timer. Start with 10 sec delay */
+ if (pciehp_poll_mode) {
+ init_timer(&ctrl->poll_timer);
+ start_int_poll_timer(ctrl, 10);
+ return 0;
+ }
+
+ /* Installs the interrupt handler */
+ retval = request_irq(irq, pcie_isr, IRQF_SHARED, MY_NAME, ctrl);
+ if (retval)
+ ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n",
+ irq);
+ return retval;
+}
+
+static inline void pciehp_free_irq(struct controller *ctrl)
+{
+ if (pciehp_poll_mode)
+ del_timer_sync(&ctrl->poll_timer);
+ else
+ free_irq(ctrl->pcie->irq, ctrl);
+}
+
+static int pcie_poll_cmd(struct controller *ctrl)
+{
+ struct pci_dev *pdev = ctrl_dev(ctrl);
+ u16 slot_status;
+ int timeout = 1000;
+
+ pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+ if (slot_status & PCI_EXP_SLTSTA_CC) {
+ pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_CC);
+ return 1;
+ }
+ while (timeout > 0) {
+ msleep(10);
+ timeout -= 10;
+ pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+ if (slot_status & PCI_EXP_SLTSTA_CC) {
+ pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_CC);
+ return 1;
+ }
+ }
+ return 0; /* timeout */
+}
+
+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;
- rc = wait_event_interruptible_timeout(ctrl->queue,
- !ctrl->cmd_busy, timeout);
+ if (poll)
+ rc = pcie_poll_cmd(ctrl);
+ else
+ 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;
+ ctrl_dbg(ctrl, "Command not completed in 1000 msec\n");
}
/**
* pcie_write_cmd - Issue controller command
- * @slot: slot to which the command is issued
+ * @ctrl: controller to which the command is issued
* @cmd: command value written to slot control register
* @mask: bitmask of slot control register to be modified
*/
-static int pcie_write_cmd(struct slot *slot, u16 cmd, u16 mask)
+static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
{
- struct controller *ctrl = slot->ctrl;
- int retval = 0;
+ struct pci_dev *pdev = ctrl_dev(ctrl);
u16 slot_status;
u16 slot_ctrl;
- unsigned long flags;
mutex_lock(&ctrl->ctrl_lock);
- retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (retval) {
- err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__);
- goto out;
- }
-
- if ((slot_status & CMD_COMPLETED) == CMD_COMPLETED ) {
- /* After 1 sec and CMD_COMPLETED still not set, just
- proceed forward to issue the next command according
- to spec. Just print out the error message */
- dbg("%s: CMD_COMPLETED not clear after 1 sec.\n",
- __FUNCTION__);
- }
-
- spin_lock_irqsave(&ctrl->lock, flags);
- retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
- if (retval) {
- err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
- goto out_spin_unlock;
+ pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+ if (slot_status & PCI_EXP_SLTSTA_CC) {
+ pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_CC);
+ if (!ctrl->no_cmd_complete) {
+ /*
+ * After 1 sec and CMD_COMPLETED still not set, just
+ * proceed forward to issue the next command according
+ * to spec. Just print out the error message.
+ */
+ ctrl_dbg(ctrl, "CMD_COMPLETED not clear after 1 sec\n");
+ } else if (!NO_CMD_CMPL(ctrl)) {
+ /*
+ * This controller seems to notify of command completed
+ * event even though it supports none of power
+ * controller, attention led, power led and EMI.
+ */
+ ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Need to wait for command completed event\n");
+ ctrl->no_cmd_complete = 0;
+ } else {
+ ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Maybe the controller is broken\n");
+ }
}
+ pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
slot_ctrl &= ~mask;
- slot_ctrl |= ((cmd & mask) | CMD_CMPL_INTR_ENABLE);
-
+ slot_ctrl |= (cmd & mask);
ctrl->cmd_busy = 1;
- retval = pciehp_writew(ctrl, SLOTCTRL, slot_ctrl);
- if (retval)
- err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__);
-
- out_spin_unlock:
- spin_unlock_irqrestore(&ctrl->lock, flags);
+ smp_mb();
+ pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl);
/*
* Wait for command completion.
*/
- if (!retval)
- retval = pcie_wait_cmd(ctrl);
- out:
+ if (!ctrl->no_cmd_complete) {
+ int poll = 0;
+ /*
+ * if hotplug interrupt is not enabled or command
+ * completed interrupt is not enabled, we need to poll
+ * command completed event.
+ */
+ if (!(slot_ctrl & PCI_EXP_SLTCTL_HPIE) ||
+ !(slot_ctrl & PCI_EXP_SLTCTL_CCIE))
+ poll = 1;
+ pcie_wait_cmd(ctrl, poll);
+ }
mutex_unlock(&ctrl->ctrl_lock);
- return retval;
}
-static int hpc_check_lnk_status(struct controller *ctrl)
+bool pciehp_check_link_active(struct controller *ctrl)
{
+ struct pci_dev *pdev = ctrl_dev(ctrl);
u16 lnk_status;
- int retval = 0;
+ bool ret;
- retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status);
- if (retval) {
- err("%s: Cannot read LNKSTATUS register\n", __FUNCTION__);
- return retval;
- }
+ pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+ ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
+
+ if (ret)
+ ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
- dbg("%s: lnk_status = %x\n", __FUNCTION__, lnk_status);
- if ( (lnk_status & LNK_TRN) || (lnk_status & LNK_TRN_ERR) ||
- !(lnk_status & NEG_LINK_WD)) {
- err("%s : Link Training Error occurs \n", __FUNCTION__);
- retval = -1;
- return retval;
+ return ret;
+}
+
+static void __pcie_wait_link_active(struct controller *ctrl, bool active)
+{
+ int timeout = 1000;
+
+ if (pciehp_check_link_active(ctrl) == active)
+ return;
+ while (timeout > 0) {
+ msleep(10);
+ timeout -= 10;
+ if (pciehp_check_link_active(ctrl) == active)
+ return;
}
+ ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n",
+ active ? "set" : "cleared");
+}
- return retval;
+static void pcie_wait_link_active(struct controller *ctrl)
+{
+ __pcie_wait_link_active(ctrl, true);
}
-static int hpc_get_attention_status(struct slot *slot, u8 *status)
+static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
{
- struct controller *ctrl = slot->ctrl;
- u16 slot_ctrl;
- u8 atten_led_state;
- int retval = 0;
+ u32 l;
+ int count = 0;
+ int delay = 1000, step = 20;
+ bool found = false;
+
+ do {
+ found = pci_bus_read_dev_vendor_id(bus, devfn, &l, 0);
+ count++;
+
+ if (found)
+ break;
- retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
- if (retval) {
- err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
- return retval;
+ msleep(step);
+ delay -= step;
+ } while (delay > 0);
+
+ if (count > 1 && pciehp_debug)
+ printk(KERN_DEBUG "pci %04x:%02x:%02x.%d id reading try %d times with interval %d ms to get %08x\n",
+ pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
+ PCI_FUNC(devfn), count, step, l);
+
+ return found;
+}
+
+int pciehp_check_link_status(struct controller *ctrl)
+{
+ struct pci_dev *pdev = ctrl_dev(ctrl);
+ bool found;
+ u16 lnk_status;
+
+ /*
+ * Data Link Layer Link Active Reporting must be capable for
+ * hot-plug capable downstream port. But old controller might
+ * not implement it. In this case, we wait for 1000 ms.
+ */
+ if (ctrl->link_active_reporting)
+ pcie_wait_link_active(ctrl);
+ else
+ msleep(1000);
+
+ /* wait 100ms before read pci conf, and try in 1s */
+ msleep(100);
+ found = pci_bus_check_dev(ctrl->pcie->port->subordinate,
+ PCI_DEVFN(0, 0));
+
+ pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+ ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
+ if ((lnk_status & PCI_EXP_LNKSTA_LT) ||
+ !(lnk_status & PCI_EXP_LNKSTA_NLW)) {
+ ctrl_err(ctrl, "Link Training Error occurs\n");
+ return -1;
}
- dbg("%s: SLOTCTRL %x, value read %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_ctrl);
+ pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status);
- atten_led_state = (slot_ctrl & ATTN_LED_CTRL) >> 6;
+ if (!found)
+ return -1;
- switch (atten_led_state) {
- case 0:
- *status = 0xFF; /* Reserved */
- break;
- case 1:
+ return 0;
+}
+
+static int __pciehp_link_set(struct controller *ctrl, bool enable)
+{
+ struct pci_dev *pdev = ctrl_dev(ctrl);
+ u16 lnk_ctrl;
+
+ pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnk_ctrl);
+
+ if (enable)
+ lnk_ctrl &= ~PCI_EXP_LNKCTL_LD;
+ else
+ lnk_ctrl |= PCI_EXP_LNKCTL_LD;
+
+ pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnk_ctrl);
+ ctrl_dbg(ctrl, "%s: lnk_ctrl = %x\n", __func__, lnk_ctrl);
+ return 0;
+}
+
+static int pciehp_link_enable(struct controller *ctrl)
+{
+ return __pciehp_link_set(ctrl, true);
+}
+
+void pciehp_get_attention_status(struct slot *slot, u8 *status)
+{
+ struct controller *ctrl = slot->ctrl;
+ struct pci_dev *pdev = ctrl_dev(ctrl);
+ u16 slot_ctrl;
+
+ pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
+ ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__,
+ pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl);
+
+ switch (slot_ctrl & PCI_EXP_SLTCTL_AIC) {
+ case PCI_EXP_SLTCTL_ATTN_IND_ON:
*status = 1; /* On */
break;
- case 2:
+ case PCI_EXP_SLTCTL_ATTN_IND_BLINK:
*status = 2; /* Blink */
break;
- case 3:
+ case PCI_EXP_SLTCTL_ATTN_IND_OFF:
*status = 0; /* Off */
break;
default:
*status = 0xFF;
break;
}
-
- return 0;
}
-static int hpc_get_power_status(struct slot *slot, u8 *status)
+void pciehp_get_power_status(struct slot *slot, u8 *status)
{
struct controller *ctrl = slot->ctrl;
+ struct pci_dev *pdev = ctrl_dev(ctrl);
u16 slot_ctrl;
- u8 pwr_state;
- int retval = 0;
- retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
- if (retval) {
- err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
- return retval;
- }
- dbg("%s: SLOTCTRL %x value read %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_ctrl);
+ pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
+ ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", __func__,
+ pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl);
- pwr_state = (slot_ctrl & PWR_CTRL) >> 10;
-
- switch (pwr_state) {
- case 0:
- *status = 1;
+ switch (slot_ctrl & PCI_EXP_SLTCTL_PCC) {
+ case PCI_EXP_SLTCTL_PWR_ON:
+ *status = 1; /* On */
break;
- case 1:
- *status = 0;
+ case PCI_EXP_SLTCTL_PWR_OFF:
+ *status = 0; /* Off */
break;
default:
*status = 0xFF;
break;
}
-
- return retval;
}
-static int hpc_get_latch_status(struct slot *slot, u8 *status)
+void pciehp_get_latch_status(struct slot *slot, u8 *status)
{
- struct controller *ctrl = slot->ctrl;
+ struct pci_dev *pdev = ctrl_dev(slot->ctrl);
u16 slot_status;
- int retval = 0;
-
- retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (retval) {
- err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__);
- return retval;
- }
-
- *status = (((slot_status & MRL_STATE) >> 5) == 0) ? 0 : 1;
- return 0;
+ pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+ *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS);
}
-static int hpc_get_adapter_status(struct slot *slot, u8 *status)
+void pciehp_get_adapter_status(struct slot *slot, u8 *status)
{
- struct controller *ctrl = slot->ctrl;
+ struct pci_dev *pdev = ctrl_dev(slot->ctrl);
u16 slot_status;
- u8 card_state;
- int retval = 0;
-
- retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (retval) {
- err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__);
- return retval;
- }
- card_state = (u8)((slot_status & PRSN_STATE) >> 6);
- *status = (card_state == 1) ? 1 : 0;
- return 0;
+ pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+ *status = !!(slot_status & PCI_EXP_SLTSTA_PDS);
}
-static int hpc_query_power_fault(struct slot *slot)
+int pciehp_query_power_fault(struct slot *slot)
{
- struct controller *ctrl = slot->ctrl;
+ struct pci_dev *pdev = ctrl_dev(slot->ctrl);
u16 slot_status;
- u8 pwr_fault;
- int retval = 0;
- retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (retval) {
- err("%s: Cannot check for power fault\n", __FUNCTION__);
- return retval;
- }
- pwr_fault = (u8)((slot_status & PWR_FAULT_DETECTED) >> 1);
-
- return pwr_fault;
+ pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+ return !!(slot_status & PCI_EXP_SLTSTA_PFD);
}
-static int hpc_get_emi_status(struct slot *slot, u8 *status)
+void pciehp_set_attention_status(struct slot *slot, u8 value)
{
struct controller *ctrl = slot->ctrl;
- u16 slot_status;
- int retval = 0;
-
- retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (retval) {
- err("%s : Cannot check EMI status\n", __FUNCTION__);
- return retval;
- }
- *status = (slot_status & EMI_STATE) >> EMI_STATUS_BIT;
-
- return retval;
-}
-
-static int hpc_toggle_emi(struct slot *slot)
-{
u16 slot_cmd;
- u16 cmd_mask;
- int rc;
-
- slot_cmd = EMI_CTRL;
- cmd_mask = EMI_CTRL;
- if (!pciehp_poll_mode) {
- slot_cmd = slot_cmd | HP_INTR_ENABLE;
- cmd_mask = cmd_mask | HP_INTR_ENABLE;
- }
-
- rc = pcie_write_cmd(slot, slot_cmd, cmd_mask);
- slot->last_emi_toggle = get_seconds();
- return rc;
-}
-
-static int hpc_set_attention_status(struct slot *slot, u8 value)
-{
- struct controller *ctrl = slot->ctrl;
- u16 slot_cmd;
- u16 cmd_mask;
- int rc;
+ if (!ATTN_LED(ctrl))
+ return;
- cmd_mask = ATTN_LED_CTRL;
switch (value) {
- case 0 : /* turn off */
- slot_cmd = 0x00C0;
- break;
- case 1: /* turn on */
- slot_cmd = 0x0040;
- break;
- case 2: /* turn blink */
- slot_cmd = 0x0080;
- break;
- default:
- return -1;
- }
- if (!pciehp_poll_mode) {
- slot_cmd = slot_cmd | HP_INTR_ENABLE;
- cmd_mask = cmd_mask | HP_INTR_ENABLE;
+ case 0: /* turn off */
+ slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_OFF;
+ break;
+ case 1: /* turn on */
+ slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_ON;
+ break;
+ case 2: /* turn blink */
+ slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_BLINK;
+ break;
+ default:
+ return;
}
-
- rc = pcie_write_cmd(slot, slot_cmd, cmd_mask);
- dbg("%s: SLOTCTRL %x write cmd %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
-
- return rc;
+ ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+ pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
+ pcie_write_cmd(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);
}
-static void hpc_set_green_led_on(struct slot *slot)
+void pciehp_green_led_on(struct slot *slot)
{
struct controller *ctrl = slot->ctrl;
- u16 slot_cmd;
- u16 cmd_mask;
-
- slot_cmd = 0x0100;
- cmd_mask = PWR_LED_CTRL;
- if (!pciehp_poll_mode) {
- slot_cmd = slot_cmd | HP_INTR_ENABLE;
- cmd_mask = cmd_mask | HP_INTR_ENABLE;
- }
- pcie_write_cmd(slot, slot_cmd, cmd_mask);
+ if (!PWR_LED(ctrl))
+ return;
- dbg("%s: SLOTCTRL %x write cmd %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
+ pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PIC);
+ ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+ pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PWR_IND_ON);
}
-static void hpc_set_green_led_off(struct slot *slot)
+void pciehp_green_led_off(struct slot *slot)
{
struct controller *ctrl = slot->ctrl;
- u16 slot_cmd;
- u16 cmd_mask;
- slot_cmd = 0x0300;
- cmd_mask = PWR_LED_CTRL;
- if (!pciehp_poll_mode) {
- slot_cmd = slot_cmd | HP_INTR_ENABLE;
- cmd_mask = cmd_mask | HP_INTR_ENABLE;
- }
+ if (!PWR_LED(ctrl))
+ return;
- pcie_write_cmd(slot, slot_cmd, cmd_mask);
- dbg("%s: SLOTCTRL %x write cmd %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
+ pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PIC);
+ ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+ pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PWR_IND_OFF);
}
-static void hpc_set_green_led_blink(struct slot *slot)
+void pciehp_green_led_blink(struct slot *slot)
{
struct controller *ctrl = slot->ctrl;
- u16 slot_cmd;
- u16 cmd_mask;
-
- slot_cmd = 0x0200;
- cmd_mask = PWR_LED_CTRL;
- if (!pciehp_poll_mode) {
- slot_cmd = slot_cmd | HP_INTR_ENABLE;
- cmd_mask = cmd_mask | HP_INTR_ENABLE;
- }
-
- pcie_write_cmd(slot, slot_cmd, cmd_mask);
-
- dbg("%s: SLOTCTRL %x write cmd %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
-}
-static void hpc_release_ctlr(struct controller *ctrl)
-{
- if (pciehp_poll_mode)
- del_timer(&ctrl->poll_timer);
- else
- free_irq(ctrl->pci_dev->irq, ctrl);
+ if (!PWR_LED(ctrl))
+ return;
- /*
- * 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);
+ pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PIC);
+ ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+ pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PWR_IND_BLINK);
}
-static int hpc_power_on_slot(struct slot * slot)
+int pciehp_power_on_slot(struct slot *slot)
{
struct controller *ctrl = slot->ctrl;
- u16 slot_cmd;
- u16 cmd_mask;
+ struct pci_dev *pdev = ctrl_dev(ctrl);
u16 slot_status;
- int retval = 0;
-
- dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot);
+ int retval;
/* Clear sticky power-fault bit from previous power failures */
- retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (retval) {
- err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__);
- return retval;
- }
- slot_status &= PWR_FAULT_DETECTED;
- if (slot_status) {
- retval = pciehp_writew(ctrl, SLOTSTATUS, slot_status);
- if (retval) {
- err("%s: Cannot write to SLOTSTATUS register\n",
- __FUNCTION__);
- return retval;
- }
- }
-
- slot_cmd = POWER_ON;
- cmd_mask = PWR_CTRL;
- /* Enable detection that we turned off at slot power-off time */
- if (!pciehp_poll_mode) {
- slot_cmd = slot_cmd |
- PWR_FAULT_DETECT_ENABLE |
- MRL_DETECT_ENABLE |
- PRSN_DETECT_ENABLE |
- HP_INTR_ENABLE;
- cmd_mask = cmd_mask |
- PWR_FAULT_DETECT_ENABLE |
- MRL_DETECT_ENABLE |
- PRSN_DETECT_ENABLE |
- HP_INTR_ENABLE;
- }
-
- retval = pcie_write_cmd(slot, slot_cmd, cmd_mask);
-
- if (retval) {
- err("%s: Write %x command failed!\n", __FUNCTION__, slot_cmd);
- return -1;
- }
- dbg("%s: SLOTCTRL %x write cmd %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
+ pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
+ if (slot_status & PCI_EXP_SLTSTA_PFD)
+ pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_PFD);
+ ctrl->power_fault_detected = 0;
+
+ pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_ON, PCI_EXP_SLTCTL_PCC);
+ ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+ pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PWR_ON);
+
+ retval = pciehp_link_enable(ctrl);
+ if (retval)
+ ctrl_err(ctrl, "%s: Can not enable the link!\n", __func__);
return retval;
}
-static inline int pcie_mask_bad_dllp(struct controller *ctrl)
-{
- struct pci_dev *dev = ctrl->pci_dev;
- int pos;
- u32 reg;
-
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
- if (!pos)
- return 0;
- pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg);
- if (reg & PCI_ERR_COR_BAD_DLLP)
- return 0;
- reg |= PCI_ERR_COR_BAD_DLLP;
- pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg);
- return 1;
-}
-
-static inline void pcie_unmask_bad_dllp(struct controller *ctrl)
-{
- struct pci_dev *dev = ctrl->pci_dev;
- u32 reg;
- int pos;
-
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
- if (!pos)
- return;
- pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg);
- if (!(reg & PCI_ERR_COR_BAD_DLLP))
- return;
- reg &= ~PCI_ERR_COR_BAD_DLLP;
- pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg);
-}
-
-static int hpc_power_off_slot(struct slot * slot)
+void pciehp_power_off_slot(struct slot *slot)
{
struct controller *ctrl = slot->ctrl;
- u16 slot_cmd;
- u16 cmd_mask;
- int retval = 0;
- int changed;
-
- dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot);
-
- /*
- * Set Bad DLLP Mask bit in Correctable Error Mask
- * Register. This is the workaround against Bad DLLP error
- * that sometimes happens during turning power off the slot
- * which conforms to PCI Express 1.0a spec.
- */
- changed = pcie_mask_bad_dllp(ctrl);
-
- slot_cmd = POWER_OFF;
- cmd_mask = PWR_CTRL;
- /*
- * If we get MRL or presence detect interrupts now, the isr
- * will notice the sticky power-fault bit too and issue power
- * indicator change commands. This will lead to an endless loop
- * of command completions, since the power-fault bit remains on
- * till the slot is powered on again.
- */
- if (!pciehp_poll_mode) {
- slot_cmd = (slot_cmd &
- ~PWR_FAULT_DETECT_ENABLE &
- ~MRL_DETECT_ENABLE &
- ~PRSN_DETECT_ENABLE) | HP_INTR_ENABLE;
- cmd_mask = cmd_mask |
- PWR_FAULT_DETECT_ENABLE |
- MRL_DETECT_ENABLE |
- PRSN_DETECT_ENABLE |
- HP_INTR_ENABLE;
- }
-
- retval = pcie_write_cmd(slot, slot_cmd, cmd_mask);
- if (retval) {
- err("%s: Write command failed!\n", __FUNCTION__);
- retval = -1;
- goto out;
- }
- dbg("%s: SLOTCTRL %x write cmd %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
-
- /*
- * After turning power off, we must wait for at least 1 second
- * before taking any action that relies on power having been
- * removed from the slot/adapter.
- */
- msleep(1000);
- out:
- if (changed)
- pcie_unmask_bad_dllp(ctrl);
- return retval;
+ pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC);
+ ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+ pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PWR_OFF);
}
static irqreturn_t pcie_isr(int irq, void *dev_id)
{
struct controller *ctrl = (struct controller *)dev_id;
- u16 slot_status, intr_detect, intr_loc;
- u16 temp_word;
- int hp_slot = 0; /* only 1 slot per PCI Express port */
- int rc = 0;
- unsigned long flags;
-
- rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (rc) {
- err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__);
- return IRQ_NONE;
- }
-
- intr_detect = (ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED |
- MRL_SENS_CHANGED | PRSN_DETECT_CHANGED | CMD_COMPLETED);
-
- intr_loc = slot_status & intr_detect;
-
- /* Check to see if it was our interrupt */
- if ( !intr_loc )
- return IRQ_NONE;
-
- dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc);
- /* Mask Hot-plug Interrupt Enable */
- if (!pciehp_poll_mode) {
- spin_lock_irqsave(&ctrl->lock, flags);
- rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
- if (rc) {
- err("%s: Cannot read SLOT_CTRL register\n",
- __FUNCTION__);
- spin_unlock_irqrestore(&ctrl->lock, flags);
- return IRQ_NONE;
- }
+ struct pci_dev *pdev = ctrl_dev(ctrl);
+ struct slot *slot = ctrl->slot;
+ u16 detected, intr_loc;
- dbg("%s: pciehp_readw(SLOTCTRL) with value %x\n",
- __FUNCTION__, temp_word);
- temp_word = (temp_word & ~HP_INTR_ENABLE &
- ~CMD_CMPL_INTR_ENABLE) | 0x00;
- rc = pciehp_writew(ctrl, SLOTCTRL, temp_word);
- if (rc) {
- err("%s: Cannot write to SLOTCTRL register\n",
- __FUNCTION__);
- spin_unlock_irqrestore(&ctrl->lock, flags);
+ /*
+ * In order to guarantee that all interrupt events are
+ * serviced, we need to re-inspect Slot Status register after
+ * clearing what is presumed to be the last pending interrupt.
+ */
+ intr_loc = 0;
+ do {
+ pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &detected);
+
+ detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
+ PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
+ PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);
+ detected &= ~intr_loc;
+ intr_loc |= detected;
+ if (!intr_loc)
return IRQ_NONE;
- }
- spin_unlock_irqrestore(&ctrl->lock, flags);
+ if (detected)
+ pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
+ intr_loc);
+ } while (detected);
- rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (rc) {
- err("%s: Cannot read SLOT_STATUS register\n",
- __FUNCTION__);
- return IRQ_NONE;
- }
- dbg("%s: pciehp_readw(SLOTSTATUS) with value %x\n",
- __FUNCTION__, slot_status);
-
- /* Clear command complete interrupt caused by this write */
- temp_word = 0x1f;
- rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word);
- if (rc) {
- err("%s: Cannot write to SLOTSTATUS register\n",
- __FUNCTION__);
- return IRQ_NONE;
- }
- }
+ ctrl_dbg(ctrl, "%s: intr_loc %x\n", __func__, intr_loc);
- if (intr_loc & CMD_COMPLETED) {
- /*
- * Command Complete Interrupt Pending
- */
+ /* Check Command Complete Interrupt Pending */
+ if (intr_loc & PCI_EXP_SLTSTA_CC) {
ctrl->cmd_busy = 0;
- wake_up_interruptible(&ctrl->queue);
+ smp_mb();
+ wake_up(&ctrl->queue);
}
- if (intr_loc & MRL_SENS_CHANGED)
- pciehp_handle_switch_change(hp_slot, ctrl);
+ if (!(intr_loc & ~PCI_EXP_SLTSTA_CC))
+ return IRQ_HANDLED;
- if (intr_loc & ATTN_BUTTN_PRESSED)
- pciehp_handle_attention_button(hp_slot, ctrl);
+ /* Check MRL Sensor Changed */
+ if (intr_loc & PCI_EXP_SLTSTA_MRLSC)
+ pciehp_handle_switch_change(slot);
- if (intr_loc & PRSN_DETECT_CHANGED)
- pciehp_handle_presence_change(hp_slot, ctrl);
+ /* Check Attention Button Pressed */
+ if (intr_loc & PCI_EXP_SLTSTA_ABP)
+ pciehp_handle_attention_button(slot);
- if (intr_loc & PWR_FAULT_DETECTED)
- pciehp_handle_power_fault(hp_slot, ctrl);
+ /* Check Presence Detect Changed */
+ if (intr_loc & PCI_EXP_SLTSTA_PDC)
+ pciehp_handle_presence_change(slot);
- /* Clear all events after serving them */
- temp_word = 0x1F;
- rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word);
- if (rc) {
- err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__);
- return IRQ_NONE;
+ /* Check Power Fault Detected */
+ if ((intr_loc & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
+ ctrl->power_fault_detected = 1;
+ pciehp_handle_power_fault(slot);
}
- /* Unmask Hot-plug Interrupt Enable */
- if (!pciehp_poll_mode) {
- spin_lock_irqsave(&ctrl->lock, flags);
- rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
- if (rc) {
- err("%s: Cannot read SLOTCTRL register\n",
- __FUNCTION__);
- spin_unlock_irqrestore(&ctrl->lock, flags);
- return IRQ_NONE;
- }
-
- dbg("%s: Unmask Hot-plug Interrupt Enable\n", __FUNCTION__);
- temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE;
- rc = pciehp_writew(ctrl, SLOTCTRL, temp_word);
- if (rc) {
- err("%s: Cannot write to SLOTCTRL register\n",
- __FUNCTION__);
- spin_unlock_irqrestore(&ctrl->lock, flags);
- return IRQ_NONE;
- }
- spin_unlock_irqrestore(&ctrl->lock, flags);
-
- rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (rc) {
- err("%s: Cannot read SLOT_STATUS register\n",
- __FUNCTION__);
- return IRQ_NONE;
- }
-
- /* Clear command complete interrupt caused by this write */
- temp_word = 0x1F;
- rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word);
- if (rc) {
- err("%s: Cannot write to SLOTSTATUS failed\n",
- __FUNCTION__);
- return IRQ_NONE;
- }
- dbg("%s: pciehp_writew(SLOTSTATUS) with value %x\n",
- __FUNCTION__, temp_word);
- }
+ if (intr_loc & PCI_EXP_SLTSTA_DLLSC)
+ pciehp_handle_linkstate_change(slot);
return IRQ_HANDLED;
}
-static int hpc_get_max_lnk_speed(struct slot *slot, enum pci_bus_speed *value)
+void pcie_enable_notification(struct controller *ctrl)
{
- struct controller *ctrl = slot->ctrl;
- enum pcie_link_speed lnk_speed;
- u32 lnk_cap;
- int retval = 0;
-
- retval = pciehp_readl(ctrl, LNKCAP, &lnk_cap);
- if (retval) {
- err("%s: Cannot read LNKCAP register\n", __FUNCTION__);
- return retval;
- }
+ u16 cmd, mask;
- switch (lnk_cap & 0x000F) {
- case 1:
- lnk_speed = PCIE_2PT5GB;
- break;
- default:
- lnk_speed = PCIE_LNK_SPEED_UNKNOWN;
- break;
- }
-
- *value = lnk_speed;
- dbg("Max link speed = %d\n", lnk_speed);
-
- return retval;
-}
-
-static int hpc_get_max_lnk_width(struct slot *slot,
- enum pcie_link_width *value)
-{
- struct controller *ctrl = slot->ctrl;
- enum pcie_link_width lnk_wdth;
- u32 lnk_cap;
- int retval = 0;
-
- retval = pciehp_readl(ctrl, LNKCAP, &lnk_cap);
- if (retval) {
- err("%s: Cannot read LNKCAP register\n", __FUNCTION__);
- return retval;
- }
+ /*
+ * TBD: Power fault detected software notification support.
+ *
+ * Power fault detected software notification is not enabled
+ * now, because it caused power fault detected interrupt storm
+ * on some machines. On those machines, power fault detected
+ * bit in the slot status register was set again immediately
+ * when it is cleared in the interrupt service routine, and
+ * next power fault detected interrupt was notified again.
+ */
- switch ((lnk_cap & 0x03F0) >> 4){
- case 0:
- lnk_wdth = PCIE_LNK_WIDTH_RESRV;
- break;
- case 1:
- lnk_wdth = PCIE_LNK_X1;
- break;
- case 2:
- lnk_wdth = PCIE_LNK_X2;
- break;
- case 4:
- lnk_wdth = PCIE_LNK_X4;
- break;
- case 8:
- lnk_wdth = PCIE_LNK_X8;
- break;
- case 12:
- lnk_wdth = PCIE_LNK_X12;
- break;
- case 16:
- lnk_wdth = PCIE_LNK_X16;
- break;
- case 32:
- lnk_wdth = PCIE_LNK_X32;
- break;
- default:
- lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN;
- break;
- }
+ /*
+ * Always enable link events: thus link-up and link-down shall
+ * always be treated as hotplug and unplug respectively. Enable
+ * presence detect only if Attention Button is not present.
+ */
+ cmd = PCI_EXP_SLTCTL_DLLSCE;
+ if (ATTN_BUTTN(ctrl))
+ cmd |= PCI_EXP_SLTCTL_ABPE;
+ else
+ cmd |= PCI_EXP_SLTCTL_PDCE;
+ if (MRL_SENS(ctrl))
+ cmd |= PCI_EXP_SLTCTL_MRLSCE;
+ if (!pciehp_poll_mode)
+ cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE;
- *value = lnk_wdth;
- dbg("Max link width = %d\n", lnk_wdth);
+ mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
+ PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE |
+ PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
+ PCI_EXP_SLTCTL_DLLSCE);
- return retval;
+ pcie_write_cmd(ctrl, cmd, mask);
}
-static int hpc_get_cur_lnk_speed(struct slot *slot, enum pci_bus_speed *value)
+static void pcie_disable_notification(struct controller *ctrl)
{
- struct controller *ctrl = slot->ctrl;
- enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN;
- int retval = 0;
- u16 lnk_status;
-
- retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status);
- if (retval) {
- err("%s: Cannot read LNKSTATUS register\n", __FUNCTION__);
- return retval;
- }
-
- switch (lnk_status & 0x0F) {
- case 1:
- lnk_speed = PCIE_2PT5GB;
- break;
- default:
- lnk_speed = PCIE_LNK_SPEED_UNKNOWN;
- break;
- }
+ u16 mask;
- *value = lnk_speed;
- dbg("Current link speed = %d\n", lnk_speed);
-
- return retval;
+ mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
+ PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE |
+ PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
+ PCI_EXP_SLTCTL_DLLSCE);
+ pcie_write_cmd(ctrl, 0, mask);
}
-static int hpc_get_cur_lnk_width(struct slot *slot,
- enum pcie_link_width *value)
+/*
+ * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
+ * bus reset of the bridge, but at the same time we want to ensure that it is
+ * not seen as a hot-unplug, followed by the hot-plug of the device. Thus,
+ * disable link state notification and presence detection change notification
+ * momentarily, if we see that they could interfere. Also, clear any spurious
+ * events after.
+ */
+int pciehp_reset_slot(struct slot *slot, int probe)
{
struct controller *ctrl = slot->ctrl;
- enum pcie_link_width lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN;
- int retval = 0;
- u16 lnk_status;
+ struct pci_dev *pdev = ctrl_dev(ctrl);
+ u16 stat_mask = 0, ctrl_mask = 0;
- retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status);
- if (retval) {
- err("%s: Cannot read LNKSTATUS register\n", __FUNCTION__);
- return retval;
- }
+ if (probe)
+ return 0;
- switch ((lnk_status & 0x03F0) >> 4){
- case 0:
- lnk_wdth = PCIE_LNK_WIDTH_RESRV;
- break;
- case 1:
- lnk_wdth = PCIE_LNK_X1;
- break;
- case 2:
- lnk_wdth = PCIE_LNK_X2;
- break;
- case 4:
- lnk_wdth = PCIE_LNK_X4;
- break;
- case 8:
- lnk_wdth = PCIE_LNK_X8;
- break;
- case 12:
- lnk_wdth = PCIE_LNK_X12;
- break;
- case 16:
- lnk_wdth = PCIE_LNK_X16;
- break;
- case 32:
- lnk_wdth = PCIE_LNK_X32;
- break;
- default:
- lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN;
- break;
+ if (!ATTN_BUTTN(ctrl)) {
+ ctrl_mask |= PCI_EXP_SLTCTL_PDCE;
+ stat_mask |= PCI_EXP_SLTSTA_PDC;
}
+ ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE;
+ stat_mask |= PCI_EXP_SLTSTA_DLLSC;
- *value = lnk_wdth;
- dbg("Current link width = %d\n", lnk_wdth);
-
- return retval;
-}
-
-static struct hpc_ops pciehp_hpc_ops = {
- .power_on_slot = hpc_power_on_slot,
- .power_off_slot = hpc_power_off_slot,
- .set_attention_status = hpc_set_attention_status,
- .get_power_status = hpc_get_power_status,
- .get_attention_status = hpc_get_attention_status,
- .get_latch_status = hpc_get_latch_status,
- .get_adapter_status = hpc_get_adapter_status,
- .get_emi_status = hpc_get_emi_status,
- .toggle_emi = hpc_toggle_emi,
-
- .get_max_bus_speed = hpc_get_max_lnk_speed,
- .get_cur_bus_speed = hpc_get_cur_lnk_speed,
- .get_max_lnk_width = hpc_get_max_lnk_width,
- .get_cur_lnk_width = hpc_get_cur_lnk_width,
-
- .query_power_fault = hpc_query_power_fault,
- .green_led_on = hpc_set_green_led_on,
- .green_led_off = hpc_set_green_led_off,
- .green_led_blink = hpc_set_green_led_blink,
-
- .release_ctlr = hpc_release_ctlr,
- .check_lnk_status = hpc_check_lnk_status,
-};
-
-#ifdef CONFIG_ACPI
-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;
- }
+ pcie_write_cmd(ctrl, 0, ctrl_mask);
+ if (pciehp_poll_mode)
+ del_timer_sync(&ctrl->poll_timer);
- 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;
- }
+ pci_reset_bridge_secondary_bus(ctrl->pcie->port);
- err("Cannot get control of hotplug hardware for pci %s\n",
- pci_name(dev));
+ pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
+ pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask);
+ if (pciehp_poll_mode)
+ int_poll_timeout(ctrl->poll_timer.data);
- kfree(string.pointer);
- return -1;
+ return 0;
}
-#endif
-static int pcie_init_hardware_part1(struct controller *ctrl,
- struct pcie_device *dev)
+int pcie_init_notification(struct controller *ctrl)
{
- int rc;
- u16 temp_word;
- u32 slot_cap;
- u16 slot_status;
-
- rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap);
- if (rc) {
- err("%s: Cannot read SLOTCAP register\n", __FUNCTION__);
+ if (pciehp_request_irq(ctrl))
return -1;
- }
-
- /* Mask Hot-plug Interrupt Enable */
- rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
- if (rc) {
- err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
- return -1;
- }
-
- dbg("%s: SLOTCTRL %x value read %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTCTRL, temp_word);
- temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) |
- 0x00;
-
- rc = pciehp_writew(ctrl, SLOTCTRL, temp_word);
- if (rc) {
- err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__);
- return -1;
- }
-
- rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (rc) {
- err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__);
- return -1;
- }
-
- temp_word = 0x1F; /* Clear all events */
- rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word);
- if (rc) {
- err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__);
- return -1;
- }
+ pcie_enable_notification(ctrl);
+ ctrl->notification_enabled = 1;
return 0;
}
-int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
+static void pcie_shutdown_notification(struct controller *ctrl)
{
- int rc;
- u16 temp_word;
- u16 intr_enable = 0;
- u32 slot_cap;
- u16 slot_status;
-
- rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
- if (rc) {
- err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
- goto abort;
+ if (ctrl->notification_enabled) {
+ pcie_disable_notification(ctrl);
+ pciehp_free_irq(ctrl);
+ ctrl->notification_enabled = 0;
}
+}
- intr_enable = intr_enable | PRSN_DETECT_ENABLE;
-
- rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap);
- if (rc) {
- err("%s: Cannot read SLOTCAP register\n", __FUNCTION__);
- goto abort;
- }
-
- if (ATTN_BUTTN(slot_cap))
- intr_enable = intr_enable | ATTN_BUTTN_ENABLE;
-
- if (POWER_CTRL(slot_cap))
- intr_enable = intr_enable | PWR_FAULT_DETECT_ENABLE;
-
- if (MRL_SENS(slot_cap))
- intr_enable = intr_enable | MRL_DETECT_ENABLE;
-
- temp_word = (temp_word & ~intr_enable) | intr_enable;
+static int pcie_init_slot(struct controller *ctrl)
+{
+ struct slot *slot;
- if (pciehp_poll_mode) {
- temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0;
- } else {
- temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE;
- }
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot)
+ return -ENOMEM;
- /*
- * Unmask Hot-plug Interrupt Enable for the interrupt
- * notification mechanism case.
- */
- rc = pciehp_writew(ctrl, SLOTCTRL, temp_word);
- if (rc) {
- err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__);
+ slot->wq = alloc_workqueue("pciehp-%u", 0, 0, PSN(ctrl));
+ if (!slot->wq)
goto abort;
- }
- rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (rc) {
- err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__);
- goto abort_disable_intr;
- }
-
- temp_word = 0x1F; /* Clear all events */
- rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word);
- if (rc) {
- err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__);
- goto abort_disable_intr;
- }
-
- if (pciehp_force) {
- dbg("Bypassing BIOS check for pciehp use on %s\n",
- pci_name(ctrl->pci_dev));
- } else {
- rc = pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev);
- if (rc)
- goto abort_disable_intr;
- }
+ slot->ctrl = ctrl;
+ mutex_init(&slot->lock);
+ mutex_init(&slot->hotplug_lock);
+ INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);
+ ctrl->slot = slot;
return 0;
-
- /* We end up here for the many possible ways to fail this API. */
-abort_disable_intr:
- rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
- if (!rc) {
- temp_word &= ~(intr_enable | HP_INTR_ENABLE);
- rc = pciehp_writew(ctrl, SLOTCTRL, temp_word);
- }
- if (rc)
- err("%s : disabling interrupts failed\n", __FUNCTION__);
abort:
- return -1;
+ kfree(slot);
+ return -ENOMEM;
}
-int pcie_init(struct controller *ctrl, struct pcie_device *dev)
+static void pcie_cleanup_slot(struct controller *ctrl)
{
- int rc;
- u16 cap_reg;
- u32 slot_cap;
- int cap_base;
- u16 slot_status, slot_ctrl;
- struct pci_dev *pdev;
-
- pdev = dev->port;
- ctrl->pci_dev = pdev; /* save pci_dev in context */
-
- dbg("%s: hotplug controller vendor id 0x%x device id 0x%x\n",
- __FUNCTION__, pdev->vendor, pdev->device);
-
- cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP);
- if (cap_base == 0) {
- dbg("%s: Can't find PCI_CAP_ID_EXP (0x10)\n", __FUNCTION__);
- goto abort;
- }
-
- ctrl->cap_base = cap_base;
-
- dbg("%s: pcie_cap_base %x\n", __FUNCTION__, cap_base);
-
- rc = pciehp_readw(ctrl, CAPREG, &cap_reg);
- if (rc) {
- err("%s: Cannot read CAPREG register\n", __FUNCTION__);
- goto abort;
- }
- dbg("%s: CAPREG offset %x cap_reg %x\n",
- __FUNCTION__, ctrl->cap_base + CAPREG, cap_reg);
-
- if (((cap_reg & SLOT_IMPL) == 0) ||
- (((cap_reg & DEV_PORT_TYPE) != 0x0040)
- && ((cap_reg & DEV_PORT_TYPE) != 0x0060))) {
- dbg("%s : This is not a root port or the port is not "
- "connected to a slot\n", __FUNCTION__);
- goto abort;
- }
+ struct slot *slot = ctrl->slot;
+ cancel_delayed_work(&slot->work);
+ destroy_workqueue(slot->wq);
+ kfree(slot);
+}
- rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap);
- if (rc) {
- err("%s: Cannot read SLOTCAP register\n", __FUNCTION__);
- goto abort;
- }
- dbg("%s: SLOTCAP offset %x slot_cap %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTCAP, slot_cap);
+static inline void dbg_ctrl(struct controller *ctrl)
+{
+ int i;
+ u16 reg16;
+ struct pci_dev *pdev = ctrl->pcie->port;
- if (!(slot_cap & HP_CAP)) {
- dbg("%s : This slot is not hot-plug capable\n", __FUNCTION__);
- goto abort;
- }
- /* For debugging purpose */
- rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
- if (rc) {
- err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__);
- goto abort;
- }
- dbg("%s: SLOTSTATUS offset %x slot_status %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTSTATUS, slot_status);
+ if (!pciehp_debug)
+ return;
- rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
- if (rc) {
- err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
+ ctrl_info(ctrl, "Hotplug Controller:\n");
+ ctrl_info(ctrl, " Seg/Bus/Dev/Func/IRQ : %s IRQ %d\n",
+ pci_name(pdev), pdev->irq);
+ ctrl_info(ctrl, " Vendor ID : 0x%04x\n", pdev->vendor);
+ ctrl_info(ctrl, " Device ID : 0x%04x\n", pdev->device);
+ ctrl_info(ctrl, " Subsystem ID : 0x%04x\n",
+ pdev->subsystem_device);
+ ctrl_info(ctrl, " Subsystem Vendor ID : 0x%04x\n",
+ pdev->subsystem_vendor);
+ ctrl_info(ctrl, " PCIe Cap offset : 0x%02x\n",
+ pci_pcie_cap(pdev));
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+ if (!pci_resource_len(pdev, i))
+ continue;
+ ctrl_info(ctrl, " PCI resource [%d] : %pR\n",
+ i, &pdev->resource[i]);
+ }
+ ctrl_info(ctrl, "Slot Capabilities : 0x%08x\n", ctrl->slot_cap);
+ ctrl_info(ctrl, " Physical Slot Number : %d\n", PSN(ctrl));
+ ctrl_info(ctrl, " Attention Button : %3s\n",
+ ATTN_BUTTN(ctrl) ? "yes" : "no");
+ ctrl_info(ctrl, " Power Controller : %3s\n",
+ POWER_CTRL(ctrl) ? "yes" : "no");
+ ctrl_info(ctrl, " MRL Sensor : %3s\n",
+ MRL_SENS(ctrl) ? "yes" : "no");
+ ctrl_info(ctrl, " Attention Indicator : %3s\n",
+ ATTN_LED(ctrl) ? "yes" : "no");
+ ctrl_info(ctrl, " Power Indicator : %3s\n",
+ PWR_LED(ctrl) ? "yes" : "no");
+ ctrl_info(ctrl, " Hot-Plug Surprise : %3s\n",
+ HP_SUPR_RM(ctrl) ? "yes" : "no");
+ ctrl_info(ctrl, " EMI Present : %3s\n",
+ EMI(ctrl) ? "yes" : "no");
+ ctrl_info(ctrl, " Command Completed : %3s\n",
+ NO_CMD_CMPL(ctrl) ? "no" : "yes");
+ pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &reg16);
+ ctrl_info(ctrl, "Slot Status : 0x%04x\n", reg16);
+ pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &reg16);
+ ctrl_info(ctrl, "Slot Control : 0x%04x\n", reg16);
+}
+
+#define FLAG(x, y) (((x) & (y)) ? '+' : '-')
+
+struct controller *pcie_init(struct pcie_device *dev)
+{
+ struct controller *ctrl;
+ u32 slot_cap, link_cap;
+ struct pci_dev *pdev = dev->port;
+
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ dev_err(&dev->device, "%s: Out of memory\n", __func__);
goto abort;
}
- dbg("%s: SLOTCTRL offset %x slot_ctrl %x\n",
- __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_ctrl);
-
- for (rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++)
- if (pci_resource_len(pdev, rc) > 0)
- dbg("pci resource[%d] start=0x%llx(len=0x%llx)\n", rc,
- (unsigned long long)pci_resource_start(pdev, rc),
- (unsigned long long)pci_resource_len(pdev, rc));
-
- 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);
-
- mutex_init(&ctrl->crit_sect);
+ ctrl->pcie = dev;
+ pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
+ ctrl->slot_cap = slot_cap;
mutex_init(&ctrl->ctrl_lock);
- spin_lock_init(&ctrl->lock);
-
- /* setup wait queue */
init_waitqueue_head(&ctrl->queue);
-
- /* return PCI Controller Info */
- ctrl->slot_device_offset = 0;
- ctrl->num_slots = 1;
- ctrl->first_slot = slot_cap >> 19;
- ctrl->ctrlcap = slot_cap & 0x0000007f;
-
- rc = pcie_init_hardware_part1(ctrl, dev);
- if (rc)
- goto abort;
-
- if (pciehp_poll_mode) {
- /* Install interrupt polling timer. Start with 10 sec delay */
- init_timer(&ctrl->poll_timer);
- start_int_poll_timer(ctrl, 10);
- } else {
- /* Installs the interrupt handler */
- rc = request_irq(ctrl->pci_dev->irq, pcie_isr, IRQF_SHARED,
- MY_NAME, (void *)ctrl);
- dbg("%s: request_irq %d for hpc%d (returns %d)\n",
- __FUNCTION__, ctrl->pci_dev->irq,
- atomic_read(&pciehp_num_controllers), rc);
- if (rc) {
- err("Can't get irq %d for the hotplug controller\n",
- ctrl->pci_dev->irq);
- goto abort;
- }
- }
- dbg("pciehp ctrl b:d:f:irq=0x%x:%x:%x:%x\n", pdev->bus->number,
- PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), dev->irq);
-
+ dbg_ctrl(ctrl);
/*
- * If this is the first controller to be initialized,
- * initialize the pciehp work queue
+ * Controller doesn't notify of command completion if the "No
+ * Command Completed Support" bit is set in Slot Capability
+ * register or the controller supports none of power
+ * controller, attention led, power led and EMI.
*/
- if (atomic_add_return(1, &pciehp_num_controllers) == 1) {
- pciehp_wq = create_singlethread_workqueue("pciehpd");
- if (!pciehp_wq) {
- rc = -ENOMEM;
- goto abort_free_irq;
- }
- }
-
- rc = pcie_init_hardware_part2(ctrl, dev);
- if (rc == 0) {
- ctrl->hpc_ops = &pciehp_hpc_ops;
- return 0;
- }
-abort_free_irq:
- if (pciehp_poll_mode)
- del_timer_sync(&ctrl->poll_timer);
- else
- free_irq(ctrl->pci_dev->irq, ctrl);
+ if (NO_CMD_CMPL(ctrl) ||
+ !(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl)))
+ ctrl->no_cmd_complete = 1;
+
+ /* Check if Data Link Layer Link Active Reporting is implemented */
+ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
+ if (link_cap & PCI_EXP_LNKCAP_DLLLARC) {
+ ctrl_dbg(ctrl, "Link Active Reporting supported\n");
+ ctrl->link_active_reporting = 1;
+ }
+
+ /* Clear all remaining event bits in Slot Status register */
+ pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
+ PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
+ PCI_EXP_SLTSTA_CC);
+
+ /* Disable software notification */
+ pcie_disable_notification(ctrl);
+
+ ctrl_info(ctrl, "Slot #%d AttnBtn%c AttnInd%c PwrInd%c PwrCtrl%c MRL%c Interlock%c NoCompl%c LLActRep%c\n",
+ (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
+ FLAG(slot_cap, PCI_EXP_SLTCAP_ABP),
+ FLAG(slot_cap, PCI_EXP_SLTCAP_AIP),
+ FLAG(slot_cap, PCI_EXP_SLTCAP_PIP),
+ FLAG(slot_cap, PCI_EXP_SLTCAP_PCP),
+ FLAG(slot_cap, PCI_EXP_SLTCAP_MRLSP),
+ FLAG(slot_cap, PCI_EXP_SLTCAP_EIP),
+ FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS),
+ FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC));
+
+ if (pcie_init_slot(ctrl))
+ goto abort_ctrl;
+
+ return ctrl;
+
+abort_ctrl:
+ kfree(ctrl);
abort:
- return -1;
+ return NULL;
+}
+
+void pciehp_release_ctrl(struct controller *ctrl)
+{
+ pcie_shutdown_notification(ctrl);
+ pcie_cleanup_slot(ctrl);
+ kfree(ctrl);
}
diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c
index 9372a840b63..5f871f4c4af 100644
--- a/drivers/pci/hotplug/pciehp_pci.c
+++ b/drivers/pci/hotplug/pciehp_pci.c
@@ -34,261 +34,102 @@
#include "../pci.h"
#include "pciehp.h"
-static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
-{
- u16 pci_cmd, pci_bctl;
-
- if (hpp->revision > 1) {
- printk(KERN_WARNING "%s: Rev.%d type0 record not supported\n",
- __FUNCTION__, hpp->revision);
- return;
- }
-
- pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size);
- pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer);
- pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
- if (hpp->enable_serr)
- pci_cmd |= PCI_COMMAND_SERR;
- else
- pci_cmd &= ~PCI_COMMAND_SERR;
- if (hpp->enable_perr)
- pci_cmd |= PCI_COMMAND_PARITY;
- else
- pci_cmd &= ~PCI_COMMAND_PARITY;
- pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
-
- /* Program bridge control value */
- if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
- pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
- hpp->latency_timer);
- pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
- if (hpp->enable_serr)
- pci_bctl |= PCI_BRIDGE_CTL_SERR;
- else
- pci_bctl &= ~PCI_BRIDGE_CTL_SERR;
- if (hpp->enable_perr)
- pci_bctl |= PCI_BRIDGE_CTL_PARITY;
- else
- pci_bctl &= ~PCI_BRIDGE_CTL_PARITY;
- pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
- }
-}
-
-static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
-{
- int pos;
- u16 reg16;
- u32 reg32;
-
- if (hpp->revision > 1) {
- printk(KERN_WARNING "%s: Rev.%d type2 record not supported\n",
- __FUNCTION__, hpp->revision);
- return;
- }
-
- /* Find PCI Express capability */
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!pos)
- return;
-
- /* Initialize Device Control Register */
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
- reg16 = (reg16 & hpp->pci_exp_devctl_and) | hpp->pci_exp_devctl_or;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16);
-
- /* Initialize Link Control Register */
- if (dev->subordinate) {
- pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &reg16);
- reg16 = (reg16 & hpp->pci_exp_lnkctl_and)
- | hpp->pci_exp_lnkctl_or;
- pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, reg16);
- }
-
- /* Find Advanced Error Reporting Enhanced Capability */
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
- if (!pos)
- return;
-
- /* Initialize Uncorrectable Error Mask Register */
- pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &reg32);
- reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or;
- pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
-
- /* Initialize Uncorrectable Error Severity Register */
- pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &reg32);
- reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or;
- pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
-
- /* Initialize Correctable Error Mask Register */
- pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg32);
- reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or;
- pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
-
- /* Initialize Advanced Error Capabilities and Control Register */
- pci_read_config_dword(dev, pos + PCI_ERR_CAP, &reg32);
- reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or;
- pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
-
- /*
- * FIXME: The following two registers are not supported yet.
- *
- * o Secondary Uncorrectable Error Severity Register
- * o Secondary Uncorrectable Error Mask Register
- */
-}
-
-static void program_fw_provided_values(struct pci_dev *dev)
-{
- struct pci_dev *cdev;
- struct hotplug_params hpp;
-
- /* Program hpp values for this device */
- if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL ||
- (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
- (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
- return;
-
- if (pciehp_get_hp_params_from_firmware(dev, &hpp)) {
- printk(KERN_WARNING "%s: Could not get hotplug parameters\n",
- __FUNCTION__);
- return;
- }
-
- if (hpp.t2)
- program_hpp_type2(dev, hpp.t2);
- if (hpp.t0)
- program_hpp_type0(dev, hpp.t0);
-
- /* Program child devices */
- if (dev->subordinate) {
- list_for_each_entry(cdev, &dev->subordinate->devices,
- bus_list)
- program_fw_provided_values(cdev);
- }
-}
-
-static int __ref pciehp_add_bridge(struct pci_dev *dev)
-{
- struct pci_bus *parent = dev->bus;
- int pass, busnr, start = parent->secondary;
- int end = parent->subordinate;
-
- for (busnr = start; busnr <= end; busnr++) {
- if (!pci_find_bus(pci_domain_nr(parent), busnr))
- break;
- }
- if (busnr-- > end) {
- err("No bus number available for hot-added bridge %s\n",
- pci_name(dev));
- return -1;
- }
- for (pass = 0; pass < 2; pass++)
- busnr = pci_scan_bridge(parent, dev, busnr, pass);
- if (!dev->subordinate)
- return -1;
- pci_bus_size_bridges(dev->subordinate);
- pci_bus_assign_resources(parent);
- pci_enable_bridges(parent);
- pci_bus_add_devices(parent);
- return 0;
-}
-
int pciehp_configure_device(struct slot *p_slot)
{
struct pci_dev *dev;
- struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate;
- int num, fn;
+ struct pci_dev *bridge = p_slot->ctrl->pcie->port;
+ struct pci_bus *parent = bridge->subordinate;
+ int num, ret = 0;
+ struct controller *ctrl = p_slot->ctrl;
+
+ pci_lock_rescan_remove();
- dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0));
+ dev = pci_get_slot(parent, PCI_DEVFN(0, 0));
if (dev) {
- err("Device %s already exists at %x:%x, cannot hot-add\n",
- pci_name(dev), p_slot->bus, p_slot->device);
+ ctrl_err(ctrl, "Device %s already exists at %04x:%02x:00, cannot hot-add\n",
+ pci_name(dev), pci_domain_nr(parent), parent->number);
pci_dev_put(dev);
- return -EINVAL;
+ ret = -EEXIST;
+ goto out;
}
- num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0));
+ num = pci_scan_slot(parent, PCI_DEVFN(0, 0));
if (num == 0) {
- err("No new device found\n");
- return -ENODEV;
+ ctrl_err(ctrl, "No new device found\n");
+ ret = -ENODEV;
+ goto out;
}
- for (fn = 0; fn < 8; fn++) {
- dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, fn));
- if (!dev)
- continue;
- if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
- err("Cannot hot-add display device %s\n",
- pci_name(dev));
- pci_dev_put(dev);
+ list_for_each_entry(dev, &parent->devices, bus_list)
+ if (pci_is_bridge(dev))
+ pci_hp_add_bridge(dev);
+
+ pci_assign_unassigned_bridge_resources(bridge);
+
+ list_for_each_entry(dev, &parent->devices, bus_list) {
+ if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
continue;
- }
- if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) ||
- (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) {
- pciehp_add_bridge(dev);
- }
- program_fw_provided_values(dev);
- pci_dev_put(dev);
+
+ pci_configure_slot(dev);
}
- pci_bus_assign_resources(parent);
pci_bus_add_devices(parent);
- return 0;
+
+ out:
+ pci_unlock_rescan_remove();
+ return ret;
}
int pciehp_unconfigure_device(struct slot *p_slot)
{
- int ret, rc = 0;
- int j;
+ int rc = 0;
u8 bctl = 0;
u8 presence = 0;
- struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate;
+ struct pci_dev *dev, *temp;
+ struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate;
u16 command;
+ struct controller *ctrl = p_slot->ctrl;
- dbg("%s: bus/dev = %x/%x\n", __FUNCTION__, p_slot->bus,
- p_slot->device);
- ret = p_slot->hpc_ops->get_adapter_status(p_slot, &presence);
- if (ret)
- presence = 0;
+ ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n",
+ __func__, pci_domain_nr(parent), parent->number);
+ pciehp_get_adapter_status(p_slot, &presence);
- for (j = 0; j < 8; j++) {
- struct pci_dev* temp = pci_get_slot(parent,
- (p_slot->device << 3) | j);
- if (!temp)
- continue;
- if ((temp->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
- err("Cannot remove display device %s\n",
- pci_name(temp));
- pci_dev_put(temp);
- continue;
- }
- if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) {
- pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl);
+ pci_lock_rescan_remove();
+
+ /*
+ * Stopping an SR-IOV PF device removes all the associated VFs,
+ * which will update the bus->devices list and confuse the
+ * iterator. Therefore, iterate in reverse so we remove the VFs
+ * first, then the PF. We do the same in pci_stop_bus_device().
+ */
+ list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
+ bus_list) {
+ pci_dev_get(dev);
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) {
+ pci_read_config_byte(dev, PCI_BRIDGE_CONTROL, &bctl);
if (bctl & PCI_BRIDGE_CTL_VGA) {
- err("Cannot remove display device %s\n",
- pci_name(temp));
- pci_dev_put(temp);
- continue;
+ ctrl_err(ctrl,
+ "Cannot remove display device %s\n",
+ pci_name(dev));
+ pci_dev_put(dev);
+ rc = -EINVAL;
+ break;
}
}
- pci_remove_bus_device(temp);
+ pci_stop_and_remove_bus_device(dev);
/*
* Ensure that no new Requests will be generated from
* the device.
*/
if (presence) {
- pci_read_config_word(temp, PCI_COMMAND, &command);
+ pci_read_config_word(dev, PCI_COMMAND, &command);
command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR);
command |= PCI_COMMAND_INTX_DISABLE;
- pci_write_config_word(temp, PCI_COMMAND, command);
+ pci_write_config_word(dev, PCI_COMMAND, command);
}
- pci_dev_put(temp);
+ pci_dev_put(dev);
}
- /*
- * Some PCI Express root ports require fixup after hot-plug operation.
- */
- if (pcie_mch_quirk)
- pci_fixup_device(pci_fixup_final, p_slot->ctrl->pci_dev);
+ pci_unlock_rescan_remove();
return rc;
}
diff --git a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c
index 50bcd3fe61d..d062c008fc9 100644
--- a/drivers/pci/hotplug/pcihp_skeleton.c
+++ b/drivers/pci/hotplug/pcihp_skeleton.c
@@ -51,15 +51,15 @@ static LIST_HEAD(slot_list);
#define dbg(format, arg...) \
do { \
if (debug) \
- printk (KERN_DEBUG "%s: " format "\n", \
- MY_NAME , ## arg); \
+ printk(KERN_DEBUG "%s: " format "\n", \
+ MY_NAME , ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
/* local variables */
-static int debug;
+static bool debug;
static int num_slots;
#define DRIVER_VERSION "0.3"
@@ -82,7 +82,6 @@ static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
static struct hotplug_slot_ops skel_hotplug_slot_ops = {
- .owner = THIS_MODULE,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.set_attention_status = set_attention_status,
@@ -98,7 +97,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
struct slot *slot = hotplug_slot->private;
int retval = 0;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in code here to enable the specified slot
@@ -112,7 +111,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
struct slot *slot = hotplug_slot->private;
int retval = 0;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in code here to disable the specified slot
@@ -126,21 +125,21 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
struct slot *slot = hotplug_slot->private;
int retval = 0;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
switch (status) {
- case 0:
- /*
- * Fill in code here to turn light off
- */
- break;
-
- case 1:
- default:
- /*
- * Fill in code here to turn light on
- */
- break;
+ case 0:
+ /*
+ * Fill in code here to turn light off
+ */
+ break;
+
+ case 1:
+ default:
+ /*
+ * Fill in code here to turn light on
+ */
+ break;
}
return retval;
@@ -151,15 +150,15 @@ static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
struct slot *slot = hotplug_slot->private;
int retval = 0;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
switch (value) {
- case 0:
- /* Specify a test here */
- break;
- case 1:
- /* Specify another test here */
- break;
+ case 0:
+ /* Specify a test here */
+ break;
+ case 1:
+ /* Specify another test here */
+ break;
}
return retval;
@@ -170,7 +169,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
struct slot *slot = hotplug_slot->private;
int retval = 0;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in logic to get the current power status of the specific
@@ -185,7 +184,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
struct slot *slot = hotplug_slot->private;
int retval = 0;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in logic to get the current attention status of the specific
@@ -200,7 +199,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
struct slot *slot = hotplug_slot->private;
int retval = 0;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in logic to get the current latch status of the specific
@@ -215,7 +214,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
struct slot *slot = hotplug_slot->private;
int retval = 0;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
/*
* Fill in logic to get the current adapter status of the specific
@@ -229,7 +228,7 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot);
kfree(slot);
@@ -253,7 +252,7 @@ static int __init init_slots(void)
struct slot *slot;
struct hotplug_slot *hotplug_slot;
struct hotplug_slot_info *info;
- int retval = -ENOMEM;
+ int retval;
int i;
/*
@@ -262,17 +261,23 @@ static int __init init_slots(void)
*/
for (i = 0; i < num_slots; ++i) {
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
- if (!slot)
+ if (!slot) {
+ retval = -ENOMEM;
goto error;
+ }
hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
- if (!hotplug_slot)
+ if (!hotplug_slot) {
+ retval = -ENOMEM;
goto error_slot;
+ }
slot->hotplug_slot = hotplug_slot;
info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
+ if (!info) {
+ retval = -ENOMEM;
goto error_hpslot;
+ }
hotplug_slot->info = info;
slot->number = i;
@@ -282,7 +287,7 @@ static int __init init_slots(void)
hotplug_slot->release = &release_slot;
make_slot_name(slot);
hotplug_slot->ops = &skel_hotplug_slot_ops;
-
+
/*
* Initialize the slot info structure with some known
* good values.
@@ -291,7 +296,7 @@ static int __init init_slots(void)
get_attention_status(hotplug_slot, &info->attention_status);
get_latch_status(hotplug_slot, &info->latch_status);
get_adapter_status(hotplug_slot, &info->adapter_status);
-
+
dbg("registering slot %d\n", i);
retval = pci_hp_register(slot->hotplug_slot);
if (retval) {
@@ -331,7 +336,7 @@ static void __exit cleanup_slots(void)
pci_hp_deregister(slot->hotplug_slot);
}
}
-
+
static int __init pcihp_skel_init(void)
{
int retval;
diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c
new file mode 100644
index 00000000000..e246a10a0d2
--- /dev/null
+++ b/drivers/pci/hotplug/pcihp_slot.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ * (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/pci.h>
+#include <linux/export.h>
+#include <linux/pci_hotplug.h>
+
+static struct hpp_type0 pci_default_type0 = {
+ .revision = 1,
+ .cache_line_size = 8,
+ .latency_timer = 0x40,
+ .enable_serr = 0,
+ .enable_perr = 0,
+};
+
+static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
+{
+ u16 pci_cmd, pci_bctl;
+
+ if (!hpp) {
+ /*
+ * Perhaps we *should* use default settings for PCIe, but
+ * pciehp didn't, so we won't either.
+ */
+ if (pci_is_pcie(dev))
+ return;
+ dev_info(&dev->dev, "using default PCI settings\n");
+ hpp = &pci_default_type0;
+ }
+
+ if (hpp->revision > 1) {
+ dev_warn(&dev->dev,
+ "PCI settings rev %d not supported; using defaults\n",
+ hpp->revision);
+ hpp = &pci_default_type0;
+ }
+
+ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer);
+ pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
+ if (hpp->enable_serr)
+ pci_cmd |= PCI_COMMAND_SERR;
+ else
+ pci_cmd &= ~PCI_COMMAND_SERR;
+ if (hpp->enable_perr)
+ pci_cmd |= PCI_COMMAND_PARITY;
+ else
+ pci_cmd &= ~PCI_COMMAND_PARITY;
+ pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
+
+ /* Program bridge control value */
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+ pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
+ hpp->latency_timer);
+ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
+ if (hpp->enable_serr)
+ pci_bctl |= PCI_BRIDGE_CTL_SERR;
+ else
+ pci_bctl &= ~PCI_BRIDGE_CTL_SERR;
+ if (hpp->enable_perr)
+ pci_bctl |= PCI_BRIDGE_CTL_PARITY;
+ else
+ pci_bctl &= ~PCI_BRIDGE_CTL_PARITY;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
+ }
+}
+
+static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
+{
+ if (hpp)
+ dev_warn(&dev->dev, "PCI-X settings not supported\n");
+}
+
+static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
+{
+ int pos;
+ u32 reg32;
+
+ if (!hpp)
+ return;
+
+ if (hpp->revision > 1) {
+ dev_warn(&dev->dev, "PCIe settings rev %d not supported\n",
+ hpp->revision);
+ return;
+ }
+
+ /* Initialize Device Control Register */
+ pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+ ~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
+
+ /* Initialize Link Control Register */
+ if (dev->subordinate)
+ pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
+ ~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
+
+ /* Find Advanced Error Reporting Enhanced Capability */
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ if (!pos)
+ return;
+
+ /* Initialize Uncorrectable Error Mask Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &reg32);
+ reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or;
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
+
+ /* Initialize Uncorrectable Error Severity Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &reg32);
+ reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or;
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
+
+ /* Initialize Correctable Error Mask Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg32);
+ reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or;
+ pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
+
+ /* Initialize Advanced Error Capabilities and Control Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_CAP, &reg32);
+ reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or;
+ pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
+
+ /*
+ * FIXME: The following two registers are not supported yet.
+ *
+ * o Secondary Uncorrectable Error Severity Register
+ * o Secondary Uncorrectable Error Mask Register
+ */
+}
+
+void pci_configure_slot(struct pci_dev *dev)
+{
+ struct pci_dev *cdev;
+ struct hotplug_params hpp;
+ int ret;
+
+ if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL ||
+ (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
+ (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
+ return;
+
+ pcie_bus_configure_settings(dev->bus);
+
+ memset(&hpp, 0, sizeof(hpp));
+ ret = pci_get_hp_params(dev, &hpp);
+ if (ret)
+ dev_warn(&dev->dev, "no hotplug settings from platform\n");
+
+ program_hpp_type2(dev, hpp.t2);
+ program_hpp_type1(dev, hpp.t1);
+ program_hpp_type0(dev, hpp.t0);
+
+ if (dev->subordinate) {
+ list_for_each_entry(cdev, &dev->subordinate->devices,
+ bus_list)
+ pci_configure_slot(cdev);
+ }
+}
+EXPORT_SYMBOL_GPL(pci_configure_slot);
diff --git a/drivers/pci/hotplug/rpadlpar.h b/drivers/pci/hotplug/rpadlpar.h
index 4a0a59b82ea..81df93931ad 100644
--- a/drivers/pci/hotplug/rpadlpar.h
+++ b/drivers/pci/hotplug/rpadlpar.h
@@ -15,10 +15,10 @@
#ifndef _RPADLPAR_IO_H_
#define _RPADLPAR_IO_H_
-extern int dlpar_sysfs_init(void);
-extern void dlpar_sysfs_exit(void);
+int dlpar_sysfs_init(void);
+void dlpar_sysfs_exit(void);
-extern int dlpar_add_slot(char *drc_name);
-extern int dlpar_remove_slot(char *drc_name);
+int dlpar_add_slot(char *drc_name);
+int dlpar_remove_slot(char *drc_name);
#endif
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
index 191954bc8e5..7660232ef46 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -14,9 +14,14 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
+
+#undef DEBUG
+
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/string.h>
+#include <linux/vmalloc.h>
#include <asm/pci-bridge.h>
#include <linux/mutex.h>
@@ -147,24 +152,23 @@ static void dlpar_pci_add_bus(struct device_node *dn)
dev = of_create_pci_dev(dn, phb->bus, pdn->devfn);
if (!dev) {
printk(KERN_ERR "%s: failed to create pci dev for %s\n",
- __FUNCTION__, dn->full_name);
+ __func__, dn->full_name);
return;
}
- if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
- dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
- of_scan_pci_bridge(dn, dev);
-
- pcibios_fixup_new_pci_devices(dev->subordinate);
-
- /* Claim new bus resources */
- pcibios_claim_one_bus(dev->bus);
+ /* Scan below the new bridge */
+ if (pci_is_bridge(dev))
+ of_scan_pci_bridge(dev);
/* Map IO space for child bus, which may or may not succeed */
pcibios_map_io_space(dev->subordinate);
- /* Add new devices to global lists. Register in proc, sysfs. */
- pci_bus_add_devices(phb->bus);
+ /* Finish adding it : resource allocation, adding devices, etc...
+ * Note that we need to perform the finish pass on the -parent-
+ * bus of the EADS bridge so the bridge device itself gets
+ * properly added
+ */
+ pcibios_finish_adding_to_bus(phb->bus);
}
static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
@@ -183,47 +187,26 @@ static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
dev = dlpar_find_new_dev(phb->bus, dn);
if (!dev) {
- printk(KERN_ERR "%s: unable to add bus %s\n", __FUNCTION__,
+ printk(KERN_ERR "%s: unable to add bus %s\n", __func__,
drc_name);
return -EIO;
}
if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
printk(KERN_ERR "%s: unexpected header type %d, unable to add bus %s\n",
- __FUNCTION__, dev->hdr_type, drc_name);
+ __func__, dev->hdr_type, drc_name);
return -EIO;
}
/* Add hotplug slot */
if (rpaphp_add_slot(dn)) {
printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
- __FUNCTION__, drc_name);
+ __func__, drc_name);
return -EIO;
}
return 0;
}
-static int dlpar_remove_root_bus(struct pci_controller *phb)
-{
- struct pci_bus *phb_bus;
- int rc;
-
- phb_bus = phb->bus;
- if (!(list_empty(&phb_bus->children) &&
- list_empty(&phb_bus->devices))) {
- return -EBUSY;
- }
-
- rc = pcibios_remove_root_bus(phb);
- if (rc)
- return -EIO;
-
- device_unregister(phb_bus->bridge);
- pci_remove_bus(phb_bus);
-
- return 0;
-}
-
static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
{
struct slot *slot;
@@ -233,20 +216,17 @@ static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
if (!pcibios_find_pci_bus(dn))
return -EINVAL;
- /* If pci slot is hotplugable, use hotplug to remove it */
+ /* If pci slot is hotpluggable, use hotplug to remove it */
slot = find_php_slot(dn);
- if (slot) {
- if (rpaphp_deregister_slot(slot)) {
- printk(KERN_ERR
- "%s: unable to remove hotplug slot %s\n",
- __FUNCTION__, drc_name);
- return -EIO;
- }
+ if (slot && rpaphp_deregister_slot(slot)) {
+ printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
+ __func__, drc_name);
+ return -EIO;
}
pdn = dn->data;
BUG_ON(!pdn || !pdn->phb);
- rc = dlpar_remove_root_bus(pdn->phb);
+ rc = remove_phb_dynamic(pdn->phb);
if (rc < 0)
return rc;
@@ -270,7 +250,7 @@ static int dlpar_add_phb(char *drc_name, struct device_node *dn)
if (rpaphp_add_slot(dn)) {
printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
- __FUNCTION__, drc_name);
+ __func__, drc_name);
return -EIO;
}
return 0;
@@ -284,7 +264,7 @@ static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
if (!vio_register_device_node(dn)) {
printk(KERN_ERR
"%s: failed to register vio node %s\n",
- __FUNCTION__, drc_name);
+ __func__, drc_name);
return -EIO;
}
return 0;
@@ -373,32 +353,52 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
{
struct pci_bus *bus;
struct slot *slot;
+ int ret = 0;
+
+ pci_lock_rescan_remove();
bus = pcibios_find_pci_bus(dn);
- if (!bus)
- return -EINVAL;
+ if (!bus) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pr_debug("PCI: Removing PCI slot below EADS bridge %s\n",
+ bus->self ? pci_name(bus->self) : "<!PHB!>");
- /* If pci slot is hotplugable, use hotplug to remove it */
slot = find_php_slot(dn);
if (slot) {
+ pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n",
+ pci_domain_nr(bus), bus->number);
+
if (rpaphp_deregister_slot(slot)) {
printk(KERN_ERR
"%s: unable to remove hotplug slot %s\n",
- __FUNCTION__, drc_name);
- return -EIO;
+ __func__, drc_name);
+ ret = -EIO;
+ goto out;
}
- } else
- pcibios_remove_pci_devices(bus);
+ }
+
+ /* Remove all devices below slot */
+ pcibios_remove_pci_devices(bus);
+ /* Unmap PCI IO space */
if (pcibios_unmap_io_space(bus)) {
printk(KERN_ERR "%s: failed to unmap bus range\n",
- __FUNCTION__);
- return -ERANGE;
+ __func__);
+ ret = -ERANGE;
+ goto out;
}
+ /* Remove the EADS bridge device itself */
BUG_ON(!bus->self);
- pci_remove_bus_device(bus->self);
- return 0;
+ pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
+ pci_stop_and_remove_bus_device(bus->self);
+
+ out:
+ pci_unlock_rescan_remove();
+ return ret;
}
/**
@@ -439,6 +439,8 @@ int dlpar_remove_slot(char *drc_name)
rc = dlpar_remove_pci_slot(drc_name, dn);
break;
}
+ vm_unmap_aliases();
+
printk(KERN_INFO "%s: slot %s removed\n", DLPAR_MODULE_NAME, drc_name);
exit:
mutex_unlock(&rpadlpar_mutex);
@@ -458,7 +460,7 @@ int __init rpadlpar_io_init(void)
if (!is_dlpar_capable()) {
printk(KERN_WARNING "%s: partition not DLPAR capable\n",
- __FUNCTION__);
+ __func__);
return -EPERM;
}
diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c
index e32148a8fa1..a796301ea03 100644
--- a/drivers/pci/hotplug/rpadlpar_sysfs.c
+++ b/drivers/pci/hotplug/rpadlpar_sysfs.c
@@ -14,15 +14,20 @@
*/
#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"
-#define ADD_SLOT_ATTR_NAME "add_slot"
-#define REMOVE_SLOT_ATTR_NAME "remove_slot"
-#define MAX_DRC_NAME_LEN 64
+/* Those two have no quotes because they are passed to __ATTR() which
+ * stringifies the argument (yuck !)
+ */
+#define ADD_SLOT_ATTR_NAME add_slot
+#define REMOVE_SLOT_ATTR_NAME remove_slot
+#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)
@@ -108,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.h b/drivers/pci/hotplug/rpaphp.h
index 7d5921b1ee7..b2593e876a0 100644
--- a/drivers/pci/hotplug/rpaphp.h
+++ b/drivers/pci/hotplug/rpaphp.h
@@ -46,12 +46,12 @@
#define PRESENT 1 /* Card in slot */
#define MY_NAME "rpaphp"
-extern int debug;
+extern bool rpaphp_debug;
#define dbg(format, arg...) \
do { \
- if (debug) \
+ if (rpaphp_debug) \
printk(KERN_DEBUG "%s: " format, \
- MY_NAME , ## arg); \
+ MY_NAME , ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
@@ -86,18 +86,18 @@ extern struct list_head rpaphp_slot_head;
/* function prototypes */
/* rpaphp_pci.c */
-extern int rpaphp_enable_slot(struct slot *slot);
-extern int rpaphp_get_sensor_state(struct slot *slot, int *state);
+int rpaphp_enable_slot(struct slot *slot);
+int rpaphp_get_sensor_state(struct slot *slot, int *state);
/* rpaphp_core.c */
-extern int rpaphp_add_slot(struct device_node *dn);
-extern int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
+int rpaphp_add_slot(struct device_node *dn);
+int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
char **drc_name, char **drc_type, int *drc_power_domain);
/* rpaphp_slot.c */
-extern void dealloc_slot_struct(struct slot *slot);
-extern struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain);
-extern int rpaphp_register_slot(struct slot *slot);
-extern int rpaphp_deregister_slot(struct slot *slot);
-
+void dealloc_slot_struct(struct slot *slot);
+struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain);
+int rpaphp_register_slot(struct slot *slot);
+int rpaphp_deregister_slot(struct slot *slot);
+
#endif /* _PPC64PHP_H */
diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
index 58f1a992770..93aa29f6d39 100644
--- a/drivers/pci/hotplug/rpaphp_core.c
+++ b/drivers/pci/hotplug/rpaphp_core.c
@@ -27,9 +27,9 @@
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
-#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/init.h>
+#include <linux/vmalloc.h>
#include <asm/eeh.h> /* for eeh_add_device() */
#include <asm/rtas.h> /* rtas_call */
#include <asm/pci-bridge.h> /* for pci_controller */
@@ -37,8 +37,9 @@
/* and pci_do_scan_bus */
#include "rpaphp.h"
-int debug;
+bool rpaphp_debug;
LIST_HEAD(rpaphp_slot_head);
+EXPORT_SYMBOL_GPL(rpaphp_slot_head);
#define DRIVER_VERSION "0.1"
#define DRIVER_AUTHOR "Linda Xie <lxie@us.ibm.com>"
@@ -50,7 +51,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-module_param(debug, bool, 0644);
+module_param_named(debug, rpaphp_debug, bool, 0644);
/**
* set_attention_status - set attention LED
@@ -88,7 +89,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
* @hotplug_slot: slot to get status
* @value: pointer to store status
*/
-static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
int retval, level;
struct slot *slot = (struct slot *)hotplug_slot->private;
@@ -104,14 +105,14 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
* @hotplug_slot: slot to get status
* @value: pointer to store status
*/
-static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = (struct slot *)hotplug_slot->private;
*value = slot->hotplug_slot->info->attention_status;
return 0;
}
-static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = (struct slot *)hotplug_slot->private;
int rc, state;
@@ -130,10 +131,9 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
return 0;
}
-static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+static enum pci_bus_speed get_max_bus_speed(struct slot *slot)
{
- struct slot *slot = (struct slot *)hotplug_slot->private;
-
+ enum pci_bus_speed speed;
switch (slot->type) {
case 1:
case 2:
@@ -141,30 +141,30 @@ static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_spe
case 4:
case 5:
case 6:
- *value = PCI_SPEED_33MHz; /* speed for case 1-6 */
+ speed = PCI_SPEED_33MHz; /* speed for case 1-6 */
break;
case 7:
case 8:
- *value = PCI_SPEED_66MHz;
+ speed = PCI_SPEED_66MHz;
break;
case 11:
case 14:
- *value = PCI_SPEED_66MHz_PCIX;
+ speed = PCI_SPEED_66MHz_PCIX;
break;
case 12:
case 15:
- *value = PCI_SPEED_100MHz_PCIX;
+ speed = PCI_SPEED_100MHz_PCIX;
break;
case 13:
case 16:
- *value = PCI_SPEED_133MHz_PCIX;
+ speed = PCI_SPEED_133MHz_PCIX;
break;
default:
- *value = PCI_SPEED_UNKNOWN;
+ speed = PCI_SPEED_UNKNOWN;
break;
-
}
- return 0;
+
+ return speed;
}
static int get_children_props(struct device_node *dn, const int **drc_indexes,
@@ -224,16 +224,16 @@ int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
type_tmp = (char *) &types[1];
/* Iterate through parent properties, looking for my-drc-index */
- for (i = 0; i < indexes[0]; i++) {
+ for (i = 0; i < be32_to_cpu(indexes[0]); i++) {
if ((unsigned int) indexes[i + 1] == *my_index) {
if (drc_name)
- *drc_name = name_tmp;
+ *drc_name = name_tmp;
if (drc_type)
*drc_type = type_tmp;
if (drc_index)
- *drc_index = *my_index;
+ *drc_index = be32_to_cpu(*my_index);
if (drc_power_domain)
- *drc_power_domain = domains[i+1];
+ *drc_power_domain = be32_to_cpu(domains[i+1]);
return 0;
}
name_tmp += (strlen(name_tmp) + 1);
@@ -242,6 +242,7 @@ int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
return -EINVAL;
}
+EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);
static int is_php_type(char *drc_type)
{
@@ -290,8 +291,8 @@ static int is_php_dn(struct device_node *dn, const int **indexes,
* rpaphp_add_slot -- declare a hotplug slot to the hotplug subsystem.
* @dn: device node of slot
*
- * This subroutine will register a hotplugable slot with the
- * PCI hotplug infrastructure. This routine is typicaly called
+ * This subroutine will register a hotpluggable slot with the
+ * PCI hotplug infrastructure. This routine is typically called
* during boot time, if the hotplug slots are present at boot time,
* or is called later, by the dlpar add code, if the slot is
* being dynamically added during runtime.
@@ -317,21 +318,24 @@ int rpaphp_add_slot(struct device_node *dn)
if (!is_php_dn(dn, &indexes, &names, &types, &power_domains))
return 0;
- dbg("Entry %s: dn->full_name=%s\n", __FUNCTION__, dn->full_name);
+ dbg("Entry %s: dn->full_name=%s\n", __func__, dn->full_name);
/* register PCI devices */
name = (char *) &names[1];
type = (char *) &types[1];
- for (i = 0; i < indexes[0]; i++) {
+ for (i = 0; i < be32_to_cpu(indexes[0]); i++) {
+ int index;
- slot = alloc_slot_struct(dn, indexes[i + 1], name, power_domains[i + 1]);
+ index = be32_to_cpu(indexes[i + 1]);
+ slot = alloc_slot_struct(dn, index, name,
+ be32_to_cpu(power_domains[i + 1]));
if (!slot)
return -ENOMEM;
slot->type = simple_strtoul(type, NULL, 10);
-
+
dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n",
- indexes[i + 1], name, type);
+ index, name, type);
retval = rpaphp_enable_slot(slot);
if (!retval)
@@ -343,11 +347,12 @@ int rpaphp_add_slot(struct device_node *dn)
name += strlen(name) + 1;
type += strlen(type) + 1;
}
- dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
+ dbg("%s - Exit: rc[%d]\n", __func__, retval);
/* XXX FIXME: reports a failure only if last entry in loop failed */
return retval;
}
+EXPORT_SYMBOL_GPL(rpaphp_add_slot);
static void __exit cleanup_slots(void)
{
@@ -357,7 +362,7 @@ static void __exit cleanup_slots(void)
/*
* Unregister all of our slots with the pci_hotplug subsystem,
* and free up all memory that we had allocated.
- * memory will be freed in release_slot callback.
+ * memory will be freed in release_slot callback.
*/
list_for_each_safe(tmp, n, &rpaphp_slot_head) {
@@ -399,15 +404,19 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
return retval;
if (state == PRESENT) {
+ pci_lock_rescan_remove();
pcibios_add_pci_devices(slot->bus);
+ pci_unlock_rescan_remove();
slot->state = CONFIGURED;
} else if (state == EMPTY) {
slot->state = EMPTY;
} else {
- err("%s: slot[%s] is in invalid state\n", __FUNCTION__, slot->name);
+ err("%s: slot[%s] is in invalid state\n", __func__, slot->name);
slot->state = NOT_VALID;
return -EINVAL;
}
+
+ slot->bus->max_bus_speed = get_max_bus_speed(slot);
return 0;
}
@@ -417,25 +426,23 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
if (slot->state == NOT_CONFIGURED)
return -EINVAL;
+ pci_lock_rescan_remove();
pcibios_remove_pci_devices(slot->bus);
+ pci_unlock_rescan_remove();
+ vm_unmap_aliases();
+
slot->state = NOT_CONFIGURED;
return 0;
}
struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
- .owner = THIS_MODULE,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.set_attention_status = set_attention_status,
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
.get_adapter_status = get_adapter_status,
- .get_max_bus_speed = get_max_bus_speed,
};
module_init(rpaphp_init);
module_exit(rpaphp_exit);
-
-EXPORT_SYMBOL_GPL(rpaphp_add_slot);
-EXPORT_SYMBOL_GPL(rpaphp_slot_head);
-EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);
diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c
index 6571e9b4c2e..9243f3e7a1c 100644
--- a/drivers/pci/hotplug/rpaphp_pci.c
+++ b/drivers/pci/hotplug/rpaphp_pci.c
@@ -42,24 +42,24 @@ int rpaphp_get_sensor_state(struct slot *slot, int *state)
if (rc < 0) {
if (rc == -EFAULT || rc == -EEXIST) {
dbg("%s: slot must be power up to get sensor-state\n",
- __FUNCTION__);
+ __func__);
- /* some slots have to be powered up
+ /* some slots have to be powered up
* before get-sensor will succeed.
*/
rc = rtas_set_power_level(slot->power_domain, POWER_ON,
&setlevel);
if (rc < 0) {
dbg("%s: power on slot[%s] failed rc=%d.\n",
- __FUNCTION__, slot->name, rc);
+ __func__, slot->name, rc);
} else {
rc = rtas_get_sensor(DR_ENTITY_SENSE,
slot->index, state);
}
} else if (rc == -ENODEV)
- info("%s: slot is unusable\n", __FUNCTION__);
+ info("%s: slot is unusable\n", __func__);
else
- err("%s failed to get sensor state\n", __FUNCTION__);
+ err("%s failed to get sensor state\n", __func__);
}
return rc;
}
@@ -95,7 +95,7 @@ int rpaphp_enable_slot(struct slot *slot)
bus = pcibios_find_pci_bus(slot->dn);
if (!bus) {
- err("%s: no pci_bus for dn %s\n", __FUNCTION__, slot->dn->full_name);
+ err("%s: no pci_bus for dn %s\n", __func__, slot->dn->full_name);
return -EINVAL;
}
@@ -111,7 +111,7 @@ int rpaphp_enable_slot(struct slot *slot)
/* non-empty slot has to have child */
if (!slot->dn->child) {
err("%s: slot[%s]'s device_node doesn't have child for adapter\n",
- __FUNCTION__, slot->name);
+ __func__, slot->name);
return -EINVAL;
}
@@ -123,9 +123,9 @@ int rpaphp_enable_slot(struct slot *slot)
slot->state = CONFIGURED;
}
- if (debug) {
+ if (rpaphp_debug) {
struct pci_dev *dev;
- dbg("%s: pci_devs of slot[%s]\n", __FUNCTION__, slot->dn->full_name);
+ dbg("%s: pci_devs of slot[%s]\n", __func__, slot->dn->full_name);
list_for_each_entry (dev, &bus->devices, bus_list)
dbg("\t%s\n", pci_name(dev));
}
@@ -133,4 +133,3 @@ int rpaphp_enable_slot(struct slot *slot)
return 0;
}
-
diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c
index 8ad3debb379..a6082cc263f 100644
--- a/drivers/pci/hotplug/rpaphp_slot.c
+++ b/drivers/pci/hotplug/rpaphp_slot.c
@@ -1,5 +1,5 @@
/*
- * RPA Virtual I/O device functions
+ * RPA Virtual I/O device functions
* Copyright (C) 2004 Linda Xie <lxie@us.ibm.com>
*
* All rights reserved.
@@ -24,7 +24,6 @@
*/
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/pci.h>
#include <linux/string.h>
@@ -33,33 +32,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)
{
@@ -70,7 +42,7 @@ static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
void dealloc_slot_struct(struct slot *slot)
{
kfree(slot->hotplug_slot->info);
- kfree(slot->hotplug_slot->name);
+ kfree(slot->name);
kfree(slot->hotplug_slot);
kfree(slot);
}
@@ -79,29 +51,27 @@ struct slot *alloc_slot_struct(struct device_node *dn,
int drc_index, char *drc_name, int power_domain)
{
struct slot *slot;
-
+
slot = kzalloc(sizeof(struct slot), GFP_KERNEL);
if (!slot)
goto error_nomem;
slot->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
if (!slot->hotplug_slot)
- goto error_slot;
+ goto error_slot;
slot->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
GFP_KERNEL);
if (!slot->hotplug_slot->info)
goto error_hpslot;
- slot->hotplug_slot->name = kmalloc(strlen(drc_name) + 1, GFP_KERNEL);
- if (!slot->hotplug_slot->name)
- goto error_info;
- slot->name = slot->hotplug_slot->name;
- strcpy(slot->name, drc_name);
+ slot->name = kstrdup(drc_name, GFP_KERNEL);
+ if (!slot->name)
+ goto error_info;
slot->dn = dn;
slot->index = drc_index;
slot->power_domain = power_domain;
slot->hotplug_slot->private = slot;
slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
slot->hotplug_slot->release = &rpaphp_release_slot;
-
+
return (slot);
error_info:
@@ -121,7 +91,7 @@ static int is_registered(struct slot *slot)
list_for_each_entry(tmp_slot, &rpaphp_slot_head, rpaphp_slot_list) {
if (!strcmp(tmp_slot->name, slot->name))
return 1;
- }
+ }
return 0;
}
@@ -131,18 +101,15 @@ int rpaphp_deregister_slot(struct slot *slot)
struct hotplug_slot *php_slot = slot->hotplug_slot;
dbg("%s - Entry: deregistering slot=%s\n",
- __FUNCTION__, slot->name);
+ __func__, slot->name);
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);
- dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
+ dbg("%s - Exit: rc[%d]\n", __func__, retval);
return retval;
}
EXPORT_SYMBOL_GPL(rpaphp_deregister_slot);
@@ -151,37 +118,30 @@ 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",
- __FUNCTION__, slot->dn->full_name, slot->index, slot->name,
+ dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
+ __func__, slot->dn->full_name, slot->index, slot->name,
slot->power_domain, slot->type);
/* should not try to register the same slot twice */
if (is_registered(slot)) {
err("rpaphp_register_slot: slot[%s] is already registered\n", slot->name);
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, slot->name);
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);
return 0;
-
-sysfs_fail:
- pci_hp_deregister(php_slot);
- return retval;
}
-
diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c
new file mode 100644
index 00000000000..d1332d2f873
--- /dev/null
+++ b/drivers/pci/hotplug/s390_pci_hpc.c
@@ -0,0 +1,214 @@
+/*
+ * PCI Hot Plug Controller Driver for System z
+ *
+ * Copyright 2012 IBM Corp.
+ *
+ * Author(s):
+ * Jan Glauber <jang@linux.vnet.ibm.com>
+ */
+
+#define COMPONENT "zPCI hpc"
+#define pr_fmt(fmt) COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <asm/pci_debug.h>
+#include <asm/sclp.h>
+
+#define SLOT_NAME_SIZE 10
+static LIST_HEAD(s390_hotplug_slot_list);
+
+MODULE_AUTHOR("Jan Glauber <jang@linux.vnet.ibm.com");
+MODULE_DESCRIPTION("Hot Plug PCI Controller for System z");
+MODULE_LICENSE("GPL");
+
+static int zpci_fn_configured(enum zpci_state state)
+{
+ return state == ZPCI_FN_STATE_CONFIGURED ||
+ state == ZPCI_FN_STATE_ONLINE;
+}
+
+/*
+ * struct slot - slot information for each *physical* slot
+ */
+struct slot {
+ struct list_head slot_list;
+ struct hotplug_slot *hotplug_slot;
+ struct zpci_dev *zdev;
+};
+
+static inline int slot_configure(struct slot *slot)
+{
+ int ret = sclp_pci_configure(slot->zdev->fid);
+
+ zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, ret);
+ if (!ret)
+ slot->zdev->state = ZPCI_FN_STATE_CONFIGURED;
+
+ return ret;
+}
+
+static inline int slot_deconfigure(struct slot *slot)
+{
+ int ret = sclp_pci_deconfigure(slot->zdev->fid);
+
+ zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, ret);
+ if (!ret)
+ slot->zdev->state = ZPCI_FN_STATE_STANDBY;
+
+ return ret;
+}
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+ int rc;
+
+ if (slot->zdev->state != ZPCI_FN_STATE_STANDBY)
+ return -EIO;
+
+ rc = slot_configure(slot);
+ if (rc)
+ return rc;
+
+ rc = zpci_enable_device(slot->zdev);
+ if (rc)
+ goto out_deconfigure;
+
+ pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN);
+ pci_lock_rescan_remove();
+ pci_bus_add_devices(slot->zdev->bus);
+ pci_unlock_rescan_remove();
+
+ return rc;
+
+out_deconfigure:
+ slot_deconfigure(slot);
+ return rc;
+}
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+ int rc;
+
+ if (!zpci_fn_configured(slot->zdev->state))
+ return -EIO;
+
+ if (slot->zdev->pdev)
+ pci_stop_and_remove_bus_device_locked(slot->zdev->pdev);
+
+ rc = zpci_disable_device(slot->zdev);
+ if (rc)
+ return rc;
+
+ return slot_deconfigure(slot);
+}
+
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ switch (slot->zdev->state) {
+ case ZPCI_FN_STATE_STANDBY:
+ *value = 0;
+ break;
+ default:
+ *value = 1;
+ break;
+ }
+ return 0;
+}
+
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ /* if the slot exits it always contains a function */
+ *value = 1;
+ return 0;
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot);
+ kfree(slot);
+}
+
+static struct hotplug_slot_ops s390_hotplug_slot_ops = {
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+ .get_power_status = get_power_status,
+ .get_adapter_status = get_adapter_status,
+};
+
+int zpci_init_slot(struct zpci_dev *zdev)
+{
+ struct hotplug_slot *hotplug_slot;
+ struct hotplug_slot_info *info;
+ char name[SLOT_NAME_SIZE];
+ struct slot *slot;
+ int rc;
+
+ if (!zdev)
+ return 0;
+
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot)
+ goto error;
+
+ hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
+ if (!hotplug_slot)
+ goto error_hp;
+ hotplug_slot->private = slot;
+
+ slot->hotplug_slot = hotplug_slot;
+ slot->zdev = zdev;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ goto error_info;
+ hotplug_slot->info = info;
+
+ hotplug_slot->ops = &s390_hotplug_slot_ops;
+ hotplug_slot->release = &release_slot;
+
+ get_power_status(hotplug_slot, &info->power_status);
+ get_adapter_status(hotplug_slot, &info->adapter_status);
+
+ snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid);
+ rc = pci_hp_register(slot->hotplug_slot, zdev->bus,
+ ZPCI_DEVFN, name);
+ if (rc)
+ goto error_reg;
+
+ list_add(&slot->slot_list, &s390_hotplug_slot_list);
+ return 0;
+
+error_reg:
+ kfree(info);
+error_info:
+ kfree(hotplug_slot);
+error_hp:
+ kfree(slot);
+error:
+ return -ENOMEM;
+}
+
+void zpci_exit_slot(struct zpci_dev *zdev)
+{
+ struct list_head *tmp, *n;
+ struct slot *slot;
+
+ list_for_each_safe(tmp, n, &s390_hotplug_slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ if (slot->zdev != zdev)
+ continue;
+ list_del(&slot->slot_list);
+ pci_hp_deregister(slot->hotplug_slot);
+ }
+}
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index ef07c36bccf..bada2099987 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -9,12 +9,14 @@
* Work to add BIOS PROM support was completed by Mike Habeck.
*/
+#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/proc_fs.h>
+#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mutex.h>
@@ -28,7 +30,6 @@
#include <asm/sn/sn_feature_sets.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/types.h>
-#include <linux/acpi.h>
#include <asm/sn/acpi.h>
#include "../pci.h"
@@ -83,7 +84,6 @@ static int disable_slot(struct hotplug_slot *slot);
static inline int get_power_status(struct hotplug_slot *slot, u8 *value);
static struct hotplug_slot_ops sn_hotplug_slot_ops = {
- .owner = THIS_MODULE,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.get_power_status = get_power_status,
@@ -91,11 +91,10 @@ static struct hotplug_slot_ops sn_hotplug_slot_ops = {
static DEFINE_MUTEX(sn_hotplug_mutex);
-static ssize_t path_show (struct hotplug_slot *bss_hotplug_slot,
- char *buf)
+static ssize_t path_show(struct pci_slot *pci_slot, char *buf)
{
int retval = -ENOENT;
- struct slot *slot = bss_hotplug_slot->private;
+ struct slot *slot = pci_slot->hotplug->private;
if (!slot)
return retval;
@@ -104,7 +103,7 @@ static ssize_t path_show (struct hotplug_slot *bss_hotplug_slot,
return retval;
}
-static struct hotplug_slot_attribute sn_slot_path_attr = __ATTR_RO(path);
+static struct pci_slot_attribute sn_slot_path_attr = __ATTR_RO(path);
static int sn_pci_slot_valid(struct pci_bus *pci_bus, int device)
{
@@ -161,7 +160,8 @@ static int sn_pci_bus_valid(struct pci_bus *pci_bus)
}
static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
- struct pci_bus *pci_bus, int device)
+ struct pci_bus *pci_bus, int device,
+ char *name)
{
struct pcibus_info *pcibus_info;
struct slot *slot;
@@ -173,15 +173,9 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
return -ENOMEM;
bss_hotplug_slot->private = slot;
- bss_hotplug_slot->name = kmalloc(SN_SLOT_NAME_SIZE, GFP_KERNEL);
- if (!bss_hotplug_slot->name) {
- kfree(bss_hotplug_slot->private);
- return -ENOMEM;
- }
-
slot->device_num = device;
slot->pci_bus = pci_bus;
- sprintf(bss_hotplug_slot->name, "%04x:%02x:%02x",
+ sprintf(name, "%04x:%02x:%02x",
pci_domain_nr(pci_bus),
((u16)pcibus_info->pbi_buscommon.bs_persist_busnum),
device + 1);
@@ -194,16 +188,18 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
return 0;
}
-static struct hotplug_slot * sn_hp_destroy(void)
+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;
}
@@ -254,15 +250,13 @@ static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
}
if (rc == PCI_L1_ERR) {
- dev_dbg(&slot->pci_bus->self->dev,
- "L1 failure %d with message: %s",
+ dev_dbg(&slot->pci_bus->self->dev, "L1 failure %d with message: %s",
resp.resp_sub_errno, resp.resp_l1_msg);
return -EPERM;
}
if (rc) {
- dev_dbg(&slot->pci_bus->self->dev,
- "insert failed with error %d sub-error %d\n",
+ dev_dbg(&slot->pci_bus->self->dev, "insert failed with error %d sub-error %d\n",
rc, resp.resp_sub_errno);
return -EIO;
}
@@ -292,21 +286,18 @@ static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
}
if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_EMPTY_33MHZ)) {
- dev_dbg(&slot->pci_bus->self->dev,
- "Cannot remove last 33MHz card\n");
+ dev_dbg(&slot->pci_bus->self->dev, "Cannot remove last 33MHz card\n");
return -EPERM;
}
if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_L1_ERR)) {
- dev_dbg(&slot->pci_bus->self->dev,
- "L1 failure %d with message \n%s\n",
+ dev_dbg(&slot->pci_bus->self->dev, "L1 failure %d with message \n%s\n",
resp.resp_sub_errno, resp.resp_l1_msg);
return -EPERM;
}
if ((action == PCI_REQ_SLOT_ELIGIBLE) && rc) {
- dev_dbg(&slot->pci_bus->self->dev,
- "remove failed with error %d sub-error %d\n",
+ dev_dbg(&slot->pci_bus->self->dev, "remove failed with error %d sub-error %d\n",
rc, resp.resp_sub_errno);
return -EIO;
}
@@ -338,7 +329,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
struct slot *slot = bss_hotplug_slot->private;
struct pci_bus *new_bus = NULL;
struct pci_dev *dev;
- int func, num_funcs;
+ int num_funcs;
int new_ppb = 0;
int rc;
char *ssdt = NULL;
@@ -367,7 +358,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
ret = acpi_load_table((struct acpi_table_header *)ssdt);
if (ACPI_FAILURE(ret)) {
printk(KERN_ERR "%s: acpi_load_table failed (0x%x)\n",
- __FUNCTION__, ret);
+ __func__, ret);
/* try to continue on */
}
}
@@ -385,53 +376,47 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
* to the Linux PCI interface and tell the drivers
* about them.
*/
- for (func = 0; func < num_funcs; func++) {
- dev = pci_get_slot(slot->pci_bus,
- PCI_DEVFN(slot->device_num + 1,
- PCI_FUNC(func)));
- if (dev) {
- /* Need to do slot fixup on PPB before fixup of children
- * (PPB's pcidev_info needs to be in pcidev_info list
- * before child's SN_PCIDEV_INFO() call to setup
- * pdi_host_pcidev_info).
- */
- pcibios_fixup_device_resources(dev);
- if (SN_ACPI_BASE_SUPPORT())
- sn_acpi_slot_fixup(dev);
- else
- sn_io_slot_fixup(dev);
- if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
- unsigned char sec_bus;
- pci_read_config_byte(dev, PCI_SECONDARY_BUS,
- &sec_bus);
- new_bus = pci_add_new_bus(dev->bus, dev,
- sec_bus);
- pci_scan_child_bus(new_bus);
+ list_for_each_entry(dev, &slot->pci_bus->devices, bus_list) {
+ if (PCI_SLOT(dev->devfn) != slot->device_num + 1)
+ continue;
+
+ /* Need to do slot fixup on PPB before fixup of children
+ * (PPB's pcidev_info needs to be in pcidev_info list
+ * before child's SN_PCIDEV_INFO() call to setup
+ * pdi_host_pcidev_info).
+ */
+ pcibios_fixup_device_resources(dev);
+ if (SN_ACPI_BASE_SUPPORT())
+ sn_acpi_slot_fixup(dev);
+ else
+ sn_io_slot_fixup(dev);
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ pci_hp_add_bridge(dev);
+ if (dev->subordinate) {
+ new_bus = dev->subordinate;
new_ppb = 1;
}
- pci_dev_put(dev);
}
}
/*
* Add the slot's devices to the ACPI infrastructure */
if (SN_ACPI_BASE_SUPPORT() && ssdt) {
- unsigned long adr;
+ unsigned long long adr;
struct acpi_device *pdevice;
- struct acpi_device *device;
acpi_handle phandle;
acpi_handle chandle = NULL;
acpi_handle rethandle;
acpi_status ret;
- phandle = PCI_CONTROLLER(slot->pci_bus)->acpi_handle;
+ phandle = acpi_device_handle(PCI_CONTROLLER(slot->pci_bus)->companion);
if (acpi_bus_get_device(phandle, &pdevice)) {
- dev_dbg(&slot->pci_bus->self->dev,
- "no parent device, assuming NULL\n");
+ dev_dbg(&slot->pci_bus->self->dev, "no parent device, assuming NULL\n");
pdevice = NULL;
}
+ acpi_scan_lock_acquire();
/*
* Walk the rootbus node's immediate children looking for
* the slot's device node(s). There can be more than
@@ -454,36 +439,33 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
if (ACPI_SUCCESS(ret) &&
(adr>>16) == (slot->device_num + 1)) {
- ret = acpi_bus_add(&device, pdevice, chandle,
- ACPI_BUS_TYPE_DEVICE);
+ ret = acpi_bus_scan(chandle);
if (ACPI_FAILURE(ret)) {
- printk(KERN_ERR "%s: acpi_bus_add "
- "failed (0x%x) for slot %d "
- "func %d\n", __FUNCTION__,
- ret, (int)(adr>>16),
+ printk(KERN_ERR "%s: acpi_bus_scan failed (0x%x) for slot %d func %d\n",
+ __func__, ret, (int)(adr>>16),
(int)(adr&0xffff));
/* try to continue on */
- } else {
- acpi_bus_start(device);
}
}
}
+ acpi_scan_lock_release();
}
+ pci_lock_rescan_remove();
+
/* Call the driver for the new device */
pci_bus_add_devices(slot->pci_bus);
/* Call the drivers for the new devices subordinate to PPB */
if (new_ppb)
pci_bus_add_devices(new_bus);
+ pci_unlock_rescan_remove();
mutex_unlock(&sn_hotplug_mutex);
if (rc == 0)
- dev_dbg(&slot->pci_bus->self->dev,
- "insert operation successful\n");
+ dev_dbg(&slot->pci_bus->self->dev, "insert operation successful\n");
else
- dev_dbg(&slot->pci_bus->self->dev,
- "insert operation failed rc = %d\n", rc);
+ dev_dbg(&slot->pci_bus->self->dev, "insert operation failed rc = %d\n", rc);
return rc;
}
@@ -491,8 +473,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
{
struct slot *slot = bss_hotplug_slot->private;
- struct pci_dev *dev;
- int func;
+ struct pci_dev *dev, *temp;
int rc;
acpi_owner_id ssdt_id = 0;
@@ -507,8 +488,8 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
/* free the ACPI resources for the slot */
if (SN_ACPI_BASE_SUPPORT() &&
- PCI_CONTROLLER(slot->pci_bus)->acpi_handle) {
- unsigned long adr;
+ PCI_CONTROLLER(slot->pci_bus)->companion) {
+ unsigned long long adr;
struct acpi_device *device;
acpi_handle phandle;
acpi_handle chandle = NULL;
@@ -516,8 +497,9 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
acpi_status ret;
/* Get the rootbus node pointer */
- phandle = PCI_CONTROLLER(slot->pci_bus)->acpi_handle;
+ phandle = acpi_device_handle(PCI_CONTROLLER(slot->pci_bus)->companion);
+ acpi_scan_lock_acquire();
/*
* Walk the rootbus node's immediate children looking for
* the slot's device node(s). There can be more than
@@ -545,32 +527,32 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
ret = acpi_bus_get_device(chandle,
&device);
if (ACPI_SUCCESS(ret))
- acpi_bus_trim(device, 1);
+ acpi_bus_trim(device);
}
}
-
+ acpi_scan_lock_release();
}
+ pci_lock_rescan_remove();
/* Free the SN resources assigned to the Linux device.*/
- for (func = 0; func < 8; func++) {
- dev = pci_get_slot(slot->pci_bus,
- PCI_DEVFN(slot->device_num + 1,
- PCI_FUNC(func)));
- if (dev) {
- sn_bus_free_data(dev);
- pci_remove_bus_device(dev);
- pci_dev_put(dev);
- }
+ list_for_each_entry_safe(dev, temp, &slot->pci_bus->devices, bus_list) {
+ if (PCI_SLOT(dev->devfn) != slot->device_num + 1)
+ continue;
+
+ pci_dev_get(dev);
+ sn_bus_free_data(dev);
+ pci_stop_and_remove_bus_device(dev);
+ pci_dev_put(dev);
}
+ pci_unlock_rescan_remove();
/* Remove the SSDT for the slot from the ACPI namespace */
if (SN_ACPI_BASE_SUPPORT() && ssdt_id) {
acpi_status ret;
ret = acpi_unload_table_id(ssdt_id);
if (ACPI_FAILURE(ret)) {
- printk(KERN_ERR "%s: acpi_unload_table_id "
- "failed (0x%x) for id %d\n",
- __FUNCTION__, ret, ssdt_id);
+ printk(KERN_ERR "%s: acpi_unload_table_id failed (0x%x) for id %d\n",
+ __func__, ret, ssdt_id);
/* try to continue on */
}
}
@@ -606,7 +588,6 @@ static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot,
static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot)
{
kfree(bss_hotplug_slot->info);
- kfree(bss_hotplug_slot->name);
kfree(bss_hotplug_slot->private);
kfree(bss_hotplug_slot);
}
@@ -614,7 +595,9 @@ 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;
+ char name[SN_SLOT_NAME_SIZE];
int rc = 0;
/*
@@ -642,19 +625,19 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
}
if (sn_hp_slot_private_alloc(bss_hotplug_slot,
- pci_bus, device)) {
+ pci_bus, device, name)) {
rc = -ENOMEM;
goto alloc_err;
}
-
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, name);
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 +647,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)
@@ -681,7 +664,7 @@ alloc_err:
return rc;
}
-static int sn_pci_hotplug_init(void)
+static int __init sn_pci_hotplug_init(void)
{
struct pci_bus *pci_bus = NULL;
int rc;
@@ -689,7 +672,7 @@ static int sn_pci_hotplug_init(void)
if (!sn_prom_feature_available(PRF_HOTPLUG_SUPPORT)) {
printk(KERN_ERR "%s: PROM version does not support hotplug.\n",
- __FUNCTION__);
+ __func__);
return -EPERM;
}
@@ -718,7 +701,7 @@ static int sn_pci_hotplug_init(void)
return registered == 1 ? 0 : -ENODEV;
}
-static void sn_pci_hotplug_exit(void)
+static void __exit sn_pci_hotplug_exit(void)
{
struct hotplug_slot *bss_hotplug_slot;
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
index 37ed0884b97..5897d516427 100644
--- a/drivers/pci/hotplug/shpchp.h
+++ b/drivers/pci/hotplug/shpchp.h
@@ -35,6 +35,7 @@
#include <linux/delay.h>
#include <linux/sched.h> /* signal_pending(), struct timer_list */
#include <linux/mutex.h>
+#include <linux/workqueue.h>
#if !defined(MODULE)
#define MY_NAME "shpchp"
@@ -42,16 +43,15 @@
#define MY_NAME THIS_MODULE->name
#endif
-extern int shpchp_poll_mode;
+extern bool shpchp_poll_mode;
extern int shpchp_poll_time;
-extern int shpchp_debug;
-extern struct workqueue_struct *shpchp_wq;
+extern bool shpchp_debug;
#define dbg(format, arg...) \
- do { \
- if (shpchp_debug) \
- printk("%s: " format, MY_NAME , ## arg); \
- } while (0)
+do { \
+ if (shpchp_debug) \
+ printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); \
+} while (0)
#define err(format, arg...) \
printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) \
@@ -59,6 +59,20 @@ extern struct workqueue_struct *shpchp_wq;
#define warn(format, arg...) \
printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
+#define ctrl_dbg(ctrl, format, arg...) \
+ do { \
+ if (shpchp_debug) \
+ dev_printk(KERN_DEBUG, &ctrl->pci_dev->dev, \
+ format, ## arg); \
+ } while (0)
+#define ctrl_err(ctrl, format, arg...) \
+ dev_err(&ctrl->pci_dev->dev, format, ## arg)
+#define ctrl_info(ctrl, format, arg...) \
+ dev_info(&ctrl->pci_dev->dev, format, ## arg)
+#define ctrl_warn(ctrl, format, arg...) \
+ dev_warn(&ctrl->pci_dev->dev, format, ## arg)
+
+
#define SLOT_NAME_SIZE 10
struct slot {
u8 bus;
@@ -69,15 +83,14 @@ struct slot {
u8 state;
u8 presence_save;
u8 pwr_save;
- struct timer_list task_event;
- u8 hp_slot;
struct controller *ctrl;
struct hpc_ops *hpc_ops;
struct hotplug_slot *hotplug_slot;
struct list_head slot_list;
- char name[SLOT_NAME_SIZE];
struct delayed_work work; /* work for button event */
struct mutex lock;
+ struct workqueue_struct *wq;
+ u8 hp_slot;
};
struct event_info {
@@ -109,7 +122,7 @@ struct controller {
#define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450
#define PCI_DEVICE_ID_AMD_POGO_7458 0x7458
-/* AMD PCIX bridge registers */
+/* AMD PCI-X bridge registers */
#define PCIX_MEM_BASE_LIMIT_OFFSET 0x1C
#define PCIX_MISCII_OFFSET 0x48
#define PCIX_MISC_BRIDGE_ERRORS_OFFSET 0x80
@@ -155,36 +168,34 @@ struct controller {
#define WRONG_BUS_FREQUENCY 0x0000000D
#define POWER_FAILURE 0x0000000E
-extern int __must_check shpchp_create_ctrl_files(struct controller *ctrl);
-extern void shpchp_remove_ctrl_files(struct controller *ctrl);
-extern int shpchp_sysfs_enable_slot(struct slot *slot);
-extern int shpchp_sysfs_disable_slot(struct slot *slot);
-extern u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl);
-extern u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl);
-extern u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl);
-extern u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl);
-extern int shpchp_configure_device(struct slot *p_slot);
-extern int shpchp_unconfigure_device(struct slot *p_slot);
-extern void cleanup_slots(struct controller *ctrl);
-extern void shpchp_queue_pushbutton_work(struct work_struct *work);
-extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev);
+int __must_check shpchp_create_ctrl_files(struct controller *ctrl);
+void shpchp_remove_ctrl_files(struct controller *ctrl);
+int shpchp_sysfs_enable_slot(struct slot *slot);
+int shpchp_sysfs_disable_slot(struct slot *slot);
+u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl);
+u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl);
+u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl);
+u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl);
+int shpchp_configure_device(struct slot *p_slot);
+int shpchp_unconfigure_device(struct slot *p_slot);
+void cleanup_slots(struct controller *ctrl);
+void shpchp_queue_pushbutton_work(struct work_struct *work);
+int shpc_init(struct controller *ctrl, struct pci_dev *pdev);
+
+static inline const char *slot_name(struct slot *slot)
+{
+ return hotplug_slot_name(slot->hotplug_slot);
+}
#ifdef CONFIG_ACPI
-static inline int get_hp_params_from_firmware(struct pci_dev *dev,
- struct hotplug_params *hpp)
+#include <linux/pci-acpi.h>
+static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev)
{
- if (ACPI_FAILURE(acpi_get_hp_params_from_firmware(dev->bus, hpp)))
- return -ENODEV;
- return 0;
+ u32 flags = OSC_PCI_SHPC_NATIVE_HP_CONTROL;
+ return acpi_get_hp_hw_control_from_firmware(dev, flags);
}
-#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)
#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 {
@@ -205,13 +216,13 @@ struct ctrl_reg {
/* offsets to the controller registers based on the above structure layout */
enum ctrl_offsets {
- BASE_OFFSET = offsetof(struct ctrl_reg, base_offset),
- SLOT_AVAIL1 = offsetof(struct ctrl_reg, slot_avail1),
+ BASE_OFFSET = offsetof(struct ctrl_reg, base_offset),
+ SLOT_AVAIL1 = offsetof(struct ctrl_reg, slot_avail1),
SLOT_AVAIL2 = offsetof(struct ctrl_reg, slot_avail2),
- SLOT_CONFIG = offsetof(struct ctrl_reg, slot_config),
+ SLOT_CONFIG = offsetof(struct ctrl_reg, slot_config),
SEC_BUS_CONFIG = offsetof(struct ctrl_reg, sec_bus_config),
MSI_CTRL = offsetof(struct ctrl_reg, msi_ctrl),
- PROG_INTERFACE = offsetof(struct ctrl_reg, prog_interface),
+ PROG_INTERFACE = offsetof(struct ctrl_reg, prog_interface),
CMD = offsetof(struct ctrl_reg, cmd),
CMD_STATUS = offsetof(struct ctrl_reg, cmd_status),
INTR_LOC = offsetof(struct ctrl_reg, intr_loc),
@@ -234,7 +245,7 @@ static inline struct slot *shpchp_find_slot(struct controller *ctrl, u8 device)
return slot;
}
- err("%s: slot (device=0x%x) not found\n", __FUNCTION__, device);
+ ctrl_err(ctrl, "Slot (device=0x%02x) not found\n", device);
return NULL;
}
@@ -268,7 +279,9 @@ static inline void amd_pogo_errata_restore_misc_reg(struct slot *p_slot)
pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, &pcix_bridge_errors_reg);
perr_set = pcix_bridge_errors_reg & PERR_OBSERVED_MASK;
if (perr_set) {
- dbg ("%s W1C: Bridge_Errors[ PERR_OBSERVED = %08X]\n",__FUNCTION__ , perr_set);
+ ctrl_dbg(p_slot->ctrl,
+ "Bridge_Errors[ PERR_OBSERVED = %08X] (W1C)\n",
+ perr_set);
pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, perr_set);
}
@@ -277,12 +290,12 @@ static inline void amd_pogo_errata_restore_misc_reg(struct slot *p_slot)
pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, &pcix_mem_base_reg);
rse_set = pcix_mem_base_reg & RSE_MASK;
if (rse_set) {
- dbg ("%s W1C: Memory_Base_Limit[ RSE ]\n",__FUNCTION__ );
+ ctrl_dbg(p_slot->ctrl, "Memory_Base_Limit[ RSE ] (W1C)\n");
pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, rse_set);
}
/* restore MiscII register */
- pci_read_config_dword( p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp );
+ pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp );
if (p_slot->ctrl->pcix_misc2_reg & SERRFATALENABLE_MASK)
pcix_misc2_temp |= SERRFATALENABLE_MASK;
@@ -321,8 +334,6 @@ struct hpc_ops {
int (*set_attention_status)(struct slot *slot, u8 status);
int (*get_latch_status)(struct slot *slot, u8 *status);
int (*get_adapter_status)(struct slot *slot, u8 *status);
- int (*get_max_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
- int (*get_cur_bus_speed)(struct slot *slot, enum pci_bus_speed *speed);
int (*get_adapter_speed)(struct slot *slot, enum pci_bus_speed *speed);
int (*get_mode1_ECC_cap)(struct slot *slot, u8 *mode);
int (*get_prog_int)(struct slot *slot, u8 *prog_int);
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index 80dec9796b3..294ef4b10cf 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -31,15 +31,14 @@
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/workqueue.h>
#include "shpchp.h"
/* Global variables */
-int shpchp_debug;
-int shpchp_poll_mode;
+bool shpchp_debug;
+bool shpchp_poll_mode;
int shpchp_poll_time;
-struct workqueue_struct *shpchp_wq;
#define DRIVER_VERSION "0.4"
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
@@ -65,12 +64,8 @@ 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);
static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
- .owner = THIS_MODULE,
.set_attention_status = set_attention_status,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
@@ -78,9 +73,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,
};
/**
@@ -91,77 +83,89 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot);
kfree(slot);
}
-static void make_slot_name(struct slot *slot)
-{
- snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%04d_%04d",
- slot->bus, 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;
+ char name[SLOT_NAME_SIZE];
+ int retval;
int i;
for (i = 0; i < ctrl->num_slots; i++) {
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
- if (!slot)
+ if (!slot) {
+ retval = -ENOMEM;
goto error;
+ }
hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
- if (!hotplug_slot)
+ if (!hotplug_slot) {
+ retval = -ENOMEM;
goto error_slot;
+ }
slot->hotplug_slot = hotplug_slot;
info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
+ if (!info) {
+ retval = -ENOMEM;
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 + (ctrl->slot_num_inc * i);
+
+ slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number);
+ if (!slot->wq) {
+ retval = -ENOMEM;
+ goto error_info;
+ }
+
mutex_init(&slot->lock);
INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work);
/* register this slot with the hotplug pci core */
hotplug_slot->private = slot;
hotplug_slot->release = &release_slot;
- make_slot_name(slot);
+ snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
hotplug_slot->ops = &shpchp_hotplug_slot_ops;
+ ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x hp_slot=%x sun=%x slot_device_offset=%x\n",
+ pci_domain_nr(ctrl->pci_dev->subordinate),
+ slot->bus, slot->device, slot->hp_slot, slot->number,
+ ctrl->slot_device_offset);
+ retval = pci_hp_register(slot->hotplug_slot,
+ ctrl->pci_dev->subordinate, slot->device, name);
+ if (retval) {
+ ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
+ retval);
+ goto error_slotwq;
+ }
+
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);
- 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);
- if (retval) {
- err("pci_hp_register failed with error %d\n", retval);
- goto error_info;
- }
-
list_add(&slot->slot_list, &ctrl->slot_list);
}
return 0;
+error_slotwq:
+ destroy_workqueue(slot->wq);
error_info:
kfree(info);
error_hpslot:
@@ -182,8 +186,7 @@ void cleanup_slots(struct controller *ctrl)
slot = list_entry(tmp, struct slot, slot_list);
list_del(&slot->slot_list);
cancel_delayed_work(&slot->work);
- flush_scheduled_work();
- flush_workqueue(shpchp_wq);
+ destroy_workqueue(slot->wq);
pci_hp_deregister(slot->hotplug_slot);
}
}
@@ -195,7 +198,8 @@ static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
{
struct slot *slot = get_slot(hotplug_slot);
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
hotplug_slot->info->attention_status = status;
slot->hpc_ops->set_attention_status(slot, status);
@@ -207,7 +211,8 @@ static int enable_slot (struct hotplug_slot *hotplug_slot)
{
struct slot *slot = get_slot(hotplug_slot);
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
return shpchp_sysfs_enable_slot(slot);
}
@@ -216,7 +221,8 @@ static int disable_slot (struct hotplug_slot *hotplug_slot)
{
struct slot *slot = get_slot(hotplug_slot);
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
return shpchp_sysfs_disable_slot(slot);
}
@@ -226,7 +232,8 @@ static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
struct slot *slot = get_slot(hotplug_slot);
int retval;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
retval = slot->hpc_ops->get_power_status(slot, value);
if (retval < 0)
@@ -240,7 +247,8 @@ static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
struct slot *slot = get_slot(hotplug_slot);
int retval;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
retval = slot->hpc_ops->get_attention_status(slot, value);
if (retval < 0)
@@ -254,7 +262,8 @@ static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
struct slot *slot = get_slot(hotplug_slot);
int retval;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
retval = slot->hpc_ops->get_latch_status(slot, value);
if (retval < 0)
@@ -268,7 +277,8 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
struct slot *slot = get_slot(hotplug_slot);
int retval;
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+ __func__, slot_name(slot));
retval = slot->hpc_ops->get_adapter_status(slot, value);
if (retval < 0)
@@ -277,55 +287,16 @@ 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", __FUNCTION__, 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)
-{
- struct slot *slot = get_slot(hotplug_slot);
- int retval;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- retval = slot->hpc_ops->get_max_bus_speed(slot, value);
- if (retval < 0)
- *value = PCI_SPEED_UNKNOWN;
-
- return 0;
-}
-
-static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
-{
- struct slot *slot = get_slot(hotplug_slot);
- int retval;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- retval = slot->hpc_ops->get_cur_bus_speed(slot, value);
- if (retval < 0)
- *value = PCI_SPEED_UNKNOWN;
-
- return 0;
-}
-
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)
@@ -338,15 +309,14 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
if (!ctrl) {
- err("%s : out of memory\n", __FUNCTION__);
+ dev_err(&pdev->dev, "%s: Out of memory\n", __func__);
goto err_out_none;
}
INIT_LIST_HEAD(&ctrl->slot_list);
rc = shpc_init(ctrl, pdev);
if (rc) {
- dbg("%s: controller initialization failed\n",
- SHPC_MODULE_NAME);
+ ctrl_dbg(ctrl, "Controller initialization failed\n");
goto err_out_free_ctrl;
}
@@ -355,7 +325,7 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Setup the slot information structures */
rc = init_slots(ctrl);
if (rc) {
- err("%s: slot initialization failed\n", SHPC_MODULE_NAME);
+ ctrl_err(ctrl, "Slot initialization failed\n");
goto err_out_release_ctlr;
}
@@ -399,11 +369,12 @@ static struct pci_driver shpc_driver = {
static int __init shpcd_init(void)
{
- int retval = 0;
+ int retval;
retval = pci_register_driver(&shpc_driver);
- dbg("%s: pci_register_driver = %d\n", __FUNCTION__, retval);
+ dbg("%s: pci_register_driver = %d\n", __func__, retval);
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
return retval;
}
diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c
index eb5cac6f08a..a81fb67ea9a 100644
--- a/drivers/pci/hotplug/shpchp_ctrl.c
+++ b/drivers/pci/hotplug/shpchp_ctrl.c
@@ -30,8 +30,8 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/workqueue.h>
#include "../pci.h"
#include "shpchp.h"
@@ -51,7 +51,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
info->p_slot = p_slot;
INIT_WORK(&info->work, interrupt_event_handler);
- schedule_work(&info->work);
+ queue_work(p_slot->wq, &info->work);
return 0;
}
@@ -62,7 +62,7 @@ u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
u32 event_type;
/* Attention Button Change */
- dbg("shpchp: Attention button interrupt received.\n");
+ ctrl_dbg(ctrl, "Attention button interrupt received\n");
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
@@ -70,7 +70,7 @@ u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
/*
* Button pressed - See if need to TAKE ACTION!!!
*/
- info("Button pressed on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot));
event_type = INT_BUTTON_PRESS;
queue_interrupt_event(p_slot, event_type);
@@ -86,29 +86,29 @@ u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
u32 event_type;
/* Switch Change */
- dbg("shpchp: Switch interrupt received.\n");
+ ctrl_dbg(ctrl, "Switch interrupt received\n");
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
- dbg("%s: Card present %x Power status %x\n", __FUNCTION__,
- p_slot->presence_save, p_slot->pwr_save);
+ ctrl_dbg(ctrl, "Card present %x Power status %x\n",
+ p_slot->presence_save, p_slot->pwr_save);
if (getstatus) {
/*
* Switch opened
*/
- info("Latch open on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot));
event_type = INT_SWITCH_OPEN;
if (p_slot->pwr_save && p_slot->presence_save) {
event_type = INT_POWER_FAULT;
- err("Surprise Removal of card\n");
+ ctrl_err(ctrl, "Surprise Removal of card\n");
}
} else {
/*
* Switch closed
*/
- info("Latch close on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot));
event_type = INT_SWITCH_CLOSE;
}
@@ -123,7 +123,7 @@ u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
u32 event_type;
/* Presence Change */
- dbg("shpchp: Presence/Notify input change.\n");
+ ctrl_dbg(ctrl, "Presence/Notify input change\n");
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
@@ -135,13 +135,15 @@ u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
/*
* Card Present
*/
- info("Card present on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Card present on Slot(%s)\n",
+ slot_name(p_slot));
event_type = INT_PRESENCE_ON;
} else {
/*
* Not Present
*/
- info("Card not present on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Card not present on Slot(%s)\n",
+ slot_name(p_slot));
event_type = INT_PRESENCE_OFF;
}
@@ -156,26 +158,27 @@ u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
u32 event_type;
/* Power fault */
- dbg("shpchp: Power fault interrupt received.\n");
+ ctrl_dbg(ctrl, "Power fault interrupt received\n");
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
- if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
+ if (!(p_slot->hpc_ops->query_power_fault(p_slot))) {
/*
* Power fault Cleared
*/
- info("Power fault cleared on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Power fault cleared on Slot(%s)\n",
+ slot_name(p_slot));
p_slot->status = 0x00;
event_type = INT_POWER_FAULT_CLEAR;
} else {
/*
* Power fault
*/
- info("Power fault on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Power fault on Slot(%s)\n", slot_name(p_slot));
event_type = INT_POWER_FAULT;
/* set power fault status for this board */
p_slot->status = 0xFF;
- info("power fault bit %x set\n", hp_slot);
+ ctrl_info(ctrl, "Power fault bit %x set\n", hp_slot);
}
queue_interrupt_event(p_slot, event_type);
@@ -191,10 +194,10 @@ static int change_bus_speed(struct controller *ctrl, struct slot *p_slot,
{
int rc = 0;
- dbg("%s: change to speed %d\n", __FUNCTION__, speed);
+ ctrl_dbg(ctrl, "Change speed to %d\n", speed);
if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) {
- err("%s: Issue of set bus speed mode command failed\n",
- __FUNCTION__);
+ ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",
+ __func__);
return WRONG_BUS_FREQUENCY;
}
return rc;
@@ -212,8 +215,8 @@ static int fix_bus_speed(struct controller *ctrl, struct slot *pslot,
*/
if (flag) {
if (asp < bsp) {
- err("%s: speed of bus %x and adapter %x mismatch\n",
- __FUNCTION__, bsp, asp);
+ ctrl_err(ctrl, "Speed of bus %x and adapter %x mismatch\n",
+ bsp, asp);
rc = WRONG_BUS_FREQUENCY;
}
return rc;
@@ -243,62 +246,50 @@ static int board_added(struct slot *p_slot)
int rc = 0;
enum pci_bus_speed asp, bsp, msp;
struct controller *ctrl = p_slot->ctrl;
+ struct pci_bus *parent = ctrl->pci_dev->subordinate;
hp_slot = p_slot->device - ctrl->slot_device_offset;
- dbg("%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n",
- __FUNCTION__, p_slot->device,
- ctrl->slot_device_offset, hp_slot);
+ ctrl_dbg(ctrl, "%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n",
+ __func__, p_slot->device, ctrl->slot_device_offset, hp_slot);
/* Power on slot without connecting to bus */
rc = p_slot->hpc_ops->power_on_slot(p_slot);
if (rc) {
- err("%s: Failed to power on slot\n", __FUNCTION__);
+ ctrl_err(ctrl, "Failed to power on slot\n");
return -1;
}
if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) {
- if (slots_not_empty)
- return WRONG_BUS_FREQUENCY;
-
if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) {
- err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
+ ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",
+ __func__);
return WRONG_BUS_FREQUENCY;
}
/* turn on board, blink green LED, turn off Amber LED */
if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
- err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
+ ctrl_err(ctrl, "Issue of Slot Enable command failed\n");
return rc;
}
}
rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &asp);
if (rc) {
- err("%s: Can't get adapter speed or bus mode mismatch\n",
- __FUNCTION__);
+ ctrl_err(ctrl, "Can't get adapter speed or bus mode mismatch\n");
return WRONG_BUS_FREQUENCY;
}
- rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bsp);
- if (rc) {
- err("%s: Can't get bus operation speed\n", __FUNCTION__);
- return WRONG_BUS_FREQUENCY;
- }
-
- rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &msp);
- if (rc) {
- err("%s: Can't get max bus operation speed\n", __FUNCTION__);
- msp = bsp;
- }
+ bsp = ctrl->pci_dev->subordinate->cur_bus_speed;
+ msp = ctrl->pci_dev->subordinate->max_bus_speed;
/* Check if there are other slots or devices on the same bus */
if (!list_empty(&ctrl->pci_dev->subordinate->devices))
slots_not_empty = 1;
- dbg("%s: slots_not_empty %d, adapter_speed %d, bus_speed %d, "
- "max_bus_speed %d\n", __FUNCTION__, slots_not_empty, asp,
- bsp, msp);
+ ctrl_dbg(ctrl, "%s: slots_not_empty %d, adapter_speed %d, bus_speed %d, max_bus_speed %d\n",
+ __func__, slots_not_empty, asp,
+ bsp, msp);
rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, asp, bsp, msp);
if (rc)
@@ -306,26 +297,26 @@ static int board_added(struct slot *p_slot)
/* turn on board, blink green LED, turn off Amber LED */
if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
- err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
+ ctrl_err(ctrl, "Issue of Slot Enable command failed\n");
return rc;
}
/* Wait for ~1 second */
msleep(1000);
- dbg("%s: slot status = %x\n", __FUNCTION__, p_slot->status);
+ ctrl_dbg(ctrl, "%s: slot status = %x\n", __func__, p_slot->status);
/* Check for a power fault */
if (p_slot->status == 0xFF) {
/* power fault occurred, but it was benign */
- dbg("%s: power fault\n", __FUNCTION__);
+ ctrl_dbg(ctrl, "%s: Power fault\n", __func__);
rc = POWER_FAILURE;
p_slot->status = 0;
goto err_exit;
}
if (shpchp_configure_device(p_slot)) {
- err("Cannot add device at 0x%x:0x%x\n", p_slot->bus,
- p_slot->device);
+ ctrl_err(ctrl, "Cannot add device at %04x:%02x:%02x\n",
+ pci_domain_nr(parent), p_slot->bus, p_slot->device);
goto err_exit;
}
@@ -341,7 +332,8 @@ err_exit:
/* turn off slot, turn on Amber LED, turn off Green LED */
rc = p_slot->hpc_ops->slot_disable(p_slot);
if (rc) {
- err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
+ ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n",
+ __func__);
return rc;
}
@@ -365,7 +357,7 @@ static int remove_board(struct slot *p_slot)
hp_slot = p_slot->device - ctrl->slot_device_offset;
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
- dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
+ ctrl_dbg(ctrl, "%s: hp_slot = %d\n", __func__, hp_slot);
/* Change status to shutdown */
if (p_slot->is_a_board)
@@ -374,13 +366,14 @@ static int remove_board(struct slot *p_slot)
/* turn off slot, turn on Amber LED, turn off Green LED */
rc = p_slot->hpc_ops->slot_disable(p_slot);
if (rc) {
- err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
+ ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n",
+ __func__);
return rc;
}
rc = p_slot->hpc_ops->set_attention_status(p_slot, 0);
if (rc) {
- err("%s: Issue of Set Attention command failed\n", __FUNCTION__);
+ ctrl_err(ctrl, "Issue of Set Attention command failed\n");
return rc;
}
@@ -439,7 +432,8 @@ void shpchp_queue_pushbutton_work(struct work_struct *work)
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
- err("%s: Cannot allocate memory\n", __FUNCTION__);
+ ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
+ __func__);
return;
}
info->p_slot = p_slot;
@@ -454,9 +448,10 @@ void shpchp_queue_pushbutton_work(struct work_struct *work)
p_slot->state = POWERON_STATE;
break;
default:
+ kfree(info);
goto out;
}
- queue_work(shpchp_wq, &info->work);
+ queue_work(p_slot->wq, &info->work);
out:
mutex_unlock(&p_slot->lock);
}
@@ -486,24 +481,25 @@ static int update_slot_info (struct slot *slot)
static void handle_button_press_event(struct slot *p_slot)
{
u8 getstatus;
+ struct controller *ctrl = p_slot->ctrl;
switch (p_slot->state) {
case STATIC_STATE:
p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (getstatus) {
p_slot->state = BLINKINGOFF_STATE;
- info("PCI slot #%s - powering off due to button "
- "press.\n", p_slot->name);
+ ctrl_info(ctrl, "PCI slot #%s - powering off due to button press\n",
+ slot_name(p_slot));
} else {
p_slot->state = BLINKINGON_STATE;
- info("PCI slot #%s - powering on due to button "
- "press.\n", p_slot->name);
+ ctrl_info(ctrl, "PCI slot #%s - powering on due to button press\n",
+ slot_name(p_slot));
}
/* blink green LED and turn off amber */
p_slot->hpc_ops->green_led_blink(p_slot);
p_slot->hpc_ops->set_attention_status(p_slot, 0);
- schedule_delayed_work(&p_slot->work, 5*HZ);
+ queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
break;
case BLINKINGOFF_STATE:
case BLINKINGON_STATE:
@@ -512,16 +508,16 @@ static void handle_button_press_event(struct slot *p_slot)
* press the attention again before the 5 sec. limit
* expires to cancel hot-add or hot-remove
*/
- info("Button cancel on Slot(%s)\n", p_slot->name);
- dbg("%s: button cancel\n", __FUNCTION__);
+ ctrl_info(ctrl, "Button cancel on Slot(%s)\n",
+ slot_name(p_slot));
cancel_delayed_work(&p_slot->work);
if (p_slot->state == BLINKINGOFF_STATE)
p_slot->hpc_ops->green_led_on(p_slot);
else
p_slot->hpc_ops->green_led_off(p_slot);
p_slot->hpc_ops->set_attention_status(p_slot, 0);
- info("PCI slot #%s - action canceled due to button press\n",
- p_slot->name);
+ ctrl_info(ctrl, "PCI slot #%s - action canceled due to button press\n",
+ slot_name(p_slot));
p_slot->state = STATIC_STATE;
break;
case POWEROFF_STATE:
@@ -531,11 +527,12 @@ static void handle_button_press_event(struct slot *p_slot)
* this means that the previous attention button action
* to hot-add or hot-remove is undergoing
*/
- info("Button ignore on Slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Button ignore on Slot(%s)\n",
+ slot_name(p_slot));
update_slot_info(p_slot);
break;
default:
- warn("Not a valid state\n");
+ ctrl_warn(ctrl, "Not a valid state\n");
break;
}
}
@@ -551,7 +548,7 @@ static void interrupt_event_handler(struct work_struct *work)
handle_button_press_event(p_slot);
break;
case INT_POWER_FAULT:
- dbg("%s: power fault\n", __FUNCTION__);
+ ctrl_dbg(p_slot->ctrl, "%s: Power fault\n", __func__);
p_slot->hpc_ops->set_attention_status(p_slot, 1);
p_slot->hpc_ops->green_led_off(p_slot);
break;
@@ -569,22 +566,24 @@ static int shpchp_enable_slot (struct slot *p_slot)
{
u8 getstatus = 0;
int rc, retval = -ENODEV;
+ struct controller *ctrl = p_slot->ctrl;
/* Check to see if (latch closed, card present, power off) */
mutex_lock(&p_slot->ctrl->crit_sect);
rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
if (rc || !getstatus) {
- info("No adapter on slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
goto out;
}
rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (rc || getstatus) {
- info("Latch open on slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot));
goto out;
}
rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (rc || getstatus) {
- info("Already enabled on slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Already enabled on slot(%s)\n",
+ slot_name(p_slot));
goto out;
}
@@ -593,7 +592,7 @@ static int shpchp_enable_slot (struct slot *p_slot)
/* We have to save the presence info for these slots */
p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
p_slot->hpc_ops->get_power_status(p_slot, &(p_slot->pwr_save));
- dbg("%s: p_slot->pwr_save %x\n", __FUNCTION__, p_slot->pwr_save);
+ ctrl_dbg(ctrl, "%s: p_slot->pwr_save %x\n", __func__, p_slot->pwr_save);
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if(((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) ||
@@ -624,6 +623,7 @@ static int shpchp_disable_slot (struct slot *p_slot)
{
u8 getstatus = 0;
int rc, retval = -ENODEV;
+ struct controller *ctrl = p_slot->ctrl;
if (!p_slot->ctrl)
return -ENODEV;
@@ -633,17 +633,18 @@ static int shpchp_disable_slot (struct slot *p_slot)
rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
if (rc || !getstatus) {
- info("No adapter on slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
goto out;
}
rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (rc || getstatus) {
- info("Latch open on slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot));
goto out;
}
rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (rc || !getstatus) {
- info("Already disabled slot(%s)\n", p_slot->name);
+ ctrl_info(ctrl, "Already disabled on slot(%s)\n",
+ slot_name(p_slot));
goto out;
}
@@ -657,6 +658,7 @@ static int shpchp_disable_slot (struct slot *p_slot)
int shpchp_sysfs_enable_slot(struct slot *p_slot)
{
int retval = -ENODEV;
+ struct controller *ctrl = p_slot->ctrl;
mutex_lock(&p_slot->lock);
switch (p_slot->state) {
@@ -670,15 +672,17 @@ int shpchp_sysfs_enable_slot(struct slot *p_slot)
p_slot->state = STATIC_STATE;
break;
case POWERON_STATE:
- info("Slot %s is already in powering on state\n",
- p_slot->name);
+ ctrl_info(ctrl, "Slot %s is already in powering on state\n",
+ slot_name(p_slot));
break;
case BLINKINGOFF_STATE:
case POWEROFF_STATE:
- info("Already enabled on slot %s\n", p_slot->name);
+ ctrl_info(ctrl, "Already enabled on slot %s\n",
+ slot_name(p_slot));
break;
default:
- err("Not a valid state on slot %s\n", p_slot->name);
+ ctrl_err(ctrl, "Not a valid state on slot %s\n",
+ slot_name(p_slot));
break;
}
mutex_unlock(&p_slot->lock);
@@ -689,6 +693,7 @@ int shpchp_sysfs_enable_slot(struct slot *p_slot)
int shpchp_sysfs_disable_slot(struct slot *p_slot)
{
int retval = -ENODEV;
+ struct controller *ctrl = p_slot->ctrl;
mutex_lock(&p_slot->lock);
switch (p_slot->state) {
@@ -702,15 +707,17 @@ int shpchp_sysfs_disable_slot(struct slot *p_slot)
p_slot->state = STATIC_STATE;
break;
case POWEROFF_STATE:
- info("Slot %s is already in powering off state\n",
- p_slot->name);
+ ctrl_info(ctrl, "Slot %s is already in powering off state\n",
+ slot_name(p_slot));
break;
case BLINKINGON_STATE:
case POWERON_STATE:
- info("Already disabled on slot %s\n", p_slot->name);
+ ctrl_info(ctrl, "Already disabled on slot %s\n",
+ slot_name(p_slot));
break;
default:
- err("Not a valid state on slot %s\n", p_slot->name);
+ ctrl_err(ctrl, "Not a valid state on slot %s\n",
+ slot_name(p_slot));
break;
}
mutex_unlock(&p_slot->lock);
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index e8aa138128c..29e22352822 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -113,10 +113,10 @@
#define CON_PFAULT_INTR_MASK (1 << 28)
#define MRL_CHANGE_SERR_MASK (1 << 29)
#define CON_PFAULT_SERR_MASK (1 << 30)
-#define SLOT_REG_RSVDZ_MASK (1 << 15) | (7 << 21)
+#define SLOT_REG_RSVDZ_MASK ((1 << 15) | (7 << 21))
/*
- * SHPC Command Code definitnions
+ * SHPC Command Code definitions
*
* Slot Operation 00h - 3Fh
* Set Bus Segment Speed/Mode A 40h - 47h
@@ -179,8 +179,6 @@
#define SLOT_EVENT_LATCH 0x2
#define SLOT_SERR_INT_MASK 0x3
-static atomic_t shpchp_num_controllers = ATOMIC_INIT(0);
-
static irqreturn_t shpc_isr(int irq, void *dev_id);
static void start_int_poll_timer(struct controller *ctrl, int sec);
static int hpc_check_cmd_status(struct controller *ctrl);
@@ -300,10 +298,10 @@ static inline int shpc_wait_cmd(struct controller *ctrl)
!is_ctrl_busy(ctrl), timeout);
if (!rc && is_ctrl_busy(ctrl)) {
retval = -EIO;
- err("Command not completed in 1000 msec\n");
+ ctrl_err(ctrl, "Command not completed in 1000 msec\n");
} else if (rc < 0) {
retval = -EINTR;
- info("Command was interrupted by a signal\n");
+ ctrl_info(ctrl, "Command was interrupted by a signal\n");
}
return retval;
@@ -320,15 +318,14 @@ static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd)
if (!shpc_poll_ctrl_busy(ctrl)) {
/* After 1 sec and and the controller is still busy */
- err("%s : Controller is still busy after 1 sec.\n",
- __FUNCTION__);
+ ctrl_err(ctrl, "Controller is still busy after 1 sec\n");
retval = -EBUSY;
goto out;
}
++t_slot;
temp_word = (t_slot << 8) | (cmd & 0xFF);
- dbg("%s: t_slot %x cmd %x\n", __FUNCTION__, t_slot, cmd);
+ ctrl_dbg(ctrl, "%s: t_slot %x cmd %x\n", __func__, t_slot, cmd);
/* To make sure the Controller Busy bit is 0 before we send out the
* command.
@@ -344,8 +341,8 @@ static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd)
cmd_status = hpc_check_cmd_status(slot->ctrl);
if (cmd_status) {
- err("%s: Failed to issued command 0x%x (error code = %d)\n",
- __FUNCTION__, cmd, cmd_status);
+ ctrl_err(ctrl, "Failed to issued command 0x%x (error code = %d)\n",
+ cmd, cmd_status);
retval = -EIO;
}
out:
@@ -364,15 +361,15 @@ static int hpc_check_cmd_status(struct controller *ctrl)
break;
case 1:
retval = SWITCH_OPEN;
- err("%s: Switch opened!\n", __FUNCTION__);
+ ctrl_err(ctrl, "Switch opened!\n");
break;
case 2:
retval = INVALID_CMD;
- err("%s: Invalid HPC command!\n", __FUNCTION__);
+ ctrl_err(ctrl, "Invalid HPC command!\n");
break;
case 4:
retval = INVALID_SPEED_MODE;
- err("%s: Invalid bus speed/mode!\n", __FUNCTION__);
+ ctrl_err(ctrl, "Invalid bus speed/mode!\n");
break;
default:
retval = cmd_status;
@@ -406,7 +403,7 @@ static int hpc_get_attention_status(struct slot *slot, u8 *status)
return 0;
}
-static int hpc_get_power_status(struct slot * slot, u8 *status)
+static int hpc_get_power_status(struct slot *slot, u8 *status)
{
struct controller *ctrl = slot->ctrl;
u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot));
@@ -483,8 +480,8 @@ static int hpc_get_adapter_speed(struct slot *slot, enum pci_bus_speed *value)
return -ENODEV;
}
- dbg("%s: slot_reg = %x, pcix_cap = %x, m66_cap = %x\n",
- __FUNCTION__, slot_reg, pcix_cap, m66_cap);
+ ctrl_dbg(ctrl, "%s: slot_reg = %x, pcix_cap = %x, m66_cap = %x\n",
+ __func__, slot_reg, pcix_cap, m66_cap);
switch (pcix_cap) {
case 0x0:
@@ -509,7 +506,7 @@ static int hpc_get_adapter_speed(struct slot *slot, enum pci_bus_speed *value)
break;
}
- dbg("Adapter speed = %d\n", *value);
+ ctrl_dbg(ctrl, "Adapter speed = %d\n", *value);
return retval;
}
@@ -526,11 +523,11 @@ static int hpc_get_mode1_ECC_cap(struct slot *slot, u8 *mode)
retval = -1;
}
- dbg("Mode 1 ECC cap = %d\n", *mode);
+ ctrl_dbg(ctrl, "Mode 1 ECC cap = %d\n", *mode);
return retval;
}
-static int hpc_query_power_fault(struct slot * slot)
+static int hpc_query_power_fault(struct slot *slot)
{
struct controller *ctrl = slot->ctrl;
u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot));
@@ -614,27 +611,20 @@ static void hpc_release_ctlr(struct controller *ctrl)
iounmap(ctrl->creg);
release_mem_region(ctrl->mmio_base, ctrl->mmio_size);
-
- /*
- * If this is the last controller to be released, destroy the
- * shpchpd work queue
- */
- if (atomic_dec_and_test(&shpchp_num_controllers))
- destroy_workqueue(shpchp_wq);
}
-static int hpc_power_on_slot(struct slot * slot)
+static int hpc_power_on_slot(struct slot *slot)
{
int retval;
retval = shpc_write_cmd(slot, slot->hp_slot, SET_SLOT_PWR);
if (retval)
- err("%s: Write command failed!\n", __FUNCTION__);
+ ctrl_err(slot->ctrl, "%s: Write command failed!\n", __func__);
return retval;
}
-static int hpc_slot_enable(struct slot * slot)
+static int hpc_slot_enable(struct slot *slot)
{
int retval;
@@ -642,12 +632,12 @@ static int hpc_slot_enable(struct slot * slot)
retval = shpc_write_cmd(slot, slot->hp_slot,
SET_SLOT_ENABLE | SET_PWR_BLINK | SET_ATTN_OFF);
if (retval)
- err("%s: Write command failed!\n", __FUNCTION__);
+ ctrl_err(slot->ctrl, "%s: Write command failed!\n", __func__);
return retval;
}
-static int hpc_slot_disable(struct slot * slot)
+static int hpc_slot_disable(struct slot *slot)
{
int retval;
@@ -655,12 +645,81 @@ static int hpc_slot_disable(struct slot * slot)
retval = shpc_write_cmd(slot, slot->hp_slot,
SET_SLOT_DISABLE | SET_PWR_OFF | SET_ATTN_ON);
if (retval)
- err("%s: Write command failed!\n", __FUNCTION__);
+ ctrl_err(slot->ctrl, "%s: Write command failed!\n", __func__);
+
+ return retval;
+}
+
+static int shpc_get_cur_bus_speed(struct controller *ctrl)
+{
+ int retval = 0;
+ struct pci_bus *bus = ctrl->pci_dev->subordinate;
+ enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
+ u16 sec_bus_reg = shpc_readw(ctrl, SEC_BUS_CONFIG);
+ u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
+ u8 speed_mode = (pi == 2) ? (sec_bus_reg & 0xF) : (sec_bus_reg & 0x7);
+
+ if ((pi == 1) && (speed_mode > 4)) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ switch (speed_mode) {
+ case 0x0:
+ bus_speed = PCI_SPEED_33MHz;
+ break;
+ case 0x1:
+ bus_speed = PCI_SPEED_66MHz;
+ break;
+ case 0x2:
+ bus_speed = PCI_SPEED_66MHz_PCIX;
+ break;
+ case 0x3:
+ bus_speed = PCI_SPEED_100MHz_PCIX;
+ break;
+ case 0x4:
+ bus_speed = PCI_SPEED_133MHz_PCIX;
+ break;
+ case 0x5:
+ bus_speed = PCI_SPEED_66MHz_PCIX_ECC;
+ break;
+ case 0x6:
+ bus_speed = PCI_SPEED_100MHz_PCIX_ECC;
+ break;
+ case 0x7:
+ bus_speed = PCI_SPEED_133MHz_PCIX_ECC;
+ break;
+ case 0x8:
+ bus_speed = PCI_SPEED_66MHz_PCIX_266;
+ break;
+ case 0x9:
+ bus_speed = PCI_SPEED_100MHz_PCIX_266;
+ break;
+ case 0xa:
+ bus_speed = PCI_SPEED_133MHz_PCIX_266;
+ break;
+ case 0xb:
+ bus_speed = PCI_SPEED_66MHz_PCIX_533;
+ break;
+ case 0xc:
+ bus_speed = PCI_SPEED_100MHz_PCIX_533;
+ break;
+ case 0xd:
+ bus_speed = PCI_SPEED_133MHz_PCIX_533;
+ break;
+ default:
+ retval = -ENODEV;
+ break;
+ }
+ out:
+ bus->cur_bus_speed = bus_speed;
+ dbg("Current bus speed = %d\n", bus_speed);
return retval;
}
-static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value)
+
+static int hpc_set_bus_speed_mode(struct slot *slot, enum pci_bus_speed value)
{
int retval;
struct controller *ctrl = slot->ctrl;
@@ -719,7 +778,9 @@ static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value)
retval = shpc_write_cmd(slot, 0, cmd);
if (retval)
- err("%s: Write command failed!\n", __FUNCTION__);
+ ctrl_err(ctrl, "%s: Write command failed!\n", __func__);
+ else
+ shpc_get_cur_bus_speed(ctrl);
return retval;
}
@@ -735,7 +796,7 @@ static irqreturn_t shpc_isr(int irq, void *dev_id)
if (!intr_loc)
return IRQ_NONE;
- dbg("%s: intr_loc = %x\n",__FUNCTION__, intr_loc);
+ ctrl_dbg(ctrl, "%s: intr_loc = %x\n", __func__, intr_loc);
if(!shpchp_poll_mode) {
/*
@@ -748,7 +809,7 @@ static irqreturn_t shpc_isr(int irq, void *dev_id)
shpc_writel(ctrl, SERR_INTR_ENABLE, serr_int);
intr_loc2 = shpc_readl(ctrl, INTR_LOC);
- dbg("%s: intr_loc2 = %x\n",__FUNCTION__, intr_loc2);
+ ctrl_dbg(ctrl, "%s: intr_loc2 = %x\n", __func__, intr_loc2);
}
if (intr_loc & CMD_INTR_PENDING) {
@@ -773,8 +834,8 @@ static irqreturn_t shpc_isr(int irq, void *dev_id)
continue;
slot_reg = shpc_readl(ctrl, SLOT_REG(hp_slot));
- dbg("%s: Slot %x with intr, slot register = %x\n",
- __FUNCTION__, hp_slot, slot_reg);
+ ctrl_dbg(ctrl, "Slot %x with intr, slot register = %x\n",
+ hp_slot, slot_reg);
if (slot_reg & MRL_CHANGE_DETECTED)
shpchp_handle_switch_change(hp_slot, ctrl);
@@ -803,10 +864,10 @@ static irqreturn_t shpc_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int hpc_get_max_bus_speed (struct slot *slot, enum pci_bus_speed *value)
+static int shpc_get_max_bus_speed(struct controller *ctrl)
{
int retval = 0;
- struct controller *ctrl = slot->ctrl;
+ struct pci_bus *bus = ctrl->pci_dev->subordinate;
enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
u32 slot_avail1 = shpc_readl(ctrl, SLOT_AVAIL1);
@@ -842,79 +903,12 @@ static int hpc_get_max_bus_speed (struct slot *slot, enum pci_bus_speed *value)
retval = -ENODEV;
}
- *value = bus_speed;
- dbg("Max bus speed = %d\n", bus_speed);
+ bus->max_bus_speed = bus_speed;
+ ctrl_dbg(ctrl, "Max bus speed = %d\n", bus_speed);
return retval;
}
-static int hpc_get_cur_bus_speed (struct slot *slot, enum pci_bus_speed *value)
-{
- int retval = 0;
- struct controller *ctrl = slot->ctrl;
- enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
- u16 sec_bus_reg = shpc_readw(ctrl, SEC_BUS_CONFIG);
- u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
- u8 speed_mode = (pi == 2) ? (sec_bus_reg & 0xF) : (sec_bus_reg & 0x7);
-
- if ((pi == 1) && (speed_mode > 4)) {
- *value = PCI_SPEED_UNKNOWN;
- return -ENODEV;
- }
-
- switch (speed_mode) {
- case 0x0:
- *value = PCI_SPEED_33MHz;
- break;
- case 0x1:
- *value = PCI_SPEED_66MHz;
- break;
- case 0x2:
- *value = PCI_SPEED_66MHz_PCIX;
- break;
- case 0x3:
- *value = PCI_SPEED_100MHz_PCIX;
- break;
- case 0x4:
- *value = PCI_SPEED_133MHz_PCIX;
- break;
- case 0x5:
- *value = PCI_SPEED_66MHz_PCIX_ECC;
- break;
- case 0x6:
- *value = PCI_SPEED_100MHz_PCIX_ECC;
- break;
- case 0x7:
- *value = PCI_SPEED_133MHz_PCIX_ECC;
- break;
- case 0x8:
- *value = PCI_SPEED_66MHz_PCIX_266;
- break;
- case 0x9:
- *value = PCI_SPEED_100MHz_PCIX_266;
- break;
- case 0xa:
- *value = PCI_SPEED_133MHz_PCIX_266;
- break;
- case 0xb:
- *value = PCI_SPEED_66MHz_PCIX_533;
- break;
- case 0xc:
- *value = PCI_SPEED_100MHz_PCIX_533;
- break;
- case 0xd:
- *value = PCI_SPEED_133MHz_PCIX_533;
- break;
- default:
- *value = PCI_SPEED_UNKNOWN;
- retval = -ENODEV;
- break;
- }
-
- dbg("Current bus speed = %d\n", bus_speed);
- return retval;
-}
-
static struct hpc_ops shpchp_hpc_ops = {
.power_on_slot = hpc_power_on_slot,
.slot_enable = hpc_slot_enable,
@@ -926,8 +920,6 @@ static struct hpc_ops shpchp_hpc_ops = {
.get_latch_status = hpc_get_latch_status,
.get_adapter_status = hpc_get_adapter_status,
- .get_max_bus_speed = hpc_get_max_bus_speed,
- .get_cur_bus_speed = hpc_get_cur_bus_speed,
.get_adapter_speed = hpc_get_adapter_speed,
.get_mode1_ECC_cap = hpc_get_mode1_ECC_cap,
.get_prog_int = hpc_get_prog_int,
@@ -949,43 +941,43 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
u8 i;
ctrl->pci_dev = pdev; /* pci_dev of the P2P bridge */
+ ctrl_dbg(ctrl, "Hotplug Controller:\n");
- if ((pdev->vendor == PCI_VENDOR_ID_AMD) || (pdev->device ==
- PCI_DEVICE_ID_AMD_GOLAM_7450)) {
+ if (pdev->vendor == PCI_VENDOR_ID_AMD &&
+ pdev->device == PCI_DEVICE_ID_AMD_GOLAM_7450) {
/* amd shpc driver doesn't use Base Offset; assume 0 */
ctrl->mmio_base = pci_resource_start(pdev, 0);
ctrl->mmio_size = pci_resource_len(pdev, 0);
} else {
ctrl->cap_offset = pci_find_capability(pdev, PCI_CAP_ID_SHPC);
if (!ctrl->cap_offset) {
- err("%s : cap_offset == 0\n", __FUNCTION__);
+ ctrl_err(ctrl, "Cannot find PCI capability\n");
goto abort;
}
- dbg("%s: cap_offset = %x\n", __FUNCTION__, ctrl->cap_offset);
+ ctrl_dbg(ctrl, " cap_offset = %x\n", ctrl->cap_offset);
rc = shpc_indirect_read(ctrl, 0, &shpc_base_offset);
if (rc) {
- err("%s: cannot read base_offset\n", __FUNCTION__);
+ ctrl_err(ctrl, "Cannot read base_offset\n");
goto abort;
}
rc = shpc_indirect_read(ctrl, 3, &tempdword);
if (rc) {
- err("%s: cannot read slot config\n", __FUNCTION__);
+ ctrl_err(ctrl, "Cannot read slot config\n");
goto abort;
}
num_slots = tempdword & SLOT_NUM;
- dbg("%s: num_slots (indirect) %x\n", __FUNCTION__, num_slots);
+ ctrl_dbg(ctrl, " num_slots (indirect) %x\n", num_slots);
for (i = 0; i < 9 + num_slots; i++) {
rc = shpc_indirect_read(ctrl, i, &tempdword);
if (rc) {
- err("%s: cannot read creg (index = %d)\n",
- __FUNCTION__, i);
+ ctrl_err(ctrl, "Cannot read creg (index = %d)\n",
+ i);
goto abort;
}
- dbg("%s: offset %d: value %x\n", __FUNCTION__,i,
- tempdword);
+ ctrl_dbg(ctrl, " offset %d: value %x\n", i, tempdword);
}
ctrl->mmio_base =
@@ -993,30 +985,31 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
ctrl->mmio_size = 0x24 + 0x4 * num_slots;
}
- 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);
+ ctrl_info(ctrl, "HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",
+ pdev->vendor, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
rc = pci_enable_device(pdev);
if (rc) {
- err("%s: pci_enable_device failed\n", __FUNCTION__);
+ ctrl_err(ctrl, "pci_enable_device failed\n");
goto abort;
}
if (!request_mem_region(ctrl->mmio_base, ctrl->mmio_size, MY_NAME)) {
- err("%s: cannot reserve MMIO region\n", __FUNCTION__);
+ ctrl_err(ctrl, "Cannot reserve MMIO region\n");
rc = -1;
goto abort;
}
ctrl->creg = ioremap(ctrl->mmio_base, ctrl->mmio_size);
if (!ctrl->creg) {
- err("%s: cannot remap MMIO region %lx @ %lx\n", __FUNCTION__,
- ctrl->mmio_size, ctrl->mmio_base);
+ ctrl_err(ctrl, "Cannot remap MMIO region %lx @ %lx\n",
+ ctrl->mmio_size, ctrl->mmio_base);
release_mem_region(ctrl->mmio_base, ctrl->mmio_size);
rc = -1;
goto abort;
}
- dbg("%s: ctrl->creg %p\n", __FUNCTION__, ctrl->creg);
+ ctrl_dbg(ctrl, "ctrl->creg %p\n", ctrl->creg);
mutex_init(&ctrl->crit_sect);
mutex_init(&ctrl->cmd_lock);
@@ -1035,21 +1028,21 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
/* Mask Global Interrupt Mask & Command Complete Interrupt Mask */
tempdword = shpc_readl(ctrl, SERR_INTR_ENABLE);
- dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);
+ ctrl_dbg(ctrl, "SERR_INTR_ENABLE = %x\n", tempdword);
tempdword |= (GLOBAL_INTR_MASK | GLOBAL_SERR_MASK |
COMMAND_INTR_MASK | ARBITER_SERR_MASK);
tempdword &= ~SERR_INTR_RSVDZ_MASK;
shpc_writel(ctrl, SERR_INTR_ENABLE, tempdword);
tempdword = shpc_readl(ctrl, SERR_INTR_ENABLE);
- dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);
+ ctrl_dbg(ctrl, "SERR_INTR_ENABLE = %x\n", tempdword);
/* Mask the MRL sensor SERR Mask of individual slot in
* Slot SERR-INT Mask & clear all the existing event if any
*/
for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) {
slot_reg = shpc_readl(ctrl, SLOT_REG(hp_slot));
- dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__,
- hp_slot, slot_reg);
+ ctrl_dbg(ctrl, "Default Logical Slot Register %d value %x\n",
+ hp_slot, slot_reg);
slot_reg |= (PRSNT_CHANGE_INTR_MASK | ISO_PFAULT_INTR_MASK |
BUTTON_PRESS_INTR_MASK | MRL_CHANGE_INTR_MASK |
CON_PFAULT_INTR_MASK | MRL_CHANGE_SERR_MASK |
@@ -1066,45 +1059,32 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
/* Installs the interrupt handler */
rc = pci_enable_msi(pdev);
if (rc) {
- info("Can't get msi for the hotplug controller\n");
- info("Use INTx for the hotplug controller\n");
+ ctrl_info(ctrl, "Can't get msi for the hotplug controller\n");
+ ctrl_info(ctrl, "Use INTx for the hotplug controller\n");
}
rc = request_irq(ctrl->pci_dev->irq, shpc_isr, IRQF_SHARED,
MY_NAME, (void *)ctrl);
- dbg("%s: request_irq %d for hpc%d (returns %d)\n",
- __FUNCTION__, ctrl->pci_dev->irq,
- atomic_read(&shpchp_num_controllers), rc);
+ ctrl_dbg(ctrl, "request_irq %d (returns %d)\n",
+ ctrl->pci_dev->irq, rc);
if (rc) {
- err("Can't get irq %d for the hotplug controller\n",
- ctrl->pci_dev->irq);
+ ctrl_err(ctrl, "Can't get irq %d for the hotplug controller\n",
+ ctrl->pci_dev->irq);
goto abort_iounmap;
}
}
- dbg("%s: HPC at b:d:f:irq=0x%x:%x:%x:%x\n", __FUNCTION__,
- pdev->bus->number, PCI_SLOT(pdev->devfn),
- PCI_FUNC(pdev->devfn), pdev->irq);
- get_hp_hw_control_from_firmware(pdev);
+ ctrl_dbg(ctrl, "HPC at %s irq=%x\n", pci_name(pdev), pdev->irq);
- /*
- * If this is the first controller to be initialized,
- * initialize the shpchpd work queue
- */
- if (atomic_add_return(1, &shpchp_num_controllers) == 1) {
- shpchp_wq = create_singlethread_workqueue("shpchpd");
- if (!shpchp_wq) {
- rc = -ENOMEM;
- goto abort_iounmap;
- }
- }
+ shpc_get_max_bus_speed(ctrl);
+ shpc_get_cur_bus_speed(ctrl);
/*
* Unmask all event interrupts of all slots
*/
for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) {
slot_reg = shpc_readl(ctrl, SLOT_REG(hp_slot));
- dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__,
- hp_slot, slot_reg);
+ ctrl_dbg(ctrl, "Default Logical Slot Register %d value %x\n",
+ hp_slot, slot_reg);
slot_reg &= ~(PRSNT_CHANGE_INTR_MASK | ISO_PFAULT_INTR_MASK |
BUTTON_PRESS_INTR_MASK | MRL_CHANGE_INTR_MASK |
CON_PFAULT_INTR_MASK | SLOT_REG_RSVDZ_MASK);
@@ -1117,7 +1097,7 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
SERR_INTR_RSVDZ_MASK);
shpc_writel(ctrl, SERR_INTR_ENABLE, tempdword);
tempdword = shpc_readl(ctrl, SERR_INTR_ENABLE);
- dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);
+ ctrl_dbg(ctrl, "SERR_INTR_ENABLE = %x\n", tempdword);
}
return 0;
diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c
index a69a2152089..469454e0cc4 100644
--- a/drivers/pci/hotplug/shpchp_pci.c
+++ b/drivers/pci/hotplug/shpchp_pci.c
@@ -34,166 +34,89 @@
#include "../pci.h"
#include "shpchp.h"
-static void program_fw_provided_values(struct pci_dev *dev)
-{
- u16 pci_cmd, pci_bctl;
- struct pci_dev *cdev;
- struct hotplug_params hpp;
-
- /* Program hpp values for this device */
- if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL ||
- (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
- (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
- return;
-
- /* use default values if we can't get them from firmware */
- if (get_hp_params_from_firmware(dev, &hpp) ||
- !hpp.t0 || (hpp.t0->revision > 1)) {
- printk(KERN_WARNING
- "%s: Could not get hotplug parameters. Use defaults\n",
- __FUNCTION__);
- hpp.t0 = &hpp.type0_data;
- hpp.t0->revision = 0;
- hpp.t0->cache_line_size = 8;
- hpp.t0->latency_timer = 0x40;
- hpp.t0->enable_serr = 0;
- hpp.t0->enable_perr = 0;
- }
-
- pci_write_config_byte(dev,
- PCI_CACHE_LINE_SIZE, hpp.t0->cache_line_size);
- pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp.t0->latency_timer);
- pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
- if (hpp.t0->enable_serr)
- pci_cmd |= PCI_COMMAND_SERR;
- else
- pci_cmd &= ~PCI_COMMAND_SERR;
- if (hpp.t0->enable_perr)
- pci_cmd |= PCI_COMMAND_PARITY;
- else
- pci_cmd &= ~PCI_COMMAND_PARITY;
- pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
-
- /* Program bridge control value and child devices */
- if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
- pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
- hpp.t0->latency_timer);
- pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
- if (hpp.t0->enable_serr)
- pci_bctl |= PCI_BRIDGE_CTL_SERR;
- else
- pci_bctl &= ~PCI_BRIDGE_CTL_SERR;
- if (hpp.t0->enable_perr)
- pci_bctl |= PCI_BRIDGE_CTL_PARITY;
- else
- pci_bctl &= ~PCI_BRIDGE_CTL_PARITY;
- pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
- if (dev->subordinate) {
- list_for_each_entry(cdev, &dev->subordinate->devices,
- bus_list)
- program_fw_provided_values(cdev);
- }
- }
-}
-
-int __ref shpchp_configure_device(struct slot *p_slot)
+int shpchp_configure_device(struct slot *p_slot)
{
struct pci_dev *dev;
- struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate;
- int num, fn;
+ struct controller *ctrl = p_slot->ctrl;
+ struct pci_dev *bridge = ctrl->pci_dev;
+ struct pci_bus *parent = bridge->subordinate;
+ int num, ret = 0;
+
+ pci_lock_rescan_remove();
dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0));
if (dev) {
- err("Device %s already exists at %x:%x, cannot hot-add\n",
- pci_name(dev), p_slot->bus, p_slot->device);
+ ctrl_err(ctrl, "Device %s already exists at %04x:%02x:%02x, cannot hot-add\n",
+ pci_name(dev), pci_domain_nr(parent),
+ p_slot->bus, p_slot->device);
pci_dev_put(dev);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0));
if (num == 0) {
- err("No new device found\n");
- return -ENODEV;
+ ctrl_err(ctrl, "No new device found\n");
+ ret = -ENODEV;
+ goto out;
}
- for (fn = 0; fn < 8; fn++) {
- dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, fn));
- if (!dev)
+ list_for_each_entry(dev, &parent->devices, bus_list) {
+ if (PCI_SLOT(dev->devfn) != p_slot->device)
continue;
- if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
- err("Cannot hot-add display device %s\n",
- pci_name(dev));
- pci_dev_put(dev);
+ if (pci_is_bridge(dev))
+ pci_hp_add_bridge(dev);
+ }
+
+ pci_assign_unassigned_bridge_resources(bridge);
+
+ list_for_each_entry(dev, &parent->devices, bus_list) {
+ if (PCI_SLOT(dev->devfn) != p_slot->device)
continue;
- }
- if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) ||
- (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) {
- /* Find an unused bus number for the new bridge */
- struct pci_bus *child;
- unsigned char busnr, start = parent->secondary;
- unsigned char end = parent->subordinate;
- for (busnr = start; busnr <= end; busnr++) {
- if (!pci_find_bus(pci_domain_nr(parent),
- busnr))
- break;
- }
- if (busnr >= end) {
- err("No free bus for hot-added bridge\n");
- pci_dev_put(dev);
- continue;
- }
- child = pci_add_new_bus(parent, dev, busnr);
- if (!child) {
- err("Cannot add new bus for %s\n",
- pci_name(dev));
- pci_dev_put(dev);
- continue;
- }
- child->subordinate = pci_do_scan_bus(child);
- pci_bus_size_bridges(child);
- }
- program_fw_provided_values(dev);
- pci_dev_put(dev);
+ pci_configure_slot(dev);
}
- pci_bus_assign_resources(parent);
pci_bus_add_devices(parent);
- pci_enable_bridges(parent);
- return 0;
+
+ out:
+ pci_unlock_rescan_remove();
+ return ret;
}
int shpchp_unconfigure_device(struct slot *p_slot)
{
int rc = 0;
- int j;
u8 bctl = 0;
struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate;
+ struct pci_dev *dev, *temp;
+ struct controller *ctrl = p_slot->ctrl;
- dbg("%s: bus/dev = %x/%x\n", __FUNCTION__, p_slot->bus, p_slot->device);
+ ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:%02x\n",
+ __func__, pci_domain_nr(parent), p_slot->bus, p_slot->device);
- for (j=0; j<8 ; j++) {
- struct pci_dev* temp = pci_get_slot(parent,
- (p_slot->device << 3) | j);
- if (!temp)
- continue;
- if ((temp->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
- err("Cannot remove display device %s\n",
- pci_name(temp));
- pci_dev_put(temp);
+ pci_lock_rescan_remove();
+
+ list_for_each_entry_safe(dev, temp, &parent->devices, bus_list) {
+ if (PCI_SLOT(dev->devfn) != p_slot->device)
continue;
- }
- if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
- pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl);
+
+ pci_dev_get(dev);
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ pci_read_config_byte(dev, PCI_BRIDGE_CONTROL, &bctl);
if (bctl & PCI_BRIDGE_CTL_VGA) {
- err("Cannot remove display device %s\n",
- pci_name(temp));
- pci_dev_put(temp);
- continue;
+ ctrl_err(ctrl,
+ "Cannot remove display device %s\n",
+ pci_name(dev));
+ pci_dev_put(dev);
+ rc = -EINVAL;
+ break;
}
}
- pci_remove_bus_device(temp);
- pci_dev_put(temp);
+ pci_stop_and_remove_bus_device(dev);
+ pci_dev_put(dev);
}
+
+ pci_unlock_rescan_remove();
return rc;
}
diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c
index 29fa9d26ada..52875b36046 100644
--- a/drivers/pci/hotplug/shpchp_sysfs.c
+++ b/drivers/pci/hotplug/shpchp_sysfs.c
@@ -38,7 +38,7 @@
static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, char *buf)
{
struct pci_dev *pdev;
- char * out = buf;
+ char *out = buf;
int index, busnr;
struct resource *res;
struct pci_bus *bus;
@@ -47,51 +47,45 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha
bus = pdev->subordinate;
out += sprintf(buf, "Free resources: memory\n");
- for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) {
- res = bus->resource[index];
+ pci_bus_for_each_resource(bus, res, index) {
if (res && (res->flags & IORESOURCE_MEM) &&
!(res->flags & IORESOURCE_PREFETCH)) {
- out += sprintf(out, "start = %8.8llx, "
- "length = %8.8llx\n",
- (unsigned long long)res->start,
- (unsigned long long)(res->end - res->start));
+ out += sprintf(out, "start = %8.8llx, length = %8.8llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)resource_size(res));
}
}
out += sprintf(out, "Free resources: prefetchable memory\n");
- for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) {
- res = bus->resource[index];
+ pci_bus_for_each_resource(bus, res, index) {
if (res && (res->flags & IORESOURCE_MEM) &&
(res->flags & IORESOURCE_PREFETCH)) {
- out += sprintf(out, "start = %8.8llx, "
- "length = %8.8llx\n",
- (unsigned long long)res->start,
- (unsigned long long)(res->end - res->start));
+ out += sprintf(out, "start = %8.8llx, length = %8.8llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)resource_size(res));
}
}
out += sprintf(out, "Free resources: IO\n");
- for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) {
- res = bus->resource[index];
+ pci_bus_for_each_resource(bus, res, index) {
if (res && (res->flags & IORESOURCE_IO)) {
- out += sprintf(out, "start = %8.8llx, "
- "length = %8.8llx\n",
- (unsigned long long)res->start,
- (unsigned long long)(res->end - res->start));
+ out += sprintf(out, "start = %8.8llx, length = %8.8llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)resource_size(res));
}
}
out += sprintf(out, "Free resources: bus numbers\n");
- for (busnr = bus->secondary; busnr <= bus->subordinate; busnr++) {
+ for (busnr = bus->busn_res.start; busnr <= bus->busn_res.end; busnr++) {
if (!pci_find_bus(pci_domain_nr(bus), busnr))
break;
}
- if (busnr < bus->subordinate)
+ if (busnr < bus->busn_res.end)
out += sprintf(out, "start = %8.8x, length = %8.8x\n",
- busnr, (bus->subordinate - busnr));
+ busnr, (int)(bus->busn_res.end - busnr));
return out - buf;
}
static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
-int __must_check shpchp_create_ctrl_files (struct controller *ctrl)
+int shpchp_create_ctrl_files (struct controller *ctrl)
{
return device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
}
diff --git a/drivers/pci/htirq.c b/drivers/pci/htirq.c
index 279c940a003..a94dd2c4183 100644
--- a/drivers/pci/htirq.c
+++ b/drivers/pci/htirq.c
@@ -9,8 +9,8 @@
#include <linux/irq.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
+#include <linux/export.h>
#include <linux/slab.h>
-#include <linux/gfp.h>
#include <linux/htirq.h>
/* Global ht irq lock.
@@ -35,7 +35,7 @@ struct ht_irq_cfg {
void write_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg)
{
- struct ht_irq_cfg *cfg = get_irq_data(irq);
+ struct ht_irq_cfg *cfg = irq_get_handler_data(irq);
unsigned long flags;
spin_lock_irqsave(&ht_irq_lock, flags);
if (cfg->msg.address_lo != msg->address_lo) {
@@ -54,32 +54,26 @@ void write_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg)
void fetch_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg)
{
- struct ht_irq_cfg *cfg = get_irq_data(irq);
+ struct ht_irq_cfg *cfg = irq_get_handler_data(irq);
*msg = cfg->msg;
}
-void mask_ht_irq(unsigned int irq)
+void mask_ht_irq(struct irq_data *data)
{
- struct ht_irq_cfg *cfg;
- struct ht_irq_msg msg;
-
- cfg = get_irq_data(irq);
+ struct ht_irq_cfg *cfg = irq_data_get_irq_handler_data(data);
+ struct ht_irq_msg msg = cfg->msg;
- msg = cfg->msg;
msg.address_lo |= 1;
- write_ht_irq_msg(irq, &msg);
+ write_ht_irq_msg(data->irq, &msg);
}
-void unmask_ht_irq(unsigned int irq)
+void unmask_ht_irq(struct irq_data *data)
{
- struct ht_irq_cfg *cfg;
- struct ht_irq_msg msg;
-
- cfg = get_irq_data(irq);
+ struct ht_irq_cfg *cfg = irq_data_get_irq_handler_data(data);
+ struct ht_irq_msg msg = cfg->msg;
- msg = cfg->msg;
msg.address_lo &= ~1;
- write_ht_irq_msg(irq, &msg);
+ write_ht_irq_msg(data->irq, &msg);
}
/**
@@ -93,11 +87,9 @@ void unmask_ht_irq(unsigned int irq)
int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
{
struct ht_irq_cfg *cfg;
+ int max_irq, pos, irq;
unsigned long flags;
u32 data;
- int max_irq;
- int pos;
- int irq;
pos = pci_find_ht_capability(dev, HT_CAPTYPE_IRQ);
if (!pos)
@@ -110,7 +102,7 @@ int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
spin_unlock_irqrestore(&ht_irq_lock, flags);
max_irq = (data >> 16) & 0xff;
- if ( idx > max_irq)
+ if (idx > max_irq)
return -EINVAL;
cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
@@ -125,12 +117,12 @@ int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
cfg->msg.address_lo = 0xffffffff;
cfg->msg.address_hi = 0xffffffff;
- irq = create_irq();
- if (irq < 0) {
+ irq = irq_alloc_hwirq(dev_to_node(&dev->dev));
+ if (!irq) {
kfree(cfg);
return -EBUSY;
}
- set_irq_data(irq, cfg);
+ irq_set_handler_data(irq, cfg);
if (arch_setup_ht_irq(irq, dev) < 0) {
ht_destroy_irq(irq);
@@ -139,6 +131,7 @@ int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
return irq;
}
+EXPORT_SYMBOL(__ht_create_irq);
/**
* ht_create_irq - create an irq and attach it to a device.
@@ -154,9 +147,11 @@ int ht_create_irq(struct pci_dev *dev, int idx)
{
return __ht_create_irq(dev, idx, NULL);
}
+EXPORT_SYMBOL(ht_create_irq);
/**
* ht_destroy_irq - destroy an irq created with ht_create_irq
+ * @irq: irq to be destroyed
*
* This reverses ht_create_irq removing the specified irq from
* existence. The irq should be free before this happens.
@@ -165,14 +160,11 @@ void ht_destroy_irq(unsigned int irq)
{
struct ht_irq_cfg *cfg;
- cfg = get_irq_data(irq);
- set_irq_chip(irq, NULL);
- set_irq_data(irq, NULL);
- destroy_irq(irq);
+ cfg = irq_get_handler_data(irq);
+ irq_set_chip(irq, NULL);
+ irq_set_handler_data(irq, NULL);
+ irq_free_hwirq(irq);
kfree(cfg);
}
-
-EXPORT_SYMBOL(__ht_create_irq);
-EXPORT_SYMBOL(ht_create_irq);
EXPORT_SYMBOL(ht_destroy_irq);
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
deleted file mode 100644
index 4cb949f0ebd..00000000000
--- a/drivers/pci/intel-iommu.c
+++ /dev/null
@@ -1,2296 +0,0 @@
-/*
- * Copyright (c) 2006, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place - Suite 330, Boston, MA 02111-1307 USA.
- *
- * Copyright (C) 2006-2008 Intel Corporation
- * Author: Ashok Raj <ashok.raj@intel.com>
- * Author: Shaohua Li <shaohua.li@intel.com>
- * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
- */
-
-#include <linux/init.h>
-#include <linux/bitmap.h>
-#include <linux/slab.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/sysdev.h>
-#include <linux/spinlock.h>
-#include <linux/pci.h>
-#include <linux/dmar.h>
-#include <linux/dma-mapping.h>
-#include <linux/mempool.h>
-#include "iova.h"
-#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 "pci.h"
-
-#define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
-#define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
-
-#define IOAPIC_RANGE_START (0xfee00000)
-#define IOAPIC_RANGE_END (0xfeefffff)
-#define IOVA_START_ADDR (0x1000)
-
-#define DEFAULT_DOMAIN_ADDRESS_WIDTH 48
-
-#define DMAR_OPERATION_TIMEOUT (HZ*60) /* 1m */
-
-#define DOMAIN_MAX_ADDR(gaw) ((((u64)1) << gaw) - 1)
-
-static void domain_remove_dev_info(struct dmar_domain *domain);
-
-static int dmar_disabled;
-static int __initdata dmar_map_gfx = 1;
-static int dmar_forcedac;
-
-#define DUMMY_DEVICE_DOMAIN_INFO ((struct device_domain_info *)(-1))
-static DEFINE_SPINLOCK(device_domain_lock);
-static LIST_HEAD(device_domain_list);
-
-static int __init intel_iommu_setup(char *str)
-{
- if (!str)
- return -EINVAL;
- while (*str) {
- if (!strncmp(str, "off", 3)) {
- dmar_disabled = 1;
- printk(KERN_INFO"Intel-IOMMU: disabled\n");
- } else if (!strncmp(str, "igfx_off", 8)) {
- dmar_map_gfx = 0;
- printk(KERN_INFO
- "Intel-IOMMU: disable GFX device mapping\n");
- } else if (!strncmp(str, "forcedac", 8)) {
- printk (KERN_INFO
- "Intel-IOMMU: Forcing DAC for PCI devices\n");
- dmar_forcedac = 1;
- }
-
- str += strcspn(str, ",");
- while (*str == ',')
- str++;
- }
- return 0;
-}
-__setup("intel_iommu=", intel_iommu_setup);
-
-static struct kmem_cache *iommu_domain_cache;
-static struct kmem_cache *iommu_devinfo_cache;
-static struct kmem_cache *iommu_iova_cache;
-
-static inline void *iommu_kmem_cache_alloc(struct kmem_cache *cachep)
-{
- unsigned int flags;
- void *vaddr;
-
- /* trying to avoid low memory issues */
- flags = current->flags & PF_MEMALLOC;
- current->flags |= PF_MEMALLOC;
- vaddr = kmem_cache_alloc(cachep, GFP_ATOMIC);
- current->flags &= (~PF_MEMALLOC | flags);
- return vaddr;
-}
-
-
-static inline void *alloc_pgtable_page(void)
-{
- unsigned int flags;
- void *vaddr;
-
- /* trying to avoid low memory issues */
- flags = current->flags & PF_MEMALLOC;
- current->flags |= PF_MEMALLOC;
- vaddr = (void *)get_zeroed_page(GFP_ATOMIC);
- current->flags &= (~PF_MEMALLOC | flags);
- return vaddr;
-}
-
-static inline void free_pgtable_page(void *vaddr)
-{
- free_page((unsigned long)vaddr);
-}
-
-static inline void *alloc_domain_mem(void)
-{
- return iommu_kmem_cache_alloc(iommu_domain_cache);
-}
-
-static inline void free_domain_mem(void *vaddr)
-{
- kmem_cache_free(iommu_domain_cache, vaddr);
-}
-
-static inline void * alloc_devinfo_mem(void)
-{
- return iommu_kmem_cache_alloc(iommu_devinfo_cache);
-}
-
-static inline void free_devinfo_mem(void *vaddr)
-{
- kmem_cache_free(iommu_devinfo_cache, vaddr);
-}
-
-struct iova *alloc_iova_mem(void)
-{
- return iommu_kmem_cache_alloc(iommu_iova_cache);
-}
-
-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)
-{
- struct root_entry *root;
- struct context_entry *context;
- unsigned long phy_addr;
- unsigned long flags;
-
- spin_lock_irqsave(&iommu->lock, flags);
- root = &iommu->root_entry[bus];
- context = get_context_addr_from_root(root);
- if (!context) {
- context = (struct context_entry *)alloc_pgtable_page();
- if (!context) {
- spin_unlock_irqrestore(&iommu->lock, flags);
- return NULL;
- }
- __iommu_flush_cache(iommu, (void *)context, PAGE_SIZE_4K);
- phy_addr = virt_to_phys((void *)context);
- set_root_value(root, phy_addr);
- set_root_present(root);
- __iommu_flush_cache(iommu, root, sizeof(*root));
- }
- spin_unlock_irqrestore(&iommu->lock, flags);
- return &context[devfn];
-}
-
-static int device_context_mapped(struct intel_iommu *iommu, u8 bus, u8 devfn)
-{
- struct root_entry *root;
- struct context_entry *context;
- int ret;
- unsigned long flags;
-
- spin_lock_irqsave(&iommu->lock, flags);
- root = &iommu->root_entry[bus];
- context = get_context_addr_from_root(root);
- if (!context) {
- ret = 0;
- goto out;
- }
- ret = context_present(context[devfn]);
-out:
- spin_unlock_irqrestore(&iommu->lock, flags);
- return ret;
-}
-
-static void clear_context_table(struct intel_iommu *iommu, u8 bus, u8 devfn)
-{
- struct root_entry *root;
- struct context_entry *context;
- unsigned long flags;
-
- spin_lock_irqsave(&iommu->lock, flags);
- root = &iommu->root_entry[bus];
- context = get_context_addr_from_root(root);
- if (context) {
- context_clear_entry(context[devfn]);
- __iommu_flush_cache(iommu, &context[devfn], \
- sizeof(*context));
- }
- spin_unlock_irqrestore(&iommu->lock, flags);
-}
-
-static void free_context_table(struct intel_iommu *iommu)
-{
- struct root_entry *root;
- int i;
- unsigned long flags;
- struct context_entry *context;
-
- spin_lock_irqsave(&iommu->lock, flags);
- if (!iommu->root_entry) {
- goto out;
- }
- for (i = 0; i < ROOT_ENTRY_NR; i++) {
- root = &iommu->root_entry[i];
- context = get_context_addr_from_root(root);
- if (context)
- free_pgtable_page(context);
- }
- free_pgtable_page(iommu->root_entry);
- iommu->root_entry = NULL;
-out:
- spin_unlock_irqrestore(&iommu->lock, flags);
-}
-
-/* page table handling */
-#define LEVEL_STRIDE (9)
-#define LEVEL_MASK (((u64)1 << LEVEL_STRIDE) - 1)
-
-static inline int agaw_to_level(int agaw)
-{
- return agaw + 2;
-}
-
-static inline int agaw_to_width(int agaw)
-{
- return 30 + agaw * LEVEL_STRIDE;
-
-}
-
-static inline int width_to_agaw(int width)
-{
- return (width - 30) / LEVEL_STRIDE;
-}
-
-static inline unsigned int level_to_offset_bits(int level)
-{
- return (12 + (level - 1) * LEVEL_STRIDE);
-}
-
-static inline int address_level_offset(u64 addr, int level)
-{
- return ((addr >> level_to_offset_bits(level)) & LEVEL_MASK);
-}
-
-static inline u64 level_mask(int level)
-{
- return ((u64)-1 << level_to_offset_bits(level));
-}
-
-static inline u64 level_size(int level)
-{
- return ((u64)1 << level_to_offset_bits(level));
-}
-
-static inline u64 align_to_level(u64 addr, int level)
-{
- return ((addr + level_size(level) - 1) & level_mask(level));
-}
-
-static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr)
-{
- int addr_width = agaw_to_width(domain->agaw);
- struct dma_pte *parent, *pte = NULL;
- int level = agaw_to_level(domain->agaw);
- int offset;
- unsigned long flags;
-
- BUG_ON(!domain->pgd);
-
- addr &= (((u64)1) << addr_width) - 1;
- parent = domain->pgd;
-
- spin_lock_irqsave(&domain->mapping_lock, flags);
- while (level > 0) {
- void *tmp_page;
-
- offset = address_level_offset(addr, level);
- pte = &parent[offset];
- if (level == 1)
- break;
-
- if (!dma_pte_present(*pte)) {
- tmp_page = alloc_pgtable_page();
-
- if (!tmp_page) {
- spin_unlock_irqrestore(&domain->mapping_lock,
- flags);
- return NULL;
- }
- __iommu_flush_cache(domain->iommu, tmp_page,
- PAGE_SIZE_4K);
- dma_set_pte_addr(*pte, virt_to_phys(tmp_page));
- /*
- * high level table always sets r/w, last level page
- * table control read/write
- */
- dma_set_pte_readable(*pte);
- dma_set_pte_writable(*pte);
- __iommu_flush_cache(domain->iommu, pte, sizeof(*pte));
- }
- parent = phys_to_virt(dma_pte_addr(*pte));
- level--;
- }
-
- spin_unlock_irqrestore(&domain->mapping_lock, flags);
- return pte;
-}
-
-/* return address's pte at specific level */
-static struct dma_pte *dma_addr_level_pte(struct dmar_domain *domain, u64 addr,
- int level)
-{
- struct dma_pte *parent, *pte = NULL;
- int total = agaw_to_level(domain->agaw);
- int offset;
-
- parent = domain->pgd;
- while (level <= total) {
- offset = address_level_offset(addr, total);
- pte = &parent[offset];
- if (level == total)
- return pte;
-
- if (!dma_pte_present(*pte))
- break;
- parent = phys_to_virt(dma_pte_addr(*pte));
- total--;
- }
- return NULL;
-}
-
-/* clear one page's page table */
-static void dma_pte_clear_one(struct dmar_domain *domain, u64 addr)
-{
- struct dma_pte *pte = NULL;
-
- /* get last level pte */
- pte = dma_addr_level_pte(domain, addr, 1);
-
- if (pte) {
- dma_clear_pte(*pte);
- __iommu_flush_cache(domain->iommu, pte, sizeof(*pte));
- }
-}
-
-/* clear last level pte, a tlb flush should be followed */
-static void dma_pte_clear_range(struct dmar_domain *domain, u64 start, u64 end)
-{
- int addr_width = agaw_to_width(domain->agaw);
-
- start &= (((u64)1) << addr_width) - 1;
- end &= (((u64)1) << addr_width) - 1;
- /* in case it's partial page */
- start = PAGE_ALIGN_4K(start);
- end &= PAGE_MASK_4K;
-
- /* we don't need lock here, nobody else touches the iova range */
- while (start < end) {
- dma_pte_clear_one(domain, start);
- start += PAGE_SIZE_4K;
- }
-}
-
-/* free page table pages. last level pte should already be cleared */
-static void dma_pte_free_pagetable(struct dmar_domain *domain,
- u64 start, u64 end)
-{
- int addr_width = agaw_to_width(domain->agaw);
- struct dma_pte *pte;
- int total = agaw_to_level(domain->agaw);
- int level;
- u64 tmp;
-
- start &= (((u64)1) << addr_width) - 1;
- end &= (((u64)1) << addr_width) - 1;
-
- /* we don't need lock here, nobody else touches the iova range */
- level = 2;
- while (level <= total) {
- tmp = align_to_level(start, level);
- if (tmp >= end || (tmp + level_size(level) > end))
- return;
-
- while (tmp < end) {
- pte = dma_addr_level_pte(domain, tmp, level);
- if (pte) {
- free_pgtable_page(
- phys_to_virt(dma_pte_addr(*pte)));
- dma_clear_pte(*pte);
- __iommu_flush_cache(domain->iommu,
- pte, sizeof(*pte));
- }
- tmp += level_size(level);
- }
- level++;
- }
- /* free pgd */
- if (start == 0 && end >= ((((u64)1) << addr_width) - 1)) {
- free_pgtable_page(domain->pgd);
- domain->pgd = NULL;
- }
-}
-
-/* iommu handling */
-static int iommu_alloc_root_entry(struct intel_iommu *iommu)
-{
- struct root_entry *root;
- unsigned long flags;
-
- root = (struct root_entry *)alloc_pgtable_page();
- if (!root)
- return -ENOMEM;
-
- __iommu_flush_cache(iommu, root, PAGE_SIZE_4K);
-
- spin_lock_irqsave(&iommu->lock, flags);
- iommu->root_entry = root;
- spin_unlock_irqrestore(&iommu->lock, flags);
-
- return 0;
-}
-
-#define IOMMU_WAIT_OP(iommu, offset, op, cond, sts) \
-{\
- unsigned long start_time = jiffies;\
- while (1) {\
- sts = op (iommu->reg + offset);\
- if (cond)\
- break;\
- if (time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT))\
- panic("DMAR hardware is malfunctioning\n");\
- cpu_relax();\
- }\
-}
-
-static void iommu_set_root_entry(struct intel_iommu *iommu)
-{
- void *addr;
- u32 cmd, sts;
- unsigned long flag;
-
- addr = iommu->root_entry;
-
- spin_lock_irqsave(&iommu->register_lock, flag);
- dmar_writeq(iommu->reg + DMAR_RTADDR_REG, virt_to_phys(addr));
-
- cmd = iommu->gcmd | DMA_GCMD_SRTP;
- writel(cmd, iommu->reg + DMAR_GCMD_REG);
-
- /* Make sure hardware complete it */
- IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
- readl, (sts & DMA_GSTS_RTPS), sts);
-
- spin_unlock_irqrestore(&iommu->register_lock, flag);
-}
-
-static void iommu_flush_write_buffer(struct intel_iommu *iommu)
-{
- u32 val;
- unsigned long flag;
-
- if (!cap_rwbf(iommu->cap))
- return;
- val = iommu->gcmd | DMA_GCMD_WBF;
-
- spin_lock_irqsave(&iommu->register_lock, flag);
- writel(val, iommu->reg + DMAR_GCMD_REG);
-
- /* Make sure hardware complete it */
- IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
- readl, (!(val & DMA_GSTS_WBFS)), val);
-
- spin_unlock_irqrestore(&iommu->register_lock, flag);
-}
-
-/* return value determine if we need a write buffer flush */
-static int __iommu_flush_context(struct intel_iommu *iommu,
- u16 did, u16 source_id, u8 function_mask, u64 type,
- int non_present_entry_flush)
-{
- u64 val = 0;
- unsigned long flag;
-
- /*
- * In the non-present entry flush case, if hardware doesn't cache
- * non-present entry we do nothing and if hardware cache non-present
- * entry, we flush entries of domain 0 (the domain id is used to cache
- * any non-present entries)
- */
- if (non_present_entry_flush) {
- if (!cap_caching_mode(iommu->cap))
- return 1;
- else
- did = 0;
- }
-
- switch (type) {
- case DMA_CCMD_GLOBAL_INVL:
- val = DMA_CCMD_GLOBAL_INVL;
- break;
- case DMA_CCMD_DOMAIN_INVL:
- val = DMA_CCMD_DOMAIN_INVL|DMA_CCMD_DID(did);
- break;
- case DMA_CCMD_DEVICE_INVL:
- val = DMA_CCMD_DEVICE_INVL|DMA_CCMD_DID(did)
- | DMA_CCMD_SID(source_id) | DMA_CCMD_FM(function_mask);
- break;
- default:
- BUG();
- }
- val |= DMA_CCMD_ICC;
-
- spin_lock_irqsave(&iommu->register_lock, flag);
- dmar_writeq(iommu->reg + DMAR_CCMD_REG, val);
-
- /* Make sure hardware complete it */
- IOMMU_WAIT_OP(iommu, DMAR_CCMD_REG,
- dmar_readq, (!(val & DMA_CCMD_ICC)), val);
-
- spin_unlock_irqrestore(&iommu->register_lock, flag);
-
- /* flush context entry will implictly flush write buffer */
- return 0;
-}
-
-static int inline iommu_flush_context_global(struct intel_iommu *iommu,
- int non_present_entry_flush)
-{
- return __iommu_flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL,
- non_present_entry_flush);
-}
-
-static int inline iommu_flush_context_domain(struct intel_iommu *iommu, u16 did,
- int non_present_entry_flush)
-{
- return __iommu_flush_context(iommu, did, 0, 0, DMA_CCMD_DOMAIN_INVL,
- non_present_entry_flush);
-}
-
-static int inline iommu_flush_context_device(struct intel_iommu *iommu,
- u16 did, u16 source_id, u8 function_mask, int non_present_entry_flush)
-{
- return __iommu_flush_context(iommu, did, source_id, function_mask,
- DMA_CCMD_DEVICE_INVL, non_present_entry_flush);
-}
-
-/* return value determine if we need a write buffer flush */
-static int __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did,
- u64 addr, unsigned int size_order, u64 type,
- int non_present_entry_flush)
-{
- int tlb_offset = ecap_iotlb_offset(iommu->ecap);
- u64 val = 0, val_iva = 0;
- unsigned long flag;
-
- /*
- * In the non-present entry flush case, if hardware doesn't cache
- * non-present entry we do nothing and if hardware cache non-present
- * entry, we flush entries of domain 0 (the domain id is used to cache
- * any non-present entries)
- */
- if (non_present_entry_flush) {
- if (!cap_caching_mode(iommu->cap))
- return 1;
- else
- did = 0;
- }
-
- switch (type) {
- case DMA_TLB_GLOBAL_FLUSH:
- /* global flush doesn't need set IVA_REG */
- val = DMA_TLB_GLOBAL_FLUSH|DMA_TLB_IVT;
- break;
- case DMA_TLB_DSI_FLUSH:
- val = DMA_TLB_DSI_FLUSH|DMA_TLB_IVT|DMA_TLB_DID(did);
- break;
- case DMA_TLB_PSI_FLUSH:
- val = DMA_TLB_PSI_FLUSH|DMA_TLB_IVT|DMA_TLB_DID(did);
- /* Note: always flush non-leaf currently */
- val_iva = size_order | addr;
- break;
- default:
- BUG();
- }
- /* Note: set drain read/write */
-#if 0
- /*
- * This is probably to be super secure.. Looks like we can
- * ignore it without any impact.
- */
- if (cap_read_drain(iommu->cap))
- val |= DMA_TLB_READ_DRAIN;
-#endif
- if (cap_write_drain(iommu->cap))
- val |= DMA_TLB_WRITE_DRAIN;
-
- spin_lock_irqsave(&iommu->register_lock, flag);
- /* Note: Only uses first TLB reg currently */
- if (val_iva)
- dmar_writeq(iommu->reg + tlb_offset, val_iva);
- dmar_writeq(iommu->reg + tlb_offset + 8, val);
-
- /* Make sure hardware complete it */
- IOMMU_WAIT_OP(iommu, tlb_offset + 8,
- dmar_readq, (!(val & DMA_TLB_IVT)), val);
-
- spin_unlock_irqrestore(&iommu->register_lock, flag);
-
- /* check IOTLB invalidation granularity */
- if (DMA_TLB_IAIG(val) == 0)
- printk(KERN_ERR"IOMMU: flush IOTLB failed\n");
- if (DMA_TLB_IAIG(val) != DMA_TLB_IIRG(type))
- pr_debug("IOMMU: tlb flush request %Lx, actual %Lx\n",
- DMA_TLB_IIRG(type), DMA_TLB_IAIG(val));
- /* flush context entry will implictly flush write buffer */
- return 0;
-}
-
-static int inline iommu_flush_iotlb_global(struct intel_iommu *iommu,
- int non_present_entry_flush)
-{
- return __iommu_flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH,
- non_present_entry_flush);
-}
-
-static int inline iommu_flush_iotlb_dsi(struct intel_iommu *iommu, u16 did,
- int non_present_entry_flush)
-{
- return __iommu_flush_iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH,
- non_present_entry_flush);
-}
-
-static int iommu_flush_iotlb_psi(struct intel_iommu *iommu, u16 did,
- u64 addr, unsigned int pages, int non_present_entry_flush)
-{
- unsigned int mask;
-
- BUG_ON(addr & (~PAGE_MASK_4K));
- BUG_ON(pages == 0);
-
- /* Fallback to domain selective flush if no PSI support */
- if (!cap_pgsel_inv(iommu->cap))
- return iommu_flush_iotlb_dsi(iommu, did,
- non_present_entry_flush);
-
- /*
- * PSI requires page size to be 2 ^ x, and the base address is naturally
- * aligned to the size
- */
- mask = ilog2(__roundup_pow_of_two(pages));
- /* Fallback to domain selective flush if size is too big */
- if (mask > cap_max_amask_val(iommu->cap))
- return iommu_flush_iotlb_dsi(iommu, did,
- non_present_entry_flush);
-
- return __iommu_flush_iotlb(iommu, did, addr, mask,
- DMA_TLB_PSI_FLUSH, non_present_entry_flush);
-}
-
-static void iommu_disable_protect_mem_regions(struct intel_iommu *iommu)
-{
- u32 pmen;
- unsigned long flags;
-
- spin_lock_irqsave(&iommu->register_lock, flags);
- pmen = readl(iommu->reg + DMAR_PMEN_REG);
- pmen &= ~DMA_PMEN_EPM;
- writel(pmen, iommu->reg + DMAR_PMEN_REG);
-
- /* wait for the protected region status bit to clear */
- IOMMU_WAIT_OP(iommu, DMAR_PMEN_REG,
- readl, !(pmen & DMA_PMEN_PRS), pmen);
-
- spin_unlock_irqrestore(&iommu->register_lock, flags);
-}
-
-static int iommu_enable_translation(struct intel_iommu *iommu)
-{
- u32 sts;
- unsigned long flags;
-
- spin_lock_irqsave(&iommu->register_lock, flags);
- writel(iommu->gcmd|DMA_GCMD_TE, iommu->reg + DMAR_GCMD_REG);
-
- /* Make sure hardware complete it */
- IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
- readl, (sts & DMA_GSTS_TES), sts);
-
- iommu->gcmd |= DMA_GCMD_TE;
- spin_unlock_irqrestore(&iommu->register_lock, flags);
- return 0;
-}
-
-static int iommu_disable_translation(struct intel_iommu *iommu)
-{
- u32 sts;
- unsigned long flag;
-
- spin_lock_irqsave(&iommu->register_lock, flag);
- iommu->gcmd &= ~DMA_GCMD_TE;
- writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);
-
- /* Make sure hardware complete it */
- IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
- readl, (!(sts & DMA_GSTS_TES)), sts);
-
- spin_unlock_irqrestore(&iommu->register_lock, flag);
- return 0;
-}
-
-/* iommu interrupt handling. Most stuff are MSI-like. */
-
-static const char *fault_reason_strings[] =
-{
- "Software",
- "Present bit in root entry is clear",
- "Present bit in context entry is clear",
- "Invalid context entry",
- "Access beyond MGAW",
- "PTE Write access is not set",
- "PTE Read access is not set",
- "Next page table ptr is invalid",
- "Root table address invalid",
- "Context table ptr is invalid",
- "non-zero reserved fields in RTP",
- "non-zero reserved fields in CTP",
- "non-zero reserved fields in PTE",
-};
-#define MAX_FAULT_REASON_IDX (ARRAY_SIZE(fault_reason_strings) - 1)
-
-const char *dmar_get_fault_reason(u8 fault_reason)
-{
- if (fault_reason > MAX_FAULT_REASON_IDX)
- return "Unknown";
- else
- return fault_reason_strings[fault_reason];
-}
-
-void dmar_msi_unmask(unsigned int irq)
-{
- struct intel_iommu *iommu = get_irq_data(irq);
- unsigned long flag;
-
- /* unmask it */
- spin_lock_irqsave(&iommu->register_lock, flag);
- writel(0, iommu->reg + DMAR_FECTL_REG);
- /* Read a reg to force flush the post write */
- readl(iommu->reg + DMAR_FECTL_REG);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
-}
-
-void dmar_msi_mask(unsigned int irq)
-{
- unsigned long flag;
- struct intel_iommu *iommu = get_irq_data(irq);
-
- /* mask it */
- spin_lock_irqsave(&iommu->register_lock, flag);
- writel(DMA_FECTL_IM, iommu->reg + DMAR_FECTL_REG);
- /* Read a reg to force flush the post write */
- readl(iommu->reg + DMAR_FECTL_REG);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
-}
-
-void dmar_msi_write(int irq, struct msi_msg *msg)
-{
- struct intel_iommu *iommu = get_irq_data(irq);
- unsigned long flag;
-
- spin_lock_irqsave(&iommu->register_lock, flag);
- writel(msg->data, iommu->reg + DMAR_FEDATA_REG);
- writel(msg->address_lo, iommu->reg + DMAR_FEADDR_REG);
- writel(msg->address_hi, iommu->reg + DMAR_FEUADDR_REG);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
-}
-
-void dmar_msi_read(int irq, struct msi_msg *msg)
-{
- struct intel_iommu *iommu = get_irq_data(irq);
- unsigned long flag;
-
- spin_lock_irqsave(&iommu->register_lock, flag);
- msg->data = readl(iommu->reg + DMAR_FEDATA_REG);
- msg->address_lo = readl(iommu->reg + DMAR_FEADDR_REG);
- msg->address_hi = readl(iommu->reg + DMAR_FEUADDR_REG);
- spin_unlock_irqrestore(&iommu->register_lock, flag);
-}
-
-static int iommu_page_fault_do_one(struct intel_iommu *iommu, int type,
- u8 fault_reason, u16 source_id, u64 addr)
-{
- const char *reason;
-
- reason = dmar_get_fault_reason(fault_reason);
-
- printk(KERN_ERR
- "DMAR:[%s] Request device [%02x:%02x.%d] "
- "fault addr %llx \n"
- "DMAR:[fault reason %02d] %s\n",
- (type ? "DMA Read" : "DMA Write"),
- (source_id >> 8), PCI_SLOT(source_id & 0xFF),
- PCI_FUNC(source_id & 0xFF), addr, fault_reason, reason);
- return 0;
-}
-
-#define PRIMARY_FAULT_REG_LEN (16)
-static irqreturn_t iommu_page_fault(int irq, void *dev_id)
-{
- struct intel_iommu *iommu = dev_id;
- int reg, fault_index;
- u32 fault_status;
- unsigned long flag;
-
- spin_lock_irqsave(&iommu->register_lock, flag);
- fault_status = readl(iommu->reg + DMAR_FSTS_REG);
-
- /* TBD: ignore advanced fault log currently */
- if (!(fault_status & DMA_FSTS_PPF))
- goto clear_overflow;
-
- fault_index = dma_fsts_fault_record_index(fault_status);
- reg = cap_fault_reg_offset(iommu->cap);
- while (1) {
- u8 fault_reason;
- u16 source_id;
- u64 guest_addr;
- int type;
- u32 data;
-
- /* highest 32 bits */
- data = readl(iommu->reg + reg +
- fault_index * PRIMARY_FAULT_REG_LEN + 12);
- if (!(data & DMA_FRCD_F))
- break;
-
- fault_reason = dma_frcd_fault_reason(data);
- type = dma_frcd_type(data);
-
- data = readl(iommu->reg + reg +
- fault_index * PRIMARY_FAULT_REG_LEN + 8);
- source_id = dma_frcd_source_id(data);
-
- guest_addr = dmar_readq(iommu->reg + reg +
- fault_index * PRIMARY_FAULT_REG_LEN);
- guest_addr = dma_frcd_page_addr(guest_addr);
- /* clear the fault */
- writel(DMA_FRCD_F, iommu->reg + reg +
- fault_index * PRIMARY_FAULT_REG_LEN + 12);
-
- spin_unlock_irqrestore(&iommu->register_lock, flag);
-
- iommu_page_fault_do_one(iommu, type, fault_reason,
- source_id, guest_addr);
-
- fault_index++;
- if (fault_index > cap_num_fault_regs(iommu->cap))
- fault_index = 0;
- spin_lock_irqsave(&iommu->register_lock, flag);
- }
-clear_overflow:
- /* clear primary fault overflow */
- fault_status = readl(iommu->reg + DMAR_FSTS_REG);
- if (fault_status & DMA_FSTS_PFO)
- writel(DMA_FSTS_PFO, iommu->reg + DMAR_FSTS_REG);
-
- spin_unlock_irqrestore(&iommu->register_lock, flag);
- return IRQ_HANDLED;
-}
-
-int dmar_set_interrupt(struct intel_iommu *iommu)
-{
- int irq, ret;
-
- irq = create_irq();
- if (!irq) {
- printk(KERN_ERR "IOMMU: no free vectors\n");
- return -EINVAL;
- }
-
- set_irq_data(irq, iommu);
- iommu->irq = irq;
-
- ret = arch_setup_dmar_msi(irq);
- if (ret) {
- set_irq_data(irq, NULL);
- iommu->irq = 0;
- destroy_irq(irq);
- return 0;
- }
-
- /* Force fault register is cleared */
- iommu_page_fault(irq, iommu);
-
- ret = request_irq(irq, iommu_page_fault, 0, iommu->name, iommu);
- if (ret)
- printk(KERN_ERR "IOMMU: can't request irq\n");
- return ret;
-}
-
-static int iommu_init_domains(struct intel_iommu *iommu)
-{
- unsigned long ndomains;
- unsigned long nlongs;
-
- ndomains = cap_ndoms(iommu->cap);
- pr_debug("Number of Domains supportd <%ld>\n", ndomains);
- nlongs = BITS_TO_LONGS(ndomains);
-
- /* TBD: there might be 64K domains,
- * consider other allocation for future chip
- */
- iommu->domain_ids = kcalloc(nlongs, sizeof(unsigned long), GFP_KERNEL);
- if (!iommu->domain_ids) {
- printk(KERN_ERR "Allocating domain id array failed\n");
- return -ENOMEM;
- }
- iommu->domains = kcalloc(ndomains, sizeof(struct dmar_domain *),
- GFP_KERNEL);
- if (!iommu->domains) {
- printk(KERN_ERR "Allocating domain array failed\n");
- kfree(iommu->domain_ids);
- return -ENOMEM;
- }
-
- /*
- * if Caching mode is set, then invalid translations are tagged
- * with domainid 0. Hence we need to pre-allocate it.
- */
- if (cap_caching_mode(iommu->cap))
- set_bit(0, iommu->domain_ids);
- return 0;
-}
-
-static struct intel_iommu *alloc_iommu(struct dmar_drhd_unit *drhd)
-{
- struct intel_iommu *iommu;
- int ret;
- int map_size;
- u32 ver;
-
- iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
- if (!iommu)
- return NULL;
- 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)
-{
- 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];
- clear_bit(i, iommu->domain_ids);
- domain_exit(domain);
- i = find_next_bit(iommu->domain_ids,
- cap_ndoms(iommu->cap), i+1);
- }
-
- if (iommu->gcmd & DMA_GCMD_TE)
- iommu_disable_translation(iommu);
-
- if (iommu->irq) {
- set_irq_data(iommu->irq, NULL);
- /* This will mask the irq */
- free_irq(iommu->irq, iommu);
- destroy_irq(iommu->irq);
- }
-
- kfree(iommu->domains);
- kfree(iommu->domain_ids);
-
- /* 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)
-{
- unsigned long num;
- unsigned long ndomains;
- struct dmar_domain *domain;
- unsigned long flags;
-
- domain = alloc_domain_mem();
- if (!domain)
- return NULL;
-
- ndomains = cap_ndoms(iommu->cap);
-
- spin_lock_irqsave(&iommu->lock, flags);
- num = find_first_zero_bit(iommu->domain_ids, ndomains);
- if (num >= ndomains) {
- spin_unlock_irqrestore(&iommu->lock, flags);
- free_domain_mem(domain);
- printk(KERN_ERR "IOMMU: no free domain ids\n");
- return NULL;
- }
-
- set_bit(num, iommu->domain_ids);
- domain->id = num;
- domain->iommu = iommu;
- iommu->domains[num] = domain;
- spin_unlock_irqrestore(&iommu->lock, flags);
-
- return domain;
-}
-
-static void iommu_free_domain(struct dmar_domain *domain)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&domain->iommu->lock, flags);
- clear_bit(domain->id, domain->iommu->domain_ids);
- spin_unlock_irqrestore(&domain->iommu->lock, flags);
-}
-
-static struct iova_domain reserved_iova_list;
-static struct lock_class_key reserved_alloc_key;
-static struct lock_class_key reserved_rbtree_key;
-
-static void dmar_init_reserved_ranges(void)
-{
- struct pci_dev *pdev = NULL;
- struct iova *iova;
- int i;
- u64 addr, size;
-
- init_iova_domain(&reserved_iova_list, DMA_32BIT_PFN);
-
- lockdep_set_class(&reserved_iova_list.iova_alloc_lock,
- &reserved_alloc_key);
- lockdep_set_class(&reserved_iova_list.iova_rbtree_lock,
- &reserved_rbtree_key);
-
- /* IOAPIC ranges shouldn't be accessed by DMA */
- iova = reserve_iova(&reserved_iova_list, IOVA_PFN(IOAPIC_RANGE_START),
- IOVA_PFN(IOAPIC_RANGE_END));
- if (!iova)
- printk(KERN_ERR "Reserve IOAPIC range failed\n");
-
- /* Reserve all PCI MMIO to avoid peer-to-peer access */
- for_each_pci_dev(pdev) {
- struct resource *r;
-
- for (i = 0; i < PCI_NUM_RESOURCES; i++) {
- r = &pdev->resource[i];
- if (!r->flags || !(r->flags & IORESOURCE_MEM))
- continue;
- addr = r->start;
- addr &= PAGE_MASK_4K;
- size = r->end - addr;
- size = PAGE_ALIGN_4K(size);
- iova = reserve_iova(&reserved_iova_list, IOVA_PFN(addr),
- IOVA_PFN(size + addr) - 1);
- if (!iova)
- printk(KERN_ERR "Reserve iova failed\n");
- }
- }
-
-}
-
-static void domain_reserve_special_ranges(struct dmar_domain *domain)
-{
- copy_reserved_iova(&reserved_iova_list, &domain->iovad);
-}
-
-static inline int guestwidth_to_adjustwidth(int gaw)
-{
- int agaw;
- int r = (gaw - 12) % 9;
-
- if (r == 0)
- agaw = gaw;
- else
- agaw = gaw + 9 - r;
- if (agaw > 64)
- agaw = 64;
- return agaw;
-}
-
-static int domain_init(struct dmar_domain *domain, int guest_width)
-{
- struct intel_iommu *iommu;
- int adjust_width, agaw;
- unsigned long sagaw;
-
- init_iova_domain(&domain->iovad, DMA_32BIT_PFN);
- spin_lock_init(&domain->mapping_lock);
-
- domain_reserve_special_ranges(domain);
-
- /* calculate AGAW */
- iommu = domain->iommu;
- if (guest_width > cap_mgaw(iommu->cap))
- guest_width = cap_mgaw(iommu->cap);
- domain->gaw = guest_width;
- adjust_width = guestwidth_to_adjustwidth(guest_width);
- agaw = width_to_agaw(adjust_width);
- sagaw = cap_sagaw(iommu->cap);
- if (!test_bit(agaw, &sagaw)) {
- /* hardware doesn't support it, choose a bigger one */
- pr_debug("IOMMU: hardware doesn't support agaw %d\n", agaw);
- agaw = find_next_bit(&sagaw, 5, agaw);
- if (agaw >= 5)
- return -ENODEV;
- }
- domain->agaw = agaw;
- INIT_LIST_HEAD(&domain->devices);
-
- /* always allocate the top pgd */
- domain->pgd = (struct dma_pte *)alloc_pgtable_page();
- if (!domain->pgd)
- return -ENOMEM;
- __iommu_flush_cache(iommu, domain->pgd, PAGE_SIZE_4K);
- return 0;
-}
-
-static void domain_exit(struct dmar_domain *domain)
-{
- u64 end;
-
- /* Domain 0 is reserved, so dont process it */
- if (!domain)
- return;
-
- domain_remove_dev_info(domain);
- /* destroy iovas */
- put_iova_domain(&domain->iovad);
- end = DOMAIN_MAX_ADDR(domain->gaw);
- end = end & (~PAGE_MASK_4K);
-
- /* clear ptes */
- dma_pte_clear_range(domain, 0, end);
-
- /* free page tables */
- dma_pte_free_pagetable(domain, 0, end);
-
- iommu_free_domain(domain);
- free_domain_mem(domain);
-}
-
-static int domain_context_mapping_one(struct dmar_domain *domain,
- u8 bus, u8 devfn)
-{
- struct context_entry *context;
- struct intel_iommu *iommu = domain->iommu;
- unsigned long flags;
-
- pr_debug("Set context mapping for %02x:%02x.%d\n",
- bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
- BUG_ON(!domain->pgd);
- context = device_to_context_entry(iommu, bus, devfn);
- if (!context)
- return -ENOMEM;
- spin_lock_irqsave(&iommu->lock, flags);
- if (context_present(*context)) {
- spin_unlock_irqrestore(&iommu->lock, flags);
- return 0;
- }
-
- context_set_domain_id(*context, domain->id);
- context_set_address_width(*context, domain->agaw);
- context_set_address_root(*context, virt_to_phys(domain->pgd));
- context_set_translation_type(*context, CONTEXT_TT_MULTI_LEVEL);
- context_set_fault_enable(*context);
- context_set_present(*context);
- __iommu_flush_cache(iommu, context, sizeof(*context));
-
- /* it's a non-present to present mapping */
- if (iommu_flush_context_device(iommu, domain->id,
- (((u16)bus) << 8) | devfn, DMA_CCMD_MASK_NOBIT, 1))
- iommu_flush_write_buffer(iommu);
- else
- iommu_flush_iotlb_dsi(iommu, 0, 0);
- spin_unlock_irqrestore(&iommu->lock, flags);
- return 0;
-}
-
-static int
-domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev)
-{
- int ret;
- struct pci_dev *tmp, *parent;
-
- ret = domain_context_mapping_one(domain, pdev->bus->number,
- pdev->devfn);
- if (ret)
- return ret;
-
- /* dependent device mapping */
- tmp = pci_find_upstream_pcie_bridge(pdev);
- if (!tmp)
- return 0;
- /* Secondary interface's bus number and devfn 0 */
- parent = pdev->bus->self;
- while (parent != tmp) {
- ret = domain_context_mapping_one(domain, parent->bus->number,
- parent->devfn);
- if (ret)
- return ret;
- parent = parent->bus->self;
- }
- if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */
- return domain_context_mapping_one(domain,
- tmp->subordinate->number, 0);
- else /* this is a legacy PCI bridge */
- return domain_context_mapping_one(domain,
- tmp->bus->number, tmp->devfn);
-}
-
-static int domain_context_mapped(struct dmar_domain *domain,
- struct pci_dev *pdev)
-{
- int ret;
- struct pci_dev *tmp, *parent;
-
- ret = device_context_mapped(domain->iommu,
- pdev->bus->number, pdev->devfn);
- if (!ret)
- return ret;
- /* dependent device mapping */
- tmp = pci_find_upstream_pcie_bridge(pdev);
- if (!tmp)
- return ret;
- /* Secondary interface's bus number and devfn 0 */
- parent = pdev->bus->self;
- while (parent != tmp) {
- ret = device_context_mapped(domain->iommu, parent->bus->number,
- parent->devfn);
- if (!ret)
- return ret;
- parent = parent->bus->self;
- }
- if (tmp->is_pcie)
- return device_context_mapped(domain->iommu,
- tmp->subordinate->number, 0);
- else
- return device_context_mapped(domain->iommu,
- tmp->bus->number, tmp->devfn);
-}
-
-static int
-domain_page_mapping(struct dmar_domain *domain, dma_addr_t iova,
- u64 hpa, size_t size, int prot)
-{
- u64 start_pfn, end_pfn;
- struct dma_pte *pte;
- int index;
-
- if ((prot & (DMA_PTE_READ|DMA_PTE_WRITE)) == 0)
- return -EINVAL;
- iova &= PAGE_MASK_4K;
- start_pfn = ((u64)hpa) >> PAGE_SHIFT_4K;
- end_pfn = (PAGE_ALIGN_4K(((u64)hpa) + size)) >> PAGE_SHIFT_4K;
- index = 0;
- while (start_pfn < end_pfn) {
- pte = addr_to_dma_pte(domain, iova + PAGE_SIZE_4K * index);
- if (!pte)
- return -ENOMEM;
- /* We don't need lock here, nobody else
- * touches the iova range
- */
- BUG_ON(dma_pte_addr(*pte));
- dma_set_pte_addr(*pte, start_pfn << PAGE_SHIFT_4K);
- dma_set_pte_prot(*pte, prot);
- __iommu_flush_cache(domain->iommu, pte, sizeof(*pte));
- start_pfn++;
- index++;
- }
- return 0;
-}
-
-static void detach_domain_for_dev(struct dmar_domain *domain, u8 bus, u8 devfn)
-{
- clear_context_table(domain->iommu, bus, devfn);
- iommu_flush_context_global(domain->iommu, 0);
- iommu_flush_iotlb_global(domain->iommu, 0);
-}
-
-static void domain_remove_dev_info(struct dmar_domain *domain)
-{
- struct device_domain_info *info;
- unsigned long flags;
-
- spin_lock_irqsave(&device_domain_lock, flags);
- while (!list_empty(&domain->devices)) {
- info = list_entry(domain->devices.next,
- struct device_domain_info, link);
- list_del(&info->link);
- list_del(&info->global);
- if (info->dev)
- info->dev->dev.archdata.iommu = NULL;
- spin_unlock_irqrestore(&device_domain_lock, flags);
-
- detach_domain_for_dev(info->domain, info->bus, info->devfn);
- free_devinfo_mem(info);
-
- spin_lock_irqsave(&device_domain_lock, flags);
- }
- spin_unlock_irqrestore(&device_domain_lock, flags);
-}
-
-/*
- * find_domain
- * Note: we use struct pci_dev->dev.archdata.iommu stores the info
- */
-struct dmar_domain *
-find_domain(struct pci_dev *pdev)
-{
- struct device_domain_info *info;
-
- /* No lock here, assumes no domain exit in normal case */
- info = pdev->dev.archdata.iommu;
- if (info)
- return info->domain;
- 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)
-{
- struct dmar_domain *domain, *found = NULL;
- struct intel_iommu *iommu;
- struct dmar_drhd_unit *drhd;
- struct device_domain_info *info, *tmp;
- struct pci_dev *dev_tmp;
- unsigned long flags;
- int bus = 0, devfn = 0;
-
- domain = find_domain(pdev);
- if (domain)
- return domain;
-
- dev_tmp = pci_find_upstream_pcie_bridge(pdev);
- if (dev_tmp) {
- if (dev_tmp->is_pcie) {
- bus = dev_tmp->subordinate->number;
- devfn = 0;
- } else {
- bus = dev_tmp->bus->number;
- devfn = dev_tmp->devfn;
- }
- spin_lock_irqsave(&device_domain_lock, flags);
- list_for_each_entry(info, &device_domain_list, global) {
- if (info->bus == bus && info->devfn == devfn) {
- found = info->domain;
- break;
- }
- }
- spin_unlock_irqrestore(&device_domain_lock, flags);
- /* pcie-pci bridge already has a domain, uses it */
- if (found) {
- domain = found;
- goto found_domain;
- }
- }
-
- /* Allocate new domain for the device */
- drhd = dmar_find_matched_drhd_unit(pdev);
- if (!drhd) {
- printk(KERN_ERR "IOMMU: can't find DMAR for device %s\n",
- pci_name(pdev));
- return NULL;
- }
- iommu = drhd->iommu;
-
- domain = iommu_alloc_domain(iommu);
- if (!domain)
- goto error;
-
- if (domain_init(domain, gaw)) {
- domain_exit(domain);
- goto error;
- }
-
- /* register pcie-to-pci device */
- if (dev_tmp) {
- info = alloc_devinfo_mem();
- if (!info) {
- domain_exit(domain);
- goto error;
- }
- info->bus = bus;
- info->devfn = devfn;
- info->dev = NULL;
- info->domain = domain;
- /* This domain is shared by devices under p2p bridge */
- domain->flags |= DOMAIN_FLAG_MULTIPLE_DEVICES;
-
- /* pcie-to-pci bridge already has a domain, uses it */
- found = NULL;
- spin_lock_irqsave(&device_domain_lock, flags);
- list_for_each_entry(tmp, &device_domain_list, global) {
- if (tmp->bus == bus && tmp->devfn == devfn) {
- found = tmp->domain;
- break;
- }
- }
- if (found) {
- free_devinfo_mem(info);
- domain_exit(domain);
- domain = found;
- } else {
- list_add(&info->link, &domain->devices);
- list_add(&info->global, &device_domain_list);
- }
- spin_unlock_irqrestore(&device_domain_lock, flags);
- }
-
-found_domain:
- info = alloc_devinfo_mem();
- if (!info)
- goto error;
- info->bus = pdev->bus->number;
- info->devfn = pdev->devfn;
- info->dev = pdev;
- info->domain = domain;
- spin_lock_irqsave(&device_domain_lock, flags);
- /* somebody is fast */
- found = find_domain(pdev);
- if (found != NULL) {
- spin_unlock_irqrestore(&device_domain_lock, flags);
- if (found != domain) {
- domain_exit(domain);
- domain = found;
- }
- free_devinfo_mem(info);
- return domain;
- }
- list_add(&info->link, &domain->devices);
- list_add(&info->global, &device_domain_list);
- pdev->dev.archdata.iommu = info;
- spin_unlock_irqrestore(&device_domain_lock, flags);
- return domain;
-error:
- /* recheck it here, maybe others set it */
- return find_domain(pdev);
-}
-
-static int iommu_prepare_identity_map(struct pci_dev *pdev, u64 start, u64 end)
-{
- struct dmar_domain *domain;
- unsigned long size;
- u64 base;
- int ret;
-
- printk(KERN_INFO
- "IOMMU: Setting identity map for device %s [0x%Lx - 0x%Lx]\n",
- pci_name(pdev), start, end);
- /* page table init */
- domain = get_domain_for_dev(pdev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
- if (!domain)
- return -ENOMEM;
-
- /* The address might not be aligned */
- base = start & PAGE_MASK_4K;
- size = end - base;
- size = PAGE_ALIGN_4K(size);
- if (!reserve_iova(&domain->iovad, IOVA_PFN(base),
- IOVA_PFN(base + size) - 1)) {
- printk(KERN_ERR "IOMMU: reserve iova failed\n");
- ret = -ENOMEM;
- goto error;
- }
-
- pr_debug("Mapping reserved region %lx@%llx for %s\n",
- size, base, pci_name(pdev));
- /*
- * RMRR range might have overlap with physical memory range,
- * clear it first
- */
- dma_pte_clear_range(domain, base, base + size);
-
- ret = domain_page_mapping(domain, base, base, size,
- DMA_PTE_READ|DMA_PTE_WRITE);
- if (ret)
- goto error;
-
- /* context entry init */
- ret = domain_context_mapping(domain, pdev);
- if (!ret)
- return 0;
-error:
- domain_exit(domain);
- return ret;
-
-}
-
-static inline int iommu_prepare_rmrr_dev(struct dmar_rmrr_unit *rmrr,
- struct pci_dev *pdev)
-{
- if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
- return 0;
- return iommu_prepare_identity_map(pdev, rmrr->base_address,
- rmrr->end_address + 1);
-}
-
-#ifdef CONFIG_DMAR_GFX_WA
-extern int arch_get_ram_range(int slot, u64 *addr, u64 *size);
-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) {
- if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO ||
- !IS_GFX_DEVICE(pdev))
- 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");
- }
-}
-#endif
-
-#ifdef CONFIG_DMAR_FLOPPY_WA
-static inline void iommu_prepare_isa(void)
-{
- struct pci_dev *pdev;
- int ret;
-
- pdev = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, NULL);
- if (!pdev)
- return;
-
- printk(KERN_INFO "IOMMU: Prepare 0-16M unity mapping for LPC\n");
- ret = iommu_prepare_identity_map(pdev, 0, 16*1024*1024);
-
- if (ret)
- printk("IOMMU: Failed to create 0-64M identity map, "
- "floppy might not work\n");
-
-}
-#else
-static inline void iommu_prepare_isa(void)
-{
- return;
-}
-#endif /* !CONFIG_DMAR_FLPY_WA */
-
-int __init init_dmars(void)
-{
- struct dmar_drhd_unit *drhd;
- struct dmar_rmrr_unit *rmrr;
- struct pci_dev *pdev;
- struct intel_iommu *iommu;
- int ret, unit = 0;
-
- /*
- * for each drhd
- * allocate root
- * initialize and program root entry to not present
- * endfor
- */
- for_each_drhd_unit(drhd) {
- if (drhd->ignored)
- continue;
- iommu = alloc_iommu(drhd);
- if (!iommu) {
- ret = -ENOMEM;
- goto error;
- }
-
- /*
- * TBD:
- * we could share the same root & context tables
- * amoung all IOMMU's. Need to Split it later.
- */
- ret = iommu_alloc_root_entry(iommu);
- if (ret) {
- printk(KERN_ERR "IOMMU: allocate root entry failed\n");
- goto error;
- }
- }
-
- /*
- * For each rmrr
- * for each dev attached to rmrr
- * do
- * locate drhd for dev, alloc domain for dev
- * allocate free domain
- * allocate page table entries for rmrr
- * if context not allocated for bus
- * allocate and init context
- * set present in root table for this bus
- * init context with domain, translation etc
- * endfor
- * endfor
- */
- for_each_rmrr_units(rmrr) {
- int i;
- for (i = 0; i < rmrr->devices_cnt; i++) {
- pdev = rmrr->devices[i];
- /* some BIOS lists non-exist devices in DMAR table */
- if (!pdev)
- continue;
- ret = iommu_prepare_rmrr_dev(rmrr, pdev);
- if (ret)
- printk(KERN_ERR
- "IOMMU: mapping reserved region failed\n");
- }
- }
-
- iommu_prepare_gfx_mapping();
-
- iommu_prepare_isa();
-
- /*
- * for each drhd
- * enable fault log
- * global invalidate context cache
- * global invalidate iotlb
- * enable translation
- */
- for_each_drhd_unit(drhd) {
- if (drhd->ignored)
- continue;
- iommu = drhd->iommu;
- sprintf (iommu->name, "dmar%d", unit++);
-
- iommu_flush_write_buffer(iommu);
-
- ret = dmar_set_interrupt(iommu);
- if (ret)
- goto error;
-
- iommu_set_root_entry(iommu);
-
- iommu_flush_context_global(iommu, 0);
- iommu_flush_iotlb_global(iommu, 0);
-
- iommu_disable_protect_mem_regions(iommu);
-
- ret = iommu_enable_translation(iommu);
- if (ret)
- goto error;
- }
-
- return 0;
-error:
- for_each_drhd_unit(drhd) {
- if (drhd->ignored)
- continue;
- iommu = drhd->iommu;
- free_iommu(iommu);
- }
- return ret;
-}
-
-static inline u64 aligned_size(u64 host_addr, size_t size)
-{
- u64 addr;
- addr = (host_addr & (~PAGE_MASK_4K)) + size;
- return PAGE_ALIGN_4K(addr);
-}
-
-struct iova *
-iommu_alloc_iova(struct dmar_domain *domain, size_t size, u64 end)
-{
- struct iova *piova;
-
- /* Make sure it's in range */
- end = min_t(u64, DOMAIN_MAX_ADDR(domain->gaw), end);
- if (!size || (IOVA_START_ADDR + size > end))
- return NULL;
-
- piova = alloc_iova(&domain->iovad,
- size >> PAGE_SHIFT_4K, IOVA_PFN(end), 1);
- return piova;
-}
-
-static struct iova *
-__intel_alloc_iova(struct device *dev, struct dmar_domain *domain,
- size_t size)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct iova *iova = NULL;
-
- if ((pdev->dma_mask <= DMA_32BIT_MASK) || (dmar_forcedac)) {
- iova = iommu_alloc_iova(domain, size, pdev->dma_mask);
- } else {
- /*
- * First try to allocate an io virtual address in
- * DMA_32BIT_MASK and if that fails then try allocating
- * from higher range
- */
- iova = iommu_alloc_iova(domain, size, DMA_32BIT_MASK);
- if (!iova)
- iova = iommu_alloc_iova(domain, size, pdev->dma_mask);
- }
-
- if (!iova) {
- printk(KERN_ERR"Allocating iova for %s failed", pci_name(pdev));
- return NULL;
- }
-
- return iova;
-}
-
-static struct dmar_domain *
-get_valid_domain_for_dev(struct pci_dev *pdev)
-{
- struct dmar_domain *domain;
- int ret;
-
- domain = get_domain_for_dev(pdev,
- DEFAULT_DOMAIN_ADDRESS_WIDTH);
- if (!domain) {
- printk(KERN_ERR
- "Allocating domain for %s failed", pci_name(pdev));
- return NULL;
- }
-
- /* make sure context mapping is ok */
- if (unlikely(!domain_context_mapped(domain, pdev))) {
- ret = domain_context_mapping(domain, pdev);
- if (ret) {
- printk(KERN_ERR
- "Domain context map for %s failed",
- pci_name(pdev));
- return NULL;
- }
- }
-
- return domain;
-}
-
-static dma_addr_t intel_map_single(struct device *hwdev, void *addr,
- size_t size, int dir)
-{
- struct pci_dev *pdev = to_pci_dev(hwdev);
- int ret;
- struct dmar_domain *domain;
- unsigned long start_addr;
- struct iova *iova;
- int prot = 0;
-
- BUG_ON(dir == DMA_NONE);
- if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
- return virt_to_bus(addr);
-
- domain = get_valid_domain_for_dev(pdev);
- if (!domain)
- return 0;
-
- addr = (void *)virt_to_phys(addr);
- size = aligned_size((u64)addr, size);
-
- iova = __intel_alloc_iova(hwdev, domain, size);
- if (!iova)
- goto error;
-
- start_addr = iova->pfn_lo << PAGE_SHIFT_4K;
-
- /*
- * Check if DMAR supports zero-length reads on write only
- * mappings..
- */
- if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL || \
- !cap_zlr(domain->iommu->cap))
- prot |= DMA_PTE_READ;
- if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)
- prot |= DMA_PTE_WRITE;
- /*
- * addr - (addr + size) might be partial page, we should map the whole
- * page. Note: if two part of one page are separately mapped, we
- * might have two guest_addr mapping to the same host addr, but this
- * is not a big problem
- */
- ret = domain_page_mapping(domain, start_addr,
- ((u64)addr) & PAGE_MASK_4K, size, prot);
- if (ret)
- goto error;
-
- pr_debug("Device %s request: %lx@%llx mapping: %lx@%llx, dir %d\n",
- pci_name(pdev), size, (u64)addr,
- size, (u64)start_addr, dir);
-
- /* it's a non-present to present mapping */
- ret = iommu_flush_iotlb_psi(domain->iommu, domain->id,
- start_addr, size >> PAGE_SHIFT_4K, 1);
- if (ret)
- iommu_flush_write_buffer(domain->iommu);
-
- return (start_addr + ((u64)addr & (~PAGE_MASK_4K)));
-
-error:
- if (iova)
- __free_iova(&domain->iovad, iova);
- printk(KERN_ERR"Device %s request: %lx@%llx dir %d --- failed\n",
- pci_name(pdev), size, (u64)addr, dir);
- return 0;
-}
-
-static void intel_unmap_single(struct device *dev, dma_addr_t dev_addr,
- size_t size, int dir)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct dmar_domain *domain;
- unsigned long start_addr;
- struct iova *iova;
-
- if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
- return;
- domain = find_domain(pdev);
- BUG_ON(!domain);
-
- iova = find_iova(&domain->iovad, IOVA_PFN(dev_addr));
- if (!iova)
- return;
-
- start_addr = iova->pfn_lo << PAGE_SHIFT_4K;
- size = aligned_size((u64)dev_addr, size);
-
- pr_debug("Device %s unmapping: %lx@%llx\n",
- pci_name(pdev), size, (u64)start_addr);
-
- /* clear the whole page */
- dma_pte_clear_range(domain, start_addr, start_addr + size);
- /* free page tables */
- dma_pte_free_pagetable(domain, start_addr, start_addr + size);
-
- if (iommu_flush_iotlb_psi(domain->iommu, domain->id, start_addr,
- size >> PAGE_SHIFT_4K, 0))
- iommu_flush_write_buffer(domain->iommu);
-
- /* free iova */
- __free_iova(&domain->iovad, iova);
-}
-
-static void * intel_alloc_coherent(struct device *hwdev, size_t size,
- dma_addr_t *dma_handle, gfp_t flags)
-{
- void *vaddr;
- int order;
-
- size = PAGE_ALIGN_4K(size);
- order = get_order(size);
- flags &= ~(GFP_DMA | GFP_DMA32);
-
- vaddr = (void *)__get_free_pages(flags, order);
- if (!vaddr)
- return NULL;
- memset(vaddr, 0, size);
-
- *dma_handle = intel_map_single(hwdev, vaddr, size, DMA_BIDIRECTIONAL);
- if (*dma_handle)
- return vaddr;
- free_pages((unsigned long)vaddr, order);
- return NULL;
-}
-
-static void intel_free_coherent(struct device *hwdev, size_t size,
- void *vaddr, dma_addr_t dma_handle)
-{
- int order;
-
- size = PAGE_ALIGN_4K(size);
- order = get_order(size);
-
- intel_unmap_single(hwdev, dma_handle, size, DMA_BIDIRECTIONAL);
- free_pages((unsigned long)vaddr, order);
-}
-
-#define SG_ENT_VIRT_ADDRESS(sg) (sg_virt((sg)))
-static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist,
- int nelems, int dir)
-{
- int i;
- struct pci_dev *pdev = to_pci_dev(hwdev);
- struct dmar_domain *domain;
- unsigned long start_addr;
- struct iova *iova;
- size_t size = 0;
- void *addr;
- struct scatterlist *sg;
-
- if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
- return;
-
- domain = find_domain(pdev);
-
- iova = find_iova(&domain->iovad, IOVA_PFN(sglist[0].dma_address));
- if (!iova)
- return;
- for_each_sg(sglist, sg, nelems, i) {
- addr = SG_ENT_VIRT_ADDRESS(sg);
- size += aligned_size((u64)addr, sg->length);
- }
-
- start_addr = iova->pfn_lo << PAGE_SHIFT_4K;
-
- /* clear the whole page */
- dma_pte_clear_range(domain, start_addr, start_addr + size);
- /* free page tables */
- dma_pte_free_pagetable(domain, start_addr, start_addr + size);
-
- if (iommu_flush_iotlb_psi(domain->iommu, domain->id, start_addr,
- size >> PAGE_SHIFT_4K, 0))
- iommu_flush_write_buffer(domain->iommu);
-
- /* free iova */
- __free_iova(&domain->iovad, iova);
-}
-
-static int intel_nontranslate_map_sg(struct device *hddev,
- struct scatterlist *sglist, int nelems, int dir)
-{
- int i;
- struct scatterlist *sg;
-
- for_each_sg(sglist, sg, nelems, i) {
- BUG_ON(!sg_page(sg));
- sg->dma_address = virt_to_bus(SG_ENT_VIRT_ADDRESS(sg));
- sg->dma_length = sg->length;
- }
- return nelems;
-}
-
-static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist,
- int nelems, int dir)
-{
- void *addr;
- int i;
- struct pci_dev *pdev = to_pci_dev(hwdev);
- struct dmar_domain *domain;
- size_t size = 0;
- int prot = 0;
- size_t offset = 0;
- struct iova *iova = NULL;
- int ret;
- struct scatterlist *sg;
- unsigned long start_addr;
-
- BUG_ON(dir == DMA_NONE);
- if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
- return intel_nontranslate_map_sg(hwdev, sglist, nelems, dir);
-
- domain = get_valid_domain_for_dev(pdev);
- if (!domain)
- return 0;
-
- for_each_sg(sglist, sg, nelems, i) {
- addr = SG_ENT_VIRT_ADDRESS(sg);
- addr = (void *)virt_to_phys(addr);
- size += aligned_size((u64)addr, sg->length);
- }
-
- iova = __intel_alloc_iova(hwdev, domain, size);
- if (!iova) {
- sglist->dma_length = 0;
- return 0;
- }
-
- /*
- * Check if DMAR supports zero-length reads on write only
- * mappings..
- */
- if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL || \
- !cap_zlr(domain->iommu->cap))
- prot |= DMA_PTE_READ;
- if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)
- prot |= DMA_PTE_WRITE;
-
- start_addr = iova->pfn_lo << PAGE_SHIFT_4K;
- offset = 0;
- for_each_sg(sglist, sg, nelems, i) {
- addr = SG_ENT_VIRT_ADDRESS(sg);
- addr = (void *)virt_to_phys(addr);
- size = aligned_size((u64)addr, sg->length);
- ret = domain_page_mapping(domain, start_addr + offset,
- ((u64)addr) & PAGE_MASK_4K,
- size, prot);
- if (ret) {
- /* clear the page */
- dma_pte_clear_range(domain, start_addr,
- start_addr + offset);
- /* free page tables */
- dma_pte_free_pagetable(domain, start_addr,
- start_addr + offset);
- /* free iova */
- __free_iova(&domain->iovad, iova);
- return 0;
- }
- sg->dma_address = start_addr + offset +
- ((u64)addr & (~PAGE_MASK_4K));
- sg->dma_length = sg->length;
- offset += size;
- }
-
- /* it's a non-present to present mapping */
- if (iommu_flush_iotlb_psi(domain->iommu, domain->id,
- start_addr, offset >> PAGE_SHIFT_4K, 1))
- iommu_flush_write_buffer(domain->iommu);
- return nelems;
-}
-
-static struct dma_mapping_ops intel_dma_ops = {
- .alloc_coherent = intel_alloc_coherent,
- .free_coherent = intel_free_coherent,
- .map_single = intel_map_single,
- .unmap_single = intel_unmap_single,
- .map_sg = intel_map_sg,
- .unmap_sg = intel_unmap_sg,
-};
-
-static inline int iommu_domain_cache_init(void)
-{
- int ret = 0;
-
- iommu_domain_cache = kmem_cache_create("iommu_domain",
- sizeof(struct dmar_domain),
- 0,
- SLAB_HWCACHE_ALIGN,
-
- NULL);
- if (!iommu_domain_cache) {
- printk(KERN_ERR "Couldn't create iommu_domain cache\n");
- ret = -ENOMEM;
- }
-
- return ret;
-}
-
-static inline int iommu_devinfo_cache_init(void)
-{
- int ret = 0;
-
- iommu_devinfo_cache = kmem_cache_create("iommu_devinfo",
- sizeof(struct device_domain_info),
- 0,
- SLAB_HWCACHE_ALIGN,
-
- NULL);
- if (!iommu_devinfo_cache) {
- printk(KERN_ERR "Couldn't create devinfo cache\n");
- ret = -ENOMEM;
- }
-
- return ret;
-}
-
-static inline int iommu_iova_cache_init(void)
-{
- int ret = 0;
-
- iommu_iova_cache = kmem_cache_create("iommu_iova",
- sizeof(struct iova),
- 0,
- SLAB_HWCACHE_ALIGN,
-
- NULL);
- if (!iommu_iova_cache) {
- printk(KERN_ERR "Couldn't create iova cache\n");
- ret = -ENOMEM;
- }
-
- return ret;
-}
-
-static int __init iommu_init_mempool(void)
-{
- int ret;
- ret = iommu_iova_cache_init();
- if (ret)
- return ret;
-
- ret = iommu_domain_cache_init();
- if (ret)
- goto domain_error;
-
- ret = iommu_devinfo_cache_init();
- if (!ret)
- return ret;
-
- kmem_cache_destroy(iommu_domain_cache);
-domain_error:
- kmem_cache_destroy(iommu_iova_cache);
-
- return -ENOMEM;
-}
-
-static void __init iommu_exit_mempool(void)
-{
- kmem_cache_destroy(iommu_devinfo_cache);
- kmem_cache_destroy(iommu_domain_cache);
- kmem_cache_destroy(iommu_iova_cache);
-
-}
-
-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;
-
- for_each_drhd_unit(drhd) {
- if (!drhd->include_all) {
- int i;
- for (i = 0; i < drhd->devices_cnt; i++)
- if (drhd->devices[i] != NULL)
- break;
- /* ignore DMAR unit if no pci devices exist */
- if (i == drhd->devices_cnt)
- drhd->ignored = 1;
- }
- }
-
- if (dmar_map_gfx)
- return;
-
- for_each_drhd_unit(drhd) {
- int i;
- if (drhd->ignored || drhd->include_all)
- continue;
-
- for (i = 0; i < drhd->devices_cnt; i++)
- if (drhd->devices[i] &&
- !IS_GFX_DEVICE(drhd->devices[i]))
- break;
-
- if (i < drhd->devices_cnt)
- continue;
-
- /* bypass IOMMU if it is just for gfx devices */
- drhd->ignored = 1;
- for (i = 0; i < drhd->devices_cnt; i++) {
- if (!drhd->devices[i])
- continue;
- drhd->devices[i]->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO;
- }
- }
-}
-
-int __init intel_iommu_init(void)
-{
- int ret = 0;
-
- if (no_iommu || swiotlb || dmar_disabled)
- return -ENODEV;
-
- if (dmar_table_init())
- return -ENODEV;
-
- iommu_init_mempool();
- dmar_init_reserved_ranges();
-
- init_no_remapping_devices();
-
- ret = init_dmars();
- if (ret) {
- printk(KERN_ERR "IOMMU: dmar init failed\n");
- put_iova_domain(&reserved_iova_list);
- iommu_exit_mempool();
- return ret;
- }
- printk(KERN_INFO
- "PCI-DMA: Intel(R) Virtualization Technology for Directed I/O\n");
-
- force_iommu = 1;
- dma_ops = &intel_dma_ops;
- return 0;
-}
-
diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h
deleted file mode 100644
index afc0ad96122..00000000000
--- a/drivers/pci/intel-iommu.h
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * Copyright (c) 2006, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place - Suite 330, Boston, MA 02111-1307 USA.
- *
- * Copyright (C) 2006-2008 Intel Corporation
- * Author: Ashok Raj <ashok.raj@intel.com>
- * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
- */
-
-#ifndef _INTEL_IOMMU_H_
-#define _INTEL_IOMMU_H_
-
-#include <linux/types.h>
-#include <linux/msi.h>
-#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)
-
-/*
- * Intel IOMMU register specification per version 1.0 public spec.
- */
-
-#define DMAR_VER_REG 0x0 /* Arch version supported by this IOMMU */
-#define DMAR_CAP_REG 0x8 /* Hardware supported capabilities */
-#define DMAR_ECAP_REG 0x10 /* Extended capabilities supported */
-#define DMAR_GCMD_REG 0x18 /* Global command register */
-#define DMAR_GSTS_REG 0x1c /* Global status register */
-#define DMAR_RTADDR_REG 0x20 /* Root entry table */
-#define DMAR_CCMD_REG 0x28 /* Context command reg */
-#define DMAR_FSTS_REG 0x34 /* Fault Status register */
-#define DMAR_FECTL_REG 0x38 /* Fault control register */
-#define DMAR_FEDATA_REG 0x3c /* Fault event interrupt data register */
-#define DMAR_FEADDR_REG 0x40 /* Fault event interrupt addr register */
-#define DMAR_FEUADDR_REG 0x44 /* Upper address register */
-#define DMAR_AFLOG_REG 0x58 /* Advanced Fault control */
-#define DMAR_PMEN_REG 0x64 /* Enable Protected Memory Region */
-#define DMAR_PLMBASE_REG 0x68 /* PMRR Low addr */
-#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 OFFSET_STRIDE (9)
-/*
-#define dmar_readl(dmar, reg) readl(dmar + reg)
-#define dmar_readq(dmar, reg) ({ \
- u32 lo, hi; \
- lo = readl(dmar + reg); \
- hi = readl(dmar + reg + 4); \
- (((u64) hi) << 32) + lo; })
-*/
-static inline u64 dmar_readq(void __iomem *addr)
-{
- u32 lo, hi;
- lo = readl(addr);
- hi = readl(addr + 4);
- return (((u64) hi) << 32) + lo;
-}
-
-static inline void dmar_writeq(void __iomem *addr, u64 val)
-{
- writel((u32)val, addr);
- writel((u32)(val >> 32), addr + 4);
-}
-
-#define DMAR_VER_MAJOR(v) (((v) & 0xf0) >> 4)
-#define DMAR_VER_MINOR(v) ((v) & 0x0f)
-
-/*
- * Decoding Capability Register
- */
-#define cap_read_drain(c) (((c) >> 55) & 1)
-#define cap_write_drain(c) (((c) >> 54) & 1)
-#define cap_max_amask_val(c) (((c) >> 48) & 0x3f)
-#define cap_num_fault_regs(c) ((((c) >> 40) & 0xff) + 1)
-#define cap_pgsel_inv(c) (((c) >> 39) & 1)
-
-#define cap_super_page_val(c) (((c) >> 34) & 0xf)
-#define cap_super_offset(c) (((find_first_bit(&cap_super_page_val(c), 4)) \
- * OFFSET_STRIDE) + 21)
-
-#define cap_fault_reg_offset(c) ((((c) >> 24) & 0x3ff) * 16)
-#define cap_max_fault_reg_offset(c) \
- (cap_fault_reg_offset(c) + cap_num_fault_regs(c) * 16)
-
-#define cap_zlr(c) (((c) >> 22) & 1)
-#define cap_isoch(c) (((c) >> 23) & 1)
-#define cap_mgaw(c) ((((c) >> 16) & 0x3f) + 1)
-#define cap_sagaw(c) (((c) >> 8) & 0x1f)
-#define cap_caching_mode(c) (((c) >> 7) & 1)
-#define cap_phmr(c) (((c) >> 6) & 1)
-#define cap_plmr(c) (((c) >> 5) & 1)
-#define cap_rwbf(c) (((c) >> 4) & 1)
-#define cap_afl(c) (((c) >> 3) & 1)
-#define cap_ndoms(c) (((unsigned long)1) << (4 + 2 * ((c) & 0x7)))
-/*
- * Extended Capability Register
- */
-
-#define ecap_niotlb_iunits(e) ((((e) >> 24) & 0xff) + 1)
-#define ecap_iotlb_offset(e) ((((e) >> 8) & 0x3ff) * 16)
-#define ecap_max_iotlb_offset(e) \
- (ecap_iotlb_offset(e) + ecap_niotlb_iunits(e) * 16)
-#define ecap_coherent(e) ((e) & 0x1)
-
-
-/* IOTLB_REG */
-#define DMA_TLB_GLOBAL_FLUSH (((u64)1) << 60)
-#define DMA_TLB_DSI_FLUSH (((u64)2) << 60)
-#define DMA_TLB_PSI_FLUSH (((u64)3) << 60)
-#define DMA_TLB_IIRG(type) ((type >> 60) & 7)
-#define DMA_TLB_IAIG(val) (((val) >> 57) & 7)
-#define DMA_TLB_READ_DRAIN (((u64)1) << 49)
-#define DMA_TLB_WRITE_DRAIN (((u64)1) << 48)
-#define DMA_TLB_DID(id) (((u64)((id) & 0xffff)) << 32)
-#define DMA_TLB_IVT (((u64)1) << 63)
-#define DMA_TLB_IH_NONLEAF (((u64)1) << 6)
-#define DMA_TLB_MAX_SIZE (0x3f)
-
-/* PMEN_REG */
-#define DMA_PMEN_EPM (((u32)1)<<31)
-#define DMA_PMEN_PRS (((u32)1)<<0)
-
-/* GCMD_REG */
-#define DMA_GCMD_TE (((u32)1) << 31)
-#define DMA_GCMD_SRTP (((u32)1) << 30)
-#define DMA_GCMD_SFL (((u32)1) << 29)
-#define DMA_GCMD_EAFL (((u32)1) << 28)
-#define DMA_GCMD_WBF (((u32)1) << 27)
-
-/* GSTS_REG */
-#define DMA_GSTS_TES (((u32)1) << 31)
-#define DMA_GSTS_RTPS (((u32)1) << 30)
-#define DMA_GSTS_FLS (((u32)1) << 29)
-#define DMA_GSTS_AFLS (((u32)1) << 28)
-#define DMA_GSTS_WBFS (((u32)1) << 27)
-
-/* CCMD_REG */
-#define DMA_CCMD_ICC (((u64)1) << 63)
-#define DMA_CCMD_GLOBAL_INVL (((u64)1) << 61)
-#define DMA_CCMD_DOMAIN_INVL (((u64)2) << 61)
-#define DMA_CCMD_DEVICE_INVL (((u64)3) << 61)
-#define DMA_CCMD_FM(m) (((u64)((m) & 0x3)) << 32)
-#define DMA_CCMD_MASK_NOBIT 0
-#define DMA_CCMD_MASK_1BIT 1
-#define DMA_CCMD_MASK_2BIT 2
-#define DMA_CCMD_MASK_3BIT 3
-#define DMA_CCMD_SID(s) (((u64)((s) & 0xffff)) << 16)
-#define DMA_CCMD_DID(d) ((u64)((d) & 0xffff))
-
-/* FECTL_REG */
-#define DMA_FECTL_IM (((u32)1) << 31)
-
-/* FSTS_REG */
-#define DMA_FSTS_PPF ((u32)2)
-#define DMA_FSTS_PFO ((u32)1)
-#define dma_fsts_fault_record_index(s) (((s) >> 8) & 0xff)
-
-/* FRCD_REG, 32 bits access */
-#define DMA_FRCD_F (((u32)1) << 31)
-#define dma_frcd_type(d) ((d >> 30) & 1)
-#define dma_frcd_fault_reason(c) (c & 0xff)
-#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;
-}
-
-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);
-
-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 */
- 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;
-};
-
-#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/ioapic.c b/drivers/pci/ioapic.c
new file mode 100644
index 00000000000..6b2b7dddbbd
--- /dev/null
+++ b/drivers/pci/ioapic.c
@@ -0,0 +1,121 @@
+/*
+ * IOAPIC/IOxAPIC/IOSAPIC driver
+ *
+ * Copyright (C) 2009 Fujitsu Limited.
+ * (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * This driver manages PCI I/O APICs added by hotplug after boot. We try to
+ * claim all I/O APIC PCI devices, but those present at boot were registered
+ * when we parsed the ACPI MADT, so we'll fail when we try to re-register
+ * them.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/slab.h>
+
+struct ioapic {
+ acpi_handle handle;
+ u32 gsi_base;
+};
+
+static int ioapic_probe(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+ acpi_handle handle;
+ acpi_status status;
+ unsigned long long gsb;
+ struct ioapic *ioapic;
+ int ret;
+ char *type;
+ struct resource *res;
+
+ handle = ACPI_HANDLE(&dev->dev);
+ if (!handle)
+ return -EINVAL;
+
+ status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsb);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ /*
+ * The previous code in acpiphp evaluated _MAT if _GSB failed, but
+ * ACPI spec 4.0 sec 6.2.2 requires _GSB for hot-pluggable I/O APICs.
+ */
+
+ ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
+ if (!ioapic)
+ return -ENOMEM;
+
+ ioapic->handle = handle;
+ ioapic->gsi_base = (u32) gsb;
+
+ if (dev->class == PCI_CLASS_SYSTEM_PIC_IOAPIC)
+ type = "IOAPIC";
+ else
+ type = "IOxAPIC";
+
+ ret = pci_enable_device(dev);
+ if (ret < 0)
+ goto exit_free;
+
+ pci_set_master(dev);
+
+ if (pci_request_region(dev, 0, type))
+ goto exit_disable;
+
+ res = &dev->resource[0];
+ if (acpi_register_ioapic(ioapic->handle, res->start, ioapic->gsi_base))
+ goto exit_release;
+
+ pci_set_drvdata(dev, ioapic);
+ dev_info(&dev->dev, "%s at %pR, GSI %u\n", type, res, ioapic->gsi_base);
+ return 0;
+
+exit_release:
+ pci_release_region(dev, 0);
+exit_disable:
+ pci_disable_device(dev);
+exit_free:
+ kfree(ioapic);
+ return -ENODEV;
+}
+
+static void ioapic_remove(struct pci_dev *dev)
+{
+ struct ioapic *ioapic = pci_get_drvdata(dev);
+
+ acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base);
+ pci_release_region(dev, 0);
+ pci_disable_device(dev);
+ kfree(ioapic);
+}
+
+
+static DEFINE_PCI_DEVICE_TABLE(ioapic_devices) = {
+ { PCI_DEVICE_CLASS(PCI_CLASS_SYSTEM_PIC_IOAPIC, ~0) },
+ { PCI_DEVICE_CLASS(PCI_CLASS_SYSTEM_PIC_IOXAPIC, ~0) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, ioapic_devices);
+
+static struct pci_driver ioapic_driver = {
+ .name = "ioapic",
+ .id_table = ioapic_devices,
+ .probe = ioapic_probe,
+ .remove = ioapic_remove,
+};
+
+static int __init ioapic_init(void)
+{
+ return pci_register_driver(&ioapic_driver);
+}
+module_init(ioapic_init);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
new file mode 100644
index 00000000000..cb6f24740ee
--- /dev/null
+++ b/drivers/pci/iov.c
@@ -0,0 +1,694 @@
+/*
+ * drivers/pci/iov.c
+ *
+ * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
+ *
+ * PCI Express I/O Virtualization (IOV) support.
+ * Single Root IOV 1.0
+ * Address Translation Service 1.0
+ */
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/export.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/pci-ats.h>
+#include "pci.h"
+
+#define VIRTFN_ID_LEN 16
+
+static inline u8 virtfn_bus(struct pci_dev *dev, int id)
+{
+ return dev->bus->number + ((dev->devfn + dev->sriov->offset +
+ dev->sriov->stride * id) >> 8);
+}
+
+static inline u8 virtfn_devfn(struct pci_dev *dev, int id)
+{
+ return (dev->devfn + dev->sriov->offset +
+ dev->sriov->stride * id) & 0xff;
+}
+
+static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
+{
+ struct pci_bus *child;
+
+ if (bus->number == busnr)
+ return bus;
+
+ child = pci_find_bus(pci_domain_nr(bus), busnr);
+ if (child)
+ return child;
+
+ child = pci_add_new_bus(bus, NULL, busnr);
+ if (!child)
+ return NULL;
+
+ pci_bus_insert_busn_res(child, busnr, busnr);
+
+ return child;
+}
+
+static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus)
+{
+ if (physbus != virtbus && list_empty(&virtbus->devices))
+ pci_remove_bus(virtbus);
+}
+
+static int virtfn_add(struct pci_dev *dev, int id, int reset)
+{
+ int i;
+ int rc = -ENOMEM;
+ u64 size;
+ char buf[VIRTFN_ID_LEN];
+ struct pci_dev *virtfn;
+ struct resource *res;
+ struct pci_sriov *iov = dev->sriov;
+ struct pci_bus *bus;
+
+ mutex_lock(&iov->dev->sriov->lock);
+ bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
+ if (!bus)
+ goto failed;
+
+ virtfn = pci_alloc_dev(bus);
+ if (!virtfn)
+ goto failed0;
+
+ virtfn->devfn = virtfn_devfn(dev, id);
+ virtfn->vendor = dev->vendor;
+ pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
+ pci_setup_device(virtfn);
+ virtfn->dev.parent = dev->dev.parent;
+ virtfn->physfn = pci_dev_get(dev);
+ virtfn->is_virtfn = 1;
+ virtfn->multifunction = 0;
+
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+ res = dev->resource + PCI_IOV_RESOURCES + i;
+ if (!res->parent)
+ continue;
+ virtfn->resource[i].name = pci_name(virtfn);
+ virtfn->resource[i].flags = res->flags;
+ size = resource_size(res);
+ do_div(size, iov->total_VFs);
+ virtfn->resource[i].start = res->start + size * id;
+ virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
+ rc = request_resource(res, &virtfn->resource[i]);
+ BUG_ON(rc);
+ }
+
+ if (reset)
+ __pci_reset_function(virtfn);
+
+ pci_device_add(virtfn, virtfn->bus);
+ mutex_unlock(&iov->dev->sriov->lock);
+
+ pci_bus_add_device(virtfn);
+ sprintf(buf, "virtfn%u", id);
+ rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
+ if (rc)
+ goto failed1;
+ rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
+ if (rc)
+ goto failed2;
+
+ kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
+
+ return 0;
+
+failed2:
+ sysfs_remove_link(&dev->dev.kobj, buf);
+failed1:
+ pci_dev_put(dev);
+ mutex_lock(&iov->dev->sriov->lock);
+ pci_stop_and_remove_bus_device(virtfn);
+failed0:
+ virtfn_remove_bus(dev->bus, bus);
+failed:
+ mutex_unlock(&iov->dev->sriov->lock);
+
+ return rc;
+}
+
+static void virtfn_remove(struct pci_dev *dev, int id, int reset)
+{
+ char buf[VIRTFN_ID_LEN];
+ struct pci_dev *virtfn;
+ struct pci_sriov *iov = dev->sriov;
+
+ virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
+ virtfn_bus(dev, id),
+ virtfn_devfn(dev, id));
+ if (!virtfn)
+ return;
+
+ if (reset) {
+ device_release_driver(&virtfn->dev);
+ __pci_reset_function(virtfn);
+ }
+
+ sprintf(buf, "virtfn%u", id);
+ sysfs_remove_link(&dev->dev.kobj, buf);
+ /*
+ * pci_stop_dev() could have been called for this virtfn already,
+ * so the directory for the virtfn may have been removed before.
+ * Double check to avoid spurious sysfs warnings.
+ */
+ if (virtfn->dev.kobj.sd)
+ sysfs_remove_link(&virtfn->dev.kobj, "physfn");
+
+ mutex_lock(&iov->dev->sriov->lock);
+ pci_stop_and_remove_bus_device(virtfn);
+ virtfn_remove_bus(dev->bus, virtfn->bus);
+ mutex_unlock(&iov->dev->sriov->lock);
+
+ /* balance pci_get_domain_bus_and_slot() */
+ pci_dev_put(virtfn);
+ pci_dev_put(dev);
+}
+
+static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
+{
+ int rc;
+ int i, j;
+ int nres;
+ u16 offset, stride, initial;
+ struct resource *res;
+ struct pci_dev *pdev;
+ struct pci_sriov *iov = dev->sriov;
+ int bars = 0;
+
+ if (!nr_virtfn)
+ return 0;
+
+ if (iov->num_VFs)
+ return -EINVAL;
+
+ pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
+ if (initial > iov->total_VFs ||
+ (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs)))
+ return -EIO;
+
+ if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs ||
+ (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
+ return -EINVAL;
+
+ pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset);
+ pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride);
+ if (!offset || (nr_virtfn > 1 && !stride))
+ return -EIO;
+
+ nres = 0;
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+ bars |= (1 << (i + PCI_IOV_RESOURCES));
+ res = dev->resource + PCI_IOV_RESOURCES + i;
+ if (res->parent)
+ nres++;
+ }
+ if (nres != iov->nres) {
+ dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n");
+ return -ENOMEM;
+ }
+
+ iov->offset = offset;
+ iov->stride = stride;
+
+ if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->busn_res.end) {
+ dev_err(&dev->dev, "SR-IOV: bus number out of range\n");
+ return -ENOMEM;
+ }
+
+ if (pci_enable_resources(dev, bars)) {
+ dev_err(&dev->dev, "SR-IOV: IOV BARS not allocated\n");
+ return -ENOMEM;
+ }
+
+ if (iov->link != dev->devfn) {
+ pdev = pci_get_slot(dev->bus, iov->link);
+ if (!pdev)
+ return -ENODEV;
+
+ if (!pdev->is_physfn) {
+ pci_dev_put(pdev);
+ return -ENOSYS;
+ }
+
+ rc = sysfs_create_link(&dev->dev.kobj,
+ &pdev->dev.kobj, "dep_link");
+ pci_dev_put(pdev);
+ if (rc)
+ return rc;
+ }
+
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
+ iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
+ pci_cfg_access_lock(dev);
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
+ msleep(100);
+ pci_cfg_access_unlock(dev);
+
+ iov->initial_VFs = initial;
+ if (nr_virtfn < initial)
+ initial = nr_virtfn;
+
+ for (i = 0; i < initial; i++) {
+ rc = virtfn_add(dev, i, 0);
+ if (rc)
+ goto failed;
+ }
+
+ kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
+ iov->num_VFs = nr_virtfn;
+
+ return 0;
+
+failed:
+ for (j = 0; j < i; j++)
+ virtfn_remove(dev, j, 0);
+
+ iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
+ pci_cfg_access_lock(dev);
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
+ ssleep(1);
+ pci_cfg_access_unlock(dev);
+
+ if (iov->link != dev->devfn)
+ sysfs_remove_link(&dev->dev.kobj, "dep_link");
+
+ return rc;
+}
+
+static void sriov_disable(struct pci_dev *dev)
+{
+ int i;
+ struct pci_sriov *iov = dev->sriov;
+
+ if (!iov->num_VFs)
+ return;
+
+ for (i = 0; i < iov->num_VFs; i++)
+ virtfn_remove(dev, i, 0);
+
+ iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
+ pci_cfg_access_lock(dev);
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
+ ssleep(1);
+ pci_cfg_access_unlock(dev);
+
+ if (iov->link != dev->devfn)
+ sysfs_remove_link(&dev->dev.kobj, "dep_link");
+
+ iov->num_VFs = 0;
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
+}
+
+static int sriov_init(struct pci_dev *dev, int pos)
+{
+ int i;
+ int rc;
+ int nres;
+ u32 pgsz;
+ u16 ctrl, total, offset, stride;
+ struct pci_sriov *iov;
+ struct resource *res;
+ struct pci_dev *pdev;
+
+ if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END &&
+ pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT)
+ return -ENODEV;
+
+ pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
+ if (ctrl & PCI_SRIOV_CTRL_VFE) {
+ pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
+ ssleep(1);
+ }
+
+ pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
+ if (!total)
+ return 0;
+
+ ctrl = 0;
+ list_for_each_entry(pdev, &dev->bus->devices, bus_list)
+ if (pdev->is_physfn)
+ goto found;
+
+ pdev = NULL;
+ if (pci_ari_enabled(dev->bus))
+ ctrl |= PCI_SRIOV_CTRL_ARI;
+
+found:
+ pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
+ pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, 0);
+ pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
+ pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
+ if (!offset || (total > 1 && !stride))
+ return -EIO;
+
+ pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
+ i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
+ pgsz &= ~((1 << i) - 1);
+ if (!pgsz)
+ return -EIO;
+
+ pgsz &= ~(pgsz - 1);
+ pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
+
+ nres = 0;
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+ res = dev->resource + PCI_IOV_RESOURCES + i;
+ i += __pci_read_base(dev, pci_bar_unknown, res,
+ pos + PCI_SRIOV_BAR + i * 4);
+ if (!res->flags)
+ continue;
+ if (resource_size(res) & (PAGE_SIZE - 1)) {
+ rc = -EIO;
+ goto failed;
+ }
+ res->end = res->start + resource_size(res) * total - 1;
+ nres++;
+ }
+
+ iov = kzalloc(sizeof(*iov), GFP_KERNEL);
+ if (!iov) {
+ rc = -ENOMEM;
+ goto failed;
+ }
+
+ iov->pos = pos;
+ iov->nres = nres;
+ iov->ctrl = ctrl;
+ iov->total_VFs = total;
+ iov->offset = offset;
+ iov->stride = stride;
+ iov->pgsz = pgsz;
+ iov->self = dev;
+ pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
+ pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
+ iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);
+
+ if (pdev)
+ iov->dev = pci_dev_get(pdev);
+ else
+ iov->dev = dev;
+
+ mutex_init(&iov->lock);
+
+ dev->sriov = iov;
+ dev->is_physfn = 1;
+
+ return 0;
+
+failed:
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+ res = dev->resource + PCI_IOV_RESOURCES + i;
+ res->flags = 0;
+ }
+
+ return rc;
+}
+
+static void sriov_release(struct pci_dev *dev)
+{
+ BUG_ON(dev->sriov->num_VFs);
+
+ if (dev != dev->sriov->dev)
+ pci_dev_put(dev->sriov->dev);
+
+ mutex_destroy(&dev->sriov->lock);
+
+ kfree(dev->sriov);
+ dev->sriov = NULL;
+}
+
+static void sriov_restore_state(struct pci_dev *dev)
+{
+ int i;
+ u16 ctrl;
+ struct pci_sriov *iov = dev->sriov;
+
+ pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
+ if (ctrl & PCI_SRIOV_CTRL_VFE)
+ return;
+
+ for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
+ pci_update_resource(dev, i);
+
+ pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->num_VFs);
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
+ if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
+ msleep(100);
+}
+
+/**
+ * pci_iov_init - initialize the IOV capability
+ * @dev: the PCI device
+ *
+ * Returns 0 on success, or negative on failure.
+ */
+int pci_iov_init(struct pci_dev *dev)
+{
+ int pos;
+
+ if (!pci_is_pcie(dev))
+ return -ENODEV;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
+ if (pos)
+ return sriov_init(dev, pos);
+
+ return -ENODEV;
+}
+
+/**
+ * pci_iov_release - release resources used by the IOV capability
+ * @dev: the PCI device
+ */
+void pci_iov_release(struct pci_dev *dev)
+{
+ if (dev->is_physfn)
+ sriov_release(dev);
+}
+
+/**
+ * pci_iov_resource_bar - get position of the SR-IOV BAR
+ * @dev: the PCI device
+ * @resno: the resource number
+ * @type: the BAR type to be filled in
+ *
+ * Returns position of the BAR encapsulated in the SR-IOV capability.
+ */
+int pci_iov_resource_bar(struct pci_dev *dev, int resno,
+ enum pci_bar_type *type)
+{
+ if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
+ return 0;
+
+ BUG_ON(!dev->is_physfn);
+
+ *type = pci_bar_unknown;
+
+ return dev->sriov->pos + PCI_SRIOV_BAR +
+ 4 * (resno - PCI_IOV_RESOURCES);
+}
+
+/**
+ * pci_sriov_resource_alignment - get resource alignment for VF BAR
+ * @dev: the PCI device
+ * @resno: the resource number
+ *
+ * Returns the alignment of the VF BAR found in the SR-IOV capability.
+ * This is not the same as the resource size which is defined as
+ * the VF BAR size multiplied by the number of VFs. The alignment
+ * is just the VF BAR size.
+ */
+resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
+{
+ struct resource tmp;
+ enum pci_bar_type type;
+ int reg = pci_iov_resource_bar(dev, resno, &type);
+
+ if (!reg)
+ return 0;
+
+ __pci_read_base(dev, type, &tmp, reg);
+ return resource_alignment(&tmp);
+}
+
+/**
+ * pci_restore_iov_state - restore the state of the IOV capability
+ * @dev: the PCI device
+ */
+void pci_restore_iov_state(struct pci_dev *dev)
+{
+ if (dev->is_physfn)
+ sriov_restore_state(dev);
+}
+
+/**
+ * pci_iov_bus_range - find bus range used by Virtual Function
+ * @bus: the PCI bus
+ *
+ * Returns max number of buses (exclude current one) used by Virtual
+ * Functions.
+ */
+int pci_iov_bus_range(struct pci_bus *bus)
+{
+ int max = 0;
+ u8 busnr;
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ if (!dev->is_physfn)
+ continue;
+ busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1);
+ if (busnr > max)
+ max = busnr;
+ }
+
+ return max ? max - bus->number : 0;
+}
+
+/**
+ * pci_enable_sriov - enable the SR-IOV capability
+ * @dev: the PCI device
+ * @nr_virtfn: number of virtual functions to enable
+ *
+ * Returns 0 on success, or negative on failure.
+ */
+int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
+{
+ might_sleep();
+
+ if (!dev->is_physfn)
+ return -ENOSYS;
+
+ return sriov_enable(dev, nr_virtfn);
+}
+EXPORT_SYMBOL_GPL(pci_enable_sriov);
+
+/**
+ * pci_disable_sriov - disable the SR-IOV capability
+ * @dev: the PCI device
+ */
+void pci_disable_sriov(struct pci_dev *dev)
+{
+ might_sleep();
+
+ if (!dev->is_physfn)
+ return;
+
+ sriov_disable(dev);
+}
+EXPORT_SYMBOL_GPL(pci_disable_sriov);
+
+/**
+ * pci_num_vf - return number of VFs associated with a PF device_release_driver
+ * @dev: the PCI device
+ *
+ * Returns number of VFs, or 0 if SR-IOV is not enabled.
+ */
+int pci_num_vf(struct pci_dev *dev)
+{
+ if (!dev->is_physfn)
+ return 0;
+
+ return dev->sriov->num_VFs;
+}
+EXPORT_SYMBOL_GPL(pci_num_vf);
+
+/**
+ * pci_vfs_assigned - returns number of VFs are assigned to a guest
+ * @dev: the PCI device
+ *
+ * Returns number of VFs belonging to this device that are assigned to a guest.
+ * If device is not a physical function returns 0.
+ */
+int pci_vfs_assigned(struct pci_dev *dev)
+{
+ struct pci_dev *vfdev;
+ unsigned int vfs_assigned = 0;
+ unsigned short dev_id;
+
+ /* only search if we are a PF */
+ if (!dev->is_physfn)
+ return 0;
+
+ /*
+ * determine the device ID for the VFs, the vendor ID will be the
+ * same as the PF so there is no need to check for that one
+ */
+ pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_VF_DID, &dev_id);
+
+ /* loop through all the VFs to see if we own any that are assigned */
+ vfdev = pci_get_device(dev->vendor, dev_id, NULL);
+ while (vfdev) {
+ /*
+ * It is considered assigned if it is a virtual function with
+ * our dev as the physical function and the assigned bit is set
+ */
+ if (vfdev->is_virtfn && (vfdev->physfn == dev) &&
+ (vfdev->dev_flags & PCI_DEV_FLAGS_ASSIGNED))
+ vfs_assigned++;
+
+ vfdev = pci_get_device(dev->vendor, dev_id, vfdev);
+ }
+
+ return vfs_assigned;
+}
+EXPORT_SYMBOL_GPL(pci_vfs_assigned);
+
+/**
+ * pci_sriov_set_totalvfs -- reduce the TotalVFs available
+ * @dev: the PCI PF device
+ * @numvfs: number that should be used for TotalVFs supported
+ *
+ * Should be called from PF driver's probe routine with
+ * device's mutex held.
+ *
+ * Returns 0 if PF is an SRIOV-capable device and
+ * value of numvfs valid. If not a PF return -ENOSYS;
+ * if numvfs is invalid return -EINVAL;
+ * if VFs already enabled, return -EBUSY.
+ */
+int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
+{
+ if (!dev->is_physfn)
+ return -ENOSYS;
+ if (numvfs > dev->sriov->total_VFs)
+ return -EINVAL;
+
+ /* Shouldn't change if VFs already enabled */
+ if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE)
+ return -EBUSY;
+ else
+ dev->sriov->driver_max_VFs = numvfs;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
+
+/**
+ * pci_sriov_get_totalvfs -- get total VFs supported on this device
+ * @dev: the PCI PF device
+ *
+ * For a PCIe device with SRIOV support, return the PCIe
+ * SRIOV capability value of TotalVFs or the value of driver_max_VFs
+ * if the driver reduced it. Otherwise 0.
+ */
+int pci_sriov_get_totalvfs(struct pci_dev *dev)
+{
+ if (!dev->is_physfn)
+ return 0;
+
+ if (dev->sriov->driver_max_VFs)
+ return dev->sriov->driver_max_VFs;
+
+ return dev->sriov->total_VFs;
+}
+EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs);
diff --git a/drivers/pci/iova.c b/drivers/pci/iova.c
deleted file mode 100644
index dbcdd6bfa63..00000000000
--- a/drivers/pci/iova.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (c) 2006, Intel Corporation.
- *
- * This file is released under the GPLv2.
- *
- * Copyright (C) 2006-2008 Intel Corporation
- * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
- */
-
-#include "iova.h"
-
-void
-init_iova_domain(struct iova_domain *iovad, unsigned long pfn_32bit)
-{
- spin_lock_init(&iovad->iova_alloc_lock);
- spin_lock_init(&iovad->iova_rbtree_lock);
- iovad->rbroot = RB_ROOT;
- iovad->cached32_node = NULL;
- iovad->dma_32bit_pfn = pfn_32bit;
-}
-
-static struct rb_node *
-__get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn)
-{
- if ((*limit_pfn != iovad->dma_32bit_pfn) ||
- (iovad->cached32_node == NULL))
- return rb_last(&iovad->rbroot);
- else {
- struct rb_node *prev_node = rb_prev(iovad->cached32_node);
- struct iova *curr_iova =
- container_of(iovad->cached32_node, struct iova, node);
- *limit_pfn = curr_iova->pfn_lo - 1;
- return prev_node;
- }
-}
-
-static void
-__cached_rbnode_insert_update(struct iova_domain *iovad,
- unsigned long limit_pfn, struct iova *new)
-{
- if (limit_pfn != iovad->dma_32bit_pfn)
- return;
- iovad->cached32_node = &new->node;
-}
-
-static void
-__cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free)
-{
- struct iova *cached_iova;
- struct rb_node *curr;
-
- if (!iovad->cached32_node)
- return;
- curr = iovad->cached32_node;
- cached_iova = container_of(curr, struct iova, node);
-
- if (free->pfn_lo >= cached_iova->pfn_lo)
- iovad->cached32_node = rb_next(&free->node);
-}
-
-/* Computes the padding size required, to make the
- * the start address naturally aligned on its size
- */
-static int
-iova_get_pad_size(int size, unsigned int limit_pfn)
-{
- unsigned int pad_size = 0;
- unsigned int order = ilog2(size);
-
- if (order)
- pad_size = (limit_pfn + 1) % (1 << order);
-
- return pad_size;
-}
-
-static int __alloc_iova_range(struct iova_domain *iovad, unsigned long size,
- unsigned long limit_pfn, struct iova *new, bool size_aligned)
-{
- struct rb_node *curr = NULL;
- unsigned long flags;
- unsigned long saved_pfn;
- unsigned int pad_size = 0;
-
- /* Walk the tree backwards */
- spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
- saved_pfn = limit_pfn;
- curr = __get_cached_rbnode(iovad, &limit_pfn);
- while (curr) {
- struct iova *curr_iova = container_of(curr, struct iova, node);
- if (limit_pfn < curr_iova->pfn_lo)
- goto move_left;
- else if (limit_pfn < curr_iova->pfn_hi)
- goto adjust_limit_pfn;
- else {
- if (size_aligned)
- pad_size = iova_get_pad_size(size, limit_pfn);
- if ((curr_iova->pfn_hi + size + pad_size) <= limit_pfn)
- break; /* found a free slot */
- }
-adjust_limit_pfn:
- limit_pfn = curr_iova->pfn_lo - 1;
-move_left:
- curr = rb_prev(curr);
- }
-
- if (!curr) {
- if (size_aligned)
- pad_size = iova_get_pad_size(size, limit_pfn);
- if ((IOVA_START_PFN + size + pad_size) > limit_pfn) {
- spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
- return -ENOMEM;
- }
- }
-
- /* pfn_lo will point to size aligned address if size_aligned is set */
- new->pfn_lo = limit_pfn - (size + pad_size) + 1;
- new->pfn_hi = new->pfn_lo + size - 1;
-
- spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
- return 0;
-}
-
-static void
-iova_insert_rbtree(struct rb_root *root, struct iova *iova)
-{
- struct rb_node **new = &(root->rb_node), *parent = NULL;
- /* Figure out where to put new node */
- while (*new) {
- struct iova *this = container_of(*new, struct iova, node);
- parent = *new;
-
- if (iova->pfn_lo < this->pfn_lo)
- new = &((*new)->rb_left);
- else if (iova->pfn_lo > this->pfn_lo)
- new = &((*new)->rb_right);
- else
- BUG(); /* this should not happen */
- }
- /* Add new node and rebalance tree. */
- rb_link_node(&iova->node, parent, new);
- rb_insert_color(&iova->node, root);
-}
-
-/**
- * alloc_iova - allocates an iova
- * @iovad - iova domain in question
- * @size - size of page frames to allocate
- * @limit_pfn - max limit address
- * @size_aligned - set if size_aligned address range is required
- * This function allocates an iova in the range limit_pfn to IOVA_START_PFN
- * looking from limit_pfn instead from IOVA_START_PFN. If the size_aligned
- * flag is set then the allocated address iova->pfn_lo will be naturally
- * aligned on roundup_power_of_two(size).
- */
-struct iova *
-alloc_iova(struct iova_domain *iovad, unsigned long size,
- unsigned long limit_pfn,
- bool size_aligned)
-{
- unsigned long flags;
- struct iova *new_iova;
- int ret;
-
- new_iova = alloc_iova_mem();
- if (!new_iova)
- return NULL;
-
- /* If size aligned is set then round the size to
- * to next power of two.
- */
- if (size_aligned)
- size = __roundup_pow_of_two(size);
-
- spin_lock_irqsave(&iovad->iova_alloc_lock, flags);
- ret = __alloc_iova_range(iovad, size, limit_pfn, new_iova,
- size_aligned);
-
- if (ret) {
- spin_unlock_irqrestore(&iovad->iova_alloc_lock, flags);
- free_iova_mem(new_iova);
- return NULL;
- }
-
- /* Insert the new_iova into domain rbtree by holding writer lock */
- spin_lock(&iovad->iova_rbtree_lock);
- iova_insert_rbtree(&iovad->rbroot, new_iova);
- __cached_rbnode_insert_update(iovad, limit_pfn, new_iova);
- spin_unlock(&iovad->iova_rbtree_lock);
-
- spin_unlock_irqrestore(&iovad->iova_alloc_lock, flags);
-
- return new_iova;
-}
-
-/**
- * find_iova - find's an iova for a given pfn
- * @iovad - iova domain in question.
- * pfn - page frame number
- * This function finds and returns an iova belonging to the
- * given doamin which matches the given pfn.
- */
-struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn)
-{
- unsigned long flags;
- struct rb_node *node;
-
- /* Take the lock so that no other thread is manipulating the rbtree */
- spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
- node = iovad->rbroot.rb_node;
- while (node) {
- struct iova *iova = container_of(node, struct iova, node);
-
- /* If pfn falls within iova's range, return iova */
- if ((pfn >= iova->pfn_lo) && (pfn <= iova->pfn_hi)) {
- spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
- /* We are not holding the lock while this iova
- * is referenced by the caller as the same thread
- * which called this function also calls __free_iova()
- * and it is by desing that only one thread can possibly
- * reference a particular iova and hence no conflict.
- */
- return iova;
- }
-
- if (pfn < iova->pfn_lo)
- node = node->rb_left;
- else if (pfn > iova->pfn_lo)
- node = node->rb_right;
- }
-
- spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
- return NULL;
-}
-
-/**
- * __free_iova - frees the given iova
- * @iovad: iova domain in question.
- * @iova: iova in question.
- * Frees the given iova belonging to the giving domain
- */
-void
-__free_iova(struct iova_domain *iovad, struct iova *iova)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
- __cached_rbnode_delete_update(iovad, iova);
- rb_erase(&iova->node, &iovad->rbroot);
- spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
- free_iova_mem(iova);
-}
-
-/**
- * free_iova - finds and frees the iova for a given pfn
- * @iovad: - iova domain in question.
- * @pfn: - pfn that is allocated previously
- * This functions finds an iova for a given pfn and then
- * frees the iova from that domain.
- */
-void
-free_iova(struct iova_domain *iovad, unsigned long pfn)
-{
- struct iova *iova = find_iova(iovad, pfn);
- if (iova)
- __free_iova(iovad, iova);
-
-}
-
-/**
- * put_iova_domain - destroys the iova doamin
- * @iovad: - iova domain in question.
- * All the iova's in that domain are destroyed.
- */
-void put_iova_domain(struct iova_domain *iovad)
-{
- struct rb_node *node;
- unsigned long flags;
-
- spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
- node = rb_first(&iovad->rbroot);
- while (node) {
- struct iova *iova = container_of(node, struct iova, node);
- rb_erase(node, &iovad->rbroot);
- free_iova_mem(iova);
- node = rb_first(&iovad->rbroot);
- }
- spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
-}
-
-static int
-__is_range_overlap(struct rb_node *node,
- unsigned long pfn_lo, unsigned long pfn_hi)
-{
- struct iova *iova = container_of(node, struct iova, node);
-
- if ((pfn_lo <= iova->pfn_hi) && (pfn_hi >= iova->pfn_lo))
- return 1;
- return 0;
-}
-
-static struct iova *
-__insert_new_range(struct iova_domain *iovad,
- unsigned long pfn_lo, unsigned long pfn_hi)
-{
- struct iova *iova;
-
- iova = alloc_iova_mem();
- if (!iova)
- return iova;
-
- iova->pfn_hi = pfn_hi;
- iova->pfn_lo = pfn_lo;
- iova_insert_rbtree(&iovad->rbroot, iova);
- return iova;
-}
-
-static void
-__adjust_overlap_range(struct iova *iova,
- unsigned long *pfn_lo, unsigned long *pfn_hi)
-{
- if (*pfn_lo < iova->pfn_lo)
- iova->pfn_lo = *pfn_lo;
- if (*pfn_hi > iova->pfn_hi)
- *pfn_lo = iova->pfn_hi + 1;
-}
-
-/**
- * reserve_iova - reserves an iova in the given range
- * @iovad: - iova domain pointer
- * @pfn_lo: - lower page frame address
- * @pfn_hi:- higher pfn adderss
- * This function allocates reserves the address range from pfn_lo to pfn_hi so
- * that this address is not dished out as part of alloc_iova.
- */
-struct iova *
-reserve_iova(struct iova_domain *iovad,
- unsigned long pfn_lo, unsigned long pfn_hi)
-{
- struct rb_node *node;
- unsigned long flags;
- struct iova *iova;
- unsigned int overlap = 0;
-
- spin_lock_irqsave(&iovad->iova_alloc_lock, flags);
- spin_lock(&iovad->iova_rbtree_lock);
- for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) {
- if (__is_range_overlap(node, pfn_lo, pfn_hi)) {
- iova = container_of(node, struct iova, node);
- __adjust_overlap_range(iova, &pfn_lo, &pfn_hi);
- if ((pfn_lo >= iova->pfn_lo) &&
- (pfn_hi <= iova->pfn_hi))
- goto finish;
- overlap = 1;
-
- } else if (overlap)
- break;
- }
-
- /* We are here either becasue this is the first reserver node
- * or need to insert remaining non overlap addr range
- */
- iova = __insert_new_range(iovad, pfn_lo, pfn_hi);
-finish:
-
- spin_unlock(&iovad->iova_rbtree_lock);
- spin_unlock_irqrestore(&iovad->iova_alloc_lock, flags);
- return iova;
-}
-
-/**
- * copy_reserved_iova - copies the reserved between domains
- * @from: - source doamin from where to copy
- * @to: - destination domin where to copy
- * This function copies reserved iova's from one doamin to
- * other.
- */
-void
-copy_reserved_iova(struct iova_domain *from, struct iova_domain *to)
-{
- unsigned long flags;
- struct rb_node *node;
-
- spin_lock_irqsave(&from->iova_alloc_lock, flags);
- spin_lock(&from->iova_rbtree_lock);
- for (node = rb_first(&from->rbroot); node; node = rb_next(node)) {
- struct iova *iova = container_of(node, struct iova, node);
- struct iova *new_iova;
- new_iova = reserve_iova(to, iova->pfn_lo, iova->pfn_hi);
- if (!new_iova)
- printk(KERN_ERR "Reserve iova range %lx@%lx failed\n",
- iova->pfn_lo, iova->pfn_lo);
- }
- spin_unlock(&from->iova_rbtree_lock);
- spin_unlock_irqrestore(&from->iova_alloc_lock, flags);
-}
diff --git a/drivers/pci/iova.h b/drivers/pci/iova.h
deleted file mode 100644
index 228f6c94b69..00000000000
--- a/drivers/pci/iova.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2006, Intel Corporation.
- *
- * This file is released under the GPLv2.
- *
- * Copyright (C) 2006-2008 Intel Corporation
- * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
- *
- */
-
-#ifndef _IOVA_H_
-#define _IOVA_H_
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/rbtree.h>
-#include <linux/dma-mapping.h>
-
-/* IO virtual address start page frame number */
-#define IOVA_START_PFN (1)
-
-/* iova structure */
-struct iova {
- struct rb_node node;
- unsigned long pfn_hi; /* IOMMU dish out addr hi */
- unsigned long pfn_lo; /* IOMMU dish out addr lo */
-};
-
-/* holds all the iova translations for a domain */
-struct iova_domain {
- spinlock_t iova_alloc_lock;/* Lock to protect iova allocation */
- spinlock_t iova_rbtree_lock; /* Lock to protect update of rbtree */
- struct rb_root rbroot; /* iova domain rbtree root */
- struct rb_node *cached32_node; /* Save last alloced node */
- unsigned long dma_32bit_pfn;
-};
-
-struct iova *alloc_iova_mem(void);
-void free_iova_mem(struct iova *iova);
-void free_iova(struct iova_domain *iovad, unsigned long pfn);
-void __free_iova(struct iova_domain *iovad, struct iova *iova);
-struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size,
- unsigned long limit_pfn,
- bool size_aligned);
-struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo,
- unsigned long pfn_hi);
-void copy_reserved_iova(struct iova_domain *from, struct iova_domain *to);
-void init_iova_domain(struct iova_domain *iovad, unsigned long pfn_32bit);
-struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn);
-void put_iova_domain(struct iova_domain *iovad);
-
-#endif
diff --git a/drivers/pci/irq.c b/drivers/pci/irq.c
new file mode 100644
index 00000000000..6684f153ab5
--- /dev/null
+++ b/drivers/pci/irq.c
@@ -0,0 +1,61 @@
+/*
+ * PCI IRQ failure handing code
+ *
+ * Copyright (c) 2008 James Bottomley <James.Bottomley@HansenPartnership.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/pci.h>
+
+static void pci_note_irq_problem(struct pci_dev *pdev, const char *reason)
+{
+ struct pci_dev *parent = to_pci_dev(pdev->dev.parent);
+
+ dev_err(&pdev->dev,
+ "Potentially misrouted IRQ (Bridge %s %04x:%04x)\n",
+ dev_name(&parent->dev), parent->vendor, parent->device);
+ dev_err(&pdev->dev, "%s\n", reason);
+ dev_err(&pdev->dev, "Please report to linux-kernel@vger.kernel.org\n");
+ WARN_ON(1);
+}
+
+/**
+ * pci_lost_interrupt - reports a lost PCI interrupt
+ * @pdev: device whose interrupt is lost
+ *
+ * The primary function of this routine is to report a lost interrupt
+ * in a standard way which users can recognise (instead of blaming the
+ * driver).
+ *
+ * Returns:
+ * a suggestion for fixing it (although the driver is not required to
+ * act on this).
+ */
+enum pci_lost_interrupt_reason pci_lost_interrupt(struct pci_dev *pdev)
+{
+ if (pdev->msi_enabled || pdev->msix_enabled) {
+ enum pci_lost_interrupt_reason ret;
+
+ if (pdev->msix_enabled) {
+ pci_note_irq_problem(pdev, "MSIX routing failure");
+ ret = PCI_LOST_IRQ_DISABLE_MSIX;
+ } else {
+ pci_note_irq_problem(pdev, "MSI routing failure");
+ ret = PCI_LOST_IRQ_DISABLE_MSI;
+ }
+ return ret;
+ }
+#ifdef CONFIG_ACPI
+ if (!(acpi_disabled || acpi_noirq)) {
+ pci_note_irq_problem(pdev, "Potential ACPI misrouting please reboot with acpi=noirq");
+ /* currently no way to fix acpi on the fly */
+ return PCI_LOST_IRQ_DISABLE_ACPI;
+ }
+#endif
+ pci_note_irq_problem(pdev, "unknown cause (not MSI or ACPI)");
+ return PCI_LOST_IRQ_NO_INFORMATION;
+}
+EXPORT_SYMBOL(pci_lost_interrupt);
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 26938da8f43..13f3d303727 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -10,263 +10,433 @@
#include <linux/mm.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
-#include <linux/init.h>
+#include <linux/export.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/msi.h>
#include <linux/smp.h>
-
-#include <asm/errno.h>
-#include <asm/io.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/slab.h>
#include "pci.h"
-#include "msi.h"
static int pci_msi_enable = 1;
+#define msix_table_size(flags) ((flags & PCI_MSIX_FLAGS_QSIZE) + 1)
+
+
/* Arch hooks */
-int __attribute__ ((weak))
-arch_msi_check_device(struct pci_dev *dev, int nvec, int type)
+int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
{
+ struct msi_chip *chip = dev->bus->msi;
+ int err;
+
+ if (!chip || !chip->setup_irq)
+ return -EINVAL;
+
+ err = chip->setup_irq(chip, dev, desc);
+ if (err < 0)
+ return err;
+
+ irq_set_chip_data(desc->irq, chip);
+
return 0;
}
-int __attribute__ ((weak))
-arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *entry)
+void __weak arch_teardown_msi_irq(unsigned int irq)
{
- return 0;
+ struct msi_chip *chip = irq_get_chip_data(irq);
+
+ if (!chip || !chip->teardown_irq)
+ return;
+
+ chip->teardown_irq(chip, irq);
}
-int __attribute__ ((weak))
-arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+int __weak arch_msi_check_device(struct pci_dev *dev, int nvec, int type)
+{
+ struct msi_chip *chip = dev->bus->msi;
+
+ if (!chip || !chip->check_device)
+ return 0;
+
+ return chip->check_device(chip, dev, nvec, type);
+}
+
+int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
struct msi_desc *entry;
int ret;
+ /*
+ * If an architecture wants to support multiple MSI, it needs to
+ * override arch_setup_msi_irqs()
+ */
+ if (type == PCI_CAP_ID_MSI && nvec > 1)
+ return 1;
+
list_for_each_entry(entry, &dev->msi_list, list) {
ret = arch_setup_msi_irq(dev, entry);
- if (ret)
+ if (ret < 0)
return ret;
+ if (ret > 0)
+ return -ENOSPC;
}
return 0;
}
-void __attribute__ ((weak)) arch_teardown_msi_irq(unsigned int irq)
+/*
+ * We have a default implementation available as a separate non-weak
+ * function, as it is used by the Xen x86 PCI code
+ */
+void default_teardown_msi_irqs(struct pci_dev *dev)
{
- return;
+ struct msi_desc *entry;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ int i, nvec;
+ if (entry->irq == 0)
+ continue;
+ if (entry->nvec_used)
+ nvec = entry->nvec_used;
+ else
+ nvec = 1 << entry->msi_attrib.multiple;
+ for (i = 0; i < nvec; i++)
+ arch_teardown_msi_irq(entry->irq + i);
+ }
+}
+
+void __weak arch_teardown_msi_irqs(struct pci_dev *dev)
+{
+ return default_teardown_msi_irqs(dev);
}
-void __attribute__ ((weak))
-arch_teardown_msi_irqs(struct pci_dev *dev)
+static void default_restore_msi_irq(struct pci_dev *dev, int irq)
{
struct msi_desc *entry;
- list_for_each_entry(entry, &dev->msi_list, list) {
- if (entry->irq != 0)
- arch_teardown_msi_irq(entry->irq);
+ entry = NULL;
+ if (dev->msix_enabled) {
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ if (irq == entry->irq)
+ break;
+ }
+ } else if (dev->msi_enabled) {
+ entry = irq_get_msi_desc(irq);
}
+
+ if (entry)
+ write_msi_msg(irq, &entry->msg);
+}
+
+void __weak arch_restore_msi_irqs(struct pci_dev *dev)
+{
+ return default_restore_msi_irqs(dev);
}
static void msi_set_enable(struct pci_dev *dev, 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;
- if (enable)
- control |= PCI_MSI_FLAGS_ENABLE;
- pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
- }
+ pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
+ control &= ~PCI_MSI_FLAGS_ENABLE;
+ if (enable)
+ control |= PCI_MSI_FLAGS_ENABLE;
+ pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
}
static void msix_set_enable(struct pci_dev *dev, int enable)
{
- int pos;
u16 control;
- pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
- if (pos) {
- pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
- control &= ~PCI_MSIX_FLAGS_ENABLE;
- if (enable)
- control |= PCI_MSIX_FLAGS_ENABLE;
- pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
- }
+ pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control);
+ control &= ~PCI_MSIX_FLAGS_ENABLE;
+ if (enable)
+ control |= PCI_MSIX_FLAGS_ENABLE;
+ pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control);
}
-static void msix_flush_writes(unsigned int irq)
+static inline __attribute_const__ u32 msi_mask(unsigned x)
{
- struct msi_desc *entry;
+ /* Don't shift by >= width of type */
+ if (x >= 5)
+ return 0xffffffff;
+ return (1 << (1 << x)) - 1;
+}
- entry = get_irq_msi(irq);
- BUG_ON(!entry || !entry->dev);
- switch (entry->msi_attrib.type) {
- case PCI_CAP_ID_MSI:
- /* nothing to do */
- break;
- case PCI_CAP_ID_MSIX:
- {
- int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
- readl(entry->mask_base + offset);
- break;
- }
- default:
- BUG();
- break;
+static inline __attribute_const__ u32 msi_capable_mask(u16 control)
+{
+ return msi_mask((control >> 1) & 7);
+}
+
+static inline __attribute_const__ u32 msi_enabled_mask(u16 control)
+{
+ return msi_mask((control >> 4) & 7);
+}
+
+/*
+ * 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.
+ */
+u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
+{
+ u32 mask_bits = desc->masked;
+
+ if (!desc->msi_attrib.maskbit)
+ return 0;
+
+ mask_bits &= ~mask;
+ mask_bits |= flag;
+ pci_write_config_dword(desc->dev, desc->mask_pos, mask_bits);
+
+ return mask_bits;
+}
+
+__weak u32 arch_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
+{
+ return default_msi_mask_irq(desc, mask, flag);
+}
+
+static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
+{
+ desc->masked = arch_msi_mask_irq(desc, mask, flag);
+}
+
+/*
+ * This internal function does not flush PCI writes to the device.
+ * All users must ensure that they read from the device before either
+ * assuming that the device state is up to date, or returning out of this
+ * file. This saves a few milliseconds when initialising devices with lots
+ * of MSI-X interrupts.
+ */
+u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag)
+{
+ u32 mask_bits = desc->masked;
+ unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
+ PCI_MSIX_ENTRY_VECTOR_CTRL;
+ mask_bits &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
+ if (flag)
+ mask_bits |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
+ writel(mask_bits, desc->mask_base + offset);
+
+ return mask_bits;
+}
+
+__weak u32 arch_msix_mask_irq(struct msi_desc *desc, u32 flag)
+{
+ return default_msix_mask_irq(desc, flag);
+}
+
+static void msix_mask_irq(struct msi_desc *desc, u32 flag)
+{
+ desc->masked = arch_msix_mask_irq(desc, flag);
+}
+
+static void msi_set_mask_bit(struct irq_data *data, u32 flag)
+{
+ struct msi_desc *desc = irq_data_get_msi(data);
+
+ if (desc->msi_attrib.is_msix) {
+ msix_mask_irq(desc, flag);
+ readl(desc->mask_base); /* Flush write to device */
+ } else {
+ unsigned offset = data->irq - desc->dev->irq;
+ msi_mask_irq(desc, 1 << offset, flag << offset);
}
}
-static void msi_set_mask_bit(unsigned int irq, int flag)
+void mask_msi_irq(struct irq_data *data)
+{
+ msi_set_mask_bit(data, 1);
+}
+
+void unmask_msi_irq(struct irq_data *data)
+{
+ msi_set_mask_bit(data, 0);
+}
+
+void default_restore_msi_irqs(struct pci_dev *dev)
{
struct msi_desc *entry;
- entry = get_irq_msi(irq);
- BUG_ON(!entry || !entry->dev);
- switch (entry->msi_attrib.type) {
- case PCI_CAP_ID_MSI:
- if (entry->msi_attrib.maskbit) {
- int pos;
- u32 mask_bits;
-
- pos = (long)entry->mask_base;
- pci_read_config_dword(entry->dev, pos, &mask_bits);
- mask_bits &= ~(1);
- mask_bits |= flag;
- pci_write_config_dword(entry->dev, pos, mask_bits);
- } else {
- msi_set_enable(entry->dev, !flag);
- }
- break;
- case PCI_CAP_ID_MSIX:
- {
- int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
- writel(flag, entry->mask_base + offset);
- readl(entry->mask_base + offset);
- break;
- }
- default:
- BUG();
- break;
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ default_restore_msi_irq(dev, entry->irq);
}
- entry->msi_attrib.masked = !!flag;
}
-void read_msi_msg(unsigned int irq, struct msi_msg *msg)
+void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
{
- struct msi_desc *entry = get_irq_msi(irq);
- switch(entry->msi_attrib.type) {
- case PCI_CAP_ID_MSI:
- {
+ BUG_ON(entry->dev->current_state != PCI_D0);
+
+ if (entry->msi_attrib.is_msix) {
+ void __iomem *base = entry->mask_base +
+ entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
+
+ msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR);
+ msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR);
+ msg->data = readl(base + PCI_MSIX_ENTRY_DATA);
+ } else {
struct pci_dev *dev = entry->dev;
- int pos = entry->msi_attrib.pos;
+ int pos = dev->msi_cap;
u16 data;
- pci_read_config_dword(dev, msi_lower_address_reg(pos),
- &msg->address_lo);
+ pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO,
+ &msg->address_lo);
if (entry->msi_attrib.is_64) {
- pci_read_config_dword(dev, msi_upper_address_reg(pos),
- &msg->address_hi);
- pci_read_config_word(dev, msi_data_reg(pos, 1), &data);
+ pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI,
+ &msg->address_hi);
+ pci_read_config_word(dev, pos + PCI_MSI_DATA_64, &data);
} else {
msg->address_hi = 0;
- pci_read_config_word(dev, msi_data_reg(pos, 0), &data);
+ pci_read_config_word(dev, pos + PCI_MSI_DATA_32, &data);
}
msg->data = data;
- break;
}
- case PCI_CAP_ID_MSIX:
- {
- void __iomem *base;
- base = entry->mask_base +
- entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
+}
- msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
- msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
- msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET);
- break;
- }
- default:
- BUG();
- }
+void read_msi_msg(unsigned int irq, struct msi_msg *msg)
+{
+ struct msi_desc *entry = irq_get_msi_desc(irq);
+
+ __read_msi_msg(entry, msg);
}
-void write_msi_msg(unsigned int irq, struct msi_msg *msg)
+void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
{
- struct msi_desc *entry = get_irq_msi(irq);
- switch (entry->msi_attrib.type) {
- case PCI_CAP_ID_MSI:
- {
- struct pci_dev *dev = entry->dev;
- int pos = entry->msi_attrib.pos;
+ /* Assert that the cache is valid, assuming that
+ * valid messages are not all-zeroes. */
+ BUG_ON(!(entry->msg.address_hi | entry->msg.address_lo |
+ entry->msg.data));
- pci_write_config_dword(dev, msi_lower_address_reg(pos),
- msg->address_lo);
- if (entry->msi_attrib.is_64) {
- pci_write_config_dword(dev, msi_upper_address_reg(pos),
- msg->address_hi);
- pci_write_config_word(dev, msi_data_reg(pos, 1),
- msg->data);
- } else {
- pci_write_config_word(dev, msi_data_reg(pos, 0),
- msg->data);
- }
- break;
- }
- case PCI_CAP_ID_MSIX:
- {
+ *msg = entry->msg;
+}
+
+void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg)
+{
+ struct msi_desc *entry = irq_get_msi_desc(irq);
+
+ __get_cached_msi_msg(entry, msg);
+}
+
+void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
+{
+ if (entry->dev->current_state != PCI_D0) {
+ /* Don't touch the hardware now */
+ } else if (entry->msi_attrib.is_msix) {
void __iomem *base;
base = entry->mask_base +
entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
- writel(msg->address_lo,
- base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
- writel(msg->address_hi,
- base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
- writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET);
- break;
- }
- default:
- BUG();
+ writel(msg->address_lo, base + PCI_MSIX_ENTRY_LOWER_ADDR);
+ writel(msg->address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR);
+ writel(msg->data, base + PCI_MSIX_ENTRY_DATA);
+ } else {
+ struct pci_dev *dev = entry->dev;
+ int pos = dev->msi_cap;
+ u16 msgctl;
+
+ pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl);
+ msgctl &= ~PCI_MSI_FLAGS_QSIZE;
+ msgctl |= entry->msi_attrib.multiple << 4;
+ pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl);
+
+ pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO,
+ msg->address_lo);
+ if (entry->msi_attrib.is_64) {
+ pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI,
+ msg->address_hi);
+ pci_write_config_word(dev, pos + PCI_MSI_DATA_64,
+ msg->data);
+ } else {
+ pci_write_config_word(dev, pos + PCI_MSI_DATA_32,
+ msg->data);
+ }
}
entry->msg = *msg;
}
-void mask_msi_irq(unsigned int irq)
+void write_msi_msg(unsigned int irq, struct msi_msg *msg)
{
- msi_set_mask_bit(irq, 1);
- msix_flush_writes(irq);
+ struct msi_desc *entry = irq_get_msi_desc(irq);
+
+ __write_msi_msg(entry, msg);
}
-void unmask_msi_irq(unsigned int irq)
+static void free_msi_irqs(struct pci_dev *dev)
{
- msi_set_mask_bit(irq, 0);
- msix_flush_writes(irq);
-}
+ struct msi_desc *entry, *tmp;
+ struct attribute **msi_attrs;
+ struct device_attribute *dev_attr;
+ int count = 0;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ int i, nvec;
+ if (!entry->irq)
+ continue;
+ if (entry->nvec_used)
+ nvec = entry->nvec_used;
+ else
+ nvec = 1 << entry->msi_attrib.multiple;
+ for (i = 0; i < nvec; i++)
+ BUG_ON(irq_has_action(entry->irq + i));
+ }
-static int msi_free_irqs(struct pci_dev* dev);
+ arch_teardown_msi_irqs(dev);
+ list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) {
+ if (entry->msi_attrib.is_msix) {
+ if (list_is_last(&entry->list, &dev->msi_list))
+ iounmap(entry->mask_base);
+ }
-static struct msi_desc* alloc_msi_entry(void)
-{
- struct msi_desc *entry;
+ /*
+ * Its possible that we get into this path
+ * When populate_msi_sysfs fails, which means the entries
+ * were not registered with sysfs. In that case don't
+ * unregister them.
+ */
+ if (entry->kobj.parent) {
+ kobject_del(&entry->kobj);
+ kobject_put(&entry->kobj);
+ }
- entry = kzalloc(sizeof(struct msi_desc), GFP_KERNEL);
- if (!entry)
+ list_del(&entry->list);
+ kfree(entry);
+ }
+
+ if (dev->msi_irq_groups) {
+ sysfs_remove_groups(&dev->dev.kobj, dev->msi_irq_groups);
+ msi_attrs = dev->msi_irq_groups[0]->attrs;
+ while (msi_attrs[count]) {
+ dev_attr = container_of(msi_attrs[count],
+ struct device_attribute, attr);
+ kfree(dev_attr->attr.name);
+ kfree(dev_attr);
+ ++count;
+ }
+ kfree(msi_attrs);
+ kfree(dev->msi_irq_groups[0]);
+ kfree(dev->msi_irq_groups);
+ dev->msi_irq_groups = NULL;
+ }
+}
+
+static struct msi_desc *alloc_msi_entry(struct pci_dev *dev)
+{
+ struct msi_desc *desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
return NULL;
- INIT_LIST_HEAD(&entry->list);
- entry->irq = 0;
- entry->dev = NULL;
+ INIT_LIST_HEAD(&desc->list);
+ desc->dev = dev;
- return entry;
+ return desc;
}
static void pci_intx_for_msi(struct pci_dev *dev, int enable)
@@ -277,54 +447,48 @@ static void pci_intx_for_msi(struct pci_dev *dev, int enable)
static void __pci_restore_msi_state(struct pci_dev *dev)
{
- int pos;
u16 control;
struct msi_desc *entry;
if (!dev->msi_enabled)
return;
- entry = get_irq_msi(dev->irq);
- pos = entry->msi_attrib.pos;
+ entry = irq_get_msi_desc(dev->irq);
pci_intx_for_msi(dev, 0);
msi_set_enable(dev, 0);
- write_msi_msg(dev->irq, &entry->msg);
- if (entry->msi_attrib.maskbit)
- msi_set_mask_bit(dev->irq, entry->msi_attrib.masked);
+ arch_restore_msi_irqs(dev);
- 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;
- pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
+ pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
+ msi_mask_irq(entry, msi_capable_mask(control), entry->masked);
+ control &= ~PCI_MSI_FLAGS_QSIZE;
+ control |= (entry->msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE;
+ pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
}
static void __pci_restore_msix_state(struct pci_dev *dev)
{
- int pos;
struct msi_desc *entry;
u16 control;
if (!dev->msix_enabled)
return;
+ BUG_ON(list_empty(&dev->msi_list));
+ entry = list_first_entry(&dev->msi_list, struct msi_desc, list);
+ pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control);
/* route the table */
pci_intx_for_msi(dev, 0);
- msix_set_enable(dev, 0);
+ control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL;
+ pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control);
+ arch_restore_msi_irqs(dev);
list_for_each_entry(entry, &dev->msi_list, list) {
- write_msi_msg(entry->irq, &entry->msg);
- msi_set_mask_bit(entry->irq, entry->msi_attrib.masked);
+ msix_mask_irq(entry, entry->masked);
}
- BUG_ON(list_empty(&dev->msi_list));
- entry = list_entry(dev->msi_list.next, struct msi_desc, list);
- pos = entry->msi_attrib.pos;
- pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
control &= ~PCI_MSIX_FLAGS_MASKALL;
- control |= PCI_MSIX_FLAGS_ENABLE;
- pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
+ pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control);
}
void pci_restore_msi_state(struct pci_dev *dev)
@@ -334,61 +498,159 @@ void pci_restore_msi_state(struct pci_dev *dev)
}
EXPORT_SYMBOL_GPL(pci_restore_msi_state);
+static ssize_t msi_mode_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct msi_desc *entry;
+ unsigned long irq;
+ int retval;
+
+ retval = kstrtoul(attr->attr.name, 10, &irq);
+ if (retval)
+ return retval;
+
+ list_for_each_entry(entry, &pdev->msi_list, list) {
+ if (entry->irq == irq) {
+ return sprintf(buf, "%s\n",
+ entry->msi_attrib.is_msix ? "msix" : "msi");
+ }
+ }
+ return -ENODEV;
+}
+
+static int populate_msi_sysfs(struct pci_dev *pdev)
+{
+ struct attribute **msi_attrs;
+ struct attribute *msi_attr;
+ struct device_attribute *msi_dev_attr;
+ struct attribute_group *msi_irq_group;
+ const struct attribute_group **msi_irq_groups;
+ struct msi_desc *entry;
+ int ret = -ENOMEM;
+ int num_msi = 0;
+ int count = 0;
+
+ /* Determine how many msi entries we have */
+ list_for_each_entry(entry, &pdev->msi_list, list) {
+ ++num_msi;
+ }
+ if (!num_msi)
+ return 0;
+
+ /* Dynamically create the MSI attributes for the PCI device */
+ msi_attrs = kzalloc(sizeof(void *) * (num_msi + 1), GFP_KERNEL);
+ if (!msi_attrs)
+ return -ENOMEM;
+ list_for_each_entry(entry, &pdev->msi_list, list) {
+ msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL);
+ if (!msi_dev_attr)
+ goto error_attrs;
+ msi_attrs[count] = &msi_dev_attr->attr;
+
+ sysfs_attr_init(&msi_dev_attr->attr);
+ msi_dev_attr->attr.name = kasprintf(GFP_KERNEL, "%d",
+ entry->irq);
+ if (!msi_dev_attr->attr.name)
+ goto error_attrs;
+ msi_dev_attr->attr.mode = S_IRUGO;
+ msi_dev_attr->show = msi_mode_show;
+ ++count;
+ }
+
+ msi_irq_group = kzalloc(sizeof(*msi_irq_group), GFP_KERNEL);
+ if (!msi_irq_group)
+ goto error_attrs;
+ msi_irq_group->name = "msi_irqs";
+ msi_irq_group->attrs = msi_attrs;
+
+ msi_irq_groups = kzalloc(sizeof(void *) * 2, GFP_KERNEL);
+ if (!msi_irq_groups)
+ goto error_irq_group;
+ msi_irq_groups[0] = msi_irq_group;
+
+ ret = sysfs_create_groups(&pdev->dev.kobj, msi_irq_groups);
+ if (ret)
+ goto error_irq_groups;
+ pdev->msi_irq_groups = msi_irq_groups;
+
+ return 0;
+
+error_irq_groups:
+ kfree(msi_irq_groups);
+error_irq_group:
+ kfree(msi_irq_group);
+error_attrs:
+ count = 0;
+ msi_attr = msi_attrs[count];
+ while (msi_attr) {
+ msi_dev_attr = container_of(msi_attr, struct device_attribute, attr);
+ kfree(msi_attr->name);
+ kfree(msi_dev_attr);
+ ++count;
+ msi_attr = msi_attrs[count];
+ }
+ kfree(msi_attrs);
+ return ret;
+}
+
/**
* msi_capability_init - configure device's MSI capability structure
* @dev: pointer to the pci_dev data structure of MSI device function
+ * @nvec: number of interrupts to allocate
*
- * Setup the MSI capability structure of device function with a single
- * MSI irq, regardless of device function is capable of handling
- * multiple messages. A return of zero indicates the successful setup
- * of an entry zero with the new MSI irq or non-zero for otherwise.
- **/
-static int msi_capability_init(struct pci_dev *dev)
+ * Setup the MSI capability structure of the device with the requested
+ * number of interrupts. A return value of zero indicates the successful
+ * setup of an entry with the new MSI irq. A negative return value indicates
+ * an error, and a positive return value indicates the number of interrupts
+ * which could have been allocated.
+ */
+static int msi_capability_init(struct pci_dev *dev, int nvec)
{
struct msi_desc *entry;
- int pos, ret;
+ int ret;
u16 control;
+ unsigned mask;
- msi_set_enable(dev, 0); /* Ensure msi is disabled as I set it up */
+ msi_set_enable(dev, 0); /* Disable MSI during set up */
- pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
- pci_read_config_word(dev, msi_control_reg(pos), &control);
+ pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
/* MSI Entry Initialization */
- entry = alloc_msi_entry();
+ entry = alloc_msi_entry(dev);
if (!entry)
return -ENOMEM;
- entry->msi_attrib.type = PCI_CAP_ID_MSI;
- entry->msi_attrib.is_64 = is_64bit_address(control);
- entry->msi_attrib.entry_nr = 0;
- entry->msi_attrib.maskbit = is_mask_bit_support(control);
- entry->msi_attrib.masked = 1;
- entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
- entry->msi_attrib.pos = pos;
- if (is_mask_bit_support(control)) {
- entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
- is_64bit_address(control));
- }
- entry->dev = dev;
- if (entry->msi_attrib.maskbit) {
- unsigned int maskbits, temp;
- /* All MSIs are unmasked by default, Mask them all */
- pci_read_config_dword(dev,
- msi_mask_bits_reg(pos, is_64bit_address(control)),
- &maskbits);
- temp = (1 << multi_msi_capable(control));
- temp = ((temp - 1) & ~temp);
- maskbits |= temp;
- pci_write_config_dword(dev,
- msi_mask_bits_reg(pos, is_64bit_address(control)),
- maskbits);
- }
+ entry->msi_attrib.is_msix = 0;
+ entry->msi_attrib.is_64 = !!(control & PCI_MSI_FLAGS_64BIT);
+ entry->msi_attrib.entry_nr = 0;
+ entry->msi_attrib.maskbit = !!(control & PCI_MSI_FLAGS_MASKBIT);
+ entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
+ entry->msi_attrib.pos = dev->msi_cap;
+
+ if (control & PCI_MSI_FLAGS_64BIT)
+ entry->mask_pos = dev->msi_cap + PCI_MSI_MASK_64;
+ else
+ entry->mask_pos = dev->msi_cap + PCI_MSI_MASK_32;
+ /* All MSIs are unmasked by default, Mask them all */
+ if (entry->msi_attrib.maskbit)
+ pci_read_config_dword(dev, entry->mask_pos, &entry->masked);
+ mask = msi_capable_mask(control);
+ msi_mask_irq(entry, mask, mask);
+
list_add_tail(&entry->list, &dev->msi_list);
/* Configure MSI capability structure */
- ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI);
+ ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI);
if (ret) {
- msi_free_irqs(dev);
+ msi_mask_irq(entry, mask, ~mask);
+ free_msi_irqs(dev);
+ return ret;
+ }
+
+ ret = populate_msi_sysfs(dev);
+ if (ret) {
+ msi_mask_irq(entry, mask, ~mask);
+ free_msi_irqs(dev);
return ret;
}
@@ -401,6 +663,69 @@ static int msi_capability_init(struct pci_dev *dev)
return 0;
}
+static void __iomem *msix_map_region(struct pci_dev *dev, unsigned nr_entries)
+{
+ resource_size_t phys_addr;
+ u32 table_offset;
+ u8 bir;
+
+ pci_read_config_dword(dev, dev->msix_cap + PCI_MSIX_TABLE,
+ &table_offset);
+ bir = (u8)(table_offset & PCI_MSIX_TABLE_BIR);
+ table_offset &= PCI_MSIX_TABLE_OFFSET;
+ phys_addr = pci_resource_start(dev, bir) + table_offset;
+
+ return ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE);
+}
+
+static int msix_setup_entries(struct pci_dev *dev, void __iomem *base,
+ struct msix_entry *entries, int nvec)
+{
+ struct msi_desc *entry;
+ int i;
+
+ for (i = 0; i < nvec; i++) {
+ entry = alloc_msi_entry(dev);
+ if (!entry) {
+ if (!i)
+ iounmap(base);
+ else
+ free_msi_irqs(dev);
+ /* No enough memory. Don't try again */
+ return -ENOMEM;
+ }
+
+ entry->msi_attrib.is_msix = 1;
+ entry->msi_attrib.is_64 = 1;
+ entry->msi_attrib.entry_nr = entries[i].entry;
+ entry->msi_attrib.default_irq = dev->irq;
+ entry->msi_attrib.pos = dev->msix_cap;
+ entry->mask_base = base;
+
+ list_add_tail(&entry->list, &dev->msi_list);
+ }
+
+ return 0;
+}
+
+static void msix_program_entries(struct pci_dev *dev,
+ struct msix_entry *entries)
+{
+ struct msi_desc *entry;
+ int i = 0;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ int offset = entries[i].entry * PCI_MSIX_ENTRY_SIZE +
+ PCI_MSIX_ENTRY_VECTOR_CTRL;
+
+ entries[i].vector = entry->irq;
+ irq_set_msi_desc(entry->irq, entry);
+ entry->masked = readl(entry->mask_base + offset);
+ msix_mask_irq(entry, 1);
+ i++;
+ }
+}
+
/**
* msix_capability_init - configure device's MSI-X capability
* @dev: pointer to the pci_dev data structure of MSI-X device function
@@ -414,80 +739,73 @@ static int msi_capability_init(struct pci_dev *dev)
static int msix_capability_init(struct pci_dev *dev,
struct msix_entry *entries, int nvec)
{
- struct msi_desc *entry;
- int pos, i, j, nr_entries, ret;
- unsigned long phys_addr;
- u32 table_offset;
- u16 control;
- u8 bir;
+ int ret;
+ u16 control;
void __iomem *base;
- msix_set_enable(dev, 0);/* Ensure msix is disabled as I set it up */
+ pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control);
+
+ /* Ensure MSI-X is disabled while it is set up */
+ control &= ~PCI_MSIX_FLAGS_ENABLE;
+ pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control);
- pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
/* Request & Map MSI-X table region */
- pci_read_config_word(dev, msi_control_reg(pos), &control);
- nr_entries = multi_msix_capable(control);
-
- pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset);
- bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK);
- table_offset &= ~PCI_MSIX_FLAGS_BIRMASK;
- phys_addr = pci_resource_start (dev, bir) + table_offset;
- base = ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE);
- if (base == NULL)
+ base = msix_map_region(dev, msix_table_size(control));
+ if (!base)
return -ENOMEM;
- /* MSI-X Table Initialization */
- for (i = 0; i < nvec; i++) {
- entry = alloc_msi_entry();
- if (!entry)
- break;
-
- j = entries[i].entry;
- entry->msi_attrib.type = PCI_CAP_ID_MSIX;
- entry->msi_attrib.is_64 = 1;
- entry->msi_attrib.entry_nr = j;
- entry->msi_attrib.maskbit = 1;
- entry->msi_attrib.masked = 1;
- entry->msi_attrib.default_irq = dev->irq;
- entry->msi_attrib.pos = pos;
- entry->dev = dev;
- entry->mask_base = base;
-
- list_add_tail(&entry->list, &dev->msi_list);
- }
+ ret = msix_setup_entries(dev, base, entries, nvec);
+ if (ret)
+ return ret;
ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
- if (ret) {
- int avail = 0;
- list_for_each_entry(entry, &dev->msi_list, list) {
- if (entry->irq != 0) {
- avail++;
- }
- }
+ if (ret)
+ goto out_avail;
- msi_free_irqs(dev);
+ /*
+ * Some devices require MSI-X to be enabled before we can touch the
+ * MSI-X registers. We need to mask all the vectors to prevent
+ * interrupts coming in before they're fully set up.
+ */
+ control |= PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE;
+ pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control);
- /* If we had some success report the number of irqs
- * we succeeded in setting up.
- */
- if (avail == 0)
- avail = ret;
- return avail;
- }
+ msix_program_entries(dev, entries);
- i = 0;
- list_for_each_entry(entry, &dev->msi_list, list) {
- entries[i].vector = entry->irq;
- set_irq_msi(entry->irq, entry);
- i++;
- }
- /* Set MSI-X enabled bits */
+ ret = populate_msi_sysfs(dev);
+ if (ret)
+ goto out_free;
+
+ /* Set MSI-X enabled bits and unmask the function */
pci_intx_for_msi(dev, 0);
- msix_set_enable(dev, 1);
dev->msix_enabled = 1;
+ control &= ~PCI_MSIX_FLAGS_MASKALL;
+ pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control);
+
return 0;
+
+out_avail:
+ if (ret < 0) {
+ /*
+ * If we had some success, report the number of irqs
+ * we succeeded in setting up.
+ */
+ struct msi_desc *entry;
+ int avail = 0;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ if (entry->irq != 0)
+ avail++;
+ }
+ if (avail != 0)
+ ret = avail;
+ }
+
+out_free:
+ free_msi_irqs(dev);
+
+ return ret;
}
/**
@@ -496,11 +814,11 @@ static int msix_capability_init(struct pci_dev *dev,
* @nvec: how many MSIs have been requested ?
* @type: are we checking for MSI or MSI-X ?
*
- * Look at global flags, the device itself, and its parent busses
+ * Look at global flags, the device itself, and its parent buses
* to determine if MSI/-X are supported for the device. If MSI/-X is
* supported return 0, else return an error code.
**/
-static int pci_msi_check_device(struct pci_dev* dev, int nvec, int type)
+static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
{
struct pci_bus *bus;
int ret;
@@ -517,8 +835,9 @@ static int pci_msi_check_device(struct pci_dev* dev, int nvec, int type)
if (nvec < 1)
return -ERANGE;
- /* Any bridge which does NOT route MSI transactions from it's
- * secondary bus to it's primary bus must set NO_MSI flag on
+ /*
+ * Any bridge which does NOT route MSI transactions from its
+ * secondary bus to its primary bus must set NO_MSI flag on
* the secondary pci_bus.
* We expect only arch-specific PCI host bus controller driver
* or quirks for specific PCI bridges to be setting NO_MSI.
@@ -531,96 +850,89 @@ static int pci_msi_check_device(struct pci_dev* dev, int nvec, int type)
if (ret)
return ret;
- if (!pci_find_capability(dev, type))
- return -EINVAL;
-
return 0;
}
/**
- * pci_enable_msi - configure device's MSI capability structure
- * @dev: pointer to the pci_dev data structure of MSI device function
+ * pci_msi_vec_count - Return the number of MSI vectors a device can send
+ * @dev: device to report about
*
- * Setup the MSI capability structure of device function with
- * a single MSI irq upon its software driver call to request for
- * MSI mode enabled on its hardware device function. A return of zero
- * indicates the successful setup of an entry zero with the new MSI
- * irq or non-zero for otherwise.
+ * This function returns the number of MSI vectors a device requested via
+ * Multiple Message Capable register. It returns a negative errno if the
+ * device is not capable sending MSI interrupts. Otherwise, the call succeeds
+ * and returns a power of two, up to a maximum of 2^5 (32), according to the
+ * MSI specification.
**/
-int pci_enable_msi(struct pci_dev* dev)
+int pci_msi_vec_count(struct pci_dev *dev)
{
- int status;
+ int ret;
+ u16 msgctl;
- status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI);
- if (status)
- return status;
+ if (!dev->msi_cap)
+ return -EINVAL;
- WARN_ON(!!dev->msi_enabled);
+ pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl);
+ ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1);
- /* 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));
- return -EINVAL;
- }
- status = msi_capability_init(dev);
- return status;
+ return ret;
}
-EXPORT_SYMBOL(pci_enable_msi);
+EXPORT_SYMBOL(pci_msi_vec_count);
-void pci_disable_msi(struct pci_dev* dev)
+void pci_msi_shutdown(struct pci_dev *dev)
{
- struct msi_desc *entry;
- int default_irq;
+ struct msi_desc *desc;
+ u32 mask;
+ u16 ctrl;
if (!pci_msi_enable || !dev || !dev->msi_enabled)
return;
+ BUG_ON(list_empty(&dev->msi_list));
+ desc = list_first_entry(&dev->msi_list, struct msi_desc, list);
+
msi_set_enable(dev, 0);
pci_intx_for_msi(dev, 1);
dev->msi_enabled = 0;
- BUG_ON(list_empty(&dev->msi_list));
- entry = list_entry(dev->msi_list.next, struct msi_desc, list);
- if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) {
- return;
- }
-
- default_irq = entry->msi_attrib.default_irq;
- msi_free_irqs(dev);
+ /* Return the device with MSI unmasked as initial states */
+ pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &ctrl);
+ mask = msi_capable_mask(ctrl);
+ /* Keep cached state to be restored */
+ arch_msi_mask_irq(desc, mask, ~mask);
/* Restore dev->irq to its default pin-assertion irq */
- dev->irq = default_irq;
+ dev->irq = desc->msi_attrib.default_irq;
}
-EXPORT_SYMBOL(pci_disable_msi);
-static int msi_free_irqs(struct pci_dev* dev)
+void pci_disable_msi(struct pci_dev *dev)
{
- struct msi_desc *entry, *tmp;
-
- list_for_each_entry(entry, &dev->msi_list, list) {
- if (entry->irq)
- BUG_ON(irq_has_action(entry->irq));
- }
+ if (!pci_msi_enable || !dev || !dev->msi_enabled)
+ return;
- arch_teardown_msi_irqs(dev);
+ pci_msi_shutdown(dev);
+ free_msi_irqs(dev);
+}
+EXPORT_SYMBOL(pci_disable_msi);
- list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) {
- if (entry->msi_attrib.type == PCI_CAP_ID_MSIX) {
- writel(1, entry->mask_base + entry->msi_attrib.entry_nr
- * PCI_MSIX_ENTRY_SIZE
- + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
+/**
+ * pci_msix_vec_count - return the number of device's MSI-X table entries
+ * @dev: pointer to the pci_dev data structure of MSI-X device function
+ * This function returns the number of device's MSI-X table entries and
+ * therefore the number of MSI-X vectors device is capable of sending.
+ * It returns a negative errno if the device is not capable of sending MSI-X
+ * interrupts.
+ **/
+int pci_msix_vec_count(struct pci_dev *dev)
+{
+ u16 control;
- if (list_is_last(&entry->list, &dev->msi_list))
- iounmap(entry->mask_base);
- }
- list_del(&entry->list);
- kfree(entry);
- }
+ if (!dev->msix_cap)
+ return -EINVAL;
- return 0;
+ pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control);
+ return msix_table_size(control);
}
+EXPORT_SYMBOL(pci_msix_vec_count);
/**
* pci_enable_msix - configure device's MSI-X capability structure
@@ -634,27 +946,26 @@ static int msi_free_irqs(struct pci_dev* dev)
* indicates the successful configuration of MSI-X capability structure
* with new allocated MSI-X irqs. A return of < 0 indicates a failure.
* Or a return of > 0 indicates that driver request is exceeding the number
- * of irqs available. Driver should use the returned value to re-send
- * its request.
+ * of irqs or MSI-X vectors available. Driver should use the returned value to
+ * re-send its request.
**/
-int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
+int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec)
{
- int status, pos, nr_entries;
+ int status, nr_entries;
int i, j;
- u16 control;
- if (!entries)
- return -EINVAL;
+ if (!entries || !dev->msix_cap || dev->current_state != PCI_D0)
+ return -EINVAL;
status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSIX);
if (status)
return status;
- pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
- pci_read_config_word(dev, msi_control_reg(pos), &control);
- nr_entries = multi_msix_capable(control);
+ nr_entries = pci_msix_vec_count(dev);
+ if (nr_entries < 0)
+ return nr_entries;
if (nvec > nr_entries)
- return -EINVAL;
+ return nr_entries;
/* Check for any invalid entries */
for (i = 0; i < nvec; i++) {
@@ -668,10 +979,8 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
WARN_ON(!!dev->msix_enabled);
/* 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));
+ if (dev->msi_enabled) {
+ dev_info(&dev->dev, "can't enable MSI-X (MSI IRQ already assigned)\n");
return -EINVAL;
}
status = msix_capability_init(dev, entries, nvec);
@@ -679,21 +988,31 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
}
EXPORT_SYMBOL(pci_enable_msix);
-static void msix_free_all_irqs(struct pci_dev *dev)
+void pci_msix_shutdown(struct pci_dev *dev)
{
- msi_free_irqs(dev);
-}
+ struct msi_desc *entry;
-void pci_disable_msix(struct pci_dev* dev)
-{
if (!pci_msi_enable || !dev || !dev->msix_enabled)
return;
+ /* Return the device with MSI-X masked as initial states */
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ /* Keep cached states to be restored */
+ arch_msix_mask_irq(entry, 1);
+ }
+
msix_set_enable(dev, 0);
pci_intx_for_msi(dev, 1);
dev->msix_enabled = 0;
+}
- msix_free_all_irqs(dev);
+void pci_disable_msix(struct pci_dev *dev)
+{
+ if (!pci_msi_enable || !dev || !dev->msix_enabled)
+ return;
+
+ pci_msix_shutdown(dev);
+ free_msi_irqs(dev);
}
EXPORT_SYMBOL(pci_disable_msix);
@@ -706,16 +1025,13 @@ EXPORT_SYMBOL(pci_disable_msix);
* allocated for this device function, are reclaimed to unused state,
* which may be used later on.
**/
-void msi_remove_pci_irq_vectors(struct pci_dev* dev)
+void msi_remove_pci_irq_vectors(struct pci_dev *dev)
{
if (!pci_msi_enable || !dev)
- return;
-
- if (dev->msi_enabled)
- msi_free_irqs(dev);
+ return;
- if (dev->msix_enabled)
- msix_free_all_irqs(dev);
+ if (dev->msi_enabled || dev->msix_enabled)
+ free_msi_irqs(dev);
}
void pci_no_msi(void)
@@ -723,7 +1039,136 @@ void pci_no_msi(void)
pci_msi_enable = 0;
}
+/**
+ * pci_msi_enabled - is MSI enabled?
+ *
+ * Returns true if MSI has not been disabled by the command-line option
+ * pci=nomsi.
+ **/
+int pci_msi_enabled(void)
+{
+ return pci_msi_enable;
+}
+EXPORT_SYMBOL(pci_msi_enabled);
+
void pci_msi_init_pci_dev(struct pci_dev *dev)
{
INIT_LIST_HEAD(&dev->msi_list);
+
+ /* Disable the msi hardware to avoid screaming interrupts
+ * during boot. This is the power on reset default so
+ * usually this should be a noop.
+ */
+ dev->msi_cap = pci_find_capability(dev, PCI_CAP_ID_MSI);
+ if (dev->msi_cap)
+ msi_set_enable(dev, 0);
+
+ dev->msix_cap = pci_find_capability(dev, PCI_CAP_ID_MSIX);
+ if (dev->msix_cap)
+ msix_set_enable(dev, 0);
+}
+
+/**
+ * pci_enable_msi_range - configure device's MSI capability structure
+ * @dev: device to configure
+ * @minvec: minimal number of interrupts to configure
+ * @maxvec: maximum number of interrupts to configure
+ *
+ * This function tries to allocate a maximum possible number of interrupts in a
+ * range between @minvec and @maxvec. It returns a negative errno if an error
+ * occurs. If it succeeds, it returns the actual number of interrupts allocated
+ * and updates the @dev's irq member to the lowest new interrupt number;
+ * the other interrupt numbers allocated to this device are consecutive.
+ **/
+int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
+{
+ int nvec;
+ int rc;
+
+ if (dev->current_state != PCI_D0)
+ return -EINVAL;
+
+ WARN_ON(!!dev->msi_enabled);
+
+ /* Check whether driver already requested MSI-X irqs */
+ if (dev->msix_enabled) {
+ dev_info(&dev->dev,
+ "can't enable MSI (MSI-X already enabled)\n");
+ return -EINVAL;
+ }
+
+ if (maxvec < minvec)
+ return -ERANGE;
+
+ nvec = pci_msi_vec_count(dev);
+ if (nvec < 0)
+ return nvec;
+ else if (nvec < minvec)
+ return -EINVAL;
+ else if (nvec > maxvec)
+ nvec = maxvec;
+
+ do {
+ rc = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI);
+ if (rc < 0) {
+ return rc;
+ } else if (rc > 0) {
+ if (rc < minvec)
+ return -ENOSPC;
+ nvec = rc;
+ }
+ } while (rc);
+
+ do {
+ rc = msi_capability_init(dev, nvec);
+ if (rc < 0) {
+ return rc;
+ } else if (rc > 0) {
+ if (rc < minvec)
+ return -ENOSPC;
+ nvec = rc;
+ }
+ } while (rc);
+
+ return nvec;
+}
+EXPORT_SYMBOL(pci_enable_msi_range);
+
+/**
+ * pci_enable_msix_range - configure device's MSI-X capability structure
+ * @dev: pointer to the pci_dev data structure of MSI-X device function
+ * @entries: pointer to an array of MSI-X entries
+ * @minvec: minimum number of MSI-X irqs requested
+ * @maxvec: maximum number of MSI-X irqs requested
+ *
+ * Setup the MSI-X capability structure of device function with a maximum
+ * possible number of interrupts in the range between @minvec and @maxvec
+ * upon its software driver call to request for MSI-X mode enabled on its
+ * hardware device function. It returns a negative errno if an error occurs.
+ * If it succeeds, it returns the actual number of interrupts allocated and
+ * indicates the successful configuration of MSI-X capability structure
+ * with new allocated MSI-X interrupts.
+ **/
+int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
+ int minvec, int maxvec)
+{
+ int nvec = maxvec;
+ int rc;
+
+ if (maxvec < minvec)
+ return -ERANGE;
+
+ do {
+ rc = pci_enable_msix(dev, entries, nvec);
+ if (rc < 0) {
+ return rc;
+ } else if (rc > 0) {
+ if (rc < minvec)
+ return -ENOSPC;
+ nvec = rc;
+ }
+ } while (rc);
+
+ return nvec;
}
+EXPORT_SYMBOL(pci_enable_msix_range);
diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h
deleted file mode 100644
index 3898f523714..00000000000
--- a/drivers/pci/msi.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2003-2004 Intel
- * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
- */
-
-#ifndef MSI_H
-#define MSI_H
-
-#define PCI_MSIX_ENTRY_SIZE 16
-#define PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET 0
-#define PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET 4
-#define PCI_MSIX_ENTRY_DATA_OFFSET 8
-#define PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET 12
-
-#define msi_control_reg(base) (base + PCI_MSI_FLAGS)
-#define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO)
-#define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI)
-#define msi_data_reg(base, is64bit) \
- ( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 )
-#define msi_mask_bits_reg(base, is64bit) \
- ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4)
-#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE
-#define multi_msi_capable(control) \
- (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1))
-#define multi_msi_enable(control, num) \
- control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE);
-#define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT))
-#define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT))
-#define msi_enable(control, num) multi_msi_enable(control, num); \
- control |= PCI_MSI_FLAGS_ENABLE
-
-#define msix_table_offset_reg(base) (base + 0x04)
-#define msix_pba_offset_reg(base) (base + 0x08)
-#define msix_enable(control) control |= PCI_MSIX_FLAGS_ENABLE
-#define msix_disable(control) control &= ~PCI_MSIX_FLAGS_ENABLE
-#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1)
-#define multi_msix_capable msix_table_size
-#define msix_unmask(address) (address & ~PCI_MSIX_FLAGS_BITMASK)
-#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK)
-#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK)
-
-#endif /* MSI_H */
diff --git a/drivers/pci/of.c b/drivers/pci/of.c
new file mode 100644
index 00000000000..f0929934bb7
--- /dev/null
+++ b/drivers/pci/of.c
@@ -0,0 +1,61 @@
+/*
+ * PCI <-> OF mapping helpers
+ *
+ * Copyright 2011 IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/of.h>
+#include <linux/of_pci.h>
+#include "pci.h"
+
+void pci_set_of_node(struct pci_dev *dev)
+{
+ if (!dev->bus->dev.of_node)
+ return;
+ dev->dev.of_node = of_pci_find_child_device(dev->bus->dev.of_node,
+ dev->devfn);
+}
+
+void pci_release_of_node(struct pci_dev *dev)
+{
+ of_node_put(dev->dev.of_node);
+ dev->dev.of_node = NULL;
+}
+
+void pci_set_bus_of_node(struct pci_bus *bus)
+{
+ if (bus->self == NULL)
+ bus->dev.of_node = pcibios_get_phb_of_node(bus);
+ else
+ bus->dev.of_node = of_node_get(bus->self->dev.of_node);
+}
+
+void pci_release_bus_of_node(struct pci_bus *bus)
+{
+ of_node_put(bus->dev.of_node);
+ bus->dev.of_node = NULL;
+}
+
+struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus)
+{
+ /* This should only be called for PHBs */
+ if (WARN_ON(bus->self || bus->parent))
+ return NULL;
+
+ /* Look for a node pointer in either the intermediary device we
+ * create above the root bus or it's own parent. Normally only
+ * the later is populated.
+ */
+ if (bus->bridge->of_node)
+ return of_node_get(bus->bridge->of_node);
+ if (bus->bridge->parent && bus->bridge->parent->of_node)
+ return of_node_get(bus->bridge->parent->of_node);
+ return NULL;
+}
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 72f7476930c..ca4927ba843 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -11,216 +11,113 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/module.h>
-#include <acpi/acpi.h>
-#include <acpi/acnamesp.h>
-#include <acpi/acresrc.h>
-#include <acpi/acpi_bus.h>
-
+#include <linux/pci-aspm.h>
#include <linux/pci-acpi.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
#include "pci.h"
-static u32 ctrlset_buf[3] = {0, 0, 0};
-static u32 global_ctrlsets = 0;
-static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
+/**
+ * pci_acpi_wake_bus - Wake-up notification handler for root buses.
+ * @handle: ACPI handle of a device the notification is for.
+ * @event: Type of the signaled event.
+ * @context: PCI root bus to wake up devices on.
+ */
+static void pci_acpi_wake_bus(acpi_handle handle, u32 event, void *context)
+{
+ struct pci_bus *pci_bus = context;
-static acpi_status
-acpi_query_osc (
- acpi_handle handle,
- u32 level,
- void *context,
- void **retval )
+ if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_bus)
+ pci_pme_wakeup_bus(pci_bus);
+}
+
+/**
+ * pci_acpi_wake_dev - Wake-up notification handler for PCI devices.
+ * @handle: ACPI handle of a device the notification is for.
+ * @event: Type of the signaled event.
+ * @context: PCI device object to wake up.
+ */
+static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
{
- acpi_status status;
- struct acpi_object_list input;
- union acpi_object in_params[4];
- struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
- union acpi_object *out_obj;
- u32 osc_dw0;
- acpi_status *ret_status = (acpi_status *)retval;
-
-
- /* Setting up input parameters */
- input.count = 4;
- input.pointer = in_params;
- in_params[0].type = ACPI_TYPE_BUFFER;
- in_params[0].buffer.length = 16;
- in_params[0].buffer.pointer = OSC_UUID;
- in_params[1].type = ACPI_TYPE_INTEGER;
- in_params[1].integer.value = 1;
- in_params[2].type = ACPI_TYPE_INTEGER;
- in_params[2].integer.value = 3;
- in_params[3].type = ACPI_TYPE_BUFFER;
- in_params[3].buffer.length = 12;
- in_params[3].buffer.pointer = (u8 *)context;
-
- status = acpi_evaluate_object(handle, "_OSC", &input, &output);
- if (ACPI_FAILURE (status)) {
- *ret_status = status;
- return status;
- }
- out_obj = output.pointer;
+ struct pci_dev *pci_dev = context;
- if (out_obj->type != ACPI_TYPE_BUFFER) {
- printk(KERN_DEBUG
- "Evaluate _OSC returns wrong type\n");
- status = AE_TYPE;
- goto query_osc_out;
- }
- osc_dw0 = *((u32 *) out_obj->buffer.pointer);
- if (osc_dw0) {
- if (osc_dw0 & OSC_REQUEST_ERROR)
- printk(KERN_DEBUG "_OSC request fails\n");
- if (osc_dw0 & OSC_INVALID_UUID_ERROR)
- printk(KERN_DEBUG "_OSC invalid UUID\n");
- if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
- printk(KERN_DEBUG "_OSC invalid revision\n");
- if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
- /* Update Global Control Set */
- global_ctrlsets = *((u32 *)(out_obj->buffer.pointer+8));
- status = AE_OK;
- goto query_osc_out;
- }
- status = AE_ERROR;
- goto query_osc_out;
+ if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev)
+ return;
+
+ if (pci_dev->pme_poll)
+ pci_dev->pme_poll = false;
+
+ if (pci_dev->current_state == PCI_D3cold) {
+ pci_wakeup_event(pci_dev);
+ pm_runtime_resume(&pci_dev->dev);
+ return;
}
- /* Update Global Control Set */
- global_ctrlsets = *((u32 *)(out_obj->buffer.pointer + 8));
- status = AE_OK;
+ /* Clear PME Status if set. */
+ if (pci_dev->pme_support)
+ pci_check_pme_status(pci_dev);
-query_osc_out:
- kfree(output.pointer);
- *ret_status = status;
- return status;
-}
+ pci_wakeup_event(pci_dev);
+ pm_runtime_resume(&pci_dev->dev);
+ if (pci_dev->subordinate)
+ pci_pme_wakeup_bus(pci_dev->subordinate);
+}
-static acpi_status
-acpi_run_osc (
- acpi_handle handle,
- void *context)
+/**
+ * pci_acpi_add_bus_pm_notifier - Register PM notifier for given PCI bus.
+ * @dev: ACPI device to add the notifier for.
+ * @pci_bus: PCI bus to walk checking for PME status if an event is signaled.
+ */
+acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev,
+ struct pci_bus *pci_bus)
{
- acpi_status status;
- struct acpi_object_list input;
- union acpi_object in_params[4];
- struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
- union acpi_object *out_obj;
- u32 osc_dw0;
-
- /* Setting up input parameters */
- input.count = 4;
- input.pointer = in_params;
- in_params[0].type = ACPI_TYPE_BUFFER;
- in_params[0].buffer.length = 16;
- in_params[0].buffer.pointer = OSC_UUID;
- in_params[1].type = ACPI_TYPE_INTEGER;
- in_params[1].integer.value = 1;
- in_params[2].type = ACPI_TYPE_INTEGER;
- in_params[2].integer.value = 3;
- in_params[3].type = ACPI_TYPE_BUFFER;
- in_params[3].buffer.length = 12;
- in_params[3].buffer.pointer = (u8 *)context;
-
- status = acpi_evaluate_object(handle, "_OSC", &input, &output);
- if (ACPI_FAILURE (status))
- return status;
-
- out_obj = output.pointer;
- if (out_obj->type != ACPI_TYPE_BUFFER) {
- printk(KERN_DEBUG
- "Evaluate _OSC returns wrong type\n");
- status = AE_TYPE;
- goto run_osc_out;
- }
- osc_dw0 = *((u32 *) out_obj->buffer.pointer);
- if (osc_dw0) {
- if (osc_dw0 & OSC_REQUEST_ERROR)
- printk(KERN_DEBUG "_OSC request fails\n");
- if (osc_dw0 & OSC_INVALID_UUID_ERROR)
- printk(KERN_DEBUG "_OSC invalid UUID\n");
- if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
- printk(KERN_DEBUG "_OSC invalid revision\n");
- if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
- printk(KERN_DEBUG "_OSC FW not grant req. control\n");
- status = AE_SUPPORT;
- goto run_osc_out;
- }
- status = AE_ERROR;
- goto run_osc_out;
- }
- status = AE_OK;
-
-run_osc_out:
- kfree(output.pointer);
- return status;
+ return acpi_add_pm_notifier(dev, pci_acpi_wake_bus, pci_bus);
}
/**
- * __pci_osc_support_set - register OS support to Firmware
- * @flags: OS support bits
- * @hid: hardware ID
- *
- * Update OS support fields and doing a _OSC Query to obtain an update
- * from Firmware on supported control bits.
- **/
-acpi_status __pci_osc_support_set(u32 flags, const char *hid)
+ * pci_acpi_remove_bus_pm_notifier - Unregister PCI bus PM notifier.
+ * @dev: ACPI device to remove the notifier from.
+ */
+acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev)
{
- u32 temp;
- acpi_status retval;
+ return acpi_remove_pm_notifier(dev, pci_acpi_wake_bus);
+}
- if (!(flags & OSC_SUPPORT_MASKS)) {
- return AE_TYPE;
- }
- ctrlset_buf[OSC_SUPPORT_TYPE] |= (flags & OSC_SUPPORT_MASKS);
-
- /* do _OSC query for all possible controls */
- temp = ctrlset_buf[OSC_CONTROL_TYPE];
- ctrlset_buf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
- ctrlset_buf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
- acpi_get_devices(hid,
- acpi_query_osc,
- ctrlset_buf,
- (void **) &retval );
- ctrlset_buf[OSC_QUERY_TYPE] = !OSC_QUERY_ENABLE;
- ctrlset_buf[OSC_CONTROL_TYPE] = temp;
- if (ACPI_FAILURE(retval)) {
- /* no osc support at all */
- ctrlset_buf[OSC_SUPPORT_TYPE] = 0;
- }
- return AE_OK;
+/**
+ * pci_acpi_add_pm_notifier - Register PM notifier for given PCI device.
+ * @dev: ACPI device to add the notifier for.
+ * @pci_dev: PCI device to check for the PME status if an event is signaled.
+ */
+acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev,
+ struct pci_dev *pci_dev)
+{
+ return acpi_add_pm_notifier(dev, pci_acpi_wake_dev, pci_dev);
}
/**
- * pci_osc_control_set - commit requested control to Firmware
- * @handle: acpi_handle for the target ACPI object
- * @flags: driver's requested control bits
- *
- * Attempt to take control from Firmware on requested control bits.
- **/
-acpi_status pci_osc_control_set(acpi_handle handle, u32 flags)
+ * pci_acpi_remove_pm_notifier - Unregister PCI device PM notifier.
+ * @dev: ACPI device to remove the notifier from.
+ */
+acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev)
{
- acpi_status status;
- u32 ctrlset;
+ return acpi_remove_pm_notifier(dev, pci_acpi_wake_dev);
+}
- ctrlset = (flags & OSC_CONTROL_MASKS);
- if (!ctrlset) {
- return AE_TYPE;
- }
- if (ctrlset_buf[OSC_SUPPORT_TYPE] &&
- ((global_ctrlsets & ctrlset) != ctrlset)) {
- return AE_SUPPORT;
- }
- ctrlset_buf[OSC_CONTROL_TYPE] |= ctrlset;
- status = acpi_run_osc(handle, ctrlset_buf);
- if (ACPI_FAILURE (status)) {
- ctrlset_buf[OSC_CONTROL_TYPE] &= ~ctrlset;
- }
-
- return status;
+phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
+{
+ acpi_status status = AE_NOT_EXIST;
+ unsigned long long mcfg_addr;
+
+ if (handle)
+ status = acpi_evaluate_integer(handle, METHOD_NAME__CBA,
+ NULL, &mcfg_addr);
+ if (ACPI_FAILURE(status))
+ return 0;
+
+ return (phys_addr_t)mcfg_addr;
}
-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".
@@ -241,16 +138,18 @@ EXPORT_SYMBOL(pci_osc_control_set);
* if (_PRW at S-state x)
* choose from highest power _SxD to lowest power _SxW
* else // no _PRW at S-state x
- * choose highest power _SxD or any lower power
+ * 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;
+ int acpi_state, d_max;
- acpi_state = acpi_pm_device_sleep_state(&pdev->dev,
- device_may_wakeup(&pdev->dev), NULL);
+ if (pdev->no_d3cold)
+ d_max = ACPI_STATE_D3_HOT;
+ else
+ d_max = ACPI_STATE_D3_COLD;
+ acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, d_max);
if (acpi_state < 0)
return PCI_POWER_ERROR;
@@ -261,97 +160,229 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev,
return PCI_D1;
case ACPI_STATE_D2:
return PCI_D2;
- case ACPI_STATE_D3:
+ case ACPI_STATE_D3_HOT:
return PCI_D3hot;
+ case ACPI_STATE_D3_COLD:
+ return PCI_D3cold;
}
return PCI_POWER_ERROR;
}
-#endif
+
+static bool acpi_pci_power_manageable(struct pci_dev *dev)
+{
+ acpi_handle handle = 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)
{
- acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
- acpi_handle tmp;
+ acpi_handle handle = ACPI_HANDLE(&dev->dev);
static const u8 state_conv[] = {
[PCI_D0] = ACPI_STATE_D0,
[PCI_D1] = ACPI_STATE_D1,
[PCI_D2] = ACPI_STATE_D2,
- [PCI_D3hot] = ACPI_STATE_D3,
- [PCI_D3cold] = ACPI_STATE_D3
+ [PCI_D3hot] = ACPI_STATE_D3_COLD,
+ [PCI_D3cold] = ACPI_STATE_D3_COLD,
};
+ 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_has_method(handle, "_EJ0"))
+ return -ENODEV;
switch (state) {
+ case PCI_D3cold:
+ if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) ==
+ PM_QOS_FLAGS_ALL) {
+ error = -EBUSY;
+ break;
+ }
case PCI_D0:
case PCI_D1:
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_dbg(&dev->dev, "power state changed by ACPI to %s\n",
+ acpi_power_state_string(state_conv[state]));
+
+ return error;
}
+static bool acpi_pci_can_wakeup(struct pci_dev *dev)
+{
+ acpi_handle handle = ACPI_HANDLE(&dev->dev);
+
+ return handle ? acpi_bus_can_wakeup(handle) : false;
+}
-/* ACPI bus type */
-static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
+static void acpi_pci_propagate_wakeup_enable(struct pci_bus *bus, bool enable)
{
- struct pci_dev * pci_dev;
- acpi_integer addr;
+ while (bus->parent) {
+ if (!acpi_pm_device_sleep_wake(&bus->self->dev, enable))
+ return;
+ bus = bus->parent;
+ }
- pci_dev = to_pci_dev(dev);
- /* Please ref to ACPI spec for the syntax of _ADR */
- addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
- *handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
- if (!*handle)
- return -ENODEV;
+ /* We have reached the root bus. */
+ if (bus->bridge)
+ acpi_pm_device_sleep_wake(bus->bridge, enable);
+}
+
+static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
+{
+ if (acpi_pci_can_wakeup(dev))
+ return acpi_pm_device_sleep_wake(&dev->dev, enable);
+
+ acpi_pci_propagate_wakeup_enable(dev->bus, enable);
return 0;
}
-static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle)
+static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable)
{
- int num;
- unsigned int seg, bus;
+ while (bus->parent) {
+ struct pci_dev *bridge = bus->self;
+
+ if (bridge->pme_interrupt)
+ return;
+ if (!acpi_pm_device_run_wake(&bridge->dev, enable))
+ return;
+ bus = bus->parent;
+ }
+
+ /* We have reached the root bus. */
+ if (bus->bridge)
+ acpi_pm_device_run_wake(bus->bridge, enable);
+}
+static int acpi_pci_run_wake(struct pci_dev *dev, bool enable)
+{
/*
- * The string should be the same as root bridge's name
- * Please look at 'pci_scan_bus_parented'
+ * Per PCI Express Base Specification Revision 2.0 section
+ * 5.3.3.2 Link Wakeup, platform support is needed for D3cold
+ * waking up to power on the main link even if there is PME
+ * support for D3cold
*/
- num = sscanf(dev->bus_id, "pci%04x:%02x", &seg, &bus);
- if (num != 2)
- return -ENODEV;
- *handle = acpi_get_pci_rootbridge_handle(seg, bus);
- if (!*handle)
- return -ENODEV;
+ if (dev->pme_interrupt && !dev->runtime_d3cold)
+ return 0;
+
+ if (!acpi_pm_device_run_wake(&dev->dev, enable))
+ return 0;
+
+ acpi_pci_propagate_run_wake(dev->bus, enable);
return 0;
}
+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,
+ .sleep_wake = acpi_pci_sleep_wake,
+ .run_wake = acpi_pci_run_wake,
+};
+
+void acpi_pci_add_bus(struct pci_bus *bus)
+{
+ if (acpi_pci_disabled || !bus->bridge)
+ return;
+
+ acpi_pci_slot_enumerate(bus);
+ acpiphp_enumerate_slots(bus);
+}
+
+void acpi_pci_remove_bus(struct pci_bus *bus)
+{
+ if (acpi_pci_disabled || !bus->bridge)
+ return;
+
+ acpiphp_remove_slots(bus);
+ acpi_pci_slot_remove(bus);
+}
+
+/* ACPI bus type */
+static struct acpi_device *acpi_pci_find_companion(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ bool check_children;
+ u64 addr;
+
+ check_children = pci_is_bridge(pci_dev);
+ /* Please ref to ACPI spec for the syntax of _ADR */
+ addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
+ return acpi_find_child_device(ACPI_COMPANION(dev->parent), addr,
+ check_children);
+}
+
+static void pci_acpi_setup(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+
+ if (!adev)
+ return;
+
+ pci_acpi_add_pm_notifier(adev, pci_dev);
+ if (!adev->wakeup.flags.valid)
+ return;
+
+ device_set_wakeup_capable(dev, true);
+ acpi_pci_sleep_wake(pci_dev, false);
+ if (adev->wakeup.flags.run_wake)
+ device_set_run_wake(dev, true);
+}
+
+static void pci_acpi_cleanup(struct device *dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+
+ if (!adev)
+ return;
+
+ pci_acpi_remove_pm_notifier(adev);
+ if (adev->wakeup.flags.valid) {
+ device_set_wakeup_capable(dev, false);
+ device_set_run_wake(dev, false);
+ }
+}
+
+static bool pci_acpi_bus_match(struct device *dev)
+{
+ return dev_is_pci(dev);
+}
+
static struct acpi_bus_type acpi_pci_bus = {
- .bus = &pci_bus_type,
- .find_device = acpi_pci_find_device,
- .find_bridge = acpi_pci_find_root_bridge,
+ .name = "PCI",
+ .match = pci_acpi_bus_match,
+ .find_companion = acpi_pci_find_companion,
+ .setup = pci_acpi_setup,
+ .cleanup = pci_acpi_cleanup,
};
static int __init acpi_pci_init(void)
{
int ret;
- if (acpi_gbl_FADT.boot_flags & BAF_MSI_NOT_SUPPORTED) {
- printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n");
+ if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_MSI) {
+ pr_info("ACPI FADT declares the system doesn't support MSI, so disable it\n");
pci_no_msi();
}
+
+ if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) {
+ pr_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);
+ acpi_pci_slot_init();
+ acpiphp_init();
+
return 0;
}
arch_initcall(acpi_pci_init);
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index e571c72e675..3f8e3dbcaa7 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -16,44 +16,46 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+#include <linux/kexec.h>
#include "pci.h"
-/*
- * Dynamic device IDs are disabled for !CONFIG_HOTPLUG
- */
-
struct pci_dynid {
struct list_head node;
struct pci_device_id id;
};
-#ifdef CONFIG_HOTPLUG
-
/**
- * store_new_id - add a new PCI device ID to this driver and re-probe devices
- * @driver: target device driver
- * @buf: buffer for scanning device ID data
- * @count: input size
+ * pci_add_dynid - add a new PCI device ID to this driver and re-probe devices
+ * @drv: target pci driver
+ * @vendor: PCI vendor ID
+ * @device: PCI device ID
+ * @subvendor: PCI subvendor ID
+ * @subdevice: PCI subdevice ID
+ * @class: PCI class
+ * @class_mask: PCI class mask
+ * @driver_data: private driver data
+ *
+ * Adds a new dynamic pci device ID to this driver and causes the
+ * driver to probe for all devices again. @drv must have been
+ * registered prior to calling this function.
*
- * Adds a new dynamic pci device ID to this driver,
- * and causes the driver to probe for all devices again.
+ * CONTEXT:
+ * Does GFP_KERNEL allocation.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
*/
-static ssize_t
-store_new_id(struct device_driver *driver, const char *buf, size_t count)
+int pci_add_dynid(struct pci_driver *drv,
+ unsigned int vendor, unsigned int device,
+ unsigned int subvendor, unsigned int subdevice,
+ unsigned int class, unsigned int class_mask,
+ unsigned long driver_data)
{
struct pci_dynid *dynid;
- struct pci_driver *pdrv = to_pci_driver(driver);
- __u32 vendor, device, subvendor=PCI_ANY_ID,
- subdevice=PCI_ANY_ID, class=0, class_mask=0;
- unsigned long driver_data=0;
- int fields=0;
- int retval = 0;
-
- fields = sscanf(buf, "%x %x %x %x %x %x %lux",
- &vendor, &device, &subvendor, &subdevice,
- &class, &class_mask, &driver_data);
- if (fields < 2)
- return -EINVAL;
+ int retval;
dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
if (!dynid)
@@ -65,26 +67,19 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
dynid->id.subdevice = subdevice;
dynid->id.class = class;
dynid->id.class_mask = class_mask;
- dynid->id.driver_data = pdrv->dynids.use_driver_data ?
- driver_data : 0UL;
+ dynid->id.driver_data = driver_data;
- spin_lock(&pdrv->dynids.lock);
- list_add_tail(&dynid->node, &pdrv->dynids.list);
- spin_unlock(&pdrv->dynids.lock);
+ spin_lock(&drv->dynids.lock);
+ list_add_tail(&dynid->node, &drv->dynids.list);
+ spin_unlock(&drv->dynids.lock);
- if (get_driver(&pdrv->driver)) {
- retval = driver_attach(&pdrv->driver);
- put_driver(&pdrv->driver);
- }
+ retval = driver_attach(&drv->driver);
- if (retval)
- return retval;
- return count;
+ return retval;
}
-static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
+EXPORT_SYMBOL_GPL(pci_add_dynid);
-static void
-pci_free_dynids(struct pci_driver *drv)
+static void pci_free_dynids(struct pci_driver *drv)
{
struct pci_dynid *dynid, *n;
@@ -96,27 +91,126 @@ pci_free_dynids(struct pci_driver *drv)
spin_unlock(&drv->dynids.lock);
}
-static int
-pci_create_newid_file(struct pci_driver *drv)
+/**
+ * store_new_id - sysfs frontend to pci_add_dynid()
+ * @driver: target device driver
+ * @buf: buffer for scanning device ID data
+ * @count: input size
+ *
+ * Allow PCI IDs to be added to an existing driver via sysfs.
+ */
+static ssize_t store_new_id(struct device_driver *driver, const char *buf,
+ size_t count)
{
- int error = 0;
- if (drv->probe != NULL)
- error = driver_create_file(&drv->driver, &driver_attr_new_id);
- return error;
-}
+ struct pci_driver *pdrv = to_pci_driver(driver);
+ const struct pci_device_id *ids = pdrv->id_table;
+ __u32 vendor, device, subvendor = PCI_ANY_ID,
+ subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
+ unsigned long driver_data = 0;
+ int fields = 0;
+ int retval = 0;
-static void pci_remove_newid_file(struct pci_driver *drv)
-{
- driver_remove_file(&drv->driver, &driver_attr_new_id);
+ fields = sscanf(buf, "%x %x %x %x %x %x %lx",
+ &vendor, &device, &subvendor, &subdevice,
+ &class, &class_mask, &driver_data);
+ if (fields < 2)
+ return -EINVAL;
+
+ if (fields != 7) {
+ struct pci_dev *pdev = kzalloc(sizeof(*pdev), GFP_KERNEL);
+ if (!pdev)
+ return -ENOMEM;
+
+ pdev->vendor = vendor;
+ pdev->device = device;
+ pdev->subsystem_vendor = subvendor;
+ pdev->subsystem_device = subdevice;
+ pdev->class = class;
+
+ if (pci_match_id(pdrv->id_table, pdev))
+ retval = -EEXIST;
+
+ kfree(pdev);
+
+ if (retval)
+ return retval;
+ }
+
+ /* Only accept driver_data values that match an existing id_table
+ entry */
+ if (ids) {
+ retval = -EINVAL;
+ while (ids->vendor || ids->subvendor || ids->class_mask) {
+ if (driver_data == ids->driver_data) {
+ retval = 0;
+ break;
+ }
+ ids++;
+ }
+ if (retval) /* No match */
+ return retval;
+ }
+
+ retval = pci_add_dynid(pdrv, vendor, device, subvendor, subdevice,
+ class, class_mask, driver_data);
+ if (retval)
+ return retval;
+ return count;
}
-#else /* !CONFIG_HOTPLUG */
-static inline void pci_free_dynids(struct pci_driver *drv) {}
-static inline int pci_create_newid_file(struct pci_driver *drv)
+static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
+
+/**
+ * store_remove_id - remove a PCI device ID from this driver
+ * @driver: target device driver
+ * @buf: buffer for scanning device ID data
+ * @count: input size
+ *
+ * Removes a dynamic pci device ID to this driver.
+ */
+static ssize_t store_remove_id(struct device_driver *driver, const char *buf,
+ size_t count)
{
- return 0;
+ struct pci_dynid *dynid, *n;
+ struct pci_driver *pdrv = to_pci_driver(driver);
+ __u32 vendor, device, subvendor = PCI_ANY_ID,
+ subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
+ int fields = 0;
+ int retval = -ENODEV;
+
+ fields = sscanf(buf, "%x %x %x %x %x %x",
+ &vendor, &device, &subvendor, &subdevice,
+ &class, &class_mask);
+ if (fields < 2)
+ return -EINVAL;
+
+ spin_lock(&pdrv->dynids.lock);
+ list_for_each_entry_safe(dynid, n, &pdrv->dynids.list, node) {
+ struct pci_device_id *id = &dynid->id;
+ if ((id->vendor == vendor) &&
+ (id->device == device) &&
+ (subvendor == PCI_ANY_ID || id->subvendor == subvendor) &&
+ (subdevice == PCI_ANY_ID || id->subdevice == subdevice) &&
+ !((id->class ^ class) & class_mask)) {
+ list_del(&dynid->node);
+ kfree(dynid);
+ retval = 0;
+ break;
+ }
+ }
+ spin_unlock(&pdrv->dynids.lock);
+
+ if (retval)
+ return retval;
+ return count;
}
-static inline void pci_remove_newid_file(struct pci_driver *drv) {}
-#endif
+static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
+
+static struct attribute *pci_drv_attrs[] = {
+ &driver_attr_new_id.attr,
+ &driver_attr_remove_id.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(pci_drv);
/**
* pci_match_id - See if a pci device matches a given pci_id table
@@ -142,6 +236,14 @@ const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
}
return NULL;
}
+EXPORT_SYMBOL(pci_match_id);
+
+static const struct pci_device_id pci_device_id_any = {
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+};
/**
* pci_match_device - Tell if a PCI device structure has a matching PCI device id structure
@@ -156,56 +258,123 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
struct pci_dev *dev)
{
struct pci_dynid *dynid;
+ const struct pci_device_id *found_id = NULL;
+
+ /* When driver_override is set, only bind to the matching driver */
+ if (dev->driver_override && strcmp(dev->driver_override, drv->name))
+ return NULL;
/* Look at the dynamic ids first, before the static ones */
spin_lock(&drv->dynids.lock);
list_for_each_entry(dynid, &drv->dynids.list, node) {
if (pci_match_one_device(&dynid->id, dev)) {
- spin_unlock(&drv->dynids.lock);
- return &dynid->id;
+ found_id = &dynid->id;
+ break;
}
}
spin_unlock(&drv->dynids.lock);
- return pci_match_id(drv->id_table, dev);
+ if (!found_id)
+ found_id = pci_match_id(drv->id_table, dev);
+
+ /* driver_override will always match, send a dummy id */
+ if (!found_id && dev->driver_override)
+ found_id = &pci_device_id_any;
+
+ return found_id;
+}
+
+struct drv_dev_and_id {
+ struct pci_driver *drv;
+ struct pci_dev *dev;
+ const struct pci_device_id *id;
+};
+
+static long local_pci_probe(void *_ddi)
+{
+ struct drv_dev_and_id *ddi = _ddi;
+ struct pci_dev *pci_dev = ddi->dev;
+ struct pci_driver *pci_drv = ddi->drv;
+ struct device *dev = &pci_dev->dev;
+ int rc;
+
+ /*
+ * Unbound PCI devices are always put in D0, regardless of
+ * runtime PM status. During probe, the device is set to
+ * active and the usage count is incremented. If the driver
+ * supports runtime PM, it should call pm_runtime_put_noidle()
+ * in its probe routine and pm_runtime_get_noresume() in its
+ * remove routine.
+ */
+ pm_runtime_get_sync(dev);
+ pci_dev->driver = pci_drv;
+ rc = pci_drv->probe(pci_dev, ddi->id);
+ if (!rc)
+ return rc;
+ if (rc < 0) {
+ pci_dev->driver = NULL;
+ pm_runtime_put_sync(dev);
+ return rc;
+ }
+ /*
+ * Probe function should return < 0 for failure, 0 for success
+ * Treat values > 0 as success, but warn.
+ */
+ dev_warn(dev, "Driver probe function unexpectedly returned %d\n", rc);
+ return 0;
}
static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
const struct pci_device_id *id)
{
- int error;
-#ifdef CONFIG_NUMA
- /* Execute driver initialization on node where the
- device's bus is attached to. This way the driver likely
- allocates its local memory on the right node without
- any need to change it. */
- struct mempolicy *oldpol;
- cpumask_t oldmask = current->cpus_allowed;
- int node = pcibus_to_node(dev->bus);
- if (node >= 0 && node_online(node))
- set_cpus_allowed(current, node_to_cpumask(node));
- /* And set default memory allocation policy */
- oldpol = current->mempolicy;
- current->mempolicy = NULL; /* fall back to system default policy */
-#endif
- error = drv->probe(dev, id);
-#ifdef CONFIG_NUMA
- set_cpus_allowed(current, oldmask);
- current->mempolicy = oldpol;
-#endif
+ int error, node;
+ struct drv_dev_and_id ddi = { drv, dev, id };
+
+ /*
+ * Execute driver initialization on node where the device is
+ * attached. This way the driver likely allocates its local memory
+ * on the right node.
+ */
+ node = dev_to_node(&dev->dev);
+
+ /*
+ * On NUMA systems, we are likely to call a PF probe function using
+ * work_on_cpu(). If that probe calls pci_enable_sriov() (which
+ * adds the VF devices via pci_bus_add_device()), we may re-enter
+ * this function to call the VF probe function. Calling
+ * work_on_cpu() again will cause a lockdep warning. Since VFs are
+ * always on the same node as the PF, we can work around this by
+ * avoiding work_on_cpu() when we're already on the correct node.
+ *
+ * Preemption is enabled, so it's theoretically unsafe to use
+ * numa_node_id(), but even if we run the probe function on the
+ * wrong node, it should be functionally correct.
+ */
+ if (node >= 0 && node != numa_node_id()) {
+ int cpu;
+
+ get_online_cpus();
+ cpu = cpumask_any_and(cpumask_of_node(node), cpu_online_mask);
+ if (cpu < nr_cpu_ids)
+ error = work_on_cpu(cpu, local_pci_probe, &ddi);
+ else
+ error = local_pci_probe(&ddi);
+ put_online_cpus();
+ } else
+ error = local_pci_probe(&ddi);
+
return error;
}
/**
- * __pci_device_probe()
+ * __pci_device_probe - check if a driver wants to claim a specific PCI device
* @drv: driver to call to check if it wants the PCI device
* @pci_dev: PCI device being probed
- *
+ *
* returns 0 on success, else error.
* side-effect: pci_dev->driver is set to drv when drv claims pci_dev.
*/
-static int
-__pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
+static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
{
const struct pci_device_id *id;
int error = 0;
@@ -216,15 +385,13 @@ __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
id = pci_match_device(drv, pci_dev);
if (id)
error = pci_call_probe(drv, pci_dev, id);
- if (error >= 0) {
- pci_dev->driver = drv;
+ if (error >= 0)
error = 0;
- }
}
return error;
}
-static int pci_device_probe(struct device * dev)
+static int pci_device_probe(struct device *dev)
{
int error = 0;
struct pci_driver *drv;
@@ -240,17 +407,23 @@ static int pci_device_probe(struct device * dev)
return error;
}
-static int pci_device_remove(struct device * dev)
+static int pci_device_remove(struct device *dev)
{
- struct pci_dev * pci_dev = to_pci_dev(dev);
- struct pci_driver * drv = pci_dev->driver;
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
if (drv) {
- if (drv->remove)
+ if (drv->remove) {
+ pm_runtime_get_sync(dev);
drv->remove(pci_dev);
+ pm_runtime_put_noidle(dev);
+ }
pci_dev->driver = NULL;
}
+ /* Undo the runtime PM settings in local_pci_probe() */
+ pm_runtime_put_sync(dev);
+
/*
* If the device is still on, set the power state as "unknown",
* since it might change by the next time we load the driver.
@@ -262,7 +435,7 @@ static int pci_device_remove(struct device * dev)
* We would love to complain here if pci_dev->is_enabled is set, that
* the driver should have called pci_disable_device(), but the
* unfortunate fact is there are too many odd BIOS and bridge setups
- * that don't like drivers doing that all of the time.
+ * that don't like drivers doing that all of the time.
* Oh well, we can dream of sane hardware when we sleep, no matter how
* horrible the crap we have to deal with is when we are awake...
*/
@@ -271,110 +444,806 @@ 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;
- int i = 0;
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
- if (drv && drv->suspend) {
- 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;
- }
- return i;
+ pm_runtime_resume(dev);
+
+ if (drv && drv->shutdown)
+ drv->shutdown(pci_dev);
+ pci_msi_shutdown(pci_dev);
+ pci_msix_shutdown(pci_dev);
+
+#ifdef CONFIG_KEXEC
+ /*
+ * If this is a kexec reboot, turn off Bus Master bit on the
+ * device to tell it to not continue to do DMA. Don't touch
+ * devices in D3cold or unknown states.
+ * If it is not a kexec reboot, firmware will hit the PCI
+ * devices with big hammer and stop their DMA any way.
+ */
+ if (kexec_in_progress && (pci_dev->current_state <= PCI_D3hot))
+ pci_clear_master(pci_dev);
+#endif
}
-static int pci_device_suspend_late(struct device * dev, pm_message_t state)
+#ifdef CONFIG_PM
+
+/* Auxiliary functions used for system resume and run-time resume. */
+
+/**
+ * pci_restore_standard_config - restore standard config registers of PCI device
+ * @pci_dev: PCI device to handle
+ */
+static int pci_restore_standard_config(struct pci_dev *pci_dev)
{
- struct pci_dev * pci_dev = to_pci_dev(dev);
- struct pci_driver * drv = pci_dev->driver;
- int i = 0;
+ pci_update_current_state(pci_dev, PCI_UNKNOWN);
- if (drv && drv->suspend_late) {
- i = drv->suspend_late(pci_dev, state);
- suspend_report_result(drv->suspend_late, i);
+ if (pci_dev->current_state != PCI_D0) {
+ int error = pci_set_power_state(pci_dev, PCI_D0);
+ if (error)
+ return error;
}
- return i;
+
+ pci_restore_state(pci_dev);
+ return 0;
+}
+
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+
+static void pci_pm_default_resume_early(struct pci_dev *pci_dev)
+{
+ pci_power_up(pci_dev);
+ pci_restore_state(pci_dev);
+ pci_fixup_device(pci_fixup_resume_early, pci_dev);
}
/*
- * Default resume method for devices that have no driver provided resume,
- * or not even a driver at all.
+ * Default "suspend" method for devices that have no driver provided suspend,
+ * or not even a driver at all (second part).
*/
-static int pci_default_resume(struct pci_dev *pci_dev)
+static void pci_pm_set_unknown_state(struct pci_dev *pci_dev)
{
- int retval = 0;
+ /*
+ * 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 (second part).
+ */
+static int pci_pm_reenable_device(struct pci_dev *pci_dev)
+{
+ int retval;
- /* 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 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_suspend(struct device *dev, pm_message_t state)
{
- int error;
- struct pci_dev * pci_dev = to_pci_dev(dev);
- struct pci_driver * drv = pci_dev->driver;
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+
+ if (drv && drv->suspend) {
+ pci_power_t prev = pci_dev->current_state;
+ int error;
+
+ error = drv->suspend(pci_dev, state);
+ suspend_report_result(drv->suspend, error);
+ if (error)
+ return error;
+
+ if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
+ && pci_dev->current_state != PCI_UNKNOWN) {
+ WARN_ONCE(pci_dev->current_state != prev,
+ "PCI PM: Device state not saved by %pF\n",
+ drv->suspend);
+ }
+ }
+
+ pci_fixup_device(pci_fixup_suspend, pci_dev);
+
+ return 0;
+}
+
+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;
+
+ if (drv && drv->suspend_late) {
+ pci_power_t prev = pci_dev->current_state;
+ int error;
+
+ error = drv->suspend_late(pci_dev, state);
+ suspend_report_result(drv->suspend_late, error);
+ if (error)
+ return error;
+
+ if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
+ && pci_dev->current_state != PCI_UNKNOWN) {
+ WARN_ONCE(pci_dev->current_state != prev,
+ "PCI PM: Device state not saved by %pF\n",
+ drv->suspend_late);
+ return 0;
+ }
+ }
+
+ if (!pci_dev->state_saved)
+ pci_save_state(pci_dev);
+
+ pci_pm_set_unknown_state(pci_dev);
+
+ return 0;
+}
+
+static int pci_legacy_resume_early(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+
+ return drv && drv->resume_early ?
+ drv->resume_early(pci_dev) : 0;
+}
+
+static int pci_legacy_resume(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+
+ pci_fixup_device(pci_fixup_resume, pci_dev);
+
+ return drv && drv->resume ?
+ drv->resume(pci_dev) : pci_pm_reenable_device(pci_dev);
+}
+
+/* Auxiliary functions used by the new power management framework */
+
+static void pci_pm_default_resume(struct pci_dev *pci_dev)
+{
+ pci_fixup_device(pci_fixup_resume, pci_dev);
+
+ if (!pci_has_subordinate(pci_dev))
+ pci_enable_wake(pci_dev, PCI_D0, false);
+}
+
+static void pci_pm_default_suspend(struct pci_dev *pci_dev)
+{
+ /* Disable non-bridge devices without PM support */
+ if (!pci_has_subordinate(pci_dev))
+ pci_disable_enabled_device(pci_dev);
+}
+
+static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev)
+{
+ struct pci_driver *drv = pci_dev->driver;
+ bool ret = drv && (drv->suspend || drv->suspend_late || drv->resume
+ || drv->resume_early);
+
+ /*
+ * Legacy PM support is used by default, so warn if the new framework is
+ * supported as well. Drivers are supposed to support either the
+ * former, or the latter, but not both at the same time.
+ */
+ WARN(ret && drv->driver.pm, "driver %s device %04x:%04x\n",
+ drv->name, pci_dev->vendor, pci_dev->device);
+
+ return ret;
+}
+
+/* New power management framework */
+
+static int pci_pm_prepare(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ /*
+ * Devices having power.ignore_children set may still be necessary for
+ * suspending their children in the next phase of device suspend.
+ */
+ if (dev->power.ignore_children)
+ pm_runtime_resume(dev);
+
+ if (drv && drv->pm && drv->pm->prepare)
+ error = drv->pm->prepare(dev);
- if (drv && drv->resume)
- error = drv->resume(pci_dev);
- else
- error = pci_default_resume(pci_dev);
return error;
}
-static int pci_device_resume_early(struct device * dev)
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define pci_pm_prepare NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_SUSPEND
+
+static int pci_pm_suspend(struct device *dev)
{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_suspend(dev, PMSG_SUSPEND);
+
+ if (!pm) {
+ pci_pm_default_suspend(pci_dev);
+ goto Fixup;
+ }
+
+ /*
+ * PCI devices suspended at run time need to be resumed at this point,
+ * because in general it is necessary to reconfigure them for system
+ * suspend. Namely, if the device is supposed to wake up the system
+ * from the sleep state, we may need to reconfigure it for this purpose.
+ * In turn, if the device is not supposed to wake up the system from the
+ * sleep state, we'll have to prevent it from signaling wake-up.
+ */
+ pm_runtime_resume(dev);
+
+ pci_dev->state_saved = false;
+ if (pm->suspend) {
+ pci_power_t prev = pci_dev->current_state;
+ int error;
+
+ error = pm->suspend(dev);
+ suspend_report_result(pm->suspend, error);
+ if (error)
+ return error;
+
+ if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
+ && pci_dev->current_state != PCI_UNKNOWN) {
+ WARN_ONCE(pci_dev->current_state != prev,
+ "PCI PM: State of device not saved by %pF\n",
+ pm->suspend);
+ }
+ }
+
+ Fixup:
+ pci_fixup_device(pci_fixup_suspend, pci_dev);
+
+ return 0;
+}
+
+static int pci_pm_suspend_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_suspend_late(dev, PMSG_SUSPEND);
+
+ if (!pm) {
+ pci_save_state(pci_dev);
+ return 0;
+ }
+
+ if (pm->suspend_noirq) {
+ pci_power_t prev = pci_dev->current_state;
+ int error;
+
+ error = pm->suspend_noirq(dev);
+ suspend_report_result(pm->suspend_noirq, error);
+ if (error)
+ return error;
+
+ if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
+ && pci_dev->current_state != PCI_UNKNOWN) {
+ WARN_ONCE(pci_dev->current_state != prev,
+ "PCI PM: State of device not saved by %pF\n",
+ pm->suspend_noirq);
+ return 0;
+ }
+ }
+
+ if (!pci_dev->state_saved) {
+ pci_save_state(pci_dev);
+ if (!pci_has_subordinate(pci_dev))
+ pci_prepare_to_sleep(pci_dev);
+ }
+
+ pci_pm_set_unknown_state(pci_dev);
+
+ /*
+ * Some BIOSes from ASUS have a bug: If a USB EHCI host controller's
+ * PCI COMMAND register isn't 0, the BIOS assumes that the controller
+ * hasn't been quiesced and tries to turn it off. If the controller
+ * is already in D3, this can hang or cause memory corruption.
+ *
+ * Since the value of the COMMAND register doesn't matter once the
+ * device has been suspended, we can safely set it to 0 here.
+ */
+ if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI)
+ pci_write_config_word(pci_dev, PCI_COMMAND, 0);
+
+ return 0;
+}
+
+static int pci_pm_resume_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
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);
+ pci_pm_default_resume_early(pci_dev);
+
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_resume_early(dev);
+
+ if (drv && drv->pm && drv->pm->resume_noirq)
+ error = drv->pm->resume_noirq(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_resume(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
- struct pci_driver *drv = pci_dev->driver;
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ int error = 0;
- if (drv && drv->shutdown)
- drv->shutdown(pci_dev);
+ /*
+ * This is necessary for the suspend error path in which resume is
+ * called without restoring the standard config registers of the device.
+ */
+ if (pci_dev->state_saved)
+ pci_restore_standard_config(pci_dev);
+
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_resume(dev);
+
+ pci_pm_default_resume(pci_dev);
+
+ if (pm) {
+ if (pm->resume)
+ error = pm->resume(dev);
+ } else {
+ pci_pm_reenable_device(pci_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_HIBERNATE_CALLBACKS
+
+
+/*
+ * pcibios_pm_ops - provide arch-specific hooks when a PCI device is doing
+ * a hibernate transition
+ */
+struct dev_pm_ops __weak pcibios_pm_ops;
+
+static int pci_pm_freeze(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_suspend(dev, PMSG_FREEZE);
+
+ if (!pm) {
+ pci_pm_default_suspend(pci_dev);
+ return 0;
+ }
+
+ /*
+ * This used to be done in pci_pm_prepare() for all devices and some
+ * drivers may depend on it, so do it here. Ideally, runtime-suspended
+ * devices should not be touched during freeze/thaw transitions,
+ * however.
+ */
+ pm_runtime_resume(dev);
+
+ pci_dev->state_saved = false;
+ if (pm->freeze) {
+ int error;
+
+ error = pm->freeze(dev);
+ suspend_report_result(pm->freeze, error);
+ if (error)
+ return error;
+ }
+
+ if (pcibios_pm_ops.freeze)
+ return pcibios_pm_ops.freeze(dev);
+
+ return 0;
+}
+
+static int pci_pm_freeze_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_suspend_late(dev, PMSG_FREEZE);
+
+ if (drv && drv->pm && drv->pm->freeze_noirq) {
+ int error;
+
+ error = drv->pm->freeze_noirq(dev);
+ suspend_report_result(drv->pm->freeze_noirq, error);
+ if (error)
+ return error;
+ }
+
+ if (!pci_dev->state_saved)
+ pci_save_state(pci_dev);
+
+ pci_pm_set_unknown_state(pci_dev);
+
+ if (pcibios_pm_ops.freeze_noirq)
+ return pcibios_pm_ops.freeze_noirq(dev);
+
+ return 0;
+}
+
+static int pci_pm_thaw_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ if (pcibios_pm_ops.thaw_noirq) {
+ error = pcibios_pm_ops.thaw_noirq(dev);
+ if (error)
+ return error;
+ }
+
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_resume_early(dev);
+
+ pci_update_current_state(pci_dev, PCI_D0);
+
+ if (drv && drv->pm && drv->pm->thaw_noirq)
+ error = drv->pm->thaw_noirq(dev);
+
+ return error;
+}
+
+static int pci_pm_thaw(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ int error = 0;
+
+ if (pcibios_pm_ops.thaw) {
+ error = pcibios_pm_ops.thaw(dev);
+ if (error)
+ return error;
+ }
+
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_resume(dev);
+
+ if (pm) {
+ if (pm->thaw)
+ error = pm->thaw(dev);
+ } else {
+ pci_pm_reenable_device(pci_dev);
+ }
+
+ pci_dev->state_saved = false;
+
+ return error;
+}
+
+static int pci_pm_poweroff(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_suspend(dev, PMSG_HIBERNATE);
+
+ if (!pm) {
+ pci_pm_default_suspend(pci_dev);
+ goto Fixup;
+ }
+
+ /* The reason to do that is the same as in pci_pm_suspend(). */
+ pm_runtime_resume(dev);
+
+ pci_dev->state_saved = false;
+ if (pm->poweroff) {
+ int error;
+
+ error = pm->poweroff(dev);
+ suspend_report_result(pm->poweroff, error);
+ if (error)
+ return error;
+ }
+
+ Fixup:
+ pci_fixup_device(pci_fixup_suspend, pci_dev);
+
+ if (pcibios_pm_ops.poweroff)
+ return pcibios_pm_ops.poweroff(dev);
+
+ return 0;
+}
+
+static int pci_pm_poweroff_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+
+ if (pci_has_legacy_pm_support(to_pci_dev(dev)))
+ return pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
+
+ if (!drv || !drv->pm)
+ return 0;
+
+ if (drv->pm->poweroff_noirq) {
+ int error;
+
+ error = drv->pm->poweroff_noirq(dev);
+ suspend_report_result(drv->pm->poweroff_noirq, error);
+ if (error)
+ return error;
+ }
+
+ if (!pci_dev->state_saved && !pci_has_subordinate(pci_dev))
+ pci_prepare_to_sleep(pci_dev);
+
+ /*
+ * The reason for doing this here is the same as for the analogous code
+ * in pci_pm_suspend_noirq().
+ */
+ if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI)
+ pci_write_config_word(pci_dev, PCI_COMMAND, 0);
+
+ if (pcibios_pm_ops.poweroff_noirq)
+ return pcibios_pm_ops.poweroff_noirq(dev);
+
+ return 0;
+}
+
+static int pci_pm_restore_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ if (pcibios_pm_ops.restore_noirq) {
+ error = pcibios_pm_ops.restore_noirq(dev);
+ if (error)
+ return error;
+ }
+
+ pci_pm_default_resume_early(pci_dev);
+
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_resume_early(dev);
+
+ if (drv && drv->pm && drv->pm->restore_noirq)
+ error = drv->pm->restore_noirq(dev);
+
+ return error;
+}
+
+static int pci_pm_restore(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ int error = 0;
+
+ if (pcibios_pm_ops.restore) {
+ error = pcibios_pm_ops.restore(dev);
+ if (error)
+ return error;
+ }
+
+ /*
+ * This is necessary for the hibernation error path in which restore is
+ * called without restoring the standard config registers of the device.
+ */
+ if (pci_dev->state_saved)
+ pci_restore_standard_config(pci_dev);
+
+ if (pci_has_legacy_pm_support(pci_dev))
+ return pci_legacy_resume(dev);
+
+ pci_pm_default_resume(pci_dev);
+
+ if (pm) {
+ if (pm->restore)
+ error = pm->restore(dev);
+ } else {
+ pci_pm_reenable_device(pci_dev);
+ }
+
+ return error;
+}
+
+#else /* !CONFIG_HIBERNATE_CALLBACKS */
+
+#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_HIBERNATE_CALLBACKS */
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int pci_pm_runtime_suspend(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ pci_power_t prev = pci_dev->current_state;
+ int error;
+
+ /*
+ * If pci_dev->driver is not set (unbound), the device should
+ * always remain in D0 regardless of the runtime PM status
+ */
+ if (!pci_dev->driver)
+ return 0;
+
+ if (!pm || !pm->runtime_suspend)
+ return -ENOSYS;
+
+ pci_dev->state_saved = false;
+ pci_dev->no_d3cold = false;
+ error = pm->runtime_suspend(dev);
+ suspend_report_result(pm->runtime_suspend, error);
+ if (error)
+ return error;
+ if (!pci_dev->d3cold_allowed)
+ pci_dev->no_d3cold = true;
+
+ pci_fixup_device(pci_fixup_suspend, pci_dev);
+
+ if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
+ && pci_dev->current_state != PCI_UNKNOWN) {
+ WARN_ONCE(pci_dev->current_state != prev,
+ "PCI PM: State of device not saved by %pF\n",
+ pm->runtime_suspend);
+ return 0;
+ }
+
+ if (!pci_dev->state_saved) {
+ pci_save_state(pci_dev);
+ pci_finish_runtime_suspend(pci_dev);
+ }
+
+ return 0;
+}
+
+static int pci_pm_runtime_resume(struct device *dev)
+{
+ int rc;
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ /*
+ * If pci_dev->driver is not set (unbound), the device should
+ * always remain in D0 regardless of the runtime PM status
+ */
+ if (!pci_dev->driver)
+ return 0;
+
+ if (!pm || !pm->runtime_resume)
+ return -ENOSYS;
+
+ pci_restore_standard_config(pci_dev);
+ pci_fixup_device(pci_fixup_resume_early, pci_dev);
+ __pci_enable_wake(pci_dev, PCI_D0, true, false);
+ pci_fixup_device(pci_fixup_resume, pci_dev);
+
+ rc = pm->runtime_resume(dev);
+
+ pci_dev->runtime_d3cold = false;
+
+ return rc;
+}
+
+static int pci_pm_runtime_idle(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ int ret = 0;
+
+ /*
+ * If pci_dev->driver is not set (unbound), the device should
+ * always remain in D0 regardless of the runtime PM status
+ */
+ if (!pci_dev->driver)
+ return 0;
+
+ if (!pm)
+ return -ENOSYS;
+
+ if (pm->runtime_idle)
+ ret = pm->runtime_idle(dev);
+
+ return ret;
}
+#else /* !CONFIG_PM_RUNTIME */
+
+#define pci_pm_runtime_suspend NULL
+#define pci_pm_runtime_resume NULL
+#define pci_pm_runtime_idle NULL
+
+#endif /* !CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM
+
+static const struct dev_pm_ops pci_dev_pm_ops = {
+ .prepare = pci_pm_prepare,
+ .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,
+ .runtime_suspend = pci_pm_runtime_suspend,
+ .runtime_resume = pci_pm_runtime_resume,
+ .runtime_idle = pci_pm_runtime_idle,
+};
+
+#define PCI_PM_OPS_PTR (&pci_dev_pm_ops)
+
+#else /* !COMFIG_PM_OPS */
+
+#define PCI_PM_OPS_PTR NULL
+
+#endif /* !COMFIG_PM_OPS */
+
/**
* __pci_register_driver - register a new pci driver
* @drv: the driver structure to register
* @owner: owner module of drv
* @mod_name: module name string
- *
+ *
* Adds the driver structure to the list of registered drivers.
- * Returns a negative value on error, otherwise 0.
- * If no error occurred, the driver remains registered even if
+ * Returns a negative value on error, otherwise 0.
+ * If no error occurred, the driver remains registered even if
* no device was claimed during registration.
*/
int __pci_register_driver(struct pci_driver *drv, struct module *owner,
const char *mod_name)
{
- int error;
-
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
@@ -385,34 +1254,26 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
INIT_LIST_HEAD(&drv->dynids.list);
/* register with core */
- error = driver_register(&drv->driver);
- if (error)
- return error;
-
- error = pci_create_newid_file(drv);
- if (error)
- driver_unregister(&drv->driver);
-
- return error;
+ return driver_register(&drv->driver);
}
+EXPORT_SYMBOL(__pci_register_driver);
/**
* pci_unregister_driver - unregister a pci driver
* @drv: the driver structure to unregister
- *
+ *
* Deletes the driver structure from the list of registered PCI drivers,
* gives it a chance to clean up by calling its remove() function for
* each device it was responsible for, and marks those devices as
* driverless.
*/
-void
-pci_unregister_driver(struct pci_driver *drv)
+void pci_unregister_driver(struct pci_driver *drv)
{
- pci_remove_newid_file(drv);
driver_unregister(&drv->driver);
pci_free_dynids(drv);
}
+EXPORT_SYMBOL(pci_unregister_driver);
static struct pci_driver pci_compat_driver = {
.name = "compat"
@@ -422,28 +1283,28 @@ static struct pci_driver pci_compat_driver = {
* pci_dev_driver - get the pci_driver of a device
* @dev: the device to query
*
- * Returns the appropriate pci_driver structure or %NULL if there is no
+ * Returns the appropriate pci_driver structure or %NULL if there is no
* registered driver for the device.
*/
-struct pci_driver *
-pci_dev_driver(const struct pci_dev *dev)
+struct pci_driver *pci_dev_driver(const struct pci_dev *dev)
{
if (dev->driver)
return dev->driver;
else {
int i;
- for(i=0; i<=PCI_ROM_RESOURCE; i++)
+ for (i = 0; i <= PCI_ROM_RESOURCE; i++)
if (dev->resource[i].flags & IORESOURCE_BUSY)
return &pci_compat_driver;
}
return NULL;
}
+EXPORT_SYMBOL(pci_dev_driver);
/**
* pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure
* @dev: the PCI device structure to match against
* @drv: the device driver to search for matching PCI device id structures
- *
+ *
* Used by a driver to check whether a PCI device present in the
* system is in its list of supported devices. Returns the matching
* pci_device_id structure or %NULL if there is no match.
@@ -451,9 +1312,13 @@ pci_dev_driver(const struct pci_dev *dev)
static int pci_bus_match(struct device *dev, struct device_driver *drv)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
- struct pci_driver *pci_drv = to_pci_driver(drv);
+ struct pci_driver *pci_drv;
const struct pci_device_id *found_id;
+ if (!pci_dev->match_driver)
+ return 0;
+
+ pci_drv = to_pci_driver(drv);
found_id = pci_match_device(pci_drv, pci_dev);
if (found_id)
return 1;
@@ -479,6 +1344,7 @@ struct pci_dev *pci_dev_get(struct pci_dev *dev)
get_device(&dev->dev);
return dev;
}
+EXPORT_SYMBOL(pci_dev_get);
/**
* pci_dev_put - release a use of the pci device structure
@@ -492,13 +1358,39 @@ void pci_dev_put(struct pci_dev *dev)
if (dev)
put_device(&dev->dev);
}
+EXPORT_SYMBOL(pci_dev_put);
-#ifndef CONFIG_HOTPLUG
-int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
{
- return -ENODEV;
+ struct pci_dev *pdev;
+
+ if (!dev)
+ return -ENODEV;
+
+ pdev = to_pci_dev(dev);
+
+ if (add_uevent_var(env, "PCI_CLASS=%04X", pdev->class))
+ return -ENOMEM;
+
+ if (add_uevent_var(env, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device))
+ return -ENOMEM;
+
+ if (add_uevent_var(env, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor,
+ pdev->subsystem_device))
+ return -ENOMEM;
+
+ if (add_uevent_var(env, "PCI_SLOT_NAME=%s", pci_name(pdev)))
+ return -ENOMEM;
+
+ if (add_uevent_var(env, "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x",
+ pdev->vendor, pdev->device,
+ pdev->subsystem_vendor, pdev->subsystem_device,
+ (u8)(pdev->class >> 16), (u8)(pdev->class >> 8),
+ (u8)(pdev->class)))
+ return -ENOMEM;
+
+ return 0;
}
-#endif
struct bus_type pci_bus_type = {
.name = "pci",
@@ -506,25 +1398,16 @@ 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,
+ .dev_groups = pci_dev_groups,
+ .bus_groups = pci_bus_groups,
+ .drv_groups = pci_drv_groups,
+ .pm = PCI_PM_OPS_PTR,
};
+EXPORT_SYMBOL(pci_bus_type);
static int __init pci_driver_init(void)
{
return bus_register(&pci_bus_type);
}
-
postcore_initcall(pci_driver_init);
-
-EXPORT_SYMBOL(pci_match_id);
-EXPORT_SYMBOL(__pci_register_driver);
-EXPORT_SYMBOL(pci_unregister_driver);
-EXPORT_SYMBOL(pci_dev_driver);
-EXPORT_SYMBOL(pci_bus_type);
-EXPORT_SYMBOL(pci_dev_get);
-EXPORT_SYMBOL(pci_dev_put);
diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c
new file mode 100644
index 00000000000..a3fbe2012ea
--- /dev/null
+++ b/drivers/pci/pci-label.c
@@ -0,0 +1,307 @@
+/*
+ * Purpose: Export the firmware instance and label associated with
+ * a pci device to sysfs
+ * Copyright (C) 2010 Dell Inc.
+ * by Narendra K <Narendra_K@dell.com>,
+ * Jordan Hargrave <Jordan_Hargrave@dell.com>
+ *
+ * PCI Firmware Specification Revision 3.1 section 4.6.7 (DSM for Naming a
+ * PCI or PCI Express Device Under Operating Systems) defines an instance
+ * number and string name. This code retrieves them and exports them to sysfs.
+ * If the system firmware does not provide the ACPI _DSM (Device Specific
+ * Method), then the SMBIOS type 41 instance number and string is exported to
+ * sysfs.
+ *
+ * SMBIOS defines type 41 for onboard pci devices. This code retrieves
+ * the instance number and string from the type 41 record and exports
+ * it to sysfs.
+ *
+ * Please see http://linux.dell.com/wiki/index.php/Oss/libnetdevname for more
+ * information.
+ */
+
+#include <linux/dmi.h>
+#include <linux/sysfs.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/nls.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include "pci.h"
+
+#define DEVICE_LABEL_DSM 0x07
+
+#ifdef CONFIG_DMI
+enum smbios_attr_enum {
+ SMBIOS_ATTR_NONE = 0,
+ SMBIOS_ATTR_LABEL_SHOW,
+ SMBIOS_ATTR_INSTANCE_SHOW,
+};
+
+static size_t find_smbios_instance_string(struct pci_dev *pdev, char *buf,
+ enum smbios_attr_enum attribute)
+{
+ const struct dmi_device *dmi;
+ struct dmi_dev_onboard *donboard;
+ int bus;
+ int devfn;
+
+ bus = pdev->bus->number;
+ devfn = pdev->devfn;
+
+ dmi = NULL;
+ while ((dmi = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD,
+ NULL, dmi)) != NULL) {
+ donboard = dmi->device_data;
+ if (donboard && donboard->bus == bus &&
+ donboard->devfn == devfn) {
+ if (buf) {
+ if (attribute == SMBIOS_ATTR_INSTANCE_SHOW)
+ return scnprintf(buf, PAGE_SIZE,
+ "%d\n",
+ donboard->instance);
+ else if (attribute == SMBIOS_ATTR_LABEL_SHOW)
+ return scnprintf(buf, PAGE_SIZE,
+ "%s\n",
+ dmi->name);
+ }
+ return strlen(dmi->name);
+ }
+ }
+ return 0;
+}
+
+static umode_t smbios_instance_string_exist(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev;
+ struct pci_dev *pdev;
+
+ dev = container_of(kobj, struct device, kobj);
+ pdev = to_pci_dev(dev);
+
+ return find_smbios_instance_string(pdev, NULL, SMBIOS_ATTR_NONE) ?
+ S_IRUGO : 0;
+}
+
+static ssize_t smbioslabel_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev;
+ pdev = to_pci_dev(dev);
+
+ return find_smbios_instance_string(pdev, buf,
+ SMBIOS_ATTR_LABEL_SHOW);
+}
+
+static ssize_t smbiosinstance_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev;
+ pdev = to_pci_dev(dev);
+
+ return find_smbios_instance_string(pdev, buf,
+ SMBIOS_ATTR_INSTANCE_SHOW);
+}
+
+static struct device_attribute smbios_attr_label = {
+ .attr = {.name = "label", .mode = 0444},
+ .show = smbioslabel_show,
+};
+
+static struct device_attribute smbios_attr_instance = {
+ .attr = {.name = "index", .mode = 0444},
+ .show = smbiosinstance_show,
+};
+
+static struct attribute *smbios_attributes[] = {
+ &smbios_attr_label.attr,
+ &smbios_attr_instance.attr,
+ NULL,
+};
+
+static struct attribute_group smbios_attr_group = {
+ .attrs = smbios_attributes,
+ .is_visible = smbios_instance_string_exist,
+};
+
+static int pci_create_smbiosname_file(struct pci_dev *pdev)
+{
+ return sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group);
+}
+
+static void pci_remove_smbiosname_file(struct pci_dev *pdev)
+{
+ sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group);
+}
+#else
+static inline int pci_create_smbiosname_file(struct pci_dev *pdev)
+{
+ return -1;
+}
+
+static inline void pci_remove_smbiosname_file(struct pci_dev *pdev)
+{
+}
+#endif
+
+#ifdef CONFIG_ACPI
+static const char device_label_dsm_uuid[] = {
+ 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D,
+ 0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D
+};
+
+enum acpi_attr_enum {
+ ACPI_ATTR_LABEL_SHOW,
+ ACPI_ATTR_INDEX_SHOW,
+};
+
+static void dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)
+{
+ int len;
+ len = utf16s_to_utf8s((const wchar_t *)obj->string.pointer,
+ obj->string.length,
+ UTF16_LITTLE_ENDIAN,
+ buf, PAGE_SIZE);
+ buf[len] = '\n';
+}
+
+static int dsm_get_label(struct device *dev, char *buf,
+ enum acpi_attr_enum attr)
+{
+ acpi_handle handle;
+ union acpi_object *obj, *tmp;
+ int len = -1;
+
+ handle = ACPI_HANDLE(dev);
+ if (!handle)
+ return -1;
+
+ obj = acpi_evaluate_dsm(handle, device_label_dsm_uuid, 0x2,
+ DEVICE_LABEL_DSM, NULL);
+ if (!obj)
+ return -1;
+
+ tmp = obj->package.elements;
+ if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 2 &&
+ tmp[0].type == ACPI_TYPE_INTEGER &&
+ tmp[1].type == ACPI_TYPE_STRING) {
+ /*
+ * The second string element is optional even when
+ * this _DSM is implemented; when not implemented,
+ * this entry must return a null string.
+ */
+ if (attr == ACPI_ATTR_INDEX_SHOW)
+ scnprintf(buf, PAGE_SIZE, "%llu\n", tmp->integer.value);
+ else if (attr == ACPI_ATTR_LABEL_SHOW)
+ dsm_label_utf16s_to_utf8s(tmp + 1, buf);
+ len = strlen(buf) > 0 ? strlen(buf) : -1;
+ }
+
+ ACPI_FREE(obj);
+
+ return len;
+}
+
+static bool device_has_dsm(struct device *dev)
+{
+ acpi_handle handle;
+
+ handle = ACPI_HANDLE(dev);
+ if (!handle)
+ return false;
+
+ return !!acpi_check_dsm(handle, device_label_dsm_uuid, 0x2,
+ 1 << DEVICE_LABEL_DSM);
+}
+
+static umode_t acpi_index_string_exist(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev;
+
+ dev = container_of(kobj, struct device, kobj);
+
+ if (device_has_dsm(dev))
+ return S_IRUGO;
+
+ return 0;
+}
+
+static ssize_t acpilabel_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return dsm_get_label(dev, buf, ACPI_ATTR_LABEL_SHOW);
+}
+
+static ssize_t acpiindex_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return dsm_get_label(dev, buf, ACPI_ATTR_INDEX_SHOW);
+}
+
+static struct device_attribute acpi_attr_label = {
+ .attr = {.name = "label", .mode = 0444},
+ .show = acpilabel_show,
+};
+
+static struct device_attribute acpi_attr_index = {
+ .attr = {.name = "acpi_index", .mode = 0444},
+ .show = acpiindex_show,
+};
+
+static struct attribute *acpi_attributes[] = {
+ &acpi_attr_label.attr,
+ &acpi_attr_index.attr,
+ NULL,
+};
+
+static struct attribute_group acpi_attr_group = {
+ .attrs = acpi_attributes,
+ .is_visible = acpi_index_string_exist,
+};
+
+static int pci_create_acpi_index_label_files(struct pci_dev *pdev)
+{
+ return sysfs_create_group(&pdev->dev.kobj, &acpi_attr_group);
+}
+
+static int pci_remove_acpi_index_label_files(struct pci_dev *pdev)
+{
+ sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group);
+ return 0;
+}
+#else
+static inline int pci_create_acpi_index_label_files(struct pci_dev *pdev)
+{
+ return -1;
+}
+
+static inline int pci_remove_acpi_index_label_files(struct pci_dev *pdev)
+{
+ return -1;
+}
+
+static inline bool device_has_dsm(struct device *dev)
+{
+ return false;
+}
+#endif
+
+void pci_create_firmware_label_files(struct pci_dev *pdev)
+{
+ if (device_has_dsm(&pdev->dev))
+ pci_create_acpi_index_label_files(pdev);
+ else
+ pci_create_smbiosname_file(pdev);
+}
+
+void pci_remove_firmware_label_files(struct pci_dev *pdev)
+{
+ if (device_has_dsm(&pdev->dev))
+ pci_remove_acpi_index_label_files(pdev);
+ else
+ pci_remove_smbiosname_file(pdev);
+}
diff --git a/drivers/pci/pci-stub.c b/drivers/pci/pci-stub.c
new file mode 100644
index 00000000000..886fb357027
--- /dev/null
+++ b/drivers/pci/pci-stub.c
@@ -0,0 +1,97 @@
+/* pci-stub - simple stub driver to reserve a pci device
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ * Author:
+ * Chris Wright
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ *
+ * Usage is simple, allocate a new id to the stub driver and bind the
+ * device to it. For example:
+ *
+ * # echo "8086 10f5" > /sys/bus/pci/drivers/pci-stub/new_id
+ * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind
+ * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/pci-stub/bind
+ * # ls -l /sys/bus/pci/devices/0000:00:19.0/driver
+ * .../0000:00:19.0/driver -> ../../../bus/pci/drivers/pci-stub
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+
+static char ids[1024] __initdata;
+
+module_param_string(ids, ids, sizeof(ids), 0);
+MODULE_PARM_DESC(ids, "Initial PCI IDs to add to the stub driver, format is "
+ "\"vendor:device[:subvendor[:subdevice[:class[:class_mask]]]]\""
+ " and multiple comma separated entries can be specified");
+
+static int pci_stub_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ dev_info(&dev->dev, "claimed by stub\n");
+ return 0;
+}
+
+static struct pci_driver stub_driver = {
+ .name = "pci-stub",
+ .id_table = NULL, /* only dynamic id's */
+ .probe = pci_stub_probe,
+};
+
+static int __init pci_stub_init(void)
+{
+ char *p, *id;
+ int rc;
+
+ rc = pci_register_driver(&stub_driver);
+ if (rc)
+ return rc;
+
+ /* no ids passed actually */
+ if (ids[0] == '\0')
+ return 0;
+
+ /* add ids specified in the module parameter */
+ p = ids;
+ while ((id = strsep(&p, ","))) {
+ unsigned int vendor, device, subvendor = PCI_ANY_ID,
+ subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
+ int fields;
+
+ if (!strlen(id))
+ continue;
+
+ fields = sscanf(id, "%x:%x:%x:%x:%x:%x",
+ &vendor, &device, &subvendor, &subdevice,
+ &class, &class_mask);
+
+ if (fields < 2) {
+ printk(KERN_WARNING
+ "pci-stub: invalid id string \"%s\"\n", id);
+ continue;
+ }
+
+ printk(KERN_INFO
+ "pci-stub: add %04X:%04X sub=%04X:%04X cls=%08X/%08X\n",
+ vendor, device, subvendor, subdevice, class, class_mask);
+
+ rc = pci_add_dynid(&stub_driver, vendor, device,
+ subvendor, subdevice, class, class_mask, 0);
+ if (rc)
+ printk(KERN_WARNING
+ "pci-stub: failed to add dynamic id (%d)\n", rc);
+ }
+
+ return 0;
+}
+
+static void __exit pci_stub_exit(void)
+{
+ pci_unregister_driver(&stub_driver);
+}
+
+module_init(pci_stub_init);
+module_exit(pci_stub_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Chris Wright <chrisw@sous-sol.org>");
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 8dcf1458aa2..9ff0a901ecf 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -10,17 +10,26 @@
*
* File attributes for PCI devices
*
- * Modeled after usb's driverfs.c
+ * Modeled after usb's driverfs.c
*
*/
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/stat.h>
+#include <linux/export.h>
#include <linux/topology.h>
#include <linux/mm.h>
+#include <linux/fs.h>
#include <linux/capability.h>
+#include <linux/security.h>
+#include <linux/pci-aspm.h>
+#include <linux/slab.h>
+#include <linux/vgaarb.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
#include "pci.h"
static int sysfs_initialized; /* = 0 */
@@ -32,9 +41,10 @@ field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct pci_dev *pdev; \
\
- pdev = to_pci_dev (dev); \
- return sprintf (buf, format_string, pdev->field); \
-}
+ pdev = to_pci_dev(dev); \
+ return sprintf(buf, format_string, pdev->field); \
+} \
+static DEVICE_ATTR_RO(field)
pci_config_attr(vendor, "0x%04x\n");
pci_config_attr(device, "0x%04x\n");
@@ -48,7 +58,7 @@ static ssize_t broken_parity_status_show(struct device *dev,
char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
- return sprintf (buf, "%u\n", pdev->broken_parity_status);
+ return sprintf(buf, "%u\n", pdev->broken_parity_status);
}
static ssize_t broken_parity_status_store(struct device *dev,
@@ -56,52 +66,114 @@ static ssize_t broken_parity_status_store(struct device *dev,
const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
- ssize_t consumed = -EINVAL;
+ unsigned long val;
- if ((count > 0) && (*buf == '0' || *buf == '1')) {
- pdev->broken_parity_status = *buf == '1' ? 1 : 0;
- consumed = count;
- }
- return consumed;
+ if (kstrtoul(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ pdev->broken_parity_status = !!val;
+
+ return count;
}
+static DEVICE_ATTR_RW(broken_parity_status);
-static ssize_t local_cpus_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- cpumask_t mask;
+static ssize_t pci_dev_show_local_cpu(struct device *dev, int type,
+ struct device_attribute *attr, char *buf)
+{
+ const struct cpumask *mask;
int len;
- mask = pcibus_to_cpumask(to_pci_dev(dev)->bus);
- len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask);
- strcat(buf,"\n");
- return 1+len;
+#ifdef CONFIG_NUMA
+ mask = (dev_to_node(dev) == -1) ? cpu_online_mask :
+ cpumask_of_node(dev_to_node(dev));
+#else
+ mask = cpumask_of_pcibus(to_pci_dev(dev)->bus);
+#endif
+ len = type ?
+ cpumask_scnprintf(buf, PAGE_SIZE-2, mask) :
+ cpulist_scnprintf(buf, PAGE_SIZE-2, mask);
+
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ return len;
+}
+
+static ssize_t local_cpus_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return pci_dev_show_local_cpu(dev, 1, attr, buf);
+}
+static DEVICE_ATTR_RO(local_cpus);
+
+static ssize_t local_cpulist_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return pci_dev_show_local_cpu(dev, 0, attr, buf);
+}
+static DEVICE_ATTR_RO(local_cpulist);
+
+/*
+ * PCI Bus Class Devices
+ */
+static ssize_t pci_bus_show_cpuaffinity(struct device *dev, int type,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ const struct cpumask *cpumask;
+
+ cpumask = cpumask_of_pcibus(to_pci_bus(dev));
+ ret = type ?
+ cpulist_scnprintf(buf, PAGE_SIZE-2, cpumask) :
+ cpumask_scnprintf(buf, PAGE_SIZE-2, cpumask);
+ buf[ret++] = '\n';
+ buf[ret] = '\0';
+ return ret;
+}
+
+static ssize_t cpuaffinity_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return pci_bus_show_cpuaffinity(dev, 0, attr, buf);
}
+static DEVICE_ATTR_RO(cpuaffinity);
+
+static ssize_t cpulistaffinity_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return pci_bus_show_cpuaffinity(dev, 1, attr, buf);
+}
+static DEVICE_ATTR_RO(cpulistaffinity);
/* show resources */
-static ssize_t
-resource_show(struct device * dev, struct device_attribute *attr, char * buf)
+static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
- struct pci_dev * pci_dev = to_pci_dev(dev);
- char * str = buf;
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ char *str = buf;
int i;
- int max = 7;
+ int max;
resource_size_t start, end;
if (pci_dev->subordinate)
max = DEVICE_COUNT_RESOURCE;
+ else
+ max = PCI_BRIDGE_RESOURCES;
for (i = 0; i < max; i++) {
struct resource *res = &pci_dev->resource[i];
pci_resource_to_user(pci_dev, i, res, &start, &end);
- str += sprintf(str,"0x%016llx 0x%016llx 0x%016llx\n",
+ str += sprintf(str, "0x%016llx 0x%016llx 0x%016llx\n",
(unsigned long long)start,
(unsigned long long)end,
(unsigned long long)res->flags);
}
return (str - buf);
}
+static DEVICE_ATTR_RO(resource);
-static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -111,122 +183,455 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
(u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8),
(u8)(pci_dev->class));
}
+static DEVICE_ATTR_RO(modalias);
-static ssize_t is_enabled_store(struct device *dev,
- struct device_attribute *attr, const char *buf,
- size_t count)
+static ssize_t enabled_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
- ssize_t result = -EINVAL;
struct pci_dev *pdev = to_pci_dev(dev);
+ unsigned long val;
+ ssize_t result = kstrtoul(buf, 0, &val);
+
+ if (result < 0)
+ return result;
/* this can crash the machine when done on the "wrong" device */
if (!capable(CAP_SYS_ADMIN))
- return count;
+ return -EPERM;
- if (*buf == '0') {
- if (atomic_read(&pdev->enable_cnt) != 0)
+ if (!val) {
+ if (pci_is_enabled(pdev))
pci_disable_device(pdev);
else
result = -EIO;
- } else if (*buf == '1')
+ } else
result = pci_enable_device(pdev);
return result < 0 ? result : count;
}
-static ssize_t is_enabled_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct pci_dev *pdev;
- pdev = to_pci_dev (dev);
- return sprintf (buf, "%u\n", atomic_read(&pdev->enable_cnt));
+ pdev = to_pci_dev(dev);
+ return sprintf(buf, "%u\n", atomic_read(&pdev->enable_cnt));
}
+static DEVICE_ATTR_RW(enabled);
#ifdef CONFIG_NUMA
-static ssize_t
-numa_node_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t numa_node_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
- return sprintf (buf, "%d\n", dev->numa_node);
+ return sprintf(buf, "%d\n", dev->numa_node);
}
+static DEVICE_ATTR_RO(numa_node);
#endif
-static ssize_t
-msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t dma_mask_bits_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%d\n", fls64(pdev->dma_mask));
+}
+static DEVICE_ATTR_RO(dma_mask_bits);
+
+static ssize_t consistent_dma_mask_bits_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", fls64(dev->coherent_dma_mask));
+}
+static DEVICE_ATTR_RO(consistent_dma_mask_bits);
+
+static ssize_t msi_bus_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
if (!pdev->subordinate)
return 0;
- return sprintf (buf, "%u\n",
- !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
+ return sprintf(buf, "%u\n",
+ !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
}
-static ssize_t
-msi_bus_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t msi_bus_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val) < 0)
+ return -EINVAL;
- /* bad things may happen if the no_msi flag is changed
- * while some drivers are loaded */
+ /*
+ * Bad things may happen if the no_msi flag is changed
+ * while drivers are loaded.
+ */
if (!capable(CAP_SYS_ADMIN))
- return count;
+ return -EPERM;
+ /*
+ * Maybe devices without subordinate buses shouldn't have this
+ * attribute in the first place?
+ */
if (!pdev->subordinate)
return count;
- if (*buf == '0') {
- pdev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
- dev_warn(&pdev->dev, "forced subordinate bus to not support MSI,"
- " bad things could happen.\n");
+ /* Is the flag going to change, or keep the value it already had? */
+ if (!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) ^
+ !!val) {
+ pdev->subordinate->bus_flags ^= PCI_BUS_FLAGS_NO_MSI;
+
+ dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI, bad things could happen\n",
+ val ? "" : " not");
+ }
+
+ return count;
+}
+static DEVICE_ATTR_RW(msi_bus);
+
+static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ struct pci_bus *b = NULL;
+
+ if (kstrtoul(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ if (val) {
+ pci_lock_rescan_remove();
+ while ((b = pci_find_next_bus(b)) != NULL)
+ pci_rescan_bus(b);
+ pci_unlock_rescan_remove();
+ }
+ return count;
+}
+static BUS_ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store);
+
+static struct attribute *pci_bus_attrs[] = {
+ &bus_attr_rescan.attr,
+ NULL,
+};
+
+static const struct attribute_group pci_bus_group = {
+ .attrs = pci_bus_attrs,
+};
+
+const struct attribute_group *pci_bus_groups[] = {
+ &pci_bus_group,
+ NULL,
+};
+
+static ssize_t dev_rescan_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (kstrtoul(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ if (val) {
+ pci_lock_rescan_remove();
+ pci_rescan_bus(pdev->bus);
+ pci_unlock_rescan_remove();
+ }
+ return count;
+}
+static struct device_attribute dev_rescan_attr = __ATTR(rescan,
+ (S_IWUSR|S_IWGRP),
+ NULL, dev_rescan_store);
+
+static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ if (val && device_remove_file_self(dev, attr))
+ pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
+ return count;
+}
+static struct device_attribute dev_remove_attr = __ATTR(remove,
+ (S_IWUSR|S_IWGRP),
+ NULL, remove_store);
+
+static ssize_t dev_bus_rescan_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct pci_bus *bus = to_pci_bus(dev);
+
+ if (kstrtoul(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ if (val) {
+ pci_lock_rescan_remove();
+ if (!pci_is_root_bus(bus) && list_empty(&bus->devices))
+ pci_rescan_bus_bridge_resize(bus->self);
+ else
+ pci_rescan_bus(bus);
+ pci_unlock_rescan_remove();
+ }
+ return count;
+}
+static DEVICE_ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_bus_rescan_store);
+
+#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI)
+static ssize_t d3cold_allowed_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ pdev->d3cold_allowed = !!val;
+ pm_runtime_resume(dev);
+
+ return count;
+}
+
+static ssize_t d3cold_allowed_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ return sprintf(buf, "%u\n", pdev->d3cold_allowed);
+}
+static DEVICE_ATTR_RW(d3cold_allowed);
+#endif
+
+#ifdef CONFIG_OF
+static ssize_t devspec_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct device_node *np = pci_device_to_OF_node(pdev);
+
+ if (np == NULL || np->full_name == NULL)
+ return 0;
+ return sprintf(buf, "%s", np->full_name);
+}
+static DEVICE_ATTR_RO(devspec);
+#endif
+
+#ifdef CONFIG_PCI_IOV
+static ssize_t sriov_totalvfs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%u\n", pci_sriov_get_totalvfs(pdev));
+}
+
+
+static ssize_t sriov_numvfs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%u\n", pdev->sriov->num_VFs);
+}
+
+/*
+ * num_vfs > 0; number of VFs to enable
+ * num_vfs = 0; disable all VFs
+ *
+ * Note: SRIOV spec doesn't allow partial VF
+ * disable, so it's all or none.
+ */
+static ssize_t sriov_numvfs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ int ret;
+ u16 num_vfs;
+
+ ret = kstrtou16(buf, 0, &num_vfs);
+ if (ret < 0)
+ return ret;
+
+ if (num_vfs > pci_sriov_get_totalvfs(pdev))
+ return -ERANGE;
+
+ if (num_vfs == pdev->sriov->num_VFs)
+ return count; /* no change */
+
+ /* is PF driver loaded w/callback */
+ if (!pdev->driver || !pdev->driver->sriov_configure) {
+ dev_info(&pdev->dev, "Driver doesn't support SRIOV configuration via sysfs\n");
+ return -ENOSYS;
+ }
+
+ if (num_vfs == 0) {
+ /* disable VFs */
+ ret = pdev->driver->sriov_configure(pdev, 0);
+ if (ret < 0)
+ return ret;
+ return count;
+ }
+
+ /* enable VFs */
+ if (pdev->sriov->num_VFs) {
+ dev_warn(&pdev->dev, "%d VFs already enabled. Disable before enabling %d VFs\n",
+ pdev->sriov->num_VFs, num_vfs);
+ return -EBUSY;
}
- if (*buf == '1') {
- pdev->subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI;
- dev_warn(&pdev->dev, "forced subordinate bus to support MSI,"
- " bad things could happen.\n");
+ ret = pdev->driver->sriov_configure(pdev, num_vfs);
+ if (ret < 0)
+ return ret;
+
+ if (ret != num_vfs)
+ dev_warn(&pdev->dev, "%d VFs requested; only %d enabled\n",
+ num_vfs, ret);
+
+ return count;
+}
+
+static struct device_attribute sriov_totalvfs_attr = __ATTR_RO(sriov_totalvfs);
+static struct device_attribute sriov_numvfs_attr =
+ __ATTR(sriov_numvfs, (S_IRUGO|S_IWUSR|S_IWGRP),
+ sriov_numvfs_show, sriov_numvfs_store);
+#endif /* CONFIG_PCI_IOV */
+
+static ssize_t driver_override_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ char *driver_override, *old = pdev->driver_override, *cp;
+
+ if (count > PATH_MAX)
+ return -EINVAL;
+
+ driver_override = kstrndup(buf, count, GFP_KERNEL);
+ if (!driver_override)
+ return -ENOMEM;
+
+ cp = strchr(driver_override, '\n');
+ if (cp)
+ *cp = '\0';
+
+ if (strlen(driver_override)) {
+ pdev->driver_override = driver_override;
+ } else {
+ kfree(driver_override);
+ pdev->driver_override = NULL;
}
+ kfree(old);
+
return count;
}
-struct device_attribute pci_dev_attrs[] = {
- __ATTR_RO(resource),
- __ATTR_RO(vendor),
- __ATTR_RO(device),
- __ATTR_RO(subsystem_vendor),
- __ATTR_RO(subsystem_device),
- __ATTR_RO(class),
- __ATTR_RO(irq),
- __ATTR_RO(local_cpus),
- __ATTR_RO(modalias),
+static ssize_t driver_override_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%s\n", pdev->driver_override);
+}
+static DEVICE_ATTR_RW(driver_override);
+
+static struct attribute *pci_dev_attrs[] = {
+ &dev_attr_resource.attr,
+ &dev_attr_vendor.attr,
+ &dev_attr_device.attr,
+ &dev_attr_subsystem_vendor.attr,
+ &dev_attr_subsystem_device.attr,
+ &dev_attr_class.attr,
+ &dev_attr_irq.attr,
+ &dev_attr_local_cpus.attr,
+ &dev_attr_local_cpulist.attr,
+ &dev_attr_modalias.attr,
#ifdef CONFIG_NUMA
- __ATTR_RO(numa_node),
+ &dev_attr_numa_node.attr,
#endif
- __ATTR(enable, 0600, is_enabled_show, is_enabled_store),
- __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),
- broken_parity_status_show,broken_parity_status_store),
- __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store),
- __ATTR_NULL,
+ &dev_attr_dma_mask_bits.attr,
+ &dev_attr_consistent_dma_mask_bits.attr,
+ &dev_attr_enabled.attr,
+ &dev_attr_broken_parity_status.attr,
+ &dev_attr_msi_bus.attr,
+#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI)
+ &dev_attr_d3cold_allowed.attr,
+#endif
+#ifdef CONFIG_OF
+ &dev_attr_devspec.attr,
+#endif
+ &dev_attr_driver_override.attr,
+ NULL,
+};
+
+static const struct attribute_group pci_dev_group = {
+ .attrs = pci_dev_attrs,
+};
+
+const struct attribute_group *pci_dev_groups[] = {
+ &pci_dev_group,
+ NULL,
+};
+
+static struct attribute *pcibus_attrs[] = {
+ &dev_attr_rescan.attr,
+ &dev_attr_cpuaffinity.attr,
+ &dev_attr_cpulistaffinity.attr,
+ NULL,
+};
+
+static const struct attribute_group pcibus_group = {
+ .attrs = pcibus_attrs,
+};
+
+const struct attribute_group *pcibus_groups[] = {
+ &pcibus_group,
+ NULL,
};
-static ssize_t
-pci_read_config(struct kobject *kobj, struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
+static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
- struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_dev *vga_dev = vga_default_device();
+
+ if (vga_dev)
+ return sprintf(buf, "%u\n", (pdev == vga_dev));
+
+ return sprintf(buf, "%u\n",
+ !!(pdev->resource[PCI_ROM_RESOURCE].flags &
+ IORESOURCE_ROM_SHADOW));
+}
+static struct device_attribute vga_attr = __ATTR_RO(boot_vga);
+
+static ssize_t pci_read_config(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct pci_dev *dev = to_pci_dev(container_of(kobj, struct device,
+ kobj));
unsigned int size = 64;
loff_t init_off = off;
- u8 *data = (u8*) buf;
+ u8 *data = (u8 *) buf;
/* Several chips lock up trying to read undefined config space */
- if (capable(CAP_SYS_ADMIN)) {
+ if (security_capable(filp->f_cred, &init_user_ns, CAP_SYS_ADMIN) == 0)
size = dev->cfg_size;
- } else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
+ else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
size = 128;
- }
if (off > size)
return 0;
@@ -237,6 +642,8 @@ pci_read_config(struct kobject *kobj, struct bin_attribute *bin_attr,
size = count;
}
+ pci_config_pm_runtime_get(dev);
+
if ((off & 1) && size) {
u8 val;
pci_user_read_config_byte(dev, off, &val);
@@ -282,17 +689,20 @@ pci_read_config(struct kobject *kobj, struct bin_attribute *bin_attr,
--size;
}
+ pci_config_pm_runtime_put(dev);
+
return count;
}
-static ssize_t
-pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
+static ssize_t pci_write_config(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
{
- struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+ struct pci_dev *dev = to_pci_dev(container_of(kobj, struct device,
+ kobj));
unsigned int size = count;
loff_t init_off = off;
- u8 *data = (u8*) buf;
+ u8 *data = (u8 *) buf;
if (off > dev->cfg_size)
return 0;
@@ -300,20 +710,22 @@ pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr,
size = dev->cfg_size - off;
count = size;
}
-
+
+ pci_config_pm_runtime_get(dev);
+
if ((off & 1) && size) {
pci_user_write_config_byte(dev, off, data[off - init_off]);
off++;
size--;
}
-
+
if ((off & 3) && size > 2) {
u16 val = data[off - init_off];
val |= (u16) data[off - init_off + 1] << 8;
- pci_user_write_config_word(dev, off, val);
- off += 2;
- size -= 2;
- }
+ pci_user_write_config_word(dev, off, val);
+ off += 2;
+ size -= 2;
+ }
while (size > 3) {
u32 val = data[off - init_off];
@@ -324,7 +736,7 @@ pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr,
off += 4;
size -= 4;
}
-
+
if (size >= 2) {
u16 val = data[off - init_off];
val |= (u16) data[off - init_off + 1] << 8;
@@ -339,13 +751,47 @@ pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr,
--size;
}
+ pci_config_pm_runtime_put(dev);
+
return count;
}
+static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct pci_dev *dev =
+ to_pci_dev(container_of(kobj, struct device, kobj));
+
+ if (off > bin_attr->size)
+ count = 0;
+ else if (count > bin_attr->size - off)
+ count = bin_attr->size - off;
+
+ return pci_read_vpd(dev, off, count, buf);
+}
+
+static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct pci_dev *dev =
+ to_pci_dev(container_of(kobj, struct device, kobj));
+
+ if (off > bin_attr->size)
+ count = 0;
+ else if (count > bin_attr->size - off)
+ count = bin_attr->size - off;
+
+ return pci_write_vpd(dev, off, count, buf);
+}
+
#ifdef HAVE_PCI_LEGACY
/**
* pci_read_legacy_io - read byte(s) from legacy I/O port space
+ * @filp: open sysfs file
* @kobj: kobject corresponding to file to read from
+ * @bin_attr: struct bin_attribute for this file
* @buf: buffer to store results
* @off: offset into legacy I/O port space
* @count: number of bytes to read
@@ -353,24 +799,25 @@ pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr,
* Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific
* callback routine (pci_legacy_read).
*/
-ssize_t
-pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
+static ssize_t pci_read_legacy_io(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
{
- struct pci_bus *bus = to_pci_bus(container_of(kobj,
- struct device,
+ struct pci_bus *bus = to_pci_bus(container_of(kobj, struct device,
kobj));
- /* Only support 1, 2 or 4 byte accesses */
- if (count != 1 && count != 2 && count != 4)
- return -EINVAL;
+ /* Only support 1, 2 or 4 byte accesses */
+ if (count != 1 && count != 2 && count != 4)
+ return -EINVAL;
- return pci_legacy_read(bus, off, (u32 *)buf, count);
+ return pci_legacy_read(bus, off, (u32 *)buf, count);
}
/**
* pci_write_legacy_io - write byte(s) to legacy I/O port space
+ * @filp: open sysfs file
* @kobj: kobject corresponding to file to read from
+ * @bin_attr: struct bin_attribute for this file
* @buf: buffer containing value to be written
* @off: offset into legacy I/O port space
* @count: number of bytes to write
@@ -378,59 +825,175 @@ pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr,
* Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific
* callback routine (pci_legacy_write).
*/
-ssize_t
-pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
+static ssize_t pci_write_legacy_io(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
{
- struct pci_bus *bus = to_pci_bus(container_of(kobj,
- struct device,
+ struct pci_bus *bus = to_pci_bus(container_of(kobj, struct device,
kobj));
- /* Only support 1, 2 or 4 byte accesses */
- if (count != 1 && count != 2 && count != 4)
- return -EINVAL;
- return pci_legacy_write(bus, off, *(u32 *)buf, count);
+ /* Only support 1, 2 or 4 byte accesses */
+ if (count != 1 && count != 2 && count != 4)
+ return -EINVAL;
+
+ return pci_legacy_write(bus, off, *(u32 *)buf, count);
}
/**
* pci_mmap_legacy_mem - map legacy PCI memory into user memory space
+ * @filp: open sysfs file
* @kobj: kobject corresponding to device to be mapped
* @attr: struct bin_attribute for this file
* @vma: struct vm_area_struct passed to mmap
*
- * Uses an arch specific callback, pci_mmap_legacy_page_range, to mmap
+ * Uses an arch specific callback, pci_mmap_legacy_mem_page_range, to mmap
* legacy memory space (first meg of bus space) into application virtual
* memory space.
*/
-int
-pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr,
- struct vm_area_struct *vma)
+static int pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ struct vm_area_struct *vma)
{
- struct pci_bus *bus = to_pci_bus(container_of(kobj,
- struct device,
+ struct pci_bus *bus = to_pci_bus(container_of(kobj, struct device,
kobj));
- return pci_mmap_legacy_page_range(bus, vma);
+ return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem);
+}
+
+/**
+ * pci_mmap_legacy_io - map legacy PCI IO into user memory space
+ * @filp: open sysfs file
+ * @kobj: kobject corresponding to device to be mapped
+ * @attr: struct bin_attribute for this file
+ * @vma: struct vm_area_struct passed to mmap
+ *
+ * Uses an arch specific callback, pci_mmap_legacy_io_page_range, to mmap
+ * legacy IO space (first meg of bus space) into application virtual
+ * memory space. Returns -ENOSYS if the operation isn't supported
+ */
+static int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ struct pci_bus *bus = to_pci_bus(container_of(kobj, struct device,
+ kobj));
+
+ return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io);
+}
+
+/**
+ * pci_adjust_legacy_attr - adjustment of legacy file attributes
+ * @b: bus to create files under
+ * @mmap_type: I/O port or memory
+ *
+ * Stub implementation. Can be overridden by arch if necessary.
+ */
+void __weak pci_adjust_legacy_attr(struct pci_bus *b,
+ enum pci_mmap_state mmap_type)
+{
+}
+
+/**
+ * pci_create_legacy_files - create legacy I/O port and memory files
+ * @b: bus to create files under
+ *
+ * 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 propagate the error to the caller
+ * as it is ok to set up the PCI bus without these files.
+ */
+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)
+ goto kzalloc_err;
+
+ sysfs_bin_attr_init(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;
+ b->legacy_io->mmap = pci_mmap_legacy_io;
+ pci_adjust_legacy_attr(b, pci_mmap_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;
+ sysfs_bin_attr_init(b->legacy_mem);
+ 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;
+ pci_adjust_legacy_attr(b, pci_mmap_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)
+{
+ if (b->legacy_io) {
+ device_remove_bin_file(&b->dev, b->legacy_io);
+ device_remove_bin_file(&b->dev, b->legacy_mem);
+ kfree(b->legacy_io); /* both are allocated here */
+ }
}
#endif /* HAVE_PCI_LEGACY */
#ifdef HAVE_PCI_MMAP
+
+int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
+ enum pci_mmap_api mmap_api)
+{
+ unsigned long nr, start, size, pci_start;
+
+ if (pci_resource_len(pdev, resno) == 0)
+ return 0;
+ nr = vma_pages(vma);
+ start = vma->vm_pgoff;
+ size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
+ pci_start = (mmap_api == PCI_MMAP_PROCFS) ?
+ pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0;
+ if (start >= pci_start && start < pci_start + size &&
+ start + nr <= pci_start + size)
+ return 1;
+ return 0;
+}
+
/**
* pci_mmap_resource - map a PCI resource into user memory space
* @kobj: kobject for mapping
* @attr: struct bin_attribute for the file being mapped
* @vma: struct vm_area_struct passed into the mmap
+ * @write_combine: 1 for write_combine mapping
*
* Use the regular PCI mapping routines to map a PCI resource into userspace.
- * FIXME: write combining? maybe automatic for prefetchable regions?
*/
-static int
-pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
- struct vm_area_struct *vma)
+static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
+ struct vm_area_struct *vma, int write_combine)
{
struct pci_dev *pdev = to_pci_dev(container_of(kobj,
struct device, kobj));
- struct resource *res = (struct resource *)attr->private;
+ struct resource *res = attr->private;
enum pci_mmap_state mmap_type;
resource_size_t start, end;
int i;
@@ -441,6 +1004,15 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
if (i >= PCI_ROM_RESOURCE)
return -ENODEV;
+ if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
+ WARN(1, "process \"%s\" tried to map 0x%08lx bytes at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n",
+ current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff,
+ pci_name(pdev), i,
+ (u64)pci_resource_start(pdev, i),
+ (u64)pci_resource_len(pdev, i));
+ 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.
@@ -449,18 +1021,95 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
vma->vm_pgoff += start >> PAGE_SHIFT;
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
- return pci_mmap_page_range(pdev, vma, mmap_type, 0);
+ if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(start))
+ return -EINVAL;
+
+ return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
+}
+
+static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ return pci_mmap_resource(kobj, attr, vma, 0);
+}
+
+static int pci_mmap_resource_wc(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ return pci_mmap_resource(kobj, attr, vma, 1);
+}
+
+static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count, bool write)
+{
+ struct pci_dev *pdev = to_pci_dev(container_of(kobj,
+ struct device, kobj));
+ struct resource *res = attr->private;
+ unsigned long port = off;
+ int i;
+
+ for (i = 0; i < PCI_ROM_RESOURCE; i++)
+ if (res == &pdev->resource[i])
+ break;
+ if (i >= PCI_ROM_RESOURCE)
+ return -ENODEV;
+
+ port += pci_resource_start(pdev, i);
+
+ if (port > pci_resource_end(pdev, i))
+ return 0;
+
+ if (port + count - 1 > pci_resource_end(pdev, i))
+ return -EINVAL;
+
+ switch (count) {
+ case 1:
+ if (write)
+ outb(*(u8 *)buf, port);
+ else
+ *(u8 *)buf = inb(port);
+ return 1;
+ case 2:
+ if (write)
+ outw(*(u16 *)buf, port);
+ else
+ *(u16 *)buf = inw(port);
+ return 2;
+ case 4:
+ if (write)
+ outl(*(u32 *)buf, port);
+ else
+ *(u32 *)buf = inl(port);
+ return 4;
+ }
+ return -EINVAL;
+}
+
+static ssize_t pci_read_resource_io(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
+{
+ return pci_resource_io(filp, kobj, attr, buf, off, count, false);
+}
+
+static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
+{
+ return pci_resource_io(filp, kobj, attr, buf, off, count, true);
}
/**
* pci_remove_resource_files - cleanup resource files
- * @dev: dev to cleanup
+ * @pdev: dev to cleanup
*
- * If we created resource files for @dev, remove them from sysfs and
+ * If we created resource files for @pdev, remove them from sysfs and
* free their resources.
*/
-static void
-pci_remove_resource_files(struct pci_dev *pdev)
+static void pci_remove_resource_files(struct pci_dev *pdev)
{
int i;
@@ -472,14 +1121,56 @@ pci_remove_resource_files(struct pci_dev *pdev)
sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
kfree(res_attr);
}
+
+ res_attr = pdev->res_attr_wc[i];
+ if (res_attr) {
+ sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
+ kfree(res_attr);
+ }
}
}
+static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
+{
+ /* allocate attribute structure, piggyback attribute name */
+ int name_len = write_combine ? 13 : 10;
+ struct bin_attribute *res_attr;
+ int retval;
+
+ res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC);
+ if (res_attr) {
+ char *res_attr_name = (char *)(res_attr + 1);
+
+ sysfs_bin_attr_init(res_attr);
+ if (write_combine) {
+ pdev->res_attr_wc[num] = res_attr;
+ sprintf(res_attr_name, "resource%d_wc", num);
+ res_attr->mmap = pci_mmap_resource_wc;
+ } else {
+ pdev->res_attr[num] = res_attr;
+ sprintf(res_attr_name, "resource%d", num);
+ res_attr->mmap = pci_mmap_resource_uc;
+ }
+ if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
+ res_attr->read = pci_read_resource_io;
+ res_attr->write = pci_write_resource_io;
+ }
+ res_attr->attr.name = res_attr_name;
+ res_attr->attr.mode = S_IRUSR | S_IWUSR;
+ res_attr->size = pci_resource_len(pdev, num);
+ res_attr->private = &pdev->resource[num];
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
+ } else
+ retval = -ENOMEM;
+
+ return retval;
+}
+
/**
* pci_create_resource_files - create resource files in sysfs for @dev
- * @dev: dev in question
+ * @pdev: dev in question
*
- * Walk the resources in @dev creating files for each resource available.
+ * Walk the resources in @pdev creating files for each resource available.
*/
static int pci_create_resource_files(struct pci_dev *pdev)
{
@@ -488,52 +1179,42 @@ static int pci_create_resource_files(struct pci_dev *pdev)
/* Expose the PCI resources from this device as files */
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
- struct bin_attribute *res_attr;
/* skip empty resources */
if (!pci_resource_len(pdev, i))
continue;
- /* allocate attribute structure, piggyback attribute name */
- res_attr = kzalloc(sizeof(*res_attr) + 10, GFP_ATOMIC);
- if (res_attr) {
- char *res_attr_name = (char *)(res_attr + 1);
-
- pdev->res_attr[i] = res_attr;
- sprintf(res_attr_name, "resource%d", i);
- res_attr->attr.name = res_attr_name;
- res_attr->attr.mode = S_IRUSR | S_IWUSR;
- res_attr->size = pci_resource_len(pdev, i);
- res_attr->mmap = pci_mmap_resource;
- res_attr->private = &pdev->resource[i];
- retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
- if (retval) {
- pci_remove_resource_files(pdev);
- return retval;
- }
- } else {
- return -ENOMEM;
+ retval = pci_create_attr(pdev, i, 0);
+ /* for prefetchable resources, create a WC mappable file */
+ if (!retval && pdev->resource[i].flags & IORESOURCE_PREFETCH)
+ retval = pci_create_attr(pdev, i, 1);
+
+ if (retval) {
+ pci_remove_resource_files(pdev);
+ return retval;
}
}
return 0;
}
#else /* !HAVE_PCI_MMAP */
-static inline int pci_create_resource_files(struct pci_dev *dev) { return 0; }
-static inline void pci_remove_resource_files(struct pci_dev *dev) { return; }
+int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; }
+void __weak pci_remove_resource_files(struct pci_dev *dev) { return; }
#endif /* HAVE_PCI_MMAP */
/**
* pci_write_rom - used to enable access to the PCI ROM display
+ * @filp: sysfs file
* @kobj: kernel object handle
+ * @bin_attr: struct bin_attribute for this file
* @buf: user input
* @off: file offset
* @count: number of byte in input
*
* writing anything except 0 enables it
*/
-static ssize_t
-pci_write_rom(struct kobject *kobj, struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
+static ssize_t pci_write_rom(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
{
struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
@@ -547,7 +1228,9 @@ pci_write_rom(struct kobject *kobj, struct bin_attribute *bin_attr,
/**
* pci_read_rom - read a PCI ROM
+ * @filp: sysfs file
* @kobj: kernel object handle
+ * @bin_attr: struct bin_attribute for this file
* @buf: where to put the data we read from the ROM
* @off: file offset
* @count: number of bytes to read
@@ -555,9 +1238,9 @@ pci_write_rom(struct kobject *kobj, struct bin_attribute *bin_attr,
* Put @count bytes starting at @off into @buf from the ROM in the PCI
* device corresponding to @kobj.
*/
-static ssize_t
-pci_read_rom(struct kobject *kobj, struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
+static ssize_t pci_read_rom(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
{
struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
void __iomem *rom;
@@ -565,21 +1248,21 @@ pci_read_rom(struct kobject *kobj, struct bin_attribute *bin_attr,
if (!pdev->rom_attr_enabled)
return -EINVAL;
-
+
rom = pci_map_rom(pdev, &size); /* size starts out as PCI window size */
- if (!rom)
- return 0;
-
+ if (!rom || !size)
+ return -EIO;
+
if (off >= size)
count = 0;
else {
if (off + count > size)
count = size - off;
-
+
memcpy_fromio(buf, rom + off, count);
}
pci_unmap_rom(pdev, rom);
-
+
return count;
}
@@ -588,7 +1271,7 @@ static struct bin_attribute pci_config_attr = {
.name = "config",
.mode = S_IRUGO | S_IWUSR,
},
- .size = 256,
+ .size = PCI_CFG_SPACE_SIZE,
.read = pci_read_config,
.write = pci_write_config,
};
@@ -598,25 +1281,89 @@ static struct bin_attribute pcie_config_attr = {
.name = "config",
.mode = S_IRUGO | S_IWUSR,
},
- .size = 4096,
+ .size = PCI_CFG_SPACE_EXP_SIZE,
.read = pci_read_config,
.write = pci_write_config,
};
-int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev)
+static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ unsigned long val;
+ ssize_t result = kstrtoul(buf, 0, &val);
+
+ if (result < 0)
+ return result;
+
+ if (val != 1)
+ return -EINVAL;
+
+ result = pci_reset_function(pdev);
+ if (result < 0)
+ return result;
+
+ return count;
+}
+
+static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_store);
+
+static int pci_create_capabilities_sysfs(struct pci_dev *dev)
+{
+ int retval;
+ struct bin_attribute *attr;
+
+ /* If the device has VPD, try to expose it in sysfs. */
+ if (dev->vpd) {
+ attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
+ if (!attr)
+ return -ENOMEM;
+
+ sysfs_bin_attr_init(attr);
+ attr->size = dev->vpd->len;
+ attr->attr.name = "vpd";
+ attr->attr.mode = S_IRUSR | S_IWUSR;
+ attr->read = read_vpd_attr;
+ attr->write = write_vpd_attr;
+ retval = sysfs_create_bin_file(&dev->dev.kobj, attr);
+ if (retval) {
+ kfree(attr);
+ return retval;
+ }
+ dev->vpd->attr = attr;
+ }
+
+ /* Active State Power Management */
+ pcie_aspm_create_sysfs_dev_files(dev);
+
+ if (!pci_probe_reset_function(dev)) {
+ retval = device_create_file(&dev->dev, &reset_attr);
+ if (retval)
+ goto error;
+ dev->reset_fn = 1;
+ }
return 0;
+
+error:
+ pcie_aspm_remove_sysfs_dev_files(dev);
+ if (dev->vpd && dev->vpd->attr) {
+ sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
+ kfree(dev->vpd->attr);
+ }
+
+ return retval;
}
-int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
{
- struct bin_attribute *rom_attr = NULL;
int retval;
+ int rom_size = 0;
+ struct bin_attribute *attr;
if (!sysfs_initialized)
return -EACCES;
- if (pdev->cfg_size < 4096)
+ if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
else
retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
@@ -625,42 +1372,53 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
retval = pci_create_resource_files(pdev);
if (retval)
- goto err_bin_file;
+ goto err_config_file;
+
+ if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+ rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+ else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
+ rom_size = 0x20000;
/* If the device has a ROM, try to expose it in sysfs. */
- if (pci_resource_len(pdev, PCI_ROM_RESOURCE) ||
- (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)) {
- rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC);
- if (rom_attr) {
- pdev->rom_attr = rom_attr;
- rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
- rom_attr->attr.name = "rom";
- rom_attr->attr.mode = S_IRUSR;
- rom_attr->read = pci_read_rom;
- rom_attr->write = pci_write_rom;
- retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
- if (retval)
- goto err_rom;
- } else {
+ if (rom_size) {
+ attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
+ if (!attr) {
retval = -ENOMEM;
goto err_resource_files;
}
+ sysfs_bin_attr_init(attr);
+ attr->size = rom_size;
+ attr->attr.name = "rom";
+ attr->attr.mode = S_IRUSR | S_IWUSR;
+ attr->read = pci_read_rom;
+ attr->write = pci_write_rom;
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);
+ if (retval) {
+ kfree(attr);
+ goto err_resource_files;
+ }
+ pdev->rom_attr = attr;
}
- /* add platform-specific attributes */
- if (pcibios_add_platform_entries(pdev))
+
+ /* add sysfs entries for various capabilities */
+ retval = pci_create_capabilities_sysfs(pdev);
+ if (retval)
goto err_rom_file;
+ pci_create_firmware_label_files(pdev);
+
return 0;
err_rom_file:
- if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
- sysfs_remove_bin_file(&pdev->dev.kobj, rom_attr);
-err_rom:
- kfree(rom_attr);
+ if (rom_size) {
+ sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+ kfree(pdev->rom_attr);
+ pdev->rom_attr = NULL;
+ }
err_resource_files:
pci_remove_resource_files(pdev);
-err_bin_file:
- if (pdev->cfg_size < 4096)
+err_config_file:
+ if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
else
sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
@@ -668,6 +1426,20 @@ err:
return retval;
}
+static void pci_remove_capabilities_sysfs(struct pci_dev *dev)
+{
+ if (dev->vpd && dev->vpd->attr) {
+ sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
+ kfree(dev->vpd->attr);
+ }
+
+ pcie_aspm_remove_sysfs_dev_files(dev);
+ if (dev->reset_fn) {
+ device_remove_file(&dev->dev, &reset_attr);
+ dev->reset_fn = 0;
+ }
+}
+
/**
* pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
* @pdev: device whose entries we should free
@@ -676,22 +1448,32 @@ err:
*/
void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
{
+ int rom_size = 0;
+
if (!sysfs_initialized)
return;
- if (pdev->cfg_size < 4096)
+ pci_remove_capabilities_sysfs(pdev);
+
+ if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
else
sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
pci_remove_resource_files(pdev);
- if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
- if (pdev->rom_attr) {
- sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
- kfree(pdev->rom_attr);
- }
+ if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+ rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+ else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
+ rom_size = 0x20000;
+
+ if (rom_size && pdev->rom_attr) {
+ sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+ kfree(pdev->rom_attr);
}
+
+ pci_remove_firmware_label_files(pdev);
+
}
static int __init pci_sysfs_init(void)
@@ -710,5 +1492,87 @@ static int __init pci_sysfs_init(void)
return 0;
}
-
late_initcall(pci_sysfs_init);
+
+static struct attribute *pci_dev_dev_attrs[] = {
+ &vga_attr.attr,
+ NULL,
+};
+
+static umode_t pci_dev_attrs_are_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (a == &vga_attr.attr)
+ if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+ return 0;
+
+ return a->mode;
+}
+
+static struct attribute *pci_dev_hp_attrs[] = {
+ &dev_remove_attr.attr,
+ &dev_rescan_attr.attr,
+ NULL,
+};
+
+static umode_t pci_dev_hp_attrs_are_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (pdev->is_virtfn)
+ return 0;
+
+ return a->mode;
+}
+
+static struct attribute_group pci_dev_hp_attr_group = {
+ .attrs = pci_dev_hp_attrs,
+ .is_visible = pci_dev_hp_attrs_are_visible,
+};
+
+#ifdef CONFIG_PCI_IOV
+static struct attribute *sriov_dev_attrs[] = {
+ &sriov_totalvfs_attr.attr,
+ &sriov_numvfs_attr.attr,
+ NULL,
+};
+
+static umode_t sriov_attrs_are_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+
+ if (!dev_is_pf(dev))
+ return 0;
+
+ return a->mode;
+}
+
+static struct attribute_group sriov_dev_attr_group = {
+ .attrs = sriov_dev_attrs,
+ .is_visible = sriov_attrs_are_visible,
+};
+#endif /* CONFIG_PCI_IOV */
+
+static struct attribute_group pci_dev_attr_group = {
+ .attrs = pci_dev_dev_attrs,
+ .is_visible = pci_dev_attrs_are_visible,
+};
+
+static const struct attribute_group *pci_dev_attr_groups[] = {
+ &pci_dev_attr_group,
+ &pci_dev_hp_attr_group,
+#ifdef CONFIG_PCI_IOV
+ &sriov_dev_attr_group,
+#endif
+ NULL,
+};
+
+struct device_type pci_dev_type = {
+ .groups = pci_dev_attr_groups,
+};
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index a4445b7210b..1c8592b0e14 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,
@@ -14,14 +12,56 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pm.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/log2.h>
-#include <asm/dma.h> /* isa_dma_bridge_buggy */
+#include <linux/pci-aspm.h>
+#include <linux/pm_wakeup.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pci_hotplug.h>
+#include <asm-generic/pci-bridge.h>
+#include <asm/setup.h>
#include "pci.h"
-unsigned int pci_pm_d3_delay = 10;
+const char *pci_power_names[] = {
+ "error", "D0", "D1", "D2", "D3hot", "D3cold", "unknown",
+};
+EXPORT_SYMBOL_GPL(pci_power_names);
+
+int isa_dma_bridge_buggy;
+EXPORT_SYMBOL(isa_dma_bridge_buggy);
+
+int pci_pci_problems;
+EXPORT_SYMBOL(pci_pci_problems);
+
+unsigned int pci_pm_d3_delay;
+
+static void pci_pme_list_scan(struct work_struct *work);
+
+static LIST_HEAD(pci_pme_list);
+static DEFINE_MUTEX(pci_pme_list_mutex);
+static DECLARE_DELAYED_WORK(pci_pme_work, pci_pme_list_scan);
+
+struct pci_pme_device {
+ struct list_head list;
+ struct pci_dev *dev;
+};
+
+#define PME_TIMEOUT 1000 /* How long between PME checks */
+
+static void pci_dev_d3_sleep(struct pci_dev *dev)
+{
+ unsigned int delay = dev->d3_delay;
+
+ if (delay < pci_pm_d3_delay)
+ delay = pci_pm_d3_delay;
+
+ msleep(delay);
+}
#ifdef CONFIG_PCI_DOMAINS
int pci_domains_supported = 1;
@@ -33,6 +73,32 @@ int pci_domains_supported = 1;
unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE;
unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE;
+#define DEFAULT_HOTPLUG_IO_SIZE (256)
+#define DEFAULT_HOTPLUG_MEM_SIZE (2*1024*1024)
+/* pci=hpmemsize=nnM,hpiosize=nn can override this */
+unsigned long pci_hotplug_io_size = DEFAULT_HOTPLUG_IO_SIZE;
+unsigned long pci_hotplug_mem_size = DEFAULT_HOTPLUG_MEM_SIZE;
+
+enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_TUNE_OFF;
+
+/*
+ * The default CLS is used if arch didn't set CLS explicitly and not
+ * all pci devices agree on the same value. Arch can override either
+ * the dfl or actual value as it sees fit. Don't forget this is
+ * measured in 32-bit words, not bytes.
+ */
+u8 pci_dfl_cache_line_size = L1_CACHE_BYTES >> 2;
+u8 pci_cache_line_size;
+
+/*
+ * If we set up a device for bus mastering, we need to check the latency
+ * timer as certain BIOSes forget to set it properly.
+ */
+unsigned int pcibios_max_latency = 255;
+
+/* If set, the PCIe ARI capability will not be used. */
+static bool pcie_ari_disabled;
+
/**
* pci_bus_max_busnr - returns maximum PCI bus number of given bus' children
* @bus: pointer to PCI bus structure to search
@@ -40,44 +106,36 @@ unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE;
* Given a PCI bus, returns the highest PCI bus number present in the set
* including the given PCI bus and its list of child PCI buses.
*/
-unsigned char pci_bus_max_busnr(struct pci_bus* bus)
+unsigned char pci_bus_max_busnr(struct pci_bus *bus)
{
- struct list_head *tmp;
+ struct pci_bus *tmp;
unsigned char max, n;
- max = bus->subordinate;
- list_for_each(tmp, &bus->children) {
- n = pci_bus_max_busnr(pci_bus_b(tmp));
- if(n > max)
+ max = bus->busn_res.end;
+ list_for_each_entry(tmp, &bus->children, node) {
+ n = pci_bus_max_busnr(tmp);
+ if (n > max)
max = n;
}
return max;
}
EXPORT_SYMBOL_GPL(pci_bus_max_busnr);
-#if 0
-/**
- * pci_max_busnr - returns maximum PCI bus number
- *
- * Returns the highest PCI bus number present in the system global list of
- * PCI buses.
- */
-unsigned char __devinit
-pci_max_busnr(void)
+#ifdef CONFIG_HAS_IOMEM
+void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar)
{
- struct pci_bus *bus = NULL;
- unsigned char max, n;
-
- max = 0;
- while ((bus = pci_find_next_bus(bus)) != NULL) {
- n = pci_bus_max_busnr(bus);
- if(n > max)
- max = n;
+ /*
+ * Make sure the BAR is actually a memory resource, not an IO resource
+ */
+ if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
+ WARN_ON(1);
+ return NULL;
}
- return max;
+ return ioremap_nocache(pci_resource_start(pdev, bar),
+ pci_resource_len(pdev, bar));
}
-
-#endif /* 0 */
+EXPORT_SYMBOL_GPL(pci_ioremap_bar);
+#endif
#define PCI_FIND_CAP_TTL 48
@@ -140,7 +198,7 @@ static int __pci_bus_find_cap_start(struct pci_bus *bus,
}
/**
- * pci_find_capability - query for devices' capabilities
+ * pci_find_capability - query for devices' capabilities
* @dev: PCI device to query
* @cap: capability code
*
@@ -149,12 +207,12 @@ static int __pci_bus_find_cap_start(struct pci_bus *bus,
* device's PCI configuration space or 0 in case the device does not
* support it. Possible values for @cap:
*
- * %PCI_CAP_ID_PM Power Management
- * %PCI_CAP_ID_AGP Accelerated Graphics Port
- * %PCI_CAP_ID_VPD Vital Product Data
- * %PCI_CAP_ID_SLOTID Slot Identification
+ * %PCI_CAP_ID_PM Power Management
+ * %PCI_CAP_ID_AGP Accelerated Graphics Port
+ * %PCI_CAP_ID_VPD Vital Product Data
+ * %PCI_CAP_ID_SLOTID Slot Identification
* %PCI_CAP_ID_MSI Message Signalled Interrupts
- * %PCI_CAP_ID_CHSWP CompactPCI HotSwap
+ * %PCI_CAP_ID_CHSWP CompactPCI HotSwap
* %PCI_CAP_ID_PCIX PCI-X
* %PCI_CAP_ID_EXP PCI Express
*/
@@ -168,15 +226,16 @@ int pci_find_capability(struct pci_dev *dev, int cap)
return pos;
}
+EXPORT_SYMBOL(pci_find_capability);
/**
- * pci_bus_find_capability - query for devices' capabilities
+ * pci_bus_find_capability - query for devices' capabilities
* @bus: the PCI bus to query
* @devfn: PCI device to query
* @cap: capability code
*
* Like pci_find_capability() but works for pci devices that do not have a
- * pci_dev structure set up yet.
+ * pci_dev structure set up yet.
*
* Returns the address of the requested capability structure within the
* device's PCI configuration space or 0 in case the device does not
@@ -195,30 +254,34 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap)
return pos;
}
+EXPORT_SYMBOL(pci_bus_find_capability);
/**
- * pci_find_ext_capability - Find an extended capability
+ * pci_find_next_ext_capability - Find an extended capability
* @dev: PCI device to query
+ * @start: address at which to start looking (0 to start at beginning of list)
* @cap: capability code
*
- * Returns the address of the requested extended capability structure
+ * Returns the address of the next matching extended capability structure
* within the device's PCI configuration space or 0 if the device does
- * not support it. Possible values for @cap:
- *
- * %PCI_EXT_CAP_ID_ERR Advanced Error Reporting
- * %PCI_EXT_CAP_ID_VC Virtual Channel
- * %PCI_EXT_CAP_ID_DSN Device Serial Number
- * %PCI_EXT_CAP_ID_PWR Power Budgeting
+ * not support it. Some capabilities can occur several times, e.g., the
+ * vendor-specific capability, and this provides a way to find them all.
*/
-int pci_find_ext_capability(struct pci_dev *dev, int cap)
+int pci_find_next_ext_capability(struct pci_dev *dev, int start, int cap)
{
u32 header;
- int ttl = 480; /* 3840 bytes, minimum 8 bytes per capability */
- int pos = 0x100;
+ int ttl;
+ int pos = PCI_CFG_SPACE_SIZE;
+
+ /* minimum 8 bytes per capability */
+ ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
- if (dev->cfg_size <= 256)
+ if (dev->cfg_size <= PCI_CFG_SPACE_SIZE)
return 0;
+ if (start)
+ pos = start;
+
if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
return 0;
@@ -230,11 +293,11 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap)
return 0;
while (ttl-- > 0) {
- if (PCI_EXT_CAP_ID(header) == cap)
+ if (PCI_EXT_CAP_ID(header) == cap && pos != start)
return pos;
pos = PCI_EXT_CAP_NEXT(header);
- if (pos < 0x100)
+ if (pos < PCI_CFG_SPACE_SIZE)
break;
if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
@@ -243,6 +306,26 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap)
return 0;
}
+EXPORT_SYMBOL_GPL(pci_find_next_ext_capability);
+
+/**
+ * pci_find_ext_capability - Find an extended capability
+ * @dev: PCI device to query
+ * @cap: capability code
+ *
+ * Returns the address of the requested extended capability structure
+ * within the device's PCI configuration space or 0 if the device does
+ * not support it. Possible values for @cap:
+ *
+ * %PCI_EXT_CAP_ID_ERR Advanced Error Reporting
+ * %PCI_EXT_CAP_ID_VC Virtual Channel
+ * %PCI_EXT_CAP_ID_DSN Device Serial Number
+ * %PCI_EXT_CAP_ID_PWR Power Budgeting
+ */
+int pci_find_ext_capability(struct pci_dev *dev, int cap)
+{
+ return pci_find_next_ext_capability(dev, 0, cap);
+}
EXPORT_SYMBOL_GPL(pci_find_ext_capability);
static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap)
@@ -320,30 +403,67 @@ EXPORT_SYMBOL_GPL(pci_find_ht_capability);
* @res: child resource record for which parent is sought
*
* For given resource region of given device, return the resource
- * region of parent bus the given region is contained in or where
- * it should be allocated from.
+ * region of parent bus the given region is contained in.
*/
-struct resource *
-pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
+struct resource *pci_find_parent_resource(const struct pci_dev *dev,
+ struct resource *res)
{
const struct pci_bus *bus = dev->bus;
+ struct resource *r;
int i;
- struct resource *best = NULL;
- for(i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
- struct resource *r = bus->resource[i];
+ pci_bus_for_each_resource(bus, r, i) {
if (!r)
continue;
- if (res->start && !(res->start >= r->start && res->end <= r->end))
- continue; /* Not contained */
- if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM))
- continue; /* Wrong type */
- if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH))
- return r; /* Exact match */
- if ((res->flags & IORESOURCE_PREFETCH) && !(r->flags & IORESOURCE_PREFETCH))
- best = r; /* Approximating prefetchable by non-prefetchable */
+ if (res->start && resource_contains(r, res)) {
+
+ /*
+ * If the window is prefetchable but the BAR is
+ * not, the allocator made a mistake.
+ */
+ if (r->flags & IORESOURCE_PREFETCH &&
+ !(res->flags & IORESOURCE_PREFETCH))
+ return NULL;
+
+ /*
+ * If we're below a transparent bridge, there may
+ * be both a positively-decoded aperture and a
+ * subtractively-decoded region that contain the BAR.
+ * We want the positively-decoded one, so this depends
+ * on pci_bus_for_each_resource() giving us those
+ * first.
+ */
+ return r;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(pci_find_parent_resource);
+
+/**
+ * pci_wait_for_pending - wait for @mask bit(s) to clear in status word @pos
+ * @dev: the PCI device to operate on
+ * @pos: config space offset of status word
+ * @mask: mask of bit(s) to care about in status word
+ *
+ * Return 1 when mask bit(s) in status word clear, 0 otherwise.
+ */
+int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask)
+{
+ int i;
+
+ /* Wait for Transaction Pending bit clean */
+ for (i = 0; i < 4; i++) {
+ u16 status;
+ if (i)
+ msleep((1 << (i - 1)) * 100);
+
+ pci_read_config_word(dev, pos, &status);
+ if (!(status & mask))
+ return 1;
}
- return best;
+
+ return 0;
}
/**
@@ -353,98 +473,99 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
* Restore the BAR values for a given device, so as to make it
* accessible by its driver.
*/
-static void
-pci_restore_bars(struct pci_dev *dev)
+static void pci_restore_bars(struct pci_dev *dev)
{
- int i, numres;
+ int i;
- switch (dev->hdr_type) {
- case PCI_HEADER_TYPE_NORMAL:
- numres = 6;
- break;
- case PCI_HEADER_TYPE_BRIDGE:
- numres = 2;
- break;
- case PCI_HEADER_TYPE_CARDBUS:
- numres = 1;
- break;
- default:
- /* Should never get here, but just in case... */
- return;
- }
+ for (i = 0; i < PCI_BRIDGE_RESOURCES; i++)
+ pci_update_resource(dev, i);
+}
+
+static struct pci_platform_pm_ops *pci_platform_pm;
+
+int pci_set_platform_pm(struct pci_platform_pm_ops *ops)
+{
+ if (!ops->is_manageable || !ops->set_state || !ops->choose_state
+ || !ops->sleep_wake)
+ return -EINVAL;
+ pci_platform_pm = ops;
+ return 0;
+}
- for (i = 0; i < numres; i ++)
- pci_update_resource(dev, &dev->resource[i], i);
+static inline bool platform_pci_power_manageable(struct pci_dev *dev)
+{
+ return pci_platform_pm ? pci_platform_pm->is_manageable(dev) : false;
}
-int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t t);
+static inline int platform_pci_set_power_state(struct pci_dev *dev,
+ pci_power_t t)
+{
+ return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS;
+}
+
+static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev)
+{
+ return pci_platform_pm ?
+ pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR;
+}
+
+static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable)
+{
+ return pci_platform_pm ?
+ pci_platform_pm->sleep_wake(dev, enable) : -ENODEV;
+}
+
+static inline int platform_pci_run_wake(struct pci_dev *dev, bool enable)
+{
+ return pci_platform_pm ?
+ pci_platform_pm->run_wake(dev, enable) : -ENODEV;
+}
/**
- * pci_set_power_state - Set the power state of a PCI device
- * @dev: PCI device to be suspended
- * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering
- *
- * Transition a device to a new power state, using the Power Management
- * Capabilities in the device's config space.
+ * 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 trying to enter a lower state than we're already in.
- * 0 if we're already in the requested state.
- * -EIO if device does not support PCI PM.
- * 0 if we can successfully change the power state.
+ * RETURN VALUE:
+ * -EINVAL if the requested state is invalid.
+ * -EIO if device does not support PCI PM or its PM capabilities register has a
+ * wrong version, or device doesn't support the requested state.
+ * 0 if device already is in the requested state.
+ * 0 if device's power state has been successfully changed.
*/
-int
-pci_set_power_state(struct pci_dev *dev, pci_power_t state)
+static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
{
- int pm, need_restore = 0;
- u16 pmcsr, pmc;
+ u16 pmcsr;
+ bool need_restore = false;
- /* bound the state we're entering */
- if (state > PCI_D3hot)
- state = PCI_D3hot;
-
- /*
- * If the device or the parent bridge can't support PCI PM, ignore
- * the request if we're doing anything besides putting it into D0
- * (which would only happen on boot).
- */
- if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
+ /* Check if we're already there */
+ if (dev->current_state == state)
return 0;
- /* find PCI PM capability in list */
- pm = pci_find_capability(dev, PCI_CAP_ID_PM);
-
- /* abort if the device doesn't support PM capabilities */
- if (!pm)
+ if (!dev->pm_cap)
return -EIO;
+ if (state < PCI_D0 || state > PCI_D3hot)
+ return -EINVAL;
+
/* Validate current state:
- * Can enter D0 from any state, but if we can only go deeper
+ * 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",
- __FUNCTION__, pci_name(dev), state, dev->current_state);
+ 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
@@ -457,10 +578,12 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
pmcsr |= state;
break;
+ case PCI_D3hot:
+ case PCI_D3cold:
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;
@@ -468,25 +591,23 @@ 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 */
if (state == PCI_D3hot || dev->current_state == PCI_D3hot)
- msleep(pci_pm_d3_delay);
+ pci_dev_d3_sleep(dev);
else if (state == PCI_D2 || dev->current_state == PCI_D2)
- udelay(200);
+ udelay(PCI_PM_D2_DELAY);
- /*
- * 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);
+ pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
+ dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
+ if (dev->current_state != state && printk_ratelimit())
+ dev_info(&dev->dev, "Refused to change power state, currently in D%d\n",
+ dev->current_state);
- dev->current_state = state;
-
- /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
+ /*
+ * According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
* INTERFACE SPECIFICATION, REV. 1.2", a device transitioning
* from D3hot to D0 _may_ perform an internal reset, thereby
* going to "D0 Uninitialized" rather than "D0 Initialized".
@@ -501,11 +622,234 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
if (need_restore)
pci_restore_bars(dev);
+ if (dev->bus->self)
+ pcie_aspm_pm_state_change(dev->bus->self);
+
+ return 0;
+}
+
+/**
+ * pci_update_current_state - Read PCI power state of given device from its
+ * PCI PM registers and cache it
+ * @dev: PCI device to handle.
+ * @state: State to cache in case the device doesn't have the PM capability
+ */
+void pci_update_current_state(struct pci_dev *dev, pci_power_t state)
+{
+ if (dev->pm_cap) {
+ u16 pmcsr;
+
+ /*
+ * Configuration space is not accessible for device in
+ * D3cold, so just keep or set D3cold for safety
+ */
+ if (dev->current_state == PCI_D3cold)
+ return;
+ if (state == PCI_D3cold) {
+ dev->current_state = PCI_D3cold;
+ return;
+ }
+ pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
+ dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
+ } else {
+ dev->current_state = state;
+ }
+}
+
+/**
+ * pci_power_up - Put the given device into D0 forcibly
+ * @dev: PCI device to power up
+ */
+void pci_power_up(struct pci_dev *dev)
+{
+ if (platform_pci_power_manageable(dev))
+ platform_pci_set_power_state(dev, PCI_D0);
+
+ pci_raw_set_power_state(dev, PCI_D0);
+ pci_update_current_state(dev, PCI_D0);
+}
+
+/**
+ * pci_platform_power_transition - Use platform to change device power state
+ * @dev: PCI device to handle.
+ * @state: State to put the device into.
+ */
+static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state)
+{
+ int error;
+
+ if (platform_pci_power_manageable(dev)) {
+ error = platform_pci_set_power_state(dev, state);
+ if (!error)
+ pci_update_current_state(dev, state);
+ } else
+ error = -ENODEV;
+
+ if (error && !dev->pm_cap) /* Fall back to PCI_D0 */
+ dev->current_state = PCI_D0;
+
+ return error;
+}
+
+/**
+ * pci_wakeup - Wake up a PCI device
+ * @pci_dev: Device to handle.
+ * @ign: ignored parameter
+ */
+static int pci_wakeup(struct pci_dev *pci_dev, void *ign)
+{
+ pci_wakeup_event(pci_dev);
+ pm_request_resume(&pci_dev->dev);
return 0;
}
-pci_power_t (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
-
+/**
+ * pci_wakeup_bus - Walk given bus and wake up devices on it
+ * @bus: Top bus of the subtree to walk.
+ */
+static void pci_wakeup_bus(struct pci_bus *bus)
+{
+ if (bus)
+ pci_walk_bus(bus, pci_wakeup, NULL);
+}
+
+/**
+ * __pci_start_power_transition - Start power transition of a PCI device
+ * @dev: PCI device to handle.
+ * @state: State to put the device into.
+ */
+static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state)
+{
+ if (state == PCI_D0) {
+ pci_platform_power_transition(dev, PCI_D0);
+ /*
+ * Mandatory power management transition delays, see
+ * PCI Express Base Specification Revision 2.0 Section
+ * 6.6.1: Conventional Reset. Do not delay for
+ * devices powered on/off by corresponding bridge,
+ * because have already delayed for the bridge.
+ */
+ if (dev->runtime_d3cold) {
+ msleep(dev->d3cold_delay);
+ /*
+ * When powering on a bridge from D3cold, the
+ * whole hierarchy may be powered on into
+ * D0uninitialized state, resume them to give
+ * them a chance to suspend again
+ */
+ pci_wakeup_bus(dev->subordinate);
+ }
+ }
+}
+
+/**
+ * __pci_dev_set_current_state - Set current state of a PCI device
+ * @dev: Device to handle
+ * @data: pointer to state to be set
+ */
+static int __pci_dev_set_current_state(struct pci_dev *dev, void *data)
+{
+ pci_power_t state = *(pci_power_t *)data;
+
+ dev->current_state = state;
+ return 0;
+}
+
+/**
+ * __pci_bus_set_current_state - Walk given bus and set current state of devices
+ * @bus: Top bus of the subtree to walk.
+ * @state: state to be set
+ */
+static void __pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state)
+{
+ if (bus)
+ pci_walk_bus(bus, __pci_dev_set_current_state, &state);
+}
+
+/**
+ * __pci_complete_power_transition - Complete power transition of a PCI device
+ * @dev: PCI device to handle.
+ * @state: State to put the device into.
+ *
+ * This function should not be called directly by device drivers.
+ */
+int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state)
+{
+ int ret;
+
+ if (state <= PCI_D0)
+ return -EINVAL;
+ ret = pci_platform_power_transition(dev, state);
+ /* Power off the bridge may power off the whole hierarchy */
+ if (!ret && state == PCI_D3cold)
+ __pci_bus_set_current_state(dev->subordinate, PCI_D3cold);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__pci_complete_power_transition);
+
+/**
+ * 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 firmware 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_D3cold)
+ state = PCI_D3cold;
+ 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;
+
+ /* Check if we're already there */
+ if (dev->current_state == state)
+ return 0;
+
+ __pci_start_power_transition(dev, state);
+
+ /* 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;
+
+ /*
+ * To put device in D3cold, we put device into D3hot in native
+ * way, then put device into D3cold with platform ops
+ */
+ error = pci_raw_set_power_state(dev, state > PCI_D3hot ?
+ PCI_D3hot : state);
+
+ if (!__pci_complete_power_transition(dev, state))
+ error = 0;
+ /*
+ * When aspm_policy is "powersave" this call ensures
+ * that ASPM is configured.
+ */
+ if (!error && dev->bus->self)
+ pcie_aspm_powersave_config_link(dev->bus->self);
+
+ return error;
+}
+EXPORT_SYMBOL(pci_set_power_state);
+
/**
* pci_choose_state - Choose the power state of a PCI device
* @dev: PCI device to be suspended
@@ -520,14 +864,12 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
{
pci_power_t ret;
- if (!pci_find_capability(dev, PCI_CAP_ID_PM))
+ if (!dev->pm_cap)
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:
@@ -539,91 +881,104 @@ 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;
}
-
EXPORT_SYMBOL(pci_choose_state);
+#define PCI_EXP_SAVE_REGS 7
+
+static struct pci_cap_saved_state *_pci_find_saved_cap(struct pci_dev *pci_dev,
+ u16 cap, bool extended)
+{
+ struct pci_cap_saved_state *tmp;
+
+ hlist_for_each_entry(tmp, &pci_dev->saved_cap_space, next) {
+ if (tmp->cap.cap_extended == extended && tmp->cap.cap_nr == cap)
+ return tmp;
+ }
+ return NULL;
+}
+
+struct pci_cap_saved_state *pci_find_saved_cap(struct pci_dev *dev, char cap)
+{
+ return _pci_find_saved_cap(dev, cap, false);
+}
+
+struct pci_cap_saved_state *pci_find_saved_ext_cap(struct pci_dev *dev, u16 cap)
+{
+ return _pci_find_saved_cap(dev, cap, true);
+}
+
static int pci_save_pcie_state(struct pci_dev *dev)
{
- int pos, i = 0;
+ int i = 0;
struct pci_cap_saved_state *save_state;
u16 *cap;
- int found = 0;
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (pos <= 0)
+ if (!pci_is_pcie(dev))
return 0;
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
- if (!save_state)
- save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL);
- else
- found = 1;
if (!save_state) {
- dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+ dev_err(&dev->dev, "buffer not found in %s\n", __func__);
return -ENOMEM;
}
- cap = (u16 *)&save_state->data[0];
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]);
- save_state->cap_nr = PCI_CAP_ID_EXP;
- if (!found)
- pci_add_saved_cap(dev, save_state);
+ cap = (u16 *)&save_state->cap.data[0];
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_SLTCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_RTCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_SLTCTL2, &cap[i++]);
+
return 0;
}
static void pci_restore_pcie_state(struct pci_dev *dev)
{
- int i = 0, pos;
+ int i = 0;
struct pci_cap_saved_state *save_state;
u16 *cap;
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!save_state || pos <= 0)
+ if (!save_state)
return;
- cap = (u16 *)&save_state->data[0];
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]);
+ cap = (u16 *)&save_state->cap.data[0];
+ pcie_capability_write_word(dev, PCI_EXP_DEVCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_LNKCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_SLTCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_RTCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_DEVCTL2, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_SLTCTL2, cap[i++]);
}
static int pci_save_pcix_state(struct pci_dev *dev)
{
- int pos, i = 0;
+ int pos;
struct pci_cap_saved_state *save_state;
- u16 *cap;
- int found = 0;
pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (pos <= 0)
return 0;
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_PCIX);
- if (!save_state)
- save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL);
- else
- found = 1;
if (!save_state) {
- dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+ dev_err(&dev->dev, "buffer not found in %s\n", __func__);
return -ENOMEM;
}
- cap = (u16 *)&save_state->data[0];
- pci_read_config_word(dev, pos + PCI_X_CMD, &cap[i++]);
- save_state->cap_nr = PCI_CAP_ID_PCIX;
- if (!found)
- pci_add_saved_cap(dev, save_state);
+ pci_read_config_word(dev, pos + PCI_X_CMD,
+ (u16 *)save_state->cap.data);
+
return 0;
}
@@ -637,7 +992,7 @@ static void pci_restore_pcix_state(struct pci_dev *dev)
pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!save_state || pos <= 0)
return;
- cap = (u16 *)&save_state->data[0];
+ cap = (u16 *)&save_state->cap.data[0];
pci_write_config_word(dev, pos + PCI_X_CMD, cap[i++]);
}
@@ -647,57 +1002,201 @@ static void pci_restore_pcix_state(struct pci_dev *dev)
* pci_save_state - save the PCI configuration space of a device before suspending
* @dev: - PCI device that we're dealing with
*/
-int
-pci_save_state(struct pci_dev *dev)
+int pci_save_state(struct pci_dev *dev)
{
int i;
/* XXX: 100% dword access ok here? */
for (i = 0; i < 16; i++)
- pci_read_config_dword(dev, i * 4,&dev->saved_config_space[i]);
+ pci_read_config_dword(dev, i * 4, &dev->saved_config_space[i]);
+ dev->state_saved = true;
if ((i = pci_save_pcie_state(dev)) != 0)
return i;
if ((i = pci_save_pcix_state(dev)) != 0)
return i;
+ if ((i = pci_save_vc_state(dev)) != 0)
+ return i;
return 0;
}
+EXPORT_SYMBOL(pci_save_state);
+
+static void pci_restore_config_dword(struct pci_dev *pdev, int offset,
+ u32 saved_val, int retry)
+{
+ u32 val;
-/**
+ pci_read_config_dword(pdev, offset, &val);
+ if (val == saved_val)
+ return;
+
+ for (;;) {
+ dev_dbg(&pdev->dev, "restoring config space at offset %#x (was %#x, writing %#x)\n",
+ offset, val, saved_val);
+ pci_write_config_dword(pdev, offset, saved_val);
+ if (retry-- <= 0)
+ return;
+
+ pci_read_config_dword(pdev, offset, &val);
+ if (val == saved_val)
+ return;
+
+ mdelay(1);
+ }
+}
+
+static void pci_restore_config_space_range(struct pci_dev *pdev,
+ int start, int end, int retry)
+{
+ int index;
+
+ for (index = end; index >= start; index--)
+ pci_restore_config_dword(pdev, 4 * index,
+ pdev->saved_config_space[index],
+ retry);
+}
+
+static void pci_restore_config_space(struct pci_dev *pdev)
+{
+ if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
+ pci_restore_config_space_range(pdev, 10, 15, 0);
+ /* Restore BARs before the command register. */
+ pci_restore_config_space_range(pdev, 4, 9, 10);
+ pci_restore_config_space_range(pdev, 0, 3, 0);
+ } else {
+ pci_restore_config_space_range(pdev, 0, 15, 0);
+ }
+}
+
+/**
* pci_restore_state - Restore the saved state of a PCI device
* @dev: - PCI device that we're dealing with
*/
-int
-pci_restore_state(struct pci_dev *dev)
+void pci_restore_state(struct pci_dev *dev)
{
- int i;
- u32 val;
+ if (!dev->state_saved)
+ return;
/* PCI Express register must be restored first */
pci_restore_pcie_state(dev);
+ pci_restore_ats_state(dev);
+ pci_restore_vc_state(dev);
+
+ pci_restore_config_space(dev);
- /*
- * The Base Address register should be programmed before the command
- * register(s)
- */
- 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]);
- pci_write_config_dword(dev,i * 4,
- dev->saved_config_space[i]);
- }
- }
pci_restore_pcix_state(dev);
pci_restore_msi_state(dev);
+ pci_restore_iov_state(dev);
+
+ dev->state_saved = false;
+}
+EXPORT_SYMBOL(pci_restore_state);
+
+struct pci_saved_state {
+ u32 config_space[16];
+ struct pci_cap_saved_data cap[0];
+};
+
+/**
+ * pci_store_saved_state - Allocate and return an opaque struct containing
+ * the device saved state.
+ * @dev: PCI device that we're dealing with
+ *
+ * Return NULL if no state or error.
+ */
+struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev)
+{
+ struct pci_saved_state *state;
+ struct pci_cap_saved_state *tmp;
+ struct pci_cap_saved_data *cap;
+ size_t size;
+
+ if (!dev->state_saved)
+ return NULL;
+
+ size = sizeof(*state) + sizeof(struct pci_cap_saved_data);
+
+ hlist_for_each_entry(tmp, &dev->saved_cap_space, next)
+ size += sizeof(struct pci_cap_saved_data) + tmp->cap.size;
+
+ state = kzalloc(size, GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ memcpy(state->config_space, dev->saved_config_space,
+ sizeof(state->config_space));
+
+ cap = state->cap;
+ hlist_for_each_entry(tmp, &dev->saved_cap_space, next) {
+ size_t len = sizeof(struct pci_cap_saved_data) + tmp->cap.size;
+ memcpy(cap, &tmp->cap, len);
+ cap = (struct pci_cap_saved_data *)((u8 *)cap + len);
+ }
+ /* Empty cap_save terminates list */
+
+ return state;
+}
+EXPORT_SYMBOL_GPL(pci_store_saved_state);
+/**
+ * pci_load_saved_state - Reload the provided save state into struct pci_dev.
+ * @dev: PCI device that we're dealing with
+ * @state: Saved state returned from pci_store_saved_state()
+ */
+static int pci_load_saved_state(struct pci_dev *dev,
+ struct pci_saved_state *state)
+{
+ struct pci_cap_saved_data *cap;
+
+ dev->state_saved = false;
+
+ if (!state)
+ return 0;
+
+ memcpy(dev->saved_config_space, state->config_space,
+ sizeof(state->config_space));
+
+ cap = state->cap;
+ while (cap->size) {
+ struct pci_cap_saved_state *tmp;
+
+ tmp = _pci_find_saved_cap(dev, cap->cap_nr, cap->cap_extended);
+ if (!tmp || tmp->cap.size != cap->size)
+ return -EINVAL;
+
+ memcpy(tmp->cap.data, cap->data, tmp->cap.size);
+ cap = (struct pci_cap_saved_data *)((u8 *)cap +
+ sizeof(struct pci_cap_saved_data) + cap->size);
+ }
+
+ dev->state_saved = true;
return 0;
}
+/**
+ * pci_load_and_free_saved_state - Reload the save state pointed to by state,
+ * and free the memory allocated for it.
+ * @dev: PCI device that we're dealing with
+ * @state: Pointer to saved state returned from pci_store_saved_state()
+ */
+int pci_load_and_free_saved_state(struct pci_dev *dev,
+ struct pci_saved_state **state)
+{
+ int ret = pci_load_saved_state(dev, *state);
+ kfree(*state);
+ *state = NULL;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pci_load_and_free_saved_state);
+
+int __weak pcibios_enable_device(struct pci_dev *dev, int bars)
+{
+ return pci_enable_resources(dev, bars);
+}
+
static int do_pci_enable_device(struct pci_dev *dev, int bars)
{
int err;
+ u16 cmd;
+ u8 pin;
err = pci_set_power_state(dev, PCI_D0);
if (err < 0 && err != -EIO)
@@ -707,6 +1206,17 @@ static int do_pci_enable_device(struct pci_dev *dev, int bars)
return err;
pci_fixup_device(pci_fixup_enable, dev);
+ if (dev->msi_enabled || dev->msix_enabled)
+ return 0;
+
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+ if (pin) {
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if (cmd & PCI_COMMAND_INTX_DISABLE)
+ pci_write_config_word(dev, PCI_COMMAND,
+ cmd & ~PCI_COMMAND_INTX_DISABLE);
+ }
+
return 0;
}
@@ -719,21 +1229,64 @@ static int do_pci_enable_device(struct pci_dev *dev, int bars)
*/
int pci_reenable_device(struct pci_dev *dev)
{
- if (atomic_read(&dev->enable_cnt))
+ if (pci_is_enabled(dev))
return do_pci_enable_device(dev, (1 << PCI_NUM_RESOURCES) - 1);
return 0;
}
+EXPORT_SYMBOL(pci_reenable_device);
-static int __pci_enable_device_flags(struct pci_dev *dev,
- resource_size_t flags)
+static void pci_enable_bridge(struct pci_dev *dev)
{
+ struct pci_dev *bridge;
+ int retval;
+
+ bridge = pci_upstream_bridge(dev);
+ if (bridge)
+ pci_enable_bridge(bridge);
+
+ if (pci_is_enabled(dev)) {
+ if (!dev->is_busmaster)
+ pci_set_master(dev);
+ return;
+ }
+
+ retval = pci_enable_device(dev);
+ if (retval)
+ dev_err(&dev->dev, "Error enabling bridge (%d), continuing\n",
+ retval);
+ pci_set_master(dev);
+}
+
+static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags)
+{
+ struct pci_dev *bridge;
int err;
int i, bars = 0;
- if (atomic_add_return(1, &dev->enable_cnt) > 1)
+ /*
+ * Power state could be unknown at this point, either due to a fresh
+ * boot or a device removal call. So get the current power state
+ * so that things like MSI message writing will behave as expected
+ * (e.g. if the device really is in D0 at enable time).
+ */
+ 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);
+ }
+
+ if (atomic_inc_return(&dev->enable_cnt) > 1)
return 0; /* already enabled */
- for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
+ bridge = pci_upstream_bridge(dev);
+ if (bridge)
+ pci_enable_bridge(bridge);
+
+ /* only skip sriov related */
+ for (i = 0; i <= PCI_ROM_RESOURCE; i++)
+ if (dev->resource[i].flags & flags)
+ bars |= (1 << i);
+ for (i = PCI_BRIDGE_RESOURCES; i < DEVICE_COUNT_RESOURCE; i++)
if (dev->resource[i].flags & flags)
bars |= (1 << i);
@@ -753,8 +1306,9 @@ static int __pci_enable_device_flags(struct pci_dev *dev,
*/
int pci_enable_device_io(struct pci_dev *dev)
{
- return __pci_enable_device_flags(dev, IORESOURCE_IO);
+ return pci_enable_device_flags(dev, IORESOURCE_IO);
}
+EXPORT_SYMBOL(pci_enable_device_io);
/**
* pci_enable_device_mem - Initialize a device for use with Memory space
@@ -766,8 +1320,9 @@ int pci_enable_device_io(struct pci_dev *dev)
*/
int pci_enable_device_mem(struct pci_dev *dev)
{
- return __pci_enable_device_flags(dev, IORESOURCE_MEM);
+ return pci_enable_device_flags(dev, IORESOURCE_MEM);
}
+EXPORT_SYMBOL(pci_enable_device_mem);
/**
* pci_enable_device - Initialize device before it's used by a driver.
@@ -782,8 +1337,9 @@ int pci_enable_device_mem(struct pci_dev *dev)
*/
int pci_enable_device(struct pci_dev *dev)
{
- return __pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
+ return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
}
+EXPORT_SYMBOL(pci_enable_device);
/*
* Managed PCI resources. This manages device on/off, intx/msi/msix
@@ -821,7 +1377,7 @@ static void pcim_release(struct device *gendev, void *res)
pci_disable_device(dev);
}
-static struct pci_devres * get_pci_dr(struct pci_dev *pdev)
+static struct pci_devres *get_pci_dr(struct pci_dev *pdev)
{
struct pci_devres *dr, *new_dr;
@@ -835,7 +1391,7 @@ static struct pci_devres * get_pci_dr(struct pci_dev *pdev)
return devres_get(&pdev->dev, new_dr, NULL, NULL);
}
-static struct pci_devres * find_pci_dr(struct pci_dev *pdev)
+static struct pci_devres *find_pci_dr(struct pci_dev *pdev)
{
if (pci_is_managed(pdev))
return devres_find(&pdev->dev, pcim_release, NULL, NULL);
@@ -866,6 +1422,7 @@ int pcim_enable_device(struct pci_dev *pdev)
}
return rc;
}
+EXPORT_SYMBOL(pcim_enable_device);
/**
* pcim_pin_device - Pin managed PCI device
@@ -884,6 +1441,30 @@ void pcim_pin_device(struct pci_dev *pdev)
if (dr)
dr->pinned = 1;
}
+EXPORT_SYMBOL(pcim_pin_device);
+
+/*
+ * pcibios_add_device - provide arch specific hooks when adding device dev
+ * @dev: the PCI device being added
+ *
+ * Permits the platform to provide architecture specific functionality when
+ * devices are added. This is the default implementation. Architecture
+ * implementations can override this.
+ */
+int __weak pcibios_add_device(struct pci_dev *dev)
+{
+ return 0;
+}
+
+/**
+ * pcibios_release_device - provide arch specific hooks when releasing device dev
+ * @dev: the PCI device being released
+ *
+ * Permits the platform to provide architecture specific functionality when
+ * devices are released. This is the default implementation. Architecture
+ * implementations can override this.
+ */
+void __weak pcibios_release_device(struct pci_dev *dev) {}
/**
* pcibios_disable_device - disable arch specific PCI resources for device dev
@@ -893,7 +1474,44 @@ void pcim_pin_device(struct pci_dev *pdev)
* is the default implementation. Architecture implementations can
* override this.
*/
-void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {}
+void __weak pcibios_disable_device (struct pci_dev *dev) {}
+
+/**
+ * pcibios_penalize_isa_irq - penalize an ISA IRQ
+ * @irq: ISA IRQ to penalize
+ * @active: IRQ active or not
+ *
+ * Permits the platform to provide architecture-specific functionality when
+ * penalizing ISA IRQs. This is the default implementation. Architecture
+ * implementations can override this.
+ */
+void __weak pcibios_penalize_isa_irq(int irq, int active) {}
+
+static void do_pci_disable_device(struct pci_dev *dev)
+{
+ u16 pci_command;
+
+ pci_read_config_word(dev, PCI_COMMAND, &pci_command);
+ if (pci_command & PCI_COMMAND_MASTER) {
+ pci_command &= ~PCI_COMMAND_MASTER;
+ pci_write_config_word(dev, PCI_COMMAND, pci_command);
+ }
+
+ pcibios_disable_device(dev);
+}
+
+/**
+ * pci_disable_enabled_device - Disable device without updating enable_cnt
+ * @dev: PCI device to disable
+ *
+ * NOTE: This function is a backend of PCI power management routines and is
+ * not supposed to be called drivers.
+ */
+void pci_disable_enabled_device(struct pci_dev *dev)
+{
+ if (pci_is_enabled(dev))
+ do_pci_disable_device(dev);
+}
/**
* pci_disable_device - Disable PCI device after use
@@ -903,49 +1521,46 @@ void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {}
* anymore. This only involves disabling PCI bus-mastering, if active.
*
* Note we don't actually disable the device until all callers of
- * pci_device_enable() have called pci_device_disable().
+ * pci_enable_device() have called pci_disable_device().
*/
-void
-pci_disable_device(struct pci_dev *dev)
+void pci_disable_device(struct pci_dev *dev)
{
struct pci_devres *dr;
- u16 pci_command;
dr = find_pci_dr(dev);
if (dr)
dr->enabled = 0;
- if (atomic_sub_return(1, &dev->enable_cnt) != 0)
+ dev_WARN_ONCE(&dev->dev, atomic_read(&dev->enable_cnt) <= 0,
+ "disabling already-disabled device");
+
+ if (atomic_dec_return(&dev->enable_cnt) != 0)
return;
- pci_read_config_word(dev, PCI_COMMAND, &pci_command);
- if (pci_command & PCI_COMMAND_MASTER) {
- pci_command &= ~PCI_COMMAND_MASTER;
- pci_write_config_word(dev, PCI_COMMAND, pci_command);
- }
- dev->is_busmaster = 0;
+ do_pci_disable_device(dev);
- pcibios_disable_device(dev);
+ dev->is_busmaster = 0;
}
+EXPORT_SYMBOL(pci_disable_device);
/**
* pcibios_set_pcie_reset_state - set reset state for device dev
- * @dev: the PCI-E device reset
+ * @dev: the PCIe device reset
* @state: Reset state to enter into
*
*
- * Sets the PCI-E reset state for the device. This is the default
+ * Sets the PCIe reset state for the device. This is the default
* implementation. Architecture implementations can override this.
*/
-int __attribute__ ((weak)) pcibios_set_pcie_reset_state(struct pci_dev *dev,
- enum pcie_reset_state state)
+int __weak pcibios_set_pcie_reset_state(struct pci_dev *dev,
+ enum pcie_reset_state state)
{
return -EINVAL;
}
/**
* pci_set_pcie_reset_state - set reset state for device dev
- * @dev: the PCI-E device reset
+ * @dev: the PCIe device reset
* @state: Reset state to enter into
*
*
@@ -955,11 +1570,198 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
{
return pcibios_set_pcie_reset_state(dev, state);
}
+EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
+
+/**
+ * pci_check_pme_status - Check if given device has generated PME.
+ * @dev: Device to check.
+ *
+ * Check the PME status of the device and if set, clear it and clear PME enable
+ * (if set). Return 'true' if PME status and PME enable were both set or
+ * 'false' otherwise.
+ */
+bool pci_check_pme_status(struct pci_dev *dev)
+{
+ int pmcsr_pos;
+ u16 pmcsr;
+ bool ret = false;
+
+ if (!dev->pm_cap)
+ return false;
+
+ pmcsr_pos = dev->pm_cap + PCI_PM_CTRL;
+ pci_read_config_word(dev, pmcsr_pos, &pmcsr);
+ if (!(pmcsr & PCI_PM_CTRL_PME_STATUS))
+ return false;
+
+ /* Clear PME status. */
+ pmcsr |= PCI_PM_CTRL_PME_STATUS;
+ if (pmcsr & PCI_PM_CTRL_PME_ENABLE) {
+ /* Disable PME to avoid interrupt flood. */
+ pmcsr &= ~PCI_PM_CTRL_PME_ENABLE;
+ ret = true;
+ }
+
+ pci_write_config_word(dev, pmcsr_pos, pmcsr);
+
+ return ret;
+}
+
+/**
+ * pci_pme_wakeup - Wake up a PCI device if its PME Status bit is set.
+ * @dev: Device to handle.
+ * @pme_poll_reset: Whether or not to reset the device's pme_poll flag.
+ *
+ * Check if @dev has generated PME and queue a resume request for it in that
+ * case.
+ */
+static int pci_pme_wakeup(struct pci_dev *dev, void *pme_poll_reset)
+{
+ if (pme_poll_reset && dev->pme_poll)
+ dev->pme_poll = false;
+
+ if (pci_check_pme_status(dev)) {
+ pci_wakeup_event(dev);
+ pm_request_resume(&dev->dev);
+ }
+ return 0;
+}
+
+/**
+ * pci_pme_wakeup_bus - Walk given bus and wake up devices on it, if necessary.
+ * @bus: Top bus of the subtree to walk.
+ */
+void pci_pme_wakeup_bus(struct pci_bus *bus)
+{
+ if (bus)
+ pci_walk_bus(bus, pci_pme_wakeup, (void *)true);
+}
+
+
+/**
+ * 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));
+}
+EXPORT_SYMBOL(pci_pme_capable);
+
+static void pci_pme_list_scan(struct work_struct *work)
+{
+ struct pci_pme_device *pme_dev, *n;
+
+ mutex_lock(&pci_pme_list_mutex);
+ list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) {
+ if (pme_dev->dev->pme_poll) {
+ struct pci_dev *bridge;
+
+ bridge = pme_dev->dev->bus->self;
+ /*
+ * If bridge is in low power state, the
+ * configuration space of subordinate devices
+ * may be not accessible
+ */
+ if (bridge && bridge->current_state != PCI_D0)
+ continue;
+ pci_pme_wakeup(pme_dev->dev, NULL);
+ } else {
+ list_del(&pme_dev->list);
+ kfree(pme_dev);
+ }
+ }
+ if (!list_empty(&pci_pme_list))
+ schedule_delayed_work(&pci_pme_work,
+ msecs_to_jiffies(PME_TIMEOUT));
+ mutex_unlock(&pci_pme_list_mutex);
+}
+
+/**
+ * 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->pme_support)
+ 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);
+
+ /*
+ * PCI (as opposed to PCIe) PME requires that the device have
+ * its PME# line hooked up correctly. Not all hardware vendors
+ * do this, so the PME never gets delivered and the device
+ * remains asleep. The easiest way around this is to
+ * periodically walk the list of suspended devices and check
+ * whether any have their PME flag set. The assumption is that
+ * we'll wake up often enough anyway that this won't be a huge
+ * hit, and the power savings from the devices will still be a
+ * win.
+ *
+ * Although PCIe uses in-band PME message instead of PME# line
+ * to report PME, PME does not work for some PCIe devices in
+ * reality. For example, there are devices that set their PME
+ * status bits, but don't really bother to send a PME message;
+ * there are PCI Express Root Ports that don't bother to
+ * trigger interrupts when they receive PME messages from the
+ * devices below. So PME poll is used for PCIe devices too.
+ */
+
+ if (dev->pme_poll) {
+ struct pci_pme_device *pme_dev;
+ if (enable) {
+ pme_dev = kmalloc(sizeof(struct pci_pme_device),
+ GFP_KERNEL);
+ if (!pme_dev) {
+ dev_warn(&dev->dev, "can't enable PME#\n");
+ return;
+ }
+ pme_dev->dev = dev;
+ mutex_lock(&pci_pme_list_mutex);
+ list_add(&pme_dev->list, &pci_pme_list);
+ if (list_is_singular(&pci_pme_list))
+ schedule_delayed_work(&pci_pme_work,
+ msecs_to_jiffies(PME_TIMEOUT));
+ mutex_unlock(&pci_pme_list_mutex);
+ } else {
+ mutex_lock(&pci_pme_list_mutex);
+ list_for_each_entry(pme_dev, &pci_pme_list, list) {
+ if (pme_dev->dev == dev) {
+ list_del(&pme_dev->list);
+ kfree(pme_dev);
+ break;
+ }
+ }
+ mutex_unlock(&pci_pme_list_mutex);
+ }
+ }
+
+ dev_dbg(&dev->dev, "PME# %s\n", enable ? "enabled" : "disabled");
+}
+EXPORT_SYMBOL(pci_pme_active);
/**
- * pci_enable_wake - enable PCI device as wakeup event source
+ * __pci_enable_wake - enable PCI device as wakeup event source
* @dev: PCI device affected
* @state: PCI state from which device will issue wakeup events
+ * @runtime: True if the events are to be generated at run time
* @enable: True to enable event generation; false to disable
*
* This enables the device as a wakeup event source, or disables it.
@@ -967,79 +1769,676 @@ 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 __pci_enable_wake(struct pci_dev *dev, pci_power_t state,
+ bool runtime, bool 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 ret = 0;
+
+ if (enable && !runtime && !device_may_wakeup(&dev->dev))
+ return -EINVAL;
+
+ /* Don't do the same thing twice in a row for one device. */
+ if (!!enable == !!dev->wakeup_prepared)
+ return 0;
+
+ /*
+ * 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) {
+ int error;
+
+ if (pci_pme_capable(dev, state))
+ pci_pme_active(dev, true);
+ else
+ ret = 1;
+ error = runtime ? platform_pci_run_wake(dev, true) :
+ platform_pci_sleep_wake(dev, true);
+ if (ret)
+ ret = error;
+ if (!ret)
+ dev->wakeup_prepared = true;
+ } else {
+ if (runtime)
+ platform_pci_run_wake(dev, false);
+ else
+ platform_pci_sleep_wake(dev, false);
+ pci_pme_active(dev, false);
+ dev->wakeup_prepared = false;
+ }
- /* find PCI PM capability in list */
- pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+ return ret;
+}
+EXPORT_SYMBOL(__pci_enable_wake);
+
+/**
+ * pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold
+ * @dev: PCI device to prepare
+ * @enable: True to enable wake-up event generation; false to disable
+ *
+ * Many drivers want the device to wake up the system from D3_hot or D3_cold
+ * and this function allows them to set that up cleanly - pci_enable_wake()
+ * should not be called twice in a row to enable wake-up due to PCI PM vs ACPI
+ * ordering constraints.
+ *
+ * This function only returns error code if the device is not capable of
+ * generating PME# from both D3_hot and D3_cold, and the platform is unable to
+ * enable wake-up power for it.
+ */
+int pci_wake_from_d3(struct pci_dev *dev, bool enable)
+{
+ return pci_pme_capable(dev, PCI_D3cold) ?
+ pci_enable_wake(dev, PCI_D3cold, enable) :
+ pci_enable_wake(dev, PCI_D3hot, enable);
+}
+EXPORT_SYMBOL(pci_wake_from_d3);
+
+/**
+ * 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.
+ */
+static pci_power_t pci_target_state(struct pci_dev *dev)
+{
+ pci_power_t target_state = PCI_D3hot;
+
+ if (platform_pci_power_manageable(dev)) {
+ /*
+ * Call the platform to choose the target state of the device
+ * and enable wake-up from this state if supported.
+ */
+ pci_power_t state = platform_pci_choose_state(dev);
+
+ switch (state) {
+ case PCI_POWER_ERROR:
+ case PCI_UNKNOWN:
+ break;
+ case PCI_D1:
+ case PCI_D2:
+ if (pci_no_d1d2(dev))
+ break;
+ default:
+ target_state = state;
+ }
+ } else if (!dev->pm_cap) {
+ target_state = PCI_D0;
+ } else if (device_may_wakeup(&dev->dev)) {
+ /*
+ * Find the deepest state from which the device can generate
+ * wake-up events, make it the target state and enable device
+ * to generate PME#.
+ */
+ if (dev->pme_support) {
+ while (target_state
+ && !(dev->pme_support & (1 << target_state)))
+ target_state--;
+ }
+ }
- /* 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.
+ return target_state;
+}
+
+/**
+ * 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 (target_state == PCI_POWER_ERROR)
+ return -EIO;
+
+ /* D3cold during system suspend/hibernate is not supported */
+ if (target_state > PCI_D3hot)
+ target_state = PCI_D3hot;
+
+ pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev));
+
+ error = pci_set_power_state(dev, target_state);
+
+ if (error)
+ pci_enable_wake(dev, target_state, false);
+
+ return error;
+}
+EXPORT_SYMBOL(pci_prepare_to_sleep);
+
+/**
+ * pci_back_from_sleep - turn PCI device on during system-wide transition into working state
+ * @dev: Device to handle.
+ *
+ * Disable device's system 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);
+}
+EXPORT_SYMBOL(pci_back_from_sleep);
+
+/**
+ * pci_finish_runtime_suspend - Carry out PCI-specific part of runtime suspend.
+ * @dev: PCI device being suspended.
+ *
+ * Prepare @dev to generate wake-up events at run time and put it into a low
+ * power state.
+ */
+int pci_finish_runtime_suspend(struct pci_dev *dev)
+{
+ pci_power_t target_state = pci_target_state(dev);
+ int error;
+
+ if (target_state == PCI_POWER_ERROR)
+ return -EIO;
+
+ dev->runtime_d3cold = target_state == PCI_D3cold;
+
+ __pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev));
+
+ error = pci_set_power_state(dev, target_state);
+
+ if (error) {
+ __pci_enable_wake(dev, target_state, true, false);
+ dev->runtime_d3cold = false;
+ }
+
+ return error;
+}
+
+/**
+ * pci_dev_run_wake - Check if device can generate run-time wake-up events.
+ * @dev: Device to check.
+ *
+ * Return true if the device itself is capable of generating wake-up events
+ * (through the platform or using the native PCIe PME) or if the device supports
+ * PME and one of its upstream bridges can generate wake-up events.
+ */
+bool pci_dev_run_wake(struct pci_dev *dev)
+{
+ struct pci_bus *bus = dev->bus;
+
+ if (device_run_wake(&dev->dev))
+ return true;
+
+ if (!dev->pme_support)
+ return false;
+
+ while (bus->parent) {
+ struct pci_dev *bridge = bus->self;
+
+ if (device_run_wake(&bridge->dev))
+ return true;
+
+ bus = bus->parent;
+ }
+
+ /* We have reached the root bus. */
+ if (bus->bridge)
+ return device_run_wake(bus->bridge);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(pci_dev_run_wake);
+
+void pci_config_pm_runtime_get(struct pci_dev *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *parent = dev->parent;
+
+ if (parent)
+ pm_runtime_get_sync(parent);
+ pm_runtime_get_noresume(dev);
+ /*
+ * pdev->current_state is set to PCI_D3cold during suspending,
+ * so wait until suspending completes
*/
- if (!pm)
- return enable ? status : 0;
+ pm_runtime_barrier(dev);
+ /*
+ * Only need to resume devices in D3cold, because config
+ * registers are still accessible for devices suspended but
+ * not in D3cold.
+ */
+ if (pdev->current_state == PCI_D3cold)
+ pm_runtime_resume(dev);
+}
+
+void pci_config_pm_runtime_put(struct pci_dev *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *parent = dev->parent;
+
+ pm_runtime_put(dev);
+ if (parent)
+ pm_runtime_put_sync(parent);
+}
+
+/**
+ * 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;
+ pm_runtime_forbid(&dev->dev);
+ pm_runtime_set_active(&dev->dev);
+ pm_runtime_enable(&dev->dev);
+ device_enable_async_suspend(&dev->dev);
+ dev->wakeup_prepared = false;
+
+ dev->pm_cap = 0;
+ dev->pme_support = 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,&value);
+ pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
- value &= PCI_PM_CAP_PME_MASK;
- value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */
+ 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;
+ }
- /* 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
+ dev->pm_cap = pm;
+ dev->d3_delay = PCI_PM_D3_WAIT;
+ dev->d3cold_delay = PCI_PM_D3COLD_WAIT;
+ dev->d3cold_allowed = true;
+
+ dev->d1_support = false;
+ dev->d2_support = false;
+ if (!pci_no_d1d2(dev)) {
+ if (pmc & PCI_PM_CAP_D1)
+ dev->d1_support = true;
+ if (pmc & PCI_PM_CAP_D2)
+ dev->d2_support = true;
+
+ if (dev->d1_support || dev->d2_support)
+ dev_printk(KERN_DEBUG, &dev->dev, "supports%s%s\n",
+ dev->d1_support ? " D1" : "",
+ dev->d2_support ? " D2" : "");
+ }
+
+ pmc &= PCI_PM_CAP_PME_MASK;
+ if (pmc) {
+ dev_printk(KERN_DEBUG, &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;
+ dev->pme_poll = true;
+ /*
+ * Make device's PM flags reflect the wake-up capability, but
+ * let the user space enable it to wake up the system as needed.
*/
- if (enable)
- call_platform_enable_wakeup(&dev->dev, 0);
- return enable ? -EINVAL : 0;
+ device_set_wakeup_capable(&dev->dev, true);
+ /* Disable the PME# generation functionality */
+ pci_pme_active(dev, false);
}
+}
- pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
+static void pci_add_saved_cap(struct pci_dev *pci_dev,
+ struct pci_cap_saved_state *new_cap)
+{
+ hlist_add_head(&new_cap->next, &pci_dev->saved_cap_space);
+}
- /* Clear PME_Status by writing 1 to it and enable PME# */
- value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
+/**
+ * _pci_add_cap_save_buffer - allocate buffer for saving given
+ * capability registers
+ * @dev: the PCI device
+ * @cap: the capability to allocate the buffer for
+ * @extended: Standard or Extended capability ID
+ * @size: requested size of the buffer
+ */
+static int _pci_add_cap_save_buffer(struct pci_dev *dev, u16 cap,
+ bool extended, unsigned int size)
+{
+ int pos;
+ struct pci_cap_saved_state *save_state;
- if (!enable)
- value &= ~PCI_PM_CTRL_PME_ENABLE;
+ if (extended)
+ pos = pci_find_ext_capability(dev, cap);
+ else
+ pos = pci_find_capability(dev, cap);
+
+ if (pos <= 0)
+ return 0;
+
+ save_state = kzalloc(sizeof(*save_state) + size, GFP_KERNEL);
+ if (!save_state)
+ return -ENOMEM;
+
+ save_state->cap.cap_nr = cap;
+ save_state->cap.cap_extended = extended;
+ save_state->cap.size = size;
+ pci_add_saved_cap(dev, save_state);
+
+ return 0;
+}
+
+int pci_add_cap_save_buffer(struct pci_dev *dev, char cap, unsigned int size)
+{
+ return _pci_add_cap_save_buffer(dev, cap, false, size);
+}
+
+int pci_add_ext_cap_save_buffer(struct pci_dev *dev, u16 cap, unsigned int size)
+{
+ return _pci_add_cap_save_buffer(dev, cap, true, size);
+}
+
+/**
+ * pci_allocate_cap_save_buffers - allocate buffers for saving capabilities
+ * @dev: the PCI device
+ */
+void pci_allocate_cap_save_buffers(struct pci_dev *dev)
+{
+ int error;
+
+ error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_EXP,
+ PCI_EXP_SAVE_REGS * sizeof(u16));
+ if (error)
+ dev_err(&dev->dev,
+ "unable to preallocate PCI Express save buffer\n");
+
+ error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_PCIX, sizeof(u16));
+ if (error)
+ dev_err(&dev->dev,
+ "unable to preallocate PCI-X save buffer\n");
+
+ pci_allocate_vc_save_buffers(dev);
+}
+
+void pci_free_cap_save_buffers(struct pci_dev *dev)
+{
+ struct pci_cap_saved_state *tmp;
+ struct hlist_node *n;
+
+ hlist_for_each_entry_safe(tmp, n, &dev->saved_cap_space, next)
+ kfree(tmp);
+}
+
+/**
+ * pci_configure_ari - enable or disable ARI forwarding
+ * @dev: the PCI device
+ *
+ * If @dev and its upstream bridge both support ARI, enable ARI in the
+ * bridge. Otherwise, disable ARI in the bridge.
+ */
+void pci_configure_ari(struct pci_dev *dev)
+{
+ u32 cap;
+ struct pci_dev *bridge;
+
+ if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn)
+ return;
+
+ bridge = dev->bus->self;
+ if (!bridge)
+ return;
+
+ pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &cap);
+ if (!(cap & PCI_EXP_DEVCAP2_ARI))
+ return;
+
+ if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI)) {
+ pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2,
+ PCI_EXP_DEVCTL2_ARI);
+ bridge->ari_enabled = 1;
+ } else {
+ pcie_capability_clear_word(bridge, PCI_EXP_DEVCTL2,
+ PCI_EXP_DEVCTL2_ARI);
+ bridge->ari_enabled = 0;
+ }
+}
+
+static int pci_acs_enable;
+
+/**
+ * pci_request_acs - ask for ACS to be enabled if supported
+ */
+void pci_request_acs(void)
+{
+ pci_acs_enable = 1;
+}
- pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
+/**
+ * pci_std_enable_acs - enable ACS on devices using standard ACS capabilites
+ * @dev: the PCI device
+ */
+static int pci_std_enable_acs(struct pci_dev *dev)
+{
+ int pos;
+ u16 cap;
+ u16 ctrl;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
+ if (!pos)
+ return -ENODEV;
+
+ pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap);
+ pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
+
+ /* Source Validation */
+ ctrl |= (cap & PCI_ACS_SV);
+
+ /* P2P Request Redirect */
+ ctrl |= (cap & PCI_ACS_RR);
+
+ /* P2P Completion Redirect */
+ ctrl |= (cap & PCI_ACS_CR);
+
+ /* Upstream Forwarding */
+ ctrl |= (cap & PCI_ACS_UF);
+
+ pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
return 0;
}
-int
-pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge)
+/**
+ * pci_enable_acs - enable ACS if hardware support it
+ * @dev: the PCI device
+ */
+void pci_enable_acs(struct pci_dev *dev)
+{
+ if (!pci_acs_enable)
+ return;
+
+ if (!pci_std_enable_acs(dev))
+ return;
+
+ pci_dev_specific_enable_acs(dev);
+}
+
+static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
+{
+ int pos;
+ u16 cap, ctrl;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
+ if (!pos)
+ return false;
+
+ /*
+ * Except for egress control, capabilities are either required
+ * or only required if controllable. Features missing from the
+ * capability field can therefore be assumed as hard-wired enabled.
+ */
+ pci_read_config_word(pdev, pos + PCI_ACS_CAP, &cap);
+ acs_flags &= (cap | PCI_ACS_EC);
+
+ pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl);
+ return (ctrl & acs_flags) == acs_flags;
+}
+
+/**
+ * pci_acs_enabled - test ACS against required flags for a given device
+ * @pdev: device to test
+ * @acs_flags: required PCI ACS flags
+ *
+ * Return true if the device supports the provided flags. Automatically
+ * filters out flags that are not implemented on multifunction devices.
+ *
+ * Note that this interface checks the effective ACS capabilities of the
+ * device rather than the actual capabilities. For instance, most single
+ * function endpoints are not required to support ACS because they have no
+ * opportunity for peer-to-peer access. We therefore return 'true'
+ * regardless of whether the device exposes an ACS capability. This makes
+ * it much easier for callers of this function to ignore the actual type
+ * or topology of the device when testing ACS support.
+ */
+bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags)
+{
+ int ret;
+
+ ret = pci_dev_specific_acs_enabled(pdev, acs_flags);
+ if (ret >= 0)
+ return ret > 0;
+
+ /*
+ * Conventional PCI and PCI-X devices never support ACS, either
+ * effectively or actually. The shared bus topology implies that
+ * any device on the bus can receive or snoop DMA.
+ */
+ if (!pci_is_pcie(pdev))
+ return false;
+
+ switch (pci_pcie_type(pdev)) {
+ /*
+ * PCI/X-to-PCIe bridges are not specifically mentioned by the spec,
+ * but since their primary interface is PCI/X, we conservatively
+ * handle them as we would a non-PCIe device.
+ */
+ case PCI_EXP_TYPE_PCIE_BRIDGE:
+ /*
+ * PCIe 3.0, 6.12.1 excludes ACS on these devices. "ACS is never
+ * applicable... must never implement an ACS Extended Capability...".
+ * This seems arbitrary, but we take a conservative interpretation
+ * of this statement.
+ */
+ case PCI_EXP_TYPE_PCI_BRIDGE:
+ case PCI_EXP_TYPE_RC_EC:
+ return false;
+ /*
+ * PCIe 3.0, 6.12.1.1 specifies that downstream and root ports should
+ * implement ACS in order to indicate their peer-to-peer capabilities,
+ * regardless of whether they are single- or multi-function devices.
+ */
+ case PCI_EXP_TYPE_DOWNSTREAM:
+ case PCI_EXP_TYPE_ROOT_PORT:
+ return pci_acs_flags_enabled(pdev, acs_flags);
+ /*
+ * PCIe 3.0, 6.12.1.2 specifies ACS capabilities that should be
+ * implemented by the remaining PCIe types to indicate peer-to-peer
+ * capabilities, but only when they are part of a multifunction
+ * device. The footnote for section 6.12 indicates the specific
+ * PCIe types included here.
+ */
+ case PCI_EXP_TYPE_ENDPOINT:
+ case PCI_EXP_TYPE_UPSTREAM:
+ case PCI_EXP_TYPE_LEG_END:
+ case PCI_EXP_TYPE_RC_END:
+ if (!pdev->multifunction)
+ break;
+
+ return pci_acs_flags_enabled(pdev, acs_flags);
+ }
+
+ /*
+ * PCIe 3.0, 6.12.1.3 specifies no ACS capabilities are applicable
+ * to single function devices with the exception of downstream ports.
+ */
+ return true;
+}
+
+/**
+ * pci_acs_path_enable - test ACS flags from start to end in a hierarchy
+ * @start: starting downstream device
+ * @end: ending upstream device or NULL to search to the root bus
+ * @acs_flags: required flags
+ *
+ * Walk up a device tree from start to end testing PCI ACS support. If
+ * any step along the way does not support the required flags, return false.
+ */
+bool pci_acs_path_enabled(struct pci_dev *start,
+ struct pci_dev *end, u16 acs_flags)
+{
+ struct pci_dev *pdev, *parent = start;
+
+ do {
+ pdev = parent;
+
+ if (!pci_acs_enabled(pdev, acs_flags))
+ return false;
+
+ if (pci_is_root_bus(pdev->bus))
+ return (end == NULL);
+
+ parent = pdev->bus->self;
+ } while (pdev != end);
+
+ return true;
+}
+
+/**
+ * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge
+ * @dev: the PCI device
+ * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD)
+ *
+ * Perform INTx swizzling for a device behind one level of bridge. This is
+ * required by section 9.1 of the PCI-to-PCI bridge specification for devices
+ * behind bridges on add-in cards. For devices with ARI enabled, the slot
+ * number is always 0 (see the Implementation Note in section 2.2.8.1 of
+ * the PCI Express Base Specification, Revision 2.1)
+ */
+u8 pci_swizzle_interrupt_pin(const struct pci_dev *dev, u8 pin)
+{
+ int slot;
+
+ if (pci_ari_enabled(dev->bus))
+ slot = 0;
+ else
+ slot = PCI_SLOT(dev->devfn);
+
+ return (((pin - 1) + slot) % 4) + 1;
+}
+
+int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge)
{
u8 pin;
pin = dev->pin;
if (!pin)
return -1;
- pin--;
- while (dev->bus->self) {
- pin = (pin + PCI_SLOT(dev->devfn)) % 4;
+
+ while (!pci_is_root_bus(dev->bus)) {
+ pin = pci_swizzle_interrupt_pin(dev, pin);
dev = dev->bus->self;
}
*bridge = dev;
@@ -1047,6 +2446,26 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge)
}
/**
+ * pci_common_swizzle - swizzle INTx all the way to root bridge
+ * @dev: the PCI device
+ * @pinp: pointer to the INTx pin value (1=INTA, 2=INTB, 3=INTD, 4=INTD)
+ *
+ * Perform INTx swizzling for a device. This traverses through all PCI-to-PCI
+ * bridges all the way up to a PCI root bus.
+ */
+u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp)
+{
+ u8 pin = *pinp;
+
+ while (!pci_is_root_bus(dev->bus)) {
+ pin = pci_swizzle_interrupt_pin(dev, pin);
+ dev = dev->bus->self;
+ }
+ *pinp = pin;
+ return PCI_SLOT(dev->devfn);
+}
+
+/**
* pci_release_region - Release a PCI bar
* @pdev: PCI device whose resources were previously reserved by pci_request_region
* @bar: BAR to release
@@ -1072,36 +2491,43 @@ void pci_release_region(struct pci_dev *pdev, int bar)
if (dr)
dr->region_mask &= ~(1 << bar);
}
+EXPORT_SYMBOL(pci_release_region);
/**
- * pci_request_region - Reserved PCI I/O and memory resource
+ * __pci_request_region - Reserved PCI I/O and memory resource
* @pdev: PCI device whose resources are to be reserved
* @bar: BAR to be reserved
* @res_name: Name to be associated with resource.
+ * @exclusive: whether the region access is exclusive or not
*
* Mark the PCI region associated with PCI device @pdev BR @bar as
* being reserved by owner @res_name. Do not access any
* address inside the PCI regions unless this call returns
* successfully.
*
+ * If @exclusive is set, then the region is marked so that userspace
+ * is explicitly not allowed to map the resource via /dev/mem or
+ * sysfs MMIO access.
+ *
* Returns 0 on success, or %EBUSY on error. A warning
* message is also printed on failure.
*/
-int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
+static int __pci_request_region(struct pci_dev *pdev, int bar,
+ const char *res_name, int exclusive)
{
struct pci_devres *dr;
if (pci_resource_len(pdev, bar) == 0)
return 0;
-
+
if (pci_resource_flags(pdev, bar) & IORESOURCE_IO) {
if (!request_region(pci_resource_start(pdev, bar),
pci_resource_len(pdev, bar), res_name))
goto err_out;
- }
- else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
- if (!request_mem_region(pci_resource_start(pdev, bar),
- pci_resource_len(pdev, bar), res_name))
+ } else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
+ if (!__request_mem_region(pci_resource_start(pdev, bar),
+ pci_resource_len(pdev, bar), res_name,
+ exclusive))
goto err_out;
}
@@ -1112,17 +2538,57 @@ 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 %pR\n", bar,
+ &pdev->resource[bar]);
return -EBUSY;
}
/**
+ * pci_request_region - Reserve PCI I/O and memory resource
+ * @pdev: PCI device whose resources are to be reserved
+ * @bar: BAR to be reserved
+ * @res_name: Name to be associated with resource
+ *
+ * Mark the PCI region associated with PCI device @pdev BAR @bar as
+ * being reserved by owner @res_name. Do not access any
+ * address inside the PCI regions unless this call returns
+ * successfully.
+ *
+ * Returns 0 on success, or %EBUSY on error. A warning
+ * message is also printed on failure.
+ */
+int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
+{
+ return __pci_request_region(pdev, bar, res_name, 0);
+}
+EXPORT_SYMBOL(pci_request_region);
+
+/**
+ * pci_request_region_exclusive - Reserved PCI I/O and memory resource
+ * @pdev: PCI device whose resources are to be reserved
+ * @bar: BAR to be reserved
+ * @res_name: Name to be associated with resource.
+ *
+ * Mark the PCI region associated with PCI device @pdev BR @bar as
+ * being reserved by owner @res_name. Do not access any
+ * address inside the PCI regions unless this call returns
+ * successfully.
+ *
+ * Returns 0 on success, or %EBUSY on error. A warning
+ * message is also printed on failure.
+ *
+ * The key difference that _exclusive makes it that userspace is
+ * explicitly not allowed to map the resource via /dev/mem or
+ * sysfs.
+ */
+int pci_request_region_exclusive(struct pci_dev *pdev, int bar,
+ const char *res_name)
+{
+ return __pci_request_region(pdev, bar, res_name, IORESOURCE_EXCLUSIVE);
+}
+EXPORT_SYMBOL(pci_request_region_exclusive);
+
+/**
* pci_release_selected_regions - Release selected PCI I/O and memory resources
* @pdev: PCI device whose resources were previously reserved
* @bars: Bitmask of BARs to be released
@@ -1138,32 +2604,49 @@ void pci_release_selected_regions(struct pci_dev *pdev, int bars)
if (bars & (1 << i))
pci_release_region(pdev, i);
}
+EXPORT_SYMBOL(pci_release_selected_regions);
-/**
- * pci_request_selected_regions - Reserve selected PCI I/O and memory resources
- * @pdev: PCI device whose resources are to be reserved
- * @bars: Bitmask of BARs to be requested
- * @res_name: Name to be associated with resource
- */
-int pci_request_selected_regions(struct pci_dev *pdev, int bars,
- const char *res_name)
+static int __pci_request_selected_regions(struct pci_dev *pdev, int bars,
+ const char *res_name, int excl)
{
int i;
for (i = 0; i < 6; i++)
if (bars & (1 << i))
- if(pci_request_region(pdev, i, res_name))
+ if (__pci_request_region(pdev, i, res_name, excl))
goto err_out;
return 0;
err_out:
- while(--i >= 0)
+ while (--i >= 0)
if (bars & (1 << i))
pci_release_region(pdev, i);
return -EBUSY;
}
+
+/**
+ * pci_request_selected_regions - Reserve selected PCI I/O and memory resources
+ * @pdev: PCI device whose resources are to be reserved
+ * @bars: Bitmask of BARs to be requested
+ * @res_name: Name to be associated with resource
+ */
+int pci_request_selected_regions(struct pci_dev *pdev, int bars,
+ const char *res_name)
+{
+ return __pci_request_selected_regions(pdev, bars, res_name, 0);
+}
+EXPORT_SYMBOL(pci_request_selected_regions);
+
+int pci_request_selected_regions_exclusive(struct pci_dev *pdev, int bars,
+ const char *res_name)
+{
+ return __pci_request_selected_regions(pdev, bars, res_name,
+ IORESOURCE_EXCLUSIVE);
+}
+EXPORT_SYMBOL(pci_request_selected_regions_exclusive);
+
/**
* pci_release_regions - Release reserved PCI I/O and memory resources
* @pdev: PCI device whose resources were previously reserved by pci_request_regions
@@ -1177,6 +2660,7 @@ void pci_release_regions(struct pci_dev *pdev)
{
pci_release_selected_regions(pdev, (1 << 6) - 1);
}
+EXPORT_SYMBOL(pci_release_regions);
/**
* pci_request_regions - Reserved PCI I/O and memory resources
@@ -1195,53 +2679,110 @@ int pci_request_regions(struct pci_dev *pdev, const char *res_name)
{
return pci_request_selected_regions(pdev, ((1 << 6) - 1), res_name);
}
+EXPORT_SYMBOL(pci_request_regions);
/**
- * pci_set_master - enables bus-mastering for device dev
- * @dev: the PCI device to enable
+ * pci_request_regions_exclusive - Reserved PCI I/O and memory resources
+ * @pdev: PCI device whose resources are to be reserved
+ * @res_name: Name to be associated with resource.
*
- * Enables bus-mastering on the device and calls pcibios_set_master()
- * to do the needed arch specific settings.
+ * Mark all PCI regions associated with PCI device @pdev as
+ * being reserved by owner @res_name. Do not access any
+ * address inside the PCI regions unless this call returns
+ * successfully.
+ *
+ * pci_request_regions_exclusive() will mark the region so that
+ * /dev/mem and the sysfs MMIO access will not be allowed.
+ *
+ * Returns 0 on success, or %EBUSY on error. A warning
+ * message is also printed on failure.
*/
-void
-pci_set_master(struct pci_dev *dev)
+int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
{
- u16 cmd;
+ return pci_request_selected_regions_exclusive(pdev,
+ ((1 << 6) - 1), res_name);
+}
+EXPORT_SYMBOL(pci_request_regions_exclusive);
- 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));
- cmd |= PCI_COMMAND_MASTER;
+static void __pci_set_master(struct pci_dev *dev, bool enable)
+{
+ u16 old_cmd, cmd;
+
+ pci_read_config_word(dev, PCI_COMMAND, &old_cmd);
+ if (enable)
+ cmd = old_cmd | PCI_COMMAND_MASTER;
+ else
+ cmd = old_cmd & ~PCI_COMMAND_MASTER;
+ if (cmd != old_cmd) {
+ dev_dbg(&dev->dev, "%s bus mastering\n",
+ enable ? "enabling" : "disabling");
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
- dev->is_busmaster = 1;
- pcibios_set_master(dev);
+ dev->is_busmaster = enable;
}
-#ifdef PCI_DISABLE_MWI
-int pci_set_mwi(struct pci_dev *dev)
+/**
+ * pcibios_setup - process "pci=" kernel boot arguments
+ * @str: string used to pass in "pci=" kernel boot arguments
+ *
+ * Process kernel boot arguments. This is the default implementation.
+ * Architecture specific implementations can override this as necessary.
+ */
+char * __weak __init pcibios_setup(char *str)
{
- return 0;
+ return str;
}
-int pci_try_set_mwi(struct pci_dev *dev)
+/**
+ * pcibios_set_master - enable PCI bus-mastering for device dev
+ * @dev: the PCI device to enable
+ *
+ * Enables PCI bus-mastering for the device. This is the default
+ * implementation. Architecture specific implementations can override
+ * this if necessary.
+ */
+void __weak pcibios_set_master(struct pci_dev *dev)
{
- return 0;
+ u8 lat;
+
+ /* The latency timer doesn't apply to PCIe (either Type 0 or Type 1) */
+ if (pci_is_pcie(dev))
+ return;
+
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+ if (lat < 16)
+ lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
+ else if (lat > pcibios_max_latency)
+ lat = pcibios_max_latency;
+ else
+ return;
+
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
}
-void pci_clear_mwi(struct pci_dev *dev)
+/**
+ * pci_set_master - enables bus-mastering for device dev
+ * @dev: the PCI device to enable
+ *
+ * Enables bus-mastering on the device and calls pcibios_set_master()
+ * to do the needed arch specific settings.
+ */
+void pci_set_master(struct pci_dev *dev)
{
+ __pci_set_master(dev, true);
+ pcibios_set_master(dev);
}
+EXPORT_SYMBOL(pci_set_master);
-#else
-
-#ifndef PCI_CACHE_LINE_BYTES
-#define PCI_CACHE_LINE_BYTES L1_CACHE_BYTES
-#endif
-
-/* This can be overridden by arch code. */
-/* Don't forget this is measured in 32-bit words, not bytes */
-u8 pci_cache_line_size = PCI_CACHE_LINE_BYTES / 4;
+/**
+ * pci_clear_master - disables bus-mastering for device dev
+ * @dev: the PCI device to disable
+ */
+void pci_clear_master(struct pci_dev *dev)
+{
+ __pci_set_master(dev, false);
+}
+EXPORT_SYMBOL(pci_clear_master);
/**
* pci_set_cacheline_size - ensure the CACHE_LINE_SIZE register is programmed
@@ -1253,13 +2794,12 @@ u8 pci_cache_line_size = PCI_CACHE_LINE_BYTES / 4;
*
* RETURNS: An appropriate -ERRNO error value on error, or zero for success.
*/
-static int
-pci_set_cacheline_size(struct pci_dev *dev)
+int pci_set_cacheline_size(struct pci_dev *dev)
{
u8 cacheline_size;
if (!pci_cache_line_size)
- return -EINVAL; /* The system doesn't support MWI. */
+ return -EINVAL;
/* Validate current setting: the PCI_CACHE_LINE_SIZE must be
equal to or multiple of the right value. */
@@ -1275,11 +2815,12 @@ 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;
}
+EXPORT_SYMBOL_GPL(pci_set_cacheline_size);
/**
* pci_set_mwi - enables memory-write-invalidate PCI transaction
@@ -1289,9 +2830,11 @@ pci_set_cacheline_size(struct pci_dev *dev)
*
* RETURNS: An appropriate -ERRNO error value on error, or zero for success.
*/
-int
-pci_set_mwi(struct pci_dev *dev)
+int pci_set_mwi(struct pci_dev *dev)
{
+#ifdef PCI_DISABLE_MWI
+ return 0;
+#else
int rc;
u16 cmd;
@@ -1300,15 +2843,15 @@ pci_set_mwi(struct pci_dev *dev)
return rc;
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));
+ if (!(cmd & PCI_COMMAND_INVALIDATE)) {
+ dev_dbg(&dev->dev, "enabling Mem-Wr-Inval\n");
cmd |= PCI_COMMAND_INVALIDATE;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
-
return 0;
+#endif
}
+EXPORT_SYMBOL(pci_set_mwi);
/**
* pci_try_set_mwi - enables memory-write-invalidate PCI transaction
@@ -1321,9 +2864,13 @@ pci_set_mwi(struct pci_dev *dev)
*/
int pci_try_set_mwi(struct pci_dev *dev)
{
- int rc = pci_set_mwi(dev);
- return rc;
+#ifdef PCI_DISABLE_MWI
+ return 0;
+#else
+ return pci_set_mwi(dev);
+#endif
}
+EXPORT_SYMBOL(pci_try_set_mwi);
/**
* pci_clear_mwi - disables Memory-Write-Invalidate for device dev
@@ -1331,9 +2878,9 @@ int pci_try_set_mwi(struct pci_dev *dev)
*
* Disables PCI Memory-Write-Invalidate transaction on the device
*/
-void
-pci_clear_mwi(struct pci_dev *dev)
+void pci_clear_mwi(struct pci_dev *dev)
{
+#ifndef PCI_DISABLE_MWI
u16 cmd;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
@@ -1341,8 +2888,9 @@ pci_clear_mwi(struct pci_dev *dev)
cmd &= ~PCI_COMMAND_INVALIDATE;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
+#endif
}
-#endif /* ! PCI_DISABLE_MWI */
+EXPORT_SYMBOL(pci_clear_mwi);
/**
* pci_intx - enables/disables PCI INTx for device dev
@@ -1351,18 +2899,16 @@ pci_clear_mwi(struct pci_dev *dev)
*
* Enables/disables PCI INTx for device dev
*/
-void
-pci_intx(struct pci_dev *pdev, int enable)
+void pci_intx(struct pci_dev *pdev, int enable)
{
u16 pci_command, new;
pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
- if (enable) {
+ if (enable)
new = pci_command & ~PCI_COMMAND_INTX_DISABLE;
- } else {
+ else
new = pci_command | PCI_COMMAND_INTX_DISABLE;
- }
if (new != pci_command) {
struct pci_devres *dr;
@@ -1376,20 +2922,139 @@ pci_intx(struct pci_dev *pdev, int enable)
}
}
}
+EXPORT_SYMBOL_GPL(pci_intx);
/**
- * pci_msi_off - disables any msi or msix capabilities
+ * pci_intx_mask_supported - probe for INTx masking support
* @dev: the PCI device to operate on
*
- * If you want to use msi see pci_enable_msi and friends.
- * This is a lower level primitive that allows us to disable
- * msi operation at the device level.
+ * Check if the device dev support INTx masking via the config space
+ * command word.
+ */
+bool pci_intx_mask_supported(struct pci_dev *dev)
+{
+ bool mask_supported = false;
+ u16 orig, new;
+
+ if (dev->broken_intx_masking)
+ return false;
+
+ pci_cfg_access_lock(dev);
+
+ pci_read_config_word(dev, PCI_COMMAND, &orig);
+ pci_write_config_word(dev, PCI_COMMAND,
+ orig ^ PCI_COMMAND_INTX_DISABLE);
+ pci_read_config_word(dev, PCI_COMMAND, &new);
+
+ /*
+ * There's no way to protect against hardware bugs or detect them
+ * reliably, but as long as we know what the value should be, let's
+ * go ahead and check it.
+ */
+ if ((new ^ orig) & ~PCI_COMMAND_INTX_DISABLE) {
+ dev_err(&dev->dev, "Command register changed from 0x%x to 0x%x: driver or hardware bug?\n",
+ orig, new);
+ } else if ((new ^ orig) & PCI_COMMAND_INTX_DISABLE) {
+ mask_supported = true;
+ pci_write_config_word(dev, PCI_COMMAND, orig);
+ }
+
+ pci_cfg_access_unlock(dev);
+ return mask_supported;
+}
+EXPORT_SYMBOL_GPL(pci_intx_mask_supported);
+
+static bool pci_check_and_set_intx_mask(struct pci_dev *dev, bool mask)
+{
+ struct pci_bus *bus = dev->bus;
+ bool mask_updated = true;
+ u32 cmd_status_dword;
+ u16 origcmd, newcmd;
+ unsigned long flags;
+ bool irq_pending;
+
+ /*
+ * We do a single dword read to retrieve both command and status.
+ * Document assumptions that make this possible.
+ */
+ BUILD_BUG_ON(PCI_COMMAND % 4);
+ BUILD_BUG_ON(PCI_COMMAND + 2 != PCI_STATUS);
+
+ raw_spin_lock_irqsave(&pci_lock, flags);
+
+ bus->ops->read(bus, dev->devfn, PCI_COMMAND, 4, &cmd_status_dword);
+
+ irq_pending = (cmd_status_dword >> 16) & PCI_STATUS_INTERRUPT;
+
+ /*
+ * Check interrupt status register to see whether our device
+ * triggered the interrupt (when masking) or the next IRQ is
+ * already pending (when unmasking).
+ */
+ if (mask != irq_pending) {
+ mask_updated = false;
+ goto done;
+ }
+
+ origcmd = cmd_status_dword;
+ newcmd = origcmd & ~PCI_COMMAND_INTX_DISABLE;
+ if (mask)
+ newcmd |= PCI_COMMAND_INTX_DISABLE;
+ if (newcmd != origcmd)
+ bus->ops->write(bus, dev->devfn, PCI_COMMAND, 2, newcmd);
+
+done:
+ raw_spin_unlock_irqrestore(&pci_lock, flags);
+
+ return mask_updated;
+}
+
+/**
+ * pci_check_and_mask_intx - mask INTx on pending interrupt
+ * @dev: the PCI device to operate on
+ *
+ * Check if the device dev has its INTx line asserted, mask it and
+ * return true in that case. False is returned if not interrupt was
+ * pending.
+ */
+bool pci_check_and_mask_intx(struct pci_dev *dev)
+{
+ return pci_check_and_set_intx_mask(dev, true);
+}
+EXPORT_SYMBOL_GPL(pci_check_and_mask_intx);
+
+/**
+ * pci_check_and_unmask_intx - unmask INTx if no interrupt is pending
+ * @dev: the PCI device to operate on
+ *
+ * Check if the device dev has its INTx line asserted, unmask it if not
+ * and return true. False is returned and the mask remains active if
+ * there was still an interrupt pending.
+ */
+bool pci_check_and_unmask_intx(struct pci_dev *dev)
+{
+ return pci_check_and_set_intx_mask(dev, false);
+}
+EXPORT_SYMBOL_GPL(pci_check_and_unmask_intx);
+
+/**
+ * pci_msi_off - disables any MSI or MSI-X capabilities
+ * @dev: the PCI device to operate on
+ *
+ * If you want to use MSI, see pci_enable_msi() and friends.
+ * This is a lower-level primitive that allows us to disable
+ * MSI operation at the device level.
*/
void pci_msi_off(struct pci_dev *dev)
{
int pos;
u16 control;
+ /*
+ * This looks like it could go in msi.c, but we need it even when
+ * CONFIG_PCI_MSI=n. For the same reason, we can't use
+ * dev->msi_cap or dev->msix_cap here.
+ */
pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
if (pos) {
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
@@ -1403,49 +3068,812 @@ void pci_msi_off(struct pci_dev *dev)
pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
}
}
+EXPORT_SYMBOL_GPL(pci_msi_off);
-#ifndef HAVE_ARCH_PCI_SET_DMA_MASK
-/*
- * These can be overridden by arch-specific implementations
+int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size)
+{
+ return dma_set_max_seg_size(&dev->dev, size);
+}
+EXPORT_SYMBOL(pci_set_dma_max_seg_size);
+
+int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask)
+{
+ return dma_set_seg_boundary(&dev->dev, mask);
+}
+EXPORT_SYMBOL(pci_set_dma_seg_boundary);
+
+/**
+ * pci_wait_for_pending_transaction - waits for pending transaction
+ * @dev: the PCI device to operate on
+ *
+ * Return 0 if transaction is pending 1 otherwise.
*/
-int
-pci_set_dma_mask(struct pci_dev *dev, u64 mask)
+int pci_wait_for_pending_transaction(struct pci_dev *dev)
{
- if (!pci_dma_supported(dev, mask))
- return -EIO;
+ if (!pci_is_pcie(dev))
+ return 1;
+
+ return pci_wait_for_pending(dev, pci_pcie_cap(dev) + PCI_EXP_DEVSTA,
+ PCI_EXP_DEVSTA_TRPND);
+}
+EXPORT_SYMBOL(pci_wait_for_pending_transaction);
+
+static int pcie_flr(struct pci_dev *dev, int probe)
+{
+ u32 cap;
+
+ pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
+ if (!(cap & PCI_EXP_DEVCAP_FLR))
+ return -ENOTTY;
+
+ if (probe)
+ return 0;
- dev->dma_mask = mask;
+ if (!pci_wait_for_pending_transaction(dev))
+ dev_err(&dev->dev, "transaction is not cleared; proceeding with reset anyway\n");
+
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
+
+ msleep(100);
return 0;
}
-
-int
-pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
+
+static int pci_af_flr(struct pci_dev *dev, int probe)
{
- if (!pci_dma_supported(dev, mask))
- return -EIO;
+ int pos;
+ u8 cap;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_AF);
+ if (!pos)
+ return -ENOTTY;
+
+ pci_read_config_byte(dev, pos + PCI_AF_CAP, &cap);
+ if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR))
+ return -ENOTTY;
+
+ if (probe)
+ return 0;
+
+ /*
+ * Wait for Transaction Pending bit to clear. A word-aligned test
+ * is used, so we use the conrol offset rather than status and shift
+ * the test bit to match.
+ */
+ if (pci_wait_for_pending(dev, pos + PCI_AF_CTRL,
+ PCI_AF_STATUS_TP << 8))
+ goto clear;
+
+ dev_err(&dev->dev, "transaction is not cleared; proceeding with reset anyway\n");
- dev->dev.coherent_dma_mask = mask;
+clear:
+ pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
+ msleep(100);
return 0;
}
-#endif
-#ifndef HAVE_ARCH_PCI_SET_DMA_MAX_SEGMENT_SIZE
-int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size)
+/**
+ * pci_pm_reset - Put device into PCI_D3 and back into PCI_D0.
+ * @dev: Device to reset.
+ * @probe: If set, only check if the device can be reset this way.
+ *
+ * If @dev supports native PCI PM and its PCI_PM_CTRL_NO_SOFT_RESET flag is
+ * unset, it will be reinitialized internally when going from PCI_D3hot to
+ * PCI_D0. If that's the case and the device is not in a low-power state
+ * already, force it into PCI_D3hot and back to PCI_D0, causing it to be reset.
+ *
+ * NOTE: This causes the caller to sleep for twice the device power transition
+ * cooldown period, which for the D0->D3hot and D3hot->D0 transitions is 10 ms
+ * by default (i.e. unless the @dev's d3_delay field has a different value).
+ * Moreover, only devices in D0 can be reset by this function.
+ */
+static int pci_pm_reset(struct pci_dev *dev, int probe)
{
- return dma_set_max_seg_size(&dev->dev, size);
+ u16 csr;
+
+ if (!dev->pm_cap)
+ return -ENOTTY;
+
+ pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &csr);
+ if (csr & PCI_PM_CTRL_NO_SOFT_RESET)
+ return -ENOTTY;
+
+ if (probe)
+ return 0;
+
+ if (dev->current_state != PCI_D0)
+ return -EINVAL;
+
+ csr &= ~PCI_PM_CTRL_STATE_MASK;
+ csr |= PCI_D3hot;
+ pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr);
+ pci_dev_d3_sleep(dev);
+
+ csr &= ~PCI_PM_CTRL_STATE_MASK;
+ csr |= PCI_D0;
+ pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr);
+ pci_dev_d3_sleep(dev);
+
+ return 0;
}
-EXPORT_SYMBOL(pci_set_dma_max_seg_size);
-#endif
-#ifndef HAVE_ARCH_PCI_SET_DMA_SEGMENT_BOUNDARY
-int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask)
+void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)
{
- return dma_set_seg_boundary(&dev->dev, mask);
+ u16 ctrl;
+
+ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
+ ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
+ /*
+ * PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms. Double
+ * this to 2ms to ensure that we meet the minimum requirement.
+ */
+ msleep(2);
+
+ ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
+
+ /*
+ * Trhfa for conventional PCI is 2^25 clock cycles.
+ * Assuming a minimum 33MHz clock this results in a 1s
+ * delay before we can consider subordinate devices to
+ * be re-initialized. PCIe has some ways to shorten this,
+ * but we don't make use of them yet.
+ */
+ ssleep(1);
}
-EXPORT_SYMBOL(pci_set_dma_seg_boundary);
-#endif
+
+/**
+ * pci_reset_bridge_secondary_bus - Reset the secondary bus on a PCI bridge.
+ * @dev: Bridge device
+ *
+ * Use the bridge control register to assert reset on the secondary bus.
+ * Devices on the secondary bus are left in power-on state.
+ */
+void pci_reset_bridge_secondary_bus(struct pci_dev *dev)
+{
+ pcibios_reset_secondary_bus(dev);
+}
+EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus);
+
+static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
+{
+ struct pci_dev *pdev;
+
+ if (pci_is_root_bus(dev->bus) || dev->subordinate || !dev->bus->self)
+ return -ENOTTY;
+
+ list_for_each_entry(pdev, &dev->bus->devices, bus_list)
+ if (pdev != dev)
+ return -ENOTTY;
+
+ if (probe)
+ return 0;
+
+ pci_reset_bridge_secondary_bus(dev->bus->self);
+
+ return 0;
+}
+
+static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, int probe)
+{
+ int rc = -ENOTTY;
+
+ if (!hotplug || !try_module_get(hotplug->ops->owner))
+ return rc;
+
+ if (hotplug->ops->reset_slot)
+ rc = hotplug->ops->reset_slot(hotplug, probe);
+
+ module_put(hotplug->ops->owner);
+
+ return rc;
+}
+
+static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
+{
+ struct pci_dev *pdev;
+
+ if (dev->subordinate || !dev->slot)
+ return -ENOTTY;
+
+ list_for_each_entry(pdev, &dev->bus->devices, bus_list)
+ if (pdev != dev && pdev->slot == dev->slot)
+ return -ENOTTY;
+
+ return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
+}
+
+static int __pci_dev_reset(struct pci_dev *dev, int probe)
+{
+ int rc;
+
+ might_sleep();
+
+ rc = pci_dev_specific_reset(dev, probe);
+ if (rc != -ENOTTY)
+ goto done;
+
+ rc = pcie_flr(dev, probe);
+ if (rc != -ENOTTY)
+ goto done;
+
+ rc = pci_af_flr(dev, probe);
+ if (rc != -ENOTTY)
+ goto done;
+
+ rc = pci_pm_reset(dev, probe);
+ if (rc != -ENOTTY)
+ goto done;
+
+ rc = pci_dev_reset_slot_function(dev, probe);
+ if (rc != -ENOTTY)
+ goto done;
+
+ rc = pci_parent_bus_reset(dev, probe);
+done:
+ return rc;
+}
+
+static void pci_dev_lock(struct pci_dev *dev)
+{
+ pci_cfg_access_lock(dev);
+ /* block PM suspend, driver probe, etc. */
+ device_lock(&dev->dev);
+}
+
+/* Return 1 on successful lock, 0 on contention */
+static int pci_dev_trylock(struct pci_dev *dev)
+{
+ if (pci_cfg_access_trylock(dev)) {
+ if (device_trylock(&dev->dev))
+ return 1;
+ pci_cfg_access_unlock(dev);
+ }
+
+ return 0;
+}
+
+static void pci_dev_unlock(struct pci_dev *dev)
+{
+ device_unlock(&dev->dev);
+ pci_cfg_access_unlock(dev);
+}
+
+/**
+ * pci_reset_notify - notify device driver of reset
+ * @dev: device to be notified of reset
+ * @prepare: 'true' if device is about to be reset; 'false' if reset attempt
+ * completed
+ *
+ * Must be called prior to device access being disabled and after device
+ * access is restored.
+ */
+static void pci_reset_notify(struct pci_dev *dev, bool prepare)
+{
+ const struct pci_error_handlers *err_handler =
+ dev->driver ? dev->driver->err_handler : NULL;
+ if (err_handler && err_handler->reset_notify)
+ err_handler->reset_notify(dev, prepare);
+}
+
+static void pci_dev_save_and_disable(struct pci_dev *dev)
+{
+ pci_reset_notify(dev, true);
+
+ /*
+ * Wake-up device prior to save. PM registers default to D0 after
+ * reset and a simple register restore doesn't reliably return
+ * to a non-D0 state anyway.
+ */
+ pci_set_power_state(dev, PCI_D0);
+
+ pci_save_state(dev);
+ /*
+ * Disable the device by clearing the Command register, except for
+ * INTx-disable which is set. This not only disables MMIO and I/O port
+ * BARs, but also prevents the device from being Bus Master, preventing
+ * DMA from the device including MSI/MSI-X interrupts. For PCI 2.3
+ * compliant devices, INTx-disable prevents legacy interrupts.
+ */
+ pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
+}
+
+static void pci_dev_restore(struct pci_dev *dev)
+{
+ pci_restore_state(dev);
+ pci_reset_notify(dev, false);
+}
+
+static int pci_dev_reset(struct pci_dev *dev, int probe)
+{
+ int rc;
+
+ if (!probe)
+ pci_dev_lock(dev);
+
+ rc = __pci_dev_reset(dev, probe);
+
+ if (!probe)
+ pci_dev_unlock(dev);
+
+ return rc;
+}
+
+/**
+ * __pci_reset_function - reset a PCI device function
+ * @dev: PCI device to reset
+ *
+ * Some devices allow an individual function to be reset without affecting
+ * other functions in the same device. The PCI device must be responsive
+ * to PCI config space in order to use this function.
+ *
+ * The device function is presumed to be unused when this function is called.
+ * Resetting the device will make the contents of PCI configuration space
+ * random, so any caller of this must be prepared to reinitialise the
+ * device including MSI, bus mastering, BARs, decoding IO and memory spaces,
+ * etc.
+ *
+ * Returns 0 if the device function was successfully reset or negative if the
+ * device doesn't support resetting a single function.
+ */
+int __pci_reset_function(struct pci_dev *dev)
+{
+ return pci_dev_reset(dev, 0);
+}
+EXPORT_SYMBOL_GPL(__pci_reset_function);
+
+/**
+ * __pci_reset_function_locked - reset a PCI device function while holding
+ * the @dev mutex lock.
+ * @dev: PCI device to reset
+ *
+ * Some devices allow an individual function to be reset without affecting
+ * other functions in the same device. The PCI device must be responsive
+ * to PCI config space in order to use this function.
+ *
+ * The device function is presumed to be unused and the caller is holding
+ * the device mutex lock when this function is called.
+ * Resetting the device will make the contents of PCI configuration space
+ * random, so any caller of this must be prepared to reinitialise the
+ * device including MSI, bus mastering, BARs, decoding IO and memory spaces,
+ * etc.
+ *
+ * Returns 0 if the device function was successfully reset or negative if the
+ * device doesn't support resetting a single function.
+ */
+int __pci_reset_function_locked(struct pci_dev *dev)
+{
+ return __pci_dev_reset(dev, 0);
+}
+EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
+
+/**
+ * pci_probe_reset_function - check whether the device can be safely reset
+ * @dev: PCI device to reset
+ *
+ * Some devices allow an individual function to be reset without affecting
+ * other functions in the same device. The PCI device must be responsive
+ * to PCI config space in order to use this function.
+ *
+ * Returns 0 if the device function can be reset or negative if the
+ * device doesn't support resetting a single function.
+ */
+int pci_probe_reset_function(struct pci_dev *dev)
+{
+ return pci_dev_reset(dev, 1);
+}
+
+/**
+ * pci_reset_function - quiesce and reset a PCI device function
+ * @dev: PCI device to reset
+ *
+ * Some devices allow an individual function to be reset without affecting
+ * other functions in the same device. The PCI device must be responsive
+ * to PCI config space in order to use this function.
+ *
+ * This function does not just reset the PCI portion of a device, but
+ * clears all the state associated with the device. This function differs
+ * from __pci_reset_function in that it saves and restores device state
+ * over the reset.
+ *
+ * Returns 0 if the device function was successfully reset or negative if the
+ * device doesn't support resetting a single function.
+ */
+int pci_reset_function(struct pci_dev *dev)
+{
+ int rc;
+
+ rc = pci_dev_reset(dev, 1);
+ if (rc)
+ return rc;
+
+ pci_dev_save_and_disable(dev);
+
+ rc = pci_dev_reset(dev, 0);
+
+ pci_dev_restore(dev);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pci_reset_function);
+
+/**
+ * pci_try_reset_function - quiesce and reset a PCI device function
+ * @dev: PCI device to reset
+ *
+ * Same as above, except return -EAGAIN if unable to lock device.
+ */
+int pci_try_reset_function(struct pci_dev *dev)
+{
+ int rc;
+
+ rc = pci_dev_reset(dev, 1);
+ if (rc)
+ return rc;
+
+ pci_dev_save_and_disable(dev);
+
+ if (pci_dev_trylock(dev)) {
+ rc = __pci_dev_reset(dev, 0);
+ pci_dev_unlock(dev);
+ } else
+ rc = -EAGAIN;
+
+ pci_dev_restore(dev);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pci_try_reset_function);
+
+/* Lock devices from the top of the tree down */
+static void pci_bus_lock(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ pci_dev_lock(dev);
+ if (dev->subordinate)
+ pci_bus_lock(dev->subordinate);
+ }
+}
+
+/* Unlock devices from the bottom of the tree up */
+static void pci_bus_unlock(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ if (dev->subordinate)
+ pci_bus_unlock(dev->subordinate);
+ pci_dev_unlock(dev);
+ }
+}
+
+/* Return 1 on successful lock, 0 on contention */
+static int pci_bus_trylock(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ if (!pci_dev_trylock(dev))
+ goto unlock;
+ if (dev->subordinate) {
+ if (!pci_bus_trylock(dev->subordinate)) {
+ pci_dev_unlock(dev);
+ goto unlock;
+ }
+ }
+ }
+ return 1;
+
+unlock:
+ list_for_each_entry_continue_reverse(dev, &bus->devices, bus_list) {
+ if (dev->subordinate)
+ pci_bus_unlock(dev->subordinate);
+ pci_dev_unlock(dev);
+ }
+ return 0;
+}
+
+/* Lock devices from the top of the tree down */
+static void pci_slot_lock(struct pci_slot *slot)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+ if (!dev->slot || dev->slot != slot)
+ continue;
+ pci_dev_lock(dev);
+ if (dev->subordinate)
+ pci_bus_lock(dev->subordinate);
+ }
+}
+
+/* Unlock devices from the bottom of the tree up */
+static void pci_slot_unlock(struct pci_slot *slot)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+ if (!dev->slot || dev->slot != slot)
+ continue;
+ if (dev->subordinate)
+ pci_bus_unlock(dev->subordinate);
+ pci_dev_unlock(dev);
+ }
+}
+
+/* Return 1 on successful lock, 0 on contention */
+static int pci_slot_trylock(struct pci_slot *slot)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+ if (!dev->slot || dev->slot != slot)
+ continue;
+ if (!pci_dev_trylock(dev))
+ goto unlock;
+ if (dev->subordinate) {
+ if (!pci_bus_trylock(dev->subordinate)) {
+ pci_dev_unlock(dev);
+ goto unlock;
+ }
+ }
+ }
+ return 1;
+
+unlock:
+ list_for_each_entry_continue_reverse(dev,
+ &slot->bus->devices, bus_list) {
+ if (!dev->slot || dev->slot != slot)
+ continue;
+ if (dev->subordinate)
+ pci_bus_unlock(dev->subordinate);
+ pci_dev_unlock(dev);
+ }
+ return 0;
+}
+
+/* Save and disable devices from the top of the tree down */
+static void pci_bus_save_and_disable(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ pci_dev_save_and_disable(dev);
+ if (dev->subordinate)
+ pci_bus_save_and_disable(dev->subordinate);
+ }
+}
+
+/*
+ * Restore devices from top of the tree down - parent bridges need to be
+ * restored before we can get to subordinate devices.
+ */
+static void pci_bus_restore(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ pci_dev_restore(dev);
+ if (dev->subordinate)
+ pci_bus_restore(dev->subordinate);
+ }
+}
+
+/* Save and disable devices from the top of the tree down */
+static void pci_slot_save_and_disable(struct pci_slot *slot)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+ if (!dev->slot || dev->slot != slot)
+ continue;
+ pci_dev_save_and_disable(dev);
+ if (dev->subordinate)
+ pci_bus_save_and_disable(dev->subordinate);
+ }
+}
+
+/*
+ * Restore devices from top of the tree down - parent bridges need to be
+ * restored before we can get to subordinate devices.
+ */
+static void pci_slot_restore(struct pci_slot *slot)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+ if (!dev->slot || dev->slot != slot)
+ continue;
+ pci_dev_restore(dev);
+ if (dev->subordinate)
+ pci_bus_restore(dev->subordinate);
+ }
+}
+
+static int pci_slot_reset(struct pci_slot *slot, int probe)
+{
+ int rc;
+
+ if (!slot)
+ return -ENOTTY;
+
+ if (!probe)
+ pci_slot_lock(slot);
+
+ might_sleep();
+
+ rc = pci_reset_hotplug_slot(slot->hotplug, probe);
+
+ if (!probe)
+ pci_slot_unlock(slot);
+
+ return rc;
+}
+
+/**
+ * pci_probe_reset_slot - probe whether a PCI slot can be reset
+ * @slot: PCI slot to probe
+ *
+ * Return 0 if slot can be reset, negative if a slot reset is not supported.
+ */
+int pci_probe_reset_slot(struct pci_slot *slot)
+{
+ return pci_slot_reset(slot, 1);
+}
+EXPORT_SYMBOL_GPL(pci_probe_reset_slot);
+
+/**
+ * pci_reset_slot - reset a PCI slot
+ * @slot: PCI slot to reset
+ *
+ * A PCI bus may host multiple slots, each slot may support a reset mechanism
+ * independent of other slots. For instance, some slots may support slot power
+ * control. In the case of a 1:1 bus to slot architecture, this function may
+ * wrap the bus reset to avoid spurious slot related events such as hotplug.
+ * Generally a slot reset should be attempted before a bus reset. All of the
+ * function of the slot and any subordinate buses behind the slot are reset
+ * through this function. PCI config space of all devices in the slot and
+ * behind the slot is saved before and restored after reset.
+ *
+ * Return 0 on success, non-zero on error.
+ */
+int pci_reset_slot(struct pci_slot *slot)
+{
+ int rc;
+
+ rc = pci_slot_reset(slot, 1);
+ if (rc)
+ return rc;
+
+ pci_slot_save_and_disable(slot);
+
+ rc = pci_slot_reset(slot, 0);
+
+ pci_slot_restore(slot);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pci_reset_slot);
+
+/**
+ * pci_try_reset_slot - Try to reset a PCI slot
+ * @slot: PCI slot to reset
+ *
+ * Same as above except return -EAGAIN if the slot cannot be locked
+ */
+int pci_try_reset_slot(struct pci_slot *slot)
+{
+ int rc;
+
+ rc = pci_slot_reset(slot, 1);
+ if (rc)
+ return rc;
+
+ pci_slot_save_and_disable(slot);
+
+ if (pci_slot_trylock(slot)) {
+ might_sleep();
+ rc = pci_reset_hotplug_slot(slot->hotplug, 0);
+ pci_slot_unlock(slot);
+ } else
+ rc = -EAGAIN;
+
+ pci_slot_restore(slot);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pci_try_reset_slot);
+
+static int pci_bus_reset(struct pci_bus *bus, int probe)
+{
+ if (!bus->self)
+ return -ENOTTY;
+
+ if (probe)
+ return 0;
+
+ pci_bus_lock(bus);
+
+ might_sleep();
+
+ pci_reset_bridge_secondary_bus(bus->self);
+
+ pci_bus_unlock(bus);
+
+ return 0;
+}
+
+/**
+ * pci_probe_reset_bus - probe whether a PCI bus can be reset
+ * @bus: PCI bus to probe
+ *
+ * Return 0 if bus can be reset, negative if a bus reset is not supported.
+ */
+int pci_probe_reset_bus(struct pci_bus *bus)
+{
+ return pci_bus_reset(bus, 1);
+}
+EXPORT_SYMBOL_GPL(pci_probe_reset_bus);
+
+/**
+ * pci_reset_bus - reset a PCI bus
+ * @bus: top level PCI bus to reset
+ *
+ * Do a bus reset on the given bus and any subordinate buses, saving
+ * and restoring state of all devices.
+ *
+ * Return 0 on success, non-zero on error.
+ */
+int pci_reset_bus(struct pci_bus *bus)
+{
+ int rc;
+
+ rc = pci_bus_reset(bus, 1);
+ if (rc)
+ return rc;
+
+ pci_bus_save_and_disable(bus);
+
+ rc = pci_bus_reset(bus, 0);
+
+ pci_bus_restore(bus);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pci_reset_bus);
+
+/**
+ * pci_try_reset_bus - Try to reset a PCI bus
+ * @bus: top level PCI bus to reset
+ *
+ * Same as above except return -EAGAIN if the bus cannot be locked
+ */
+int pci_try_reset_bus(struct pci_bus *bus)
+{
+ int rc;
+
+ rc = pci_bus_reset(bus, 1);
+ if (rc)
+ return rc;
+
+ pci_bus_save_and_disable(bus);
+
+ if (pci_bus_trylock(bus)) {
+ might_sleep();
+ pci_reset_bridge_secondary_bus(bus->self);
+ pci_bus_unlock(bus);
+ } else
+ rc = -EAGAIN;
+
+ pci_bus_restore(bus);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pci_try_reset_bus);
/**
* pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
@@ -1456,18 +3884,17 @@ EXPORT_SYMBOL(pci_set_dma_seg_boundary);
*/
int pcix_get_max_mmrbc(struct pci_dev *dev)
{
- int err, cap;
+ int cap;
u32 stat;
cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!cap)
return -EINVAL;
- err = pci_read_config_dword(dev, cap + PCI_X_STATUS, &stat);
- if (err)
+ if (pci_read_config_dword(dev, cap + PCI_X_STATUS, &stat))
return -EINVAL;
- return (stat & PCI_X_STATUS_MAX_READ) >> 12;
+ return 512 << ((stat & PCI_X_STATUS_MAX_READ) >> 21);
}
EXPORT_SYMBOL(pcix_get_max_mmrbc);
@@ -1480,18 +3907,17 @@ EXPORT_SYMBOL(pcix_get_max_mmrbc);
*/
int pcix_get_mmrbc(struct pci_dev *dev)
{
- int ret, cap;
- u32 cmd;
+ int cap;
+ u16 cmd;
cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!cap)
return -EINVAL;
- ret = pci_read_config_dword(dev, cap + PCI_X_CMD, &cmd);
- if (!ret)
- ret = 512 << ((cmd & PCI_X_CMD_MAX_READ) >> 2);
+ if (pci_read_config_word(dev, cap + PCI_X_CMD, &cmd))
+ return -EINVAL;
- return ret;
+ return 512 << ((cmd & PCI_X_CMD_MAX_READ) >> 2);
}
EXPORT_SYMBOL(pcix_get_mmrbc);
@@ -1506,41 +3932,39 @@ EXPORT_SYMBOL(pcix_get_mmrbc);
*/
int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc)
{
- int cap, err = -EINVAL;
- u32 stat, cmd, v, o;
+ int cap;
+ u32 stat, v, o;
+ u16 cmd;
if (mmrbc < 512 || mmrbc > 4096 || !is_power_of_2(mmrbc))
- goto out;
+ return -EINVAL;
v = ffs(mmrbc) - 10;
cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!cap)
- goto out;
+ return -EINVAL;
- err = pci_read_config_dword(dev, cap + PCI_X_STATUS, &stat);
- if (err)
- goto out;
+ if (pci_read_config_dword(dev, cap + PCI_X_STATUS, &stat))
+ return -EINVAL;
if (v > (stat & PCI_X_STATUS_MAX_READ) >> 21)
return -E2BIG;
- err = pci_read_config_dword(dev, cap + PCI_X_CMD, &cmd);
- if (err)
- goto out;
+ if (pci_read_config_word(dev, cap + PCI_X_CMD, &cmd))
+ return -EINVAL;
o = (cmd & PCI_X_CMD_MAX_READ) >> 2;
if (o != v) {
- if (v > o && dev->bus &&
- (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MMRBC))
+ if (v > o && (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MMRBC))
return -EIO;
cmd &= ~PCI_X_CMD_MAX_READ;
cmd |= v << 2;
- err = pci_write_config_dword(dev, cap + PCI_X_CMD, cmd);
+ if (pci_write_config_word(dev, cap + PCI_X_CMD, cmd))
+ return -EIO;
}
-out:
- return err;
+ return 0;
}
EXPORT_SYMBOL(pcix_set_mmrbc);
@@ -1553,18 +3977,11 @@ EXPORT_SYMBOL(pcix_set_mmrbc);
*/
int pcie_get_readrq(struct pci_dev *dev)
{
- int ret, cap;
u16 ctl;
- cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!cap)
- return -EINVAL;
-
- ret = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
- if (!ret)
- ret = 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12);
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);
- return ret;
+ return 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12);
}
EXPORT_SYMBOL(pcie_get_readrq);
@@ -1574,36 +3991,118 @@ EXPORT_SYMBOL(pcie_get_readrq);
* @rq: maximum memory read count in bytes
* valid values are 128, 256, 512, 1024, 2048, 4096
*
- * If possible sets maximum read byte count
+ * If possible sets maximum memory read request in bytes
*/
int pcie_set_readrq(struct pci_dev *dev, int rq)
{
- int cap, err = -EINVAL;
- u16 ctl, v;
+ u16 v;
if (rq < 128 || rq > 4096 || !is_power_of_2(rq))
- goto out;
+ return -EINVAL;
+
+ /*
+ * If using the "performance" PCIe config, we clamp the
+ * read rq size to the max packet size to prevent the
+ * host bridge generating requests larger than we can
+ * cope with
+ */
+ if (pcie_bus_config == PCIE_BUS_PERFORMANCE) {
+ int mps = pcie_get_mps(dev);
+
+ if (mps < rq)
+ rq = mps;
+ }
v = (ffs(rq) - 8) << 12;
- cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!cap)
- goto out;
+ return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_READRQ, v);
+}
+EXPORT_SYMBOL(pcie_set_readrq);
+
+/**
+ * pcie_get_mps - get PCI Express maximum payload size
+ * @dev: PCI device to query
+ *
+ * Returns maximum payload size in bytes
+ */
+int pcie_get_mps(struct pci_dev *dev)
+{
+ u16 ctl;
+
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);
+
+ return 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
+}
+EXPORT_SYMBOL(pcie_get_mps);
+
+/**
+ * pcie_set_mps - set PCI Express maximum payload size
+ * @dev: PCI device to query
+ * @mps: maximum payload size in bytes
+ * valid values are 128, 256, 512, 1024, 2048, 4096
+ *
+ * If possible sets maximum payload size
+ */
+int pcie_set_mps(struct pci_dev *dev, int mps)
+{
+ u16 v;
+
+ if (mps < 128 || mps > 4096 || !is_power_of_2(mps))
+ return -EINVAL;
+
+ v = ffs(mps) - 8;
+ if (v > dev->pcie_mpss)
+ return -EINVAL;
+ v <<= 5;
+
+ return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_PAYLOAD, v);
+}
+EXPORT_SYMBOL(pcie_set_mps);
+
+/**
+ * pcie_get_minimum_link - determine minimum link settings of a PCI device
+ * @dev: PCI device to query
+ * @speed: storage for minimum speed
+ * @width: storage for minimum width
+ *
+ * This function will walk up the PCI device chain and determine the minimum
+ * link width and speed of the device.
+ */
+int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed,
+ enum pcie_link_width *width)
+{
+ int ret;
+
+ *speed = PCI_SPEED_UNKNOWN;
+ *width = PCIE_LNK_WIDTH_UNKNOWN;
+
+ while (dev) {
+ u16 lnksta;
+ enum pci_bus_speed next_speed;
+ enum pcie_link_width next_width;
- err = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
- if (err)
- goto out;
+ ret = pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
+ if (ret)
+ return ret;
+
+ next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS];
+ next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >>
+ PCI_EXP_LNKSTA_NLW_SHIFT;
+
+ if (next_speed < *speed)
+ *speed = next_speed;
- if ((ctl & PCI_EXP_DEVCTL_READRQ) != v) {
- ctl &= ~PCI_EXP_DEVCTL_READRQ;
- ctl |= v;
- err = pci_write_config_dword(dev, cap + PCI_EXP_DEVCTL, ctl);
+ if (next_width < *width)
+ *width = next_width;
+
+ dev = dev->bus->self;
}
-out:
- return err;
+ return 0;
}
-EXPORT_SYMBOL(pcie_set_readrq);
+EXPORT_SYMBOL(pcie_get_minimum_link);
/**
* pci_select_bars - Make BAR mask from the type of resource
@@ -1620,25 +4119,306 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags)
bars |= (1 << i);
return bars;
}
+EXPORT_SYMBOL(pci_select_bars);
+
+/**
+ * pci_resource_bar - get position of the BAR associated with a resource
+ * @dev: the PCI device
+ * @resno: the resource number
+ * @type: the BAR type to be filled in
+ *
+ * Returns BAR position in config space, or 0 if the BAR is invalid.
+ */
+int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type)
+{
+ int reg;
+
+ if (resno < PCI_ROM_RESOURCE) {
+ *type = pci_bar_unknown;
+ return PCI_BASE_ADDRESS_0 + 4 * resno;
+ } else if (resno == PCI_ROM_RESOURCE) {
+ *type = pci_bar_mem32;
+ return dev->rom_base_reg;
+ } else if (resno < PCI_BRIDGE_RESOURCES) {
+ /* device specific resource */
+ reg = pci_iov_resource_bar(dev, resno, type);
+ if (reg)
+ return reg;
+ }
+
+ dev_err(&dev->dev, "BAR %d: invalid resource\n", resno);
+ return 0;
+}
+
+/* Some architectures require additional programming to enable VGA */
+static arch_set_vga_state_t arch_set_vga_state;
+
+void __init pci_register_set_vga_state(arch_set_vga_state_t func)
+{
+ arch_set_vga_state = func; /* NULL disables */
+}
+
+static int pci_set_vga_state_arch(struct pci_dev *dev, bool decode,
+ unsigned int command_bits, u32 flags)
+{
+ if (arch_set_vga_state)
+ return arch_set_vga_state(dev, decode, command_bits,
+ flags);
+ return 0;
+}
+
+/**
+ * pci_set_vga_state - set VGA decode state on device and parents if requested
+ * @dev: the PCI device
+ * @decode: true = enable decoding, false = disable decoding
+ * @command_bits: PCI_COMMAND_IO and/or PCI_COMMAND_MEMORY
+ * @flags: traverse ancestors and change bridges
+ * CHANGE_BRIDGE_ONLY / CHANGE_BRIDGE
+ */
+int pci_set_vga_state(struct pci_dev *dev, bool decode,
+ unsigned int command_bits, u32 flags)
+{
+ struct pci_bus *bus;
+ struct pci_dev *bridge;
+ u16 cmd;
+ int rc;
+
+ WARN_ON((flags & PCI_VGA_STATE_CHANGE_DECODES) && (command_bits & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY)));
+
+ /* ARCH specific VGA enables */
+ rc = pci_set_vga_state_arch(dev, decode, command_bits, flags);
+ if (rc)
+ return rc;
+
+ if (flags & PCI_VGA_STATE_CHANGE_DECODES) {
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if (decode == true)
+ cmd |= command_bits;
+ else
+ cmd &= ~command_bits;
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+
+ if (!(flags & PCI_VGA_STATE_CHANGE_BRIDGE))
+ return 0;
+
+ bus = dev->bus;
+ while (bus) {
+ bridge = bus->self;
+ if (bridge) {
+ pci_read_config_word(bridge, PCI_BRIDGE_CONTROL,
+ &cmd);
+ if (decode == true)
+ cmd |= PCI_BRIDGE_CTL_VGA;
+ else
+ cmd &= ~PCI_BRIDGE_CTL_VGA;
+ pci_write_config_word(bridge, PCI_BRIDGE_CONTROL,
+ cmd);
+ }
+ bus = bus->parent;
+ }
+ return 0;
+}
-static void __devinit pci_no_domains(void)
+bool pci_device_is_present(struct pci_dev *pdev)
+{
+ u32 v;
+
+ return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0);
+}
+EXPORT_SYMBOL_GPL(pci_device_is_present);
+
+#define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE
+static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0};
+static DEFINE_SPINLOCK(resource_alignment_lock);
+
+/**
+ * pci_specified_resource_alignment - get resource alignment specified by user.
+ * @dev: the PCI device to get
+ *
+ * RETURNS: Resource alignment if it is specified.
+ * Zero if it is not specified.
+ */
+static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev)
+{
+ int seg, bus, slot, func, align_order, count;
+ resource_size_t align = 0;
+ char *p;
+
+ spin_lock(&resource_alignment_lock);
+ p = resource_alignment_param;
+ while (*p) {
+ count = 0;
+ if (sscanf(p, "%d%n", &align_order, &count) == 1 &&
+ p[count] == '@') {
+ p += count + 1;
+ } else {
+ align_order = -1;
+ }
+ if (sscanf(p, "%x:%x:%x.%x%n",
+ &seg, &bus, &slot, &func, &count) != 4) {
+ seg = 0;
+ if (sscanf(p, "%x:%x.%x%n",
+ &bus, &slot, &func, &count) != 3) {
+ /* Invalid format */
+ printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: %s\n",
+ p);
+ break;
+ }
+ }
+ p += count;
+ if (seg == pci_domain_nr(dev->bus) &&
+ bus == dev->bus->number &&
+ slot == PCI_SLOT(dev->devfn) &&
+ func == PCI_FUNC(dev->devfn)) {
+ if (align_order == -1)
+ align = PAGE_SIZE;
+ else
+ align = 1 << align_order;
+ /* Found */
+ break;
+ }
+ if (*p != ';' && *p != ',') {
+ /* End of param or invalid format */
+ break;
+ }
+ p++;
+ }
+ spin_unlock(&resource_alignment_lock);
+ return align;
+}
+
+/*
+ * This function disables memory decoding and releases memory resources
+ * of the device specified by kernel's boot parameter 'pci=resource_alignment='.
+ * It also rounds up size to specified alignment.
+ * Later on, the kernel will assign page-aligned memory resource back
+ * to the device.
+ */
+void pci_reassigndev_resource_alignment(struct pci_dev *dev)
+{
+ int i;
+ struct resource *r;
+ resource_size_t align, size;
+ u16 command;
+
+ /* check if specified PCI is target device to reassign */
+ align = pci_specified_resource_alignment(dev);
+ if (!align)
+ return;
+
+ if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL &&
+ (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) {
+ dev_warn(&dev->dev,
+ "Can't reassign resources to host bridge.\n");
+ return;
+ }
+
+ dev_info(&dev->dev,
+ "Disabling memory decoding and releasing memory resources.\n");
+ pci_read_config_word(dev, PCI_COMMAND, &command);
+ command &= ~PCI_COMMAND_MEMORY;
+ pci_write_config_word(dev, PCI_COMMAND, command);
+
+ for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) {
+ r = &dev->resource[i];
+ if (!(r->flags & IORESOURCE_MEM))
+ continue;
+ size = resource_size(r);
+ if (size < align) {
+ size = align;
+ dev_info(&dev->dev,
+ "Rounding up size of resource #%d to %#llx.\n",
+ i, (unsigned long long)size);
+ }
+ r->flags |= IORESOURCE_UNSET;
+ r->end = size - 1;
+ r->start = 0;
+ }
+ /* Need to disable bridge's resource window,
+ * to enable the kernel to reassign new resource
+ * window later on.
+ */
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
+ (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+ for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
+ r = &dev->resource[i];
+ if (!(r->flags & IORESOURCE_MEM))
+ continue;
+ r->flags |= IORESOURCE_UNSET;
+ r->end = resource_size(r) - 1;
+ r->start = 0;
+ }
+ pci_disable_bridge_window(dev);
+ }
+}
+
+static ssize_t pci_set_resource_alignment_param(const char *buf, size_t count)
+{
+ if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1)
+ count = RESOURCE_ALIGNMENT_PARAM_SIZE - 1;
+ spin_lock(&resource_alignment_lock);
+ strncpy(resource_alignment_param, buf, count);
+ resource_alignment_param[count] = '\0';
+ spin_unlock(&resource_alignment_lock);
+ return count;
+}
+
+static ssize_t pci_get_resource_alignment_param(char *buf, size_t size)
+{
+ size_t count;
+ spin_lock(&resource_alignment_lock);
+ count = snprintf(buf, size, "%s", resource_alignment_param);
+ spin_unlock(&resource_alignment_lock);
+ return count;
+}
+
+static ssize_t pci_resource_alignment_show(struct bus_type *bus, char *buf)
+{
+ return pci_get_resource_alignment_param(buf, PAGE_SIZE);
+}
+
+static ssize_t pci_resource_alignment_store(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ return pci_set_resource_alignment_param(buf, count);
+}
+
+BUS_ATTR(resource_alignment, 0644, pci_resource_alignment_show,
+ pci_resource_alignment_store);
+
+static int __init pci_resource_alignment_sysfs_init(void)
+{
+ return bus_create_file(&pci_bus_type,
+ &bus_attr_resource_alignment);
+}
+late_initcall(pci_resource_alignment_sysfs_init);
+
+static void pci_no_domains(void)
{
#ifdef CONFIG_PCI_DOMAINS
pci_domains_supported = 0;
#endif
}
-static int __devinit pci_init(void)
+/**
+ * pci_ext_cfg_avail - can we access extended PCI config space?
+ *
+ * Returns 1 if we can access PCI extended config space (offsets
+ * greater than 0xff). This is the default implementation. Architecture
+ * implementations can override this.
+ */
+int __weak pci_ext_cfg_avail(void)
{
- struct pci_dev *dev = NULL;
+ return 1;
+}
- while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
- pci_fixup_device(pci_fixup_final, dev);
- }
- return 0;
+void __weak pci_fixup_cardbus(struct pci_bus *bus)
+{
}
+EXPORT_SYMBOL(pci_fixup_cardbus);
-static int __devinit pci_setup(char *str)
+static int __init pci_setup(char *str)
{
while (str) {
char *k = strchr(str, ',');
@@ -1649,12 +4429,37 @@ static int __devinit pci_setup(char *str)
pci_no_msi();
} else if (!strcmp(str, "noaer")) {
pci_no_aer();
+ } else if (!strncmp(str, "realloc=", 8)) {
+ pci_realloc_get_opt(str + 8);
+ } else if (!strncmp(str, "realloc", 7)) {
+ pci_realloc_get_opt("on");
} else if (!strcmp(str, "nodomains")) {
pci_no_domains();
+ } else if (!strncmp(str, "noari", 5)) {
+ pcie_ari_disabled = true;
} else if (!strncmp(str, "cbiosize=", 9)) {
pci_cardbus_io_size = memparse(str + 9, &str);
} else if (!strncmp(str, "cbmemsize=", 10)) {
pci_cardbus_mem_size = memparse(str + 10, &str);
+ } else if (!strncmp(str, "resource_alignment=", 19)) {
+ pci_set_resource_alignment_param(str + 19,
+ strlen(str + 19));
+ } else if (!strncmp(str, "ecrc=", 5)) {
+ pcie_ecrc_get_policy(str + 5);
+ } else if (!strncmp(str, "hpiosize=", 9)) {
+ pci_hotplug_io_size = memparse(str + 9, &str);
+ } else if (!strncmp(str, "hpmemsize=", 10)) {
+ pci_hotplug_mem_size = memparse(str + 10, &str);
+ } else if (!strncmp(str, "pcie_bus_tune_off", 17)) {
+ pcie_bus_config = PCIE_BUS_TUNE_OFF;
+ } else if (!strncmp(str, "pcie_bus_safe", 13)) {
+ pcie_bus_config = PCIE_BUS_SAFE;
+ } else if (!strncmp(str, "pcie_bus_perf", 13)) {
+ pcie_bus_config = PCIE_BUS_PERFORMANCE;
+ } else if (!strncmp(str, "pcie_bus_peer2peer", 18)) {
+ pcie_bus_config = PCIE_BUS_PEER2PEER;
+ } else if (!strncmp(str, "pcie_scan_all", 13)) {
+ pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS);
} else {
printk(KERN_ERR "PCI: Unknown option `%s'\n",
str);
@@ -1665,38 +4470,3 @@ static int __devinit pci_setup(char *str)
return 0;
}
early_param("pci", pci_setup);
-
-device_initcall(pci_init);
-
-EXPORT_SYMBOL(pci_reenable_device);
-EXPORT_SYMBOL(pci_enable_device_io);
-EXPORT_SYMBOL(pci_enable_device_mem);
-EXPORT_SYMBOL(pci_enable_device);
-EXPORT_SYMBOL(pcim_enable_device);
-EXPORT_SYMBOL(pcim_pin_device);
-EXPORT_SYMBOL(pci_disable_device);
-EXPORT_SYMBOL(pci_find_capability);
-EXPORT_SYMBOL(pci_bus_find_capability);
-EXPORT_SYMBOL(pci_release_regions);
-EXPORT_SYMBOL(pci_request_regions);
-EXPORT_SYMBOL(pci_release_region);
-EXPORT_SYMBOL(pci_request_region);
-EXPORT_SYMBOL(pci_release_selected_regions);
-EXPORT_SYMBOL(pci_request_selected_regions);
-EXPORT_SYMBOL(pci_set_master);
-EXPORT_SYMBOL(pci_set_mwi);
-EXPORT_SYMBOL(pci_try_set_mwi);
-EXPORT_SYMBOL(pci_clear_mwi);
-EXPORT_SYMBOL_GPL(pci_intx);
-EXPORT_SYMBOL(pci_set_dma_mask);
-EXPORT_SYMBOL(pci_set_consistent_dma_mask);
-EXPORT_SYMBOL(pci_assign_resource);
-EXPORT_SYMBOL(pci_find_parent_resource);
-EXPORT_SYMBOL(pci_select_bars);
-
-EXPORT_SYMBOL(pci_set_power_state);
-EXPORT_SYMBOL(pci_save_state);
-EXPORT_SYMBOL(pci_restore_state);
-EXPORT_SYMBOL(pci_enable_wake);
-EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
-
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index eabeb1f2ec9..0601890db22 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1,28 +1,111 @@
+#ifndef DRIVERS_PCI_H
+#define DRIVERS_PCI_H
+
+#define PCI_CFG_SPACE_SIZE 256
+#define PCI_CFG_SPACE_EXP_SIZE 4096
+
+extern const unsigned char pcie_link_speed[];
+
/* Functions internal to the PCI core code */
-extern int pci_uevent(struct device *dev, struct kobj_uevent_env *env);
-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);
+int pci_create_sysfs_dev_files(struct pci_dev *pdev);
+void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
+#if !defined(CONFIG_DMI) && !defined(CONFIG_ACPI)
+static inline void pci_create_firmware_label_files(struct pci_dev *pdev)
+{ return; }
+static inline void pci_remove_firmware_label_files(struct pci_dev *pdev)
+{ return; }
+#else
+void pci_create_firmware_label_files(struct pci_dev *pdev);
+void pci_remove_firmware_label_files(struct pci_dev *pdev);
+#endif
+void pci_cleanup_rom(struct pci_dev *dev);
+#ifdef HAVE_PCI_MMAP
+enum pci_mmap_api {
+ PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */
+ PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/<BDF> */
+};
+int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
+ enum pci_mmap_api mmap_api);
+#endif
+int pci_probe_reset_function(struct pci_dev *dev);
+
+/**
+ * struct pci_platform_pm_ops - 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
+ *
+ * @sleep_wake: enables/disables the system wake up capability of given device
+ *
+ * @run_wake: enables/disables the platform to generate run-time wake-up events
+ * for given device (the device's wake-up capability has to be
+ * enabled by @sleep_wake for this feature to work)
+ *
+ * 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);
+ int (*sleep_wake)(struct pci_dev *dev, bool enable);
+ int (*run_wake)(struct pci_dev *dev, bool enable);
+};
+
+int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
+void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
+void pci_power_up(struct pci_dev *dev);
+void pci_disable_enabled_device(struct pci_dev *dev);
+int pci_finish_runtime_suspend(struct pci_dev *dev);
+int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
+void pci_config_pm_runtime_get(struct pci_dev *dev);
+void pci_config_pm_runtime_put(struct pci_dev *dev);
+void pci_pm_init(struct pci_dev *dev);
+void pci_allocate_cap_save_buffers(struct pci_dev *dev);
+void pci_free_cap_save_buffers(struct pci_dev *dev);
+
+static inline void pci_wakeup_event(struct pci_dev *dev)
+{
+ /* Wait 100 ms before the system can be put into a sleep state. */
+ pm_wakeup_event(&dev->dev, 100);
+}
+
+static inline bool pci_has_subordinate(struct pci_dev *pci_dev)
+{
+ return !!(pci_dev->subordinate);
+}
-/* 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);
+struct pci_vpd_ops {
+ ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
+ ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
+ void (*release)(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);
-extern int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val);
-extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val);
-extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val);
-extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);
+struct pci_vpd {
+ unsigned int len;
+ const struct pci_vpd_ops *ops;
+ struct bin_attribute *attr; /* descriptor for sysfs VPD entry */
+};
+
+int pci_vpd_pci22_init(struct pci_dev *dev);
+static inline void pci_vpd_release(struct pci_dev *dev)
+{
+ if (dev->vpd)
+ dev->vpd->ops->release(dev);
+}
/* PCI /proc functions */
#ifdef CONFIG_PROC_FS
-extern int pci_proc_attach_device(struct pci_dev *dev);
-extern int pci_proc_detach_device(struct pci_dev *dev);
-extern int pci_proc_detach_bus(struct pci_bus *bus);
+int pci_proc_attach_device(struct pci_dev *dev);
+int pci_proc_detach_device(struct pci_dev *dev);
+int pci_proc_detach_bus(struct pci_bus *bus);
#else
static inline int pci_proc_attach_device(struct pci_dev *dev) { return 0; }
static inline int pci_proc_detach_device(struct pci_dev *dev) { return 0; }
@@ -30,28 +113,32 @@ static inline int pci_proc_detach_bus(struct pci_bus *bus) { return 0; }
#endif
/* Functions for PCI Hotplug drivers to use */
-extern unsigned int pci_do_scan_bus(struct pci_bus *bus);
+int pci_hp_add_bridge(struct pci_dev *dev);
-extern void pci_remove_legacy_files(struct pci_bus *bus);
+#ifdef HAVE_PCI_LEGACY
+void pci_create_legacy_files(struct pci_bus *bus);
+void pci_remove_legacy_files(struct pci_bus *bus);
+#else
+static inline void pci_create_legacy_files(struct pci_bus *bus) { return; }
+static inline void pci_remove_legacy_files(struct pci_bus *bus) { return; }
+#endif
/* Lock for read/write access to pci device and bus lists */
extern struct rw_semaphore pci_bus_sem;
+extern raw_spinlock_t pci_lock;
+
extern unsigned int pci_pm_d3_delay;
#ifdef CONFIG_PCI_MSI
void pci_no_msi(void);
-extern void pci_msi_init_pci_dev(struct pci_dev *dev);
+void pci_msi_init_pci_dev(struct pci_dev *dev);
#else
static inline void pci_no_msi(void) { }
static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { }
#endif
-#ifdef CONFIG_PCIEAER
-void pci_no_aer(void);
-#else
-static inline void pci_no_aer(void) { }
-#endif
+void pci_realloc_get_opt(char *);
static inline int pci_no_d1d2(struct pci_dev *dev)
{
@@ -62,9 +149,11 @@ static inline int pci_no_d1d2(struct pci_dev *dev)
return (dev->no_d1d2 || parent_dstates);
}
-extern int pcie_mch_quirk;
-extern struct device_attribute pci_dev_attrs[];
-extern struct device_attribute dev_attr_cpuaffinity;
+extern const struct attribute_group *pci_dev_groups[];
+extern const struct attribute_group *pcibus_groups[];
+extern struct device_type pci_dev_type;
+extern const struct attribute_group *pci_bus_groups[];
+
/**
* pci_match_one_device - Tell if a PCI device structure has a matching
@@ -86,4 +175,143 @@ pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
return NULL;
}
-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)
+
+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 */
+};
+
+bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
+ int crs_timeout);
+int pci_setup_device(struct pci_dev *dev);
+int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
+ struct resource *res, unsigned int reg);
+int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type);
+void pci_configure_ari(struct pci_dev *dev);
+void __pci_bus_size_bridges(struct pci_bus *bus,
+ struct list_head *realloc_head);
+void __pci_bus_assign_resources(const struct pci_bus *bus,
+ struct list_head *realloc_head,
+ struct list_head *fail_head);
+
+/**
+ * pci_ari_enabled - query ARI forwarding status
+ * @bus: the PCI bus
+ *
+ * Returns 1 if ARI forwarding is enabled, or 0 if not enabled;
+ */
+static inline int pci_ari_enabled(struct pci_bus *bus)
+{
+ return bus->self && bus->self->ari_enabled;
+}
+
+void pci_reassigndev_resource_alignment(struct pci_dev *dev);
+void pci_disable_bridge_window(struct pci_dev *dev);
+
+/* Single Root I/O Virtualization */
+struct pci_sriov {
+ int pos; /* capability position */
+ int nres; /* number of resources */
+ u32 cap; /* SR-IOV Capabilities */
+ u16 ctrl; /* SR-IOV Control */
+ u16 total_VFs; /* total VFs associated with the PF */
+ u16 initial_VFs; /* initial VFs associated with the PF */
+ u16 num_VFs; /* number of VFs available */
+ u16 offset; /* first VF Routing ID offset */
+ u16 stride; /* following VF stride */
+ u32 pgsz; /* page size for BAR alignment */
+ u8 link; /* Function Dependency Link */
+ u16 driver_max_VFs; /* max num VFs driver supports */
+ struct pci_dev *dev; /* lowest numbered PF */
+ struct pci_dev *self; /* this PF */
+ struct mutex lock; /* lock for VF bus */
+};
+
+#ifdef CONFIG_PCI_ATS
+void pci_restore_ats_state(struct pci_dev *dev);
+#else
+static inline void pci_restore_ats_state(struct pci_dev *dev)
+{
+}
+#endif /* CONFIG_PCI_ATS */
+
+#ifdef CONFIG_PCI_IOV
+int pci_iov_init(struct pci_dev *dev);
+void pci_iov_release(struct pci_dev *dev);
+int pci_iov_resource_bar(struct pci_dev *dev, int resno,
+ enum pci_bar_type *type);
+resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
+void pci_restore_iov_state(struct pci_dev *dev);
+int pci_iov_bus_range(struct pci_bus *bus);
+
+#else
+static inline int pci_iov_init(struct pci_dev *dev)
+{
+ return -ENODEV;
+}
+static inline void pci_iov_release(struct pci_dev *dev)
+
+{
+}
+static inline int pci_iov_resource_bar(struct pci_dev *dev, int resno,
+ enum pci_bar_type *type)
+{
+ return 0;
+}
+static inline void pci_restore_iov_state(struct pci_dev *dev)
+{
+}
+static inline int pci_iov_bus_range(struct pci_bus *bus)
+{
+ return 0;
+}
+
+#endif /* CONFIG_PCI_IOV */
+
+unsigned long pci_cardbus_resource_alignment(struct resource *);
+
+static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
+ struct resource *res)
+{
+#ifdef CONFIG_PCI_IOV
+ int resno = res - dev->resource;
+
+ if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
+ return pci_sriov_resource_alignment(dev, resno);
+#endif
+ if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS)
+ return pci_cardbus_resource_alignment(res);
+ return resource_alignment(res);
+}
+
+void pci_enable_acs(struct pci_dev *dev);
+
+struct pci_dev_reset_methods {
+ u16 vendor;
+ u16 device;
+ int (*reset)(struct pci_dev *dev, int probe);
+};
+
+#ifdef CONFIG_PCI_QUIRKS
+int pci_dev_specific_reset(struct pci_dev *dev, int probe);
+#else
+static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe)
+{
+ return -ENOTTY;
+}
+#endif
+
+#endif /* DRIVERS_PCI_H */
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 287a9311716..7958e59d607 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -2,7 +2,7 @@
# PCI Express Port Bus Configuration
#
config PCIEPORTBUS
- bool "PCI Express support"
+ bool "PCI Express Port Bus support"
depends on PCI
help
This automatically enables PCI Express Port Bus support. Users can
@@ -14,15 +14,69 @@ config PCIEPORTBUS
# Include service Kconfig here
#
config HOTPLUG_PCI_PCIE
- tristate "PCI Express Hotplug driver"
+ bool "PCI Express Hotplug driver"
depends on HOTPLUG_PCI && PCIEPORTBUS
help
Say Y here if you have a motherboard that supports PCI Express Native
Hotplug
- To compile this driver as a module, choose M here: the
- module will be called pciehp.
-
When in doubt, say N.
source "drivers/pci/pcie/aer/Kconfig"
+
+#
+# PCI Express ASPM
+#
+config PCIEASPM
+ bool "PCI Express ASPM control" if EXPERT
+ depends on PCI && PCIEPORTBUS
+ default y
+ help
+ This enables OS control over PCI Express ASPM (Active State
+ Power Management) and Clock Power Management. ASPM supports
+ state L0/L0s/L1.
+
+ ASPM is initially set up by the firmware. With this option enabled,
+ Linux can modify this state in order to disable ASPM on known-bad
+ hardware or configurations and enable it when known-safe.
+
+ ASPM can be disabled or enabled at runtime via
+ /sys/module/pcie_aspm/parameters/policy
+
+ When in doubt, say Y.
+config PCIEASPM_DEBUG
+ bool "Debug PCI Express ASPM"
+ depends on PCIEASPM
+ default n
+ help
+ This enables PCI Express ASPM debug support. It will add per-device
+ interface to control ASPM.
+
+choice
+ prompt "Default ASPM policy"
+ default PCIEASPM_DEFAULT
+ depends on PCIEASPM
+
+config PCIEASPM_DEFAULT
+ bool "BIOS default"
+ depends on PCIEASPM
+ help
+ Use the BIOS defaults for PCI Express ASPM.
+
+config PCIEASPM_POWERSAVE
+ bool "Powersave"
+ depends on PCIEASPM
+ help
+ Enable PCI Express ASPM L0s and L1 where possible, even if the
+ BIOS did not.
+
+config PCIEASPM_PERFORMANCE
+ bool "Performance"
+ depends on PCIEASPM
+ help
+ Disable PCI Express ASPM L0s and L1, even if the BIOS enabled them.
+endchoice
+
+config PCIE_PME
+ def_bool y
+ depends on PCIEPORTBUS && PM_RUNTIME
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index e00fb99acf4..00c62df5a9f 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -2,9 +2,15 @@
# Makefile for PCI-Express PORT Driver
#
+# Build PCI Express ASPM if needed
+obj-$(CONFIG_PCIEASPM) += aspm.o
+
pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
+pcieportdrv-$(CONFIG_ACPI) += portdrv_acpi.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
# Build PCI Express AER if needed
obj-$(CONFIG_PCIEAER) += aer/
+
+obj-$(CONFIG_PCIE_PME) += pme.o
diff --git a/drivers/pci/pcie/aer/Kconfig b/drivers/pci/pcie/aer/Kconfig
index c3bde588aa1..50e94e02378 100644
--- a/drivers/pci/pcie/aer/Kconfig
+++ b/drivers/pci/pcie/aer/Kconfig
@@ -10,3 +10,18 @@ config PCIEAER
This enables PCI Express Root Port Advanced Error Reporting
(AER) driver support. Error reporting messages sent to Root
Port will be handled by PCI Express AER driver.
+
+
+#
+# PCI Express ECRC
+#
+config PCIE_ECRC
+ bool "PCI Express ECRC settings control"
+ depends on PCIEAER
+ help
+ Used to override firmware/bios settings for PCI Express ECRC
+ (transaction layer end-to-end CRC checking).
+
+ When in doubt, say N.
+
+source "drivers/pci/pcie/aer/Kconfig.debug"
diff --git a/drivers/pci/pcie/aer/Kconfig.debug b/drivers/pci/pcie/aer/Kconfig.debug
new file mode 100644
index 00000000000..9142949734f
--- /dev/null
+++ b/drivers/pci/pcie/aer/Kconfig.debug
@@ -0,0 +1,18 @@
+#
+# PCI Express Root Port Device AER Debug Configuration
+#
+
+config PCIEAER_INJECT
+ tristate "PCIe AER error injector support"
+ depends on PCIEAER
+ default n
+ help
+ This enables PCI Express Root Port Advanced Error Reporting
+ (AER) software error injector.
+
+ Debugging PCIe AER code is quite difficult because it is hard
+ to trigger various real hardware errors. Software based
+ error injection can fake almost all kinds of errors with the
+ help of a user space helper tool aer-inject, which can be
+ gotten from:
+ http://www.kernel.org/pub/linux/utils/pci/aer-inject/
diff --git a/drivers/pci/pcie/aer/Makefile b/drivers/pci/pcie/aer/Makefile
index 8da3bd8455a..2cba67510dc 100644
--- a/drivers/pci/pcie/aer/Makefile
+++ b/drivers/pci/pcie/aer/Makefile
@@ -4,6 +4,9 @@
obj-$(CONFIG_PCIEAER) += aerdriver.o
+obj-$(CONFIG_PCIE_ECRC) += ecrc.o
+
aerdriver-objs := aerdrv_errprint.o aerdrv_core.o aerdrv.o
aerdriver-$(CONFIG_ACPI) += aerdrv_acpi.o
+obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c
new file mode 100644
index 00000000000..182224acedb
--- /dev/null
+++ b/drivers/pci/pcie/aer/aer_inject.c
@@ -0,0 +1,536 @@
+/*
+ * PCIe AER software error injection support.
+ *
+ * Debuging PCIe AER code is quite difficult because it is hard to
+ * trigger various real hardware errors. Software based error
+ * injection can fake almost all kinds of errors with the help of a
+ * user space helper tool aer-inject, which can be gotten from:
+ * http://www.kernel.org/pub/linux/utils/pci/aer-inject/
+ *
+ * Copyright 2009 Intel Corporation.
+ * Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/stddef.h>
+#include "aerdrv.h"
+
+/* Override the existing corrected and uncorrected error masks */
+static bool aer_mask_override;
+module_param(aer_mask_override, bool, 0);
+
+struct aer_error_inj {
+ u8 bus;
+ u8 dev;
+ u8 fn;
+ u32 uncor_status;
+ u32 cor_status;
+ u32 header_log0;
+ u32 header_log1;
+ u32 header_log2;
+ u32 header_log3;
+ u16 domain;
+};
+
+struct aer_error {
+ struct list_head list;
+ u16 domain;
+ unsigned int bus;
+ unsigned int devfn;
+ int pos_cap_err;
+
+ u32 uncor_status;
+ u32 cor_status;
+ u32 header_log0;
+ u32 header_log1;
+ u32 header_log2;
+ u32 header_log3;
+ u32 root_status;
+ u32 source_id;
+};
+
+struct pci_bus_ops {
+ struct list_head list;
+ struct pci_bus *bus;
+ struct pci_ops *ops;
+};
+
+static LIST_HEAD(einjected);
+
+static LIST_HEAD(pci_bus_ops_list);
+
+/* Protect einjected and pci_bus_ops_list */
+static DEFINE_SPINLOCK(inject_lock);
+
+static void aer_error_init(struct aer_error *err, u16 domain,
+ unsigned int bus, unsigned int devfn,
+ int pos_cap_err)
+{
+ INIT_LIST_HEAD(&err->list);
+ err->domain = domain;
+ err->bus = bus;
+ err->devfn = devfn;
+ err->pos_cap_err = pos_cap_err;
+}
+
+/* inject_lock must be held before calling */
+static struct aer_error *__find_aer_error(u16 domain, unsigned int bus,
+ unsigned int devfn)
+{
+ struct aer_error *err;
+
+ list_for_each_entry(err, &einjected, list) {
+ if (domain == err->domain &&
+ bus == err->bus &&
+ devfn == err->devfn)
+ return err;
+ }
+ return NULL;
+}
+
+/* inject_lock must be held before calling */
+static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev)
+{
+ int domain = pci_domain_nr(dev->bus);
+ if (domain < 0)
+ return NULL;
+ return __find_aer_error((u16)domain, dev->bus->number, dev->devfn);
+}
+
+/* inject_lock must be held before calling */
+static struct pci_ops *__find_pci_bus_ops(struct pci_bus *bus)
+{
+ struct pci_bus_ops *bus_ops;
+
+ list_for_each_entry(bus_ops, &pci_bus_ops_list, list) {
+ if (bus_ops->bus == bus)
+ return bus_ops->ops;
+ }
+ return NULL;
+}
+
+static struct pci_bus_ops *pci_bus_ops_pop(void)
+{
+ unsigned long flags;
+ struct pci_bus_ops *bus_ops = NULL;
+
+ spin_lock_irqsave(&inject_lock, flags);
+ if (list_empty(&pci_bus_ops_list))
+ bus_ops = NULL;
+ else {
+ struct list_head *lh = pci_bus_ops_list.next;
+ list_del(lh);
+ bus_ops = list_entry(lh, struct pci_bus_ops, list);
+ }
+ spin_unlock_irqrestore(&inject_lock, flags);
+ return bus_ops;
+}
+
+static u32 *find_pci_config_dword(struct aer_error *err, int where,
+ int *prw1cs)
+{
+ int rw1cs = 0;
+ u32 *target = NULL;
+
+ if (err->pos_cap_err == -1)
+ return NULL;
+
+ switch (where - err->pos_cap_err) {
+ case PCI_ERR_UNCOR_STATUS:
+ target = &err->uncor_status;
+ rw1cs = 1;
+ break;
+ case PCI_ERR_COR_STATUS:
+ target = &err->cor_status;
+ rw1cs = 1;
+ break;
+ case PCI_ERR_HEADER_LOG:
+ target = &err->header_log0;
+ break;
+ case PCI_ERR_HEADER_LOG+4:
+ target = &err->header_log1;
+ break;
+ case PCI_ERR_HEADER_LOG+8:
+ target = &err->header_log2;
+ break;
+ case PCI_ERR_HEADER_LOG+12:
+ target = &err->header_log3;
+ break;
+ case PCI_ERR_ROOT_STATUS:
+ target = &err->root_status;
+ rw1cs = 1;
+ break;
+ case PCI_ERR_ROOT_ERR_SRC:
+ target = &err->source_id;
+ break;
+ }
+ if (prw1cs)
+ *prw1cs = rw1cs;
+ return target;
+}
+
+static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where,
+ int size, u32 *val)
+{
+ u32 *sim;
+ struct aer_error *err;
+ unsigned long flags;
+ struct pci_ops *ops;
+ int domain;
+
+ spin_lock_irqsave(&inject_lock, flags);
+ if (size != sizeof(u32))
+ goto out;
+ domain = pci_domain_nr(bus);
+ if (domain < 0)
+ goto out;
+ err = __find_aer_error((u16)domain, bus->number, devfn);
+ if (!err)
+ goto out;
+
+ sim = find_pci_config_dword(err, where, NULL);
+ if (sim) {
+ *val = *sim;
+ spin_unlock_irqrestore(&inject_lock, flags);
+ return 0;
+ }
+out:
+ ops = __find_pci_bus_ops(bus);
+ spin_unlock_irqrestore(&inject_lock, flags);
+ return ops->read(bus, devfn, where, size, val);
+}
+
+static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where,
+ int size, u32 val)
+{
+ u32 *sim;
+ struct aer_error *err;
+ unsigned long flags;
+ int rw1cs;
+ struct pci_ops *ops;
+ int domain;
+
+ spin_lock_irqsave(&inject_lock, flags);
+ if (size != sizeof(u32))
+ goto out;
+ domain = pci_domain_nr(bus);
+ if (domain < 0)
+ goto out;
+ err = __find_aer_error((u16)domain, bus->number, devfn);
+ if (!err)
+ goto out;
+
+ sim = find_pci_config_dword(err, where, &rw1cs);
+ if (sim) {
+ if (rw1cs)
+ *sim ^= val;
+ else
+ *sim = val;
+ spin_unlock_irqrestore(&inject_lock, flags);
+ return 0;
+ }
+out:
+ ops = __find_pci_bus_ops(bus);
+ spin_unlock_irqrestore(&inject_lock, flags);
+ return ops->write(bus, devfn, where, size, val);
+}
+
+static struct pci_ops pci_ops_aer = {
+ .read = pci_read_aer,
+ .write = pci_write_aer,
+};
+
+static void pci_bus_ops_init(struct pci_bus_ops *bus_ops,
+ struct pci_bus *bus,
+ struct pci_ops *ops)
+{
+ INIT_LIST_HEAD(&bus_ops->list);
+ bus_ops->bus = bus;
+ bus_ops->ops = ops;
+}
+
+static int pci_bus_set_aer_ops(struct pci_bus *bus)
+{
+ struct pci_ops *ops;
+ struct pci_bus_ops *bus_ops;
+ unsigned long flags;
+
+ bus_ops = kmalloc(sizeof(*bus_ops), GFP_KERNEL);
+ if (!bus_ops)
+ return -ENOMEM;
+ ops = pci_bus_set_ops(bus, &pci_ops_aer);
+ spin_lock_irqsave(&inject_lock, flags);
+ if (ops == &pci_ops_aer)
+ goto out;
+ pci_bus_ops_init(bus_ops, bus, ops);
+ list_add(&bus_ops->list, &pci_bus_ops_list);
+ bus_ops = NULL;
+out:
+ spin_unlock_irqrestore(&inject_lock, flags);
+ kfree(bus_ops);
+ return 0;
+}
+
+static struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
+{
+ while (1) {
+ if (!pci_is_pcie(dev))
+ break;
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
+ return dev;
+ if (!dev->bus->self)
+ break;
+ dev = dev->bus->self;
+ }
+ return NULL;
+}
+
+static int find_aer_device_iter(struct device *device, void *data)
+{
+ struct pcie_device **result = data;
+ struct pcie_device *pcie_dev;
+
+ if (device->bus == &pcie_port_bus_type) {
+ pcie_dev = to_pcie_device(device);
+ if (pcie_dev->service & PCIE_PORT_SERVICE_AER) {
+ *result = pcie_dev;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int find_aer_device(struct pci_dev *dev, struct pcie_device **result)
+{
+ return device_for_each_child(&dev->dev, result, find_aer_device_iter);
+}
+
+static int aer_inject(struct aer_error_inj *einj)
+{
+ struct aer_error *err, *rperr;
+ struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
+ struct pci_dev *dev, *rpdev;
+ struct pcie_device *edev;
+ unsigned long flags;
+ unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn);
+ int pos_cap_err, rp_pos_cap_err;
+ u32 sever, cor_mask, uncor_mask, cor_mask_orig = 0, uncor_mask_orig = 0;
+ int ret = 0;
+
+ dev = pci_get_domain_bus_and_slot((int)einj->domain, einj->bus, devfn);
+ if (!dev)
+ return -ENODEV;
+ rpdev = pcie_find_root_port(dev);
+ if (!rpdev) {
+ ret = -ENODEV;
+ goto out_put;
+ }
+
+ pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ if (!pos_cap_err) {
+ ret = -EPERM;
+ goto out_put;
+ }
+ pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever);
+ pci_read_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, &cor_mask);
+ pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK,
+ &uncor_mask);
+
+ rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR);
+ if (!rp_pos_cap_err) {
+ ret = -EPERM;
+ goto out_put;
+ }
+
+ err_alloc = kzalloc(sizeof(struct aer_error), GFP_KERNEL);
+ if (!err_alloc) {
+ ret = -ENOMEM;
+ goto out_put;
+ }
+ rperr_alloc = kzalloc(sizeof(struct aer_error), GFP_KERNEL);
+ if (!rperr_alloc) {
+ ret = -ENOMEM;
+ goto out_put;
+ }
+
+ if (aer_mask_override) {
+ cor_mask_orig = cor_mask;
+ cor_mask &= !(einj->cor_status);
+ pci_write_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK,
+ cor_mask);
+
+ uncor_mask_orig = uncor_mask;
+ uncor_mask &= !(einj->uncor_status);
+ pci_write_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK,
+ uncor_mask);
+ }
+
+ spin_lock_irqsave(&inject_lock, flags);
+
+ err = __find_aer_error_by_dev(dev);
+ if (!err) {
+ err = err_alloc;
+ err_alloc = NULL;
+ aer_error_init(err, einj->domain, einj->bus, devfn,
+ pos_cap_err);
+ list_add(&err->list, &einjected);
+ }
+ err->uncor_status |= einj->uncor_status;
+ err->cor_status |= einj->cor_status;
+ err->header_log0 = einj->header_log0;
+ err->header_log1 = einj->header_log1;
+ err->header_log2 = einj->header_log2;
+ err->header_log3 = einj->header_log3;
+
+ if (!aer_mask_override && einj->cor_status &&
+ !(einj->cor_status & ~cor_mask)) {
+ ret = -EINVAL;
+ printk(KERN_WARNING "The correctable error(s) is masked by device\n");
+ spin_unlock_irqrestore(&inject_lock, flags);
+ goto out_put;
+ }
+ if (!aer_mask_override && einj->uncor_status &&
+ !(einj->uncor_status & ~uncor_mask)) {
+ ret = -EINVAL;
+ printk(KERN_WARNING "The uncorrectable error(s) is masked by device\n");
+ spin_unlock_irqrestore(&inject_lock, flags);
+ goto out_put;
+ }
+
+ rperr = __find_aer_error_by_dev(rpdev);
+ if (!rperr) {
+ rperr = rperr_alloc;
+ rperr_alloc = NULL;
+ aer_error_init(rperr, pci_domain_nr(rpdev->bus),
+ rpdev->bus->number, rpdev->devfn,
+ rp_pos_cap_err);
+ list_add(&rperr->list, &einjected);
+ }
+ if (einj->cor_status) {
+ if (rperr->root_status & PCI_ERR_ROOT_COR_RCV)
+ rperr->root_status |= PCI_ERR_ROOT_MULTI_COR_RCV;
+ else
+ rperr->root_status |= PCI_ERR_ROOT_COR_RCV;
+ rperr->source_id &= 0xffff0000;
+ rperr->source_id |= (einj->bus << 8) | devfn;
+ }
+ if (einj->uncor_status) {
+ if (rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV)
+ rperr->root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV;
+ if (sever & einj->uncor_status) {
+ rperr->root_status |= PCI_ERR_ROOT_FATAL_RCV;
+ if (!(rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV))
+ rperr->root_status |= PCI_ERR_ROOT_FIRST_FATAL;
+ } else
+ rperr->root_status |= PCI_ERR_ROOT_NONFATAL_RCV;
+ rperr->root_status |= PCI_ERR_ROOT_UNCOR_RCV;
+ rperr->source_id &= 0x0000ffff;
+ rperr->source_id |= ((einj->bus << 8) | devfn) << 16;
+ }
+ spin_unlock_irqrestore(&inject_lock, flags);
+
+ if (aer_mask_override) {
+ pci_write_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK,
+ cor_mask_orig);
+ pci_write_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK,
+ uncor_mask_orig);
+ }
+
+ ret = pci_bus_set_aer_ops(dev->bus);
+ if (ret)
+ goto out_put;
+ ret = pci_bus_set_aer_ops(rpdev->bus);
+ if (ret)
+ goto out_put;
+
+ if (find_aer_device(rpdev, &edev)) {
+ if (!get_service_data(edev)) {
+ printk(KERN_WARNING "AER service is not initialized\n");
+ ret = -EINVAL;
+ goto out_put;
+ }
+ aer_irq(-1, edev);
+ } else
+ ret = -EINVAL;
+out_put:
+ kfree(err_alloc);
+ kfree(rperr_alloc);
+ pci_dev_put(dev);
+ return ret;
+}
+
+static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf,
+ size_t usize, loff_t *off)
+{
+ struct aer_error_inj einj;
+ int ret;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (usize < offsetof(struct aer_error_inj, domain) ||
+ usize > sizeof(einj))
+ return -EINVAL;
+
+ memset(&einj, 0, sizeof(einj));
+ if (copy_from_user(&einj, ubuf, usize))
+ return -EFAULT;
+
+ ret = aer_inject(&einj);
+ return ret ? ret : usize;
+}
+
+static const struct file_operations aer_inject_fops = {
+ .write = aer_inject_write,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+};
+
+static struct miscdevice aer_inject_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "aer_inject",
+ .fops = &aer_inject_fops,
+};
+
+static int __init aer_inject_init(void)
+{
+ return misc_register(&aer_inject_device);
+}
+
+static void __exit aer_inject_exit(void)
+{
+ struct aer_error *err, *err_next;
+ unsigned long flags;
+ struct pci_bus_ops *bus_ops;
+
+ misc_deregister(&aer_inject_device);
+
+ while ((bus_ops = pci_bus_ops_pop())) {
+ pci_bus_set_ops(bus_ops->bus, bus_ops->ops);
+ kfree(bus_ops);
+ }
+
+ spin_lock_irqsave(&inject_lock, flags);
+ list_for_each_entry_safe(err, err_next, &einjected, list) {
+ list_del(&err->list);
+ kfree(err);
+ }
+ spin_unlock_irqrestore(&inject_lock, flags);
+}
+
+module_init(aer_inject_init);
+module_exit(aer_inject_exit);
+
+MODULE_DESCRIPTION("PCIe AER software error injector");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index 7a62f7dd900..0bf82a20a0f 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -17,6 +17,8 @@
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/pci-acpi.h>
+#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pm.h>
@@ -24,8 +26,10 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/pcieport_if.h>
+#include <linux/slab.h>
#include "aerdrv.h"
+#include "../../pci.h"
/*
* Version Information
@@ -37,45 +41,26 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-static int __devinit aer_probe (struct pcie_device *dev,
- const struct pcie_port_service_id *id );
+static int aer_probe(struct pcie_device *dev);
static void aer_remove(struct pcie_device *dev);
-static int aer_suspend(struct pcie_device *dev, pm_message_t state)
-{return 0;}
-static int aer_resume(struct pcie_device *dev) {return 0;}
static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
enum pci_channel_state error);
static void aer_error_resume(struct pci_dev *dev);
static pci_ers_result_t aer_root_reset(struct pci_dev *dev);
-/*
- * PCI Express bus's AER Root service driver data structure
- */
-static struct pcie_port_service_id aer_id[] = {
- {
- .vendor = PCI_ANY_ID,
- .device = PCI_ANY_ID,
- .port_type = PCIE_RC_PORT,
- .service_type = PCIE_PORT_SERVICE_AER,
- },
- { /* end: all zeroes */ }
-};
-
-static struct pci_error_handlers aer_error_handlers = {
+static const struct pci_error_handlers aer_error_handlers = {
.error_detected = aer_error_detected,
- .resume = aer_error_resume,
+ .resume = aer_error_resume,
};
static struct pcie_port_service_driver aerdriver = {
.name = "aer",
- .id_table = &aer_id[0],
+ .port_type = PCI_EXP_TYPE_ROOT_PORT,
+ .service = PCIE_PORT_SERVICE_AER,
.probe = aer_probe,
.remove = aer_remove,
- .suspend = aer_suspend,
- .resume = aer_resume,
-
.err_handler = &aer_error_handlers,
.reset_link = aer_root_reset,
@@ -88,14 +73,125 @@ void pci_no_aer(void)
pcie_aer_disable = 1; /* has priority over 'forceload' */
}
+bool pci_aer_available(void)
+{
+ return !pcie_aer_disable && pci_msi_enabled();
+}
+
+static int set_device_error_reporting(struct pci_dev *dev, void *data)
+{
+ bool enable = *((bool *)data);
+ int type = pci_pcie_type(dev);
+
+ if ((type == PCI_EXP_TYPE_ROOT_PORT) ||
+ (type == PCI_EXP_TYPE_UPSTREAM) ||
+ (type == PCI_EXP_TYPE_DOWNSTREAM)) {
+ if (enable)
+ pci_enable_pcie_error_reporting(dev);
+ else
+ pci_disable_pcie_error_reporting(dev);
+ }
+
+ if (enable)
+ pcie_set_ecrc_checking(dev);
+
+ return 0;
+}
+
+/**
+ * set_downstream_devices_error_reporting - enable/disable the error reporting bits on the root port and its downstream ports.
+ * @dev: pointer to root port's pci_dev data structure
+ * @enable: true = enable error reporting, false = disable error reporting.
+ */
+static void set_downstream_devices_error_reporting(struct pci_dev *dev,
+ bool enable)
+{
+ set_device_error_reporting(dev, &enable);
+
+ if (!dev->subordinate)
+ return;
+ pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable);
+}
+
+/**
+ * aer_enable_rootport - enable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIe bus loads AER service driver.
+ */
+static void aer_enable_rootport(struct aer_rpc *rpc)
+{
+ struct pci_dev *pdev = rpc->rpd->port;
+ int aer_pos;
+ u16 reg16;
+ u32 reg32;
+
+ /* Clear PCIe Capability's Device Status */
+ pcie_capability_read_word(pdev, PCI_EXP_DEVSTA, &reg16);
+ pcie_capability_write_word(pdev, PCI_EXP_DEVSTA, reg16);
+
+ /* Disable system error generation in response to error messages */
+ pcie_capability_clear_word(pdev, PCI_EXP_RTCTL,
+ SYSTEM_ERROR_INTR_ON_MESG_MASK);
+
+ aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
+ /* Clear error status */
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
+
+ /*
+ * Enable error reporting for the root port device and downstream port
+ * devices.
+ */
+ set_downstream_devices_error_reporting(pdev, true);
+
+ /* Enable Root Port's interrupt in response to error messages */
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, &reg32);
+ reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32);
+}
+
+/**
+ * aer_disable_rootport - disable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIe bus unloads AER service driver.
+ */
+static void aer_disable_rootport(struct aer_rpc *rpc)
+{
+ struct pci_dev *pdev = rpc->rpd->port;
+ u32 reg32;
+ int pos;
+
+ /*
+ * Disable error reporting for the root port device and downstream port
+ * devices.
+ */
+ set_downstream_devices_error_reporting(pdev, false);
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
+ /* Disable Root's interrupt in response to error messages */
+ pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
+ reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
+ pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, reg32);
+
+ /* Clear Root's error status reg */
+ pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, &reg32);
+ pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
+}
+
/**
* aer_irq - Root Port's ISR
* @irq: IRQ assigned to Root Port
* @context: pointer to Root Port data structure
*
* Invoked when Root Port detects AER messages.
- **/
-static irqreturn_t aer_irq(int irq, void *context)
+ */
+irqreturn_t aer_irq(int irq, void *context)
{
unsigned int status, id;
struct pcie_device *pdev = (struct pcie_device *)context;
@@ -104,7 +200,7 @@ static irqreturn_t aer_irq(int irq, void *context)
unsigned long flags;
int pos;
- pos = pci_find_aer_capability(pdev->port);
+ pos = pci_find_ext_capability(pdev->port, PCI_EXT_CAP_ID_ERR);
/*
* Must lock access to Root Error Status Reg, Root Error ID Reg,
* and Root error producer/consumer index
@@ -113,13 +209,13 @@ static irqreturn_t aer_irq(int irq, void *context)
/* Read error status */
pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
- if (!(status & ROOT_ERR_STATUS_MASKS)) {
+ if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) {
spin_unlock_irqrestore(&rpc->e_lock, flags);
return IRQ_NONE;
}
/* Read error source and clear error status */
- pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id);
+ pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id);
pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);
/* Store error source for later DPC handler */
@@ -144,34 +240,31 @@ static irqreturn_t aer_irq(int irq, void *context)
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_GPL(aer_irq);
/**
* aer_alloc_rpc - allocate Root Port data structure
* @dev: pointer to the pcie_dev data structure
*
* Invoked when Root Port's AER service is loaded.
- **/
-static struct aer_rpc* aer_alloc_rpc(struct pcie_device *dev)
+ */
+static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
{
struct aer_rpc *rpc;
- if (!(rpc = kzalloc(sizeof(struct aer_rpc),
- GFP_KERNEL)))
+ rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL);
+ if (!rpc)
return NULL;
- /*
- * Initialize Root lock access, e_lock, to Root Error Status Reg,
- * Root Error ID Reg, and Root error producer/consumer index.
- */
+ /* Initialize Root lock access, e_lock, to Root Error Status Reg */
spin_lock_init(&rpc->e_lock);
rpc->rpd = dev;
INIT_WORK(&rpc->dpc_handler, aer_isr);
- rpc->prod_idx = rpc->cons_idx = 0;
mutex_init(&rpc->rpc_mutex);
init_waitqueue_head(&rpc->wait_release);
- /* Use PCIE bus function to store rpc into PCIE device */
+ /* Use PCIe bus function to store rpc into PCIe device */
set_service_data(dev, rpc);
return rpc;
@@ -182,7 +275,7 @@ static struct aer_rpc* aer_alloc_rpc(struct pcie_device *dev)
* @dev: pointer to the pcie_dev data structure
*
* Invoked when PCI Express bus unloads or AER probe fails.
- **/
+ */
static void aer_remove(struct pcie_device *dev)
{
struct aer_rpc *rpc = get_service_data(dev);
@@ -194,7 +287,8 @@ static void aer_remove(struct pcie_device *dev)
wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx);
- aer_delete_rootport(rpc);
+ aer_disable_rootport(rpc);
+ kfree(rpc);
set_service_data(dev, NULL);
}
}
@@ -205,31 +299,30 @@ static void aer_remove(struct pcie_device *dev)
* @id: pointer to the service id data structure
*
* Invoked when PCI Express bus loads AER service driver.
- **/
-static int __devinit aer_probe (struct pcie_device *dev,
- const struct pcie_port_service_id *id )
+ */
+static int aer_probe(struct pcie_device *dev)
{
int status;
struct aer_rpc *rpc;
struct device *device = &dev->device;
/* Init */
- if ((status = aer_init(dev)))
+ status = aer_init(dev);
+ if (status)
return status;
/* Alloc rpc data structure */
- if (!(rpc = aer_alloc_rpc(dev))) {
- printk(KERN_DEBUG "%s: Alloc rpc fails on PCIE device[%s]\n",
- __FUNCTION__, device->bus_id);
+ rpc = aer_alloc_rpc(dev);
+ if (!rpc) {
+ dev_printk(KERN_DEBUG, device, "alloc rpc failed\n");
aer_remove(dev);
return -ENOMEM;
}
/* 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",
- __FUNCTION__, device->bus_id);
+ status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev);
+ if (status) {
+ dev_printk(KERN_DEBUG, device, "request IRQ failed\n");
aer_remove(dev);
return status;
}
@@ -246,41 +339,30 @@ static int __devinit aer_probe (struct pcie_device *dev,
* @dev: pointer to Root Port's pci_dev data structure
*
* Invoked by Port Bus driver when performing link reset at Root Port.
- **/
+ */
static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
{
- u16 p2p_ctrl;
- u32 status;
+ u32 reg32;
int pos;
- pos = pci_find_aer_capability(dev);
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
/* Disable Root's interrupt in response to error messages */
- pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0);
+ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
+ reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
+ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
- /* Assert Secondary Bus Reset */
- pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
- p2p_ctrl |= PCI_CB_BRIDGE_CTL_CB_RESET;
- pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
+ pci_reset_bridge_secondary_bus(dev);
+ dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
- /* De-assert Secondary Bus Reset */
- p2p_ctrl &= ~PCI_CB_BRIDGE_CTL_CB_RESET;
- pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
-
- /*
- * System software must wait for at least 100ms from the end
- * of a reset of one or more device before it is permitted
- * to issue Configuration Requests to those devices.
- */
- msleep(200);
- printk(KERN_DEBUG "Complete link reset at Root[%s]\n", dev->dev.bus_id);
+ /* Clear Root Error Status */
+ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
+ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, reg32);
/* Enable Root Port's interrupt in response to error messages */
- pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
- pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status);
- pci_write_config_dword(dev,
- pos + PCI_ERR_ROOT_COMMAND,
- ROOT_PORT_INTR_ON_MESG_MASK);
+ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
+ reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
+ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
return PCI_ERS_RESULT_RECOVERED;
}
@@ -291,7 +373,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
* @error: error severity being notified by port bus
*
* Invoked by Port Bus driver during error recovery.
- **/
+ */
static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
enum pci_channel_state error)
{
@@ -304,7 +386,7 @@ static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
* @dev: pointer to Root Port's pci_dev data structure
*
* Invoked by Port Bus driver during nonfatal recovery.
- **/
+ */
static void aer_error_resume(struct pci_dev *dev)
{
int pos;
@@ -312,12 +394,11 @@ static void aer_error_resume(struct pci_dev *dev)
u16 reg16;
/* Clean up Root device status */
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &reg16);
- pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16);
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &reg16);
+ pcie_capability_write_word(dev, PCI_EXP_DEVSTA, reg16);
/* Clean AER Root Error Status */
- pos = pci_find_aer_capability(dev);
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
if (dev->error_state == pci_channel_io_normal)
@@ -331,10 +412,10 @@ static void aer_error_resume(struct pci_dev *dev)
* aer_service_init - register AER root service driver
*
* Invoked when AER root service driver is loaded.
- **/
+ */
static int __init aer_service_init(void)
{
- if (pcie_aer_disable)
+ if (!pci_aer_available() || aer_acpi_firmware_first())
return -ENXIO;
return pcie_port_service_register(&aerdriver);
}
@@ -343,7 +424,7 @@ static int __init aer_service_init(void)
* aer_service_exit - unregister AER root service driver
*
* Invoked when AER root service driver is unloaded.
- **/
+ */
static void __exit aer_service_exit(void)
{
pcie_port_service_unregister(&aerdriver);
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h
index c7ad68b6c6d..84420b7c945 100644
--- a/drivers/pci/pcie/aer/aerdrv.h
+++ b/drivers/pci/pcie/aer/aerdrv.h
@@ -11,16 +11,7 @@
#include <linux/workqueue.h>
#include <linux/pcieport_if.h>
#include <linux/aer.h>
-
-#define AER_NONFATAL 0
-#define AER_FATAL 1
-#define AER_CORRECTABLE 2
-#define AER_UNCORRECTABLE 4
-#define AER_ERROR_MASK 0x001fffff
-#define AER_ERROR(d) (d & AER_ERROR_MASK)
-
-/* Root Error Status Register Bits */
-#define ROOT_ERR_STATUS_MASKS 0x0f
+#include <linux/interrupt.h>
#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \
PCI_EXP_RTCTL_SENFEE| \
@@ -31,8 +22,6 @@
#define ERR_COR_ID(d) (d & 0xffff)
#define ERR_UNCOR_ID(d) (d >> 16)
-#define AER_SUCCESS 0
-#define AER_UNSUCCESS 1
#define AER_ERROR_SOURCES_MAX 100
#define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \
@@ -42,25 +31,24 @@
PCI_ERR_UNC_UNX_COMP| \
PCI_ERR_UNC_MALF_TLP)
-/* AER Error Info Flags */
-#define AER_TLP_HEADER_VALID_FLAG 0x00000001
-#define AER_MULTI_ERROR_VALID_FLAG 0x00000002
+#define AER_MAX_MULTI_ERR_DEVICES 5 /* Not likely to have more */
+struct aer_err_info {
+ struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES];
+ int error_dev_num;
-#define ERR_CORRECTABLE_ERROR_MASK 0x000031c1
-#define ERR_UNCORRECTABLE_ERROR_MASK 0x001ff010
+ unsigned int id:16;
-struct header_log_regs {
- unsigned int dw0;
- unsigned int dw1;
- unsigned int dw2;
- unsigned int dw3;
-};
+ unsigned int severity:2; /* 0:NONFATAL | 1:FATAL | 2:COR */
+ unsigned int __pad1:5;
+ unsigned int multi_error_valid:1;
+
+ unsigned int first_error:5;
+ unsigned int __pad2:2;
+ unsigned int tlp_header_valid:1;
-struct aer_err_info {
- int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */
- int flags;
unsigned int status; /* COR/UNCOR Error Status */
- struct header_log_regs tlp; /* TLP Header */
+ unsigned int mask; /* COR/UNCOR Error Mask */
+ struct aer_header_log_regs tlp; /* TLP Header */
};
struct aer_err_source {
@@ -95,6 +83,12 @@ struct aer_broadcast_data {
static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
enum pci_ers_result new)
{
+ if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
+ return PCI_ERS_RESULT_NO_AER_DRIVER;
+
+ if (new == PCI_ERS_RESULT_NONE)
+ return orig;
+
switch (orig) {
case PCI_ERS_RESULT_CAN_RECOVER:
case PCI_ERS_RESULT_RECOVERED:
@@ -102,7 +96,7 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
break;
case PCI_ERS_RESULT_DISCONNECT:
if (new == PCI_ERS_RESULT_NEED_RESET)
- orig = new;
+ orig = PCI_ERS_RESULT_NEED_RESET;
break;
default:
break;
@@ -112,19 +106,27 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
}
extern struct bus_type pcie_port_bus_type;
-extern void aer_enable_rootport(struct aer_rpc *rpc);
-extern void aer_delete_rootport(struct aer_rpc *rpc);
-extern int aer_init(struct pcie_device *dev);
-extern void aer_isr(struct work_struct *work);
-extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
-
-#ifdef CONFIG_ACPI
-extern int aer_osc_setup(struct pcie_device *pciedev);
+int aer_init(struct pcie_device *dev);
+void aer_isr(struct work_struct *work);
+void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
+void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info);
+irqreturn_t aer_irq(int irq, void *context);
+
+#ifdef CONFIG_ACPI_APEI
+int pcie_aer_get_firmware_first(struct pci_dev *pci_dev);
#else
-static inline int aer_osc_setup(struct pcie_device *pciedev)
+static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev)
{
+ if (pci_dev->__aer_firmware_first_valid)
+ return pci_dev->__aer_firmware_first;
return 0;
}
#endif
-#endif //_AERDRV_H_
+static inline void pcie_aer_force_firmware_first(struct pci_dev *pci_dev,
+ int enable)
+{
+ pci_dev->__aer_firmware_first = !!enable;
+ pci_dev->__aer_firmware_first_valid = 1;
+}
+#endif /* _AERDRV_H_ */
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c
index 8c199ae84f6..01906576ab9 100644
--- a/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ b/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -16,43 +16,126 @@
#include <linux/acpi.h>
#include <linux/pci-acpi.h>
#include <linux/delay.h>
+#include <acpi/apei.h>
#include "aerdrv.h"
-/**
- * aer_osc_setup - run ACPI _OSC method
- * @pciedev: pcie_device which AER is being enabled on
- *
- * @return: Zero on success. Nonzero otherwise.
- *
- * Invoked when PCIE bus loads AER service driver. To avoid conflict with
- * BIOS AER support requires BIOS to yield AER control to OS native driver.
- **/
-int aer_osc_setup(struct pcie_device *pciedev)
+#ifdef CONFIG_ACPI_APEI
+static inline int hest_match_pci(struct acpi_hest_aer_common *p,
+ struct pci_dev *pci)
{
- acpi_status status = AE_NOT_FOUND;
- struct pci_dev *pdev = pciedev->port;
- acpi_handle handle = 0;
-
- /* Find root host bridge */
- while (pdev->bus && pdev->bus->self)
- pdev = pdev->bus->self;
- handle = acpi_get_pci_rootbridge_handle(
- pci_domain_nr(pdev->bus), pdev->bus->number);
-
- if (handle) {
- pcie_osc_support_set(OSC_EXT_PCI_CONFIG_SUPPORT);
- status = pci_osc_control_set(handle,
- OSC_PCI_EXPRESS_AER_CONTROL |
- OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
- }
+ return ACPI_HEST_SEGMENT(p->bus) == pci_domain_nr(pci->bus) &&
+ ACPI_HEST_BUS(p->bus) == pci->bus->number &&
+ p->device == PCI_SLOT(pci->devfn) &&
+ p->function == PCI_FUNC(pci->devfn);
+}
+
+static inline bool hest_match_type(struct acpi_hest_header *hest_hdr,
+ struct pci_dev *dev)
+{
+ u16 hest_type = hest_hdr->type;
+ u8 pcie_type = pci_pcie_type(dev);
+
+ if ((hest_type == ACPI_HEST_TYPE_AER_ROOT_PORT &&
+ pcie_type == PCI_EXP_TYPE_ROOT_PORT) ||
+ (hest_type == ACPI_HEST_TYPE_AER_ENDPOINT &&
+ pcie_type == PCI_EXP_TYPE_ENDPOINT) ||
+ (hest_type == ACPI_HEST_TYPE_AER_BRIDGE &&
+ (dev->class >> 16) == PCI_BASE_CLASS_BRIDGE))
+ return true;
+ return false;
+}
+
+struct aer_hest_parse_info {
+ struct pci_dev *pci_dev;
+ int firmware_first;
+};
+
+static int hest_source_is_pcie_aer(struct acpi_hest_header *hest_hdr)
+{
+ if (hest_hdr->type == ACPI_HEST_TYPE_AER_ROOT_PORT ||
+ hest_hdr->type == ACPI_HEST_TYPE_AER_ENDPOINT ||
+ hest_hdr->type == ACPI_HEST_TYPE_AER_BRIDGE)
+ return 1;
+ return 0;
+}
+
+static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data)
+{
+ struct aer_hest_parse_info *info = data;
+ struct acpi_hest_aer_common *p;
+ int ff;
+
+ if (!hest_source_is_pcie_aer(hest_hdr))
+ return 0;
+
+ p = (struct acpi_hest_aer_common *)(hest_hdr + 1);
+ ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
- 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");
- return -1;
+ /*
+ * If no specific device is supplied, determine whether
+ * FIRMWARE_FIRST is set for *any* PCIe device.
+ */
+ if (!info->pci_dev) {
+ info->firmware_first |= ff;
+ return 0;
}
+ /* Otherwise, check the specific device */
+ if (p->flags & ACPI_HEST_GLOBAL) {
+ if (hest_match_type(hest_hdr, info->pci_dev))
+ info->firmware_first = ff;
+ } else
+ if (hest_match_pci(p, info->pci_dev))
+ info->firmware_first = ff;
+
return 0;
}
+
+static void aer_set_firmware_first(struct pci_dev *pci_dev)
+{
+ int rc;
+ struct aer_hest_parse_info info = {
+ .pci_dev = pci_dev,
+ .firmware_first = 0,
+ };
+
+ rc = apei_hest_parse(aer_hest_parse, &info);
+
+ if (rc)
+ pci_dev->__aer_firmware_first = 0;
+ else
+ pci_dev->__aer_firmware_first = info.firmware_first;
+ pci_dev->__aer_firmware_first_valid = 1;
+}
+
+int pcie_aer_get_firmware_first(struct pci_dev *dev)
+{
+ if (!pci_is_pcie(dev))
+ return 0;
+
+ if (!dev->__aer_firmware_first_valid)
+ aer_set_firmware_first(dev);
+ return dev->__aer_firmware_first;
+}
+
+static bool aer_firmware_first;
+
+/**
+ * aer_acpi_firmware_first - Check if APEI should control AER.
+ */
+bool aer_acpi_firmware_first(void)
+{
+ static bool parsed = false;
+ struct aer_hest_parse_info info = {
+ .pci_dev = NULL, /* Check all PCIe devices */
+ .firmware_first = 0,
+ };
+
+ if (!parsed) {
+ apei_hest_parse(aer_hest_parse, &info);
+ aer_firmware_first = info.firmware_first;
+ parsed = true;
+ }
+ return aer_firmware_first;
+}
+#endif
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index 3c0d8d138f5..5653ea94547 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -23,188 +23,195 @@
#include <linux/pm.h>
#include <linux/suspend.h>
#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/kfifo.h>
#include "aerdrv.h"
-static int forceload;
+static bool forceload;
+static bool nosourceid;
module_param(forceload, bool, 0);
+module_param(nosourceid, bool, 0);
-#define PCI_CFG_SPACE_SIZE (0x100)
-int pci_find_aer_capability(struct pci_dev *dev)
-{
- int pos;
- u32 reg32 = 0;
-
- /* Check if it's a pci-express device */
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!pos)
- return 0;
-
- /* Check if it supports pci-express AER */
- pos = PCI_CFG_SPACE_SIZE;
- while (pos) {
- if (pci_read_config_dword(dev, pos, &reg32))
- return 0;
-
- /* some broken boards return ~0 */
- if (reg32 == 0xffffffff)
- return 0;
-
- if (PCI_EXT_CAP_ID(reg32) == PCI_EXT_CAP_ID_ERR)
- break;
-
- pos = reg32 >> 20;
- }
-
- return pos;
-}
+#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
int pci_enable_pcie_error_reporting(struct pci_dev *dev)
{
- u16 reg16 = 0;
- int pos;
+ if (pcie_aer_get_firmware_first(dev))
+ return -EIO;
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!pos)
+ if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
return -EIO;
- pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
- reg16 = reg16 |
- PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE |
- PCI_EXP_DEVCTL_URRE;
- pci_write_config_word(dev, pos+PCI_EXP_DEVCTL,
- reg16);
- return 0;
+ return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS);
}
+EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);
int pci_disable_pcie_error_reporting(struct pci_dev *dev)
{
- u16 reg16 = 0;
- int pos;
-
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!pos)
+ if (pcie_aer_get_firmware_first(dev))
return -EIO;
- pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
- reg16 = reg16 & ~(PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE |
- PCI_EXP_DEVCTL_URRE);
- pci_write_config_word(dev, pos+PCI_EXP_DEVCTL,
- reg16);
- return 0;
+ return pcie_capability_clear_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_AER_FLAGS);
}
+EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);
int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
{
int pos;
- u32 status, mask;
+ u32 status;
- pos = pci_find_aer_capability(dev);
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (!pos)
return -EIO;
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
- pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
- if (dev->error_state == pci_channel_io_normal)
- status &= ~mask; /* Clear corresponding nonfatal bits */
- else
- status &= mask; /* Clear corresponding fatal bits */
- pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
+ if (status)
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
return 0;
}
+EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
+
+/**
+ * add_error_device - list device to be handled
+ * @e_info: pointer to error info
+ * @dev: pointer to pci_dev to be added
+ */
+static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev)
+{
+ if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) {
+ e_info->dev[e_info->error_dev_num] = dev;
+ e_info->error_dev_num++;
+ return 0;
+ }
+ return -ENOSPC;
+}
-int pci_cleanup_aer_correct_error_status(struct pci_dev *dev)
+/**
+ * is_error_source - check whether the device is source of reported error
+ * @dev: pointer to pci_dev to be checked
+ * @e_info: pointer to reported error info
+ */
+static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info)
{
int pos;
- u32 status;
+ u32 status, mask;
+ u16 reg16;
+
+ /*
+ * When bus id is equal to 0, it might be a bad id
+ * reported by root port.
+ */
+ if (!nosourceid && (PCI_BUS_NUM(e_info->id) != 0)) {
+ /* Device ID match? */
+ if (e_info->id == ((dev->bus->number << 8) | dev->devfn))
+ return true;
+
+ /* Continue id comparing if there is no multiple error */
+ if (!e_info->multi_error_valid)
+ return false;
+ }
- pos = pci_find_aer_capability(dev);
+ /*
+ * When either
+ * 1) nosourceid==y;
+ * 2) bus id is equal to 0. Some ports might lose the bus
+ * id of error source id;
+ * 3) There are multiple errors and prior id comparing fails;
+ * We check AER status registers to find possible reporter.
+ */
+ if (atomic_read(&dev->enable_cnt) == 0)
+ return false;
+
+ /* Check if AER is enabled */
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &reg16);
+ if (!(reg16 & PCI_EXP_AER_FLAGS))
+ return false;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (!pos)
- return -EIO;
+ return false;
- pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status);
- pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status);
+ /* Check if error is recorded */
+ if (e_info->severity == AER_CORRECTABLE) {
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status);
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask);
+ } else {
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask);
+ }
+ if (status & ~mask)
+ return true;
- return 0;
+ return false;
}
-static int find_device_iter(struct device *device, void *data)
+static int find_device_iter(struct pci_dev *dev, void *data)
{
- struct pci_dev *dev;
- u16 id = *(unsigned long *)data;
- u8 secondary, subordinate, d_bus = id >> 8;
+ struct aer_err_info *e_info = (struct aer_err_info *)data;
- if (device->bus == &pci_bus_type) {
- dev = to_pci_dev(device);
- if (id == ((dev->bus->number << 8) | dev->devfn)) {
- /*
- * Device ID match
- */
- *(unsigned long*)data = (unsigned long)device;
+ if (is_error_source(dev, e_info)) {
+ /* List this device */
+ if (add_error_device(e_info, dev)) {
+ /* We cannot handle more... Stop iteration */
+ /* TODO: Should print error message here? */
return 1;
}
- /*
- * If device is P2P, check if it is an upstream?
- */
- if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
- pci_read_config_byte(dev, PCI_SECONDARY_BUS,
- &secondary);
- pci_read_config_byte(dev, PCI_SUBORDINATE_BUS,
- &subordinate);
- if (d_bus >= secondary && d_bus <= subordinate) {
- *(unsigned long*)data = (unsigned long)device;
- return 1;
- }
- }
+ /* If there is only a single error, stop iteration */
+ if (!e_info->multi_error_valid)
+ return 1;
}
-
return 0;
}
/**
* find_source_device - search through device hierarchy for source device
* @parent: pointer to Root Port pci_dev data structure
- * @id: device ID of agent who sends an error message to this Root Port
+ * @e_info: including detailed error information such like id
+ *
+ * Return true if found.
*
- * Invoked when error is detected at the Root Port.
+ * Invoked by DPC when error is detected at the Root Port.
+ * Caller of this function must set id, severity, and multi_error_valid of
+ * struct aer_err_info pointed by @e_info properly. This function must fill
+ * e_info->error_dev_num and e_info->dev[], based on the given information.
*/
-static struct device* find_source_device(struct pci_dev *parent, u16 id)
+static bool find_source_device(struct pci_dev *parent,
+ struct aer_err_info *e_info)
{
struct pci_dev *dev = parent;
- struct device *device;
- unsigned long device_addr;
- int status;
+ int result;
+
+ /* Must reset in this function */
+ e_info->error_dev_num = 0;
/* Is Root Port an agent that sends error message? */
- if (id == ((dev->bus->number << 8) | dev->devfn))
- return &dev->dev;
-
- do {
- device_addr = id;
- if ((status = device_for_each_child(&dev->dev,
- &device_addr, find_device_iter))) {
- device = (struct device*)device_addr;
- dev = to_pci_dev(device);
- if (id == ((dev->bus->number << 8) | dev->devfn))
- return device;
- }
- }while (status);
+ result = find_device_iter(dev, e_info);
+ if (result)
+ return true;
+
+ pci_walk_bus(parent->subordinate, find_device_iter, e_info);
- return NULL;
+ if (!e_info->error_dev_num) {
+ dev_printk(KERN_DEBUG, &parent->dev,
+ "can't find device of ID%04x\n",
+ e_info->id);
+ return false;
+ }
+ return true;
}
-static void report_error_detected(struct pci_dev *dev, void *data)
+static int report_error_detected(struct pci_dev *dev, void *data)
{
pci_ers_result_t vote;
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
struct aer_broadcast_data *result_data;
result_data = (struct aer_broadcast_data *) data;
+ device_lock(&dev->dev);
dev->error_state = result_data->state;
if (!dev->driver ||
@@ -219,69 +226,94 @@ 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;
+
+ /*
+ * If there's any device in the subtree that does not
+ * have an error_detected callback, returning
+ * PCI_ERS_RESULT_NO_AER_DRIVER prevents calling of
+ * the subsequent mmio_enabled/slot_reset/resume
+ * callbacks of "any" device in the subtree. All the
+ * devices in the subtree are left in the error state
+ * without recovery.
+ */
+
+ if (!(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE))
+ vote = PCI_ERS_RESULT_NO_AER_DRIVER;
+ else
+ vote = PCI_ERS_RESULT_NONE;
+ } else {
+ err_handler = dev->driver->err_handler;
+ vote = err_handler->error_detected(dev, result_data->state);
}
- err_handler = dev->driver->err_handler;
- vote = err_handler->error_detected(dev, result_data->state);
result_data->result = merge_result(result_data->result, vote);
- return;
+ device_unlock(&dev->dev);
+ return 0;
}
-static void report_mmio_enabled(struct pci_dev *dev, void *data)
+static int report_mmio_enabled(struct pci_dev *dev, void *data)
{
pci_ers_result_t vote;
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
struct aer_broadcast_data *result_data;
result_data = (struct aer_broadcast_data *) data;
+ device_lock(&dev->dev);
if (!dev->driver ||
!dev->driver->err_handler ||
!dev->driver->err_handler->mmio_enabled)
- return;
+ goto out;
err_handler = dev->driver->err_handler;
vote = err_handler->mmio_enabled(dev);
result_data->result = merge_result(result_data->result, vote);
- return;
+out:
+ device_unlock(&dev->dev);
+ return 0;
}
-static void report_slot_reset(struct pci_dev *dev, void *data)
+static int report_slot_reset(struct pci_dev *dev, void *data)
{
pci_ers_result_t vote;
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
struct aer_broadcast_data *result_data;
result_data = (struct aer_broadcast_data *) data;
+ device_lock(&dev->dev);
if (!dev->driver ||
!dev->driver->err_handler ||
!dev->driver->err_handler->slot_reset)
- return;
+ goto out;
err_handler = dev->driver->err_handler;
vote = err_handler->slot_reset(dev);
result_data->result = merge_result(result_data->result, vote);
- return;
+out:
+ device_unlock(&dev->dev);
+ return 0;
}
-static void report_resume(struct pci_dev *dev, void *data)
+static int report_resume(struct pci_dev *dev, void *data)
{
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
+ device_lock(&dev->dev);
dev->error_state = pci_channel_io_normal;
if (!dev->driver ||
!dev->driver->err_handler ||
- !dev->driver->err_handler->slot_reset)
- return;
+ !dev->driver->err_handler->resume)
+ goto out;
err_handler = dev->driver->err_handler;
err_handler->resume(dev);
- return;
+out:
+ device_unlock(&dev->dev);
+ return 0;
}
/**
@@ -298,11 +330,11 @@ static void report_resume(struct pci_dev *dev, void *data)
static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
enum pci_channel_state state,
char *error_mesg,
- void (*cb)(struct pci_dev *, void *))
+ int (*cb)(struct pci_dev *, void *))
{
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;
@@ -323,8 +355,7 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
pci_cleanup_aer_uncorrect_error_status(dev);
dev->error_state = pci_channel_io_normal;
}
- }
- else {
+ } else {
/*
* If the error is reported by an end point, we think this
* error is related to the upstream link of the end point.
@@ -335,85 +366,79 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
return result_data.result;
}
-struct find_aer_service_data {
- struct pcie_port_service_driver *aer_driver;
- int is_downstream;
-};
+/**
+ * default_reset_link - default reset function
+ * @dev: pointer to pci_dev data structure
+ *
+ * Invoked when performing link reset on a Downstream Port or a
+ * Root Port with no aer driver.
+ */
+static pci_ers_result_t default_reset_link(struct pci_dev *dev)
+{
+ pci_reset_bridge_secondary_bus(dev);
+ dev_printk(KERN_DEBUG, &dev->dev, "downstream link has been reset\n");
+ return PCI_ERS_RESULT_RECOVERED;
+}
static int find_aer_service_iter(struct device *device, void *data)
{
- struct device_driver *driver;
- struct pcie_port_service_driver *service_driver;
- struct pcie_device *pcie_dev;
- struct find_aer_service_data *result;
-
- result = (struct find_aer_service_data *) data;
-
- if (device->bus == &pcie_port_bus_type) {
- pcie_dev = to_pcie_device(device);
- if (pcie_dev->id.port_type == PCIE_SW_DOWNSTREAM_PORT)
- result->is_downstream = 1;
-
- driver = device->driver;
- if (driver) {
- service_driver = to_service_driver(driver);
- if (service_driver->id_table->service_type ==
- PCIE_PORT_SERVICE_AER) {
- result->aer_driver = service_driver;
- return 1;
- }
+ struct pcie_port_service_driver *service_driver, **drv;
+
+ drv = (struct pcie_port_service_driver **) data;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ service_driver = to_service_driver(device->driver);
+ if (service_driver->service == PCIE_PORT_SERVICE_AER) {
+ *drv = service_driver;
+ return 1;
}
}
return 0;
}
-static void find_aer_service(struct pci_dev *dev,
- struct find_aer_service_data *data)
+static struct pcie_port_service_driver *find_aer_service(struct pci_dev *dev)
{
- int retval;
- retval = device_for_each_child(&dev->dev, data, find_aer_service_iter);
+ struct pcie_port_service_driver *drv = NULL;
+
+ device_for_each_child(&dev->dev, &drv, find_aer_service_iter);
+
+ return drv;
}
-static pci_ers_result_t reset_link(struct pcie_device *aerdev,
- struct pci_dev *dev)
+static pci_ers_result_t reset_link(struct pci_dev *dev)
{
struct pci_dev *udev;
pci_ers_result_t status;
- struct find_aer_service_data data;
+ struct pcie_port_service_driver *driver;
- if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)
+ if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+ /* Reset this port for all subordinates */
udev = dev;
- else
- udev= dev->bus->self;
+ } else {
+ /* Reset the upstream component (likely downstream port) */
+ udev = dev->bus->self;
+ }
- data.is_downstream = 0;
- data.aer_driver = NULL;
- find_aer_service(udev, &data);
+ /* Use the aer driver of the component firstly */
+ driver = find_aer_service(udev);
- /*
- * Use the aer driver of the error agent firstly.
- * If it hasn't the aer driver, use the root port's
- */
- if (!data.aer_driver || !data.aer_driver->reset_link) {
- if (data.is_downstream &&
- aerdev->device.driver &&
- to_service_driver(aerdev->device.driver)->reset_link) {
- 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);
- return PCI_ERS_RESULT_DISCONNECT;
- }
+ if (driver && driver->reset_link) {
+ status = driver->reset_link(udev);
+ } else if (pci_pcie_type(udev) == PCI_EXP_TYPE_DOWNSTREAM ||
+ pci_pcie_type(udev) == PCI_EXP_TYPE_ROOT_PORT) {
+ status = default_reset_link(udev);
+ } else {
+ dev_printk(KERN_DEBUG, &dev->dev,
+ "no link-reset support at upstream device %s\n",
+ pci_name(udev));
+ 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;
}
@@ -422,7 +447,6 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
/**
* do_recovery - handle nonfatal/fatal error recovery process
- * @aerdev: pointer to a pcie_device data structure of root port
* @dev: pointer to a pci_dev data structure of agent detecting an error
* @severity: error severity type
*
@@ -430,9 +454,7 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
* error detected message to all downstream drivers within a hierarchy in
* question and return the returned code.
*/
-static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
- struct pci_dev *dev,
- int severity)
+static void do_recovery(struct pci_dev *dev, int severity)
{
pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED;
enum pci_channel_state state;
@@ -448,11 +470,9 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
report_error_detected);
if (severity == AER_FATAL) {
- result = reset_link(aerdev, dev);
- if (result != PCI_ERS_RESULT_RECOVERED) {
- /* TODO: Should panic here? */
- return result;
- }
+ result = reset_link(dev);
+ if (result != PCI_ERS_RESULT_RECOVERED)
+ goto failed;
}
if (status == PCI_ERS_RESULT_CAN_RECOVER)
@@ -473,13 +493,20 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
report_slot_reset);
}
- if (status == PCI_ERS_RESULT_RECOVERED)
- broadcast_error_message(dev,
+ if (status != PCI_ERS_RESULT_RECOVERED)
+ goto failed;
+
+ broadcast_error_message(dev,
state,
"resume",
report_resume);
- return status;
+ dev_info(&dev->dev, "AER: Device recovery successful\n");
+ return;
+
+failed:
+ /* TODO: Should kernel panic here? */
+ dev_info(&dev->dev, "AER: Device recovery failed\n");
}
/**
@@ -490,154 +517,139 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
*
* Invoked when an error being detected by Root Port.
*/
-static void handle_error_source(struct pcie_device * aerdev,
+static void handle_error_source(struct pcie_device *aerdev,
struct pci_dev *dev,
- struct aer_err_info info)
+ struct aer_err_info *info)
{
- pci_ers_result_t status = 0;
int pos;
- if (info.severity == AER_CORRECTABLE) {
+ if (info->severity == AER_CORRECTABLE) {
/*
- * Correctable error does not need software intevention.
+ * Correctable error does not need software intervention.
* No need to go through error recovery process.
*/
- pos = pci_find_aer_capability(dev);
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (pos)
pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
- info.status);
- } else {
- status = do_recovery(aerdev, dev, info.severity);
- if (status == PCI_ERS_RESULT_RECOVERED) {
- printk(KERN_DEBUG "AER driver successfully recovered\n");
- } else {
- /* TODO: Should kernel panic here? */
- printk(KERN_DEBUG "AER driver didn't recover\n");
- }
- }
-}
-
-/**
- * aer_enable_rootport - enable Root Port's interrupts when receiving messages
- * @rpc: pointer to a Root Port data structure
- *
- * Invoked when PCIE bus loads AER service driver.
- */
-void aer_enable_rootport(struct aer_rpc *rpc)
-{
- struct pci_dev *pdev = rpc->rpd->port;
- int pos, aer_pos;
- u16 reg16;
- u32 reg32;
-
- pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
- /* Clear PCIE Capability's Device Status */
- pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
- pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);
-
- /* Disable system error generation in response to error messages */
- pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, &reg16);
- reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
- pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);
-
- aer_pos = pci_find_aer_capability(pdev);
- /* Clear error status */
- pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32);
- pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
- pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32);
- pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
- pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32);
- pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
-
- /* Enable Root Port device reporting error itself */
- pci_read_config_word(pdev, pos+PCI_EXP_DEVCTL, &reg16);
- reg16 = reg16 |
- PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE |
- PCI_EXP_DEVCTL_URRE;
- pci_write_config_word(pdev, pos+PCI_EXP_DEVCTL,
- reg16);
-
- /* Enable Root Port's interrupt in response to error messages */
- pci_write_config_dword(pdev,
- aer_pos + PCI_ERR_ROOT_COMMAND,
- ROOT_PORT_INTR_ON_MESG_MASK);
+ info->status);
+ } else
+ do_recovery(dev, info->severity);
}
-/**
- * disable_root_aer - disable Root Port's interrupts when receiving messages
- * @rpc: pointer to a Root Port data structure
- *
- * Invoked when PCIE bus unloads AER service driver.
- */
-static void disable_root_aer(struct aer_rpc *rpc)
-{
- struct pci_dev *pdev = rpc->rpd->port;
- u32 reg32;
- int pos;
+#ifdef CONFIG_ACPI_APEI_PCIEAER
+static void aer_recover_work_func(struct work_struct *work);
- pos = pci_find_aer_capability(pdev);
- /* Disable Root's interrupt in response to error messages */
- pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0);
+#define AER_RECOVER_RING_ORDER 4
+#define AER_RECOVER_RING_SIZE (1 << AER_RECOVER_RING_ORDER)
- /* Clear Root's error status reg */
- pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, &reg32);
- pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
-}
+struct aer_recover_entry {
+ u8 bus;
+ u8 devfn;
+ u16 domain;
+ int severity;
+ struct aer_capability_regs *regs;
+};
-/**
- * get_e_source - retrieve an error source
- * @rpc: pointer to the root port which holds an error
- *
- * Invoked by DPC handler to consume an error.
+static DEFINE_KFIFO(aer_recover_ring, struct aer_recover_entry,
+ AER_RECOVER_RING_SIZE);
+/*
+ * Mutual exclusion for writers of aer_recover_ring, reader side don't
+ * need lock, because there is only one reader and lock is not needed
+ * between reader and writer.
*/
-static struct aer_err_source* get_e_source(struct aer_rpc *rpc)
+static DEFINE_SPINLOCK(aer_recover_ring_lock);
+static DECLARE_WORK(aer_recover_work, aer_recover_work_func);
+
+void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
+ int severity, struct aer_capability_regs *aer_regs)
{
- struct aer_err_source *e_source;
unsigned long flags;
+ struct aer_recover_entry entry = {
+ .bus = bus,
+ .devfn = devfn,
+ .domain = domain,
+ .severity = severity,
+ .regs = aer_regs,
+ };
+
+ spin_lock_irqsave(&aer_recover_ring_lock, flags);
+ if (kfifo_put(&aer_recover_ring, entry))
+ schedule_work(&aer_recover_work);
+ else
+ pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n",
+ domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+ spin_unlock_irqrestore(&aer_recover_ring_lock, flags);
+}
+EXPORT_SYMBOL_GPL(aer_recover_queue);
- /* Lock access to Root error producer/consumer index */
- spin_lock_irqsave(&rpc->e_lock, flags);
- if (rpc->prod_idx == rpc->cons_idx) {
- spin_unlock_irqrestore(&rpc->e_lock, flags);
- return NULL;
+static void aer_recover_work_func(struct work_struct *work)
+{
+ struct aer_recover_entry entry;
+ struct pci_dev *pdev;
+
+ while (kfifo_get(&aer_recover_ring, &entry)) {
+ pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus,
+ entry.devfn);
+ if (!pdev) {
+ pr_err("AER recover: Can not find pci_dev for %04x:%02x:%02x:%x\n",
+ entry.domain, entry.bus,
+ PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn));
+ continue;
+ }
+ cper_print_aer(pdev, entry.severity, entry.regs);
+ do_recovery(pdev, entry.severity);
+ pci_dev_put(pdev);
}
- e_source = &rpc->e_sources[rpc->cons_idx];
- rpc->cons_idx++;
- if (rpc->cons_idx == AER_ERROR_SOURCES_MAX)
- rpc->cons_idx = 0;
- spin_unlock_irqrestore(&rpc->e_lock, flags);
-
- return e_source;
}
+#endif
+/**
+ * get_device_error_info - read error status from dev and store it to info
+ * @dev: pointer to the device expected to have a error record
+ * @info: pointer to structure to store the error record
+ *
+ * Return 1 on success, 0 on error.
+ *
+ * Note that @info is reused among all error devices. Clear fields properly.
+ */
static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
{
- int pos;
+ int pos, temp;
- pos = pci_find_aer_capability(dev);
+ /* Must reset in this function */
+ info->status = 0;
+ info->tlp_header_valid = 0;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
/* The device might not support AER */
if (!pos)
- return AER_SUCCESS;
+ return 1;
if (info->severity == AER_CORRECTABLE) {
pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS,
&info->status);
- if (!(info->status & ERR_CORRECTABLE_ERROR_MASK))
- return AER_UNSUCCESS;
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK,
+ &info->mask);
+ if (!(info->status & ~info->mask))
+ return 0;
} else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE ||
info->severity == AER_NONFATAL) {
/* Link is still healthy for IO reads */
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
&info->status);
- if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK))
- return AER_UNSUCCESS;
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK,
+ &info->mask);
+ if (!(info->status & ~info->mask))
+ return 0;
+
+ /* Get First Error Pointer */
+ pci_read_config_dword(dev, pos + PCI_ERR_CAP, &temp);
+ info->first_error = PCI_ERR_CAP_FEP(temp);
if (info->status & AER_LOG_TLP_MASKS) {
- info->flags |= AER_TLP_HEADER_VALID_FLAG;
+ info->tlp_header_valid = 1;
pci_read_config_dword(dev,
pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0);
pci_read_config_dword(dev,
@@ -649,7 +661,23 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
}
}
- return AER_SUCCESS;
+ return 1;
+}
+
+static inline void aer_process_err_devices(struct pcie_device *p_device,
+ struct aer_err_info *e_info)
+{
+ int i;
+
+ /* Report all before handle them, not to lost records by reset etc. */
+ for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
+ if (get_device_error_info(e_info->dev[i], e_info))
+ aer_print_error(e_info->dev[i], e_info);
+ }
+ for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
+ if (get_device_error_info(e_info->dev[i], e_info))
+ handle_error_source(p_device, e_info->dev[i], e_info);
+ }
}
/**
@@ -660,46 +688,84 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
static void aer_isr_one_error(struct pcie_device *p_device,
struct aer_err_source *e_src)
{
- struct device *s_device;
- struct aer_err_info e_info = {0, 0, 0,};
- int i;
- u16 id;
+ struct aer_err_info *e_info;
+
+ /* struct aer_err_info might be big, so we allocate it with slab */
+ e_info = kmalloc(sizeof(struct aer_err_info), GFP_KERNEL);
+ if (!e_info) {
+ dev_printk(KERN_DEBUG, &p_device->port->dev,
+ "Can't allocate mem when processing AER errors\n");
+ return;
+ }
/*
* There is a possibility that both correctable error and
* uncorrectable error being logged. Report correctable error first.
*/
- for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) {
- if (i > 4)
- break;
- if (!(e_src->status & i))
- continue;
+ if (e_src->status & PCI_ERR_ROOT_COR_RCV) {
+ e_info->id = ERR_COR_ID(e_src->id);
+ e_info->severity = AER_CORRECTABLE;
- /* Init comprehensive error information */
- if (i & PCI_ERR_ROOT_COR_RCV) {
- id = ERR_COR_ID(e_src->id);
- e_info.severity = AER_CORRECTABLE;
- } else {
- id = ERR_UNCOR_ID(e_src->id);
- e_info.severity = ((e_src->status >> 6) & 1);
- }
- if (e_src->status &
- (PCI_ERR_ROOT_MULTI_COR_RCV |
- PCI_ERR_ROOT_MULTI_UNCOR_RCV))
- e_info.flags |= AER_MULTI_ERROR_VALID_FLAG;
- if (!(s_device = find_source_device(p_device->port, id))) {
- printk(KERN_DEBUG "%s->can't find device of ID%04x\n",
- __FUNCTION__, id);
- continue;
- }
- if (get_device_error_info(to_pci_dev(s_device), &e_info) ==
- AER_SUCCESS) {
- aer_print_error(to_pci_dev(s_device), &e_info);
- handle_error_source(p_device,
- to_pci_dev(s_device),
- e_info);
- }
+ if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV)
+ e_info->multi_error_valid = 1;
+ else
+ e_info->multi_error_valid = 0;
+
+ aer_print_port_info(p_device->port, e_info);
+
+ if (find_source_device(p_device->port, e_info))
+ aer_process_err_devices(p_device, e_info);
+ }
+
+ if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {
+ e_info->id = ERR_UNCOR_ID(e_src->id);
+
+ if (e_src->status & PCI_ERR_ROOT_FATAL_RCV)
+ e_info->severity = AER_FATAL;
+ else
+ e_info->severity = AER_NONFATAL;
+
+ if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV)
+ e_info->multi_error_valid = 1;
+ else
+ e_info->multi_error_valid = 0;
+
+ aer_print_port_info(p_device->port, e_info);
+
+ if (find_source_device(p_device->port, e_info))
+ aer_process_err_devices(p_device, e_info);
}
+
+ kfree(e_info);
+}
+
+/**
+ * get_e_source - retrieve an error source
+ * @rpc: pointer to the root port which holds an error
+ * @e_src: pointer to store retrieved error source
+ *
+ * Return 1 if an error source is retrieved, otherwise 0.
+ *
+ * Invoked by DPC handler to consume an error.
+ */
+static int get_e_source(struct aer_rpc *rpc, struct aer_err_source *e_src)
+{
+ unsigned long flags;
+
+ /* Lock access to Root error producer/consumer index */
+ spin_lock_irqsave(&rpc->e_lock, flags);
+ if (rpc->prod_idx == rpc->cons_idx) {
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+ return 0;
+ }
+
+ *e_src = rpc->e_sources[rpc->cons_idx];
+ rpc->cons_idx++;
+ if (rpc->cons_idx == AER_ERROR_SOURCES_MAX)
+ rpc->cons_idx = 0;
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+
+ return 1;
}
/**
@@ -712,34 +778,17 @@ void aer_isr(struct work_struct *work)
{
struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler);
struct pcie_device *p_device = rpc->rpd;
- struct aer_err_source *e_src;
+ struct aer_err_source uninitialized_var(e_src);
mutex_lock(&rpc->rpc_mutex);
- e_src = get_e_source(rpc);
- while (e_src) {
- aer_isr_one_error(p_device, e_src);
- e_src = get_e_source(rpc);
- }
+ while (get_e_source(rpc, &e_src))
+ aer_isr_one_error(p_device, &e_src);
mutex_unlock(&rpc->rpc_mutex);
wake_up(&rpc->wait_release);
}
/**
- * aer_delete_rootport - disable root port aer and delete service data
- * @rpc: pointer to a root port device being deleted
- *
- * Invoked when AER service unloaded on a specific Root Port
- */
-void aer_delete_rootport(struct aer_rpc *rpc)
-{
- /* Disable root port AER itself */
- disable_root_aer(rpc);
-
- kfree(rpc);
-}
-
-/**
* aer_init - provide AER initialization
* @dev: pointer to AER pcie device
*
@@ -747,15 +796,10 @@ void aer_delete_rootport(struct aer_rpc *rpc)
*/
int aer_init(struct pcie_device *dev)
{
- if (aer_osc_setup(dev) && !forceload)
- return -ENXIO;
-
- return AER_SUCCESS;
+ if (forceload) {
+ dev_printk(KERN_DEBUG, &dev->device,
+ "aerdrv forceload requested.\n");
+ pcie_aer_force_firmware_first(dev->port, 0);
+ }
+ return 0;
}
-
-EXPORT_SYMBOL_GPL(pci_find_aer_capability);
-EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);
-EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);
-EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
-EXPORT_SYMBOL_GPL(pci_cleanup_aer_correct_error_status);
-
diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c
index 3933d4f30e8..36ed31b5219 100644
--- a/drivers/pci/pcie/aer/aerdrv_errprint.c
+++ b/drivers/pci/pcie/aer/aerdrv_errprint.c
@@ -19,123 +19,86 @@
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/suspend.h>
+#include <linux/cper.h>
#include "aerdrv.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/ras.h>
+
#define AER_AGENT_RECEIVER 0
#define AER_AGENT_REQUESTER 1
#define AER_AGENT_COMPLETER 2
#define AER_AGENT_TRANSMITTER 3
-#define AER_AGENT_REQUESTER_MASK (PCI_ERR_UNC_COMP_TIME| \
- PCI_ERR_UNC_UNSUP)
-
-#define AER_AGENT_COMPLETER_MASK PCI_ERR_UNC_COMP_ABORT
-
-#define AER_AGENT_TRANSMITTER_MASK(t, e) (e & (PCI_ERR_COR_REP_ROLL| \
- ((t == AER_CORRECTABLE) ? PCI_ERR_COR_REP_TIMER: 0)))
+#define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \
+ 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP))
+#define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \
+ 0 : PCI_ERR_UNC_COMP_ABORT)
+#define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \
+ (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0)
#define AER_GET_AGENT(t, e) \
- ((e & AER_AGENT_COMPLETER_MASK) ? AER_AGENT_COMPLETER : \
- (e & AER_AGENT_REQUESTER_MASK) ? AER_AGENT_REQUESTER : \
- (AER_AGENT_TRANSMITTER_MASK(t, e)) ? AER_AGENT_TRANSMITTER : \
+ ((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \
+ (e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \
+ (e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \
AER_AGENT_RECEIVER)
-#define AER_PHYSICAL_LAYER_ERROR_MASK PCI_ERR_COR_RCVR
-#define AER_DATA_LINK_LAYER_ERROR_MASK(t, e) \
- (PCI_ERR_UNC_DLP| \
- PCI_ERR_COR_BAD_TLP| \
- PCI_ERR_COR_BAD_DLLP| \
- PCI_ERR_COR_REP_ROLL| \
- ((t == AER_CORRECTABLE) ? \
- PCI_ERR_COR_REP_TIMER: 0))
-
#define AER_PHYSICAL_LAYER_ERROR 0
#define AER_DATA_LINK_LAYER_ERROR 1
#define AER_TRANSACTION_LAYER_ERROR 2
-#define AER_GET_LAYER_ERROR(t, e) \
- ((e & AER_PHYSICAL_LAYER_ERROR_MASK) ? \
- AER_PHYSICAL_LAYER_ERROR : \
- (e & AER_DATA_LINK_LAYER_ERROR_MASK(t, e)) ? \
- AER_DATA_LINK_LAYER_ERROR : \
- AER_TRANSACTION_LAYER_ERROR)
+#define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \
+ PCI_ERR_COR_RCVR : 0)
+#define AER_DATA_LINK_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \
+ (PCI_ERR_COR_BAD_TLP| \
+ PCI_ERR_COR_BAD_DLLP| \
+ PCI_ERR_COR_REP_ROLL| \
+ PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP)
+
+#define AER_GET_LAYER_ERROR(t, e) \
+ ((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \
+ (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \
+ AER_TRANSACTION_LAYER_ERROR)
/*
* AER error strings
*/
-static char* aer_error_severity_string[] = {
+static const char *aer_error_severity_string[] = {
"Uncorrected (Non-Fatal)",
"Uncorrected (Fatal)",
"Corrected"
};
-static char* aer_error_layer[] = {
+static const char *aer_error_layer[] = {
"Physical Layer",
"Data Link Layer",
"Transaction Layer"
};
-static char* aer_correctable_error_string[] = {
- "Receiver Error ", /* Bit Position 0 */
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- "Bad TLP ", /* Bit Position 6 */
- "Bad DLLP ", /* Bit Position 7 */
- "RELAY_NUM Rollover ", /* Bit Position 8 */
- NULL,
- NULL,
- NULL,
- "Replay Timer Timeout ", /* Bit Position 12 */
- "Advisory Non-Fatal ", /* Bit Position 13 */
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
+
+static const char *aer_correctable_error_string[] = {
+ "Receiver Error", /* Bit Position 0 */
NULL,
NULL,
NULL,
NULL,
NULL,
+ "Bad TLP", /* Bit Position 6 */
+ "Bad DLLP", /* Bit Position 7 */
+ "RELAY_NUM Rollover", /* Bit Position 8 */
NULL,
NULL,
NULL,
+ "Replay Timer Timeout", /* Bit Position 12 */
+ "Advisory Non-Fatal", /* Bit Position 13 */
};
-static char* aer_uncorrectable_error_string[] = {
- NULL,
- NULL,
- NULL,
- NULL,
- "Data Link Protocol ", /* Bit Position 4 */
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- "Poisoned TLP ", /* Bit Position 12 */
- "Flow Control Protocol ", /* Bit Position 13 */
- "Completion Timeout ", /* Bit Position 14 */
- "Completer Abort ", /* Bit Position 15 */
- "Unexpected Completion ", /* Bit Position 16 */
- "Receiver Overflow ", /* Bit Position 17 */
- "Malformed TLP ", /* Bit Position 18 */
- "ECRC ", /* Bit Position 19 */
- "Unsupported Request ", /* Bit Position 20 */
+static const char *aer_uncorrectable_error_string[] = {
NULL,
NULL,
NULL,
NULL,
+ "Data Link Protocol", /* Bit Position 4 */
NULL,
NULL,
NULL,
@@ -143,106 +106,160 @@ static char* aer_uncorrectable_error_string[] = {
NULL,
NULL,
NULL,
+ "Poisoned TLP", /* Bit Position 12 */
+ "Flow Control Protocol", /* Bit Position 13 */
+ "Completion Timeout", /* Bit Position 14 */
+ "Completer Abort", /* Bit Position 15 */
+ "Unexpected Completion", /* Bit Position 16 */
+ "Receiver Overflow", /* Bit Position 17 */
+ "Malformed TLP", /* Bit Position 18 */
+ "ECRC", /* Bit Position 19 */
+ "Unsupported Request", /* Bit Position 20 */
};
-static char* aer_agent_string[] = {
+static const char *aer_agent_string[] = {
"Receiver ID",
"Requester ID",
"Completer ID",
"Transmitter ID"
};
-static char * aer_get_error_source_name(int severity,
- unsigned int status,
- char errmsg_buff[])
+static void __print_tlp_header(struct pci_dev *dev,
+ struct aer_header_log_regs *t)
+{
+ unsigned char *tlp = (unsigned char *)&t;
+
+ dev_err(&dev->dev, " TLP Header:"
+ " %02x%02x%02x%02x %02x%02x%02x%02x"
+ " %02x%02x%02x%02x %02x%02x%02x%02x\n",
+ *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp,
+ *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4),
+ *(tlp + 11), *(tlp + 10), *(tlp + 9),
+ *(tlp + 8), *(tlp + 15), *(tlp + 14),
+ *(tlp + 13), *(tlp + 12));
+}
+
+static void __aer_print_error(struct pci_dev *dev,
+ struct aer_err_info *info)
{
- int i;
- char * errmsg = NULL;
+ int i, status;
+ const char *errmsg = NULL;
+ status = (info->status & ~info->mask);
for (i = 0; i < 32; i++) {
if (!(status & (1 << i)))
continue;
- if (severity == AER_CORRECTABLE)
- errmsg = aer_correctable_error_string[i];
+ if (info->severity == AER_CORRECTABLE)
+ errmsg = i < ARRAY_SIZE(aer_correctable_error_string) ?
+ aer_correctable_error_string[i] : NULL;
else
- errmsg = aer_uncorrectable_error_string[i];
+ errmsg = i < ARRAY_SIZE(aer_uncorrectable_error_string) ?
+ aer_uncorrectable_error_string[i] : NULL;
- if (!errmsg) {
- sprintf(errmsg_buff, "Unknown Error Bit %2d ", i);
- errmsg = errmsg_buff;
- }
+ if (errmsg)
+ dev_err(&dev->dev, " [%2d] %-22s%s\n", i, errmsg,
+ info->first_error == i ? " (First)" : "");
+ else
+ dev_err(&dev->dev, " [%2d] Unknown Error Bit%s\n",
+ i, info->first_error == i ? " (First)" : "");
+ }
+}
- break;
+void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
+{
+ int layer, agent;
+ int id = ((dev->bus->number << 8) | dev->devfn);
+
+ if (!info->status) {
+ dev_err(&dev->dev, "PCIe Bus Error: severity=%s, type=Unaccessible, id=%04x(Unregistered Agent ID)\n",
+ aer_error_severity_string[info->severity], id);
+ goto out;
}
- return errmsg;
+ layer = AER_GET_LAYER_ERROR(info->severity, info->status);
+ agent = AER_GET_AGENT(info->severity, info->status);
+
+ dev_err(&dev->dev, "PCIe Bus Error: severity=%s, type=%s, id=%04x(%s)\n",
+ aer_error_severity_string[info->severity],
+ aer_error_layer[layer], id, aer_agent_string[agent]);
+
+ dev_err(&dev->dev, " device [%04x:%04x] error status/mask=%08x/%08x\n",
+ dev->vendor, dev->device,
+ info->status, info->mask);
+
+ __aer_print_error(dev, info);
+
+ if (info->tlp_header_valid)
+ __print_tlp_header(dev, &info->tlp);
+
+out:
+ if (info->id && info->error_dev_num > 1 && info->id == id)
+ dev_err(&dev->dev, " Error of this Agent(%04x) is reported first\n", id);
+
+ trace_aer_event(dev_name(&dev->dev), (info->status & ~info->mask),
+ info->severity);
}
-static DEFINE_SPINLOCK(logbuf_lock);
-static char errmsg_buff[100];
-void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
+void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info)
{
- char * errmsg;
- int err_layer, agent;
- char * loglevel;
-
- if (info->severity == AER_CORRECTABLE)
- loglevel = KERN_WARNING;
- else
- loglevel = KERN_ERR;
-
- printk("%s+------ PCI-Express Device Error ------+\n", loglevel);
- printk("%sError Severity\t\t: %s\n", loglevel,
- aer_error_severity_string[info->severity]);
-
- if ( info->status == 0) {
- printk("%sPCIE Bus Error type\t: (Unaccessible)\n", loglevel);
- printk("%sUnaccessible Received\t: %s\n", loglevel,
- info->flags & AER_MULTI_ERROR_VALID_FLAG ?
- "Multiple" : "First");
- printk("%sUnregistered Agent ID\t: %04x\n", loglevel,
- (dev->bus->number << 8) | dev->devfn);
- } else {
- err_layer = AER_GET_LAYER_ERROR(info->severity, info->status);
- printk("%sPCIE Bus Error type\t: %s\n", loglevel,
- aer_error_layer[err_layer]);
-
- spin_lock(&logbuf_lock);
- errmsg = aer_get_error_source_name(info->severity,
- info->status,
- errmsg_buff);
- printk("%s%s\t: %s\n", loglevel, errmsg,
- info->flags & AER_MULTI_ERROR_VALID_FLAG ?
- "Multiple" : "First");
- spin_unlock(&logbuf_lock);
-
- agent = AER_GET_AGENT(info->severity, info->status);
- printk("%s%s\t\t: %04x\n", loglevel,
- aer_agent_string[agent],
- (dev->bus->number << 8) | dev->devfn);
-
- printk("%sVendorID=%04xh, DeviceID=%04xh,"
- " Bus=%02xh, Device=%02xh, Function=%02xh\n",
- loglevel,
- dev->vendor,
- dev->device,
- dev->bus->number,
- PCI_SLOT(dev->devfn),
- PCI_FUNC(dev->devfn));
-
- if (info->flags & AER_TLP_HEADER_VALID_FLAG) {
- unsigned char *tlp = (unsigned char *) &info->tlp;
- printk("%sTLB Header:\n", loglevel);
- printk("%s%02x%02x%02x%02x %02x%02x%02x%02x"
- " %02x%02x%02x%02x %02x%02x%02x%02x\n",
- loglevel,
- *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp,
- *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4),
- *(tlp + 11), *(tlp + 10), *(tlp + 9),
- *(tlp + 8), *(tlp + 15), *(tlp + 14),
- *(tlp + 13), *(tlp + 12));
- }
+ dev_info(&dev->dev, "AER: %s%s error received: id=%04x\n",
+ info->multi_error_valid ? "Multiple " : "",
+ aer_error_severity_string[info->severity], info->id);
+}
+
+#ifdef CONFIG_ACPI_APEI_PCIEAER
+int cper_severity_to_aer(int cper_severity)
+{
+ switch (cper_severity) {
+ case CPER_SEV_RECOVERABLE:
+ return AER_NONFATAL;
+ case CPER_SEV_FATAL:
+ return AER_FATAL;
+ default:
+ return AER_CORRECTABLE;
}
}
+EXPORT_SYMBOL_GPL(cper_severity_to_aer);
+
+void cper_print_aer(struct pci_dev *dev, int cper_severity,
+ struct aer_capability_regs *aer)
+{
+ int aer_severity, layer, agent, status_strs_size, tlp_header_valid = 0;
+ u32 status, mask;
+ const char **status_strs;
+
+ aer_severity = cper_severity_to_aer(cper_severity);
+ if (aer_severity == AER_CORRECTABLE) {
+ status = aer->cor_status;
+ mask = aer->cor_mask;
+ status_strs = aer_correctable_error_string;
+ status_strs_size = ARRAY_SIZE(aer_correctable_error_string);
+ } else {
+ status = aer->uncor_status;
+ mask = aer->uncor_mask;
+ status_strs = aer_uncorrectable_error_string;
+ status_strs_size = ARRAY_SIZE(aer_uncorrectable_error_string);
+ tlp_header_valid = status & AER_LOG_TLP_MASKS;
+ }
+
+ layer = AER_GET_LAYER_ERROR(aer_severity, status);
+ agent = AER_GET_AGENT(aer_severity, status);
+
+ dev_err(&dev->dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", status, mask);
+ cper_print_bits("", status, status_strs, status_strs_size);
+ dev_err(&dev->dev, "aer_layer=%s, aer_agent=%s\n",
+ aer_error_layer[layer], aer_agent_string[agent]);
+
+ if (aer_severity != AER_CORRECTABLE)
+ dev_err(&dev->dev, "aer_uncor_severity: 0x%08x\n",
+ aer->uncor_severity);
+
+ if (tlp_header_valid)
+ __print_tlp_header(dev, &aer->header_log);
+
+ trace_aer_event(dev_name(&dev->dev), (status & ~mask),
+ aer_severity);
+}
+#endif
diff --git a/drivers/pci/pcie/aer/ecrc.c b/drivers/pci/pcie/aer/ecrc.c
new file mode 100644
index 00000000000..a2747a663bc
--- /dev/null
+++ b/drivers/pci/pcie/aer/ecrc.c
@@ -0,0 +1,131 @@
+/*
+ * Enables/disables PCIe ECRC checking.
+ *
+ * (C) Copyright 2009 Hewlett-Packard Development Company, L.P.
+ * Andrew Patterson <andrew.patterson@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/errno.h>
+#include "../../pci.h"
+
+#define ECRC_POLICY_DEFAULT 0 /* ECRC set by BIOS */
+#define ECRC_POLICY_OFF 1 /* ECRC off for performance */
+#define ECRC_POLICY_ON 2 /* ECRC on for data integrity */
+
+static int ecrc_policy = ECRC_POLICY_DEFAULT;
+
+static const char *ecrc_policy_str[] = {
+ [ECRC_POLICY_DEFAULT] = "bios",
+ [ECRC_POLICY_OFF] = "off",
+ [ECRC_POLICY_ON] = "on"
+};
+
+/**
+ * enable_ercr_checking - enable PCIe ECRC checking for a device
+ * @dev: the PCI device
+ *
+ * Returns 0 on success, or negative on failure.
+ */
+static int enable_ecrc_checking(struct pci_dev *dev)
+{
+ int pos;
+ u32 reg32;
+
+ if (!pci_is_pcie(dev))
+ return -ENODEV;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ if (!pos)
+ return -ENODEV;
+
+ pci_read_config_dword(dev, pos + PCI_ERR_CAP, &reg32);
+ if (reg32 & PCI_ERR_CAP_ECRC_GENC)
+ reg32 |= PCI_ERR_CAP_ECRC_GENE;
+ if (reg32 & PCI_ERR_CAP_ECRC_CHKC)
+ reg32 |= PCI_ERR_CAP_ECRC_CHKE;
+ pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
+
+ return 0;
+}
+
+/**
+ * disable_ercr_checking - disables PCIe ECRC checking for a device
+ * @dev: the PCI device
+ *
+ * Returns 0 on success, or negative on failure.
+ */
+static int disable_ecrc_checking(struct pci_dev *dev)
+{
+ int pos;
+ u32 reg32;
+
+ if (!pci_is_pcie(dev))
+ return -ENODEV;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ if (!pos)
+ return -ENODEV;
+
+ pci_read_config_dword(dev, pos + PCI_ERR_CAP, &reg32);
+ reg32 &= ~(PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE);
+ pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
+
+ return 0;
+}
+
+/**
+ * pcie_set_ecrc_checking - set/unset PCIe ECRC checking for a device based on global policy
+ * @dev: the PCI device
+ */
+void pcie_set_ecrc_checking(struct pci_dev *dev)
+{
+ switch (ecrc_policy) {
+ case ECRC_POLICY_DEFAULT:
+ return;
+ case ECRC_POLICY_OFF:
+ disable_ecrc_checking(dev);
+ break;
+ case ECRC_POLICY_ON:
+ enable_ecrc_checking(dev);
+ break;
+ default:
+ return;
+ }
+}
+
+/**
+ * pcie_ecrc_get_policy - parse kernel command-line ecrc option
+ */
+void pcie_ecrc_get_policy(char *str)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++)
+ if (!strncmp(str, ecrc_policy_str[i],
+ strlen(ecrc_policy_str[i])))
+ break;
+ if (i >= ARRAY_SIZE(ecrc_policy_str))
+ return;
+
+ ecrc_policy = i;
+}
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
new file mode 100644
index 00000000000..e1e7026b838
--- /dev/null
+++ b/drivers/pci/pcie/aspm.c
@@ -0,0 +1,991 @@
+/*
+ * File: drivers/pci/pcie/aspm.c
+ * Enabling PCIe link L0s/L1 state and Clock Power Management
+ *
+ * Copyright (C) 2007 Intel
+ * Copyright (C) Zhang Yanmin (yanmin.zhang@intel.com)
+ * Copyright (C) Shaohua Li (shaohua.li@intel.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/pci-aspm.h>
+#include "../pci.h"
+
+#ifdef MODULE_PARAM_PREFIX
+#undef MODULE_PARAM_PREFIX
+#endif
+#define MODULE_PARAM_PREFIX "pcie_aspm."
+
+/* Note: those are not register definitions */
+#define ASPM_STATE_L0S_UP (1) /* Upstream direction L0s state */
+#define ASPM_STATE_L0S_DW (2) /* Downstream direction L0s state */
+#define ASPM_STATE_L1 (4) /* L1 state */
+#define ASPM_STATE_L0S (ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW)
+#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1)
+
+struct aspm_latency {
+ u32 l0s; /* L0s latency (nsec) */
+ u32 l1; /* L1 latency (nsec) */
+};
+
+struct pcie_link_state {
+ struct pci_dev *pdev; /* Upstream component of the Link */
+ struct pcie_link_state *root; /* pointer to the root port link */
+ struct pcie_link_state *parent; /* pointer to the parent Link state */
+ struct list_head sibling; /* node in link_list */
+ struct list_head children; /* list of child link states */
+ struct list_head link; /* node in parent's children list */
+
+ /* ASPM state */
+ u32 aspm_support:3; /* Supported ASPM state */
+ u32 aspm_enabled:3; /* Enabled ASPM state */
+ u32 aspm_capable:3; /* Capable ASPM state with latency */
+ u32 aspm_default:3; /* Default ASPM state by BIOS */
+ u32 aspm_disable:3; /* Disabled ASPM state */
+
+ /* Clock PM state */
+ u32 clkpm_capable:1; /* Clock PM capable? */
+ u32 clkpm_enabled:1; /* Current Clock PM state */
+ u32 clkpm_default:1; /* Default Clock PM state by BIOS */
+
+ /* Exit latencies */
+ struct aspm_latency latency_up; /* Upstream direction exit latency */
+ struct aspm_latency latency_dw; /* Downstream direction exit latency */
+ /*
+ * Endpoint acceptable latencies. A pcie downstream port only
+ * has one slot under it, so at most there are 8 functions.
+ */
+ struct aspm_latency acceptable[8];
+};
+
+static int aspm_disabled, aspm_force;
+static bool aspm_support_enabled = true;
+static DEFINE_MUTEX(aspm_lock);
+static LIST_HEAD(link_list);
+
+#define POLICY_DEFAULT 0 /* BIOS default setting */
+#define POLICY_PERFORMANCE 1 /* high performance */
+#define POLICY_POWERSAVE 2 /* high power saving */
+
+#ifdef CONFIG_PCIEASPM_PERFORMANCE
+static int aspm_policy = POLICY_PERFORMANCE;
+#elif defined CONFIG_PCIEASPM_POWERSAVE
+static int aspm_policy = POLICY_POWERSAVE;
+#else
+static int aspm_policy;
+#endif
+
+static const char *policy_str[] = {
+ [POLICY_DEFAULT] = "default",
+ [POLICY_PERFORMANCE] = "performance",
+ [POLICY_POWERSAVE] = "powersave"
+};
+
+#define LINK_RETRAIN_TIMEOUT HZ
+
+static int policy_to_aspm_state(struct pcie_link_state *link)
+{
+ switch (aspm_policy) {
+ case POLICY_PERFORMANCE:
+ /* Disable ASPM and Clock PM */
+ return 0;
+ case POLICY_POWERSAVE:
+ /* Enable ASPM L0s/L1 */
+ return ASPM_STATE_ALL;
+ case POLICY_DEFAULT:
+ return link->aspm_default;
+ }
+ return 0;
+}
+
+static int policy_to_clkpm_state(struct pcie_link_state *link)
+{
+ switch (aspm_policy) {
+ case POLICY_PERFORMANCE:
+ /* Disable ASPM and Clock PM */
+ return 0;
+ case POLICY_POWERSAVE:
+ /* Disable Clock PM */
+ return 1;
+ case POLICY_DEFAULT:
+ return link->clkpm_default;
+ }
+ return 0;
+}
+
+static void pcie_set_clkpm_nocheck(struct pcie_link_state *link, int enable)
+{
+ struct pci_dev *child;
+ struct pci_bus *linkbus = link->pdev->subordinate;
+
+ list_for_each_entry(child, &linkbus->devices, bus_list) {
+ if (enable)
+ pcie_capability_set_word(child, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
+ else
+ pcie_capability_clear_word(child, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
+ }
+ link->clkpm_enabled = !!enable;
+}
+
+static void pcie_set_clkpm(struct pcie_link_state *link, int enable)
+{
+ /* Don't enable Clock PM if the link is not Clock PM capable */
+ if (!link->clkpm_capable && enable)
+ enable = 0;
+ /* Need nothing if the specified equals to current state */
+ if (link->clkpm_enabled == enable)
+ return;
+ pcie_set_clkpm_nocheck(link, enable);
+}
+
+static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
+{
+ int capable = 1, enabled = 1;
+ u32 reg32;
+ u16 reg16;
+ struct pci_dev *child;
+ struct pci_bus *linkbus = link->pdev->subordinate;
+
+ /* All functions should have the same cap and state, take the worst */
+ list_for_each_entry(child, &linkbus->devices, bus_list) {
+ pcie_capability_read_dword(child, PCI_EXP_LNKCAP, &reg32);
+ if (!(reg32 & PCI_EXP_LNKCAP_CLKPM)) {
+ capable = 0;
+ enabled = 0;
+ break;
+ }
+ pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
+ if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN))
+ enabled = 0;
+ }
+ link->clkpm_enabled = enabled;
+ link->clkpm_default = enabled;
+ link->clkpm_capable = (blacklist) ? 0 : capable;
+}
+
+/*
+ * pcie_aspm_configure_common_clock: check if the 2 ends of a link
+ * could use common clock. If they are, configure them to use the
+ * common clock. That will reduce the ASPM state exit latency.
+ */
+static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
+{
+ int same_clock = 1;
+ u16 reg16, parent_reg, child_reg[8];
+ unsigned long start_jiffies;
+ struct pci_dev *child, *parent = link->pdev;
+ struct pci_bus *linkbus = parent->subordinate;
+ /*
+ * All functions of a slot should have the same Slot Clock
+ * Configuration, so just check one function
+ */
+ child = list_entry(linkbus->devices.next, struct pci_dev, bus_list);
+ BUG_ON(!pci_is_pcie(child));
+
+ /* Check downstream component if bit Slot Clock Configuration is 1 */
+ pcie_capability_read_word(child, PCI_EXP_LNKSTA, &reg16);
+ if (!(reg16 & PCI_EXP_LNKSTA_SLC))
+ same_clock = 0;
+
+ /* Check upstream component if bit Slot Clock Configuration is 1 */
+ pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &reg16);
+ if (!(reg16 & PCI_EXP_LNKSTA_SLC))
+ same_clock = 0;
+
+ /* Configure downstream component, all functions */
+ list_for_each_entry(child, &linkbus->devices, bus_list) {
+ pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
+ child_reg[PCI_FUNC(child->devfn)] = reg16;
+ if (same_clock)
+ reg16 |= PCI_EXP_LNKCTL_CCC;
+ else
+ reg16 &= ~PCI_EXP_LNKCTL_CCC;
+ pcie_capability_write_word(child, PCI_EXP_LNKCTL, reg16);
+ }
+
+ /* Configure upstream component */
+ pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
+ parent_reg = reg16;
+ if (same_clock)
+ reg16 |= PCI_EXP_LNKCTL_CCC;
+ else
+ reg16 &= ~PCI_EXP_LNKCTL_CCC;
+ pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
+
+ /* Retrain link */
+ reg16 |= PCI_EXP_LNKCTL_RL;
+ pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
+
+ /* Wait for link training end. Break out after waiting for timeout */
+ start_jiffies = jiffies;
+ for (;;) {
+ pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &reg16);
+ if (!(reg16 & PCI_EXP_LNKSTA_LT))
+ break;
+ if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT))
+ break;
+ msleep(1);
+ }
+ if (!(reg16 & PCI_EXP_LNKSTA_LT))
+ return;
+
+ /* Training failed. Restore common clock configurations */
+ dev_err(&parent->dev, "ASPM: Could not configure common clock\n");
+ list_for_each_entry(child, &linkbus->devices, bus_list)
+ pcie_capability_write_word(child, PCI_EXP_LNKCTL,
+ child_reg[PCI_FUNC(child->devfn)]);
+ pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_reg);
+}
+
+/* Convert L0s latency encoding to ns */
+static u32 calc_l0s_latency(u32 encoding)
+{
+ if (encoding == 0x7)
+ return (5 * 1000); /* > 4us */
+ return (64 << encoding);
+}
+
+/* Convert L0s acceptable latency encoding to ns */
+static u32 calc_l0s_acceptable(u32 encoding)
+{
+ if (encoding == 0x7)
+ return -1U;
+ return (64 << encoding);
+}
+
+/* Convert L1 latency encoding to ns */
+static u32 calc_l1_latency(u32 encoding)
+{
+ if (encoding == 0x7)
+ return (65 * 1000); /* > 64us */
+ return (1000 << encoding);
+}
+
+/* Convert L1 acceptable latency encoding to ns */
+static u32 calc_l1_acceptable(u32 encoding)
+{
+ if (encoding == 0x7)
+ return -1U;
+ return (1000 << encoding);
+}
+
+struct aspm_register_info {
+ u32 support:2;
+ u32 enabled:2;
+ u32 latency_encoding_l0s;
+ u32 latency_encoding_l1;
+};
+
+static void pcie_get_aspm_reg(struct pci_dev *pdev,
+ struct aspm_register_info *info)
+{
+ u16 reg16;
+ u32 reg32;
+
+ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &reg32);
+ info->support = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10;
+ info->latency_encoding_l0s = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12;
+ info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
+ pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &reg16);
+ info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC;
+}
+
+static void pcie_aspm_check_latency(struct pci_dev *endpoint)
+{
+ u32 latency, l1_switch_latency = 0;
+ struct aspm_latency *acceptable;
+ struct pcie_link_state *link;
+
+ /* Device not in D0 doesn't need latency check */
+ if ((endpoint->current_state != PCI_D0) &&
+ (endpoint->current_state != PCI_UNKNOWN))
+ return;
+
+ link = endpoint->bus->self->link_state;
+ acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)];
+
+ while (link) {
+ /* Check upstream direction L0s latency */
+ if ((link->aspm_capable & ASPM_STATE_L0S_UP) &&
+ (link->latency_up.l0s > acceptable->l0s))
+ link->aspm_capable &= ~ASPM_STATE_L0S_UP;
+
+ /* Check downstream direction L0s latency */
+ if ((link->aspm_capable & ASPM_STATE_L0S_DW) &&
+ (link->latency_dw.l0s > acceptable->l0s))
+ link->aspm_capable &= ~ASPM_STATE_L0S_DW;
+ /*
+ * Check L1 latency.
+ * Every switch on the path to root complex need 1
+ * more microsecond for L1. Spec doesn't mention L0s.
+ */
+ latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1);
+ if ((link->aspm_capable & ASPM_STATE_L1) &&
+ (latency + l1_switch_latency > acceptable->l1))
+ link->aspm_capable &= ~ASPM_STATE_L1;
+ l1_switch_latency += 1000;
+
+ link = link->parent;
+ }
+}
+
+static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
+{
+ struct pci_dev *child, *parent = link->pdev;
+ struct pci_bus *linkbus = parent->subordinate;
+ struct aspm_register_info upreg, dwreg;
+
+ if (blacklist) {
+ /* Set enabled/disable so that we will disable ASPM later */
+ link->aspm_enabled = ASPM_STATE_ALL;
+ link->aspm_disable = ASPM_STATE_ALL;
+ return;
+ }
+
+ /* Configure common clock before checking latencies */
+ pcie_aspm_configure_common_clock(link);
+
+ /* Get upstream/downstream components' register state */
+ pcie_get_aspm_reg(parent, &upreg);
+ child = list_entry(linkbus->devices.next, struct pci_dev, bus_list);
+ pcie_get_aspm_reg(child, &dwreg);
+
+ /*
+ * Setup L0s state
+ *
+ * Note that we must not enable L0s in either direction on a
+ * given link unless components on both sides of the link each
+ * support L0s.
+ */
+ if (dwreg.support & upreg.support & PCIE_LINK_STATE_L0S)
+ link->aspm_support |= ASPM_STATE_L0S;
+ if (dwreg.enabled & PCIE_LINK_STATE_L0S)
+ link->aspm_enabled |= ASPM_STATE_L0S_UP;
+ if (upreg.enabled & PCIE_LINK_STATE_L0S)
+ link->aspm_enabled |= ASPM_STATE_L0S_DW;
+ link->latency_up.l0s = calc_l0s_latency(upreg.latency_encoding_l0s);
+ link->latency_dw.l0s = calc_l0s_latency(dwreg.latency_encoding_l0s);
+
+ /* Setup L1 state */
+ if (upreg.support & dwreg.support & PCIE_LINK_STATE_L1)
+ link->aspm_support |= ASPM_STATE_L1;
+ if (upreg.enabled & dwreg.enabled & PCIE_LINK_STATE_L1)
+ link->aspm_enabled |= ASPM_STATE_L1;
+ link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1);
+ link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1);
+
+ /* Save default state */
+ link->aspm_default = link->aspm_enabled;
+
+ /* Setup initial capable state. Will be updated later */
+ link->aspm_capable = link->aspm_support;
+ /*
+ * If the downstream component has pci bridge function, don't
+ * do ASPM for now.
+ */
+ list_for_each_entry(child, &linkbus->devices, bus_list) {
+ if (pci_pcie_type(child) == PCI_EXP_TYPE_PCI_BRIDGE) {
+ link->aspm_disable = ASPM_STATE_ALL;
+ break;
+ }
+ }
+
+ /* Get and check endpoint acceptable latencies */
+ list_for_each_entry(child, &linkbus->devices, bus_list) {
+ u32 reg32, encoding;
+ struct aspm_latency *acceptable =
+ &link->acceptable[PCI_FUNC(child->devfn)];
+
+ if (pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT &&
+ pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END)
+ continue;
+
+ pcie_capability_read_dword(child, PCI_EXP_DEVCAP, &reg32);
+ /* Calculate endpoint L0s acceptable latency */
+ encoding = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6;
+ acceptable->l0s = calc_l0s_acceptable(encoding);
+ /* Calculate endpoint L1 acceptable latency */
+ encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9;
+ acceptable->l1 = calc_l1_acceptable(encoding);
+
+ pcie_aspm_check_latency(child);
+ }
+}
+
+static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
+{
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_ASPMC, val);
+}
+
+static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
+{
+ u32 upstream = 0, dwstream = 0;
+ struct pci_dev *child, *parent = link->pdev;
+ struct pci_bus *linkbus = parent->subordinate;
+
+ /* Nothing to do if the link is already in the requested state */
+ state &= (link->aspm_capable & ~link->aspm_disable);
+ if (link->aspm_enabled == state)
+ return;
+ /* Convert ASPM state to upstream/downstream ASPM register state */
+ if (state & ASPM_STATE_L0S_UP)
+ dwstream |= PCI_EXP_LNKCTL_ASPM_L0S;
+ if (state & ASPM_STATE_L0S_DW)
+ upstream |= PCI_EXP_LNKCTL_ASPM_L0S;
+ if (state & ASPM_STATE_L1) {
+ upstream |= PCI_EXP_LNKCTL_ASPM_L1;
+ dwstream |= PCI_EXP_LNKCTL_ASPM_L1;
+ }
+ /*
+ * Spec 2.0 suggests all functions should be configured the
+ * same setting for ASPM. Enabling ASPM L1 should be done in
+ * upstream component first and then downstream, and vice
+ * versa for disabling ASPM L1. Spec doesn't mention L0S.
+ */
+ if (state & ASPM_STATE_L1)
+ pcie_config_aspm_dev(parent, upstream);
+ list_for_each_entry(child, &linkbus->devices, bus_list)
+ pcie_config_aspm_dev(child, dwstream);
+ if (!(state & ASPM_STATE_L1))
+ pcie_config_aspm_dev(parent, upstream);
+
+ link->aspm_enabled = state;
+}
+
+static void pcie_config_aspm_path(struct pcie_link_state *link)
+{
+ while (link) {
+ pcie_config_aspm_link(link, policy_to_aspm_state(link));
+ link = link->parent;
+ }
+}
+
+static void free_link_state(struct pcie_link_state *link)
+{
+ link->pdev->link_state = NULL;
+ kfree(link);
+}
+
+static int pcie_aspm_sanity_check(struct pci_dev *pdev)
+{
+ struct pci_dev *child;
+ u32 reg32;
+
+ /*
+ * Some functions in a slot might not all be PCIe functions,
+ * very strange. Disable ASPM for the whole slot
+ */
+ list_for_each_entry(child, &pdev->subordinate->devices, bus_list) {
+ if (!pci_is_pcie(child))
+ return -EINVAL;
+
+ /*
+ * If ASPM is disabled then we're not going to change
+ * the BIOS state. It's safe to continue even if it's a
+ * pre-1.1 device
+ */
+
+ if (aspm_disabled)
+ continue;
+
+ /*
+ * 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
+ */
+ pcie_capability_read_dword(child, PCI_EXP_DEVCAP, &reg32);
+ if (!(reg32 & PCI_EXP_DEVCAP_RBER) && !aspm_force) {
+ dev_info(&child->dev, "disabling ASPM on pre-1.1 PCIe device. You can enable it with 'pcie_aspm=force'\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
+{
+ struct pcie_link_state *link;
+
+ link = kzalloc(sizeof(*link), GFP_KERNEL);
+ if (!link)
+ return NULL;
+ INIT_LIST_HEAD(&link->sibling);
+ INIT_LIST_HEAD(&link->children);
+ INIT_LIST_HEAD(&link->link);
+ link->pdev = pdev;
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM) {
+ struct pcie_link_state *parent;
+ parent = pdev->bus->parent->self->link_state;
+ if (!parent) {
+ kfree(link);
+ return NULL;
+ }
+ link->parent = parent;
+ list_add(&link->link, &parent->children);
+ }
+ /* Setup a pointer to the root port link */
+ if (!link->parent)
+ link->root = link;
+ else
+ link->root = link->parent->root;
+
+ list_add(&link->sibling, &link_list);
+ pdev->link_state = link;
+ return link;
+}
+
+/*
+ * pcie_aspm_init_link_state: Initiate PCI express link state.
+ * It is called after the pcie and its children devices are scanned.
+ * @pdev: the root port or switch downstream port
+ */
+void pcie_aspm_init_link_state(struct pci_dev *pdev)
+{
+ struct pcie_link_state *link;
+ int blacklist = !!pcie_aspm_sanity_check(pdev);
+
+ if (!aspm_support_enabled)
+ return;
+
+ if (!pci_is_pcie(pdev) || pdev->link_state)
+ return;
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+ pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM)
+ return;
+
+ /* VIA has a strange chipset, root port is under a bridge */
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT &&
+ pdev->bus->self)
+ return;
+
+ down_read(&pci_bus_sem);
+ if (list_empty(&pdev->subordinate->devices))
+ goto out;
+
+ mutex_lock(&aspm_lock);
+ link = alloc_pcie_link_state(pdev);
+ if (!link)
+ goto unlock;
+ /*
+ * Setup initial ASPM state. Note that we need to configure
+ * upstream links also because capable state of them can be
+ * update through pcie_aspm_cap_init().
+ */
+ pcie_aspm_cap_init(link, blacklist);
+
+ /* Setup initial Clock PM state */
+ pcie_clkpm_cap_init(link, blacklist);
+
+ /*
+ * At this stage drivers haven't had an opportunity to change the
+ * link policy setting. Enabling ASPM on broken hardware can cripple
+ * it even before the driver has had a chance to disable ASPM, so
+ * default to a safe level right now. If we're enabling ASPM beyond
+ * the BIOS's expectation, we'll do so once pci_enable_device() is
+ * called.
+ */
+ if (aspm_policy != POLICY_POWERSAVE) {
+ pcie_config_aspm_path(link);
+ pcie_set_clkpm(link, policy_to_clkpm_state(link));
+ }
+
+unlock:
+ mutex_unlock(&aspm_lock);
+out:
+ up_read(&pci_bus_sem);
+}
+
+/* Recheck latencies and update aspm_capable for links under the root */
+static void pcie_update_aspm_capable(struct pcie_link_state *root)
+{
+ struct pcie_link_state *link;
+ BUG_ON(root->parent);
+ list_for_each_entry(link, &link_list, sibling) {
+ if (link->root != root)
+ continue;
+ link->aspm_capable = link->aspm_support;
+ }
+ list_for_each_entry(link, &link_list, sibling) {
+ struct pci_dev *child;
+ struct pci_bus *linkbus = link->pdev->subordinate;
+ if (link->root != root)
+ continue;
+ list_for_each_entry(child, &linkbus->devices, bus_list) {
+ if ((pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT) &&
+ (pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END))
+ continue;
+ pcie_aspm_check_latency(child);
+ }
+ }
+}
+
+/* @pdev: the endpoint device */
+void pcie_aspm_exit_link_state(struct pci_dev *pdev)
+{
+ struct pci_dev *parent = pdev->bus->self;
+ struct pcie_link_state *link, *root, *parent_link;
+
+ if (!parent || !parent->link_state)
+ return;
+
+ down_read(&pci_bus_sem);
+ mutex_lock(&aspm_lock);
+ /*
+ * All PCIe functions are in one slot, remove one function will remove
+ * the whole slot, so just wait until we are the last function left.
+ */
+ if (!list_is_last(&pdev->bus_list, &parent->subordinate->devices))
+ goto out;
+
+ link = parent->link_state;
+ root = link->root;
+ parent_link = link->parent;
+
+ /* All functions are removed, so just disable ASPM for the link */
+ pcie_config_aspm_link(link, 0);
+ list_del(&link->sibling);
+ list_del(&link->link);
+ /* Clock PM is for endpoint device */
+ free_link_state(link);
+
+ /* Recheck latencies and configure upstream links */
+ if (parent_link) {
+ pcie_update_aspm_capable(root);
+ pcie_config_aspm_path(parent_link);
+ }
+out:
+ mutex_unlock(&aspm_lock);
+ up_read(&pci_bus_sem);
+}
+
+/* @pdev: the root port or switch downstream port */
+void pcie_aspm_pm_state_change(struct pci_dev *pdev)
+{
+ struct pcie_link_state *link = pdev->link_state;
+
+ if (aspm_disabled || !pci_is_pcie(pdev) || !link)
+ return;
+ if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
+ return;
+ /*
+ * Devices changed PM state, we should recheck if latency
+ * meets all functions' requirement
+ */
+ down_read(&pci_bus_sem);
+ mutex_lock(&aspm_lock);
+ pcie_update_aspm_capable(link->root);
+ pcie_config_aspm_path(link);
+ mutex_unlock(&aspm_lock);
+ up_read(&pci_bus_sem);
+}
+
+void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
+{
+ struct pcie_link_state *link = pdev->link_state;
+
+ if (aspm_disabled || !pci_is_pcie(pdev) || !link)
+ return;
+
+ if (aspm_policy != POLICY_POWERSAVE)
+ return;
+
+ if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
+ return;
+
+ down_read(&pci_bus_sem);
+ mutex_lock(&aspm_lock);
+ pcie_config_aspm_path(link);
+ pcie_set_clkpm(link, policy_to_clkpm_state(link));
+ mutex_unlock(&aspm_lock);
+ up_read(&pci_bus_sem);
+}
+
+static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
+ bool force)
+{
+ struct pci_dev *parent = pdev->bus->self;
+ struct pcie_link_state *link;
+
+ if (!pci_is_pcie(pdev))
+ return;
+
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM)
+ parent = pdev;
+ if (!parent || !parent->link_state)
+ return;
+
+ /*
+ * A driver requested that ASPM be disabled on this device, but
+ * if we don't have permission to manage ASPM (e.g., on ACPI
+ * systems we have to observe the FADT ACPI_FADT_NO_ASPM bit and
+ * the _OSC method), we can't honor that request. Windows has
+ * a similar mechanism using "PciASPMOptOut", which is also
+ * ignored in this situation.
+ */
+ if (aspm_disabled && !force) {
+ dev_warn(&pdev->dev, "can't disable ASPM; OS doesn't have ASPM control\n");
+ return;
+ }
+
+ if (sem)
+ down_read(&pci_bus_sem);
+ mutex_lock(&aspm_lock);
+ link = parent->link_state;
+ if (state & PCIE_LINK_STATE_L0S)
+ link->aspm_disable |= ASPM_STATE_L0S;
+ if (state & PCIE_LINK_STATE_L1)
+ link->aspm_disable |= ASPM_STATE_L1;
+ pcie_config_aspm_link(link, policy_to_aspm_state(link));
+
+ if (state & PCIE_LINK_STATE_CLKPM) {
+ link->clkpm_capable = 0;
+ pcie_set_clkpm(link, 0);
+ }
+ mutex_unlock(&aspm_lock);
+ if (sem)
+ up_read(&pci_bus_sem);
+}
+
+void pci_disable_link_state_locked(struct pci_dev *pdev, int state)
+{
+ __pci_disable_link_state(pdev, state, false, false);
+}
+EXPORT_SYMBOL(pci_disable_link_state_locked);
+
+/**
+ * pci_disable_link_state - Disable device's link state, so the link will
+ * never enter specific states. Note that if the BIOS didn't grant ASPM
+ * control to the OS, this does nothing because we can't touch the LNKCTL
+ * register.
+ *
+ * @pdev: PCI device
+ * @state: ASPM link state to disable
+ */
+void pci_disable_link_state(struct pci_dev *pdev, int state)
+{
+ __pci_disable_link_state(pdev, state, true, false);
+}
+EXPORT_SYMBOL(pci_disable_link_state);
+
+void pcie_clear_aspm(struct pci_bus *bus)
+{
+ struct pci_dev *child;
+
+ if (aspm_force)
+ return;
+
+ /*
+ * Clear any ASPM setup that the firmware has carried out on this bus
+ */
+ list_for_each_entry(child, &bus->devices, bus_list) {
+ __pci_disable_link_state(child, PCIE_LINK_STATE_L0S |
+ PCIE_LINK_STATE_L1 |
+ PCIE_LINK_STATE_CLKPM,
+ false, true);
+ }
+}
+
+static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp)
+{
+ int i;
+ struct pcie_link_state *link;
+
+ if (aspm_disabled)
+ return -EPERM;
+ for (i = 0; i < ARRAY_SIZE(policy_str); i++)
+ if (!strncmp(val, policy_str[i], strlen(policy_str[i])))
+ break;
+ if (i >= ARRAY_SIZE(policy_str))
+ return -EINVAL;
+ if (i == aspm_policy)
+ return 0;
+
+ down_read(&pci_bus_sem);
+ mutex_lock(&aspm_lock);
+ aspm_policy = i;
+ list_for_each_entry(link, &link_list, sibling) {
+ pcie_config_aspm_link(link, policy_to_aspm_state(link));
+ pcie_set_clkpm(link, policy_to_clkpm_state(link));
+ }
+ mutex_unlock(&aspm_lock);
+ up_read(&pci_bus_sem);
+ return 0;
+}
+
+static int pcie_aspm_get_policy(char *buffer, struct kernel_param *kp)
+{
+ int i, cnt = 0;
+ for (i = 0; i < ARRAY_SIZE(policy_str); i++)
+ if (i == aspm_policy)
+ cnt += sprintf(buffer + cnt, "[%s] ", policy_str[i]);
+ else
+ cnt += sprintf(buffer + cnt, "%s ", policy_str[i]);
+ return cnt;
+}
+
+module_param_call(policy, pcie_aspm_set_policy, pcie_aspm_get_policy,
+ NULL, 0644);
+
+#ifdef CONFIG_PCIEASPM_DEBUG
+static ssize_t link_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pci_device = to_pci_dev(dev);
+ struct pcie_link_state *link_state = pci_device->link_state;
+
+ return sprintf(buf, "%d\n", link_state->aspm_enabled);
+}
+
+static ssize_t link_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pcie_link_state *link, *root = pdev->link_state->root;
+ u32 val = buf[0] - '0', state = 0;
+
+ if (aspm_disabled)
+ return -EPERM;
+ if (n < 1 || val > 3)
+ return -EINVAL;
+
+ /* Convert requested state to ASPM state */
+ if (val & PCIE_LINK_STATE_L0S)
+ state |= ASPM_STATE_L0S;
+ if (val & PCIE_LINK_STATE_L1)
+ state |= ASPM_STATE_L1;
+
+ down_read(&pci_bus_sem);
+ mutex_lock(&aspm_lock);
+ list_for_each_entry(link, &link_list, sibling) {
+ if (link->root != root)
+ continue;
+ pcie_config_aspm_link(link, state);
+ }
+ mutex_unlock(&aspm_lock);
+ up_read(&pci_bus_sem);
+ return n;
+}
+
+static ssize_t clk_ctl_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pci_device = to_pci_dev(dev);
+ struct pcie_link_state *link_state = pci_device->link_state;
+
+ return sprintf(buf, "%d\n", link_state->clkpm_enabled);
+}
+
+static ssize_t clk_ctl_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ int state;
+
+ if (n < 1)
+ return -EINVAL;
+ state = buf[0]-'0';
+
+ down_read(&pci_bus_sem);
+ mutex_lock(&aspm_lock);
+ pcie_set_clkpm_nocheck(pdev->link_state, !!state);
+ mutex_unlock(&aspm_lock);
+ up_read(&pci_bus_sem);
+
+ return n;
+}
+
+static DEVICE_ATTR(link_state, 0644, link_state_show, link_state_store);
+static DEVICE_ATTR(clk_ctl, 0644, clk_ctl_show, clk_ctl_store);
+
+static char power_group[] = "power";
+void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev)
+{
+ struct pcie_link_state *link_state = pdev->link_state;
+
+ if (!pci_is_pcie(pdev) ||
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+ pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+ return;
+
+ if (link_state->aspm_support)
+ sysfs_add_file_to_group(&pdev->dev.kobj,
+ &dev_attr_link_state.attr, power_group);
+ if (link_state->clkpm_capable)
+ sysfs_add_file_to_group(&pdev->dev.kobj,
+ &dev_attr_clk_ctl.attr, power_group);
+}
+
+void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
+{
+ struct pcie_link_state *link_state = pdev->link_state;
+
+ if (!pci_is_pcie(pdev) ||
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+ pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+ return;
+
+ if (link_state->aspm_support)
+ sysfs_remove_file_from_group(&pdev->dev.kobj,
+ &dev_attr_link_state.attr, power_group);
+ if (link_state->clkpm_capable)
+ sysfs_remove_file_from_group(&pdev->dev.kobj,
+ &dev_attr_clk_ctl.attr, power_group);
+}
+#endif
+
+static int __init pcie_aspm_disable(char *str)
+{
+ if (!strcmp(str, "off")) {
+ aspm_policy = POLICY_DEFAULT;
+ aspm_disabled = 1;
+ aspm_support_enabled = false;
+ printk(KERN_INFO "PCIe ASPM is disabled\n");
+ } else if (!strcmp(str, "force")) {
+ aspm_force = 1;
+ printk(KERN_INFO "PCIe ASPM is forcibly enabled\n");
+ }
+ return 1;
+}
+
+__setup("pcie_aspm=", pcie_aspm_disable);
+
+void pcie_no_aspm(void)
+{
+ /*
+ * Disabling ASPM is intended to prevent the kernel from modifying
+ * existing hardware state, not to clear existing state. To that end:
+ * (a) set policy to POLICY_DEFAULT in order to avoid changing state
+ * (b) prevent userspace from changing policy
+ */
+ if (!aspm_force) {
+ aspm_policy = POLICY_DEFAULT;
+ aspm_disabled = 1;
+ }
+}
+
+bool pcie_aspm_support_enabled(void)
+{
+ return aspm_support_enabled;
+}
+EXPORT_SYMBOL(pcie_aspm_support_enabled);
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
new file mode 100644
index 00000000000..82e06a86cd7
--- /dev/null
+++ b/drivers/pci/pcie/pme.c
@@ -0,0 +1,438 @@
+/*
+ * PCIe Native PME support
+ *
+ * Copyright (C) 2007 - 2009 Intel Corp
+ * Copyright (C) 2007 - 2009 Shaohua Li <shaohua.li@intel.com>
+ * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License V2. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/pcieport_if.h>
+#include <linux/pm_runtime.h>
+
+#include "../pci.h"
+#include "portdrv.h"
+
+/*
+ * If this switch is set, MSI will not be used for PCIe PME signaling. This
+ * causes the PCIe port driver to use INTx interrupts only, but it turns out
+ * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
+ * wake-up from system sleep states.
+ */
+bool pcie_pme_msi_disabled;
+
+static int __init pcie_pme_setup(char *str)
+{
+ if (!strncmp(str, "nomsi", 5))
+ pcie_pme_msi_disabled = true;
+
+ return 1;
+}
+__setup("pcie_pme=", pcie_pme_setup);
+
+struct pcie_pme_service_data {
+ spinlock_t lock;
+ struct pcie_device *srv;
+ struct work_struct work;
+ bool noirq; /* Don't enable the PME interrupt used by this service. */
+};
+
+/**
+ * pcie_pme_interrupt_enable - Enable/disable PCIe PME interrupt generation.
+ * @dev: PCIe root port or event collector.
+ * @enable: Enable or disable the interrupt.
+ */
+void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
+{
+ if (enable)
+ pcie_capability_set_word(dev, PCI_EXP_RTCTL,
+ PCI_EXP_RTCTL_PMEIE);
+ else
+ pcie_capability_clear_word(dev, PCI_EXP_RTCTL,
+ PCI_EXP_RTCTL_PMEIE);
+}
+
+/**
+ * pcie_pme_walk_bus - Scan a PCI bus for devices asserting PME#.
+ * @bus: PCI bus to scan.
+ *
+ * Scan given PCI bus and all buses under it for devices asserting PME#.
+ */
+static bool pcie_pme_walk_bus(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+ bool ret = false;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ /* Skip PCIe devices in case we started from a root port. */
+ if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) {
+ if (dev->pme_poll)
+ dev->pme_poll = false;
+
+ pci_wakeup_event(dev);
+ pm_request_resume(&dev->dev);
+ ret = true;
+ }
+
+ if (dev->subordinate && pcie_pme_walk_bus(dev->subordinate))
+ ret = true;
+ }
+
+ return ret;
+}
+
+/**
+ * pcie_pme_from_pci_bridge - Check if PCIe-PCI bridge generated a PME.
+ * @bus: Secondary bus of the bridge.
+ * @devfn: Device/function number to check.
+ *
+ * PME from PCI devices under a PCIe-PCI bridge may be converted to an in-band
+ * PCIe PME message. In such that case the bridge should use the Requester ID
+ * of device/function number 0 on its secondary bus.
+ */
+static bool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn)
+{
+ struct pci_dev *dev;
+ bool found = false;
+
+ if (devfn)
+ return false;
+
+ dev = pci_dev_get(bus->self);
+ if (!dev)
+ return false;
+
+ if (pci_is_pcie(dev) && pci_pcie_type(dev) == PCI_EXP_TYPE_PCI_BRIDGE) {
+ down_read(&pci_bus_sem);
+ if (pcie_pme_walk_bus(bus))
+ found = true;
+ up_read(&pci_bus_sem);
+ }
+
+ pci_dev_put(dev);
+ return found;
+}
+
+/**
+ * pcie_pme_handle_request - Find device that generated PME and handle it.
+ * @port: Root port or event collector that generated the PME interrupt.
+ * @req_id: PCIe Requester ID of the device that generated the PME.
+ */
+static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
+{
+ u8 busnr = req_id >> 8, devfn = req_id & 0xff;
+ struct pci_bus *bus;
+ struct pci_dev *dev;
+ bool found = false;
+
+ /* First, check if the PME is from the root port itself. */
+ if (port->devfn == devfn && port->bus->number == busnr) {
+ if (port->pme_poll)
+ port->pme_poll = false;
+
+ if (pci_check_pme_status(port)) {
+ pm_request_resume(&port->dev);
+ found = true;
+ } else {
+ /*
+ * Apparently, the root port generated the PME on behalf
+ * of a non-PCIe device downstream. If this is done by
+ * a root port, the Requester ID field in its status
+ * register may contain either the root port's, or the
+ * source device's information (PCI Express Base
+ * Specification, Rev. 2.0, Section 6.1.9).
+ */
+ down_read(&pci_bus_sem);
+ found = pcie_pme_walk_bus(port->subordinate);
+ up_read(&pci_bus_sem);
+ }
+ goto out;
+ }
+
+ /* Second, find the bus the source device is on. */
+ bus = pci_find_bus(pci_domain_nr(port->bus), busnr);
+ if (!bus)
+ goto out;
+
+ /* Next, check if the PME is from a PCIe-PCI bridge. */
+ found = pcie_pme_from_pci_bridge(bus, devfn);
+ if (found)
+ goto out;
+
+ /* Finally, try to find the PME source on the bus. */
+ down_read(&pci_bus_sem);
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ pci_dev_get(dev);
+ if (dev->devfn == devfn) {
+ found = true;
+ break;
+ }
+ pci_dev_put(dev);
+ }
+ up_read(&pci_bus_sem);
+
+ if (found) {
+ /* The device is there, but we have to check its PME status. */
+ found = pci_check_pme_status(dev);
+ if (found) {
+ if (dev->pme_poll)
+ dev->pme_poll = false;
+
+ pci_wakeup_event(dev);
+ pm_request_resume(&dev->dev);
+ }
+ pci_dev_put(dev);
+ } else if (devfn) {
+ /*
+ * The device is not there, but we can still try to recover by
+ * assuming that the PME was reported by a PCIe-PCI bridge that
+ * used devfn different from zero.
+ */
+ dev_dbg(&port->dev, "PME interrupt generated for non-existent device %02x:%02x.%d\n",
+ busnr, PCI_SLOT(devfn), PCI_FUNC(devfn));
+ found = pcie_pme_from_pci_bridge(bus, 0);
+ }
+
+ out:
+ if (!found)
+ dev_dbg(&port->dev, "Spurious native PME interrupt!\n");
+}
+
+/**
+ * pcie_pme_work_fn - Work handler for PCIe PME interrupt.
+ * @work: Work structure giving access to service data.
+ */
+static void pcie_pme_work_fn(struct work_struct *work)
+{
+ struct pcie_pme_service_data *data =
+ container_of(work, struct pcie_pme_service_data, work);
+ struct pci_dev *port = data->srv->port;
+ u32 rtsta;
+
+ spin_lock_irq(&data->lock);
+
+ for (;;) {
+ if (data->noirq)
+ break;
+
+ pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
+ if (rtsta & PCI_EXP_RTSTA_PME) {
+ /*
+ * Clear PME status of the port. If there are other
+ * pending PMEs, the status will be set again.
+ */
+ pcie_clear_root_pme_status(port);
+
+ spin_unlock_irq(&data->lock);
+ pcie_pme_handle_request(port, rtsta & 0xffff);
+ spin_lock_irq(&data->lock);
+
+ continue;
+ }
+
+ /* No need to loop if there are no more PMEs pending. */
+ if (!(rtsta & PCI_EXP_RTSTA_PENDING))
+ break;
+
+ spin_unlock_irq(&data->lock);
+ cpu_relax();
+ spin_lock_irq(&data->lock);
+ }
+
+ if (!data->noirq)
+ pcie_pme_interrupt_enable(port, true);
+
+ spin_unlock_irq(&data->lock);
+}
+
+/**
+ * pcie_pme_irq - Interrupt handler for PCIe root port PME interrupt.
+ * @irq: Interrupt vector.
+ * @context: Interrupt context pointer.
+ */
+static irqreturn_t pcie_pme_irq(int irq, void *context)
+{
+ struct pci_dev *port;
+ struct pcie_pme_service_data *data;
+ u32 rtsta;
+ unsigned long flags;
+
+ port = ((struct pcie_device *)context)->port;
+ data = get_service_data((struct pcie_device *)context);
+
+ spin_lock_irqsave(&data->lock, flags);
+ pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
+
+ if (!(rtsta & PCI_EXP_RTSTA_PME)) {
+ spin_unlock_irqrestore(&data->lock, flags);
+ return IRQ_NONE;
+ }
+
+ pcie_pme_interrupt_enable(port, false);
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* We don't use pm_wq, because it's freezable. */
+ schedule_work(&data->work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * pcie_pme_set_native - Set the PME interrupt flag for given device.
+ * @dev: PCI device to handle.
+ * @ign: Ignored.
+ */
+static int pcie_pme_set_native(struct pci_dev *dev, void *ign)
+{
+ dev_info(&dev->dev, "Signaling PME through PCIe PME interrupt\n");
+
+ device_set_run_wake(&dev->dev, true);
+ dev->pme_interrupt = true;
+ return 0;
+}
+
+/**
+ * pcie_pme_mark_devices - Set the PME interrupt flag for devices below a port.
+ * @port: PCIe root port or event collector to handle.
+ *
+ * For each device below given root port, including the port itself (or for each
+ * root complex integrated endpoint if @port is a root complex event collector)
+ * set the flag indicating that it can signal run-time wake-up events via PCIe
+ * PME interrupts.
+ */
+static void pcie_pme_mark_devices(struct pci_dev *port)
+{
+ pcie_pme_set_native(port, NULL);
+ if (port->subordinate) {
+ pci_walk_bus(port->subordinate, pcie_pme_set_native, NULL);
+ } else {
+ struct pci_bus *bus = port->bus;
+ struct pci_dev *dev;
+
+ /* Check if this is a root port event collector. */
+ if (pci_pcie_type(port) != PCI_EXP_TYPE_RC_EC || !bus)
+ return;
+
+ down_read(&pci_bus_sem);
+ list_for_each_entry(dev, &bus->devices, bus_list)
+ if (pci_is_pcie(dev)
+ && pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
+ pcie_pme_set_native(dev, NULL);
+ up_read(&pci_bus_sem);
+ }
+}
+
+/**
+ * pcie_pme_probe - Initialize PCIe PME service for given root port.
+ * @srv: PCIe service to initialize.
+ */
+static int pcie_pme_probe(struct pcie_device *srv)
+{
+ struct pci_dev *port;
+ struct pcie_pme_service_data *data;
+ int ret;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ spin_lock_init(&data->lock);
+ INIT_WORK(&data->work, pcie_pme_work_fn);
+ data->srv = srv;
+ set_service_data(srv, data);
+
+ port = srv->port;
+ pcie_pme_interrupt_enable(port, false);
+ pcie_clear_root_pme_status(port);
+
+ ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
+ if (ret) {
+ kfree(data);
+ } else {
+ pcie_pme_mark_devices(port);
+ pcie_pme_interrupt_enable(port, true);
+ }
+
+ return ret;
+}
+
+/**
+ * pcie_pme_suspend - Suspend PCIe PME service device.
+ * @srv: PCIe service device to suspend.
+ */
+static int pcie_pme_suspend(struct pcie_device *srv)
+{
+ struct pcie_pme_service_data *data = get_service_data(srv);
+ struct pci_dev *port = srv->port;
+
+ spin_lock_irq(&data->lock);
+ pcie_pme_interrupt_enable(port, false);
+ pcie_clear_root_pme_status(port);
+ data->noirq = true;
+ spin_unlock_irq(&data->lock);
+
+ synchronize_irq(srv->irq);
+
+ return 0;
+}
+
+/**
+ * pcie_pme_resume - Resume PCIe PME service device.
+ * @srv - PCIe service device to resume.
+ */
+static int pcie_pme_resume(struct pcie_device *srv)
+{
+ struct pcie_pme_service_data *data = get_service_data(srv);
+ struct pci_dev *port = srv->port;
+
+ spin_lock_irq(&data->lock);
+ data->noirq = false;
+ pcie_clear_root_pme_status(port);
+ pcie_pme_interrupt_enable(port, true);
+ spin_unlock_irq(&data->lock);
+
+ return 0;
+}
+
+/**
+ * pcie_pme_remove - Prepare PCIe PME service device for removal.
+ * @srv - PCIe service device to remove.
+ */
+static void pcie_pme_remove(struct pcie_device *srv)
+{
+ pcie_pme_suspend(srv);
+ free_irq(srv->irq, srv);
+ kfree(get_service_data(srv));
+}
+
+static struct pcie_port_service_driver pcie_pme_driver = {
+ .name = "pcie_pme",
+ .port_type = PCI_EXP_TYPE_ROOT_PORT,
+ .service = PCIE_PORT_SERVICE_PME,
+
+ .probe = pcie_pme_probe,
+ .suspend = pcie_pme_suspend,
+ .resume = pcie_pme_resume,
+ .remove = pcie_pme_remove,
+};
+
+/**
+ * pcie_pme_service_init - Register the PCIe PME service driver.
+ */
+static int __init pcie_pme_service_init(void)
+{
+ return pcie_port_service_register(&pcie_pme_driver);
+}
+
+module_init(pcie_pme_service_init);
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index 3656e0349dd..d525548404d 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -11,37 +11,73 @@
#include <linux/compiler.h>
-#if !defined(PCI_CAP_ID_PME)
-#define PCI_CAP_ID_PME 1
-#endif
-
-#if !defined(PCI_CAP_ID_EXP)
-#define PCI_CAP_ID_EXP 0x10
-#endif
-
-#define PORT_TYPE_MASK 0xf
-#define PORT_TO_SLOT_MASK 0x100
-#define SLOT_HP_CAPABLE_MASK 0x40
-#define PCIE_CAPABILITIES_REG 0x2
-#define PCIE_SLOT_CAPABILITIES_REG 0x14
-#define PCIE_PORT_DEVICE_MAXSERVICES 4
-#define PCI_CFG_SPACE_SIZE 256
+#define PCIE_PORT_DEVICE_MAXSERVICES 4
+/*
+ * According to the PCI Express Base Specification 2.0, the indices of
+ * the MSI-X table entries used by port services must not exceed 31
+ */
+#define PCIE_PORT_MAX_MSIX_ENTRIES 32
#define get_descriptor_id(type, service) (((type - 4) << 4) | service)
-struct pcie_port_device_ext {
- int interrupt_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */
-};
-
extern struct bus_type pcie_port_bus_type;
-extern int pcie_port_device_probe(struct pci_dev *dev);
-extern int pcie_port_device_register(struct pci_dev *dev);
+int pcie_port_device_register(struct pci_dev *dev);
#ifdef CONFIG_PM
-extern int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state);
-extern int pcie_port_device_resume(struct pci_dev *dev);
+int pcie_port_device_suspend(struct device *dev);
+int pcie_port_device_resume(struct device *dev);
#endif
-extern void pcie_port_device_remove(struct pci_dev *dev);
-extern int __must_check pcie_port_bus_register(void);
-extern void pcie_port_bus_unregister(void);
+void pcie_port_device_remove(struct pci_dev *dev);
+int __must_check pcie_port_bus_register(void);
+void pcie_port_bus_unregister(void);
+
+struct pci_dev;
+
+void pcie_clear_root_pme_status(struct pci_dev *dev);
+
+#ifdef CONFIG_HOTPLUG_PCI_PCIE
+extern bool pciehp_msi_disabled;
+
+static inline bool pciehp_no_msi(void)
+{
+ return pciehp_msi_disabled;
+}
+
+#else /* !CONFIG_HOTPLUG_PCI_PCIE */
+static inline bool pciehp_no_msi(void) { return false; }
+#endif /* !CONFIG_HOTPLUG_PCI_PCIE */
+
+#ifdef CONFIG_PCIE_PME
+extern bool pcie_pme_msi_disabled;
+
+static inline void pcie_pme_disable_msi(void)
+{
+ pcie_pme_msi_disabled = true;
+}
+
+static inline bool pcie_pme_no_msi(void)
+{
+ return pcie_pme_msi_disabled;
+}
+
+void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable);
+#else /* !CONFIG_PCIE_PME */
+static inline void pcie_pme_disable_msi(void) {}
+static inline bool pcie_pme_no_msi(void) { return false; }
+static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
+#endif /* !CONFIG_PCIE_PME */
+
+#ifdef CONFIG_ACPI
+int pcie_port_acpi_setup(struct pci_dev *port, int *mask);
+
+static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
+{
+ return pcie_port_acpi_setup(port, mask);
+}
+#else /* !CONFIG_ACPI */
+static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
+{
+ return 0;
+}
+#endif /* !CONFIG_ACPI */
#endif /* _PORTDRV_H_ */
diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c
new file mode 100644
index 00000000000..b4d2894ee3f
--- /dev/null
+++ b/drivers/pci/pcie/portdrv_acpi.c
@@ -0,0 +1,63 @@
+/*
+ * PCIe Port Native Services Support, ACPI-Related Part
+ *
+ * Copyright (C) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License V2. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/pcieport_if.h>
+
+#include "aer/aerdrv.h"
+#include "../pci.h"
+#include "portdrv.h"
+
+/**
+ * pcie_port_acpi_setup - Request the BIOS to release control of PCIe services.
+ * @port: PCIe Port service for a root port or event collector.
+ * @srv_mask: Bit mask of services that can be enabled for @port.
+ *
+ * Invoked when @port is identified as a PCIe port device. To avoid conflicts
+ * with the BIOS PCIe port native services support requires the BIOS to yield
+ * control of these services to the kernel. The mask of services that the BIOS
+ * allows to be enabled for @port is written to @srv_mask.
+ *
+ * NOTE: It turns out that we cannot do that for individual port services
+ * separately, because that would make some systems work incorrectly.
+ */
+int pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
+{
+ struct acpi_pci_root *root;
+ acpi_handle handle;
+ u32 flags;
+
+ if (acpi_pci_disabled)
+ return 0;
+
+ handle = acpi_find_root_bridge_handle(port);
+ if (!handle)
+ return -EINVAL;
+
+ root = acpi_pci_find_root(handle);
+ if (!root)
+ return -ENODEV;
+
+ flags = root->osc_control_set;
+
+ *srv_mask = PCIE_PORT_SERVICE_VC;
+ if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
+ *srv_mask |= PCIE_PORT_SERVICE_HP;
+ if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
+ *srv_mask |= PCIE_PORT_SERVICE_PME;
+ if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
+ *srv_mask |= PCIE_PORT_SERVICE_AER;
+
+ return 0;
+}
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
index 3f0976868ed..87e79a6ffb5 100644
--- a/drivers/pci/pcie/portdrv_bus.c
+++ b/drivers/pci/pcie/portdrv_bus.c
@@ -13,16 +13,13 @@
#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);
-static int pcie_port_bus_resume(struct device *dev);
struct bus_type pcie_port_bus_type = {
- .name = "pci_express",
- .match = pcie_port_bus_match,
- .suspend = pcie_port_bus_suspend,
- .resume = pcie_port_bus_resume,
+ .name = "pci_express",
+ .match = pcie_port_bus_match,
};
EXPORT_SYMBOL_GPL(pcie_port_bus_type);
@@ -33,47 +30,26 @@ static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
return 0;
-
+
pciedev = to_pcie_device(dev);
driver = to_service_driver(drv);
- if ( (driver->id_table->vendor != PCI_ANY_ID &&
- driver->id_table->vendor != pciedev->id.vendor) ||
- (driver->id_table->device != PCI_ANY_ID &&
- driver->id_table->device != pciedev->id.device) ||
- (driver->id_table->port_type != PCIE_ANY_PORT &&
- driver->id_table->port_type != pciedev->id.port_type) ||
- driver->id_table->service_type != pciedev->id.service_type )
+
+ if (driver->service != pciedev->service)
+ return 0;
+
+ if ((driver->port_type != PCIE_ANY_PORT) &&
+ (driver->port_type != pci_pcie_type(pciedev->port)))
return 0;
return 1;
}
-static int pcie_port_bus_suspend(struct device *dev, pm_message_t state)
+int pcie_port_bus_register(void)
{
- struct pcie_device *pciedev;
- struct pcie_port_service_driver *driver;
-
- if (!dev || !dev->driver)
- return 0;
-
- pciedev = to_pcie_device(dev);
- driver = to_service_driver(dev->driver);
- if (driver && driver->suspend)
- driver->suspend(pciedev, state);
- return 0;
+ return bus_register(&pcie_port_bus_type);
}
-static int pcie_port_bus_resume(struct device *dev)
+void pcie_port_bus_unregister(void)
{
- struct pcie_device *pciedev;
- struct pcie_port_service_driver *driver;
-
- if (!dev || !dev->driver)
- return 0;
-
- pciedev = to_pcie_device(dev);
- driver = to_service_driver(dev->driver);
- if (driver && driver->resume)
- driver->resume(pciedev);
- return 0;
+ bus_unregister(&pcie_port_bus_type);
}
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 23d9eb07329..2f0ce668a77 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -14,331 +14,424 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/pcieport_if.h>
+#include <linux/aer.h>
+#include "../pci.h"
#include "portdrv.h"
-extern int pcie_mch_quirk; /* MSI-quirk Indicator */
+bool pciehp_msi_disabled;
-static int pcie_port_probe_service(struct device *dev)
+static int __init pciehp_setup(char *str)
{
- struct pcie_device *pciedev;
- struct pcie_port_service_driver *driver;
- int status = -ENODEV;
+ if (!strncmp(str, "nomsi", 5))
+ pciehp_msi_disabled = true;
- if (!dev || !dev->driver)
- return status;
+ return 1;
+}
+__setup("pcie_hp=", pciehp_setup);
- driver = to_service_driver(dev->driver);
- if (!driver || !driver->probe)
- return status;
+/**
+ * release_pcie_device - free PCI Express port service device structure
+ * @dev: Port service device to release
+ *
+ * Invoked automatically when device is being removed in response to
+ * device_unregister(dev). Release all resources being claimed.
+ */
+static void release_pcie_device(struct device *dev)
+{
+ kfree(to_pcie_device(dev));
+}
- 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);
- get_device(dev);
- }
- return status;
+/**
+ * pcie_port_msix_add_entry - add entry to given array of MSI-X entries
+ * @entries: Array of MSI-X entries
+ * @new_entry: Index of the entry to add to the array
+ * @nr_entries: Number of entries already in the array
+ *
+ * Return value: Position of the added entry in the array
+ */
+static int pcie_port_msix_add_entry(
+ struct msix_entry *entries, int new_entry, int nr_entries)
+{
+ int j;
+
+ for (j = 0; j < nr_entries; j++)
+ if (entries[j].entry == new_entry)
+ return j;
+
+ entries[j].entry = new_entry;
+ return j;
}
-static int pcie_port_remove_service(struct device *dev)
+/**
+ * pcie_port_enable_msix - try to set up MSI-X as interrupt mode for given port
+ * @dev: PCI Express port to handle
+ * @vectors: Array of interrupt vectors to populate
+ * @mask: Bitmask of port capabilities returned by get_port_device_capability()
+ *
+ * Return value: 0 on success, error code on failure
+ */
+static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
{
- struct pcie_device *pciedev;
- struct pcie_port_service_driver *driver;
+ struct msix_entry *msix_entries;
+ int idx[PCIE_PORT_DEVICE_MAXSERVICES];
+ int nr_entries, status, pos, i, nvec;
+ u16 reg16;
+ u32 reg32;
- if (!dev || !dev->driver)
- return 0;
+ nr_entries = pci_msix_vec_count(dev);
+ if (nr_entries < 0)
+ return nr_entries;
+ BUG_ON(!nr_entries);
+ if (nr_entries > PCIE_PORT_MAX_MSIX_ENTRIES)
+ nr_entries = PCIE_PORT_MAX_MSIX_ENTRIES;
- 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);
- driver->remove(pciedev);
- put_device(dev);
+ msix_entries = kzalloc(sizeof(*msix_entries) * nr_entries, GFP_KERNEL);
+ if (!msix_entries)
+ return -ENOMEM;
+
+ /*
+ * Allocate as many entries as the port wants, so that we can check
+ * which of them will be useful. Moreover, if nr_entries is correctly
+ * equal to the number of entries this port actually uses, we'll happily
+ * go through without any tricks.
+ */
+ for (i = 0; i < nr_entries; i++)
+ msix_entries[i].entry = i;
+
+ status = pci_enable_msix_exact(dev, msix_entries, nr_entries);
+ if (status)
+ goto Exit;
+
+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
+ idx[i] = -1;
+ status = -EIO;
+ nvec = 0;
+
+ if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
+ int entry;
+
+ /*
+ * The code below follows the PCI Express Base Specification 2.0
+ * stating in Section 6.1.6 that "PME and Hot-Plug Event
+ * interrupts (when both are implemented) always share the same
+ * MSI or MSI-X vector, as indicated by the Interrupt Message
+ * Number field in the PCI Express Capabilities register", where
+ * according to Section 7.8.2 of the specification "For MSI-X,
+ * the value in this field indicates which MSI-X Table entry is
+ * used to generate the interrupt message."
+ */
+ pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
+ entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
+ if (entry >= nr_entries)
+ goto Error;
+
+ i = pcie_port_msix_add_entry(msix_entries, entry, nvec);
+ if (i == nvec)
+ nvec++;
+
+ idx[PCIE_PORT_SERVICE_PME_SHIFT] = i;
+ idx[PCIE_PORT_SERVICE_HP_SHIFT] = i;
}
- return 0;
-}
-static void pcie_port_shutdown_service(struct device *dev) {}
+ if (mask & PCIE_PORT_SERVICE_AER) {
+ int entry;
+
+ /*
+ * The code below follows Section 7.10.10 of the PCI Express
+ * Base Specification 2.0 stating that bits 31-27 of the Root
+ * Error Status Register contain a value indicating which of the
+ * MSI/MSI-X vectors assigned to the port is going to be used
+ * for AER, where "For MSI-X, the value in this register
+ * indicates which MSI-X Table entry is used to generate the
+ * interrupt message."
+ */
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
+ entry = reg32 >> 27;
+ if (entry >= nr_entries)
+ goto Error;
+
+ i = pcie_port_msix_add_entry(msix_entries, entry, nvec);
+ if (i == nvec)
+ nvec++;
-static int pcie_port_suspend_service(struct device *dev, pm_message_t state)
-{
- struct pcie_device *pciedev;
- struct pcie_port_service_driver *driver;
+ idx[PCIE_PORT_SERVICE_AER_SHIFT] = i;
+ }
- if (!dev || !dev->driver)
- return 0;
+ /*
+ * If nvec is equal to the allocated number of entries, we can just use
+ * what we have. Otherwise, the port has some extra entries not for the
+ * services we know and we need to work around that.
+ */
+ if (nvec == nr_entries) {
+ status = 0;
+ } else {
+ /* Drop the temporary MSI-X setup */
+ pci_disable_msix(dev);
- pciedev = to_pcie_device(dev);
- driver = to_service_driver(dev->driver);
- if (driver && driver->suspend)
- driver->suspend(pciedev, state);
- return 0;
+ /* Now allocate the MSI-X vectors for real */
+ status = pci_enable_msix_exact(dev, msix_entries, nvec);
+ if (status)
+ goto Exit;
+ }
+
+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
+ vectors[i] = idx[i] >= 0 ? msix_entries[idx[i]].vector : -1;
+
+ Exit:
+ kfree(msix_entries);
+ return status;
+
+ Error:
+ pci_disable_msix(dev);
+ goto Exit;
}
-static int pcie_port_resume_service(struct device *dev)
+/**
+ * init_service_irqs - initialize irqs for PCI Express port services
+ * @dev: PCI Express port to handle
+ * @irqs: Array of irqs to populate
+ * @mask: Bitmask of port capabilities returned by get_port_device_capability()
+ *
+ * Return value: Interrupt mode associated with the port
+ */
+static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{
- struct pcie_device *pciedev;
- struct pcie_port_service_driver *driver;
+ int i, irq = -1;
+
+ /*
+ * If MSI cannot be used for PCIe PME or hotplug, we have to use
+ * INTx or other interrupts, e.g. system shared interrupt.
+ */
+ if (((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) ||
+ ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())) {
+ if (dev->irq)
+ irq = dev->irq;
+ goto no_msi;
+ }
- if (!dev || !dev->driver)
+ /* Try to use MSI-X if supported */
+ if (!pcie_port_enable_msix(dev, irqs, mask))
return 0;
- pciedev = to_pcie_device(dev);
- driver = to_service_driver(dev->driver);
+ /*
+ * We're not going to use MSI-X, so try MSI and fall back to INTx.
+ * If neither MSI/MSI-X nor INTx available, try other interrupt. On
+ * some platforms, root port doesn't support MSI/MSI-X/INTx in RC mode.
+ */
+ if (!pci_enable_msi(dev) || dev->irq)
+ irq = dev->irq;
+
+ no_msi:
+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
+ irqs[i] = irq;
+ irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1;
- if (driver && driver->resume)
- driver->resume(pciedev);
+ if (irq < 0)
+ return -ENODEV;
return 0;
}
-/*
- * release_pcie_device
- *
- * Being invoked automatically when device is being removed
- * in response to device_unregister(dev) call.
- * Release all resources being claimed.
- */
-static void release_pcie_device(struct device *dev)
+static void cleanup_service_irqs(struct pci_dev *dev)
{
- printk(KERN_DEBUG "Free Port Service[%s]\n", dev->bus_id);
- kfree(to_pcie_device(dev));
+ if (dev->msix_enabled)
+ pci_disable_msix(dev);
+ else if (dev->msi_enabled)
+ pci_disable_msi(dev);
}
-static int is_msi_quirked(struct pci_dev *dev)
+/**
+ * get_port_device_capability - discover capabilities of a PCI Express port
+ * @dev: PCI Express port to examine
+ *
+ * The capabilities are read from the port's PCI Express configuration registers
+ * as described in PCI Express Base Specification 1.0a sections 7.8.2, 7.8.9 and
+ * 7.9 - 7.11.
+ *
+ * Return value: Bitmask of discovered port capabilities
+ */
+static int get_port_device_capability(struct pci_dev *dev)
{
- int port_type, quirk = 0;
- u16 reg16;
+ int services = 0;
+ u32 reg32;
+ int cap_mask = 0;
+ int err;
- pci_read_config_word(dev,
- pci_find_capability(dev, PCI_CAP_ID_EXP) +
- PCIE_CAPABILITIES_REG, &reg16);
- port_type = (reg16 >> 4) & PORT_TYPE_MASK;
- switch(port_type) {
- case PCIE_RC_PORT:
- if (pcie_mch_quirk == 1)
- quirk = 1;
- break;
- case PCIE_SW_UPSTREAM_PORT:
- case PCIE_SW_DOWNSTREAM_PORT:
- default:
- break;
- }
- return quirk;
-}
-
-static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
-{
- int i, pos, nvec, status = -EINVAL;
- int interrupt_mode = PCIE_PORT_INTx_MODE;
+ if (pcie_ports_disabled)
+ return 0;
- /* Set INTx as default */
- for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
- if (mask & (1 << i))
- nvec++;
- vectors[i] = dev->irq;
- }
-
- /* Check MSI quirk */
- if (is_msi_quirked(dev))
- return interrupt_mode;
-
- /* Select MSI-X over MSI if supported */
- pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
- 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", __FUNCTION__);
- status = pci_enable_msix(dev, msix_entries, nvec);
- if (!status) {
- int j = 0;
-
- interrupt_mode = PCIE_PORT_MSIX_MODE;
- for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
- if (mask & (1 << i))
- vectors[i] = msix_entries[j++].vector;
- }
- }
- }
- if (status) {
- pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
- if (pos) {
- printk("%s Found MSI capability\n", __FUNCTION__);
- status = pci_enable_msi(dev);
- if (!status) {
- interrupt_mode = PCIE_PORT_MSI_MODE;
- for (i = 0;i < PCIE_PORT_DEVICE_MAXSERVICES;i++)
- vectors[i] = dev->irq;
- }
- }
- }
- return interrupt_mode;
-}
+ cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
+ | PCIE_PORT_SERVICE_VC;
+ if (pci_aer_available())
+ cap_mask |= PCIE_PORT_SERVICE_AER;
-static int get_port_device_capability(struct pci_dev *dev)
-{
- int services = 0, pos;
- u16 reg16;
- u32 reg32;
+ if (pcie_ports_auto) {
+ err = pcie_port_platform_notify(dev, &cap_mask);
+ if (err)
+ return 0;
+ }
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg16);
/* Hot-Plug Capable */
- if (reg16 & PORT_TO_SLOT_MASK) {
- pci_read_config_dword(dev,
- pos + PCIE_SLOT_CAPABILITIES_REG, &reg32);
- if (reg32 & SLOT_HP_CAPABLE_MASK)
+ if ((cap_mask & PCIE_PORT_SERVICE_HP) &&
+ pcie_caps_reg(dev) & PCI_EXP_FLAGS_SLOT) {
+ pcie_capability_read_dword(dev, PCI_EXP_SLTCAP, &reg32);
+ if (reg32 & PCI_EXP_SLTCAP_HPC) {
services |= PCIE_PORT_SERVICE_HP;
- }
- /* PME Capable - root port capability */
- if (((reg16 >> 4) & PORT_TYPE_MASK) == PCIE_RC_PORT)
- services |= PCIE_PORT_SERVICE_PME;
-
- pos = PCI_CFG_SPACE_SIZE;
- while (pos) {
- pci_read_config_dword(dev, pos, &reg32);
- switch (reg32 & 0xffff) {
- case PCI_EXT_CAP_ID_ERR:
- services |= PCIE_PORT_SERVICE_AER;
- pos = reg32 >> 20;
- break;
- case PCI_EXT_CAP_ID_VC:
- services |= PCIE_PORT_SERVICE_VC;
- pos = reg32 >> 20;
- break;
- default:
- pos = 0;
- break;
+ /*
+ * Disable hot-plug interrupts in case they have been
+ * enabled by the BIOS and the hot-plug service driver
+ * is not loaded.
+ */
+ pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
}
}
+ /* AER capable */
+ if ((cap_mask & PCIE_PORT_SERVICE_AER)
+ && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
+ services |= PCIE_PORT_SERVICE_AER;
+ /*
+ * Disable AER on this port in case it's been enabled by the
+ * BIOS (the AER service driver will enable it when necessary).
+ */
+ pci_disable_pcie_error_reporting(dev);
+ }
+ /* VC support */
+ if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
+ services |= PCIE_PORT_SERVICE_VC;
+ /* Root ports are capable of generating PME too */
+ if ((cap_mask & PCIE_PORT_SERVICE_PME)
+ && pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
+ services |= PCIE_PORT_SERVICE_PME;
+ /*
+ * Disable PME interrupt on this port in case it's been enabled
+ * by the BIOS (the PME service driver will enable it when
+ * necessary).
+ */
+ pcie_pme_interrupt_enable(dev, false);
+ }
return services;
}
-static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev,
- int port_type, int service_type, int irq, int irq_mode)
+/**
+ * pcie_device_init - allocate and initialize PCI Express port service device
+ * @pdev: PCI Express port to associate the service device with
+ * @service: Type of service to associate with the service device
+ * @irq: Interrupt vector to associate with the service device
+ */
+static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
{
+ int retval;
+ struct pcie_device *pcie;
struct device *device;
- dev->port = parent;
- dev->interrupt_mode = irq_mode;
- dev->irq = irq;
- dev->id.vendor = parent->vendor;
- dev->id.device = parent->device;
- dev->id.port_type = port_type;
- dev->id.service_type = (1 << service_type);
+ pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+ pcie->port = pdev;
+ pcie->irq = irq;
+ pcie->service = service;
/* Initialize generic device interface */
- device = &dev->device;
- memset(device, 0, sizeof(struct device));
+ device = &pcie->device;
device->bus = &pcie_port_bus_type;
- device->driver = NULL;
- device->driver_data = NULL;
device->release = release_pcie_device; /* callback to free pcie dev */
- snprintf(device->bus_id, sizeof(device->bus_id), "%s:pcie%02x",
- pci_name(parent), get_descriptor_id(port_type, service_type));
- device->parent = &parent->dev;
-}
-
-static struct pcie_device* alloc_pcie_device(struct pci_dev *parent,
- int port_type, int service_type, int irq, int irq_mode)
-{
- struct pcie_device *device;
-
- device = kzalloc(sizeof(struct pcie_device), GFP_KERNEL);
- if (!device)
- 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);
- return device;
-}
-
-int pcie_port_device_probe(struct pci_dev *dev)
-{
- int pos, type;
- u16 reg;
-
- if (!(pos = pci_find_capability(dev, PCI_CAP_ID_EXP)))
- return -ENODEV;
-
- pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg);
- type = (reg >> 4) & PORT_TYPE_MASK;
- if ( type == PCIE_RC_PORT || type == PCIE_SW_UPSTREAM_PORT ||
- type == PCIE_SW_DOWNSTREAM_PORT )
- return 0;
+ dev_set_name(device, "%s:pcie%02x",
+ pci_name(pdev),
+ get_descriptor_id(pci_pcie_type(pdev), service));
+ device->parent = &pdev->dev;
+ device_enable_async_suspend(device);
+
+ retval = device_register(device);
+ if (retval) {
+ put_device(device);
+ return retval;
+ }
- return -ENODEV;
+ return 0;
}
+/**
+ * pcie_port_device_register - register PCI Express port
+ * @dev: PCI Express port to register
+ *
+ * Allocate the port extension structure and register services associated with
+ * the port.
+ */
int pcie_port_device_register(struct pci_dev *dev)
{
- struct pcie_port_device_ext *p_ext;
- int status, type, capabilities, irq_mode, i;
- int vectors[PCIE_PORT_DEVICE_MAXSERVICES];
- u16 reg16;
-
- /* Allocate port device extension */
- if (!(p_ext = kmalloc(sizeof(struct pcie_port_device_ext), GFP_KERNEL)))
- return -ENOMEM;
+ int status, capabilities, i, nr_service;
+ int irqs[PCIE_PORT_DEVICE_MAXSERVICES];
- pci_set_drvdata(dev, p_ext);
-
- /* Get port type */
- pci_read_config_word(dev,
- pci_find_capability(dev, PCI_CAP_ID_EXP) +
- PCIE_CAPABILITIES_REG, &reg16);
- type = (reg16 >> 4) & PORT_TYPE_MASK;
+ /* Enable PCI Express port device */
+ status = pci_enable_device(dev);
+ if (status)
+ return status;
- /* Now get port services */
+ /* Get and check PCI Express port services */
capabilities = get_port_device_capability(dev);
- irq_mode = assign_interrupt_mode(dev, vectors, capabilities);
- p_ext->interrupt_mode = irq_mode;
+ if (!capabilities)
+ return 0;
+
+ pci_set_master(dev);
+ /*
+ * Initialize service irqs. Don't use service devices that
+ * require interrupts if there is no way to generate them.
+ * However, some drivers may have a polling mode (e.g. pciehp_poll_mode)
+ * that can be used in the absence of irqs. Allow them to determine
+ * if that is to be used.
+ */
+ status = init_service_irqs(dev, irqs, capabilities);
+ if (status) {
+ capabilities &= PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_HP;
+ if (!capabilities)
+ goto error_disable;
+ }
/* Allocate child services if any */
+ status = -ENODEV;
+ nr_service = 0;
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
- struct pcie_device *child;
-
- if (capabilities & (1 << i)) {
- child = alloc_pcie_device(
- dev, /* parent */
- type, /* port type */
- i, /* service type */
- vectors[i], /* irq */
- irq_mode /* interrupt mode */);
- if (child) {
- status = device_register(&child->device);
- if (status) {
- kfree(child);
- continue;
- }
- get_device(&child->device);
- }
- }
+ int service = 1 << i;
+ if (!(capabilities & service))
+ continue;
+ if (!pcie_device_init(dev, service, irqs[i]))
+ nr_service++;
}
+ if (!nr_service)
+ goto error_cleanup_irqs;
+
return 0;
+
+error_cleanup_irqs:
+ cleanup_service_irqs(dev);
+error_disable:
+ pci_disable_device(dev);
+ return status;
}
#ifdef CONFIG_PM
static int suspend_iter(struct device *dev, void *data)
{
struct pcie_port_service_driver *service_driver;
- pm_message_t state = * (pm_message_t *) data;
-
- if ((dev->bus == &pcie_port_bus_type) &&
- (dev->driver)) {
- service_driver = to_service_driver(dev->driver);
- if (service_driver->suspend)
- service_driver->suspend(to_pcie_device(dev), state);
- }
+
+ if ((dev->bus == &pcie_port_bus_type) && dev->driver) {
+ service_driver = to_service_driver(dev->driver);
+ if (service_driver->suspend)
+ service_driver->suspend(to_pcie_device(dev));
+ }
return 0;
}
-int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state)
+/**
+ * pcie_port_device_suspend - suspend port services associated with a PCIe port
+ * @dev: PCI Express port to handle
+ */
+int pcie_port_device_suspend(struct device *dev)
{
- return device_for_each_child(&dev->dev, &state, suspend_iter);
+ return device_for_each_child(dev, NULL, suspend_iter);
}
static int resume_iter(struct device *dev, void *data)
@@ -354,78 +447,132 @@ static int resume_iter(struct device *dev, void *data)
return 0;
}
-int pcie_port_device_resume(struct pci_dev *dev)
+/**
+ * pcie_port_device_suspend - resume port services associated with a PCIe port
+ * @dev: PCI Express port to handle
+ */
+int pcie_port_device_resume(struct device *dev)
{
- return device_for_each_child(&dev->dev, NULL, resume_iter);
+ return device_for_each_child(dev, NULL, resume_iter);
}
-#endif
+#endif /* PM */
static int remove_iter(struct device *dev, void *data)
{
- struct pcie_port_service_driver *service_driver;
-
- if (dev->bus == &pcie_port_bus_type) {
- if (dev->driver) {
- service_driver = to_service_driver(dev->driver);
- if (service_driver->remove)
- service_driver->remove(to_pcie_device(dev));
- }
- *(unsigned long*)data = (unsigned long)dev;
- return 1;
- }
+ if (dev->bus == &pcie_port_bus_type)
+ device_unregister(dev);
return 0;
}
+/**
+ * pcie_port_device_remove - unregister PCI Express port service devices
+ * @dev: PCI Express port the service devices to unregister are associated with
+ *
+ * Remove PCI Express port service devices associated with given port and
+ * disable MSI-X or MSI for the port.
+ */
void pcie_port_device_remove(struct pci_dev *dev)
{
- struct device *device;
- unsigned long device_addr;
- int interrupt_mode = PCIE_PORT_INTx_MODE;
- int status;
-
- do {
- status = device_for_each_child(&dev->dev, &device_addr, remove_iter);
- if (status) {
- device = (struct device*)device_addr;
- interrupt_mode = (to_pcie_device(device))->interrupt_mode;
- put_device(device);
- device_unregister(device);
- }
- } while (status);
- /* Switch to INTx by default if MSI enabled */
- if (interrupt_mode == PCIE_PORT_MSIX_MODE)
- pci_disable_msix(dev);
- else if (interrupt_mode == PCIE_PORT_MSI_MODE)
- pci_disable_msi(dev);
+ device_for_each_child(&dev->dev, NULL, remove_iter);
+ cleanup_service_irqs(dev);
+ pci_disable_device(dev);
}
-int pcie_port_bus_register(void)
+/**
+ * pcie_port_probe_service - probe driver for given PCI Express port service
+ * @dev: PCI Express port service device to probe against
+ *
+ * If PCI Express port service driver is registered with
+ * pcie_port_service_register(), this function will be called by the driver core
+ * whenever match is found between the driver and a port service device.
+ */
+static int pcie_port_probe_service(struct device *dev)
{
- return bus_register(&pcie_port_bus_type);
+ struct pcie_device *pciedev;
+ struct pcie_port_service_driver *driver;
+ int status;
+
+ if (!dev || !dev->driver)
+ return -ENODEV;
+
+ driver = to_service_driver(dev->driver);
+ if (!driver || !driver->probe)
+ return -ENODEV;
+
+ pciedev = to_pcie_device(dev);
+ status = driver->probe(pciedev);
+ if (status)
+ return status;
+
+ dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n", driver->name);
+ get_device(dev);
+ return 0;
}
-void pcie_port_bus_unregister(void)
+/**
+ * pcie_port_remove_service - detach driver from given PCI Express port service
+ * @dev: PCI Express port service device to handle
+ *
+ * If PCI Express port service driver is registered with
+ * pcie_port_service_register(), this function will be called by the driver core
+ * when device_unregister() is called for the port service device associated
+ * with the driver.
+ */
+static int pcie_port_remove_service(struct device *dev)
{
- bus_unregister(&pcie_port_bus_type);
+ struct pcie_device *pciedev;
+ struct pcie_port_service_driver *driver;
+
+ if (!dev || !dev->driver)
+ return 0;
+
+ pciedev = to_pcie_device(dev);
+ driver = to_service_driver(dev->driver);
+ if (driver && driver->remove) {
+ dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n",
+ driver->name);
+ driver->remove(pciedev);
+ put_device(dev);
+ }
+ return 0;
}
+/**
+ * pcie_port_shutdown_service - shut down given PCI Express port service
+ * @dev: PCI Express port service device to handle
+ *
+ * If PCI Express port service driver is registered with
+ * pcie_port_service_register(), this function will be called by the driver core
+ * when device_shutdown() is called for the port service device associated
+ * with the driver.
+ */
+static void pcie_port_shutdown_service(struct device *dev) {}
+
+/**
+ * pcie_port_service_register - register PCI Express port service driver
+ * @new: PCI Express port service driver to register
+ */
int pcie_port_service_register(struct pcie_port_service_driver *new)
{
- new->driver.name = (char *)new->name;
+ if (pcie_ports_disabled)
+ return -ENODEV;
+
+ new->driver.name = new->name;
new->driver.bus = &pcie_port_bus_type;
new->driver.probe = pcie_port_probe_service;
new->driver.remove = pcie_port_remove_service;
new->driver.shutdown = pcie_port_shutdown_service;
- new->driver.suspend = pcie_port_suspend_service;
- new->driver.resume = pcie_port_resume_service;
return driver_register(&new->driver);
}
+EXPORT_SYMBOL(pcie_port_service_register);
-void pcie_port_service_unregister(struct pcie_port_service_driver *new)
+/**
+ * pcie_port_service_unregister - unregister PCI Express port service driver
+ * @drv: PCI Express port service driver to unregister
+ */
+void pcie_port_service_unregister(struct pcie_port_service_driver *drv)
{
- driver_unregister(&new->driver);
+ driver_unregister(&drv->driver);
}
-
-EXPORT_SYMBOL(pcie_port_service_register);
EXPORT_SYMBOL(pcie_port_service_unregister);
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 26057f98f72..80887eaa066 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -11,10 +11,12 @@
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include <linux/init.h>
-#include <linux/slab.h>
#include <linux/pcieport_if.h>
#include <linux/aer.h>
+#include <linux/dmi.h>
+#include <linux/pci-aspm.h>
#include "portdrv.h"
#include "aer/aerdrv.h"
@@ -24,24 +26,51 @@
*/
#define DRIVER_VERSION "v1.0"
#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
-#define DRIVER_DESC "PCIE Port Bus Driver"
+#define DRIVER_DESC "PCIe Port Bus Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
+/* If this switch is set, PCIe port native services should not be enabled. */
+bool pcie_ports_disabled;
+
+/*
+ * If this switch is set, ACPI _OSC will be used to determine whether or not to
+ * enable PCIe port native services.
+ */
+bool pcie_ports_auto = true;
+
+static int __init pcie_port_setup(char *str)
+{
+ if (!strncmp(str, "compat", 6)) {
+ pcie_ports_disabled = true;
+ } else if (!strncmp(str, "native", 6)) {
+ pcie_ports_disabled = false;
+ pcie_ports_auto = false;
+ } else if (!strncmp(str, "auto", 4)) {
+ pcie_ports_disabled = false;
+ pcie_ports_auto = true;
+ }
+
+ return 1;
+}
+__setup("pcie_ports=", pcie_port_setup);
+
/* global data */
-static const char device_name[] = "pcieport-driver";
-static int pcie_portdrv_save_config(struct pci_dev *dev)
+/**
+ * pcie_clear_root_pme_status - Clear root port PME interrupt status.
+ * @dev: PCIe root port or event collector.
+ */
+void pcie_clear_root_pme_status(struct pci_dev *dev)
{
- return pci_save_state(dev);
+ pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
}
static int pcie_portdrv_restore_config(struct pci_dev *dev)
{
int retval;
- pci_restore_state(dev);
retval = pci_enable_device(dev);
if (retval)
return retval;
@@ -50,67 +79,150 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev)
}
#ifdef CONFIG_PM
-static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state)
+static int pcie_port_resume_noirq(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ /*
+ * Some BIOSes forget to clear Root PME Status bits after system wakeup
+ * which breaks ACPI-based runtime wakeup on PCI Express, so clear those
+ * bits now just in case (shouldn't hurt).
+ */
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)
+ pcie_clear_root_pme_status(pdev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+struct d3cold_info {
+ bool no_d3cold;
+ unsigned int d3cold_delay;
+};
+
+static int pci_dev_d3cold_info(struct pci_dev *pdev, void *data)
{
- int ret = pcie_port_device_suspend(dev, state);
+ struct d3cold_info *info = data;
- if (!ret)
- ret = pcie_portdrv_save_config(dev);
- return ret;
+ info->d3cold_delay = max_t(unsigned int, pdev->d3cold_delay,
+ info->d3cold_delay);
+ if (pdev->no_d3cold)
+ info->no_d3cold = true;
+ return 0;
+}
+
+static int pcie_port_runtime_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct d3cold_info d3cold_info = {
+ .no_d3cold = false,
+ .d3cold_delay = PCI_PM_D3_WAIT,
+ };
+
+ /*
+ * If any subordinate device disable D3cold, we should not put
+ * the port into D3cold. The D3cold delay of port should be
+ * the max of that of all subordinate devices.
+ */
+ pci_walk_bus(pdev->subordinate, pci_dev_d3cold_info, &d3cold_info);
+ pdev->no_d3cold = d3cold_info.no_d3cold;
+ pdev->d3cold_delay = d3cold_info.d3cold_delay;
+ return 0;
+}
+
+static int pcie_port_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+
+static int pci_dev_pme_poll(struct pci_dev *pdev, void *data)
+{
+ bool *pme_poll = data;
+
+ if (pdev->pme_poll)
+ *pme_poll = true;
+ return 0;
}
-static int pcie_portdrv_resume(struct pci_dev *dev)
+static int pcie_port_runtime_idle(struct device *dev)
{
- pcie_portdrv_restore_config(dev);
- return pcie_port_device_resume(dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ bool pme_poll = false;
+
+ /*
+ * If any subordinate device needs pme poll, we should keep
+ * the port in D0, because we need port in D0 to poll it.
+ */
+ pci_walk_bus(pdev->subordinate, pci_dev_pme_poll, &pme_poll);
+ /* Delay for a short while to prevent too frequent suspend/resume */
+ if (!pme_poll)
+ pm_schedule_suspend(dev, 10);
+ return -EBUSY;
}
#else
-#define pcie_portdrv_suspend NULL
-#define pcie_portdrv_resume NULL
+#define pcie_port_runtime_suspend NULL
+#define pcie_port_runtime_resume NULL
+#define pcie_port_runtime_idle NULL
#endif
+static const struct dev_pm_ops pcie_portdrv_pm_ops = {
+ .suspend = pcie_port_device_suspend,
+ .resume = pcie_port_device_resume,
+ .freeze = pcie_port_device_suspend,
+ .thaw = pcie_port_device_resume,
+ .poweroff = pcie_port_device_suspend,
+ .restore = pcie_port_device_resume,
+ .resume_noirq = pcie_port_resume_noirq,
+ .runtime_suspend = pcie_port_runtime_suspend,
+ .runtime_resume = pcie_port_runtime_resume,
+ .runtime_idle = pcie_port_runtime_idle,
+};
+
+#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)
+
+#else /* !PM */
+
+#define PCIE_PORTDRV_PM_OPS NULL
+#endif /* !PM */
+
/*
* pcie_portdrv_probe - Probe PCI-Express port devices
* @dev: PCI-Express port device being probed
*
- * If detected invokes the pcie_port_device_register() method for
+ * If detected invokes the pcie_port_device_register() method for
* this port device.
*
*/
-static int __devinit pcie_portdrv_probe (struct pci_dev *dev,
- const struct pci_device_id *id )
+static int pcie_portdrv_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
{
- int status;
-
- status = pcie_port_device_probe(dev);
- if (status)
- return status;
+ int status;
- if (pci_enable_device(dev) < 0)
+ if (!pci_is_pcie(dev) ||
+ ((pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT) &&
+ (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM) &&
+ (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)))
return -ENODEV;
-
- pci_set_master(dev);
- if (!dev->irq && dev->pin) {
- printk(KERN_WARNING
- "%s->Dev[%04x:%04x] has invalid IRQ. Check vendor BIOS\n",
- __FUNCTION__, dev->vendor, dev->device);
- }
- if (pcie_port_device_register(dev)) {
- pci_disable_device(dev);
- return -ENOMEM;
- }
-
- pcie_portdrv_save_config(dev);
- pci_enable_pcie_error_reporting(dev);
+ if (!dev->irq && dev->pin) {
+ dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; check vendor BIOS\n",
+ dev->vendor, dev->device);
+ }
+ status = pcie_port_device_register(dev);
+ if (status)
+ return status;
+ pci_save_state(dev);
+ /*
+ * D3cold may not work properly on some PCIe port, so disable
+ * it by default.
+ */
+ dev->d3cold_allowed = false;
return 0;
}
-static void pcie_portdrv_remove (struct pci_dev *dev)
+static void pcie_portdrv_remove(struct pci_dev *dev)
{
pcie_port_device_remove(dev);
- kfree(pci_get_drvdata(dev));
}
static int error_detected_iter(struct device *device, void *data)
@@ -145,14 +257,11 @@ static int error_detected_iter(struct device *device, void *data)
static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
enum pci_channel_state error)
{
- struct aer_broadcast_data result_data =
- {error, PCI_ERS_RESULT_CAN_RECOVER};
- int retval;
+ struct aer_broadcast_data data = {error, PCI_ERS_RESULT_CAN_RECOVER};
- /* can not fail */
- retval = device_for_each_child(&dev->dev, &result_data, error_detected_iter);
-
- return result_data.result;
+ /* get true return value from &data */
+ device_for_each_child(&dev->dev, &data, error_detected_iter);
+ return data.result;
}
static int mmio_enabled_iter(struct device *device, void *data)
@@ -183,10 +292,9 @@ static int mmio_enabled_iter(struct device *device, void *data)
static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
{
pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
- int retval;
/* get true return value from &status */
- retval = device_for_each_child(&dev->dev, &status, mmio_enabled_iter);
+ device_for_each_child(&dev->dev, &status, mmio_enabled_iter);
return status;
}
@@ -217,18 +325,18 @@ static int slot_reset_iter(struct device *device, void *data)
static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)
{
- pci_ers_result_t status = PCI_ERS_RESULT_NONE;
- int retval;
+ pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
/* If fatal, restore cfg space for possible link reset at upstream */
if (dev->error_state == pci_channel_io_frozen) {
+ dev->state_saved = true;
+ pci_restore_state(dev);
pcie_portdrv_restore_config(dev);
pci_enable_pcie_error_reporting(dev);
}
/* get true return value from &status */
- retval = device_for_each_child(&dev->dev, &status, slot_reset_iter);
-
+ device_for_each_child(&dev->dev, &status, slot_reset_iter);
return status;
}
@@ -254,9 +362,7 @@ static int resume_iter(struct device *device, void *data)
static void pcie_portdrv_err_resume(struct pci_dev *dev)
{
- int retval;
- /* nothing to do with error value, if it ever happens */
- retval = device_for_each_child(&dev->dev, NULL, resume_iter);
+ device_for_each_child(&dev->dev, NULL, resume_iter);
}
/*
@@ -269,30 +375,58 @@ static const struct pci_device_id port_pci_ids[] = { {
};
MODULE_DEVICE_TABLE(pci, port_pci_ids);
-static struct pci_error_handlers pcie_portdrv_err_handler = {
- .error_detected = pcie_portdrv_error_detected,
- .mmio_enabled = pcie_portdrv_mmio_enabled,
- .slot_reset = pcie_portdrv_slot_reset,
- .resume = pcie_portdrv_err_resume,
+static const struct pci_error_handlers pcie_portdrv_err_handler = {
+ .error_detected = pcie_portdrv_error_detected,
+ .mmio_enabled = pcie_portdrv_mmio_enabled,
+ .slot_reset = pcie_portdrv_slot_reset,
+ .resume = pcie_portdrv_err_resume,
};
static struct pci_driver pcie_portdriver = {
- .name = (char *)device_name,
+ .name = "pcieport",
.id_table = &port_pci_ids[0],
.probe = pcie_portdrv_probe,
.remove = pcie_portdrv_remove,
- .suspend = pcie_portdrv_suspend,
- .resume = pcie_portdrv_resume,
+ .err_handler = &pcie_portdrv_err_handler,
- .err_handler = &pcie_portdrv_err_handler,
+ .driver.pm = PCIE_PORTDRV_PM_OPS,
+};
+
+static int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d)
+{
+ pr_notice("%s detected: will not use MSI for PCIe PME signaling\n",
+ d->ident);
+ pcie_pme_disable_msi();
+ return 0;
+}
+
+static struct dmi_system_id __initdata pcie_portdrv_dmi_table[] = {
+ /*
+ * Boxes that should not use MSI for PCIe PME signaling.
+ */
+ {
+ .callback = dmi_pcie_pme_disable_msi,
+ .ident = "MSI Wind U-100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "MICRO-STAR INTERNATIONAL CO., LTD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "U-100"),
+ },
+ },
+ {}
};
static int __init pcie_portdrv_init(void)
{
int retval;
+ if (pcie_ports_disabled)
+ return pci_register_driver(&pcie_portdriver);
+
+ dmi_check_system(pcie_portdrv_dmi_table);
+
retval = pcie_port_bus_register();
if (retval) {
printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
@@ -305,11 +439,4 @@ static int __init pcie_portdrv_init(void)
return retval;
}
-static void __exit pcie_portdrv_exit(void)
-{
- pci_unregister_driver(&pcie_portdriver);
- pcie_port_bus_unregister();
-}
-
module_init(pcie_portdrv_init);
-module_exit(pcie_portdrv_exit);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 2db2e4bb0d1..e3cf8a2e629 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -9,92 +9,75 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/cpumask.h>
+#include <linux/pci-aspm.h>
+#include <asm-generic/pci-bridge.h>
#include "pci.h"
#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */
#define CARDBUS_RESERVE_BUSNR 3
-#define PCI_CFG_SPACE_SIZE 256
-#define PCI_CFG_SPACE_EXP_SIZE 4096
+
+static struct resource busn_resource = {
+ .name = "PCI busn",
+ .start = 0,
+ .end = 255,
+ .flags = IORESOURCE_BUS,
+};
/* Ugh. Need to stop exporting this to modules. */
LIST_HEAD(pci_root_buses);
EXPORT_SYMBOL(pci_root_buses);
-LIST_HEAD(pci_devices);
+static LIST_HEAD(pci_domain_busn_res_list);
-/*
- * Some device drivers need know if pci is initiated.
- * Basically, we think pci is not initiated when there
- * is no device in list of pci_devices.
- */
-int no_pci_devices(void)
+struct pci_domain_busn_res {
+ struct list_head list;
+ struct resource res;
+ int domain_nr;
+};
+
+static struct resource *get_pci_domain_busn_res(int domain_nr)
{
- return list_empty(&pci_devices);
-}
+ struct pci_domain_busn_res *r;
-EXPORT_SYMBOL(no_pci_devices);
+ list_for_each_entry(r, &pci_domain_busn_res_list, list)
+ if (r->domain_nr == domain_nr)
+ return &r->res;
-#ifdef HAVE_PCI_LEGACY
-/**
- * pci_create_legacy_files - create legacy I/O port and memory files
- * @b: bus to create files under
- *
- * 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
- */
-static void pci_create_legacy_files(struct pci_bus *b)
-{
- 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);
- }
+ r = kzalloc(sizeof(*r), GFP_KERNEL);
+ if (!r)
+ return NULL;
+
+ r->domain_nr = domain_nr;
+ r->res.start = 0;
+ r->res.end = 0xff;
+ r->res.flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED;
+
+ list_add_tail(&r->list, &pci_domain_busn_res_list);
+
+ return &r->res;
}
-void pci_remove_legacy_files(struct pci_bus *b)
+static int find_anything(struct device *dev, void *data)
{
- if (b->legacy_io) {
- device_remove_bin_file(&b->dev, b->legacy_io);
- device_remove_bin_file(&b->dev, b->legacy_mem);
- kfree(b->legacy_io); /* both are allocated here */
- }
+ return 1;
}
-#else /* !HAVE_PCI_LEGACY */
-static inline void pci_create_legacy_files(struct pci_bus *bus) { return; }
-void pci_remove_legacy_files(struct pci_bus *bus) { return; }
-#endif /* HAVE_PCI_LEGACY */
/*
- * PCI Bus Class Devices
+ * Some device drivers need know if pci is initiated.
+ * Basically, we think pci is not initiated when there
+ * is no device to be found on the pci_bus_type.
*/
-static ssize_t pci_bus_show_cpuaffinity(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+int no_pci_devices(void)
{
- int ret;
- cpumask_t cpumask;
+ struct device *dev;
+ int no_devices;
- cpumask = pcibus_to_cpumask(to_pci_bus(dev));
- ret = cpumask_scnprintf(buf, PAGE_SIZE, cpumask);
- if (ret < PAGE_SIZE)
- buf[ret++] = '\n';
- return ret;
+ dev = bus_find_device(&pci_bus_type, NULL, NULL, find_anything);
+ no_devices = (dev == NULL);
+ put_device(dev);
+ return no_devices;
}
-DEVICE_ATTR(cpuaffinity, S_IRUGO, pci_bus_show_cpuaffinity, NULL);
+EXPORT_SYMBOL(no_pci_devices);
/*
* PCI Bus Class
@@ -105,12 +88,15 @@ static void release_pcibus_dev(struct device *dev)
if (pci_bus->bridge)
put_device(pci_bus->bridge);
+ pci_bus_remove_resources(pci_bus);
+ pci_release_bus_of_node(pci_bus);
kfree(pci_bus);
}
static struct class pcibus_class = {
.name = "pci_bus",
.dev_release = &release_pcibus_dev,
+ .dev_groups = pcibus_groups,
};
static int __init pcibus_class_init(void)
@@ -119,27 +105,9 @@ static int __init pcibus_class_init(void)
}
postcore_initcall(pcibus_class_init);
-/*
- * Translate the low bits of the PCI base
- * to the resource type
- */
-static inline unsigned int pci_calc_resource_flags(unsigned int flags)
+static u64 pci_size(u64 base, u64 maxbase, u64 mask)
{
- if (flags & PCI_BASE_ADDRESS_SPACE_IO)
- return IORESOURCE_IO;
-
- if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
- return IORESOURCE_MEM | IORESOURCE_PREFETCH;
-
- return IORESOURCE_MEM;
-}
-
-/*
- * Find the extent of a PCI decode..
- */
-static u32 pci_size(u32 base, u32 maxbase, u32 mask)
-{
- u32 size = mask & maxbase; /* Find the significant bits */
+ u64 size = mask & maxbase; /* Find the significant bits */
if (!size)
return 0;
@@ -155,199 +123,302 @@ static u32 pci_size(u32 base, u32 maxbase, u32 mask)
return size;
}
-static u64 pci_size64(u64 base, u64 maxbase, u64 mask)
+static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar)
{
- u64 size = mask & maxbase; /* Find the significant bits */
- if (!size)
- return 0;
+ u32 mem_type;
+ unsigned long flags;
- /* Get the lowest of them to find the decode size, and
- from that the extent. */
- size = (size & ~(size-1)) - 1;
+ if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+ flags = bar & ~PCI_BASE_ADDRESS_IO_MASK;
+ flags |= IORESOURCE_IO;
+ return flags;
+ }
- /* 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;
+ flags = bar & ~PCI_BASE_ADDRESS_MEM_MASK;
+ flags |= IORESOURCE_MEM;
+ if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
+ flags |= IORESOURCE_PREFETCH;
- return size;
+ mem_type = bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
+ switch (mem_type) {
+ case PCI_BASE_ADDRESS_MEM_TYPE_32:
+ break;
+ case PCI_BASE_ADDRESS_MEM_TYPE_1M:
+ /* 1M mem BAR treated as 32-bit BAR */
+ break;
+ case PCI_BASE_ADDRESS_MEM_TYPE_64:
+ flags |= IORESOURCE_MEM_64;
+ break;
+ default:
+ /* mem unknown type treated as 32-bit BAR */
+ break;
+ }
+ return flags;
}
-static inline int is_64bit_memory(u32 mask)
-{
- 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;
-}
+#define PCI_COMMAND_DECODE_ENABLE (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)
-static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
+/**
+ * pci_read_base - read a PCI BAR
+ * @dev: the PCI device
+ * @type: type of the BAR
+ * @res: resource buffer to be filled in
+ * @pos: BAR position in the config space
+ *
+ * Returns 1 if the BAR is 64-bit, or 0 if 32-bit.
+ */
+int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
+ struct resource *res, unsigned int pos)
{
- unsigned int pos, reg, next;
- u32 l, sz;
- struct resource *res;
+ u32 l, sz, mask;
+ u64 l64, sz64, mask64;
+ u16 orig_cmd;
+ struct pci_bus_region region, inverted_region;
+ bool bar_too_big = false, bar_too_high = false, bar_invalid = false;
+
+ mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
+
+ /* No printks while decoding is disabled! */
+ if (!dev->mmio_always_on) {
+ pci_read_config_word(dev, PCI_COMMAND, &orig_cmd);
+ if (orig_cmd & PCI_COMMAND_DECODE_ENABLE) {
+ pci_write_config_word(dev, PCI_COMMAND,
+ orig_cmd & ~PCI_COMMAND_DECODE_ENABLE);
+ }
+ }
- 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, l | 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) {
+ res->flags = decode_bar(dev, l);
+ res->flags |= IORESOURCE_SIZEALIGN;
+ if (res->flags & IORESOURCE_IO) {
+ l &= PCI_BASE_ADDRESS_IO_MASK;
+ mask = PCI_BASE_ADDRESS_IO_MASK & (u32) IO_SPACE_LIMIT;
} 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);
- 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 (res->flags & IORESOURCE_MEM_64) {
+ l64 = l;
+ sz64 = sz;
+ 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(dma_addr_t) < 8 || sizeof(resource_size_t) < 8) &&
+ sz64 > 0x100000000ULL) {
+ res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
+ res->start = 0;
+ res->end = 0;
+ bar_too_big = true;
+ goto out;
+ }
+
+ if ((sizeof(dma_addr_t) < 8) && l) {
+ /* Above 32-bit boundary; try to reallocate */
+ res->flags |= IORESOURCE_UNSET;
+ res->start = 0;
+ res->end = sz64;
+ bar_too_high = true;
+ goto out;
+ } else {
+ region.start = l64;
+ region.end = l64 + sz64;
}
+ } else {
+ sz = pci_size(l, sz, mask);
+
+ if (!sz)
+ goto fail;
+
+ region.start = l;
+ region.end = l + sz;
+ }
+
+ pcibios_bus_to_resource(dev->bus, res, &region);
+ pcibios_resource_to_bus(dev->bus, &inverted_region, res);
+
+ /*
+ * If "A" is a BAR value (a bus address), "bus_to_resource(A)" is
+ * the corresponding resource address (the physical address used by
+ * the CPU. Converting that resource address back to a bus address
+ * should yield the original BAR value:
+ *
+ * resource_to_bus(bus_to_resource(A)) == A
+ *
+ * If it doesn't, CPU accesses to "bus_to_resource(A)" will not
+ * be claimed by the device.
+ */
+ if (inverted_region.start != region.start) {
+ res->flags |= IORESOURCE_UNSET;
+ res->start = 0;
+ res->end = region.end - region.start;
+ bar_invalid = true;
}
+
+ goto out;
+
+
+fail:
+ res->flags = 0;
+out:
+ if (!dev->mmio_always_on &&
+ (orig_cmd & PCI_COMMAND_DECODE_ENABLE))
+ pci_write_config_word(dev, PCI_COMMAND, orig_cmd);
+
+ if (bar_too_big)
+ dev_err(&dev->dev, "reg 0x%x: can't handle BAR larger than 4GB (size %#010llx)\n",
+ pos, (unsigned long long) sz64);
+ if (bar_too_high)
+ dev_info(&dev->dev, "reg 0x%x: can't handle BAR above 4G (bus address %#010llx)\n",
+ pos, (unsigned long long) l64);
+ if (bar_invalid)
+ dev_info(&dev->dev, "reg 0x%x: initial BAR value %#010llx invalid\n",
+ pos, (unsigned long long) region.start);
+ if (res->flags)
+ dev_printk(KERN_DEBUG, &dev->dev, "reg 0x%x: %pR\n", pos, res);
+
+ return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
+}
+
+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;
- 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);
}
}
-void __devinit pci_read_bridge_bases(struct pci_bus *child)
+static void pci_read_bridge_io(struct pci_bus *child)
{
struct pci_dev *dev = child->self;
u8 io_base_lo, io_limit_lo;
- u16 mem_base_lo, mem_limit_lo;
- unsigned long base, limit;
+ unsigned long io_mask, io_granularity, base, limit;
+ struct pci_bus_region region;
struct resource *res;
- int i;
-
- if (!dev) /* It's a host bus, nothing to read */
- return;
- if (dev->transparent) {
- printk(KERN_INFO "PCI: Transparent bridge - %s\n", pci_name(dev));
- for(i = 3; i < PCI_BUS_NUM_RESOURCES; i++)
- child->resource[i] = child->parent->resource[i - 3];
+ io_mask = PCI_IO_RANGE_MASK;
+ io_granularity = 0x1000;
+ if (dev->io_window_1k) {
+ /* Support 1K I/O space granularity */
+ io_mask = PCI_IO_1K_RANGE_MASK;
+ io_granularity = 0x400;
}
- for(i=0; i<3; i++)
- child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i];
-
res = child->resource[0];
pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);
pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo);
- base = (io_base_lo & PCI_IO_RANGE_MASK) << 8;
- limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8;
+ base = (io_base_lo & io_mask) << 8;
+ limit = (io_limit_lo & io_mask) << 8;
if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
u16 io_base_hi, io_limit_hi;
+
pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi);
pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi);
- base |= (io_base_hi << 16);
- limit |= (io_limit_hi << 16);
+ base |= ((unsigned long) io_base_hi << 16);
+ limit |= ((unsigned long) io_limit_hi << 16);
}
if (base <= limit) {
res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;
- if (!res->start)
- res->start = base;
- if (!res->end)
- res->end = limit + 0xfff;
+ region.start = base;
+ region.end = limit + io_granularity - 1;
+ pcibios_bus_to_resource(dev->bus, res, &region);
+ dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res);
}
+}
+
+static void pci_read_bridge_mmio(struct pci_bus *child)
+{
+ struct pci_dev *dev = child->self;
+ u16 mem_base_lo, mem_limit_lo;
+ unsigned long base, limit;
+ struct pci_bus_region region;
+ struct resource *res;
res = child->resource[1];
pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo);
- base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
- limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
+ base = ((unsigned long) mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
+ limit = ((unsigned long) mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
if (base <= limit) {
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
- res->start = base;
- res->end = limit + 0xfffff;
+ region.start = base;
+ region.end = limit + 0xfffff;
+ pcibios_bus_to_resource(dev->bus, res, &region);
+ dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res);
}
+}
+
+static void pci_read_bridge_mmio_pref(struct pci_bus *child)
+{
+ struct pci_dev *dev = child->self;
+ u16 mem_base_lo, mem_limit_lo;
+ unsigned long base, limit;
+ struct pci_bus_region region;
+ struct resource *res;
res = child->resource[2];
pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
- base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
- limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
+ base = ((unsigned long) mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
+ limit = ((unsigned long) mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
u32 mem_base_hi, mem_limit_hi;
+
pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi);
pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi);
@@ -358,41 +429,244 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
*/
if (mem_base_hi <= mem_limit_hi) {
#if BITS_PER_LONG == 64
- base |= ((long) mem_base_hi) << 32;
- limit |= ((long) mem_limit_hi) << 32;
+ base |= ((unsigned long) mem_base_hi) << 32;
+ limit |= ((unsigned 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
}
}
if (base <= limit) {
- res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH;
- res->start = base;
- res->end = limit + 0xfffff;
+ res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) |
+ IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ if (res->flags & PCI_PREF_RANGE_TYPE_64)
+ res->flags |= IORESOURCE_MEM_64;
+ region.start = base;
+ region.end = limit + 0xfffff;
+ pcibios_bus_to_resource(dev->bus, res, &region);
+ dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res);
}
}
-static struct pci_bus * pci_alloc_bus(void)
+void pci_read_bridge_bases(struct pci_bus *child)
+{
+ struct pci_dev *dev = child->self;
+ struct resource *res;
+ int i;
+
+ if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */
+ return;
+
+ dev_info(&dev->dev, "PCI bridge to %pR%s\n",
+ &child->busn_res,
+ dev->transparent ? " (subtractive decode)" : "");
+
+ pci_bus_remove_resources(child);
+ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
+ child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i];
+
+ pci_read_bridge_io(child);
+ pci_read_bridge_mmio(child);
+ pci_read_bridge_mmio_pref(child);
+
+ if (dev->transparent) {
+ pci_bus_for_each_resource(child->parent, res, i) {
+ if (res && res->flags) {
+ pci_bus_add_resource(child, res,
+ PCI_SUBTRACTIVE_DECODE);
+ dev_printk(KERN_DEBUG, &dev->dev,
+ " bridge window %pR (subtractive decode)\n",
+ res);
+ }
+ }
+ }
+}
+
+static struct pci_bus *pci_alloc_bus(void)
{
struct pci_bus *b;
b = kzalloc(sizeof(*b), GFP_KERNEL);
- if (b) {
- INIT_LIST_HEAD(&b->node);
- INIT_LIST_HEAD(&b->children);
- INIT_LIST_HEAD(&b->devices);
- }
+ if (!b)
+ return NULL;
+
+ INIT_LIST_HEAD(&b->node);
+ INIT_LIST_HEAD(&b->children);
+ INIT_LIST_HEAD(&b->devices);
+ INIT_LIST_HEAD(&b->slots);
+ INIT_LIST_HEAD(&b->resources);
+ b->max_bus_speed = PCI_SPEED_UNKNOWN;
+ b->cur_bus_speed = PCI_SPEED_UNKNOWN;
return b;
}
-static struct pci_bus * __devinit
-pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
+static void pci_release_host_bridge_dev(struct device *dev)
+{
+ struct pci_host_bridge *bridge = to_pci_host_bridge(dev);
+
+ if (bridge->release_fn)
+ bridge->release_fn(bridge);
+
+ pci_free_resource_list(&bridge->windows);
+
+ kfree(bridge);
+}
+
+static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b)
+{
+ struct pci_host_bridge *bridge;
+
+ bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+ if (!bridge)
+ return NULL;
+
+ INIT_LIST_HEAD(&bridge->windows);
+ bridge->bus = b;
+ return bridge;
+}
+
+static const unsigned char pcix_bus_speed[] = {
+ PCI_SPEED_UNKNOWN, /* 0 */
+ PCI_SPEED_66MHz_PCIX, /* 1 */
+ PCI_SPEED_100MHz_PCIX, /* 2 */
+ PCI_SPEED_133MHz_PCIX, /* 3 */
+ PCI_SPEED_UNKNOWN, /* 4 */
+ PCI_SPEED_66MHz_PCIX_ECC, /* 5 */
+ PCI_SPEED_100MHz_PCIX_ECC, /* 6 */
+ PCI_SPEED_133MHz_PCIX_ECC, /* 7 */
+ PCI_SPEED_UNKNOWN, /* 8 */
+ PCI_SPEED_66MHz_PCIX_266, /* 9 */
+ PCI_SPEED_100MHz_PCIX_266, /* A */
+ PCI_SPEED_133MHz_PCIX_266, /* B */
+ PCI_SPEED_UNKNOWN, /* C */
+ PCI_SPEED_66MHz_PCIX_533, /* D */
+ PCI_SPEED_100MHz_PCIX_533, /* E */
+ PCI_SPEED_133MHz_PCIX_533 /* F */
+};
+
+const unsigned char pcie_link_speed[] = {
+ PCI_SPEED_UNKNOWN, /* 0 */
+ PCIE_SPEED_2_5GT, /* 1 */
+ PCIE_SPEED_5_0GT, /* 2 */
+ PCIE_SPEED_8_0GT, /* 3 */
+ PCI_SPEED_UNKNOWN, /* 4 */
+ PCI_SPEED_UNKNOWN, /* 5 */
+ PCI_SPEED_UNKNOWN, /* 6 */
+ PCI_SPEED_UNKNOWN, /* 7 */
+ PCI_SPEED_UNKNOWN, /* 8 */
+ PCI_SPEED_UNKNOWN, /* 9 */
+ PCI_SPEED_UNKNOWN, /* A */
+ PCI_SPEED_UNKNOWN, /* B */
+ PCI_SPEED_UNKNOWN, /* C */
+ PCI_SPEED_UNKNOWN, /* D */
+ PCI_SPEED_UNKNOWN, /* E */
+ PCI_SPEED_UNKNOWN /* F */
+};
+
+void pcie_update_link_speed(struct pci_bus *bus, u16 linksta)
+{
+ bus->cur_bus_speed = pcie_link_speed[linksta & PCI_EXP_LNKSTA_CLS];
+}
+EXPORT_SYMBOL_GPL(pcie_update_link_speed);
+
+static unsigned char agp_speeds[] = {
+ AGP_UNKNOWN,
+ AGP_1X,
+ AGP_2X,
+ AGP_4X,
+ AGP_8X
+};
+
+static enum pci_bus_speed agp_speed(int agp3, int agpstat)
+{
+ int index = 0;
+
+ if (agpstat & 4)
+ index = 3;
+ else if (agpstat & 2)
+ index = 2;
+ else if (agpstat & 1)
+ index = 1;
+ else
+ goto out;
+
+ if (agp3) {
+ index += 2;
+ if (index == 5)
+ index = 0;
+ }
+
+ out:
+ return agp_speeds[index];
+}
+
+static void pci_set_bus_speed(struct pci_bus *bus)
+{
+ struct pci_dev *bridge = bus->self;
+ int pos;
+
+ pos = pci_find_capability(bridge, PCI_CAP_ID_AGP);
+ if (!pos)
+ pos = pci_find_capability(bridge, PCI_CAP_ID_AGP3);
+ if (pos) {
+ u32 agpstat, agpcmd;
+
+ pci_read_config_dword(bridge, pos + PCI_AGP_STATUS, &agpstat);
+ bus->max_bus_speed = agp_speed(agpstat & 8, agpstat & 7);
+
+ pci_read_config_dword(bridge, pos + PCI_AGP_COMMAND, &agpcmd);
+ bus->cur_bus_speed = agp_speed(agpstat & 8, agpcmd & 7);
+ }
+
+ pos = pci_find_capability(bridge, PCI_CAP_ID_PCIX);
+ if (pos) {
+ u16 status;
+ enum pci_bus_speed max;
+
+ pci_read_config_word(bridge, pos + PCI_X_BRIDGE_SSTATUS,
+ &status);
+
+ if (status & PCI_X_SSTATUS_533MHZ) {
+ max = PCI_SPEED_133MHz_PCIX_533;
+ } else if (status & PCI_X_SSTATUS_266MHZ) {
+ max = PCI_SPEED_133MHz_PCIX_266;
+ } else if (status & PCI_X_SSTATUS_133MHZ) {
+ if ((status & PCI_X_SSTATUS_VERS) == PCI_X_SSTATUS_V2)
+ max = PCI_SPEED_133MHz_PCIX_ECC;
+ else
+ max = PCI_SPEED_133MHz_PCIX;
+ } else {
+ max = PCI_SPEED_66MHz_PCIX;
+ }
+
+ bus->max_bus_speed = max;
+ bus->cur_bus_speed = pcix_bus_speed[
+ (status & PCI_X_SSTATUS_FREQ) >> 6];
+
+ return;
+ }
+
+ if (pci_is_pcie(bridge)) {
+ u32 linkcap;
+ u16 linksta;
+
+ pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap);
+ bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS];
+
+ pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta);
+ pcie_update_link_speed(bus, linksta);
+ }
+}
+
+static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
+ struct pci_dev *bridge, int busnr)
{
struct pci_bus *child;
int i;
+ int ret;
/*
* Allocate a new bus, and inherit stuff from the parent..
@@ -401,39 +675,58 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
if (!child)
return NULL;
- child->self = bridge;
child->parent = parent;
child->ops = parent->ops;
+ child->msi = parent->msi;
child->sysdata = parent->sysdata;
child->bus_flags = parent->bus_flags;
- child->bridge = get_device(&bridge->dev);
/* initialize some portions of the bus device, but don't register it
- * now as the parent is not properly set up yet. This device will get
- * registered later in pci_bus_add_devices()
+ * now as the parent is not properly set up yet.
*/
child->dev.class = &pcibus_class;
- sprintf(child->dev.bus_id, "%04x:%02x", pci_domain_nr(child), busnr);
+ dev_set_name(&child->dev, "%04x:%02x", pci_domain_nr(child), busnr);
/*
* Set up the primary, secondary and subordinate
* bus numbers.
*/
- child->number = child->secondary = busnr;
- child->primary = parent->secondary;
- child->subordinate = 0xff;
+ child->number = child->busn_res.start = busnr;
+ child->primary = parent->busn_res.start;
+ child->busn_res.end = 0xff;
+
+ if (!bridge) {
+ child->dev.parent = parent->bridge;
+ goto add_dev;
+ }
+
+ child->self = bridge;
+ child->bridge = get_device(&bridge->dev);
+ child->dev.parent = child->bridge;
+ pci_set_bus_of_node(child);
+ pci_set_bus_speed(child);
/* Set up default resource pointers and names.. */
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) {
child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i];
child->resource[i]->name = child->name;
}
bridge->subordinate = child;
+add_dev:
+ ret = device_register(&child->dev);
+ WARN_ON(ret < 0);
+
+ pcibios_add_bus(child);
+
+ /* Create legacy_io and legacy_mem files for this bus */
+ pci_create_legacy_files(child);
+
return child;
}
-struct pci_bus *__ref pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr)
+struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev,
+ int busnr)
{
struct pci_bus *child;
@@ -445,22 +738,7 @@ struct pci_bus *__ref pci_add_new_bus(struct pci_bus *parent, struct pci_dev *de
}
return child;
}
-
-static void pci_fixup_parent_subordinate_busnr(struct pci_bus *child, int max)
-{
- struct pci_bus *parent = child->parent;
-
- /* Attempts to fix that up are really dangerous unless
- we're going to re-assign all bus numbers. */
- if (!pcibios_assign_all_busses())
- return;
-
- while (parent->parent && parent->subordinate < max) {
- parent->subordinate = max;
- pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, max);
- parent = parent->parent;
- }
-}
+EXPORT_SYMBOL(pci_add_new_bus);
/*
* If it's a bridge, configure it and scan the bus behind it.
@@ -472,63 +750,83 @@ static void pci_fixup_parent_subordinate_busnr(struct pci_bus *child, int max)
* them, we proceed to assigning numbers to the remaining buses in
* order to avoid overlaps between old and new bus numbers.
*/
-int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
+int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
{
struct pci_bus *child;
int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS);
u32 buses, i, j = 0;
u16 bctl;
+ u8 primary, secondary, subordinate;
+ int broken = 0;
pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);
+ primary = buses & 0xFF;
+ secondary = (buses >> 8) & 0xFF;
+ subordinate = (buses >> 16) & 0xFF;
+
+ dev_dbg(&dev->dev, "scanning [bus %02x-%02x] behind bridge, pass %d\n",
+ secondary, subordinate, pass);
- pr_debug("PCI: Scanning behind PCI bridge %s, config %06x, pass %d\n",
- pci_name(dev), buses & 0xffffff, pass);
+ if (!primary && (primary != bus->number) && secondary && subordinate) {
+ dev_warn(&dev->dev, "Primary bus is hard wired to 0\n");
+ primary = bus->number;
+ }
+
+ /* Check if setup is sensible at all */
+ if (!pass &&
+ (primary != bus->number || secondary <= bus->number ||
+ secondary > subordinate || subordinate > bus->busn_res.end)) {
+ dev_info(&dev->dev, "bridge configuration invalid ([bus %02x-%02x]), reconfiguring\n",
+ secondary, subordinate);
+ broken = 1;
+ }
/* Disable MasterAbortMode during probing to avoid reporting
- of bus errors (in some architectures) */
+ of bus errors (in some architectures) */
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bctl);
pci_write_config_word(dev, PCI_BRIDGE_CONTROL,
bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
- if ((buses & 0xffff00) && !pcibios_assign_all_busses() && !is_cardbus) {
- unsigned int cmax, busnr;
+ if ((secondary || subordinate) && !pcibios_assign_all_busses() &&
+ !is_cardbus && !broken) {
+ unsigned int cmax;
/*
* Bus already configured by firmware, process it in the first
* pass and just note the configuration.
*/
if (pass)
goto out;
- busnr = (buses >> 8) & 0xFF;
/*
- * If we already got to this bus through a different bridge,
- * ignore it. This can happen with the i450NX chipset.
+ * The bus might already exist for two reasons: Either we are
+ * rescanning the bus or the bus is reachable through more than
+ * one bridge. The second case 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);
- goto out;
+ child = pci_find_bus(pci_domain_nr(bus), secondary);
+ if (!child) {
+ child = pci_add_new_bus(bus, dev, secondary);
+ if (!child)
+ goto out;
+ child->primary = primary;
+ pci_bus_insert_busn_res(child, secondary, subordinate);
+ child->bridge_ctl = bctl;
}
- child = pci_add_new_bus(bus, dev, busnr);
- if (!child)
- goto out;
- child->primary = buses & 0xFF;
- child->subordinate = (buses >> 16) & 0xFF;
- child->bridge_ctl = bctl;
-
cmax = pci_scan_child_bus(child);
- if (cmax > max)
- max = cmax;
- if (child->subordinate > max)
- max = child->subordinate;
+ if (cmax > subordinate)
+ dev_warn(&dev->dev, "bridge has subordinate %02x but max busn %02x\n",
+ subordinate, cmax);
+ /* subordinate should equal child->busn_res.end */
+ if (subordinate > max)
+ max = subordinate;
} else {
/*
* We need to assign a number to this bus which we always
* do in the second pass.
*/
if (!pass) {
- if (pcibios_assign_all_busses())
+ if (pcibios_assign_all_busses() || broken || is_cardbus)
/* Temporarily disable forwarding of the
configuration cycles on all bridges in
this bus segment to avoid possible
@@ -540,18 +838,29 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
goto out;
}
+ if (max >= bus->busn_res.end) {
+ dev_warn(&dev->dev, "can't allocate child bus %02x from %pR\n",
+ max, &bus->busn_res);
+ goto out;
+ }
+
/* Clear errors */
pci_write_config_word(dev, PCI_STATUS, 0xffff);
- /* Prevent assigning a bus number that already exists.
- * This can happen when a bridge is hot-plugged */
- if (pci_find_bus(pci_domain_nr(bus), max+1))
- goto out;
- child = pci_add_new_bus(bus, dev, ++max);
+ /* The bus will already exist if we are rescanning */
+ child = pci_find_bus(pci_domain_nr(bus), max+1);
+ if (!child) {
+ child = pci_add_new_bus(bus, dev, max+1);
+ if (!child)
+ goto out;
+ pci_bus_insert_busn_res(child, max+1,
+ bus->busn_res.end);
+ }
+ max++;
buses = (buses & 0xff000000)
| ((unsigned int)(child->primary) << 0)
- | ((unsigned int)(child->secondary) << 8)
- | ((unsigned int)(child->subordinate) << 16);
+ | ((unsigned int)(child->busn_res.start) << 8)
+ | ((unsigned int)(child->busn_res.end) << 16);
/*
* yenta.c forces a secondary latency timer of 176.
@@ -561,7 +870,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
buses &= ~0xff000000;
buses |= CARDBUS_LATENCY_TIMER << 24;
}
-
+
/*
* We need to blast all three values with a single write.
*/
@@ -569,35 +878,22 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
if (!is_cardbus) {
child->bridge_ctl = bctl;
- /*
- * Adjust subordinate busnr in parent buses.
- * We do this before scanning for children because
- * some devices may not be detected if the bios
- * was lazy.
- */
- pci_fixup_parent_subordinate_busnr(child, max);
- /* Now we can scan all subordinate buses... */
max = pci_scan_child_bus(child);
- /*
- * now fix it up again since we have found
- * the real value of max.
- */
- pci_fixup_parent_subordinate_busnr(child, max);
} else {
/*
* For CardBus bridges, we leave 4 bus numbers
* as cards with a PCI-to-PCI bridge can be
* inserted later.
*/
- for (i=0; i<CARDBUS_RESERVE_BUSNR; i++) {
+ for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) {
struct pci_bus *parent = bus;
if (pci_find_bus(pci_domain_nr(bus),
max+i+1))
break;
while (parent->parent) {
if ((!pcibios_assign_all_busses()) &&
- (parent->subordinate > max) &&
- (parent->subordinate <= max+i)) {
+ (parent->busn_res.end > max) &&
+ (parent->busn_res.end <= max+i)) {
j = 1;
}
parent = parent->parent;
@@ -613,31 +909,37 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
}
}
max += i;
- pci_fixup_parent_subordinate_busnr(child, max);
}
/*
* Set the subordinate bus number to its real value.
*/
- child->subordinate = max;
+ if (max > bus->busn_res.end) {
+ dev_warn(&dev->dev, "max busn %02x is outside %pR\n",
+ max, &bus->busn_res);
+ max = bus->busn_res.end;
+ }
+ pci_bus_update_busn_res_end(child, max);
pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
}
- sprintf(child->name, (is_cardbus ? "PCI CardBus #%02x" : "PCI Bus #%02x"), child->number);
+ sprintf(child->name,
+ (is_cardbus ? "PCI CardBus %04x:%02x" : "PCI Bus %04x:%02x"),
+ pci_domain_nr(bus), child->number);
/* Has only triggered on CardBus, fixup is in yenta_socket */
while (bus->parent) {
- if ((child->subordinate > bus->subordinate) ||
- (child->number > bus->subordinate) ||
+ if ((child->busn_res.end > bus->busn_res.end) ||
+ (child->number > bus->busn_res.end) ||
(child->number < bus->number) ||
- (child->subordinate < bus->number)) {
- pr_debug("PCI: Bus #%02x (-#%02x) is %s "
- "hidden behind%s bridge #%02x (-#%02x)\n",
- child->number, child->subordinate,
- (bus->number > child->subordinate &&
- bus->subordinate < child->number) ?
+ (child->busn_res.end < bus->number)) {
+ dev_info(&child->dev, "%pR %s hidden behind%s bridge %s %pR\n",
+ &child->busn_res,
+ (bus->number > child->busn_res.end &&
+ bus->busn_res.end < child->number) ?
"wholly" : "partially",
bus->self->transparent ? " transparent" : "",
- bus->number, bus->subordinate);
+ dev_name(&bus->dev),
+ &bus->busn_res);
}
bus = bus->parent;
}
@@ -647,6 +949,7 @@ out:
return max;
}
+EXPORT_SYMBOL(pci_scan_bridge);
/*
* Read interrupt line and base address registers.
@@ -663,39 +966,180 @@ static void pci_read_irq(struct pci_dev *dev)
dev->irq = irq;
}
+void set_pcie_port_type(struct pci_dev *pdev)
+{
+ int pos;
+ u16 reg16;
+
+ pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return;
+ pdev->pcie_cap = pos;
+ pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
+ pdev->pcie_flags_reg = reg16;
+ pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, &reg16);
+ pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
+}
+
+void set_pcie_hotplug_bridge(struct pci_dev *pdev)
+{
+ u32 reg32;
+
+ pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32);
+ if (reg32 & PCI_EXP_SLTCAP_HPC)
+ pdev->is_hotplug_bridge = 1;
+}
+
+/**
+ * pci_ext_cfg_is_aliased - is ext config space just an alias of std config?
+ * @dev: PCI device
+ *
+ * PCI Express to PCI/PCI-X Bridge Specification, rev 1.0, 4.1.4 says that
+ * when forwarding a type1 configuration request the bridge must check that
+ * the extended register address field is zero. The bridge is not permitted
+ * to forward the transactions and must handle it as an Unsupported Request.
+ * Some bridges do not follow this rule and simply drop the extended register
+ * bits, resulting in the standard config space being aliased, every 256
+ * bytes across the entire configuration space. Test for this condition by
+ * comparing the first dword of each potential alias to the vendor/device ID.
+ * Known offenders:
+ * ASM1083/1085 PCIe-to-PCI Reversible Bridge (1b21:1080, rev 01 & 03)
+ * AMD/ATI SBx00 PCI to PCI Bridge (1002:4384, rev 40)
+ */
+static bool pci_ext_cfg_is_aliased(struct pci_dev *dev)
+{
+#ifdef CONFIG_PCI_QUIRKS
+ int pos;
+ u32 header, tmp;
+
+ pci_read_config_dword(dev, PCI_VENDOR_ID, &header);
+
+ for (pos = PCI_CFG_SPACE_SIZE;
+ pos < PCI_CFG_SPACE_EXP_SIZE; pos += PCI_CFG_SPACE_SIZE) {
+ if (pci_read_config_dword(dev, pos, &tmp) != PCIBIOS_SUCCESSFUL
+ || header != tmp)
+ return false;
+ }
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+/**
+ * pci_cfg_space_size - get the configuration space size of the PCI device.
+ * @dev: PCI device
+ *
+ * Regular PCI devices have 256 bytes, but PCI-X 2 and PCI Express devices
+ * have 4096 bytes. Even if the device is capable, that doesn't mean we can
+ * access it. Maybe we don't have a way to generate extended config space
+ * accesses, or the device is behind a reverse Express bridge. So we try
+ * reading the dword at 0x100 which must either be 0 or a valid extended
+ * capability header.
+ */
+static int pci_cfg_space_size_ext(struct pci_dev *dev)
+{
+ u32 status;
+ int pos = PCI_CFG_SPACE_SIZE;
+
+ if (pci_read_config_dword(dev, pos, &status) != PCIBIOS_SUCCESSFUL)
+ goto fail;
+ if (status == 0xffffffff || pci_ext_cfg_is_aliased(dev))
+ goto fail;
+
+ return PCI_CFG_SPACE_EXP_SIZE;
+
+ fail:
+ return PCI_CFG_SPACE_SIZE;
+}
+
+int pci_cfg_space_size(struct pci_dev *dev)
+{
+ int pos;
+ u32 status;
+ u16 class;
+
+ class = dev->class >> 8;
+ if (class == PCI_CLASS_BRIDGE_HOST)
+ return pci_cfg_space_size_ext(dev);
+
+ if (!pci_is_pcie(dev)) {
+ pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
+ if (!pos)
+ goto fail;
+
+ pci_read_config_dword(dev, pos + PCI_X_STATUS, &status);
+ if (!(status & (PCI_X_STATUS_266MHZ | PCI_X_STATUS_533MHZ)))
+ goto fail;
+ }
+
+ return pci_cfg_space_size_ext(dev);
+
+ fail:
+ return PCI_CFG_SPACE_SIZE;
+}
+
#define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
/**
* pci_setup_device - fill in class and map information of a device
* @dev: the device structure to fill
*
- * Initialize the device structure with information about the device's
+ * Initialize the device structure with information about the device's
* vendor,class,memory and IO-space addresses,IRQ lines etc.
* Called at initialisation of the PCI subsystem and by CardBus services.
- * Returns 0 on success and -1 if unknown type of device (not normal, bridge
- * or CardBus).
+ * Returns 0 on success and negative if unknown type of device (not normal,
+ * bridge or CardBus).
*/
-static int pci_setup_device(struct pci_dev * dev)
+int pci_setup_device(struct pci_dev *dev)
{
u32 class;
+ u8 hdr_type;
+ struct pci_slot *slot;
+ int pos = 0;
+ struct pci_bus_region region;
+ struct resource *res;
+
+ if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type))
+ return -EIO;
- 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->sysdata = dev->bus->sysdata;
+ dev->dev.parent = dev->bus->bridge;
+ dev->dev.bus = &pci_bus_type;
+ dev->hdr_type = hdr_type & 0x7f;
+ dev->multifunction = !!(hdr_type & 0x80);
+ dev->error_state = pci_channel_io_normal;
+ set_pcie_port_type(dev);
+
+ list_for_each_entry(slot, &dev->bus->slots, list)
+ if (PCI_SLOT(dev->devfn) == slot->number)
+ dev->slot = slot;
+
+ /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer)
+ set this higher, assuming the system even supports it. */
+ dev->dma_mask = 0xffffffff;
+
+ 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;
- class >>= 8; /* upper 3 bytes */
- dev->class = class;
- class >>= 8;
+ dev->class = class >> 8; /* upper 3 bytes */
+
+ dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %02x class %#08x\n",
+ dev->vendor, dev->device, dev->hdr_type, dev->class);
- pr_debug("PCI: Found %s [%04x/%04x] %06x %02x\n", pci_name(dev),
- dev->vendor, dev->device, class, dev->hdr_type);
+ /* need to have dev->class ready */
+ dev->cfg_size = pci_cfg_space_size(dev);
/* "Unknown power state" */
dev->current_state = PCI_UNKNOWN;
/* Early fixups, before probing the BARs */
pci_fixup_device(pci_fixup_early, dev);
+ /* device class may be changed after fixup */
class = dev->class >> 8;
switch (dev->hdr_type) { /* header type */
@@ -708,29 +1152,45 @@ static int pci_setup_device(struct pci_dev * dev)
pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);
/*
- * Do the ugly legacy mode stuff here rather than broken chip
- * quirk code. Legacy mode ATA controllers have fixed
- * addresses. These are not always echoed in BAR0-3, and
- * BAR0-3 in a few cases contain junk!
+ * Do the ugly legacy mode stuff here rather than broken chip
+ * quirk code. Legacy mode ATA controllers have fixed
+ * addresses. These are not always echoed in BAR0-3, and
+ * BAR0-3 in a few cases contain junk!
*/
if (class == PCI_CLASS_STORAGE_IDE) {
u8 progif;
pci_read_config_byte(dev, PCI_CLASS_PROG, &progif);
if ((progif & 1) == 0) {
- dev->resource[0].start = 0x1F0;
- dev->resource[0].end = 0x1F7;
- dev->resource[0].flags = LEGACY_IO_RESOURCE;
- dev->resource[1].start = 0x3F6;
- dev->resource[1].end = 0x3F6;
- dev->resource[1].flags = LEGACY_IO_RESOURCE;
+ region.start = 0x1F0;
+ region.end = 0x1F7;
+ res = &dev->resource[0];
+ res->flags = LEGACY_IO_RESOURCE;
+ pcibios_bus_to_resource(dev->bus, res, &region);
+ dev_info(&dev->dev, "legacy IDE quirk: reg 0x10: %pR\n",
+ res);
+ region.start = 0x3F6;
+ region.end = 0x3F6;
+ res = &dev->resource[1];
+ res->flags = LEGACY_IO_RESOURCE;
+ pcibios_bus_to_resource(dev->bus, res, &region);
+ dev_info(&dev->dev, "legacy IDE quirk: reg 0x14: %pR\n",
+ res);
}
if ((progif & 4) == 0) {
- dev->resource[2].start = 0x170;
- dev->resource[2].end = 0x177;
- dev->resource[2].flags = LEGACY_IO_RESOURCE;
- dev->resource[3].start = 0x376;
- dev->resource[3].end = 0x376;
- dev->resource[3].flags = LEGACY_IO_RESOURCE;
+ region.start = 0x170;
+ region.end = 0x177;
+ res = &dev->resource[2];
+ res->flags = LEGACY_IO_RESOURCE;
+ pcibios_bus_to_resource(dev->bus, res, &region);
+ dev_info(&dev->dev, "legacy IDE quirk: reg 0x18: %pR\n",
+ res);
+ region.start = 0x376;
+ region.end = 0x376;
+ res = &dev->resource[3];
+ res->flags = LEGACY_IO_RESOURCE;
+ pcibios_bus_to_resource(dev->bus, res, &region);
+ dev_info(&dev->dev, "legacy IDE quirk: reg 0x1c: %pR\n",
+ res);
}
}
break;
@@ -740,10 +1200,16 @@ static int pci_setup_device(struct pci_dev * dev)
goto bad;
/* The PCI-to-PCI bridge spec requires that subtractive
decoding (i.e. transparent) bridge must have programming
- interface code of 0x01. */
+ interface code of 0x01. */
pci_read_irq(dev);
dev->transparent = ((dev->class & 0xff) == 1);
pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
+ set_pcie_hotplug_bridge(dev);
+ pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);
+ if (pos) {
+ pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);
+ pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);
+ }
break;
case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */
@@ -756,13 +1222,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);
- return -1;
+ dev_err(&dev->dev, "unknown header type %02x, ignoring device\n",
+ dev->hdr_type);
+ return -EIO;
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 %#08x (doesn't match header type %02x)\n",
+ dev->class, dev->hdr_type);
dev->class = PCI_CLASS_NOT_DEFINED;
}
@@ -770,6 +1236,13 @@ static int pci_setup_device(struct pci_dev * dev)
return 0;
}
+static void pci_release_capabilities(struct pci_dev *dev)
+{
+ pci_vpd_release(dev);
+ pci_iov_release(dev);
+ pci_free_cap_save_buffers(dev);
+}
+
/**
* pci_release_dev - free a pci device structure when all users of it are finished.
* @dev: device that's been disconnected
@@ -782,66 +1255,15 @@ static void pci_release_dev(struct device *dev)
struct pci_dev *pci_dev;
pci_dev = to_pci_dev(dev);
+ pci_release_capabilities(pci_dev);
+ pci_release_of_node(pci_dev);
+ pcibios_release_device(pci_dev);
+ pci_bus_put(pci_dev->bus);
+ kfree(pci_dev->driver_override);
kfree(pci_dev);
}
-static void set_pcie_port_type(struct pci_dev *pdev)
-{
- int pos;
- u16 reg16;
-
- pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
- if (!pos)
- return;
- pdev->is_pcie = 1;
- pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
- pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4;
-}
-
-/**
- * pci_cfg_space_size - get the configuration space size of the PCI device.
- * @dev: PCI device
- *
- * Regular PCI devices have 256 bytes, but PCI-X 2 and PCI Express devices
- * have 4096 bytes. Even if the device is capable, that doesn't mean we can
- * access it. Maybe we don't have a way to generate extended config space
- * accesses, or the device is behind a reverse Express bridge. So we try
- * reading the dword at 0x100 which must either be 0 or a valid extended
- * capability header.
- */
-int pci_cfg_space_size(struct pci_dev *dev)
-{
- int pos;
- u32 status;
-
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!pos) {
- pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
- if (!pos)
- goto fail;
-
- pci_read_config_dword(dev, pos + PCI_X_STATUS, &status);
- if (!(status & (PCI_X_STATUS_266MHZ | PCI_X_STATUS_533MHZ)))
- goto fail;
- }
-
- if (pci_read_config_dword(dev, 256, &status) != PCIBIOS_SUCCESSFUL)
- goto fail;
- if (status == 0xffffffff)
- goto fail;
-
- return PCI_CFG_SPACE_EXP_SIZE;
-
- fail:
- return PCI_CFG_SPACE_SIZE;
-}
-
-static void pci_release_bus_bridge_dev(struct device *dev)
-{
- kfree(dev);
-}
-
-struct pci_dev *alloc_pci_dev(void)
+struct pci_dev *pci_alloc_dev(struct pci_bus *bus)
{
struct pci_dev *dev;
@@ -849,75 +1271,73 @@ struct pci_dev *alloc_pci_dev(void)
if (!dev)
return NULL;
- INIT_LIST_HEAD(&dev->global_list);
INIT_LIST_HEAD(&dev->bus_list);
-
- pci_msi_init_pci_dev(dev);
+ dev->dev.type = &pci_dev_type;
+ dev->bus = pci_bus_get(bus);
return dev;
}
-EXPORT_SYMBOL(alloc_pci_dev);
+EXPORT_SYMBOL(pci_alloc_dev);
-/*
- * Read the config data for a PCI device, sanity-check it
- * and fill in the dev structure...
- */
-static struct pci_dev * __devinit
-pci_scan_device(struct pci_bus *bus, int devfn)
+bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
+ int crs_timeout)
{
- struct pci_dev *dev;
- u32 l;
- u8 hdr_type;
int delay = 1;
- if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
- return NULL;
+ if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
+ return false;
/* some broken boards return 0 or ~0 if a slot is empty: */
- if (l == 0xffffffff || l == 0x00000000 ||
- l == 0x0000ffff || l == 0xffff0000)
- return NULL;
+ if (*l == 0xffffffff || *l == 0x00000000 ||
+ *l == 0x0000ffff || *l == 0xffff0000)
+ return false;
/* Configuration request Retry Status */
- while (l == 0xffff0001) {
+ while (*l == 0xffff0001) {
+ if (!crs_timeout)
+ return false;
+
msleep(delay);
delay *= 2;
- if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
- return NULL;
+ if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
+ return false;
/* Card hasn't responded in 60 seconds? Must be stuck. */
- if (delay > 60 * 1000) {
- printk(KERN_WARNING "Device %04x:%02x:%02x.%d not "
- "responding\n", pci_domain_nr(bus),
- bus->number, PCI_SLOT(devfn),
- PCI_FUNC(devfn));
- return NULL;
+ if (delay > crs_timeout) {
+ printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not responding\n",
+ pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
+ PCI_FUNC(devfn));
+ return false;
}
}
- if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type))
+ return true;
+}
+EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
+
+/*
+ * Read the config data for a PCI device, sanity-check it
+ * and fill in the dev structure...
+ */
+static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
+{
+ struct pci_dev *dev;
+ u32 l;
+
+ if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
return NULL;
- dev = alloc_pci_dev();
+ dev = pci_alloc_dev(bus);
if (!dev)
return NULL;
- dev->bus = bus;
- dev->sysdata = bus->sysdata;
- dev->dev.parent = bus->bridge;
- dev->dev.bus = &pci_bus_type;
dev->devfn = devfn;
- dev->hdr_type = hdr_type & 0x7f;
- dev->multifunction = !!(hdr_type & 0x80);
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
- dev->cfg_size = pci_cfg_space_size(dev);
- dev->error_state = pci_channel_io_normal;
- set_pcie_port_type(dev);
- /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer)
- set this higher, assuming the system even supports it. */
- dev->dma_mask = 0xffffffff;
- if (pci_setup_device(dev) < 0) {
+ pci_set_of_node(dev);
+
+ if (pci_setup_device(dev)) {
+ pci_bus_put(dev->bus);
kfree(dev);
return NULL;
}
@@ -925,11 +1345,36 @@ pci_scan_device(struct pci_bus *bus, int devfn)
return dev;
}
+static void pci_init_capabilities(struct pci_dev *dev)
+{
+ /* MSI/MSI-X list */
+ pci_msi_init_pci_dev(dev);
+
+ /* Buffers for saving PCIe and PCI-X capabilities */
+ pci_allocate_cap_save_buffers(dev);
+
+ /* Power Management */
+ pci_pm_init(dev);
+
+ /* Vital Product Data */
+ pci_vpd_pci22_init(dev);
+
+ /* Alternative Routing-ID Forwarding */
+ pci_configure_ari(dev);
+
+ /* Single Root I/O Virtualization */
+ pci_iov_init(dev);
+
+ /* Enable ACS P2P upstream forwarding */
+ pci_enable_acs(dev);
+}
+
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
{
+ int ret;
+
device_initialize(&dev->dev);
dev->dev.release = pci_release_dev;
- pci_dev_get(dev);
set_dev_node(&dev->dev, pcibus_to_node(bus));
dev->dev.dma_mask = &dev->dma_mask;
@@ -942,20 +1387,42 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
/* Fix up broken headers */
pci_fixup_device(pci_fixup_header, dev);
+ /* moved out from quirk header fixup code */
+ pci_reassigndev_resource_alignment(dev);
+
+ /* Clear the state_saved flag. */
+ dev->state_saved = false;
+
+ /* Initialize various capabilities */
+ pci_init_capabilities(dev);
+
/*
* Add the device to our list of discovered devices
* and the bus list for fixup functions, etc.
*/
- INIT_LIST_HEAD(&dev->global_list);
down_write(&pci_bus_sem);
list_add_tail(&dev->bus_list, &bus->devices);
up_write(&pci_bus_sem);
+
+ ret = pcibios_add_device(dev);
+ WARN_ON(ret < 0);
+
+ /* Notifier could use PCI capabilities */
+ dev->match_driver = false;
+ ret = device_add(&dev->dev);
+ WARN_ON(ret < 0);
}
-struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn)
+struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn)
{
struct pci_dev *dev;
+ dev = pci_get_slot(bus, devfn);
+ if (dev) {
+ pci_dev_put(dev);
+ return dev;
+ }
+
dev = pci_scan_device(bus, devfn);
if (!dev)
return NULL;
@@ -966,6 +1433,48 @@ struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn)
}
EXPORT_SYMBOL(pci_scan_single_device);
+static unsigned next_fn(struct pci_bus *bus, struct pci_dev *dev, unsigned fn)
+{
+ int pos;
+ u16 cap = 0;
+ unsigned next_fn;
+
+ if (pci_ari_enabled(bus)) {
+ if (!dev)
+ return 0;
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
+ if (!pos)
+ return 0;
+
+ pci_read_config_word(dev, pos + PCI_ARI_CAP, &cap);
+ next_fn = PCI_ARI_CAP_NFN(cap);
+ if (next_fn <= fn)
+ return 0; /* protect against malformed list */
+
+ return next_fn;
+ }
+
+ /* dev may be NULL for non-contiguous multifunction devices */
+ if (!dev || dev->multifunction)
+ return (fn + 1) % 8;
+
+ return 0;
+}
+
+static int only_one_child(struct pci_bus *bus)
+{
+ struct pci_dev *parent = bus->self;
+
+ if (!parent || !pci_is_pcie(parent))
+ return 0;
+ if (pci_pcie_type(parent) == PCI_EXP_TYPE_ROOT_PORT)
+ return 1;
+ if (pci_pcie_type(parent) == PCI_EXP_TYPE_DOWNSTREAM &&
+ !pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS))
+ return 1;
+ return 0;
+}
+
/**
* pci_scan_slot - scan a PCI slot on a bus for devices.
* @bus: PCI bus to scan
@@ -973,62 +1482,238 @@ EXPORT_SYMBOL(pci_scan_single_device);
*
* Scan a PCI slot on the specified PCI bus for devices, adding
* discovered devices to the @bus->devices list. New devices
- * will have an empty dev->global_list head.
+ * will not have is_added set.
+ *
+ * Returns the number of new devices found.
*/
int pci_scan_slot(struct pci_bus *bus, int devfn)
{
- int func, nr = 0;
- int scan_all_fns;
+ unsigned fn, nr = 0;
+ struct pci_dev *dev;
- scan_all_fns = pcibios_scan_all_fns(bus, devfn);
+ if (only_one_child(bus) && (devfn > 0))
+ return 0; /* Already scanned the entire slot */
- for (func = 0; func < 8; func++, devfn++) {
- struct pci_dev *dev;
+ dev = pci_scan_single_device(bus, devfn);
+ if (!dev)
+ return 0;
+ if (!dev->is_added)
+ nr++;
- dev = pci_scan_single_device(bus, devfn);
+ for (fn = next_fn(bus, dev, 0); fn > 0; fn = next_fn(bus, dev, fn)) {
+ dev = pci_scan_single_device(bus, devfn + fn);
if (dev) {
- nr++;
-
- /*
- * If this is a single function device,
- * don't scan past the first function.
- */
- if (!dev->multifunction) {
- if (func > 0) {
- dev->multifunction = 1;
- } else {
- break;
- }
- }
- } else {
- if (func == 0 && !scan_all_fns)
- break;
+ if (!dev->is_added)
+ nr++;
+ dev->multifunction = 1;
}
}
+
+ /* only one slot has pcie device */
+ if (bus->self && nr)
+ pcie_aspm_init_link_state(bus->self);
+
return nr;
}
+EXPORT_SYMBOL(pci_scan_slot);
+
+static int pcie_find_smpss(struct pci_dev *dev, void *data)
+{
+ u8 *smpss = data;
+
+ if (!pci_is_pcie(dev))
+ return 0;
+
+ /*
+ * We don't have a way to change MPS settings on devices that have
+ * drivers attached. A hot-added device might support only the minimum
+ * MPS setting (MPS=128). Therefore, if the fabric contains a bridge
+ * where devices may be hot-added, we limit the fabric MPS to 128 so
+ * hot-added devices will work correctly.
+ *
+ * However, if we hot-add a device to a slot directly below a Root
+ * Port, it's impossible for there to be other existing devices below
+ * the port. We don't limit the MPS in this case because we can
+ * reconfigure MPS on both the Root Port and the hot-added device,
+ * and there are no other devices involved.
+ *
+ * Note that this PCIE_BUS_SAFE path assumes no peer-to-peer DMA.
+ */
+ if (dev->is_hotplug_bridge &&
+ pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT)
+ *smpss = 0;
+
+ if (*smpss > dev->pcie_mpss)
+ *smpss = dev->pcie_mpss;
+
+ return 0;
+}
+
+static void pcie_write_mps(struct pci_dev *dev, int mps)
+{
+ int rc;
+
+ if (pcie_bus_config == PCIE_BUS_PERFORMANCE) {
+ mps = 128 << dev->pcie_mpss;
+
+ if (pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT &&
+ dev->bus->self)
+ /* For "Performance", the assumption is made that
+ * downstream communication will never be larger than
+ * the MRRS. So, the MPS only needs to be configured
+ * for the upstream communication. This being the case,
+ * walk from the top down and set the MPS of the child
+ * to that of the parent bus.
+ *
+ * Configure the device MPS with the smaller of the
+ * device MPSS or the bridge MPS (which is assumed to be
+ * properly configured at this point to the largest
+ * allowable MPS based on its parent bus).
+ */
+ mps = min(mps, pcie_get_mps(dev->bus->self));
+ }
+
+ rc = pcie_set_mps(dev, mps);
+ if (rc)
+ dev_err(&dev->dev, "Failed attempting to set the MPS\n");
+}
+
+static void pcie_write_mrrs(struct pci_dev *dev)
+{
+ int rc, mrrs;
+
+ /* In the "safe" case, do not configure the MRRS. There appear to be
+ * issues with setting MRRS to 0 on a number of devices.
+ */
+ if (pcie_bus_config != PCIE_BUS_PERFORMANCE)
+ return;
+
+ /* For Max performance, the MRRS must be set to the largest supported
+ * value. However, it cannot be configured larger than the MPS the
+ * device or the bus can support. This should already be properly
+ * configured by a prior call to pcie_write_mps.
+ */
+ mrrs = pcie_get_mps(dev);
+
+ /* MRRS is a R/W register. Invalid values can be written, but a
+ * subsequent read will verify if the value is acceptable or not.
+ * If the MRRS value provided is not acceptable (e.g., too large),
+ * shrink the value until it is acceptable to the HW.
+ */
+ while (mrrs != pcie_get_readrq(dev) && mrrs >= 128) {
+ rc = pcie_set_readrq(dev, mrrs);
+ if (!rc)
+ break;
+
+ dev_warn(&dev->dev, "Failed attempting to set the MRRS\n");
+ mrrs /= 2;
+ }
+
+ if (mrrs < 128)
+ dev_err(&dev->dev, "MRRS was unable to be configured with a safe value. If problems are experienced, try running with pci=pcie_bus_safe\n");
+}
+
+static void pcie_bus_detect_mps(struct pci_dev *dev)
+{
+ struct pci_dev *bridge = dev->bus->self;
+ int mps, p_mps;
+
+ if (!bridge)
+ return;
+
+ mps = pcie_get_mps(dev);
+ p_mps = pcie_get_mps(bridge);
+
+ if (mps != p_mps)
+ dev_warn(&dev->dev, "Max Payload Size %d, but upstream %s set to %d; if necessary, use \"pci=pcie_bus_safe\" and report a bug\n",
+ mps, pci_name(bridge), p_mps);
+}
-unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
+static int pcie_bus_configure_set(struct pci_dev *dev, void *data)
{
- unsigned int devfn, pass, max = bus->secondary;
+ int mps, orig_mps;
+
+ if (!pci_is_pcie(dev))
+ return 0;
+
+ if (pcie_bus_config == PCIE_BUS_TUNE_OFF) {
+ pcie_bus_detect_mps(dev);
+ return 0;
+ }
+
+ mps = 128 << *(u8 *)data;
+ orig_mps = pcie_get_mps(dev);
+
+ pcie_write_mps(dev, mps);
+ pcie_write_mrrs(dev);
+
+ dev_info(&dev->dev, "Max Payload Size set to %4d/%4d (was %4d), Max Read Rq %4d\n",
+ pcie_get_mps(dev), 128 << dev->pcie_mpss,
+ orig_mps, pcie_get_readrq(dev));
+
+ return 0;
+}
+
+/* pcie_bus_configure_settings requires that pci_walk_bus work in a top-down,
+ * parents then children fashion. If this changes, then this code will not
+ * work as designed.
+ */
+void pcie_bus_configure_settings(struct pci_bus *bus)
+{
+ u8 smpss = 0;
+
+ if (!bus->self)
+ return;
+
+ if (!pci_is_pcie(bus->self))
+ return;
+
+ /* FIXME - Peer to peer DMA is possible, though the endpoint would need
+ * to be aware of the MPS of the destination. To work around this,
+ * simply force the MPS of the entire system to the smallest possible.
+ */
+ if (pcie_bus_config == PCIE_BUS_PEER2PEER)
+ smpss = 0;
+
+ if (pcie_bus_config == PCIE_BUS_SAFE) {
+ smpss = bus->self->pcie_mpss;
+
+ pcie_find_smpss(bus->self, &smpss);
+ pci_walk_bus(bus, pcie_find_smpss, &smpss);
+ }
+
+ pcie_bus_configure_set(bus->self, &smpss);
+ pci_walk_bus(bus, pcie_bus_configure_set, &smpss);
+}
+EXPORT_SYMBOL_GPL(pcie_bus_configure_settings);
+
+unsigned int pci_scan_child_bus(struct pci_bus *bus)
+{
+ unsigned int devfn, pass, max = bus->busn_res.start;
struct pci_dev *dev;
- pr_debug("PCI: Scanning bus %04x:%02x\n", pci_domain_nr(bus), bus->number);
+ dev_dbg(&bus->dev, "scanning bus\n");
/* Go find them, Rover! */
for (devfn = 0; devfn < 0x100; devfn += 8)
pci_scan_slot(bus, devfn);
+ /* Reserve buses for SR-IOV capability. */
+ max += pci_iov_bus_range(bus);
+
/*
* After performing arch-dependent fixup of the bus, look behind
* all PCI-to-PCI bridges on this bus.
*/
- pr_debug("PCI: Fixups for bus %04x:%02x\n", pci_domain_nr(bus), bus->number);
- pcibios_fixup_bus(bus);
- for (pass=0; pass < 2; pass++)
+ if (!bus->is_added) {
+ dev_dbg(&bus->dev, "fixups for bus\n");
+ pcibios_fixup_bus(bus);
+ bus->is_added = 1;
+ }
+
+ for (pass = 0; pass < 2; pass++)
list_for_each_entry(dev, &bus->devices, bus_list) {
- if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
- dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
+ if (pci_is_bridge(dev))
max = pci_scan_bridge(bus, dev, max, pass);
}
@@ -1039,193 +1724,355 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
*
* Return how far we've got finding sub-buses.
*/
- pr_debug("PCI: Bus scan for %04x:%02x returning with max=%02x\n",
- pci_domain_nr(bus), bus->number, max);
+ dev_dbg(&bus->dev, "bus scan returning with max=%02x\n", max);
return max;
}
+EXPORT_SYMBOL_GPL(pci_scan_child_bus);
-struct pci_bus * pci_create_bus(struct device *parent,
- int bus, struct pci_ops *ops, void *sysdata)
+/**
+ * pcibios_root_bridge_prepare - Platform-specific host bridge setup.
+ * @bridge: Host bridge to set up.
+ *
+ * Default empty implementation. Replace with an architecture-specific setup
+ * routine, if necessary.
+ */
+int __weak pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ return 0;
+}
+
+void __weak pcibios_add_bus(struct pci_bus *bus)
+{
+}
+
+void __weak pcibios_remove_bus(struct pci_bus *bus)
+{
+}
+
+struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
+ struct pci_ops *ops, void *sysdata, struct list_head *resources)
{
int error;
- struct pci_bus *b;
- struct device *dev;
+ struct pci_host_bridge *bridge;
+ struct pci_bus *b, *b2;
+ struct pci_host_bridge_window *window, *n;
+ struct resource *res;
+ resource_size_t offset;
+ char bus_addr[64];
+ char *fmt;
b = pci_alloc_bus();
if (!b)
return NULL;
- dev = kmalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev){
- kfree(b);
- return NULL;
- }
-
b->sysdata = sysdata;
b->ops = ops;
-
- if (pci_find_bus(pci_domain_nr(b), bus)) {
+ b->number = b->busn_res.start = bus;
+ b2 = pci_find_bus(pci_domain_nr(b), bus);
+ if (b2) {
/* If we already got to this bus through a different bridge, ignore it */
- pr_debug("PCI: Bus %04x:%02x already known\n", pci_domain_nr(b), bus);
+ dev_dbg(&b2->dev, "bus already known\n");
goto err_out;
}
- down_write(&pci_bus_sem);
- list_add_tail(&b->node, &pci_root_buses);
- up_write(&pci_bus_sem);
+ bridge = pci_alloc_host_bridge(b);
+ if (!bridge)
+ goto err_out;
- memset(dev, 0, sizeof(*dev));
- dev->parent = parent;
- dev->release = pci_release_bus_bridge_dev;
- sprintf(dev->bus_id, "pci%04x:%02x", pci_domain_nr(b), bus);
- error = device_register(dev);
- if (error)
- goto dev_reg_err;
- b->bridge = get_device(dev);
+ bridge->dev.parent = parent;
+ bridge->dev.release = pci_release_host_bridge_dev;
+ dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
+ error = pcibios_root_bridge_prepare(bridge);
+ if (error) {
+ kfree(bridge);
+ goto err_out;
+ }
+
+ error = device_register(&bridge->dev);
+ if (error) {
+ put_device(&bridge->dev);
+ goto err_out;
+ }
+ b->bridge = get_device(&bridge->dev);
+ device_enable_async_suspend(b->bridge);
+ pci_set_bus_of_node(b);
+
+ if (!parent)
+ set_dev_node(b->bridge, pcibus_to_node(b));
b->dev.class = &pcibus_class;
b->dev.parent = b->bridge;
- sprintf(b->dev.bus_id, "%04x:%02x", pci_domain_nr(b), bus);
+ dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), bus);
error = device_register(&b->dev);
if (error)
goto class_dev_reg_err;
- error = device_create_file(&b->dev, &dev_attr_cpuaffinity);
- if (error)
- goto dev_create_file_err;
+
+ pcibios_add_bus(b);
/* Create legacy_io and legacy_mem files for this bus */
pci_create_legacy_files(b);
- b->number = b->secondary = bus;
- b->resource[0] = &ioport_resource;
- b->resource[1] = &iomem_resource;
+ if (parent)
+ dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev));
+ else
+ printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev));
+
+ /* Add initial resources to the bus */
+ list_for_each_entry_safe(window, n, resources, list) {
+ list_move_tail(&window->list, &bridge->windows);
+ res = window->res;
+ offset = window->offset;
+ if (res->flags & IORESOURCE_BUS)
+ pci_bus_insert_busn_res(b, bus, res->end);
+ else
+ pci_bus_add_resource(b, res, 0);
+ if (offset) {
+ if (resource_type(res) == IORESOURCE_IO)
+ fmt = " (bus address [%#06llx-%#06llx])";
+ else
+ fmt = " (bus address [%#010llx-%#010llx])";
+ snprintf(bus_addr, sizeof(bus_addr), fmt,
+ (unsigned long long) (res->start - offset),
+ (unsigned long long) (res->end - offset));
+ } else
+ bus_addr[0] = '\0';
+ dev_info(&b->dev, "root bus resource %pR%s\n", res, bus_addr);
+ }
+
+ down_write(&pci_bus_sem);
+ list_add_tail(&b->node, &pci_root_buses);
+ up_write(&pci_bus_sem);
return b;
-dev_create_file_err:
- device_unregister(&b->dev);
class_dev_reg_err:
- device_unregister(dev);
-dev_reg_err:
- down_write(&pci_bus_sem);
- list_del(&b->node);
- up_write(&pci_bus_sem);
+ put_device(&bridge->dev);
+ device_unregister(&bridge->dev);
err_out:
- kfree(dev);
kfree(b);
return NULL;
}
-struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent,
+int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max)
+{
+ struct resource *res = &b->busn_res;
+ struct resource *parent_res, *conflict;
+
+ res->start = bus;
+ res->end = bus_max;
+ res->flags = IORESOURCE_BUS;
+
+ if (!pci_is_root_bus(b))
+ parent_res = &b->parent->busn_res;
+ else {
+ parent_res = get_pci_domain_busn_res(pci_domain_nr(b));
+ res->flags |= IORESOURCE_PCI_FIXED;
+ }
+
+ conflict = request_resource_conflict(parent_res, res);
+
+ if (conflict)
+ dev_printk(KERN_DEBUG, &b->dev,
+ "busn_res: can not insert %pR under %s%pR (conflicts with %s %pR)\n",
+ res, pci_is_root_bus(b) ? "domain " : "",
+ parent_res, conflict->name, conflict);
+
+ return conflict == NULL;
+}
+
+int pci_bus_update_busn_res_end(struct pci_bus *b, int bus_max)
+{
+ struct resource *res = &b->busn_res;
+ struct resource old_res = *res;
+ resource_size_t size;
+ int ret;
+
+ if (res->start > bus_max)
+ return -EINVAL;
+
+ size = bus_max - res->start + 1;
+ ret = adjust_resource(res, res->start, size);
+ dev_printk(KERN_DEBUG, &b->dev,
+ "busn_res: %pR end %s updated to %02x\n",
+ &old_res, ret ? "can not be" : "is", bus_max);
+
+ if (!ret && !res->parent)
+ pci_bus_insert_busn_res(b, res->start, res->end);
+
+ return ret;
+}
+
+void pci_bus_release_busn_res(struct pci_bus *b)
+{
+ struct resource *res = &b->busn_res;
+ int ret;
+
+ if (!res->flags || !res->parent)
+ return;
+
+ ret = release_resource(res);
+ dev_printk(KERN_DEBUG, &b->dev,
+ "busn_res: %pR %s released\n",
+ res, ret ? "can not be" : "is");
+}
+
+struct pci_bus *pci_scan_root_bus(struct device *parent, int bus,
+ struct pci_ops *ops, void *sysdata, struct list_head *resources)
+{
+ struct pci_host_bridge_window *window;
+ bool found = false;
+ struct pci_bus *b;
+ int max;
+
+ list_for_each_entry(window, resources, list)
+ if (window->res->flags & IORESOURCE_BUS) {
+ found = true;
+ break;
+ }
+
+ b = pci_create_root_bus(parent, bus, ops, sysdata, resources);
+ if (!b)
+ return NULL;
+
+ if (!found) {
+ dev_info(&b->dev,
+ "No busn resource found for root bus, will use [bus %02x-ff]\n",
+ bus);
+ pci_bus_insert_busn_res(b, bus, 255);
+ }
+
+ max = pci_scan_child_bus(b);
+
+ if (!found)
+ pci_bus_update_busn_res_end(b, max);
+
+ pci_bus_add_devices(b);
+ return b;
+}
+EXPORT_SYMBOL(pci_scan_root_bus);
+
+/* Deprecated; use pci_scan_root_bus() instead */
+struct pci_bus *pci_scan_bus_parented(struct device *parent,
int bus, struct pci_ops *ops, void *sysdata)
{
+ LIST_HEAD(resources);
struct pci_bus *b;
- b = pci_create_bus(parent, bus, ops, sysdata);
+ pci_add_resource(&resources, &ioport_resource);
+ pci_add_resource(&resources, &iomem_resource);
+ pci_add_resource(&resources, &busn_resource);
+ b = pci_create_root_bus(parent, bus, ops, sysdata, &resources);
if (b)
- b->subordinate = pci_scan_child_bus(b);
+ pci_scan_child_bus(b);
+ else
+ pci_free_resource_list(&resources);
return b;
}
EXPORT_SYMBOL(pci_scan_bus_parented);
-#ifdef CONFIG_HOTPLUG
-EXPORT_SYMBOL(pci_add_new_bus);
-EXPORT_SYMBOL(pci_scan_slot);
-EXPORT_SYMBOL(pci_scan_bridge);
-EXPORT_SYMBOL_GPL(pci_scan_child_bus);
-#endif
-
-static int __init pci_sort_bf_cmp(const struct pci_dev *a, const struct pci_dev *b)
+struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops,
+ void *sysdata)
{
- if (pci_domain_nr(a->bus) < pci_domain_nr(b->bus)) return -1;
- else if (pci_domain_nr(a->bus) > pci_domain_nr(b->bus)) return 1;
-
- if (a->bus->number < b->bus->number) return -1;
- else if (a->bus->number > b->bus->number) return 1;
-
- if (a->devfn < b->devfn) return -1;
- else if (a->devfn > b->devfn) return 1;
+ LIST_HEAD(resources);
+ struct pci_bus *b;
- return 0;
+ pci_add_resource(&resources, &ioport_resource);
+ pci_add_resource(&resources, &iomem_resource);
+ pci_add_resource(&resources, &busn_resource);
+ b = pci_create_root_bus(NULL, bus, ops, sysdata, &resources);
+ if (b) {
+ pci_scan_child_bus(b);
+ pci_bus_add_devices(b);
+ } else {
+ pci_free_resource_list(&resources);
+ }
+ return b;
}
+EXPORT_SYMBOL(pci_scan_bus);
-/*
- * Yes, this forcably breaks the klist abstraction temporarily. It
- * just wants to sort the klist, not change reference counts and
- * take/drop locks rapidly in the process. It does all this while
- * holding the lock for the list, so objects can't otherwise be
- * added/removed while we're swizzling.
+/**
+ * pci_rescan_bus_bridge_resize - scan a PCI bus for devices.
+ * @bridge: PCI bridge for the bus to scan
+ *
+ * Scan a PCI bus and child buses for new devices, add them,
+ * and enable them, resizing bridge mmio/io resource if necessary
+ * and possible. The caller must ensure the child devices are already
+ * removed for resizing to occur.
+ *
+ * Returns the max number of subordinate bus discovered.
*/
-static void __init pci_insertion_sort_klist(struct pci_dev *a, struct list_head *list)
+unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge)
{
- struct list_head *pos;
- struct klist_node *n;
- struct device *dev;
- struct pci_dev *b;
-
- list_for_each(pos, list) {
- n = container_of(pos, struct klist_node, n_node);
- dev = container_of(n, struct device, knode_bus);
- b = to_pci_dev(dev);
- if (pci_sort_bf_cmp(a, b) <= 0) {
- list_move_tail(&a->dev.knode_bus.n_node, &b->dev.knode_bus.n_node);
- return;
- }
- }
- list_move_tail(&a->dev.knode_bus.n_node, list);
+ unsigned int max;
+ struct pci_bus *bus = bridge->subordinate;
+
+ max = pci_scan_child_bus(bus);
+
+ pci_assign_unassigned_bridge_resources(bridge);
+
+ pci_bus_add_devices(bus);
+
+ return max;
}
-static void __init pci_sort_breadthfirst_klist(void)
+/**
+ * pci_rescan_bus - scan a PCI bus for devices.
+ * @bus: PCI bus to scan
+ *
+ * Scan a PCI bus and child buses for new devices, adds them,
+ * and enables them.
+ *
+ * Returns the max number of subordinate bus discovered.
+ */
+unsigned int pci_rescan_bus(struct pci_bus *bus)
{
- LIST_HEAD(sorted_devices);
- struct list_head *pos, *tmp;
- struct klist_node *n;
- struct device *dev;
- struct pci_dev *pdev;
- struct klist *device_klist;
+ unsigned int max;
- device_klist = bus_get_device_klist(&pci_bus_type);
+ max = pci_scan_child_bus(bus);
+ pci_assign_unassigned_bus_resources(bus);
+ pci_bus_add_devices(bus);
- spin_lock(&device_klist->k_lock);
- list_for_each_safe(pos, tmp, &device_klist->k_list) {
- n = container_of(pos, struct klist_node, n_node);
- dev = container_of(n, struct device, knode_bus);
- pdev = to_pci_dev(dev);
- pci_insertion_sort_klist(pdev, &sorted_devices);
- }
- list_splice(&sorted_devices, &device_klist->k_list);
- spin_unlock(&device_klist->k_lock);
+ return max;
}
+EXPORT_SYMBOL_GPL(pci_rescan_bus);
-static void __init pci_insertion_sort_devices(struct pci_dev *a, struct list_head *list)
+/*
+ * pci_rescan_bus(), pci_rescan_bus_bridge_resize() and PCI device removal
+ * routines should always be executed under this mutex.
+ */
+static DEFINE_MUTEX(pci_rescan_remove_lock);
+
+void pci_lock_rescan_remove(void)
{
- struct pci_dev *b;
+ mutex_lock(&pci_rescan_remove_lock);
+}
+EXPORT_SYMBOL_GPL(pci_lock_rescan_remove);
- list_for_each_entry(b, list, global_list) {
- if (pci_sort_bf_cmp(a, b) <= 0) {
- list_move_tail(&a->global_list, &b->global_list);
- return;
- }
- }
- list_move_tail(&a->global_list, list);
+void pci_unlock_rescan_remove(void)
+{
+ mutex_unlock(&pci_rescan_remove_lock);
}
+EXPORT_SYMBOL_GPL(pci_unlock_rescan_remove);
-static void __init pci_sort_breadthfirst_devices(void)
+static int __init pci_sort_bf_cmp(const struct device *d_a,
+ const struct device *d_b)
{
- LIST_HEAD(sorted_devices);
- struct pci_dev *dev, *tmp;
+ const struct pci_dev *a = to_pci_dev(d_a);
+ const struct pci_dev *b = to_pci_dev(d_b);
- down_write(&pci_bus_sem);
- list_for_each_entry_safe(dev, tmp, &pci_devices, global_list) {
- pci_insertion_sort_devices(dev, &sorted_devices);
- }
- list_splice(&sorted_devices, &pci_devices);
- up_write(&pci_bus_sem);
+ if (pci_domain_nr(a->bus) < pci_domain_nr(b->bus)) return -1;
+ else if (pci_domain_nr(a->bus) > pci_domain_nr(b->bus)) return 1;
+
+ if (a->bus->number < b->bus->number) return -1;
+ else if (a->bus->number > b->bus->number) return 1;
+
+ if (a->devfn < b->devfn) return -1;
+ else if (a->devfn > b->devfn) return 1;
+
+ return 0;
}
void __init pci_sort_breadthfirst(void)
{
- pci_sort_breadthfirst_devices();
- pci_sort_breadthfirst_klist();
+ bus_sort_breadthfirst(&pci_bus_type, &pci_sort_bf_cmp);
}
-
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index ef18fcd641e..3f155e78513 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>
@@ -8,10 +6,10 @@
#include <linux/init.h>
#include <linux/pci.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
-#include <linux/smp_lock.h>
#include <linux/capability.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -19,38 +17,16 @@
static int proc_initialized; /* = 0 */
-static loff_t
-proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
+static loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
{
- loff_t new = -1;
- struct inode *inode = file->f_path.dentry->d_inode;
-
- mutex_lock(&inode->i_mutex);
- switch (whence) {
- case 0:
- new = off;
- break;
- case 1:
- new = file->f_pos + off;
- break;
- case 2:
- new = inode->i_size + off;
- break;
- }
- if (new < 0 || new > inode->i_size)
- new = -EINVAL;
- else
- file->f_pos = new;
- mutex_unlock(&inode->i_mutex);
- return new;
+ struct pci_dev *dev = PDE_DATA(file_inode(file));
+ return fixed_size_llseek(file, off, whence, dev->cfg_size);
}
-static ssize_t
-proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
{
- const struct inode *ino = file->f_path.dentry->d_inode;
- const struct proc_dir_entry *dp = PDE(ino);
- struct pci_dev *dev = dp->data;
+ struct pci_dev *dev = PDE_DATA(file_inode(file));
unsigned int pos = *ppos;
unsigned int cnt, size;
@@ -61,7 +37,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
*/
if (capable(CAP_SYS_ADMIN))
- size = dp->size;
+ size = dev->cfg_size;
else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
size = 128;
else
@@ -78,6 +54,8 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
if (!access_ok(VERIFY_WRITE, buf, cnt))
return -EINVAL;
+ pci_config_pm_runtime_get(dev);
+
if ((pos & 1) && cnt) {
unsigned char val;
pci_user_read_config_byte(dev, pos, &val);
@@ -90,7 +68,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 +77,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 +86,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;
@@ -123,18 +101,19 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
cnt--;
}
+ pci_config_pm_runtime_put(dev);
+
*ppos = pos;
return nbytes;
}
-static ssize_t
-proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos)
+static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
{
- struct inode *ino = file->f_path.dentry->d_inode;
- const struct proc_dir_entry *dp = PDE(ino);
- struct pci_dev *dev = dp->data;
+ struct inode *ino = file_inode(file);
+ struct pci_dev *dev = PDE_DATA(ino);
int pos = *ppos;
- int size = dp->size;
+ int size = dev->cfg_size;
int cnt;
if (pos >= size)
@@ -148,6 +127,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
if (!access_ok(VERIFY_READ, buf, cnt))
return -EINVAL;
+ pci_config_pm_runtime_get(dev);
+
if ((pos & 1) && cnt) {
unsigned char val;
__get_user(val, buf);
@@ -158,8 +139,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 +148,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 +157,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;
@@ -193,8 +174,10 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
cnt--;
}
+ pci_config_pm_runtime_put(dev);
+
*ppos = pos;
- i_size_write(ino, dp->size);
+ i_size_write(ino, dev->cfg_size);
return nbytes;
}
@@ -206,15 +189,12 @@ struct pci_filp_private {
static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
- const struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode);
- struct pci_dev *dev = dp->data;
+ struct pci_dev *dev = PDE_DATA(file_inode(file));
#ifdef HAVE_PCI_MMAP
struct pci_filp_private *fpriv = file->private_data;
#endif /* HAVE_PCI_MMAP */
int ret = 0;
- lock_kernel();
-
switch (cmd) {
case PCIIOC_CONTROLLER:
ret = pci_domain_nr(dev->bus);
@@ -241,24 +221,30 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
default:
ret = -EINVAL;
break;
- };
+ }
- unlock_kernel();
return ret;
}
#ifdef HAVE_PCI_MMAP
static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct inode *inode = file->f_path.dentry->d_inode;
- const struct proc_dir_entry *dp = PDE(inode);
- struct pci_dev *dev = dp->data;
+ struct pci_dev *dev = PDE_DATA(file_inode(file));
struct pci_filp_private *fpriv = file->private_data;
- int ret;
+ int i, ret;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
+ /* Make sure the caller is mapping a real resource for this device */
+ for (i = 0; i < PCI_ROM_RESOURCE; i++) {
+ if (pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS))
+ break;
+ }
+
+ if (i >= PCI_ROM_RESOURCE)
+ return -ENODEV;
+
ret = pci_mmap_page_range(dev, vma,
fpriv->mmap_state,
fpriv->write_combine);
@@ -293,10 +279,12 @@ static int proc_bus_pci_release(struct inode *inode, struct file *file)
#endif /* HAVE_PCI_MMAP */
static const struct file_operations proc_bus_pci_operations = {
+ .owner = THIS_MODULE,
.llseek = proc_bus_pci_lseek,
.read = proc_bus_pci_read,
.write = proc_bus_pci_write,
.unlocked_ioctl = proc_bus_pci_ioctl,
+ .compat_ioctl = proc_bus_pci_ioctl,
#ifdef HAVE_PCI_MMAP
.open = proc_bus_pci_open,
.release = proc_bus_pci_release,
@@ -353,15 +341,16 @@ static int show_device(struct seq_file *m, void *v)
dev->vendor,
dev->device,
dev->irq);
- /* Here should be 7 and not PCI_NUM_RESOURCES as we need to preserve compatibility */
- for (i=0; i<7; i++) {
+
+ /* only print standard and ROM resources to preserve compatibility */
+ for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
resource_size_t start, end;
pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
seq_printf(m, "\t%16llx",
(unsigned long long)(start |
(dev->resource[i].flags & PCI_REGION_FLAG_MASK)));
}
- for (i=0; i<7; i++) {
+ for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
resource_size_t start, end;
pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
seq_printf(m, "\t%16llx",
@@ -406,12 +395,11 @@ int pci_proc_attach_device(struct pci_dev *dev)
}
sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
- e = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir);
+ e = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir,
+ &proc_bus_pci_operations, dev);
if (!e)
return -ENOMEM;
- e->proc_fops = &proc_bus_pci_operations;
- e->data = dev;
- e->size = dev->cfg_size;
+ proc_set_size(e, dev->cfg_size);
dev->procent = e;
return 0;
@@ -419,41 +407,14 @@ int pci_proc_attach_device(struct pci_dev *dev)
int pci_proc_detach_device(struct pci_dev *dev)
{
- struct proc_dir_entry *e;
-
- if ((e = dev->procent)) {
- if (atomic_read(&e->count) > 1)
- return -EBUSY;
- remove_proc_entry(e->name, dev->bus->procdir);
- dev->procent = NULL;
- }
+ proc_remove(dev->procent);
+ dev->procent = NULL;
return 0;
}
-#if 0
-int pci_proc_attach_bus(struct pci_bus* bus)
+int pci_proc_detach_bus(struct pci_bus *bus)
{
- struct proc_dir_entry *de = bus->procdir;
-
- if (!proc_initialized)
- return -EACCES;
-
- if (!de) {
- char name[16];
- sprintf(name, "%02x", bus->number);
- de = bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
- if (!de)
- return -ENOMEM;
- }
- return 0;
-}
-#endif /* 0 */
-
-int pci_proc_detach_bus(struct pci_bus* bus)
-{
- struct proc_dir_entry *de = bus->procdir;
- if (de)
- remove_proc_entry(de->name, proc_bus_pci_dir);
+ proc_remove(bus->procdir);
return 0;
}
@@ -461,7 +422,9 @@ static int proc_bus_pci_dev_open(struct inode *inode, struct file *file)
{
return seq_open(file, &proc_bus_pci_devices_op);
}
+
static const struct file_operations proc_bus_pci_dev_operations = {
+ .owner = THIS_MODULE,
.open = proc_bus_pci_dev_open,
.read = seq_read,
.llseek = seq_lseek,
@@ -470,18 +433,14 @@ static const struct file_operations proc_bus_pci_dev_operations = {
static int __init pci_proc_init(void)
{
- struct proc_dir_entry *entry;
struct pci_dev *dev = NULL;
- proc_bus_pci_dir = proc_mkdir("pci", proc_bus);
- entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
- if (entry)
- entry->proc_fops = &proc_bus_pci_dev_operations;
+ proc_bus_pci_dir = proc_mkdir("bus/pci", NULL);
+ proc_create("devices", 0, proc_bus_pci_dir,
+ &proc_bus_pci_dev_operations);
proc_initialized = 1;
- while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+ for_each_pci_dev(dev)
pci_proc_attach_device(dev);
- }
+
return 0;
}
-
-__initcall(pci_proc_init);
-
+device_initcall(pci_proc_init);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index e887aa45c9c..d0f69269eb6 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -9,33 +9,49 @@
*
* Init/reset quirks for USB host controllers should be in the
* USB quirks file, where their drivers can access reuse it.
- *
- * The bridge optimization stuff has been removed. If you really
- * have a silly BIOS which is unable to set your host bridge right,
- * use the PowerTweak utility (see http://powertweak.sourceforge.net).
*/
#include <linux/types.h>
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/kallsyms.h>
+#include <linux/dmi.h>
+#include <linux/pci-aspm.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/ktime.h>
+#include <asm/dma.h> /* isa_dma_bridge_buggy */
#include "pci.h"
+/*
+ * Decoding should be disabled for a PCI device during BAR sizing to avoid
+ * conflict. But doing so may cause problems on host bridge and perhaps other
+ * key system devices. For devices that need to have mmio decoding always-on,
+ * we need to set the dev->mmio_always_on bit.
+ */
+static void quirk_mmio_always_on(struct pci_dev *dev)
+{
+ dev->mmio_always_on = 1;
+}
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on);
+
/* The Mellanox Tavor device gives false positive parity errors
* Mark this device with a broken_parity_status, to allow
* PCI scanning code to "skip" this now blacklisted device.
*/
-static void __devinit quirk_mellanox_tavor(struct pci_dev *dev)
+static void quirk_mellanox_tavor(struct pci_dev *dev)
{
dev->broken_parity_status = 1; /* This device gives false positives */
}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX,PCI_DEVICE_ID_MELLANOX_TAVOR,quirk_mellanox_tavor);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX,PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE,quirk_mellanox_tavor);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_TAVOR, quirk_mellanox_tavor);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE, quirk_mellanox_tavor);
-/* Deal with broken BIOS'es that neglect to enable passive release,
+/* Deal with broken BIOSes that neglect to enable passive release,
which can cause problems in combination with the 82441FX/PPro MTRRs */
static void quirk_passive_release(struct pci_dev *dev)
{
@@ -47,7 +63,7 @@ static void quirk_passive_release(struct pci_dev *dev)
while ((d = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, d))) {
pci_read_config_byte(d, 0x82, &dlc);
if (!(dlc & 1<<1)) {
- dev_err(&d->dev, "PIIX3: Enabling Passive Release\n");
+ dev_info(&d->dev, "PIIX3: Enabling Passive Release\n");
dlc |= 1<<1;
pci_write_config_byte(d, 0x82, dlc);
}
@@ -58,17 +74,15 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, quirk_p
/* The VIA VP2/VP3/MVP3 seem to have some 'features'. There may be a workaround
but VIA don't answer queries. If you happen to have good contacts at VIA
- ask them for me please -- Alan
-
- This appears to be BIOS not version dependent. So presumably there is a
+ ask them for me please -- Alan
+
+ This appears to be BIOS not version dependent. So presumably there is a
chipset level fix */
-int isa_dma_bridge_buggy;
-EXPORT_SYMBOL(isa_dma_bridge_buggy);
-
-static void __devinit quirk_isa_dma_hangs(struct pci_dev *dev)
+
+static void quirk_isa_dma_hangs(struct pci_dev *dev)
{
if (!isa_dma_bridge_buggy) {
- isa_dma_bridge_buggy=1;
+ isa_dma_bridge_buggy = 1;
dev_info(&dev->dev, "Activating ISA DMA hang workarounds\n");
}
}
@@ -79,20 +93,37 @@ static void __devinit quirk_isa_dma_hangs(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, quirk_isa_dma_hangs);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, quirk_isa_dma_hangs);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, quirk_isa_dma_hangs);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, quirk_isa_dma_hangs);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, quirk_isa_dma_hangs);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_CBUS_1, quirk_isa_dma_hangs);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_CBUS_2, quirk_isa_dma_hangs);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_CBUS_3, quirk_isa_dma_hangs);
-int pci_pci_problems;
-EXPORT_SYMBOL(pci_pci_problems);
+/*
+ * Intel NM10 "TigerPoint" LPC PM1a_STS.BM_STS must be clear
+ * for some HT machines to use C4 w/o hanging.
+ */
+static void quirk_tigerpoint_bm_sts(struct pci_dev *dev)
+{
+ u32 pmbase;
+ u16 pm1a;
+
+ pci_read_config_dword(dev, 0x40, &pmbase);
+ pmbase = pmbase & 0xff80;
+ pm1a = inw(pmbase);
+
+ if (pm1a & 0x10) {
+ dev_info(&dev->dev, FW_BUG "TigerPoint LPC.BM_STS cleared\n");
+ outw(0x10, pmbase);
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TGP_LPC, quirk_tigerpoint_bm_sts);
/*
* Chipsets where PCI->PCI transfers vanish or hang
*/
-static void __devinit quirk_nopcipci(struct pci_dev *dev)
+static void quirk_nopcipci(struct pci_dev *dev)
{
- if ((pci_pci_problems & PCIPCI_FAIL)==0) {
+ if ((pci_pci_problems & PCIPCI_FAIL) == 0) {
dev_info(&dev->dev, "Disabling direct PCI/PCI transfers\n");
pci_pci_problems |= PCIPCI_FAIL;
}
@@ -100,7 +131,7 @@ static void __devinit quirk_nopcipci(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, quirk_nopcipci);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, quirk_nopcipci);
-static void __devinit quirk_nopciamd(struct pci_dev *dev)
+static void quirk_nopciamd(struct pci_dev *dev)
{
u8 rev;
pci_read_config_byte(dev, 0x08, &rev);
@@ -115,27 +146,28 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8151_0, quirk_nopci
/*
* Triton requires workarounds to be used by the drivers
*/
-static void __devinit quirk_triton(struct pci_dev *dev)
+static void quirk_triton(struct pci_dev *dev)
{
- if ((pci_pci_problems&PCIPCI_TRITON)==0) {
+ if ((pci_pci_problems&PCIPCI_TRITON) == 0) {
dev_info(&dev->dev, "Limiting direct PCI/PCI transfers\n");
pci_pci_problems |= PCIPCI_TRITON;
}
}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, quirk_triton);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX, quirk_triton);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82439, quirk_triton);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82439TX, quirk_triton);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, quirk_triton);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX, quirk_triton);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82439, quirk_triton);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82439TX, quirk_triton);
/*
* VIA Apollo KT133 needs PCI latency patch
* Made according to a windows driver based patch by George E. Breese
* see PCI Latency Adjust on http://www.viahardware.com/download/viatweak.shtm
- * Also see http://www.au-ja.org/review-kt133a-1-en.phtml for
- * the info on which Mr Breese based his work.
+ * and http://www.georgebreese.com/net/software/#PCI
+ * Also see http://www.au-ja.org/review-kt133a-1-en.phtml for
+ * the info on which Mr Breese based his work.
*
* Updated based on further information from the site and also on
- * information provided by VIA
+ * information provided by VIA
*/
static void quirk_vialatency(struct pci_dev *dev)
{
@@ -143,37 +175,37 @@ static void quirk_vialatency(struct pci_dev *dev)
u8 busarb;
/* Ok we have a potential problem chipset here. Now see if we have
a buggy southbridge */
-
+
p = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, NULL);
- if (p!=NULL) {
+ if (p != NULL) {
/* 0x40 - 0x4f == 686B, 0x10 - 0x2f == 686A; thanks Dan Hollis */
/* Check for buggy part revisions */
if (p->revision < 0x40 || p->revision > 0x42)
goto exit;
} else {
p = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231, NULL);
- if (p==NULL) /* No problem parts */
+ if (p == NULL) /* No problem parts */
goto exit;
/* Check for buggy part revisions */
if (p->revision < 0x10 || p->revision > 0x12)
goto exit;
}
-
+
/*
- * Ok we have the problem. Now set the PCI master grant to
+ * Ok we have the problem. Now set the PCI master grant to
* occur every master grant. The apparent bug is that under high
* PCI load (quite common in Linux of course) you can get data
* loss when the CPU is held off the bus for 3 bus master requests
* This happens to include the IDE controllers....
*
* VIA only apply this fix when an SB Live! is present but under
- * both Linux and Windows this isnt enough, and we have seen
+ * both Linux and Windows this isn't enough, and we have seen
* corruption without SB Live! but with things like 3 UDMA IDE
* controllers. So we ignore that bit of the VIA recommendation..
*/
pci_read_config_byte(dev, 0x76, &busarb);
- /* Set bit 4 and bi 5 of byte 76 to 0x01
+ /* Set bit 4 and bi 5 of byte 76 to 0x01
"Master priority rotation on every PCI master grant */
busarb &= ~(1<<5);
busarb |= (1<<4);
@@ -193,18 +225,18 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8361, quirk_viala
/*
* VIA Apollo VP3 needs ETBF on BT848/878
*/
-static void __devinit quirk_viaetbf(struct pci_dev *dev)
+static void quirk_viaetbf(struct pci_dev *dev)
{
- if ((pci_pci_problems&PCIPCI_VIAETBF)==0) {
+ if ((pci_pci_problems&PCIPCI_VIAETBF) == 0) {
dev_info(&dev->dev, "Limiting direct PCI/PCI transfers\n");
pci_pci_problems |= PCIPCI_VIAETBF;
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_viaetbf);
-static void __devinit quirk_vsfx(struct pci_dev *dev)
+static void quirk_vsfx(struct pci_dev *dev)
{
- if ((pci_pci_problems&PCIPCI_VSFX)==0) {
+ if ((pci_pci_problems&PCIPCI_VSFX) == 0) {
dev_info(&dev->dev, "Limiting direct PCI/PCI transfers\n");
pci_pci_problems |= PCIPCI_VSFX;
}
@@ -216,40 +248,40 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576, quirk_vsfx)
* that DMA to AGP space. Latency must be set to 0xA and triton
* workaround applied too
* [Info kindly provided by ALi]
- */
-static void __init quirk_alimagik(struct pci_dev *dev)
+ */
+static void quirk_alimagik(struct pci_dev *dev)
{
- if ((pci_pci_problems&PCIPCI_ALIMAGIK)==0) {
+ if ((pci_pci_problems&PCIPCI_ALIMAGIK) == 0) {
dev_info(&dev->dev, "Limiting direct PCI/PCI transfers\n");
pci_pci_problems |= PCIPCI_ALIMAGIK|PCIPCI_TRITON;
}
}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1647, quirk_alimagik);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1651, quirk_alimagik);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1647, quirk_alimagik);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1651, quirk_alimagik);
/*
* Natoma has some interesting boundary conditions with Zoran stuff
* at least
*/
-static void __devinit quirk_natoma(struct pci_dev *dev)
+static void quirk_natoma(struct pci_dev *dev)
{
- if ((pci_pci_problems&PCIPCI_NATOMA)==0) {
+ if ((pci_pci_problems&PCIPCI_NATOMA) == 0) {
dev_info(&dev->dev, "Limiting direct PCI/PCI transfers\n");
pci_pci_problems |= PCIPCI_NATOMA;
}
}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, quirk_natoma);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443LX_0, quirk_natoma);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443LX_1, quirk_natoma);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_0, quirk_natoma);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_1, quirk_natoma);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2, quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443LX_0, quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443LX_1, quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_0, quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_1, quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2, quirk_natoma);
/*
* This chip can cause PCI parity errors if config register 0xA0 is read
* while DMAs are occurring.
*/
-static void __devinit quirk_citrine(struct pci_dev *dev)
+static void quirk_citrine(struct pci_dev *dev)
{
dev->cfg_size = 0xA0;
}
@@ -259,11 +291,12 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, quirk_cit
* S3 868 and 968 chips report region size equal to 32M, but they decode 64M.
* If it's needed, re-allocate the region.
*/
-static void __devinit quirk_s3_64M(struct pci_dev *dev)
+static void quirk_s3_64M(struct pci_dev *dev)
{
struct resource *r = &dev->resource[0];
if ((r->start & 0x3ffffff) || r->end != r->start + 0x3ffffff) {
+ r->flags |= IORESOURCE_UNSET;
r->start = 0;
r->end = 0x3ffffff;
}
@@ -271,34 +304,52 @@ static void __devinit quirk_s3_64M(struct pci_dev *dev)
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_868, quirk_s3_64M);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_968, quirk_s3_64M);
-static void __devinit quirk_io_region(struct pci_dev *dev, unsigned region,
- unsigned size, int nr, const char *name)
+/*
+ * Some CS5536 BIOSes (for example, the Soekris NET5501 board w/ comBIOS
+ * ver. 1.33 20070103) don't set the correct ISA PCI region header info.
+ * BAR0 should be 8 bytes; instead, it may be set to something like 8k
+ * (which conflicts w/ BAR1's memory range).
+ */
+static void quirk_cs5536_vsa(struct pci_dev *dev)
{
- region &= ~(size-1);
- if (region) {
- struct pci_bus_region bus_region;
- struct resource *res = dev->resource + nr;
+ if (pci_resource_len(dev, 0) != 8) {
+ struct resource *res = &dev->resource[0];
+ res->end = res->start + 8 - 1;
+ dev_info(&dev->dev, "CS5536 ISA bridge bug detected (incorrect header); workaround applied\n");
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, quirk_cs5536_vsa);
- res->name = pci_name(dev);
- res->start = region;
- res->end = region + size - 1;
- res->flags = IORESOURCE_IO;
+static void quirk_io_region(struct pci_dev *dev, int port,
+ unsigned size, int nr, const char *name)
+{
+ u16 region;
+ struct pci_bus_region bus_region;
+ struct resource *res = dev->resource + nr;
- /* Convert from PCI bus to resource space. */
- bus_region.start = res->start;
- bus_region.end = res->end;
- pcibios_bus_to_resource(dev, res, &bus_region);
+ pci_read_config_word(dev, port, &region);
+ region &= ~(size - 1);
- pci_claim_resource(dev, nr);
- dev_info(&dev->dev, "quirk: region %04x-%04x claimed by %s\n", region, region + size - 1, name);
- }
-}
+ if (!region)
+ return;
+
+ res->name = pci_name(dev);
+ res->flags = IORESOURCE_IO;
+
+ /* Convert from PCI bus to resource space */
+ bus_region.start = region;
+ bus_region.end = region + size - 1;
+ pcibios_bus_to_resource(dev->bus, res, &bus_region);
+
+ if (!pci_claim_resource(dev, nr))
+ dev_info(&dev->dev, "quirk: %pR claimed by %s\n", res, name);
+}
/*
* ATI Northbridge setups MCE the processor if you even
* read somewhere between 0x3b0->0x3bb or read 0x3d3
*/
-static void __devinit quirk_ati_exploding_mce(struct pci_dev *dev)
+static void quirk_ati_exploding_mce(struct pci_dev *dev)
{
dev_info(&dev->dev, "ATI Northbridge, reserving I/O ports 0x3b0 to 0x3bb\n");
/* Mae rhaid i ni beidio ag edrych ar y lleoliadiau I/O hyn */
@@ -318,14 +369,10 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, quirk_ati_
* 0xE0 (64 bytes of ACPI registers)
* 0xE2 (32 bytes of SMB registers)
*/
-static void __devinit quirk_ali7101_acpi(struct pci_dev *dev)
+static void quirk_ali7101_acpi(struct pci_dev *dev)
{
- u16 region;
-
- pci_read_config_word(dev, 0xE0, &region);
- quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES, "ali7101 ACPI");
- pci_read_config_word(dev, 0xE2, &region);
- quirk_io_region(dev, region, 32, PCI_BRIDGE_RESOURCES+1, "ali7101 SMB");
+ quirk_io_region(dev, 0xE0, 64, PCI_BRIDGE_RESOURCES, "ali7101 ACPI");
+ quirk_io_region(dev, 0xE2, 32, PCI_BRIDGE_RESOURCES+1, "ali7101 SMB");
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, quirk_ali7101_acpi);
@@ -349,10 +396,11 @@ static void piix4_io_quirk(struct pci_dev *dev, const char *name, unsigned int p
/*
* For now we only print it out. Eventually we'll want to
* reserve it (at least if it's in the 0x1000+ range), but
- * let's get enough confirmation reports first.
+ * let's get enough confirmation reports first.
*/
base &= -size;
- dev_info(&dev->dev, "%s PIO at %04x-%04x\n", name, base, base + size - 1);
+ dev_info(&dev->dev, "%s PIO at %04x-%04x\n", name, base,
+ base + size - 1);
}
static void piix4_mem_quirk(struct pci_dev *dev, const char *name, unsigned int port, unsigned int enable)
@@ -374,10 +422,11 @@ static void piix4_mem_quirk(struct pci_dev *dev, const char *name, unsigned int
}
/*
* For now we only print it out. Eventually we'll want to
- * reserve it, but let's get enough confirmation reports first.
+ * reserve it, but let's get enough confirmation reports first.
*/
base &= -size;
- dev_info(&dev->dev, "%s MMIO at %04x-%04x\n", name, base, base + size - 1);
+ dev_info(&dev->dev, "%s MMIO at %04x-%04x\n", name, base,
+ base + size - 1);
}
/*
@@ -386,14 +435,12 @@ static void piix4_mem_quirk(struct pci_dev *dev, const char *name, unsigned int
* 0x90 (16 bytes of SMB registers)
* and a few strange programmable PIIX4 device resources.
*/
-static void __devinit quirk_piix4_acpi(struct pci_dev *dev)
+static void quirk_piix4_acpi(struct pci_dev *dev)
{
- u32 region, res_a;
+ u32 res_a;
- pci_read_config_dword(dev, 0x40, &region);
- quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES, "PIIX4 ACPI");
- pci_read_config_dword(dev, 0x90, &region);
- quirk_io_region(dev, region, 16, PCI_BRIDGE_RESOURCES+1, "PIIX4 SMB");
+ quirk_io_region(dev, 0x40, 64, PCI_BRIDGE_RESOURCES, "PIIX4 ACPI");
+ quirk_io_region(dev, 0x90, 16, PCI_BRIDGE_RESOURCES+1, "PIIX4 SMB");
/* Device resource A has enables for some of the other ones */
pci_read_config_dword(dev, 0x5c, &res_a);
@@ -419,20 +466,43 @@ static void __devinit quirk_piix4_acpi(struct pci_dev *dev)
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, quirk_piix4_acpi);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, quirk_piix4_acpi);
+#define ICH_PMBASE 0x40
+#define ICH_ACPI_CNTL 0x44
+#define ICH4_ACPI_EN 0x10
+#define ICH6_ACPI_EN 0x80
+#define ICH4_GPIOBASE 0x58
+#define ICH4_GPIO_CNTL 0x5c
+#define ICH4_GPIO_EN 0x10
+#define ICH6_GPIOBASE 0x48
+#define ICH6_GPIO_CNTL 0x4c
+#define ICH6_GPIO_EN 0x10
+
/*
* ICH4, ICH4-M, ICH5, ICH5-M ACPI: Three IO regions pointed to by longwords at
* 0x40 (128 bytes of ACPI, GPIO & TCO registers)
* 0x58 (64 bytes of GPIO I/O space)
*/
-static void __devinit quirk_ich4_lpc_acpi(struct pci_dev *dev)
+static void quirk_ich4_lpc_acpi(struct pci_dev *dev)
{
- u32 region;
+ u8 enable;
- pci_read_config_dword(dev, 0x40, &region);
- quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, "ICH4 ACPI/GPIO/TCO");
-
- pci_read_config_dword(dev, 0x58, &region);
- quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH4 GPIO");
+ /*
+ * The check for PCIBIOS_MIN_IO is to ensure we won't create a conflict
+ * with low legacy (and fixed) ports. We don't know the decoding
+ * priority and can't tell whether the legacy device or the one created
+ * here is really at that address. This happens on boards with broken
+ * BIOSes.
+ */
+
+ pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable);
+ if (enable & ICH4_ACPI_EN)
+ quirk_io_region(dev, ICH_PMBASE, 128, PCI_BRIDGE_RESOURCES,
+ "ICH4 ACPI/GPIO/TCO");
+
+ pci_read_config_byte(dev, ICH4_GPIO_CNTL, &enable);
+ if (enable & ICH4_GPIO_EN)
+ quirk_io_region(dev, ICH4_GPIOBASE, 64, PCI_BRIDGE_RESOURCES+1,
+ "ICH4 GPIO");
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, quirk_ich4_lpc_acpi);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, quirk_ich4_lpc_acpi);
@@ -445,44 +515,119 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12,
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, quirk_ich4_lpc_acpi);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, quirk_ich4_lpc_acpi);
-static void __devinit quirk_ich6_lpc_acpi(struct pci_dev *dev)
+static void ich6_lpc_acpi_gpio(struct pci_dev *dev)
+{
+ u8 enable;
+
+ pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable);
+ if (enable & ICH6_ACPI_EN)
+ quirk_io_region(dev, ICH_PMBASE, 128, PCI_BRIDGE_RESOURCES,
+ "ICH6 ACPI/GPIO/TCO");
+
+ pci_read_config_byte(dev, ICH6_GPIO_CNTL, &enable);
+ if (enable & ICH6_GPIO_EN)
+ quirk_io_region(dev, ICH6_GPIOBASE, 64, PCI_BRIDGE_RESOURCES+1,
+ "ICH6 GPIO");
+}
+
+static void ich6_lpc_generic_decode(struct pci_dev *dev, unsigned reg, const char *name, int dynsize)
{
- u32 region;
+ u32 val;
+ u32 size, base;
- pci_read_config_dword(dev, 0x40, &region);
- quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, "ICH6 ACPI/GPIO/TCO");
+ pci_read_config_dword(dev, reg, &val);
- pci_read_config_dword(dev, 0x48, &region);
- quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH6 GPIO");
+ /* Enabled? */
+ if (!(val & 1))
+ return;
+ base = val & 0xfffc;
+ if (dynsize) {
+ /*
+ * This is not correct. It is 16, 32 or 64 bytes depending on
+ * register D31:F0:ADh bits 5:4.
+ *
+ * But this gets us at least _part_ of it.
+ */
+ size = 16;
+ } else {
+ size = 128;
+ }
+ base &= ~(size-1);
+
+ /* Just print it out for now. We should reserve it after more debugging */
+ dev_info(&dev->dev, "%s PIO at %04x-%04x\n", name, base, base+size-1);
}
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_0, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_2, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_3, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_1, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_4, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_2, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_4, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_7, quirk_ich6_lpc_acpi);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_8, quirk_ich6_lpc_acpi);
+
+static void quirk_ich6_lpc(struct pci_dev *dev)
+{
+ /* Shared ACPI/GPIO decode with all ICH6+ */
+ ich6_lpc_acpi_gpio(dev);
+
+ /* ICH6-specific generic IO decode */
+ ich6_lpc_generic_decode(dev, 0x84, "LPC Generic IO decode 1", 0);
+ ich6_lpc_generic_decode(dev, 0x88, "LPC Generic IO decode 2", 1);
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, quirk_ich6_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, quirk_ich6_lpc);
+
+static void ich7_lpc_generic_decode(struct pci_dev *dev, unsigned reg, const char *name)
+{
+ u32 val;
+ u32 mask, base;
+
+ pci_read_config_dword(dev, reg, &val);
+
+ /* Enabled? */
+ if (!(val & 1))
+ return;
+
+ /*
+ * IO base in bits 15:2, mask in bits 23:18, both
+ * are dword-based
+ */
+ base = val & 0xfffc;
+ mask = (val >> 16) & 0xfc;
+ mask |= 3;
+
+ /* Just print it out for now. We should reserve it after more debugging */
+ dev_info(&dev->dev, "%s PIO at %04x (mask %04x)\n", name, base, mask);
+}
+
+/* ICH7-10 has the same common LPC generic IO decode registers */
+static void quirk_ich7_lpc(struct pci_dev *dev)
+{
+ /* We share the common ACPI/GPIO decode with ICH6 */
+ ich6_lpc_acpi_gpio(dev);
+
+ /* And have 4 ICH7+ generic decodes */
+ ich7_lpc_generic_decode(dev, 0x84, "ICH7 LPC Generic IO decode 1");
+ ich7_lpc_generic_decode(dev, 0x88, "ICH7 LPC Generic IO decode 2");
+ ich7_lpc_generic_decode(dev, 0x8c, "ICH7 LPC Generic IO decode 3");
+ ich7_lpc_generic_decode(dev, 0x90, "ICH7 LPC Generic IO decode 4");
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_0, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_2, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_3, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_1, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_4, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_2, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_4, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_7, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_8, quirk_ich7_lpc);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_1, quirk_ich7_lpc);
/*
* VIA ACPI: One IO region pointed to by longword at
* 0x48 or 0x20 (256 bytes of ACPI registers)
*/
-static void __devinit quirk_vt82c586_acpi(struct pci_dev *dev)
+static void quirk_vt82c586_acpi(struct pci_dev *dev)
{
- u32 region;
-
- if (dev->revision & 0x10) {
- pci_read_config_dword(dev, 0x48, &region);
- region &= PCI_BASE_ADDRESS_IO_MASK;
- quirk_io_region(dev, region, 256, PCI_BRIDGE_RESOURCES, "vt82c586 ACPI");
- }
+ if (dev->revision & 0x10)
+ quirk_io_region(dev, 0x48, 256, PCI_BRIDGE_RESOURCES,
+ "vt82c586 ACPI");
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_vt82c586_acpi);
@@ -492,20 +637,14 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_vt
* 0x70 (128 bytes of hardware monitoring register)
* 0x90 (16 bytes of SMB registers)
*/
-static void __devinit quirk_vt82c686_acpi(struct pci_dev *dev)
+static void quirk_vt82c686_acpi(struct pci_dev *dev)
{
- u16 hm;
- u32 smb;
-
quirk_vt82c586_acpi(dev);
- pci_read_config_word(dev, 0x70, &hm);
- hm &= PCI_BASE_ADDRESS_IO_MASK;
- quirk_io_region(dev, hm, 128, PCI_BRIDGE_RESOURCES + 1, "vt82c686 HW-mon");
+ quirk_io_region(dev, 0x70, 128, PCI_BRIDGE_RESOURCES+1,
+ "vt82c686 HW-mon");
- pci_read_config_dword(dev, 0x90, &smb);
- smb &= PCI_BASE_ADDRESS_IO_MASK;
- quirk_io_region(dev, smb, 16, PCI_BRIDGE_RESOURCES + 2, "vt82c686 SMB");
+ quirk_io_region(dev, 0x90, 16, PCI_BRIDGE_RESOURCES+2, "vt82c686 SMB");
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_vt82c686_acpi);
@@ -514,22 +653,33 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_vt
* 0x88 (128 bytes of power management registers)
* 0xd0 (16 bytes of SMB registers)
*/
-static void __devinit quirk_vt8235_acpi(struct pci_dev *dev)
+static void quirk_vt8235_acpi(struct pci_dev *dev)
{
- u16 pm, smb;
-
- pci_read_config_word(dev, 0x88, &pm);
- pm &= PCI_BASE_ADDRESS_IO_MASK;
- quirk_io_region(dev, pm, 128, PCI_BRIDGE_RESOURCES, "vt8235 PM");
-
- pci_read_config_word(dev, 0xd0, &smb);
- smb &= PCI_BASE_ADDRESS_IO_MASK;
- quirk_io_region(dev, smb, 16, PCI_BRIDGE_RESOURCES + 1, "vt8235 SMB");
+ quirk_io_region(dev, 0x88, 128, PCI_BRIDGE_RESOURCES, "vt8235 PM");
+ quirk_io_region(dev, 0xd0, 16, PCI_BRIDGE_RESOURCES+1, "vt8235 SMB");
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, quirk_vt8235_acpi);
+/*
+ * TI XIO2000a PCIe-PCI Bridge erroneously reports it supports fast back-to-back:
+ * Disable fast back-to-back on the secondary bus segment
+ */
+static void quirk_xio2000a(struct pci_dev *dev)
+{
+ struct pci_dev *pdev;
+ u16 command;
+
+ dev_warn(&dev->dev, "TI XIO2000a quirk detected; secondary bus fast back-to-back transfers disabled\n");
+ list_for_each_entry(pdev, &dev->subordinate->devices, bus_list) {
+ pci_read_config_word(pdev, PCI_COMMAND, &command);
+ if (command & PCI_COMMAND_FAST_BACK)
+ pci_write_config_word(pdev, PCI_COMMAND, command & ~PCI_COMMAND_FAST_BACK);
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XIO2000A,
+ quirk_xio2000a);
-#ifdef CONFIG_X86_IO_APIC
+#ifdef CONFIG_X86_IO_APIC
#include <asm/io_apic.h>
@@ -543,23 +693,23 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, quirk_vt8235
static void quirk_via_ioapic(struct pci_dev *dev)
{
u8 tmp;
-
+
if (nr_ioapics < 1)
tmp = 0; /* nothing routed to external APIC */
else
tmp = 0x1f; /* all known bits (4-0) routed to external APIC */
-
+
dev_info(&dev->dev, "%sbling VIA external APIC routing\n",
tmp == 0 ? "Disa" : "Ena");
/* Offset 0x58: External APIC IRQ output control */
- pci_write_config_byte (dev, 0x58, tmp);
+ 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.
+ * VIA 8237: Some BIOSes don't set the 'Bypass APIC De-Assert Message' Bit.
* This leads to doubled level interrupt rates.
* Set this bit to get rid of cycle wastage.
* Otherwise uncritical.
@@ -576,7 +726,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.
@@ -587,7 +737,7 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_v
* noapic specified. For the moment we assume it's the erratum. We may be wrong
* of course. However the advice is demonstrably good even if so..
*/
-static void __devinit quirk_amd_ioapic(struct pci_dev *dev)
+static void quirk_amd_ioapic(struct pci_dev *dev)
{
if (dev->revision >= 0x02) {
dev_warn(&dev->dev, "I/O APIC: AMD Erratum #22 may be present. In the event of instability try\n");
@@ -596,44 +746,23 @@ static void __devinit quirk_amd_ioapic(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, quirk_amd_ioapic);
-static void __init quirk_ioapic_rmw(struct pci_dev *dev)
+static void quirk_ioapic_rmw(struct pci_dev *dev)
{
if (dev->devfn == 0 && dev->bus->number == 0)
sis_apic_bug = 1;
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_ANY_ID, quirk_ioapic_rmw);
-
-#define AMD8131_revA0 0x01
-#define AMD8131_revB0 0x11
-#define AMD8131_MISC 0x40
-#define AMD8131_NIOAMODE_BIT 0
-static void quirk_amd_8131_ioapic(struct pci_dev *dev)
-{
- unsigned char tmp;
-
- if (nr_ioapics == 0)
- return;
-
- if (dev->revision == AMD8131_revA0 || dev->revision == AMD8131_revB0) {
- dev_info(&dev->dev, "Fixing up AMD8131 IOAPIC mode\n");
- pci_read_config_byte( dev, AMD8131_MISC, &tmp);
- tmp &= ~(1 << AMD8131_NIOAMODE_BIT);
- pci_write_config_byte( dev, AMD8131_MISC, tmp);
- }
-}
-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);
#endif /* CONFIG_X86_IO_APIC */
/*
* Some settings of MMRBC can lead to data corruption so block changes.
* See AMD 8131 HyperTransport PCI-X Tunnel Revision Guide
*/
-static void __init quirk_amd_8131_mmrbc(struct pci_dev *dev)
+static void quirk_amd_8131_mmrbc(struct pci_dev *dev)
{
if (dev->subordinate && dev->revision <= 0x12) {
- dev_info(&dev->dev, "AMD8131 rev %x detected; "
- "disabling PCI-X MMRBC\n", dev->revision);
+ dev_info(&dev->dev, "AMD8131 rev %x detected; disabling PCI-X MMRBC\n",
+ dev->revision);
dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MMRBC;
}
}
@@ -647,7 +776,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_
* value of the ACPI SCI interrupt is only done for convenience.
* -jgarzik
*/
-static void __devinit quirk_via_acpi(struct pci_dev *d)
+static void quirk_via_acpi(struct pci_dev *d)
{
/*
* VIA ACPI device: SCI IRQ line in PCI config byte 0x42
@@ -754,7 +883,7 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_ANY_ID, quirk_via_vlink);
* We need to switch it off to be able to recognize the real
* type of the chip.
*/
-static void __devinit quirk_vt82c598_id(struct pci_dev *dev)
+static void quirk_vt82c598_id(struct pci_dev *dev)
{
pci_write_config_byte(dev, 0xfc, 0);
pci_read_config_word(dev, PCI_DEVICE_ID, &dev->device);
@@ -769,12 +898,12 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_vt
*/
static void quirk_cardbus_legacy(struct pci_dev *dev)
{
- if ((PCI_CLASS_BRIDGE_CARDBUS << 8) ^ dev->class)
- return;
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_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_BRIDGE_CARDBUS, 8, quirk_cardbus_legacy);
+DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_BRIDGE_CARDBUS, 8, quirk_cardbus_legacy);
/*
* Following the PCI ordering rules is optional on the AMD762. I'm not
@@ -787,17 +916,17 @@ static void quirk_amd_ordering(struct pci_dev *dev)
{
u32 pcic;
pci_read_config_dword(dev, 0x4C, &pcic);
- if ((pcic&6)!=6) {
+ if ((pcic & 6) != 6) {
pcic |= 6;
dev_warn(&dev->dev, "BIOS failed to enable PCI standards compliance; fixing this error\n");
pci_write_config_dword(dev, 0x4C, pcic);
pci_read_config_dword(dev, 0x84, &pcic);
- pcic |= (1<<23); /* Required in this mode */
+ pcic |= (1 << 23); /* Required in this mode */
pci_write_config_dword(dev, 0x84, pcic);
}
}
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
@@ -806,9 +935,11 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quir
* assigned to it. We force a larger allocation to ensure that
* nothing gets put too close to it.
*/
-static void __devinit quirk_dunord ( struct pci_dev * dev )
+static void quirk_dunord(struct pci_dev *dev)
{
- struct resource *r = &dev->resource [1];
+ struct resource *r = &dev->resource[1];
+
+ r->flags |= IORESOURCE_UNSET;
r->start = 0;
r->end = 0xffffff;
}
@@ -820,7 +951,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_DUNORD, PCI_DEVICE_ID_DUNORD_I3000, quirk
* in the ProgIf. Unfortunately, the ProgIf value is wrong - 0x80
* instead of 0x01.
*/
-static void __devinit quirk_transparent_bridge(struct pci_dev *dev)
+static void quirk_transparent_bridge(struct pci_dev *dev)
{
dev->transparent = 1;
}
@@ -830,17 +961,19 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA, 0x605, quirk_transparent_bridge)
/*
* Common misconfiguration of the MediaGX/Geode PCI master that will
* reduce PCI bandwidth from 70MB/s to 25MB/s. See the GXM/GXLV/GX1
- * datasheets found at http://www.national.com/ds/GX for info on what
+ * datasheets found at http://www.national.com/analog for info on what
* these bits do. <christer@weinigel.se>
*/
static void quirk_mediagx_master(struct pci_dev *dev)
{
u8 reg;
+
pci_read_config_byte(dev, 0x41, &reg);
if (reg & 2) {
reg &= ~2;
- dev_info(&dev->dev, "Fixup for MediaGX/Geode Slave Disconnect Boundary (0x41=0x%02x)\n", reg);
- pci_write_config_byte(dev, 0x41, reg);
+ dev_info(&dev->dev, "Fixup for MediaGX/Geode Slave Disconnect Boundary (0x41=0x%02x)\n",
+ reg);
+ pci_write_config_byte(dev, 0x41, reg);
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_PCI_MASTER, quirk_mediagx_master);
@@ -854,7 +987,7 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_PCI_MASTER, qu
static void quirk_disable_pxb(struct pci_dev *pdev)
{
u16 config;
-
+
if (pdev->revision != 0x04) /* Only C0 requires this */
return;
pci_read_config_word(pdev, 0x40, &config);
@@ -865,11 +998,11 @@ 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)
+static void quirk_amd_ide_mode(struct pci_dev *pdev)
{
- /* set sb600/sb700/sb800 sata to ahci mode */
+ /* set SBX00/Hudson-2 SATA in IDE mode to AHCI mode */
u8 tmp;
pci_read_config_byte(pdev, PCI_CLASS_DEVICE, &tmp);
@@ -885,14 +1018,18 @@ 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);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE, quirk_amd_ide_mode);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE, quirk_amd_ide_mode);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x7900, quirk_amd_ide_mode);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, 0x7900, quirk_amd_ide_mode);
/*
* Serverworks CSB5 IDE does not fully support native mode
*/
-static void __devinit quirk_svwks_csb5ide(struct pci_dev *pdev)
+static void quirk_svwks_csb5ide(struct pci_dev *pdev)
{
u8 prog;
pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog);
@@ -908,7 +1045,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB
/*
* Intel 82801CAM ICH3-M datasheet says IDE modes must be the same
*/
-static void __init quirk_ide_samemode(struct pci_dev *pdev)
+static void quirk_ide_samemode(struct pci_dev *pdev)
{
u8 prog;
@@ -923,10 +1060,31 @@ 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 quirk_no_ata_d3(struct pci_dev *pdev)
+{
+ pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
+}
+/* Quirk the legacy ATA devices only. The AHCI ones are ok */
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_ATI, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3);
+/* ALi loses some register settings that we cannot then restore */
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3);
+/* VIA comes back fine but we need to keep it alive or ACPI GTM failures
+ occur when mode detecting */
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_VIA, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_IDE, 8, 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.
*/
-static void __init quirk_eisa_bridge(struct pci_dev *dev)
+static void quirk_eisa_bridge(struct pci_dev *dev)
{
dev->class = PCI_CLASS_BRIDGE_EISA << 8;
}
@@ -937,11 +1095,11 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82375, quirk_e
* On ASUS P4B boards, the SMBus PCI Device within the ICH2/4 southbridge
* is not activated. The myth is that Asus said that they do not want the
* users to be irritated by just another PCI Device in the Win98 device
- * manager. (see the file prog/hotplug/README.p4b in the lm_sensors
+ * manager. (see the file prog/hotplug/README.p4b in the lm_sensors
* package 2.7.0 for details)
*
- * The SMBus PCI Device can be activated by setting a bit in the ICH LPC
- * bridge. Unfortunately, this device has no subvendor/subdevice ID. So it
+ * The SMBus PCI Device can be activated by setting a bit in the ICH LPC
+ * bridge. Unfortunately, this device has no subvendor/subdevice ID. So it
* becomes necessary to do this tweak in two steps -- the chosen trigger
* is either the Host bridge (preferred) or on-board VGA controller.
*
@@ -960,11 +1118,11 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82375, quirk_e
*/
static int asus_hides_smbus;
-static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
+static void asus_hides_smbus_hostbridge(struct pci_dev *dev)
{
if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK)) {
if (dev->device == PCI_DEVICE_ID_INTEL_82845_HB)
- switch(dev->subsystem_device) {
+ switch (dev->subsystem_device) {
case 0x8025: /* P4B-LX */
case 0x8070: /* P4B */
case 0x8088: /* P4B533 */
@@ -972,14 +1130,14 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
asus_hides_smbus = 1;
}
else if (dev->device == PCI_DEVICE_ID_INTEL_82845G_HB)
- switch(dev->subsystem_device) {
+ switch (dev->subsystem_device) {
case 0x80b1: /* P4GE-V */
case 0x80b2: /* P4PE */
case 0x8093: /* P4B533-V */
asus_hides_smbus = 1;
}
else if (dev->device == PCI_DEVICE_ID_INTEL_82850_HB)
- switch(dev->subsystem_device) {
+ switch (dev->subsystem_device) {
case 0x8030: /* P4T533 */
asus_hides_smbus = 1;
}
@@ -997,6 +1155,7 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
switch (dev->subsystem_device) {
case 0x1751: /* M2N notebook */
case 0x1821: /* M5N notebook */
+ case 0x1897: /* A6L notebook */
asus_hides_smbus = 1;
}
else if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB)
@@ -1018,7 +1177,7 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
}
} else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_HP)) {
if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB)
- switch(dev->subsystem_device) {
+ switch (dev->subsystem_device) {
case 0x088C: /* HP Compaq nc8000 */
case 0x0890: /* HP Compaq nc6000 */
asus_hides_smbus = 1;
@@ -1027,6 +1186,7 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
switch (dev->subsystem_device) {
case 0x12bc: /* HP D330L */
case 0x12bd: /* HP D530 */
+ case 0x006a: /* HP Compaq nx9500 */
asus_hides_smbus = 1;
}
else if (dev->device == PCI_DEVICE_ID_INTEL_82875_HB)
@@ -1034,26 +1194,46 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
case 0x12bf: /* HP xw4100 */
asus_hides_smbus = 1;
}
- } else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG)) {
- if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB)
- switch(dev->subsystem_device) {
- case 0xC00C: /* Samsung P35 notebook */
- asus_hides_smbus = 1;
- }
+ } else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG)) {
+ if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB)
+ switch (dev->subsystem_device) {
+ case 0xC00C: /* Samsung P35 notebook */
+ asus_hides_smbus = 1;
+ }
} else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ)) {
if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB)
- switch(dev->subsystem_device) {
+ switch (dev->subsystem_device) {
case 0x0058: /* Compaq Evo N620c */
asus_hides_smbus = 1;
}
else if (dev->device == PCI_DEVICE_ID_INTEL_82810_IG3)
- switch(dev->subsystem_device) {
+ switch (dev->subsystem_device) {
case 0xB16C: /* Compaq Deskpro EP 401963-001 (PCA# 010174) */
/* Motherboard doesn't have Host bridge
* subvendor/subdevice IDs, therefore checking
* its on-board VGA controller */
asus_hides_smbus = 1;
}
+ else if (dev->device == PCI_DEVICE_ID_INTEL_82801DB_2)
+ switch (dev->subsystem_device) {
+ case 0x00b8: /* Compaq Evo D510 CMT */
+ case 0x00b9: /* Compaq Evo D510 SFF */
+ case 0x00ba: /* Compaq Evo D510 USDT */
+ /* Motherboard doesn't have Host bridge
+ * subvendor/subdevice IDs and on-board VGA
+ * controller is disabled if an AGP card is
+ * inserted, therefore checking USB UHCI
+ * Controller #1 */
+ 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,11 +1248,13 @@ 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_82801DB_2, 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)
{
u16 val;
-
+
if (likely(!asus_hides_smbus))
return;
@@ -1081,7 +1263,8 @@ static void asus_hides_smbus_lpc(struct pci_dev *dev)
pci_write_config_word(dev, 0xF2, val & (~0x8));
pci_read_config_word(dev, 0xF2, &val);
if (val & 0x8)
- dev_info(&dev->dev, "i801 SMBus device continues to play 'hide and seek'! 0x%x\n", val);
+ dev_info(&dev->dev, "i801 SMBus device continues to play 'hide and seek'! 0x%x\n",
+ val);
else
dev_info(&dev->dev, "Enabled i801 SMBus device\n");
}
@@ -1093,31 +1276,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 +1348,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 +1385,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);
/*
@@ -1199,13 +1412,14 @@ static void asus_hides_ac97_lpc(struct pci_dev *dev)
pci_write_config_byte(dev, 0x50, val & (~0xc0));
pci_read_config_byte(dev, 0x50, &val);
if (val & 0xc0)
- dev_info(&dev->dev, "Onboard AC97/MC97 devices continue to play 'hide and seek'! 0x%x\n", val);
+ dev_info(&dev->dev, "Onboard AC97/MC97 devices continue to play 'hide and seek'! 0x%x\n",
+ val);
else
dev_info(&dev->dev, "Enabled onboard AC97/MC97 devices\n");
}
}
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)
@@ -1230,7 +1444,9 @@ static void quirk_jmicron_ata(struct pci_dev *pdev)
conf5 &= ~(1 << 24); /* Clear bit 24 */
switch (pdev->device) {
- case PCI_DEVICE_ID_JMICRON_JMB360:
+ case PCI_DEVICE_ID_JMICRON_JMB360: /* SATA single port */
+ case PCI_DEVICE_ID_JMICRON_JMB362: /* SATA dual ports */
+ case PCI_DEVICE_ID_JMICRON_JMB364: /* SATA dual ports */
/* The controller should be in single function ahci mode */
conf1 |= 0x0002A100; /* Set 8, 13, 15, 17 */
break;
@@ -1242,6 +1458,7 @@ static void quirk_jmicron_ata(struct pci_dev *pdev)
/* Fall through */
case PCI_DEVICE_ID_JMICRON_JMB361:
case PCI_DEVICE_ID_JMICRON_JMB363:
+ case PCI_DEVICE_ID_JMICRON_JMB369:
/* Enable dual function mode, AHCI on fn 0, IDE fn1 */
/* Set the class codes correctly and then direct IDE 0 */
conf1 |= 0x00C2A1B3; /* Set 0, 1, 4, 5, 7, 8, 13, 15, 17, 22, 23 */
@@ -1266,21 +1483,27 @@ static void quirk_jmicron_ata(struct pci_dev *pdev)
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB362, quirk_jmicron_ata);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB364, quirk_jmicron_ata);
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_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB369, 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_JMB362, 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_JMB364, 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);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB369, quirk_jmicron_ata);
#endif
#ifdef CONFIG_X86_IO_APIC
-static void __init quirk_alder_ioapic(struct pci_dev *pdev)
+static void quirk_alder_ioapic(struct pci_dev *pdev)
{
int i;
@@ -1295,20 +1518,16 @@ static void __init quirk_alder_ioapic(struct pci_dev *pdev)
/* The next five BARs all seem to be rubbish, so just clean
* them out */
- for (i=1; i < 6; i++) {
+ for (i = 1; i < 6; i++)
memset(&pdev->resource[i], 0, sizeof(pdev->resource[i]));
- }
-
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EESSC, quirk_alder_ioapic);
#endif
-int pcie_mch_quirk;
-EXPORT_SYMBOL(pcie_mch_quirk);
-
-static void __devinit quirk_pcie_mch(struct pci_dev *pdev)
+static void quirk_pcie_mch(struct pci_dev *pdev)
{
- pcie_mch_quirk = 1;
+ pci_msi_off(pdev);
+ pdev->no_msi = 1;
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7520_MCH, quirk_pcie_mch);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7320_MCH, quirk_pcie_mch);
@@ -1319,7 +1538,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7525_MCH, quir
* It's possible for the MSI to get corrupted if shpc and acpi
* are used together on certain PXH-based systems.
*/
-static void __devinit quirk_pcie_pxh(struct pci_dev *dev)
+static void quirk_pcie_pxh(struct pci_dev *dev)
{
pci_msi_off(dev);
dev->no_msi = 1;
@@ -1335,7 +1554,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXHV, quirk_pci
* Some Intel PCI Express chipsets have trouble with downstream
* device power management.
*/
-static void quirk_intel_pcie_pm(struct pci_dev * dev)
+static void quirk_intel_pcie_pm(struct pci_dev *dev)
{
pci_pm_d3_delay = 120;
dev->no_d1d2 = 1;
@@ -1363,16 +1582,170 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2609, quirk_intel_pcie_pm);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260a, quirk_intel_pcie_pm);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260b, quirk_intel_pcie_pm);
+#ifdef CONFIG_X86_IO_APIC
+/*
+ * Boot interrupts on some chipsets cannot be turned off. For these chipsets,
+ * remap the original interrupt in the linux kernel to the boot interrupt, so
+ * that a PCI device's interrupt handler is installed on the boot interrupt
+ * line instead.
+ */
+static void quirk_reroute_to_boot_interrupts_intel(struct pci_dev *dev)
+{
+ if (noioapicquirk || noioapicreroute)
+ return;
+
+ dev->irq_reroute_variant = INTEL_IRQ_REROUTE_VARIANT;
+ dev_info(&dev->dev, "rerouting interrupts for [%04x:%04x]\n",
+ dev->vendor, dev->device);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_1, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_1, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXHV, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_1, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_1, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_1, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXHV, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_1, quirk_reroute_to_boot_interrupts_intel);
+
+/*
+ * On some chipsets we can disable the generation of legacy INTx boot
+ * interrupts.
+ */
+
+/*
+ * IO-APIC1 on 6300ESB generates boot interrupts, see intel order no
+ * 300641-004US, section 5.7.3.
+ */
+#define INTEL_6300_IOAPIC_ABAR 0x40
+#define INTEL_6300_DISABLE_BOOT_IRQ (1<<14)
+
+static void quirk_disable_intel_boot_interrupt(struct pci_dev *dev)
+{
+ u16 pci_config_word;
+
+ if (noioapicquirk)
+ return;
+
+ pci_read_config_word(dev, INTEL_6300_IOAPIC_ABAR, &pci_config_word);
+ pci_config_word |= INTEL_6300_DISABLE_BOOT_IRQ;
+ pci_write_config_word(dev, INTEL_6300_IOAPIC_ABAR, pci_config_word);
+
+ dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n",
+ dev->vendor, dev->device);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_10, quirk_disable_intel_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_10, quirk_disable_intel_boot_interrupt);
+
+/*
+ * disable boot interrupts on HT-1000
+ */
+#define BC_HT1000_FEATURE_REG 0x64
+#define BC_HT1000_PIC_REGS_ENABLE (1<<0)
+#define BC_HT1000_MAP_IDX 0xC00
+#define BC_HT1000_MAP_DATA 0xC01
+
+static void quirk_disable_broadcom_boot_interrupt(struct pci_dev *dev)
+{
+ u32 pci_config_dword;
+ u8 irq;
+
+ if (noioapicquirk)
+ return;
+
+ pci_read_config_dword(dev, BC_HT1000_FEATURE_REG, &pci_config_dword);
+ pci_write_config_dword(dev, BC_HT1000_FEATURE_REG, pci_config_dword |
+ BC_HT1000_PIC_REGS_ENABLE);
+
+ for (irq = 0x10; irq < 0x10 + 32; irq++) {
+ outb(irq, BC_HT1000_MAP_IDX);
+ outb(0x00, BC_HT1000_MAP_DATA);
+ }
+
+ pci_write_config_dword(dev, BC_HT1000_FEATURE_REG, pci_config_dword);
+
+ dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n",
+ dev->vendor, dev->device);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000SB, quirk_disable_broadcom_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000SB, quirk_disable_broadcom_boot_interrupt);
+
+/*
+ * disable boot interrupts on AMD and ATI chipsets
+ */
+/*
+ * NOIOAMODE needs to be disabled to disable "boot interrupts". For AMD 8131
+ * rev. A0 and B0, NOIOAMODE needs to be disabled anyway to fix IO-APIC mode
+ * (due to an erratum).
+ */
+#define AMD_813X_MISC 0x40
+#define AMD_813X_NOIOAMODE (1<<0)
+#define AMD_813X_REV_B1 0x12
+#define AMD_813X_REV_B2 0x13
+
+static void quirk_disable_amd_813x_boot_interrupt(struct pci_dev *dev)
+{
+ u32 pci_config_dword;
+
+ if (noioapicquirk)
+ return;
+ if ((dev->revision == AMD_813X_REV_B1) ||
+ (dev->revision == AMD_813X_REV_B2))
+ return;
+
+ pci_read_config_dword(dev, AMD_813X_MISC, &pci_config_dword);
+ pci_config_dword &= ~AMD_813X_NOIOAMODE;
+ pci_write_config_dword(dev, AMD_813X_MISC, pci_config_dword);
+
+ dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n",
+ dev->vendor, dev->device);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_disable_amd_813x_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_disable_amd_813x_boot_interrupt);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8132_BRIDGE, quirk_disable_amd_813x_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8132_BRIDGE, quirk_disable_amd_813x_boot_interrupt);
+
+#define AMD_8111_PCI_IRQ_ROUTING 0x56
+
+static void quirk_disable_amd_8111_boot_interrupt(struct pci_dev *dev)
+{
+ u16 pci_config_word;
+
+ if (noioapicquirk)
+ return;
+
+ pci_read_config_word(dev, AMD_8111_PCI_IRQ_ROUTING, &pci_config_word);
+ if (!pci_config_word) {
+ dev_info(&dev->dev, "boot interrupts on device [%04x:%04x] already disabled\n",
+ dev->vendor, dev->device);
+ return;
+ }
+ pci_write_config_word(dev, AMD_8111_PCI_IRQ_ROUTING, 0);
+ dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n",
+ dev->vendor, dev->device);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS, quirk_disable_amd_8111_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS, quirk_disable_amd_8111_boot_interrupt);
+#endif /* CONFIG_X86_IO_APIC */
+
/*
* Toshiba TC86C001 IDE controller reports the standard 8-byte BAR0 size
* but the PIO transfers won't work if BAR0 falls at the odd 8 bytes.
* Re-allocate the region if needed...
*/
-static void __init quirk_tc86c001_ide(struct pci_dev *dev)
+static void quirk_tc86c001_ide(struct pci_dev *dev)
{
struct resource *r = &dev->resource[0];
if (r->start & 0x8) {
+ r->flags |= IORESOURCE_UNSET;
r->start = 0;
r->end = 0xf;
}
@@ -1381,7 +1754,46 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA_2,
PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE,
quirk_tc86c001_ide);
-static void __devinit quirk_netmos(struct pci_dev *dev)
+/*
+ * PLX PCI 9050 PCI Target bridge controller has an errata that prevents the
+ * local configuration registers accessible via BAR0 (memory) or BAR1 (i/o)
+ * being read correctly if bit 7 of the base address is set.
+ * The BAR0 or BAR1 region may be disabled (size 0) or enabled (size 128).
+ * Re-allocate the regions to a 256-byte boundary if necessary.
+ */
+static void quirk_plx_pci9050(struct pci_dev *dev)
+{
+ unsigned int bar;
+
+ /* Fixed in revision 2 (PCI 9052). */
+ if (dev->revision >= 2)
+ return;
+ for (bar = 0; bar <= 1; bar++)
+ if (pci_resource_len(dev, bar) == 0x80 &&
+ (pci_resource_start(dev, bar) & 0x80)) {
+ struct resource *r = &dev->resource[bar];
+ dev_info(&dev->dev, "Re-allocating PLX PCI 9050 BAR %u to length 256 to avoid bit 7 bug\n",
+ bar);
+ r->flags |= IORESOURCE_UNSET;
+ r->start = 0;
+ r->end = 0xff;
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+ quirk_plx_pci9050);
+/*
+ * The following Meilhaus (vendor ID 0x1402) device IDs (amongst others)
+ * may be using the PLX PCI 9050: 0x0630, 0x0940, 0x0950, 0x0960, 0x100b,
+ * 0x1400, 0x140a, 0x140b, 0x14e0, 0x14ea, 0x14eb, 0x1604, 0x1608, 0x160c,
+ * 0x168f, 0x2000, 0x2600, 0x3000, 0x810a, 0x810b.
+ *
+ * Currently, device IDs 0x2000 and 0x2600 are used by the Comedi "me_daq"
+ * driver.
+ */
+DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2000, quirk_plx_pci9050);
+DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2600, quirk_plx_pci9050);
+
+static void quirk_netmos(struct pci_dev *dev)
{
unsigned int num_parallel = (dev->subsystem_device & 0xf0) >> 4;
unsigned int num_serial = dev->subsystem_device & 0xf;
@@ -1397,30 +1809,31 @@ static void __devinit quirk_netmos(struct pci_dev *dev)
* of parallel ports and <S> is the number of serial ports.
*/
switch (dev->device) {
+ case PCI_DEVICE_ID_NETMOS_9835:
+ /* Well, this rule doesn't hold for the following 9835 device */
+ if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
+ dev->subsystem_device == 0x0299)
+ return;
case PCI_DEVICE_ID_NETMOS_9735:
case PCI_DEVICE_ID_NETMOS_9745:
- case PCI_DEVICE_ID_NETMOS_9835:
case PCI_DEVICE_ID_NETMOS_9845:
case PCI_DEVICE_ID_NETMOS_9855:
- if ((dev->class >> 8) == PCI_CLASS_COMMUNICATION_SERIAL &&
- num_parallel) {
- dev_info(&dev->dev, "Netmos %04x (%u parallel, "
- "%u serial); changing class SERIAL to OTHER "
- "(use parport_serial)\n",
+ if (num_parallel) {
+ dev_info(&dev->dev, "Netmos %04x (%u parallel, %u serial); changing class SERIAL to OTHER (use parport_serial)\n",
dev->device, num_parallel, num_serial);
dev->class = (PCI_CLASS_COMMUNICATION_OTHER << 8) |
(dev->class & 0xff);
}
}
}
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, quirk_netmos);
+DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID,
+ PCI_CLASS_COMMUNICATION_SERIAL, 8, quirk_netmos);
-static void __devinit quirk_e100_interrupt(struct pci_dev *dev)
+static void quirk_e100_interrupt(struct pci_dev *dev)
{
u16 command, pmcsr;
u8 __iomem *csr;
u8 cmd_hi;
- int pm;
switch (dev->device) {
/* PCI IDs taken from drivers/net/e100.c */
@@ -1458,9 +1871,8 @@ static void __devinit quirk_e100_interrupt(struct pci_dev *dev)
* Check that the device is in the D0 power state. If it's not,
* there is no point to look any further.
*/
- pm = pci_find_capability(dev, PCI_CAP_ID_PM);
- if (pm) {
- pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
+ if (dev->pm_cap) {
+ pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
if ((pmcsr & PCI_PM_CTRL_STATE_MASK) != PCI_D0)
return;
}
@@ -1474,16 +1886,40 @@ static void __devinit quirk_e100_interrupt(struct pci_dev *dev)
cmd_hi = readb(csr + 3);
if (cmd_hi == 0) {
- dev_warn(&dev->dev, "Firmware left e100 interrupts enabled; "
- "disabling\n");
+ dev_warn(&dev->dev, "Firmware left e100 interrupts enabled; disabling\n");
writeb(1, csr + 3);
}
iounmap(csr);
}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_e100_interrupt);
+DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+ PCI_CLASS_NETWORK_ETHERNET, 8, quirk_e100_interrupt);
-static void __devinit fixup_rev1_53c810(struct pci_dev* dev)
+/*
+ * The 82575 and 82598 may experience data corruption issues when transitioning
+ * out of L0S. To prevent this we need to disable L0S on the pci-e link
+ */
+static void quirk_disable_aspm_l0s(struct pci_dev *dev)
+{
+ dev_info(&dev->dev, "Disabling L0s\n");
+ pci_disable_link_state(dev, PCIE_LINK_STATE_L0S);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10a7, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10a9, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10b6, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10c6, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10c7, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10c8, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10d6, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10db, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10dd, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10e1, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10ec, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10f1, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10f4, quirk_disable_aspm_l0s);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1508, quirk_disable_aspm_l0s);
+
+static void fixup_rev1_53c810(struct pci_dev *dev)
{
/* rev 1 ncr53c810 chips don't set the class at all which means
* they don't get their resources remapped. Fix that here.
@@ -1496,123 +1932,20 @@ static void __devinit fixup_rev1_53c810(struct pci_dev* dev)
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, fixup_rev1_53c810);
-static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end)
-{
- while (f < end) {
- if ((f->vendor == dev->vendor || f->vendor == (u16) PCI_ANY_ID) &&
- (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) {
-#ifdef DEBUG
- dev_dbg(&dev->dev, "calling quirk 0x%p", f->hook);
- print_fn_descriptor_symbol(": %s()\n",
- (unsigned long) f->hook);
-#endif
- f->hook(dev);
- }
- f++;
- }
-}
-
-extern struct pci_fixup __start_pci_fixups_early[];
-extern struct pci_fixup __end_pci_fixups_early[];
-extern struct pci_fixup __start_pci_fixups_header[];
-extern struct pci_fixup __end_pci_fixups_header[];
-extern struct pci_fixup __start_pci_fixups_final[];
-extern struct pci_fixup __end_pci_fixups_final[];
-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[];
-
-
-void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
-{
- struct pci_fixup *start, *end;
-
- switch(pass) {
- case pci_fixup_early:
- start = __start_pci_fixups_early;
- end = __end_pci_fixups_early;
- break;
-
- case pci_fixup_header:
- start = __start_pci_fixups_header;
- end = __end_pci_fixups_header;
- break;
-
- case pci_fixup_final:
- start = __start_pci_fixups_final;
- end = __end_pci_fixups_final;
- break;
-
- case pci_fixup_enable:
- start = __start_pci_fixups_enable;
- end = __end_pci_fixups_enable;
- break;
-
- case pci_fixup_resume:
- start = __start_pci_fixups_resume;
- end = __end_pci_fixups_resume;
- break;
-
- default:
- /* stupid compiler warning, you would think with an enum... */
- return;
- }
- pci_do_fixups(dev, start, end);
-}
-EXPORT_SYMBOL(pci_fixup_device);
-
/* Enable 1k I/O space granularity on the Intel P64H2 */
-static void __devinit quirk_p64h2_1k_io(struct pci_dev *dev)
+static void quirk_p64h2_1k_io(struct pci_dev *dev)
{
u16 en1k;
- u8 io_base_lo, io_limit_lo;
- unsigned long base, limit;
- struct resource *res = dev->resource + PCI_BRIDGE_RESOURCES;
pci_read_config_word(dev, 0x40, &en1k);
if (en1k & 0x200) {
dev_info(&dev->dev, "Enable I/O Space to 1KB granularity\n");
-
- pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);
- pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo);
- base = (io_base_lo & (PCI_IO_RANGE_MASK | 0x0c)) << 8;
- limit = (io_limit_lo & (PCI_IO_RANGE_MASK | 0x0c)) << 8;
-
- if (base <= limit) {
- res->start = base;
- res->end = limit + 0x3ff;
- }
+ dev->io_window_1k = 1;
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1460, quirk_p64h2_1k_io);
-/* Fix the IOBL_ADR for 1k I/O space granularity on the Intel P64H2
- * The IOBL_ADR gets re-written to 4k boundaries in pci_setup_bridge()
- * in drivers/pci/setup-bus.c
- */
-static void __devinit quirk_p64h2_1k_io_fix_iobl(struct pci_dev *dev)
-{
- u16 en1k, iobl_adr, iobl_adr_1k;
- struct resource *res = dev->resource + PCI_BRIDGE_RESOURCES;
-
- pci_read_config_word(dev, 0x40, &en1k);
-
- if (en1k & 0x200) {
- pci_read_config_word(dev, PCI_IO_BASE, &iobl_adr);
-
- iobl_adr_1k = iobl_adr | (res->start >> 8) | (res->end & 0xfc00);
-
- if (iobl_adr != iobl_adr_1k) {
- dev_info(&dev->dev, "Fixing P64H2 IOBL_ADR from 0x%x to 0x%x for 1KB granularity\n",
- iobl_adr,iobl_adr_1k);
- pci_write_config_word(dev, PCI_IO_BASE, iobl_adr_1k);
- }
- }
-}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1460, quirk_p64h2_1k_io_fix_iobl);
-
/* Under some circumstances, AER is not linked with extended capabilities.
* Force it to be linked by setting the corresponding control bit in the
* config space.
@@ -1623,51 +1956,178 @@ static void quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev)
if (pci_read_config_byte(dev, 0xf41, &b) == 0) {
if (!(b & 0x20)) {
pci_write_config_byte(dev, 0xf41, b | 0x20);
- dev_info(&dev->dev,
- "Linking AER extended capability\n");
+ dev_info(&dev->dev, "Linking AER extended capability\n");
}
}
}
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)
+static void quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
{
/*
* Disable PCI Bus Parking and PCI Master read caching on CX700
* which causes unspecified timing errors with a VT6212L on the PCI
- * bus leading to USB2.0 packet loss. The defaults are that these
- * features are turned off but some BIOSes turn them on.
+ * bus leading to USB2.0 packet loss.
+ *
+ * This quirk is only enabled if a second (on the external PCI bus)
+ * VT6212L is found -- the CX700 core itself also contains a USB
+ * host controller with the same PCI ID as the VT6212L.
*/
+ /* Count VT6212L instances */
+ struct pci_dev *p = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_8235_USB_2, NULL);
uint8_t b;
+
+ /* p should contain the first (internal) VT6212L -- see if we have
+ an external one by searching again */
+ p = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235_USB_2, p);
+ if (!p)
+ return;
+ pci_dev_put(p);
+
if (pci_read_config_byte(dev, 0x76, &b) == 0) {
if (b & 0x40) {
/* Turn off PCI Bus Parking */
pci_write_config_byte(dev, 0x76, b ^ 0x40);
+ dev_info(&dev->dev, "Disabling VIA CX700 PCI parking\n");
+ }
+ }
+
+ if (pci_read_config_byte(dev, 0x72, &b) == 0) {
+ if (b != 0) {
/* Turn off PCI Master read caching */
pci_write_config_byte(dev, 0x72, 0x0);
+
+ /* Set PCI Master Bus time-out to "1x16 PCLK" */
pci_write_config_byte(dev, 0x75, 0x1);
+
+ /* Disable "Read FIFO Timer" */
pci_write_config_byte(dev, 0x77, 0x0);
- dev_info(&dev->dev,
- "Disabling VIA CX700 PCI parking/caching\n");
+ dev_info(&dev->dev, "Disabling VIA CX700 PCI caching\n");
}
}
}
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching);
+DECLARE_PCI_FIXUP_FINAL(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 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_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5706,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5706S,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5708,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5708S,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5709,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5709S,
+ quirk_brcm_570x_limit_vpd);
+
+static void quirk_brcm_5719_limit_mrrs(struct pci_dev *dev)
+{
+ u32 rev;
+
+ pci_read_config_dword(dev, 0xf4, &rev);
+
+ /* Only CAP the MRRS if the device is a 5719 A0 */
+ if (rev == 0x05719000) {
+ int readrq = pcie_get_readrq(dev);
+ if (readrq > 2048)
+ pcie_set_readrq(dev, 2048);
+ }
+}
+
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_TIGON3_5719,
+ quirk_brcm_5719_limit_mrrs);
+
+/* Originally in EDAC sources for i82875P:
+ * Intel tells BIOS developers to hide device 6 which
+ * configures the overflow device access containing
+ * the DRBs - this is where we expose device 6.
+ * http://www.x86-secret.com/articles/tweak/pat/patsecrets-2.htm
+ */
+static void quirk_unhide_mch_dev6(struct pci_dev *dev)
+{
+ u8 reg;
+
+ if (pci_read_config_byte(dev, 0xF4, &reg) == 0 && !(reg & 0x02)) {
+ dev_info(&dev->dev, "Enabling MCH 'Overflow' Device\n");
+ pci_write_config_byte(dev, 0xF4, reg | 0x02);
+ }
+}
+
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82865_HB,
+ quirk_unhide_mch_dev6);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82875_HB,
+ quirk_unhide_mch_dev6);
+
+#ifdef CONFIG_TILEPRO
+/*
+ * The Tilera TILEmpower tilepro platform needs to set the link speed
+ * to 2.5GT(Giga-Transfers)/s (Gen 1). The default link speed
+ * setting is 5GT/s (Gen 2). 0x98 is the Link Control2 PCIe
+ * capability register of the PEX8624 PCIe switch. The switch
+ * supports link speed auto negotiation, but falsely sets
+ * the link speed to 5GT/s.
+ */
+static void quirk_tile_plx_gen1(struct pci_dev *dev)
+{
+ if (tile_plx_gen1) {
+ pci_write_config_dword(dev, 0x98, 0x1);
+ mdelay(50);
+ }
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8624, quirk_tile_plx_gen1);
+#endif /* CONFIG_TILEPRO */
#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
- * some other busses controlled by the chipset even if Linux is not
- * aware of it. Instead of setting the flag on all busses in the
+ * some other buses controlled by the chipset even if Linux is not
+ * aware of it. Instead of setting the flag on all buses in the
* machine, simply disable MSI globally.
*/
-static void __init quirk_disable_all_msi(struct pci_dev *dev)
+static void quirk_disable_all_msi(struct pci_dev *dev)
{
pci_no_msi();
dev_warn(&dev->dev, "MSI quirk detected; MSI disabled\n");
@@ -1675,22 +2135,46 @@ 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);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3364, quirk_disable_all_msi);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8380_0, quirk_disable_all_msi);
/* Disable MSI on chipsets that are known to not support it */
-static void __devinit quirk_disable_msi(struct pci_dev *dev)
+static void quirk_disable_msi(struct pci_dev *dev)
{
if (dev->subordinate) {
- dev_warn(&dev->dev, "MSI quirk detected; "
- "subordinate MSI disabled\n");
+ dev_warn(&dev->dev, "MSI quirk detected; subordinate MSI disabled\n");
dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_disable_msi);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0xa238, quirk_disable_msi);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x5a3f, quirk_disable_msi);
+
+/*
+ * The APC bridge device in AMD 780 family northbridges has some random
+ * OEM subsystem ID in its vendor ID register (erratum 18), so instead
+ * we use the possible vendor/device IDs of the host bridge for the
+ * declared quirk, and search for the APC bridge by slot number.
+ */
+static void quirk_amd_780_apc_msi(struct pci_dev *host_bridge)
+{
+ struct pci_dev *apc_bridge;
+
+ apc_bridge = pci_get_slot(host_bridge->bus, PCI_DEVFN(1, 0));
+ if (apc_bridge) {
+ if (apc_bridge->device == 0x9602)
+ quirk_disable_msi(apc_bridge);
+ pci_dev_put(apc_bridge);
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x9600, quirk_amd_780_apc_msi);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x9601, quirk_amd_780_apc_msi);
/* Go through the list of Hypertransport capabilities and
* return 1 if a HT MSI capability is found and enabled */
-static int __devinit msi_ht_cap_enabled(struct pci_dev *dev)
+static int msi_ht_cap_enabled(struct pci_dev *dev)
{
int pos, ttl = 48;
@@ -1699,8 +2183,7 @@ static int __devinit msi_ht_cap_enabled(struct pci_dev *dev)
u8 flags;
if (pci_read_config_byte(dev, pos + HT_MSI_FLAGS,
- &flags) == 0)
- {
+ &flags) == 0) {
dev_info(&dev->dev, "Found %s HT MSI Mapping\n",
flags & HT_MSI_FLAGS_ENABLE ?
"enabled" : "disabled");
@@ -1714,22 +2197,20 @@ static int __devinit msi_ht_cap_enabled(struct pci_dev *dev)
}
/* Check the hypertransport MSI mapping to know whether MSI is enabled or not */
-static void __devinit quirk_msi_ht_cap(struct pci_dev *dev)
+static void quirk_msi_ht_cap(struct pci_dev *dev)
{
if (dev->subordinate && !msi_ht_cap_enabled(dev)) {
- dev_warn(&dev->dev, "MSI quirk detected; "
- "subordinate MSI disabled\n");
+ dev_warn(&dev->dev, "MSI quirk detected; subordinate MSI disabled\n");
dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE,
quirk_msi_ht_cap);
-
/* The nVidia CK804 chipset may have 2 HT MSI mappings.
* MSI are supported if the MSI capability set in any of these mappings.
*/
-static void __devinit quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev)
+static void quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev)
{
struct pci_dev *pdev;
@@ -1743,8 +2224,7 @@ static void __devinit quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev)
if (!pdev)
return;
if (!msi_ht_cap_enabled(dev) && !msi_ht_cap_enabled(pdev)) {
- dev_warn(&dev->dev, "MSI quirk detected; "
- "subordinate MSI disabled\n");
+ dev_warn(&dev->dev, "MSI quirk detected; subordinate MSI disabled\n");
dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
}
pci_dev_put(pdev);
@@ -1753,7 +2233,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
quirk_nvidia_ck804_msi_ht_cap);
/* Force enable MSI mapping capability on HT bridges */
-static void __devinit ht_enable_msi_mapping(struct pci_dev *dev)
+static void ht_enable_msi_mapping(struct pci_dev *dev)
{
int pos, ttl = 48;
@@ -1776,51 +2256,278 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SERVERWORKS,
PCI_DEVICE_ID_SERVERWORKS_HT1000_PXB,
ht_enable_msi_mapping);
-static void __devinit nv_msi_ht_cap_quirk(struct pci_dev *dev)
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8132_BRIDGE,
+ ht_enable_msi_mapping);
+
+/* The P5N32-SLI motherboards from Asus have a problem with msi
+ * for the MCP55 NIC. It is not yet determined whether the msi problem
+ * also affects other devices. As for now, turn off msi for this device.
+ */
+static void nvenet_msi_disable(struct pci_dev *dev)
+{
+ const char *board_name = dmi_get_system_info(DMI_BOARD_NAME);
+
+ if (board_name &&
+ (strstr(board_name, "P5N32-SLI PREMIUM") ||
+ strstr(board_name, "P5N32-E SLI"))) {
+ dev_info(&dev->dev, "Disabling msi for MCP55 NIC on P5N32-SLI\n");
+ dev->no_msi = 1;
+ }
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA,
+ PCI_DEVICE_ID_NVIDIA_NVENET_15,
+ nvenet_msi_disable);
+
+/*
+ * Some versions of the MCP55 bridge from Nvidia have a legacy IRQ routing
+ * config register. This register controls the routing of legacy
+ * interrupts from devices that route through the MCP55. If this register
+ * is misprogrammed, interrupts are only sent to the BSP, unlike
+ * conventional systems where the IRQ is broadcast to all online CPUs. Not
+ * having this register set properly prevents kdump from booting up
+ * properly, so let's make sure that we have it set correctly.
+ * Note that this is an undocumented register.
+ */
+static void nvbridge_check_legacy_irq_routing(struct pci_dev *dev)
+{
+ u32 cfg;
+
+ if (!pci_find_capability(dev, PCI_CAP_ID_HT))
+ return;
+
+ pci_read_config_dword(dev, 0x74, &cfg);
+
+ if (cfg & ((1 << 2) | (1 << 15))) {
+ printk(KERN_INFO "Rewriting irq routing register on MCP55\n");
+ cfg &= ~((1 << 2) | (1 << 15));
+ pci_write_config_dword(dev, 0x74, cfg);
+ }
+}
+
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA,
+ PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V0,
+ nvbridge_check_legacy_irq_routing);
+
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA,
+ PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V4,
+ nvbridge_check_legacy_irq_routing);
+
+static int ht_check_msi_mapping(struct pci_dev *dev)
+{
+ int pos, ttl = 48;
+ int found = 0;
+
+ /* check if there is HT MSI cap or enabled on this device */
+ pos = pci_find_ht_capability(dev, HT_CAPTYPE_MSI_MAPPING);
+ while (pos && ttl--) {
+ u8 flags;
+
+ if (found < 1)
+ found = 1;
+ if (pci_read_config_byte(dev, pos + HT_MSI_FLAGS,
+ &flags) == 0) {
+ if (flags & HT_MSI_FLAGS_ENABLE) {
+ if (found < 2) {
+ found = 2;
+ break;
+ }
+ }
+ }
+ pos = pci_find_next_ht_capability(dev, pos,
+ HT_CAPTYPE_MSI_MAPPING);
+ }
+
+ return found;
+}
+
+static int host_bridge_with_leaf(struct pci_dev *host_bridge)
+{
+ struct pci_dev *dev;
+ int pos;
+ int i, dev_no;
+ int found = 0;
+
+ dev_no = host_bridge->devfn >> 3;
+ for (i = dev_no + 1; i < 0x20; i++) {
+ dev = pci_get_slot(host_bridge->bus, PCI_DEVFN(i, 0));
+ if (!dev)
+ continue;
+
+ /* found next host bridge ?*/
+ pos = pci_find_ht_capability(dev, HT_CAPTYPE_SLAVE);
+ if (pos != 0) {
+ pci_dev_put(dev);
+ break;
+ }
+
+ if (ht_check_msi_mapping(dev)) {
+ found = 1;
+ pci_dev_put(dev);
+ break;
+ }
+ pci_dev_put(dev);
+ }
+
+ return found;
+}
+
+#define PCI_HT_CAP_SLAVE_CTRL0 4 /* link control */
+#define PCI_HT_CAP_SLAVE_CTRL1 8 /* link control to */
+
+static int is_end_of_ht_chain(struct pci_dev *dev)
+{
+ int pos, ctrl_off;
+ int end = 0;
+ u16 flags, ctrl;
+
+ pos = pci_find_ht_capability(dev, HT_CAPTYPE_SLAVE);
+
+ if (!pos)
+ goto out;
+
+ pci_read_config_word(dev, pos + PCI_CAP_FLAGS, &flags);
+
+ ctrl_off = ((flags >> 10) & 1) ?
+ PCI_HT_CAP_SLAVE_CTRL0 : PCI_HT_CAP_SLAVE_CTRL1;
+ pci_read_config_word(dev, pos + ctrl_off, &ctrl);
+
+ if (ctrl & (1 << 6))
+ end = 1;
+
+out:
+ return end;
+}
+
+static void nv_ht_enable_msi_mapping(struct pci_dev *dev)
{
struct pci_dev *host_bridge;
+ int pos;
+ int i, dev_no;
+ int found = 0;
+
+ dev_no = dev->devfn >> 3;
+ for (i = dev_no; i >= 0; i--) {
+ host_bridge = pci_get_slot(dev->bus, PCI_DEVFN(i, 0));
+ if (!host_bridge)
+ continue;
+
+ pos = pci_find_ht_capability(host_bridge, HT_CAPTYPE_SLAVE);
+ if (pos != 0) {
+ found = 1;
+ break;
+ }
+ pci_dev_put(host_bridge);
+ }
+
+ if (!found)
+ return;
+
+ /* don't enable end_device/host_bridge with leaf directly here */
+ if (host_bridge == dev && is_end_of_ht_chain(host_bridge) &&
+ host_bridge_with_leaf(host_bridge))
+ goto out;
+
+ /* root did that ! */
+ if (msi_ht_cap_enabled(host_bridge))
+ goto out;
+
+ ht_enable_msi_mapping(dev);
+
+out:
+ pci_dev_put(host_bridge);
+}
+
+static void ht_disable_msi_mapping(struct pci_dev *dev)
+{
int pos, ttl = 48;
+ pos = pci_find_ht_capability(dev, HT_CAPTYPE_MSI_MAPPING);
+ while (pos && ttl--) {
+ u8 flags;
+
+ if (pci_read_config_byte(dev, pos + HT_MSI_FLAGS,
+ &flags) == 0) {
+ dev_info(&dev->dev, "Disabling HT MSI Mapping\n");
+
+ pci_write_config_byte(dev, pos + HT_MSI_FLAGS,
+ flags & ~HT_MSI_FLAGS_ENABLE);
+ }
+ pos = pci_find_next_ht_capability(dev, pos,
+ HT_CAPTYPE_MSI_MAPPING);
+ }
+}
+
+static void __nv_msi_ht_cap_quirk(struct pci_dev *dev, int all)
+{
+ struct pci_dev *host_bridge;
+ int pos;
+ int found;
+
+ if (!pci_msi_enabled())
+ return;
+
+ /* check if there is HT MSI cap or enabled on this device */
+ found = ht_check_msi_mapping(dev);
+
+ /* no HT MSI CAP */
+ if (found == 0)
+ return;
+
/*
* HT MSI mapping should be disabled on devices that are below
* a non-Hypertransport host bridge. Locate the host bridge...
*/
host_bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
if (host_bridge == NULL) {
- dev_warn(&dev->dev,
- "nv_msi_ht_cap_quirk didn't locate host bridge\n");
+ dev_warn(&dev->dev, "nv_msi_ht_cap_quirk didn't locate host bridge\n");
return;
}
pos = pci_find_ht_capability(host_bridge, HT_CAPTYPE_SLAVE);
if (pos != 0) {
/* Host bridge is to HT */
- ht_enable_msi_mapping(dev);
- return;
+ if (found == 1) {
+ /* it is not enabled, try to enable it */
+ if (all)
+ ht_enable_msi_mapping(dev);
+ else
+ nv_ht_enable_msi_mapping(dev);
+ }
+ goto out;
}
+ /* HT MSI is not enabled */
+ if (found == 1)
+ goto out;
+
/* Host bridge is not to HT, disable HT MSI mapping on this device */
- pos = pci_find_ht_capability(dev, HT_CAPTYPE_MSI_MAPPING);
- while (pos && ttl--) {
- u8 flags;
+ ht_disable_msi_mapping(dev);
- if (pci_read_config_byte(dev, pos + HT_MSI_FLAGS,
- &flags) == 0) {
- dev_info(&dev->dev, "Disabling HT MSI mapping");
- pci_write_config_byte(dev, pos + HT_MSI_FLAGS,
- flags & ~HT_MSI_FLAGS_ENABLE);
- }
- pos = pci_find_next_ht_capability(dev, pos,
- HT_CAPTYPE_MSI_MAPPING);
- }
+out:
+ pci_dev_put(host_bridge);
}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_quirk);
-static void __devinit quirk_msi_intx_disable_bug(struct pci_dev *dev)
+static void nv_msi_ht_cap_quirk_all(struct pci_dev *dev)
+{
+ return __nv_msi_ht_cap_quirk(dev, 1);
+}
+
+static void nv_msi_ht_cap_quirk_leaf(struct pci_dev *dev)
+{
+ return __nv_msi_ht_cap_quirk(dev, 0);
+}
+
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_quirk_leaf);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_quirk_leaf);
+
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk_all);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk_all);
+
+static void quirk_msi_intx_disable_bug(struct pci_dev *dev)
{
dev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG;
}
-static void __devinit quirk_msi_intx_disable_ati_bug(struct pci_dev *dev)
+static void quirk_msi_intx_disable_ati_bug(struct pci_dev *dev)
{
struct pci_dev *p;
@@ -1837,6 +2544,14 @@ static void __devinit quirk_msi_intx_disable_ati_bug(struct pci_dev *dev)
dev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG;
pci_dev_put(p);
}
+static void quirk_msi_intx_disable_qca_bug(struct pci_dev *dev)
+{
+ /* AR816X/AR817X/E210X MSI is fixed at HW level from revision 0x18 */
+ if (dev->revision < 0x18) {
+ dev_info(&dev->dev, "set MSI_INTX_DISABLE_BUG flag\n");
+ dev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG;
+ }
+}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
PCI_DEVICE_ID_TIGON3_5780,
quirk_msi_intx_disable_bug);
@@ -1874,4 +2589,1140 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4374,
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4375,
quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x1062,
+ quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x1063,
+ quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x2060,
+ quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x2062,
+ quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x1073,
+ quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x1083,
+ quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x1090,
+ quirk_msi_intx_disable_qca_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x1091,
+ quirk_msi_intx_disable_qca_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x10a0,
+ quirk_msi_intx_disable_qca_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0x10a1,
+ quirk_msi_intx_disable_qca_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0xe091,
+ quirk_msi_intx_disable_qca_bug);
#endif /* CONFIG_PCI_MSI */
+
+/* Allow manual resource allocation for PCI hotplug bridges
+ * via pci=hpmemsize=nnM and pci=hpiosize=nnM parameters. For
+ * some PCI-PCI hotplug bridges, like PLX 6254 (former HINT HB6),
+ * kernel fails to allocate resources when hotplug device is
+ * inserted and PCI bus is rescanned.
+ */
+static void quirk_hotplug_bridge(struct pci_dev *dev)
+{
+ dev->is_hotplug_bridge = 1;
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_HINT, 0x0020, quirk_hotplug_bridge);
+
+/*
+ * This is a quirk for the Ricoh MMC controller found as a part of
+ * some mulifunction chips.
+
+ * This is very similar and based on the ricoh_mmc driver written by
+ * Philip Langdale. Thank you for these magic sequences.
+ *
+ * These chips implement the four main memory card controllers (SD, MMC, MS, xD)
+ * and one or both of cardbus or firewire.
+ *
+ * It happens that they implement SD and MMC
+ * support as separate controllers (and PCI functions). The linux SDHCI
+ * driver supports MMC cards but the chip detects MMC cards in hardware
+ * and directs them to the MMC controller - so the SDHCI driver never sees
+ * them.
+ *
+ * To get around this, we must disable the useless MMC controller.
+ * At that point, the SDHCI controller will start seeing them
+ * It seems to be the case that the relevant PCI registers to deactivate the
+ * MMC controller live on PCI function 0, which might be the cardbus controller
+ * or the firewire controller, depending on the particular chip in question
+ *
+ * This has to be done early, because as soon as we disable the MMC controller
+ * other pci functions shift up one level, e.g. function #2 becomes function
+ * #1, and this will confuse the pci core.
+ */
+
+#ifdef CONFIG_MMC_RICOH_MMC
+static void ricoh_mmc_fixup_rl5c476(struct pci_dev *dev)
+{
+ /* disable via cardbus interface */
+ u8 write_enable;
+ u8 write_target;
+ u8 disable;
+
+ /* disable must be done via function #0 */
+ if (PCI_FUNC(dev->devfn))
+ return;
+
+ pci_read_config_byte(dev, 0xB7, &disable);
+ if (disable & 0x02)
+ return;
+
+ pci_read_config_byte(dev, 0x8E, &write_enable);
+ pci_write_config_byte(dev, 0x8E, 0xAA);
+ pci_read_config_byte(dev, 0x8D, &write_target);
+ pci_write_config_byte(dev, 0x8D, 0xB7);
+ pci_write_config_byte(dev, 0xB7, disable | 0x02);
+ pci_write_config_byte(dev, 0x8E, write_enable);
+ pci_write_config_byte(dev, 0x8D, write_target);
+
+ dev_notice(&dev->dev, "proprietary Ricoh MMC controller disabled (via cardbus function)\n");
+ dev_notice(&dev->dev, "MMC cards are now supported by standard SDHCI controller\n");
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476, ricoh_mmc_fixup_rl5c476);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476, ricoh_mmc_fixup_rl5c476);
+
+static void ricoh_mmc_fixup_r5c832(struct pci_dev *dev)
+{
+ /* disable via firewire interface */
+ u8 write_enable;
+ u8 disable;
+
+ /* disable must be done via function #0 */
+ if (PCI_FUNC(dev->devfn))
+ return;
+ /*
+ * RICOH 0xe822 and 0xe823 SD/MMC card readers fail to recognize
+ * certain types of SD/MMC cards. Lowering the SD base
+ * clock frequency from 200Mhz to 50Mhz fixes this issue.
+ *
+ * 0x150 - SD2.0 mode enable for changing base clock
+ * frequency to 50Mhz
+ * 0xe1 - Base clock frequency
+ * 0x32 - 50Mhz new clock frequency
+ * 0xf9 - Key register for 0x150
+ * 0xfc - key register for 0xe1
+ */
+ if (dev->device == PCI_DEVICE_ID_RICOH_R5CE822 ||
+ dev->device == PCI_DEVICE_ID_RICOH_R5CE823) {
+ pci_write_config_byte(dev, 0xf9, 0xfc);
+ pci_write_config_byte(dev, 0x150, 0x10);
+ pci_write_config_byte(dev, 0xf9, 0x00);
+ pci_write_config_byte(dev, 0xfc, 0x01);
+ pci_write_config_byte(dev, 0xe1, 0x32);
+ pci_write_config_byte(dev, 0xfc, 0x00);
+
+ dev_notice(&dev->dev, "MMC controller base frequency changed to 50Mhz.\n");
+ }
+
+ pci_read_config_byte(dev, 0xCB, &disable);
+
+ if (disable & 0x02)
+ return;
+
+ pci_read_config_byte(dev, 0xCA, &write_enable);
+ pci_write_config_byte(dev, 0xCA, 0x57);
+ pci_write_config_byte(dev, 0xCB, disable | 0x02);
+ pci_write_config_byte(dev, 0xCA, write_enable);
+
+ dev_notice(&dev->dev, "proprietary Ricoh MMC controller disabled (via firewire function)\n");
+ dev_notice(&dev->dev, "MMC cards are now supported by standard SDHCI controller\n");
+
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, ricoh_mmc_fixup_r5c832);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, ricoh_mmc_fixup_r5c832);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5CE822, ricoh_mmc_fixup_r5c832);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5CE822, ricoh_mmc_fixup_r5c832);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5CE823, ricoh_mmc_fixup_r5c832);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5CE823, ricoh_mmc_fixup_r5c832);
+#endif /*CONFIG_MMC_RICOH_MMC*/
+
+#ifdef CONFIG_DMAR_TABLE
+#define VTUNCERRMSK_REG 0x1ac
+#define VTD_MSK_SPEC_ERRORS (1 << 31)
+/*
+ * This is a quirk for masking vt-d spec defined errors to platform error
+ * handling logic. With out this, platforms using Intel 7500, 5500 chipsets
+ * (and the derivative chipsets like X58 etc) seem to generate NMI/SMI (based
+ * on the RAS config settings of the platform) when a vt-d fault happens.
+ * The resulting SMI caused the system to hang.
+ *
+ * VT-d spec related errors are already handled by the VT-d OS code, so no
+ * need to report the same error through other channels.
+ */
+static void vtd_mask_spec_errors(struct pci_dev *dev)
+{
+ u32 word;
+
+ pci_read_config_dword(dev, VTUNCERRMSK_REG, &word);
+ pci_write_config_dword(dev, VTUNCERRMSK_REG, word | VTD_MSK_SPEC_ERRORS);
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x342e, vtd_mask_spec_errors);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x3c28, vtd_mask_spec_errors);
+#endif
+
+static void fixup_ti816x_class(struct pci_dev *dev)
+{
+ /* TI 816x devices do not have class code set when in PCIe boot mode */
+ dev_info(&dev->dev, "Setting PCI class for 816x PCIe device\n");
+ dev->class = PCI_CLASS_MULTIMEDIA_VIDEO;
+}
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_TI, 0xb800,
+ PCI_CLASS_NOT_DEFINED, 0, fixup_ti816x_class);
+
+/* Some PCIe devices do not work reliably with the claimed maximum
+ * payload size supported.
+ */
+static void fixup_mpss_256(struct pci_dev *dev)
+{
+ dev->pcie_mpss = 1; /* 256 bytes */
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SOLARFLARE,
+ PCI_DEVICE_ID_SOLARFLARE_SFC4000A_0, fixup_mpss_256);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SOLARFLARE,
+ PCI_DEVICE_ID_SOLARFLARE_SFC4000A_1, fixup_mpss_256);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SOLARFLARE,
+ PCI_DEVICE_ID_SOLARFLARE_SFC4000B, fixup_mpss_256);
+
+/* Intel 5000 and 5100 Memory controllers have an errata with read completion
+ * coalescing (which is enabled by default on some BIOSes) and MPS of 256B.
+ * Since there is no way of knowing what the PCIE MPS on each fabric will be
+ * until all of the devices are discovered and buses walked, read completion
+ * coalescing must be disabled. Unfortunately, it cannot be re-enabled because
+ * it is possible to hotplug a device with MPS of 256B.
+ */
+static void quirk_intel_mc_errata(struct pci_dev *dev)
+{
+ int err;
+ u16 rcc;
+
+ if (pcie_bus_config == PCIE_BUS_TUNE_OFF)
+ return;
+
+ /* Intel errata specifies bits to change but does not say what they are.
+ * Keeping them magical until such time as the registers and values can
+ * be explained.
+ */
+ err = pci_read_config_word(dev, 0x48, &rcc);
+ if (err) {
+ dev_err(&dev->dev, "Error attempting to read the read completion coalescing register\n");
+ return;
+ }
+
+ if (!(rcc & (1 << 10)))
+ return;
+
+ rcc &= ~(1 << 10);
+
+ err = pci_write_config_word(dev, 0x48, rcc);
+ if (err) {
+ dev_err(&dev->dev, "Error attempting to write the read completion coalescing register\n");
+ return;
+ }
+
+ pr_info_once("Read completion coalescing disabled due to hardware errata relating to 256B MPS\n");
+}
+/* Intel 5000 series memory controllers and ports 2-7 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25c0, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25d0, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25d4, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25d8, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25e2, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25e3, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25e4, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25e5, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25e6, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25e7, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25f7, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25f8, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25f9, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25fa, quirk_intel_mc_errata);
+/* Intel 5100 series memory controllers and ports 2-7 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65c0, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65e2, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65e3, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65e4, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65e5, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65e6, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65e7, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f7, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f8, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata);
+
+
+/*
+ * Ivytown NTB BAR sizes are misreported by the hardware due to an erratum. To
+ * work around this, query the size it should be configured to by the device and
+ * modify the resource end to correspond to this new size.
+ */
+static void quirk_intel_ntb(struct pci_dev *dev)
+{
+ int rc;
+ u8 val;
+
+ rc = pci_read_config_byte(dev, 0x00D0, &val);
+ if (rc)
+ return;
+
+ dev->resource[2].end = dev->resource[2].start + ((u64) 1 << val) - 1;
+
+ rc = pci_read_config_byte(dev, 0x00D1, &val);
+ if (rc)
+ return;
+
+ dev->resource[4].end = dev->resource[4].start + ((u64) 1 << val) - 1;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
+
+static ktime_t fixup_debug_start(struct pci_dev *dev,
+ void (*fn)(struct pci_dev *dev))
+{
+ ktime_t calltime = ktime_set(0, 0);
+
+ dev_dbg(&dev->dev, "calling %pF\n", fn);
+ if (initcall_debug) {
+ pr_debug("calling %pF @ %i for %s\n",
+ fn, task_pid_nr(current), dev_name(&dev->dev));
+ calltime = ktime_get();
+ }
+
+ return calltime;
+}
+
+static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime,
+ void (*fn)(struct pci_dev *dev))
+{
+ ktime_t delta, rettime;
+ unsigned long long duration;
+
+ if (initcall_debug) {
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+ pr_debug("pci fixup %pF returned after %lld usecs for %s\n",
+ fn, duration, dev_name(&dev->dev));
+ }
+}
+
+/*
+ * Some BIOS implementations leave the Intel GPU interrupts enabled,
+ * even though no one is handling them (f.e. i915 driver is never loaded).
+ * Additionally the interrupt destination is not set up properly
+ * and the interrupt ends up -somewhere-.
+ *
+ * These spurious interrupts are "sticky" and the kernel disables
+ * the (shared) interrupt line after 100.000+ generated interrupts.
+ *
+ * Fix it by disabling the still enabled interrupts.
+ * This resolves crashes often seen on monitor unplug.
+ */
+#define I915_DEIER_REG 0x4400c
+static void disable_igfx_irq(struct pci_dev *dev)
+{
+ void __iomem *regs = pci_iomap(dev, 0, 0);
+ if (regs == NULL) {
+ dev_warn(&dev->dev, "igfx quirk: Can't iomap PCI device\n");
+ return;
+ }
+
+ /* Check if any interrupt line is still enabled */
+ if (readl(regs + I915_DEIER_REG) != 0) {
+ dev_warn(&dev->dev, "BIOS left Intel GPU interrupts enabled; disabling\n");
+
+ writel(0, regs + I915_DEIER_REG);
+ }
+
+ pci_iounmap(dev, regs);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);
+
+/*
+ * PCI devices which are on Intel chips can skip the 10ms delay
+ * before entering D3 mode.
+ */
+static void quirk_remove_d3_delay(struct pci_dev *dev)
+{
+ dev->d3_delay = 0;
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c00, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0412, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c0c, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c31, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3a, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3d, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c2d, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c20, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c18, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c1c, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c26, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c4e, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c02, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c22, quirk_remove_d3_delay);
+
+/*
+ * Some devices may pass our check in pci_intx_mask_supported if
+ * PCI_COMMAND_INTX_DISABLE works though they actually do not properly
+ * support this feature.
+ */
+static void quirk_broken_intx_masking(struct pci_dev *dev)
+{
+ dev->broken_intx_masking = 1;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, 0x0030,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_HEADER(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */
+ quirk_broken_intx_masking);
+/*
+ * Realtek RTL8169 PCI Gigabit Ethernet Controller (rev 10)
+ * Subsystem: Realtek RTL8169/8110 Family PCI Gigabit Ethernet NIC
+ *
+ * RTL8110SC - Fails under PCI device assignment using DisINTx masking.
+ */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x8169,
+ quirk_broken_intx_masking);
+
+static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
+ struct pci_fixup *end)
+{
+ ktime_t calltime;
+
+ for (; f < end; f++)
+ if ((f->class == (u32) (dev->class >> f->class_shift) ||
+ f->class == (u32) PCI_ANY_ID) &&
+ (f->vendor == dev->vendor ||
+ f->vendor == (u16) PCI_ANY_ID) &&
+ (f->device == dev->device ||
+ f->device == (u16) PCI_ANY_ID)) {
+ calltime = fixup_debug_start(dev, f->hook);
+ f->hook(dev);
+ fixup_debug_report(dev, calltime, f->hook);
+ }
+}
+
+extern struct pci_fixup __start_pci_fixups_early[];
+extern struct pci_fixup __end_pci_fixups_early[];
+extern struct pci_fixup __start_pci_fixups_header[];
+extern struct pci_fixup __end_pci_fixups_header[];
+extern struct pci_fixup __start_pci_fixups_final[];
+extern struct pci_fixup __end_pci_fixups_final[];
+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[];
+
+static bool pci_apply_fixup_final_quirks;
+
+void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
+{
+ struct pci_fixup *start, *end;
+
+ switch (pass) {
+ case pci_fixup_early:
+ start = __start_pci_fixups_early;
+ end = __end_pci_fixups_early;
+ break;
+
+ case pci_fixup_header:
+ start = __start_pci_fixups_header;
+ end = __end_pci_fixups_header;
+ break;
+
+ case pci_fixup_final:
+ if (!pci_apply_fixup_final_quirks)
+ return;
+ start = __start_pci_fixups_final;
+ end = __end_pci_fixups_final;
+ break;
+
+ case pci_fixup_enable:
+ start = __start_pci_fixups_enable;
+ end = __end_pci_fixups_enable;
+ break;
+
+ case pci_fixup_resume:
+ start = __start_pci_fixups_resume;
+ 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;
+ }
+ pci_do_fixups(dev, start, end);
+}
+EXPORT_SYMBOL(pci_fixup_device);
+
+
+static int __init pci_apply_final_quirks(void)
+{
+ struct pci_dev *dev = NULL;
+ u8 cls = 0;
+ u8 tmp;
+
+ if (pci_cache_line_size)
+ printk(KERN_DEBUG "PCI: CLS %u bytes\n",
+ pci_cache_line_size << 2);
+
+ pci_apply_fixup_final_quirks = true;
+ for_each_pci_dev(dev) {
+ pci_fixup_device(pci_fixup_final, dev);
+ /*
+ * If arch hasn't set it explicitly yet, use the CLS
+ * value shared by all PCI devices. If there's a
+ * mismatch, fall back to the default value.
+ */
+ if (!pci_cache_line_size) {
+ pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &tmp);
+ if (!cls)
+ cls = tmp;
+ if (!tmp || cls == tmp)
+ continue;
+
+ printk(KERN_DEBUG "PCI: CLS mismatch (%u != %u), using %u bytes\n",
+ cls << 2, tmp << 2,
+ pci_dfl_cache_line_size << 2);
+ pci_cache_line_size = pci_dfl_cache_line_size;
+ }
+ }
+
+ if (!pci_cache_line_size) {
+ printk(KERN_DEBUG "PCI: CLS %u bytes, default %u\n",
+ cls << 2, pci_dfl_cache_line_size << 2);
+ pci_cache_line_size = cls ? cls : pci_dfl_cache_line_size;
+ }
+
+ return 0;
+}
+
+fs_initcall_sync(pci_apply_final_quirks);
+
+/*
+ * Followings are device-specific reset methods which can be used to
+ * reset a single function if other methods (e.g. FLR, PM D0->D3) are
+ * not available.
+ */
+static int reset_intel_generic_dev(struct pci_dev *dev, int probe)
+{
+ int pos;
+
+ /* only implement PCI_CLASS_SERIAL_USB at present */
+ if (dev->class == PCI_CLASS_SERIAL_USB) {
+ pos = pci_find_capability(dev, PCI_CAP_ID_VNDR);
+ if (!pos)
+ return -ENOTTY;
+
+ if (probe)
+ return 0;
+
+ pci_write_config_byte(dev, pos + 0x4, 1);
+ msleep(100);
+
+ return 0;
+ } else {
+ return -ENOTTY;
+ }
+}
+
+static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, int probe)
+{
+ /*
+ * http://www.intel.com/content/dam/doc/datasheet/82599-10-gbe-controller-datasheet.pdf
+ *
+ * The 82599 supports FLR on VFs, but FLR support is reported only
+ * in the PF DEVCAP (sec 9.3.10.4), not in the VF DEVCAP (sec 9.5).
+ * Therefore, we can't use pcie_flr(), which checks the VF DEVCAP.
+ */
+
+ if (probe)
+ return 0;
+
+ if (!pci_wait_for_pending_transaction(dev))
+ dev_err(&dev->dev, "transaction is not cleared; proceeding with reset anyway\n");
+
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
+
+ msleep(100);
+
+ return 0;
+}
+
+#include "../gpu/drm/i915/i915_reg.h"
+#define MSG_CTL 0x45010
+#define NSDE_PWR_STATE 0xd0100
+#define IGD_OPERATION_TIMEOUT 10000 /* set timeout 10 seconds */
+
+static int reset_ivb_igd(struct pci_dev *dev, int probe)
+{
+ void __iomem *mmio_base;
+ unsigned long timeout;
+ u32 val;
+
+ if (probe)
+ return 0;
+
+ mmio_base = pci_iomap(dev, 0, 0);
+ if (!mmio_base)
+ return -ENOMEM;
+
+ iowrite32(0x00000002, mmio_base + MSG_CTL);
+
+ /*
+ * Clobbering SOUTH_CHICKEN2 register is fine only if the next
+ * driver loaded sets the right bits. However, this's a reset and
+ * the bits have been set by i915 previously, so we clobber
+ * SOUTH_CHICKEN2 register directly here.
+ */
+ iowrite32(0x00000005, mmio_base + SOUTH_CHICKEN2);
+
+ val = ioread32(mmio_base + PCH_PP_CONTROL) & 0xfffffffe;
+ iowrite32(val, mmio_base + PCH_PP_CONTROL);
+
+ timeout = jiffies + msecs_to_jiffies(IGD_OPERATION_TIMEOUT);
+ do {
+ val = ioread32(mmio_base + PCH_PP_STATUS);
+ if ((val & 0xb0000000) == 0)
+ goto reset_complete;
+ msleep(10);
+ } while (time_before(jiffies, timeout));
+ dev_warn(&dev->dev, "timeout during reset\n");
+
+reset_complete:
+ iowrite32(0x00000002, mmio_base + NSDE_PWR_STATE);
+
+ pci_iounmap(dev, mmio_base);
+ return 0;
+}
+
+/*
+ * Device-specific reset method for Chelsio T4-based adapters.
+ */
+static int reset_chelsio_generic_dev(struct pci_dev *dev, int probe)
+{
+ u16 old_command;
+ u16 msix_flags;
+
+ /*
+ * If this isn't a Chelsio T4-based device, return -ENOTTY indicating
+ * that we have no device-specific reset method.
+ */
+ if ((dev->device & 0xf000) != 0x4000)
+ return -ENOTTY;
+
+ /*
+ * If this is the "probe" phase, return 0 indicating that we can
+ * reset this device.
+ */
+ if (probe)
+ return 0;
+
+ /*
+ * T4 can wedge if there are DMAs in flight within the chip and Bus
+ * Master has been disabled. We need to have it on till the Function
+ * Level Reset completes. (BUS_MASTER is disabled in
+ * pci_reset_function()).
+ */
+ pci_read_config_word(dev, PCI_COMMAND, &old_command);
+ pci_write_config_word(dev, PCI_COMMAND,
+ old_command | PCI_COMMAND_MASTER);
+
+ /*
+ * Perform the actual device function reset, saving and restoring
+ * configuration information around the reset.
+ */
+ pci_save_state(dev);
+
+ /*
+ * T4 also suffers a Head-Of-Line blocking problem if MSI-X interrupts
+ * are disabled when an MSI-X interrupt message needs to be delivered.
+ * So we briefly re-enable MSI-X interrupts for the duration of the
+ * FLR. The pci_restore_state() below will restore the original
+ * MSI-X state.
+ */
+ pci_read_config_word(dev, dev->msix_cap+PCI_MSIX_FLAGS, &msix_flags);
+ if ((msix_flags & PCI_MSIX_FLAGS_ENABLE) == 0)
+ pci_write_config_word(dev, dev->msix_cap+PCI_MSIX_FLAGS,
+ msix_flags |
+ PCI_MSIX_FLAGS_ENABLE |
+ PCI_MSIX_FLAGS_MASKALL);
+
+ /*
+ * Start of pcie_flr() code sequence. This reset code is a copy of
+ * the guts of pcie_flr() because that's not an exported function.
+ */
+
+ if (!pci_wait_for_pending_transaction(dev))
+ dev_err(&dev->dev, "transaction is not cleared; proceeding with reset anyway\n");
+
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
+ msleep(100);
+
+ /*
+ * End of pcie_flr() code sequence.
+ */
+
+ /*
+ * Restore the configuration information (BAR values, etc.) including
+ * the original PCI Configuration Space Command word, and return
+ * success.
+ */
+ pci_restore_state(dev);
+ pci_write_config_word(dev, PCI_COMMAND, old_command);
+ return 0;
+}
+
+#define PCI_DEVICE_ID_INTEL_82599_SFP_VF 0x10ed
+#define PCI_DEVICE_ID_INTEL_IVB_M_VGA 0x0156
+#define PCI_DEVICE_ID_INTEL_IVB_M2_VGA 0x0166
+
+static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82599_SFP_VF,
+ reset_intel_82599_sfp_virtfn },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IVB_M_VGA,
+ reset_ivb_igd },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IVB_M2_VGA,
+ reset_ivb_igd },
+ { PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+ reset_intel_generic_dev },
+ { PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
+ reset_chelsio_generic_dev },
+ { 0 }
+};
+
+/*
+ * These device-specific reset methods are here rather than in a driver
+ * because when a host assigns a device to a guest VM, the host may need
+ * to reset the device but probably doesn't have a driver for it.
+ */
+int pci_dev_specific_reset(struct pci_dev *dev, int probe)
+{
+ const struct pci_dev_reset_methods *i;
+
+ for (i = pci_dev_reset_methods; i->reset; i++) {
+ if ((i->vendor == dev->vendor ||
+ i->vendor == (u16)PCI_ANY_ID) &&
+ (i->device == dev->device ||
+ i->device == (u16)PCI_ANY_ID))
+ return i->reset(dev, probe);
+ }
+
+ return -ENOTTY;
+}
+
+static void quirk_dma_func0_alias(struct pci_dev *dev)
+{
+ if (PCI_FUNC(dev->devfn) != 0) {
+ dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0);
+ dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
+ }
+}
+
+/*
+ * https://bugzilla.redhat.com/show_bug.cgi?id=605888
+ *
+ * Some Ricoh devices use function 0 as the PCIe requester ID for DMA.
+ */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe832, quirk_dma_func0_alias);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe476, quirk_dma_func0_alias);
+
+static void quirk_dma_func1_alias(struct pci_dev *dev)
+{
+ if (PCI_FUNC(dev->devfn) != 1) {
+ dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 1);
+ dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
+ }
+}
+
+/*
+ * Marvell 88SE9123 uses function 1 as the requester ID for DMA. In some
+ * SKUs function 1 is present and is a legacy IDE controller, in other
+ * SKUs this function is not present, making this a ghost requester.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=42679
+ */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9123,
+ quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c14 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9130,
+ quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c47 + c57 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9172,
+ quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c59 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x917a,
+ quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c46 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0,
+ quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c49 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230,
+ quirk_dma_func1_alias);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TTI, 0x0642,
+ quirk_dma_func1_alias);
+/* https://bugs.gentoo.org/show_bug.cgi?id=497630 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_JMICRON,
+ PCI_DEVICE_ID_JMICRON_JMB388_ESD,
+ quirk_dma_func1_alias);
+
+/*
+ * A few PCIe-to-PCI bridges fail to expose a PCIe capability, resulting in
+ * using the wrong DMA alias for the device. Some of these devices can be
+ * used as either forward or reverse bridges, so we need to test whether the
+ * device is operating in the correct mode. We could probably apply this
+ * quirk to PCI_ANY_ID, but for now we'll just use known offenders. The test
+ * is for a non-root, non-PCIe bridge where the upstream device is PCIe and
+ * is not a PCIe-to-PCI bridge, then @pdev is actually a PCIe-to-PCI bridge.
+ */
+static void quirk_use_pcie_bridge_dma_alias(struct pci_dev *pdev)
+{
+ if (!pci_is_root_bus(pdev->bus) &&
+ pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
+ !pci_is_pcie(pdev) && pci_is_pcie(pdev->bus->self) &&
+ pci_pcie_type(pdev->bus->self) != PCI_EXP_TYPE_PCI_BRIDGE)
+ pdev->dev_flags |= PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS;
+}
+/* ASM1083/1085, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c46 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASMEDIA, 0x1080,
+ quirk_use_pcie_bridge_dma_alias);
+/* Tundra 8113, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c43 */
+DECLARE_PCI_FIXUP_HEADER(0x10e3, 0x8113, quirk_use_pcie_bridge_dma_alias);
+/* ITE 8892, https://bugzilla.kernel.org/show_bug.cgi?id=73551 */
+DECLARE_PCI_FIXUP_HEADER(0x1283, 0x8892, quirk_use_pcie_bridge_dma_alias);
+
+static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev)
+{
+ if (!PCI_FUNC(dev->devfn))
+ return pci_dev_get(dev);
+
+ return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+}
+
+static const struct pci_dev_dma_source {
+ u16 vendor;
+ u16 device;
+ struct pci_dev *(*dma_source)(struct pci_dev *dev);
+} pci_dev_dma_source[] = {
+ /*
+ * https://bugzilla.redhat.com/show_bug.cgi?id=605888
+ *
+ * Some Ricoh devices use the function 0 source ID for DMA on
+ * other functions of a multifunction device. The DMA devices
+ * is therefore function 0, which will have implications of the
+ * iommu grouping of these devices.
+ */
+ { PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source },
+ { PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source },
+ { PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source },
+ { PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source },
+ { 0 }
+};
+
+/*
+ * IOMMUs with isolation capabilities need to be programmed with the
+ * correct source ID of a device. In most cases, the source ID matches
+ * the device doing the DMA, but sometimes hardware is broken and will
+ * tag the DMA as being sourced from a different device. This function
+ * allows that translation. Note that the reference count of the
+ * returned device is incremented on all paths.
+ */
+struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
+{
+ const struct pci_dev_dma_source *i;
+
+ for (i = pci_dev_dma_source; i->dma_source; i++) {
+ if ((i->vendor == dev->vendor ||
+ i->vendor == (u16)PCI_ANY_ID) &&
+ (i->device == dev->device ||
+ i->device == (u16)PCI_ANY_ID))
+ return i->dma_source(dev);
+ }
+
+ return pci_dev_get(dev);
+}
+
+/*
+ * AMD has indicated that the devices below do not support peer-to-peer
+ * in any system where they are found in the southbridge with an AMD
+ * IOMMU in the system. Multifunction devices that do not support
+ * peer-to-peer between functions can claim to support a subset of ACS.
+ * Such devices effectively enable request redirect (RR) and completion
+ * redirect (CR) since all transactions are redirected to the upstream
+ * root complex.
+ *
+ * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/94086
+ * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/94102
+ * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/99402
+ *
+ * 1002:4385 SBx00 SMBus Controller
+ * 1002:439c SB7x0/SB8x0/SB9x0 IDE Controller
+ * 1002:4383 SBx00 Azalia (Intel HDA)
+ * 1002:439d SB7x0/SB8x0/SB9x0 LPC host controller
+ * 1002:4384 SBx00 PCI to PCI Bridge
+ * 1002:4399 SB7x0/SB8x0/SB9x0 USB OHCI2 Controller
+ */
+static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags)
+{
+#ifdef CONFIG_ACPI
+ struct acpi_table_header *header = NULL;
+ acpi_status status;
+
+ /* Targeting multifunction devices on the SB (appears on root bus) */
+ if (!dev->multifunction || !pci_is_root_bus(dev->bus))
+ return -ENODEV;
+
+ /* The IVRS table describes the AMD IOMMU */
+ status = acpi_get_table("IVRS", 0, &header);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ /* Filter out flags not applicable to multifunction */
+ acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC | PCI_ACS_DT);
+
+ return acs_flags & ~(PCI_ACS_RR | PCI_ACS_CR) ? 0 : 1;
+#else
+ return -ENODEV;
+#endif
+}
+
+/*
+ * Many Intel PCH root ports do provide ACS-like features to disable peer
+ * transactions and validate bus numbers in requests, but do not provide an
+ * actual PCIe ACS capability. This is the list of device IDs known to fall
+ * into that category as provided by Intel in Red Hat bugzilla 1037684.
+ */
+static const u16 pci_quirk_intel_pch_acs_ids[] = {
+ /* Ibexpeak PCH */
+ 0x3b42, 0x3b43, 0x3b44, 0x3b45, 0x3b46, 0x3b47, 0x3b48, 0x3b49,
+ 0x3b4a, 0x3b4b, 0x3b4c, 0x3b4d, 0x3b4e, 0x3b4f, 0x3b50, 0x3b51,
+ /* Cougarpoint PCH */
+ 0x1c10, 0x1c11, 0x1c12, 0x1c13, 0x1c14, 0x1c15, 0x1c16, 0x1c17,
+ 0x1c18, 0x1c19, 0x1c1a, 0x1c1b, 0x1c1c, 0x1c1d, 0x1c1e, 0x1c1f,
+ /* Pantherpoint PCH */
+ 0x1e10, 0x1e11, 0x1e12, 0x1e13, 0x1e14, 0x1e15, 0x1e16, 0x1e17,
+ 0x1e18, 0x1e19, 0x1e1a, 0x1e1b, 0x1e1c, 0x1e1d, 0x1e1e, 0x1e1f,
+ /* Lynxpoint-H PCH */
+ 0x8c10, 0x8c11, 0x8c12, 0x8c13, 0x8c14, 0x8c15, 0x8c16, 0x8c17,
+ 0x8c18, 0x8c19, 0x8c1a, 0x8c1b, 0x8c1c, 0x8c1d, 0x8c1e, 0x8c1f,
+ /* Lynxpoint-LP PCH */
+ 0x9c10, 0x9c11, 0x9c12, 0x9c13, 0x9c14, 0x9c15, 0x9c16, 0x9c17,
+ 0x9c18, 0x9c19, 0x9c1a, 0x9c1b,
+ /* Wildcat PCH */
+ 0x9c90, 0x9c91, 0x9c92, 0x9c93, 0x9c94, 0x9c95, 0x9c96, 0x9c97,
+ 0x9c98, 0x9c99, 0x9c9a, 0x9c9b,
+ /* Patsburg (X79) PCH */
+ 0x1d10, 0x1d12, 0x1d14, 0x1d16, 0x1d18, 0x1d1a, 0x1d1c, 0x1d1e,
+};
+
+static bool pci_quirk_intel_pch_acs_match(struct pci_dev *dev)
+{
+ int i;
+
+ /* Filter out a few obvious non-matches first */
+ if (!pci_is_pcie(dev) || pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(pci_quirk_intel_pch_acs_ids); i++)
+ if (pci_quirk_intel_pch_acs_ids[i] == dev->device)
+ return true;
+
+ return false;
+}
+
+#define INTEL_PCH_ACS_FLAGS (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_SV)
+
+static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
+{
+ u16 flags = dev->dev_flags & PCI_DEV_FLAGS_ACS_ENABLED_QUIRK ?
+ INTEL_PCH_ACS_FLAGS : 0;
+
+ if (!pci_quirk_intel_pch_acs_match(dev))
+ return -ENOTTY;
+
+ return acs_flags & ~flags ? 0 : 1;
+}
+
+static const struct pci_dev_acs_enabled {
+ u16 vendor;
+ u16 device;
+ int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags);
+} pci_dev_acs_enabled[] = {
+ { PCI_VENDOR_ID_ATI, 0x4385, pci_quirk_amd_sb_acs },
+ { PCI_VENDOR_ID_ATI, 0x439c, pci_quirk_amd_sb_acs },
+ { PCI_VENDOR_ID_ATI, 0x4383, pci_quirk_amd_sb_acs },
+ { PCI_VENDOR_ID_ATI, 0x439d, pci_quirk_amd_sb_acs },
+ { PCI_VENDOR_ID_ATI, 0x4384, pci_quirk_amd_sb_acs },
+ { PCI_VENDOR_ID_ATI, 0x4399, pci_quirk_amd_sb_acs },
+ { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
+ { 0 }
+};
+
+int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags)
+{
+ const struct pci_dev_acs_enabled *i;
+ int ret;
+
+ /*
+ * Allow devices that do not expose standard PCIe ACS capabilities
+ * or control to indicate their support here. Multi-function express
+ * devices which do not allow internal peer-to-peer between functions,
+ * but do not implement PCIe ACS may wish to return true here.
+ */
+ for (i = pci_dev_acs_enabled; i->acs_enabled; i++) {
+ if ((i->vendor == dev->vendor ||
+ i->vendor == (u16)PCI_ANY_ID) &&
+ (i->device == dev->device ||
+ i->device == (u16)PCI_ANY_ID)) {
+ ret = i->acs_enabled(dev, acs_flags);
+ if (ret >= 0)
+ return ret;
+ }
+ }
+
+ return -ENOTTY;
+}
+
+/* Config space offset of Root Complex Base Address register */
+#define INTEL_LPC_RCBA_REG 0xf0
+/* 31:14 RCBA address */
+#define INTEL_LPC_RCBA_MASK 0xffffc000
+/* RCBA Enable */
+#define INTEL_LPC_RCBA_ENABLE (1 << 0)
+
+/* Backbone Scratch Pad Register */
+#define INTEL_BSPR_REG 0x1104
+/* Backbone Peer Non-Posted Disable */
+#define INTEL_BSPR_REG_BPNPD (1 << 8)
+/* Backbone Peer Posted Disable */
+#define INTEL_BSPR_REG_BPPD (1 << 9)
+
+/* Upstream Peer Decode Configuration Register */
+#define INTEL_UPDCR_REG 0x1114
+/* 5:0 Peer Decode Enable bits */
+#define INTEL_UPDCR_REG_MASK 0x3f
+
+static int pci_quirk_enable_intel_lpc_acs(struct pci_dev *dev)
+{
+ u32 rcba, bspr, updcr;
+ void __iomem *rcba_mem;
+
+ /*
+ * Read the RCBA register from the LPC (D31:F0). PCH root ports
+ * are D28:F* and therefore get probed before LPC, thus we can't
+ * use pci_get_slot/pci_read_config_dword here.
+ */
+ pci_bus_read_config_dword(dev->bus, PCI_DEVFN(31, 0),
+ INTEL_LPC_RCBA_REG, &rcba);
+ if (!(rcba & INTEL_LPC_RCBA_ENABLE))
+ return -EINVAL;
+
+ rcba_mem = ioremap_nocache(rcba & INTEL_LPC_RCBA_MASK,
+ PAGE_ALIGN(INTEL_UPDCR_REG));
+ if (!rcba_mem)
+ return -ENOMEM;
+
+ /*
+ * The BSPR can disallow peer cycles, but it's set by soft strap and
+ * therefore read-only. If both posted and non-posted peer cycles are
+ * disallowed, we're ok. If either are allowed, then we need to use
+ * the UPDCR to disable peer decodes for each port. This provides the
+ * PCIe ACS equivalent of PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF
+ */
+ bspr = readl(rcba_mem + INTEL_BSPR_REG);
+ bspr &= INTEL_BSPR_REG_BPNPD | INTEL_BSPR_REG_BPPD;
+ if (bspr != (INTEL_BSPR_REG_BPNPD | INTEL_BSPR_REG_BPPD)) {
+ updcr = readl(rcba_mem + INTEL_UPDCR_REG);
+ if (updcr & INTEL_UPDCR_REG_MASK) {
+ dev_info(&dev->dev, "Disabling UPDCR peer decodes\n");
+ updcr &= ~INTEL_UPDCR_REG_MASK;
+ writel(updcr, rcba_mem + INTEL_UPDCR_REG);
+ }
+ }
+
+ iounmap(rcba_mem);
+ return 0;
+}
+
+/* Miscellaneous Port Configuration register */
+#define INTEL_MPC_REG 0xd8
+/* MPC: Invalid Receive Bus Number Check Enable */
+#define INTEL_MPC_REG_IRBNCE (1 << 26)
+
+static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev)
+{
+ u32 mpc;
+
+ /*
+ * When enabled, the IRBNCE bit of the MPC register enables the
+ * equivalent of PCI ACS Source Validation (PCI_ACS_SV), which
+ * ensures that requester IDs fall within the bus number range
+ * of the bridge. Enable if not already.
+ */
+ pci_read_config_dword(dev, INTEL_MPC_REG, &mpc);
+ if (!(mpc & INTEL_MPC_REG_IRBNCE)) {
+ dev_info(&dev->dev, "Enabling MPC IRBNCE\n");
+ mpc |= INTEL_MPC_REG_IRBNCE;
+ pci_write_config_word(dev, INTEL_MPC_REG, mpc);
+ }
+}
+
+static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev)
+{
+ if (!pci_quirk_intel_pch_acs_match(dev))
+ return -ENOTTY;
+
+ if (pci_quirk_enable_intel_lpc_acs(dev)) {
+ dev_warn(&dev->dev, "Failed to enable Intel PCH ACS quirk\n");
+ return 0;
+ }
+
+ pci_quirk_enable_intel_rp_mpc_acs(dev);
+
+ dev->dev_flags |= PCI_DEV_FLAGS_ACS_ENABLED_QUIRK;
+
+ dev_info(&dev->dev, "Intel PCH root port ACS workaround enabled\n");
+
+ return 0;
+}
+
+static const struct pci_dev_enable_acs {
+ u16 vendor;
+ u16 device;
+ int (*enable_acs)(struct pci_dev *dev);
+} pci_dev_enable_acs[] = {
+ { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_pch_acs },
+ { 0 }
+};
+
+void pci_dev_specific_enable_acs(struct pci_dev *dev)
+{
+ const struct pci_dev_enable_acs *i;
+ int ret;
+
+ for (i = pci_dev_enable_acs; i->enable_acs; i++) {
+ if ((i->vendor == dev->vendor ||
+ i->vendor == (u16)PCI_ANY_ID) &&
+ (i->device == dev->device ||
+ i->device == (u16)PCI_ANY_ID)) {
+ ret = i->enable_acs(dev);
+ if (ret >= 0)
+ return;
+ }
+ }
+}
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 9684e1bde27..8bd76c9ba21 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -1,13 +1,12 @@
#include <linux/pci.h>
#include <linux/module.h>
+#include <linux/pci-aspm.h>
#include "pci.h"
static void pci_free_resources(struct pci_dev *dev)
{
int i;
- msi_remove_pci_irq_vectors(dev);
-
pci_cleanup_rom(dev);
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *res = dev->resource + i;
@@ -18,69 +17,87 @@ static void pci_free_resources(struct pci_dev *dev)
static void pci_stop_dev(struct pci_dev *dev)
{
- if (!dev->global_list.next)
- return;
+ pci_pme_active(dev, false);
- if (!list_empty(&dev->global_list)) {
+ if (dev->is_added) {
pci_proc_detach_device(dev);
pci_remove_sysfs_dev_files(dev);
- device_unregister(&dev->dev);
- down_write(&pci_bus_sem);
- list_del(&dev->global_list);
- dev->global_list.next = dev->global_list.prev = NULL;
- up_write(&pci_bus_sem);
+ device_release_driver(&dev->dev);
+ dev->is_added = 0;
}
+
+ if (dev->bus->self)
+ pcie_aspm_exit_link_state(dev);
}
static void pci_destroy_dev(struct pci_dev *dev)
{
- pci_stop_dev(dev);
+ if (!dev->dev.kobj.parent)
+ return;
+
+ device_del(&dev->dev);
- /* Remove the device from the device lists, and prevent any further
- * list accesses from this device */
down_write(&pci_bus_sem);
list_del(&dev->bus_list);
- dev->bus_list.next = dev->bus_list.prev = NULL;
up_write(&pci_bus_sem);
pci_free_resources(dev);
- pci_dev_put(dev);
+ put_device(&dev->dev);
}
-/**
- * pci_remove_device_safe - remove an unused hotplug device
- * @dev: the device to remove
- *
- * Delete the device structure from the device lists and
- * notify userspace (/sbin/hotplug), but only if the device
- * in question is not being used by a driver.
- * Returns 0 on success.
- */
-#if 0
-int pci_remove_device_safe(struct pci_dev *dev)
+void pci_remove_bus(struct pci_bus *bus)
{
- if (pci_dev_driver(dev))
- return -EBUSY;
- pci_destroy_dev(dev);
- return 0;
-}
-#endif /* 0 */
-
-void pci_remove_bus(struct pci_bus *pci_bus)
-{
- pci_proc_detach_bus(pci_bus);
+ pci_proc_detach_bus(bus);
down_write(&pci_bus_sem);
- list_del(&pci_bus->node);
+ list_del(&bus->node);
+ pci_bus_release_busn_res(bus);
up_write(&pci_bus_sem);
- pci_remove_legacy_files(pci_bus);
- device_remove_file(&pci_bus->dev, &dev_attr_cpuaffinity);
- device_unregister(&pci_bus->dev);
+ pci_remove_legacy_files(bus);
+ pcibios_remove_bus(bus);
+ device_unregister(&bus->dev);
}
EXPORT_SYMBOL(pci_remove_bus);
+static void pci_stop_bus_device(struct pci_dev *dev)
+{
+ struct pci_bus *bus = dev->subordinate;
+ struct pci_dev *child, *tmp;
+
+ /*
+ * Stopping an SR-IOV PF device removes all the associated VFs,
+ * which will update the bus->devices list and confuse the
+ * iterator. Therefore, iterate in reverse so we remove the VFs
+ * first, then the PF.
+ */
+ if (bus) {
+ list_for_each_entry_safe_reverse(child, tmp,
+ &bus->devices, bus_list)
+ pci_stop_bus_device(child);
+ }
+
+ pci_stop_dev(dev);
+}
+
+static void pci_remove_bus_device(struct pci_dev *dev)
+{
+ struct pci_bus *bus = dev->subordinate;
+ struct pci_dev *child, *tmp;
+
+ if (bus) {
+ list_for_each_entry_safe(child, tmp,
+ &bus->devices, bus_list)
+ pci_remove_bus_device(child);
+
+ pci_remove_bus(bus);
+ dev->subordinate = NULL;
+ }
+
+ pci_destroy_dev(dev);
+}
+
/**
- * pci_remove_bus_device - remove a PCI device and any children
+ * pci_stop_and_remove_bus_device - remove a PCI device and any children
* @dev: the device to remove
*
* Remove a PCI device from the device lists, informing the drivers
@@ -91,66 +108,53 @@ EXPORT_SYMBOL(pci_remove_bus);
* device lists, remove the /proc entry, and notify userspace
* (/sbin/hotplug).
*/
-void pci_remove_bus_device(struct pci_dev *dev)
+void pci_stop_and_remove_bus_device(struct pci_dev *dev)
{
- if (dev->subordinate) {
- struct pci_bus *b = dev->subordinate;
-
- pci_remove_behind_bridge(dev);
- pci_remove_bus(b);
- dev->subordinate = NULL;
- }
+ pci_stop_bus_device(dev);
+ pci_remove_bus_device(dev);
+}
+EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
- pci_destroy_dev(dev);
+void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev)
+{
+ pci_lock_rescan_remove();
+ pci_stop_and_remove_bus_device(dev);
+ pci_unlock_rescan_remove();
}
+EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);
-/**
- * pci_remove_behind_bridge - remove all devices behind a PCI bridge
- * @dev: PCI bridge device
- *
- * Remove all devices on the bus, except for the parent bridge.
- * This also removes any child buses, and any devices they may
- * contain in a depth-first manner.
- */
-void pci_remove_behind_bridge(struct pci_dev *dev)
+void pci_stop_root_bus(struct pci_bus *bus)
{
- struct list_head *l, *n;
+ struct pci_dev *child, *tmp;
+ struct pci_host_bridge *host_bridge;
- if (dev->subordinate) {
- list_for_each_safe(l, n, &dev->subordinate->devices) {
- struct pci_dev *dev = pci_dev_b(l);
+ if (!pci_is_root_bus(bus))
+ return;
- pci_remove_bus_device(dev);
- }
- }
+ host_bridge = to_pci_host_bridge(bus->bridge);
+ list_for_each_entry_safe_reverse(child, tmp,
+ &bus->devices, bus_list)
+ pci_stop_bus_device(child);
+
+ /* stop the host bridge */
+ device_release_driver(&host_bridge->dev);
}
-static void pci_stop_bus_devices(struct pci_bus *bus)
+void pci_remove_root_bus(struct pci_bus *bus)
{
- struct list_head *l, *n;
+ struct pci_dev *child, *tmp;
+ struct pci_host_bridge *host_bridge;
- list_for_each_safe(l, n, &bus->devices) {
- struct pci_dev *dev = pci_dev_b(l);
- pci_stop_bus_device(dev);
- }
-}
+ if (!pci_is_root_bus(bus))
+ return;
-/**
- * pci_stop_bus_device - stop a PCI device and any children
- * @dev: the device to stop
- *
- * Stop a PCI device (detach the driver, remove from the global list
- * and so on). This also stop any subordinate buses and children in a
- * depth-first manner.
- */
-void pci_stop_bus_device(struct pci_dev *dev)
-{
- if (dev->subordinate)
- pci_stop_bus_devices(dev->subordinate);
+ host_bridge = to_pci_host_bridge(bus->bridge);
+ list_for_each_entry_safe(child, tmp,
+ &bus->devices, bus_list)
+ pci_remove_bus_device(child);
+ pci_remove_bus(bus);
+ host_bridge->bus = NULL;
- pci_stop_dev(dev);
+ /* remove the host bridge */
+ device_unregister(&host_bridge->dev);
}
-
-EXPORT_SYMBOL(pci_remove_bus_device);
-EXPORT_SYMBOL(pci_remove_behind_bridge);
-EXPORT_SYMBOL_GPL(pci_stop_bus_device);
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
index bd5c0e03139..f955edb9bea 100644
--- a/drivers/pci/rom.c
+++ b/drivers/pci/rom.c
@@ -7,6 +7,7 @@
* PCI ROM access routines
*/
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/pci.h>
#include <linux/slab.h>
@@ -21,7 +22,7 @@
* between the ROM and other resources, so enabling it may disable access
* to MMIO registers or other card memory.
*/
-static int pci_enable_rom(struct pci_dev *pdev)
+int pci_enable_rom(struct pci_dev *pdev)
{
struct resource *res = pdev->resource + PCI_ROM_RESOURCE;
struct pci_bus_region region;
@@ -30,13 +31,14 @@ static int pci_enable_rom(struct pci_dev *pdev)
if (!res->flags)
return -1;
- pcibios_resource_to_bus(pdev, &region, res);
+ pcibios_resource_to_bus(pdev->bus, &region, res);
pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
rom_addr &= ~PCI_ROM_ADDRESS_MASK;
rom_addr |= region.start | PCI_ROM_ADDRESS_ENABLE;
pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
return 0;
}
+EXPORT_SYMBOL_GPL(pci_enable_rom);
/**
* pci_disable_rom - disable ROM decoding for a PCI device
@@ -45,16 +47,18 @@ static int pci_enable_rom(struct pci_dev *pdev)
* Disable ROM decoding on a PCI device by turning off the last bit in the
* ROM BAR.
*/
-static void pci_disable_rom(struct pci_dev *pdev)
+void pci_disable_rom(struct pci_dev *pdev)
{
u32 rom_addr;
pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
}
+EXPORT_SYMBOL_GPL(pci_disable_rom);
/**
* pci_get_rom_size - obtain the actual size of the ROM image
+ * @pdev: target PCI device
* @rom: kernel virtual pointer to image of ROM
* @size: size of PCI window
* return: size of actual ROM image
@@ -63,7 +67,7 @@ static void pci_disable_rom(struct pci_dev *pdev)
* The PCI window size could be much larger than the
* actual image size.
*/
-size_t pci_get_rom_size(void __iomem *rom, size_t size)
+size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size)
{
void __iomem *image;
int last_image;
@@ -72,8 +76,10 @@ size_t pci_get_rom_size(void __iomem *rom, size_t size)
do {
void __iomem *pds;
/* Standard PCI ROMs start out with these bytes 55 AA */
- if (readb(image) != 0x55)
+ if (readb(image) != 0x55) {
+ dev_err(&pdev->dev, "Invalid ROM contents\n");
break;
+ }
if (readb(image + 1) != 0xAA)
break;
/* get the PCI data structure and check its signature */
@@ -100,7 +106,8 @@ size_t pci_get_rom_size(void __iomem *rom, size_t size)
* pci_map_rom - map a PCI ROM to kernel space
* @pdev: pointer to pci device struct
* @size: pointer to receive size of pci window over ROM
- * @return: kernel virtual pointer to image of ROM
+ *
+ * Return: kernel virtual pointer to image of ROM
*
* Map a PCI ROM into kernel space. If ROM is boot video ROM,
* the shadow BIOS copy will be returned instead of the
@@ -130,7 +137,7 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
} else {
/* assign the ROM an address if it doesn't have one */
if (res->parent == NULL &&
- pci_assign_resource(pdev,PCI_ROM_RESOURCE))
+ pci_assign_resource(pdev, PCI_ROM_RESOURCE))
return NULL;
start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
@@ -158,46 +165,10 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
* size is much larger than the actual size of the ROM.
* True size is important if the ROM is going to be copied.
*/
- *size = pci_get_rom_size(rom, *size);
+ *size = pci_get_rom_size(pdev, rom, *size);
return rom;
}
-
-#if 0
-/**
- * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
- * @pdev: pointer to pci device struct
- * @size: pointer to receive size of pci window over ROM
- * @return: kernel virtual pointer to image of ROM
- *
- * Map a PCI ROM into kernel space. If ROM is boot video ROM,
- * the shadow BIOS copy will be returned instead of the
- * actual ROM.
- */
-void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
-{
- struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
- void __iomem *rom;
-
- rom = pci_map_rom(pdev, size);
- if (!rom)
- return NULL;
-
- if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW |
- IORESOURCE_ROM_BIOS_COPY))
- return rom;
-
- res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
- if (!res->start)
- return rom;
-
- res->end = res->start + *size;
- memcpy_fromio((void*)(unsigned long)res->start, rom, *size);
- pci_unmap_rom(pdev, rom);
- res->flags |= IORESOURCE_ROM_COPY;
-
- return (void __iomem *)(unsigned long)res->start;
-}
-#endif /* 0 */
+EXPORT_SYMBOL(pci_map_rom);
/**
* pci_unmap_rom - unmap the ROM from kernel space
@@ -219,27 +190,7 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
pci_disable_rom(pdev);
}
-
-#if 0
-/**
- * pci_remove_rom - disable the ROM and remove its sysfs attribute
- * @pdev: pointer to pci device struct
- *
- * Remove the rom file in sysfs and disable ROM decoding.
- */
-void pci_remove_rom(struct pci_dev *pdev)
-{
- struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
-
- if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
- sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
- if (!(res->flags & (IORESOURCE_ROM_ENABLE |
- IORESOURCE_ROM_SHADOW |
- IORESOURCE_ROM_BIOS_COPY |
- IORESOURCE_ROM_COPY)))
- pci_disable_rom(pdev);
-}
-#endif /* 0 */
+EXPORT_SYMBOL(pci_unmap_rom);
/**
* pci_cleanup_rom - free the ROM copy created by pci_map_rom_copy
@@ -250,13 +201,29 @@ void pci_remove_rom(struct pci_dev *pdev)
void pci_cleanup_rom(struct pci_dev *pdev)
{
struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
if (res->flags & IORESOURCE_ROM_COPY) {
- kfree((void*)(unsigned long)res->start);
+ kfree((void *)(unsigned long)res->start);
+ res->flags |= IORESOURCE_UNSET;
res->flags &= ~IORESOURCE_ROM_COPY;
res->start = 0;
res->end = 0;
}
}
-EXPORT_SYMBOL(pci_map_rom);
-EXPORT_SYMBOL(pci_unmap_rom);
+/**
+ * pci_platform_rom - provides a pointer to any ROM image provided by the
+ * platform
+ * @pdev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ */
+void __iomem *pci_platform_rom(struct pci_dev *pdev, size_t *size)
+{
+ if (pdev->rom && pdev->romlen) {
+ *size = pdev->romlen;
+ return phys_to_virt((phys_addr_t)pdev->rom);
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(pci_platform_rom);
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index 8541034021f..827ad831f1d 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -1,5 +1,5 @@
/*
- * PCI searching functions.
+ * PCI searching functions.
*
* Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter,
* David Mosberger-Tang
@@ -7,38 +7,126 @@
* Copyright (C) 2003 -- 2004 Greg Kroah-Hartman <greg@kroah.com>
*/
-#include <linux/init.h>
#include <linux/pci.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include "pci.h"
DECLARE_RWSEM(pci_bus_sem);
+EXPORT_SYMBOL_GPL(pci_bus_sem);
+
+/*
+ * pci_for_each_dma_alias - Iterate over DMA aliases for a device
+ * @pdev: starting downstream device
+ * @fn: function to call for each alias
+ * @data: opaque data to pass to @fn
+ *
+ * Starting @pdev, walk up the bus calling @fn for each possible alias
+ * of @pdev at the root bus.
+ */
+int pci_for_each_dma_alias(struct pci_dev *pdev,
+ int (*fn)(struct pci_dev *pdev,
+ u16 alias, void *data), void *data)
+{
+ struct pci_bus *bus;
+ int ret;
+
+ ret = fn(pdev, PCI_DEVID(pdev->bus->number, pdev->devfn), data);
+ if (ret)
+ return ret;
+
+ /*
+ * If the device is broken and uses an alias requester ID for
+ * DMA, iterate over that too.
+ */
+ if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) {
+ ret = fn(pdev, PCI_DEVID(pdev->bus->number,
+ pdev->dma_alias_devfn), data);
+ if (ret)
+ return ret;
+ }
+
+ for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
+ struct pci_dev *tmp;
+
+ /* Skip virtual buses */
+ if (!bus->self)
+ continue;
+
+ tmp = bus->self;
+
+ /*
+ * PCIe-to-PCI/X bridges alias transactions from downstream
+ * devices using the subordinate bus number (PCI Express to
+ * PCI/PCI-X Bridge Spec, rev 1.0, sec 2.3). For all cases
+ * where the upstream bus is PCI/X we alias to the bridge
+ * (there are various conditions in the previous reference
+ * where the bridge may take ownership of transactions, even
+ * when the secondary interface is PCI-X).
+ */
+ if (pci_is_pcie(tmp)) {
+ switch (pci_pcie_type(tmp)) {
+ case PCI_EXP_TYPE_ROOT_PORT:
+ case PCI_EXP_TYPE_UPSTREAM:
+ case PCI_EXP_TYPE_DOWNSTREAM:
+ continue;
+ case PCI_EXP_TYPE_PCI_BRIDGE:
+ ret = fn(tmp,
+ PCI_DEVID(tmp->subordinate->number,
+ PCI_DEVFN(0, 0)), data);
+ if (ret)
+ return ret;
+ continue;
+ case PCI_EXP_TYPE_PCIE_BRIDGE:
+ ret = fn(tmp,
+ PCI_DEVID(tmp->bus->number,
+ tmp->devfn), data);
+ if (ret)
+ return ret;
+ continue;
+ }
+ } else {
+ if (tmp->dev_flags & PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS)
+ ret = fn(tmp,
+ PCI_DEVID(tmp->subordinate->number,
+ PCI_DEVFN(0, 0)), data);
+ else
+ ret = fn(tmp,
+ PCI_DEVID(tmp->bus->number,
+ tmp->devfn), data);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
/*
- * find the upstream PCIE-to-PCI bridge of a PCI device
+ * find the upstream PCIe-to-PCI bridge of a PCI device
* if the device is PCIE, return NULL
- * if the device isn't connected to a PCIE bridge (that is its parent is a
+ * if the device isn't connected to a PCIe bridge (that is its parent is a
* legacy PCI bridge and the bridge is directly connected to bus 0), return its
* parent
*/
-struct pci_dev *
-pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
+struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
{
struct pci_dev *tmp = NULL;
- if (pdev->is_pcie)
+ if (pci_is_pcie(pdev))
return NULL;
while (1) {
- if (!pdev->bus->self)
+ if (pci_is_root_bus(pdev->bus))
break;
pdev = pdev->bus->self;
/* a p2p bridge */
- if (!pdev->is_pcie) {
+ if (!pci_is_pcie(pdev)) {
tmp = pdev;
continue;
}
- /* PCI device should connect to a PCIE bridge */
- if (pdev->pcie_type != PCI_EXP_TYPE_PCI_BRIDGE) {
+ /* PCI device should connect to a PCIe bridge */
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_PCI_BRIDGE) {
/* Busted hardware? */
WARN_ON_ONCE(1);
return NULL;
@@ -51,15 +139,15 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)
{
- struct pci_bus* child;
- struct list_head *tmp;
+ struct pci_bus *child;
+ struct pci_bus *tmp;
- if(bus->number == busnr)
+ if (bus->number == busnr)
return bus;
- list_for_each(tmp, &bus->children) {
- child = pci_do_find_bus(pci_bus_b(tmp), busnr);
- if(child)
+ list_for_each_entry(tmp, &bus->children, node) {
+ child = pci_do_find_bus(tmp, busnr);
+ if (child)
return child;
}
return NULL;
@@ -74,7 +162,7 @@ static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)
* in the global list of PCI buses. If the bus is found, a pointer to its
* data structure is returned. If no bus is found, %NULL is returned.
*/
-struct pci_bus * pci_find_bus(int domain, int busnr)
+struct pci_bus *pci_find_bus(int domain, int busnr)
{
struct pci_bus *bus = NULL;
struct pci_bus *tmp_bus;
@@ -88,18 +176,18 @@ struct pci_bus * pci_find_bus(int domain, int busnr)
}
return NULL;
}
+EXPORT_SYMBOL(pci_find_bus);
/**
* pci_find_next_bus - begin or continue searching for a PCI bus
* @from: Previous PCI bus found, or %NULL for new search.
*
- * Iterates through the list of known PCI busses. A new search is
+ * Iterates through the list of known PCI buses. A new search is
* initiated by passing %NULL as the @from argument. Otherwise if
* @from is not %NULL, searches continue from next device on the
* global list.
*/
-struct pci_bus *
-pci_find_next_bus(const struct pci_bus *from)
+struct pci_bus *pci_find_next_bus(const struct pci_bus *from)
{
struct list_head *n;
struct pci_bus *b = NULL;
@@ -108,63 +196,34 @@ pci_find_next_bus(const struct pci_bus *from)
down_read(&pci_bus_sem);
n = from ? from->node.next : pci_root_buses.next;
if (n != &pci_root_buses)
- b = pci_bus_b(n);
+ b = list_entry(n, struct pci_bus, node);
up_read(&pci_bus_sem);
return b;
}
-
-#ifdef CONFIG_PCI_LEGACY
-
-/**
- * pci_find_slot - locate PCI device from a given PCI slot
- * @bus: number of PCI bus on which desired PCI device resides
- * @devfn: encodes number of PCI slot in which the desired PCI
- * device resides and the logical device number within that slot
- * in case of multi-function devices.
- *
- * Given a PCI bus and slot/function number, the desired PCI device
- * is located in system global list of PCI devices. If the device
- * is found, a pointer to its data structure is returned. If no
- * device is found, %NULL is returned.
- */
-struct pci_dev *
-pci_find_slot(unsigned int bus, unsigned int devfn)
-{
- struct pci_dev *dev = NULL;
-
- while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
- if (dev->bus->number == bus && dev->devfn == devfn)
- return dev;
- }
- return NULL;
-}
-
-#endif /* CONFIG_PCI_LEGACY */
+EXPORT_SYMBOL(pci_find_next_bus);
/**
* pci_get_slot - locate PCI device for a given PCI slot
* @bus: PCI bus on which desired PCI device resides
- * @devfn: encodes number of PCI slot in which the desired PCI
- * device resides and the logical device number within that slot
+ * @devfn: encodes number of PCI slot in which the desired PCI
+ * device resides and the logical device number within that slot
* in case of multi-function devices.
*
- * Given a PCI bus and slot/function number, the desired PCI device
+ * Given a PCI bus and slot/function number, the desired PCI device
* is located in the list of PCI devices.
* If the device is found, its reference count is increased and this
* function returns a pointer to its data structure. The caller must
* decrement the reference count by calling pci_dev_put().
* If no device is found, %NULL is returned.
*/
-struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn)
+struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn)
{
- struct list_head *tmp;
struct pci_dev *dev;
WARN_ON(in_interrupt());
down_read(&pci_bus_sem);
- list_for_each(tmp, &bus->devices) {
- dev = pci_dev_b(tmp);
+ list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->devfn == devfn)
goto out;
}
@@ -175,115 +234,81 @@ struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn)
up_read(&pci_bus_sem);
return dev;
}
+EXPORT_SYMBOL(pci_get_slot);
/**
- * pci_get_bus_and_slot - locate PCI device from a given PCI bus & slot
- * @bus: number of PCI bus on which desired PCI device resides
- * @devfn: encodes number of PCI slot in which the desired PCI
- * device resides and the logical device number within that slot
- * in case of multi-function devices.
- *
- * Note: the bus/slot search is limited to PCI domain (segment) 0.
+ * pci_get_domain_bus_and_slot - locate PCI device for a given PCI domain (segment), bus, and slot
+ * @domain: PCI domain/segment on which the PCI device resides.
+ * @bus: PCI bus on which desired PCI device resides
+ * @devfn: encodes number of PCI slot in which the desired PCI device
+ * resides and the logical device number within that slot in case of
+ * multi-function devices.
*
- * Given a PCI bus and slot/function number, the desired PCI device
- * is located in system global list of PCI devices. If the device
- * is found, a pointer to its data structure is returned. If no
- * device is found, %NULL is returned. The returned device has its
- * reference count bumped by one.
+ * Given a PCI domain, bus, and slot/function number, the desired PCI
+ * device is located in the list of PCI devices. If the device is
+ * found, its reference count is increased and this function returns a
+ * pointer to its data structure. The caller must decrement the
+ * reference count by calling pci_dev_put(). If no device is found,
+ * %NULL is returned.
*/
-
-struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn)
+struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus,
+ unsigned int devfn)
{
struct pci_dev *dev = NULL;
- while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
- if (pci_domain_nr(dev->bus) == 0 &&
- (dev->bus->number == bus && dev->devfn == devfn))
+ for_each_pci_dev(dev) {
+ if (pci_domain_nr(dev->bus) == domain &&
+ (dev->bus->number == bus && dev->devfn == devfn))
return dev;
}
return NULL;
}
+EXPORT_SYMBOL(pci_get_domain_bus_and_slot);
-#ifdef CONFIG_PCI_LEGACY
-/**
- * pci_find_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id
- * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
- * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
- * @ss_vendor: PCI subsystem vendor id to match, or %PCI_ANY_ID to match all vendor ids
- * @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids
- * @from: Previous PCI device found in search, or %NULL for new search.
- *
- * Iterates through the list of known PCI devices. If a PCI device is
- * found with a matching @vendor, @device, @ss_vendor and @ss_device, a
- * pointer to its device structure is returned. Otherwise, %NULL is returned.
- * A new search is initiated by passing %NULL as the @from argument.
- * Otherwise if @from is not %NULL, searches continue from next device
- * on the global list.
- *
- * NOTE: Do not use this function any more; use pci_get_subsys() instead, as
- * the PCI device returned by this function can disappear at any moment in
- * time.
- */
-static struct pci_dev * pci_find_subsys(unsigned int vendor,
- unsigned int device,
- unsigned int ss_vendor,
- unsigned int ss_device,
- const struct pci_dev *from)
+static int match_pci_dev_by_id(struct device *dev, void *data)
{
- struct list_head *n;
- struct pci_dev *dev;
-
- WARN_ON(in_interrupt());
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_device_id *id = data;
- /*
- * pci_find_subsys() can be called on the ide_setup() path, super-early
- * in boot. But the down_read() will enable local interrupts, which
- * can cause some machines to crash. So here we detect and flag that
- * situation and bail out early.
- */
- if (unlikely(no_pci_devices()))
- return NULL;
- down_read(&pci_bus_sem);
- n = from ? from->global_list.next : pci_devices.next;
-
- while (n && (n != &pci_devices)) {
- dev = pci_dev_g(n);
- if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
- (device == PCI_ANY_ID || dev->device == device) &&
- (ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) &&
- (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device))
- goto exit;
- n = n->next;
- }
- dev = NULL;
-exit:
- up_read(&pci_bus_sem);
- return dev;
+ if (pci_match_one_device(id, pdev))
+ return 1;
+ return 0;
}
-/**
- * pci_find_device - begin or continue searching for a PCI device by vendor/device id
- * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
- * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
+/*
+ * pci_get_dev_by_id - begin or continue searching for a PCI device by id
+ * @id: pointer to struct pci_device_id to match for the device
* @from: Previous PCI device found in search, or %NULL for new search.
*
* Iterates through the list of known PCI devices. If a PCI device is found
- * with a matching @vendor and @device, a pointer to its device structure is
- * returned. Otherwise, %NULL is returned.
- * A new search is initiated by passing %NULL as the @from argument.
- * Otherwise if @from is not %NULL, searches continue from next device
- * on the global list.
- *
- * NOTE: Do not use this function any more; use pci_get_device() instead, as
- * the PCI device returned by this function can disappear at any moment in
- * time.
+ * with a matching id a pointer to its device structure is returned, and the
+ * reference count to the device is incremented. Otherwise, %NULL is returned.
+ * A new search is initiated by passing %NULL as the @from argument. Otherwise
+ * if @from is not %NULL, searches continue from next device on the global
+ * list. The reference count for @from is always decremented if it is not
+ * %NULL.
+ *
+ * This is an internal function for use by the other search functions in
+ * this file.
*/
-struct pci_dev *
-pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *from)
+static struct pci_dev *pci_get_dev_by_id(const struct pci_device_id *id,
+ struct pci_dev *from)
{
- return pci_find_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
+ struct device *dev;
+ struct device *dev_start = NULL;
+ struct pci_dev *pdev = NULL;
+
+ WARN_ON(in_interrupt());
+ if (from)
+ dev_start = &from->dev;
+ dev = bus_find_device(&pci_bus_type, dev_start, (void *)id,
+ match_pci_dev_by_id);
+ if (dev)
+ pdev = to_pci_dev(dev);
+ if (from)
+ pci_dev_put(from);
+ return pdev;
}
-#endif /* CONFIG_PCI_LEGACY */
/**
* pci_get_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id
@@ -301,43 +326,20 @@ pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *
* searches continue from next device on the global list.
* The reference count for @from is always decremented if it is not %NULL.
*/
-struct pci_dev *
-pci_get_subsys(unsigned int vendor, unsigned int device,
- unsigned int ss_vendor, unsigned int ss_device,
- struct pci_dev *from)
+struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,
+ unsigned int ss_vendor, unsigned int ss_device,
+ struct pci_dev *from)
{
- struct list_head *n;
- struct pci_dev *dev;
-
- WARN_ON(in_interrupt());
-
- /*
- * pci_get_subsys() can potentially be called by drivers super-early
- * in boot. But the down_read() will enable local interrupts, which
- * can cause some machines to crash. So here we detect and flag that
- * situation and bail out early.
- */
- if (unlikely(no_pci_devices()))
- return NULL;
- down_read(&pci_bus_sem);
- n = from ? from->global_list.next : pci_devices.next;
-
- while (n && (n != &pci_devices)) {
- dev = pci_dev_g(n);
- if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
- (device == PCI_ANY_ID || dev->device == device) &&
- (ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) &&
- (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device))
- goto exit;
- n = n->next;
- }
- dev = NULL;
-exit:
- dev = pci_dev_get(dev);
- up_read(&pci_bus_sem);
- pci_dev_put(from);
- return dev;
+ struct pci_device_id id = {
+ .vendor = vendor,
+ .device = device,
+ .subvendor = ss_vendor,
+ .subdevice = ss_device,
+ };
+
+ return pci_get_dev_by_id(&id, from);
}
+EXPORT_SYMBOL(pci_get_subsys);
/**
* pci_get_device - begin or continue searching for a PCI device by vendor/device id
@@ -353,51 +355,12 @@ exit:
* from next device on the global list. The reference count for @from is
* always decremented if it is not %NULL.
*/
-struct pci_dev *
-pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
+struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device,
+ struct pci_dev *from)
{
return pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
}
-
-/**
- * pci_get_device_reverse - begin or continue searching for a PCI device by vendor/device id
- * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
- * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
- * @from: Previous PCI device found in search, or %NULL for new search.
- *
- * Iterates through the list of known PCI devices in the reverse order of
- * pci_get_device.
- * If a PCI device is found with a matching @vendor and @device, the reference
- * count to the device is incremented and a pointer to its device structure
- * is returned Otherwise, %NULL is returned. A new search is initiated by
- * passing %NULL as the @from argument. Otherwise if @from is not %NULL,
- * searches continue from next device on the global list. The reference
- * count for @from is always decremented if it is not %NULL.
- */
-struct pci_dev *
-pci_get_device_reverse(unsigned int vendor, unsigned int device, struct pci_dev *from)
-{
- struct list_head *n;
- struct pci_dev *dev;
-
- WARN_ON(in_interrupt());
- down_read(&pci_bus_sem);
- n = from ? from->global_list.prev : pci_devices.prev;
-
- while (n && (n != &pci_devices)) {
- dev = pci_dev_g(n);
- if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
- (device == PCI_ANY_ID || dev->device == device))
- goto exit;
- n = n->prev;
- }
- dev = NULL;
-exit:
- dev = pci_dev_get(dev);
- up_read(&pci_bus_sem);
- pci_dev_put(from);
- return dev;
-}
+EXPORT_SYMBOL(pci_get_device);
/**
* pci_get_class - begin or continue searching for a PCI device by class
@@ -415,45 +378,18 @@ exit:
*/
struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from)
{
- struct list_head *n;
- struct pci_dev *dev;
-
- WARN_ON(in_interrupt());
- down_read(&pci_bus_sem);
- n = from ? from->global_list.next : pci_devices.next;
-
- while (n && (n != &pci_devices)) {
- dev = pci_dev_g(n);
- if (dev->class == class)
- goto exit;
- n = n->next;
- }
- dev = NULL;
-exit:
- dev = pci_dev_get(dev);
- up_read(&pci_bus_sem);
- pci_dev_put(from);
- return dev;
-}
-
-const struct pci_device_id *pci_find_present(const struct pci_device_id *ids)
-{
- struct pci_dev *dev;
- const struct pci_device_id *found = NULL;
-
- WARN_ON(in_interrupt());
- down_read(&pci_bus_sem);
- while (ids->vendor || ids->subvendor || ids->class_mask) {
- list_for_each_entry(dev, &pci_devices, global_list) {
- if ((found = pci_match_one_device(ids, dev)) != NULL)
- goto exit;
- }
- ids++;
- }
-exit:
- up_read(&pci_bus_sem);
- return found;
+ struct pci_device_id id = {
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class_mask = PCI_ANY_ID,
+ .class = class,
+ };
+
+ return pci_get_dev_by_id(&id, from);
}
+EXPORT_SYMBOL(pci_get_class);
/**
* pci_dev_present - Returns 1 if device matching the device list is present, 0 if not.
@@ -468,24 +404,18 @@ exit:
*/
int pci_dev_present(const struct pci_device_id *ids)
{
- return pci_find_present(ids) == NULL ? 0 : 1;
-}
-
-EXPORT_SYMBOL(pci_dev_present);
-EXPORT_SYMBOL(pci_find_present);
+ struct pci_dev *found = NULL;
-#ifdef CONFIG_PCI_LEGACY
-EXPORT_SYMBOL(pci_find_device);
-EXPORT_SYMBOL(pci_find_slot);
-#endif /* CONFIG_PCI_LEGACY */
+ WARN_ON(in_interrupt());
+ while (ids->vendor || ids->subvendor || ids->class_mask) {
+ found = pci_get_dev_by_id(ids, NULL);
+ if (found) {
+ pci_dev_put(found);
+ return 1;
+ }
+ ids++;
+ }
-/* For boot time work */
-EXPORT_SYMBOL(pci_find_bus);
-EXPORT_SYMBOL(pci_find_next_bus);
-/* For everyone */
-EXPORT_SYMBOL(pci_get_device);
-EXPORT_SYMBOL(pci_get_device_reverse);
-EXPORT_SYMBOL(pci_get_subsys);
-EXPORT_SYMBOL(pci_get_slot);
-EXPORT_SYMBOL(pci_get_bus_and_slot);
-EXPORT_SYMBOL(pci_get_class);
+ return 0;
+}
+EXPORT_SYMBOL(pci_dev_present);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index f7cb8e0758b..a5a63ecfb62 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -25,106 +25,492 @@
#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/slab.h>
+#include <asm-generic/pci-bridge.h>
+#include "pci.h"
+unsigned int pci_flags;
-#define DEBUG_CONFIG 1
-#if DEBUG_CONFIG
-#define DBG(x...) printk(x)
-#else
-#define DBG(x...)
-#endif
+struct pci_dev_resource {
+ struct list_head list;
+ struct resource *res;
+ struct pci_dev *dev;
+ resource_size_t start;
+ resource_size_t end;
+ resource_size_t add_size;
+ resource_size_t min_align;
+ unsigned long flags;
+};
+
+static void free_list(struct list_head *head)
+{
+ struct pci_dev_resource *dev_res, *tmp;
+
+ list_for_each_entry_safe(dev_res, tmp, head, list) {
+ list_del(&dev_res->list);
+ kfree(dev_res);
+ }
+}
-static void pbus_assign_resources_sorted(struct pci_bus *bus)
+/**
+ * add_to_list() - add a new resource tracker to the list
+ * @head: Head of the list
+ * @dev: device corresponding to which the resource
+ * belongs
+ * @res: The resource to be tracked
+ * @add_size: additional size to be optionally added
+ * to the resource
+ */
+static int add_to_list(struct list_head *head,
+ struct pci_dev *dev, struct resource *res,
+ resource_size_t add_size, resource_size_t min_align)
+{
+ struct pci_dev_resource *tmp;
+
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp) {
+ pr_warn("add_to_list: kmalloc() failed!\n");
+ return -ENOMEM;
+ }
+
+ tmp->res = res;
+ tmp->dev = dev;
+ tmp->start = res->start;
+ tmp->end = res->end;
+ tmp->flags = res->flags;
+ tmp->add_size = add_size;
+ tmp->min_align = min_align;
+
+ list_add(&tmp->list, head);
+
+ return 0;
+}
+
+static void remove_from_list(struct list_head *head,
+ struct resource *res)
+{
+ struct pci_dev_resource *dev_res, *tmp;
+
+ list_for_each_entry_safe(dev_res, tmp, head, list) {
+ if (dev_res->res == res) {
+ list_del(&dev_res->list);
+ kfree(dev_res);
+ break;
+ }
+ }
+}
+
+static resource_size_t get_res_add_size(struct list_head *head,
+ struct resource *res)
+{
+ struct pci_dev_resource *dev_res;
+
+ list_for_each_entry(dev_res, head, list) {
+ if (dev_res->res == res) {
+ int idx = res - &dev_res->dev->resource[0];
+
+ dev_printk(KERN_DEBUG, &dev_res->dev->dev,
+ "res[%d]=%pR get_res_add_size add_size %llx\n",
+ idx, dev_res->res,
+ (unsigned long long)dev_res->add_size);
+
+ return dev_res->add_size;
+ }
+ }
+
+ return 0;
+}
+
+/* Sort resources by alignment */
+static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head)
+{
+ int i;
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ struct resource *r;
+ struct pci_dev_resource *dev_res, *tmp;
+ resource_size_t r_align;
+ struct list_head *n;
+
+ r = &dev->resource[i];
+
+ if (r->flags & IORESOURCE_PCI_FIXED)
+ continue;
+
+ if (!(r->flags) || r->parent)
+ continue;
+
+ r_align = pci_resource_alignment(dev, r);
+ if (!r_align) {
+ dev_warn(&dev->dev, "BAR %d: %pR has bogus alignment\n",
+ i, r);
+ continue;
+ }
+
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ panic("pdev_sort_resources(): kmalloc() failed!\n");
+ tmp->res = r;
+ tmp->dev = dev;
+
+ /* fallback is smallest one or list is empty*/
+ n = head;
+ list_for_each_entry(dev_res, head, list) {
+ resource_size_t align;
+
+ align = pci_resource_alignment(dev_res->dev,
+ dev_res->res);
+
+ if (r_align > align) {
+ n = &dev_res->list;
+ break;
+ }
+ }
+ /* Insert it just before n*/
+ list_add_tail(&tmp->list, n);
+ }
+}
+
+static void __dev_sort_resources(struct pci_dev *dev,
+ struct list_head *head)
+{
+ u16 class = dev->class >> 8;
+
+ /* Don't touch classless devices or host bridges or ioapics. */
+ if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST)
+ return;
+
+ /* Don't touch ioapic devices already enabled by firmware */
+ if (class == PCI_CLASS_SYSTEM_PIC) {
+ u16 command;
+ pci_read_config_word(dev, PCI_COMMAND, &command);
+ if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
+ return;
+ }
+
+ pdev_sort_resources(dev, head);
+}
+
+static inline void reset_resource(struct resource *res)
+{
+ res->start = 0;
+ res->end = 0;
+ res->flags = 0;
+}
+
+/**
+ * reassign_resources_sorted() - satisfy any additional resource requests
+ *
+ * @realloc_head : head of the list tracking requests requiring additional
+ * resources
+ * @head : head of the list tracking requests with allocated
+ * resources
+ *
+ * Walk through each element of the realloc_head and try to procure
+ * additional resources for the element, provided the element
+ * is in the head list.
+ */
+static void reassign_resources_sorted(struct list_head *realloc_head,
+ struct list_head *head)
{
- struct pci_dev *dev;
struct resource *res;
- struct resource_list head, *list, *tmp;
+ struct pci_dev_resource *add_res, *tmp;
+ struct pci_dev_resource *dev_res;
+ resource_size_t add_size;
int idx;
- head.next = NULL;
- list_for_each_entry(dev, &bus->devices, bus_list) {
- u16 class = dev->class >> 8;
+ list_for_each_entry_safe(add_res, tmp, realloc_head, list) {
+ bool found_match = false;
+
+ res = add_res->res;
+ /* skip resource that has been reset */
+ if (!res->flags)
+ goto out;
- /* Don't touch classless devices or host bridges or ioapics. */
- if (class == PCI_CLASS_NOT_DEFINED ||
- class == PCI_CLASS_BRIDGE_HOST)
+ /* skip this resource if not found in head list */
+ list_for_each_entry(dev_res, head, list) {
+ if (dev_res->res == res) {
+ found_match = true;
+ break;
+ }
+ }
+ if (!found_match)/* just skip */
continue;
- /* Don't touch ioapic devices already enabled by firmware */
- if (class == PCI_CLASS_SYSTEM_PIC) {
- u16 command;
- pci_read_config_word(dev, PCI_COMMAND, &command);
- if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
- continue;
+ idx = res - &add_res->dev->resource[0];
+ add_size = add_res->add_size;
+ if (!resource_size(res)) {
+ res->start = add_res->start;
+ res->end = res->start + add_size - 1;
+ if (pci_assign_resource(add_res->dev, idx))
+ reset_resource(res);
+ } else {
+ resource_size_t align = add_res->min_align;
+ res->flags |= add_res->flags &
+ (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN);
+ if (pci_reassign_resource(add_res->dev, idx,
+ add_size, align))
+ dev_printk(KERN_DEBUG, &add_res->dev->dev,
+ "failed to add %llx res[%d]=%pR\n",
+ (unsigned long long)add_size,
+ idx, res);
+ }
+out:
+ list_del(&add_res->list);
+ kfree(add_res);
+ }
+}
+
+/**
+ * assign_requested_resources_sorted() - satisfy resource requests
+ *
+ * @head : head of the list tracking requests for resources
+ * @fail_head : head of the list tracking requests that could
+ * not be allocated
+ *
+ * Satisfy resource requests of each element in the list. Add
+ * requests that could not satisfied to the failed_list.
+ */
+static void assign_requested_resources_sorted(struct list_head *head,
+ struct list_head *fail_head)
+{
+ struct resource *res;
+ struct pci_dev_resource *dev_res;
+ int idx;
+
+ list_for_each_entry(dev_res, head, list) {
+ res = dev_res->res;
+ idx = res - &dev_res->dev->resource[0];
+ if (resource_size(res) &&
+ pci_assign_resource(dev_res->dev, idx)) {
+ if (fail_head) {
+ /*
+ * if the failed res is for ROM BAR, and it will
+ * be enabled later, don't add it to the list
+ */
+ if (!((idx == PCI_ROM_RESOURCE) &&
+ (!(res->flags & IORESOURCE_ROM_ENABLE))))
+ add_to_list(fail_head,
+ dev_res->dev, res,
+ 0 /* don't care */,
+ 0 /* don't care */);
+ }
+ reset_resource(res);
}
+ }
+}
+
+static unsigned long pci_fail_res_type_mask(struct list_head *fail_head)
+{
+ struct pci_dev_resource *fail_res;
+ unsigned long mask = 0;
+
+ /* check failed type */
+ list_for_each_entry(fail_res, fail_head, list)
+ mask |= fail_res->flags;
- pdev_sort_resources(dev, &head);
+ /*
+ * one pref failed resource will set IORESOURCE_MEM,
+ * as we can allocate pref in non-pref range.
+ * Will release all assigned non-pref sibling resources
+ * according to that bit.
+ */
+ return mask & (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH);
+}
+
+static bool pci_need_to_release(unsigned long mask, struct resource *res)
+{
+ if (res->flags & IORESOURCE_IO)
+ return !!(mask & IORESOURCE_IO);
+
+ /* check pref at first */
+ if (res->flags & IORESOURCE_PREFETCH) {
+ if (mask & IORESOURCE_PREFETCH)
+ return true;
+ /* count pref if its parent is non-pref */
+ else if ((mask & IORESOURCE_MEM) &&
+ !(res->parent->flags & IORESOURCE_PREFETCH))
+ return true;
+ else
+ return false;
}
- for (list = head.next; list;) {
- res = list->res;
- idx = res - &list->dev->resource[0];
- if (pci_assign_resource(list->dev, idx)) {
- res->start = 0;
- res->end = 0;
- res->flags = 0;
+ if (res->flags & IORESOURCE_MEM)
+ return !!(mask & IORESOURCE_MEM);
+
+ return false; /* should not get here */
+}
+
+static void __assign_resources_sorted(struct list_head *head,
+ struct list_head *realloc_head,
+ struct list_head *fail_head)
+{
+ /*
+ * Should not assign requested resources at first.
+ * they could be adjacent, so later reassign can not reallocate
+ * them one by one in parent resource window.
+ * Try to assign requested + add_size at beginning
+ * if could do that, could get out early.
+ * if could not do that, we still try to assign requested at first,
+ * then try to reassign add_size for some resources.
+ *
+ * Separate three resource type checking if we need to release
+ * assigned resource after requested + add_size try.
+ * 1. if there is io port assign fail, will release assigned
+ * io port.
+ * 2. if there is pref mmio assign fail, release assigned
+ * pref mmio.
+ * if assigned pref mmio's parent is non-pref mmio and there
+ * is non-pref mmio assign fail, will release that assigned
+ * pref mmio.
+ * 3. if there is non-pref mmio assign fail or pref mmio
+ * assigned fail, will release assigned non-pref mmio.
+ */
+ LIST_HEAD(save_head);
+ LIST_HEAD(local_fail_head);
+ struct pci_dev_resource *save_res;
+ struct pci_dev_resource *dev_res, *tmp_res;
+ unsigned long fail_type;
+
+ /* Check if optional add_size is there */
+ if (!realloc_head || list_empty(realloc_head))
+ goto requested_and_reassign;
+
+ /* Save original start, end, flags etc at first */
+ list_for_each_entry(dev_res, head, list) {
+ if (add_to_list(&save_head, dev_res->dev, dev_res->res, 0, 0)) {
+ free_list(&save_head);
+ goto requested_and_reassign;
+ }
+ }
+
+ /* Update res in head list with add_size in realloc_head list */
+ list_for_each_entry(dev_res, head, list)
+ dev_res->res->end += get_res_add_size(realloc_head,
+ dev_res->res);
+
+ /* Try updated head list with add_size added */
+ assign_requested_resources_sorted(head, &local_fail_head);
+
+ /* all assigned with add_size ? */
+ if (list_empty(&local_fail_head)) {
+ /* Remove head list from realloc_head list */
+ list_for_each_entry(dev_res, head, list)
+ remove_from_list(realloc_head, dev_res->res);
+ free_list(&save_head);
+ free_list(head);
+ return;
+ }
+
+ /* check failed type */
+ fail_type = pci_fail_res_type_mask(&local_fail_head);
+ /* remove not need to be released assigned res from head list etc */
+ list_for_each_entry_safe(dev_res, tmp_res, head, list)
+ if (dev_res->res->parent &&
+ !pci_need_to_release(fail_type, dev_res->res)) {
+ /* remove it from realloc_head list */
+ remove_from_list(realloc_head, dev_res->res);
+ remove_from_list(&save_head, dev_res->res);
+ list_del(&dev_res->list);
+ kfree(dev_res);
}
- tmp = list;
- list = list->next;
- kfree(tmp);
+
+ free_list(&local_fail_head);
+ /* Release assigned resource */
+ list_for_each_entry(dev_res, head, list)
+ if (dev_res->res->parent)
+ release_resource(dev_res->res);
+ /* Restore start/end/flags from saved list */
+ list_for_each_entry(save_res, &save_head, list) {
+ struct resource *res = save_res->res;
+
+ res->start = save_res->start;
+ res->end = save_res->end;
+ res->flags = save_res->flags;
}
+ free_list(&save_head);
+
+requested_and_reassign:
+ /* Satisfy the must-have resource requests */
+ assign_requested_resources_sorted(head, fail_head);
+
+ /* Try to satisfy any additional optional resource
+ requests */
+ if (realloc_head)
+ reassign_resources_sorted(realloc_head, head);
+ free_list(head);
+}
+
+static void pdev_assign_resources_sorted(struct pci_dev *dev,
+ struct list_head *add_head,
+ struct list_head *fail_head)
+{
+ LIST_HEAD(head);
+
+ __dev_sort_resources(dev, &head);
+ __assign_resources_sorted(&head, add_head, fail_head);
+
+}
+
+static void pbus_assign_resources_sorted(const struct pci_bus *bus,
+ struct list_head *realloc_head,
+ struct list_head *fail_head)
+{
+ struct pci_dev *dev;
+ LIST_HEAD(head);
+
+ list_for_each_entry(dev, &bus->devices, bus_list)
+ __dev_sort_resources(dev, &head);
+
+ __assign_resources_sorted(&head, realloc_head, fail_head);
}
void pci_setup_cardbus(struct pci_bus *bus)
{
struct pci_dev *bridge = bus->self;
+ struct resource *res;
struct pci_bus_region region;
- printk("PCI: Bus %d, cardbus bridge: %s\n",
- bus->number, pci_name(bridge));
+ dev_info(&bridge->dev, "CardBus bridge to %pR\n",
+ &bus->busn_res);
- pcibios_resource_to_bus(bridge, &region, bus->resource[0]);
- if (bus->resource[0]->flags & IORESOURCE_IO) {
+ res = bus->resource[0];
+ pcibios_resource_to_bus(bridge->bus, &region, res);
+ if (res->flags & IORESOURCE_IO) {
/*
* 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",
- (unsigned long)region.start,
- (unsigned long)region.end);
+ dev_info(&bridge->dev, " bridge window %pR\n", res);
pci_write_config_dword(bridge, PCI_CB_IO_BASE_0,
region.start);
pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0,
region.end);
}
- 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",
- (unsigned long)region.start,
- (unsigned long)region.end);
+ res = bus->resource[1];
+ pcibios_resource_to_bus(bridge->bus, &region, res);
+ if (res->flags & IORESOURCE_IO) {
+ dev_info(&bridge->dev, " bridge window %pR\n", res);
pci_write_config_dword(bridge, PCI_CB_IO_BASE_1,
region.start);
pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1,
region.end);
}
- 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",
- (unsigned long)region.start,
- (unsigned long)region.end);
+ res = bus->resource[2];
+ pcibios_resource_to_bus(bridge->bus, &region, res);
+ if (res->flags & IORESOURCE_MEM) {
+ dev_info(&bridge->dev, " bridge window %pR\n", res);
pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0,
region.start);
pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0,
region.end);
}
- 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",
- (unsigned long)region.start,
- (unsigned long)region.end);
+ res = bus->resource[3];
+ pcibios_resource_to_bus(bridge->bus, &region, res);
+ if (res->flags & IORESOURCE_MEM) {
+ dev_info(&bridge->dev, " bridge window %pR\n", res);
pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1,
region.start);
pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1,
@@ -144,56 +530,70 @@ EXPORT_SYMBOL(pci_setup_cardbus);
config space writes, so it's quite possible that an I/O window of
the bridge will have some undesirable address (e.g. 0) after the
first write. Ditto 64-bit prefetchable MMIO. */
-static void __devinit
-pci_setup_bridge(struct pci_bus *bus)
+static void pci_setup_bridge_io(struct pci_bus *bus)
{
struct pci_dev *bridge = bus->self;
+ struct resource *res;
struct pci_bus_region region;
- u32 l, bu, lu, io_upper16;
+ unsigned long io_mask;
+ u8 io_base_lo, io_limit_lo;
+ u16 l;
+ u32 io_upper16;
- DBG(KERN_INFO "PCI: Bridge: %s\n", pci_name(bridge));
+ io_mask = PCI_IO_RANGE_MASK;
+ if (bridge->io_window_1k)
+ io_mask = PCI_IO_1K_RANGE_MASK;
/* Set up the top and bottom of the PCI I/O segment for this bus. */
- pcibios_resource_to_bus(bridge, &region, bus->resource[0]);
- if (bus->resource[0]->flags & IORESOURCE_IO) {
- pci_read_config_dword(bridge, PCI_IO_BASE, &l);
- l &= 0xffff0000;
- l |= (region.start >> 8) & 0x00f0;
- l |= region.end & 0xf000;
+ res = bus->resource[0];
+ pcibios_resource_to_bus(bridge->bus, &region, res);
+ if (res->flags & IORESOURCE_IO) {
+ pci_read_config_word(bridge, PCI_IO_BASE, &l);
+ io_base_lo = (region.start >> 8) & io_mask;
+ io_limit_lo = (region.end >> 8) & io_mask;
+ l = ((u16) io_limit_lo << 8) | io_base_lo;
/* 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",
- (unsigned long)region.start,
- (unsigned long)region.end);
- }
- else {
+ dev_info(&bridge->dev, " bridge window %pR\n", res);
+ } else {
/* Clear upper 16 bits of I/O base/limit. */
io_upper16 = 0;
l = 0x00f0;
- DBG(KERN_INFO " 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);
/* Update lower 16 bits of I/O base/limit. */
- pci_write_config_dword(bridge, PCI_IO_BASE, l);
+ pci_write_config_word(bridge, PCI_IO_BASE, l);
/* Update upper 16 bits of I/O base/limit. */
pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16);
+}
- /* Set up the top and bottom of the PCI Memory segment
- for this bus. */
- pcibios_resource_to_bus(bridge, &region, bus->resource[1]);
- if (bus->resource[1]->flags & IORESOURCE_MEM) {
+static void pci_setup_bridge_mmio(struct pci_bus *bus)
+{
+ struct pci_dev *bridge = bus->self;
+ struct resource *res;
+ struct pci_bus_region region;
+ u32 l;
+
+ /* Set up the top and bottom of the PCI Memory segment for this bus. */
+ res = bus->resource[1];
+ pcibios_resource_to_bus(bridge->bus, &region, res);
+ if (res->flags & IORESOURCE_MEM) {
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
- DBG(KERN_INFO " MEM window: 0x%08lx-0x%08lx\n",
- (unsigned long)region.start,
- (unsigned long)region.end);
- }
- else {
+ dev_info(&bridge->dev, " bridge window %pR\n", res);
+ } else {
l = 0x0000fff0;
- DBG(KERN_INFO " MEM window: disabled.\n");
}
pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
+}
+
+static void pci_setup_bridge_mmio_pref(struct pci_bus *bus)
+{
+ struct pci_dev *bridge = bus->self;
+ struct resource *res;
+ struct pci_bus_region region;
+ u32 l, bu, lu;
/* Clear out the upper 32 bits of PREF limit.
If PCI_PREF_BASE_UPPER32 was non-zero, this temporarily
@@ -202,29 +602,53 @@ pci_setup_bridge(struct pci_bus *bus)
/* Set up PREF base/limit. */
bu = lu = 0;
- pcibios_resource_to_bus(bridge, &region, bus->resource[2]);
- if (bus->resource[2]->flags & IORESOURCE_PREFETCH) {
+ res = bus->resource[2];
+ pcibios_resource_to_bus(bridge->bus, &region, res);
+ if (res->flags & IORESOURCE_PREFETCH) {
l = (region.start >> 16) & 0xfff0;
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",
- (unsigned long long)region.start,
- (unsigned long long)region.end);
- }
- else {
+ if (res->flags & IORESOURCE_MEM_64) {
+ bu = upper_32_bits(region.start);
+ lu = upper_32_bits(region.end);
+ }
+ dev_info(&bridge->dev, " bridge window %pR\n", res);
+ } else {
l = 0x0000fff0;
- DBG(KERN_INFO " PREFETCH window: disabled.\n");
}
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
/* Set the upper 32 bits of PREF base & limit. */
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
+}
+
+static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type)
+{
+ struct pci_dev *bridge = bus->self;
+
+ dev_info(&bridge->dev, "PCI bridge to %pR\n",
+ &bus->busn_res);
+
+ if (type & IORESOURCE_IO)
+ pci_setup_bridge_io(bus);
+
+ if (type & IORESOURCE_MEM)
+ pci_setup_bridge_mmio(bus);
+
+ if (type & IORESOURCE_PREFETCH)
+ pci_setup_bridge_mmio_pref(bus);
pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
}
+void pci_setup_bridge(struct pci_bus *bus)
+{
+ unsigned long type = IORESOURCE_IO | IORESOURCE_MEM |
+ IORESOURCE_PREFETCH;
+
+ __pci_setup_bridge(bus, type);
+}
+
/* Check whether the bridge supports optional I/O and
prefetchable memory ranges. If not, the respective
base/limit registers must be read-only and read as 0. */
@@ -240,41 +664,61 @@ static void pci_bridge_check_ranges(struct pci_bus *bus)
pci_read_config_word(bridge, PCI_IO_BASE, &io);
if (!io) {
- pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0);
+ pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0);
pci_read_config_word(bridge, PCI_IO_BASE, &io);
- pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
- }
- if (io)
+ pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
+ }
+ if (io)
b_res[0].flags |= IORESOURCE_IO;
+
/* DECchip 21050 pass 2 errata: the bridge may miss an address
disconnect boundary by one PCI data phase.
Workaround: do not use prefetching on this device. */
if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001)
return;
+
pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
if (!pmem) {
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE,
- 0xfff0fff0);
+ 0xffe0fff0);
pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);
}
- if (pmem)
+ if (pmem) {
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ if ((pmem & PCI_PREF_RANGE_TYPE_MASK) ==
+ PCI_PREF_RANGE_TYPE_64) {
+ b_res[2].flags |= IORESOURCE_MEM_64;
+ b_res[2].flags |= PCI_PREF_RANGE_TYPE_64;
+ }
+ }
+
+ /* double check if bridge does support 64 bit pref */
+ if (b_res[2].flags & IORESOURCE_MEM_64) {
+ u32 mem_base_hi, tmp;
+ pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32,
+ &mem_base_hi);
+ pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
+ 0xffffffff);
+ pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp);
+ if (!tmp)
+ b_res[2].flags &= ~IORESOURCE_MEM_64;
+ pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
+ mem_base_hi);
+ }
}
/* Helper function for sizing routines: find first available
bus resource of a given type. Note: we intentionally skip
the bus resources which have already been assigned (that is,
have non-NULL parent resource). */
-static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned long type)
+static struct resource *find_free_bus_resource(struct pci_bus *bus,
+ unsigned long type_mask, unsigned long type)
{
int i;
struct resource *r;
- unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
- IORESOURCE_PREFETCH;
- for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
- r = bus->resource[i];
+ pci_bus_for_each_resource(bus, r, i) {
if (r == &ioport_resource || r == &iomem_resource)
continue;
if (r && (r->flags & type_mask) == type && !r->parent)
@@ -283,19 +727,102 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned lon
return NULL;
}
-/* Sizing the IO windows of the PCI-PCI bridge is trivial,
- since these windows have 4K granularity and the IO ranges
- of non-bridge PCI devices are limited to 256 bytes.
- We must be careful with the ISA aliasing though. */
-static void pbus_size_io(struct pci_bus *bus)
+static resource_size_t calculate_iosize(resource_size_t size,
+ resource_size_t min_size,
+ resource_size_t size1,
+ resource_size_t old_size,
+ resource_size_t align)
+{
+ if (size < min_size)
+ size = min_size;
+ if (old_size == 1)
+ old_size = 0;
+ /* To be fixed in 2.5: we should have sort of HAVE_ISA
+ flag in the struct pci_bus. */
+#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
+ size = (size & 0xff) + ((size & ~0xffUL) << 2);
+#endif
+ size = ALIGN(size + size1, align);
+ if (size < old_size)
+ size = old_size;
+ return size;
+}
+
+static resource_size_t calculate_memsize(resource_size_t size,
+ resource_size_t min_size,
+ resource_size_t size1,
+ resource_size_t old_size,
+ resource_size_t align)
+{
+ if (size < min_size)
+ size = min_size;
+ if (old_size == 1)
+ old_size = 0;
+ if (size < old_size)
+ size = old_size;
+ size = ALIGN(size + size1, align);
+ return size;
+}
+
+resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus,
+ unsigned long type)
+{
+ return 1;
+}
+
+#define PCI_P2P_DEFAULT_MEM_ALIGN 0x100000 /* 1MiB */
+#define PCI_P2P_DEFAULT_IO_ALIGN 0x1000 /* 4KiB */
+#define PCI_P2P_DEFAULT_IO_ALIGN_1K 0x400 /* 1KiB */
+
+static resource_size_t window_alignment(struct pci_bus *bus,
+ unsigned long type)
+{
+ resource_size_t align = 1, arch_align;
+
+ if (type & IORESOURCE_MEM)
+ align = PCI_P2P_DEFAULT_MEM_ALIGN;
+ else if (type & IORESOURCE_IO) {
+ /*
+ * Per spec, I/O windows are 4K-aligned, but some
+ * bridges have an extension to support 1K alignment.
+ */
+ if (bus->self->io_window_1k)
+ align = PCI_P2P_DEFAULT_IO_ALIGN_1K;
+ else
+ align = PCI_P2P_DEFAULT_IO_ALIGN;
+ }
+
+ arch_align = pcibios_window_alignment(bus, type);
+ return max(align, arch_align);
+}
+
+/**
+ * pbus_size_io() - size the io window of a given bus
+ *
+ * @bus : the bus
+ * @min_size : the minimum io window that must to be allocated
+ * @add_size : additional optional io window
+ * @realloc_head : track the additional io window on this list
+ *
+ * Sizing the IO windows of the PCI-PCI bridge is trivial,
+ * since these windows have 1K or 4K granularity and the IO ranges
+ * of non-bridge PCI devices are limited to 256 bytes.
+ * We must be careful with the ISA aliasing though.
+ */
+static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
+ resource_size_t add_size, struct list_head *realloc_head)
{
struct pci_dev *dev;
- struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
- unsigned long size = 0, size1 = 0;
+ struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO,
+ IORESOURCE_IO);
+ resource_size_t size = 0, size0 = 0, size1 = 0;
+ resource_size_t children_add_size = 0;
+ resource_size_t min_align, align;
if (!b_res)
- return;
+ return;
+ min_align = window_alignment(bus, IORESOURCE_IO);
list_for_each_entry(dev, &bus->devices, bus_list) {
int i;
@@ -305,42 +832,107 @@ static void pbus_size_io(struct pci_bus *bus)
if (r->parent || !(r->flags & IORESOURCE_IO))
continue;
- r_size = r->end - r->start + 1;
+ r_size = resource_size(r);
if (r_size < 0x400)
/* Might be re-aligned for ISA */
size += r_size;
else
size1 += r_size;
+
+ align = pci_resource_alignment(dev, r);
+ if (align > min_align)
+ min_align = align;
+
+ if (realloc_head)
+ children_add_size += get_res_add_size(realloc_head, r);
}
}
-/* To be fixed in 2.5: we should have sort of HAVE_ISA
- flag in the struct pci_bus. */
-#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
- size = (size & 0xff) + ((size & ~0xffUL) << 2);
-#endif
- size = ALIGN(size + size1, 4096);
- if (!size) {
+
+ size0 = calculate_iosize(size, min_size, size1,
+ resource_size(b_res), min_align);
+ if (children_add_size > add_size)
+ add_size = children_add_size;
+ size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
+ calculate_iosize(size, min_size, add_size + size1,
+ resource_size(b_res), min_align);
+ if (!size0 && !size1) {
+ if (b_res->start || b_res->end)
+ dev_info(&bus->self->dev, "disabling bridge window %pR to %pR (unused)\n",
+ b_res, &bus->busn_res);
b_res->flags = 0;
return;
}
- /* Alignment of the IO window is always 4K */
- b_res->start = 4096;
- b_res->end = b_res->start + size - 1;
+
+ b_res->start = min_align;
+ b_res->end = b_res->start + size0 - 1;
+ b_res->flags |= IORESOURCE_STARTALIGN;
+ if (size1 > size0 && realloc_head) {
+ add_to_list(realloc_head, bus->self, b_res, size1-size0,
+ min_align);
+ dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window %pR to %pR add_size %llx\n",
+ b_res, &bus->busn_res,
+ (unsigned long long)size1-size0);
+ }
}
-/* Calculate the size of the bus and minimal alignment which
- guarantees that all child resources fit in this size. */
-static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type)
+static inline resource_size_t calculate_mem_align(resource_size_t *aligns,
+ int max_order)
+{
+ resource_size_t align = 0;
+ resource_size_t min_align = 0;
+ int order;
+
+ for (order = 0; order <= max_order; order++) {
+ resource_size_t align1 = 1;
+
+ align1 <<= (order + 20);
+
+ if (!align)
+ min_align = align1;
+ else if (ALIGN(align + min_align, min_align) < align1)
+ min_align = align1 >> 1;
+ align += aligns[order];
+ }
+
+ return min_align;
+}
+
+/**
+ * pbus_size_mem() - size the memory window of a given bus
+ *
+ * @bus : the bus
+ * @mask: mask the resource flag, then compare it with type
+ * @type: the type of free resource from bridge
+ * @type2: second match type
+ * @type3: third match type
+ * @min_size : the minimum memory window that must to be allocated
+ * @add_size : additional optional memory window
+ * @realloc_head : track the additional memory window on this list
+ *
+ * Calculate the size of the bus and minimal alignment which
+ * guarantees that all child resources fit in this size.
+ *
+ * Returns -ENOSPC if there's no available bus resource of the desired type.
+ * Otherwise, sets the bus resource start/end to indicate the required
+ * size, adds things to realloc_head (if supplied), and returns 0.
+ */
+static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
+ unsigned long type, unsigned long type2,
+ unsigned long type3,
+ resource_size_t min_size, resource_size_t add_size,
+ struct list_head *realloc_head)
{
struct pci_dev *dev;
- resource_size_t min_align, align, size;
- resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */
+ resource_size_t min_align, align, size, size0, size1;
+ resource_size_t aligns[14]; /* Alignments from 1Mb to 8Gb */
int order, max_order;
- struct resource *b_res = find_free_bus_resource(bus, type);
+ struct resource *b_res = find_free_bus_resource(bus,
+ mask | IORESOURCE_PREFETCH, type);
+ resource_size_t children_add_size = 0;
if (!b_res)
- return 0;
+ return -ENOSPC;
memset(aligns, 0, sizeof(aligns));
max_order = 0;
@@ -348,80 +940,134 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
list_for_each_entry(dev, &bus->devices, bus_list) {
int i;
-
+
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
resource_size_t r_size;
- if (r->parent || (r->flags & mask) != type)
+ if (r->parent || ((r->flags & mask) != type &&
+ (r->flags & mask) != type2 &&
+ (r->flags & mask) != type3))
continue;
- r_size = r->end - r->start + 1;
- /* For bridges size != alignment */
- align = (i < PCI_BRIDGE_RESOURCES) ? r_size : r->start;
+ r_size = resource_size(r);
+#ifdef CONFIG_PCI_IOV
+ /* put SRIOV requested res to the optional list */
+ if (realloc_head && i >= PCI_IOV_RESOURCES &&
+ i <= PCI_IOV_RESOURCE_END) {
+ r->end = r->start - 1;
+ add_to_list(realloc_head, dev, r, r_size, 0/* don't care */);
+ children_add_size += r_size;
+ continue;
+ }
+#endif
+ /*
+ * aligns[0] is for 1MB (since bridge memory
+ * windows are always at least 1MB aligned), so
+ * keep "order" from being negative for smaller
+ * resources.
+ */
+ align = pci_resource_alignment(dev, 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,
- (unsigned long long)r->start,
- (unsigned long long)r->end);
+ if (order < 0)
+ order = 0;
+ if (order >= ARRAY_SIZE(aligns)) {
+ dev_warn(&dev->dev, "disabling BAR %d: %pR (bad alignment %#llx)\n",
+ i, r, (unsigned long long) align);
r->flags = 0;
continue;
}
size += r_size;
- if (order < 0)
- order = 0;
/* Exclude ranges with size > align from
calculation of the alignment. */
if (r_size == align)
aligns[order] += align;
if (order > max_order)
max_order = order;
+
+ if (realloc_head)
+ children_add_size += get_res_add_size(realloc_head, r);
}
}
- align = 0;
- min_align = 0;
- for (order = 0; order <= max_order; order++) {
-#ifdef CONFIG_RESOURCES_64BIT
- resource_size_t align1 = 1ULL << (order + 20);
-#else
- resource_size_t align1 = 1U << (order + 20);
-#endif
- if (!align)
- min_align = align1;
- else if (ALIGN(align + min_align, min_align) < align1)
- min_align = align1 >> 1;
- align += aligns[order];
- }
- size = ALIGN(size, min_align);
- if (!size) {
+ min_align = calculate_mem_align(aligns, max_order);
+ min_align = max(min_align, window_alignment(bus, b_res->flags));
+ size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align);
+ if (children_add_size > add_size)
+ add_size = children_add_size;
+ size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
+ calculate_memsize(size, min_size, add_size,
+ resource_size(b_res), min_align);
+ if (!size0 && !size1) {
+ if (b_res->start || b_res->end)
+ dev_info(&bus->self->dev, "disabling bridge window %pR to %pR (unused)\n",
+ b_res, &bus->busn_res);
b_res->flags = 0;
- return 1;
+ return 0;
}
b_res->start = min_align;
- b_res->end = size + min_align - 1;
- return 1;
+ b_res->end = size0 + min_align - 1;
+ b_res->flags |= IORESOURCE_STARTALIGN;
+ if (size1 > size0 && realloc_head) {
+ add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align);
+ dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window %pR to %pR add_size %llx\n",
+ b_res, &bus->busn_res,
+ (unsigned long long)size1-size0);
+ }
+ return 0;
+}
+
+unsigned long pci_cardbus_resource_alignment(struct resource *res)
+{
+ if (res->flags & IORESOURCE_IO)
+ return pci_cardbus_io_size;
+ if (res->flags & IORESOURCE_MEM)
+ return pci_cardbus_mem_size;
+ return 0;
}
-static void __devinit
-pci_bus_size_cardbus(struct pci_bus *bus)
+static void pci_bus_size_cardbus(struct pci_bus *bus,
+ struct list_head *realloc_head)
{
struct pci_dev *bridge = bus->self;
struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
+ resource_size_t b_res_3_size = pci_cardbus_mem_size * 2;
u16 ctrl;
+ if (b_res[0].parent)
+ goto handle_b_res_1;
/*
* Reserve some resources for CardBus. We reserve
* a fixed amount of bus space for CardBus bridges.
*/
b_res[0].start = pci_cardbus_io_size;
b_res[0].end = b_res[0].start + pci_cardbus_io_size - 1;
- b_res[0].flags |= IORESOURCE_IO;
+ b_res[0].flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res[0].end -= pci_cardbus_io_size;
+ add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size,
+ pci_cardbus_io_size);
+ }
+handle_b_res_1:
+ if (b_res[1].parent)
+ goto handle_b_res_2;
b_res[1].start = pci_cardbus_io_size;
b_res[1].end = b_res[1].start + pci_cardbus_io_size - 1;
- b_res[1].flags |= IORESOURCE_IO;
+ b_res[1].flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res[1].end -= pci_cardbus_io_size;
+ add_to_list(realloc_head, bridge, b_res+1, pci_cardbus_io_size,
+ pci_cardbus_io_size);
+ }
+
+handle_b_res_2:
+ /* MEM1 must not be pref mmio */
+ pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
+ if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) {
+ ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1;
+ pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl);
+ pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
+ }
/*
* Check whether prefetchable memory is supported
@@ -434,6 +1080,8 @@ pci_bus_size_cardbus(struct pci_bus *bus)
pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
}
+ if (b_res[2].parent)
+ goto handle_b_res_3;
/*
* If we have prefetchable memory support, allocate
* two regions. Otherwise, allocate one region of
@@ -442,22 +1090,41 @@ pci_bus_size_cardbus(struct pci_bus *bus)
if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) {
b_res[2].start = pci_cardbus_mem_size;
b_res[2].end = b_res[2].start + pci_cardbus_mem_size - 1;
- b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH |
+ IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res[2].end -= pci_cardbus_mem_size;
+ add_to_list(realloc_head, bridge, b_res+2,
+ pci_cardbus_mem_size, pci_cardbus_mem_size);
+ }
- b_res[3].start = pci_cardbus_mem_size;
- b_res[3].end = b_res[3].start + pci_cardbus_mem_size - 1;
- b_res[3].flags |= IORESOURCE_MEM;
- } else {
- b_res[3].start = pci_cardbus_mem_size * 2;
- b_res[3].end = b_res[3].start + pci_cardbus_mem_size * 2 - 1;
- b_res[3].flags |= IORESOURCE_MEM;
+ /* reduce that to half */
+ b_res_3_size = pci_cardbus_mem_size;
}
+
+handle_b_res_3:
+ if (b_res[3].parent)
+ goto handle_done;
+ b_res[3].start = pci_cardbus_mem_size;
+ b_res[3].end = b_res[3].start + b_res_3_size - 1;
+ b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res[3].end -= b_res_3_size;
+ add_to_list(realloc_head, bridge, b_res+3, b_res_3_size,
+ pci_cardbus_mem_size);
+ }
+
+handle_done:
+ ;
}
-void __ref pci_bus_size_bridges(struct pci_bus *bus)
+void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
{
struct pci_dev *dev;
- unsigned long mask, prefmask;
+ unsigned long mask, prefmask, type2 = 0, type3 = 0;
+ resource_size_t additional_mem_size = 0, additional_io_size = 0;
+ struct resource *b_res;
+ int ret;
list_for_each_entry(dev, &bus->devices, bus_list) {
struct pci_bus *b = dev->subordinate;
@@ -466,18 +1133,18 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus)
switch (dev->class >> 8) {
case PCI_CLASS_BRIDGE_CARDBUS:
- pci_bus_size_cardbus(b);
+ pci_bus_size_cardbus(b, realloc_head);
break;
case PCI_CLASS_BRIDGE_PCI:
default:
- pci_bus_size_bridges(b);
+ __pci_bus_size_bridges(b, realloc_head);
break;
}
}
/* The root bus? */
- if (!bus->self)
+ if (pci_is_root_bus(bus))
return;
switch (bus->self->class >> 8) {
@@ -487,40 +1154,114 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus)
case PCI_CLASS_BRIDGE_PCI:
pci_bridge_check_ranges(bus);
+ if (bus->self->is_hotplug_bridge) {
+ additional_io_size = pci_hotplug_io_size;
+ additional_mem_size = pci_hotplug_mem_size;
+ }
+ /* Fall through */
default:
- pbus_size_io(bus);
- /* If the bridge supports prefetchable range, size it
- separately. If it doesn't, or its prefetchable window
- has already been allocated by arch code, try
- non-prefetchable range for both types of PCI memory
- resources. */
+ pbus_size_io(bus, realloc_head ? 0 : additional_io_size,
+ additional_io_size, realloc_head);
+
+ /*
+ * If there's a 64-bit prefetchable MMIO window, compute
+ * the size required to put all 64-bit prefetchable
+ * resources in it.
+ */
+ b_res = &bus->self->resource[PCI_BRIDGE_RESOURCES];
mask = IORESOURCE_MEM;
prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;
- if (pbus_size_mem(bus, prefmask, prefmask))
- mask = prefmask; /* Success, size non-prefetch only. */
- pbus_size_mem(bus, mask, IORESOURCE_MEM);
+ if (b_res[2].flags & IORESOURCE_MEM_64) {
+ prefmask |= IORESOURCE_MEM_64;
+ ret = pbus_size_mem(bus, prefmask, prefmask,
+ prefmask, prefmask,
+ realloc_head ? 0 : additional_mem_size,
+ additional_mem_size, realloc_head);
+
+ /*
+ * If successful, all non-prefetchable resources
+ * and any 32-bit prefetchable resources will go in
+ * the non-prefetchable window.
+ */
+ if (ret == 0) {
+ mask = prefmask;
+ type2 = prefmask & ~IORESOURCE_MEM_64;
+ type3 = prefmask & ~IORESOURCE_PREFETCH;
+ }
+ }
+
+ /*
+ * If there is no 64-bit prefetchable window, compute the
+ * size required to put all prefetchable resources in the
+ * 32-bit prefetchable window (if there is one).
+ */
+ if (!type2) {
+ prefmask &= ~IORESOURCE_MEM_64;
+ ret = pbus_size_mem(bus, prefmask, prefmask,
+ prefmask, prefmask,
+ realloc_head ? 0 : additional_mem_size,
+ additional_mem_size, realloc_head);
+
+ /*
+ * If successful, only non-prefetchable resources
+ * will go in the non-prefetchable window.
+ */
+ if (ret == 0)
+ mask = prefmask;
+ else
+ additional_mem_size += additional_mem_size;
+
+ type2 = type3 = IORESOURCE_MEM;
+ }
+
+ /*
+ * Compute the size required to put everything else in the
+ * non-prefetchable window. This includes:
+ *
+ * - all non-prefetchable resources
+ * - 32-bit prefetchable resources if there's a 64-bit
+ * prefetchable window or no prefetchable window at all
+ * - 64-bit prefetchable resources if there's no
+ * prefetchable window at all
+ *
+ * Note that the strategy in __pci_assign_resource() must
+ * match that used here. Specifically, we cannot put a
+ * 32-bit prefetchable resource in a 64-bit prefetchable
+ * window.
+ */
+ pbus_size_mem(bus, mask, IORESOURCE_MEM, type2, type3,
+ realloc_head ? 0 : additional_mem_size,
+ additional_mem_size, realloc_head);
break;
}
}
+
+void pci_bus_size_bridges(struct pci_bus *bus)
+{
+ __pci_bus_size_bridges(bus, NULL);
+}
EXPORT_SYMBOL(pci_bus_size_bridges);
-void __ref pci_bus_assign_resources(struct pci_bus *bus)
+void __pci_bus_assign_resources(const struct pci_bus *bus,
+ struct list_head *realloc_head,
+ struct list_head *fail_head)
{
struct pci_bus *b;
struct pci_dev *dev;
- pbus_assign_resources_sorted(bus);
+ pbus_assign_resources_sorted(bus, realloc_head, fail_head);
list_for_each_entry(dev, &bus->devices, bus_list) {
b = dev->subordinate;
if (!b)
continue;
- pci_bus_assign_resources(b);
+ __pci_bus_assign_resources(b, realloc_head, fail_head);
switch (dev->class >> 8) {
case PCI_CLASS_BRIDGE_PCI:
- pci_setup_bridge(b);
+ if (!pci_is_enabled(dev))
+ pci_setup_bridge(b);
break;
case PCI_CLASS_BRIDGE_CARDBUS:
@@ -528,27 +1269,452 @@ 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;
}
}
}
+
+void pci_bus_assign_resources(const struct pci_bus *bus)
+{
+ __pci_bus_assign_resources(bus, NULL, NULL);
+}
EXPORT_SYMBOL(pci_bus_assign_resources);
-void __init
-pci_assign_unassigned_resources(void)
+static void __pci_bridge_assign_resources(const struct pci_dev *bridge,
+ struct list_head *add_head,
+ struct list_head *fail_head)
+{
+ struct pci_bus *b;
+
+ pdev_assign_resources_sorted((struct pci_dev *)bridge,
+ add_head, fail_head);
+
+ b = bridge->subordinate;
+ if (!b)
+ return;
+
+ __pci_bus_assign_resources(b, add_head, fail_head);
+
+ switch (bridge->class >> 8) {
+ case PCI_CLASS_BRIDGE_PCI:
+ pci_setup_bridge(b);
+ break;
+
+ case PCI_CLASS_BRIDGE_CARDBUS:
+ pci_setup_cardbus(b);
+ break;
+
+ default:
+ dev_info(&bridge->dev, "not setting up bridge for bus %04x:%02x\n",
+ pci_domain_nr(b), b->number);
+ break;
+ }
+}
+static void pci_bridge_release_resources(struct pci_bus *bus,
+ unsigned long type)
+{
+ struct pci_dev *dev = bus->self;
+ struct resource *r;
+ unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
+ IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
+ unsigned old_flags = 0;
+ struct resource *b_res;
+ int idx = 1;
+
+ b_res = &dev->resource[PCI_BRIDGE_RESOURCES];
+
+ /*
+ * 1. if there is io port assign fail, will release bridge
+ * io port.
+ * 2. if there is non pref mmio assign fail, release bridge
+ * nonpref mmio.
+ * 3. if there is 64bit pref mmio assign fail, and bridge pref
+ * is 64bit, release bridge pref mmio.
+ * 4. if there is pref mmio assign fail, and bridge pref is
+ * 32bit mmio, release bridge pref mmio
+ * 5. if there is pref mmio assign fail, and bridge pref is not
+ * assigned, release bridge nonpref mmio.
+ */
+ if (type & IORESOURCE_IO)
+ idx = 0;
+ else if (!(type & IORESOURCE_PREFETCH))
+ idx = 1;
+ else if ((type & IORESOURCE_MEM_64) &&
+ (b_res[2].flags & IORESOURCE_MEM_64))
+ idx = 2;
+ else if (!(b_res[2].flags & IORESOURCE_MEM_64) &&
+ (b_res[2].flags & IORESOURCE_PREFETCH))
+ idx = 2;
+ else
+ idx = 1;
+
+ r = &b_res[idx];
+
+ if (!r->parent)
+ return;
+
+ /*
+ * if there are children under that, we should release them
+ * all
+ */
+ release_child_resources(r);
+ if (!release_resource(r)) {
+ type = old_flags = r->flags & type_mask;
+ dev_printk(KERN_DEBUG, &dev->dev, "resource %d %pR released\n",
+ PCI_BRIDGE_RESOURCES + idx, r);
+ /* keep the old size */
+ r->end = resource_size(r) - 1;
+ r->start = 0;
+ r->flags = 0;
+
+ /* avoiding touch the one without PREF */
+ if (type & IORESOURCE_PREFETCH)
+ type = IORESOURCE_PREFETCH;
+ __pci_setup_bridge(bus, type);
+ /* for next child res under same bridge */
+ r->flags = old_flags;
+ }
+}
+
+enum release_type {
+ leaf_only,
+ whole_subtree,
+};
+/*
+ * try to release pci bridge resources that is from leaf bridge,
+ * so we can allocate big new one later
+ */
+static void pci_bus_release_bridge_resources(struct pci_bus *bus,
+ unsigned long type,
+ enum release_type rel_type)
{
- struct pci_bus *bus;
+ struct pci_dev *dev;
+ bool is_leaf_bridge = true;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ struct pci_bus *b = dev->subordinate;
+ if (!b)
+ continue;
+ is_leaf_bridge = false;
+
+ if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI)
+ continue;
+
+ if (rel_type == whole_subtree)
+ pci_bus_release_bridge_resources(b, type,
+ whole_subtree);
+ }
+
+ if (pci_is_root_bus(bus))
+ return;
+
+ if ((bus->self->class >> 8) != PCI_CLASS_BRIDGE_PCI)
+ return;
+
+ if ((rel_type == whole_subtree) || is_leaf_bridge)
+ pci_bridge_release_resources(bus, type);
+}
+
+static void pci_bus_dump_res(struct pci_bus *bus)
+{
+ struct resource *res;
+ int i;
+
+ pci_bus_for_each_resource(bus, res, i) {
+ if (!res || !res->end || !res->flags)
+ continue;
+
+ dev_printk(KERN_DEBUG, &bus->dev, "resource %d %pR\n", i, res);
+ }
+}
+
+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);
+ }
+}
+
+static int pci_bus_get_depth(struct pci_bus *bus)
+{
+ int depth = 0;
+ struct pci_bus *child_bus;
+
+ list_for_each_entry(child_bus, &bus->children, node) {
+ int ret;
+
+ ret = pci_bus_get_depth(child_bus);
+ if (ret + 1 > depth)
+ depth = ret + 1;
+ }
+
+ return depth;
+}
+
+/*
+ * -1: undefined, will auto detect later
+ * 0: disabled by user
+ * 1: disabled by auto detect
+ * 2: enabled by user
+ * 3: enabled by auto detect
+ */
+enum enable_type {
+ undefined = -1,
+ user_disabled,
+ auto_disabled,
+ user_enabled,
+ auto_enabled,
+};
+
+static enum enable_type pci_realloc_enable = undefined;
+void __init pci_realloc_get_opt(char *str)
+{
+ if (!strncmp(str, "off", 3))
+ pci_realloc_enable = user_disabled;
+ else if (!strncmp(str, "on", 2))
+ pci_realloc_enable = user_enabled;
+}
+static bool pci_realloc_enabled(enum enable_type enable)
+{
+ return enable >= user_enabled;
+}
+
+#if defined(CONFIG_PCI_IOV) && defined(CONFIG_PCI_REALLOC_ENABLE_AUTO)
+static int iov_resources_unassigned(struct pci_dev *dev, void *data)
+{
+ int i;
+ bool *unassigned = data;
+
+ for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) {
+ struct resource *r = &dev->resource[i];
+ struct pci_bus_region region;
+
+ /* Not assigned or rejected by kernel? */
+ if (!r->flags)
+ continue;
+
+ pcibios_resource_to_bus(dev->bus, &region, r);
+ if (!region.start) {
+ *unassigned = true;
+ return 1; /* return early from pci_walk_bus() */
+ }
+ }
+
+ return 0;
+}
+
+static enum enable_type pci_realloc_detect(struct pci_bus *bus,
+ enum enable_type enable_local)
+{
+ bool unassigned = false;
+
+ if (enable_local != undefined)
+ return enable_local;
+
+ pci_walk_bus(bus, iov_resources_unassigned, &unassigned);
+ if (unassigned)
+ return auto_enabled;
+
+ return enable_local;
+}
+#else
+static enum enable_type pci_realloc_detect(struct pci_bus *bus,
+ enum enable_type enable_local)
+{
+ return enable_local;
+}
+#endif
+
+/*
+ * first try will not touch pci bridge res
+ * second and later try will clear small leaf bridge res
+ * will stop till to the max depth if can not find good one
+ */
+void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
+{
+ LIST_HEAD(realloc_head); /* list of resources that
+ want additional resources */
+ struct list_head *add_list = NULL;
+ int tried_times = 0;
+ enum release_type rel_type = leaf_only;
+ LIST_HEAD(fail_head);
+ struct pci_dev_resource *fail_res;
+ unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
+ IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
+ int pci_try_num = 1;
+ enum enable_type enable_local;
+
+ /* don't realloc if asked to do so */
+ enable_local = pci_realloc_detect(bus, pci_realloc_enable);
+ if (pci_realloc_enabled(enable_local)) {
+ int max_depth = pci_bus_get_depth(bus);
+
+ pci_try_num = max_depth + 1;
+ dev_printk(KERN_DEBUG, &bus->dev,
+ "max bus depth: %d pci_try_num: %d\n",
+ max_depth, pci_try_num);
+ }
+
+again:
+ /*
+ * last try will use add_list, otherwise will try good to have as
+ * must have, so can realloc parent bridge resource
+ */
+ if (tried_times + 1 == pci_try_num)
+ add_list = &realloc_head;
/* Depth first, calculate sizes and alignments of all
subordinate buses. */
- list_for_each_entry(bus, &pci_root_buses, node) {
- pci_bus_size_bridges(bus);
- }
+ __pci_bus_size_bridges(bus, add_list);
+
/* Depth last, allocate resources and update the hardware. */
- list_for_each_entry(bus, &pci_root_buses, node) {
- pci_bus_assign_resources(bus);
- pci_enable_bridges(bus);
+ __pci_bus_assign_resources(bus, add_list, &fail_head);
+ if (add_list)
+ BUG_ON(!list_empty(add_list));
+ tried_times++;
+
+ /* any device complain? */
+ if (list_empty(&fail_head))
+ goto dump;
+
+ if (tried_times >= pci_try_num) {
+ if (enable_local == undefined)
+ dev_info(&bus->dev, "Some PCI device resources are unassigned, try booting with pci=realloc\n");
+ else if (enable_local == auto_enabled)
+ dev_info(&bus->dev, "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n");
+
+ free_list(&fail_head);
+ goto dump;
}
+
+ dev_printk(KERN_DEBUG, &bus->dev,
+ "No. %d try to assign unassigned res\n", tried_times + 1);
+
+ /* third times and later will not check if it is leaf */
+ if ((tried_times + 1) > 2)
+ rel_type = whole_subtree;
+
+ /*
+ * Try to release leaf bridge's resources that doesn't fit resource of
+ * child device under that bridge
+ */
+ list_for_each_entry(fail_res, &fail_head, list)
+ pci_bus_release_bridge_resources(fail_res->dev->bus,
+ fail_res->flags & type_mask,
+ rel_type);
+
+ /* restore size and flags */
+ list_for_each_entry(fail_res, &fail_head, list) {
+ struct resource *res = fail_res->res;
+
+ res->start = fail_res->start;
+ res->end = fail_res->end;
+ res->flags = fail_res->flags;
+ if (fail_res->dev->subordinate)
+ res->flags = 0;
+ }
+ free_list(&fail_head);
+
+ goto again;
+
+dump:
+ /* dump the resource on buses */
+ pci_bus_dump_resources(bus);
+}
+
+void __init pci_assign_unassigned_resources(void)
+{
+ struct pci_bus *root_bus;
+
+ list_for_each_entry(root_bus, &pci_root_buses, node)
+ pci_assign_unassigned_root_bus_resources(root_bus);
+}
+
+void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
+{
+ struct pci_bus *parent = bridge->subordinate;
+ LIST_HEAD(add_list); /* list of resources that
+ want additional resources */
+ int tried_times = 0;
+ LIST_HEAD(fail_head);
+ struct pci_dev_resource *fail_res;
+ int retval;
+ unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
+ IORESOURCE_PREFETCH;
+
+again:
+ __pci_bus_size_bridges(parent, &add_list);
+ __pci_bridge_assign_resources(bridge, &add_list, &fail_head);
+ BUG_ON(!list_empty(&add_list));
+ tried_times++;
+
+ if (list_empty(&fail_head))
+ goto enable_all;
+
+ if (tried_times >= 2) {
+ /* still fail, don't need to try more */
+ free_list(&fail_head);
+ goto enable_all;
+ }
+
+ printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n",
+ tried_times + 1);
+
+ /*
+ * Try to release leaf bridge's resources that doesn't fit resource of
+ * child device under that bridge
+ */
+ list_for_each_entry(fail_res, &fail_head, list)
+ pci_bus_release_bridge_resources(fail_res->dev->bus,
+ fail_res->flags & type_mask,
+ whole_subtree);
+
+ /* restore size and flags */
+ list_for_each_entry(fail_res, &fail_head, list) {
+ struct resource *res = fail_res->res;
+
+ res->start = fail_res->start;
+ res->end = fail_res->end;
+ res->flags = fail_res->flags;
+ if (fail_res->dev->subordinate)
+ res->flags = 0;
+ }
+ free_list(&fail_head);
+
+ goto again;
+
+enable_all:
+ retval = pci_reenable_device(bridge);
+ if (retval)
+ dev_err(&bridge->dev, "Error reenabling bridge (%d)\n", retval);
+ pci_set_master(bridge);
+}
+EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
+
+void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+ LIST_HEAD(add_list); /* list of resources that
+ want additional resources */
+
+ down_read(&pci_bus_sem);
+ list_for_each_entry(dev, &bus->devices, bus_list)
+ if (pci_is_bridge(dev) && pci_has_subordinate(dev))
+ __pci_bus_size_bridges(dev->subordinate,
+ &add_list);
+ up_read(&pci_bus_sem);
+ __pci_bus_assign_resources(bus, &add_list, NULL);
+ BUG_ON(!list_empty(&add_list));
}
diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c
index 05ca2ed9eb5..4e2d595d50c 100644
--- a/drivers/pci/setup-irq.c
+++ b/drivers/pci/setup-irq.c
@@ -10,18 +10,21 @@
*/
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/cache.h>
+void __weak pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+ dev_dbg(&dev->dev, "assigning IRQ %02d\n", irq);
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+}
-static void __init
-pdev_fixup_irq(struct pci_dev *dev,
- u8 (*swizzle)(struct pci_dev *, u8 *),
- int (*map_irq)(struct pci_dev *, u8, u8))
+static void pdev_fixup_irq(struct pci_dev *dev,
+ u8 (*swizzle)(struct pci_dev *, u8 *),
+ int (*map_irq)(const struct pci_dev *, u8, u8))
{
u8 pin, slot;
int irq = 0;
@@ -47,20 +50,18 @@ 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. */
pcibios_update_irq(dev, irq);
}
-void __init
-pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
- int (*map_irq)(struct pci_dev *, u8, u8))
+void pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
+ int (*map_irq)(const struct pci_dev *, u8, u8))
{
struct pci_dev *dev = NULL;
- while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+
+ for_each_pci_dev(dev)
pdev_fixup_irq(dev, swizzle, map_irq);
- }
}
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 4be7ccf7e3a..caed1ce6fac 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -16,8 +16,8 @@
* Resource sorting
*/
-#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
@@ -26,12 +26,15 @@
#include "pci.h"
-void
-pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
+void pci_update_resource(struct pci_dev *dev, int resno)
{
struct pci_bus_region region;
+ bool disable;
+ u16 cmd;
u32 new, check, mask;
int reg;
+ enum pci_bar_type type;
+ struct resource *res = dev->resource + resno;
/*
* Ignore resources for unimplemented BARs and unused resource slots
@@ -40,23 +43,18 @@ pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
if (!res->flags)
return;
+ if (res->flags & IORESOURCE_UNSET)
+ return;
+
/*
* 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,
- (unsigned long long)res->end,
- (unsigned long long)region.start,
- (unsigned long long)region.end,
- (unsigned long)res->flags, resno, pci_name(dev));
+ pcibios_resource_to_bus(dev->bus, &region, res);
new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
if (res->flags & IORESOURCE_IO)
@@ -64,202 +62,330 @@ pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
else
mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
- if (resno < 6) {
- reg = PCI_BASE_ADDRESS_0 + 4 * resno;
- } else if (resno == PCI_ROM_RESOURCE) {
+ reg = pci_resource_bar(dev, resno, &type);
+ if (!reg)
+ return;
+ if (type != pci_bar_unknown) {
if (!(res->flags & IORESOURCE_ROM_ENABLE))
return;
new |= PCI_ROM_ADDRESS_ENABLE;
- reg = dev->rom_base_reg;
- } else {
- /* Hmm, non-standard resource. */
-
- return; /* kill uninitialised var warning */
+ }
+
+ /*
+ * We can't update a 64-bit BAR atomically, so when possible,
+ * disable decoding so that a half-updated BAR won't conflict
+ * with another device.
+ */
+ disable = (res->flags & IORESOURCE_MEM_64) && !dev->mmio_always_on;
+ if (disable) {
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ pci_write_config_word(dev, PCI_COMMAND,
+ cmd & ~PCI_COMMAND_MEMORY);
}
pci_write_config_dword(dev, reg, new);
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)) ==
- (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) {
+ if (res->flags & IORESOURCE_MEM_64) {
new = region.start >> 16 >> 16;
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);
+
+ if (disable)
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
}
int pci_claim_resource(struct pci_dev *dev, int resource)
{
struct resource *res = &dev->resource[resource];
- struct resource *root = NULL;
- char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
- int err;
-
- root = pcibios_select_root(dev, res);
-
- err = -EINVAL;
- if (root != NULL)
- 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),
- (unsigned long long)res->start,
- (unsigned long long)res->end);
+ struct resource *root, *conflict;
+
+ if (res->flags & IORESOURCE_UNSET) {
+ dev_info(&dev->dev, "can't claim BAR %d %pR: no address assigned\n",
+ resource, res);
+ return -EINVAL;
+ }
+
+ root = pci_find_parent_resource(dev, res);
+ if (!root) {
+ dev_info(&dev->dev, "can't claim BAR %d %pR: no compatible bridge window\n",
+ resource, res);
+ return -EINVAL;
+ }
+
+ conflict = request_resource_conflict(root, res);
+ if (conflict) {
+ dev_info(&dev->dev, "can't claim BAR %d %pR: address conflict with %s %pR\n",
+ resource, res, conflict->name, conflict);
+ return -EBUSY;
}
- return err;
+ return 0;
}
+EXPORT_SYMBOL(pci_claim_resource);
-int pci_assign_resource(struct pci_dev *dev, int resno)
+void pci_disable_bridge_window(struct pci_dev *dev)
+{
+ dev_info(&dev->dev, "disabling bridge mem windows\n");
+
+ /* MMIO Base/Limit */
+ pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0);
+
+ /* Prefetchable MMIO Base/Limit */
+ pci_write_config_dword(dev, PCI_PREF_LIMIT_UPPER32, 0);
+ pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0);
+ pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff);
+}
+
+/*
+ * Generic function that returns a value indicating that the device's
+ * original BIOS BAR address was not saved and so is not available for
+ * reinstatement.
+ *
+ * Can be over-ridden by architecture specific code that implements
+ * reinstatement functionality rather than leaving it disabled when
+ * normal allocation attempts fail.
+ */
+resource_size_t __weak pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx)
+{
+ return 0;
+}
+
+static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
+ int resno, resource_size_t size)
+{
+ struct resource *root, *conflict;
+ resource_size_t fw_addr, start, end;
+ int ret = 0;
+
+ fw_addr = pcibios_retrieve_fw_addr(dev, resno);
+ if (!fw_addr)
+ return 1;
+
+ start = res->start;
+ end = res->end;
+ res->start = fw_addr;
+ res->end = res->start + size - 1;
+
+ root = pci_find_parent_resource(dev, res);
+ if (!root) {
+ if (res->flags & IORESOURCE_IO)
+ root = &ioport_resource;
+ else
+ root = &iomem_resource;
+ }
+
+ dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n",
+ resno, res);
+ conflict = request_resource_conflict(root, res);
+ if (conflict) {
+ dev_info(&dev->dev,
+ "BAR %d: %pR conflicts with %s %pR\n", resno,
+ res, conflict->name, conflict);
+ res->start = start;
+ res->end = end;
+ ret = 1;
+ }
+ return ret;
+}
+
+static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
+ int resno, resource_size_t size, resource_size_t align)
{
- struct pci_bus *bus = dev->bus;
struct resource *res = dev->resource + resno;
- resource_size_t size, min, align;
+ resource_size_t min;
int ret;
- size = res->end - res->start + 1;
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
- /* The bridge resources are special, as their
- size != alignment. Sizing routines return
- required alignment in the "start" field. */
- align = (resno < PCI_BRIDGE_RESOURCES) ? size : res->start;
- /* First, try exact prefetching match.. */
+ /*
+ * First, try exact prefetching match. Even if a 64-bit
+ * prefetchable bridge window is below 4GB, we can't put a 32-bit
+ * prefetchable resource in it because pbus_size_mem() assumes a
+ * 64-bit window will contain no 32-bit resources. If we assign
+ * things differently than they were sized, not everything will fit.
+ */
ret = pci_bus_alloc_resource(bus, res, size, align, min,
- IORESOURCE_PREFETCH,
+ IORESOURCE_PREFETCH | IORESOURCE_MEM_64,
pcibios_align_resource, dev);
+ if (ret == 0)
+ return 0;
- if (ret < 0 && (res->flags & IORESOURCE_PREFETCH)) {
- /*
- * That failed.
- *
- * But a prefetching area can handle a non-prefetching
- * window (it will just not perform as well).
- */
+ /*
+ * If the prefetchable window is only 32 bits wide, we can put
+ * 64-bit prefetchable resources in it.
+ */
+ if ((res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) ==
+ (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) {
+ ret = pci_bus_alloc_resource(bus, res, size, align, min,
+ IORESOURCE_PREFETCH,
+ pcibios_align_resource, dev);
+ if (ret == 0)
+ return 0;
+ }
+
+ /*
+ * If we didn't find a better match, we can put any memory resource
+ * in a non-prefetchable window. If this resource is 32 bits and
+ * non-prefetchable, the first call already tried the only possibility
+ * so we don't need to try again.
+ */
+ if (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64))
ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
pcibios_align_resource, dev);
+
+ return ret;
+}
+
+static int _pci_assign_resource(struct pci_dev *dev, int resno,
+ resource_size_t size, resource_size_t min_align)
+{
+ struct resource *res = dev->resource + resno;
+ struct pci_bus *bus;
+ int ret;
+ char *type;
+
+ bus = dev->bus;
+ while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) {
+ if (!bus->parent || !bus->self->transparent)
+ break;
+ bus = bus->parent;
}
if (ret) {
- printk(KERN_ERR "PCI: Failed to allocate %s resource "
- "#%d:%llx@%llx for %s\n",
- res->flags & IORESOURCE_IO ? "I/O" : "mem",
- resno, (unsigned long long)size,
- (unsigned long long)res->start, pci_name(dev));
- } else if (resno < PCI_BRIDGE_RESOURCES) {
- pci_update_resource(dev, res, resno);
+ if (res->flags & IORESOURCE_MEM)
+ if (res->flags & IORESOURCE_PREFETCH)
+ type = "mem pref";
+ else
+ type = "mem";
+ else if (res->flags & IORESOURCE_IO)
+ type = "io";
+ else
+ type = "unknown";
+ dev_info(&dev->dev,
+ "BAR %d: can't assign %s (size %#llx)\n",
+ resno, type, (unsigned long long) resource_size(res));
}
return ret;
}
-#ifdef CONFIG_EMBEDDED
-int pci_assign_resource_fixed(struct pci_dev *dev, int resno)
+int pci_assign_resource(struct pci_dev *dev, int resno)
{
- struct pci_bus *bus = dev->bus;
struct resource *res = dev->resource + resno;
- unsigned int type_mask;
- int i, ret = -EBUSY;
+ resource_size_t align, size;
+ int ret;
- type_mask = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ res->flags |= IORESOURCE_UNSET;
+ align = pci_resource_alignment(dev, res);
+ if (!align) {
+ dev_info(&dev->dev, "BAR %d: can't assign %pR (bogus alignment)\n",
+ resno, res);
+ return -EINVAL;
+ }
- for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
- struct resource *r = bus->resource[i];
- if (!r)
- continue;
+ size = resource_size(res);
+ ret = _pci_assign_resource(dev, resno, size, align);
- /* type_mask must match */
- if ((res->flags ^ r->flags) & type_mask)
- continue;
+ /*
+ * If we failed to assign anything, let's try the address
+ * where firmware left it. That at least has a chance of
+ * working, which is better than just leaving it disabled.
+ */
+ if (ret < 0)
+ ret = pci_revert_fw_address(res, dev, resno, size);
+
+ if (!ret) {
+ res->flags &= ~IORESOURCE_UNSET;
+ res->flags &= ~IORESOURCE_STARTALIGN;
+ dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
+ if (resno < PCI_BRIDGE_RESOURCES)
+ pci_update_resource(dev, resno);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(pci_assign_resource);
- ret = request_resource(r, res);
+int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize,
+ resource_size_t min_align)
+{
+ struct resource *res = dev->resource + resno;
+ resource_size_t new_size;
+ int ret;
- if (ret == 0)
- break;
+ res->flags |= IORESOURCE_UNSET;
+ if (!res->parent) {
+ dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR\n",
+ resno, res);
+ return -EINVAL;
}
- if (ret) {
- printk(KERN_ERR "PCI: Failed to allocate %s resource "
- "#%d:%llx@%llx for %s\n",
- res->flags & IORESOURCE_IO ? "I/O" : "mem",
- resno, (unsigned long long)(res->end - res->start + 1),
- (unsigned long long)res->start, pci_name(dev));
- } else if (resno < PCI_BRIDGE_RESOURCES) {
- pci_update_resource(dev, res, resno);
+ /* already aligned with min_align */
+ new_size = resource_size(res) + addsize;
+ ret = _pci_assign_resource(dev, resno, new_size, min_align);
+ if (!ret) {
+ res->flags &= ~IORESOURCE_UNSET;
+ res->flags &= ~IORESOURCE_STARTALIGN;
+ dev_info(&dev->dev, "BAR %d: reassigned %pR\n", resno, res);
+ if (resno < PCI_BRIDGE_RESOURCES)
+ pci_update_resource(dev, resno);
}
-
return ret;
}
-EXPORT_SYMBOL_GPL(pci_assign_resource_fixed);
-#endif
-/* Sort resources by alignment */
-void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
+int pci_enable_resources(struct pci_dev *dev, int mask)
{
+ u16 cmd, old_cmd;
int i;
+ struct resource *r;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ old_cmd = cmd;
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
- struct resource *r;
- struct resource_list *list, *tmp;
- resource_size_t r_align;
+ if (!(mask & (1 << i)))
+ continue;
r = &dev->resource[i];
- if (r->flags & IORESOURCE_PCI_FIXED)
+ if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
continue;
-
- r_align = r->end - r->start;
-
- if (!(r->flags) || r->parent)
- continue;
- if (!r_align) {
- printk(KERN_WARNING "PCI: Ignore bogus resource %d "
- "[%llx:%llx] of %s\n",
- i, (unsigned long long)r->start,
- (unsigned long long)r->end, pci_name(dev));
+ if ((i == PCI_ROM_RESOURCE) &&
+ (!(r->flags & IORESOURCE_ROM_ENABLE)))
continue;
+
+ if (r->flags & IORESOURCE_UNSET) {
+ dev_err(&dev->dev, "can't enable device: BAR %d %pR not assigned\n",
+ i, r);
+ return -EINVAL;
}
- r_align = (i < PCI_BRIDGE_RESOURCES) ? r_align + 1 : r->start;
- for (list = head; ; list = list->next) {
- resource_size_t align = 0;
- struct resource_list *ln = list->next;
- int idx;
-
- if (ln) {
- idx = ln->res - &ln->dev->resource[0];
- align = (idx < PCI_BRIDGE_RESOURCES) ?
- ln->res->end - ln->res->start + 1 :
- ln->res->start;
- }
- if (r_align > align) {
- tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
- if (!tmp)
- panic("pdev_sort_resources(): "
- "kmalloc() failed!\n");
- tmp->next = ln;
- tmp->res = r;
- tmp->dev = dev;
- list->next = tmp;
- break;
- }
+
+ if (!r->parent) {
+ dev_err(&dev->dev, "can't enable device: BAR %d %pR not claimed\n",
+ i, r);
+ return -EINVAL;
}
+
+ if (r->flags & IORESOURCE_IO)
+ cmd |= PCI_COMMAND_IO;
+ if (r->flags & IORESOURCE_MEM)
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+
+ if (cmd != old_cmd) {
+ dev_info(&dev->dev, "enabling device (%04x -> %04x)\n",
+ old_cmd, cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
}
+ return 0;
}
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
new file mode 100644
index 00000000000..396c200b9dd
--- /dev/null
+++ b/drivers/pci/slot.c
@@ -0,0 +1,397 @@
+/*
+ * drivers/pci/slot.c
+ * Copyright (C) 2006 Matthew Wilcox <matthew@wil.cx>
+ * Copyright (C) 2006-2009 Hewlett-Packard Development Company, L.P.
+ * Alex Chiang <achiang@hp.com>
+ */
+
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/module.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 const 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);
+}
+
+/* these strings match up with the values in pci_bus_speed */
+static const char *pci_bus_speed_strings[] = {
+ "33 MHz PCI", /* 0x00 */
+ "66 MHz PCI", /* 0x01 */
+ "66 MHz PCI-X", /* 0x02 */
+ "100 MHz PCI-X", /* 0x03 */
+ "133 MHz PCI-X", /* 0x04 */
+ NULL, /* 0x05 */
+ NULL, /* 0x06 */
+ NULL, /* 0x07 */
+ NULL, /* 0x08 */
+ "66 MHz PCI-X 266", /* 0x09 */
+ "100 MHz PCI-X 266", /* 0x0a */
+ "133 MHz PCI-X 266", /* 0x0b */
+ "Unknown AGP", /* 0x0c */
+ "1x AGP", /* 0x0d */
+ "2x AGP", /* 0x0e */
+ "4x AGP", /* 0x0f */
+ "8x AGP", /* 0x10 */
+ "66 MHz PCI-X 533", /* 0x11 */
+ "100 MHz PCI-X 533", /* 0x12 */
+ "133 MHz PCI-X 533", /* 0x13 */
+ "2.5 GT/s PCIe", /* 0x14 */
+ "5.0 GT/s PCIe", /* 0x15 */
+ "8.0 GT/s PCIe", /* 0x16 */
+};
+
+static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf)
+{
+ const char *speed_string;
+
+ if (speed < ARRAY_SIZE(pci_bus_speed_strings))
+ speed_string = pci_bus_speed_strings[speed];
+ else
+ speed_string = "Unknown";
+
+ return sprintf(buf, "%s\n", speed_string);
+}
+
+static ssize_t max_speed_read_file(struct pci_slot *slot, char *buf)
+{
+ return bus_speed_read(slot->bus->max_bus_speed, buf);
+}
+
+static ssize_t cur_speed_read_file(struct pci_slot *slot, char *buf)
+{
+ return bus_speed_read(slot->bus->cur_bus_speed, buf);
+}
+
+static void pci_slot_release(struct kobject *kobj)
+{
+ struct pci_dev *dev;
+ struct pci_slot *slot = to_pci_slot(kobj);
+
+ dev_dbg(&slot->bus->dev, "dev %02x, released physical slot %s\n",
+ slot->number, pci_slot_name(slot));
+
+ list_for_each_entry(dev, &slot->bus->devices, bus_list)
+ if (PCI_SLOT(dev->devfn) == slot->number)
+ dev->slot = NULL;
+
+ list_del(&slot->list);
+
+ kfree(slot);
+}
+
+static struct pci_slot_attribute pci_slot_attr_address =
+ __ATTR(address, S_IRUGO, address_read_file, NULL);
+static struct pci_slot_attribute pci_slot_attr_max_speed =
+ __ATTR(max_bus_speed, S_IRUGO, max_speed_read_file, NULL);
+static struct pci_slot_attribute pci_slot_attr_cur_speed =
+ __ATTR(cur_bus_speed, S_IRUGO, cur_speed_read_file, NULL);
+
+static struct attribute *pci_slot_default_attrs[] = {
+ &pci_slot_attr_address.attr,
+ &pci_slot_attr_max_speed.attr,
+ &pci_slot_attr_cur_speed.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,
+};
+
+static char *make_slot_name(const char *name)
+{
+ char *new_name;
+ int len, max, dup;
+
+ new_name = kstrdup(name, GFP_KERNEL);
+ if (!new_name)
+ return NULL;
+
+ /*
+ * Make sure we hit the realloc case the first time through the
+ * loop. 'len' will be strlen(name) + 3 at that point which is
+ * enough space for "name-X" and the trailing NUL.
+ */
+ len = strlen(name) + 2;
+ max = 1;
+ dup = 1;
+
+ for (;;) {
+ struct kobject *dup_slot;
+ dup_slot = kset_find_obj(pci_slots_kset, new_name);
+ if (!dup_slot)
+ break;
+ kobject_put(dup_slot);
+ if (dup == max) {
+ len++;
+ max *= 10;
+ kfree(new_name);
+ new_name = kmalloc(len, GFP_KERNEL);
+ if (!new_name)
+ break;
+ }
+ sprintf(new_name, "%s-%d", name, dup++);
+ }
+
+ return new_name;
+}
+
+static int rename_slot(struct pci_slot *slot, const char *name)
+{
+ int result = 0;
+ char *slot_name;
+
+ if (strcmp(pci_slot_name(slot), name) == 0)
+ return result;
+
+ slot_name = make_slot_name(name);
+ if (!slot_name)
+ return -ENOMEM;
+
+ result = kobject_rename(&slot->kobj, slot_name);
+ kfree(slot_name);
+
+ return result;
+}
+
+static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr)
+{
+ struct pci_slot *slot;
+ /*
+ * We already hold pci_bus_sem so don't worry
+ */
+ list_for_each_entry(slot, &parent->slots, list)
+ if (slot->number == slot_nr) {
+ kobject_get(&slot->kobj);
+ return slot;
+ }
+
+ return NULL;
+}
+
+/**
+ * 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>
+ * @hotplug: set if caller is hotplug driver, NULL otherwise
+ *
+ * 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 tuple.
+ *
+ * There are known platforms with broken firmware that assign the same
+ * name to multiple slots. Workaround these broken platforms by renaming
+ * the slots on behalf of the caller. If firmware assigns name N to
+ * multiple slots:
+ *
+ * The first slot is assigned N
+ * The second slot is assigned N-1
+ * The third slot is assigned N-2
+ * etc.
+ *
+ * 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, we no longer check for an existing %struct pci_slot, as there
+ * may be many slots with @slot_nr of -1. 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 hotplug_slot *hotplug)
+{
+ struct pci_dev *dev;
+ struct pci_slot *slot;
+ int err = 0;
+ char *slot_name = NULL;
+
+ down_write(&pci_bus_sem);
+
+ if (slot_nr == -1)
+ goto placeholder;
+
+ /*
+ * Hotplug drivers are allowed to rename an existing slot,
+ * but only if not already claimed.
+ */
+ slot = get_slot(parent, slot_nr);
+ if (slot) {
+ if (hotplug) {
+ if ((err = slot->hotplug ? -EBUSY : 0)
+ || (err = rename_slot(slot, name))) {
+ kobject_put(&slot->kobj);
+ slot = NULL;
+ goto err;
+ }
+ }
+ goto out;
+ }
+
+placeholder:
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ slot->bus = parent;
+ slot->number = slot_nr;
+
+ slot->kobj.kset = pci_slots_kset;
+
+ slot_name = make_slot_name(name);
+ if (!slot_name) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL,
+ "%s", slot_name);
+ if (err)
+ goto err;
+
+ INIT_LIST_HEAD(&slot->list);
+ list_add(&slot->list, &parent->slots);
+
+ list_for_each_entry(dev, &parent->devices, bus_list)
+ if (PCI_SLOT(dev->devfn) == slot_nr)
+ dev->slot = slot;
+
+ dev_dbg(&parent->dev, "dev %02x, created physical slot %s\n",
+ slot_nr, pci_slot_name(slot));
+
+out:
+ kfree(slot_name);
+ up_write(&pci_bus_sem);
+ return slot;
+err:
+ kfree(slot);
+ slot = ERR_PTR(err);
+ goto out;
+}
+EXPORT_SYMBOL_GPL(pci_create_slot);
+
+/**
+ * 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)
+{
+ dev_dbg(&slot->bus->dev, "dev %02x, dec refcount to %d\n",
+ slot->number, atomic_read(&slot->kobj.kref.refcount) - 1);
+
+ down_write(&pci_bus_sem);
+ kobject_put(&slot->kobj);
+ up_write(&pci_bus_sem);
+}
+EXPORT_SYMBOL_GPL(pci_destroy_slot);
+
+#if defined(CONFIG_HOTPLUG_PCI) || defined(CONFIG_HOTPLUG_PCI_MODULE)
+#include <linux/pci_hotplug.h>
+/**
+ * pci_hp_create_link - create symbolic link to the hotplug driver module.
+ * @pci_slot: struct pci_slot
+ *
+ * Helper function for pci_hotplug_core.c to create symbolic link to
+ * the hotplug driver module.
+ */
+void pci_hp_create_module_link(struct pci_slot *pci_slot)
+{
+ struct hotplug_slot *slot = pci_slot->hotplug;
+ struct kobject *kobj = NULL;
+ int ret;
+
+ if (!slot || !slot->ops)
+ return;
+ kobj = kset_find_obj(module_kset, slot->ops->mod_name);
+ if (!kobj)
+ return;
+ ret = sysfs_create_link(&pci_slot->kobj, kobj, "module");
+ if (ret)
+ dev_err(&pci_slot->bus->dev, "Error creating sysfs link (%d)\n",
+ ret);
+ kobject_put(kobj);
+}
+EXPORT_SYMBOL_GPL(pci_hp_create_module_link);
+
+/**
+ * pci_hp_remove_link - remove symbolic link to the hotplug driver module.
+ * @pci_slot: struct pci_slot
+ *
+ * Helper function for pci_hotplug_core.c to remove symbolic link to
+ * the hotplug driver module.
+ */
+void pci_hp_remove_module_link(struct pci_slot *pci_slot)
+{
+ sysfs_remove_link(&pci_slot->kobj, "module");
+}
+EXPORT_SYMBOL_GPL(pci_hp_remove_module_link);
+#endif
+
+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);
diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c
index 645d7a60e41..b91c4da6836 100644
--- a/drivers/pci/syscall.c
+++ b/drivers/pci/syscall.c
@@ -9,15 +9,12 @@
#include <linux/errno.h>
#include <linux/pci.h>
-#include <linux/smp_lock.h>
#include <linux/syscalls.h>
#include <asm/uaccess.h>
#include "pci.h"
-asmlinkage long
-sys_pciconfig_read(unsigned long bus, unsigned long dfn,
- unsigned long off, unsigned long len,
- void __user *buf)
+SYSCALL_DEFINE5(pciconfig_read, unsigned long, bus, unsigned long, dfn,
+ unsigned long, off, unsigned long, len, void __user *, buf)
{
struct pci_dev *dev;
u8 byte;
@@ -47,7 +44,7 @@ sys_pciconfig_read(unsigned long bus, unsigned long dfn,
default:
err = -EINVAL;
goto error;
- };
+ }
err = -EIO;
if (cfg_ret != PCIBIOS_SUCCESSFUL)
@@ -86,10 +83,8 @@ error:
return err;
}
-asmlinkage long
-sys_pciconfig_write(unsigned long bus, unsigned long dfn,
- unsigned long off, unsigned long len,
- void __user *buf)
+SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn,
+ unsigned long, off, unsigned long, len, void __user *, buf)
{
struct pci_dev *dev;
u8 byte;
@@ -104,7 +99,7 @@ sys_pciconfig_write(unsigned long bus, unsigned long dfn,
if (!dev)
return -ENODEV;
- switch(len) {
+ switch (len) {
case 1:
err = get_user(byte, (u8 __user *)buf);
if (err)
diff --git a/drivers/pci/vc.c b/drivers/pci/vc.c
new file mode 100644
index 00000000000..7e1304d2e38
--- /dev/null
+++ b/drivers/pci/vc.c
@@ -0,0 +1,434 @@
+/*
+ * PCI Virtual Channel support
+ *
+ * Copyright (C) 2013 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/types.h>
+
+/**
+ * pci_vc_save_restore_dwords - Save or restore a series of dwords
+ * @dev: device
+ * @pos: starting config space position
+ * @buf: buffer to save to or restore from
+ * @dwords: number of dwords to save/restore
+ * @save: whether to save or restore
+ */
+static void pci_vc_save_restore_dwords(struct pci_dev *dev, int pos,
+ u32 *buf, int dwords, bool save)
+{
+ int i;
+
+ for (i = 0; i < dwords; i++, buf++) {
+ if (save)
+ pci_read_config_dword(dev, pos + (i * 4), buf);
+ else
+ pci_write_config_dword(dev, pos + (i * 4), *buf);
+ }
+}
+
+/**
+ * pci_vc_load_arb_table - load and wait for VC arbitration table
+ * @dev: device
+ * @pos: starting position of VC capability (VC/VC9/MFVC)
+ *
+ * Set Load VC Arbitration Table bit requesting hardware to apply the VC
+ * Arbitration Table (previously loaded). When the VC Arbitration Table
+ * Status clears, hardware has latched the table into VC arbitration logic.
+ */
+static void pci_vc_load_arb_table(struct pci_dev *dev, int pos)
+{
+ u16 ctrl;
+
+ pci_read_config_word(dev, pos + PCI_VC_PORT_CTRL, &ctrl);
+ pci_write_config_word(dev, pos + PCI_VC_PORT_CTRL,
+ ctrl | PCI_VC_PORT_CTRL_LOAD_TABLE);
+ if (pci_wait_for_pending(dev, pos + PCI_VC_PORT_STATUS,
+ PCI_VC_PORT_STATUS_TABLE))
+ return;
+
+ dev_err(&dev->dev, "VC arbitration table failed to load\n");
+}
+
+/**
+ * pci_vc_load_port_arb_table - Load and wait for VC port arbitration table
+ * @dev: device
+ * @pos: starting position of VC capability (VC/VC9/MFVC)
+ * @res: VC resource number, ie. VCn (0-7)
+ *
+ * Set Load Port Arbitration Table bit requesting hardware to apply the Port
+ * Arbitration Table (previously loaded). When the Port Arbitration Table
+ * Status clears, hardware has latched the table into port arbitration logic.
+ */
+static void pci_vc_load_port_arb_table(struct pci_dev *dev, int pos, int res)
+{
+ int ctrl_pos, status_pos;
+ u32 ctrl;
+
+ ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF);
+ status_pos = pos + PCI_VC_RES_STATUS + (res * PCI_CAP_VC_PER_VC_SIZEOF);
+
+ pci_read_config_dword(dev, ctrl_pos, &ctrl);
+ pci_write_config_dword(dev, ctrl_pos,
+ ctrl | PCI_VC_RES_CTRL_LOAD_TABLE);
+
+ if (pci_wait_for_pending(dev, status_pos, PCI_VC_RES_STATUS_TABLE))
+ return;
+
+ dev_err(&dev->dev, "VC%d port arbitration table failed to load\n", res);
+}
+
+/**
+ * pci_vc_enable - Enable virtual channel
+ * @dev: device
+ * @pos: starting position of VC capability (VC/VC9/MFVC)
+ * @res: VC res number, ie. VCn (0-7)
+ *
+ * A VC is enabled by setting the enable bit in matching resource control
+ * registers on both sides of a link. We therefore need to find the opposite
+ * end of the link. To keep this simple we enable from the downstream device.
+ * RC devices do not have an upstream device, nor does it seem that VC9 do
+ * (spec is unclear). Once we find the upstream device, match the VC ID to
+ * get the correct resource, disable and enable on both ends.
+ */
+static void pci_vc_enable(struct pci_dev *dev, int pos, int res)
+{
+ int ctrl_pos, status_pos, id, pos2, evcc, i, ctrl_pos2, status_pos2;
+ u32 ctrl, header, cap1, ctrl2;
+ struct pci_dev *link = NULL;
+
+ /* Enable VCs from the downstream device */
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM)
+ return;
+
+ ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF);
+ status_pos = pos + PCI_VC_RES_STATUS + (res * PCI_CAP_VC_PER_VC_SIZEOF);
+
+ pci_read_config_dword(dev, ctrl_pos, &ctrl);
+ id = ctrl & PCI_VC_RES_CTRL_ID;
+
+ pci_read_config_dword(dev, pos, &header);
+
+ /* If there is no opposite end of the link, skip to enable */
+ if (PCI_EXT_CAP_ID(header) == PCI_EXT_CAP_ID_VC9 ||
+ pci_is_root_bus(dev->bus))
+ goto enable;
+
+ pos2 = pci_find_ext_capability(dev->bus->self, PCI_EXT_CAP_ID_VC);
+ if (!pos2)
+ goto enable;
+
+ pci_read_config_dword(dev->bus->self, pos2 + PCI_VC_PORT_CAP1, &cap1);
+ evcc = cap1 & PCI_VC_CAP1_EVCC;
+
+ /* VC0 is hardwired enabled, so we can start with 1 */
+ for (i = 1; i < evcc + 1; i++) {
+ ctrl_pos2 = pos2 + PCI_VC_RES_CTRL +
+ (i * PCI_CAP_VC_PER_VC_SIZEOF);
+ status_pos2 = pos2 + PCI_VC_RES_STATUS +
+ (i * PCI_CAP_VC_PER_VC_SIZEOF);
+ pci_read_config_dword(dev->bus->self, ctrl_pos2, &ctrl2);
+ if ((ctrl2 & PCI_VC_RES_CTRL_ID) == id) {
+ link = dev->bus->self;
+ break;
+ }
+ }
+
+ if (!link)
+ goto enable;
+
+ /* Disable if enabled */
+ if (ctrl2 & PCI_VC_RES_CTRL_ENABLE) {
+ ctrl2 &= ~PCI_VC_RES_CTRL_ENABLE;
+ pci_write_config_dword(link, ctrl_pos2, ctrl2);
+ }
+
+ /* Enable on both ends */
+ ctrl2 |= PCI_VC_RES_CTRL_ENABLE;
+ pci_write_config_dword(link, ctrl_pos2, ctrl2);
+enable:
+ ctrl |= PCI_VC_RES_CTRL_ENABLE;
+ pci_write_config_dword(dev, ctrl_pos, ctrl);
+
+ if (!pci_wait_for_pending(dev, status_pos, PCI_VC_RES_STATUS_NEGO))
+ dev_err(&dev->dev, "VC%d negotiation stuck pending\n", id);
+
+ if (link && !pci_wait_for_pending(link, status_pos2,
+ PCI_VC_RES_STATUS_NEGO))
+ dev_err(&link->dev, "VC%d negotiation stuck pending\n", id);
+}
+
+/**
+ * pci_vc_do_save_buffer - Size, save, or restore VC state
+ * @dev: device
+ * @pos: starting position of VC capability (VC/VC9/MFVC)
+ * @save_state: buffer for save/restore
+ * @name: for error message
+ * @save: if provided a buffer, this indicates what to do with it
+ *
+ * Walking Virtual Channel config space to size, save, or restore it
+ * is complicated, so we do it all from one function to reduce code and
+ * guarantee ordering matches in the buffer. When called with NULL
+ * @save_state, return the size of the necessary save buffer. When called
+ * with a non-NULL @save_state, @save determines whether we save to the
+ * buffer or restore from it.
+ */
+static int pci_vc_do_save_buffer(struct pci_dev *dev, int pos,
+ struct pci_cap_saved_state *save_state,
+ bool save)
+{
+ u32 cap1;
+ char evcc, lpevcc, parb_size;
+ int i, len = 0;
+ u8 *buf = save_state ? (u8 *)save_state->cap.data : NULL;
+
+ /* Sanity check buffer size for save/restore */
+ if (buf && save_state->cap.size !=
+ pci_vc_do_save_buffer(dev, pos, NULL, save)) {
+ dev_err(&dev->dev,
+ "VC save buffer size does not match @0x%x\n", pos);
+ return -ENOMEM;
+ }
+
+ pci_read_config_dword(dev, pos + PCI_VC_PORT_CAP1, &cap1);
+ /* Extended VC Count (not counting VC0) */
+ evcc = cap1 & PCI_VC_CAP1_EVCC;
+ /* Low Priority Extended VC Count (not counting VC0) */
+ lpevcc = (cap1 & PCI_VC_CAP1_LPEVCC) >> 4;
+ /* Port Arbitration Table Entry Size (bits) */
+ parb_size = 1 << ((cap1 & PCI_VC_CAP1_ARB_SIZE) >> 10);
+
+ /*
+ * Port VC Control Register contains VC Arbitration Select, which
+ * cannot be modified when more than one LPVC is in operation. We
+ * therefore save/restore it first, as only VC0 should be enabled
+ * after device reset.
+ */
+ if (buf) {
+ if (save)
+ pci_read_config_word(dev, pos + PCI_VC_PORT_CTRL,
+ (u16 *)buf);
+ else
+ pci_write_config_word(dev, pos + PCI_VC_PORT_CTRL,
+ *(u16 *)buf);
+ buf += 2;
+ }
+ len += 2;
+
+ /*
+ * If we have any Low Priority VCs and a VC Arbitration Table Offset
+ * in Port VC Capability Register 2 then save/restore it next.
+ */
+ if (lpevcc) {
+ u32 cap2;
+ int vcarb_offset;
+
+ pci_read_config_dword(dev, pos + PCI_VC_PORT_CAP2, &cap2);
+ vcarb_offset = ((cap2 & PCI_VC_CAP2_ARB_OFF) >> 24) * 16;
+
+ if (vcarb_offset) {
+ int size, vcarb_phases = 0;
+
+ if (cap2 & PCI_VC_CAP2_128_PHASE)
+ vcarb_phases = 128;
+ else if (cap2 & PCI_VC_CAP2_64_PHASE)
+ vcarb_phases = 64;
+ else if (cap2 & PCI_VC_CAP2_32_PHASE)
+ vcarb_phases = 32;
+
+ /* Fixed 4 bits per phase per lpevcc (plus VC0) */
+ size = ((lpevcc + 1) * vcarb_phases * 4) / 8;
+
+ if (size && buf) {
+ pci_vc_save_restore_dwords(dev,
+ pos + vcarb_offset,
+ (u32 *)buf,
+ size / 4, save);
+ /*
+ * On restore, we need to signal hardware to
+ * re-load the VC Arbitration Table.
+ */
+ if (!save)
+ pci_vc_load_arb_table(dev, pos);
+
+ buf += size;
+ }
+ len += size;
+ }
+ }
+
+ /*
+ * In addition to each VC Resource Control Register, we may have a
+ * Port Arbitration Table attached to each VC. The Port Arbitration
+ * Table Offset in each VC Resource Capability Register tells us if
+ * it exists. The entry size is global from the Port VC Capability
+ * Register1 above. The number of phases is determined per VC.
+ */
+ for (i = 0; i < evcc + 1; i++) {
+ u32 cap;
+ int parb_offset;
+
+ pci_read_config_dword(dev, pos + PCI_VC_RES_CAP +
+ (i * PCI_CAP_VC_PER_VC_SIZEOF), &cap);
+ parb_offset = ((cap & PCI_VC_RES_CAP_ARB_OFF) >> 24) * 16;
+ if (parb_offset) {
+ int size, parb_phases = 0;
+
+ if (cap & PCI_VC_RES_CAP_256_PHASE)
+ parb_phases = 256;
+ else if (cap & (PCI_VC_RES_CAP_128_PHASE |
+ PCI_VC_RES_CAP_128_PHASE_TB))
+ parb_phases = 128;
+ else if (cap & PCI_VC_RES_CAP_64_PHASE)
+ parb_phases = 64;
+ else if (cap & PCI_VC_RES_CAP_32_PHASE)
+ parb_phases = 32;
+
+ size = (parb_size * parb_phases) / 8;
+
+ if (size && buf) {
+ pci_vc_save_restore_dwords(dev,
+ pos + parb_offset,
+ (u32 *)buf,
+ size / 4, save);
+ buf += size;
+ }
+ len += size;
+ }
+
+ /* VC Resource Control Register */
+ if (buf) {
+ int ctrl_pos = pos + PCI_VC_RES_CTRL +
+ (i * PCI_CAP_VC_PER_VC_SIZEOF);
+ if (save)
+ pci_read_config_dword(dev, ctrl_pos,
+ (u32 *)buf);
+ else {
+ u32 tmp, ctrl = *(u32 *)buf;
+ /*
+ * For an FLR case, the VC config may remain.
+ * Preserve enable bit, restore the rest.
+ */
+ pci_read_config_dword(dev, ctrl_pos, &tmp);
+ tmp &= PCI_VC_RES_CTRL_ENABLE;
+ tmp |= ctrl & ~PCI_VC_RES_CTRL_ENABLE;
+ pci_write_config_dword(dev, ctrl_pos, tmp);
+ /* Load port arbitration table if used */
+ if (ctrl & PCI_VC_RES_CTRL_ARB_SELECT)
+ pci_vc_load_port_arb_table(dev, pos, i);
+ /* Re-enable if needed */
+ if ((ctrl ^ tmp) & PCI_VC_RES_CTRL_ENABLE)
+ pci_vc_enable(dev, pos, i);
+ }
+ buf += 4;
+ }
+ len += 4;
+ }
+
+ return buf ? 0 : len;
+}
+
+static struct {
+ u16 id;
+ const char *name;
+} vc_caps[] = { { PCI_EXT_CAP_ID_MFVC, "MFVC" },
+ { PCI_EXT_CAP_ID_VC, "VC" },
+ { PCI_EXT_CAP_ID_VC9, "VC9" } };
+
+/**
+ * pci_save_vc_state - Save VC state to pre-allocate save buffer
+ * @dev: device
+ *
+ * For each type of VC capability, VC/VC9/MFVC, find the capability and
+ * save it to the pre-allocated save buffer.
+ */
+int pci_save_vc_state(struct pci_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vc_caps); i++) {
+ int pos, ret;
+ struct pci_cap_saved_state *save_state;
+
+ pos = pci_find_ext_capability(dev, vc_caps[i].id);
+ if (!pos)
+ continue;
+
+ save_state = pci_find_saved_ext_cap(dev, vc_caps[i].id);
+ if (!save_state) {
+ dev_err(&dev->dev, "%s buffer not found in %s\n",
+ vc_caps[i].name, __func__);
+ return -ENOMEM;
+ }
+
+ ret = pci_vc_do_save_buffer(dev, pos, save_state, true);
+ if (ret) {
+ dev_err(&dev->dev, "%s save unsuccessful %s\n",
+ vc_caps[i].name, __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * pci_restore_vc_state - Restore VC state from save buffer
+ * @dev: device
+ *
+ * For each type of VC capability, VC/VC9/MFVC, find the capability and
+ * restore it from the previously saved buffer.
+ */
+void pci_restore_vc_state(struct pci_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vc_caps); i++) {
+ int pos;
+ struct pci_cap_saved_state *save_state;
+
+ pos = pci_find_ext_capability(dev, vc_caps[i].id);
+ save_state = pci_find_saved_ext_cap(dev, vc_caps[i].id);
+ if (!save_state || !pos)
+ continue;
+
+ pci_vc_do_save_buffer(dev, pos, save_state, false);
+ }
+}
+
+/**
+ * pci_allocate_vc_save_buffers - Allocate save buffers for VC caps
+ * @dev: device
+ *
+ * For each type of VC capability, VC/VC9/MFVC, find the capability, size
+ * it, and allocate a buffer for save/restore.
+ */
+
+void pci_allocate_vc_save_buffers(struct pci_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vc_caps); i++) {
+ int len, pos = pci_find_ext_capability(dev, vc_caps[i].id);
+
+ if (!pos)
+ continue;
+
+ len = pci_vc_do_save_buffer(dev, pos, NULL, false);
+ if (pci_add_ext_cap_save_buffer(dev, vc_caps[i].id, len))
+ dev_err(&dev->dev,
+ "unable to preallocate %s save buffer\n",
+ vc_caps[i].name);
+ }
+}
diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c
new file mode 100644
index 00000000000..39b79070335
--- /dev/null
+++ b/drivers/pci/vpd.c
@@ -0,0 +1,62 @@
+/*
+ * File: vpd.c
+ * Purpose: Provide PCI VPD support
+ *
+ * Copyright (C) 2010 Broadcom Corporation.
+ */
+
+#include <linux/pci.h>
+#include <linux/export.h>
+
+int pci_vpd_find_tag(const u8 *buf, unsigned int off, unsigned int len, u8 rdt)
+{
+ int i;
+
+ for (i = off; i < len; ) {
+ u8 val = buf[i];
+
+ if (val & PCI_VPD_LRDT) {
+ /* Don't return success of the tag isn't complete */
+ if (i + PCI_VPD_LRDT_TAG_SIZE > len)
+ break;
+
+ if (val == rdt)
+ return i;
+
+ i += PCI_VPD_LRDT_TAG_SIZE +
+ pci_vpd_lrdt_size(&buf[i]);
+ } else {
+ u8 tag = val & ~PCI_VPD_SRDT_LEN_MASK;
+
+ if (tag == rdt)
+ return i;
+
+ if (tag == PCI_VPD_SRDT_END)
+ break;
+
+ i += PCI_VPD_SRDT_TAG_SIZE +
+ pci_vpd_srdt_size(&buf[i]);
+ }
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(pci_vpd_find_tag);
+
+int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off,
+ unsigned int len, const char *kw)
+{
+ int i;
+
+ for (i = off; i + PCI_VPD_INFO_FLD_HDR_SIZE <= off + len;) {
+ if (buf[i + 0] == kw[0] &&
+ buf[i + 1] == kw[1])
+ return i;
+
+ i += PCI_VPD_INFO_FLD_HDR_SIZE +
+ pci_vpd_info_field_size(&buf[i]);
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(pci_vpd_find_info_keyword);
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
new file mode 100644
index 00000000000..53df39a22c8
--- /dev/null
+++ b/drivers/pci/xen-pcifront.c
@@ -0,0 +1,1168 @@
+/*
+ * Xen PCI Frontend.
+ *
+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <xen/xenbus.h>
+#include <xen/events.h>
+#include <xen/grant_table.h>
+#include <xen/page.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <linux/msi.h>
+#include <xen/interface/io/pciif.h>
+#include <asm/xen/pci.h>
+#include <linux/interrupt.h>
+#include <linux/atomic.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+#include <linux/time.h>
+#include <xen/platform_pci.h>
+
+#include <asm/xen/swiotlb-xen.h>
+#define INVALID_GRANT_REF (0)
+#define INVALID_EVTCHN (-1)
+
+struct pci_bus_entry {
+ struct list_head list;
+ struct pci_bus *bus;
+};
+
+#define _PDEVB_op_active (0)
+#define PDEVB_op_active (1 << (_PDEVB_op_active))
+
+struct pcifront_device {
+ struct xenbus_device *xdev;
+ struct list_head root_buses;
+
+ int evtchn;
+ int gnt_ref;
+
+ int irq;
+
+ /* Lock this when doing any operations in sh_info */
+ spinlock_t sh_info_lock;
+ struct xen_pci_sharedinfo *sh_info;
+ struct work_struct op_work;
+ unsigned long flags;
+
+};
+
+struct pcifront_sd {
+ int domain;
+ struct pcifront_device *pdev;
+};
+
+static inline struct pcifront_device *
+pcifront_get_pdev(struct pcifront_sd *sd)
+{
+ return sd->pdev;
+}
+
+static inline void pcifront_init_sd(struct pcifront_sd *sd,
+ unsigned int domain, unsigned int bus,
+ struct pcifront_device *pdev)
+{
+ sd->domain = domain;
+ sd->pdev = pdev;
+}
+
+static DEFINE_SPINLOCK(pcifront_dev_lock);
+static struct pcifront_device *pcifront_dev;
+
+static int verbose_request;
+module_param(verbose_request, int, 0644);
+
+static int errno_to_pcibios_err(int errno)
+{
+ switch (errno) {
+ case XEN_PCI_ERR_success:
+ return PCIBIOS_SUCCESSFUL;
+
+ case XEN_PCI_ERR_dev_not_found:
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ case XEN_PCI_ERR_invalid_offset:
+ case XEN_PCI_ERR_op_failed:
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ case XEN_PCI_ERR_not_implemented:
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+
+ case XEN_PCI_ERR_access_denied:
+ return PCIBIOS_SET_FAILED;
+ }
+ return errno;
+}
+
+static inline void schedule_pcifront_aer_op(struct pcifront_device *pdev)
+{
+ if (test_bit(_XEN_PCIB_active, (unsigned long *)&pdev->sh_info->flags)
+ && !test_and_set_bit(_PDEVB_op_active, &pdev->flags)) {
+ dev_dbg(&pdev->xdev->dev, "schedule aer frontend job\n");
+ schedule_work(&pdev->op_work);
+ }
+}
+
+static int do_pci_op(struct pcifront_device *pdev, struct xen_pci_op *op)
+{
+ int err = 0;
+ struct xen_pci_op *active_op = &pdev->sh_info->op;
+ unsigned long irq_flags;
+ evtchn_port_t port = pdev->evtchn;
+ unsigned irq = pdev->irq;
+ s64 ns, ns_timeout;
+ struct timeval tv;
+
+ spin_lock_irqsave(&pdev->sh_info_lock, irq_flags);
+
+ memcpy(active_op, op, sizeof(struct xen_pci_op));
+
+ /* Go */
+ wmb();
+ set_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags);
+ notify_remote_via_evtchn(port);
+
+ /*
+ * We set a poll timeout of 3 seconds but give up on return after
+ * 2 seconds. It is better to time out too late rather than too early
+ * (in the latter case we end up continually re-executing poll() with a
+ * timeout in the past). 1s difference gives plenty of slack for error.
+ */
+ do_gettimeofday(&tv);
+ ns_timeout = timeval_to_ns(&tv) + 2 * (s64)NSEC_PER_SEC;
+
+ xen_clear_irq_pending(irq);
+
+ while (test_bit(_XEN_PCIF_active,
+ (unsigned long *)&pdev->sh_info->flags)) {
+ xen_poll_irq_timeout(irq, jiffies + 3*HZ);
+ xen_clear_irq_pending(irq);
+ do_gettimeofday(&tv);
+ ns = timeval_to_ns(&tv);
+ if (ns > ns_timeout) {
+ dev_err(&pdev->xdev->dev,
+ "pciback not responding!!!\n");
+ clear_bit(_XEN_PCIF_active,
+ (unsigned long *)&pdev->sh_info->flags);
+ err = XEN_PCI_ERR_dev_not_found;
+ goto out;
+ }
+ }
+
+ /*
+ * We might lose backend service request since we
+ * reuse same evtchn with pci_conf backend response. So re-schedule
+ * aer pcifront service.
+ */
+ if (test_bit(_XEN_PCIB_active,
+ (unsigned long *)&pdev->sh_info->flags)) {
+ dev_err(&pdev->xdev->dev,
+ "schedule aer pcifront service\n");
+ schedule_pcifront_aer_op(pdev);
+ }
+
+ memcpy(op, active_op, sizeof(struct xen_pci_op));
+
+ err = op->err;
+out:
+ spin_unlock_irqrestore(&pdev->sh_info_lock, irq_flags);
+ return err;
+}
+
+/* Access to this function is spinlocked in drivers/pci/access.c */
+static int pcifront_bus_read(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ int err = 0;
+ struct xen_pci_op op = {
+ .cmd = XEN_PCI_OP_conf_read,
+ .domain = pci_domain_nr(bus),
+ .bus = bus->number,
+ .devfn = devfn,
+ .offset = where,
+ .size = size,
+ };
+ struct pcifront_sd *sd = bus->sysdata;
+ struct pcifront_device *pdev = pcifront_get_pdev(sd);
+
+ if (verbose_request)
+ dev_info(&pdev->xdev->dev,
+ "read dev=%04x:%02x:%02x.%d - offset %x size %d\n",
+ pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
+ PCI_FUNC(devfn), where, size);
+
+ err = do_pci_op(pdev, &op);
+
+ if (likely(!err)) {
+ if (verbose_request)
+ dev_info(&pdev->xdev->dev, "read got back value %x\n",
+ op.value);
+
+ *val = op.value;
+ } else if (err == -ENODEV) {
+ /* No device here, pretend that it just returned 0 */
+ err = 0;
+ *val = 0;
+ }
+
+ return errno_to_pcibios_err(err);
+}
+
+/* Access to this function is spinlocked in drivers/pci/access.c */
+static int pcifront_bus_write(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ struct xen_pci_op op = {
+ .cmd = XEN_PCI_OP_conf_write,
+ .domain = pci_domain_nr(bus),
+ .bus = bus->number,
+ .devfn = devfn,
+ .offset = where,
+ .size = size,
+ .value = val,
+ };
+ struct pcifront_sd *sd = bus->sysdata;
+ struct pcifront_device *pdev = pcifront_get_pdev(sd);
+
+ if (verbose_request)
+ dev_info(&pdev->xdev->dev,
+ "write dev=%04x:%02x:%02x.%d - "
+ "offset %x size %d val %x\n",
+ pci_domain_nr(bus), bus->number,
+ PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val);
+
+ return errno_to_pcibios_err(do_pci_op(pdev, &op));
+}
+
+static struct pci_ops pcifront_bus_ops = {
+ .read = pcifront_bus_read,
+ .write = pcifront_bus_write,
+};
+
+#ifdef CONFIG_PCI_MSI
+static int pci_frontend_enable_msix(struct pci_dev *dev,
+ int vector[], int nvec)
+{
+ int err;
+ int i;
+ struct xen_pci_op op = {
+ .cmd = XEN_PCI_OP_enable_msix,
+ .domain = pci_domain_nr(dev->bus),
+ .bus = dev->bus->number,
+ .devfn = dev->devfn,
+ .value = nvec,
+ };
+ struct pcifront_sd *sd = dev->bus->sysdata;
+ struct pcifront_device *pdev = pcifront_get_pdev(sd);
+ struct msi_desc *entry;
+
+ if (nvec > SH_INFO_MAX_VEC) {
+ dev_err(&dev->dev, "too much vector for pci frontend: %x."
+ " Increase SH_INFO_MAX_VEC.\n", nvec);
+ return -EINVAL;
+ }
+
+ i = 0;
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ op.msix_entries[i].entry = entry->msi_attrib.entry_nr;
+ /* Vector is useless at this point. */
+ op.msix_entries[i].vector = -1;
+ i++;
+ }
+
+ err = do_pci_op(pdev, &op);
+
+ if (likely(!err)) {
+ if (likely(!op.value)) {
+ /* we get the result */
+ for (i = 0; i < nvec; i++) {
+ if (op.msix_entries[i].vector <= 0) {
+ dev_warn(&dev->dev, "MSI-X entry %d is invalid: %d!\n",
+ i, op.msix_entries[i].vector);
+ err = -EINVAL;
+ vector[i] = -1;
+ continue;
+ }
+ vector[i] = op.msix_entries[i].vector;
+ }
+ } else {
+ printk(KERN_DEBUG "enable msix get value %x\n",
+ op.value);
+ err = op.value;
+ }
+ } else {
+ dev_err(&dev->dev, "enable msix get err %x\n", err);
+ }
+ return err;
+}
+
+static void pci_frontend_disable_msix(struct pci_dev *dev)
+{
+ int err;
+ struct xen_pci_op op = {
+ .cmd = XEN_PCI_OP_disable_msix,
+ .domain = pci_domain_nr(dev->bus),
+ .bus = dev->bus->number,
+ .devfn = dev->devfn,
+ };
+ struct pcifront_sd *sd = dev->bus->sysdata;
+ struct pcifront_device *pdev = pcifront_get_pdev(sd);
+
+ err = do_pci_op(pdev, &op);
+
+ /* What should do for error ? */
+ if (err)
+ dev_err(&dev->dev, "pci_disable_msix get err %x\n", err);
+}
+
+static int pci_frontend_enable_msi(struct pci_dev *dev, int vector[])
+{
+ int err;
+ struct xen_pci_op op = {
+ .cmd = XEN_PCI_OP_enable_msi,
+ .domain = pci_domain_nr(dev->bus),
+ .bus = dev->bus->number,
+ .devfn = dev->devfn,
+ };
+ struct pcifront_sd *sd = dev->bus->sysdata;
+ struct pcifront_device *pdev = pcifront_get_pdev(sd);
+
+ err = do_pci_op(pdev, &op);
+ if (likely(!err)) {
+ vector[0] = op.value;
+ if (op.value <= 0) {
+ dev_warn(&dev->dev, "MSI entry is invalid: %d!\n",
+ op.value);
+ err = -EINVAL;
+ vector[0] = -1;
+ }
+ } else {
+ dev_err(&dev->dev, "pci frontend enable msi failed for dev "
+ "%x:%x\n", op.bus, op.devfn);
+ err = -EINVAL;
+ }
+ return err;
+}
+
+static void pci_frontend_disable_msi(struct pci_dev *dev)
+{
+ int err;
+ struct xen_pci_op op = {
+ .cmd = XEN_PCI_OP_disable_msi,
+ .domain = pci_domain_nr(dev->bus),
+ .bus = dev->bus->number,
+ .devfn = dev->devfn,
+ };
+ struct pcifront_sd *sd = dev->bus->sysdata;
+ struct pcifront_device *pdev = pcifront_get_pdev(sd);
+
+ err = do_pci_op(pdev, &op);
+ if (err == XEN_PCI_ERR_dev_not_found) {
+ /* XXX No response from backend, what shall we do? */
+ printk(KERN_DEBUG "get no response from backend for disable MSI\n");
+ return;
+ }
+ if (err)
+ /* how can pciback notify us fail? */
+ printk(KERN_DEBUG "get fake response frombackend\n");
+}
+
+static struct xen_pci_frontend_ops pci_frontend_ops = {
+ .enable_msi = pci_frontend_enable_msi,
+ .disable_msi = pci_frontend_disable_msi,
+ .enable_msix = pci_frontend_enable_msix,
+ .disable_msix = pci_frontend_disable_msix,
+};
+
+static void pci_frontend_registrar(int enable)
+{
+ if (enable)
+ xen_pci_frontend = &pci_frontend_ops;
+ else
+ xen_pci_frontend = NULL;
+};
+#else
+static inline void pci_frontend_registrar(int enable) { };
+#endif /* CONFIG_PCI_MSI */
+
+/* Claim resources for the PCI frontend as-is, backend won't allow changes */
+static int pcifront_claim_resource(struct pci_dev *dev, void *data)
+{
+ struct pcifront_device *pdev = data;
+ int i;
+ struct resource *r;
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ r = &dev->resource[i];
+
+ if (!r->parent && r->start && r->flags) {
+ dev_info(&pdev->xdev->dev, "claiming resource %s/%d\n",
+ pci_name(dev), i);
+ if (pci_claim_resource(dev, i)) {
+ dev_err(&pdev->xdev->dev, "Could not claim resource %s/%d! "
+ "Device offline. Try using e820_host=1 in the guest config.\n",
+ pci_name(dev), i);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int pcifront_scan_bus(struct pcifront_device *pdev,
+ unsigned int domain, unsigned int bus,
+ struct pci_bus *b)
+{
+ struct pci_dev *d;
+ unsigned int devfn;
+
+ /* Scan the bus for functions and add.
+ * We omit handling of PCI bridge attachment because pciback prevents
+ * bridges from being exported.
+ */
+ for (devfn = 0; devfn < 0x100; devfn++) {
+ d = pci_get_slot(b, devfn);
+ if (d) {
+ /* Device is already known. */
+ pci_dev_put(d);
+ continue;
+ }
+
+ d = pci_scan_single_device(b, devfn);
+ if (d)
+ dev_info(&pdev->xdev->dev, "New device on "
+ "%04x:%02x:%02x.%d found.\n", domain, bus,
+ PCI_SLOT(devfn), PCI_FUNC(devfn));
+ }
+
+ return 0;
+}
+
+static int pcifront_scan_root(struct pcifront_device *pdev,
+ unsigned int domain, unsigned int bus)
+{
+ struct pci_bus *b;
+ struct pcifront_sd *sd = NULL;
+ struct pci_bus_entry *bus_entry = NULL;
+ int err = 0;
+
+#ifndef CONFIG_PCI_DOMAINS
+ if (domain != 0) {
+ dev_err(&pdev->xdev->dev,
+ "PCI Root in non-zero PCI Domain! domain=%d\n", domain);
+ dev_err(&pdev->xdev->dev,
+ "Please compile with CONFIG_PCI_DOMAINS\n");
+ err = -EINVAL;
+ goto err_out;
+ }
+#endif
+
+ dev_info(&pdev->xdev->dev, "Creating PCI Frontend Bus %04x:%02x\n",
+ domain, bus);
+
+ bus_entry = kmalloc(sizeof(*bus_entry), GFP_KERNEL);
+ sd = kmalloc(sizeof(*sd), GFP_KERNEL);
+ if (!bus_entry || !sd) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ pcifront_init_sd(sd, domain, bus, pdev);
+
+ pci_lock_rescan_remove();
+
+ b = pci_scan_bus_parented(&pdev->xdev->dev, bus,
+ &pcifront_bus_ops, sd);
+ if (!b) {
+ dev_err(&pdev->xdev->dev,
+ "Error creating PCI Frontend Bus!\n");
+ err = -ENOMEM;
+ pci_unlock_rescan_remove();
+ goto err_out;
+ }
+
+ bus_entry->bus = b;
+
+ list_add(&bus_entry->list, &pdev->root_buses);
+
+ /* pci_scan_bus_parented skips devices which do not have a have
+ * devfn==0. The pcifront_scan_bus enumerates all devfn. */
+ err = pcifront_scan_bus(pdev, domain, bus, b);
+
+ /* Claim resources before going "live" with our devices */
+ pci_walk_bus(b, pcifront_claim_resource, pdev);
+
+ /* Create SysFS and notify udev of the devices. Aka: "going live" */
+ pci_bus_add_devices(b);
+
+ pci_unlock_rescan_remove();
+ return err;
+
+err_out:
+ kfree(bus_entry);
+ kfree(sd);
+
+ return err;
+}
+
+static int pcifront_rescan_root(struct pcifront_device *pdev,
+ unsigned int domain, unsigned int bus)
+{
+ int err;
+ struct pci_bus *b;
+
+#ifndef CONFIG_PCI_DOMAINS
+ if (domain != 0) {
+ dev_err(&pdev->xdev->dev,
+ "PCI Root in non-zero PCI Domain! domain=%d\n", domain);
+ dev_err(&pdev->xdev->dev,
+ "Please compile with CONFIG_PCI_DOMAINS\n");
+ return -EINVAL;
+ }
+#endif
+
+ dev_info(&pdev->xdev->dev, "Rescanning PCI Frontend Bus %04x:%02x\n",
+ domain, bus);
+
+ b = pci_find_bus(domain, bus);
+ if (!b)
+ /* If the bus is unknown, create it. */
+ return pcifront_scan_root(pdev, domain, bus);
+
+ err = pcifront_scan_bus(pdev, domain, bus, b);
+
+ /* Claim resources before going "live" with our devices */
+ pci_walk_bus(b, pcifront_claim_resource, pdev);
+
+ /* Create SysFS and notify udev of the devices. Aka: "going live" */
+ pci_bus_add_devices(b);
+
+ return err;
+}
+
+static void free_root_bus_devs(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+
+ while (!list_empty(&bus->devices)) {
+ dev = container_of(bus->devices.next, struct pci_dev,
+ bus_list);
+ dev_dbg(&dev->dev, "removing device\n");
+ pci_stop_and_remove_bus_device(dev);
+ }
+}
+
+static void pcifront_free_roots(struct pcifront_device *pdev)
+{
+ struct pci_bus_entry *bus_entry, *t;
+
+ dev_dbg(&pdev->xdev->dev, "cleaning up root buses\n");
+
+ pci_lock_rescan_remove();
+ list_for_each_entry_safe(bus_entry, t, &pdev->root_buses, list) {
+ list_del(&bus_entry->list);
+
+ free_root_bus_devs(bus_entry->bus);
+
+ kfree(bus_entry->bus->sysdata);
+
+ device_unregister(bus_entry->bus->bridge);
+ pci_remove_bus(bus_entry->bus);
+
+ kfree(bus_entry);
+ }
+ pci_unlock_rescan_remove();
+}
+
+static pci_ers_result_t pcifront_common_process(int cmd,
+ struct pcifront_device *pdev,
+ pci_channel_state_t state)
+{
+ pci_ers_result_t result;
+ struct pci_driver *pdrv;
+ int bus = pdev->sh_info->aer_op.bus;
+ int devfn = pdev->sh_info->aer_op.devfn;
+ struct pci_dev *pcidev;
+ int flag = 0;
+
+ dev_dbg(&pdev->xdev->dev,
+ "pcifront AER process: cmd %x (bus:%x, devfn%x)",
+ cmd, bus, devfn);
+ result = PCI_ERS_RESULT_NONE;
+
+ pcidev = pci_get_bus_and_slot(bus, devfn);
+ if (!pcidev || !pcidev->driver) {
+ dev_err(&pdev->xdev->dev, "device or AER driver is NULL\n");
+ if (pcidev)
+ pci_dev_put(pcidev);
+ return result;
+ }
+ pdrv = pcidev->driver;
+
+ if (pdrv) {
+ if (pdrv->err_handler && pdrv->err_handler->error_detected) {
+ dev_dbg(&pcidev->dev,
+ "trying to call AER service\n");
+ if (pcidev) {
+ flag = 1;
+ switch (cmd) {
+ case XEN_PCI_OP_aer_detected:
+ result = pdrv->err_handler->
+ error_detected(pcidev, state);
+ break;
+ case XEN_PCI_OP_aer_mmio:
+ result = pdrv->err_handler->
+ mmio_enabled(pcidev);
+ break;
+ case XEN_PCI_OP_aer_slotreset:
+ result = pdrv->err_handler->
+ slot_reset(pcidev);
+ break;
+ case XEN_PCI_OP_aer_resume:
+ pdrv->err_handler->resume(pcidev);
+ break;
+ default:
+ dev_err(&pdev->xdev->dev,
+ "bad request in aer recovery "
+ "operation!\n");
+
+ }
+ }
+ }
+ }
+ if (!flag)
+ result = PCI_ERS_RESULT_NONE;
+
+ return result;
+}
+
+
+static void pcifront_do_aer(struct work_struct *data)
+{
+ struct pcifront_device *pdev =
+ container_of(data, struct pcifront_device, op_work);
+ int cmd = pdev->sh_info->aer_op.cmd;
+ pci_channel_state_t state =
+ (pci_channel_state_t)pdev->sh_info->aer_op.err;
+
+ /*If a pci_conf op is in progress,
+ we have to wait until it is done before service aer op*/
+ dev_dbg(&pdev->xdev->dev,
+ "pcifront service aer bus %x devfn %x\n",
+ pdev->sh_info->aer_op.bus, pdev->sh_info->aer_op.devfn);
+
+ pdev->sh_info->aer_op.err = pcifront_common_process(cmd, pdev, state);
+
+ /* Post the operation to the guest. */
+ wmb();
+ clear_bit(_XEN_PCIB_active, (unsigned long *)&pdev->sh_info->flags);
+ notify_remote_via_evtchn(pdev->evtchn);
+
+ /*in case of we lost an aer request in four lines time_window*/
+ smp_mb__before_atomic();
+ clear_bit(_PDEVB_op_active, &pdev->flags);
+ smp_mb__after_atomic();
+
+ schedule_pcifront_aer_op(pdev);
+
+}
+
+static irqreturn_t pcifront_handler_aer(int irq, void *dev)
+{
+ struct pcifront_device *pdev = dev;
+ schedule_pcifront_aer_op(pdev);
+ return IRQ_HANDLED;
+}
+static int pcifront_connect_and_init_dma(struct pcifront_device *pdev)
+{
+ int err = 0;
+
+ spin_lock(&pcifront_dev_lock);
+
+ if (!pcifront_dev) {
+ dev_info(&pdev->xdev->dev, "Installing PCI frontend\n");
+ pcifront_dev = pdev;
+ } else
+ err = -EEXIST;
+
+ spin_unlock(&pcifront_dev_lock);
+
+ if (!err && !swiotlb_nr_tbl()) {
+ err = pci_xen_swiotlb_init_late();
+ if (err)
+ dev_err(&pdev->xdev->dev, "Could not setup SWIOTLB!\n");
+ }
+ return err;
+}
+
+static void pcifront_disconnect(struct pcifront_device *pdev)
+{
+ spin_lock(&pcifront_dev_lock);
+
+ if (pdev == pcifront_dev) {
+ dev_info(&pdev->xdev->dev,
+ "Disconnecting PCI Frontend Buses\n");
+ pcifront_dev = NULL;
+ }
+
+ spin_unlock(&pcifront_dev_lock);
+}
+static struct pcifront_device *alloc_pdev(struct xenbus_device *xdev)
+{
+ struct pcifront_device *pdev;
+
+ pdev = kzalloc(sizeof(struct pcifront_device), GFP_KERNEL);
+ if (pdev == NULL)
+ goto out;
+
+ pdev->sh_info =
+ (struct xen_pci_sharedinfo *)__get_free_page(GFP_KERNEL);
+ if (pdev->sh_info == NULL) {
+ kfree(pdev);
+ pdev = NULL;
+ goto out;
+ }
+ pdev->sh_info->flags = 0;
+
+ /*Flag for registering PV AER handler*/
+ set_bit(_XEN_PCIB_AERHANDLER, (void *)&pdev->sh_info->flags);
+
+ dev_set_drvdata(&xdev->dev, pdev);
+ pdev->xdev = xdev;
+
+ INIT_LIST_HEAD(&pdev->root_buses);
+
+ spin_lock_init(&pdev->sh_info_lock);
+
+ pdev->evtchn = INVALID_EVTCHN;
+ pdev->gnt_ref = INVALID_GRANT_REF;
+ pdev->irq = -1;
+
+ INIT_WORK(&pdev->op_work, pcifront_do_aer);
+
+ dev_dbg(&xdev->dev, "Allocated pdev @ 0x%p pdev->sh_info @ 0x%p\n",
+ pdev, pdev->sh_info);
+out:
+ return pdev;
+}
+
+static void free_pdev(struct pcifront_device *pdev)
+{
+ dev_dbg(&pdev->xdev->dev, "freeing pdev @ 0x%p\n", pdev);
+
+ pcifront_free_roots(pdev);
+
+ cancel_work_sync(&pdev->op_work);
+
+ if (pdev->irq >= 0)
+ unbind_from_irqhandler(pdev->irq, pdev);
+
+ if (pdev->evtchn != INVALID_EVTCHN)
+ xenbus_free_evtchn(pdev->xdev, pdev->evtchn);
+
+ if (pdev->gnt_ref != INVALID_GRANT_REF)
+ gnttab_end_foreign_access(pdev->gnt_ref, 0 /* r/w page */,
+ (unsigned long)pdev->sh_info);
+ else
+ free_page((unsigned long)pdev->sh_info);
+
+ dev_set_drvdata(&pdev->xdev->dev, NULL);
+
+ kfree(pdev);
+}
+
+static int pcifront_publish_info(struct pcifront_device *pdev)
+{
+ int err = 0;
+ struct xenbus_transaction trans;
+
+ err = xenbus_grant_ring(pdev->xdev, virt_to_mfn(pdev->sh_info));
+ if (err < 0)
+ goto out;
+
+ pdev->gnt_ref = err;
+
+ err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn);
+ if (err)
+ goto out;
+
+ err = bind_evtchn_to_irqhandler(pdev->evtchn, pcifront_handler_aer,
+ 0, "pcifront", pdev);
+
+ if (err < 0)
+ return err;
+
+ pdev->irq = err;
+
+do_publish:
+ err = xenbus_transaction_start(&trans);
+ if (err) {
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error writing configuration for backend "
+ "(start transaction)");
+ goto out;
+ }
+
+ err = xenbus_printf(trans, pdev->xdev->nodename,
+ "pci-op-ref", "%u", pdev->gnt_ref);
+ if (!err)
+ err = xenbus_printf(trans, pdev->xdev->nodename,
+ "event-channel", "%u", pdev->evtchn);
+ if (!err)
+ err = xenbus_printf(trans, pdev->xdev->nodename,
+ "magic", XEN_PCI_MAGIC);
+
+ if (err) {
+ xenbus_transaction_end(trans, 1);
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error writing configuration for backend");
+ goto out;
+ } else {
+ err = xenbus_transaction_end(trans, 0);
+ if (err == -EAGAIN)
+ goto do_publish;
+ else if (err) {
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error completing transaction "
+ "for backend");
+ goto out;
+ }
+ }
+
+ xenbus_switch_state(pdev->xdev, XenbusStateInitialised);
+
+ dev_dbg(&pdev->xdev->dev, "publishing successful!\n");
+
+out:
+ return err;
+}
+
+static int pcifront_try_connect(struct pcifront_device *pdev)
+{
+ int err = -EFAULT;
+ int i, num_roots, len;
+ char str[64];
+ unsigned int domain, bus;
+
+
+ /* Only connect once */
+ if (xenbus_read_driver_state(pdev->xdev->nodename) !=
+ XenbusStateInitialised)
+ goto out;
+
+ err = pcifront_connect_and_init_dma(pdev);
+ if (err && err != -EEXIST) {
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error setting up PCI Frontend");
+ goto out;
+ }
+
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend,
+ "root_num", "%d", &num_roots);
+ if (err == -ENOENT) {
+ xenbus_dev_error(pdev->xdev, err,
+ "No PCI Roots found, trying 0000:00");
+ err = pcifront_scan_root(pdev, 0, 0);
+ num_roots = 0;
+ } else if (err != 1) {
+ if (err == 0)
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading number of PCI roots");
+ goto out;
+ }
+
+ for (i = 0; i < num_roots; i++) {
+ len = snprintf(str, sizeof(str), "root-%d", i);
+ if (unlikely(len >= (sizeof(str) - 1))) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str,
+ "%x:%x", &domain, &bus);
+ if (err != 2) {
+ if (err >= 0)
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading PCI root %d", i);
+ goto out;
+ }
+
+ err = pcifront_scan_root(pdev, domain, bus);
+ if (err) {
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error scanning PCI root %04x:%02x",
+ domain, bus);
+ goto out;
+ }
+ }
+
+ err = xenbus_switch_state(pdev->xdev, XenbusStateConnected);
+
+out:
+ return err;
+}
+
+static int pcifront_try_disconnect(struct pcifront_device *pdev)
+{
+ int err = 0;
+ enum xenbus_state prev_state;
+
+
+ prev_state = xenbus_read_driver_state(pdev->xdev->nodename);
+
+ if (prev_state >= XenbusStateClosing)
+ goto out;
+
+ if (prev_state == XenbusStateConnected) {
+ pcifront_free_roots(pdev);
+ pcifront_disconnect(pdev);
+ }
+
+ err = xenbus_switch_state(pdev->xdev, XenbusStateClosed);
+
+out:
+
+ return err;
+}
+
+static int pcifront_attach_devices(struct pcifront_device *pdev)
+{
+ int err = -EFAULT;
+ int i, num_roots, len;
+ unsigned int domain, bus;
+ char str[64];
+
+ if (xenbus_read_driver_state(pdev->xdev->nodename) !=
+ XenbusStateReconfiguring)
+ goto out;
+
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend,
+ "root_num", "%d", &num_roots);
+ if (err == -ENOENT) {
+ xenbus_dev_error(pdev->xdev, err,
+ "No PCI Roots found, trying 0000:00");
+ err = pcifront_rescan_root(pdev, 0, 0);
+ num_roots = 0;
+ } else if (err != 1) {
+ if (err == 0)
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading number of PCI roots");
+ goto out;
+ }
+
+ for (i = 0; i < num_roots; i++) {
+ len = snprintf(str, sizeof(str), "root-%d", i);
+ if (unlikely(len >= (sizeof(str) - 1))) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str,
+ "%x:%x", &domain, &bus);
+ if (err != 2) {
+ if (err >= 0)
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading PCI root %d", i);
+ goto out;
+ }
+
+ err = pcifront_rescan_root(pdev, domain, bus);
+ if (err) {
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error scanning PCI root %04x:%02x",
+ domain, bus);
+ goto out;
+ }
+ }
+
+ xenbus_switch_state(pdev->xdev, XenbusStateConnected);
+
+out:
+ return err;
+}
+
+static int pcifront_detach_devices(struct pcifront_device *pdev)
+{
+ int err = 0;
+ int i, num_devs;
+ unsigned int domain, bus, slot, func;
+ struct pci_dev *pci_dev;
+ char str[64];
+
+ if (xenbus_read_driver_state(pdev->xdev->nodename) !=
+ XenbusStateConnected)
+ goto out;
+
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, "num_devs", "%d",
+ &num_devs);
+ if (err != 1) {
+ if (err >= 0)
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading number of PCI devices");
+ goto out;
+ }
+
+ /* Find devices being detached and remove them. */
+ for (i = 0; i < num_devs; i++) {
+ int l, state;
+ l = snprintf(str, sizeof(str), "state-%d", i);
+ if (unlikely(l >= (sizeof(str) - 1))) {
+ err = -ENOMEM;
+ goto out;
+ }
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, "%d",
+ &state);
+ if (err != 1)
+ state = XenbusStateUnknown;
+
+ if (state != XenbusStateClosing)
+ continue;
+
+ /* Remove device. */
+ l = snprintf(str, sizeof(str), "vdev-%d", i);
+ if (unlikely(l >= (sizeof(str) - 1))) {
+ err = -ENOMEM;
+ goto out;
+ }
+ err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str,
+ "%x:%x:%x.%x", &domain, &bus, &slot, &func);
+ if (err != 4) {
+ if (err >= 0)
+ err = -EINVAL;
+ xenbus_dev_fatal(pdev->xdev, err,
+ "Error reading PCI device %d", i);
+ goto out;
+ }
+
+ pci_dev = pci_get_domain_bus_and_slot(domain, bus,
+ PCI_DEVFN(slot, func));
+ if (!pci_dev) {
+ dev_dbg(&pdev->xdev->dev,
+ "Cannot get PCI device %04x:%02x:%02x.%d\n",
+ domain, bus, slot, func);
+ continue;
+ }
+ pci_lock_rescan_remove();
+ pci_stop_and_remove_bus_device(pci_dev);
+ pci_dev_put(pci_dev);
+ pci_unlock_rescan_remove();
+
+ dev_dbg(&pdev->xdev->dev,
+ "PCI device %04x:%02x:%02x.%d removed.\n",
+ domain, bus, slot, func);
+ }
+
+ err = xenbus_switch_state(pdev->xdev, XenbusStateReconfiguring);
+
+out:
+ return err;
+}
+
+static void __init_refok pcifront_backend_changed(struct xenbus_device *xdev,
+ enum xenbus_state be_state)
+{
+ struct pcifront_device *pdev = dev_get_drvdata(&xdev->dev);
+
+ switch (be_state) {
+ case XenbusStateUnknown:
+ case XenbusStateInitialising:
+ case XenbusStateInitWait:
+ case XenbusStateInitialised:
+ break;
+
+ case XenbusStateConnected:
+ pcifront_try_connect(pdev);
+ break;
+
+ case XenbusStateClosed:
+ if (xdev->state == XenbusStateClosed)
+ break;
+ /* Missed the backend's CLOSING state -- fallthrough */
+ case XenbusStateClosing:
+ dev_warn(&xdev->dev, "backend going away!\n");
+ pcifront_try_disconnect(pdev);
+ break;
+
+ case XenbusStateReconfiguring:
+ pcifront_detach_devices(pdev);
+ break;
+
+ case XenbusStateReconfigured:
+ pcifront_attach_devices(pdev);
+ break;
+ }
+}
+
+static int pcifront_xenbus_probe(struct xenbus_device *xdev,
+ const struct xenbus_device_id *id)
+{
+ int err = 0;
+ struct pcifront_device *pdev = alloc_pdev(xdev);
+
+ if (pdev == NULL) {
+ err = -ENOMEM;
+ xenbus_dev_fatal(xdev, err,
+ "Error allocating pcifront_device struct");
+ goto out;
+ }
+
+ err = pcifront_publish_info(pdev);
+ if (err)
+ free_pdev(pdev);
+
+out:
+ return err;
+}
+
+static int pcifront_xenbus_remove(struct xenbus_device *xdev)
+{
+ struct pcifront_device *pdev = dev_get_drvdata(&xdev->dev);
+ if (pdev)
+ free_pdev(pdev);
+
+ return 0;
+}
+
+static const struct xenbus_device_id xenpci_ids[] = {
+ {"pci"},
+ {""},
+};
+
+static DEFINE_XENBUS_DRIVER(xenpci, "pcifront",
+ .probe = pcifront_xenbus_probe,
+ .remove = pcifront_xenbus_remove,
+ .otherend_changed = pcifront_backend_changed,
+);
+
+static int __init pcifront_init(void)
+{
+ if (!xen_pv_domain() || xen_initial_domain())
+ return -ENODEV;
+
+ if (!xen_has_pv_devices())
+ return -ENODEV;
+
+ pci_frontend_registrar(1 /* enable */);
+
+ return xenbus_register_frontend(&xenpci_driver);
+}
+
+static void __exit pcifront_cleanup(void)
+{
+ xenbus_unregister_driver(&xenpci_driver);
+ pci_frontend_registrar(0 /* disable */);
+}
+module_init(pcifront_init);
+module_exit(pcifront_cleanup);
+
+MODULE_DESCRIPTION("Xen PCI passthrough frontend.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("xen:pci");