--- /dev/null
+/** @file\r
+File to contain all the hardware specific stuff for the Periodical Timer dispatch protocol.\r
+\r
+Copyright (c) 2013-2015 Intel Corporation.\r
+\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+\r
+**/\r
+\r
+//\r
+// Include common header file for this module.\r
+//\r
+#include "CommonHeader.h"\r
+\r
+#include "QNCSmmHelpers.h"\r
+\r
+typedef enum {\r
+ PERIODIC_TIMER = 0,\r
+ NUM_TIMERS\r
+} SUPPORTED_TIMER;\r
+\r
+typedef struct _TIMER_INTERVAL\r
+{\r
+ UINT64 Interval;\r
+ UINT8 AssociatedTimer;\r
+} TIMER_INTERVAL;\r
+\r
+//\r
+// Time constants, in 100 nano-second units\r
+//\r
+#define TIME_64s 640000000 /* 64 s */\r
+#define TIME_32s 320000000 /* 32 s */\r
+#define TIME_16s 160000000 /* 16 s */\r
+#define TIME_8s 80000000 /* 8 s */\r
+#define TIME_64ms 640000 /* 64 ms */\r
+#define TIME_32ms 320000 /* 32 ms */\r
+#define TIME_16ms 160000 /* 16 ms */\r
+#define TIME_1_5ms 15000 /* 1.5 ms */\r
+\r
+// PMCW (GPE+28h) [2:0] Periodic SMI Rate selection\r
+// 000 1.5ms\r
+// 001 16ms\r
+// 010 32ms\r
+// 011 64ms\r
+// 100 8s\r
+// 101 16s\r
+// 110 32s\r
+// 111 64s\r
+\r
+typedef enum {\r
+ INDEX_TIME_1_5ms = 0,\r
+ INDEX_TIME_16ms,\r
+ INDEX_TIME_32ms,\r
+ INDEX_TIME_64ms,\r
+ INDEX_TIME_8s,\r
+ INDEX_TIME_16s,\r
+ INDEX_TIME_32s,\r
+ INDEX_TIME_64s,\r
+ INDEX_TIME_MAX\r
+} TIMER_INTERVAL_INDEX;\r
+\r
+TIMER_INTERVAL mSmmPeriodicTimerIntervals[INDEX_TIME_MAX] = {\r
+ {TIME_1_5ms, PERIODIC_TIMER},\r
+ {TIME_16ms, PERIODIC_TIMER},\r
+ {TIME_32ms, PERIODIC_TIMER},\r
+ {TIME_64ms, PERIODIC_TIMER},\r
+ { TIME_8s, PERIODIC_TIMER },\r
+ {TIME_16s, PERIODIC_TIMER},\r
+ {TIME_32s, PERIODIC_TIMER},\r
+ {TIME_64s, PERIODIC_TIMER}\r
+};\r
+\r
+typedef struct _TIMER_INFO {\r
+ UINTN NumChildren; // number of children using this timer\r
+ UINT64 MinReqInterval; // minimum interval required by children\r
+ UINTN CurrentSetting; // interval this timer is set at right now (index into interval table)\r
+} TIMER_INFO;\r
+\r
+TIMER_INFO mTimers[NUM_TIMERS];\r
+\r
+QNC_SMM_SOURCE_DESC mTIMER_SOURCE_DESCS[NUM_TIMERS] = {\r
+ {\r
+ QNC_SMM_NO_FLAGS,\r
+ {\r
+ {{GPE_ADDR_TYPE, {R_QNC_GPE0BLK_SMIE}}, S_QNC_GPE0BLK_SMIE, N_QNC_GPE0BLK_SMIE_SWT},\r
+ NULL_BIT_DESC_INITIALIZER\r
+ },\r
+ {\r
+ {{GPE_ADDR_TYPE, {R_QNC_GPE0BLK_SMIS}}, S_QNC_GPE0BLK_SMIS, N_QNC_GPE0BLK_SMIS_SWT}\r
+ }\r
+ }\r
+};\r
+\r
+VOID\r
+QNCSmmPeriodicTimerProgramTimers(\r
+ VOID\r
+ );\r
+\r
+\r
+TIMER_INTERVAL *\r
+ContextToTimerInterval (\r
+ IN QNC_SMM_CONTEXT *RegisterContext\r
+ )\r
+{\r
+ UINTN loopvar;\r
+\r
+ //\r
+ // Determine which timer this child is using\r
+ //\r
+ for (loopvar = 0; loopvar < INDEX_TIME_MAX; loopvar++) {\r
+ if (((RegisterContext->PeriodicTimer.SmiTickInterval == 0) && (RegisterContext->PeriodicTimer.Period >= mSmmPeriodicTimerIntervals[loopvar].Interval)) ||\r
+ (RegisterContext->PeriodicTimer.SmiTickInterval == mSmmPeriodicTimerIntervals[loopvar].Interval)\r
+ ) {\r
+ return &mSmmPeriodicTimerIntervals[loopvar];\r
+ }\r
+ }\r
+\r
+ //\r
+ // If this assertion fires, then either:\r
+ // (1) the context contains an invalid interval\r
+ // (2) the timer interval table is corrupt\r
+ //\r
+ // ASSERT (FALSE);\r
+\r
+ return NULL;\r
+}\r
+\r
+EFI_STATUS\r
+MapPeriodicTimerToSrcDesc (\r
+ IN QNC_SMM_CONTEXT *RegisterContext,\r
+ OUT QNC_SMM_SOURCE_DESC *SrcDesc\r
+ )\r
+{\r
+ TIMER_INTERVAL *TimerInterval;\r
+\r
+ //\r
+ // Figure out which timer the child is requesting and\r
+ // send back the source description\r
+ //\r
+ TimerInterval = ContextToTimerInterval (RegisterContext);\r
+ if (TimerInterval == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ CopyMem (SrcDesc, &mTIMER_SOURCE_DESCS[TimerInterval->AssociatedTimer], sizeof (QNC_SMM_SOURCE_DESC));;\r
+\r
+ //\r
+ // Program the value of the interval into hardware\r
+ //\r
+ QNCSmmPeriodicTimerProgramTimers ();\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+VOID\r
+PeriodicTimerGetContext (\r
+ IN DATABASE_RECORD *Record,\r
+ OUT QNC_SMM_CONTEXT *HwContext\r
+ )\r
+{\r
+ TIMER_INTERVAL *TimerInterval;\r
+\r
+ ASSERT (Record->ProtocolType == PeriodicTimerType);\r
+\r
+ TimerInterval = ContextToTimerInterval (&Record->ChildContext);\r
+\r
+ if (TimerInterval != NULL) {\r
+ //\r
+ // Ignore the hardware context. It's not required for this protocol.\r
+ // Instead, just increment the child's context.\r
+ // Update the elapsed time w/ the data from our tables\r
+ //\r
+ Record->CommBuffer.PeriodicTimer.ElapsedTime += TimerInterval->Interval;\r
+ *HwContext = Record->ChildContext;\r
+ }\r
+}\r
+\r
+BOOLEAN\r
+PeriodicTimerCmpContext (\r
+ IN QNC_SMM_CONTEXT *HwContext,\r
+ IN QNC_SMM_CONTEXT *ChildContext\r
+ )\r
+{\r
+ DATABASE_RECORD *Record;\r
+\r
+ Record = DATABASE_RECORD_FROM_CONTEXT (ChildContext);\r
+\r
+ if (Record->CommBuffer.PeriodicTimer.ElapsedTime >= ChildContext->PeriodicTimer.Period) {\r
+ //\r
+ // This child should be dispatched\r
+ // The timer will be restarted on the "ClearSource" call.\r
+ //\r
+ return TRUE;\r
+ } else {\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+VOID\r
+PeriodicTimerGetBuffer (\r
+ IN DATABASE_RECORD * Record\r
+ )\r
+{\r
+ //\r
+ // CommBuffer has been updated by PeriodicTimerGetContext, so return directly\r
+ //\r
+ return;\r
+}\r
+\r
+VOID\r
+QNCSmmPeriodicTimerProgramTimers (\r
+ VOID\r
+ )\r
+{\r
+ UINT32 GpePmcwValue;\r
+ SUPPORTED_TIMER Timer;\r
+ DATABASE_RECORD *RecordInDb;\r
+ LIST_ENTRY *LinkInDb;\r
+ TIMER_INTERVAL *TimerInterval;\r
+\r
+ //\r
+ // Find the minimum required interval for each timer\r
+ //\r
+ for (Timer = (SUPPORTED_TIMER)0; Timer < NUM_TIMERS; Timer++) {\r
+ mTimers[Timer].MinReqInterval = ~(UINT64)0x0;\r
+ mTimers[Timer].NumChildren = 0;\r
+ }\r
+ LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);\r
+ while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {\r
+ RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);\r
+ if (RecordInDb->ProtocolType == PeriodicTimerType) {\r
+ //\r
+ // This child is registerd with the PeriodicTimer protocol\r
+ //\r
+ TimerInterval = ContextToTimerInterval (&RecordInDb->ChildContext);\r
+\r
+ if(TimerInterval != NULL) {\r
+ Timer = (SUPPORTED_TIMER)((TIMER_INTERVAL *) (TimerInterval))->AssociatedTimer;\r
+\r
+ ASSERT (Timer >= 0 && Timer < NUM_TIMERS);\r
+\r
+ if (mTimers[Timer].MinReqInterval > RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval) {\r
+ mTimers[Timer].MinReqInterval = RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval;\r
+ }\r
+ mTimers[Timer].NumChildren++;\r
+ }\r
+ }\r
+ LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);\r
+ }\r
+\r
+ //\r
+ // Program the hardware\r
+ //\r
+ GpePmcwValue = 0;\r
+ if (mTimers[PERIODIC_TIMER].NumChildren > 0) {\r
+ switch (mTimers[PERIODIC_TIMER].MinReqInterval) {\r
+\r
+ case TIME_64s:\r
+ GpePmcwValue = INDEX_TIME_64s;\r
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64s;\r
+ break;\r
+\r
+ case TIME_32s:\r
+ GpePmcwValue = INDEX_TIME_32s;\r
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32s;\r
+ break;\r
+\r
+ case TIME_16s:\r
+ GpePmcwValue = INDEX_TIME_16s;\r
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16s;\r
+ break;\r
+\r
+ case TIME_8s:\r
+ GpePmcwValue = INDEX_TIME_8s;\r
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_8s;\r
+ break;\r
+\r
+ case TIME_64ms:\r
+ GpePmcwValue = INDEX_TIME_64ms;\r
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64ms;\r
+ break;\r
+\r
+ case TIME_32ms:\r
+ GpePmcwValue = INDEX_TIME_32ms;\r
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32ms;\r
+ break;\r
+\r
+ case TIME_16ms:\r
+ GpePmcwValue = INDEX_TIME_16ms;\r
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16ms;\r
+ break;\r
+\r
+ case TIME_1_5ms:\r
+ GpePmcwValue = INDEX_TIME_1_5ms;\r
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_1_5ms;\r
+ break;\r
+\r
+ default:\r
+ ASSERT (FALSE);\r
+ break;\r
+ };\r
+\r
+ GpePmcwValue |= B_QNC_GPE0BLK_PMCW_PSE;\r
+\r
+ IoOr32(((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_PMCW), GpePmcwValue);\r
+\r
+ //\r
+ // Restart the timer here, just need to clear the SMI\r
+ //\r
+ QNCSmmClearSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);\r
+ } else {\r
+ QNCSmmDisableSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);\r
+ }\r
+}\r
+\r
+EFI_STATUS\r
+QNCSmmPeriodicTimerDispatchGetNextShorterInterval (\r
+ IN CONST EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL *This,\r
+ IN OUT UINT64 **SmiTickInterval\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ This services returns the next SMI tick period that is supported by the chipset.\r
+ The order returned is from longest to shortest interval period.\r
+\r
+Arguments:\r
+\r
+ This - Pointer to the EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL instance.\r
+ SmiTickInterval - Pointer to pointer of the next shorter SMI interval period that is supported by the child.\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS - The service returned successfully.\r
+ EFI_INVALID_PARAMETER - The parameter SmiTickInterval is invalid.\r
+\r
+--*/\r
+{\r
+ TIMER_INTERVAL *IntervalPointer;\r
+\r
+ ASSERT (SmiTickInterval != NULL);\r
+\r
+ IntervalPointer = (TIMER_INTERVAL*)*SmiTickInterval;\r
+\r
+ if (IntervalPointer == NULL) {\r
+ //\r
+ // The first time child requesting an interval\r
+ //\r
+ IntervalPointer = &mSmmPeriodicTimerIntervals[0];\r
+ } else if (IntervalPointer == &mSmmPeriodicTimerIntervals[INDEX_TIME_MAX - 1]) {\r
+ //\r
+ // At end of the list\r
+ //\r
+ IntervalPointer = NULL;\r
+ } else {\r
+ if ((IntervalPointer >= &mSmmPeriodicTimerIntervals[0]) &&\r
+ (IntervalPointer < &mSmmPeriodicTimerIntervals[INDEX_TIME_MAX - 1])) {\r
+ //\r
+ // Get the next interval in the list\r
+ //\r
+ IntervalPointer++;\r
+ } else {\r
+ //\r
+ // Input is out of range\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if (IntervalPointer != NULL) {\r
+ *SmiTickInterval = &IntervalPointer->Interval;\r
+ } else {\r
+ *SmiTickInterval = NULL;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+VOID\r
+QNCSmmPeriodicTimerClearSource (\r
+ IN QNC_SMM_SOURCE_DESC *SrcDesc\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ This function is responsible for calculating and enabling any timers that are required\r
+ to dispatch messages to children. The SrcDesc argument isn't acutally used.\r
+\r
+Arguments:\r
+\r
+ SrcDesc - Pointer to the QNC_SMM_SOURCE_DESC instance.\r
+\r
+Returns:\r
+\r
+ None.\r
+\r
+--*/\r
+{\r
+ DATABASE_RECORD *RecordInDb;\r
+ LIST_ENTRY *LinkInDb;\r
+\r
+ QNCSmmPeriodicTimerProgramTimers ();\r
+\r
+ //\r
+ // Reset Elapsed time\r
+ //\r
+ LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);\r
+ while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {\r
+ RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);\r
+ if (RecordInDb->ProtocolType == PeriodicTimerType) {\r
+ //\r
+ // This child is registerd with the PeriodicTimer protocol and Callback\r
+ // has been invoked, so reset the ElapsedTime to 0\r
+ //\r
+ if (RecordInDb->CommBuffer.PeriodicTimer.ElapsedTime >= RecordInDb->ChildContext.PeriodicTimer.Period) {\r
+ RecordInDb->CommBuffer.PeriodicTimer.ElapsedTime = 0;\r
+ }\r
+ }\r
+ LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);\r
+ }\r
+}\r
+\r