| /* Copyright (c) 2009, 2014 The Regents of the University of California | 
 |  * Barret Rhoden <brho@cs.berkeley.edu> | 
 |  * See LICENSE for details. | 
 |  * | 
 |  * PIC: 8259 interrupt controller */ | 
 |  | 
 | #include <arch/pic.h> | 
 | #include <arch/x86.h> | 
 | #include <atomic.h> | 
 | #include <assert.h> | 
 | #include <stdio.h> | 
 |  | 
 | spinlock_t piclock = SPINLOCK_INITIALIZER_IRQSAVE; | 
 |  | 
 | /* Remaps the Programmable Interrupt Controller to use IRQs 32-47 | 
 |  * http://wiki.osdev.org/PIC | 
 |  * Check osdev for a more thorough explanation/implementation. | 
 |  * http://bochs.sourceforge.net/techspec/PORTS.LST  */ | 
 | void pic_remap(void) | 
 | { | 
 | 	spin_lock_irqsave(&piclock); | 
 | 	/* start initialization (ICW1) */ | 
 | 	outb(PIC1_CMD, 0x11); | 
 | 	outb(PIC2_CMD, 0x11); | 
 | 	/* set new offsets (ICW2) */ | 
 | 	outb(PIC1_DATA, PIC1_OFFSET); | 
 | 	outb(PIC2_DATA, PIC2_OFFSET); | 
 | 	/* set up cascading (ICW3) */ | 
 | 	outb(PIC1_DATA, 0x04); | 
 | 	outb(PIC2_DATA, 0x02); | 
 | 	/* other stuff (put in 8086/88 mode, or whatever) (ICW4) */ | 
 | 	outb(PIC1_DATA, 0x01); | 
 | 	outb(PIC2_DATA, 0x01); | 
 | 	/* Init done, further data R/W access the interrupt mask */ | 
 | 	/* set masks, defaulting to all masked for now */ | 
 | 	outb(PIC1_DATA, 0xff); | 
 | 	outb(PIC2_DATA, 0xff); | 
 | 	spin_unlock_irqsave(&piclock); | 
 | } | 
 |  | 
 | void pic_mask_irq(struct irq_handler *unused, int trap_nr) | 
 | { | 
 | 	int irq = trap_nr - PIC1_OFFSET; | 
 |  | 
 | 	spin_lock_irqsave(&piclock); | 
 | 	if (irq > 7) | 
 | 		outb(PIC2_DATA, inb(PIC2_DATA) | (1 << (irq - 8))); | 
 | 	else | 
 | 		outb(PIC1_DATA, inb(PIC1_DATA) | (1 << irq)); | 
 | 	spin_unlock_irqsave(&piclock); | 
 | } | 
 |  | 
 | void pic_unmask_irq(struct irq_handler *unused, int trap_nr) | 
 | { | 
 | 	int irq = trap_nr - PIC1_OFFSET; | 
 | 	printd("PIC unmask for TRAP %d, IRQ %d\n", trap_nr, irq); | 
 | 	spin_lock_irqsave(&piclock); | 
 | 	if (irq > 7) { | 
 | 		outb(PIC2_DATA, inb(PIC2_DATA) & ~(1 << (irq - 8))); | 
 | 		// make sure irq2 is unmasked | 
 | 		outb(PIC1_DATA, inb(PIC1_DATA) & 0xfb); | 
 | 	} else | 
 | 		outb(PIC1_DATA, inb(PIC1_DATA) & ~(1 << irq)); | 
 | 	spin_unlock_irqsave(&piclock); | 
 | } | 
 |  | 
 | void pic_mask_all(void) | 
 | { | 
 | 	for (int i = 0 + PIC1_OFFSET; i < 16 + PIC1_OFFSET; i++) | 
 | 		pic_mask_irq(0, i); | 
 | } | 
 |  | 
 | /* Aka, the IMR.  Simply reading the data port are OCW1s. */ | 
 | uint16_t pic_get_mask(void) | 
 | { | 
 | 	uint16_t ret; | 
 |  | 
 | 	spin_lock_irqsave(&piclock); | 
 | 	ret = (inb(PIC2_DATA) << 8) | inb(PIC1_DATA); | 
 | 	spin_unlock_irqsave(&piclock); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static uint16_t __pic_get_irq_reg(int ocw3) | 
 | { | 
 | 	uint16_t ret; | 
 |  | 
 | 	spin_lock_irqsave(&piclock); | 
 | 	/* OCW3 to PIC CMD to get the register values.  PIC2 is chained, and | 
 | 	 * represents IRQs 8-15.  PIC1 is IRQs 0-7, with 2 being the chain */ | 
 | 	outb(PIC1_CMD, ocw3); | 
 | 	outb(PIC2_CMD, ocw3); | 
 | 	ret = (inb(PIC2_CMD) << 8) | inb(PIC1_CMD); | 
 | 	spin_unlock_irqsave(&piclock); | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* Returns the combined value of the cascaded PICs irq request register */ | 
 | uint16_t pic_get_irr(void) | 
 | { | 
 | 	return __pic_get_irq_reg(PIC_READ_IRR); | 
 | } | 
 |  | 
 | /* Returns the combined value of the cascaded PICs irq service register */ | 
 | uint16_t pic_get_isr(void) | 
 | { | 
 | 	return __pic_get_irq_reg(PIC_READ_ISR); | 
 | } | 
 |  | 
 | /* Takes a raw vector/trap number (32-47), not a device IRQ (0-15) */ | 
 | bool pic_check_spurious(int trap_nr) | 
 | { | 
 | 	/* the PIC may send spurious irqs via one of the chips irq 7.  if the | 
 | 	 * isr doesn't show that irq, then it was spurious, and we don't send an | 
 | 	 * eoi.  Check out http://wiki.osdev.org/8259_PIC#Spurious_IRQs */ | 
 | 	if ((trap_nr == PIC1_SPURIOUS) && !(pic_get_isr() & (1 << 7))) { | 
 | 		/* want to know if this happens */ | 
 | 		printd("Spurious PIC1 irq!\n"); | 
 | 		return TRUE; | 
 | 	} | 
 | 	if ((trap_nr == PIC2_SPURIOUS) && !(pic_get_isr() & (1 << 15))) { | 
 | 		/* want to know if this happens */ | 
 | 		printd("Spurious PIC2 irq!\n"); | 
 | 		/* for the cascaded PIC, we *do* need to send an EOI to the | 
 | 		 * master's cascade irq (2). */ | 
 | 		pic_send_eoi(2 + PIC1_OFFSET); | 
 | 		return TRUE; | 
 | 	} | 
 | 	return FALSE; | 
 | } | 
 |  | 
 | void pic_send_eoi(int trap_nr) | 
 | { | 
 | 	int irq = trap_nr - PIC1_OFFSET; | 
 |  | 
 | 	spin_lock_irqsave(&piclock); | 
 | 	// all irqs beyond the first seven need to be chained to the slave | 
 | 	if (irq > 7) | 
 | 		outb(PIC2_CMD, PIC_EOI); | 
 | 	outb(PIC1_CMD, PIC_EOI); | 
 | 	spin_unlock_irqsave(&piclock); | 
 | } |