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