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