From patchwork Thu Sep 15 16:28:04 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 76331 Delivered-To: patch@linaro.org Received: by 10.140.106.72 with SMTP id d66csp43626qgf; Thu, 15 Sep 2016 09:28:47 -0700 (PDT) X-Received: by 10.55.106.2 with SMTP id f2mr10811411qkc.124.1473956927316; Thu, 15 Sep 2016 09:28:47 -0700 (PDT) Return-Path: Received: from lists.linaro.org (lists.linaro.org. [54.225.227.206]) by mx.google.com with ESMTP id 125si2918515qkj.22.2016.09.15.09.28.45; Thu, 15 Sep 2016 09:28:47 -0700 (PDT) Received-SPF: pass (google.com: domain of linaro-uefi-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) client-ip=54.225.227.206; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linaro-uefi-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) smtp.mailfrom=linaro-uefi-bounces@lists.linaro.org; dmarc=pass (p=NONE dis=NONE) header.from=linaro.org Received: by lists.linaro.org (Postfix, from userid 109) id D8B6860C46; Thu, 15 Sep 2016 16:28:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on ip-10-142-244-252 X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL autolearn=disabled version=3.4.0 Received: from [127.0.0.1] (localhost [127.0.0.1]) by lists.linaro.org (Postfix) with ESMTP id 0401F60A45; Thu, 15 Sep 2016 16:28:27 +0000 (UTC) X-Original-To: linaro-uefi@lists.linaro.org Delivered-To: linaro-uefi@lists.linaro.org Received: by lists.linaro.org (Postfix, from userid 109) id 6E39160C42; Thu, 15 Sep 2016 16:28:21 +0000 (UTC) Received: from mail-qk0-f177.google.com (mail-qk0-f177.google.com [209.85.220.177]) by lists.linaro.org (Postfix) with ESMTPS id 9317B60A15 for ; Thu, 15 Sep 2016 16:28:19 +0000 (UTC) Received: by mail-qk0-f177.google.com with SMTP id t7so60579275qkh.2 for ; Thu, 15 Sep 2016 09:28:19 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=grVII8v2Rl87pZM8gj2CUv20+YL5zFd1+tGuCzrXmic=; b=NDS//GBSfVYYfa1Ojhcl6Pr/MzhtL6B4UflSqGD8XgngFVB/xAMuH4YkXuNXroHC7G JCuOfWNlxCCnA6Y1rOCVC4qk5S9m8GnAxV9BvY2M8j5kYIcCvBAA6z6i8+JPjXwiQpF2 ZMa5lQOzfRWQNZp5k5GylFN1eKIlFVopPNN818JQ7/fkxOQQ2N0QkKslFe4ijr88NftY uJZjSUUtpy+rcm5md9Xr0NDHQOj4e/vWLQAJpHtX6femalaLaRF5pFGOv0NEJDvA9KMy ESsFudZDJTsEBXzN3Da7SZpLyl1PzjCSk6IGfCgs+WnieMWYeeJlGgPhheAZdLg5WU8H ir2g== X-Gm-Message-State: AE9vXwMJGFHBmQyaNJeQfnmX05BCa2AtUIbmdSayzEN47JMamK74dFKtdrkSHurxrJg0DlwC8zc= X-Received: by 10.194.103.3 with SMTP id fs3mr8823666wjb.115.1473956899016; Thu, 15 Sep 2016 09:28:19 -0700 (PDT) Received: from localhost.localdomain ([160.164.73.10]) by smtp.gmail.com with ESMTPSA id ab9sm4234994wjc.7.2016.09.15.09.28.14 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 15 Sep 2016 09:28:18 -0700 (PDT) From: Ard Biesheuvel To: linaro-uefi@lists.linaro.org, leif.lindholm@linaro.org, ricardo.salveti@linaro.org, leo.duran@amd.com Date: Thu, 15 Sep 2016 17:28:04 +0100 Message-Id: <1473956885-5468-2-git-send-email-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1473956885-5468-1-git-send-email-ard.biesheuvel@linaro.org> References: <1473956885-5468-1-git-send-email-ard.biesheuvel@linaro.org> Subject: [Linaro-uefi] [PATCH v2 1/2] Drivers/Xhci: implement firmware upload driver for Renesas PD72020x X-BeenThere: linaro-uefi@lists.linaro.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: linaro-uefi-bounces@lists.linaro.org Sender: "Linaro-uefi" This implements a PCI driver for the Renesas PD720201/PD720202 PCIe XHCI controller, which in some cases requires a device firmware to be uploaded by the system firmware in order to function. By registering a UEFI driver with high dispatch priority, this driver is able to access the device early, install the firmware, and then decline the driver dispatch so that the device is fully functional when the real XHCI driver gets to see it. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ard Biesheuvel --- Drivers/Xhci/RenesasFirmwarePD720202/RenesasFirmwarePD720202.c | 386 ++++++++++++++++++++ Drivers/Xhci/RenesasFirmwarePD720202/RenesasFirmwarePD720202.inf | 43 +++ OpenPlatformPkg.dec | 3 + 3 files changed, 432 insertions(+) diff --git a/Drivers/Xhci/RenesasFirmwarePD720202/RenesasFirmwarePD720202.c b/Drivers/Xhci/RenesasFirmwarePD720202/RenesasFirmwarePD720202.c new file mode 100644 index 000000000000..617fb1c7c858 --- /dev/null +++ b/Drivers/Xhci/RenesasFirmwarePD720202/RenesasFirmwarePD720202.c @@ -0,0 +1,386 @@ +/** @file + Implementation of driver entry point and driver binding protocol. + +Copyright (c) 2016, 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. + +**/ + +#include + +#include +#include +#include +#include + +#include + +#include + +#define PCI_VENDOR_ID_RENESAS 0x1912 +#define PCI_DEVICE_ID_PD720201 0x14 +#define PCI_DEVICE_ID_PD720202 0x15 + +#define PCI_FW_CTL_STAT_REG 0xF4 +#define PCI_FW_CTL_DATA_REG 0xF5 +#define PCI_EXT_ROM_CTL_REG 0xF6 +#define PCI_FW_DATA0 0xF8 +#define PCI_FW_DATA1 0xFC + +#define PCI_FW_CTL_STAT_REG_FW_DL_ENABLE (1U << 0) +#define PCI_FW_CTL_STAT_REG_RESULT_CODE (7U << 4) +#define PCI_FW_CTL_STAT_REG_RESULT_INVALID (0) +#define PCI_FW_CTL_STAT_REG_RESULT_OK (1U << 4) +#define PCI_FW_CTL_STAT_REG_SET_DATA0 (1U << 0) +#define PCI_FW_CTL_STAT_REG_SET_DATA1 (1U << 1) + +#define PCI_EXT_ROM_CTL_REG_ROM_EXISTS (1U << 15) + +STATIC CONST UINT32 *mFirmwareImage; +STATIC UINTN mFirmwareImageSize; + +STATIC +UINT8 +ReadCtlStatVal ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINTN Offset + ) +{ + UINT8 CtlStatVal; + EFI_STATUS Status; + + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, Offset, 1, &CtlStatVal); + ASSERT_EFI_ERROR (Status); + + return CtlStatVal; +} + +STATIC +VOID +WriteCtlStatVal ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINTN Offset, + IN UINT8 CtlStatVal + ) +{ + EFI_STATUS Status; + + Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, Offset, 1, &CtlStatVal); + ASSERT_EFI_ERROR (Status); +} + +STATIC +VOID +WriteDataVal ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINTN Offset, + IN UINT32 DataVal + ) +{ + EFI_STATUS Status; + + Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, Offset, 1, &DataVal); + ASSERT_EFI_ERROR (Status); +} + +STATIC +BOOLEAN +WaitReadCtlStatVal ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINTN Offset, + IN UINT8 Mask, + IN UINT8 Val + ) +{ + UINTN Timeout; + + for (Timeout = 0; (ReadCtlStatVal (PciIo, Offset) & Mask) != Val; Timeout++) { + if (Timeout > 1000) { + DEBUG ((EFI_D_ERROR, + "%a: Timeout waiting for reg [+0x%x] & 0x%x to become 0x%x\n", + __FUNCTION__, Offset, Mask, Val)); + return FALSE; + } + gBS->Stall (10); + } + return TRUE; +} + +STATIC +VOID +DownloadPD720202Firmware ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + UINTN Idx; + + Idx = 0; + + // 1. Set "FW Download Enable" to '1b'. + WriteCtlStatVal (PciIo, PCI_FW_CTL_STAT_REG, + PCI_FW_CTL_STAT_REG_FW_DL_ENABLE); + + // 2. Read "Set DATA0" and confirm it is '0b'. + if (!WaitReadCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG, + PCI_FW_CTL_STAT_REG_SET_DATA0, 0)) { + return; + } + + // 3. Write FW data to "DATA0". + WriteDataVal (PciIo, PCI_FW_DATA0, mFirmwareImage[Idx++]); + + // 4. Read "Set DATA1" and confirm it is '0b'. + if (!WaitReadCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG, + PCI_FW_CTL_STAT_REG_SET_DATA1, 0)) { + return; + } + + // 5. Write FW data to "DATA1". + WriteDataVal (PciIo, PCI_FW_DATA1, mFirmwareImage[Idx++]); + + // 6. Set "Set DATA0" & "Set DATA1" to '1b'. + WriteCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG, + PCI_FW_CTL_STAT_REG_SET_DATA0 | PCI_FW_CTL_STAT_REG_SET_DATA1); + + while (Idx < mFirmwareImageSize / sizeof(UINT32)) { + + // 7. Read "Set DATA0" and confirm it is '0b'. + if (!WaitReadCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG, + PCI_FW_CTL_STAT_REG_SET_DATA0, 0)) { + return; + } + + // 8. Write FW data to"DATA0". Set "Set DATA0" to '1b'. + WriteDataVal (PciIo, PCI_FW_DATA0, mFirmwareImage[Idx++]); + WriteCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG, PCI_FW_CTL_STAT_REG_SET_DATA0); + + // 9. Read "Set DATA1" and confirm it is '0b'. + if (!WaitReadCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG, + PCI_FW_CTL_STAT_REG_SET_DATA1, 0)) { + return; + } + + // 10. Write FW data to"DATA1". Set "Set DATA1" to '1b'. + WriteDataVal (PciIo, PCI_FW_DATA1, mFirmwareImage[Idx++]); + WriteCtlStatVal (PciIo, PCI_FW_CTL_DATA_REG, PCI_FW_CTL_STAT_REG_SET_DATA1); + + // 11. Return to step 7 and repeat the sequence from step 7 to step 10. + } + + // 12. After writing the last data of FW, the System Software must set "FW Download Enable" to '0b'. + WriteCtlStatVal (PciIo, PCI_FW_CTL_STAT_REG, 0); + + // 13. Read "Result Code" and confirm it is '001b'. + if (WaitReadCtlStatVal (PciIo, PCI_FW_CTL_STAT_REG, + PCI_FW_CTL_STAT_REG_RESULT_CODE, PCI_FW_CTL_STAT_REG_RESULT_OK)) { + DEBUG ((EFI_D_INFO, "%a: Renesas PD720202 firmware download successful\n", + __FUNCTION__)); + } else { + DEBUG ((EFI_D_ERROR, "%a: Renesas PD720202 firmware download FAILED\n", + __FUNCTION__)); + } +} + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +STATIC +EFI_STATUS +EFIAPI +RenesasPD720202DriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT32 PciID; + UINT16 RomCtlVal; + UINT8 CtlStatVal; + + // + // Check for the PCI IO Protocol + // + Status = gBS->OpenProtocol (Controller, &gEfiPciIoProtocolGuid, + (VOID **)&PciIo, This->DriverBindingHandle, Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, PCI_VENDOR_ID_OFFSET, + 1, &PciID); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, + "%a: Pci->Pci.Read() of vendor/device id failed (Status == %r)\n", + __FUNCTION__, Status)); + goto CloseProtocol; + } + + if ((PciID & 0xffff) != PCI_VENDOR_ID_RENESAS || + ((PciID >> 16) != PCI_DEVICE_ID_PD720201 && + (PciID >> 16) != PCI_DEVICE_ID_PD720202)) { + DEBUG ((EFI_D_INFO, "%a: ignoring unsupported PCI device 0x%04x:0x%04x\n", + __FUNCTION__, PciID & 0xffff, PciID >> 16)); + goto CloseProtocol; + } + + // + // We have found a device of the type we are looking for. Check if the + // firmware needs to be loaded. + // + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_EXT_ROM_CTL_REG, + 1, &RomCtlVal); + if (!EFI_ERROR (Status) && + (RomCtlVal & PCI_EXT_ROM_CTL_REG_ROM_EXISTS) != 0) { + + DEBUG ((EFI_D_INFO, "%a: ignoring device with external ROM\n", __FUNCTION__)); + goto CloseProtocol; + } + + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_FW_CTL_STAT_REG, + 1, &CtlStatVal); + if (!EFI_ERROR (Status) && + (CtlStatVal & PCI_FW_CTL_STAT_REG_RESULT_CODE) == PCI_FW_CTL_STAT_REG_RESULT_INVALID) { + // + // Firmware download required + // + DEBUG ((EFI_D_INFO, "%a: downloading firmware\n", __FUNCTION__)); + DownloadPD720202Firmware (PciIo); + } + +CloseProtocol: + gBS->CloseProtocol (Controller, &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, Controller); + + // + // Always return unsupported: we are not interested in driving the device, + // only in having the opportunity to install the firmware before the real + // driver attaches to it. + // + return EFI_UNSUPPORTED; +} + +/** + Start this driver on Controller. Not used. + + @param [in] This Protocol instance pointer. + @param [in] Controller Handle of device to work with. + @param [in] RemainingDevicePath Not used, always produce all possible children. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval other This driver does not support this device. + +**/ +STATIC +EFI_STATUS +EFIAPI +RenesasPD720202DriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + // + // We are not interested in driving the device, we only poke the firmware + // in the .Supported() callback. + // + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; +} + +/** + Stop this driver on Controller. Not used. + + @param [in] This Protocol instance pointer. + @param [in] Controller Handle of device to stop driver on. + @param [in] NumberOfChildren How many children need to be stopped. + @param [in] ChildHandleBuffer Not used. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + @retval other This driver was not removed from this device. + +**/ +STATIC +EFI_STATUS +EFIAPI +RenesasPD720202DriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + ASSERT (FALSE); + return EFI_SUCCESS; +} + +// +// UEFI Driver Model entry point +// +STATIC EFI_DRIVER_BINDING_PROTOCOL RenesasPD720202DriverBinding = { + RenesasPD720202DriverSupported, + RenesasPD720202DriverStart, + RenesasPD720202DriverStop, + + // Version values of 0xfffffff0-0xffffffff are reserved for platform/OEM + // specific drivers. Protocol instances with higher 'Version' properties + // will be used before lower 'Version' ones. XhciDxe uses version 0x30, + // so this driver will be called in preference, and XhciDxe will be invoked + // after RenesasPD720202DriverSupported returns EFI_UNSUPPORTED. + 0xfffffff0, + NULL, + NULL +}; + +EFI_STATUS +EFIAPI +InitializeRenesasPD720202Driver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // First, try to locate the firmware image. If it is missing, there is no + // point in proceeding. + // + Status = GetSectionFromAnyFv (&gRenesasFirmwarePD720202ImageId, + EFI_SECTION_RAW, 0, (VOID **) (VOID **)&mFirmwareImage, + &mFirmwareImageSize); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a: could not locate PD720202 firmware image\n", + __FUNCTION__)); + ASSERT_EFI_ERROR (Status); + return Status; + } + + return EfiLibInstallDriverBinding (ImageHandle, SystemTable, + &RenesasPD720202DriverBinding, NULL); +} diff --git a/Drivers/Xhci/RenesasFirmwarePD720202/RenesasFirmwarePD720202.inf b/Drivers/Xhci/RenesasFirmwarePD720202/RenesasFirmwarePD720202.inf new file mode 100644 index 000000000000..f999bf7b1e60 --- /dev/null +++ b/Drivers/Xhci/RenesasFirmwarePD720202/RenesasFirmwarePD720202.inf @@ -0,0 +1,43 @@ +## file +# Component description file for Reneses PD720202 firmware download driver +# +# Copyright (c) 2016, Linaro Ltd. 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = RenesasFirmwarePD720202 + FILE_GUID = 5979ebfe-d53c-4150-a972-568231583969 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeRenesasPD720202Driver + +[Sources] + RenesasFirmwarePD720202.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OpenPlatformPkg/OpenPlatformPkg.dec + +[LibraryClasses] + DebugLib + DxeServicesLib + UefiBootServicesTableLib + UefiLib + UefiDriverEntryPoint + +[Protocols] + gEfiPciIoProtocolGuid + +[Guids] + gRenesasFirmwarePD720202ImageId diff --git a/OpenPlatformPkg.dec b/OpenPlatformPkg.dec index af38488aa80b..2db143d637b3 100644 --- a/OpenPlatformPkg.dec +++ b/OpenPlatformPkg.dec @@ -31,6 +31,9 @@ ## Vendor GUID for RAM disk device path gRamDiskGuid = {0x6ED4DD0, 0xFF78, 0x11D3, {0xBD, 0xC4, 0x00, 0xA0, 0xC9, 0x40, 0x53, 0xD1}} + ## GUID for RenesasFirmwarePD720202 firmware image + gRenesasFirmwarePD720202ImageId = {0xA059EBC4, 0xD73D, 0x4279, {0x81,0xBF,0xE4,0xA8,0x93,0x08,0xB9,0x23}} + [LibraryClasses] [PcdsFixedAtBuild]