Commit 0bdf0621 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'irq-core-2024-01-08' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull irq subsystem updates from Ingo Molnar:

 - Add support for the IA55 interrupt controller on RZ/G3S SoC's

 - Update/fix the Qualcom MPM Interrupt Controller driver's register
   enumeration within the somewhat exotic "RPM Message RAM" MMIO-mapped
   shared memory region that is used for other purposes as well

 - Clean up the Xtensa built-in Programmable Interrupt Controller driver
   (xtensa-pic) a bit

* tag 'irq-core-2024-01-08' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  irqchip/irq-xtensa-pic: Clean up
  irqchip/qcom-mpm: Support passing a slice of SRAM as reg space
  dt-bindings: interrupt-controller: mpm: Pass MSG RAM slice through phandle
  dt-bindings: interrupt-controller: renesas,rzg2l-irqc: Document RZ/G3S
  irqchip/renesas-rzg2l: Add support for suspend to RAM
  irqchip/renesas-rzg2l: Add macro to retrieve TITSR register offset based on register's index
  irqchip/renesas-rzg2l: Implement restriction when writing ISCR register
  irqchip/renesas-rzg2l: Document structure members
  irqchip/renesas-rzg2l: Align struct member names to tabs
  irqchip/renesas-rzg2l: Use tabs instead of spaces
