io_apic.c revision 145057
11590Srgrimes/*- 21590Srgrimes * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org> 31590Srgrimes * All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. Neither the name of the author nor the names of any co-contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 301590Srgrimes#include <sys/cdefs.h> 311590Srgrimes__FBSDID("$FreeBSD: head/sys/i386/i386/io_apic.c 145057 2005-04-14 06:33:23Z jhb $"); 321590Srgrimes 331590Srgrimes#include "opt_isa.h" 3423246Swosch#include "opt_no_mixed_mode.h" 351590Srgrimes 361590Srgrimes#include <sys/param.h> 371590Srgrimes#include <sys/systm.h> 3823246Swosch#include <sys/bus.h> 391590Srgrimes#include <sys/kernel.h> 401590Srgrimes#include <sys/malloc.h> 4127605Scharnier#include <sys/lock.h> 421590Srgrimes#include <sys/mutex.h> 4327605Scharnier 4427605Scharnier#include <vm/vm.h> 4550477Speter#include <vm/pmap.h> 461590Srgrimes 471590Srgrimes#include <machine/apicreg.h> 481590Srgrimes#include <machine/frame.h> 491590Srgrimes#include <machine/intr_machdep.h> 501590Srgrimes#include <machine/apicvar.h> 511590Srgrimes#include <machine/segments.h> 521590Srgrimes 531590Srgrimes#define IOAPIC_ISA_INTS 16 5423246Swosch#define IOAPIC_MEM_REGION 32 551590Srgrimes#define IOAPIC_REDTBL_LO(i) (IOAPIC_REDTBL + (i) * 2) 561590Srgrimes#define IOAPIC_REDTBL_HI(i) (IOAPIC_REDTBL_LO(i) + 1) 5757339Sshin 581590Srgrimes#define VECTOR_EXTINT 252 591590Srgrimes#define VECTOR_NMI 253 601590Srgrimes#define VECTOR_SMI 254 6116423Sache#define VECTOR_DISABLED 255 6216423Sache 631590Srgrimes#define DEST_NONE -1 641590Srgrimes#define DEST_EXTINT -2 651590Srgrimes 661590Srgrimes#define TODO printf("%s: not implemented!\n", __func__) 6740102Smarkm 6841079Sjdpstatic MALLOC_DEFINE(M_IOAPIC, "I/O APIC", "I/O APIC structures"); 6916423Sache 701590Srgrimes/* 711590Srgrimes * New interrupt support code.. 721590Srgrimes * 731590Srgrimes * XXX: we really should have the interrupt cookie passed up from new-bus 741590Srgrimes * just be a int pin, and not map 1:1 to interrupt vector number but should 751590Srgrimes * use INTR_TYPE_FOO to set priority bands for device classes and do all the 761590Srgrimes * magic remapping of intpin to vector in here. For now we just cheat as on 771590Srgrimes * ia64 and map intpin X to vector NRSVIDT + X. Note that we assume that the 781590Srgrimes * first IO APIC has ISA interrupts on pins 1-15. Not sure how you are 791590Srgrimes * really supposed to figure out which IO APIC in a system with multiple IO 801590Srgrimes * APIC's actually has the ISA interrupts routed to it. As far as interrupt 8174874Smarkm * pin numbers, we use the ACPI System Interrupt number model where each 8241279Sjdp * IO APIC has a contiguous chunk of the System Interrupt address space. 8341279Sjdp */ 8474874Smarkm 8574874Smarkm/* 863702Spst * Direct the ExtINT pin on the first I/O APIC to a logical cluster of 871590Srgrimes * CPUs rather than a physical destination of just the BSP. 881590Srgrimes * 8957339Sshin * Note: This is disabled by default as test systems seem to croak with it 9057339Sshin * enabled. 9157339Sshin#define ENABLE_EXTINT_LOGICAL_DESTINATION 9257339Sshin */ 9357339Sshin 941590Srgrimesstruct ioapic_intsrc { 951590Srgrimes struct intsrc io_intsrc; 961590Srgrimes u_int io_intpin:8; 971590Srgrimes u_int io_vector:8; 9821528Sdavidn u_int io_activehi:1; 991590Srgrimes u_int io_edgetrigger:1; 1001590Srgrimes u_int io_masked:1; 1011590Srgrimes int io_dest:5; 10223985Sdavidn int io_bus:4; 1031590Srgrimes}; 1041590Srgrimes 10529922Smarkmstruct ioapic { 1062224Sguido struct pic io_pic; 1071590Srgrimes u_int io_id:8; /* logical ID */ 10874874Smarkm u_int io_apic_id:4; 10941279Sjdp u_int io_intbase:8; /* System Interrupt base */ 11072215Snectar u_int io_numintr:8; 11172215Snectar volatile ioapic_t *io_addr; /* XXX: should use bus_space */ 11274874Smarkm STAILQ_ENTRY(ioapic) io_next; 11374874Smarkm struct ioapic_intsrc io_pins[0]; 11474874Smarkm}; 11574874Smarkm 11674874Smarkmstatic u_int ioapic_read(volatile ioapic_t *apic, int reg); 11774874Smarkmstatic void ioapic_write(volatile ioapic_t *apic, int reg, u_int val); 11874874Smarkmstatic const char *ioapic_bus_string(int bus_type); 11974874Smarkmstatic void ioapic_print_vector(struct ioapic_intsrc *intpin); 12074874Smarkmstatic void ioapic_enable_source(struct intsrc *isrc); 12174874Smarkmstatic void ioapic_disable_source(struct intsrc *isrc, int eoi); 12274874Smarkmstatic void ioapic_eoi_source(struct intsrc *isrc); 12374874Smarkmstatic void ioapic_enable_intr(struct intsrc *isrc); 12474874Smarkmstatic int ioapic_vector(struct intsrc *isrc); 12541279Sjdpstatic int ioapic_source_pending(struct intsrc *isrc); 1261590Srgrimesstatic int ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig, 12727605Scharnier enum intr_polarity pol); 1281590Srgrimesstatic void ioapic_suspend(struct intsrc *isrc); 1291590Srgrimesstatic void ioapic_resume(struct intsrc *isrc); 13023985Sdavidnstatic void ioapic_program_destination(struct ioapic_intsrc *intpin); 13123985Sdavidnstatic void ioapic_program_intpin(struct ioapic_intsrc *intpin); 13276786Sobrienstatic void ioapic_setup_mixed_mode(struct ioapic_intsrc *intpin); 13376786Sobrien 1341590Srgrimesstatic STAILQ_HEAD(,ioapic) ioapic_list = STAILQ_HEAD_INITIALIZER(ioapic_list); 1351590Srgrimesstruct pic ioapic_template = { ioapic_enable_source, ioapic_disable_source, 1361590Srgrimes ioapic_eoi_source, ioapic_enable_intr, 1371590Srgrimes ioapic_vector, ioapic_source_pending, 1381590Srgrimes ioapic_suspend, ioapic_resume, 1391590Srgrimes ioapic_config_intr }; 1401590Srgrimes 14142272Seivindstatic int bsp_id, current_cluster, logical_clusters, next_ioapic_base; 14242272Seivindstatic u_int mixed_mode_enabled, next_id, program_logical_dest; 14342272Seivind#ifdef NO_MIXED_MODE 1441590Srgrimesstatic int mixed_mode_active = 0; 1451590Srgrimes#else 14676786Sobrienstatic int mixed_mode_active = 1; 14716423Sache#endif 1481590SrgrimesTUNABLE_INT("hw.apic.mixed_mode", &mixed_mode_active); 1491590Srgrimes 1501590Srgrimesstatic __inline void 1511590Srgrimes_ioapic_eoi_source(struct intsrc *isrc) 1521590Srgrimes{ 1531590Srgrimes lapic_eoi(); 1541590Srgrimes} 1551590Srgrimes 1561590Srgrimesstatic u_int 1571590Srgrimesioapic_read(volatile ioapic_t *apic, int reg) 1581590Srgrimes{ 15923985Sdavidn 1601590Srgrimes mtx_assert(&icu_lock, MA_OWNED); 1614878Sugen apic->ioregsel = reg; 16223985Sdavidn return (apic->iowin); 16335559Speter} 16446007Sache 16545431Sbrianstatic void 16642272Seivindioapic_write(volatile ioapic_t *apic, int reg, u_int val) 16742272Seivind{ 16823985Sdavidn 16921528Sdavidn mtx_assert(&icu_lock, MA_OWNED); 17074874Smarkm apic->ioregsel = reg; 17174874Smarkm apic->iowin = val; 17274874Smarkm} 17374874Smarkm 1741590Srgrimesstatic const char * 17542272Seivindioapic_bus_string(int bus_type) 17642272Seivind{ 17742272Seivind 17842272Seivind switch (bus_type) { 17942272Seivind case APIC_BUS_ISA: 18042272Seivind return ("ISA"); 18142272Seivind case APIC_BUS_EISA: 18242272Seivind return ("EISA"); 18342272Seivind case APIC_BUS_PCI: 1841590Srgrimes return ("PCI"); 1851590Srgrimes default: 1861590Srgrimes return ("unknown"); 1871590Srgrimes } 1881590Srgrimes} 1891590Srgrimes 1901590Srgrimesstatic void 1911590Srgrimesioapic_print_vector(struct ioapic_intsrc *intpin) 1921590Srgrimes{ 1931590Srgrimes 1941590Srgrimes switch (intpin->io_vector) { 1951590Srgrimes case VECTOR_DISABLED: 1963205Spst printf("disabled"); 19723985Sdavidn break; 1981590Srgrimes case VECTOR_EXTINT: 1991590Srgrimes printf("ExtINT"); 2001590Srgrimes break; 20135557Speter case VECTOR_NMI: 20246007Sache printf("NMI"); 20324360Simp break; 2041590Srgrimes case VECTOR_SMI: 2051590Srgrimes printf("SMI"); 2061590Srgrimes break; 2071590Srgrimes default: 2081590Srgrimes printf("%s IRQ %u", ioapic_bus_string(intpin->io_bus), 2091590Srgrimes intpin->io_vector); 2101590Srgrimes } 2111590Srgrimes} 2123205Spst 21336559Samuraistatic void 21445431Sbrianioapic_enable_source(struct intsrc *isrc) 21536559Samurai{ 21616423Sache struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc; 21757339Sshin struct ioapic *io = (struct ioapic *)isrc->is_pic; 21857339Sshin uint32_t flags; 21957339Sshin 22057339Sshin mtx_lock_spin(&icu_lock); 22157339Sshin if (intpin->io_masked) { 22257339Sshin flags = ioapic_read(io->io_addr, 22357339Sshin IOAPIC_REDTBL_LO(intpin->io_intpin)); 22457339Sshin flags &= ~(IOART_INTMASK); 22557339Sshin ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin), 22616423Sache flags); 22757339Sshin intpin->io_masked = 0; 22857339Sshin } 22957339Sshin mtx_unlock_spin(&icu_lock); 23057339Sshin} 23157339Sshin 23257339Sshinstatic void 23357339Sshinioapic_disable_source(struct intsrc *isrc, int eoi) 23416423Sache{ 23516423Sache struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc; 23657339Sshin struct ioapic *io = (struct ioapic *)isrc->is_pic; 23757339Sshin uint32_t flags; 23816423Sache 2391590Srgrimes mtx_lock_spin(&icu_lock); 2401590Srgrimes if (!intpin->io_masked && !intpin->io_edgetrigger) { 2411590Srgrimes flags = ioapic_read(io->io_addr, 2421590Srgrimes IOAPIC_REDTBL_LO(intpin->io_intpin)); 2431590Srgrimes flags |= IOART_INTMSET; 2441590Srgrimes ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin), 2451590Srgrimes flags); 2461590Srgrimes intpin->io_masked = 1; 2471590Srgrimes } 24827605Scharnier 2491590Srgrimes if (eoi == PIC_EOI) 2501590Srgrimes _ioapic_eoi_source(isrc); 2511590Srgrimes 2521590Srgrimes mtx_unlock_spin(&icu_lock); 2531590Srgrimes} 2541590Srgrimes 2551590Srgrimesstatic void 2561590Srgrimesioapic_eoi_source(struct intsrc *isrc) 2571590Srgrimes{ 2581590Srgrimes 2591590Srgrimes _ioapic_eoi_source(isrc); 2601590Srgrimes} 2611590Srgrimes 2621590Srgrimes/* 2631590Srgrimes * Completely program an intpin based on the data in its interrupt source 2641590Srgrimes * structure. 2651590Srgrimes */ 2661590Srgrimesstatic void 26723985Sdavidnioapic_program_intpin(struct ioapic_intsrc *intpin) 2681590Srgrimes{ 2691590Srgrimes struct ioapic *io = (struct ioapic *)intpin->io_intsrc.is_pic; 2701590Srgrimes uint32_t low, high, value; 2711590Srgrimes 27223985Sdavidn /* 27323985Sdavidn * For pins routed via mixed mode or disabled, just ensure that 27423985Sdavidn * they are masked. 27523985Sdavidn */ 27676786Sobrien if (intpin->io_dest == DEST_EXTINT || 27776786Sobrien intpin->io_vector == VECTOR_DISABLED) { 27876786Sobrien low = ioapic_read(io->io_addr, 27923985Sdavidn IOAPIC_REDTBL_LO(intpin->io_intpin)); 28023985Sdavidn if ((low & IOART_INTMASK) == IOART_INTMCLR) 28123985Sdavidn ioapic_write(io->io_addr, 28223985Sdavidn IOAPIC_REDTBL_LO(intpin->io_intpin), 28321528Sdavidn low | IOART_INTMSET); 2841590Srgrimes return; 2851590Srgrimes } 2861590Srgrimes 2871590Srgrimes /* Set the destination. */ 2881590Srgrimes if (intpin->io_dest == DEST_NONE) { 2891590Srgrimes low = IOART_DESTPHY; 29021528Sdavidn high = bsp_id << APIC_ID_SHIFT; 29121528Sdavidn } else { 2921590Srgrimes low = IOART_DESTLOG; 2931590Srgrimes high = (intpin->io_dest << APIC_ID_CLUSTER_SHIFT | 2941590Srgrimes APIC_ID_CLUSTER_ID) << APIC_ID_SHIFT; 2951590Srgrimes } 2961590Srgrimes 2971590Srgrimes /* Program the rest of the low word. */ 2981590Srgrimes if (intpin->io_edgetrigger) 2991590Srgrimes low |= IOART_TRGREDG; 3001590Srgrimes else 3011590Srgrimes low |= IOART_TRGRLVL; 3021590Srgrimes if (intpin->io_activehi) 3031590Srgrimes low |= IOART_INTAHI; 30427605Scharnier else 30527605Scharnier low |= IOART_INTALO; 3061590Srgrimes if (intpin->io_masked) 30741279Sjdp low |= IOART_INTMSET; 30823985Sdavidn switch (intpin->io_vector) { 30923985Sdavidn case VECTOR_EXTINT: 3101590Srgrimes KASSERT(intpin->io_edgetrigger, 3111590Srgrimes ("EXTINT not edge triggered")); 3121590Srgrimes low |= IOART_DELEXINT; 3131590Srgrimes break; 3141590Srgrimes case VECTOR_NMI: 31521528Sdavidn KASSERT(intpin->io_edgetrigger, 3163205Spst ("NMI not edge triggered")); 3173205Spst low |= IOART_DELNMI; 3183205Spst break; 31923985Sdavidn case VECTOR_SMI: 32023985Sdavidn KASSERT(intpin->io_edgetrigger, 3213205Spst ("SMI not edge triggered")); 3223205Spst low |= IOART_DELSMI; 3233205Spst break; 32424321Sdavidn default: 32524251Sdavidn low |= IOART_DELLOPRI | apic_irq_to_idt(intpin->io_vector); 32624251Sdavidn } 32724251Sdavidn 32824251Sdavidn /* Write the values to the APIC. */ 3293205Spst mtx_lock_spin(&icu_lock); 3303205Spst ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin), low); 3313205Spst value = ioapic_read(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin)); 3321590Srgrimes value &= ~IOART_DEST; 3331590Srgrimes value |= high; 3341590Srgrimes ioapic_write(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin), value); 3351590Srgrimes mtx_unlock_spin(&icu_lock); 33674874Smarkm} 33741279Sjdp 33841279Sjdp/* 33941279Sjdp * Program an individual intpin's logical destination. 34041279Sjdp */ 34141279Sjdpstatic void 34241279Sjdpioapic_program_destination(struct ioapic_intsrc *intpin) 34374874Smarkm{ 34441279Sjdp struct ioapic *io = (struct ioapic *)intpin->io_intsrc.is_pic; 34523985Sdavidn 34641279Sjdp KASSERT(intpin->io_dest != DEST_NONE, 34723985Sdavidn ("intpin not assigned to a cluster")); 34874874Smarkm KASSERT(intpin->io_dest != DEST_EXTINT, 34941279Sjdp ("intpin routed via ExtINT")); 35041279Sjdp if (bootverbose) { 35141279Sjdp printf("ioapic%u: routing intpin %u (", io->io_id, 35241279Sjdp intpin->io_intpin); 35341279Sjdp ioapic_print_vector(intpin); 35441279Sjdp printf(") to cluster %u\n", intpin->io_dest); 35541279Sjdp } 35674874Smarkm ioapic_program_intpin(intpin); 3571590Srgrimes} 3583205Spst 3591590Srgrimesstatic void 3601590Srgrimesioapic_assign_cluster(struct ioapic_intsrc *intpin) 3611590Srgrimes{ 3621590Srgrimes 36324222Sdavidn /* 36424222Sdavidn * Assign this intpin to a logical APIC cluster in a 36524222Sdavidn * round-robin fashion. We don't actually use the logical 36624222Sdavidn * destination for this intpin until after all the CPU's 36724222Sdavidn * have been started so that we don't end up with interrupts 3681590Srgrimes * that don't go anywhere. Another alternative might be to 3691590Srgrimes * start up the CPU's earlier so that they can handle interrupts 3701590Srgrimes * sooner. 3711590Srgrimes */ 37223985Sdavidn intpin->io_dest = current_cluster; 37323985Sdavidn current_cluster++; 37423985Sdavidn if (current_cluster >= logical_clusters) 37523985Sdavidn current_cluster = 0; 37623985Sdavidn if (program_logical_dest) 37723985Sdavidn ioapic_program_destination(intpin); 37823985Sdavidn} 3791590Srgrimes 3801590Srgrimesstatic void 3811590Srgrimesioapic_enable_intr(struct intsrc *isrc) 38238374Sjkoshy{ 3831590Srgrimes struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc; 3841590Srgrimes 3851590Srgrimes KASSERT(intpin->io_dest != DEST_EXTINT, 3861590Srgrimes ("ExtINT pin trying to use ioapic enable_intr method")); 3871590Srgrimes if (intpin->io_dest == DEST_NONE) { 3881590Srgrimes ioapic_assign_cluster(intpin); 3891590Srgrimes lapic_enable_intr(intpin->io_vector); 3901590Srgrimes } 39141279Sjdp} 39241279Sjdp 39341279Sjdpstatic int 39441279Sjdpioapic_vector(struct intsrc *isrc) 39541279Sjdp{ 3961590Srgrimes struct ioapic_intsrc *pin; 3971590Srgrimes 39821528Sdavidn pin = (struct ioapic_intsrc *)isrc; 3991590Srgrimes return (pin->io_vector); 40021528Sdavidn} 40146007Sache 40246007Sachestatic int 40335557Speterioapic_source_pending(struct intsrc *isrc) 40421528Sdavidn{ 40526021Spst struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc; 40623985Sdavidn 40724485Sdavidn return (lapic_intr_pending(intpin->io_vector)); 40823985Sdavidn} 40923985Sdavidn 41023985Sdavidnstatic int 41157546Sacheioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig, 4121590Srgrimes enum intr_polarity pol) 41335557Speter{ 41446007Sache struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc; 41521528Sdavidn struct ioapic *io = (struct ioapic *)isrc->is_pic; 41621528Sdavidn int changed; 4171590Srgrimes 4181590Srgrimes KASSERT(!(trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM), 4191590Srgrimes ("%s: Conforming trigger or polarity\n", __func__)); 4202532Sjkh 42130564Sjoerg /* 42221528Sdavidn * EISA interrupts always use active high polarity, so don't allow 42376710Seric * them to be set to active low. 42476710Seric * 42576710Seric * XXX: Should we write to the ELCR if the trigger mode changes for 42676710Seric * an EISA IRQ or an ISA IRQ with the ELCR present? 42776710Seric */ 42876710Seric if (intpin->io_bus == APIC_BUS_EISA) 42976710Seric pol = INTR_POLARITY_HIGH; 43076710Seric changed = 0; 43176710Seric if (intpin->io_edgetrigger != (trig == INTR_TRIGGER_EDGE)) { 43276710Seric if (bootverbose) 43376710Seric printf("ioapic%u: Changing trigger for pin %u to %s\n", 43476710Seric io->io_id, intpin->io_intpin, 43576710Seric trig == INTR_TRIGGER_EDGE ? "edge" : "level"); 43623985Sdavidn intpin->io_edgetrigger = (trig == INTR_TRIGGER_EDGE); 43723985Sdavidn changed++; 43823985Sdavidn } 4394878Sugen if (intpin->io_activehi != (pol == INTR_POLARITY_HIGH)) { 44021528Sdavidn if (bootverbose) 4411590Srgrimes printf("ioapic%u: Changing polarity for pin %u to %s\n", 4421590Srgrimes io->io_id, intpin->io_intpin, 4434878Sugen pol == INTR_POLARITY_HIGH ? "high" : "low"); 44423985Sdavidn intpin->io_activehi = (pol == INTR_POLARITY_HIGH); 44523985Sdavidn changed++; 44623985Sdavidn } 44723985Sdavidn if (changed) 44823985Sdavidn ioapic_program_intpin(intpin); 44923985Sdavidn return (0); 45023985Sdavidn} 45123985Sdavidn 45221528Sdavidnstatic void 45321528Sdavidnioapic_suspend(struct intsrc *isrc) 45457339Sshin{ 45557339Sshin 45621528Sdavidn TODO; 45757339Sshin} 45857339Sshin 45957339Sshinstatic void 46057339Sshinioapic_resume(struct intsrc *isrc) 46157339Sshin{ 46257339Sshin 46357339Sshin ioapic_program_intpin((struct ioapic_intsrc *)isrc); 46457339Sshin} 46557339Sshin 46657339Sshin/* 46757339Sshin * APIC enumerators call this function to indicate that the 8259A AT PICs 46857339Sshin * are available and that mixed mode can be used. 46921528Sdavidn */ 47057339Sshinvoid 47157339Sshinioapic_enable_mixed_mode(void) 47223985Sdavidn{ 47323985Sdavidn 47421528Sdavidn mixed_mode_enabled = 1; 47521528Sdavidn} 47623985Sdavidn 47723985Sdavidn/* 47821528Sdavidn * Allocate and return a logical cluster ID. Note that the first time 47923985Sdavidn * this is called, it returns cluster 0. ioapic_enable_intr() treats 48023985Sdavidn * the two cases of logical_clusters == 0 and logical_clusters == 1 the 48121528Sdavidn * same: one cluster of ID 0 exists. The logical_clusters == 0 case is 48223985Sdavidn * for UP kernels, which should never call this function. 48323985Sdavidn */ 48423985Sdavidnint 48523985Sdavidnioapic_next_logical_cluster(void) 48623985Sdavidn{ 48723985Sdavidn 48823985Sdavidn if (logical_clusters >= APIC_MAX_CLUSTER) 48923985Sdavidn panic("WARNING: Local APIC cluster IDs exhausted!"); 49023985Sdavidn return (logical_clusters++); 49121528Sdavidn} 49221528Sdavidn 49323985Sdavidn/* 49423985Sdavidn * Create a plain I/O APIC object. 49521528Sdavidn */ 49621528Sdavidnvoid * 4971590Srgrimesioapic_create(uintptr_t addr, int32_t apic_id, int intbase) 4981590Srgrimes{ 4991590Srgrimes struct ioapic *io; 5001590Srgrimes struct ioapic_intsrc *intpin; 5011590Srgrimes volatile ioapic_t *apic; 5021590Srgrimes u_int numintr, i; 5031590Srgrimes uint32_t value; 5041590Srgrimes 5051590Srgrimes /* Map the register window so we can access the device. */ 5061590Srgrimes apic = (ioapic_t *)pmap_mapdev(addr, IOAPIC_MEM_REGION); 5071590Srgrimes mtx_lock_spin(&icu_lock); 5082224Sguido value = ioapic_read(apic, IOAPIC_VER); 5092224Sguido mtx_unlock_spin(&icu_lock); 5102224Sguido 5112224Sguido /* If it's version register doesn't seem to work, punt. */ 5122224Sguido if (value == 0xffffff) { 5132224Sguido pmap_unmapdev((vm_offset_t)apic, IOAPIC_MEM_REGION); 5142224Sguido return (NULL); 51550124Simp } 51650124Simp 51750124Simp /* Determine the number of vectors and set the APIC ID. */ 51850124Simp numintr = ((value & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1; 51950124Simp io = malloc(sizeof(struct ioapic) + 52050124Simp numintr * sizeof(struct ioapic_intsrc), M_IOAPIC, M_WAITOK); 52159621Ssheldonh io->io_pic = ioapic_template; 52250124Simp mtx_lock_spin(&icu_lock); 52350124Simp io->io_id = next_id++; 52450124Simp io->io_apic_id = ioapic_read(apic, IOAPIC_ID) >> APIC_ID_SHIFT; 52550124Simp if (apic_id != -1 && io->io_apic_id != apic_id) { 5261590Srgrimes ioapic_write(apic, IOAPIC_ID, apic_id << APIC_ID_SHIFT); 52750124Simp mtx_unlock_spin(&icu_lock); 52823985Sdavidn io->io_apic_id = apic_id; 52923985Sdavidn printf("ioapic%u: Changing APIC ID to %d\n", io->io_id, 53023985Sdavidn apic_id); 53124222Sdavidn } else 53224222Sdavidn mtx_unlock_spin(&icu_lock); 5331590Srgrimes if (intbase == -1) { 53423985Sdavidn intbase = next_ioapic_base; 53523985Sdavidn printf("ioapic%u: Assuming intbase of %d\n", io->io_id, 53623985Sdavidn intbase); 53723985Sdavidn } else if (intbase != next_ioapic_base) 53823985Sdavidn printf("ioapic%u: WARNING: intbase %d != expected base %d\n", 53924894Sdavidn io->io_id, intbase, next_ioapic_base); 5401590Srgrimes io->io_intbase = intbase; 5411590Srgrimes next_ioapic_base = intbase + numintr; 5423205Spst io->io_numintr = numintr; 5433205Spst io->io_addr = apic; 5443205Spst 5453205Spst /* 5463205Spst * Initialize pins. Start off with interrupts disabled. Default 54723985Sdavidn * to active-hi and edge-triggered for ISA interrupts and active-lo 54823985Sdavidn * and level-triggered for all others. 54923985Sdavidn */ 55023985Sdavidn bzero(io->io_pins, sizeof(struct ioapic_intsrc) * numintr); 55123985Sdavidn mtx_lock_spin(&icu_lock); 55223985Sdavidn for (i = 0, intpin = io->io_pins; i < numintr; i++, intpin++) { 55321528Sdavidn intpin->io_intsrc.is_pic = (struct pic *)io; 55421528Sdavidn intpin->io_intpin = i; 55523985Sdavidn intpin->io_vector = intbase + i; 55623985Sdavidn 55723985Sdavidn /* 55823985Sdavidn * Assume that pin 0 on the first I/O APIC is an ExtINT pin 55923985Sdavidn * if mixed mode is enabled and an ISA interrupt if not. 56023985Sdavidn * Assume that pins 1-15 are ISA interrupts and that all 56123985Sdavidn * other pins are PCI interrupts. 56223985Sdavidn */ 56323985Sdavidn if (intpin->io_vector == 0 && mixed_mode_enabled) 56423985Sdavidn ioapic_set_extint(io, i); 56523985Sdavidn else if (intpin->io_vector < IOAPIC_ISA_INTS) { 56623985Sdavidn intpin->io_bus = APIC_BUS_ISA; 56723985Sdavidn intpin->io_activehi = 1; 56823985Sdavidn intpin->io_edgetrigger = 1; 56923985Sdavidn intpin->io_masked = 1; 57023985Sdavidn } else { 57123985Sdavidn intpin->io_bus = APIC_BUS_PCI; 57223985Sdavidn intpin->io_activehi = 0; 57321528Sdavidn intpin->io_edgetrigger = 0; 57421528Sdavidn intpin->io_masked = 1; 57521528Sdavidn } 57674874Smarkm 57723985Sdavidn /* 57872215Snectar * Route interrupts to the BSP by default using physical 57972215Snectar * addressing. Vectored interrupts get readdressed using 58072215Snectar * logical IDs to CPU clusters when they are enabled. 58174874Smarkm */ 58274874Smarkm intpin->io_dest = DEST_NONE; 58374874Smarkm if (bootverbose && intpin->io_vector != VECTOR_DISABLED) { 58474874Smarkm printf("ioapic%u: intpin %d -> ", io->io_id, i); 58574874Smarkm ioapic_print_vector(intpin); 58674874Smarkm printf(" (%s, %s)\n", intpin->io_edgetrigger ? 58772215Snectar "edge" : "level", intpin->io_activehi ? "high" : 58872215Snectar "low"); 58974874Smarkm } 59074874Smarkm value = ioapic_read(apic, IOAPIC_REDTBL_LO(i)); 59174874Smarkm ioapic_write(apic, IOAPIC_REDTBL_LO(i), value | IOART_INTMSET); 59274874Smarkm } 59374874Smarkm mtx_unlock_spin(&icu_lock); 59474874Smarkm 59574874Smarkm return (io); 59674874Smarkm} 59774874Smarkm 59874874Smarkmint 59974874Smarkmioapic_get_vector(void *cookie, u_int pin) 60074874Smarkm{ 60174874Smarkm struct ioapic *io; 60274874Smarkm 60374874Smarkm io = (struct ioapic *)cookie; 60474874Smarkm if (pin >= io->io_numintr) 60574874Smarkm return (-1); 60674874Smarkm return (io->io_pins[pin].io_vector); 60774874Smarkm} 60874874Smarkm 60974874Smarkmint 61074874Smarkmioapic_disable_pin(void *cookie, u_int pin) 61174874Smarkm{ 61274874Smarkm struct ioapic *io; 61374874Smarkm 61474874Smarkm io = (struct ioapic *)cookie; 61574874Smarkm if (pin >= io->io_numintr) 61674874Smarkm return (EINVAL); 61774874Smarkm if (io->io_pins[pin].io_vector == VECTOR_DISABLED) 61874874Smarkm return (EINVAL); 61974874Smarkm io->io_pins[pin].io_vector = VECTOR_DISABLED; 62074874Smarkm if (bootverbose) 62174874Smarkm printf("ioapic%u: intpin %d disabled\n", io->io_id, pin); 62274874Smarkm return (0); 62374874Smarkm} 62474874Smarkm 62574874Smarkmint 62623985Sdavidnioapic_remap_vector(void *cookie, u_int pin, int vector) 62721528Sdavidn{ 62821528Sdavidn struct ioapic *io; 62941279Sjdp 63041279Sjdp io = (struct ioapic *)cookie; 63141279Sjdp if (pin >= io->io_numintr || vector < 0) 63241279Sjdp return (EINVAL); 63341279Sjdp if (io->io_pins[pin].io_vector >= NUM_IO_INTS) 63474874Smarkm return (EINVAL); 63521528Sdavidn io->io_pins[pin].io_vector = vector; 63621528Sdavidn if (bootverbose) 6373205Spst printf("ioapic%u: Routing IRQ %d -> intpin %d\n", io->io_id, 63821528Sdavidn vector, pin); 63923148Sache return (0); 64021528Sdavidn} 64123985Sdavidn 64221528Sdavidnint 64321528Sdavidnioapic_set_bus(void *cookie, u_int pin, int bus_type) 64423985Sdavidn{ 64521528Sdavidn struct ioapic *io; 64641279Sjdp 64741279Sjdp if (bus_type < 0 || bus_type > APIC_BUS_MAX) 64821528Sdavidn return (EINVAL); 64921528Sdavidn io = (struct ioapic *)cookie; 6501590Srgrimes if (pin >= io->io_numintr) 65123985Sdavidn return (EINVAL); 65223985Sdavidn if (io->io_pins[pin].io_vector >= NUM_IO_INTS) 65323985Sdavidn return (EINVAL); 65421528Sdavidn io->io_pins[pin].io_bus = bus_type; 65521528Sdavidn if (bootverbose) 65621528Sdavidn printf("ioapic%u: intpin %d bus %s\n", io->io_id, pin, 65723985Sdavidn ioapic_bus_string(bus_type)); 65823985Sdavidn return (0); 65923985Sdavidn} 66023985Sdavidn 66123985Sdavidnint 66223985Sdavidnioapic_set_nmi(void *cookie, u_int pin) 66323985Sdavidn{ 66421528Sdavidn struct ioapic *io; 66521528Sdavidn 66621528Sdavidn io = (struct ioapic *)cookie; 66721528Sdavidn if (pin >= io->io_numintr) 66823985Sdavidn return (EINVAL); 66921528Sdavidn if (io->io_pins[pin].io_vector == VECTOR_NMI) 67021528Sdavidn return (0); 67121528Sdavidn if (io->io_pins[pin].io_vector >= NUM_IO_INTS) 67221528Sdavidn return (EINVAL); 67321528Sdavidn io->io_pins[pin].io_bus = APIC_BUS_UNKNOWN; 67423985Sdavidn io->io_pins[pin].io_vector = VECTOR_NMI; 67523985Sdavidn io->io_pins[pin].io_masked = 0; 6761590Srgrimes io->io_pins[pin].io_edgetrigger = 1; 67723985Sdavidn io->io_pins[pin].io_activehi = 1; 67823985Sdavidn if (bootverbose) 6791590Srgrimes printf("ioapic%u: Routing NMI -> intpin %d\n", 6801590Srgrimes io->io_id, pin); 68121528Sdavidn return (0); 6823205Spst} 6831590Srgrimes 6841590Srgrimesint 6851590Srgrimesioapic_set_smi(void *cookie, u_int pin) 6861590Srgrimes{ 6871590Srgrimes struct ioapic *io; 6884878Sugen 68921528Sdavidn io = (struct ioapic *)cookie; 6904878Sugen if (pin >= io->io_numintr) 6914878Sugen return (EINVAL); 6924878Sugen if (io->io_pins[pin].io_vector == VECTOR_SMI) 69323985Sdavidn return (0); 69423985Sdavidn if (io->io_pins[pin].io_vector >= NUM_IO_INTS) 69523985Sdavidn return (EINVAL); 69623985Sdavidn io->io_pins[pin].io_bus = APIC_BUS_UNKNOWN; 69723985Sdavidn io->io_pins[pin].io_vector = VECTOR_SMI; 69823985Sdavidn io->io_pins[pin].io_masked = 0; 69921528Sdavidn io->io_pins[pin].io_edgetrigger = 1; 70021528Sdavidn io->io_pins[pin].io_activehi = 1; 7011590Srgrimes if (bootverbose) 7021590Srgrimes printf("ioapic%u: Routing SMI -> intpin %d\n", 70341279Sjdp io->io_id, pin); 70441279Sjdp return (0); 70541279Sjdp} 70641279Sjdp 70741279Sjdpint 70841279Sjdpioapic_set_extint(void *cookie, u_int pin) 70941279Sjdp{ 71041279Sjdp struct ioapic *io; 71141279Sjdp 71241279Sjdp io = (struct ioapic *)cookie; 71341279Sjdp if (pin >= io->io_numintr) 71476786Sobrien return (EINVAL); 71541279Sjdp if (io->io_pins[pin].io_vector == VECTOR_EXTINT) 71641279Sjdp return (0); 71741279Sjdp if (io->io_pins[pin].io_vector >= NUM_IO_INTS) 71841279Sjdp return (EINVAL); 71941279Sjdp io->io_pins[pin].io_bus = APIC_BUS_UNKNOWN; 72041279Sjdp io->io_pins[pin].io_vector = VECTOR_EXTINT; 72141279Sjdp 72241279Sjdp /* Enable this pin if mixed mode is available and active. */ 72341279Sjdp if (mixed_mode_enabled && mixed_mode_active) 72441279Sjdp io->io_pins[pin].io_masked = 0; 72541279Sjdp else 72641279Sjdp io->io_pins[pin].io_masked = 1; 72741279Sjdp io->io_pins[pin].io_edgetrigger = 1; 72841279Sjdp io->io_pins[pin].io_activehi = 1; 72974874Smarkm if (bootverbose) 73041279Sjdp printf("ioapic%u: Routing external 8259A's -> intpin %d\n", 73141279Sjdp io->io_id, pin); 73241279Sjdp return (0); 73341279Sjdp} 73441279Sjdp 73541279Sjdpint 73641279Sjdpioapic_set_polarity(void *cookie, u_int pin, enum intr_polarity pol) 73741279Sjdp{ 73841279Sjdp struct ioapic *io; 73941279Sjdp 74041279Sjdp io = (struct ioapic *)cookie; 74141279Sjdp if (pin >= io->io_numintr || pol == INTR_POLARITY_CONFORM) 74241279Sjdp return (EINVAL); 74341279Sjdp if (io->io_pins[pin].io_vector >= NUM_IO_INTS) 74441279Sjdp return (EINVAL); 74541279Sjdp io->io_pins[pin].io_activehi = (pol == INTR_POLARITY_HIGH); 74641279Sjdp if (bootverbose) 74741279Sjdp printf("ioapic%u: intpin %d polarity: %s\n", io->io_id, pin, 74841279Sjdp pol == INTR_POLARITY_HIGH ? "high" : "low"); 74941279Sjdp return (0); 75041279Sjdp} 75141279Sjdp 75241279Sjdpint 75341279Sjdpioapic_set_triggermode(void *cookie, u_int pin, enum intr_trigger trigger) 75441279Sjdp{ 75541279Sjdp struct ioapic *io; 75641279Sjdp 75741279Sjdp io = (struct ioapic *)cookie; 75841279Sjdp if (pin >= io->io_numintr || trigger == INTR_TRIGGER_CONFORM) 75941279Sjdp return (EINVAL); 76041279Sjdp if (io->io_pins[pin].io_vector >= NUM_IO_INTS) 76141279Sjdp return (EINVAL); 76241279Sjdp io->io_pins[pin].io_edgetrigger = (trigger == INTR_TRIGGER_EDGE); 76341279Sjdp if (bootverbose) 76441279Sjdp printf("ioapic%u: intpin %d trigger: %s\n", io->io_id, pin, 76541279Sjdp trigger == INTR_TRIGGER_EDGE ? "edge" : "level"); 76641279Sjdp return (0); 76741279Sjdp} 76841279Sjdp 76941279Sjdp/* 77041279Sjdp * Register a complete I/O APIC object with the interrupt subsystem. 77141279Sjdp */ 77241279Sjdpvoid 77341279Sjdpioapic_register(void *cookie) 77441279Sjdp{ 77541279Sjdp struct ioapic_intsrc *pin; 77641279Sjdp struct ioapic *io; 77741279Sjdp volatile ioapic_t *apic; 77841279Sjdp uint32_t flags; 77941279Sjdp int i; 78041279Sjdp 78141279Sjdp io = (struct ioapic *)cookie; 78241279Sjdp apic = io->io_addr; 78341279Sjdp mtx_lock_spin(&icu_lock); 78441279Sjdp flags = ioapic_read(apic, IOAPIC_VER) & IOART_VER_VERSION; 78541279Sjdp STAILQ_INSERT_TAIL(&ioapic_list, io, io_next); 78641279Sjdp mtx_unlock_spin(&icu_lock); 78741279Sjdp printf("ioapic%u <Version %u.%u> irqs %u-%u on motherboard\n", 78841279Sjdp io->io_id, flags >> 4, flags & 0xf, io->io_intbase, 78941279Sjdp io->io_intbase + io->io_numintr - 1); 79041279Sjdp bsp_id = PCPU_GET(apic_id); 79141279Sjdp for (i = 0, pin = io->io_pins; i < io->io_numintr; i++, pin++) { 79241279Sjdp /* 79341279Sjdp * Finish initializing the pins by programming the vectors 79441279Sjdp * and delivery mode. 79541279Sjdp */ 79641279Sjdp if (pin->io_vector == VECTOR_DISABLED) 79741279Sjdp continue; 79841279Sjdp ioapic_program_intpin(pin); 79941279Sjdp if (pin->io_vector >= NUM_IO_INTS) 80074874Smarkm continue; 80141279Sjdp /* 80241279Sjdp * Route IRQ0 via the 8259A using mixed mode if mixed mode 80341279Sjdp * is available and turned on. 80474874Smarkm */ 80574874Smarkm if (pin->io_vector == 0 && mixed_mode_active && 80674874Smarkm mixed_mode_enabled) 80774874Smarkm ioapic_setup_mixed_mode(pin); 80874874Smarkm else 80974874Smarkm intr_register_source(&pin->io_intsrc); 81074874Smarkm } 81174874Smarkm} 81274874Smarkm 81374874Smarkm/* 81474874Smarkm * Program all the intpins to use logical destinations once the AP's 81574874Smarkm * have been launched. 81641279Sjdp */ 81774874Smarkmstatic void 81874874Smarkmioapic_set_logical_destinations(void *arg __unused) 81974874Smarkm{ 82074874Smarkm struct ioapic *io; 82174874Smarkm int i; 82274874Smarkm 82374874Smarkm program_logical_dest = 1; 82441279Sjdp STAILQ_FOREACH(io, &ioapic_list, io_next) 82541279Sjdp for (i = 0; i < io->io_numintr; i++) 82672215Snectar if (io->io_pins[i].io_dest != DEST_NONE && 82772215Snectar io->io_pins[i].io_dest != DEST_EXTINT) 82872215Snectar ioapic_program_destination(&io->io_pins[i]); 82972215Snectar} 83072215SnectarSYSINIT(ioapic_destinations, SI_SUB_SMP, SI_ORDER_SECOND, 83172215Snectar ioapic_set_logical_destinations, NULL) 83272215Snectar 83372215Snectar/* 83472215Snectar * Support for mixed-mode interrupt sources. These sources route an ISA 83572215Snectar * IRQ through the 8259A's via the ExtINT on pin 0 of the I/O APIC that 83672215Snectar * routes the ISA interrupts. We just ignore the intpins that use this 83772215Snectar * mode and allow the atpic driver to register its interrupt source for 83872215Snectar * that IRQ instead. 83972215Snectar */ 84072215Snectar 84172215Snectarstatic void 84272215Snectarioapic_setup_mixed_mode(struct ioapic_intsrc *intpin) 84372215Snectar{ 84472215Snectar struct ioapic_intsrc *extint; 84572215Snectar struct ioapic *io; 84672215Snectar 84772215Snectar /* 84872215Snectar * Mark the associated I/O APIC intpin as being delivered via 84972215Snectar * ExtINT and enable the ExtINT pin on the I/O APIC if needed. 85072215Snectar */ 85172215Snectar intpin->io_dest = DEST_EXTINT; 85272215Snectar io = (struct ioapic *)intpin->io_intsrc.is_pic; 85372215Snectar extint = &io->io_pins[0]; 85472215Snectar if (extint->io_vector != VECTOR_EXTINT) 85572215Snectar panic("Can't find ExtINT pin to route through!"); 85672215Snectar#ifdef ENABLE_EXTINT_LOGICAL_DESTINATION 85772215Snectar if (extint->io_dest == DEST_NONE) 85872215Snectar ioapic_assign_cluster(extint); 85972215Snectar#endif 86072215Snectar} 86172215Snectar