blob: 37939c020758f48473792ee954712ffff1dd6dfc [file] [log] [blame] [edit]
/*
* 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;
}