forked from Minki/linux
PCI changes for the v4.6 merge window:
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) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJW6XgMAAoJEFmIoMA60/r8Yq4P/1nNwwZPikU+9Z8k0HyGPll6 vqXBOYj/wlbAxJTzH2weaoyUamFrwvsKaO3Vap3xHkAeTFPD/Dp0TipCCNMrZ82Z j1y83JJpenkRyX6ifLARCNYpOtvnvgzSrO9x7Sb2Xfqb64dPb7+jGAfOpGNzhKsO n1nj/L7RGx8Q6fNFGf8ANMXKTsdkdL+1pdwegjUXmD5WdOT+oW8DmqVbhyfSKwl0 E8r4Ml2lIg7Qd5Wu5iKMIBsR0+5HEyrwV7ch92wXChwKfoRwG70qnn7FGdc0y5ZB XvJuj8UD5UeMxEUeoRa9SwU6wWQT3Q9e6BzMS+P+43z36SPYjMfy/Xffv054z/bY rQomLjuGxNLESpmfNK5JfKxWoe2YNXjHQIDWMrAHyNlwdKJbYiwPcxnZJhvOa/eB p0QYcGS7O43STjibG9PZhzeq8tuSJRshxi0W6iB9QlqO8qs8nJQxIO+sZj/vl4yz lSnswWcV9062KITl8Fe9xDw244/RTz1xSVCdldlSoDhJyeMOjRvzS8raUMyyVmbA YULsI3l2iCl+fwDm/T21o7hJG966oYdAmgEv7lc7BWfgEAMg//LZXvMzVvrPFB2D R77u/0idtOciVJrmnO/x9DnQO2hzro9SLmVH6m0+0YU4wSSpZfGn98PCrtkatOAU c8zT9dJgyJVE3Z7cnPJ4 =otsF -----END PGP SIGNATURE----- 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:
commit
63e30271b0
@ -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
Loading…
Reference in New Issue
Block a user