/** @file
|
Device driver for the Broadcom GENET controller
|
|
Copyright (c) 2020 Jared McNeill. All rights reserved.
|
Copyright (c) 2020, ARM Limited. All rights reserved.
|
Copyright (c) 2020 Andrey Warkentin <andrey.warkentin@gmail.com>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
#include <Uefi.h>
|
#include <Library/BaseMemoryLib.h>
|
#include <Library/DebugLib.h>
|
#include <Library/IoLib.h>
|
#include <Library/MemoryAllocationLib.h>
|
#include <Library/NetLib.h>
|
#include <Library/UefiBootServicesTableLib.h>
|
#include <Library/UefiLib.h>
|
#include <Protocol/BcmGenetPlatformDevice.h>
|
|
#include "BcmGenetDxe.h"
|
|
STATIC EFI_EVENT mProtocolNotifyEvent;
|
STATIC VOID *mProtocolNotifyEventRegistration;
|
|
/**
|
Tests to see if this driver supports a given controller.
|
|
@param This[in] A pointer to the EFI_DRIVER_BINDING_PROTOCOL
|
instance.
|
@param ControllerHandle[in] The handle of the controller to test.
|
@param RemainingDevicePath[in] The remaining device path.
|
(Ignored - this is not a bus driver.)
|
|
@retval EFI_SUCCESS The driver supports this controller.
|
@retval EFI_ALREADY_STARTED The device specified by ControllerHandle is
|
already being managed by the driver specified
|
by This.
|
@retval EFI_UNSUPPORTED The device specified by ControllerHandle is
|
not supported by the driver specified by This.
|
|
**/
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
GenetDriverBindingSupported (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE ControllerHandle,
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
)
|
{
|
BCM_GENET_PLATFORM_DEVICE_PROTOCOL *Dev;
|
EFI_STATUS Status;
|
|
//
|
// Connect to the non-discoverable device
|
//
|
Status = gBS->OpenProtocol (ControllerHandle,
|
&gBcmGenetPlatformDeviceProtocolGuid,
|
(VOID **)&Dev,
|
This->DriverBindingHandle,
|
ControllerHandle,
|
EFI_OPEN_PROTOCOL_BY_DRIVER);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
//
|
// Clean up.
|
//
|
gBS->CloseProtocol (ControllerHandle,
|
&gBcmGenetPlatformDeviceProtocolGuid,
|
This->DriverBindingHandle,
|
ControllerHandle);
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Callback function to shut down the network device at ExitBootServices
|
|
@param Event Pointer to this event
|
@param Context Event handler private data
|
|
**/
|
STATIC
|
VOID
|
EFIAPI
|
GenetNotifyExitBootServices (
|
EFI_EVENT Event,
|
VOID *Context
|
)
|
{
|
GenetDisableTxRx ((GENET_PRIVATE_DATA *)Context);
|
}
|
|
/**
|
Starts a device controller or a bus controller.
|
|
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
|
instance.
|
@param[in] ControllerHandle The handle of the device to start. This
|
handle must support a protocol interface that
|
supplies an I/O abstraction to the driver.
|
@param[in] RemainingDevicePath The remaining portion of the device path.
|
(Ignored - this is not a bus driver.)
|
|
@retval EFI_SUCCESS The device was started.
|
@retval EFI_DEVICE_ERROR The device could not be started due to a
|
device error.
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a
|
lack of resources.
|
|
**/
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
GenetDriverBindingStart (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE ControllerHandle,
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
|
)
|
{
|
GENET_PRIVATE_DATA *Genet;
|
EFI_STATUS Status;
|
|
// Allocate Resources
|
Genet = AllocateZeroPool (sizeof (GENET_PRIVATE_DATA));
|
if (Genet == NULL) {
|
DEBUG ((DEBUG_ERROR,
|
"%a: Couldn't allocate private data\n", __FUNCTION__));
|
return EFI_OUT_OF_RESOURCES;
|
}
|
|
Status = gBS->OpenProtocol (ControllerHandle,
|
&gBcmGenetPlatformDeviceProtocolGuid,
|
(VOID **)&Genet->Dev,
|
This->DriverBindingHandle,
|
ControllerHandle,
|
EFI_OPEN_PROTOCOL_BY_DRIVER);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR,
|
"%a: Couldn't open protocol: %r\n", __FUNCTION__, Status));
|
goto FreeDevice;
|
}
|
|
Status = GenetDmaAlloc (Genet);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR,
|
"%a: Couldn't allocate DMA buffers: %r\n", __FUNCTION__, Status));
|
goto FreeDevice;
|
}
|
|
Genet->Signature = GENET_DRIVER_SIGNATURE;
|
Genet->RegBase = Genet->Dev->BaseAddress;
|
Genet->Phy.PrivateData = Genet;
|
Genet->Phy.Read = GenetPhyRead;
|
Genet->Phy.Write = GenetPhyWrite;
|
Genet->Phy.Configure = GenetPhyConfigure;
|
Genet->Phy.ResetAction = GenetPhyResetAction;
|
Genet->PhyMode = GENET_PHY_MODE_RGMII_RXID;
|
|
EfiInitializeLock (&Genet->Lock, TPL_CALLBACK);
|
CopyMem (&Genet->Snp, &gGenetSimpleNetworkTemplate, sizeof Genet->Snp);
|
CopyMem (&Genet->Aip, &gGenetAdapterInfoTemplate, sizeof Genet->Aip);
|
|
Genet->Snp.Mode = &Genet->SnpMode;
|
Genet->SnpMode.State = EfiSimpleNetworkStopped;
|
Genet->SnpMode.HwAddressSize = NET_ETHER_ADDR_LEN;
|
Genet->SnpMode.MediaHeaderSize = sizeof (ETHER_HEAD);
|
Genet->SnpMode.MaxPacketSize = MAX_ETHERNET_PKT_SIZE;
|
Genet->SnpMode.NvRamSize = 0;
|
Genet->SnpMode.NvRamAccessSize = 0;
|
Genet->SnpMode.ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
|
EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST |
|
EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
|
Genet->SnpMode.ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
|
EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
|
Genet->SnpMode.MaxMCastFilterCount = 0;
|
Genet->SnpMode.MCastFilterCount = 0;
|
Genet->SnpMode.IfType = NET_IFTYPE_ETHERNET;
|
Genet->SnpMode.MacAddressChangeable = TRUE;
|
Genet->SnpMode.MultipleTxSupported = FALSE;
|
Genet->SnpMode.MediaPresentSupported = TRUE;
|
Genet->SnpMode.MediaPresent = FALSE;
|
|
SetMem (&Genet->SnpMode.BroadcastAddress, sizeof (EFI_MAC_ADDRESS), 0xff);
|
|
CopyMem (&Genet->SnpMode.PermanentAddress, &Genet->Dev->MacAddress,
|
sizeof(EFI_MAC_ADDRESS));
|
CopyMem (&Genet->SnpMode.CurrentAddress, &Genet->Dev->MacAddress,
|
sizeof(EFI_MAC_ADDRESS));
|
|
Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
|
GenetNotifyExitBootServices, Genet,
|
&gEfiEventExitBootServicesGuid,
|
&Genet->ExitBootServicesEvent);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_WARN,
|
"GenetDriverBindingStart: failed to register for ExitBootServices event - %r\n",
|
Status));
|
goto FreeDevice;
|
}
|
|
Status = gBS->InstallMultipleProtocolInterfaces (&ControllerHandle,
|
&gEfiSimpleNetworkProtocolGuid, &Genet->Snp,
|
&gEfiAdapterInformationProtocolGuid, &Genet->Aip,
|
NULL);
|
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR,
|
"%a: Couldn't install protocol interfaces: %r\n", __FUNCTION__, Status));
|
gBS->CloseProtocol (ControllerHandle,
|
&gBcmGenetPlatformDeviceProtocolGuid,
|
This->DriverBindingHandle,
|
ControllerHandle);
|
goto FreeEvent;
|
}
|
|
Genet->ControllerHandle = ControllerHandle;
|
return EFI_SUCCESS;
|
|
FreeEvent:
|
gBS->CloseEvent (Genet->ExitBootServicesEvent);
|
FreeDevice:
|
DEBUG ((DEBUG_WARN, "%a: Returning %r\n", __FUNCTION__, Status));
|
FreePool (Genet);
|
return Status;
|
}
|
|
|
/**
|
Stops a device controller or a bus controller.
|
|
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
|
instance.
|
@param[in] ControllerHandle A handle to the device being stopped. The handle
|
must support a bus specific I/O protocol for the
|
driver to use to stop the device.
|
@param[in] NumberOfChildren The number of child device handles in
|
ChildHandleBuffer.
|
@param[in] ChildHandleBuffer An array of child handles to be freed. May be
|
NULL if NumberOfChildren is 0.
|
|
@retval EFI_SUCCESS The device was stopped.
|
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device
|
error.
|
|
**/
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
GenetDriverBindingStop (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE ControllerHandle,
|
IN UINTN NumberOfChildren,
|
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
|
)
|
{
|
EFI_SIMPLE_NETWORK_PROTOCOL *SnpProtocol;
|
GENET_PRIVATE_DATA *Genet;
|
EFI_STATUS Status;
|
|
Status = gBS->HandleProtocol (ControllerHandle,
|
&gEfiSimpleNetworkProtocolGuid,
|
(VOID **)&SnpProtocol
|
);
|
ASSERT_EFI_ERROR (Status);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Genet = GENET_PRIVATE_DATA_FROM_SNP_THIS (SnpProtocol);
|
|
ASSERT (Genet->ControllerHandle == ControllerHandle);
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (ControllerHandle,
|
&gEfiSimpleNetworkProtocolGuid, &Genet->Snp,
|
&gEfiAdapterInformationProtocolGuid, &Genet->Aip,
|
NULL);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Status = gBS->CloseEvent (Genet->ExitBootServicesEvent);
|
ASSERT_EFI_ERROR (Status);
|
|
GenetDmaFree (Genet);
|
|
Status = gBS->CloseProtocol (ControllerHandle,
|
&gBcmGenetPlatformDeviceProtocolGuid,
|
This->DriverBindingHandle,
|
ControllerHandle);
|
ASSERT_EFI_ERROR (Status);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
FreePool (Genet);
|
|
return EFI_SUCCESS;
|
}
|
|
EFI_DRIVER_BINDING_PROTOCOL mGenetDriverBinding = {
|
GenetDriverBindingSupported,
|
GenetDriverBindingStart,
|
GenetDriverBindingStop,
|
GENET_VERSION,
|
NULL,
|
NULL
|
};
|
|
STATIC
|
VOID
|
EFIAPI
|
OnProtocolNotify (
|
IN EFI_EVENT Event,
|
IN VOID *Context
|
)
|
{
|
BCM_GENET_PLATFORM_DEVICE_PROTOCOL *GenetDevice;
|
EFI_STATUS Status;
|
EFI_HANDLE *HandleBuffer;
|
UINTN HandleCount;
|
UINTN Index;
|
UINT32 Mac0, Mac1;
|
|
while (TRUE) {
|
Status = gBS->LocateHandleBuffer (ByRegisterNotify, NULL,
|
mProtocolNotifyEventRegistration, &HandleCount,
|
&HandleBuffer);
|
if (EFI_ERROR (Status)) {
|
break;
|
}
|
|
for (Index = 0; Index < HandleCount; Index++) {
|
Status = gBS->HandleProtocol (HandleBuffer[Index],
|
&gBcmGenetPlatformDeviceProtocolGuid,
|
(VOID **)&GenetDevice);
|
ASSERT_EFI_ERROR (Status);
|
|
Mac0 = (GenetDevice->MacAddress.Addr[3]) |
|
(GenetDevice->MacAddress.Addr[2] << 8) |
|
(GenetDevice->MacAddress.Addr[1] << 16) |
|
(GenetDevice->MacAddress.Addr[0] << 24);
|
Mac1 = (GenetDevice->MacAddress.Addr[5]) |
|
(GenetDevice->MacAddress.Addr[4] << 8);
|
|
MmioOr32 (GenetDevice->BaseAddress + GENET_SYS_RBUF_FLUSH_CTRL,
|
GENET_SYS_RBUF_FLUSH_RESET);
|
gBS->Stall (10);
|
MmioAnd32 (GenetDevice->BaseAddress + GENET_SYS_RBUF_FLUSH_CTRL,
|
~GENET_SYS_RBUF_FLUSH_RESET);
|
|
MemoryFence ();
|
|
MmioWrite32 (GenetDevice->BaseAddress + GENET_UMAC_MAC0, Mac0);
|
MmioWrite32 (GenetDevice->BaseAddress + GENET_UMAC_MAC1, Mac1);
|
}
|
FreePool (HandleBuffer);
|
}
|
}
|
|
/**
|
The entry point of GENET UEFI Driver.
|
|
@param ImageHandle The image handle of the UEFI Driver.
|
@param SystemTable A pointer to the EFI System Table.
|
|
@retval EFI_SUCCESS The Driver or UEFI Driver exited normally.
|
@retval EFI_INCOMPATIBLE_VERSION _gUefiDriverRevision is greater than
|
SystemTable->Hdr.Revision.
|
|
**/
|
EFI_STATUS
|
EFIAPI
|
GenetEntryPoint (
|
IN EFI_HANDLE ImageHandle,
|
IN EFI_SYSTEM_TABLE *SystemTable
|
)
|
{
|
EFI_STATUS Status;
|
|
Status = EfiLibInstallDriverBindingComponentName2 (
|
ImageHandle,
|
SystemTable,
|
&mGenetDriverBinding,
|
ImageHandle,
|
&gGenetComponentName,
|
&gGenetComponentName2
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
//
|
// We need to program the MAC address into each existing controller,
|
// regardless of whether UEFI ever makes use of the interface, so that
|
// the OS driver will not have to care about this.
|
//
|
mProtocolNotifyEvent = EfiCreateProtocolNotifyEvent (
|
&gBcmGenetPlatformDeviceProtocolGuid,
|
TPL_CALLBACK, OnProtocolNotify, NULL,
|
&mProtocolNotifyEventRegistration);
|
ASSERT (mProtocolNotifyEvent != NULL);
|
|
DEBUG ((DEBUG_INIT | DEBUG_INFO, "Installed GENET UEFI driver!\n"));
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Unload function of GENET UEFI Driver.
|
|
@param ImageHandle The allocated handle for the EFI image
|
|
@retval EFI_SUCCESS The driver was unloaded successfully
|
@retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
|
|
**/
|
EFI_STATUS
|
EFIAPI
|
GenetUnload (
|
IN EFI_HANDLE ImageHandle
|
)
|
{
|
EFI_STATUS Status;
|
EFI_HANDLE *HandleBuffer;
|
UINTN HandleCount;
|
UINTN Index;
|
|
//
|
// Retrieve all BcmGenetPlatformDevice handles in the handle database
|
//
|
Status = gBS->LocateHandleBuffer (ByProtocol,
|
&gBcmGenetPlatformDeviceProtocolGuid,
|
NULL,
|
&HandleCount,
|
&HandleBuffer);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
//
|
// Disconnect the driver from the handles in the handle database
|
//
|
for (Index = 0; Index < HandleCount && !EFI_ERROR (Status); Index++) {
|
Status = gBS->DisconnectController (HandleBuffer[Index],
|
gImageHandle,
|
NULL);
|
}
|
|
//
|
// Free the handle array
|
//
|
gBS->FreePool (HandleBuffer);
|
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_WARN, "%a: failed to disconnect all controllers - %r\n",
|
__FUNCTION__, Status));
|
return Status;
|
}
|
|
//
|
// Uninstall protocols installed by the driver in its entrypoint
|
//
|
Status = EfiLibUninstallDriverBindingComponentName2 (
|
&mGenetDriverBinding,
|
&gGenetComponentName,
|
&gGenetComponentName2
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
return EFI_SUCCESS;
|
}
|