/** @file Copyright (c) 2014, Applied Micro Curcuit Corporation. All rights reserved.
Copyright (c) 2015 - 2020, Hisilicon Limited. All rights reserved.
Copyright (c) 2015, Linaro Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent 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 #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 ACPI_ETH_MAC_KEY "local-mac-address" #define ACPI_ETH_SAS_KEY "sas-addr" #define PREFIX_VARIABLE_NAME L"MAC" #define PREFIX_VARIABLE_NAME_COMPAT L"RGMII_MAC" #define ADDRESS_MAX_LEN 30 CHAR8 *mHisiAcpiDevId[] = {"HISI00C1","HISI00C2","HISI0162"}; typedef enum { DsdtDeviceUnknown, DsdtDeviceLan, DsdtDeviceSas } DSDT_DEVICE_TYPE; STATIC 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; } STATIC EFI_STATUS GetSasAddress ( IN UINT8 Index, IN OUT UINT8 *SasAddrBuffer ) { EFI_STATUS Status; HISI_SAS_CONFIG_PROTOCOL *HisiSasConf; if (SasAddrBuffer == NULL) { return EFI_INVALID_PARAMETER; } Status = gBS->LocateProtocol (&gHisiSasConfigProtocolGuid, NULL, (VOID **)&HisiSasConf); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Locate Sas Config Protocol failed %r\n", Status)); SasAddrBuffer[0] = 0x00; SasAddrBuffer[1] = 0x00; SasAddrBuffer[2] = 0x00; SasAddrBuffer[3] = 0x00; SasAddrBuffer[4] = 0x00; SasAddrBuffer[5] = 0x00; SasAddrBuffer[6] = 0x00; SasAddrBuffer[7] = Index; return Status; } return HisiSasConf->GetAddr (Index, SasAddrBuffer); } STATIC EFI_STATUS UpdateAddressInOption ( IN EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol, IN EFI_ACPI_HANDLE ChildHandle, IN UINTN DevNextID, IN DSDT_DEVICE_TYPE FoundDev ) { EFI_STATUS Status; EFI_ACPI_DATA_TYPE DataType; CONST VOID *Buffer; UINTN DataSize; UINTN Count; EFI_ACPI_HANDLE CurrentHandle; UINT8 *AddressBuffer; UINT8 AddressByte; AddressByte = 0; AddressBuffer = AllocateZeroPool (ADDRESS_MAX_LEN); if (AddressBuffer == NULL) { DEBUG ((DEBUG_ERROR, "%a:%d AllocateZeroPool failed\n", __FILE__, __LINE__)); return EFI_OUT_OF_RESOURCES; } switch (FoundDev) { case DsdtDeviceLan: //Update the MAC Status = GetEnvMac (DevNextID, AddressBuffer); AddressByte = 6; break; case DsdtDeviceSas: //Update SAS Address. Status = GetSasAddress (DevNextID, AddressBuffer); AddressByte = 8; break; default: Status = EFI_INVALID_PARAMETER; } if (EFI_ERROR (Status)) { FreePool (AddressBuffer); return Status; } for (Count = 0; Count < AddressByte; Count++) { Status = AcpiTableProtocol->GetOption (CurrentHandle, 1, &DataType, &Buffer, &DataSize); if (EFI_ERROR (Status)) { break; } if (DataType != EFI_ACPI_DATA_TYPE_UINT) break; // only need one byte. // FIXME: Assume the CPU is little endian Status = AcpiTableProtocol->SetOption ( CurrentHandle, 1, AddressBuffer + Count, sizeof(UINT8)); if (EFI_ERROR (Status)) { break; } Status = AcpiTableProtocol->GetChild (ChildHandle, &CurrentHandle); if (EFI_ERROR (Status) || CurrentHandle == NULL) { break; } } FreePool (AddressBuffer); return Status; } STATIC EFI_STATUS UpdateAddressInPackage ( IN EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol, IN EFI_ACPI_HANDLE ChildHandle, IN UINTN Level, IN OUT BOOLEAN *Found, IN UINTN DevNextID, IN DSDT_DEVICE_TYPE FoundDev ) { // 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; EFI_ACPI_HANDLE CurrentHandle; EFI_ACPI_HANDLE NextHandle; EFI_ACPI_HANDLE Level1Handle; DBG("In Level:%d\n", Level); Level1Handle = NULL; Status = EFI_SUCCESS; for (CurrentHandle = NULL; ;) { Status = AcpiTableProtocol->GetChild(ChildHandle, &CurrentHandle); if (Level == 1) { Level1Handle = 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) && (AsciiStrCmp ((CHAR8 *) Data, ACPI_ETH_SAS_KEY) != 0))) { ChildHandle = Level1Handle; continue; } DBG("_DSD Key Type %d. Found address key\n", DataType); // // We found the node. // *Found = TRUE; continue; } if (Level == 3 && *Found) { Status = UpdateAddressInOption (AcpiTableProtocol, ChildHandle, DevNextID, FoundDev); break; } if (Level > 3) break; //Search next package AcpiTableProtocol->Open((VOID *) Buffer, &NextHandle); Status = UpdateAddressInPackage ( AcpiTableProtocol, NextHandle, Level + 1, Found, DevNextID, FoundDev); AcpiTableProtocol->Close(NextHandle); if (!EFI_ERROR(Status)) break; } return Status; } STATIC EFI_STATUS SearchReplacePackageAddress ( IN EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol, IN EFI_ACPI_HANDLE ChildHandle, IN UINTN DevNextID, IN DSDT_DEVICE_TYPE FoundDev ) { BOOLEAN Found = FALSE; UINTN Level = 0; return UpdateAddressInPackage (AcpiTableProtocol, ChildHandle, Level, &Found, DevNextID, FoundDev); } EFI_STATUS GetDeviceInfo ( EFI_ACPI_SDT_PROTOCOL *AcpiTableProtocol, EFI_ACPI_HANDLE ChildHandle, UINTN *DevID, DSDT_DEVICE_TYPE *FoundDev ) { EFI_STATUS Status; EFI_ACPI_DATA_TYPE DataType; CHAR8 Data[5]; CONST VOID *Buffer; UINTN DataSize; // Get NameString 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) || (Data[3] > '9' || Data[3] < '0')) { DEBUG ((DEBUG_ERROR, "The NameString %a is not ETHn or SASn\n", Data)); return EFI_INVALID_PARAMETER; } if (AsciiStrnCmp ("ETH", Data, 3) == 0) { *FoundDev = DsdtDeviceLan; } else if (AsciiStrnCmp ("SAS", Data, 3) == 0) { *FoundDev = DsdtDeviceSas; } else { DEBUG ((DEBUG_ERROR, "[%a:%d] The NameString %a is not ETHn or SASn\n", __FUNCTION__, __LINE__, Data)); return EFI_INVALID_PARAMETER; } *DevID = 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; DSDT_DEVICE_TYPE FoundDev = DsdtDeviceUnknown; UINTN DevNextID; BOOLEAN HisiAcpiDevNotFound; UINTN Index; 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 Device 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) { AcpiTableProtocol->Close (ValueHandle); FoundDev = DsdtDeviceUnknown; continue; } HisiAcpiDevNotFound = TRUE; for (Index = 0; Index < ARRAY_SIZE (mHisiAcpiDevId); Index++) { if (AsciiStrCmp ((CHAR8 *)Data, mHisiAcpiDevId[Index]) == 0) { HisiAcpiDevNotFound = FALSE; break; } } if (HisiAcpiDevNotFound) { AcpiTableProtocol->Close (ValueHandle); FoundDev = DsdtDeviceUnknown; continue; } DBG("Found device\n"); AcpiTableProtocol->Close(ValueHandle); Status = GetDeviceInfo (AcpiTableProtocol, ChildHandle, &DevNextID, &FoundDev); if (EFI_ERROR (Status)) { continue; } } else if ((FoundDev != DsdtDeviceUnknown) && AsciiStrnCmp((CHAR8 *) Data, "_DSD", 4) == 0) { // // Patch DSD data // 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 = SearchReplacePackageAddress (AcpiTableProtocol, PkgHandle, DevNextID, FoundDev); } AcpiTableProtocol->Close(PkgHandle); } else if (AsciiStrnCmp ((CHAR8 *) Data, "_ADR", 4) == 0) { Status = AcpiTableProtocol->GetOption (DevHandle, 2, &DataType, &Buffer, &DataSize); if (EFI_ERROR (Status)) { break; } if (DataType != EFI_ACPI_DATA_TYPE_CHILD) { continue; } Status = GetDeviceInfo (AcpiTableProtocol, ChildHandle, &DevNextID, &FoundDev); if (EFI_ERROR (Status)) { continue; } } } } else if ((DataSize == 2) && (Data[0] == AML_EXT_OP) && (Data[1] == AML_EXT_DEVICE_OP)) { ProcessDSDTDevice (AcpiTableProtocol, DevHandle); } } 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; } STATIC VOID AcpiCheckSum ( IN OUT EFI_ACPI_SDT_HEADER *Table ) { UINTN ChecksumOffset; UINT8 *Buffer; ChecksumOffset = OFFSET_OF (EFI_ACPI_DESCRIPTION_HEADER, Checksum); Buffer = (UINT8 *)Table; // // set checksum to 0 first // Buffer[ChecksumOffset] = 0; // // Update checksum value // Buffer[ChecksumOffset] = CalculateCheckSum8 (Buffer, Table->Length); } EFI_STATUS UpdateAcpiDsdtTable ( 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); AcpiCheckSum (Table); } return EFI_SUCCESS; }