1
0
Fork 0

pci:add support aer/pme interrupts with none MSI/MSI-X/INTx mode

On some platforms, root port doesn't support MSI/MSI-X/INTx in RC mode.
When chip support the aer/pme interrupts with none MSI/MSI-X/INTx mode,
maybe there is interrupt line for aer pme etc. Search the interrupt
number in the fdt file. Then fixup the dev->irq with it.

Signed-off-by: Po Liu <po.liu@nxp.com>
Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
5.4-rM2-2.2.x-imx-squashed
Po Liu 2016-09-30 17:11:37 +08:00 committed by Dong Aisheng
parent 219d54332a
commit 958dafe933
5 changed files with 127 additions and 4 deletions

View File

@ -26,8 +26,12 @@ Required properties:
- reg: base addresses and lengths of the PCIe controller register blocks. - reg: base addresses and lengths of the PCIe controller register blocks.
- interrupts: A list of interrupt outputs of the controller. Must contain an - interrupts: A list of interrupt outputs of the controller. Must contain an
entry for each entry in the interrupt-names property. entry for each entry in the interrupt-names property.
- interrupt-names: Must include the following entries: - interrupt-names: It could include the following entries:
"intr": The interrupt that is asserted for controller interrupts "aer": Asserted for aer interrupt when chip support the aer interrupt with
none MSI/MSI-X/INTx mode,but there is interrupt line for aer.
"pme": Asserted for pme interrupt when chip support the pme interrupt with
none MSI/MSI-X/INTx mode,but there is interrupt line for pme.
......
- fsl,pcie-scfg: Must include two entries. - fsl,pcie-scfg: Must include two entries.
The first entry must be a link to the SCFG device node The first entry must be a link to the SCFG device node
The second entry must be '0' or '1' based on physical PCIe controller index. The second entry must be '0' or '1' based on physical PCIe controller index.
@ -43,8 +47,9 @@ Example:
reg = <0x00 0x03400000 0x0 0x00010000 /* controller registers */ reg = <0x00 0x03400000 0x0 0x00010000 /* controller registers */
0x40 0x00000000 0x0 0x00002000>; /* configuration space */ 0x40 0x00000000 0x0 0x00002000>; /* configuration space */
reg-names = "regs", "config"; reg-names = "regs", "config";
interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>; /* controller interrupt */ interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>, /* aer interrupt */
interrupt-names = "intr"; <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>; /* pme interrupt */
interrupt-names = "aer", "pme";
fsl,pcie-scfg = <&scfg 0>; fsl,pcie-scfg = <&scfg 0>;
#address-cells = <3>; #address-cells = <3>;
#size-cells = <2>; #size-cells = <2>;

View File

