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