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