@@ -29,6 +29,113 @@ extern char _mtk_adsp_dram_end[];
29
29
#define LOG_LEN 0x100000
30
30
#endif
31
31
32
+ /* The MT8196 interrupt controller is very simple at runtime, with
33
+ * just an enable and status register needed, like its
34
+ * predecessors. But it has routing control which resets to "nothing
35
+ * enabled", so needs a driver.
36
+ *
37
+ * There are 64 interrupt inputs to the controller, controlled by
38
+ * pairs of words (the "intc64" type below). Each interrupt is
39
+ * associated with one[1] of 16 "groups", each of which directs to a
40
+ * different Xtensa architectural interrupt. So each Xtensa interrupt
41
+ * can be configured to handle any subset of interrupt inputs.
42
+ *
43
+ * The mapping of groups to Xtensa interrupts is given below. Note
44
+ * particularly that the final two groups are NMIs directed to an
45
+ * interrupt level higher than EXCM_LEVEL, so cannot be safely used
46
+ * for OS code (they'll interrupt spinlocks), but an app might exploit
47
+ * them for e.g. debug or watchdog hooks.
48
+ *
49
+ * GroupNum XtensaIRQ XtensaLevel
50
+ * 0-5 0-5 1 (L1 is shared w/exceptions, poor choice)
51
+ * 6-7 7-8 1
52
+ * 8-10 9-11 2
53
+ * 11-13 16-18 3
54
+ * 14,15 20,21 4 (Unmaskable! Do not use w/Zephyr code!)
55
+ *
56
+ * Naming of the inputs looks like this, though obviously only a small
57
+ * fraction have been validated (or are even useful for an audio DSP):
58
+ *
59
+ * 0: CCU 20: USB1 40: WDT
60
+ * 1: SCP 21: SCPVOW 41: CONNSYS1
61
+ * 2: SPM 22: CCIF3_C0 42: CONNSYS3
62
+ * 3: PCIE 23: CCIF3_C1 43: CONNSYS4
63
+ * 4: INFRA_HANG 24: PWR_CTRL 44: CONNSYS2
64
+ * 5: PERI_TIMEOUT 25: DMA_C0 45: IPIC
65
+ * 6: MBOX_C0 26: DMA_C1 46: AXI_DMA2
66
+ * 7: MBOX_C1 27: AXI_DMA0 47: AXI_DMA3
67
+ * 8: TIMER0 28: AXI_DMA1 48: APSRC_DDREN
68
+ * 9: TIMER1 29: AUDIO_C0 49: LAT_MON_EMI
69
+ * 10: IPC_C0 30: AUDIO_C1 50: LAT_MON_INFRA
70
+ * 11: IPC_C1 31: HIFI5_WDT_C0 51: DEVAPC_VIO
71
+ * 12: IPC1_RSV 32: HIFI5_WDT_C1 52: AO_INFRA_HANG
72
+ * 13: C2C_SW_C0 33: APU_MBOX_C0 53: BUS_TRA_EMI
73
+ * 14: C2C_SW_C1 34: APU_MBOX_C1 54: BUS_TRA_INFRA
74
+ * 15: UART 35: TIMER2 55: L2SRAM_VIO
75
+ * 16: UART_BT 36: PWR_ON_C0_IRQ 56: L2SRAM_SETERR
76
+ * 17: LATENCY_MON 37: PWR_ON_C1_IRQ 57: PCIERC_GRP2
77
+ * 18: BUS_TRACKER 38: WAKEUP_SRC_C0 58: PCIERC_GRP3
78
+ * 19: USB0 39: WAKEUP_SRC_C1 59: IRQ_MAX_CHANNEL
79
+ *
80
+ * [1] It is legal and works as expected for an interrupt to be part
81
+ * of more than one group (more than one interrupt fires to handle
82
+ * it), though I don't understand why an application would want to
83
+ * do that.
84
+ */
85
+
86
+ struct intc64 { uint32_t lo , hi ; };
87
+
88
+ struct intc_8196 {
89
+ struct intc64 input ; /* Raw (?) input signal, normally high */
90
+ struct intc64 status ; /* Latched input, inverted (active == 1) */
91
+ struct intc64 enable ; /* Interrupt enable */
92
+ struct intc64 polarity ; /* 1 == active low */
93
+ struct intc64 wake_enable ;
94
+ struct intc64 _unused ;
95
+ struct intc64 stage1_enable ;
96
+ struct intc64 sw_trigger ;
97
+ struct intc64 groups [16 ]; /* set bit == "member of group" */
98
+ struct intc64 group_status [16 ]; /* status, but masked by group */
99
+ };
100
+
101
+ #define INTC (*(volatile struct intc_8196 *)0x1a014000)
102
+
103
+ static void set_group_bit (volatile struct intc64 * g , uint32_t bit , bool val )
104
+ {
105
+ volatile uint32_t * p = bit < 32 ? & g -> lo : & g -> hi ;
106
+ volatile uint32_t mask = BIT (bit & 0x1f );
107
+
108
+ * p = val ? (* p | mask ) : (* p & ~mask );
109
+ }
110
+
111
+ static void mt8196_intc_set_irq_group (uint32_t irq , uint32_t group )
112
+ {
113
+ for (int i = 0 ; i < 16 ; i ++ ) {
114
+ set_group_bit (& INTC .groups [i ], irq , i == group );
115
+ }
116
+ }
117
+
118
+ void mt8196_intc_init (void )
119
+ {
120
+ struct intc64 zero = { 0 , 0 };
121
+
122
+ INTC .enable = zero ;
123
+ INTC .polarity .lo = 0xffffffff ;
124
+ INTC .polarity .hi = 0xffffffff ;
125
+ INTC .wake_enable = zero ;
126
+ INTC .stage1_enable = zero ;
127
+ for (int i = 0 ; i < ARRAY_SIZE (INTC .groups ); i ++ ) {
128
+ INTC .groups [i ] = zero ;
129
+ }
130
+
131
+ /* Now wire up known interrupts for existing drivers to their
132
+ * legacy settings
133
+ */
134
+ mt8196_intc_set_irq_group (6 , 2 ); /* mbox0 in group 2 */
135
+ mt8196_intc_set_irq_group (7 , 2 ); /* mbox1 in group 2 */
136
+ mt8196_intc_set_irq_group (8 , 1 ); /* ostimer in group 1 */
137
+ }
138
+
32
139
/* This is the true boot vector. This device allows for direct
33
140
* setting of the alternate reset vector, so we let it link wherever
34
141
* it lands and extract its address in the loader. This represents
@@ -209,6 +316,10 @@ void c_boot(void)
209
316
/* Default console, a driver can override this later */
210
317
__stdout_hook_install (arch_printk_char_out );
211
318
319
+ #ifdef CONFIG_SOC_MT8196
320
+ mt8196_intc_init ();
321
+ #endif
322
+
212
323
void z_prep_c (void );
213
324
z_prep_c ();
214
325
}
0 commit comments