parents f24dc33f 69ffab9b
...@@ -29,6 +29,12 @@ properties: ...@@ -29,6 +29,12 @@ properties:
maxItems: 1 maxItems: 1
description: description:
Specifies the base address and size of vMPM registers in RPM MSG RAM. Specifies the base address and size of vMPM registers in RPM MSG RAM.
deprecated: true
qcom,rpm-msg-ram:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to the APSS MPM slice of the RPM Message RAM
interrupts: interrupts:
maxItems: 1 maxItems: 1
...@@ -67,34 +73,46 @@ properties: ...@@ -67,34 +73,46 @@ properties:
required: required:
- compatible - compatible
- reg
- interrupts - interrupts
- mboxes - mboxes
- interrupt-controller - interrupt-controller
- '#interrupt-cells' - '#interrupt-cells'
- qcom,mpm-pin-count - qcom,mpm-pin-count
- qcom,mpm-pin-map - qcom,mpm-pin-map
- qcom,rpm-msg-ram
additionalProperties: false additionalProperties: false
examples: examples:
- | - |
#include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/interrupt-controller/arm-gic.h>
mpm: interrupt-controller@45f01b8 {
compatible = "qcom,mpm"; remoteproc-rpm {
interrupts = <GIC_SPI 197 IRQ_TYPE_EDGE_RISING>; compatible = "qcom,msm8998-rpm-proc", "qcom,rpm-proc";
reg = <0x45f01b8 0x1000>;
mboxes = <&apcs_glb 1>; glink-edge {
interrupt-controller; compatible = "qcom,glink-rpm";
#interrupt-cells = <2>;
interrupt-parent = <&intc>; interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>;
qcom,mpm-pin-count = <96>; qcom,rpm-msg-ram = <&rpm_msg_ram>;
qcom,mpm-pin-map = <2 275>, mboxes = <&apcs_glb 0>;
<5 296>, };
<12 422>,
<24 79>, mpm: interrupt-controller {
<86 183>, compatible = "qcom,mpm";
<90 260>, qcom,rpm-msg-ram = <&apss_mpm>;
<91 260>; interrupts = <GIC_SPI 197 IRQ_TYPE_EDGE_RISING>;
#power-domain-cells = <0>; mboxes = <&apcs_glb 1>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&intc>;
qcom,mpm-pin-count = <96>;
qcom,mpm-pin-map = <2 275>,
<5 296>,
<12 422>,
<24 79>,
<86 183>,
<91 260>;
#power-domain-cells = <0>;
};
}; };
...@@ -26,6 +26,7 @@ properties: ...@@ -26,6 +26,7 @@ properties:
- renesas,r9a07g043u-irqc # RZ/G2UL - renesas,r9a07g043u-irqc # RZ/G2UL
- renesas,r9a07g044-irqc # RZ/G2{L,LC} - renesas,r9a07g044-irqc # RZ/G2{L,LC}
- renesas,r9a07g054-irqc # RZ/V2L - renesas,r9a07g054-irqc # RZ/V2L
- renesas,r9a08g045-irqc # RZ/G3S
- const: renesas,rzg2l-irqc - const: renesas,rzg2l-irqc
'#interrupt-cells': '#interrupt-cells':
...@@ -167,7 +168,9 @@ allOf: ...@@ -167,7 +168,9 @@ allOf:
properties: properties:
compatible: compatible:
contains: contains:
const: renesas,r9a07g043u-irqc enum:
- renesas,r9a07g043u-irqc
- renesas,r9a08g045-irqc
then: then:
properties: properties:
interrupts: interrupts:
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/mailbox_client.h> #include <linux/mailbox_client.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
...@@ -322,8 +323,10 @@ static int qcom_mpm_init(struct device_node *np, struct device_node *parent) ...@@ -322,8 +323,10 @@ static int qcom_mpm_init(struct device_node *np, struct device_node *parent)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct irq_domain *parent_domain; struct irq_domain *parent_domain;
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
struct device_node *msgram_np;
struct qcom_mpm_priv *priv; struct qcom_mpm_priv *priv;
unsigned int pin_cnt; unsigned int pin_cnt;
struct resource res;
int i, irq; int i, irq;
int ret; int ret;
...@@ -374,9 +377,26 @@ static int qcom_mpm_init(struct device_node *np, struct device_node *parent) ...@@ -374,9 +377,26 @@ static int qcom_mpm_init(struct device_node *np, struct device_node *parent)
raw_spin_lock_init(&priv->lock); raw_spin_lock_init(&priv->lock);
priv->base = devm_platform_ioremap_resource(pdev, 0); /* If we have a handle to an RPM message ram partition, use it. */
if (IS_ERR(priv->base)) msgram_np = of_parse_phandle(np, "qcom,rpm-msg-ram", 0);
return PTR_ERR(priv->base); if (msgram_np) {
ret = of_address_to_resource(msgram_np, 0, &res);
if (ret) {
of_node_put(msgram_np);
return ret;
}
/* Don't use devm_ioremap_resource, as we're accessing a shared region. */
priv->base = devm_ioremap(dev, res.start, resource_size(&res));
of_node_put(msgram_np);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
} else {
/* Otherwise, fall back to simple MMIO. */
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
}
for (i = 0; i < priv->reg_stride; i++) { for (i = 0; i < priv->reg_stride; i++) {
qcom_mpm_write(priv, MPM_REG_ENABLE, i, 0); qcom_mpm_write(priv, MPM_REG_ENABLE, i, 0);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/syscore_ops.h>
#define IRQC_IRQ_START 1 #define IRQC_IRQ_START 1
#define IRQC_IRQ_COUNT 8 #define IRQC_IRQ_COUNT 8
...@@ -28,8 +29,7 @@ ...@@ -28,8 +29,7 @@
#define ISCR 0x10 #define ISCR 0x10
#define IITSR 0x14 #define IITSR 0x14
#define TSCR 0x20 #define TSCR 0x20
#define TITSR0 0x24 #define TITSR(n) (0x24 + (n) * 4)
#define TITSR1 0x28
#define TITSR0_MAX_INT 16 #define TITSR0_MAX_INT 16
#define TITSEL_WIDTH 0x2 #define TITSEL_WIDTH 0x2
#define TSSR(n) (0x30 + ((n) * 4)) #define TSSR(n) (0x30 + ((n) * 4))
...@@ -53,15 +53,33 @@ ...@@ -53,15 +53,33 @@
#define IITSR_IITSEL_EDGE_BOTH 3 #define IITSR_IITSEL_EDGE_BOTH 3
#define IITSR_IITSEL_MASK(n) IITSR_IITSEL((n), 3) #define IITSR_IITSEL_MASK(n) IITSR_IITSEL((n), 3)
#define TINT_EXTRACT_HWIRQ(x) FIELD_GET(GENMASK(15, 0), (x)) #define TINT_EXTRACT_HWIRQ(x) FIELD_GET(GENMASK(15, 0), (x))
#define TINT_EXTRACT_GPIOINT(x) FIELD_GET(GENMASK(31, 16), (x)) #define TINT_EXTRACT_GPIOINT(x) FIELD_GET(GENMASK(31, 16), (x))
struct rzg2l_irqc_priv { /**
void __iomem *base; * struct rzg2l_irqc_reg_cache - registers cache (necessary for suspend/resume)
struct irq_fwspec fwspec[IRQC_NUM_IRQ]; * @iitsr: IITSR register
raw_spinlock_t lock; * @titsr: TITSR registers
*/
struct rzg2l_irqc_reg_cache {
u32 iitsr;
u32 titsr[2];
}; };
/**
* struct rzg2l_irqc_priv - IRQ controller private data structure
* @base: Controller's base address
* @fwspec: IRQ firmware specific data
* @lock: Lock to serialize access to hardware registers
* @cache: Registers cache for suspend/resume
*/
static struct rzg2l_irqc_priv {
void __iomem *base;
struct irq_fwspec fwspec[IRQC_NUM_IRQ];
raw_spinlock_t lock;
struct rzg2l_irqc_reg_cache cache;
} *rzg2l_irqc_data;
static struct rzg2l_irqc_priv *irq_data_to_priv(struct irq_data *data) static struct rzg2l_irqc_priv *irq_data_to_priv(struct irq_data *data)
{ {
return data->domain->host_data; return data->domain->host_data;
...@@ -72,11 +90,17 @@ static void rzg2l_irq_eoi(struct irq_data *d) ...@@ -72,11 +90,17 @@ static void rzg2l_irq_eoi(struct irq_data *d)
unsigned int hw_irq = irqd_to_hwirq(d) - IRQC_IRQ_START; unsigned int hw_irq = irqd_to_hwirq(d) - IRQC_IRQ_START;
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
u32 bit = BIT(hw_irq); u32 bit = BIT(hw_irq);
u32 reg; u32 iitsr, iscr;
reg = readl_relaxed(priv->base + ISCR); iscr = readl_relaxed(priv->base + ISCR);
if (reg & bit) iitsr = readl_relaxed(priv->base + IITSR);
writel_relaxed(reg & ~bit, priv->base + ISCR);
/*
* ISCR can only be cleared if the type is falling-edge, rising-edge or
* falling/rising-edge.
*/
if ((iscr & bit) && (iitsr & IITSR_IITSEL_MASK(hw_irq)))
writel_relaxed(iscr & ~bit, priv->base + ISCR);
} }
static void rzg2l_tint_eoi(struct irq_data *d) static void rzg2l_tint_eoi(struct irq_data *d)
...@@ -188,8 +212,7 @@ static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type) ...@@ -188,8 +212,7 @@ static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type)
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
unsigned int hwirq = irqd_to_hwirq(d); unsigned int hwirq = irqd_to_hwirq(d);
u32 titseln = hwirq - IRQC_TINT_START; u32 titseln = hwirq - IRQC_TINT_START;
u32 offset; u8 index, sense;
u8 sense;
u32 reg; u32 reg;
switch (type & IRQ_TYPE_SENSE_MASK) { switch (type & IRQ_TYPE_SENSE_MASK) {
...@@ -205,17 +228,17 @@ static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type) ...@@ -205,17 +228,17 @@ static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type)
return -EINVAL; return -EINVAL;
} }
offset = TITSR0; index = 0;
if (titseln >= TITSR0_MAX_INT) { if (titseln >= TITSR0_MAX_INT) {
titseln -= TITSR0_MAX_INT; titseln -= TITSR0_MAX_INT;
offset = TITSR1; index = 1;
} }
raw_spin_lock(&priv->lock); raw_spin_lock(&priv->lock);
reg = readl_relaxed(priv->base + offset); reg = readl_relaxed(priv->base + TITSR(index));
reg &= ~(IRQ_MASK << (titseln * TITSEL_WIDTH)); reg &= ~(IRQ_MASK << (titseln * TITSEL_WIDTH));
reg |= sense << (titseln * TITSEL_WIDTH); reg |= sense << (titseln * TITSEL_WIDTH);
writel_relaxed(reg, priv->base + offset); writel_relaxed(reg, priv->base + TITSR(index));
raw_spin_unlock(&priv->lock); raw_spin_unlock(&priv->lock);
return 0; return 0;
...@@ -236,6 +259,38 @@ static int rzg2l_irqc_set_type(struct irq_data *d, unsigned int type) ...@@ -236,6 +259,38 @@ static int rzg2l_irqc_set_type(struct irq_data *d, unsigned int type)
return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
} }
static int rzg2l_irqc_irq_suspend(void)
{
struct rzg2l_irqc_reg_cache *cache = &rzg2l_irqc_data->cache;
void __iomem *base = rzg2l_irqc_data->base;
cache->iitsr = readl_relaxed(base + IITSR);
for (u8 i = 0; i < 2; i++)
cache->titsr[i] = readl_relaxed(base + TITSR(i));
return 0;
}
static void rzg2l_irqc_irq_resume(void)
{
struct rzg2l_irqc_reg_cache *cache = &rzg2l_irqc_data->cache;
void __iomem *base = rzg2l_irqc_data->base;
/*
* Restore only interrupt type. TSSRx will be restored at the
* request of pin controller to avoid spurious interrupts due
* to invalid PIN states.
*/
for (u8 i = 0; i < 2; i++)
writel_relaxed(cache->titsr[i], base + TITSR(i));
writel_relaxed(cache->iitsr, base + IITSR);
}
static struct syscore_ops rzg2l_irqc_syscore_ops = {
.suspend = rzg2l_irqc_irq_suspend,
.resume = rzg2l_irqc_irq_resume,
};
static const struct irq_chip irqc_chip = { static const struct irq_chip irqc_chip = {
.name = "rzg2l-irqc", .name = "rzg2l-irqc",
.irq_eoi = rzg2l_irqc_eoi, .irq_eoi = rzg2l_irqc_eoi,
...@@ -321,7 +376,6 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent) ...@@ -321,7 +376,6 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent)
struct irq_domain *irq_domain, *parent_domain; struct irq_domain *irq_domain, *parent_domain;
struct platform_device *pdev; struct platform_device *pdev;
struct reset_control *resetn; struct reset_control *resetn;
struct rzg2l_irqc_priv *priv;
int ret; int ret;
pdev = of_find_device_by_node(node); pdev = of_find_device_by_node(node);
...@@ -334,15 +388,15 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent) ...@@ -334,15 +388,15 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent)
return -ENODEV; return -ENODEV;
} }
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); rzg2l_irqc_data = devm_kzalloc(&pdev->dev, sizeof(*rzg2l_irqc_data), GFP_KERNEL);
if (!priv) if (!rzg2l_irqc_data)
return -ENOMEM; return -ENOMEM;
priv->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL); rzg2l_irqc_data->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL);
if (IS_ERR(priv->base)) if (IS_ERR(rzg2l_irqc_data->base))
return PTR_ERR(priv->base); return PTR_ERR(rzg2l_irqc_data->base);
ret = rzg2l_irqc_parse_interrupts(priv, node); ret = rzg2l_irqc_parse_interrupts(rzg2l_irqc_data, node);
if (ret) { if (ret) {
dev_err(&pdev->dev, "cannot parse interrupts: %d\n", ret); dev_err(&pdev->dev, "cannot parse interrupts: %d\n", ret);
return ret; return ret;
...@@ -365,17 +419,19 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent) ...@@ -365,17 +419,19 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent)
goto pm_disable; goto pm_disable;
} }
raw_spin_lock_init(&priv->lock); raw_spin_lock_init(&rzg2l_irqc_data->lock);
irq_domain = irq_domain_add_hierarchy(parent_domain, 0, IRQC_NUM_IRQ, irq_domain = irq_domain_add_hierarchy(parent_domain, 0, IRQC_NUM_IRQ,
node, &rzg2l_irqc_domain_ops, node, &rzg2l_irqc_domain_ops,
priv); rzg2l_irqc_data);
if (!irq_domain) { if (!irq_domain) {
dev_err(&pdev->dev, "failed to add irq domain\n"); dev_err(&pdev->dev, "failed to add irq domain\n");
ret = -ENOMEM; ret = -ENOMEM;
goto pm_put; goto pm_put;
} }
register_syscore_ops(&rzg2l_irqc_syscore_ops);
return 0; return 0;
pm_put: pm_put:
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
* Kevin Chea * Kevin Chea
*/ */
#include <linux/bits.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/irq.h> #include <linux/irq.h>
...@@ -19,8 +20,6 @@ ...@@ -19,8 +20,6 @@
#include <linux/irqchip/xtensa-pic.h> #include <linux/irqchip/xtensa-pic.h>
#include <linux/of.h> #include <linux/of.h>
unsigned int cached_irq_mask;
/* /*
* Device Tree IRQ specifier translation function which works with one or * Device Tree IRQ specifier translation function which works with one or
* two cell bindings. First cell value maps directly to the hwirq number. * two cell bindings. First cell value maps directly to the hwirq number.
...@@ -44,34 +43,30 @@ static const struct irq_domain_ops xtensa_irq_domain_ops = { ...@@ -44,34 +43,30 @@ static const struct irq_domain_ops xtensa_irq_domain_ops = {
static void xtensa_irq_mask(struct irq_data *d) static void xtensa_irq_mask(struct irq_data *d)
{ {
cached_irq_mask &= ~(1 << d->hwirq); u32 irq_mask;
xtensa_set_sr(cached_irq_mask, intenable);
}
static void xtensa_irq_unmask(struct irq_data *d) irq_mask = xtensa_get_sr(intenable);
{ irq_mask &= ~BIT(d->hwirq);
cached_irq_mask |= 1 << d->hwirq; xtensa_set_sr(irq_mask, intenable);
xtensa_set_sr(cached_irq_mask, intenable);
} }
static void xtensa_irq_enable(struct irq_data *d) static void xtensa_irq_unmask(struct irq_data *d)
{ {
xtensa_irq_unmask(d); u32 irq_mask;
}
static void xtensa_irq_disable(struct irq_data *d) irq_mask = xtensa_get_sr(intenable);
{ irq_mask |= BIT(d->hwirq);
xtensa_irq_mask(d); xtensa_set_sr(irq_mask, intenable);
} }
static void xtensa_irq_ack(struct irq_data *d) static void xtensa_irq_ack(struct irq_data *d)
{ {
xtensa_set_sr(1 << d->hwirq, intclear); xtensa_set_sr(BIT(d->hwirq), intclear);
} }
static int xtensa_irq_retrigger(struct irq_data *d) static int xtensa_irq_retrigger(struct irq_data *d)
{ {
unsigned int mask = 1u << d->hwirq; unsigned int mask = BIT(d->hwirq);
if (WARN_ON(mask & ~XCHAL_INTTYPE_MASK_SOFTWARE)) if (WARN_ON(mask & ~XCHAL_INTTYPE_MASK_SOFTWARE))
return 0; return 0;
...@@ -81,8 +76,6 @@ static int xtensa_irq_retrigger(struct irq_data *d) ...@@ -81,8 +76,6 @@ static int xtensa_irq_retrigger(struct irq_data *d)
static struct irq_chip xtensa_irq_chip = { static struct irq_chip xtensa_irq_chip = {
.name = "xtensa", .name = "xtensa",
.irq_enable = xtensa_irq_enable,
.irq_disable = xtensa_irq_disable,
.irq_mask = xtensa_irq_mask, .irq_mask = xtensa_irq_mask,
.irq_unmask = xtensa_irq_unmask, .irq_unmask = xtensa_irq_unmask,
.irq_ack = xtensa_irq_ack, .irq_ack = xtensa_irq_ack,
......
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