PCI changes for the v4.4 merge window:
Resource management Add support for Enhanced Allocation devices (Sean O. Stalley) Add Enhanced Allocation register entries (Sean O. Stalley) Handle IORESOURCE_PCI_FIXED when sizing resources (David Daney) Handle IORESOURCE_PCI_FIXED when assigning resources (David Daney) Handle Enhanced Allocation capability for SR-IOV devices (David Daney) Clear IORESOURCE_UNSET when reverting to firmware-assigned address (Bjorn Helgaas) Make Enhanced Allocation bitmasks more obvious (Bjorn Helgaas) Expand Enhanced Allocation BAR output (Bjorn Helgaas) Add of_pci_check_probe_only to parse "linux,pci-probe-only" (Marc Zyngier) Fix lookup of linux,pci-probe-only property (Marc Zyngier) Add sparc mem64 resource parsing for root bus (Yinghai Lu) PCI device hotplug pciehp: Queue power work requests in dedicated function (Guenter Roeck) Driver binding Add builtin_pci_driver() to avoid registration boilerplate (Paul Gortmaker) Virtualization Set SR-IOV NumVFs to zero after enumeration (Alexander Duyck) Remove redundant validation of SR-IOV offset/stride registers (Alexander Duyck) Remove VFs in reverse order if virtfn_add() fails (Alexander Duyck) Reorder pcibios_sriov_disable() (Alexander Duyck) Wait 1 second between disabling VFs and clearing NumVFs (Alexander Duyck) Fix sriov_enable() error path for pcibios_enable_sriov() failures (Alexander Duyck) Enable SR-IOV ARI Capable Hierarchy before reading TotalVFs (Ben Shelton) Don't try to restore VF BARs (Wei Yang) MSI Don't alloc pcibios-irq when MSI is enabled (Joerg Roedel) Add msi_controller setup_irqs() method for special multivector setup (Lucas Stach) Export all remapped MSIs to sysfs attributes (Romain Bezut) Disable MSI on SiS 761 (Ondrej Zary) AER Clear error status registers during enumeration and restore (Taku Izumi) Generic host bridge driver Fix lookup of linux,pci-probe-only property (Marc Zyngier) Allow multiple hosts with different map_bus() methods (David Daney) Pass starting bus number to pci_scan_root_bus() (David Daney) Fix address window calculation for non-zero starting bus (David Daney) Altera host bridge driver Add msi.h to ARM Kbuild (Ley Foon Tan) Add Altera PCIe host controller driver (Ley Foon Tan) Add Altera PCIe MSI driver (Ley Foon Tan) APM X-Gene host bridge driver Remove msi_controller assignment (Duc Dang) Broadcom iProc host bridge driver Fix header comment "Corporation" misspelling (Florian Fainelli) Fix code comment to match code (Ray Jui) Remove unused struct iproc_pcie.irqs[] (Ray Jui) Call pci_fixup_irqs() for ARM64 as well as ARM (Ray Jui) Fix PCIe reset logic (Ray Jui) Improve link detection logic (Ray Jui) Update PCIe device tree bindings (Ray Jui) Add outbound mapping support (Ray Jui) Freescale i.MX6 host bridge driver Return real error code from imx6_add_pcie_port() (Fabio Estevam) Add PCIE_PHY_RX_ASIC_OUT_VALID definition (Fabio Estevam) Freescale Layerscape host bridge driver Remove ls_pcie_establish_link() (Minghuan Lian) Ignore PCIe controllers in Endpoint mode (Minghuan Lian) Factor out SCFG related function (Minghuan Lian) Update ls_add_pcie_port() (Minghuan Lian) Remove unused fields from struct ls_pcie (Minghuan Lian) Add support for LS1043a and LS2080a (Minghuan Lian) Add ls_pcie_msi_host_init() (Minghuan Lian) HiSilicon host bridge driver Add HiSilicon SoC Hip05 PCIe driver (Zhou Wang) Marvell MVEBU host bridge driver Return zero for reserved or unimplemented config space (Russell King) Use exact config access size; don't read/modify/write (Russell King) Use of_get_available_child_count() (Russell King) Use for_each_available_child_of_node() to walk child nodes (Russell King) Report full node name when reporting a DT error (Russell King) Use port->name rather than "PCIe%d.%d" (Russell King) Move port parsing and resource claiming to separate function (Russell King) Fix memory leaks and refcount leaks (Russell King) Split port parsing and resource claiming from port setup (Russell King) Use gpio_set_value_cansleep() (Russell King) Use devm_kcalloc() to allocate an array (Russell King) Use gpio_desc to carry around gpio (Russell King) Improve clock/reset handling (Russell King) Add PCI Express root complex capability block (Russell King) Remove code restricting accesses to slot 0 (Russell King) NVIDIA Tegra host bridge driver Wrap static pgprot_t initializer with __pgprot() (Ard Biesheuvel) Renesas R-Car host bridge driver Build pci-rcar-gen2.c only on ARM (Geert Uytterhoeven) Build pcie-rcar.c only on ARM (Geert Uytterhoeven) Make PCI aware of the I/O resources (Phil Edworthy) Remove dependency on ARM-specific struct hw_pci (Phil Edworthy) Set root bus nr to that provided in DT (Phil Edworthy) Fix I/O offset for multiple host bridges (Phil Edworthy) ST Microelectronics SPEAr13xx host bridge driver Fix dw_pcie_cfg_read/write() usage (Gabriele Paoloni) Synopsys DesignWare host bridge driver Make "clocks" and "clock-names" optional DT properties (Bhupesh Sharma) Use exact access size in dw_pcie_cfg_read() (Gabriele Paoloni) Simplify dw_pcie_cfg_read/write() interfaces (Gabriele Paoloni) Require config accesses to be naturally aligned (Gabriele Paoloni) Make "num-lanes" an optional DT property (Gabriele Paoloni) Move calculation of bus addresses to DRA7xx (Gabriele Paoloni) Replace ARM pci_sys_data->align_resource with global function pointer (Gabriele Paoloni) Factor out MSI msg setup (Lucas Stach) Implement multivector MSI IRQ setup (Lucas Stach) Make get_msi_addr() return phys_addr_t, not u32 (Lucas Stach) Set up high part of MSI target address (Lucas Stach) Fix PORT_LOGIC_LINK_WIDTH_MASK (Zhou Wang) Revert "PCI: designware: Program ATU with untranslated address" (Zhou Wang) Use of_pci_get_host_bridge_resources() to parse DT (Zhou Wang) Make driver arch-agnostic (Zhou Wang) Miscellaneous Make x86 pci_subsys_init() static (Alexander Kuleshov) Turn off Request Attributes to avoid Chelsio T5 Completion erratum (Hariprasad Shenai) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWPM9aAAoJEFmIoMA60/r89f8QALFMHegqKk5M08ZcCaG7unLF 5U5t88y6KF/kNYF6HOqLQiHh79U3ToU5HdrNaAtutr0UnvgbFC2WulLqKYgLiq6Y YJnwR3EfgGmdG7DKAVAXq19+nc2hgTPAEe8sciU7HKlTbqQmGj6//y3sQULGNLx3 zur0C33DCtrDgKDP7to273lkHO8Vl0YuLzyqwt0ePCMNcXR0h1dK8QxSTjuXBxaR c+T1V1Hw64MTxLz3xJd1/1ipy32u+9LnqqcdRz0zRq6qi48G9ch/i4Z6DHa8kTbj DUZrrTYKILQ2TcjcZSBmTueX11Z1Xa4/I/45sehIi6gVWL9qQbmGpt2E5YtFED+4 GdcmBSbWG/qsNsabXk38uiM3ww7+ltXEOhTXbcK+EgjvIhE6gSK/plYG0fU9pybs AKViEXVdHoT1X0N1dLK12mq7kvDCQvShHn08lz97Q9YrZ32wv1Fnij6WVSbJvfWt DubtPtisVM+rVy+VTpOImNR9wO54lTmG5jK53yNqH7I20K89y1kqARlN9nMXMB1a 2nQnwe9yWlsGj9gVNCn1KmyQSPOWjg+3Z+ekfwbxpca14s1AaN3Jm0N9Z61dXFoF y2ygoQtZ/z9BHr3quBpxXGt+aVUg2kcNw5GYeDYiALxXdJSObyzRrZ6HDb/zicU2 ZH9hBj0ctXvucmy6I2mt =uZrt -----END PGP SIGNATURE----- Merge tag 'pci-v4.4-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci Pull PCI updates from Bjorn Helgaas: "Resource management: - Add support for Enhanced Allocation devices (Sean O. Stalley) - Add Enhanced Allocation register entries (Sean O. Stalley) - Handle IORESOURCE_PCI_FIXED when sizing resources (David Daney) - Handle IORESOURCE_PCI_FIXED when assigning resources (David Daney) - Handle Enhanced Allocation capability for SR-IOV devices (David Daney) - Clear IORESOURCE_UNSET when reverting to firmware-assigned address (Bjorn Helgaas) - Make Enhanced Allocation bitmasks more obvious (Bjorn Helgaas) - Expand Enhanced Allocation BAR output (Bjorn Helgaas) - Add of_pci_check_probe_only to parse "linux,pci-probe-only" (Marc Zyngier) - Fix lookup of linux,pci-probe-only property (Marc Zyngier) - Add sparc mem64 resource parsing for root bus (Yinghai Lu) PCI device hotplug: - pciehp: Queue power work requests in dedicated function (Guenter Roeck) Driver binding: - Add builtin_pci_driver() to avoid registration boilerplate (Paul Gortmaker) Virtualization: - Set SR-IOV NumVFs to zero after enumeration (Alexander Duyck) - Remove redundant validation of SR-IOV offset/stride registers (Alexander Duyck) - Remove VFs in reverse order if virtfn_add() fails (Alexander Duyck) - Reorder pcibios_sriov_disable() (Alexander Duyck) - Wait 1 second between disabling VFs and clearing NumVFs (Alexander Duyck) - Fix sriov_enable() error path for pcibios_enable_sriov() failures (Alexander Duyck) - Enable SR-IOV ARI Capable Hierarchy before reading TotalVFs (Ben Shelton) - Don't try to restore VF BARs (Wei Yang) MSI: - Don't alloc pcibios-irq when MSI is enabled (Joerg Roedel) - Add msi_controller setup_irqs() method for special multivector setup (Lucas Stach) - Export all remapped MSIs to sysfs attributes (Romain Bezut) - Disable MSI on SiS 761 (Ondrej Zary) AER: - Clear error status registers during enumeration and restore (Taku Izumi) Generic host bridge driver: - Fix lookup of linux,pci-probe-only property (Marc Zyngier) - Allow multiple hosts with different map_bus() methods (David Daney) - Pass starting bus number to pci_scan_root_bus() (David Daney) - Fix address window calculation for non-zero starting bus (David Daney) Altera host bridge driver: - Add msi.h to ARM Kbuild (Ley Foon Tan) - Add Altera PCIe host controller driver (Ley Foon Tan) - Add Altera PCIe MSI driver (Ley Foon Tan) APM X-Gene host bridge driver: - Remove msi_controller assignment (Duc Dang) Broadcom iProc host bridge driver: - Fix header comment "Corporation" misspelling (Florian Fainelli) - Fix code comment to match code (Ray Jui) - Remove unused struct iproc_pcie.irqs[] (Ray Jui) - Call pci_fixup_irqs() for ARM64 as well as ARM (Ray Jui) - Fix PCIe reset logic (Ray Jui) - Improve link detection logic (Ray Jui) - Update PCIe device tree bindings (Ray Jui) - Add outbound mapping support (Ray Jui) Freescale i.MX6 host bridge driver: - Return real error code from imx6_add_pcie_port() (Fabio Estevam) - Add PCIE_PHY_RX_ASIC_OUT_VALID definition (Fabio Estevam) Freescale Layerscape host bridge driver: - Remove ls_pcie_establish_link() (Minghuan Lian) - Ignore PCIe controllers in Endpoint mode (Minghuan Lian) - Factor out SCFG related function (Minghuan Lian) - Update ls_add_pcie_port() (Minghuan Lian) - Remove unused fields from struct ls_pcie (Minghuan Lian) - Add support for LS1043a and LS2080a (Minghuan Lian) - Add ls_pcie_msi_host_init() (Minghuan Lian) HiSilicon host bridge driver: - Add HiSilicon SoC Hip05 PCIe driver (Zhou Wang) Marvell MVEBU host bridge driver: - Return zero for reserved or unimplemented config space (Russell King) - Use exact config access size; don't read/modify/write (Russell King) - Use of_get_available_child_count() (Russell King) - Use for_each_available_child_of_node() to walk child nodes (Russell King) - Report full node name when reporting a DT error (Russell King) - Use port->name rather than "PCIe%d.%d" (Russell King) - Move port parsing and resource claiming to separate function (Russell King) - Fix memory leaks and refcount leaks (Russell King) - Split port parsing and resource claiming from port setup (Russell King) - Use gpio_set_value_cansleep() (Russell King) - Use devm_kcalloc() to allocate an array (Russell King) - Use gpio_desc to carry around gpio (Russell King) - Improve clock/reset handling (Russell King) - Add PCI Express root complex capability block (Russell King) - Remove code restricting accesses to slot 0 (Russell King) NVIDIA Tegra host bridge driver: - Wrap static pgprot_t initializer with __pgprot() (Ard Biesheuvel) Renesas R-Car host bridge driver: - Build pci-rcar-gen2.c only on ARM (Geert Uytterhoeven) - Build pcie-rcar.c only on ARM (Geert Uytterhoeven) - Make PCI aware of the I/O resources (Phil Edworthy) - Remove dependency on ARM-specific struct hw_pci (Phil Edworthy) - Set root bus nr to that provided in DT (Phil Edworthy) - Fix I/O offset for multiple host bridges (Phil Edworthy) ST Microelectronics SPEAr13xx host bridge driver: - Fix dw_pcie_cfg_read/write() usage (Gabriele Paoloni) Synopsys DesignWare host bridge driver: - Make "clocks" and "clock-names" optional DT properties (Bhupesh Sharma) - Use exact access size in dw_pcie_cfg_read() (Gabriele Paoloni) - Simplify dw_pcie_cfg_read/write() interfaces (Gabriele Paoloni) - Require config accesses to be naturally aligned (Gabriele Paoloni) - Make "num-lanes" an optional DT property (Gabriele Paoloni) - Move calculation of bus addresses to DRA7xx (Gabriele Paoloni) - Replace ARM pci_sys_data->align_resource with global function pointer (Gabriele Paoloni) - Factor out MSI msg setup (Lucas Stach) - Implement multivector MSI IRQ setup (Lucas Stach) - Make get_msi_addr() return phys_addr_t, not u32 (Lucas Stach) - Set up high part of MSI target address (Lucas Stach) - Fix PORT_LOGIC_LINK_WIDTH_MASK (Zhou Wang) - Revert "PCI: designware: Program ATU with untranslated address" (Zhou Wang) - Use of_pci_get_host_bridge_resources() to parse DT (Zhou Wang) - Make driver arch-agnostic (Zhou Wang) Miscellaneous: - Make x86 pci_subsys_init() static (Alexander Kuleshov) - Turn off Request Attributes to avoid Chelsio T5 Completion erratum (Hariprasad Shenai)" * tag 'pci-v4.4-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (94 commits) PCI: altera: Add Altera PCIe MSI driver PCI: hisi: Add HiSilicon SoC Hip05 PCIe driver PCI: layerscape: Add ls_pcie_msi_host_init() PCI: layerscape: Add support for LS1043a and LS2080a PCI: layerscape: Remove unused fields from struct ls_pcie PCI: layerscape: Update ls_add_pcie_port() PCI: layerscape: Factor out SCFG related function PCI: layerscape: Ignore PCIe controllers in Endpoint mode PCI: layerscape: Remove ls_pcie_establish_link() PCI: designware: Make "clocks" and "clock-names" optional DT properties PCI: designware: Make driver arch-agnostic ARM/PCI: Replace pci_sys_data->align_resource with global function pointer PCI: designware: Use of_pci_get_host_bridge_resources() to parse DT Revert "PCI: designware: Program ATU with untranslated address" PCI: designware: Move calculation of bus addresses to DRA7xx PCI: designware: Make "num-lanes" an optional DT property PCI: designware: Require config accesses to be naturally aligned PCI: designware: Simplify dw_pcie_cfg_read/write() interfaces PCI: designware: Use exact access size in dw_pcie_cfg_read() PCI: spear: Fix dw_pcie_cfg_read/write() usage ...
This commit is contained in:
commit
3c87b79188
|
@ -166,6 +166,23 @@ Example:
|
|||
reboot-offset = <0x4>;
|
||||
};
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
Hisilicon HiP05 PCIe-SAS system controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "hisilicon,pcie-sas-subctrl", "syscon";
|
||||
- reg : Register address and size
|
||||
|
||||
The HiP05 PCIe-SAS system controller is shared by PCIe and SAS controllers in
|
||||
HiP05 Soc to implement some basic configurations.
|
||||
|
||||
Example:
|
||||
/* for HiP05 PCIe-SAS system */
|
||||
pcie_sas: system_controller@0xb0000000 {
|
||||
compatible = "hisilicon,pcie-sas-subctrl", "syscon";
|
||||
reg = <0xb0000000 0x10000>;
|
||||
};
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
Hisilicon CPU controller
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
* Altera PCIe MSI controller
|
||||
|
||||
Required properties:
|
||||
- compatible: should contain "altr,msi-1.0"
|
||||
- reg: specifies the physical base address of the controller and
|
||||
the length of the memory mapped region.
|
||||
- reg-names: must include the following entries:
|
||||
"csr": CSR registers
|
||||
"vector_slave": vectors slave port region
|
||||
- interrupt-parent: interrupt source phandle.
|
||||
- interrupts: specifies the interrupt source of the parent interrupt
|
||||
controller. The format of the interrupt specifier depends on the
|
||||
parent interrupt controller.
|
||||
- num-vectors: number of vectors, range 1 to 32.
|
||||
- msi-controller: indicates that this is MSI controller node
|
||||
|
||||
|
||||
Example
|
||||
msi0: msi@0xFF200000 {
|
||||
compatible = "altr,msi-1.0";
|
||||
reg = <0xFF200000 0x00000010
|
||||
0xFF200010 0x00000080>;
|
||||
reg-names = "csr", "vector_slave";
|
||||
interrupt-parent = <&hps_0_arm_gic_0>;
|
||||
interrupts = <0 42 4>;
|
||||
msi-controller;
|
||||
num-vectors = <32>;
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
* Altera PCIe controller
|
||||
|
||||
Required properties:
|
||||
- compatible : should contain "altr,pcie-root-port-1.0"
|
||||
- reg: a list of physical base address and length for TXS and CRA.
|
||||
- reg-names: must include the following entries:
|
||||
"Txs": TX slave port region
|
||||
"Cra": Control register access region
|
||||
- interrupt-parent: interrupt source phandle.
|
||||
- interrupts: specifies the interrupt source of the parent interrupt controller.
|
||||
The format of the interrupt specifier depends on the parent interrupt
|
||||
controller.
|
||||
- device_type: must be "pci"
|
||||
- #address-cells: set to <3>
|
||||
- #size-cells: set to <2>
|
||||
- #interrupt-cells: set to <1>
|
||||
- ranges: describes the translation of addresses for root ports and standard
|
||||
PCI regions.
|
||||
- interrupt-map-mask and interrupt-map: standard PCI properties to define the
|
||||
mapping of the PCIe interface to interrupt numbers.
|
||||
|
||||
Optional properties:
|
||||
- msi-parent: Link to the hardware entity that serves as the MSI controller for this PCIe
|
||||
controller.
|
||||
- bus-range: PCI bus numbers covered
|
||||
|
||||
Example
|
||||
pcie_0: pcie@0xc00000000 {
|
||||
compatible = "altr,pcie-root-port-1.0";
|
||||
reg = <0xc0000000 0x20000000>,
|
||||
<0xff220000 0x00004000>;
|
||||
reg-names = "Txs", "Cra";
|
||||
interrupt-parent = <&hps_0_arm_gic_0>;
|
||||
interrupts = <0 40 4>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
bus-range = <0x0 0xFF>;
|
||||
device_type = "pci";
|
||||
msi-parent = <&msi_to_gic_gen_0>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0 0 0 1 &pcie_0 1>,
|
||||
<0 0 0 2 &pcie_0 2>,
|
||||
<0 0 0 3 &pcie_0 3>,
|
||||
<0 0 0 4 &pcie_0 4>;
|
||||
ranges = <0x82000000 0x00000000 0x00000000 0xc0000000 0x00000000 0x10000000
|
||||
0x82000000 0x00000000 0x10000000 0xd0000000 0x00000000 0x10000000>;
|
||||
};
|
|
@ -17,6 +17,21 @@ Optional properties:
|
|||
- phys: phandle of the PCIe PHY device
|
||||
- phy-names: must be "pcie-phy"
|
||||
|
||||
- brcm,pcie-ob: Some iProc SoCs do not have the outbound address mapping done
|
||||
by the ASIC after power on reset. In this case, SW needs to configure it
|
||||
|
||||
If the brcm,pcie-ob property is present, the following properties become
|
||||
effective:
|
||||
|
||||
Required:
|
||||
- brcm,pcie-ob-axi-offset: The offset from the AXI address to the internal
|
||||
address used by the iProc PCIe core (not the PCIe address)
|
||||
- brcm,pcie-ob-window-size: The outbound address mapping window size (in MB)
|
||||
|
||||
Optional:
|
||||
- brcm,pcie-ob-oarr-size: Some iProc SoCs need the OARR size bit to be set to
|
||||
increase the outbound window size
|
||||
|
||||
Example:
|
||||
pcie0: pcie@18012000 {
|
||||
compatible = "brcm,iproc-pcie";
|
||||
|
@ -38,6 +53,11 @@ Example:
|
|||
|
||||
phys = <&phy 0 5>;
|
||||
phy-names = "pcie-phy";
|
||||
|
||||
brcm,pcie-ob;
|
||||
brcm,pcie-ob-oarr-size;
|
||||
brcm,pcie-ob-axi-offset = <0x00000000>;
|
||||
brcm,pcie-ob-window-size = <256>;
|
||||
};
|
||||
|
||||
pcie1: pcie@18013000 {
|
||||
|
|
|
@ -15,14 +15,16 @@ Required properties:
|
|||
to define the mapping of the PCIe interface to interrupt
|
||||
numbers.
|
||||
- num-lanes: number of lanes to use
|
||||
|
||||
Optional properties:
|
||||
- num-lanes: number of lanes to use (this property should be specified unless
|
||||
the link is brought already up in BIOS)
|
||||
- reset-gpio: gpio pin number of power good signal
|
||||
- bus-range: PCI bus numbers covered (it is recommended for new devicetrees to
|
||||
specify this property, to keep backwards compatibility a range of 0x00-0xff
|
||||
is assumed if not present)
|
||||
- clocks: Must contain an entry for each entry in clock-names.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names: Must include the following entries:
|
||||
- "pcie"
|
||||
- "pcie_bus"
|
||||
|
||||
Optional properties:
|
||||
- reset-gpio: gpio pin number of power good signal
|
||||
- bus-range: PCI bus numbers covered (it is recommended for new devicetrees to
|
||||
specify this property, to keep backwards compatibility a range of 0x00-0xff
|
||||
is assumed if not present)
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
HiSilicon PCIe host bridge DT description
|
||||
|
||||
HiSilicon PCIe host controller is based on Designware PCI core.
|
||||
It shares common functions with PCIe Designware core driver and inherits
|
||||
common properties defined in
|
||||
Documentation/devicetree/bindings/pci/designware-pci.txt.
|
||||
|
||||
Additional properties are described here:
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "hisilicon,hip05-pcie".
|
||||
- reg: Should contain rc_dbi, config registers location and length.
|
||||
- reg-names: Must include the following entries:
|
||||
"rc_dbi": controller configuration registers;
|
||||
"config": PCIe configuration space registers.
|
||||
- msi-parent: Should be its_pcie which is an ITS receiving MSI interrupts.
|
||||
- port-id: Should be 0, 1, 2 or 3.
|
||||
|
||||
Optional properties:
|
||||
- status: Either "ok" or "disabled".
|
||||
- dma-coherent: Present if DMA operations are coherent.
|
||||
|
||||
Example:
|
||||
pcie@0xb0080000 {
|
||||
compatible = "hisilicon,hip05-pcie", "snps,dw-pcie";
|
||||
reg = <0 0xb0080000 0 0x10000>, <0x220 0x00000000 0 0x2000>;
|
||||
reg-names = "rc_dbi", "config";
|
||||
bus-range = <0 15>;
|
||||
msi-parent = <&its_pcie>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
device_type = "pci";
|
||||
dma-coherent;
|
||||
ranges = <0x82000000 0 0x00000000 0x220 0x00000000 0 0x10000000>;
|
||||
num-lanes = <8>;
|
||||
port-id = <1>;
|
||||
#interrupts-cells = <1>;
|
||||
interrupts-map-mask = <0xf800 0 0 7>;
|
||||
interrupts-map = <0x0 0 0 1 &mbigen_pcie 1 10
|
||||
0x0 0 0 2 &mbigen_pcie 2 11
|
||||
0x0 0 0 3 &mbigen_pcie 3 12
|
||||
0x0 0 0 4 &mbigen_pcie 4 13>;
|
||||
status = "ok";
|
||||
};
|
|
@ -34,8 +34,9 @@ Properties of the host controller node:
|
|||
- #size-cells : Must be 2.
|
||||
|
||||
- reg : The Configuration Space base address and size, as accessed
|
||||
from the parent bus.
|
||||
|
||||
from the parent bus. The base address corresponds to
|
||||
the first bus in the "bus-range" property. If no
|
||||
"bus-range" is specified, this will be bus 0 (the default).
|
||||
|
||||
Properties of the /chosen node:
|
||||
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
Freescale Layerscape PCIe controller
|
||||
|
||||
This PCIe host controller is based on the Synopsis Designware PCIe IP
|
||||
This PCIe host controller is based on the Synopsys DesignWare PCIe IP
|
||||
and thus inherits all the common properties defined in designware-pcie.txt.
|
||||
|
||||
This controller derives its clocks from the Reset Configuration Word (RCW)
|
||||
which is used to describe the PLL settings at the time of chip-reset.
|
||||
|
||||
Also as per the available Reference Manuals, there is no specific 'version'
|
||||
register available in the Freescale PCIe controller register set,
|
||||
which can allow determining the underlying DesignWare PCIe controller version
|
||||
information.
|
||||
|
||||
Required properties:
|
||||
- compatible: should contain the platform identifier such as "fsl,ls1021a-pcie"
|
||||
- compatible: should contain the platform identifier such as:
|
||||
"fsl,ls1021a-pcie", "snps,dw-pcie"
|
||||
"fsl,ls2080a-pcie", "snps,dw-pcie"
|
||||
- reg: base addresses and lengths of the PCIe controller
|
||||
- interrupts: A list of interrupt outputs of the controller. Must contain an
|
||||
entry for each entry in the interrupt-names property.
|
||||
|
|
23
MAINTAINERS
23
MAINTAINERS
|
@ -8063,6 +8063,14 @@ F: include/linux/pci*
|
|||
F: arch/x86/pci/
|
||||
F: arch/x86/kernel/quirks.c
|
||||
|
||||
PCI DRIVER FOR ALTERA PCIE IP
|
||||
M: Ley Foon Tan <lftan@altera.com>
|
||||
L: rfi@lists.rocketboards.org (moderated for non-subscribers)
|
||||
L: linux-pci@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/pci/altera-pcie.txt
|
||||
F: drivers/pci/host/pcie-altera.c
|
||||
|
||||
PCI DRIVER FOR ARM VERSATILE PLATFORM
|
||||
M: Rob Herring <robh@kernel.org>
|
||||
L: linux-pci@vger.kernel.org
|
||||
|
@ -8164,6 +8172,14 @@ L: linux-pci@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/pci/host/*spear*
|
||||
|
||||
PCI MSI DRIVER FOR ALTERA MSI IP
|
||||
M: Ley Foon Tan <lftan@altera.com>
|
||||
L: rfi@lists.rocketboards.org (moderated for non-subscribers)
|
||||
L: linux-pci@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
|
||||
F: drivers/pci/host/pcie-altera-msi.c
|
||||
|
||||
PCI MSI DRIVER FOR APPLIEDMICRO XGENE
|
||||
M: Duc Dang <dhdang@apm.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
|
@ -8172,6 +8188,13 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
|
||||
F: drivers/pci/host/pci-xgene-msi.c
|
||||
|
||||
PCIE DRIVER FOR HISILICON
|
||||
M: Zhou Wang <wangzhou1@hisilicon.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/pci/hisilicon-pcie.txt
|
||||
F: drivers/pci/host/pcie-hisi.c
|
||||
|
||||
PCMCIA SUBSYSTEM
|
||||
P: Linux PCMCIA Team
|
||||
L: linux-pcmcia@lists.infradead.org
|
||||
|
|
|
@ -14,6 +14,7 @@ generic-y += local.h
|
|||
generic-y += local64.h
|
||||
generic-y += mm-arch-hooks.h
|
||||
generic-y += msgbuf.h
|
||||
generic-y += msi.h
|
||||
generic-y += param.h
|
||||
generic-y += parport.h
|
||||
generic-y += poll.h
|
||||
|
|
|
@ -52,12 +52,6 @@ struct pci_sys_data {
|
|||
u8 (*swizzle)(struct pci_dev *, u8 *);
|
||||
/* IRQ mapping */
|
||||
int (*map_irq)(const struct pci_dev *, u8, u8);
|
||||
/* Resource alignement requirements */
|
||||
resource_size_t (*align_resource)(struct pci_dev *dev,
|
||||
const struct resource *res,
|
||||
resource_size_t start,
|
||||
resource_size_t size,
|
||||
resource_size_t align);
|
||||
void *private_data; /* platform controller private data */
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
#include <asm/mach/pci.h>
|
||||
|
||||
static int debug_pci;
|
||||
static resource_size_t (*align_resource)(struct pci_dev *dev,
|
||||
const struct resource *res,
|
||||
resource_size_t start,
|
||||
resource_size_t size,
|
||||
resource_size_t align) = NULL;
|
||||
|
||||
/*
|
||||
* We can't use pci_get_device() here since we are
|
||||
|
@ -456,7 +461,7 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw,
|
|||
sys->busnr = busnr;
|
||||
sys->swizzle = hw->swizzle;
|
||||
sys->map_irq = hw->map_irq;
|
||||
sys->align_resource = hw->align_resource;
|
||||
align_resource = hw->align_resource;
|
||||
INIT_LIST_HEAD(&sys->resources);
|
||||
|
||||
if (hw->private_data)
|
||||
|
@ -572,7 +577,6 @@ 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;
|
||||
struct pci_sys_data *sys = dev->sysdata;
|
||||
resource_size_t start = res->start;
|
||||
|
||||
if (res->flags & IORESOURCE_IO && start & 0x300)
|
||||
|
@ -580,8 +584,8 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
|
|||
|
||||
start = (start + align - 1) & ~(align - 1);
|
||||
|
||||
if (sys->align_resource)
|
||||
return sys->align_resource(dev, res, start, size, align);
|
||||
if (align_resource)
|
||||
return align_resource(dev, res, start, size, align);
|
||||
|
||||
return start;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
chosen {
|
||||
stdout-path = &serial0;
|
||||
linux,pci-probe-only;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <linux/seq_file.h>
|
||||
#include <linux/root_dev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/kexec.h>
|
||||
|
||||
#include <asm/mmu.h>
|
||||
|
@ -495,18 +496,7 @@ static void __init find_and_init_phbs(void)
|
|||
* PCI_PROBE_ONLY and PCI_REASSIGN_ALL_BUS can be set via properties
|
||||
* in chosen.
|
||||
*/
|
||||
if (of_chosen) {
|
||||
const int *prop;
|
||||
|
||||
prop = of_get_property(of_chosen,
|
||||
"linux,pci-probe-only", NULL);
|
||||
if (prop) {
|
||||
if (*prop)
|
||||
pci_add_flags(PCI_PROBE_ONLY);
|
||||
else
|
||||
pci_clear_flags(PCI_PROBE_ONLY);
|
||||
}
|
||||
}
|
||||
of_pci_check_probe_only();
|
||||
}
|
||||
|
||||
static void __init pSeries_setup_arch(void)
|
||||
|
|
|
@ -185,8 +185,10 @@ static unsigned long pci_parse_of_flags(u32 addr0)
|
|||
|
||||
if (addr0 & 0x02000000) {
|
||||
flags = IORESOURCE_MEM | PCI_BASE_ADDRESS_SPACE_MEMORY;
|
||||
flags |= (addr0 >> 22) & PCI_BASE_ADDRESS_MEM_TYPE_64;
|
||||
flags |= (addr0 >> 28) & PCI_BASE_ADDRESS_MEM_TYPE_1M;
|
||||
if (addr0 & 0x01000000)
|
||||
flags |= IORESOURCE_MEM_64
|
||||
| PCI_BASE_ADDRESS_MEM_TYPE_64;
|
||||
if (addr0 & 0x40000000)
|
||||
flags |= IORESOURCE_PREFETCH
|
||||
| PCI_BASE_ADDRESS_MEM_PREFETCH;
|
||||
|
@ -655,6 +657,9 @@ struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm,
|
|||
pbm->io_space.start);
|
||||
pci_add_resource_offset(&resources, &pbm->mem_space,
|
||||
pbm->mem_space.start);
|
||||
if (pbm->mem64_space.flags)
|
||||
pci_add_resource_offset(&resources, &pbm->mem64_space,
|
||||
pbm->mem_space.start);
|
||||
pbm->busn.start = pbm->pci_first_busno;
|
||||
pbm->busn.end = pbm->pci_last_busno;
|
||||
pbm->busn.flags = IORESOURCE_BUS;
|
||||
|
|
|
@ -406,6 +406,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
|
|||
}
|
||||
|
||||
num_pbm_ranges = i / sizeof(*pbm_ranges);
|
||||
memset(&pbm->mem64_space, 0, sizeof(struct resource));
|
||||
|
||||
for (i = 0; i < num_pbm_ranges; i++) {
|
||||
const struct linux_prom_pci_ranges *pr = &pbm_ranges[i];
|
||||
|
@ -451,7 +452,12 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
|
|||
break;
|
||||
|
||||
case 3:
|
||||
/* XXX 64-bit MEM handling XXX */
|
||||
/* 64-bit MEM handling */
|
||||
pbm->mem64_space.start = a;
|
||||
pbm->mem64_space.end = a + size - 1UL;
|
||||
pbm->mem64_space.flags = IORESOURCE_MEM;
|
||||
saw_mem = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -465,15 +471,22 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
|
|||
prom_halt();
|
||||
}
|
||||
|
||||
printk("%s: PCI IO[%llx] MEM[%llx]\n",
|
||||
printk("%s: PCI IO[%llx] MEM[%llx]",
|
||||
pbm->name,
|
||||
pbm->io_space.start,
|
||||
pbm->mem_space.start);
|
||||
if (pbm->mem64_space.flags)
|
||||
printk(" MEM64[%llx]",
|
||||
pbm->mem64_space.start);
|
||||
printk("\n");
|
||||
|
||||
pbm->io_space.name = pbm->mem_space.name = pbm->name;
|
||||
pbm->mem64_space.name = pbm->name;
|
||||
|
||||
request_resource(&ioport_resource, &pbm->io_space);
|
||||
request_resource(&iomem_resource, &pbm->mem_space);
|
||||
if (pbm->mem64_space.flags)
|
||||
request_resource(&iomem_resource, &pbm->mem64_space);
|
||||
|
||||
pci_register_legacy_regions(&pbm->io_space,
|
||||
&pbm->mem_space);
|
||||
|
|
|
@ -97,6 +97,7 @@ struct pci_pbm_info {
|
|||
/* PBM I/O and Memory space resources. */
|
||||
struct resource io_space;
|
||||
struct resource mem_space;
|
||||
struct resource mem64_space;
|
||||
struct resource busn;
|
||||
|
||||
/* Base of PCI Config space, can be per-PBM or shared. */
|
||||
|
|
|
@ -675,6 +675,14 @@ int pcibios_add_device(struct pci_dev *dev)
|
|||
|
||||
int pcibios_alloc_irq(struct pci_dev *dev)
|
||||
{
|
||||
/*
|
||||
* If the PCI device was already claimed by core code and has
|
||||
* MSI enabled, probing of the pcibios IRQ will overwrite
|
||||
* dev->irq. So bail out if MSI is already enabled.
|
||||
*/
|
||||
if (pci_dev_msi_enabled(dev))
|
||||
return -EBUSY;
|
||||
|
||||
return pcibios_enable_irq(dev);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ void pcibios_scan_specific_bus(int busn)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(pcibios_scan_specific_bus);
|
||||
|
||||
int __init pci_subsys_init(void)
|
||||
static int __init pci_subsys_init(void)
|
||||
{
|
||||
/*
|
||||
* The init function returns an non zero value when
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#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)
|
||||
|
@ -117,6 +118,31 @@ int of_get_pci_domain_nr(struct device_node *node)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(of_get_pci_domain_nr);
|
||||
|
||||
/**
|
||||
* of_pci_check_probe_only - Setup probe only mode if linux,pci-probe-only
|
||||
* is present and valid
|
||||
*/
|
||||
void of_pci_check_probe_only(void)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(of_chosen, "linux,pci-probe-only", &val);
|
||||
if (ret) {
|
||||
if (ret == -ENODATA || ret == -EOVERFLOW)
|
||||
pr_warn("linux,pci-probe-only without valid value, ignoring\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (val)
|
||||
pci_add_flags(PCI_PROBE_ONLY);
|
||||
else
|
||||
pci_clear_flags(PCI_PROBE_ONLY);
|
||||
|
||||
pr_info("PCI: PROBE_ONLY %sabled\n", val ? "en" : "dis");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_pci_check_probe_only);
|
||||
|
||||
/**
|
||||
* of_pci_dma_configure - Setup DMA configuration
|
||||
* @dev: ptr to pci_dev struct of the PCI device
|
||||
|
|
|
@ -39,7 +39,8 @@ config PCI_TEGRA
|
|||
|
||||
config PCI_RCAR_GEN2
|
||||
bool "Renesas R-Car Gen2 Internal PCI controller"
|
||||
depends on ARCH_SHMOBILE || (ARM && COMPILE_TEST)
|
||||
depends on ARM
|
||||
depends on ARCH_SHMOBILE || 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
|
||||
|
@ -47,7 +48,8 @@ config PCI_RCAR_GEN2
|
|||
|
||||
config PCI_RCAR_GEN2_PCIE
|
||||
bool "Renesas R-Car PCIe controller"
|
||||
depends on ARCH_SHMOBILE || (ARM && COMPILE_TEST)
|
||||
depends on ARM
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you want PCIe controller support on R-Car Gen2 SoCs.
|
||||
|
||||
|
@ -105,7 +107,7 @@ config PCI_XGENE_MSI
|
|||
|
||||
config PCI_LAYERSCAPE
|
||||
bool "Freescale Layerscape PCIe controller"
|
||||
depends on OF && ARM
|
||||
depends on OF && (ARM || ARCH_LAYERSCAPE)
|
||||
select PCIE_DW
|
||||
select MFD_SYSCON
|
||||
help
|
||||
|
@ -145,4 +147,29 @@ config PCIE_IPROC_BCMA
|
|||
Say Y here if you want to use the Broadcom iProc PCIe controller
|
||||
through the BCMA bus interface
|
||||
|
||||
config PCIE_ALTERA
|
||||
bool "Altera PCIe controller"
|
||||
depends on ARM || NIOS2
|
||||
depends on OF_PCI
|
||||
select PCI_DOMAINS
|
||||
help
|
||||
Say Y here if you want to enable PCIe controller support on Altera
|
||||
FPGA.
|
||||
|
||||
config PCIE_ALTERA_MSI
|
||||
bool "Altera PCIe MSI feature"
|
||||
depends on PCIE_ALTERA && PCI_MSI
|
||||
select PCI_MSI_IRQ_DOMAIN
|
||||
help
|
||||
Say Y here if you want PCIe MSI support for the Altera FPGA.
|
||||
This MSI driver supports Altera MSI to GIC controller IP.
|
||||
|
||||
config PCI_HISI
|
||||
depends on OF && ARM64
|
||||
bool "HiSilicon SoC HIP05 PCIe controller"
|
||||
select PCIEPORTBUS
|
||||
select PCIE_DW
|
||||
help
|
||||
Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -17,3 +17,6 @@ obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
|
|||
obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
|
||||
obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
|
||||
obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
|
||||
obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
|
||||
obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
|
||||
obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
|
||||
#define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C
|
||||
#define LINK_UP BIT(16)
|
||||
#define DRA7XX_CPU_TO_BUS_ADDR 0x0FFFFFFF
|
||||
|
||||
struct dra7xx_pcie {
|
||||
void __iomem *base;
|
||||
|
@ -151,6 +152,12 @@ static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp)
|
|||
static void dra7xx_pcie_host_init(struct pcie_port *pp)
|
||||
{
|
||||
dw_pcie_setup_rc(pp);
|
||||
|
||||
pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||
pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||
pp->cfg0_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||
pp->cfg1_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||
|
||||
dra7xx_pcie_establish_link(pp);
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI))
|
||||
dw_pcie_msi_init(pp);
|
||||
|
|
|
@ -454,7 +454,7 @@ static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
|
|||
int ret;
|
||||
|
||||
exynos_pcie_sideband_dbi_r_mode(pp, true);
|
||||
ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, size, val);
|
||||
ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
|
||||
exynos_pcie_sideband_dbi_r_mode(pp, false);
|
||||
return ret;
|
||||
}
|
||||
|
@ -465,8 +465,7 @@ static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
|
|||
int ret;
|
||||
|
||||
exynos_pcie_sideband_dbi_w_mode(pp, true);
|
||||
ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3),
|
||||
where, size, val);
|
||||
ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
|
||||
exynos_pcie_sideband_dbi_w_mode(pp, false);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
struct gen_pci_cfg_bus_ops {
|
||||
u32 bus_shift;
|
||||
void __iomem *(*map_bus)(struct pci_bus *, unsigned int, int);
|
||||
struct pci_ops ops;
|
||||
};
|
||||
|
||||
struct gen_pci_cfg_windows {
|
||||
|
@ -35,7 +35,7 @@ struct gen_pci_cfg_windows {
|
|||
struct resource *bus_range;
|
||||
void __iomem **win;
|
||||
|
||||
const struct gen_pci_cfg_bus_ops *ops;
|
||||
struct gen_pci_cfg_bus_ops *ops;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -65,7 +65,11 @@ static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus,
|
|||
|
||||
static struct gen_pci_cfg_bus_ops gen_pci_cfg_cam_bus_ops = {
|
||||
.bus_shift = 16,
|
||||
.map_bus = gen_pci_map_cfg_bus_cam,
|
||||
.ops = {
|
||||
.map_bus = gen_pci_map_cfg_bus_cam,
|
||||
.read = pci_generic_config_read,
|
||||
.write = pci_generic_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
|
||||
|
@ -80,12 +84,11 @@ static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
|
|||
|
||||
static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = {
|
||||
.bus_shift = 20,
|
||||
.map_bus = gen_pci_map_cfg_bus_ecam,
|
||||
};
|
||||
|
||||
static struct pci_ops gen_pci_ops = {
|
||||
.read = pci_generic_config_read,
|
||||
.write = pci_generic_config_write,
|
||||
.ops = {
|
||||
.map_bus = gen_pci_map_cfg_bus_ecam,
|
||||
.read = pci_generic_config_read,
|
||||
.write = pci_generic_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct of_device_id gen_pci_of_match[] = {
|
||||
|
@ -166,6 +169,7 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
|
|||
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) {
|
||||
|
@ -193,10 +197,9 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
|
|||
bus_range = pci->cfg.bus_range;
|
||||
for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
|
||||
u32 idx = busn - bus_range->start;
|
||||
u32 sz = 1 << pci->cfg.ops->bus_shift;
|
||||
|
||||
pci->cfg.win[idx] = devm_ioremap(dev,
|
||||
pci->cfg.res.start + busn * sz,
|
||||
pci->cfg.res.start + idx * sz,
|
||||
sz);
|
||||
if (!pci->cfg.win[idx])
|
||||
return -ENOMEM;
|
||||
|
@ -210,7 +213,6 @@ static int gen_pci_probe(struct platform_device *pdev)
|
|||
int err;
|
||||
const char *type;
|
||||
const struct of_device_id *of_id;
|
||||
const int *prop;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
||||
|
@ -225,17 +227,10 @@ static int gen_pci_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
prop = of_get_property(of_chosen, "linux,pci-probe-only", NULL);
|
||||
if (prop) {
|
||||
if (*prop)
|
||||
pci_add_flags(PCI_PROBE_ONLY);
|
||||
else
|
||||
pci_clear_flags(PCI_PROBE_ONLY);
|
||||
}
|
||||
of_pci_check_probe_only();
|
||||
|
||||
of_id = of_match_node(gen_pci_of_match, np);
|
||||
pci->cfg.ops = of_id->data;
|
||||
gen_pci_ops.map_bus = pci->cfg.ops->map_bus;
|
||||
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);
|
||||
|
@ -256,7 +251,9 @@ static int gen_pci_probe(struct platform_device *pdev)
|
|||
if (!pci_has_flag(PCI_PROBE_ONLY))
|
||||
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
|
||||
|
||||
bus = pci_scan_root_bus(dev, 0, &gen_pci_ops, pci, &pci->resources);
|
||||
|
||||
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;
|
||||
|
|
|
@ -74,6 +74,7 @@ struct imx6_pcie {
|
|||
|
||||
/* PHY registers (not memory-mapped) */
|
||||
#define PCIE_PHY_RX_ASIC_OUT 0x100D
|
||||
#define PCIE_PHY_RX_ASIC_OUT_VALID (1 << 0)
|
||||
|
||||
#define PHY_RX_OVRD_IN_LO 0x1005
|
||||
#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
|
||||
|
@ -503,7 +504,7 @@ static int imx6_pcie_link_up(struct pcie_port *pp)
|
|||
pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid);
|
||||
debug_r0 = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0);
|
||||
|
||||
if (rx_valid & 0x01)
|
||||
if (rx_valid & PCIE_PHY_RX_ASIC_OUT_VALID)
|
||||
return 0;
|
||||
|
||||
if ((debug_r0 & 0x3f) != 0x0d)
|
||||
|
@ -539,7 +540,7 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp,
|
|||
IRQF_SHARED, "mx6-pcie-msi", pp);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request MSI irq\n");
|
||||
return -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset,
|
|||
*bit_pos = offset >> 3;
|
||||
}
|
||||
|
||||
u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp)
|
||||
phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
|
||||
|
||||
|
@ -322,7 +322,7 @@ static void ks_dw_pcie_clear_dbi_mode(void __iomem *reg_virt)
|
|||
void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie)
|
||||
{
|
||||
struct pcie_port *pp = &ks_pcie->pp;
|
||||
u32 start = pp->mem.start, end = pp->mem.end;
|
||||
u32 start = pp->mem->start, end = pp->mem->end;
|
||||
int i, tr_size;
|
||||
|
||||
/* Disable BARs for inbound access */
|
||||
|
@ -398,7 +398,7 @@ int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
|||
|
||||
addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
|
||||
|
||||
return dw_pcie_cfg_read(addr + (where & ~0x3), where, size, val);
|
||||
return dw_pcie_cfg_read(addr + where, size, val);
|
||||
}
|
||||
|
||||
int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
|
@ -410,7 +410,7 @@ int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
|||
|
||||
addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
|
||||
|
||||
return dw_pcie_cfg_write(addr + (where & ~0x3), where, size, val);
|
||||
return dw_pcie_cfg_write(addr + where, size, val);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,7 +37,7 @@ struct keystone_pcie {
|
|||
|
||||
/* Keystone DW specific MSI controller APIs/definitions */
|
||||
void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset);
|
||||
u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp);
|
||||
phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp);
|
||||
|
||||
/* Keystone specific PCI controller APIs */
|
||||
void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2014 Freescale Semiconductor.
|
||||
*
|
||||
* Author: Minghuan Lian <Minghuan.Lian@freescale.com>
|
||||
* Author: Minghuan Lian <Minghuan.Lian@freescale.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
|
||||
|
@ -11,7 +11,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_pci.h>
|
||||
|
@ -32,27 +31,60 @@
|
|||
#define LTSSM_STATE_MASK 0x3f
|
||||
#define LTSSM_PCIE_L0 0x11 /* L0 state */
|
||||
|
||||
/* Symbol Timer Register and Filter Mask Register 1 */
|
||||
#define PCIE_STRFMR1 0x71c
|
||||
/* PEX Internal Configuration Registers */
|
||||
#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */
|
||||
#define PCIE_DBI_RO_WR_EN 0x8bc /* DBI Read-Only Write Enable Register */
|
||||
|
||||
/* PEX LUT registers */
|
||||
#define PCIE_LUT_DBG 0x7FC /* PEX LUT Debug Register */
|
||||
|
||||
struct ls_pcie_drvdata {
|
||||
u32 lut_offset;
|
||||
u32 ltssm_shift;
|
||||
struct pcie_host_ops *ops;
|
||||
};
|
||||
|
||||
struct ls_pcie {
|
||||
struct list_head node;
|
||||
struct device *dev;
|
||||
struct pci_bus *bus;
|
||||
void __iomem *dbi;
|
||||
void __iomem *lut;
|
||||
struct regmap *scfg;
|
||||
struct pcie_port pp;
|
||||
const struct ls_pcie_drvdata *drvdata;
|
||||
int index;
|
||||
int msi_irq;
|
||||
};
|
||||
|
||||
#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp)
|
||||
|
||||
static int ls_pcie_link_up(struct pcie_port *pp)
|
||||
static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
|
||||
{
|
||||
u32 header_type;
|
||||
|
||||
header_type = ioread8(pcie->dbi + PCI_HEADER_TYPE);
|
||||
header_type &= 0x7f;
|
||||
|
||||
return header_type == PCI_HEADER_TYPE_BRIDGE;
|
||||
}
|
||||
|
||||
/* Clear multi-function bit */
|
||||
static void ls_pcie_clear_multifunction(struct ls_pcie *pcie)
|
||||
{
|
||||
iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->dbi + PCI_HEADER_TYPE);
|
||||
}
|
||||
|
||||
/* Fix class value */
|
||||
static void ls_pcie_fix_class(struct ls_pcie *pcie)
|
||||
{
|
||||
iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->dbi + PCI_CLASS_DEVICE);
|
||||
}
|
||||
|
||||
static int ls1021_pcie_link_up(struct pcie_port *pp)
|
||||
{
|
||||
u32 state;
|
||||
struct ls_pcie *pcie = to_ls_pcie(pp);
|
||||
|
||||
if (!pcie->scfg)
|
||||
return 0;
|
||||
|
||||
regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state);
|
||||
state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK;
|
||||
|
||||
|
@ -62,27 +94,27 @@ static int ls_pcie_link_up(struct pcie_port *pp)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int ls_pcie_establish_link(struct pcie_port *pp)
|
||||
{
|
||||
unsigned int retries;
|
||||
|
||||
for (retries = 0; retries < 200; retries++) {
|
||||
if (dw_pcie_link_up(pp))
|
||||
return 0;
|
||||
usleep_range(100, 1000);
|
||||
}
|
||||
|
||||
dev_err(pp->dev, "phy link never came up\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void ls_pcie_host_init(struct pcie_port *pp)
|
||||
static void ls1021_pcie_host_init(struct pcie_port *pp)
|
||||
{
|
||||
struct ls_pcie *pcie = to_ls_pcie(pp);
|
||||
u32 val;
|
||||
u32 val, index[2];
|
||||
|
||||
pcie->scfg = syscon_regmap_lookup_by_phandle(pp->dev->of_node,
|
||||
"fsl,pcie-scfg");
|
||||
if (IS_ERR(pcie->scfg)) {
|
||||
dev_err(pp->dev, "No syscfg phandle specified\n");
|
||||
pcie->scfg = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (of_property_read_u32_array(pp->dev->of_node,
|
||||
"fsl,pcie-scfg", index, 2)) {
|
||||
pcie->scfg = NULL;
|
||||
return;
|
||||
}
|
||||
pcie->index = index[1];
|
||||
|
||||
dw_pcie_setup_rc(pp);
|
||||
ls_pcie_establish_link(pp);
|
||||
|
||||
/*
|
||||
* LS1021A Workaround for internal TKT228622
|
||||
|
@ -93,21 +125,97 @@ static void ls_pcie_host_init(struct pcie_port *pp)
|
|||
iowrite32(val, pcie->dbi + PCIE_STRFMR1);
|
||||
}
|
||||
|
||||
static int ls_pcie_link_up(struct pcie_port *pp)
|
||||
{
|
||||
struct ls_pcie *pcie = to_ls_pcie(pp);
|
||||
u32 state;
|
||||
|
||||
state = (ioread32(pcie->lut + PCIE_LUT_DBG) >>
|
||||
pcie->drvdata->ltssm_shift) &
|
||||
LTSSM_STATE_MASK;
|
||||
|
||||
if (state < LTSSM_PCIE_L0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ls_pcie_host_init(struct pcie_port *pp)
|
||||
{
|
||||
struct ls_pcie *pcie = to_ls_pcie(pp);
|
||||
|
||||
iowrite32(1, pcie->dbi + PCIE_DBI_RO_WR_EN);
|
||||
ls_pcie_fix_class(pcie);
|
||||
ls_pcie_clear_multifunction(pcie);
|
||||
iowrite32(0, pcie->dbi + PCIE_DBI_RO_WR_EN);
|
||||
}
|
||||
|
||||
static int ls_pcie_msi_host_init(struct pcie_port *pp,
|
||||
struct msi_controller *chip)
|
||||
{
|
||||
struct device_node *msi_node;
|
||||
struct device_node *np = pp->dev->of_node;
|
||||
|
||||
/*
|
||||
* The MSI domain is set by the generic of_msi_configure(). This
|
||||
* .msi_host_init() function keeps us from doing the default MSI
|
||||
* domain setup in dw_pcie_host_init() and also enforces the
|
||||
* requirement that "msi-parent" exists.
|
||||
*/
|
||||
msi_node = of_parse_phandle(np, "msi-parent", 0);
|
||||
if (!msi_node) {
|
||||
dev_err(pp->dev, "failed to find msi-parent\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pcie_host_ops ls1021_pcie_host_ops = {
|
||||
.link_up = ls1021_pcie_link_up,
|
||||
.host_init = ls1021_pcie_host_init,
|
||||
.msi_host_init = ls_pcie_msi_host_init,
|
||||
};
|
||||
|
||||
static struct pcie_host_ops ls_pcie_host_ops = {
|
||||
.link_up = ls_pcie_link_up,
|
||||
.host_init = ls_pcie_host_init,
|
||||
.msi_host_init = ls_pcie_msi_host_init,
|
||||
};
|
||||
|
||||
static int ls_add_pcie_port(struct ls_pcie *pcie)
|
||||
{
|
||||
struct pcie_port *pp;
|
||||
int ret;
|
||||
static struct ls_pcie_drvdata ls1021_drvdata = {
|
||||
.ops = &ls1021_pcie_host_ops,
|
||||
};
|
||||
|
||||
pp = &pcie->pp;
|
||||
pp->dev = pcie->dev;
|
||||
static struct ls_pcie_drvdata ls1043_drvdata = {
|
||||
.lut_offset = 0x10000,
|
||||
.ltssm_shift = 24,
|
||||
.ops = &ls_pcie_host_ops,
|
||||
};
|
||||
|
||||
static struct ls_pcie_drvdata ls2080_drvdata = {
|
||||
.lut_offset = 0x80000,
|
||||
.ltssm_shift = 0,
|
||||
.ops = &ls_pcie_host_ops,
|
||||
};
|
||||
|
||||
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 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ls_pcie_of_match);
|
||||
|
||||
static int __init ls_add_pcie_port(struct pcie_port *pp,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct ls_pcie *pcie = to_ls_pcie(pp);
|
||||
|
||||
pp->dev = &pdev->dev;
|
||||
pp->dbi_base = pcie->dbi;
|
||||
pp->root_bus_nr = -1;
|
||||
pp->ops = &ls_pcie_host_ops;
|
||||
pp->ops = pcie->drvdata->ops;
|
||||
|
||||
ret = dw_pcie_host_init(pp);
|
||||
if (ret) {
|
||||
|
@ -120,17 +228,19 @@ static int ls_add_pcie_port(struct ls_pcie *pcie)
|
|||
|
||||
static int __init ls_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct ls_pcie *pcie;
|
||||
struct resource *dbi_base;
|
||||
u32 index[2];
|
||||
int ret;
|
||||
|
||||
match = of_match_device(ls_pcie_of_match, &pdev->dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
|
||||
if (!pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
pcie->dev = &pdev->dev;
|
||||
|
||||
dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
|
||||
pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base);
|
||||
if (IS_ERR(pcie->dbi)) {
|
||||
|
@ -138,20 +248,13 @@ static int __init ls_pcie_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(pcie->dbi);
|
||||
}
|
||||
|
||||
pcie->scfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||
"fsl,pcie-scfg");
|
||||
if (IS_ERR(pcie->scfg)) {
|
||||
dev_err(&pdev->dev, "No syscfg phandle specified\n");
|
||||
return PTR_ERR(pcie->scfg);
|
||||
}
|
||||
pcie->drvdata = match->data;
|
||||
pcie->lut = pcie->dbi + pcie->drvdata->lut_offset;
|
||||
|
||||
ret = of_property_read_u32_array(pdev->dev.of_node,
|
||||
"fsl,pcie-scfg", index, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
pcie->index = index[1];
|
||||
if (!ls_pcie_is_bridge(pcie))
|
||||
return -ENODEV;
|
||||
|
||||
ret = ls_add_pcie_port(pcie);
|
||||
ret = ls_add_pcie_port(&pcie->pp, pdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -160,12 +263,6 @@ static int __init ls_pcie_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ls_pcie_of_match[] = {
|
||||
{ .compatible = "fsl,ls1021a-pcie" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ls_pcie_of_match);
|
||||
|
||||
static struct platform_driver ls_pcie_driver = {
|
||||
.driver = {
|
||||
.name = "layerscape-pcie",
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#define PCIE_DEV_REV_OFF 0x0008
|
||||
#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3))
|
||||
#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3))
|
||||
#define PCIE_CAP_PCIEXP 0x0060
|
||||
#define PCIE_HEADER_LOG_4_OFF 0x0128
|
||||
#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4))
|
||||
#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4))
|
||||
|
@ -57,14 +58,35 @@
|
|||
#define PCIE_STAT_BUS 0xff00
|
||||
#define PCIE_STAT_DEV 0x1f0000
|
||||
#define PCIE_STAT_LINK_DOWN BIT(0)
|
||||
#define PCIE_RC_RTSTA 0x1a14
|
||||
#define PCIE_DEBUG_CTRL 0x1a60
|
||||
#define PCIE_DEBUG_SOFT_RESET BIT(20)
|
||||
|
||||
enum {
|
||||
PCISWCAP = PCI_BRIDGE_CONTROL + 2,
|
||||
PCISWCAP_EXP_LIST_ID = PCISWCAP + PCI_CAP_LIST_ID,
|
||||
PCISWCAP_EXP_DEVCAP = PCISWCAP + PCI_EXP_DEVCAP,
|
||||
PCISWCAP_EXP_DEVCTL = PCISWCAP + PCI_EXP_DEVCTL,
|
||||
PCISWCAP_EXP_LNKCAP = PCISWCAP + PCI_EXP_LNKCAP,
|
||||
PCISWCAP_EXP_LNKCTL = PCISWCAP + PCI_EXP_LNKCTL,
|
||||
PCISWCAP_EXP_SLTCAP = PCISWCAP + PCI_EXP_SLTCAP,
|
||||
PCISWCAP_EXP_SLTCTL = PCISWCAP + PCI_EXP_SLTCTL,
|
||||
PCISWCAP_EXP_RTCTL = PCISWCAP + PCI_EXP_RTCTL,
|
||||
PCISWCAP_EXP_RTSTA = PCISWCAP + PCI_EXP_RTSTA,
|
||||
PCISWCAP_EXP_DEVCAP2 = PCISWCAP + PCI_EXP_DEVCAP2,
|
||||
PCISWCAP_EXP_DEVCTL2 = PCISWCAP + PCI_EXP_DEVCTL2,
|
||||
PCISWCAP_EXP_LNKCAP2 = PCISWCAP + PCI_EXP_LNKCAP2,
|
||||
PCISWCAP_EXP_LNKCTL2 = PCISWCAP + PCI_EXP_LNKCTL2,
|
||||
PCISWCAP_EXP_SLTCAP2 = PCISWCAP + PCI_EXP_SLTCAP2,
|
||||
PCISWCAP_EXP_SLTCTL2 = PCISWCAP + PCI_EXP_SLTCTL2,
|
||||
};
|
||||
|
||||
/* PCI configuration space of a PCI-to-PCI bridge */
|
||||
struct mvebu_sw_pci_bridge {
|
||||
u16 vendor;
|
||||
u16 device;
|
||||
u16 command;
|
||||
u16 status;
|
||||
u16 class;
|
||||
u8 interface;
|
||||
u8 revision;
|
||||
|
@ -84,13 +106,15 @@ struct mvebu_sw_pci_bridge {
|
|||
u16 memlimit;
|
||||
u16 iobaseupper;
|
||||
u16 iolimitupper;
|
||||
u8 cappointer;
|
||||
u8 reserved1;
|
||||
u16 reserved2;
|
||||
u32 romaddr;
|
||||
u8 intline;
|
||||
u8 intpin;
|
||||
u16 bridgectrl;
|
||||
|
||||
/* PCI express capability */
|
||||
u32 pcie_sltcap;
|
||||
u16 pcie_devctl;
|
||||
u16 pcie_rtctl;
|
||||
};
|
||||
|
||||
struct mvebu_pcie_port;
|
||||
|
@ -119,8 +143,7 @@ struct mvebu_pcie_port {
|
|||
unsigned int io_target;
|
||||
unsigned int io_attr;
|
||||
struct clk *clk;
|
||||
int reset_gpio;
|
||||
int reset_active_low;
|
||||
struct gpio_desc *reset_gpio;
|
||||
char *reset_name;
|
||||
struct mvebu_sw_pci_bridge bridge;
|
||||
struct device_node *dn;
|
||||
|
@ -254,15 +277,22 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,
|
|||
struct pci_bus *bus,
|
||||
u32 devfn, int where, int size, u32 *val)
|
||||
{
|
||||
void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
|
||||
|
||||
mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
|
||||
PCIE_CONF_ADDR_OFF);
|
||||
|
||||
*val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
|
||||
|
||||
if (size == 1)
|
||||
*val = (*val >> (8 * (where & 3))) & 0xff;
|
||||
else if (size == 2)
|
||||
*val = (*val >> (8 * (where & 3))) & 0xffff;
|
||||
switch (size) {
|
||||
case 1:
|
||||
*val = readb_relaxed(conf_data + (where & 3));
|
||||
break;
|
||||
case 2:
|
||||
*val = readw_relaxed(conf_data + (where & 2));
|
||||
break;
|
||||
case 4:
|
||||
*val = readl_relaxed(conf_data);
|
||||
break;
|
||||
}
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
@ -271,22 +301,24 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,
|
|||
struct pci_bus *bus,
|
||||
u32 devfn, int where, int size, u32 val)
|
||||
{
|
||||
u32 _val, shift = 8 * (where & 3);
|
||||
void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
|
||||
|
||||
mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
|
||||
PCIE_CONF_ADDR_OFF);
|
||||
_val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
|
||||
|
||||
if (size == 4)
|
||||
_val = val;
|
||||
else if (size == 2)
|
||||
_val = (_val & ~(0xffff << shift)) | ((val & 0xffff) << shift);
|
||||
else if (size == 1)
|
||||
_val = (_val & ~(0xff << shift)) | ((val & 0xff) << shift);
|
||||
else
|
||||
switch (size) {
|
||||
case 1:
|
||||
writeb(val, conf_data + (where & 3));
|
||||
break;
|
||||
case 2:
|
||||
writew(val, conf_data + (where & 2));
|
||||
break;
|
||||
case 4:
|
||||
writel(val, conf_data);
|
||||
break;
|
||||
default:
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
mvebu_writel(port, _val, PCIE_CONF_DATA_OFF);
|
||||
}
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
@ -443,6 +475,9 @@ static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)
|
|||
/* We support 32 bits I/O addressing */
|
||||
bridge->iobase = PCI_IO_RANGE_TYPE_32;
|
||||
bridge->iolimit = PCI_IO_RANGE_TYPE_32;
|
||||
|
||||
/* Add capabilities */
|
||||
bridge->status = PCI_STATUS_CAP_LIST;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -460,7 +495,7 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
|
|||
break;
|
||||
|
||||
case PCI_COMMAND:
|
||||
*value = bridge->command;
|
||||
*value = bridge->command | bridge->status << 16;
|
||||
break;
|
||||
|
||||
case PCI_CLASS_REVISION:
|
||||
|
@ -505,6 +540,10 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
|
|||
*value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
|
||||
break;
|
||||
|
||||
case PCI_CAPABILITY_LIST:
|
||||
*value = PCISWCAP;
|
||||
break;
|
||||
|
||||
case PCI_ROM_ADDRESS1:
|
||||
*value = 0;
|
||||
break;
|
||||
|
@ -514,9 +553,67 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
|
|||
*value = 0;
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_LIST_ID:
|
||||
/* Set PCIe v2, root port, slot support */
|
||||
*value = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2 |
|
||||
PCI_EXP_FLAGS_SLOT) << 16 | PCI_CAP_ID_EXP;
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_DEVCAP:
|
||||
*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_DEVCTL:
|
||||
*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL) &
|
||||
~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE |
|
||||
PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE);
|
||||
*value |= bridge->pcie_devctl;
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_LNKCAP:
|
||||
/*
|
||||
* PCIe requires the clock power management capability to be
|
||||
* hard-wired to zero for downstream ports
|
||||
*/
|
||||
*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) &
|
||||
~PCI_EXP_LNKCAP_CLKPM;
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_LNKCTL:
|
||||
*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_SLTCAP:
|
||||
*value = bridge->pcie_sltcap;
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_SLTCTL:
|
||||
*value = PCI_EXP_SLTSTA_PDS << 16;
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_RTCTL:
|
||||
*value = bridge->pcie_rtctl;
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_RTSTA:
|
||||
*value = mvebu_readl(port, PCIE_RC_RTSTA);
|
||||
break;
|
||||
|
||||
/* PCIe requires the v2 fields to be hard-wired to zero */
|
||||
case PCISWCAP_EXP_DEVCAP2:
|
||||
case PCISWCAP_EXP_DEVCTL2:
|
||||
case PCISWCAP_EXP_LNKCAP2:
|
||||
case PCISWCAP_EXP_LNKCTL2:
|
||||
case PCISWCAP_EXP_SLTCAP2:
|
||||
case PCISWCAP_EXP_SLTCTL2:
|
||||
default:
|
||||
*value = 0xffffffff;
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
/*
|
||||
* PCI defines configuration read accesses to reserved or
|
||||
* unimplemented registers to read as zero and complete
|
||||
* normally.
|
||||
*/
|
||||
*value = 0;
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
if (size == 2)
|
||||
|
@ -601,6 +698,51 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
|
|||
mvebu_pcie_set_local_bus_nr(port, bridge->secondary_bus);
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_DEVCTL:
|
||||
/*
|
||||
* Armada370 data says these bits must always
|
||||
* be zero when in root complex mode.
|
||||
*/
|
||||
value &= ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE |
|
||||
PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE);
|
||||
|
||||
/*
|
||||
* If the mask is 0xffff0000, then we only want to write
|
||||
* the device control register, rather than clearing the
|
||||
* RW1C bits in the device status register. Mask out the
|
||||
* status register bits.
|
||||
*/
|
||||
if (mask == 0xffff0000)
|
||||
value &= 0xffff;
|
||||
|
||||
mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL);
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_LNKCTL:
|
||||
/*
|
||||
* If we don't support CLKREQ, we must ensure that the
|
||||
* CLKREQ enable bit always reads zero. Since we haven't
|
||||
* had this capability, and it's dependent on board wiring,
|
||||
* disable it for the time being.
|
||||
*/
|
||||
value &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
|
||||
|
||||
/*
|
||||
* If the mask is 0xffff0000, then we only want to write
|
||||
* the link control register, rather than clearing the
|
||||
* RW1C bits in the link status register. Mask out the
|
||||
* status register bits.
|
||||
*/
|
||||
if (mask == 0xffff0000)
|
||||
value &= 0xffff;
|
||||
|
||||
mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
|
||||
break;
|
||||
|
||||
case PCISWCAP_EXP_RTSTA:
|
||||
mvebu_writel(port, value, PCIE_RC_RTSTA);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -652,17 +794,6 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
|
|||
if (!mvebu_pcie_link_up(port))
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
/*
|
||||
* On the secondary bus, we don't want to expose any other
|
||||
* device than the device physically connected in the PCIe
|
||||
* slot, visible in slot 0. In slot 1, there's a special
|
||||
* Marvell device that only makes sense when the Armada is
|
||||
* used as a PCIe endpoint.
|
||||
*/
|
||||
if (bus->number == port->bridge.secondary_bus &&
|
||||
PCI_SLOT(devfn) != 0)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
/* Access the real PCIe interface */
|
||||
ret = mvebu_pcie_hw_wr_conf(port, bus, devfn,
|
||||
where, size, val);
|
||||
|
@ -693,19 +824,6 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
|
|||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
* On the secondary bus, we don't want to expose any other
|
||||
* device than the device physically connected in the PCIe
|
||||
* slot, visible in slot 0. In slot 1, there's a special
|
||||
* Marvell device that only makes sense when the Armada is
|
||||
* used as a PCIe endpoint.
|
||||
*/
|
||||
if (bus->number == port->bridge.secondary_bus &&
|
||||
PCI_SLOT(devfn) != 0) {
|
||||
*val = 0xffffffff;
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* Access the real PCIe interface */
|
||||
ret = mvebu_pcie_hw_rd_conf(port, bus, devfn,
|
||||
where, size, val);
|
||||
|
@ -914,12 +1032,167 @@ static int mvebu_pcie_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mvebu_pcie_port_clk_put(void *data)
|
||||
{
|
||||
struct mvebu_pcie_port *port = data;
|
||||
|
||||
clk_put(port->clk);
|
||||
}
|
||||
|
||||
static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
|
||||
struct mvebu_pcie_port *port, struct device_node *child)
|
||||
{
|
||||
struct device *dev = &pcie->pdev->dev;
|
||||
enum of_gpio_flags flags;
|
||||
int reset_gpio, ret;
|
||||
|
||||
port->pcie = pcie;
|
||||
|
||||
if (of_property_read_u32(child, "marvell,pcie-port", &port->port)) {
|
||||
dev_warn(dev, "ignoring %s, missing pcie-port property\n",
|
||||
of_node_full_name(child));
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(child, "marvell,pcie-lane", &port->lane))
|
||||
port->lane = 0;
|
||||
|
||||
port->name = devm_kasprintf(dev, GFP_KERNEL, "pcie%d.%d", port->port,
|
||||
port->lane);
|
||||
if (!port->name) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
port->devfn = of_pci_get_devfn(child);
|
||||
if (port->devfn < 0)
|
||||
goto skip;
|
||||
|
||||
ret = mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_MEM,
|
||||
&port->mem_target, &port->mem_attr);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s: cannot get tgt/attr for mem window\n",
|
||||
port->name);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (resource_size(&pcie->io) != 0) {
|
||||
mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_IO,
|
||||
&port->io_target, &port->io_attr);
|
||||
} else {
|
||||
port->io_target = -1;
|
||||
port->io_attr = -1;
|
||||
}
|
||||
|
||||
reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags);
|
||||
if (reset_gpio == -EPROBE_DEFER) {
|
||||
ret = reset_gpio;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(reset_gpio)) {
|
||||
unsigned long gpio_flags;
|
||||
|
||||
port->reset_name = devm_kasprintf(dev, GFP_KERNEL, "%s-reset",
|
||||
port->name);
|
||||
if (!port->reset_name) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (flags & OF_GPIO_ACTIVE_LOW) {
|
||||
dev_info(dev, "%s: reset gpio is active low\n",
|
||||
of_node_full_name(child));
|
||||
gpio_flags = GPIOF_ACTIVE_LOW |
|
||||
GPIOF_OUT_INIT_LOW;
|
||||
} else {
|
||||
gpio_flags = GPIOF_OUT_INIT_HIGH;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request_one(dev, reset_gpio, gpio_flags,
|
||||
port->reset_name);
|
||||
if (ret) {
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto err;
|
||||
goto skip;
|
||||
}
|
||||
|
||||
port->reset_gpio = gpio_to_desc(reset_gpio);
|
||||
}
|
||||
|
||||
port->clk = of_clk_get_by_name(child, NULL);
|
||||
if (IS_ERR(port->clk)) {
|
||||
dev_err(dev, "%s: cannot get clock\n", port->name);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
ret = devm_add_action(dev, mvebu_pcie_port_clk_put, port);
|
||||
if (ret < 0) {
|
||||
clk_put(port->clk);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
skip:
|
||||
ret = 0;
|
||||
|
||||
/* In the case of skipping, we need to free these */
|
||||
devm_kfree(dev, port->reset_name);
|
||||
port->reset_name = NULL;
|
||||
devm_kfree(dev, port->name);
|
||||
port->name = NULL;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power up a PCIe port. PCIe requires the refclk to be stable for 100µs
|
||||
* prior to releasing PERST. See table 2-4 in section 2.6.2 AC Specifications
|
||||
* of the PCI Express Card Electromechanical Specification, 1.1.
|
||||
*/
|
||||
static int mvebu_pcie_powerup(struct mvebu_pcie_port *port)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(port->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (port->reset_gpio) {
|
||||
u32 reset_udelay = 20000;
|
||||
|
||||
of_property_read_u32(port->dn, "reset-delay-us",
|
||||
&reset_udelay);
|
||||
|
||||
udelay(100);
|
||||
|
||||
gpiod_set_value_cansleep(port->reset_gpio, 0);
|
||||
msleep(reset_udelay / 1000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power down a PCIe port. Strictly, PCIe requires us to place the card
|
||||
* in D3hot state before asserting PERST#.
|
||||
*/
|
||||
static void mvebu_pcie_powerdown(struct mvebu_pcie_port *port)
|
||||
{
|
||||
if (port->reset_gpio)
|
||||
gpiod_set_value_cansleep(port->reset_gpio, 1);
|
||||
|
||||
clk_disable_unprepare(port->clk);
|
||||
}
|
||||
|
||||
static int mvebu_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mvebu_pcie *pcie;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *child;
|
||||
int i, ret;
|
||||
int num, i, ret;
|
||||
|
||||
pcie = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pcie),
|
||||
GFP_KERNEL);
|
||||
|
@ -955,112 +1228,52 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for_each_child_of_node(pdev->dev.of_node, child) {
|
||||
if (!of_device_is_available(child))
|
||||
continue;
|
||||
i++;
|
||||
}
|
||||
num = of_get_available_child_count(pdev->dev.of_node);
|
||||
|
||||
pcie->ports = devm_kzalloc(&pdev->dev, i *
|
||||
sizeof(struct mvebu_pcie_port),
|
||||
pcie->ports = devm_kcalloc(&pdev->dev, num, sizeof(*pcie->ports),
|
||||
GFP_KERNEL);
|
||||
if (!pcie->ports)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
for_each_child_of_node(pdev->dev.of_node, child) {
|
||||
for_each_available_child_of_node(pdev->dev.of_node, child) {
|
||||
struct mvebu_pcie_port *port = &pcie->ports[i];
|
||||
enum of_gpio_flags flags;
|
||||
|
||||
if (!of_device_is_available(child))
|
||||
continue;
|
||||
|
||||
port->pcie = pcie;
|
||||
|
||||
if (of_property_read_u32(child, "marvell,pcie-port",
|
||||
&port->port)) {
|
||||
dev_warn(&pdev->dev,
|
||||
"ignoring PCIe DT node, missing pcie-port property\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(child, "marvell,pcie-lane",
|
||||
&port->lane))
|
||||
port->lane = 0;
|
||||
|
||||
port->name = kasprintf(GFP_KERNEL, "pcie%d.%d",
|
||||
port->port, port->lane);
|
||||
|
||||
port->devfn = of_pci_get_devfn(child);
|
||||
if (port->devfn < 0)
|
||||
continue;
|
||||
|
||||
ret = mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_MEM,
|
||||
&port->mem_target, &port->mem_attr);
|
||||
ret = mvebu_pcie_parse_port(pcie, port, child);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "PCIe%d.%d: cannot get tgt/attr for mem window\n",
|
||||
port->port, port->lane);
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
} else if (ret == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resource_size(&pcie->io) != 0)
|
||||
mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO,
|
||||
&port->io_target, &port->io_attr);
|
||||
else {
|
||||
port->io_target = -1;
|
||||
port->io_attr = -1;
|
||||
}
|
||||
port->dn = child;
|
||||
i++;
|
||||
}
|
||||
pcie->nports = i;
|
||||
|
||||
port->reset_gpio = of_get_named_gpio_flags(child,
|
||||
"reset-gpios", 0, &flags);
|
||||
if (gpio_is_valid(port->reset_gpio)) {
|
||||
u32 reset_udelay = 20000;
|
||||
for (i = 0; i < pcie->nports; i++) {
|
||||
struct mvebu_pcie_port *port = &pcie->ports[i];
|
||||
|
||||
port->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
|
||||
port->reset_name = kasprintf(GFP_KERNEL,
|
||||
"pcie%d.%d-reset", port->port, port->lane);
|
||||
of_property_read_u32(child, "reset-delay-us",
|
||||
&reset_udelay);
|
||||
|
||||
ret = devm_gpio_request_one(&pdev->dev,
|
||||
port->reset_gpio, GPIOF_DIR_OUT, port->reset_name);
|
||||
if (ret) {
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
continue;
|
||||
}
|
||||
|
||||
gpio_set_value(port->reset_gpio,
|
||||
(port->reset_active_low) ? 1 : 0);
|
||||
msleep(reset_udelay/1000);
|
||||
}
|
||||
|
||||
port->clk = of_clk_get_by_name(child, NULL);
|
||||
if (IS_ERR(port->clk)) {
|
||||
dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n",
|
||||
port->port, port->lane);
|
||||
child = port->dn;
|
||||
if (!child)
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(port->clk);
|
||||
if (ret)
|
||||
ret = mvebu_pcie_powerup(port);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
port->base = mvebu_pcie_map_registers(pdev, child, port);
|
||||
if (IS_ERR(port->base)) {
|
||||
dev_err(&pdev->dev, "PCIe%d.%d: cannot map registers\n",
|
||||
port->port, port->lane);
|
||||
dev_err(&pdev->dev, "%s: cannot map registers\n",
|
||||
port->name);
|
||||
port->base = NULL;
|
||||
clk_disable_unprepare(port->clk);
|
||||
mvebu_pcie_powerdown(port);
|
||||
continue;
|
||||
}
|
||||
|
||||
mvebu_pcie_set_local_dev_nr(port, 1);
|
||||
|
||||
port->dn = child;
|
||||
mvebu_sw_pci_bridge_init(port);
|
||||
i++;
|
||||
}
|
||||
|
||||
pcie->nports = i;
|
||||
|
|
|
@ -382,8 +382,8 @@ static unsigned long tegra_pcie_conf_offset(unsigned int devfn, int where)
|
|||
static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
|
||||
unsigned int busnr)
|
||||
{
|
||||
pgprot_t prot = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_XN |
|
||||
L_PTE_MT_DEV_SHARED | L_PTE_SHARED;
|
||||
pgprot_t prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
|
||||
L_PTE_XN | L_PTE_MT_DEV_SHARED | L_PTE_SHARED);
|
||||
phys_addr_t cs = pcie->cs->start;
|
||||
struct tegra_pcie_bus *bus;
|
||||
unsigned int i;
|
||||
|
|
|
@ -509,24 +509,6 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int xgene_pcie_msi_enable(struct pci_bus *bus)
|
||||
{
|
||||
struct device_node *msi_node;
|
||||
|
||||
msi_node = of_parse_phandle(bus->dev.of_node,
|
||||
"msi-parent", 0);
|
||||
if (!msi_node)
|
||||
return -ENODEV;
|
||||
|
||||
bus->msi = of_pci_find_msi_chip_by_node(msi_node);
|
||||
if (!bus->msi)
|
||||
return -ENODEV;
|
||||
|
||||
of_node_put(msi_node);
|
||||
bus->msi->dev = &bus->dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xgene_pcie_probe_bridge(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *dn = pdev->dev.of_node;
|
||||
|
@ -567,10 +549,6 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
|
|||
if (!bus)
|
||||
return -ENOMEM;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI))
|
||||
if (xgene_pcie_msi_enable(bus))
|
||||
dev_info(port->dev, "failed to enable MSI\n");
|
||||
|
||||
pci_scan_child_bus(bus);
|
||||
pci_assign_unassigned_bus_resources(bus);
|
||||
pci_bus_add_devices(bus);
|
||||
|
|
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
* Copyright Altera Corporation (C) 2013-2015. All rights reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define MSI_STATUS 0x0
|
||||
#define MSI_ERROR 0x4
|
||||
#define MSI_INTMASK 0x8
|
||||
|
||||
#define MAX_MSI_VECTORS 32
|
||||
|
||||
struct altera_msi {
|
||||
DECLARE_BITMAP(used, MAX_MSI_VECTORS);
|
||||
struct mutex lock; /* protect "used" bitmap */
|
||||
struct platform_device *pdev;
|
||||
struct irq_domain *msi_domain;
|
||||
struct irq_domain *inner_domain;
|
||||
void __iomem *csr_base;
|
||||
void __iomem *vector_base;
|
||||
phys_addr_t vector_phy;
|
||||
u32 num_of_vectors;
|
||||
int irq;
|
||||
};
|
||||
|
||||
static inline void msi_writel(struct altera_msi *msi, const u32 value,
|
||||
const u32 reg)
|
||||
{
|
||||
writel_relaxed(value, msi->csr_base + reg);
|
||||
}
|
||||
|
||||
static inline u32 msi_readl(struct altera_msi *msi, const u32 reg)
|
||||
{
|
||||
return readl_relaxed(msi->csr_base + reg);
|
||||
}
|
||||
|
||||
static void altera_msi_isr(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
struct altera_msi *msi;
|
||||
unsigned long status;
|
||||
u32 num_of_vectors;
|
||||
u32 bit;
|
||||
u32 virq;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
msi = irq_desc_get_handler_data(desc);
|
||||
num_of_vectors = msi->num_of_vectors;
|
||||
|
||||
while ((status = msi_readl(msi, MSI_STATUS)) != 0) {
|
||||
for_each_set_bit(bit, &status, msi->num_of_vectors) {
|
||||
/* Dummy read from vector to clear the interrupt */
|
||||
readl_relaxed(msi->vector_base + (bit * sizeof(u32)));
|
||||
|
||||
virq = irq_find_mapping(msi->inner_domain, bit);
|
||||
if (virq)
|
||||
generic_handle_irq(virq);
|
||||
else
|
||||
dev_err(&msi->pdev->dev, "unexpected MSI\n");
|
||||
}
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static struct irq_chip altera_msi_irq_chip = {
|
||||
.name = "Altera PCIe MSI",
|
||||
.irq_mask = pci_msi_mask_irq,
|
||||
.irq_unmask = pci_msi_unmask_irq,
|
||||
};
|
||||
|
||||
static struct msi_domain_info altera_msi_domain_info = {
|
||||
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
|
||||
MSI_FLAG_PCI_MSIX),
|
||||
.chip = &altera_msi_irq_chip,
|
||||
};
|
||||
|
||||
static void altera_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
{
|
||||
struct altera_msi *msi = irq_data_get_irq_chip_data(data);
|
||||
phys_addr_t addr = msi->vector_phy + (data->hwirq * sizeof(u32));
|
||||
|
||||
msg->address_lo = lower_32_bits(addr);
|
||||
msg->address_hi = upper_32_bits(addr);
|
||||
msg->data = data->hwirq;
|
||||
|
||||
dev_dbg(&msi->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n",
|
||||
(int)data->hwirq, msg->address_hi, msg->address_lo);
|
||||
}
|
||||
|
||||
static int altera_msi_set_affinity(struct irq_data *irq_data,
|
||||
const struct cpumask *mask, bool force)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct irq_chip altera_msi_bottom_irq_chip = {
|
||||
.name = "Altera MSI",
|
||||
.irq_compose_msi_msg = altera_compose_msi_msg,
|
||||
.irq_set_affinity = altera_msi_set_affinity,
|
||||
};
|
||||
|
||||
static int altera_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs, void *args)
|
||||
{
|
||||
struct altera_msi *msi = domain->host_data;
|
||||
unsigned long bit;
|
||||
u32 mask;
|
||||
|
||||
WARN_ON(nr_irqs != 1);
|
||||
mutex_lock(&msi->lock);
|
||||
|
||||
bit = find_first_zero_bit(msi->used, msi->num_of_vectors);
|
||||
if (bit >= msi->num_of_vectors) {
|
||||
mutex_unlock(&msi->lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
set_bit(bit, msi->used);
|
||||
|
||||
mutex_unlock(&msi->lock);
|
||||
|
||||
irq_domain_set_info(domain, virq, bit, &altera_msi_bottom_irq_chip,
|
||||
domain->host_data, handle_simple_irq,
|
||||
NULL, NULL);
|
||||
|
||||
mask = msi_readl(msi, MSI_INTMASK);
|
||||
mask |= 1 << bit;
|
||||
msi_writel(msi, mask, MSI_INTMASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void altera_irq_domain_free(struct irq_domain *domain,
|
||||
unsigned int virq, unsigned int nr_irqs)
|
||||
{
|
||||
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
|
||||
struct altera_msi *msi = irq_data_get_irq_chip_data(d);
|
||||
u32 mask;
|
||||
|
||||
mutex_lock(&msi->lock);
|
||||
|
||||
if (!test_bit(d->hwirq, msi->used)) {
|
||||
dev_err(&msi->pdev->dev, "trying to free unused MSI#%lu\n",
|
||||
d->hwirq);
|
||||
} else {
|
||||
__clear_bit(d->hwirq, msi->used);
|
||||
mask = msi_readl(msi, MSI_INTMASK);
|
||||
mask &= ~(1 << d->hwirq);
|
||||
msi_writel(msi, mask, MSI_INTMASK);
|
||||
}
|
||||
|
||||
mutex_unlock(&msi->lock);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops msi_domain_ops = {
|
||||
.alloc = altera_irq_domain_alloc,
|
||||
.free = altera_irq_domain_free,
|
||||
};
|
||||
|
||||
static int altera_allocate_domains(struct altera_msi *msi)
|
||||
{
|
||||
struct fwnode_handle *fwnode = of_node_to_fwnode(msi->pdev->dev.of_node);
|
||||
|
||||
msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
|
||||
&msi_domain_ops, msi);
|
||||
if (!msi->inner_domain) {
|
||||
dev_err(&msi->pdev->dev, "failed to create IRQ domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
msi->msi_domain = pci_msi_create_irq_domain(fwnode,
|
||||
&altera_msi_domain_info, msi->inner_domain);
|
||||
if (!msi->msi_domain) {
|
||||
dev_err(&msi->pdev->dev, "failed to create MSI domain\n");
|
||||
irq_domain_remove(msi->inner_domain);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void altera_free_domains(struct altera_msi *msi)
|
||||
{
|
||||
irq_domain_remove(msi->msi_domain);
|
||||
irq_domain_remove(msi->inner_domain);
|
||||
}
|
||||
|
||||
static int altera_msi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct altera_msi *msi = platform_get_drvdata(pdev);
|
||||
|
||||
msi_writel(msi, 0, MSI_INTMASK);
|
||||
irq_set_chained_handler(msi->irq, NULL);
|
||||
irq_set_handler_data(msi->irq, NULL);
|
||||
|
||||
altera_free_domains(msi);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int altera_msi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct altera_msi *msi;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
msi = devm_kzalloc(&pdev->dev, sizeof(struct altera_msi),
|
||||
GFP_KERNEL);
|
||||
if (!msi)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&msi->lock);
|
||||
msi->pdev = pdev;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no csr memory resource defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
msi->csr_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(msi->csr_base)) {
|
||||
dev_err(&pdev->dev, "failed to map csr memory\n");
|
||||
return PTR_ERR(msi->csr_base);
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"vector_slave");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no vector_slave memory resource defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
msi->vector_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(msi->vector_base)) {
|
||||
dev_err(&pdev->dev, "failed to map vector_slave memory\n");
|
||||
return PTR_ERR(msi->vector_base);
|
||||
}
|
||||
|
||||
msi->vector_phy = res->start;
|
||||
|
||||
if (of_property_read_u32(np, "num-vectors", &msi->num_of_vectors)) {
|
||||
dev_err(&pdev->dev, "failed to parse the number of vectors\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = altera_allocate_domains(msi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msi->irq = platform_get_irq(pdev, 0);
|
||||
if (msi->irq <= 0) {
|
||||
dev_err(&pdev->dev, "failed to map IRQ: %d\n", msi->irq);
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
irq_set_chained_handler_and_data(msi->irq, altera_msi_isr, msi);
|
||||
platform_set_drvdata(pdev, msi);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
altera_msi_remove(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id altera_msi_of_match[] = {
|
||||
{ .compatible = "altr,msi-1.0", NULL },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver altera_msi_driver = {
|
||||
.driver = {
|
||||
.name = "altera-msi",
|
||||
.of_match_table = altera_msi_of_match,
|
||||
},
|
||||
.probe = altera_msi_probe,
|
||||
.remove = altera_msi_remove,
|
||||
};
|
||||
|
||||
static int __init altera_msi_init(void)
|
||||
{
|
||||
return platform_driver_register(&altera_msi_driver);
|
||||
}
|
||||
subsys_initcall(altera_msi_init);
|
||||
|
||||
MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
|
||||
MODULE_DESCRIPTION("Altera PCIe MSI support");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,579 @@
|
|||
/*
|
||||
* Copyright Altera Corporation (C) 2013-2015. All rights reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define RP_TX_REG0 0x2000
|
||||
#define RP_TX_REG1 0x2004
|
||||
#define RP_TX_CNTRL 0x2008
|
||||
#define RP_TX_EOP 0x2
|
||||
#define RP_TX_SOP 0x1
|
||||
#define RP_RXCPL_STATUS 0x2010
|
||||
#define RP_RXCPL_EOP 0x2
|
||||
#define RP_RXCPL_SOP 0x1
|
||||
#define RP_RXCPL_REG0 0x2014
|
||||
#define RP_RXCPL_REG1 0x2018
|
||||
#define P2A_INT_STATUS 0x3060
|
||||
#define P2A_INT_STS_ALL 0xf
|
||||
#define P2A_INT_ENABLE 0x3070
|
||||
#define P2A_INT_ENA_ALL 0xf
|
||||
#define RP_LTSSM 0x3c64
|
||||
#define LTSSM_L0 0xf
|
||||
|
||||
/* TLP configuration type 0 and 1 */
|
||||
#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
|
||||
#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
|
||||
#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
|
||||
#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */
|
||||
#define TLP_PAYLOAD_SIZE 0x01
|
||||
#define TLP_READ_TAG 0x1d
|
||||
#define TLP_WRITE_TAG 0x10
|
||||
#define TLP_CFG_DW0(fmttype) (((fmttype) << 24) | TLP_PAYLOAD_SIZE)
|
||||
#define TLP_CFG_DW1(reqid, tag, be) (((reqid) << 16) | (tag << 8) | (be))
|
||||
#define TLP_CFG_DW2(bus, devfn, offset) \
|
||||
(((bus) << 24) | ((devfn) << 16) | (offset))
|
||||
#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn))
|
||||
#define TLP_HDR_SIZE 3
|
||||
#define TLP_LOOP 500
|
||||
|
||||
#define INTX_NUM 4
|
||||
|
||||
#define DWORD_MASK 3
|
||||
|
||||
struct altera_pcie {
|
||||
struct platform_device *pdev;
|
||||
void __iomem *cra_base;
|
||||
int irq;
|
||||
u8 root_bus_nr;
|
||||
struct irq_domain *irq_domain;
|
||||
struct resource bus_range;
|
||||
struct list_head resources;
|
||||
};
|
||||
|
||||
struct tlp_rp_regpair_t {
|
||||
u32 ctrl;
|
||||
u32 reg0;
|
||||
u32 reg1;
|
||||
};
|
||||
|
||||
static void altera_pcie_retrain(struct pci_dev *dev)
|
||||
{
|
||||
u16 linkcap, linkstat;
|
||||
|
||||
/*
|
||||
* Set the retrain bit if the PCIe rootport support > 2.5GB/s, but
|
||||
* current speed is 2.5 GB/s.
|
||||
*/
|
||||
pcie_capability_read_word(dev, PCI_EXP_LNKCAP, &linkcap);
|
||||
|
||||
if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
|
||||
return;
|
||||
|
||||
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &linkstat);
|
||||
if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB)
|
||||
pcie_capability_set_word(dev, PCI_EXP_LNKCTL,
|
||||
PCI_EXP_LNKCTL_RL);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_EARLY(0x1172, PCI_ANY_ID, altera_pcie_retrain);
|
||||
|
||||
/*
|
||||
* Altera PCIe port uses BAR0 of RC's configuration space as the translation
|
||||
* from PCI bus to native BUS. Entire DDR region is mapped into PCIe space
|
||||
* using these registers, so it can be reached by DMA from EP devices.
|
||||
* This BAR0 will also access to MSI vector when receiving MSI/MSIX interrupt
|
||||
* from EP devices, eventually trigger interrupt to GIC. The BAR0 of bridge
|
||||
* should be hidden during enumeration to avoid the sizing and resource
|
||||
* allocation by PCIe core.
|
||||
*/
|
||||
static bool altera_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn,
|
||||
int offset)
|
||||
{
|
||||
if (pci_is_root_bus(bus) && (devfn == 0) &&
|
||||
(offset == PCI_BASE_ADDRESS_0))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void cra_writel(struct altera_pcie *pcie, const u32 value,
|
||||
const u32 reg)
|
||||
{
|
||||
writel_relaxed(value, pcie->cra_base + reg);
|
||||
}
|
||||
|
||||
static inline u32 cra_readl(struct altera_pcie *pcie, const u32 reg)
|
||||
{
|
||||
return readl_relaxed(pcie->cra_base + reg);
|
||||
}
|
||||
|
||||
static void tlp_write_tx(struct altera_pcie *pcie,
|
||||
struct tlp_rp_regpair_t *tlp_rp_regdata)
|
||||
{
|
||||
cra_writel(pcie, tlp_rp_regdata->reg0, RP_TX_REG0);
|
||||
cra_writel(pcie, tlp_rp_regdata->reg1, RP_TX_REG1);
|
||||
cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL);
|
||||
}
|
||||
|
||||
static bool altera_pcie_link_is_up(struct altera_pcie *pcie)
|
||||
{
|
||||
return !!(cra_readl(pcie, RP_LTSSM) & LTSSM_L0);
|
||||
}
|
||||
|
||||
static bool altera_pcie_valid_config(struct altera_pcie *pcie,
|
||||
struct pci_bus *bus, int dev)
|
||||
{
|
||||
/* If there is no link, then there is no device */
|
||||
if (bus->number != pcie->root_bus_nr) {
|
||||
if (!altera_pcie_link_is_up(pcie))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* access only one slot on each root port */
|
||||
if (bus->number == pcie->root_bus_nr && dev > 0)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Do not read more than one device on the bus directly attached
|
||||
* to root port, root port can only attach to one downstream port.
|
||||
*/
|
||||
if (bus->primary == pcie->root_bus_nr && dev > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
|
||||
{
|
||||
u8 loop;
|
||||
bool sop = 0;
|
||||
u32 ctrl;
|
||||
u32 reg0, reg1;
|
||||
|
||||
/*
|
||||
* Minimum 2 loops to read TLP headers and 1 loop to read data
|
||||
* payload.
|
||||
*/
|
||||
for (loop = 0; loop < TLP_LOOP; loop++) {
|
||||
ctrl = cra_readl(pcie, RP_RXCPL_STATUS);
|
||||
if ((ctrl & RP_RXCPL_SOP) || (ctrl & RP_RXCPL_EOP) || sop) {
|
||||
reg0 = cra_readl(pcie, RP_RXCPL_REG0);
|
||||
reg1 = cra_readl(pcie, RP_RXCPL_REG1);
|
||||
|
||||
if (ctrl & RP_RXCPL_SOP)
|
||||
sop = true;
|
||||
|
||||
if (ctrl & RP_RXCPL_EOP) {
|
||||
if (value)
|
||||
*value = reg0;
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
}
|
||||
udelay(5);
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers,
|
||||
u32 data, bool align)
|
||||
{
|
||||
struct tlp_rp_regpair_t tlp_rp_regdata;
|
||||
|
||||
tlp_rp_regdata.reg0 = headers[0];
|
||||
tlp_rp_regdata.reg1 = headers[1];
|
||||
tlp_rp_regdata.ctrl = RP_TX_SOP;
|
||||
tlp_write_tx(pcie, &tlp_rp_regdata);
|
||||
|
||||
if (align) {
|
||||
tlp_rp_regdata.reg0 = headers[2];
|
||||
tlp_rp_regdata.reg1 = 0;
|
||||
tlp_rp_regdata.ctrl = 0;
|
||||
tlp_write_tx(pcie, &tlp_rp_regdata);
|
||||
|
||||
tlp_rp_regdata.reg0 = data;
|
||||
tlp_rp_regdata.reg1 = 0;
|
||||
} else {
|
||||
tlp_rp_regdata.reg0 = headers[2];
|
||||
tlp_rp_regdata.reg1 = data;
|
||||
}
|
||||
|
||||
tlp_rp_regdata.ctrl = RP_TX_EOP;
|
||||
tlp_write_tx(pcie, &tlp_rp_regdata);
|
||||
}
|
||||
|
||||
static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
|
||||
int where, u8 byte_en, u32 *value)
|
||||
{
|
||||
u32 headers[TLP_HDR_SIZE];
|
||||
|
||||
if (bus == pcie->root_bus_nr)
|
||||
headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD0);
|
||||
else
|
||||
headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD1);
|
||||
|
||||
headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn),
|
||||
TLP_READ_TAG, byte_en);
|
||||
headers[2] = TLP_CFG_DW2(bus, devfn, where);
|
||||
|
||||
tlp_write_packet(pcie, headers, 0, false);
|
||||
|
||||
return tlp_read_packet(pcie, value);
|
||||
}
|
||||
|
||||
static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
|
||||
int where, u8 byte_en, u32 value)
|
||||
{
|
||||
u32 headers[TLP_HDR_SIZE];
|
||||
int ret;
|
||||
|
||||
if (bus == pcie->root_bus_nr)
|
||||
headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR0);
|
||||
else
|
||||
headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR1);
|
||||
|
||||
headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn),
|
||||
TLP_WRITE_TAG, byte_en);
|
||||
headers[2] = TLP_CFG_DW2(bus, devfn, where);
|
||||
|
||||
/* check alignment to Qword */
|
||||
if ((where & 0x7) == 0)
|
||||
tlp_write_packet(pcie, headers, value, true);
|
||||
else
|
||||
tlp_write_packet(pcie, headers, value, false);
|
||||
|
||||
ret = tlp_read_packet(pcie, NULL);
|
||||
if (ret != PCIBIOS_SUCCESSFUL)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Monitor changes to PCI_PRIMARY_BUS register on root port
|
||||
* and update local copy of root bus number accordingly.
|
||||
*/
|
||||
if ((bus == pcie->root_bus_nr) && (where == PCI_PRIMARY_BUS))
|
||||
pcie->root_bus_nr = (u8)(value);
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 *value)
|
||||
{
|
||||
struct altera_pcie *pcie = bus->sysdata;
|
||||
int ret;
|
||||
u32 data;
|
||||
u8 byte_en;
|
||||
|
||||
if (altera_pcie_hide_rc_bar(bus, devfn, where))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) {
|
||||
*value = 0xffffffff;
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
byte_en = 1 << (where & 3);
|
||||
break;
|
||||
case 2:
|
||||
byte_en = 3 << (where & 3);
|
||||
break;
|
||||
default:
|
||||
byte_en = 0xf;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = tlp_cfg_dword_read(pcie, bus->number, devfn,
|
||||
(where & ~DWORD_MASK), byte_en, &data);
|
||||
if (ret != PCIBIOS_SUCCESSFUL)
|
||||
return ret;
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
*value = (data >> (8 * (where & 0x3))) & 0xff;
|
||||
break;
|
||||
case 2:
|
||||
*value = (data >> (8 * (where & 0x2))) & 0xffff;
|
||||
break;
|
||||
default:
|
||||
*value = data;
|
||||
break;
|
||||
}
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 value)
|
||||
{
|
||||
struct altera_pcie *pcie = bus->sysdata;
|
||||
u32 data32;
|
||||
u32 shift = 8 * (where & 3);
|
||||
u8 byte_en;
|
||||
|
||||
if (altera_pcie_hide_rc_bar(bus, devfn, where))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn)))
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
data32 = (value & 0xff) << shift;
|
||||
byte_en = 1 << (where & 3);
|
||||
break;
|
||||
case 2:
|
||||
data32 = (value & 0xffff) << shift;
|
||||
byte_en = 3 << (where & 3);
|
||||
break;
|
||||
default:
|
||||
data32 = value;
|
||||
byte_en = 0xf;
|
||||
break;
|
||||
}
|
||||
|
||||
return tlp_cfg_dword_write(pcie, bus->number, devfn,
|
||||
(where & ~DWORD_MASK), byte_en, data32);
|
||||
}
|
||||
|
||||
static struct pci_ops altera_pcie_ops = {
|
||||
.read = altera_pcie_cfg_read,
|
||||
.write = altera_pcie_cfg_write,
|
||||
};
|
||||
|
||||
static int altera_pcie_intx_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 intx_domain_ops = {
|
||||
.map = altera_pcie_intx_map,
|
||||
};
|
||||
|
||||
static void altera_pcie_isr(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
struct altera_pcie *pcie;
|
||||
unsigned long status;
|
||||
u32 bit;
|
||||
u32 virq;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
pcie = irq_desc_get_handler_data(desc);
|
||||
|
||||
while ((status = cra_readl(pcie, P2A_INT_STATUS)
|
||||
& P2A_INT_STS_ALL) != 0) {
|
||||
for_each_set_bit(bit, &status, INTX_NUM) {
|
||||
/* clear interrupts */
|
||||
cra_writel(pcie, 1 << bit, P2A_INT_STATUS);
|
||||
|
||||
virq = irq_find_mapping(pcie->irq_domain, bit + 1);
|
||||
if (virq)
|
||||
generic_handle_irq(virq);
|
||||
else
|
||||
dev_err(&pcie->pdev->dev,
|
||||
"unexpected IRQ, INT%d\n", bit);
|
||||
}
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static void altera_pcie_release_of_pci_ranges(struct altera_pcie *pcie)
|
||||
{
|
||||
pci_free_resource_list(&pcie->resources);
|
||||
}
|
||||
|
||||
static int altera_pcie_parse_request_of_pci_ranges(struct altera_pcie *pcie)
|
||||
{
|
||||
int err, res_valid = 0;
|
||||
struct device *dev = &pcie->pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct resource_entry *win;
|
||||
|
||||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pcie->resources,
|
||||
NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
resource_list_for_each_entry(win, &pcie->resources) {
|
||||
struct resource *parent, *res = win->res;
|
||||
|
||||
switch (resource_type(res)) {
|
||||
case IORESOURCE_MEM:
|
||||
parent = &iomem_resource;
|
||||
res_valid |= !(res->flags & IORESOURCE_PREFETCH);
|
||||
break;
|
||||
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:
|
||||
altera_pcie_release_of_pci_ranges(pcie);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
|
||||
{
|
||||
struct device *dev = &pcie->pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
/* Setup INTx */
|
||||
pcie->irq_domain = irq_domain_add_linear(node, INTX_NUM,
|
||||
&intx_domain_ops, pcie);
|
||||
if (!pcie->irq_domain) {
|
||||
dev_err(dev, "Failed to get a INTx IRQ domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int altera_pcie_parse_dt(struct altera_pcie *pcie)
|
||||
{
|
||||
struct resource *cra;
|
||||
struct platform_device *pdev = pcie->pdev;
|
||||
|
||||
cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra");
|
||||
if (!cra) {
|
||||
dev_err(&pdev->dev, "no Cra memory resource defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pcie->cra_base = devm_ioremap_resource(&pdev->dev, cra);
|
||||
if (IS_ERR(pcie->cra_base)) {
|
||||
dev_err(&pdev->dev, "failed to map cra memory\n");
|
||||
return PTR_ERR(pcie->cra_base);
|
||||
}
|
||||
|
||||
/* setup IRQ */
|
||||
pcie->irq = platform_get_irq(pdev, 0);
|
||||
if (pcie->irq <= 0) {
|
||||
dev_err(&pdev->dev, "failed to get IRQ: %d\n", pcie->irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int altera_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct altera_pcie *pcie;
|
||||
struct pci_bus *bus;
|
||||
struct pci_bus *child;
|
||||
int ret;
|
||||
|
||||
pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
|
||||
if (!pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
pcie->pdev = pdev;
|
||||
|
||||
ret = altera_pcie_parse_dt(pcie);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Parsing DT failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&pcie->resources);
|
||||
|
||||
ret = altera_pcie_parse_request_of_pci_ranges(pcie);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed add resources\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = altera_pcie_init_irq_domain(pcie);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed creating IRQ Domain\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* clear all interrupts */
|
||||
cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS);
|
||||
/* enable all interrupts */
|
||||
cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE);
|
||||
|
||||
bus = pci_scan_root_bus(&pdev->dev, pcie->root_bus_nr, &altera_pcie_ops,
|
||||
pcie, &pcie->resources);
|
||||
if (!bus)
|
||||
return -ENOMEM;
|
||||
|
||||
pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
|
||||
pci_assign_unassigned_bus_resources(bus);
|
||||
|
||||
/* Configure PCI Express setting. */
|
||||
list_for_each_entry(child, &bus->children, node)
|
||||
pcie_bus_configure_settings(child);
|
||||
|
||||
pci_bus_add_devices(bus);
|
||||
|
||||
platform_set_drvdata(pdev, pcie);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id altera_pcie_of_match[] = {
|
||||
{ .compatible = "altr,pcie-root-port-1.0", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, altera_pcie_of_match);
|
||||
|
||||
static struct platform_driver altera_pcie_driver = {
|
||||
.probe = altera_pcie_probe,
|
||||
.driver = {
|
||||
.name = "altera-pcie",
|
||||
.of_match_table = altera_pcie_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
static int altera_pcie_init(void)
|
||||
{
|
||||
return platform_driver_register(&altera_pcie_driver);
|
||||
}
|
||||
module_init(altera_pcie_init);
|
||||
|
||||
MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
|
||||
MODULE_DESCRIPTION("Altera PCIe host controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
|
||||
#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
|
||||
#define PORT_LOGIC_LINK_WIDTH_MASK (0x1ff << 8)
|
||||
#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8)
|
||||
#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8)
|
||||
#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8)
|
||||
#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8)
|
||||
|
@ -69,39 +69,40 @@
|
|||
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
|
||||
#define PCIE_ATU_UPPER_TARGET 0x91C
|
||||
|
||||
static struct hw_pci dw_pci;
|
||||
static struct pci_ops dw_pcie_ops;
|
||||
|
||||
static unsigned long global_io_offset;
|
||||
|
||||
static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys)
|
||||
int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
|
||||
{
|
||||
BUG_ON(!sys->private_data);
|
||||
|
||||
return sys->private_data;
|
||||
}
|
||||
|
||||
int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val)
|
||||
{
|
||||
*val = readl(addr);
|
||||
|
||||
if (size == 1)
|
||||
*val = (*val >> (8 * (where & 3))) & 0xff;
|
||||
else if (size == 2)
|
||||
*val = (*val >> (8 * (where & 3))) & 0xffff;
|
||||
else if (size != 4)
|
||||
if ((uintptr_t)addr & (size - 1)) {
|
||||
*val = 0;
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
}
|
||||
|
||||
if (size == 4)
|
||||
*val = readl(addr);
|
||||
else if (size == 2)
|
||||
*val = readw(addr);
|
||||
else if (size == 1)
|
||||
*val = readb(addr);
|
||||
else {
|
||||
*val = 0;
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
}
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val)
|
||||
int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val)
|
||||
{
|
||||
if ((uintptr_t)addr & (size - 1))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
if (size == 4)
|
||||
writel(val, addr);
|
||||
else if (size == 2)
|
||||
writew(val, addr + (where & 2));
|
||||
writew(val, addr);
|
||||
else if (size == 1)
|
||||
writeb(val, addr + (where & 3));
|
||||
writeb(val, addr);
|
||||
else
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
|
@ -132,8 +133,7 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
|
|||
if (pp->ops->rd_own_conf)
|
||||
ret = pp->ops->rd_own_conf(pp, where, size, val);
|
||||
else
|
||||
ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where,
|
||||
size, val);
|
||||
ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -146,8 +146,7 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
|
|||
if (pp->ops->wr_own_conf)
|
||||
ret = pp->ops->wr_own_conf(pp, where, size, val);
|
||||
else
|
||||
ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where,
|
||||
size, val);
|
||||
ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -205,12 +204,16 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
|
|||
|
||||
void dw_pcie_msi_init(struct pcie_port *pp)
|
||||
{
|
||||
u64 msi_target;
|
||||
|
||||
pp->msi_data = __get_free_pages(GFP_KERNEL, 0);
|
||||
msi_target = virt_to_phys((void *)pp->msi_data);
|
||||
|
||||
/* program the msi_data */
|
||||
dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
|
||||
virt_to_phys((void *)pp->msi_data));
|
||||
dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
|
||||
(u32)(msi_target & 0xffffffff));
|
||||
dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4,
|
||||
(u32)(msi_target >> 32 & 0xffffffff));
|
||||
}
|
||||
|
||||
static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
|
||||
|
@ -255,7 +258,7 @@ static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
|
|||
static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
|
||||
{
|
||||
int irq, pos0, i;
|
||||
struct pcie_port *pp = sys_to_pcie(msi_desc_to_pci_sysdata(desc));
|
||||
struct pcie_port *pp = (struct pcie_port *) msi_desc_to_pci_sysdata(desc);
|
||||
|
||||
pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS,
|
||||
order_base_2(no_irqs));
|
||||
|
@ -286,6 +289,9 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
|
|||
}
|
||||
|
||||
*pos = pos0;
|
||||
desc->nvec_used = no_irqs;
|
||||
desc->msi_attrib.multiple = order_base_2(no_irqs);
|
||||
|
||||
return irq;
|
||||
|
||||
no_valid_irq:
|
||||
|
@ -293,12 +299,32 @@ no_valid_irq:
|
|||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos)
|
||||
{
|
||||
struct msi_msg msg;
|
||||
u64 msi_target;
|
||||
|
||||
if (pp->ops->get_msi_addr)
|
||||
msi_target = pp->ops->get_msi_addr(pp);
|
||||
else
|
||||
msi_target = virt_to_phys((void *)pp->msi_data);
|
||||
|
||||
msg.address_lo = (u32)(msi_target & 0xffffffff);
|
||||
msg.address_hi = (u32)(msi_target >> 32 & 0xffffffff);
|
||||
|
||||
if (pp->ops->get_msi_data)
|
||||
msg.data = pp->ops->get_msi_data(pp, pos);
|
||||
else
|
||||
msg.data = pos;
|
||||
|
||||
pci_write_msi_msg(irq, &msg);
|
||||
}
|
||||
|
||||
static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
|
||||
struct msi_desc *desc)
|
||||
{
|
||||
int irq, pos;
|
||||
struct msi_msg msg;
|
||||
struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata);
|
||||
struct pcie_port *pp = pdev->bus->sysdata;
|
||||
|
||||
if (desc->msi_attrib.is_msix)
|
||||
return -EINVAL;
|
||||
|
@ -307,33 +333,50 @@ static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
|
|||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
if (pp->ops->get_msi_addr)
|
||||
msg.address_lo = pp->ops->get_msi_addr(pp);
|
||||
else
|
||||
msg.address_lo = virt_to_phys((void *)pp->msi_data);
|
||||
msg.address_hi = 0x0;
|
||||
|
||||
if (pp->ops->get_msi_data)
|
||||
msg.data = pp->ops->get_msi_data(pp, pos);
|
||||
else
|
||||
msg.data = pos;
|
||||
|
||||
pci_write_msi_msg(irq, &msg);
|
||||
dw_msi_setup_msg(pp, irq, pos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_msi_setup_irqs(struct msi_controller *chip, struct pci_dev *pdev,
|
||||
int nvec, int type)
|
||||
{
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
int irq, pos;
|
||||
struct msi_desc *desc;
|
||||
struct pcie_port *pp = pdev->bus->sysdata;
|
||||
|
||||
/* MSI-X interrupts are not supported */
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
return -EINVAL;
|
||||
|
||||
WARN_ON(!list_is_singular(&pdev->dev.msi_list));
|
||||
desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list);
|
||||
|
||||
irq = assign_irq(nvec, desc, &pos);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
dw_msi_setup_msg(pp, irq, pos);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -EINVAL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
|
||||
{
|
||||
struct irq_data *data = irq_get_irq_data(irq);
|
||||
struct msi_desc *msi = irq_data_get_msi_desc(data);
|
||||
struct pcie_port *pp = sys_to_pcie(msi_desc_to_pci_sysdata(msi));
|
||||
struct pcie_port *pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi);
|
||||
|
||||
clear_irq_range(pp, irq, 1, data->hwirq);
|
||||
}
|
||||
|
||||
static struct msi_controller dw_pcie_msi_chip = {
|
||||
.setup_irq = dw_msi_setup_irq,
|
||||
.setup_irqs = dw_msi_setup_irqs,
|
||||
.teardown_irq = dw_msi_teardown_irq,
|
||||
};
|
||||
|
||||
|
@ -362,18 +405,12 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
{
|
||||
struct device_node *np = pp->dev->of_node;
|
||||
struct platform_device *pdev = to_platform_device(pp->dev);
|
||||
struct of_pci_range range;
|
||||
struct of_pci_range_parser parser;
|
||||
struct pci_bus *bus, *child;
|
||||
struct resource *cfg_res;
|
||||
u32 val, na, ns;
|
||||
const __be32 *addrp;
|
||||
int i, index, ret;
|
||||
|
||||
/* Find the address cell size and the number of cells in order to get
|
||||
* the untranslated address.
|
||||
*/
|
||||
of_property_read_u32(np, "#address-cells", &na);
|
||||
ns = of_n_size_cells(np);
|
||||
u32 val;
|
||||
int i, ret;
|
||||
LIST_HEAD(res);
|
||||
struct resource_entry *win;
|
||||
|
||||
cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
|
||||
if (cfg_res) {
|
||||
|
@ -381,88 +418,61 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
pp->cfg1_size = resource_size(cfg_res)/2;
|
||||
pp->cfg0_base = cfg_res->start;
|
||||
pp->cfg1_base = cfg_res->start + pp->cfg0_size;
|
||||
|
||||
/* Find the untranslated configuration space address */
|
||||
index = of_property_match_string(np, "reg-names", "config");
|
||||
addrp = of_get_address(np, index, NULL, NULL);
|
||||
pp->cfg0_mod_base = of_read_number(addrp, ns);
|
||||
pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size;
|
||||
} else if (!pp->va_cfg0_base) {
|
||||
dev_err(pp->dev, "missing *config* reg space\n");
|
||||
}
|
||||
|
||||
if (of_pci_range_parser_init(&parser, np)) {
|
||||
dev_err(pp->dev, "missing ranges property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &pp->io_base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Get the I/O and memory ranges from DT */
|
||||
for_each_of_pci_range(&parser, &range) {
|
||||
unsigned long restype = range.flags & IORESOURCE_TYPE_BITS;
|
||||
|
||||
if (restype == IORESOURCE_IO) {
|
||||
of_pci_range_to_resource(&range, np, &pp->io);
|
||||
pp->io.name = "I/O";
|
||||
pp->io.start = max_t(resource_size_t,
|
||||
PCIBIOS_MIN_IO,
|
||||
range.pci_addr + global_io_offset);
|
||||
pp->io.end = min_t(resource_size_t,
|
||||
IO_SPACE_LIMIT,
|
||||
range.pci_addr + range.size
|
||||
+ global_io_offset - 1);
|
||||
pp->io_size = resource_size(&pp->io);
|
||||
pp->io_bus_addr = range.pci_addr;
|
||||
pp->io_base = range.cpu_addr;
|
||||
|
||||
/* Find the untranslated IO space address */
|
||||
pp->io_mod_base = of_read_number(parser.range -
|
||||
parser.np + na, ns);
|
||||
resource_list_for_each_entry(win, &res) {
|
||||
switch (resource_type(win->res)) {
|
||||
case IORESOURCE_IO:
|
||||
pp->io = win->res;
|
||||
pp->io->name = "I/O";
|
||||
pp->io_size = resource_size(pp->io);
|
||||
pp->io_bus_addr = pp->io->start - win->offset;
|
||||
ret = pci_remap_iospace(pp->io, pp->io_base);
|
||||
if (ret) {
|
||||
dev_warn(pp->dev, "error %d: failed to map resource %pR\n",
|
||||
ret, pp->io);
|
||||
continue;
|
||||
}
|
||||
pp->io_base = pp->io->start;
|
||||
break;
|
||||
case IORESOURCE_MEM:
|
||||
pp->mem = win->res;
|
||||
pp->mem->name = "MEM";
|
||||
pp->mem_size = resource_size(pp->mem);
|
||||
pp->mem_bus_addr = pp->mem->start - win->offset;
|
||||
break;
|
||||
case 0:
|
||||
pp->cfg = win->res;
|
||||
pp->cfg0_size = resource_size(pp->cfg)/2;
|
||||
pp->cfg1_size = resource_size(pp->cfg)/2;
|
||||
pp->cfg0_base = pp->cfg->start;
|
||||
pp->cfg1_base = pp->cfg->start + pp->cfg0_size;
|
||||
break;
|
||||
case IORESOURCE_BUS:
|
||||
pp->busn = win->res;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
if (restype == IORESOURCE_MEM) {
|
||||
of_pci_range_to_resource(&range, np, &pp->mem);
|
||||
pp->mem.name = "MEM";
|
||||
pp->mem_size = resource_size(&pp->mem);
|
||||
pp->mem_bus_addr = range.pci_addr;
|
||||
|
||||
/* Find the untranslated MEM space address */
|
||||
pp->mem_mod_base = of_read_number(parser.range -
|
||||
parser.np + na, ns);
|
||||
}
|
||||
if (restype == 0) {
|
||||
of_pci_range_to_resource(&range, np, &pp->cfg);
|
||||
pp->cfg0_size = resource_size(&pp->cfg)/2;
|
||||
pp->cfg1_size = resource_size(&pp->cfg)/2;
|
||||
pp->cfg0_base = pp->cfg.start;
|
||||
pp->cfg1_base = pp->cfg.start + pp->cfg0_size;
|
||||
|
||||
/* Find the untranslated configuration space address */
|
||||
pp->cfg0_mod_base = of_read_number(parser.range -
|
||||
parser.np + na, ns);
|
||||
pp->cfg1_mod_base = pp->cfg0_mod_base +
|
||||
pp->cfg0_size;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_pci_parse_bus_range(np, &pp->busn);
|
||||
if (ret < 0) {
|
||||
pp->busn.name = np->name;
|
||||
pp->busn.start = 0;
|
||||
pp->busn.end = 0xff;
|
||||
pp->busn.flags = IORESOURCE_BUS;
|
||||
dev_dbg(pp->dev, "failed to parse bus-range property: %d, using default %pR\n",
|
||||
ret, &pp->busn);
|
||||
}
|
||||
|
||||
if (!pp->dbi_base) {
|
||||
pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start,
|
||||
resource_size(&pp->cfg));
|
||||
pp->dbi_base = devm_ioremap(pp->dev, pp->cfg->start,
|
||||
resource_size(pp->cfg));
|
||||
if (!pp->dbi_base) {
|
||||
dev_err(pp->dev, "error with ioremap\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
pp->mem_base = pp->mem.start;
|
||||
pp->mem_base = pp->mem->start;
|
||||
|
||||
if (!pp->va_cfg0_base) {
|
||||
pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base,
|
||||
|
@ -482,10 +492,9 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
}
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "num-lanes", &pp->lanes)) {
|
||||
dev_err(pp->dev, "Failed to parse the number of lanes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = of_property_read_u32(np, "num-lanes", &pp->lanes);
|
||||
if (ret)
|
||||
pp->lanes = 0;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
if (!pp->ops->msi_host_init) {
|
||||
|
@ -511,7 +520,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
|
||||
if (!pp->ops->rd_other_conf)
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
|
||||
PCIE_ATU_TYPE_MEM, pp->mem_mod_base,
|
||||
PCIE_ATU_TYPE_MEM, pp->mem_base,
|
||||
pp->mem_bus_addr, pp->mem_size);
|
||||
|
||||
dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
|
||||
|
@ -523,15 +532,35 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
val |= PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
|
||||
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
dw_pcie_msi_chip.dev = pp->dev;
|
||||
pp->root_bus_nr = pp->busn->start;
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
bus = pci_scan_root_bus_msi(pp->dev, pp->root_bus_nr,
|
||||
&dw_pcie_ops, pp, &res,
|
||||
&dw_pcie_msi_chip);
|
||||
dw_pcie_msi_chip.dev = pp->dev;
|
||||
} else
|
||||
bus = pci_scan_root_bus(pp->dev, pp->root_bus_nr, &dw_pcie_ops,
|
||||
pp, &res);
|
||||
if (!bus)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pp->ops->scan_bus)
|
||||
pp->ops->scan_bus(pp);
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
/* support old dtbs that incorrectly describe IRQs */
|
||||
pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
|
||||
#endif
|
||||
|
||||
dw_pci.nr_controllers = 1;
|
||||
dw_pci.private_data = (void **)&pp;
|
||||
if (!pci_has_flag(PCI_PROBE_ONLY)) {
|
||||
pci_bus_size_bridges(bus);
|
||||
pci_bus_assign_resources(bus);
|
||||
|
||||
pci_common_init_dev(pp->dev, &dw_pci);
|
||||
list_for_each_entry(child, &bus->children, node)
|
||||
pcie_bus_configure_settings(child);
|
||||
}
|
||||
|
||||
pci_bus_add_devices(bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -539,22 +568,21 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
|||
u32 devfn, int where, int size, u32 *val)
|
||||
{
|
||||
int ret, type;
|
||||
u32 address, busdev, cfg_size;
|
||||
u32 busdev, cfg_size;
|
||||
u64 cpu_addr;
|
||||
void __iomem *va_cfg_base;
|
||||
|
||||
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
|
||||
PCIE_ATU_FUNC(PCI_FUNC(devfn));
|
||||
address = where & ~0x3;
|
||||
|
||||
if (bus->parent->number == pp->root_bus_nr) {
|
||||
type = PCIE_ATU_TYPE_CFG0;
|
||||
cpu_addr = pp->cfg0_mod_base;
|
||||
cpu_addr = pp->cfg0_base;
|
||||
cfg_size = pp->cfg0_size;
|
||||
va_cfg_base = pp->va_cfg0_base;
|
||||
} else {
|
||||
type = PCIE_ATU_TYPE_CFG1;
|
||||
cpu_addr = pp->cfg1_mod_base;
|
||||
cpu_addr = pp->cfg1_base;
|
||||
cfg_size = pp->cfg1_size;
|
||||
va_cfg_base = pp->va_cfg1_base;
|
||||
}
|
||||
|
@ -562,9 +590,9 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
|||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
|
||||
type, cpu_addr,
|
||||
busdev, cfg_size);
|
||||
ret = dw_pcie_cfg_read(va_cfg_base + address, where, size, val);
|
||||
ret = dw_pcie_cfg_read(va_cfg_base + where, size, val);
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
|
||||
PCIE_ATU_TYPE_IO, pp->io_mod_base,
|
||||
PCIE_ATU_TYPE_IO, pp->io_base,
|
||||
pp->io_bus_addr, pp->io_size);
|
||||
|
||||
return ret;
|
||||
|
@ -574,22 +602,21 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
|||
u32 devfn, int where, int size, u32 val)
|
||||
{
|
||||
int ret, type;
|
||||
u32 address, busdev, cfg_size;
|
||||
u32 busdev, cfg_size;
|
||||
u64 cpu_addr;
|
||||
void __iomem *va_cfg_base;
|
||||
|
||||
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
|
||||
PCIE_ATU_FUNC(PCI_FUNC(devfn));
|
||||
address = where & ~0x3;
|
||||
|
||||
if (bus->parent->number == pp->root_bus_nr) {
|
||||
type = PCIE_ATU_TYPE_CFG0;
|
||||
cpu_addr = pp->cfg0_mod_base;
|
||||
cpu_addr = pp->cfg0_base;
|
||||
cfg_size = pp->cfg0_size;
|
||||
va_cfg_base = pp->va_cfg0_base;
|
||||
} else {
|
||||
type = PCIE_ATU_TYPE_CFG1;
|
||||
cpu_addr = pp->cfg1_mod_base;
|
||||
cpu_addr = pp->cfg1_base;
|
||||
cfg_size = pp->cfg1_size;
|
||||
va_cfg_base = pp->va_cfg1_base;
|
||||
}
|
||||
|
@ -597,9 +624,9 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
|||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
|
||||
type, cpu_addr,
|
||||
busdev, cfg_size);
|
||||
ret = dw_pcie_cfg_write(va_cfg_base + address, where, size, val);
|
||||
ret = dw_pcie_cfg_write(va_cfg_base + where, size, val);
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
|
||||
PCIE_ATU_TYPE_IO, pp->io_mod_base,
|
||||
PCIE_ATU_TYPE_IO, pp->io_base,
|
||||
pp->io_bus_addr, pp->io_size);
|
||||
|
||||
return ret;
|
||||
|
@ -631,7 +658,7 @@ static int dw_pcie_valid_config(struct pcie_port *pp,
|
|||
static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
|
||||
int size, u32 *val)
|
||||
{
|
||||
struct pcie_port *pp = sys_to_pcie(bus->sysdata);
|
||||
struct pcie_port *pp = bus->sysdata;
|
||||
int ret;
|
||||
|
||||
if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
|
||||
|
@ -655,7 +682,7 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
|
|||
static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
|
||||
int where, int size, u32 val)
|
||||
{
|
||||
struct pcie_port *pp = sys_to_pcie(bus->sysdata);
|
||||
struct pcie_port *pp = bus->sysdata;
|
||||
int ret;
|
||||
|
||||
if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
|
||||
|
@ -679,69 +706,6 @@ static struct pci_ops dw_pcie_ops = {
|
|||
.write = dw_pcie_wr_conf,
|
||||
};
|
||||
|
||||
static int dw_pcie_setup(int nr, struct pci_sys_data *sys)
|
||||
{
|
||||
struct pcie_port *pp;
|
||||
|
||||
pp = sys_to_pcie(sys);
|
||||
|
||||
if (global_io_offset < SZ_1M && pp->io_size > 0) {
|
||||
sys->io_offset = global_io_offset - pp->io_bus_addr;
|
||||
pci_ioremap_io(global_io_offset, pp->io_base);
|
||||
global_io_offset += SZ_64K;
|
||||
pci_add_resource_offset(&sys->resources, &pp->io,
|
||||
sys->io_offset);
|
||||
}
|
||||
|
||||
sys->mem_offset = pp->mem.start - pp->mem_bus_addr;
|
||||
pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset);
|
||||
pci_add_resource(&sys->resources, &pp->busn);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys)
|
||||
{
|
||||
struct pci_bus *bus;
|
||||
struct pcie_port *pp = sys_to_pcie(sys);
|
||||
|
||||
pp->root_bus_nr = sys->busnr;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI))
|
||||
bus = pci_scan_root_bus_msi(pp->dev, sys->busnr, &dw_pcie_ops,
|
||||
sys, &sys->resources,
|
||||
&dw_pcie_msi_chip);
|
||||
else
|
||||
bus = pci_scan_root_bus(pp->dev, sys->busnr, &dw_pcie_ops,
|
||||
sys, &sys->resources);
|
||||
|
||||
if (!bus)
|
||||
return NULL;
|
||||
|
||||
if (bus && pp->ops->scan_bus)
|
||||
pp->ops->scan_bus(pp);
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
static int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
|
||||
{
|
||||
struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata);
|
||||
int irq;
|
||||
|
||||
irq = of_irq_parse_and_map_pci(dev, slot, pin);
|
||||
if (!irq)
|
||||
irq = pp->irq;
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
static struct hw_pci dw_pci = {
|
||||
.setup = dw_pcie_setup,
|
||||
.scan = dw_pcie_scan_bus,
|
||||
.map_irq = dw_pcie_map_irq,
|
||||
};
|
||||
|
||||
void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||
{
|
||||
u32 val;
|
||||
|
@ -764,6 +728,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
|||
case 8:
|
||||
val |= PORT_LINK_MODE_8_LANES;
|
||||
break;
|
||||
default:
|
||||
dev_err(pp->dev, "num-lanes %u: invalid value\n", pp->lanes);
|
||||
return;
|
||||
}
|
||||
dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
|
||||
|
||||
|
|
|
@ -27,25 +27,21 @@ struct pcie_port {
|
|||
u8 root_bus_nr;
|
||||
void __iomem *dbi_base;
|
||||
u64 cfg0_base;
|
||||
u64 cfg0_mod_base;
|
||||
void __iomem *va_cfg0_base;
|
||||
u32 cfg0_size;
|
||||
u64 cfg1_base;
|
||||
u64 cfg1_mod_base;
|
||||
void __iomem *va_cfg1_base;
|
||||
u32 cfg1_size;
|
||||
u64 io_base;
|
||||
u64 io_mod_base;
|
||||
resource_size_t io_base;
|
||||
phys_addr_t io_bus_addr;
|
||||
u32 io_size;
|
||||
u64 mem_base;
|
||||
u64 mem_mod_base;
|
||||
phys_addr_t mem_bus_addr;
|
||||
u32 mem_size;
|
||||
struct resource cfg;
|
||||
struct resource io;
|
||||
struct resource mem;
|
||||
struct resource busn;
|
||||
struct resource *cfg;
|
||||
struct resource *io;
|
||||
struct resource *mem;
|
||||
struct resource *busn;
|
||||
int irq;
|
||||
u32 lanes;
|
||||
struct pcie_host_ops *ops;
|
||||
|
@ -70,14 +66,14 @@ struct pcie_host_ops {
|
|||
void (*host_init)(struct pcie_port *pp);
|
||||
void (*msi_set_irq)(struct pcie_port *pp, int irq);
|
||||
void (*msi_clear_irq)(struct pcie_port *pp, int irq);
|
||||
u32 (*get_msi_addr)(struct pcie_port *pp);
|
||||
phys_addr_t (*get_msi_addr)(struct pcie_port *pp);
|
||||
u32 (*get_msi_data)(struct pcie_port *pp, int pos);
|
||||
void (*scan_bus)(struct pcie_port *pp);
|
||||
int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip);
|
||||
};
|
||||
|
||||
int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val);
|
||||
int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val);
|
||||
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_link_up(struct pcie_port *pp);
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* PCIe host controller driver for HiSilicon Hip05 SoC
|
||||
*
|
||||
* Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com
|
||||
*
|
||||
* Author: Zhou Wang <wangzhou1@hisilicon.com>
|
||||
* Dacai Zhu <zhudacai@hisilicon.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/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "pcie-designware.h"
|
||||
|
||||
#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818
|
||||
#define PCIE_LTSSM_LINKUP_STATE 0x11
|
||||
#define PCIE_LTSSM_STATE_MASK 0x3F
|
||||
|
||||
#define to_hisi_pcie(x) container_of(x, struct hisi_pcie, pp)
|
||||
|
||||
struct hisi_pcie {
|
||||
struct regmap *subctrl;
|
||||
void __iomem *reg_base;
|
||||
u32 port_id;
|
||||
struct pcie_port pp;
|
||||
};
|
||||
|
||||
static inline void hisi_pcie_apb_writel(struct hisi_pcie *pcie,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
writel(val, pcie->reg_base + reg);
|
||||
}
|
||||
|
||||
static inline u32 hisi_pcie_apb_readl(struct hisi_pcie *pcie, u32 reg)
|
||||
{
|
||||
return readl(pcie->reg_base + reg);
|
||||
}
|
||||
|
||||
/* Hip05 PCIe host only supports 32-bit config access */
|
||||
static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
|
||||
u32 *val)
|
||||
{
|
||||
u32 reg;
|
||||
u32 reg_val;
|
||||
struct hisi_pcie *pcie = to_hisi_pcie(pp);
|
||||
void *walker = ®_val;
|
||||
|
||||
walker += (where & 0x3);
|
||||
reg = where & ~0x3;
|
||||
reg_val = hisi_pcie_apb_readl(pcie, reg);
|
||||
|
||||
if (size == 1)
|
||||
*val = *(u8 __force *) walker;
|
||||
else if (size == 2)
|
||||
*val = *(u16 __force *) walker;
|
||||
else if (size != 4)
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
/* Hip05 PCIe host only supports 32-bit config access */
|
||||
static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size,
|
||||
u32 val)
|
||||
{
|
||||
u32 reg_val;
|
||||
u32 reg;
|
||||
struct hisi_pcie *pcie = to_hisi_pcie(pp);
|
||||
void *walker = ®_val;
|
||||
|
||||
walker += (where & 0x3);
|
||||
reg = where & ~0x3;
|
||||
if (size == 4)
|
||||
hisi_pcie_apb_writel(pcie, val, reg);
|
||||
else if (size == 2) {
|
||||
reg_val = hisi_pcie_apb_readl(pcie, reg);
|
||||
*(u16 __force *) walker = val;
|
||||
hisi_pcie_apb_writel(pcie, reg_val, reg);
|
||||
} else if (size == 1) {
|
||||
reg_val = hisi_pcie_apb_readl(pcie, reg);
|
||||
*(u8 __force *) walker = val;
|
||||
hisi_pcie_apb_writel(pcie, reg_val, reg);
|
||||
} else
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int hisi_pcie_link_up(struct pcie_port *pp)
|
||||
{
|
||||
u32 val;
|
||||
struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp);
|
||||
|
||||
regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG +
|
||||
0x100 * hisi_pcie->port_id, &val);
|
||||
|
||||
return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
|
||||
}
|
||||
|
||||
static struct pcie_host_ops hisi_pcie_host_ops = {
|
||||
.rd_own_conf = hisi_pcie_cfg_read,
|
||||
.wr_own_conf = hisi_pcie_cfg_write,
|
||||
.link_up = hisi_pcie_link_up,
|
||||
};
|
||||
|
||||
static int __init hisi_add_pcie_port(struct pcie_port *pp,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
u32 port_id;
|
||||
struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp);
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "port-id", &port_id)) {
|
||||
dev_err(&pdev->dev, "failed to read port-id\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (port_id > 3) {
|
||||
dev_err(&pdev->dev, "Invalid port-id: %d\n", port_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
hisi_pcie->port_id = port_id;
|
||||
|
||||
pp->ops = &hisi_pcie_host_ops;
|
||||
|
||||
ret = dw_pcie_host_init(pp);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init hisi_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hisi_pcie *hisi_pcie;
|
||||
struct pcie_port *pp;
|
||||
struct resource *reg;
|
||||
int ret;
|
||||
|
||||
hisi_pcie = devm_kzalloc(&pdev->dev, sizeof(*hisi_pcie), GFP_KERNEL);
|
||||
if (!hisi_pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
pp = &hisi_pcie->pp;
|
||||
pp->dev = &pdev->dev;
|
||||
|
||||
hisi_pcie->subctrl =
|
||||
syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl");
|
||||
if (IS_ERR(hisi_pcie->subctrl)) {
|
||||
dev_err(pp->dev, "cannot get subctrl base\n");
|
||||
return PTR_ERR(hisi_pcie->subctrl);
|
||||
}
|
||||
|
||||
reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi");
|
||||
hisi_pcie->reg_base = devm_ioremap_resource(&pdev->dev, reg);
|
||||
if (IS_ERR(hisi_pcie->reg_base)) {
|
||||
dev_err(pp->dev, "cannot get rc_dbi base\n");
|
||||
return PTR_ERR(hisi_pcie->reg_base);
|
||||
}
|
||||
|
||||
hisi_pcie->pp.dbi_base = hisi_pcie->reg_base;
|
||||
|
||||
ret = hisi_add_pcie_port(pp, pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, hisi_pcie);
|
||||
|
||||
dev_warn(pp->dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id hisi_pcie_of_match[] = {
|
||||
{.compatible = "hisilicon,hip05-pcie",},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, hisi_pcie_of_match);
|
||||
|
||||
static struct platform_driver hisi_pcie_driver = {
|
||||
.probe = hisi_pcie_probe,
|
||||
.driver = {
|
||||
.name = "hisi-pcie",
|
||||
.of_match_table = hisi_pcie_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(hisi_pcie_driver);
|
|
@ -54,6 +54,33 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(np, "brcm,pcie-ob")) {
|
||||
u32 val;
|
||||
|
||||
ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset",
|
||||
&val);
|
||||
if (ret) {
|
||||
dev_err(pcie->dev,
|
||||
"missing brcm,pcie-ob-axi-offset property\n");
|
||||
return ret;
|
||||
}
|
||||
pcie->ob.axi_offset = val;
|
||||
|
||||
ret = of_property_read_u32(np, "brcm,pcie-ob-window-size",
|
||||
&val);
|
||||
if (ret) {
|
||||
dev_err(pcie->dev,
|
||||
"missing brcm,pcie-ob-window-size property\n");
|
||||
return ret;
|
||||
}
|
||||
pcie->ob.window_size = (resource_size_t)val * SZ_1M;
|
||||
|
||||
if (of_property_read_bool(np, "brcm,pcie-ob-oarr-size"))
|
||||
pcie->ob.set_oarr_size = true;
|
||||
|
||||
pcie->need_ob_cfg = true;
|
||||
}
|
||||
|
||||
/* PHY use is optional */
|
||||
pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy");
|
||||
if (IS_ERR(pcie->phy)) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Hauke Mehrtens <hauke@hauke-m.de>
|
||||
* Copyright (C) 2015 Broadcom Corporatcommon ion
|
||||
* Copyright (C) 2015 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
|
@ -31,6 +31,8 @@
|
|||
#include "pcie-iproc.h"
|
||||
|
||||
#define CLK_CONTROL_OFFSET 0x000
|
||||
#define EP_PERST_SOURCE_SELECT_SHIFT 2
|
||||
#define EP_PERST_SOURCE_SELECT BIT(EP_PERST_SOURCE_SELECT_SHIFT)
|
||||
#define EP_MODE_SURVIVE_PERST_SHIFT 1
|
||||
#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT)
|
||||
#define RC_PCIE_RST_OUTPUT_SHIFT 0
|
||||
|
@ -58,6 +60,24 @@
|
|||
#define SYS_RC_INTX_EN 0x330
|
||||
#define SYS_RC_INTX_MASK 0xf
|
||||
|
||||
#define PCIE_LINK_STATUS_OFFSET 0xf0c
|
||||
#define PCIE_PHYLINKUP_SHIFT 3
|
||||
#define PCIE_PHYLINKUP BIT(PCIE_PHYLINKUP_SHIFT)
|
||||
#define PCIE_DL_ACTIVE_SHIFT 2
|
||||
#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT)
|
||||
|
||||
#define OARR_VALID_SHIFT 0
|
||||
#define OARR_VALID BIT(OARR_VALID_SHIFT)
|
||||
#define OARR_SIZE_CFG_SHIFT 1
|
||||
#define OARR_SIZE_CFG BIT(OARR_SIZE_CFG_SHIFT)
|
||||
|
||||
#define OARR_LO(window) (0xd20 + (window) * 8)
|
||||
#define OARR_HI(window) (0xd24 + (window) * 8)
|
||||
#define OMAP_LO(window) (0xd40 + (window) * 8)
|
||||
#define OMAP_HI(window) (0xd44 + (window) * 8)
|
||||
|
||||
#define MAX_NUM_OB_WINDOWS 2
|
||||
|
||||
static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
|
||||
{
|
||||
struct iproc_pcie *pcie;
|
||||
|
@ -119,23 +139,32 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
|
|||
u32 val;
|
||||
|
||||
/*
|
||||
* Configure the PCIe controller as root complex and send a downstream
|
||||
* reset
|
||||
* Select perst_b signal as reset source. Put the device into reset,
|
||||
* and then bring it out of reset
|
||||
*/
|
||||
val = EP_MODE_SURVIVE_PERST | RC_PCIE_RST_OUTPUT;
|
||||
val = readl(pcie->base + CLK_CONTROL_OFFSET);
|
||||
val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
|
||||
~RC_PCIE_RST_OUTPUT;
|
||||
writel(val, pcie->base + CLK_CONTROL_OFFSET);
|
||||
udelay(250);
|
||||
val &= ~EP_MODE_SURVIVE_PERST;
|
||||
|
||||
val |= RC_PCIE_RST_OUTPUT;
|
||||
writel(val, pcie->base + CLK_CONTROL_OFFSET);
|
||||
msleep(250);
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
|
||||
{
|
||||
u8 hdr_type;
|
||||
u32 link_ctrl;
|
||||
u32 link_ctrl, class, val;
|
||||
u16 pos, link_status;
|
||||
int link_is_active = 0;
|
||||
bool link_is_active = false;
|
||||
|
||||
val = readl(pcie->base + PCIE_LINK_STATUS_OFFSET);
|
||||
if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) {
|
||||
dev_err(pcie->dev, "PHY or data link is INACTIVE!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* make sure we are not in EP mode */
|
||||
pci_bus_read_config_byte(bus, 0, PCI_HEADER_TYPE, &hdr_type);
|
||||
|
@ -145,14 +174,19 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
|
|||
}
|
||||
|
||||
/* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */
|
||||
pci_bus_write_config_word(bus, 0, PCI_CLASS_DEVICE,
|
||||
PCI_CLASS_BRIDGE_PCI);
|
||||
#define PCI_BRIDGE_CTRL_REG_OFFSET 0x43c
|
||||
#define PCI_CLASS_BRIDGE_MASK 0xffff00
|
||||
#define PCI_CLASS_BRIDGE_SHIFT 8
|
||||
pci_bus_read_config_dword(bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, &class);
|
||||
class &= ~PCI_CLASS_BRIDGE_MASK;
|
||||
class |= (PCI_CLASS_BRIDGE_PCI << PCI_CLASS_BRIDGE_SHIFT);
|
||||
pci_bus_write_config_dword(bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, class);
|
||||
|
||||
/* check link status to see if link is active */
|
||||
pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP);
|
||||
pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, &link_status);
|
||||
if (link_status & PCI_EXP_LNKSTA_NLW)
|
||||
link_is_active = 1;
|
||||
link_is_active = true;
|
||||
|
||||
if (!link_is_active) {
|
||||
/* try GEN 1 link speed */
|
||||
|
@ -176,7 +210,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
|
|||
pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA,
|
||||
&link_status);
|
||||
if (link_status & PCI_EXP_LNKSTA_NLW)
|
||||
link_is_active = 1;
|
||||
link_is_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,6 +224,101 @@ static void iproc_pcie_enable(struct iproc_pcie *pcie)
|
|||
writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some iProc SoCs require the SW to configure the outbound address mapping
|
||||
*
|
||||
* Outbound address translation:
|
||||
*
|
||||
* iproc_pcie_address = axi_address - axi_offset
|
||||
* OARR = iproc_pcie_address
|
||||
* OMAP = pci_addr
|
||||
*
|
||||
* axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address
|
||||
*/
|
||||
static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
|
||||
u64 pci_addr, resource_size_t size)
|
||||
{
|
||||
struct iproc_pcie_ob *ob = &pcie->ob;
|
||||
unsigned i;
|
||||
u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
|
||||
u64 remainder;
|
||||
|
||||
if (size > max_size) {
|
||||
dev_err(pcie->dev,
|
||||
"res size 0x%pap exceeds max supported size 0x%llx\n",
|
||||
&size, max_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
div64_u64_rem(size, ob->window_size, &remainder);
|
||||
if (remainder) {
|
||||
dev_err(pcie->dev,
|
||||
"res size %pap needs to be multiple of window size %pap\n",
|
||||
&size, &ob->window_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (axi_addr < ob->axi_offset) {
|
||||
dev_err(pcie->dev,
|
||||
"axi address %pap less than offset %pap\n",
|
||||
&axi_addr, &ob->axi_offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate the AXI address to the internal address used by the iProc
|
||||
* PCIe core before programming the OARR
|
||||
*/
|
||||
axi_addr -= ob->axi_offset;
|
||||
|
||||
for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) {
|
||||
writel(lower_32_bits(axi_addr) | OARR_VALID |
|
||||
(ob->set_oarr_size ? 1 : 0), pcie->base + OARR_LO(i));
|
||||
writel(upper_32_bits(axi_addr), pcie->base + OARR_HI(i));
|
||||
writel(lower_32_bits(pci_addr), pcie->base + OMAP_LO(i));
|
||||
writel(upper_32_bits(pci_addr), pcie->base + OMAP_HI(i));
|
||||
|
||||
size -= ob->window_size;
|
||||
if (size == 0)
|
||||
break;
|
||||
|
||||
axi_addr += ob->window_size;
|
||||
pci_addr += ob->window_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
|
||||
struct list_head *resources)
|
||||
{
|
||||
struct resource_entry *window;
|
||||
int ret;
|
||||
|
||||
resource_list_for_each_entry(window, resources) {
|
||||
struct resource *res = window->res;
|
||||
u64 res_type = resource_type(res);
|
||||
|
||||
switch (res_type) {
|
||||
case IORESOURCE_IO:
|
||||
case IORESOURCE_BUS:
|
||||
break;
|
||||
case IORESOURCE_MEM:
|
||||
ret = iproc_pcie_setup_ob(pcie, res->start,
|
||||
res->start - window->offset,
|
||||
resource_size(res));
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
dev_err(pcie->dev, "invalid resource %pR\n", res);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
|
||||
{
|
||||
int ret;
|
||||
|
@ -213,6 +342,14 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
|
|||
|
||||
iproc_pcie_reset(pcie);
|
||||
|
||||
if (pcie->need_ob_cfg) {
|
||||
ret = iproc_pcie_map_ranges(pcie, res);
|
||||
if (ret) {
|
||||
dev_err(pcie->dev, "map failed\n");
|
||||
goto err_power_off_phy;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
pcie->sysdata.private_data = pcie;
|
||||
sysdata = &pcie->sysdata;
|
||||
|
@ -238,9 +375,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
|
|||
|
||||
pci_scan_child_bus(bus);
|
||||
pci_assign_unassigned_bus_resources(bus);
|
||||
#ifdef CONFIG_ARM
|
||||
pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
|
||||
#endif
|
||||
pci_bus_add_devices(bus);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -14,17 +14,30 @@
|
|||
#ifndef _PCIE_IPROC_H
|
||||
#define _PCIE_IPROC_H
|
||||
|
||||
#define IPROC_PCIE_MAX_NUM_IRQS 6
|
||||
/**
|
||||
* iProc PCIe outbound mapping
|
||||
* @set_oarr_size: indicates the OARR size bit needs to be set
|
||||
* @axi_offset: offset from the AXI address to the internal address used by
|
||||
* the iProc PCIe core
|
||||
* @window_size: outbound window size
|
||||
*/
|
||||
struct iproc_pcie_ob {
|
||||
bool set_oarr_size;
|
||||
resource_size_t axi_offset;
|
||||
resource_size_t window_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* iProc PCIe device
|
||||
* @dev: pointer to device data structure
|
||||
* @base: PCIe host controller I/O register base
|
||||
* @resources: linked list of all PCI resources
|
||||
* @sysdata: Per PCI controller data (ARM-specific)
|
||||
* @root_bus: pointer to root bus
|
||||
* @phy: optional PHY device that controls the Serdes
|
||||
* @irqs: interrupt IDs
|
||||
* @map_irq: function callback to map interrupts
|
||||
* @need_ob_cfg: indidates SW needs to configure the outbound mapping window
|
||||
* @ob: outbound mapping parameters
|
||||
*/
|
||||
struct iproc_pcie {
|
||||
struct device *dev;
|
||||
|
@ -34,8 +47,9 @@ struct iproc_pcie {
|
|||
#endif
|
||||
struct pci_bus *root_bus;
|
||||
struct phy *phy;
|
||||
int irqs[IPROC_PCIE_MAX_NUM_IRQS];
|
||||
int (*map_irq)(const struct pci_dev *, u8, u8);
|
||||
bool need_ob_cfg;
|
||||
struct iproc_pcie_ob ob;
|
||||
};
|
||||
|
||||
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
|
||||
|
|
|
@ -108,6 +108,8 @@
|
|||
#define RCAR_PCI_MAX_RESOURCES 4
|
||||
#define MAX_NR_INBOUND_MAPS 6
|
||||
|
||||
static unsigned long global_io_offset;
|
||||
|
||||
struct rcar_msi {
|
||||
DECLARE_BITMAP(used, INT_PCI_MSI_NR);
|
||||
struct irq_domain *domain;
|
||||
|
@ -124,7 +126,16 @@ static inline struct rcar_msi *to_rcar_msi(struct msi_controller *chip)
|
|||
}
|
||||
|
||||
/* Structure representing the PCIe interface */
|
||||
/*
|
||||
* ARM pcibios functions expect the ARM struct pci_sys_data as the PCI
|
||||
* sysdata. Add pci_sys_data as the first element in struct gen_pci so
|
||||
* that when we use a gen_pci pointer as sysdata, it is also a pointer to
|
||||
* a struct pci_sys_data.
|
||||
*/
|
||||
struct rcar_pcie {
|
||||
#ifdef CONFIG_ARM
|
||||
struct pci_sys_data sys;
|
||||
#endif
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct resource res[RCAR_PCI_MAX_RESOURCES];
|
||||
|
@ -135,11 +146,6 @@ struct rcar_pcie {
|
|||
struct rcar_msi msi;
|
||||
};
|
||||
|
||||
static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys)
|
||||
{
|
||||
return sys->private_data;
|
||||
}
|
||||
|
||||
static void rcar_pci_write_reg(struct rcar_pcie *pcie, unsigned long val,
|
||||
unsigned long reg)
|
||||
{
|
||||
|
@ -258,7 +264,7 @@ static int rcar_pcie_config_access(struct rcar_pcie *pcie,
|
|||
static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 *val)
|
||||
{
|
||||
struct rcar_pcie *pcie = sys_to_pcie(bus->sysdata);
|
||||
struct rcar_pcie *pcie = bus->sysdata;
|
||||
int ret;
|
||||
|
||||
ret = rcar_pcie_config_access(pcie, RCAR_PCI_ACCESS_READ,
|
||||
|
@ -283,7 +289,7 @@ static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
|
|||
static int rcar_pcie_write_conf(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 val)
|
||||
{
|
||||
struct rcar_pcie *pcie = sys_to_pcie(bus->sysdata);
|
||||
struct rcar_pcie *pcie = bus->sysdata;
|
||||
int shift, ret;
|
||||
u32 data;
|
||||
|
||||
|
@ -353,13 +359,12 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
|
|||
rcar_pci_write_reg(pcie, mask, PCIEPTCTLR(win));
|
||||
}
|
||||
|
||||
static int rcar_pcie_setup(int nr, struct pci_sys_data *sys)
|
||||
static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie *pcie)
|
||||
{
|
||||
struct rcar_pcie *pcie = sys_to_pcie(sys);
|
||||
struct resource *res;
|
||||
int i;
|
||||
|
||||
pcie->root_bus_nr = -1;
|
||||
pcie->root_bus_nr = pcie->busn.start;
|
||||
|
||||
/* Setup PCI resources */
|
||||
for (i = 0; i < RCAR_PCI_MAX_RESOURCES; i++) {
|
||||
|
@ -372,32 +377,53 @@ static int rcar_pcie_setup(int nr, struct pci_sys_data *sys)
|
|||
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
phys_addr_t io_start = pci_pio_to_address(res->start);
|
||||
pci_ioremap_io(nr * SZ_64K, io_start);
|
||||
} else
|
||||
pci_add_resource(&sys->resources, res);
|
||||
pci_ioremap_io(global_io_offset, io_start);
|
||||
global_io_offset += SZ_64K;
|
||||
}
|
||||
|
||||
pci_add_resource(resource, res);
|
||||
}
|
||||
pci_add_resource(&sys->resources, &pcie->busn);
|
||||
pci_add_resource(resource, &pcie->busn);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct hw_pci rcar_pci = {
|
||||
.setup = rcar_pcie_setup,
|
||||
.map_irq = of_irq_parse_and_map_pci,
|
||||
.ops = &rcar_pcie_ops,
|
||||
};
|
||||
|
||||
static void rcar_pcie_enable(struct rcar_pcie *pcie)
|
||||
static int rcar_pcie_enable(struct rcar_pcie *pcie)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(pcie->dev);
|
||||
struct pci_bus *bus, *child;
|
||||
LIST_HEAD(res);
|
||||
|
||||
rcar_pci.nr_controllers = 1;
|
||||
rcar_pci.private_data = (void **)&pcie;
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
rcar_pci.msi_ctrl = &pcie->msi.chip;
|
||||
#endif
|
||||
rcar_pcie_setup(&res, pcie);
|
||||
|
||||
pci_common_init_dev(&pdev->dev, &rcar_pci);
|
||||
/* 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);
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI))
|
||||
bus = pci_scan_root_bus_msi(pcie->dev, pcie->root_bus_nr,
|
||||
&rcar_pcie_ops, pcie, &res, &pcie->msi.chip);
|
||||
else
|
||||
bus = pci_scan_root_bus(pcie->dev, pcie->root_bus_nr,
|
||||
&rcar_pcie_ops, pcie, &res);
|
||||
|
||||
if (!bus) {
|
||||
dev_err(pcie->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;
|
||||
}
|
||||
|
||||
static int phy_wait_for_ack(struct rcar_pcie *pcie)
|
||||
|
@ -970,9 +996,7 @@ static int rcar_pcie_probe(struct platform_device *pdev)
|
|||
data = rcar_pci_read_reg(pcie, MACSR);
|
||||
dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);
|
||||
|
||||
rcar_pcie_enable(pcie);
|
||||
|
||||
return 0;
|
||||
return rcar_pcie_enable(pcie);
|
||||
}
|
||||
|
||||
static struct platform_driver rcar_pcie_driver = {
|
||||
|
|
|
@ -163,34 +163,34 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
|
|||
* default value in capability register is 512 bytes. So force
|
||||
* it to 128 here.
|
||||
*/
|
||||
dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_DEVCTL, 4, &val);
|
||||
dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, &val);
|
||||
val &= ~PCI_EXP_DEVCTL_READRQ;
|
||||
dw_pcie_cfg_write(pp->dbi_base, exp_cap_off + PCI_EXP_DEVCTL, 4, val);
|
||||
dw_pcie_cfg_write(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, val);
|
||||
|
||||
dw_pcie_cfg_write(pp->dbi_base, PCI_VENDOR_ID, 2, 0x104A);
|
||||
dw_pcie_cfg_write(pp->dbi_base, PCI_DEVICE_ID, 2, 0xCD80);
|
||||
dw_pcie_cfg_write(pp->dbi_base + PCI_VENDOR_ID, 2, 0x104A);
|
||||
dw_pcie_cfg_write(pp->dbi_base + PCI_DEVICE_ID, 2, 0xCD80);
|
||||
|
||||
/*
|
||||
* if is_gen1 is set then handle it, so that some buggy card
|
||||
* also works
|
||||
*/
|
||||
if (spear13xx_pcie->is_gen1) {
|
||||
dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_LNKCAP, 4,
|
||||
&val);
|
||||
dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCAP,
|
||||
4, &val);
|
||||
if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
|
||||
val &= ~((u32)PCI_EXP_LNKCAP_SLS);
|
||||
val |= PCI_EXP_LNKCAP_SLS_2_5GB;
|
||||
dw_pcie_cfg_write(pp->dbi_base, exp_cap_off +
|
||||
PCI_EXP_LNKCAP, 4, val);
|
||||
dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
|
||||
PCI_EXP_LNKCAP, 4, val);
|
||||
}
|
||||
|
||||
dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_LNKCTL2, 4,
|
||||
&val);
|
||||
dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2,
|
||||
2, &val);
|
||||
if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
|
||||
val &= ~((u32)PCI_EXP_LNKCAP_SLS);
|
||||
val |= PCI_EXP_LNKCAP_SLS_2_5GB;
|
||||
dw_pcie_cfg_write(pp->dbi_base, exp_cap_off +
|
||||
PCI_EXP_LNKCTL2, 4, val);
|
||||
dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
|
||||
PCI_EXP_LNKCTL2, 2, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -204,36 +204,39 @@ static void pciehp_power_thread(struct work_struct *work)
|
|||
kfree(info);
|
||||
}
|
||||
|
||||
void pciehp_queue_pushbutton_work(struct work_struct *work)
|
||||
static void pciehp_queue_power_work(struct slot *p_slot, int req)
|
||||
{
|
||||
struct slot *p_slot = container_of(work, struct slot, work.work);
|
||||
struct power_work_info *info;
|
||||
|
||||
p_slot->state = (req == ENABLE_REQ) ? POWERON_STATE : POWEROFF_STATE;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
|
||||
__func__);
|
||||
ctrl_err(p_slot->ctrl, "no memory to queue %s request\n",
|
||||
(req == ENABLE_REQ) ? "poweron" : "poweroff");
|
||||
return;
|
||||
}
|
||||
info->p_slot = p_slot;
|
||||
INIT_WORK(&info->work, pciehp_power_thread);
|
||||
info->req = req;
|
||||
queue_work(p_slot->wq, &info->work);
|
||||
}
|
||||
|
||||
void pciehp_queue_pushbutton_work(struct work_struct *work)
|
||||
{
|
||||
struct slot *p_slot = container_of(work, struct slot, work.work);
|
||||
|
||||
mutex_lock(&p_slot->lock);
|
||||
switch (p_slot->state) {
|
||||
case BLINKINGOFF_STATE:
|
||||
p_slot->state = POWEROFF_STATE;
|
||||
info->req = DISABLE_REQ;
|
||||
pciehp_queue_power_work(p_slot, DISABLE_REQ);
|
||||
break;
|
||||
case BLINKINGON_STATE:
|
||||
p_slot->state = POWERON_STATE;
|
||||
info->req = ENABLE_REQ;
|
||||
pciehp_queue_power_work(p_slot, ENABLE_REQ);
|
||||
break;
|
||||
default:
|
||||
kfree(info);
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
queue_work(p_slot->wq, &info->work);
|
||||
out:
|
||||
mutex_unlock(&p_slot->lock);
|
||||
}
|
||||
|
||||
|
@ -301,27 +304,12 @@ static void handle_button_press_event(struct slot *p_slot)
|
|||
static void handle_surprise_event(struct slot *p_slot)
|
||||
{
|
||||
u8 getstatus;
|
||||
struct power_work_info *info;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
info->p_slot = p_slot;
|
||||
INIT_WORK(&info->work, pciehp_power_thread);
|
||||
|
||||
pciehp_get_adapter_status(p_slot, &getstatus);
|
||||
if (!getstatus) {
|
||||
p_slot->state = POWEROFF_STATE;
|
||||
info->req = DISABLE_REQ;
|
||||
} else {
|
||||
p_slot->state = POWERON_STATE;
|
||||
info->req = ENABLE_REQ;
|
||||
}
|
||||
|
||||
queue_work(p_slot->wq, &info->work);
|
||||
if (!getstatus)
|
||||
pciehp_queue_power_work(p_slot, DISABLE_REQ);
|
||||
else
|
||||
pciehp_queue_power_work(p_slot, ENABLE_REQ);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -330,17 +318,6 @@ static void handle_surprise_event(struct slot *p_slot)
|
|||
static void handle_link_event(struct slot *p_slot, u32 event)
|
||||
{
|
||||
struct controller *ctrl = p_slot->ctrl;
|
||||
struct power_work_info *info;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
info->p_slot = p_slot;
|
||||
info->req = event == INT_LINK_UP ? ENABLE_REQ : DISABLE_REQ;
|
||||
INIT_WORK(&info->work, pciehp_power_thread);
|
||||
|
||||
switch (p_slot->state) {
|
||||
case BLINKINGON_STATE:
|
||||
|
@ -348,22 +325,19 @@ static void handle_link_event(struct slot *p_slot, u32 event)
|
|||
cancel_delayed_work(&p_slot->work);
|
||||
/* Fall through */
|
||||
case STATIC_STATE:
|
||||
p_slot->state = event == INT_LINK_UP ?
|
||||
POWERON_STATE : POWEROFF_STATE;
|
||||
queue_work(p_slot->wq, &info->work);
|
||||
pciehp_queue_power_work(p_slot, event == INT_LINK_UP ?
|
||||
ENABLE_REQ : DISABLE_REQ);
|
||||
break;
|
||||
case POWERON_STATE:
|
||||
if (event == INT_LINK_UP) {
|
||||
ctrl_info(ctrl,
|
||||
"Link Up event ignored on slot(%s): already powering on\n",
|
||||
slot_name(p_slot));
|
||||
kfree(info);
|
||||
} else {
|
||||
ctrl_info(ctrl,
|
||||
"Link Down event queued on slot(%s): currently getting powered on\n",
|
||||
slot_name(p_slot));
|
||||
p_slot->state = POWEROFF_STATE;
|
||||
queue_work(p_slot->wq, &info->work);
|
||||
pciehp_queue_power_work(p_slot, DISABLE_REQ);
|
||||
}
|
||||
break;
|
||||
case POWEROFF_STATE:
|
||||
|
@ -371,19 +345,16 @@ static void handle_link_event(struct slot *p_slot, u32 event)
|
|||
ctrl_info(ctrl,
|
||||
"Link Up event queued on slot(%s): currently getting powered off\n",
|
||||
slot_name(p_slot));
|
||||
p_slot->state = POWERON_STATE;
|
||||
queue_work(p_slot->wq, &info->work);
|
||||
pciehp_queue_power_work(p_slot, ENABLE_REQ);
|
||||
} else {
|
||||
ctrl_info(ctrl,
|
||||
"Link Down event ignored on slot(%s): already powering off\n",
|
||||
slot_name(p_slot));
|
||||
kfree(info);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ctrl_err(ctrl, "ignoring invalid state %#x on slot(%s)\n",
|
||||
p_slot->state, slot_name(p_slot));
|
||||
kfree(info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,24 +54,29 @@ static inline void pci_iov_set_numvfs(struct pci_dev *dev, int nr_virtfn)
|
|||
* The PF consumes one bus number. NumVFs, First VF Offset, and VF Stride
|
||||
* determine how many additional bus numbers will be consumed by VFs.
|
||||
*
|
||||
* Iterate over all valid NumVFs and calculate the maximum number of bus
|
||||
* numbers that could ever be required.
|
||||
* Iterate over all valid NumVFs, validate offset and stride, and calculate
|
||||
* the maximum number of bus numbers that could ever be required.
|
||||
*/
|
||||
static inline u8 virtfn_max_buses(struct pci_dev *dev)
|
||||
static int compute_max_vf_buses(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_sriov *iov = dev->sriov;
|
||||
int nr_virtfn;
|
||||
u8 max = 0;
|
||||
int busnr;
|
||||
int nr_virtfn, busnr, rc = 0;
|
||||
|
||||
for (nr_virtfn = 1; nr_virtfn <= iov->total_VFs; nr_virtfn++) {
|
||||
for (nr_virtfn = iov->total_VFs; nr_virtfn; nr_virtfn--) {
|
||||
pci_iov_set_numvfs(dev, nr_virtfn);
|
||||
if (!iov->offset || (nr_virtfn > 1 && !iov->stride)) {
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
busnr = pci_iov_virtfn_bus(dev, nr_virtfn - 1);
|
||||
if (busnr > max)
|
||||
max = busnr;
|
||||
if (busnr > iov->max_VF_buses)
|
||||
iov->max_VF_buses = busnr;
|
||||
}
|
||||
|
||||
return max;
|
||||
out:
|
||||
pci_iov_set_numvfs(dev, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
|
||||
|
@ -222,21 +227,25 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset)
|
|||
|
||||
int __weak pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs)
|
||||
{
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __weak pcibios_sriov_disable(struct pci_dev *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
|
||||
{
|
||||
int rc;
|
||||
int i, j;
|
||||
int i;
|
||||
int nres;
|
||||
u16 offset, stride, initial;
|
||||
u16 initial;
|
||||
struct resource *res;
|
||||
struct pci_dev *pdev;
|
||||
struct pci_sriov *iov = dev->sriov;
|
||||
int bars = 0;
|
||||
int bus;
|
||||
int retval;
|
||||
|
||||
if (!nr_virtfn)
|
||||
return 0;
|
||||
|
@ -253,11 +262,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
|
|||
(!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
|
||||
return -EINVAL;
|
||||
|
||||
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset);
|
||||
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride);
|
||||
if (!offset || (nr_virtfn > 1 && !stride))
|
||||
return -EIO;
|
||||
|
||||
nres = 0;
|
||||
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
|
||||
bars |= (1 << (i + PCI_IOV_RESOURCES));
|
||||
|
@ -270,9 +274,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
iov->offset = offset;
|
||||
iov->stride = stride;
|
||||
|
||||
bus = pci_iov_virtfn_bus(dev, nr_virtfn - 1);
|
||||
if (bus > dev->bus->busn_res.end) {
|
||||
dev_err(&dev->dev, "can't enable %d VFs (bus %02x out of range of %pR)\n",
|
||||
|
@ -313,10 +314,10 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
|
|||
if (nr_virtfn < initial)
|
||||
initial = nr_virtfn;
|
||||
|
||||
if ((retval = pcibios_sriov_enable(dev, initial))) {
|
||||
dev_err(&dev->dev, "failure %d from pcibios_sriov_enable()\n",
|
||||
retval);
|
||||
return retval;
|
||||
rc = pcibios_sriov_enable(dev, initial);
|
||||
if (rc) {
|
||||
dev_err(&dev->dev, "failure %d from pcibios_sriov_enable()\n", rc);
|
||||
goto err_pcibios;
|
||||
}
|
||||
|
||||
for (i = 0; i < initial; i++) {
|
||||
|
@ -331,27 +332,24 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
|
|||
return 0;
|
||||
|
||||
failed:
|
||||
for (j = 0; j < i; j++)
|
||||
virtfn_remove(dev, j, 0);
|
||||
while (i--)
|
||||
virtfn_remove(dev, i, 0);
|
||||
|
||||
pcibios_sriov_disable(dev);
|
||||
err_pcibios:
|
||||
iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
|
||||
pci_cfg_access_lock(dev);
|
||||
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
|
||||
pci_iov_set_numvfs(dev, 0);
|
||||
ssleep(1);
|
||||
pci_cfg_access_unlock(dev);
|
||||
|
||||
if (iov->link != dev->devfn)
|
||||
sysfs_remove_link(&dev->dev.kobj, "dep_link");
|
||||
|
||||
pci_iov_set_numvfs(dev, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __weak pcibios_sriov_disable(struct pci_dev *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sriov_disable(struct pci_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
@ -384,7 +382,7 @@ static int sriov_init(struct pci_dev *dev, int pos)
|
|||
int rc;
|
||||
int nres;
|
||||
u32 pgsz;
|
||||
u16 ctrl, total, offset, stride;
|
||||
u16 ctrl, total;
|
||||
struct pci_sriov *iov;
|
||||
struct resource *res;
|
||||
struct pci_dev *pdev;
|
||||
|
@ -399,10 +397,6 @@ static int sriov_init(struct pci_dev *dev, int pos)
|
|||
ssleep(1);
|
||||
}
|
||||
|
||||
pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
|
||||
if (!total)
|
||||
return 0;
|
||||
|
||||
ctrl = 0;
|
||||
list_for_each_entry(pdev, &dev->bus->devices, bus_list)
|
||||
if (pdev->is_physfn)
|
||||
|
@ -414,11 +408,10 @@ static int sriov_init(struct pci_dev *dev, int pos)
|
|||
|
||||
found:
|
||||
pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
|
||||
pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, 0);
|
||||
pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
|
||||
pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
|
||||
if (!offset || (total > 1 && !stride))
|
||||
return -EIO;
|
||||
|
||||
pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
|
||||
if (!total)
|
||||
return 0;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
|
||||
i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
|
||||
|
@ -436,8 +429,15 @@ found:
|
|||
nres = 0;
|
||||
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
|
||||
res = &dev->resource[i + PCI_IOV_RESOURCES];
|
||||
bar64 = __pci_read_base(dev, pci_bar_unknown, res,
|
||||
pos + PCI_SRIOV_BAR + i * 4);
|
||||
/*
|
||||
* If it is already FIXED, don't change it, something
|
||||
* (perhaps EA or header fixups) wants it this way.
|
||||
*/
|
||||
if (res->flags & IORESOURCE_PCI_FIXED)
|
||||
bar64 = (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
|
||||
else
|
||||
bar64 = __pci_read_base(dev, pci_bar_unknown, res,
|
||||
pos + PCI_SRIOV_BAR + i * 4);
|
||||
if (!res->flags)
|
||||
continue;
|
||||
if (resource_size(res) & (PAGE_SIZE - 1)) {
|
||||
|
@ -456,8 +456,6 @@ found:
|
|||
iov->nres = nres;
|
||||
iov->ctrl = ctrl;
|
||||
iov->total_VFs = total;
|
||||
iov->offset = offset;
|
||||
iov->stride = stride;
|
||||
iov->pgsz = pgsz;
|
||||
iov->self = dev;
|
||||
pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
|
||||
|
@ -474,10 +472,15 @@ found:
|
|||
|
||||
dev->sriov = iov;
|
||||
dev->is_physfn = 1;
|
||||
iov->max_VF_buses = virtfn_max_buses(dev);
|
||||
rc = compute_max_vf_buses(dev);
|
||||
if (rc)
|
||||
goto fail_max_buses;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_max_buses:
|
||||
dev->sriov = NULL;
|
||||
dev->is_physfn = 0;
|
||||
failed:
|
||||
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
|
||||
res = &dev->resource[i + PCI_IOV_RESOURCES];
|
||||
|
|
|
@ -106,9 +106,12 @@ void __weak arch_teardown_msi_irq(unsigned int irq)
|
|||
|
||||
int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
|
||||
{
|
||||
struct msi_controller *chip = dev->bus->msi;
|
||||
struct msi_desc *entry;
|
||||
int ret;
|
||||
|
||||
if (chip && chip->setup_irqs)
|
||||
return chip->setup_irqs(chip, dev, nvec, type);
|
||||
/*
|
||||
* If an architecture wants to support multiple MSI, it needs to
|
||||
* override arch_setup_msi_irqs()
|
||||
|
@ -476,10 +479,11 @@ static int populate_msi_sysfs(struct pci_dev *pdev)
|
|||
int ret = -ENOMEM;
|
||||
int num_msi = 0;
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
/* Determine how many msi entries we have */
|
||||
for_each_pci_msi_entry(entry, pdev)
|
||||
++num_msi;
|
||||
num_msi += entry->nvec_used;
|
||||
if (!num_msi)
|
||||
return 0;
|
||||
|
||||
|
@ -488,19 +492,21 @@ static int populate_msi_sysfs(struct pci_dev *pdev)
|
|||
if (!msi_attrs)
|
||||
return -ENOMEM;
|
||||
for_each_pci_msi_entry(entry, pdev) {
|
||||
msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL);
|
||||
if (!msi_dev_attr)
|
||||
goto error_attrs;
|
||||
msi_attrs[count] = &msi_dev_attr->attr;
|
||||
for (i = 0; i < entry->nvec_used; i++) {
|
||||
msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL);
|
||||
if (!msi_dev_attr)
|
||||
goto error_attrs;
|
||||
msi_attrs[count] = &msi_dev_attr->attr;
|
||||
|
||||
sysfs_attr_init(&msi_dev_attr->attr);
|
||||
msi_dev_attr->attr.name = kasprintf(GFP_KERNEL, "%d",
|
||||
entry->irq);
|
||||
if (!msi_dev_attr->attr.name)
|
||||
goto error_attrs;
|
||||
msi_dev_attr->attr.mode = S_IRUGO;
|
||||
msi_dev_attr->show = msi_mode_show;
|
||||
++count;
|
||||
sysfs_attr_init(&msi_dev_attr->attr);
|
||||
msi_dev_attr->attr.name = kasprintf(GFP_KERNEL, "%d",
|
||||
entry->irq + i);
|
||||
if (!msi_dev_attr->attr.name)
|
||||
goto error_attrs;
|
||||
msi_dev_attr->attr.mode = S_IRUGO;
|
||||
msi_dev_attr->show = msi_mode_show;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
msi_irq_group = kzalloc(sizeof(*msi_irq_group), GFP_KERNEL);
|
||||
|
|
|
@ -172,7 +172,7 @@ static ssize_t store_remove_id(struct device_driver *driver, const char *buf,
|
|||
__u32 vendor, device, subvendor = PCI_ANY_ID,
|
||||
subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
|
||||
int fields = 0;
|
||||
int retval = -ENODEV;
|
||||
size_t retval = -ENODEV;
|
||||
|
||||
fields = sscanf(buf, "%x %x %x %x %x %x",
|
||||
&vendor, &device, &subvendor, &subdevice,
|
||||
|
@ -190,15 +190,13 @@ static ssize_t store_remove_id(struct device_driver *driver, const char *buf,
|
|||
!((id->class ^ class) & class_mask)) {
|
||||
list_del(&dynid->node);
|
||||
kfree(dynid);
|
||||
retval = 0;
|
||||
retval = count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&pdrv->dynids.lock);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
return count;
|
||||
return retval;
|
||||
}
|
||||
static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/pci_hotplug.h>
|
||||
#include <asm-generic/pci-bridge.h>
|
||||
#include <asm/setup.h>
|
||||
#include <linux/aer.h>
|
||||
#include "pci.h"
|
||||
|
||||
const char *pci_power_names[] = {
|
||||
|
@ -457,6 +458,30 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev,
|
|||
}
|
||||
EXPORT_SYMBOL(pci_find_parent_resource);
|
||||
|
||||
/**
|
||||
* pci_find_pcie_root_port - return PCIe Root Port
|
||||
* @dev: PCI device to query
|
||||
*
|
||||
* Traverse up the parent chain and return the PCIe Root Port PCI Device
|
||||
* for a given PCI Device.
|
||||
*/
|
||||
struct pci_dev *pci_find_pcie_root_port(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *bridge, *highest_pcie_bridge = NULL;
|
||||
|
||||
bridge = pci_upstream_bridge(dev);
|
||||
while (bridge && pci_is_pcie(bridge)) {
|
||||
highest_pcie_bridge = bridge;
|
||||
bridge = pci_upstream_bridge(bridge);
|
||||
}
|
||||
|
||||
if (pci_pcie_type(highest_pcie_bridge) != PCI_EXP_TYPE_ROOT_PORT)
|
||||
return NULL;
|
||||
|
||||
return highest_pcie_bridge;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_find_pcie_root_port);
|
||||
|
||||
/**
|
||||
* pci_wait_for_pending - wait for @mask bit(s) to clear in status word @pos
|
||||
* @dev: the PCI device to operate on
|
||||
|
@ -484,7 +509,7 @@ int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask)
|
|||
}
|
||||
|
||||
/**
|
||||
* pci_restore_bars - restore a devices BAR values (e.g. after wake-up)
|
||||
* pci_restore_bars - restore a device's BAR values (e.g. after wake-up)
|
||||
* @dev: PCI device to have its BARs restored
|
||||
*
|
||||
* Restore the BAR values for a given device, so as to make it
|
||||
|
@ -494,6 +519,10 @@ static void pci_restore_bars(struct pci_dev *dev)
|
|||
{
|
||||
int i;
|
||||
|
||||
/* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */
|
||||
if (dev->is_virtfn)
|
||||
return;
|
||||
|
||||
for (i = 0; i < PCI_BRIDGE_RESOURCES; i++)
|
||||
pci_update_resource(dev, i);
|
||||
}
|
||||
|
@ -1099,6 +1128,8 @@ void pci_restore_state(struct pci_dev *dev)
|
|||
pci_restore_ats_state(dev);
|
||||
pci_restore_vc_state(dev);
|
||||
|
||||
pci_cleanup_aer_error_status_regs(dev);
|
||||
|
||||
pci_restore_config_space(dev);
|
||||
|
||||
pci_restore_pcix_state(dev);
|
||||
|
@ -2196,6 +2227,198 @@ void pci_pm_init(struct pci_dev *dev)
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned long pci_ea_flags(struct pci_dev *dev, u8 prop)
|
||||
{
|
||||
unsigned long flags = IORESOURCE_PCI_FIXED;
|
||||
|
||||
switch (prop) {
|
||||
case PCI_EA_P_MEM:
|
||||
case PCI_EA_P_VF_MEM:
|
||||
flags |= IORESOURCE_MEM;
|
||||
break;
|
||||
case PCI_EA_P_MEM_PREFETCH:
|
||||
case PCI_EA_P_VF_MEM_PREFETCH:
|
||||
flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
|
||||
break;
|
||||
case PCI_EA_P_IO:
|
||||
flags |= IORESOURCE_IO;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static struct resource *pci_ea_get_resource(struct pci_dev *dev, u8 bei,
|
||||
u8 prop)
|
||||
{
|
||||
if (bei <= PCI_EA_BEI_BAR5 && prop <= PCI_EA_P_IO)
|
||||
return &dev->resource[bei];
|
||||
#ifdef CONFIG_PCI_IOV
|
||||
else if (bei >= PCI_EA_BEI_VF_BAR0 && bei <= PCI_EA_BEI_VF_BAR5 &&
|
||||
(prop == PCI_EA_P_VF_MEM || prop == PCI_EA_P_VF_MEM_PREFETCH))
|
||||
return &dev->resource[PCI_IOV_RESOURCES +
|
||||
bei - PCI_EA_BEI_VF_BAR0];
|
||||
#endif
|
||||
else if (bei == PCI_EA_BEI_ROM)
|
||||
return &dev->resource[PCI_ROM_RESOURCE];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Read an Enhanced Allocation (EA) entry */
|
||||
static int pci_ea_read(struct pci_dev *dev, int offset)
|
||||
{
|
||||
struct resource *res;
|
||||
int ent_size, ent_offset = offset;
|
||||
resource_size_t start, end;
|
||||
unsigned long flags;
|
||||
u32 dw0, bei, base, max_offset;
|
||||
u8 prop;
|
||||
bool support_64 = (sizeof(resource_size_t) >= 8);
|
||||
|
||||
pci_read_config_dword(dev, ent_offset, &dw0);
|
||||
ent_offset += 4;
|
||||
|
||||
/* Entry size field indicates DWORDs after 1st */
|
||||
ent_size = ((dw0 & PCI_EA_ES) + 1) << 2;
|
||||
|
||||
if (!(dw0 & PCI_EA_ENABLE)) /* Entry not enabled */
|
||||
goto out;
|
||||
|
||||
bei = (dw0 & PCI_EA_BEI) >> 4;
|
||||
prop = (dw0 & PCI_EA_PP) >> 8;
|
||||
|
||||
/*
|
||||
* If the Property is in the reserved range, try the Secondary
|
||||
* Property instead.
|
||||
*/
|
||||
if (prop > PCI_EA_P_BRIDGE_IO && prop < PCI_EA_P_MEM_RESERVED)
|
||||
prop = (dw0 & PCI_EA_SP) >> 16;
|
||||
if (prop > PCI_EA_P_BRIDGE_IO)
|
||||
goto out;
|
||||
|
||||
res = pci_ea_get_resource(dev, bei, prop);
|
||||
if (!res) {
|
||||
dev_err(&dev->dev, "Unsupported EA entry BEI: %u\n", bei);
|
||||
goto out;
|
||||
}
|
||||
|
||||
flags = pci_ea_flags(dev, prop);
|
||||
if (!flags) {
|
||||
dev_err(&dev->dev, "Unsupported EA properties: %#x\n", prop);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Read Base */
|
||||
pci_read_config_dword(dev, ent_offset, &base);
|
||||
start = (base & PCI_EA_FIELD_MASK);
|
||||
ent_offset += 4;
|
||||
|
||||
/* Read MaxOffset */
|
||||
pci_read_config_dword(dev, ent_offset, &max_offset);
|
||||
ent_offset += 4;
|
||||
|
||||
/* Read Base MSBs (if 64-bit entry) */
|
||||
if (base & PCI_EA_IS_64) {
|
||||
u32 base_upper;
|
||||
|
||||
pci_read_config_dword(dev, ent_offset, &base_upper);
|
||||
ent_offset += 4;
|
||||
|
||||
flags |= IORESOURCE_MEM_64;
|
||||
|
||||
/* entry starts above 32-bit boundary, can't use */
|
||||
if (!support_64 && base_upper)
|
||||
goto out;
|
||||
|
||||
if (support_64)
|
||||
start |= ((u64)base_upper << 32);
|
||||
}
|
||||
|
||||
end = start + (max_offset | 0x03);
|
||||
|
||||
/* Read MaxOffset MSBs (if 64-bit entry) */
|
||||
if (max_offset & PCI_EA_IS_64) {
|
||||
u32 max_offset_upper;
|
||||
|
||||
pci_read_config_dword(dev, ent_offset, &max_offset_upper);
|
||||
ent_offset += 4;
|
||||
|
||||
flags |= IORESOURCE_MEM_64;
|
||||
|
||||
/* entry too big, can't use */
|
||||
if (!support_64 && max_offset_upper)
|
||||
goto out;
|
||||
|
||||
if (support_64)
|
||||
end += ((u64)max_offset_upper << 32);
|
||||
}
|
||||
|
||||
if (end < start) {
|
||||
dev_err(&dev->dev, "EA Entry crosses address boundary\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ent_size != ent_offset - offset) {
|
||||
dev_err(&dev->dev,
|
||||
"EA Entry Size (%d) does not match length read (%d)\n",
|
||||
ent_size, ent_offset - offset);
|
||||
goto out;
|
||||
}
|
||||
|
||||
res->name = pci_name(dev);
|
||||
res->start = start;
|
||||
res->end = end;
|
||||
res->flags = flags;
|
||||
|
||||
if (bei <= PCI_EA_BEI_BAR5)
|
||||
dev_printk(KERN_DEBUG, &dev->dev, "BAR %d: %pR (from Enhanced Allocation, properties %#02x)\n",
|
||||
bei, res, prop);
|
||||
else if (bei == PCI_EA_BEI_ROM)
|
||||
dev_printk(KERN_DEBUG, &dev->dev, "ROM: %pR (from Enhanced Allocation, properties %#02x)\n",
|
||||
res, prop);
|
||||
else if (bei >= PCI_EA_BEI_VF_BAR0 && bei <= PCI_EA_BEI_VF_BAR5)
|
||||
dev_printk(KERN_DEBUG, &dev->dev, "VF BAR %d: %pR (from Enhanced Allocation, properties %#02x)\n",
|
||||
bei - PCI_EA_BEI_VF_BAR0, res, prop);
|
||||
else
|
||||
dev_printk(KERN_DEBUG, &dev->dev, "BEI %d res: %pR (from Enhanced Allocation, properties %#02x)\n",
|
||||
bei, res, prop);
|
||||
|
||||
out:
|
||||
return offset + ent_size;
|
||||
}
|
||||
|
||||
/* Enhanced Allocation Initalization */
|
||||
void pci_ea_init(struct pci_dev *dev)
|
||||
{
|
||||
int ea;
|
||||
u8 num_ent;
|
||||
int offset;
|
||||
int i;
|
||||
|
||||
/* find PCI EA capability in list */
|
||||
ea = pci_find_capability(dev, PCI_CAP_ID_EA);
|
||||
if (!ea)
|
||||
return;
|
||||
|
||||
/* determine the number of entries */
|
||||
pci_bus_read_config_byte(dev->bus, dev->devfn, ea + PCI_EA_NUM_ENT,
|
||||
&num_ent);
|
||||
num_ent &= PCI_EA_NUM_ENT_MASK;
|
||||
|
||||
offset = ea + PCI_EA_FIRST_ENT;
|
||||
|
||||
/* Skip DWORD 2 for type 1 functions */
|
||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
|
||||
offset += 4;
|
||||
|
||||
/* parse each EA entry */
|
||||
for (i = 0; i < num_ent; ++i)
|
||||
offset = pci_ea_read(dev, offset);
|
||||
}
|
||||
|
||||
static void pci_add_saved_cap(struct pci_dev *pci_dev,
|
||||
struct pci_cap_saved_state *new_cap)
|
||||
{
|
||||
|
|
|
@ -79,6 +79,7 @@ void pci_dev_complete_resume(struct pci_dev *pci_dev);
|
|||
void pci_config_pm_runtime_get(struct pci_dev *dev);
|
||||
void pci_config_pm_runtime_put(struct pci_dev *dev);
|
||||
void pci_pm_init(struct pci_dev *dev);
|
||||
void pci_ea_init(struct pci_dev *dev);
|
||||
void pci_allocate_cap_save_buffers(struct pci_dev *dev);
|
||||
void pci_free_cap_save_buffers(struct pci_dev *dev);
|
||||
|
||||
|
|
|
@ -74,6 +74,34 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
|
||||
|
||||
int pci_cleanup_aer_error_status_regs(struct pci_dev *dev)
|
||||
{
|
||||
int pos;
|
||||
u32 status;
|
||||
int port_type;
|
||||
|
||||
if (!pci_is_pcie(dev))
|
||||
return -ENODEV;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!pos)
|
||||
return -EIO;
|
||||
|
||||
port_type = pci_pcie_type(dev);
|
||||
if (port_type == PCI_EXP_TYPE_ROOT_PORT) {
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status);
|
||||
}
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status);
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status);
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* add_error_device - list device to be handled
|
||||
* @e_info: pointer to error info
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/pci-aspm.h>
|
||||
#include <linux/aer.h>
|
||||
#include <asm-generic/pci-bridge.h>
|
||||
#include "pci.h"
|
||||
|
||||
|
@ -1597,6 +1598,9 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
|
|||
|
||||
static void pci_init_capabilities(struct pci_dev *dev)
|
||||
{
|
||||
/* Enhanced Allocation */
|
||||
pci_ea_init(dev);
|
||||
|
||||
/* MSI/MSI-X list */
|
||||
pci_msi_init_pci_dev(dev);
|
||||
|
||||
|
@ -1620,6 +1624,8 @@ static void pci_init_capabilities(struct pci_dev *dev)
|
|||
|
||||
/* Enable ACS P2P upstream forwarding */
|
||||
pci_enable_acs(dev);
|
||||
|
||||
pci_cleanup_aer_error_status_regs(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -2246,6 +2246,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3336, quirk_disab
|
|||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3351, quirk_disable_all_msi);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3364, quirk_disable_all_msi);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8380_0, quirk_disable_all_msi);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, 0x0761, quirk_disable_all_msi);
|
||||
|
||||
/* Disable MSI on chipsets that are known to not support it */
|
||||
static void quirk_disable_msi(struct pci_dev *dev)
|
||||
|
@ -3707,6 +3708,63 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(0x1797, 0x6868, PCI_CLASS_NOT_DEFINED, 8,
|
|||
DECLARE_PCI_FIXUP_CLASS_EARLY(0x1797, 0x6869, PCI_CLASS_NOT_DEFINED, 8,
|
||||
quirk_tw686x_class);
|
||||
|
||||
/*
|
||||
* Per PCIe r3.0, sec 2.2.9, "Completion headers must supply the same
|
||||
* values for the Attribute as were supplied in the header of the
|
||||
* corresponding Request, except as explicitly allowed when IDO is used."
|
||||
*
|
||||
* If a non-compliant device generates a completion with a different
|
||||
* attribute than the request, the receiver may accept it (which itself
|
||||
* seems non-compliant based on sec 2.3.2), or it may handle it as a
|
||||
* Malformed TLP or an Unexpected Completion, which will probably lead to a
|
||||
* device access timeout.
|
||||
*
|
||||
* If the non-compliant device generates completions with zero attributes
|
||||
* (instead of copying the attributes from the request), we can work around
|
||||
* this by disabling the "Relaxed Ordering" and "No Snoop" attributes in
|
||||
* upstream devices so they always generate requests with zero attributes.
|
||||
*
|
||||
* This affects other devices under the same Root Port, but since these
|
||||
* attributes are performance hints, there should be no functional problem.
|
||||
*
|
||||
* Note that Configuration Space accesses are never supposed to have TLP
|
||||
* Attributes, so we're safe waiting till after any Configuration Space
|
||||
* accesses to do the Root Port fixup.
|
||||
*/
|
||||
static void quirk_disable_root_port_attributes(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_dev *root_port = pci_find_pcie_root_port(pdev);
|
||||
|
||||
if (!root_port) {
|
||||
dev_warn(&pdev->dev, "PCIe Completion erratum may cause device errors\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_info(&root_port->dev, "Disabling No Snoop/Relaxed Ordering Attributes to avoid PCIe Completion erratum in %s\n",
|
||||
dev_name(&pdev->dev));
|
||||
pcie_capability_clear_and_set_word(root_port, PCI_EXP_DEVCTL,
|
||||
PCI_EXP_DEVCTL_RELAX_EN |
|
||||
PCI_EXP_DEVCTL_NOSNOOP_EN, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The Chelsio T5 chip fails to copy TLP Attributes from a Request to the
|
||||
* Completion it generates.
|
||||
*/
|
||||
static void quirk_chelsio_T5_disable_root_port_attributes(struct pci_dev *pdev)
|
||||
{
|
||||
/*
|
||||
* This mask/compare operation selects for Physical Function 4 on a
|
||||
* T5. We only need to fix up the Root Port once for any of the
|
||||
* PFs. PF[0..3] have PCI Device IDs of 0x50xx, but PF4 is uniquely
|
||||
* 0x54xx so we use that one,
|
||||
*/
|
||||
if ((pdev->device & 0xff00) == 0x5400)
|
||||
quirk_disable_root_port_attributes(pdev);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
|
||||
quirk_chelsio_T5_disable_root_port_attributes);
|
||||
|
||||
/*
|
||||
* AMD has indicated that the devices below do not support peer-to-peer
|
||||
* in any system where they are found in the southbridge with an AMD
|
||||
|
|
|
@ -1037,9 +1037,10 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
|||
struct resource *r = &dev->resource[i];
|
||||
resource_size_t r_size;
|
||||
|
||||
if (r->parent || ((r->flags & mask) != type &&
|
||||
(r->flags & mask) != type2 &&
|
||||
(r->flags & mask) != type3))
|
||||
if (r->parent || (r->flags & IORESOURCE_PCI_FIXED) ||
|
||||
((r->flags & mask) != type &&
|
||||
(r->flags & mask) != type2 &&
|
||||
(r->flags & mask) != type3))
|
||||
continue;
|
||||
r_size = resource_size(r);
|
||||
#ifdef CONFIG_PCI_IOV
|
||||
|
@ -1340,6 +1341,47 @@ void pci_bus_size_bridges(struct pci_bus *bus)
|
|||
}
|
||||
EXPORT_SYMBOL(pci_bus_size_bridges);
|
||||
|
||||
static void assign_fixed_resource_on_bus(struct pci_bus *b, struct resource *r)
|
||||
{
|
||||
int i;
|
||||
struct resource *parent_r;
|
||||
unsigned long mask = IORESOURCE_IO | IORESOURCE_MEM |
|
||||
IORESOURCE_PREFETCH;
|
||||
|
||||
pci_bus_for_each_resource(b, parent_r, i) {
|
||||
if (!parent_r)
|
||||
continue;
|
||||
|
||||
if ((r->flags & mask) == (parent_r->flags & mask) &&
|
||||
resource_contains(parent_r, r))
|
||||
request_resource(parent_r, r);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to assign any resources marked as IORESOURCE_PCI_FIXED, as they
|
||||
* are skipped by pbus_assign_resources_sorted().
|
||||
*/
|
||||
static void pdev_assign_fixed_resources(struct pci_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
||||
struct pci_bus *b;
|
||||
struct resource *r = &dev->resource[i];
|
||||
|
||||
if (r->parent || !(r->flags & IORESOURCE_PCI_FIXED) ||
|
||||
!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
|
||||
continue;
|
||||
|
||||
b = dev->bus;
|
||||
while (b && !r->parent) {
|
||||
assign_fixed_resource_on_bus(b, r);
|
||||
b = b->parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __pci_bus_assign_resources(const struct pci_bus *bus,
|
||||
struct list_head *realloc_head,
|
||||
struct list_head *fail_head)
|
||||
|
@ -1350,6 +1392,8 @@ void __pci_bus_assign_resources(const struct pci_bus *bus,
|
|||
pbus_assign_resources_sorted(bus, realloc_head, fail_head);
|
||||
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
pdev_assign_fixed_resources(dev);
|
||||
|
||||
b = dev->subordinate;
|
||||
if (!b)
|
||||
continue;
|
||||
|
|
|
@ -36,6 +36,11 @@ void pci_update_resource(struct pci_dev *dev, int resno)
|
|||
enum pci_bar_type type;
|
||||
struct resource *res = dev->resource + resno;
|
||||
|
||||
if (dev->is_virtfn) {
|
||||
dev_warn(&dev->dev, "can't update VF BAR%d\n", resno);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore resources for unimplemented BARs and unused resource slots
|
||||
* for 64 bit BARs.
|
||||
|
@ -177,6 +182,7 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
|
|||
end = res->end;
|
||||
res->start = fw_addr;
|
||||
res->end = res->start + size - 1;
|
||||
res->flags &= ~IORESOURCE_UNSET;
|
||||
|
||||
root = pci_find_parent_resource(dev, res);
|
||||
if (!root) {
|
||||
|
@ -194,6 +200,7 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
|
|||
resno, res, conflict->name, conflict);
|
||||
res->start = start;
|
||||
res->end = end;
|
||||
res->flags |= IORESOURCE_UNSET;
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -42,6 +42,7 @@ struct aer_capability_regs {
|
|||
int pci_enable_pcie_error_reporting(struct pci_dev *dev);
|
||||
int pci_disable_pcie_error_reporting(struct pci_dev *dev);
|
||||
int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev);
|
||||
int pci_cleanup_aer_error_status_regs(struct pci_dev *dev);
|
||||
#else
|
||||
static inline int pci_enable_pcie_error_reporting(struct pci_dev *dev)
|
||||
{
|
||||
|
@ -55,6 +56,10 @@ static inline int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
|
|||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int pci_cleanup_aer_error_status_regs(struct pci_dev *dev)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
void cper_print_aer(struct pci_dev *dev, int cper_severity,
|
||||
|
|
|
@ -163,6 +163,8 @@ struct msi_controller {
|
|||
|
||||
int (*setup_irq)(struct msi_controller *chip, struct pci_dev *dev,
|
||||
struct msi_desc *desc);
|
||||
int (*setup_irqs)(struct msi_controller *chip, struct pci_dev *dev,
|
||||
int nvec, int type);
|
||||
void (*teardown_irq)(struct msi_controller *chip, unsigned int irq);
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
|
|||
int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
|
||||
int of_get_pci_domain_nr(struct device_node *node);
|
||||
void of_pci_dma_configure(struct pci_dev *pci_dev);
|
||||
void of_pci_check_probe_only(void);
|
||||
#else
|
||||
static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
|
||||
{
|
||||
|
@ -53,6 +54,8 @@ of_get_pci_domain_nr(struct device_node *node)
|
|||
}
|
||||
|
||||
static inline void of_pci_dma_configure(struct pci_dev *pci_dev) { }
|
||||
|
||||
static inline void of_pci_check_probe_only(void) { }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF_ADDRESS)
|
||||
|
|
|
@ -820,6 +820,7 @@ void pci_bus_add_device(struct pci_dev *dev);
|
|||
void pci_read_bridge_bases(struct pci_bus *child);
|
||||
struct resource *pci_find_parent_resource(const struct pci_dev *dev,
|
||||
struct resource *res);
|
||||
struct pci_dev *pci_find_pcie_root_port(struct pci_dev *dev);
|
||||
u8 pci_swizzle_interrupt_pin(const struct pci_dev *dev, u8 pin);
|
||||
int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge);
|
||||
u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp);
|
||||
|
@ -1192,6 +1193,17 @@ void pci_unregister_driver(struct pci_driver *dev);
|
|||
module_driver(__pci_driver, pci_register_driver, \
|
||||
pci_unregister_driver)
|
||||
|
||||
/**
|
||||
* builtin_pci_driver() - Helper macro for registering a PCI driver
|
||||
* @__pci_driver: pci_driver struct
|
||||
*
|
||||
* Helper macro for PCI drivers which do not do anything special in their
|
||||
* init code. This eliminates a lot of boilerplate. Each driver may only
|
||||
* use this macro once, and calling it replaces device_initcall(...)
|
||||
*/
|
||||
#define builtin_pci_driver(__pci_driver) \
|
||||
builtin_driver(__pci_driver, pci_register_driver)
|
||||
|
||||
struct pci_driver *pci_dev_driver(const struct pci_dev *dev);
|
||||
int pci_add_dynid(struct pci_driver *drv,
|
||||
unsigned int vendor, unsigned int device,
|
||||
|
|
|
@ -216,7 +216,8 @@
|
|||
#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */
|
||||
#define PCI_CAP_ID_SATA 0x12 /* SATA Data/Index Conf. */
|
||||
#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */
|
||||
#define PCI_CAP_ID_MAX PCI_CAP_ID_AF
|
||||
#define PCI_CAP_ID_EA 0x14 /* PCI Enhanced Allocation */
|
||||
#define PCI_CAP_ID_MAX PCI_CAP_ID_EA
|
||||
#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */
|
||||
#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */
|
||||
#define PCI_CAP_SIZEOF 4
|
||||
|
@ -353,6 +354,46 @@
|
|||
#define PCI_AF_STATUS_TP 0x01
|
||||
#define PCI_CAP_AF_SIZEOF 6 /* size of AF registers */
|
||||
|
||||
/* PCI Enhanced Allocation registers */
|
||||
|
||||
#define PCI_EA_NUM_ENT 2 /* Number of Capability Entries */
|
||||
#define PCI_EA_NUM_ENT_MASK 0x3f /* Num Entries Mask */
|
||||
#define PCI_EA_FIRST_ENT 4 /* First EA Entry in List */
|
||||
#define PCI_EA_FIRST_ENT_BRIDGE 8 /* First EA Entry for Bridges */
|
||||
#define PCI_EA_ES 0x00000007 /* Entry Size */
|
||||
#define PCI_EA_BEI 0x000000f0 /* BAR Equivalent Indicator */
|
||||
/* 0-5 map to BARs 0-5 respectively */
|
||||
#define PCI_EA_BEI_BAR0 0
|
||||
#define PCI_EA_BEI_BAR5 5
|
||||
#define PCI_EA_BEI_BRIDGE 6 /* Resource behind bridge */
|
||||
#define PCI_EA_BEI_ENI 7 /* Equivalent Not Indicated */
|
||||
#define PCI_EA_BEI_ROM 8 /* Expansion ROM */
|
||||
/* 9-14 map to VF BARs 0-5 respectively */
|
||||
#define PCI_EA_BEI_VF_BAR0 9
|
||||
#define PCI_EA_BEI_VF_BAR5 14
|
||||
#define PCI_EA_BEI_RESERVED 15 /* Reserved - Treat like ENI */
|
||||
#define PCI_EA_PP 0x0000ff00 /* Primary Properties */
|
||||
#define PCI_EA_SP 0x00ff0000 /* Secondary Properties */
|
||||
#define PCI_EA_P_MEM 0x00 /* Non-Prefetch Memory */
|
||||
#define PCI_EA_P_MEM_PREFETCH 0x01 /* Prefetchable Memory */
|
||||
#define PCI_EA_P_IO 0x02 /* I/O Space */
|
||||
#define PCI_EA_P_VF_MEM_PREFETCH 0x03 /* VF Prefetchable Memory */
|
||||
#define PCI_EA_P_VF_MEM 0x04 /* VF Non-Prefetch Memory */
|
||||
#define PCI_EA_P_BRIDGE_MEM 0x05 /* Bridge Non-Prefetch Memory */
|
||||
#define PCI_EA_P_BRIDGE_MEM_PREFETCH 0x06 /* Bridge Prefetchable Memory */
|
||||
#define PCI_EA_P_BRIDGE_IO 0x07 /* Bridge I/O Space */
|
||||
/* 0x08-0xfc reserved */
|
||||
#define PCI_EA_P_MEM_RESERVED 0xfd /* Reserved Memory */
|
||||
#define PCI_EA_P_IO_RESERVED 0xfe /* Reserved I/O Space */
|
||||
#define PCI_EA_P_UNAVAILABLE 0xff /* Entry Unavailable */
|
||||
#define PCI_EA_WRITABLE 0x40000000 /* Writable: 1 = RW, 0 = HwInit */
|
||||
#define PCI_EA_ENABLE 0x80000000 /* Enable for this entry */
|
||||
#define PCI_EA_BASE 4 /* Base Address Offset */
|
||||
#define PCI_EA_MAX_OFFSET 8 /* MaxOffset (resource length) */
|
||||
/* bit 0 is reserved */
|
||||
#define PCI_EA_IS_64 0x00000002 /* 64-bit field flag */
|
||||
#define PCI_EA_FIELD_MASK 0xfffffffc /* For Base & Max Offset */
|
||||
|
||||
/* PCI-X registers (Type 0 (non-bridge) devices) */
|
||||
|
||||
#define PCI_X_CMD 2 /* Modes & Features */
|
||||
|
|
Loading…
Reference in New Issue