/** @file
Copyright (c) 2014, Applied Micro Curcuit Corporation. All rights reserved.
Copyright (c) 2015, Hisilicon Limited. All rights reserved.
Copyright (c) 2015, Linaro Limited. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
This driver is called to initialize the FW part of the PHY in preparation
for the OS.
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// Turn on debug message by enabling below define
//#define ACPI_DEBUG
#ifdef ACPI_DEBUG
#define DBG(arg...) DEBUG((EFI_D_ERROR,## arg))
#else
#define DBG(arg...)
#endif
#define EFI_ACPI_MAX_NUM_TABLES 20
#define DSDT_SIGNATURE 0x54445344
#define D02_ACPI_ETH_ID "HISI00C1"
#define D03_ACPI_ETH_ID "HISI00C2"
#define ACPI_ETH_MAC_KEY "local-mac-address"
#define PREFIX_VARIABLE_NAME L"MAC"
#define PREFIX_VARIABLE_NAME_COMPAT L"RGMII_MAC"
#define MAC_MAX_LEN 30
EFI_STATUS GetEnvMac(
IN UINTN MacNextID,
IN OUT UINT8 *MacBuffer)
{
EFI_MAC_ADDRESS Mac;
EFI_STATUS Status;
HISI_BOARD_NIC_PROTOCOL *OemNic = NULL;
Status = gBS->LocateProtocol(&gHisiBoardNicProtocolGuid, NULL, (VOID **)&OemNic);
if(EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "[%a]:[%dL] LocateProtocol failed %r\n", __FUNCTION__, __LINE__, Status));
return Status;
}
Status = OemNic->GetMac(&Mac, MacNextID);
if(EFI_ERROR(Status))
{
DEBUG((EFI_D_ERROR, "[%a]:[%dL] GetMac failed %r\n", __FUNCTION__, __LINE__, Status));
return Status;
}
CopyMem (MacBuffer, &Mac, 6);
DEBUG((EFI_D_ERROR, "Port %d MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
MacNextID,
MacBuffer[0],
MacBuffer[1],
MacBuffer[2],
MacBuffer[3],
MacBuffer[4],
MacBuffer[5]
));
return EFI_SUCCESS;
}
EFI_STATUS _SearchReplacePackageMACAddress(
IN EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol,
IN EFI_ACPI_HANDLE ChildHandle,
IN UINTN Level,
IN OUT BOOLEAN *Found,
IN UINTN MacNextID)
{
// ASL template for ethernet driver:
/*
* Name (_DSD, Package () {
* ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
* Package () {
* Package (2) {"mac-address", Package (6) { 00, 11, 22, 33, 44, 55 }}
* Package (2) {"phy-channel", 0},
* Package (2) {"phy-mode", "rgmii"},
* Package (2) {"max-transfer-unit", 0x5dc}, // MTU of 1500
* Package (2) {"max-speed", 0x3e8}, // 1000 Mbps
* }
* })
*/
EFI_STATUS Status;
EFI_ACPI_DATA_TYPE DataType;
CONST UINT8 *Data;
CONST VOID *Buffer;
UINTN DataSize;
UINTN Count;
EFI_ACPI_HANDLE CurrentHandle;
EFI_ACPI_HANDLE NextHandle;
UINT8 MACBuffer[MAC_MAX_LEN];
DBG("In Level:%d\n", Level);
Status = EFI_SUCCESS;
for (CurrentHandle = NULL; ;) {
Status = AcpiTableProtocol->GetChild(ChildHandle, &CurrentHandle);
if (Level != 3 && (EFI_ERROR(Status) || CurrentHandle == NULL))
break;
Status = AcpiTableProtocol->GetOption(CurrentHandle, 0, &DataType, &Buffer, &DataSize);
Data = Buffer;
DBG("_DSD Child Subnode Store Op Code 0x%02X 0x%02X %02X\n",
DataSize, Data[0], DataSize > 1 ? Data[1] : 0);
if (Level < 2 && Data[0] != AML_PACKAGE_OP)
continue;
if (Level == 2 && Data[0] == AML_STRING_PREFIX) {
Status = AcpiTableProtocol->GetOption(CurrentHandle, 1, &DataType, &Buffer, &DataSize);
if (EFI_ERROR(Status))
break;
DBG(" _DSD Child Subnode Store Op Code 0x%02X 0x%02X %02X\n",
DataSize, Data[0], DataSize > 1 ? Data[1] : 0);
Data = Buffer;
if (DataType != EFI_ACPI_DATA_TYPE_STRING
|| AsciiStrCmp((CHAR8 *) Data, ACPI_ETH_MAC_KEY) != 0)
continue;
DBG("_DSD Key Type %d. Found MAC address key\n", DataType);
//
// We found the node.
//
*Found = TRUE;
continue;
}
if (Level == 3 && *Found) {
//Update the MAC
Status = GetEnvMac(MacNextID, MACBuffer);
if (EFI_ERROR(Status))
break;
for (Count = 0; Count < 6; Count++) {
Status = AcpiTableProtocol->GetOption(CurrentHandle, 1, &DataType, &Buffer, &DataSize);
if (EFI_ERROR(Status))
break;
Data = Buffer;
DBG(" _DSD Child Subnode Store Op Code 0x%02X 0x%02X %02X DataType 0x%X\n",
DataSize, Data[0], DataSize > 1 ? Data[1] : 0, DataType);
if (DataType != EFI_ACPI_DATA_TYPE_UINT)
break;
// only need one byte.
// FIXME: Assume the CPU is little endian
Status = AcpiTableProtocol->SetOption(CurrentHandle, 1, (VOID *)&MACBuffer[Count], sizeof(UINT8));
if (EFI_ERROR(Status))
break;
Status = AcpiTableProtocol->GetChild(ChildHandle, &CurrentHandle);
if (EFI_ERROR(Status) || CurrentHandle == NULL)
break;
}
break;
}
if (Level > 3)
break;
//Search next package
AcpiTableProtocol->Open((VOID *) Buffer, &NextHandle);
Status = _SearchReplacePackageMACAddress(AcpiTableProtocol, NextHandle, Level + 1, Found, MacNextID);
AcpiTableProtocol->Close(NextHandle);
if (!EFI_ERROR(Status))
break;
}
return Status;
}
EFI_STATUS SearchReplacePackageMACAddress(
IN EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol,
IN EFI_ACPI_HANDLE ChildHandle,
IN UINTN MacNextID)
{
BOOLEAN Found = FALSE;
UINTN Level = 0;
return _SearchReplacePackageMACAddress(AcpiTableProtocol, ChildHandle, Level, &Found, MacNextID);
}
EFI_STATUS
GetEthID (
EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol,
EFI_ACPI_HANDLE ChildHandle,
UINTN *EthID
)
{
EFI_STATUS Status;
EFI_ACPI_DATA_TYPE DataType;
CHAR8 Data[5];
CONST VOID *Buffer;
UINTN DataSize;
// Get NameString ETHx
Status = AcpiTableProtocol->GetOption (ChildHandle, 1, &DataType, &Buffer, &DataSize);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "[%a:%d] Get NameString failed: %r\n", __FUNCTION__, __LINE__, Status));
return Status;
}
CopyMem (Data, Buffer, 4);
DBG("Size %p Data %02x %02x %02x %02x\n", DataSize, Data[0], Data[1], Data[2], Data[3]);
Data[4] = '\0';
if (DataSize != 4 ||
AsciiStrnCmp ("ETH", Data, 3) != 0 ||
Data[3] > '9' || Data[3] < '0') {
DEBUG ((EFI_D_ERROR, "[%a:%d] The NameString %a is not ETHn\n", __FUNCTION__, __LINE__, Data));
return EFI_INVALID_PARAMETER;
}
*EthID = Data[3] - '0';
return EFI_SUCCESS;
}
EFI_STATUS ProcessDSDTDevice (
EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol,
EFI_ACPI_HANDLE ChildHandle)
{
EFI_STATUS Status;
EFI_ACPI_DATA_TYPE DataType;
CONST UINT8 *Data;
CONST VOID *Buffer;
UINTN DataSize;
EFI_ACPI_HANDLE DevHandle;
INTN Found = 0;
UINTN MacNextID;
Status = AcpiTableProtocol->GetOption(ChildHandle, 0, &DataType, &Buffer, &DataSize);
if (EFI_ERROR(Status))
return EFI_SUCCESS;
Data = Buffer;
//
// Skip all non-device type
//
if (DataSize != 2 || Data[0] != AML_EXT_OP || Data[1] != AML_EXT_DEVICE_OP)
return EFI_SUCCESS;
//
// Walk the device type node
//
for (DevHandle = NULL; ; ) {
Status = AcpiTableProtocol->GetChild(ChildHandle, &DevHandle);
if (EFI_ERROR(Status) || DevHandle == NULL)
break;
//
// Search for _HID with Ethernet ID
//
Status = AcpiTableProtocol->GetOption(DevHandle, 0, &DataType, &Buffer, &DataSize);
if (EFI_ERROR(Status))
break;
Data = Buffer;
DBG("Data Type 0x%02X %02X\n", Data[0], DataSize > 1 ? Data[1] : 0);
if (DataSize == 1 && Data[0] == AML_NAME_OP) {
Status = AcpiTableProtocol->GetOption(DevHandle, 1, &DataType, &Buffer, &DataSize);
if (EFI_ERROR(Status))
break;
Data = Buffer;
if (DataType == EFI_ACPI_DATA_TYPE_NAME_STRING) {
if (AsciiStrnCmp((CHAR8 *) Data, "_HID", 4) == 0) {
EFI_ACPI_HANDLE ValueHandle;
Status = AcpiTableProtocol->GetOption(DevHandle, 2, &DataType, &Buffer, &DataSize);
if (EFI_ERROR(Status))
break;
if (DataType != EFI_ACPI_DATA_TYPE_CHILD)
continue;
AcpiTableProtocol->Open((VOID *) Buffer, &ValueHandle);
Status = AcpiTableProtocol->GetOption(ValueHandle, 1, &DataType, &Buffer, &DataSize);
Data = Buffer;
DBG("[%a:%d] - _HID = %a\n", __FUNCTION__, __LINE__, Data);
if (EFI_ERROR(Status) ||
DataType != EFI_ACPI_DATA_TYPE_STRING ||
((AsciiStrCmp((CHAR8 *) Data, D02_ACPI_ETH_ID) != 0) &&
(AsciiStrCmp((CHAR8 *) Data, D03_ACPI_ETH_ID) != 0))) {
AcpiTableProtocol->Close(ValueHandle);
Found = 0;
continue;
}
DBG("Found Ethernet device\n");
AcpiTableProtocol->Close(ValueHandle);
Status = GetEthID (AcpiTableProtocol, ChildHandle, &MacNextID);
if (EFI_ERROR (Status)) {
continue;
}
Found = 1;
} else if (Found == 1 && AsciiStrnCmp((CHAR8 *) Data, "_DSD", 4) == 0) {
//
// Patch MAC address for open source kernel
//
EFI_ACPI_HANDLE PkgHandle;
Status = AcpiTableProtocol->GetOption(DevHandle, 2, &DataType, &Buffer, &DataSize);
if (EFI_ERROR(Status))
break;
if (DataType != EFI_ACPI_DATA_TYPE_CHILD)
continue;
//
// Open package data
//
AcpiTableProtocol->Open((VOID *) Buffer, &PkgHandle);
Status = AcpiTableProtocol->GetOption(PkgHandle, 0, &DataType, &Buffer, &DataSize);
Data = Buffer;
DBG("_DSD Subnode Store Op Code 0x%02X %02X\n",
Data[0], DataSize > 1 ? Data[1] : 0);
//
// Walk the _DSD node
//
if (DataSize == 1 && Data[0] == AML_PACKAGE_OP)
Status = SearchReplacePackageMACAddress(AcpiTableProtocol, PkgHandle, MacNextID);
AcpiTableProtocol->Close(PkgHandle);
}
}
}
}
return EFI_SUCCESS;
}
BOOLEAN
IsSbScope (
EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol,
EFI_ACPI_HANDLE ChildHandle
)
{
EFI_STATUS Status;
EFI_ACPI_DATA_TYPE DataType;
CONST UINT8 *Data;
CONST VOID *Buffer;
UINTN DataSize;
Status = AcpiTableProtocol->GetOption (ChildHandle, 0, &DataType, &Buffer, &DataSize);
if (EFI_ERROR(Status)) return FALSE;
Data = Buffer;
if (DataSize != 1 || Data[0] != AML_SCOPE_OP) {
return FALSE;
}
return TRUE;
}
EFI_STATUS ProcessDSDTChild(
EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol,
EFI_ACPI_HANDLE ChildHandle)
{
EFI_STATUS Status;
EFI_ACPI_HANDLE DevHandle;
// Check Scope(_SB) at first
if (!IsSbScope (AcpiTableProtocol, ChildHandle)) {
return ProcessDSDTDevice (AcpiTableProtocol, ChildHandle);
}
for (DevHandle = NULL; ; ) {
Status = AcpiTableProtocol->GetChild (ChildHandle, &DevHandle);
if (EFI_ERROR(Status) || DevHandle == NULL) {
break;
}
ProcessDSDTDevice (AcpiTableProtocol, DevHandle);
}
return EFI_SUCCESS;
}
static EFI_STATUS ProcessDSDT(
EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol,
EFI_ACPI_HANDLE TableHandle)
{
EFI_STATUS Status;
EFI_ACPI_HANDLE ChildHandle;
//
// Parse table for device type
DBG ("[%a:%d] - TableHandle=%p\n", __FUNCTION__, __LINE__, TableHandle);
for (ChildHandle = NULL; ; ) {
Status = AcpiTableProtocol->GetChild(TableHandle, &ChildHandle);
DBG ("[%a:%d] - Child=%p, %r\n", __FUNCTION__, __LINE__, ChildHandle, Status);
if (EFI_ERROR(Status))
break;
if (ChildHandle == NULL)
break;
ProcessDSDTChild(AcpiTableProtocol, ChildHandle);
}
return EFI_SUCCESS;
}
EFI_STATUS EthMacInit(void)
{
EFI_STATUS Status;
EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol;
EFI_ACPI_SDT_HEADER *Table;
EFI_ACPI_TABLE_VERSION TableVersion;
UINTN TableKey;
EFI_ACPI_HANDLE TableHandle;
UINTN i;
DEBUG ((EFI_D_ERROR, "Updating Ethernet MAC in ACPI DSDT...\n"));
//
// Find the AcpiTable protocol
Status = gBS->LocateProtocol(&gEfiAcpiSdtProtocolGuid, NULL, (VOID**) &AcpiTableProtocol);
if (EFI_ERROR(Status)) {
DBG("Unable to locate ACPI table protocol\n");
return EFI_SUCCESS;
}
//
// Search for DSDT Table
for (i = 0; i < EFI_ACPI_MAX_NUM_TABLES; i++) {
Status = AcpiTableProtocol->GetAcpiTable(i, &Table, &TableVersion, &TableKey);
if (EFI_ERROR(Status))
break;
if (Table->Signature != DSDT_SIGNATURE)
continue;
Status = AcpiTableProtocol->OpenSdt(TableKey, &TableHandle);
if (EFI_ERROR(Status))
break;
ProcessDSDT(AcpiTableProtocol, TableHandle);
AcpiTableProtocol->Close(TableHandle);
}
return EFI_SUCCESS;
}