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