X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ArmPlatformPkg%2FLibrary%2FSP804TimerLib%2FSP804TimerLib.c;h=a042dac9a3f396ce0eb72942221be5e0aca4f695;hb=1e57a46299244793beb27e74be171d1540606999;hp=fcaa23eddee91caa169e2287ae836a68585d9756;hpb=5767f22fca7c337cdc113e14b411c1fd0ea7bd53;p=mirror_edk2.git diff --git a/ArmPlatformPkg/Library/SP804TimerLib/SP804TimerLib.c b/ArmPlatformPkg/Library/SP804TimerLib/SP804TimerLib.c index fcaa23edde..a042dac9a3 100644 --- a/ArmPlatformPkg/Library/SP804TimerLib/SP804TimerLib.c +++ b/ArmPlatformPkg/Library/SP804TimerLib/SP804TimerLib.c @@ -1,260 +1,260 @@ -/** @file - - Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.
- Copyright (c) 2011, ARM Limited. All rights reserved. - - This program and the accompanying materials - are licensed and made available under the terms and conditions of the BSD License - which accompanies this distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php - - THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#include - -#include -#include -#include -#include -#include -#include - -#define SP804_TIMER_METRONOME_BASE ((UINTN)PcdGet32 (PcdSP804TimerMetronomeBase)) -#define SP804_TIMER_PERFORMANCE_BASE ((UINTN)PcdGet32 (PcdSP804TimerPerformanceBase)) - -// Setup SP810's Timer2 for managing delay functions. And Timer3 for Performance counter -// Note: ArmVE's Timer0 and Timer1 are used by TimerDxe. -RETURN_STATUS -EFIAPI -TimerConstructor ( - VOID - ) -{ - // Check if the Metronome Timer is already initialized - if (MmioRead32(SP804_TIMER_METRONOME_BASE + SP804_TIMER_CONTROL_REG) & SP804_TIMER_CTRL_ENABLE) { - return RETURN_SUCCESS; - } else { - // Configure the Metronome Timer for free running operation, 32 bits, no prescaler, and interrupt disabled - MmioWrite32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_32BIT | SP804_PRESCALE_DIV_1); - - // Start the Metronome Timer ticking - MmioOr32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_ENABLE); - } - - // Check if the Performance Timer is already initialized - if (MmioRead32(SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CONTROL_REG) & SP804_TIMER_CTRL_ENABLE) { - return RETURN_SUCCESS; - } else { - // Configure the Performance timer for free running operation, 32 bits, no prescaler, interrupt disabled - MmioWrite32 (SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_32BIT | SP804_PRESCALE_DIV_1); - - // Start the Performance Timer ticking - MmioOr32 (SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_ENABLE); - } - - return RETURN_SUCCESS; -} - -/** - Stalls the CPU for at least the given number of microseconds. - - Stalls the CPU for the number of microseconds specified by MicroSeconds. - The hardware timer is 32 bits. - The maximum possible delay is (0xFFFFFFFF / TimerFrequencyMHz), i.e. ([32bits] / FreqInMHz) - For example: - +----------------+------------+----------+----------+ - | TimerFrequency | MaxDelay | MaxDelay | MaxDelay | - | (MHz) | (us) | (s) | (min) | - +----------------+------------+----------+----------+ - | 1 | 0xFFFFFFFF | 4294 | 71.5 | - | 5 | 0x33333333 | 859 | 14.3 | - | 10 | 0x19999999 | 429 | 7.2 | - | 50 | 0x051EB851 | 86 | 1.4 | - +----------------+------------+----------+----------+ - If it becomes necessary to support higher delays, then consider using the - real time clock. - - During this delay, the cpu is not yielded to any other process, with one exception: - events that are triggered off a timer and which execute at a higher TPL than - this function. These events may call MicroSecondDelay (or NanoSecondDelay) to - fulfil their own needs. - Therefore, this function must be re-entrant, as it may be interrupted and re-started. - - @param MicroSeconds The minimum number of microseconds to delay. - - @return The value of MicroSeconds inputted. - -**/ -UINTN -EFIAPI -MicroSecondDelay ( - IN UINTN MicroSeconds - ) -{ - UINT64 DelayTicks64; // Convert from microseconds to timer ticks, more bits to detect over-range conditions. - UINTN DelayTicks; // Convert from microseconds to timer ticks, native size for general calculations. - UINTN StartTicks; // Timer value snapshot at the start of the delay - UINTN TargetTicks; // Timer value to signal the end of the delay - UINTN CurrentTicks; // Current value of the 64-bit timer value at any given moment - - // If we snapshot the timer at the start of the delay function then we minimise unaccounted overheads. - StartTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG); - - // We are operating at the limit of 32bits. For the range checking work in 64 bits to avoid overflows. - DelayTicks64 = MultU64x32((UINT64)MicroSeconds, PcdGet32(PcdSP804TimerFrequencyInMHz)); - - // We are limited to 32 bits. - // If the specified delay is exactly equal to the max range of the timer, - // then the start will be equal to the stop plus one timer overflow (wrap-around). - // To avoid having to check for that, reduce the maximum acceptable range by 1 tick, - // i.e. reject delays equal or greater than the max range of the timer. - if (DelayTicks64 >= (UINT64)SP804_MAX_TICKS) { - DEBUG((EFI_D_ERROR,"MicroSecondDelay: ERROR: MicroSeconds=%d exceed SP804 count range. Max MicroSeconds=%d\n", - MicroSeconds, - ((UINTN)SP804_MAX_TICKS/PcdGet32(PcdSP804TimerFrequencyInMHz)))); - } - ASSERT(DelayTicks64 < (UINT64)SP804_MAX_TICKS); - - // From now on do calculations only in native bit size. - DelayTicks = (UINTN)DelayTicks64; - - // Calculate the target value of the timer. - - //Note: SP804 timer is counting down - if (StartTicks >= DelayTicks) { - // In this case we do not expect a wrap-around of the timer to occur. - // CurrentTicks must be less than StartTicks and higher than TargetTicks. - // If this is not the case, then the delay has been reached and may even have been exceeded if this - // function was suspended by a higher priority interrupt. - - TargetTicks = StartTicks - DelayTicks; - - do { - CurrentTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG); - } while ((CurrentTicks > TargetTicks) && (CurrentTicks <= StartTicks)); - - } else { - // In this case TargetTicks is larger than StartTicks. - // This means we expect a wrap-around of the timer to occur and we must wait for it. - // Before the wrap-around, CurrentTicks must be less than StartTicks and less than TargetTicks. - // After the wrap-around, CurrentTicks must be larger than StartTicks and larger than TargetTicks. - // If this is not the case, then the delay has been reached and may even have been exceeded if this - // function was suspended by a higher priority interrupt. - - // The order of operations is essential to avoid arithmetic overflow problems - TargetTicks = ((UINTN)SP804_MAX_TICKS - DelayTicks) + StartTicks; - - // First wait for the wrap-around to occur - do { - CurrentTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG); - } while (CurrentTicks <= StartTicks); - - // Then wait for the target - do { - CurrentTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG); - } while (CurrentTicks > TargetTicks); - } - - return MicroSeconds; -} - -/** - Stalls the CPU for at least the given number of nanoseconds. - - Stalls the CPU for the number of nanoseconds specified by NanoSeconds. - - When the timer frequency is 1MHz, each tick corresponds to 1 microsecond. - Therefore, the nanosecond delay will be rounded up to the nearest 1 microsecond. - - @param NanoSeconds The minimum number of nanoseconds to delay. - - @return The value of NanoSeconds inputted. - -**/ -UINTN -EFIAPI -NanoSecondDelay ( - IN UINTN NanoSeconds - ) -{ - UINTN MicroSeconds; - - // Round up to 1us Tick Number - MicroSeconds = NanoSeconds / 1000; - MicroSeconds += ((NanoSeconds % 1000) == 0) ? 0 : 1; - - MicroSecondDelay (MicroSeconds); - - return NanoSeconds; -} - -/** - Retrieves the current value of a 64-bit free running performance counter. - - The counter can either count up by 1 or count down by 1. If the physical - performance counter counts by a larger increment, then the counter values - must be translated. The properties of the counter can be retrieved from - GetPerformanceCounterProperties(). - - @return The current value of the free running performance counter. - -**/ -UINT64 -EFIAPI -GetPerformanceCounter ( - VOID - ) -{ - // Free running 64-bit/32-bit counter is needed here. - // Don't think we need this to boot, just to do performance profile - UINT64 Value; - Value = MmioRead32 (SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CURRENT_REG); - return Value; -} - - -/** - Retrieves the 64-bit frequency in Hz and the range of performance counter - values. - - If StartValue is not NULL, then the value that the performance counter starts - with immediately after is it rolls over is returned in StartValue. If - EndValue is not NULL, then the value that the performance counter end with - immediately before it rolls over is returned in EndValue. The 64-bit - frequency of the performance counter in Hz is always returned. If StartValue - is less than EndValue, then the performance counter counts up. If StartValue - is greater than EndValue, then the performance counter counts down. For - example, a 64-bit free running counter that counts up would have a StartValue - of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter - that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0. - - @param StartValue The value the performance counter starts with when it - rolls over. - @param EndValue The value that the performance counter ends with before - it rolls over. - - @return The frequency in Hz. - -**/ -UINT64 -EFIAPI -GetPerformanceCounterProperties ( - OUT UINT64 *StartValue, OPTIONAL - OUT UINT64 *EndValue OPTIONAL - ) -{ - if (StartValue != NULL) { - // Timer starts with the reload value - *StartValue = 0xFFFFFFFF; - } - - if (EndValue != NULL) { - // Timer counts down to 0x0 - *EndValue = (UINT64)0ULL; - } - - return PcdGet64 (PcdEmbeddedPerformanceCounterFrequencyInHz); -} +/** @file + + Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.
+ Copyright (c) 2011, ARM Limited. All rights reserved. + + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include +#include +#include +#include +#include +#include + +#define SP804_TIMER_METRONOME_BASE ((UINTN)PcdGet32 (PcdSP804TimerMetronomeBase)) +#define SP804_TIMER_PERFORMANCE_BASE ((UINTN)PcdGet32 (PcdSP804TimerPerformanceBase)) + +// Setup SP810's Timer2 for managing delay functions. And Timer3 for Performance counter +// Note: ArmVE's Timer0 and Timer1 are used by TimerDxe. +RETURN_STATUS +EFIAPI +TimerConstructor ( + VOID + ) +{ + // Check if the Metronome Timer is already initialized + if (MmioRead32(SP804_TIMER_METRONOME_BASE + SP804_TIMER_CONTROL_REG) & SP804_TIMER_CTRL_ENABLE) { + return RETURN_SUCCESS; + } else { + // Configure the Metronome Timer for free running operation, 32 bits, no prescaler, and interrupt disabled + MmioWrite32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_32BIT | SP804_PRESCALE_DIV_1); + + // Start the Metronome Timer ticking + MmioOr32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_ENABLE); + } + + // Check if the Performance Timer is already initialized + if (MmioRead32(SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CONTROL_REG) & SP804_TIMER_CTRL_ENABLE) { + return RETURN_SUCCESS; + } else { + // Configure the Performance timer for free running operation, 32 bits, no prescaler, interrupt disabled + MmioWrite32 (SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_32BIT | SP804_PRESCALE_DIV_1); + + // Start the Performance Timer ticking + MmioOr32 (SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CONTROL_REG, SP804_TIMER_CTRL_ENABLE); + } + + return RETURN_SUCCESS; +} + +/** + Stalls the CPU for at least the given number of microseconds. + + Stalls the CPU for the number of microseconds specified by MicroSeconds. + The hardware timer is 32 bits. + The maximum possible delay is (0xFFFFFFFF / TimerFrequencyMHz), i.e. ([32bits] / FreqInMHz) + For example: + +----------------+------------+----------+----------+ + | TimerFrequency | MaxDelay | MaxDelay | MaxDelay | + | (MHz) | (us) | (s) | (min) | + +----------------+------------+----------+----------+ + | 1 | 0xFFFFFFFF | 4294 | 71.5 | + | 5 | 0x33333333 | 859 | 14.3 | + | 10 | 0x19999999 | 429 | 7.2 | + | 50 | 0x051EB851 | 86 | 1.4 | + +----------------+------------+----------+----------+ + If it becomes necessary to support higher delays, then consider using the + real time clock. + + During this delay, the cpu is not yielded to any other process, with one exception: + events that are triggered off a timer and which execute at a higher TPL than + this function. These events may call MicroSecondDelay (or NanoSecondDelay) to + fulfil their own needs. + Therefore, this function must be re-entrant, as it may be interrupted and re-started. + + @param MicroSeconds The minimum number of microseconds to delay. + + @return The value of MicroSeconds inputted. + +**/ +UINTN +EFIAPI +MicroSecondDelay ( + IN UINTN MicroSeconds + ) +{ + UINT64 DelayTicks64; // Convert from microseconds to timer ticks, more bits to detect over-range conditions. + UINTN DelayTicks; // Convert from microseconds to timer ticks, native size for general calculations. + UINTN StartTicks; // Timer value snapshot at the start of the delay + UINTN TargetTicks; // Timer value to signal the end of the delay + UINTN CurrentTicks; // Current value of the 64-bit timer value at any given moment + + // If we snapshot the timer at the start of the delay function then we minimise unaccounted overheads. + StartTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG); + + // We are operating at the limit of 32bits. For the range checking work in 64 bits to avoid overflows. + DelayTicks64 = MultU64x32((UINT64)MicroSeconds, PcdGet32(PcdSP804TimerFrequencyInMHz)); + + // We are limited to 32 bits. + // If the specified delay is exactly equal to the max range of the timer, + // then the start will be equal to the stop plus one timer overflow (wrap-around). + // To avoid having to check for that, reduce the maximum acceptable range by 1 tick, + // i.e. reject delays equal or greater than the max range of the timer. + if (DelayTicks64 >= (UINT64)SP804_MAX_TICKS) { + DEBUG((EFI_D_ERROR,"MicroSecondDelay: ERROR: MicroSeconds=%d exceed SP804 count range. Max MicroSeconds=%d\n", + MicroSeconds, + ((UINTN)SP804_MAX_TICKS/PcdGet32(PcdSP804TimerFrequencyInMHz)))); + } + ASSERT(DelayTicks64 < (UINT64)SP804_MAX_TICKS); + + // From now on do calculations only in native bit size. + DelayTicks = (UINTN)DelayTicks64; + + // Calculate the target value of the timer. + + //Note: SP804 timer is counting down + if (StartTicks >= DelayTicks) { + // In this case we do not expect a wrap-around of the timer to occur. + // CurrentTicks must be less than StartTicks and higher than TargetTicks. + // If this is not the case, then the delay has been reached and may even have been exceeded if this + // function was suspended by a higher priority interrupt. + + TargetTicks = StartTicks - DelayTicks; + + do { + CurrentTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG); + } while ((CurrentTicks > TargetTicks) && (CurrentTicks <= StartTicks)); + + } else { + // In this case TargetTicks is larger than StartTicks. + // This means we expect a wrap-around of the timer to occur and we must wait for it. + // Before the wrap-around, CurrentTicks must be less than StartTicks and less than TargetTicks. + // After the wrap-around, CurrentTicks must be larger than StartTicks and larger than TargetTicks. + // If this is not the case, then the delay has been reached and may even have been exceeded if this + // function was suspended by a higher priority interrupt. + + // The order of operations is essential to avoid arithmetic overflow problems + TargetTicks = ((UINTN)SP804_MAX_TICKS - DelayTicks) + StartTicks; + + // First wait for the wrap-around to occur + do { + CurrentTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG); + } while (CurrentTicks <= StartTicks); + + // Then wait for the target + do { + CurrentTicks = MmioRead32 (SP804_TIMER_METRONOME_BASE + SP804_TIMER_CURRENT_REG); + } while (CurrentTicks > TargetTicks); + } + + return MicroSeconds; +} + +/** + Stalls the CPU for at least the given number of nanoseconds. + + Stalls the CPU for the number of nanoseconds specified by NanoSeconds. + + When the timer frequency is 1MHz, each tick corresponds to 1 microsecond. + Therefore, the nanosecond delay will be rounded up to the nearest 1 microsecond. + + @param NanoSeconds The minimum number of nanoseconds to delay. + + @return The value of NanoSeconds inputted. + +**/ +UINTN +EFIAPI +NanoSecondDelay ( + IN UINTN NanoSeconds + ) +{ + UINTN MicroSeconds; + + // Round up to 1us Tick Number + MicroSeconds = NanoSeconds / 1000; + MicroSeconds += ((NanoSeconds % 1000) == 0) ? 0 : 1; + + MicroSecondDelay (MicroSeconds); + + return NanoSeconds; +} + +/** + Retrieves the current value of a 64-bit free running performance counter. + + The counter can either count up by 1 or count down by 1. If the physical + performance counter counts by a larger increment, then the counter values + must be translated. The properties of the counter can be retrieved from + GetPerformanceCounterProperties(). + + @return The current value of the free running performance counter. + +**/ +UINT64 +EFIAPI +GetPerformanceCounter ( + VOID + ) +{ + // Free running 64-bit/32-bit counter is needed here. + // Don't think we need this to boot, just to do performance profile + UINT64 Value; + Value = MmioRead32 (SP804_TIMER_PERFORMANCE_BASE + SP804_TIMER_CURRENT_REG); + return Value; +} + + +/** + Retrieves the 64-bit frequency in Hz and the range of performance counter + values. + + If StartValue is not NULL, then the value that the performance counter starts + with immediately after is it rolls over is returned in StartValue. If + EndValue is not NULL, then the value that the performance counter end with + immediately before it rolls over is returned in EndValue. The 64-bit + frequency of the performance counter in Hz is always returned. If StartValue + is less than EndValue, then the performance counter counts up. If StartValue + is greater than EndValue, then the performance counter counts down. For + example, a 64-bit free running counter that counts up would have a StartValue + of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter + that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0. + + @param StartValue The value the performance counter starts with when it + rolls over. + @param EndValue The value that the performance counter ends with before + it rolls over. + + @return The frequency in Hz. + +**/ +UINT64 +EFIAPI +GetPerformanceCounterProperties ( + OUT UINT64 *StartValue, OPTIONAL + OUT UINT64 *EndValue OPTIONAL + ) +{ + if (StartValue != NULL) { + // Timer starts with the reload value + *StartValue = 0xFFFFFFFF; + } + + if (EndValue != NULL) { + // Timer counts down to 0x0 + *EndValue = (UINT64)0ULL; + } + + return PcdGet64 (PcdEmbeddedPerformanceCounterFrequencyInHz); +}