audk/QuarkSocPkg/QuarkNorthCluster/Smm/DxeSmm/QncSmmDispatcher/QNC/QNCSmmPeriodicTimer.c

431 lines
11 KiB
C

/** @file
File to contain all the hardware specific stuff for the Periodical Timer dispatch protocol.
Copyright (c) 2013-2016 Intel Corporation.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
//
// Include common header file for this module.
//
#include "CommonHeader.h"
#include "QNCSmmHelpers.h"
typedef enum {
PERIODIC_TIMER = 0,
NUM_TIMERS
} SUPPORTED_TIMER;
typedef struct _TIMER_INTERVAL
{
UINT64 Interval;
UINT8 AssociatedTimer;
} TIMER_INTERVAL;
//
// 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 */
// PMCW (GPE+28h) [2:0] Periodic SMI Rate selection
// 000 1.5ms
// 001 16ms
// 010 32ms
// 011 64ms
// 100 8s
// 101 16s
// 110 32s
// 111 64s
typedef enum {
INDEX_TIME_1_5ms = 0,
INDEX_TIME_16ms,
INDEX_TIME_32ms,
INDEX_TIME_64ms,
INDEX_TIME_8s,
INDEX_TIME_16s,
INDEX_TIME_32s,
INDEX_TIME_64s,
INDEX_TIME_MAX
} TIMER_INTERVAL_INDEX;
TIMER_INTERVAL mSmmPeriodicTimerIntervals[INDEX_TIME_MAX] = {
{TIME_1_5ms, PERIODIC_TIMER},
{TIME_16ms, PERIODIC_TIMER},
{TIME_32ms, PERIODIC_TIMER},
{TIME_64ms, PERIODIC_TIMER},
{ TIME_8s, PERIODIC_TIMER },
{TIME_16s, PERIODIC_TIMER},
{TIME_32s, PERIODIC_TIMER},
{TIME_64s, PERIODIC_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];
QNC_SMM_SOURCE_DESC mTIMER_SOURCE_DESCS[NUM_TIMERS] = {
{
QNC_SMM_NO_FLAGS,
{
{{GPE_ADDR_TYPE, {R_QNC_GPE0BLK_SMIE}}, S_QNC_GPE0BLK_SMIE, N_QNC_GPE0BLK_SMIE_SWT},
NULL_BIT_DESC_INITIALIZER
},
{
{{GPE_ADDR_TYPE, {R_QNC_GPE0BLK_SMIS}}, S_QNC_GPE0BLK_SMIS, N_QNC_GPE0BLK_SMIS_SWT}
}
}
};
VOID
QNCSmmPeriodicTimerProgramTimers(
VOID
);
TIMER_INTERVAL *
ContextToTimerInterval (
IN QNC_SMM_CONTEXT *RegisterContext
)
{
UINTN loopvar;
//
// Determine which timer this child is using
//
for (loopvar = 0; loopvar < INDEX_TIME_MAX; loopvar++) {
if (((RegisterContext->PeriodicTimer.SmiTickInterval == 0) && (RegisterContext->PeriodicTimer.Period >= mSmmPeriodicTimerIntervals[loopvar].Interval)) ||
(RegisterContext->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;
}
EFI_STATUS
MapPeriodicTimerToSrcDesc (
IN QNC_SMM_CONTEXT *RegisterContext,
OUT QNC_SMM_SOURCE_DESC *SrcDesc
)
{
TIMER_INTERVAL *TimerInterval;
//
// Figure out which timer the child is requesting and
// send back the source description
//
TimerInterval = ContextToTimerInterval (RegisterContext);
if (TimerInterval == NULL) {
return EFI_INVALID_PARAMETER;
}
CopyMem (SrcDesc, &mTIMER_SOURCE_DESCS[TimerInterval->AssociatedTimer], sizeof (QNC_SMM_SOURCE_DESC));;
//
// Program the value of the interval into hardware
//
QNCSmmPeriodicTimerProgramTimers ();
return EFI_SUCCESS;
}
VOID
PeriodicTimerGetContext (
IN DATABASE_RECORD *Record,
OUT QNC_SMM_CONTEXT *HwContext
)
{
TIMER_INTERVAL *TimerInterval;
ASSERT (Record->ProtocolType == PeriodicTimerType);
TimerInterval = ContextToTimerInterval (&Record->ChildContext);
if (TimerInterval != NULL) {
//
// 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->CommBuffer.PeriodicTimer.ElapsedTime += TimerInterval->Interval;
CopyMem (HwContext, &Record->ChildContext, sizeof (QNC_SMM_CONTEXT));
}
}
BOOLEAN
PeriodicTimerCmpContext (
IN QNC_SMM_CONTEXT *HwContext,
IN QNC_SMM_CONTEXT *ChildContext
)
{
DATABASE_RECORD *Record;
Record = DATABASE_RECORD_FROM_CONTEXT (ChildContext);
if (Record->CommBuffer.PeriodicTimer.ElapsedTime >= ChildContext->PeriodicTimer.Period) {
//
// This child should be dispatched
// The timer will be restarted on the "ClearSource" call.
//
return TRUE;
} else {
return FALSE;
}
}
VOID
PeriodicTimerGetBuffer (
IN DATABASE_RECORD * Record
)
{
//
// CommBuffer has been updated by PeriodicTimerGetContext, so return directly
//
return;
}
VOID
QNCSmmPeriodicTimerProgramTimers (
VOID
)
{
UINT32 GpePmcwValue;
SUPPORTED_TIMER Timer;
DATABASE_RECORD *RecordInDb;
LIST_ENTRY *LinkInDb;
TIMER_INTERVAL *TimerInterval;
//
// Find the minimum required interval for each timer
//
for (Timer = (SUPPORTED_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) {
Timer = (SUPPORTED_TIMER)((TIMER_INTERVAL *) (TimerInterval))->AssociatedTimer;
ASSERT (Timer >= 0 && Timer < NUM_TIMERS);
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
//
GpePmcwValue = 0;
if (mTimers[PERIODIC_TIMER].NumChildren > 0) {
switch (mTimers[PERIODIC_TIMER].MinReqInterval) {
case TIME_64s:
GpePmcwValue = INDEX_TIME_64s;
mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64s;
break;
case TIME_32s:
GpePmcwValue = INDEX_TIME_32s;
mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32s;
break;
case TIME_16s:
GpePmcwValue = INDEX_TIME_16s;
mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16s;
break;
case TIME_8s:
GpePmcwValue = INDEX_TIME_8s;
mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_8s;
break;
case TIME_64ms:
GpePmcwValue = INDEX_TIME_64ms;
mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64ms;
break;
case TIME_32ms:
GpePmcwValue = INDEX_TIME_32ms;
mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32ms;
break;
case TIME_16ms:
GpePmcwValue = INDEX_TIME_16ms;
mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16ms;
break;
case TIME_1_5ms:
GpePmcwValue = INDEX_TIME_1_5ms;
mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_1_5ms;
break;
default:
ASSERT (FALSE);
break;
};
GpePmcwValue |= B_QNC_GPE0BLK_PMCW_PSE;
IoOr32(((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_PMCW), GpePmcwValue);
//
// Restart the timer here, just need to clear the SMI
//
QNCSmmClearSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);
} else {
QNCSmmDisableSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);
}
}
EFI_STATUS
QNCSmmPeriodicTimerDispatchGetNextShorterInterval (
IN CONST EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL *This,
IN OUT UINT64 **SmiTickInterval
)
/*++
Routine Description:
This services returns the next SMI tick period that is supported by the chipset.
The order returned is from longest to shortest interval period.
Arguments:
This - Pointer to the EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL instance.
SmiTickInterval - Pointer to pointer of the next shorter SMI interval period that is supported by the child.
Returns:
EFI_SUCCESS - The service returned successfully.
EFI_INVALID_PARAMETER - The parameter SmiTickInterval is invalid.
--*/
{
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[INDEX_TIME_MAX - 1]) {
//
// At end of the list
//
IntervalPointer = NULL;
} else {
if ((IntervalPointer >= &mSmmPeriodicTimerIntervals[0]) &&
(IntervalPointer < &mSmmPeriodicTimerIntervals[INDEX_TIME_MAX - 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;
}
VOID
QNCSmmPeriodicTimerClearSource (
IN QNC_SMM_SOURCE_DESC *SrcDesc
)
/*++
Routine Description:
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.
Arguments:
SrcDesc - Pointer to the QNC_SMM_SOURCE_DESC instance.
Returns:
None.
--*/
{
DATABASE_RECORD *RecordInDb;
LIST_ENTRY *LinkInDb;
QNCSmmPeriodicTimerProgramTimers ();
//
// Reset Elapsed time
//
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 and Callback
// has been invoked, so reset the ElapsedTime to 0
//
if (RecordInDb->CommBuffer.PeriodicTimer.ElapsedTime >= RecordInDb->ChildContext.PeriodicTimer.Period) {
RecordInDb->CommBuffer.PeriodicTimer.ElapsedTime = 0;
}
}
LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
}
}