From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2a00:1450:4864:20::441; helo=mail-wr1-x441.google.com; envelope-from=leif.lindholm@linaro.org; receiver=edk2-devel@lists.01.org Received: from mail-wr1-x441.google.com (mail-wr1-x441.google.com [IPv6:2a00:1450:4864:20::441]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id EDD5121199552 for ; Thu, 13 Dec 2018 09:33:17 -0800 (PST) Received: by mail-wr1-x441.google.com with SMTP id z5so2853780wrt.11 for ; Thu, 13 Dec 2018 09:33:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=gJyDAxh8zFUnZ9+r/ldYp8Ku7odQk7f5XZsfLIdstbw=; b=d7IDDknHTkaLAdKU8OEQB1JCBZCg2l3Xg+bwj1zj7vAqg66ukuQAm7HZ2RLl50M3e7 fqxrA+uBQZt3g3WIaFHmMhals45EYkHOQNNejovla5uvXHGyxJ01TbTn5NtIoDUNbL8K DZTo5SOYGtu0dj0ymEx5myn9EvLUJH9+8oh/c= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=gJyDAxh8zFUnZ9+r/ldYp8Ku7odQk7f5XZsfLIdstbw=; b=j09kJV/IGjXWO7CCXMEljdpfkd0U6W3Bmj+tK9sK8JswGkgxYALEBqLkI/cLsrzKe/ aQUs/SAxzn12/ppjHNg4liow0EicrYKenHXQou5VZgySXLs87vs21V/QtEhPKIbcctxa GAcRwxiBOeAFl8qzWnLNGJRVspy3LBvDEjg7uZAKAQj3YuyT0JeX1HANVIVTgLcYtj52 u7Q/QjcwE9Xx05CtQvXYSxP1va23/0RIuwXIq/YhVSfRe4s1IUUr4dMjcqpEO762O/uK DLEEX/1roCwDFODxygSSj63YJwJKB9kHhNwuqi5MHmQNjCUgf6nzytB5dXWMXhqbbxlI H0Ng== X-Gm-Message-State: AA+aEWaWYofY1CZB5LjQtMVF2e6eES4hDAOxriHCCNU5JcwbHHhOEZHN rjOnmovYk3Eh9vRcwMPUHq+C+EqbfpU= X-Google-Smtp-Source: AFSGD/XP1SuMDvbri50Kbsf1IXLRrNQjxqu9xhan5affXD14yu/kCpeYqrFfj6CEIpU0pp78+1VjPQ== X-Received: by 2002:a5d:42ce:: with SMTP id t14mr3636796wrr.51.1544722396138; Thu, 13 Dec 2018 09:33:16 -0800 (PST) Received: from bivouac.eciton.net (bivouac.eciton.net. [2a00:1098:0:86:1000:23:0:2]) by smtp.gmail.com with ESMTPSA id d16sm3227892wru.52.2018.12.13.09.33.14 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 13 Dec 2018 09:33:15 -0800 (PST) Date: Thu, 13 Dec 2018 17:33:13 +0000 From: Leif Lindholm To: Chris Co Cc: "edk2-devel@lists.01.org" , Ard Biesheuvel , Michael D Kinney Message-ID: <20181213173313.7lbmo6vy2o5morrw@bivouac.eciton.net> References: <20180921082542.35768-1-christopher.co@microsoft.com> <20180921082542.35768-17-christopher.co@microsoft.com> MIME-Version: 1.0 In-Reply-To: <20180921082542.35768-17-christopher.co@microsoft.com> User-Agent: NeoMutt/20170113 (1.7.2) Subject: Re: [PATCH edk2-platforms 16/27] Silicon/NXP: Add i.MX6 Timer DXE driver X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 13 Dec 2018 17:33:18 -0000 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Fri, Sep 21, 2018 at 08:26:08AM +0000, Chris Co wrote: > This adds DXE support for EPIT timer on NXP i.MX6 SoCs. > > Contributed-under: TianoCore Contribution Agreement 1.1 > Signed-off-by: Christopher Co > Cc: Ard Biesheuvel > Cc: Leif Lindholm > Cc: Michael D Kinney > --- > Silicon/NXP/iMX6Pkg/Drivers/TimerDxe/Timer.c | 278 ++++++++++++++++++++ > Silicon/NXP/iMX6Pkg/Drivers/TimerDxe/TimerDxe.inf | 55 ++++ > 2 files changed, 333 insertions(+) > > diff --git a/Silicon/NXP/iMX6Pkg/Drivers/TimerDxe/Timer.c b/Silicon/NXP/iMX6Pkg/Drivers/TimerDxe/Timer.c > new file mode 100644 > index 000000000000..6b4db6185b48 > --- /dev/null > +++ b/Silicon/NXP/iMX6Pkg/Drivers/TimerDxe/Timer.c > @@ -0,0 +1,278 @@ > +/** @file > +* > +* Copyright (c) 2018 Microsoft Corporation. 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 > +#include > + > +#include > +#include > + > +#include > +#include > + > +// The notification function to call on every timer interrupt. > +volatile EFI_TIMER_NOTIFY mTimerNotifyFunction = (EFI_TIMER_NOTIFY) NULL; > +EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT) NULL; > + > +// Cached copy of the Hardware Interrupt protocol instance > +EFI_HARDWARE_INTERRUPT_PROTOCOL *gInterrupt = NULL; > + > +// Cached interrupt vector > +volatile UINTN mVector; > +UINT64 mCurrentTimerPeriod; > + > +EFI_STATUS > +EFIAPI > +TimerDriverRegisterHandler ( > + IN EFI_TIMER_ARCH_PROTOCOL *This, > + IN EFI_TIMER_NOTIFY NotifyFunction > + ) > +{ > + DEBUG ((DEBUG_VERBOSE, "++TimerDriverRegisterHandler()\n")); Please use %a and __FUNCTION__ rather than manually typing the function name. (Throughout the set.) > + if ((NotifyFunction == NULL) && (mTimerNotifyFunction == NULL)) { > + return EFI_INVALID_PARAMETER; > + } > + > + if ((NotifyFunction != NULL) && (mTimerNotifyFunction != NULL)) { > + return EFI_ALREADY_STARTED; > + } > + > + mTimerNotifyFunction = NotifyFunction; > + DEBUG ((DEBUG_VERBOSE, "--TimerDriverRegisterHandler()=ok\n")); > + return EFI_SUCCESS; > +} > + > +EFI_STATUS > +EFIAPI > +TimerDriverSetTimerPeriod ( > + IN EFI_TIMER_ARCH_PROTOCOL *This, > + IN UINT64 TimerPeriod > + ) > +{ > + PCSP_EPIT_REG pEpit; > + UINT16 EpitPreScalar; > + EFI_STATUS Status; > + UINT32 TimerCount; > + UINT32 Value; > + > + DEBUG ((DEBUG_VERBOSE, "++TimerDriverSetTimerPeriod(%d)\n", TimerPeriod)); > + > + pEpit = (PCSP_EPIT_REG) CSP_BASE_REG_PA_EPIT1; > + DEBUG ((DEBUG_VERBOSE, > + "TimerDriverSetTimerPeriod() disable timer. EPIT_REG adr=%p\n", pEpit)); > + > + // First stop the timer. > + Value = MmioRead32 ((UINTN)&pEpit->CR); > + Value &= ~(((1 << EPIT_CR_EN_WID) - 1) << EPIT_CR_EN_LSH); Just to clarify comment from last email: I did mean global search/replace on _LSH to _SHIFT for the entire set. > + Value |= (EPIT_CR_EN_DISABLE << EPIT_CR_EN_LSH); > + MmioWrite32 ((UINTN)&pEpit->CR, Value); > + > + if (TimerPeriod == 0) { > + Status = gInterrupt->DisableInterruptSource (gInterrupt, mVector); > + mCurrentTimerPeriod = 0; > + DEBUG ((DEBUG_VERBOSE, "--TimerDriverSetTimerPeriod() Timer Disabled\n")); > + return Status; > + } > + > + // Configure EPIT to be sourced from iMX6 24 MHz crystal oscialltor > + // Aim to have UEFI tick counting at 1 MHz clock or another frequency as set in pcd > + EpitPreScalar = 68; That 68 needs a #define. > + DEBUG ((DEBUG_VERBOSE, > + "TimerDriverSetTimerPeriod() using corrected EPIT prescalar=%d\n", > + EpitPreScalar)); > + > + MmioWrite32 ((UINTN)&pEpit->CR, > + (EPIT_CR_ENMOD_LOAD << EPIT_CR_ENMOD_LSH) | > + (EPIT_CR_OCIEN_ENABLE << EPIT_CR_OCIEN_LSH) | > + (EPIT_CR_RLD_RELOAD << EPIT_CR_RLD_LSH) | > + ((EpitPreScalar - 1) << EPIT_CR_PRESCALAR_LSH) | > + (EPIT_CR_SWR_NORESET << EPIT_CR_SWR_LSH) | > + (EPIT_CR_IOVW_OVR << EPIT_CR_IOVW_LSH) | > + (EPIT_CR_DBGEN_ACTIVE << EPIT_CR_DBGEN_LSH) | > + (EPIT_CR_WAITEN_ENABLE << EPIT_CR_WAITEN_LSH) | > + (EPIT_CR_DOZEN_ENABLE << EPIT_CR_DOZEN_LSH) | > + (EPIT_CR_STOPEN_ENABLE << EPIT_CR_STOPEN_LSH) | > + (EPIT_CR_OM_DICONNECT << EPIT_CR_OM_LSH) | > + (EPIT_CR_CLKSRC_IPGCLK << EPIT_CR_CLKSRC_LSH)); > + > + // Clear timer compare interrupt flag (write-1-clear) > + MmioWrite32 ((UINTN)&pEpit->SR, ((1 << EPIT_SR_OCIF_WID) - 1) << EPIT_SR_OCIF_LSH); > + TimerCount = (UINT32) (TimerPeriod / 10); > + if ((UINT64)TimerCount > (UINT64)0xffffffff) { > + TimerCount = 0xffffffff; MAX_UINT32? / Leif > + } > + > + mCurrentTimerPeriod = TimerPeriod; > + MmioWrite32 ((UINTN)&pEpit->CMPR, TimerCount); > + MmioWrite32 ((UINTN)&pEpit->LR, TimerCount); > + Status = gInterrupt->EnableInterruptSource (gInterrupt, mVector); > + > + // Turn the timer on > + Value = MmioRead32 ((UINTN)&pEpit->CR); > + Value &= ~(((1 << EPIT_CR_EN_WID) - 1) << EPIT_CR_EN_LSH); > + Value |= EPIT_CR_EN_ENABLE << EPIT_CR_EN_LSH; > + MmioWrite32 ((UINTN)&pEpit->CR, Value); > + > + DEBUG ((DEBUG_VERBOSE, "--TimerDriverSetTimerPeriod(%d)=%Xh\n", TimerPeriod, > + Status)); > + return Status; > +} > + > +EFI_STATUS > +EFIAPI > +TimerDriverGetTimerPeriod ( > + IN EFI_TIMER_ARCH_PROTOCOL *This, > + OUT UINT64 *TimerPeriod > + ) > +{ > + *TimerPeriod = mCurrentTimerPeriod; > + DEBUG ((DEBUG_VERBOSE, "+-TimerDriverGetTimerPeriod(%d)=ok\n", > + mCurrentTimerPeriod)); > + return EFI_SUCCESS; > +} > + > +VOID > +EFIAPI > +TimerInterruptHandler ( > + IN HARDWARE_INTERRUPT_SOURCE Source, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ) > +{ > + EFI_TPL OriginalTPL; > + PCSP_EPIT_REG pEpit; > + > + pEpit = (PCSP_EPIT_REG) CSP_BASE_REG_PA_EPIT1; > + > + // DXE core uses this callback for the EFI timer tick. The DXE core uses locks > + // that raise to TPL_HIGH and then restore back to current level. Thus we need > + // to make sure TPL level is set to TPL_HIGH while we are handling the timer tick. > + OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); > + > + // Check if the timer interrupt is active > + if (MmioRead32 ((UINTN)&pEpit->SR) != 0) { > + // Acknowledge the EPIT interrupt > + MmioWrite32 ((UINTN)&pEpit->SR, 0x1); > + > + // Signal EOI to avoid losing subsequent ticks from long duration handlers > + gInterrupt->EndOfInterrupt (gInterrupt, Source); > + > + if (mTimerNotifyFunction) { > + mTimerNotifyFunction (mCurrentTimerPeriod); > + } > + } > + > + gBS->RestoreTPL (OriginalTPL); > +} > + > +EFI_STATUS > +EFIAPI > +TimerDriverGenerateSoftInterrupt ( > + IN EFI_TIMER_ARCH_PROTOCOL *This > + ) > +{ > + return EFI_UNSUPPORTED; > +} > + > +EFI_TIMER_ARCH_PROTOCOL gTimer = { > + TimerDriverRegisterHandler, > + TimerDriverSetTimerPeriod, > + TimerDriverGetTimerPeriod, > + TimerDriverGenerateSoftInterrupt > +}; > + > +VOID > +EFIAPI > +ExitBootServicesEvent ( > + IN EFI_EVENT Event, > + IN VOID *Context > + ) > +{ > + EFI_STATUS Status; > + > + DEBUG ((DEBUG_INFO, "Disabling EPIT timer on ExitBootServicesEvent")); > + > + // Disable the timer > + Status = TimerDriverSetTimerPeriod (&gTimer, 0); > + ASSERT_EFI_ERROR (Status); > +} > + > +EFI_STATUS > +EFIAPI > +TimerInitialize ( > + IN EFI_HANDLE ImageHandle, > + IN EFI_SYSTEM_TABLE *SystemTable > +) > +{ > + EFI_HANDLE Handle; > + EFI_STATUS Status; > + DEBUG ((DEBUG_VERBOSE, "++TimerInitialize()\n")); > + > + Handle = NULL; > + mVector = IRQ_EPIT1; > + > + // Find the interrupt controller protocol. > + Status = gBS->LocateProtocol ( > + &gHardwareInterruptProtocolGuid, > + NULL, > + (VOID **) &gInterrupt); > + ASSERT_EFI_ERROR (Status); > + > + // Disable the timer > + Status = TimerDriverSetTimerPeriod (&gTimer, 0); > + ASSERT_EFI_ERROR (Status); > + > + // Install interrupt handler > + Status = gInterrupt->RegisterInterruptSource (gInterrupt, mVector, > + TimerInterruptHandler); > + ASSERT_EFI_ERROR (Status); > + > + // Set up default timer > + Status = TimerDriverSetTimerPeriod (&gTimer, FixedPcdGet32 (PcdTimerPeriod)); > + ASSERT_EFI_ERROR (Status); > + > + DEBUG (( > + DEBUG_VERBOSE, > + "EPIT Timer initialized to default period %d x 100ns ~ %dms\n", > + FixedPcdGet32 (PcdTimerPeriod), > + FixedPcdGet32 (PcdTimerPeriod) / 10000)); > + > + // Install the Timer Architectural Protocol onto a new handle > + Status = gBS->InstallMultipleProtocolInterfaces ( > + &Handle, > + &gEfiTimerArchProtocolGuid, > + &gTimer, > + NULL); > + > + ASSERT_EFI_ERROR (Status); > + > + // Register for ExitBootServicesEvent > + Status = gBS->CreateEvent ( > + EVT_SIGNAL_EXIT_BOOT_SERVICES, > + TPL_NOTIFY, > + ExitBootServicesEvent, > + NULL, > + &EfiExitBootServicesEvent); > + > + ASSERT_EFI_ERROR (Status); > + > + DEBUG ((DEBUG_VERBOSE, "--TimerInitialize()\n")); > + return Status; > +} > diff --git a/Silicon/NXP/iMX6Pkg/Drivers/TimerDxe/TimerDxe.inf b/Silicon/NXP/iMX6Pkg/Drivers/TimerDxe/TimerDxe.inf > new file mode 100644 > index 000000000000..adcc158e29ab > --- /dev/null > +++ b/Silicon/NXP/iMX6Pkg/Drivers/TimerDxe/TimerDxe.inf > @@ -0,0 +1,55 @@ > +#/** @file > +# > +# Copyright (c) 2009, Apple Inc. All rights reserved.
> +# Copyright (c) 2018 Microsoft Corporation. All rights reserved. > +# > +# 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 = 0x0001001A > + BASE_NAME = iMX6TimerDxe > + FILE_GUID = 7CAF576F-F1D9-4104-A922-CB64537FD7AE > + MODULE_TYPE = DXE_DRIVER > + VERSION_STRING = 1.0 > + ENTRY_POINT = TimerInitialize > + > +[Sources.common] > + Timer.c > + > +[Packages] > + ArmPkg/ArmPkg.dec > + EmbeddedPkg/EmbeddedPkg.dec > + MdePkg/MdePkg.dec > + Silicon/NXP/iMX6Pkg/iMX6Pkg.dec > + > +[LibraryClasses] > + BaseLib > + BaseMemoryLib > + DebugLib > + IoLib > + PerformanceLib > + UefiBootServicesTableLib > + UefiDriverEntryPoint > + UefiLib > + UefiRuntimeServicesTableLib > + > +[Guids] > + > +[Protocols] > + gEfiTimerArchProtocolGuid > + gHardwareInterruptProtocolGuid > + > +[Pcd.common] > + gEmbeddedTokenSpaceGuid.PcdEmbeddedFdPerformanceCounterPeriodInNanoseconds > + gEmbeddedTokenSpaceGuid.PcdTimerPeriod > + > +[Depex] > + gHardwareInterruptProtocolGuid > -- > 2.16.2.gvfs.1.33.gf5370f1 >