[PATCH 3/3] irqchip/irq-pruss-intc: Fix processing of IEP interrupts

From: MD Danish Anwar
Date: Tue Sep 19 2023 - 02:20:27 EST


From: Suman Anna <s-anna@xxxxxx>

It was discovered that IEP capture/compare IRQs (event #7 on all SoCs
and event #56 on K3 SoCs) are always triggered twice when PPS is
generated and CMP hit event detected by IEP.

An example of the problem is:
pruss_intc_irq_handler
generic_handle_irq
handle_level_irq
mask_ack_irq -> IRQ 7 masked and asked in INTC,
but it's not yet cleared on HW level
handle_irq_event()
<threaded on RT>
icss_iep_cap_cmp_handler() -> IRQ 7 is actually processed in HW
irq_finalize_oneshot()
unmask_irq()
pruss_intc_irq_unmask() -> IRQ 7 status is still observed as set

The solution is to actually ack these IRQs from pruss_intc_irq_unmask()
after the IRQ source is cleared in HW.

No public errata available for this yet.

Fixes: 04e2d1e06978 ("irqchip/irq-pruss-intc: Add a PRUSS irqchip driver for PRUSS interrupts")

Signed-off-by: Grygorii Strashko <grygorii.strashko@xxxxxx>
Signed-off-by: Suman Anna <s-anna@xxxxxx>
Signed-off-by: MD Danish Anwar <danishanwar@xxxxxx>
---
drivers/irqchip/irq-pruss-intc.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/drivers/irqchip/irq-pruss-intc.c b/drivers/irqchip/irq-pruss-intc.c
index 3cf684ede564..9907847dbda8 100644
--- a/drivers/irqchip/irq-pruss-intc.c
+++ b/drivers/irqchip/irq-pruss-intc.c
@@ -70,6 +70,8 @@
#define MAX_PRU_SYS_EVENTS 160
#define MAX_PRU_CHANNELS 20

+#define MAX_PRU_INT_EVENTS 64
+
/**
* struct pruss_intc_map_record - keeps track of actual mapping state
* @value: The currently mapped value (channel or host)
@@ -85,10 +87,13 @@ struct pruss_intc_map_record {
* @num_system_events: number of input system events handled by the PRUSS INTC
* @num_host_events: number of host events (which is equal to number of
* channels) supported by the PRUSS INTC
+ * @quirky_events: bitmask of events that need quirky IRQ handling (limited to
+ * (internal sources only for now, so 64 bits suffice)
*/
struct pruss_intc_match_data {
u8 num_system_events;
u8 num_host_events;
+ u64 quirky_events;
};

/**
@@ -304,6 +309,10 @@ static void pruss_intc_irq_ack(struct irq_data *data)
struct pruss_intc *intc = irq_data_get_irq_chip_data(data);
unsigned int hwirq = data->hwirq;

+ if (hwirq < MAX_PRU_INT_EVENTS &&
+ intc->soc_config->quirky_events & BIT_ULL(hwirq))
+ return;
+
pruss_intc_write_reg(intc, PRU_INTC_SICR, hwirq);
}

@@ -320,6 +329,9 @@ static void pruss_intc_irq_unmask(struct irq_data *data)
struct pruss_intc *intc = irq_data_get_irq_chip_data(data);
unsigned int hwirq = data->hwirq;

+ if (hwirq < MAX_PRU_INT_EVENTS &&
+ intc->soc_config->quirky_events & BIT_ULL(hwirq))
+ pruss_intc_write_reg(intc, PRU_INTC_SICR, hwirq);
pruss_intc_write_reg(intc, PRU_INTC_EISR, hwirq);
}

@@ -644,11 +656,13 @@ static int pruss_intc_remove(struct platform_device *pdev)
static const struct pruss_intc_match_data pruss_intc_data = {
.num_system_events = 64,
.num_host_events = 10,
+ .quirky_events = BIT_ULL(7), /* IEP capture/compare event */
};

static const struct pruss_intc_match_data icssg_intc_data = {
.num_system_events = 160,
.num_host_events = 20,
+ .quirky_events = BIT_ULL(7) | BIT_ULL(56), /* IEP{0,1} capture/compare events */
};

static const struct of_device_id pruss_intc_of_match[] = {
--
2.34.1