/*-------------------------------------------------------------------- | |
Copyright(c) 2015 Intel Corporation. All rights reserved. | |
Redistribution and use in source and binary forms, with or without | |
modification, are permitted provided that the following conditions | |
are met: | |
* Redistributions of source code must retain the above copyright | |
notice, this list of conditions and the following disclaimer. | |
* Redistributions in binary form must reproduce the above copyright | |
notice, this list of conditions and the following disclaimer in | |
the documentation and/or other materials provided with the | |
distribution. | |
* Neither the name of Intel Corporation nor the names of its | |
contributors may be used to endorse or promote products derived | |
from this software without specific prior written permission. | |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
--------------------------------------------------------------------*/ | |
/*----------------------------------------------------------------------- | |
* Any required includes | |
*------------------------------------------------------------------------ | |
*/ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "portmacro.h" | |
#include "galileo_support.h" | |
/*----------------------------------------------------------------------- | |
* Function prototypes | |
*------------------------------------------------------------------------ | |
*/ | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
static void vHPETIRQHandler0(void); | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
void vHPETIRQHandler1(void); | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
static void vHPETIRQHandler2(void); | |
#endif | |
/*----------------------------------------------------------------------- | |
* Always inline HPET ISR related routines (even with no optimization), | |
* This is done for speed reasons to keep the ISR as fast as possible. | |
*------------------------------------------------------------------------ | |
*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static inline void vSetTVS( uint32_t ) __attribute__((always_inline)); | |
static inline void vSetHPETComparator( uint32_t, uint64_t ) __attribute__((always_inline)); | |
static inline uint64_t ullReadHPETCounters( void ) __attribute__((always_inline)); | |
static inline void vHPET_ISR (uint32_t) __attribute__((always_inline)); | |
#endif | |
/*----------------------------------------------------------------------- | |
* Global variables | |
*------------------------------------------------------------------------ | |
*/ | |
volatile uint32_t hpet_general_status; | |
volatile uint32_t ulHPETTimerNumber [3] = {0, 1, 2}; | |
volatile uint32_t ulHPETTotalInterrupts [3] = {0, 0, 0}; | |
volatile uint32_t ulHPETElapsedSeconds [3] = {0, 0, 0}; | |
volatile uint32_t ulHPETInterruptFrequency [3] = {0, 0, 0}; | |
volatile uint32_t ulHPETTicksToInterrupt [3] = {0, 0, 0}; | |
struct hpet_info PrintInfo[3] = | |
{ | |
{0, 0, 0, 0, 0, 0, 0}, | |
{1, 0, 0, 0, 0, 0, 0}, | |
{2, 0, 0, 0, 0, 0, 0}, | |
}; | |
/*----------------------------------------------------------------------- | |
* Static variables | |
*------------------------------------------------------------------------ | |
*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static uint32_t hpet_general_id; | |
static uint32_t hpet_counter_tick_period; | |
#endif | |
/*----------------------------------------------------------------------- | |
* General HPET functions | |
*------------------------------------------------------------------------ | |
*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static void vClearHPETCounters( void ) | |
{ | |
hpetHPET_MAIN_CTR_LOW = 0UL; | |
hpetHPET_MAIN_CTR_HIGH = 0UL; | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
void vClearHPETElapsedSeconds( void ) | |
{ | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
{ | |
ulHPETElapsedSeconds[0] = 0UL; | |
ulHPETTotalInterrupts [0] = 0UL; | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
{ | |
ulHPETElapsedSeconds[1] = 0UL; | |
ulHPETTotalInterrupts [1] = 0UL; | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
{ | |
ulHPETElapsedSeconds[2] = 0UL; | |
ulHPETTotalInterrupts [2] = 0UL; | |
} | |
#endif | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static inline void vSetTVS( uint32_t TimerNumber ) | |
{ | |
volatile uint32_t hpet_cfg = 0UL; | |
const uint32_t uiTVS = (1UL << 6UL); | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
{ | |
if (TimerNumber == 0) | |
{ | |
hpet_cfg = hpetHPET_TMR0_CONFIG_LOW | uiTVS; | |
hpetHPET_TMR0_CONFIG_LOW = hpet_cfg; | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
{ | |
if (TimerNumber == 1) | |
{ | |
hpet_cfg = hpetHPET_TMR1_CONFIG_LOW | uiTVS; | |
hpetHPET_TMR1_CONFIG_LOW = hpet_cfg; | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
{ | |
if (TimerNumber == 2) | |
{ | |
hpet_cfg = hpetHPET_TMR2_CONFIG_LOW | uiTVS; | |
hpetHPET_TMR2_CONFIG_LOW = hpet_cfg; | |
} | |
} | |
#endif | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static inline void vSetHPETComparator( uint32_t TimerNumber, uint64_t Value ) | |
{ | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
{ | |
if (TimerNumber == 0) | |
{ | |
vSetTVS(TimerNumber); | |
hpetHPET_TMR0_COMPARATOR_LOW = (uint32_t)(Value & 0xFFFFFFFFULL); | |
vSetTVS(TimerNumber); | |
hpetHPET_TMR0_COMPARATOR_HIGH = (uint32_t)((Value >> 32UL) & 0xFFFFFFFFULL); | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
{ | |
if (TimerNumber == 1) | |
{ | |
vSetTVS(TimerNumber); | |
hpetHPET_TMR1_COMPARATOR_LOW = (uint32_t)(Value & 0xFFFFFFFFULL); | |
vSetTVS(TimerNumber); | |
hpetHPET_TMR1_COMPARATOR_HIGH = (uint32_t)((Value >> 32UL) & 0xFFFFFFFFULL); | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
{ | |
if (TimerNumber == 2) | |
{ | |
vSetTVS(TimerNumber); | |
hpetHPET_TMR2_COMPARATOR_LOW = (uint32_t)(Value & 0xFFFFFFFFULL); | |
vSetTVS(TimerNumber); | |
hpetHPET_TMR2_COMPARATOR_HIGH = (uint32_t)((Value >> 32UL) & 0xFFFFFFFFULL); | |
} | |
} | |
#endif | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static inline uint64_t ullReadHPETCounters( void ) | |
{ | |
volatile uint64_t Counters = (uint64_t) | |
(((uint64_t)hpetHPET_MAIN_CTR_HIGH << 32UL) | | |
(uint64_t)hpetHPET_MAIN_CTR_LOW); | |
return Counters; | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static void vStopHPETTimer( uint32_t ClearCounters ) | |
{ | |
uint32_t hpet_cfg = 0UL; | |
/* Clear configuration enable bit */ | |
hpet_cfg = hpetHPET_GENERAL_CONFIGURATION; | |
hpet_cfg &= ~hpetHPET_CFG_ENABLE; | |
hpetHPET_GENERAL_CONFIGURATION = hpet_cfg; | |
/* Clear counters */ | |
if (ClearCounters) | |
vClearHPETCounters(); | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static void vStartHPETTimer( void ) | |
{ | |
uint32_t hpet_cfg = 0UL; | |
uint8_t LegacyMode = TRUE; // See table in doc # 329676 page 867 | |
hpet_general_status = hpetHPET_GENERAL_STATUS; | |
if (hpet_general_status != 0x0UL) | |
hpetHPET_GENERAL_STATUS = hpet_general_status; | |
hpet_cfg = hpetHPET_GENERAL_CONFIGURATION; | |
hpet_cfg |= hpetHPET_CFG_ENABLE; | |
if(LegacyMode != FALSE) | |
hpet_cfg |= hpetHPET_CFG_LEGACY; | |
else | |
hpet_cfg &= ~hpetHPET_CFG_LEGACY; | |
hpetHPET_GENERAL_CONFIGURATION = hpet_cfg; | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static void vConfigureHPETTimer( uint32_t TimerNumber, uint32_t PeriodicMode ) | |
{ | |
uint32_t hpet_cfg = 0UL; // Configuration data | |
uint8_t IRQNumber = 0; // Hardware ISR number | |
uint8_t Triggering = 0; // Level or Edge sensitive | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
{ | |
if (TimerNumber == 0) | |
{ | |
IRQNumber = TIMER0_IRQ; | |
Triggering = TIMER0_TRIGGERING; | |
hpet_cfg = hpetHPET_TMR0_CONFIG_LOW; | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
{ | |
if (TimerNumber == 1) | |
{ | |
IRQNumber = TIMER1_IRQ; | |
Triggering = TIMER1_TRIGGERING; | |
hpet_cfg = hpetHPET_TMR1_CONFIG_LOW; | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
{ | |
if (TimerNumber == 2) | |
{ | |
IRQNumber = TIMER2_IRQ; | |
Triggering = TIMER2_TRIGGERING; | |
hpet_cfg = hpetHPET_TMR2_CONFIG_LOW; | |
} | |
} | |
#endif | |
/* Modify configuration */ | |
if (PeriodicMode != FALSE) | |
{ | |
hpet_cfg |= hpetHPET_TN_ENABLE | hpetHPET_TN_PERIODIC | hpetHPET_TN_SETVAL | | |
hpetHPET_TN_32BIT | ((IRQNumber & 0x1F) << 9UL); | |
} | |
else | |
{ | |
hpet_cfg |= hpetHPET_TN_ENABLE | hpetHPET_TN_SETVAL | | |
hpetHPET_TN_32BIT | ((IRQNumber & 0x1F) << 9UL); | |
} | |
/* Setup triggering bit */ | |
if (Triggering != hpetHPET_INT_EDGE) | |
hpet_cfg |= (1UL << 1UL); | |
else | |
hpet_cfg &= ~(1UL << 1UL); | |
/* write-out configuration */ | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
if (TimerNumber == 0) | |
{ | |
hpetHPET_TMR0_CONFIG_LOW = hpet_cfg; | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
if (TimerNumber == 1) | |
{ | |
hpetHPET_TMR1_CONFIG_LOW = hpet_cfg; | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
if (TimerNumber == 2) | |
{ | |
hpetHPET_TMR2_CONFIG_LOW = hpet_cfg; | |
} | |
#endif | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static void vGetHPETCapabilitiesAndStatus( void ) | |
{ | |
/* Get HPET capabilities and ID */ | |
hpet_general_id = hpetHPET_GENERAL_ID; | |
/* Invalid vendor ID - Should be Intel (0x8086") */ | |
if ((hpet_general_id >> 16) != 0x8086UL) | |
{ | |
configASSERT( 0 ); | |
} | |
/* Get number of ns/tick - default is 69.841279 */ | |
hpet_counter_tick_period = hpetHPET_COUNTER_TICK_PERIOD; | |
/* General status of HPET - bit 0 = T0, bit 1 = T1 and bit 2 = T2. | |
* In level triggered mode 1 means interrupt is active */ | |
hpet_general_status = hpetHPET_GENERAL_STATUS; | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static void vCheckHPETIRQCapabilities( uint32_t TimerNumber) | |
{ | |
uint32_t hpet_cfg_h = 0UL; | |
uint32_t hpet_cfg_l = 0UL; | |
uint32_t IRQNumber = 0UL; | |
uint32_t Triggering = hpetHPET_INT_EDGE; | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
{ | |
if (TimerNumber == 0) | |
{ | |
IRQNumber = TIMER0_IRQ; | |
Triggering = TIMER0_TRIGGERING; | |
hpet_cfg_h = hpetHPET_TMR0_CONFIG_HIGH; | |
hpet_cfg_l = hpetHPET_TMR0_CONFIG_LOW; | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
{ | |
if (TimerNumber == 1) | |
{ | |
IRQNumber = TIMER1_IRQ; | |
Triggering = TIMER1_TRIGGERING; | |
hpet_cfg_h = hpetHPET_TMR1_CONFIG_HIGH; | |
hpet_cfg_l = hpetHPET_TMR1_CONFIG_LOW; | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
{ | |
if (TimerNumber == 2) | |
{ | |
IRQNumber = TIMER2_IRQ; | |
Triggering = TIMER2_TRIGGERING; | |
hpet_cfg_h = hpetHPET_TMR2_CONFIG_HIGH; | |
hpet_cfg_l = hpetHPET_TMR2_CONFIG_LOW; | |
} | |
} | |
#endif | |
/* Setup configuration register */ | |
hpet_cfg_l |= hpetHPET_TN_ENABLE | hpetHPET_TN_PERIODIC | | |
hpetHPET_TN_32BIT | ((IRQNumber & 0x1F) << 9UL); | |
/* Setup triggering bit */ | |
if (Triggering != hpetHPET_INT_EDGE) | |
hpet_cfg_l |= (1UL << 1UL); | |
else | |
hpet_cfg_l &= ~(1UL << 1UL); | |
/* Write then read back configuration */ | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
{ | |
if (TimerNumber == 0) | |
{ | |
hpetHPET_TMR0_CONFIG_HIGH = hpet_cfg_h; | |
hpetHPET_TMR0_CONFIG_LOW = hpet_cfg_l; | |
hpet_cfg_l = hpetHPET_TMR0_CONFIG_LOW; | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
{ | |
if (TimerNumber == 1) | |
{ | |
hpetHPET_TMR1_CONFIG_HIGH = hpet_cfg_h; | |
hpetHPET_TMR1_CONFIG_LOW = hpet_cfg_l; | |
hpet_cfg_l = hpetHPET_TMR1_CONFIG_LOW; | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
{ | |
if (TimerNumber == 2) | |
{ | |
hpetHPET_TMR2_CONFIG_HIGH = hpet_cfg_h; | |
hpetHPET_TMR2_CONFIG_LOW = hpet_cfg_l; | |
hpet_cfg_l = hpetHPET_TMR2_CONFIG_LOW; | |
} | |
} | |
#endif | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static uint32_t uiCalibrateHPETTimer(uint32_t TimerNumber, uint32_t Calibrate) | |
{ | |
uint32_t ticks_per_ms = 15422; // 1e-3/64.84127e-9 (denominator is hpet_counter_tick_period) | |
if (Calibrate) | |
{ | |
uint32_t uiRunningTotal = 0UL; | |
uint32_t i = 0UL; | |
for (i = 0; i < 5; i++) | |
uiRunningTotal += uiCalibrateTimer(TimerNumber, hpetHPETIMER); | |
ticks_per_ms = (uiRunningTotal / 5); | |
} | |
return ticks_per_ms; | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static void vSetupIOApic( uint32_t TimerNumber ) | |
{ | |
uint8_t DeliveryMode = 1; // 0 means fixed (use ISR Vector) | |
uint8_t DestinationMode = 0; // Used by local APIC for MSI | |
uint8_t IRQPolarity = 1; // 0 means active high, 1 = active low | |
uint8_t InterruptMask = 0; // 0 means allow interrupts | |
uint8_t Triggering = hpetHPET_INT_EDGE; // Level or Edge sensitive | |
uint8_t IRQNumber = 0; // Hardware IRQ number | |
uint8_t ISRVector = 0; // Desired ISR vector | |
/* Select polarity and triggering */ | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
{ | |
if (TimerNumber == 0) | |
{ | |
IRQNumber = TIMER0_IRQ; | |
ISRVector = hpetHPET_TIMER0_ISR_VECTOR; | |
IRQPolarity = TIMER0_POLARITY; | |
Triggering = TIMER0_TRIGGERING; | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
{ | |
if (TimerNumber == 1) | |
{ | |
IRQNumber = TIMER1_IRQ; | |
ISRVector = hpetHPET_TIMER1_ISR_VECTOR; | |
IRQPolarity = TIMER1_POLARITY; | |
Triggering = TIMER1_TRIGGERING; | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
{ | |
if (TimerNumber == 2) | |
{ | |
IRQNumber = TIMER2_IRQ; | |
ISRVector = hpetHPET_TIMER2_ISR_VECTOR; | |
IRQPolarity = TIMER2_POLARITY; | |
Triggering = TIMER2_TRIGGERING; | |
} | |
} | |
#endif | |
/* Data to write to RTE register Lower DW */ | |
uint32_t ConfigDataLDW = (uint32_t)(ISRVector | ((DeliveryMode & 0x07) << 8UL)); | |
/* Set or clear bits in configuration data */ | |
if (DestinationMode == 0) | |
ConfigDataLDW &= ~(1UL << 11UL); | |
else | |
ConfigDataLDW |= (1UL << 11UL); | |
if (IRQPolarity == 0) | |
ConfigDataLDW &= ~(1UL << 13UL); | |
else | |
ConfigDataLDW |= (1UL << 13UL); | |
if (Triggering != FALSE) | |
ConfigDataLDW |= (1UL << 15UL); | |
else | |
ConfigDataLDW &= ~(1UL << 15UL); | |
if (InterruptMask == 0) | |
ConfigDataLDW &= ~(1UL << 16UL); | |
else | |
ConfigDataLDW |= (1UL << 16UL); | |
/* Data to write to RTE register Upper DW */ | |
uint32_t LocalAPIC_DID = ((portAPIC_ID_REGISTER & 0xFF000000UL) >> 24UL); // get local APIC DID | |
uint32_t LocalAPIC_EDID = ((portAPIC_ID_REGISTER & 0x00FF0000UL) >> 16UL); // get local APIC Extended DID | |
uint32_t ConfigDataUDW = (uint32_t)(((LocalAPIC_DID << 24UL) & 0xFF000000UL) | | |
((LocalAPIC_EDID << 16UL) & 0x00FF0000UL)); | |
/* Setup IDX and WDW register to write RTE data */ | |
hpetIO_APIC_IDX = hpetIO_APIC_RTE_OFFSET + ((2 * IRQNumber) + 1); | |
hpetIO_APIC_WDW = ConfigDataUDW; | |
hpetIO_APIC_IDX = hpetIO_APIC_RTE_OFFSET + ((2 * IRQNumber) + 0); | |
hpetIO_APIC_WDW = ConfigDataLDW; | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static void vInitilizeHPETInterrupt( uint32_t TimerNumber ) | |
{ | |
/* NOTE: In non-legacy mode interrupts are sent as MSI messages to LAPIC */ | |
uint32_t ticks_per_ms = 0UL; // Get # ticks per ms | |
uint32_t InterruptFrequency = 0UL; // Get times per second to interrupt | |
/* Stop the timers and reset the main counter */ | |
vStopHPETTimer(true); | |
/* Initialise hardware */ | |
vSetupIOApic(TimerNumber); | |
/* Register ISRs. Purely for demonstration purposes, timer 0 and timer 2 | |
use the central interrupt entry code, so are installed using | |
xPortRegisterCInterruptHandler(), while timer 1 uses its own interrupt | |
entry code, so is installed using xPortInstallInterruptHandler(). For | |
convenience the entry code for timer 1 is implemented at the bottom of | |
RegTest.S.See | |
http://www.freertos.org/RTOS_Intel_Quark_Galileo_GCC.html#interrupts for | |
more information. */ | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
{ | |
if (TimerNumber == 0) | |
{ | |
InterruptFrequency = hpetHPET_TIMER0_INTERRUPT_RATE; | |
xPortRegisterCInterruptHandler( vHPETIRQHandler0, hpetHPET_TIMER0_ISR_VECTOR ); | |
} | |
} | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
{ | |
if (TimerNumber == 1) | |
{ | |
extern void vApplicationHPETTimer1Wrapper( void ); | |
InterruptFrequency = hpetHPET_TIMER1_INTERRUPT_RATE; | |
xPortInstallInterruptHandler( vApplicationHPETTimer1Wrapper, hpetHPET_TIMER1_ISR_VECTOR ); | |
} | |
} | |
#endif | |
#if ( hpetUSE_HPET_TIMER_NUMBER_2 == 1) | |
{ | |
if (TimerNumber == 2) | |
{ | |
configASSERT(TimerNumber == 2) | |
InterruptFrequency = hpetHPET_TIMER2_INTERRUPT_RATE; | |
xPortRegisterCInterruptHandler( vHPETIRQHandler2, hpetHPET_TIMER2_ISR_VECTOR ); | |
} | |
} | |
#endif | |
/* Get calibrated ticks per millisecond before initialization. */ | |
ticks_per_ms = uiCalibrateHPETTimer(TimerNumber, TRUE); | |
/* Check IRQ compatibility - will assert here if there is a problem. */ | |
vCheckHPETIRQCapabilities(TimerNumber); | |
/* Get HPET capabilities and ID and status */ | |
vGetHPETCapabilitiesAndStatus(); | |
/* Sanity check for frequency */ | |
if ( InterruptFrequency < 1 ) | |
InterruptFrequency = 20; // default is 50 ms interrupt rate | |
/* Save interrupt frequency */ | |
ulHPETInterruptFrequency[TimerNumber] = InterruptFrequency; | |
/* Calculate required number of ticks */ | |
uint32_t ticks = ( ticks_per_ms * 1000UL ) / ulHPETInterruptFrequency[TimerNumber]; | |
/* Save the number of ticks to interrupt */ | |
ulHPETTicksToInterrupt[TimerNumber] = ticks; | |
/* Make sure counters are zeroed */ | |
vClearHPETCounters(); | |
/* Write out comparator value */ | |
vSetHPETComparator(TimerNumber, ticks); | |
/* Set target timer non-periodic mode with first interrupt at tick */ | |
vConfigureHPETTimer(TimerNumber, FALSE); | |
/* Start the timer */ | |
vStartHPETTimer(); | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
void vInitializeAllHPETInterrupts( void ) | |
{ | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
vInitilizeHPETInterrupt( 0 ); | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
vInitilizeHPETInterrupt( 1 ); | |
#endif | |
#if( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
vInitilizeHPETInterrupt( 2 ); | |
#endif | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
uint32_t uiCalibrateTimer( uint32_t TimerNumber, uint32_t TimerType) | |
{ | |
/*---------------------------------------------------------------------*/ | |
/* NOTE: If TimerType = LVTIMER then TimerNumber is ignored (PIT # 2 */ | |
/* is always used) */ | |
/*---------------------------------------------------------------------*/ | |
/*---------------------------------------------------------------------*/ | |
/* PIT (programmable interval timer) mode register Bit definitions */ | |
/*---------------------------------------------------------------------- | |
Mode register is at address 0x43: | |
Bits Usage | |
6 and 7 Select channel : | |
0 0 = Channel 0 | |
0 1 = Channel 1 | |
1 0 = Channel 2 | |
1 1 = Read-back command (8254 only) | |
4 and 5 Access mode : | |
0 0 = Latch count value command | |
0 1 = Access mode: lobyte only | |
1 0 = Access mode: hibyte only | |
1 1 = Access mode: lobyte/hibyte | |
1 to 3 Operating mode : | |
0 0 0 = Mode 0 (interrupt on terminal count) | |
0 0 1 = Mode 1 (hardware re-triggerable one-shot) | |
0 1 0 = Mode 2 (rate generator) | |
0 1 1 = Mode 3 (square wave generator) | |
1 0 0 = Mode 4 (software triggered strobe) | |
1 0 1 = Mode 5 (hardware triggered strobe) | |
1 1 0 = Mode 2 (rate generator, same as 010b) | |
1 1 1 = Mode 3 (square wave generator, same as 011b) | |
0 BCD/Binary mode: 0 = 16-bit binary, 1 = four-digit BCD | |
----------------------------------------------------------------------*/ | |
/* Used to calculate LVT ticks */ | |
const uint32_t uiLargeNumber = 0x7fffffffUL; | |
/* Default return value */ | |
uint32_t ticks = 0; | |
/* Check timer type */ | |
switch (TimerType) | |
{ | |
case hpetLVTIMER: | |
case hpetHPETIMER: | |
break; | |
default: | |
return ticks; | |
break; | |
} | |
/* Set timeout counter to a very large value */ | |
uint64_t timeout_counter = (uint64_t) (uiLargeNumber * 4); | |
/* Set PIT Ch2 to one-shot mode */ | |
uint32_t gate_register = ((inw(GATE_CONTROL) & 0xfd) | 0x01); | |
outw(GATE_CONTROL, gate_register); | |
outw(MODE_REGISTER, ONESHOT_MODE); | |
/* Set counter for 10 ms - 1193180/100 Hz ~ 11932 */ | |
uint16_t pit_counter = 11932; | |
outb(CHANNEL2_DATA, (char) (pit_counter & 0xff)); | |
outb(CHANNEL2_DATA, (char) ((pit_counter >> 8) & 0xff)); | |
/* Start target timer */ | |
if (TimerType == hpetLVTIMER) | |
{ | |
portAPIC_LVT_TIMER = portAPIC_TIMER_INT_VECTOR; | |
portAPIC_TMRDIV = portAPIC_DIV_16; | |
} | |
else if (TimerType == hpetHPETIMER) | |
{ | |
#if (hpetHPET_TIMER_IN_USE) | |
// Initialize HPE timer | |
vStopHPETTimer(TRUE); | |
/* Write out comparator value - we don't want it to interrupt */ | |
vSetHPETComparator(TimerNumber, 0xFFFFFFFFUL); | |
// Configure HPE timer for non-periodic mode | |
vConfigureHPETTimer(TimerNumber, FALSE); | |
#else | |
( void ) TimerNumber; | |
#endif | |
} | |
/* Reset PIT one-shot counter */ | |
gate_register = (inw(GATE_CONTROL) & 0xfe); | |
outw(GATE_CONTROL, gate_register); | |
gate_register |= 0x01; | |
outw(GATE_CONTROL, gate_register); | |
/* Setup target timer initial counts */ | |
if (TimerType == hpetLVTIMER) | |
{ | |
portAPIC_TIMER_INITIAL_COUNT = uiLargeNumber; | |
} | |
else if (TimerType == hpetHPETIMER) | |
{ | |
#if (hpetHPET_TIMER_IN_USE) | |
vStartHPETTimer(); | |
#endif | |
} | |
/* Wait for PIT counter to expire */ | |
for (;;) | |
{ | |
gate_register = inw(GATE_CONTROL); | |
if ((gate_register & 0x20) || (--timeout_counter == 0)) | |
{ | |
/* Stop target timer and exit loop */ | |
if (TimerType == hpetLVTIMER) | |
{ | |
portAPIC_LVT_TIMER = portAPIC_DISABLE; | |
break; | |
} | |
else if (TimerType == hpetHPETIMER) | |
{ | |
#if (hpetHPET_TIMER_IN_USE) | |
vStopHPETTimer(FALSE); | |
break; | |
#endif | |
} | |
} | |
} | |
/* Check for timeout */ | |
if (timeout_counter != 0) | |
{ | |
if (TimerType == hpetLVTIMER) | |
{ | |
/* Counter started at a large number so subtract counts */ | |
ticks = (uiLargeNumber - portAPIC_TIMER_CURRENT_COUNT); | |
/* adjust ticks for 1 ms and divider ratio */ | |
ticks = ((((ticks << 4UL) * 100) / 1000) >> 4UL); | |
} | |
else if (TimerType == hpetHPETIMER) | |
{ | |
#if (hpetHPET_TIMER_IN_USE) | |
/* Read timer counter - we only need the low counter */ | |
ticks = (uint32_t)(ullReadHPETCounters() & 0xFFFFFFFFULL); | |
/* Clear timer counter */ | |
vClearHPETCounters(); | |
/* Return 1 ms tick counts. Timed for 10 ms so just divide by 10 */ | |
ticks /= 10; | |
#endif | |
} | |
} | |
/* Return adjusted counts for a 1 ms interrupt rate. | |
* Should be approximately 25000 for LV Timer. | |
* Should be approximately 15000 for HPE Timers */ | |
return ticks; | |
} | |
/*----------------------------------------------------------------------- | |
* Interrupt service functions | |
*------------------------------------------------------------------------ | |
*/ | |
#if (hpetHPET_TIMER_IN_USE) | |
static inline void vHPET_ISR( uint32_t TimerNumber ) | |
{ | |
/*-----------------------------------------------------------------*/ | |
/* Notes: In edge triggered mode, no need to clear int status bits.*/ | |
/* */ | |
/* In non-periodic mode, comparator is added to current counts, */ | |
/* do not clear main counters. */ | |
/*-----------------------------------------------------------------*/ | |
__asm volatile( "cli" ); | |
/* Bump HPE timer interrupt count - available in a global variable */ | |
ulHPETTotalInterrupts[TimerNumber] += 1UL; | |
/* Bump HPE timer elapsed seconds count - available in a global variable */ | |
if ((ulHPETTotalInterrupts[TimerNumber] % | |
(ulHPETInterruptFrequency[TimerNumber] + 0UL)) == 0UL) | |
ulHPETElapsedSeconds[TimerNumber] += 1UL; | |
/* Reload comparators - a must do in non-periodic mode */ | |
uint64_t ullNewValue = (uint64_t) | |
(ullReadHPETCounters() + (uint64_t)ulHPETTicksToInterrupt[TimerNumber]); | |
vSetHPETComparator(TimerNumber, ullNewValue); | |
__asm volatile( "sti" ); | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if( hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
extern void vApplicationHPETTimer0Handler( void ); | |
static void vHPETIRQHandler0( void ) | |
{ | |
vHPET_ISR( 0 ); | |
vApplicationHPETTimer0Handler(); | |
hpetIO_APIC_EOI = hpetHPET_TIMER0_ISR_VECTOR; | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
extern void vApplicationHPETTimer1Handler( void ); | |
void vHPETIRQHandler1( void ) | |
{ | |
vHPET_ISR( 1 ); | |
vApplicationHPETTimer1Handler(); | |
hpetIO_APIC_EOI = hpetHPET_TIMER1_ISR_VECTOR; | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
extern void vApplicationHPETTimer2Handler( void ); | |
static void vHPETIRQHandler2( void ) | |
{ | |
vHPET_ISR( 2 ); | |
vApplicationHPETTimer2Handler(); | |
hpetIO_APIC_EOI = hpetHPET_TIMER2_ISR_VECTOR; | |
} | |
#endif | |
/*----------------------------------------------------------------------- | |
* Print HPET information functions | |
*------------------------------------------------------------------------ | |
*/ | |
#if ((hpetHPET_PRINT_INFO == 1 ) && (hpetHPET_TIMER_IN_USE)) | |
static void prvUpdateHPETInfoTask( void *pvParameters ) | |
{ | |
TickType_t xNextWakeTime, xBlockTime; | |
uint32_t TimerNumber; | |
uint8_t row, col, execute; | |
struct hpet_info *pi; | |
/* Remove compiler warning about unused parameter. */ | |
( void ) pvParameters; | |
/* Initialise xNextWakeTime - this only needs to be done once. */ | |
xNextWakeTime = xTaskGetTickCount(); | |
/* Set task blocking period. */ | |
xBlockTime = pdMS_TO_TICKS( 500 ); | |
for( ;; ) | |
{ | |
/* Place this task in the blocked state until it is time to run again. */ | |
vTaskDelayUntil( &xNextWakeTime, xBlockTime ); | |
/* Print all information */ | |
for (TimerNumber = 0; TimerNumber <= 2; TimerNumber++) | |
{ | |
execute = pdFALSE; | |
pi = &PrintInfo[TimerNumber]; | |
pi->timer_number = TimerNumber; | |
pi->main_counter_h = hpetHPET_MAIN_CTR_HIGH; | |
pi->main_counter_l = hpetHPET_MAIN_CTR_LOW; | |
pi->total_interrupts = ulHPETTotalInterrupts[TimerNumber]; | |
pi->elapsed_seconds = ulHPETElapsedSeconds[TimerNumber]; | |
#if (hpetUSE_HPET_TIMER_NUMBER_0 == 1 ) | |
if(TimerNumber == 0) | |
{ | |
row = 9 ; col = 1; | |
pi->comparator_h = hpetHPET_TMR0_COMPARATOR_HIGH; | |
pi->comparator_l = hpetHPET_TMR0_COMPARATOR_LOW; | |
execute = pdTRUE; | |
} | |
#endif | |
#if ( hpetUSE_HPET_TIMER_NUMBER_1 == 1 ) | |
if(TimerNumber == 1) | |
{ | |
row = 13 ; col = 1; | |
pi->comparator_h = hpetHPET_TMR1_COMPARATOR_HIGH; | |
pi->comparator_l = hpetHPET_TMR1_COMPARATOR_LOW; | |
execute = pdTRUE; | |
} | |
#endif | |
#if ( hpetUSE_HPET_TIMER_NUMBER_2 == 1 ) | |
if(TimerNumber == 2) | |
{ | |
row = 17 ; col = 1; | |
pi->comparator_h = hpetHPET_TMR2_COMPARATOR_HIGH; | |
pi->comparator_l = hpetHPET_TMR2_COMPARATOR_LOW; | |
execute = pdTRUE; | |
} | |
#endif | |
/* Print information on screen */ | |
if(execute == pdTRUE) | |
{ | |
g_printf_rcc(row, col, ANSI_COLOR_WHITE, | |
" HPE Timer Number = %d", pi->timer_number); | |
g_printf_rcc(row+1, col, ANSI_COLOR_WHITE, | |
" Timer Counters = 0x%08x:%08x, Comparator = 0x%08x:%08x", | |
pi->main_counter_h, pi->main_counter_l, | |
pi->comparator_h, pi->comparator_l); | |
g_printf_rcc(row+2, col, ANSI_COLOR_WHITE, | |
" Total Interrupts = 0x%08x Elapsed Seconds = %u", | |
pi->total_interrupts, pi->elapsed_seconds); | |
g_printf_rcc(row+3, col, DEFAULT_SCREEN_COLOR , "\r"); | |
} | |
} | |
} | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
void vCreateHPETInfoUpdateTask( void ) | |
{ | |
#if ((hpetHPET_PRINT_INFO == 1 ) && (hpetHPET_TIMER_IN_USE)) | |
/* Create the task that displays HPE timer information. */ | |
xTaskCreate( prvUpdateHPETInfoTask, "HPETInfo", (configMINIMAL_STACK_SIZE << 1), | |
NULL, (tskIDLE_PRIORITY), NULL ); | |
#endif | |
} | |
/*-----------------------------------------------------------*/ | |