Merge tag 'pci-v4.6-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
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() ...
This commit is contained in:
@ -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>;
|
||||
};
|
||||
|
@ -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 {
|
||||
|
30
Documentation/devicetree/bindings/pci/pci-thunder-ecam.txt
Normal file
30
Documentation/devicetree/bindings/pci/pci-thunder-ecam.txt
Normal file
@ -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 */
|
||||
};
|
43
Documentation/devicetree/bindings/pci/pci-thunder-pem.txt
Normal file
43
Documentation/devicetree/bindings/pci/pci-thunder-pem.txt
Normal file
@ -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 */
|
||||
};
|
68
Documentation/devicetree/bindings/pci/xilinx-nwl-pcie.txt
Normal file
68
Documentation/devicetree/bindings/pci/xilinx-nwl-pcie.txt
Normal file
@ -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>;
|
||||
};
|
||||
|
||||
};
|
@ -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 = <µblaze_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>;
|
||||
};
|
||||
|
||||
};
|
||||
|
17
MAINTAINERS
17
MAINTAINERS
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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"
|
||||
|
@ -10,5 +10,10 @@
|
||||
#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
|
||||
|
@ -16,6 +16,15 @@
|
||||
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)
|
||||
|
28
arch/arc/include/asm/pci.h
Normal file
28
arch/arc/include/asm/pci.h
Normal file
@ -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 */
|
@ -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
|
||||
|
22
arch/arc/kernel/pcibios.c
Normal file
22
arch/arc/kernel/pcibios.c
Normal file
@ -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)
|
||||
{
|
||||
}
|
@ -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
|
||||
|
@ -1212,7 +1212,6 @@ config PCI_HOST_ITE8152
|
||||
select DMABOUNCE
|
||||
|
||||
source "drivers/pci/Kconfig"
|
||||
source "drivers/pci/pcie/Kconfig"
|
||||
|
||||
source "drivers/pcmcia/Kconfig"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -5,6 +5,4 @@
|
||||
|
||||
#define PCI_DMA_BUS_IS_PHYS (1)
|
||||
|
||||
#include <asm-generic/pci-dma-compat.h>
|
||||
|
||||
#endif /* __ASM_AVR32_PCI_H__ */
|
||||
|
@ -1233,8 +1233,6 @@ source "drivers/pci/Kconfig"
|
||||
|
||||
source "drivers/pcmcia/Kconfig"
|
||||
|
||||
source "drivers/pci/hotplug/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Executable file formats"
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -17,14 +17,14 @@
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -387,8 +387,6 @@ config ISA
|
||||
|
||||
source "drivers/pcmcia/Kconfig"
|
||||
|
||||
source "drivers/pci/hotplug/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -5,7 +5,4 @@
|
||||
#else
|
||||
#include <asm/pci_32.h>
|
||||
#endif
|
||||
|
||||
#include <asm-generic/pci-dma-compat.h>
|
||||
|
||||
#endif
|
||||
|
@ -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"
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -28,11 +28,6 @@
|
||||
#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__ */
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
},
|
||||
{
|
||||
|
@ -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,}
|
||||
|
@ -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 */
|
||||
|
@ -28,7 +28,6 @@
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
#include <asm/prom.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#endif
|
||||
|
||||
#define DRV_NAME "pdc202xx_new"
|
||||
|
@ -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>
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include <asm/macio.h>
|
||||
#include <asm/pmac_feature.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -110,8 +110,6 @@ config IOMMU_HELPER
|
||||
|
||||
source "drivers/pcmcia/Kconfig"
|
||||
|
||||
source "drivers/pci/hotplug/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
menu "PA-RISC specific drivers"
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
194
drivers/pci/host/pci-host-common.c
Normal file
194
drivers/pci/host/pci-host-common.c
Normal file
@ -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");
|
47
drivers/pci/host/pci-host-common.h
Normal file
47
drivers/pci/host/pci-host-common.h
Normal file
@ -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 */
|
@ -25,24 +25,7 @@
|
||||
#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 = {
|
||||
|
2346
drivers/pci/host/pci-hyperv.c
Normal file
2346
drivers/pci/host/pci-hyperv.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
/* check if the link is up or not */
|
||||
if (!dw_pcie_wait_for_link(pp))
|
||||
return 0;
|
||||
|
||||
for (retries = 0; retries < 200; retries++) {
|
||||
if (dw_pcie_link_up(pp))
|
||||
return 0;
|
||||
usleep_range(100, 1000);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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 (void __iomem *)bus->area->addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus,
|
||||
unsigned int devfn,
|
||||
int where)
|
||||
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_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";
|
||||
|
403
drivers/pci/host/pci-thunder-ecam.c
Normal file
403
drivers/pci/host/pci-thunder-ecam.c
Normal file
@ -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");
|
346
drivers/pci/host/pci-thunder-pem.c
Normal file
346
drivers/pci/host/pci-thunder-pem.c
Normal file
@ -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");
|
@ -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,
|
||||
|
138
drivers/pci/host/pcie-designware-plat.c
Normal file
138
drivers/pci/host/pcie-designware-plat.c
Normal file
@ -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");
|
@ -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"
|
||||
|
||||
@ -69,6 +70,11 @@
|
||||
#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;
|
||||
|
@ -22,6 +22,11 @@
|
||||
#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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
881
drivers/pci/host/pcie-xilinx-nwl.c
Normal file
881
drivers/pci/host/pcie-xilinx-nwl.c
Normal file
@ -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");
|
@ -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
|
||||
@ -105,7 +102,6 @@
|
||||
* @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;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user