/** @file Board-specific EC commands. Copyright (c) 2021, Baruch Binyamin Doron Copyright (c) 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include /* 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 }