1 /*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7 #include <config.h>
8
9 #include <linker.h>
10 #include <machine/io.h>
11 #include <plat/machine/hardware.h>
12 #include <plat/machine/ioapic.h>
13
14 #define IOAPIC_REGSEL 0x00
15 #define IOAPIC_WINDOW 0x10
16
17 #define IOAPIC_REG_IOAPICID 0x00
18 #define IOAPIC_REG_IOREDTBL 0x10
19
20 #define IOREDTBL_LOW(reg) (IOAPIC_REG_IOREDTBL + (reg) * 2)
21 #define IOREDTBL_HIGH(reg) (IOREDTBL_LOW(reg) + 1)
22
23 #define IOREDTBL_LOW_INTERRUPT_MASK BIT(16)
24 #define IOREDTBL_LOW_TRIGGER_MODE_LEVEL BIT(15)
25 #define IOREDTBL_LOW_TRIGGER_MODE_SHIFT 15
26 #define IOREDTBL_LOW_POLARITY_LOW BIT(13)
27 #define IOREDTBL_LOW_POLARITY_SHIFT 13
28 #define IOREDTBL_LOW_DEST_MODE_LOGCIAL BIT(11)
29
30 #define IOAPICID_ID_BITS 4
31 #define IOAPICID_ID_OFFSET 24
32
33 #define IOREDTBL_HIGH_RESERVED_BITS 24
34
35 /* Cache what we believe is in the low word of the IOREDTBL. This
36 * has all the state of trigger modes etc etc */
37 static uint32_t ioredtbl_state[IOAPIC_IRQ_LINES * MAX(1, CONFIG_MAX_NUM_IOAPIC)];
38
39 /* Number of IOAPICs in the system */
40 static uint32_t num_ioapics = 0;
41
ioapic_write(uint32_t ioapic,word_t reg,uint32_t value)42 static void ioapic_write(uint32_t ioapic, word_t reg, uint32_t value)
43 {
44 *(volatile uint32_t *)((word_t)(PPTR_IOAPIC_START + ioapic * BIT(PAGE_BITS)) + reg) = value;
45 }
46
ioapic_read(uint32_t ioapic,word_t reg)47 static uint32_t ioapic_read(uint32_t ioapic, word_t reg)
48 {
49 return *(volatile uint32_t *)((word_t)(PPTR_IOAPIC_START + ioapic * BIT(PAGE_BITS)) + reg);
50 }
51
single_ioapic_init(word_t ioapic,cpu_id_t delivery_cpu)52 static void single_ioapic_init(word_t ioapic, cpu_id_t delivery_cpu)
53 {
54 uint32_t i;
55
56 /* Mask all the IRQs. In doing so we happen to set
57 * the vector to 0, which we can assert against in
58 * mask_interrupt to ensure a vector is assigned
59 * before we unmask */
60 for (i = 0; i < IOAPIC_IRQ_LINES; i++) {
61 /* Send to desired cpu */
62 ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_HIGH(i));
63 ioapic_write(ioapic, IOAPIC_WINDOW, (ioapic_read(ioapic,
64 IOAPIC_WINDOW) & MASK(IOREDTBL_HIGH_RESERVED_BITS)) | (delivery_cpu << IOREDTBL_HIGH_RESERVED_BITS));
65 /* mask and set 0 vector */
66 ioredtbl_state[i] = IOREDTBL_LOW_INTERRUPT_MASK;
67 ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_LOW(i));
68 /* The upper 16 bits are reserved, so we make sure to preserve them */
69 ioredtbl_state[i] |= ioapic_read(ioapic, IOAPIC_WINDOW) & ~MASK(16);
70 ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_state[i]);
71 }
72 }
73
74 static cpu_id_t ioapic_target_cpu = 0;
ioapic_init(uint32_t num_nodes,cpu_id_t * cpu_list,uint32_t num_ioapic)75 void ioapic_init(uint32_t num_nodes, cpu_id_t *cpu_list, uint32_t num_ioapic)
76 {
77 uint32_t ioapic;
78 num_ioapics = num_ioapic;
79 ioapic_target_cpu = cpu_list[0];
80
81 for (ioapic = 0; ioapic < num_ioapic; ioapic++) {
82 /* Init this ioapic */
83 single_ioapic_init(ioapic, cpu_list[0]);
84 }
85 }
86
ioapic_mask(bool_t mask,uint32_t ioapic,uint32_t pin)87 void ioapic_mask(bool_t mask, uint32_t ioapic, uint32_t pin)
88 {
89 int index = ioapic * IOAPIC_IRQ_LINES + pin;
90 if (ioapic >= num_ioapics || pin >= IOAPIC_IRQ_LINES) {
91 /* silently ignore requests to non existent parts of the interrupt space */
92 return;
93 }
94 if (mask) {
95 ioredtbl_state[index] |= IOREDTBL_LOW_INTERRUPT_MASK;
96 } else {
97 ioredtbl_state[index] &= ~IOREDTBL_LOW_INTERRUPT_MASK;
98 /* it should not be possible to be unmasking an interrupt, without
99 * it having been mapped to a vector, assert that this is the case */
100 assert((ioredtbl_state[index] & 0xff) != 0);
101 }
102 ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_LOW(pin));
103 ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_state[index]);
104 }
105
ioapic_decode_map_pin_to_vector(word_t ioapic,word_t pin,word_t level,word_t polarity,word_t vector)106 exception_t ioapic_decode_map_pin_to_vector(word_t ioapic, word_t pin, word_t level,
107 word_t polarity, word_t vector)
108 {
109 if (num_ioapics == 0) {
110 userError("System has no IOAPICs");
111 current_syscall_error.type = seL4_IllegalOperation;
112 return EXCEPTION_SYSCALL_ERROR;
113 }
114 if (ioapic >= num_ioapics) {
115 userError("Invalid IOAPIC %ld, only have %ld", (long)ioapic, (long)num_ioapics);
116 current_syscall_error.type = seL4_RangeError;
117 current_syscall_error.rangeErrorMin = 0;
118 current_syscall_error.rangeErrorMax = num_ioapics - 1;
119 return EXCEPTION_SYSCALL_ERROR;
120 }
121 if (pin >= IOAPIC_IRQ_LINES) {
122 userError("Invalid IOAPIC pin %ld, there are %d pins", (long)pin, IOAPIC_IRQ_LINES);
123 current_syscall_error.type = seL4_RangeError;
124 current_syscall_error.rangeErrorMin = 0;
125 current_syscall_error.rangeErrorMax = IOAPIC_IRQ_LINES - 1;
126 return EXCEPTION_SYSCALL_ERROR;
127 }
128
129 if (level != 0 && level != 1) {
130 userError("Level should be 0 or 1, not %d", (int)level);
131 current_syscall_error.type = seL4_RangeError;
132 current_syscall_error.rangeErrorMin = 0;
133 current_syscall_error.rangeErrorMax = 1;
134 return EXCEPTION_SYSCALL_ERROR;
135 }
136 if (polarity != 0 && polarity != 1) {
137 userError("Polarity should be 0 or 1, not %d", (int)polarity);
138 current_syscall_error.type = seL4_RangeError;
139 current_syscall_error.rangeErrorMin = 0;
140 current_syscall_error.rangeErrorMax = 1;
141 return EXCEPTION_SYSCALL_ERROR;
142 }
143 return EXCEPTION_NONE;
144 }
145
ioapic_map_pin_to_vector(word_t ioapic,word_t pin,word_t level,word_t polarity,word_t vector)146 void ioapic_map_pin_to_vector(word_t ioapic, word_t pin, word_t level,
147 word_t polarity, word_t vector)
148 {
149 uint32_t ioredtbl_high = 0;
150 uint32_t index = 0;
151
152 index = ioapic * IOAPIC_IRQ_LINES + pin;
153 ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_HIGH(pin));
154 ioredtbl_high = ioapic_read(ioapic, IOAPIC_WINDOW) & MASK(IOREDTBL_HIGH_RESERVED_BITS);
155 /* delivery mode: physical mode only, using APIC ID */
156 ioredtbl_high |= (ioapic_target_cpu << IOREDTBL_HIGH_RESERVED_BITS);
157 ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_high);
158 /* we do not need to add IRQ_INT_OFFSET to the vector here */
159 ioredtbl_state[index] = IOREDTBL_LOW_INTERRUPT_MASK |
160 (level << IOREDTBL_LOW_TRIGGER_MODE_SHIFT) |
161 (polarity << IOREDTBL_LOW_POLARITY_SHIFT) |
162 vector;
163
164 ioapic_write(ioapic, IOAPIC_REGSEL, IOREDTBL_LOW(pin));
165 /* the upper 16 bits are reserved */
166 ioredtbl_state[index] |= ioapic_read(ioapic, IOAPIC_WINDOW) & ~MASK(16);
167 ioapic_write(ioapic, IOAPIC_WINDOW, ioredtbl_state[index]);
168 }
169