/** @file
|
Board-specific EC commands.
|
|
Copyright (c) 2021, Baruch Binyamin Doron
|
Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
#include <Base.h>
|
#include <Uefi.h>
|
#include <Library/DebugLib.h>
|
#include <Library/EcLib.h>
|
#include <Library/IoLib.h>
|
|
/* Notes:
|
* - ACPI "CMDB": Writing to this offset is equivalent to sending commands.
|
* The CMDx bytes contain the command parameters.
|
*
|
* TODO - Implement:
|
* - Commands: 0x58, 0xE1 and 0xE2
|
* - 0x51, 0x52: EC flash write?
|
* - ACPI CMDB: 0x63 and 0x64, 0xC7
|
* - 0x0B: Flash write (Boolean argument? Set in offset 0x0B?)
|
*
|
* Reversing vendor's protocols:
|
* - Only read and write are used.
|
* - Query, ACPI "CMDB" processing and command 58 are unused.
|
* - Equivalent KbcPeim is an unused PPI.
|
*
|
* NB: Also look for potential EC library
|
*/
|
|
#define EC_INDEX_IO_PORT 0x1200
|
#define EC_INDEX_IO_HIGH_ADDR_PORT EC_INDEX_IO_PORT+1
|
#define EC_INDEX_IO_LOW_ADDR_PORT EC_INDEX_IO_PORT+2
|
#define EC_INDEX_IO_DATA_PORT EC_INDEX_IO_PORT+3
|
|
/**
|
Reads a byte of EC RAM.
|
|
@param[in] Address Address to read
|
@param[out] Data Data received
|
|
@retval EFI_SUCCESS Command success
|
@retval EFI_INVALID_PARAMETER Data is NULL
|
@retval EFI_DEVICE_ERROR Command error
|
@retval EFI_TIMEOUT Command timeout
|
**/
|
EFI_STATUS
|
EcCmd90Read (
|
IN UINT8 Address,
|
OUT UINT8 *Data
|
)
|
{
|
EFI_STATUS Status;
|
|
if (Data == NULL) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
Status = SendEcCommand (0x90);
|
if (EFI_ERROR (Status)) {
|
DEBUG((DEBUG_ERROR, "%a(): SendEcCommand(0x90) failed!\n", __FUNCTION__));
|
return Status;
|
}
|
|
Status = SendEcData (Address);
|
if (EFI_ERROR (Status)) {
|
DEBUG((DEBUG_ERROR, "%a(): SendEcData(Address) failed!\n", __FUNCTION__));
|
return Status;
|
}
|
|
Status = ReceiveEcData (Data);
|
if (EFI_ERROR (Status)) {
|
DEBUG((DEBUG_ERROR, "%a(): ReceiveEcData(Data) failed!\n", __FUNCTION__));
|
return Status;
|
}
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Writes a byte of EC RAM.
|
|
@param[in] Address Address to write
|
@param[in] Data Data to write
|
|
@retval EFI_SUCCESS Command success
|
@retval EFI_DEVICE_ERROR Command error
|
@retval EFI_TIMEOUT Command timeout
|
**/
|
EFI_STATUS
|
EcCmd91Write (
|
IN UINT8 Address,
|
IN UINT8 Data
|
)
|
{
|
EFI_STATUS Status;
|
|
Status = SendEcCommand (0x91);
|
if (EFI_ERROR (Status)) {
|
DEBUG((DEBUG_ERROR, "%a(): SendEcCommand(0x91) failed!\n", __FUNCTION__));
|
return Status;
|
}
|
|
Status = SendEcData (Address);
|
if (EFI_ERROR (Status)) {
|
DEBUG((DEBUG_ERROR, "%a(): SendEcData(Address) failed!\n", __FUNCTION__));
|
return Status;
|
}
|
|
Status = SendEcData (Data);
|
if (EFI_ERROR (Status)) {
|
DEBUG((DEBUG_ERROR, "%a(): SendEcData(Data) failed!\n", __FUNCTION__));
|
return Status;
|
}
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Query the EC status.
|
|
@param[out] Status EC status byte
|
|
@retval EFI_SUCCESS Command success
|
@retval EFI_INVALID_PARAMETER Data is NULL
|
@retval EFI_DEVICE_ERROR Command error
|
@retval EFI_TIMEOUT Command timeout
|
**/
|
EFI_STATUS
|
EcCmd94Query (
|
OUT UINT8 *Data
|
)
|
{
|
EFI_STATUS Status;
|
|
if (Data == NULL) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
Status = SendEcCommand (0x94);
|
if (EFI_ERROR (Status)) {
|
DEBUG((DEBUG_ERROR, "%a(): SendEcCommand(0x94) failed!\n", __FUNCTION__));
|
return Status;
|
}
|
|
Status = ReceiveEcData (Data);
|
if (EFI_ERROR (Status)) {
|
DEBUG((DEBUG_ERROR, "%a(): ReceiveEcData(Data) failed!\n", __FUNCTION__));
|
return Status;
|
}
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Reads a byte of EC (index) RAM.
|
|
@param[in] Address Address to read
|
@param[out] Data Data received
|
**/
|
VOID
|
EcIdxRead (
|
IN UINT16 Address,
|
OUT UINT8 *Data
|
)
|
{
|
if (Data == NULL) {
|
return;
|
}
|
|
IoWrite8 (EC_INDEX_IO_HIGH_ADDR_PORT, (UINT8) (Address >> 8));
|
IoWrite8 (EC_INDEX_IO_LOW_ADDR_PORT, (UINT8) Address);
|
*Data = IoRead8 (EC_INDEX_IO_DATA_PORT);
|
}
|
|
/**
|
Writes a byte of EC (index) RAM.
|
|
@param[in] Address Address to read
|
@param[in] Data Data received
|
**/
|
VOID
|
EcIdxWrite (
|
IN UINT16 Address,
|
IN UINT8 Data
|
)
|
{
|
IoWrite8 (EC_INDEX_IO_HIGH_ADDR_PORT, (UINT8) (Address >> 8));
|
IoWrite8 (EC_INDEX_IO_LOW_ADDR_PORT, (UINT8) Address);
|
IoWrite8 (EC_INDEX_IO_DATA_PORT, Data);
|
}
|
|
/**
|
Read EC analog-digital converter.
|
TODO: Check if ADC is valid.
|
|
@param[in] Adc
|
@param[out] DataBuffer
|
**/
|
VOID
|
ReadEcAdcConverter (
|
IN UINT8 Adc,
|
OUT UINT16 *DataBuffer
|
)
|
{
|
UINT8 AdcConvertersEnabled; // Contains some ADCs and some DACs
|
UINT8 IdxData;
|
|
if (DataBuffer == NULL) {
|
return;
|
}
|
|
// Backup enabled ADCs
|
EcIdxRead (0xff15, &AdcConvertersEnabled); // ADDAEN
|
|
// Enable desired ADC in bitmask (not enabled by EC FW, not used by vendor FW)
|
EcIdxWrite (0xff15, AdcConvertersEnabled | ((1 << Adc) & 0xf)); // ADDAEN
|
|
// Sample the desired ADC in binary field; OR the start bit
|
EcIdxWrite (0xff18, ((Adc << 1) & 0xf) | 1); // ADCTRL
|
|
// Read the desired ADC
|
EcIdxRead (0xff19, &IdxData); // ADCDAT
|
*DataBuffer = (IdxData << 2);
|
// Lower 2-bits of 10-bit ADC are in high bits of next register
|
EcIdxRead (0xff1a, &IdxData); // ECIF
|
*DataBuffer |= ((IdxData & 0xc0) >> 6);
|
|
// Restore enabled ADCs
|
EcIdxWrite (0xff15, AdcConvertersEnabled); // ADDAEN
|
}
|