Merge branch 'remotes/lorenzo/pci/msi'

- Convert tegra to MSI domains (Marc Zyngier)

- Use rcar controller address as MSI doorbell instead of allocating a page
  (Marc Zyngier)

- Convert rcar to MSI domains (Marc Zyngier)

- Use xilinx port structure as MSI doorbell instead of allocating a page
  (Marc Zyngier)

- Convert xilinx to MSI domains (Marc Zyngier)

- Remove unused Hyper-V msi_controller structure (Marc Zyngier)

- Remove unused PCI core msi_controller support (Marc Zyngier)

- Remove struct msi_controller (Marc Zyngier)

- Remove unused default_teardown_msi_irqs() (Marc Zyngier)

- Let host bridges declare their reliance on MSI domains (Marc Zyngier)

- Make pci_host_common_probe() declare its reliance on MSI domains (Marc
  Zyngier)

- Advertise mediatek lack of built-in MSI handling (Thomas Gleixner)

- Document ways of ending up with NO_MSI (Marc Zyngier)

- Refactor HT advertising of NO_MSI flag (Marc Zyngier)

* remotes/lorenzo/pci/msi:
  PCI: Refactor HT advertising of NO_MSI flag
  PCI/MSI: Document the various ways of ending up with NO_MSI
  PCI: mediatek: Advertise lack of built-in MSI handling
  PCI/MSI: Make pci_host_common_probe() declare its reliance on MSI domains
  PCI/MSI: Let PCI host bridges declare their reliance on MSI domains
  PCI/MSI: Kill default_teardown_msi_irqs()
  PCI/MSI: Kill msi_controller structure
  PCI/MSI: Drop use of msi_controller from core code
  PCI: hv: Drop msi_controller structure
  PCI: xilinx: Convert to MSI domains
  PCI: xilinx: Don't allocate extra memory for the MSI capture address
  PCI: rcar: Convert to MSI domains
  PCI: rcar: Don't allocate extra memory for the MSI capture address
  PCI: tegra: Convert to MSI domains
This commit is contained in:
Bjorn Helgaas 2021-05-04 10:43:30 -05:00
commit 51bc2b7ffd
12 changed files with 516 additions and 585 deletions

View file

@ -41,7 +41,6 @@ config PCI_TEGRA
bool "NVIDIA Tegra PCIe controller" bool "NVIDIA Tegra PCIe controller"
depends on ARCH_TEGRA || COMPILE_TEST depends on ARCH_TEGRA || COMPILE_TEST
depends on PCI_MSI_IRQ_DOMAIN depends on PCI_MSI_IRQ_DOMAIN
select PCI_MSI_ARCH_FALLBACKS
help help
Say Y here if you want support for the PCIe host controller found Say Y here if you want support for the PCIe host controller found
on NVIDIA Tegra SoCs. on NVIDIA Tegra SoCs.
@ -59,7 +58,6 @@ config PCIE_RCAR_HOST
bool "Renesas R-Car PCIe host controller" bool "Renesas R-Car PCIe host controller"
depends on ARCH_RENESAS || COMPILE_TEST depends on ARCH_RENESAS || COMPILE_TEST
depends on PCI_MSI_IRQ_DOMAIN depends on PCI_MSI_IRQ_DOMAIN
select PCI_MSI_ARCH_FALLBACKS
help help
Say Y here if you want PCIe controller support on R-Car SoCs in host Say Y here if you want PCIe controller support on R-Car SoCs in host
mode. mode.
@ -88,7 +86,7 @@ config PCI_HOST_GENERIC
config PCIE_XILINX config PCIE_XILINX
bool "Xilinx AXI PCIe host bridge support" bool "Xilinx AXI PCIe host bridge support"
depends on OF || COMPILE_TEST depends on OF || COMPILE_TEST
select PCI_MSI_ARCH_FALLBACKS depends on PCI_MSI_IRQ_DOMAIN
help help
Say 'Y' here if you want kernel to support the Xilinx AXI PCIe Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
Host Bridge driver. Host Bridge driver.

View file

@ -79,6 +79,7 @@ int pci_host_common_probe(struct platform_device *pdev)
bridge->sysdata = cfg; bridge->sysdata = cfg;
bridge->ops = (struct pci_ops *)&ops->pci_ops; bridge->ops = (struct pci_ops *)&ops->pci_ops;
bridge->msi_domain = true;
return pci_host_probe(bridge); return pci_host_probe(bridge);
} }

View file

