]> git.proxmox.com Git - mirror_edk2.git/blobdiff - QuarkSocPkg/QuarkNorthCluster/Smm/DxeSmm/QncSmmDispatcher/QNC/QNCSmmPeriodicTimer.c
QuarkSocPkg: Add new package for Quark SoC X1000
[mirror_edk2.git] / QuarkSocPkg / QuarkNorthCluster / Smm / DxeSmm / QncSmmDispatcher / QNC / QNCSmmPeriodicTimer.c
diff --git a/QuarkSocPkg/QuarkNorthCluster/Smm/DxeSmm/QncSmmDispatcher/QNC/QNCSmmPeriodicTimer.c b/QuarkSocPkg/QuarkNorthCluster/Smm/DxeSmm/QncSmmDispatcher/QNC/QNCSmmPeriodicTimer.c
new file mode 100644 (file)
index 0000000..1d1030c
--- /dev/null
@@ -0,0 +1,430 @@
+/** @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