@ -12,11 +12,14 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of_irq.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <asm/mach/pci.h> #include <asm/mach/pci.h>
#include "../../../drivers/pci/pcie/portdrv.h"
static int debug_pci; static int debug_pci;
/* /*
@ -64,6 +67,47 @@ void pcibios_report_status(u_int status_mask, int warn)
pcibios_bus_report_status(bus, status_mask, warn); pcibios_bus_report_status(bus, status_mask, warn);
} }
/*
* Check device tree if the service interrupts are there
*/
int pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{
int ret, count = 0;
struct device_node *np = NULL;
if (dev->bus->dev.of_node)
np = dev->bus->dev.of_node;
if (np == NULL)
return 0;
if (!IS_ENABLED(CONFIG_OF_IRQ))
return 0;
/* If root port doesn't support MSI/MSI-X/INTx in RC mode,
* request irq for aer
*/
if (mask & PCIE_PORT_SERVICE_AER) {
ret = of_irq_get_byname(np, "aer");
if (ret > 0) {
irqs[PCIE_PORT_SERVICE_AER_SHIFT] = ret;
count++;
}
}
if (mask & PCIE_PORT_SERVICE_PME) {
ret = of_irq_get_byname(np, "pme");
if (ret > 0) {
irqs[PCIE_PORT_SERVICE_PME_SHIFT] = ret;
count++;
}
}
/* TODO: add more service interrupts if there it is in the device tree*/
return count;
}
/* /*
* We don't use this to fix the device, but initialisation of it. * We don't use this to fix the device, but initialisation of it.
* It's not the correct use for this, but it works. * It's not the correct use for this, but it works.

View File

@ -13,11 +13,14 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/of_pci.h> #include <linux/of_pci.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of_irq.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pci-acpi.h> #include <linux/pci-acpi.h>
#include <linux/pci-ecam.h> #include <linux/pci-ecam.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "../../../drivers/pci/pcie/portdrv.h"
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
/* /*
* Try to assign the IRQ number when probing a new device * Try to assign the IRQ number when probing a new device
@ -31,6 +34,47 @@ int pcibios_alloc_irq(struct pci_dev *dev)
} }
#endif #endif
/*
* Check device tree if the service interrupts are there
*/
int pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{
int ret, count = 0;
struct device_node *np = NULL;
if (dev->bus->dev.of_node)
np = dev->bus->dev.of_node;
if (np == NULL)
return 0;
if (!IS_ENABLED(CONFIG_OF_IRQ))
return 0;
/* If root port doesn't support MSI/MSI-X/INTx in RC mode,
* request irq for aer
*/
if (mask & PCIE_PORT_SERVICE_AER) {
ret = of_irq_get_byname(np, "aer");
if (ret > 0) {
irqs[PCIE_PORT_SERVICE_AER_SHIFT] = ret;
count++;
}
}
if (mask & PCIE_PORT_SERVICE_PME) {
ret = of_irq_get_byname(np, "pme");
if (ret > 0) {
irqs[PCIE_PORT_SERVICE_PME_SHIFT] = ret;
count++;
}
}
/* TODO: add more service interrupts if there it is in the device tree*/
return count;
}
/* /*
* raw_pci_read/write - Platform-specific PCI config space access. * raw_pci_read/write - Platform-specific PCI config space access.
*/ */

View File

@ -37,6 +37,20 @@ static void release_pcie_device(struct device *dev)
kfree(to_pcie_device(dev)); kfree(to_pcie_device(dev));
} }
/**
* pcibios_check_service_irqs - check irqs in the device tree
* @dev: PCI Express port to handle
* @irqs: Array of irqs to populate
* @mask: Bitmask of port capabilities returned by get_port_device_capability()
*
* Return value: 0 means no service irqs in the device tree
*
*/
int __weak pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{
return 0;
}
/* /*
* Fill in *pme, *aer, *dpc with the relevant Interrupt Message Numbers if * Fill in *pme, *aer, *dpc with the relevant Interrupt Message Numbers if
* services are enabled in "mask". Return the number of MSI/MSI-X vectors * services are enabled in "mask". Return the number of MSI/MSI-X vectors
@ -165,10 +179,25 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask) static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{ {
int ret, i; int ret, i;
int irq = -1;
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
irqs[i] = -1; irqs[i] = -1;
/* Check if some platforms owns independent irq pins for AER/PME etc.
* Some platforms may own independent AER/PME interrupts and set
* them in the device tree file.
*/
ret = pcibios_check_service_irqs(dev, irqs, mask);
if (ret) {
if (dev->irq)
irq = dev->irq;
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
if (irqs[i] == -1)
irqs[i] = irq;
return 0;
}
/* /*
* If we support PME but can't use MSI/MSI-X for it, we have to * If we support PME but can't use MSI/MSI-X for it, we have to
* fall back to INTx or other interrupts, e.g., a system shared * fall back to INTx or other interrupts, e.g., a system shared

View File

@ -2021,6 +2021,7 @@ static inline void pcibios_penalize_isa_irq(int irq, int active) {}
int pcibios_alloc_irq(struct pci_dev *dev); int pcibios_alloc_irq(struct pci_dev *dev);
void pcibios_free_irq(struct pci_dev *dev); void pcibios_free_irq(struct pci_dev *dev);
resource_size_t pcibios_default_alignment(void); resource_size_t pcibios_default_alignment(void);
int pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask);
#ifdef CONFIG_HIBERNATE_CALLBACKS #ifdef CONFIG_HIBERNATE_CALLBACKS
extern struct dev_pm_ops pcibios_pm_ops; extern struct dev_pm_ops pcibios_pm_ops;