414 lines
12 KiB
C
414 lines
12 KiB
C
/** @file
|
|
TigerLake iPCM DXE driver
|
|
|
|
This DXE driver provides the support of the calibration data.
|
|
|
|
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
|
|
This software and associated documentation (if any) is furnished
|
|
under a license and may only be used or copied in accordance
|
|
with the terms of the license. Except as permitted by such
|
|
license, no part of this software or documentation may be
|
|
reproduced, stored in a retrieval system, or transmitted in any
|
|
form or by any means without the express written consent of
|
|
Intel Corporation.
|
|
|
|
**/
|
|
#include <Uefi.h>
|
|
#include <Library/UefiLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Protocol/Spi.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/MmPciLib.h>
|
|
#include <SetupVariable.h>
|
|
|
|
#include "..\..\Include\E3DongleLib.h"
|
|
#include "E3DongleDxe.h"
|
|
|
|
|
|
|
|
/**
|
|
This function calculates an UINT8 checksum.
|
|
|
|
@param[in] Buffer Pointer to buffer to checksum
|
|
@param[in] Size Number of bytes to checksum
|
|
|
|
@retval EFI_SUCCESS The function completed successfully.
|
|
**/
|
|
UINT8
|
|
Checksum (
|
|
IN VOID *Buffer,
|
|
IN UINTN Size
|
|
)
|
|
{
|
|
UINT8 Sum;
|
|
UINT8 *Ptr;
|
|
|
|
Sum = 0;
|
|
///
|
|
/// Initialize pointer
|
|
///
|
|
Ptr = Buffer;
|
|
|
|
///
|
|
/// add all content of buffer
|
|
///
|
|
while (Size--) {
|
|
Sum = (UINT8) (Sum + (*Ptr++));
|
|
}
|
|
///
|
|
/// check checksum
|
|
///
|
|
return ((0xFF-Sum+1) & 0xFF);
|
|
}
|
|
|
|
|
|
/**
|
|
This function calculates an UINT8 checksum.
|
|
|
|
@param[in] Buffer Pointer to buffer to checksum
|
|
@param[in] Size Number of bytes to checksum
|
|
|
|
@retval EFI_SUCCESS The function completed successfully.
|
|
**/
|
|
EFI_STATUS
|
|
CheckChecksum (
|
|
IN VOID *Buffer,
|
|
IN UINTN Size
|
|
)
|
|
{
|
|
///
|
|
/// check checksum
|
|
///
|
|
if (Checksum(Buffer, Size) != 0)
|
|
return EFI_CRC_ERROR;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
ReadPDRData (
|
|
IN UINTN offset,
|
|
IN UINTN length,
|
|
OUT UINT8 *buf
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
PCH_SPI_PROTOCOL *mSpiProtocol;
|
|
|
|
Status = gBS->LocateProtocol (
|
|
&gPchSpiProtocolGuid,
|
|
NULL,
|
|
(VOID **)&mSpiProtocol
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Print(L"Cannot locate PchSpiProtocol!!!\n");
|
|
return Status;
|
|
}
|
|
|
|
Status = mSpiProtocol->FlashRead(
|
|
mSpiProtocol,
|
|
FlashRegionPlatformData,
|
|
(UINT32) offset,
|
|
(UINT32) length,
|
|
(UINT8 *)buf
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Print(L"Cannot read the data in PDR!!!\n");
|
|
return Status;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
This function handles E3DongleReadyToBootCallback task at the ready to boot
|
|
|
|
@param[in] Event The Event this notify function registered to.
|
|
@param[in] Context Pointer to the context data registered to the
|
|
Event.
|
|
**/
|
|
E3_CALIBRATION_STATUS
|
|
E3DongleUpdateCalibrationData (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
E3_CALIBRATION_STATUS CalibrationDataStatus = E3DongleNotAttached;
|
|
EFI_STATUS Status;
|
|
E3_CALIBRATION_DATA_HEADER E3CalibrationDataHeader;
|
|
E3_CALIBRATION_DATA_HEADER *pHeader;
|
|
UINT8 * PdrBuffer = NULL;
|
|
UINT8 * DongleBuffer = NULL;
|
|
|
|
DEBUG ((DEBUG_INFO, "E3DongleUpdateCalibrationData: enter...!\n"));
|
|
|
|
//gBS->CloseEvent (Event);
|
|
|
|
//
|
|
// Check if E3 Dongle is attached.
|
|
// If no attached, exit directly.
|
|
//
|
|
if (!IsE3DongleAttached()) {
|
|
//SwitchE3MuxMode(0); // switch to usb mode
|
|
Print(L"E3 Dongle: Not attached. \n");
|
|
gBS->Stall(500000);
|
|
return E3DongleNotAttached;
|
|
}
|
|
|
|
//
|
|
// If E3 Dongle is attached,
|
|
// 1. switch USB/I2C mux to I2C mode and set E3 chip to normal mode (default is slow mode)
|
|
// 2. read the E3 calibration data from PDR region;
|
|
// If no data, prompt that the board need E3 calibration before measurement, then wait for 10S and exit (CalibrationDataStatus = 1);
|
|
//
|
|
// 3. read the calibration data from E3 dongle;
|
|
// If the data in E3 dongle is existing, valid and different from the data on the current board, ask user to confirm if it need to be updated (CalibrationDataStatus = 2).
|
|
// If E3 dongle data is same as the data on the current board, prompt that the calibration in E3 dongle is same as the current one (CalibrationDataStatus = 3).
|
|
// If E3 dongle data is not existing or not valid, prompt that the update progress until it's completed(CalibrationDataStatus = 4).
|
|
//
|
|
|
|
Print(L"E3 Dongle: Attached! \n");
|
|
|
|
//Turn on E3 chip power which is supplied by dongle
|
|
ExternalPowerControlForE3Chip(TRUE);
|
|
|
|
//SwitchE3MuxMode(1); // switch to i2c mode
|
|
|
|
// Read the E3 calibration header from PDR (Platform Data Region)
|
|
Status = ReadPDRData(E3_CALIBRATION_DATA_OFFSET_IN_PDR, sizeof (E3_CALIBRATION_DATA_HEADER), (UINT8*) &E3CalibrationDataHeader);
|
|
if (EFI_ERROR (Status)) {
|
|
Print(L"E3 Dongle Error: Cannot read the header in PDR!!!\n");
|
|
return E3ReadPDRError;
|
|
}
|
|
// Check if the signature in the header is valid
|
|
if (E3CalibrationDataHeader.Signature != E3_CALIBRATION_DATA_HEADER_SIGNATURE) {
|
|
Print(L"E3 Dongle Error: The data is not existing-invalid signature in PDR!!!\n");
|
|
return E3DataNotInPDR;
|
|
}
|
|
// Allocate the buffer for PDR.
|
|
Status= gBS->AllocatePool(EfiBootServicesData, E3CalibrationDataHeader.Length, &PdrBuffer);
|
|
if (EFI_ERROR (Status)) {
|
|
Print(L"E3 Dongle Error: Cannot AllocatePool!!!\n");
|
|
return E3DataNotInPDR;
|
|
}
|
|
// Copy the header to the buffer
|
|
CopyMem((VOID *)PdrBuffer, (VOID *)&E3CalibrationDataHeader, sizeof(E3CalibrationDataHeader));
|
|
// Read the data from PDR
|
|
Status = ReadPDRData(E3CalibrationDataHeader.DataOffset, (UINTN)(E3CalibrationDataHeader.DataLength & 0xFFFF), (UINT8 *)(PdrBuffer + sizeof(E3CalibrationDataHeader)));
|
|
if (EFI_ERROR (Status)) {
|
|
Print(L"E3 Dongle Error: Cannot read the header in PDR!!!\n");
|
|
CalibrationDataStatus = E3ReadPDRError;
|
|
goto Error_Handling;
|
|
}
|
|
|
|
// Check the checksum
|
|
Status = CheckChecksum(PdrBuffer, E3CalibrationDataHeader.Length);
|
|
if (EFI_ERROR (Status)) {
|
|
Print(L"E3 Dongle Error: The data is not existing-invalid checksum in PDR!!!\n");
|
|
CalibrationDataStatus = E3DataNotInPDR;
|
|
goto Error_Handling;
|
|
}
|
|
|
|
// Fix the data offset and recalculate the checksum in PdrBuffer
|
|
pHeader = (E3_CALIBRATION_DATA_HEADER*)PdrBuffer;
|
|
pHeader->DataOffset = sizeof(E3_CALIBRATION_DATA_HEADER);
|
|
pHeader->Checksum = 0;
|
|
pHeader->Checksum = Checksum(PdrBuffer, pHeader->Length);
|
|
|
|
//Only for debug
|
|
Print(L"E3 Dongle Debug: The header in PDR*****\n");
|
|
Print(L"Signature: 0x%X\n", pHeader->Signature);
|
|
Print(L"Length: %d\n", pHeader->Length);
|
|
Print(L"Revision: 0x%X\n", pHeader->Revision);
|
|
Print(L"Checksum: 0x%X\n", pHeader->Checksum);
|
|
Print(L"DataOffset: 0x%X\n", pHeader->DataOffset);
|
|
Print(L"DataLength: 0x%X\n", pHeader->DataLength);
|
|
Print(L"E3 Dongle Debug: *********************\n");
|
|
|
|
ZeroMem(&E3CalibrationDataHeader,sizeof(E3CalibrationDataHeader));
|
|
|
|
// Read the E3 dongle EEPROM
|
|
Status = ReadE3DongleBuffer(0, sizeof(E3CalibrationDataHeader),(UINT8*)&E3CalibrationDataHeader);
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"E3 Dongle Error: cannot read the dongle EEPROM over I2C!!!\n");
|
|
CalibrationDataStatus = E3DataI2CError;
|
|
goto Error_Handling;
|
|
}
|
|
|
|
// Check if the dongle has valid signature
|
|
if (E3CalibrationDataHeader.Signature != E3_CALIBRATION_DATA_HEADER_SIGNATURE) {
|
|
Print(L"E3 Dongle Error: The data is not existing-invalid signature in dongle!!!\n");
|
|
CalibrationDataStatus = E3DataNotInDongle;
|
|
goto Update_Dongle;
|
|
}
|
|
|
|
// Allocate the buffer for dongle data
|
|
Status= gBS->AllocatePool(EfiBootServicesData, E3CalibrationDataHeader.Length, &DongleBuffer);
|
|
if (EFI_ERROR (Status)) {
|
|
Print(L"E3 Dongle Error: Cannot AllocatePool!!!\n");
|
|
CalibrationDataStatus = E3DataCommonError;
|
|
goto Update_Dongle;
|
|
}
|
|
|
|
// Read all data from dongle
|
|
Status = ReadE3DongleBuffer(0, (UINTN)(E3CalibrationDataHeader.Length & 0xFFFF),DongleBuffer);
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"E3 Dongle Error: cannot read the dongle EEPROM over I2C!!!\n");
|
|
CalibrationDataStatus = E3DataI2CError;
|
|
goto Error_Handling;
|
|
}
|
|
|
|
// Check if the data in E3 dongle is same as it in PDR
|
|
if (CompareMem(PdrBuffer,DongleBuffer, E3CalibrationDataHeader.Length) == 0) {
|
|
CalibrationDataStatus = E3DataAlreadyExistingAndSame;
|
|
} else {
|
|
CalibrationDataStatus = E3DataAlreadyExistingButDifferent;
|
|
}
|
|
|
|
Update_Dongle:
|
|
//
|
|
// Take the action according to the CalibrationDataStatus
|
|
//
|
|
if (CalibrationDataStatus == E3DataAlreadyExistingAndSame) {
|
|
Print(L"E3 Dongle Info: the data has already been in the dongle, skip to update.\n");
|
|
} else {
|
|
Print(L"E3 Dongle Info: start to update the data in the dongle EEPROM, please wait...\n");
|
|
Status = WriteE3DongleBuffer(0, (UINTN)(pHeader->Length & 0xFFFF),PdrBuffer);
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"E3 Dongle Error: cannot write the dongle EEPROM over I2C!!!\n");
|
|
CalibrationDataStatus = E3DataI2CError;
|
|
goto Error_Handling;
|
|
}
|
|
|
|
// Allocate the buffer for dongle data
|
|
Status= gBS->AllocatePool(EfiBootServicesData, pHeader->Length, &DongleBuffer);
|
|
if (EFI_ERROR (Status)) {
|
|
Print(L"E3 Dongle Error: Cannot AllocatePool!!!\n");
|
|
CalibrationDataStatus = E3DataCommonError;
|
|
goto Update_Dongle;
|
|
}
|
|
|
|
// Read all data from dongle
|
|
Status = ReadE3DongleBuffer(0, (UINTN)(pHeader->Length & 0xFFFF),DongleBuffer);
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"E3 Dongle Error: cannot read the dongle EEPROM over I2C!!!\n");
|
|
CalibrationDataStatus = E3DataI2CError;
|
|
goto Error_Handling;
|
|
}
|
|
|
|
// Check if the data in E3 dongle is same as it in PDR
|
|
if (CompareMem(PdrBuffer,DongleBuffer, pHeader->Length) == 0) {
|
|
CalibrationDataStatus = E3DataUpdateSuccessful;
|
|
Print(L"E3 Dongle: complete updating data in dongle.\n");
|
|
} else {
|
|
CalibrationDataStatus = E3DataCommonError;
|
|
Print(L"E3 Dongle: Failed to update data in dongle!!!!\n");
|
|
}
|
|
//Print(L"E3 Dongle: PDR header=0x%x\n", *(UINT32*)(&PdrBuffer[0]));
|
|
}
|
|
|
|
Error_Handling:
|
|
if (PdrBuffer != NULL) {
|
|
gBS->FreePool(PdrBuffer);
|
|
}
|
|
|
|
if (DongleBuffer != NULL) {
|
|
gBS->FreePool(DongleBuffer);
|
|
}
|
|
|
|
return CalibrationDataStatus;
|
|
|
|
}
|
|
|
|
/**
|
|
This function handles E3DongleReadyToBootCallback task at the ready to boot
|
|
|
|
@param[in] Event The Event this notify function registered to.
|
|
@param[in] Context Pointer to the context data registered to the
|
|
Event.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
E3DongleReadyToBootCallback (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
E3_CALIBRATION_STATUS CalibrationDataStatus = E3DongleNotAttached;
|
|
|
|
DEBUG ((DEBUG_INFO, "E3DongleReadyToBootCallback: enter...\n"));
|
|
|
|
CalibrationDataStatus = E3DongleUpdateCalibrationData(Event, Context);
|
|
|
|
gBS->CloseEvent (Event);
|
|
}
|
|
|
|
|
|
/**
|
|
Registers callback for E3DongleReadyToBootCallback
|
|
|
|
**/
|
|
VOID
|
|
RegisterReadyToBootCallback (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_EVENT Event;
|
|
|
|
Status = EfiCreateEventReadyToBootEx (
|
|
TPL_NOTIFY,
|
|
E3DongleReadyToBootCallback,
|
|
NULL,
|
|
&Event
|
|
);
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
E3DongleDxeEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN VarDataSize;
|
|
SETUP_DATA SystemConfiguration;
|
|
|
|
DEBUG ((DEBUG_INFO, "E3DongleDxeEntryPoint: Enter...\n"));
|
|
|
|
VarDataSize = sizeof (SETUP_DATA);
|
|
Status = gRT->GetVariable (
|
|
L"Setup",
|
|
&gSetupVariableGuid,
|
|
NULL,
|
|
&VarDataSize,
|
|
&SystemConfiguration
|
|
);
|
|
if (EFI_ERROR(Status))
|
|
{
|
|
DEBUG ((DEBUG_INFO, "E3DongleDxeEntryPoint: Cannot get BIOS setup variable!\n"));
|
|
return Status;
|
|
}
|
|
|
|
if (SystemConfiguration.iRPMMode == IRPM_BIOSETUP_DONGLEMODE) {
|
|
DEBUG ((DEBUG_INFO, "E3DongleDxeEntryPoint: Dongle mode is enabled, register callback!\n"));
|
|
RegisterReadyToBootCallback();
|
|
}
|
|
|
|
//E3DongleUpdateCalibrationData(NULL, NULL);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|