Merge branches 'pci/host-aardvark', 'pci/host-altera', 'pci/host-dra7xx', 'pci/host-hv', 'pci/host-vmd' and 'pci/host-xilinx' into next
* pci/host-aardvark: arm64: dts: marvell: Add Aardvark PCIe support for Armada 3700 PCI: aardvark: Add Aardvark PCI host controller driver dt-bindings: add DT binding for the Aardvark PCIe controller * pci/host-altera: PCI: altera: Poll for link up status after retraining the link PCI: altera: Check link status before retrain link PCI: altera: Reorder read/write functions * pci/host-dra7xx: PCI: dra7xx: Fix return value in case of error * pci/host-hv: PCI: hv: Fix interrupt cleanup path PCI: hv: Handle all pending messages in hv_pci_onchannelcallback() PCI: hv: Don't leak buffer in hv_pci_onchannelcallback() * pci/host-vmd: x86/PCI: VMD: Separate MSI and MSI-X vector sharing x86/PCI: VMD: Use x86_vector_domain as parent domain x86/PCI: VMD: Use lock save/restore in interrupt enable path x86/PCI: VMD: Initialize list item in IRQ disable x86/PCI: VMD: Select device dma ops to override * pci/host-xilinx: PCI: xilinx: Fix return value in case of error Manually apply changes from pci/demodularize-hosts and pci/host-request-windows to drivers/pci/host/pci-aardvark.c
This commit is contained in:
commit
a04bee8285
12 changed files with 1182 additions and 51 deletions
56
Documentation/devicetree/bindings/pci/aardvark-pci.txt
Normal file
56
Documentation/devicetree/bindings/pci/aardvark-pci.txt
Normal file
|
@ -0,0 +1,56 @@
|
|||
Aardvark PCIe controller
|
||||
|
||||
This PCIe controller is used on the Marvell Armada 3700 ARM64 SoC.
|
||||
|
||||
The Device Tree node describing an Aardvark PCIe controller must
|
||||
contain the following properties:
|
||||
|
||||
- compatible: Should be "marvell,armada-3700-pcie"
|
||||
- reg: range of registers for the PCIe controller
|
||||
- interrupts: the interrupt line of the PCIe controller
|
||||
- #address-cells: set to <3>
|
||||
- #size-cells: set to <2>
|
||||
- device_type: set to "pci"
|
||||
- ranges: ranges for the PCI memory and I/O regions
|
||||
- #interrupt-cells: set to <1>
|
||||
- msi-controller: indicates that the PCIe controller can itself
|
||||
handle MSI interrupts
|
||||
- msi-parent: pointer to the MSI controller to be used
|
||||
- interrupt-map-mask and interrupt-map: standard PCI properties to
|
||||
define the mapping of the PCIe interface to interrupt numbers.
|
||||
- bus-range: PCI bus numbers covered
|
||||
|
||||
In addition, the Device Tree describing an Aardvark PCIe controller
|
||||
must include a sub-node that describes the legacy interrupt controller
|
||||
built into the PCIe controller. This sub-node must have the following
|
||||
properties:
|
||||
|
||||
- interrupt-controller
|
||||
- #interrupt-cells: set to <1>
|
||||
|
||||
Example:
|
||||
|
||||
pcie0: pcie@d0070000 {
|
||||
compatible = "marvell,armada-3700-pcie";
|
||||
device_type = "pci";
|
||||
status = "disabled";
|
||||
reg = <0 0xd0070000 0 0x20000>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
bus-range = <0x00 0xff>;
|
||||
interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#interrupt-cells = <1>;
|
||||
msi-controller;
|
||||
msi-parent = <&pcie0>;
|
||||
ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */
|
||||
0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0 0 0 1 &pcie_intc 0>,
|
||||
<0 0 0 2 &pcie_intc 1>,
|
||||
<0 0 0 3 &pcie_intc 2>,
|
||||
<0 0 0 4 &pcie_intc 3>;
|
||||
pcie_intc: interrupt-controller {
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
};
|
|
@ -8742,6 +8742,13 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
|||
S: Maintained
|
||||
F: drivers/pci/host/*mvebu*
|
||||
|
||||
PCI DRIVER FOR AARDVARK (Marvell Armada 3700)
|
||||
M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: drivers/pci/host/pci-aardvark.c
|
||||
|
||||
PCI DRIVER FOR NVIDIA TEGRA
|
||||
M: Thierry Reding <thierry.reding@gmail.com>
|
||||
L: linux-tegra@vger.kernel.org
|
||||
|
|
|
@ -76,3 +76,8 @@
|
|||
&usb3 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
/* CON17 (PCIe) / CON12 (mini-PCIe) */
|
||||
&pcie0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
|
|
@ -141,5 +141,30 @@
|
|||
<0x1d40000 0x40000>; /* GICR */
|
||||
};
|
||||
};
|
||||
|
||||
pcie0: pcie@d0070000 {
|
||||
compatible = "marvell,armada-3700-pcie";
|
||||
device_type = "pci";
|
||||
status = "disabled";
|
||||
reg = <0 0xd0070000 0 0x20000>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
bus-range = <0x00 0xff>;
|
||||
interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#interrupt-cells = <1>;
|
||||
msi-parent = <&pcie0>;
|
||||
msi-controller;
|
||||
ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */
|
||||
0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0 0 0 1 &pcie_intc 0>,
|
||||
<0 0 0 2 &pcie_intc 1>,
|
||||
<0 0 0 3 &pcie_intc 2>,
|
||||
<0 0 0 4 &pcie_intc 3>;
|
||||
pcie_intc: interrupt-controller {
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -119,10 +119,11 @@ static void vmd_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
|||
static void vmd_irq_enable(struct irq_data *data)
|
||||
{
|
||||
struct vmd_irq *vmdirq = data->chip_data;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock(&list_lock);
|
||||
raw_spin_lock_irqsave(&list_lock, flags);
|
||||
list_add_tail_rcu(&vmdirq->node, &vmdirq->irq->irq_list);
|
||||
raw_spin_unlock(&list_lock);
|
||||
raw_spin_unlock_irqrestore(&list_lock, flags);
|
||||
|
||||
data->chip->irq_unmask(data);
|
||||
}
|
||||
|
@ -130,12 +131,14 @@ static void vmd_irq_enable(struct irq_data *data)
|
|||
static void vmd_irq_disable(struct irq_data *data)
|
||||
{
|
||||
struct vmd_irq *vmdirq = data->chip_data;
|
||||
unsigned long flags;
|
||||
|
||||
data->chip->irq_mask(data);
|
||||
|
||||
raw_spin_lock(&list_lock);
|
||||
raw_spin_lock_irqsave(&list_lock, flags);
|
||||
list_del_rcu(&vmdirq->node);
|
||||
raw_spin_unlock(&list_lock);
|
||||
INIT_LIST_HEAD_RCU(&vmdirq->node);
|
||||
raw_spin_unlock_irqrestore(&list_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -166,16 +169,20 @@ static irq_hw_number_t vmd_get_hwirq(struct msi_domain_info *info,
|
|||
* XXX: We can be even smarter selecting the best IRQ once we solve the
|
||||
* affinity problem.
|
||||
*/
|
||||
static struct vmd_irq_list *vmd_next_irq(struct vmd_dev *vmd)
|
||||
static struct vmd_irq_list *vmd_next_irq(struct vmd_dev *vmd, struct msi_desc *desc)
|
||||
{
|
||||
int i, best = 0;
|
||||
int i, best = 1;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock(&list_lock);
|
||||
if (!desc->msi_attrib.is_msix || vmd->msix_count == 1)
|
||||
return &vmd->irqs[0];
|
||||
|
||||
raw_spin_lock_irqsave(&list_lock, flags);
|
||||
for (i = 1; i < vmd->msix_count; i++)
|
||||
if (vmd->irqs[i].count < vmd->irqs[best].count)
|
||||
best = i;
|
||||
vmd->irqs[best].count++;
|
||||
raw_spin_unlock(&list_lock);
|
||||
raw_spin_unlock_irqrestore(&list_lock, flags);
|
||||
|
||||
return &vmd->irqs[best];
|
||||
}
|
||||
|
@ -184,14 +191,15 @@ static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info,
|
|||
unsigned int virq, irq_hw_number_t hwirq,
|
||||
msi_alloc_info_t *arg)
|
||||
{
|
||||
struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(arg->desc)->bus);
|
||||
struct msi_desc *desc = arg->desc;
|
||||
struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(desc)->bus);
|
||||
struct vmd_irq *vmdirq = kzalloc(sizeof(*vmdirq), GFP_KERNEL);
|
||||
|
||||
if (!vmdirq)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&vmdirq->node);
|
||||
vmdirq->irq = vmd_next_irq(vmd);
|
||||
vmdirq->irq = vmd_next_irq(vmd, desc);
|
||||
vmdirq->virq = virq;
|
||||
|
||||
irq_domain_set_info(domain, virq, vmdirq->irq->vmd_vector, info->chip,
|
||||
|
@ -203,11 +211,12 @@ static void vmd_msi_free(struct irq_domain *domain,
|
|||
struct msi_domain_info *info, unsigned int virq)
|
||||
{
|
||||
struct vmd_irq *vmdirq = irq_get_chip_data(virq);
|
||||
unsigned long flags;
|
||||
|
||||
/* XXX: Potential optimization to rebalance */
|
||||
raw_spin_lock(&list_lock);
|
||||
raw_spin_lock_irqsave(&list_lock, flags);
|
||||
vmdirq->irq->count--;
|
||||
raw_spin_unlock(&list_lock);
|
||||
raw_spin_unlock_irqrestore(&list_lock, flags);
|
||||
|
||||
kfree_rcu(vmdirq, rcu);
|
||||
}
|
||||
|
@ -261,7 +270,7 @@ static struct device *to_vmd_dev(struct device *dev)
|
|||
|
||||
static struct dma_map_ops *vmd_dma_ops(struct device *dev)
|
||||
{
|
||||
return to_vmd_dev(dev)->archdata.dma_ops;
|
||||
return get_dma_ops(to_vmd_dev(dev));
|
||||
}
|
||||
|
||||
static void *vmd_alloc(struct device *dev, size_t size, dma_addr_t *addr,
|
||||
|
@ -367,7 +376,7 @@ static void vmd_teardown_dma_ops(struct vmd_dev *vmd)
|
|||
{
|
||||
struct dma_domain *domain = &vmd->dma_domain;
|
||||
|
||||
if (vmd->dev->dev.archdata.dma_ops)
|
||||
if (get_dma_ops(&vmd->dev->dev))
|
||||
del_dma_domain(domain);
|
||||
}
|
||||
|
||||
|
@ -379,7 +388,7 @@ static void vmd_teardown_dma_ops(struct vmd_dev *vmd)
|
|||
|
||||
static void vmd_setup_dma_ops(struct vmd_dev *vmd)
|
||||
{
|
||||
const struct dma_map_ops *source = vmd->dev->dev.archdata.dma_ops;
|
||||
const struct dma_map_ops *source = get_dma_ops(&vmd->dev->dev);
|
||||
struct dma_map_ops *dest = &vmd->dma_ops;
|
||||
struct dma_domain *domain = &vmd->dma_domain;
|
||||
|
||||
|
@ -594,7 +603,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd)
|
|||
sd->node = pcibus_to_node(vmd->dev->bus);
|
||||
|
||||
vmd->irq_domain = pci_msi_create_irq_domain(NULL, &vmd_msi_domain_info,
|
||||
NULL);
|
||||
x86_vector_domain);
|
||||
if (!vmd->irq_domain)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
|
@ -17,6 +17,15 @@ config PCI_MVEBU
|
|||
depends on ARM
|
||||
depends on OF
|
||||
|
||||
config PCI_AARDVARK
|
||||
bool "Aardvark PCIe controller"
|
||||
depends on ARCH_MVEBU && ARM64
|
||||
depends on OF
|
||||
depends on PCI_MSI_IRQ_DOMAIN
|
||||
help
|
||||
Add support for Aardvark 64bit PCIe Host Controller. This
|
||||
controller is part of the South Bridge of the Marvel Armada
|
||||
3700 SoC.
|
||||
|
||||
config PCIE_XILINX_NWL
|
||||
bool "NWL PCIe Core"
|
||||
|
|
|
@ -5,6 +5,7 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
|
|||
obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
|
||||
obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o
|
||||
obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
|
||||
obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o
|
||||
obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
|
||||
obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
|
||||
obj-$(CONFIG_PCIE_RCAR) += pcie-rcar.o
|
||||
|
|
1001
drivers/pci/host/pci-aardvark.c
Normal file
1001
drivers/pci/host/pci-aardvark.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -181,14 +181,14 @@ static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp)
|
|||
|
||||
if (!pcie_intc_node) {
|
||||
dev_err(dev, "No PCIe Intc node found\n");
|
||||
return PTR_ERR(pcie_intc_node);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pp->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
|
||||
&intx_domain_ops, pp);
|
||||
if (!pp->irq_domain) {
|
||||
dev_err(dev, "Failed to get a INTx IRQ domain\n");
|
||||
return PTR_ERR(pp->irq_domain);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -732,16 +732,18 @@ static void hv_msi_free(struct irq_domain *domain, struct msi_domain_info *info,
|
|||
|
||||
pdev = msi_desc_to_pci_dev(msi);
|
||||
hbus = info->data;
|
||||
hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn));
|
||||
if (!hpdev)
|
||||
int_desc = irq_data_get_irq_chip_data(irq_data);
|
||||
if (!int_desc)
|
||||
return;
|
||||
|
||||
int_desc = irq_data_get_irq_chip_data(irq_data);
|
||||
if (int_desc) {
|
||||
irq_data->chip_data = NULL;
|
||||
hv_int_desc_free(hpdev, int_desc);
|
||||
irq_data->chip_data = NULL;
|
||||
hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn));
|
||||
if (!hpdev) {
|
||||
kfree(int_desc);
|
||||
return;
|
||||
}
|
||||
|
||||
hv_int_desc_free(hpdev, int_desc);
|
||||
put_pcichild(hpdev, hv_pcidev_ref_by_slot);
|
||||
}
|
||||
|
||||
|
@ -1657,14 +1659,16 @@ static void hv_pci_onchannelcallback(void *context)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Zero length indicates there are no more packets. */
|
||||
if (ret || !bytes_recvd)
|
||||
break;
|
||||
|
||||
/*
|
||||
* All incoming packets must be at least as large as a
|
||||
* response.
|
||||
*/
|
||||
if (bytes_recvd <= sizeof(struct pci_response)) {
|
||||
kfree(buffer);
|
||||
return;
|
||||
}
|
||||
if (bytes_recvd <= sizeof(struct pci_response))
|
||||
continue;
|
||||
desc = (struct vmpacket_descriptor *)buffer;
|
||||
|
||||
switch (desc->type) {
|
||||
|
@ -1679,8 +1683,7 @@ static void hv_pci_onchannelcallback(void *context)
|
|||
comp_packet->completion_func(comp_packet->compl_ctxt,
|
||||
response,
|
||||
bytes_recvd);
|
||||
kfree(buffer);
|
||||
return;
|
||||
break;
|
||||
|
||||
case VM_PKT_DATA_INBAND:
|
||||
|
||||
|
@ -1727,8 +1730,9 @@ static void hv_pci_onchannelcallback(void *context)
|
|||
desc->type, req_id, bytes_recvd);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
kfree(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -61,6 +61,8 @@
|
|||
#define TLP_LOOP 500
|
||||
#define RP_DEVFN 0
|
||||
|
||||
#define LINK_UP_TIMEOUT 5000
|
||||
|
||||
#define INTX_NUM 4
|
||||
|
||||
#define DWORD_MASK 3
|
||||
|
@ -81,9 +83,30 @@ struct tlp_rp_regpair_t {
|
|||
u32 reg1;
|
||||
};
|
||||
|
||||
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 bool altera_pcie_link_is_up(struct altera_pcie *pcie)
|
||||
{
|
||||
return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0);
|
||||
}
|
||||
|
||||
static void altera_pcie_retrain(struct pci_dev *dev)
|
||||
{
|
||||
u16 linkcap, linkstat;
|
||||
struct altera_pcie *pcie = dev->bus->sysdata;
|
||||
int timeout = 0;
|
||||
|
||||
if (!altera_pcie_link_is_up(pcie))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Set the retrain bit if the PCIe rootport support > 2.5GB/s, but
|
||||
|
@ -95,9 +118,16 @@ static void altera_pcie_retrain(struct pci_dev *dev)
|
|||
return;
|
||||
|
||||
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &linkstat);
|
||||
if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB)
|
||||
if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
|
||||
pcie_capability_set_word(dev, PCI_EXP_LNKCTL,
|
||||
PCI_EXP_LNKCTL_RL);
|
||||
while (!altera_pcie_link_is_up(pcie)) {
|
||||
timeout++;
|
||||
if (timeout > LINK_UP_TIMEOUT)
|
||||
break;
|
||||
udelay(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_EARLY(0x1172, PCI_ANY_ID, altera_pcie_retrain);
|
||||
|
||||
|
@ -120,17 +150,6 @@ static bool altera_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn,
|
|||
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)
|
||||
{
|
||||
|
@ -139,11 +158,6 @@ static void tlp_write_tx(struct altera_pcie *pcie,
|
|||
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) & RP_LTSSM_MASK) == LTSSM_L0);
|
||||
}
|
||||
|
||||
static bool altera_pcie_valid_config(struct altera_pcie *pcie,
|
||||
struct pci_bus *bus, int dev)
|
||||
{
|
||||
|
|
|
@ -550,7 +550,7 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
|
|||
pcie_intc_node = of_get_next_child(node, NULL);
|
||||
if (!pcie_intc_node) {
|
||||
dev_err(dev, "No PCIe Intc node found\n");
|
||||
return PTR_ERR(pcie_intc_node);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
port->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
|
||||
|
@ -558,7 +558,7 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
|
|||
port);
|
||||
if (!port->irq_domain) {
|
||||
dev_err(dev, "Failed to get a INTx IRQ domain\n");
|
||||
return PTR_ERR(port->irq_domain);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Setup MSI */
|
||||
|
@ -569,7 +569,7 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
|
|||
&xilinx_pcie_msi_chip);
|
||||
if (!port->irq_domain) {
|
||||
dev_err(dev, "Failed to get a MSI IRQ domain\n");
|
||||
return PTR_ERR(port->irq_domain);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
xilinx_pcie_enable_msi(port);
|
||||
|
|
Loading…
Reference in a new issue