Commit 36de4f94 authored by Thomas Gleixner's avatar Thomas Gleixner

Merge tag 'irqchip-6.1' of...

Merge tag 'irqchip-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core

Pull irqchip updates from Marc Zyngier:

 - A new driver for the FSL MU widget that provides platform MSI

 - An update for the Realtek RTL irqchip to use a DT binding that
   actually describes the hardware

 - A handful of DT updates, as well as minor code and spelling fixes

Link: https://lore.kernel.org/r/20221002125554.3902840-1-maz@kernel.org
parents 94ec234a 732d69c8
# 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# ...@@ -6,6 +6,14 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Realtek RTL SoC interrupt controller devicetree bindings 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: maintainers:
- Birger Koblitz <mail@birger-koblitz.de> - Birger Koblitz <mail@birger-koblitz.de>
- Bert Vermeulen <bert@biot.com> - Bert Vermeulen <bert@biot.com>
...@@ -13,23 +21,33 @@ maintainers: ...@@ -13,23 +21,33 @@ maintainers:
properties: properties:
compatible: compatible:
const: realtek,rtl-intc oneOf:
- items:
- enum:
- realtek,rtl8380-intc
- const: realtek,rtl-intc
- const: realtek,rtl-intc
deprecated: true
"#interrupt-cells": "#interrupt-cells":
description:
SoC interrupt line index.
const: 1 const: 1
reg: reg:
maxItems: 1 maxItems: 1
interrupts: 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 interrupt-controller: true
"#address-cells":
const: 0
interrupt-map: interrupt-map:
deprecated: true
description: Describes mapping from SoC interrupts to CPU interrupts description: Describes mapping from SoC interrupts to CPU interrupts
required: required:
...@@ -37,21 +55,33 @@ required: ...@@ -37,21 +55,33 @@ required:
- reg - reg
- "#interrupt-cells" - "#interrupt-cells"
- interrupt-controller - 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 additionalProperties: false
examples: examples:
- | - |
intc: interrupt-controller@3000 { interrupt-controller@3000 {
compatible = "realtek,rtl-intc"; compatible = "realtek,rtl8380-intc", "realtek,rtl-intc";
#interrupt-cells = <1>; #interrupt-cells = <1>;
interrupt-controller; interrupt-controller;
reg = <0x3000 0x20>; reg = <0x3000 0x18>;
#address-cells = <0>;
interrupt-map = interrupt-parent = <&cpuintc>;
<31 &cpuintc 2>, interrupts = <2>, <3>, <4>, <5>, <6>;
<30 &cpuintc 1>,
<29 &cpuintc 5>;
}; };
...@@ -37,6 +37,7 @@ properties: ...@@ -37,6 +37,7 @@ properties:
- renesas,intc-ex-r8a77990 # R-Car E3 - renesas,intc-ex-r8a77990 # R-Car E3
- renesas,intc-ex-r8a77995 # R-Car D3 - renesas,intc-ex-r8a77995 # R-Car D3
- renesas,intc-ex-r8a779a0 # R-Car V3U - renesas,intc-ex-r8a779a0 # R-Car V3U
- renesas,intc-ex-r8a779g0 # R-Car V4H
- const: renesas,irqc - const: renesas,irqc
'#interrupt-cells': '#interrupt-cells':
......
...@@ -59,6 +59,9 @@ properties: ...@@ -59,6 +59,9 @@ properties:
interrupt-controller: true interrupt-controller: true
'#interrupt-cells':
const: 0
msi-controller: true msi-controller: true
ti,interrupt-ranges: ti,interrupt-ranges:
......
...@@ -58,6 +58,9 @@ properties: ...@@ -58,6 +58,9 @@ properties:
1 = If intr supports edge triggered interrupts. 1 = If intr supports edge triggered interrupts.
4 = If intr supports level triggered interrupts. 4 = If intr supports level triggered interrupts.
reg:
maxItems: 1
interrupt-controller: true interrupt-controller: true
'#interrupt-cells': '#interrupt-cells':
......
...@@ -138,6 +138,7 @@ struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode, ...@@ -138,6 +138,7 @@ struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
return domain; return domain;
} }
EXPORT_SYMBOL_GPL(platform_msi_create_irq_domain);
static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec, static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
irq_write_msi_msg_t write_msi_msg) irq_write_msi_msg_t write_msi_msg)
......
...@@ -3,7 +3,7 @@ menu "IRQ chip support" ...@@ -3,7 +3,7 @@ menu "IRQ chip support"
config IRQCHIP config IRQCHIP
def_bool y def_bool y
depends on OF_IRQ depends on (OF_IRQ || ACPI_GENERIC_GSI)
config ARM_GIC config ARM_GIC
bool bool
...@@ -481,6 +481,20 @@ config IMX_INTMUX ...@@ -481,6 +481,20 @@ config IMX_INTMUX
help help
Support for the i.MX INTMUX interrupt multiplexer. 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
default m if ARCH_MXC
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
select GENERIC_MSI_IRQ_DOMAIN
help
Provide a driver for the MU 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 config LS1X_IRQ
bool "Loongson-1 Interrupt Controller" bool "Loongson-1 Interrupt Controller"
depends on MACH_LOONGSON32 depends on MACH_LOONGSON32
......
...@@ -99,6 +99,7 @@ obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o ...@@ -99,6 +99,7 @@ obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o
obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.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_MADERA_IRQ) += irq-madera.o
obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.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, ...@@ -978,7 +978,7 @@ static int __gic_update_rdist_properties(struct redist_region *region,
u64 typer = gic_read_typer(ptr + GICR_TYPER); u64 typer = gic_read_typer(ptr + GICR_TYPER);
u32 ctlr = readl_relaxed(ptr + GICR_CTLR); u32 ctlr = readl_relaxed(ptr + GICR_CTLR);
/* Boot-time cleanip */ /* Boot-time cleanup */
if ((typer & GICR_TYPER_VLPIS) && (typer & GICR_TYPER_RVPEID)) { if ((typer & GICR_TYPER_VLPIS) && (typer & GICR_TYPER_RVPEID)) {
u64 val; u64 val;
......
// 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_GSR] = 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");
...@@ -21,11 +21,33 @@ ...@@ -21,11 +21,33 @@
#define RTL_ICTL_IRR2 0x10 #define RTL_ICTL_IRR2 0x10
#define RTL_ICTL_IRR3 0x14 #define RTL_ICTL_IRR3 0x14
#define RTL_ICTL_NUM_INPUTS 32
#define REG(x) (realtek_ictl_base + x) #define REG(x) (realtek_ictl_base + x)
static DEFINE_RAW_SPINLOCK(irq_lock); static DEFINE_RAW_SPINLOCK(irq_lock);
static void __iomem *realtek_ictl_base; 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) static void realtek_ictl_unmask_irq(struct irq_data *i)
{ {
unsigned long flags; unsigned long flags;
...@@ -62,8 +84,14 @@ static struct irq_chip realtek_ictl_irq = { ...@@ -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) 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); 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; return 0;
} }
...@@ -95,90 +123,50 @@ static void realtek_irq_dispatch(struct irq_desc *desc) ...@@ -95,90 +123,50 @@ static void realtek_irq_dispatch(struct irq_desc *desc)
chained_irq_exit(chip, desc); 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) static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
{ {
struct of_phandle_args oirq;
struct irq_domain *domain; struct irq_domain *domain;
int ret; unsigned int soc_irq;
int parent_irq;
realtek_ictl_base = of_iomap(node, 0); realtek_ictl_base = of_iomap(node, 0);
if (!realtek_ictl_base) if (!realtek_ictl_base)
return -ENXIO; return -ENXIO;
/* Disable all cascaded interrupts */ /* Disable all cascaded interrupts and clear routing */
writel(0, REG(RTL_ICTL_GIMR)); 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);
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;
parent_irq = irq_create_of_mapping(&oirq);
of_node_put(oirq.np);
} else {
parent_irq = of_irq_get(node, 0);
}
domain = irq_domain_add_simple(node, 32, 0, if (parent_irq < 0)
&irq_domain_ops, NULL); return parent_irq;
else if (!parent_irq)
return -ENODEV;
ret = map_interrupts(node, domain); domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, NULL);
if (ret) { if (!domain)
pr_err("invalid interrupt map\n"); return -ENOMEM;
return ret;
} irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, domain);
return 0; return 0;
} }
......
...@@ -44,7 +44,8 @@ static const struct of_device_id drv_name##_irqchip_match_table[] = { ...@@ -44,7 +44,8 @@ static const struct of_device_id drv_name##_irqchip_match_table[] = {
#define IRQCHIP_MATCH(compat, fn) { .compatible = compat, \ #define IRQCHIP_MATCH(compat, fn) { .compatible = compat, \
.data = typecheck_irq_init_cb(fn), }, .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); \ MODULE_DEVICE_TABLE(of, drv_name##_irqchip_match_table); \
...@@ -56,6 +57,7 @@ static struct platform_driver drv_name##_driver = { \ ...@@ -56,6 +57,7 @@ static struct platform_driver drv_name##_driver = { \
.owner = THIS_MODULE, \ .owner = THIS_MODULE, \
.of_match_table = drv_name##_irqchip_match_table, \ .of_match_table = drv_name##_irqchip_match_table, \
.suppress_bind_attrs = true, \ .suppress_bind_attrs = true, \
__VA_ARGS__ \
}, \ }, \
}; \ }; \
builtin_platform_driver(drv_name##_driver) builtin_platform_driver(drv_name##_driver)
......
...@@ -37,9 +37,8 @@ extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data); ...@@ -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, extern int of_irq_to_resource(struct device_node *dev, int index,
struct resource *r); struct resource *r);
extern void of_irq_init(const struct of_device_id *matches);
#ifdef CONFIG_OF_IRQ #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, extern int of_irq_parse_one(struct device_node *device, int index,
struct of_phandle_args *out_irq); struct of_phandle_args *out_irq);
extern int of_irq_count(struct device_node *dev); 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, ...@@ -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); 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); u32 of_msi_map_id(struct device *dev, struct device_node *msi_np, u32 id_in);
#else #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, static inline int of_irq_parse_one(struct device_node *device, int index,
struct of_phandle_args *out_irq) struct of_phandle_args *out_irq)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment