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