@ -473,7 +473,6 @@ struct hv_pcibus_device {
struct list_head dr_list; struct list_head dr_list;
struct msi_domain_info msi_info; struct msi_domain_info msi_info;
struct msi_controller msi_chip;
struct irq_domain *irq_domain; struct irq_domain *irq_domain;
spinlock_t retarget_msi_interrupt_lock; spinlock_t retarget_msi_interrupt_lock;
@ -1866,9 +1865,6 @@ static int create_root_hv_pci_bus(struct hv_pcibus_device *hbus)
if (!hbus->pci_bus) if (!hbus->pci_bus)
return -ENODEV; return -ENODEV;
hbus->pci_bus->msi = &hbus->msi_chip;
hbus->pci_bus->msi->dev = &hbus->hdev->device;
pci_lock_rescan_remove(); pci_lock_rescan_remove();
pci_scan_child_bus(hbus->pci_bus); pci_scan_child_bus(hbus->pci_bus);
hv_pci_assign_numa_node(hbus); hv_pci_assign_numa_node(hbus);

View file

@ -21,6 +21,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
@ -78,23 +79,8 @@
#define AFI_MSI_FPCI_BAR_ST 0x64 #define AFI_MSI_FPCI_BAR_ST 0x64
#define AFI_MSI_AXI_BAR_ST 0x68 #define AFI_MSI_AXI_BAR_ST 0x68
#define AFI_MSI_VEC0 0x6c #define AFI_MSI_VEC(x) (0x6c + ((x) * 4))
#define AFI_MSI_VEC1 0x70 #define AFI_MSI_EN_VEC(x) (0x8c + ((x) * 4))
#define AFI_MSI_VEC2 0x74
#define AFI_MSI_VEC3 0x78
#define AFI_MSI_VEC4 0x7c
#define AFI_MSI_VEC5 0x80
#define AFI_MSI_VEC6 0x84
#define AFI_MSI_VEC7 0x88
#define AFI_MSI_EN_VEC0 0x8c
#define AFI_MSI_EN_VEC1 0x90
#define AFI_MSI_EN_VEC2 0x94
#define AFI_MSI_EN_VEC3 0x98
#define AFI_MSI_EN_VEC4 0x9c
#define AFI_MSI_EN_VEC5 0xa0
#define AFI_MSI_EN_VEC6 0xa4
#define AFI_MSI_EN_VEC7 0xa8
#define AFI_CONFIGURATION 0xac #define AFI_CONFIGURATION 0xac
#define AFI_CONFIGURATION_EN_FPCI (1 << 0) #define AFI_CONFIGURATION_EN_FPCI (1 << 0)
@ -280,10 +266,10 @@
#define LINK_RETRAIN_TIMEOUT 100000 /* in usec */ #define LINK_RETRAIN_TIMEOUT 100000 /* in usec */
struct tegra_msi { struct tegra_msi {
struct msi_controller chip;
DECLARE_BITMAP(used, INT_PCI_MSI_NR); DECLARE_BITMAP(used, INT_PCI_MSI_NR);
struct irq_domain *domain; struct irq_domain *domain;
struct mutex lock; struct mutex map_lock;
spinlock_t mask_lock;
void *virt; void *virt;
dma_addr_t phys; dma_addr_t phys;
int irq; int irq;
@ -333,11 +319,6 @@ struct tegra_pcie_soc {
} ectl; } ectl;
}; };
static inline struct tegra_msi *to_tegra_msi(struct msi_controller *chip)
{
return container_of(chip, struct tegra_msi, chip);
}
struct tegra_pcie { struct tegra_pcie {
struct device *dev; struct device *dev;
@ -372,6 +353,11 @@ struct tegra_pcie {
struct dentry *debugfs; struct dentry *debugfs;
}; };
static inline struct tegra_pcie *msi_to_pcie(struct tegra_msi *msi)
{
return container_of(msi, struct tegra_pcie, msi);
}
struct tegra_pcie_port { struct tegra_pcie_port {
struct tegra_pcie *pcie; struct tegra_pcie *pcie;
struct device_node *np; struct device_node *np;
@ -1432,7 +1418,6 @@ static void tegra_pcie_phys_put(struct tegra_pcie *pcie)
} }
} }
static int tegra_pcie_get_resources(struct tegra_pcie *pcie) static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
{ {
struct device *dev = pcie->dev; struct device *dev = pcie->dev;
@ -1509,6 +1494,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
phys_put: phys_put:
if (soc->program_uphy) if (soc->program_uphy)
tegra_pcie_phys_put(pcie); tegra_pcie_phys_put(pcie);
return err; return err;
} }
@ -1551,161 +1537,227 @@ static void tegra_pcie_pme_turnoff(struct tegra_pcie_port *port)
afi_writel(pcie, val, AFI_PCIE_PME); afi_writel(pcie, val, AFI_PCIE_PME);
} }
static int tegra_msi_alloc(struct tegra_msi *chip) static void tegra_pcie_msi_irq(struct irq_desc *desc)
{ {
int msi; struct tegra_pcie *pcie = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
mutex_lock(&chip->lock);
msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR);
if (msi < INT_PCI_MSI_NR)
set_bit(msi, chip->used);
else
msi = -ENOSPC;
mutex_unlock(&chip->lock);
return msi;
}
static void tegra_msi_free(struct tegra_msi *chip, unsigned long irq)
{
struct device *dev = chip->chip.dev;
mutex_lock(&chip->lock);
if (!test_bit(irq, chip->used))
dev_err(dev, "trying to free unused MSI#%lu\n", irq);
else
clear_bit(irq, chip->used);
mutex_unlock(&chip->lock);
}
static irqreturn_t tegra_pcie_msi_irq(int irq, void *data)
{
struct tegra_pcie *pcie = data;
struct device *dev = pcie->dev;
struct tegra_msi *msi = &pcie->msi; struct tegra_msi *msi = &pcie->msi;
unsigned int i, processed = 0; struct device *dev = pcie->dev;
unsigned int i;
chained_irq_enter(chip, desc);
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
unsigned long reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4); unsigned long reg = afi_readl(pcie, AFI_MSI_VEC(i));
while (reg) { while (reg) {
unsigned int offset = find_first_bit(&reg, 32); unsigned int offset = find_first_bit(&reg, 32);
unsigned int index = i * 32 + offset; unsigned int index = i * 32 + offset;
unsigned int irq; unsigned int irq;
/* clear the interrupt */ irq = irq_find_mapping(msi->domain->parent, index);
afi_writel(pcie, 1 << offset, AFI_MSI_VEC0 + i * 4);
irq = irq_find_mapping(msi->domain, index);
if (irq) { if (irq) {
if (test_bit(index, msi->used))
generic_handle_irq(irq); generic_handle_irq(irq);
else
dev_info(dev, "unhandled MSI\n");
} else { } else {
/* /*
* that's weird who triggered this? * that's weird who triggered this?
* just clear it * just clear it
*/ */
dev_info(dev, "unexpected MSI\n"); dev_info(dev, "unexpected MSI\n");
afi_writel(pcie, BIT(index % 32), AFI_MSI_VEC(index));
} }
/* see if there's any more pending in this vector */ /* see if there's any more pending in this vector */
reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4); reg = afi_readl(pcie, AFI_MSI_VEC(i));
processed++;
} }
} }
return processed > 0 ? IRQ_HANDLED : IRQ_NONE; chained_irq_exit(chip, desc);
} }
static int tegra_msi_setup_irq(struct msi_controller *chip, static void tegra_msi_top_irq_ack(struct irq_data *d)
struct pci_dev *pdev, struct msi_desc *desc)
{ {
struct tegra_msi *msi = to_tegra_msi(chip); irq_chip_ack_parent(d);
struct msi_msg msg;
unsigned int irq;
int hwirq;
hwirq = tegra_msi_alloc(msi);
if (hwirq < 0)
return hwirq;
irq = irq_create_mapping(msi->domain, hwirq);
if (!irq) {
tegra_msi_free(msi, hwirq);
return -EINVAL;
}
irq_set_msi_desc(irq, desc);
msg.address_lo = lower_32_bits(msi->phys);
msg.address_hi = upper_32_bits(msi->phys);
msg.data = hwirq;
pci_write_msi_msg(irq, &msg);
return 0;
} }
static void tegra_msi_teardown_irq(struct msi_controller *chip, static void tegra_msi_top_irq_mask(struct irq_data *d)
unsigned int irq)
{ {
struct tegra_msi *msi = to_tegra_msi(chip); pci_msi_mask_irq(d);
struct irq_data *d = irq_get_irq_data(irq); irq_chip_mask_parent(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
irq_dispose_mapping(irq);
tegra_msi_free(msi, hwirq);
} }
static struct irq_chip tegra_msi_irq_chip = { static void tegra_msi_top_irq_unmask(struct irq_data *d)
{
pci_msi_unmask_irq(d);
irq_chip_unmask_parent(d);
}
static struct irq_chip tegra_msi_top_chip = {
.name = "Tegra PCIe MSI", .name = "Tegra PCIe MSI",
.irq_enable = pci_msi_unmask_irq, .irq_ack = tegra_msi_top_irq_ack,
.irq_disable = pci_msi_mask_irq, .irq_mask = tegra_msi_top_irq_mask,
.irq_mask = pci_msi_mask_irq, .irq_unmask = tegra_msi_top_irq_unmask,
.irq_unmask = pci_msi_unmask_irq,
}; };
static int tegra_msi_map(struct irq_domain *domain, unsigned int irq, static void tegra_msi_irq_ack(struct irq_data *d)
irq_hw_number_t hwirq)
{ {
irq_set_chip_and_handler(irq, &tegra_msi_irq_chip, handle_simple_irq); struct tegra_msi *msi = irq_data_get_irq_chip_data(d);
irq_set_chip_data(irq, domain->host_data); struct tegra_pcie *pcie = msi_to_pcie(msi);
unsigned int index = d->hwirq / 32;
/* clear the interrupt */
afi_writel(pcie, BIT(d->hwirq % 32), AFI_MSI_VEC(index));
}
static void tegra_msi_irq_mask(struct irq_data *d)
{
struct tegra_msi *msi = irq_data_get_irq_chip_data(d);
struct tegra_pcie *pcie = msi_to_pcie(msi);
unsigned int index = d->hwirq / 32;
unsigned long flags;
u32 value;
spin_lock_irqsave(&msi->mask_lock, flags);
value = afi_readl(pcie, AFI_MSI_EN_VEC(index));
value &= ~BIT(d->hwirq % 32);
afi_writel(pcie, value, AFI_MSI_EN_VEC(index));
spin_unlock_irqrestore(&msi->mask_lock, flags);
}
static void tegra_msi_irq_unmask(struct irq_data *d)
{
struct tegra_msi *msi = irq_data_get_irq_chip_data(d);
struct tegra_pcie *pcie = msi_to_pcie(msi);
unsigned int index = d->hwirq / 32;
unsigned long flags;
u32 value;
spin_lock_irqsave(&msi->mask_lock, flags);
value = afi_readl(pcie, AFI_MSI_EN_VEC(index));
value |= BIT(d->hwirq % 32);
afi_writel(pcie, value, AFI_MSI_EN_VEC(index));
spin_unlock_irqrestore(&msi->mask_lock, flags);
}
static int tegra_msi_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force)
{
return -EINVAL;
}
static void tegra_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
struct tegra_msi *msi = irq_data_get_irq_chip_data(data);
msg->address_lo = lower_32_bits(msi->phys);
msg->address_hi = upper_32_bits(msi->phys);
msg->data = data->hwirq;
}
static struct irq_chip tegra_msi_bottom_chip = {
.name = "Tegra MSI",
.irq_ack = tegra_msi_irq_ack,
.irq_mask = tegra_msi_irq_mask,
.irq_unmask = tegra_msi_irq_unmask,
.irq_set_affinity = tegra_msi_set_affinity,
.irq_compose_msi_msg = tegra_compose_msi_msg,
};
static int tegra_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *args)
{
struct tegra_msi *msi = domain->host_data;
unsigned int i;
int hwirq;
mutex_lock(&msi->map_lock);
hwirq = bitmap_find_free_region(msi->used, INT_PCI_MSI_NR, order_base_2(nr_irqs));
mutex_unlock(&msi->map_lock);
if (hwirq < 0)
return -ENOSPC;
for (i = 0; i < nr_irqs; i++)
irq_domain_set_info(domain, virq + i, hwirq + i,
&tegra_msi_bottom_chip, domain->host_data,
handle_edge_irq, NULL, NULL);
tegra_cpuidle_pcie_irqs_in_use(); tegra_cpuidle_pcie_irqs_in_use();
return 0; return 0;
} }
static const struct irq_domain_ops msi_domain_ops = { static void tegra_msi_domain_free(struct irq_domain *domain, unsigned int virq,
.map = tegra_msi_map, unsigned int nr_irqs)
{
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
struct tegra_msi *msi = domain->host_data;
mutex_lock(&msi->map_lock);
bitmap_release_region(msi->used, d->hwirq, order_base_2(nr_irqs));
mutex_unlock(&msi->map_lock);
}
static const struct irq_domain_ops tegra_msi_domain_ops = {
.alloc = tegra_msi_domain_alloc,
.free = tegra_msi_domain_free,
}; };
static struct msi_domain_info tegra_msi_info = {
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
MSI_FLAG_PCI_MSIX),
.chip = &tegra_msi_top_chip,
};
static int tegra_allocate_domains(struct tegra_msi *msi)
{
struct tegra_pcie *pcie = msi_to_pcie(msi);
struct fwnode_handle *fwnode = dev_fwnode(pcie->dev);
struct irq_domain *parent;
parent = irq_domain_create_linear(fwnode, INT_PCI_MSI_NR,
&tegra_msi_domain_ops, msi);
if (!parent) {
dev_err(pcie->dev, "failed to create IRQ domain\n");
return -ENOMEM;
}
irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS);
msi->domain = pci_msi_create_irq_domain(fwnode, &tegra_msi_info, parent);
if (!msi->domain) {
dev_err(pcie->dev, "failed to create MSI domain\n");
irq_domain_remove(parent);
return -ENOMEM;
}
return 0;
}
static void tegra_free_domains(struct tegra_msi *msi)
{
struct irq_domain *parent = msi->domain->parent;
irq_domain_remove(msi->domain);
irq_domain_remove(parent);
}
static int tegra_pcie_msi_setup(struct tegra_pcie *pcie) static int tegra_pcie_msi_setup(struct tegra_pcie *pcie)
{ {
struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
struct platform_device *pdev = to_platform_device(pcie->dev); struct platform_device *pdev = to_platform_device(pcie->dev);
struct tegra_msi *msi = &pcie->msi; struct tegra_msi *msi = &pcie->msi;
struct device *dev = pcie->dev; struct device *dev = pcie->dev;
int err; int err;
mutex_init(&msi->lock); mutex_init(&msi->map_lock);
spin_lock_init(&msi->mask_lock);
msi->chip.dev = dev; if (IS_ENABLED(CONFIG_PCI_MSI)) {
msi->chip.setup_irq = tegra_msi_setup_irq; err = tegra_allocate_domains(msi);
msi->chip.teardown_irq = tegra_msi_teardown_irq; if (err)
return err;
msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR,
&msi_domain_ops, &msi->chip);
if (!msi->domain) {
dev_err(dev, "failed to create IRQ domain\n");
return -ENOMEM;
} }
err = platform_get_irq_byname(pdev, "msi"); err = platform_get_irq_byname(pdev, "msi");
@ -1714,12 +1766,7 @@ static int tegra_pcie_msi_setup(struct tegra_pcie *pcie)
msi->irq = err; msi->irq = err;
err = request_irq(msi->irq, tegra_pcie_msi_irq, IRQF_NO_THREAD, irq_set_chained_handler_and_data(msi->irq, tegra_pcie_msi_irq, pcie);
tegra_msi_irq_chip.name, pcie);
if (err < 0) {
dev_err(dev, "failed to request IRQ: %d\n", err);
goto free_irq_domain;
}
/* Though the PCIe controller can address >32-bit address space, to /* Though the PCIe controller can address >32-bit address space, to
* facilitate endpoints that support only 32-bit MSI target address, * facilitate endpoints that support only 32-bit MSI target address,
@ -1740,14 +1787,14 @@ static int tegra_pcie_msi_setup(struct tegra_pcie *pcie)
goto free_irq; goto free_irq;
} }
host->msi = &msi->chip;
return 0; return 0;
free_irq: free_irq:
free_irq(msi->irq, pcie); irq_set_chained_handler_and_data(msi->irq, NULL, NULL);
free_irq_domain: free_irq_domain:
irq_domain_remove(msi->domain); if (IS_ENABLED(CONFIG_PCI_MSI))
tegra_free_domains(msi);
return err; return err;
} }
@ -1755,22 +1802,18 @@ static void tegra_pcie_enable_msi(struct tegra_pcie *pcie)
{ {
const struct tegra_pcie_soc *soc = pcie->soc; const struct tegra_pcie_soc *soc = pcie->soc;
struct tegra_msi *msi = &pcie->msi; struct tegra_msi *msi = &pcie->msi;
u32 reg; u32 reg, msi_state[INT_PCI_MSI_NR / 32];
int i;
afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST); afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST);
afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST); afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST);
/* this register is in 4K increments */ /* this register is in 4K increments */
afi_writel(pcie, 1, AFI_MSI_BAR_SZ); afi_writel(pcie, 1, AFI_MSI_BAR_SZ);
/* enable all MSI vectors */ /* Restore the MSI allocation state */
afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC0); bitmap_to_arr32(msi_state, msi->used, INT_PCI_MSI_NR);
afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC1); for (i = 0; i < ARRAY_SIZE(msi_state); i++)
afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC2); afi_writel(pcie, msi_state[i], AFI_MSI_EN_VEC(i));
afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC3);
afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC4);
afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC5);
afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC6);
afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC7);
/* and unmask the MSI interrupt */ /* and unmask the MSI interrupt */
reg = afi_readl(pcie, AFI_INTR_MASK); reg = afi_readl(pcie, AFI_INTR_MASK);
@ -1786,16 +1829,16 @@ static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie)
dma_free_attrs(pcie->dev, PAGE_SIZE, msi->virt, msi->phys, dma_free_attrs(pcie->dev, PAGE_SIZE, msi->virt, msi->phys,
DMA_ATTR_NO_KERNEL_MAPPING); DMA_ATTR_NO_KERNEL_MAPPING);
if (msi->irq > 0)
free_irq(msi->irq, pcie);
for (i = 0; i < INT_PCI_MSI_NR; i++) { for (i = 0; i < INT_PCI_MSI_NR; i++) {
irq = irq_find_mapping(msi->domain, i); irq = irq_find_mapping(msi->domain, i);
if (irq > 0) if (irq > 0)
irq_dispose_mapping(irq); irq_domain_free_irqs(irq, 1);
} }
irq_domain_remove(msi->domain); irq_set_chained_handler_and_data(msi->irq, NULL, NULL);
if (IS_ENABLED(CONFIG_PCI_MSI))
tegra_free_domains(msi);
} }
static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) static int tegra_pcie_disable_msi(struct tegra_pcie *pcie)
@ -1807,16 +1850,6 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie)
value &= ~AFI_INTR_MASK_MSI_MASK; value &= ~AFI_INTR_MASK_MSI_MASK;
afi_writel(pcie, value, AFI_INTR_MASK); afi_writel(pcie, value, AFI_INTR_MASK);
/* disable all MSI vectors */
afi_writel(pcie, 0, AFI_MSI_EN_VEC0);
afi_writel(pcie, 0, AFI_MSI_EN_VEC1);
afi_writel(pcie, 0, AFI_MSI_EN_VEC2);
afi_writel(pcie, 0, AFI_MSI_EN_VEC3);
afi_writel(pcie, 0, AFI_MSI_EN_VEC4);
afi_writel(pcie, 0, AFI_MSI_EN_VEC5);
afi_writel(pcie, 0, AFI_MSI_EN_VEC6);
afi_writel(pcie, 0, AFI_MSI_EN_VEC7);
return 0; return 0;
} }

