embox
125 строк · 2.7 Кб
1/**
2* @file
3* @brief
4*
5* @date 28.12.2012
6* @author Anton Bulychev
7*/
8
9#include <embox/unit.h>10
11#include <stdint.h>12#include <errno.h>13
14#include <asm/io.h>15
16#include <hal/ipl.h>17#include <hal/cpu.h>18
19#include <hal/clock.h>20#include <kernel/irq.h>21#include <kernel/panic.h>22#include <kernel/time/clock_source.h>23#include <kernel/time/ktime.h>24
25#include <module/embox/driver/interrupt/lapic.h>26
27#define IRQ0 0x028#define LAPIC_HZ 1000 /* You can change it */29
30static int lapic_clock_setup(struct clock_source *cs);31
32static irq_return_t clock_handler(unsigned int irq_nr, void *dev_id) {33clock_tick_handler(dev_id);34return IRQ_HANDLED;35}
36
37static struct time_event_device lapic_event_device = {38.set_periodic = lapic_clock_setup,39.name = "lapic clock",40.irq_nr = IRQ0,41};42
43static int lapic_timer_init(struct clock_source *cs) {44ipl_t ipl = ipl_save();45
46if (ENOERR != irq_attach(IRQ0, clock_handler, 0, cs, "lapic")) {47panic("lapic timer irq_attach failed");48}49
50lapic_clock_setup(NULL);51
52ipl_restore(ipl);53
54return ENOERR;55}
56
57int lapic_clock_setup(struct clock_source *cs) {58static int initialized = 0;59uint32_t ticks, cpubusfreq, counter;60uint8_t tmp;61
62if (initialized & 0x1 << cpu_get_id()) {63return ENOERR;64}else65initialized |= 0x1 << cpu_get_id();66
67/*68* Map APIC timer to an interrupt, and by that enable it in
69* one-shot mode.
70*/
71lapic_write(LAPIC_LVT_TR, 32);72
73/* Set up divide value to 16 */74lapic_write(LAPIC_TIMER_DCR, 0x03);75
76/*77* Initialize PIT Ch 2 in one-shot mode.
78* Waiting 1/100 sec.
79*/
80outb((inb(0x61) & 0xFD) | 1, 0x61);81outb(0xB2, 0x43);82
83/* 1193180/100 Hz = 11931 = 2e9bh */84outb(0x9B, 0x42); /* LSB */85inb(0x60); /* short delay */86outb(0x2E, 0x42); /* MSB */87
88/* Reset PIT one-shot counter (start counting) */89tmp = inb(0x61) & 0xFE;90outb((uint8_t) tmp, 0x61); /* Gate low */91outb((uint8_t) tmp | 1, 0x61); /* Gate high */92
93/* Reset APIC timer (set counter to -1) */94lapic_write(LAPIC_TIMER_ICR, 0xFFFFFFFF);95
96/* Now wait until PIT counter reaches zero */97while(!(inb(0x61) & 0x20));98
99/* Stop APIC timer */100lapic_write(LAPIC_LVT_TR, 0x10000);101
102/* Now do the math... */103ticks = (0xFFFFFFFF - lapic_read(LAPIC_TIMER_CCR)) + 1;104cpubusfreq = ticks * 16 * 100;105counter = cpubusfreq / LAPIC_HZ / 16;106
107/* Set APIC timer counter initializer */108lapic_write(LAPIC_TIMER_ICR, counter < 16 ? 16 : counter);109
110/* Finally re-enable timer in periodic mode. */111lapic_write(LAPIC_LVT_TR, 32 | 0x20000);112
113#if 0114/*115* Setting divide value register again not needed by the manuals
116* although I have found buggy hardware that required it
117*/
118lapic_write(LAPIC_TIMER_DCR, 0x03);119#endif120
121return ENOERR;122}
123
124CLOCK_SOURCE_DEF(lapic_timer, lapic_timer_init, NULL,125&lapic_event_device, NULL);126