summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/LynxPoint/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/LynxPoint/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c')
-rw-r--r--ReferenceCode/Chipset/LynxPoint/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c519
1 files changed, 519 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/LynxPoint/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c b/ReferenceCode/Chipset/LynxPoint/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c
new file mode 100644
index 0000000..ee554f7
--- /dev/null
+++ b/ReferenceCode/Chipset/LynxPoint/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c
@@ -0,0 +1,519 @@
+/** @file
+ File to contain all the hardware specific stuff for the Periodical Timer dispatch protocol.
+
+@copyright
+ Copyright (c) 1999 - 2012 Intel Corporation. All rights reserved
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part of this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and uniquely
+ identified as "Intel Reference Module" and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement
+
+**/
+#include "PchSmmHelpers.h"
+
+typedef enum {
+ PERIODIC_TIMER= 0,
+ SWSMI_TIMER,
+ NUM_TIMERS
+} SUPPORTED_TIMER;
+
+typedef struct _TIMER_INTERVAL {
+ UINT64 Interval;
+ UINT8 AssociatedTimer;
+} TIMER_INTERVAL;
+
+#define NUM_INTERVALS 8
+
+//
+// Time constants, in 100 nano-second units
+//
+#define TIME_64s 640000000 ///< 64 s
+#define TIME_32s 320000000 ///< 32 s
+#define TIME_16s 160000000 ///< 16 s
+#define TIME_8s 80000000 ///< 8 s
+#define TIME_64ms 640000 ///< 64 ms
+#define TIME_32ms 320000 ///< 32 ms
+#define TIME_16ms 160000 ///< 16 ms
+#define TIME_1_5ms 15000 ///< 1.5 ms
+
+typedef enum {
+ INDEX_TIME_64s = 0,
+ INDEX_TIME_32s,
+ INDEX_TIME_16s,
+ INDEX_TIME_8s,
+ INDEX_TIME_64ms,
+ INDEX_TIME_32ms,
+ INDEX_TIME_16ms,
+ INDEX_TIME_1_5ms,
+ INDEX_TIME_MAX
+} TIMER_INTERVAL_INDEX;
+
+static TIMER_INTERVAL mSmmPeriodicTimerIntervals[NUM_INTERVALS] = {
+ {
+ TIME_64s,
+ PERIODIC_TIMER
+ },
+ {
+ TIME_32s,
+ PERIODIC_TIMER
+ },
+ {
+ TIME_16s,
+ PERIODIC_TIMER
+ },
+ {
+ TIME_8s,
+ PERIODIC_TIMER
+ },
+ {
+ TIME_64ms,
+ SWSMI_TIMER
+ },
+ {
+ TIME_32ms,
+ SWSMI_TIMER
+ },
+ {
+ TIME_16ms,
+ SWSMI_TIMER
+ },
+ {
+ TIME_1_5ms,
+ SWSMI_TIMER
+ },
+};
+
+typedef struct _TIMER_INFO {
+ UINTN NumChildren; ///< number of children using this timer
+ UINT64 MinReqInterval; ///< minimum interval required by children
+ UINTN CurrentSetting; ///< interval this timer is set at right now (index into interval table)
+} TIMER_INFO;
+
+TIMER_INFO mTimers[NUM_TIMERS];
+
+PCH_SMM_SOURCE_DESC mTIMER_SOURCE_DESCS[NUM_TIMERS] = {
+ {
+ PCH_SMM_NO_FLAGS,
+ {
+ {
+ {
+ ACPI_ADDR_TYPE,
+ R_PCH_SMI_EN
+ },
+ S_PCH_SMI_EN,
+ N_PCH_SMI_EN_PERIODIC
+ },
+ NULL_BIT_DESC_INITIALIZER
+ },
+ {
+ {
+ {
+ ACPI_ADDR_TYPE,
+ R_PCH_SMI_STS
+ },
+ S_PCH_SMI_STS,
+ N_PCH_SMI_STS_PERIODIC
+ }
+ }
+ },
+ {
+ PCH_SMM_NO_FLAGS,
+ {
+ {
+ {
+ ACPI_ADDR_TYPE,
+ R_PCH_SMI_EN
+ },
+ S_PCH_SMI_EN,
+ N_PCH_SMI_EN_SWSMI_TMR
+ },
+ NULL_BIT_DESC_INITIALIZER
+ },
+ {
+ {
+ {
+ ACPI_ADDR_TYPE,
+ R_PCH_SMI_STS
+ },
+ S_PCH_SMI_STS,
+ N_PCH_SMI_STS_SWSMI_TMR
+ }
+ }
+ }
+};
+
+VOID
+PchSmmPeriodicTimerProgramTimers (
+ VOID
+ );
+
+/**
+ Convert the dispatch context to the timer interval, this function will assert if then either:
+ (1) The context contains an invalid interval
+ (2) The timer interval table is corrupt
+
+ @param[in] DispatchContext The pointer to the Dispatch Context
+
+ @retval TIMER_INTERVAL The timer interval of input dispatch context
+**/
+TIMER_INTERVAL *
+ContextToTimerInterval (
+ IN PCH_SMM_CONTEXT *DispatchContext
+ )
+{
+ UINTN loopvar;
+
+ ///
+ /// Determine which timer this child is using
+ ///
+ for (loopvar = 0; loopvar < NUM_INTERVALS; loopvar++) {
+ if (((DispatchContext->PeriodicTimer.SmiTickInterval == 0) &&
+ (DispatchContext->PeriodicTimer.Period >= mSmmPeriodicTimerIntervals[loopvar].Interval)) ||
+ (DispatchContext->PeriodicTimer.SmiTickInterval == mSmmPeriodicTimerIntervals[loopvar].Interval)) {
+ return &mSmmPeriodicTimerIntervals[loopvar];
+ }
+ }
+ ///
+ /// If this assertion fires, then either:
+ /// (1) the context contains an invalid interval
+ /// (2) the timer interval table is corrupt
+ ///
+ ASSERT (FALSE);
+
+ return NULL;
+}
+
+/**
+ Figure out which timer the child is requesting and
+ send back the source description
+
+ @param[in] DispatchContext The pointer to the Dispatch Context instances
+ @param[out] SrcDesc The pointer to the source description
+
+ @retval None
+**/
+VOID
+MapPeriodicTimerToSrcDesc (
+ IN PCH_SMM_CONTEXT *DispatchContext,
+ OUT PCH_SMM_SOURCE_DESC *SrcDesc
+ )
+{
+ TIMER_INTERVAL *TimerInterval;
+
+ ///
+ /// Figure out which timer the child is requesting and
+ /// send back the source description
+ ///
+ TimerInterval = ContextToTimerInterval (DispatchContext);
+ if (TimerInterval == NULL) {
+ return;
+ }
+
+ CopyMem (
+ (VOID *) SrcDesc,
+ (VOID *) (&mTIMER_SOURCE_DESCS[TimerInterval->AssociatedTimer]),
+ sizeof (PCH_SMM_SOURCE_DESC)
+ );
+
+ ///
+ /// Program the value of the interval into hardware
+ ///
+ PchSmmPeriodicTimerProgramTimers ();
+}
+
+/**
+ Update the elapsed time from the Interval data of DATABASE_RECORD
+
+ @param[in] Record The pointer to the DATABASE_RECORD.
+ @param[out] HwContext The Context to be updated.
+
+ @retval None
+**/
+VOID
+EFIAPI
+PeriodicTimerGetContext (
+ IN DATABASE_RECORD *Record,
+ OUT PCH_SMM_CONTEXT *HwContext
+ )
+{
+ TIMER_INTERVAL *TimerInterval;
+
+ ASSERT (Record->ProtocolType == PeriodicTimerType);
+
+ TimerInterval = ContextToTimerInterval (&Record->ChildContext);
+ if (TimerInterval == NULL) {
+ return;
+ }
+ ///
+ /// Ignore the hardware context. It's not required for this protocol.
+ /// Instead, just increment the child's context.
+ /// Update the elapsed time w/ the data from our tables
+ ///
+ Record->ChildContext.PeriodicTimer.ElapsedTime += TimerInterval->Interval;
+ *HwContext = Record->ChildContext;
+}
+
+/**
+ Check whether Periodic Timer of two contexts match
+
+ @param[in] Context1 Context 1 that includes Periodic Timer 1
+ @param[in] Context2 Context 2 that includes Periodic Timer 2
+
+ @retval FALSE Periodic Timer match
+ @retval TRUE Periodic Timer don't match
+**/
+BOOLEAN
+EFIAPI
+PeriodicTimerCmpContext (
+ IN PCH_SMM_CONTEXT *HwContext,
+ IN PCH_SMM_CONTEXT *ChildContext
+ )
+{
+
+ if (ChildContext->PeriodicTimer.ElapsedTime >= ChildContext->PeriodicTimer.Period) {
+ ///
+ /// This child should be dispatched
+ /// Need reset ElapsedTime, or SMI handler will be invoked during SmiTickInterval instead of Period.
+ ///
+ ChildContext->PeriodicTimer.ElapsedTime = 0;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/**
+ Program Smm Periodic Timer
+
+ @param[in] None
+
+ @retval None
+**/
+VOID
+PchSmmPeriodicTimerProgramTimers (
+ VOID
+ )
+{
+ UINT16 GenPmCon1;
+ UINT8 GenPmCon3;
+ SUPPORTED_TIMER Timer;
+ DATABASE_RECORD *RecordInDb;
+ LIST_ENTRY *LinkInDb;
+ UINTN PciD31F0RegBase;
+ TIMER_INTERVAL *TimerInterval;
+
+ PciD31F0RegBase = MmPciAddress (
+ 0,
+ DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LPC,
+ PCI_FUNCTION_NUMBER_PCH_LPC,
+ 0
+ );
+ ///
+ /// Find the minimum required interval for each timer
+ ///
+ for (Timer = 0; Timer < NUM_TIMERS; Timer++) {
+ mTimers[Timer].MinReqInterval = ~ (UINT64) 0x0;
+ mTimers[Timer].NumChildren = 0;
+ }
+
+ LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
+ while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
+ RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
+ if (RecordInDb->ProtocolType == PeriodicTimerType) {
+ ///
+ /// This child is registerd with the PeriodicTimer protocol
+ ///
+ TimerInterval = ContextToTimerInterval (&RecordInDb->ChildContext);
+ if (TimerInterval == NULL) {
+ return;
+ }
+
+ Timer = TimerInterval->AssociatedTimer;
+ if (Timer < 0 || Timer >= NUM_TIMERS) {
+ ASSERT (FALSE);
+ EFI_DEADLOOP ();
+ }
+
+ if (mTimers[Timer].MinReqInterval > RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval) {
+ mTimers[Timer].MinReqInterval = RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval;
+ }
+
+ mTimers[Timer].NumChildren++;
+ }
+
+ LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
+ }
+ ///
+ /// Program the hardware
+ ///
+ if (mTimers[PERIODIC_TIMER].NumChildren > 0) {
+ GenPmCon1 = MmioRead16 (PciD31F0RegBase + R_PCH_LPC_GEN_PMCON_1);
+
+ GenPmCon1 &= ~B_PCH_LPC_GEN_PMCON_PER_SMI_SEL;
+ switch (mTimers[PERIODIC_TIMER].MinReqInterval) {
+ case TIME_64s:
+ GenPmCon1 |= V_PCH_LPC_GEN_PMCON_PER_SMI_64S;
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64s;
+ break;
+
+ case TIME_32s:
+ GenPmCon1 |= V_PCH_LPC_GEN_PMCON_PER_SMI_32S;
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32s;
+ break;
+
+ case TIME_16s:
+ GenPmCon1 |= V_PCH_LPC_GEN_PMCON_PER_SMI_16S;
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16s;
+ break;
+
+ case TIME_8s:
+ GenPmCon1 |= V_PCH_LPC_GEN_PMCON_PER_SMI_8S;
+ mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_8s;
+ break;
+
+ default:
+ ASSERT (FALSE);
+ break;
+ }
+
+ MmioWrite16 (PciD31F0RegBase + R_PCH_LPC_GEN_PMCON_1, GenPmCon1);
+
+ ///
+ /// Restart the timer here, just need to clear the SMI
+ ///
+ PchSmmClearSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);
+ } else {
+ PchSmmDisableSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);
+ }
+
+ if (mTimers[SWSMI_TIMER].NumChildren > 0) {
+ ///
+ /// ICH9, ICH10 and PCH share the same bit positions for SW SMI Rate settings
+ ///
+ GenPmCon3 = MmioRead8 (PciD31F0RegBase + R_PCH_LPC_GEN_PMCON_3);
+ GenPmCon3 &= ~B_PCH_LPC_GEN_PMCON_SWSMI_RTSL;
+ switch (mTimers[SWSMI_TIMER].MinReqInterval) {
+ case TIME_64ms:
+ GenPmCon3 |= V_PCH_LPC_GEN_PMCON_SWSMI_RTSL_64MS;
+ mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_64ms;
+ break;
+
+ case TIME_32ms:
+ GenPmCon3 |= V_PCH_LPC_GEN_PMCON_SWSMI_RTSL_32MS;
+ mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_32ms;
+ break;
+
+ case TIME_16ms:
+ GenPmCon3 |= V_PCH_LPC_GEN_PMCON_SWSMI_RTSL_16MS;
+ mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_16ms;
+ break;
+
+ case TIME_1_5ms:
+ GenPmCon3 |= V_PCH_LPC_GEN_PMCON_SWSMI_RTSL_1_5MS;
+ mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_1_5ms;
+ break;
+
+ default:
+ ASSERT (FALSE);
+ break;
+ }
+ ///
+ /// SWSMI_RATE_SEL BIT (D31:F0:A4h[7:6]) bits are in RTC well
+ ///
+ MmioWrite8 (PciD31F0RegBase + R_PCH_LPC_GEN_PMCON_3, GenPmCon3);
+
+ ///
+ /// Restart the timer here, need to disable, clear, then enable to restart this timer
+ ///
+ PchSmmDisableSource (&mTIMER_SOURCE_DESCS[SWSMI_TIMER]);
+ PchSmmClearSource (&mTIMER_SOURCE_DESCS[SWSMI_TIMER]);
+ PchSmmEnableSource (&mTIMER_SOURCE_DESCS[SWSMI_TIMER]);
+ } else {
+ PchSmmDisableSource (&mTIMER_SOURCE_DESCS[SWSMI_TIMER]);
+ }
+}
+
+/**
+ This services returns the next SMI tick period that is supported by the chipset.
+ The order returned is from longest to shortest interval period.
+
+ @param[in] This Pointer to the EFI_SMM_PERIODIC_TIMER_DISPATCH_PROTOCOL instance.
+ @param[in, out] SmiTickInterval Pointer to pointer of the next shorter SMI interval period that is supported by the child.
+
+ @retval EFI_SUCCESS The service returned successfully.
+ @retval EFI_INVALID_PARAMETER The parameter SmiTickInterval is invalid.
+**/
+EFI_STATUS
+PchSmmPeriodicTimerDispatchGetNextShorterInterval (
+ IN EFI_SMM_PERIODIC_TIMER_DISPATCH_PROTOCOL *This,
+ IN OUT UINT64 **SmiTickInterval
+ )
+{
+ TIMER_INTERVAL *IntervalPointer;
+
+ ASSERT (SmiTickInterval != NULL);
+
+ IntervalPointer = (TIMER_INTERVAL *) *SmiTickInterval;
+
+ if (IntervalPointer == NULL) {
+ ///
+ /// The first time child requesting an interval
+ ///
+ IntervalPointer = &mSmmPeriodicTimerIntervals[0];
+ } else if (IntervalPointer == &mSmmPeriodicTimerIntervals[NUM_INTERVALS - 1]) {
+ ///
+ /// At end of the list
+ ///
+ IntervalPointer = NULL;
+ } else {
+ if ((IntervalPointer >= &mSmmPeriodicTimerIntervals[0]) &&
+ (IntervalPointer < &mSmmPeriodicTimerIntervals[NUM_INTERVALS - 1])
+ ) {
+ ///
+ /// Get the next interval in the list
+ ///
+ IntervalPointer++;
+ } else {
+ ///
+ /// Input is out of range
+ ///
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (IntervalPointer != NULL) {
+ *SmiTickInterval = &IntervalPointer->Interval;
+ } else {
+ *SmiTickInterval = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is responsible for calculating and enabling any timers that are required
+ to dispatch messages to children. The SrcDesc argument isn't acutally used.
+
+ @param[in] SrcDesc Pointer to the PCH_SMM_SOURCE_DESC instance.
+
+ @retval None.
+**/
+VOID
+EFIAPI
+PchSmmPeriodicTimerClearSource (
+ IN PCH_SMM_SOURCE_DESC *SrcDesc
+ )
+{
+ PchSmmPeriodicTimerProgramTimers ();
+}