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 */ 37static uint32_t ioredtbl_state[IOAPIC_IRQ_LINES * CONFIG_MAX_NUM_IOAPIC]; 38 39/* Number of IOAPICs in the system */ 40static uint32_t num_ioapics = 0; 41 42static 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 47static 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 52static 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 74static cpu_id_t ioapic_target_cpu = 0; 75void 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 87void 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 106exception_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 146void 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