+/** @file\r
+\r
+This driver is responsible for the registration of child drivers\r
+and the abstraction of the QNC SMI sources.\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
+//\r
+// Include common header file for this module.\r
+//\r
+#include "CommonHeader.h"\r
+\r
+#include "QNCSmmHelpers.h"\r
+\r
+//\r
+// Help handle porting bit shifts to IA-64.\r
+//\r
+#define BIT_ZERO 0x00000001\r
+\r
+\r
+VOID\r
+QNCSmmPublishDispatchProtocols(\r
+ VOID\r
+ )\r
+{\r
+ UINTN Index;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Install protocol interfaces.\r
+ //\r
+ for (Index = 0; Index < NUM_PROTOCOLS; Index++) {\r
+ Status = gSmst->SmmInstallProtocolInterface (\r
+ &mPrivateData.InstallMultProtHandle,\r
+ mPrivateData.Protocols[Index].Guid,\r
+ EFI_NATIVE_INTERFACE,\r
+ &mPrivateData.Protocols[Index].Protocols.Generic\r
+ );\r
+\r
+ ASSERT_EFI_ERROR (Status);\r
+}\r
+}\r
+\r
+EFI_STATUS\r
+QNCSmmInitHardware(\r
+ VOID\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Initialize bits that aren't necessarily related to an SMI source.\r
+\r
+Dependencies:\r
+\r
+ gSmst - SMM System Table; contains an entry for SMM CPU IO\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS. Asserts, otherwise.\r
+\r
+--*/\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Clear all SMIs\r
+ //\r
+ QNCSmmClearSmi();\r
+\r
+ Status = QNCSmmEnableGlobalSmiBit ();\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Be *really* sure to clear all SMIs\r
+ //\r
+ QNCSmmClearSmi ();\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+QNCSmmEnableGlobalSmiBit (\r
+ VOID\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Enables the QNC to generate SMIs. Note that no SMIs will be generated\r
+ if no SMI sources are enabled. Conversely, no enabled SMI source will\r
+ generate SMIs if SMIs are not globally enabled. This is the main\r
+ switchbox for SMI generation.\r
+\r
+Arguments:\r
+\r
+ None\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS.\r
+ Asserts, otherwise.\r
+\r
+--*/\r
+{\r
+ UINT32 NewValue;\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
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+QNCSmmClearSmi(\r
+ VOID\r
+ )\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ Clears the SMI after all SMI source have been processed.\r
+ Note that this function will not work correctly (as it is\r
+ written) unless all SMI sources have been processed.\r
+ A revision of this function could manually clear all SMI\r
+ status bits to guarantee success.\r
+\r
+Returns:\r
+\r
+ EFI_SUCCESS.\r
+ Asserts, otherwise.\r
+\r
+--*/\r
+{\r
+ BOOLEAN EosSet;\r
+ BOOLEAN SciEn;\r
+\r
+ UINT32 Pm1Cnt = 0;\r
+ UINT16 Pm1Sts = 0;\r
+ UINT32 Gpe0Sts = 0;\r
+ UINT32 SmiSts = 0;\r
+\r
+ //\r
+ // Determine whether an ACPI OS is present (via the SCI_EN bit)\r
+ //\r
+ Pm1Cnt = IoRead32(PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1C);\r
+ SciEn = (BOOLEAN)((Pm1Cnt & B_QNC_PM1BLK_PM1C_SCIEN) == B_QNC_PM1BLK_PM1C_SCIEN);\r
+\r
+ if (SciEn == FALSE) {\r
+\r
+ //\r
+ // Clear any SMIs that double as SCIs (when SCI_EN==0)\r
+ //\r
+ Pm1Sts = (B_QNC_PM1BLK_PM1S_WAKE | B_QNC_PM1BLK_PM1S_PCIEWSTS | B_QNC_PM1BLK_PM1S_RTC | B_QNC_PM1BLK_PM1S_GLOB | B_QNC_PM1BLK_PM1S_TO);\r
+\r
+ Gpe0Sts = B_QNC_GPE0BLK_GPE0S_ALL;\r
+\r
+ IoOr16((PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1S), Pm1Sts);\r
+ IoOr32(((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_GPE0S), Gpe0Sts);\r
+ }\r
+\r
+ //\r
+ // Clear all SMIs that are unaffected by SCI_EN\r
+ //\r
+ SmiSts = IoRead32((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_SMIS);\r
+ SmiSts |= B_QNC_GPE0BLK_SMIS_ALL;\r
+ IoWrite32(((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_SMIS), SmiSts);\r
+\r
+ //\r
+ // Try to clear the EOS bit. ASSERT on an error\r
+ //\r
+ EosSet = QNCSmmSetAndCheckEos();\r
+ ASSERT (EosSet);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+BOOLEAN\r
+QNCSmmSetAndCheckEos(\r
+ VOID\r
+ )\r
+{\r
+ //\r
+ // Reset the QNC to generate subsequent SMIs\r
+ //\r
+ IoOr32(((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_SMIS), B_QNC_GPE0BLK_SMIS_EOS);\r
+ return TRUE;\r
+}\r
+\r
+BOOLEAN\r
+QNCSmmGetSciEn(\r
+ )\r
+{\r
+ BOOLEAN SciEn;\r
+ UINT32 Pm1Cnt;\r
+\r
+ //\r
+ // Determine whether an ACPI OS is present (via the SCI_EN bit)\r
+ //\r
+ Pm1Cnt = IoRead32(PcdGet16 (PcdPm1blkIoBaseAddress) + R_QNC_PM1BLK_PM1C);\r
+\r
+ SciEn = (BOOLEAN)((Pm1Cnt & B_QNC_PM1BLK_PM1C_SCIEN) == B_QNC_PM1BLK_PM1C_SCIEN);\r
+\r
+ return SciEn;\r
+}\r
+\r
+//\r
+// These may or may not need to change w/ the QNC version; they're highly IA-32 dependent, though.\r
+//\r
+\r
+BOOLEAN\r
+ReadBitDesc (\r
+ CONST QNC_SMM_BIT_DESC *BitDesc\r
+ )\r
+{\r
+ UINT64 Register;\r
+ UINT32 PciBus;\r
+ UINT32 PciDev;\r
+ UINT32 PciFun;\r
+ UINT32 PciReg;\r
+ BOOLEAN BitWasOne;\r
+\r
+ ASSERT (BitDesc != NULL );\r
+ ASSERT (!IS_BIT_DESC_NULL( *BitDesc ) );\r
+\r
+ Register = 0;\r
+ BitWasOne = FALSE;\r
+\r
+ switch (BitDesc->Reg.Type) {\r
+\r
+ case ACPI_ADDR_TYPE:\r
+ //\r
+ // Double check that we correctly read in the acpi base address\r
+ //\r
+ ASSERT ((PcdGet16 (PcdPm1blkIoBaseAddress) != 0x0) && ((PcdGet16 (PcdPm1blkIoBaseAddress) & 0x1) != 0x1) );\r
+\r
+ switch (BitDesc->SizeInBytes) {\r
+\r
+ case 0:\r
+ //\r
+ // Chances are that this field didn't get initialized.\r
+ // Check your assignments to bit descriptions.\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+\r
+ case 1:\r
+ Register = (UINT64) IoRead8 (PcdGet16 (PcdPm1blkIoBaseAddress) + BitDesc->Reg.Data.acpi);\r
+ break;\r
+\r
+ case 2:\r
+ Register = (UINT64) IoRead16 (PcdGet16 (PcdPm1blkIoBaseAddress) + BitDesc->Reg.Data.acpi);\r
+ break;\r
+\r
+ case 4:\r
+ Register = (UINT64) IoRead32 (PcdGet16 (PcdPm1blkIoBaseAddress) + BitDesc->Reg.Data.acpi);\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Unsupported or invalid register size\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+ };\r
+\r
+ if ((Register & LShiftU64 (BIT_ZERO, BitDesc->Bit)) != 0) {\r
+ BitWasOne = TRUE;\r
+ } else {\r
+ BitWasOne = FALSE;\r
+ }\r
+ break;\r
+\r
+ case GPE_ADDR_TYPE:\r
+ //\r
+ // Double check that we correctly read in the gpe base address\r
+ //\r
+ ASSERT (((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) != 0x0) && (((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) & 0x1) != 0x1) );\r
+\r
+ switch (BitDesc->SizeInBytes) {\r
+\r
+ case 0:\r
+ //\r
+ // Chances are that this field didn't get initialized.\r
+ // Check your assignments to bit descriptions.\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+\r
+ case 1:\r
+ Register = (UINT64) IoRead8 ((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + BitDesc->Reg.Data.gpe);\r
+ break;\r
+\r
+ case 2:\r
+ Register = (UINT64) IoRead16 ((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + BitDesc->Reg.Data.gpe);\r
+ break;\r
+\r
+ case 4:\r
+ Register = (UINT64) IoRead32 ((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + BitDesc->Reg.Data.gpe);\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Unsupported or invalid register size\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+ };\r
+\r
+ if ((Register & LShiftU64 (BIT_ZERO, BitDesc->Bit)) != 0) {\r
+ BitWasOne = TRUE;\r
+ } else {\r
+ BitWasOne = FALSE;\r
+ }\r
+ break;\r
+\r
+ case MEMORY_MAPPED_IO_ADDRESS_TYPE:\r
+ //\r
+ // Read the register, and it with the bit to read\r
+ //\r
+\r
+ //\r
+ // This code does not support reads greater then 64 bits\r
+ //\r
+ ASSERT (BitDesc->SizeInBytes <= 8);\r
+ CopyMem (&Register, BitDesc->Reg.Data.Mmio, BitDesc->SizeInBytes);\r
+ Register &= LShiftU64 (BIT0, BitDesc->Bit);\r
+ if (Register) {\r
+ BitWasOne = TRUE;\r
+ } else {\r
+ BitWasOne = FALSE;\r
+ }\r
+ break;\r
+\r
+ case PCI_ADDR_TYPE:\r
+ PciBus = BitDesc->Reg.Data.pci.Fields.Bus;\r
+ PciDev = BitDesc->Reg.Data.pci.Fields.Dev;\r
+ PciFun = BitDesc->Reg.Data.pci.Fields.Fnc;\r
+ PciReg = BitDesc->Reg.Data.pci.Fields.Reg;\r
+ switch (BitDesc->SizeInBytes) {\r
+\r
+ case 0:\r
+ //\r
+ // Chances are that this field didn't get initialized.\r
+ // Check your assignments to bit descriptions.\r
+ ASSERT (FALSE );\r
+ break;\r
+\r
+ case 1:\r
+ Register = (UINT64) PciRead8 (PCI_LIB_ADDRESS (PciBus, PciDev, PciFun, PciReg));\r
+ break;\r
+\r
+ case 2:\r
+ Register = (UINT64) PciRead16 (PCI_LIB_ADDRESS (PciBus, PciDev, PciFun, PciReg));\r
+ break;\r
+\r
+ case 4:\r
+ Register = (UINT64) PciRead32 (PCI_LIB_ADDRESS (PciBus, PciDev, PciFun, PciReg));\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Unsupported or invalid register size\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+ };\r
+\r
+ if ((Register & LShiftU64 (BIT_ZERO, BitDesc->Bit)) != 0) {\r
+ BitWasOne = TRUE;\r
+ } else {\r
+ BitWasOne = FALSE;\r
+ }\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // This address type is not yet implemented\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+ };\r
+\r
+ return BitWasOne;\r
+}\r
+\r
+VOID\r
+WriteBitDesc (\r
+ CONST QNC_SMM_BIT_DESC *BitDesc,\r
+ CONST BOOLEAN ValueToWrite\r
+ )\r
+{\r
+ UINT64 Register;\r
+ UINT64 AndVal;\r
+ UINT64 OrVal;\r
+ UINT32 PciBus;\r
+ UINT32 PciDev;\r
+ UINT32 PciFun;\r
+ UINT32 PciReg;\r
+\r
+ ASSERT (BitDesc != NULL);\r
+ ASSERT (!IS_BIT_DESC_NULL(*BitDesc));\r
+\r
+ AndVal = ~(BIT_ZERO << (BitDesc->Bit));\r
+ OrVal = ((UINT32)ValueToWrite) << (BitDesc->Bit);\r
+\r
+ switch (BitDesc->Reg.Type) {\r
+\r
+ case ACPI_ADDR_TYPE:\r
+ //\r
+ // Double check that we correctly read in the acpi base address\r
+ //\r
+ ASSERT ((PcdGet16 (PcdPm1blkIoBaseAddress) != 0x0) && ((PcdGet16 (PcdPm1blkIoBaseAddress) & 0x1) != 0x1));\r
+\r
+ switch (BitDesc->SizeInBytes) {\r
+\r
+ case 0:\r
+ //\r
+ // Chances are that this field didn't get initialized.\r
+ // Check your assignments to bit descriptions.\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+\r
+ case 1:\r
+ IoAndThenOr8 (PcdGet16 (PcdPm1blkIoBaseAddress) + BitDesc->Reg.Data.acpi, (UINT8)AndVal, (UINT8)OrVal);\r
+ break;\r
+\r
+ case 2:\r
+ IoAndThenOr16 (PcdGet16 (PcdPm1blkIoBaseAddress) + BitDesc->Reg.Data.acpi, (UINT16)AndVal, (UINT16)OrVal);\r
+ break;\r
+\r
+ case 4:\r
+ IoAndThenOr32 (PcdGet16 (PcdPm1blkIoBaseAddress) + BitDesc->Reg.Data.acpi, (UINT32)AndVal, (UINT32)OrVal);\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Unsupported or invalid register size\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+ };\r
+ break;\r
+\r
+ case GPE_ADDR_TYPE:\r
+ //\r
+ // Double check that we correctly read in the gpe base address\r
+ //\r
+ ASSERT (((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) != 0x0) && (((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) & 0x1) != 0x1));\r
+\r
+ switch (BitDesc->SizeInBytes) {\r
+\r
+ case 0:\r
+ //\r
+ // Chances are that this field didn't get initialized.\r
+ // Check your assignments to bit descriptions.\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+\r
+ case 1:\r
+ IoAndThenOr8 ((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + BitDesc->Reg.Data.gpe, (UINT8)AndVal, (UINT8)OrVal);\r
+ break;\r
+\r
+ case 2:\r
+ IoAndThenOr16 ((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + BitDesc->Reg.Data.gpe, (UINT16)AndVal, (UINT16)OrVal);\r
+ break;\r
+\r
+ case 4:\r
+ IoAndThenOr32 ((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + BitDesc->Reg.Data.gpe, (UINT32)AndVal, (UINT32)OrVal);\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Unsupported or invalid register size\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+ };\r
+ break;\r
+\r
+ case MEMORY_MAPPED_IO_ADDRESS_TYPE:\r
+ //\r
+ // Read the register, or it with the bit to set, then write it back.\r
+ //\r
+\r
+ //\r
+ // This code does not support writes greater then 64 bits\r
+ //\r
+ ASSERT (BitDesc->SizeInBytes <= 8);\r
+ CopyMem (&Register, BitDesc->Reg.Data.Mmio, BitDesc->SizeInBytes);\r
+ Register &= AndVal;\r
+ Register |= OrVal;\r
+ CopyMem (BitDesc->Reg.Data.Mmio, &Register, BitDesc->SizeInBytes);\r
+ break;\r
+\r
+ case PCI_ADDR_TYPE:\r
+ PciBus = BitDesc->Reg.Data.pci.Fields.Bus;\r
+ PciDev = BitDesc->Reg.Data.pci.Fields.Dev;\r
+ PciFun = BitDesc->Reg.Data.pci.Fields.Fnc;\r
+ PciReg = BitDesc->Reg.Data.pci.Fields.Reg;\r
+ switch (BitDesc->SizeInBytes) {\r
+\r
+ case 0:\r
+ //\r
+ // Chances are that this field didn't get initialized -- check your assignments\r
+ // to bit descriptions.\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+\r
+ case 1:\r
+ PciAndThenOr8 (PCI_LIB_ADDRESS (PciBus, PciDev, PciFun, PciReg), (UINT8) AndVal, (UINT8) OrVal);\r
+ break;\r
+\r
+ case 2:\r
+ PciAndThenOr16 (PCI_LIB_ADDRESS (PciBus, PciDev, PciFun, PciReg), (UINT16) AndVal, (UINT16) OrVal);\r
+ break;\r
+\r
+ case 4:\r
+ PciAndThenOr32 (PCI_LIB_ADDRESS (PciBus, PciDev, PciFun, PciReg), (UINT32) AndVal, (UINT32) OrVal);\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Unsupported or invalid register size\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+ };\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // This address type is not yet implemented\r
+ //\r
+ ASSERT (FALSE );\r
+ break;\r
+ };\r
+}\r