Merge tag 'pci-v4.6-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 16 Mar 2016 21:45:55 +0000 (14:45 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 16 Mar 2016 21:45:55 +0000 (14:45 -0700)
Pull PCI updates from Bjorn Helgaas:
 "PCI changes for v4.6:

  Enumeration:
   - Disable IO/MEM decoding for devices with non-compliant BARs (Bjorn Helgaas)
   - Mark Broadwell-EP Home Agent & PCU as having non-compliant BARs (Bjorn Helgaas

  Resource management:
   - Mark shadow copy of VGA ROM as IORESOURCE_PCI_FIXED (Bjorn Helgaas)
   - Don't assign or reassign immutable resources (Bjorn Helgaas)
   - Don't enable/disable ROM BAR if we're using a RAM shadow copy (Bjorn Helgaas)
   - Set ROM shadow location in arch code, not in PCI core (Bjorn Helgaas)
   - Remove arch-specific IORESOURCE_ROM_SHADOW size from sysfs (Bjorn Helgaas)
   - ia64: Use ioremap() instead of open-coded equivalent (Bjorn Helgaas)
   - ia64: Keep CPU physical (not virtual) addresses in shadow ROM resource (Bjorn Helgaas)
   - MIPS: Keep CPU physical (not virtual) addresses in shadow ROM resource (Bjorn Helgaas)
   - Remove unused IORESOURCE_ROM_COPY and IORESOURCE_ROM_BIOS_COPY (Bjorn Helgaas)
   - Don't leak memory if sysfs_create_bin_file() fails (Bjorn Helgaas)
   - rcar: Remove PCI_PROBE_ONLY handling (Lorenzo Pieralisi)
   - designware: Remove PCI_PROBE_ONLY handling (Lorenzo Pieralisi)

  Virtualization:
   - Wait for up to 1000ms after FLR reset (Alex Williamson)
   - Support SR-IOV on any function type (Kelly Zytaruk)
   - Add ACS quirk for all Cavium devices (Manish Jaggi)

  AER:
   - Rename pci_ops_aer to aer_inj_pci_ops (Bjorn Helgaas)
   - Restore pci_ops pointer while calling original pci_ops (David Daney)
   - Fix aer_inject error codes (Jean Delvare)
   - Use dev_warn() in aer_inject (Jean Delvare)
   - Log actual error causes in aer_inject (Jean Delvare)
   - Log aer_inject error injections (Jean Delvare)

  VPD:
   - Prevent VPD access for buggy devices (Babu Moger)
   - Move pci_read_vpd() and pci_write_vpd() close to other VPD code (Bjorn Helgaas)
   - Move pci_vpd_release() from header file to pci/access.c (Bjorn Helgaas)
   - Remove struct pci_vpd_ops.release function pointer (Bjorn Helgaas)
   - Rename VPD symbols to remove unnecessary "pci22" (Bjorn Helgaas)
   - Fold struct pci_vpd_pci22 into struct pci_vpd (Bjorn Helgaas)
   - Sleep rather than busy-wait for VPD access completion (Bjorn Helgaas)
   - Update VPD definitions (Hannes Reinecke)
   - Allow access to VPD attributes with size 0 (Hannes Reinecke)
   - Determine actual VPD size on first access (Hannes Reinecke)

  Generic host bridge driver:
   - Move structure definitions to separate header file (David Daney)
   - Add pci_host_common_probe(), based on gen_pci_probe() (David Daney)
   - Expose pci_host_common_probe() for use by other drivers (David Daney)

  Altera host bridge driver:
   - Fix altera_pcie_link_is_up() (Ley Foon Tan)

  Cavium ThunderX host bridge driver:
   - Add PCIe host driver for ThunderX processors (David Daney)
   - Add driver for ThunderX-pass{1,2} on-chip devices (David Daney)

  Freescale i.MX6 host bridge driver:
   - Add DT bindings to configure PHY Tx driver settings (Justin Waters)
   - Move imx6_pcie_reset_phy() near other PHY handling functions (Lucas Stach)
   - Move PHY reset into imx6_pcie_establish_link() (Lucas Stach)
   - Remove broken Gen2 workaround (Lucas Stach)
   - Move link up check into imx6_pcie_wait_for_link() (Lucas Stach)

  Freescale Layerscape host bridge driver:
   - Add "fsl,ls2085a-pcie" compatible ID (Yang Shi)

  Intel VMD host bridge driver:
   - Attach VMD resources to parent domain's resource tree (Jon Derrick)
   - Set bus resource start to 0 (Keith Busch)

  Microsoft Hyper-V host bridge driver:
   - Add fwnode_handle to x86 pci_sysdata (Jake Oshins)
   - Look up IRQ domain by fwnode_handle (Jake Oshins)
   - Add paravirtual PCI front-end for Microsoft Hyper-V VMs (Jake Oshins)

  NVIDIA Tegra host bridge driver:
   - Add pci_ops.{add,remove}_bus() callbacks (Thierry Reding)
   - Implement ->{add,remove}_bus() callbacks (Thierry Reding)
   - Remove unused struct tegra_pcie.num_ports field (Thierry Reding)
   - Track bus -> CPU mapping (Thierry Reding)
   - Remove misleading PHYS_OFFSET (Thierry Reding)

  Renesas R-Car host bridge driver:
   - Depend on ARCH_RENESAS, not ARCH_SHMOBILE (Simon Horman)

  Synopsys DesignWare host bridge driver:
   - ARC: Add PCI support (Joao Pinto)
   - Add generic dw_pcie_wait_for_link() (Joao Pinto)
   - Add default link up check if sub-driver doesn't override (Joao Pinto)
   - Add driver for prototyping kits based on ARC SDP (Joao Pinto)

  TI Keystone host bridge driver:
   - Defer probing if devm_phy_get() returns -EPROBE_DEFER (Shawn Lin)

  Xilinx AXI host bridge driver:
   - Use of_pci_get_host_bridge_resources() to parse DT (Bharat Kumar Gogada)
   - Remove dependency on ARM-specific struct hw_pci (Bharat Kumar Gogada)
   - Don't call pci_fixup_irqs() on Microblaze (Bharat Kumar Gogada)
   - Update Zynq binding with Microblaze node (Bharat Kumar Gogada)
   - microblaze: Support generic Xilinx AXI PCIe Host Bridge IP driver (Bharat Kumar Gogada)

  Xilinx NWL host bridge driver:
   - Add support for Xilinx NWL PCIe Host Controller (Bharat Kumar Gogada)

  Miscellaneous:
   - Check device_attach() return value always (Bjorn Helgaas)
   - Move pci_set_flags() from asm-generic/pci-bridge.h to linux/pci.h (Bjorn Helgaas)
   - Remove includes of empty asm-generic/pci-bridge.h (Bjorn Helgaas)
   - ARM64: Remove generated include of asm-generic/pci-bridge.h (Bjorn Helgaas)
   - Remove empty asm-generic/pci-bridge.h (Bjorn Helgaas)
   - Remove includes of asm/pci-bridge.h (Bjorn Helgaas)
   - Consolidate PCI DMA constants and interfaces in linux/pci-dma-compat.h (Bjorn Helgaas)
   - unicore32: Remove unused HAVE_ARCH_PCI_SET_DMA_MASK definition (Bjorn Helgaas)
   - Cleanup pci/pcie/Kconfig whitespace (Andreas Ziegler)
   - Include pci/hotplug Kconfig directly from pci/Kconfig (Bjorn Helgaas)
   - Include pci/pcie/Kconfig directly from pci/Kconfig (Bogicevic Sasa)
   - frv: Remove stray pci_{alloc,free}_consistent() declaration (Christoph Hellwig)
   - Move pci_dma_* helpers to common code (Christoph Hellwig)
   - Add PCI_CLASS_SERIAL_USB_DEVICE definition (Heikki Krogerus)
   - Add QEMU top-level IDs for (sub)vendor & device (Robin H. Johnson)
   - Fix broken URL for Dell biosdevname (Naga Venkata Sai Indubhaskar Jupudi)"

* tag 'pci-v4.6-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (94 commits)
  PCI: Add PCI_CLASS_SERIAL_USB_DEVICE definition
  PCI: designware: Add driver for prototyping kits based on ARC SDP
  PCI: designware: Add default link up check if sub-driver doesn't override
  PCI: designware: Add generic dw_pcie_wait_for_link()
  PCI: Cleanup pci/pcie/Kconfig whitespace
  PCI: Simplify pci_create_attr() control flow
  PCI: Don't leak memory if sysfs_create_bin_file() fails
  PCI: Simplify sysfs ROM cleanup
  PCI: Remove unused IORESOURCE_ROM_COPY and IORESOURCE_ROM_BIOS_COPY
  MIPS: Loongson 3: Keep CPU physical (not virtual) addresses in shadow ROM resource
  MIPS: Loongson 3: Use temporary struct resource * to avoid repetition
  ia64/PCI: Keep CPU physical (not virtual) addresses in shadow ROM resource
  ia64/PCI: Use ioremap() instead of open-coded equivalent
  ia64/PCI: Use temporary struct resource * to avoid repetition
  PCI: Clean up pci_map_rom() whitespace
  PCI: Remove arch-specific IORESOURCE_ROM_SHADOW size from sysfs
  PCI: thunder: Add driver for ThunderX-pass{1,2} on-chip devices
  PCI: thunder: Add PCIe host driver for ThunderX processors
  PCI: generic: Expose pci_host_common_probe() for use by other drivers
  PCI: generic: Add pci_host_common_probe(), based on gen_pci_probe()
  ...

135 files changed:
Documentation/devicetree/bindings/pci/designware-pcie.txt
Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
Documentation/devicetree/bindings/pci/pci-thunder-ecam.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/pci-thunder-pem.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/xilinx-nwl-pcie.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/xilinx-pcie.txt
MAINTAINERS
arch/alpha/include/asm/pci.h
arch/arc/Kconfig
arch/arc/include/asm/dma.h
arch/arc/include/asm/io.h
arch/arc/include/asm/pci.h [new file with mode: 0644]
arch/arc/kernel/Makefile
arch/arc/kernel/pcibios.c [new file with mode: 0644]
arch/arc/plat-axs10x/Kconfig
arch/arm/Kconfig
arch/arm/include/asm/pci.h
arch/arm64/Kconfig
arch/arm64/include/asm/Kbuild
arch/arm64/include/asm/pci.h
arch/arm64/kernel/pci.c
arch/avr32/include/asm/pci.h
arch/blackfin/Kconfig
arch/blackfin/include/asm/pci.h
arch/cris/include/asm/pci.h
arch/frv/include/asm/pci.h
arch/ia64/Kconfig
arch/ia64/include/asm/pci.h
arch/ia64/pci/fixup.c
arch/ia64/sn/kernel/io_acpi_init.c
arch/ia64/sn/kernel/io_init.c
arch/m32r/Kconfig
arch/m68k/include/asm/pci.h
arch/microblaze/Kconfig
arch/microblaze/include/asm/pci.h
arch/microblaze/pci/pci-common.c
arch/mips/Kconfig
arch/mips/include/asm/pci.h
arch/mips/pci/fixup-loongson3.c
arch/mn10300/include/asm/pci.h
arch/parisc/include/asm/pci.h
arch/powerpc/Kconfig
arch/powerpc/include/asm/pci-bridge.h
arch/powerpc/include/asm/pci.h
arch/s390/Kconfig
arch/s390/include/asm/pci.h
arch/sh/Kconfig
arch/sh/include/asm/pci.h
arch/sparc/include/asm/pci.h
arch/tile/Kconfig
arch/tile/include/asm/pci.h
arch/unicore32/include/asm/pci.h
arch/unicore32/include/mach/hardware.h
arch/x86/Kconfig
arch/x86/include/asm/pci.h
arch/x86/pci/common.c
arch/x86/pci/fixup.c
arch/x86/pci/vmd.c
arch/xtensa/Kconfig
arch/xtensa/include/asm/pci.h
drivers/ata/pata_macio.c
drivers/char/agp/uninorth-agp.c
drivers/gpu/drm/bochs/bochs_drv.c
drivers/gpu/drm/cirrus/cirrus_drv.c
drivers/gpu/drm/radeon/radeon_combios.c
drivers/ide/pdc202xx_new.c
drivers/ide/pmac.c
drivers/macintosh/macio_asic.c
drivers/misc/cxl/pci.c
drivers/net/ethernet/sun/sungem.c
drivers/net/ethernet/toshiba/spider_net.c
drivers/of/of_pci.c
drivers/parisc/Kconfig
drivers/pci/Kconfig
drivers/pci/Makefile
drivers/pci/access.c
drivers/pci/bus.c
drivers/pci/host/Kconfig
drivers/pci/host/Makefile
drivers/pci/host/pci-dra7xx.c
drivers/pci/host/pci-exynos.c
drivers/pci/host/pci-host-common.c [new file with mode: 0644]
drivers/pci/host/pci-host-common.h [new file with mode: 0644]
drivers/pci/host/pci-host-generic.c
drivers/pci/host/pci-hyperv.c [new file with mode: 0644]
drivers/pci/host/pci-imx6.c
drivers/pci/host/pci-keystone.c
drivers/pci/host/pci-layerscape.c
drivers/pci/host/pci-tegra.c
drivers/pci/host/pci-thunder-ecam.c [new file with mode: 0644]
drivers/pci/host/pci-thunder-pem.c [new file with mode: 0644]
drivers/pci/host/pcie-altera.c
drivers/pci/host/pcie-designware-plat.c [new file with mode: 0644]
drivers/pci/host/pcie-designware.c
drivers/pci/host/pcie-designware.h
drivers/pci/host/pcie-qcom.c
drivers/pci/host/pcie-rcar.c
drivers/pci/host/pcie-spear13xx.c
drivers/pci/host/pcie-xilinx-nwl.c [new file with mode: 0644]
drivers/pci/host/pcie-xilinx.c
drivers/pci/iov.c
drivers/pci/pci-label.c
drivers/pci/pci-sysfs.c
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/pcie/Kconfig
drivers/pci/pcie/aer/aer_inject.c
drivers/pci/pcie/pme.c
drivers/pci/probe.c
drivers/pci/quirks.c
drivers/pci/remove.c
drivers/pci/rom.c
drivers/pci/setup-bus.c
drivers/pci/setup-res.c
drivers/scsi/mac53c94.c
drivers/scsi/mesh.c
drivers/usb/core/hcd-pci.c
drivers/usb/gadget/udc/amd5536udc.c
drivers/usb/gadget/udc/goku_udc.c
drivers/usb/gadget/udc/net2280.c
drivers/usb/gadget/udc/pch_udc.c
drivers/video/fbdev/aty/aty128fb.c
drivers/video/fbdev/aty/radeon_base.c
drivers/video/fbdev/imsttfb.c
drivers/video/fbdev/matrox/matroxfb_base.h
drivers/video/fbdev/offb.c
drivers/virtio/virtio_pci_common.c
include/asm-generic/pci-bridge.h [deleted file]
include/asm-generic/pci-dma-compat.h [deleted file]
include/linux/ioport.h
include/linux/pci-dma-compat.h [new file with mode: 0644]
include/linux/pci.h
include/linux/pci_ids.h
sound/pci/intel8x0.c
sound/ppc/pmac.c

index 5b0853d..64f2fff 100644 (file)
@@ -28,3 +28,20 @@ Optional properties:
 - clock-names: Must include the following entries:
        - "pcie"
        - "pcie_bus"
+
+Example configuration:
+
+       pcie: pcie@0xdffff000 {
+               compatible = "snps,dw-pcie";
+               reg = <0xdffff000 0x1000>, /* Controller registers */
+                     <0xd0000000 0x2000>; /* PCI config space */
+               reg-names = "ctrlreg", "config";
+               #address-cells = <3>;
+               #size-cells = <2>;
+               device_type = "pci";
+               ranges = <0x81000000 0 0x00000000 0xde000000 0 0x00010000
+                         0x82000000 0 0xd0400000 0xd0400000 0 0x0d000000>;
+               interrupts = <25>, <24>;
+               #interrupt-cells = <1>;
+               num-lanes = <1>;
+       };
index 6fbba53..3be80c6 100644 (file)
@@ -13,6 +13,13 @@ Required properties:
 - clock-names: Must include the following additional entries:
        - "pcie_phy"
 
+Optional properties:
+- fsl,tx-deemph-gen1: Gen1 De-emphasis value. Default: 0
+- fsl,tx-deemph-gen2-3p5db: Gen2 (3.5db) De-emphasis value. Default: 0
+- fsl,tx-deemph-gen2-6db: Gen2 (6db) De-emphasis value. Default: 20
+- fsl,tx-swing-full: Gen2 TX SWING FULL value. Default: 127
+- fsl,tx-swing-low: TX launch amplitude swing_low value. Default: 127
+
 Example:
 
        pcie@0x01000000 {
diff --git a/Documentation/devicetree/bindings/pci/pci-thunder-ecam.txt b/Documentation/devicetree/bindings/pci/pci-thunder-ecam.txt
new file mode 100644 (file)
index 0000000..f478874
--- /dev/null
@@ -0,0 +1,30 @@
+* ThunderX PCI host controller for pass-1.x silicon
+
+Firmware-initialized PCI host controller to on-chip devices found on
+some Cavium ThunderX processors.  These devices have ECAM-based config
+access, but the BARs are all at fixed addresses.  We handle the fixed
+addresses by synthesizing Enhanced Allocation (EA) capabilities for
+these devices.
+
+The properties and their meanings are identical to those described in
+host-generic-pci.txt except as listed below.
+
+Properties of the host controller node that differ from
+host-generic-pci.txt:
+
+- compatible     : Must be "cavium,pci-host-thunder-ecam"
+
+Example:
+
+       pcie@84b000000000 {
+               compatible = "cavium,pci-host-thunder-ecam";
+               device_type = "pci";
+               msi-parent = <&its>;
+               msi-map = <0 &its 0x30000 0x10000>;
+               bus-range = <0 31>;
+               #size-cells = <2>;
+               #address-cells = <3>;
+               #stream-id-cells = <1>;
+               reg = <0x84b0 0x00000000 0 0x02000000>;  /* Configuration space */
+               ranges = <0x03000000 0x8180 0x00000000 0x8180 0x00000000 0x80 0x00000000>; /* mem ranges */
+       };
diff --git a/Documentation/devicetree/bindings/pci/pci-thunder-pem.txt b/Documentation/devicetree/bindings/pci/pci-thunder-pem.txt
new file mode 100644 (file)
index 0000000..f131fae
--- /dev/null
@@ -0,0 +1,43 @@
+* ThunderX PEM PCIe host controller
+
+Firmware-initialized PCI host controller found on some Cavium
+ThunderX processors.
+
+The properties and their meanings are identical to those described in
+host-generic-pci.txt except as listed below.
+
+Properties of the host controller node that differ from
+host-generic-pci.txt:
+
+- compatible     : Must be "cavium,pci-host-thunder-pem"
+
+- reg            : Two entries: First the configuration space for down
+                   stream devices base address and size, as accessed
+                   from the parent bus. Second, the register bank of
+                   the PEM device PCIe bridge.
+
+Example:
+
+    pci@87e0,c2000000 {
+       compatible = "cavium,pci-host-thunder-pem";
+       device_type = "pci";
+       msi-parent = <&its>;
+       msi-map = <0 &its 0x10000 0x10000>;
+       bus-range = <0x8f 0xc7>;
+       #size-cells = <2>;
+       #address-cells = <3>;
+
+       reg = <0x8880 0x8f000000 0x0 0x39000000>,  /* Configuration space */
+             <0x87e0 0xc2000000 0x0 0x00010000>; /* PEM space */
+       ranges = <0x01000000 0x00 0x00020000 0x88b0 0x00020000 0x00 0x00010000>, /* I/O */
+                <0x03000000 0x00 0x10000000 0x8890 0x10000000 0x0f 0xf0000000>, /* mem64 */
+                <0x43000000 0x10 0x00000000 0x88a0 0x00000000 0x10 0x00000000>, /* mem64-pref */
+                <0x03000000 0x87e0 0xc2f00000 0x87e0 0xc2000000 0x00 0x00100000>; /* mem64 PEM BAR4 */
+
+       #interrupt-cells = <1>;
+       interrupt-map-mask = <0 0 0 7>;
+       interrupt-map = <0 0 0 1 &gic0 0 0 0 24 4>, /* INTA */
+                       <0 0 0 2 &gic0 0 0 0 25 4>, /* INTB */
+                       <0 0 0 3 &gic0 0 0 0 26 4>, /* INTC */
+                       <0 0 0 4 &gic0 0 0 0 27 4>; /* INTD */
+    };
diff --git a/Documentation/devicetree/bindings/pci/xilinx-nwl-pcie.txt b/Documentation/devicetree/bindings/pci/xilinx-nwl-pcie.txt
new file mode 100644 (file)
index 0000000..337fc97
--- /dev/null
@@ -0,0 +1,68 @@
+* Xilinx NWL PCIe Root Port Bridge DT description
+
+Required properties:
+- compatible: Should contain "xlnx,nwl-pcie-2.11"
+- #address-cells: Address representation for root ports, set to <3>
+- #size-cells: Size representation for root ports, set to <2>
+- #interrupt-cells: specifies the number of cells needed to encode an
+       interrupt source. The value must be 1.
+- reg: Should contain Bridge, PCIe Controller registers location,
+       configuration space, and length
+- reg-names: Must include the following entries:
+       "breg": bridge registers
+       "pcireg": PCIe controller registers
+       "cfg": configuration space region
+- device_type: must be "pci"
+- interrupts: Should contain NWL PCIe interrupt
+- interrupt-names: Must include the following entries:
+       "msi1, msi0": interrupt asserted when MSI is received
+       "intx": interrupt asserted when a legacy interrupt is received
+       "misc": interrupt asserted when miscellaneous is received
+- interrupt-map-mask and interrupt-map: standard PCI properties to define the
+       mapping of the PCI interface to interrupt numbers.
+- ranges: ranges for the PCI memory regions (I/O space region is not
+       supported by hardware)
+       Please refer to the standard PCI bus binding document for a more
+       detailed explanation
+- msi-controller: indicates that this is MSI controller node
+- msi-parent:  MSI parent of the root complex itself
+- legacy-interrupt-controller: Interrupt controller device node for Legacy interrupts
+       - interrupt-controller: identifies the node as an interrupt controller
+       - #interrupt-cells: should be set to 1
+       - #address-cells: specifies the number of cells needed to encode an
+               address. The value must be 0.
+
+
+Example:
+++++++++
+
+nwl_pcie: pcie@fd0e0000 {
+       #address-cells = <3>;
+       #size-cells = <2>;
+       compatible = "xlnx,nwl-pcie-2.11";
+       #interrupt-cells = <1>;
+       msi-controller;
+       device_type = "pci";
+       interrupt-parent = <&gic>;
+       interrupts = <0 114 4>, <0 115 4>, <0 116 4>, <0 117 4>, <0 118 4>;
+       interrupt-names = "msi0", "msi1", "intx", "dummy", "misc";
+       interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+       interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc 0x1>,
+                       <0x0 0x0 0x0 0x2 &pcie_intc 0x2>,
+                       <0x0 0x0 0x0 0x3 &pcie_intc 0x3>,
+                       <0x0 0x0 0x0 0x4 &pcie_intc 0x4>;
+
+       msi-parent = <&nwl_pcie>;
+       reg = <0x0 0xfd0e0000 0x0 0x1000>,
+             <0x0 0xfd480000 0x0 0x1000>,
+             <0x0 0xe0000000 0x0 0x1000000>;
+       reg-names = "breg", "pcireg", "cfg";
+       ranges = <0x02000000 0x00000000 0xe1000000 0x00000000 0xe1000000 0 0x0f000000>;
+
+       pcie_intc: legacy-interrupt-controller {
+               interrupt-controller;
+               #address-cells = <0>;
+               #interrupt-cells = <1>;
+       };
+
+};
index 02f979a..fd57a81 100644 (file)
@@ -17,7 +17,7 @@ Required properties:
        Please refer to the standard PCI bus binding document for a more
        detailed explanation
 
-Optional properties:
+Optional properties for Zynq/Microblaze:
 - bus-range: PCI bus numbers covered
 
 Interrupt controller child node
@@ -38,13 +38,13 @@ the four INTx interrupts in ISR and route them to this domain.
 
 Example:
 ++++++++
-
+Zynq:
        pci_express: axi-pcie@50000000 {
                #address-cells = <3>;
                #size-cells = <2>;
                #interrupt-cells = <1>;
                compatible = "xlnx,axi-pcie-host-1.00.a";
-               reg = < 0x50000000 0x10000000 >;
+               reg = < 0x50000000 0x1000000 >;
                device_type = "pci";
                interrupts = < 0 52 4 >;
                interrupt-map-mask = <0 0 0 7>;
@@ -60,3 +60,29 @@ Example:
                        #interrupt-cells = <1>;
                };
        };
+
+
+Microblaze:
+       pci_express: axi-pcie@10000000 {
+               #address-cells = <3>;
+               #size-cells = <2>;
+               #interrupt-cells = <1>;
+               compatible = "xlnx,axi-pcie-host-1.00.a";
+               reg = <0x10000000 0x4000000>;
+               device_type = "pci";
+               interrupt-parent = <&microblaze_0_intc>;
+               interrupts = <1 2>;
+               interrupt-map-mask = <0 0 0 7>;
+               interrupt-map = <0 0 0 1 &pcie_intc 1>,
+                               <0 0 0 2 &pcie_intc 2>,
+                               <0 0 0 3 &pcie_intc 3>,
+                               <0 0 0 4 &pcie_intc 4>;
+               ranges = <0x02000000 0x00000000 0x80000000 0x80000000 0x00000000 0x10000000>;
+
+               pcie_intc: interrupt-controller {
+                       interrupt-controller;
+                       #address-cells = <0>;
+                       #interrupt-cells = <1>;
+               };
+
+       };
index 860e306..25d7cd7 100644 (file)
@@ -5205,6 +5205,7 @@ F:        arch/x86/kernel/cpu/mshyperv.c
 F:     drivers/hid/hid-hyperv.c
 F:     drivers/hv/
 F:     drivers/input/serio/hyperv-keyboard.c
+F:     drivers/pci/host/pci-hyperv.c
 F:     drivers/net/hyperv/
 F:     drivers/scsi/storvsc_drv.c
 F:     drivers/video/fbdev/hyperv_fb.c
@@ -8383,12 +8384,20 @@ L:      linux-pci@vger.kernel.org
 S:     Maintained
 F:     drivers/pci/host/*designware*
 
+PCI DRIVER FOR SYNOPSYS PROTOTYPING DEVICE
+M:     Joao Pinto <jpinto@synopsys.com>
+L:     linux-pci@vger.kernel.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/pci/designware-pcie.txt
+F:     drivers/pci/host/pcie-designware-plat.c
+
 PCI DRIVER FOR GENERIC OF HOSTS
 M:     Will Deacon <will.deacon@arm.com>
 L:     linux-pci@vger.kernel.org
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     Documentation/devicetree/bindings/pci/host-generic-pci.txt
+F:     drivers/pci/host/pci-host-common.c
 F:     drivers/pci/host/pci-host-generic.c
 
 PCI DRIVER FOR INTEL VOLUME MANAGEMENT DEVICE (VMD)
@@ -8434,6 +8443,14 @@ L:     linux-arm-msm@vger.kernel.org
 S:     Maintained
 F:     drivers/pci/host/*qcom*
 
+PCIE DRIVER FOR CAVIUM THUNDERX
+M:     David Daney <david.daney@cavium.com>
+L:     linux-pci@vger.kernel.org
+L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S:     Supported
+F:     Documentation/devicetree/bindings/pci/pci-thunder-*
+F:     drivers/pci/host/pci-thunder-*
+
 PCMCIA SUBSYSTEM
 P:     Linux PCMCIA Team
 L:     linux-pcmcia@lists.infradead.org
index 98f2eee..a06c24b 100644 (file)
@@ -7,7 +7,6 @@
 #include <linux/dma-mapping.h>
 #include <linux/scatterlist.h>
 #include <asm/machvec.h>
-#include <asm-generic/pci-bridge.h>
 
 /*
  * The following structure is used to manage multiple PCI busses.
@@ -66,13 +65,6 @@ extern void pcibios_set_master(struct pci_dev *dev);
    decisions.  */
 #define PCI_DMA_BUS_IS_PHYS  0
 
-#ifdef CONFIG_PCI
-
-/* implement the pci_ DMA API in terms of the generic device dma_ one */
-#include <asm-generic/pci-dma-compat.h>
-
-#endif
-
 /* TODO: integrate with include/asm-generic/pci.h ? */
 static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 {
index 8a188bc..07a5cb9 100644 (file)
@@ -17,6 +17,7 @@ config ARC
        select GENERIC_FIND_FIRST_BIT
        # for now, we don't need GENERIC_IRQ_PROBE, CONFIG_GENERIC_IRQ_CHIP
        select GENERIC_IRQ_SHOW
+       select GENERIC_PCI_IOMAP
        select GENERIC_PENDING_IRQ if SMP
        select GENERIC_SMP_IDLE_THREAD
        select HAVE_ARCH_KGDB
@@ -37,6 +38,9 @@ config ARC
        select PERF_USE_VMALLOC
        select HAVE_DEBUG_STACKOVERFLOW
 
+config MIGHT_HAVE_PCI
+       bool
+
 config TRACE_IRQFLAGS_SUPPORT
        def_bool y
 
@@ -569,6 +573,28 @@ config FORCE_MAX_ZONEORDER
 
 source "net/Kconfig"
 source "drivers/Kconfig"
+
+menu "Bus Support"
+
+config PCI
+       bool "PCI support" if MIGHT_HAVE_PCI
+       help
+         PCI is the name of a bus system, i.e., the way the CPU talks to
+         the other stuff inside your box.  Find out if your board/platform
+         has PCI.
+
+         Note: PCIe support for Synopsys Device will be available only
+         when HAPS DX is configured with PCIe RC bitmap. If you have PCI,
+         say Y, otherwise N.
+
+config PCI_SYSCALL
+       def_bool PCI
+
+source "drivers/pci/Kconfig"
+source "drivers/pci/pcie/Kconfig"
+
+endmenu
+
 source "fs/Kconfig"
 source "arch/arc/Kconfig.debug"
 source "security/Kconfig"
index ca7c451..01e47a6 100644 (file)
 #define ASM_ARC_DMA_H
 
 #define MAX_DMA_ADDRESS 0xC0000000
+#ifdef CONFIG_PCI
+extern int isa_dma_bridge_buggy;
+#else
+#define isa_dma_bridge_buggy   0
+#endif
 
 #endif
index 694ece8..947bf0c 100644 (file)
 extern void __iomem *ioremap(unsigned long physaddr, unsigned long size);
 extern void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size,
                                  unsigned long flags);
+static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
+{
+       return (void __iomem *)port;
+}
+
+static inline void ioport_unmap(void __iomem *addr)
+{
+}
+
 extern void iounmap(const void __iomem *addr);
 
 #define ioremap_nocache(phy, sz)       ioremap(phy, sz)
diff --git a/arch/arc/include/asm/pci.h b/arch/arc/include/asm/pci.h
new file mode 100644 (file)
index 0000000..ba56c23
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _ASM_ARC_PCI_H
+#define _ASM_ARC_PCI_H
+
+#ifdef __KERNEL__
+#include <linux/ioport.h>
+
+#define PCIBIOS_MIN_IO 0x100
+#define PCIBIOS_MIN_MEM 0x100000
+
+#define pcibios_assign_all_busses()    1
+/*
+ * The PCI address space does equal the physical memory address space.
+ * The networking and block device layers use this boolean for bounce
+ * buffer decisions.
+ */
+#define PCI_DMA_BUS_IS_PHYS    1
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_ARC_PCI_H */
index e7f3625..1bc2036 100644 (file)
@@ -12,6 +12,7 @@ obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o process.o devtree.o
 obj-y  += signal.o traps.o sys.o troubleshoot.o stacktrace.o disasm.o clk.o
 obj-$(CONFIG_ISA_ARCOMPACT)            += entry-compact.o intc-compact.o
 obj-$(CONFIG_ISA_ARCV2)                        += entry-arcv2.o intc-arcv2.o
+obj-$(CONFIG_PCI)                      += pcibios.o
 
 obj-$(CONFIG_MODULES)                  += arcksyms.o module.o
 obj-$(CONFIG_SMP)                      += smp.o
diff --git a/arch/arc/kernel/pcibios.c b/arch/arc/kernel/pcibios.c
new file mode 100644 (file)
index 0000000..72e1d73
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014-2015 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/pci.h>
+
+/*
+ * We don't have to worry about legacy ISA devices, so nothing to do here
+ */
+resource_size_t pcibios_align_resource(void *data, const struct resource *res,
+                               resource_size_t size, resource_size_t align)
+{
+       return res->start;
+}
+
+void pcibios_fixup_bus(struct pci_bus *bus)
+{
+}
index d475f9d..426ac4b 100644 (file)
@@ -11,6 +11,7 @@ menuconfig ARC_PLAT_AXS10X
        select DW_APB_ICTL
        select GPIO_DWAPB
        select OF_GPIO
+       select MIGHT_HAVE_PCI
        select GENERIC_IRQ_CHIP
        select ARCH_REQUIRE_GPIOLIB
        help
index 4f799e5..1d00da1 100644 (file)
@@ -1212,7 +1212,6 @@ config PCI_HOST_ITE8152
        select DMABOUNCE
 
 source "drivers/pci/Kconfig"
-source "drivers/pci/pcie/Kconfig"
 
 source "drivers/pcmcia/Kconfig"
 
index a563544..057d381 100644 (file)
@@ -2,9 +2,6 @@
 #define ASMARM_PCI_H
 
 #ifdef __KERNEL__
-#include <asm-generic/pci-dma-compat.h>
-#include <asm-generic/pci-bridge.h>
-
 #include <asm/mach/pci.h> /* for pci_sys_data */
 
 extern unsigned long pcibios_min_io;
@@ -41,5 +38,4 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 }
 
 #endif /* __KERNEL__ */
 #endif
index cf118d9..b3f2522 100644 (file)
@@ -235,8 +235,6 @@ config PCI_SYSCALL
        def_bool PCI
 
 source "drivers/pci/Kconfig"
-source "drivers/pci/pcie/Kconfig"
-source "drivers/pci/hotplug/Kconfig"
 
 endmenu
 
index 70fd9ff..cff532a 100644 (file)
@@ -1,5 +1,3 @@
-
-
 generic-y += bug.h
 generic-y += bugs.h
 generic-y += checksum.h
@@ -31,7 +29,6 @@ generic-y += msgbuf.h
 generic-y += msi.h
 generic-y += mutex.h
 generic-y += pci.h
-generic-y += pci-bridge.h
 generic-y += poll.h
 generic-y += preempt.h
 generic-y += resource.h
index b008a72..b9a7ba9 100644 (file)
@@ -7,8 +7,6 @@
 #include <linux/dma-mapping.h>
 
 #include <asm/io.h>
-#include <asm-generic/pci-bridge.h>
-#include <asm-generic/pci-dma-compat.h>
 
 #define PCIBIOS_MIN_IO         0x1000
 #define PCIBIOS_MIN_MEM                0
index b3d098b..c72de66 100644 (file)
@@ -19,8 +19,6 @@
 #include <linux/of_platform.h>
 #include <linux/slab.h>
 
-#include <asm/pci-bridge.h>
-
 /*
  * Called after each bus is probed, but before its children are examined
  */
index a32a023..0f5f134 100644 (file)
@@ -5,6 +5,4 @@
 
 #define PCI_DMA_BUS_IS_PHYS    (1)
 
-#include <asm-generic/pci-dma-compat.h>
-
 #endif /* __ASM_AVR32_PCI_H__ */
index af76634..a63c122 100644 (file)
@@ -1233,8 +1233,6 @@ source "drivers/pci/Kconfig"
 
 source "drivers/pcmcia/Kconfig"
 
-source "drivers/pci/hotplug/Kconfig"
-
 endmenu
 
 menu "Executable file formats"
index 14efc0d..11ea1cb 100644 (file)
@@ -4,7 +4,6 @@
 #define _ASM_BFIN_PCI_H
 
 #include <linux/scatterlist.h>
-#include <asm-generic/pci-dma-compat.h>
 #include <asm-generic/pci.h>
 
 #define PCIBIOS_MIN_IO 0x00001000
index c15b4b4..b1b289d 100644 (file)
@@ -48,9 +48,6 @@ extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
 
 #endif /* __KERNEL__ */
 
-/* implement the pci_ DMA API in terms of the generic device dma_ one */
-#include <asm-generic/pci-dma-compat.h>
-
 /* generic pci stuff */
 #include <asm-generic/pci.h>
 
index e43d22c..809cfc6 100644 (file)
@@ -15,7 +15,6 @@
 
 #include <linux/mm.h>
 #include <linux/scatterlist.h>
-#include <asm-generic/pci-dma-compat.h>
 #include <asm-generic/pci.h>
 
 struct pci_dev;
@@ -32,12 +31,6 @@ extern void consistent_sync_page(struct page *page, unsigned long offset,
                                 size_t size, int direction);
 #endif
 
-extern void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
-                                 dma_addr_t *dma_handle);
-
-extern void pci_free_consistent(struct pci_dev *hwdev, size_t size,
-                               void *vaddr, dma_addr_t dma_handle);
-
 /* Return the index of the PCI controller for device PDEV. */
 #define pci_controller_num(PDEV)       (0)
 
index fb0515e..b534eba 100644 (file)
@@ -574,12 +574,8 @@ config PCI_DOMAINS
 config PCI_SYSCALL
        def_bool PCI
 
-source "drivers/pci/pcie/Kconfig"
-
 source "drivers/pci/Kconfig"
 
-source "drivers/pci/hotplug/Kconfig"
-
 source "drivers/pcmcia/Kconfig"
 
 endmenu
index 07039d1..c0835b0 100644 (file)
@@ -50,8 +50,6 @@ struct pci_dev;
 extern unsigned long ia64_max_iommu_merge_mask;
 #define PCI_DMA_BUS_IS_PHYS    (ia64_max_iommu_merge_mask == ~0UL)
 
-#include <asm-generic/pci-dma-compat.h>
-
 #define HAVE_PCI_MMAP
 extern int pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma,
                                enum pci_mmap_state mmap_state, int write_combine);
index fc505d5..41caa99 100644 (file)
  *
  * The standard boot ROM sequence for an x86 machine uses the BIOS
  * to select an initial video card for boot display. This boot video
- * card will have it's BIOS copied to C0000 in system RAM.
+ * card will have its BIOS copied to 0xC0000 in system RAM.
  * IORESOURCE_ROM_SHADOW is used to associate the boot video
  * card with this copy. On laptops this copy has to be used since
  * the main ROM may be compressed or combined with another image.
  * See pci_map_rom() for use of this flag. Before marking the device
  * with IORESOURCE_ROM_SHADOW check if a vga_default_device is already set
- * by either arch cde or vga-arbitration, if so only apply the fixup to this
- * already determined primary video card.
+ * by either arch code or vga-arbitration; if so only apply the fixup to this
+ * already-determined primary video card.
  */
 
 static void pci_fixup_video(struct pci_dev *pdev)
@@ -32,6 +32,7 @@ static void pci_fixup_video(struct pci_dev *pdev)
        struct pci_dev *bridge;
        struct pci_bus *bus;
        u16 config;
+       struct resource *res;
 
        if ((strcmp(ia64_platform_name, "dig") != 0)
            && (strcmp(ia64_platform_name, "hpzx1")  != 0))
@@ -61,8 +62,18 @@ static void pci_fixup_video(struct pci_dev *pdev)
        if (!vga_default_device() || pdev == vga_default_device()) {
                pci_read_config_word(pdev, PCI_COMMAND, &config);
                if (config & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
-                       pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
-                       dev_printk(KERN_DEBUG, &pdev->dev, "Video device with shadowed ROM\n");
+                       res = &pdev->resource[PCI_ROM_RESOURCE];
+
+                       pci_disable_rom(pdev);
+                       if (res->parent)
+                               release_resource(res);
+
+                       res->start = 0xC0000;
+                       res->end = res->start + 0x20000 - 1;
+                       res->flags = IORESOURCE_MEM | IORESOURCE_ROM_SHADOW |
+                                    IORESOURCE_PCI_FIXED;
+                       dev_info(&pdev->dev, "Video device with shadowed ROM at %pR\n",
+                                res);
                }
        }
 }
index 0640739..231234c 100644 (file)
@@ -429,7 +429,8 @@ sn_acpi_slot_fixup(struct pci_dev *dev)
        void __iomem *addr;
        struct pcidev_info *pcidev_info = NULL;
        struct sn_irq_info *sn_irq_info = NULL;
-       size_t image_size, size;
+       struct resource *res;
+       size_t size;
 
        if (sn_acpi_get_pcidev_info(dev, &pcidev_info, &sn_irq_info)) {
                panic("%s:  Failure obtaining pcidev_info for %s\n",
@@ -443,17 +444,20 @@ sn_acpi_slot_fixup(struct pci_dev *dev)
                 * of the shadowed copy, and the actual length of the ROM image.
                 */
                size = pci_resource_len(dev, PCI_ROM_RESOURCE);
-               addr = ioremap(pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE],
-                              size);
-               image_size = pci_get_rom_size(dev, addr, size);
-               dev->resource[PCI_ROM_RESOURCE].start = (unsigned long) addr;
-               dev->resource[PCI_ROM_RESOURCE].end =
-                                       (unsigned long) addr + image_size - 1;
-               dev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_BIOS_COPY;
+
+               res = &dev->resource[PCI_ROM_RESOURCE];
+
+               pci_disable_rom(dev);
+               if (res->parent)
+                       release_resource(res);
+
+               res->start = pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE];
+               res->end = res->start + size - 1;
+               res->flags = IORESOURCE_MEM | IORESOURCE_ROM_SHADOW |
+                            IORESOURCE_PCI_FIXED;
        }
        sn_pci_fixup_slot(dev, pcidev_info, sn_irq_info);
 }
-
 EXPORT_SYMBOL(sn_acpi_slot_fixup);
 
 
index 1be65eb..c15a41e 100644 (file)
@@ -150,7 +150,8 @@ void
 sn_io_slot_fixup(struct pci_dev *dev)
 {
        int idx;
-       unsigned long addr, end, size, start;
+       struct resource *res;
+       unsigned long addr, size;
        struct pcidev_info *pcidev_info;
        struct sn_irq_info *sn_irq_info;
        int status;
@@ -175,55 +176,41 @@ sn_io_slot_fixup(struct pci_dev *dev)
 
        /* Copy over PIO Mapped Addresses */
        for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) {
-
-               if (!pcidev_info->pdi_pio_mapped_addr[idx]) {
+               if (!pcidev_info->pdi_pio_mapped_addr[idx])
                        continue;
-               }
 
-               start = dev->resource[idx].start;
-               end = dev->resource[idx].end;
-               size = end - start;
-               if (size == 0) {
+               res = &dev->resource[idx];
+
+               size = res->end - res->start;
+               if (size == 0)
                        continue;
-               }
-               addr = pcidev_info->pdi_pio_mapped_addr[idx];
-               addr = ((addr << 4) >> 4) | __IA64_UNCACHED_OFFSET;
-               dev->resource[idx].start = addr;
-               dev->resource[idx].end = addr + size;
+
+               res->start = pcidev_info->pdi_pio_mapped_addr[idx];
+               res->end = addr + size;
 
                /*
                 * if it's already in the device structure, remove it before
                 * inserting
                 */
-               if (dev->resource[idx].parent && dev->resource[idx].parent->child)
-                       release_resource(&dev->resource[idx]);
+               if (res->parent && res->parent->child)
+                       release_resource(res);
 
-               if (dev->resource[idx].flags & IORESOURCE_IO)
-                       insert_resource(&ioport_resource, &dev->resource[idx]);
+               if (res->flags & IORESOURCE_IO)
+                       insert_resource(&ioport_resource, res);
                else
-                       insert_resource(&iomem_resource, &dev->resource[idx]);
+                       insert_resource(&iomem_resource, res);
                /*
-                * If ROM, set the actual ROM image size, and mark as
-                * shadowed in PROM.
+                * If ROM, mark as shadowed in PROM.
                 */
                if (idx == PCI_ROM_RESOURCE) {
-                       size_t image_size;
-                       void __iomem *rom;
-
-                       rom = ioremap(pci_resource_start(dev, PCI_ROM_RESOURCE),
-                                     size + 1);
-                       image_size = pci_get_rom_size(dev, rom, size + 1);
-                       dev->resource[PCI_ROM_RESOURCE].end =
-                               dev->resource[PCI_ROM_RESOURCE].start +
-                               image_size - 1;
-                       dev->resource[PCI_ROM_RESOURCE].flags |=
-                                                IORESOURCE_ROM_BIOS_COPY;
+                       pci_disable_rom(dev);
+                       res->flags = IORESOURCE_MEM | IORESOURCE_ROM_SHADOW |
+                                    IORESOURCE_PCI_FIXED;
                }
        }
 
        sn_pci_fixup_slot(dev, pcidev_info, sn_irq_info);
 }
-
 EXPORT_SYMBOL(sn_io_slot_fixup);
 
 /*
index 2841c0a..c82b292 100644 (file)
@@ -387,8 +387,6 @@ config ISA
 
 source "drivers/pcmcia/Kconfig"
 
-source "drivers/pci/hotplug/Kconfig"
-
 endmenu
 
 
index 848c3df..3a3dbcf 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef _ASM_M68K_PCI_H
 #define _ASM_M68K_PCI_H
 
-#include <asm-generic/pci-dma-compat.h>
 #include <asm-generic/pci.h>
 
 /* The PCI address space does equal the physical memory
index 53b69de..3d793b5 100644 (file)
@@ -267,6 +267,9 @@ config PCI
 config PCI_DOMAINS
        def_bool PCI
 
+config PCI_DOMAINS_GENERIC
+       def_bool PCI_DOMAINS
+
 config PCI_SYSCALL
        def_bool PCI
 
index dc9eb66..fc3ecb5 100644 (file)
@@ -22,8 +22,6 @@
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
 
-#include <asm-generic/pci-dma-compat.h>
-
 #define PCIBIOS_MIN_IO         0x1000
 #define PCIBIOS_MIN_MEM                0x10000000
 
index ae838ed..35654be 100644 (file)
@@ -123,17 +123,6 @@ unsigned long pci_address_to_pio(phys_addr_t address)
 }
 EXPORT_SYMBOL_GPL(pci_address_to_pio);
 
-/*
- * Return the domain number for this bus.
- */
-int pci_domain_nr(struct pci_bus *bus)
-{
-       struct pci_controller *hose = pci_bus_to_host(bus);
-
-       return hose->global_number;
-}
-EXPORT_SYMBOL(pci_domain_nr);
-
 /* This routine is meant to be used early during boot, when the
  * PCI bus numbers have not yet been assigned, and you need to
  * issue PCI config cycles to an OF device.
@@ -863,26 +852,10 @@ void pcibios_setup_bus_devices(struct pci_bus *bus)
 
 void pcibios_fixup_bus(struct pci_bus *bus)
 {
-       /* When called from the generic PCI probe, read PCI<->PCI bridge
-        * bases. This is -not- called when generating the PCI tree from
-        * the OF device-tree.
-        */
-       if (bus->self != NULL)
-               pci_read_bridge_bases(bus);
-
-       /* Now fixup the bus bus */
-       pcibios_setup_bus_self(bus);
-
-       /* Now fixup devices on that bus */
-       pcibios_setup_bus_devices(bus);
+       /* nothing to do */
 }
 EXPORT_SYMBOL(pcibios_fixup_bus);
 
-static int skip_isa_ioresource_align(struct pci_dev *dev)
-{
-       return 0;
-}
-
 /*
  * We need to avoid collisions with `mirrored' VGA ports
  * and other strange ISA hardware, so we always want the
@@ -899,20 +872,18 @@ static int skip_isa_ioresource_align(struct pci_dev *dev)
 resource_size_t pcibios_align_resource(void *data, const struct resource *res,
                                resource_size_t size, resource_size_t align)
 {
-       struct pci_dev *dev = data;
-       resource_size_t start = res->start;
-
-       if (res->flags & IORESOURCE_IO) {
-               if (skip_isa_ioresource_align(dev))
-                       return start;
-               if (start & 0x300)
-                       start = (start + 0x3ff) & ~0x3ff;
-       }
-
-       return start;
+       return res->start;
 }
 EXPORT_SYMBOL(pcibios_align_resource);
 
+int pcibios_add_device(struct pci_dev *dev)
+{
+       dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
+
+       return 0;
+}
+EXPORT_SYMBOL(pcibios_add_device);
+
 /*
  * Reparent resource children of pr that conflict with res
  * under res, and make res replace those children.
@@ -1333,13 +1304,6 @@ static void pcibios_setup_phb_resources(struct pci_controller *hose,
                 (unsigned long)hose->io_base_virt - _IO_BASE);
 }
 
-struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
-{
-       struct pci_controller *hose = bus->sysdata;
-
-       return of_node_get(hose->dn);
-}
-
 static void pcibios_scan_phb(struct pci_controller *hose)
 {
        LIST_HEAD(resources);
index a65eacf..7c4a4ce 100644 (file)
@@ -2871,8 +2871,6 @@ config PCI_DOMAINS
 
 source "drivers/pci/Kconfig"
 
-source "drivers/pci/pcie/Kconfig"
-
 #
 # ISA support is now enabled via select.  Too many systems still have the one
 # or other ISA chip on the board that users don't know about so don't expect
@@ -2932,8 +2930,6 @@ config ZONE_DMA32
 
 source "drivers/pcmcia/Kconfig"
 
-source "drivers/pci/hotplug/Kconfig"
-
 config RAPIDIO
        tristate "RapidIO support"
        depends on PCI
index 98c31e5..8c16fb7 100644 (file)
@@ -102,7 +102,6 @@ static inline void pci_resource_to_user(const struct pci_dev *dev, int bar,
 #include <linux/scatterlist.h>
 #include <linux/string.h>
 #include <asm/io.h>
-#include <asm-generic/pci-bridge.h>
 
 struct pci_dev;
 
@@ -125,9 +124,6 @@ static inline int pci_proc_domain(struct pci_bus *bus)
 
 #endif /* __KERNEL__ */
 
-/* implement the pci_ DMA API in terms of the generic device dma_ one */
-#include <asm-generic/pci-dma-compat.h>
-
 /* Do platform specific device initialization at pci_enable_device() time */
 extern int pcibios_plat_dev_init(struct pci_dev *dev);
 
index d708ae4..2b6d5e1 100644 (file)
@@ -40,20 +40,25 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 
 static void pci_fixup_radeon(struct pci_dev *pdev)
 {
-       if (pdev->resource[PCI_ROM_RESOURCE].start)
+       struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+       if (res->start)
                return;
 
        if (!loongson_sysconf.vgabios_addr)
                return;
 
-       pdev->resource[PCI_ROM_RESOURCE].start =
-               loongson_sysconf.vgabios_addr;
-       pdev->resource[PCI_ROM_RESOURCE].end   =
-               loongson_sysconf.vgabios_addr + 256*1024 - 1;
-       pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_COPY;
+       pci_disable_rom(pdev);
+       if (res->parent)
+               release_resource(res);
+
+       res->start = virt_to_phys((void *) loongson_sysconf.vgabios_addr);
+       res->end   = res->start + 256*1024 - 1;
+       res->flags = IORESOURCE_MEM | IORESOURCE_ROM_SHADOW |
+                    IORESOURCE_PCI_FIXED;
 
        dev_info(&pdev->dev, "BAR %d: assigned %pR for Radeon ROM\n",
-                       PCI_ROM_RESOURCE, &pdev->resource[PCI_ROM_RESOURCE]);
+                PCI_ROM_RESOURCE, res);
 }
 
 DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, PCI_ANY_ID,
index be3debb..51159ff 100644 (file)
@@ -80,9 +80,6 @@ extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
 
 #endif /* __KERNEL__ */
 
-/* implement the pci_ DMA API in terms of the generic device dma_ one */
-#include <asm-generic/pci-dma-compat.h>
-
 static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 {
        return channel ? 15 : 14;
index 89c53bf..defebd9 100644 (file)
@@ -194,9 +194,6 @@ extern void pcibios_init_bridge(struct pci_dev *);
 #define PCIBIOS_MIN_IO          0x10
 #define PCIBIOS_MIN_MEM         0x1000 /* NBPG - but pci/setup-res.c dies */
 
-/* export the pci_ DMA API in terms of the dma_ one */
-#include <asm-generic/pci-dma-compat.h>
-
 static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 {
        return channel ? 15 : 14;
index 9faa18c..832cc46 100644 (file)
@@ -828,14 +828,10 @@ config PCI_8260
        select PPC_INDIRECT_PCI
        default y
 
-source "drivers/pci/pcie/Kconfig"
-
 source "drivers/pci/Kconfig"
 
 source "drivers/pcmcia/Kconfig"
 
-source "drivers/pci/hotplug/Kconfig"
-
 config HAS_RAPIDIO
        bool
        default n
index 54843ca..78968c1 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/pci.h>
 #include <linux/list.h>
 #include <linux/ioport.h>
-#include <asm-generic/pci-bridge.h>
 
 struct device_node;
 
index 6f8065a..a6f3ac0 100644 (file)
@@ -20,8 +20,6 @@
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
 
-#include <asm-generic/pci-dma-compat.h>
-
 /* Return values for pci_controller_ops.probe_mode function */
 #define PCI_PROBE_NONE         -1      /* Don't look at this bus at all */
 #define PCI_PROBE_NORMAL       0       /* Do normal PCI probing */
index 683af90..7e3e8a8 100644 (file)
@@ -605,8 +605,6 @@ config PCI_NR_MSI
          PCI devices.
 
 source "drivers/pci/Kconfig"
-source "drivers/pci/pcie/Kconfig"
-source "drivers/pci/hotplug/Kconfig"
 
 endif  # PCI
 
index f833082..b6bfa16 100644 (file)
@@ -9,7 +9,6 @@
 #include <linux/pci.h>
 #include <linux/mutex.h>
 #include <asm-generic/pci.h>
-#include <asm-generic/pci-dma-compat.h>
 #include <asm/pci_clp.h>
 #include <asm/pci_debug.h>
 
index e13da05..17a4f15 100644 (file)
@@ -847,14 +847,10 @@ config PCI
 config PCI_DOMAINS
        bool
 
-source "drivers/pci/pcie/Kconfig"
-
 source "drivers/pci/Kconfig"
 
 source "drivers/pcmcia/Kconfig"
 
-source "drivers/pci/hotplug/Kconfig"
-
 endmenu
 
 menu "Executable file formats"
index e343dbd..644314f 100644 (file)
@@ -105,9 +105,6 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
        return channel ? 15 : 14;
 }
 
-/* generic DMA-mapping stuff */
-#include <asm-generic/pci-dma-compat.h>
-
 #endif /* __KERNEL__ */
 #endif /* __ASM_SH_PCI_H */
 
index d9c031f..6e14fd1 100644 (file)
@@ -5,7 +5,4 @@
 #else
 #include <asm/pci_32.h>
 #endif
-
-#include <asm-generic/pci-dma-compat.h>
-
 #endif
index de4a4ff..8171930 100644 (file)
@@ -455,8 +455,6 @@ config TILE_PCI_IO
 
 source "drivers/pci/Kconfig"
 
-source "drivers/pci/pcie/Kconfig"
-
 config TILE_USB
        tristate "Tilera USB host adapter support"
        default y
@@ -467,8 +465,6 @@ config TILE_USB
          Provides USB host adapter support for the built-in EHCI and OHCI
          interfaces on TILE-Gx chips.
 
-source "drivers/pci/hotplug/Kconfig"
-
 endmenu
 
 menu "Executable file formats"
index dfedd7a..fe3de50 100644 (file)
@@ -226,7 +226,4 @@ static inline int pcibios_assign_all_busses(void)
 /* Use any cpu for PCI. */
 #define cpumask_of_pcibus(bus) cpu_online_mask
 
-/* implement the pci_ DMA API in terms of the generic device dma_ one */
-#include <asm-generic/pci-dma-compat.h>
-
 #endif /* _ASM_TILE_PCI_H */
index 38b3f37..37e55d0 100644 (file)
@@ -13,8 +13,6 @@
 #define __UNICORE_PCI_H__
 
 #ifdef __KERNEL__
-#include <asm-generic/pci-dma-compat.h>
-#include <asm-generic/pci-bridge.h>
 #include <asm-generic/pci.h>
 #include <mach/hardware.h> /* for PCIBIOS_MIN_* */
 
@@ -23,5 +21,4 @@ extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
        enum pci_mmap_state mmap_state, int write_combine);
 
 #endif /* __KERNEL__ */
-
 #endif
index 9e20b5d..2514623 100644 (file)
 #define PCIBIOS_MIN_IO                 0x4000 /* should lower than 64KB */
 #define PCIBIOS_MIN_MEM                        io_v2p(PKUNITY_PCIMEM_BASE)
 
-/*
- * We override the standard dma-mask routines for bouncing.
- */
-#define        HAVE_ARCH_PCI_SET_DMA_MASK
-
 #define pcibios_assign_all_busses()    1
 
 #endif  /* __MACH_PUV3_HARDWARE_H__ */
index 8f2e665..3c74b54 100644 (file)
@@ -2435,8 +2435,6 @@ config PCI_CNB20LE_QUIRK
 
          You should say N unless you know you need this.
 
-source "drivers/pci/pcie/Kconfig"
-
 source "drivers/pci/Kconfig"
 
 # x86_64 have no ISA slots, but can have ISA-style DMA.
@@ -2592,8 +2590,6 @@ config AMD_NB
 
 source "drivers/pcmcia/Kconfig"
 
-source "drivers/pci/hotplug/Kconfig"
-
 config RAPIDIO
        tristate "RapidIO support"
        depends on PCI
index 4625943..9ab7507 100644 (file)
@@ -20,6 +20,9 @@ struct pci_sysdata {
 #ifdef CONFIG_X86_64
        void            *iommu;         /* IOMMU private data */
 #endif
+#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
+       void            *fwnode;        /* IRQ domain for MSI assignment */
+#endif
 };
 
 extern int pci_routeirq;
@@ -32,6 +35,7 @@ extern int noioapicreroute;
 static inline int pci_domain_nr(struct pci_bus *bus)
 {
        struct pci_sysdata *sd = bus->sysdata;
+
        return sd->domain;
 }
 
@@ -41,6 +45,17 @@ static inline int pci_proc_domain(struct pci_bus *bus)
 }
 #endif
 
+#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
+static inline void *_pci_root_bus_fwnode(struct pci_bus *bus)
+{
+       struct pci_sysdata *sd = bus->sysdata;
+
+       return sd->fwnode;
+}
+
+#define pci_root_bus_fwnode    _pci_root_bus_fwnode
+#endif
+
 /* Can be used to override the logic in pci_scan_bus for skipping
    already-configured bus numbers - to be used for buggy BIOSes
    or architectures with incomplete PCI setup by the loader */
@@ -105,9 +120,6 @@ void native_restore_msi_irqs(struct pci_dev *dev);
 #include <asm/pci_64.h>
 #endif
 
-/* implement the pci_ DMA API in terms of the generic device dma_ one */
-#include <asm-generic/pci-dma-compat.h>
-
 /* generic pci stuff */
 #include <asm-generic/pci.h>
 
index d34b511..381a43c 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/dmi.h>
 #include <linux/slab.h>
 
-#include <asm-generic/pci-bridge.h>
 #include <asm/acpi.h>
 #include <asm/segment.h>
 #include <asm/io.h>
index e585655..b7de192 100644 (file)
@@ -297,14 +297,14 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,      PCI_DEVICE_ID_INTEL_MCH_PC1,    pcie_r
  *
  * The standard boot ROM sequence for an x86 machine uses the BIOS
  * to select an initial video card for boot display. This boot video
- * card will have it's BIOS copied to C0000 in system RAM.
+ * card will have its BIOS copied to 0xC0000 in system RAM.
  * IORESOURCE_ROM_SHADOW is used to associate the boot video
  * card with this copy. On laptops this copy has to be used since
  * the main ROM may be compressed or combined with another image.
  * See pci_map_rom() for use of this flag. Before marking the device
  * with IORESOURCE_ROM_SHADOW check if a vga_default_device is already set
- * by either arch cde or vga-arbitration, if so only apply the fixup to this
- * already determined primary video card.
+ * by either arch code or vga-arbitration; if so only apply the fixup to this
+ * already-determined primary video card.
  */
 
 static void pci_fixup_video(struct pci_dev *pdev)
@@ -312,6 +312,7 @@ static void pci_fixup_video(struct pci_dev *pdev)
        struct pci_dev *bridge;
        struct pci_bus *bus;
        u16 config;
+       struct resource *res;
 
        /* Is VGA routed to us? */
        bus = pdev->bus;
@@ -336,8 +337,18 @@ static void pci_fixup_video(struct pci_dev *pdev)
        if (!vga_default_device() || pdev == vga_default_device()) {
                pci_read_config_word(pdev, PCI_COMMAND, &config);
                if (config & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
-                       pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
-                       dev_printk(KERN_DEBUG, &pdev->dev, "Video device with shadowed ROM\n");
+                       res = &pdev->resource[PCI_ROM_RESOURCE];
+
+                       pci_disable_rom(pdev);
+                       if (res->parent)
+                               release_resource(res);
+
+                       res->start = 0xC0000;
+                       res->end = res->start + 0x20000 - 1;
+                       res->flags = IORESOURCE_MEM | IORESOURCE_ROM_SHADOW |
+                                    IORESOURCE_PCI_FIXED;
+                       dev_info(&pdev->dev, "Video device with shadowed ROM at %pR\n",
+                                res);
                }
        }
 }
@@ -540,3 +551,10 @@ static void twinhead_reserve_killing_zone(struct pci_dev *dev)
         }
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x27B9, twinhead_reserve_killing_zone);
+
+static void pci_bdwep_bar(struct pci_dev *dev)
+{
+       dev->non_compliant_bars = 1;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fa0, pci_bdwep_bar);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fc0, pci_bdwep_bar);
index d57e480..7792aba 100644 (file)
@@ -503,6 +503,18 @@ static struct pci_ops vmd_ops = {
        .write          = vmd_pci_write,
 };
 
+static void vmd_attach_resources(struct vmd_dev *vmd)
+{
+       vmd->dev->resource[VMD_MEMBAR1].child = &vmd->resources[1];
+       vmd->dev->resource[VMD_MEMBAR2].child = &vmd->resources[2];
+}
+
+static void vmd_detach_resources(struct vmd_dev *vmd)
+{
+       vmd->dev->resource[VMD_MEMBAR1].child = NULL;
+       vmd->dev->resource[VMD_MEMBAR2].child = NULL;
+}
+
 /*
  * VMD domains start at 0x1000 to not clash with ACPI _SEG domains.
  */
@@ -527,11 +539,28 @@ static int vmd_enable_domain(struct vmd_dev *vmd)
        res = &vmd->dev->resource[VMD_CFGBAR];
        vmd->resources[0] = (struct resource) {
                .name  = "VMD CFGBAR",
-               .start = res->start,
+               .start = 0,
                .end   = (resource_size(res) >> 20) - 1,
                .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
        };
 
+       /*
+        * If the window is below 4GB, clear IORESOURCE_MEM_64 so we can
+        * put 32-bit resources in the window.
+        *
+        * There's no hardware reason why a 64-bit window *couldn't*
+        * contain a 32-bit resource, but pbus_size_mem() computes the
+        * bridge window size assuming a 64-bit window will contain no
+        * 32-bit resources.  __pci_assign_resource() enforces that
+        * artificial restriction to make sure everything will fit.
+        *
+        * The only way we could use a 64-bit non-prefechable MEMBAR is
+        * if its address is <4GB so that we can convert it to a 32-bit
+        * resource.  To be visible to the host OS, all VMD endpoints must
+        * be initially configured by platform BIOS, which includes setting
+        * up these resources.  We can assume the device is configured
+        * according to the platform needs.
+        */
        res = &vmd->dev->resource[VMD_MEMBAR1];
        upper_bits = upper_32_bits(res->end);
        flags = res->flags & ~IORESOURCE_SIZEALIGN;
@@ -542,6 +571,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd)
                .start = res->start,
                .end   = res->end,
                .flags = flags,
+               .parent = res,
        };
 
        res = &vmd->dev->resource[VMD_MEMBAR2];
@@ -554,6 +584,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd)
                .start = res->start + 0x2000,
                .end   = res->end,
                .flags = flags,
+               .parent = res,
        };
 
        sd->domain = vmd_find_free_domain();
@@ -578,6 +609,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd)
                return -ENODEV;
        }
 
+       vmd_attach_resources(vmd);
        vmd_setup_dma_ops(vmd);
        dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
        pci_rescan_bus(vmd->bus);
@@ -674,6 +706,7 @@ static void vmd_remove(struct pci_dev *dev)
 {
        struct vmd_dev *vmd = pci_get_drvdata(dev);
 
+       vmd_detach_resources(vmd);
        pci_set_drvdata(dev, NULL);
        sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
        pci_stop_root_bus(vmd->bus);
index e9df156..7e9464b 100644 (file)
@@ -413,8 +413,6 @@ config FORCE_MAX_ZONEORDER
 
 source "drivers/pcmcia/Kconfig"
 
-source "drivers/pci/hotplug/Kconfig"
-
 config PLATFORM_WANT_DEFAULT_MEM
        def_bool n
 
index e438a00..5d6bd93 100644 (file)
@@ -55,9 +55,6 @@ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma,
 
 #endif /* __KERNEL__ */
 
-/* Implement the pci_ DMA API in terms of the generic device dma_ one */
-#include <asm-generic/pci-dma-compat.h>
-
 /* Generic PCI */
 #include <asm-generic/pci.h>
 
index e3d4b05..e347e7a 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/scatterlist.h>
 #include <linux/of.h>
 #include <linux/gfp.h>
+#include <linux/pci.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
@@ -30,7 +31,6 @@
 #include <asm/macio.h>
 #include <asm/io.h>
 #include <asm/dbdma.h>
-#include <asm/pci-bridge.h>
 #include <asm/machdep.h>
 #include <asm/pmac_feature.h>
 #include <asm/mediabay.h>
index 0575544..fdced54 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/delay.h>
 #include <linux/vmalloc.h>
 #include <asm/uninorth.h>
-#include <asm/pci-bridge.h>
 #include <asm/prom.h>
 #include <asm/pmac_feature.h>
 #include "agp.h"
index 7f1a360..b332b4d 100644 (file)
@@ -182,8 +182,8 @@ static const struct pci_device_id bochs_pci_tbl[] = {
        {
                .vendor      = 0x1234,
                .device      = 0x1111,
-               .subvendor   = 0x1af4,
-               .subdevice   = 0x1100,
+               .subvendor   = PCI_SUBVENDOR_ID_REDHAT_QUMRANET,
+               .subdevice   = PCI_SUBDEVICE_ID_QEMU,
                .driver_data = BOCHS_QEMU_STDVGA,
        },
        {
index b1619e2..7bc394e 100644 (file)
@@ -33,8 +33,9 @@ static struct drm_driver driver;
 
 /* only bind to the cirrus chip in qemu */
 static const struct pci_device_id pciidlist[] = {
-       { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, 0x1af4, 0x1100, 0,
-         0, 0 },
+       { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446,
+         PCI_SUBVENDOR_ID_REDHAT_QUMRANET, PCI_SUBDEVICE_ID_QEMU,
+         0, 0, 0 },
        { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, PCI_VENDOR_ID_XEN,
          0x0001, 0, 0, 0 },
        {0,}
index a9b01bc..432480f 100644 (file)
@@ -34,7 +34,6 @@
 #include <asm/machdep.h>
 #include <asm/pmac_feature.h>
 #include <asm/prom.h>
-#include <asm/pci-bridge.h>
 #endif /* CONFIG_PPC_PMAC */
 
 /* from radeon_legacy_encoder.c */
index 9ad014a..b33646b 100644 (file)
@@ -28,7 +28,6 @@
 
 #ifdef CONFIG_PPC_PMAC
 #include <asm/prom.h>
-#include <asm/pci-bridge.h>
 #endif
 
 #define DRV_NAME "pdc202xx_new"
index 96a3452..7f0434f 100644 (file)
@@ -40,7 +40,6 @@
 #include <asm/io.h>
 #include <asm/dbdma.h>
 #include <asm/ide.h>
-#include <asm/pci-bridge.h>
 #include <asm/machdep.h>
 #include <asm/pmac_feature.h>
 #include <asm/sections.h>
index 4f12c6f..b6819f0 100644 (file)
@@ -31,7 +31,6 @@
 #include <asm/macio.h>
 #include <asm/pmac_feature.h>
 #include <asm/prom.h>
-#include <asm/pci-bridge.h>
 
 #undef DEBUG
 
index 0c6c17a..a896083 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/delay.h>
 #include <asm/opal.h>
 #include <asm/msi_bitmap.h>
-#include <asm/pci-bridge.h> /* for struct pci_controller */
 #include <asm/pnv-pci.h>
 #include <asm/io.h>
 
index e23a642..2437227 100644 (file)
@@ -51,7 +51,6 @@
 #endif
 
 #ifdef CONFIG_PPC_PMAC
-#include <asm/pci-bridge.h>
 #include <asm/prom.h>
 #include <asm/machdep.h>
 #include <asm/pmac_feature.h>
index 3c54a2c..6761027 100644 (file)
@@ -48,7 +48,6 @@
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 #include <linux/bitops.h>
-#include <asm/pci-bridge.h>
 #include <net/checksum.h>
 
 #include "spider_net.h"
index b1449f7..13f4fed 100644 (file)
@@ -5,7 +5,6 @@
 #include <linux/of_device.h>
 #include <linux/of_pci.h>
 #include <linux/slab.h>
-#include <asm-generic/pci-bridge.h>
 
 static inline int __of_pci_pci_compare(struct device_node *node,
                                       unsigned int data)
index 592de56..3a102a8 100644 (file)
@@ -110,8 +110,6 @@ config IOMMU_HELPER
 
 source "drivers/pcmcia/Kconfig"
 
-source "drivers/pci/hotplug/Kconfig"
-
 endmenu
 
 menu "PA-RISC specific drivers"
index 73de4ef..a1f37db 100644 (file)
@@ -1,6 +1,9 @@
 #
 # PCI configuration
 #
+
+source "drivers/pci/pcie/Kconfig"
+
 config PCI_BUS_ADDR_T_64BIT
        def_bool y if (ARCH_DMA_ADDR_T_64BIT || 64BIT)
        depends on PCI
@@ -118,4 +121,11 @@ config PCI_LABEL
        def_bool y if (DMI || ACPI)
        select NLS
 
+config PCI_HYPERV
+        tristate "Hyper-V PCI Frontend"
+        depends on PCI && X86 && HYPERV && PCI_MSI && PCI_MSI_IRQ_DOMAIN && X86_64
+        help
+          The PCI device frontend driver allows the kernel to import arbitrary
+          PCI devices from a PCI backend to support PCI driver domains.
+
 source "drivers/pci/host/Kconfig"
index be3f631..2154092 100644 (file)
@@ -32,6 +32,7 @@ obj-$(CONFIG_PCI_IOV) += iov.o
 # Some architectures use the generic PCI setup functions
 #
 obj-$(CONFIG_ALPHA) += setup-irq.o
+obj-$(CONFIG_ARC) += setup-irq.o
 obj-$(CONFIG_ARM) += setup-irq.o
 obj-$(CONFIG_ARM64) += setup-irq.o
 obj-$(CONFIG_UNICORE32) += setup-irq.o
index 8c05b5c..01b9d0a 100644 (file)
@@ -174,38 +174,6 @@ struct pci_ops *pci_bus_set_ops(struct pci_bus *bus, struct pci_ops *ops)
 }
 EXPORT_SYMBOL(pci_bus_set_ops);
 
-/**
- * pci_read_vpd - Read one entry from Vital Product Data
- * @dev:       pci device struct
- * @pos:       offset in vpd space
- * @count:     number of bytes to read
- * @buf:       pointer to where to store result
- *
- */
-ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf)
-{
-       if (!dev->vpd || !dev->vpd->ops)
-               return -ENODEV;
-       return dev->vpd->ops->read(dev, pos, count, buf);
-}
-EXPORT_SYMBOL(pci_read_vpd);
-
-/**
- * pci_write_vpd - Write entry to Vital Product Data
- * @dev:       pci device struct
- * @pos:       offset in vpd space
- * @count:     number of bytes to write
- * @buf:       buffer containing write data
- *
- */
-ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf)
-{
-       if (!dev->vpd || !dev->vpd->ops)
-               return -ENODEV;
-       return dev->vpd->ops->write(dev, pos, count, buf);
-}
-EXPORT_SYMBOL(pci_write_vpd);
-
 /*
  * The following routines are to prevent the user from accessing PCI config
  * space when it's unsafe to do so.  Some devices require this during BIST and
@@ -277,15 +245,91 @@ PCI_USER_WRITE_CONFIG(dword, u32)
 
 /* VPD access through PCI 2.2+ VPD capability */
 
-#define PCI_VPD_PCI22_SIZE (PCI_VPD_ADDR_MASK + 1)
+/**
+ * pci_read_vpd - Read one entry from Vital Product Data
+ * @dev:       pci device struct
+ * @pos:       offset in vpd space
+ * @count:     number of bytes to read
+ * @buf:       pointer to where to store result
+ */
+ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf)
+{
+       if (!dev->vpd || !dev->vpd->ops)
+               return -ENODEV;
+       return dev->vpd->ops->read(dev, pos, count, buf);
+}
+EXPORT_SYMBOL(pci_read_vpd);
 
-struct pci_vpd_pci22 {
-       struct pci_vpd base;
-       struct mutex lock;
-       u16     flag;
-       bool    busy;
-       u8      cap;
-};
+/**
+ * pci_write_vpd - Write entry to Vital Product Data
+ * @dev:       pci device struct
+ * @pos:       offset in vpd space
+ * @count:     number of bytes to write
+ * @buf:       buffer containing write data
+ */
+ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf)
+{
+       if (!dev->vpd || !dev->vpd->ops)
+               return -ENODEV;
+       return dev->vpd->ops->write(dev, pos, count, buf);
+}
+EXPORT_SYMBOL(pci_write_vpd);
+
+#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1)
+
+/**
+ * pci_vpd_size - determine actual size of Vital Product Data
+ * @dev:       pci device struct
+ * @old_size:  current assumed size, also maximum allowed size
+ */
+static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size)
+{
+       size_t off = 0;
+       unsigned char header[1+2];      /* 1 byte tag, 2 bytes length */
+
+       while (off < old_size &&
+              pci_read_vpd(dev, off, 1, header) == 1) {
+               unsigned char tag;
+
+               if (header[0] & PCI_VPD_LRDT) {
+                       /* Large Resource Data Type Tag */
+                       tag = pci_vpd_lrdt_tag(header);
+                       /* Only read length from known tag items */
+                       if ((tag == PCI_VPD_LTIN_ID_STRING) ||
+                           (tag == PCI_VPD_LTIN_RO_DATA) ||
+                           (tag == PCI_VPD_LTIN_RW_DATA)) {
+                               if (pci_read_vpd(dev, off+1, 2,
+                                                &header[1]) != 2) {
+                                       dev_warn(&dev->dev,
+                                                "invalid large VPD tag %02x size at offset %zu",
+                                                tag, off + 1);
+                                       return 0;
+                               }
+                               off += PCI_VPD_LRDT_TAG_SIZE +
+                                       pci_vpd_lrdt_size(header);
+                       }
+               } else {
+                       /* Short Resource Data Type Tag */
+                       off += PCI_VPD_SRDT_TAG_SIZE +
+                               pci_vpd_srdt_size(header);
+                       tag = pci_vpd_srdt_tag(header);
+               }
+
+               if (tag == PCI_VPD_STIN_END)    /* End tag descriptor */
+                       return off;
+
+               if ((tag != PCI_VPD_LTIN_ID_STRING) &&
+                   (tag != PCI_VPD_LTIN_RO_DATA) &&
+                   (tag != PCI_VPD_LTIN_RW_DATA)) {
+                       dev_warn(&dev->dev,
+                                "invalid %s VPD tag %02x at offset %zu",
+                                (header[0] & PCI_VPD_LRDT) ? "large" : "short",
+                                tag, off);
+                       return 0;
+               }
+       }
+       return 0;
+}
 
 /*
  * Wait for last operation to complete.
@@ -295,55 +339,71 @@ struct pci_vpd_pci22 {
  *
  * Returns 0 on success, negative values indicate error.
  */
-static int pci_vpd_pci22_wait(struct pci_dev *dev)
+static int pci_vpd_wait(struct pci_dev *dev)
 {
-       struct pci_vpd_pci22 *vpd =
-               container_of(dev->vpd, struct pci_vpd_pci22, base);
-       unsigned long timeout = jiffies + HZ/20 + 2;
+       struct pci_vpd *vpd = dev->vpd;
+       unsigned long timeout = jiffies + msecs_to_jiffies(50);
+       unsigned long max_sleep = 16;
        u16 status;
        int ret;
 
        if (!vpd->busy)
                return 0;
 
-       for (;;) {
+       while (time_before(jiffies, timeout)) {
                ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR,
                                                &status);
                if (ret < 0)
                        return ret;
 
                if ((status & PCI_VPD_ADDR_F) == vpd->flag) {
-                       vpd->busy = false;
+                       vpd->busy = 0;
                        return 0;
                }
 
-               if (time_after(jiffies, timeout)) {
-                       dev_printk(KERN_DEBUG, &dev->dev, "vpd r/w failed.  This is likely a firmware bug on this device.  Contact the card vendor for a firmware update\n");
-                       return -ETIMEDOUT;
-               }
                if (fatal_signal_pending(current))
                        return -EINTR;
-               if (!cond_resched())
-                       udelay(10);
+
+               usleep_range(10, max_sleep);
+               if (max_sleep < 1024)
+                       max_sleep *= 2;
        }
+
+       dev_warn(&dev->dev, "VPD access failed.  This is likely a firmware bug on this device.  Contact the card vendor for a firmware update\n");
+       return -ETIMEDOUT;
 }
 
-static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count,
-                                 void *arg)
+static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count,
+                           void *arg)
 {
-       struct pci_vpd_pci22 *vpd =
-               container_of(dev->vpd, struct pci_vpd_pci22, base);
+       struct pci_vpd *vpd = dev->vpd;
        int ret;
        loff_t end = pos + count;
        u8 *buf = arg;
 
-       if (pos < 0 || pos > vpd->base.len || end > vpd->base.len)
+       if (pos < 0)
                return -EINVAL;
 
+       if (!vpd->valid) {
+               vpd->valid = 1;
+               vpd->len = pci_vpd_size(dev, vpd->len);
+       }
+
+       if (vpd->len == 0)
+               return -EIO;
+
+       if (pos > vpd->len)
+               return 0;
+
+       if (end > vpd->len) {
+               end = vpd->len;
+               count = end - pos;
+       }
+
        if (mutex_lock_killable(&vpd->lock))
                return -EINTR;
 
-       ret = pci_vpd_pci22_wait(dev);
+       ret = pci_vpd_wait(dev);
        if (ret < 0)
                goto out;
 
@@ -355,9 +415,9 @@ static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count,
                                                 pos & ~3);
                if (ret < 0)
                        break;
-               vpd->busy = true;
+               vpd->busy = 1;
                vpd->flag = PCI_VPD_ADDR_F;
-               ret = pci_vpd_pci22_wait(dev);
+               ret = pci_vpd_wait(dev);
                if (ret < 0)
                        break;
 
@@ -380,22 +440,32 @@ out:
        return ret ? ret : count;
 }
 
-static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count,
-                                  const void *arg)
+static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count,
+                            const void *arg)
 {
-       struct pci_vpd_pci22 *vpd =
-               container_of(dev->vpd, struct pci_vpd_pci22, base);
+       struct pci_vpd *vpd = dev->vpd;
        const u8 *buf = arg;
        loff_t end = pos + count;
        int ret = 0;
 
-       if (pos < 0 || (pos & 3) || (count & 3) || end > vpd->base.len)
+       if (pos < 0 || (pos & 3) || (count & 3))
+               return -EINVAL;
+
+       if (!vpd->valid) {
+               vpd->valid = 1;
+               vpd->len = pci_vpd_size(dev, vpd->len);
+       }
+
+       if (vpd->len == 0)
+               return -EIO;
+
+       if (end > vpd->len)
                return -EINVAL;
 
        if (mutex_lock_killable(&vpd->lock))
                return -EINTR;
 
-       ret = pci_vpd_pci22_wait(dev);
+       ret = pci_vpd_wait(dev);
        if (ret < 0)
                goto out;
 
@@ -415,9 +485,9 @@ static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count
                if (ret < 0)
                        break;
 
-               vpd->busy = true;
+               vpd->busy = 1;
                vpd->flag = 0;
-               ret = pci_vpd_pci22_wait(dev);
+               ret = pci_vpd_wait(dev);
                if (ret < 0)
                        break;
 
@@ -428,15 +498,9 @@ out:
        return ret ? ret : count;
 }
 
-static void pci_vpd_pci22_release(struct pci_dev *dev)
-{
-       kfree(container_of(dev->vpd, struct pci_vpd_pci22, base));
-}
-
-static const struct pci_vpd_ops pci_vpd_pci22_ops = {
-       .read = pci_vpd_pci22_read,
-       .write = pci_vpd_pci22_write,
-       .release = pci_vpd_pci22_release,
+static const struct pci_vpd_ops pci_vpd_ops = {
+       .read = pci_vpd_read,
+       .write = pci_vpd_write,
 };
 
 static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count,
@@ -472,12 +536,11 @@ static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count,
 static const struct pci_vpd_ops pci_vpd_f0_ops = {
        .read = pci_vpd_f0_read,
        .write = pci_vpd_f0_write,
-       .release = pci_vpd_pci22_release,
 };
 
-int pci_vpd_pci22_init(struct pci_dev *dev)
+int pci_vpd_init(struct pci_dev *dev)
 {
-       struct pci_vpd_pci22 *vpd;
+       struct pci_vpd *vpd;
        u8 cap;
 
        cap = pci_find_capability(dev, PCI_CAP_ID_VPD);
@@ -488,18 +551,24 @@ int pci_vpd_pci22_init(struct pci_dev *dev)
        if (!vpd)
                return -ENOMEM;
 
-       vpd->base.len = PCI_VPD_PCI22_SIZE;
+       vpd->len = PCI_VPD_MAX_SIZE;
        if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0)
-               vpd->base.ops = &pci_vpd_f0_ops;
+               vpd->ops = &pci_vpd_f0_ops;
        else
-               vpd->base.ops = &pci_vpd_pci22_ops;
+               vpd->ops = &pci_vpd_ops;
        mutex_init(&vpd->lock);
        vpd->cap = cap;
-       vpd->busy = false;
-       dev->vpd = &vpd->base;
+       vpd->busy = 0;
+       vpd->valid = 0;
+       dev->vpd = vpd;
        return 0;
 }
 
+void pci_vpd_release(struct pci_dev *dev)
+{
+       kfree(dev->vpd);
+}
+
 /**
  * pci_cfg_access_lock - Lock PCI config reads/writes
  * @dev:       pci device struct
index 89b3bef..f2187d4 100644 (file)
@@ -291,7 +291,12 @@ void pci_bus_add_device(struct pci_dev *dev)
 
        dev->match_driver = true;
        retval = device_attach(&dev->dev);
-       WARN_ON(retval < 0);
+       if (retval < 0) {
+               dev_warn(&dev->dev, "device attach failed (%d)\n", retval);
+               pci_proc_detach_device(dev);
+               pci_remove_sysfs_dev_files(dev);
+               return;
+       }
 
        dev->is_added = 1;
 }
index d1cdd9c..c5014bf 100644 (file)
@@ -17,6 +17,28 @@ config PCI_MVEBU
        depends on ARM
        depends on OF
 
+
+config PCIE_XILINX_NWL
+       bool "NWL PCIe Core"
+       depends on ARCH_ZYNQMP
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       help
+        Say 'Y' here if you want kernel support for Xilinx
+        NWL PCIe controller. The controller can act as Root Port
+        or End Point. The current option selection will only
+        support root port enabling.
+
+config PCIE_DW_PLAT
+       bool "Platform bus based DesignWare PCIe Controller"
+       select PCIE_DW
+       ---help---
+        This selects the DesignWare PCIe controller support. Select this if
+        you have a PCIe controller on Platform bus.
+
+        If you have a controller with this interface, say Y or M here.
+
+        If unsure, say N.
+
 config PCIE_DW
        bool
 
@@ -42,7 +64,7 @@ config PCI_TEGRA
 config PCI_RCAR_GEN2
        bool "Renesas R-Car Gen2 Internal PCI controller"
        depends on ARM
-       depends on ARCH_SHMOBILE || COMPILE_TEST
+       depends on ARCH_RENESAS || COMPILE_TEST
        help
          Say Y here if you want internal PCI support on R-Car Gen2 SoC.
          There are 3 internal PCI controllers available with a single
@@ -50,13 +72,17 @@ config PCI_RCAR_GEN2
 
 config PCI_RCAR_GEN2_PCIE
        bool "Renesas R-Car PCIe controller"
-       depends on ARCH_SHMOBILE || (ARM && COMPILE_TEST)
+       depends on ARCH_RENESAS || (ARM && COMPILE_TEST)
        help
          Say Y here if you want PCIe controller support on R-Car Gen2 SoCs.
 
+config PCI_HOST_COMMON
+       bool
+
 config PCI_HOST_GENERIC
        bool "Generic PCI host controller"
        depends on (ARM || ARM64) && OF
+       select PCI_HOST_COMMON
        help
          Say Y here if you want to support a simple generic PCI host
          controller, such as the one emulated by kvmtool.
@@ -82,7 +108,7 @@ config PCI_KEYSTONE
 
 config PCIE_XILINX
        bool "Xilinx AXI PCIe host bridge support"
-       depends on ARCH_ZYNQ
+       depends on ARCH_ZYNQ || MICROBLAZE
        help
          Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
          Host Bridge driver.
@@ -192,4 +218,18 @@ config PCIE_QCOM
          PCIe controller uses the Designware core plus Qualcomm-specific
          hardware wrappers.
 
+config PCI_HOST_THUNDER_PEM
+       bool "Cavium Thunder PCIe controller to off-chip devices"
+       depends on OF && ARM64
+       select PCI_HOST_COMMON
+       help
+         Say Y here if you want PCIe support for CN88XX Cavium Thunder SoCs.
+
+config PCI_HOST_THUNDER_ECAM
+       bool "Cavium Thunder ECAM controller to on-chip devices on pass-1.x silicon"
+       depends on OF && ARM64
+       select PCI_HOST_COMMON
+       help
+         Say Y here if you want ECAM support for CN88XX-Pass-1.x Cavium Thunder SoCs.
+
 endmenu
index 7b2f20c..d85b5fa 100644 (file)
@@ -1,15 +1,19 @@
 obj-$(CONFIG_PCIE_DW) += pcie-designware.o
+obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
 obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
 obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
 obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
+obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o
 obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
 obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
 obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
 obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
+obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o
 obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
 obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
+obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
 obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
@@ -22,3 +26,5 @@ obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
 obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
 obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
 obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
+obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
+obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
index 923607b..2ca3a1f 100644 (file)
@@ -10,7 +10,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
@@ -108,7 +107,6 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
 {
        struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
        u32 reg;
-       unsigned int retries;
 
        if (dw_pcie_link_up(pp)) {
                dev_err(pp->dev, "link is already up\n");
@@ -119,14 +117,7 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
        reg |= LTSSM_EN;
        dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
 
-       for (retries = 0; retries < 1000; retries++) {
-               if (dw_pcie_link_up(pp))
-                       return 0;
-               usleep_range(10, 20);
-       }
-
-       dev_err(pp->dev, "link is not up\n");
-       return -EINVAL;
+       return dw_pcie_wait_for_link(pp);
 }
 
 static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp)
index d997d22..2199761 100644 (file)
@@ -318,7 +318,6 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
 {
        struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
        u32 val;
-       unsigned int retries;
 
        if (dw_pcie_link_up(pp)) {
                dev_err(pp->dev, "Link already up\n");
@@ -357,13 +356,8 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
                          PCIE_APP_LTSSM_ENABLE);
 
        /* check if the link is up or not */
-       for (retries = 0; retries < 10; retries++) {
-               if (dw_pcie_link_up(pp)) {
-                       dev_info(pp->dev, "Link up\n");
-                       return 0;
-               }
-               mdelay(100);
-       }
+       if (!dw_pcie_wait_for_link(pp))
+               return 0;
 
        while (exynos_phy_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED) == 0) {
                val = exynos_blk_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED);
@@ -372,8 +366,7 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
        /* power off phy */
        exynos_pcie_power_off_phy(pp);
 
-       dev_err(pp->dev, "PCIe Link Fail\n");
-       return -EINVAL;
+       return -ETIMEDOUT;
 }
 
 static void exynos_pcie_clear_irq_pulse(struct pcie_port *pp)
diff --git a/drivers/pci/host/pci-host-common.c b/drivers/pci/host/pci-host-common.c
new file mode 100644 (file)
index 0000000..e9f850f
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2014 ARM Limited
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/platform_device.h>
+
+#include "pci-host-common.h"
+
+static void gen_pci_release_of_pci_ranges(struct gen_pci *pci)
+{
+       pci_free_resource_list(&pci->resources);
+}
+
+static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
+{
+       int err, res_valid = 0;
+       struct device *dev = pci->host.dev.parent;
+       struct device_node *np = dev->of_node;
+       resource_size_t iobase;
+       struct resource_entry *win;
+
+       err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources,
+                                              &iobase);
+       if (err)
+               return err;
+
+       resource_list_for_each_entry(win, &pci->resources) {
+               struct resource *parent, *res = win->res;
+
+               switch (resource_type(res)) {
+               case IORESOURCE_IO:
+                       parent = &ioport_resource;
+                       err = pci_remap_iospace(res, iobase);
+                       if (err) {
+                               dev_warn(dev, "error %d: failed to map resource %pR\n",
+                                        err, res);
+                               continue;
+                       }
+                       break;
+               case IORESOURCE_MEM:
+                       parent = &iomem_resource;
+                       res_valid |= !(res->flags & IORESOURCE_PREFETCH);
+                       break;
+               case IORESOURCE_BUS:
+                       pci->cfg.bus_range = res;
+               default:
+                       continue;
+               }
+
+               err = devm_request_resource(dev, parent, res);
+               if (err)
+                       goto out_release_res;
+       }
+
+       if (!res_valid) {
+               dev_err(dev, "non-prefetchable memory resource required\n");
+               err = -EINVAL;
+               goto out_release_res;
+       }
+
+       return 0;
+
+out_release_res:
+       gen_pci_release_of_pci_ranges(pci);
+       return err;
+}
+
+static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
+{
+       int err;
+       u8 bus_max;
+       resource_size_t busn;
+       struct resource *bus_range;
+       struct device *dev = pci->host.dev.parent;
+       struct device_node *np = dev->of_node;
+       u32 sz = 1 << pci->cfg.ops->bus_shift;
+
+       err = of_address_to_resource(np, 0, &pci->cfg.res);
+       if (err) {
+               dev_err(dev, "missing \"reg\" property\n");
+               return err;
+       }
+
+       /* Limit the bus-range to fit within reg */
+       bus_max = pci->cfg.bus_range->start +
+                 (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
+       pci->cfg.bus_range->end = min_t(resource_size_t,
+                                       pci->cfg.bus_range->end, bus_max);
+
+       pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range),
+                                   sizeof(*pci->cfg.win), GFP_KERNEL);
+       if (!pci->cfg.win)
+               return -ENOMEM;
+
+       /* Map our Configuration Space windows */
+       if (!devm_request_mem_region(dev, pci->cfg.res.start,
+                                    resource_size(&pci->cfg.res),
+                                    "Configuration Space"))
+               return -ENOMEM;
+
+       bus_range = pci->cfg.bus_range;
+       for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
+               u32 idx = busn - bus_range->start;
+
+               pci->cfg.win[idx] = devm_ioremap(dev,
+                                                pci->cfg.res.start + idx * sz,
+                                                sz);
+               if (!pci->cfg.win[idx])
+                       return -ENOMEM;
+       }
+
+       return 0;
+}
+
+int pci_host_common_probe(struct platform_device *pdev,
+                         struct gen_pci *pci)
+{
+       int err;
+       const char *type;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct pci_bus *bus, *child;
+
+       type = of_get_property(np, "device_type", NULL);
+       if (!type || strcmp(type, "pci")) {
+               dev_err(dev, "invalid \"device_type\" %s\n", type);
+               return -EINVAL;
+       }
+
+       of_pci_check_probe_only();
+
+       pci->host.dev.parent = dev;
+       INIT_LIST_HEAD(&pci->host.windows);
+       INIT_LIST_HEAD(&pci->resources);
+
+       /* Parse our PCI ranges and request their resources */
+       err = gen_pci_parse_request_of_pci_ranges(pci);
+       if (err)
+               return err;
+
+       /* Parse and map our Configuration Space windows */
+       err = gen_pci_parse_map_cfg_windows(pci);
+       if (err) {
+               gen_pci_release_of_pci_ranges(pci);
+               return err;
+       }
+
+       /* Do not reassign resources if probe only */
+       if (!pci_has_flag(PCI_PROBE_ONLY))
+               pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
+
+
+       bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start,
+                               &pci->cfg.ops->ops, pci, &pci->resources);
+       if (!bus) {
+               dev_err(dev, "Scanning rootbus failed");
+               return -ENODEV;
+       }
+
+       pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+
+       if (!pci_has_flag(PCI_PROBE_ONLY)) {
+               pci_bus_size_bridges(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;
+}
+
+MODULE_DESCRIPTION("Generic PCI host driver common code");
+MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-host-common.h b/drivers/pci/host/pci-host-common.h
new file mode 100644 (file)
index 0000000..09f3fa0
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2014 ARM Limited
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ */
+
+#ifndef _PCI_HOST_COMMON_H
+#define _PCI_HOST_COMMON_H
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+struct gen_pci_cfg_bus_ops {
+       u32 bus_shift;
+       struct pci_ops ops;
+};
+
+struct gen_pci_cfg_windows {
+       struct resource                         res;
+       struct resource                         *bus_range;
+       void __iomem                            **win;
+
+       struct gen_pci_cfg_bus_ops              *ops;
+};
+
+struct gen_pci {
+       struct pci_host_bridge                  host;
+       struct gen_pci_cfg_windows              cfg;
+       struct list_head                        resources;
+};
+
+int pci_host_common_probe(struct platform_device *pdev,
+                         struct gen_pci *pci);
+
+#endif /* _PCI_HOST_COMMON_H */
index 1652bc7..e8aa78f 100644 (file)
 #include <linux/of_pci.h>
 #include <linux/platform_device.h>
 
-struct gen_pci_cfg_bus_ops {
-       u32 bus_shift;
-       struct pci_ops ops;
-};
-
-struct gen_pci_cfg_windows {
-       struct resource                         res;
-       struct resource                         *bus_range;
-       void __iomem                            **win;
-
-       struct gen_pci_cfg_bus_ops              *ops;
-};
-
-struct gen_pci {
-       struct pci_host_bridge                  host;
-       struct gen_pci_cfg_windows              cfg;
-       struct list_head                        resources;
-};
+#include "pci-host-common.h"
 
 static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus,
                                             unsigned int devfn,
@@ -93,175 +76,19 @@ static const struct of_device_id gen_pci_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, gen_pci_of_match);
 
-static void gen_pci_release_of_pci_ranges(struct gen_pci *pci)
-{
-       pci_free_resource_list(&pci->resources);
-}
-
-static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
-{
-       int err, res_valid = 0;
-       struct device *dev = pci->host.dev.parent;
-       struct device_node *np = dev->of_node;
-       resource_size_t iobase;
-       struct resource_entry *win;
-
-       err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources,
-                                              &iobase);
-       if (err)
-               return err;
-
-       resource_list_for_each_entry(win, &pci->resources) {
-               struct resource *parent, *res = win->res;
-
-               switch (resource_type(res)) {
-               case IORESOURCE_IO:
-                       parent = &ioport_resource;
-                       err = pci_remap_iospace(res, iobase);
-                       if (err) {
-                               dev_warn(dev, "error %d: failed to map resource %pR\n",
-                                        err, res);
-                               continue;
-                       }
-                       break;
-               case IORESOURCE_MEM:
-                       parent = &iomem_resource;
-                       res_valid |= !(res->flags & IORESOURCE_PREFETCH);
-                       break;
-               case IORESOURCE_BUS:
-                       pci->cfg.bus_range = res;
-               default:
-                       continue;
-               }
-
-               err = devm_request_resource(dev, parent, res);
-               if (err)
-                       goto out_release_res;
-       }
-
-       if (!res_valid) {
-               dev_err(dev, "non-prefetchable memory resource required\n");
-               err = -EINVAL;
-               goto out_release_res;
-       }
-
-       return 0;
-
-out_release_res:
-       gen_pci_release_of_pci_ranges(pci);
-       return err;
-}
-
-static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
-{
-       int err;
-       u8 bus_max;
-       resource_size_t busn;
-       struct resource *bus_range;
-       struct device *dev = pci->host.dev.parent;
-       struct device_node *np = dev->of_node;
-       u32 sz = 1 << pci->cfg.ops->bus_shift;
-
-       err = of_address_to_resource(np, 0, &pci->cfg.res);
-       if (err) {
-               dev_err(dev, "missing \"reg\" property\n");
-               return err;
-       }
-
-       /* Limit the bus-range to fit within reg */
-       bus_max = pci->cfg.bus_range->start +
-                 (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
-       pci->cfg.bus_range->end = min_t(resource_size_t,
-                                       pci->cfg.bus_range->end, bus_max);
-
-       pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range),
-                                   sizeof(*pci->cfg.win), GFP_KERNEL);
-       if (!pci->cfg.win)
-               return -ENOMEM;
-
-       /* Map our Configuration Space windows */
-       if (!devm_request_mem_region(dev, pci->cfg.res.start,
-                                    resource_size(&pci->cfg.res),
-                                    "Configuration Space"))
-               return -ENOMEM;
-
-       bus_range = pci->cfg.bus_range;
-       for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
-               u32 idx = busn - bus_range->start;
-
-               pci->cfg.win[idx] = devm_ioremap(dev,
-                                                pci->cfg.res.start + idx * sz,
-                                                sz);
-               if (!pci->cfg.win[idx])
-                       return -ENOMEM;
-       }
-
-       return 0;
-}
-
 static int gen_pci_probe(struct platform_device *pdev)
 {
-       int err;
-       const char *type;
-       const struct of_device_id *of_id;
        struct device *dev = &pdev->dev;
-       struct device_node *np = dev->of_node;
+       const struct of_device_id *of_id;
        struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
-       struct pci_bus *bus, *child;
 
        if (!pci)
                return -ENOMEM;
 
-       type = of_get_property(np, "device_type", NULL);
-       if (!type || strcmp(type, "pci")) {
-               dev_err(dev, "invalid \"device_type\" %s\n", type);
-               return -EINVAL;
-       }
-
-       of_pci_check_probe_only();
-
-       of_id = of_match_node(gen_pci_of_match, np);
+       of_id = of_match_node(gen_pci_of_match, dev->of_node);
        pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
-       pci->host.dev.parent = dev;
-       INIT_LIST_HEAD(&pci->host.windows);
-       INIT_LIST_HEAD(&pci->resources);
-
-       /* Parse our PCI ranges and request their resources */
-       err = gen_pci_parse_request_of_pci_ranges(pci);
-       if (err)
-               return err;
-
-       /* Parse and map our Configuration Space windows */
-       err = gen_pci_parse_map_cfg_windows(pci);
-       if (err) {
-               gen_pci_release_of_pci_ranges(pci);
-               return err;
-       }
-
-       /* Do not reassign resources if probe only */
-       if (!pci_has_flag(PCI_PROBE_ONLY))
-               pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
-
-
-       bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start,
-                               &pci->cfg.ops->ops, pci, &pci->resources);
-       if (!bus) {
-               dev_err(dev, "Scanning rootbus failed");
-               return -ENODEV;
-       }
-
-       pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
-
-       if (!pci_has_flag(PCI_PROBE_ONLY)) {
-               pci_bus_size_bridges(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;
+       return pci_host_common_probe(pdev, pci);
 }
 
 static struct platform_driver gen_pci_driver = {
diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c
new file mode 100644 (file)
index 0000000..ed651ba
--- /dev/null
@@ -0,0 +1,2346 @@
+/*
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Author:
+ *   Jake Oshins <jakeo@microsoft.com>
+ *
+ * This driver acts as a paravirtual front-end for PCI Express root buses.
+ * When a PCI Express function (either an entire device or an SR-IOV
+ * Virtual Function) is being passed through to the VM, this driver exposes
+ * a new bus to the guest VM.  This is modeled as a root PCI bus because
+ * no bridges are being exposed to the VM.  In fact, with a "Generation 2"
+ * VM within Hyper-V, there may seem to be no PCI bus at all in the VM
+ * until a device as been exposed using this driver.
+ *
+ * Each root PCI bus has its own PCI domain, which is called "Segment" in
+ * the PCI Firmware Specifications.  Thus while each device passed through
+ * to the VM using this front-end will appear at "device 0", the domain will
+ * be unique.  Typically, each bus will have one PCI function on it, though
+ * this driver does support more than one.
+ *
+ * In order to map the interrupts from the device through to the guest VM,
+ * this driver also implements an IRQ Domain, which handles interrupts (either
+ * MSI or MSI-X) associated with the functions on the bus.  As interrupts are
+ * set up, torn down, or reaffined, this driver communicates with the
+ * underlying hypervisor to adjust the mappings in the I/O MMU so that each
+ * interrupt will be delivered to the correct virtual processor at the right
+ * vector.  This driver does not support level-triggered (line-based)
+ * interrupts, and will report that the Interrupt Line register in the
+ * function's configuration space is zero.
+ *
+ * The rest of this driver mostly maps PCI concepts onto underlying Hyper-V
+ * facilities.  For instance, the configuration space of a function exposed
+ * by Hyper-V is mapped into a single page of memory space, and the
+ * read and write handlers for config space must be aware of this mechanism.
+ * Similarly, device setup and teardown involves messages sent to and from
+ * the PCI back-end driver in Hyper-V.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/semaphore.h>
+#include <linux/irqdomain.h>
+#include <asm/irqdomain.h>
+#include <asm/apic.h>
+#include <linux/msi.h>
+#include <linux/hyperv.h>
+#include <asm/mshyperv.h>
+
+/*
+ * Protocol versions. The low word is the minor version, the high word the
+ * major version.
+ */
+
+#define PCI_MAKE_VERSION(major, minor) ((u32)(((major) << 16) | (major)))
+#define PCI_MAJOR_VERSION(version) ((u32)(version) >> 16)
+#define PCI_MINOR_VERSION(version) ((u32)(version) & 0xff)
+
+enum {
+       PCI_PROTOCOL_VERSION_1_1 = PCI_MAKE_VERSION(1, 1),
+       PCI_PROTOCOL_VERSION_CURRENT = PCI_PROTOCOL_VERSION_1_1
+};
+
+#define PCI_CONFIG_MMIO_LENGTH 0x2000
+#define CFG_PAGE_OFFSET 0x1000
+#define CFG_PAGE_SIZE (PCI_CONFIG_MMIO_LENGTH - CFG_PAGE_OFFSET)
+
+#define MAX_SUPPORTED_MSI_MESSAGES 0x400
+
+/*
+ * Message Types
+ */
+
+enum pci_message_type {
+       /*
+        * Version 1.1
+        */
+       PCI_MESSAGE_BASE                = 0x42490000,
+       PCI_BUS_RELATIONS               = PCI_MESSAGE_BASE + 0,
+       PCI_QUERY_BUS_RELATIONS         = PCI_MESSAGE_BASE + 1,
+       PCI_POWER_STATE_CHANGE          = PCI_MESSAGE_BASE + 4,
+       PCI_QUERY_RESOURCE_REQUIREMENTS = PCI_MESSAGE_BASE + 5,
+       PCI_QUERY_RESOURCE_RESOURCES    = PCI_MESSAGE_BASE + 6,
+       PCI_BUS_D0ENTRY                 = PCI_MESSAGE_BASE + 7,
+       PCI_BUS_D0EXIT                  = PCI_MESSAGE_BASE + 8,
+       PCI_READ_BLOCK                  = PCI_MESSAGE_BASE + 9,
+       PCI_WRITE_BLOCK                 = PCI_MESSAGE_BASE + 0xA,
+       PCI_EJECT                       = PCI_MESSAGE_BASE + 0xB,
+       PCI_QUERY_STOP                  = PCI_MESSAGE_BASE + 0xC,
+       PCI_REENABLE                    = PCI_MESSAGE_BASE + 0xD,
+       PCI_QUERY_STOP_FAILED           = PCI_MESSAGE_BASE + 0xE,
+       PCI_EJECTION_COMPLETE           = PCI_MESSAGE_BASE + 0xF,
+       PCI_RESOURCES_ASSIGNED          = PCI_MESSAGE_BASE + 0x10,
+       PCI_RESOURCES_RELEASED          = PCI_MESSAGE_BASE + 0x11,
+       PCI_INVALIDATE_BLOCK            = PCI_MESSAGE_BASE + 0x12,
+       PCI_QUERY_PROTOCOL_VERSION      = PCI_MESSAGE_BASE + 0x13,
+       PCI_CREATE_INTERRUPT_MESSAGE    = PCI_MESSAGE_BASE + 0x14,
+       PCI_DELETE_INTERRUPT_MESSAGE    = PCI_MESSAGE_BASE + 0x15,
+       PCI_MESSAGE_MAXIMUM
+};
+
+/*
+ * Structures defining the virtual PCI Express protocol.
+ */
+
+union pci_version {
+       struct {
+               u16 minor_version;
+               u16 major_version;
+       } parts;
+       u32 version;
+} __packed;
+
+/*
+ * Function numbers are 8-bits wide on Express, as interpreted through ARI,
+ * which is all this driver does.  This representation is the one used in
+ * Windows, which is what is expected when sending this back and forth with
+ * the Hyper-V parent partition.
+ */
+union win_slot_encoding {
+       struct {
+               u32     func:8;
+               u32     reserved:24;
+       } bits;
+       u32 slot;
+} __packed;
+
+/*
+ * Pretty much as defined in the PCI Specifications.
+ */
+struct pci_function_description {
+       u16     v_id;   /* vendor ID */
+       u16     d_id;   /* device ID */
+       u8      rev;
+       u8      prog_intf;
+       u8      subclass;
+       u8      base_class;
+       u32     subsystem_id;
+       union win_slot_encoding win_slot;
+       u32     ser;    /* serial number */
+} __packed;
+
+/**
+ * struct hv_msi_desc
+ * @vector:            IDT entry
+ * @delivery_mode:     As defined in Intel's Programmer's
+ *                     Reference Manual, Volume 3, Chapter 8.
+ * @vector_count:      Number of contiguous entries in the
+ *                     Interrupt Descriptor Table that are
+ *                     occupied by this Message-Signaled
+ *                     Interrupt. For "MSI", as first defined
+ *                     in PCI 2.2, this can be between 1 and
+ *                     32. For "MSI-X," as first defined in PCI
+ *                     3.0, this must be 1, as each MSI-X table
+ *                     entry would have its own descriptor.
+ * @reserved:          Empty space
+ * @cpu_mask:          All the target virtual processors.
+ */
+struct hv_msi_desc {
+       u8      vector;
+       u8      delivery_mode;
+       u16     vector_count;
+       u32     reserved;
+       u64     cpu_mask;
+} __packed;
+
+/**
+ * struct tran_int_desc
+ * @reserved:          unused, padding
+ * @vector_count:      same as in hv_msi_desc
+ * @data:              This is the "data payload" value that is
+ *                     written by the device when it generates
+ *                     a message-signaled interrupt, either MSI
+ *                     or MSI-X.
+ * @address:           This is the address to which the data
+ *                     payload is written on interrupt
+ *                     generation.
+ */
+struct tran_int_desc {
+       u16     reserved;
+       u16     vector_count;
+       u32     data;
+       u64     address;
+} __packed;
+
+/*
+ * A generic message format for virtual PCI.
+ * Specific message formats are defined later in the file.
+ */
+
+struct pci_message {
+       u32 message_type;
+} __packed;
+
+struct pci_child_message {
+       u32 message_type;
+       union win_slot_encoding wslot;
+} __packed;
+
+struct pci_incoming_message {
+       struct vmpacket_descriptor hdr;
+       struct pci_message message_type;
+} __packed;
+
+struct pci_response {
+       struct vmpacket_descriptor hdr;
+       s32 status;                     /* negative values are failures */
+} __packed;
+
+struct pci_packet {
+       void (*completion_func)(void *context, struct pci_response *resp,
+                               int resp_packet_size);
+       void *compl_ctxt;
+       struct pci_message message;
+};
+
+/*
+ * Specific message types supporting the PCI protocol.
+ */
+
+/*
+ * Version negotiation message. Sent from the guest to the host.
+ * The guest is free to try different versions until the host
+ * accepts the version.
+ *
+ * pci_version: The protocol version requested.
+ * is_last_attempt: If TRUE, this is the last version guest will request.
+ * reservedz: Reserved field, set to zero.
+ */
+
+struct pci_version_request {
+       struct pci_message message_type;
+       enum pci_message_type protocol_version;
+} __packed;
+
+/*
+ * Bus D0 Entry.  This is sent from the guest to the host when the virtual
+ * bus (PCI Express port) is ready for action.
+ */
+
+struct pci_bus_d0_entry {
+       struct pci_message message_type;
+       u32 reserved;
+       u64 mmio_base;
+} __packed;
+
+struct pci_bus_relations {
+       struct pci_incoming_message incoming;
+       u32 device_count;
+       struct pci_function_description func[1];
+} __packed;
+
+struct pci_q_res_req_response {
+       struct vmpacket_descriptor hdr;
+       s32 status;                     /* negative values are failures */
+       u32 probed_bar[6];
+} __packed;
+
+struct pci_set_power {
+       struct pci_message message_type;
+       union win_slot_encoding wslot;
+       u32 power_state;                /* In Windows terms */
+       u32 reserved;
+} __packed;
+
+struct pci_set_power_response {
+       struct vmpacket_descriptor hdr;
+       s32 status;                     /* negative values are failures */
+       union win_slot_encoding wslot;
+       u32 resultant_state;            /* In Windows terms */
+       u32 reserved;
+} __packed;
+
+struct pci_resources_assigned {
+       struct pci_message message_type;
+       union win_slot_encoding wslot;
+       u8 memory_range[0x14][6];       /* not used here */
+       u32 msi_descriptors;
+       u32 reserved[4];
+} __packed;
+
+struct pci_create_interrupt {
+       struct pci_message message_type;
+       union win_slot_encoding wslot;
+       struct hv_msi_desc int_desc;
+} __packed;
+
+struct pci_create_int_response {
+       struct pci_response response;
+       u32 reserved;
+       struct tran_int_desc int_desc;
+} __packed;
+
+struct pci_delete_interrupt {
+       struct pci_message message_type;
+       union win_slot_encoding wslot;
+       struct tran_int_desc int_desc;
+} __packed;
+
+struct pci_dev_incoming {
+       struct pci_incoming_message incoming;
+       union win_slot_encoding wslot;
+} __packed;
+
+struct pci_eject_response {
+       u32 message_type;
+       union win_slot_encoding wslot;
+       u32 status;
+} __packed;
+
+static int pci_ring_size = (4 * PAGE_SIZE);
+
+/*
+ * Definitions or interrupt steering hypercall.
+ */
+#define HV_PARTITION_ID_SELF           ((u64)-1)
+#define HVCALL_RETARGET_INTERRUPT      0x7e
+
+struct retarget_msi_interrupt {
+       u64     partition_id;           /* use "self" */
+       u64     device_id;
+       u32     source;                 /* 1 for MSI(-X) */
+       u32     reserved1;
+       u32     address;
+       u32     data;
+       u64     reserved2;
+       u32     vector;
+       u32     flags;
+       u64     vp_mask;
+} __packed;
+
+/*
+ * Driver specific state.
+ */
+
+enum hv_pcibus_state {
+       hv_pcibus_init = 0,
+       hv_pcibus_probed,
+       hv_pcibus_installed,
+       hv_pcibus_maximum
+};
+
+struct hv_pcibus_device {
+       struct pci_sysdata sysdata;
+       enum hv_pcibus_state state;
+       atomic_t remove_lock;
+       struct hv_device *hdev;
+       resource_size_t low_mmio_space;
+       resource_size_t high_mmio_space;
+       struct resource *mem_config;
+       struct resource *low_mmio_res;
+       struct resource *high_mmio_res;
+       struct completion *survey_event;
+       struct completion remove_event;
+       struct pci_bus *pci_bus;
+       spinlock_t config_lock; /* Avoid two threads writing index page */
+       spinlock_t device_list_lock;    /* Protect lists below */
+       void __iomem *cfg_addr;
+
+       struct semaphore enum_sem;
+       struct list_head resources_for_children;
+
+       struct list_head children;
+       struct list_head dr_list;
+       struct work_struct wrk;
+
+       struct msi_domain_info msi_info;
+       struct msi_controller msi_chip;
+       struct irq_domain *irq_domain;
+};
+
+/*
+ * Tracks "Device Relations" messages from the host, which must be both
+ * processed in order and deferred so that they don't run in the context
+ * of the incoming packet callback.
+ */
+struct hv_dr_work {
+       struct work_struct wrk;
+       struct hv_pcibus_device *bus;
+};
+
+struct hv_dr_state {
+       struct list_head list_entry;
+       u32 device_count;
+       struct pci_function_description func[1];
+};
+
+enum hv_pcichild_state {
+       hv_pcichild_init = 0,
+       hv_pcichild_requirements,
+       hv_pcichild_resourced,
+       hv_pcichild_ejecting,
+       hv_pcichild_maximum
+};
+
+enum hv_pcidev_ref_reason {
+       hv_pcidev_ref_invalid = 0,
+       hv_pcidev_ref_initial,
+       hv_pcidev_ref_by_slot,
+       hv_pcidev_ref_packet,
+       hv_pcidev_ref_pnp,
+       hv_pcidev_ref_childlist,
+       hv_pcidev_irqdata,
+       hv_pcidev_ref_max
+};
+
+struct hv_pci_dev {
+       /* List protected by pci_rescan_remove_lock */
+       struct list_head list_entry;
+       atomic_t refs;
+       enum hv_pcichild_state state;
+       struct pci_function_description desc;
+       bool reported_missing;
+       struct hv_pcibus_device *hbus;
+       struct work_struct wrk;
+
+       /*
+        * What would be observed if one wrote 0xFFFFFFFF to a BAR and then
+        * read it back, for each of the BAR offsets within config space.
+        */
+       u32 probed_bar[6];
+};
+
+struct hv_pci_compl {
+       struct completion host_event;
+       s32 completion_status;
+};
+
+/**
+ * hv_pci_generic_compl() - Invoked for a completion packet
+ * @context:           Set up by the sender of the packet.
+ * @resp:              The response packet
+ * @resp_packet_size:  Size in bytes of the packet
+ *
+ * This function is used to trigger an event and report status
+ * for any message for which the completion packet contains a
+ * status and nothing else.
+ */
+static
+void
+hv_pci_generic_compl(void *context, struct pci_response *resp,
+                    int resp_packet_size)
+{
+       struct hv_pci_compl *comp_pkt = context;
+
+       if (resp_packet_size >= offsetofend(struct pci_response, status))
+               comp_pkt->completion_status = resp->status;
+       complete(&comp_pkt->host_event);
+}
+
+static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus,
+                                               u32 wslot);
+static void get_pcichild(struct hv_pci_dev *hv_pcidev,
+                        enum hv_pcidev_ref_reason reason);
+static void put_pcichild(struct hv_pci_dev *hv_pcidev,
+                        enum hv_pcidev_ref_reason reason);
+
+static void get_hvpcibus(struct hv_pcibus_device *hv_pcibus);
+static void put_hvpcibus(struct hv_pcibus_device *hv_pcibus);
+
+/**
+ * devfn_to_wslot() - Convert from Linux PCI slot to Windows
+ * @devfn:     The Linux representation of PCI slot
+ *
+ * Windows uses a slightly different representation of PCI slot.
+ *
+ * Return: The Windows representation
+ */
+static u32 devfn_to_wslot(int devfn)
+{
+       union win_slot_encoding wslot;
+
+       wslot.slot = 0;
+       wslot.bits.func = PCI_SLOT(devfn) | (PCI_FUNC(devfn) << 5);
+
+       return wslot.slot;
+}
+
+/**
+ * wslot_to_devfn() - Convert from Windows PCI slot to Linux
+ * @wslot:     The Windows representation of PCI slot
+ *
+ * Windows uses a slightly different representation of PCI slot.
+ *
+ * Return: The Linux representation
+ */
+static int wslot_to_devfn(u32 wslot)
+{
+       union win_slot_encoding slot_no;
+
+       slot_no.slot = wslot;
+       return PCI_DEVFN(0, slot_no.bits.func);
+}
+
+/*
+ * PCI Configuration Space for these root PCI buses is implemented as a pair
+ * of pages in memory-mapped I/O space.  Writing to the first page chooses
+ * the PCI function being written or read.  Once the first page has been
+ * written to, the following page maps in the entire configuration space of
+ * the function.
+ */
+
+/**
+ * _hv_pcifront_read_config() - Internal PCI config read
+ * @hpdev:     The PCI driver's representation of the device
+ * @where:     Offset within config space
+ * @size:      Size of the transfer
+ * @val:       Pointer to the buffer receiving the data
+ */
+static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
+                                    int size, u32 *val)
+{
+       unsigned long flags;
+       void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + where;
+
+       /*
+        * If the attempt is to read the IDs or the ROM BAR, simulate that.
+        */
+       if (where + size <= PCI_COMMAND) {
+               memcpy(val, ((u8 *)&hpdev->desc.v_id) + where, size);
+       } else if (where >= PCI_CLASS_REVISION && where + size <=
+                  PCI_CACHE_LINE_SIZE) {
+               memcpy(val, ((u8 *)&hpdev->desc.rev) + where -
+                      PCI_CLASS_REVISION, size);
+       } else if (where >= PCI_SUBSYSTEM_VENDOR_ID && where + size <=
+                  PCI_ROM_ADDRESS) {
+               memcpy(val, (u8 *)&hpdev->desc.subsystem_id + where -
+                      PCI_SUBSYSTEM_VENDOR_ID, size);
+       } else if (where >= PCI_ROM_ADDRESS && where + size <=
+                  PCI_CAPABILITY_LIST) {
+               /* ROM BARs are unimplemented */
+               *val = 0;
+       } else if (where >= PCI_INTERRUPT_LINE && where + size <=
+                  PCI_INTERRUPT_PIN) {
+               /*
+                * Interrupt Line and Interrupt PIN are hard-wired to zero
+                * because this front-end only supports message-signaled
+                * interrupts.
+                */
+               *val = 0;
+       } else if (where + size <= CFG_PAGE_SIZE) {
+               spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
+               /* Choose the function to be read. (See comment above) */
+               writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
+               /* Read from that function's config space. */
+               switch (size) {
+               case 1:
+                       *val = readb(addr);
+                       break;
+               case 2:
+                       *val = readw(addr);
+                       break;
+               default:
+                       *val = readl(addr);
+                       break;
+               }
+               spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
+       } else {
+               dev_err(&hpdev->hbus->hdev->device,
+                       "Attempt to read beyond a function's config space.\n");
+       }
+}
+
+/**
+ * _hv_pcifront_write_config() - Internal PCI config write
+ * @hpdev:     The PCI driver's representation of the device
+ * @where:     Offset within config space
+ * @size:      Size of the transfer
+ * @val:       The data being transferred
+ */
+static void _hv_pcifront_write_config(struct hv_pci_dev *hpdev, int where,
+                                     int size, u32 val)
+{
+       unsigned long flags;
+       void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + where;
+
+       if (where >= PCI_SUBSYSTEM_VENDOR_ID &&
+           where + size <= PCI_CAPABILITY_LIST) {
+               /* SSIDs and ROM BARs are read-only */
+       } else if (where >= PCI_COMMAND && where + size <= CFG_PAGE_SIZE) {
+               spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
+               /* Choose the function to be written. (See comment above) */
+               writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
+               /* Write to that function's config space. */
+               switch (size) {
+               case 1:
+                       writeb(val, addr);
+                       break;
+               case 2:
+                       writew(val, addr);
+                       break;
+               default:
+                       writel(val, addr);
+                       break;
+               }
+               spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
+       } else {
+               dev_err(&hpdev->hbus->hdev->device,
+                       "Attempt to write beyond a function's config space.\n");
+       }
+}
+
+/**
+ * hv_pcifront_read_config() - Read configuration space
+ * @bus: PCI Bus structure
+ * @devfn: Device/function
+ * @where: Offset from base
+ * @size: Byte/word/dword
+ * @val: Value to be read
+ *
+ * Return: PCIBIOS_SUCCESSFUL on success
+ *        PCIBIOS_DEVICE_NOT_FOUND on failure
+ */
+static int hv_pcifront_read_config(struct pci_bus *bus, unsigned int devfn,
+                                  int where, int size, u32 *val)
+{
+       struct hv_pcibus_device *hbus =
+               container_of(bus->sysdata, struct hv_pcibus_device, sysdata);
+       struct hv_pci_dev *hpdev;
+
+       hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(devfn));
+       if (!hpdev)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       _hv_pcifront_read_config(hpdev, where, size, val);
+
+       put_pcichild(hpdev, hv_pcidev_ref_by_slot);
+       return PCIBIOS_SUCCESSFUL;
+}
+
+/**
+ * hv_pcifront_write_config() - Write configuration space
+ * @bus: PCI Bus structure
+ * @devfn: Device/function
+ * @where: Offset from base
+ * @size: Byte/word/dword
+ * @val: Value to be written to device
+ *
+ * Return: PCIBIOS_SUCCESSFUL on success
+ *        PCIBIOS_DEVICE_NOT_FOUND on failure
+ */
+static int hv_pcifront_write_config(struct pci_bus *bus, unsigned int devfn,
+                                   int where, int size, u32 val)
+{
+       struct hv_pcibus_device *hbus =
+           container_of(bus->sysdata, struct hv_pcibus_device, sysdata);
+       struct hv_pci_dev *hpdev;
+
+       hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(devfn));
+       if (!hpdev)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       _hv_pcifront_write_config(hpdev, where, size, val);
+
+       put_pcichild(hpdev, hv_pcidev_ref_by_slot);
+       return PCIBIOS_SUCCESSFUL;
+}
+
+/* PCIe operations */
+static struct pci_ops hv_pcifront_ops = {
+       .read  = hv_pcifront_read_config,
+       .write = hv_pcifront_write_config,
+};
+
+/* Interrupt management hooks */
+static void hv_int_desc_free(struct hv_pci_dev *hpdev,
+                            struct tran_int_desc *int_desc)
+{
+       struct pci_delete_interrupt *int_pkt;
+       struct {
+               struct pci_packet pkt;
+               u8 buffer[sizeof(struct pci_delete_interrupt) -
+                         sizeof(struct pci_message)];
+       } ctxt;
+
+       memset(&ctxt, 0, sizeof(ctxt));
+       int_pkt = (struct pci_delete_interrupt *)&ctxt.pkt.message;
+       int_pkt->message_type.message_type =
+               PCI_DELETE_INTERRUPT_MESSAGE;
+       int_pkt->wslot.slot = hpdev->desc.win_slot.slot;
+       int_pkt->int_desc = *int_desc;
+       vmbus_sendpacket(hpdev->hbus->hdev->channel, int_pkt, sizeof(*int_pkt),
+                        (unsigned long)&ctxt.pkt, VM_PKT_DATA_INBAND, 0);
+       kfree(int_desc);
+}
+
+/**
+ * hv_msi_free() - Free the MSI.
+ * @domain:    The interrupt domain pointer
+ * @info:      Extra MSI-related context
+ * @irq:       Identifies the IRQ.
+ *
+ * The Hyper-V parent partition and hypervisor are tracking the
+ * messages that are in use, keeping the interrupt redirection
+ * table up to date.  This callback sends a message that frees
+ * the IRT entry and related tracking nonsense.
+ */
+static void hv_msi_free(struct irq_domain *domain, struct msi_domain_info *info,
+                       unsigned int irq)
+{
+       struct hv_pcibus_device *hbus;
+       struct hv_pci_dev *hpdev;
+       struct pci_dev *pdev;
+       struct tran_int_desc *int_desc;
+       struct irq_data *irq_data = irq_domain_get_irq_data(domain, irq);
+       struct msi_desc *msi = irq_data_get_msi_desc(irq_data);
+
+       pdev = msi_desc_to_pci_dev(msi);
+       hbus = info->data;
+       hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn));
+       if (!hpdev)
+               return;
+
+       int_desc = irq_data_get_irq_chip_data(irq_data);
+       if (int_desc) {
+               irq_data->chip_data = NULL;
+               hv_int_desc_free(hpdev, int_desc);
+       }
+
+       put_pcichild(hpdev, hv_pcidev_ref_by_slot);
+}
+
+static int hv_set_affinity(struct irq_data *data, const struct cpumask *dest,
+                          bool force)
+{
+       struct irq_data *parent = data->parent_data;
+
+       return parent->chip->irq_set_affinity(parent, dest, force);
+}
+
+void hv_irq_mask(struct irq_data *data)
+{
+       pci_msi_mask_irq(data);
+}
+
+/**
+ * hv_irq_unmask() - "Unmask" the IRQ by setting its current
+ * affinity.
+ * @data:      Describes the IRQ
+ *
+ * Build new a destination for the MSI and make a hypercall to
+ * update the Interrupt Redirection Table. "Device Logical ID"
+ * is built out of this PCI bus's instance GUID and the function
+ * number of the device.
+ */
+void hv_irq_unmask(struct irq_data *data)
+{
+       struct msi_desc *msi_desc = irq_data_get_msi_desc(data);
+       struct irq_cfg *cfg = irqd_cfg(data);
+       struct retarget_msi_interrupt params;
+       struct hv_pcibus_device *hbus;
+       struct cpumask *dest;
+       struct pci_bus *pbus;
+       struct pci_dev *pdev;
+       int cpu;
+
+       dest = irq_data_get_affinity_mask(data);
+       pdev = msi_desc_to_pci_dev(msi_desc);
+       pbus = pdev->bus;
+       hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata);
+
+       memset(&params, 0, sizeof(params));
+       params.partition_id = HV_PARTITION_ID_SELF;
+       params.source = 1; /* MSI(-X) */
+       params.address = msi_desc->msg.address_lo;
+       params.data = msi_desc->msg.data;
+       params.device_id = (hbus->hdev->dev_instance.b[5] << 24) |
+                          (hbus->hdev->dev_instance.b[4] << 16) |
+                          (hbus->hdev->dev_instance.b[7] << 8) |
+                          (hbus->hdev->dev_instance.b[6] & 0xf8) |
+                          PCI_FUNC(pdev->devfn);
+       params.vector = cfg->vector;
+
+       for_each_cpu_and(cpu, dest, cpu_online_mask)
+               params.vp_mask |= (1ULL << vmbus_cpu_number_to_vp_number(cpu));
+
+       hv_do_hypercall(HVCALL_RETARGET_INTERRUPT, &params, NULL);
+
+       pci_msi_unmask_irq(data);
+}
+
+struct compose_comp_ctxt {
+       struct hv_pci_compl comp_pkt;
+       struct tran_int_desc int_desc;
+};
+
+static void hv_pci_compose_compl(void *context, struct pci_response *resp,
+                                int resp_packet_size)
+{
+       struct compose_comp_ctxt *comp_pkt = context;
+       struct pci_create_int_response *int_resp =
+               (struct pci_create_int_response *)resp;
+
+       comp_pkt->comp_pkt.completion_status = resp->status;
+       comp_pkt->int_desc = int_resp->int_desc;
+       complete(&comp_pkt->comp_pkt.host_event);
+}
+
+/**
+ * hv_compose_msi_msg() - Supplies a valid MSI address/data
+ * @data:      Everything about this MSI
+ * @msg:       Buffer that is filled in by this function
+ *
+ * This function unpacks the IRQ looking for target CPU set, IDT
+ * vector and mode and sends a message to the parent partition
+ * asking for a mapping for that tuple in this partition.  The
+ * response supplies a data value and address to which that data
+ * should be written to trigger that interrupt.
+ */
+static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct irq_cfg *cfg = irqd_cfg(data);
+       struct hv_pcibus_device *hbus;
+       struct hv_pci_dev *hpdev;
+       struct pci_bus *pbus;
+       struct pci_dev *pdev;
+       struct pci_create_interrupt *int_pkt;
+       struct compose_comp_ctxt comp;
+       struct tran_int_desc *int_desc;
+       struct cpumask *affinity;
+       struct {
+               struct pci_packet pkt;
+               u8 buffer[sizeof(struct pci_create_interrupt) -
+                         sizeof(struct pci_message)];
+       } ctxt;
+       int cpu;
+       int ret;
+
+       pdev = msi_desc_to_pci_dev(irq_data_get_msi_desc(data));
+       pbus = pdev->bus;
+       hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata);
+       hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn));
+       if (!hpdev)
+               goto return_null_message;
+
+       /* Free any previous message that might have already been composed. */
+       if (data->chip_data) {
+               int_desc = data->chip_data;
+               data->chip_data = NULL;
+               hv_int_desc_free(hpdev, int_desc);
+       }
+
+       int_desc = kzalloc(sizeof(*int_desc), GFP_KERNEL);
+       if (!int_desc)
+               goto drop_reference;
+
+       memset(&ctxt, 0, sizeof(ctxt));
+       init_completion(&comp.comp_pkt.host_event);
+       ctxt.pkt.completion_func = hv_pci_compose_compl;
+       ctxt.pkt.compl_ctxt = &comp;
+       int_pkt = (struct pci_create_interrupt *)&ctxt.pkt.message;
+       int_pkt->message_type.message_type = PCI_CREATE_INTERRUPT_MESSAGE;
+       int_pkt->wslot.slot = hpdev->desc.win_slot.slot;
+       int_pkt->int_desc.vector = cfg->vector;
+       int_pkt->int_desc.vector_count = 1;
+       int_pkt->int_desc.delivery_mode =
+               (apic->irq_delivery_mode == dest_LowestPrio) ? 1 : 0;
+
+       /*
+        * This bit doesn't have to work on machines with more than 64
+        * processors because Hyper-V only supports 64 in a guest.
+        */
+       affinity = irq_data_get_affinity_mask(data);
+       for_each_cpu_and(cpu, affinity, cpu_online_mask) {
+               int_pkt->int_desc.cpu_mask |=
+                       (1ULL << vmbus_cpu_number_to_vp_number(cpu));
+       }
+
+       ret = vmbus_sendpacket(hpdev->hbus->hdev->channel, int_pkt,
+                              sizeof(*int_pkt), (unsigned long)&ctxt.pkt,
+                              VM_PKT_DATA_INBAND,
+                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (!ret)
+               wait_for_completion(&comp.comp_pkt.host_event);
+
+       if (comp.comp_pkt.completion_status < 0) {
+               dev_err(&hbus->hdev->device,
+                       "Request for interrupt failed: 0x%x",
+                       comp.comp_pkt.completion_status);
+               goto free_int_desc;
+       }
+
+       /*
+        * Record the assignment so that this can be unwound later. Using
+        * irq_set_chip_data() here would be appropriate, but the lock it takes
+        * is already held.
+        */
+       *int_desc = comp.int_desc;
+       data->chip_data = int_desc;
+
+       /* Pass up the result. */
+       msg->address_hi = comp.int_desc.address >> 32;
+       msg->address_lo = comp.int_desc.address & 0xffffffff;
+       msg->data = comp.int_desc.data;
+
+       put_pcichild(hpdev, hv_pcidev_ref_by_slot);
+       return;
+
+free_int_desc:
+       kfree(int_desc);
+drop_reference:
+       put_pcichild(hpdev, hv_pcidev_ref_by_slot);
+return_null_message:
+       msg->address_hi = 0;
+       msg->address_lo = 0;
+       msg->data = 0;
+}
+
+/* HW Interrupt Chip Descriptor */
+static struct irq_chip hv_msi_irq_chip = {
+       .name                   = "Hyper-V PCIe MSI",
+       .irq_compose_msi_msg    = hv_compose_msi_msg,
+       .irq_set_affinity       = hv_set_affinity,
+       .irq_ack                = irq_chip_ack_parent,
+       .irq_mask               = hv_irq_mask,
+       .irq_unmask             = hv_irq_unmask,
+};
+
+static irq_hw_number_t hv_msi_domain_ops_get_hwirq(struct msi_domain_info *info,
+                                                  msi_alloc_info_t *arg)
+{
+       return arg->msi_hwirq;
+}
+
+static struct msi_domain_ops hv_msi_ops = {
+       .get_hwirq      = hv_msi_domain_ops_get_hwirq,
+       .msi_prepare    = pci_msi_prepare,
+       .set_desc       = pci_msi_set_desc,
+       .msi_free       = hv_msi_free,
+};
+
+/**
+ * hv_pcie_init_irq_domain() - Initialize IRQ domain
+ * @hbus:      The root PCI bus
+ *
+ * This function creates an IRQ domain which will be used for
+ * interrupts from devices that have been passed through.  These
+ * devices only support MSI and MSI-X, not line-based interrupts
+ * or simulations of line-based interrupts through PCIe's
+ * fabric-layer messages.  Because interrupts are remapped, we
+ * can support multi-message MSI here.
+ *
+ * Return: '0' on success and error value on failure
+ */
+static int hv_pcie_init_irq_domain(struct hv_pcibus_device *hbus)
+{
+       hbus->msi_info.chip = &hv_msi_irq_chip;
+       hbus->msi_info.ops = &hv_msi_ops;
+       hbus->msi_info.flags = (MSI_FLAG_USE_DEF_DOM_OPS |
+               MSI_FLAG_USE_DEF_CHIP_OPS | MSI_FLAG_MULTI_PCI_MSI |
+               MSI_FLAG_PCI_MSIX);
+       hbus->msi_info.handler = handle_edge_irq;
+       hbus->msi_info.handler_name = "edge";
+       hbus->msi_info.data = hbus;
+       hbus->irq_domain = pci_msi_create_irq_domain(hbus->sysdata.fwnode,
+                                                    &hbus->msi_info,
+                                                    x86_vector_domain);
+       if (!hbus->irq_domain) {
+               dev_err(&hbus->hdev->device,
+                       "Failed to build an MSI IRQ domain\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+/**
+ * get_bar_size() - Get the address space consumed by a BAR
+ * @bar_val:   Value that a BAR returned after -1 was written
+ *              to it.
+ *
+ * This function returns the size of the BAR, rounded up to 1
+ * page.  It has to be rounded up because the hypervisor's page
+ * table entry that maps the BAR into the VM can't specify an
+ * offset within a page.  The invariant is that the hypervisor
+ * must place any BARs of smaller than page length at the
+ * beginning of a page.
+ *
+ * Return:     Size in bytes of the consumed MMIO space.
+ */
+static u64 get_bar_size(u64 bar_val)
+{
+       return round_up((1 + ~(bar_val & PCI_BASE_ADDRESS_MEM_MASK)),
+                       PAGE_SIZE);
+}
+
+/**
+ * survey_child_resources() - Total all MMIO requirements
+ * @hbus:      Root PCI bus, as understood by this driver
+ */
+static void survey_child_resources(struct hv_pcibus_device *hbus)
+{
+       struct list_head *iter;
+       struct hv_pci_dev *hpdev;
+       resource_size_t bar_size = 0;
+       unsigned long flags;
+       struct completion *event;
+       u64 bar_val;
+       int i;
+
+       /* If nobody is waiting on the answer, don't compute it. */
+       event = xchg(&hbus->survey_event, NULL);
+       if (!event)
+               return;
+
+       /* If the answer has already been computed, go with it. */
+       if (hbus->low_mmio_space || hbus->high_mmio_space) {
+               complete(event);
+               return;
+       }
+
+       spin_lock_irqsave(&hbus->device_list_lock, flags);
+
+       /*
+        * Due to an interesting quirk of the PCI spec, all memory regions
+        * for a child device are a power of 2 in size and aligned in memory,
+        * so it's sufficient to just add them up without tracking alignment.
+        */
+       list_for_each(iter, &hbus->children) {
+               hpdev = container_of(iter, struct hv_pci_dev, list_entry);
+               for (i = 0; i < 6; i++) {
+                       if (hpdev->probed_bar[i] & PCI_BASE_ADDRESS_SPACE_IO)
+                               dev_err(&hbus->hdev->device,
+                                       "There's an I/O BAR in this list!\n");
+
+                       if (hpdev->probed_bar[i] != 0) {
+                               /*
+                                * A probed BAR has all the upper bits set that
+                                * can be changed.
+                                */
+
+                               bar_val = hpdev->probed_bar[i];
+                               if (bar_val & PCI_BASE_ADDRESS_MEM_TYPE_64)
+                                       bar_val |=
+                                       ((u64)hpdev->probed_bar[++i] << 32);
+                               else
+                                       bar_val |= 0xffffffff00000000ULL;
+
+                               bar_size = get_bar_size(bar_val);
+
+                               if (bar_val & PCI_BASE_ADDRESS_MEM_TYPE_64)
+                                       hbus->high_mmio_space += bar_size;
+                               else
+                                       hbus->low_mmio_space += bar_size;
+                       }
+               }
+       }
+
+       spin_unlock_irqrestore(&hbus->device_list_lock, flags);
+       complete(event);
+}
+
+/**
+ * prepopulate_bars() - Fill in BARs with defaults
+ * @hbus:      Root PCI bus, as understood by this driver
+ *
+ * The core PCI driver code seems much, much happier if the BARs
+ * for a device have values upon first scan. So fill them in.
+ * The algorithm below works down from large sizes to small,
+ * attempting to pack the assignments optimally. The assumption,
+ * enforced in other parts of the code, is that the beginning of
+ * the memory-mapped I/O space will be aligned on the largest
+ * BAR size.
+ */
+static void prepopulate_bars(struct hv_pcibus_device *hbus)
+{
+       resource_size_t high_size = 0;
+       resource_size_t low_size = 0;
+       resource_size_t high_base = 0;
+       resource_size_t low_base = 0;
+       resource_size_t bar_size;
+       struct hv_pci_dev *hpdev;
+       struct list_head *iter;
+       unsigned long flags;
+       u64 bar_val;
+       u32 command;
+       bool high;
+       int i;
+
+       if (hbus->low_mmio_space) {
+               low_size = 1ULL << (63 - __builtin_clzll(hbus->low_mmio_space));
+               low_base = hbus->low_mmio_res->start;
+       }
+
+       if (hbus->high_mmio_space) {
+               high_size = 1ULL <<
+                       (63 - __builtin_clzll(hbus->high_mmio_space));
+               high_base = hbus->high_mmio_res->start;
+       }
+
+       spin_lock_irqsave(&hbus->device_list_lock, flags);
+
+       /* Pick addresses for the BARs. */
+       do {
+               list_for_each(iter, &hbus->children) {
+                       hpdev = container_of(iter, struct hv_pci_dev,
+                                            list_entry);
+                       for (i = 0; i < 6; i++) {
+                               bar_val = hpdev->probed_bar[i];
+                               if (bar_val == 0)
+                                       continue;
+                               high = bar_val & PCI_BASE_ADDRESS_MEM_TYPE_64;
+                               if (high) {
+                                       bar_val |=
+                                               ((u64)hpdev->probed_bar[i + 1]
+                                                << 32);
+                               } else {
+                                       bar_val |= 0xffffffffULL << 32;
+                               }
+                               bar_size = get_bar_size(bar_val);
+                               if (high) {
+                                       if (high_size != bar_size) {
+                                               i++;
+                                               continue;
+                                       }
+                                       _hv_pcifront_write_config(hpdev,
+                                               PCI_BASE_ADDRESS_0 + (4 * i),
+                                               4,
+                                               (u32)(high_base & 0xffffff00));
+                                       i++;
+                                       _hv_pcifront_write_config(hpdev,
+                                               PCI_BASE_ADDRESS_0 + (4 * i),
+                                               4, (u32)(high_base >> 32));
+                                       high_base += bar_size;
+                               } else {
+                                       if (low_size != bar_size)
+                                               continue;
+                                       _hv_pcifront_write_config(hpdev,
+                                               PCI_BASE_ADDRESS_0 + (4 * i),
+                                               4,
+                                               (u32)(low_base & 0xffffff00));
+                                       low_base += bar_size;
+                               }
+                       }
+                       if (high_size <= 1 && low_size <= 1) {
+                               /* Set the memory enable bit. */
+                               _hv_pcifront_read_config(hpdev, PCI_COMMAND, 2,
+                                                        &command);
+                               command |= PCI_COMMAND_MEMORY;
+                               _hv_pcifront_write_config(hpdev, PCI_COMMAND, 2,
+                                                         command);
+                               break;
+                       }
+               }
+
+               high_size >>= 1;
+               low_size >>= 1;
+       }  while (high_size || low_size);
+
+       spin_unlock_irqrestore(&hbus->device_list_lock, flags);
+}
+
+/**
+ * create_root_hv_pci_bus() - Expose a new root PCI bus
+ * @hbus:      Root PCI bus, as understood by this driver
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int create_root_hv_pci_bus(struct hv_pcibus_device *hbus)
+{
+       /* Register the device */
+       hbus->pci_bus = pci_create_root_bus(&hbus->hdev->device,
+                                           0, /* bus number is always zero */
+                                           &hv_pcifront_ops,
+                                           &hbus->sysdata,
+                                           &hbus->resources_for_children);
+       if (!hbus->pci_bus)
+               return -ENODEV;
+
+       hbus->pci_bus->msi = &hbus->msi_chip;
+       hbus->pci_bus->msi->dev = &hbus->hdev->device;
+
+       pci_scan_child_bus(hbus->pci_bus);
+       pci_bus_assign_resources(hbus->pci_bus);
+       pci_bus_add_devices(hbus->pci_bus);
+       hbus->state = hv_pcibus_installed;
+       return 0;
+}
+
+struct q_res_req_compl {
+       struct completion host_event;
+       struct hv_pci_dev *hpdev;
+};
+
+/**
+ * q_resource_requirements() - Query Resource Requirements
+ * @context:           The completion context.
+ * @resp:              The response that came from the host.
+ * @resp_packet_size:  The size in bytes of resp.
+ *
+ * This function is invoked on completion of a Query Resource
+ * Requirements packet.
+ */
+static void q_resource_requirements(void *context, struct pci_response *resp,
+                                   int resp_packet_size)
+{
+       struct q_res_req_compl *completion = context;
+       struct pci_q_res_req_response *q_res_req =
+               (struct pci_q_res_req_response *)resp;
+       int i;
+
+       if (resp->status < 0) {
+               dev_err(&completion->hpdev->hbus->hdev->device,
+                       "query resource requirements failed: %x\n",
+                       resp->status);
+       } else {
+               for (i = 0; i < 6; i++) {
+                       completion->hpdev->probed_bar[i] =
+                               q_res_req->probed_bar[i];
+               }
+       }
+
+       complete(&completion->host_event);
+}
+
+static void get_pcichild(struct hv_pci_dev *hpdev,
+                           enum hv_pcidev_ref_reason reason)
+{
+       atomic_inc(&hpdev->refs);
+}
+
+static void put_pcichild(struct hv_pci_dev *hpdev,
+                           enum hv_pcidev_ref_reason reason)
+{
+       if (atomic_dec_and_test(&hpdev->refs))
+               kfree(hpdev);
+}
+
+/**
+ * new_pcichild_device() - Create a new child device
+ * @hbus:      The internal struct tracking this root PCI bus.
+ * @desc:      The information supplied so far from the host
+ *              about the device.
+ *
+ * This function creates the tracking structure for a new child
+ * device and kicks off the process of figuring out what it is.
+ *
+ * Return: Pointer to the new tracking struct
+ */
+static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus,
+               struct pci_function_description *desc)
+{
+       struct hv_pci_dev *hpdev;
+       struct pci_child_message *res_req;
+       struct q_res_req_compl comp_pkt;
+       union {
+       struct pci_packet init_packet;
+               u8 buffer[0x100];
+       } pkt;
+       unsigned long flags;
+       int ret;
+
+       hpdev = kzalloc(sizeof(*hpdev), GFP_ATOMIC);
+       if (!hpdev)
+               return NULL;
+
+       hpdev->hbus = hbus;
+
+       memset(&pkt, 0, sizeof(pkt));
+       init_completion(&comp_pkt.host_event);
+       comp_pkt.hpdev = hpdev;
+       pkt.init_packet.compl_ctxt = &comp_pkt;
+       pkt.init_packet.completion_func = q_resource_requirements;
+       res_req = (struct pci_child_message *)&pkt.init_packet.message;
+       res_req->message_type = PCI_QUERY_RESOURCE_REQUIREMENTS;
+       res_req->wslot.slot = desc->win_slot.slot;
+
+       ret = vmbus_sendpacket(hbus->hdev->channel, res_req,
+                              sizeof(struct pci_child_message),
+                              (unsigned long)&pkt.init_packet,
+                              VM_PKT_DATA_INBAND,
+                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (ret)
+               goto error;
+
+       wait_for_completion(&comp_pkt.host_event);
+
+       hpdev->desc = *desc;
+       get_pcichild(hpdev, hv_pcidev_ref_initial);
+       get_pcichild(hpdev, hv_pcidev_ref_childlist);
+       spin_lock_irqsave(&hbus->device_list_lock, flags);
+       list_add_tail(&hpdev->list_entry, &hbus->children);
+       spin_unlock_irqrestore(&hbus->device_list_lock, flags);
+       return hpdev;
+
+error:
+       kfree(hpdev);
+       return NULL;
+}
+
+/**
+ * get_pcichild_wslot() - Find device from slot
+ * @hbus:      Root PCI bus, as understood by this driver
+ * @wslot:     Location on the bus
+ *
+ * This function looks up a PCI device and returns the internal
+ * representation of it.  It acquires a reference on it, so that
+ * the device won't be deleted while somebody is using it.  The
+ * caller is responsible for calling put_pcichild() to release
+ * this reference.
+ *
+ * Return:     Internal representation of a PCI device
+ */
+static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus,
+                                            u32 wslot)
+{
+       unsigned long flags;
+       struct hv_pci_dev *iter, *hpdev = NULL;
+
+       spin_lock_irqsave(&hbus->device_list_lock, flags);
+       list_for_each_entry(iter, &hbus->children, list_entry) {
+               if (iter->desc.win_slot.slot == wslot) {
+                       hpdev = iter;
+                       get_pcichild(hpdev, hv_pcidev_ref_by_slot);
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&hbus->device_list_lock, flags);
+
+       return hpdev;
+}
+
+/**
+ * pci_devices_present_work() - Handle new list of child devices
+ * @work:      Work struct embedded in struct hv_dr_work
+ *
+ * "Bus Relations" is the Windows term for "children of this
+ * bus."  The terminology is preserved here for people trying to
+ * debug the interaction between Hyper-V and Linux.  This
+ * function is called when the parent partition reports a list
+ * of functions that should be observed under this PCI Express
+ * port (bus).
+ *
+ * This function updates the list, and must tolerate being
+ * called multiple times with the same information.  The typical
+ * number of child devices is one, with very atypical cases
+ * involving three or four, so the algorithms used here can be
+ * simple and inefficient.
+ *
+ * It must also treat the omission of a previously observed device as
+ * notification that the device no longer exists.
+ *
+ * Note that this function is a work item, and it may not be
+ * invoked in the order that it was queued.  Back to back
+ * updates of the list of present devices may involve queuing
+ * multiple work items, and this one may run before ones that
+ * were sent later. As such, this function only does something
+ * if is the last one in the queue.
+ */
+static void pci_devices_present_work(struct work_struct *work)
+{
+       u32 child_no;
+       bool found;
+       struct list_head *iter;
+       struct pci_function_description *new_desc;
+       struct hv_pci_dev *hpdev;
+       struct hv_pcibus_device *hbus;
+       struct list_head removed;
+       struct hv_dr_work *dr_wrk;
+       struct hv_dr_state *dr = NULL;
+       unsigned long flags;
+
+       dr_wrk = container_of(work, struct hv_dr_work, wrk);
+       hbus = dr_wrk->bus;
+       kfree(dr_wrk);
+
+       INIT_LIST_HEAD(&removed);
+
+       if (down_interruptible(&hbus->enum_sem)) {
+               put_hvpcibus(hbus);
+               return;
+       }
+
+       /* Pull this off the queue and process it if it was the last one. */
+       spin_lock_irqsave(&hbus->device_list_lock, flags);
+       while (!list_empty(&hbus->dr_list)) {
+               dr = list_first_entry(&hbus->dr_list, struct hv_dr_state,
+                                     list_entry);
+               list_del(&dr->list_entry);
+
+               /* Throw this away if the list still has stuff in it. */
+               if (!list_empty(&hbus->dr_list)) {
+                       kfree(dr);
+                       continue;
+               }
+       }
+       spin_unlock_irqrestore(&hbus->device_list_lock, flags);
+
+       if (!dr) {
+               up(&hbus->enum_sem);
+               put_hvpcibus(hbus);
+               return;
+       }
+
+       /* First, mark all existing children as reported missing. */
+       spin_lock_irqsave(&hbus->device_list_lock, flags);
+       list_for_each(iter, &hbus->children) {
+                       hpdev = container_of(iter, struct hv_pci_dev,
+                                            list_entry);
+                       hpdev->reported_missing = true;
+       }
+       spin_unlock_irqrestore(&hbus->device_list_lock, flags);
+
+       /* Next, add back any reported devices. */
+       for (child_no = 0; child_no < dr->device_count; child_no++) {
+               found = false;
+               new_desc = &dr->func[child_no];
+
+               spin_lock_irqsave(&hbus->device_list_lock, flags);
+               list_for_each(iter, &hbus->children) {
+                       hpdev = container_of(iter, struct hv_pci_dev,
+                                            list_entry);
+                       if ((hpdev->desc.win_slot.slot ==
+                            new_desc->win_slot.slot) &&
+                           (hpdev->desc.v_id == new_desc->v_id) &&
+                           (hpdev->desc.d_id == new_desc->d_id) &&
+                           (hpdev->desc.ser == new_desc->ser)) {
+                               hpdev->reported_missing = false;
+                               found = true;
+                       }
+               }
+               spin_unlock_irqrestore(&hbus->device_list_lock, flags);
+
+               if (!found) {
+                       hpdev = new_pcichild_device(hbus, new_desc);
+                       if (!hpdev)
+                               dev_err(&hbus->hdev->device,
+                                       "couldn't record a child device.\n");
+               }
+       }
+
+       /* Move missing children to a list on the stack. */
+       spin_lock_irqsave(&hbus->device_list_lock, flags);
+       do {
+               found = false;
+               list_for_each(iter, &hbus->children) {
+                       hpdev = container_of(iter, struct hv_pci_dev,
+                                            list_entry);
+                       if (hpdev->reported_missing) {
+                               found = true;
+                               put_pcichild(hpdev, hv_pcidev_ref_childlist);
+                               list_del(&hpdev->list_entry);
+                               list_add_tail(&hpdev->list_entry, &removed);
+                               break;
+                       }
+               }
+       } while (found);
+       spin_unlock_irqrestore(&hbus->device_list_lock, flags);
+
+       /* Delete everything that should no longer exist. */
+       while (!list_empty(&removed)) {
+               hpdev = list_first_entry(&removed, struct hv_pci_dev,
+                                        list_entry);
+               list_del(&hpdev->list_entry);
+               put_pcichild(hpdev, hv_pcidev_ref_initial);
+       }
+
+       /* Tell the core to rescan bus because there may have been changes. */
+       if (hbus->state == hv_pcibus_installed) {
+               pci_lock_rescan_remove();
+               pci_scan_child_bus(hbus->pci_bus);
+               pci_unlock_rescan_remove();
+       } else {
+               survey_child_resources(hbus);
+       }
+
+       up(&hbus->enum_sem);
+       put_hvpcibus(hbus);
+       kfree(dr);
+}
+
+/**
+ * hv_pci_devices_present() - Handles list of new children
+ * @hbus:      Root PCI bus, as understood by this driver
+ * @relations: Packet from host listing children
+ *
+ * This function is invoked whenever a new list of devices for
+ * this bus appears.
+ */
+static void hv_pci_devices_present(struct hv_pcibus_device *hbus,
+                                  struct pci_bus_relations *relations)
+{
+       struct hv_dr_state *dr;
+       struct hv_dr_work *dr_wrk;
+       unsigned long flags;
+
+       dr_wrk = kzalloc(sizeof(*dr_wrk), GFP_NOWAIT);
+       if (!dr_wrk)
+               return;
+
+       dr = kzalloc(offsetof(struct hv_dr_state, func) +
+                    (sizeof(struct pci_function_description) *
+                     (relations->device_count)), GFP_NOWAIT);
+       if (!dr)  {
+               kfree(dr_wrk);
+               return;
+       }
+
+       INIT_WORK(&dr_wrk->wrk, pci_devices_present_work);
+       dr_wrk->bus = hbus;
+       dr->device_count = relations->device_count;
+       if (dr->device_count != 0) {
+               memcpy(dr->func, relations->func,
+                      sizeof(struct pci_function_description) *
+                      dr->device_count);
+       }
+
+       spin_lock_irqsave(&hbus->device_list_lock, flags);
+       list_add_tail(&dr->list_entry, &hbus->dr_list);
+       spin_unlock_irqrestore(&hbus->device_list_lock, flags);
+
+       get_hvpcibus(hbus);
+       schedule_work(&dr_wrk->wrk);
+}
+
+/**
+ * hv_eject_device_work() - Asynchronously handles ejection
+ * @work:      Work struct embedded in internal device struct
+ *
+ * This function handles ejecting a device.  Windows will
+ * attempt to gracefully eject a device, waiting 60 seconds to
+ * hear back from the guest OS that this completed successfully.
+ * If this timer expires, the device will be forcibly removed.
+ */
+static void hv_eject_device_work(struct work_struct *work)
+{
+       struct pci_eject_response *ejct_pkt;
+       struct hv_pci_dev *hpdev;
+       struct pci_dev *pdev;
+       unsigned long flags;
+       int wslot;
+       struct {
+               struct pci_packet pkt;
+               u8 buffer[sizeof(struct pci_eject_response) -
+                         sizeof(struct pci_message)];
+       } ctxt;
+
+       hpdev = container_of(work, struct hv_pci_dev, wrk);
+
+       if (hpdev->state != hv_pcichild_ejecting) {
+               put_pcichild(hpdev, hv_pcidev_ref_pnp);
+               return;
+       }
+
+       /*
+        * Ejection can come before or after the PCI bus has been set up, so
+        * attempt to find it and tear down the bus state, if it exists.  This
+        * must be done without constructs like pci_domain_nr(hbus->pci_bus)
+        * because hbus->pci_bus may not exist yet.
+        */
+       wslot = wslot_to_devfn(hpdev->desc.win_slot.slot);
+       pdev = pci_get_domain_bus_and_slot(hpdev->hbus->sysdata.domain, 0,
+                                          wslot);
+       if (pdev) {
+               pci_stop_and_remove_bus_device(pdev);
+               pci_dev_put(pdev);
+       }
+
+       memset(&ctxt, 0, sizeof(ctxt));
+       ejct_pkt = (struct pci_eject_response *)&ctxt.pkt.message;
+       ejct_pkt->message_type = PCI_EJECTION_COMPLETE;
+       ejct_pkt->wslot.slot = hpdev->desc.win_slot.slot;
+       vmbus_sendpacket(hpdev->hbus->hdev->channel, ejct_pkt,
+                        sizeof(*ejct_pkt), (unsigned long)&ctxt.pkt,
+                        VM_PKT_DATA_INBAND, 0);
+
+       spin_lock_irqsave(&hpdev->hbus->device_list_lock, flags);
+       list_del(&hpdev->list_entry);
+       spin_unlock_irqrestore(&hpdev->hbus->device_list_lock, flags);
+
+       put_pcichild(hpdev, hv_pcidev_ref_childlist);
+       put_pcichild(hpdev, hv_pcidev_ref_pnp);
+       put_hvpcibus(hpdev->hbus);
+}
+
+/**
+ * hv_pci_eject_device() - Handles device ejection
+ * @hpdev:     Internal device tracking struct
+ *
+ * This function is invoked when an ejection packet arrives.  It
+ * just schedules work so that we don't re-enter the packet
+ * delivery code handling the ejection.
+ */
+static void hv_pci_eject_device(struct hv_pci_dev *hpdev)
+{
+       hpdev->state = hv_pcichild_ejecting;
+       get_pcichild(hpdev, hv_pcidev_ref_pnp);
+       INIT_WORK(&hpdev->wrk, hv_eject_device_work);
+       get_hvpcibus(hpdev->hbus);
+       schedule_work(&hpdev->wrk);
+}
+
+/**
+ * hv_pci_onchannelcallback() - Handles incoming packets
+ * @context:   Internal bus tracking struct
+ *
+ * This function is invoked whenever the host sends a packet to
+ * this channel (which is private to this root PCI bus).
+ */
+static void hv_pci_onchannelcallback(void *context)
+{
+       const int packet_size = 0x100;
+       int ret;
+       struct hv_pcibus_device *hbus = context;
+       u32 bytes_recvd;
+       u64 req_id;
+       struct vmpacket_descriptor *desc;
+       unsigned char *buffer;
+       int bufferlen = packet_size;
+       struct pci_packet *comp_packet;
+       struct pci_response *response;
+       struct pci_incoming_message *new_message;
+       struct pci_bus_relations *bus_rel;
+       struct pci_dev_incoming *dev_message;
+       struct hv_pci_dev *hpdev;
+
+       buffer = kmalloc(bufferlen, GFP_ATOMIC);
+       if (!buffer)
+               return;
+
+       while (1) {
+               ret = vmbus_recvpacket_raw(hbus->hdev->channel, buffer,
+                                          bufferlen, &bytes_recvd, &req_id);
+
+               if (ret == -ENOBUFS) {
+                       kfree(buffer);
+                       /* Handle large packet */
+                       bufferlen = bytes_recvd;
+                       buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
+                       if (!buffer)
+                               return;
+                       continue;
+               }
+
+               /*
+                * All incoming packets must be at least as large as a
+                * response.
+                */
+               if (bytes_recvd <= sizeof(struct pci_response)) {
+                       kfree(buffer);
+                       return;
+               }
+               desc = (struct vmpacket_descriptor *)buffer;
+
+               switch (desc->type) {
+               case VM_PKT_COMP:
+
+                       /*
+                        * The host is trusted, and thus it's safe to interpret
+                        * this transaction ID as a pointer.
+                        */
+                       comp_packet = (struct pci_packet *)req_id;
+                       response = (struct pci_response *)buffer;
+                       comp_packet->completion_func(comp_packet->compl_ctxt,
+                                                    response,
+                                                    bytes_recvd);
+                       kfree(buffer);
+                       return;
+
+               case VM_PKT_DATA_INBAND:
+
+                       new_message = (struct pci_incoming_message *)buffer;
+                       switch (new_message->message_type.message_type) {
+                       case PCI_BUS_RELATIONS:
+
+                               bus_rel = (struct pci_bus_relations *)buffer;
+                               if (bytes_recvd <
+                                   offsetof(struct pci_bus_relations, func) +
+                                   (sizeof(struct pci_function_description) *
+                                    (bus_rel->device_count))) {
+                                       dev_err(&hbus->hdev->device,
+                                               "bus relations too small\n");
+                                       break;
+                               }
+
+                               hv_pci_devices_present(hbus, bus_rel);
+                               break;
+
+                       case PCI_EJECT:
+
+                               dev_message = (struct pci_dev_incoming *)buffer;
+                               hpdev = get_pcichild_wslot(hbus,
+                                                     dev_message->wslot.slot);
+                               if (hpdev) {
+                                       hv_pci_eject_device(hpdev);
+                                       put_pcichild(hpdev,
+                                                       hv_pcidev_ref_by_slot);
+                               }
+                               break;
+
+                       default:
+                               dev_warn(&hbus->hdev->device,
+                                       "Unimplemented protocol message %x\n",
+                                       new_message->message_type.message_type);
+                               break;
+                       }
+                       break;
+
+               default:
+                       dev_err(&hbus->hdev->device,
+                               "unhandled packet type %d, tid %llx len %d\n",
+                               desc->type, req_id, bytes_recvd);
+                       break;
+               }
+               break;
+       }
+}
+
+/**
+ * hv_pci_protocol_negotiation() - Set up protocol
+ * @hdev:      VMBus's tracking struct for this root PCI bus
+ *
+ * This driver is intended to support running on Windows 10
+ * (server) and later versions. It will not run on earlier
+ * versions, as they assume that many of the operations which
+ * Linux needs accomplished with a spinlock held were done via
+ * asynchronous messaging via VMBus.  Windows 10 increases the
+ * surface area of PCI emulation so that these actions can take
+ * place by suspending a virtual processor for their duration.
+ *
+ * This function negotiates the channel protocol version,
+ * failing if the host doesn't support the necessary protocol
+ * level.
+ */
+static int hv_pci_protocol_negotiation(struct hv_device *hdev)
+{
+       struct pci_version_request *version_req;
+       struct hv_pci_compl comp_pkt;
+       struct pci_packet *pkt;
+       int ret;
+
+       /*
+        * Initiate the handshake with the host and negotiate
+        * a version that the host can support. We start with the
+        * highest version number and go down if the host cannot
+        * support it.
+        */
+       pkt = kzalloc(sizeof(*pkt) + sizeof(*version_req), GFP_KERNEL);
+       if (!pkt)
+               return -ENOMEM;
+
+       init_completion(&comp_pkt.host_event);
+       pkt->completion_func = hv_pci_generic_compl;
+       pkt->compl_ctxt = &comp_pkt;
+       version_req = (struct pci_version_request *)&pkt->message;
+       version_req->message_type.message_type = PCI_QUERY_PROTOCOL_VERSION;
+       version_req->protocol_version = PCI_PROTOCOL_VERSION_CURRENT;
+
+       ret = vmbus_sendpacket(hdev->channel, version_req,
+                              sizeof(struct pci_version_request),
+                              (unsigned long)pkt, VM_PKT_DATA_INBAND,
+                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (ret)
+               goto exit;
+
+       wait_for_completion(&comp_pkt.host_event);
+
+       if (comp_pkt.completion_status < 0) {
+               dev_err(&hdev->device,
+                       "PCI Pass-through VSP failed version request %x\n",
+                       comp_pkt.completion_status);
+               ret = -EPROTO;
+               goto exit;
+       }
+
+       ret = 0;
+
+exit:
+       kfree(pkt);
+       return ret;
+}
+
+/**
+ * hv_pci_free_bridge_windows() - Release memory regions for the
+ * bus
+ * @hbus:      Root PCI bus, as understood by this driver
+ */
+static void hv_pci_free_bridge_windows(struct hv_pcibus_device *hbus)
+{
+       /*
+        * Set the resources back to the way they looked when they
+        * were allocated by setting IORESOURCE_BUSY again.
+        */
+
+       if (hbus->low_mmio_space && hbus->low_mmio_res) {
+               hbus->low_mmio_res->flags |= IORESOURCE_BUSY;
+               release_mem_region(hbus->low_mmio_res->start,
+                                  resource_size(hbus->low_mmio_res));
+       }
+
+       if (hbus->high_mmio_space && hbus->high_mmio_res) {
+               hbus->high_mmio_res->flags |= IORESOURCE_BUSY;
+               release_mem_region(hbus->high_mmio_res->start,
+                                  resource_size(hbus->high_mmio_res));
+       }
+}
+
+/**
+ * hv_pci_allocate_bridge_windows() - Allocate memory regions
+ * for the bus
+ * @hbus:      Root PCI bus, as understood by this driver
+ *
+ * This function calls vmbus_allocate_mmio(), which is itself a
+ * bit of a compromise.  Ideally, we might change the pnp layer
+ * in the kernel such that it comprehends either PCI devices
+ * which are "grandchildren of ACPI," with some intermediate bus
+ * node (in this case, VMBus) or change it such that it
+ * understands VMBus.  The pnp layer, however, has been declared
+ * deprecated, and not subject to change.
+ *
+ * The workaround, implemented here, is to ask VMBus to allocate
+ * MMIO space for this bus.  VMBus itself knows which ranges are
+ * appropriate by looking at its own ACPI objects.  Then, after
+ * these ranges are claimed, they're modified to look like they
+ * would have looked if the ACPI and pnp code had allocated
+ * bridge windows.  These descriptors have to exist in this form
+ * in order to satisfy the code which will get invoked when the
+ * endpoint PCI function driver calls request_mem_region() or
+ * request_mem_region_exclusive().
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int hv_pci_allocate_bridge_windows(struct hv_pcibus_device *hbus)
+{
+       resource_size_t align;
+       int ret;
+
+       if (hbus->low_mmio_space) {
+               align = 1ULL << (63 - __builtin_clzll(hbus->low_mmio_space));
+               ret = vmbus_allocate_mmio(&hbus->low_mmio_res, hbus->hdev, 0,
+                                         (u64)(u32)0xffffffff,
+                                         hbus->low_mmio_space,
+                                         align, false);
+               if (ret) {
+                       dev_err(&hbus->hdev->device,
+                               "Need %#llx of low MMIO space. Consider reconfiguring the VM.\n",
+                               hbus->low_mmio_space);
+                       return ret;
+               }
+
+               /* Modify this resource to become a bridge window. */
+               hbus->low_mmio_res->flags |= IORESOURCE_WINDOW;
+               hbus->low_mmio_res->flags &= ~IORESOURCE_BUSY;
+               pci_add_resource(&hbus->resources_for_children,
+                                hbus->low_mmio_res);
+       }
+
+       if (hbus->high_mmio_space) {
+               align = 1ULL << (63 - __builtin_clzll(hbus->high_mmio_space));
+               ret = vmbus_allocate_mmio(&hbus->high_mmio_res, hbus->hdev,
+                                         0x100000000, -1,
+                                         hbus->high_mmio_space, align,
+                                         false);
+               if (ret) {
+                       dev_err(&hbus->hdev->device,
+                               "Need %#llx of high MMIO space. Consider reconfiguring the VM.\n",
+                               hbus->high_mmio_space);
+                       goto release_low_mmio;
+               }
+
+               /* Modify this resource to become a bridge window. */
+               hbus->high_mmio_res->flags |= IORESOURCE_WINDOW;
+               hbus->high_mmio_res->flags &= ~IORESOURCE_BUSY;
+               pci_add_resource(&hbus->resources_for_children,
+                                hbus->high_mmio_res);
+       }
+
+       return 0;
+
+release_low_mmio:
+       if (hbus->low_mmio_res) {
+               release_mem_region(hbus->low_mmio_res->start,
+                                  resource_size(hbus->low_mmio_res));
+       }
+
+       return ret;
+}
+
+/**
+ * hv_allocate_config_window() - Find MMIO space for PCI Config
+ * @hbus:      Root PCI bus, as understood by this driver
+ *
+ * This function claims memory-mapped I/O space for accessing
+ * configuration space for the functions on this bus.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int hv_allocate_config_window(struct hv_pcibus_device *hbus)
+{
+       int ret;
+
+       /*
+        * Set up a region of MMIO space to use for accessing configuration
+        * space.
+        */
+       ret = vmbus_allocate_mmio(&hbus->mem_config, hbus->hdev, 0, -1,
+                                 PCI_CONFIG_MMIO_LENGTH, 0x1000, false);
+       if (ret)
+               return ret;
+
+       /*
+        * vmbus_allocate_mmio() gets used for allocating both device endpoint
+        * resource claims (those which cannot be overlapped) and the ranges
+        * which are valid for the children of this bus, which are intended
+        * to be overlapped by those children.  Set the flag on this claim
+        * meaning that this region can't be overlapped.
+        */
+
+       hbus->mem_config->flags |= IORESOURCE_BUSY;
+
+       return 0;
+}
+
+static void hv_free_config_window(struct hv_pcibus_device *hbus)
+{
+       release_mem_region(hbus->mem_config->start, PCI_CONFIG_MMIO_LENGTH);
+}
+
+/**
+ * hv_pci_enter_d0() - Bring the "bus" into the D0 power state
+ * @hdev:      VMBus's tracking struct for this root PCI bus
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int hv_pci_enter_d0(struct hv_device *hdev)
+{
+       struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
+       struct pci_bus_d0_entry *d0_entry;
+       struct hv_pci_compl comp_pkt;
+       struct pci_packet *pkt;
+       int ret;
+
+       /*
+        * Tell the host that the bus is ready to use, and moved into the
+        * powered-on state.  This includes telling the host which region
+        * of memory-mapped I/O space has been chosen for configuration space
+        * access.
+        */
+       pkt = kzalloc(sizeof(*pkt) + sizeof(*d0_entry), GFP_KERNEL);
+       if (!pkt)
+               return -ENOMEM;
+
+       init_completion(&comp_pkt.host_event);
+       pkt->completion_func = hv_pci_generic_compl;
+       pkt->compl_ctxt = &comp_pkt;
+       d0_entry = (struct pci_bus_d0_entry *)&pkt->message;
+       d0_entry->message_type.message_type = PCI_BUS_D0ENTRY;
+       d0_entry->mmio_base = hbus->mem_config->start;
+
+       ret = vmbus_sendpacket(hdev->channel, d0_entry, sizeof(*d0_entry),
+                              (unsigned long)pkt, VM_PKT_DATA_INBAND,
+                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (ret)
+               goto exit;
+
+       wait_for_completion(&comp_pkt.host_event);
+
+       if (comp_pkt.completion_status < 0) {
+               dev_err(&hdev->device,
+                       "PCI Pass-through VSP failed D0 Entry with status %x\n",
+                       comp_pkt.completion_status);
+               ret = -EPROTO;
+               goto exit;
+       }
+
+       ret = 0;
+
+exit:
+       kfree(pkt);
+       return ret;
+}
+
+/**
+ * hv_pci_query_relations() - Ask host to send list of child
+ * devices
+ * @hdev:      VMBus's tracking struct for this root PCI bus
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int hv_pci_query_relations(struct hv_device *hdev)
+{
+       struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
+       struct pci_message message;
+       struct completion comp;
+       int ret;
+
+       /* Ask the host to send along the list of child devices */
+       init_completion(&comp);
+       if (cmpxchg(&hbus->survey_event, NULL, &comp))
+               return -ENOTEMPTY;
+
+       memset(&message, 0, sizeof(message));
+       message.message_type = PCI_QUERY_BUS_RELATIONS;
+
+       ret = vmbus_sendpacket(hdev->channel, &message, sizeof(message),
+                              0, VM_PKT_DATA_INBAND, 0);
+       if (ret)
+               return ret;
+
+       wait_for_completion(&comp);
+       return 0;
+}
+
+/**
+ * hv_send_resources_allocated() - Report local resource choices
+ * @hdev:      VMBus's tracking struct for this root PCI bus
+ *
+ * The host OS is expecting to be sent a request as a message
+ * which contains all the resources that the device will use.
+ * The response contains those same resources, "translated"
+ * which is to say, the values which should be used by the
+ * hardware, when it delivers an interrupt.  (MMIO resources are
+ * used in local terms.)  This is nice for Windows, and lines up
+ * with the FDO/PDO split, which doesn't exist in Linux.  Linux
+ * is deeply expecting to scan an emulated PCI configuration
+ * space.  So this message is sent here only to drive the state
+ * machine on the host forward.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int hv_send_resources_allocated(struct hv_device *hdev)
+{
+       struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
+       struct pci_resources_assigned *res_assigned;
+       struct hv_pci_compl comp_pkt;
+       struct hv_pci_dev *hpdev;
+       struct pci_packet *pkt;
+       u32 wslot;
+       int ret;
+
+       pkt = kmalloc(sizeof(*pkt) + sizeof(*res_assigned), GFP_KERNEL);
+       if (!pkt)
+               return -ENOMEM;
+
+       ret = 0;
+
+       for (wslot = 0; wslot < 256; wslot++) {
+               hpdev = get_pcichild_wslot(hbus, wslot);
+               if (!hpdev)
+                       continue;
+
+               memset(pkt, 0, sizeof(*pkt) + sizeof(*res_assigned));
+               init_completion(&comp_pkt.host_event);
+               pkt->completion_func = hv_pci_generic_compl;
+               pkt->compl_ctxt = &comp_pkt;
+               pkt->message.message_type = PCI_RESOURCES_ASSIGNED;
+               res_assigned = (struct pci_resources_assigned *)&pkt->message;
+               res_assigned->wslot.slot = hpdev->desc.win_slot.slot;
+
+               put_pcichild(hpdev, hv_pcidev_ref_by_slot);
+
+               ret = vmbus_sendpacket(
+                       hdev->channel, &pkt->message,
+                       sizeof(*res_assigned),
+                       (unsigned long)pkt,
+                       VM_PKT_DATA_INBAND,
+                       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+               if (ret)
+                       break;
+
+               wait_for_completion(&comp_pkt.host_event);
+
+               if (comp_pkt.completion_status < 0) {
+                       ret = -EPROTO;
+                       dev_err(&hdev->device,
+                               "resource allocated returned 0x%x",
+                               comp_pkt.completion_status);
+                       break;
+               }
+       }
+
+       kfree(pkt);
+       return ret;
+}
+
+/**
+ * hv_send_resources_released() - Report local resources
+ * released
+ * @hdev:      VMBus's tracking struct for this root PCI bus
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int hv_send_resources_released(struct hv_device *hdev)
+{
+       struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
+       struct pci_child_message pkt;
+       struct hv_pci_dev *hpdev;
+       u32 wslot;
+       int ret;
+
+       for (wslot = 0; wslot < 256; wslot++) {
+               hpdev = get_pcichild_wslot(hbus, wslot);
+               if (!hpdev)
+                       continue;
+
+               memset(&pkt, 0, sizeof(pkt));
+               pkt.message_type = PCI_RESOURCES_RELEASED;
+               pkt.wslot.slot = hpdev->desc.win_slot.slot;
+
+               put_pcichild(hpdev, hv_pcidev_ref_by_slot);
+
+               ret = vmbus_sendpacket(hdev->channel, &pkt, sizeof(pkt), 0,
+                                      VM_PKT_DATA_INBAND, 0);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void get_hvpcibus(struct hv_pcibus_device *hbus)
+{
+       atomic_inc(&hbus->remove_lock);
+}
+
+static void put_hvpcibus(struct hv_pcibus_device *hbus)
+{
+       if (atomic_dec_and_test(&hbus->remove_lock))
+               complete(&hbus->remove_event);
+}
+
+/**
+ * hv_pci_probe() - New VMBus channel probe, for a root PCI bus
+ * @hdev:      VMBus's tracking struct for this root PCI bus
+ * @dev_id:    Identifies the device itself
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int hv_pci_probe(struct hv_device *hdev,
+                       const struct hv_vmbus_device_id *dev_id)
+{
+       struct hv_pcibus_device *hbus;
+       int ret;
+
+       hbus = kzalloc(sizeof(*hbus), GFP_KERNEL);
+       if (!hbus)
+               return -ENOMEM;
+
+       /*
+        * The PCI bus "domain" is what is called "segment" in ACPI and
+        * other specs.  Pull it from the instance ID, to get something
+        * unique.  Bytes 8 and 9 are what is used in Windows guests, so
+        * do the same thing for consistency.  Note that, since this code
+        * only runs in a Hyper-V VM, Hyper-V can (and does) guarantee
+        * that (1) the only domain in use for something that looks like
+        * a physical PCI bus (which is actually emulated by the
+        * hypervisor) is domain 0 and (2) there will be no overlap
+        * between domains derived from these instance IDs in the same
+        * VM.
+        */
+       hbus->sysdata.domain = hdev->dev_instance.b[9] |
+                              hdev->dev_instance.b[8] << 8;
+
+       hbus->hdev = hdev;
+       atomic_inc(&hbus->remove_lock);
+       INIT_LIST_HEAD(&hbus->children);
+       INIT_LIST_HEAD(&hbus->dr_list);
+       INIT_LIST_HEAD(&hbus->resources_for_children);
+       spin_lock_init(&hbus->config_lock);
+       spin_lock_init(&hbus->device_list_lock);
+       sema_init(&hbus->enum_sem, 1);
+       init_completion(&hbus->remove_event);
+
+       ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0,
+                        hv_pci_onchannelcallback, hbus);
+       if (ret)
+               goto free_bus;
+
+       hv_set_drvdata(hdev, hbus);
+
+       ret = hv_pci_protocol_negotiation(hdev);
+       if (ret)
+               goto close;
+
+       ret = hv_allocate_config_window(hbus);
+       if (ret)
+               goto close;
+
+       hbus->cfg_addr = ioremap(hbus->mem_config->start,
+                                PCI_CONFIG_MMIO_LENGTH);
+       if (!hbus->cfg_addr) {
+               dev_err(&hdev->device,
+                       "Unable to map a virtual address for config space\n");
+               ret = -ENOMEM;
+               goto free_config;
+       }
+
+       hbus->sysdata.fwnode = irq_domain_alloc_fwnode(hbus);
+       if (!hbus->sysdata.fwnode) {
+               ret = -ENOMEM;
+               goto unmap;
+       }
+
+       ret = hv_pcie_init_irq_domain(hbus);
+       if (ret)
+               goto free_fwnode;
+
+       ret = hv_pci_query_relations(hdev);
+       if (ret)
+               goto free_irq_domain;
+
+       ret = hv_pci_enter_d0(hdev);
+       if (ret)
+               goto free_irq_domain;
+
+       ret = hv_pci_allocate_bridge_windows(hbus);
+       if (ret)
+               goto free_irq_domain;
+
+       ret = hv_send_resources_allocated(hdev);
+       if (ret)
+               goto free_windows;
+
+       prepopulate_bars(hbus);
+
+       hbus->state = hv_pcibus_probed;
+
+       ret = create_root_hv_pci_bus(hbus);
+       if (ret)
+               goto free_windows;
+
+       return 0;
+
+free_windows:
+       hv_pci_free_bridge_windows(hbus);
+free_irq_domain:
+       irq_domain_remove(hbus->irq_domain);
+free_fwnode:
+       irq_domain_free_fwnode(hbus->sysdata.fwnode);
+unmap:
+       iounmap(hbus->cfg_addr);
+free_config:
+       hv_free_config_window(hbus);
+close:
+       vmbus_close(hdev->channel);
+free_bus:
+       kfree(hbus);
+       return ret;
+}
+
+/**
+ * hv_pci_remove() - Remove routine for this VMBus channel
+ * @hdev:      VMBus's tracking struct for this root PCI bus
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int hv_pci_remove(struct hv_device *hdev)
+{
+       int ret;
+       struct hv_pcibus_device *hbus;
+       union {
+               struct pci_packet teardown_packet;
+               u8 buffer[0x100];
+       } pkt;
+       struct pci_bus_relations relations;
+       struct hv_pci_compl comp_pkt;
+
+       hbus = hv_get_drvdata(hdev);
+
+       ret = hv_send_resources_released(hdev);
+       if (ret)
+               dev_err(&hdev->device,
+                       "Couldn't send resources released packet(s)\n");
+
+       memset(&pkt.teardown_packet, 0, sizeof(pkt.teardown_packet));
+       init_completion(&comp_pkt.host_event);
+       pkt.teardown_packet.completion_func = hv_pci_generic_compl;
+       pkt.teardown_packet.compl_ctxt = &comp_pkt;
+       pkt.teardown_packet.message.message_type = PCI_BUS_D0EXIT;
+
+       ret = vmbus_sendpacket(hdev->channel, &pkt.teardown_packet.message,
+                              sizeof(struct pci_message),
+                              (unsigned long)&pkt.teardown_packet,
+                              VM_PKT_DATA_INBAND,
+                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (!ret)
+               wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ);
+
+       if (hbus->state == hv_pcibus_installed) {
+               /* Remove the bus from PCI's point of view. */
+               pci_lock_rescan_remove();
+               pci_stop_root_bus(hbus->pci_bus);
+               pci_remove_root_bus(hbus->pci_bus);
+               pci_unlock_rescan_remove();
+       }
+
+       vmbus_close(hdev->channel);
+
+       /* Delete any children which might still exist. */
+       memset(&relations, 0, sizeof(relations));
+       hv_pci_devices_present(hbus, &relations);
+
+       iounmap(hbus->cfg_addr);
+       hv_free_config_window(hbus);
+       pci_free_resource_list(&hbus->resources_for_children);
+       hv_pci_free_bridge_windows(hbus);
+       irq_domain_remove(hbus->irq_domain);
+       irq_domain_free_fwnode(hbus->sysdata.fwnode);
+       put_hvpcibus(hbus);
+       wait_for_completion(&hbus->remove_event);
+       kfree(hbus);
+       return 0;
+}
+
+static const struct hv_vmbus_device_id hv_pci_id_table[] = {
+       /* PCI Pass-through Class ID */
+       /* 44C4F61D-4444-4400-9D52-802E27EDE19F */
+       { HV_PCIE_GUID, },
+       { },
+};
+
+MODULE_DEVICE_TABLE(vmbus, hv_pci_id_table);
+
+static struct hv_driver hv_pci_drv = {
+       .name           = "hv_pci",
+       .id_table       = hv_pci_id_table,
+       .probe          = hv_pci_probe,
+       .remove         = hv_pci_remove,
+};
+
+static void __exit exit_hv_pci_drv(void)
+{
+       vmbus_driver_unregister(&hv_pci_drv);
+}
+
+static int __init init_hv_pci_drv(void)
+{
+       return vmbus_driver_register(&hv_pci_drv);
+}
+
+module_init(init_hv_pci_drv);
+module_exit(exit_hv_pci_drv);
+
+MODULE_DESCRIPTION("Hyper-V PCI");
+MODULE_LICENSE("GPL v2");
index fe60096..eb5a275 100644 (file)
@@ -39,6 +39,11 @@ struct imx6_pcie {
        struct pcie_port        pp;
        struct regmap           *iomuxc_gpr;
        void __iomem            *mem_base;
+       u32                     tx_deemph_gen1;
+       u32                     tx_deemph_gen2_3p5db;
+       u32                     tx_deemph_gen2_6db;
+       u32                     tx_swing_full;
+       u32                     tx_swing_low;
 };
 
 /* PCIe Root Complex registers (memory-mapped) */
@@ -202,6 +207,23 @@ static int pcie_phy_write(void __iomem *dbi_base, int addr, int data)
        return 0;
 }
 
+static void imx6_pcie_reset_phy(struct pcie_port *pp)
+{
+       u32 tmp;
+
+       pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp);
+       tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
+               PHY_RX_OVRD_IN_LO_RX_PLL_EN);
+       pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp);
+
+       usleep_range(2000, 3000);
+
+       pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp);
+       tmp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN |
+                 PHY_RX_OVRD_IN_LO_RX_PLL_EN);
+       pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp);
+}
+
 /*  Added for PCI abort handling */
 static int imx6q_pcie_abort_handler(unsigned long addr,
                unsigned int fsr, struct pt_regs *regs)
@@ -317,32 +339,32 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
                        IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
 
        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                       IMX6Q_GPR8_TX_DEEMPH_GEN1, 0 << 0);
+                          IMX6Q_GPR8_TX_DEEMPH_GEN1,
+                          imx6_pcie->tx_deemph_gen1 << 0);
        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                       IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, 0 << 6);
+                          IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB,
+                          imx6_pcie->tx_deemph_gen2_3p5db << 6);
        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                       IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, 20 << 12);
+                          IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB,
+                          imx6_pcie->tx_deemph_gen2_6db << 12);
        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                       IMX6Q_GPR8_TX_SWING_FULL, 127 << 18);
+                          IMX6Q_GPR8_TX_SWING_FULL,
+                          imx6_pcie->tx_swing_full << 18);
        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                       IMX6Q_GPR8_TX_SWING_LOW, 127 << 25);
+                          IMX6Q_GPR8_TX_SWING_LOW,
+                          imx6_pcie->tx_swing_low << 25);
 }
 
 static int imx6_pcie_wait_for_link(struct pcie_port *pp)
 {
-       unsigned int retries;
-
-       for (retries = 0; retries < 200; retries++) {
-               if (dw_pcie_link_up(pp))
-                       return 0;
-               usleep_range(100, 1000);
-       }
+       /* check if the link is up or not */
+       if (!dw_pcie_wait_for_link(pp))
+               return 0;
 
-       dev_err(pp->dev, "phy link never came up\n");
        dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
                readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
                readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
-       return -EINVAL;
+       return -ETIMEDOUT;
 }
 
 static int imx6_pcie_wait_for_speed_change(struct pcie_port *pp)
@@ -390,8 +412,10 @@ static int imx6_pcie_establish_link(struct pcie_port *pp)
                        IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
 
        ret = imx6_pcie_wait_for_link(pp);
-       if (ret)
-               return ret;
+       if (ret) {
+               dev_info(pp->dev, "Link never came up\n");
+               goto err_reset_phy;
+       }
 
        /* Allow Gen2 mode after the link is up. */
        tmp = readl(pp->dbi_base + PCIE_RC_LCR);
@@ -410,19 +434,28 @@ static int imx6_pcie_establish_link(struct pcie_port *pp)
        ret = imx6_pcie_wait_for_speed_change(pp);
        if (ret) {
                dev_err(pp->dev, "Failed to bring link up!\n");
-               return ret;
+               goto err_reset_phy;
        }
 
        /* Make sure link training is finished as well! */
        ret = imx6_pcie_wait_for_link(pp);
        if (ret) {
                dev_err(pp->dev, "Failed to bring link up!\n");
-               return ret;
+               goto err_reset_phy;
        }
 
        tmp = readl(pp->dbi_base + PCIE_RC_LCSR);
        dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf);
+
        return 0;
+
+err_reset_phy:
+       dev_dbg(pp->dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
+               readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
+               readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
+       imx6_pcie_reset_phy(pp);
+
+       return ret;
 }
 
 static void imx6_pcie_host_init(struct pcie_port *pp)
@@ -441,81 +474,10 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
                dw_pcie_msi_init(pp);
 }
 
-static void imx6_pcie_reset_phy(struct pcie_port *pp)
-{
-       u32 tmp;
-
-       pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp);
-       tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
-               PHY_RX_OVRD_IN_LO_RX_PLL_EN);
-       pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp);
-
-       usleep_range(2000, 3000);
-
-       pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp);
-       tmp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN |
-                 PHY_RX_OVRD_IN_LO_RX_PLL_EN);
-       pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp);
-}
-
 static int imx6_pcie_link_up(struct pcie_port *pp)
 {
-       u32 rc, debug_r0, rx_valid;
-       int count = 5;
-
-       /*
-        * Test if the PHY reports that the link is up and also that the LTSSM
-        * training finished. There are three possible states of the link when
-        * this code is called:
-        * 1) The link is DOWN (unlikely)
-        *     The link didn't come up yet for some reason. This usually means
-        *     we have a real problem somewhere. Reset the PHY and exit. This
-        *     state calls for inspection of the DEBUG registers.
-        * 2) The link is UP, but still in LTSSM training
-        *     Wait for the training to finish, which should take a very short
-        *     time. If the training does not finish, we have a problem and we
-        *     need to inspect the DEBUG registers. If the training does finish,
-        *     the link is up and operating correctly.
-        * 3) The link is UP and no longer in LTSSM training
-        *     The link is up and operating correctly.
-        */
-       while (1) {
-               rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
-               if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP))
-                       break;
-               if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING))
-                       return 1;
-               if (!count--)
-                       break;
-               dev_dbg(pp->dev, "Link is up, but still in training\n");
-               /*
-                * Wait a little bit, then re-check if the link finished
-                * the training.
-                */
-               usleep_range(1000, 2000);
-       }
-       /*
-        * From L0, initiate MAC entry to gen2 if EP/RC supports gen2.
-        * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2).
-        * If (MAC/LTSSM.state == Recovery.RcvrLock)
-        * && (PHY/rx_valid==0) then pulse PHY/rx_reset. Transition
-        * to gen2 is stuck
-        */
-       pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid);
-       debug_r0 = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0);
-
-       if (rx_valid & PCIE_PHY_RX_ASIC_OUT_VALID)
-               return 0;
-
-       if ((debug_r0 & 0x3f) != 0x0d)
-               return 0;
-
-       dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n");
-       dev_dbg(pp->dev, "debug_r0=%08x debug_r1=%08x\n", debug_r0, rc);
-
-       imx6_pcie_reset_phy(pp);
-
-       return 0;
+       return readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) &
+                       PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
 }
 
 static struct pcie_host_ops imx6_pcie_host_ops = {
@@ -562,6 +524,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
        struct imx6_pcie *imx6_pcie;
        struct pcie_port *pp;
        struct resource *dbi_base;
+       struct device_node *node = pdev->dev.of_node;
        int ret;
 
        imx6_pcie = devm_kzalloc(&pdev->dev, sizeof(*imx6_pcie), GFP_KERNEL);
@@ -614,6 +577,27 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                return PTR_ERR(imx6_pcie->iomuxc_gpr);
        }
 
+       /* Grab PCIe PHY Tx Settings */
+       if (of_property_read_u32(node, "fsl,tx-deemph-gen1",
+                                &imx6_pcie->tx_deemph_gen1))
+               imx6_pcie->tx_deemph_gen1 = 0;
+
+       if (of_property_read_u32(node, "fsl,tx-deemph-gen2-3p5db",
+                                &imx6_pcie->tx_deemph_gen2_3p5db))
+               imx6_pcie->tx_deemph_gen2_3p5db = 0;
+
+       if (of_property_read_u32(node, "fsl,tx-deemph-gen2-6db",
+                                &imx6_pcie->tx_deemph_gen2_6db))
+               imx6_pcie->tx_deemph_gen2_6db = 20;
+
+       if (of_property_read_u32(node, "fsl,tx-swing-full",
+                                &imx6_pcie->tx_swing_full))
+               imx6_pcie->tx_swing_full = 127;
+
+       if (of_property_read_u32(node, "fsl,tx-swing-low",
+                                &imx6_pcie->tx_swing_low))
+               imx6_pcie->tx_swing_low = 127;
+
        ret = imx6_add_pcie_port(pp, pdev);
        if (ret < 0)
                return ret;
index 0aa81bd..b71f55b 100644 (file)
@@ -97,17 +97,15 @@ static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
                return 0;
        }
 
-       ks_dw_pcie_initiate_link_train(ks_pcie);
        /* check if the link is up or not */
-       for (retries = 0; retries < 200; retries++) {
-               if (dw_pcie_link_up(pp))
-                       return 0;
-               usleep_range(100, 1000);
+       for (retries = 0; retries < 5; retries++) {
                ks_dw_pcie_initiate_link_train(ks_pcie);
+               if (!dw_pcie_wait_for_link(pp))
+                       return 0;
        }
 
        dev_err(pp->dev, "phy link never came up\n");
-       return -EINVAL;
+       return -ETIMEDOUT;
 }
 
 static void ks_pcie_msi_irq_handler(struct irq_desc *desc)
@@ -359,6 +357,9 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
 
        /* initialize SerDes Phy if present */
        phy = devm_phy_get(dev, "pcie-phy");
+       if (PTR_ERR_OR_ZERO(phy) == -EPROBE_DEFER)
+               return PTR_ERR(phy);
+
        if (!IS_ERR_OR_NULL(phy)) {
                ret = phy_init(phy);
                if (ret < 0)
index f39961b..a21e229 100644 (file)
@@ -208,6 +208,7 @@ static const struct of_device_id ls_pcie_of_match[] = {
        { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata },
        { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata },
        { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata },
+       { .compatible = "fsl,ls2085a-pcie", .data = &ls2080_drvdata },
        { },
 };
 MODULE_DEVICE_TABLE(of, ls_pcie_of_match);
index 3032311..68d1f41 100644 (file)
@@ -281,6 +281,11 @@ struct tegra_pcie {
        struct resource prefetch;
        struct resource busn;
 
+       struct {
+               resource_size_t mem;
+               resource_size_t io;
+       } offset;
+
        struct clk *pex_clk;
        struct clk *afi_clk;
        struct clk *pll_e;
@@ -295,7 +300,6 @@ struct tegra_pcie {
        struct tegra_msi msi;
 
        struct list_head ports;
-       unsigned int num_ports;
        u32 xbar_config;
 
        struct regulator_bulk_data *supplies;
@@ -426,31 +430,38 @@ free:
        return ERR_PTR(err);
 }
 
-/*
- * Look up a virtual address mapping for the specified bus number. If no such
- * mapping exists, try to create one.
- */
-static void __iomem *tegra_pcie_bus_map(struct tegra_pcie *pcie,
-                                       unsigned int busnr)
+static int tegra_pcie_add_bus(struct pci_bus *bus)
 {
-       struct tegra_pcie_bus *bus;
+       struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata);
+       struct tegra_pcie_bus *b;
 
-       list_for_each_entry(bus, &pcie->buses, list)
-               if (bus->nr == busnr)
-                       return (void __iomem *)bus->area->addr;
+       b = tegra_pcie_bus_alloc(pcie, bus->number);
+       if (IS_ERR(b))
+               return PTR_ERR(b);
 
-       bus = tegra_pcie_bus_alloc(pcie, busnr);
-       if (IS_ERR(bus))
-               return NULL;
+       list_add_tail(&b->list, &pcie->buses);
 
-       list_add_tail(&bus->list, &pcie->buses);
+       return 0;
+}
 
-       return (void __iomem *)bus->area->addr;
+static void tegra_pcie_remove_bus(struct pci_bus *child)
+{
+       struct tegra_pcie *pcie = sys_to_pcie(child->sysdata);
+       struct tegra_pcie_bus *bus, *tmp;
+
+       list_for_each_entry_safe(bus, tmp, &pcie->buses, list) {
+               if (bus->nr == child->number) {
+                       vunmap(bus->area->addr);
+                       list_del(&bus->list);
+                       kfree(bus);
+                       break;
+               }
+       }
 }
 
-static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus,
-                                            unsigned int devfn,
-                                            int where)
+static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus,
+                                       unsigned int devfn,
+                                       int where)
 {
        struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata);
        void __iomem *addr = NULL;
@@ -466,7 +477,12 @@ static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus,
                        }
                }
        } else {
-               addr = tegra_pcie_bus_map(pcie, bus->number);
+               struct tegra_pcie_bus *b;
+
+               list_for_each_entry(b, &pcie->buses, list)
+                       if (b->nr == bus->number)
+                               addr = (void __iomem *)b->area->addr;
+
                if (!addr) {
                        dev_err(pcie->dev,
                                "failed to map cfg. space for bus %u\n",
@@ -481,7 +497,9 @@ static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus,
 }
 
 static struct pci_ops tegra_pcie_ops = {
-       .map_bus = tegra_pcie_conf_address,
+       .add_bus = tegra_pcie_add_bus,
+       .remove_bus = tegra_pcie_remove_bus,
+       .map_bus = tegra_pcie_map_bus,
        .read = pci_generic_config_read32,
        .write = pci_generic_config_write32,
 };
@@ -598,6 +616,17 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
        struct tegra_pcie *pcie = sys_to_pcie(sys);
        int err;
 
+       sys->mem_offset = pcie->offset.mem;
+       sys->io_offset = pcie->offset.io;
+
+       err = devm_request_resource(pcie->dev, &pcie->all, &pcie->io);
+       if (err < 0)
+               return err;
+
+       err = devm_request_resource(pcie->dev, &ioport_resource, &pcie->pio);
+       if (err < 0)
+               return err;
+
        err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem);
        if (err < 0)
                return err;
@@ -606,6 +635,7 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
        if (err)
                return err;
 
+       pci_add_resource_offset(&sys->resources, &pcie->pio, sys->io_offset);
        pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
        pci_add_resource_offset(&sys->resources, &pcie->prefetch,
                                sys->mem_offset);
@@ -741,7 +771,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
        afi_writel(pcie, 0, AFI_FPCI_BAR5);
 
        /* map all upstream transactions as uncached */
-       afi_writel(pcie, PHYS_OFFSET, AFI_CACHE_BAR0_ST);
+       afi_writel(pcie, 0, AFI_CACHE_BAR0_ST);
        afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ);
        afi_writel(pcie, 0, AFI_CACHE_BAR1_ST);
        afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ);
@@ -1601,6 +1631,9 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 
                switch (res.flags & IORESOURCE_TYPE_BITS) {
                case IORESOURCE_IO:
+                       /* Track the bus -> CPU I/O mapping offset. */
+                       pcie->offset.io = res.start - range.pci_addr;
+
                        memcpy(&pcie->pio, &res, sizeof(res));
                        pcie->pio.name = np->full_name;
 
@@ -1621,6 +1654,14 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
                        break;
 
                case IORESOURCE_MEM:
+                       /*
+                        * Track the bus -> CPU memory mapping offset. This
+                        * assumes that the prefetchable and non-prefetchable
+                        * regions will be the last of type IORESOURCE_MEM in
+                        * the ranges property.
+                        * */
+                       pcie->offset.mem = res.start - range.pci_addr;
+
                        if (res.flags & IORESOURCE_PREFETCH) {
                                memcpy(&pcie->prefetch, &res, sizeof(res));
                                pcie->prefetch.name = "prefetchable";
diff --git a/drivers/pci/host/pci-thunder-ecam.c b/drivers/pci/host/pci-thunder-ecam.c
new file mode 100644 (file)
index 0000000..d71935c
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2015, 2016 Cavium, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/of_pci.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "pci-host-common.h"
+
+/* Mapping is standard ECAM */
+static void __iomem *thunder_ecam_map_bus(struct pci_bus *bus,
+                                         unsigned int devfn,
+                                         int where)
+{
+       struct gen_pci *pci = bus->sysdata;
+       resource_size_t idx = bus->number - pci->cfg.bus_range->start;
+
+       return pci->cfg.win[idx] + ((devfn << 12) | where);
+}
+
+static void set_val(u32 v, int where, int size, u32 *val)
+{
+       int shift = (where & 3) * 8;
+
+       pr_debug("set_val %04x: %08x\n", (unsigned)(where & ~3), v);
+       v >>= shift;
+       if (size == 1)
+               v &= 0xff;
+       else if (size == 2)
+               v &= 0xffff;
+       *val = v;
+}
+
+static int handle_ea_bar(u32 e0, int bar, struct pci_bus *bus,
+                        unsigned int devfn, int where, int size, u32 *val)
+{
+       void __iomem *addr;
+       u32 v;
+
+       /* Entries are 16-byte aligned; bits[2,3] select word in entry */
+       int where_a = where & 0xc;
+
+       if (where_a == 0) {
+               set_val(e0, where, size, val);
+               return PCIBIOS_SUCCESSFUL;
+       }
+       if (where_a == 0x4) {
+               addr = bus->ops->map_bus(bus, devfn, bar); /* BAR 0 */
+               if (!addr) {
+                       *val = ~0;
+                       return PCIBIOS_DEVICE_NOT_FOUND;
+               }
+               v = readl(addr);
+               v &= ~0xf;
+               v |= 2; /* EA entry-1. Base-L */
+               set_val(v, where, size, val);
+               return PCIBIOS_SUCCESSFUL;
+       }
+       if (where_a == 0x8) {
+               u32 barl_orig;
+               u32 barl_rb;
+
+               addr = bus->ops->map_bus(bus, devfn, bar); /* BAR 0 */
+               if (!addr) {
+                       *val = ~0;
+                       return PCIBIOS_DEVICE_NOT_FOUND;
+               }
+               barl_orig = readl(addr + 0);
+               writel(0xffffffff, addr + 0);
+               barl_rb = readl(addr + 0);
+               writel(barl_orig, addr + 0);
+               /* zeros in unsettable bits */
+               v = ~barl_rb & ~3;
+               v |= 0xc; /* EA entry-2. Offset-L */
+               set_val(v, where, size, val);
+               return PCIBIOS_SUCCESSFUL;
+       }
+       if (where_a == 0xc) {
+               addr = bus->ops->map_bus(bus, devfn, bar + 4); /* BAR 1 */
+               if (!addr) {
+                       *val = ~0;
+                       return PCIBIOS_DEVICE_NOT_FOUND;
+               }
+               v = readl(addr); /* EA entry-3. Base-H */
+               set_val(v, where, size, val);
+               return PCIBIOS_SUCCESSFUL;
+       }
+       return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn,
+                                      int where, int size, u32 *val)
+{
+       struct gen_pci *pci = bus->sysdata;
+       int where_a = where & ~3;
+       void __iomem *addr;
+       u32 node_bits;
+       u32 v;
+
+       /* EA Base[63:32] may be missing some bits ... */
+       switch (where_a) {
+       case 0xa8:
+       case 0xbc:
+       case 0xd0:
+       case 0xe4:
+               break;
+       default:
+               return pci_generic_config_read(bus, devfn, where, size, val);
+       }
+
+       addr = bus->ops->map_bus(bus, devfn, where_a);
+       if (!addr) {
+               *val = ~0;
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       }
+
+       v = readl(addr);
+
+       /*
+        * Bit 44 of the 64-bit Base must match the same bit in
+        * the config space access window.  Since we are working with
+        * the high-order 32 bits, shift everything down by 32 bits.
+        */
+       node_bits = (pci->cfg.res.start >> 32) & (1 << 12);
+
+       v |= node_bits;
+       set_val(v, where, size, val);
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int thunder_ecam_config_read(struct pci_bus *bus, unsigned int devfn,
+                                   int where, int size, u32 *val)
+{
+       u32 v;
+       u32 vendor_device;
+       u32 class_rev;
+       void __iomem *addr;
+       int cfg_type;
+       int where_a = where & ~3;
+
+       addr = bus->ops->map_bus(bus, devfn, 0xc);
+       if (!addr) {
+               *val = ~0;
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       }
+
+       v = readl(addr);
+
+       /* Check for non type-00 header */
+       cfg_type = (v >> 16) & 0x7f;
+
+       addr = bus->ops->map_bus(bus, devfn, 8);
+       if (!addr) {
+               *val = ~0;
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       }
+
+       class_rev = readl(addr);
+       if (class_rev == 0xffffffff)
+               goto no_emulation;
+
+       if ((class_rev & 0xff) >= 8) {
+               /* Pass-2 handling */
+               if (cfg_type)
+                       goto no_emulation;
+               return thunder_ecam_p2_config_read(bus, devfn, where,
+                                                  size, val);
+       }
+
+       /*
+        * All BARs have fixed addresses specified by the EA
+        * capability; they must return zero on read.
+        */
+       if (cfg_type == 0 &&
+           ((where >= 0x10 && where < 0x2c) ||
+            (where >= 0x1a4 && where < 0x1bc))) {
+               /* BAR or SR-IOV BAR */
+               *val = 0;
+               return PCIBIOS_SUCCESSFUL;
+       }
+
+       addr = bus->ops->map_bus(bus, devfn, 0);
+       if (!addr) {
+               *val = ~0;
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       }
+
+       vendor_device = readl(addr);
+       if (vendor_device == 0xffffffff)
+               goto no_emulation;
+
+       pr_debug("%04x:%04x - Fix pass#: %08x, where: %03x, devfn: %03x\n",
+                vendor_device & 0xffff, vendor_device >> 16, class_rev,
+                (unsigned) where, devfn);
+
+       /* Check for non type-00 header */
+       if (cfg_type == 0) {
+               bool has_msix;
+               bool is_nic = (vendor_device == 0xa01e177d);
+               bool is_tns = (vendor_device == 0xa01f177d);
+
+               addr = bus->ops->map_bus(bus, devfn, 0x70);
+               if (!addr) {
+                       *val = ~0;
+                       return PCIBIOS_DEVICE_NOT_FOUND;
+               }
+               /* E_CAP */
+               v = readl(addr);
+               has_msix = (v & 0xff00) != 0;
+
+               if (!has_msix && where_a == 0x70) {
+                       v |= 0xbc00; /* next capability is EA at 0xbc */
+                       set_val(v, where, size, val);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               if (where_a == 0xb0) {
+                       addr = bus->ops->map_bus(bus, devfn, where_a);
+                       if (!addr) {
+                               *val = ~0;
+                               return PCIBIOS_DEVICE_NOT_FOUND;
+                       }
+                       v = readl(addr);
+                       if (v & 0xff00)
+                               pr_err("Bad MSIX cap header: %08x\n", v);
+                       v |= 0xbc00; /* next capability is EA at 0xbc */
+                       set_val(v, where, size, val);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               if (where_a == 0xbc) {
+                       if (is_nic)
+                               v = 0x40014; /* EA last in chain, 4 entries */
+                       else if (is_tns)
+                               v = 0x30014; /* EA last in chain, 3 entries */
+                       else if (has_msix)
+                               v = 0x20014; /* EA last in chain, 2 entries */
+                       else
+                               v = 0x10014; /* EA last in chain, 1 entry */
+                       set_val(v, where, size, val);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               if (where_a >= 0xc0 && where_a < 0xd0)
+                       /* EA entry-0. PP=0, BAR0 Size:3 */
+                       return handle_ea_bar(0x80ff0003,
+                                            0x10, bus, devfn, where,
+                                            size, val);
+               if (where_a >= 0xd0 && where_a < 0xe0 && has_msix)
+                        /* EA entry-1. PP=0, BAR4 Size:3 */
+                       return handle_ea_bar(0x80ff0043,
+                                            0x20, bus, devfn, where,
+                                            size, val);
+               if (where_a >= 0xe0 && where_a < 0xf0 && is_tns)
+                       /* EA entry-2. PP=0, BAR2, Size:3 */
+                       return handle_ea_bar(0x80ff0023,
+                                            0x18, bus, devfn, where,
+                                            size, val);
+               if (where_a >= 0xe0 && where_a < 0xf0 && is_nic)
+                       /* EA entry-2. PP=4, VF_BAR0 (9), Size:3 */
+                       return handle_ea_bar(0x80ff0493,
+                                            0x1a4, bus, devfn, where,
+                                            size, val);
+               if (where_a >= 0xf0 && where_a < 0x100 && is_nic)
+                       /* EA entry-3. PP=4, VF_BAR4 (d), Size:3 */
+                       return handle_ea_bar(0x80ff04d3,
+                                            0x1b4, bus, devfn, where,
+                                            size, val);
+       } else if (cfg_type == 1) {
+               bool is_rsl_bridge = devfn == 0x08;
+               bool is_rad_bridge = devfn == 0xa0;
+               bool is_zip_bridge = devfn == 0xa8;
+               bool is_dfa_bridge = devfn == 0xb0;
+               bool is_nic_bridge = devfn == 0x10;
+
+               if (where_a == 0x70) {
+                       addr = bus->ops->map_bus(bus, devfn, where_a);
+                       if (!addr) {
+                               *val = ~0;
+                               return PCIBIOS_DEVICE_NOT_FOUND;
+                       }
+                       v = readl(addr);
+                       if (v & 0xff00)
+                               pr_err("Bad PCIe cap header: %08x\n", v);
+                       v |= 0xbc00; /* next capability is EA at 0xbc */
+                       set_val(v, where, size, val);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               if (where_a == 0xbc) {
+                       if (is_nic_bridge)
+                               v = 0x10014; /* EA last in chain, 1 entry */
+                       else
+                               v = 0x00014; /* EA last in chain, no entries */
+                       set_val(v, where, size, val);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               if (where_a == 0xc0) {
+                       if (is_rsl_bridge || is_nic_bridge)
+                               v = 0x0101; /* subordinate:secondary = 1:1 */
+                       else if (is_rad_bridge)
+                               v = 0x0202; /* subordinate:secondary = 2:2 */
+                       else if (is_zip_bridge)
+                               v = 0x0303; /* subordinate:secondary = 3:3 */
+                       else if (is_dfa_bridge)
+                               v = 0x0404; /* subordinate:secondary = 4:4 */
+                       set_val(v, where, size, val);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               if (where_a == 0xc4 && is_nic_bridge) {
+                       /* Enabled, not-Write, SP=ff, PP=05, BEI=6, ES=4 */
+                       v = 0x80ff0564;
+                       set_val(v, where, size, val);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               if (where_a == 0xc8 && is_nic_bridge) {
+                       v = 0x00000002; /* Base-L 64-bit */
+                       set_val(v, where, size, val);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               if (where_a == 0xcc && is_nic_bridge) {
+                       v = 0xfffffffe; /* MaxOffset-L 64-bit */
+                       set_val(v, where, size, val);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               if (where_a == 0xd0 && is_nic_bridge) {
+                       v = 0x00008430; /* NIC Base-H */
+                       set_val(v, where, size, val);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               if (where_a == 0xd4 && is_nic_bridge) {
+                       v = 0x0000000f; /* MaxOffset-H */
+                       set_val(v, where, size, val);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+       }
+no_emulation:
+       return pci_generic_config_read(bus, devfn, where, size, val);
+}
+
+static int thunder_ecam_config_write(struct pci_bus *bus, unsigned int devfn,
+                                    int where, int size, u32 val)
+{
+       /*
+        * All BARs have fixed addresses; ignore BAR writes so they
+        * don't get corrupted.
+        */
+       if ((where >= 0x10 && where < 0x2c) ||
+           (where >= 0x1a4 && where < 0x1bc))
+               /* BAR or SR-IOV BAR */
+               return PCIBIOS_SUCCESSFUL;
+
+       return pci_generic_config_write(bus, devfn, where, size, val);
+}
+
+static struct gen_pci_cfg_bus_ops thunder_ecam_bus_ops = {
+       .bus_shift      = 20,
+       .ops            = {
+               .map_bus        = thunder_ecam_map_bus,
+               .read           = thunder_ecam_config_read,
+               .write          = thunder_ecam_config_write,
+       }
+};
+
+static const struct of_device_id thunder_ecam_of_match[] = {
+       { .compatible = "cavium,pci-host-thunder-ecam",
+         .data = &thunder_ecam_bus_ops },
+
+       { },
+};
+MODULE_DEVICE_TABLE(of, thunder_ecam_of_match);
+
+static int thunder_ecam_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct of_device_id *of_id;
+       struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+
+       if (!pci)
+               return -ENOMEM;
+
+       of_id = of_match_node(thunder_ecam_of_match, dev->of_node);
+       pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
+
+       return pci_host_common_probe(pdev, pci);
+}
+
+static struct platform_driver thunder_ecam_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = thunder_ecam_of_match,
+       },
+       .probe = thunder_ecam_probe,
+};
+module_platform_driver(thunder_ecam_driver);
+
+MODULE_DESCRIPTION("Thunder ECAM PCI host driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c
new file mode 100644 (file)
index 0000000..cabb92a
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2015 - 2016 Cavium, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/platform_device.h>
+
+#include "pci-host-common.h"
+
+#define PEM_CFG_WR 0x28
+#define PEM_CFG_RD 0x30
+
+struct thunder_pem_pci {
+       struct gen_pci  gen_pci;
+       u32             ea_entry[3];
+       void __iomem    *pem_reg_base;
+};
+
+static void __iomem *thunder_pem_map_bus(struct pci_bus *bus,
+                                        unsigned int devfn, int where)
+{
+       struct gen_pci *pci = bus->sysdata;
+       resource_size_t idx = bus->number - pci->cfg.bus_range->start;
+
+       return pci->cfg.win[idx] + ((devfn << 16) | where);
+}
+
+static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
+                                  int where, int size, u32 *val)
+{
+       u64 read_val;
+       struct thunder_pem_pci *pem_pci;
+       struct gen_pci *pci = bus->sysdata;
+
+       pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
+
+       if (devfn != 0 || where >= 2048) {
+               *val = ~0;
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       }
+
+       /*
+        * 32-bit accesses only.  Write the address to the low order
+        * bits of PEM_CFG_RD, then trigger the read by reading back.
+        * The config data lands in the upper 32-bits of PEM_CFG_RD.
+        */
+       read_val = where & ~3ull;
+       writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD);
+       read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
+       read_val >>= 32;
+
+       /*
+        * The config space contains some garbage, fix it up.  Also
+        * synthesize an EA capability for the BAR used by MSI-X.
+        */
+       switch (where & ~3) {
+       case 0x40:
+               read_val &= 0xffff00ff;
+               read_val |= 0x00007000; /* Skip MSI CAP */
+               break;
+       case 0x70: /* Express Cap */
+               /* PME interrupt on vector 2*/
+               read_val |= (2u << 25);
+               break;
+       case 0xb0: /* MSI-X Cap */
+               /* TableSize=4, Next Cap is EA */
+               read_val &= 0xc00000ff;
+               read_val |= 0x0003bc00;
+               break;
+       case 0xb4:
+               /* Table offset=0, BIR=0 */
+               read_val = 0x00000000;
+               break;
+       case 0xb8:
+               /* BPA offset=0xf0000, BIR=0 */
+               read_val = 0x000f0000;
+               break;
+       case 0xbc:
+               /* EA, 1 entry, no next Cap */
+               read_val = 0x00010014;
+               break;
+       case 0xc0:
+               /* DW2 for type-1 */
+               read_val = 0x00000000;
+               break;
+       case 0xc4:
+               /* Entry BEI=0, PP=0x00, SP=0xff, ES=3 */
+               read_val = 0x80ff0003;
+               break;
+       case 0xc8:
+               read_val = pem_pci->ea_entry[0];
+               break;
+       case 0xcc:
+               read_val = pem_pci->ea_entry[1];
+               break;
+       case 0xd0:
+               read_val = pem_pci->ea_entry[2];
+               break;
+       default:
+               break;
+       }
+       read_val >>= (8 * (where & 3));
+       switch (size) {
+       case 1:
+               read_val &= 0xff;
+               break;
+       case 2:
+               read_val &= 0xffff;
+               break;
+       default:
+               break;
+       }
+       *val = read_val;
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn,
+                                  int where, int size, u32 *val)
+{
+       struct gen_pci *pci = bus->sysdata;
+
+       if (bus->number < pci->cfg.bus_range->start ||
+           bus->number > pci->cfg.bus_range->end)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       /*
+        * The first device on the bus is the PEM PCIe bridge.
+        * Special case its config access.
+        */
+       if (bus->number == pci->cfg.bus_range->start)
+               return thunder_pem_bridge_read(bus, devfn, where, size, val);
+
+       return pci_generic_config_read(bus, devfn, where, size, val);
+}
+
+/*
+ * Some of the w1c_bits below also include read-only or non-writable
+ * reserved bits, this makes the code simpler and is OK as the bits
+ * are not affected by writing zeros to them.
+ */
+static u32 thunder_pem_bridge_w1c_bits(int where)
+{
+       u32 w1c_bits = 0;
+
+       switch (where & ~3) {
+       case 0x04: /* Command/Status */
+       case 0x1c: /* Base and I/O Limit/Secondary Status */
+               w1c_bits = 0xff000000;
+               break;
+       case 0x44: /* Power Management Control and Status */
+               w1c_bits = 0xfffffe00;
+               break;
+       case 0x78: /* Device Control/Device Status */
+       case 0x80: /* Link Control/Link Status */
+       case 0x88: /* Slot Control/Slot Status */
+       case 0x90: /* Root Status */
+       case 0xa0: /* Link Control 2 Registers/Link Status 2 */
+               w1c_bits = 0xffff0000;
+               break;
+       case 0x104: /* Uncorrectable Error Status */
+       case 0x110: /* Correctable Error Status */
+       case 0x130: /* Error Status */
+       case 0x160: /* Link Control 4 */
+               w1c_bits = 0xffffffff;
+               break;
+       default:
+               break;
+       }
+       return w1c_bits;
+}
+
+static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
+                                   int where, int size, u32 val)
+{
+       struct gen_pci *pci = bus->sysdata;
+       struct thunder_pem_pci *pem_pci;
+       u64 write_val, read_val;
+       u32 mask = 0;
+
+       pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
+
+       if (devfn != 0 || where >= 2048)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       /*
+        * 32-bit accesses only.  If the write is for a size smaller
+        * than 32-bits, we must first read the 32-bit value and merge
+        * in the desired bits and then write the whole 32-bits back
+        * out.
+        */
+       switch (size) {
+       case 1:
+               read_val = where & ~3ull;
+               writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD);
+               read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
+               read_val >>= 32;
+               mask = ~(0xff << (8 * (where & 3)));
+               read_val &= mask;
+               val = (val & 0xff) << (8 * (where & 3));
+               val |= (u32)read_val;
+               break;
+       case 2:
+               read_val = where & ~3ull;
+               writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD);
+               read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
+               read_val >>= 32;
+               mask = ~(0xffff << (8 * (where & 3)));
+               read_val &= mask;
+               val = (val & 0xffff) << (8 * (where & 3));
+               val |= (u32)read_val;
+               break;
+       default:
+               break;
+       }
+
+       /*
+        * By expanding the write width to 32 bits, we may
+        * inadvertently hit some W1C bits that were not intended to
+        * be written.  Calculate the mask that must be applied to the
+        * data to be written to avoid these cases.
+        */
+       if (mask) {
+               u32 w1c_bits = thunder_pem_bridge_w1c_bits(where);
+
+               if (w1c_bits) {
+                       mask &= w1c_bits;
+                       val &= ~mask;
+               }
+       }
+
+       /*
+        * Low order bits are the config address, the high order 32
+        * bits are the data to be written.
+        */
+       write_val = where & ~3ull;
+       write_val |= (((u64)val) << 32);
+       writeq(write_val, pem_pci->pem_reg_base + PEM_CFG_WR);
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn,
+                                   int where, int size, u32 val)
+{
+       struct gen_pci *pci = bus->sysdata;
+
+       if (bus->number < pci->cfg.bus_range->start ||
+           bus->number > pci->cfg.bus_range->end)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       /*
+        * The first device on the bus is the PEM PCIe bridge.
+        * Special case its config access.
+        */
+       if (bus->number == pci->cfg.bus_range->start)
+               return thunder_pem_bridge_write(bus, devfn, where, size, val);
+
+
+       return pci_generic_config_write(bus, devfn, where, size, val);
+}
+
+static struct gen_pci_cfg_bus_ops thunder_pem_bus_ops = {
+       .bus_shift      = 24,
+       .ops            = {
+               .map_bus        = thunder_pem_map_bus,
+               .read           = thunder_pem_config_read,
+               .write          = thunder_pem_config_write,
+       }
+};
+
+static const struct of_device_id thunder_pem_of_match[] = {
+       { .compatible = "cavium,pci-host-thunder-pem",
+         .data = &thunder_pem_bus_ops },
+
+       { },
+};
+MODULE_DEVICE_TABLE(of, thunder_pem_of_match);
+
+static int thunder_pem_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct of_device_id *of_id;
+       resource_size_t bar4_start;
+       struct resource *res_pem;
+       struct thunder_pem_pci *pem_pci;
+
+       pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL);
+       if (!pem_pci)
+               return -ENOMEM;
+
+       of_id = of_match_node(thunder_pem_of_match, dev->of_node);
+       pem_pci->gen_pci.cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
+
+       /*
+        * The second register range is the PEM bridge to the PCIe
+        * bus.  It has a different config access method than those
+        * devices behind the bridge.
+        */
+       res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!res_pem) {
+               dev_err(dev, "missing \"reg[1]\"property\n");
+               return -EINVAL;
+       }
+
+       pem_pci->pem_reg_base = devm_ioremap(dev, res_pem->start, 0x10000);
+       if (!pem_pci->pem_reg_base)
+               return -ENOMEM;
+
+       /*
+        * The MSI-X BAR for the PEM and AER interrupts is located at
+        * a fixed offset from the PEM register base.  Generate a
+        * fragment of the synthesized Enhanced Allocation capability
+        * structure here for the BAR.
+        */
+       bar4_start = res_pem->start + 0xf00000;
+       pem_pci->ea_entry[0] = (u32)bar4_start | 2;
+       pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u;
+       pem_pci->ea_entry[2] = (u32)(bar4_start >> 32);
+
+       return pci_host_common_probe(pdev, &pem_pci->gen_pci);
+}
+
+static struct platform_driver thunder_pem_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = thunder_pem_of_match,
+       },
+       .probe = thunder_pem_probe,
+};
+module_platform_driver(thunder_pem_driver);
+
+MODULE_DESCRIPTION("Thunder PEM PCIe host driver");
+MODULE_LICENSE("GPL v2");
index 99da549..dbac6fb 100644 (file)
@@ -40,6 +40,7 @@
 #define P2A_INT_ENABLE                 0x3070
 #define P2A_INT_ENA_ALL                        0xf
 #define RP_LTSSM                       0x3c64
+#define RP_LTSSM_MASK                  0x1f
 #define LTSSM_L0                       0xf
 
 /* TLP configuration type 0 and 1 */
@@ -140,7 +141,7 @@ static void tlp_write_tx(struct altera_pcie *pcie,
 
 static bool altera_pcie_link_is_up(struct altera_pcie *pcie)
 {
-       return !!(cra_readl(pcie, RP_LTSSM) & LTSSM_L0);
+       return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0);
 }
 
 static bool altera_pcie_valid_config(struct altera_pcie *pcie,
diff --git a/drivers/pci/host/pcie-designware-plat.c b/drivers/pci/host/pcie-designware-plat.c
new file mode 100644 (file)
index 0000000..b350099
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * PCIe RC driver for Synopsys DesignWare Core
+ *
+ * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ * Authors: Joao Pinto <jpinto@synopsys.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+struct dw_plat_pcie {
+       void __iomem            *mem_base;
+       struct pcie_port        pp;
+};
+
+static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
+{
+       struct pcie_port *pp = arg;
+
+       return dw_handle_msi_irq(pp);
+}
+
+static void dw_plat_pcie_host_init(struct pcie_port *pp)
+{
+       dw_pcie_setup_rc(pp);
+       dw_pcie_wait_for_link(pp);
+
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               dw_pcie_msi_init(pp);
+}
+
+static struct pcie_host_ops dw_plat_pcie_host_ops = {
+       .host_init = dw_plat_pcie_host_init,
+};
+
+static int dw_plat_add_pcie_port(struct pcie_port *pp,
+                                struct platform_device *pdev)
+{
+       int ret;
+
+       pp->irq = platform_get_irq(pdev, 1);
+       if (pp->irq < 0)
+               return pp->irq;
+
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               pp->msi_irq = platform_get_irq(pdev, 0);
+               if (pp->msi_irq < 0)
+                       return pp->msi_irq;
+
+               ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+                                       dw_plat_pcie_msi_irq_handler,
+                                       IRQF_SHARED, "dw-plat-pcie-msi", pp);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to request MSI IRQ\n");
+                       return ret;
+               }
+       }
+
+       pp->root_bus_nr = -1;
+       pp->ops = &dw_plat_pcie_host_ops;
+
+       ret = dw_pcie_host_init(pp);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to initialize host\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int dw_plat_pcie_probe(struct platform_device *pdev)
+{
+       struct dw_plat_pcie *dw_plat_pcie;
+       struct pcie_port *pp;
+       struct resource *res;  /* Resource from DT */
+       int ret;
+
+       dw_plat_pcie = devm_kzalloc(&pdev->dev, sizeof(*dw_plat_pcie),
+                                       GFP_KERNEL);
+       if (!dw_plat_pcie)
+               return -ENOMEM;
+
+       pp = &dw_plat_pcie->pp;
+       pp->dev = &pdev->dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENODEV;
+
+       dw_plat_pcie->mem_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(dw_plat_pcie->mem_base))
+               return PTR_ERR(dw_plat_pcie->mem_base);
+
+       pp->dbi_base = dw_plat_pcie->mem_base;
+
+       ret = dw_plat_add_pcie_port(pp, pdev);
+       if (ret < 0)
+               return ret;
+
+       platform_set_drvdata(pdev, dw_plat_pcie);
+       return 0;
+}
+
+static const struct of_device_id dw_plat_pcie_of_match[] = {
+       { .compatible = "snps,dw-pcie", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, dw_plat_pcie_of_match);
+
+static struct platform_driver dw_plat_pcie_driver = {
+       .driver = {
+               .name   = "dw-pcie",
+               .of_match_table = dw_plat_pcie_of_match,
+       },
+       .probe = dw_plat_pcie_probe,
+};
+
+module_platform_driver(dw_plat_pcie_driver);
+
+MODULE_AUTHOR("Joao Pinto <Joao.Pinto@synopsys.com>");
+MODULE_DESCRIPTION("Synopsys PCIe host controller glue platform driver");
+MODULE_LICENSE("GPL v2");
index 2171682..a4cccd3 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/pci_regs.h>
 #include <linux/platform_device.h>
 #include <linux/types.h>
+#include <linux/delay.h>
 
 #include "pcie-designware.h"
 
 #define PCIE_ATU_FUNC(x)               (((x) & 0x7) << 16)
 #define PCIE_ATU_UPPER_TARGET          0x91C
 
+/* PCIe Port Logic registers */
+#define PLR_OFFSET                     0x700
+#define PCIE_PHY_DEBUG_R1              (PLR_OFFSET + 0x2c)
+#define PCIE_PHY_DEBUG_R1_LINK_UP      0x00000010
+
 static struct pci_ops dw_pcie_ops;
 
 int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
@@ -380,12 +386,33 @@ static struct msi_controller dw_pcie_msi_chip = {
        .teardown_irq = dw_msi_teardown_irq,
 };
 
+int dw_pcie_wait_for_link(struct pcie_port *pp)
+{
+       int retries;
+
+       /* check if the link is up or not */
+       for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+               if (dw_pcie_link_up(pp)) {
+                       dev_info(pp->dev, "link up\n");
+                       return 0;
+               }
+               usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
+       }
+
+       dev_err(pp->dev, "phy link never came up\n");
+
+       return -ETIMEDOUT;
+}
+
 int dw_pcie_link_up(struct pcie_port *pp)
 {
+       u32 val;
+
        if (pp->ops->link_up)
                return pp->ops->link_up(pp);
 
-       return 0;
+       val = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
+       return val & PCIE_PHY_DEBUG_R1_LINK_UP;
 }
 
 static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
@@ -517,6 +544,11 @@ int dw_pcie_host_init(struct pcie_port *pp)
        if (pp->ops->host_init)
                pp->ops->host_init(pp);
 
+       /*
+        * If the platform provides ->rd_other_conf, it means the platform
+        * uses its own address translation component rather than ATU, so
+        * we should not program the ATU here.
+        */
        if (!pp->ops->rd_other_conf)
                dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
                                          PCIE_ATU_TYPE_MEM, pp->mem_base,
@@ -551,13 +583,11 @@ int dw_pcie_host_init(struct pcie_port *pp)
        pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
 #endif
 
-       if (!pci_has_flag(PCI_PROBE_ONLY)) {
-               pci_bus_size_bridges(bus);
-               pci_bus_assign_resources(bus);
+       pci_bus_size_bridges(bus);
+       pci_bus_assign_resources(bus);
 
-               list_for_each_entry(child, &bus->children, node)
-                       pcie_bus_configure_settings(child);
-       }
+       list_for_each_entry(child, &bus->children, node)
+               pcie_bus_configure_settings(child);
 
        pci_bus_add_devices(bus);
        return 0;
index 2356d29..f437f9b 100644 (file)
 #define MAX_MSI_IRQS                   32
 #define MAX_MSI_CTRLS                  (MAX_MSI_IRQS / 32)
 
+/* Parameters for the waiting for link up routine */
+#define LINK_WAIT_MAX_RETRIES          10
+#define LINK_WAIT_USLEEP_MIN           90000
+#define LINK_WAIT_USLEEP_MAX           100000
+
 struct pcie_port {
        struct device           *dev;
        u8                      root_bus_nr;
@@ -76,6 +81,7 @@ int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val);
 int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val);
 irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
 void dw_pcie_msi_init(struct pcie_port *pp);
+int dw_pcie_wait_for_link(struct pcie_port *pp);
 int dw_pcie_link_up(struct pcie_port *pp);
 void dw_pcie_setup_rc(struct pcie_port *pp);
 int dw_pcie_host_init(struct pcie_port *pp);
index e845fba..f2f90c5 100644 (file)
@@ -116,8 +116,6 @@ static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg)
 
 static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
 {
-       struct device *dev = pcie->dev;
-       unsigned int retries = 0;
        u32 val;
 
        if (dw_pcie_link_up(&pcie->pp))
@@ -128,15 +126,7 @@ static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
        val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE;
        writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL);
 
-       do {
-               if (dw_pcie_link_up(&pcie->pp))
-                       return 0;
-               usleep_range(250, 1000);
-       } while (retries < 200);
-
-       dev_warn(dev, "phy link never came up\n");
-
-       return -ETIMEDOUT;
+       return dw_pcie_wait_for_link(&pcie->pp);
 }
 
 static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
index 4edb518..3509218 100644 (file)
@@ -390,9 +390,7 @@ static int rcar_pcie_enable(struct rcar_pcie *pcie)
 
        rcar_pcie_setup(&res, pcie);
 
-       /* Do not reassign resources if probe only */
-       if (!pci_has_flag(PCI_PROBE_ONLY))
-               pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
+       pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
 
        if (IS_ENABLED(CONFIG_PCI_MSI))
                bus = pci_scan_root_bus_msi(pcie->dev, pcie->root_bus_nr,
@@ -408,13 +406,11 @@ static int rcar_pcie_enable(struct rcar_pcie *pcie)
 
        pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
 
-       if (!pci_has_flag(PCI_PROBE_ONLY)) {
-               pci_bus_size_bridges(bus);
-               pci_bus_assign_resources(bus);
+       pci_bus_size_bridges(bus);
+       pci_bus_assign_resources(bus);
 
-               list_for_each_entry(child, &bus->children, node)
-                       pcie_bus_configure_settings(child);
-       }
+       list_for_each_entry(child, &bus->children, node)
+               pcie_bus_configure_settings(child);
 
        pci_bus_add_devices(bus);
 
index a6cd823..a4060b8 100644 (file)
@@ -13,7 +13,6 @@
  */
 
 #include <linux/clk.h>
-#include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -149,7 +148,6 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
        struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
        struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
        u32 exp_cap_off = EXP_CAP_ID_OFFSET;
-       unsigned int retries;
 
        if (dw_pcie_link_up(pp)) {
                dev_err(pp->dev, "link already up\n");
@@ -200,17 +198,7 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
                        | ((u32)1 << REG_TRANSLATION_ENABLE),
                        &app_reg->app_ctrl_0);
 
-       /* check if the link is up or not */
-       for (retries = 0; retries < 10; retries++) {
-               if (dw_pcie_link_up(pp)) {
-                       dev_info(pp->dev, "link up\n");
-                       return 0;
-               }
-               mdelay(100);
-       }
-
-       dev_err(pp->dev, "link Fail\n");
-       return -EINVAL;
+       return dw_pcie_wait_for_link(pp);
 }
 
 static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg)
diff --git a/drivers/pci/host/pcie-xilinx-nwl.c b/drivers/pci/host/pcie-xilinx-nwl.c
new file mode 100644 (file)
index 0000000..5139e64
--- /dev/null
@@ -0,0 +1,881 @@
+/*
+ * PCIe host controller driver for NWL PCIe Bridge
+ * Based on pcie-xilinx.c, pci-tegra.c
+ *
+ * (C) Copyright 2014 - 2015, Xilinx, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/irqchip/chained_irq.h>
+
+/* Bridge core config registers */
+#define BRCFG_PCIE_RX0                 0x00000000
+#define BRCFG_INTERRUPT                        0x00000010
+#define BRCFG_PCIE_RX_MSG_FILTER       0x00000020
+
+/* Egress - Bridge translation registers */
+#define E_BREG_CAPABILITIES            0x00000200
+#define E_BREG_CONTROL                 0x00000208
+#define E_BREG_BASE_LO                 0x00000210
+#define E_BREG_BASE_HI                 0x00000214
+#define E_ECAM_CAPABILITIES            0x00000220
+#define E_ECAM_CONTROL                 0x00000228
+#define E_ECAM_BASE_LO                 0x00000230
+#define E_ECAM_BASE_HI                 0x00000234
+
+/* Ingress - address translations */
+#define I_MSII_CAPABILITIES            0x00000300
+#define I_MSII_CONTROL                 0x00000308
+#define I_MSII_BASE_LO                 0x00000310
+#define I_MSII_BASE_HI                 0x00000314
+
+#define I_ISUB_CONTROL                 0x000003E8
+#define SET_ISUB_CONTROL               BIT(0)
+/* Rxed msg fifo  - Interrupt status registers */
+#define MSGF_MISC_STATUS               0x00000400
+#define MSGF_MISC_MASK                 0x00000404
+#define MSGF_LEG_STATUS                        0x00000420
+#define MSGF_LEG_MASK                  0x00000424
+#define MSGF_MSI_STATUS_LO             0x00000440
+#define MSGF_MSI_STATUS_HI             0x00000444
+#define MSGF_MSI_MASK_LO               0x00000448
+#define MSGF_MSI_MASK_HI               0x0000044C
+
+/* Msg filter mask bits */
+#define CFG_ENABLE_PM_MSG_FWD          BIT(1)
+#define CFG_ENABLE_INT_MSG_FWD         BIT(2)
+#define CFG_ENABLE_ERR_MSG_FWD         BIT(3)
+#define CFG_ENABLE_SLT_MSG_FWD         BIT(5)
+#define CFG_ENABLE_VEN_MSG_FWD         BIT(7)
+#define CFG_ENABLE_OTH_MSG_FWD         BIT(13)
+#define CFG_ENABLE_VEN_MSG_EN          BIT(14)
+#define CFG_ENABLE_VEN_MSG_VEN_INV     BIT(15)
+#define CFG_ENABLE_VEN_MSG_VEN_ID      GENMASK(31, 16)
+#define CFG_ENABLE_MSG_FILTER_MASK     (CFG_ENABLE_PM_MSG_FWD | \
+                                       CFG_ENABLE_INT_MSG_FWD | \
+                                       CFG_ENABLE_ERR_MSG_FWD | \
+                                       CFG_ENABLE_SLT_MSG_FWD | \
+                                       CFG_ENABLE_VEN_MSG_FWD | \
+                                       CFG_ENABLE_OTH_MSG_FWD | \
+                                       CFG_ENABLE_VEN_MSG_EN | \
+                                       CFG_ENABLE_VEN_MSG_VEN_INV | \
+                                       CFG_ENABLE_VEN_MSG_VEN_ID)
+
+/* Misc interrupt status mask bits */
+#define MSGF_MISC_SR_RXMSG_AVAIL       BIT(0)
+#define MSGF_MISC_SR_RXMSG_OVER                BIT(1)
+#define MSGF_MISC_SR_SLAVE_ERR         BIT(4)
+#define MSGF_MISC_SR_MASTER_ERR                BIT(5)
+#define MSGF_MISC_SR_I_ADDR_ERR                BIT(6)
+#define MSGF_MISC_SR_E_ADDR_ERR                BIT(7)
+#define MSGF_MISC_SR_UR_DETECT          BIT(20)
+
+#define MSGF_MISC_SR_PCIE_CORE         GENMASK(18, 16)
+#define MSGF_MISC_SR_PCIE_CORE_ERR     GENMASK(31, 22)
+
+#define MSGF_MISC_SR_MASKALL           (MSGF_MISC_SR_RXMSG_AVAIL | \
+                                       MSGF_MISC_SR_RXMSG_OVER | \
+                                       MSGF_MISC_SR_SLAVE_ERR | \
+                                       MSGF_MISC_SR_MASTER_ERR | \
+                                       MSGF_MISC_SR_I_ADDR_ERR | \
+                                       MSGF_MISC_SR_E_ADDR_ERR | \
+                                       MSGF_MISC_SR_UR_DETECT | \
+                                       MSGF_MISC_SR_PCIE_CORE | \
+                                       MSGF_MISC_SR_PCIE_CORE_ERR)
+
+/* Legacy interrupt status mask bits */
+#define MSGF_LEG_SR_INTA               BIT(0)
+#define MSGF_LEG_SR_INTB               BIT(1)
+#define MSGF_LEG_SR_INTC               BIT(2)
+#define MSGF_LEG_SR_INTD               BIT(3)
+#define MSGF_LEG_SR_MASKALL            (MSGF_LEG_SR_INTA | MSGF_LEG_SR_INTB | \
+                                       MSGF_LEG_SR_INTC | MSGF_LEG_SR_INTD)
+
+/* MSI interrupt status mask bits */
+#define MSGF_MSI_SR_LO_MASK            BIT(0)
+#define MSGF_MSI_SR_HI_MASK            BIT(0)
+
+#define MSII_PRESENT                   BIT(0)
+#define MSII_ENABLE                    BIT(0)
+#define MSII_STATUS_ENABLE             BIT(15)
+
+/* Bridge config interrupt mask */
+#define BRCFG_INTERRUPT_MASK           BIT(0)
+#define BREG_PRESENT                   BIT(0)
+#define BREG_ENABLE                    BIT(0)
+#define BREG_ENABLE_FORCE              BIT(1)
+
+/* E_ECAM status mask bits */
+#define E_ECAM_PRESENT                 BIT(0)
+#define E_ECAM_CR_ENABLE               BIT(0)
+#define E_ECAM_SIZE_LOC                        GENMASK(20, 16)
+#define E_ECAM_SIZE_SHIFT              16
+#define ECAM_BUS_LOC_SHIFT             20
+#define ECAM_DEV_LOC_SHIFT             12
+#define NWL_ECAM_VALUE_DEFAULT         12
+
+#define CFG_DMA_REG_BAR                        GENMASK(2, 0)
+
+#define INT_PCI_MSI_NR                 (2 * 32)
+#define INTX_NUM                       4
+
+/* Readin the PS_LINKUP */
+#define PS_LINKUP_OFFSET               0x00000238
+#define PCIE_PHY_LINKUP_BIT            BIT(0)
+#define PHY_RDY_LINKUP_BIT             BIT(1)
+
+/* Parameters for the waiting for link up routine */
+#define LINK_WAIT_MAX_RETRIES          10
+#define LINK_WAIT_USLEEP_MIN           90000
+#define LINK_WAIT_USLEEP_MAX           100000
+
+struct nwl_msi {                       /* MSI information */
+       struct irq_domain *msi_domain;
+       unsigned long *bitmap;
+       struct irq_domain *dev_domain;
+       struct mutex lock;              /* protect bitmap variable */
+       int irq_msi0;
+       int irq_msi1;
+};
+
+struct nwl_pcie {
+       struct device *dev;
+       void __iomem *breg_base;
+       void __iomem *pcireg_base;
+       void __iomem *ecam_base;
+       phys_addr_t phys_breg_base;     /* Physical Bridge Register Base */
+       phys_addr_t phys_pcie_reg_base; /* Physical PCIe Controller Base */
+       phys_addr_t phys_ecam_base;     /* Physical Configuration Base */
+       u32 breg_size;
+       u32 pcie_reg_size;
+       u32 ecam_size;
+       int irq_intx;
+       int irq_misc;
+       u32 ecam_value;
+       u8 last_busno;
+       u8 root_busno;
+       struct nwl_msi msi;
+       struct irq_domain *legacy_irq_domain;
+};
+
+static inline u32 nwl_bridge_readl(struct nwl_pcie *pcie, u32 off)
+{
+       return readl(pcie->breg_base + off);
+}
+
+static inline void nwl_bridge_writel(struct nwl_pcie *pcie, u32 val, u32 off)
+{
+       writel(val, pcie->breg_base + off);
+}
+
+static bool nwl_pcie_link_up(struct nwl_pcie *pcie)
+{
+       if (readl(pcie->pcireg_base + PS_LINKUP_OFFSET) & PCIE_PHY_LINKUP_BIT)
+               return true;
+       return false;
+}
+
+static bool nwl_phy_link_up(struct nwl_pcie *pcie)
+{
+       if (readl(pcie->pcireg_base + PS_LINKUP_OFFSET) & PHY_RDY_LINKUP_BIT)
+               return true;
+       return false;
+}
+
+static int nwl_wait_for_link(struct nwl_pcie *pcie)
+{
+       int retries;
+
+       /* check if the link is up or not */
+       for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+               if (nwl_phy_link_up(pcie))
+                       return 0;
+               usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
+       }
+
+       dev_err(pcie->dev, "PHY link never came up\n");
+       return -ETIMEDOUT;
+}
+
+static bool nwl_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
+{
+       struct nwl_pcie *pcie = bus->sysdata;
+
+       /* Check link before accessing downstream ports */
+       if (bus->number != pcie->root_busno) {
+               if (!nwl_pcie_link_up(pcie))
+                       return false;
+       }
+
+       /* Only one device down on each root port */
+       if (bus->number == pcie->root_busno && devfn > 0)
+               return false;
+
+       return true;
+}
+
+/**
+ * nwl_pcie_map_bus - Get configuration base
+ *
+ * @bus: Bus structure of current bus
+ * @devfn: Device/function
+ * @where: Offset from base
+ *
+ * Return: Base address of the configuration space needed to be
+ *        accessed.
+ */
+static void __iomem *nwl_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
+                                     int where)
+{
+       struct nwl_pcie *pcie = bus->sysdata;
+       int relbus;
+
+       if (!nwl_pcie_valid_device(bus, devfn))
+               return NULL;
+
+       relbus = (bus->number << ECAM_BUS_LOC_SHIFT) |
+                       (devfn << ECAM_DEV_LOC_SHIFT);
+
+       return pcie->ecam_base + relbus + where;
+}
+
+/* PCIe operations */
+static struct pci_ops nwl_pcie_ops = {
+       .map_bus = nwl_pcie_map_bus,
+       .read  = pci_generic_config_read,
+       .write = pci_generic_config_write,
+};
+
+static irqreturn_t nwl_pcie_misc_handler(int irq, void *data)
+{
+       struct nwl_pcie *pcie = data;
+       u32 misc_stat;
+
+       /* Checking for misc interrupts */
+       misc_stat = nwl_bridge_readl(pcie, MSGF_MISC_STATUS) &
+                                    MSGF_MISC_SR_MASKALL;
+       if (!misc_stat)
+               return IRQ_NONE;
+
+       if (misc_stat & MSGF_MISC_SR_RXMSG_OVER)
+               dev_err(pcie->dev, "Received Message FIFO Overflow\n");
+
+       if (misc_stat & MSGF_MISC_SR_SLAVE_ERR)
+               dev_err(pcie->dev, "Slave error\n");
+
+       if (misc_stat & MSGF_MISC_SR_MASTER_ERR)
+               dev_err(pcie->dev, "Master error\n");
+
+       if (misc_stat & MSGF_MISC_SR_I_ADDR_ERR)
+               dev_err(pcie->dev,
+                       "In Misc Ingress address translation error\n");
+
+       if (misc_stat & MSGF_MISC_SR_E_ADDR_ERR)
+               dev_err(pcie->dev,
+                       "In Misc Egress address translation error\n");
+
+       if (misc_stat & MSGF_MISC_SR_PCIE_CORE_ERR)
+               dev_err(pcie->dev, "PCIe Core error\n");
+
+       /* Clear misc interrupt status */
+       nwl_bridge_writel(pcie, misc_stat, MSGF_MISC_STATUS);
+
+       return IRQ_HANDLED;
+}
+
+static void nwl_pcie_leg_handler(struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct nwl_pcie *pcie;
+       unsigned long status;
+       u32 bit;
+       u32 virq;
+
+       chained_irq_enter(chip, desc);
+       pcie = irq_desc_get_handler_data(desc);
+
+       while ((status = nwl_bridge_readl(pcie, MSGF_LEG_STATUS) &
+                               MSGF_LEG_SR_MASKALL) != 0) {
+               for_each_set_bit(bit, &status, INTX_NUM) {
+                       virq = irq_find_mapping(pcie->legacy_irq_domain,
+                                               bit + 1);
+                       if (virq)
+                               generic_handle_irq(virq);
+               }
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static void nwl_pcie_handle_msi_irq(struct nwl_pcie *pcie, u32 status_reg)
+{
+       struct nwl_msi *msi;
+       unsigned long status;
+       u32 bit;
+       u32 virq;
+
+       msi = &pcie->msi;
+
+       while ((status = nwl_bridge_readl(pcie, status_reg)) != 0) {
+               for_each_set_bit(bit, &status, 32) {
+                       nwl_bridge_writel(pcie, 1 << bit, status_reg);
+                       virq = irq_find_mapping(msi->dev_domain, bit);
+                       if (virq)
+                               generic_handle_irq(virq);
+               }
+       }
+}
+
+static void nwl_pcie_msi_handler_high(struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct nwl_pcie *pcie = irq_desc_get_handler_data(desc);
+
+       chained_irq_enter(chip, desc);
+       nwl_pcie_handle_msi_irq(pcie, MSGF_MSI_STATUS_HI);
+       chained_irq_exit(chip, desc);
+}
+
+static void nwl_pcie_msi_handler_low(struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct nwl_pcie *pcie = irq_desc_get_handler_data(desc);
+
+       chained_irq_enter(chip, desc);
+       nwl_pcie_handle_msi_irq(pcie, MSGF_MSI_STATUS_LO);
+       chained_irq_exit(chip, desc);
+}
+
+static int nwl_legacy_map(struct irq_domain *domain, unsigned int irq,
+                         irq_hw_number_t hwirq)
+{
+       irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
+       irq_set_chip_data(irq, domain->host_data);
+
+       return 0;
+}
+
+static const struct irq_domain_ops legacy_domain_ops = {
+       .map = nwl_legacy_map,
+};
+
+#ifdef CONFIG_PCI_MSI
+static struct irq_chip nwl_msi_irq_chip = {
+       .name = "nwl_pcie:msi",
+       .irq_enable = unmask_msi_irq,
+       .irq_disable = mask_msi_irq,
+       .irq_mask = mask_msi_irq,
+       .irq_unmask = unmask_msi_irq,
+
+};
+
+static struct msi_domain_info nwl_msi_domain_info = {
+       .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_MULTI_PCI_MSI),
+       .chip = &nwl_msi_irq_chip,
+};
+#endif
+
+static void nwl_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data);
+       phys_addr_t msi_addr = pcie->phys_pcie_reg_base;
+
+       msg->address_lo = lower_32_bits(msi_addr);
+       msg->address_hi = upper_32_bits(msi_addr);
+       msg->data = data->hwirq;
+}
+
+static int nwl_msi_set_affinity(struct irq_data *irq_data,
+                               const struct cpumask *mask, bool force)
+{
+       return -EINVAL;
+}
+
+static struct irq_chip nwl_irq_chip = {
+       .name = "Xilinx MSI",
+       .irq_compose_msi_msg = nwl_compose_msi_msg,
+       .irq_set_affinity = nwl_msi_set_affinity,
+};
+
+static int nwl_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                               unsigned int nr_irqs, void *args)
+{
+       struct nwl_pcie *pcie = domain->host_data;
+       struct nwl_msi *msi = &pcie->msi;
+       int bit;
+       int i;
+
+       mutex_lock(&msi->lock);
+       bit = bitmap_find_next_zero_area(msi->bitmap, INT_PCI_MSI_NR, 0,
+                                        nr_irqs, 0);
+       if (bit >= INT_PCI_MSI_NR) {
+               mutex_unlock(&msi->lock);
+               return -ENOSPC;
+       }
+
+       bitmap_set(msi->bitmap, bit, nr_irqs);
+
+       for (i = 0; i < nr_irqs; i++) {
+               irq_domain_set_info(domain, virq + i, bit + i, &nwl_irq_chip,
+                               domain->host_data, handle_simple_irq,
+                               NULL, NULL);
+       }
+       mutex_unlock(&msi->lock);
+       return 0;
+}
+
+static void nwl_irq_domain_free(struct irq_domain *domain, unsigned int virq,
+                                       unsigned int nr_irqs)
+{
+       struct irq_data *data = irq_domain_get_irq_data(domain, virq);
+       struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data);
+       struct nwl_msi *msi = &pcie->msi;
+
+       mutex_lock(&msi->lock);
+       bitmap_clear(msi->bitmap, data->hwirq, nr_irqs);
+       mutex_unlock(&msi->lock);
+}
+
+static const struct irq_domain_ops dev_msi_domain_ops = {
+       .alloc  = nwl_irq_domain_alloc,
+       .free   = nwl_irq_domain_free,
+};
+
+static void nwl_msi_free_irq_domain(struct nwl_pcie *pcie)
+{
+       struct nwl_msi *msi = &pcie->msi;
+
+       if (msi->irq_msi0)
+               irq_set_chained_handler_and_data(msi->irq_msi0, NULL, NULL);
+       if (msi->irq_msi1)
+               irq_set_chained_handler_and_data(msi->irq_msi1, NULL, NULL);
+
+       if (msi->msi_domain)
+               irq_domain_remove(msi->msi_domain);
+       if (msi->dev_domain)
+               irq_domain_remove(msi->dev_domain);
+
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+}
+
+static void nwl_pcie_free_irq_domain(struct nwl_pcie *pcie)
+{
+       int i;
+       u32 irq;
+
+       for (i = 0; i < INTX_NUM; i++) {
+               irq = irq_find_mapping(pcie->legacy_irq_domain, i + 1);
+               if (irq > 0)
+                       irq_dispose_mapping(irq);
+       }
+       if (pcie->legacy_irq_domain)
+               irq_domain_remove(pcie->legacy_irq_domain);
+
+       nwl_msi_free_irq_domain(pcie);
+}
+
+static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie)
+{
+#ifdef CONFIG_PCI_MSI
+       struct fwnode_handle *fwnode = of_node_to_fwnode(pcie->dev->of_node);
+       struct nwl_msi *msi = &pcie->msi;
+
+       msi->dev_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR,
+                                               &dev_msi_domain_ops, pcie);
+       if (!msi->dev_domain) {
+               dev_err(pcie->dev, "failed to create dev IRQ domain\n");
+               return -ENOMEM;
+       }
+       msi->msi_domain = pci_msi_create_irq_domain(fwnode,
+                                                   &nwl_msi_domain_info,
+                                                   msi->dev_domain);
+       if (!msi->msi_domain) {
+               dev_err(pcie->dev, "failed to create msi IRQ domain\n");
+               irq_domain_remove(msi->dev_domain);
+               return -ENOMEM;
+       }
+#endif
+       return 0;
+}
+
+static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie)
+{
+       struct device_node *node = pcie->dev->of_node;
+       struct device_node *legacy_intc_node;
+
+       legacy_intc_node = of_get_next_child(node, NULL);
+       if (!legacy_intc_node) {
+               dev_err(pcie->dev, "No legacy intc node found\n");
+               return -EINVAL;
+       }
+
+       pcie->legacy_irq_domain = irq_domain_add_linear(legacy_intc_node,
+                                                       INTX_NUM,
+                                                       &legacy_domain_ops,
+                                                       pcie);
+
+       if (!pcie->legacy_irq_domain) {
+               dev_err(pcie->dev, "failed to create IRQ domain\n");
+               return -ENOMEM;
+       }
+
+       nwl_pcie_init_msi_irq_domain(pcie);
+       return 0;
+}
+
+static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus)
+{
+       struct platform_device *pdev = to_platform_device(pcie->dev);
+       struct nwl_msi *msi = &pcie->msi;
+       unsigned long base;
+       int ret;
+       int size = BITS_TO_LONGS(INT_PCI_MSI_NR) * sizeof(long);
+
+       mutex_init(&msi->lock);
+
+       msi->bitmap = kzalloc(size, GFP_KERNEL);
+       if (!msi->bitmap)
+               return -ENOMEM;
+
+       /* Get msi_1 IRQ number */
+       msi->irq_msi1 = platform_get_irq_byname(pdev, "msi1");
+       if (msi->irq_msi1 < 0) {
+               dev_err(&pdev->dev, "failed to get IRQ#%d\n", msi->irq_msi1);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       irq_set_chained_handler_and_data(msi->irq_msi1,
+                                        nwl_pcie_msi_handler_high, pcie);
+
+       /* Get msi_0 IRQ number */
+       msi->irq_msi0 = platform_get_irq_byname(pdev, "msi0");
+       if (msi->irq_msi0 < 0) {
+               dev_err(&pdev->dev, "failed to get IRQ#%d\n", msi->irq_msi0);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       irq_set_chained_handler_and_data(msi->irq_msi0,
+                                        nwl_pcie_msi_handler_low, pcie);
+
+       /* Check for msii_present bit */
+       ret = nwl_bridge_readl(pcie, I_MSII_CAPABILITIES) & MSII_PRESENT;
+       if (!ret) {
+               dev_err(pcie->dev, "MSI not present\n");
+               ret = -EIO;
+               goto err;
+       }
+
+       /* Enable MSII */
+       nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, I_MSII_CONTROL) |
+                         MSII_ENABLE, I_MSII_CONTROL);
+
+       /* Enable MSII status */
+       nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, I_MSII_CONTROL) |
+                         MSII_STATUS_ENABLE, I_MSII_CONTROL);
+
+       /* setup AFI/FPCI range */
+       base = pcie->phys_pcie_reg_base;
+       nwl_bridge_writel(pcie, lower_32_bits(base), I_MSII_BASE_LO);
+       nwl_bridge_writel(pcie, upper_32_bits(base), I_MSII_BASE_HI);
+
+       /*
+        * For high range MSI interrupts: disable, clear any pending,
+        * and enable
+        */
+       nwl_bridge_writel(pcie, (u32)~MSGF_MSI_SR_HI_MASK, MSGF_MSI_MASK_HI);
+
+       nwl_bridge_writel(pcie, nwl_bridge_readl(pcie,  MSGF_MSI_STATUS_HI) &
+                         MSGF_MSI_SR_HI_MASK, MSGF_MSI_STATUS_HI);
+
+       nwl_bridge_writel(pcie, MSGF_MSI_SR_HI_MASK, MSGF_MSI_MASK_HI);
+
+       /*
+        * For low range MSI interrupts: disable, clear any pending,
+        * and enable
+        */
+       nwl_bridge_writel(pcie, (u32)~MSGF_MSI_SR_LO_MASK, MSGF_MSI_MASK_LO);
+
+       nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_LO) &
+                         MSGF_MSI_SR_LO_MASK, MSGF_MSI_STATUS_LO);
+
+       nwl_bridge_writel(pcie, MSGF_MSI_SR_LO_MASK, MSGF_MSI_MASK_LO);
+
+       return 0;
+err:
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+       return ret;
+}
+
+static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
+{
+       struct platform_device *pdev = to_platform_device(pcie->dev);
+       u32 breg_val, ecam_val, first_busno = 0;
+       int err;
+
+       breg_val = nwl_bridge_readl(pcie, E_BREG_CAPABILITIES) & BREG_PRESENT;
+       if (!breg_val) {
+               dev_err(pcie->dev, "BREG is not present\n");
+               return breg_val;
+       }
+
+       /* Write bridge_off to breg base */
+       nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_breg_base),
+                         E_BREG_BASE_LO);
+       nwl_bridge_writel(pcie, upper_32_bits(pcie->phys_breg_base),
+                         E_BREG_BASE_HI);
+
+       /* Enable BREG */
+       nwl_bridge_writel(pcie, ~BREG_ENABLE_FORCE & BREG_ENABLE,
+                         E_BREG_CONTROL);
+
+       /* Disable DMA channel registers */
+       nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, BRCFG_PCIE_RX0) |
+                         CFG_DMA_REG_BAR, BRCFG_PCIE_RX0);
+
+       /* Enable Ingress subtractive decode translation */
+       nwl_bridge_writel(pcie, SET_ISUB_CONTROL, I_ISUB_CONTROL);
+
+       /* Enable msg filtering details */
+       nwl_bridge_writel(pcie, CFG_ENABLE_MSG_FILTER_MASK,
+                         BRCFG_PCIE_RX_MSG_FILTER);
+
+       err = nwl_wait_for_link(pcie);
+       if (err)
+               return err;
+
+       ecam_val = nwl_bridge_readl(pcie, E_ECAM_CAPABILITIES) & E_ECAM_PRESENT;
+       if (!ecam_val) {
+               dev_err(pcie->dev, "ECAM is not present\n");
+               return ecam_val;
+       }
+
+       /* Enable ECAM */
+       nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) |
+                         E_ECAM_CR_ENABLE, E_ECAM_CONTROL);
+
+       nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) |
+                         (pcie->ecam_value << E_ECAM_SIZE_SHIFT),
+                         E_ECAM_CONTROL);
+
+       nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_ecam_base),
+                         E_ECAM_BASE_LO);
+       nwl_bridge_writel(pcie, upper_32_bits(pcie->phys_ecam_base),
+                         E_ECAM_BASE_HI);
+
+       /* Get bus range */
+       ecam_val = nwl_bridge_readl(pcie, E_ECAM_CONTROL);
+       pcie->last_busno = (ecam_val & E_ECAM_SIZE_LOC) >> E_ECAM_SIZE_SHIFT;
+       /* Write primary, secondary and subordinate bus numbers */
+       ecam_val = first_busno;
+       ecam_val |= (first_busno + 1) << 8;
+       ecam_val |= (pcie->last_busno << E_ECAM_SIZE_SHIFT);
+       writel(ecam_val, (pcie->ecam_base + PCI_PRIMARY_BUS));
+
+       if (nwl_pcie_link_up(pcie))
+               dev_info(pcie->dev, "Link is UP\n");
+       else
+               dev_info(pcie->dev, "Link is DOWN\n");
+
+       /* Get misc IRQ number */
+       pcie->irq_misc = platform_get_irq_byname(pdev, "misc");
+       if (pcie->irq_misc < 0) {
+               dev_err(&pdev->dev, "failed to get misc IRQ %d\n",
+                       pcie->irq_misc);
+               return -EINVAL;
+       }
+
+       err = devm_request_irq(pcie->dev, pcie->irq_misc,
+                              nwl_pcie_misc_handler, IRQF_SHARED,
+                              "nwl_pcie:misc", pcie);
+       if (err) {
+               dev_err(pcie->dev, "fail to register misc IRQ#%d\n",
+                       pcie->irq_misc);
+               return err;
+       }
+
+       /* Disable all misc interrupts */
+       nwl_bridge_writel(pcie, (u32)~MSGF_MISC_SR_MASKALL, MSGF_MISC_MASK);
+
+       /* Clear pending misc interrupts */
+       nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MISC_STATUS) &
+                         MSGF_MISC_SR_MASKALL, MSGF_MISC_STATUS);
+
+       /* Enable all misc interrupts */
+       nwl_bridge_writel(pcie, MSGF_MISC_SR_MASKALL, MSGF_MISC_MASK);
+
+
+       /* Disable all legacy interrupts */
+       nwl_bridge_writel(pcie, (u32)~MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK);
+
+       /* Clear pending legacy interrupts */
+       nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_LEG_STATUS) &
+                         MSGF_LEG_SR_MASKALL, MSGF_LEG_STATUS);
+
+       /* Enable all legacy interrupts */
+       nwl_bridge_writel(pcie, MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK);
+
+       /* Enable the bridge config interrupt */
+       nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, BRCFG_INTERRUPT) |
+                         BRCFG_INTERRUPT_MASK, BRCFG_INTERRUPT);
+
+       return 0;
+}
+
+static int nwl_pcie_parse_dt(struct nwl_pcie *pcie,
+                            struct platform_device *pdev)
+{
+       struct device_node *node = pcie->dev->of_node;
+       struct resource *res;
+       const char *type;
+
+       /* Check for device type */
+       type = of_get_property(node, "device_type", NULL);
+       if (!type || strcmp(type, "pci")) {
+               dev_err(pcie->dev, "invalid \"device_type\" %s\n", type);
+               return -EINVAL;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "breg");
+       pcie->breg_base = devm_ioremap_resource(pcie->dev, res);
+       if (IS_ERR(pcie->breg_base))
+               return PTR_ERR(pcie->breg_base);
+       pcie->phys_breg_base = res->start;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcireg");
+       pcie->pcireg_base = devm_ioremap_resource(pcie->dev, res);
+       if (IS_ERR(pcie->pcireg_base))
+               return PTR_ERR(pcie->pcireg_base);
+       pcie->phys_pcie_reg_base = res->start;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
+       pcie->ecam_base = devm_ioremap_resource(pcie->dev, res);
+       if (IS_ERR(pcie->ecam_base))
+               return PTR_ERR(pcie->ecam_base);
+       pcie->phys_ecam_base = res->start;
+
+       /* Get intx IRQ number */
+       pcie->irq_intx = platform_get_irq_byname(pdev, "intx");
+       if (pcie->irq_intx < 0) {
+               dev_err(&pdev->dev, "failed to get intx IRQ %d\n",
+                       pcie->irq_intx);
+               return -EINVAL;
+       }
+
+       irq_set_chained_handler_and_data(pcie->irq_intx,
+                                        nwl_pcie_leg_handler, pcie);
+
+       return 0;
+}
+
+static const struct of_device_id nwl_pcie_of_match[] = {
+       { .compatible = "xlnx,nwl-pcie-2.11", },
+       {}
+};
+
+static int nwl_pcie_probe(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct nwl_pcie *pcie;
+       struct pci_bus *bus;
+       struct pci_bus *child;
+       int err;
+       resource_size_t iobase = 0;
+       LIST_HEAD(res);
+
+       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       if (!pcie)
+               return -ENOMEM;
+
+       pcie->dev = &pdev->dev;
+       pcie->ecam_value = NWL_ECAM_VALUE_DEFAULT;
+
+       err = nwl_pcie_parse_dt(pcie, pdev);
+       if (err) {
+               dev_err(pcie->dev, "Parsing DT failed\n");
+               return err;
+       }
+
+       err = nwl_pcie_bridge_init(pcie);
+       if (err) {
+               dev_err(pcie->dev, "HW Initalization failed\n");
+               return err;
+       }
+
+       err = of_pci_get_host_bridge_resources(node, 0, 0xff, &res, &iobase);
+       if (err) {
+               pr_err("Getting bridge resources failed\n");
+               return err;
+       }
+
+       err = nwl_pcie_init_irq_domain(pcie);
+       if (err) {
+               dev_err(pcie->dev, "Failed creating IRQ Domain\n");
+               return err;
+       }
+
+       bus = pci_create_root_bus(&pdev->dev, pcie->root_busno,
+                                 &nwl_pcie_ops, pcie, &res);
+       if (!bus)
+               return -ENOMEM;
+
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               err = nwl_pcie_enable_msi(pcie, bus);
+               if (err < 0) {
+                       dev_err(&pdev->dev,
+                               "failed to enable MSI support: %d\n", err);
+                       return err;
+               }
+       }
+       pci_scan_child_bus(bus);
+       pci_assign_unassigned_bus_resources(bus);
+       list_for_each_entry(child, &bus->children, node)
+               pcie_bus_configure_settings(child);
+       pci_bus_add_devices(bus);
+       platform_set_drvdata(pdev, pcie);
+       return 0;
+}
+
+static int nwl_pcie_remove(struct platform_device *pdev)
+{
+       struct nwl_pcie *pcie = platform_get_drvdata(pdev);
+
+       nwl_pcie_free_irq_domain(pcie);
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+static struct platform_driver nwl_pcie_driver = {
+       .driver = {
+               .name = "nwl-pcie",
+               .of_match_table = nwl_pcie_of_match,
+       },
+       .probe = nwl_pcie_probe,
+       .remove = nwl_pcie_remove,
+};
+module_platform_driver(nwl_pcie_driver);
+
+MODULE_AUTHOR("Xilinx, Inc");
+MODULE_DESCRIPTION("NWL PCIe driver");
+MODULE_LICENSE("GPL");
index 4cfa463..65f0fe0 100644 (file)
@@ -94,9 +94,6 @@
 /* Number of MSI IRQs */
 #define XILINX_NUM_MSI_IRQS            128
 
-/* Number of Memory Resources */
-#define XILINX_MAX_NUM_RESOURCES       3
-
 /**
  * struct xilinx_pcie_port - PCIe port information
  * @reg_base: IO Mapped Register Base
  * @root_busno: Root Bus number
  * @dev: Device pointer
  * @irq_domain: IRQ domain pointer
- * @bus_range: Bus range
  * @resources: Bus Resources
  */
 struct xilinx_pcie_port {
@@ -115,17 +111,11 @@ struct xilinx_pcie_port {
        u8 root_busno;
        struct device *dev;
        struct irq_domain *irq_domain;
-       struct resource bus_range;
        struct list_head resources;
 };
 
 static DECLARE_BITMAP(msi_irq_in_use, XILINX_NUM_MSI_IRQS);
 
-static inline struct xilinx_pcie_port *sys_to_pcie(struct pci_sys_data *sys)
-{
-       return sys->private_data;
-}
-
 static inline u32 pcie_read(struct xilinx_pcie_port *port, u32 reg)
 {
        return readl(port->reg_base + reg);
@@ -167,7 +157,7 @@ static void xilinx_pcie_clear_err_interrupts(struct xilinx_pcie_port *port)
  */
 static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
 {
-       struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata);
+       struct xilinx_pcie_port *port = bus->sysdata;
 
        /* Check if link is up when trying to access downstream ports */
        if (bus->number != port->root_busno)
@@ -200,7 +190,7 @@ static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
 static void __iomem *xilinx_pcie_map_bus(struct pci_bus *bus,
                                         unsigned int devfn, int where)
 {
-       struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata);
+       struct xilinx_pcie_port *port = bus->sysdata;
        int relbus;
 
        if (!xilinx_pcie_valid_device(bus, devfn))
@@ -232,7 +222,7 @@ static void xilinx_pcie_destroy_msi(unsigned int irq)
 
        if (!test_bit(irq, msi_irq_in_use)) {
                msi = irq_get_msi_desc(irq);
-               port = sys_to_pcie(msi_desc_to_pci_sysdata(msi));
+               port = msi_desc_to_pci_sysdata(msi);
                dev_err(port->dev, "Trying to free unused MSI#%d\n", irq);
        } else {
                clear_bit(irq, msi_irq_in_use);
@@ -281,7 +271,7 @@ static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip,
                                     struct pci_dev *pdev,
                                     struct msi_desc *desc)
 {
-       struct xilinx_pcie_port *port = sys_to_pcie(pdev->bus->sysdata);
+       struct xilinx_pcie_port *port = pdev->bus->sysdata;
        unsigned int irq;
        int hwirq;
        struct msi_msg msg;
@@ -617,138 +607,6 @@ static void xilinx_pcie_init_port(struct xilinx_pcie_port *port)
                   XILINX_PCIE_REG_RPSC);
 }
 
-/**
- * xilinx_pcie_setup - Setup memory resources
- * @nr: Bus number
- * @sys: Per controller structure
- *
- * Return: '1' on success and error value on failure
- */
-static int xilinx_pcie_setup(int nr, struct pci_sys_data *sys)
-{
-       struct xilinx_pcie_port *port = sys_to_pcie(sys);
-
-       list_splice_init(&port->resources, &sys->resources);
-
-       return 1;
-}
-
-/**
- * xilinx_pcie_scan_bus - Scan PCIe bus for devices
- * @nr: Bus number
- * @sys: Per controller structure
- *
- * Return: Valid Bus pointer on success and NULL on failure
- */
-static struct pci_bus *xilinx_pcie_scan_bus(int nr, struct pci_sys_data *sys)
-{
-       struct xilinx_pcie_port *port = sys_to_pcie(sys);
-       struct pci_bus *bus;
-
-       port->root_busno = sys->busnr;
-
-       if (IS_ENABLED(CONFIG_PCI_MSI))
-               bus = pci_scan_root_bus_msi(port->dev, sys->busnr,
-                                           &xilinx_pcie_ops, sys,
-                                           &sys->resources,
-                                           &xilinx_pcie_msi_chip);
-       else
-               bus = pci_scan_root_bus(port->dev, sys->busnr,
-                                       &xilinx_pcie_ops, sys, &sys->resources);
-       return bus;
-}
-
-/**
- * xilinx_pcie_parse_and_add_res - Add resources by parsing ranges
- * @port: PCIe port information
- *
- * Return: '0' on success and error value on failure
- */
-static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port)
-{
-       struct device *dev = port->dev;
-       struct device_node *node = dev->of_node;
-       struct resource *mem;
-       resource_size_t offset;
-       struct of_pci_range_parser parser;
-       struct of_pci_range range;
-       struct resource_entry *win;
-       int err = 0, mem_resno = 0;
-
-       /* Get the ranges */
-       if (of_pci_range_parser_init(&parser, node)) {
-               dev_err(dev, "missing \"ranges\" property\n");
-               return -EINVAL;
-       }
-
-       /* Parse the ranges and add the resources found to the list */
-       for_each_of_pci_range(&parser, &range) {
-
-               if (mem_resno >= XILINX_MAX_NUM_RESOURCES) {
-                       dev_err(dev, "Maximum memory resources exceeded\n");
-                       return -EINVAL;
-               }
-
-               mem = devm_kmalloc(dev, sizeof(*mem), GFP_KERNEL);
-               if (!mem) {
-                       err = -ENOMEM;
-                       goto free_resources;
-               }
-
-               of_pci_range_to_resource(&range, node, mem);
-
-               switch (mem->flags & IORESOURCE_TYPE_BITS) {
-               case IORESOURCE_MEM:
-                       offset = range.cpu_addr - range.pci_addr;
-                       mem_resno++;
-                       break;
-               default:
-                       err = -EINVAL;
-                       break;
-               }
-
-               if (err < 0) {
-                       dev_warn(dev, "Invalid resource found %pR\n", mem);
-                       continue;
-               }
-
-               err = request_resource(&iomem_resource, mem);
-               if (err)
-                       goto free_resources;
-
-               pci_add_resource_offset(&port->resources, mem, offset);
-       }
-
-       /* Get the bus range */
-       if (of_pci_parse_bus_range(node, &port->bus_range)) {
-               u32 val = pcie_read(port, XILINX_PCIE_REG_BIR);
-               u8 last;
-
-               last = (val & XILINX_PCIE_BIR_ECAM_SZ_MASK) >>
-                       XILINX_PCIE_BIR_ECAM_SZ_SHIFT;
-
-               port->bus_range = (struct resource) {
-                       .name   = node->name,
-                       .start  = 0,
-                       .end    = last,
-                       .flags  = IORESOURCE_BUS,
-               };
-       }
-
-       /* Register bus resource */
-       pci_add_resource(&port->resources, &port->bus_range);
-
-       return 0;
-
-free_resources:
-       release_child_resources(&iomem_resource);
-       resource_list_for_each_entry(win, &port->resources)
-               devm_kfree(dev, win->res);
-       pci_free_resource_list(&port->resources);
-
-       return err;
-}
-
 /**
  * xilinx_pcie_parse_dt - Parse Device tree
  * @port: PCIe port information
@@ -800,9 +658,12 @@ static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)
 static int xilinx_pcie_probe(struct platform_device *pdev)
 {
        struct xilinx_pcie_port *port;
-       struct hw_pci hw;
        struct device *dev = &pdev->dev;
+       struct pci_bus *bus;
+
        int err;
+       resource_size_t iobase = 0;
+       LIST_HEAD(res);
 
        if (!dev->of_node)
                return -ENODEV;
@@ -827,34 +688,28 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
                return err;
        }
 
-       /*
-        * Parse PCI ranges, configuration bus range and
-        * request their resources
-        */
-       INIT_LIST_HEAD(&port->resources);
-       err = xilinx_pcie_parse_and_add_res(port);
+       err = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff, &res,
+                                              &iobase);
        if (err) {
-               dev_err(dev, "Failed adding resources\n");
+               dev_err(dev, "Getting bridge resources failed\n");
                return err;
        }
-
-       platform_set_drvdata(pdev, port);
-
-       /* Register the device */
-       memset(&hw, 0, sizeof(hw));
-       hw = (struct hw_pci) {
-               .nr_controllers = 1,
-               .private_data   = (void **)&port,
-               .setup          = xilinx_pcie_setup,
-               .map_irq        = of_irq_parse_and_map_pci,
-               .scan           = xilinx_pcie_scan_bus,
-               .ops            = &xilinx_pcie_ops,
-       };
+       bus = pci_create_root_bus(&pdev->dev, 0,
+                                 &xilinx_pcie_ops, port, &res);
+       if (!bus)
+               return -ENOMEM;
 
 #ifdef CONFIG_PCI_MSI
        xilinx_pcie_msi_chip.dev = port->dev;
+       bus->msi = &xilinx_pcie_msi_chip;
 #endif
-       pci_common_init_dev(dev, &hw);
+       pci_scan_child_bus(bus);
+       pci_assign_unassigned_bus_resources(bus);
+#ifndef CONFIG_MICROBLAZE
+       pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+#endif
+       pci_bus_add_devices(bus);
+       platform_set_drvdata(pdev, port);
 
        return 0;
 }
index 31f31d4..fe4bd0a 100644 (file)
@@ -387,10 +387,6 @@ static int sriov_init(struct pci_dev *dev, int pos)
        struct resource *res;
        struct pci_dev *pdev;
 
-       if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END &&
-           pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT)
-               return -ENODEV;
-
        pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
        if (ctrl & PCI_SRIOV_CTRL_VFE) {
                pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
index 0ae74d9..5135737 100644 (file)
@@ -16,7 +16,7 @@
  * the instance number and string from the type 41 record and exports
  * it to sysfs.
  *
- * Please see http://linux.dell.com/wiki/index.php/Oss/libnetdevname for more
+ * Please see http://linux.dell.com/files/biosdevname/ for more
  * information.
  */
 
index 95d9e7b..e982010 100644 (file)
@@ -769,10 +769,12 @@ static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj,
 {
        struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
 
-       if (off > bin_attr->size)
-               count = 0;
-       else if (count > bin_attr->size - off)
-               count = bin_attr->size - off;
+       if (bin_attr->size > 0) {
+               if (off > bin_attr->size)
+                       count = 0;
+               else if (count > bin_attr->size - off)
+                       count = bin_attr->size - off;
+       }
 
        return pci_read_vpd(dev, off, count, buf);
 }
@@ -783,10 +785,12 @@ static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj,
 {
        struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
 
-       if (off > bin_attr->size)
-               count = 0;
-       else if (count > bin_attr->size - off)
-               count = bin_attr->size - off;
+       if (bin_attr->size > 0) {
+               if (off > bin_attr->size)
+                       count = 0;
+               else if (count > bin_attr->size - off)
+                       count = bin_attr->size - off;
+       }
 
        return pci_write_vpd(dev, off, count, buf);
 }
@@ -1134,33 +1138,36 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
        /* allocate attribute structure, piggyback attribute name */
        int name_len = write_combine ? 13 : 10;
        struct bin_attribute *res_attr;
+       char *res_attr_name;
        int retval;
 
        res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC);
-       if (res_attr) {
-               char *res_attr_name = (char *)(res_attr + 1);
-
-               sysfs_bin_attr_init(res_attr);
-               if (write_combine) {
-                       pdev->res_attr_wc[num] = res_attr;
-                       sprintf(res_attr_name, "resource%d_wc", num);
-                       res_attr->mmap = pci_mmap_resource_wc;
-               } else {
-                       pdev->res_attr[num] = res_attr;
-                       sprintf(res_attr_name, "resource%d", num);
-                       res_attr->mmap = pci_mmap_resource_uc;
-               }
-               if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
-                       res_attr->read = pci_read_resource_io;
-                       res_attr->write = pci_write_resource_io;
-               }
-               res_attr->attr.name = res_attr_name;
-               res_attr->attr.mode = S_IRUSR | S_IWUSR;
-               res_attr->size = pci_resource_len(pdev, num);
-               res_attr->private = &pdev->resource[num];
-               retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
-       } else
-               retval = -ENOMEM;
+       if (!res_attr)
+               return -ENOMEM;
+
+       res_attr_name = (char *)(res_attr + 1);
+
+       sysfs_bin_attr_init(res_attr);
+       if (write_combine) {
+               pdev->res_attr_wc[num] = res_attr;
+               sprintf(res_attr_name, "resource%d_wc", num);
+               res_attr->mmap = pci_mmap_resource_wc;
+       } else {
+               pdev->res_attr[num] = res_attr;
+               sprintf(res_attr_name, "resource%d", num);
+               res_attr->mmap = pci_mmap_resource_uc;
+       }
+       if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
+               res_attr->read = pci_read_resource_io;
+               res_attr->write = pci_write_resource_io;
+       }
+       res_attr->attr.name = res_attr_name;
+       res_attr->attr.mode = S_IRUSR | S_IWUSR;
+       res_attr->size = pci_resource_len(pdev, num);
+       res_attr->private = &pdev->resource[num];
+       retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
+       if (retval)
+               kfree(res_attr);
 
        return retval;
 }
@@ -1319,7 +1326,7 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev)
                        return -ENOMEM;
 
                sysfs_bin_attr_init(attr);
-               attr->size = dev->vpd->len;
+               attr->size = 0;
                attr->attr.name = "vpd";
                attr->attr.mode = S_IRUSR | S_IWUSR;
                attr->read = read_vpd_attr;
@@ -1356,7 +1363,7 @@ error:
 int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
 {
        int retval;
-       int rom_size = 0;
+       int rom_size;
        struct bin_attribute *attr;
 
        if (!sysfs_initialized)
@@ -1373,12 +1380,8 @@ int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
        if (retval)
                goto err_config_file;
 
-       if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
-               rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
-       else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
-               rom_size = 0x20000;
-
        /* If the device has a ROM, try to expose it in sysfs. */
+       rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
        if (rom_size) {
                attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
                if (!attr) {
@@ -1409,7 +1412,7 @@ int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
        return 0;
 
 err_rom_file:
-       if (rom_size) {
+       if (pdev->rom_attr) {
                sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
                kfree(pdev->rom_attr);
                pdev->rom_attr = NULL;
@@ -1447,8 +1450,6 @@ static void pci_remove_capabilities_sysfs(struct pci_dev *dev)
  */
 void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
 {
-       int rom_size = 0;
-
        if (!sysfs_initialized)
                return;
 
@@ -1461,18 +1462,13 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
 
        pci_remove_resource_files(pdev);
 
-       if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
-               rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
-       else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
-               rom_size = 0x20000;
-
-       if (rom_size && pdev->rom_attr) {
+       if (pdev->rom_attr) {
                sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
                kfree(pdev->rom_attr);
+               pdev->rom_attr = NULL;
        }
 
        pci_remove_firmware_label_files(pdev);
-
 }
 
 static int __init pci_sysfs_init(void)
index f89db3a..25e0327 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/device.h>
 #include <linux/pm_runtime.h>
 #include <linux/pci_hotplug.h>
-#include <asm-generic/pci-bridge.h>
 #include <asm/setup.h>
 #include <linux/aer.h>
 #include "pci.h"
@@ -3386,18 +3385,6 @@ bool pci_check_and_unmask_intx(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_check_and_unmask_intx);
 
-int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size)
-{
-       return dma_set_max_seg_size(&dev->dev, size);
-}
-EXPORT_SYMBOL(pci_set_dma_max_seg_size);
-
-int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask)
-{
-       return dma_set_seg_boundary(&dev->dev, mask);
-}
-EXPORT_SYMBOL(pci_set_dma_seg_boundary);
-
 /**
  * pci_wait_for_pending_transaction - waits for pending transaction
  * @dev: the PCI device to operate on
@@ -3414,6 +3401,29 @@ int pci_wait_for_pending_transaction(struct pci_dev *dev)
 }
 EXPORT_SYMBOL(pci_wait_for_pending_transaction);
 
+/*
+ * We should only need to wait 100ms after FLR, but some devices take longer.
+ * Wait for up to 1000ms for config space to return something other than -1.
+ * Intel IGD requires this when an LCD panel is attached.  We read the 2nd
+ * dword because VFs don't implement the 1st dword.
+ */
+static void pci_flr_wait(struct pci_dev *dev)
+{
+       int i = 0;
+       u32 id;
+
+       do {
+               msleep(100);
+               pci_read_config_dword(dev, PCI_COMMAND, &id);
+       } while (i++ < 10 && id == ~0);
+
+       if (id == ~0)
+               dev_warn(&dev->dev, "Failed to return from FLR\n");
+       else if (i > 1)
+               dev_info(&dev->dev, "Required additional %dms to return from FLR\n",
+                        (i - 1) * 100);
+}
+
 static int pcie_flr(struct pci_dev *dev, int probe)
 {
        u32 cap;
@@ -3429,7 +3439,7 @@ static int pcie_flr(struct pci_dev *dev, int probe)
                dev_err(&dev->dev, "timed out waiting for pending transaction; performing function level reset anyway\n");
 
        pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
-       msleep(100);
+       pci_flr_wait(dev);
        return 0;
 }
 
@@ -3459,7 +3469,7 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
                dev_err(&dev->dev, "timed out waiting for pending transaction; performing AF function level reset anyway\n");
 
        pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
-       msleep(100);
+       pci_flr_wait(dev);
        return 0;
 }
 
index 9a1660f..d0fb934 100644 (file)
@@ -97,21 +97,21 @@ static inline bool pci_has_subordinate(struct pci_dev *pci_dev)
 struct pci_vpd_ops {
        ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
        ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
-       void (*release)(struct pci_dev *dev);
 };
 
 struct pci_vpd {
-       unsigned int len;
        const struct pci_vpd_ops *ops;
        struct bin_attribute *attr; /* descriptor for sysfs VPD entry */
+       struct mutex    lock;
+       unsigned int    len;
+       u16             flag;
+       u8              cap;
+       u8              busy:1;
+       u8              valid:1;
 };
 
-int pci_vpd_pci22_init(struct pci_dev *dev);
-static inline void pci_vpd_release(struct pci_dev *dev)
-{
-       if (dev->vpd)
-               dev->vpd->ops->release(dev);
-}
+int pci_vpd_init(struct pci_dev *dev);
+void pci_vpd_release(struct pci_dev *dev);
 
 /* PCI /proc functions */
 #ifdef CONFIG_PROC_FS
index e294713..72db7f4 100644 (file)
@@ -44,6 +44,7 @@ config PCIEASPM
          /sys/module/pcie_aspm/parameters/policy
 
          When in doubt, say Y.
+
 config PCIEASPM_DEBUG
        bool "Debug PCI Express ASPM"
        depends on PCIEASPM
@@ -58,20 +59,20 @@ choice
        depends on PCIEASPM
 
 config PCIEASPM_DEFAULT
-        bool "BIOS default"
+       bool "BIOS default"
        depends on PCIEASPM
        help
          Use the BIOS defaults for PCI Express ASPM.
 
 config PCIEASPM_POWERSAVE
-        bool "Powersave"
+       bool "Powersave"
        depends on PCIEASPM
        help
          Enable PCI Express ASPM L0s and L1 where possible, even if the
          BIOS did not.
 
 config PCIEASPM_PERFORMANCE
-        bool "Performance"
+       bool "Performance"
        depends on PCIEASPM
        help
          Disable PCI Express ASPM L0s and L1, even if the BIOS enabled them.
index 20db790..db553dc 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/fs.h>
 #include <linux/uaccess.h>
 #include <linux/stddef.h>
+#include <linux/device.h>
 #include "aerdrv.h"
 
 /* Override the existing corrected and uncorrected error masks */
@@ -124,16 +125,13 @@ static struct pci_ops *__find_pci_bus_ops(struct pci_bus *bus)
 static struct pci_bus_ops *pci_bus_ops_pop(void)
 {
        unsigned long flags;
-       struct pci_bus_ops *bus_ops = NULL;
+       struct pci_bus_ops *bus_ops;
 
        spin_lock_irqsave(&inject_lock, flags);
-       if (list_empty(&pci_bus_ops_list))
-               bus_ops = NULL;
-       else {
-               struct list_head *lh = pci_bus_ops_list.next;
-               list_del(lh);
-               bus_ops = list_entry(lh, struct pci_bus_ops, list);
-       }
+       bus_ops = list_first_entry_or_null(&pci_bus_ops_list,
+                                          struct pci_bus_ops, list);
+       if (bus_ops)
+               list_del(&bus_ops->list);
        spin_unlock_irqrestore(&inject_lock, flags);
        return bus_ops;
 }
@@ -181,14 +179,16 @@ static u32 *find_pci_config_dword(struct aer_error *err, int where,
        return target;
 }
 
-static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where,
-                       int size, u32 *val)
+static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
+                              int where, int size, u32 *val)
 {
        u32 *sim;
        struct aer_error *err;
        unsigned long flags;
        struct pci_ops *ops;
+       struct pci_ops *my_ops;
        int domain;
+       int rv;
 
        spin_lock_irqsave(&inject_lock, flags);
        if (size != sizeof(u32))
@@ -208,19 +208,32 @@ static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where,
        }
 out:
        ops = __find_pci_bus_ops(bus);
+       /*
+        * pci_lock must already be held, so we can directly
+        * manipulate bus->ops.  Many config access functions,
+        * including pci_generic_config_read() require the original
+        * bus->ops be installed to function, so temporarily put them
+        * back.
+        */
+       my_ops = bus->ops;
+       bus->ops = ops;
+       rv = ops->read(bus, devfn, where, size, val);
+       bus->ops = my_ops;
        spin_unlock_irqrestore(&inject_lock, flags);
-       return ops->read(bus, devfn, where, size, val);
+       return rv;
 }
 
-static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where,
-                        int size, u32 val)
+static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
+                               int where, int size, u32 val)
 {
        u32 *sim;
        struct aer_error *err;
        unsigned long flags;
        int rw1cs;
        struct pci_ops *ops;
+       struct pci_ops *my_ops;
        int domain;
+       int rv;
 
        spin_lock_irqsave(&inject_lock, flags);
        if (size != sizeof(u32))
@@ -243,13 +256,24 @@ static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where,
        }
 out:
        ops = __find_pci_bus_ops(bus);
+       /*
+        * pci_lock must already be held, so we can directly
+        * manipulate bus->ops.  Many config access functions,
+        * including pci_generic_config_write() require the original
+        * bus->ops be installed to function, so temporarily put them
+        * back.
+        */
+       my_ops = bus->ops;
+       bus->ops = ops;
+       rv = ops->write(bus, devfn, where, size, val);
+       bus->ops = my_ops;
        spin_unlock_irqrestore(&inject_lock, flags);
-       return ops->write(bus, devfn, where, size, val);
+       return rv;
 }
 
-static struct pci_ops pci_ops_aer = {
-       .read = pci_read_aer,
-       .write = pci_write_aer,
+static struct pci_ops aer_inj_pci_ops = {
+       .read = aer_inj_read_config,
+       .write = aer_inj_write_config,
 };
 
 static void pci_bus_ops_init(struct pci_bus_ops *bus_ops,
@@ -270,9 +294,9 @@ static int pci_bus_set_aer_ops(struct pci_bus *bus)
        bus_ops = kmalloc(sizeof(*bus_ops), GFP_KERNEL);
        if (!bus_ops)
                return -ENOMEM;
-       ops = pci_bus_set_ops(bus, &pci_ops_aer);
+       ops = pci_bus_set_ops(bus, &aer_inj_pci_ops);
        spin_lock_irqsave(&inject_lock, flags);
-       if (ops == &pci_ops_aer)
+       if (ops == &aer_inj_pci_ops)
                goto out;
        pci_bus_ops_init(bus_ops, bus, ops);
        list_add(&bus_ops->list, &pci_bus_ops_list);
@@ -334,13 +358,15 @@ static int aer_inject(struct aer_error_inj *einj)
                return -ENODEV;
        rpdev = pcie_find_root_port(dev);
        if (!rpdev) {
+               dev_err(&dev->dev, "aer_inject: Root port not found\n");
                ret = -ENODEV;
                goto out_put;
        }
 
        pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
        if (!pos_cap_err) {
-               ret = -EPERM;
+               dev_err(&dev->dev, "aer_inject: Device doesn't support AER\n");
+               ret = -EPROTONOSUPPORT;
                goto out_put;
        }
        pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever);
@@ -350,7 +376,9 @@ static int aer_inject(struct aer_error_inj *einj)
 
        rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR);
        if (!rp_pos_cap_err) {
-               ret = -EPERM;
+               dev_err(&rpdev->dev,
+                       "aer_inject: Root port doesn't support AER\n");
+               ret = -EPROTONOSUPPORT;
                goto out_put;
        }
 
@@ -397,14 +425,16 @@ static int aer_inject(struct aer_error_inj *einj)
        if (!aer_mask_override && einj->cor_status &&
            !(einj->cor_status & ~cor_mask)) {
                ret = -EINVAL;
-               printk(KERN_WARNING "The correctable error(s) is masked by device\n");
+               dev_warn(&dev->dev,
+                        "aer_inject: The correctable error(s) is masked by device\n");
                spin_unlock_irqrestore(&inject_lock, flags);
                goto out_put;
        }
        if (!aer_mask_override && einj->uncor_status &&
            !(einj->uncor_status & ~uncor_mask)) {
                ret = -EINVAL;
-               printk(KERN_WARNING "The uncorrectable error(s) is masked by device\n");
+               dev_warn(&dev->dev,
+                        "aer_inject: The uncorrectable error(s) is masked by device\n");
                spin_unlock_irqrestore(&inject_lock, flags);
                goto out_put;
        }
@@ -457,13 +487,19 @@ static int aer_inject(struct aer_error_inj *einj)
 
        if (find_aer_device(rpdev, &edev)) {
                if (!get_service_data(edev)) {
-                       printk(KERN_WARNING "AER service is not initialized\n");
-                       ret = -EINVAL;
+                       dev_warn(&edev->device,
+                                "aer_inject: AER service is not initialized\n");
+                       ret = -EPROTONOSUPPORT;
                        goto out_put;
                }
+               dev_info(&edev->device,
+                        "aer_inject: Injecting errors %08x/%08x into device %s\n",
+                        einj->cor_status, einj->uncor_status, pci_name(dev));
                aer_irq(-1, edev);
-       } else
-               ret = -EINVAL;
+       } else {
+               dev_err(&rpdev->dev, "aer_inject: AER device not found\n");
+               ret = -ENODEV;
+       }
 out_put:
        kfree(err_alloc);
        kfree(rperr_alloc);
index 63fc639..1ae4c73 100644 (file)
@@ -396,7 +396,7 @@ static int pcie_pme_suspend(struct pcie_device *srv)
 {
        struct pcie_pme_service_data *data = get_service_data(srv);
        struct pci_dev *port = srv->port;
-       bool wakeup;
+       bool wakeup, wake_irq_enabled = false;
        int ret;
 
        if (device_may_wakeup(&port->dev)) {
@@ -409,11 +409,12 @@ static int pcie_pme_suspend(struct pcie_device *srv)
        spin_lock_irq(&data->lock);
        if (wakeup) {
                ret = enable_irq_wake(srv->irq);
-               data->suspend_level = PME_SUSPEND_WAKEUP;
+               if (ret == 0) {
+                       data->suspend_level = PME_SUSPEND_WAKEUP;
+                       wake_irq_enabled = true;
+               }
        }
-       if (!wakeup || ret) {
-               struct pci_dev *port = srv->port;
-
+       if (!wake_irq_enabled) {
                pcie_pme_interrupt_enable(port, false);
                pcie_clear_root_pme_status(port);
                data->suspend_level = PME_SUSPEND_NOIRQ;
index 6d7ab9b..8004f67 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/pci-aspm.h>
 #include <linux/aer.h>
 #include <linux/acpi.h>
-#include <asm-generic/pci-bridge.h>
+#include <linux/irqdomain.h>
 #include "pci.h"
 
 #define CARDBUS_LATENCY_TIMER  176     /* secondary latency timer */
@@ -179,6 +179,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
        u16 orig_cmd;
        struct pci_bus_region region, inverted_region;
 
+       if (dev->non_compliant_bars)
+               return 0;
+
        mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
 
        /* No printks while decoding is disabled! */
@@ -675,6 +678,20 @@ static struct irq_domain *pci_host_bridge_msi_domain(struct pci_bus *bus)
        if (!d)
                d = pci_host_bridge_acpi_msi_domain(bus);
 
+#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
+       /*
+        * If no IRQ domain was found via the OF tree, try looking it up
+        * directly through the fwnode_handle.
+        */
+       if (!d) {
+               struct fwnode_handle *fwnode = pci_root_bus_fwnode(bus);
+
+               if (fwnode)
+                       d = irq_find_matching_fwnode(fwnode,
+                                                    DOMAIN_BUS_PCI_MSI);
+       }
+#endif
+
        return d;
 }
 
@@ -758,6 +775,12 @@ add_dev:
 
        pcibios_add_bus(child);
 
+       if (child->ops->add_bus) {
+               ret = child->ops->add_bus(child);
+               if (WARN_ON(ret < 0))
+                       dev_err(&child->dev, "failed to add bus: %d\n", ret);
+       }
+
        /* Create legacy_io and legacy_mem files for this bus */
        pci_create_legacy_files(child);
 
@@ -1171,6 +1194,7 @@ static void pci_msi_setup_pci_dev(struct pci_dev *dev)
 int pci_setup_device(struct pci_dev *dev)
 {
        u32 class;
+       u16 cmd;
        u8 hdr_type;
        int pos = 0;
        struct pci_bus_region region;
@@ -1214,6 +1238,16 @@ int pci_setup_device(struct pci_dev *dev)
        /* device class may be changed after fixup */
        class = dev->class >> 8;
 
+       if (dev->non_compliant_bars) {
+               pci_read_config_word(dev, PCI_COMMAND, &cmd);
+               if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
+                       dev_info(&dev->dev, "device has non-compliant BARs; disabling IO/MEM decoding\n");
+                       cmd &= ~PCI_COMMAND_IO;
+                       cmd &= ~PCI_COMMAND_MEMORY;
+                       pci_write_config_word(dev, PCI_COMMAND, cmd);
+               }
+       }
+
        switch (dev->hdr_type) {                    /* header type */
        case PCI_HEADER_TYPE_NORMAL:                /* standard header */
                if (class == PCI_CLASS_BRIDGE_PCI)
@@ -1608,7 +1642,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
        pci_pm_init(dev);
 
        /* Vital Product Data */
-       pci_vpd_pci22_init(dev);
+       pci_vpd_init(dev);
 
        /* Alternative Routing-ID Forwarding */
        pci_configure_ari(dev);
@@ -1803,6 +1837,13 @@ static int only_one_child(struct pci_bus *bus)
                return 0;
        if (pci_pcie_type(parent) == PCI_EXP_TYPE_ROOT_PORT)
                return 1;
+
+       /*
+        * PCIe downstream ports are bridges that normally lead to only a
+        * device 0, but if PCI_SCAN_ALL_PCIE_DEVS is set, scan all
+        * possible devices, not just device 0.  See PCIe spec r3.0,
+        * sec 7.3.1.
+        */
        if (parent->has_secondary_link &&
            !pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS))
                return 1;
index 0575a1e..8e67802 100644 (file)
@@ -438,7 +438,7 @@ static void quirk_amd_nl_class(struct pci_dev *pdev)
        u32 class = pdev->class;
 
        /* Use "USB Device (not host controller)" class */
-       pdev->class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe;
+       pdev->class = PCI_CLASS_SERIAL_USB_DEVICE;
        dev_info(&pdev->dev, "PCI class overridden (%#08x -> %#08x) so dwc3 driver can claim this instead of xhci\n",
                 class, pdev->class);
 }
@@ -2134,6 +2134,35 @@ static void quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching);
 
+/*
+ * If a device follows the VPD format spec, the PCI core will not read or
+ * write past the VPD End Tag.  But some vendors do not follow the VPD
+ * format spec, so we can't tell how much data is safe to access.  Devices
+ * may behave unpredictably if we access too much.  Blacklist these devices
+ * so we don't touch VPD at all.
+ */
+static void quirk_blacklist_vpd(struct pci_dev *dev)
+{
+       if (dev->vpd) {
+               dev->vpd->len = 0;
+               dev_warn(&dev->dev, FW_BUG "VPD access disabled\n");
+       }
+}
+
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID,
+               quirk_blacklist_vpd);
+
 /*
  * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the
  * VPD end tag will hang the device.  This problem was initially
@@ -3832,6 +3861,19 @@ static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags)
 #endif
 }
 
+static int pci_quirk_cavium_acs(struct pci_dev *dev, u16 acs_flags)
+{
+       /*
+        * Cavium devices matching this quirk do not perform peer-to-peer
+        * with other functions, allowing masking out these bits as if they
+        * were unimplemented in the ACS capability.
+        */
+       acs_flags &= ~(PCI_ACS_SV | PCI_ACS_TB | PCI_ACS_RR |
+                      PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_DT);
+
+       return acs_flags ? 0 : 1;
+}
+
 /*
  * Many Intel PCH root ports do provide ACS-like features to disable peer
  * transactions and validate bus numbers in requests, but do not provide an
@@ -3984,6 +4026,8 @@ static const struct pci_dev_acs_enabled {
        { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
        { 0x19a2, 0x710, pci_quirk_mf_endpoint_acs }, /* Emulex BE3-R */
        { 0x10df, 0x720, pci_quirk_mf_endpoint_acs }, /* Emulex Skyhawk-R */
+       /* Cavium ThunderX */
+       { PCI_VENDOR_ID_CAVIUM, PCI_ANY_ID, pci_quirk_cavium_acs },
        { 0 }
 };
 
index 8a280e9..8982026 100644 (file)
@@ -7,7 +7,6 @@ static void pci_free_resources(struct pci_dev *dev)
 {
        int i;
 
-       pci_cleanup_rom(dev);
        for (i = 0; i < PCI_NUM_RESOURCES; i++) {
                struct resource *res = dev->resource + i;
                if (res->parent)
@@ -54,6 +53,10 @@ void pci_remove_bus(struct pci_bus *bus)
        pci_bus_release_busn_res(bus);
        up_write(&pci_bus_sem);
        pci_remove_legacy_files(bus);
+
+       if (bus->ops->remove_bus)
+               bus->ops->remove_bus(bus);
+
        pcibios_remove_bus(bus);
        device_unregister(&bus->dev);
 }
index 9eaca39..06663d3 100644 (file)
  */
 int pci_enable_rom(struct pci_dev *pdev)
 {
-       struct resource *res = pdev->resource + PCI_ROM_RESOURCE;
+       struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
        struct pci_bus_region region;
        u32 rom_addr;
 
        if (!res->flags)
                return -1;
 
+       /* Nothing to enable if we're using a shadow copy in RAM */
+       if (res->flags & IORESOURCE_ROM_SHADOW)
+               return 0;
+
        pcibios_resource_to_bus(pdev->bus, &region, res);
        pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
        rom_addr &= ~PCI_ROM_ADDRESS_MASK;
@@ -49,7 +53,12 @@ EXPORT_SYMBOL_GPL(pci_enable_rom);
  */
 void pci_disable_rom(struct pci_dev *pdev)
 {
+       struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
        u32 rom_addr;
+
+       if (res->flags & IORESOURCE_ROM_SHADOW)
+               return;
+
        pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
        rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
        pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
@@ -119,43 +128,23 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
        loff_t start;
        void __iomem *rom;
 
-       /*
-        * IORESOURCE_ROM_SHADOW set on x86, x86_64 and IA64 supports legacy
-        * memory map if the VGA enable bit of the Bridge Control register is
-        * set for embedded VGA.
-        */
-       if (res->flags & IORESOURCE_ROM_SHADOW) {
-               /* primary video rom always starts here */
-               start = (loff_t)0xC0000;
-               *size = 0x20000; /* cover C000:0 through E000:0 */
-       } else {
-               if (res->flags &
-                       (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY)) {
-                       *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
-                       return (void __iomem *)(unsigned long)
-                               pci_resource_start(pdev, PCI_ROM_RESOURCE);
-               } else {
-                       /* assign the ROM an address if it doesn't have one */
-                       if (res->parent == NULL &&
-                           pci_assign_resource(pdev, PCI_ROM_RESOURCE))
-                               return NULL;
-                       start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
-                       *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
-                       if (*size == 0)
-                               return NULL;
-
-                       /* Enable ROM space decodes */
-                       if (pci_enable_rom(pdev))
-                               return NULL;
-               }
-       }
+       /* assign the ROM an address if it doesn't have one */
+       if (res->parent == NULL && pci_assign_resource(pdev, PCI_ROM_RESOURCE))
+               return NULL;
+
+       start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+       *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+       if (*size == 0)
+               return NULL;
+
+       /* Enable ROM space decodes */
+       if (pci_enable_rom(pdev))
+               return NULL;
 
        rom = ioremap(start, *size);
        if (!rom) {
                /* restore enable if ioremap fails */
-               if (!(res->flags & (IORESOURCE_ROM_ENABLE |
-                                   IORESOURCE_ROM_SHADOW |
-                                   IORESOURCE_ROM_COPY)))
+               if (!(res->flags & IORESOURCE_ROM_ENABLE))
                        pci_disable_rom(pdev);
                return NULL;
        }
@@ -181,36 +170,14 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
 {
        struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
 
-       if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY))
-               return;
-
        iounmap(rom);
 
-       /* Disable again before continuing, leave enabled if pci=rom */
-       if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+       /* Disable again before continuing */
+       if (!(res->flags & IORESOURCE_ROM_ENABLE))
                pci_disable_rom(pdev);
 }
 EXPORT_SYMBOL(pci_unmap_rom);
 
-/**
- * pci_cleanup_rom - free the ROM copy created by pci_map_rom_copy
- * @pdev: pointer to pci device struct
- *
- * Free the copied ROM if we allocated one.
- */
-void pci_cleanup_rom(struct pci_dev *pdev)
-{
-       struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
-
-       if (res->flags & IORESOURCE_ROM_COPY) {
-               kfree((void *)(unsigned long)res->start);
-               res->flags |= IORESOURCE_UNSET;
-               res->flags &= ~IORESOURCE_ROM_COPY;
-               res->start = 0;
-               res->end = 0;
-       }
-}
-
 /**
  * pci_platform_rom - provides a pointer to any ROM image provided by the
  * platform
index 7796d0a..55641a3 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/ioport.h>
 #include <linux/cache.h>
 #include <linux/slab.h>
-#include <asm-generic/pci-bridge.h>
 #include "pci.h"
 
 unsigned int pci_flags;
index 604011e..66c4d8f 100644 (file)
@@ -276,6 +276,9 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
        resource_size_t align, size;
        int ret;
 
+       if (res->flags & IORESOURCE_PCI_FIXED)
+               return 0;
+
        res->flags |= IORESOURCE_UNSET;
        align = pci_resource_alignment(dev, res);
        if (!align) {
@@ -321,6 +324,9 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz
        resource_size_t new_size;
        int ret;
 
+       if (res->flags & IORESOURCE_PCI_FIXED)
+               return 0;
+
        flags = res->flags;
        res->flags |= IORESOURCE_UNSET;
        if (!res->parent) {
index 1412266..a6682c5 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/pci.h>
 #include <asm/dbdma.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
 #include <asm/prom.h>
-#include <asm/pci-bridge.h>
 #include <asm/macio.h>
 
 #include <scsi/scsi.h>
index 555367f..1753e42 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/interrupt.h>
 #include <linux/reboot.h>
 #include <linux/spinlock.h>
+#include <linux/pci.h>
 #include <asm/dbdma.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
@@ -38,7 +39,6 @@
 #include <asm/processor.h>
 #include <asm/machdep.h>
 #include <asm/pmac_feature.h>
-#include <asm/pci-bridge.h>
 #include <asm/macio.h>
 
 #include <scsi/scsi.h>
index 9eb1cff..c3640f8 100644 (file)
@@ -28,7 +28,6 @@
 #ifdef CONFIG_PPC_PMAC
 #include <asm/machdep.h>
 #include <asm/pmac_feature.h>
-#include <asm/pci-bridge.h>
 #include <asm/prom.h>
 #endif
 
index cd87641..39d70b4 100644 (file)
@@ -3397,7 +3397,7 @@ err_pcidev:
 static const struct pci_device_id pci_id[] = {
        {
                PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096),
-               .class =        (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
+               .class =        PCI_CLASS_SERIAL_USB_DEVICE,
                .class_mask =   0xffffffff,
        },
        {},
index 1fdfec1..d2205d9 100644 (file)
@@ -1846,7 +1846,7 @@ err:
 /*-------------------------------------------------------------------------*/
 
 static const struct pci_device_id pci_ids[] = { {
-       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+       .class =        PCI_CLASS_SERIAL_USB_DEVICE,
        .class_mask =   ~0,
        .vendor =       0x102f,         /* Toshiba */
        .device =       0x0107,         /* this UDC */
index 6706aef..c894b94 100644 (file)
@@ -3735,7 +3735,7 @@ static void net2280_shutdown(struct pci_dev *pdev)
 /*-------------------------------------------------------------------------*/
 
 static const struct pci_device_id pci_ids[] = { {
-       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+       .class =        PCI_CLASS_SERIAL_USB_DEVICE,
        .class_mask =   ~0,
        .vendor =       PCI_VENDOR_ID_PLX_LEGACY,
        .device =       0x2280,
@@ -3743,7 +3743,7 @@ static const struct pci_device_id pci_ids[] = { {
        .subdevice =    PCI_ANY_ID,
        .driver_data =  PLX_LEGACY | PLX_2280,
        }, {
-       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+       .class =        PCI_CLASS_SERIAL_USB_DEVICE,
        .class_mask =   ~0,
        .vendor =       PCI_VENDOR_ID_PLX_LEGACY,
        .device =       0x2282,
@@ -3752,7 +3752,7 @@ static const struct pci_device_id pci_ids[] = { {
        .driver_data =  PLX_LEGACY,
        },
        {
-       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+       .class =        PCI_CLASS_SERIAL_USB_DEVICE,
        .class_mask =   ~0,
        .vendor =       PCI_VENDOR_ID_PLX,
        .device =       0x3380,
@@ -3761,7 +3761,7 @@ static const struct pci_device_id pci_ids[] = { {
        .driver_data =  PLX_SUPERSPEED,
         },
        {
-       .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+       .class =        PCI_CLASS_SERIAL_USB_DEVICE,
        .class_mask =   ~0,
        .vendor =       PCI_VENDOR_ID_PLX,
        .device =       0x3382,
index 7a04157..9571ef5 100644 (file)
@@ -3234,22 +3234,22 @@ static const struct pci_device_id pch_udc_pcidev_id[] = {
        {
                PCI_DEVICE(PCI_VENDOR_ID_INTEL,
                           PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC),
-               .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
+               .class = PCI_CLASS_SERIAL_USB_DEVICE,
                .class_mask = 0xffffffff,
        },
        {
                PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC),
-               .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
+               .class = PCI_CLASS_SERIAL_USB_DEVICE,
                .class_mask = 0xffffffff,
        },
        {
                PCI_DEVICE(PCI_VENDOR_ID_ROHM, PCI_DEVICE_ID_ML7213_IOH_UDC),
-               .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
+               .class = PCI_CLASS_SERIAL_USB_DEVICE,
                .class_mask = 0xffffffff,
        },
        {
                PCI_DEVICE(PCI_VENDOR_ID_ROHM, PCI_DEVICE_ID_ML7831_IOH_UDC),
-               .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
+               .class = PCI_CLASS_SERIAL_USB_DEVICE,
                .class_mask = 0xffffffff,
        },
        { 0 },
index c42ce2f..0a46268 100644 (file)
@@ -68,7 +68,6 @@
 #include <asm/machdep.h>
 #include <asm/pmac_feature.h>
 #include <asm/prom.h>
-#include <asm/pci-bridge.h>
 #include "../macmodes.h"
 #endif
 
index ce0b1d0..218339a 100644 (file)
@@ -76,7 +76,6 @@
 
 #ifdef CONFIG_PPC
 
-#include <asm/pci-bridge.h>
 #include "../macmodes.h"
 
 #ifdef CONFIG_BOOTX_TEXT
index 9b167f7..4363c64 100644 (file)
@@ -33,7 +33,6 @@
 #if defined(CONFIG_PPC)
 #include <linux/nvram.h>
 #include <asm/prom.h>
-#include <asm/pci-bridge.h>
 #include "macmodes.h"
 #endif
 
index 09b02cd..7a90ea2 100644 (file)
@@ -47,7 +47,6 @@
 
 #if defined(CONFIG_PPC_PMAC)
 #include <asm/prom.h>
-#include <asm/pci-bridge.h>
 #include "../macmodes.h"
 #endif
 
index 43a0a52..fb60a8f 100644 (file)
 #include <linux/pci.h>
 #include <asm/io.h>
 
-#ifdef CONFIG_PPC64
-#include <asm/pci-bridge.h>
-#endif
-
 #ifdef CONFIG_PPC32
 #include <asm/bootx.h>
 #endif
index f6bed86..d9a9058 100644 (file)
@@ -467,7 +467,7 @@ static const struct dev_pm_ops virtio_pci_pm_ops = {
 
 /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
 static const struct pci_device_id virtio_pci_id_table[] = {
-       { PCI_DEVICE(0x1af4, PCI_ANY_ID) },
+       { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_ANY_ID) },
        { 0 }
 };
 
diff --git a/include/asm-generic/pci-bridge.h b/include/asm-generic/pci-bridge.h
deleted file mode 100644 (file)
index 20db2e5..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-#ifndef _ASM_GENERIC_PCI_BRIDGE_H
-#define _ASM_GENERIC_PCI_BRIDGE_H
-
-#ifdef __KERNEL__
-
-enum {
-       /* Force re-assigning all resources (ignore firmware
-        * setup completely)
-        */
-       PCI_REASSIGN_ALL_RSRC   = 0x00000001,
-
-       /* Re-assign all bus numbers */
-       PCI_REASSIGN_ALL_BUS    = 0x00000002,
-
-       /* Do not try to assign, just use existing setup */
-       PCI_PROBE_ONLY          = 0x00000004,
-
-       /* Don't bother with ISA alignment unless the bridge has
-        * ISA forwarding enabled
-        */
-       PCI_CAN_SKIP_ISA_ALIGN  = 0x00000008,
-
-       /* Enable domain numbers in /proc */
-       PCI_ENABLE_PROC_DOMAINS = 0x00000010,
-       /* ... except for domain 0 */
-       PCI_COMPAT_DOMAIN_0     = 0x00000020,
-
-       /* PCIe downstream ports are bridges that normally lead to only a
-        * device 0, but if this is set, we scan all possible devices, not
-        * just device 0.
-        */
-       PCI_SCAN_ALL_PCIE_DEVS  = 0x00000040,
-};
-
-#ifdef CONFIG_PCI
-extern unsigned int pci_flags;
-
-static inline void pci_set_flags(int flags)
-{
-       pci_flags = flags;
-}
-
-static inline void pci_add_flags(int flags)
-{
-       pci_flags |= flags;
-}
-
-static inline void pci_clear_flags(int flags)
-{
-       pci_flags &= ~flags;
-}
-
-static inline int pci_has_flag(int flag)
-{
-       return pci_flags & flag;
-}
-#else
-static inline void pci_set_flags(int flags) { }
-static inline void pci_add_flags(int flags) { }
-static inline void pci_clear_flags(int flags) { }
-static inline int pci_has_flag(int flag)
-{
-       return 0;
-}
-#endif /* CONFIG_PCI */
-
-#endif /* __KERNEL__ */
-#endif /* _ASM_GENERIC_PCI_BRIDGE_H */
diff --git a/include/asm-generic/pci-dma-compat.h b/include/asm-generic/pci-dma-compat.h
deleted file mode 100644 (file)
index eafce7b..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/* include this file if the platform implements the dma_ DMA Mapping API
- * and wants to provide the pci_ DMA Mapping API in terms of it */
-
-#ifndef _ASM_GENERIC_PCI_DMA_COMPAT_H
-#define _ASM_GENERIC_PCI_DMA_COMPAT_H
-
-#include <linux/dma-mapping.h>
-
-static inline void *
-pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
-                    dma_addr_t *dma_handle)
-{
-       return dma_alloc_coherent(hwdev == NULL ? NULL : &hwdev->dev, size, dma_handle, GFP_ATOMIC);
-}
-
-static inline void *
-pci_zalloc_consistent(struct pci_dev *hwdev, size_t size,
-                     dma_addr_t *dma_handle)
-{
-       return dma_zalloc_coherent(hwdev == NULL ? NULL : &hwdev->dev,
-                                  size, dma_handle, GFP_ATOMIC);
-}
-
-static inline void
-pci_free_consistent(struct pci_dev *hwdev, size_t size,
-                   void *vaddr, dma_addr_t dma_handle)
-{
-       dma_free_coherent(hwdev == NULL ? NULL : &hwdev->dev, size, vaddr, dma_handle);
-}
-
-static inline dma_addr_t
-pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction)
-{
-       return dma_map_single(hwdev == NULL ? NULL : &hwdev->dev, ptr, size, (enum dma_data_direction)direction);
-}
-
-static inline void
-pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
-                size_t size, int direction)
-{
-       dma_unmap_single(hwdev == NULL ? NULL : &hwdev->dev, dma_addr, size, (enum dma_data_direction)direction);
-}
-
-static inline dma_addr_t
-pci_map_page(struct pci_dev *hwdev, struct page *page,
-            unsigned long offset, size_t size, int direction)
-{
-       return dma_map_page(hwdev == NULL ? NULL : &hwdev->dev, page, offset, size, (enum dma_data_direction)direction);
-}
-
-static inline void
-pci_unmap_page(struct pci_dev *hwdev, dma_addr_t dma_address,
-              size_t size, int direction)
-{
-       dma_unmap_page(hwdev == NULL ? NULL : &hwdev->dev, dma_address, size, (enum dma_data_direction)direction);
-}
-
-static inline int
-pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg,
-          int nents, int direction)
-{
-       return dma_map_sg(hwdev == NULL ? NULL : &hwdev->dev, sg, nents, (enum dma_data_direction)direction);
-}
-
-static inline void
-pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg,
-            int nents, int direction)
-{
-       dma_unmap_sg(hwdev == NULL ? NULL : &hwdev->dev, sg, nents, (enum dma_data_direction)direction);
-}
-
-static inline void
-pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, dma_addr_t dma_handle,
-                   size_t size, int direction)
-{
-       dma_sync_single_for_cpu(hwdev == NULL ? NULL : &hwdev->dev, dma_handle, size, (enum dma_data_direction)direction);
-}
-
-static inline void
-pci_dma_sync_single_for_device(struct pci_dev *hwdev, dma_addr_t dma_handle,
-                   size_t size, int direction)
-{
-       dma_sync_single_for_device(hwdev == NULL ? NULL : &hwdev->dev, dma_handle, size, (enum dma_data_direction)direction);
-}
-
-static inline void
-pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, struct scatterlist *sg,
-               int nelems, int direction)
-{
-       dma_sync_sg_for_cpu(hwdev == NULL ? NULL : &hwdev->dev, sg, nelems, (enum dma_data_direction)direction);
-}
-
-static inline void
-pci_dma_sync_sg_for_device(struct pci_dev *hwdev, struct scatterlist *sg,
-               int nelems, int direction)
-{
-       dma_sync_sg_for_device(hwdev == NULL ? NULL : &hwdev->dev, sg, nelems, (enum dma_data_direction)direction);
-}
-
-static inline int
-pci_dma_mapping_error(struct pci_dev *pdev, dma_addr_t dma_addr)
-{
-       return dma_mapping_error(&pdev->dev, dma_addr);
-}
-
-#ifdef CONFIG_PCI
-static inline int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
-{
-       return dma_set_mask(&dev->dev, mask);
-}
-
-static inline int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
-{
-       return dma_set_coherent_mask(&dev->dev, mask);
-}
-#endif
-
-#endif
index afb4559..212a8e6 100644 (file)
@@ -106,9 +106,7 @@ struct resource {
 
 /* PCI ROM control bits (IORESOURCE_BITS) */
 #define IORESOURCE_ROM_ENABLE          (1<<0)  /* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
-#define IORESOURCE_ROM_SHADOW          (1<<1)  /* ROM is copy at C000:0 */
-#define IORESOURCE_ROM_COPY            (1<<2)  /* ROM is alloc'd copy, resource field overlaid */
-#define IORESOURCE_ROM_BIOS_COPY       (1<<3)  /* ROM is BIOS copy, resource field overlaid */
+#define IORESOURCE_ROM_SHADOW          (1<<1)  /* Use RAM image, not ROM BAR */
 
 /* PCI control bits.  Shares IORESOURCE_BITS with above PCI ROM.  */
 #define IORESOURCE_PCI_FIXED           (1<<4)  /* Do not move resource */
diff --git a/include/linux/pci-dma-compat.h b/include/linux/pci-dma-compat.h
new file mode 100644 (file)
index 0000000..39726ca
--- /dev/null
@@ -0,0 +1,147 @@
+/* include this file if the platform implements the dma_ DMA Mapping API
+ * and wants to provide the pci_ DMA Mapping API in terms of it */
+
+#ifndef _ASM_GENERIC_PCI_DMA_COMPAT_H
+#define _ASM_GENERIC_PCI_DMA_COMPAT_H
+
+#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
+
+static inline void *
+pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
+                    dma_addr_t *dma_handle)
+{
+       return dma_alloc_coherent(hwdev == NULL ? NULL : &hwdev->dev, size, dma_handle, GFP_ATOMIC);
+}
+
+static inline void *
+pci_zalloc_consistent(struct pci_dev *hwdev, size_t size,
+                     dma_addr_t *dma_handle)
+{
+       return dma_zalloc_coherent(hwdev == NULL ? NULL : &hwdev->dev,
+                                  size, dma_handle, GFP_ATOMIC);
+}
+
+static inline void
+pci_free_consistent(struct pci_dev *hwdev, size_t size,
+                   void *vaddr, dma_addr_t dma_handle)
+{
+       dma_free_coherent(hwdev == NULL ? NULL : &hwdev->dev, size, vaddr, dma_handle);
+}
+
+static inline dma_addr_t
+pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction)
+{
+       return dma_map_single(hwdev == NULL ? NULL : &hwdev->dev, ptr, size, (enum dma_data_direction)direction);
+}
+
+static inline void
+pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
+                size_t size, int direction)
+{
+       dma_unmap_single(hwdev == NULL ? NULL : &hwdev->dev, dma_addr, size, (enum dma_data_direction)direction);
+}
+
+static inline dma_addr_t
+pci_map_page(struct pci_dev *hwdev, struct page *page,
+            unsigned long offset, size_t size, int direction)
+{
+       return dma_map_page(hwdev == NULL ? NULL : &hwdev->dev, page, offset, size, (enum dma_data_direction)direction);
+}
+
+static inline void
+pci_unmap_page(struct pci_dev *hwdev, dma_addr_t dma_address,
+              size_t size, int direction)
+{
+       dma_unmap_page(hwdev == NULL ? NULL : &hwdev->dev, dma_address, size, (enum dma_data_direction)direction);
+}
+
+static inline int
+pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg,
+          int nents, int direction)
+{
+       return dma_map_sg(hwdev == NULL ? NULL : &hwdev->dev, sg, nents, (enum dma_data_direction)direction);
+}
+
+static inline void
+pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg,
+            int nents, int direction)
+{
+       dma_unmap_sg(hwdev == NULL ? NULL : &hwdev->dev, sg, nents, (enum dma_data_direction)direction);
+}
+
+static inline void
+pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, dma_addr_t dma_handle,
+                   size_t size, int direction)
+{
+       dma_sync_single_for_cpu(hwdev == NULL ? NULL : &hwdev->dev, dma_handle, size, (enum dma_data_direction)direction);
+}
+
+static inline void
+pci_dma_sync_single_for_device(struct pci_dev *hwdev, dma_addr_t dma_handle,
+                   size_t size, int direction)
+{
+       dma_sync_single_for_device(hwdev == NULL ? NULL : &hwdev->dev, dma_handle, size, (enum dma_data_direction)direction);
+}
+
+static inline void
+pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, struct scatterlist *sg,
+               int nelems, int direction)
+{
+       dma_sync_sg_for_cpu(hwdev == NULL ? NULL : &hwdev->dev, sg, nelems, (enum dma_data_direction)direction);
+}
+
+static inline void
+pci_dma_sync_sg_for_device(struct pci_dev *hwdev, struct scatterlist *sg,
+               int nelems, int direction)
+{
+       dma_sync_sg_for_device(hwdev == NULL ? NULL : &hwdev->dev, sg, nelems, (enum dma_data_direction)direction);
+}
+
+static inline int
+pci_dma_mapping_error(struct pci_dev *pdev, dma_addr_t dma_addr)
+{
+       return dma_mapping_error(&pdev->dev, dma_addr);
+}
+
+#ifdef CONFIG_PCI
+static inline int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
+{
+       return dma_set_mask(&dev->dev, mask);
+}
+
+static inline int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
+{
+       return dma_set_coherent_mask(&dev->dev, mask);
+}
+
+static inline int pci_set_dma_max_seg_size(struct pci_dev *dev,
+                                          unsigned int size)
+{
+       return dma_set_max_seg_size(&dev->dev, size);
+}
+
+static inline int pci_set_dma_seg_boundary(struct pci_dev *dev,
+                                          unsigned long mask)
+{
+       return dma_set_seg_boundary(&dev->dev, mask);
+}
+#else
+static inline int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
+{ return -EIO; }
+static inline int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
+{ return -EIO; }
+static inline int pci_set_dma_max_seg_size(struct pci_dev *dev,
+                                          unsigned int size)
+{ return -EIO; }
+static inline int pci_set_dma_seg_boundary(struct pci_dev *dev,
+                                          unsigned long mask)
+{ return -EIO; }
+#endif
+
+#endif
index 2771625..17e17c2 100644 (file)
@@ -70,12 +70,6 @@ enum pci_mmap_state {
        pci_mmap_mem
 };
 
-/* 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
-
 /*
  *  For PCI devices, the region numbers are assigned this way:
  */
@@ -359,6 +353,7 @@ struct pci_dev {
        unsigned int    io_window_1k:1; /* Intel P2P bridge 1K I/O windows */
        unsigned int    irq_managed:1;
        unsigned int    has_secondary_link:1;
+       unsigned int    non_compliant_bars:1;   /* broken BARs; ignore them */
        pci_dev_flags_t dev_flags;
        atomic_t        enable_cnt;     /* pci_enable_device has been called */
 
@@ -578,6 +573,8 @@ static inline int pcibios_err_to_errno(int err)
 /* Low-level architecture-dependent routines */
 
 struct pci_ops {
+       int (*add_bus)(struct pci_bus *bus);
+       void (*remove_bus)(struct pci_bus *bus);
        void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where);
        int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);
        int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val);
@@ -746,9 +743,26 @@ struct pci_driver {
        .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \
        .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0
 
+enum {
+       PCI_REASSIGN_ALL_RSRC   = 0x00000001,   /* ignore firmware setup */
+       PCI_REASSIGN_ALL_BUS    = 0x00000002,   /* reassign all bus numbers */
+       PCI_PROBE_ONLY          = 0x00000004,   /* use existing setup */
+       PCI_CAN_SKIP_ISA_ALIGN  = 0x00000008,   /* don't do ISA alignment */
+       PCI_ENABLE_PROC_DOMAINS = 0x00000010,   /* enable domains in /proc */
+       PCI_COMPAT_DOMAIN_0     = 0x00000020,   /* ... except domain 0 */
+       PCI_SCAN_ALL_PCIE_DEVS  = 0x00000040,   /* scan all, not just dev 0 */
+};
+
 /* these external functions are only available when PCI support is enabled */
 #ifdef CONFIG_PCI
 
+extern unsigned int pci_flags;
+
+static inline void pci_set_flags(int flags) { pci_flags = flags; }
+static inline void pci_add_flags(int flags) { pci_flags |= flags; }
+static inline void pci_clear_flags(int flags) { pci_flags &= ~flags; }
+static inline int pci_has_flag(int flag) { return pci_flags & flag; }
+
 void pcie_bus_configure_settings(struct pci_bus *bus);
 
 enum pcie_bus_config_types {
@@ -1004,8 +1018,6 @@ void pci_intx(struct pci_dev *dev, int enable);
 bool pci_intx_mask_supported(struct pci_dev *dev);
 bool pci_check_and_mask_intx(struct pci_dev *dev);
 bool pci_check_and_unmask_intx(struct pci_dev *dev);
-int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size);
-int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask);
 int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask);
 int pci_wait_for_pending_transaction(struct pci_dev *dev);
 int pcix_get_max_mmrbc(struct pci_dev *dev);
@@ -1221,6 +1233,7 @@ resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno);
 
 int pci_set_vga_state(struct pci_dev *pdev, bool decode,
                      unsigned int command_bits, u32 flags);
+
 /* kmem_cache style wrapper around pci_alloc_consistent() */
 
 #include <linux/pci-dma.h>
@@ -1388,6 +1401,11 @@ void pci_register_set_vga_state(arch_set_vga_state_t func);
 
 #else /* CONFIG_PCI is not enabled */
 
+static inline void pci_set_flags(int flags) { }
+static inline void pci_add_flags(int flags) { }
+static inline void pci_clear_flags(int flags) { }
+static inline int pci_has_flag(int flag) { return 0; }
+
 /*
  *  If the system does not have PCI, clearly these return errors.  Define
  *  these as simple inline functions to avoid hair in drivers.
@@ -1427,16 +1445,6 @@ static inline struct pci_dev *pci_get_class(unsigned int class,
 static inline void pci_set_master(struct pci_dev *dev) { }
 static inline int pci_enable_device(struct pci_dev *dev) { return -EIO; }
 static inline void pci_disable_device(struct pci_dev *dev) { }
-static inline int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
-{ return -EIO; }
-static inline int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
-{ return -EIO; }
-static inline int pci_set_dma_max_seg_size(struct pci_dev *dev,
-                                       unsigned int size)
-{ return -EIO; }
-static inline int pci_set_dma_seg_boundary(struct pci_dev *dev,
-                                       unsigned long mask)
-{ return -EIO; }
 static inline int pci_assign_resource(struct pci_dev *dev, int i)
 { return -EBUSY; }
 static inline int __pci_register_driver(struct pci_driver *drv,
@@ -1498,6 +1506,10 @@ static inline int pci_get_new_domain_nr(void) { return -ENOSYS; }
 
 #include <asm/pci.h>
 
+#ifndef pci_root_bus_fwnode
+#define pci_root_bus_fwnode(bus)       NULL
+#endif
+
 /* these helpers provide future and backwards compatibility
  * for accessing popular PCI BAR info */
 #define pci_resource_start(dev, bar)   ((dev)->resource[(bar)].start)
@@ -1817,12 +1829,13 @@ bool pci_acs_path_enabled(struct pci_dev *start,
 #define PCI_VPD_LRDT_RW_DATA           PCI_VPD_LRDT_ID(PCI_VPD_LTIN_RW_DATA)
 
 /* Small Resource Data Type Tag Item Names */
-#define PCI_VPD_STIN_END               0x78    /* End */
+#define PCI_VPD_STIN_END               0x0f    /* End */
 
-#define PCI_VPD_SRDT_END               PCI_VPD_STIN_END
+#define PCI_VPD_SRDT_END               (PCI_VPD_STIN_END << 3)
 
 #define PCI_VPD_SRDT_TIN_MASK          0x78
 #define PCI_VPD_SRDT_LEN_MASK          0x07
+#define PCI_VPD_LRDT_TIN_MASK          0x7f
 
 #define PCI_VPD_LRDT_TAG_SIZE          3
 #define PCI_VPD_SRDT_TAG_SIZE          1
@@ -1845,6 +1858,17 @@ static inline u16 pci_vpd_lrdt_size(const u8 *lrdt)
        return (u16)lrdt[1] + ((u16)lrdt[2] << 8);
 }
 
+/**
+ * pci_vpd_lrdt_tag - Extracts the Large Resource Data Type Tag Item
+ * @lrdt: Pointer to the beginning of the Large Resource Data Type tag
+ *
+ * Returns the extracted Large Resource Data Type Tag item.
+ */
+static inline u16 pci_vpd_lrdt_tag(const u8 *lrdt)
+{
+    return (u16)(lrdt[0] & PCI_VPD_LRDT_TIN_MASK);
+}
+
 /**
  * pci_vpd_srdt_size - Extracts the Small Resource Data Type length
  * @lrdt: Pointer to the beginning of the Small Resource Data Type tag
@@ -1856,6 +1880,17 @@ static inline u8 pci_vpd_srdt_size(const u8 *srdt)
        return (*srdt) & PCI_VPD_SRDT_LEN_MASK;
 }
 
+/**
+ * pci_vpd_srdt_tag - Extracts the Small Resource Data Type Tag Item
+ * @lrdt: Pointer to the beginning of the Small Resource Data Type tag
+ *
+ * Returns the extracted Small Resource Data Type Tag Item.
+ */
+static inline u8 pci_vpd_srdt_tag(const u8 *srdt)
+{
+       return ((*srdt) & PCI_VPD_SRDT_TIN_MASK) >> 3;
+}
+
 /**
  * pci_vpd_info_field_size - Extracts the information field length
  * @lrdt: Pointer to the beginning of an information field header
@@ -1972,4 +2007,8 @@ static inline bool pci_ari_enabled(struct pci_bus *bus)
 {
        return bus->self && bus->self->ari_enabled;
 }
+
+/* provide the legacy pci_dma_* API */
+#include <linux/pci-dma-compat.h>
+
 #endif /* LINUX_PCI_H */
index 37f05cb..247da8c 100644 (file)
 #define PCI_CLASS_SERIAL_USB_OHCI      0x0c0310
 #define PCI_CLASS_SERIAL_USB_EHCI      0x0c0320
 #define PCI_CLASS_SERIAL_USB_XHCI      0x0c0330
+#define PCI_CLASS_SERIAL_USB_DEVICE    0x0c03fe
 #define PCI_CLASS_SERIAL_FIBER         0x0c04
 #define PCI_CLASS_SERIAL_SMBUS         0x0c05
 
 
 #define PCI_VENDOR_ID_AZWAVE           0x1a3b
 
+#define PCI_VENDOR_ID_REDHAT_QUMRANET    0x1af4
+#define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4
+#define PCI_SUBDEVICE_ID_QEMU            0x1100
+
 #define PCI_VENDOR_ID_ASMEDIA          0x1b21
 
 #define PCI_VENDOR_ID_CIRCUITCO                0x1cc8
index 42bcbac..12c2c18 100644 (file)
@@ -2980,8 +2980,8 @@ static int snd_intel8x0_inside_vm(struct pci_dev *pci)
                goto fini;
 
        /* check for known (emulated) devices */
-       if (pci->subsystem_vendor == 0x1af4 &&
-           pci->subsystem_device == 0x1100) {
+       if (pci->subsystem_vendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET &&
+           pci->subsystem_device == PCI_SUBDEVICE_ID_QEMU) {
                /* KVM emulated sound, PCI SSID: 1af4:1100 */
                msg = "enable KVM";
        } else if (pci->subsystem_vendor == 0x1ab8) {
index 0095a80..a5843fc 100644 (file)
@@ -34,7 +34,6 @@
 #include "pmac.h"
 #include <sound/pcm_params.h>
 #include <asm/pmac_feature.h>
-#include <asm/pci-bridge.h>
 
 
 /* fixed frequency table for awacs, screamer, burgundy, DACA (44100 max) */