Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'irq-core-2022-10-12' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull interrupt updates from Thomas Gleixner:
"Core code:

- Provide a generic wrapper which can be utilized in drivers to
handle the problem of force threaded demultiplex interrupts on RT
enabled kernels. This avoids conditionals and horrible quirks in
drivers all over the place

- Fix up affected pinctrl and GPIO drivers to make them cleanly RT
safe

Interrupt drivers:

- A new driver for the FSL MU platform specific MSI implementation

- Make irqchip_init() available for pure ACPI based systems

- Provide a functional DT binding for the Realtek RTL interrupt chip

- The usual DT updates and small code improvements all over the
place"

* tag 'irq-core-2022-10-12' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (21 commits)
irqchip: IMX_MU_MSI should depend on ARCH_MXC
irqchip/imx-mu-msi: Fix wrong register offset for 8ulp
irqchip/ls-extirq: Fix invalid wait context by avoiding to use regmap
dt-bindings: irqchip: Describe the IMX MU block as a MSI controller
irqchip: Add IMX MU MSI controller driver
dt-bindings: irqchip: renesas,irqc: Add r8a779g0 support
irqchip/gic-v3: Fix typo in comment
dt-bindings: interrupt-controller: ti,sci-intr: Fix missing reg property in the binding
dt-bindings: irqchip: ti,sci-inta: Fix warning for missing #interrupt-cells
irqchip: Allow extra fields to be passed to IRQCHIP_PLATFORM_DRIVER_END
platform-msi: Export symbol platform_msi_create_irq_domain()
irqchip/realtek-rtl: use parent interrupts
dt-bindings: interrupt-controller: realtek,rtl-intc: require parents
irqchip/realtek-rtl: use irq_domain_add_linear()
irqchip: Make irqchip_init() usable on pure ACPI systems
bcma: gpio: Use generic_handle_irq_safe()
gpio: mlxbf2: Use generic_handle_irq_safe()
platform/x86: intel_int0002_vgpio: Use generic_handle_irq_safe()
ssb: gpio: Use generic_handle_irq_safe()
pinctrl: amd: Use generic_handle_irq_safe()
...

