1 /** @file
2 File to contain all the hardware specific stuff for the Periodical Timer dispatch protocol.
3 
4 Copyright (c) 2013-2015 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 *
ContextToTimerInterval(IN QNC_SMM_CONTEXT * RegisterContext)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
MapPeriodicTimerToSrcDesc(IN QNC_SMM_CONTEXT * RegisterContext,OUT QNC_SMM_SOURCE_DESC * SrcDesc)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
PeriodicTimerGetContext(IN DATABASE_RECORD * Record,OUT QNC_SMM_CONTEXT * HwContext)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     *HwContext = Record->ChildContext;
181   }
182 }
183 
184 BOOLEAN
PeriodicTimerCmpContext(IN QNC_SMM_CONTEXT * HwContext,IN QNC_SMM_CONTEXT * ChildContext)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
PeriodicTimerGetBuffer(IN DATABASE_RECORD * Record)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
QNCSmmPeriodicTimerProgramTimers(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
QNCSmmPeriodicTimerDispatchGetNextShorterInterval(IN CONST EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL * This,IN OUT UINT64 ** SmiTickInterval)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
QNCSmmPeriodicTimerClearSource(IN QNC_SMM_SOURCE_DESC * SrcDesc)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