mirror of
https://github.com/torvalds/linux.git
synced 2026-01-25 15:03:52 +08:00
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
This commit is contained in:
@@ -29,6 +29,12 @@ properties:
|
||||
maxItems: 1
|
||||
description:
|
||||
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:
|
||||
maxItems: 1
|
||||
@@ -67,34 +73,46 @@ properties:
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- mboxes
|
||||
- interrupt-controller
|
||||
- '#interrupt-cells'
|
||||
- qcom,mpm-pin-count
|
||||
- qcom,mpm-pin-map
|
||||
- qcom,rpm-msg-ram
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
mpm: interrupt-controller@45f01b8 {
|
||||
compatible = "qcom,mpm";
|
||||
interrupts = <GIC_SPI 197 IRQ_TYPE_EDGE_RISING>;
|
||||
reg = <0x45f01b8 0x1000>;
|
||||
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>,
|
||||
<90 260>,
|
||||
<91 260>;
|
||||
#power-domain-cells = <0>;
|
||||
|
||||
remoteproc-rpm {
|
||||
compatible = "qcom,msm8998-rpm-proc", "qcom,rpm-proc";
|
||||
|
||||
glink-edge {
|
||||
compatible = "qcom,glink-rpm";
|
||||
|
||||
interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>;
|
||||
qcom,rpm-msg-ram = <&rpm_msg_ram>;
|
||||
mboxes = <&apcs_glb 0>;
|
||||
};
|
||||
|
||||
mpm: interrupt-controller {
|
||||
compatible = "qcom,mpm";
|
||||
qcom,rpm-msg-ram = <&apss_mpm>;
|
||||
interrupts = <GIC_SPI 197 IRQ_TYPE_EDGE_RISING>;
|
||||
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:
|
||||
- renesas,r9a07g043u-irqc # RZ/G2UL
|
||||
- renesas,r9a07g044-irqc # RZ/G2{L,LC}
|
||||
- renesas,r9a07g054-irqc # RZ/V2L
|
||||
- renesas,r9a08g045-irqc # RZ/G3S
|
||||
- const: renesas,rzg2l-irqc
|
||||
|
||||
'#interrupt-cells':
|
||||
@@ -167,7 +168,9 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,r9a07g043u-irqc
|
||||
enum:
|
||||
- renesas,r9a07g043u-irqc
|
||||
- renesas,r9a08g045-irqc
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
@@ -322,8 +323,10 @@ static int qcom_mpm_init(struct device_node *np, struct device_node *parent)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct irq_domain *parent_domain;
|
||||
struct generic_pm_domain *genpd;
|
||||
struct device_node *msgram_np;
|
||||
struct qcom_mpm_priv *priv;
|
||||
unsigned int pin_cnt;
|
||||
struct resource res;
|
||||
int i, irq;
|
||||
int ret;
|
||||
|
||||
@@ -374,9 +377,26 @@ static int qcom_mpm_init(struct device_node *np, struct device_node *parent)
|
||||
|
||||
raw_spin_lock_init(&priv->lock);
|
||||
|
||||
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
/* If we have a handle to an RPM message ram partition, use it. */
|
||||
msgram_np = of_parse_phandle(np, "qcom,rpm-msg-ram", 0);
|
||||
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++) {
|
||||
qcom_mpm_write(priv, MPM_REG_ENABLE, i, 0);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#define IRQC_IRQ_START 1
|
||||
#define IRQC_IRQ_COUNT 8
|
||||
@@ -28,8 +29,7 @@
|
||||
#define ISCR 0x10
|
||||
#define IITSR 0x14
|
||||
#define TSCR 0x20
|
||||
#define TITSR0 0x24
|
||||
#define TITSR1 0x28
|
||||
#define TITSR(n) (0x24 + (n) * 4)
|
||||
#define TITSR0_MAX_INT 16
|
||||
#define TITSEL_WIDTH 0x2
|
||||
#define TSSR(n) (0x30 + ((n) * 4))
|
||||
@@ -53,15 +53,33 @@
|
||||
#define IITSR_IITSEL_EDGE_BOTH 3
|
||||
#define IITSR_IITSEL_MASK(n) IITSR_IITSEL((n), 3)
|
||||
|
||||
#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_HWIRQ(x) FIELD_GET(GENMASK(15, 0), (x))
|
||||
#define TINT_EXTRACT_GPIOINT(x) FIELD_GET(GENMASK(31, 16), (x))
|
||||
|
||||
struct rzg2l_irqc_priv {
|
||||
void __iomem *base;
|
||||
struct irq_fwspec fwspec[IRQC_NUM_IRQ];
|
||||
raw_spinlock_t lock;
|
||||
/**
|
||||
* struct rzg2l_irqc_reg_cache - registers cache (necessary for suspend/resume)
|
||||
* @iitsr: IITSR register
|
||||
* @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)
|
||||
{
|
||||
return data->domain->host_data;
|
||||
@@ -72,11 +90,17 @@ static void rzg2l_irq_eoi(struct irq_data *d)
|
||||
unsigned int hw_irq = irqd_to_hwirq(d) - IRQC_IRQ_START;
|
||||
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
|
||||
u32 bit = BIT(hw_irq);
|
||||
u32 reg;
|
||||
u32 iitsr, iscr;
|
||||
|
||||
reg = readl_relaxed(priv->base + ISCR);
|
||||
if (reg & bit)
|
||||
writel_relaxed(reg & ~bit, priv->base + ISCR);
|
||||
iscr = readl_relaxed(priv->base + ISCR);
|
||||
iitsr = readl_relaxed(priv->base + IITSR);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
@@ -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);
|
||||
unsigned int hwirq = irqd_to_hwirq(d);
|
||||
u32 titseln = hwirq - IRQC_TINT_START;
|
||||
u32 offset;
|
||||
u8 sense;
|
||||
u8 index, sense;
|
||||
u32 reg;
|
||||
|
||||
switch (type & IRQ_TYPE_SENSE_MASK) {
|
||||
@@ -205,17 +228,17 @@ static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
offset = TITSR0;
|
||||
index = 0;
|
||||
if (titseln >= TITSR0_MAX_INT) {
|
||||
titseln -= TITSR0_MAX_INT;
|
||||
offset = TITSR1;
|
||||
index = 1;
|
||||
}
|
||||
|
||||
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 |= sense << (titseln * TITSEL_WIDTH);
|
||||
writel_relaxed(reg, priv->base + offset);
|
||||
writel_relaxed(reg, priv->base + TITSR(index));
|
||||
raw_spin_unlock(&priv->lock);
|
||||
|
||||
return 0;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
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 = {
|
||||
.name = "rzg2l-irqc",
|
||||
.irq_eoi = rzg2l_irqc_eoi,
|
||||
@@ -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 platform_device *pdev;
|
||||
struct reset_control *resetn;
|
||||
struct rzg2l_irqc_priv *priv;
|
||||
int ret;
|
||||
|
||||
pdev = of_find_device_by_node(node);
|
||||
@@ -334,15 +388,15 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
rzg2l_irqc_data = devm_kzalloc(&pdev->dev, sizeof(*rzg2l_irqc_data), GFP_KERNEL);
|
||||
if (!rzg2l_irqc_data)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
rzg2l_irqc_data->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL);
|
||||
if (IS_ERR(rzg2l_irqc_data->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) {
|
||||
dev_err(&pdev->dev, "cannot parse interrupts: %d\n", ret);
|
||||
return ret;
|
||||
@@ -365,17 +419,19 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent)
|
||||
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,
|
||||
node, &rzg2l_irqc_domain_ops,
|
||||
priv);
|
||||
rzg2l_irqc_data);
|
||||
if (!irq_domain) {
|
||||
dev_err(&pdev->dev, "failed to add irq domain\n");
|
||||
ret = -ENOMEM;
|
||||
goto pm_put;
|
||||
}
|
||||
|
||||
register_syscore_ops(&rzg2l_irqc_syscore_ops);
|
||||
|
||||
return 0;
|
||||
|
||||
pm_put:
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
* Kevin Chea
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irq.h>
|
||||
@@ -19,8 +20,6 @@
|
||||
#include <linux/irqchip/xtensa-pic.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
unsigned int cached_irq_mask;
|
||||
|
||||
/*
|
||||
* Device Tree IRQ specifier translation function which works with one or
|
||||
* 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 = {
|
||||
|
||||
static void xtensa_irq_mask(struct irq_data *d)
|
||||
{
|
||||
cached_irq_mask &= ~(1 << d->hwirq);
|
||||
xtensa_set_sr(cached_irq_mask, intenable);
|
||||
u32 irq_mask;
|
||||
|
||||
irq_mask = xtensa_get_sr(intenable);
|
||||
irq_mask &= ~BIT(d->hwirq);
|
||||
xtensa_set_sr(irq_mask, intenable);
|
||||
}
|
||||
|
||||
static void xtensa_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
cached_irq_mask |= 1 << d->hwirq;
|
||||
xtensa_set_sr(cached_irq_mask, intenable);
|
||||
}
|
||||
u32 irq_mask;
|
||||
|
||||
static void xtensa_irq_enable(struct irq_data *d)
|
||||
{
|
||||
xtensa_irq_unmask(d);
|
||||
}
|
||||
|
||||
static void xtensa_irq_disable(struct irq_data *d)
|
||||
{
|
||||
xtensa_irq_mask(d);
|
||||
irq_mask = xtensa_get_sr(intenable);
|
||||
irq_mask |= BIT(d->hwirq);
|
||||
xtensa_set_sr(irq_mask, intenable);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned int mask = 1u << d->hwirq;
|
||||
unsigned int mask = BIT(d->hwirq);
|
||||
|
||||
if (WARN_ON(mask & ~XCHAL_INTTYPE_MASK_SOFTWARE))
|
||||
return 0;
|
||||
@@ -81,8 +76,6 @@ static int xtensa_irq_retrigger(struct irq_data *d)
|
||||
|
||||
static struct irq_chip xtensa_irq_chip = {
|
||||
.name = "xtensa",
|
||||
.irq_enable = xtensa_irq_enable,
|
||||
.irq_disable = xtensa_irq_disable,
|
||||
.irq_mask = xtensa_irq_mask,
|
||||
.irq_unmask = xtensa_irq_unmask,
|
||||
.irq_ack = xtensa_irq_ack,
|
||||
|
||||
Reference in New Issue
Block a user