View file

@ -143,6 +143,7 @@ struct mtk_pcie_port;
* struct mtk_pcie_soc - differentiate between host generations * struct mtk_pcie_soc - differentiate between host generations
* @need_fix_class_id: whether this host's class ID needed to be fixed or not * @need_fix_class_id: whether this host's class ID needed to be fixed or not
* @need_fix_device_id: whether this host's device ID needed to be fixed or not * @need_fix_device_id: whether this host's device ID needed to be fixed or not
* @no_msi: Bridge has no MSI support, and relies on an external block
* @device_id: device ID which this host need to be fixed * @device_id: device ID which this host need to be fixed
* @ops: pointer to configuration access functions * @ops: pointer to configuration access functions
* @startup: pointer to controller setting functions * @startup: pointer to controller setting functions
@ -151,6 +152,7 @@ struct mtk_pcie_port;
struct mtk_pcie_soc { struct mtk_pcie_soc {
bool need_fix_class_id; bool need_fix_class_id;
bool need_fix_device_id; bool need_fix_device_id;
bool no_msi;
unsigned int device_id; unsigned int device_id;
struct pci_ops *ops; struct pci_ops *ops;
int (*startup)(struct mtk_pcie_port *port); int (*startup)(struct mtk_pcie_port *port);
@ -1087,6 +1089,7 @@ static int mtk_pcie_probe(struct platform_device *pdev)
host->ops = pcie->soc->ops; host->ops = pcie->soc->ops;
host->sysdata = pcie; host->sysdata = pcie;
host->msi_domain = pcie->soc->no_msi;
err = pci_host_probe(host); err = pci_host_probe(host);
if (err) if (err)
@ -1176,6 +1179,7 @@ static const struct dev_pm_ops mtk_pcie_pm_ops = {
}; };
static const struct mtk_pcie_soc mtk_pcie_soc_v1 = { static const struct mtk_pcie_soc mtk_pcie_soc_v1 = {
.no_msi = true,
.ops = &mtk_pcie_ops, .ops = &mtk_pcie_ops,
.startup = mtk_pcie_startup_port, .startup = mtk_pcie_startup_port,
}; };

View file

@ -35,18 +35,12 @@
struct rcar_msi { struct rcar_msi {
DECLARE_BITMAP(used, INT_PCI_MSI_NR); DECLARE_BITMAP(used, INT_PCI_MSI_NR);
struct irq_domain *domain; struct irq_domain *domain;
struct msi_controller chip; struct mutex map_lock;
unsigned long pages; spinlock_t mask_lock;
struct mutex lock;
int irq1; int irq1;
int irq2; int irq2;
}; };
static inline struct rcar_msi *to_rcar_msi(struct msi_controller *chip)
{
return container_of(chip, struct rcar_msi, chip);
}
/* Structure representing the PCIe interface */ /* Structure representing the PCIe interface */
struct rcar_pcie_host { struct rcar_pcie_host {
struct rcar_pcie pcie; struct rcar_pcie pcie;
@ -56,6 +50,11 @@ struct rcar_pcie_host {
int (*phy_init_fn)(struct rcar_pcie_host *host); int (*phy_init_fn)(struct rcar_pcie_host *host);
}; };
static struct rcar_pcie_host *msi_to_host(struct rcar_msi *msi)
{
return container_of(msi, struct rcar_pcie_host, msi);
}
static u32 rcar_read_conf(struct rcar_pcie *pcie, int where) static u32 rcar_read_conf(struct rcar_pcie *pcie, int where)
{ {
unsigned int shift = BITS_PER_BYTE * (where & 3); unsigned int shift = BITS_PER_BYTE * (where & 3);
@ -292,8 +291,6 @@ static int rcar_pcie_enable(struct rcar_pcie_host *host)
bridge->sysdata = host; bridge->sysdata = host;
bridge->ops = &rcar_pcie_ops; bridge->ops = &rcar_pcie_ops;
if (IS_ENABLED(CONFIG_PCI_MSI))
bridge->msi = &host->msi.chip;
return pci_host_probe(bridge); return pci_host_probe(bridge);
} }
@ -473,42 +470,6 @@ static int rcar_pcie_phy_init_gen3(struct rcar_pcie_host *host)
return err; return err;
} }
static int rcar_msi_alloc(struct rcar_msi *chip)
{
int msi;
mutex_lock(&chip->lock);
msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR);
if (msi < INT_PCI_MSI_NR)
set_bit(msi, chip->used);
else
msi = -ENOSPC;
mutex_unlock(&chip->lock);
return msi;
}
static int rcar_msi_alloc_region(struct rcar_msi *chip, int no_irqs)
{
int msi;
mutex_lock(&chip->lock);
msi = bitmap_find_free_region(chip->used, INT_PCI_MSI_NR,
order_base_2(no_irqs));
mutex_unlock(&chip->lock);
return msi;
}
static void rcar_msi_free(struct rcar_msi *chip, unsigned long irq)
{
mutex_lock(&chip->lock);
clear_bit(irq, chip->used);
mutex_unlock(&chip->lock);
}
static irqreturn_t rcar_pcie_msi_irq(int irq, void *data) static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)
{ {
struct rcar_pcie_host *host = data; struct rcar_pcie_host *host = data;
@ -527,18 +488,13 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)
unsigned int index = find_first_bit(&reg, 32); unsigned int index = find_first_bit(&reg, 32);
unsigned int msi_irq; unsigned int msi_irq;
/* clear the interrupt */ msi_irq = irq_find_mapping(msi->domain->parent, index);
rcar_pci_write_reg(pcie, 1 << index, PCIEMSIFR);
msi_irq = irq_find_mapping(msi->domain, index);
if (msi_irq) { if (msi_irq) {
if (test_bit(index, msi->used))
generic_handle_irq(msi_irq); generic_handle_irq(msi_irq);
else
dev_info(dev, "unhandled MSI\n");
} else { } else {
/* Unknown MSI, just clear it */ /* Unknown MSI, just clear it */
dev_dbg(dev, "unexpected MSI\n"); dev_dbg(dev, "unexpected MSI\n");
rcar_pci_write_reg(pcie, BIT(index), PCIEMSIFR);
} }
/* see if there's any more pending in this vector */ /* see if there's any more pending in this vector */
@ -548,149 +504,169 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int rcar_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, static void rcar_msi_top_irq_ack(struct irq_data *d)
struct msi_desc *desc)
{ {
struct rcar_msi *msi = to_rcar_msi(chip); irq_chip_ack_parent(d);
struct rcar_pcie_host *host = container_of(chip, struct rcar_pcie_host,
msi.chip);
struct rcar_pcie *pcie = &host->pcie;
struct msi_msg msg;
unsigned int irq;
int hwirq;
hwirq = rcar_msi_alloc(msi);
if (hwirq < 0)
return hwirq;
irq = irq_find_mapping(msi->domain, hwirq);
if (!irq) {
rcar_msi_free(msi, hwirq);
return -EINVAL;
}
irq_set_msi_desc(irq, desc);
msg.address_lo = rcar_pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE;
msg.address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR);
msg.data = hwirq;
pci_write_msi_msg(irq, &msg);
return 0;
} }
static int rcar_msi_setup_irqs(struct msi_controller *chip, static void rcar_msi_top_irq_mask(struct irq_data *d)
struct pci_dev *pdev, int nvec, int type)
{ {
struct rcar_msi *msi = to_rcar_msi(chip); pci_msi_mask_irq(d);
struct rcar_pcie_host *host = container_of(chip, struct rcar_pcie_host, irq_chip_mask_parent(d);
msi.chip); }
struct rcar_pcie *pcie = &host->pcie;
struct msi_desc *desc;
struct msi_msg msg;
unsigned int irq;
int hwirq;
int i;
/* MSI-X interrupts are not supported */ static void rcar_msi_top_irq_unmask(struct irq_data *d)
if (type == PCI_CAP_ID_MSIX) {
pci_msi_unmask_irq(d);
irq_chip_unmask_parent(d);
}
static struct irq_chip rcar_msi_top_chip = {
.name = "PCIe MSI",
.irq_ack = rcar_msi_top_irq_ack,
.irq_mask = rcar_msi_top_irq_mask,
.irq_unmask = rcar_msi_top_irq_unmask,
};
static void rcar_msi_irq_ack(struct irq_data *d)
{
struct rcar_msi *msi = irq_data_get_irq_chip_data(d);
struct rcar_pcie *pcie = &msi_to_host(msi)->pcie;
/* clear the interrupt */
rcar_pci_write_reg(pcie, BIT(d->hwirq), PCIEMSIFR);
}
static void rcar_msi_irq_mask(struct irq_data *d)
{
struct rcar_msi *msi = irq_data_get_irq_chip_data(d);
struct rcar_pcie *pcie = &msi_to_host(msi)->pcie;
unsigned long flags;
u32 value;
spin_lock_irqsave(&msi->mask_lock, flags);
value = rcar_pci_read_reg(pcie, PCIEMSIIER);
value &= ~BIT(d->hwirq);
rcar_pci_write_reg(pcie, value, PCIEMSIIER);
spin_unlock_irqrestore(&msi->mask_lock, flags);
}
static void rcar_msi_irq_unmask(struct irq_data *d)
{
struct rcar_msi *msi = irq_data_get_irq_chip_data(d);
struct rcar_pcie *pcie = &msi_to_host(msi)->pcie;
unsigned long flags;
u32 value;
spin_lock_irqsave(&msi->mask_lock, flags);
value = rcar_pci_read_reg(pcie, PCIEMSIIER);
value |= BIT(d->hwirq);
rcar_pci_write_reg(pcie, value, PCIEMSIIER);
spin_unlock_irqrestore(&msi->mask_lock, flags);
}
static int rcar_msi_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force)
{
return -EINVAL; return -EINVAL;
}
WARN_ON(!list_is_singular(&pdev->dev.msi_list)); static void rcar_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list); {
struct rcar_msi *msi = irq_data_get_irq_chip_data(data);
struct rcar_pcie *pcie = &msi_to_host(msi)->pcie;
msg->address_lo = rcar_pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE;
msg->address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR);
msg->data = data->hwirq;
}
static struct irq_chip rcar_msi_bottom_chip = {
.name = "Rcar MSI",
.irq_ack = rcar_msi_irq_ack,
.irq_mask = rcar_msi_irq_mask,
.irq_unmask = rcar_msi_irq_unmask,
.irq_set_affinity = rcar_msi_set_affinity,
.irq_compose_msi_msg = rcar_compose_msi_msg,
};
static int rcar_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *args)
{
struct rcar_msi *msi = domain->host_data;
unsigned int i;
int hwirq;
mutex_lock(&msi->map_lock);
hwirq = bitmap_find_free_region(msi->used, INT_PCI_MSI_NR, order_base_2(nr_irqs));
mutex_unlock(&msi->map_lock);
hwirq = rcar_msi_alloc_region(msi, nvec);
if (hwirq < 0) if (hwirq < 0)
return -ENOSPC; return -ENOSPC;
irq = irq_find_mapping(msi->domain, hwirq); for (i = 0; i < nr_irqs; i++)
if (!irq) irq_domain_set_info(domain, virq + i, hwirq + i,
return -ENOSPC; &rcar_msi_bottom_chip, domain->host_data,
handle_edge_irq, NULL, NULL);
for (i = 0; i < nvec; i++) {
/*
* irq_create_mapping() called from rcar_pcie_probe() pre-
* allocates descs, so there is no need to allocate descs here.
* We can therefore assume that if irq_find_mapping() above
* returns non-zero, then the descs are also successfully
* allocated.
*/
if (irq_set_msi_desc_off(irq, i, desc)) {
/* TODO: clear */
return -EINVAL;
}
}
desc->nvec_used = nvec;
desc->msi_attrib.multiple = order_base_2(nvec);
msg.address_lo = rcar_pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE;
msg.address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR);
msg.data = hwirq;
pci_write_msi_msg(irq, &msg);
return 0; return 0;
} }
static void rcar_msi_teardown_irq(struct msi_controller *chip, unsigned int irq) static void rcar_msi_domain_free(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs)
{ {
struct rcar_msi *msi = to_rcar_msi(chip); struct irq_data *d = irq_domain_get_irq_data(domain, virq);
struct irq_data *d = irq_get_irq_data(irq); struct rcar_msi *msi = domain->host_data;
rcar_msi_free(msi, d->hwirq); mutex_lock(&msi->map_lock);
bitmap_release_region(msi->used, d->hwirq, order_base_2(nr_irqs));
mutex_unlock(&msi->map_lock);
} }
static struct irq_chip rcar_msi_irq_chip = { static const struct irq_domain_ops rcar_msi_domain_ops = {
.name = "R-Car PCIe MSI", .alloc = rcar_msi_domain_alloc,
.irq_enable = pci_msi_unmask_irq, .free = rcar_msi_domain_free,
.irq_disable = pci_msi_mask_irq,
.irq_mask = pci_msi_mask_irq,
.irq_unmask = pci_msi_unmask_irq,
}; };
static int rcar_msi_map(struct irq_domain *domain, unsigned int irq, static struct msi_domain_info rcar_msi_info = {
irq_hw_number_t hwirq) .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
MSI_FLAG_MULTI_PCI_MSI),
.chip = &rcar_msi_top_chip,
};
static int rcar_allocate_domains(struct rcar_msi *msi)
{ {
irq_set_chip_and_handler(irq, &rcar_msi_irq_chip, handle_simple_irq); struct rcar_pcie *pcie = &msi_to_host(msi)->pcie;
irq_set_chip_data(irq, domain->host_data); struct fwnode_handle *fwnode = dev_fwnode(pcie->dev);
struct irq_domain *parent;
parent = irq_domain_create_linear(fwnode, INT_PCI_MSI_NR,
&rcar_msi_domain_ops, msi);
if (!parent) {
dev_err(pcie->dev, "failed to create IRQ domain\n");
return -ENOMEM;
}
irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS);
msi->domain = pci_msi_create_irq_domain(fwnode, &rcar_msi_info, parent);
if (!msi->domain) {
dev_err(pcie->dev, "failed to create MSI domain\n");
irq_domain_remove(parent);
return -ENOMEM;
}
return 0; return 0;
} }
static const struct irq_domain_ops msi_domain_ops = { static void rcar_free_domains(struct rcar_msi *msi)
.map = rcar_msi_map,
};
static void rcar_pcie_unmap_msi(struct rcar_pcie_host *host)
{ {
struct rcar_msi *msi = &host->msi; struct irq_domain *parent = msi->domain->parent;
int i, irq;
for (i = 0; i < INT_PCI_MSI_NR; i++) {
irq = irq_find_mapping(msi->domain, i);
if (irq > 0)
irq_dispose_mapping(irq);
}
irq_domain_remove(msi->domain); irq_domain_remove(msi->domain);
} irq_domain_remove(parent);
static void rcar_pcie_hw_enable_msi(struct rcar_pcie_host *host)
{
struct rcar_pcie *pcie = &host->pcie;
struct rcar_msi *msi = &host->msi;
unsigned long base;
/* setup MSI data target */
base = virt_to_phys((void *)msi->pages);
rcar_pci_write_reg(pcie, lower_32_bits(base) | MSIFE, PCIEMSIALR);
rcar_pci_write_reg(pcie, upper_32_bits(base), PCIEMSIAUR);
/* enable all MSI interrupts */
rcar_pci_write_reg(pcie, 0xffffffff, PCIEMSIIER);
} }
static int rcar_pcie_enable_msi(struct rcar_pcie_host *host) static int rcar_pcie_enable_msi(struct rcar_pcie_host *host)
@ -698,29 +674,24 @@ static int rcar_pcie_enable_msi(struct rcar_pcie_host *host)
struct rcar_pcie *pcie = &host->pcie; struct rcar_pcie *pcie = &host->pcie;
struct device *dev = pcie->dev; struct device *dev = pcie->dev;
struct rcar_msi *msi = &host->msi; struct rcar_msi *msi = &host->msi;
int err, i; struct resource res;
int err;
mutex_init(&msi->lock); mutex_init(&msi->map_lock);
spin_lock_init(&msi->mask_lock);
msi->chip.dev = dev; err = of_address_to_resource(dev->of_node, 0, &res);
msi->chip.setup_irq = rcar_msi_setup_irq; if (err)
msi->chip.setup_irqs = rcar_msi_setup_irqs; return err;
msi->chip.teardown_irq = rcar_msi_teardown_irq;
msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR, err = rcar_allocate_domains(msi);
&msi_domain_ops, &msi->chip); if (err)
if (!msi->domain) { return err;
dev_err(dev, "failed to create IRQ domain\n");
return -ENOMEM;
}
for (i = 0; i < INT_PCI_MSI_NR; i++)
irq_create_mapping(msi->domain, i);
/* Two irqs are for MSI, but they are also used for non-MSI irqs */ /* Two irqs are for MSI, but they are also used for non-MSI irqs */
err = devm_request_irq(dev, msi->irq1, rcar_pcie_msi_irq, err = devm_request_irq(dev, msi->irq1, rcar_pcie_msi_irq,
IRQF_SHARED | IRQF_NO_THREAD, IRQF_SHARED | IRQF_NO_THREAD,
rcar_msi_irq_chip.name, host); rcar_msi_bottom_chip.name, host);
if (err < 0) { if (err < 0) {
dev_err(dev, "failed to request IRQ: %d\n", err); dev_err(dev, "failed to request IRQ: %d\n", err);
goto err; goto err;
@ -728,27 +699,32 @@ static int rcar_pcie_enable_msi(struct rcar_pcie_host *host)
err = devm_request_irq(dev, msi->irq2, rcar_pcie_msi_irq, err = devm_request_irq(dev, msi->irq2, rcar_pcie_msi_irq,
IRQF_SHARED | IRQF_NO_THREAD, IRQF_SHARED | IRQF_NO_THREAD,
rcar_msi_irq_chip.name, host); rcar_msi_bottom_chip.name, host);
if (err < 0) { if (err < 0) {
dev_err(dev, "failed to request IRQ: %d\n", err); dev_err(dev, "failed to request IRQ: %d\n", err);
goto err; goto err;
} }
/* setup MSI data target */ /* disable all MSIs */
msi->pages = __get_free_pages(GFP_KERNEL | GFP_DMA32, 0); rcar_pci_write_reg(pcie, 0, PCIEMSIIER);
rcar_pcie_hw_enable_msi(host);
/*
* Setup MSI data target using RC base address address, which
* is guaranteed to be in the low 32bit range on any RCar HW.
*/
rcar_pci_write_reg(pcie, lower_32_bits(res.start) | MSIFE, PCIEMSIALR);
rcar_pci_write_reg(pcie, upper_32_bits(res.start), PCIEMSIAUR);
return 0; return 0;
err: err:
rcar_pcie_unmap_msi(host); rcar_free_domains(msi);
return err; return err;
} }
static void rcar_pcie_teardown_msi(struct rcar_pcie_host *host) static void rcar_pcie_teardown_msi(struct rcar_pcie_host *host)
{ {
struct rcar_pcie *pcie = &host->pcie; struct rcar_pcie *pcie = &host->pcie;
struct rcar_msi *msi = &host->msi;
/* Disable all MSI interrupts */ /* Disable all MSI interrupts */
rcar_pci_write_reg(pcie, 0, PCIEMSIIER); rcar_pci_write_reg(pcie, 0, PCIEMSIIER);
@ -756,9 +732,7 @@ static void rcar_pcie_teardown_msi(struct rcar_pcie_host *host)
/* Disable address decoding of the MSI interrupt, MSIFE */ /* Disable address decoding of the MSI interrupt, MSIFE */
rcar_pci_write_reg(pcie, 0, PCIEMSIALR); rcar_pci_write_reg(pcie, 0, PCIEMSIALR);
free_pages(msi->pages, 0); rcar_free_domains(&host->msi);
rcar_pcie_unmap_msi(host);
} }
static int rcar_pcie_get_resources(struct rcar_pcie_host *host) static int rcar_pcie_get_resources(struct rcar_pcie_host *host)
@ -1011,8 +985,17 @@ static int __maybe_unused rcar_pcie_resume(struct device *dev)
dev_info(dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); dev_info(dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);
/* Enable MSI */ /* Enable MSI */
if (IS_ENABLED(CONFIG_PCI_MSI)) if (IS_ENABLED(CONFIG_PCI_MSI)) {
rcar_pcie_hw_enable_msi(host); struct resource res;
u32 val;
of_address_to_resource(dev->of_node, 0, &res);
rcar_pci_write_reg(pcie, upper_32_bits(res.start), PCIEMSIAUR);
rcar_pci_write_reg(pcie, lower_32_bits(res.start) | MSIFE, PCIEMSIALR);
bitmap_to_arr32(&val, host->msi.used, INT_PCI_MSI_NR);
rcar_pci_write_reg(pcie, val, PCIEMSIIER);
}
rcar_pcie_hw_enable(host); rcar_pcie_hw_enable(host);

