1 /*
2  * Copyright (c) 2022 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT intel_ace_intc
8 
9 #include <zephyr/device.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/devicetree/interrupt_controller.h>
12 #include <zephyr/irq_nextlevel.h>
13 #include <zephyr/arch/xtensa/irq.h>
14 #include <zephyr/sw_isr_table.h>
15 #include <zephyr/drivers/interrupt_controller/dw_ace.h>
16 #include <soc.h>
17 #include <adsp_interrupt.h>
18 #include <zephyr/irq.h>
19 #include "intc_dw.h"
20 
21 /* ACE device interrupts are all packed into a single line on Xtensa's
22  * architectural IRQ 4 (see below), run by a Designware interrupt
23  * controller with 28 lines instantiated.  They get numbered
24  * immediately after the Xtensa interrupt space in the numbering
25  * (i.e. interrupts 0-31 are Xtensa IRQs, 32 represents DW input 0,
26  * etc...).
27  *
28  * That IRQ 4 indeed has an interrupt type of "EXTERN_LEVEL" and an
29  * interrupt level of 2.  The CPU has a level 1 external interrupt on
30  * IRQ 1 and a level 3 on IRQ 6, but nothing seems wired there.  Note
31  * that this level 2 ISR is also shared with the CCOUNT timer on IRQ3.
32  * This interrupt is a very busy place!
33  *
34  * But, because there can never be a situation where all interrupts on
35  * the Synopsys controller are disabled (such a system would halt
36  * forever if it reached idle!), we at least can take advantage to
37  * implement a simplified masking architecture.  Xtensa INTENABLE
38  * always has the line active, and we do all masking of external
39  * interrupts on the single controller.
40  *
41  * Finally: note that there is an extra layer of masking on ACE.  The
42  * ACE_DINT registers provide separately maskable interrupt delivery
43  * for each core, and with some devices for different internal
44  * interrupt sources.  Responsibility for these mask bits is left with
45  * the driver.
46  *
47  * Thus, the masking architecture picked here is:
48  *
49  * + Drivers manage ACE_DINT themselves, as there are device-specific
50  *   mask indexes that only the driver can interpret.  If
51  *   core-asymmetric interrupt routing needs to happen, it happens
52  *   here.
53  *
54  * + The DW layer is en/disabled uniformly across all cores.  This is
55  *   the layer toggled by arch_irq_en/disable().
56  *
57  * + Index 4 in the INTENABLE SR is set at core startup and stays
58  *   enabled always.
59  */
60 
61 /* ACE also has per-core instantiations of a Synopsys interrupt
62  * controller.  These inputs (with the same indices as ACE_INTL_*
63  * above) are downstream of the DINT layer, and must be independently
64  * masked/enabled.  The core Zephyr intc_dw driver unfortunately
65  * doesn't understand this kind of MP implementation.  Note also that
66  * as instantiated (there are only 28 sources), the high 32 bit
67  * registers don't exist and aren't named here.  Access via e.g.:
68  *
69  *     ACE_INTC[core_id].irq_inten_l |= interrupt_bit;
70  */
71 
72 #define ACE_INTC ((volatile struct dw_ictl_registers *)DT_INST_REG_ADDR(0))
73 
is_dw_irq(uint32_t irq)74 static inline bool is_dw_irq(uint32_t irq)
75 {
76 	if (((irq & XTENSA_IRQ_NUM_MASK) == ACE_INTC_IRQ)
77 	    && ((irq & ~XTENSA_IRQ_NUM_MASK) != 0)) {
78 		return true;
79 	}
80 
81 	return false;
82 }
83 
dw_ace_irq_enable(const struct device * dev,uint32_t irq)84 void dw_ace_irq_enable(const struct device *dev, uint32_t irq)
85 {
86 	ARG_UNUSED(dev);
87 
88 	if (is_dw_irq(irq)) {
89 		unsigned int num_cpus = arch_num_cpus();
90 
91 		for (int i = 0; i < num_cpus; i++) {
92 			ACE_INTC[i].irq_inten_l |= BIT(ACE_IRQ_FROM_ZEPHYR(irq));
93 			ACE_INTC[i].irq_intmask_l &= ~BIT(ACE_IRQ_FROM_ZEPHYR(irq));
94 		}
95 	} else if ((irq & ~XTENSA_IRQ_NUM_MASK) == 0U) {
96 		xtensa_irq_enable(XTENSA_IRQ_NUMBER(irq));
97 	}
98 }
99 
dw_ace_irq_disable(const struct device * dev,uint32_t irq)100 void dw_ace_irq_disable(const struct device *dev, uint32_t irq)
101 {
102 	ARG_UNUSED(dev);
103 
104 	if (is_dw_irq(irq)) {
105 		unsigned int num_cpus = arch_num_cpus();
106 
107 		for (int i = 0; i < num_cpus; i++) {
108 			ACE_INTC[i].irq_inten_l &= ~BIT(ACE_IRQ_FROM_ZEPHYR(irq));
109 			ACE_INTC[i].irq_intmask_l |= BIT(ACE_IRQ_FROM_ZEPHYR(irq));
110 		}
111 	} else if ((irq & ~XTENSA_IRQ_NUM_MASK) == 0U) {
112 		xtensa_irq_disable(XTENSA_IRQ_NUMBER(irq));
113 	}
114 }
115 
dw_ace_irq_is_enabled(const struct device * dev,unsigned int irq)116 int dw_ace_irq_is_enabled(const struct device *dev, unsigned int irq)
117 {
118 	ARG_UNUSED(dev);
119 
120 	if (is_dw_irq(irq)) {
121 		return ACE_INTC[0].irq_inten_l & BIT(ACE_IRQ_FROM_ZEPHYR(irq));
122 	} else if ((irq & ~XTENSA_IRQ_NUM_MASK) == 0U) {
123 		return xtensa_irq_is_enabled(XTENSA_IRQ_NUMBER(irq));
124 	}
125 
126 	return false;
127 }
128 
129 #ifdef CONFIG_DYNAMIC_INTERRUPTS
dw_ace_irq_connect_dynamic(const struct device * dev,unsigned int irq,unsigned int priority,void (* routine)(const void * parameter),const void * parameter,uint32_t flags)130 int dw_ace_irq_connect_dynamic(const struct device *dev, unsigned int irq,
131 				   unsigned int priority,
132 				   void (*routine)(const void *parameter),
133 				   const void *parameter, uint32_t flags)
134 {
135 	/* Simple architecture means that the Zephyr irq number and
136 	 * the index into the ISR table are identical.
137 	 */
138 	ARG_UNUSED(dev);
139 	ARG_UNUSED(flags);
140 	ARG_UNUSED(priority);
141 	z_isr_install(irq, routine, parameter);
142 	return irq;
143 }
144 #endif
145 
dwint_isr(const void * arg)146 static void dwint_isr(const void *arg)
147 {
148 	uint32_t fs = ACE_INTC[arch_proc_id()].irq_finalstatus_l;
149 
150 	while (fs) {
151 		uint32_t bit = find_lsb_set(fs) - 1;
152 		uint32_t offset = CONFIG_2ND_LVL_ISR_TBL_OFFSET + bit;
153 		struct _isr_table_entry *ent = &_sw_isr_table[offset];
154 
155 		fs &= ~BIT(bit);
156 		ent->isr(ent->arg);
157 	}
158 }
159 
dw_ace_init(const struct device * dev)160 static int dw_ace_init(const struct device *dev)
161 {
162 	ARG_UNUSED(dev);
163 
164 	IRQ_CONNECT(ACE_INTC_IRQ, 0, dwint_isr, 0, 0);
165 	xtensa_irq_enable(ACE_INTC_IRQ);
166 
167 	return 0;
168 }
169 
170 static const struct dw_ace_v1_ictl_driver_api dw_ictl_ace_v1x_apis = {
171 	.intr_enable = dw_ace_irq_enable,
172 	.intr_disable = dw_ace_irq_disable,
173 	.intr_is_enabled = dw_ace_irq_is_enabled,
174 #ifdef CONFIG_DYNAMIC_INTERRUPTS
175 	.intr_connect_dynamic = dw_ace_irq_connect_dynamic,
176 #endif
177 };
178 
179 DEVICE_DT_INST_DEFINE(0, dw_ace_init, NULL, NULL, NULL,
180 		 PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY,
181 		 &dw_ictl_ace_v1x_apis);
182 
183 IRQ_PARENT_ENTRY_DEFINE(ace_intc, DEVICE_DT_INST_GET(0), DT_INST_IRQN(0),
184 			INTC_BASE_ISR_TBL_OFFSET(DT_DRV_INST(0)),
185 			DT_INST_INTC_GET_AGGREGATOR_LEVEL(0));
186