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:
@@ -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
|
||||
|
||||
|
||||
28
Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
Normal file
28
Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
Normal file
@@ -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>;
|
||||
};
|
||||
49
Documentation/devicetree/bindings/pci/altera-pcie.txt
Normal file
49
Documentation/devicetree/bindings/pci/altera-pcie.txt
Normal file
@@ -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)
|
||||
|
||||
44
Documentation/devicetree/bindings/pci/hisilicon-pcie.txt
Normal file
44
Documentation/devicetree/bindings/pci/hisilicon-pcie.txt
Normal file
@@ -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);
|
||||
|
||||
314
drivers/pci/host/pcie-altera-msi.c
Normal file
314
drivers/pci/host/pcie-altera-msi.c
Normal file
@@ -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");
|
||||
579
drivers/pci/host/pcie-altera.c
Normal file
579
drivers/pci/host/pcie-altera.c
Normal file
@@ -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);
|
||||
|
||||
198
drivers/pci/host/pcie-hisi.c
Normal file
198
drivers/pci/host/pcie-hisi.c
Normal file
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user