]> git.proxmox.com Git - mirror_edk2.git/blame - Omap35xxPkg/TimerDxe/Timer.c
Add some ldm/vldm optimized CopyMem routines. Add performance macros to BDS
[mirror_edk2.git] / Omap35xxPkg / TimerDxe / Timer.c
CommitLineData
a3f98646 1/** @file
2 Template for Timer Architecture Protocol driver of the ARM flavor
3
4 Copyright (c) 2008-2009, Apple Inc. All rights reserved.
5
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14**/
15
16
17#include <PiDxe.h>
18
19#include <Library/BaseLib.h>
20#include <Library/DebugLib.h>
21#include <Library/BaseMemoryLib.h>
22#include <Library/UefiBootServicesTableLib.h>
23#include <Library/UefiLib.h>
24#include <Library/PcdLib.h>
25#include <Library/IoLib.h>
26#include <Library/OmapLib.h>
27
28#include <Protocol/Timer.h>
29#include <Protocol/HardwareInterrupt.h>
30#include <Protocol/TimerDebugSupport.h>
31
32#include <Omap3530/Omap3530.h>
33
34
35// The notification function to call on every timer interrupt.
36volatile EFI_TIMER_NOTIFY mTimerNotifyFunction = (EFI_TIMER_NOTIFY)NULL;
37volatile EFI_PERIODIC_CALLBACK mTimerPeriodicCallback = (EFI_PERIODIC_CALLBACK)NULL;
38
39
40// The current period of the timer interrupt
41volatile UINT64 mTimerPeriod = 0;
42
43// Cached copy of the Hardware Interrupt protocol instance
44EFI_HARDWARE_INTERRUPT_PROTOCOL *gInterrupt = NULL;
45
46// Cached registers
47volatile UINT32 TISR;
48volatile UINT32 TCLR;
49volatile UINT32 TLDR;
50volatile UINT32 TCRR;
51volatile UINT32 TIER;
52
53// Cached interrupt vector
54volatile UINTN gVector;
55
56
026e30c4 57/**
58
59 C Interrupt Handler calledin the interrupt context when Source interrupt is active.
60
61
026e30c4 62 @param Source Source of the interrupt. Hardware routing off a specific platform defines
026e30c4 63 what source means.
64
65 @param SystemContext Pointer to system register context. Mostly used by debuggers and will
026e30c4 66 update the system context after the return from the interrupt if
026e30c4 67 modified. Don't change these values unless you know what you are doing
68
a3f98646 69**/
70VOID
71EFIAPI
72TimerInterruptHandler (
73 IN HARDWARE_INTERRUPT_SOURCE Source,
74 IN EFI_SYSTEM_CONTEXT SystemContext
75 )
76{
026e30c4 77 EFI_TPL OriginalTPL;
78
79
80
81 //
026e30c4 82 // DXE core uses this callback for the EFI timer tick. The DXE core uses locks
026e30c4 83 // that raise to TPL_HIGH and then restore back to current level. Thus we need
026e30c4 84 // to make sure TPL level is set to TPL_HIGH while we are handling the timer tick.
026e30c4 85 //
026e30c4 86 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
87
a3f98646 88
89 if (mTimerPeriodicCallback) {
90 mTimerPeriodicCallback(SystemContext);
91 }
92
93 if (mTimerNotifyFunction) {
94 mTimerNotifyFunction(mTimerPeriod);
95 }
96
97 // Clear all timer interrupts
026e30c4 98 MmioWrite32 (TISR, TISR_CLEAR_ALL);
a3f98646 99
100 // Poll interrupt status bits to ensure clearing
43263288 101 while ((MmioRead32 (TISR) & TISR_ALL_INTERRUPT_MASK) != TISR_NO_INTERRUPTS_PENDING);
a3f98646 102
103 gBS->RestoreTPL (OriginalTPL);
104}
105
026e30c4 106/**
026e30c4 107 This function registers the handler NotifyFunction so it is called every time
026e30c4 108 the timer interrupt fires. It also passes the amount of time since the last
026e30c4 109 handler call to the NotifyFunction. If NotifyFunction is NULL, then the
026e30c4 110 handler is unregistered. If the handler is registered, then EFI_SUCCESS is
026e30c4 111 returned. If the CPU does not support registering a timer interrupt handler,
026e30c4 112 then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler
026e30c4 113 when a handler is already registered, then EFI_ALREADY_STARTED is returned.
026e30c4 114 If an attempt is made to unregister a handler when a handler is not registered,
026e30c4 115 then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to
026e30c4 116 register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR
026e30c4 117 is returned.
118
026e30c4 119 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
026e30c4 120 @param NotifyFunction The function to call when a timer interrupt fires. This
026e30c4 121 function executes at TPL_HIGH_LEVEL. The DXE Core will
026e30c4 122 register a handler for the timer interrupt, so it can know
026e30c4 123 how much time has passed. This information is used to
026e30c4 124 signal timer based events. NULL will unregister the handler.
026e30c4 125 @retval EFI_SUCCESS The timer handler was registered.
026e30c4 126 @retval EFI_UNSUPPORTED The platform does not support timer interrupts.
026e30c4 127 @retval EFI_ALREADY_STARTED NotifyFunction is not NULL, and a handler is already
026e30c4 128 registered.
026e30c4 129 @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a handler was not
026e30c4 130 previously registered.
026e30c4 131 @retval EFI_DEVICE_ERROR The timer handler could not be registered.
132
026e30c4 133**/
a3f98646 134EFI_STATUS
135EFIAPI
136TimerDriverRegisterHandler (
137 IN EFI_TIMER_ARCH_PROTOCOL *This,
138 IN EFI_TIMER_NOTIFY NotifyFunction
139 )
140{
141 if ((NotifyFunction == NULL) && (mTimerNotifyFunction == NULL)) {
142 return EFI_INVALID_PARAMETER;
143 }
144
145 if ((NotifyFunction != NULL) && (mTimerNotifyFunction != NULL)) {
146 return EFI_ALREADY_STARTED;
147 }
148
149 mTimerNotifyFunction = NotifyFunction;
150
151 return EFI_SUCCESS;
152}
153
026e30c4 154/**
155
156 This function adjusts the period of timer interrupts to the value specified
026e30c4 157 by TimerPeriod. If the timer period is updated, then the selected timer
026e30c4 158 period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If
026e30c4 159 the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
026e30c4 160 If an error occurs while attempting to update the timer period, then the
026e30c4 161 timer hardware will be put back in its state prior to this call, and
026e30c4 162 EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt
026e30c4 163 is disabled. This is not the same as disabling the CPU's interrupts.
026e30c4 164 Instead, it must either turn off the timer hardware, or it must adjust the
026e30c4 165 interrupt controller so that a CPU interrupt is not generated when the timer
026e30c4 166 interrupt fires.
167
026e30c4 168 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
026e30c4 169 @param TimerPeriod The rate to program the timer interrupt in 100 nS units. If
026e30c4 170 the timer hardware is not programmable, then EFI_UNSUPPORTED is
026e30c4 171 returned. If the timer is programmable, then the timer period
026e30c4 172 will be rounded up to the nearest timer period that is supported
026e30c4 173 by the timer hardware. If TimerPeriod is set to 0, then the
026e30c4 174 timer interrupts will be disabled.
175
176
026e30c4 177 @retval EFI_SUCCESS The timer period was changed.
026e30c4 178 @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt.
026e30c4 179 @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error.
180
a3f98646 181**/
182EFI_STATUS
183EFIAPI
184TimerDriverSetTimerPeriod (
185 IN EFI_TIMER_ARCH_PROTOCOL *This,
186 IN UINT64 TimerPeriod
187 )
188{
189 EFI_STATUS Status;
190 UINT64 TimerCount;
191 INT32 LoadValue;
192
193 if (TimerPeriod == 0) {
194 // Turn off GPTIMER3
026e30c4 195 MmioWrite32 (TCLR, TCLR_ST_OFF);
a3f98646 196
197 Status = gInterrupt->DisableInterruptSource(gInterrupt, gVector);
198 } else {
199 // Calculate required timer count
200 TimerCount = DivU64x32(TimerPeriod * 100, PcdGet32(PcdEmbeddedFdPerformanceCounterPeriodInNanoseconds));
201
202 // Set GPTIMER3 Load register
203 LoadValue = (INT32) -TimerCount;
026e30c4 204 MmioWrite32 (TLDR, LoadValue);
205 MmioWrite32 (TCRR, LoadValue);
a3f98646 206
207 // Enable Overflow interrupt
026e30c4 208 MmioWrite32 (TIER, TIER_TCAR_IT_DISABLE | TIER_OVF_IT_ENABLE | TIER_MAT_IT_DISABLE);
a3f98646 209
210 // Turn on GPTIMER3, it will reload at overflow
026e30c4 211 MmioWrite32 (TCLR, TCLR_AR_AUTORELOAD | TCLR_ST_ON);
a3f98646 212
213 Status = gInterrupt->EnableInterruptSource(gInterrupt, gVector);
214 }
215
216 //
217 // Save the new timer period
218 //
219 mTimerPeriod = TimerPeriod;
220 return Status;
221}
222
223
026e30c4 224/**
026e30c4 225 This function retrieves the period of timer interrupts in 100 ns units,
026e30c4 226 returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod
026e30c4 227 is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is
026e30c4 228 returned, then the timer is currently disabled.
229
026e30c4 230 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
026e30c4 231 @param TimerPeriod A pointer to the timer period to retrieve in 100 ns units. If
026e30c4 232 0 is returned, then the timer is currently disabled.
233
234
026e30c4 235 @retval EFI_SUCCESS The timer period was returned in TimerPeriod.
026e30c4 236 @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.
237
a3f98646 238**/
239EFI_STATUS
240EFIAPI
241TimerDriverGetTimerPeriod (
242 IN EFI_TIMER_ARCH_PROTOCOL *This,
243 OUT UINT64 *TimerPeriod
244 )
245{
246 if (TimerPeriod == NULL) {
247 return EFI_INVALID_PARAMETER;
248 }
249
250 *TimerPeriod = mTimerPeriod;
251 return EFI_SUCCESS;
252}
253
026e30c4 254/**
026e30c4 255 This function generates a soft timer interrupt. If the platform does not support soft
026e30c4 256 timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
026e30c4 257 If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
026e30c4 258 service, then a soft timer interrupt will be generated. If the timer interrupt is
026e30c4 259 enabled when this service is called, then the registered handler will be invoked. The
026e30c4 260 registered handler should not be able to distinguish a hardware-generated timer
026e30c4 261 interrupt from a software-generated timer interrupt.
262
026e30c4 263 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
264
026e30c4 265 @retval EFI_SUCCESS The soft timer interrupt was generated.
026e30c4 266 @retval EFI_UNSUPPORTED The platform does not support the generation of soft timer interrupts.
267
a3f98646 268**/
269EFI_STATUS
270EFIAPI
271TimerDriverGenerateSoftInterrupt (
272 IN EFI_TIMER_ARCH_PROTOCOL *This
273 )
274{
275 return EFI_UNSUPPORTED;
276}
277
278
279EFI_STATUS
280EFIAPI
281TimerDriverRegisterPeriodicCallback (
282 IN TIMER_DEBUG_SUPPORT_PROTOCOL *This,
283 IN EFI_PERIODIC_CALLBACK PeriodicCallback
284 )
285{
286 if ((PeriodicCallback == NULL) && (mTimerPeriodicCallback == NULL)) {
287 return EFI_INVALID_PARAMETER;
288 }
289
290 if ((PeriodicCallback != NULL) && (mTimerPeriodicCallback != NULL)) {
291 return EFI_ALREADY_STARTED;
292 }
293
294 mTimerPeriodicCallback = PeriodicCallback;
295
296 return EFI_SUCCESS;
297}
298
299
026e30c4 300/**
026e30c4 301 Interface stucture for the Timer Architectural Protocol.
302
026e30c4 303 @par Protocol Description:
026e30c4 304 This protocol provides the services to initialize a periodic timer
026e30c4 305 interrupt, and to register a handler that is called each time the timer
026e30c4 306 interrupt fires. It may also provide a service to adjust the rate of the
026e30c4 307 periodic timer interrupt. When a timer interrupt occurs, the handler is
026e30c4 308 passed the amount of time that has passed since the previous timer
026e30c4 309 interrupt.
310
026e30c4 311 @param RegisterHandler
026e30c4 312 Registers a handler that will be called each time the
026e30c4 313 timer interrupt fires. TimerPeriod defines the minimum
026e30c4 314 time between timer interrupts, so TimerPeriod will also
026e30c4 315 be the minimum time between calls to the registered
026e30c4 316 handler.
317
026e30c4 318 @param SetTimerPeriod
026e30c4 319 Sets the period of the timer interrupt in 100 nS units.
026e30c4 320 This function is optional, and may return EFI_UNSUPPORTED.
026e30c4 321 If this function is supported, then the timer period will
026e30c4 322 be rounded up to the nearest supported timer period.
323
324
026e30c4 325 @param GetTimerPeriod
026e30c4 326 Retrieves the period of the timer interrupt in 100 nS units.
327
026e30c4 328 @param GenerateSoftInterrupt
026e30c4 329 Generates a soft timer interrupt that simulates the firing of
026c3d34 330 the timer interrupt. This service can be used to invoke the registered handler if the timer interrupt has been masked for
026e30c4 331 a period of time.
332
a3f98646 333**/
334EFI_TIMER_ARCH_PROTOCOL gTimer = {
335 TimerDriverRegisterHandler,
336 TimerDriverSetTimerPeriod,
337 TimerDriverGetTimerPeriod,
338 TimerDriverGenerateSoftInterrupt
339};
340
341TIMER_DEBUG_SUPPORT_PROTOCOL gTimerDebugSupport = {
342 TimerDriverRegisterPeriodicCallback
343};
344
345
026e30c4 346/**
026e30c4 347 Initialize the state information for the Timer Architectural Protocol and
026e30c4 348 the Timer Debug support protocol that allows the debugger to break into a
026e30c4 349 running program.
350
026e30c4 351 @param ImageHandle of the loaded driver
026e30c4 352 @param SystemTable Pointer to the System Table
353
026e30c4 354 @retval EFI_SUCCESS Protocol registered
026e30c4 355 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
026e30c4 356 @retval EFI_DEVICE_ERROR Hardware problems
357
a3f98646 358**/
359EFI_STATUS
360EFIAPI
361TimerInitialize (
362 IN EFI_HANDLE ImageHandle,
363 IN EFI_SYSTEM_TABLE *SystemTable
364 )
365{
366 EFI_HANDLE Handle = NULL;
367 EFI_STATUS Status;
368 UINT32 TimerBaseAddress;
369
370 // Find the interrupt controller protocol. ASSERT if not found.
371 Status = gBS->LocateProtocol(&gHardwareInterruptProtocolGuid, NULL, (VOID **)&gInterrupt);
372 ASSERT_EFI_ERROR (Status);
373
374 // Set up the timer registers
43263288 375 TimerBaseAddress = TimerBase(FixedPcdGet32(PcdOmap35xxArchTimer));
a3f98646 376 TISR = TimerBaseAddress + GPTIMER_TISR;
377 TCLR = TimerBaseAddress + GPTIMER_TCLR;
378 TLDR = TimerBaseAddress + GPTIMER_TLDR;
379 TCRR = TimerBaseAddress + GPTIMER_TCRR;
380 TIER = TimerBaseAddress + GPTIMER_TIER;
381
382 // Disable the timer
383 Status = TimerDriverSetTimerPeriod(&gTimer, 0);
384 ASSERT_EFI_ERROR (Status);
385
386 // Install interrupt handler
43263288 387 gVector = InterruptVectorForTimer(FixedPcdGet32(PcdOmap35xxArchTimer));
a3f98646 388 Status = gInterrupt->RegisterInterruptSource(gInterrupt, gVector, TimerInterruptHandler);
389 ASSERT_EFI_ERROR (Status);
390
391 // Set up default timer
392 Status = TimerDriverSetTimerPeriod(&gTimer, FixedPcdGet32(PcdTimerPeriod));
393 ASSERT_EFI_ERROR (Status);
394
395 // Install the Timer Architectural Protocol onto a new handle
396 Status = gBS->InstallMultipleProtocolInterfaces(&Handle,
397 &gEfiTimerArchProtocolGuid, &gTimer,
398 &gTimerDebugSupportProtocolGuid, &gTimerDebugSupport,
399 NULL);
400 ASSERT_EFI_ERROR(Status);
401
402 return Status;
403}
404