Merge branch 'remotes/lorenzo/pci/mobiveil'
authorBjorn Helgaas <bhelgaas@google.com>
Wed, 15 Aug 2018 19:59:14 +0000 (14:59 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Wed, 15 Aug 2018 19:59:14 +0000 (14:59 -0500)
  - Fix mobiveil iomem/phys_addr_t type usage (Lorenzo Pieralisi)

  - Fix mobiveil missing include file (Lorenzo Pieralisi)

  - Add mobiveil Kconfig/Makefile support (Lorenzo Pieralisi)

* remotes/lorenzo/pci/mobiveil:
  PCI: mobiveil: Add Kconfig/Makefile entries
  PCI: mobiveil: Add missing ../pci.h include
  PCI: mobiveil: Fix struct mobiveil_pcie.pcie_reg_base address type
  PCI: mobiveil: Integer overflow in IB_WIN_SIZE

132 files changed:
Documentation/ABI/testing/sysfs-bus-pci-devices-aer_stats [new file with mode: 0644]
Documentation/PCI/00-INDEX
Documentation/PCI/acpi-info.txt [new file with mode: 0644]
Documentation/PCI/endpoint/function/binding/pci-test.txt
Documentation/PCI/endpoint/pci-endpoint.txt
Documentation/PCI/endpoint/pci-test-function.txt
Documentation/PCI/endpoint/pci-test-howto.txt
Documentation/PCI/pcieaer-howto.txt
Documentation/admin-guide/kernel-parameters.txt
Documentation/devicetree/bindings/pci/cdns,cdns-pcie-ep.txt
Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.txt
Documentation/ioctl/ioctl-number.txt
Documentation/misc-devices/pci-endpoint-test.txt
arch/arm/Kconfig
arch/arm/mach-bcm/Kconfig
arch/arm/mach-socfpga/Kconfig
arch/powerpc/kernel/dma.c
arch/powerpc/kernel/pci-common.c
arch/powerpc/platforms/powernv/pci-ioda.c
arch/powerpc/platforms/pseries/setup.c
arch/sh/drivers/pci/pci.c
arch/x86/include/asm/pci-direct.h
arch/x86/kernel/pci-dma.c
arch/x86/kernel/setup.c
arch/x86/pci/common.c
arch/x86/pci/early.c
drivers/infiniband/hw/hfi1/pcie.c
drivers/misc/pci_endpoint_test.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/netronome/nfp/nfp_main.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/intel/iwlwifi/pcie/drv.c
drivers/net/wireless/ralink/rt2x00/rt2x00pci.h
drivers/ntb/hw/mscc/ntb_hw_switchtec.c
drivers/pci/Makefile
drivers/pci/ats.c
drivers/pci/bus.c
drivers/pci/controller/Kconfig
drivers/pci/controller/dwc/Kconfig
drivers/pci/controller/dwc/pci-dra7xx.c
drivers/pci/controller/dwc/pci-exynos.c
drivers/pci/controller/dwc/pci-imx6.c
drivers/pci/controller/dwc/pci-keystone.c
drivers/pci/controller/dwc/pcie-armada8k.c
drivers/pci/controller/dwc/pcie-artpec6.c
drivers/pci/controller/dwc/pcie-designware-ep.c
drivers/pci/controller/dwc/pcie-designware-host.c
drivers/pci/controller/dwc/pcie-designware-plat.c
drivers/pci/controller/dwc/pcie-designware.h
drivers/pci/controller/dwc/pcie-histb.c
drivers/pci/controller/dwc/pcie-kirin.c
drivers/pci/controller/dwc/pcie-qcom.c
drivers/pci/controller/dwc/pcie-spear13xx.c
drivers/pci/controller/pci-aardvark.c
drivers/pci/controller/pci-ftpci100.c
drivers/pci/controller/pci-hyperv.c
drivers/pci/controller/pci-v3-semi.c
drivers/pci/controller/pci-versatile.c
drivers/pci/controller/pci-xgene.c
drivers/pci/controller/pcie-cadence-ep.c
drivers/pci/controller/pcie-cadence-host.c
drivers/pci/controller/pcie-cadence.c
drivers/pci/controller/pcie-cadence.h
drivers/pci/controller/pcie-iproc.c
drivers/pci/controller/pcie-iproc.h
drivers/pci/controller/pcie-mediatek.c
drivers/pci/controller/pcie-rcar.c
drivers/pci/controller/pcie-rockchip-ep.c
drivers/pci/controller/pcie-xilinx-nwl.c
drivers/pci/controller/pcie-xilinx.c
drivers/pci/endpoint/functions/pci-epf-test.c
drivers/pci/endpoint/pci-ep-cfs.c
drivers/pci/endpoint/pci-epc-core.c
drivers/pci/endpoint/pci-epf-core.c
drivers/pci/hotplug/acpi_pcihp.c
drivers/pci/hotplug/acpiphp_core.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/hotplug/cpci_hotplug_core.c
drivers/pci/hotplug/cpqphp_core.c
drivers/pci/hotplug/ibmphp_core.c
drivers/pci/hotplug/ibmphp_ebda.c
drivers/pci/hotplug/pci_hotplug_core.c
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_ctrl.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/hotplug/pciehp_pci.c
drivers/pci/hotplug/pcihp_skeleton.c [deleted file]
drivers/pci/hotplug/pnv_php.c
drivers/pci/hotplug/rpaphp_core.c
drivers/pci/hotplug/rpaphp_slot.c
drivers/pci/hotplug/s390_pci_hpc.c
drivers/pci/hotplug/sgi_hotplug.c
drivers/pci/hotplug/shpchp_core.c
drivers/pci/hotplug/shpchp_ctrl.c
drivers/pci/iov.c
drivers/pci/irq.c
drivers/pci/msi.c
drivers/pci/of.c
drivers/pci/pci-acpi.c
drivers/pci/pci-driver.c
drivers/pci/pci-sysfs.c
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/pcie/aer.c
drivers/pci/pcie/aspm.c
drivers/pci/pcie/dpc.c
drivers/pci/pcie/err.c
drivers/pci/pcie/portdrv.h
drivers/pci/pcie/portdrv_core.c
drivers/pci/pcie/portdrv_pci.c
drivers/pci/probe.c
drivers/pci/quirks.c
drivers/pci/remove.c
drivers/pci/rom.c
drivers/pci/switch/switchtec.c
drivers/pci/vpd.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/vfio/pci/vfio_pci.c
include/linux/dma-direction.h
include/linux/pci-dma-compat.h
include/linux/pci-epc.h
include/linux/pci-epf.h
include/linux/pci.h
include/linux/pci_hotplug.h
include/linux/pci_ids.h
include/linux/switchtec.h
include/uapi/linux/pci_regs.h
include/uapi/linux/pcitest.h
tools/pci/pcitest.c
tools/pci/pcitest.sh

diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-aer_stats b/Documentation/ABI/testing/sysfs-bus-pci-devices-aer_stats
new file mode 100644 (file)
index 0000000..4b0318c
--- /dev/null
@@ -0,0 +1,122 @@
+==========================
+PCIe Device AER statistics
+==========================
+These attributes show up under all the devices that are AER capable. These
+statistical counters indicate the errors "as seen/reported by the device".
+Note that this may mean that if an endpoint is causing problems, the AER
+counters may increment at its link partner (e.g. root port) because the
+errors may be "seen" / reported by the link partner and not the
+problematic endpoint itself (which may report all counters as 0 as it never
+saw any problems).
+
+Where:         /sys/bus/pci/devices/<dev>/aer_dev_correctable
+Date:          July 2018
+Kernel Version: 4.19.0
+Contact:       linux-pci@vger.kernel.org, rajatja@google.com
+Description:   List of correctable errors seen and reported by this
+               PCI device using ERR_COR. Note that since multiple errors may
+               be reported using a single ERR_COR message, thus
+               TOTAL_ERR_COR at the end of the file may not match the actual
+               total of all the errors in the file. Sample output:
+-------------------------------------------------------------------------
+localhost /sys/devices/pci0000:00/0000:00:1c.0 # cat aer_dev_correctable
+Receiver Error 2
+Bad TLP 0
+Bad DLLP 0
+RELAY_NUM Rollover 0
+Replay Timer Timeout 0
+Advisory Non-Fatal 0
+Corrected Internal Error 0
+Header Log Overflow 0
+TOTAL_ERR_COR 2
+-------------------------------------------------------------------------
+
+Where:         /sys/bus/pci/devices/<dev>/aer_dev_fatal
+Date:          July 2018
+Kernel Version: 4.19.0
+Contact:       linux-pci@vger.kernel.org, rajatja@google.com
+Description:   List of uncorrectable fatal errors seen and reported by this
+               PCI device using ERR_FATAL. Note that since multiple errors may
+               be reported using a single ERR_FATAL message, thus
+               TOTAL_ERR_FATAL at the end of the file may not match the actual
+               total of all the errors in the file. Sample output:
+-------------------------------------------------------------------------
+localhost /sys/devices/pci0000:00/0000:00:1c.0 # cat aer_dev_fatal
+Undefined 0
+Data Link Protocol 0
+Surprise Down Error 0
+Poisoned TLP 0
+Flow Control Protocol 0
+Completion Timeout 0
+Completer Abort 0
+Unexpected Completion 0
+Receiver Overflow 0
+Malformed TLP 0
+ECRC 0
+Unsupported Request 0
+ACS Violation 0
+Uncorrectable Internal Error 0
+MC Blocked TLP 0
+AtomicOp Egress Blocked 0
+TLP Prefix Blocked Error 0
+TOTAL_ERR_FATAL 0
+-------------------------------------------------------------------------
+
+Where:         /sys/bus/pci/devices/<dev>/aer_dev_nonfatal
+Date:          July 2018
+Kernel Version: 4.19.0
+Contact:       linux-pci@vger.kernel.org, rajatja@google.com
+Description:   List of uncorrectable nonfatal errors seen and reported by this
+               PCI device using ERR_NONFATAL. Note that since multiple errors
+               may be reported using a single ERR_FATAL message, thus
+               TOTAL_ERR_NONFATAL at the end of the file may not match the
+               actual total of all the errors in the file. Sample output:
+-------------------------------------------------------------------------
+localhost /sys/devices/pci0000:00/0000:00:1c.0 # cat aer_dev_nonfatal
+Undefined 0
+Data Link Protocol 0
+Surprise Down Error 0
+Poisoned TLP 0
+Flow Control Protocol 0
+Completion Timeout 0
+Completer Abort 0
+Unexpected Completion 0
+Receiver Overflow 0
+Malformed TLP 0
+ECRC 0
+Unsupported Request 0
+ACS Violation 0
+Uncorrectable Internal Error 0
+MC Blocked TLP 0
+AtomicOp Egress Blocked 0
+TLP Prefix Blocked Error 0
+TOTAL_ERR_NONFATAL 0
+-------------------------------------------------------------------------
+
+============================
+PCIe Rootport AER statistics
+============================
+These attributes show up under only the rootports (or root complex event
+collectors) that are AER capable. These indicate the number of error messages as
+"reported to" the rootport. Please note that the rootports also transmit
+(internally) the ERR_* messages for errors seen by the internal rootport PCI
+device, so these counters include them and are thus cumulative of all the error
+messages on the PCI hierarchy originating at that root port.
+
+Where:         /sys/bus/pci/devices/<dev>/aer_stats/aer_rootport_total_err_cor
+Date:          July 2018
+Kernel Version: 4.19.0
+Contact:       linux-pci@vger.kernel.org, rajatja@google.com
+Description:   Total number of ERR_COR messages reported to rootport.
+
+Where:     /sys/bus/pci/devices/<dev>/aer_stats/aer_rootport_total_err_fatal
+Date:          July 2018
+Kernel Version: 4.19.0
+Contact:       linux-pci@vger.kernel.org, rajatja@google.com
+Description:   Total number of ERR_FATAL messages reported to rootport.
+
+Where:     /sys/bus/pci/devices/<dev>/aer_stats/aer_rootport_total_err_nonfatal
+Date:          July 2018
+Kernel Version: 4.19.0
+Contact:       linux-pci@vger.kernel.org, rajatja@google.com
+Description:   Total number of ERR_NONFATAL messages reported to rootport.
index 00c9a90..206b1d5 100644 (file)
@@ -1,5 +1,7 @@
 00-INDEX
        - this file
+acpi-info.txt
+       - info on how PCI host bridges are represented in ACPI
 MSI-HOWTO.txt
        - the Message Signaled Interrupts (MSI) Driver Guide HOWTO and FAQ.
 PCIEBUS-HOWTO.txt
diff --git a/Documentation/PCI/acpi-info.txt b/Documentation/PCI/acpi-info.txt
new file mode 100644 (file)
index 0000000..3ffa3b0
--- /dev/null
@@ -0,0 +1,187 @@
+               ACPI considerations for PCI host bridges
+
+The general rule is that the ACPI namespace should describe everything the
+OS might use unless there's another way for the OS to find it [1, 2].
+
+For example, there's no standard hardware mechanism for enumerating PCI
+host bridges, so the ACPI namespace must describe each host bridge, the
+method for accessing PCI config space below it, the address space windows
+the host bridge forwards to PCI (using _CRS), and the routing of legacy
+INTx interrupts (using _PRT).
+
+PCI devices, which are below the host bridge, generally do not need to be
+described via ACPI.  The OS can discover them via the standard PCI
+enumeration mechanism, using config accesses to discover and identify
+devices and read and size their BARs.  However, ACPI may describe PCI
+devices if it provides power management or hotplug functionality for them
+or if the device has INTx interrupts connected by platform interrupt
+controllers and a _PRT is needed to describe those connections.
+
+ACPI resource description is done via _CRS objects of devices in the ACPI
+namespace [2]. Â  The _CRS is like a generalized PCI BAR: the OS can read
+_CRS and figure out what resource is being consumed even if it doesn't have
+a driver for the device [3]. Â That's important because it means an old OS
+can work correctly even on a system with new devices unknown to the OS.
+The new devices might not do anything, but the OS can at least make sure no
+resources conflict with them.
+
+Static tables like MCFG, HPET, ECDT, etc., are *not* mechanisms for
+reserving address space.  The static tables are for things the OS needs to
+know early in boot, before it can parse the ACPI namespace.  If a new table
+is defined, an old OS needs to operate correctly even though it ignores the
+table.  _CRS allows that because it is generic and understood by the old
+OS; a static table does not.
+
+If the OS is expected to manage a non-discoverable device described via
+ACPI, that device will have a specific _HID/_CID that tells the OS what
+driver to bind to it, and the _CRS tells the OS and the driver where the
+device's registers are.
+
+PCI host bridges are PNP0A03 or PNP0A08 devices. Â Their _CRS should
+describe all the address space they consume. Â This includes all the windows
+they forward down to the PCI bus, as well as registers of the host bridge
+itself that are not forwarded to PCI. Â The host bridge registers include
+things like secondary/subordinate bus registers that determine the bus
+range below the bridge, window registers that describe the apertures, etc.
+These are all device-specific, non-architected things, so the only way a
+PNP0A03/PNP0A08 driver can manage them is via _PRS/_CRS/_SRS, which contain
+the device-specific details. Â The host bridge registers also include ECAM
+space, since it is consumed by the host bridge.
+
+ACPI defines a Consumer/Producer bit to distinguish the bridge registers
+("Consumer") from the bridge apertures ("Producer") [4, 5], but early
+BIOSes didn't use that bit correctly.  The result is that the current ACPI
+spec defines Consumer/Producer only for the Extended Address Space
+descriptors; the bit should be ignored in the older QWord/DWord/Word
+Address Space descriptors.  Consequently, OSes have to assume all
+QWord/DWord/Word descriptors are windows.
+
+Prior to the addition of Extended Address Space descriptors, the failure of
+Consumer/Producer meant there was no way to describe bridge registers in
+the PNP0A03/PNP0A08 device itself.  The workaround was to describe the
+bridge registers (including ECAM space) in PNP0C02 catch-all devices [6].
+With the exception of ECAM, the bridge register space is device-specific
+anyway, so the generic PNP0A03/PNP0A08 driver (pci_root.c) has no need to
+know about it. Â 
+
+New architectures should be able to use "Consumer" Extended Address Space
+descriptors in the PNP0A03 device for bridge registers, including ECAM,
+although a strict interpretation of [6] might prohibit this.  Old x86 and
+ia64 kernels assume all address space descriptors, including "Consumer"
+Extended Address Space ones, are windows, so it would not be safe to
+describe bridge registers this way on those architectures.
+
+PNP0C02 "motherboard" devices are basically a catch-all. Â There's no
+programming model for them other than "don't use these resources for
+anything else." Â So a PNP0C02 _CRS should claim any address space that is
+(1) not claimed by _CRS under any other device object in the ACPI namespace
+and (2) should not be assigned by the OS to something else.
+
+The PCIe spec requires the Enhanced Configuration Access Method (ECAM)
+unless there's a standard firmware interface for config access, e.g., the
+ia64 SAL interface [7].  A host bridge consumes ECAM memory address space
+and converts memory accesses into PCI configuration accesses.  The spec
+defines the ECAM address space layout and functionality; only the base of
+the address space is device-specific.  An ACPI OS learns the base address
+from either the static MCFG table or a _CBA method in the PNP0A03 device.
+
+The MCFG table must describe the ECAM space of non-hot pluggable host
+bridges [8].  Since MCFG is a static table and can't be updated by hotplug,
+a _CBA method in the PNP0A03 device describes the ECAM space of a
+hot-pluggable host bridge [9].  Note that for both MCFG and _CBA, the base
+address always corresponds to bus 0, even if the bus range below the bridge
+(which is reported via _CRS) doesn't start at 0.
+
+
+[1] ACPI 6.2, sec 6.1:
+    For any device that is on a non-enumerable type of bus (for example, an
+    ISA bus), OSPM enumerates the devices' identifier(s) and the ACPI
+    system firmware must supply an _HID object ... for each device to
+    enable OSPM to do that.
+
+[2] ACPI 6.2, sec 3.7:
+    The OS enumerates motherboard devices simply by reading through the
+    ACPI Namespace looking for devices with hardware IDs.
+
+    Each device enumerated by ACPI includes ACPI-defined objects in the
+    ACPI Namespace that report the hardware resources the device could
+    occupy [_PRS], an object that reports the resources that are currently
+    used by the device [_CRS], and objects for configuring those resources
+    [_SRS].  The information is used by the Plug and Play OS (OSPM) to
+    configure the devices.
+
+[3] ACPI 6.2, sec 6.2:
+    OSPM uses device configuration objects to configure hardware resources
+    for devices enumerated via ACPI.  Device configuration objects provide
+    information about current and possible resource requirements, the
+    relationship between shared resources, and methods for configuring
+    hardware resources.
+
+    When OSPM enumerates a device, it calls _PRS to determine the resource
+    requirements of the device.  It may also call _CRS to find the current
+    resource settings for the device.  Using this information, the Plug and
+    Play system determines what resources the device should consume and
+    sets those resources by calling the device’s _SRS control method.
+
+    In ACPI, devices can consume resources (for example, legacy keyboards),
+    provide resources (for example, a proprietary PCI bridge), or do both.
+    Unless otherwise specified, resources for a device are assumed to be
+    taken from the nearest matching resource above the device in the device
+    hierarchy.
+
+[4] ACPI 6.2, sec 6.4.3.5.1, 2, 3, 4:
+    QWord/DWord/Word Address Space Descriptor (.1, .2, .3)
+    General Flags: Bit [0] Ignored
+
+    Extended Address Space Descriptor (.4)
+    General Flags: Bit [0] Consumer/Producer:
+       1–This device consumes this resource
+       0–This device produces and consumes this resource
+
+[5] ACPI 6.2, sec 19.6.43:
+    ResourceUsage specifies whether the Memory range is consumed by
+    this device (ResourceConsumer) or passed on to child devices
+    (ResourceProducer).  If nothing is specified, then
+    ResourceConsumer is assumed.
+
+[6] PCI Firmware 3.2, sec 4.1.2:
+    If the operating system does not natively comprehend reserving the
+    MMCFG region, the MMCFG region must be reserved by firmware.  The
+    address range reported in the MCFG table or by _CBA method (see Section
+    4.1.3) must be reserved by declaring a motherboard resource.  For most
+    systems, the motherboard resource would appear at the root of the ACPI
+    namespace (under \_SB) in a node with a _HID of EISAID (PNP0C02), and
+    the resources in this case should not be claimed in the root PCI bus’s
+    _CRS.  The resources can optionally be returned in Int15 E820 or
+    EFIGetMemoryMap as reserved memory but must always be reported through
+    ACPI as a motherboard resource.
+
+[7] PCI Express 4.0, sec 7.2.2:
+    For systems that are PC-compatible, or that do not implement a
+    processor-architecture-specific firmware interface standard that allows
+    access to the Configuration Space, the ECAM is required as defined in
+    this section.
+
+[8] PCI Firmware 3.2, sec 4.1.2:
+    The MCFG table is an ACPI table that is used to communicate the base
+    addresses corresponding to the non-hot removable PCI Segment Groups
+    range within a PCI Segment Group available to the operating system at
+    boot. This is required for the PC-compatible systems.
+
+    The MCFG table is only used to communicate the base addresses
+    corresponding to the PCI Segment Groups available to the system at
+    boot.
+
+[9] PCI Firmware 3.2, sec 4.1.3:
+    The _CBA (Memory mapped Configuration Base Address) control method is
+    an optional ACPI object that returns the 64-bit memory mapped
+    configuration base address for the hot plug capable host bridge. The
+    base address returned by _CBA is processor-relative address. The _CBA
+    control method evaluates to an Integer.
+
+    This control method appears under a host bridge object. When the _CBA
+    method appears under an active host bridge object, the operating system
+    evaluates this structure to identify the memory mapped configuration
+    base address corresponding to the PCI Segment Group for the bus number
+    range specified in _CRS method. An ACPI name space object that contains
+    the _CBA method must also contain a corresponding _SEG method.
index 3b68b95..cd76ba4 100644 (file)
@@ -15,3 +15,5 @@ subsys_id      : don't care
 interrupt_pin   : Should be 1 - INTA, 2 - INTB, 3 - INTC, 4 -INTD
 msi_interrupts  : Should be 1 to 32 depending on the number of MSI interrupts
                   to test
+msix_interrupts         : Should be 1 to 2048 depending on the number of MSI-X
+                  interrupts to test
index 9b1d668..e86a96b 100644 (file)
@@ -44,7 +44,7 @@ by the PCI controller driver.
         * clear_bar: ops to reset the BAR
         * alloc_addr_space: ops to allocate in PCI controller address space
         * free_addr_space: ops to free the allocated address space
-        * raise_irq: ops to raise a legacy or MSI interrupt
+        * raise_irq: ops to raise a legacy, MSI or MSI-X interrupt
         * start: ops to start the PCI link
         * stop: ops to stop the PCI link
 
@@ -96,7 +96,7 @@ by the PCI endpoint function driver.
 *) pci_epc_raise_irq()
 
    The PCI endpoint function driver should use pci_epc_raise_irq() to raise
-   Legacy Interrupt or MSI Interrupt.
+   Legacy Interrupt, MSI or MSI-X Interrupt.
 
 *) pci_epc_mem_alloc_addr()
 
index 0c519c9..5916f1f 100644 (file)
@@ -20,6 +20,8 @@ The PCI endpoint test device has the following registers:
        5) PCI_ENDPOINT_TEST_DST_ADDR
        6) PCI_ENDPOINT_TEST_SIZE
        7) PCI_ENDPOINT_TEST_CHECKSUM
+       8) PCI_ENDPOINT_TEST_IRQ_TYPE
+       9) PCI_ENDPOINT_TEST_IRQ_NUMBER
 
 *) PCI_ENDPOINT_TEST_MAGIC
 
@@ -34,10 +36,10 @@ that the endpoint device must perform.
 Bitfield Description:
   Bit 0                : raise legacy IRQ
   Bit 1                : raise MSI IRQ
-  Bit 2 - 7    : MSI interrupt number
-  Bit 8                : read command (read data from RC buffer)
-  Bit 9                : write command (write data to RC buffer)
-  Bit 10       : copy command (copy data from one RC buffer to another
+  Bit 2                : raise MSI-X IRQ
+  Bit 3                : read command (read data from RC buffer)
+  Bit 4                : write command (write data to RC buffer)
+  Bit 5                : copy command (copy data from one RC buffer to another
                  RC buffer)
 
 *) PCI_ENDPOINT_TEST_STATUS
@@ -64,3 +66,22 @@ COPY/READ command.
 
 This register contains the destination address (RC buffer address) for
 the COPY/WRITE command.
+
+*) PCI_ENDPOINT_TEST_IRQ_TYPE
+
+This register contains the interrupt type (Legacy/MSI) triggered
+for the READ/WRITE/COPY and raise IRQ (Legacy/MSI) commands.
+
+Possible types:
+ - Legacy      : 0
+ - MSI         : 1
+ - MSI-X       : 2
+
+*) PCI_ENDPOINT_TEST_IRQ_NUMBER
+
+This register contains the triggered ID interrupt.
+
+Admissible values:
+ - Legacy      : 0
+ - MSI         : [1 .. 32]
+ - MSI-X       : [1 .. 2048]
index 75f48c3..e40cf0f 100644 (file)
@@ -45,9 +45,9 @@ The PCI endpoint framework populates the directory with the following
 configurable fields.
 
        # ls functions/pci_epf_test/func1
-         baseclass_code        interrupt_pin   revid           subsys_vendor_id
-         cache_line_size       msi_interrupts  subclass_code   vendorid
-         deviceid              progif_code     subsys_id
+         baseclass_code        interrupt_pin   progif_code     subsys_id
+         cache_line_size       msi_interrupts  revid           subsys_vendorid
+         deviceid              msix_interrupts subclass_code   vendorid
 
 The PCI endpoint function driver populates these entries with default values
 when the device is bound to the driver. The pci-epf-test driver populates
@@ -67,6 +67,7 @@ device, the following commands can be used.
        # echo 0x104c > functions/pci_epf_test/func1/vendorid
        # echo 0xb500 > functions/pci_epf_test/func1/deviceid
        # echo 16 > functions/pci_epf_test/func1/msi_interrupts
+       # echo 8 > functions/pci_epf_test/func1/msix_interrupts
 
 1.5 Binding pci-epf-test Device to EP Controller
 
@@ -120,7 +121,9 @@ following commands.
 
        Interrupt tests
 
+       SET IRQ TYPE TO LEGACY:         OKAY
        LEGACY IRQ:     NOT OKAY
+       SET IRQ TYPE TO MSI:            OKAY
        MSI1:           OKAY
        MSI2:           OKAY
        MSI3:           OKAY
@@ -153,9 +156,30 @@ following commands.
        MSI30:          NOT OKAY
        MSI31:          NOT OKAY
        MSI32:          NOT OKAY
+       SET IRQ TYPE TO MSI-X:          OKAY
+       MSI-X1:         OKAY
+       MSI-X2:         OKAY
+       MSI-X3:         OKAY
+       MSI-X4:         OKAY
+       MSI-X5:         OKAY
+       MSI-X6:         OKAY
+       MSI-X7:         OKAY
+       MSI-X8:         OKAY
+       MSI-X9:         NOT OKAY
+       MSI-X10:        NOT OKAY
+       MSI-X11:        NOT OKAY
+       MSI-X12:        NOT OKAY
+       MSI-X13:        NOT OKAY
+       MSI-X14:        NOT OKAY
+       MSI-X15:        NOT OKAY
+       MSI-X16:        NOT OKAY
+       [...]
+       MSI-X2047:      NOT OKAY
+       MSI-X2048:      NOT OKAY
 
        Read Tests
 
+       SET IRQ TYPE TO MSI:            OKAY
        READ (      1 bytes):           OKAY
        READ (   1024 bytes):           OKAY
        READ (   1025 bytes):           OKAY
index acd0ddd..48ce790 100644 (file)
@@ -73,6 +73,11 @@ In the example, 'Requester ID' means the ID of the device who sends
 the error message to root port. Pls. refer to pci express specs for
 other fields.
 
+2.4 AER Statistics / Counters
+
+When PCIe AER errors are captured, the counters / statistics are also exposed
+in the form of sysfs attributes which are documented at
+Documentation/ABI/testing/sysfs-bus-pci-devices-aer_stats
 
 3. Developer Guide
 
index efc7aa7..e372208 100644 (file)
                        See header of drivers/block/paride/pcd.c.
                        See also Documentation/blockdev/paride.txt.
 
-       pci=option[,option...]  [PCI] various PCI subsystem options:
-               earlydump       [X86] dump PCI config space before the kernel
+       pci=option[,option...]  [PCI] various PCI subsystem options.
+
+                               Some options herein operate on a specific device
+                               or a set of devices (<pci_dev>). These are
+                               specified in one of the following formats:
+
+                               [<domain>:]<bus>:<dev>.<func>[/<dev>.<func>]*
+                               pci:<vendor>:<device>[:<subvendor>:<subdevice>]
+
+                               Note: the first format specifies a PCI
+                               bus/device/function address which may change
+                               if new hardware is inserted, if motherboard
+                               firmware changes, or due to changes caused
+                               by other kernel parameters. If the
+                               domain is left unspecified, it is
+                               taken to be zero. Optionally, a path
+                               to a device through multiple device/function
+                               addresses can be specified after the base
+                               address (this is more robust against
+                               renumbering issues).  The second format
+                               selects devices using IDs from the
+                               configuration space which may match multiple
+                               devices in the system.
+
+               earlydump       dump PCI config space before the kernel
                                changes anything
                off             [X86] don't probe for the PCI bus
                bios            [X86-32] force use of PCI BIOS, don't access
                                window. The default value is 64 megabytes.
                resource_alignment=
                                Format:
-                               [<order of align>@][<domain>:]<bus>:<slot>.<func>[; ...]
-                               [<order of align>@]pci:<vendor>:<device>\
-                                               [:<subvendor>:<subdevice>][; ...]
+                               [<order of align>@]<pci_dev>[; ...]
                                Specifies alignment and device to reassign
-                               aligned memory resources.
+                               aligned memory resources. How to
+                               specify the device is described above.
                                If <order of align> is not specified,
                                PAGE_SIZE is used as alignment.
                                PCI-PCI bridge can be specified, if resource
                                Adding the window is slightly risky (it may
                                conflict with unreported devices), so this
                                taints the kernel.
+               disable_acs_redir=<pci_dev>[; ...]
+                               Specify one or more PCI devices (in the format
+                               specified above) separated by semicolons.
+                               Each device specified will have the PCI ACS
+                               redirect capabilities forced off which will
+                               allow P2P traffic between devices through
+                               bridges without forcing it upstream. Note:
+                               this removes isolation between devices and
+                               may put more devices in an IOMMU group.
 
        pcie_aspm=      [PCIE] Forcibly enable or disable PCIe Active State Power
                        Management.
index 9a30523..4a0475e 100644 (file)
@@ -9,6 +9,9 @@ Required properties:
 
 Optional properties:
 - max-functions: Maximum number of functions that can be configured (default 1).
+- phys: From PHY bindings: List of Generic PHY phandles. One per lane if more
+  than one in the list.  If only one PHY listed it must manage all lanes. 
+- phy-names:  List of names to identify the PHY.
 
 Example:
 
@@ -19,4 +22,6 @@ pcie@fc000000 {
        reg-names = "reg", "mem";
        cdns,max-outbound-regions = <16>;
        max-functions = /bits/ 8 <8>;
+       phys = <&ep_phy0 &ep_phy1>;
+       phy-names = "pcie-lane0","pcie-lane1";
 };
index 20a33f3..91de69c 100644 (file)
@@ -24,6 +24,9 @@ Optional properties:
   translations (default 32)
 - vendor-id: The PCI vendor ID (16 bits, default is design dependent)
 - device-id: The PCI device ID (16 bits, default is design dependent)
+- phys: From PHY bindings: List of Generic PHY phandles. One per lane if more
+  than one in the list.  If only one PHY listed it must manage all lanes. 
+- phy-names:  List of names to identify the PHY.
 
 Example:
 
@@ -57,4 +60,7 @@ pcie@fb000000 {
        interrupt-map-mask = <0x0 0x0 0x0  0x7>;
 
        msi-parent = <&its_pci>;
+
+       phys = <&pcie_phy0>;
+       phy-names = "pcie-phy";
 };
index 480c860..c15c4f3 100644 (file)
@@ -166,6 +166,7 @@ Code  Seq#(hex)     Include File            Comments
 'P'    all     linux/soundcard.h       conflict!
 'P'    60-6F   sound/sscape_ioctl.h    conflict!
 'P'    00-0F   drivers/usb/class/usblp.c       conflict!
+'P'    01-09   drivers/misc/pci_endpoint_test.c        conflict!
 'Q'    all     linux/soundcard.h
 'R'    00-1F   linux/random.h          conflict!
 'R'    01      linux/rfkill.h          conflict!
index 4ebc359..58ccca4 100644 (file)
@@ -10,6 +10,7 @@ The PCI driver for the test device performs the following tests
        *) verifying addresses programmed in BAR
        *) raise legacy IRQ
        *) raise MSI IRQ
+       *) raise MSI-X IRQ
        *) read data
        *) write data
        *) copy data
@@ -25,6 +26,11 @@ ioctl
  PCITEST_LEGACY_IRQ: Tests legacy IRQ
  PCITEST_MSI: Tests message signalled interrupts. The MSI number
              to be tested should be passed as argument.
+ PCITEST_MSIX: Tests message signalled interrupts. The MSI-X number
+             to be tested should be passed as argument.
+ PCITEST_SET_IRQTYPE: Changes driver IRQ type configuration. The IRQ type
+             should be passed as argument (0: Legacy, 1:MSI, 2:MSI-X).
+ PCITEST_GET_IRQTYPE: Gets driver IRQ type configuration.
  PCITEST_WRITE: Perform write tests. The size of the buffer should be passed
                as argument.
  PCITEST_READ: Perform read tests. The size of the buffer should be passed
index 54eeb8d..843edfd 100644 (file)
@@ -1245,8 +1245,14 @@ config PCI
          VESA. If you have PCI, say Y, otherwise N.
 
 config PCI_DOMAINS
-       bool
+       bool "Support for multiple PCI domains"
        depends on PCI
+       help
+         Enable PCI domains kernel management. Say Y if your machine
+         has a PCI bus hierarchy that requires more than one PCI
+         domain (aka segment) to be correctly managed. Say N otherwise.
+
+         If you don't know what to do here, say N.
 
 config PCI_DOMAINS_GENERIC
        def_bool PCI_DOMAINS
index c46a728..25aac6e 100644 (file)
@@ -20,6 +20,7 @@ config ARCH_BCM_IPROC
        select GPIOLIB
        select ARM_AMBA
        select PINCTRL
+       select PCI_DOMAINS if PCI
        help
          This enables support for systems based on Broadcom IPROC architected SoCs.
          The IPROC complex contains one or more ARM CPUs along with common
index d0f62ea..4adb901 100644 (file)
@@ -10,6 +10,7 @@ menuconfig ARCH_SOCFPGA
        select HAVE_ARM_SCU
        select HAVE_ARM_TWD if SMP
        select MFD_SYSCON
+       select PCI_DOMAINS if PCI
 
 if ARCH_SOCFPGA
 config SOCFPGA_SUSPEND
index 155170d..dbfc705 100644 (file)
@@ -357,9 +357,6 @@ EXPORT_SYMBOL_GPL(dma_get_required_mask);
 
 static int __init dma_init(void)
 {
-#ifdef CONFIG_PCI
-       dma_debug_add_bus(&pci_bus_type);
-#endif
 #ifdef CONFIG_IBMVIO
        dma_debug_add_bus(&vio_bus_type);
 #endif
index fe9733f..471aac3 100644 (file)
@@ -42,6 +42,8 @@
 #include <asm/ppc-pci.h>
 #include <asm/eeh.h>
 
+#include "../../../drivers/pci/pci.h"
+
 /* hose_spinlock protects accesses to the the phb_bitmap. */
 static DEFINE_SPINLOCK(hose_spinlock);
 LIST_HEAD(hose_list);
@@ -1014,7 +1016,7 @@ void pcibios_setup_bus_devices(struct pci_bus *bus)
                /* Cardbus can call us to add new devices to a bus, so ignore
                 * those who are already fully discovered
                 */
-               if (dev->is_added)
+               if (pci_dev_is_added(dev))
                        continue;
 
                pcibios_setup_device(dev);
index 5bd0eb6..70b2e1e 100644 (file)
@@ -46,6 +46,7 @@
 
 #include "powernv.h"
 #include "pci.h"
+#include "../../../../drivers/pci/pci.h"
 
 #define PNV_IODA1_M64_NUM      16      /* Number of M64 BARs   */
 #define PNV_IODA1_M64_SEGS     8       /* Segments per M64 BAR */
@@ -3138,7 +3139,7 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev)
        struct pci_dn *pdn;
        int mul, total_vfs;
 
-       if (!pdev->is_physfn || pdev->is_added)
+       if (!pdev->is_physfn || pci_dev_is_added(pdev))
                return;
 
        pdn = pci_get_pdn(pdev);
index 139f0af..8a4868a 100644 (file)
@@ -71,6 +71,7 @@
 #include <asm/security_features.h>
 
 #include "pseries.h"
+#include "../../../../drivers/pci/pci.h"
 
 int CMO_PrPSP = -1;
 int CMO_SecPSP = -1;
@@ -664,7 +665,7 @@ static void pseries_pci_fixup_iov_resources(struct pci_dev *pdev)
        const int *indexes;
        struct device_node *dn = pci_device_to_OF_node(pdev);
 
-       if (!pdev->is_physfn || pdev->is_added)
+       if (!pdev->is_physfn || pci_dev_is_added(pdev))
                return;
        /*Firmware must support open sriov otherwise dont configure*/
        indexes = of_get_property(dn, "ibm,open-sriov-vf-bar-info", NULL);
index e5b7437..8256626 100644 (file)
@@ -160,8 +160,6 @@ static int __init pcibios_init(void)
        for (hose = hose_head; hose; hose = hose->next)
                pcibios_scanbus(hose);
 
-       dma_debug_add_bus(&pci_bus_type);
-
        pci_initialized = 1;
 
        return 0;
index e1084f7..94597a3 100644 (file)
@@ -15,8 +15,4 @@ extern void write_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset, u8 val);
 extern void write_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset, u16 val);
 
 extern int early_pci_allowed(void);
-
-extern unsigned int pci_early_dump_regs;
-extern void early_dump_pci_device(u8 bus, u8 slot, u8 func);
-extern void early_dump_pci_devices(void);
 #endif /* _ASM_X86_PCI_DIRECT_H */
index ab5d9dd..43f5863 100644 (file)
@@ -155,9 +155,6 @@ static int __init pci_iommu_init(void)
 {
        struct iommu_table_entry *p;
 
-#ifdef CONFIG_PCI
-       dma_debug_add_bus(&pci_bus_type);
-#endif
        x86_init.iommu.iommu_init();
 
        for (p = __iommu_table; p < __iommu_table_end; p++) {
index 2f86d88..480f250 100644 (file)
@@ -991,11 +991,6 @@ void __init setup_arch(char **cmdline_p)
                setup_clear_cpu_cap(X86_FEATURE_APIC);
        }
 
-#ifdef CONFIG_PCI
-       if (pci_early_dump_regs)
-               early_dump_pci_devices();
-#endif
-
        e820__reserve_setup_data();
        e820__finish_early_params();
 
index 563049c..d4ec117 100644 (file)
@@ -22,7 +22,6 @@
 unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2 |
                                PCI_PROBE_MMCONF;
 
-unsigned int pci_early_dump_regs;
 static int pci_bf_sort;
 int pci_routeirq;
 int noioapicquirk;
@@ -599,9 +598,6 @@ char *__init pcibios_setup(char *str)
                pci_probe |= PCI_BIG_ROOT_WINDOW;
                return NULL;
 #endif
-       } else if (!strcmp(str, "earlydump")) {
-               pci_early_dump_regs = 1;
-               return NULL;
        } else if (!strcmp(str, "routeirq")) {
                pci_routeirq = 1;
                return NULL;
index e5f753c..f5fc953 100644 (file)
@@ -57,47 +57,3 @@ int early_pci_allowed(void)
                        PCI_PROBE_CONF1;
 }
 
-void early_dump_pci_device(u8 bus, u8 slot, u8 func)
-{
-       u32 value[256 / 4];
-       int i;
-
-       pr_info("pci 0000:%02x:%02x.%d config space:\n", bus, slot, func);
-
-       for (i = 0; i < 256; i += 4)
-               value[i / 4] = read_pci_config(bus, slot, func, i);
-
-       print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, value, 256, false);
-}
-
-void early_dump_pci_devices(void)
-{
-       unsigned bus, slot, func;
-
-       if (!early_pci_allowed())
-               return;
-
-       for (bus = 0; bus < 256; bus++) {
-               for (slot = 0; slot < 32; slot++) {
-                       for (func = 0; func < 8; func++) {
-                               u32 class;
-                               u8 type;
-
-                               class = read_pci_config(bus, slot, func,
-                                                       PCI_CLASS_REVISION);
-                               if (class == 0xffffffff)
-                                       continue;
-
-                               early_dump_pci_device(bus, slot, func);
-
-                               if (func == 0) {
-                                       type = read_pci_config_byte(bus, slot,
-                                                                   func,
-                                                              PCI_HEADER_TYPE);
-                                       if (!(type & 0x80))
-                                               break;
-                               }
-                       }
-               }
-       }
-}
index 4d4371b..baf7c32 100644 (file)
@@ -905,9 +905,7 @@ static int trigger_sbr(struct hfi1_devdata *dd)
         * delay after a reset is required.  Per spec requirements,
         * the link is either working or not after that point.
         */
-       pci_reset_bridge_secondary_bus(dev->bus->self);
-
-       return 0;
+       return pci_reset_bus(dev);
 }
 
 /*
index 7b37046..896e2df 100644 (file)
 
 #include <uapi/linux/pcitest.h>
 
-#define DRV_MODULE_NAME                        "pci-endpoint-test"
-
-#define PCI_ENDPOINT_TEST_MAGIC                0x0
-
-#define PCI_ENDPOINT_TEST_COMMAND      0x4
-#define COMMAND_RAISE_LEGACY_IRQ       BIT(0)
-#define COMMAND_RAISE_MSI_IRQ          BIT(1)
-#define MSI_NUMBER_SHIFT               2
-/* 6 bits for MSI number */
-#define COMMAND_READ                    BIT(8)
-#define COMMAND_WRITE                   BIT(9)
-#define COMMAND_COPY                    BIT(10)
-
-#define PCI_ENDPOINT_TEST_STATUS       0x8
-#define STATUS_READ_SUCCESS             BIT(0)
-#define STATUS_READ_FAIL                BIT(1)
-#define STATUS_WRITE_SUCCESS            BIT(2)
-#define STATUS_WRITE_FAIL               BIT(3)
-#define STATUS_COPY_SUCCESS             BIT(4)
-#define STATUS_COPY_FAIL                BIT(5)
-#define STATUS_IRQ_RAISED               BIT(6)
-#define STATUS_SRC_ADDR_INVALID         BIT(7)
-#define STATUS_DST_ADDR_INVALID         BIT(8)
-
-#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR       0xc
+#define DRV_MODULE_NAME                                "pci-endpoint-test"
+
+#define IRQ_TYPE_UNDEFINED                     -1
+#define IRQ_TYPE_LEGACY                                0
+#define IRQ_TYPE_MSI                           1
+#define IRQ_TYPE_MSIX                          2
+
+#define PCI_ENDPOINT_TEST_MAGIC                        0x0
+
+#define PCI_ENDPOINT_TEST_COMMAND              0x4
+#define COMMAND_RAISE_LEGACY_IRQ               BIT(0)
+#define COMMAND_RAISE_MSI_IRQ                  BIT(1)
+#define COMMAND_RAISE_MSIX_IRQ                 BIT(2)
+#define COMMAND_READ                           BIT(3)
+#define COMMAND_WRITE                          BIT(4)
+#define COMMAND_COPY                           BIT(5)
+
+#define PCI_ENDPOINT_TEST_STATUS               0x8
+#define STATUS_READ_SUCCESS                    BIT(0)
+#define STATUS_READ_FAIL                       BIT(1)
+#define STATUS_WRITE_SUCCESS                   BIT(2)
+#define STATUS_WRITE_FAIL                      BIT(3)
+#define STATUS_COPY_SUCCESS                    BIT(4)
+#define STATUS_COPY_FAIL                       BIT(5)
+#define STATUS_IRQ_RAISED                      BIT(6)
+#define STATUS_SRC_ADDR_INVALID                        BIT(7)
+#define STATUS_DST_ADDR_INVALID                        BIT(8)
+
+#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR       0x0c
 #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR       0x10
 
 #define PCI_ENDPOINT_TEST_LOWER_DST_ADDR       0x14
 #define PCI_ENDPOINT_TEST_UPPER_DST_ADDR       0x18
 
-#define PCI_ENDPOINT_TEST_SIZE         0x1c
-#define PCI_ENDPOINT_TEST_CHECKSUM     0x20
+#define PCI_ENDPOINT_TEST_SIZE                 0x1c
+#define PCI_ENDPOINT_TEST_CHECKSUM             0x20
+
+#define PCI_ENDPOINT_TEST_IRQ_TYPE             0x24
+#define PCI_ENDPOINT_TEST_IRQ_NUMBER           0x28
 
 static DEFINE_IDA(pci_endpoint_test_ida);
 
@@ -77,6 +84,10 @@ static bool no_msi;
 module_param(no_msi, bool, 0444);
 MODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test");
 
+static int irq_type = IRQ_TYPE_MSI;
+module_param(irq_type, int, 0444);
+MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI, 2 - MSI-X)");
+
 enum pci_barno {
        BAR_0,
        BAR_1,
@@ -103,7 +114,7 @@ struct pci_endpoint_test {
 struct pci_endpoint_test_data {
        enum pci_barno test_reg_bar;
        size_t alignment;
-       bool no_msi;
+       int irq_type;
 };
 
 static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test,
@@ -147,6 +158,100 @@ static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static void pci_endpoint_test_free_irq_vectors(struct pci_endpoint_test *test)
+{
+       struct pci_dev *pdev = test->pdev;
+
+       pci_free_irq_vectors(pdev);
+}
+
+static bool pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test,
+                                               int type)
+{
+       int irq = -1;
+       struct pci_dev *pdev = test->pdev;
+       struct device *dev = &pdev->dev;
+       bool res = true;
+
+       switch (type) {
+       case IRQ_TYPE_LEGACY:
+               irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY);
+               if (irq < 0)
+                       dev_err(dev, "Failed to get Legacy interrupt\n");
+               break;
+       case IRQ_TYPE_MSI:
+               irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
+               if (irq < 0)
+                       dev_err(dev, "Failed to get MSI interrupts\n");
+               break;
+       case IRQ_TYPE_MSIX:
+               irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX);
+               if (irq < 0)
+                       dev_err(dev, "Failed to get MSI-X interrupts\n");
+               break;
+       default:
+               dev_err(dev, "Invalid IRQ type selected\n");
+       }
+
+       if (irq < 0) {
+               irq = 0;
+               res = false;
+       }
+       test->num_irqs = irq;
+
+       return res;
+}
+
+static void pci_endpoint_test_release_irq(struct pci_endpoint_test *test)
+{
+       int i;
+       struct pci_dev *pdev = test->pdev;
+       struct device *dev = &pdev->dev;
+
+       for (i = 0; i < test->num_irqs; i++)
+               devm_free_irq(dev, pci_irq_vector(pdev, i), test);
+
+       test->num_irqs = 0;
+}
+
+static bool pci_endpoint_test_request_irq(struct pci_endpoint_test *test)
+{
+       int i;
+       int err;
+       struct pci_dev *pdev = test->pdev;
+       struct device *dev = &pdev->dev;
+
+       for (i = 0; i < test->num_irqs; i++) {
+               err = devm_request_irq(dev, pci_irq_vector(pdev, i),
+                                      pci_endpoint_test_irqhandler,
+                                      IRQF_SHARED, DRV_MODULE_NAME, test);
+               if (err)
+                       goto fail;
+       }
+
+       return true;
+
+fail:
+       switch (irq_type) {
+       case IRQ_TYPE_LEGACY:
+               dev_err(dev, "Failed to request IRQ %d for Legacy\n",
+                       pci_irq_vector(pdev, i));
+               break;
+       case IRQ_TYPE_MSI:
+               dev_err(dev, "Failed to request IRQ %d for MSI %d\n",
+                       pci_irq_vector(pdev, i),
+                       i + 1);
+               break;
+       case IRQ_TYPE_MSIX:
+               dev_err(dev, "Failed to request IRQ %d for MSI-X %d\n",
+                       pci_irq_vector(pdev, i),
+                       i + 1);
+               break;
+       }
+
+       return false;
+}
+
 static bool pci_endpoint_test_bar(struct pci_endpoint_test *test,
                                  enum pci_barno barno)
 {
@@ -179,6 +284,9 @@ static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test)
 {
        u32 val;
 
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
+                                IRQ_TYPE_LEGACY);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 0);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
                                 COMMAND_RAISE_LEGACY_IRQ);
        val = wait_for_completion_timeout(&test->irq_raised,
@@ -190,14 +298,18 @@ static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test)
 }
 
 static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
-                                     u8 msi_num)
+                                      u16 msi_num, bool msix)
 {
        u32 val;
        struct pci_dev *pdev = test->pdev;
 
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
+                                msix == false ? IRQ_TYPE_MSI :
+                                IRQ_TYPE_MSIX);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
-                                msi_num << MSI_NUMBER_SHIFT |
-                                COMMAND_RAISE_MSI_IRQ);
+                                msix == false ? COMMAND_RAISE_MSI_IRQ :
+                                COMMAND_RAISE_MSIX_IRQ);
        val = wait_for_completion_timeout(&test->irq_raised,
                                          msecs_to_jiffies(1000));
        if (!val)
@@ -230,6 +342,11 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
        if (size > SIZE_MAX - alignment)
                goto err;
 
+       if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
+               dev_err(dev, "Invalid IRQ type option\n");
+               goto err;
+       }
+
        orig_src_addr = dma_alloc_coherent(dev, size + alignment,
                                           &orig_src_phys_addr, GFP_KERNEL);
        if (!orig_src_addr) {
@@ -281,8 +398,10 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE,
                                 size);
 
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
-                                1 << MSI_NUMBER_SHIFT | COMMAND_COPY);
+                                COMMAND_COPY);
 
        wait_for_completion(&test->irq_raised);
 
@@ -318,6 +437,11 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
        if (size > SIZE_MAX - alignment)
                goto err;
 
+       if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
+               dev_err(dev, "Invalid IRQ type option\n");
+               goto err;
+       }
+
        orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr,
                                       GFP_KERNEL);
        if (!orig_addr) {
@@ -348,8 +472,10 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
 
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
 
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
-                                1 << MSI_NUMBER_SHIFT | COMMAND_READ);
+                                COMMAND_READ);
 
        wait_for_completion(&test->irq_raised);
 
@@ -379,6 +505,11 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
        if (size > SIZE_MAX - alignment)
                goto err;
 
+       if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
+               dev_err(dev, "Invalid IRQ type option\n");
+               goto err;
+       }
+
        orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr,
                                       GFP_KERNEL);
        if (!orig_addr) {
@@ -403,8 +534,10 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
 
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
 
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
-                                1 << MSI_NUMBER_SHIFT | COMMAND_WRITE);
+                                COMMAND_WRITE);
 
        wait_for_completion(&test->irq_raised);
 
@@ -417,6 +550,38 @@ err:
        return ret;
 }
 
+static bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
+                                     int req_irq_type)
+{
+       struct pci_dev *pdev = test->pdev;
+       struct device *dev = &pdev->dev;
+
+       if (req_irq_type < IRQ_TYPE_LEGACY || req_irq_type > IRQ_TYPE_MSIX) {
+               dev_err(dev, "Invalid IRQ type option\n");
+               return false;
+       }
+
+       if (irq_type == req_irq_type)
+               return true;
+
+       pci_endpoint_test_release_irq(test);
+       pci_endpoint_test_free_irq_vectors(test);
+
+       if (!pci_endpoint_test_alloc_irq_vectors(test, req_irq_type))
+               goto err;
+
+       if (!pci_endpoint_test_request_irq(test))
+               goto err;
+
+       irq_type = req_irq_type;
+       return true;
+
+err:
+       pci_endpoint_test_free_irq_vectors(test);
+       irq_type = IRQ_TYPE_UNDEFINED;
+       return false;
+}
+
 static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
                                    unsigned long arg)
 {
@@ -436,7 +601,8 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
                ret = pci_endpoint_test_legacy_irq(test);
                break;
        case PCITEST_MSI:
-               ret = pci_endpoint_test_msi_irq(test, arg);
+       case PCITEST_MSIX:
+               ret = pci_endpoint_test_msi_irq(test, arg, cmd == PCITEST_MSIX);
                break;
        case PCITEST_WRITE:
                ret = pci_endpoint_test_write(test, arg);
@@ -447,6 +613,12 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
        case PCITEST_COPY:
                ret = pci_endpoint_test_copy(test, arg);
                break;
+       case PCITEST_SET_IRQTYPE:
+               ret = pci_endpoint_test_set_irq(test, arg);
+               break;
+       case PCITEST_GET_IRQTYPE:
+               ret = irq_type;
+               break;
        }
 
 ret:
@@ -462,9 +634,7 @@ static const struct file_operations pci_endpoint_test_fops = {
 static int pci_endpoint_test_probe(struct pci_dev *pdev,
                                   const struct pci_device_id *ent)
 {
-       int i;
        int err;
-       int irq = 0;
        int id;
        char name[20];
        enum pci_barno bar;
@@ -486,11 +656,14 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
        test->alignment = 0;
        test->pdev = pdev;
 
+       if (no_msi)
+               irq_type = IRQ_TYPE_LEGACY;
+
        data = (struct pci_endpoint_test_data *)ent->driver_data;
        if (data) {
                test_reg_bar = data->test_reg_bar;
                test->alignment = data->alignment;
-               no_msi = data->no_msi;
+               irq_type = data->irq_type;
        }
 
        init_completion(&test->irq_raised);
@@ -510,28 +683,11 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
 
        pci_set_master(pdev);
 
-       if (!no_msi) {
-               irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
-               if (irq < 0)
-                       dev_err(dev, "Failed to get MSI interrupts\n");
-               test->num_irqs = irq;
-       }
-
-       err = devm_request_irq(dev, pdev->irq, pci_endpoint_test_irqhandler,
-                              IRQF_SHARED, DRV_MODULE_NAME, test);
-       if (err) {
-               dev_err(dev, "Failed to request IRQ %d\n", pdev->irq);
-               goto err_disable_msi;
-       }
+       if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type))
+               goto err_disable_irq;
 
-       for (i = 1; i < irq; i++) {
-               err = devm_request_irq(dev, pci_irq_vector(pdev, i),
-                                      pci_endpoint_test_irqhandler,
-                                      IRQF_SHARED, DRV_MODULE_NAME, test);
-               if (err)
-                       dev_err(dev, "failed to request IRQ %d for MSI %d\n",
-                               pci_irq_vector(pdev, i), i + 1);
-       }
+       if (!pci_endpoint_test_request_irq(test))
+               goto err_disable_irq;
 
        for (bar = BAR_0; bar <= BAR_5; bar++) {
                if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
@@ -590,12 +746,10 @@ err_iounmap:
                if (test->bar[bar])
                        pci_iounmap(pdev, test->bar[bar]);
        }
+       pci_endpoint_test_release_irq(test);
 
-       for (i = 0; i < irq; i++)
-               devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test);
-
-err_disable_msi:
-       pci_disable_msi(pdev);
+err_disable_irq:
+       pci_endpoint_test_free_irq_vectors(test);
        pci_release_regions(pdev);
 
 err_disable_pdev:
@@ -607,7 +761,6 @@ err_disable_pdev:
 static void pci_endpoint_test_remove(struct pci_dev *pdev)
 {
        int id;
-       int i;
        enum pci_barno bar;
        struct pci_endpoint_test *test = pci_get_drvdata(pdev);
        struct miscdevice *misc_device = &test->miscdev;
@@ -624,9 +777,10 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
                if (test->bar[bar])
                        pci_iounmap(pdev, test->bar[bar]);
        }
-       for (i = 0; i < test->num_irqs; i++)
-               devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), test);
-       pci_disable_msi(pdev);
+
+       pci_endpoint_test_release_irq(test);
+       pci_endpoint_test_free_irq_vectors(test);
+
        pci_release_regions(pdev);
        pci_disable_device(pdev);
 }
index f707709..c77fda0 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/if.h>
 #include <linux/if_vlan.h>
 #include <linux/pci.h>
-#include <linux/pci-aspm.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/ip.h>
index 46b76d5..152283d 100644 (file)
@@ -240,7 +240,6 @@ static int nfp_pcie_sriov_read_nfd_limit(struct nfp_pf *pf)
                return pci_sriov_set_totalvfs(pf->pdev, pf->limit_vfs);
 
        pf->limit_vfs = ~0;
-       pci_sriov_set_totalvfs(pf->pdev, 0); /* 0 is unset */
        /* Allow any setting for backwards compatibility if symbol not found */
        if (err == -ENOENT)
                return 0;
@@ -668,7 +667,7 @@ static int nfp_pci_probe(struct pci_dev *pdev,
 
        err = nfp_net_pci_probe(pf);
        if (err)
-               goto err_sriov_unlimit;
+               goto err_fw_unload;
 
        err = nfp_hwmon_register(pf);
        if (err) {
@@ -680,8 +679,6 @@ static int nfp_pci_probe(struct pci_dev *pdev,
 
 err_net_remove:
        nfp_net_pci_remove(pf);
-err_sriov_unlimit:
-       pci_sriov_set_totalvfs(pf->pdev, 0);
 err_fw_unload:
        kfree(pf->rtbl);
        nfp_mip_close(pf->mip);
@@ -715,7 +712,6 @@ static void nfp_pci_remove(struct pci_dev *pdev)
        nfp_hwmon_unregister(pf);
 
        nfp_pcie_sriov_disable(pdev);
-       pci_sriov_set_totalvfs(pf->pdev, 0);
 
        nfp_net_pci_remove(pf);
 
index 645f0fb..92b2dd3 100644 (file)
@@ -18,7 +18,6 @@
 
 #include <linux/nl80211.h>
 #include <linux/pci.h>
-#include <linux/pci-aspm.h>
 #include <linux/module.h>
 #include "ath9k.h"
 
index 38234bd..d6c55e1 100644 (file)
@@ -72,7 +72,6 @@
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/pci.h>
-#include <linux/pci-aspm.h>
 #include <linux/acpi.h>
 
 #include "fw/acpi.h"
index bc0ca5f..283e2e6 100644 (file)
 #include <linux/io.h>
 #include <linux/pci.h>
 
-/*
- * This variable should be used with the
- * pci_driver structure initialization.
- */
-#define PCI_DEVICE_DATA(__ops) .driver_data = (kernel_ulong_t)(__ops)
-
 /*
  * PCI driver handlers.
  */
index f624ae2..5ee5f40 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/kthread.h>
 #include <linux/interrupt.h>
 #include <linux/ntb.h>
+#include <linux/pci.h>
 
 MODULE_DESCRIPTION("Microsemi Switchtec(tm) NTB Driver");
 MODULE_VERSION("0.1");
@@ -1487,7 +1488,7 @@ static int switchtec_ntb_add(struct device *dev,
 
        stdev->sndev = NULL;
 
-       if (stdev->pdev->class != MICROSEMI_NTB_CLASSCODE)
+       if (stdev->pdev->class != (PCI_CLASS_BRIDGE_OTHER << 8))
                return -ENODEV;
 
        sndev = kzalloc_node(sizeof(*sndev), GFP_KERNEL, dev_to_node(dev));
index 5352019..1b2cfe5 100644 (file)
@@ -28,10 +28,10 @@ obj-$(CONFIG_PCI_PF_STUB)   += pci-pf-stub.o
 obj-$(CONFIG_PCI_ECAM)         += ecam.o
 obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
 
-obj-y                          += controller/
-obj-y                          += switch/
-
 # Endpoint library must be initialized before its users
 obj-$(CONFIG_PCI_ENDPOINT)     += endpoint/
 
+obj-y                          += controller/
+obj-y                          += switch/
+
 ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
index 4923a2a..5b78f3b 100644 (file)
@@ -273,6 +273,9 @@ int pci_enable_pasid(struct pci_dev *pdev, int features)
        if (WARN_ON(pdev->pasid_enabled))
                return -EBUSY;
 
+       if (!pdev->eetlp_prefix_path)
+               return -EINVAL;
+
        pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
        if (!pos)
                return -EINVAL;
index 35b7fc8..5cb40b2 100644 (file)
@@ -330,7 +330,7 @@ void pci_bus_add_device(struct pci_dev *dev)
                return;
        }
 
-       dev->is_added = 1;
+       pci_dev_assign_added(dev, true);
 }
 EXPORT_SYMBOL_GPL(pci_bus_add_device);
 
@@ -347,14 +347,14 @@ void pci_bus_add_devices(const struct pci_bus *bus)
 
        list_for_each_entry(dev, &bus->devices, bus_list) {
                /* Skip already-added devices */
-               if (dev->is_added)
+               if (pci_dev_is_added(dev))
                        continue;
                pci_bus_add_device(dev);
        }
 
        list_for_each_entry(dev, &bus->devices, bus_list) {
                /* Skip if device attach failed */
-               if (!dev->is_added)
+               if (!pci_dev_is_added(dev))
                        continue;
                child = dev->subordinate;
                if (child)
index fc4dbcd..028b287 100644 (file)
@@ -96,14 +96,13 @@ config PCI_HOST_GENERIC
        depends on OF
        select PCI_HOST_COMMON
        select IRQ_DOMAIN
-       select PCI_DOMAINS
        help
          Say Y here if you want to support a simple generic PCI host
          controller, such as the one emulated by kvmtool.
 
 config PCIE_XILINX
        bool "Xilinx AXI PCIe host bridge support"
-       depends on ARCH_ZYNQ || MICROBLAZE || (MIPS && PCI_DRIVERS_GENERIC) || COMPILE_TEST
+       depends on OF || COMPILE_TEST
        help
          Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
          Host Bridge driver.
@@ -138,7 +137,6 @@ config PCI_VERSATILE
 
 config PCIE_IPROC
        tristate
-       select PCI_DOMAINS
        help
          This enables the iProc PCIe core controller support for Broadcom's
          iProc family of SoCs. An appropriate bus interface driver needs
@@ -176,7 +174,6 @@ config PCIE_IPROC_MSI
 config PCIE_ALTERA
        bool "Altera PCIe controller"
        depends on ARM || NIOS2 || COMPILE_TEST
-       select PCI_DOMAINS
        help
          Say Y here if you want to enable PCIe controller support on Altera
          FPGA.
index 16f52c6..91b0194 100644 (file)
@@ -58,7 +58,6 @@ config PCIE_DW_PLAT_HOST
        depends on PCI && PCI_MSI_IRQ_DOMAIN
        select PCIE_DW_HOST
        select PCIE_DW_PLAT
-       default y
        help
          Enables support for the PCIe controller in the Designware IP to
          work in host mode. There are two instances of PCIe controller in
index 345aab5..ce9224a 100644 (file)
@@ -370,7 +370,7 @@ static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx,
 }
 
 static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
-                                enum pci_epc_irq_type type, u8 interrupt_num)
+                                enum pci_epc_irq_type type, u16 interrupt_num)
 {
        struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
        struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
index 4cc1e5d..cee5f2f 100644 (file)
@@ -421,7 +421,6 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *ep,
                }
        }
 
-       pp->root_bus_nr = -1;
        pp->ops = &exynos_pcie_host_ops;
 
        ret = dw_pcie_host_init(pp);
index 80f6046..4a9a673 100644 (file)
@@ -667,7 +667,6 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
                }
        }
 
-       pp->root_bus_nr = -1;
        pp->ops = &imx6_pcie_host_ops;
 
        ret = dw_pcie_host_init(pp);
index 3722a5f..e88bd22 100644 (file)
@@ -347,7 +347,6 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
                }
        }
 
-       pp->root_bus_nr = -1;
        pp->ops = &keystone_pcie_host_ops;
        ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np);
        if (ret) {
index 072fd7e..0c389a3 100644 (file)
@@ -172,7 +172,6 @@ static int armada8k_add_pcie_port(struct armada8k_pcie *pcie,
        struct device *dev = &pdev->dev;
        int ret;
 
-       pp->root_bus_nr = -1;
        pp->ops = &armada8k_pcie_host_ops;
 
        pp->irq = platform_get_irq(pdev, 0);
index 321b56c..dba83ab 100644 (file)
@@ -399,7 +399,6 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,
                }
        }
 
-       pp->root_bus_nr = -1;
        pp->ops = &artpec6_pcie_host_ops;
 
        ret = dw_pcie_host_init(pp);
@@ -428,7 +427,7 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep)
 }
 
 static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
-                                 enum pci_epc_irq_type type, u8 interrupt_num)
+                                 enum pci_epc_irq_type type, u16 interrupt_num)
 {
        struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
 
index 8650416..1e7b022 100644 (file)
@@ -40,6 +40,39 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
        __dw_pcie_ep_reset_bar(pci, bar, 0);
 }
 
+static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr,
+                             u8 cap)
+{
+       u8 cap_id, next_cap_ptr;
+       u16 reg;
+
+       reg = dw_pcie_readw_dbi(pci, cap_ptr);
+       next_cap_ptr = (reg & 0xff00) >> 8;
+       cap_id = (reg & 0x00ff);
+
+       if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
+               return 0;
+
+       if (cap_id == cap)
+               return cap_ptr;
+
+       return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
+}
+
+static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap)
+{
+       u8 next_cap_ptr;
+       u16 reg;
+
+       reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST);
+       next_cap_ptr = (reg & 0x00ff);
+
+       if (!next_cap_ptr)
+               return 0;
+
+       return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
+}
+
 static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
                                   struct pci_epf_header *hdr)
 {
@@ -213,36 +246,84 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
 
 static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
 {
-       int val;
        struct dw_pcie_ep *ep = epc_get_drvdata(epc);
        struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       u32 val, reg;
+
+       if (!ep->msi_cap)
+               return -EINVAL;
+
+       reg = ep->msi_cap + PCI_MSI_FLAGS;
+       val = dw_pcie_readw_dbi(pci, reg);
+       if (!(val & PCI_MSI_FLAGS_ENABLE))
+               return -EINVAL;
+
+       val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
+
+       return val;
+}
+
+static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts)
+{
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       u32 val, reg;
+
+       if (!ep->msi_cap)
+               return -EINVAL;
+
+       reg = ep->msi_cap + PCI_MSI_FLAGS;
+       val = dw_pcie_readw_dbi(pci, reg);
+       val &= ~PCI_MSI_FLAGS_QMASK;
+       val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
+       dw_pcie_dbi_ro_wr_en(pci);
+       dw_pcie_writew_dbi(pci, reg, val);
+       dw_pcie_dbi_ro_wr_dis(pci);
+
+       return 0;
+}
+
+static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
+{
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       u32 val, reg;
 
-       val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL);
-       if (!(val & MSI_CAP_MSI_EN_MASK))
+       if (!ep->msix_cap)
                return -EINVAL;
 
-       val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT;
+       reg = ep->msix_cap + PCI_MSIX_FLAGS;
+       val = dw_pcie_readw_dbi(pci, reg);
+       if (!(val & PCI_MSIX_FLAGS_ENABLE))
+               return -EINVAL;
+
+       val &= PCI_MSIX_FLAGS_QSIZE;
+
        return val;
 }
 
-static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int)
+static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)
 {
-       int val;
        struct dw_pcie_ep *ep = epc_get_drvdata(epc);
        struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       u32 val, reg;
+
+       if (!ep->msix_cap)
+               return -EINVAL;
 
-       val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL);
-       val &= ~MSI_CAP_MMC_MASK;
-       val |= (encode_int << MSI_CAP_MMC_SHIFT) & MSI_CAP_MMC_MASK;
+       reg = ep->msix_cap + PCI_MSIX_FLAGS;
+       val = dw_pcie_readw_dbi(pci, reg);
+       val &= ~PCI_MSIX_FLAGS_QSIZE;
+       val |= interrupts;
        dw_pcie_dbi_ro_wr_en(pci);
-       dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val);
+       dw_pcie_writew_dbi(pci, reg, val);
        dw_pcie_dbi_ro_wr_dis(pci);
 
        return 0;
 }
 
 static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
-                               enum pci_epc_irq_type type, u8 interrupt_num)
+                               enum pci_epc_irq_type type, u16 interrupt_num)
 {
        struct dw_pcie_ep *ep = epc_get_drvdata(epc);
 
@@ -282,32 +363,52 @@ static const struct pci_epc_ops epc_ops = {
        .unmap_addr             = dw_pcie_ep_unmap_addr,
        .set_msi                = dw_pcie_ep_set_msi,
        .get_msi                = dw_pcie_ep_get_msi,
+       .set_msix               = dw_pcie_ep_set_msix,
+       .get_msix               = dw_pcie_ep_get_msix,
        .raise_irq              = dw_pcie_ep_raise_irq,
        .start                  = dw_pcie_ep_start,
        .stop                   = dw_pcie_ep_stop,
 };
 
+int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       struct device *dev = pci->dev;
+
+       dev_err(dev, "EP cannot trigger legacy IRQs\n");
+
+       return -EINVAL;
+}
+
 int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
                             u8 interrupt_num)
 {
        struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
        struct pci_epc *epc = ep->epc;
        u16 msg_ctrl, msg_data;
-       u32 msg_addr_lower, msg_addr_upper;
+       u32 msg_addr_lower, msg_addr_upper, reg;
        u64 msg_addr;
        bool has_upper;
        int ret;
 
+       if (!ep->msi_cap)
+               return -EINVAL;
+
        /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */
-       msg_ctrl = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL);
+       reg = ep->msi_cap + PCI_MSI_FLAGS;
+       msg_ctrl = dw_pcie_readw_dbi(pci, reg);
        has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
-       msg_addr_lower = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32);
+       reg = ep->msi_cap + PCI_MSI_ADDRESS_LO;
+       msg_addr_lower = dw_pcie_readl_dbi(pci, reg);
        if (has_upper) {
-               msg_addr_upper = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32);
-               msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_64);
+               reg = ep->msi_cap + PCI_MSI_ADDRESS_HI;
+               msg_addr_upper = dw_pcie_readl_dbi(pci, reg);
+               reg = ep->msi_cap + PCI_MSI_DATA_64;
+               msg_data = dw_pcie_readw_dbi(pci, reg);
        } else {
                msg_addr_upper = 0;
-               msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_32);
+               reg = ep->msi_cap + PCI_MSI_DATA_32;
+               msg_data = dw_pcie_readw_dbi(pci, reg);
        }
        msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
        ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
@@ -322,6 +423,64 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
        return 0;
 }
 
+int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
+                            u16 interrupt_num)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       struct pci_epc *epc = ep->epc;
+       u16 tbl_offset, bir;
+       u32 bar_addr_upper, bar_addr_lower;
+       u32 msg_addr_upper, msg_addr_lower;
+       u32 reg, msg_data, vec_ctrl;
+       u64 tbl_addr, msg_addr, reg_u64;
+       void __iomem *msix_tbl;
+       int ret;
+
+       reg = ep->msix_cap + PCI_MSIX_TABLE;
+       tbl_offset = dw_pcie_readl_dbi(pci, reg);
+       bir = (tbl_offset & PCI_MSIX_TABLE_BIR);
+       tbl_offset &= PCI_MSIX_TABLE_OFFSET;
+       tbl_offset >>= 3;
+
+       reg = PCI_BASE_ADDRESS_0 + (4 * bir);
+       bar_addr_upper = 0;
+       bar_addr_lower = dw_pcie_readl_dbi(pci, reg);
+       reg_u64 = (bar_addr_lower & PCI_BASE_ADDRESS_MEM_TYPE_MASK);
+       if (reg_u64 == PCI_BASE_ADDRESS_MEM_TYPE_64)
+               bar_addr_upper = dw_pcie_readl_dbi(pci, reg + 4);
+
+       tbl_addr = ((u64) bar_addr_upper) << 32 | bar_addr_lower;
+       tbl_addr += (tbl_offset + ((interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE));
+       tbl_addr &= PCI_BASE_ADDRESS_MEM_MASK;
+
+       msix_tbl = ioremap_nocache(ep->phys_base + tbl_addr,
+                                  PCI_MSIX_ENTRY_SIZE);
+       if (!msix_tbl)
+               return -EINVAL;
+
+       msg_addr_lower = readl(msix_tbl + PCI_MSIX_ENTRY_LOWER_ADDR);
+       msg_addr_upper = readl(msix_tbl + PCI_MSIX_ENTRY_UPPER_ADDR);
+       msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
+       msg_data = readl(msix_tbl + PCI_MSIX_ENTRY_DATA);
+       vec_ctrl = readl(msix_tbl + PCI_MSIX_ENTRY_VECTOR_CTRL);
+
+       iounmap(msix_tbl);
+
+       if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)
+               return -EPERM;
+
+       ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
+                                 epc->mem->page_size);
+       if (ret)
+               return ret;
+
+       writel(msg_data, ep->msi_mem);
+
+       dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
+
+       return 0;
+}
+
 void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
 {
        struct pci_epc *epc = ep->epc;
@@ -386,15 +545,18 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
                return -ENOMEM;
        ep->outbound_addr = addr;
 
-       if (ep->ops->ep_init)
-               ep->ops->ep_init(ep);
-
        epc = devm_pci_epc_create(dev, &epc_ops);
        if (IS_ERR(epc)) {
                dev_err(dev, "Failed to create epc device\n");
                return PTR_ERR(epc);
        }
 
+       ep->epc = epc;
+       epc_set_drvdata(epc, ep);
+
+       if (ep->ops->ep_init)
+               ep->ops->ep_init(ep);
+
        ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
        if (ret < 0)
                epc->max_functions = 1;
@@ -409,15 +571,13 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
        ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
                                             epc->mem->page_size);
        if (!ep->msi_mem) {
-               dev_err(dev, "Failed to reserve memory for MSI\n");
+               dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
                return -ENOMEM;
        }
+       ep->msi_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSI);
 
-       epc->features = EPC_FEATURE_NO_LINKUP_NOTIFIER;
-       EPC_FEATURE_SET_BAR(epc->features, BAR_0);
+       ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX);
 
-       ep->epc = epc;
-       epc_set_drvdata(epc, ep);
        dw_pcie_setup(pci);
 
        return 0;
index 781aa03..29a0575 100644 (file)
@@ -363,7 +363,8 @@ int dw_pcie_host_init(struct pcie_port *pp)
        resource_list_for_each_entry_safe(win, tmp, &bridge->windows) {
                switch (resource_type(win->res)) {
                case IORESOURCE_IO:
-                       ret = pci_remap_iospace(win->res, pp->io_base);
+                       ret = devm_pci_remap_iospace(dev, win->res,
+                                                    pp->io_base);
                        if (ret) {
                                dev_warn(dev, "Error %d: failed to map resource %pR\n",
                                         ret, win->res);
index 5937fed..c12bf79 100644 (file)
@@ -70,24 +70,29 @@ static const struct dw_pcie_ops dw_pcie_ops = {
 static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep)
 {
        struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       struct pci_epc *epc = ep->epc;
        enum pci_barno bar;
 
        for (bar = BAR_0; bar <= BAR_5; bar++)
                dw_pcie_ep_reset_bar(pci, bar);
+
+       epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER;
+       epc->features |= EPC_FEATURE_MSIX_AVAILABLE;
 }
 
 static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
                                     enum pci_epc_irq_type type,
-                                    u8 interrupt_num)
+                                    u16 interrupt_num)
 {
        struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
 
        switch (type) {
        case PCI_EPC_IRQ_LEGACY:
-               dev_err(pci->dev, "EP cannot trigger legacy IRQs\n");
-               return -EINVAL;
+               return dw_pcie_ep_raise_legacy_irq(ep, func_no);
        case PCI_EPC_IRQ_MSI:
                return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
+       case PCI_EPC_IRQ_MSIX:
+               return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
        default:
                dev_err(pci->dev, "UNKNOWN IRQ type\n");
        }
@@ -118,7 +123,6 @@ static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie,
                        return pp->msi_irq;
        }
 
-       pp->root_bus_nr = -1;
        pp->ops = &dw_plat_pcie_host_ops;
 
        ret = dw_pcie_host_init(pp);
index bee4e25..96126fd 100644 (file)
 #define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region)                                \
                        ((0x3 << 20) | ((region) << 9) | (0x1 << 8))
 
-#define MSI_MESSAGE_CONTROL            0x52
-#define MSI_CAP_MMC_SHIFT              1
-#define MSI_CAP_MMC_MASK               (7 << MSI_CAP_MMC_SHIFT)
-#define MSI_CAP_MME_SHIFT              4
-#define MSI_CAP_MSI_EN_MASK            0x1
-#define MSI_CAP_MME_MASK               (7 << MSI_CAP_MME_SHIFT)
-#define MSI_MESSAGE_ADDR_L32           0x54
-#define MSI_MESSAGE_ADDR_U32           0x58
-#define MSI_MESSAGE_DATA_32            0x58
-#define MSI_MESSAGE_DATA_64            0x5C
-
 #define MAX_MSI_IRQS                   256
 #define MAX_MSI_IRQS_PER_CTRL          32
 #define MAX_MSI_CTRLS                  (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL)
@@ -191,7 +180,7 @@ enum dw_pcie_as_type {
 struct dw_pcie_ep_ops {
        void    (*ep_init)(struct dw_pcie_ep *ep);
        int     (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
-                            enum pci_epc_irq_type type, u8 interrupt_num);
+                            enum pci_epc_irq_type type, u16 interrupt_num);
 };
 
 struct dw_pcie_ep {
@@ -208,6 +197,8 @@ struct dw_pcie_ep {
        u32                     num_ob_windows;
        void __iomem            *msi_mem;
        phys_addr_t             msi_mem_phys;
+       u8                      msi_cap;        /* MSI capability offset */
+       u8                      msix_cap;       /* MSI-X capability offset */
 };
 
 struct dw_pcie_ops {
@@ -357,8 +348,11 @@ static inline int dw_pcie_allocate_domains(struct pcie_port *pp)
 void dw_pcie_ep_linkup(struct dw_pcie_ep *ep);
 int dw_pcie_ep_init(struct dw_pcie_ep *ep);
 void dw_pcie_ep_exit(struct dw_pcie_ep *ep);
+int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no);
 int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
                             u8 interrupt_num);
+int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
+                            u16 interrupt_num);
 void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar);
 #else
 static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
@@ -374,12 +368,23 @@ static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
 {
 }
 
+static inline int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
+{
+       return 0;
+}
+
 static inline int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
                                           u8 interrupt_num)
 {
        return 0;
 }
 
+static inline int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
+                                          u16 interrupt_num)
+{
+       return 0;
+}
+
 static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
 {
 }
index 3611d6c..7b32e61 100644 (file)
@@ -420,7 +420,6 @@ static int histb_pcie_probe(struct platform_device *pdev)
                phy_init(hipcie->phy);
        }
 
-       pp->root_bus_nr = -1;
        pp->ops = &histb_pcie_host_ops;
 
        platform_set_drvdata(pdev, hipcie);
index d2970a0..5352e0c 100644 (file)
@@ -430,6 +430,9 @@ static int kirin_pcie_host_init(struct pcie_port *pp)
 {
        kirin_pcie_establish_link(pp);
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               dw_pcie_msi_init(pp);
+
        return 0;
 }
 
@@ -445,9 +448,34 @@ static const struct dw_pcie_host_ops kirin_pcie_host_ops = {
        .host_init = kirin_pcie_host_init,
 };
 
+static int kirin_pcie_add_msi(struct dw_pcie *pci,
+                               struct platform_device *pdev)
+{
+       int irq;
+
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               irq = platform_get_irq(pdev, 0);
+               if (irq < 0) {
+                       dev_err(&pdev->dev,
+                               "failed to get MSI IRQ (%d)\n", irq);
+                       return irq;
+               }
+
+               pci->pp.msi_irq = irq;
+       }
+
+       return 0;
+}
+
 static int __init kirin_add_pcie_port(struct dw_pcie *pci,
                                      struct platform_device *pdev)
 {
+       int ret;
+
+       ret = kirin_pcie_add_msi(pci, pdev);
+       if (ret)
+               return ret;
+
        pci->pp.ops = &kirin_pcie_host_ops;
 
        return dw_pcie_host_init(&pci->pp);
index a1d0198..4352c1c 100644 (file)
@@ -1251,7 +1251,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       pp->root_bus_nr = -1;
        pp->ops = &qcom_pcie_dw_ops;
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
index ecb58f7..7d0cdfd 100644 (file)
@@ -210,7 +210,6 @@ static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie,
                return ret;
        }
 
-       pp->root_bus_nr = -1;
        pp->ops = &spear13xx_pcie_host_ops;
 
        ret = dw_pcie_host_init(pp);
index d3172d5..6b4555f 100644 (file)
 #define PCIE_MSI_MASK_REG                      (CONTROL_BASE_ADDR + 0x5C)
 #define PCIE_MSI_PAYLOAD_REG                   (CONTROL_BASE_ADDR + 0x9C)
 
-/* PCIe window configuration */
-#define OB_WIN_BASE_ADDR                       0x4c00
-#define OB_WIN_BLOCK_SIZE                      0x20
-#define OB_WIN_REG_ADDR(win, offset)           (OB_WIN_BASE_ADDR + \
-                                                OB_WIN_BLOCK_SIZE * (win) + \
-                                                (offset))
-#define OB_WIN_MATCH_LS(win)                   OB_WIN_REG_ADDR(win, 0x00)
-#define OB_WIN_MATCH_MS(win)                   OB_WIN_REG_ADDR(win, 0x04)
-#define OB_WIN_REMAP_LS(win)                   OB_WIN_REG_ADDR(win, 0x08)
-#define OB_WIN_REMAP_MS(win)                   OB_WIN_REG_ADDR(win, 0x0c)
-#define OB_WIN_MASK_LS(win)                    OB_WIN_REG_ADDR(win, 0x10)
-#define OB_WIN_MASK_MS(win)                    OB_WIN_REG_ADDR(win, 0x14)
-#define OB_WIN_ACTIONS(win)                    OB_WIN_REG_ADDR(win, 0x18)
-
-/* PCIe window types */
-#define OB_PCIE_MEM                            0x0
-#define OB_PCIE_IO                             0x4
-
 /* LMI registers base address and register offsets */
 #define LMI_BASE_ADDR                          0x6000
 #define CFG_REG                                        (LMI_BASE_ADDR + 0x0)
@@ -247,34 +229,9 @@ static int advk_pcie_wait_for_link(struct advk_pcie *pcie)
        return -ETIMEDOUT;
 }
 
-/*
- * Set PCIe address window register which could be used for memory
- * mapping.
- */
-static void advk_pcie_set_ob_win(struct advk_pcie *pcie,
-                                u32 win_num, u32 match_ms,
-                                u32 match_ls, u32 mask_ms,
-                                u32 mask_ls, u32 remap_ms,
-                                u32 remap_ls, u32 action)
-{
-       advk_writel(pcie, match_ls, OB_WIN_MATCH_LS(win_num));
-       advk_writel(pcie, match_ms, OB_WIN_MATCH_MS(win_num));
-       advk_writel(pcie, mask_ms, OB_WIN_MASK_MS(win_num));
-       advk_writel(pcie, mask_ls, OB_WIN_MASK_LS(win_num));
-       advk_writel(pcie, remap_ms, OB_WIN_REMAP_MS(win_num));
-       advk_writel(pcie, remap_ls, OB_WIN_REMAP_LS(win_num));
-       advk_writel(pcie, action, OB_WIN_ACTIONS(win_num));
-       advk_writel(pcie, match_ls | BIT(0), OB_WIN_MATCH_LS(win_num));
-}
-
 static void advk_pcie_setup_hw(struct advk_pcie *pcie)
 {
        u32 reg;
-       int i;
-
-       /* Point PCIe unit MBUS decode windows to DRAM space */
-       for (i = 0; i < 8; i++)
-               advk_pcie_set_ob_win(pcie, i, 0, 0, 0, 0, 0, 0, 0);
 
        /* Set to Direct mode */
        reg = advk_readl(pcie, CTRL_CONFIG_REG);
@@ -433,6 +390,15 @@ static int advk_pcie_wait_pio(struct advk_pcie *pcie)
        return -ETIMEDOUT;
 }
 
+static bool advk_pcie_valid_device(struct advk_pcie *pcie, struct pci_bus *bus,
+                                 int devfn)
+{
+       if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0)
+               return false;
+
+       return true;
+}
+
 static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
                             int where, int size, u32 *val)
 {
@@ -440,7 +406,7 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
        u32 reg;
        int ret;
 
-       if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0) {
+       if (!advk_pcie_valid_device(pcie, bus, devfn)) {
                *val = 0xffffffff;
                return PCIBIOS_DEVICE_NOT_FOUND;
        }
@@ -494,7 +460,7 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
        int offset;
        int ret;
 
-       if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0)
+       if (!advk_pcie_valid_device(pcie, bus, devfn))
                return PCIBIOS_DEVICE_NOT_FOUND;
 
        if (where % size)
@@ -843,13 +809,7 @@ static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie)
 
                switch (resource_type(res)) {
                case IORESOURCE_IO:
-                       advk_pcie_set_ob_win(pcie, 1,
-                                            upper_32_bits(res->start),
-                                            lower_32_bits(res->start),
-                                            0, 0xF8000000, 0,
-                                            lower_32_bits(res->start),
-                                            OB_PCIE_IO);
-                       err = pci_remap_iospace(res, iobase);
+                       err = devm_pci_remap_iospace(dev, res, iobase);
                        if (err) {
                                dev_warn(dev, "error %d: failed to map resource %pR\n",
                                         err, res);
@@ -857,12 +817,6 @@ static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie)
                        }
                        break;
                case IORESOURCE_MEM:
-                       advk_pcie_set_ob_win(pcie, 0,
-                                            upper_32_bits(res->start),
-                                            lower_32_bits(res->start),
-                                            0x0, 0xF8000000, 0,
-                                            lower_32_bits(res->start),
-                                            (2 << 20) | OB_PCIE_MEM);
                        res_valid |= !(res->flags & IORESOURCE_PREFETCH);
                        break;
                case IORESOURCE_BUS:
@@ -889,7 +843,6 @@ static int advk_pcie_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct advk_pcie *pcie;
        struct resource *res;
-       struct pci_bus *bus, *child;
        struct pci_host_bridge *bridge;
        int ret, irq;
 
@@ -943,21 +896,13 @@ static int advk_pcie_probe(struct platform_device *pdev)
        bridge->map_irq = of_irq_parse_and_map_pci;
        bridge->swizzle_irq = pci_common_swizzle;
 
-       ret = pci_scan_root_bus_bridge(bridge);
+       ret = pci_host_probe(bridge);
        if (ret < 0) {
                advk_pcie_remove_msi_irq_domain(pcie);
                advk_pcie_remove_irq_domain(pcie);
                return ret;
        }
 
-       bus = bridge->bus;
-
-       pci_bus_assign_resources(bus);
-
-       list_for_each_entry(child, &bus->children, node)
-               pcie_bus_configure_settings(child);
-
-       pci_bus_add_devices(bus);
        return 0;
 }
 
index a1ebe9e..bf5ece5 100644 (file)
@@ -355,11 +355,13 @@ static int faraday_pci_setup_cascaded_irq(struct faraday_pci *p)
        irq = of_irq_get(intc, 0);
        if (irq <= 0) {
                dev_err(p->dev, "failed to get parent IRQ\n");
+               of_node_put(intc);
                return irq ?: -EINVAL;
        }
 
        p->irqdomain = irq_domain_add_linear(intc, PCI_NUM_INTX,
                                             &faraday_pci_irqdomain_ops, p);
+       of_node_put(intc);
        if (!p->irqdomain) {
                dev_err(p->dev, "failed to create Gemini PCI IRQ domain\n");
                return -EINVAL;
@@ -501,7 +503,7 @@ static int faraday_pci_probe(struct platform_device *pdev)
                                dev_err(dev, "illegal IO mem size\n");
                                return -EINVAL;
                        }
-                       ret = pci_remap_iospace(io, io_base);
+                       ret = devm_pci_remap_iospace(dev, io, io_base);
                        if (ret) {
                                dev_warn(dev, "error %d: failed to map resource %pR\n",
                                         ret, io);
index 6cc5036..eb20296 100644 (file)
@@ -1073,6 +1073,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
        struct pci_bus *pbus;
        struct pci_dev *pdev;
        struct cpumask *dest;
+       unsigned long flags;
        struct compose_comp_ctxt comp;
        struct tran_int_desc *int_desc;
        struct {
@@ -1164,14 +1165,15 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
                 * the channel callback directly when channel->target_cpu is
                 * the current CPU. When the higher level interrupt code
                 * calls us with interrupt enabled, let's add the
-                * local_bh_disable()/enable() to avoid race.
+                * local_irq_save()/restore() to avoid race:
+                * hv_pci_onchannelcallback() can also run in tasklet.
                 */
-               local_bh_disable();
+               local_irq_save(flags);
 
                if (hbus->hdev->channel->target_cpu == smp_processor_id())
                        hv_pci_onchannelcallback(hbus);
 
-               local_bh_enable();
+               local_irq_restore(flags);
 
                if (hpdev->state == hv_pcichild_ejecting) {
                        dev_err_once(&hbus->hdev->device,
@@ -1543,7 +1545,7 @@ static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus,
        unsigned long flags;
        int ret;
 
-       hpdev = kzalloc(sizeof(*hpdev), GFP_ATOMIC);
+       hpdev = kzalloc(sizeof(*hpdev), GFP_KERNEL);
        if (!hpdev)
                return NULL;
 
index 68b8bfb..d219404 100644 (file)
@@ -537,7 +537,7 @@ static int v3_pci_setup_resource(struct v3_pci *v3,
                v3->io_bus_addr = io->start - win->offset;
                dev_dbg(dev, "I/O window %pR, bus addr %pap\n",
                        io, &v3->io_bus_addr);
-               ret = pci_remap_iospace(io, io_base);
+               ret = devm_pci_remap_iospace(dev, io, io_base);
                if (ret) {
                        dev_warn(dev,
                                 "error %d: failed to map resource %pR\n",
index 994f320..f59ad27 100644 (file)
@@ -82,7 +82,7 @@ static int versatile_pci_parse_request_of_pci_ranges(struct device *dev,
 
                switch (resource_type(res)) {
                case IORESOURCE_IO:
-                       err = pci_remap_iospace(res, iobase);
+                       err = devm_pci_remap_iospace(dev, res, iobase);
                        if (err) {
                                dev_warn(dev, "error %d: failed to map resource %pR\n",
                                         err, res);
index d854d67..ffda3e8 100644 (file)
@@ -423,7 +423,7 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port,
                case IORESOURCE_IO:
                        xgene_pcie_setup_ob_reg(port, res, OMR3BARL, io_base,
                                                res->start - window->offset);
-                       ret = pci_remap_iospace(res, io_base);
+                       ret = devm_pci_remap_iospace(dev, res, io_base);
                        if (ret < 0)
                                return ret;
                        break;
index e3fe412..9e87dd7 100644 (file)
@@ -238,7 +238,7 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn)
        struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
        struct cdns_pcie *pcie = &ep->pcie;
        u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
-       u16 flags, mmc, mme;
+       u16 flags, mme;
 
        /* Validate that the MSI feature is actually enabled. */
        flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
@@ -249,7 +249,6 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn)
         * Get the Multiple Message Enable bitfield from the Message Control
         * register.
         */
-       mmc = (flags & PCI_MSI_FLAGS_QMASK) >> 1;
        mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
 
        return mme;
@@ -363,7 +362,8 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn,
 }
 
 static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn,
-                                 enum pci_epc_irq_type type, u8 interrupt_num)
+                                 enum pci_epc_irq_type type,
+                                 u16 interrupt_num)
 {
        struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
 
@@ -439,6 +439,7 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev)
        struct pci_epc *epc;
        struct resource *res;
        int ret;
+       int phy_count;
 
        ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
        if (!ep)
@@ -473,6 +474,12 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev)
        if (!ep->ob_addr)
                return -ENOMEM;
 
+       ret = cdns_pcie_init_phy(dev, pcie);
+       if (ret) {
+               dev_err(dev, "failed to init phy\n");
+               return ret;
+       }
+       platform_set_drvdata(pdev, pcie);
        pm_runtime_enable(dev);
        ret = pm_runtime_get_sync(dev);
        if (ret < 0) {
@@ -521,6 +528,10 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev)
 
  err_get_sync:
        pm_runtime_disable(dev);
+       cdns_pcie_disable_phy(pcie);
+       phy_count = pcie->phy_count;
+       while (phy_count--)
+               device_link_del(pcie->link[phy_count]);
 
        return ret;
 }
@@ -528,6 +539,7 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev)
 static void cdns_pcie_ep_shutdown(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
+       struct cdns_pcie *pcie = dev_get_drvdata(dev);
        int ret;
 
        ret = pm_runtime_put_sync(dev);
@@ -536,13 +548,14 @@ static void cdns_pcie_ep_shutdown(struct platform_device *pdev)
 
        pm_runtime_disable(dev);
 
-       /* The PCIe controller can't be disabled. */
+       cdns_pcie_disable_phy(pcie);
 }
 
 static struct platform_driver cdns_pcie_ep_driver = {
        .driver = {
                .name = "cdns-pcie-ep",
                .of_match_table = cdns_pcie_ep_of_match,
+               .pm     = &cdns_pcie_pm_ops,
        },
        .probe = cdns_pcie_ep_probe,
        .shutdown = cdns_pcie_ep_shutdown,
index a4ebbd3..ec394f6 100644 (file)
@@ -58,6 +58,11 @@ static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn,
 
                return pcie->reg_base + (where & 0xfff);
        }
+       /* Check that the link is up */
+       if (!(cdns_pcie_readl(pcie, CDNS_PCIE_LM_BASE) & 0x1))
+               return NULL;
+       /* Clear AXI link-down status */
+       cdns_pcie_writel(pcie, CDNS_PCIE_AT_LINKDOWN, 0x0);
 
        /* Update Output registers for AXI region 0. */
        addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(12) |
@@ -239,6 +244,7 @@ static int cdns_pcie_host_probe(struct platform_device *pdev)
        struct cdns_pcie *pcie;
        struct resource *res;
        int ret;
+       int phy_count;
 
        bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc));
        if (!bridge)
@@ -290,6 +296,13 @@ static int cdns_pcie_host_probe(struct platform_device *pdev)
        }
        pcie->mem_res = res;
 
+       ret = cdns_pcie_init_phy(dev, pcie);
+       if (ret) {
+               dev_err(dev, "failed to init phy\n");
+               return ret;
+       }
+       platform_set_drvdata(pdev, pcie);
+
        pm_runtime_enable(dev);
        ret = pm_runtime_get_sync(dev);
        if (ret < 0) {
@@ -322,15 +335,35 @@ static int cdns_pcie_host_probe(struct platform_device *pdev)
 
  err_get_sync:
        pm_runtime_disable(dev);
+       cdns_pcie_disable_phy(pcie);
+       phy_count = pcie->phy_count;
+       while (phy_count--)
+               device_link_del(pcie->link[phy_count]);
 
        return ret;
 }
 
+static void cdns_pcie_shutdown(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct cdns_pcie *pcie = dev_get_drvdata(dev);
+       int ret;
+
+       ret = pm_runtime_put_sync(dev);
+       if (ret < 0)
+               dev_dbg(dev, "pm_runtime_put_sync failed\n");
+
+       pm_runtime_disable(dev);
+       cdns_pcie_disable_phy(pcie);
+}
+
 static struct platform_driver cdns_pcie_host_driver = {
        .driver = {
                .name = "cdns-pcie-host",
                .of_match_table = cdns_pcie_host_of_match,
+               .pm     = &cdns_pcie_pm_ops,
        },
        .probe = cdns_pcie_host_probe,
+       .shutdown = cdns_pcie_shutdown,
 };
 builtin_platform_driver(cdns_pcie_host_driver);
index 138d113..86f1b00 100644 (file)
@@ -124,3 +124,126 @@ void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r)
        cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), 0);
        cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), 0);
 }
+
+void cdns_pcie_disable_phy(struct cdns_pcie *pcie)
+{
+       int i = pcie->phy_count;
+
+       while (i--) {
+               phy_power_off(pcie->phy[i]);
+               phy_exit(pcie->phy[i]);
+       }
+}
+
+int cdns_pcie_enable_phy(struct cdns_pcie *pcie)
+{
+       int ret;
+       int i;
+
+       for (i = 0; i < pcie->phy_count; i++) {
+               ret = phy_init(pcie->phy[i]);
+               if (ret < 0)
+                       goto err_phy;
+
+               ret = phy_power_on(pcie->phy[i]);
+               if (ret < 0) {
+                       phy_exit(pcie->phy[i]);
+                       goto err_phy;
+               }
+       }
+
+       return 0;
+
+err_phy:
+       while (--i >= 0) {
+               phy_power_off(pcie->phy[i]);
+               phy_exit(pcie->phy[i]);
+       }
+
+       return ret;
+}
+
+int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie)
+{
+       struct device_node *np = dev->of_node;
+       int phy_count;
+       struct phy **phy;
+       struct device_link **link;
+       int i;
+       int ret;
+       const char *name;
+
+       phy_count = of_property_count_strings(np, "phy-names");
+       if (phy_count < 1) {
+               dev_err(dev, "no phy-names.  PHY will not be initialized\n");
+               pcie->phy_count = 0;
+               return 0;
+       }
+
+       phy = devm_kzalloc(dev, sizeof(*phy) * phy_count, GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       link = devm_kzalloc(dev, sizeof(*link) * phy_count, GFP_KERNEL);
+       if (!link)
+               return -ENOMEM;
+
+       for (i = 0; i < phy_count; i++) {
+               of_property_read_string_index(np, "phy-names", i, &name);
+               phy[i] = devm_phy_optional_get(dev, name);
+               if (IS_ERR(phy))
+                       return PTR_ERR(phy);
+
+               link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS);
+               if (!link[i]) {
+                       ret = -EINVAL;
+                       goto err_link;
+               }
+       }
+
+       pcie->phy_count = phy_count;
+       pcie->phy = phy;
+       pcie->link = link;
+
+       ret =  cdns_pcie_enable_phy(pcie);
+       if (ret)
+               goto err_link;
+
+       return 0;
+
+err_link:
+       while (--i >= 0)
+               device_link_del(link[i]);
+
+       return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cdns_pcie_suspend_noirq(struct device *dev)
+{
+       struct cdns_pcie *pcie = dev_get_drvdata(dev);
+
+       cdns_pcie_disable_phy(pcie);
+
+       return 0;
+}
+
+static int cdns_pcie_resume_noirq(struct device *dev)
+{
+       struct cdns_pcie *pcie = dev_get_drvdata(dev);
+       int ret;
+
+       ret = cdns_pcie_enable_phy(pcie);
+       if (ret) {
+               dev_err(dev, "failed to enable phy\n");
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+const struct dev_pm_ops cdns_pcie_pm_ops = {
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cdns_pcie_suspend_noirq,
+                                     cdns_pcie_resume_noirq)
+};
index 4bb2733..ae6bf2a 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/kernel.h>
 #include <linux/pci.h>
+#include <linux/phy/phy.h>
 
 /*
  * Local Management Registers
 #define CDNS_PCIE_AT_IB_RP_BAR_ADDR1(bar) \
        (CDNS_PCIE_AT_BASE + 0x0804 + (bar) * 0x0008)
 
+/* AXI link down register */
+#define CDNS_PCIE_AT_LINKDOWN (CDNS_PCIE_AT_BASE + 0x0824)
+
 enum cdns_pcie_rp_bar {
        RP_BAR0,
        RP_BAR1,
@@ -229,6 +233,9 @@ struct cdns_pcie {
        struct resource         *mem_res;
        bool                    is_rc;
        u8                      bus;
+       int                     phy_count;
+       struct phy              **phy;
+       struct device_link      **link;
 };
 
 /* Register access */
@@ -279,7 +286,7 @@ static inline void cdns_pcie_ep_fn_writew(struct cdns_pcie *pcie, u8 fn,
 }
 
 static inline void cdns_pcie_ep_fn_writel(struct cdns_pcie *pcie, u8 fn,
-                                         u32 reg, u16 value)
+                                         u32 reg, u32 value)
 {
        writel(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg);
 }
@@ -307,5 +314,9 @@ void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn,
                                                  u32 r, u64 cpu_addr);
 
 void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r);
+void cdns_pcie_disable_phy(struct cdns_pcie *pcie);
+int cdns_pcie_enable_phy(struct cdns_pcie *pcie);
+int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie);
+extern const struct dev_pm_ops cdns_pcie_pm_ops;
 
 #endif /* _PCIE_CADENCE_H */
index 3c76c5f..3160e93 100644 (file)
@@ -85,6 +85,8 @@
 #define IMAP_VALID_SHIFT               0
 #define IMAP_VALID                     BIT(IMAP_VALID_SHIFT)
 
+#define IPROC_PCI_PM_CAP               0x48
+#define IPROC_PCI_PM_CAP_MASK          0xffff
 #define IPROC_PCI_EXP_CAP              0xac
 
 #define IPROC_PCIE_REG_INVALID         0xffff
@@ -375,6 +377,17 @@ static const u16 iproc_pcie_reg_paxc_v2[] = {
        [IPROC_PCIE_CFG_DATA]           = 0x1fc,
 };
 
+/*
+ * List of device IDs of controllers that have corrupted capability list that
+ * require SW fixup
+ */
+static const u16 iproc_pcie_corrupt_cap_did[] = {
+       0x16cd,
+       0x16f0,
+       0xd802,
+       0xd804
+};
+
 static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
 {
        struct iproc_pcie *pcie = bus->sysdata;
@@ -495,6 +508,49 @@ static unsigned int iproc_pcie_cfg_retry(void __iomem *cfg_data_p)
        return data;
 }
 
+static void iproc_pcie_fix_cap(struct iproc_pcie *pcie, int where, u32 *val)
+{
+       u32 i, dev_id;
+
+       switch (where & ~0x3) {
+       case PCI_VENDOR_ID:
+               dev_id = *val >> 16;
+
+               /*
+                * Activate fixup for those controllers that have corrupted
+                * capability list registers
+                */
+               for (i = 0; i < ARRAY_SIZE(iproc_pcie_corrupt_cap_did); i++)
+                       if (dev_id == iproc_pcie_corrupt_cap_did[i])
+                               pcie->fix_paxc_cap = true;
+               break;
+
+       case IPROC_PCI_PM_CAP:
+               if (pcie->fix_paxc_cap) {
+                       /* advertise PM, force next capability to PCIe */
+                       *val &= ~IPROC_PCI_PM_CAP_MASK;
+                       *val |= IPROC_PCI_EXP_CAP << 8 | PCI_CAP_ID_PM;
+               }
+               break;
+
+       case IPROC_PCI_EXP_CAP:
+               if (pcie->fix_paxc_cap) {
+                       /* advertise root port, version 2, terminate here */
+                       *val = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2) << 16 |
+                               PCI_CAP_ID_EXP;
+               }
+               break;
+
+       case IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL:
+               /* Don't advertise CRS SV support */
+               *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
+               break;
+
+       default:
+               break;
+       }
+}
+
 static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
                                  int where, int size, u32 *val)
 {
@@ -509,13 +565,10 @@ static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
        /* root complex access */
        if (busno == 0) {
                ret = pci_generic_config_read32(bus, devfn, where, size, val);
-               if (ret != PCIBIOS_SUCCESSFUL)
-                       return ret;
+               if (ret == PCIBIOS_SUCCESSFUL)
+                       iproc_pcie_fix_cap(pcie, where, val);
 
-               /* Don't advertise CRS SV support */
-               if ((where & ~0x3) == IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL)
-                       *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
-               return PCIBIOS_SUCCESSFUL;
+               return ret;
        }
 
        cfg_data_p = iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where);
@@ -529,6 +582,25 @@ static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
        if (size <= 2)
                *val = (data >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
 
+       /*
+        * For PAXC and PAXCv2, the total number of PFs that one can enumerate
+        * depends on the firmware configuration. Unfortunately, due to an ASIC
+        * bug, unconfigured PFs cannot be properly hidden from the root
+        * complex. As a result, write access to these PFs will cause bus lock
+        * up on the embedded processor
+        *
+        * Since all unconfigured PFs are left with an incorrect, staled device
+        * ID of 0x168e (PCI_DEVICE_ID_NX2_57810), we try to catch those access
+        * early here and reject them all
+        */
+#define DEVICE_ID_MASK     0xffff0000
+#define DEVICE_ID_SHIFT    16
+       if (pcie->rej_unconfig_pf &&
+           (where & CFG_ADDR_REG_NUM_MASK) == PCI_VENDOR_ID)
+               if ((*val & DEVICE_ID_MASK) ==
+                   (PCI_DEVICE_ID_NX2_57810 << DEVICE_ID_SHIFT))
+                       return PCIBIOS_FUNC_NOT_SUPPORTED;
+
        return PCIBIOS_SUCCESSFUL;
 }
 
@@ -628,7 +700,7 @@ static int iproc_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
        struct iproc_pcie *pcie = iproc_data(bus);
 
        iproc_pcie_apb_err_disable(bus, true);
-       if (pcie->type == IPROC_PCIE_PAXB_V2)
+       if (pcie->iproc_cfg_read)
                ret = iproc_pcie_config_read(bus, devfn, where, size, val);
        else
                ret = pci_generic_config_read32(bus, devfn, where, size, val);
@@ -808,14 +880,14 @@ static inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx,
        writel(lower_32_bits(pci_addr), pcie->base + omap_offset);
        writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4);
 
-       dev_info(dev, "ob window [%d]: offset 0x%x axi %pap pci %pap\n",
-                window_idx, oarr_offset, &axi_addr, &pci_addr);
-       dev_info(dev, "oarr lo 0x%x oarr hi 0x%x\n",
-                readl(pcie->base + oarr_offset),
-                readl(pcie->base + oarr_offset + 4));
-       dev_info(dev, "omap lo 0x%x omap hi 0x%x\n",
-                readl(pcie->base + omap_offset),
-                readl(pcie->base + omap_offset + 4));
+       dev_dbg(dev, "ob window [%d]: offset 0x%x axi %pap pci %pap\n",
+               window_idx, oarr_offset, &axi_addr, &pci_addr);
+       dev_dbg(dev, "oarr lo 0x%x oarr hi 0x%x\n",
+               readl(pcie->base + oarr_offset),
+               readl(pcie->base + oarr_offset + 4));
+       dev_dbg(dev, "omap lo 0x%x omap hi 0x%x\n",
+               readl(pcie->base + omap_offset),
+               readl(pcie->base + omap_offset + 4));
 
        return 0;
 }
@@ -982,8 +1054,8 @@ static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
            iproc_pcie_reg_is_invalid(imap_offset))
                return -EINVAL;
 
-       dev_info(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n",
-                region_idx, iarr_offset, &axi_addr, &pci_addr);
+       dev_dbg(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n",
+               region_idx, iarr_offset, &axi_addr, &pci_addr);
 
        /*
         * Program the IARR registers.  The upper 32-bit IARR register is
@@ -993,9 +1065,9 @@ static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
               pcie->base + iarr_offset);
        writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4);
 
-       dev_info(dev, "iarr lo 0x%x iarr hi 0x%x\n",
-                readl(pcie->base + iarr_offset),
-                readl(pcie->base + iarr_offset + 4));
+       dev_dbg(dev, "iarr lo 0x%x iarr hi 0x%x\n",
+               readl(pcie->base + iarr_offset),
+               readl(pcie->base + iarr_offset + 4));
 
        /*
         * Now program the IMAP registers.  Each IARR region may have one or
@@ -1009,10 +1081,10 @@ static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
                writel(upper_32_bits(axi_addr),
                       pcie->base + imap_offset + ib_map->imap_addr_offset);
 
-               dev_info(dev, "imap window [%d] lo 0x%x hi 0x%x\n",
-                        window_idx, readl(pcie->base + imap_offset),
-                        readl(pcie->base + imap_offset +
-                              ib_map->imap_addr_offset));
+               dev_dbg(dev, "imap window [%d] lo 0x%x hi 0x%x\n",
+                       window_idx, readl(pcie->base + imap_offset),
+                       readl(pcie->base + imap_offset +
+                             ib_map->imap_addr_offset));
 
                imap_offset += ib_map->imap_window_offset;
                axi_addr += size;
@@ -1144,10 +1216,22 @@ static int iproc_pcie_paxb_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr)
        return ret;
 }
 
-static void iproc_pcie_paxc_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr)
+static void iproc_pcie_paxc_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr,
+                                        bool enable)
 {
        u32 val;
 
+       if (!enable) {
+               /*
+                * Disable PAXC MSI steering. All write transfers will be
+                * treated as non-MSI transfers
+                */
+               val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_EN_CFG);
+               val &= ~MSI_ENABLE_CFG;
+               iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_EN_CFG, val);
+               return;
+       }
+
        /*
         * Program bits [43:13] of address of GITS_TRANSLATER register into
         * bits [30:0] of the MSI base address register.  In fact, in all iProc
@@ -1201,7 +1285,7 @@ static int iproc_pcie_msi_steer(struct iproc_pcie *pcie,
                        return ret;
                break;
        case IPROC_PCIE_PAXC_V2:
-               iproc_pcie_paxc_v2_msi_steer(pcie, msi_addr);
+               iproc_pcie_paxc_v2_msi_steer(pcie, msi_addr, true);
                break;
        default:
                return -EINVAL;
@@ -1271,6 +1355,7 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
                break;
        case IPROC_PCIE_PAXB:
                regs = iproc_pcie_reg_paxb;
+               pcie->iproc_cfg_read = true;
                pcie->has_apb_err_disable = true;
                if (pcie->need_ob_cfg) {
                        pcie->ob_map = paxb_ob_map;
@@ -1293,10 +1378,14 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
        case IPROC_PCIE_PAXC:
                regs = iproc_pcie_reg_paxc;
                pcie->ep_is_internal = true;
+               pcie->iproc_cfg_read = true;
+               pcie->rej_unconfig_pf = true;
                break;
        case IPROC_PCIE_PAXC_V2:
                regs = iproc_pcie_reg_paxc_v2;
                pcie->ep_is_internal = true;
+               pcie->iproc_cfg_read = true;
+               pcie->rej_unconfig_pf = true;
                pcie->need_msi_steer = true;
                break;
        default:
@@ -1427,6 +1516,24 @@ int iproc_pcie_remove(struct iproc_pcie *pcie)
 }
 EXPORT_SYMBOL(iproc_pcie_remove);
 
+/*
+ * The MSI parsing logic in certain revisions of Broadcom PAXC based root
+ * complex does not work and needs to be disabled
+ */
+static void quirk_paxc_disable_msi_parsing(struct pci_dev *pdev)
+{
+       struct iproc_pcie *pcie = iproc_data(pdev->bus);
+
+       if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
+               iproc_pcie_paxc_v2_msi_steer(pcie, 0, false);
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0,
+                       quirk_paxc_disable_msi_parsing);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802,
+                       quirk_paxc_disable_msi_parsing);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804,
+                       quirk_paxc_disable_msi_parsing);
+
 MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
 MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver");
 MODULE_LICENSE("GPL v2");
index 814b600..4f03ea5 100644 (file)
@@ -58,8 +58,13 @@ struct iproc_msi;
  * @phy: optional PHY device that controls the Serdes
  * @map_irq: function callback to map interrupts
  * @ep_is_internal: indicates an internal emulated endpoint device is connected
+ * @iproc_cfg_read: indicates the iProc config read function should be used
+ * @rej_unconfig_pf: indicates the root complex needs to detect and reject
+ * enumeration against unconfigured physical functions emulated in the ASIC
  * @has_apb_err_disable: indicates the controller can be configured to prevent
  * unsupported request from being forwarded as an APB bus error
+ * @fix_paxc_cap: indicates the controller has corrupted capability list in its
+ * config space registers and requires SW based fixup
  *
  * @need_ob_cfg: indicates SW needs to configure the outbound mapping window
  * @ob: outbound mapping related parameters
@@ -84,7 +89,10 @@ struct iproc_pcie {
        struct phy *phy;
        int (*map_irq)(const struct pci_dev *, u8, u8);
        bool ep_is_internal;
+       bool iproc_cfg_read;
+       bool rej_unconfig_pf;
        bool has_apb_err_disable;
+       bool fix_paxc_cap;
 
        bool need_ob_cfg;
        struct iproc_pcie_ob ob;
index 0baabe3..861dda6 100644 (file)
@@ -1109,7 +1109,7 @@ static int mtk_pcie_request_resources(struct mtk_pcie *pcie)
        if (err < 0)
                return err;
 
-       pci_remap_iospace(&pcie->pio, pcie->io.start);
+       devm_pci_remap_iospace(dev, &pcie->pio, pcie->io.start);
 
        return 0;
 }
index 874d75c..c8febb0 100644 (file)
@@ -680,7 +680,11 @@ static int rcar_pcie_phy_init_gen3(struct rcar_pcie *pcie)
        if (err)
                return err;
 
-       return phy_power_on(pcie->phy);
+       err = phy_power_on(pcie->phy);
+       if (err)
+               phy_exit(pcie->phy);
+
+       return err;
 }
 
 static int rcar_msi_alloc(struct rcar_msi *chip)
@@ -1165,7 +1169,7 @@ static int rcar_pcie_probe(struct platform_device *pdev)
        if (rcar_pcie_hw_init(pcie)) {
                dev_info(dev, "PCIe link down\n");
                err = -ENODEV;
-               goto err_clk_disable;
+               goto err_phy_shutdown;
        }
 
        data = rcar_pci_read_reg(pcie, MACSR);
@@ -1177,7 +1181,7 @@ static int rcar_pcie_probe(struct platform_device *pdev)
                        dev_err(dev,
                                "failed to enable MSI support: %d\n",
                                err);
-                       goto err_clk_disable;
+                       goto err_phy_shutdown;
                }
        }
 
@@ -1191,6 +1195,12 @@ err_msi_teardown:
        if (IS_ENABLED(CONFIG_PCI_MSI))
                rcar_pcie_teardown_msi(pcie);
 
+err_phy_shutdown:
+       if (pcie->phy) {
+               phy_power_off(pcie->phy);
+               phy_exit(pcie->phy);
+       }
+
 err_clk_disable:
        clk_disable_unprepare(pcie->bus_clk);
 
index 6beba8e..b8163c5 100644 (file)
@@ -472,7 +472,7 @@ static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn,
 
 static int rockchip_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn,
                                      enum pci_epc_irq_type type,
-                                     u8 interrupt_num)
+                                     u16 interrupt_num)
 {
        struct rockchip_pcie_ep *ep = epc_get_drvdata(epc);
 
index 6a4bbb5..fb32840 100644 (file)
@@ -559,7 +559,7 @@ static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie)
                                                        PCI_NUM_INTX,
                                                        &legacy_domain_ops,
                                                        pcie);
-
+       of_node_put(legacy_intc_node);
        if (!pcie->legacy_irq_domain) {
                dev_err(dev, "failed to create IRQ domain\n");
                return -ENOMEM;
index b110a3a..7b1389d 100644 (file)
@@ -509,6 +509,7 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
        port->leg_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
                                                 &intx_domain_ops,
                                                 port);
+       of_node_put(pcie_intc_node);
        if (!port->leg_domain) {
                dev_err(dev, "Failed to get a INTx IRQ domain\n");
                return -ENODEV;
index 63ed706..3e86fa3 100644 (file)
 #include <linux/pci-epf.h>
 #include <linux/pci_regs.h>
 
+#define IRQ_TYPE_LEGACY                        0
+#define IRQ_TYPE_MSI                   1
+#define IRQ_TYPE_MSIX                  2
+
 #define COMMAND_RAISE_LEGACY_IRQ       BIT(0)
 #define COMMAND_RAISE_MSI_IRQ          BIT(1)
-#define MSI_NUMBER_SHIFT               2
-#define MSI_NUMBER_MASK                        (0x3f << MSI_NUMBER_SHIFT)
-#define COMMAND_READ                   BIT(8)
-#define COMMAND_WRITE                  BIT(9)
-#define COMMAND_COPY                   BIT(10)
+#define COMMAND_RAISE_MSIX_IRQ         BIT(2)
+#define COMMAND_READ                   BIT(3)
+#define COMMAND_WRITE                  BIT(4)
+#define COMMAND_COPY                   BIT(5)
 
 #define STATUS_READ_SUCCESS            BIT(0)
 #define STATUS_READ_FAIL               BIT(1)
@@ -45,6 +48,7 @@ struct pci_epf_test {
        struct pci_epf          *epf;
        enum pci_barno          test_reg_bar;
        bool                    linkup_notifier;
+       bool                    msix_available;
        struct delayed_work     cmd_handler;
 };
 
@@ -56,6 +60,8 @@ struct pci_epf_test_reg {
        u64     dst_addr;
        u32     size;
        u32     checksum;
+       u32     irq_type;
+       u32     irq_number;
 } __packed;
 
 static struct pci_epf_header test_header = {
@@ -244,31 +250,42 @@ err:
        return ret;
 }
 
-static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, u8 irq)
+static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, u8 irq_type,
+                                  u16 irq)
 {
-       u8 msi_count;
        struct pci_epf *epf = epf_test->epf;
+       struct device *dev = &epf->dev;
        struct pci_epc *epc = epf->epc;
        enum pci_barno test_reg_bar = epf_test->test_reg_bar;
        struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
 
        reg->status |= STATUS_IRQ_RAISED;
-       msi_count = pci_epc_get_msi(epc, epf->func_no);
-       if (irq > msi_count || msi_count <= 0)
+
+       switch (irq_type) {
+       case IRQ_TYPE_LEGACY:
                pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_LEGACY, 0);
-       else
+               break;
+       case IRQ_TYPE_MSI:
                pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq);
+               break;
+       case IRQ_TYPE_MSIX:
+               pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX, irq);
+               break;
+       default:
+               dev_err(dev, "Failed to raise IRQ, unknown type\n");
+               break;
+       }
 }
 
 static void pci_epf_test_cmd_handler(struct work_struct *work)
 {
        int ret;
-       u8 irq;
-       u8 msi_count;
+       int count;
        u32 command;
        struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test,
                                                     cmd_handler.work);
        struct pci_epf *epf = epf_test->epf;
+       struct device *dev = &epf->dev;
        struct pci_epc *epc = epf->epc;
        enum pci_barno test_reg_bar = epf_test->test_reg_bar;
        struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
@@ -280,7 +297,10 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
        reg->command = 0;
        reg->status = 0;
 
-       irq = (command & MSI_NUMBER_MASK) >> MSI_NUMBER_SHIFT;
+       if (reg->irq_type > IRQ_TYPE_MSIX) {
+               dev_err(dev, "Failed to detect IRQ type\n");
+               goto reset_handler;
+       }
 
        if (command & COMMAND_RAISE_LEGACY_IRQ) {
                reg->status = STATUS_IRQ_RAISED;
@@ -294,7 +314,8 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
                        reg->status |= STATUS_WRITE_FAIL;
                else
                        reg->status |= STATUS_WRITE_SUCCESS;
-               pci_epf_test_raise_irq(epf_test, irq);
+               pci_epf_test_raise_irq(epf_test, reg->irq_type,
+                                      reg->irq_number);
                goto reset_handler;
        }
 
@@ -304,7 +325,8 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
                        reg->status |= STATUS_READ_SUCCESS;
                else
                        reg->status |= STATUS_READ_FAIL;
-               pci_epf_test_raise_irq(epf_test, irq);
+               pci_epf_test_raise_irq(epf_test, reg->irq_type,
+                                      reg->irq_number);
                goto reset_handler;
        }
 
@@ -314,16 +336,28 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
                        reg->status |= STATUS_COPY_SUCCESS;
                else
                        reg->status |= STATUS_COPY_FAIL;
-               pci_epf_test_raise_irq(epf_test, irq);
+               pci_epf_test_raise_irq(epf_test, reg->irq_type,
+                                      reg->irq_number);
                goto reset_handler;
        }
 
        if (command & COMMAND_RAISE_MSI_IRQ) {
-               msi_count = pci_epc_get_msi(epc, epf->func_no);
-               if (irq > msi_count || msi_count <= 0)
+               count = pci_epc_get_msi(epc, epf->func_no);
+               if (reg->irq_number > count || count <= 0)
                        goto reset_handler;
                reg->status = STATUS_IRQ_RAISED;
-               pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq);
+               pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI,
+                                 reg->irq_number);
+               goto reset_handler;
+       }
+
+       if (command & COMMAND_RAISE_MSIX_IRQ) {
+               count = pci_epc_get_msix(epc, epf->func_no);
+               if (reg->irq_number > count || count <= 0)
+                       goto reset_handler;
+               reg->status = STATUS_IRQ_RAISED;
+               pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX,
+                                 reg->irq_number);
                goto reset_handler;
        }
 
@@ -440,6 +474,8 @@ static int pci_epf_test_bind(struct pci_epf *epf)
        else
                epf_test->linkup_notifier = true;
 
+       epf_test->msix_available = epc->features & EPC_FEATURE_MSIX_AVAILABLE;
+
        epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features);
 
        ret = pci_epc_write_header(epc, epf->func_no, header);
@@ -457,8 +493,18 @@ static int pci_epf_test_bind(struct pci_epf *epf)
                return ret;
 
        ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts);
-       if (ret)
+       if (ret) {
+               dev_err(dev, "MSI configuration failed\n");
                return ret;
+       }
+
+       if (epf_test->msix_available) {
+               ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts);
+               if (ret) {
+                       dev_err(dev, "MSI-X configuration failed\n");
+                       return ret;
+               }
+       }
 
        if (!epf_test->linkup_notifier)
                queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);
index 018ea34..d1288a0 100644 (file)
@@ -286,6 +286,28 @@ static ssize_t pci_epf_msi_interrupts_show(struct config_item *item,
                       to_pci_epf_group(item)->epf->msi_interrupts);
 }
 
+static ssize_t pci_epf_msix_interrupts_store(struct config_item *item,
+                                            const char *page, size_t len)
+{
+       u16 val;
+       int ret;
+
+       ret = kstrtou16(page, 0, &val);
+       if (ret)
+               return ret;
+
+       to_pci_epf_group(item)->epf->msix_interrupts = val;
+
+       return len;
+}
+
+static ssize_t pci_epf_msix_interrupts_show(struct config_item *item,
+                                           char *page)
+{
+       return sprintf(page, "%d\n",
+                      to_pci_epf_group(item)->epf->msix_interrupts);
+}
+
 PCI_EPF_HEADER_R(vendorid)
 PCI_EPF_HEADER_W_u16(vendorid)
 
@@ -327,6 +349,7 @@ CONFIGFS_ATTR(pci_epf_, subsys_vendor_id);
 CONFIGFS_ATTR(pci_epf_, subsys_id);
 CONFIGFS_ATTR(pci_epf_, interrupt_pin);
 CONFIGFS_ATTR(pci_epf_, msi_interrupts);
+CONFIGFS_ATTR(pci_epf_, msix_interrupts);
 
 static struct configfs_attribute *pci_epf_attrs[] = {
        &pci_epf_attr_vendorid,
@@ -340,6 +363,7 @@ static struct configfs_attribute *pci_epf_attrs[] = {
        &pci_epf_attr_subsys_id,
        &pci_epf_attr_interrupt_pin,
        &pci_epf_attr_msi_interrupts,
+       &pci_epf_attr_msix_interrupts,
        NULL,
 };
 
index b0ee427..094dcc3 100644 (file)
@@ -131,13 +131,13 @@ EXPORT_SYMBOL_GPL(pci_epc_start);
  * pci_epc_raise_irq() - interrupt the host system
  * @epc: the EPC device which has to interrupt the host
  * @func_no: the endpoint function number in the EPC device
- * @type: specify the type of interrupt; legacy or MSI
- * @interrupt_num: the MSI interrupt number
+ * @type: specify the type of interrupt; legacy, MSI or MSI-X
+ * @interrupt_num: the MSI or MSI-X interrupt number
  *
- * Invoke to raise an MSI or legacy interrupt
+ * Invoke to raise an legacy, MSI or MSI-X interrupt
  */
 int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,
-                     enum pci_epc_irq_type type, u8 interrupt_num)
+                     enum pci_epc_irq_type type, u16 interrupt_num)
 {
        int ret;
        unsigned long flags;
@@ -201,7 +201,8 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts)
        u8 encode_int;
        unsigned long flags;
 
-       if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
+       if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
+           interrupts > 32)
                return -EINVAL;
 
        if (!epc->ops->set_msi)
@@ -217,6 +218,63 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts)
 }
 EXPORT_SYMBOL_GPL(pci_epc_set_msi);
 
+/**
+ * pci_epc_get_msix() - get the number of MSI-X interrupt numbers allocated
+ * @epc: the EPC device to which MSI-X interrupts was requested
+ * @func_no: the endpoint function number in the EPC device
+ *
+ * Invoke to get the number of MSI-X interrupts allocated by the RC
+ */
+int pci_epc_get_msix(struct pci_epc *epc, u8 func_no)
+{
+       int interrupt;
+       unsigned long flags;
+
+       if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
+               return 0;
+
+       if (!epc->ops->get_msix)
+               return 0;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       interrupt = epc->ops->get_msix(epc, func_no);
+       spin_unlock_irqrestore(&epc->lock, flags);
+
+       if (interrupt < 0)
+               return 0;
+
+       return interrupt + 1;
+}
+EXPORT_SYMBOL_GPL(pci_epc_get_msix);
+
+/**
+ * pci_epc_set_msix() - set the number of MSI-X interrupt numbers required
+ * @epc: the EPC device on which MSI-X has to be configured
+ * @func_no: the endpoint function number in the EPC device
+ * @interrupts: number of MSI-X interrupts required by the EPF
+ *
+ * Invoke to set the required number of MSI-X interrupts.
+ */
+int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)
+{
+       int ret;
+       unsigned long flags;
+
+       if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
+           interrupts < 1 || interrupts > 2048)
+               return -EINVAL;
+
+       if (!epc->ops->set_msix)
+               return 0;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       ret = epc->ops->set_msix(epc, func_no, interrupts - 1);
+       spin_unlock_irqrestore(&epc->lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pci_epc_set_msix);
+
 /**
  * pci_epc_unmap_addr() - unmap CPU address from PCI address
  * @epc: the EPC device on which address is allocated
index 523a8ca..825fa24 100644 (file)
@@ -137,6 +137,20 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar)
 }
 EXPORT_SYMBOL_GPL(pci_epf_alloc_space);
 
+static void pci_epf_remove_cfs(struct pci_epf_driver *driver)
+{
+       struct config_group *group, *tmp;
+
+       if (!IS_ENABLED(CONFIG_PCI_ENDPOINT_CONFIGFS))
+               return;
+
+       mutex_lock(&pci_epf_mutex);
+       list_for_each_entry_safe(group, tmp, &driver->epf_group, group_entry)
+               pci_ep_cfs_remove_epf_group(group);
+       list_del(&driver->epf_group);
+       mutex_unlock(&pci_epf_mutex);
+}
+
 /**
  * pci_epf_unregister_driver() - unregister the PCI EPF driver
  * @driver: the PCI EPF driver that has to be unregistered
@@ -145,17 +159,38 @@ EXPORT_SYMBOL_GPL(pci_epf_alloc_space);
  */
 void pci_epf_unregister_driver(struct pci_epf_driver *driver)
 {
-       struct config_group *group;
-
-       mutex_lock(&pci_epf_mutex);
-       list_for_each_entry(group, &driver->epf_group, group_entry)
-               pci_ep_cfs_remove_epf_group(group);
-       list_del(&driver->epf_group);
-       mutex_unlock(&pci_epf_mutex);
+       pci_epf_remove_cfs(driver);
        driver_unregister(&driver->driver);
 }
 EXPORT_SYMBOL_GPL(pci_epf_unregister_driver);
 
+static int pci_epf_add_cfs(struct pci_epf_driver *driver)
+{
+       struct config_group *group;
+       const struct pci_epf_device_id *id;
+
+       if (!IS_ENABLED(CONFIG_PCI_ENDPOINT_CONFIGFS))
+               return 0;
+
+       INIT_LIST_HEAD(&driver->epf_group);
+
+       id = driver->id_table;
+       while (id->name[0]) {
+               group = pci_ep_cfs_add_epf_group(id->name);
+               if (IS_ERR(group)) {
+                       pci_epf_remove_cfs(driver);
+                       return PTR_ERR(group);
+               }
+
+               mutex_lock(&pci_epf_mutex);
+               list_add_tail(&group->group_entry, &driver->epf_group);
+               mutex_unlock(&pci_epf_mutex);
+               id++;
+       }
+
+       return 0;
+}
+
 /**
  * __pci_epf_register_driver() - register a new PCI EPF driver
  * @driver: structure representing PCI EPF driver
@@ -167,8 +202,6 @@ int __pci_epf_register_driver(struct pci_epf_driver *driver,
                              struct module *owner)
 {
        int ret;
-       struct config_group *group;
-       const struct pci_epf_device_id *id;
 
        if (!driver->ops)
                return -EINVAL;
@@ -183,16 +216,7 @@ int __pci_epf_register_driver(struct pci_epf_driver *driver,
        if (ret)
                return ret;
 
-       INIT_LIST_HEAD(&driver->epf_group);
-
-       id = driver->id_table;
-       while (id->name[0]) {
-               group = pci_ep_cfs_add_epf_group(id->name);
-               mutex_lock(&pci_epf_mutex);
-               list_add_tail(&group->group_entry, &driver->epf_group);
-               mutex_unlock(&pci_epf_mutex);
-               id++;
-       }
+       pci_epf_add_cfs(driver);
 
        return 0;
 }
index 3979f89..6b7c1ed 100644 (file)
@@ -7,7 +7,6 @@
  * All rights reserved.
  *
  * Send feedback to <kristen.c.accardi@intel.com>
- *
  */
 
 #include <linux/module.h>
@@ -75,23 +74,34 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev)
        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 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.
+        * If there's no ACPI host bridge (i.e., ACPI support is compiled
+        * into the kernel but the hardware platform doesn't support ACPI),
+        * there's nothing to do here.
         */
-       if (shpchp_is_native(pdev))
-               return 0;
-
-       /* If _OSC exists, we should not evaluate OSHP */
        host = pci_find_host_bridge(pdev->bus);
        root = acpi_pci_find_root(ACPI_HANDLE(&host->dev));
-       if (root->osc_support_set)
-               goto no_control;
+       if (!root)
+               return 0;
 
+       /*
+        * If _OSC exists, it determines whether we're allowed to manage
+        * the SHPC.  We executed it while enumerating the host bridge.
+        */
+       if (root->osc_support_set) {
+               if (host->native_shpc_hotplug)
+                       return 0;
+               return -ENODEV;
+       }
+
+       /*
+        * In the absence of _OSC, we're always allowed to manage the SHPC.
+        * However, if an OSHP method is present, we must execute it so the
+        * firmware can transfer control to the OS, e.g., direct interrupts
+        * to the OS instead of to the firmware.
+        *
+        * N.B. The PCI Firmware Spec (r3.2, sec 4.8) does not endorse
+        * searching up the ACPI hierarchy, so the loops below are suspect.
+        */
        handle = ACPI_HANDLE(&pdev->dev);
        if (!handle) {
                /*
@@ -120,7 +130,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev)
                if (ACPI_FAILURE(status))
                        break;
        }
-no_control:
+
        pci_info(pdev, "Cannot get control of SHPC hotplug\n");
        kfree(string.pointer);
        return -ENODEV;
index 12b5655..ad32ffb 100644 (file)
@@ -254,20 +254,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
        return 0;
 }
 
-/**
- * release_slot - free up the memory used by a slot
- * @hotplug_slot: slot to free
- */
-static void release_slot(struct hotplug_slot *hotplug_slot)
-{
-       struct slot *slot = hotplug_slot->private;
-
-       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,
                                  unsigned int sun)
@@ -287,7 +273,6 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
        slot->hotplug_slot->info = &slot->info;
 
        slot->hotplug_slot->private = slot;
-       slot->hotplug_slot->release = &release_slot;
        slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
 
        slot->acpi_slot = acpiphp_slot;
@@ -324,13 +309,12 @@ error:
 void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
 {
        struct slot *slot = acpiphp_slot->slot;
-       int retval = 0;
 
        pr_info("Slot [%s] unregistered\n", slot_name(slot));
 
-       retval = pci_hp_deregister(slot->hotplug_slot);
-       if (retval)
-               pr_err("pci_hp_deregister failed with error %d\n", retval);
+       pci_hp_deregister(slot->hotplug_slot);
+       kfree(slot->hotplug_slot);
+       kfree(slot);
 }
 
 
index 3a17b29..ef0b1b6 100644 (file)
@@ -509,7 +509,7 @@ static void enable_slot(struct acpiphp_slot *slot)
 
        list_for_each_entry(dev, &bus->devices, bus_list) {
                /* Assume that newly added devices are powered on already. */
-               if (!dev->is_added)
+               if (!pci_dev_is_added(dev))
                        dev->current_state = PCI_D0;
        }
 
index 07b533a..52a339b 100644 (file)
@@ -195,10 +195,8 @@ get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
        return 0;
 }
 
-static void release_slot(struct hotplug_slot *hotplug_slot)
+static void release_slot(struct slot *slot)
 {
-       struct slot *slot = hotplug_slot->private;
-
        kfree(slot->hotplug_slot->info);
        kfree(slot->hotplug_slot);
        pci_dev_put(slot->dev);
@@ -253,7 +251,6 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
                snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i);
 
                hotplug_slot->private = slot;
-               hotplug_slot->release = &release_slot;
                hotplug_slot->ops = &cpci_hotplug_slot_ops;
 
                /*
@@ -308,12 +305,8 @@ cpci_hp_unregister_bus(struct pci_bus *bus)
                        slots--;
 
                        dbg("deregistering slot %s", slot_name(slot));
-                       status = pci_hp_deregister(slot->hotplug_slot);
-                       if (status) {
-                               err("pci_hp_deregister failed with error %d",
-                                   status);
-                               break;
-                       }
+                       pci_hp_deregister(slot->hotplug_slot);
+                       release_slot(slot);
                }
        }
        up_write(&list_rwsem);
@@ -623,6 +616,7 @@ cleanup_slots(void)
        list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
                list_del(&slot->slot_list);
                pci_hp_deregister(slot->hotplug_slot);
+               release_slot(slot);
        }
 cleanup_null:
        up_write(&list_rwsem);
index 1797e36..5a06636 100644 (file)
@@ -266,17 +266,6 @@ static void __iomem *get_SMBIOS_entry(void __iomem *smbios_start,
        return previous;
 }
 
-static void release_slot(struct hotplug_slot *hotplug_slot)
-{
-       struct slot *slot = hotplug_slot->private;
-
-       dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
-
-       kfree(slot->hotplug_slot->info);
-       kfree(slot->hotplug_slot);
-       kfree(slot);
-}
-
 static int ctrl_slot_cleanup(struct controller *ctrl)
 {
        struct slot *old_slot, *next_slot;
@@ -285,9 +274,11 @@ static int ctrl_slot_cleanup(struct controller *ctrl)
        ctrl->slot = NULL;
 
        while (old_slot) {
-               /* memory will be freed by the release_slot callback */
                next_slot = old_slot->next;
                pci_hp_deregister(old_slot->hotplug_slot);
+               kfree(old_slot->hotplug_slot->info);
+               kfree(old_slot->hotplug_slot);
+               kfree(old_slot);
                old_slot = next_slot;
        }
 
@@ -678,7 +669,6 @@ static int ctrl_slot_setup(struct controller *ctrl,
                        ((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;
index 1869b04..4ea57e9 100644 (file)
@@ -673,7 +673,20 @@ static void free_slots(void)
 
        list_for_each_entry_safe(slot_cur, next, &ibmphp_slot_head,
                                 ibm_slot_list) {
-               pci_hp_deregister(slot_cur->hotplug_slot);
+               pci_hp_del(slot_cur->hotplug_slot);
+               slot_cur->ctrl = NULL;
+               slot_cur->bus_on = NULL;
+
+               /*
+                * We don't want to actually remove the resources,
+                * since ibmphp_free_resources() will do just that.
+                */
+               ibmphp_unconfigure_card(&slot_cur, -1);
+
+               pci_hp_destroy(slot_cur->hotplug_slot);
+               kfree(slot_cur->hotplug_slot->info);
+               kfree(slot_cur->hotplug_slot);
+               kfree(slot_cur);
        }
        debug("%s -- exit\n", __func__);
 }
index 64549aa..6f8e90e 100644 (file)
@@ -699,25 +699,6 @@ static int fillslotinfo(struct hotplug_slot *hotplug_slot)
        return rc;
 }
 
-static void release_slot(struct hotplug_slot *hotplug_slot)
-{
-       struct slot *slot;
-
-       if (!hotplug_slot || !hotplug_slot->private)
-               return;
-
-       slot = hotplug_slot->private;
-       kfree(slot->hotplug_slot->info);
-       kfree(slot->hotplug_slot);
-       slot->ctrl = NULL;
-       slot->bus_on = NULL;
-
-       /* we don't want to actually remove the resources, since free_resources will do just that */
-       ibmphp_unconfigure_card(&slot, -1);
-
-       kfree(slot);
-}
-
 static struct pci_driver ibmphp_driver;
 
 /*
@@ -941,7 +922,6 @@ static int __init ebda_rsrc_controller(void)
                        tmp_slot->hotplug_slot = hp_slot_ptr;
 
                        hp_slot_ptr->private = tmp_slot;
-                       hp_slot_ptr->release = release_slot;
 
                        rc = fillslotinfo(hp_slot_ptr);
                        if (rc)
index af92fed..90fde5f 100644 (file)
@@ -396,8 +396,9 @@ static struct hotplug_slot *get_slot_from_name(const char *name)
  * @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.
+ * Prepares a hotplug slot for in-kernel use and immediately publishes it to
+ * user space in one go.  Drivers may alternatively carry out the two steps
+ * separately by invoking pci_hp_initialize() and pci_hp_add().
  *
  * Returns 0 if successful, anything else for an error.
  */
@@ -406,45 +407,91 @@ int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
                      struct module *owner, const char *mod_name)
 {
        int result;
+
+       result = __pci_hp_initialize(slot, bus, devnr, name, owner, mod_name);
+       if (result)
+               return result;
+
+       result = pci_hp_add(slot);
+       if (result)
+               pci_hp_destroy(slot);
+
+       return result;
+}
+EXPORT_SYMBOL_GPL(__pci_hp_register);
+
+/**
+ * __pci_hp_initialize - prepare hotplug slot for in-kernel use
+ * @slot: pointer to the &struct hotplug_slot to initialize
+ * @bus: bus this slot is on
+ * @devnr: slot number
+ * @name: name registered with kobject core
+ * @owner: caller module owner
+ * @mod_name: caller module name
+ *
+ * Allocate and fill in a PCI slot for use by a hotplug driver.  Once this has
+ * been called, the driver may invoke hotplug_slot_name() to get the slot's
+ * unique name.  The driver must be prepared to handle a ->reset_slot callback
+ * from this point on.
+ *
+ * Returns 0 on success or a negative int on error.
+ */
+int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus,
+                       int devnr, const char *name, struct module *owner,
+                       const char *mod_name)
+{
        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");
-               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;
-       }
+       if (IS_ERR(pci_slot))
+               return PTR_ERR(pci_slot);
 
        slot->pci_slot = pci_slot;
        pci_slot->hotplug = slot;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__pci_hp_initialize);
 
-       list_add(&slot->slot_list, &pci_hotplug_slot_list);
+/**
+ * pci_hp_add - publish hotplug slot to user space
+ * @slot: pointer to the &struct hotplug_slot to publish
+ *
+ * Make a hotplug slot's sysfs interface available and inform user space of its
+ * addition by sending a uevent.  The hotplug driver must be prepared to handle
+ * all &struct hotplug_slot_ops callbacks from this point on.
+ *
+ * Returns 0 on success or a negative int on error.
+ */
+int pci_hp_add(struct hotplug_slot *slot)
+{
+       struct pci_slot *pci_slot = slot->pci_slot;
+       int result;
 
        result = fs_add_slot(pci_slot);
+       if (result)
+               return result;
+
        kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
-       dbg("Added slot %s to the list\n", name);
-out:
+       mutex_lock(&pci_hp_mutex);
+       list_add(&slot->slot_list, &pci_hotplug_slot_list);
        mutex_unlock(&pci_hp_mutex);
-       return result;
+       dbg("Added slot %s to the list\n", hotplug_slot_name(slot));
+       return 0;
 }
-EXPORT_SYMBOL_GPL(__pci_hp_register);
+EXPORT_SYMBOL_GPL(pci_hp_add);
 
 /**
  * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
@@ -455,35 +502,62 @@ EXPORT_SYMBOL_GPL(__pci_hp_register);
  *
  * Returns 0 if successful, anything else for an error.
  */
-int pci_hp_deregister(struct hotplug_slot *slot)
+void pci_hp_deregister(struct hotplug_slot *slot)
+{
+       pci_hp_del(slot);
+       pci_hp_destroy(slot);
+}
+EXPORT_SYMBOL_GPL(pci_hp_deregister);
+
+/**
+ * pci_hp_del - unpublish hotplug slot from user space
+ * @slot: pointer to the &struct hotplug_slot to unpublish
+ *
+ * Remove a hotplug slot's sysfs interface.
+ *
+ * Returns 0 on success or a negative int on error.
+ */
+void pci_hp_del(struct hotplug_slot *slot)
 {
        struct hotplug_slot *temp;
-       struct pci_slot *pci_slot;
 
-       if (!slot)
-               return -ENODEV;
+       if (WARN_ON(!slot))
+               return;
 
        mutex_lock(&pci_hp_mutex);
        temp = get_slot_from_name(hotplug_slot_name(slot));
-       if (temp != slot) {
+       if (WARN_ON(temp != slot)) {
                mutex_unlock(&pci_hp_mutex);
-               return -ENODEV;
+               return;
        }
 
        list_del(&slot->slot_list);
-
-       pci_slot = slot->pci_slot;
-       fs_remove_slot(pci_slot);
+       mutex_unlock(&pci_hp_mutex);
        dbg("Removed slot %s from the list\n", hotplug_slot_name(slot));
+       fs_remove_slot(slot->pci_slot);
+}
+EXPORT_SYMBOL_GPL(pci_hp_del);
 
-       slot->release(slot);
+/**
+ * pci_hp_destroy - remove hotplug slot from in-kernel use
+ * @slot: pointer to the &struct hotplug_slot to destroy
+ *
+ * Destroy a PCI slot used by a hotplug driver.  Once this has been called,
+ * the driver may no longer invoke hotplug_slot_name() to get the slot's
+ * unique name.  The driver no longer needs to handle a ->reset_slot callback
+ * from this point on.
+ *
+ * Returns 0 on success or a negative int on error.
+ */
+void pci_hp_destroy(struct hotplug_slot *slot)
+{
+       struct pci_slot *pci_slot = slot->pci_slot;
+
+       slot->pci_slot = NULL;
        pci_slot->hotplug = NULL;
        pci_destroy_slot(pci_slot);
-       mutex_unlock(&pci_hp_mutex);
-
-       return 0;
 }
-EXPORT_SYMBOL_GPL(pci_hp_deregister);
+EXPORT_SYMBOL_GPL(pci_hp_destroy);
 
 /**
  * pci_hp_change_slot_info - changes the slot's information structure in the core
index 5f89206..811cf83 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/delay.h>
 #include <linux/sched/signal.h>                /* signal_pending() */
 #include <linux/mutex.h>
+#include <linux/rwsem.h>
 #include <linux/workqueue.h>
 
 #include "../pcie/portdrv.h"
@@ -57,49 +58,111 @@ do {                                                                       \
        dev_warn(&ctrl->pcie->device, format, ## arg)
 
 #define SLOT_NAME_SIZE 10
+
+/**
+ * struct slot - PCIe hotplug slot
+ * @state: current state machine position
+ * @ctrl: pointer to the slot's controller structure
+ * @hotplug_slot: pointer to the structure registered with the PCI hotplug core
+ * @work: work item to turn the slot on or off after 5 seconds in response to
+ *     an Attention Button press
+ * @lock: protects reads and writes of @state;
+ *     protects scheduling, execution and cancellation of @work
+ */
 struct slot {
        u8 state;
        struct controller *ctrl;
        struct hotplug_slot *hotplug_slot;
-       struct delayed_work work;       /* work for button event */
+       struct delayed_work work;
        struct mutex lock;
-       struct mutex hotplug_lock;
-       struct workqueue_struct *wq;
-};
-
-struct event_info {
-       u32 event_type;
-       struct slot *p_slot;
-       struct work_struct work;
 };
 
+/**
+ * struct controller - PCIe hotplug controller
+ * @ctrl_lock: serializes writes to the Slot Control register
+ * @pcie: pointer to the controller's PCIe port service device
+ * @reset_lock: prevents access to the Data Link Layer Link Active bit in the
+ *     Link Status register and to the Presence Detect State bit in the Slot
+ *     Status register during a slot reset which may cause them to flap
+ * @slot: pointer to the controller's slot structure
+ * @queue: wait queue to wake up on reception of a Command Completed event,
+ *     used for synchronous writes to the Slot Control register
+ * @slot_cap: cached copy of the Slot Capabilities register
+ * @slot_ctrl: cached copy of the Slot Control register
+ * @poll_thread: thread to poll for slot events if no IRQ is available,
+ *     enabled with pciehp_poll_mode module parameter
+ * @cmd_started: jiffies when the Slot Control register was last written;
+ *     the next write is allowed 1 second later, absent a Command Completed
+ *     interrupt (PCIe r4.0, sec 6.7.3.2)
+ * @cmd_busy: flag set on Slot Control register write, cleared by IRQ handler
+ *     on reception of a Command Completed event
+ * @link_active_reporting: cached copy of Data Link Layer Link Active Reporting
+ *     Capable bit in Link Capabilities register; if this bit is zero, the
+ *     Data Link Layer Link Active bit in the Link Status register will never
+ *     be set and the driver is thus confined to wait 1 second before assuming
+ *     the link to a hotplugged device is up and accessing it
+ * @notification_enabled: whether the IRQ was requested successfully
+ * @power_fault_detected: whether a power fault was detected by the hardware
+ *     that has not yet been cleared by the user
+ * @pending_events: used by the IRQ handler to save events retrieved from the
+ *     Slot Status register for later consumption by the IRQ thread
+ * @request_result: result of last user request submitted to the IRQ thread
+ * @requester: wait queue to wake up on completion of user request,
+ *     used for synchronous slot enable/disable request via sysfs
+ */
 struct controller {
-       struct mutex ctrl_lock;         /* controller lock */
-       struct pcie_device *pcie;       /* PCI Express port service */
+       struct mutex ctrl_lock;
+       struct pcie_device *pcie;
+       struct rw_semaphore reset_lock;
        struct slot *slot;
-       wait_queue_head_t queue;        /* sleep & wake process */
+       wait_queue_head_t queue;
        u32 slot_cap;
        u16 slot_ctrl;
-       struct timer_list poll_timer;
+       struct task_struct *poll_thread;
        unsigned long cmd_started;      /* jiffies */
        unsigned int cmd_busy:1;
        unsigned int link_active_reporting:1;
        unsigned int notification_enabled:1;
        unsigned int power_fault_detected;
+       atomic_t pending_events;
+       int request_result;
+       wait_queue_head_t requester;
 };
 
-#define INT_PRESENCE_ON                        1
-#define INT_PRESENCE_OFF               2
-#define INT_POWER_FAULT                        3
-#define INT_BUTTON_PRESS               4
-#define INT_LINK_UP                    5
-#define INT_LINK_DOWN                  6
-
-#define STATIC_STATE                   0
+/**
+ * DOC: Slot state
+ *
+ * @OFF_STATE: slot is powered off, no subordinate devices are enumerated
+ * @BLINKINGON_STATE: slot will be powered on after the 5 second delay,
+ *     green led is blinking
+ * @BLINKINGOFF_STATE: slot will be powered off after the 5 second delay,
+ *     green led is blinking
+ * @POWERON_STATE: slot is currently powering on
+ * @POWEROFF_STATE: slot is currently powering off
+ * @ON_STATE: slot is powered on, subordinate devices have been enumerated
+ */
+#define OFF_STATE                      0
 #define BLINKINGON_STATE               1
 #define BLINKINGOFF_STATE              2
 #define POWERON_STATE                  3
 #define POWEROFF_STATE                 4
+#define ON_STATE                       5
+
+/**
+ * DOC: Flags to request an action from the IRQ thread
+ *
+ * These are stored together with events read from the Slot Status register,
+ * hence must be greater than its 16-bit width.
+ *
+ * %DISABLE_SLOT: Disable the slot in response to a user request via sysfs or
+ *     an Attention Button press after the 5 second delay
+ * %RERUN_ISR: Used by the IRQ handler to inform the IRQ thread that the
+ *     hotplug port was inaccessible when the interrupt occurred, requiring
+ *     that the IRQ handler is rerun by the IRQ thread after it has made the
+ *     hotplug port accessible by runtime resuming its parents to D0
+ */
+#define DISABLE_SLOT           (1 << 16)
+#define RERUN_ISR              (1 << 17)
 
 #define ATTN_BUTTN(ctrl)       ((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP)
 #define POWER_CTRL(ctrl)       ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP)
@@ -113,15 +176,17 @@ struct controller {
 
 int pciehp_sysfs_enable_slot(struct slot *slot);
 int pciehp_sysfs_disable_slot(struct slot *slot);
-void pciehp_queue_interrupt_event(struct slot *slot, u32 event_type);
+void pciehp_request(struct controller *ctrl, int action);
+void pciehp_handle_button_press(struct slot *slot);
+void pciehp_handle_disable_request(struct slot *slot);
+void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events);
 int pciehp_configure_device(struct slot *p_slot);
-int pciehp_unconfigure_device(struct slot *p_slot);
+void 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);
-void pcie_reenable_notification(struct controller *ctrl);
+void pcie_shutdown_notification(struct controller *ctrl);
+void pcie_clear_hotplug_events(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);
index 44a6a63..ec48c94 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/time.h>
 
+#include "../pci.h"
+
 /* Global variables */
 bool pciehp_debug;
 bool pciehp_poll_mode;
 int pciehp_poll_time;
-static bool pciehp_force;
 
 /*
  * not really modular, but the easiest way to keep compat with existing
@@ -39,11 +40,9 @@ static bool pciehp_force;
 module_param(pciehp_debug, bool, 0644);
 module_param(pciehp_poll_mode, bool, 0644);
 module_param(pciehp_poll_time, int, 0644);
-module_param(pciehp_force, bool, 0644);
 MODULE_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 OSHP is missing");
 
 #define PCIE_MODULE_NAME "pciehp"
 
@@ -56,17 +55,6 @@ static int get_latch_status(struct hotplug_slot *slot, u8 *value);
 static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
 static int reset_slot(struct hotplug_slot *slot, int probe);
 
-/**
- * release_slot - free up the memory used by a slot
- * @hotplug_slot: slot to free
- */
-static void release_slot(struct hotplug_slot *hotplug_slot)
-{
-       kfree(hotplug_slot->ops);
-       kfree(hotplug_slot->info);
-       kfree(hotplug_slot);
-}
-
 static int init_slot(struct controller *ctrl)
 {
        struct slot *slot = ctrl->slot;
@@ -107,15 +95,14 @@ static int init_slot(struct controller *ctrl)
        /* 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));
 
-       retval = pci_hp_register(hotplug,
-                                ctrl->pcie->port->subordinate, 0, name);
+       retval = pci_hp_initialize(hotplug,
+                                  ctrl->pcie->port->subordinate, 0, name);
        if (retval)
-               ctrl_err(ctrl, "pci_hp_register failed: error %d\n", retval);
+               ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval);
 out:
        if (retval) {
                kfree(ops);
@@ -127,7 +114,12 @@ out:
 
 static void cleanup_slot(struct controller *ctrl)
 {
-       pci_hp_deregister(ctrl->slot->hotplug_slot);
+       struct hotplug_slot *hotplug_slot = ctrl->slot->hotplug_slot;
+
+       pci_hp_destroy(hotplug_slot);
+       kfree(hotplug_slot->ops);
+       kfree(hotplug_slot->info);
+       kfree(hotplug_slot);
 }
 
 /*
@@ -136,8 +128,11 @@ static void cleanup_slot(struct controller *ctrl)
 static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
 {
        struct slot *slot = hotplug_slot->private;
+       struct pci_dev *pdev = slot->ctrl->pcie->port;
 
+       pci_config_pm_runtime_get(pdev);
        pciehp_set_attention_status(slot, status);
+       pci_config_pm_runtime_put(pdev);
        return 0;
 }
 
@@ -160,8 +155,11 @@ 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;
+       struct pci_dev *pdev = slot->ctrl->pcie->port;
 
+       pci_config_pm_runtime_get(pdev);
        pciehp_get_power_status(slot, value);
+       pci_config_pm_runtime_put(pdev);
        return 0;
 }
 
@@ -176,16 +174,22 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        struct slot *slot = hotplug_slot->private;
+       struct pci_dev *pdev = slot->ctrl->pcie->port;
 
+       pci_config_pm_runtime_get(pdev);
        pciehp_get_latch_status(slot, value);
+       pci_config_pm_runtime_put(pdev);
        return 0;
 }
 
 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        struct slot *slot = hotplug_slot->private;
+       struct pci_dev *pdev = slot->ctrl->pcie->port;
 
+       pci_config_pm_runtime_get(pdev);
        pciehp_get_adapter_status(slot, value);
+       pci_config_pm_runtime_put(pdev);
        return 0;
 }
 
@@ -196,12 +200,40 @@ static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
        return pciehp_reset_slot(slot, probe);
 }
 
+/**
+ * pciehp_check_presence() - synthesize event if presence has changed
+ *
+ * On probe and resume, an explicit presence check is necessary to bring up an
+ * occupied slot or bring down an unoccupied slot.  This can't be triggered by
+ * events in the Slot Status register, they may be stale and are therefore
+ * cleared.  Secondly, sending an interrupt for "events that occur while
+ * interrupt generation is disabled [when] interrupt generation is subsequently
+ * enabled" is optional per PCIe r4.0, sec 6.7.3.4.
+ */
+static void pciehp_check_presence(struct controller *ctrl)
+{
+       struct slot *slot = ctrl->slot;
+       u8 occupied;
+
+       down_read(&ctrl->reset_lock);
+       mutex_lock(&slot->lock);
+
+       pciehp_get_adapter_status(slot, &occupied);
+       if ((occupied && (slot->state == OFF_STATE ||
+                         slot->state == BLINKINGON_STATE)) ||
+           (!occupied && (slot->state == ON_STATE ||
+                          slot->state == BLINKINGOFF_STATE)))
+               pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
+
+       mutex_unlock(&slot->lock);
+       up_read(&ctrl->reset_lock);
+}
+
 static int pciehp_probe(struct pcie_device *dev)
 {
        int rc;
        struct controller *ctrl;
        struct slot *slot;
-       u8 occupied, poweron;
 
        /* If this is not a "hotplug" service, we have no business here. */
        if (dev->service != PCIE_PORT_SERVICE_HP)
@@ -238,21 +270,20 @@ static int pciehp_probe(struct pcie_device *dev)
                goto err_out_free_ctrl_slot;
        }
 
-       /* Check if slot is occupied */
+       /* Publish to user space */
        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);
+       rc = pci_hp_add(slot->hotplug_slot);
+       if (rc) {
+               ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc);
+               goto err_out_shutdown_notification;
        }
-       /* If empty slot's power status is on, turn power off */
-       if (!occupied && poweron && POWER_CTRL(ctrl))
-               pciehp_power_off_slot(slot);
+
+       pciehp_check_presence(ctrl);
 
        return 0;
 
+err_out_shutdown_notification:
+       pcie_shutdown_notification(ctrl);
 err_out_free_ctrl_slot:
        cleanup_slot(ctrl);
 err_out_release_ctlr:
@@ -264,6 +295,8 @@ static void pciehp_remove(struct pcie_device *dev)
 {
        struct controller *ctrl = get_service_data(dev);
 
+       pci_hp_del(ctrl->slot->hotplug_slot);
+       pcie_shutdown_notification(ctrl);
        cleanup_slot(ctrl);
        pciehp_release_ctrl(ctrl);
 }
@@ -274,27 +307,28 @@ static int pciehp_suspend(struct pcie_device *dev)
        return 0;
 }
 
-static int pciehp_resume(struct pcie_device *dev)
+static int pciehp_resume_noirq(struct pcie_device *dev)
 {
-       struct controller *ctrl;
-       struct slot *slot;
-       u8 status;
+       struct controller *ctrl = get_service_data(dev);
+       struct slot *slot = ctrl->slot;
 
-       ctrl = get_service_data(dev);
+       /* pci_restore_state() just wrote to the Slot Control register */
+       ctrl->cmd_started = jiffies;
+       ctrl->cmd_busy = true;
 
-       /* reinitialize the chipset's event detection logic */
-       pcie_reenable_notification(ctrl);
+       /* clear spurious events from rediscovery of inserted card */
+       if (slot->state == ON_STATE || slot->state == BLINKINGOFF_STATE)
+               pcie_clear_hotplug_events(ctrl);
 
-       slot = ctrl->slot;
+       return 0;
+}
+
+static int pciehp_resume(struct pcie_device *dev)
+{
+       struct controller *ctrl = get_service_data(dev);
+
+       pciehp_check_presence(ctrl);
 
-       /* 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 /* PM */
@@ -309,6 +343,7 @@ static struct pcie_port_service_driver hpdriver_portdrv = {
 
 #ifdef CONFIG_PM
        .suspend        = pciehp_suspend,
+       .resume_noirq   = pciehp_resume_noirq,
        .resume         = pciehp_resume,
 #endif /* PM */
 };
index c684faa..da7c723 100644 (file)
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
 #include <linux/pci.h>
 #include "../pci.h"
 #include "pciehp.h"
 
-static void interrupt_event_handler(struct work_struct *work);
-
-void pciehp_queue_interrupt_event(struct slot *p_slot, u32 event_type)
-{
-       struct event_info *info;
-
-       info = kmalloc(sizeof(*info), GFP_ATOMIC);
-       if (!info) {
-               ctrl_err(p_slot->ctrl, "dropped event %d (ENOMEM)\n", event_type);
-               return;
-       }
-
-       INIT_WORK(&info->work, interrupt_event_handler);
-       info->event_type = event_type;
-       info->p_slot = p_slot;
-       queue_work(p_slot->wq, &info->work);
-}
-
 /* The following routines constitute the bulk of the
    hotplug controller logic
  */
@@ -119,14 +102,11 @@ err_exit:
  * remove_board - Turns off slot and LEDs
  * @p_slot: slot where board is being removed
  */
-static int remove_board(struct slot *p_slot)
+static void remove_board(struct slot *p_slot)
 {
-       int retval;
        struct controller *ctrl = p_slot->ctrl;
 
-       retval = pciehp_unconfigure_device(p_slot);
-       if (retval)
-               return retval;
+       pciehp_unconfigure_device(p_slot);
 
        if (POWER_CTRL(ctrl)) {
                pciehp_power_off_slot(p_slot);
@@ -141,86 +121,30 @@ static int remove_board(struct slot *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
-};
-
-/**
- * pciehp_power_thread - handle pushbutton events
- * @work: &struct work_struct describing work to be done
- *
- * Scheduled procedure to handle blocking stuff for the pushbuttons.
- * Handles all pending events and exits.
- */
-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;
-       int ret;
-
-       switch (info->req) {
-       case DISABLE_REQ:
-               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;
-               mutex_unlock(&p_slot->lock);
-               break;
-       case ENABLE_REQ:
-               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;
-       }
-
-       kfree(info);
-}
+static int pciehp_enable_slot(struct slot *slot);
+static int pciehp_disable_slot(struct slot *slot);
 
-static void pciehp_queue_power_work(struct slot *p_slot, int req)
+void pciehp_request(struct controller *ctrl, int action)
 {
-       struct power_work_info *info;
-
-       p_slot->state = (req == ENABLE_REQ) ? POWERON_STATE : POWEROFF_STATE;
-
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
-       if (!info) {
-               ctrl_err(p_slot->ctrl, "no memory to queue %s request\n",
-                        (req == ENABLE_REQ) ? "poweron" : "poweroff");
-               return;
-       }
-       info->p_slot = p_slot;
-       INIT_WORK(&info->work, pciehp_power_thread);
-       info->req = req;
-       queue_work(p_slot->wq, &info->work);
+       atomic_or(action, &ctrl->pending_events);
+       if (!pciehp_poll_mode)
+               irq_wake_thread(ctrl->pcie->irq, ctrl);
 }
 
 void pciehp_queue_pushbutton_work(struct work_struct *work)
 {
        struct slot *p_slot = container_of(work, struct slot, work.work);
+       struct controller *ctrl = p_slot->ctrl;
 
        mutex_lock(&p_slot->lock);
        switch (p_slot->state) {
        case BLINKINGOFF_STATE:
-               pciehp_queue_power_work(p_slot, DISABLE_REQ);
+               pciehp_request(ctrl, DISABLE_SLOT);
                break;
        case BLINKINGON_STATE:
-               pciehp_queue_power_work(p_slot, ENABLE_REQ);
+               pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
                break;
        default:
                break;
@@ -228,18 +152,15 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
        mutex_unlock(&p_slot->lock);
 }
 
-/*
- * Note: This function must be called with slot->lock held
- */
-static void handle_button_press_event(struct slot *p_slot)
+void pciehp_handle_button_press(struct slot *p_slot)
 {
        struct controller *ctrl = p_slot->ctrl;
-       u8 getstatus;
 
+       mutex_lock(&p_slot->lock);
        switch (p_slot->state) {
-       case STATIC_STATE:
-               pciehp_get_power_status(p_slot, &getstatus);
-               if (getstatus) {
+       case OFF_STATE:
+       case ON_STATE:
+               if (p_slot->state == ON_STATE) {
                        p_slot->state = BLINKINGOFF_STATE;
                        ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
                                  slot_name(p_slot));
@@ -251,7 +172,7 @@ static void handle_button_press_event(struct slot *p_slot)
                /* blink green LED and turn off amber */
                pciehp_green_led_blink(p_slot);
                pciehp_set_attention_status(p_slot, 0);
-               queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
+               schedule_delayed_work(&p_slot->work, 5 * HZ);
                break;
        case BLINKINGOFF_STATE:
        case BLINKINGON_STATE:
@@ -262,118 +183,104 @@ static void handle_button_press_event(struct slot *p_slot)
                 */
                ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot));
                cancel_delayed_work(&p_slot->work);
-               if (p_slot->state == BLINKINGOFF_STATE)
+               if (p_slot->state == BLINKINGOFF_STATE) {
+                       p_slot->state = ON_STATE;
                        pciehp_green_led_on(p_slot);
-               else
+               } else {
+                       p_slot->state = OFF_STATE;
                        pciehp_green_led_off(p_slot);
+               }
                pciehp_set_attention_status(p_slot, 0);
                ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
                          slot_name(p_slot));
-               p_slot->state = STATIC_STATE;
-               break;
-       case POWEROFF_STATE:
-       case POWERON_STATE:
-               /*
-                * Ignore if the slot is on power-on or power-off state;
-                * this means that the previous attention button action
-                * to hot-add or hot-remove is undergoing
-                */
-               ctrl_info(ctrl, "Slot(%s): Button ignored\n",
-                         slot_name(p_slot));
                break;
        default:
                ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
                         slot_name(p_slot), p_slot->state);
                break;
        }
+       mutex_unlock(&p_slot->lock);
 }
 
-/*
- * Note: This function must be called with slot->lock held
- */
-static void handle_link_event(struct slot *p_slot, u32 event)
+void pciehp_handle_disable_request(struct slot *slot)
 {
-       struct controller *ctrl = p_slot->ctrl;
+       struct controller *ctrl = slot->ctrl;
 
-       switch (p_slot->state) {
+       mutex_lock(&slot->lock);
+       switch (slot->state) {
        case BLINKINGON_STATE:
        case BLINKINGOFF_STATE:
-               cancel_delayed_work(&p_slot->work);
-               /* Fall through */
-       case STATIC_STATE:
-               pciehp_queue_power_work(p_slot, event == INT_LINK_UP ?
-                                       ENABLE_REQ : DISABLE_REQ);
-               break;
-       case POWERON_STATE:
-               if (event == INT_LINK_UP) {
-                       ctrl_info(ctrl, "Slot(%s): Link Up event ignored; already powering on\n",
-                                 slot_name(p_slot));
-               } else {
-                       ctrl_info(ctrl, "Slot(%s): Link Down event queued; currently getting powered on\n",
-                                 slot_name(p_slot));
-                       pciehp_queue_power_work(p_slot, DISABLE_REQ);
-               }
-               break;
-       case POWEROFF_STATE:
-               if (event == INT_LINK_UP) {
-                       ctrl_info(ctrl, "Slot(%s): Link Up event queued; currently getting powered off\n",
-                                 slot_name(p_slot));
-                       pciehp_queue_power_work(p_slot, ENABLE_REQ);
-               } else {
-                       ctrl_info(ctrl, "Slot(%s): Link Down event ignored; already powering off\n",
-                                 slot_name(p_slot));
-               }
-               break;
-       default:
-               ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
-                        slot_name(p_slot), p_slot->state);
+               cancel_delayed_work(&slot->work);
                break;
        }
+       slot->state = POWEROFF_STATE;
+       mutex_unlock(&slot->lock);
+
+       ctrl->request_result = pciehp_disable_slot(slot);
 }
 
-static void interrupt_event_handler(struct work_struct *work)
+void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events)
 {
-       struct event_info *info = container_of(work, struct event_info, work);
-       struct slot *p_slot = info->p_slot;
-       struct controller *ctrl = p_slot->ctrl;
-
-       mutex_lock(&p_slot->lock);
-       switch (info->event_type) {
-       case INT_BUTTON_PRESS:
-               handle_button_press_event(p_slot);
-               break;
-       case INT_POWER_FAULT:
-               if (!POWER_CTRL(ctrl))
-                       break;
-               pciehp_set_attention_status(p_slot, 1);
-               pciehp_green_led_off(p_slot);
-               break;
-       case INT_PRESENCE_ON:
-               pciehp_queue_power_work(p_slot, ENABLE_REQ);
+       struct controller *ctrl = slot->ctrl;
+       bool link_active;
+       u8 present;
+
+       /*
+        * If the slot is on and presence or link has changed, turn it off.
+        * Even if it's occupied again, we cannot assume the card is the same.
+        */
+       mutex_lock(&slot->lock);
+       switch (slot->state) {
+       case BLINKINGOFF_STATE:
+               cancel_delayed_work(&slot->work);
+               /* fall through */
+       case ON_STATE:
+               slot->state = POWEROFF_STATE;
+               mutex_unlock(&slot->lock);
+               if (events & PCI_EXP_SLTSTA_DLLSC)
+                       ctrl_info(ctrl, "Slot(%s): Link Down\n",
+                                 slot_name(slot));
+               if (events & PCI_EXP_SLTSTA_PDC)
+                       ctrl_info(ctrl, "Slot(%s): Card not present\n",
+                                 slot_name(slot));
+               pciehp_disable_slot(slot);
                break;
-       case INT_PRESENCE_OFF:
-               /*
-                * Regardless of surprise capability, we need to
-                * definitely remove a card that has been pulled out!
-                */
-               pciehp_queue_power_work(p_slot, DISABLE_REQ);
+       default:
+               mutex_unlock(&slot->lock);
                break;
-       case INT_LINK_UP:
-       case INT_LINK_DOWN:
-               handle_link_event(p_slot, info->event_type);
+       }
+
+       /* Turn the slot on if it's occupied or link is up */
+       mutex_lock(&slot->lock);
+       pciehp_get_adapter_status(slot, &present);
+       link_active = pciehp_check_link_active(ctrl);
+       if (!present && !link_active) {
+               mutex_unlock(&slot->lock);
+               return;
+       }
+
+       switch (slot->state) {
+       case BLINKINGON_STATE:
+               cancel_delayed_work(&slot->work);
+               /* fall through */
+       case OFF_STATE:
+               slot->state = POWERON_STATE;
+               mutex_unlock(&slot->lock);
+               if (present)
+                       ctrl_info(ctrl, "Slot(%s): Card present\n",
+                                 slot_name(slot));
+               if (link_active)
+                       ctrl_info(ctrl, "Slot(%s): Link Up\n",
+                                 slot_name(slot));
+               ctrl->request_result = pciehp_enable_slot(slot);
                break;
        default:
+               mutex_unlock(&slot->lock);
                break;
        }
-       mutex_unlock(&p_slot->lock);
-
-       kfree(info);
 }
 
-/*
- * Note: This function must be called with slot->hotplug_lock held
- */
-int pciehp_enable_slot(struct slot *p_slot)
+static int __pciehp_enable_slot(struct slot *p_slot)
 {
        u8 getstatus = 0;
        struct controller *ctrl = p_slot->ctrl;
@@ -404,17 +311,29 @@ int pciehp_enable_slot(struct slot *p_slot)
        return board_added(p_slot);
 }
 
-/*
- * Note: This function must be called with slot->hotplug_lock held
- */
-int pciehp_disable_slot(struct slot *p_slot)
+static int pciehp_enable_slot(struct slot *slot)
+{
+       struct controller *ctrl = slot->ctrl;
+       int ret;
+
+       pm_runtime_get_sync(&ctrl->pcie->port->dev);
+       ret = __pciehp_enable_slot(slot);
+       if (ret && ATTN_BUTTN(ctrl))
+               pciehp_green_led_off(slot); /* may be blinking */
+       pm_runtime_put(&ctrl->pcie->port->dev);
+
+       mutex_lock(&slot->lock);
+       slot->state = ret ? OFF_STATE : ON_STATE;
+       mutex_unlock(&slot->lock);
+
+       return ret;
+}
+
+static int __pciehp_disable_slot(struct slot *p_slot)
 {
        u8 getstatus = 0;
        struct controller *ctrl = p_slot->ctrl;
 
-       if (!p_slot->ctrl)
-               return 1;
-
        if (POWER_CTRL(p_slot->ctrl)) {
                pciehp_get_power_status(p_slot, &getstatus);
                if (!getstatus) {
@@ -424,32 +343,50 @@ int pciehp_disable_slot(struct slot *p_slot)
                }
        }
 
-       return remove_board(p_slot);
+       remove_board(p_slot);
+       return 0;
+}
+
+static int pciehp_disable_slot(struct slot *slot)
+{
+       struct controller *ctrl = slot->ctrl;
+       int ret;
+
+       pm_runtime_get_sync(&ctrl->pcie->port->dev);
+       ret = __pciehp_disable_slot(slot);
+       pm_runtime_put(&ctrl->pcie->port->dev);
+
+       mutex_lock(&slot->lock);
+       slot->state = OFF_STATE;
+       mutex_unlock(&slot->lock);
+
+       return ret;
 }
 
 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) {
        case BLINKINGON_STATE:
-               cancel_delayed_work(&p_slot->work);
-       case STATIC_STATE:
-               p_slot->state = POWERON_STATE;
+       case OFF_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;
+               /*
+                * The IRQ thread becomes a no-op if the user pulls out the
+                * card before the thread wakes up, so initialize to -ENODEV.
+                */
+               ctrl->request_result = -ENODEV;
+               pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
+               wait_event(ctrl->requester,
+                          !atomic_read(&ctrl->pending_events));
+               return ctrl->request_result;
        case POWERON_STATE:
                ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
                          slot_name(p_slot));
                break;
        case BLINKINGOFF_STATE:
+       case ON_STATE:
        case POWEROFF_STATE:
                ctrl_info(ctrl, "Slot(%s): Already enabled\n",
                          slot_name(p_slot));
@@ -461,32 +398,28 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
        }
        mutex_unlock(&p_slot->lock);
 
-       return retval;
+       return -ENODEV;
 }
 
 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) {
        case BLINKINGOFF_STATE:
-               cancel_delayed_work(&p_slot->work);
-       case STATIC_STATE:
-               p_slot->state = POWEROFF_STATE;
+       case ON_STATE:
                mutex_unlock(&p_slot->lock);
-               mutex_lock(&p_slot->hotplug_lock);
-               retval = pciehp_disable_slot(p_slot);
-               mutex_unlock(&p_slot->hotplug_lock);
-               mutex_lock(&p_slot->lock);
-               p_slot->state = STATIC_STATE;
-               break;
+               pciehp_request(ctrl, DISABLE_SLOT);
+               wait_event(ctrl->requester,
+                          !atomic_read(&ctrl->pending_events));
+               return ctrl->request_result;
        case POWEROFF_STATE:
                ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
                          slot_name(p_slot));
                break;
        case BLINKINGON_STATE:
+       case OFF_STATE:
        case POWERON_STATE:
                ctrl_info(ctrl, "Slot(%s): Already disabled\n",
                          slot_name(p_slot));
@@ -498,5 +431,5 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
        }
        mutex_unlock(&p_slot->lock);
 
-       return retval;
+       return -ENODEV;
 }
index 718b607..7136e34 100644 (file)
@@ -17,8 +17,9 @@
 #include <linux/types.h>
 #include <linux/signal.h>
 #include <linux/jiffies.h>
-#include <linux/timer.h>
+#include <linux/kthread.h>
 #include <linux/pci.h>
+#include <linux/pm_runtime.h>
 #include <linux/interrupt.h>
 #include <linux/time.h>
 #include <linux/slab.h>
@@ -31,47 +32,24 @@ static inline struct pci_dev *ctrl_dev(struct controller *ctrl)
        return ctrl->pcie->port;
 }
 
-static irqreturn_t pcie_isr(int irq, void *dev_id);
-static void start_int_poll_timer(struct controller *ctrl, int sec);
-
-/* This is the interrupt polling timeout function. */
-static void int_poll_timeout(struct timer_list *t)
-{
-       struct controller *ctrl = from_timer(ctrl, t, poll_timer);
-
-       /* Poll for interrupt events.  regs == NULL => polling */
-       pcie_isr(0, ctrl);
-
-       if (!pciehp_poll_time)
-               pciehp_poll_time = 2; /* default polling interval is 2 sec */
-
-       start_int_poll_timer(ctrl, pciehp_poll_time);
-}
-
-/* This function starts the interrupt polling timer. */
-static void start_int_poll_timer(struct controller *ctrl, int sec)
-{
-       /* Clamp to sane value */
-       if ((sec <= 0) || (sec > 60))
-               sec = 2;
-
-       ctrl->poll_timer.expires = jiffies + sec * HZ;
-       add_timer(&ctrl->poll_timer);
-}
+static irqreturn_t pciehp_isr(int irq, void *dev_id);
+static irqreturn_t pciehp_ist(int irq, void *dev_id);
+static int pciehp_poll(void *data);
 
 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) {
-               timer_setup(&ctrl->poll_timer, int_poll_timeout, 0);
-               start_int_poll_timer(ctrl, 10);
-               return 0;
+               ctrl->poll_thread = kthread_run(&pciehp_poll, ctrl,
+                                               "pciehp_poll-%s",
+                                               slot_name(ctrl->slot));
+               return PTR_ERR_OR_ZERO(ctrl->poll_thread);
        }
 
        /* Installs the interrupt handler */
-       retval = request_irq(irq, pcie_isr, IRQF_SHARED, MY_NAME, ctrl);
+       retval = request_threaded_irq(irq, pciehp_isr, pciehp_ist,
+                                     IRQF_SHARED, MY_NAME, ctrl);
        if (retval)
                ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n",
                         irq);
@@ -81,7 +59,7 @@ static inline int pciehp_request_irq(struct controller *ctrl)
 static inline void pciehp_free_irq(struct controller *ctrl)
 {
        if (pciehp_poll_mode)
-               del_timer_sync(&ctrl->poll_timer);
+               kthread_stop(ctrl->poll_thread);
        else
                free_irq(ctrl->pcie->irq, ctrl);
 }
@@ -293,6 +271,11 @@ int pciehp_check_link_status(struct controller *ctrl)
        found = pci_bus_check_dev(ctrl->pcie->port->subordinate,
                                        PCI_DEVFN(0, 0));
 
+       /* ignore link or presence changes up to this point */
+       if (found)
+               atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC),
+                          &ctrl->pending_events);
+
        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) ||
@@ -339,7 +322,9 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,
        struct pci_dev *pdev = ctrl_dev(slot->ctrl);
        u16 slot_ctrl;
 
+       pci_config_pm_runtime_get(pdev);
        pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
+       pci_config_pm_runtime_put(pdev);
        *status = (slot_ctrl & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6;
        return 0;
 }
@@ -350,7 +335,9 @@ void pciehp_get_attention_status(struct slot *slot, u8 *status)
        struct pci_dev *pdev = ctrl_dev(ctrl);
        u16 slot_ctrl;
 
+       pci_config_pm_runtime_get(pdev);
        pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
+       pci_config_pm_runtime_put(pdev);
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl);
 
@@ -425,9 +412,12 @@ int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
 {
        struct slot *slot = hotplug_slot->private;
        struct controller *ctrl = slot->ctrl;
+       struct pci_dev *pdev = ctrl_dev(ctrl);
 
+       pci_config_pm_runtime_get(pdev);
        pcie_write_cmd_nowait(ctrl, status << 6,
                              PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC);
+       pci_config_pm_runtime_put(pdev);
        return 0;
 }
 
@@ -539,20 +529,35 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
 {
        struct controller *ctrl = (struct controller *)dev_id;
        struct pci_dev *pdev = ctrl_dev(ctrl);
-       struct pci_bus *subordinate = pdev->subordinate;
-       struct pci_dev *dev;
-       struct slot *slot = ctrl->slot;
+       struct device *parent = pdev->dev.parent;
        u16 status, events;
-       u8 present;
-       bool link;
 
-       /* Interrupts cannot originate from a controller that's asleep */
+       /*
+        * Interrupts only occur in D3hot or shallower (PCIe r4.0, sec 6.7.3.4).
+        */
        if (pdev->current_state == PCI_D3cold)
                return IRQ_NONE;
 
+       /*
+        * Keep the port accessible by holding a runtime PM ref on its parent.
+        * Defer resume of the parent to the IRQ thread if it's suspended.
+        * Mask the interrupt until then.
+        */
+       if (parent) {
+               pm_runtime_get_noresume(parent);
+               if (!pm_runtime_active(parent)) {
+                       pm_runtime_put(parent);
+                       disable_irq_nosync(irq);
+                       atomic_or(RERUN_ISR, &ctrl->pending_events);
+                       return IRQ_WAKE_THREAD;
+               }
+       }
+
        pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &status);
        if (status == (u16) ~0) {
                ctrl_info(ctrl, "%s: no response from device\n", __func__);
+               if (parent)
+                       pm_runtime_put(parent);
                return IRQ_NONE;
        }
 
@@ -571,86 +576,119 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
        if (ctrl->power_fault_detected)
                events &= ~PCI_EXP_SLTSTA_PFD;
 
-       if (!events)
+       if (!events) {
+               if (parent)
+                       pm_runtime_put(parent);
                return IRQ_NONE;
-
-       /* Capture link status before clearing interrupts */
-       if (events & PCI_EXP_SLTSTA_DLLSC)
-               link = pciehp_check_link_active(ctrl);
+       }
 
        pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events);
        ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", events);
+       if (parent)
+               pm_runtime_put(parent);
 
-       /* Check Command Complete Interrupt Pending */
+       /*
+        * Command Completed notifications are not deferred to the
+        * IRQ thread because it may be waiting for their arrival.
+        */
        if (events & PCI_EXP_SLTSTA_CC) {
                ctrl->cmd_busy = 0;
                smp_mb();
                wake_up(&ctrl->queue);
+
+               if (events == PCI_EXP_SLTSTA_CC)
+                       return IRQ_HANDLED;
+
+               events &= ~PCI_EXP_SLTSTA_CC;
+       }
+
+       if (pdev->ignore_hotplug) {
+               ctrl_dbg(ctrl, "ignoring hotplug event %#06x\n", events);
+               return IRQ_HANDLED;
        }
 
-       if (subordinate) {
-               list_for_each_entry(dev, &subordinate->devices, bus_list) {
-                       if (dev->ignore_hotplug) {
-                               ctrl_dbg(ctrl, "ignoring hotplug event %#06x (%s requested no hotplug)\n",
-                                        events, pci_name(dev));
-                               return IRQ_HANDLED;
-                       }
+       /* Save pending events for consumption by IRQ thread. */
+       atomic_or(events, &ctrl->pending_events);
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t pciehp_ist(int irq, void *dev_id)
+{
+       struct controller *ctrl = (struct controller *)dev_id;
+       struct pci_dev *pdev = ctrl_dev(ctrl);
+       struct slot *slot = ctrl->slot;
+       irqreturn_t ret;
+       u32 events;
+
+       pci_config_pm_runtime_get(pdev);
+
+       /* rerun pciehp_isr() if the port was inaccessible on interrupt */
+       if (atomic_fetch_and(~RERUN_ISR, &ctrl->pending_events) & RERUN_ISR) {
+               ret = pciehp_isr(irq, dev_id);
+               enable_irq(irq);
+               if (ret != IRQ_WAKE_THREAD) {
+                       pci_config_pm_runtime_put(pdev);
+                       return ret;
                }
        }
 
+       synchronize_hardirq(irq);
+       events = atomic_xchg(&ctrl->pending_events, 0);
+       if (!events) {
+               pci_config_pm_runtime_put(pdev);
+               return IRQ_NONE;
+       }
+
        /* Check Attention Button Pressed */
        if (events & PCI_EXP_SLTSTA_ABP) {
                ctrl_info(ctrl, "Slot(%s): Attention button pressed\n",
                          slot_name(slot));
-               pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS);
+               pciehp_handle_button_press(slot);
        }
 
        /*
-        * Check Link Status Changed at higher precedence than Presence
-        * Detect Changed.  The PDS value may be set to "card present" from
-        * out-of-band detection, which may be in conflict with a Link Down
-        * and cause the wrong event to queue.
+        * Disable requests have higher priority than Presence Detect Changed
+        * or Data Link Layer State Changed events.
         */
-       if (events & PCI_EXP_SLTSTA_DLLSC) {
-               ctrl_info(ctrl, "Slot(%s): Link %s\n", slot_name(slot),
-                         link ? "Up" : "Down");
-               pciehp_queue_interrupt_event(slot, link ? INT_LINK_UP :
-                                            INT_LINK_DOWN);
-       } else if (events & PCI_EXP_SLTSTA_PDC) {
-               present = !!(status & PCI_EXP_SLTSTA_PDS);
-               ctrl_info(ctrl, "Slot(%s): Card %spresent\n", slot_name(slot),
-                         present ? "" : "not ");
-               pciehp_queue_interrupt_event(slot, present ? INT_PRESENCE_ON :
-                                            INT_PRESENCE_OFF);
-       }
+       down_read(&ctrl->reset_lock);
+       if (events & DISABLE_SLOT)
+               pciehp_handle_disable_request(slot);
+       else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC))
+               pciehp_handle_presence_or_link_change(slot, events);
+       up_read(&ctrl->reset_lock);
 
        /* Check Power Fault Detected */
        if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
                ctrl->power_fault_detected = 1;
                ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(slot));
-               pciehp_queue_interrupt_event(slot, INT_POWER_FAULT);
+               pciehp_set_attention_status(slot, 1);
+               pciehp_green_led_off(slot);
        }
 
+       pci_config_pm_runtime_put(pdev);
+       wake_up(&ctrl->requester);
        return IRQ_HANDLED;
 }
 
-static irqreturn_t pcie_isr(int irq, void *dev_id)
+static int pciehp_poll(void *data)
 {
-       irqreturn_t rc, handled = IRQ_NONE;
+       struct controller *ctrl = data;
 
-       /*
-        * 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.
-        */
-       do {
-               rc = pciehp_isr(irq, dev_id);
-               if (rc == IRQ_HANDLED)
-                       handled = IRQ_HANDLED;
-       } while (rc == IRQ_HANDLED);
+       schedule_timeout_idle(10 * HZ); /* start with 10 sec delay */
+
+       while (!kthread_should_stop()) {
+               /* poll for interrupt events or user requests */
+               while (pciehp_isr(IRQ_NOTCONNECTED, ctrl) == IRQ_WAKE_THREAD ||
+                      atomic_read(&ctrl->pending_events))
+                       pciehp_ist(IRQ_NOTCONNECTED, ctrl);
 
-       /* Return IRQ_HANDLED if we handled one or more events */
-       return handled;
+               if (pciehp_poll_time <= 0 || pciehp_poll_time > 60)
+                       pciehp_poll_time = 2; /* clamp to sane value */
+
+               schedule_timeout_idle(pciehp_poll_time * HZ);
+       }
+
+       return 0;
 }
 
 static void pcie_enable_notification(struct controller *ctrl)
@@ -691,17 +729,6 @@ static void pcie_enable_notification(struct controller *ctrl)
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd);
 }
 
-void pcie_reenable_notification(struct controller *ctrl)
-{
-       /*
-        * Clear both Presence and Data Link Layer Changed to make sure
-        * those events still fire after we have re-enabled them.
-        */
-       pcie_capability_write_word(ctrl->pcie->port, PCI_EXP_SLTSTA,
-                                  PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
-       pcie_enable_notification(ctrl);
-}
-
 static void pcie_disable_notification(struct controller *ctrl)
 {
        u16 mask;
@@ -715,6 +742,12 @@ static void pcie_disable_notification(struct controller *ctrl)
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
 }
 
+void pcie_clear_hotplug_events(struct controller *ctrl)
+{
+       pcie_capability_write_word(ctrl_dev(ctrl), PCI_EXP_SLTSTA,
+                                  PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
+}
+
 /*
  * 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
@@ -728,10 +761,13 @@ int pciehp_reset_slot(struct slot *slot, int probe)
        struct controller *ctrl = slot->ctrl;
        struct pci_dev *pdev = ctrl_dev(ctrl);
        u16 stat_mask = 0, ctrl_mask = 0;
+       int rc;
 
        if (probe)
                return 0;
 
+       down_write(&ctrl->reset_lock);
+
        if (!ATTN_BUTTN(ctrl)) {
                ctrl_mask |= PCI_EXP_SLTCTL_PDCE;
                stat_mask |= PCI_EXP_SLTSTA_PDC;
@@ -742,18 +778,16 @@ int pciehp_reset_slot(struct slot *slot, int probe)
        pcie_write_cmd(ctrl, 0, ctrl_mask);
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
-       if (pciehp_poll_mode)
-               del_timer_sync(&ctrl->poll_timer);
 
-       pci_reset_bridge_secondary_bus(ctrl->pcie->port);
+       rc = pci_bridge_secondary_bus_reset(ctrl->pcie->port);
 
        pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
        pcie_write_cmd_nowait(ctrl, ctrl_mask, ctrl_mask);
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask);
-       if (pciehp_poll_mode)
-               int_poll_timeout(&ctrl->poll_timer);
-       return 0;
+
+       up_write(&ctrl->reset_lock);
+       return rc;
 }
 
 int pcie_init_notification(struct controller *ctrl)
@@ -765,7 +799,7 @@ int pcie_init_notification(struct controller *ctrl)
        return 0;
 }
 
-static void pcie_shutdown_notification(struct controller *ctrl)
+void pcie_shutdown_notification(struct controller *ctrl)
 {
        if (ctrl->notification_enabled) {
                pcie_disable_notification(ctrl);
@@ -776,32 +810,29 @@ static void pcie_shutdown_notification(struct controller *ctrl)
 
 static int pcie_init_slot(struct controller *ctrl)
 {
+       struct pci_bus *subordinate = ctrl_dev(ctrl)->subordinate;
        struct slot *slot;
 
        slot = kzalloc(sizeof(*slot), GFP_KERNEL);
        if (!slot)
                return -ENOMEM;
 
-       slot->wq = alloc_ordered_workqueue("pciehp-%u", 0, PSN(ctrl));
-       if (!slot->wq)
-               goto abort;
+       down_read(&pci_bus_sem);
+       slot->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE;
+       up_read(&pci_bus_sem);
 
        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;
-abort:
-       kfree(slot);
-       return -ENOMEM;
 }
 
 static void pcie_cleanup_slot(struct controller *ctrl)
 {
        struct slot *slot = ctrl->slot;
-       cancel_delayed_work(&slot->work);
-       destroy_workqueue(slot->wq);
+
+       cancel_delayed_work_sync(&slot->work);
        kfree(slot);
 }
 
@@ -826,6 +857,7 @@ struct controller *pcie_init(struct pcie_device *dev)
 {
        struct controller *ctrl;
        u32 slot_cap, link_cap;
+       u8 occupied, poweron;
        struct pci_dev *pdev = dev->port;
 
        ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
@@ -847,6 +879,8 @@ struct controller *pcie_init(struct pcie_device *dev)
 
        ctrl->slot_cap = slot_cap;
        mutex_init(&ctrl->ctrl_lock);
+       init_rwsem(&ctrl->reset_lock);
+       init_waitqueue_head(&ctrl->requester);
        init_waitqueue_head(&ctrl->queue);
        dbg_ctrl(ctrl);
 
@@ -855,16 +889,11 @@ struct controller *pcie_init(struct pcie_device *dev)
        if (link_cap & PCI_EXP_LNKCAP_DLLLARC)
                ctrl->link_active_reporting = 1;
 
-       /*
-        * Clear all remaining event bits in Slot Status register except
-        * Presence Detect Changed. We want to make sure possible
-        * hotplug event is triggered when the interrupt is unmasked so
-        * that we don't lose that event.
-        */
+       /* 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_CC |
-               PCI_EXP_SLTSTA_DLLSC);
+               PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC);
 
        ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c LLActRep%c%s\n",
                (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
@@ -883,6 +912,19 @@ struct controller *pcie_init(struct pcie_device *dev)
        if (pcie_init_slot(ctrl))
                goto abort_ctrl;
 
+       /*
+        * If empty slot's power status is on, turn power off.  The IRQ isn't
+        * requested yet, so avoid triggering a notification with this command.
+        */
+       if (POWER_CTRL(ctrl)) {
+               pciehp_get_adapter_status(ctrl->slot, &occupied);
+               pciehp_get_power_status(ctrl->slot, &poweron);
+               if (!occupied && poweron) {
+                       pcie_disable_notification(ctrl);
+                       pciehp_power_off_slot(ctrl->slot);
+               }
+       }
+
        return ctrl;
 
 abort_ctrl:
@@ -893,7 +935,6 @@ abort:
 
 void pciehp_release_ctrl(struct controller *ctrl)
 {
-       pcie_shutdown_notification(ctrl);
        pcie_cleanup_slot(ctrl);
        kfree(ctrl);
 }
index 3f518de..5c58c22 100644 (file)
@@ -62,9 +62,8 @@ int pciehp_configure_device(struct slot *p_slot)
        return ret;
 }
 
-int pciehp_unconfigure_device(struct slot *p_slot)
+void pciehp_unconfigure_device(struct slot *p_slot)
 {
-       int rc = 0;
        u8 presence = 0;
        struct pci_dev *dev, *temp;
        struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate;
@@ -107,5 +106,4 @@ int pciehp_unconfigure_device(struct slot *p_slot)
        }
 
        pci_unlock_rescan_remove();
-       return rc;
 }
diff --git a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c
deleted file mode 100644 (file)
index c19694a..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * PCI Hot Plug Controller Skeleton Driver - 0.3
- *
- * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
- * Copyright (C) 2001,2003 IBM Corp.
- *
- * All rights reserved.
- *
- * This driver is to be used as a skeleton driver to show how to interface
- * with the pci hotplug core easily.
- *
- * Send feedback to <greg@kroah.com>
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <linux/pci_hotplug.h>
-#include <linux/init.h>
-
-#define SLOT_NAME_SIZE 10
-struct slot {
-       u8 number;
-       struct hotplug_slot *hotplug_slot;
-       struct list_head slot_list;
-       char name[SLOT_NAME_SIZE];
-};
-
-static LIST_HEAD(slot_list);
-
-#define MY_NAME        "pcihp_skeleton"
-
-#define dbg(format, arg...)                                    \
-       do {                                                    \
-               if (debug)                                      \
-                       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 bool debug;
-static int num_slots;
-
-#define DRIVER_VERSION "0.3"
-#define DRIVER_AUTHOR  "Greg Kroah-Hartman <greg@kroah.com>"
-#define DRIVER_DESC    "Hot Plug PCI Controller Skeleton Driver"
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-module_param(debug, bool, 0644);
-MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
-
-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 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 struct hotplug_slot_ops skel_hotplug_slot_ops = {
-       .enable_slot =          enable_slot,
-       .disable_slot =         disable_slot,
-       .set_attention_status = set_attention_status,
-       .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,
-};
-
-static int enable_slot(struct hotplug_slot *hotplug_slot)
-{
-       struct slot *slot = hotplug_slot->private;
-       int retval = 0;
-
-       dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
-
-       /*
-        * Fill in code here to enable the specified slot
-        */
-
-       return retval;
-}
-
-static int disable_slot(struct hotplug_slot *hotplug_slot)
-{
-       struct slot *slot = hotplug_slot->private;
-       int retval = 0;
-
-       dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
-
-       /*
-        * Fill in code here to disable the specified slot
-        */
-
-       return retval;
-}
-
-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", __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;
-       }
-
-       return retval;
-}
-
-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", __func__, hotplug_slot->name);
-
-       switch (value) {
-       case 0:
-               /* Specify a test here */
-               break;
-       case 1:
-               /* Specify another test here */
-               break;
-       }
-
-       return retval;
-}
-
-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", __func__, hotplug_slot->name);
-
-       /*
-        * Fill in logic to get the current power status of the specific
-        * slot and store it in the *value location.
-        */
-
-       return retval;
-}
-
-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", __func__, hotplug_slot->name);
-
-       /*
-        * Fill in logic to get the current attention status of the specific
-        * slot and store it in the *value location.
-        */
-
-       return retval;
-}
-
-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", __func__, hotplug_slot->name);
-
-       /*
-        * Fill in logic to get the current latch status of the specific
-        * slot and store it in the *value location.
-        */
-
-       return retval;
-}
-
-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", __func__, hotplug_slot->name);
-
-       /*
-        * Fill in logic to get the current adapter status of the specific
-        * slot and store it in the *value location.
-        */
-
-       return retval;
-}
-
-static void release_slot(struct hotplug_slot *hotplug_slot)
-{
-       struct slot *slot = hotplug_slot->private;
-
-       dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
-       kfree(slot->hotplug_slot->info);
-       kfree(slot->hotplug_slot);
-       kfree(slot);
-}
-
-static void make_slot_name(struct slot *slot)
-{
-       /*
-        * Stupid way to make a filename out of the slot name.
-        * replace this if your hardware provides a better way to name slots.
-        */
-       snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", slot->number);
-}
-
-/**
- * init_slots - initialize 'struct slot' structures for each slot
- *
- */
-static int __init init_slots(void)
-{
-       struct slot *slot;
-       struct hotplug_slot *hotplug_slot;
-       struct hotplug_slot_info *info;
-       int retval;
-       int i;
-
-       /*
-        * Create a structure for each slot, and register that slot
-        * with the pci_hotplug subsystem.
-        */
-       for (i = 0; i < num_slots; ++i) {
-               slot = kzalloc(sizeof(*slot), GFP_KERNEL);
-               if (!slot) {
-                       retval = -ENOMEM;
-                       goto error;
-               }
-
-               hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
-               if (!hotplug_slot) {
-                       retval = -ENOMEM;
-                       goto error_slot;
-               }
-               slot->hotplug_slot = hotplug_slot;
-
-               info = kzalloc(sizeof(*info), GFP_KERNEL);
-               if (!info) {
-                       retval = -ENOMEM;
-                       goto error_hpslot;
-               }
-               hotplug_slot->info = info;
-
-               slot->number = i;
-
-               hotplug_slot->name = slot->name;
-               hotplug_slot->private = slot;
-               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.
-                */
-               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 slot %d\n", i);
-               retval = pci_hp_register(slot->hotplug_slot);
-               if (retval) {
-                       err("pci_hp_register failed with error %d\n", retval);
-                       goto error_info;
-               }
-
-               /* add slot to our internal list */
-               list_add(&slot->slot_list, &slot_list);
-       }
-
-       return 0;
-error_info:
-       kfree(info);
-error_hpslot:
-       kfree(hotplug_slot);
-error_slot:
-       kfree(slot);
-error:
-       return retval;
-}
-
-static void __exit cleanup_slots(void)
-{
-       struct slot *slot, *next;
-
-       /*
-        * Unregister all of our slots with the pci_hotplug subsystem.
-        * Memory will be freed in release_slot() callback after slot's
-        * lifespan is finished.
-        */
-       list_for_each_entry_safe(slot, next, &slot_list, slot_list) {
-               list_del(&slot->slot_list);
-               pci_hp_deregister(slot->hotplug_slot);
-       }
-}
-
-static int __init pcihp_skel_init(void)
-{
-       int retval;
-
-       info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
-       /*
-        * Do specific initialization stuff for your driver here
-        * like initializing your controller hardware (if any) and
-        * determining the number of slots you have in the system
-        * right now.
-        */
-       num_slots = 5;
-
-       return init_slots();
-}
-
-static void __exit pcihp_skel_exit(void)
-{
-       /*
-        * Clean everything up.
-        */
-       cleanup_slots();
-}
-
-module_init(pcihp_skel_init);
-module_exit(pcihp_skel_exit);
index 6c2e8d7..3276a5e 100644 (file)
@@ -538,9 +538,8 @@ static struct hotplug_slot_ops php_slot_ops = {
        .disable_slot           = pnv_php_disable_slot,
 };
 
-static void pnv_php_release(struct hotplug_slot *slot)
+static void pnv_php_release(struct pnv_php_slot *php_slot)
 {
-       struct pnv_php_slot *php_slot = slot->private;
        unsigned long flags;
 
        /* Remove from global or child list */
@@ -596,7 +595,6 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn)
        php_slot->power_state_check     = false;
        php_slot->slot.ops              = &php_slot_ops;
        php_slot->slot.info             = &php_slot->slot_info;
-       php_slot->slot.release          = pnv_php_release;
        php_slot->slot.private          = php_slot;
 
        INIT_LIST_HEAD(&php_slot->children);
@@ -924,6 +922,7 @@ static void pnv_php_unregister_one(struct device_node *dn)
 
        php_slot->state = PNV_PHP_STATE_OFFLINE;
        pci_hp_deregister(&php_slot->slot);
+       pnv_php_release(php_slot);
        pnv_php_put_slot(php_slot);
 }
 
index fb5e084..857c358 100644 (file)
@@ -404,13 +404,13 @@ 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.
         */
 
        list_for_each_entry_safe(slot, next, &rpaphp_slot_head,
                                 rpaphp_slot_list) {
                list_del(&slot->rpaphp_slot_list);
                pci_hp_deregister(slot->hotplug_slot);
+               dealloc_slot_struct(slot);
        }
        return;
 }
index 3840a20..b916c8e 100644 (file)
 #include "rpaphp.h"
 
 /* free up the memory used by a slot */
-static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
-{
-       struct slot *slot = (struct slot *) hotplug_slot->private;
-       dealloc_slot_struct(slot);
-}
-
 void dealloc_slot_struct(struct slot *slot)
 {
        kfree(slot->hotplug_slot->info);
@@ -56,7 +50,6 @@ struct slot *alloc_slot_struct(struct device_node *dn,
        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);
 
@@ -90,10 +83,8 @@ int rpaphp_deregister_slot(struct slot *slot)
                __func__, slot->name);
 
        list_del(&slot->rpaphp_slot_list);
-
-       retval = pci_hp_deregister(php_slot);
-       if (retval)
-               err("Problem unregistering a slot %s\n", slot->name);
+       pci_hp_deregister(php_slot);
+       dealloc_slot_struct(slot);
 
        dbg("%s - Exit: rc[%d]\n", __func__, retval);
        return retval;
index ffdc297..93b5341 100644 (file)
@@ -130,15 +130,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
        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,
@@ -175,7 +166,6 @@ int zpci_init_slot(struct zpci_dev *zdev)
        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);
@@ -209,5 +199,8 @@ void zpci_exit_slot(struct zpci_dev *zdev)
                        continue;
                list_del(&slot->slot_list);
                pci_hp_deregister(slot->hotplug_slot);
+               kfree(slot->hotplug_slot->info);
+               kfree(slot->hotplug_slot);
+               kfree(slot);
        }
 }
index 78b6bdb..babd234 100644 (file)
@@ -628,7 +628,6 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
                        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, pci_bus, device, name);
                if (rc)
@@ -656,8 +655,10 @@ alloc_err:
                sn_release_slot(bss_hotplug_slot);
 
        /* destroy anything else on the list */
-       while ((bss_hotplug_slot = sn_hp_destroy()))
+       while ((bss_hotplug_slot = sn_hp_destroy())) {
                pci_hp_deregister(bss_hotplug_slot);
+               sn_release_slot(bss_hotplug_slot);
+       }
 
        return rc;
 }
@@ -703,8 +704,10 @@ static void __exit sn_pci_hotplug_exit(void)
 {
        struct hotplug_slot *bss_hotplug_slot;
 
-       while ((bss_hotplug_slot = sn_hp_destroy()))
+       while ((bss_hotplug_slot = sn_hp_destroy())) {
                pci_hp_deregister(bss_hotplug_slot);
+               sn_release_slot(bss_hotplug_slot);
+       }
 
        if (!list_empty(&sn_hp_list))
                printk(KERN_ERR "%s: internal list is not empty\n", __FILE__);
index e91be28..97cee23 100644 (file)
@@ -61,22 +61,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
        .get_adapter_status =   get_adapter_status,
 };
 
-/**
- * release_slot - free up the memory used by a slot
- * @hotplug_slot: slot to free
- */
-static void release_slot(struct hotplug_slot *hotplug_slot)
-{
-       struct slot *slot = hotplug_slot->private;
-
-       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 int init_slots(struct controller *ctrl)
 {
        struct slot *slot;
@@ -125,7 +109,6 @@ static int init_slots(struct controller *ctrl)
 
                /* register this slot with the hotplug pci core */
                hotplug_slot->private = slot;
-               hotplug_slot->release = &release_slot;
                snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
                hotplug_slot->ops = &shpchp_hotplug_slot_ops;
 
@@ -171,6 +154,9 @@ void cleanup_slots(struct controller *ctrl)
                cancel_delayed_work(&slot->work);
                destroy_workqueue(slot->wq);
                pci_hp_deregister(slot->hotplug_slot);
+               kfree(slot->hotplug_slot->info);
+               kfree(slot->hotplug_slot);
+               kfree(slot);
        }
 }
 
@@ -270,11 +256,30 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
        return 0;
 }
 
+static bool shpc_capable(struct pci_dev *bridge)
+{
+       /*
+        * It is assumed that AMD GOLAM chips support SHPC but they do not
+        * have SHPC capability.
+        */
+       if (bridge->vendor == PCI_VENDOR_ID_AMD &&
+           bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450)
+               return true;
+
+       if (pci_find_capability(bridge, PCI_CAP_ID_SHPC))
+               return true;
+
+       return false;
+}
+
 static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        int rc;
        struct controller *ctrl;
 
+       if (!shpc_capable(pdev))
+               return -ENODEV;
+
        if (acpi_get_hp_hw_control_from_firmware(pdev))
                return -ENODEV;
 
@@ -303,6 +308,7 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (rc)
                goto err_cleanup_slots;
 
+       pdev->shpc_managed = 1;
        return 0;
 
 err_cleanup_slots:
@@ -319,6 +325,7 @@ static void shpc_remove(struct pci_dev *dev)
 {
        struct controller *ctrl = pci_get_drvdata(dev);
 
+       dev->shpc_managed = 0;
        shpchp_remove_ctrl_files(ctrl);
        ctrl->hpc_ops->release_ctlr(ctrl);
        kfree(ctrl);
index 1047b56..1267dcc 100644 (file)
@@ -654,6 +654,7 @@ int shpchp_sysfs_enable_slot(struct slot *p_slot)
        switch (p_slot->state) {
        case BLINKINGON_STATE:
                cancel_delayed_work(&p_slot->work);
+               /* fall through */
        case STATIC_STATE:
                p_slot->state = POWERON_STATE;
                mutex_unlock(&p_slot->lock);
@@ -689,6 +690,7 @@ int shpchp_sysfs_disable_slot(struct slot *p_slot)
        switch (p_slot->state) {
        case BLINKINGOFF_STATE:
                cancel_delayed_work(&p_slot->work);
+               /* fall through */
        case STATIC_STATE:
                p_slot->state = POWEROFF_STATE;
                mutex_unlock(&p_slot->lock);
index d0d73db..c5f3cd4 100644 (file)
@@ -574,6 +574,22 @@ void pci_iov_release(struct pci_dev *dev)
                sriov_release(dev);
 }
 
+/**
+ * pci_iov_remove - clean up SR-IOV state after PF driver is detached
+ * @dev: the PCI device
+ */
+void pci_iov_remove(struct pci_dev *dev)
+{
+       struct pci_sriov *iov = dev->sriov;
+
+       if (!dev->is_physfn)
+               return;
+
+       iov->driver_max_VFs = iov->total_VFs;
+       if (iov->num_VFs)
+               pci_warn(dev, "driver left SR-IOV enabled after remove\n");
+}
+
 /**
  * pci_iov_update_resource - update a VF BAR
  * @dev: the PCI device
@@ -802,15 +818,15 @@ 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;
 
+       dev->sriov->driver_max_VFs = numvfs;
        return 0;
 }
 EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
index 2a808e1..a1de501 100644 (file)
@@ -86,13 +86,17 @@ int pci_request_irq(struct pci_dev *dev, unsigned int nr, irq_handler_t handler,
        va_list ap;
        int ret;
        char *devname;
+       unsigned long irqflags = IRQF_SHARED;
+
+       if (!handler)
+               irqflags |= IRQF_ONESHOT;
 
        va_start(ap, fmt);
        devname = kvasprintf(GFP_KERNEL, fmt, ap);
        va_end(ap);
 
        ret = request_threaded_irq(pci_irq_vector(dev, nr), handler, thread_fn,
-                       IRQF_SHARED, devname, dev_id);
+                                  irqflags, devname, dev_id);
        if (ret)
                kfree(devname);
        return ret;
index 4d88afd..f2ef896 100644 (file)
@@ -1446,6 +1446,9 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
        if (IS_ENABLED(CONFIG_GENERIC_IRQ_RESERVATION_MODE))
                info->flags |= MSI_FLAG_MUST_REACTIVATE;
 
+       /* PCI-MSI is oneshot-safe */
+       info->chip->flags |= IRQCHIP_ONESHOT_SAFE;
+
        domain = msi_create_irq_domain(fwnode, info, parent);
        if (!domain)
                return NULL;
index d088c91..1836b8d 100644 (file)
@@ -266,7 +266,7 @@ int devm_of_pci_get_host_bridge_resources(struct device *dev,
                        struct list_head *resources, resource_size_t *io_base)
 {
        struct device_node *dev_node = dev->of_node;
-       struct resource *res;
+       struct resource *res, tmp_res;
        struct resource *bus_range;
        struct of_pci_range range;
        struct of_pci_range_parser parser;
@@ -320,18 +320,16 @@ int devm_of_pci_get_host_bridge_resources(struct device *dev,
                if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
                        continue;
 
-               res = devm_kzalloc(dev, sizeof(struct resource), GFP_KERNEL);
+               err = of_pci_range_to_resource(&range, dev_node, &tmp_res);
+               if (err)
+                       continue;
+
+               res = devm_kmemdup(dev, &tmp_res, sizeof(tmp_res), GFP_KERNEL);
                if (!res) {
                        err = -ENOMEM;
                        goto failed;
                }
 
-               err = of_pci_range_to_resource(&range, dev_node, res);
-               if (err) {
-                       devm_kfree(dev, res);
-                       continue;
-               }
-
                if (resource_type(res) == IORESOURCE_IO) {
                        if (!io_base) {
                                dev_err(dev, "I/O range found for %pOF. Please provide an io_base pointer to save CPU base address\n",
@@ -612,7 +610,7 @@ int pci_parse_request_of_pci_ranges(struct device *dev,
 
                switch (resource_type(res)) {
                case IORESOURCE_IO:
-                       err = pci_remap_iospace(res, iobase);
+                       err = devm_pci_remap_iospace(dev, res, iobase);
                        if (err) {
                                dev_warn(dev, "error %d: failed to map resource %pR\n",
                                         err, res);
index 65113b6..5100fd2 100644 (file)
@@ -403,24 +403,7 @@ bool pciehp_is_native(struct pci_dev *bridge)
  */
 bool shpchp_is_native(struct pci_dev *bridge)
 {
-       const struct pci_host_bridge *host;
-
-       if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_SHPC))
-               return false;
-
-       /*
-        * It is assumed that AMD GOLAM chips support SHPC but they do not
-        * have SHPC capability.
-        */
-       if (bridge->vendor == PCI_VENDOR_ID_AMD &&
-           bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450)
-               return true;
-
-       if (!pci_find_capability(bridge, PCI_CAP_ID_SHPC))
-               return false;
-
-       host = pci_find_host_bridge(bridge->bus);
-       return host->native_shpc_hotplug;
+       return bridge->shpc_managed;
 }
 
 /**
index c125d53..bef17c3 100644 (file)
@@ -445,6 +445,7 @@ static int pci_device_remove(struct device *dev)
                }
                pcibios_free_irq(pci_dev);
                pci_dev->driver = NULL;
+               pci_iov_remove(pci_dev);
        }
 
        /* Undo the runtime PM settings in local_pci_probe() */
@@ -1667,7 +1668,7 @@ static int __init pci_driver_init(void)
        if (ret)
                return ret;
 #endif
-
+       dma_debug_add_bus(&pci_bus_type);
        return 0;
 }
 postcore_initcall(pci_driver_init);
index 0c4653c..9ecfe13 100644 (file)
@@ -23,7 +23,6 @@
 #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>
@@ -1449,7 +1448,9 @@ static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
        if (val != 1)
                return -EINVAL;
 
+       pm_runtime_get_sync(dev);
        result = pci_reset_function(pdev);
+       pm_runtime_put(dev);
        if (result < 0)
                return result;
 
@@ -1746,6 +1747,9 @@ static const struct attribute_group *pci_dev_attr_groups[] = {
 #endif
        &pci_bridge_attr_group,
        &pcie_dev_attr_group,
+#ifdef CONFIG_PCIEAER
+       &aer_stats_attr_group,
+#endif
        NULL,
 };
 
index 97acba7..80da484 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/string.h>
 #include <linux/log2.h>
 #include <linux/logic_pio.h>
-#include <linux/pci-aspm.h>
 #include <linux/pm_wakeup.h>
 #include <linux/interrupt.h>
 #include <linux/device.h>
@@ -115,6 +114,9 @@ static bool pcie_ari_disabled;
 /* If set, the PCIe ATS capability will not be used. */
 static bool pcie_ats_disabled;
 
+/* If set, the PCI config space of each device is printed during boot. */
+bool pci_early_dump;
+
 bool pci_ats_disabled(void)
 {
        return pcie_ats_disabled;
@@ -191,6 +193,168 @@ void __iomem *pci_ioremap_wc_bar(struct pci_dev *pdev, int bar)
 EXPORT_SYMBOL_GPL(pci_ioremap_wc_bar);
 #endif
 
+/**
+ * pci_dev_str_match_path - test if a path string matches a device
+ * @dev:    the PCI device to test
+ * @p:      string to match the device against
+ * @endptr: pointer to the string after the match
+ *
+ * Test if a string (typically from a kernel parameter) formatted as a
+ * path of device/function addresses matches a PCI device. The string must
+ * be of the form:
+ *
+ *   [<domain>:]<bus>:<device>.<func>[/<device>.<func>]*
+ *
+ * A path for a device can be obtained using 'lspci -t'.  Using a path
+ * is more robust against bus renumbering than using only a single bus,
+ * device and function address.
+ *
+ * Returns 1 if the string matches the device, 0 if it does not and
+ * a negative error code if it fails to parse the string.
+ */
+static int pci_dev_str_match_path(struct pci_dev *dev, const char *path,
+                                 const char **endptr)
+{
+       int ret;
+       int seg, bus, slot, func;
+       char *wpath, *p;
+       char end;
+
+       *endptr = strchrnul(path, ';');
+
+       wpath = kmemdup_nul(path, *endptr - path, GFP_KERNEL);
+       if (!wpath)
+               return -ENOMEM;
+
+       while (1) {
+               p = strrchr(wpath, '/');
+               if (!p)
+                       break;
+               ret = sscanf(p, "/%x.%x%c", &slot, &func, &end);
+               if (ret != 2) {
+                       ret = -EINVAL;
+                       goto free_and_exit;
+               }
+
+               if (dev->devfn != PCI_DEVFN(slot, func)) {
+                       ret = 0;
+                       goto free_and_exit;
+               }
+
+               /*
+                * Note: we don't need to get a reference to the upstream
+                * bridge because we hold a reference to the top level
+                * device which should hold a reference to the bridge,
+                * and so on.
+                */
+               dev = pci_upstream_bridge(dev);
+               if (!dev) {
+                       ret = 0;
+                       goto free_and_exit;
+               }
+
+               *p = 0;
+       }
+
+       ret = sscanf(wpath, "%x:%x:%x.%x%c", &seg, &bus, &slot,
+                    &func, &end);
+       if (ret != 4) {
+               seg = 0;
+               ret = sscanf(wpath, "%x:%x.%x%c", &bus, &slot, &func, &end);
+               if (ret != 3) {
+                       ret = -EINVAL;
+                       goto free_and_exit;
+               }
+       }
+
+       ret = (seg == pci_domain_nr(dev->bus) &&
+              bus == dev->bus->number &&
+              dev->devfn == PCI_DEVFN(slot, func));
+
+free_and_exit:
+       kfree(wpath);
+       return ret;
+}
+
+/**
+ * pci_dev_str_match - test if a string matches a device
+ * @dev:    the PCI device to test
+ * @p:      string to match the device against
+ * @endptr: pointer to the string after the match
+ *
+ * Test if a string (typically from a kernel parameter) matches a specified
+ * PCI device. The string may be of one of the following formats:
+ *
+ *   [<domain>:]<bus>:<device>.<func>[/<device>.<func>]*
+ *   pci:<vendor>:<device>[:<subvendor>:<subdevice>]
+ *
+ * The first format specifies a PCI bus/device/function address which
+ * may change if new hardware is inserted, if motherboard firmware changes,
+ * or due to changes caused in kernel parameters. If the domain is
+ * left unspecified, it is taken to be 0.  In order to be robust against
+ * bus renumbering issues, a path of PCI device/function numbers may be used
+ * to address the specific device.  The path for a device can be determined
+ * through the use of 'lspci -t'.
+ *
+ * The second format matches devices using IDs in the configuration
+ * space which may match multiple devices in the system. A value of 0
+ * for any field will match all devices. (Note: this differs from
+ * in-kernel code that uses PCI_ANY_ID which is ~0; this is for
+ * legacy reasons and convenience so users don't have to specify
+ * FFFFFFFFs on the command line.)
+ *
+ * Returns 1 if the string matches the device, 0 if it does not and
+ * a negative error code if the string cannot be parsed.
+ */
+static int pci_dev_str_match(struct pci_dev *dev, const char *p,
+                            const char **endptr)
+{
+       int ret;
+       int count;
+       unsigned short vendor, device, subsystem_vendor, subsystem_device;
+
+       if (strncmp(p, "pci:", 4) == 0) {
+               /* PCI vendor/device (subvendor/subdevice) IDs are specified */
+               p += 4;
+               ret = sscanf(p, "%hx:%hx:%hx:%hx%n", &vendor, &device,
+                            &subsystem_vendor, &subsystem_device, &count);
+               if (ret != 4) {
+                       ret = sscanf(p, "%hx:%hx%n", &vendor, &device, &count);
+                       if (ret != 2)
+                               return -EINVAL;
+
+                       subsystem_vendor = 0;
+                       subsystem_device = 0;
+               }
+
+               p += count;
+
+               if ((!vendor || vendor == dev->vendor) &&
+                   (!device || device == dev->device) &&
+                   (!subsystem_vendor ||
+                           subsystem_vendor == dev->subsystem_vendor) &&
+                   (!subsystem_device ||
+                           subsystem_device == dev->subsystem_device))
+                       goto found;
+       } else {
+               /*
+                * PCI Bus, Device, Function IDs are specified
+                *  (optionally, may include a path of devfns following it)
+                */
+               ret = pci_dev_str_match_path(dev, p, &p);
+               if (ret < 0)
+                       return ret;
+               else if (ret)
+                       goto found;
+       }
+
+       *endptr = p;
+       return 0;
+
+found:
+       *endptr = p;
+       return 1;
+}
 
 static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn,
                                   u8 pos, int cap, int *ttl)
@@ -1171,6 +1335,33 @@ static void pci_restore_config_space(struct pci_dev *pdev)
        }
 }
 
+static void pci_restore_rebar_state(struct pci_dev *pdev)
+{
+       unsigned int pos, nbars, i;
+       u32 ctrl;
+
+       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+       if (!pos)
+               return;
+
+       pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+       nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
+                   PCI_REBAR_CTRL_NBAR_SHIFT;
+
+       for (i = 0; i < nbars; i++, pos += 8) {
+               struct resource *res;
+               int bar_idx, size;
+
+               pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+               bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+               res = pdev->resource + bar_idx;
+               size = order_base_2((resource_size(res) >> 20) | 1) - 1;
+               ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+               ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
+               pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+       }
+}
+
 /**
  * pci_restore_state - Restore the saved state of a PCI device
  * @dev: - PCI device that we're dealing with
@@ -1186,6 +1377,7 @@ void pci_restore_state(struct pci_dev *dev)
        pci_restore_pri_state(dev);
        pci_restore_ats_state(dev);
        pci_restore_vc_state(dev);
+       pci_restore_rebar_state(dev);
 
        pci_cleanup_aer_error_status_regs(dev);
 
@@ -2045,6 +2237,7 @@ static pci_power_t pci_target_state(struct pci_dev *dev, bool wakeup)
                case PCI_D2:
                        if (pci_no_d1d2(dev))
                                break;
+                       /* else: fall through */
                default:
                        target_state = state;
                }
@@ -2290,7 +2483,7 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
  * @bridge: Bridge to check
  *
  * This function checks if it is possible to move the bridge to D3.
- * Currently we only allow D3 for recent enough PCIe ports.
+ * Currently we only allow D3 for recent enough PCIe ports and Thunderbolt.
  */
 bool pci_bridge_d3_possible(struct pci_dev *bridge)
 {
@@ -2305,18 +2498,27 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
                        return false;
 
                /*
-                * Hotplug interrupts cannot be delivered if the link is down,
-                * so parents of a hotplug port must stay awake. In addition,
-                * hotplug ports handled by firmware in System Management Mode
+                * Hotplug ports handled by firmware in System Management Mode
                 * may not be put into D3 by the OS (Thunderbolt on non-Macs).
-                * For simplicity, disallow in general for now.
                 */
-               if (bridge->is_hotplug_bridge)
+               if (bridge->is_hotplug_bridge && !pciehp_is_native(bridge))
                        return false;
 
                if (pci_bridge_d3_force)
                        return true;
 
+               /* Even the oldest 2010 Thunderbolt controller supports D3. */
+               if (bridge->is_thunderbolt)
+                       return true;
+
+               /*
+                * Hotplug ports handled natively by the OS were not validated
+                * by vendors for runtime D3 at least until 2018 because there
+                * was no OS support.
+                */
+               if (bridge->is_hotplug_bridge)
+                       return false;
+
                /*
                 * It should be safe to put PCIe ports from 2015 or newer
                 * to D3.
@@ -2820,6 +3022,66 @@ void pci_request_acs(void)
        pci_acs_enable = 1;
 }
 
+static const char *disable_acs_redir_param;
+
+/**
+ * pci_disable_acs_redir - disable ACS redirect capabilities
+ * @dev: the PCI device
+ *
+ * For only devices specified in the disable_acs_redir parameter.
+ */
+static void pci_disable_acs_redir(struct pci_dev *dev)
+{
+       int ret = 0;
+       const char *p;
+       int pos;
+       u16 ctrl;
+
+       if (!disable_acs_redir_param)
+               return;
+
+       p = disable_acs_redir_param;
+       while (*p) {
+               ret = pci_dev_str_match(dev, p, &p);
+               if (ret < 0) {
+                       pr_info_once("PCI: Can't parse disable_acs_redir parameter: %s\n",
+                                    disable_acs_redir_param);
+
+                       break;
+               } else if (ret == 1) {
+                       /* Found a match */
+                       break;
+               }
+
+               if (*p != ';' && *p != ',') {
+                       /* End of param or invalid format */
+                       break;
+               }
+               p++;
+       }
+
+       if (ret != 1)
+               return;
+
+       if (!pci_dev_specific_disable_acs_redir(dev))
+               return;
+
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
+       if (!pos) {
+               pci_warn(dev, "cannot disable ACS redirect for this hardware as it does not have ACS capabilities\n");
+               return;
+       }
+
+       pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
+
+       /* P2P Request & Completion Redirect */
+       ctrl &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC);
+
+       pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
+
+       pci_info(dev, "disabled ACS redirect\n");
+}
+
 /**
  * pci_std_enable_acs - enable ACS on devices using standard ACS capabilites
  * @dev: the PCI device
@@ -2859,12 +3121,22 @@ static void pci_std_enable_acs(struct pci_dev *dev)
 void pci_enable_acs(struct pci_dev *dev)
 {
        if (!pci_acs_enable)
-               return;
+               goto disable_acs_redir;
 
        if (!pci_dev_specific_enable_acs(dev))
-               return;
+               goto disable_acs_redir;
 
        pci_std_enable_acs(dev);
+
+disable_acs_redir:
+       /*
+        * Note: pci_disable_acs_redir() must be called even if ACS was not
+        * enabled by the kernel because it may have been enabled by
+        * platform firmware.  So if we are told to disable it, we should
+        * always disable it after setting the kernel's default
+        * preferences.
+        */
+       pci_disable_acs_redir(dev);
 }
 
 static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
@@ -3070,7 +3342,7 @@ int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
                return pos;
 
        pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
-       return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> 8;
+       return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT;
 }
 
 /**
@@ -3093,7 +3365,7 @@ int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
 
        pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
        ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
-       ctrl |= size << 8;
+       ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
        pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
        return 0;
 }
@@ -3579,6 +3851,44 @@ void pci_unmap_iospace(struct resource *res)
 }
 EXPORT_SYMBOL(pci_unmap_iospace);
 
+static void devm_pci_unmap_iospace(struct device *dev, void *ptr)
+{
+       struct resource **res = ptr;
+
+       pci_unmap_iospace(*res);
+}
+
+/**
+ * devm_pci_remap_iospace - Managed pci_remap_iospace()
+ * @dev: Generic device to remap IO address for
+ * @res: Resource describing the I/O space
+ * @phys_addr: physical address of range to be mapped
+ *
+ * Managed pci_remap_iospace().  Map is automatically unmapped on driver
+ * detach.
+ */
+int devm_pci_remap_iospace(struct device *dev, const struct resource *res,
+                          phys_addr_t phys_addr)
+{
+       const struct resource **ptr;
+       int error;
+
+       ptr = devres_alloc(devm_pci_unmap_iospace, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+
+       error = pci_remap_iospace(res, phys_addr);
+       if (error) {
+               devres_free(ptr);
+       } else  {
+               *ptr = res;
+               devres_add(dev, ptr);
+       }
+
+       return error;
+}
+EXPORT_SYMBOL(devm_pci_remap_iospace);
+
 /**
  * devm_pci_remap_cfgspace - Managed pci_remap_cfgspace()
  * @dev: Generic device to remap IO address for
@@ -4039,7 +4349,7 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
  * Returns true if the device advertises support for PCIe function level
  * resets.
  */
-static bool pcie_has_flr(struct pci_dev *dev)
+bool pcie_has_flr(struct pci_dev *dev)
 {
        u32 cap;
 
@@ -4049,6 +4359,7 @@ static bool pcie_has_flr(struct pci_dev *dev)
        pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
        return cap & PCI_EXP_DEVCAP_FLR;
 }
+EXPORT_SYMBOL_GPL(pcie_has_flr);
 
 /**
  * pcie_flr - initiate a PCIe function level reset
@@ -4224,19 +4535,18 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)
 }
 
 /**
- * pci_reset_bridge_secondary_bus - Reset the secondary bus on a PCI bridge.
+ * pci_bridge_secondary_bus_reset - 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.
  */
-int pci_reset_bridge_secondary_bus(struct pci_dev *dev)
+int pci_bridge_secondary_bus_reset(struct pci_dev *dev)
 {
        pcibios_reset_secondary_bus(dev);
 
        return pci_dev_wait(dev, "bus reset", PCIE_RESET_READY_POLL_MS);
 }
-EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus);
 
 static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
 {
@@ -4253,9 +4563,7 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
        if (probe)
                return 0;
 
-       pci_reset_bridge_secondary_bus(dev->bus->self);
-
-       return 0;
+       return pci_bridge_secondary_bus_reset(dev->bus->self);
 }
 
 static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, int probe)
@@ -4787,7 +5095,7 @@ int pci_probe_reset_slot(struct pci_slot *slot)
 EXPORT_SYMBOL_GPL(pci_probe_reset_slot);
 
 /**
- * pci_reset_slot - reset a PCI slot
+ * __pci_reset_slot - Try to reset a PCI slot
  * @slot: PCI slot to reset
  *
  * A PCI bus may host multiple slots, each slot may support a reset mechanism
@@ -4799,33 +5107,9 @@ EXPORT_SYMBOL_GPL(pci_probe_reset_slot);
  * 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)
+static int __pci_reset_slot(struct pci_slot *slot)
 {
        int rc;
 
@@ -4846,10 +5130,11 @@ int pci_try_reset_slot(struct pci_slot *slot)
 
        return rc;
 }
-EXPORT_SYMBOL_GPL(pci_try_reset_slot);
 
 static int pci_bus_reset(struct pci_bus *bus, int probe)
 {
+       int ret;
+
        if (!bus->self || !pci_bus_resetable(bus))
                return -ENOTTY;
 
@@ -4860,11 +5145,11 @@ static int pci_bus_reset(struct pci_bus *bus, int probe)
 
        might_sleep();
 
-       pci_reset_bridge_secondary_bus(bus->self);
+       ret = pci_bridge_secondary_bus_reset(bus->self);
 
        pci_bus_unlock(bus);
 
-       return 0;
+       return ret;
 }
 
 /**
@@ -4880,15 +5165,12 @@ int pci_probe_reset_bus(struct pci_bus *bus)
 EXPORT_SYMBOL_GPL(pci_probe_reset_bus);
 
 /**
- * pci_reset_bus - reset a PCI bus
+ * __pci_reset_bus - Try to 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.
+ * Same as above except return -EAGAIN if the bus cannot be locked
  */
-int pci_reset_bus(struct pci_bus *bus)
+static int __pci_reset_bus(struct pci_bus *bus)
 {
        int rc;
 
@@ -4898,42 +5180,30 @@ int pci_reset_bus(struct pci_bus *bus)
 
        pci_bus_save_and_disable(bus);
 
-       rc = pci_bus_reset(bus, 0);
+       if (pci_bus_trylock(bus)) {
+               might_sleep();
+               rc = pci_bridge_secondary_bus_reset(bus->self);
+               pci_bus_unlock(bus);
+       } else
+               rc = -EAGAIN;
 
        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
+ * pci_reset_bus - Try to reset a PCI bus
+ * @pdev: top level PCI device to reset via slot/bus
  *
  * Same as above except return -EAGAIN if the bus cannot be locked
  */
-int pci_try_reset_bus(struct pci_bus *bus)
+int pci_reset_bus(struct pci_dev *pdev)
 {
-       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;
+       return pci_probe_reset_slot(pdev->slot) ?
+           __pci_reset_slot(pdev->slot) : __pci_reset_bus(pdev->bus);
 }
-EXPORT_SYMBOL_GPL(pci_try_reset_bus);
+EXPORT_SYMBOL_GPL(pci_reset_bus);
 
 /**
  * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
@@ -5264,14 +5534,16 @@ u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
 }
 
 /**
- * pcie_print_link_status - Report the PCI device's link speed and width
+ * __pcie_print_link_status - Report the PCI device's link speed and width
  * @dev: PCI device to query
+ * @verbose: Print info even when enough bandwidth is available
  *
- * Report the available bandwidth at the device.  If this is less than the
- * device is capable of, report the device's maximum possible bandwidth and
- * the upstream link that limits its performance to less than that.
+ * If the available bandwidth at the device is less than the device is
+ * capable of, report the device's maximum possible bandwidth and the
+ * upstream link that limits its performance.  If @verbose, always print
+ * the available bandwidth, even if the device isn't constrained.
  */
-void pcie_print_link_status(struct pci_dev *dev)
+void __pcie_print_link_status(struct pci_dev *dev, bool verbose)
 {
        enum pcie_link_width width, width_cap;
        enum pci_bus_speed speed, speed_cap;
@@ -5281,11 +5553,11 @@ void pcie_print_link_status(struct pci_dev *dev)
        bw_cap = pcie_bandwidth_capable(dev, &speed_cap, &width_cap);
        bw_avail = pcie_bandwidth_available(dev, &limiting_dev, &speed, &width);
 
-       if (bw_avail >= bw_cap)
+       if (bw_avail >= bw_cap && verbose)
                pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth (%s x%d link)\n",
                         bw_cap / 1000, bw_cap % 1000,
                         PCIE_SPEED2STR(speed_cap), width_cap);
-       else
+       else if (bw_avail < bw_cap)
                pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)\n",
                         bw_avail / 1000, bw_avail % 1000,
                         PCIE_SPEED2STR(speed), width,
@@ -5293,6 +5565,17 @@ void pcie_print_link_status(struct pci_dev *dev)
                         bw_cap / 1000, bw_cap % 1000,
                         PCIE_SPEED2STR(speed_cap), width_cap);
 }
+
+/**
+ * pcie_print_link_status - Report the PCI device's link speed and width
+ * @dev: PCI device to query
+ *
+ * Report the available bandwidth at the device.
+ */
+void pcie_print_link_status(struct pci_dev *dev)
+{
+       __pcie_print_link_status(dev, true);
+}
 EXPORT_SYMBOL(pcie_print_link_status);
 
 /**
@@ -5387,8 +5670,19 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode,
  * @dev: the PCI device for which alias is added
  * @devfn: alias slot and function
  *
- * This helper encodes 8-bit devfn as bit number in dma_alias_mask.
- * It should be called early, preferably as PCI fixup header quirk.
+ * This helper encodes an 8-bit devfn as a bit number in dma_alias_mask
+ * which is used to program permissible bus-devfn source addresses for DMA
+ * requests in an IOMMU.  These aliases factor into IOMMU group creation
+ * and are useful for devices generating DMA requests beyond or different
+ * from their logical bus-devfn.  Examples include device quirks where the
+ * device simply uses the wrong devfn, as well as non-transparent bridges
+ * where the alias may be a proxy for devices in another domain.
+ *
+ * IOMMU group creation is performed during device discovery or addition,
+ * prior to any potential DMA mapping and therefore prior to driver probing
+ * (especially for userspace assigned devices where IOMMU group definition
+ * cannot be left as a userspace activity).  DMA aliases should therefore
+ * be configured via quirks, such as the PCI fixup header quirk.
  */
 void pci_add_dma_alias(struct pci_dev *dev, u8 devfn)
 {
@@ -5454,10 +5748,10 @@ static DEFINE_SPINLOCK(resource_alignment_lock);
 static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev,
                                                        bool *resize)
 {
-       int seg, bus, slot, func, align_order, count;
-       unsigned short vendor, device, subsystem_vendor, subsystem_device;
+       int align_order, count;
        resource_size_t align = pcibios_default_alignment();
-       char *p;
+       const char *p;
+       int ret;
 
        spin_lock(&resource_alignment_lock);
        p = resource_alignment_param;
@@ -5477,58 +5771,21 @@ static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev,
                } else {
                        align_order = -1;
                }
-               if (strncmp(p, "pci:", 4) == 0) {
-                       /* PCI vendor/device (subvendor/subdevice) ids are specified */
-                       p += 4;
-                       if (sscanf(p, "%hx:%hx:%hx:%hx%n",
-                               &vendor, &device, &subsystem_vendor, &subsystem_device, &count) != 4) {
-                               if (sscanf(p, "%hx:%hx%n", &vendor, &device, &count) != 2) {
-                                       printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: pci:%s\n",
-                                               p);
-                                       break;
-                               }
-                               subsystem_vendor = subsystem_device = 0;
-                       }
-                       p += count;
-                       if ((!vendor || (vendor == dev->vendor)) &&
-                               (!device || (device == dev->device)) &&
-                               (!subsystem_vendor || (subsystem_vendor == dev->subsystem_vendor)) &&
-                               (!subsystem_device || (subsystem_device == dev->subsystem_device))) {
-                               *resize = true;
-                               if (align_order == -1)
-                                       align = PAGE_SIZE;
-                               else
-                                       align = 1 << align_order;
-                               /* Found */
-                               break;
-                       }
-               }
-               else {
-                       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)) {
-                               *resize = true;
-                               if (align_order == -1)
-                                       align = PAGE_SIZE;
-                               else
-                                       align = 1 << align_order;
-                               /* Found */
-                               break;
-                       }
+
+               ret = pci_dev_str_match(dev, p, &p);
+               if (ret == 1) {
+                       *resize = true;
+                       if (align_order == -1)
+                               align = PAGE_SIZE;
+                       else
+                               align = 1 << align_order;
+                       break;
+               } else if (ret < 0) {
+                       pr_err("PCI: Can't parse resource_alignment parameter: %s\n",
+                              p);
+                       break;
                }
+
                if (*p != ';' && *p != ',') {
                        /* End of param or invalid format */
                        break;
@@ -5805,6 +6062,8 @@ static int __init pci_setup(char *str)
                                pcie_ats_disabled = true;
                        } else if (!strcmp(str, "noaer")) {
                                pci_no_aer();
+                       } else if (!strcmp(str, "earlydump")) {
+                               pci_early_dump = true;
                        } else if (!strncmp(str, "realloc=", 8)) {
                                pci_realloc_get_opt(str + 8);
                        } else if (!strncmp(str, "realloc", 7)) {
@@ -5841,6 +6100,8 @@ static int __init pci_setup(char *str)
                                pcie_bus_config = PCIE_BUS_PEER2PEER;
                        } else if (!strncmp(str, "pcie_scan_all", 13)) {
                                pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS);
+                       } else if (!strncmp(str, "disable_acs_redir=", 18)) {
+                               disable_acs_redir_param = str + 18;
                        } else {
                                printk(KERN_ERR "PCI: Unknown option `%s'\n",
                                                str);
index c358e7a..6e0d152 100644 (file)
@@ -7,6 +7,7 @@
 #define PCI_VSEC_ID_INTEL_TBT  0x1234  /* Thunderbolt */
 
 extern const unsigned char pcie_link_speed[];
+extern bool pci_early_dump;
 
 bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
 
@@ -33,6 +34,7 @@ int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
                  enum pci_mmap_api mmap_api);
 
 int pci_probe_reset_function(struct pci_dev *dev);
+int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
 
 /**
  * struct pci_platform_pm_ops - Firmware PM callbacks
@@ -225,6 +227,10 @@ enum pci_bar_type {
 int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
 bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
                                int crs_timeout);
+bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
+                                       int crs_timeout);
+int pci_idt_bus_quirk(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);
@@ -259,6 +265,7 @@ enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev);
 enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev);
 u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
                           enum pcie_link_width *width);
+void __pcie_print_link_status(struct pci_dev *dev, bool verbose);
 
 /* Single Root I/O Virtualization */
 struct pci_sriov {
@@ -288,6 +295,7 @@ struct pci_sriov {
 
 /* pci_dev priv_flags */
 #define PCI_DEV_DISCONNECTED 0
+#define PCI_DEV_ADDED 1
 
 static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused)
 {
@@ -300,6 +308,44 @@ static inline bool pci_dev_is_disconnected(const struct pci_dev *dev)
        return test_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags);
 }
 
+static inline void pci_dev_assign_added(struct pci_dev *dev, bool added)
+{
+       assign_bit(PCI_DEV_ADDED, &dev->priv_flags, added);
+}
+
+static inline bool pci_dev_is_added(const struct pci_dev *dev)
+{
+       return test_bit(PCI_DEV_ADDED, &dev->priv_flags);
+}
+
+#ifdef CONFIG_PCIEAER
+#include <linux/aer.h>
+
+#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;
+
+       unsigned int id:16;
+
+       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;
+
+       unsigned int status;            /* COR/UNCOR Error Status */
+       unsigned int mask;              /* COR/UNCOR Error Mask */
+       struct aer_header_log_regs tlp; /* TLP Header */
+};
+
+int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info);
+void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
+#endif /* CONFIG_PCIEAER */
+
 #ifdef CONFIG_PCI_ATS
 void pci_restore_ats_state(struct pci_dev *dev);
 #else
@@ -311,6 +357,7 @@ static inline void pci_restore_ats_state(struct pci_dev *dev)
 #ifdef CONFIG_PCI_IOV
 int pci_iov_init(struct pci_dev *dev);
 void pci_iov_release(struct pci_dev *dev);
+void pci_iov_remove(struct pci_dev *dev);
 void pci_iov_update_resource(struct pci_dev *dev, int resno);
 resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
 void pci_restore_iov_state(struct pci_dev *dev);
@@ -323,6 +370,9 @@ static inline int pci_iov_init(struct pci_dev *dev)
 }
 static inline void pci_iov_release(struct pci_dev *dev)
 
+{
+}
+static inline void pci_iov_remove(struct pci_dev *dev)
 {
 }
 static inline void pci_restore_iov_state(struct pci_dev *dev)
@@ -352,6 +402,25 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
 }
 
 void pci_enable_acs(struct pci_dev *dev);
+#ifdef CONFIG_PCI_QUIRKS
+int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
+int pci_dev_specific_enable_acs(struct pci_dev *dev);
+int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
+#else
+static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
+                                              u16 acs_flags)
+{
+       return -ENOTTY;
+}
+static inline int pci_dev_specific_enable_acs(struct pci_dev *dev)
+{
+       return -ENOTTY;
+}
+static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
+{
+       return -ENOTTY;
+}
+#endif
 
 /* PCI error reporting and recovery */
 void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service);
@@ -452,4 +521,19 @@ static inline int devm_of_pci_get_host_bridge_resources(struct device *dev,
 }
 #endif
 
+#ifdef CONFIG_PCIEAER
+void pci_no_aer(void);
+void pci_aer_init(struct pci_dev *dev);
+void pci_aer_exit(struct pci_dev *dev);
+extern const struct attribute_group aer_stats_attr_group;
+void pci_aer_clear_fatal_status(struct pci_dev *dev);
+void pci_aer_clear_device_status(struct pci_dev *dev);
+#else
+static inline void pci_no_aer(void) { }
+static inline int pci_aer_init(struct pci_dev *d) { return -ENODEV; }
+static inline void pci_aer_exit(struct pci_dev *d) { }
+static inline void pci_aer_clear_fatal_status(struct pci_dev *dev) { }
+static inline void pci_aer_clear_device_status(struct pci_dev *dev) { }
+#endif
+
 #endif /* DRIVERS_PCI_H */
index a2e8838..83180ed 100644 (file)
 #include "portdrv.h"
 
 #define AER_ERROR_SOURCES_MAX          100
-#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;
-
-       unsigned int id:16;
-
-       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;
-
-       unsigned int status;            /* COR/UNCOR Error Status */
-       unsigned int mask;              /* COR/UNCOR Error Mask */
-       struct aer_header_log_regs tlp; /* TLP Header */
-};
+#define AER_MAX_TYPEOF_COR_ERRS                16      /* as per PCI_ERR_COR_STATUS */
+#define AER_MAX_TYPEOF_UNCOR_ERRS      26      /* as per PCI_ERR_UNCOR_STATUS*/
 
 struct aer_err_source {
        unsigned int status;
@@ -76,6 +59,42 @@ struct aer_rpc {
                                         */
 };
 
+/* AER stats for the device */
+struct aer_stats {
+
+       /*
+        * Fields for all AER capable devices. They indicate the errors
+        * "as seen by this device". Note that this may mean that if an
+        * end point is causing problems, the AER counters may increment
+        * at its link partner (e.g. root port) because the errors will be
+        * "seen" by the link partner and not the the problematic end point
+        * itself (which may report all counters as 0 as it never saw any
+        * problems).
+        */
+       /* Counters for different type of correctable errors */
+       u64 dev_cor_errs[AER_MAX_TYPEOF_COR_ERRS];
+       /* Counters for different type of fatal uncorrectable errors */
+       u64 dev_fatal_errs[AER_MAX_TYPEOF_UNCOR_ERRS];
+       /* Counters for different type of nonfatal uncorrectable errors */
+       u64 dev_nonfatal_errs[AER_MAX_TYPEOF_UNCOR_ERRS];
+       /* Total number of ERR_COR sent by this device */
+       u64 dev_total_cor_errs;
+       /* Total number of ERR_FATAL sent by this device */
+       u64 dev_total_fatal_errs;
+       /* Total number of ERR_NONFATAL sent by this device */
+       u64 dev_total_nonfatal_errs;
+
+       /*
+        * Fields for Root ports & root complex event collectors only, these
+        * indicate the total number of ERR_COR, ERR_FATAL, and ERR_NONFATAL
+        * messages received by the root port / event collector, INCLUDING the
+        * ones that are generated internally (by the rootport itself)
+        */
+       u64 rootport_total_cor_errs;
+       u64 rootport_total_fatal_errs;
+       u64 rootport_total_nonfatal_errs;
+};
+
 #define AER_LOG_TLP_MASKS              (PCI_ERR_UNC_POISON_TLP|        \
                                        PCI_ERR_UNC_ECRC|               \
                                        PCI_ERR_UNC_UNSUP|              \
@@ -303,12 +322,13 @@ int pcie_aer_get_firmware_first(struct pci_dev *dev)
        if (!pci_is_pcie(dev))
                return 0;
 
+       if (pcie_ports_native)
+               return 0;
+
        if (!dev->__aer_firmware_first_valid)
                aer_set_firmware_first(dev);
        return dev->__aer_firmware_first;
 }
-#define        PCI_EXP_AER_FLAGS       (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
-                                PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
 
 static bool aer_firmware_first;
 
@@ -323,6 +343,9 @@ bool aer_acpi_firmware_first(void)
                .firmware_first = 0,
        };
 
+       if (pcie_ports_native)
+               return false;
+
        if (!parsed) {
                apei_hest_parse(aer_hest_parse, &info);
                aer_firmware_first = info.firmware_first;
@@ -357,16 +380,30 @@ int pci_disable_pcie_error_reporting(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);
 
+void pci_aer_clear_device_status(struct pci_dev *dev)
+{
+       u16 sta;
+
+       pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &sta);
+       pcie_capability_write_word(dev, PCI_EXP_DEVSTA, sta);
+}
+
 int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
 {
        int pos;
-       u32 status;
+       u32 status, sev;
 
        pos = dev->aer_cap;
        if (!pos)
                return -EIO;
 
+       if (pcie_aer_get_firmware_first(dev))
+               return -EIO;
+
+       /* Clear status bits for ERR_NONFATAL errors only */
        pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
+       pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &sev);
+       status &= ~sev;
        if (status)
                pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
 
@@ -374,6 +411,26 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
 
+void pci_aer_clear_fatal_status(struct pci_dev *dev)
+{
+       int pos;
+       u32 status, sev;
+
+       pos = dev->aer_cap;
+       if (!pos)
+               return;
+
+       if (pcie_aer_get_firmware_first(dev))
+               return;
+
+       /* Clear status bits for ERR_FATAL errors only */
+       pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
+       pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &sev);
+       status &= sev;
+       if (status)
+               pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
+}
+
 int pci_cleanup_aer_error_status_regs(struct pci_dev *dev)
 {
        int pos;
@@ -387,6 +444,9 @@ int pci_cleanup_aer_error_status_regs(struct pci_dev *dev)
        if (!pos)
                return -EIO;
 
+       if (pcie_aer_get_firmware_first(dev))
+               return -EIO;
+
        port_type = pci_pcie_type(dev);
        if (port_type == PCI_EXP_TYPE_ROOT_PORT) {
                pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
@@ -402,10 +462,20 @@ int pci_cleanup_aer_error_status_regs(struct pci_dev *dev)
        return 0;
 }
 
-int pci_aer_init(struct pci_dev *dev)
+void pci_aer_init(struct pci_dev *dev)
 {
        dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
-       return pci_cleanup_aer_error_status_regs(dev);
+
+       if (dev->aer_cap)
+               dev->aer_stats = kzalloc(sizeof(struct aer_stats), GFP_KERNEL);
+
+       pci_cleanup_aer_error_status_regs(dev);
+}
+
+void pci_aer_exit(struct pci_dev *dev)
+{
+       kfree(dev->aer_stats);
+       dev->aer_stats = NULL;
 }
 
 #define AER_AGENT_RECEIVER             0
@@ -458,52 +528,52 @@ static const char *aer_error_layer[] = {
        "Transaction Layer"
 };
 
-static const char *aer_correctable_error_string[] = {
-       "Receiver Error",               /* Bit Position 0       */
+static const char *aer_correctable_error_string[AER_MAX_TYPEOF_COR_ERRS] = {
+       "RxErr",                        /* Bit Position 0       */
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
-       "Bad TLP",                      /* Bit Position 6       */
-       "Bad DLLP",                     /* Bit Position 7       */
-       "RELAY_NUM Rollover",           /* Bit Position 8       */
+       "BadTLP",                       /* Bit Position 6       */
+       "BadDLLP",                      /* Bit Position 7       */
+       "Rollover",                     /* Bit Position 8       */
        NULL,
        NULL,
        NULL,
-       "Replay Timer Timeout",         /* Bit Position 12      */
-       "Advisory Non-Fatal",           /* Bit Position 13      */
-       "Corrected Internal Error",     /* Bit Position 14      */
-       "Header Log Overflow",          /* Bit Position 15      */
+       "Timeout",                      /* Bit Position 12      */
+       "NonFatalErr",                  /* Bit Position 13      */
+       "CorrIntErr",                   /* Bit Position 14      */
+       "HeaderOF",                     /* Bit Position 15      */
 };
 
-static const char *aer_uncorrectable_error_string[] = {
+static const char *aer_uncorrectable_error_string[AER_MAX_TYPEOF_UNCOR_ERRS] = {
        "Undefined",                    /* Bit Position 0       */
        NULL,
        NULL,
        NULL,
-       "Data Link Protocol",           /* Bit Position 4       */
-       "Surprise Down Error",          /* Bit Position 5       */
+       "DLP",                          /* Bit Position 4       */
+       "SDES",                         /* Bit Position 5       */
        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      */
+       "TLP",                          /* Bit Position 12      */
+       "FCP",                          /* Bit Position 13      */
+       "CmpltTO",                      /* Bit Position 14      */
+       "CmpltAbrt",                    /* Bit Position 15      */
+       "UnxCmplt",                     /* Bit Position 16      */
+       "RxOF",                         /* Bit Position 17      */
+       "MalfTLP",                      /* Bit Position 18      */
        "ECRC",                         /* Bit Position 19      */
-       "Unsupported Request",          /* Bit Position 20      */
-       "ACS Violation",                /* Bit Position 21      */
-       "Uncorrectable Internal Error", /* Bit Position 22      */
-       "MC Blocked TLP",               /* Bit Position 23      */
-       "AtomicOp Egress Blocked",      /* Bit Position 24      */
-       "TLP Prefix Blocked Error",     /* Bit Position 25      */
+       "UnsupReq",                     /* Bit Position 20      */
+       "ACSViol",                      /* Bit Position 21      */
+       "UncorrIntErr",                 /* Bit Position 22      */
+       "BlockedTLP",                   /* Bit Position 23      */
+       "AtomicOpBlocked",              /* Bit Position 24      */
+       "TLPBlockedErr",                /* Bit Position 25      */
 };
 
 static const char *aer_agent_string[] = {
@@ -513,6 +583,144 @@ static const char *aer_agent_string[] = {
        "Transmitter ID"
 };
 
+#define aer_stats_dev_attr(name, stats_array, strings_array,           \
+                          total_string, total_field)                   \
+       static ssize_t                                                  \
+       name##_show(struct device *dev, struct device_attribute *attr,  \
+                    char *buf)                                         \
+{                                                                      \
+       unsigned int i;                                                 \
+       char *str = buf;                                                \
+       struct pci_dev *pdev = to_pci_dev(dev);                         \
+       u64 *stats = pdev->aer_stats->stats_array;                      \
+                                                                       \
+       for (i = 0; i < ARRAY_SIZE(strings_array); i++) {               \
+               if (strings_array[i])                                   \
+                       str += sprintf(str, "%s %llu\n",                \
+                                      strings_array[i], stats[i]);     \
+               else if (stats[i])                                      \
+                       str += sprintf(str, #stats_array "_bit[%d] %llu\n",\
+                                      i, stats[i]);                    \
+       }                                                               \
+       str += sprintf(str, "TOTAL_%s %llu\n", total_string,            \
+                      pdev->aer_stats->total_field);                   \
+       return str-buf;                                                 \
+}                                                                      \
+static DEVICE_ATTR_RO(name)
+
+aer_stats_dev_attr(aer_dev_correctable, dev_cor_errs,
+                  aer_correctable_error_string, "ERR_COR",
+                  dev_total_cor_errs);
+aer_stats_dev_attr(aer_dev_fatal, dev_fatal_errs,
+                  aer_uncorrectable_error_string, "ERR_FATAL",
+                  dev_total_fatal_errs);
+aer_stats_dev_attr(aer_dev_nonfatal, dev_nonfatal_errs,
+                  aer_uncorrectable_error_string, "ERR_NONFATAL",
+                  dev_total_nonfatal_errs);
+
+#define aer_stats_rootport_attr(name, field)                           \
+       static ssize_t                                                  \
+       name##_show(struct device *dev, struct device_attribute *attr,  \
+                    char *buf)                                         \
+{                                                                      \
+       struct pci_dev *pdev = to_pci_dev(dev);                         \
+       return sprintf(buf, "%llu\n", pdev->aer_stats->field);          \
+}                                                                      \
+static DEVICE_ATTR_RO(name)
+
+aer_stats_rootport_attr(aer_rootport_total_err_cor,
+                        rootport_total_cor_errs);
+aer_stats_rootport_attr(aer_rootport_total_err_fatal,
+                        rootport_total_fatal_errs);
+aer_stats_rootport_attr(aer_rootport_total_err_nonfatal,
+                        rootport_total_nonfatal_errs);
+
+static struct attribute *aer_stats_attrs[] __ro_after_init = {
+       &dev_attr_aer_dev_correctable.attr,
+       &dev_attr_aer_dev_fatal.attr,
+       &dev_attr_aer_dev_nonfatal.attr,
+       &dev_attr_aer_rootport_total_err_cor.attr,
+       &dev_attr_aer_rootport_total_err_fatal.attr,
+       &dev_attr_aer_rootport_total_err_nonfatal.attr,
+       NULL
+};
+
+static umode_t aer_stats_attrs_are_visible(struct kobject *kobj,
+                                          struct attribute *a, int n)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct pci_dev *pdev = to_pci_dev(dev);
+
+       if (!pdev->aer_stats)
+               return 0;
+
+       if ((a == &dev_attr_aer_rootport_total_err_cor.attr ||
+            a == &dev_attr_aer_rootport_total_err_fatal.attr ||
+            a == &dev_attr_aer_rootport_total_err_nonfatal.attr) &&
+           pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT)
+               return 0;
+
+       return a->mode;
+}
+
+const struct attribute_group aer_stats_attr_group = {
+       .attrs  = aer_stats_attrs,
+       .is_visible = aer_stats_attrs_are_visible,
+};
+
+static void pci_dev_aer_stats_incr(struct pci_dev *pdev,
+                                  struct aer_err_info *info)
+{
+       int status, i, max = -1;
+       u64 *counter = NULL;
+       struct aer_stats *aer_stats = pdev->aer_stats;
+
+       if (!aer_stats)
+               return;
+
+       switch (info->severity) {
+       case AER_CORRECTABLE:
+               aer_stats->dev_total_cor_errs++;
+               counter = &aer_stats->dev_cor_errs[0];
+               max = AER_MAX_TYPEOF_COR_ERRS;
+               break;
+       case AER_NONFATAL:
+               aer_stats->dev_total_nonfatal_errs++;
+               counter = &aer_stats->dev_nonfatal_errs[0];
+               max = AER_MAX_TYPEOF_UNCOR_ERRS;
+               break;
+       case AER_FATAL:
+               aer_stats->dev_total_fatal_errs++;
+               counter = &aer_stats->dev_fatal_errs[0];
+               max = AER_MAX_TYPEOF_UNCOR_ERRS;
+               break;
+       }
+
+       status = (info->status & ~info->mask);
+       for (i = 0; i < max; i++)
+               if (status & (1 << i))
+                       counter[i]++;
+}
+
+static void pci_rootport_aer_stats_incr(struct pci_dev *pdev,
+                                struct aer_err_source *e_src)
+{
+       struct aer_stats *aer_stats = pdev->aer_stats;
+
+       if (!aer_stats)
+               return;
+
+       if (e_src->status & PCI_ERR_ROOT_COR_RCV)
+               aer_stats->rootport_total_cor_errs++;
+
+       if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {
+               if (e_src->status & PCI_ERR_ROOT_FATAL_RCV)
+                       aer_stats->rootport_total_fatal_errs++;
+               else
+                       aer_stats->rootport_total_nonfatal_errs++;
+       }
+}
+
 static void __print_tlp_header(struct pci_dev *dev,
                               struct aer_header_log_regs *t)
 {
@@ -545,9 +753,10 @@ static void __aer_print_error(struct pci_dev *dev,
                        pci_err(dev, "   [%2d] Unknown Error Bit%s\n",
                                i, info->first_error == i ? " (First)" : "");
        }
+       pci_dev_aer_stats_incr(dev, info);
 }
 
-static void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
+void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
 {
        int layer, agent;
        int id = ((dev->bus->number << 8) | dev->devfn);
@@ -799,6 +1008,7 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
                if (pos)
                        pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
                                        info->status);
+               pci_aer_clear_device_status(dev);
        } else if (info->severity == AER_NONFATAL)
                pcie_do_nonfatal_recovery(dev);
        else if (info->severity == AER_FATAL)
@@ -876,7 +1086,7 @@ EXPORT_SYMBOL_GPL(aer_recover_queue);
 #endif
 
 /**
- * get_device_error_info - read error status from dev and store it to info
+ * aer_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
  *
@@ -884,7 +1094,7 @@ EXPORT_SYMBOL_GPL(aer_recover_queue);
  *
  * 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 aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
 {
        int pos, temp;
 
@@ -942,11 +1152,11 @@ static inline void aer_process_err_devices(struct aer_err_info *e_info)
 
        /* 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))
+               if (aer_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))
+               if (aer_get_device_error_info(e_info->dev[i], e_info))
                        handle_error_source(e_info->dev[i], e_info);
        }
 }
@@ -962,6 +1172,8 @@ static void aer_isr_one_error(struct aer_rpc *rpc,
        struct pci_dev *pdev = rpc->rpd;
        struct aer_err_info *e_info = &rpc->e_info;
 
+       pci_rootport_aer_stats_incr(pdev, e_src);
+
        /*
         * There is a possibility that both correctable error and
         * uncorrectable error being logged. Report correctable error first.
@@ -1305,6 +1517,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
 {
        u32 reg32;
        int pos;
+       int rc;
 
        pos = dev->aer_cap;
 
@@ -1313,7 +1526,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
        reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
        pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
 
-       pci_reset_bridge_secondary_bus(dev);
+       rc = pci_bridge_secondary_bus_reset(dev);
        pci_printk(KERN_DEBUG, dev, "Root Port link has been reset\n");
 
        /* Clear Root Error Status */
@@ -1325,7 +1538,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
        reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
        pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
 
-       return PCI_ERS_RESULT_RECOVERED;
+       return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
 }
 
 /**
@@ -1336,20 +1549,8 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
  */
 static void aer_error_resume(struct pci_dev *dev)
 {
-       int pos;
-       u32 status, mask;
-       u16 reg16;
-
-       /* Clean up Root device status */
-       pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &reg16);
-       pcie_capability_write_word(dev, PCI_EXP_DEVSTA, reg16);
-
-       /* Clean AER Root Error Status */
-       pos = dev->aer_cap;
-       pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
-       pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
-       status &= ~mask; /* Clear corresponding nonfatal bits */
-       pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
+       pci_aer_clear_device_status(dev);
+       pci_cleanup_aer_uncorrect_error_status(dev);
 }
 
 static struct pcie_port_service_driver aerdriver = {
index c687c81..5326916 100644 (file)
@@ -1127,11 +1127,9 @@ static int pcie_aspm_set_policy(const char *val,
 
        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;
+       i = sysfs_match_string(policy_str, val);
+       if (i < 0)
+               return i;
        if (i == aspm_policy)
                return 0;
 
index 921ed97..f03279f 100644 (file)
@@ -6,6 +6,7 @@
  * Copyright (C) 2016 Intel Corp.
  */
 
+#include <linux/aer.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 
 struct dpc_dev {
        struct pcie_device      *dev;
-       struct work_struct      work;
        u16                     cap_pos;
        bool                    rp_extensions;
-       u32                     rp_pio_status;
        u8                      rp_log_size;
 };
 
@@ -65,19 +64,13 @@ static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
        return 0;
 }
 
-static void dpc_wait_link_inactive(struct dpc_dev *dpc)
-{
-       struct pci_dev *pdev = dpc->dev->port;
-
-       pcie_wait_for_link(pdev, false);
-}
-
 static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
 {
        struct dpc_dev *dpc;
        struct pcie_device *pciedev;
        struct device *devdpc;
-       u16 cap, ctl;
+
+       u16 cap;
 
        /*
         * DPC disables the Link automatically in hardware, so it has
@@ -92,34 +85,17 @@ static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
         * Wait until the Link is inactive, then clear DPC Trigger Status
         * to allow the Port to leave DPC.
         */
-       dpc_wait_link_inactive(dpc);
+       pcie_wait_for_link(pdev, false);
 
        if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc))
                return PCI_ERS_RESULT_DISCONNECT;
-       if (dpc->rp_extensions && dpc->rp_pio_status) {
-               pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS,
-                                      dpc->rp_pio_status);
-               dpc->rp_pio_status = 0;
-       }
 
        pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
                              PCI_EXP_DPC_STATUS_TRIGGER);
 
-       pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
-       pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
-                             ctl | PCI_EXP_DPC_CTL_INT_EN);
-
        return PCI_ERS_RESULT_RECOVERED;
 }
 
-static void dpc_work(struct work_struct *work)
-{
-       struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
-       struct pci_dev *pdev = dpc->dev->port;
-
-       /* We configure DPC so it only triggers on ERR_FATAL */
-       pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_DPC);
-}
 
 static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
 {
@@ -134,8 +110,6 @@ static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
        dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n",
                status, mask);
 
-       dpc->rp_pio_status = status;
-
        pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SEVERITY, &sev);
        pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SYSERROR, &syserr);
        pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_EXCEPTION, &exc);
@@ -146,15 +120,14 @@ static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
        pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status);
        first_error = (dpc_status & 0x1f00) >> 8;
 
-       status &= ~mask;
        for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) {
-               if (status & (1 << i))
+               if ((status & ~mask) & (1 << i))
                        dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i],
                                first_error == i ? " (First)" : "");
        }
 
        if (dpc->rp_log_size < 4)
-               return;
+               goto clear_status;
        pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG,
                              &dw0);
        pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4,
@@ -167,7 +140,7 @@ static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
                dw0, dw1, dw2, dw3);
 
        if (dpc->rp_log_size < 5)
-               return;
+               goto clear_status;
        pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log);
        dev_err(dev, "RP PIO ImpSpec Log %#010x\n", log);
 
@@ -176,43 +149,26 @@ static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
                        cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG, &prefix);
                dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix);
        }
+ clear_status:
+       pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, status);
 }
 
-static irqreturn_t dpc_irq(int irq, void *context)
+static irqreturn_t dpc_handler(int irq, void *context)
 {
-       struct dpc_dev *dpc = (struct dpc_dev *)context;
+       struct aer_err_info info;
+       struct dpc_dev *dpc = context;
        struct pci_dev *pdev = dpc->dev->port;
        struct device *dev = &dpc->dev->device;
-       u16 cap = dpc->cap_pos, ctl, status, source, reason, ext_reason;
-
-       pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
-
-       if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0))
-               return IRQ_NONE;
+       u16 cap = dpc->cap_pos, status, source, reason, ext_reason;
 
        pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
-
-       if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT))
-               return IRQ_NONE;
-
-       if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) {
-               pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
-                                     PCI_EXP_DPC_STATUS_INTERRUPT);
-               return IRQ_HANDLED;
-       }
-
-       pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
-                             ctl & ~PCI_EXP_DPC_CTL_INT_EN);
-
-       pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID,
-                            &source);
+       pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID, &source);
 
        dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n",
-               status, source);
+                status, source);
 
        reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1;
        ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5;
-
        dev_warn(dev, "DPC %s detected, remove downstream devices\n",
                 (reason == 0) ? "unmasked uncorrectable error" :
                 (reason == 1) ? "ERR_NONFATAL" :
@@ -220,15 +176,36 @@ static irqreturn_t dpc_irq(int irq, void *context)
                 (ext_reason == 0) ? "RP PIO error" :
                 (ext_reason == 1) ? "software trigger" :
                                     "reserved error");
+
        /* show RP PIO error detail information */
        if (dpc->rp_extensions && reason == 3 && ext_reason == 0)
                dpc_process_rp_pio_error(dpc);
+       else if (reason == 0 && aer_get_device_error_info(pdev, &info)) {
+               aer_print_error(pdev, &info);
+               pci_cleanup_aer_uncorrect_error_status(pdev);
+       }
 
-       pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
-                             PCI_EXP_DPC_STATUS_INTERRUPT);
+       /* We configure DPC so it only triggers on ERR_FATAL */
+       pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_DPC);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t dpc_irq(int irq, void *context)
+{
+       struct dpc_dev *dpc = (struct dpc_dev *)context;
+       struct pci_dev *pdev = dpc->dev->port;
+       u16 cap = dpc->cap_pos, status;
+
+       pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
 
-       schedule_work(&dpc->work);
+       if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT) || status == (u16)(~0))
+               return IRQ_NONE;
 
+       pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
+                             PCI_EXP_DPC_STATUS_INTERRUPT);
+       if (status & PCI_EXP_DPC_STATUS_TRIGGER)
+               return IRQ_WAKE_THREAD;
        return IRQ_HANDLED;
 }
 
@@ -250,11 +227,11 @@ static int dpc_probe(struct pcie_device *dev)
 
        dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC);
        dpc->dev = dev;
-       INIT_WORK(&dpc->work, dpc_work);
        set_service_data(dev, dpc);
 
-       status = devm_request_irq(device, dev->irq, dpc_irq, IRQF_SHARED,
-                                 "pcie-dpc", dpc);
+       status = devm_request_threaded_irq(device, dev->irq, dpc_irq,
+                                          dpc_handler, IRQF_SHARED,
+                                          "pcie-dpc", dpc);
        if (status) {
                dev_warn(device, "request IRQ%d failed: %d\n", dev->irq,
                         status);
index f7ce0cb..708fd3a 100644 (file)
@@ -175,9 +175,11 @@ out:
  */
 static pci_ers_result_t default_reset_link(struct pci_dev *dev)
 {
-       pci_reset_bridge_secondary_bus(dev);
+       int rc;
+
+       rc = pci_bridge_secondary_bus_reset(dev);
        pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n");
-       return PCI_ERS_RESULT_RECOVERED;
+       return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
 }
 
 static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
@@ -252,6 +254,7 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
                        dev->error_state = state;
                pci_walk_bus(dev->subordinate, cb, &result_data);
                if (cb == report_resume) {
+                       pci_aer_clear_device_status(dev);
                        pci_cleanup_aer_uncorrect_error_status(dev);
                        dev->error_state = pci_channel_io_normal;
                }
@@ -259,15 +262,10 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
                /*
                 * If the error is reported by an end point, we think this
                 * error is related to the upstream link of the end point.
+                * The error is non fatal so the bus is ok; just invoke
+                * the callback for the function that logged the error.
                 */
-               if (state == pci_channel_io_normal)
-                       /*
-                        * the error is non fatal so the bus is ok, just invoke
-                        * the callback for the function that logged the error.
-                        */
-                       cb(dev, &result_data);
-               else
-                       pci_walk_bus(dev->bus, cb, &result_data);
+               cb(dev, &result_data);
        }
 
        return result_data.result;
@@ -295,6 +293,7 @@ void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service)
 
        parent = udev->subordinate;
        pci_lock_rescan_remove();
+       pci_dev_get(dev);
        list_for_each_entry_safe_reverse(pdev, temp, &parent->devices,
                                         bus_list) {
                pci_dev_get(pdev);
@@ -316,7 +315,8 @@ void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service)
                 * do error recovery on all subordinates of the bridge instead
                 * of the bridge and clear the error status of the bridge.
                 */
-               pci_cleanup_aer_uncorrect_error_status(dev);
+               pci_aer_clear_fatal_status(dev);
+               pci_aer_clear_device_status(dev);
        }
 
        if (result == PCI_ERS_RESULT_RECOVERED) {
@@ -328,6 +328,7 @@ void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service)
                pci_info(dev, "Device recovery from fatal error failed\n");
        }
 
+       pci_dev_put(dev);
        pci_unlock_rescan_remove();
 }
 
index 6ffc797..d59afa4 100644 (file)
@@ -50,6 +50,7 @@ struct pcie_port_service_driver {
        int (*probe) (struct pcie_device *dev);
        void (*remove) (struct pcie_device *dev);
        int (*suspend) (struct pcie_device *dev);
+       int (*resume_noirq) (struct pcie_device *dev);
        int (*resume) (struct pcie_device *dev);
 
        /* Device driver may resume normal operations */
@@ -82,6 +83,7 @@ extern struct bus_type pcie_port_bus_type;
 int pcie_port_device_register(struct pci_dev *dev);
 #ifdef CONFIG_PM
 int pcie_port_device_suspend(struct device *dev);
+int pcie_port_device_resume_noirq(struct device *dev);
 int pcie_port_device_resume(struct device *dev);
 #endif
 void pcie_port_device_remove(struct pci_dev *dev);
index e0261ad..7c37d81 100644 (file)
@@ -353,14 +353,19 @@ error_disable:
 }
 
 #ifdef CONFIG_PM
-static int suspend_iter(struct device *dev, void *data)
+typedef int (*pcie_pm_callback_t)(struct pcie_device *);
+
+static int pm_iter(struct device *dev, void *data)
 {
        struct pcie_port_service_driver *service_driver;
+       size_t offset = *(size_t *)data;
+       pcie_pm_callback_t cb;
 
        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));
+               cb = *(pcie_pm_callback_t *)((void *)service_driver + offset);
+               if (cb)
+                       return cb(to_pcie_device(dev));
        }
        return 0;
 }
@@ -371,20 +376,14 @@ static int suspend_iter(struct device *dev, void *data)
  */
 int pcie_port_device_suspend(struct device *dev)
 {
-       return device_for_each_child(dev, NULL, suspend_iter);
+       size_t off = offsetof(struct pcie_port_service_driver, suspend);
+       return device_for_each_child(dev, &off, pm_iter);
 }
 
-static int resume_iter(struct device *dev, void *data)
+int pcie_port_device_resume_noirq(struct device *dev)
 {
-       struct pcie_port_service_driver *service_driver;
-
-       if ((dev->bus == &pcie_port_bus_type) &&
-           (dev->driver)) {
-               service_driver = to_service_driver(dev->driver);
-               if (service_driver->resume)
-                       service_driver->resume(to_pcie_device(dev));
-       }
-       return 0;
+       size_t off = offsetof(struct pcie_port_service_driver, resume_noirq);
+       return device_for_each_child(dev, &off, pm_iter);
 }
 
 /**
@@ -393,7 +392,8 @@ static int resume_iter(struct device *dev, void *data)
  */
 int pcie_port_device_resume(struct device *dev)
 {
-       return device_for_each_child(dev, NULL, resume_iter);
+       size_t off = offsetof(struct pcie_port_service_driver, resume);
+       return device_for_each_child(dev, &off, pm_iter);
 }
 #endif /* PM */
 
index 973f1b8..eef22dc 100644 (file)
@@ -42,17 +42,6 @@ __setup("pcie_ports=", pcie_port_setup);
 
 /* global data */
 
-static int pcie_portdrv_restore_config(struct pci_dev *dev)
-{
-       int retval;
-
-       retval = pci_enable_device(dev);
-       if (retval)
-               return retval;
-       pci_set_master(dev);
-       return 0;
-}
-
 #ifdef CONFIG_PM
 static int pcie_port_runtime_suspend(struct device *dev)
 {
@@ -76,10 +65,12 @@ static int pcie_port_runtime_idle(struct device *dev)
 
 static const struct dev_pm_ops pcie_portdrv_pm_ops = {
        .suspend        = pcie_port_device_suspend,
+       .resume_noirq   = pcie_port_device_resume_noirq,
        .resume         = pcie_port_device_resume,
        .freeze         = pcie_port_device_suspend,
        .thaw           = pcie_port_device_resume,
        .poweroff       = pcie_port_device_suspend,
+       .restore_noirq  = pcie_port_device_resume_noirq,
        .restore        = pcie_port_device_resume,
        .runtime_suspend = pcie_port_runtime_suspend,
        .runtime_resume = pcie_port_runtime_resume,
@@ -160,19 +151,6 @@ static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
        return PCI_ERS_RESULT_RECOVERED;
 }
 
-static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)
-{
-       /* 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);
-       }
-
-       return PCI_ERS_RESULT_RECOVERED;
-}
-
 static int resume_iter(struct device *device, void *data)
 {
        struct pcie_device *pcie_device;
@@ -208,7 +186,6 @@ static const struct pci_device_id port_pci_ids[] = { {
 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,
 };
 
index ac876e3..ec78400 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/cpumask.h>
-#include <linux/pci-aspm.h>
 #include <linux/aer.h>
 #include <linux/acpi.h>
 #include <linux/hypervisor.h>
@@ -1549,6 +1548,20 @@ static int pci_intx_mask_broken(struct pci_dev *dev)
        return 0;
 }
 
+static void early_dump_pci_device(struct pci_dev *pdev)
+{
+       u32 value[256 / 4];
+       int i;
+
+       pci_info(pdev, "config space:\n");
+
+       for (i = 0; i < 256; i += 4)
+               pci_read_config_dword(pdev, i, &value[i / 4]);
+
+       print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+                      value, 256, false);
+}
+
 /**
  * pci_setup_device - Fill in class and map information of a device
  * @dev: the device structure to fill
@@ -1598,6 +1611,9 @@ int pci_setup_device(struct pci_dev *dev)
        pci_printk(KERN_DEBUG, dev, "[%04x:%04x] type %02x class %#08x\n",
                   dev->vendor, dev->device, dev->hdr_type, dev->class);
 
+       if (pci_early_dump)
+               early_dump_pci_device(dev);
+
        /* Need to have dev->class ready */
        dev->cfg_size = pci_cfg_space_size(dev);
 
@@ -1725,11 +1741,15 @@ int pci_setup_device(struct pci_dev *dev)
 static void pci_configure_mps(struct pci_dev *dev)
 {
        struct pci_dev *bridge = pci_upstream_bridge(dev);
-       int mps, p_mps, rc;
+       int mps, mpss, p_mps, rc;
 
        if (!pci_is_pcie(dev) || !bridge || !pci_is_pcie(bridge))
                return;
 
+       /* MPS and MRRS fields are of type 'RsvdP' for VFs, short-circuit out */
+       if (dev->is_virtfn)
+               return;
+
        mps = pcie_get_mps(dev);
        p_mps = pcie_get_mps(bridge);
 
@@ -1749,6 +1769,14 @@ static void pci_configure_mps(struct pci_dev *dev)
        if (pcie_bus_config != PCIE_BUS_DEFAULT)
                return;
 
+       mpss = 128 << dev->pcie_mpss;
+       if (mpss < p_mps && pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT) {
+               pcie_set_mps(bridge, mpss);
+               pci_info(dev, "Upstream bridge's Max Payload Size set to %d (was %d, max %d)\n",
+                        mpss, p_mps, 128 << bridge->pcie_mpss);
+               p_mps = pcie_get_mps(bridge);
+       }
+
        rc = pcie_set_mps(dev, p_mps);
        if (rc) {
                pci_warn(dev, "can't set Max Payload Size to %d; if necessary, use \"pci=pcie_bus_safe\" and report a bug\n",
@@ -1757,7 +1785,7 @@ static void pci_configure_mps(struct pci_dev *dev)
        }
 
        pci_info(dev, "Max Payload Size set to %d (was %d, max %d)\n",
-                p_mps, mps, 128 << dev->pcie_mpss);
+                p_mps, mps, mpss);
 }
 
 static struct hpp_type0 pci_default_type0 = {
@@ -2042,6 +2070,29 @@ static void pci_configure_ltr(struct pci_dev *dev)
 #endif
 }
 
+static void pci_configure_eetlp_prefix(struct pci_dev *dev)
+{
+#ifdef CONFIG_PCI_PASID
+       struct pci_dev *bridge;
+       u32 cap;
+
+       if (!pci_is_pcie(dev))
+               return;
+
+       pcie_capability_read_dword(dev, PCI_EXP_DEVCAP2, &cap);
+       if (!(cap & PCI_EXP_DEVCAP2_EE_PREFIX))
+               return;
+
+       if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
+               dev->eetlp_prefix_path = 1;
+       else {
+               bridge = pci_upstream_bridge(dev);
+               if (bridge && bridge->eetlp_prefix_path)
+                       dev->eetlp_prefix_path = 1;
+       }
+#endif
+}
+
 static void pci_configure_device(struct pci_dev *dev)
 {
        struct hotplug_params hpp;
@@ -2051,6 +2102,7 @@ static void pci_configure_device(struct pci_dev *dev)
        pci_configure_extended_tags(dev, NULL);
        pci_configure_relaxed_ordering(dev);
        pci_configure_ltr(dev);
+       pci_configure_eetlp_prefix(dev);
 
        memset(&hpp, 0, sizeof(hpp));
        ret = pci_get_hp_params(dev, &hpp);
@@ -2064,6 +2116,7 @@ static void pci_configure_device(struct pci_dev *dev)
 
 static void pci_release_capabilities(struct pci_dev *dev)
 {
+       pci_aer_exit(dev);
        pci_vpd_release(dev);
        pci_iov_release(dev);
        pci_free_cap_save_buffers(dev);
@@ -2156,8 +2209,8 @@ static bool pci_bus_wait_crs(struct pci_bus *bus, int devfn, u32 *l,
        return true;
 }
 
-bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
-                               int timeout)
+bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
+                                       int timeout)
 {
        if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
                return false;
@@ -2172,6 +2225,24 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
 
        return true;
 }
+
+bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
+                               int timeout)
+{
+#ifdef CONFIG_PCI_QUIRKS
+       struct pci_dev *bridge = bus->self;
+
+       /*
+        * Certain IDT switches have an issue where they improperly trigger
+        * ACS Source Validation errors on completions for config reads.
+        */
+       if (bridge && bridge->vendor == PCI_VENDOR_ID_IDT &&
+           bridge->device == 0x80b5)
+               return pci_idt_bus_quirk(bus, devfn, l, timeout);
+#endif
+
+       return pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);
+}
 EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
 
 /*
@@ -2205,6 +2276,25 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
        return dev;
 }
 
+static void pcie_report_downtraining(struct pci_dev *dev)
+{
+       if (!pci_is_pcie(dev))
+               return;
+
+       /* Look from the device up to avoid downstream ports with no devices */
+       if ((pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT) &&
+           (pci_pcie_type(dev) != PCI_EXP_TYPE_LEG_END) &&
+           (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM))
+               return;
+
+       /* Multi-function PCIe devices share the same link/status */
+       if (PCI_FUNC(dev->devfn) != 0 || dev->is_virtfn)
+               return;
+
+       /* Print link status only if the device is constrained by the fabric */
+       __pcie_print_link_status(dev, false);
+}
+
 static void pci_init_capabilities(struct pci_dev *dev)
 {
        /* Enhanced Allocation */
@@ -2240,6 +2330,8 @@ static void pci_init_capabilities(struct pci_dev *dev)
        /* Advanced Error Reporting */
        pci_aer_init(dev);
 
+       pcie_report_downtraining(dev);
+
        if (pci_probe_reset_function(dev) == 0)
                dev->reset_fn = 1;
 }
@@ -2433,13 +2525,13 @@ int pci_scan_slot(struct pci_bus *bus, int devfn)
        dev = pci_scan_single_device(bus, devfn);
        if (!dev)
                return 0;
-       if (!dev->is_added)
+       if (!pci_dev_is_added(dev))
                nr++;
 
        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) {
-                       if (!dev->is_added)
+                       if (!pci_dev_is_added(dev))
                                nr++;
                        dev->multifunction = 1;
                }
index f439de8..46f58a9 100644 (file)
 #include <linux/sched.h>
 #include <linux/ktime.h>
 #include <linux/mm.h>
+#include <linux/nvme.h>
 #include <linux/platform_data/x86/apple.h>
 #include <linux/pm_runtime.h>
+#include <linux/switchtec.h>
 #include <asm/dma.h>   /* isa_dma_bridge_buggy */
 #include "pci.h"
 
@@ -460,6 +462,7 @@ static void quirk_nfp6000(struct pci_dev *dev)
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME,      PCI_DEVICE_ID_NETRONOME_NFP4000,        quirk_nfp6000);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME,      PCI_DEVICE_ID_NETRONOME_NFP6000,        quirk_nfp6000);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME,      PCI_DEVICE_ID_NETRONOME_NFP5000,        quirk_nfp6000);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME,      PCI_DEVICE_ID_NETRONOME_NFP6000_VF,     quirk_nfp6000);
 
 /*  On IBM Crocodile ipr SAS adapters, expand BAR to system page size */
@@ -2105,6 +2108,7 @@ static void quirk_netmos(struct pci_dev *dev)
                if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
                                dev->subsystem_device == 0x0299)
                        return;
+               /* else: fall through */
        case PCI_DEVICE_ID_NETMOS_9735:
        case PCI_DEVICE_ID_NETMOS_9745:
        case PCI_DEVICE_ID_NETMOS_9845:
@@ -2352,6 +2356,9 @@ static void quirk_paxc_bridge(struct pci_dev *pdev)
 }
 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge);
 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd750, quirk_paxc_bridge);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, quirk_paxc_bridge);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_bridge);
 #endif
 
 /*
@@ -3664,6 +3671,108 @@ static int reset_chelsio_generic_dev(struct pci_dev *dev, int probe)
 #define PCI_DEVICE_ID_INTEL_IVB_M_VGA      0x0156
 #define PCI_DEVICE_ID_INTEL_IVB_M2_VGA     0x0166
 
+/*
+ * The Samsung SM961/PM961 controller can sometimes enter a fatal state after
+ * FLR where config space reads from the device return -1.  We seem to be
+ * able to avoid this condition if we disable the NVMe controller prior to
+ * FLR.  This quirk is generic for any NVMe class device requiring similar
+ * assistance to quiesce the device prior to FLR.
+ *
+ * NVMe specification: https://nvmexpress.org/resources/specifications/
+ * Revision 1.0e:
+ *    Chapter 2: Required and optional PCI config registers
+ *    Chapter 3: NVMe control registers
+ *    Chapter 7.3: Reset behavior
+ */
+static int nvme_disable_and_flr(struct pci_dev *dev, int probe)
+{
+       void __iomem *bar;
+       u16 cmd;
+       u32 cfg;
+
+       if (dev->class != PCI_CLASS_STORAGE_EXPRESS ||
+           !pcie_has_flr(dev) || !pci_resource_start(dev, 0))
+               return -ENOTTY;
+
+       if (probe)
+               return 0;
+
+       bar = pci_iomap(dev, 0, NVME_REG_CC + sizeof(cfg));
+       if (!bar)
+               return -ENOTTY;
+
+       pci_read_config_word(dev, PCI_COMMAND, &cmd);
+       pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
+
+       cfg = readl(bar + NVME_REG_CC);
+
+       /* Disable controller if enabled */
+       if (cfg & NVME_CC_ENABLE) {
+               u32 cap = readl(bar + NVME_REG_CAP);
+               unsigned long timeout;
+
+               /*
+                * Per nvme_disable_ctrl() skip shutdown notification as it
+                * could complete commands to the admin queue.  We only intend
+                * to quiesce the device before reset.
+                */
+               cfg &= ~(NVME_CC_SHN_MASK | NVME_CC_ENABLE);
+
+               writel(cfg, bar + NVME_REG_CC);
+
+               /*
+                * Some controllers require an additional delay here, see
+                * NVME_QUIRK_DELAY_BEFORE_CHK_RDY.  None of those are yet
+                * supported by this quirk.
+                */
+
+               /* Cap register provides max timeout in 500ms increments */
+               timeout = ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2) + jiffies;
+
+               for (;;) {
+                       u32 status = readl(bar + NVME_REG_CSTS);
+
+                       /* Ready status becomes zero on disable complete */
+                       if (!(status & NVME_CSTS_RDY))
+                               break;
+
+                       msleep(100);
+
+                       if (time_after(jiffies, timeout)) {
+                               pci_warn(dev, "Timeout waiting for NVMe ready status to clear after disable\n");
+                               break;
+                       }
+               }
+       }
+
+       pci_iounmap(dev, bar);
+
+       pcie_flr(dev);
+
+       return 0;
+}
+
+/*
+ * Intel DC P3700 NVMe controller will timeout waiting for ready status
+ * to change after NVMe enable if the driver starts interacting with the
+ * device too soon after FLR.  A 250ms delay after FLR has heuristically
+ * proven to produce reliably working results for device assignment cases.
+ */
+static int delay_250ms_after_flr(struct pci_dev *dev, int probe)
+{
+       if (!pcie_has_flr(dev))
+               return -ENOTTY;
+
+       if (probe)
+               return 0;
+
+       pcie_flr(dev);
+
+       msleep(250);
+
+       return 0;
+}
+
 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 },
@@ -3671,6 +3780,8 @@ static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
                reset_ivb_igd },
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IVB_M2_VGA,
                reset_ivb_igd },
+       { PCI_VENDOR_ID_SAMSUNG, 0xa804, nvme_disable_and_flr },
+       { PCI_VENDOR_ID_INTEL, 0x0953, delay_250ms_after_flr },
        { PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
                reset_chelsio_generic_dev },
        { 0 }
@@ -3740,6 +3851,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x917a,
 /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c78 */
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9182,
                         quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c134 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9183,
+                        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);
@@ -4553,27 +4667,79 @@ static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev)
        return 0;
 }
 
-static const struct pci_dev_enable_acs {
+static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)
+{
+       int pos;
+       u32 cap, ctrl;
+
+       if (!pci_quirk_intel_spt_pch_acs_match(dev))
+               return -ENOTTY;
+
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
+       if (!pos)
+               return -ENOTTY;
+
+       pci_read_config_dword(dev, pos + PCI_ACS_CAP, &cap);
+       pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl);
+
+       ctrl &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC);
+
+       pci_write_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, ctrl);
+
+       pci_info(dev, "Intel SPT PCH root port workaround: disabled ACS redirect\n");
+
+       return 0;
+}
+
+static const struct pci_dev_acs_ops {
        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 },
-       { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_spt_pch_acs },
-       { 0 }
+       int (*disable_acs_redir)(struct pci_dev *dev);
+} pci_dev_acs_ops[] = {
+       { PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+           .enable_acs = pci_quirk_enable_intel_pch_acs,
+       },
+       { PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+           .enable_acs = pci_quirk_enable_intel_spt_pch_acs,
+           .disable_acs_redir = pci_quirk_disable_intel_spt_pch_acs_redir,
+       },
 };
 
 int pci_dev_specific_enable_acs(struct pci_dev *dev)
 {
-       const struct pci_dev_enable_acs *i;
-       int ret;
+       const struct pci_dev_acs_ops *p;
+       int i, ret;
+
+       for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
+               p = &pci_dev_acs_ops[i];
+               if ((p->vendor == dev->vendor ||
+                    p->vendor == (u16)PCI_ANY_ID) &&
+                   (p->device == dev->device ||
+                    p->device == (u16)PCI_ANY_ID) &&
+                   p->enable_acs) {
+                       ret = p->enable_acs(dev);
+                       if (ret >= 0)
+                               return 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);
+       return -ENOTTY;
+}
+
+int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
+{
+       const struct pci_dev_acs_ops *p;
+       int i, ret;
+
+       for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
+               p = &pci_dev_acs_ops[i];
+               if ((p->vendor == dev->vendor ||
+                    p->vendor == (u16)PCI_ANY_ID) &&
+                   (p->device == dev->device ||
+                    p->device == (u16)PCI_ANY_ID) &&
+                   p->disable_acs_redir) {
+                       ret = p->disable_acs_redir(dev);
                        if (ret >= 0)
                                return ret;
                }
@@ -4753,3 +4919,197 @@ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMD, PCI_ANY_ID,
                              PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
 DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
                              PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
+
+/*
+ * Some IDT switches incorrectly flag an ACS Source Validation error on
+ * completions for config read requests even though PCIe r4.0, sec
+ * 6.12.1.1, says that completions are never affected by ACS Source
+ * Validation.  Here's the text of IDT 89H32H8G3-YC, erratum #36:
+ *
+ *   Item #36 - Downstream port applies ACS Source Validation to Completions
+ *   Section 6.12.1.1 of the PCI Express Base Specification 3.1 states that
+ *   completions are never affected by ACS Source Validation.  However,
+ *   completions received by a downstream port of the PCIe switch from a
+ *   device that has not yet captured a PCIe bus number are incorrectly
+ *   dropped by ACS Source Validation by the switch downstream port.
+ *
+ * The workaround suggested by IDT is to issue a config write to the
+ * downstream device before issuing the first config read.  This allows the
+ * downstream device to capture its bus and device numbers (see PCIe r4.0,
+ * sec 2.2.9), thus avoiding the ACS error on the completion.
+ *
+ * However, we don't know when the device is ready to accept the config
+ * write, so we do config reads until we receive a non-Config Request Retry
+ * Status, then do the config write.
+ *
+ * To avoid hitting the erratum when doing the config reads, we disable ACS
+ * SV around this process.
+ */
+int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *l, int timeout)
+{
+       int pos;
+       u16 ctrl = 0;
+       bool found;
+       struct pci_dev *bridge = bus->self;
+
+       pos = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ACS);
+
+       /* Disable ACS SV before initial config reads */
+       if (pos) {
+               pci_read_config_word(bridge, pos + PCI_ACS_CTRL, &ctrl);
+               if (ctrl & PCI_ACS_SV)
+                       pci_write_config_word(bridge, pos + PCI_ACS_CTRL,
+                                             ctrl & ~PCI_ACS_SV);
+       }
+
+       found = pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);
+
+       /* Write Vendor ID (read-only) so the endpoint latches its bus/dev */
+       if (found)
+               pci_bus_write_config_word(bus, devfn, PCI_VENDOR_ID, 0);
+
+       /* Re-enable ACS_SV if it was previously enabled */
+       if (ctrl & PCI_ACS_SV)
+               pci_write_config_word(bridge, pos + PCI_ACS_CTRL, ctrl);
+
+       return found;
+}
+
+/*
+ * Microsemi Switchtec NTB uses devfn proxy IDs to move TLPs between
+ * NT endpoints via the internal switch fabric. These IDs replace the
+ * originating requestor ID TLPs which access host memory on peer NTB
+ * ports. Therefore, all proxy IDs must be aliased to the NTB device
+ * to permit access when the IOMMU is turned on.
+ */
+static void quirk_switchtec_ntb_dma_alias(struct pci_dev *pdev)
+{
+       void __iomem *mmio;
+       struct ntb_info_regs __iomem *mmio_ntb;
+       struct ntb_ctrl_regs __iomem *mmio_ctrl;
+       struct sys_info_regs __iomem *mmio_sys_info;
+       u64 partition_map;
+       u8 partition;
+       int pp;
+
+       if (pci_enable_device(pdev)) {
+               pci_err(pdev, "Cannot enable Switchtec device\n");
+               return;
+       }
+
+       mmio = pci_iomap(pdev, 0, 0);
+       if (mmio == NULL) {
+               pci_disable_device(pdev);
+               pci_err(pdev, "Cannot iomap Switchtec device\n");
+               return;
+       }
+
+       pci_info(pdev, "Setting Switchtec proxy ID aliases\n");
+
+       mmio_ntb = mmio + SWITCHTEC_GAS_NTB_OFFSET;
+       mmio_ctrl = (void __iomem *) mmio_ntb + SWITCHTEC_NTB_REG_CTRL_OFFSET;
+       mmio_sys_info = mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET;
+
+       partition = ioread8(&mmio_ntb->partition_id);
+
+       partition_map = ioread32(&mmio_ntb->ep_map);
+       partition_map |= ((u64) ioread32(&mmio_ntb->ep_map + 4)) << 32;
+       partition_map &= ~(1ULL << partition);
+
+       for (pp = 0; pp < (sizeof(partition_map) * 8); pp++) {
+               struct ntb_ctrl_regs __iomem *mmio_peer_ctrl;
+               u32 table_sz = 0;
+               int te;
+
+               if (!(partition_map & (1ULL << pp)))
+                       continue;
+
+               pci_dbg(pdev, "Processing partition %d\n", pp);
+
+               mmio_peer_ctrl = &mmio_ctrl[pp];
+
+               table_sz = ioread16(&mmio_peer_ctrl->req_id_table_size);
+               if (!table_sz) {
+                       pci_warn(pdev, "Partition %d table_sz 0\n", pp);
+                       continue;
+               }
+
+               if (table_sz > 512) {
+                       pci_warn(pdev,
+                                "Invalid Switchtec partition %d table_sz %d\n",
+                                pp, table_sz);
+                       continue;
+               }
+
+               for (te = 0; te < table_sz; te++) {
+                       u32 rid_entry;
+                       u8 devfn;
+
+                       rid_entry = ioread32(&mmio_peer_ctrl->req_id_table[te]);
+                       devfn = (rid_entry >> 1) & 0xFF;
+                       pci_dbg(pdev,
+                               "Aliasing Partition %d Proxy ID %02x.%d\n",
+                               pp, PCI_SLOT(devfn), PCI_FUNC(devfn));
+                       pci_add_dma_alias(pdev, devfn);
+               }
+       }
+
+       pci_iounmap(pdev, mmio);
+       pci_disable_device(pdev);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8531,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8532,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8533,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8534,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8535,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8536,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8543,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8544,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8545,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8546,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8551,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8552,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8553,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8554,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8555,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8556,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8561,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8562,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8563,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8564,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8565,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8566,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8571,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8572,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8573,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8574,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8575,
+                       quirk_switchtec_ntb_dma_alias);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8576,
+                       quirk_switchtec_ntb_dma_alias);
index 6f072ea..461e7fd 100644 (file)
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #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)
@@ -19,11 +18,12 @@ static void pci_stop_dev(struct pci_dev *dev)
 {
        pci_pme_active(dev, false);
 
-       if (dev->is_added) {
+       if (pci_dev_is_added(dev)) {
                device_release_driver(&dev->dev);
                pci_proc_detach_device(dev);
                pci_remove_sysfs_dev_files(dev);
-               dev->is_added = 0;
+
+               pci_dev_assign_added(dev, false);
        }
 
        if (dev->bus->self)
index a7b5c37..137bf0c 100644 (file)
@@ -80,7 +80,8 @@ EXPORT_SYMBOL_GPL(pci_disable_rom);
  * The PCI window size could be much larger than the
  * actual image size.
  */
-size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size)
+static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom,
+                              size_t size)
 {
        void __iomem *image;
        int last_image;
@@ -106,8 +107,14 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size)
                length = readw(pds + 16);
                image += length * 512;
                /* Avoid iterating through memory outside the resource window */
-               if (image > rom + size)
+               if (image >= rom + size)
                        break;
+               if (!last_image) {
+                       if (readw(image) != 0xAA55) {
+                               pci_info(pdev, "No more image in the PCI ROM\n");
+                               break;
+                       }
+               }
        } while (length && !last_image);
 
        /* never return a size larger than the PCI resource window */
index 47cd0c0..9940cc7 100644 (file)
@@ -641,7 +641,7 @@ static int ioctl_event_summary(struct switchtec_dev *stdev,
 
        for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) {
                reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id);
-               if (reg != MICROSEMI_VENDOR_ID)
+               if (reg != PCI_VENDOR_ID_MICROSEMI)
                        break;
 
                reg = ioread32(&stdev->mmio_pff_csr[i].pff_event_summary);
@@ -1203,7 +1203,7 @@ static void init_pff(struct switchtec_dev *stdev)
 
        for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) {
                reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id);
-               if (reg != MICROSEMI_VENDOR_ID)
+               if (reg != PCI_VENDOR_ID_MICROSEMI)
                        break;
        }
 
@@ -1267,7 +1267,7 @@ static int switchtec_pci_probe(struct pci_dev *pdev,
        struct switchtec_dev *stdev;
        int rc;
 
-       if (pdev->class == MICROSEMI_NTB_CLASSCODE)
+       if (pdev->class == (PCI_CLASS_BRIDGE_OTHER << 8))
                request_module_nowait("ntb_hw_switchtec");
 
        stdev = stdev_create(pdev);
@@ -1321,19 +1321,19 @@ static void switchtec_pci_remove(struct pci_dev *pdev)
 
 #define SWITCHTEC_PCI_DEVICE(device_id) \
        { \
-               .vendor     = MICROSEMI_VENDOR_ID, \
+               .vendor     = PCI_VENDOR_ID_MICROSEMI, \
                .device     = device_id, \
                .subvendor  = PCI_ANY_ID, \
                .subdevice  = PCI_ANY_ID, \
-               .class      = MICROSEMI_MGMT_CLASSCODE, \
+               .class      = (PCI_CLASS_MEMORY_OTHER << 8), \
                .class_mask = 0xFFFFFFFF, \
        }, \
        { \
-               .vendor     = MICROSEMI_VENDOR_ID, \
+               .vendor     = PCI_VENDOR_ID_MICROSEMI, \
                .device     = device_id, \
                .subvendor  = PCI_ANY_ID, \
                .subdevice  = PCI_ANY_ID, \
-               .class      = MICROSEMI_NTB_CLASSCODE, \
+               .class      = (PCI_CLASS_BRIDGE_OTHER << 8), \
                .class_mask = 0xFFFFFFFF, \
        }
 
index 8617565..4963c2e 100644 (file)
@@ -146,7 +146,7 @@ static int pci_vpd_wait(struct pci_dev *dev)
        if (!vpd->busy)
                return 0;
 
-       while (time_before(jiffies, timeout)) {
+       do {
                ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR,
                                                &status);
                if (ret < 0)
@@ -160,10 +160,13 @@ static int pci_vpd_wait(struct pci_dev *dev)
                if (fatal_signal_pending(current))
                        return -EINTR;
 
+               if (time_after(jiffies, timeout))
+                       break;
+
                usleep_range(10, max_sleep);
                if (max_sleep < 1024)
                        max_sleep *= 2;
-       }
+       } while (true);
 
        pci_warn(dev, "VPD access failed.  This is likely a firmware bug on this device.  Contact the card vendor for a firmware update\n");
        return -ETIMEDOUT;
index 3d523ca..d67f32a 100644 (file)
@@ -858,12 +858,6 @@ static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
        return 0;
 }
 
-static void asus_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
-{
-       kfree(hotplug_slot->info);
-       kfree(hotplug_slot);
-}
-
 static struct hotplug_slot_ops asus_hotplug_slot_ops = {
        .owner = THIS_MODULE,
        .get_adapter_status = asus_get_adapter_status,
@@ -905,7 +899,6 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus)
                goto error_info;
 
        asus->hotplug_slot->private = asus;
-       asus->hotplug_slot->release = &asus_cleanup_pci_hotplug;
        asus->hotplug_slot->ops = &asus_hotplug_slot_ops;
        asus_get_adapter_status(asus->hotplug_slot,
                                &asus->hotplug_slot->info->adapter_status);
@@ -1051,8 +1044,11 @@ static void asus_wmi_rfkill_exit(struct asus_wmi *asus)
         * asus_unregister_rfkill_notifier()
         */
        asus_rfkill_hotplug(asus);
-       if (asus->hotplug_slot)
+       if (asus->hotplug_slot) {
                pci_hp_deregister(asus->hotplug_slot);
+               kfree(asus->hotplug_slot->info);
+               kfree(asus->hotplug_slot);
+       }
        if (asus->hotplug_workqueue)
                destroy_workqueue(asus->hotplug_workqueue);
 
index 4c38904..a4bbf6e 100644 (file)
@@ -726,12 +726,6 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
        return 0;
 }
 
-static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
-{
-       kfree(hotplug_slot->info);
-       kfree(hotplug_slot);
-}
-
 static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
        .owner = THIS_MODULE,
        .get_adapter_status = eeepc_get_adapter_status,
@@ -758,7 +752,6 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
                goto error_info;
 
        eeepc->hotplug_slot->private = eeepc;
-       eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
        eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
        eeepc_get_adapter_status(eeepc->hotplug_slot,
                                 &eeepc->hotplug_slot->info->adapter_status);
@@ -837,8 +830,11 @@ static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
                eeepc->wlan_rfkill = NULL;
        }
 
-       if (eeepc->hotplug_slot)
+       if (eeepc->hotplug_slot) {
                pci_hp_deregister(eeepc->hotplug_slot);
+               kfree(eeepc->hotplug_slot->info);
+               kfree(eeepc->hotplug_slot);
+       }
 
        if (eeepc->bluetooth_rfkill) {
                rfkill_unregister(eeepc->bluetooth_rfkill);
index b423a30..345c0dc 100644 (file)
@@ -1010,8 +1010,7 @@ reset_info_exit:
                                                    &info, slot);
                if (!ret)
                        /* User has access, do the reset */
-                       ret = slot ? pci_try_reset_slot(vdev->pdev->slot) :
-                                    pci_try_reset_bus(vdev->pdev->bus);
+                       ret = pci_reset_bus(vdev->pdev);
 
 hot_reset_release:
                for (i--; i >= 0; i--)
@@ -1373,8 +1372,7 @@ static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev)
        }
 
        if (needs_reset)
-               ret = slot ? pci_try_reset_slot(vdev->pdev->slot) :
-                            pci_try_reset_bus(vdev->pdev->bus);
+               ret = pci_reset_bus(vdev->pdev);
 
 put_devs:
        for (i = 0; i < devs.cur_index; i++) {
index 3649a03..9c96e30 100644 (file)
@@ -1,14 +1,12 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _LINUX_DMA_DIRECTION_H
 #define _LINUX_DMA_DIRECTION_H
-/*
- * These definitions mirror those in pci.h, so they can be used
- * interchangeably with their PCI_ counterparts.
- */
+
 enum dma_data_direction {
        DMA_BIDIRECTIONAL = 0,
        DMA_TO_DEVICE = 1,
        DMA_FROM_DEVICE = 2,
        DMA_NONE = 3,
 };
+
 #endif
index 0dd1a3f..c3f1b44 100644 (file)
@@ -8,10 +8,10 @@
 #include <linux/dma-mapping.h>
 
 /* This defines the direction arg to the DMA mapping routines. */
-#define PCI_DMA_BIDIRECTIONAL  0
-#define PCI_DMA_TODEVICE       1
-#define PCI_DMA_FROMDEVICE     2
-#define PCI_DMA_NONE           3
+#define PCI_DMA_BIDIRECTIONAL  DMA_BIDIRECTIONAL
+#define PCI_DMA_TODEVICE       DMA_TO_DEVICE
+#define PCI_DMA_FROMDEVICE     DMA_FROM_DEVICE
+#define PCI_DMA_NONE           DMA_NONE
 
 static inline void *
 pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
index 243eaa5..37dab81 100644 (file)
@@ -17,6 +17,7 @@ enum pci_epc_irq_type {
        PCI_EPC_IRQ_UNKNOWN,
        PCI_EPC_IRQ_LEGACY,
        PCI_EPC_IRQ_MSI,
+       PCI_EPC_IRQ_MSIX,
 };
 
 /**
@@ -30,7 +31,11 @@ enum pci_epc_irq_type {
  *          capability register
  * @get_msi: ops to get the number of MSI interrupts allocated by the RC from
  *          the MSI capability register
- * @raise_irq: ops to raise a legacy or MSI interrupt
+ * @set_msix: ops to set the requested number of MSI-X interrupts in the
+ *          MSI-X capability register
+ * @get_msix: ops to get the number of MSI-X interrupts allocated by the RC
+ *          from the MSI-X capability register
+ * @raise_irq: ops to raise a legacy, MSI or MSI-X interrupt
  * @start: ops to start the PCI link
  * @stop: ops to stop the PCI link
  * @owner: the module owner containing the ops
@@ -48,8 +53,10 @@ struct pci_epc_ops {
                              phys_addr_t addr);
        int     (*set_msi)(struct pci_epc *epc, u8 func_no, u8 interrupts);
        int     (*get_msi)(struct pci_epc *epc, u8 func_no);
+       int     (*set_msix)(struct pci_epc *epc, u8 func_no, u16 interrupts);
+       int     (*get_msix)(struct pci_epc *epc, u8 func_no);
        int     (*raise_irq)(struct pci_epc *epc, u8 func_no,
-                            enum pci_epc_irq_type type, u8 interrupt_num);
+                            enum pci_epc_irq_type type, u16 interrupt_num);
        int     (*start)(struct pci_epc *epc);
        void    (*stop)(struct pci_epc *epc);
        struct module *owner;
@@ -95,6 +102,7 @@ struct pci_epc {
 
 #define EPC_FEATURE_NO_LINKUP_NOTIFIER         BIT(0)
 #define EPC_FEATURE_BAR_MASK                   (BIT(1) | BIT(2) | BIT(3))
+#define EPC_FEATURE_MSIX_AVAILABLE             BIT(4)
 #define EPC_FEATURE_SET_BAR(features, bar)     \
                (features |= (EPC_FEATURE_BAR_MASK & (bar << 1)))
 #define EPC_FEATURE_GET_BAR(features)          \
@@ -144,8 +152,10 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no,
                        phys_addr_t phys_addr);
 int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts);
 int pci_epc_get_msi(struct pci_epc *epc, u8 func_no);
+int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts);
+int pci_epc_get_msix(struct pci_epc *epc, u8 func_no);
 int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,
-                     enum pci_epc_irq_type type, u8 interrupt_num);
+                     enum pci_epc_irq_type type, u16 interrupt_num);
 int pci_epc_start(struct pci_epc *epc);
 void pci_epc_stop(struct pci_epc *epc);
 struct pci_epc *pci_epc_get(const char *epc_name);
index 4e77649..ec02f58 100644 (file)
@@ -119,6 +119,7 @@ struct pci_epf {
        struct pci_epf_header   *header;
        struct pci_epf_bar      bar[6];
        u8                      msi_interrupts;
+       u16                     msix_interrupts;
        u8                      func_no;
 
        struct pci_epc          *epc;
index 340029b..0543800 100644 (file)
@@ -299,6 +299,7 @@ struct pci_dev {
        u8              hdr_type;       /* PCI header type (`multi' flag masked out) */
 #ifdef CONFIG_PCIEAER
        u16             aer_cap;        /* AER capability offset */
+       struct aer_stats *aer_stats;    /* AER stats for this device */
 #endif
        u8              pcie_cap;       /* PCIe capability offset */
        u8              msi_cap;        /* MSI capability offset */
@@ -350,6 +351,7 @@ struct pci_dev {
        unsigned int    ltr_path:1;     /* Latency Tolerance Reporting
                                           supported from root to here */
 #endif
+       unsigned int    eetlp_prefix_path:1;    /* End-to-End TLP Prefix */
 
        pci_channel_state_t error_state;        /* Current connectivity state */
        struct device   dev;                    /* Generic device interface */
@@ -368,7 +370,6 @@ struct pci_dev {
        unsigned int    transparent:1;          /* Subtractive decode bridge */
        unsigned int    multifunction:1;        /* Multi-function device */
 
-       unsigned int    is_added:1;
        unsigned int    is_busmaster:1;         /* Is busmaster */
        unsigned int    no_msi:1;               /* May not use MSI */
        unsigned int    no_64bit_msi:1;         /* May only use 32-bit MSIs */
@@ -388,6 +389,7 @@ struct pci_dev {
        unsigned int    is_virtfn:1;
        unsigned int    reset_fn:1;
        unsigned int    is_hotplug_bridge:1;
+       unsigned int    shpc_managed:1;         /* SHPC owned by shpchp */
        unsigned int    is_thunderbolt:1;       /* Thunderbolt controller */
        unsigned int    __aer_firmware_first_valid:1;
        unsigned int    __aer_firmware_first:1;
@@ -819,6 +821,21 @@ struct pci_driver {
        .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \
        .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0
 
+/**
+ * PCI_DEVICE_DATA - macro used to describe a specific PCI device in very short form
+ * @vend: the vendor name (without PCI_VENDOR_ID_ prefix)
+ * @dev: the device name (without PCI_DEVICE_ID_<vend>_ prefix)
+ * @data: the driver data to be filled
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific PCI device.  The subvendor, and subdevice fields will be set
+ * to PCI_ANY_ID.
+ */
+#define PCI_DEVICE_DATA(vend, dev, data) \
+       .vendor = PCI_VENDOR_ID_##vend, .device = PCI_DEVICE_ID_##vend##_##dev, \
+       .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0, \
+       .driver_data = (kernel_ulong_t)(data)
+
 enum {
        PCI_REASSIGN_ALL_RSRC   = 0x00000001,   /* Ignore firmware setup */
        PCI_REASSIGN_ALL_BUS    = 0x00000002,   /* Reassign all bus numbers */
@@ -1089,20 +1106,17 @@ u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev,
                             enum pci_bus_speed *speed,
                             enum pcie_link_width *width);
 void pcie_print_link_status(struct pci_dev *dev);
+bool pcie_has_flr(struct pci_dev *dev);
 int pcie_flr(struct pci_dev *dev);
 int __pci_reset_function_locked(struct pci_dev *dev);
 int pci_reset_function(struct pci_dev *dev);
 int pci_reset_function_locked(struct pci_dev *dev);
 int pci_try_reset_function(struct pci_dev *dev);
 int pci_probe_reset_slot(struct pci_slot *slot);
-int pci_reset_slot(struct pci_slot *slot);
-int pci_try_reset_slot(struct pci_slot *slot);
 int pci_probe_reset_bus(struct pci_bus *bus);
-int pci_reset_bus(struct pci_bus *bus);
-int pci_try_reset_bus(struct pci_bus *bus);
+int pci_reset_bus(struct pci_dev *dev);
 void pci_reset_secondary_bus(struct pci_dev *dev);
 void pcibios_reset_secondary_bus(struct pci_dev *dev);
-int pci_reset_bridge_secondary_bus(struct pci_dev *dev);
 void pci_update_resource(struct pci_dev *dev, int resno);
 int __must_check pci_assign_resource(struct pci_dev *dev, int i);
 int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
@@ -1122,7 +1136,6 @@ int pci_enable_rom(struct pci_dev *pdev);
 void pci_disable_rom(struct pci_dev *pdev);
 void __iomem __must_check *pci_map_rom(struct pci_dev *pdev, size_t *size);
 void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom);
-size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size);
 void __iomem __must_check *pci_platform_rom(struct pci_dev *pdev, size_t *size);
 
 /* Power management related routines */
@@ -1240,6 +1253,8 @@ int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
 unsigned long pci_address_to_pio(phys_addr_t addr);
 phys_addr_t pci_pio_to_address(unsigned long pio);
 int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
+int devm_pci_remap_iospace(struct device *dev, const struct resource *res,
+                          phys_addr_t phys_addr);
 void pci_unmap_iospace(struct resource *res);
 void __iomem *devm_pci_remap_cfgspace(struct device *dev,
                                      resource_size_t offset,
@@ -1468,13 +1483,9 @@ static inline bool pcie_aspm_support_enabled(void) { return false; }
 #endif
 
 #ifdef CONFIG_PCIEAER
-void pci_no_aer(void);
 bool pci_aer_available(void);
-int pci_aer_init(struct pci_dev *dev);
 #else
-static inline void pci_no_aer(void) { }
 static inline bool pci_aer_available(void) { return false; }
-static inline int pci_aer_init(struct pci_dev *d) { return -ENODEV; }
 #endif
 
 #ifdef CONFIG_PCIE_ECRC
@@ -1876,20 +1887,9 @@ enum pci_fixup_pass {
 
 #ifdef CONFIG_PCI_QUIRKS
 void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
-int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
-int pci_dev_specific_enable_acs(struct pci_dev *dev);
 #else
 static inline void pci_fixup_device(enum pci_fixup_pass pass,
                                    struct pci_dev *dev) { }
-static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
-                                              u16 acs_flags)
-{
-       return -ENOTTY;
-}
-static inline int pci_dev_specific_enable_acs(struct pci_dev *dev)
-{
-       return -ENOTTY;
-}
 #endif
 
 void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen);
index cf5e221..a6d6650 100644 (file)
@@ -80,15 +80,12 @@ struct hotplug_slot_info {
  * @ops: pointer to the &struct hotplug_slot_ops to be used for this slot
  * @info: pointer to the &struct hotplug_slot_info for the initial values for
  * this slot.
- * @release: called during pci_hp_deregister to free memory allocated in a
- * hotplug_slot structure.
  * @private: used by the hotplug pci controller driver to store whatever it
  * needs.
  */
 struct hotplug_slot {
        struct hotplug_slot_ops         *ops;
        struct hotplug_slot_info        *info;
-       void (*release) (struct hotplug_slot *slot);
        void                            *private;
 
        /* Variables below this are for use only by the hotplug pci core. */
@@ -104,13 +101,23 @@ static inline const char *hotplug_slot_name(const struct hotplug_slot *slot)
 int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *pbus, int nr,
                      const char *name, struct module *owner,
                      const char *mod_name);
-int pci_hp_deregister(struct hotplug_slot *slot);
+int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus, int nr,
+                       const char *name, struct module *owner,
+                       const char *mod_name);
+int pci_hp_add(struct hotplug_slot *slot);
+
+void pci_hp_del(struct hotplug_slot *slot);
+void pci_hp_destroy(struct hotplug_slot *slot);
+void pci_hp_deregister(struct hotplug_slot *slot);
+
 int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
                                         struct hotplug_slot_info *info);
 
 /* use a define to avoid include chaining to get THIS_MODULE & friends */
 #define pci_hp_register(slot, pbus, devnr, name) \
        __pci_hp_register(slot, pbus, devnr, name, THIS_MODULE, KBUILD_MODNAME)
+#define pci_hp_initialize(slot, bus, nr, name) \
+       __pci_hp_initialize(slot, bus, nr, name, THIS_MODULE, KBUILD_MODNAME)
 
 /* PCI Setting Record (Type 0) */
 struct hpp_type0 {
index 2950223..99d366c 100644 (file)
 #define PCI_DEVICE_ID_COMPEX_ENET100VG4        0x0112
 
 #define PCI_VENDOR_ID_PMC_Sierra       0x11f8
+#define PCI_VENDOR_ID_MICROSEMI                0x11f8
 
 #define PCI_VENDOR_ID_RP               0x11fe
 #define PCI_DEVICE_ID_RP32INTF         0x0001
 #define PCI_DEVICE_ID_NETRONOME_NFP3200        0x3200
 #define PCI_DEVICE_ID_NETRONOME_NFP3240        0x3240
 #define PCI_DEVICE_ID_NETRONOME_NFP4000        0x4000
+#define PCI_DEVICE_ID_NETRONOME_NFP5000        0x5000
 #define PCI_DEVICE_ID_NETRONOME_NFP6000        0x6000
 #define PCI_DEVICE_ID_NETRONOME_NFP6000_VF     0x6003
 
index ec93e93..ab400af 100644 (file)
 #include <linux/pci.h>
 #include <linux/cdev.h>
 
-#define MICROSEMI_VENDOR_ID         0x11f8
-#define MICROSEMI_NTB_CLASSCODE     0x068000
-#define MICROSEMI_MGMT_CLASSCODE    0x058000
-
 #define SWITCHTEC_MRPC_PAYLOAD_SIZE 1024
 #define SWITCHTEC_MAX_PFF_CSR 48
 
index 4da87e2..ee556cc 100644 (file)
 #define  PCI_EXP_DEVCAP2_OBFF_MASK     0x000c0000 /* OBFF support mechanism */
 #define  PCI_EXP_DEVCAP2_OBFF_MSG      0x00040000 /* New message signaling */
 #define  PCI_EXP_DEVCAP2_OBFF_WAKE     0x00080000 /* Re-use WAKE# for OBFF */
+#define PCI_EXP_DEVCAP2_EE_PREFIX      0x00200000 /* End-End TLP Prefix */
 #define PCI_EXP_DEVCTL2                40      /* Device Control 2 */
 #define  PCI_EXP_DEVCTL2_COMP_TIMEOUT  0x000f  /* Completion Timeout Value */
 #define  PCI_EXP_DEVCTL2_COMP_TMOUT_DIS        0x0010  /* Completion Timeout Disable */
 #define PCI_REBAR_CTRL         8       /* control register */
 #define  PCI_REBAR_CTRL_BAR_IDX                0x00000007  /* BAR index */
 #define  PCI_REBAR_CTRL_NBAR_MASK      0x000000E0  /* # of resizable BARs */
-#define  PCI_REBAR_CTRL_NBAR_SHIFT     5           /* shift for # of BARs */
+#define  PCI_REBAR_CTRL_NBAR_SHIFT     5           /* shift for # of BARs */
 #define  PCI_REBAR_CTRL_BAR_SIZE       0x00001F00  /* BAR size */
+#define  PCI_REBAR_CTRL_BAR_SHIFT      8           /* shift for BAR size */
 
 /* Dynamic Power Allocation */
 #define PCI_DPA_CAP            4       /* capability register */
index 953cf03..cbf422e 100644 (file)
@@ -16,5 +16,8 @@
 #define PCITEST_WRITE          _IOW('P', 0x4, unsigned long)
 #define PCITEST_READ           _IOW('P', 0x5, unsigned long)
 #define PCITEST_COPY           _IOW('P', 0x6, unsigned long)
+#define PCITEST_MSIX           _IOW('P', 0x7, int)
+#define PCITEST_SET_IRQTYPE    _IOW('P', 0x8, int)
+#define PCITEST_GET_IRQTYPE    _IO('P', 0x9)
 
 #endif /* __UAPI_LINUX_PCITEST_H */
index 9074b47..af146bb 100644 (file)
 #define BILLION 1E9
 
 static char *result[] = { "NOT OKAY", "OKAY" };
+static char *irq[] = { "LEGACY", "MSI", "MSI-X" };
 
 struct pci_test {
        char            *device;
        char            barnum;
        bool            legacyirq;
        unsigned int    msinum;
+       unsigned int    msixnum;
+       int             irqtype;
+       bool            set_irqtype;
+       bool            get_irqtype;
        bool            read;
        bool            write;
        bool            copy;
@@ -65,6 +70,24 @@ static int run_test(struct pci_test *test)
                        fprintf(stdout, "%s\n", result[ret]);
        }
 
+       if (test->set_irqtype) {
+               ret = ioctl(fd, PCITEST_SET_IRQTYPE, test->irqtype);
+               fprintf(stdout, "SET IRQ TYPE TO %s:\t\t", irq[test->irqtype]);
+               if (ret < 0)
+                       fprintf(stdout, "FAILED\n");
+               else
+                       fprintf(stdout, "%s\n", result[ret]);
+       }
+
+       if (test->get_irqtype) {
+               ret = ioctl(fd, PCITEST_GET_IRQTYPE);
+               fprintf(stdout, "GET IRQ TYPE:\t\t");
+               if (ret < 0)
+                       fprintf(stdout, "FAILED\n");
+               else
+                       fprintf(stdout, "%s\n", irq[ret]);
+       }
+
        if (test->legacyirq) {
                ret = ioctl(fd, PCITEST_LEGACY_IRQ, 0);
                fprintf(stdout, "LEGACY IRQ:\t");
@@ -83,6 +106,15 @@ static int run_test(struct pci_test *test)
                        fprintf(stdout, "%s\n", result[ret]);
        }
 
+       if (test->msixnum > 0 && test->msixnum <= 2048) {
+               ret = ioctl(fd, PCITEST_MSIX, test->msixnum);
+               fprintf(stdout, "MSI-X%d:\t\t", test->msixnum);
+               if (ret < 0)
+                       fprintf(stdout, "TEST FAILED\n");
+               else
+                       fprintf(stdout, "%s\n", result[ret]);
+       }
+
        if (test->write) {
                ret = ioctl(fd, PCITEST_WRITE, test->size);
                fprintf(stdout, "WRITE (%7ld bytes):\t\t", test->size);
@@ -133,7 +165,7 @@ int main(int argc, char **argv)
        /* set default endpoint device */
        test->device = "/dev/pci-endpoint-test.0";
 
-       while ((c = getopt(argc, argv, "D:b:m:lrwcs:")) != EOF)
+       while ((c = getopt(argc, argv, "D:b:m:x:i:Ilrwcs:")) != EOF)
        switch (c) {
        case 'D':
                test->device = optarg;
@@ -151,6 +183,20 @@ int main(int argc, char **argv)
                if (test->msinum < 1 || test->msinum > 32)
                        goto usage;
                continue;
+       case 'x':
+               test->msixnum = atoi(optarg);
+               if (test->msixnum < 1 || test->msixnum > 2048)
+                       goto usage;
+               continue;
+       case 'i':
+               test->irqtype = atoi(optarg);
+               if (test->irqtype < 0 || test->irqtype > 2)
+                       goto usage;
+               test->set_irqtype = true;
+               continue;
+       case 'I':
+               test->get_irqtype = true;
+               continue;
        case 'r':
                test->read = true;
                continue;
@@ -173,6 +219,9 @@ usage:
                        "\t-D <dev>             PCI endpoint test device {default: /dev/pci-endpoint-test.0}\n"
                        "\t-b <bar num>         BAR test (bar number between 0..5)\n"
                        "\t-m <msi num>         MSI test (msi number between 1..32)\n"
+                       "\t-x <msix num>        \tMSI-X test (msix number between 1..2048)\n"
+                       "\t-i <irq type>        \tSet IRQ type (0 - Legacy, 1 - MSI, 2 - MSI-X)\n"
+                       "\t-I                   Get current IRQ type configured\n"
                        "\t-l                   Legacy IRQ test\n"
                        "\t-r                   Read buffer test\n"
                        "\t-w                   Write buffer test\n"
index 77e8c85..75ed48f 100644 (file)
@@ -16,7 +16,10 @@ echo
 echo "Interrupt tests"
 echo
 
+pcitest -i 0
 pcitest -l
+
+pcitest -i 1
 msi=1
 
 while [ $msi -lt 33 ]
@@ -26,9 +29,21 @@ do
 done
 echo
 
+pcitest -i 2
+msix=1
+
+while [ $msix -lt 2049 ]
+do
+        pcitest -x $msix
+        msix=`expr $msix + 1`
+done
+echo
+
 echo "Read Tests"
 echo
 
+pcitest -i 1
+
 pcitest -r -s 1
 pcitest -r -s 1024
 pcitest -r -s 1025