2 File to contain all the hardware specific stuff for the Periodical Timer dispatch protocol.
4 Copyright (c) 2013-2015 Intel Corporation.
6 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
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.
18 // Include common header file for this module.
20 #include "CommonHeader.h"
22 #include "QNCSmmHelpers.h"
29 typedef struct _TIMER_INTERVAL
32 UINT8 AssociatedTimer
;
36 // Time constants, in 100 nano-second units
38 #define TIME_64s 640000000 /* 64 s */
39 #define TIME_32s 320000000 /* 32 s */
40 #define TIME_16s 160000000 /* 16 s */
41 #define TIME_8s 80000000 /* 8 s */
42 #define TIME_64ms 640000 /* 64 ms */
43 #define TIME_32ms 320000 /* 32 ms */
44 #define TIME_16ms 160000 /* 16 ms */
45 #define TIME_1_5ms 15000 /* 1.5 ms */
47 // PMCW (GPE+28h) [2:0] Periodic SMI Rate selection
67 } TIMER_INTERVAL_INDEX
;
69 TIMER_INTERVAL mSmmPeriodicTimerIntervals
[INDEX_TIME_MAX
] = {
70 {TIME_1_5ms
, PERIODIC_TIMER
},
71 {TIME_16ms
, PERIODIC_TIMER
},
72 {TIME_32ms
, PERIODIC_TIMER
},
73 {TIME_64ms
, PERIODIC_TIMER
},
74 { TIME_8s
, PERIODIC_TIMER
},
75 {TIME_16s
, PERIODIC_TIMER
},
76 {TIME_32s
, PERIODIC_TIMER
},
77 {TIME_64s
, PERIODIC_TIMER
}
80 typedef struct _TIMER_INFO
{
81 UINTN NumChildren
; // number of children using this timer
82 UINT64 MinReqInterval
; // minimum interval required by children
83 UINTN CurrentSetting
; // interval this timer is set at right now (index into interval table)
86 TIMER_INFO mTimers
[NUM_TIMERS
];
88 QNC_SMM_SOURCE_DESC mTIMER_SOURCE_DESCS
[NUM_TIMERS
] = {
92 {{GPE_ADDR_TYPE
, {R_QNC_GPE0BLK_SMIE
}}, S_QNC_GPE0BLK_SMIE
, N_QNC_GPE0BLK_SMIE_SWT
},
93 NULL_BIT_DESC_INITIALIZER
96 {{GPE_ADDR_TYPE
, {R_QNC_GPE0BLK_SMIS
}}, S_QNC_GPE0BLK_SMIS
, N_QNC_GPE0BLK_SMIS_SWT
}
102 QNCSmmPeriodicTimerProgramTimers(
108 ContextToTimerInterval (
109 IN QNC_SMM_CONTEXT
*RegisterContext
115 // Determine which timer this child is using
117 for (loopvar
= 0; loopvar
< INDEX_TIME_MAX
; loopvar
++) {
118 if (((RegisterContext
->PeriodicTimer
.SmiTickInterval
== 0) && (RegisterContext
->PeriodicTimer
.Period
>= mSmmPeriodicTimerIntervals
[loopvar
].Interval
)) ||
119 (RegisterContext
->PeriodicTimer
.SmiTickInterval
== mSmmPeriodicTimerIntervals
[loopvar
].Interval
)
121 return &mSmmPeriodicTimerIntervals
[loopvar
];
126 // If this assertion fires, then either:
127 // (1) the context contains an invalid interval
128 // (2) the timer interval table is corrupt
136 MapPeriodicTimerToSrcDesc (
137 IN QNC_SMM_CONTEXT
*RegisterContext
,
138 OUT QNC_SMM_SOURCE_DESC
*SrcDesc
141 TIMER_INTERVAL
*TimerInterval
;
144 // Figure out which timer the child is requesting and
145 // send back the source description
147 TimerInterval
= ContextToTimerInterval (RegisterContext
);
148 if (TimerInterval
== NULL
) {
149 return EFI_INVALID_PARAMETER
;
151 CopyMem (SrcDesc
, &mTIMER_SOURCE_DESCS
[TimerInterval
->AssociatedTimer
], sizeof (QNC_SMM_SOURCE_DESC
));;
154 // Program the value of the interval into hardware
156 QNCSmmPeriodicTimerProgramTimers ();
162 PeriodicTimerGetContext (
163 IN DATABASE_RECORD
*Record
,
164 OUT QNC_SMM_CONTEXT
*HwContext
167 TIMER_INTERVAL
*TimerInterval
;
169 ASSERT (Record
->ProtocolType
== PeriodicTimerType
);
171 TimerInterval
= ContextToTimerInterval (&Record
->ChildContext
);
173 if (TimerInterval
!= NULL
) {
175 // Ignore the hardware context. It's not required for this protocol.
176 // Instead, just increment the child's context.
177 // Update the elapsed time w/ the data from our tables
179 Record
->CommBuffer
.PeriodicTimer
.ElapsedTime
+= TimerInterval
->Interval
;
180 *HwContext
= Record
->ChildContext
;
185 PeriodicTimerCmpContext (
186 IN QNC_SMM_CONTEXT
*HwContext
,
187 IN QNC_SMM_CONTEXT
*ChildContext
190 DATABASE_RECORD
*Record
;
192 Record
= DATABASE_RECORD_FROM_CONTEXT (ChildContext
);
194 if (Record
->CommBuffer
.PeriodicTimer
.ElapsedTime
>= ChildContext
->PeriodicTimer
.Period
) {
196 // This child should be dispatched
197 // The timer will be restarted on the "ClearSource" call.
206 PeriodicTimerGetBuffer (
207 IN DATABASE_RECORD
* Record
211 // CommBuffer has been updated by PeriodicTimerGetContext, so return directly
217 QNCSmmPeriodicTimerProgramTimers (
222 SUPPORTED_TIMER Timer
;
223 DATABASE_RECORD
*RecordInDb
;
224 LIST_ENTRY
*LinkInDb
;
225 TIMER_INTERVAL
*TimerInterval
;
228 // Find the minimum required interval for each timer
230 for (Timer
= (SUPPORTED_TIMER
)0; Timer
< NUM_TIMERS
; Timer
++) {
231 mTimers
[Timer
].MinReqInterval
= ~(UINT64
)0x0;
232 mTimers
[Timer
].NumChildren
= 0;
234 LinkInDb
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
235 while (!IsNull (&mPrivateData
.CallbackDataBase
, LinkInDb
)) {
236 RecordInDb
= DATABASE_RECORD_FROM_LINK (LinkInDb
);
237 if (RecordInDb
->ProtocolType
== PeriodicTimerType
) {
239 // This child is registerd with the PeriodicTimer protocol
241 TimerInterval
= ContextToTimerInterval (&RecordInDb
->ChildContext
);
243 if(TimerInterval
!= NULL
) {
244 Timer
= (SUPPORTED_TIMER
)((TIMER_INTERVAL
*) (TimerInterval
))->AssociatedTimer
;
246 ASSERT (Timer
>= 0 && Timer
< NUM_TIMERS
);
248 if (mTimers
[Timer
].MinReqInterval
> RecordInDb
->ChildContext
.PeriodicTimer
.SmiTickInterval
) {
249 mTimers
[Timer
].MinReqInterval
= RecordInDb
->ChildContext
.PeriodicTimer
.SmiTickInterval
;
251 mTimers
[Timer
].NumChildren
++;
254 LinkInDb
= GetNextNode (&mPrivateData
.CallbackDataBase
, &RecordInDb
->Link
);
258 // Program the hardware
261 if (mTimers
[PERIODIC_TIMER
].NumChildren
> 0) {
262 switch (mTimers
[PERIODIC_TIMER
].MinReqInterval
) {
265 GpePmcwValue
= INDEX_TIME_64s
;
266 mTimers
[PERIODIC_TIMER
].CurrentSetting
= INDEX_TIME_64s
;
270 GpePmcwValue
= INDEX_TIME_32s
;
271 mTimers
[PERIODIC_TIMER
].CurrentSetting
= INDEX_TIME_32s
;
275 GpePmcwValue
= INDEX_TIME_16s
;
276 mTimers
[PERIODIC_TIMER
].CurrentSetting
= INDEX_TIME_16s
;
280 GpePmcwValue
= INDEX_TIME_8s
;
281 mTimers
[PERIODIC_TIMER
].CurrentSetting
= INDEX_TIME_8s
;
285 GpePmcwValue
= INDEX_TIME_64ms
;
286 mTimers
[PERIODIC_TIMER
].CurrentSetting
= INDEX_TIME_64ms
;
290 GpePmcwValue
= INDEX_TIME_32ms
;
291 mTimers
[PERIODIC_TIMER
].CurrentSetting
= INDEX_TIME_32ms
;
295 GpePmcwValue
= INDEX_TIME_16ms
;
296 mTimers
[PERIODIC_TIMER
].CurrentSetting
= INDEX_TIME_16ms
;
300 GpePmcwValue
= INDEX_TIME_1_5ms
;
301 mTimers
[PERIODIC_TIMER
].CurrentSetting
= INDEX_TIME_1_5ms
;
309 GpePmcwValue
|= B_QNC_GPE0BLK_PMCW_PSE
;
311 IoOr32(((UINT16
)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK
) & 0xFFFF) + R_QNC_GPE0BLK_PMCW
), GpePmcwValue
);
314 // Restart the timer here, just need to clear the SMI
316 QNCSmmClearSource (&mTIMER_SOURCE_DESCS
[PERIODIC_TIMER
]);
318 QNCSmmDisableSource (&mTIMER_SOURCE_DESCS
[PERIODIC_TIMER
]);
323 QNCSmmPeriodicTimerDispatchGetNextShorterInterval (
324 IN CONST EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL
*This
,
325 IN OUT UINT64
**SmiTickInterval
331 This services returns the next SMI tick period that is supported by the chipset.
332 The order returned is from longest to shortest interval period.
336 This - Pointer to the EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL instance.
337 SmiTickInterval - Pointer to pointer of the next shorter SMI interval period that is supported by the child.
341 EFI_SUCCESS - The service returned successfully.
342 EFI_INVALID_PARAMETER - The parameter SmiTickInterval is invalid.
346 TIMER_INTERVAL
*IntervalPointer
;
348 ASSERT (SmiTickInterval
!= NULL
);
350 IntervalPointer
= (TIMER_INTERVAL
*)*SmiTickInterval
;
352 if (IntervalPointer
== NULL
) {
354 // The first time child requesting an interval
356 IntervalPointer
= &mSmmPeriodicTimerIntervals
[0];
357 } else if (IntervalPointer
== &mSmmPeriodicTimerIntervals
[INDEX_TIME_MAX
- 1]) {
359 // At end of the list
361 IntervalPointer
= NULL
;
363 if ((IntervalPointer
>= &mSmmPeriodicTimerIntervals
[0]) &&
364 (IntervalPointer
< &mSmmPeriodicTimerIntervals
[INDEX_TIME_MAX
- 1])) {
366 // Get the next interval in the list
371 // Input is out of range
373 return EFI_INVALID_PARAMETER
;
377 if (IntervalPointer
!= NULL
) {
378 *SmiTickInterval
= &IntervalPointer
->Interval
;
380 *SmiTickInterval
= NULL
;
387 QNCSmmPeriodicTimerClearSource (
388 IN QNC_SMM_SOURCE_DESC
*SrcDesc
394 This function is responsible for calculating and enabling any timers that are required
395 to dispatch messages to children. The SrcDesc argument isn't acutally used.
399 SrcDesc - Pointer to the QNC_SMM_SOURCE_DESC instance.
407 DATABASE_RECORD
*RecordInDb
;
408 LIST_ENTRY
*LinkInDb
;
410 QNCSmmPeriodicTimerProgramTimers ();
413 // Reset Elapsed time
415 LinkInDb
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
416 while (!IsNull (&mPrivateData
.CallbackDataBase
, LinkInDb
)) {
417 RecordInDb
= DATABASE_RECORD_FROM_LINK (LinkInDb
);
418 if (RecordInDb
->ProtocolType
== PeriodicTimerType
) {
420 // This child is registerd with the PeriodicTimer protocol and Callback
421 // has been invoked, so reset the ElapsedTime to 0
423 if (RecordInDb
->CommBuffer
.PeriodicTimer
.ElapsedTime
>= RecordInDb
->ChildContext
.PeriodicTimer
.Period
) {
424 RecordInDb
->CommBuffer
.PeriodicTimer
.ElapsedTime
= 0;
427 LinkInDb
= GetNextNode (&mPrivateData
.CallbackDataBase
, &RecordInDb
->Link
);