1 /** @file
2
3 Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
4
5
6 This program and the accompanying materials are licensed and made available under
7
8 the terms and conditions of the BSD License that accompanies this distribution.
9
10 The full text of the license may be found at
11
12 http://opensource.org/licenses/bsd-license.php.
13
14
15
16 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
17
18 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19
20
21
22
23 Module Name:
24
25 Platform.c
26
27 Abstract:
28
29 This is a generic template for a child of the IchSmm driver.
30
31
32 --*/
33
34 #include "SmmPlatform.h"
35 #include <Protocol/CpuIo2.h>
36
37
38 //
39 // Local variables
40 //
41 typedef struct {
42 UINT8 Device;
43 UINT8 Function;
44 } EFI_PCI_BUS_MASTER;
45
46 EFI_PCI_BUS_MASTER mPciBm[] = {
47 { PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORTS, PCI_FUNCTION_NUMBER_PCH_PCIE_ROOT_PORT_1 },
48 { PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORTS, PCI_FUNCTION_NUMBER_PCH_PCIE_ROOT_PORT_2 },
49 { PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORTS, PCI_FUNCTION_NUMBER_PCH_PCIE_ROOT_PORT_3 },
50 { PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORTS, PCI_FUNCTION_NUMBER_PCH_PCIE_ROOT_PORT_4 },
51 { PCI_DEVICE_NUMBER_PCH_USB, PCI_FUNCTION_NUMBER_PCH_EHCI }
52 };
53
54
55 UINT16 mAcpiBaseAddr;
56 SYSTEM_CONFIGURATION mSystemConfiguration;
57 EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable;
58 EFI_GLOBAL_NVS_AREA_PROTOCOL *mGlobalNvsAreaPtr;
59
60 UINT16 mPM1_SaveState16;
61 UINT32 mGPE_SaveState32;
62
63 BOOLEAN mSetSmmVariableProtocolSmiAllowed = TRUE;
64
65
66 //
67 // Variables. Need to initialize this from Setup
68 //
69 BOOLEAN mWakeOnLanS5Variable;
70 BOOLEAN mWakeOnRtcVariable;
71 UINT8 mWakeupDay;
72 UINT8 mWakeupHour;
73 UINT8 mWakeupMinute;
74 UINT8 mWakeupSecond;
75
76 //
77 // Use an enum. 0 is Stay Off, 1 is Last State, 2 is Stay On
78 //
79 UINT8 mAcLossVariable;
80
81
82 static
83 UINT8 mTco1Sources[] = {
84 IchnNmi
85 };
86
87 UINTN
88 DevicePathSize (
89 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
90 );
91
92 VOID
93 S4S5ProgClock();
94
95 EFI_STATUS
96 InitRuntimeScriptTable (
97 IN EFI_SYSTEM_TABLE *SystemTable
98 );
99
100 VOID
101 S5SleepWakeOnRtcCallBack (
102 IN EFI_HANDLE DispatchHandle,
103 IN EFI_SMM_SX_DISPATCH_CONTEXT *DispatchContext
104 );
105
106
107 VOID
108 EnableS5WakeOnRtc();
109
110 UINT8
111 HexToBcd(
112 UINT8 HexValue
113 );
114
115 UINT8
116 BcdToHex(
117 IN UINT8 BcdValue
118 );
119
120
121 VOID
122 CpuSmmSxWorkAround(
123 );
124
125 /**
126 Initializes the SMM Handler Driver
InitializePlatformSmm(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)127
128 @param ImageHandle
129 @param SystemTable
130
131 @retval None
132
133 **/
134 EFI_STATUS
135 EFIAPI
136 InitializePlatformSmm (
137 IN EFI_HANDLE ImageHandle,
138 IN EFI_SYSTEM_TABLE *SystemTable
139 )
140 {
141 EFI_STATUS Status;
142 UINT8 Index;
143 EFI_HANDLE Handle;
144 EFI_SMM_POWER_BUTTON_DISPATCH_CONTEXT PowerButtonContext;
145 EFI_SMM_POWER_BUTTON_DISPATCH_PROTOCOL *PowerButtonDispatch;
146 EFI_SMM_ICHN_DISPATCH_CONTEXT IchnContext;
147 EFI_SMM_ICHN_DISPATCH_PROTOCOL *IchnDispatch;
148 EFI_SMM_SX_DISPATCH_PROTOCOL *SxDispatch;
149 EFI_SMM_SX_DISPATCH_CONTEXT EntryDispatchContext;
150 EFI_SMM_SW_DISPATCH_PROTOCOL *SwDispatch;
151 EFI_SMM_SW_DISPATCH_CONTEXT SwContext;
152 UINTN VarSize;
153 EFI_BOOT_MODE BootMode;
154
155 Handle = NULL;
156
157 //
158 // Locate the Global NVS Protocol.
159 //
160 Status = gBS->LocateProtocol (
161 &gEfiGlobalNvsAreaProtocolGuid,
162 NULL,
163 (void **)&mGlobalNvsAreaPtr
164 );
165 ASSERT_EFI_ERROR (Status);
166
167
168 //
169 // Get the ACPI Base Address
170 //
171
172 mAcpiBaseAddr = PchLpcPciCfg16( R_PCH_LPC_ACPI_BASE ) & B_PCH_LPC_ACPI_BASE_BAR;
173
174 VarSize = sizeof(SYSTEM_CONFIGURATION);
175 Status = SystemTable->RuntimeServices->GetVariable(
176 L"Setup",
177 &gEfiSetupVariableGuid,
178 NULL,
179 &VarSize,
180 &mSystemConfiguration
181 );
182 if (EFI_ERROR (Status) || VarSize != sizeof(SYSTEM_CONFIGURATION)) {
183 //The setup variable is corrupted
184 VarSize = sizeof(SYSTEM_CONFIGURATION);
185 Status = SystemTable->RuntimeServices->GetVariable(
186 L"SetupRecovery",
187 &gEfiSetupVariableGuid,
188 NULL,
189 &VarSize,
190 &mSystemConfiguration
191 );
192 ASSERT_EFI_ERROR (Status);
193 }
194 if (!EFI_ERROR(Status)) {
195 mAcLossVariable = mSystemConfiguration.StateAfterG3;
196
197 //
198 // If LAN is disabled, WOL function should be disabled too.
199 //
200 if (mSystemConfiguration.Lan == 0x01){
201 mWakeOnLanS5Variable = mSystemConfiguration.WakeOnLanS5;
202 } else {
203 mWakeOnLanS5Variable = FALSE;
204 }
205
206 mWakeOnRtcVariable = mSystemConfiguration.WakeOnRtcS5;
207 }
208
209 BootMode = GetBootModeHob ();
210
211 //
212 // Get the Power Button protocol
213 //
214 Status = gBS->LocateProtocol(
215 &gEfiSmmPowerButtonDispatchProtocolGuid,
216 NULL,
217 (void **)&PowerButtonDispatch
218 );
219 ASSERT_EFI_ERROR(Status);
220
221 if (BootMode != BOOT_ON_FLASH_UPDATE) {
222 //
223 // Register for the power button event
224 //
225 PowerButtonContext.Phase = PowerButtonEntry;
226 Status = PowerButtonDispatch->Register(
227 PowerButtonDispatch,
228 PowerButtonCallback,
229 &PowerButtonContext,
230 &Handle
231 );
232 ASSERT_EFI_ERROR(Status);
233 }
234 //
235 // Get the Sx dispatch protocol
236 //
237 Status = gBS->LocateProtocol (
238 &gEfiSmmSxDispatchProtocolGuid,
239 NULL,
240 (void **)&SxDispatch
241 );
242 ASSERT_EFI_ERROR(Status);
243
244 //
245 // Register entry phase call back function
246 //
247 EntryDispatchContext.Type = SxS3;
248 EntryDispatchContext.Phase = SxEntry;
249
250 Status = SxDispatch->Register (
251 SxDispatch,
252 (EFI_SMM_SX_DISPATCH)SxSleepEntryCallBack,
253 &EntryDispatchContext,
254 &Handle
255 );
256
257
258 EntryDispatchContext.Type = SxS4;
259
260 Status = SxDispatch->Register (
261 SxDispatch,
262 S4S5CallBack,
263 &EntryDispatchContext,
264 &Handle
265 );
266 ASSERT_EFI_ERROR(Status);
267
268
269 EntryDispatchContext.Type = SxS5;
270
271 Status = SxDispatch->Register (
272 SxDispatch,
273 S4S5CallBack,
274 &EntryDispatchContext,
275 &Handle
276 );
277 ASSERT_EFI_ERROR(Status);
278
279 Status = SxDispatch->Register (
280 SxDispatch,
281 S5SleepAcLossCallBack,
282 &EntryDispatchContext,
283 &Handle
284 );
285 ASSERT_EFI_ERROR(Status);
286
287 //
288 // Get the Sw dispatch protocol
289 //
290 Status = gBS->LocateProtocol (
291 &gEfiSmmSwDispatchProtocolGuid,
292 NULL,
293 (void **)&SwDispatch
294 );
295 ASSERT_EFI_ERROR(Status);
296
297 //
298 // Register ACPI enable handler
299 //
300 SwContext.SwSmiInputValue = ACPI_ENABLE;
301 Status = SwDispatch->Register (
302 SwDispatch,
303 EnableAcpiCallback,
304 &SwContext,
305 &Handle
306 );
307 ASSERT_EFI_ERROR(Status);
308
309 //
310 // Register ACPI disable handler
311 //
312 SwContext.SwSmiInputValue = ACPI_DISABLE;
313 Status = SwDispatch->Register (
314 SwDispatch,
315 DisableAcpiCallback,
316 &SwContext,
317 &Handle
318 );
319 ASSERT_EFI_ERROR(Status);
320
321
322 //
323 // Register for SmmReadyToBootCallback
324 //
325 SwContext.SwSmiInputValue = SMI_SET_SMMVARIABLE_PROTOCOL;
326 Status = SwDispatch->Register(
327 SwDispatch,
328 SmmReadyToBootCallback,
329 &SwContext,
330 &Handle
331 );
332 ASSERT_EFI_ERROR(Status);
333
334 //
335 // Get the ICHn protocol
336 //
337 Status = gBS->LocateProtocol(
338 &gEfiSmmIchnDispatchProtocolGuid,
339 NULL,
340 (void **)&IchnDispatch
341 );
342 ASSERT_EFI_ERROR(Status);
343
344 //
345 // Register for the events that may happen that we do not care.
346 // This is true for SMI related to TCO since TCO is enabled by BIOS WP
347 //
348 for (Index = 0; Index < sizeof(mTco1Sources)/sizeof(UINT8); Index++) {
349 IchnContext.Type = mTco1Sources[Index];
350 Status = IchnDispatch->Register(
351 IchnDispatch,
352 (EFI_SMM_ICHN_DISPATCH)DummyTco1Callback,
353 &IchnContext,
354 &Handle
355 );
356 ASSERT_EFI_ERROR( Status );
357 }
358
359 //
360 // Lock TCO_EN bit.
361 //
362 IoWrite16( mAcpiBaseAddr + R_PCH_TCO_CNT, IoRead16( mAcpiBaseAddr + R_PCH_TCO_CNT ) | B_PCH_TCO_CNT_LOCK );
363
364 //
365 // Set to power on from G3 dependent on WOL instead of AC Loss variable in order to support WOL from G3 feature.
366 //
367 //
368 // Set wake from G3 dependent on AC Loss variable and Wake On LAN variable.
369 // This is because no matter how, if WOL enabled or AC Loss variable not disabled, the board needs to wake from G3 to program the LAN WOL settings.
370 // This needs to be done after LAN enable/disable so that the PWR_FLR state clear not impacted the WOL from G3 feature.
371 //
372 if (mAcLossVariable != 0x00) {
373 SetAfterG3On (TRUE);
374 } else {
375 SetAfterG3On (FALSE);
376 }
377
378
379
380
381 return EFI_SUCCESS;
382 }
383
384 VOID
385 EFIAPI
386 SmmReadyToBootCallback (
387 IN EFI_HANDLE DispatchHandle,
388 IN EFI_SMM_SW_DISPATCH_CONTEXT *DispatchContext
389 )
390 {
391 EFI_STATUS Status;
392
393 if (mSetSmmVariableProtocolSmiAllowed)
394 {
395 //
396 // It is okay to use gBS->LocateProtocol here because
397 // we are still in trusted execution.
398 //
399 Status = gBS->LocateProtocol(
400 &gEfiSmmVariableProtocolGuid,
401 NULL,
402 (void **)&mSmmVariable
403 );
404
405 ASSERT_EFI_ERROR(Status);
406
407 //
408 // mSetSmmVariableProtocolSmiAllowed will prevent this function from
409 // being executed more than 1 time.
410 //
411 mSetSmmVariableProtocolSmiAllowed = FALSE;
412 }
413
414 }
415
416 /**
417
SxSleepEntryCallBack(IN EFI_HANDLE DispatchHandle,IN EFI_SMM_SX_DISPATCH_CONTEXT * DispatchContext)418 @param DispatchHandle The handle of this callback, obtained when registering
419 @param DispatchContext The predefined context which contained sleep type and phase
420
421
422 @retval EFI_SUCCESS Operation successfully performed
423
424 **/
425 EFI_STATUS
426 EFIAPI
427 SxSleepEntryCallBack (
428 IN EFI_HANDLE DispatchHandle,
429 IN EFI_SMM_SX_DISPATCH_CONTEXT *DispatchContext
430 )
431 {
432 EFI_STATUS Status;
433
434 Status = SaveRuntimeScriptTable ();
435 if (EFI_ERROR(Status)) {
436 return Status;
437 }
438
439 //
440 // Workaround for S3 wake hang if C State is enabled
441 //
442 CpuSmmSxWorkAround();
443
444 return EFI_SUCCESS;
445 }
446
447 VOID
448 CpuSmmSxWorkAround(
449 )
450 {
451 UINT64 MsrValue;
452
453 MsrValue = AsmReadMsr64 (0xE2);
454
455 if (MsrValue & BIT15) {
456 return;
ClearP2PBusMaster()457 }
458
459 if (MsrValue & BIT10) {
460 MsrValue &= ~BIT10;
461 AsmWriteMsr64 (0xE2, MsrValue);
462 }
463 }
464
465 VOID
466 ClearP2PBusMaster(
467 )
468 {
469 UINT8 Command;
470 UINT8 Index;
471
472 for (Index = 0; Index < sizeof(mPciBm)/sizeof(EFI_PCI_BUS_MASTER); Index++) {
473 Command = MmioRead8 (
474 MmPciAddress (0,
475 DEFAULT_PCI_BUS_NUMBER_PCH,
476 mPciBm[Index].Device,
477 mPciBm[Index].Function,
478 PCI_COMMAND_OFFSET
479 )
480 );
481 Command &= ~EFI_PCI_COMMAND_BUS_MASTER;
482 MmioWrite8 (
483 MmPciAddress (0,
484 DEFAULT_PCI_BUS_NUMBER_PCH,
485 mPciBm[Index].Device,
486 mPciBm[Index].Function,
487 PCI_COMMAND_OFFSET
488 ),
489 Command
490 );
SetAfterG3On(BOOLEAN Enable)491 }
492 }
493
494 /**
495
496 Set the AC Loss to turn on or off.
497
498 **/
499 VOID
500 SetAfterG3On (
501 BOOLEAN Enable
502 )
503 {
504 UINT8 PmCon1;
505
506 //
507 // ICH handling portion
508 //
509 PmCon1 = MmioRead8 ( PMC_BASE_ADDRESS + R_PCH_PMC_GEN_PMCON_1 );
510 PmCon1 &= ~B_PCH_PMC_GEN_PMCON_AFTERG3_EN;
511 if (Enable) {
512 PmCon1 |= B_PCH_PMC_GEN_PMCON_AFTERG3_EN;
513 }
514 MmioWrite8 (PMC_BASE_ADDRESS + R_PCH_PMC_GEN_PMCON_1, PmCon1);
515
516 }
517
518 /**
519 When a power button event happens, it shuts off the machine
520
521 **/
522 VOID
523 EFIAPI
524 PowerButtonCallback (
525 IN EFI_HANDLE DispatchHandle,
526 IN EFI_SMM_POWER_BUTTON_DISPATCH_CONTEXT *DispatchContext
527 )
528 {
529 //
530 // Check what the state to return to after AC Loss. If Last State, then
531 // set it to Off.
532 //
533 UINT16 data16;
534
535 if (mWakeOnRtcVariable) {
536 EnableS5WakeOnRtc();
537 }
538
539 if (mAcLossVariable == 1) {
540 SetAfterG3On (TRUE);
541 }
542
543 ClearP2PBusMaster();
544
545 //
546 // Program clock chip
547 //
548 S4S5ProgClock();
549
550
551 data16 = (UINT16)(IoRead16(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_EN));
552 data16 &= B_PCH_ACPI_GPE0a_EN_PCI_EXP;
553
554
555 //
556 // Clear Sleep SMI Status
557 //
558 IoWrite16 (mAcpiBaseAddr + R_PCH_SMI_STS,
559 (UINT16)(IoRead16 (mAcpiBaseAddr + R_PCH_SMI_STS) | B_PCH_SMI_STS_ON_SLP_EN));
560 //
561 // Clear Sleep Type Enable
562 //
563 IoWrite16 (mAcpiBaseAddr + R_PCH_SMI_EN,
564 (UINT16)(IoRead16 (mAcpiBaseAddr + R_PCH_SMI_EN) & (~B_PCH_SMI_EN_ON_SLP_EN)));
565
566 //
567 // Clear Power Button Status
568 //
569 IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_STS, B_PCH_ACPI_PM1_STS_PWRBTN);
570
571 //
572 // Shut it off now!
573 //
574 IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT, V_PCH_ACPI_PM1_CNT_S5);
575 IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT, B_PCH_ACPI_PM1_CNT_SLP_EN | V_PCH_ACPI_PM1_CNT_S5);
576
577 //
578 // Should not return
579 //
580 CpuDeadLoop();
581 }
582
S5SleepAcLossCallBack(IN EFI_HANDLE DispatchHandle,IN EFI_SMM_SX_DISPATCH_CONTEXT * DispatchContext)583
584 /**
585 @param DispatchHandle - The handle of this callback, obtained when registering
586
587 @param DispatchContext - The predefined context which contained sleep type and phase
588
589 **/
590 VOID
591 EFIAPI
592 S5SleepAcLossCallBack (
593 IN EFI_HANDLE DispatchHandle,
594 IN EFI_SMM_SX_DISPATCH_CONTEXT *DispatchContext
595 )
596 {
597 //
598 // Check what the state to return to after AC Loss. If Last State, then
599 // set it to Off.
600 //
601 if (mAcLossVariable == 1) {
602 SetAfterG3On (TRUE);
603 }
604 }
605
606 /**
S4S5CallBack(IN EFI_HANDLE DispatchHandle,IN EFI_SMM_SX_DISPATCH_CONTEXT * DispatchContext)607
608 @param DispatchHandle The handle of this callback, obtained when registering
609 @param DispatchContext The predefined context which contained sleep type and phase
610
611 @retval Clears the Save State bit in the clock.
612
613 **/
614 VOID
615 EFIAPI
616 S4S5CallBack (
617 IN EFI_HANDLE DispatchHandle,
618 IN EFI_SMM_SX_DISPATCH_CONTEXT *DispatchContext
619 )
620 {
621
622 UINT32 Data32;
623
624 //
625 // Enable/Disable USB Charging
626 //
627 if (mSystemConfiguration.UsbCharging == 0x01) {
628 Data32 = IoRead32 (GPIO_BASE_ADDRESS + R_PCH_GPIO_SC_LVL);
629 Data32 |= BIT8;
630 IoWrite32(GPIO_BASE_ADDRESS + R_PCH_GPIO_SC_LVL, Data32);
631 }
632
633 }
634
635
636 VOID
637 S4S5ProgClock()
638 {
639 }
640
641 /**
642 SMI handler to enable ACPI mode
643
644 Dispatched on reads from APM port with value 0xA0
645
646 Disables the SW SMI Timer.
647 ACPI events are disabled and ACPI event status is cleared.
648 SCI mode is then enabled.
649
650 Disable SW SMI Timer
651
652 Clear all ACPI event status and disable all ACPI events
653 Disable PM sources except power button
654 Clear status bits
655
656 Disable GPE0 sources
657 Clear status bits
658
659 Disable GPE1 sources
660 Clear status bits
661
662 Guarantee day-of-month alarm is invalid (ACPI 5.0 Section 4.8.2.4 "Real Time Clock Alarm")
663
664 Enable SCI
EnableAcpiCallback(IN EFI_HANDLE DispatchHandle,IN EFI_SMM_SW_DISPATCH_CONTEXT * DispatchContext)665
666 @param DispatchHandle - EFI Handle
667 @param DispatchContext - Pointer to the EFI_SMM_SW_DISPATCH_CONTEXT
668
669 @retval Nothing
670
671 **/
672 VOID
673 EFIAPI
674 EnableAcpiCallback (
675 IN EFI_HANDLE DispatchHandle,
676 IN EFI_SMM_SW_DISPATCH_CONTEXT *DispatchContext
677 )
678 {
679 UINT32 SmiEn;
680 UINT16 Pm1Cnt;
681 UINT16 wordValue;
682 UINT32 RegData32;
683
684 //
685 // Disable SW SMI Timer
686 //
687 SmiEn = IoRead32(mAcpiBaseAddr + R_PCH_SMI_EN);
688 SmiEn &= ~B_PCH_SMI_STS_SWSMI_TMR;
689 IoWrite32(mAcpiBaseAddr + R_PCH_SMI_EN, SmiEn);
690
691 wordValue = IoRead16(mAcpiBaseAddr + R_PCH_ACPI_PM1_STS);
692 if(wordValue & B_PCH_ACPI_PM1_STS_WAK) {
693 IoWrite32((mAcpiBaseAddr + R_PCH_ACPI_GPE0a_EN), 0x0000);
694 IoWrite32((mAcpiBaseAddr + R_PCH_ACPI_GPE0a_STS), 0xffffffff);
695 }
696 else {
697 mPM1_SaveState16 = IoRead16(mAcpiBaseAddr + R_PCH_ACPI_PM1_EN);
698
699 //
700 // Disable PM sources except power button
701 //
702 // power button is enabled only for PCAT. Disabled it on Tablet platform
703 //
704 IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_EN, B_PCH_ACPI_PM1_EN_PWRBTN);
705 IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_STS, 0xffff);
706
707 mGPE_SaveState32 = IoRead16(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_EN);
708 IoWrite32(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_EN, 0x0000);
709 IoWrite32(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_STS, 0xffffffff);
710
711 }
712
713 //
714 // Guarantee day-of-month alarm is invalid (ACPI 5.0 Section 4.8.2.4 "Real Time Clock Alarm")
715 // Clear Status D reg VM bit, Date of month Alarm to make Data in CMOS RAM is no longer Valid
716 //
717 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_D);
718 IoWrite8 (PCAT_RTC_DATA_REGISTER, 0x0);
719
720
721 RegData32 = IoRead32(ACPI_BASE_ADDRESS + R_PCH_ALT_GP_SMI_EN);
722 RegData32 &= ~(BIT7);
723 IoWrite32((ACPI_BASE_ADDRESS + R_PCH_ALT_GP_SMI_EN), RegData32);
724
725
726 //
727 // Enable SCI
728 //
729 Pm1Cnt = IoRead16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT);
730 Pm1Cnt |= B_PCH_ACPI_PM1_CNT_SCI_EN;
731 IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT, Pm1Cnt);
732
733
734 }
735
736 /**
737 SMI handler to disable ACPI mode
738
739 Dispatched on reads from APM port with value 0xA1
740
741 ACPI events are disabled and ACPI event status is cleared.
742 SCI mode is then disabled.
743 Clear all ACPI event status and disable all ACPI events
744 Disable PM sources except power button
745 Clear status bits
746 Disable GPE0 sources
747 Clear status bits
748 Disable GPE1 sources
749 Clear status bits
750 Disable SCI
DisableAcpiCallback(IN EFI_HANDLE DispatchHandle,IN EFI_SMM_SW_DISPATCH_CONTEXT * DispatchContext)751
752 @param DispatchHandle - EFI Handle
753 @param DispatchContext - Pointer to the EFI_SMM_SW_DISPATCH_CONTEXT
754
755 @retval Nothing
756
757 **/
758 VOID
759 EFIAPI
760 DisableAcpiCallback (
761 IN EFI_HANDLE DispatchHandle,
762 IN EFI_SMM_SW_DISPATCH_CONTEXT *DispatchContext
763 )
764 {
765 UINT16 Pm1Cnt;
766
767 IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_STS, 0xffff);
768 IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_EN, mPM1_SaveState16);
769
770 IoWrite32(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_STS, 0xffffffff);
771 IoWrite32(mAcpiBaseAddr + R_PCH_ACPI_GPE0a_EN, mGPE_SaveState32);
772
773 //
774 // Disable SCI
775 //
776 Pm1Cnt = IoRead16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT);
777 Pm1Cnt &= ~B_PCH_ACPI_PM1_CNT_SCI_EN;
778 IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT, Pm1Cnt);
779
780 }
781
782 /**
783 When an unknown event happen.
784
785 @retval None
786
787 **/
DevicePathSize(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)788 VOID
789 DummyTco1Callback (
790 IN EFI_HANDLE DispatchHandle,
791 IN EFI_SMM_ICHN_DISPATCH_CONTEXT *DispatchContext
792 )
793 {
794 }
795
796 UINTN
797 DevicePathSize (
798 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
799 )
800 {
801 EFI_DEVICE_PATH_PROTOCOL *Start;
802
803 if (DevicePath == NULL) {
804 return 0;
805 }
806
807 //
808 // Search for the end of the device path structure
809 //
810 Start = DevicePath;
811 while (!IsDevicePathEnd (DevicePath)) {
812 DevicePath = NextDevicePathNode (DevicePath);
813 }
814
815 //
816 // Compute the size and add back in the size of the end device path structure
817 //
818 return ((UINTN)DevicePath - (UINTN)Start) + sizeof(EFI_DEVICE_PATH_PROTOCOL);
S5SleepWakeOnRtcCallBack(IN EFI_HANDLE DispatchHandle,IN EFI_SMM_SX_DISPATCH_CONTEXT * DispatchContext)819 }
820
821 /**
822
823 @param DispatchHandle The handle of this callback, obtained when registering
824 @param DispatchContext The predefined context which contained sleep type and phase
825
826 **/
827 VOID
828 S5SleepWakeOnRtcCallBack (
829 IN EFI_HANDLE DispatchHandle,
830 IN EFI_SMM_SX_DISPATCH_CONTEXT *DispatchContext
831 )
832 {
833 EnableS5WakeOnRtc();
834 }
835
836 /**
EnableS5WakeOnRtc()837
838 @retval 1. Check Alarm interrupt is not set.
839 2. Clear Alarm interrupt.
840 2. Set RTC wake up date and time.
841 2. Enable RTC wake up alarm.
842 3. Enable ICH PM1 EN Bit 10(RTC_EN)
843
844 **/
845 VOID
846 EnableS5WakeOnRtc()
847 {
848 UINT8 CmosData;
849 UINTN i;
850 EFI_STATUS Status;
851 UINTN VarSize;
852
853 //
854 // make sure EFI_SMM_VARIABLE_PROTOCOL is available
855 //
856 if (!mSmmVariable) {
857 return;
858 }
859
860 VarSize = sizeof(SYSTEM_CONFIGURATION);
861
862 //
863 // read the variable into the buffer
864 //
865 Status = mSmmVariable->SmmGetVariable(
866 L"Setup",
867 &gEfiSetupVariableGuid,
868 NULL,
869 &VarSize,
870 &mSystemConfiguration
871 );
872 if (EFI_ERROR(Status) || VarSize != sizeof(SYSTEM_CONFIGURATION)) {
873 //The setup variable is corrupted
874 VarSize = sizeof(SYSTEM_CONFIGURATION);
875 Status = mSmmVariable->SmmGetVariable(
876 L"SetupRecovery",
877 &gEfiSetupVariableGuid,
878 NULL,
879 &VarSize,
880 &mSystemConfiguration
881 );
882 ASSERT_EFI_ERROR (Status);
883 }
884
885 if (!mSystemConfiguration.WakeOnRtcS5) {
886 return;
887 }
888 mWakeupDay = HexToBcd((UINT8)mSystemConfiguration.RTCWakeupDate);
889 mWakeupHour = HexToBcd((UINT8)mSystemConfiguration.RTCWakeupTimeHour);
890 mWakeupMinute = HexToBcd((UINT8)mSystemConfiguration.RTCWakeupTimeMinute);
891 mWakeupSecond = HexToBcd((UINT8)mSystemConfiguration.RTCWakeupTimeSecond);
892
893 //
894 // Check RTC alarm interrupt is enabled. If enabled, someone already
895 // grabbed RTC alarm. Just return.
896 //
897 IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_B);
898 if(IoRead8(PCAT_RTC_DATA_REGISTER) & B_RTC_ALARM_INT_ENABLE){
899 return;
900 }
901
902 //
903 // Set Date
904 //
905 IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_D);
906 CmosData = IoRead8(PCAT_RTC_DATA_REGISTER);
907 CmosData &= ~(B_RTC_DATE_ALARM_MASK);
908 CmosData |= mWakeupDay ;
909 for(i = 0 ; i < 0xffff ; i++){
910 IoWrite8(PCAT_RTC_DATA_REGISTER, CmosData);
911 SmmStall(1);
912 if(((CmosData = IoRead8(PCAT_RTC_DATA_REGISTER)) & B_RTC_DATE_ALARM_MASK)
913 == mWakeupDay){
914 break;
915 }
916 }
917
918 //
919 // Set Second
920 //
921 IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_SECOND_ALARM);
922 for(i = 0 ; i < 0xffff ; i++){
923 IoWrite8(PCAT_RTC_DATA_REGISTER, mWakeupSecond);
924 SmmStall(1);
925 if(IoRead8(PCAT_RTC_DATA_REGISTER) == mWakeupSecond){
926 break;
927 }
928 }
929
930 //
931 // Set Minute
932 //
933 IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_MINUTE_ALARM);
934 for(i = 0 ; i < 0xffff ; i++){
935 IoWrite8(PCAT_RTC_DATA_REGISTER, mWakeupMinute);
936 SmmStall(1);
937 if(IoRead8(PCAT_RTC_DATA_REGISTER) == mWakeupMinute){
938 break;
939 }
940 }
941
942 //
943 // Set Hour
944 //
945 IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_HOUR_ALARM);
946 for(i = 0 ; i < 0xffff ; i++){
947 IoWrite8(PCAT_RTC_DATA_REGISTER, mWakeupHour);
948 SmmStall(1);
949 if(IoRead8(PCAT_RTC_DATA_REGISTER) == mWakeupHour){
950 break;
951 }
952 }
953
954 //
955 // Wait for UIP to arm RTC alarm
956 //
957 IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_A);
958 while (IoRead8(PCAT_RTC_DATA_REGISTER) & 0x80);
959
960 //
961 // Read RTC register 0C to clear pending RTC interrupts
962 //
963 IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_C);
964 IoRead8(PCAT_RTC_DATA_REGISTER);
965
966 //
967 // Enable RTC Alarm Interrupt
968 //
969 IoWrite8(PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_B);
970 IoWrite8(PCAT_RTC_DATA_REGISTER, IoRead8(PCAT_RTC_DATA_REGISTER) | B_RTC_ALARM_INT_ENABLE);
971
972 //
973 // Clear ICH RTC Status
974 //
975 IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_STS, B_PCH_ACPI_PM1_STS_RTC);
976
977 //
978 // Enable ICH RTC event
979 //
980 IoWrite16(mAcpiBaseAddr + R_PCH_ACPI_PM1_EN,
981 (UINT16)(IoRead16(mAcpiBaseAddr + R_PCH_ACPI_PM1_EN) | B_PCH_ACPI_PM1_EN_RTC));
982 }
983
984 UINT8
985 HexToBcd(
986 IN UINT8 HexValue
987 )
988 {
989 UINTN HighByte;
BcdToHex(IN UINT8 BcdValue)990 UINTN LowByte;
991
992 HighByte = (UINTN)HexValue / 10;
993 LowByte = (UINTN)HexValue % 10;
994
995 return ((UINT8)(LowByte + (HighByte << 4)));
996 }
997
998 UINT8
999 BcdToHex(
1000 IN UINT8 BcdValue
1001 )
1002 {
1003 UINTN HighByte;
1004 UINTN LowByte;
1005
1006 HighByte = (UINTN)((BcdValue >> 4) * 10);
1007 LowByte = (UINTN)(BcdValue & 0x0F);
1008
1009 return ((UINT8)(LowByte + HighByte));
1010 }
1011
1012