From 38737d82f9f0168955f9944c3f8bd3bb262c7e88 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 27 Oct 2014 10:44:36 +0800 Subject: PCI/MSI: Add pci_msi_ignore_mask to prevent writes to MSI/MSI-X Mask Bits MSI-X vector Mask Bits are in MSI-X Tables in PCI memory space. Xen PV guests can't write to those tables. MSI vector Mask Bits are in PCI configuration space. Xen PV guests can write to config space, but those writes are ignored. Commit 0e4ccb1505a9 ("PCI: Add x86_msi.msi_mask_irq() and msix_mask_irq()") added a way to override default_mask_msi_irqs() and default_mask_msix_irqs() so they can be no-ops in Xen guests, but this is more complicated than necessary. Add "pci_msi_ignore_mask" in the core PCI MSI code. If set, default_mask_msi_irqs() and default_mask_msix_irqs() return without doing anything. This is less flexible, but much simpler. [bhelgaas: changelog] Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Reviewed-by: David Vrabel CC: Konrad Rzeszutek Wilk CC: xen-devel@lists.xenproject.org--- include/linux/msi.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/msi.h b/include/linux/msi.h index 44f4746d033b..86dc501a1534 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -10,6 +10,7 @@ struct msi_msg { u32 data; /* 16 bits of msi message data */ }; +extern int pci_msi_ignore_mask; /* Helper functions */ struct irq_data; struct msi_desc; -- cgit v1.2.3 From 03f56e42d03eb7d0a47e40e9ae72a3ac0afeff08 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 27 Oct 2014 10:44:37 +0800 Subject: Revert "PCI: Add x86_msi.msi_mask_irq() and msix_mask_irq()" The problem fixed by 0e4ccb1505a9 ("PCI: Add x86_msi.msi_mask_irq() and msix_mask_irq()") has been fixed in a simpler way by a previous commit ("PCI/MSI: Add pci_msi_ignore_mask to prevent writes to MSI/MSI-X Mask Bits"). The msi_mask_irq() and msix_mask_irq() x86_msi_ops added by 0e4ccb1505a9 are no longer needed, so revert the commit. default_msi_mask_irq() and default_msix_mask_irq() were added by 0e4ccb1505a9 and are still used by s390, so keep them for now. [bhelgaas: changelog] Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: David Vrabel CC: Konrad Rzeszutek Wilk CC: xen-devel@lists.xenproject.org--- arch/x86/include/asm/x86_init.h | 3 --- arch/x86/kernel/x86_init.c | 10 ---------- arch/x86/pci/xen.c | 13 +------------ drivers/pci/msi.c | 22 ++++++---------------- include/linux/msi.h | 6 ++++-- 5 files changed, 11 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index e45e4da96bf1..f58a9c7a3c86 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h @@ -172,7 +172,6 @@ struct x86_platform_ops { struct pci_dev; struct msi_msg; -struct msi_desc; struct x86_msi_ops { int (*setup_msi_irqs)(struct pci_dev *dev, int nvec, int type); @@ -183,8 +182,6 @@ struct x86_msi_ops { void (*teardown_msi_irqs)(struct pci_dev *dev); void (*restore_msi_irqs)(struct pci_dev *dev); int (*setup_hpet_msi)(unsigned int irq, unsigned int id); - u32 (*msi_mask_irq)(struct msi_desc *desc, u32 mask, u32 flag); - u32 (*msix_mask_irq)(struct msi_desc *desc, u32 flag); }; struct IO_APIC_route_entry; diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c index e48b674639cc..234b0722de53 100644 --- a/arch/x86/kernel/x86_init.c +++ b/arch/x86/kernel/x86_init.c @@ -116,8 +116,6 @@ struct x86_msi_ops x86_msi = { .teardown_msi_irqs = default_teardown_msi_irqs, .restore_msi_irqs = default_restore_msi_irqs, .setup_hpet_msi = default_setup_hpet_msi, - .msi_mask_irq = default_msi_mask_irq, - .msix_mask_irq = default_msix_mask_irq, }; /* MSI arch specific hooks */ @@ -140,14 +138,6 @@ void arch_restore_msi_irqs(struct pci_dev *dev) { x86_msi.restore_msi_irqs(dev); } -u32 arch_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) -{ - return x86_msi.msi_mask_irq(desc, mask, flag); -} -u32 arch_msix_mask_irq(struct msi_desc *desc, u32 flag) -{ - return x86_msi.msix_mask_irq(desc, flag); -} #endif struct x86_io_apic_ops x86_io_apic_ops = { diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index 5ef62ed20ba4..466b978e13a5 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -394,14 +394,7 @@ static void xen_teardown_msi_irq(unsigned int irq) { xen_destroy_irq(irq); } -static u32 xen_nop_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) -{ - return 0; -} -static u32 xen_nop_msix_mask_irq(struct msi_desc *desc, u32 flag) -{ - return 0; -} + #endif int __init pci_xen_init(void) @@ -425,8 +418,6 @@ int __init pci_xen_init(void) x86_msi.setup_msi_irqs = xen_setup_msi_irqs; x86_msi.teardown_msi_irq = xen_teardown_msi_irq; x86_msi.teardown_msi_irqs = xen_teardown_msi_irqs; - x86_msi.msi_mask_irq = xen_nop_msi_mask_irq; - x86_msi.msix_mask_irq = xen_nop_msix_mask_irq; pci_msi_ignore_mask = 1; #endif return 0; @@ -507,8 +498,6 @@ int __init pci_xen_initial_domain(void) x86_msi.setup_msi_irqs = xen_initdom_setup_msi_irqs; x86_msi.teardown_msi_irq = xen_teardown_msi_irq; x86_msi.restore_msi_irqs = xen_initdom_restore_msi_irqs; - x86_msi.msi_mask_irq = xen_nop_msi_mask_irq; - x86_msi.msix_mask_irq = xen_nop_msix_mask_irq; pci_msi_ignore_mask = 1; #endif xen_setup_acpi_sci(); diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 066c2fb9763a..d9a92cf6a6d6 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -164,7 +164,7 @@ static inline __attribute_const__ u32 msi_mask(unsigned x) * reliably as devices without an INTx disable bit will then generate a * level IRQ which will never be cleared. */ -u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) +u32 __msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) { u32 mask_bits = desc->masked; @@ -178,14 +178,9 @@ u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) return mask_bits; } -__weak u32 arch_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) -{ - return default_msi_mask_irq(desc, mask, flag); -} - static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) { - desc->masked = arch_msi_mask_irq(desc, mask, flag); + desc->masked = __msi_mask_irq(desc, mask, flag); } /* @@ -195,7 +190,7 @@ static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) * file. This saves a few milliseconds when initialising devices with lots * of MSI-X interrupts. */ -u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag) +u32 __msix_mask_irq(struct msi_desc *desc, u32 flag) { u32 mask_bits = desc->masked; unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + @@ -212,14 +207,9 @@ u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag) return mask_bits; } -__weak u32 arch_msix_mask_irq(struct msi_desc *desc, u32 flag) -{ - return default_msix_mask_irq(desc, flag); -} - static void msix_mask_irq(struct msi_desc *desc, u32 flag) { - desc->masked = arch_msix_mask_irq(desc, flag); + desc->masked = __msix_mask_irq(desc, flag); } static void msi_set_mask_bit(struct irq_data *data, u32 flag) @@ -874,7 +864,7 @@ void pci_msi_shutdown(struct pci_dev *dev) /* Return the device with MSI unmasked as initial states */ mask = msi_mask(desc->msi_attrib.multi_cap); /* Keep cached state to be restored */ - arch_msi_mask_irq(desc, mask, ~mask); + __msi_mask_irq(desc, mask, ~mask); /* Restore dev->irq to its default pin-assertion irq */ dev->irq = desc->msi_attrib.default_irq; @@ -972,7 +962,7 @@ void pci_msix_shutdown(struct pci_dev *dev) /* Return the device with MSI-X masked as initial states */ list_for_each_entry(entry, &dev->msi_list, list) { /* Keep cached states to be restored */ - arch_msix_mask_irq(entry, 1); + __msix_mask_irq(entry, 1); } msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); diff --git a/include/linux/msi.h b/include/linux/msi.h index 86dc501a1534..f6630a53be70 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -22,6 +22,8 @@ void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void read_msi_msg(unsigned int irq, struct msi_msg *msg); void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg); void write_msi_msg(unsigned int irq, struct msi_msg *msg); +u32 __msix_mask_irq(struct msi_desc *desc, u32 flag); +u32 __msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); struct msi_desc { struct { @@ -62,8 +64,8 @@ void arch_restore_msi_irqs(struct pci_dev *dev); void default_teardown_msi_irqs(struct pci_dev *dev); void default_restore_msi_irqs(struct pci_dev *dev); -u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); -u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag); +#define default_msi_mask_irq __msi_mask_irq +#define default_msix_mask_irq __msix_mask_irq struct msi_chip { struct module *owner; -- cgit v1.2.3 From f8338694270224970cbaae7e404517ec39f9c753 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 27 Oct 2014 10:44:38 +0800 Subject: s390/MSI: Use __msi_mask_irq() instead of default_msi_mask_irq() Now only s390/MSI use default_msi_mask_irq() and default_msix_mask_irq(), replace them with the common MSI mask IRQ functions __msi_mask_irq() and __msix_mask_irq(). Remove default_msi_mask_irq() and default_msix_mask_irq(). Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: Sebastian Ott CC: linux-s390@vger.kernel.org--- arch/s390/pci/pci.c | 4 ++-- include/linux/msi.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 2fa7b14b9c08..552b9908aa77 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -448,9 +448,9 @@ void arch_teardown_msi_irqs(struct pci_dev *pdev) /* Release MSI interrupts */ list_for_each_entry(msi, &pdev->msi_list, list) { if (msi->msi_attrib.is_msix) - default_msix_mask_irq(msi, 1); + __msix_mask_irq(msi, 1); else - default_msi_mask_irq(msi, 1, 1); + __msi_mask_irq(msi, 1, 1); irq_set_msi_desc(msi->irq, NULL); irq_free_desc(msi->irq); msi->msg.address_lo = 0; diff --git a/include/linux/msi.h b/include/linux/msi.h index f6630a53be70..efad12742e45 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -64,8 +64,6 @@ void arch_restore_msi_irqs(struct pci_dev *dev); void default_teardown_msi_irqs(struct pci_dev *dev); void default_restore_msi_irqs(struct pci_dev *dev); -#define default_msi_mask_irq __msi_mask_irq -#define default_msix_mask_irq __msix_mask_irq struct msi_chip { struct module *owner; -- cgit v1.2.3 From c2791b806988100cc1c047e2b0b5c5d0914aa3b6 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Tue, 11 Nov 2014 17:45:45 -0700 Subject: PCI/MSI: Rename "struct msi_chip" to "struct msi_controller" "msi_chip" isn't very descriptive, so rename it to "msi_controller". That tells a little more about what it does and is already used in device tree bindings. No functional change. [bhelgaas: changelog, change *only* the struct name so it's reviewable] Suggested-by: Bjorn Helgaas Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/irqchip/irq-armada-370-xp.c | 6 +++--- drivers/of/of_pci.c | 8 ++++---- drivers/pci/host/pci-keystone-dw.c | 2 +- drivers/pci/host/pci-keystone.h | 2 +- drivers/pci/host/pci-mvebu.c | 2 +- drivers/pci/host/pci-tegra.c | 11 ++++++----- drivers/pci/host/pcie-designware.c | 6 +++--- drivers/pci/host/pcie-designware.h | 2 +- drivers/pci/host/pcie-rcar.c | 8 ++++---- drivers/pci/host/pcie-xilinx.c | 7 ++++--- drivers/pci/msi.c | 4 ++-- include/linux/msi.h | 6 +++--- include/linux/of_pci.h | 12 ++++++------ include/linux/pci.h | 2 +- 14 files changed, 40 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 3e238cd049e6..4e202375e027 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -129,7 +129,7 @@ static void armada_370_xp_free_msi(int hwirq) mutex_unlock(&msi_used_lock); } -static int armada_370_xp_setup_msi_irq(struct msi_chip *chip, +static int armada_370_xp_setup_msi_irq(struct msi_controller *chip, struct pci_dev *pdev, struct msi_desc *desc) { @@ -160,7 +160,7 @@ static int armada_370_xp_setup_msi_irq(struct msi_chip *chip, return 0; } -static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip, +static void armada_370_xp_teardown_msi_irq(struct msi_controller *chip, unsigned int irq) { struct irq_data *d = irq_get_irq_data(irq); @@ -195,7 +195,7 @@ static const struct irq_domain_ops armada_370_xp_msi_irq_ops = { static int armada_370_xp_msi_init(struct device_node *node, phys_addr_t main_int_phys_base) { - struct msi_chip *msi_chip; + struct msi_controller *msi_chip; u32 reg; int ret; diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 8882b467be95..88471d3d98cd 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -236,7 +236,7 @@ EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources); static LIST_HEAD(of_pci_msi_chip_list); static DEFINE_MUTEX(of_pci_msi_chip_mutex); -int of_pci_msi_chip_add(struct msi_chip *chip) +int of_pci_msi_chip_add(struct msi_controller *chip) { if (!of_property_read_bool(chip->of_node, "msi-controller")) return -EINVAL; @@ -249,7 +249,7 @@ int of_pci_msi_chip_add(struct msi_chip *chip) } EXPORT_SYMBOL_GPL(of_pci_msi_chip_add); -void of_pci_msi_chip_remove(struct msi_chip *chip) +void of_pci_msi_chip_remove(struct msi_controller *chip) { mutex_lock(&of_pci_msi_chip_mutex); list_del(&chip->list); @@ -257,9 +257,9 @@ void of_pci_msi_chip_remove(struct msi_chip *chip) } EXPORT_SYMBOL_GPL(of_pci_msi_chip_remove); -struct msi_chip *of_pci_find_msi_chip_by_node(struct device_node *of_node) +struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node) { - struct msi_chip *c; + struct msi_controller *c; mutex_lock(&of_pci_msi_chip_mutex); list_for_each_entry(c, &of_pci_msi_chip_list, list) { diff --git a/drivers/pci/host/pci-keystone-dw.c b/drivers/pci/host/pci-keystone-dw.c index 34086ce88e8e..ac3d08c42770 100644 --- a/drivers/pci/host/pci-keystone-dw.c +++ b/drivers/pci/host/pci-keystone-dw.c @@ -205,7 +205,7 @@ const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = { .map = ks_dw_pcie_msi_map, }; -int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_chip *chip) +int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip) { struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); int i; diff --git a/drivers/pci/host/pci-keystone.h b/drivers/pci/host/pci-keystone.h index 1fc1fceede9e..478d932b602d 100644 --- a/drivers/pci/host/pci-keystone.h +++ b/drivers/pci/host/pci-keystone.h @@ -55,4 +55,4 @@ void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq); void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq); void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp); int ks_dw_pcie_msi_host_init(struct pcie_port *pp, - struct msi_chip *chip); + struct msi_controller *chip); diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index b1315e197ffb..cf907fec0652 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -99,7 +99,7 @@ struct mvebu_pcie_port; struct mvebu_pcie { struct platform_device *pdev; struct mvebu_pcie_port *ports; - struct msi_chip *msi; + struct msi_controller *msi; struct resource io; char io_name[30]; struct resource realio; diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 3d43874319be..5529de7328ba 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -238,7 +238,7 @@ ) struct tegra_msi { - struct msi_chip chip; + struct msi_controller chip; DECLARE_BITMAP(used, INT_PCI_MSI_NR); struct irq_domain *domain; unsigned long pages; @@ -259,7 +259,7 @@ struct tegra_pcie_soc_data { bool has_gen2; }; -static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip) +static inline struct tegra_msi *to_tegra_msi(struct msi_controller *chip) { return container_of(chip, struct tegra_msi, chip); } @@ -1283,8 +1283,8 @@ static irqreturn_t tegra_pcie_msi_irq(int irq, void *data) return processed > 0 ? IRQ_HANDLED : IRQ_NONE; } -static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, - struct msi_desc *desc) +static int tegra_msi_setup_irq(struct msi_controller *chip, + struct pci_dev *pdev, struct msi_desc *desc) { struct tegra_msi *msi = to_tegra_msi(chip); struct msi_msg msg; @@ -1313,7 +1313,8 @@ static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, return 0; } -static void tegra_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) +static void tegra_msi_teardown_irq(struct msi_controller *chip, + unsigned int irq) { struct tegra_msi *msi = to_tegra_msi(chip); struct irq_data *d = irq_get_irq_data(irq); diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index dfed00aa3ac0..f4decad80ee4 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -276,7 +276,7 @@ no_valid_irq: return -ENOSPC; } -static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, +static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, struct msi_desc *desc) { int irq, pos; @@ -303,7 +303,7 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, return 0; } -static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) +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(data); @@ -312,7 +312,7 @@ static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) clear_irq_range(pp, irq, 1, data->hwirq); } -static struct msi_chip dw_pcie_msi_chip = { +static struct msi_controller dw_pcie_msi_chip = { .setup_irq = dw_msi_setup_irq, .teardown_irq = dw_msi_teardown_irq, }; diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index c6256751daff..d0bbd276840d 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -73,7 +73,7 @@ struct pcie_host_ops { u32 (*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_chip *chip); + 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); diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index 61158e03ab5f..b986c51d4df3 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -111,14 +111,14 @@ struct rcar_msi { DECLARE_BITMAP(used, INT_PCI_MSI_NR); struct irq_domain *domain; - struct msi_chip chip; + struct msi_controller chip; unsigned long pages; struct mutex lock; int irq1; int irq2; }; -static inline struct rcar_msi *to_rcar_msi(struct msi_chip *chip) +static inline struct rcar_msi *to_rcar_msi(struct msi_controller *chip) { return container_of(chip, struct rcar_msi, chip); } @@ -622,7 +622,7 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data) return IRQ_HANDLED; } -static int rcar_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, +static int rcar_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, struct msi_desc *desc) { struct rcar_msi *msi = to_rcar_msi(chip); @@ -652,7 +652,7 @@ static int rcar_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, return 0; } -static void rcar_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) +static void rcar_msi_teardown_irq(struct msi_controller *chip, unsigned int irq) { struct rcar_msi *msi = to_rcar_msi(chip); struct irq_data *d = irq_get_irq_data(irq); diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index ccc496b33a97..f41bc601b7b0 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -335,7 +335,8 @@ static int xilinx_pcie_assign_msi(struct xilinx_pcie_port *port) * @chip: MSI Chip descriptor * @irq: MSI IRQ to destroy */ -static void xilinx_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) +static void xilinx_msi_teardown_irq(struct msi_controller *chip, + unsigned int irq) { xilinx_pcie_destroy_msi(irq); } @@ -348,7 +349,7 @@ static void xilinx_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) * * Return: '0' on success and error value on failure */ -static int xilinx_pcie_msi_setup_irq(struct msi_chip *chip, +static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, struct msi_desc *desc) { @@ -380,7 +381,7 @@ static int xilinx_pcie_msi_setup_irq(struct msi_chip *chip, } /* MSI Chip Descriptor */ -static struct msi_chip xilinx_pcie_msi_chip = { +static struct msi_controller xilinx_pcie_msi_chip = { .setup_irq = xilinx_pcie_msi_setup_irq, .teardown_irq = xilinx_msi_teardown_irq, }; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index d9a92cf6a6d6..0260f3933211 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -32,7 +32,7 @@ int pci_msi_ignore_mask; int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) { - struct msi_chip *chip = dev->bus->msi; + struct msi_controller *chip = dev->bus->msi; int err; if (!chip || !chip->setup_irq) @@ -49,7 +49,7 @@ int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) void __weak arch_teardown_msi_irq(unsigned int irq) { - struct msi_chip *chip = irq_get_chip_data(irq); + struct msi_controller *chip = irq_get_chip_data(irq); if (!chip || !chip->teardown_irq) return; diff --git a/include/linux/msi.h b/include/linux/msi.h index efad12742e45..6704991b0174 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -65,15 +65,15 @@ void arch_restore_msi_irqs(struct pci_dev *dev); void default_teardown_msi_irqs(struct pci_dev *dev); void default_restore_msi_irqs(struct pci_dev *dev); -struct msi_chip { +struct msi_controller { struct module *owner; struct device *dev; struct device_node *of_node; struct list_head list; - int (*setup_irq)(struct msi_chip *chip, struct pci_dev *dev, + int (*setup_irq)(struct msi_controller *chip, struct pci_dev *dev, struct msi_desc *desc); - void (*teardown_irq)(struct msi_chip *chip, unsigned int irq); + void (*teardown_irq)(struct msi_controller *chip, unsigned int irq); }; #endif /* LINUX_MSI_H */ diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index 1fd207e7a847..ce0e5abeb454 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -59,13 +59,13 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, #endif #if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI) -int of_pci_msi_chip_add(struct msi_chip *chip); -void of_pci_msi_chip_remove(struct msi_chip *chip); -struct msi_chip *of_pci_find_msi_chip_by_node(struct device_node *of_node); +int of_pci_msi_chip_add(struct msi_controller *chip); +void of_pci_msi_chip_remove(struct msi_controller *chip); +struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node); #else -static inline int of_pci_msi_chip_add(struct msi_chip *chip) { return -EINVAL; } -static inline void of_pci_msi_chip_remove(struct msi_chip *chip) { } -static inline struct msi_chip * +static inline int of_pci_msi_chip_add(struct msi_controller *chip) { return -EINVAL; } +static inline void of_pci_msi_chip_remove(struct msi_controller *chip) { } +static inline struct msi_controller * of_pci_find_msi_chip_by_node(struct device_node *of_node) { return NULL; } #endif diff --git a/include/linux/pci.h b/include/linux/pci.h index 5be8db45e368..90a5fdec6b9f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -449,7 +449,7 @@ struct pci_bus { struct resource busn_res; /* bus numbers routed to this bus */ struct pci_ops *ops; /* configuration access functions */ - struct msi_chip *msi; /* MSI controller */ + struct msi_controller *msi; /* MSI controller */ void *sysdata; /* hook for sys-specific extension */ struct proc_dir_entry *procdir; /* directory entry in /proc/bus/pci */ -- cgit v1.2.3 From 891d4a48f7da39de2be17a59b47df62dccf0f3d5 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sun, 9 Nov 2014 23:10:33 +0800 Subject: PCI/MSI: Rename __read_msi_msg() to __pci_read_msi_msg() Rename __read_msi_msg() to __pci_read_msi_msg() and kill unused read_msi_msg(). It's a preparation to separate generic MSI code from PCI core. Signed-off-by: Jiang Liu Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yingjoe Chen Cc: Yijing Wang Signed-off-by: Thomas Gleixner --- arch/powerpc/platforms/pseries/msi.c | 2 +- arch/x86/pci/xen.c | 2 +- drivers/pci/msi.c | 9 +-------- include/linux/msi.h | 5 +++-- 4 files changed, 6 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c index 8ab5add4ac82..90f756d0f58f 100644 --- a/arch/powerpc/platforms/pseries/msi.c +++ b/arch/powerpc/platforms/pseries/msi.c @@ -476,7 +476,7 @@ again: irq_set_msi_desc(virq, entry); /* Read config space back so we can restore after reset */ - __read_msi_msg(entry, &msg); + __pci_read_msi_msg(entry, &msg); entry->msg = msg; } diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index 466b978e13a5..ff0068b8ce5e 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -229,7 +229,7 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) return 1; list_for_each_entry(msidesc, &dev->msi_list, list) { - __read_msi_msg(msidesc, &msg); + __pci_read_msi_msg(msidesc, &msg); pirq = MSI_ADDR_EXT_DEST_ID(msg.address_hi) | ((msg.address_lo >> MSI_ADDR_DEST_ID_SHIFT) & 0xff); if (msg.data != XEN_PIRQ_MSI_DATA || diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 5cb471ec3e61..c77adc735f8a 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -252,7 +252,7 @@ void default_restore_msi_irqs(struct pci_dev *dev) default_restore_msi_irq(dev, entry->irq); } -void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { BUG_ON(entry->dev->current_state != PCI_D0); @@ -282,13 +282,6 @@ void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) } } -void read_msi_msg(unsigned int irq, struct msi_msg *msg) -{ - struct msi_desc *entry = irq_get_msi_desc(irq); - - __read_msi_msg(entry, msg); -} - void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { /* Assert that the cache is valid, assuming that diff --git a/include/linux/msi.h b/include/linux/msi.h index 6704991b0174..f36c37b46f10 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -16,12 +16,11 @@ struct irq_data; struct msi_desc; void mask_msi_irq(struct irq_data *data); void unmask_msi_irq(struct irq_data *data); -void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); -void read_msi_msg(unsigned int irq, struct msi_msg *msg); void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg); void write_msi_msg(unsigned int irq, struct msi_msg *msg); + u32 __msix_mask_irq(struct msi_desc *desc, u32 flag); u32 __msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); @@ -51,6 +50,8 @@ struct msi_desc { struct msi_msg msg; }; +void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); + /* * The arch hooks to setup up msi irqs. Those functions are * implemented as weak symbols so that they /can/ be overriden by -- cgit v1.2.3 From 83a18912b0e8d275001bca6fc9c0fe519d98f280 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sun, 9 Nov 2014 23:10:34 +0800 Subject: PCI/MSI: Rename write_msi_msg() to pci_write_msi_msg() Rename write_msi_msg() to pci_write_msi_msg() to mark it as PCI specific. Signed-off-by: Jiang Liu Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yingjoe Chen Cc: Yijing Wang Signed-off-by: Thomas Gleixner --- arch/arm/mach-iop13xx/msi.c | 2 +- arch/ia64/kernel/msi_ia64.c | 4 ++-- arch/ia64/sn/kernel/msi_sn.c | 4 ++-- arch/mips/pci/msi-octeon.c | 2 +- arch/mips/pci/msi-xlp.c | 4 ++-- arch/mips/pci/pci-xlr.c | 2 +- arch/powerpc/platforms/cell/axon_msi.c | 2 +- arch/powerpc/platforms/powernv/pci.c | 2 +- arch/powerpc/sysdev/fsl_msi.c | 2 +- arch/powerpc/sysdev/mpic_pasemi_msi.c | 2 +- arch/powerpc/sysdev/mpic_u3msi.c | 2 +- arch/powerpc/sysdev/ppc4xx_hsta_msi.c | 2 +- arch/powerpc/sysdev/ppc4xx_msi.c | 2 +- arch/s390/pci/pci.c | 2 +- arch/sparc/kernel/pci_msi.c | 2 +- arch/tile/kernel/pci_gx.c | 2 +- arch/x86/kernel/apic/io_apic.c | 4 ++-- arch/x86/pci/xen.c | 2 +- drivers/irqchip/irq-armada-370-xp.c | 2 +- drivers/pci/host/pci-tegra.c | 2 +- drivers/pci/host/pcie-designware.c | 2 +- drivers/pci/host/pcie-rcar.c | 2 +- drivers/pci/host/pcie-xilinx.c | 2 +- drivers/pci/msi.c | 10 +++++----- drivers/vfio/pci/vfio_pci_intrs.c | 2 +- include/linux/msi.h | 14 ++++++++++++-- 26 files changed, 45 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-iop13xx/msi.c b/arch/arm/mach-iop13xx/msi.c index e7730cf9c15d..3f7739cdb85b 100644 --- a/arch/arm/mach-iop13xx/msi.c +++ b/arch/arm/mach-iop13xx/msi.c @@ -153,7 +153,7 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) id = iop13xx_cpu_id(); msg.data = (id << IOP13XX_MU_MIMR_CORE_SELECT) | (irq & 0x7f); - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); irq_set_chip_and_handler(irq, &iop13xx_msi_chip, handle_simple_irq); return 0; diff --git a/arch/ia64/kernel/msi_ia64.c b/arch/ia64/kernel/msi_ia64.c index 8c3730c3c63d..35b10dd2bb76 100644 --- a/arch/ia64/kernel/msi_ia64.c +++ b/arch/ia64/kernel/msi_ia64.c @@ -35,7 +35,7 @@ static int ia64_set_msi_irq_affinity(struct irq_data *idata, data |= MSI_DATA_VECTOR(irq_to_vector(irq)); msg.data = data; - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); cpumask_copy(idata->affinity, cpumask_of(cpu)); return 0; @@ -71,7 +71,7 @@ int ia64_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) MSI_DATA_DELIVERY_FIXED | MSI_DATA_VECTOR(vector); - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); irq_set_chip_and_handler(irq, &ia64_msi_chip, handle_edge_irq); return 0; diff --git a/arch/ia64/sn/kernel/msi_sn.c b/arch/ia64/sn/kernel/msi_sn.c index 446e7799928c..433cafed5c3a 100644 --- a/arch/ia64/sn/kernel/msi_sn.c +++ b/arch/ia64/sn/kernel/msi_sn.c @@ -145,7 +145,7 @@ int sn_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *entry) msg.data = 0x100 + irq; irq_set_msi_desc(irq, entry); - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); irq_set_chip_and_handler(irq, &sn_msi_chip, handle_edge_irq); return 0; @@ -205,7 +205,7 @@ static int sn_set_msi_irq_affinity(struct irq_data *data, msg.address_hi = (u32)(bus_addr >> 32); msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff); - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); cpumask_copy(data->affinity, cpu_mask); return 0; diff --git a/arch/mips/pci/msi-octeon.c b/arch/mips/pci/msi-octeon.c index 63bbe07a1ccd..cffaaf4aae3c 100644 --- a/arch/mips/pci/msi-octeon.c +++ b/arch/mips/pci/msi-octeon.c @@ -178,7 +178,7 @@ msi_irq_allocated: pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); irq_set_msi_desc(irq, desc); - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); return 0; } diff --git a/arch/mips/pci/msi-xlp.c b/arch/mips/pci/msi-xlp.c index fa374fe3746b..81ea2b40db61 100644 --- a/arch/mips/pci/msi-xlp.c +++ b/arch/mips/pci/msi-xlp.c @@ -345,7 +345,7 @@ static int xlp_setup_msi(uint64_t lnkbase, int node, int link, if (ret < 0) return ret; - write_msi_msg(xirq, &msg); + pci_write_msi_msg(xirq, &msg); return 0; } @@ -448,7 +448,7 @@ static int xlp_setup_msix(uint64_t lnkbase, int node, int link, return ret; } - write_msi_msg(xirq, &msg); + pci_write_msi_msg(xirq, &msg); return 0; } diff --git a/arch/mips/pci/pci-xlr.c b/arch/mips/pci/pci-xlr.c index 0dde80332d3a..26d2dabef281 100644 --- a/arch/mips/pci/pci-xlr.c +++ b/arch/mips/pci/pci-xlr.c @@ -260,7 +260,7 @@ int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) if (ret < 0) return ret; - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); return 0; } #endif diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c index 862b32702d29..26fa3432352e 100644 --- a/arch/powerpc/platforms/cell/axon_msi.c +++ b/arch/powerpc/platforms/cell/axon_msi.c @@ -279,7 +279,7 @@ static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) irq_set_msi_desc(virq, entry); msg.data = virq; - write_msi_msg(virq, &msg); + pci_write_msi_msg(virq, &msg); } return 0; diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index b3ca77ddf36d..e79a38a1df9f 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -91,7 +91,7 @@ static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) return rc; } irq_set_msi_desc(virq, entry); - write_msi_msg(virq, &msg); + pci_write_msi_msg(virq, &msg); } return 0; } diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index de40b48b460e..bd5a9f7c0029 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -242,7 +242,7 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) irq_set_msi_desc(virq, entry); fsl_compose_msi_msg(pdev, hwirq, &msg, msi_data); - write_msi_msg(virq, &msg); + pci_write_msi_msg(virq, &msg); } return 0; diff --git a/arch/powerpc/sysdev/mpic_pasemi_msi.c b/arch/powerpc/sysdev/mpic_pasemi_msi.c index 15dccd35fa11..45a6f407883c 100644 --- a/arch/powerpc/sysdev/mpic_pasemi_msi.c +++ b/arch/powerpc/sysdev/mpic_pasemi_msi.c @@ -136,7 +136,7 @@ static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) * register to generate MSI [512...1023] */ msg.data = hwirq-0x200; - write_msi_msg(virq, &msg); + pci_write_msi_msg(virq, &msg); } return 0; diff --git a/arch/powerpc/sysdev/mpic_u3msi.c b/arch/powerpc/sysdev/mpic_u3msi.c index 623d7fba15b4..ac6f1f9d03ce 100644 --- a/arch/powerpc/sysdev/mpic_u3msi.c +++ b/arch/powerpc/sysdev/mpic_u3msi.c @@ -171,7 +171,7 @@ static int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) printk("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n", virq, hwirq, (unsigned long)addr); msg.data = hwirq; - write_msi_msg(virq, &msg); + pci_write_msi_msg(virq, &msg); hwirq++; } diff --git a/arch/powerpc/sysdev/ppc4xx_hsta_msi.c b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c index a6a4dbda9078..908105f835d1 100644 --- a/arch/powerpc/sysdev/ppc4xx_hsta_msi.c +++ b/arch/powerpc/sysdev/ppc4xx_hsta_msi.c @@ -85,7 +85,7 @@ static int hsta_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) msi_bitmap_free_hwirqs(&ppc4xx_hsta_msi.bmp, irq, 1); return -EINVAL; } - write_msi_msg(hwirq, &msg); + pci_write_msi_msg(hwirq, &msg); } return 0; diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c index 22b5200636e7..518eabbe0bdc 100644 --- a/arch/powerpc/sysdev/ppc4xx_msi.c +++ b/arch/powerpc/sysdev/ppc4xx_msi.c @@ -116,7 +116,7 @@ static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) irq_set_msi_desc(virq, entry); msg.data = int_no; - write_msi_msg(virq, &msg); + pci_write_msi_msg(virq, &msg); } return 0; } diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 552b9908aa77..ad1a9e2a1831 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -403,7 +403,7 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) msg.data = hwirq; msg.address_lo = zdev->msi_addr & 0xffffffff; msg.address_hi = zdev->msi_addr >> 32; - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); airq_iv_set_data(zdev->aibv, hwirq, irq); hwirq++; } diff --git a/arch/sparc/kernel/pci_msi.c b/arch/sparc/kernel/pci_msi.c index 580651af73f2..73db0ed80d83 100644 --- a/arch/sparc/kernel/pci_msi.c +++ b/arch/sparc/kernel/pci_msi.c @@ -161,7 +161,7 @@ static int sparc64_setup_msi_irq(unsigned int *irq_p, msg.data = msi; irq_set_msi_desc(*irq_p, entry); - write_msi_msg(*irq_p, &msg); + pci_write_msi_msg(*irq_p, &msg); return 0; diff --git a/arch/tile/kernel/pci_gx.c b/arch/tile/kernel/pci_gx.c index e39f9c542807..376d9f1d9951 100644 --- a/arch/tile/kernel/pci_gx.c +++ b/arch/tile/kernel/pci_gx.c @@ -1590,7 +1590,7 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) msg.address_hi = msi_addr >> 32; msg.address_lo = msi_addr & 0xffffffff; - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); irq_set_chip_and_handler(irq, &tilegx_msi_chip, handle_level_irq); irq_set_handler_data(irq, controller); diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 1183d545da1e..d775aef42b87 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -3158,7 +3158,7 @@ msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; msg.address_lo |= MSI_ADDR_DEST_ID(dest); - __write_msi_msg(data->msi_desc, &msg); + __pci_write_msi_msg(data->msi_desc, &msg); return IRQ_SET_MASK_OK_NOCOPY; } @@ -3196,7 +3196,7 @@ int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, * MSI message denotes a contiguous group of IRQs, written for 0th IRQ. */ if (!irq_offset) - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); setup_remapped_irq(irq, irq_cfg(irq), chip); diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index ff0068b8ce5e..1819a91bbb9f 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -240,7 +240,7 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) goto error; } xen_msi_compose_msg(dev, pirq, &msg); - __write_msi_msg(msidesc, &msg); + __pci_write_msi_msg(msidesc, &msg); dev_dbg(&dev->dev, "xen: msi bound to pirq=%d\n", pirq); } else { dev_dbg(&dev->dev, diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 4e202375e027..8c7f077abb09 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -156,7 +156,7 @@ static int armada_370_xp_setup_msi_irq(struct msi_controller *chip, msg.address_hi = 0; msg.data = 0xf00 | (hwirq + 16); - write_msi_msg(virq, &msg); + pci_write_msi_msg(virq, &msg); return 0; } diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 1f01faa918cc..31841718bedd 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -1299,7 +1299,7 @@ static int tegra_msi_setup_irq(struct msi_controller *chip, msg.address_hi = 0; msg.data = hwirq; - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); return 0; } diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 5278d3e63070..e03f4e7e1f7d 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -298,7 +298,7 @@ static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, else msg.data = pos; - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); return 0; } diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index c2e3fcb55fd4..c1177cd55ce3 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -640,7 +640,7 @@ static int rcar_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, msg.address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR); msg.data = hwirq; - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); return 0; } diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index eca292347c69..67683380f0f7 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -375,7 +375,7 @@ static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip, msg.address_lo = msg_addr; msg.data = irq; - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); return 0; } diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index c77adc735f8a..156ba8f809b7 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -130,7 +130,7 @@ static void default_restore_msi_irq(struct pci_dev *dev, int irq) } if (entry) - __write_msi_msg(entry, &entry->msg); + __pci_write_msi_msg(entry, &entry->msg); } void __weak arch_restore_msi_irqs(struct pci_dev *dev) @@ -300,7 +300,7 @@ void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg) } EXPORT_SYMBOL_GPL(get_cached_msi_msg); -void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { if (entry->dev->current_state != PCI_D0) { /* Don't touch the hardware now */ @@ -337,13 +337,13 @@ void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) entry->msg = *msg; } -void write_msi_msg(unsigned int irq, struct msi_msg *msg) +void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg) { struct msi_desc *entry = irq_get_msi_desc(irq); - __write_msi_msg(entry, msg); + __pci_write_msi_msg(entry, msg); } -EXPORT_SYMBOL_GPL(write_msi_msg); +EXPORT_SYMBOL_GPL(pci_write_msi_msg); static void free_msi_irqs(struct pci_dev *dev) { diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index 553212f037c3..e8d695b3f54e 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -560,7 +560,7 @@ static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev, struct msi_msg msg; get_cached_msi_msg(irq, &msg); - write_msi_msg(irq, &msg); + pci_write_msi_msg(irq, &msg); } ret = request_irq(irq, vfio_msihandler, 0, diff --git a/include/linux/msi.h b/include/linux/msi.h index f36c37b46f10..301adecbc34d 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -17,9 +17,7 @@ struct msi_desc; void mask_msi_irq(struct irq_data *data); void unmask_msi_irq(struct irq_data *data); void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg); -void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg); -void write_msi_msg(unsigned int irq, struct msi_msg *msg); u32 __msix_mask_irq(struct msi_desc *desc, u32 flag); u32 __msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); @@ -51,6 +49,18 @@ struct msi_desc { }; void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); +void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); +void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg); + +/* Conversion helpers. Should be removed after merging */ +static inline void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + __pci_write_msi_msg(entry, msg); +} +static inline void write_msi_msg(int irq, struct msi_msg *msg) +{ + pci_write_msi_msg(irq, msg); +} /* * The arch hooks to setup up msi irqs. Those functions are -- cgit v1.2.3 From 23ed8d57f3b87520e045ba0e3a2340638b31198a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Nov 2014 11:55:58 +0100 Subject: PCI/MSI: Rename mask/unmask_msi_irq et al mask/unmask_msi_irq and __mask_msi/msix_irq are PCI/MSI specific functions and should be named accordingly. This is a preparatory patch to support MSI on non PCI devices. Rename mask/unmask_msi_irq to pci_msi_mask/unmask_irq and document the functions. Provide conversion helpers. Rename __mask_msi/msix_irq to __pci_msi/msix_desc_mask so its clear that they operated on msi_desc. Fixup the only user outside of pci/msi. Signed-off-by: Thomas Gleixner Cc: Bjorn Helgaas Cc: Jiang Liu Cc: Grant Likely Cc: Marc Zyngier Cc: Yijing Wang Cc: Heiko Carstens --- arch/s390/pci/pci.c | 4 ++-- drivers/pci/msi.c | 24 ++++++++++++++++-------- include/linux/msi.h | 18 +++++++++++++----- 3 files changed, 31 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index ad1a9e2a1831..fb54bbdbb48d 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -448,9 +448,9 @@ void arch_teardown_msi_irqs(struct pci_dev *pdev) /* Release MSI interrupts */ list_for_each_entry(msi, &pdev->msi_list, list) { if (msi->msi_attrib.is_msix) - __msix_mask_irq(msi, 1); + __pci_msix_desc_mask_irq(msi, 1); else - __msi_mask_irq(msi, 1, 1); + __pci_msi_desc_mask_irq(msi, 1, 1); irq_set_msi_desc(msi->irq, NULL); irq_free_desc(msi->irq); msi->msg.address_lo = 0; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 156ba8f809b7..b5bf2f641770 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -173,7 +173,7 @@ static inline __attribute_const__ u32 msi_mask(unsigned x) * reliably as devices without an INTx disable bit will then generate a * level IRQ which will never be cleared. */ -u32 __msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) +u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) { u32 mask_bits = desc->masked; @@ -189,7 +189,7 @@ u32 __msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) { - desc->masked = __msi_mask_irq(desc, mask, flag); + desc->masked = __pci_msi_desc_mask_irq(desc, mask, flag); } /* @@ -199,7 +199,7 @@ static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) * file. This saves a few milliseconds when initialising devices with lots * of MSI-X interrupts. */ -u32 __msix_mask_irq(struct msi_desc *desc, u32 flag) +u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag) { u32 mask_bits = desc->masked; unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + @@ -218,7 +218,7 @@ u32 __msix_mask_irq(struct msi_desc *desc, u32 flag) static void msix_mask_irq(struct msi_desc *desc, u32 flag) { - desc->masked = __msix_mask_irq(desc, flag); + desc->masked = __pci_msix_desc_mask_irq(desc, flag); } static void msi_set_mask_bit(struct irq_data *data, u32 flag) @@ -234,12 +234,20 @@ static void msi_set_mask_bit(struct irq_data *data, u32 flag) } } -void mask_msi_irq(struct irq_data *data) +/** + * pci_msi_mask_irq - Generic irq chip callback to mask PCI/MSI interrupts + * @data: pointer to irqdata associated to that interrupt + */ +void pci_msi_mask_irq(struct irq_data *data) { msi_set_mask_bit(data, 1); } -void unmask_msi_irq(struct irq_data *data) +/** + * pci_msi_unmask_irq - Generic irq chip callback to unmask PCI/MSI interrupts + * @data: pointer to irqdata associated to that interrupt + */ +void pci_msi_unmask_irq(struct irq_data *data) { msi_set_mask_bit(data, 0); } @@ -858,7 +866,7 @@ void pci_msi_shutdown(struct pci_dev *dev) /* Return the device with MSI unmasked as initial states */ mask = msi_mask(desc->msi_attrib.multi_cap); /* Keep cached state to be restored */ - __msi_mask_irq(desc, mask, ~mask); + __pci_msi_desc_mask_irq(desc, mask, ~mask); /* Restore dev->irq to its default pin-assertion irq */ dev->irq = desc->msi_attrib.default_irq; @@ -956,7 +964,7 @@ void pci_msix_shutdown(struct pci_dev *dev) /* Return the device with MSI-X masked as initial states */ list_for_each_entry(entry, &dev->msi_list, list) { /* Keep cached states to be restored */ - __msix_mask_irq(entry, 1); + __pci_msix_desc_mask_irq(entry, 1); } msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); diff --git a/include/linux/msi.h b/include/linux/msi.h index 301adecbc34d..1dcad0c1e806 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -14,14 +14,9 @@ extern int pci_msi_ignore_mask; /* Helper functions */ struct irq_data; struct msi_desc; -void mask_msi_irq(struct irq_data *data); -void unmask_msi_irq(struct irq_data *data); void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg); -u32 __msix_mask_irq(struct msi_desc *desc, u32 flag); -u32 __msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); - struct msi_desc { struct { __u8 is_msix : 1; @@ -52,6 +47,11 @@ void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg); +u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag); +u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); +void pci_msi_mask_irq(struct irq_data *data); +void pci_msi_unmask_irq(struct irq_data *data); + /* Conversion helpers. Should be removed after merging */ static inline void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { @@ -61,6 +61,14 @@ static inline void write_msi_msg(int irq, struct msi_msg *msg) { pci_write_msi_msg(irq, msg); } +static inline void mask_msi_irq(struct irq_data *data) +{ + pci_msi_mask_irq(data); +} +static inline void unmask_msi_irq(struct irq_data *data) +{ + pci_msi_unmask_irq(data); +} /* * The arch hooks to setup up msi irqs. Those functions are -- cgit v1.2.3 From d31eb342409b24e3d2e1989c775f3361e93acc08 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 15 Nov 2014 22:24:03 +0800 Subject: PCI/MSI: Introduce helpers to hide struct msi_desc implementation details Introduce helpers to hide struct msi_desc implementation details, so we could easily support non-PCI-compliant MSI devices later by moving msi_list into struct device. Signed-off-by: Jiang Liu Cc: Tony Luck Cc: linux-arm-kernel@lists.infradead.org Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yijing Wang Cc: Yingjoe Chen Cc: Borislav Petkov Cc: Matthias Brugger Cc: Alexander Gordeev Link: http://lkml.kernel.org/r/1416061447-9472-6-git-send-email-jiang.liu@linux.intel.com Signed-off-by: Thomas Gleixner --- include/linux/msi.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'include') diff --git a/include/linux/msi.h b/include/linux/msi.h index 1dcad0c1e806..e0d08517bd3d 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -43,6 +43,25 @@ struct msi_desc { struct msi_msg msg; }; +/* Helpers to hide struct msi_desc implementation details */ +#define msi_desc_to_dev(desc) (&(desc)->dev.dev) +#define dev_to_msi_list(dev) (&to_pci_dev((dev))->msi_list) +#define first_msi_entry(dev) \ + list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list) +#define for_each_msi_entry(desc, dev) \ + list_for_each_entry((desc), dev_to_msi_list((dev)), list) + +#ifdef CONFIG_PCI_MSI +#define first_pci_msi_entry(pdev) first_msi_entry(&(pdev)->dev) +#define for_each_pci_msi_entry(desc, pdev) \ + for_each_msi_entry((desc), &(pdev)->dev) + +static inline struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc) +{ + return desc->dev; +} +#endif /* CONFIG_PCI_MSI */ + void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg); -- cgit v1.2.3 From f8264e34965aaf43203912ed8f7b543c00c8d70f Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Thu, 6 Nov 2014 22:20:14 +0800 Subject: irqdomain: Introduce new interfaces to support hierarchy irqdomains We plan to use hierarchy irqdomain to suppport CPU vector assignment, interrupt remapping controller, IO-APIC controller, MSI interrupt and hypertransport interrupt etc on x86 platforms. So extend irqdomain interfaces to support hierarchy irqdomain. There are already many clients of current irqdomain interfaces. To minimize the changes, we choose to introduce new version 2 interfaces to support hierarchy instead of extending existing irqdomain interfaces. According to Thomas's suggestion, the most important design decision is to build hierarchy struct irq_data to support hierarchy irqdomain, so hierarchy irqdomain related data could be saved in struct irq_data. With support of hierarchy irq_data, we could also support stacked irq_chips. This is most useful in case of set_affinity(). The new hierarchy irqdomain introduces following interfaces: 1) irq_domain_alloc_irqs()/irq_domain_free_irqs(): allocate/release IRQ and related resources. 2) __irq_domain_alloc_irqs(): a special version to support legacy IRQs. 3) irq_domain_activate_irq()/irq_domain_deactivate_irq(): program interrupt controllers to activate/deactivate interrupt. There are also several help functions to ease irqdomain implemenations: 1) irq_domain_get_irq_data(): get irq_data associated with a specific irqdomain. 2) irq_domain_set_hwirq_and_chip(): save irqdomain specific data into irq_data. 3) irq_domain_alloc_irqs_parent()/irq_domain_free_irqs_parent(): invoke parent irqdomain's alloc/free callbacks. We also changed irq_startup()/irq_shutdown() to invoke irq_domain_activate_irq()/irq_domain_deactivate_irq() to program interrupt controller when start/stop interrupts. [ tglx: Folded parts of the later patch series in ] Signed-off-by: Jiang Liu Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yingjoe Chen Cc: Yijing Wang Signed-off-by: Thomas Gleixner --- Documentation/IRQ-domain.txt | 71 ++++++++ include/linux/irq.h | 5 + include/linux/irqdomain.h | 98 ++++++++++ kernel/irq/Kconfig | 5 + kernel/irq/chip.c | 3 + kernel/irq/irqdomain.c | 415 +++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 581 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/Documentation/IRQ-domain.txt b/Documentation/IRQ-domain.txt index 8a8b82c9ca53..39cfa72732ff 100644 --- a/Documentation/IRQ-domain.txt +++ b/Documentation/IRQ-domain.txt @@ -151,3 +151,74 @@ used and no descriptor gets allocated it is very important to make sure that the driver using the simple domain call irq_create_mapping() before any irq_find_mapping() since the latter will actually work for the static IRQ assignment case. + +==== Hierarchy IRQ domain ==== +On some architectures, there may be multiple interrupt controllers +involved in delivering an interrupt from the device to the target CPU. +Let's look at a typical interrupt delivering path on x86 platforms: + +Device --> IOAPIC -> Interrupt remapping Controller -> Local APIC -> CPU + +There are three interrupt controllers involved: +1) IOAPIC controller +2) Interrupt remapping controller +3) Local APIC controller + +To support such a hardware topology and make software architecture match +hardware architecture, an irq_domain data structure is built for each +interrupt controller and those irq_domains are organized into hierarchy. +When building irq_domain hierarchy, the irq_domain near to the device is +child and the irq_domain near to CPU is parent. So a hierarchy structure +as below will be built for the example above. + CPU Vector irq_domain (root irq_domain to manage CPU vectors) + ^ + | + Interrupt Remapping irq_domain (manage irq_remapping entries) + ^ + | + IOAPIC irq_domain (manage IOAPIC delivery entries/pins) + +There are four major interfaces to use hierarchy irq_domain: +1) irq_domain_alloc_irqs(): allocate IRQ descriptors and interrupt + controller related resources to deliver these interrupts. +2) irq_domain_free_irqs(): free IRQ descriptors and interrupt controller + related resources associated with these interrupts. +3) irq_domain_activate_irq(): activate interrupt controller hardware to + deliver the interrupt. +3) irq_domain_deactivate_irq(): deactivate interrupt controller hardware + to stop delivering the interrupt. + +Following changes are needed to support hierarchy irq_domain. +1) a new field 'parent' is added to struct irq_domain; it's used to + maintain irq_domain hierarchy information. +2) a new field 'parent_data' is added to struct irq_data; it's used to + build hierarchy irq_data to match hierarchy irq_domains. The irq_data + is used to store irq_domain pointer and hardware irq number. +3) new callbacks are added to struct irq_domain_ops to support hierarchy + irq_domain operations. + +With support of hierarchy irq_domain and hierarchy irq_data ready, an +irq_domain structure is built for each interrupt controller, and an +irq_data structure is allocated for each irq_domain associated with an +IRQ. Now we could go one step further to support stacked(hierarchy) +irq_chip. That is, an irq_chip is associated with each irq_data along +the hierarchy. A child irq_chip may implement a required action by +itself or by cooperating with its parent irq_chip. + +With stacked irq_chip, interrupt controller driver only needs to deal +with the hardware managed by itself and may ask for services from its +parent irq_chip when needed. So we could achieve a much cleaner +software architecture. + +For an interrupt controller driver to support hierarchy irq_domain, it +needs to: +1) Implement irq_domain_ops.alloc and irq_domain_ops.free +2) Optionally implement irq_domain_ops.activate and + irq_domain_ops.deactivate. +3) Optionally implement an irq_chip to manage the interrupt controller + hardware. +4) No need to implement irq_domain_ops.map and irq_domain_ops.unmap, + they are unused with hierarchy irq_domain. + +Hierarchy irq_domain may also be used to support other architectures, +such as ARM, ARM64 etc. diff --git a/include/linux/irq.h b/include/linux/irq.h index 03f48d936f66..13ba412ce3a0 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -133,6 +133,8 @@ struct irq_domain; * @chip: low level interrupt hardware access * @domain: Interrupt translation domain; responsible for mapping * between hwirq number and linux irq number. + * @parent_data: pointer to parent struct irq_data to support hierarchy + * irq_domain * @handler_data: per-IRQ data for the irq_chip methods * @chip_data: platform-specific per-chip private data for the chip * methods, to allow shared chip implementations @@ -151,6 +153,9 @@ struct irq_data { unsigned int state_use_accessors; struct irq_chip *chip; struct irq_domain *domain; +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + struct irq_data *parent_data; +#endif void *handler_data; void *chip_data; struct msi_desc *msi_desc; diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index b0f9d16e48f6..f8563dcfd254 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -38,6 +38,8 @@ struct device_node; struct irq_domain; struct of_device_id; +struct irq_chip; +struct irq_data; /* Number of irqs reserved for a legacy isa controller */ #define NUM_ISA_INTERRUPTS 16 @@ -64,6 +66,16 @@ struct irq_domain_ops { int (*xlate)(struct irq_domain *d, struct device_node *node, const u32 *intspec, unsigned int intsize, unsigned long *out_hwirq, unsigned int *out_type); + +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + /* extended V2 interfaces to support hierarchy irq_domains */ + int (*alloc)(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs, void *arg); + void (*free)(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs); + void (*activate)(struct irq_domain *d, struct irq_data *irq_data); + void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data); +#endif }; extern struct irq_domain_ops irq_generic_chip_ops; @@ -77,6 +89,7 @@ struct irq_domain_chip_generic; * @ops: pointer to irq_domain methods * @host_data: private data pointer for use by owner. Not touched by irq_domain * core code. + * @flags: host per irq_domain flags * * Optional elements * @of_node: Pointer to device tree nodes associated with the irq_domain. Used @@ -84,6 +97,7 @@ struct irq_domain_chip_generic; * @gc: Pointer to a list of generic chips. There is a helper function for * setting up one or more generic chips for interrupt controllers * drivers using the generic chip library which uses this pointer. + * @parent: Pointer to parent irq_domain to support hierarchy irq_domains * * Revmap data, used internally by irq_domain * @revmap_direct_max_irq: The largest hwirq that can be set for controllers that @@ -97,10 +111,14 @@ struct irq_domain { const char *name; const struct irq_domain_ops *ops; void *host_data; + unsigned int flags; /* Optional data */ struct device_node *of_node; struct irq_domain_chip_generic *gc; +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + struct irq_domain *parent; +#endif /* reverse map data. The linear map gets appended to the irq_domain */ irq_hw_number_t hwirq_max; @@ -110,6 +128,19 @@ struct irq_domain { unsigned int linear_revmap[]; }; +/* Irq domain flags */ +enum { + /* Irq domain is hierarchical */ + IRQ_DOMAIN_FLAG_HIERARCHY = (1 << 0), + + /* + * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved + * for implementation specific purposes and ignored by the + * core code. + */ + IRQ_DOMAIN_FLAG_NONCORE = (1 << 16), +}; + #ifdef CONFIG_IRQ_DOMAIN struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, irq_hw_number_t hwirq_max, int direct_max, @@ -220,8 +251,75 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, irq_hw_number_t *out_hwirq, unsigned int *out_type); +/* V2 interfaces to support hierarchy IRQ domains. */ +extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, + unsigned int virq); +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY +extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, + unsigned int nr_irqs, int node, void *arg, + bool realloc); +extern void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs); +extern void irq_domain_activate_irq(struct irq_data *irq_data); +extern void irq_domain_deactivate_irq(struct irq_data *irq_data); + +static inline int irq_domain_alloc_irqs(struct irq_domain *domain, + unsigned int nr_irqs, int node, void *arg) +{ + return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false); +} + +extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, + unsigned int virq, + irq_hw_number_t hwirq, + struct irq_chip *chip, + void *chip_data); +extern void irq_domain_reset_irq_data(struct irq_data *irq_data); +extern void irq_domain_free_irqs_common(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs); +extern void irq_domain_free_irqs_top(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs); + +static inline int irq_domain_alloc_irqs_parent(struct irq_domain *domain, + unsigned int irq_base, + unsigned int nr_irqs, void *arg) +{ + if (domain->parent && domain->parent->ops->alloc) + return domain->parent->ops->alloc(domain->parent, irq_base, + nr_irqs, arg); + return -ENOSYS; +} + +static inline void irq_domain_free_irqs_parent(struct irq_domain *domain, + unsigned int irq_base, unsigned int nr_irqs) +{ + if (domain->parent && domain->parent->ops->free) + domain->parent->ops->free(domain->parent, irq_base, nr_irqs); +} + +static inline bool irq_domain_is_hierarchy(struct irq_domain *domain) +{ + return domain->flags & IRQ_DOMAIN_FLAG_HIERARCHY; +} +#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */ +static inline void irq_domain_activate_irq(struct irq_data *data) { } +static inline void irq_domain_deactivate_irq(struct irq_data *data) { } +static inline int irq_domain_alloc_irqs(struct irq_domain *domain, + unsigned int nr_irqs, int node, void *arg) +{ + return -1; +} + +static inline bool irq_domain_is_hierarchy(struct irq_domain *domain) +{ + return false; +} +#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ + #else /* CONFIG_IRQ_DOMAIN */ static inline void irq_dispose_mapping(unsigned int virq) { } +static inline void irq_domain_activate_irq(struct irq_data *data) { } +static inline void irq_domain_deactivate_irq(struct irq_data *data) { } #endif /* !CONFIG_IRQ_DOMAIN */ #endif /* _LINUX_IRQDOMAIN_H */ diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 225086b2652e..4f2eb2b1f23b 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -55,6 +55,11 @@ config GENERIC_IRQ_CHIP config IRQ_DOMAIN bool +# Support for hierarchical irq domains +config IRQ_DOMAIN_HIERARCHY + bool + select IRQ_DOMAIN + config HANDLE_DOMAIN_IRQ bool diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index e5202f00cabc..72a93086216b 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -178,6 +179,7 @@ int irq_startup(struct irq_desc *desc, bool resend) irq_state_clr_disabled(desc); desc->depth = 0; + irq_domain_activate_irq(&desc->irq_data); if (desc->irq_data.chip->irq_startup) { ret = desc->irq_data.chip->irq_startup(&desc->irq_data); irq_state_clr_masked(desc); @@ -199,6 +201,7 @@ void irq_shutdown(struct irq_desc *desc) desc->irq_data.chip->irq_disable(&desc->irq_data); else desc->irq_data.chip->irq_mask(&desc->irq_data); + irq_domain_deactivate_irq(&desc->irq_data); irq_state_set_masked(desc); } diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 6534ff6ce02e..43f3be6fac70 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -23,6 +23,10 @@ static DEFINE_MUTEX(irq_domain_mutex); static DEFINE_MUTEX(revmap_trees_mutex); static struct irq_domain *irq_default_domain; +static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs, + irq_hw_number_t hwirq, int node); +static void irq_domain_check_hierarchy(struct irq_domain *domain); + /** * __irq_domain_add() - Allocate a new irq_domain data structure * @of_node: optional device-tree node of the interrupt controller @@ -30,7 +34,7 @@ static struct irq_domain *irq_default_domain; * @hwirq_max: Maximum number of interrupts supported by controller * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no * direct mapping - * @ops: map/unmap domain callbacks + * @ops: domain callbacks * @host_data: Controller private data pointer * * Allocates and initialize and irq_domain structure. @@ -56,6 +60,7 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, domain->hwirq_max = hwirq_max; domain->revmap_size = size; domain->revmap_direct_max_irq = direct_max; + irq_domain_check_hierarchy(domain); mutex_lock(&irq_domain_mutex); list_add(&domain->link, &irq_domain_list); @@ -109,7 +114,7 @@ EXPORT_SYMBOL_GPL(irq_domain_remove); * @first_irq: first number of irq block assigned to the domain, * pass zero to assign irqs on-the-fly. If first_irq is non-zero, then * pre-map all of the irqs in the domain to virqs starting at first_irq. - * @ops: map/unmap domain callbacks + * @ops: domain callbacks * @host_data: Controller private data pointer * * Allocates an irq_domain, and optionally if first_irq is positive then also @@ -174,10 +179,8 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, domain = __irq_domain_add(of_node, first_hwirq + size, first_hwirq + size, 0, ops, host_data); - if (!domain) - return NULL; - - irq_domain_associate_many(domain, first_irq, first_hwirq, size); + if (domain) + irq_domain_associate_many(domain, first_irq, first_hwirq, size); return domain; } @@ -388,7 +391,6 @@ EXPORT_SYMBOL_GPL(irq_create_direct_mapping); unsigned int irq_create_mapping(struct irq_domain *domain, irq_hw_number_t hwirq) { - unsigned int hint; int virq; pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq); @@ -410,12 +412,8 @@ unsigned int irq_create_mapping(struct irq_domain *domain, } /* Allocate a virtual interrupt number */ - hint = hwirq % nr_irqs; - if (hint == 0) - hint++; - virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node)); - if (virq <= 0) - virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node)); + virq = irq_domain_alloc_descs(-1, 1, hwirq, + of_node_to_nid(domain->of_node)); if (virq <= 0) { pr_debug("-> virq allocation failed\n"); return 0; @@ -471,7 +469,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) struct irq_domain *domain; irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; - unsigned int virq; + int virq; domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain; if (!domain) { @@ -480,6 +478,11 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) return 0; } + if (irq_domain_is_hierarchy(domain)) { + virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data); + return virq <= 0 ? 0 : virq; + } + /* If domain has no translation, then we assume interrupt line */ if (domain->ops->xlate == NULL) hwirq = irq_data->args[0]; @@ -540,8 +543,8 @@ unsigned int irq_find_mapping(struct irq_domain *domain, return 0; if (hwirq < domain->revmap_direct_max_irq) { - data = irq_get_irq_data(hwirq); - if (data && (data->domain == domain) && (data->hwirq == hwirq)) + data = irq_domain_get_irq_data(domain, hwirq); + if (data && data->hwirq == hwirq) return hwirq; } @@ -709,3 +712,383 @@ const struct irq_domain_ops irq_domain_simple_ops = { .xlate = irq_domain_xlate_onetwocell, }; EXPORT_SYMBOL_GPL(irq_domain_simple_ops); + +static int irq_domain_alloc_descs(int virq, unsigned int cnt, + irq_hw_number_t hwirq, int node) +{ + unsigned int hint; + + if (virq >= 0) { + virq = irq_alloc_descs(virq, virq, cnt, node); + } else { + hint = hwirq % nr_irqs; + if (hint == 0) + hint++; + virq = irq_alloc_descs_from(hint, cnt, node); + if (virq <= 0 && hint > 1) + virq = irq_alloc_descs_from(1, cnt, node); + } + + return virq; +} + +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY +static void irq_domain_insert_irq(int virq) +{ + struct irq_data *data; + + for (data = irq_get_irq_data(virq); data; data = data->parent_data) { + struct irq_domain *domain = data->domain; + irq_hw_number_t hwirq = data->hwirq; + + if (hwirq < domain->revmap_size) { + domain->linear_revmap[hwirq] = virq; + } else { + mutex_lock(&revmap_trees_mutex); + radix_tree_insert(&domain->revmap_tree, hwirq, data); + mutex_unlock(&revmap_trees_mutex); + } + + /* If not already assigned, give the domain the chip's name */ + if (!domain->name && data->chip) + domain->name = data->chip->name; + } + + irq_clear_status_flags(virq, IRQ_NOREQUEST); +} + +static void irq_domain_remove_irq(int virq) +{ + struct irq_data *data; + + irq_set_status_flags(virq, IRQ_NOREQUEST); + irq_set_chip_and_handler(virq, NULL, NULL); + synchronize_irq(virq); + smp_mb(); + + for (data = irq_get_irq_data(virq); data; data = data->parent_data) { + struct irq_domain *domain = data->domain; + irq_hw_number_t hwirq = data->hwirq; + + if (hwirq < domain->revmap_size) { + domain->linear_revmap[hwirq] = 0; + } else { + mutex_lock(&revmap_trees_mutex); + radix_tree_delete(&domain->revmap_tree, hwirq); + mutex_unlock(&revmap_trees_mutex); + } + } +} + +static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain, + struct irq_data *child) +{ + struct irq_data *irq_data; + + irq_data = kzalloc_node(sizeof(*irq_data), GFP_KERNEL, child->node); + if (irq_data) { + child->parent_data = irq_data; + irq_data->irq = child->irq; + irq_data->node = child->node; + irq_data->domain = domain; + } + + return irq_data; +} + +static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *irq_data, *tmp; + int i; + + for (i = 0; i < nr_irqs; i++) { + irq_data = irq_get_irq_data(virq + i); + tmp = irq_data->parent_data; + irq_data->parent_data = NULL; + irq_data->domain = NULL; + + while (tmp) { + irq_data = tmp; + tmp = tmp->parent_data; + kfree(irq_data); + } + } +} + +static int irq_domain_alloc_irq_data(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *irq_data; + struct irq_domain *parent; + int i; + + /* The outermost irq_data is embedded in struct irq_desc */ + for (i = 0; i < nr_irqs; i++) { + irq_data = irq_get_irq_data(virq + i); + irq_data->domain = domain; + + for (parent = domain->parent; parent; parent = parent->parent) { + irq_data = irq_domain_insert_irq_data(parent, irq_data); + if (!irq_data) { + irq_domain_free_irq_data(virq, i + 1); + return -ENOMEM; + } + } + } + + return 0; +} + +/** + * irq_domain_get_irq_data - Get irq_data associated with @virq and @domain + * @domain: domain to match + * @virq: IRQ number to get irq_data + */ +struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, + unsigned int virq) +{ + struct irq_data *irq_data; + + for (irq_data = irq_get_irq_data(virq); irq_data; + irq_data = irq_data->parent_data) + if (irq_data->domain == domain) + return irq_data; + + return NULL; +} + +/** + * irq_domain_set_hwirq_and_chip - Set hwirq and irqchip of @virq at @domain + * @domain: Interrupt domain to match + * @virq: IRQ number + * @hwirq: The hwirq number + * @chip: The associated interrupt chip + * @chip_data: The associated chip data + */ +int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq, struct irq_chip *chip, + void *chip_data) +{ + struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq); + + if (!irq_data) + return -ENOENT; + + irq_data->hwirq = hwirq; + irq_data->chip = chip ? chip : &no_irq_chip; + irq_data->chip_data = chip_data; + + return 0; +} + +/** + * irq_domain_reset_irq_data - Clear hwirq, chip and chip_data in @irq_data + * @irq_data: The pointer to irq_data + */ +void irq_domain_reset_irq_data(struct irq_data *irq_data) +{ + irq_data->hwirq = 0; + irq_data->chip = &no_irq_chip; + irq_data->chip_data = NULL; +} + +/** + * irq_domain_free_irqs_common - Clear irq_data and free the parent + * @domain: Interrupt domain to match + * @virq: IRQ number to start with + * @nr_irqs: The number of irqs to free + */ +void irq_domain_free_irqs_common(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *irq_data; + int i; + + for (i = 0; i < nr_irqs; i++) { + irq_data = irq_domain_get_irq_data(domain, virq + i); + if (irq_data) + irq_domain_reset_irq_data(irq_data); + } + irq_domain_free_irqs_parent(domain, virq, nr_irqs); +} + +/** + * irq_domain_free_irqs_top - Clear handler and handler data, clear irqdata and free parent + * @domain: Interrupt domain to match + * @virq: IRQ number to start with + * @nr_irqs: The number of irqs to free + */ +void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + int i; + + for (i = 0; i < nr_irqs; i++) { + irq_set_handler_data(virq + i, NULL); + irq_set_handler(virq + i, NULL); + } + irq_domain_free_irqs_common(domain, virq, nr_irqs); +} + +/** + * __irq_domain_alloc_irqs - Allocate IRQs from domain + * @domain: domain to allocate from + * @irq_base: allocate specified IRQ nubmer if irq_base >= 0 + * @nr_irqs: number of IRQs to allocate + * @node: NUMA node id for memory allocation + * @arg: domain specific argument + * @realloc: IRQ descriptors have already been allocated if true + * + * Allocate IRQ numbers and initialized all data structures to support + * hierarchy IRQ domains. + * Parameter @realloc is mainly to support legacy IRQs. + * Returns error code or allocated IRQ number + * + * The whole process to setup an IRQ has been split into two steps. + * The first step, __irq_domain_alloc_irqs(), is to allocate IRQ + * descriptor and required hardware resources. The second step, + * irq_domain_activate_irq(), is to program hardwares with preallocated + * resources. In this way, it's easier to rollback when failing to + * allocate resources. + */ +int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, + unsigned int nr_irqs, int node, void *arg, + bool realloc) +{ + int i, ret, virq; + + if (domain == NULL) { + domain = irq_default_domain; + if (WARN(!domain, "domain is NULL; cannot allocate IRQ\n")) + return -EINVAL; + } + + if (!domain->ops->alloc) { + pr_debug("domain->ops->alloc() is NULL\n"); + return -ENOSYS; + } + + if (realloc && irq_base >= 0) { + virq = irq_base; + } else { + virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node); + if (virq < 0) { + pr_debug("cannot allocate IRQ(base %d, count %d)\n", + irq_base, nr_irqs); + return virq; + } + } + + if (irq_domain_alloc_irq_data(domain, virq, nr_irqs)) { + pr_debug("cannot allocate memory for IRQ%d\n", virq); + ret = -ENOMEM; + goto out_free_desc; + } + + mutex_lock(&irq_domain_mutex); + ret = domain->ops->alloc(domain, virq, nr_irqs, arg); + if (ret < 0) { + mutex_unlock(&irq_domain_mutex); + goto out_free_irq_data; + } + for (i = 0; i < nr_irqs; i++) + irq_domain_insert_irq(virq + i); + mutex_unlock(&irq_domain_mutex); + + return virq; + +out_free_irq_data: + irq_domain_free_irq_data(virq, nr_irqs); +out_free_desc: + irq_free_descs(virq, nr_irqs); + return ret; +} + +/** + * irq_domain_free_irqs - Free IRQ number and associated data structures + * @virq: base IRQ number + * @nr_irqs: number of IRQs to free + */ +void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *data = irq_get_irq_data(virq); + int i; + + if (WARN(!data || !data->domain || !data->domain->ops->free, + "NULL pointer, cannot free irq\n")) + return; + + mutex_lock(&irq_domain_mutex); + for (i = 0; i < nr_irqs; i++) + irq_domain_remove_irq(virq + i); + data->domain->ops->free(data->domain, virq, nr_irqs); + mutex_unlock(&irq_domain_mutex); + + irq_domain_free_irq_data(virq, nr_irqs); + irq_free_descs(virq, nr_irqs); +} + +/** + * irq_domain_activate_irq - Call domain_ops->activate recursively to activate + * interrupt + * @irq_data: outermost irq_data associated with interrupt + * + * This is the second step to call domain_ops->activate to program interrupt + * controllers, so the interrupt could actually get delivered. + */ +void irq_domain_activate_irq(struct irq_data *irq_data) +{ + if (irq_data && irq_data->domain) { + struct irq_domain *domain = irq_data->domain; + + if (irq_data->parent_data) + irq_domain_activate_irq(irq_data->parent_data); + if (domain->ops->activate) + domain->ops->activate(domain, irq_data); + } +} + +/** + * irq_domain_deactivate_irq - Call domain_ops->deactivate recursively to + * deactivate interrupt + * @irq_data: outermost irq_data associated with interrupt + * + * It calls domain_ops->deactivate to program interrupt controllers to disable + * interrupt delivery. + */ +void irq_domain_deactivate_irq(struct irq_data *irq_data) +{ + if (irq_data && irq_data->domain) { + struct irq_domain *domain = irq_data->domain; + + if (domain->ops->deactivate) + domain->ops->deactivate(domain, irq_data); + if (irq_data->parent_data) + irq_domain_deactivate_irq(irq_data->parent_data); + } +} + +static void irq_domain_check_hierarchy(struct irq_domain *domain) +{ + /* Hierarchy irq_domains must implement callback alloc() */ + if (domain->ops->alloc) + domain->flags |= IRQ_DOMAIN_FLAG_HIERARCHY; +} +#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */ +/** + * irq_domain_get_irq_data - Get irq_data associated with @virq and @domain + * @domain: domain to match + * @virq: IRQ number to get irq_data + */ +struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, + unsigned int virq) +{ + struct irq_data *irq_data = irq_get_irq_data(virq); + + return (irq_data && irq_data->domain == domain) ? irq_data : NULL; +} + +static void irq_domain_check_hierarchy(struct irq_domain *domain) +{ +} +#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ -- cgit v1.2.3 From 85f08c17de26f117be6ca7aa260d2ec02a2248ba Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Thu, 6 Nov 2014 22:20:16 +0800 Subject: genirq: Introduce helper functions to support stacked irq_chip Now we already support hierarchy irq_data, so introduce several helpers to support stacked irq_chips. Signed-off-by: Jiang Liu Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yingjoe Chen Cc: Yijing Wang Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 5 +++++ kernel/irq/chip.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 13ba412ce3a0..0adcbbbf2e87 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -443,6 +443,11 @@ extern void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc); extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc); extern void handle_nested_irq(unsigned int irq); +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY +extern void irq_chip_ack_parent(struct irq_data *data); +extern int irq_chip_retrigger_hierarchy(struct irq_data *data); +#endif + /* Handling of unhandled and spurious interrupts: */ extern void note_interrupt(unsigned int irq, struct irq_desc *desc, irqreturn_t action_ret); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 72a93086216b..dd1d3c4c93a2 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -850,3 +850,31 @@ void irq_cpu_offline(void) raw_spin_unlock_irqrestore(&desc->lock, flags); } } + +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY +/** + * irq_chip_ack_parent - Acknowledge the parent interrupt + * @data: Pointer to interrupt specific data + */ +void irq_chip_ack_parent(struct irq_data *data) +{ + data = data->parent_data; + data->chip->irq_ack(data); +} + +/** + * irq_chip_retrigger_hierarchy - Retrigger an interrupt in hardware + * @data: Pointer to interrupt specific data + * + * Iterate through the domain hierarchy of the interrupt and check + * whether a hw retrigger function exists. If yes, invoke it. + */ +int irq_chip_retrigger_hierarchy(struct irq_data *data) +{ + for (data = data->parent_data; data; data = data->parent_data) + if (data->chip && data->chip->irq_retrigger) + return data->chip->irq_retrigger(data); + + return -ENOSYS; +} +#endif -- cgit v1.2.3 From 56e8abab615e0c5858cfb9fa0015a44641762b9d Mon Sep 17 00:00:00 2001 From: Yingjoe Chen Date: Thu, 13 Nov 2014 23:37:05 +0800 Subject: genirq: Add more helper functions to support stacked irq_chip Add more helper function for stacked irq_chip to just call parent's function. Signed-off-by: Yingjoe Chen Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Matthias Brugger Cc: Russell King Cc: Jason Cooper Cc: Gran Likely Cc: Boris BREZILLON Cc: Cc: Bjorn Helgaas Cc: Yijing Wang Cc: Cc: Cc: Cc: Cc: Cc: Cc: Sascha Hauer Cc: Jiang Liu Cc: Marc Zyngier Link: http://lkml.kernel.org/r/1415893029-2971-3-git-send-email-yingjoe.chen@mediatek.com Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 6 ++++++ kernel/irq/chip.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 0adcbbbf2e87..fad4bf6f15f6 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -446,6 +446,12 @@ extern void handle_nested_irq(unsigned int irq); #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY extern void irq_chip_ack_parent(struct irq_data *data); extern int irq_chip_retrigger_hierarchy(struct irq_data *data); +extern void irq_chip_mask_parent(struct irq_data *data); +extern void irq_chip_unmask_parent(struct irq_data *data); +extern void irq_chip_eoi_parent(struct irq_data *data); +extern int irq_chip_set_affinity_parent(struct irq_data *data, + const struct cpumask *dest, + bool force); #endif /* Handling of unhandled and spurious interrupts: */ diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index dd1d3c4c93a2..47f4c6469a43 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -862,6 +862,54 @@ void irq_chip_ack_parent(struct irq_data *data) data->chip->irq_ack(data); } +/** + * irq_chip_mask_parent - Mask the parent interrupt + * @data: Pointer to interrupt specific data + */ +void irq_chip_mask_parent(struct irq_data *data) +{ + data = data->parent_data; + data->chip->irq_mask(data); +} + +/** + * irq_chip_unmask_parent - Unmask the parent interrupt + * @data: Pointer to interrupt specific data + */ +void irq_chip_unmask_parent(struct irq_data *data) +{ + data = data->parent_data; + data->chip->irq_unmask(data); +} + +/** + * irq_chip_eoi_parent - Invoke EOI on the parent interrupt + * @data: Pointer to interrupt specific data + */ +void irq_chip_eoi_parent(struct irq_data *data) +{ + data = data->parent_data; + data->chip->irq_eoi(data); +} + +/** + * irq_chip_set_affinity_parent - Set affinity on the parent interrupt + * @data: Pointer to interrupt specific data + * @dest: The affinity mask to set + * @force: Flag to enforce setting (disable online checks) + * + * Conditinal, as the underlying parent chip might not implement it. + */ +int irq_chip_set_affinity_parent(struct irq_data *data, + const struct cpumask *dest, bool force) +{ + data = data->parent_data; + if (data->chip->irq_set_affinity) + return data->chip->irq_set_affinity(data, dest, force); + + return -ENOSYS; +} + /** * irq_chip_retrigger_hierarchy - Retrigger an interrupt in hardware * @data: Pointer to interrupt specific data -- cgit v1.2.3 From 515085ef7ee74694bc9b02bc45196452defad59a Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Thu, 6 Nov 2014 22:20:17 +0800 Subject: genirq: Introduce irq_chip.irq_compose_msi_msg() to support stacked irqchip Add callback irq_compose_msi_msg to struct irq_chip, which will be used to support stacked irqchip. Signed-off-by: Jiang Liu Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yingjoe Chen Cc: Yijing Wang Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 5 +++++ kernel/irq/chip.c | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index fad4bf6f15f6..d58e58935465 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -29,6 +29,7 @@ struct seq_file; struct module; struct irq_desc; struct irq_data; +struct msi_msg; typedef void (*irq_flow_handler_t)(unsigned int irq, struct irq_desc *desc); typedef void (*irq_preflow_handler_t)(struct irq_data *data); @@ -320,6 +321,7 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d) * any other callback related to this irq * @irq_release_resources: optional to release resources acquired with * irq_request_resources + * @irq_compose_msi_msg: optional to compose message content for MSI * @flags: chip specific flags */ struct irq_chip { @@ -356,6 +358,8 @@ struct irq_chip { int (*irq_request_resources)(struct irq_data *data); void (*irq_release_resources)(struct irq_data *data); + void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg); + unsigned long flags; }; @@ -443,6 +447,7 @@ extern void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc); extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc); extern void handle_nested_irq(unsigned int irq); +extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg); #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY extern void irq_chip_ack_parent(struct irq_data *data); extern int irq_chip_retrigger_hierarchy(struct irq_data *data); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 47f4c6469a43..63c16d165e78 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -926,3 +926,29 @@ int irq_chip_retrigger_hierarchy(struct irq_data *data) return -ENOSYS; } #endif + +/** + * irq_chip_compose_msi_msg - Componse msi message for a irq chip + * @data: Pointer to interrupt specific data + * @msg: Pointer to the MSI message + * + * For hierarchical domains we find the first chip in the hierarchy + * which implements the irq_compose_msi_msg callback. For non + * hierarchical we use the top level chip. + */ +int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct irq_data *pos = NULL; + +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + for (; data; data = data->parent_data) +#endif + if (data->chip && data->chip->irq_compose_msi_msg) + pos = data; + if (!pos) + return -ENOSYS; + + pos->chip->irq_compose_msi_msg(pos, msg); + + return 0; +} -- cgit v1.2.3 From 2cb625478f8cea0f72b565007a35e1eb7882ac3a Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Thu, 6 Nov 2014 22:20:18 +0800 Subject: genirq: Add IRQ_SET_MASK_OK_DONE to support stacked irqchip Add IRQ_SET_MASK_OK_DONE in addition to IRQ_SET_MASK_OK and IRQ_SET_MASK_OK_NOCOPY to support stacked irqchip. IRQ_SET_MASK_OK_DONE is the same as IRQ_SET_MASK_OK to irq core. To stacked irqchip, it means that ascendant irqchips have done all the work and no more handling needed in descendant irqchips. Signed-off-by: Jiang Liu Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yingjoe Chen Cc: Yijing Wang Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 4 ++++ kernel/irq/manage.c | 2 ++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index d58e58935465..566b1e541323 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -114,10 +114,14 @@ enum { * * IRQ_SET_MASK_OK - OK, core updates irq_data.affinity * IRQ_SET_MASK_NOCPY - OK, chip did update irq_data.affinity + * IRQ_SET_MASK_OK_DONE - Same as IRQ_SET_MASK_OK for core. Special code to + * support stacked irqchips, which indicates skipping + * all descendent irqchips. */ enum { IRQ_SET_MASK_OK = 0, IRQ_SET_MASK_OK_NOCOPY, + IRQ_SET_MASK_OK_DONE, }; struct msi_desc; diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 0a9104b4608b..80692373abd6 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -183,6 +183,7 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask, ret = chip->irq_set_affinity(data, mask, force); switch (ret) { case IRQ_SET_MASK_OK: + case IRQ_SET_MASK_OK_DONE: cpumask_copy(data->affinity, mask); case IRQ_SET_MASK_OK_NOCOPY: irq_set_thread_affinity(desc); @@ -600,6 +601,7 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, switch (ret) { case IRQ_SET_MASK_OK: + case IRQ_SET_MASK_OK_DONE: irqd_clear(&desc->irq_data, IRQD_TRIGGER_MASK); irqd_set(&desc->irq_data, flags); -- cgit v1.2.3 From 75ffc0075007ca649131a2c42863ce6995d9bf80 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 11 Nov 2014 21:58:34 +0100 Subject: genirq: Split out flow handler typedefs into seperate header file Required to avoid circular include dependencies. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 6 +----- include/linux/irqhandler.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 include/linux/irqhandler.h (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 566b1e541323..677482bd8b92 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -27,12 +28,7 @@ struct seq_file; struct module; -struct irq_desc; -struct irq_data; struct msi_msg; -typedef void (*irq_flow_handler_t)(unsigned int irq, - struct irq_desc *desc); -typedef void (*irq_preflow_handler_t)(struct irq_data *data); /* * IRQ line status. diff --git a/include/linux/irqhandler.h b/include/linux/irqhandler.h new file mode 100644 index 000000000000..62d543004197 --- /dev/null +++ b/include/linux/irqhandler.h @@ -0,0 +1,14 @@ +#ifndef _LINUX_IRQHANDLER_H +#define _LINUX_IRQHANDLER_H + +/* + * Interrupt flow handler typedefs are defined here to avoid circular + * include dependencies. + */ + +struct irq_desc; +struct irq_data; +typedef void (*irq_flow_handler_t)(unsigned int irq, struct irq_desc *desc); +typedef void (*irq_preflow_handler_t)(struct irq_data *data); + +#endif -- cgit v1.2.3 From 1b5377087cb4e68d719a875120894fddfbcbf0f9 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sun, 9 Nov 2014 23:10:24 +0800 Subject: genirq: Introduce helper irq_domain_set_info() to reduce duplicated code Signed-off-by: Jiang Liu Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yingjoe Chen Cc: Yijing Wang Signed-off-by: Thomas Gleixner --- include/linux/irqdomain.h | 5 +++++ kernel/irq/irqdomain.c | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index f8563dcfd254..7aca1adb68a1 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -33,6 +33,7 @@ #define _LINUX_IRQDOMAIN_H #include +#include #include struct device_node; @@ -273,6 +274,10 @@ extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, irq_hw_number_t hwirq, struct irq_chip *chip, void *chip_data); +extern void irq_domain_set_info(struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq, struct irq_chip *chip, + void *chip_data, irq_flow_handler_t handler, + void *handler_data, const char *handler_name); extern void irq_domain_reset_irq_data(struct irq_data *irq_data); extern void irq_domain_free_irqs_common(struct irq_domain *domain, unsigned int virq, diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 9a61de21933a..4e62832ace82 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -890,6 +890,27 @@ int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq, return 0; } +/** + * irq_domain_set_info - Set the complete data for a @virq in @domain + * @domain: Interrupt domain to match + * @virq: IRQ number + * @hwirq: The hardware interrupt number + * @chip: The associated interrupt chip + * @chip_data: The associated interrupt chip data + * @handler: The interrupt flow handler + * @handler_data: The interrupt flow handler data + * @handler_name: The interrupt handler name + */ +void irq_domain_set_info(struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq, struct irq_chip *chip, + void *chip_data, irq_flow_handler_t handler, + void *handler_data, const char *handler_name) +{ + irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, chip_data); + __irq_set_handler(virq, handler, 0, handler_name); + irq_set_handler_data(virq, handler_data); +} + /** * irq_domain_reset_irq_data - Clear hwirq, chip and chip_data in @irq_data * @irq_data: The pointer to irq_data -- cgit v1.2.3 From 36d727310cb9f85efb5ac089ffb1797e7c3538e1 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 15 Nov 2014 22:24:01 +0800 Subject: irqdomain: Implement a method to automatically call parent domains alloc/free Add a flags to irq_domain.flags to control whether the irqdomain core should automatically call parent irqdomain's alloc/free callbacks. It help to reduce hierarchy irqdomains users' code size. Signed-off-by: Jiang Liu Cc: Tony Luck Cc: linux-arm-kernel@lists.infradead.org Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yijing Wang Cc: Yingjoe Chen Cc: Borislav Petkov Cc: Benjamin Herrenschmidt Cc: Matthias Brugger Link: http://lkml.kernel.org/r/1416061447-9472-4-git-send-email-jiang.liu@linux.intel.com Signed-off-by: Thomas Gleixner --- include/linux/irqdomain.h | 24 ++++++-------- kernel/irq/irqdomain.c | 82 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 7aca1adb68a1..dd2709bdad56 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -134,6 +134,9 @@ enum { /* Irq domain is hierarchical */ IRQ_DOMAIN_FLAG_HIERARCHY = (1 << 0), + /* Core calls alloc/free recursive through the domain hierarchy. */ + IRQ_DOMAIN_FLAG_AUTO_RECURSIVE = (1 << 1), + /* * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved * for implementation specific purposes and ignored by the @@ -285,22 +288,13 @@ extern void irq_domain_free_irqs_common(struct irq_domain *domain, extern void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs); -static inline int irq_domain_alloc_irqs_parent(struct irq_domain *domain, - unsigned int irq_base, - unsigned int nr_irqs, void *arg) -{ - if (domain->parent && domain->parent->ops->alloc) - return domain->parent->ops->alloc(domain->parent, irq_base, - nr_irqs, arg); - return -ENOSYS; -} +extern int irq_domain_alloc_irqs_parent(struct irq_domain *domain, + unsigned int irq_base, + unsigned int nr_irqs, void *arg); -static inline void irq_domain_free_irqs_parent(struct irq_domain *domain, - unsigned int irq_base, unsigned int nr_irqs) -{ - if (domain->parent && domain->parent->ops->free) - domain->parent->ops->free(domain->parent, irq_base, nr_irqs); -} +extern void irq_domain_free_irqs_parent(struct irq_domain *domain, + unsigned int irq_base, + unsigned int nr_irqs); static inline bool irq_domain_is_hierarchy(struct irq_domain *domain) { diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 4e62832ace82..9c88db7056d4 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -960,6 +960,43 @@ void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq, irq_domain_free_irqs_common(domain, virq, nr_irqs); } +static bool irq_domain_is_auto_recursive(struct irq_domain *domain) +{ + return domain->flags & IRQ_DOMAIN_FLAG_AUTO_RECURSIVE; +} + +static void irq_domain_free_irqs_recursive(struct irq_domain *domain, + unsigned int irq_base, + unsigned int nr_irqs) +{ + domain->ops->free(domain, irq_base, nr_irqs); + if (irq_domain_is_auto_recursive(domain)) { + BUG_ON(!domain->parent); + irq_domain_free_irqs_recursive(domain->parent, irq_base, + nr_irqs); + } +} + +static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain, + unsigned int irq_base, + unsigned int nr_irqs, void *arg) +{ + int ret = 0; + struct irq_domain *parent = domain->parent; + bool recursive = irq_domain_is_auto_recursive(domain); + + BUG_ON(recursive && !parent); + if (recursive) + ret = irq_domain_alloc_irqs_recursive(parent, irq_base, + nr_irqs, arg); + if (ret >= 0) + ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg); + if (ret < 0 && recursive) + irq_domain_free_irqs_recursive(parent, irq_base, nr_irqs); + + return ret; +} + /** * __irq_domain_alloc_irqs - Allocate IRQs from domain * @domain: domain to allocate from @@ -1016,7 +1053,7 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, } mutex_lock(&irq_domain_mutex); - ret = domain->ops->alloc(domain, virq, nr_irqs, arg); + ret = irq_domain_alloc_irqs_recursive(domain, virq, nr_irqs, arg); if (ret < 0) { mutex_unlock(&irq_domain_mutex); goto out_free_irq_data; @@ -1051,13 +1088,54 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs) mutex_lock(&irq_domain_mutex); for (i = 0; i < nr_irqs; i++) irq_domain_remove_irq(virq + i); - data->domain->ops->free(data->domain, virq, nr_irqs); + irq_domain_free_irqs_recursive(data->domain, virq, nr_irqs); mutex_unlock(&irq_domain_mutex); irq_domain_free_irq_data(virq, nr_irqs); irq_free_descs(virq, nr_irqs); } +/** + * irq_domain_alloc_irqs_parent - Allocate interrupts from parent domain + * @irq_base: Base IRQ number + * @nr_irqs: Number of IRQs to allocate + * @arg: Allocation data (arch/domain specific) + * + * Check whether the domain has been setup recursive. If not allocate + * through the parent domain. + */ +int irq_domain_alloc_irqs_parent(struct irq_domain *domain, + unsigned int irq_base, unsigned int nr_irqs, + void *arg) +{ + /* irq_domain_alloc_irqs_recursive() has called parent's alloc() */ + if (irq_domain_is_auto_recursive(domain)) + return 0; + + domain = domain->parent; + if (domain) + return irq_domain_alloc_irqs_recursive(domain, irq_base, + nr_irqs, arg); + return -ENOSYS; +} + +/** + * irq_domain_free_irqs_parent - Free interrupts from parent domain + * @irq_base: Base IRQ number + * @nr_irqs: Number of IRQs to free + * + * Check whether the domain has been setup recursive. If not free + * through the parent domain. + */ +void irq_domain_free_irqs_parent(struct irq_domain *domain, + unsigned int irq_base, unsigned int nr_irqs) +{ + /* irq_domain_free_irqs_recursive() will call parent's free */ + if (!irq_domain_is_auto_recursive(domain) && domain->parent) + irq_domain_free_irqs_recursive(domain->parent, irq_base, + nr_irqs); +} + /** * irq_domain_activate_irq - Call domain_ops->activate recursively to activate * interrupt -- cgit v1.2.3 From afb7da83b9f476728623130703acb553d7c7c4d9 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 15 Nov 2014 22:24:02 +0800 Subject: irqdomain: Introduce helper function irq_domain_add_hierarchy() Introduce helper function irq_domain_add_hierarchy(), which creates a linear irqdomain if parameter 'size' is not zero, otherwise creates a tree irqdomain. Signed-off-by: Jiang Liu Cc: Tony Luck Cc: linux-arm-kernel@lists.infradead.org Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yijing Wang Cc: Yingjoe Chen Cc: Borislav Petkov Cc: Benjamin Herrenschmidt Cc: Matthias Brugger Link: http://lkml.kernel.org/r/1416061447-9472-5-git-send-email-jiang.liu@linux.intel.com Signed-off-by: Thomas Gleixner --- include/linux/irqdomain.h | 4 ++++ kernel/irq/irqdomain.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) (limited to 'include') diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index dd2709bdad56..676d7306a360 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -259,6 +259,10 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr, extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, unsigned int virq); #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY +extern struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent, + unsigned int flags, unsigned int size, + struct device_node *node, + const struct irq_domain_ops *ops, void *host_data); extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, unsigned int nr_irqs, int node, void *arg, bool realloc); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 9c88db7056d4..7fac311057b8 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -742,6 +742,42 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt, } #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY +/** + * irq_domain_add_hierarchy - Add a irqdomain into the hierarchy + * @parent: Parent irq domain to associate with the new domain + * @flags: Irq domain flags associated to the domain + * @size: Size of the domain. See below + * @node: Optional device-tree node of the interrupt controller + * @ops: Pointer to the interrupt domain callbacks + * @host_data: Controller private data pointer + * + * If @size is 0 a tree domain is created, otherwise a linear domain. + * + * If successful the parent is associated to the new domain and the + * domain flags are set. + * Returns pointer to IRQ domain, or NULL on failure. + */ +struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent, + unsigned int flags, + unsigned int size, + struct device_node *node, + const struct irq_domain_ops *ops, + void *host_data) +{ + struct irq_domain *domain; + + if (size) + domain = irq_domain_add_linear(node, size, ops, host_data); + else + domain = irq_domain_add_tree(node, ops, host_data); + if (domain) { + domain->parent = parent; + domain->flags |= flags; + } + + return domain; +} + static void irq_domain_insert_irq(int virq) { struct irq_data *data; -- cgit v1.2.3 From 9dde55b72dc80bfae4280ddce5dbd69ba8240813 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sun, 9 Nov 2014 23:10:28 +0800 Subject: genirq: Introduce callback irq_chip.irq_write_msi_msg Introduce callback irq_chip.irq_write_msi_msg, so we can share common code among MSI alike interrupt controllers, such as HPET and DMAR. Signed-off-by: Jiang Liu Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yingjoe Chen Cc: Yijing Wang Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 677482bd8b92..8badf34baf0f 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -322,6 +322,7 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d) * @irq_release_resources: optional to release resources acquired with * irq_request_resources * @irq_compose_msi_msg: optional to compose message content for MSI + * @irq_write_msi_msg: optional to write message content for MSI * @flags: chip specific flags */ struct irq_chip { @@ -359,6 +360,7 @@ struct irq_chip { void (*irq_release_resources)(struct irq_data *data); void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg); + void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg); unsigned long flags; }; @@ -459,6 +461,12 @@ extern int irq_chip_set_affinity_parent(struct irq_data *data, bool force); #endif +static inline void irq_chip_write_msi_msg(struct irq_data *data, + struct msi_msg *msg) +{ + data->chip->irq_write_msi_msg(data, msg); +} + /* Handling of unhandled and spurious interrupts: */ extern void note_interrupt(unsigned int irq, struct irq_desc *desc, irqreturn_t action_ret); -- cgit v1.2.3 From f3cf8bb0d6c3c11ddedf01f02f856f2ae8c33aa4 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 12 Nov 2014 11:39:03 +0100 Subject: genirq: Add generic msi irq domain support Implement the basic functions for MSI interrupt support with hierarchical interrupt domains. [ tglx: Extracted and combined from several patches ] Signed-off-by: Jiang Liu Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yingjoe Chen Cc: Yijing Wang Signed-off-by: Thomas Gleixner --- include/linux/msi.h | 45 +++++++++++++++++ kernel/irq/Kconfig | 10 ++++ kernel/irq/Makefile | 1 + kernel/irq/msi.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 197 insertions(+) create mode 100644 kernel/irq/msi.c (limited to 'include') diff --git a/include/linux/msi.h b/include/linux/msi.h index e0d08517bd3d..b5ca2456769c 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -114,4 +114,49 @@ struct msi_controller { void (*teardown_irq)(struct msi_controller *chip, unsigned int irq); }; +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +struct irq_domain; +struct irq_chip; +struct device_node; +struct msi_domain_info; + +/** + * struct msi_domain_ops - MSI interrupt domain callbacks + * @get_hwirq: Retrieve the resulting hw irq number + * @msi_init: Domain specific init function for MSI interrupts + * @msi_free: Domain specific function to free a MSI interrupts + */ +struct msi_domain_ops { + irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, void *arg); + int (*msi_init)(struct irq_domain *domain, + struct msi_domain_info *info, + unsigned int virq, irq_hw_number_t hwirq, + void *arg); + void (*msi_free)(struct irq_domain *domain, + struct msi_domain_info *info, + unsigned int virq); +}; + +/** + * struct msi_domain_info - MSI interrupt domain data + * @ops: The callback data structure + * @chip: The associated interrupt chip + * @data: Domain specific data + */ +struct msi_domain_info { + struct msi_domain_ops *ops; + struct irq_chip *chip; + void *data; +}; + +int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, + bool force); + +struct irq_domain *msi_create_irq_domain(struct device_node *of_node, + struct msi_domain_info *info, + struct irq_domain *parent); +struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain); + +#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ + #endif /* LINUX_MSI_H */ diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 4f2eb2b1f23b..9a76e3beda54 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -60,6 +60,16 @@ config IRQ_DOMAIN_HIERARCHY bool select IRQ_DOMAIN +# Generic MSI interrupt support +config GENERIC_MSI_IRQ + bool + +# Generic MSI hierarchical interrupt domain support +config GENERIC_MSI_IRQ_DOMAIN + bool + select IRQ_DOMAIN_HIERARCHY + select GENERIC_MSI_IRQ + config HANDLE_DOMAIN_IRQ bool diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile index fff17381f0af..d12123526e2b 100644 --- a/kernel/irq/Makefile +++ b/kernel/irq/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o obj-$(CONFIG_PM_SLEEP) += pm.o +obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c new file mode 100644 index 000000000000..5e0cef4741d9 --- /dev/null +++ b/kernel/irq/msi.c @@ -0,0 +1,141 @@ +/* + * linux/kernel/irq/msi.c + * + * Copyright (C) 2014 Intel Corp. + * Author: Jiang Liu + * + * This file is licensed under GPLv2. + * + * This file contains common code to support Message Signalled Interrupt for + * PCI compatible and non PCI compatible devices. + */ +#include +#include +#include + +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +/** + * msi_domain_set_affinity - Generic affinity setter function for MSI domains + * @irq_data: The irq data associated to the interrupt + * @mask: The affinity mask to set + * @force: Flag to enforce setting (disable online checks) + * + * Intended to be used by MSI interrupt controllers which are + * implemented with hierarchical domains. + */ +int msi_domain_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + struct irq_data *parent = irq_data->parent_data; + struct msi_msg msg; + int ret; + + ret = parent->chip->irq_set_affinity(parent, mask, force); + if (ret >= 0 && ret != IRQ_SET_MASK_OK_DONE) { + BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); + irq_chip_write_msi_msg(irq_data, &msg); + } + + return ret; +} + +static void msi_domain_activate(struct irq_domain *domain, + struct irq_data *irq_data) +{ + struct msi_msg msg; + + BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); + irq_chip_write_msi_msg(irq_data, &msg); +} + +static void msi_domain_deactivate(struct irq_domain *domain, + struct irq_data *irq_data) +{ + struct msi_msg msg; + + memset(&msg, 0, sizeof(msg)); + irq_chip_write_msi_msg(irq_data, &msg); +} + +static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct msi_domain_info *info = domain->host_data; + struct msi_domain_ops *ops = info->ops; + irq_hw_number_t hwirq = ops->get_hwirq(info, arg); + int i, ret; + + if (irq_find_mapping(domain, hwirq) > 0) + return -EEXIST; + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); + if (ret < 0) + return ret; + + for (i = 0; i < nr_irqs; i++) { + ret = ops->msi_init(domain, info, virq + i, hwirq + i, arg); + if (ret < 0) { + if (ops->msi_free) { + for (i--; i > 0; i--) + ops->msi_free(domain, info, virq + i); + } + irq_domain_free_irqs_top(domain, virq, nr_irqs); + return ret; + } + } + + return 0; +} + +static void msi_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct msi_domain_info *info = domain->host_data; + int i; + + if (info->ops->msi_free) { + for (i = 0; i < nr_irqs; i++) + info->ops->msi_free(domain, info, virq + i); + } + irq_domain_free_irqs_top(domain, virq, nr_irqs); +} + +static struct irq_domain_ops msi_domain_ops = { + .alloc = msi_domain_alloc, + .free = msi_domain_free, + .activate = msi_domain_activate, + .deactivate = msi_domain_deactivate, +}; + +/** + * msi_create_irq_domain - Create a MSI interrupt domain + * @of_node: Optional device-tree node of the interrupt controller + * @info: MSI domain info + * @parent: Parent irq domain + */ +struct irq_domain *msi_create_irq_domain(struct device_node *of_node, + struct msi_domain_info *info, + struct irq_domain *parent) +{ + struct irq_domain *domain; + + domain = irq_domain_add_tree(of_node, &msi_domain_ops, info); + if (domain) + domain->parent = parent; + + return domain; +} + +/** + * msi_get_domain_info - Get the MSI interrupt domain info for @domain + * @domain: The interrupt domain to retrieve data from + * + * Returns the pointer to the msi_domain_info stored in + * @domain->host_data. + */ +struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain) +{ + return (struct msi_domain_info *)domain->host_data; +} + +#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ -- cgit v1.2.3 From 926ff9ad76e097011030feaee904395e06eea17a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Nov 2014 18:09:34 +0100 Subject: asm-generic: Add msi.h To support MSI irq domains we want a generic data structure for allocation, but we need the option to provide an architecture specific version of it. So instead of playing #ifdef games in linux/msi.h we add a generic header file and let architectures decide whether to include it or to provide their own implementation and provide the required typedef. I know that typedefs are not really nice, but in this case there are no forward declarations required and it's the simplest solution. Signed-off-by: Thomas Gleixner Acked-by: Arnd Bergmann Cc: Jiang Liu Cc: Tony Luck Cc: linux-arm-kernel@lists.infradead.org Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yijing Wang Cc: Yingjoe Chen Cc: Borislav Petkov Cc: Matthias Brugger Cc: Alexander Gordeev --- include/asm-generic/msi.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 include/asm-generic/msi.h (limited to 'include') diff --git a/include/asm-generic/msi.h b/include/asm-generic/msi.h new file mode 100644 index 000000000000..61c58d8878ce --- /dev/null +++ b/include/asm-generic/msi.h @@ -0,0 +1,32 @@ +#ifndef __ASM_GENERIC_MSI_H +#define __ASM_GENERIC_MSI_H + +#include + +#ifndef NUM_MSI_ALLOC_SCRATCHPAD_REGS +# define NUM_MSI_ALLOC_SCRATCHPAD_REGS 2 +#endif + +struct msi_desc; + +/** + * struct msi_alloc_info - Default structure for MSI interrupt allocation. + * @desc: Pointer to msi descriptor + * @hwirq: Associated hw interrupt number in the domain + * @scratchpad: Storage for implementation specific scratch data + * + * Architectures can provide their own implementation by not including + * asm-generic/msi.h into their arch specific header file. + */ +typedef struct msi_alloc_info { + struct msi_desc *desc; + irq_hw_number_t hwirq; + union { + unsigned long ul; + void *ptr; + } scratchpad[NUM_MSI_ALLOC_SCRATCHPAD_REGS]; +} msi_alloc_info_t; + +#define GENERIC_MSI_DOMAIN_OPS 1 + +#endif -- cgit v1.2.3 From d9109698be6e7439e6082aa00d79d4556114739b Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 15 Nov 2014 22:24:04 +0800 Subject: genirq: Introduce msi_domain_alloc/free_irqs() Introduce msi_domain_{alloc|free}_irqs() to alloc/free interrupts from generic MSI irqdomain. Signed-off-by: Jiang Liu Cc: Tony Luck Cc: linux-arm-kernel@lists.infradead.org Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yijing Wang Cc: Yingjoe Chen Cc: Borislav Petkov Cc: Matthias Brugger Cc: Alexander Gordeev Link: http://lkml.kernel.org/r/1416061447-9472-7-git-send-email-jiang.liu@linux.intel.com Signed-off-by: Thomas Gleixner --- include/linux/msi.h | 29 +++++++++++++++++++++ kernel/irq/msi.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) (limited to 'include') diff --git a/include/linux/msi.h b/include/linux/msi.h index b5ca2456769c..7a93a988dce8 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -115,6 +115,9 @@ struct msi_controller { }; #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN + +#include + struct irq_domain; struct irq_chip; struct device_node; @@ -125,6 +128,18 @@ struct msi_domain_info; * @get_hwirq: Retrieve the resulting hw irq number * @msi_init: Domain specific init function for MSI interrupts * @msi_free: Domain specific function to free a MSI interrupts + * @msi_check: Callback for verification of the domain/info/dev data + * @msi_prepare: Prepare the allocation of the interrupts in the domain + * @msi_finish: Optional callbacl to finalize the allocation + * @set_desc: Set the msi descriptor for an interrupt + * @handle_error: Optional error handler if the allocation fails + * + * @get_hwirq, @msi_init and @msi_free are callbacks used by + * msi_create_irq_domain() and related interfaces + * + * @msi_check, @msi_prepare, @msi_finish, @set_desc and @handle_error + * are callbacks used by msi_irq_domain_alloc_irqs() and related + * interfaces which are based on msi_desc. */ struct msi_domain_ops { irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, void *arg); @@ -135,6 +150,17 @@ struct msi_domain_ops { void (*msi_free)(struct irq_domain *domain, struct msi_domain_info *info, unsigned int virq); + int (*msi_check)(struct irq_domain *domain, + struct msi_domain_info *info, + struct device *dev); + int (*msi_prepare)(struct irq_domain *domain, + struct device *dev, int nvec, + msi_alloc_info_t *arg); + void (*msi_finish)(msi_alloc_info_t *arg, int retval); + void (*set_desc)(msi_alloc_info_t *arg, + struct msi_desc *desc); + int (*handle_error)(struct irq_domain *domain, + struct msi_desc *desc, int error); }; /** @@ -155,6 +181,9 @@ int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, struct irq_domain *msi_create_irq_domain(struct device_node *of_node, struct msi_domain_info *info, struct irq_domain *parent); +int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, + int nvec); +void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain); #endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 5e0cef4741d9..23111aaa06b2 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -13,6 +13,9 @@ #include #include +/* Temparory solution for building, will be removed later */ +#include + #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN /** * msi_domain_set_affinity - Generic affinity setter function for MSI domains @@ -126,6 +129,78 @@ struct irq_domain *msi_create_irq_domain(struct device_node *of_node, return domain; } +/** + * msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain + * @domain: The domain to allocate from + * @dev: Pointer to device struct of the device for which the interrupts + * are allocated + * @nvec: The number of interrupts to allocate + * + * Returns 0 on success or an error code. + */ +int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, + int nvec) +{ + struct msi_domain_info *info = domain->host_data; + struct msi_domain_ops *ops = info->ops; + msi_alloc_info_t arg; + struct msi_desc *desc; + int i, ret, virq = -1; + + ret = ops->msi_check(domain, info, dev); + if (ret == 0) + ret = ops->msi_prepare(domain, dev, nvec, &arg); + if (ret) + return ret; + + for_each_msi_entry(desc, dev) { + ops->set_desc(&arg, desc); + + virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used, + dev_to_node(dev), &arg, false); + if (virq < 0) { + ret = -ENOSPC; + if (ops->handle_error) + ret = ops->handle_error(domain, desc, ret); + if (ops->msi_finish) + ops->msi_finish(&arg, ret); + return ret; + } + + for (i = 0; i < desc->nvec_used; i++) + irq_set_msi_desc_off(virq, i, desc); + } + + if (ops->msi_finish) + ops->msi_finish(&arg, 0); + + for_each_msi_entry(desc, dev) { + if (desc->nvec_used == 1) + dev_dbg(dev, "irq %d for MSI\n", virq); + else + dev_dbg(dev, "irq [%d-%d] for MSI\n", + virq, virq + desc->nvec_used - 1); + } + + return 0; +} + +/** + * msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev + * @domain: The domain to managing the interrupts + * @dev: Pointer to device struct of the device for which the interrupts + * are free + */ +void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) +{ + struct msi_desc *desc; + + for_each_msi_entry(desc, dev) { + irq_domain_free_irqs(desc->irq, desc->nvec_used); + desc->irq = 0; + } +} + /** * msi_get_domain_info - Get the MSI interrupt domain info for @domain * @domain: The interrupt domain to retrieve data from -- cgit v1.2.3 From aeeb59657c35da64068336c20068da237f41ab76 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 15 Nov 2014 22:24:05 +0800 Subject: genirq: Provide default callbacks for msi_domain_ops Extend struct msi_domain_info and provide default callbacks for msi_domain_ops. Signed-off-by: Jiang Liu Cc: Tony Luck Cc: linux-arm-kernel@lists.infradead.org Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yijing Wang Cc: Yingjoe Chen Cc: Borislav Petkov Cc: Matthias Brugger Cc: Alexander Gordeev Link: http://lkml.kernel.org/r/1416061447-9472-8-git-send-email-jiang.liu@linux.intel.com Signed-off-by: Thomas Gleixner --- include/linux/msi.h | 42 +++++++++++++++++--- kernel/irq/msi.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 140 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/msi.h b/include/linux/msi.h index 7a93a988dce8..0098e2c8fd42 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -116,6 +116,7 @@ struct msi_controller { #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +#include #include struct irq_domain; @@ -142,11 +143,12 @@ struct msi_domain_info; * interfaces which are based on msi_desc. */ struct msi_domain_ops { - irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, void *arg); + irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, + msi_alloc_info_t *arg); int (*msi_init)(struct irq_domain *domain, struct msi_domain_info *info, unsigned int virq, irq_hw_number_t hwirq, - void *arg); + msi_alloc_info_t *arg); void (*msi_free)(struct irq_domain *domain, struct msi_domain_info *info, unsigned int virq); @@ -165,16 +167,46 @@ struct msi_domain_ops { /** * struct msi_domain_info - MSI interrupt domain data - * @ops: The callback data structure - * @chip: The associated interrupt chip - * @data: Domain specific data + * @flags: Flags to decribe features and capabilities + * @ops: The callback data structure + * @chip: Optional: associated interrupt chip + * @chip_data: Optional: associated interrupt chip data + * @handler: Optional: associated interrupt flow handler + * @handler_data: Optional: associated interrupt flow handler data + * @handler_name: Optional: associated interrupt flow handler name + * @data: Optional: domain specific data */ struct msi_domain_info { + u32 flags; struct msi_domain_ops *ops; struct irq_chip *chip; + void *chip_data; + irq_flow_handler_t handler; + void *handler_data; + const char *handler_name; void *data; }; +/* Flags for msi_domain_info */ +enum { + /* + * Init non implemented ops callbacks with default MSI domain + * callbacks. + */ + MSI_FLAG_USE_DEF_DOM_OPS = (1 << 0), + /* + * Init non implemented chip callbacks with default MSI chip + * callbacks. + */ + MSI_FLAG_USE_DEF_CHIP_OPS = (1 << 1), + /* Build identity map between hwirq and irq */ + MSI_FLAG_IDENTITY_MAP = (1 << 2), + /* Support multiple PCI MSI interrupts */ + MSI_FLAG_MULTI_PCI_MSI = (1 << 3), + /* Support PCI MSIX interrupts */ + MSI_FLAG_PCI_MSIX = (1 << 4), +}; + int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force); diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 23111aaa06b2..d0fe84d881f6 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -9,6 +9,8 @@ * This file contains common code to support Message Signalled Interrupt for * PCI compatible and non PCI compatible devices. */ +#include +#include #include #include #include @@ -110,23 +112,112 @@ static struct irq_domain_ops msi_domain_ops = { .deactivate = msi_domain_deactivate, }; +#ifdef GENERIC_MSI_DOMAIN_OPS +static irq_hw_number_t msi_domain_ops_get_hwirq(struct msi_domain_info *info, + msi_alloc_info_t *arg) +{ + return arg->hwirq; +} + +static int msi_domain_ops_prepare(struct irq_domain *domain, struct device *dev, + int nvec, msi_alloc_info_t *arg) +{ + memset(arg, 0, sizeof(*arg)); + return 0; +} + +static void msi_domain_ops_set_desc(msi_alloc_info_t *arg, + struct msi_desc *desc) +{ + arg->desc = desc; +} +#else +#define msi_domain_ops_get_hwirq NULL +#define msi_domain_ops_prepare NULL +#define msi_domain_ops_set_desc NULL +#endif /* !GENERIC_MSI_DOMAIN_OPS */ + +static int msi_domain_ops_init(struct irq_domain *domain, + struct msi_domain_info *info, + unsigned int virq, irq_hw_number_t hwirq, + msi_alloc_info_t *arg) +{ + irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip, + info->chip_data); + if (info->handler && info->handler_name) { + __irq_set_handler(virq, info->handler, 0, info->handler_name); + if (info->handler_data) + irq_set_handler_data(virq, info->handler_data); + } + return 0; +} + +static int msi_domain_ops_check(struct irq_domain *domain, + struct msi_domain_info *info, + struct device *dev) +{ + return 0; +} + +static struct msi_domain_ops msi_domain_ops_default = { + .get_hwirq = msi_domain_ops_get_hwirq, + .msi_init = msi_domain_ops_init, + .msi_check = msi_domain_ops_check, + .msi_prepare = msi_domain_ops_prepare, + .set_desc = msi_domain_ops_set_desc, +}; + +static void msi_domain_update_dom_ops(struct msi_domain_info *info) +{ + struct msi_domain_ops *ops = info->ops; + + if (ops == NULL) { + info->ops = &msi_domain_ops_default; + return; + } + + if (ops->get_hwirq == NULL) + ops->get_hwirq = msi_domain_ops_default.get_hwirq; + if (ops->msi_init == NULL) + ops->msi_init = msi_domain_ops_default.msi_init; + if (ops->msi_check == NULL) + ops->msi_check = msi_domain_ops_default.msi_check; + if (ops->msi_prepare == NULL) + ops->msi_prepare = msi_domain_ops_default.msi_prepare; + if (ops->set_desc == NULL) + ops->set_desc = msi_domain_ops_default.set_desc; +} + +static void msi_domain_update_chip_ops(struct msi_domain_info *info) +{ + struct irq_chip *chip = info->chip; + + BUG_ON(!chip); + if (!chip->irq_mask) + chip->irq_mask = pci_msi_mask_irq; + if (!chip->irq_unmask) + chip->irq_unmask = pci_msi_unmask_irq; + if (!chip->irq_set_affinity) + chip->irq_set_affinity = msi_domain_set_affinity; +} + /** * msi_create_irq_domain - Create a MSI interrupt domain * @of_node: Optional device-tree node of the interrupt controller * @info: MSI domain info * @parent: Parent irq domain */ -struct irq_domain *msi_create_irq_domain(struct device_node *of_node, +struct irq_domain *msi_create_irq_domain(struct device_node *node, struct msi_domain_info *info, struct irq_domain *parent) { - struct irq_domain *domain; + if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) + msi_domain_update_dom_ops(info); + if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) + msi_domain_update_chip_ops(info); - domain = irq_domain_add_tree(of_node, &msi_domain_ops, info); - if (domain) - domain->parent = parent; - - return domain; + return irq_domain_add_hierarchy(parent, 0, 0, node, &msi_domain_ops, + info); } /** @@ -155,8 +246,12 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, for_each_msi_entry(desc, dev) { ops->set_desc(&arg, desc); + if (info->flags & MSI_FLAG_IDENTITY_MAP) + virq = (int)ops->get_hwirq(info, &arg); + else + virq = -1; - virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used, + virq = __irq_domain_alloc_irqs(domain, virq, desc->nvec_used, dev_to_node(dev), &arg, false); if (virq < 0) { ret = -ENOSPC; -- cgit v1.2.3 From 3878eaefb89aa841ae4c2150490cee864ac628cb Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Tue, 11 Nov 2014 21:02:18 +0800 Subject: PCI/MSI: Enhance core to support hierarchy irqdomain Enhance PCI MSI core to support hierarchy irqdomain, so the common code can be shared across architectures. [ tglx: Extracted and combined from several patches ] Signed-off-by: Jiang Liu Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yingjoe Chen Cc: Yijing Wang Signed-off-by: Thomas Gleixner --- drivers/pci/Kconfig | 5 ++ drivers/pci/msi.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/msi.h | 14 +++++ 3 files changed, 187 insertions(+) (limited to 'include') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 82f95f4512f8..cced84233ac0 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -17,6 +17,11 @@ config PCI_MSI If you don't know what to do here, say Y. +config PCI_MSI_IRQ_DOMAIN + bool + depends on PCI_MSI + select GENERIC_MSI_IRQ_DOMAIN + config PCI_DEBUG bool "PCI Debugging" depends on PCI && DEBUG_KERNEL diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 103700e127bf..36b0c2e55929 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "pci.h" @@ -1091,3 +1092,170 @@ int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, return nvec; } EXPORT_SYMBOL(pci_enable_msix_range); + +#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN +/** + * pci_msi_domain_write_msg - Helper to write MSI message to PCI config space + * @irq_data: Pointer to interrupt data of the MSI interrupt + * @msg: Pointer to the message + */ +void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg) +{ + struct msi_desc *desc = irq_data->msi_desc; + + /* + * For MSI-X desc->irq is always equal to irq_data->irq. For + * MSI only the first interrupt of MULTI MSI passes the test. + */ + if (desc->irq == irq_data->irq) + __pci_write_msi_msg(desc, msg); +} + +/** + * pci_msi_domain_calc_hwirq - Generate a unique ID for an MSI source + * @dev: Pointer to the PCI device + * @desc: Pointer to the msi descriptor + * + * The ID number is only used within the irqdomain. + */ +irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, + struct msi_desc *desc) +{ + return (irq_hw_number_t)desc->msi_attrib.entry_nr | + PCI_DEVID(dev->bus->number, dev->devfn) << 11 | + (pci_domain_nr(dev->bus) & 0xFFFFFFFF) << 27; +} + +static inline bool pci_msi_desc_is_multi_msi(struct msi_desc *desc) +{ + return !desc->msi_attrib.is_msix && desc->nvec_used > 1; +} + +/** + * pci_msi_domain_check_cap - Verify that @domain supports the capabilities for @dev + * @domain: The interrupt domain to check + * @info: The domain info for verification + * @dev: The device to check + * + * Returns: + * 0 if the functionality is supported + * 1 if Multi MSI is requested, but the domain does not support it + * -ENOTSUPP otherwise + */ +int pci_msi_domain_check_cap(struct irq_domain *domain, + struct msi_domain_info *info, struct device *dev) +{ + struct msi_desc *desc = first_pci_msi_entry(to_pci_dev(dev)); + + /* Special handling to support pci_enable_msi_range() */ + if (pci_msi_desc_is_multi_msi(desc) && + !(info->flags & MSI_FLAG_MULTI_PCI_MSI)) + return 1; + else if (desc->msi_attrib.is_msix && !(info->flags & MSI_FLAG_PCI_MSIX)) + return -ENOTSUPP; + + return 0; +} + +static int pci_msi_domain_handle_error(struct irq_domain *domain, + struct msi_desc *desc, int error) +{ + /* Special handling to support pci_enable_msi_range() */ + if (pci_msi_desc_is_multi_msi(desc) && error == -ENOSPC) + return 1; + + return error; +} + +#ifdef GENERIC_MSI_DOMAIN_OPS +static void pci_msi_domain_set_desc(msi_alloc_info_t *arg, + struct msi_desc *desc) +{ + arg->desc = desc; + arg->hwirq = pci_msi_domain_calc_hwirq(msi_desc_to_pci_dev(desc), + desc); +} +#else +#define pci_msi_domain_set_desc NULL +#endif + +static struct msi_domain_ops pci_msi_domain_ops_default = { + .set_desc = pci_msi_domain_set_desc, + .msi_check = pci_msi_domain_check_cap, + .handle_error = pci_msi_domain_handle_error, +}; + +static void pci_msi_domain_update_dom_ops(struct msi_domain_info *info) +{ + struct msi_domain_ops *ops = info->ops; + + if (ops == NULL) { + info->ops = &pci_msi_domain_ops_default; + } else { + if (ops->set_desc == NULL) + ops->set_desc = pci_msi_domain_set_desc; + if (ops->msi_check == NULL) + ops->msi_check = pci_msi_domain_check_cap; + if (ops->handle_error == NULL) + ops->handle_error = pci_msi_domain_handle_error; + } +} + +static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info) +{ + struct irq_chip *chip = info->chip; + + BUG_ON(!chip); + if (!chip->irq_write_msi_msg) + chip->irq_write_msi_msg = pci_msi_domain_write_msg; +} + +/** + * pci_msi_create_irq_domain - Creat a MSI interrupt domain + * @node: Optional device-tree node of the interrupt controller + * @info: MSI domain info + * @parent: Parent irq domain + * + * Updates the domain and chip ops and creates a MSI interrupt domain. + * + * Returns: + * A domain pointer or NULL in case of failure. + */ +struct irq_domain *pci_msi_create_irq_domain(struct device_node *node, + struct msi_domain_info *info, + struct irq_domain *parent) +{ + if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) + pci_msi_domain_update_dom_ops(info); + if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) + pci_msi_domain_update_chip_ops(info); + + return msi_create_irq_domain(node, info, parent); +} + +/** + * pci_msi_domain_alloc_irqs - Allocate interrupts for @dev in @domain + * @domain: The interrupt domain to allocate from + * @dev: The device for which to allocate + * @nvec: The number of interrupts to allocate + * @type: Unused to allow simpler migration from the arch_XXX interfaces + * + * Returns: + * A virtual interrupt number or an error code in case of failure + */ +int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev, + int nvec, int type) +{ + return msi_domain_alloc_irqs(domain, &dev->dev, nvec); +} + +/** + * pci_msi_domain_free_irqs - Free interrupts for @dev in @domain + * @domain: The interrupt domain + * @dev: The device for which to free interrupts + */ +void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev) +{ + msi_domain_free_irqs(domain, &dev->dev); +} +#endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */ diff --git a/include/linux/msi.h b/include/linux/msi.h index 0098e2c8fd42..1628788aee25 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -220,4 +220,18 @@ struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain); #endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ +#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN +void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg); +struct irq_domain *pci_msi_create_irq_domain(struct device_node *node, + struct msi_domain_info *info, + struct irq_domain *parent); +int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev, + int nvec, int type); +void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev); +irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, + struct msi_desc *desc); +int pci_msi_domain_check_cap(struct irq_domain *domain, + struct msi_domain_info *info, struct device *dev); +#endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */ + #endif /* LINUX_MSI_H */ -- cgit v1.2.3 From 8e047adae969701c6cec136484bb9de8572af934 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 15 Nov 2014 22:24:07 +0800 Subject: PCI/MSI: Provide mechanism to alloc/free MSI/MSIX interrupt from irqdomain Provide mechanism to directly alloc/free MSI/MSIX interrupt from irqdomain, which will be used to replace arch_setup_msi_irq()/ arch_setup_msi_irqs()/arch_teardown_msi_irq()/arch_teardown_msi_irqs(). To kill weak functions, this patch introduce a new weak function arch_get_pci_msi_domain(), which is to retrieve the MSI irqdomain for a PCI device. This weak function could be killed once we get a common way to associate MSI domain with PCI device. Signed-off-by: Jiang Liu Cc: Tony Luck Cc: linux-arm-kernel@lists.infradead.org Cc: Bjorn Helgaas Cc: Grant Likely Cc: Marc Zyngier Cc: Yijing Wang Cc: Yingjoe Chen Cc: Borislav Petkov Cc: Matthias Brugger Cc: Alexander Gordeev Link: http://lkml.kernel.org/r/1416061447-9472-10-git-send-email-jiang.liu@linux.intel.com Signed-off-by: Thomas Gleixner --- drivers/pci/msi.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++--- include/linux/msi.h | 3 +++ 2 files changed, 67 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 36b0c2e55929..4befe09053c1 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -28,6 +28,40 @@ int pci_msi_ignore_mask; #define msix_table_size(flags) ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) +#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN +static struct irq_domain *pci_msi_default_domain; +static DEFINE_MUTEX(pci_msi_domain_lock); + +struct irq_domain * __weak arch_get_pci_msi_domain(struct pci_dev *dev) +{ + return pci_msi_default_domain; +} + +static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ + struct irq_domain *domain; + + domain = arch_get_pci_msi_domain(dev); + if (domain) + return pci_msi_domain_alloc_irqs(domain, dev, nvec, type); + + return arch_setup_msi_irqs(dev, nvec, type); +} + +static void pci_msi_teardown_msi_irqs(struct pci_dev *dev) +{ + struct irq_domain *domain; + + domain = arch_get_pci_msi_domain(dev); + if (domain) + pci_msi_domain_free_irqs(domain, dev); + else + arch_teardown_msi_irqs(dev); +} +#else +#define pci_msi_setup_msi_irqs arch_setup_msi_irqs +#define pci_msi_teardown_msi_irqs arch_teardown_msi_irqs +#endif /* Arch hooks */ @@ -348,7 +382,7 @@ static void free_msi_irqs(struct pci_dev *dev) for (i = 0; i < entry->nvec_used; i++) BUG_ON(irq_has_action(entry->irq + i)); - arch_teardown_msi_irqs(dev); + pci_msi_teardown_msi_irqs(dev); list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { if (entry->msi_attrib.is_msix) { @@ -600,7 +634,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) list_add_tail(&entry->list, &dev->msi_list); /* Configure MSI capability structure */ - ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); + ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); if (ret) { msi_mask_irq(entry, mask, ~mask); free_msi_irqs(dev); @@ -715,7 +749,7 @@ static int msix_capability_init(struct pci_dev *dev, if (ret) return ret; - ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); + ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); if (ret) goto out_avail; @@ -1258,4 +1292,31 @@ void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev) { msi_domain_free_irqs(domain, &dev->dev); } + +/** + * pci_msi_create_default_irq_domain - Create a default MSI interrupt domain + * @node: Optional device-tree node of the interrupt controller + * @info: MSI domain info + * @parent: Parent irq domain + * + * Returns: A domain pointer or NULL in case of failure. If successful + * the default PCI/MSI irqdomain pointer is updated. + */ +struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node, + struct msi_domain_info *info, struct irq_domain *parent) +{ + struct irq_domain *domain; + + mutex_lock(&pci_msi_domain_lock); + if (pci_msi_default_domain) { + pr_err("PCI: default irq domain for PCI MSI has already been created.\n"); + domain = NULL; + } else { + domain = pci_msi_create_irq_domain(node, info, parent); + pci_msi_default_domain = domain; + } + mutex_unlock(&pci_msi_domain_lock); + + return domain; +} #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */ diff --git a/include/linux/msi.h b/include/linux/msi.h index 1628788aee25..692f217ae813 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -228,6 +228,9 @@ struct irq_domain *pci_msi_create_irq_domain(struct device_node *node, int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev, int nvec, int type); void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev); +struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node, + struct msi_domain_info *info, struct irq_domain *parent); + irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, struct msi_desc *desc); int pci_msi_domain_check_cap(struct irq_domain *domain, -- cgit v1.2.3 From 020c312658d61297ffe43b412441c69b1c36fb1b Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sat, 15 Nov 2014 10:49:12 +0000 Subject: PCI/MSI: Allow an msi_controller to be associated to an irq domain With the new stacked irq domains, it becomes pretty tempting to allocate an MSI domain per PCI bus, which would remove the requirement of either relying on arch-specific code, or a default PCI MSI domain. By allowing the msi_controller structure to carry a pointer to an irq_domain, we can easily use this in pci_msi_setup_msi_irqs. The existing code can still be used as a fallback if the MSI driver does not populate the domain field. Tested on arm64 with the GICv3 ITS driver. Signed-off-by: Marc Zyngier Cc: Yingjoe Chen Cc: Bjorn Helgaas Cc: linux-arm-kernel@lists.infradead.org Cc: Jiang Liu Link: http://lkml.kernel.org/r/1416048553-29289-2-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner --- drivers/pci/msi.c | 16 ++++++++++++++-- include/linux/msi.h | 3 +++ 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 4befe09053c1..476f4b1a2727 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -37,11 +37,23 @@ struct irq_domain * __weak arch_get_pci_msi_domain(struct pci_dev *dev) return pci_msi_default_domain; } +static struct irq_domain *pci_msi_get_domain(struct pci_dev *dev) +{ + struct irq_domain *domain = NULL; + + if (dev->bus->msi) + domain = dev->bus->msi->domain; + if (!domain) + domain = arch_get_pci_msi_domain(dev); + + return domain; +} + static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) { struct irq_domain *domain; - domain = arch_get_pci_msi_domain(dev); + domain = pci_msi_get_domain(dev); if (domain) return pci_msi_domain_alloc_irqs(domain, dev, nvec, type); @@ -52,7 +64,7 @@ static void pci_msi_teardown_msi_irqs(struct pci_dev *dev) { struct irq_domain *domain; - domain = arch_get_pci_msi_domain(dev); + domain = pci_msi_get_domain(dev); if (domain) pci_msi_domain_free_irqs(domain, dev); else diff --git a/include/linux/msi.h b/include/linux/msi.h index 692f217ae813..8ac4a68ffae2 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -108,6 +108,9 @@ struct msi_controller { struct device *dev; struct device_node *of_node; struct list_head list; +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN + struct irq_domain *domain; +#endif int (*setup_irq)(struct msi_controller *chip, struct pci_dev *dev, struct msi_desc *desc); -- cgit v1.2.3 From 74faaf7aa64c76b60db0f5c994fd43a46be772ce Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 6 Dec 2014 21:20:20 +0100 Subject: genirq: Move irq_chip_write_msi_msg() helper to core No point to expose this to the world. The only legitimate user is the core code. Signed-off-by: Thomas Gleixner Cc: Jiang Liu Cc: Marc Zyngier --- include/linux/irq.h | 6 ------ kernel/irq/msi.c | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 8badf34baf0f..33da579d727c 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -461,12 +461,6 @@ extern int irq_chip_set_affinity_parent(struct irq_data *data, bool force); #endif -static inline void irq_chip_write_msi_msg(struct irq_data *data, - struct msi_msg *msg) -{ - data->chip->irq_write_msi_msg(data, msg); -} - /* Handling of unhandled and spurious interrupts: */ extern void note_interrupt(unsigned int irq, struct irq_desc *desc, irqreturn_t action_ret); diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index f477a2f8ce56..3e18163f336f 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -32,6 +32,12 @@ void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg) EXPORT_SYMBOL_GPL(get_cached_msi_msg); #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +static inline void irq_chip_write_msi_msg(struct irq_data *data, + struct msi_msg *msg) +{ + data->chip->irq_write_msi_msg(data, msg); +} + /** * msi_domain_set_affinity - Generic affinity setter function for MSI domains * @irq_data: The irq data associated to the interrupt -- cgit v1.2.3