mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 04:02:20 +00:00
Interrupt subsystem updates:
- 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. -----BEGIN PGP SIGNATURE----- iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmNGxRYTHHRnbHhAbGlu dXRyb25peC5kZQAKCRCmGPVMDXSYoWJyD/0emJAlIuD0DzkEkoAtnHSq7eyGFMpI PFMyZ0IYXlVWuxEmQMyd7E9M+fmlRqnnhErg6x7jPW1bKzoyIn1A7eNE/cvhXPru BiTy6g2o7pNegUh5bQrE8p0Yyq6/HsVO4YyE3RGxpUQVh/qwB+RKnzUY6RfDj87z naQx10+15b+76SXvTQpIrvQTWhfTswk9un2MYDkjHctfVgjcnb/8dTPQuXsZrdTQ VBWWwjLpCKcqqQS1e9MQqmQKpVqGs/DGW8XNTPk3jI4QF1fIHjhNdcoI51/lM4Ri r912FPE8R48FS9g0dQgpMxGmHjikYpf3rXXosn8uyWkt5zNy6CXOEEg3DRIoAIdg czKve+bgZZXUK/QcSSdPuPthBoLKQCG5MZsVFNF8IArmPCHaiYcOQBe7pel3U4cc MpQe9yUXJI40XgwTAyAOlidjmD69384nEhzbI5d/AfJI5ssdXcBMrFN/xEeBDWdz Dg2+Yle9HNglxBA6E3GX3yiaCQJxHFhKMnqd1zhxWjXFRzkfGF7bBpRj1j+vXnzN ap/wMQuMlOWriWsH3UkZtFrC4PvgByGVfzlzYA076CjutyYfQolQ8k0bLHnp2VSu VWUn4WATfaxJcqij7vyI9BYtFXdrB/yYhFasDBepQbDgiy8WEAmX+bObvXWs9XYa UGVCNGsYx2TKMA== =2ok5 -----END PGP SIGNATURE----- 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() ...
This commit is contained in:
commit
60ac35bf6b
@ -0,0 +1,99 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/interrupt-controller/fsl,mu-msi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale/NXP i.MX Messaging Unit (MU) work as msi controller
|
||||
|
||||
maintainers:
|
||||
- Frank Li <Frank.Li@nxp.com>
|
||||
|
||||
description: |
|
||||
The Messaging Unit module enables two processors within the SoC to
|
||||
communicate and coordinate by passing messages (e.g. data, status
|
||||
and control) through the MU interface. The MU also provides the ability
|
||||
for one processor (A side) to signal the other processor (B side) using
|
||||
interrupts.
|
||||
|
||||
Because the MU manages the messaging between processors, the MU uses
|
||||
different clocks (from each side of the different peripheral buses).
|
||||
Therefore, the MU must synchronize the accesses from one side to the
|
||||
other. The MU accomplishes synchronization using two sets of matching
|
||||
registers (Processor A-side, Processor B-side).
|
||||
|
||||
MU can work as msi interrupt controller to do doorbell
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/interrupt-controller/msi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- fsl,imx6sx-mu-msi
|
||||
- fsl,imx7ulp-mu-msi
|
||||
- fsl,imx8ulp-mu-msi
|
||||
- fsl,imx8ulp-mu-msi-s4
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: a side register base address
|
||||
- description: b side register base address
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: processor-a-side
|
||||
- const: processor-b-side
|
||||
|
||||
interrupts:
|
||||
description: a side interrupt number.
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
items:
|
||||
- description: a side power domain
|
||||
- description: b side power domain
|
||||
|
||||
power-domain-names:
|
||||
items:
|
||||
- const: processor-a-side
|
||||
- const: processor-b-side
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
msi-controller: true
|
||||
|
||||
"#msi-cells":
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-controller
|
||||
- msi-controller
|
||||
- "#msi-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/firmware/imx/rsrc.h>
|
||||
|
||||
msi-controller@5d270000 {
|
||||
compatible = "fsl,imx6sx-mu-msi";
|
||||
msi-controller;
|
||||
#msi-cells = <0>;
|
||||
interrupt-controller;
|
||||
reg = <0x5d270000 0x10000>, /* A side */
|
||||
<0x5d300000 0x10000>; /* B side */
|
||||
reg-names = "processor-a-side", "processor-b-side";
|
||||
interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>;
|
||||
power-domains = <&pd IMX_SC_R_MU_12A>,
|
||||
<&pd IMX_SC_R_MU_12B>;
|
||||
power-domain-names = "processor-a-side", "processor-b-side";
|
||||
};
|
@ -6,6 +6,14 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Realtek RTL SoC interrupt controller devicetree bindings
|
||||
|
||||
description:
|
||||
Interrupt controller and router for Realtek MIPS SoCs, allowing each SoC
|
||||
interrupt to be routed to one parent CPU (hardware) interrupt, or left
|
||||
disconnected.
|
||||
All connected input lines from SoC peripherals can be masked individually,
|
||||
and an interrupt status register is present to indicate which interrupts are
|
||||
pending.
|
||||
|
||||
maintainers:
|
||||
- Birger Koblitz <mail@birger-koblitz.de>
|
||||
- Bert Vermeulen <bert@biot.com>
|
||||
@ -13,23 +21,33 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: realtek,rtl-intc
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- realtek,rtl8380-intc
|
||||
- const: realtek,rtl-intc
|
||||
- const: realtek,rtl-intc
|
||||
deprecated: true
|
||||
|
||||
"#interrupt-cells":
|
||||
description:
|
||||
SoC interrupt line index.
|
||||
const: 1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 15
|
||||
description:
|
||||
List of parent interrupts, in the order that they are connected to this
|
||||
interrupt router's outputs, starting at the first output.
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
"#address-cells":
|
||||
const: 0
|
||||
|
||||
interrupt-map:
|
||||
deprecated: true
|
||||
description: Describes mapping from SoC interrupts to CPU interrupts
|
||||
|
||||
required:
|
||||
@ -37,21 +55,33 @@ required:
|
||||
- reg
|
||||
- "#interrupt-cells"
|
||||
- interrupt-controller
|
||||
- "#address-cells"
|
||||
- interrupt-map
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: realtek,rtl-intc
|
||||
then:
|
||||
properties:
|
||||
"#address-cells":
|
||||
const: 0
|
||||
required:
|
||||
- "#address-cells"
|
||||
- interrupt-map
|
||||
else:
|
||||
required:
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
intc: interrupt-controller@3000 {
|
||||
compatible = "realtek,rtl-intc";
|
||||
interrupt-controller@3000 {
|
||||
compatible = "realtek,rtl8380-intc", "realtek,rtl-intc";
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-controller;
|
||||
reg = <0x3000 0x20>;
|
||||
#address-cells = <0>;
|
||||
interrupt-map =
|
||||
<31 &cpuintc 2>,
|
||||
<30 &cpuintc 1>,
|
||||
<29 &cpuintc 5>;
|
||||
reg = <0x3000 0x18>;
|
||||
|
||||
interrupt-parent = <&cpuintc>;
|
||||
interrupts = <2>, <3>, <4>, <5>, <6>;
|
||||
};
|
||||
|
@ -37,6 +37,7 @@ properties:
|
||||
- renesas,intc-ex-r8a77990 # R-Car E3
|
||||
- renesas,intc-ex-r8a77995 # R-Car D3
|
||||
- renesas,intc-ex-r8a779a0 # R-Car V3U
|
||||
- renesas,intc-ex-r8a779g0 # R-Car V4H
|
||||
- const: renesas,irqc
|
||||
|
||||
'#interrupt-cells':
|
||||
|
@ -59,6 +59,9 @@ properties:
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 0
|
||||
|
||||
msi-controller: true
|
||||
|
||||
ti,interrupt-ranges:
|
||||
|
@ -58,6 +58,9 @@ properties:
|
||||
1 = If intr supports edge triggered interrupts.
|
||||
4 = If intr supports level triggered interrupts.
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
'#interrupt-cells':
|
||||
|
@ -138,6 +138,7 @@ struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
|
||||
|
||||
return domain;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_msi_create_irq_domain);
|
||||
|
||||
static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
|
||||
irq_write_msi_msg_t write_msi_msg)
|
||||
|
@ -115,7 +115,7 @@ static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
|
||||
for_each_set_bit(gpio, &irqs, gc->ngpio)
|
||||
generic_handle_irq(irq_find_mapping(gc->irq.domain, gpio));
|
||||
generic_handle_domain_irq_safe(gc->irq.domain, gpio);
|
||||
bcma_chipco_gpio_polarity(cc, irqs, val & irqs);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
@ -273,10 +273,8 @@ static irqreturn_t mlxbf2_gpio_irq_handler(int irq, void *ptr)
|
||||
pending = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CAUSE_EVTEN0);
|
||||
writel(pending, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE);
|
||||
|
||||
for_each_set_bit(level, &pending, gc->ngpio) {
|
||||
int gpio_irq = irq_find_mapping(gc->irq.domain, level);
|
||||
generic_handle_irq(gpio_irq);
|
||||
}
|
||||
for_each_set_bit(level, &pending, gc->ngpio)
|
||||
generic_handle_domain_irq_safe(gc->irq.domain, level);
|
||||
|
||||
return IRQ_RETVAL(pending);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ menu "IRQ chip support"
|
||||
|
||||
config IRQCHIP
|
||||
def_bool y
|
||||
depends on OF_IRQ
|
||||
depends on (OF_IRQ || ACPI_GENERIC_GSI)
|
||||
|
||||
config ARM_GIC
|
||||
bool
|
||||
@ -481,6 +481,21 @@ config IMX_INTMUX
|
||||
help
|
||||
Support for the i.MX INTMUX interrupt multiplexer.
|
||||
|
||||
config IMX_MU_MSI
|
||||
tristate "i.MX MU used as MSI controller"
|
||||
depends on OF && HAS_IOMEM
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
default m if ARCH_MXC
|
||||
select IRQ_DOMAIN
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select GENERIC_MSI_IRQ_DOMAIN
|
||||
help
|
||||
Provide a driver for the i.MX Messaging Unit block used as a
|
||||
CPU-to-CPU MSI controller. This requires a specially crafted DT
|
||||
to make use of this driver.
|
||||
|
||||
If unsure, say N
|
||||
|
||||
config LS1X_IRQ
|
||||
bool "Loongson-1 Interrupt Controller"
|
||||
depends on MACH_LOONGSON32
|
||||
|
@ -99,6 +99,7 @@ obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o
|
||||
obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
|
||||
obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
|
||||
obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o
|
||||
obj-$(CONFIG_IMX_MU_MSI) += irq-imx-mu-msi.o
|
||||
obj-$(CONFIG_MADERA_IRQ) += irq-madera.o
|
||||
obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
|
||||
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
|
||||
|
@ -978,7 +978,7 @@ static int __gic_update_rdist_properties(struct redist_region *region,
|
||||
u64 typer = gic_read_typer(ptr + GICR_TYPER);
|
||||
u32 ctlr = readl_relaxed(ptr + GICR_CTLR);
|
||||
|
||||
/* Boot-time cleanip */
|
||||
/* Boot-time cleanup */
|
||||
if ((typer & GICR_TYPER_VLPIS) && (typer & GICR_TYPER_RVPEID)) {
|
||||
u64 val;
|
||||
|
||||
|
453
drivers/irqchip/irq-imx-mu-msi.c
Normal file
453
drivers/irqchip/irq-imx-mu-msi.c
Normal file
@ -0,0 +1,453 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Freescale MU used as MSI controller
|
||||
*
|
||||
* Copyright (c) 2018 Pengutronix, Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
* Copyright 2022 NXP
|
||||
* Frank Li <Frank.Li@nxp.com>
|
||||
* Peng Fan <peng.fan@nxp.com>
|
||||
*
|
||||
* Based on drivers/mailbox/imx-mailbox.c
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define IMX_MU_CHANS 4
|
||||
|
||||
enum imx_mu_xcr {
|
||||
IMX_MU_GIER,
|
||||
IMX_MU_GCR,
|
||||
IMX_MU_TCR,
|
||||
IMX_MU_RCR,
|
||||
IMX_MU_xCR_MAX,
|
||||
};
|
||||
|
||||
enum imx_mu_xsr {
|
||||
IMX_MU_SR,
|
||||
IMX_MU_GSR,
|
||||
IMX_MU_TSR,
|
||||
IMX_MU_RSR,
|
||||
IMX_MU_xSR_MAX
|
||||
};
|
||||
|
||||
enum imx_mu_type {
|
||||
IMX_MU_V2 = BIT(1),
|
||||
};
|
||||
|
||||
/* Receive Interrupt Enable */
|
||||
#define IMX_MU_xCR_RIEn(data, x) ((data->cfg->type) & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
|
||||
#define IMX_MU_xSR_RFn(data, x) ((data->cfg->type) & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
|
||||
|
||||
struct imx_mu_dcfg {
|
||||
enum imx_mu_type type;
|
||||
u32 xTR; /* Transmit Register0 */
|
||||
u32 xRR; /* Receive Register0 */
|
||||
u32 xSR[IMX_MU_xSR_MAX]; /* Status Registers */
|
||||
u32 xCR[IMX_MU_xCR_MAX]; /* Control Registers */
|
||||
};
|
||||
|
||||
struct imx_mu_msi {
|
||||
raw_spinlock_t lock;
|
||||
struct irq_domain *msi_domain;
|
||||
void __iomem *regs;
|
||||
phys_addr_t msiir_addr;
|
||||
const struct imx_mu_dcfg *cfg;
|
||||
unsigned long used;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static void imx_mu_write(struct imx_mu_msi *msi_data, u32 val, u32 offs)
|
||||
{
|
||||
iowrite32(val, msi_data->regs + offs);
|
||||
}
|
||||
|
||||
static u32 imx_mu_read(struct imx_mu_msi *msi_data, u32 offs)
|
||||
{
|
||||
return ioread32(msi_data->regs + offs);
|
||||
}
|
||||
|
||||
static u32 imx_mu_xcr_rmw(struct imx_mu_msi *msi_data, enum imx_mu_xcr type, u32 set, u32 clr)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
raw_spin_lock_irqsave(&msi_data->lock, flags);
|
||||
val = imx_mu_read(msi_data, msi_data->cfg->xCR[type]);
|
||||
val &= ~clr;
|
||||
val |= set;
|
||||
imx_mu_write(msi_data, val, msi_data->cfg->xCR[type]);
|
||||
raw_spin_unlock_irqrestore(&msi_data->lock, flags);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void imx_mu_msi_parent_mask_irq(struct irq_data *data)
|
||||
{
|
||||
struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data);
|
||||
|
||||
imx_mu_xcr_rmw(msi_data, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(msi_data, data->hwirq));
|
||||
}
|
||||
|
||||
static void imx_mu_msi_parent_unmask_irq(struct irq_data *data)
|
||||
{
|
||||
struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data);
|
||||
|
||||
imx_mu_xcr_rmw(msi_data, IMX_MU_RCR, IMX_MU_xCR_RIEn(msi_data, data->hwirq), 0);
|
||||
}
|
||||
|
||||
static void imx_mu_msi_parent_ack_irq(struct irq_data *data)
|
||||
{
|
||||
struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data);
|
||||
|
||||
imx_mu_read(msi_data, msi_data->cfg->xRR + data->hwirq * 4);
|
||||
}
|
||||
|
||||
static struct irq_chip imx_mu_msi_irq_chip = {
|
||||
.name = "MU-MSI",
|
||||
.irq_ack = irq_chip_ack_parent,
|
||||
};
|
||||
|
||||
static struct msi_domain_ops imx_mu_msi_irq_ops = {
|
||||
};
|
||||
|
||||
static struct msi_domain_info imx_mu_msi_domain_info = {
|
||||
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
|
||||
.ops = &imx_mu_msi_irq_ops,
|
||||
.chip = &imx_mu_msi_irq_chip,
|
||||
};
|
||||
|
||||
static void imx_mu_msi_parent_compose_msg(struct irq_data *data,
|
||||
struct msi_msg *msg)
|
||||
{
|
||||
struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(data);
|
||||
u64 addr = msi_data->msiir_addr + 4 * data->hwirq;
|
||||
|
||||
msg->address_hi = upper_32_bits(addr);
|
||||
msg->address_lo = lower_32_bits(addr);
|
||||
msg->data = data->hwirq;
|
||||
}
|
||||
|
||||
static int imx_mu_msi_parent_set_affinity(struct irq_data *irq_data,
|
||||
const struct cpumask *mask, bool force)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct irq_chip imx_mu_msi_parent_chip = {
|
||||
.name = "MU",
|
||||
.irq_mask = imx_mu_msi_parent_mask_irq,
|
||||
.irq_unmask = imx_mu_msi_parent_unmask_irq,
|
||||
.irq_ack = imx_mu_msi_parent_ack_irq,
|
||||
.irq_compose_msi_msg = imx_mu_msi_parent_compose_msg,
|
||||
.irq_set_affinity = imx_mu_msi_parent_set_affinity,
|
||||
};
|
||||
|
||||
static int imx_mu_msi_domain_irq_alloc(struct irq_domain *domain,
|
||||
unsigned int virq,
|
||||
unsigned int nr_irqs,
|
||||
void *args)
|
||||
{
|
||||
struct imx_mu_msi *msi_data = domain->host_data;
|
||||
unsigned long flags;
|
||||
int pos, err = 0;
|
||||
|
||||
WARN_ON(nr_irqs != 1);
|
||||
|
||||
raw_spin_lock_irqsave(&msi_data->lock, flags);
|
||||
pos = find_first_zero_bit(&msi_data->used, IMX_MU_CHANS);
|
||||
if (pos < IMX_MU_CHANS)
|
||||
__set_bit(pos, &msi_data->used);
|
||||
else
|
||||
err = -ENOSPC;
|
||||
raw_spin_unlock_irqrestore(&msi_data->lock, flags);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
irq_domain_set_info(domain, virq, pos,
|
||||
&imx_mu_msi_parent_chip, msi_data,
|
||||
handle_edge_irq, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void imx_mu_msi_domain_irq_free(struct irq_domain *domain,
|
||||
unsigned int virq, unsigned int nr_irqs)
|
||||
{
|
||||
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
|
||||
struct imx_mu_msi *msi_data = irq_data_get_irq_chip_data(d);
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&msi_data->lock, flags);
|
||||
__clear_bit(d->hwirq, &msi_data->used);
|
||||
raw_spin_unlock_irqrestore(&msi_data->lock, flags);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops imx_mu_msi_domain_ops = {
|
||||
.alloc = imx_mu_msi_domain_irq_alloc,
|
||||
.free = imx_mu_msi_domain_irq_free,
|
||||
};
|
||||
|
||||
static void imx_mu_msi_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct imx_mu_msi *msi_data = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
u32 status;
|
||||
int i;
|
||||
|
||||
status = imx_mu_read(msi_data, msi_data->cfg->xSR[IMX_MU_RSR]);
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
for (i = 0; i < IMX_MU_CHANS; i++) {
|
||||
if (status & IMX_MU_xSR_RFn(msi_data, i))
|
||||
generic_handle_domain_irq(msi_data->msi_domain, i);
|
||||
}
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static int imx_mu_msi_domains_init(struct imx_mu_msi *msi_data, struct device *dev)
|
||||
{
|
||||
struct fwnode_handle *fwnodes = dev_fwnode(dev);
|
||||
struct irq_domain *parent;
|
||||
|
||||
/* Initialize MSI domain parent */
|
||||
parent = irq_domain_create_linear(fwnodes,
|
||||
IMX_MU_CHANS,
|
||||
&imx_mu_msi_domain_ops,
|
||||
msi_data);
|
||||
if (!parent) {
|
||||
dev_err(dev, "failed to create IRQ domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS);
|
||||
|
||||
msi_data->msi_domain = platform_msi_create_irq_domain(fwnodes,
|
||||
&imx_mu_msi_domain_info,
|
||||
parent);
|
||||
|
||||
if (!msi_data->msi_domain) {
|
||||
dev_err(dev, "failed to create MSI domain\n");
|
||||
irq_domain_remove(parent);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
irq_domain_set_pm_device(msi_data->msi_domain, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Register offset of different version MU IP */
|
||||
static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = {
|
||||
.type = 0,
|
||||
.xTR = 0x0,
|
||||
.xRR = 0x10,
|
||||
.xSR = {
|
||||
[IMX_MU_SR] = 0x20,
|
||||
[IMX_MU_GSR] = 0x20,
|
||||
[IMX_MU_TSR] = 0x20,
|
||||
[IMX_MU_RSR] = 0x20,
|
||||
},
|
||||
.xCR = {
|
||||
[IMX_MU_GIER] = 0x24,
|
||||
[IMX_MU_GCR] = 0x24,
|
||||
[IMX_MU_TCR] = 0x24,
|
||||
[IMX_MU_RCR] = 0x24,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = {
|
||||
.type = 0,
|
||||
.xTR = 0x20,
|
||||
.xRR = 0x40,
|
||||
.xSR = {
|
||||
[IMX_MU_SR] = 0x60,
|
||||
[IMX_MU_GSR] = 0x60,
|
||||
[IMX_MU_TSR] = 0x60,
|
||||
[IMX_MU_RSR] = 0x60,
|
||||
},
|
||||
.xCR = {
|
||||
[IMX_MU_GIER] = 0x64,
|
||||
[IMX_MU_GCR] = 0x64,
|
||||
[IMX_MU_TCR] = 0x64,
|
||||
[IMX_MU_RCR] = 0x64,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
|
||||
.type = IMX_MU_V2,
|
||||
.xTR = 0x200,
|
||||
.xRR = 0x280,
|
||||
.xSR = {
|
||||
[IMX_MU_SR] = 0xC,
|
||||
[IMX_MU_GSR] = 0x118,
|
||||
[IMX_MU_TSR] = 0x124,
|
||||
[IMX_MU_RSR] = 0x12C,
|
||||
},
|
||||
.xCR = {
|
||||
[IMX_MU_GIER] = 0x110,
|
||||
[IMX_MU_GCR] = 0x114,
|
||||
[IMX_MU_TCR] = 0x120,
|
||||
[IMX_MU_RCR] = 0x128
|
||||
},
|
||||
};
|
||||
|
||||
static int __init imx_mu_of_init(struct device_node *dn,
|
||||
struct device_node *parent,
|
||||
const struct imx_mu_dcfg *cfg)
|
||||
{
|
||||
struct platform_device *pdev = of_find_device_by_node(dn);
|
||||
struct device_link *pd_link_a;
|
||||
struct device_link *pd_link_b;
|
||||
struct imx_mu_msi *msi_data;
|
||||
struct resource *res;
|
||||
struct device *pd_a;
|
||||
struct device *pd_b;
|
||||
struct device *dev;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
dev = &pdev->dev;
|
||||
|
||||
msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
|
||||
if (!msi_data)
|
||||
return -ENOMEM;
|
||||
|
||||
msi_data->cfg = cfg;
|
||||
|
||||
msi_data->regs = devm_platform_ioremap_resource_byname(pdev, "processor-a-side");
|
||||
if (IS_ERR(msi_data->regs)) {
|
||||
dev_err(&pdev->dev, "failed to initialize 'regs'\n");
|
||||
return PTR_ERR(msi_data->regs);
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "processor-b-side");
|
||||
if (!res)
|
||||
return -EIO;
|
||||
|
||||
msi_data->msiir_addr = res->start + msi_data->cfg->xTR;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0)
|
||||
return -ENODEV;
|
||||
|
||||
platform_set_drvdata(pdev, msi_data);
|
||||
|
||||
msi_data->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(msi_data->clk))
|
||||
return PTR_ERR(msi_data->clk);
|
||||
|
||||
pd_a = dev_pm_domain_attach_by_name(dev, "processor-a-side");
|
||||
if (IS_ERR(pd_a))
|
||||
return PTR_ERR(pd_a);
|
||||
|
||||
pd_b = dev_pm_domain_attach_by_name(dev, "processor-b-side");
|
||||
if (IS_ERR(pd_b))
|
||||
return PTR_ERR(pd_b);
|
||||
|
||||
pd_link_a = device_link_add(dev, pd_a,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
|
||||
if (!pd_link_a) {
|
||||
dev_err(dev, "Failed to add device_link to mu a.\n");
|
||||
goto err_pd_a;
|
||||
}
|
||||
|
||||
pd_link_b = device_link_add(dev, pd_b,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
|
||||
|
||||
if (!pd_link_b) {
|
||||
dev_err(dev, "Failed to add device_link to mu a.\n");
|
||||
goto err_pd_b;
|
||||
}
|
||||
|
||||
ret = imx_mu_msi_domains_init(msi_data, dev);
|
||||
if (ret)
|
||||
goto err_dm_init;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
irq_set_chained_handler_and_data(irq,
|
||||
imx_mu_msi_irq_handler,
|
||||
msi_data);
|
||||
|
||||
return 0;
|
||||
|
||||
err_dm_init:
|
||||
device_link_remove(dev, pd_b);
|
||||
err_pd_b:
|
||||
device_link_remove(dev, pd_a);
|
||||
err_pd_a:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx_mu_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct imx_mu_msi *priv = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx_mu_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct imx_mu_msi *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to enable clock\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops imx_mu_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend,
|
||||
imx_mu_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static int __init imx_mu_imx7ulp_of_init(struct device_node *dn,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx7ulp);
|
||||
}
|
||||
|
||||
static int __init imx_mu_imx6sx_of_init(struct device_node *dn,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx6sx);
|
||||
}
|
||||
|
||||
static int __init imx_mu_imx8ulp_of_init(struct device_node *dn,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx8ulp);
|
||||
}
|
||||
|
||||
IRQCHIP_PLATFORM_DRIVER_BEGIN(imx_mu_msi)
|
||||
IRQCHIP_MATCH("fsl,imx7ulp-mu-msi", imx_mu_imx7ulp_of_init)
|
||||
IRQCHIP_MATCH("fsl,imx6sx-mu-msi", imx_mu_imx6sx_of_init)
|
||||
IRQCHIP_MATCH("fsl,imx8ulp-mu-msi", imx_mu_imx8ulp_of_init)
|
||||
IRQCHIP_PLATFORM_DRIVER_END(imx_mu_msi, .pm = &imx_mu_pm_ops)
|
||||
|
||||
|
||||
MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>");
|
||||
MODULE_DESCRIPTION("Freescale MU MSI controller driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -6,8 +6,7 @@
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
@ -16,13 +15,41 @@
|
||||
#define LS1021A_SCFGREVCR 0x200
|
||||
|
||||
struct ls_extirq_data {
|
||||
struct regmap *syscon;
|
||||
u32 intpcr;
|
||||
void __iomem *intpcr;
|
||||
raw_spinlock_t lock;
|
||||
bool big_endian;
|
||||
bool is_ls1021a_or_ls1043a;
|
||||
u32 nirq;
|
||||
struct irq_fwspec map[MAXIRQ];
|
||||
};
|
||||
|
||||
static void ls_extirq_intpcr_rmw(struct ls_extirq_data *priv, u32 mask,
|
||||
u32 value)
|
||||
{
|
||||
u32 intpcr;
|
||||
|
||||
/*
|
||||
* Serialize concurrent calls to ls_extirq_set_type() from multiple
|
||||
* IRQ descriptors, making sure the read-modify-write is atomic.
|
||||
*/
|
||||
raw_spin_lock(&priv->lock);
|
||||
|
||||
if (priv->big_endian)
|
||||
intpcr = ioread32be(priv->intpcr);
|
||||
else
|
||||
intpcr = ioread32(priv->intpcr);
|
||||
|
||||
intpcr &= ~mask;
|
||||
intpcr |= value;
|
||||
|
||||
if (priv->big_endian)
|
||||
iowrite32be(intpcr, priv->intpcr);
|
||||
else
|
||||
iowrite32(intpcr, priv->intpcr);
|
||||
|
||||
raw_spin_unlock(&priv->lock);
|
||||
}
|
||||
|
||||
static int
|
||||
ls_extirq_set_type(struct irq_data *data, unsigned int type)
|
||||
{
|
||||
@ -51,7 +78,8 @@ ls_extirq_set_type(struct irq_data *data, unsigned int type)
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
regmap_update_bits(priv->syscon, priv->intpcr, mask, value);
|
||||
|
||||
ls_extirq_intpcr_rmw(priv, mask, value);
|
||||
|
||||
return irq_chip_set_type_parent(data, type);
|
||||
}
|
||||
@ -143,7 +171,6 @@ ls_extirq_parse_map(struct ls_extirq_data *priv, struct device_node *node)
|
||||
static int __init
|
||||
ls_extirq_of_init(struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
|
||||
struct irq_domain *domain, *parent_domain;
|
||||
struct ls_extirq_data *priv;
|
||||
int ret;
|
||||
@ -151,40 +178,52 @@ ls_extirq_of_init(struct device_node *node, struct device_node *parent)
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
pr_err("Cannot find parent domain\n");
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
goto err_irq_find_host;
|
||||
}
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->syscon = syscon_node_to_regmap(node->parent);
|
||||
if (IS_ERR(priv->syscon)) {
|
||||
ret = PTR_ERR(priv->syscon);
|
||||
pr_err("Failed to lookup parent regmap\n");
|
||||
goto out;
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc_priv;
|
||||
}
|
||||
ret = of_property_read_u32(node, "reg", &priv->intpcr);
|
||||
if (ret) {
|
||||
pr_err("Missing INTPCR offset value\n");
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* All extirq OF nodes are under a scfg/syscon node with
|
||||
* the 'ranges' property
|
||||
*/
|
||||
priv->intpcr = of_iomap(node, 0);
|
||||
if (!priv->intpcr) {
|
||||
pr_err("Cannot ioremap OF node %pOF\n", node);
|
||||
ret = -ENOMEM;
|
||||
goto err_iomap;
|
||||
}
|
||||
|
||||
ret = ls_extirq_parse_map(priv, node);
|
||||
if (ret)
|
||||
goto out;
|
||||
goto err_parse_map;
|
||||
|
||||
priv->big_endian = of_device_is_big_endian(parent);
|
||||
priv->is_ls1021a_or_ls1043a = of_device_is_compatible(node, "fsl,ls1021a-extirq") ||
|
||||
of_device_is_compatible(node, "fsl,ls1043a-extirq");
|
||||
raw_spin_lock_init(&priv->lock);
|
||||
|
||||
domain = irq_domain_add_hierarchy(parent_domain, 0, priv->nirq, node,
|
||||
&extirq_domain_ops, priv);
|
||||
if (!domain)
|
||||
if (!domain) {
|
||||
ret = -ENOMEM;
|
||||
goto err_add_hierarchy;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
kfree(priv);
|
||||
return 0;
|
||||
|
||||
err_add_hierarchy:
|
||||
err_parse_map:
|
||||
iounmap(priv->intpcr);
|
||||
err_iomap:
|
||||
kfree(priv);
|
||||
err_alloc_priv:
|
||||
err_irq_find_host:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -21,11 +21,33 @@
|
||||
#define RTL_ICTL_IRR2 0x10
|
||||
#define RTL_ICTL_IRR3 0x14
|
||||
|
||||
#define RTL_ICTL_NUM_INPUTS 32
|
||||
|
||||
#define REG(x) (realtek_ictl_base + x)
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(irq_lock);
|
||||
static void __iomem *realtek_ictl_base;
|
||||
|
||||
/*
|
||||
* IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering,
|
||||
* placing IRQ 31 in the first four bits. A routing value of '0' means the
|
||||
* interrupt is left disconnected. Routing values {1..15} connect to output
|
||||
* lines {0..14}.
|
||||
*/
|
||||
#define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32))
|
||||
#define IRR_SHIFT(idx) ((idx * 4) % 32)
|
||||
|
||||
static void write_irr(void __iomem *irr0, int idx, u32 value)
|
||||
{
|
||||
unsigned int offset = IRR_OFFSET(idx);
|
||||
unsigned int shift = IRR_SHIFT(idx);
|
||||
u32 irr;
|
||||
|
||||
irr = readl(irr0 + offset) & ~(0xf << shift);
|
||||
irr |= (value & 0xf) << shift;
|
||||
writel(irr, irr0 + offset);
|
||||
}
|
||||
|
||||
static void realtek_ictl_unmask_irq(struct irq_data *i)
|
||||
{
|
||||
unsigned long flags;
|
||||
@ -62,8 +84,14 @@ static struct irq_chip realtek_ictl_irq = {
|
||||
|
||||
static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq);
|
||||
|
||||
raw_spin_lock_irqsave(&irq_lock, flags);
|
||||
write_irr(REG(RTL_ICTL_IRR0), hw, 1);
|
||||
raw_spin_unlock_irqrestore(&irq_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -95,91 +123,51 @@ out:
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
/*
|
||||
* SoC interrupts are cascaded to MIPS CPU interrupts according to the
|
||||
* interrupt-map in the device tree. Each SoC interrupt gets 4 bits for
|
||||
* the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts
|
||||
* thus go into 4 IRRs. A routing value of '0' means the interrupt is left
|
||||
* disconnected. Routing values {1..15} connect to output lines {0..14}.
|
||||
*/
|
||||
static int __init map_interrupts(struct device_node *node, struct irq_domain *domain)
|
||||
{
|
||||
struct device_node *cpu_ictl;
|
||||
const __be32 *imap;
|
||||
u32 imaplen, soc_int, cpu_int, tmp, regs[4];
|
||||
int ret, i, irr_regs[] = {
|
||||
RTL_ICTL_IRR3,
|
||||
RTL_ICTL_IRR2,
|
||||
RTL_ICTL_IRR1,
|
||||
RTL_ICTL_IRR0,
|
||||
};
|
||||
u8 mips_irqs_set;
|
||||
|
||||
ret = of_property_read_u32(node, "#address-cells", &tmp);
|
||||
if (ret || tmp)
|
||||
return -EINVAL;
|
||||
|
||||
imap = of_get_property(node, "interrupt-map", &imaplen);
|
||||
if (!imap || imaplen % 3)
|
||||
return -EINVAL;
|
||||
|
||||
mips_irqs_set = 0;
|
||||
memset(regs, 0, sizeof(regs));
|
||||
for (i = 0; i < imaplen; i += 3 * sizeof(u32)) {
|
||||
soc_int = be32_to_cpup(imap);
|
||||
if (soc_int > 31)
|
||||
return -EINVAL;
|
||||
|
||||
cpu_ictl = of_find_node_by_phandle(be32_to_cpup(imap + 1));
|
||||
if (!cpu_ictl)
|
||||
return -EINVAL;
|
||||
ret = of_property_read_u32(cpu_ictl, "#interrupt-cells", &tmp);
|
||||
of_node_put(cpu_ictl);
|
||||
if (ret || tmp != 1)
|
||||
return -EINVAL;
|
||||
|
||||
cpu_int = be32_to_cpup(imap + 2);
|
||||
if (cpu_int > 7 || cpu_int < 2)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(mips_irqs_set & BIT(cpu_int))) {
|
||||
irq_set_chained_handler_and_data(cpu_int, realtek_irq_dispatch,
|
||||
domain);
|
||||
mips_irqs_set |= BIT(cpu_int);
|
||||
}
|
||||
|
||||
/* Use routing values (1..6) for CPU interrupts (2..7) */
|
||||
regs[(soc_int * 4) / 32] |= (cpu_int - 1) << (soc_int * 4) % 32;
|
||||
imap += 3;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
writel(regs[i], REG(irr_regs[i]));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
struct of_phandle_args oirq;
|
||||
struct irq_domain *domain;
|
||||
int ret;
|
||||
unsigned int soc_irq;
|
||||
int parent_irq;
|
||||
|
||||
realtek_ictl_base = of_iomap(node, 0);
|
||||
if (!realtek_ictl_base)
|
||||
return -ENXIO;
|
||||
|
||||
/* Disable all cascaded interrupts */
|
||||
/* Disable all cascaded interrupts and clear routing */
|
||||
writel(0, REG(RTL_ICTL_GIMR));
|
||||
for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++)
|
||||
write_irr(REG(RTL_ICTL_IRR0), soc_irq, 0);
|
||||
|
||||
domain = irq_domain_add_simple(node, 32, 0,
|
||||
&irq_domain_ops, NULL);
|
||||
if (WARN_ON(!of_irq_count(node))) {
|
||||
/*
|
||||
* If DT contains no parent interrupts, assume MIPS CPU IRQ 2
|
||||
* (HW0) is connected to the first output. This is the case for
|
||||
* all known hardware anyway. "interrupt-map" is deprecated, so
|
||||
* don't bother trying to parse that.
|
||||
*/
|
||||
oirq.np = of_find_compatible_node(NULL, NULL, "mti,cpu-interrupt-controller");
|
||||
oirq.args_count = 1;
|
||||
oirq.args[0] = 2;
|
||||
|
||||
ret = map_interrupts(node, domain);
|
||||
if (ret) {
|
||||
pr_err("invalid interrupt map\n");
|
||||
return ret;
|
||||
parent_irq = irq_create_of_mapping(&oirq);
|
||||
|
||||
of_node_put(oirq.np);
|
||||
} else {
|
||||
parent_irq = of_irq_get(node, 0);
|
||||
}
|
||||
|
||||
if (parent_irq < 0)
|
||||
return parent_irq;
|
||||
else if (!parent_irq)
|
||||
return -ENODEV;
|
||||
|
||||
domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, NULL);
|
||||
if (!domain)
|
||||
return -ENOMEM;
|
||||
|
||||
irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, domain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -639,7 +639,7 @@ static bool do_amd_gpio_irq_handler(int irq, void *dev_id)
|
||||
if (!(regval & PIN_IRQ_PENDING) ||
|
||||
!(regval & BIT(INTERRUPT_MASK_OFF)))
|
||||
continue;
|
||||
generic_handle_domain_irq(gc->irq.domain, irqnr + i);
|
||||
generic_handle_domain_irq_safe(gc->irq.domain, irqnr + i);
|
||||
|
||||
/* Clear interrupt.
|
||||
* We must read the pin register again, in case the
|
||||
|
@ -125,8 +125,7 @@ static irqreturn_t int0002_irq(int irq, void *data)
|
||||
if (!(gpe_sts_reg & GPE0A_PME_B0_STS_BIT))
|
||||
return IRQ_NONE;
|
||||
|
||||
generic_handle_irq(irq_find_mapping(chip->irq.domain,
|
||||
GPE0A_PME_B0_VIRT_GPIO_PIN));
|
||||
generic_handle_domain_irq_safe(chip->irq.domain, GPE0A_PME_B0_VIRT_GPIO_PIN);
|
||||
|
||||
pm_wakeup_hard_event(chip->parent);
|
||||
|
||||
|
@ -132,7 +132,8 @@ static irqreturn_t ssb_gpio_irq_chipco_handler(int irq, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
|
||||
for_each_set_bit(gpio, &irqs, bus->gpio.ngpio)
|
||||
generic_handle_irq(ssb_gpio_to_irq(&bus->gpio, gpio));
|
||||
generic_handle_domain_irq_safe(bus->irq_domain, gpio);
|
||||
|
||||
ssb_chipco_gpio_polarity(chipco, irqs, val & irqs);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -330,7 +331,8 @@ static irqreturn_t ssb_gpio_irq_extif_handler(int irq, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
|
||||
for_each_set_bit(gpio, &irqs, bus->gpio.ngpio)
|
||||
generic_handle_irq(ssb_gpio_to_irq(&bus->gpio, gpio));
|
||||
generic_handle_domain_irq_safe(bus->irq_domain, gpio);
|
||||
|
||||
ssb_extif_gpio_polarity(extif, irqs, val & irqs);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
@ -44,7 +44,8 @@ static const struct of_device_id drv_name##_irqchip_match_table[] = {
|
||||
#define IRQCHIP_MATCH(compat, fn) { .compatible = compat, \
|
||||
.data = typecheck_irq_init_cb(fn), },
|
||||
|
||||
#define IRQCHIP_PLATFORM_DRIVER_END(drv_name) \
|
||||
|
||||
#define IRQCHIP_PLATFORM_DRIVER_END(drv_name, ...) \
|
||||
{}, \
|
||||
}; \
|
||||
MODULE_DEVICE_TABLE(of, drv_name##_irqchip_match_table); \
|
||||
@ -56,6 +57,7 @@ static struct platform_driver drv_name##_driver = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.of_match_table = drv_name##_irqchip_match_table, \
|
||||
.suppress_bind_attrs = true, \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
}; \
|
||||
builtin_platform_driver(drv_name##_driver)
|
||||
|
@ -169,6 +169,7 @@ int generic_handle_irq_safe(unsigned int irq);
|
||||
* conversion failed.
|
||||
*/
|
||||
int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq);
|
||||
int generic_handle_domain_irq_safe(struct irq_domain *domain, unsigned int hwirq);
|
||||
int generic_handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq);
|
||||
#endif
|
||||
|
||||
|
@ -37,9 +37,8 @@ extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data);
|
||||
extern int of_irq_to_resource(struct device_node *dev, int index,
|
||||
struct resource *r);
|
||||
|
||||
extern void of_irq_init(const struct of_device_id *matches);
|
||||
|
||||
#ifdef CONFIG_OF_IRQ
|
||||
extern void of_irq_init(const struct of_device_id *matches);
|
||||
extern int of_irq_parse_one(struct device_node *device, int index,
|
||||
struct of_phandle_args *out_irq);
|
||||
extern int of_irq_count(struct device_node *dev);
|
||||
@ -57,6 +56,9 @@ extern struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
|
||||
extern void of_msi_configure(struct device *dev, struct device_node *np);
|
||||
u32 of_msi_map_id(struct device *dev, struct device_node *msi_np, u32 id_in);
|
||||
#else
|
||||
static inline void of_irq_init(const struct of_device_id *matches)
|
||||
{
|
||||
}
|
||||
static inline int of_irq_parse_one(struct device_node *device, int index,
|
||||
struct of_phandle_args *out_irq)
|
||||
{
|
||||
|
@ -705,6 +705,30 @@ int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(generic_handle_domain_irq);
|
||||
|
||||
/**
|
||||
* generic_handle_irq_safe - Invoke the handler for a HW irq belonging
|
||||
* to a domain from any context.
|
||||
* @domain: The domain where to perform the lookup
|
||||
* @hwirq: The HW irq number to convert to a logical one
|
||||
*
|
||||
* Returns: 0 on success, a negative value on error.
|
||||
*
|
||||
* This function can be called from any context (IRQ or process
|
||||
* context). If the interrupt is marked as 'enforce IRQ-context only' then
|
||||
* the function must be invoked from hard interrupt context.
|
||||
*/
|
||||
int generic_handle_domain_irq_safe(struct irq_domain *domain, unsigned int hwirq)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
local_irq_save(flags);
|
||||
ret = handle_irq_desc(irq_resolve_mapping(domain, hwirq));
|
||||
local_irq_restore(flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(generic_handle_domain_irq_safe);
|
||||
|
||||
/**
|
||||
* generic_handle_domain_nmi - Invoke the handler for a HW nmi belonging
|
||||
* to a domain.
|
||||
|
Loading…
Reference in New Issue
Block a user