/** @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 SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #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; }