| /* |
| * Copyright (c) 2009 The Regents of the University of California |
| * Barret Rhoden <brho@cs.berkeley.edu> |
| * See LICENSE for details. |
| */ |
| |
| #ifdef __SHARC__ |
| #pragma nosharc |
| #define SINIT(x) x |
| #endif |
| |
| #include <arch/mmu.h> |
| #include <arch/x86.h> |
| #include <arch/arch.h> |
| #include <arch/apic.h> |
| #include <time.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <bitmask.h> |
| |
| system_timing_t RO system_timing = {0, 0, 0xffff, 0}; |
| bool core_id_ready = FALSE; |
| 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(uint8_t irq) |
| { |
| 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(uint8_t irq) |
| { |
| spin_lock_irqsave(&piclock); |
| if (irq > 7) { |
| outb(PIC2_DATA, inb(PIC2_DATA) & ~(1 << (irq - 8))); |
| outb(PIC1_DATA, inb(PIC1_DATA) & 0xfb); // make sure irq2 is unmasked |
| } else |
| outb(PIC1_DATA, inb(PIC1_DATA) & ~(1 << irq)); |
| spin_unlock_irqsave(&piclock); |
| } |
| |
| /* 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); |
| } |
| |
| void pic_send_eoi(uint32_t irq) |
| { |
| 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); |
| } |
| |
| /* Debugging helper. Note the ISR/IRR are 32 bits at a time, spaced every 16 |
| * bytes in the LAPIC address space. */ |
| void lapic_print_isr(void) |
| { |
| printk("LAPIC ISR on core %d\n--------------\n", core_id()); |
| for (int i = 7; i >= 0; i--) |
| printk("%3d-%3d: %p\n", (i + 1) * 32 - 1, i * 32, |
| *(uint32_t*)(LAPIC_ISR + i * 0x10)); |
| printk("LAPIC IRR on core %d\n--------------\n", core_id()); |
| for (int i = 7; i >= 0; i--) |
| printk("%3d-%3d: %p\n", (i + 1) * 32 - 1, i * 32, |
| *(uint32_t*)(LAPIC_IRR + i * 0x10)); |
| } |
| |
| /* Returns TRUE if the bit 'vector' is set in the LAPIC ISR or IRR (whatever you |
| * pass in. These registers consist of 8, 32 byte registers spaced every 16 |
| * bytes from the base in the LAPIC. */ |
| static bool __lapic_get_isrr_bit(unsigned long base, uint8_t vector) |
| { |
| int which_reg = vector >> 5; /* 32 bits per reg */ |
| uint32_t *lapic_reg = (uint32_t*)(base + which_reg * 0x10); /* offset 16 */ |
| return (*lapic_reg & (1 << (vector % 32)) ? 1 : 0); |
| } |
| |
| bool lapic_get_isr_bit(uint8_t vector) |
| { |
| return __lapic_get_isrr_bit(LAPIC_ISR, vector); |
| } |
| |
| bool lapic_get_irr_bit(uint8_t vector) |
| { |
| return __lapic_get_isrr_bit(LAPIC_IRR, vector); |
| } |
| |
| /* This works for any interrupt that goes through the LAPIC, but not things like |
| * ExtInts. To prevent abuse, we'll use it just for IPIs for now (which only |
| * come via the APIC). |
| * |
| * We only check the IRR, due to how we send EOIs. Since we don't send til |
| * after handlers return, the ISR will show pending for the current IRQ. It is |
| * the EOI that clears the bit from the ISR. */ |
| bool ipi_is_pending(uint8_t vector) |
| { |
| return lapic_get_isr_bit(vector); |
| } |
| |
| /* |
| * Sets the LAPIC timer to go off after a certain number of ticks. The primary |
| * clock freq is actually the bus clock, which we figure out during timer_init |
| * Unmasking is implied. Ref SDM, 3A, 9.6.4 |
| */ |
| void __lapic_set_timer(uint32_t ticks, uint8_t vec, bool periodic, uint8_t div) |
| { |
| #ifdef CONFIG_LOUSY_LAPIC_TIMER |
| /* qemu without kvm seems to delay timer IRQs on occasion, and needs extra |
| * IRQs from any source to get them delivered. periodic does the trick. */ |
| periodic = TRUE; |
| #endif |
| // clears bottom bit and then set divider |
| write_mmreg32(LAPIC_TIMER_DIVIDE, (read_mmreg32(LAPIC_TIMER_DIVIDE) &~0xf) | |
| (div & 0xf)); |
| // set LVT with interrupt handling information |
| write_mmreg32(LAPIC_LVT_TIMER, vec | (periodic << 17)); |
| write_mmreg32(LAPIC_TIMER_INIT, ticks); |
| // For debugging when we expand this |
| //cprintf("LAPIC LVT Timer: 0x%08x\n", read_mmreg32(LAPIC_LVT_TIMER)); |
| //cprintf("LAPIC Init Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_INIT)); |
| //cprintf("LAPIC Current Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_CURRENT)); |
| } |
| |
| void lapic_set_timer(uint32_t usec, bool periodic) |
| { |
| /* If we overflowed a uint32, send in the max timer possible. The lapic can |
| * only handle a 32 bit. We could muck with changing the divisor, but even |
| * then, we might not be able to match 4000 sec (based on the bus speed). |
| * The kernel alarm code can handle spurious timer interrupts, so we just |
| * set the timer for as close as we can get to the desired time. */ |
| uint64_t ticks64 = (usec * system_timing.bus_freq) / LAPIC_TIMER_DIVISOR_VAL |
| / 1000000; |
| uint32_t ticks32 = ((ticks64 >> 32) ? 0xffffffff : ticks64); |
| assert(ticks32 > 0); |
| __lapic_set_timer(ticks32, LAPIC_TIMER_DEFAULT_VECTOR, periodic, |
| LAPIC_TIMER_DIVISOR_BITS); |
| } |
| |
| void set_core_timer(uint32_t usec, bool periodic) |
| { |
| if (usec) |
| lapic_set_timer(usec, periodic); |
| else |
| lapic_disable_timer(); |
| } |
| |
| uint32_t lapic_get_default_id(void) |
| { |
| uint32_t ebx; |
| cpuid(0x1, 0x0, 0, &ebx, 0, 0); |
| // p6 family only uses 4 bits here, and 0xf is reserved for the IOAPIC |
| return (ebx & 0xFF000000) >> 24; |
| } |
| |
| // timer init calibrates both tsc timer and lapic timer using PIT |
| void timer_init(void){ |
| /* some boards have this unmasked early on. */ |
| pic_mask_irq(0); |
| uint64_t tscval[2]; |
| long timercount[2]; |
| pit_set_timer(0xffff, TIMER_RATEGEN); |
| // assume tsc exist |
| tscval[0] = read_tsc(); |
| udelay_pit(1000000); |
| tscval[1] = read_tsc(); |
| system_timing.tsc_freq = SINIT(tscval[1] - tscval[0]); |
| cprintf("TSC Frequency: %llu\n", system_timing.tsc_freq); |
| __lapic_set_timer(0xffffffff, LAPIC_TIMER_DEFAULT_VECTOR, FALSE, |
| LAPIC_TIMER_DIVISOR_BITS); |
| // Mask the LAPIC Timer, so we never receive this interrupt (minor race) |
| mask_lapic_lvt(LAPIC_LVT_TIMER); |
| timercount[0] = read_mmreg32(LAPIC_TIMER_CURRENT); |
| udelay_pit(1000000); |
| timercount[1] = read_mmreg32(LAPIC_TIMER_CURRENT); |
| system_timing.bus_freq = (timercount[0] - timercount[1]) |
| * LAPIC_TIMER_DIVISOR_VAL; |
| /* The time base for the timer is derived from the processor's bus clock, |
| * divided by the value specified in the divide configuration register. |
| * Note we mult and div by the divisor, saving the actual freq (even though |
| * we don't use it yet). */ |
| cprintf("Bus Frequency: %llu\n", system_timing.bus_freq); |
| } |
| |
| void pit_set_timer(uint32_t divisor, uint32_t mode) |
| { |
| if (divisor & 0xffff0000) |
| warn("Divisor too large!"); |
| mode = TIMER_SEL0|TIMER_16BIT|mode; |
| outb(TIMER_MODE, mode); |
| outb(TIMER_CNTR0, divisor & 0xff); |
| outb(TIMER_CNTR0, (divisor >> 8) ); |
| system_timing.pit_mode = SINIT(mode); |
| system_timing.pit_divisor = SINIT(divisor); |
| // cprintf("timer mode set to %d, divisor %d\n",mode, divisor); |
| } |
| |
| static int getpit() |
| { |
| int high, low; |
| // TODO: need a lock to protect access to PIT |
| |
| /* Select counter 0 and latch counter value. */ |
| outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); |
| |
| low = inb(TIMER_CNTR0); |
| high = inb(TIMER_CNTR0); |
| |
| return ((high << 8) | low); |
| } |
| |
| // forces cpu to relax for usec miliseconds. declared in kern/include/time.h |
| void udelay(uint64_t usec) |
| { |
| #if !defined(__BOCHS__) |
| if (system_timing.tsc_freq != 0) |
| { |
| uint64_t start, end, now; |
| |
| start = read_tsc(); |
| end = start + usec2tsc(usec); |
| //cprintf("start %llu, end %llu\n", start, end); |
| if (end == 0) cprintf("This is terribly wrong \n"); |
| do { |
| cpu_relax(); |
| now = read_tsc(); |
| //cprintf("now %llu\n", now); |
| } while (now < end || (now > start && end < start)); |
| return; |
| |
| } else |
| #endif |
| { |
| udelay_pit(usec); |
| } |
| } |
| |
| void udelay_pit(uint64_t usec) |
| { |
| |
| int64_t delta, prev_tick, tick, ticks_left; |
| prev_tick = getpit(); |
| /* |
| * Calculate (n * (i8254_freq / 1e6)) without using floating point |
| * and without any avoidable overflows. |
| */ |
| if (usec <= 0) |
| ticks_left = 0; |
| // some optimization from bsd code |
| else if (usec < 256) |
| /* |
| * Use fixed point to avoid a slow division by 1000000. |
| * 39099 = 1193182 * 2^15 / 10^6 rounded to nearest. |
| * 2^15 is the first power of 2 that gives exact results |
| * for n between 0 and 256. |
| */ |
| ticks_left = ((uint64_t)usec * 39099 + (1 << 15) - 1) >> 15; |
| else |
| // round up the ticks left |
| ticks_left = ((uint64_t)usec * (long long)PIT_FREQ+ 999999) |
| / 1000000; |
| while (ticks_left > 0) { |
| tick = getpit(); |
| delta = prev_tick - tick; |
| prev_tick = tick; |
| if (delta < 0) { |
| // counter looped around during the delta time period |
| delta += system_timing.pit_divisor; // maximum count |
| if (delta < 0) |
| delta = 0; |
| } |
| ticks_left -= delta; |
| } |
| } |
| |
| uint64_t gettimer(void) |
| { |
| return read_tsc(); |
| } |
| |
| uint64_t getfreq(void) |
| { |
| return system_timing.tsc_freq; |
| } |