--- /dev/null
+/** @file\r
+This module produces the SMM COntrol2 Protocol for QNC\r
+\r
+Copyright (c) 2013-2015 Intel Corporation.\r
+\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <PiDxe.h>\r
+#include <Protocol/SmmControl2.h>\r
+#include <IndustryStandard/Pci.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/PciLib.h>\r
+#include <IntelQNCDxe.h>\r
+#include <Library/QNCAccessLib.h>\r
+#include <Uefi/UefiBaseType.h>\r
+\r
+#define EFI_INTERNAL_POINTER 0x00000004\r
+\r
+extern EFI_GUID gEfiEventVirtualAddressChangeGuid;\r
+\r
+/**\r
+ Generates an SMI using the parameters passed in.\r
+\r
+ @param This A pointer to an instance of\r
+ EFI_SMM_CONTROL2_PROTOCOL\r
+ @param ArgumentBuffer The argument buffer\r
+ @param ArgumentBufferSize The size of the argument buffer\r
+ @param Periodic TRUE to indicate a periodical SMI\r
+ @param ActivationInterval Interval of the periodical SMI\r
+\r
+ @retval EFI_INVALID_PARAMETER Periodic is TRUE or ArgumentBufferSize > 1\r
+ @return Return value from SmmTrigger().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Activate (\r
+ IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,\r
+ IN OUT UINT8 *CommandPort OPTIONAL,\r
+ IN OUT UINT8 *DataPort OPTIONAL,\r
+ IN BOOLEAN Periodic OPTIONAL,\r
+ IN EFI_SMM_PERIOD ActivationInterval OPTIONAL\r
+ );\r
+\r
+/**\r
+ Clears an SMI.\r
+\r
+ @param This Pointer to an instance of EFI_SMM_CONTROL2_PROTOCOL\r
+ @param Periodic TRUE to indicate a periodical SMI\r
+\r
+ @return Return value from SmmClear()\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Deactivate (\r
+ IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,\r
+ IN BOOLEAN Periodic OPTIONAL\r
+ );\r
+\r
+///\r
+/// Handle for the SMM Control2 Protocol\r
+///\r
+EFI_HANDLE mSmmControl2Handle = NULL;\r
+\r
+///\r
+/// SMM COntrol2 Protocol instance\r
+///\r
+EFI_SMM_CONTROL2_PROTOCOL mSmmControl2 = {\r
+ Activate,\r
+ Deactivate,\r
+ 0\r
+};\r
+\r
+VOID\r
+EFIAPI\r
+SmmControlVirtualddressChangeEvent (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Fixup internal data pointers so that the services can be called in virtual mode.\r
+\r
+Arguments:\r
+\r
+ Event The event registered.\r
+ Context Event context.\r
+\r
+Returns:\r
+\r
+ None.\r
+\r
+--*/\r
+{\r
+ gRT->ConvertPointer (EFI_INTERNAL_POINTER, (VOID *) &(mSmmControl2.Trigger));\r
+ gRT->ConvertPointer (EFI_INTERNAL_POINTER, (VOID *) &(mSmmControl2.Clear));\r
+}\r
+\r
+/**\r
+ Clear SMI related chipset status and re-enable SMI by setting the EOS bit.\r
+\r
+ @retval EFI_SUCCESS The requested operation has been carried out successfully\r
+ @retval EFI_DEVICE_ERROR The EOS bit could not be set.\r
+\r
+**/\r
+EFI_STATUS\r
+SmmClear (\r
+ VOID\r
+ )\r
+{\r
+ UINT16 PM1BLK_Base;\r
+ UINT16 GPE0BLK_Base;\r
+\r
+ //\r
+ // Get PM1BLK_Base & GPE0BLK_Base\r
+ //\r
+ PM1BLK_Base = PcdGet16 (PcdPm1blkIoBaseAddress);\r
+ GPE0BLK_Base = PcdGet16 (PcdGpe0blkIoBaseAddress);\r
+\r
+ //\r
+ // Clear the Power Button Override Status Bit, it gates EOS from being set.\r
+ // In QuarkNcSocId - Bit is read only. Handled by external SMC, do nothing.\r
+ //\r
+\r
+ //\r
+ // Clear the APM SMI Status Bit\r
+ //\r
+ IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIS), B_QNC_GPE0BLK_SMIS_APM);\r
+\r
+ //\r
+ // Set the EOS Bit\r
+ //\r
+ IoOr32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIS), B_QNC_GPE0BLK_SMIS_EOS);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Generates an SMI using the parameters passed in.\r
+\r
+ @param This A pointer to an instance of\r
+ EFI_SMM_CONTROL_PROTOCOL\r
+ @param ArgumentBuffer The argument buffer\r
+ @param ArgumentBufferSize The size of the argument buffer\r
+ @param Periodic TRUE to indicate a periodical SMI\r
+ @param ActivationInterval Interval of the periodical SMI\r
+\r
+ @retval EFI_INVALID_PARAMETER Periodic is TRUE or ArgumentBufferSize > 1\r
+ @retval EFI_SUCCESS SMI generated\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Activate (\r
+ IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,\r
+ IN OUT UINT8 *CommandPort OPTIONAL,\r
+ IN OUT UINT8 *DataPort OPTIONAL,\r
+ IN BOOLEAN Periodic OPTIONAL,\r
+ IN EFI_SMM_PERIOD ActivationInterval OPTIONAL\r
+ )\r
+{\r
+ UINT16 GPE0BLK_Base;\r
+ UINT32 NewValue;\r
+\r
+ //\r
+ // Get GPE0BLK_Base\r
+ //\r
+ GPE0BLK_Base = PcdGet16 (PcdGpe0blkIoBaseAddress);\r
+\r
+ if (Periodic) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Clear any pending the APM SMI\r
+ //\r
+ if (EFI_ERROR (SmmClear())) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Enable the APMC SMI\r
+ //\r
+ IoOr32 (GPE0BLK_Base + R_QNC_GPE0BLK_SMIE, B_QNC_GPE0BLK_SMIE_APM);\r
+\r
+ //\r
+ // Enable SMI globally\r
+ //\r
+ NewValue = QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC);\r
+ NewValue |= SMI_EN;\r
+ QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC, NewValue);\r
+\r
+\r
+ //\r
+ // Set APMC_STS\r
+ //\r
+ if (DataPort == NULL) {\r
+ IoWrite8 (PcdGet16 (PcdSmmDataPort), 0xFF);\r
+ } else {\r
+ IoWrite8 (PcdGet16 (PcdSmmDataPort), *DataPort);\r
+ }\r
+\r
+ //\r
+ // Generate the APMC SMI\r
+ //\r
+ if (CommandPort == NULL) {\r
+ IoWrite8 (PcdGet16 (PcdSmmActivationPort), 0xFF);\r
+ } else {\r
+ IoWrite8 (PcdGet16 (PcdSmmActivationPort), *CommandPort);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Clears an SMI.\r
+\r
+ @param This Pointer to an instance of EFI_SMM_CONTROL_PROTOCOL\r
+ @param Periodic TRUE to indicate a periodical SMI\r
+\r
+ @return Return value from SmmClear()\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Deactivate (\r
+ IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,\r
+ IN BOOLEAN Periodic\r
+ )\r
+{\r
+ if (Periodic) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return SmmClear();\r
+}\r
+\r
+/**\r
+ This is the constructor for the SMM Control protocol.\r
+\r
+ This function installs EFI_SMM_CONTROL2_PROTOCOL.\r
+\r
+ @param ImageHandle Handle for the image of this driver\r
+ @param SystemTable Pointer to the EFI System Table\r
+\r
+ @retval EFI_UNSUPPORTED There's no Intel ICH on this platform\r
+ @return The status returned from InstallProtocolInterface().\r
+\r
+--*/\r
+EFI_STATUS\r
+SmmControl2Init (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_EVENT Event;\r
+ UINT16 PM1BLK_Base;\r
+ UINT16 GPE0BLK_Base;\r
+ BOOLEAN SciEn;\r
+ UINT32 NewValue;\r
+\r
+ //\r
+ // Get PM1BLK_Base & GPE0BLK_Base\r
+ //\r
+ PM1BLK_Base = PcdGet16 (PcdPm1blkIoBaseAddress);\r
+ GPE0BLK_Base = PcdGet16 (PcdGpe0blkIoBaseAddress);\r
+\r
+ //\r
+ // Install our protocol interfaces on the device's handle\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &mSmmControl2Handle,\r
+ &gEfiSmmControl2ProtocolGuid, &mSmmControl2,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Determine whether an ACPI OS is present (via the SCI_EN bit)\r
+ //\r
+ SciEn = (BOOLEAN)((IoRead16 (PM1BLK_Base + R_QNC_PM1BLK_PM1C) & B_QNC_PM1BLK_PM1C_SCIEN) != 0);\r
+ if (!SciEn) {\r
+ //\r
+ // Clear any SMIs that double as SCIs (when SCI_EN==0)\r
+ //\r
+ IoWrite16 ((PM1BLK_Base + R_QNC_PM1BLK_PM1S), B_QNC_PM1BLK_PM1S_ALL);\r
+ IoWrite16 ((PM1BLK_Base + R_QNC_PM1BLK_PM1E), 0x00000000);\r
+ IoWrite32 ((PM1BLK_Base + R_QNC_PM1BLK_PM1C), 0x00000000);\r
+ IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_GPE0S), B_QNC_GPE0BLK_GPE0S_ALL);\r
+ IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_GPE0E), 0x00000000);\r
+ }\r
+\r
+ //\r
+ // Clear and disable all SMIs that are unaffected by SCI_EN\r
+ // Set EOS\r
+ //\r
+ IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIE), 0x00000000);\r
+ IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIS), (B_QNC_GPE0BLK_SMIS_EOS + B_QNC_GPE0BLK_SMIS_ALL));\r
+\r
+ //\r
+ // Enable SMI globally\r
+ //\r
+ NewValue = QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC);\r
+ NewValue |= SMI_EN;\r
+ QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC, NewValue);\r
+\r
+ //\r
+ // Make sure to write this register last -- EOS re-enables SMIs for the QNC\r
+ //\r
+ IoAndThenOr32 (\r
+ GPE0BLK_Base + R_QNC_GPE0BLK_SMIE,\r
+ (UINT32)(~B_QNC_GPE0BLK_SMIE_ALL),\r
+ B_QNC_GPE0BLK_SMIE_APM\r
+ );\r
+\r
+ //\r
+ // Make sure EOS bit cleared\r
+ //\r
+ DEBUG_CODE_BEGIN ();\r
+ if (IoRead32 (GPE0BLK_Base + R_QNC_GPE0BLK_SMIS) & B_QNC_GPE0BLK_SMIS_EOS) {\r
+ DEBUG ((\r
+ EFI_D_ERROR,\r
+ "******************************************************************************\n"\r
+ "BIG ERROR: SmmControl constructor couldn't properly initialize the ACPI table.\n"\r
+ " SmmControl->Clear will probably hang. \n"\r
+ " NOTE: SCI_EN = %d \n"\r
+ "******************************************************************************\n",\r
+ SciEn\r
+ ));\r
+\r
+ //\r
+ // If we want the system to stop, then keep the ASSERT(FALSE).\r
+ // Otherwise, comment it out.\r
+ //\r
+ ASSERT (FALSE);\r
+ }\r
+ DEBUG_CODE_END ();\r
+\r
+ Status = gBS->CreateEventEx (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ SmmControlVirtualddressChangeEvent,\r
+ NULL,\r
+ &gEfiEventVirtualAddressChangeGuid,\r
+ &Event\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r