/* * \brief Timer driver for core * \author Norman Feske * \date 2013-04-05 */ /* * Copyright (C) 2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _TIMER_H_ #define _TIMER_H_ #include #include #include #include #include namespace Genode { /** * LAPIC-based timer driver for core */ class Timer; } class Genode::Timer : public Mmio { private: enum { /* PIT constants */ PIT_TICK_RATE = 1193182ul, PIT_SLEEP_MS = 50, PIT_SLEEP_TICS = (PIT_TICK_RATE / 1000) * PIT_SLEEP_MS, PIT_CH0_DATA = 0x40, PIT_CH2_DATA = 0x42, PIT_CH2_GATE = 0x61, PIT_MODE = 0x43, }; /* Timer registers */ struct Tmr_lvt : Register<0x320, 32> { struct Vector : Bitfield<0, 8> { }; struct Delivery : Bitfield<8, 3> { }; struct Mask : Bitfield<16, 1> { }; struct Timer_mode : Bitfield<17, 2> { }; }; struct Tmr_initial : Register <0x380, 32> { }; struct Tmr_current : Register <0x390, 32> { }; uint32_t _tics_per_ms = 0; /* Measure LAPIC timer frequency using PIT channel 2 */ uint32_t pit_calc_timer_freq(void) { uint32_t t_start, t_end; /* Set channel gate high and disable speaker */ outb(PIT_CH2_GATE, (inb(0x61) & ~0x02) | 0x01); /* Set timer counter (mode 0, binary count) */ outb(PIT_MODE, 0xb0); outb(PIT_CH2_DATA, PIT_SLEEP_TICS & 0xff); outb(PIT_CH2_DATA, PIT_SLEEP_TICS >> 8); write(~0U); t_start = read(); while ((inb(PIT_CH2_GATE) & 0x20) == 0) { asm volatile("pause" : : : "memory"); } t_end = read(); write(0); return (t_start - t_end) / PIT_SLEEP_MS; } public: Timer() : Mmio(Board::MMIO_LAPIC_BASE) { /* Enable LAPIC timer in one-shot mode */ write(Board::TIMER_VECTOR_KERNEL); write(0); write(0); write(0); /* Calculate timer frequency */ _tics_per_ms = pit_calc_timer_freq(); PINF("LAPIC: timer frequency %u kHz", _tics_per_ms); } /** * Disable PIT timer channel. This is necessary since BIOS sets up * channel 0 to fire periodically. */ static void disable_pit(void) { outb(PIT_MODE, 0x30); outb(PIT_CH0_DATA, 0); outb(PIT_CH0_DATA, 0); } static unsigned interrupt_id(int) { return Board::TIMER_VECTOR_KERNEL; } inline void start_one_shot(uint32_t const tics, unsigned) { write(tics); } uint32_t ms_to_tics(unsigned const ms) { return ms * _tics_per_ms; } unsigned value(unsigned) { return read(); } }; namespace Kernel { class Timer : public Genode::Timer { }; } #endif /* _TIMER_H_ */