View file

@ -93,25 +93,23 @@
/** /**
* struct xilinx_pcie_port - PCIe port information * struct xilinx_pcie_port - PCIe port information
* @reg_base: IO Mapped Register Base * @reg_base: IO Mapped Register Base
* @irq: Interrupt number
* @msi_pages: MSI pages
* @dev: Device pointer * @dev: Device pointer
* @msi_map: Bitmap of allocated MSIs
* @map_lock: Mutex protecting the MSI allocation
* @msi_domain: MSI IRQ domain pointer * @msi_domain: MSI IRQ domain pointer
* @leg_domain: Legacy IRQ domain pointer * @leg_domain: Legacy IRQ domain pointer
* @resources: Bus Resources * @resources: Bus Resources
*/ */
struct xilinx_pcie_port { struct xilinx_pcie_port {
void __iomem *reg_base; void __iomem *reg_base;
u32 irq;
unsigned long msi_pages;
struct device *dev; struct device *dev;
unsigned long msi_map[BITS_TO_LONGS(XILINX_NUM_MSI_IRQS)];
struct mutex map_lock;
struct irq_domain *msi_domain; struct irq_domain *msi_domain;
struct irq_domain *leg_domain; struct irq_domain *leg_domain;
struct list_head resources; struct list_head resources;
}; };
static DECLARE_BITMAP(msi_irq_in_use, XILINX_NUM_MSI_IRQS);
static inline u32 pcie_read(struct xilinx_pcie_port *port, u32 reg) static inline u32 pcie_read(struct xilinx_pcie_port *port, u32 reg)
{ {
return readl(port->reg_base + reg); return readl(port->reg_base + reg);
@ -196,151 +194,118 @@ static struct pci_ops xilinx_pcie_ops = {
/* MSI functions */ /* MSI functions */
/** static void xilinx_msi_top_irq_ack(struct irq_data *d)
* xilinx_pcie_destroy_msi - Free MSI number
* @irq: IRQ to be freed
*/
static void xilinx_pcie_destroy_msi(unsigned int irq)
{ {
struct msi_desc *msi; /*
struct xilinx_pcie_port *port; * xilinx_pcie_intr_handler() will have performed the Ack.
struct irq_data *d = irq_get_irq_data(irq); * Eventually, this should be fixed and the Ack be moved in
irq_hw_number_t hwirq = irqd_to_hwirq(d); * the respective callbacks for INTx and MSI.
*/
if (!test_bit(hwirq, msi_irq_in_use)) {
msi = irq_get_msi_desc(irq);
port = msi_desc_to_pci_sysdata(msi);
dev_err(port->dev, "Trying to free unused MSI#%d\n", irq);
} else {
clear_bit(hwirq, msi_irq_in_use);
}
} }
/** static struct irq_chip xilinx_msi_top_chip = {
* xilinx_pcie_assign_msi - Allocate MSI number .name = "PCIe MSI",
* .irq_ack = xilinx_msi_top_irq_ack,
* Return: A valid IRQ on success and error value on failure. };
*/
static int xilinx_pcie_assign_msi(void)
{
int pos;
pos = find_first_zero_bit(msi_irq_in_use, XILINX_NUM_MSI_IRQS); static int xilinx_msi_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force)
if (pos < XILINX_NUM_MSI_IRQS) {
set_bit(pos, msi_irq_in_use); return -EINVAL;
else }
static void xilinx_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
struct xilinx_pcie_port *pcie = irq_data_get_irq_chip_data(data);
phys_addr_t pa = ALIGN_DOWN(virt_to_phys(pcie), SZ_4K);
msg->address_lo = lower_32_bits(pa);
msg->address_hi = upper_32_bits(pa);
msg->data = data->hwirq;
}
static struct irq_chip xilinx_msi_bottom_chip = {
.name = "Xilinx MSI",
.irq_set_affinity = xilinx_msi_set_affinity,
.irq_compose_msi_msg = xilinx_compose_msi_msg,
};
static int xilinx_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *args)
{
struct xilinx_pcie_port *port = domain->host_data;
int hwirq, i;
mutex_lock(&port->map_lock);
hwirq = bitmap_find_free_region(port->msi_map, XILINX_NUM_MSI_IRQS, order_base_2(nr_irqs));
mutex_unlock(&port->map_lock);
if (hwirq < 0)
return -ENOSPC; return -ENOSPC;
return pos; for (i = 0; i < nr_irqs; i++)
} irq_domain_set_info(domain, virq + i, hwirq + i,
&xilinx_msi_bottom_chip, domain->host_data,
/** handle_edge_irq, NULL, NULL);
* xilinx_msi_teardown_irq - Destroy the MSI
* @chip: MSI Chip descriptor
* @irq: MSI IRQ to destroy
*/
static void xilinx_msi_teardown_irq(struct msi_controller *chip,
unsigned int irq)
{
xilinx_pcie_destroy_msi(irq);
irq_dispose_mapping(irq);
}
/**
* xilinx_pcie_msi_setup_irq - Setup MSI request
* @chip: MSI chip pointer
* @pdev: PCIe device pointer
* @desc: MSI descriptor pointer
*
* Return: '0' on success and error value on failure
*/
static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip,
struct pci_dev *pdev,
struct msi_desc *desc)
{
struct xilinx_pcie_port *port = pdev->bus->sysdata;
unsigned int irq;
int hwirq;
struct msi_msg msg;
phys_addr_t msg_addr;
hwirq = xilinx_pcie_assign_msi();
if (hwirq < 0)
return hwirq;
irq = irq_create_mapping(port->msi_domain, hwirq);
if (!irq)
return -EINVAL;
irq_set_msi_desc(irq, desc);
msg_addr = virt_to_phys((void *)port->msi_pages);
msg.address_hi = 0;
msg.address_lo = msg_addr;
msg.data = irq;
pci_write_msi_msg(irq, &msg);
return 0; return 0;
} }
/* MSI Chip Descriptor */ static void xilinx_msi_domain_free(struct irq_domain *domain, unsigned int virq,
static struct msi_controller xilinx_pcie_msi_chip = { unsigned int nr_irqs)
.setup_irq = xilinx_pcie_msi_setup_irq,
.teardown_irq = xilinx_msi_teardown_irq,
};
/* HW Interrupt Chip Descriptor */
static struct irq_chip xilinx_msi_irq_chip = {
.name = "Xilinx PCIe MSI",
.irq_enable = pci_msi_unmask_irq,
.irq_disable = pci_msi_mask_irq,
.irq_mask = pci_msi_mask_irq,
.irq_unmask = pci_msi_unmask_irq,
};
/**
* xilinx_pcie_msi_map - Set the handler for the MSI and mark IRQ as valid
* @domain: IRQ domain
* @irq: Virtual IRQ number
* @hwirq: HW interrupt number
*
* Return: Always returns 0.
*/
static int xilinx_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
irq_hw_number_t hwirq)
{ {
irq_set_chip_and_handler(irq, &xilinx_msi_irq_chip, handle_simple_irq); struct irq_data *d = irq_domain_get_irq_data(domain, virq);
irq_set_chip_data(irq, domain->host_data); struct xilinx_pcie_port *port = domain->host_data;
return 0; mutex_lock(&port->map_lock);
bitmap_release_region(port->msi_map, d->hwirq, order_base_2(nr_irqs));
mutex_unlock(&port->map_lock);
} }
/* IRQ Domain operations */ static const struct irq_domain_ops xilinx_msi_domain_ops = {
static const struct irq_domain_ops msi_domain_ops = { .alloc = xilinx_msi_domain_alloc,
.map = xilinx_pcie_msi_map, .free = xilinx_msi_domain_free,
}; };
/** static struct msi_domain_info xilinx_msi_info = {
* xilinx_pcie_enable_msi - Enable MSI support .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
* @port: PCIe port information .chip = &xilinx_msi_top_chip,
*/ };
static int xilinx_pcie_enable_msi(struct xilinx_pcie_port *port)
{
phys_addr_t msg_addr;
port->msi_pages = __get_free_pages(GFP_KERNEL, 0); static int xilinx_allocate_msi_domains(struct xilinx_pcie_port *pcie)
if (!port->msi_pages) {
struct fwnode_handle *fwnode = dev_fwnode(pcie->dev);
struct irq_domain *parent;
parent = irq_domain_create_linear(fwnode, XILINX_NUM_MSI_IRQS,
&xilinx_msi_domain_ops, pcie);
if (!parent) {
dev_err(pcie->dev, "failed to create IRQ domain\n");
return -ENOMEM; return -ENOMEM;
}
irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS);
msg_addr = virt_to_phys((void *)port->msi_pages); pcie->msi_domain = pci_msi_create_irq_domain(fwnode, &xilinx_msi_info, parent);
pcie_write(port, 0x0, XILINX_PCIE_REG_MSIBASE1); if (!pcie->msi_domain) {
pcie_write(port, msg_addr, XILINX_PCIE_REG_MSIBASE2); dev_err(pcie->dev, "failed to create MSI domain\n");
irq_domain_remove(parent);
return -ENOMEM;
}
return 0; return 0;
} }
static void xilinx_free_msi_domains(struct xilinx_pcie_port *pcie)
{
struct irq_domain *parent = pcie->msi_domain->parent;
irq_domain_remove(pcie->msi_domain);
irq_domain_remove(parent);
}
/* INTx Functions */ /* INTx Functions */
/** /**
@ -420,6 +385,8 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
} }
if (status & (XILINX_PCIE_INTR_INTX | XILINX_PCIE_INTR_MSI)) { if (status & (XILINX_PCIE_INTR_INTX | XILINX_PCIE_INTR_MSI)) {
unsigned int irq;
val = pcie_read(port, XILINX_PCIE_REG_RPIFR1); val = pcie_read(port, XILINX_PCIE_REG_RPIFR1);
/* Check whether interrupt valid */ /* Check whether interrupt valid */
@ -432,20 +399,19 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
if (val & XILINX_PCIE_RPIFR1_MSI_INTR) { if (val & XILINX_PCIE_RPIFR1_MSI_INTR) {
val = pcie_read(port, XILINX_PCIE_REG_RPIFR2) & val = pcie_read(port, XILINX_PCIE_REG_RPIFR2) &
XILINX_PCIE_RPIFR2_MSG_DATA; XILINX_PCIE_RPIFR2_MSG_DATA;
irq = irq_find_mapping(port->msi_domain->parent, val);
} else { } else {
val = (val & XILINX_PCIE_RPIFR1_INTR_MASK) >> val = (val & XILINX_PCIE_RPIFR1_INTR_MASK) >>
XILINX_PCIE_RPIFR1_INTR_SHIFT; XILINX_PCIE_RPIFR1_INTR_SHIFT;
val = irq_find_mapping(port->leg_domain, val); irq = irq_find_mapping(port->leg_domain, val);
} }
/* Clear interrupt FIFO register 1 */ /* Clear interrupt FIFO register 1 */
pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK, pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK,
XILINX_PCIE_REG_RPIFR1); XILINX_PCIE_REG_RPIFR1);
/* Handle the interrupt */ if (irq)
if (IS_ENABLED(CONFIG_PCI_MSI) || generic_handle_irq(irq);
!(val & XILINX_PCIE_RPIFR1_MSI_INTR))
generic_handle_irq(val);
} }
if (status & XILINX_PCIE_INTR_SLV_UNSUPP) if (status & XILINX_PCIE_INTR_SLV_UNSUPP)
@ -491,12 +457,11 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port) static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
{ {
struct device *dev = port->dev; struct device *dev = port->dev;
struct device_node *node = dev->of_node;
struct device_node *pcie_intc_node; struct device_node *pcie_intc_node;
int ret; int ret;
/* Setup INTx */ /* Setup INTx */
pcie_intc_node = of_get_next_child(node, NULL); pcie_intc_node = of_get_next_child(dev->of_node, NULL);
if (!pcie_intc_node) { if (!pcie_intc_node) {
dev_err(dev, "No PCIe Intc node found\n"); dev_err(dev, "No PCIe Intc node found\n");
return -ENODEV; return -ENODEV;
@ -513,18 +478,14 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
/* Setup MSI */ /* Setup MSI */
if (IS_ENABLED(CONFIG_PCI_MSI)) { if (IS_ENABLED(CONFIG_PCI_MSI)) {
port->msi_domain = irq_domain_add_linear(node, phys_addr_t pa = ALIGN_DOWN(virt_to_phys(port), SZ_4K);
XILINX_NUM_MSI_IRQS,
&msi_domain_ops,
&xilinx_pcie_msi_chip);
if (!port->msi_domain) {
dev_err(dev, "Failed to get a MSI IRQ domain\n");
return -ENODEV;
}
ret = xilinx_pcie_enable_msi(port); ret = xilinx_allocate_msi_domains(port);
if (ret) if (ret)
return ret; return ret;
pcie_write(port, upper_32_bits(pa), XILINX_PCIE_REG_MSIBASE1);
pcie_write(port, lower_32_bits(pa), XILINX_PCIE_REG_MSIBASE2);
} }
return 0; return 0;
@ -572,6 +533,7 @@ static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)
struct device *dev = port->dev; struct device *dev = port->dev;
struct device_node *node = dev->of_node; struct device_node *node = dev->of_node;
struct resource regs; struct resource regs;
unsigned int irq;
int err; int err;
err = of_address_to_resource(node, 0, &regs); err = of_address_to_resource(node, 0, &regs);
@ -584,12 +546,12 @@ static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)
if (IS_ERR(port->reg_base)) if (IS_ERR(port->reg_base))
return PTR_ERR(port->reg_base); return PTR_ERR(port->reg_base);
port->irq = irq_of_parse_and_map(node, 0); irq = irq_of_parse_and_map(node, 0);
err = devm_request_irq(dev, port->irq, xilinx_pcie_intr_handler, err = devm_request_irq(dev, irq, xilinx_pcie_intr_handler,
IRQF_SHARED | IRQF_NO_THREAD, IRQF_SHARED | IRQF_NO_THREAD,
"xilinx-pcie", port); "xilinx-pcie", port);
if (err) { if (err) {
dev_err(dev, "unable to request irq %d\n", port->irq); dev_err(dev, "unable to request irq %d\n", irq);
return err; return err;
} }
@ -617,7 +579,7 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
port = pci_host_bridge_priv(bridge); port = pci_host_bridge_priv(bridge);
mutex_init(&port->map_lock);
port->dev = dev; port->dev = dev;
err = xilinx_pcie_parse_dt(port); err = xilinx_pcie_parse_dt(port);
@ -637,11 +599,11 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
bridge->sysdata = port; bridge->sysdata = port;
bridge->ops = &xilinx_pcie_ops; bridge->ops = &xilinx_pcie_ops;
#ifdef CONFIG_PCI_MSI err = pci_host_probe(bridge);
xilinx_pcie_msi_chip.dev = dev; if (err)
bridge->msi = &xilinx_pcie_msi_chip; xilinx_free_msi_domains(port);
#endif
return pci_host_probe(bridge); return err;
} }
static const struct of_device_id xilinx_pcie_of_match[] = { static const struct of_device_id xilinx_pcie_of_match[] = {

View file

@ -64,39 +64,18 @@ static void pci_msi_teardown_msi_irqs(struct pci_dev *dev)
/* Arch hooks */ /* Arch hooks */
int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
{ {
struct msi_controller *chip = dev->bus->msi;
int err;
if (!chip || !chip->setup_irq)
return -EINVAL; return -EINVAL;
err = chip->setup_irq(chip, dev, desc);
if (err < 0)
return err;
irq_set_chip_data(desc->irq, chip);
return 0;
} }
void __weak arch_teardown_msi_irq(unsigned int irq) void __weak arch_teardown_msi_irq(unsigned int irq)
{ {
struct msi_controller *chip = irq_get_chip_data(irq);
if (!chip || !chip->teardown_irq)
return;
chip->teardown_irq(chip, irq);
} }
int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) 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; struct msi_desc *entry;
int ret; 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 * If an architecture wants to support multiple MSI, it needs to
* override arch_setup_msi_irqs() * override arch_setup_msi_irqs()
@ -115,11 +94,7 @@ int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
return 0; return 0;
} }
/* void __weak arch_teardown_msi_irqs(struct pci_dev *dev)
* We have a default implementation available as a separate non-weak
* function, as it is used by the Xen x86 PCI code
*/
void default_teardown_msi_irqs(struct pci_dev *dev)
{ {
int i; int i;
struct msi_desc *entry; struct msi_desc *entry;
@ -129,11 +104,6 @@ void default_teardown_msi_irqs(struct pci_dev *dev)
for (i = 0; i < entry->nvec_used; i++) for (i = 0; i < entry->nvec_used; i++)
arch_teardown_msi_irq(entry->irq + i); arch_teardown_msi_irq(entry->irq + i);
} }
void __weak arch_teardown_msi_irqs(struct pci_dev *dev)
{
return default_teardown_msi_irqs(dev);
}
#endif /* CONFIG_PCI_MSI_ARCH_FALLBACKS */ #endif /* CONFIG_PCI_MSI_ARCH_FALLBACKS */
static void default_restore_msi_irq(struct pci_dev *dev, int irq) static void default_restore_msi_irq(struct pci_dev *dev, int irq)
@ -901,8 +871,15 @@ static int pci_msi_supported(struct pci_dev *dev, int nvec)
* Any bridge which does NOT route MSI transactions from its * Any bridge which does NOT route MSI transactions from its
* secondary bus to its primary bus must set NO_MSI flag on * secondary bus to its primary bus must set NO_MSI flag on
* the secondary pci_bus. * the secondary pci_bus.
* We expect only arch-specific PCI host bus controller driver *
* or quirks for specific PCI bridges to be setting NO_MSI. * The NO_MSI flag can either be set directly by:
* - arch-specific PCI host bus controller drivers (deprecated)
* - quirks for specific PCI bridges
*
* or indirectly by platform-specific PCI host bridge drivers by
* advertising the 'msi_domain' property, which results in
* the NO_MSI flag when no MSI domain is found for this bridge
* at probe time.
*/ */
for (bus = dev->bus; bus; bus = bus->parent) for (bus = dev->bus; bus; bus = bus->parent)
if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)

