/** @file
TigerLake iPCM DXE driver
This DXE driver provides the support of the calibration data.
Copyright (c) 2019, Intel Corporation. All rights reserved.
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}