#include "lapic.h" #include "lapic_registers.h" #include "core.h" #include "pit.h" #include "../debug/output.h" namespace LAPIC { namespace Timer { /*! \brief Timer Delivery Status */ enum DeliveryStatus { IDLE = 0, SEND_PENDING = 1 }; /*! \brief Timer Mode */ enum TimerMode { ONE_SHOT = 0, PERIODIC = 1, DEADLINE = 2 // reserved }; /*! \brief Timer Mask */ enum Mask { NOT_MASKED = 0, MASKED = 1 }; static const Register INVALID_DIV = 0xff; /*! \brief LAPIC-Timer Control Register * * \see [ISDMv3 10.5.1 Local Vector Table](intel_manual_vol3.pdf#page=375) */ union ControlRegister { struct { uint32_t vector : 8; ///< Vector uint32_t : 4; DeliveryStatus delivery_status : 1; ///< Delivery Status (readonly) uint32_t : 3; Mask masked : 1; ///< Interrupt Mask (if set, interrupt will not trigger) TimerMode timer_mode : 2; ///< Timer Mode uint32_t : 13; }; Register value; } __attribute__((packed)); /*! \brief LAPIC timer divider table * * \see [ISDMv3 10.5.4 APIC Timer](intel_manual_vol3.pdf#page=378) */ static const Register div_masks[] = { 0xb, ///< divides by 1 0x0, ///< divides by 2 0x1, ///< divides by 4 0x2, ///< divides by 8 0x3, ///< divides by 16 0x8, ///< divides by 32 0x9, ///< divides by 64 0xa ///< divides by 128 }; /*! \brief Calculate the bit mask for the LAPIC-timer divider. * \param div Divider, must be power of two: 1, 2, 4, 8, 16, 32, 64, 128 * \return Bit mask for LAPIC::setTimer() or `0xff` if `div` is invalid. */ static Register getClockDiv(uint8_t div) { switch (div) { case 1: return div_masks[0]; case 2: return div_masks[1]; case 4: return div_masks[2]; case 8: return div_masks[3]; case 16: return div_masks[4]; case 32: return div_masks[5]; case 64: return div_masks[6]; case 128: return div_masks[7]; default : return 0xff; } } /*! \brief Calculate the LAPIC-timer divider for the bit mask. * \param div_mask The bit mask, must be one of: 0xb, 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xa * \return LAPIC-timer divider or `0xff` if `div_mask` is invalid. */ static uint8_t fromClockDiv(Register div_mask) { if (div_mask == div_masks[0]) return 1; if (div_mask == div_masks[1]) return 2; if (div_mask == div_masks[2]) return 4; if (div_mask == div_masks[3]) return 8; if (div_mask == div_masks[4]) return 16; if (div_mask == div_masks[5]) return 32; if (div_mask == div_masks[6]) return 64; if (div_mask == div_masks[7]) return 128; return 0xff; } uint32_t ticks(void) { static uint32_t ticks = 0; // ticks per millisecond if (ticks != 0) return ticks; // Prepare Counter LAPIC::Timer::set(0, 1, 0, false, true); // Set timer for 10ms PIT::set(10 * 1000); // Start LAPIC-Timer LAPIC::write(TIMER_INITIAL_COUNTER, UINT32_MAX); PIT::waitForTimeout(); // Get final Count uint32_t counter = LAPIC::read(TIMER_CURRENT_COUNTER); // Disable LAPIC-Timer LAPIC::write(TIMER_INITIAL_COUNTER, 0); // Calculate tick count. ticks = (UINT32_MAX - counter) / 10; DBG << "LAPIC-Timer calibration using PIT" << endl; return ticks; } void set(uint32_t counter, uint8_t divide, uint8_t vector, bool periodic, bool masked) { if (divide == 0 || (divide & (divide - 1)) != 0) { // Not a power of 2 return; } ControlRegister tcr; tcr.vector = Core::Interrupt::Vector::TIMER; tcr.timer_mode = periodic ? PERIODIC : ONE_SHOT; tcr.masked = masked ? MASKED : NOT_MASKED; LAPIC::write(TIMER_CONTROL, tcr.value); LAPIC::write(TIMER_DIVIDE_CONFIGURATION, getClockDiv(divide)); LAPIC::write(TIMER_INITIAL_COUNTER, counter); } uint64_t time_tick; uint8_t dividender; bool setup(uint32_t us) { time_tick = (static_cast(ticks()) * us) / 1000ULL; dividender = 1; while (time_tick > UINT32_MAX) { // While timer_ticks is to large to fit in 32bits. time_tick >>= 1; dividender <<= 1; } if (dividender > 128) return false; // Timer interval is to large. // Setup Masked interrupts to effectively disable the timer. return true; } uint32_t interval() { uint32_t timer_ticks = LAPIC::read(TIMER_INITIAL_COUNTER); uint32_t divisor = fromClockDiv(LAPIC::read(TIMER_DIVIDE_CONFIGURATION)); uint64_t us = (static_cast(timer_ticks) * divisor * 1000ULL) / static_cast(ticks()); return static_cast(us); } void activate() { // Disable Counter to avoid spouriose interrupts. LAPIC::write(TIMER_INITIAL_COUNTER, 0); set(static_cast(time_tick), dividender, Core::Interrupt::TIMER, true, true); // Activate Timer interrupts. setMasked(false); // enable counter with correct value. LAPIC::write(TIMER_INITIAL_COUNTER, time_tick); } void setMasked(bool masked) { ControlRegister tcr; tcr.value = LAPIC::read(TIMER_CONTROL); tcr.masked = masked ? MASKED : NOT_MASKED; LAPIC::write(TIMER_CONTROL, tcr.value); } } // namespace Timer } // namespace LAPIC