View file

@ -895,7 +895,6 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
/* Temporarily move resources off the list */ /* Temporarily move resources off the list */
list_splice_init(&bridge->windows, &resources); list_splice_init(&bridge->windows, &resources);
bus->sysdata = bridge->sysdata; bus->sysdata = bridge->sysdata;
bus->msi = bridge->msi;
bus->ops = bridge->ops; bus->ops = bridge->ops;
bus->number = bus->busn_res.start = bridge->busnr; bus->number = bus->busn_res.start = bridge->busnr;
#ifdef CONFIG_PCI_DOMAINS_GENERIC #ifdef CONFIG_PCI_DOMAINS_GENERIC
@ -926,6 +925,8 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
device_enable_async_suspend(bus->bridge); device_enable_async_suspend(bus->bridge);
pci_set_bus_of_node(bus); pci_set_bus_of_node(bus);
pci_set_bus_msi_domain(bus); pci_set_bus_msi_domain(bus);
if (bridge->msi_domain && !dev_get_msi_domain(&bus->dev))
bus->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
if (!parent) if (!parent)
set_dev_node(bus->bridge, pcibus_to_node(bus)); set_dev_node(bus->bridge, pcibus_to_node(bus));
@ -1053,7 +1054,6 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
return NULL; return NULL;
child->parent = parent; child->parent = parent;
child->msi = parent->msi;
child->sysdata = parent->sysdata; child->sysdata = parent->sysdata;
child->bus_flags = parent->bus_flags; child->bus_flags = parent->bus_flags;

