/** @file ;****************************************************************************** ;* Copyright (c) 2012 - 2019, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include #include #include #include #include #include #include #include // // Driver Consumed PPI Prototypes // #include #include #include typedef struct { UINT16 NBVId; UINT16 PlatformId; CLOCK_GEN_VGA_TYPE VgaType; UINT8 *ClkGenData; UINT8 BlockSize; UINT8 ClkGenVId; UINT8 ClkGenDId; } REAL_CLKGEN_INFO; EFI_STATUS ClockGenWriteSmBus ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_SMBUS2_PPI *SmbusPpi, IN EFI_SMBUS_DEVICE_ADDRESS SmbusSlaveAddress, IN EFI_SMBUS_DEVICE_COMMAND SmbusDeviceCommand, IN CLOCK_GEN_DATA_INFO *InitTable, IN UINT8 *DataBuffer, IN UINTN DataLength ); EFI_STATUS ShiftBit ( IN UINT8 ByteValue, IN UINT8 MaskValue, OUT UINT8 *RealByteValue ); /** User entry for this PEIM driver. @param FileHandle Handle of the file being invoked. @param PeiServices Describes the list of possible PEI Services. @retval EFI_SUCCESS Program clock generator successfully. @return Others Fail to program clock generator **/ EFI_STATUS EFIAPI PeimInitializeClkGen ( IN EFI_PEI_FILE_HANDLE FileHandle, IN CONST EFI_PEI_SERVICES **PeiServices ) { EFI_STATUS Status; UINT8 SlaveAddress; UINT8 StartOffset; CLOCK_GEN_UNION_INFO *ClkGenBigTable; REAL_CLKGEN_INFO RealClkGenInfo; UINT16 BigTableSize; EFI_PEI_SMBUS2_PPI *SmbusPpi; UINT8 TableIndex; UINTN DataLength; UINT8 *DataBuffer; EFI_SMBUS_DEVICE_ADDRESS SmbusSlaveAddress; EFI_SMBUS_DEVICE_COMMAND SmbusDeviceCommand; EFI_SMBUS_OPERATION SmbusOperation; UINTN OperationLength; UINTN ClockGenIndex; UINTN IndexEnd; UINT16 VenderID; UINT32 ReadData; UINT8 ClassCode; ClkGenBigTable = NULL; DataLength = 0; DataBuffer = NULL; ClassCode = 0; BigTableSize = 0; // // (OemServices) Get CLKGEN Modify data information // DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcChipsetModifyClockGenInfo \n")); Status = OemSvcChipsetModifyClockGenInfo ( &SlaveAddress, &StartOffset, &ClkGenBigTable, &RealClkGenInfo.PlatformId, &BigTableSize ); DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcChipsetModifyClockGenInfo Status: %r\n", Status)); if (!EFI_ERROR (Status)) { // // Message will show on command line and Procedure will not dead lock. // DEBUG ((EFI_D_ERROR, "Get ClkGenBigTable error\n")); return EFI_UNSUPPORTED; } Status = PeiServicesLocatePpi ( &gEfiPeiSmbus2PpiGuid, // GUID 0, // INSTANCE NULL, // PEI_PPI_DESCRIPTOR (VOID **)&SmbusPpi // PPI ); if (EFI_ERROR(Status)) { // // Message will show on command line and Procedure will not dead lock. // DEBUG ((EFI_D_ERROR, "Locate SMBus error\n")); return Status; } if (ClkGenBigTable == NULL) { return EFI_INVALID_PARAMETER; } if (ClkGenBigTable[0].UnionDataInfo->DataInfo[0].WriteType == CLKGEN_FAST_BLOCK_OPERATION) { TableIndex = 0; while (ClkGenBigTable[0].UnionDataInfo->DataInfo[TableIndex].WriteType != CLKGEN_DATA_END) { TableIndex++; } DataLength = TableIndex; Status = PeiServicesAllocatePool ( DataLength, (VOID **)&DataBuffer ); if (EFI_ERROR (Status)) { // // Message will show on command line and Procedure will not dead lock. // DEBUG ((EFI_D_ERROR, "Allocate DataBuffer error\n")); return Status; } SetMem(DataBuffer, DataLength, 0); TableIndex = 0; while (ClkGenBigTable[0].UnionDataInfo->DataInfo[TableIndex].WriteType != CLKGEN_DATA_END) { DataBuffer [ClkGenBigTable[0].UnionDataInfo->DataInfo[TableIndex].Byte] = ClkGenBigTable[0].UnionDataInfo->DataInfo[TableIndex].OrSourceValue; TableIndex++; } SmbusSlaveAddress.SmbusDeviceAddress = SlaveAddress; SmbusDeviceCommand = 0; SmbusOperation = EfiSmbusWriteBlock; OperationLength = TableIndex; Status = SmbusPpi->Execute ( SmbusPpi, SmbusSlaveAddress, SmbusDeviceCommand, SmbusOperation, FALSE, &OperationLength, &DataBuffer [0] ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Write CLKGEN data error\n")); return Status; } else { return EFI_SUCCESS; } } // // Get NB Vendor ID // RealClkGenInfo.NBVId = PciCf8Read16 (PCI_CF8_LIB_ADDRESS (0, 0, 0, PCI_VENDOR_ID_OFFSET)); // // Get Graphic Type // // // Check UMA Graphic // if (PcdGet8 (PcdUmaDeviceNumber) == 0xFF ) { VenderID = 0xFFFF; } else { VenderID = PciCf8Read16 (PCI_CF8_LIB_ADDRESS (0, PcdGet8 (PcdUmaDeviceNumber), 0, PCI_VENDOR_ID_OFFSET)); } if (VenderID == 0xFFFF) { // // check DESCRIPTER Graphic // // // Set PEG PortBus = 1 to Read Endpoint. // if ( PcdGet8 (PcdPegDeviceNumber) == 0xFF ) { VenderID = 0xFFFF; } else { ReadData = PciCf8Read32 (PCI_CF8_LIB_ADDRESS (0, PcdGet8 (PcdPegDeviceNumber), 0, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET)); ReadData = ((ReadData & 0xFF0000FF) | 0x00010100); PciCf8Write32 ( PCI_CF8_LIB_ADDRESS (0, PcdGet8 (PcdPegDeviceNumber), 0, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET), ReadData ); // // Get VID. // VenderID = PciCf8Read16 (PCI_CF8_LIB_ADDRESS (1, 0, 0, PCI_VENDOR_ID_OFFSET)); // // Get device class code. // ClassCode = PciCf8Read8 (PCI_CF8_LIB_ADDRESS (1, 0, 0, (PCI_CLASSCODE_OFFSET + 2))); ReadData = (ReadData & 0xFF0000FF); PciCf8Write32 ( PCI_CF8_LIB_ADDRESS (0, PcdGet8 (PcdPegDeviceNumber), 0, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET), ReadData ); } if (VenderID != 0xFFFF) { // // Perform PEG Endpoint Class Code Check. If the Endpoint Class Code is // not GFX, then the Port is being used as a standard PCI Express Port. // if (ClassCode == PCI_CLASS_DISPLAY || ClassCode == PCI_CLASS_BRIDGE) { RealClkGenInfo.VgaType = CLOCK_GEN_VGA_DESCRIPTER; } else { return EFI_UNSUPPORTED; } } else { return EFI_UNSUPPORTED; } } else { // // To check if this is an UMA and PEG combined SKU. // the UMA device was detected, check if there is other PEG device. // // // Set PEG PortBus = 1 to Read Endpoint. // if ( PcdGet8 (PcdPegDeviceNumber) == 0xFF ) { VenderID = 0xFFFF; } else { ReadData = PciCf8Read32 (PCI_CF8_LIB_ADDRESS (0, PcdGet8 (PcdPegDeviceNumber), 0, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET)); ReadData = ((ReadData & 0xFF0000FF) | 0x00010100); PciCf8Write32 ( PCI_CF8_LIB_ADDRESS (0, PcdGet8 (PcdPegDeviceNumber), 0, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET), ReadData ); // // Get VID. // VenderID = PciCf8Read16 (PCI_CF8_LIB_ADDRESS (1, 0, 0, PCI_VENDOR_ID_OFFSET)); // // Get device class code. // ClassCode = PciCf8Read8 (PCI_CF8_LIB_ADDRESS (1, 0, 0, (PCI_CLASSCODE_OFFSET + 2))); ReadData = (ReadData & 0xFF0000FF); PciCf8Write32 ( PCI_CF8_LIB_ADDRESS (0, PcdGet8 (PcdPegDeviceNumber), 0, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET), ReadData ); } if (VenderID != 0xFFFF) { // // Perform PEG Endpoint Class Code Check. If the Endpoint Class Code is // not GFX, then the Port is being used as a standard PCI Express Port. // if (ClassCode == PCI_CLASS_DISPLAY || ClassCode == PCI_CLASS_BRIDGE) { RealClkGenInfo.VgaType = CLOCK_GEN_VGA_UNITED; } else { RealClkGenInfo.VgaType = CLOCK_GEN_VGA_UMA; } } else { RealClkGenInfo.VgaType = CLOCK_GEN_VGA_UMA; } } // // Get CLKGEN Data and Length // // // Get CLKGEN Length // DataLength = 1; Status = PeiServicesAllocatePool ( DataLength, (VOID **)&DataBuffer ); if (EFI_ERROR (Status)) { // // Message will show on command line and Procedure will not dead lock. // DEBUG ((EFI_D_ERROR, "Allocate DataBuffer error\n")); return Status; } SetMem (DataBuffer, DataLength, 0); SmbusSlaveAddress.SmbusDeviceAddress = SlaveAddress; SmbusDeviceCommand = (UINTN)StartOffset; SmbusOperation = EfiSmbusReadBlock; OperationLength = DataLength; Status = SmbusPpi->Execute ( SmbusPpi, SmbusSlaveAddress, SmbusDeviceCommand, SmbusOperation, FALSE, &OperationLength, &DataBuffer[0] ); RealClkGenInfo.BlockSize = (UINT8)OperationLength; // // Get CLKGEN Data // DataLength = RealClkGenInfo.BlockSize; Status = PeiServicesAllocatePool ( DataLength, (VOID **)&DataBuffer ); if (EFI_ERROR (Status)) { // // Message will show on command line and Procedure will not dead lock. // DEBUG ((EFI_D_ERROR, "Allocate DataBuffer error\n")); return Status; } SetMem (DataBuffer, DataLength, 0); SmbusSlaveAddress.SmbusDeviceAddress = SlaveAddress; SmbusDeviceCommand = (UINTN)StartOffset; SmbusOperation = EfiSmbusReadBlock; OperationLength = DataLength; Status = SmbusPpi->Execute ( SmbusPpi, SmbusSlaveAddress, SmbusDeviceCommand, SmbusOperation, FALSE, &OperationLength, &DataBuffer[0] ); if (EFI_ERROR (Status)) { // // Message will show on command line and Procedure will not dead lock. // DEBUG ((EFI_D_ERROR, "Read CLKGEN data error\n")); return Status; } else if ((DataBuffer[0] == 0xFF) && (DataBuffer[1] == 0xFF) && (DataBuffer[2] == 0xFF) && (DataBuffer[3] == 0xFF)) { DEBUG ((EFI_D_ERROR, "Read CLKGEN data error\n")); return EFI_UNSUPPORTED; } RealClkGenInfo.ClkGenData = DataBuffer; IndexEnd = BigTableSize; // // Find PRI_INIT table and pre init CLKGEN // for (ClockGenIndex = 0; ClockGenIndex < IndexEnd ; ClockGenIndex++) { ShiftBit(RealClkGenInfo.ClkGenData[ClkGenBigTable[ClockGenIndex].DeviceInfo->VenderIdReg.Byte], ClkGenBigTable[ClockGenIndex].DeviceInfo->VenderIdReg.Mask, &RealClkGenInfo.ClkGenVId); ShiftBit(RealClkGenInfo.ClkGenData[ClkGenBigTable[ClockGenIndex].DeviceInfo->DeviceIdReg.Byte], ClkGenBigTable[ClockGenIndex].DeviceInfo->DeviceIdReg.Mask, &RealClkGenInfo.ClkGenDId); if (ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->ChipVId == NO_VERFY_CHIP_VID) { RealClkGenInfo.NBVId = NO_VERFY_CHIP_VID; } if (ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->VgaType == NO_VERFY_VGA) { RealClkGenInfo.VgaType = NO_VERFY_VGA; } if (ClkGenBigTable[ClockGenIndex].DeviceInfo->VenderId == NO_VERFY_CLKGEN_VID_DID) { RealClkGenInfo.ClkGenVId = NO_VERFY_CLKGEN_VID_DID; } if (ClkGenBigTable[ClockGenIndex].DeviceInfo->DeviceId == NO_VERFY_CLKGEN_VID_DID) { RealClkGenInfo.ClkGenDId = NO_VERFY_CLKGEN_VID_DID; } // // Compare NB Vendor ID, Compare Platform ID, Compare Graphic Type, Compare Init Type, Compare CLKGEN Vendor ID and CLKGEN Device ID // if ((ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->InitType == CLOCK_GEN_PRI_INIT) && (ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->ChipVId == RealClkGenInfo.NBVId) && (ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->PlatformId == RealClkGenInfo.PlatformId) && (ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->VgaType == RealClkGenInfo.VgaType) && (ClkGenBigTable[ClockGenIndex].DeviceInfo->VenderId == RealClkGenInfo.ClkGenVId) && (ClkGenBigTable[ClockGenIndex].DeviceInfo->DeviceId == RealClkGenInfo.ClkGenDId)){ // // Write Smbus // SmbusSlaveAddress.SmbusDeviceAddress = SlaveAddress; SmbusDeviceCommand = (UINTN)StartOffset; Status = ClockGenWriteSmBus ((EFI_PEI_SERVICES**) PeiServices, SmbusPpi, SmbusSlaveAddress, SmbusDeviceCommand, ClkGenBigTable[ClockGenIndex].UnionDataInfo->DataInfo, RealClkGenInfo.ClkGenData, RealClkGenInfo.BlockSize ); if (EFI_ERROR (Status)) { // // Message will show on command line and Procedure will not dead lock. // DEBUG ((EFI_D_ERROR, "Write CLKGEN data error\n")); return Status; } // // Get CLKGEN Length again for pre init change CLKGEN Length. // SmbusSlaveAddress.SmbusDeviceAddress = SlaveAddress; SmbusDeviceCommand = (UINTN)StartOffset; SmbusOperation = EfiSmbusReadBlock; OperationLength = RealClkGenInfo.BlockSize; Status = SmbusPpi->Execute ( SmbusPpi, SmbusSlaveAddress, SmbusDeviceCommand, SmbusOperation, FALSE, &OperationLength, &DataBuffer[0] ); if ((UINT8)OperationLength != RealClkGenInfo.BlockSize) { RealClkGenInfo.BlockSize = (UINT8)OperationLength; SetMem (DataBuffer, DataLength, 0); SmbusSlaveAddress.SmbusDeviceAddress = SlaveAddress; SmbusDeviceCommand = (UINTN)StartOffset; SmbusOperation = EfiSmbusReadBlock; Status = SmbusPpi->Execute ( SmbusPpi, SmbusSlaveAddress, SmbusDeviceCommand, SmbusOperation, FALSE, &OperationLength, &DataBuffer[0] ); } } } // // Find INIT table and init CLKGEN // for (ClockGenIndex = 0; ClockGenIndex < IndexEnd ; ClockGenIndex++) { ShiftBit(RealClkGenInfo.ClkGenData[ClkGenBigTable[ClockGenIndex].DeviceInfo->VenderIdReg.Byte], ClkGenBigTable[ClockGenIndex].DeviceInfo->VenderIdReg.Mask, &RealClkGenInfo.ClkGenVId); ShiftBit(RealClkGenInfo.ClkGenData[ClkGenBigTable[ClockGenIndex].DeviceInfo->DeviceIdReg.Byte], ClkGenBigTable[ClockGenIndex].DeviceInfo->DeviceIdReg.Mask, &RealClkGenInfo.ClkGenDId); if (ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->ChipVId == NO_VERFY_CHIP_VID) { RealClkGenInfo.NBVId = NO_VERFY_CHIP_VID; } if (ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->VgaType == NO_VERFY_VGA) { RealClkGenInfo.VgaType = NO_VERFY_VGA; } if (ClkGenBigTable[ClockGenIndex].DeviceInfo->VenderId == NO_VERFY_CLKGEN_VID_DID) { RealClkGenInfo.ClkGenVId = NO_VERFY_CLKGEN_VID_DID; } if (ClkGenBigTable[ClockGenIndex].DeviceInfo->DeviceId == NO_VERFY_CLKGEN_VID_DID) { RealClkGenInfo.ClkGenDId = NO_VERFY_CLKGEN_VID_DID; } // // Compare NB Vendor ID, Compare Platform ID, Compare Graphic Type, Compare Init Type, Compare CLKGEN Vendor ID and CLKGEN Device ID // if ((ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->InitType == CLOCK_GEN_INIT) && (ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->ChipVId == RealClkGenInfo.NBVId) && (ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->PlatformId == RealClkGenInfo.PlatformId) && (ClkGenBigTable[ClockGenIndex].UnionDataInfo->ExtraInfo->VgaType == RealClkGenInfo.VgaType) && (ClkGenBigTable[ClockGenIndex].DeviceInfo->VenderId == RealClkGenInfo.ClkGenVId) && (ClkGenBigTable[ClockGenIndex].DeviceInfo->DeviceId == RealClkGenInfo.ClkGenDId)){ // // Write Smbus // SmbusSlaveAddress.SmbusDeviceAddress = SlaveAddress; SmbusDeviceCommand = (UINTN)StartOffset; Status = ClockGenWriteSmBus ((EFI_PEI_SERVICES**) PeiServices, SmbusPpi, SmbusSlaveAddress, SmbusDeviceCommand, ClkGenBigTable[ClockGenIndex].UnionDataInfo->DataInfo, RealClkGenInfo.ClkGenData, RealClkGenInfo.BlockSize ); if (EFI_ERROR (Status)) { // // Message will show on command line and Procedure will not dead lock. // DEBUG ((EFI_D_ERROR, "Write CLKGEN data error\n")); return Status; } else { break; } } else if (ClockGenIndex == (IndexEnd - 1)){ return EFI_UNSUPPORTED; } } return EFI_SUCCESS; } /** SMBus write function for clock gen. @param InitTable Clock Generator information table @param DataBuffer Real Clock Generator data @param DataLength Real Clock Generator data length @param DataInfoSize Clock Generator information table size @retval EFI_SUCCESS If Clock Generator write success. **/ EFI_STATUS ClockGenWriteSmBus ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_SMBUS2_PPI *SmbusPpi, IN EFI_SMBUS_DEVICE_ADDRESS SmbusSlaveAddress, IN EFI_SMBUS_DEVICE_COMMAND SmbusDeviceCommand, IN CLOCK_GEN_DATA_INFO *InitTable, IN UINT8 *DataBuffer, IN UINTN DataLength ) { EFI_SMBUS_OPERATION SmbusOperation; UINTN InitTableIndex; EFI_STATUS Status; UINTN OperationLength; BOOLEAN SmbusWriteBlock; EFI_SMBUS_DEVICE_COMMAND Command; UINT8 FixData; UINT8 Index; Index = 0; FixData = 0; InitTableIndex = 0; SmbusWriteBlock = FALSE; while (InitTable [InitTableIndex].WriteType != CLKGEN_DATA_END) { switch (InitTable [InitTableIndex].WriteType) { case CLKGEN_BLOCK_OPERATION: case CLKGEN_FAST_BLOCK_OPERATION: DataBuffer [InitTable[InitTableIndex].Byte] = ((DataBuffer [InitTable[InitTableIndex].Byte] & InitTable [InitTableIndex].AndSourceValue) | InitTable [InitTableIndex].OrSourceValue); SmbusWriteBlock = TRUE; break; case CLKGEN_BYTE_OPERATION: SmbusOperation = EfiSmbusWriteByte; OperationLength = 1; Command = InitTable [InitTableIndex].Byte; FixData = ((DataBuffer [InitTable[InitTableIndex].Byte] & InitTable [InitTableIndex].AndSourceValue) | InitTable [InitTableIndex].OrSourceValue); Status = SmbusPpi->Execute ( SmbusPpi, SmbusSlaveAddress, Command, SmbusOperation, FALSE, &OperationLength, &FixData ); if (EFI_ERROR (Status)) { return Status; } break; default: break; } InitTableIndex++; } if (SmbusWriteBlock) { Command = SmbusDeviceCommand; SmbusOperation = EfiSmbusWriteBlock; while (TRUE) { if (DataLength > MAX_SMBUS_RW_BYTE) { OperationLength = MAX_SMBUS_RW_BYTE; Status = SmbusPpi->Execute ( SmbusPpi, SmbusSlaveAddress, Command, SmbusOperation, FALSE, &OperationLength, &DataBuffer[MAX_SMBUS_RW_BYTE * Index] ); if (EFI_ERROR (Status)) { return Status; } Index++; DataLength -= MAX_SMBUS_RW_BYTE; } else { OperationLength = DataLength; Status = SmbusPpi->Execute ( SmbusPpi, SmbusSlaveAddress, Command, SmbusOperation, FALSE, &OperationLength, &DataBuffer[MAX_SMBUS_RW_BYTE * Index] ); if (EFI_ERROR (Status)) { return Status; } break; } } } return EFI_SUCCESS; } /** Shift Byte. @param ByteValue Original byte value @param MaskValue Mask value @retval EFI_SUCCESS If byte value shift success. **/ EFI_STATUS ShiftBit ( IN UINT8 ByteValue, IN UINT8 MaskValue, OUT UINT8 *RealByteValue ) { *RealByteValue = ByteValue & MaskValue; return EFI_SUCCESS; }