1 /** @file
2 File to contain all the hardware specific stuff for the Periodical Timer dispatch protocol.
3
4 Copyright (c) 2013-2016 Intel Corporation.
5
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
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 //
18 // Include common header file for this module.
19 //
20 #include "CommonHeader.h"
21
22 #include "QNCSmmHelpers.h"
23
24 typedef enum {
25 PERIODIC_TIMER = 0,
26 NUM_TIMERS
27 } SUPPORTED_TIMER;
28
29 typedef struct _TIMER_INTERVAL
30 {
31 UINT64 Interval;
32 UINT8 AssociatedTimer;
33 } TIMER_INTERVAL;
34
35 //
36 // Time constants, in 100 nano-second units
37 //
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 */
46
47 // PMCW (GPE+28h) [2:0] Periodic SMI Rate selection
48 // 000 1.5ms
49 // 001 16ms
50 // 010 32ms
51 // 011 64ms
52 // 100 8s
53 // 101 16s
54 // 110 32s
55 // 111 64s
56
57 typedef enum {
58 INDEX_TIME_1_5ms = 0,
59 INDEX_TIME_16ms,
60 INDEX_TIME_32ms,
61 INDEX_TIME_64ms,
62 INDEX_TIME_8s,
63 INDEX_TIME_16s,
64 INDEX_TIME_32s,
65 INDEX_TIME_64s,
66 INDEX_TIME_MAX
67 } TIMER_INTERVAL_INDEX;
68
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}
78 };
79
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)
84 } TIMER_INFO;
85
86 TIMER_INFO mTimers[NUM_TIMERS];
87
88 QNC_SMM_SOURCE_DESC mTIMER_SOURCE_DESCS[NUM_TIMERS] = {
89 {
90 QNC_SMM_NO_FLAGS,
91 {
92 {{GPE_ADDR_TYPE, {R_QNC_GPE0BLK_SMIE}}, S_QNC_GPE0BLK_SMIE, N_QNC_GPE0BLK_SMIE_SWT},
93 NULL_BIT_DESC_INITIALIZER
94 },
95 {
96 {{GPE_ADDR_TYPE, {R_QNC_GPE0BLK_SMIS}}, S_QNC_GPE0BLK_SMIS, N_QNC_GPE0BLK_SMIS_SWT}
97 }
98 }
99 };
100
101 VOID
102 QNCSmmPeriodicTimerProgramTimers(
103 VOID
104 );
105
106
107 TIMER_INTERVAL *
ContextToTimerInterval(IN QNC_SMM_CONTEXT * RegisterContext)108 ContextToTimerInterval (
109 IN QNC_SMM_CONTEXT *RegisterContext
110 )
111 {
112 UINTN loopvar;
113
114 //
115 // Determine which timer this child is using
116 //
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)
120 ) {
121 return &mSmmPeriodicTimerIntervals[loopvar];
122 }
123 }
124
125 //
126 // If this assertion fires, then either:
127 // (1) the context contains an invalid interval
128 // (2) the timer interval table is corrupt
129 //
130 // ASSERT (FALSE);
131
132 return NULL;
133 }
134
135 EFI_STATUS
MapPeriodicTimerToSrcDesc(IN QNC_SMM_CONTEXT * RegisterContext,OUT QNC_SMM_SOURCE_DESC * SrcDesc)136 MapPeriodicTimerToSrcDesc (
137 IN QNC_SMM_CONTEXT *RegisterContext,
138 OUT QNC_SMM_SOURCE_DESC *SrcDesc
139 )
140 {
141 TIMER_INTERVAL *TimerInterval;
142
143 //
144 // Figure out which timer the child is requesting and
145 // send back the source description
146 //
147 TimerInterval = ContextToTimerInterval (RegisterContext);
148 if (TimerInterval == NULL) {
149 return EFI_INVALID_PARAMETER;
150 }
151 CopyMem (SrcDesc, &mTIMER_SOURCE_DESCS[TimerInterval->AssociatedTimer], sizeof (QNC_SMM_SOURCE_DESC));;
152
153 //
154 // Program the value of the interval into hardware
155 //
156 QNCSmmPeriodicTimerProgramTimers ();
157
158 return EFI_SUCCESS;
159 }
160
161 VOID
PeriodicTimerGetContext(IN DATABASE_RECORD * Record,OUT QNC_SMM_CONTEXT * HwContext)162 PeriodicTimerGetContext (
163 IN DATABASE_RECORD *Record,
164 OUT QNC_SMM_CONTEXT *HwContext
165 )
166 {
167 TIMER_INTERVAL *TimerInterval;
168
169 ASSERT (Record->ProtocolType == PeriodicTimerType);
170
171 TimerInterval = ContextToTimerInterval (&Record->ChildContext);
172
173 if (TimerInterval != NULL) {
174 //
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
178 //
179 Record->CommBuffer.PeriodicTimer.ElapsedTime += TimerInterval->Interval;
180 CopyMem (HwContext, &Record->ChildContext, sizeof (QNC_SMM_CONTEXT));
181 }
182 }
183
184 BOOLEAN
PeriodicTimerCmpContext(IN QNC_SMM_CONTEXT * HwContext,IN QNC_SMM_CONTEXT * ChildContext)185 PeriodicTimerCmpContext (
186 IN QNC_SMM_CONTEXT *HwContext,
187 IN QNC_SMM_CONTEXT *ChildContext
188 )
189 {
190 DATABASE_RECORD *Record;
191
192 Record = DATABASE_RECORD_FROM_CONTEXT (ChildContext);
193
194 if (Record->CommBuffer.PeriodicTimer.ElapsedTime >= ChildContext->PeriodicTimer.Period) {
195 //
196 // This child should be dispatched
197 // The timer will be restarted on the "ClearSource" call.
198 //
199 return TRUE;
200 } else {
201 return FALSE;
202 }
203 }
204
205 VOID
PeriodicTimerGetBuffer(IN DATABASE_RECORD * Record)206 PeriodicTimerGetBuffer (
207 IN DATABASE_RECORD * Record
208 )
209 {
210 //
211 // CommBuffer has been updated by PeriodicTimerGetContext, so return directly
212 //
213 return;
214 }
215
216 VOID
QNCSmmPeriodicTimerProgramTimers(VOID)217 QNCSmmPeriodicTimerProgramTimers (
218 VOID
219 )
220 {
221 UINT32 GpePmcwValue;
222 SUPPORTED_TIMER Timer;
223 DATABASE_RECORD *RecordInDb;
224 LIST_ENTRY *LinkInDb;
225 TIMER_INTERVAL *TimerInterval;
226
227 //
228 // Find the minimum required interval for each timer
229 //
230 for (Timer = (SUPPORTED_TIMER)0; Timer < NUM_TIMERS; Timer++) {
231 mTimers[Timer].MinReqInterval = ~(UINT64)0x0;
232 mTimers[Timer].NumChildren = 0;
233 }
234 LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
235 while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
236 RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
237 if (RecordInDb->ProtocolType == PeriodicTimerType) {
238 //
239 // This child is registerd with the PeriodicTimer protocol
240 //
241 TimerInterval = ContextToTimerInterval (&RecordInDb->ChildContext);
242
243 if(TimerInterval != NULL) {
244 Timer = (SUPPORTED_TIMER)((TIMER_INTERVAL *) (TimerInterval))->AssociatedTimer;
245
246 ASSERT (Timer >= 0 && Timer < NUM_TIMERS);
247
248 if (mTimers[Timer].MinReqInterval > RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval) {
249 mTimers[Timer].MinReqInterval = RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval;
250 }
251 mTimers[Timer].NumChildren++;
252 }
253 }
254 LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
255 }
256
257 //
258 // Program the hardware
259 //
260 GpePmcwValue = 0;
261 if (mTimers[PERIODIC_TIMER].NumChildren > 0) {
262 switch (mTimers[PERIODIC_TIMER].MinReqInterval) {
263
264 case TIME_64s:
265 GpePmcwValue = INDEX_TIME_64s;
266 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64s;
267 break;
268
269 case TIME_32s:
270 GpePmcwValue = INDEX_TIME_32s;
271 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32s;
272 break;
273
274 case TIME_16s:
275 GpePmcwValue = INDEX_TIME_16s;
276 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16s;
277 break;
278
279 case TIME_8s:
280 GpePmcwValue = INDEX_TIME_8s;
281 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_8s;
282 break;
283
284 case TIME_64ms:
285 GpePmcwValue = INDEX_TIME_64ms;
286 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64ms;
287 break;
288
289 case TIME_32ms:
290 GpePmcwValue = INDEX_TIME_32ms;
291 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32ms;
292 break;
293
294 case TIME_16ms:
295 GpePmcwValue = INDEX_TIME_16ms;
296 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16ms;
297 break;
298
299 case TIME_1_5ms:
300 GpePmcwValue = INDEX_TIME_1_5ms;
301 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_1_5ms;
302 break;
303
304 default:
305 ASSERT (FALSE);
306 break;
307 };
308
309 GpePmcwValue |= B_QNC_GPE0BLK_PMCW_PSE;
310
311 IoOr32(((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_PMCW), GpePmcwValue);
312
313 //
314 // Restart the timer here, just need to clear the SMI
315 //
316 QNCSmmClearSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);
317 } else {
318 QNCSmmDisableSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);
319 }
320 }
321
322 EFI_STATUS
QNCSmmPeriodicTimerDispatchGetNextShorterInterval(IN CONST EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL * This,IN OUT UINT64 ** SmiTickInterval)323 QNCSmmPeriodicTimerDispatchGetNextShorterInterval (
324 IN CONST EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL *This,
325 IN OUT UINT64 **SmiTickInterval
326 )
327 /*++
328
329 Routine Description:
330
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.
333
334 Arguments:
335
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.
338
339 Returns:
340
341 EFI_SUCCESS - The service returned successfully.
342 EFI_INVALID_PARAMETER - The parameter SmiTickInterval is invalid.
343
344 --*/
345 {
346 TIMER_INTERVAL *IntervalPointer;
347
348 ASSERT (SmiTickInterval != NULL);
349
350 IntervalPointer = (TIMER_INTERVAL*)*SmiTickInterval;
351
352 if (IntervalPointer == NULL) {
353 //
354 // The first time child requesting an interval
355 //
356 IntervalPointer = &mSmmPeriodicTimerIntervals[0];
357 } else if (IntervalPointer == &mSmmPeriodicTimerIntervals[INDEX_TIME_MAX - 1]) {
358 //
359 // At end of the list
360 //
361 IntervalPointer = NULL;
362 } else {
363 if ((IntervalPointer >= &mSmmPeriodicTimerIntervals[0]) &&
364 (IntervalPointer < &mSmmPeriodicTimerIntervals[INDEX_TIME_MAX - 1])) {
365 //
366 // Get the next interval in the list
367 //
368 IntervalPointer++;
369 } else {
370 //
371 // Input is out of range
372 //
373 return EFI_INVALID_PARAMETER;
374 }
375 }
376
377 if (IntervalPointer != NULL) {
378 *SmiTickInterval = &IntervalPointer->Interval;
379 } else {
380 *SmiTickInterval = NULL;
381 }
382
383 return EFI_SUCCESS;
384 }
385
386 VOID
QNCSmmPeriodicTimerClearSource(IN QNC_SMM_SOURCE_DESC * SrcDesc)387 QNCSmmPeriodicTimerClearSource (
388 IN QNC_SMM_SOURCE_DESC *SrcDesc
389 )
390 /*++
391
392 Routine Description:
393
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.
396
397 Arguments:
398
399 SrcDesc - Pointer to the QNC_SMM_SOURCE_DESC instance.
400
401 Returns:
402
403 None.
404
405 --*/
406 {
407 DATABASE_RECORD *RecordInDb;
408 LIST_ENTRY *LinkInDb;
409
410 QNCSmmPeriodicTimerProgramTimers ();
411
412 //
413 // Reset Elapsed time
414 //
415 LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
416 while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
417 RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
418 if (RecordInDb->ProtocolType == PeriodicTimerType) {
419 //
420 // This child is registerd with the PeriodicTimer protocol and Callback
421 // has been invoked, so reset the ElapsedTime to 0
422 //
423 if (RecordInDb->CommBuffer.PeriodicTimer.ElapsedTime >= RecordInDb->ChildContext.PeriodicTimer.Period) {
424 RecordInDb->CommBuffer.PeriodicTimer.ElapsedTime = 0;
425 }
426 }
427 LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
428 }
429 }
430
431