]> git.proxmox.com Git - mirror_edk2.git/blame - QuarkSocPkg/QuarkNorthCluster/Smm/DxeSmm/QncSmmDispatcher/QNC/QNCSmmPeriodicTimer.c
QuarkSocPkg/QncSmmDispatcher: Fix context passed to SMI handlers
[mirror_edk2.git] / QuarkSocPkg / QuarkNorthCluster / Smm / DxeSmm / QncSmmDispatcher / QNC / QNCSmmPeriodicTimer.c
CommitLineData
9b6bbcdb
MK
1/** @file\r
2File to contain all the hardware specific stuff for the Periodical Timer dispatch protocol.\r
3\r
29f169d1 4Copyright (c) 2013-2016 Intel Corporation.\r
9b6bbcdb
MK
5\r
6This program and the accompanying materials\r
7are licensed and made available under the terms and conditions of the BSD License\r
8which accompanies this distribution. The full text of the license may be found at\r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14\r
15**/\r
16\r
17//\r
18// Include common header file for this module.\r
19//\r
20#include "CommonHeader.h"\r
21\r
22#include "QNCSmmHelpers.h"\r
23\r
24typedef enum {\r
25 PERIODIC_TIMER = 0,\r
26 NUM_TIMERS\r
27} SUPPORTED_TIMER;\r
28\r
29typedef struct _TIMER_INTERVAL\r
30{\r
31 UINT64 Interval;\r
32 UINT8 AssociatedTimer;\r
33} TIMER_INTERVAL;\r
34\r
35//\r
36// Time constants, in 100 nano-second units\r
37//\r
38#define TIME_64s 640000000 /* 64 s */\r
39#define TIME_32s 320000000 /* 32 s */\r
40#define TIME_16s 160000000 /* 16 s */\r
41#define TIME_8s 80000000 /* 8 s */\r
42#define TIME_64ms 640000 /* 64 ms */\r
43#define TIME_32ms 320000 /* 32 ms */\r
44#define TIME_16ms 160000 /* 16 ms */\r
45#define TIME_1_5ms 15000 /* 1.5 ms */\r
46\r
47// PMCW (GPE+28h) [2:0] Periodic SMI Rate selection\r
48// 000 1.5ms\r
49// 001 16ms\r
50// 010 32ms\r
51// 011 64ms\r
52// 100 8s\r
53// 101 16s\r
54// 110 32s\r
55// 111 64s\r
56\r
57typedef enum {\r
58 INDEX_TIME_1_5ms = 0,\r
59 INDEX_TIME_16ms,\r
60 INDEX_TIME_32ms,\r
61 INDEX_TIME_64ms,\r
62 INDEX_TIME_8s,\r
63 INDEX_TIME_16s,\r
64 INDEX_TIME_32s,\r
65 INDEX_TIME_64s,\r
66 INDEX_TIME_MAX\r
67} TIMER_INTERVAL_INDEX;\r
68\r
69TIMER_INTERVAL mSmmPeriodicTimerIntervals[INDEX_TIME_MAX] = {\r
70 {TIME_1_5ms, PERIODIC_TIMER},\r
71 {TIME_16ms, PERIODIC_TIMER},\r
72 {TIME_32ms, PERIODIC_TIMER},\r
73 {TIME_64ms, PERIODIC_TIMER},\r
74 { TIME_8s, PERIODIC_TIMER },\r
75 {TIME_16s, PERIODIC_TIMER},\r
76 {TIME_32s, PERIODIC_TIMER},\r
77 {TIME_64s, PERIODIC_TIMER}\r
78};\r
79\r
80typedef struct _TIMER_INFO {\r
81 UINTN NumChildren; // number of children using this timer\r
82 UINT64 MinReqInterval; // minimum interval required by children\r
83 UINTN CurrentSetting; // interval this timer is set at right now (index into interval table)\r
84} TIMER_INFO;\r
85\r
86TIMER_INFO mTimers[NUM_TIMERS];\r
87\r
88QNC_SMM_SOURCE_DESC mTIMER_SOURCE_DESCS[NUM_TIMERS] = {\r
89 {\r
90 QNC_SMM_NO_FLAGS,\r
91 {\r
92 {{GPE_ADDR_TYPE, {R_QNC_GPE0BLK_SMIE}}, S_QNC_GPE0BLK_SMIE, N_QNC_GPE0BLK_SMIE_SWT},\r
93 NULL_BIT_DESC_INITIALIZER\r
94 },\r
95 {\r
96 {{GPE_ADDR_TYPE, {R_QNC_GPE0BLK_SMIS}}, S_QNC_GPE0BLK_SMIS, N_QNC_GPE0BLK_SMIS_SWT}\r
97 }\r
98 }\r
99};\r
100\r
101VOID\r
102QNCSmmPeriodicTimerProgramTimers(\r
103 VOID\r
104 );\r
105\r
106\r
107TIMER_INTERVAL *\r
108ContextToTimerInterval (\r
109 IN QNC_SMM_CONTEXT *RegisterContext\r
110 )\r
111{\r
112 UINTN loopvar;\r
113\r
114 //\r
115 // Determine which timer this child is using\r
116 //\r
117 for (loopvar = 0; loopvar < INDEX_TIME_MAX; loopvar++) {\r
118 if (((RegisterContext->PeriodicTimer.SmiTickInterval == 0) && (RegisterContext->PeriodicTimer.Period >= mSmmPeriodicTimerIntervals[loopvar].Interval)) ||\r
119 (RegisterContext->PeriodicTimer.SmiTickInterval == mSmmPeriodicTimerIntervals[loopvar].Interval)\r
120 ) {\r
121 return &mSmmPeriodicTimerIntervals[loopvar];\r
122 }\r
123 }\r
124\r
125 //\r
126 // If this assertion fires, then either:\r
127 // (1) the context contains an invalid interval\r
128 // (2) the timer interval table is corrupt\r
129 //\r
130 // ASSERT (FALSE);\r
131\r
132 return NULL;\r
133}\r
134\r
135EFI_STATUS\r
136MapPeriodicTimerToSrcDesc (\r
137 IN QNC_SMM_CONTEXT *RegisterContext,\r
138 OUT QNC_SMM_SOURCE_DESC *SrcDesc\r
139 )\r
140{\r
141 TIMER_INTERVAL *TimerInterval;\r
142\r
143 //\r
144 // Figure out which timer the child is requesting and\r
145 // send back the source description\r
146 //\r
147 TimerInterval = ContextToTimerInterval (RegisterContext);\r
148 if (TimerInterval == NULL) {\r
149 return EFI_INVALID_PARAMETER;\r
150 }\r
151 CopyMem (SrcDesc, &mTIMER_SOURCE_DESCS[TimerInterval->AssociatedTimer], sizeof (QNC_SMM_SOURCE_DESC));;\r
152\r
153 //\r
154 // Program the value of the interval into hardware\r
155 //\r
156 QNCSmmPeriodicTimerProgramTimers ();\r
157\r
158 return EFI_SUCCESS;\r
159}\r
160\r
161VOID\r
162PeriodicTimerGetContext (\r
163 IN DATABASE_RECORD *Record,\r
164 OUT QNC_SMM_CONTEXT *HwContext\r
165 )\r
166{\r
167 TIMER_INTERVAL *TimerInterval;\r
168\r
169 ASSERT (Record->ProtocolType == PeriodicTimerType);\r
170\r
171 TimerInterval = ContextToTimerInterval (&Record->ChildContext);\r
172\r
173 if (TimerInterval != NULL) {\r
174 //\r
175 // Ignore the hardware context. It's not required for this protocol.\r
176 // Instead, just increment the child's context.\r
177 // Update the elapsed time w/ the data from our tables\r
178 //\r
179 Record->CommBuffer.PeriodicTimer.ElapsedTime += TimerInterval->Interval;\r
29f169d1 180 CopyMem (HwContext, &Record->ChildContext, sizeof (QNC_SMM_CONTEXT));\r
9b6bbcdb
MK
181 }\r
182}\r
183\r
184BOOLEAN\r
185PeriodicTimerCmpContext (\r
186 IN QNC_SMM_CONTEXT *HwContext,\r
187 IN QNC_SMM_CONTEXT *ChildContext\r
188 )\r
189{\r
190 DATABASE_RECORD *Record;\r
191\r
192 Record = DATABASE_RECORD_FROM_CONTEXT (ChildContext);\r
193\r
194 if (Record->CommBuffer.PeriodicTimer.ElapsedTime >= ChildContext->PeriodicTimer.Period) {\r
195 //\r
196 // This child should be dispatched\r
197 // The timer will be restarted on the "ClearSource" call.\r
198 //\r
199 return TRUE;\r
200 } else {\r
201 return FALSE;\r
202 }\r
203}\r
204\r
205VOID\r
206PeriodicTimerGetBuffer (\r
207 IN DATABASE_RECORD * Record\r
208 )\r
209{\r
210 //\r
211 // CommBuffer has been updated by PeriodicTimerGetContext, so return directly\r
212 //\r
213 return;\r
214}\r
215\r
216VOID\r
217QNCSmmPeriodicTimerProgramTimers (\r
218 VOID\r
219 )\r
220{\r
221 UINT32 GpePmcwValue;\r
222 SUPPORTED_TIMER Timer;\r
223 DATABASE_RECORD *RecordInDb;\r
224 LIST_ENTRY *LinkInDb;\r
225 TIMER_INTERVAL *TimerInterval;\r
226\r
227 //\r
228 // Find the minimum required interval for each timer\r
229 //\r
230 for (Timer = (SUPPORTED_TIMER)0; Timer < NUM_TIMERS; Timer++) {\r
231 mTimers[Timer].MinReqInterval = ~(UINT64)0x0;\r
232 mTimers[Timer].NumChildren = 0;\r
233 }\r
234 LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);\r
235 while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {\r
236 RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);\r
237 if (RecordInDb->ProtocolType == PeriodicTimerType) {\r
238 //\r
239 // This child is registerd with the PeriodicTimer protocol\r
240 //\r
241 TimerInterval = ContextToTimerInterval (&RecordInDb->ChildContext);\r
242\r
243 if(TimerInterval != NULL) {\r
244 Timer = (SUPPORTED_TIMER)((TIMER_INTERVAL *) (TimerInterval))->AssociatedTimer;\r
245\r
246 ASSERT (Timer >= 0 && Timer < NUM_TIMERS);\r
247\r
248 if (mTimers[Timer].MinReqInterval > RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval) {\r
249 mTimers[Timer].MinReqInterval = RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval;\r
250 }\r
251 mTimers[Timer].NumChildren++;\r
252 }\r
253 }\r
254 LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);\r
255 }\r
256\r
257 //\r
258 // Program the hardware\r
259 //\r
260 GpePmcwValue = 0;\r
261 if (mTimers[PERIODIC_TIMER].NumChildren > 0) {\r
262 switch (mTimers[PERIODIC_TIMER].MinReqInterval) {\r
263\r
264 case TIME_64s:\r
265 GpePmcwValue = INDEX_TIME_64s;\r
266 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64s;\r
267 break;\r
268\r
269 case TIME_32s:\r
270 GpePmcwValue = INDEX_TIME_32s;\r
271 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32s;\r
272 break;\r
273\r
274 case TIME_16s:\r
275 GpePmcwValue = INDEX_TIME_16s;\r
276 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16s;\r
277 break;\r
278\r
279 case TIME_8s:\r
280 GpePmcwValue = INDEX_TIME_8s;\r
281 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_8s;\r
282 break;\r
283\r
284 case TIME_64ms:\r
285 GpePmcwValue = INDEX_TIME_64ms;\r
286 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64ms;\r
287 break;\r
288\r
289 case TIME_32ms:\r
290 GpePmcwValue = INDEX_TIME_32ms;\r
291 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32ms;\r
292 break;\r
293\r
294 case TIME_16ms:\r
295 GpePmcwValue = INDEX_TIME_16ms;\r
296 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16ms;\r
297 break;\r
298\r
299 case TIME_1_5ms:\r
300 GpePmcwValue = INDEX_TIME_1_5ms;\r
301 mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_1_5ms;\r
302 break;\r
303\r
304 default:\r
305 ASSERT (FALSE);\r
306 break;\r
307 };\r
308\r
309 GpePmcwValue |= B_QNC_GPE0BLK_PMCW_PSE;\r
310\r
311 IoOr32(((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_PMCW), GpePmcwValue);\r
312\r
313 //\r
314 // Restart the timer here, just need to clear the SMI\r
315 //\r
316 QNCSmmClearSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);\r
317 } else {\r
318 QNCSmmDisableSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);\r
319 }\r
320}\r
321\r
322EFI_STATUS\r
323QNCSmmPeriodicTimerDispatchGetNextShorterInterval (\r
324 IN CONST EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL *This,\r
325 IN OUT UINT64 **SmiTickInterval\r
326 )\r
327/*++\r
328\r
329Routine Description:\r
330\r
331 This services returns the next SMI tick period that is supported by the chipset.\r
332 The order returned is from longest to shortest interval period.\r
333\r
334Arguments:\r
335\r
336 This - Pointer to the EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL instance.\r
337 SmiTickInterval - Pointer to pointer of the next shorter SMI interval period that is supported by the child.\r
338\r
339Returns:\r
340\r
341 EFI_SUCCESS - The service returned successfully.\r
342 EFI_INVALID_PARAMETER - The parameter SmiTickInterval is invalid.\r
343\r
344--*/\r
345{\r
346 TIMER_INTERVAL *IntervalPointer;\r
347\r
348 ASSERT (SmiTickInterval != NULL);\r
349\r
350 IntervalPointer = (TIMER_INTERVAL*)*SmiTickInterval;\r
351\r
352 if (IntervalPointer == NULL) {\r
353 //\r
354 // The first time child requesting an interval\r
355 //\r
356 IntervalPointer = &mSmmPeriodicTimerIntervals[0];\r
357 } else if (IntervalPointer == &mSmmPeriodicTimerIntervals[INDEX_TIME_MAX - 1]) {\r
358 //\r
359 // At end of the list\r
360 //\r
361 IntervalPointer = NULL;\r
362 } else {\r
363 if ((IntervalPointer >= &mSmmPeriodicTimerIntervals[0]) &&\r
364 (IntervalPointer < &mSmmPeriodicTimerIntervals[INDEX_TIME_MAX - 1])) {\r
365 //\r
366 // Get the next interval in the list\r
367 //\r
368 IntervalPointer++;\r
369 } else {\r
370 //\r
371 // Input is out of range\r
372 //\r
373 return EFI_INVALID_PARAMETER;\r
374 }\r
375 }\r
376\r
377 if (IntervalPointer != NULL) {\r
378 *SmiTickInterval = &IntervalPointer->Interval;\r
379 } else {\r
380 *SmiTickInterval = NULL;\r
381 }\r
382\r
383 return EFI_SUCCESS;\r
384}\r
385\r
386VOID\r
387QNCSmmPeriodicTimerClearSource (\r
388 IN QNC_SMM_SOURCE_DESC *SrcDesc\r
389 )\r
390/*++\r
391\r
392Routine Description:\r
393\r
394 This function is responsible for calculating and enabling any timers that are required\r
395 to dispatch messages to children. The SrcDesc argument isn't acutally used.\r
396\r
397Arguments:\r
398\r
399 SrcDesc - Pointer to the QNC_SMM_SOURCE_DESC instance.\r
400\r
401Returns:\r
402\r
403 None.\r
404\r
405--*/\r
406{\r
407 DATABASE_RECORD *RecordInDb;\r
408 LIST_ENTRY *LinkInDb;\r
409\r
410 QNCSmmPeriodicTimerProgramTimers ();\r
411\r
412 //\r
413 // Reset Elapsed time\r
414 //\r
415 LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);\r
416 while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {\r
417 RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);\r
418 if (RecordInDb->ProtocolType == PeriodicTimerType) {\r
419 //\r
420 // This child is registerd with the PeriodicTimer protocol and Callback\r
421 // has been invoked, so reset the ElapsedTime to 0\r
422 //\r
423 if (RecordInDb->CommBuffer.PeriodicTimer.ElapsedTime >= RecordInDb->ChildContext.PeriodicTimer.Period) {\r
424 RecordInDb->CommBuffer.PeriodicTimer.ElapsedTime = 0;\r
425 }\r
426 }\r
427 LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);\r
428 }\r
429}\r
430\r