View file

@ -2580,10 +2580,8 @@ static int msi_ht_cap_enabled(struct pci_dev *dev)
/* Check the HyperTransport MSI mapping to know whether MSI is enabled or not */ /* Check the HyperTransport MSI mapping to know whether MSI is enabled or not */
static void quirk_msi_ht_cap(struct pci_dev *dev) static void quirk_msi_ht_cap(struct pci_dev *dev)
{ {
if (dev->subordinate && !msi_ht_cap_enabled(dev)) { if (!msi_ht_cap_enabled(dev))
pci_warn(dev, "MSI quirk detected; subordinate MSI disabled\n"); quirk_disable_msi(dev);
dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
}
} }
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE, DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE,
quirk_msi_ht_cap); quirk_msi_ht_cap);
@ -2596,9 +2594,6 @@ static void quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev)
{ {
struct pci_dev *pdev; struct pci_dev *pdev;
if (!dev->subordinate)
return;
/* /*
* Check HT MSI cap on this chipset and the root one. A single one * Check HT MSI cap on this chipset and the root one. A single one
* having MSI is enough to be sure that MSI is supported. * having MSI is enough to be sure that MSI is supported.
@ -2606,10 +2601,8 @@ static void quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev)
pdev = pci_get_slot(dev->bus, 0); pdev = pci_get_slot(dev->bus, 0);
if (!pdev) if (!pdev)
return; return;
if (!msi_ht_cap_enabled(dev) && !msi_ht_cap_enabled(pdev)) { if (!msi_ht_cap_enabled(pdev))
pci_warn(dev, "MSI quirk detected; subordinate MSI disabled\n"); quirk_msi_ht_cap(dev);
dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
}
pci_dev_put(pdev); pci_dev_put(pdev);
} }
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,

View file

@ -240,8 +240,7 @@ void pci_msi_unmask_irq(struct irq_data *data);
/* /*
* The arch hooks to setup up msi irqs. Default functions are implemented * The arch hooks to setup up msi irqs. Default functions are implemented
* as weak symbols so that they /can/ be overriden by architecture specific * as weak symbols so that they /can/ be overriden by architecture specific
* code if needed. These hooks must be enabled by the architecture or by * code if needed. These hooks can only be enabled by the architecture.
* drivers which depend on them via msi_controller based MSI handling.
* *
* If CONFIG_PCI_MSI_ARCH_FALLBACKS is not selected they are replaced by * If CONFIG_PCI_MSI_ARCH_FALLBACKS is not selected they are replaced by
* stubs with warnings. * stubs with warnings.
@ -251,7 +250,6 @@ int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc);
void arch_teardown_msi_irq(unsigned int irq); void arch_teardown_msi_irq(unsigned int irq);
int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type); int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type);
void arch_teardown_msi_irqs(struct pci_dev *dev); void arch_teardown_msi_irqs(struct pci_dev *dev);
void default_teardown_msi_irqs(struct pci_dev *dev);
#else #else
static inline int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) static inline int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{ {
@ -272,19 +270,6 @@ static inline void arch_teardown_msi_irqs(struct pci_dev *dev)
void arch_restore_msi_irqs(struct pci_dev *dev); void arch_restore_msi_irqs(struct pci_dev *dev);
void default_restore_msi_irqs(struct pci_dev *dev); void default_restore_msi_irqs(struct pci_dev *dev);
struct msi_controller {
struct module *owner;
struct device *dev;
struct device_node *of_node;
struct list_head list;
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);
};
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
#include <linux/irqhandler.h> #include <linux/irqhandler.h>

View file

@ -539,7 +539,6 @@ struct pci_host_bridge {
int (*map_irq)(const struct pci_dev *, u8, u8); int (*map_irq)(const struct pci_dev *, u8, u8);
void (*release_fn)(struct pci_host_bridge *); void (*release_fn)(struct pci_host_bridge *);
void *release_data; void *release_data;
struct msi_controller *msi;
unsigned int ignore_reset_delay:1; /* For entire hierarchy */ unsigned int ignore_reset_delay:1; /* For entire hierarchy */
unsigned int no_ext_tags:1; /* No Extended Tags */ unsigned int no_ext_tags:1; /* No Extended Tags */
unsigned int native_aer:1; /* OS may use PCIe AER */ unsigned int native_aer:1; /* OS may use PCIe AER */
@ -550,6 +549,7 @@ struct pci_host_bridge {
unsigned int native_dpc:1; /* OS may use PCIe DPC */ unsigned int native_dpc:1; /* OS may use PCIe DPC */
unsigned int preserve_config:1; /* Preserve FW resource setup */ unsigned int preserve_config:1; /* Preserve FW resource setup */
unsigned int size_windows:1; /* Enable root bus sizing */ unsigned int size_windows:1; /* Enable root bus sizing */
unsigned int msi_domain:1; /* Bridge wants MSI domain */
/* Resource alignment requirements */ /* Resource alignment requirements */
resource_size_t (*align_resource)(struct pci_dev *dev, resource_size_t (*align_resource)(struct pci_dev *dev,
@ -620,7 +620,6 @@ struct pci_bus {
struct resource busn_res; /* Bus numbers routed to this bus */ struct resource busn_res; /* Bus numbers routed to this bus */
struct pci_ops *ops; /* Configuration access functions */ struct pci_ops *ops; /* Configuration access functions */
struct msi_controller *msi; /* MSI controller */
void *sysdata; /* Hook for sys-specific extension */ void *sysdata; /* Hook for sys-specific extension */
struct proc_dir_entry *procdir; /* Directory entry in /proc/bus/pci */ struct proc_dir_entry *procdir; /* Directory entry in /proc/bus/pci */