refine the DataHubRecords.h to comply with DataHubSubclass/MemSubclass/ProcSubclass...
[edk2.git] / PcAtChipsetPkg / 8254TimerDxe / Timer.c
blob2874301bbcae60e0acd630fc6091a665b6e55952
1 /*++
3 Copyright (c) 2005 - 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 Module Name:
15 Timer.c
17 Abstract:
19 Timer Architectural Protocol as defined in the DXE CIS
21 --*/
23 #include "Timer.h"
26 // The handle onto which the Timer Architectural Protocol will be installed
28 EFI_HANDLE mTimerHandle = NULL;
31 // The Timer Architectural Protocol that this driver produces
33 EFI_TIMER_ARCH_PROTOCOL mTimer = {
34 TimerDriverRegisterHandler,
35 TimerDriverSetTimerPeriod,
36 TimerDriverGetTimerPeriod,
37 TimerDriverGenerateSoftInterrupt
41 // Pointer to the CPU Architectural Protocol instance
43 EFI_CPU_ARCH_PROTOCOL *mCpu;
46 // Pointer to the Legacy 8259 Protocol instance
48 EFI_LEGACY_8259_PROTOCOL *mLegacy8259;
51 // The notification function to call on every timer interrupt.
52 // A bug in the compiler prevents us from initializing this here.
54 EFI_TIMER_NOTIFY mTimerNotifyFunction;
57 // The current period of the timer interrupt
59 volatile UINT64 mTimerPeriod = 0;
62 // Worker Functions
64 VOID
65 SetPitCount (
66 IN UINT16 Count
68 /*++
70 Routine Description:
72 Sets the counter value for Timer #0 in a legacy 8254 timer.
74 Arguments:
76 Count - The 16-bit counter value to program into Timer #0 of the legacy 8254 timer.
78 Returns:
80 None
82 --*/
84 IoWrite8 (TIMER_CONTROL_PORT, 0x36);
85 IoWrite8 (TIMER0_COUNT_PORT, (UINT8)(Count & 0xff));
86 IoWrite8 (TIMER0_COUNT_PORT, (UINT8)((Count >> 8) & 0xff));
89 VOID
90 EFIAPI
91 TimerInterruptHandler (
92 IN EFI_EXCEPTION_TYPE InterruptType,
93 IN EFI_SYSTEM_CONTEXT SystemContext
95 /*++
97 Routine Description:
99 8254 Timer #0 Interrupt Handler
101 Arguments:
103 InterruptType - The type of interrupt that occured
105 SystemContext - A pointer to the system context when the interrupt occured
107 Returns:
109 None
111 --*/
113 EFI_TPL OriginalTPL;
115 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
117 mLegacy8259->EndOfInterrupt (mLegacy8259, Efi8259Irq0);
119 if (mTimerNotifyFunction) {
121 // BUGBUG : This does not handle missed timer interrupts
123 mTimerNotifyFunction (mTimerPeriod);
126 gBS->RestoreTPL (OriginalTPL);
129 EFI_STATUS
130 EFIAPI
131 TimerDriverRegisterHandler (
132 IN EFI_TIMER_ARCH_PROTOCOL *This,
133 IN EFI_TIMER_NOTIFY NotifyFunction
135 /*++
137 Routine Description:
139 This function registers the handler NotifyFunction so it is called every time
140 the timer interrupt fires. It also passes the amount of time since the last
141 handler call to the NotifyFunction. If NotifyFunction is NULL, then the
142 handler is unregistered. If the handler is registered, then EFI_SUCCESS is
143 returned. If the CPU does not support registering a timer interrupt handler,
144 then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler
145 when a handler is already registered, then EFI_ALREADY_STARTED is returned.
146 If an attempt is made to unregister a handler when a handler is not registered,
147 then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to
148 register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR
149 is returned.
151 Arguments:
153 This - The EFI_TIMER_ARCH_PROTOCOL instance.
155 NotifyFunction - The function to call when a timer interrupt fires. This
156 function executes at TPL_HIGH_LEVEL. The DXE Core will
157 register a handler for the timer interrupt, so it can know
158 how much time has passed. This information is used to
159 signal timer based events. NULL will unregister the handler.
161 Returns:
163 EFI_SUCCESS - The timer handler was registered.
165 EFI_UNSUPPORTED - The platform does not support timer interrupts.
167 EFI_ALREADY_STARTED - NotifyFunction is not NULL, and a handler is already
168 registered.
170 EFI_INVALID_PARAMETER - NotifyFunction is NULL, and a handler was not
171 previously registered.
173 EFI_DEVICE_ERROR - The timer handler could not be registered.
175 --*/
178 // Check for invalid parameters
180 if (NotifyFunction == NULL && mTimerNotifyFunction == NULL) {
181 return EFI_INVALID_PARAMETER;
184 if (NotifyFunction != NULL && mTimerNotifyFunction != NULL) {
185 return EFI_ALREADY_STARTED;
188 mTimerNotifyFunction = NotifyFunction;
190 return EFI_SUCCESS;
193 EFI_STATUS
194 EFIAPI
195 TimerDriverSetTimerPeriod (
196 IN EFI_TIMER_ARCH_PROTOCOL *This,
197 IN UINT64 TimerPeriod
199 /*++
201 Routine Description:
203 This function adjusts the period of timer interrupts to the value specified
204 by TimerPeriod. If the timer period is updated, then the selected timer
205 period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If
206 the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
207 If an error occurs while attempting to update the timer period, then the
208 timer hardware will be put back in its state prior to this call, and
209 EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt
210 is disabled. This is not the same as disabling the CPU's interrupts.
211 Instead, it must either turn off the timer hardware, or it must adjust the
212 interrupt controller so that a CPU interrupt is not generated when the timer
213 interrupt fires.
215 Arguments:
217 This - The EFI_TIMER_ARCH_PROTOCOL instance.
219 TimerPeriod - The rate to program the timer interrupt in 100 nS units. If
220 the timer hardware is not programmable, then EFI_UNSUPPORTED is
221 returned. If the timer is programmable, then the timer period
222 will be rounded up to the nearest timer period that is supported
223 by the timer hardware. If TimerPeriod is set to 0, then the
224 timer interrupts will be disabled.
226 Returns:
228 EFI_SUCCESS - The timer period was changed.
230 EFI_UNSUPPORTED - The platform cannot change the period of the timer interrupt.
232 EFI_DEVICE_ERROR - The timer period could not be changed due to a device error.
234 --*/
236 UINT64 TimerCount;
239 // The basic clock is 1.19318 MHz or 0.119318 ticks per 100 ns.
240 // TimerPeriod * 0.119318 = 8254 timer divisor. Using integer arithmetic
241 // TimerCount = (TimerPeriod * 119318)/1000000.
243 // Round up to next highest integer. This guarantees that the timer is
244 // equal to or slightly longer than the requested time.
245 // TimerCount = ((TimerPeriod * 119318) + 500000)/1000000
247 // Note that a TimerCount of 0 is equivalent to a count of 65,536
249 // Since TimerCount is limited to 16 bits for IA32, TimerPeriod is limited
250 // to 20 bits.
252 if (TimerPeriod == 0) {
254 // Disable timer interrupt for a TimerPeriod of 0
256 mLegacy8259->DisableIrq (mLegacy8259, Efi8259Irq0);
257 } else {
260 // Convert TimerPeriod into 8254 counts
262 TimerCount = DivU64x32 (MultU64x32 (119318, (UINT32) TimerPeriod) + 500000, 1000000);
265 // Check for overflow
267 if (TimerCount >= 65536) {
268 TimerCount = 0;
269 if (TimerPeriod >= DEFAULT_TIMER_TICK_DURATION) {
270 TimerPeriod = DEFAULT_TIMER_TICK_DURATION;
274 // Program the 8254 timer with the new count value
276 SetPitCount ((UINT16) TimerCount);
279 // Enable timer interrupt
281 mLegacy8259->EnableIrq (mLegacy8259, Efi8259Irq0, FALSE);
284 // Save the new timer period
286 mTimerPeriod = TimerPeriod;
288 return EFI_SUCCESS;
291 EFI_STATUS
292 EFIAPI
293 TimerDriverGetTimerPeriod (
294 IN EFI_TIMER_ARCH_PROTOCOL *This,
295 OUT UINT64 *TimerPeriod
297 /*++
299 Routine Description:
301 This function retrieves the period of timer interrupts in 100 ns units,
302 returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod
303 is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is
304 returned, then the timer is currently disabled.
306 Arguments:
308 This - The EFI_TIMER_ARCH_PROTOCOL instance.
310 TimerPeriod - A pointer to the timer period to retrieve in 100 ns units. If
311 0 is returned, then the timer is currently disabled.
313 Returns:
315 EFI_SUCCESS - The timer period was returned in TimerPeriod.
317 EFI_INVALID_PARAMETER - TimerPeriod is NULL.
319 --*/
321 if (TimerPeriod == NULL) {
322 return EFI_INVALID_PARAMETER;
325 *TimerPeriod = mTimerPeriod;
327 return EFI_SUCCESS;
330 EFI_STATUS
331 EFIAPI
332 TimerDriverGenerateSoftInterrupt (
333 IN EFI_TIMER_ARCH_PROTOCOL *This
335 /*++
337 Routine Description:
339 This function generates a soft timer interrupt. If the platform does not support soft
340 timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
341 If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
342 service, then a soft timer interrupt will be generated. If the timer interrupt is
343 enabled when this service is called, then the registered handler will be invoked. The
344 registered handler should not be able to distinguish a hardware-generated timer
345 interrupt from a software-generated timer interrupt.
347 Arguments:
349 This - The EFI_TIMER_ARCH_PROTOCOL instance.
351 Returns:
353 EFI_SUCCESS - The soft timer interrupt was generated.
355 EFI_UNSUPPORTEDT - The platform does not support the generation of soft timer interrupts.
357 --*/
359 EFI_STATUS Status;
360 UINT16 IRQMask;
361 EFI_TPL OriginalTPL;
364 // If the timer interrupt is enabled, then the registered handler will be invoked.
366 Status = mLegacy8259->GetMask (mLegacy8259, NULL, NULL, &IRQMask, NULL);
367 ASSERT_EFI_ERROR (Status);
368 if ((IRQMask & 0x1) == 0) {
370 // Invoke the registered handler
372 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
374 if (mTimerNotifyFunction) {
376 // BUGBUG : This does not handle missed timer interrupts
378 mTimerNotifyFunction (mTimerPeriod);
381 gBS->RestoreTPL (OriginalTPL);
382 } else {
383 return EFI_UNSUPPORTED;
386 return EFI_SUCCESS;
389 EFI_STATUS
390 EFIAPI
391 TimerDriverInitialize (
392 IN EFI_HANDLE ImageHandle,
393 IN EFI_SYSTEM_TABLE *SystemTable
395 /*++
397 Routine Description:
399 Initialize the Timer Architectural Protocol driver
401 Arguments:
403 ImageHandle - ImageHandle of the loaded driver
405 SystemTable - Pointer to the System Table
407 Returns:
409 EFI_SUCCESS - Timer Architectural Protocol created
411 EFI_OUT_OF_RESOURCES - Not enough resources available to initialize driver.
413 EFI_DEVICE_ERROR - A device error occured attempting to initialize the driver.
415 --*/
417 EFI_STATUS Status;
418 UINT32 TimerVector;
421 // Initialize the pointer to our notify function.
423 mTimerNotifyFunction = NULL;
426 // Make sure the Timer Architectural Protocol is not already installed in the system
428 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);
431 // Find the CPU architectural protocol.
433 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &mCpu);
434 ASSERT_EFI_ERROR (Status);
437 // Find the Legacy8259 protocol.
439 Status = gBS->LocateProtocol (&gEfiLegacy8259ProtocolGuid, NULL, (VOID **) &mLegacy8259);
440 ASSERT_EFI_ERROR (Status);
443 // Force the timer to be disabled
445 Status = TimerDriverSetTimerPeriod (&mTimer, 0);
446 ASSERT_EFI_ERROR (Status);
449 // Get the interrupt vector number corresponding to IRQ0 from the 8259 driver
451 TimerVector = 0;
452 Status = mLegacy8259->GetVector (mLegacy8259, Efi8259Irq0, (UINT8 *) &TimerVector);
453 ASSERT_EFI_ERROR (Status);
456 // Install interrupt handler for 8254 Timer #0 (ISA IRQ0)
458 Status = mCpu->RegisterInterruptHandler (mCpu, TimerVector, TimerInterruptHandler);
459 ASSERT_EFI_ERROR (Status);
462 // Force the timer to be enabled at its default period
464 Status = TimerDriverSetTimerPeriod (&mTimer, DEFAULT_TIMER_TICK_DURATION);
465 ASSERT_EFI_ERROR (Status);
468 // Install the Timer Architectural Protocol onto a new handle
470 Status = gBS->InstallMultipleProtocolInterfaces (
471 &mTimerHandle,
472 &gEfiTimerArchProtocolGuid, &mTimer,
473 NULL
475 ASSERT_EFI_ERROR (Status);
477 return Status;