mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
irqchip/renesas-rzt2h: Add software-triggered interrupts support
The Renesas RZ/T2H ICU supports software-triggerable interrupts. Add a dedicated rzt2h_icu_intcpu_chip irq_chip which implements rzt2h_icu_intcpu_set_irqchip_state() to allow injecting these interrupts. Request the INTCPU IRQs when IRQ injection is enabled to report them when they occur. Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com> Signed-off-by: Thomas Gleixner <tglx@kernel.org> Link: https://patch.msgid.link/20260520203117.1516442-3-cosmin-gabriel.tanislav.xa@renesas.com
This commit is contained in:
parent
0ed645812f
commit
a964dabc28
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/irq-renesas-rzt2h.h>
|
||||
|
|
@ -40,6 +41,9 @@
|
|||
((n) >= RZT2H_ICU_##type##_START && \
|
||||
(n) < RZT2H_ICU_##type##_START + RZT2H_ICU_##type##_COUNT)
|
||||
|
||||
#define RZT2H_ICU_SWINT 0x0
|
||||
#define RZT2H_ICU_SWINT_IC_MASK(i) BIT(i)
|
||||
|
||||
#define RZT2H_ICU_PORTNF_MD 0xc
|
||||
#define RZT2H_ICU_PORTNF_MDi_MASK(i) (GENMASK(1, 0) << ((i) * 2))
|
||||
#define RZT2H_ICU_PORTNF_MDi_PREP(i, val) (FIELD_PREP(GENMASK(1, 0), val) << ((i) * 2))
|
||||
|
|
@ -99,6 +103,12 @@ static inline int rzt2h_icu_irq_to_offset(struct irq_data *d, void __iomem **bas
|
|||
} else if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, IRQ_S) || RZT2H_ICU_IRQ_IN_RANGE(hwirq, SEI)) {
|
||||
*offset = hwirq - RZT2H_ICU_IRQ_S_START;
|
||||
*base = priv->base_s;
|
||||
} else if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_NS)) {
|
||||
*offset = hwirq - RZT2H_ICU_INTCPU_NS_START;
|
||||
*base = priv->base_ns;
|
||||
} else if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_S)) {
|
||||
*offset = hwirq - RZT2H_ICU_INTCPU_S_START;
|
||||
*base = priv->base_s;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -164,6 +174,28 @@ static int rzt2h_icu_set_type(struct irq_data *d, unsigned int type)
|
|||
return irq_chip_set_type_parent(d, IRQ_TYPE_EDGE_RISING);
|
||||
}
|
||||
|
||||
static int rzt2h_icu_intcpu_set_irqchip_state(struct irq_data *d, enum irqchip_irq_state which,
|
||||
bool state)
|
||||
{
|
||||
unsigned int offset;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
if (which != IRQCHIP_STATE_PENDING)
|
||||
return irq_chip_set_parent_state(d, which, state);
|
||||
|
||||
if (!state)
|
||||
return 0;
|
||||
|
||||
ret = rzt2h_icu_irq_to_offset(d, &base, &offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel_relaxed(RZT2H_ICU_SWINT_IC_MASK(offset), base + RZT2H_ICU_SWINT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_chip rzt2h_icu_chip = {
|
||||
.name = "rzt2h-icu",
|
||||
.irq_mask = irq_chip_mask_parent,
|
||||
|
|
@ -180,10 +212,27 @@ static const struct irq_chip rzt2h_icu_chip = {
|
|||
IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
static const struct irq_chip rzt2h_icu_intcpu_chip = {
|
||||
.name = "rzt2h-icu",
|
||||
.irq_mask = irq_chip_mask_parent,
|
||||
.irq_unmask = irq_chip_unmask_parent,
|
||||
.irq_eoi = irq_chip_eoi_parent,
|
||||
.irq_set_type = irq_chip_set_type_parent,
|
||||
.irq_set_wake = irq_chip_set_wake_parent,
|
||||
.irq_set_affinity = irq_chip_set_affinity_parent,
|
||||
.irq_retrigger = irq_chip_retrigger_hierarchy,
|
||||
.irq_get_irqchip_state = irq_chip_get_parent_state,
|
||||
.irq_set_irqchip_state = rzt2h_icu_intcpu_set_irqchip_state,
|
||||
.flags = IRQCHIP_MASK_ON_SUSPEND |
|
||||
IRQCHIP_SET_TYPE_MASKED |
|
||||
IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
static int rzt2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs,
|
||||
void *arg)
|
||||
{
|
||||
struct rzt2h_icu_priv *priv = domain->host_data;
|
||||
const struct irq_chip *chip;
|
||||
irq_hw_number_t hwirq;
|
||||
unsigned int type;
|
||||
int ret;
|
||||
|
|
@ -192,7 +241,12 @@ static int rzt2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigne
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &rzt2h_icu_chip, NULL);
|
||||
if (RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_NS) || RZT2H_ICU_IRQ_IN_RANGE(hwirq, INTCPU_S))
|
||||
chip = &rzt2h_icu_intcpu_chip;
|
||||
else
|
||||
chip = &rzt2h_icu_chip;
|
||||
|
||||
ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -222,6 +276,60 @@ static int rzt2h_icu_parse_interrupts(struct rzt2h_icu_priv *priv, struct device
|
|||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t rzt2h_icu_intcpu_irq(int irq, void *data)
|
||||
{
|
||||
unsigned int intcpu = (uintptr_t)data;
|
||||
|
||||
pr_info("INTCPU%u software interrupt\n", intcpu);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rzt2h_icu_request_irqs(struct platform_device *pdev, struct irq_domain *irq_domain,
|
||||
unsigned int start, unsigned int count, irq_handler_t handler,
|
||||
void *data)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
unsigned int offset, virq;
|
||||
struct irq_fwspec fwspec;
|
||||
int ret;
|
||||
|
||||
for (offset = start; offset < start + count; offset++) {
|
||||
fwspec.fwnode = irq_domain->fwnode;
|
||||
fwspec.param_count = 2;
|
||||
fwspec.param[0] = offset;
|
||||
fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
|
||||
|
||||
virq = irq_create_fwspec_mapping(&fwspec);
|
||||
if (!virq)
|
||||
return dev_err_probe(dev, -EINVAL, "Failed to create IRQ %u mapping\n", offset);
|
||||
|
||||
ret = devm_request_irq(dev, virq, handler, 0, dev_name(dev),
|
||||
data ?: (void *)(uintptr_t)offset);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request IRQ %u\n", offset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzt2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain *irq_domain)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_GENERIC_IRQ_INJECTION)) {
|
||||
int ret = rzt2h_icu_request_irqs(pdev, irq_domain, RZT2H_ICU_INTCPU_NS_START,
|
||||
RZT2H_ICU_INTCPU_NS_COUNT, rzt2h_icu_intcpu_irq,
|
||||
NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rzt2h_icu_request_irqs(pdev, irq_domain, RZT2H_ICU_INTCPU_S_START,
|
||||
RZT2H_ICU_INTCPU_S_COUNT, rzt2h_icu_intcpu_irq, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzt2h_icu_init(struct platform_device *pdev, struct device_node *parent)
|
||||
{
|
||||
struct irq_domain *irq_domain, *parent_domain;
|
||||
|
|
@ -265,11 +373,20 @@ static int rzt2h_icu_init(struct platform_device *pdev, struct device_node *pare
|
|||
irq_domain = irq_domain_create_hierarchy(parent_domain, 0, RZT2H_ICU_NUM_IRQ,
|
||||
dev_fwnode(dev), &rzt2h_icu_domain_ops, priv);
|
||||
if (!irq_domain) {
|
||||
pm_runtime_put_sync(dev);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err_pm_put;
|
||||
}
|
||||
|
||||
ret = rzt2h_icu_setup_irqs(pdev, irq_domain);
|
||||
if (ret)
|
||||
goto err_irq_domain_free;
|
||||
return 0;
|
||||
|
||||
err_irq_domain_free:
|
||||
irq_domain_remove(irq_domain);
|
||||
err_pm_put:
|
||||
pm_runtime_put_sync(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
IRQCHIP_PLATFORM_DRIVER_BEGIN(rzt2h_icu)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user