+787 -126
+99
Documentation/devicetree/bindings/interrupt-controller/fsl,mu-msi.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/interrupt-controller/fsl,mu-msi.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Freescale/NXP i.MX Messaging Unit (MU) work as msi controller 8 + 9 + maintainers: 10 + - Frank Li <Frank.Li@nxp.com> 11 + 12 + description: | 13 + The Messaging Unit module enables two processors within the SoC to 14 + communicate and coordinate by passing messages (e.g. data, status 15 + and control) through the MU interface. The MU also provides the ability 16 + for one processor (A side) to signal the other processor (B side) using 17 + interrupts. 18 + 19 + Because the MU manages the messaging between processors, the MU uses 20 + different clocks (from each side of the different peripheral buses). 21 + Therefore, the MU must synchronize the accesses from one side to the 22 + other. The MU accomplishes synchronization using two sets of matching 23 + registers (Processor A-side, Processor B-side). 24 + 25 + MU can work as msi interrupt controller to do doorbell 26 + 27 + allOf: 28 + - $ref: /schemas/interrupt-controller/msi-controller.yaml# 29 + 30 + properties: 31 + compatible: 32 + enum: 33 + - fsl,imx6sx-mu-msi 34 + - fsl,imx7ulp-mu-msi 35 + - fsl,imx8ulp-mu-msi 36 + - fsl,imx8ulp-mu-msi-s4 37 + 38 + reg: 39 + items: 40 + - description: a side register base address 41 + - description: b side register base address 42 + 43 + reg-names: 44 + items: 45 + - const: processor-a-side 46 + - const: processor-b-side 47 + 48 + interrupts: 49 + description: a side interrupt number. 50 + maxItems: 1 51 + 52 + clocks: 53 + maxItems: 1 54 + 55 + power-domains: 56 + items: 57 + - description: a side power domain 58 + - description: b side power domain 59 + 60 + power-domain-names: 61 + items: 62 + - const: processor-a-side 63 + - const: processor-b-side 64 + 65 + interrupt-controller: true 66 + 67 + msi-controller: true 68 + 69 + "#msi-cells": 70 + const: 0 71 + 72 + required: 73 + - compatible 74 + - reg 75 + - interrupts 76 + - interrupt-controller 77 + - msi-controller 78 + - "#msi-cells" 79 + 80 + additionalProperties: false 81 + 82 + examples: 83 + - | 84 + #include <dt-bindings/interrupt-controller/arm-gic.h> 85 + #include <dt-bindings/firmware/imx/rsrc.h> 86 + 87 + msi-controller@5d270000 { 88 + compatible = "fsl,imx6sx-mu-msi"; 89 + msi-controller; 90 + #msi-cells = <0>; 91 + interrupt-controller; 92 + reg = <0x5d270000 0x10000>, /* A side */ 93 + <0x5d300000 0x10000>; /* B side */ 94 + reg-names = "processor-a-side", "processor-b-side"; 95 + interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>; 96 + power-domains = <&pd IMX_SC_R_MU_12A>, 97 + <&pd IMX_SC_R_MU_12B>; 98 + power-domain-names = "processor-a-side", "processor-b-side"; 99 + };
+45 -15
Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml
··· 6 6 7 7 title: Realtek RTL SoC interrupt controller devicetree bindings 8 8 9 + description: 10 + Interrupt controller and router for Realtek MIPS SoCs, allowing each SoC 11 + interrupt to be routed to one parent CPU (hardware) interrupt, or left 12 + disconnected. 13 + All connected input lines from SoC peripherals can be masked individually, 14 + and an interrupt status register is present to indicate which interrupts are 15 + pending. 16 + 9 17 maintainers: 10 18 - Birger Koblitz <mail@birger-koblitz.de> 11 19 - Bert Vermeulen <bert@biot.com> ··· 21 13 22 14 properties: 23 15 compatible: 24 - const: realtek,rtl-intc 16 + oneOf: 17 + - items: 18 + - enum: 19 + - realtek,rtl8380-intc 20 + - const: realtek,rtl-intc 21 + - const: realtek,rtl-intc 22 + deprecated: true 25 23 26 24 "#interrupt-cells": 25 + description: 26 + SoC interrupt line index. 27 27 const: 1 28 28 29 29 reg: 30 30 maxItems: 1 31 31 32 32 interrupts: 33 - maxItems: 1 33 + minItems: 1 34 + maxItems: 15 35 + description: 36 + List of parent interrupts, in the order that they are connected to this 37 + interrupt router's outputs, starting at the first output. 34 38 35 39 interrupt-controller: true 36 40 37 - "#address-cells": 38 - const: 0 39 - 40 41 interrupt-map: 42 + deprecated: true 41 43 description: Describes mapping from SoC interrupts to CPU interrupts 42 44 43 45 required: ··· 55 37 - reg 56 38 - "#interrupt-cells" 57 39 - interrupt-controller 58 - - "#address-cells" 59 - - interrupt-map 40 + 41 + allOf: 42 + - if: 43 + properties: 44 + compatible: 45 + const: realtek,rtl-intc 46 + then: 47 + properties: 48 + "#address-cells": 49 + const: 0 50 + required: 51 + - "#address-cells" 52 + - interrupt-map 53 + else: 54 + required: 55 + - interrupts 60 56 61 57 additionalProperties: false 62 58 63 59 examples: 64 60 - | 65 - intc: interrupt-controller@3000 { 66 - compatible = "realtek,rtl-intc"; 61 + interrupt-controller@3000 { 62 + compatible = "realtek,rtl8380-intc", "realtek,rtl-intc"; 67 63 #interrupt-cells = <1>; 68 64 interrupt-controller; 69 - reg = <0x3000 0x20>; 70 - #address-cells = <0>; 71 - interrupt-map = 72 - <31 &cpuintc 2>, 73 - <30 &cpuintc 1>, 74 - <29 &cpuintc 5>; 65 + reg = <0x3000 0x18>; 66 + 67 + interrupt-parent = <&cpuintc>; 68 + interrupts = <2>, <3>, <4>, <5>, <6>; 75 69 };
+1
Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.yaml
··· 37 37 - renesas,intc-ex-r8a77990 # R-Car E3 38 38 - renesas,intc-ex-r8a77995 # R-Car D3 39 39 - renesas,intc-ex-r8a779a0 # R-Car V3U 40 + - renesas,intc-ex-r8a779g0 # R-Car V4H 40 41 - const: renesas,irqc 41 42 42 43 '#interrupt-cells':
+3
Documentation/devicetree/bindings/interrupt-controller/ti,sci-inta.yaml
··· 59 59 60 60 interrupt-controller: true 61 61 62 + '#interrupt-cells': 63 + const: 0 64 + 62 65 msi-controller: true 63 66 64 67 ti,interrupt-ranges:
+3
Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.yaml
··· 58 58 1 = If intr supports edge triggered interrupts. 59 59 4 = If intr supports level triggered interrupts. 60 60 61 + reg: 62 + maxItems: 1 63 + 61 64 interrupt-controller: true 62 65 63 66 '#interrupt-cells':
+1
drivers/base/platform-msi.c
··· 138 138 139 139 return domain; 140 140 } 141 + EXPORT_SYMBOL_GPL(platform_msi_create_irq_domain); 141 142 142 143 static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec, 143 144 irq_write_msi_msg_t write_msi_msg)
+1 -1
drivers/bcma/driver_gpio.c
··· 115 115 return IRQ_NONE; 116 116 117 117 for_each_set_bit(gpio, &irqs, gc->ngpio) 118 - generic_handle_irq(irq_find_mapping(gc->irq.domain, gpio)); 118 + generic_handle_domain_irq_safe(gc->irq.domain, gpio); 119 119 bcma_chipco_gpio_polarity(cc, irqs, val & irqs); 120 120 121 121 return IRQ_HANDLED;
+2 -4
drivers/gpio/gpio-mlxbf2.c
··· 273 273 pending = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CAUSE_EVTEN0); 274 274 writel(pending, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); 275 275 276 - for_each_set_bit(level, &pending, gc->ngpio) { 277 - int gpio_irq = irq_find_mapping(gc->irq.domain, level); 278 - generic_handle_irq(gpio_irq); 279 - } 276 + for_each_set_bit(level, &pending, gc->ngpio) 277 + generic_handle_domain_irq_safe(gc->irq.domain, level); 280 278 281 279 return IRQ_RETVAL(pending); 282 280 }
+16 -1
drivers/irqchip/Kconfig
··· 3 3 4 4 config IRQCHIP 5 5 def_bool y 6 - depends on OF_IRQ 6 + depends on (OF_IRQ || ACPI_GENERIC_GSI) 7 7 8 8 config ARM_GIC 9 9 bool ··· 480 480 select IRQ_DOMAIN 481 481 help 482 482 Support for the i.MX INTMUX interrupt multiplexer. 483 + 484 + config IMX_MU_MSI 485 + tristate "i.MX MU used as MSI controller" 486 + depends on OF && HAS_IOMEM 487 + depends on ARCH_MXC || COMPILE_TEST 488 + default m if ARCH_MXC 489 + select IRQ_DOMAIN 490 + select IRQ_DOMAIN_HIERARCHY 491 + select GENERIC_MSI_IRQ_DOMAIN 492 + help 493 + Provide a driver for the i.MX Messaging Unit block used as a 494 + CPU-to-CPU MSI controller. This requires a specially crafted DT 495 + to make use of this driver. 496 + 497 + If unsure, say N 483 498 484 499 config LS1X_IRQ 485 500 bool "Loongson-1 Interrupt Controller"
+1
drivers/irqchip/Makefile
··· 99 99 obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o 100 100 obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o 101 101 obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o 102 + obj-$(CONFIG_IMX_MU_MSI) += irq-imx-mu-msi.o 102 103 obj-$(CONFIG_MADERA_IRQ) += irq-madera.o 103 104 obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o 104 105 obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
+1 -1
drivers/irqchip/irq-gic-v3.c
··· 978 978 u64 typer = gic_read_typer(ptr + GICR_TYPER); 979 979 u32 ctlr = readl_relaxed(ptr + GICR_CTLR); 980 980 981 - /* Boot-time cleanip */ 981 + /* Boot-time cleanup */ 982 982 if ((typer & GICR_TYPER_VLPIS) && (typer & GICR_TYPER_RVPEID)) { 983 983 u64 val; 984 984
+453
drivers/irqchip/irq-imx-mu-msi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Freescale MU used as MSI controller 4 + * 5 + * Copyright (c) 2018 Pengutronix, Oleksij Rempel <o.rempel@pengutronix.de> 6 + * Copyright 2022 NXP 7 + * Frank Li <Frank.Li@nxp.com> 8 + * Peng Fan <peng.fan@nxp.com> 9 + * 10 + * Based on drivers/mailbox/imx-mailbox.c 11 + */ 12 + 13 + #include <linux/clk.h> 14 + #include <linux/irq.h> 15 + #include <linux/irqchip.h> 16 + #include <linux/irqchip/chained_irq.h> 17 + #include <linux/irqdomain.h> 18 + #include <linux/kernel.h> 19 + #include <linux/module.h> 20 + #include <linux/msi.h> 21 + #include <linux/of_irq.h> 22 + #include <linux/of_platform.h> 23 + #include <linux/pm_runtime.h> 24 + #include <linux/pm_domain.h> 25 + #include <linux/spinlock.h> 26 + 27 + #define IMX_MU_CHANS 4 28 + 29 + enum imx_mu_xcr { 30 + IMX_MU_GIER, 31 + IMX_MU_GCR, 32 + IMX_MU_TCR, 33 + IMX_MU_RCR, 34 + IMX_MU_xCR_MAX, 35 + }; 36 + 37 + enum imx_mu_xsr { 38 + IMX_MU_SR, 39 + IMX_MU_GSR, 40 + IMX_MU_TSR, 41 + IMX_MU_RSR, 42 + IMX_MU_xSR_MAX 43 + }; 44 + 45 + enum imx_mu_type { 46 + IMX_MU_V2 = BIT(1), 47 + }; 48 + 49 + /* Receive Interrupt Enable */ 50 + #define IMX_MU_xCR_RIEn(data, x) ((data->cfg->type) & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) 51 + #define IMX_MU_xSR_RFn(data, x) ((data->cfg->type) & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) 52 + 53 + struct imx_mu_dcfg { 54 + enum imx_mu_type type; 55 + u32 xTR; /* Transmit Register0 */ 56 + u32 xRR; /* Receive Register0 */ 57 + u32 xSR[IMX_MU_xSR_MAX]; /* Status Registers */ 58 + u32 xCR[IMX_MU_xCR_MAX]; /* Control Registers */ 59 + }; 60 + 61 + struct imx_mu_msi { 62 + raw_spinlock_t lock; 63 + struct irq_domain *msi_domain; 64 + void __iomem *regs; 65 + phys_addr_t msiir_addr; 66 + const struct imx_mu_dcfg *cfg; 67 + unsigned long used; 68 + struct clk *clk; 69 + }; 70 + 71 + static void imx_mu_write(struct imx_mu_msi *msi_data, u32 val, u32 offs) 72 + { 73 + iowrite32(val, msi_data->regs + offs); 74 + } 75 + 76 + static u32 imx_mu_read(struct imx_mu_msi *msi_data, u32 offs) 77 + { 78 + return ioread32(msi_data->regs + offs); 79 + } 80 + 81 + static u32 imx_mu_xcr_rmw(struct imx_mu_msi *msi_data, enum imx_mu_xcr type, u32 set, u32 clr) 82 + { 83 + unsigned long flags; 84 + u32 val; 85 + 86 + raw_spin_lock_irqsave(&msi_data->lock, flags); 87 + val = imx_mu_read(msi_data, msi_data->cfg->xCR[type]); 88 + val &= ~clr; 89 + val |= set; 90 + imx_mu_write(msi_data, val, msi_data->cfg->xCR[type]); 91 + raw_spin_unlock_irqrestore(&msi_data->lock, flags); 92 + 93 + return val; 94 + } 95 + 96 + static void imx_mu_msi_parent_mask_irq(struct irq_data *data) 97 + { 98 + struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data); 99 + 100 + imx_mu_xcr_rmw(msi_data, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(msi_data, data->hwirq)); 101 + } 102 + 103 + static void imx_mu_msi_parent_unmask_irq(struct irq_data *data) 104 + { 105 + struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data); 106 + 107 + imx_mu_xcr_rmw(msi_data, IMX_MU_RCR, IMX_MU_xCR_RIEn(msi_data, data->hwirq), 0); 108 + } 109 + 110 + static void imx_mu_msi_parent_ack_irq(struct irq_data *data) 111 + { 112 + struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data); 113 + 114 + imx_mu_read(msi_data, msi_data->cfg->xRR + data->hwirq * 4); 115 + } 116 + 117 + static struct irq_chip imx_mu_msi_irq_chip = { 118 + .name = "MU-MSI", 119 + .irq_ack = irq_chip_ack_parent, 120 + }; 121 + 122 + static struct msi_domain_ops imx_mu_msi_irq_ops = { 123 + }; 124 + 125 + static struct msi_domain_info imx_mu_msi_domain_info = { 126 + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), 127 + .ops = &imx_mu_msi_irq_ops, 128 + .chip = &imx_mu_msi_irq_chip, 129 + }; 130 + 131 + static void imx_mu_msi_parent_compose_msg(struct irq_data *data, 132 + struct msi_msg *msg) 133 + { 134 + struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data); 135 + u64 addr = msi_data->msiir_addr + 4 * data->hwirq; 136 + 137 + msg->address_hi = upper_32_bits(addr); 138 + msg->address_lo = lower_32_bits(addr); 139 + msg->data = data->hwirq; 140 + } 141 + 142 + static int imx_mu_msi_parent_set_affinity(struct irq_data *irq_data, 143 + const struct cpumask *mask, bool force) 144 + { 145 + return -EINVAL; 146 + } 147 + 148 + static struct irq_chip imx_mu_msi_parent_chip = { 149 + .name = "MU", 150 + .irq_mask = imx_mu_msi_parent_mask_irq, 151 + .irq_unmask = imx_mu_msi_parent_unmask_irq, 152 + .irq_ack = imx_mu_msi_parent_ack_irq, 153 + .irq_compose_msi_msg = imx_mu_msi_parent_compose_msg, 154 + .irq_set_affinity = imx_mu_msi_parent_set_affinity, 155 + }; 156 + 157 + static int imx_mu_msi_domain_irq_alloc(struct irq_domain *domain, 158 + unsigned int virq, 159 + unsigned int nr_irqs, 160 + void *args) 161 + { 162 + struct imx_mu_msi *msi_data = domain->host_data; 163 + unsigned long flags; 164 + int pos, err = 0; 165 + 166 + WARN_ON(nr_irqs != 1); 167 + 168 + raw_spin_lock_irqsave(&msi_data->lock, flags); 169 + pos = find_first_zero_bit(&msi_data->used, IMX_MU_CHANS); 170 + if (pos < IMX_MU_CHANS) 171 + __set_bit(pos, &msi_data->used); 172 + else 173 + err = -ENOSPC; 174 + raw_spin_unlock_irqrestore(&msi_data->lock, flags); 175 + 176 + if (err) 177 + return err; 178 + 179 + irq_domain_set_info(domain, virq, pos, 180 + &imx_mu_msi_parent_chip, msi_data, 181 + handle_edge_irq, NULL, NULL); 182 + return 0; 183 + } 184 + 185 + static void imx_mu_msi_domain_irq_free(struct irq_domain *domain, 186 + unsigned int virq, unsigned int nr_irqs) 187 + { 188 + struct irq_data *d = irq_domain_get_irq_data(domain, virq); 189 + struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(d); 190 + unsigned long flags; 191 + 192 + raw_spin_lock_irqsave(&msi_data->lock, flags); 193 + __clear_bit(d->hwirq, &msi_data->used); 194 + raw_spin_unlock_irqrestore(&msi_data->lock, flags); 195 + } 196 + 197 + static const struct irq_domain_ops imx_mu_msi_domain_ops = { 198 + .alloc = imx_mu_msi_domain_irq_alloc, 199 + .free = imx_mu_msi_domain_irq_free, 200 + }; 201 + 202 + static void imx_mu_msi_irq_handler(struct irq_desc *desc) 203 + { 204 + struct imx_mu_msi *msi_data = irq_desc_get_handler_data(desc); 205 + struct irq_chip *chip = irq_desc_get_chip(desc); 206 + u32 status; 207 + int i; 208 + 209 + status = imx_mu_read(msi_data, msi_data->cfg->xSR[IMX_MU_RSR]); 210 + 211 + chained_irq_enter(chip, desc); 212 + for (i = 0; i < IMX_MU_CHANS; i++) { 213 + if (status & IMX_MU_xSR_RFn(msi_data, i)) 214 + generic_handle_domain_irq(msi_data->msi_domain, i); 215 + } 216 + chained_irq_exit(chip, desc); 217 + } 218 + 219 + static int imx_mu_msi_domains_init(struct imx_mu_msi *msi_data, struct device *dev) 220 + { 221 + struct fwnode_handle *fwnodes = dev_fwnode(dev); 222 + struct irq_domain *parent; 223 + 224 + /* Initialize MSI domain parent */ 225 + parent = irq_domain_create_linear(fwnodes, 226 + IMX_MU_CHANS, 227 + &imx_mu_msi_domain_ops, 228 + msi_data); 229 + if (!parent) { 230 + dev_err(dev, "failed to create IRQ domain\n"); 231 + return -ENOMEM; 232 + } 233 + 234 + irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS); 235 + 236 + msi_data->msi_domain = platform_msi_create_irq_domain(fwnodes, 237 + &imx_mu_msi_domain_info, 238 + parent); 239 + 240 + if (!msi_data->msi_domain) { 241 + dev_err(dev, "failed to create MSI domain\n"); 242 + irq_domain_remove(parent); 243 + return -ENOMEM; 244 + } 245 + 246 + irq_domain_set_pm_device(msi_data->msi_domain, dev); 247 + 248 + return 0; 249 + } 250 + 251 + /* Register offset of different version MU IP */ 252 + static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = { 253 + .type = 0, 254 + .xTR = 0x0, 255 + .xRR = 0x10, 256 + .xSR = { 257 + [IMX_MU_SR] = 0x20, 258 + [IMX_MU_GSR] = 0x20, 259 + [IMX_MU_TSR] = 0x20, 260 + [IMX_MU_RSR] = 0x20, 261 + }, 262 + .xCR = { 263 + [IMX_MU_GIER] = 0x24, 264 + [IMX_MU_GCR] = 0x24, 265 + [IMX_MU_TCR] = 0x24, 266 + [IMX_MU_RCR] = 0x24, 267 + }, 268 + }; 269 + 270 + static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = { 271 + .type = 0, 272 + .xTR = 0x20, 273 + .xRR = 0x40, 274 + .xSR = { 275 + [IMX_MU_SR] = 0x60, 276 + [IMX_MU_GSR] = 0x60, 277 + [IMX_MU_TSR] = 0x60, 278 + [IMX_MU_RSR] = 0x60, 279 + }, 280 + .xCR = { 281 + [IMX_MU_GIER] = 0x64, 282 + [IMX_MU_GCR] = 0x64, 283 + [IMX_MU_TCR] = 0x64, 284 + [IMX_MU_RCR] = 0x64, 285 + }, 286 + }; 287 + 288 + static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = { 289 + .type = IMX_MU_V2, 290 + .xTR = 0x200, 291 + .xRR = 0x280, 292 + .xSR = { 293 + [IMX_MU_SR] = 0xC, 294 + [IMX_MU_GSR] = 0x118, 295 + [IMX_MU_TSR] = 0x124, 296 + [IMX_MU_RSR] = 0x12C, 297 + }, 298 + .xCR = { 299 + [IMX_MU_GIER] = 0x110, 300 + [IMX_MU_GCR] = 0x114, 301 + [IMX_MU_TCR] = 0x120, 302 + [IMX_MU_RCR] = 0x128 303 + }, 304 + }; 305 + 306 + static int __init imx_mu_of_init(struct device_node *dn, 307 + struct device_node *parent, 308 + const struct imx_mu_dcfg *cfg) 309 + { 310 + struct platform_device *pdev = of_find_device_by_node(dn); 311 + struct device_link *pd_link_a; 312 + struct device_link *pd_link_b; 313 + struct imx_mu_msi *msi_data; 314 + struct resource *res; 315 + struct device *pd_a; 316 + struct device *pd_b; 317 + struct device *dev; 318 + int ret; 319 + int irq; 320 + 321 + dev = &pdev->dev; 322 + 323 + msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL); 324 + if (!msi_data) 325 + return -ENOMEM; 326 + 327 + msi_data->cfg = cfg; 328 + 329 + msi_data->regs = devm_platform_ioremap_resource_byname(pdev, "processor-a-side"); 330 + if (IS_ERR(msi_data->regs)) { 331 + dev_err(&pdev->dev, "failed to initialize 'regs'\n"); 332 + return PTR_ERR(msi_data->regs); 333 + } 334 + 335 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "processor-b-side"); 336 + if (!res) 337 + return -EIO; 338 + 339 + msi_data->msiir_addr = res->start + msi_data->cfg->xTR; 340 + 341 + irq = platform_get_irq(pdev, 0); 342 + if (irq <= 0) 343 + return -ENODEV; 344 + 345 + platform_set_drvdata(pdev, msi_data); 346 + 347 + msi_data->clk = devm_clk_get(dev, NULL); 348 + if (IS_ERR(msi_data->clk)) 349 + return PTR_ERR(msi_data->clk); 350 + 351 + pd_a = dev_pm_domain_attach_by_name(dev, "processor-a-side"); 352 + if (IS_ERR(pd_a)) 353 + return PTR_ERR(pd_a); 354 + 355 + pd_b = dev_pm_domain_attach_by_name(dev, "processor-b-side"); 356 + if (IS_ERR(pd_b)) 357 + return PTR_ERR(pd_b); 358 + 359 + pd_link_a = device_link_add(dev, pd_a, 360 + DL_FLAG_STATELESS | 361 + DL_FLAG_PM_RUNTIME | 362 + DL_FLAG_RPM_ACTIVE); 363 + 364 + if (!pd_link_a) { 365 + dev_err(dev, "Failed to add device_link to mu a.\n"); 366 + goto err_pd_a; 367 + } 368 + 369 + pd_link_b = device_link_add(dev, pd_b, 370 + DL_FLAG_STATELESS | 371 + DL_FLAG_PM_RUNTIME | 372 + DL_FLAG_RPM_ACTIVE); 373 + 374 + 375 + if (!pd_link_b) { 376 + dev_err(dev, "Failed to add device_link to mu a.\n"); 377 + goto err_pd_b; 378 + } 379 + 380 + ret = imx_mu_msi_domains_init(msi_data, dev); 381 + if (ret) 382 + goto err_dm_init; 383 + 384 + pm_runtime_enable(dev); 385 + 386 + irq_set_chained_handler_and_data(irq, 387 + imx_mu_msi_irq_handler, 388 + msi_data); 389 + 390 + return 0; 391 + 392 + err_dm_init: 393 + device_link_remove(dev, pd_b); 394 + err_pd_b: 395 + device_link_remove(dev, pd_a); 396 + err_pd_a: 397 + return -EINVAL; 398 + } 399 + 400 + static int __maybe_unused imx_mu_runtime_suspend(struct device *dev) 401 + { 402 + struct imx_mu_msi *priv = dev_get_drvdata(dev); 403 + 404 + clk_disable_unprepare(priv->clk); 405 + 406 + return 0; 407 + } 408 + 409 + static int __maybe_unused imx_mu_runtime_resume(struct device *dev) 410 + { 411 + struct imx_mu_msi *priv = dev_get_drvdata(dev); 412 + int ret; 413 + 414 + ret = clk_prepare_enable(priv->clk); 415 + if (ret) 416 + dev_err(dev, "failed to enable clock\n"); 417 + 418 + return ret; 419 + } 420 + 421 + static const struct dev_pm_ops imx_mu_pm_ops = { 422 + SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend, 423 + imx_mu_runtime_resume, NULL) 424 + }; 425 + 426 + static int __init imx_mu_imx7ulp_of_init(struct device_node *dn, 427 + struct device_node *parent) 428 + { 429 + return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx7ulp); 430 + } 431 + 432 + static int __init imx_mu_imx6sx_of_init(struct device_node *dn, 433 + struct device_node *parent) 434 + { 435 + return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx6sx); 436 + } 437 + 438 + static int __init imx_mu_imx8ulp_of_init(struct device_node *dn, 439 + struct device_node *parent) 440 + { 441 + return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx8ulp); 442 + } 443 + 444 + IRQCHIP_PLATFORM_DRIVER_BEGIN(imx_mu_msi) 445 + IRQCHIP_MATCH("fsl,imx7ulp-mu-msi", imx_mu_imx7ulp_of_init) 446 + IRQCHIP_MATCH("fsl,imx6sx-mu-msi", imx_mu_imx6sx_of_init) 447 + IRQCHIP_MATCH("fsl,imx8ulp-mu-msi", imx_mu_imx8ulp_of_init) 448 + IRQCHIP_PLATFORM_DRIVER_END(imx_mu_msi, .pm = &imx_mu_pm_ops) 449 + 450 + 451 + MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>"); 452 + MODULE_DESCRIPTION("Freescale MU MSI controller driver"); 453 + MODULE_LICENSE("GPL");
+63 -24
drivers/irqchip/irq-ls-extirq.c
··· 6 6 #include <linux/irqchip.h> 7 7 #include <linux/irqdomain.h> 8 8 #include <linux/of.h> 9 - #include <linux/mfd/syscon.h> 10 - #include <linux/regmap.h> 9 + #include <linux/of_address.h> 11 10 #include <linux/slab.h> 12 11 13 12 #include <dt-bindings/interrupt-controller/arm-gic.h> ··· 15 16 #define LS1021A_SCFGREVCR 0x200 16 17 17 18 struct ls_extirq_data { 18 - struct regmap *syscon; 19 - u32 intpcr; 19 + void __iomem *intpcr; 20 + raw_spinlock_t lock; 21 + bool big_endian; 20 22 bool is_ls1021a_or_ls1043a; 21 23 u32 nirq; 22 24 struct irq_fwspec map[MAXIRQ]; 23 25 }; 26 + 27 + static void ls_extirq_intpcr_rmw(struct ls_extirq_data *priv, u32 mask, 28 + u32 value) 29 + { 30 + u32 intpcr; 31 + 32 + /* 33 + * Serialize concurrent calls to ls_extirq_set_type() from multiple 34 + * IRQ descriptors, making sure the read-modify-write is atomic. 35 + */ 36 + raw_spin_lock(&priv->lock); 37 + 38 + if (priv->big_endian) 39 + intpcr = ioread32be(priv->intpcr); 40 + else 41 + intpcr = ioread32(priv->intpcr); 42 + 43 + intpcr &= ~mask; 44 + intpcr |= value; 45 + 46 + if (priv->big_endian) 47 + iowrite32be(intpcr, priv->intpcr); 48 + else 49 + iowrite32(intpcr, priv->intpcr); 50 + 51 + raw_spin_unlock(&priv->lock); 52 + } 24 53 25 54 static int 26 55 ls_extirq_set_type(struct irq_data *data, unsigned int type) ··· 78 51 default: 79 52 return -EINVAL; 80 53 } 81 - regmap_update_bits(priv->syscon, priv->intpcr, mask, value); 54 + 55 + ls_extirq_intpcr_rmw(priv, mask, value); 82 56 83 57 return irq_chip_set_type_parent(data, type); 84 58 } ··· 171 143 static int __init 172 144 ls_extirq_of_init(struct device_node *node, struct device_node *parent) 173 145 { 174 - 175 146 struct irq_domain *domain, *parent_domain; 176 147 struct ls_extirq_data *priv; 177 148 int ret; ··· 178 151 parent_domain = irq_find_host(parent); 179 152 if (!parent_domain) { 180 153 pr_err("Cannot find parent domain\n"); 181 - return -ENODEV; 154 + ret = -ENODEV; 155 + goto err_irq_find_host; 182 156 } 183 157 184 158 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 185 - if (!priv) 186 - return -ENOMEM; 187 - 188 - priv->syscon = syscon_node_to_regmap(node->parent); 189 - if (IS_ERR(priv->syscon)) { 190 - ret = PTR_ERR(priv->syscon); 191 - pr_err("Failed to lookup parent regmap\n"); 192 - goto out; 159 + if (!priv) { 160 + ret = -ENOMEM; 161 + goto err_alloc_priv; 193 162 } 194 - ret = of_property_read_u32(node, "reg", &priv->intpcr); 195 - if (ret) { 196 - pr_err("Missing INTPCR offset value\n"); 197 - goto out; 163 + 164 + /* 165 + * All extirq OF nodes are under a scfg/syscon node with 166 + * the 'ranges' property 167 + */ 168 + priv->intpcr = of_iomap(node, 0); 169 + if (!priv->intpcr) { 170 + pr_err("Cannot ioremap OF node %pOF\n", node); 171 + ret = -ENOMEM; 172 + goto err_iomap; 198 173 } 199 174 200 175 ret = ls_extirq_parse_map(priv, node); 201 176 if (ret) 202 - goto out; 177 + goto err_parse_map; 203 178 179 + priv->big_endian = of_device_is_big_endian(parent); 204 180 priv->is_ls1021a_or_ls1043a = of_device_is_compatible(node, "fsl,ls1021a-extirq") || 205 181 of_device_is_compatible(node, "fsl,ls1043a-extirq"); 182 + raw_spin_lock_init(&priv->lock); 206 183 207 184 domain = irq_domain_add_hierarchy(parent_domain, 0, priv->nirq, node, 208 185 &extirq_domain_ops, priv); 209 - if (!domain) 186 + if (!domain) { 210 187 ret = -ENOMEM; 188 + goto err_add_hierarchy; 189 + } 211 190 212 - out: 213 - if (ret) 214 - kfree(priv); 191 + return 0; 192 + 193 + err_add_hierarchy: 194 + err_parse_map: 195 + iounmap(priv->intpcr); 196 + err_iomap: 197 + kfree(priv); 198 + err_alloc_priv: 199 + err_irq_find_host: 215 200 return ret; 216 201 } 217 202
+60 -72
drivers/irqchip/irq-realtek-rtl.c
··· 21 21 #define RTL_ICTL_IRR2 0x10 22 22 #define RTL_ICTL_IRR3 0x14 23 23 24 + #define RTL_ICTL_NUM_INPUTS 32 25 + 24 26 #define REG(x) (realtek_ictl_base + x) 25 27 26 28 static DEFINE_RAW_SPINLOCK(irq_lock); 27 29 static void __iomem *realtek_ictl_base; 30 + 31 + /* 32 + * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering, 33 + * placing IRQ 31 in the first four bits. A routing value of '0' means the 34 + * interrupt is left disconnected. Routing values {1..15} connect to output 35 + * lines {0..14}. 36 + */ 37 + #define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32)) 38 + #define IRR_SHIFT(idx) ((idx * 4) % 32) 39 + 40 + static void write_irr(void __iomem *irr0, int idx, u32 value) 41 + { 42 + unsigned int offset = IRR_OFFSET(idx); 43 + unsigned int shift = IRR_SHIFT(idx); 44 + u32 irr; 45 + 46 + irr = readl(irr0 + offset) & ~(0xf << shift); 47 + irr |= (value & 0xf) << shift; 48 + writel(irr, irr0 + offset); 49 + } 28 50 29 51 static void realtek_ictl_unmask_irq(struct irq_data *i) 30 52 { ··· 84 62 85 63 static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) 86 64 { 65 + unsigned long flags; 66 + 87 67 irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq); 68 + 69 + raw_spin_lock_irqsave(&irq_lock, flags); 70 + write_irr(REG(RTL_ICTL_IRR0), hw, 1); 71 + raw_spin_unlock_irqrestore(&irq_lock, flags); 88 72 89 73 return 0; 90 74 } ··· 123 95 chained_irq_exit(chip, desc); 124 96 } 125 97 126 - /* 127 - * SoC interrupts are cascaded to MIPS CPU interrupts according to the 128 - * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for 129 - * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts 130 - * thus go into 4 IRRs. A routing value of '0' means the interrupt is left 131 - * disconnected. Routing values {1..15} connect to output lines {0..14}. 132 - */ 133 - static int __init map_interrupts(struct device_node *node, struct irq_domain *domain) 134 - { 135 - struct device_node *cpu_ictl; 136 - const __be32 *imap; 137 - u32 imaplen, soc_int, cpu_int, tmp, regs[4]; 138 - int ret, i, irr_regs[] = { 139 - RTL_ICTL_IRR3, 140 - RTL_ICTL_IRR2, 141 - RTL_ICTL_IRR1, 142 - RTL_ICTL_IRR0, 143 - }; 144 - u8 mips_irqs_set; 145 - 146 - ret = of_property_read_u32(node, "#address-cells", &tmp); 147 - if (ret || tmp) 148 - return -EINVAL; 149 - 150 - imap = of_get_property(node, "interrupt-map", &imaplen); 151 - if (!imap || imaplen % 3) 152 - return -EINVAL; 153 - 154 - mips_irqs_set = 0; 155 - memset(regs, 0, sizeof(regs)); 156 - for (i = 0; i < imaplen; i += 3 * sizeof(u32)) { 157 - soc_int = be32_to_cpup(imap); 158 - if (soc_int > 31) 159 - return -EINVAL; 160 - 161 - cpu_ictl = of_find_node_by_phandle(be32_to_cpup(imap + 1)); 162 - if (!cpu_ictl) 163 - return -EINVAL; 164 - ret = of_property_read_u32(cpu_ictl, "#interrupt-cells", &tmp); 165 - of_node_put(cpu_ictl); 166 - if (ret || tmp != 1) 167 - return -EINVAL; 168 - 169 - cpu_int = be32_to_cpup(imap + 2); 170 - if (cpu_int > 7 || cpu_int < 2) 171 - return -EINVAL; 172 - 173 - if (!(mips_irqs_set & BIT(cpu_int))) { 174 - irq_set_chained_handler_and_data(cpu_int, realtek_irq_dispatch, 175 - domain); 176 - mips_irqs_set |= BIT(cpu_int); 177 - } 178 - 179 - /* Use routing values (1..6) for CPU interrupts (2..7) */ 180 - regs[(soc_int * 4) / 32] |= (cpu_int - 1) << (soc_int * 4) % 32; 181 - imap += 3; 182 - } 183 - 184 - for (i = 0; i < 4; i++) 185 - writel(regs[i], REG(irr_regs[i])); 186 - 187 - return 0; 188 - } 189 - 190 98 static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent) 191 99 { 100 + struct of_phandle_args oirq; 192 101 struct irq_domain *domain; 193 - int ret; 102 + unsigned int soc_irq; 103 + int parent_irq; 194 104 195 105 realtek_ictl_base = of_iomap(node, 0); 196 106 if (!realtek_ictl_base) 197 107 return -ENXIO; 198 108 199 - /* Disable all cascaded interrupts */ 109 + /* Disable all cascaded interrupts and clear routing */ 200 110 writel(0, REG(RTL_ICTL_GIMR)); 111 + for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) 112 + write_irr(REG(RTL_ICTL_IRR0), soc_irq, 0); 201 113 202 - domain = irq_domain_add_simple(node, 32, 0, 203 - &irq_domain_ops, NULL); 114 + if (WARN_ON(!of_irq_count(node))) { 115 + /* 116 + * If DT contains no parent interrupts, assume MIPS CPU IRQ 2 117 + * (HW0) is connected to the first output. This is the case for 118 + * all known hardware anyway. "interrupt-map" is deprecated, so 119 + * don't bother trying to parse that. 120 + */ 121 + oirq.np = of_find_compatible_node(NULL, NULL, "mti,cpu-interrupt-controller"); 122 + oirq.args_count = 1; 123 + oirq.args[0] = 2; 204 124 205 - ret = map_interrupts(node, domain); 206 - if (ret) { 207 - pr_err("invalid interrupt map\n"); 208 - return ret; 125 + parent_irq = irq_create_of_mapping(&oirq); 126 + 127 + of_node_put(oirq.np); 128 + } else { 129 + parent_irq = of_irq_get(node, 0); 209 130 } 131 + 132 + if (parent_irq < 0) 133 + return parent_irq; 134 + else if (!parent_irq) 135 + return -ENODEV; 136 + 137 + domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, NULL); 138 + if (!domain) 139 + return -ENOMEM; 140 + 141 + irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, domain); 210 142 211 143 return 0; 212 144 }
+1 -1
drivers/pinctrl/pinctrl-amd.c
··· 639 639 if (!(regval & PIN_IRQ_PENDING) || 640 640 !(regval & BIT(INTERRUPT_MASK_OFF))) 641 641 continue; 642 - generic_handle_domain_irq(gc->irq.domain, irqnr + i); 642 + generic_handle_domain_irq_safe(gc->irq.domain, irqnr + i); 643 643 644 644 /* Clear interrupt. 645 645 * We must read the pin register again, in case the
+1 -2
drivers/platform/x86/intel/int0002_vgpio.c
··· 125 125 if (!(gpe_sts_reg & GPE0A_PME_B0_STS_BIT)) 126 126 return IRQ_NONE; 127 127 128 - generic_handle_irq(irq_find_mapping(chip->irq.domain, 129 - GPE0A_PME_B0_VIRT_GPIO_PIN)); 128 + generic_handle_domain_irq_safe(chip->irq.domain, GPE0A_PME_B0_VIRT_GPIO_PIN); 130 129 131 130 pm_wakeup_hard_event(chip->parent); 132 131
+4 -2
drivers/ssb/driver_gpio.c
··· 132 132 return IRQ_NONE; 133 133 134 134 for_each_set_bit(gpio, &irqs, bus->gpio.ngpio) 135 - generic_handle_irq(ssb_gpio_to_irq(&bus->gpio, gpio)); 135 + generic_handle_domain_irq_safe(bus->irq_domain, gpio); 136 + 136 137 ssb_chipco_gpio_polarity(chipco, irqs, val & irqs); 137 138 138 139 return IRQ_HANDLED; ··· 331 330 return IRQ_NONE; 332 331 333 332 for_each_set_bit(gpio, &irqs, bus->gpio.ngpio) 334 - generic_handle_irq(ssb_gpio_to_irq(&bus->gpio, gpio)); 333 + generic_handle_domain_irq_safe(bus->irq_domain, gpio); 334 + 335 335 ssb_extif_gpio_polarity(extif, irqs, val & irqs); 336 336 337 337 return IRQ_HANDLED;
+3 -1
include/linux/irqchip.h
··· 44 44 #define IRQCHIP_MATCH(compat, fn) { .compatible = compat, \ 45 45 .data = typecheck_irq_init_cb(fn), }, 46 46 47 - #define IRQCHIP_PLATFORM_DRIVER_END(drv_name) \ 47 + 48 + #define IRQCHIP_PLATFORM_DRIVER_END(drv_name, ...) \ 48 49 {}, \ 49 50 }; \ 50 51 MODULE_DEVICE_TABLE(of, drv_name##_irqchip_match_table); \ ··· 57 56 .owner = THIS_MODULE, \ 58 57 .of_match_table = drv_name##_irqchip_match_table, \ 59 58 .suppress_bind_attrs = true, \ 59 + __VA_ARGS__ \ 60 60 }, \ 61 61 }; \ 62 62 builtin_platform_driver(drv_name##_driver)
+1
include/linux/irqdesc.h
··· 169 169 * conversion failed. 170 170 */ 171 171 int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq); 172 + int generic_handle_domain_irq_safe(struct irq_domain *domain, unsigned int hwirq); 172 173 int generic_handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq); 173 174 #endif 174 175
+4 -2
include/linux/of_irq.h
··· 37 37 extern int of_irq_to_resource(struct device_node *dev, int index, 38 38 struct resource *r); 39 39 40 - extern void of_irq_init(const struct of_device_id *matches); 41 - 42 40 #ifdef CONFIG_OF_IRQ 41 + extern void of_irq_init(const struct of_device_id *matches); 43 42 extern int of_irq_parse_one(struct device_node *device, int index, 44 43 struct of_phandle_args *out_irq); 45 44 extern int of_irq_count(struct device_node *dev); ··· 56 57 extern void of_msi_configure(struct device *dev, struct device_node *np); 57 58 u32 of_msi_map_id(struct device *dev, struct device_node *msi_np, u32 id_in); 58 59 #else 60 + static inline void of_irq_init(const struct of_device_id *matches) 61 + { 62 + } 59 63 static inline int of_irq_parse_one(struct device_node *device, int index, 60 64 struct of_phandle_args *out_irq) 61 65 {
+24
kernel/irq/irqdesc.c
··· 705 705 } 706 706 EXPORT_SYMBOL_GPL(generic_handle_domain_irq); 707 707 708 + /** 709 + * generic_handle_irq_safe - Invoke the handler for a HW irq belonging 710 + * to a domain from any context. 711 + * @domain: The domain where to perform the lookup 712 + * @hwirq: The HW irq number to convert to a logical one 713 + * 714 + * Returns: 0 on success, a negative value on error. 715 + * 716 + * This function can be called from any context (IRQ or process 717 + * context). If the interrupt is marked as 'enforce IRQ-context only' then 718 + * the function must be invoked from hard interrupt context. 719 + */ 720 + int generic_handle_domain_irq_safe(struct irq_domain *domain, unsigned int hwirq) 721 + { 722 + unsigned long flags; 723 + int ret; 724 + 725 + local_irq_save(flags); 726 + ret = handle_irq_desc(irq_resolve_mapping(domain, hwirq)); 727 + local_irq_restore(flags); 728 + return ret; 729 + } 730 + EXPORT_SYMBOL_GPL(generic_handle_domain_irq_safe); 731 + 708 732 /** 709 733 * generic_handle_domain_nmi - Invoke the handler for a HW nmi belonging 710 734 * to a domain.