Merge branches 'pci/host-altera', 'pci/host-designware', 'pci/host-generic', 'pci/host-imx6', 'pci/host-iproc', 'pci/host-mvebu', 'pci/host-rcar', 'pci/host-tegra' and 'pci/host-xgene' into next

* pci/host-altera:
  PCI: altera: Add Altera PCIe MSI driver
  PCI: altera: Add Altera PCIe host controller driver
  ARM: Add msi.h to Kbuild

* pci/host-designware:
  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
  PCI: designware: Set up high part of MSI target address
  PCI: designware: Make get_msi_addr() return phys_addr_t, not u32
  PCI: designware: Implement multivector MSI IRQ setup
  PCI: designware: Factor out MSI msg setup
  PCI: Add msi_controller setup_irqs() method for special multivector setup
  PCI: designware: Fix PORT_LOGIC_LINK_WIDTH_MASK

* pci/host-generic:
  PCI: generic: Fix address window calculation for non-zero starting bus
  PCI: generic: Pass starting bus number to pci_scan_root_bus()
  PCI: generic: Allow multiple hosts with different map_bus() methods
  arm64: dts: Drop linux,pci-probe-only from the Seattle DTS
  powerpc/PCI: Fix lookup of linux,pci-probe-only property
  PCI: generic: Fix lookup of linux,pci-probe-only property
  of/pci: Add of_pci_check_probe_only to parse "linux,pci-probe-only"

* pci/host-imx6:
  PCI: imx6: Add PCIE_PHY_RX_ASIC_OUT_VALID definition
  PCI: imx6: Return real error code from imx6_add_pcie_port()

* pci/host-iproc:
  PCI: iproc: Fix header comment "Corporation" misspelling
  PCI: iproc: Add outbound mapping support
  PCI: iproc: Update PCIe device tree bindings
  PCI: iproc: Improve link detection logic
  PCI: iproc: Fix PCIe reset logic
  PCI: iproc: Call pci_fixup_irqs() for ARM64 as well as ARM
  PCI: iproc: Remove unused struct iproc_pcie.irqs[]
  PCI: iproc: Fix code comment to match code

* pci/host-mvebu:
  PCI: mvebu: Remove code restricting accesses to slot 0
  PCI: mvebu: Add PCI Express root complex capability block
  PCI: mvebu: Improve clock/reset handling
  PCI: mvebu: Use gpio_desc to carry around gpio
  PCI: mvebu: Use devm_kcalloc() to allocate an array
  PCI: mvebu: Use gpio_set_value_cansleep()
  PCI: mvebu: Split port parsing and resource claiming from  port setup
  PCI: mvebu: Fix memory leaks and refcount leaks
  PCI: mvebu: Move port parsing and resource claiming to  separate function
  PCI: mvebu: Use port->name rather than "PCIe%d.%d"
  PCI: mvebu: Report full node name when reporting a DT error
  PCI: mvebu: Use for_each_available_child_of_node() to walk child nodes
  PCI: mvebu: Use of_get_available_child_count()
  PCI: mvebu: Use exact config access size; don't read/modify/write
  PCI: mvebu: Return zero for reserved or unimplemented config space

* pci/host-rcar:
  PCI: rcar: Fix I/O offset for multiple host bridges
  PCI: rcar: Set root bus nr to that provided in DT
  PCI: rcar: Remove dependency on ARM-specific struct hw_pci
  PCI: rcar: Make PCI aware of the I/O resources
  PCI: rcar: Build pcie-rcar.c only on ARM
  PCI: rcar: Build pci-rcar-gen2.c only on ARM

* pci/host-tegra:
  PCI: tegra: Wrap static pgprot_t initializer with __pgprot()

* pci/host-xgene:
  PCI/MSI: xgene: Remove msi_controller assignment
This commit is contained in:
Bjorn Helgaas 2015-11-03 08:38:27 -06:00
35 changed files with 1907 additions and 499 deletions

View 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>;
};

View 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>;
};

View File

@ -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 {

View File

@ -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)

View File

@ -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:

View File

@ -7938,6 +7938,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
@ -8039,6 +8047,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

View File

@ -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

View File

@ -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 */
};

View File

@ -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;
}

View File

@ -14,7 +14,6 @@
chosen {
stdout-path = &serial0;
linux,pci-probe-only;
};
};

View File

@ -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)

View File

@ -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

View File

@ -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.
@ -145,4 +147,21 @@ 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.
endmenu

View File

@ -17,3 +17,5 @@ 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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);
}
/**

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -0,0 +1,312 @@
/*
* 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)
{
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(msi->pdev->dev.of_node,
&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");

View 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");

View File

@ -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);

View File

@ -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);

View File

@ -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)) {

View File

@ -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;

View File

@ -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);

View File

@ -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 = {

View File

@ -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);
}
}

View File

@ -105,9 +105,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()

View File

@ -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);
};

View File

@ -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)