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:
Linus Torvalds 2022-10-12 10:23:24 -07:00
commit 60ac35bf6b
21 changed files with 787 additions and 126 deletions

View File

@ -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";
};

View File

@ -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>;
};

View File

@ -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':

View File

@ -59,6 +59,9 @@ properties:
interrupt-controller: true
'#interrupt-cells':
const: 0
msi-controller: true
ti,interrupt-ranges:

View File

@ -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':

View File

@ -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)

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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;

View 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");

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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)
{

View File

@ -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.