1*433d6423SLionel Sambuc /* 2*433d6423SLionel Sambuc * APIC handling routines. APIC is a requirement for SMP 3*433d6423SLionel Sambuc */ 4*433d6423SLionel Sambuc #include "kernel/kernel.h" 5*433d6423SLionel Sambuc #include <assert.h> 6*433d6423SLionel Sambuc 7*433d6423SLionel Sambuc #include <unistd.h> 8*433d6423SLionel Sambuc #include <minix/portio.h> 9*433d6423SLionel Sambuc 10*433d6423SLionel Sambuc #include <minix/syslib.h> 11*433d6423SLionel Sambuc #include <machine/cmos.h> 12*433d6423SLionel Sambuc 13*433d6423SLionel Sambuc #include "arch_proto.h" 14*433d6423SLionel Sambuc 15*433d6423SLionel Sambuc #include <minix/u64.h> 16*433d6423SLionel Sambuc 17*433d6423SLionel Sambuc #include "apic.h" 18*433d6423SLionel Sambuc #include "apic_asm.h" 19*433d6423SLionel Sambuc #include "kernel/clock.h" 20*433d6423SLionel Sambuc #include "glo.h" 21*433d6423SLionel Sambuc #include "hw_intr.h" 22*433d6423SLionel Sambuc 23*433d6423SLionel Sambuc #include "acpi.h" 24*433d6423SLionel Sambuc 25*433d6423SLionel Sambuc #ifdef USE_WATCHDOG 26*433d6423SLionel Sambuc #include "kernel/watchdog.h" 27*433d6423SLionel Sambuc #endif 28*433d6423SLionel Sambuc 29*433d6423SLionel Sambuc #define APIC_ENABLE 0x100 30*433d6423SLionel Sambuc #define APIC_FOCUS_DISABLED (1 << 9) 31*433d6423SLionel Sambuc #define APIC_SIV 0xFF 32*433d6423SLionel Sambuc 33*433d6423SLionel Sambuc #define APIC_TDCR_2 0x00 34*433d6423SLionel Sambuc #define APIC_TDCR_4 0x01 35*433d6423SLionel Sambuc #define APIC_TDCR_8 0x02 36*433d6423SLionel Sambuc #define APIC_TDCR_16 0x03 37*433d6423SLionel Sambuc #define APIC_TDCR_32 0x08 38*433d6423SLionel Sambuc #define APIC_TDCR_64 0x09 39*433d6423SLionel Sambuc #define APIC_TDCR_128 0x0a 40*433d6423SLionel Sambuc #define APIC_TDCR_1 0x0b 41*433d6423SLionel Sambuc 42*433d6423SLionel Sambuc #define IS_SET(mask) (mask) 43*433d6423SLionel Sambuc #define IS_CLEAR(mask) 0 44*433d6423SLionel Sambuc 45*433d6423SLionel Sambuc #define APIC_LVTT_VECTOR_MASK 0x000000FF 46*433d6423SLionel Sambuc #define APIC_LVTT_DS_PENDING (1 << 12) 47*433d6423SLionel Sambuc #define APIC_LVTT_MASK (1 << 16) 48*433d6423SLionel Sambuc #define APIC_LVTT_TM (1 << 17) 49*433d6423SLionel Sambuc 50*433d6423SLionel Sambuc #define APIC_LVT_IIPP_MASK 0x00002000 51*433d6423SLionel Sambuc #define APIC_LVT_IIPP_AH 0x00002000 52*433d6423SLionel Sambuc #define APIC_LVT_IIPP_AL 0x00000000 53*433d6423SLionel Sambuc 54*433d6423SLionel Sambuc #define APIC_LVT_TM_ONESHOT IS_CLEAR(APIC_LVTT_TM) 55*433d6423SLionel Sambuc #define APIC_LVT_TM_PERIODIC IS_SET(APIC_LVTT_TM) 56*433d6423SLionel Sambuc 57*433d6423SLionel Sambuc #define IOAPIC_REGSEL 0x0 58*433d6423SLionel Sambuc #define IOAPIC_RW 0x10 59*433d6423SLionel Sambuc 60*433d6423SLionel Sambuc #define APIC_ICR_DM_MASK 0x00000700 61*433d6423SLionel Sambuc #define APIC_ICR_VECTOR APIC_LVTT_VECTOR_MASK 62*433d6423SLionel Sambuc #define APIC_ICR_DM_FIXED (0 << 8) 63*433d6423SLionel Sambuc #define APIC_ICR_DM_LOWEST_PRIORITY (1 << 8) 64*433d6423SLionel Sambuc #define APIC_ICR_DM_SMI (2 << 8) 65*433d6423SLionel Sambuc #define APIC_ICR_DM_RESERVED (3 << 8) 66*433d6423SLionel Sambuc #define APIC_ICR_DM_NMI (4 << 8) 67*433d6423SLionel Sambuc #define APIC_ICR_DM_INIT (5 << 8) 68*433d6423SLionel Sambuc #define APIC_ICR_DM_STARTUP (6 << 8) 69*433d6423SLionel Sambuc #define APIC_ICR_DM_EXTINT (7 << 8) 70*433d6423SLionel Sambuc 71*433d6423SLionel Sambuc #define APIC_ICR_DM_PHYSICAL (0 << 11) 72*433d6423SLionel Sambuc #define APIC_ICR_DM_LOGICAL (1 << 11) 73*433d6423SLionel Sambuc 74*433d6423SLionel Sambuc #define APIC_ICR_DELIVERY_PENDING (1 << 12) 75*433d6423SLionel Sambuc 76*433d6423SLionel Sambuc #define APIC_ICR_INT_POLARITY (1 << 13) 77*433d6423SLionel Sambuc #define APIC_ICR_INTPOL_LOW IS_SET(APIC_ICR_INT_POLARITY) 78*433d6423SLionel Sambuc #define APIC_ICR_INTPOL_HIGH IS_CLEAR(APIC_ICR_INT_POLARITY) 79*433d6423SLionel Sambuc 80*433d6423SLionel Sambuc #define APIC_ICR_LEVEL_ASSERT (1 << 14) 81*433d6423SLionel Sambuc #define APIC_ICR_LEVEL_DEASSERT (0 << 14) 82*433d6423SLionel Sambuc 83*433d6423SLionel Sambuc #define APIC_ICR_TRIGGER (1 << 15) 84*433d6423SLionel Sambuc #define APIC_ICR_TM_LEVEL IS_CLEAR(APIC_ICR_TRIGGER) 85*433d6423SLionel Sambuc #define APIC_ICR_TM_EDGE IS_CLEAR(APIC_ICR_TRIGGER) 86*433d6423SLionel Sambuc 87*433d6423SLionel Sambuc #define APIC_ICR_INT_MASK (1 << 16) 88*433d6423SLionel Sambuc 89*433d6423SLionel Sambuc #define APIC_ICR_DEST_FIELD (0 << 18) 90*433d6423SLionel Sambuc #define APIC_ICR_DEST_SELF (1 << 18) 91*433d6423SLionel Sambuc #define APIC_ICR_DEST_ALL (2 << 18) 92*433d6423SLionel Sambuc #define APIC_ICR_DEST_ALL_BUT_SELF (3 << 18) 93*433d6423SLionel Sambuc 94*433d6423SLionel Sambuc #define IA32_APIC_BASE 0x1b 95*433d6423SLionel Sambuc #define IA32_APIC_BASE_ENABLE_BIT 11 96*433d6423SLionel Sambuc 97*433d6423SLionel Sambuc /* FIXME we should spread the irqs across as many priority levels as possible 98*433d6423SLionel Sambuc * due to buggy hw */ 99*433d6423SLionel Sambuc #define LAPIC_VECTOR(irq) (IRQ0_VECTOR +(irq)) 100*433d6423SLionel Sambuc 101*433d6423SLionel Sambuc #define IOAPIC_IRQ_STATE_MASKED 0x1 102*433d6423SLionel Sambuc 103*433d6423SLionel Sambuc /* currently only 2 interrupt priority levels are used */ 104*433d6423SLionel Sambuc #define SPL0 0x0 105*433d6423SLionel Sambuc #define SPLHI 0xF 106*433d6423SLionel Sambuc 107*433d6423SLionel Sambuc 108*433d6423SLionel Sambuc struct io_apic io_apic[MAX_NR_IOAPICS]; 109*433d6423SLionel Sambuc unsigned nioapics; 110*433d6423SLionel Sambuc 111*433d6423SLionel Sambuc struct irq; 112*433d6423SLionel Sambuc typedef void (* eoi_method_t)(struct irq *); 113*433d6423SLionel Sambuc 114*433d6423SLionel Sambuc struct irq { 115*433d6423SLionel Sambuc struct io_apic * ioa; 116*433d6423SLionel Sambuc unsigned pin; 117*433d6423SLionel Sambuc unsigned vector; 118*433d6423SLionel Sambuc eoi_method_t eoi; 119*433d6423SLionel Sambuc unsigned state; 120*433d6423SLionel Sambuc }; 121*433d6423SLionel Sambuc 122*433d6423SLionel Sambuc static struct irq io_apic_irq[NR_IRQ_VECTORS]; 123*433d6423SLionel Sambuc 124*433d6423SLionel Sambuc /* 125*433d6423SLionel Sambuc * to make APIC work if SMP is not configured, we need to set the maximal number 126*433d6423SLionel Sambuc * of CPUS to 1, cpuid to return 0 and the current cpu is always BSP 127*433d6423SLionel Sambuc */ 128*433d6423SLionel Sambuc #ifdef CONFIG_SMP 129*433d6423SLionel Sambuc 130*433d6423SLionel Sambuc #include "kernel/smp.h" 131*433d6423SLionel Sambuc 132*433d6423SLionel Sambuc #endif 133*433d6423SLionel Sambuc 134*433d6423SLionel Sambuc #include "kernel/spinlock.h" 135*433d6423SLionel Sambuc 136*433d6423SLionel Sambuc 137*433d6423SLionel Sambuc #define lapic_write_icr1(val) lapic_write(LAPIC_ICR1, val) 138*433d6423SLionel Sambuc #define lapic_write_icr2(val) lapic_write(LAPIC_ICR2, val) 139*433d6423SLionel Sambuc 140*433d6423SLionel Sambuc #define lapic_read_icr1(x) lapic_read(LAPIC_ICR1) 141*433d6423SLionel Sambuc #define lapic_read_icr2(x) lapic_read(LAPIC_ICR2) 142*433d6423SLionel Sambuc 143*433d6423SLionel Sambuc #define is_boot_apic(apicid) ((apicid) == bsp_lapic_id) 144*433d6423SLionel Sambuc 145*433d6423SLionel Sambuc #define VERBOSE_APIC(x) x 146*433d6423SLionel Sambuc 147*433d6423SLionel Sambuc int ioapic_enabled; 148*433d6423SLionel Sambuc u32_t lapic_addr_vaddr; 149*433d6423SLionel Sambuc vir_bytes lapic_addr; 150*433d6423SLionel Sambuc vir_bytes lapic_eoi_addr; 151*433d6423SLionel Sambuc int bsp_lapic_id; 152*433d6423SLionel Sambuc 153*433d6423SLionel Sambuc static volatile unsigned probe_ticks; 154*433d6423SLionel Sambuc static u64_t tsc0, tsc1; 155*433d6423SLionel Sambuc static u32_t lapic_tctr0, lapic_tctr1; 156*433d6423SLionel Sambuc 157*433d6423SLionel Sambuc static unsigned apic_imcrp; 158*433d6423SLionel Sambuc static const unsigned nlints = 0; 159*433d6423SLionel Sambuc 160*433d6423SLionel Sambuc void arch_eoi(void) 161*433d6423SLionel Sambuc { 162*433d6423SLionel Sambuc apic_eoi(); 163*433d6423SLionel Sambuc } 164*433d6423SLionel Sambuc 165*433d6423SLionel Sambuc /* 166*433d6423SLionel Sambuc * FIXME this should be a cpulocal variable but there are some problems with 167*433d6423SLionel Sambuc * arch specific cpulocals. As this variable is write-once-read-only it is ok to 168*433d6423SLionel Sambuc * have at as an array until we resolve the cpulocals properly 169*433d6423SLionel Sambuc */ 170*433d6423SLionel Sambuc static u32_t lapic_bus_freq[CONFIG_MAX_CPUS]; 171*433d6423SLionel Sambuc /* the probe period will be roughly 100ms */ 172*433d6423SLionel Sambuc #define PROBE_TICKS (system_hz / 10) 173*433d6423SLionel Sambuc 174*433d6423SLionel Sambuc #define IOAPIC_IOREGSEL 0x0 175*433d6423SLionel Sambuc #define IOAPIC_IOWIN 0x10 176*433d6423SLionel Sambuc 177*433d6423SLionel Sambuc static u32_t ioapic_read(u32_t ioa_base, u32_t reg) 178*433d6423SLionel Sambuc { 179*433d6423SLionel Sambuc *((volatile u32_t *)(ioa_base + IOAPIC_IOREGSEL)) = (reg & 0xff); 180*433d6423SLionel Sambuc return *(volatile u32_t *)(ioa_base + IOAPIC_IOWIN); 181*433d6423SLionel Sambuc } 182*433d6423SLionel Sambuc 183*433d6423SLionel Sambuc static void ioapic_write(u32_t ioa_base, u8_t reg, u32_t val) 184*433d6423SLionel Sambuc { 185*433d6423SLionel Sambuc *((volatile u32_t *)(ioa_base + IOAPIC_IOREGSEL)) = reg; 186*433d6423SLionel Sambuc *((volatile u32_t *)(ioa_base + IOAPIC_IOWIN)) = val; 187*433d6423SLionel Sambuc } 188*433d6423SLionel Sambuc 189*433d6423SLionel Sambuc void lapic_microsec_sleep(unsigned count); 190*433d6423SLionel Sambuc void apic_idt_init(const int reset); 191*433d6423SLionel Sambuc 192*433d6423SLionel Sambuc static void ioapic_enable_pin(vir_bytes ioapic_addr, int pin) 193*433d6423SLionel Sambuc { 194*433d6423SLionel Sambuc u32_t lo = ioapic_read(ioapic_addr, IOAPIC_REDIR_TABLE + pin * 2); 195*433d6423SLionel Sambuc 196*433d6423SLionel Sambuc lo &= ~APIC_ICR_INT_MASK; 197*433d6423SLionel Sambuc ioapic_write(ioapic_addr, IOAPIC_REDIR_TABLE + pin * 2, lo); 198*433d6423SLionel Sambuc } 199*433d6423SLionel Sambuc 200*433d6423SLionel Sambuc static void ioapic_disable_pin(vir_bytes ioapic_addr, int pin) 201*433d6423SLionel Sambuc { 202*433d6423SLionel Sambuc u32_t lo = ioapic_read(ioapic_addr, IOAPIC_REDIR_TABLE + pin * 2); 203*433d6423SLionel Sambuc 204*433d6423SLionel Sambuc lo |= APIC_ICR_INT_MASK; 205*433d6423SLionel Sambuc ioapic_write(ioapic_addr, IOAPIC_REDIR_TABLE + pin * 2, lo); 206*433d6423SLionel Sambuc } 207*433d6423SLionel Sambuc 208*433d6423SLionel Sambuc #if 0 209*433d6423SLionel Sambuc static void ioapic_redirt_entry_read(void * ioapic_addr, 210*433d6423SLionel Sambuc int entry, 211*433d6423SLionel Sambuc u32_t *hi, 212*433d6423SLionel Sambuc u32_t *lo) 213*433d6423SLionel Sambuc { 214*433d6423SLionel Sambuc *lo = ioapic_read((u32_t)ioapic_addr, (u8_t) (IOAPIC_REDIR_TABLE + entry * 2)); 215*433d6423SLionel Sambuc *hi = ioapic_read((u32_t)ioapic_addr, (u8_t) (IOAPIC_REDIR_TABLE + entry * 2 + 1)); 216*433d6423SLionel Sambuc 217*433d6423SLionel Sambuc } 218*433d6423SLionel Sambuc #endif 219*433d6423SLionel Sambuc 220*433d6423SLionel Sambuc static void ioapic_redirt_entry_write(void * ioapic_addr, 221*433d6423SLionel Sambuc int entry, 222*433d6423SLionel Sambuc u32_t hi, 223*433d6423SLionel Sambuc u32_t lo) 224*433d6423SLionel Sambuc { 225*433d6423SLionel Sambuc #if 0 226*433d6423SLionel Sambuc VERBOSE_APIC(printf("IO apic redir entry %3d " 227*433d6423SLionel Sambuc "write 0x%08x 0x%08x\n", entry, hi, lo)); 228*433d6423SLionel Sambuc #endif 229*433d6423SLionel Sambuc ioapic_write((u32_t)ioapic_addr, (u8_t) (IOAPIC_REDIR_TABLE + entry * 2 + 1), hi); 230*433d6423SLionel Sambuc ioapic_write((u32_t)ioapic_addr, (u8_t) (IOAPIC_REDIR_TABLE + entry * 2), lo); 231*433d6423SLionel Sambuc } 232*433d6423SLionel Sambuc 233*433d6423SLionel Sambuc #define apic_read_tmr_vector(vec) \ 234*433d6423SLionel Sambuc lapic_read(LAPIC_TMR + 0x10 * ((vec) >> 5)) 235*433d6423SLionel Sambuc 236*433d6423SLionel Sambuc #define apic_read_irr_vector(vec) \ 237*433d6423SLionel Sambuc lapic_read(LAPIC_IRR + 0x10 * ((vec) >> 5)) 238*433d6423SLionel Sambuc 239*433d6423SLionel Sambuc #define apic_read_isr_vector(vec) \ 240*433d6423SLionel Sambuc lapic_read(LAPIC_ISR + 0x10 * ((vec) >> 5)) 241*433d6423SLionel Sambuc 242*433d6423SLionel Sambuc #define lapic_test_delivery_val(val, vector) ((val) & (1 << ((vector) & 0x1f))) 243*433d6423SLionel Sambuc 244*433d6423SLionel Sambuc static void ioapic_eoi_level(struct irq * irq) 245*433d6423SLionel Sambuc { 246*433d6423SLionel Sambuc reg_t tmr; 247*433d6423SLionel Sambuc 248*433d6423SLionel Sambuc tmr = apic_read_tmr_vector(irq->vector); 249*433d6423SLionel Sambuc apic_eoi(); 250*433d6423SLionel Sambuc 251*433d6423SLionel Sambuc /* 252*433d6423SLionel Sambuc * test if it was a level or edge triggered interrupt. If delivered as 253*433d6423SLionel Sambuc * edge exec the workaround for broken chipsets 254*433d6423SLionel Sambuc */ 255*433d6423SLionel Sambuc if (!lapic_test_delivery_val(tmr, irq->vector)) { 256*433d6423SLionel Sambuc int is_masked; 257*433d6423SLionel Sambuc u32_t lo; 258*433d6423SLionel Sambuc 259*433d6423SLionel Sambuc panic("EDGE instead of LEVEL!"); 260*433d6423SLionel Sambuc 261*433d6423SLionel Sambuc lo = ioapic_read(irq->ioa->addr, 262*433d6423SLionel Sambuc IOAPIC_REDIR_TABLE + irq->pin * 2); 263*433d6423SLionel Sambuc 264*433d6423SLionel Sambuc is_masked = lo & APIC_ICR_INT_MASK; 265*433d6423SLionel Sambuc 266*433d6423SLionel Sambuc /* set mask and edge */ 267*433d6423SLionel Sambuc lo |= APIC_ICR_INT_MASK; 268*433d6423SLionel Sambuc lo &= ~APIC_ICR_TRIGGER; 269*433d6423SLionel Sambuc ioapic_write(irq->ioa->addr, 270*433d6423SLionel Sambuc IOAPIC_REDIR_TABLE + irq->pin * 2, lo); 271*433d6423SLionel Sambuc 272*433d6423SLionel Sambuc /* set back to level and restore the mask bit */ 273*433d6423SLionel Sambuc lo = ioapic_read(irq->ioa->addr, 274*433d6423SLionel Sambuc IOAPIC_REDIR_TABLE + irq->pin * 2); 275*433d6423SLionel Sambuc 276*433d6423SLionel Sambuc lo |= APIC_ICR_TRIGGER; 277*433d6423SLionel Sambuc if (is_masked) 278*433d6423SLionel Sambuc lo |= APIC_ICR_INT_MASK; 279*433d6423SLionel Sambuc else 280*433d6423SLionel Sambuc lo &= ~APIC_ICR_INT_MASK; 281*433d6423SLionel Sambuc ioapic_write(irq->ioa->addr, 282*433d6423SLionel Sambuc IOAPIC_REDIR_TABLE + irq->pin * 2, lo); 283*433d6423SLionel Sambuc } 284*433d6423SLionel Sambuc } 285*433d6423SLionel Sambuc 286*433d6423SLionel Sambuc static void ioapic_eoi_edge(__unused struct irq * irq) 287*433d6423SLionel Sambuc { 288*433d6423SLionel Sambuc apic_eoi(); 289*433d6423SLionel Sambuc } 290*433d6423SLionel Sambuc 291*433d6423SLionel Sambuc void ioapic_eoi(int irq) 292*433d6423SLionel Sambuc { 293*433d6423SLionel Sambuc if (ioapic_enabled) { 294*433d6423SLionel Sambuc io_apic_irq[irq].eoi(&io_apic_irq[irq]); 295*433d6423SLionel Sambuc } 296*433d6423SLionel Sambuc else 297*433d6423SLionel Sambuc irq_8259_eoi(irq); 298*433d6423SLionel Sambuc } 299*433d6423SLionel Sambuc 300*433d6423SLionel Sambuc void ioapic_set_id(u32_t addr, unsigned int id) 301*433d6423SLionel Sambuc { 302*433d6423SLionel Sambuc ioapic_write(addr, IOAPIC_ID, id << 24); 303*433d6423SLionel Sambuc } 304*433d6423SLionel Sambuc 305*433d6423SLionel Sambuc int ioapic_enable_all(void) 306*433d6423SLionel Sambuc { 307*433d6423SLionel Sambuc i8259_disable(); 308*433d6423SLionel Sambuc 309*433d6423SLionel Sambuc if (apic_imcrp) { 310*433d6423SLionel Sambuc /* Select IMCR and disconnect 8259s. */ 311*433d6423SLionel Sambuc outb(0x22, 0x70); 312*433d6423SLionel Sambuc outb(0x23, 0x01); 313*433d6423SLionel Sambuc } 314*433d6423SLionel Sambuc 315*433d6423SLionel Sambuc return ioapic_enabled = 1; 316*433d6423SLionel Sambuc } 317*433d6423SLionel Sambuc 318*433d6423SLionel Sambuc /* disables a single IO APIC */ 319*433d6423SLionel Sambuc static void ioapic_disable(struct io_apic * ioapic) 320*433d6423SLionel Sambuc { 321*433d6423SLionel Sambuc unsigned p; 322*433d6423SLionel Sambuc 323*433d6423SLionel Sambuc for (p = 0; p < io_apic->pins; p++) { 324*433d6423SLionel Sambuc u32_t low_32, hi_32; 325*433d6423SLionel Sambuc low_32 = ioapic_read((u32_t)ioapic->addr, 326*433d6423SLionel Sambuc (uint8_t) (IOAPIC_REDIR_TABLE + p * 2)); 327*433d6423SLionel Sambuc hi_32 = ioapic_read((u32_t)ioapic->addr, 328*433d6423SLionel Sambuc (uint8_t) (IOAPIC_REDIR_TABLE + p * 2 + 1)); 329*433d6423SLionel Sambuc 330*433d6423SLionel Sambuc if (!(low_32 & APIC_ICR_INT_MASK)) { 331*433d6423SLionel Sambuc low_32 |= APIC_ICR_INT_MASK; 332*433d6423SLionel Sambuc ioapic_write((u32_t)ioapic->addr, 333*433d6423SLionel Sambuc (uint8_t) (IOAPIC_REDIR_TABLE + p * 2 + 1), hi_32); 334*433d6423SLionel Sambuc ioapic_write((u32_t)ioapic->addr, 335*433d6423SLionel Sambuc (uint8_t) (IOAPIC_REDIR_TABLE + p * 2), low_32); 336*433d6423SLionel Sambuc } 337*433d6423SLionel Sambuc } 338*433d6423SLionel Sambuc } 339*433d6423SLionel Sambuc 340*433d6423SLionel Sambuc /* disables all IO APICs */ 341*433d6423SLionel Sambuc void ioapic_disable_all(void) 342*433d6423SLionel Sambuc { 343*433d6423SLionel Sambuc unsigned ioa; 344*433d6423SLionel Sambuc if (!ioapic_enabled) 345*433d6423SLionel Sambuc return; 346*433d6423SLionel Sambuc 347*433d6423SLionel Sambuc for (ioa = 0 ; ioa < nioapics; ioa++) 348*433d6423SLionel Sambuc ioapic_disable(&io_apic[ioa]); 349*433d6423SLionel Sambuc 350*433d6423SLionel Sambuc ioapic_enabled = 0; /* io apic, disabled */ 351*433d6423SLionel Sambuc 352*433d6423SLionel Sambuc /* Enable 8259 - write 0x00 in OCW1 master and slave. */ 353*433d6423SLionel Sambuc if (apic_imcrp) { 354*433d6423SLionel Sambuc outb(0x22, 0x70); 355*433d6423SLionel Sambuc outb(0x23, 0x00); 356*433d6423SLionel Sambuc } 357*433d6423SLionel Sambuc 358*433d6423SLionel Sambuc lapic_microsec_sleep(200); /* to enable APIC to switch to PIC */ 359*433d6423SLionel Sambuc 360*433d6423SLionel Sambuc apic_idt_init(TRUE); /* reset */ 361*433d6423SLionel Sambuc idt_reload(); 362*433d6423SLionel Sambuc } 363*433d6423SLionel Sambuc 364*433d6423SLionel Sambuc static void ioapic_disable_irq(unsigned irq) 365*433d6423SLionel Sambuc { 366*433d6423SLionel Sambuc if(!(io_apic_irq[irq].ioa)) { 367*433d6423SLionel Sambuc printf("ioapic_disable_irq: no ioa set for irq %d!\n", irq); 368*433d6423SLionel Sambuc return; 369*433d6423SLionel Sambuc } 370*433d6423SLionel Sambuc 371*433d6423SLionel Sambuc assert(io_apic_irq[irq].ioa); 372*433d6423SLionel Sambuc 373*433d6423SLionel Sambuc ioapic_disable_pin(io_apic_irq[irq].ioa->addr, io_apic_irq[irq].pin); 374*433d6423SLionel Sambuc io_apic_irq[irq].state |= IOAPIC_IRQ_STATE_MASKED; 375*433d6423SLionel Sambuc } 376*433d6423SLionel Sambuc 377*433d6423SLionel Sambuc static void ioapic_enable_irq(unsigned irq) 378*433d6423SLionel Sambuc { 379*433d6423SLionel Sambuc if(!(io_apic_irq[irq].ioa)) { 380*433d6423SLionel Sambuc printf("ioapic_enable_irq: no ioa set for irq %d!\n", irq); 381*433d6423SLionel Sambuc return; 382*433d6423SLionel Sambuc } 383*433d6423SLionel Sambuc 384*433d6423SLionel Sambuc assert(io_apic_irq[irq].ioa); 385*433d6423SLionel Sambuc 386*433d6423SLionel Sambuc ioapic_enable_pin(io_apic_irq[irq].ioa->addr, io_apic_irq[irq].pin); 387*433d6423SLionel Sambuc io_apic_irq[irq].state &= ~IOAPIC_IRQ_STATE_MASKED; 388*433d6423SLionel Sambuc } 389*433d6423SLionel Sambuc 390*433d6423SLionel Sambuc void ioapic_unmask_irq(unsigned irq) 391*433d6423SLionel Sambuc { 392*433d6423SLionel Sambuc if (ioapic_enabled) 393*433d6423SLionel Sambuc ioapic_enable_irq(irq); 394*433d6423SLionel Sambuc else 395*433d6423SLionel Sambuc /* FIXME unlikely */ 396*433d6423SLionel Sambuc irq_8259_unmask(irq); 397*433d6423SLionel Sambuc } 398*433d6423SLionel Sambuc 399*433d6423SLionel Sambuc void ioapic_mask_irq(unsigned irq) 400*433d6423SLionel Sambuc { 401*433d6423SLionel Sambuc if (ioapic_enabled) 402*433d6423SLionel Sambuc ioapic_disable_irq(irq); 403*433d6423SLionel Sambuc else 404*433d6423SLionel Sambuc /* FIXME unlikely */ 405*433d6423SLionel Sambuc irq_8259_mask(irq); 406*433d6423SLionel Sambuc } 407*433d6423SLionel Sambuc 408*433d6423SLionel Sambuc unsigned int apicid(void) 409*433d6423SLionel Sambuc { 410*433d6423SLionel Sambuc return lapic_read(LAPIC_ID) >> 24; 411*433d6423SLionel Sambuc } 412*433d6423SLionel Sambuc 413*433d6423SLionel Sambuc static int calib_clk_handler(irq_hook_t * UNUSED(hook)) 414*433d6423SLionel Sambuc { 415*433d6423SLionel Sambuc u32_t tcrt; 416*433d6423SLionel Sambuc u64_t tsc; 417*433d6423SLionel Sambuc 418*433d6423SLionel Sambuc probe_ticks++; 419*433d6423SLionel Sambuc read_tsc_64(&tsc); 420*433d6423SLionel Sambuc tcrt = lapic_read(LAPIC_TIMER_CCR); 421*433d6423SLionel Sambuc 422*433d6423SLionel Sambuc 423*433d6423SLionel Sambuc if (probe_ticks == 1) { 424*433d6423SLionel Sambuc lapic_tctr0 = tcrt; 425*433d6423SLionel Sambuc tsc0 = tsc; 426*433d6423SLionel Sambuc } 427*433d6423SLionel Sambuc else if (probe_ticks == PROBE_TICKS) { 428*433d6423SLionel Sambuc lapic_tctr1 = tcrt; 429*433d6423SLionel Sambuc tsc1 = tsc; 430*433d6423SLionel Sambuc stop_8253A_timer(); 431*433d6423SLionel Sambuc } 432*433d6423SLionel Sambuc 433*433d6423SLionel Sambuc BKL_UNLOCK(); 434*433d6423SLionel Sambuc return 1; 435*433d6423SLionel Sambuc } 436*433d6423SLionel Sambuc 437*433d6423SLionel Sambuc static int spurious_irq_handler(irq_hook_t * UNUSED(hook)) 438*433d6423SLionel Sambuc { 439*433d6423SLionel Sambuc /* 440*433d6423SLionel Sambuc * Do nothing, only unlock the kernel so we do not deadlock! 441*433d6423SLionel Sambuc */ 442*433d6423SLionel Sambuc BKL_UNLOCK(); 443*433d6423SLionel Sambuc return 1; 444*433d6423SLionel Sambuc } 445*433d6423SLionel Sambuc 446*433d6423SLionel Sambuc static void apic_calibrate_clocks(unsigned cpu) 447*433d6423SLionel Sambuc { 448*433d6423SLionel Sambuc u32_t lvtt, val, lapic_delta; 449*433d6423SLionel Sambuc u64_t tsc_delta; 450*433d6423SLionel Sambuc u64_t cpu_freq; 451*433d6423SLionel Sambuc 452*433d6423SLionel Sambuc irq_hook_t calib_clk, spurious_irq; 453*433d6423SLionel Sambuc 454*433d6423SLionel Sambuc BOOT_VERBOSE(printf("Calibrating clock\n")); 455*433d6423SLionel Sambuc /* 456*433d6423SLionel Sambuc * Set Initial count register to the highest value so it does not 457*433d6423SLionel Sambuc * underflow during the testing period 458*433d6423SLionel Sambuc * */ 459*433d6423SLionel Sambuc val = 0xffffffff; 460*433d6423SLionel Sambuc lapic_write (LAPIC_TIMER_ICR, val); 461*433d6423SLionel Sambuc 462*433d6423SLionel Sambuc /* Set Current count register */ 463*433d6423SLionel Sambuc val = 0; 464*433d6423SLionel Sambuc lapic_write (LAPIC_TIMER_CCR, val); 465*433d6423SLionel Sambuc 466*433d6423SLionel Sambuc lvtt = lapic_read(LAPIC_TIMER_DCR) & ~0x0b; 467*433d6423SLionel Sambuc /* Set Divide configuration register to 1 */ 468*433d6423SLionel Sambuc lvtt = APIC_TDCR_1; 469*433d6423SLionel Sambuc lapic_write(LAPIC_TIMER_DCR, lvtt); 470*433d6423SLionel Sambuc 471*433d6423SLionel Sambuc /* 472*433d6423SLionel Sambuc * mask the APIC timer interrupt in the LVT Timer Register so that we 473*433d6423SLionel Sambuc * don't get an interrupt upon underflow which we don't know how to 474*433d6423SLionel Sambuc * handle right know. If underflow happens, the system will not continue 475*433d6423SLionel Sambuc * as something is wrong with the clock IRQ 0 and we cannot calibrate 476*433d6423SLionel Sambuc * the clock which mean that we cannot run processes 477*433d6423SLionel Sambuc */ 478*433d6423SLionel Sambuc lvtt = lapic_read (LAPIC_LVTTR); 479*433d6423SLionel Sambuc lvtt |= APIC_LVTT_MASK; 480*433d6423SLionel Sambuc lapic_write (LAPIC_LVTTR, lvtt); 481*433d6423SLionel Sambuc 482*433d6423SLionel Sambuc /* set the probe, we use the legacy timer, IRQ 0 */ 483*433d6423SLionel Sambuc put_irq_handler(&calib_clk, CLOCK_IRQ, calib_clk_handler); 484*433d6423SLionel Sambuc 485*433d6423SLionel Sambuc /* 486*433d6423SLionel Sambuc * A spurious interrupt may occur during the clock calibration. Since we 487*433d6423SLionel Sambuc * do this calibration in kernel, we need a special handler which will 488*433d6423SLionel Sambuc * leave the BKL unlocked like the clock handler. This is a corner case, 489*433d6423SLionel Sambuc * boot time only situation 490*433d6423SLionel Sambuc */ 491*433d6423SLionel Sambuc put_irq_handler(&spurious_irq, SPURIOUS_IRQ, spurious_irq_handler); 492*433d6423SLionel Sambuc 493*433d6423SLionel Sambuc /* set the PIC timer to get some time */ 494*433d6423SLionel Sambuc init_8253A_timer(system_hz); 495*433d6423SLionel Sambuc 496*433d6423SLionel Sambuc /* 497*433d6423SLionel Sambuc * We must unlock BKL here as the in-kernel interrupt will lock it 498*433d6423SLionel Sambuc * again. The handler will unlock it after it is done. This is 499*433d6423SLionel Sambuc * absolutely safe as only the BSP is running. It is just a workaround a 500*433d6423SLionel Sambuc * corner case for APIC timer calibration 501*433d6423SLionel Sambuc */ 502*433d6423SLionel Sambuc BKL_UNLOCK(); 503*433d6423SLionel Sambuc intr_enable(); 504*433d6423SLionel Sambuc 505*433d6423SLionel Sambuc /* loop for some time to get a sample */ 506*433d6423SLionel Sambuc while(probe_ticks < PROBE_TICKS) { 507*433d6423SLionel Sambuc intr_enable(); 508*433d6423SLionel Sambuc } 509*433d6423SLionel Sambuc 510*433d6423SLionel Sambuc intr_disable(); 511*433d6423SLionel Sambuc BKL_LOCK(); 512*433d6423SLionel Sambuc 513*433d6423SLionel Sambuc /* remove the probe */ 514*433d6423SLionel Sambuc rm_irq_handler(&calib_clk); 515*433d6423SLionel Sambuc rm_irq_handler(&spurious_irq); 516*433d6423SLionel Sambuc 517*433d6423SLionel Sambuc lapic_delta = lapic_tctr0 - lapic_tctr1; 518*433d6423SLionel Sambuc tsc_delta = tsc1 - tsc0; 519*433d6423SLionel Sambuc 520*433d6423SLionel Sambuc lapic_bus_freq[cpuid] = system_hz * lapic_delta / (PROBE_TICKS - 1); 521*433d6423SLionel Sambuc BOOT_VERBOSE(printf("APIC bus freq %u MHz\n", 522*433d6423SLionel Sambuc lapic_bus_freq[cpuid] / 1000000)); 523*433d6423SLionel Sambuc cpu_freq = (tsc_delta / (PROBE_TICKS - 1)) * make64(system_hz, 0); 524*433d6423SLionel Sambuc cpu_set_freq(cpuid, cpu_freq); 525*433d6423SLionel Sambuc cpu_info[cpuid].freq = (unsigned long)(cpu_freq / 1000000); 526*433d6423SLionel Sambuc BOOT_VERBOSE(cpu_print_freq(cpuid)); 527*433d6423SLionel Sambuc } 528*433d6423SLionel Sambuc 529*433d6423SLionel Sambuc void lapic_set_timer_one_shot(const u32_t usec) 530*433d6423SLionel Sambuc { 531*433d6423SLionel Sambuc /* sleep in micro seconds */ 532*433d6423SLionel Sambuc u32_t lvtt; 533*433d6423SLionel Sambuc u32_t ticks_per_us; 534*433d6423SLionel Sambuc const u8_t cpu = cpuid; 535*433d6423SLionel Sambuc 536*433d6423SLionel Sambuc ticks_per_us = (lapic_bus_freq[cpu] / 1000000) * config_apic_timer_x; 537*433d6423SLionel Sambuc 538*433d6423SLionel Sambuc lapic_write(LAPIC_TIMER_ICR, usec * ticks_per_us); 539*433d6423SLionel Sambuc 540*433d6423SLionel Sambuc lvtt = APIC_TDCR_1; 541*433d6423SLionel Sambuc lapic_write(LAPIC_TIMER_DCR, lvtt); 542*433d6423SLionel Sambuc 543*433d6423SLionel Sambuc /* configure timer as one-shot */ 544*433d6423SLionel Sambuc lvtt = APIC_TIMER_INT_VECTOR; 545*433d6423SLionel Sambuc lapic_write(LAPIC_LVTTR, lvtt); 546*433d6423SLionel Sambuc } 547*433d6423SLionel Sambuc 548*433d6423SLionel Sambuc void lapic_set_timer_periodic(const unsigned freq) 549*433d6423SLionel Sambuc { 550*433d6423SLionel Sambuc /* sleep in micro seconds */ 551*433d6423SLionel Sambuc u32_t lvtt; 552*433d6423SLionel Sambuc u32_t lapic_ticks_per_clock_tick; 553*433d6423SLionel Sambuc const u8_t cpu = cpuid; 554*433d6423SLionel Sambuc 555*433d6423SLionel Sambuc lapic_ticks_per_clock_tick = (lapic_bus_freq[cpu] / freq) * config_apic_timer_x; 556*433d6423SLionel Sambuc 557*433d6423SLionel Sambuc lvtt = APIC_TDCR_1; 558*433d6423SLionel Sambuc lapic_write(LAPIC_TIMER_DCR, lvtt); 559*433d6423SLionel Sambuc 560*433d6423SLionel Sambuc /* configure timer as periodic */ 561*433d6423SLionel Sambuc lvtt = APIC_LVTT_TM | APIC_TIMER_INT_VECTOR; 562*433d6423SLionel Sambuc lapic_write(LAPIC_LVTTR, lvtt); 563*433d6423SLionel Sambuc 564*433d6423SLionel Sambuc lapic_write(LAPIC_TIMER_ICR, lapic_ticks_per_clock_tick); 565*433d6423SLionel Sambuc } 566*433d6423SLionel Sambuc 567*433d6423SLionel Sambuc void lapic_stop_timer(void) 568*433d6423SLionel Sambuc { 569*433d6423SLionel Sambuc u32_t lvtt; 570*433d6423SLionel Sambuc lvtt = lapic_read(LAPIC_LVTTR); 571*433d6423SLionel Sambuc lapic_write(LAPIC_LVTTR, lvtt | APIC_LVTT_MASK); 572*433d6423SLionel Sambuc /* zero the current counter so it can be restarted again */ 573*433d6423SLionel Sambuc lapic_write(LAPIC_TIMER_ICR, 0); 574*433d6423SLionel Sambuc lapic_write(LAPIC_TIMER_CCR, 0); 575*433d6423SLionel Sambuc } 576*433d6423SLionel Sambuc 577*433d6423SLionel Sambuc void lapic_restart_timer(void) 578*433d6423SLionel Sambuc { 579*433d6423SLionel Sambuc /* restart the timer only if the counter reached zero, i.e. expired */ 580*433d6423SLionel Sambuc if (lapic_read(LAPIC_TIMER_CCR) == 0) 581*433d6423SLionel Sambuc lapic_set_timer_one_shot(1000000/system_hz); 582*433d6423SLionel Sambuc } 583*433d6423SLionel Sambuc 584*433d6423SLionel Sambuc void lapic_microsec_sleep(unsigned count) 585*433d6423SLionel Sambuc { 586*433d6423SLionel Sambuc lapic_set_timer_one_shot(count); 587*433d6423SLionel Sambuc while (lapic_read(LAPIC_TIMER_CCR)) 588*433d6423SLionel Sambuc arch_pause(); 589*433d6423SLionel Sambuc } 590*433d6423SLionel Sambuc 591*433d6423SLionel Sambuc static u32_t lapic_errstatus(void) 592*433d6423SLionel Sambuc { 593*433d6423SLionel Sambuc lapic_write(LAPIC_ESR, 0); 594*433d6423SLionel Sambuc return lapic_read(LAPIC_ESR); 595*433d6423SLionel Sambuc } 596*433d6423SLionel Sambuc 597*433d6423SLionel Sambuc #ifdef CONFIG_SMP 598*433d6423SLionel Sambuc static int lapic_disable_in_msr(void) 599*433d6423SLionel Sambuc { 600*433d6423SLionel Sambuc u32_t msr_hi, msr_lo; 601*433d6423SLionel Sambuc 602*433d6423SLionel Sambuc ia32_msr_read(IA32_APIC_BASE, &msr_hi, &msr_lo); 603*433d6423SLionel Sambuc 604*433d6423SLionel Sambuc msr_lo &= ~(1 << IA32_APIC_BASE_ENABLE_BIT); 605*433d6423SLionel Sambuc ia32_msr_write(IA32_APIC_BASE, msr_hi, msr_lo); 606*433d6423SLionel Sambuc 607*433d6423SLionel Sambuc return 1; 608*433d6423SLionel Sambuc } 609*433d6423SLionel Sambuc #endif /* CONFIG_SMP */ 610*433d6423SLionel Sambuc 611*433d6423SLionel Sambuc void lapic_disable(void) 612*433d6423SLionel Sambuc { 613*433d6423SLionel Sambuc /* Disable current APIC and close interrupts from PIC */ 614*433d6423SLionel Sambuc u32_t val; 615*433d6423SLionel Sambuc 616*433d6423SLionel Sambuc if (!lapic_addr) 617*433d6423SLionel Sambuc return; 618*433d6423SLionel Sambuc 619*433d6423SLionel Sambuc #ifdef CONFIG_SMP 620*433d6423SLionel Sambuc if (cpu_is_bsp(cpuid) && !apic_imcrp) 621*433d6423SLionel Sambuc #endif 622*433d6423SLionel Sambuc { 623*433d6423SLionel Sambuc /* leave it enabled if imcr is not set */ 624*433d6423SLionel Sambuc val = lapic_read(LAPIC_LINT0); 625*433d6423SLionel Sambuc val &= ~(APIC_ICR_DM_MASK|APIC_ICR_INT_MASK); 626*433d6423SLionel Sambuc val |= APIC_ICR_DM_EXTINT; /* ExtINT at LINT0 */ 627*433d6423SLionel Sambuc lapic_write (LAPIC_LINT0, val); 628*433d6423SLionel Sambuc return; 629*433d6423SLionel Sambuc } 630*433d6423SLionel Sambuc 631*433d6423SLionel Sambuc #ifdef CONFIG_SMP 632*433d6423SLionel Sambuc val = lapic_read(LAPIC_LINT0) & 0xFFFE58FF; 633*433d6423SLionel Sambuc val |= APIC_ICR_INT_MASK; 634*433d6423SLionel Sambuc lapic_write (LAPIC_LINT0, val); 635*433d6423SLionel Sambuc 636*433d6423SLionel Sambuc val = lapic_read(LAPIC_LINT1) & 0xFFFE58FF; 637*433d6423SLionel Sambuc val |= APIC_ICR_INT_MASK; 638*433d6423SLionel Sambuc lapic_write (LAPIC_LINT1, val); 639*433d6423SLionel Sambuc 640*433d6423SLionel Sambuc val = lapic_read(LAPIC_SIVR) & 0xFFFFFF00; 641*433d6423SLionel Sambuc val &= ~APIC_ENABLE; 642*433d6423SLionel Sambuc lapic_write(LAPIC_SIVR, val); 643*433d6423SLionel Sambuc 644*433d6423SLionel Sambuc lapic_disable_in_msr(); 645*433d6423SLionel Sambuc #endif /* CONFIG_SMP */ 646*433d6423SLionel Sambuc } 647*433d6423SLionel Sambuc 648*433d6423SLionel Sambuc static int lapic_enable_in_msr(void) 649*433d6423SLionel Sambuc { 650*433d6423SLionel Sambuc u32_t msr_hi, msr_lo; 651*433d6423SLionel Sambuc 652*433d6423SLionel Sambuc ia32_msr_read(IA32_APIC_BASE, &msr_hi, &msr_lo); 653*433d6423SLionel Sambuc 654*433d6423SLionel Sambuc #if 0 655*433d6423SLionel Sambuc u32_t addr; 656*433d6423SLionel Sambuc /*FIXME this is a problem on AP */ 657*433d6423SLionel Sambuc /* 658*433d6423SLionel Sambuc * FIXME if the location is different (unlikely) then the one we expect, 659*433d6423SLionel Sambuc * update it 660*433d6423SLionel Sambuc */ 661*433d6423SLionel Sambuc addr = (msr_lo >> 12) | ((msr_hi & 0xf) << 20); 662*433d6423SLionel Sambuc if (addr != (lapic_addr >> 12)) { 663*433d6423SLionel Sambuc if (msr_hi & 0xf) { 664*433d6423SLionel Sambuc printf("ERROR : APIC address needs more then 32 bits\n"); 665*433d6423SLionel Sambuc return 0; 666*433d6423SLionel Sambuc } 667*433d6423SLionel Sambuc lapic_addr = msr_lo & ~((1 << 12) - 1); 668*433d6423SLionel Sambuc } 669*433d6423SLionel Sambuc #endif 670*433d6423SLionel Sambuc 671*433d6423SLionel Sambuc msr_lo |= (1 << IA32_APIC_BASE_ENABLE_BIT); 672*433d6423SLionel Sambuc ia32_msr_write(IA32_APIC_BASE, msr_hi, msr_lo); 673*433d6423SLionel Sambuc 674*433d6423SLionel Sambuc return 1; 675*433d6423SLionel Sambuc } 676*433d6423SLionel Sambuc 677*433d6423SLionel Sambuc int lapic_enable(unsigned cpu) 678*433d6423SLionel Sambuc { 679*433d6423SLionel Sambuc u32_t val, nlvt; 680*433d6423SLionel Sambuc 681*433d6423SLionel Sambuc if (!lapic_addr) 682*433d6423SLionel Sambuc return 0; 683*433d6423SLionel Sambuc 684*433d6423SLionel Sambuc cpu_has_tsc = _cpufeature(_CPUF_I386_TSC); 685*433d6423SLionel Sambuc if (!cpu_has_tsc) { 686*433d6423SLionel Sambuc printf("CPU lacks timestamp counter, " 687*433d6423SLionel Sambuc "cannot calibrate LAPIC timer\n"); 688*433d6423SLionel Sambuc return 0; 689*433d6423SLionel Sambuc } 690*433d6423SLionel Sambuc 691*433d6423SLionel Sambuc if (!lapic_enable_in_msr()) 692*433d6423SLionel Sambuc return 0; 693*433d6423SLionel Sambuc 694*433d6423SLionel Sambuc /* set the highest priority for ever */ 695*433d6423SLionel Sambuc lapic_write(LAPIC_TPR, 0x0); 696*433d6423SLionel Sambuc 697*433d6423SLionel Sambuc lapic_eoi_addr = LAPIC_EOI; 698*433d6423SLionel Sambuc /* clear error state register. */ 699*433d6423SLionel Sambuc val = lapic_errstatus (); 700*433d6423SLionel Sambuc 701*433d6423SLionel Sambuc /* Enable Local APIC and set the spurious vector to 0xff. */ 702*433d6423SLionel Sambuc val = lapic_read(LAPIC_SIVR); 703*433d6423SLionel Sambuc val |= APIC_ENABLE | APIC_SPURIOUS_INT_VECTOR; 704*433d6423SLionel Sambuc val &= ~APIC_FOCUS_DISABLED; 705*433d6423SLionel Sambuc lapic_write(LAPIC_SIVR, val); 706*433d6423SLionel Sambuc (void) lapic_read(LAPIC_SIVR); 707*433d6423SLionel Sambuc 708*433d6423SLionel Sambuc apic_eoi(); 709*433d6423SLionel Sambuc 710*433d6423SLionel Sambuc /* Program Logical Destination Register. */ 711*433d6423SLionel Sambuc val = lapic_read(LAPIC_LDR) & ~0xFF000000; 712*433d6423SLionel Sambuc val |= (cpu & 0xFF) << 24; 713*433d6423SLionel Sambuc lapic_write(LAPIC_LDR, val); 714*433d6423SLionel Sambuc 715*433d6423SLionel Sambuc /* Program Destination Format Register for Flat mode. */ 716*433d6423SLionel Sambuc val = lapic_read(LAPIC_DFR) | 0xF0000000; 717*433d6423SLionel Sambuc lapic_write (LAPIC_DFR, val); 718*433d6423SLionel Sambuc 719*433d6423SLionel Sambuc val = lapic_read (LAPIC_LVTER) & 0xFFFFFF00; 720*433d6423SLionel Sambuc lapic_write (LAPIC_LVTER, val); 721*433d6423SLionel Sambuc 722*433d6423SLionel Sambuc nlvt = (lapic_read(LAPIC_VERSION)>>16) & 0xFF; 723*433d6423SLionel Sambuc 724*433d6423SLionel Sambuc if(nlvt >= 4) { 725*433d6423SLionel Sambuc val = lapic_read(LAPIC_LVTTMR); 726*433d6423SLionel Sambuc lapic_write(LAPIC_LVTTMR, val | APIC_ICR_INT_MASK); 727*433d6423SLionel Sambuc } 728*433d6423SLionel Sambuc 729*433d6423SLionel Sambuc if(nlvt >= 5) { 730*433d6423SLionel Sambuc val = lapic_read(LAPIC_LVTPCR); 731*433d6423SLionel Sambuc lapic_write(LAPIC_LVTPCR, val | APIC_ICR_INT_MASK); 732*433d6423SLionel Sambuc } 733*433d6423SLionel Sambuc 734*433d6423SLionel Sambuc /* setup TPR to allow all interrupts. */ 735*433d6423SLionel Sambuc val = lapic_read (LAPIC_TPR); 736*433d6423SLionel Sambuc /* accept all interrupts */ 737*433d6423SLionel Sambuc lapic_write (LAPIC_TPR, val & ~0xFF); 738*433d6423SLionel Sambuc 739*433d6423SLionel Sambuc (void) lapic_read (LAPIC_SIVR); 740*433d6423SLionel Sambuc apic_eoi(); 741*433d6423SLionel Sambuc 742*433d6423SLionel Sambuc apic_calibrate_clocks(cpu); 743*433d6423SLionel Sambuc BOOT_VERBOSE(printf("APIC timer calibrated\n")); 744*433d6423SLionel Sambuc 745*433d6423SLionel Sambuc return 1; 746*433d6423SLionel Sambuc } 747*433d6423SLionel Sambuc 748*433d6423SLionel Sambuc void apic_spurios_intr_handler(void) 749*433d6423SLionel Sambuc { 750*433d6423SLionel Sambuc static unsigned x; 751*433d6423SLionel Sambuc 752*433d6423SLionel Sambuc x++; 753*433d6423SLionel Sambuc if (x == 1 || (x % 100) == 0) 754*433d6423SLionel Sambuc printf("WARNING spurious interrupt(s) %d on cpu %d\n", x, cpuid); 755*433d6423SLionel Sambuc } 756*433d6423SLionel Sambuc 757*433d6423SLionel Sambuc void apic_error_intr_handler(void) 758*433d6423SLionel Sambuc { 759*433d6423SLionel Sambuc static unsigned x; 760*433d6423SLionel Sambuc 761*433d6423SLionel Sambuc x++; 762*433d6423SLionel Sambuc if (x == 1 || (x % 100) == 0) 763*433d6423SLionel Sambuc printf("WARNING apic error (0x%x) interrupt(s) %d on cpu %d\n", 764*433d6423SLionel Sambuc lapic_errstatus(), x, cpuid); 765*433d6423SLionel Sambuc } 766*433d6423SLionel Sambuc 767*433d6423SLionel Sambuc static struct gate_table_s gate_table_ioapic[] = { 768*433d6423SLionel Sambuc { apic_hwint0, LAPIC_VECTOR( 0), INTR_PRIVILEGE }, 769*433d6423SLionel Sambuc { apic_hwint1, LAPIC_VECTOR( 1), INTR_PRIVILEGE }, 770*433d6423SLionel Sambuc { apic_hwint2, LAPIC_VECTOR( 2), INTR_PRIVILEGE }, 771*433d6423SLionel Sambuc { apic_hwint3, LAPIC_VECTOR( 3), INTR_PRIVILEGE }, 772*433d6423SLionel Sambuc { apic_hwint4, LAPIC_VECTOR( 4), INTR_PRIVILEGE }, 773*433d6423SLionel Sambuc { apic_hwint5, LAPIC_VECTOR( 5), INTR_PRIVILEGE }, 774*433d6423SLionel Sambuc { apic_hwint6, LAPIC_VECTOR( 6), INTR_PRIVILEGE }, 775*433d6423SLionel Sambuc { apic_hwint7, LAPIC_VECTOR( 7), INTR_PRIVILEGE }, 776*433d6423SLionel Sambuc { apic_hwint8, LAPIC_VECTOR( 8), INTR_PRIVILEGE }, 777*433d6423SLionel Sambuc { apic_hwint9, LAPIC_VECTOR( 9), INTR_PRIVILEGE }, 778*433d6423SLionel Sambuc { apic_hwint10, LAPIC_VECTOR(10), INTR_PRIVILEGE }, 779*433d6423SLionel Sambuc { apic_hwint11, LAPIC_VECTOR(11), INTR_PRIVILEGE }, 780*433d6423SLionel Sambuc { apic_hwint12, LAPIC_VECTOR(12), INTR_PRIVILEGE }, 781*433d6423SLionel Sambuc { apic_hwint13, LAPIC_VECTOR(13), INTR_PRIVILEGE }, 782*433d6423SLionel Sambuc { apic_hwint14, LAPIC_VECTOR(14), INTR_PRIVILEGE }, 783*433d6423SLionel Sambuc { apic_hwint15, LAPIC_VECTOR(15), INTR_PRIVILEGE }, 784*433d6423SLionel Sambuc { apic_hwint16, LAPIC_VECTOR(16), INTR_PRIVILEGE }, 785*433d6423SLionel Sambuc { apic_hwint17, LAPIC_VECTOR(17), INTR_PRIVILEGE }, 786*433d6423SLionel Sambuc { apic_hwint18, LAPIC_VECTOR(18), INTR_PRIVILEGE }, 787*433d6423SLionel Sambuc { apic_hwint19, LAPIC_VECTOR(19), INTR_PRIVILEGE }, 788*433d6423SLionel Sambuc { apic_hwint20, LAPIC_VECTOR(20), INTR_PRIVILEGE }, 789*433d6423SLionel Sambuc { apic_hwint21, LAPIC_VECTOR(21), INTR_PRIVILEGE }, 790*433d6423SLionel Sambuc { apic_hwint22, LAPIC_VECTOR(22), INTR_PRIVILEGE }, 791*433d6423SLionel Sambuc { apic_hwint23, LAPIC_VECTOR(23), INTR_PRIVILEGE }, 792*433d6423SLionel Sambuc { apic_hwint24, LAPIC_VECTOR(24), INTR_PRIVILEGE }, 793*433d6423SLionel Sambuc { apic_hwint25, LAPIC_VECTOR(25), INTR_PRIVILEGE }, 794*433d6423SLionel Sambuc { apic_hwint26, LAPIC_VECTOR(26), INTR_PRIVILEGE }, 795*433d6423SLionel Sambuc { apic_hwint27, LAPIC_VECTOR(27), INTR_PRIVILEGE }, 796*433d6423SLionel Sambuc { apic_hwint28, LAPIC_VECTOR(28), INTR_PRIVILEGE }, 797*433d6423SLionel Sambuc { apic_hwint29, LAPIC_VECTOR(29), INTR_PRIVILEGE }, 798*433d6423SLionel Sambuc { apic_hwint30, LAPIC_VECTOR(30), INTR_PRIVILEGE }, 799*433d6423SLionel Sambuc { apic_hwint31, LAPIC_VECTOR(31), INTR_PRIVILEGE }, 800*433d6423SLionel Sambuc { apic_hwint32, LAPIC_VECTOR(32), INTR_PRIVILEGE }, 801*433d6423SLionel Sambuc { apic_hwint33, LAPIC_VECTOR(33), INTR_PRIVILEGE }, 802*433d6423SLionel Sambuc { apic_hwint34, LAPIC_VECTOR(34), INTR_PRIVILEGE }, 803*433d6423SLionel Sambuc { apic_hwint35, LAPIC_VECTOR(35), INTR_PRIVILEGE }, 804*433d6423SLionel Sambuc { apic_hwint36, LAPIC_VECTOR(36), INTR_PRIVILEGE }, 805*433d6423SLionel Sambuc { apic_hwint37, LAPIC_VECTOR(37), INTR_PRIVILEGE }, 806*433d6423SLionel Sambuc { apic_hwint38, LAPIC_VECTOR(38), INTR_PRIVILEGE }, 807*433d6423SLionel Sambuc { apic_hwint39, LAPIC_VECTOR(39), INTR_PRIVILEGE }, 808*433d6423SLionel Sambuc { apic_hwint40, LAPIC_VECTOR(40), INTR_PRIVILEGE }, 809*433d6423SLionel Sambuc { apic_hwint41, LAPIC_VECTOR(41), INTR_PRIVILEGE }, 810*433d6423SLionel Sambuc { apic_hwint42, LAPIC_VECTOR(42), INTR_PRIVILEGE }, 811*433d6423SLionel Sambuc { apic_hwint43, LAPIC_VECTOR(43), INTR_PRIVILEGE }, 812*433d6423SLionel Sambuc { apic_hwint44, LAPIC_VECTOR(44), INTR_PRIVILEGE }, 813*433d6423SLionel Sambuc { apic_hwint45, LAPIC_VECTOR(45), INTR_PRIVILEGE }, 814*433d6423SLionel Sambuc { apic_hwint46, LAPIC_VECTOR(46), INTR_PRIVILEGE }, 815*433d6423SLionel Sambuc { apic_hwint47, LAPIC_VECTOR(47), INTR_PRIVILEGE }, 816*433d6423SLionel Sambuc { apic_hwint48, LAPIC_VECTOR(48), INTR_PRIVILEGE }, 817*433d6423SLionel Sambuc { apic_hwint49, LAPIC_VECTOR(49), INTR_PRIVILEGE }, 818*433d6423SLionel Sambuc { apic_hwint50, LAPIC_VECTOR(50), INTR_PRIVILEGE }, 819*433d6423SLionel Sambuc { apic_hwint51, LAPIC_VECTOR(51), INTR_PRIVILEGE }, 820*433d6423SLionel Sambuc { apic_hwint52, LAPIC_VECTOR(52), INTR_PRIVILEGE }, 821*433d6423SLionel Sambuc { apic_hwint53, LAPIC_VECTOR(53), INTR_PRIVILEGE }, 822*433d6423SLionel Sambuc { apic_hwint54, LAPIC_VECTOR(54), INTR_PRIVILEGE }, 823*433d6423SLionel Sambuc { apic_hwint55, LAPIC_VECTOR(55), INTR_PRIVILEGE }, 824*433d6423SLionel Sambuc { apic_hwint56, LAPIC_VECTOR(56), INTR_PRIVILEGE }, 825*433d6423SLionel Sambuc { apic_hwint57, LAPIC_VECTOR(57), INTR_PRIVILEGE }, 826*433d6423SLionel Sambuc { apic_hwint58, LAPIC_VECTOR(58), INTR_PRIVILEGE }, 827*433d6423SLionel Sambuc { apic_hwint59, LAPIC_VECTOR(59), INTR_PRIVILEGE }, 828*433d6423SLionel Sambuc { apic_hwint60, LAPIC_VECTOR(60), INTR_PRIVILEGE }, 829*433d6423SLionel Sambuc { apic_hwint61, LAPIC_VECTOR(61), INTR_PRIVILEGE }, 830*433d6423SLionel Sambuc { apic_hwint62, LAPIC_VECTOR(62), INTR_PRIVILEGE }, 831*433d6423SLionel Sambuc { apic_hwint63, LAPIC_VECTOR(63), INTR_PRIVILEGE }, 832*433d6423SLionel Sambuc { apic_spurios_intr, APIC_SPURIOUS_INT_VECTOR, INTR_PRIVILEGE }, 833*433d6423SLionel Sambuc { apic_error_intr, APIC_ERROR_INT_VECTOR, INTR_PRIVILEGE }, 834*433d6423SLionel Sambuc { NULL, 0, 0} 835*433d6423SLionel Sambuc }; 836*433d6423SLionel Sambuc 837*433d6423SLionel Sambuc static struct gate_table_s gate_table_common[] = { 838*433d6423SLionel Sambuc { ipc_entry_softint_orig, IPC_VECTOR_ORIG, USER_PRIVILEGE }, 839*433d6423SLionel Sambuc { kernel_call_entry_orig, KERN_CALL_VECTOR_ORIG, USER_PRIVILEGE }, 840*433d6423SLionel Sambuc { ipc_entry_softint_um, IPC_VECTOR_UM, USER_PRIVILEGE }, 841*433d6423SLionel Sambuc { kernel_call_entry_um, KERN_CALL_VECTOR_UM, USER_PRIVILEGE }, 842*433d6423SLionel Sambuc { NULL, 0, 0} 843*433d6423SLionel Sambuc }; 844*433d6423SLionel Sambuc 845*433d6423SLionel Sambuc #ifdef CONFIG_SMP 846*433d6423SLionel Sambuc static struct gate_table_s gate_table_smp[] = { 847*433d6423SLionel Sambuc { apic_ipi_sched_intr, APIC_SMP_SCHED_PROC_VECTOR, INTR_PRIVILEGE }, 848*433d6423SLionel Sambuc { apic_ipi_halt_intr, APIC_SMP_CPU_HALT_VECTOR, INTR_PRIVILEGE }, 849*433d6423SLionel Sambuc { NULL, 0, 0} 850*433d6423SLionel Sambuc }; 851*433d6423SLionel Sambuc #endif 852*433d6423SLionel Sambuc 853*433d6423SLionel Sambuc #ifdef APIC_DEBUG 854*433d6423SLionel Sambuc static void lapic_set_dummy_handlers(void) 855*433d6423SLionel Sambuc { 856*433d6423SLionel Sambuc char * handler; 857*433d6423SLionel Sambuc int vect = 32; /* skip the reserved vectors */ 858*433d6423SLionel Sambuc 859*433d6423SLionel Sambuc handler = &lapic_intr_dummy_handles_start; 860*433d6423SLionel Sambuc handler += vect * LAPIC_INTR_DUMMY_HANDLER_SIZE; 861*433d6423SLionel Sambuc for(; handler < &lapic_intr_dummy_handles_end; 862*433d6423SLionel Sambuc handler += LAPIC_INTR_DUMMY_HANDLER_SIZE) { 863*433d6423SLionel Sambuc int_gate_idt(vect++, (vir_bytes) handler, 864*433d6423SLionel Sambuc PRESENT | INT_GATE_TYPE | 865*433d6423SLionel Sambuc (INTR_PRIVILEGE << DPL_SHIFT)); 866*433d6423SLionel Sambuc } 867*433d6423SLionel Sambuc } 868*433d6423SLionel Sambuc #endif 869*433d6423SLionel Sambuc 870*433d6423SLionel Sambuc /* Build descriptors for interrupt gates in IDT. */ 871*433d6423SLionel Sambuc void apic_idt_init(const int reset) 872*433d6423SLionel Sambuc { 873*433d6423SLionel Sambuc u32_t val; 874*433d6423SLionel Sambuc 875*433d6423SLionel Sambuc /* Set up idt tables for smp mode. 876*433d6423SLionel Sambuc */ 877*433d6423SLionel Sambuc int is_bsp; 878*433d6423SLionel Sambuc 879*433d6423SLionel Sambuc if (reset) { 880*433d6423SLionel Sambuc idt_copy_vectors_pic(); 881*433d6423SLionel Sambuc idt_copy_vectors(gate_table_common); 882*433d6423SLionel Sambuc return; 883*433d6423SLionel Sambuc } 884*433d6423SLionel Sambuc 885*433d6423SLionel Sambuc is_bsp = is_boot_apic(apicid()); 886*433d6423SLionel Sambuc 887*433d6423SLionel Sambuc #ifdef APIC_DEBUG 888*433d6423SLionel Sambuc if (is_bsp) 889*433d6423SLionel Sambuc printf("APIC debugging is enabled\n"); 890*433d6423SLionel Sambuc lapic_set_dummy_handlers(); 891*433d6423SLionel Sambuc #endif 892*433d6423SLionel Sambuc 893*433d6423SLionel Sambuc /* Build descriptors for interrupt gates in IDT. */ 894*433d6423SLionel Sambuc if (ioapic_enabled) 895*433d6423SLionel Sambuc idt_copy_vectors(gate_table_ioapic); 896*433d6423SLionel Sambuc else 897*433d6423SLionel Sambuc idt_copy_vectors_pic(); 898*433d6423SLionel Sambuc 899*433d6423SLionel Sambuc idt_copy_vectors(gate_table_common); 900*433d6423SLionel Sambuc 901*433d6423SLionel Sambuc #ifdef CONFIG_SMP 902*433d6423SLionel Sambuc idt_copy_vectors(gate_table_smp); 903*433d6423SLionel Sambuc #endif 904*433d6423SLionel Sambuc 905*433d6423SLionel Sambuc /* Setup error interrupt vector */ 906*433d6423SLionel Sambuc val = lapic_read(LAPIC_LVTER); 907*433d6423SLionel Sambuc val |= APIC_ERROR_INT_VECTOR; 908*433d6423SLionel Sambuc val &= ~ APIC_ICR_INT_MASK; 909*433d6423SLionel Sambuc lapic_write(LAPIC_LVTER, val); 910*433d6423SLionel Sambuc (void) lapic_read(LAPIC_LVTER); 911*433d6423SLionel Sambuc 912*433d6423SLionel Sambuc /* configure the timer interupt handler */ 913*433d6423SLionel Sambuc if (is_bsp) { 914*433d6423SLionel Sambuc BOOT_VERBOSE(printf("Initiating APIC timer handler\n")); 915*433d6423SLionel Sambuc /* register the timer interrupt handler for this CPU */ 916*433d6423SLionel Sambuc int_gate_idt(APIC_TIMER_INT_VECTOR, (vir_bytes) lapic_timer_int_handler, 917*433d6423SLionel Sambuc PRESENT | INT_GATE_TYPE | (INTR_PRIVILEGE << DPL_SHIFT)); 918*433d6423SLionel Sambuc } 919*433d6423SLionel Sambuc 920*433d6423SLionel Sambuc } 921*433d6423SLionel Sambuc 922*433d6423SLionel Sambuc static int acpi_get_ioapics(struct io_apic * ioa, unsigned * nioa, unsigned max) 923*433d6423SLionel Sambuc { 924*433d6423SLionel Sambuc unsigned n = 0; 925*433d6423SLionel Sambuc struct acpi_madt_ioapic * acpi_ioa; 926*433d6423SLionel Sambuc 927*433d6423SLionel Sambuc while (n < max) { 928*433d6423SLionel Sambuc acpi_ioa = acpi_get_ioapic_next(); 929*433d6423SLionel Sambuc if (acpi_ioa == NULL) 930*433d6423SLionel Sambuc break; 931*433d6423SLionel Sambuc 932*433d6423SLionel Sambuc assert(acpi_ioa->address); 933*433d6423SLionel Sambuc 934*433d6423SLionel Sambuc ioa[n].id = acpi_ioa->id; 935*433d6423SLionel Sambuc ioa[n].addr = acpi_ioa->address; 936*433d6423SLionel Sambuc ioa[n].paddr = (phys_bytes) acpi_ioa->address; 937*433d6423SLionel Sambuc ioa[n].gsi_base = acpi_ioa->global_int_base; 938*433d6423SLionel Sambuc ioa[n].pins = ((ioapic_read(ioa[n].addr, 939*433d6423SLionel Sambuc IOAPIC_VERSION) & 0xff0000) >> 16)+1; 940*433d6423SLionel Sambuc printf("IO APIC idx %d id %d addr 0x%lx paddr 0x%lx pins %d\n", 941*433d6423SLionel Sambuc n, acpi_ioa->id, ioa[n].addr, ioa[n].paddr, 942*433d6423SLionel Sambuc ioa[n].pins); 943*433d6423SLionel Sambuc n++; 944*433d6423SLionel Sambuc } 945*433d6423SLionel Sambuc 946*433d6423SLionel Sambuc *nioa = n; 947*433d6423SLionel Sambuc return n; 948*433d6423SLionel Sambuc } 949*433d6423SLionel Sambuc 950*433d6423SLionel Sambuc int detect_ioapics(void) 951*433d6423SLionel Sambuc { 952*433d6423SLionel Sambuc int status; 953*433d6423SLionel Sambuc 954*433d6423SLionel Sambuc if (machine.acpi_rsdp) { 955*433d6423SLionel Sambuc status = acpi_get_ioapics(io_apic, &nioapics, MAX_NR_IOAPICS); 956*433d6423SLionel Sambuc } else { 957*433d6423SLionel Sambuc status = 0; 958*433d6423SLionel Sambuc } 959*433d6423SLionel Sambuc if (!status) { 960*433d6423SLionel Sambuc /* try something different like MPS */ 961*433d6423SLionel Sambuc } 962*433d6423SLionel Sambuc 963*433d6423SLionel Sambuc return status; 964*433d6423SLionel Sambuc } 965*433d6423SLionel Sambuc 966*433d6423SLionel Sambuc #ifdef CONFIG_SMP 967*433d6423SLionel Sambuc 968*433d6423SLionel Sambuc void apic_send_ipi(unsigned vector, unsigned cpu, int type) 969*433d6423SLionel Sambuc { 970*433d6423SLionel Sambuc u32_t icr1, icr2; 971*433d6423SLionel Sambuc 972*433d6423SLionel Sambuc if (ncpus == 1) 973*433d6423SLionel Sambuc /* no need of sending an IPI */ 974*433d6423SLionel Sambuc return; 975*433d6423SLionel Sambuc 976*433d6423SLionel Sambuc while (lapic_read_icr1() & APIC_ICR_DELIVERY_PENDING) 977*433d6423SLionel Sambuc arch_pause(); 978*433d6423SLionel Sambuc 979*433d6423SLionel Sambuc icr1 = lapic_read_icr1() & 0xFFF0F800; 980*433d6423SLionel Sambuc icr2 = lapic_read_icr2() & 0xFFFFFF; 981*433d6423SLionel Sambuc 982*433d6423SLionel Sambuc switch (type) { 983*433d6423SLionel Sambuc case APIC_IPI_DEST: 984*433d6423SLionel Sambuc if (!cpu_is_ready(cpu)) 985*433d6423SLionel Sambuc return; 986*433d6423SLionel Sambuc lapic_write_icr2(icr2 | (cpuid2apicid[cpu] << 24)); 987*433d6423SLionel Sambuc lapic_write_icr1(icr1 | APIC_ICR_DEST_FIELD | vector); 988*433d6423SLionel Sambuc break; 989*433d6423SLionel Sambuc case APIC_IPI_SELF: 990*433d6423SLionel Sambuc lapic_write_icr2(icr2); 991*433d6423SLionel Sambuc lapic_write_icr1(icr1 | APIC_ICR_DEST_SELF | vector); 992*433d6423SLionel Sambuc break; 993*433d6423SLionel Sambuc case APIC_IPI_TO_ALL_BUT_SELF: 994*433d6423SLionel Sambuc lapic_write_icr2(icr2); 995*433d6423SLionel Sambuc lapic_write_icr1(icr1 | APIC_ICR_DEST_ALL_BUT_SELF | vector); 996*433d6423SLionel Sambuc break; 997*433d6423SLionel Sambuc case APIC_IPI_TO_ALL: 998*433d6423SLionel Sambuc lapic_write_icr2(icr2); 999*433d6423SLionel Sambuc lapic_write_icr1(icr1 | APIC_ICR_DEST_ALL | vector); 1000*433d6423SLionel Sambuc break; 1001*433d6423SLionel Sambuc default: 1002*433d6423SLionel Sambuc printf("WARNING : unknown send ipi type request\n"); 1003*433d6423SLionel Sambuc } 1004*433d6423SLionel Sambuc 1005*433d6423SLionel Sambuc } 1006*433d6423SLionel Sambuc 1007*433d6423SLionel Sambuc int apic_send_startup_ipi(unsigned cpu, phys_bytes trampoline) 1008*433d6423SLionel Sambuc { 1009*433d6423SLionel Sambuc int timeout; 1010*433d6423SLionel Sambuc u32_t errstatus = 0; 1011*433d6423SLionel Sambuc int i; 1012*433d6423SLionel Sambuc 1013*433d6423SLionel Sambuc /* INIT-SIPI-SIPI sequence */ 1014*433d6423SLionel Sambuc 1015*433d6423SLionel Sambuc for (i = 0; i < 2; i++) { 1016*433d6423SLionel Sambuc u32_t val; 1017*433d6423SLionel Sambuc 1018*433d6423SLionel Sambuc /* clear err status */ 1019*433d6423SLionel Sambuc lapic_errstatus(); 1020*433d6423SLionel Sambuc 1021*433d6423SLionel Sambuc /* set target pe */ 1022*433d6423SLionel Sambuc val = lapic_read(LAPIC_ICR2) & 0xFFFFFF; 1023*433d6423SLionel Sambuc val |= cpuid2apicid[cpu] << 24; 1024*433d6423SLionel Sambuc lapic_write(LAPIC_ICR2, val); 1025*433d6423SLionel Sambuc 1026*433d6423SLionel Sambuc /* send SIPI */ 1027*433d6423SLionel Sambuc val = lapic_read(LAPIC_ICR1) & 0xFFF32000; 1028*433d6423SLionel Sambuc val |= APIC_ICR_LEVEL_ASSERT |APIC_ICR_DM_STARTUP; 1029*433d6423SLionel Sambuc val |= (((u32_t)trampoline >> 12)&0xff); 1030*433d6423SLionel Sambuc lapic_write(LAPIC_ICR1, val); 1031*433d6423SLionel Sambuc 1032*433d6423SLionel Sambuc timeout = 1000; 1033*433d6423SLionel Sambuc 1034*433d6423SLionel Sambuc /* wait for 200 micro-seconds*/ 1035*433d6423SLionel Sambuc lapic_microsec_sleep (200); 1036*433d6423SLionel Sambuc errstatus = 0; 1037*433d6423SLionel Sambuc 1038*433d6423SLionel Sambuc while ((lapic_read(LAPIC_ICR1) & APIC_ICR_DELIVERY_PENDING) && 1039*433d6423SLionel Sambuc !errstatus) { 1040*433d6423SLionel Sambuc errstatus = lapic_errstatus(); 1041*433d6423SLionel Sambuc timeout--; 1042*433d6423SLionel Sambuc if (!timeout) break; 1043*433d6423SLionel Sambuc } 1044*433d6423SLionel Sambuc 1045*433d6423SLionel Sambuc /* skip this one and continue with another cpu */ 1046*433d6423SLionel Sambuc if (errstatus) 1047*433d6423SLionel Sambuc return -1; 1048*433d6423SLionel Sambuc } 1049*433d6423SLionel Sambuc 1050*433d6423SLionel Sambuc return 0; 1051*433d6423SLionel Sambuc } 1052*433d6423SLionel Sambuc 1053*433d6423SLionel Sambuc int apic_send_init_ipi(unsigned cpu, phys_bytes trampoline) 1054*433d6423SLionel Sambuc { 1055*433d6423SLionel Sambuc u32_t ptr, errstatus = 0; 1056*433d6423SLionel Sambuc int timeout; 1057*433d6423SLionel Sambuc 1058*433d6423SLionel Sambuc /* set the warm reset vector */ 1059*433d6423SLionel Sambuc ptr = (u32_t)(trampoline & 0xF); 1060*433d6423SLionel Sambuc phys_copy(0x467, vir2phys(&ptr), sizeof(u16_t )); 1061*433d6423SLionel Sambuc ptr = (u32_t)(trampoline >> 4); 1062*433d6423SLionel Sambuc phys_copy(0x469, vir2phys(&ptr), sizeof(u16_t )); 1063*433d6423SLionel Sambuc 1064*433d6423SLionel Sambuc /* set shutdown code */ 1065*433d6423SLionel Sambuc outb (RTC_INDEX, 0xF); 1066*433d6423SLionel Sambuc outb (RTC_IO, 0xA); 1067*433d6423SLionel Sambuc 1068*433d6423SLionel Sambuc /* clear error state register. */ 1069*433d6423SLionel Sambuc (void) lapic_errstatus(); 1070*433d6423SLionel Sambuc 1071*433d6423SLionel Sambuc /* assert INIT IPI , No Shorthand, destination mode : physical */ 1072*433d6423SLionel Sambuc lapic_write(LAPIC_ICR2, (lapic_read (LAPIC_ICR2) & 0xFFFFFF) | 1073*433d6423SLionel Sambuc (cpuid2apicid[cpu] << 24)); 1074*433d6423SLionel Sambuc lapic_write(LAPIC_ICR1, (lapic_read (LAPIC_ICR1) & 0xFFF32000) | 1075*433d6423SLionel Sambuc APIC_ICR_DM_INIT | APIC_ICR_TM_LEVEL | APIC_ICR_LEVEL_ASSERT); 1076*433d6423SLionel Sambuc 1077*433d6423SLionel Sambuc timeout = 1000; 1078*433d6423SLionel Sambuc 1079*433d6423SLionel Sambuc /* sleep for 200 micro-seconds */ 1080*433d6423SLionel Sambuc lapic_microsec_sleep(200); 1081*433d6423SLionel Sambuc 1082*433d6423SLionel Sambuc errstatus = 0; 1083*433d6423SLionel Sambuc 1084*433d6423SLionel Sambuc while ((lapic_read(LAPIC_ICR1) & APIC_ICR_DELIVERY_PENDING) && !errstatus) { 1085*433d6423SLionel Sambuc errstatus = lapic_errstatus(); 1086*433d6423SLionel Sambuc timeout--; 1087*433d6423SLionel Sambuc if (!timeout) break; 1088*433d6423SLionel Sambuc } 1089*433d6423SLionel Sambuc 1090*433d6423SLionel Sambuc if (errstatus) 1091*433d6423SLionel Sambuc return -1; /* to continue with a new processor */ 1092*433d6423SLionel Sambuc 1093*433d6423SLionel Sambuc /* clear error state register. */ 1094*433d6423SLionel Sambuc lapic_errstatus(); 1095*433d6423SLionel Sambuc 1096*433d6423SLionel Sambuc /* deassert INIT IPI , No Shorthand, destination mode : physical */ 1097*433d6423SLionel Sambuc lapic_write(LAPIC_ICR2, (lapic_read (LAPIC_ICR2) & 0xFFFFFF) | 1098*433d6423SLionel Sambuc (cpuid2apicid[cpu] << 24)); 1099*433d6423SLionel Sambuc lapic_write(LAPIC_ICR1, (lapic_read (LAPIC_ICR1) & 0xFFF32000) | 1100*433d6423SLionel Sambuc APIC_ICR_DEST_ALL | APIC_ICR_TM_LEVEL); 1101*433d6423SLionel Sambuc 1102*433d6423SLionel Sambuc timeout = 1000; 1103*433d6423SLionel Sambuc errstatus = 0; 1104*433d6423SLionel Sambuc 1105*433d6423SLionel Sambuc /* sleep for 200 micro-seconds */ 1106*433d6423SLionel Sambuc lapic_microsec_sleep(200); 1107*433d6423SLionel Sambuc 1108*433d6423SLionel Sambuc while ((lapic_read(LAPIC_ICR1)&APIC_ICR_DELIVERY_PENDING) && !errstatus) { 1109*433d6423SLionel Sambuc errstatus = lapic_errstatus(); 1110*433d6423SLionel Sambuc timeout--; 1111*433d6423SLionel Sambuc if(!timeout) break; 1112*433d6423SLionel Sambuc } 1113*433d6423SLionel Sambuc 1114*433d6423SLionel Sambuc if (errstatus) 1115*433d6423SLionel Sambuc return -1; /* with the new processor */ 1116*433d6423SLionel Sambuc 1117*433d6423SLionel Sambuc /* clear error state register. */ 1118*433d6423SLionel Sambuc (void) lapic_errstatus(); 1119*433d6423SLionel Sambuc 1120*433d6423SLionel Sambuc /* wait 10ms */ 1121*433d6423SLionel Sambuc lapic_microsec_sleep (10000); 1122*433d6423SLionel Sambuc 1123*433d6423SLionel Sambuc return 0; 1124*433d6423SLionel Sambuc } 1125*433d6423SLionel Sambuc #endif 1126*433d6423SLionel Sambuc 1127*433d6423SLionel Sambuc #ifndef CONFIG_SMP 1128*433d6423SLionel Sambuc int apic_single_cpu_init(void) 1129*433d6423SLionel Sambuc { 1130*433d6423SLionel Sambuc if (!cpu_feature_apic_on_chip()) 1131*433d6423SLionel Sambuc return 0; 1132*433d6423SLionel Sambuc 1133*433d6423SLionel Sambuc lapic_addr = LOCAL_APIC_DEF_ADDR; 1134*433d6423SLionel Sambuc ioapic_enabled = 0; 1135*433d6423SLionel Sambuc 1136*433d6423SLionel Sambuc if (!lapic_enable(0)) { 1137*433d6423SLionel Sambuc lapic_addr = 0x0; 1138*433d6423SLionel Sambuc return 0; 1139*433d6423SLionel Sambuc } 1140*433d6423SLionel Sambuc 1141*433d6423SLionel Sambuc bsp_lapic_id = apicid(); 1142*433d6423SLionel Sambuc printf("Boot cpu apic id %d\n", bsp_lapic_id); 1143*433d6423SLionel Sambuc 1144*433d6423SLionel Sambuc acpi_init(); 1145*433d6423SLionel Sambuc 1146*433d6423SLionel Sambuc if (!detect_ioapics()) { 1147*433d6423SLionel Sambuc lapic_disable(); 1148*433d6423SLionel Sambuc lapic_addr = 0x0; 1149*433d6423SLionel Sambuc return 0; 1150*433d6423SLionel Sambuc } 1151*433d6423SLionel Sambuc 1152*433d6423SLionel Sambuc ioapic_enable_all(); 1153*433d6423SLionel Sambuc 1154*433d6423SLionel Sambuc if (ioapic_enabled) 1155*433d6423SLionel Sambuc machine.apic_enabled = 1; 1156*433d6423SLionel Sambuc 1157*433d6423SLionel Sambuc apic_idt_init(0); /* Not a reset ! */ 1158*433d6423SLionel Sambuc idt_reload(); 1159*433d6423SLionel Sambuc return 1; 1160*433d6423SLionel Sambuc } 1161*433d6423SLionel Sambuc #endif 1162*433d6423SLionel Sambuc 1163*433d6423SLionel Sambuc static eoi_method_t set_eoi_method(unsigned irq) 1164*433d6423SLionel Sambuc { 1165*433d6423SLionel Sambuc /* 1166*433d6423SLionel Sambuc * in APIC mode the lowest 16 IRQs are reserved for legacy (E)ISA edge 1167*433d6423SLionel Sambuc * triggered interrupts. All the rest is for PCI level triggered 1168*433d6423SLionel Sambuc * interrupts 1169*433d6423SLionel Sambuc */ 1170*433d6423SLionel Sambuc if (irq < 16) 1171*433d6423SLionel Sambuc return ioapic_eoi_edge; 1172*433d6423SLionel Sambuc else 1173*433d6423SLionel Sambuc return ioapic_eoi_level; 1174*433d6423SLionel Sambuc } 1175*433d6423SLionel Sambuc 1176*433d6423SLionel Sambuc void set_irq_redir_low(unsigned irq, u32_t * low) 1177*433d6423SLionel Sambuc { 1178*433d6423SLionel Sambuc u32_t val = 0; 1179*433d6423SLionel Sambuc 1180*433d6423SLionel Sambuc /* clear the polarity, trigger, mask and vector fields */ 1181*433d6423SLionel Sambuc val &= ~(APIC_ICR_VECTOR | APIC_ICR_INT_MASK | 1182*433d6423SLionel Sambuc APIC_ICR_TRIGGER | APIC_ICR_INT_POLARITY); 1183*433d6423SLionel Sambuc 1184*433d6423SLionel Sambuc if (irq < 16) { 1185*433d6423SLionel Sambuc /* ISA active-high */ 1186*433d6423SLionel Sambuc val &= ~APIC_ICR_INT_POLARITY; 1187*433d6423SLionel Sambuc /* ISA edge triggered */ 1188*433d6423SLionel Sambuc val &= ~APIC_ICR_TRIGGER; 1189*433d6423SLionel Sambuc } 1190*433d6423SLionel Sambuc else { 1191*433d6423SLionel Sambuc /* PCI active-low */ 1192*433d6423SLionel Sambuc val |= APIC_ICR_INT_POLARITY; 1193*433d6423SLionel Sambuc /* PCI level triggered */ 1194*433d6423SLionel Sambuc val |= APIC_ICR_TRIGGER; 1195*433d6423SLionel Sambuc } 1196*433d6423SLionel Sambuc 1197*433d6423SLionel Sambuc val |= io_apic_irq[irq].vector; 1198*433d6423SLionel Sambuc 1199*433d6423SLionel Sambuc *low = val; 1200*433d6423SLionel Sambuc } 1201*433d6423SLionel Sambuc 1202*433d6423SLionel Sambuc void ioapic_set_irq(unsigned irq) 1203*433d6423SLionel Sambuc { 1204*433d6423SLionel Sambuc unsigned ioa; 1205*433d6423SLionel Sambuc 1206*433d6423SLionel Sambuc assert(irq < NR_IRQ_VECTORS); 1207*433d6423SLionel Sambuc 1208*433d6423SLionel Sambuc /* shared irq, already set */ 1209*433d6423SLionel Sambuc if (io_apic_irq[irq].ioa && io_apic_irq[irq].eoi) 1210*433d6423SLionel Sambuc return; 1211*433d6423SLionel Sambuc 1212*433d6423SLionel Sambuc assert(!io_apic_irq[irq].ioa || !io_apic_irq[irq].eoi); 1213*433d6423SLionel Sambuc 1214*433d6423SLionel Sambuc for (ioa = 0; ioa < nioapics; ioa++) { 1215*433d6423SLionel Sambuc if (io_apic[ioa].gsi_base <= irq && 1216*433d6423SLionel Sambuc io_apic[ioa].gsi_base + 1217*433d6423SLionel Sambuc io_apic[ioa].pins > irq) { 1218*433d6423SLionel Sambuc u32_t hi_32, low_32; 1219*433d6423SLionel Sambuc 1220*433d6423SLionel Sambuc io_apic_irq[irq].ioa = &io_apic[ioa]; 1221*433d6423SLionel Sambuc io_apic_irq[irq].pin = irq - io_apic[ioa].gsi_base; 1222*433d6423SLionel Sambuc io_apic_irq[irq].eoi = set_eoi_method(irq); 1223*433d6423SLionel Sambuc io_apic_irq[irq].vector = LAPIC_VECTOR(irq); 1224*433d6423SLionel Sambuc 1225*433d6423SLionel Sambuc set_irq_redir_low(irq, &low_32); 1226*433d6423SLionel Sambuc /* 1227*433d6423SLionel Sambuc * route the interrupts to the bsp by default 1228*433d6423SLionel Sambuc */ 1229*433d6423SLionel Sambuc hi_32 = bsp_lapic_id << 24; 1230*433d6423SLionel Sambuc ioapic_redirt_entry_write((void *) io_apic[ioa].addr, 1231*433d6423SLionel Sambuc io_apic_irq[irq].pin, hi_32, low_32); 1232*433d6423SLionel Sambuc } 1233*433d6423SLionel Sambuc } 1234*433d6423SLionel Sambuc } 1235*433d6423SLionel Sambuc 1236*433d6423SLionel Sambuc void ioapic_unset_irq(unsigned irq) 1237*433d6423SLionel Sambuc { 1238*433d6423SLionel Sambuc assert(irq < NR_IRQ_VECTORS); 1239*433d6423SLionel Sambuc 1240*433d6423SLionel Sambuc ioapic_disable_irq(irq); 1241*433d6423SLionel Sambuc io_apic_irq[irq].ioa = NULL; 1242*433d6423SLionel Sambuc io_apic_irq[irq].eoi = NULL; 1243*433d6423SLionel Sambuc } 1244*433d6423SLionel Sambuc 1245*433d6423SLionel Sambuc void ioapic_reset_pic(void) 1246*433d6423SLionel Sambuc { 1247*433d6423SLionel Sambuc apic_idt_init(TRUE); /* reset */ 1248*433d6423SLionel Sambuc idt_reload(); 1249*433d6423SLionel Sambuc 1250*433d6423SLionel Sambuc /* Enable 8259 - write 0x00 in OCW1 1251*433d6423SLionel Sambuc * master and slave. */ 1252*433d6423SLionel Sambuc outb(0x22, 0x70); 1253*433d6423SLionel Sambuc outb(0x23, 0x00); 1254*433d6423SLionel Sambuc } 1255*433d6423SLionel Sambuc 1256*433d6423SLionel Sambuc static void irq_lapic_status(int irq) 1257*433d6423SLionel Sambuc { 1258*433d6423SLionel Sambuc u32_t lo; 1259*433d6423SLionel Sambuc reg_t tmr, irr, isr; 1260*433d6423SLionel Sambuc int vector; 1261*433d6423SLionel Sambuc struct irq * intr; 1262*433d6423SLionel Sambuc 1263*433d6423SLionel Sambuc intr = &io_apic_irq[irq]; 1264*433d6423SLionel Sambuc 1265*433d6423SLionel Sambuc if (!intr->ioa) 1266*433d6423SLionel Sambuc return; 1267*433d6423SLionel Sambuc 1268*433d6423SLionel Sambuc vector = LAPIC_VECTOR(irq); 1269*433d6423SLionel Sambuc tmr = apic_read_tmr_vector(vector); 1270*433d6423SLionel Sambuc irr = apic_read_irr_vector(vector); 1271*433d6423SLionel Sambuc isr = apic_read_isr_vector(vector); 1272*433d6423SLionel Sambuc 1273*433d6423SLionel Sambuc 1274*433d6423SLionel Sambuc if (lapic_test_delivery_val(isr, vector)) { 1275*433d6423SLionel Sambuc printf("IRQ %d vec %d trigger %s irr %d isr %d\n", 1276*433d6423SLionel Sambuc irq, vector, 1277*433d6423SLionel Sambuc lapic_test_delivery_val(tmr, vector) ? 1278*433d6423SLionel Sambuc "level" : "edge", 1279*433d6423SLionel Sambuc lapic_test_delivery_val(irr, vector) ? 1 : 0, 1280*433d6423SLionel Sambuc lapic_test_delivery_val(isr, vector) ? 1 : 0); 1281*433d6423SLionel Sambuc } else { 1282*433d6423SLionel Sambuc printf("IRQ %d vec %d irr %d\n", 1283*433d6423SLionel Sambuc irq, vector, 1284*433d6423SLionel Sambuc lapic_test_delivery_val(irr, vector) ? 1 : 0); 1285*433d6423SLionel Sambuc } 1286*433d6423SLionel Sambuc 1287*433d6423SLionel Sambuc lo = ioapic_read(intr->ioa->addr, 1288*433d6423SLionel Sambuc IOAPIC_REDIR_TABLE + intr->pin * 2); 1289*433d6423SLionel Sambuc printf("\tpin %2d vec 0x%02x ioa %d redir_lo 0x%08x %s\n", 1290*433d6423SLionel Sambuc intr->pin, 1291*433d6423SLionel Sambuc intr->vector, 1292*433d6423SLionel Sambuc intr->ioa->id, 1293*433d6423SLionel Sambuc lo, 1294*433d6423SLionel Sambuc intr->state & IOAPIC_IRQ_STATE_MASKED ? 1295*433d6423SLionel Sambuc "masked" : "unmasked"); 1296*433d6423SLionel Sambuc } 1297*433d6423SLionel Sambuc 1298*433d6423SLionel Sambuc void dump_apic_irq_state(void) 1299*433d6423SLionel Sambuc { 1300*433d6423SLionel Sambuc int irq; 1301*433d6423SLionel Sambuc 1302*433d6423SLionel Sambuc printf("--- IRQs state dump ---\n"); 1303*433d6423SLionel Sambuc for (irq = 0; irq < NR_IRQ_VECTORS; irq++) { 1304*433d6423SLionel Sambuc irq_lapic_status(irq); 1305*433d6423SLionel Sambuc } 1306*433d6423SLionel Sambuc printf("--- all ---\n"); 1307*433d6423SLionel Sambuc } 1308