From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=104.47.42.119; helo=nam03-by2-obe.outbound.protection.outlook.com; envelope-from=christopher.co@microsoft.com; receiver=edk2-devel@lists.01.org Received: from NAM03-BY2-obe.outbound.protection.outlook.com (mail-by2nam03on0119.outbound.protection.outlook.com [104.47.42.119]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 8AB982098EAD0 for ; Thu, 19 Jul 2018 23:34:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=5RfAnOiBmTRlBHBY0lgBBVbWSwZBpKyIXGa2Xy1AkXI=; b=D3TG1kr+kKXPwc/0eJBBR7j4Tn1zCOpKY4yHHZWgCztoC5Rzr0RSqJBwamK5nNvOruEX3NMMfs7g/u4OMp29iqD025HZLSr8zrVmBtEupzpsZmmmwPzpZtknqHOuCqhKfNwswxbl+5IcXYKwR+Iqt6AMyRIlTazgINgPYFw+YLo= Received: from SN6PR2101MB1136.namprd21.prod.outlook.com (52.132.114.25) by SN6PR2101MB1133.namprd21.prod.outlook.com (52.132.114.22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.995.0; Fri, 20 Jul 2018 06:33:56 +0000 Received: from SN6PR2101MB1136.namprd21.prod.outlook.com ([fe80::78f8:214:33a:3c4]) by SN6PR2101MB1136.namprd21.prod.outlook.com ([fe80::78f8:214:33a:3c4%5]) with mapi id 15.20.0995.008; Fri, 20 Jul 2018 06:33:56 +0000 From: Chris Co To: "edk2-devel@lists.01.org" CC: Ard Biesheuvel , Leif Lindholm , Michael D Kinney Thread-Topic: [PATCH edk2-platforms 08/13] Silicon/NXP: Add i.MX6 Clock Library Thread-Index: AQHUH/OhIjHygmC/n0mr/lp4SCrbBA== Date: Fri, 20 Jul 2018 06:33:56 +0000 Message-ID: <20180720063328.26856-9-christopher.co@microsoft.com> References: <20180720063328.26856-1-christopher.co@microsoft.com> In-Reply-To: <20180720063328.26856-1-christopher.co@microsoft.com> Accept-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-clientproxiedby: CY4PR18CA0029.namprd18.prod.outlook.com (2603:10b6:903:9a::15) To SN6PR2101MB1136.namprd21.prod.outlook.com (2603:10b6:805:4::25) x-ms-exchange-messagesentrepresentingtype: 1 x-originating-ip: [2001:4898:80e8:a:75ac:13c7:7dde:5215] x-ms-publictraffictype: Email x-microsoft-exchange-diagnostics: 1; SN6PR2101MB1133; 6:HSKIg61mcsdPnrZg0HTMBNjHQZ0wiin7oyAPM0PFbh11xU/d73qmd1WTqA3VBaAid+8KSiXo7KBVpHn7FqQdbkc2MMUqpjWr3KGk5lKCu2s/C1cozokJFw2mp7cVqFwbYz0y81SvQqURGLp7wliLwUaRJY+yiZi87RUeHAmM8SzxWYDbr5Y/CWGV3yxl6sJwb0hx/zQhPTW9oPh7F5TWHtxAWUUMpCVe+d4g1Mw5SJ3gtxecE1RojULMf3KCLL+1JDyCtHsVPlyjLIc4UwoqY4bniby7+3S5FU0SgwzsNrktuRWd5pgiAVx0XDAuTptZZOwxSXQ+zFA8S86NfL/fVD/kDsUEtKQ4T7H9PNPnhiF2JZAWTCeTjC0ZCpTMAL5ovFCHuNv+55lCGRu5HQekV3GcfMD7sQdQgTRrh2hbV/r0MzVDzE2ZA4b/RSe+nbq17iWPXGJkdlTN1dhqmzQibg==; 5:wN3LEEvgPMXLEpFzFIG7D2N7DZTFBNtwK06x/S17ozCVlnp9Q7xGoZPkT1XOr8srFO0940WUJrJjdxgcpL+tyOWS5ql/mS/5/LeWNiQu3F7vNuMGtXV6S5DswcG0XbHLdGxDfClxg/T6yzfge64b+XZ+TO3qnt0UtvjbuMVzaYw=; 7:A5FNMxbECBO595g7JhfEnv6+PQVlAK+qQUbdSGHMwz1MSMN/C95/50qGQNNdaVel4ajqYRW61/ALUiw+v5XmbNAjZCdZgLx1TnqjQTvQkhpa1qYmqRFHDqu0yIh04fIkiEIGwqeqt6WbmubThcIkXz7ayDHsBMmZkxOkp15WKI7P9vMar53cgazs2eyAHAyakGqSpCDYPQrO++1pO8R5B9OH4LvsPcnMmWHkVW7lCoXn0UDzNUnF7jlM1DFs+yeQ x-ms-office365-filtering-correlation-id: ac4f50e6-5d93-45a9-1753-08d5ee0ac3fc x-ms-office365-filtering-ht: Tenant x-microsoft-antispam: BCL:0; PCL:0; RULEID:(7020095)(4652040)(8989117)(5600067)(711020)(4618075)(4534165)(4627221)(201703031133081)(201702281549075)(8990107)(2017052603328)(7193020); SRVR:SN6PR2101MB1133; x-ms-traffictypediagnostic: SN6PR2101MB1133: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Christopher.Co@microsoft.com; x-microsoft-antispam-prvs: x-exchange-antispam-report-test: UriScan:(28532068793085)(89211679590171)(228905959029699); x-ms-exchange-senderadcheck: 1 x-exchange-antispam-report-cfa-test: BCL:0; PCL:0; RULEID:(8211001083)(6040522)(2401047)(8121501046)(5005006)(10201501046)(3002001)(3231311)(944501410)(52105095)(2018427008)(93006095)(93001095)(6055026)(149027)(150027)(6041310)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123558120)(20161123562045)(20161123560045)(20161123564045)(6072148)(201708071742011)(7699016); SRVR:SN6PR2101MB1133; BCL:0; PCL:0; RULEID:; SRVR:SN6PR2101MB1133; x-forefront-prvs: 073966E86B x-forefront-antispam-report: SFV:NSPM; SFS:(10019020)(396003)(376002)(366004)(346002)(136003)(39860400002)(189003)(199004)(16200700003)(52116002)(16799955002)(53946003)(46003)(2906002)(2900100001)(15188155005)(36756003)(97736004)(386003)(256004)(99286004)(19627235002)(86612001)(575784001)(6506007)(14444005)(86362001)(186003)(76176011)(10090500001)(102836004)(1076002)(6306002)(6512007)(8936002)(6486002)(2616005)(476003)(5660300001)(486006)(478600001)(8676002)(6116002)(81156014)(81166006)(25786009)(4326008)(5640700003)(53376002)(53936002)(68736007)(6436002)(2501003)(10290500003)(22452003)(2351001)(7736002)(305945005)(5250100002)(6916009)(54906003)(14454004)(11346002)(72206003)(106356001)(966005)(316002)(446003)(105586002)(569006); DIR:OUT; SFP:1102; SCL:1; SRVR:SN6PR2101MB1133; H:SN6PR2101MB1136.namprd21.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; MX:1; A:1; received-spf: None (protection.outlook.com: microsoft.com does not designate permitted sender hosts) x-microsoft-antispam-message-info: Bnv3/3xpUqATdPy7hBvxPHGfL3aMx8Lpl8fSZt3jfbaQUxY186rAnF/H5TU0ZCMJTyleOQwaiShuGugkcWZYltK65FVMnk9paQ9jtVksQU8iM+2AldEPeNeOHvl4EVBcY+YPiKe/RSh+A2HPW3OYi/ZAYGDham9K3ZcCtw2jmBfo1bB3YEO6lLXfmBHr9LpPeCqFrjwjiIKOKfTBO7rlQpF/V3ARKm/A1/uS3o4b5/VSzy180RDdXZbZZEwvu8/kerwYvxW1A+v7yUNip91hSpDngKfl5MGLW36+p6TAFfWppQkkkVzjuXWTEj8rHxyuU2PDFQdNfddBgiyrIYCuNuC6bysZefW4vvlpBQcB3zc= spamdiagnosticoutput: 1:99 spamdiagnosticmetadata: NSPM MIME-Version: 1.0 X-OriginatorOrg: microsoft.com X-MS-Exchange-CrossTenant-Network-Message-Id: ac4f50e6-5d93-45a9-1753-08d5ee0ac3fc X-MS-Exchange-CrossTenant-originalarrivaltime: 20 Jul 2018 06:33:56.2642 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 72f988bf-86f1-41af-91ab-2d7cd011db47 X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN6PR2101MB1133 Subject: [PATCH edk2-platforms 08/13] Silicon/NXP: Add i.MX6 Clock Library X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.27 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 20 Jul 2018 06:34:00 -0000 Content-Language: en-US Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable This adds support for managing clocks on NXP i.MX6 SoC. It will manipulate the Clock Gating registers (CCGR). 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/Library/iMX6ClkPwrLib/iMX6ClkPwr.c | 501 ++++= ++++ Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6ClkPwrLib.inf | 46 + Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6ClkPwr_private.h | 203 ++++ Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6DQClkPwr.inc | 1278 ++++= ++++++++++++++++ Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6SDLClkPwr.inc | 1231 ++++= +++++++++++++++ Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6SXClkPwr.inc | 665 ++++= ++++++ 6 files changed, 3924 insertions(+) diff --git a/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6ClkPwr.c b/Silic= on/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6ClkPwr.c new file mode 100644 index 000000000000..91811ae44cdc --- /dev/null +++ b/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6ClkPwr.c @@ -0,0 +1,501 @@ +/** @file +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* This program and the accompanying materials +* are licensed and made available under the terms and conditions of the B= SD 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 IM= PLIED. +* +**/ + +#include +#include +#include +#include +#include + +#include +#include +#include "iMX6ClkPwr_private.h" + +// Let GCC know it's OK to compare enum values +#if defined (__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wenum-compare" +#endif + +C_ASSERT(IMX_CLOCK_GATE_STATE_OFF =3D=3D IMX_CCM_CCGR_OFF); +C_ASSERT(IMX_CLOCK_GATE_STATE_ON_RUN =3D=3D IMX_CCM_CCGR_ON_RUN); +C_ASSERT(IMX_CLOCK_GATE_STATE_ON =3D=3D IMX_CCM_CCGR_ON); + +#if defined (__GNUC__) +#pragma GCC diagnostic pop // -Wenum-compare +#endif + +/** + Caches clock values, since clocks towards the root of the tree are + requested frequently. +**/ +static IMX_CLOCK_TREE_CACHE ImxpClockPwrCache; + +#if defined(CPU_IMX6DQ) +#include "iMX6DQClkPwr.inc" +#elif defined(CPU_IMX6SX) +#include "iMX6SXClkPwr.inc" +#elif defined(CPU_IMX6SDL) +#include "iMX6SDLClkPwr.inc" +#else +#error CPU Preprocessor Flag Not Defined +#endif + +// +// Common private functions +// + +/** + Reset (invalidate) the clock tree cache. The clock tree cache must be + invalidated whenever the clock tree is modified, e.g. when changing + PLL configuration, clock mux, or divider. +**/ +VOID ImxpClkPwrCacheReset () +{ + SetMem (&ImxpClockPwrCache.Valid, sizeof(ImxpClockPwrCache.Valid), 0); +} + +/** + Configure clock gating for the specified clock signal. +**/ +VOID ImxClkPwrSetClockGate (IMX_CLK_GATE ClockGate, IMX_CLOCK_GATE_STATE S= tate) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + // Extract register index + const IMX_CCGR_INDEX index =3D ImxpCcgrIndexFromClkGate(ClockGate); + const UINTN startBit =3D index.GateNumber * 2; + const UINTN endBit =3D startBit + 1; + + MmioBitFieldWrite32 ( + (UINTN) &ccmRegisters->CCGR[index.RegisterIndex], + startBit, + endBit, + State); +} + +/** + Determine if gating TZASC1_IPG_MASTER_CLK should be skipped. + **/ +BOOLEAN ImxClkPwrShouldSkipTZASC1 () +{ + BOOLEAN Skip =3D FALSE; + +#if defined(CPU_IMX6DQ) + IMX_IOMUXC_GPR_REGISTERS *IoMuxMmioBasePtr =3D + (IMX_IOMUXC_GPR_REGISTERS *)IOMUXC_GPR_BASE_ADDRESS; + + UINTN IomuxGPR9 =3D MmioRead32 ((UINTN) &IoMuxMmioBasePtr->GPR9); + if (IomuxGPR9 & IMX_IOMUXC_TZASC1_BYP) { + // TZASC-1 is active. + Skip =3D TRUE; + } +#endif + + return Skip; +} + +/** + Determine if a clock gate should be skipped + **/ +BOOLEAN ImxClkPwrShouldSkipGate (IMX_CLK_GATE ClockGate) +{ + switch(ClockGate) { + case IMX_IPSYNC_IP2APB_TZASC1_IPG_MASTER_CLK_ENABLE: + return ImxClkPwrShouldSkipTZASC1 (); + + default: + return FALSE; + } +} + +/** + Set multiple clock gates to a given state efficiently. +**/ +VOID ImxClkPwrSetClockGates ( + const IMX_CLK_GATE *ClockGateList, + UINTN ClockGateCount, + IMX_CLOCK_GATE_STATE State + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + UINTN i; + + // Read all CCGR registers to local copy + UINT32 ccgrRegisters[ARRAYSIZE (ccmRegisters->CCGR)]; + for (i =3D 0; i < ARRAYSIZE (ccgrRegisters); ++i) { + ccgrRegisters[i] =3D MmioRead32 ((UINTN) &ccmRegisters->CCGR[i]); + } + + // Compute new CCGR register values + for (i =3D 0; i < ClockGateCount; ++i) { + if (ImxClkPwrShouldSkipGate(ClockGateList[i])) { + continue; + } + + IMX_CCGR_INDEX index =3D ImxpCcgrIndexFromClkGate (ClockGateList[i]); + ccgrRegisters[index.RegisterIndex] =3D + (ccgrRegisters[index.RegisterIndex] & ~(0x3 << (2 * index.GateNumber= ))) | + (State << (2 * index.GateNumber)); + } + + // Write back to registers + for (i =3D 0; i < ARRAYSIZE (ccgrRegisters); ++i) { + MmioWrite32 ((UINTN) &ccmRegisters->CCGR[i], ccgrRegisters[i]); + } +} + +/** + Get the current clock gating setting for the specified clock gate. +**/ +IMX_CLOCK_GATE_STATE ImxClkPwrGetClockGate (IMX_CLK_GATE ClockGate) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + const IMX_CCGR_INDEX index =3D ImxpCcgrIndexFromClkGate(ClockGate); + const UINTN startBit =3D index.GateNumber * 2; + const UINTN endBit =3D startBit + 1; + + UINT32 value =3D MmioBitFieldRead32 ( + (UINTN) &ccmRegisters->CCGR[index.RegisterIndex], + startBit, + endBit); + + if ((value !=3D IMX_CCM_CCGR_OFF) && (value !=3D IMX_CCM_CCGR_ON_RUN) && + (value !=3D IMX_CCM_CCGR_ON)) { + ASSERT (FALSE); + } + + return (IMX_CLOCK_GATE_STATE) value; +} + +EFI_STATUS +ImxpGetPll3MainClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_ANALOG_REGISTERS *ccmAnalogRegisters =3D + (IMX_CCM_ANALOG_REGISTERS *) IMX_CCM_ANALOG_BASE; + + IMX_CCM_ANALOG_PLL_USB1_REG pllUsb1Reg; pllUsb1Reg.AsUint32 =3D + MmioRead32((UINTN)&ccmAnalogRegisters->PLL_USB1); + + const IMX_CLK parent =3D ImxpClkFromBypassClkSource (pllUsb1Reg.BYPASS_C= LK_SRC); + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + if (pllUsb1Reg.DIV_SELECT =3D=3D 0) { + ClockInfo->Frequency =3D parentInfo.Frequency * 20; + } else { + ClockInfo->Frequency =3D parentInfo.Frequency * 22; + } + + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetPll3PfdClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + IMX_PLL_PFD PfdIndex, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_ANALOG_REGISTERS *ccmAnalogRegisters =3D + (IMX_CCM_ANALOG_REGISTERS *) IMX_CCM_ANALOG_BASE; + + IMX_CCM_PFD_480_REG pfd480Reg;pfd480Reg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmAnalogRegisters->PFD_480); + + UINT32 pfdFrac; + switch (PfdIndex) { + case IMX_PLL_PFD0: + pfdFrac =3D pfd480Reg.PFD0_FRAC; + break; + case IMX_PLL_PFD1: + pfdFrac =3D pfd480Reg.PFD1_FRAC; + break; + case IMX_PLL_PFD2: + pfdFrac =3D pfd480Reg.PFD2_FRAC; + break; + case IMX_PLL_PFD3: + pfdFrac =3D pfd480Reg.PFD3_FRAC; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, IMX_PLL3_MAIN_CLK, &paren= tInfo); + if (EFI_ERROR (status)) { + return status; + } + + // The resulting frequency shall be 480*18/PFDn_FRAC + // where PFD0_FRAC is in the range 12-35. + ASSERT ((pfdFrac >=3D 12) && (pfdFrac <=3D 35)); + ClockInfo->Frequency =3D (UINT32) ((UINT64) parentInfo.Frequency * 18 / = pfdFrac); + ClockInfo->Parent =3D IMX_PLL3_MAIN_CLK; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetPll3SwClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CCSR_REG ccsrReg; ccsrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CCSR); + + IMX_CLK parent; + if (ccsrReg.pll3_sw_clk_sel =3D=3D IMX_CCM_PLL3_SW_CLK_SEL_PLL3_MAIN_CLK= ) { + parent =3D IMX_PLL3_MAIN_CLK; + } else { + ASSERT (ccsrReg.pll3_sw_clk_sel =3D=3D IMX_CCM_PLL3_SW_CLK_SEL_PLL3_BY= PASS_CLK); + + ASSERT (!"Not implemented"); + return EFI_UNSUPPORTED; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency; + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + + +EFI_STATUS +ImxpGetPll1MainClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_ANALOG_REGISTERS *ccmAnalogRegisters =3D + (IMX_CCM_ANALOG_REGISTERS *) IMX_CCM_ANALOG_BASE; + + IMX_CCM_ANALOG_PLL_ARM_REG pllArmReg;pllArmReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmAnalogRegisters->PLL_ARM); + + const IMX_CLK parent =3D ImxpClkFromBypassClkSource (pllArmReg.BYPASS_CL= K_SRC); + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + if (pllArmReg.BYPASS !=3D 0) { + ClockInfo->Frequency =3D parentInfo.Frequency; + ClockInfo->Parent =3D parent; + return EFI_SUCCESS; + } + + ClockInfo->Frequency =3D + (UINT32) ((UINT64) parentInfo.Frequency * pllArmReg.DIV_SELECT / 2); + + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetPll2MainClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_ANALOG_REGISTERS *ccmAnalogRegisters =3D + (IMX_CCM_ANALOG_REGISTERS *) IMX_CCM_ANALOG_BASE; + + IMX_CCM_ANALOG_PLL_SYS_REG pllSysReg; pllSysReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmAnalogRegisters->PLL_SYS); + + // Determine the reference clock source + const IMX_CLK parent =3D ImxpClkFromBypassClkSource (pllSysReg.BYPASS_CL= K_SRC); + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + if (pllSysReg.BYPASS !=3D 0) { + ClockInfo->Frequency =3D parentInfo.Frequency; + ClockInfo->Parent =3D parent; + return EFI_SUCCESS; + } + + if (pllSysReg.DIV_SELECT =3D=3D 0) { + ClockInfo->Frequency =3D parentInfo.Frequency * 20; + } else { + ASSERT (pllSysReg.DIV_SELECT =3D=3D 1); + ClockInfo->Frequency =3D parentInfo.Frequency * 22; + } + + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetArmClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CLOCK_INFO pll1Info; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, IMX_PLL1_MAIN_CLK, &pll1I= nfo); + if (EFI_ERROR(status)) { + return status; + } + + IMX_CCM_CACRR_REG cacrrReg;cacrrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CACRR); + + ClockInfo->Frequency =3D pll1Info.Frequency / (1 + cacrrReg.arm_podf); + ClockInfo->Parent =3D IMX_PLL1_MAIN_CLK; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetPrePeriphClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + IMX_CLK parent; + switch (cbcmrReg.pre_periph_clk_sel) { + case IMX_CCM_PRE_PERIPH_CLK_SEL_PLL2: + parent =3D IMX_PLL2_MAIN_CLK; + break; + case IMX_CCM_PRE_PERIPH_CLK_SEL_PLL2_PFD2: + parent =3D IMX_PLL2_PFD2; + break; + case IMX_CCM_PRE_PERIPH_CLK_SEL_PLL2_PFD0: + parent =3D IMX_PLL2_PFD0; + break; + case IMX_CCM_PRE_PERIPH_CLK_SEL_PLL2_PFD2_DIV2: + parent =3D IMX_PLL2_PFD2; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + if (cbcmrReg.pre_periph_clk_sel =3D=3D IMX_CCM_PRE_PERIPH_CLK_SEL_PLL2_P= FD2_DIV2) { + ClockInfo->Frequency =3D parentInfo.Frequency / 2; + } else { + ClockInfo->Frequency =3D parentInfo.Frequency; + } + + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +VOID ImxpGetOsc24ClkInfo (OUT IMX_CLOCK_INFO *ClockInfo) +{ + ClockInfo->Frequency =3D IMX_REF_CLK_24M_FREQ; + ClockInfo->Parent =3D IMX_CLK_NONE; +} + +EFI_STATUS +ImxpGetAhbClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_= CCM_BASE; + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, IMX_PERIPH_CLK, &parentIn= fo); + if (EFI_ERROR (status)) { + return status; + } + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cbcdrReg.ahb_podf); + ClockInfo->Parent =3D IMX_PERIPH_CLK; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetIpgClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, IMX_AHB_CLK_ROOT, &parent= Info); + if (EFI_ERROR (status)) { + return status; + } + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cbcdrReg.ipg_podf); + ClockInfo->Parent =3D IMX_AHB_CLK_ROOT; + + return EFI_SUCCESS; +} + +// +// Public functions +// + +EFI_STATUS +ImxClkPwrGetClockInfo ( + IMX_CLK ClockId, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + return ImxpGetClockInfo (&ImxpClockPwrCache, ClockId, ClockInfo); +} diff --git a/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6ClkPwrLib.inf b/= Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6ClkPwrLib.inf new file mode 100644 index 000000000000..39ae4dfc2e38 --- /dev/null +++ b/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6ClkPwrLib.inf @@ -0,0 +1,46 @@ +## @file +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the B= SD 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 IM= PLIED. +# +## + +[Defines] + INF_VERSION =3D 0x00010005 + BASE_NAME =3D iMX6ClkPwrLib + FILE_GUID =3D 8DB4B460-9201-435A-B86A-24B58CED9A9E + MODULE_TYPE =3D BASE + VERSION_STRING =3D 1.0 + LIBRARY_CLASS =3D iMX6ClkPwrLib + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + ArmPkg/ArmPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + Silicon/NXP/iMXPlatformPkg/iMXPlatformPkg.dec + Silicon/NXP/iMX6Pkg/iMX6Pkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + IoLib + TimerLib + iMXIoMuxLib + +[Sources.common] + iMX6ClkPwr.c + +[FeaturePcd] + giMX6TokenSpaceGuid.PcdLvdsEnable + +[FixedPcd] + giMXPlatformTokenSpaceGuid.PcdGpioBankMemoryRange diff --git a/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6ClkPwr_private.h= b/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6ClkPwr_private.h new file mode 100644 index 000000000000..8cb6c6062148 --- /dev/null +++ b/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6ClkPwr_private.h @@ -0,0 +1,203 @@ +/** @file +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Copyright 2018 NXP +* +* This program and the accompanying materials +* are licensed and made available under the terms and conditions of the B= SD 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 IM= PLIED. +* +**/ + +#ifndef _IMX6_CLK_PWR_PRIVATE_H_ +#define _IMX6_CLK_PWR_PRIVATE_H_ + +#ifndef ARRAYSIZE +#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0])) +#endif // ARRAYSIZE + +#ifndef C_ASSERT +#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +#endif // C_ASSERT + +typedef enum { + IMX_PLL_PFD0, + IMX_PLL_PFD1, + IMX_PLL_PFD2, + IMX_PLL_PFD3, +} IMX_PLL_PFD; + +typedef struct { + UINT16 RegisterIndex; // Register index (0-6) + UINT16 GateNumber; // Gate number within register (0-15) +} IMX_CCGR_INDEX; + +IMX_CCGR_INDEX ImxpCcgrIndexFromClkGate (IMX_CLK_GATE ClockGate); + +#define _BITS_PER_UINTN (8 * sizeof(UINTN)) + +typedef struct { + UINTN Valid[(IMX_CLK_MAX + _BITS_PER_UINTN) / _BITS_PER_UINTN]; + IMX_CLOCK_INFO Table[IMX_CLK_MAX]; +} IMX_CLOCK_TREE_CACHE; + +VOID ImxpClkPwrCacheReset (); + +IMX_CLK ImxpClkFromBypassClkSource (IMX_PLL_BYPASS_CLK_SRC BypassClockSour= ce); + +VOID ImxCcmConfigureGpuClockTree (); + +VOID ImxCcmConfigureIPUDIxClockTree (); + +VOID ImxCcmConfigureIPULDBxClockTree (); + +#if (defined(CPU_IMX6DQ) || defined(CPU_IMX6SDL)) +VOID ImxSetClockRatePLL5 (UINT32 ClockRate, IMX_CCM_PLL_VIDEO_CTRL_POST_DI= V_SELECT PostDivSelect); +#elif defined(CPU_IMX6SX) +VOID ImxSetClockRatePLL5 (UINT32 TargetClockRate, UINT32 PreDividerLcdif1V= al, UINT32 PostDividerLcdif1Val); +#else +#error iMX6 CPU Type Not Defined! +#endif + +// +// Clock Info functions +// + +EFI_STATUS +ImxpGetClockInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + IN IMX_CLK ClockId, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +VOID ImxpGetOsc24ClkInfo (OUT IMX_CLOCK_INFO *ClockInfo); + +EFI_STATUS +ImxpGetPll1MainClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetPll2MainClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetPll2PfdClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + IMX_PLL_PFD PfdIndex, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetPll3MainClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetPll3PfdClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + IMX_PLL_PFD PfdIndex, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetPll3SwClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetAxiClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetPeriphClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetPrePeriphClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetPeriphClk2Info ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetArmClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetMmdcCh0ClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetAhbClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetIpgClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetGpu2dAxiClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetGpu3dAxiClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetGpu2dCoreClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetGpu3dCoreClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +EFI_STATUS +ImxpGetGpu3dShaderClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ); + +// +// Power functions +// + +VOID ImxEnableGpuVpuPowerDomain (); + +VOID ImxDisableGpuVpuPowerDomain (); + +#endif // _IMX6_CLK_PWR_PRIVATE_H_ diff --git a/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6DQClkPwr.inc b/S= ilicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6DQClkPwr.inc new file mode 100644 index 000000000000..e01e663775c6 --- /dev/null +++ b/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6DQClkPwr.inc @@ -0,0 +1,1278 @@ +/** @file +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Copyright 2018 NXP +* +* This program and the accompanying materials +* are licensed and made available under the terms and conditions of the B= SD 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 IM= PLIED. +* +**/ + +#if !defined(CPU_IMX6DQ) +#error iMX6DQClkPwr.inc should not be compiled for non iMX6DQ platform. +#endif + +/** + Get the CCGR register index and gate number for a clock gate. +**/ + +IMX_CCGR_INDEX ImxpCcgrIndexFromClkGate (IMX_CLK_GATE ClockGate) +{ + static const IMX_CCGR_INDEX ImxpCcgrIndexMap[] =3D { + {0, 0}, // MX6_AIPS_TZ1_CLK_ENABLE + {0, 1}, // MX6_AIPS_TZ2_CLK_ENABLE + {0, 2}, // MX6_APBHDMA_HCLK_ENABLE + {0, 3}, // MX6_ASRC_CLK_ENABLE + {0, 4}, // MX6_CAAM_SECURE_MEM_CLK_ENABLE + {0, 5}, // MX6_CAAM_WRAPPER_ACLK_ENABLE + {0, 6}, // MX6_CAAM_WRAPPER_IPG_ENABLE + {0, 7}, // MX6_CAN1_CLK_ENABLE + {0, 8}, // MX6_CAN1_SERIAL_CLK_ENABLE + {0, 9}, // MX6_CAN2_CLK_ENABLE + {0, 10}, // MX6_CAN2_SERIAL_CLK_ENABLE + {0, 11}, // MX6_ARM_DBG_CLK_ENABLE + {0, 12}, // MX6_DCIC1_CLK_ENABLE + {0, 13}, // MX6_DCIC2_CLK_ENABLE + {0, 14}, // MX6_DTCP_CLK_ENABLE + {1, 0}, // MX6_ECSPI1_CLK_ENABLE + {1, 1}, // MX6_ECSPI2_CLK_ENABLE + {1, 2}, // MX6_ECSPI3_CLK_ENABLE + {1, 3}, // MX6_ECSPI4_CLK_ENABLE + {1, 4}, // MX6_ECSPI5_CLK_ENABLE + {1, 5}, // MX6_ENET_CLK_ENABLE + {1, 6}, // MX6_EPIT1_CLK_ENABLE + {1, 7}, // MX6_EPIT2_CLK_ENABLE + {1, 8}, // MX6_ESAI_CLK_ENABLE + {1, 10}, // MX6_GPT_CLK_ENABLE + {1, 11}, // MX6_GPT_SERIAL_CLK_ENABLE + {1, 12}, // MX6_GPU2D_CLK_ENABLE + {1, 13}, // MX6_GPU3D_CLK_ENABLE + {2, 0}, // MX6_HDMI_TX_ENABLE + {2, 2}, // MX6_HDMI_TX_ISFRCLK_ENABLE + {2, 3}, // MX6_I2C1_SERIAL_CLK_ENABLE + {2, 4}, // MX6_I2C2_SERIAL_CLK_ENABLE + {2, 5}, // MX6_I2C3_SERIAL_CLK_ENABLE + {2, 6}, // MX6_IIM_CLK_ENABLE + {2, 7}, // MX6_IOMUX_IPT_CLK_IO_ENABLE + {2, 8}, // MX6_IPMUX1_CLK_ENABLE + {2, 9}, // MX6_IPMUX2_CLK_ENABLE + {2, 10}, // MX6_IPMUX3_CLK_ENABLE + {2, 11}, // MX6_IPSYNC_IP2APB_TZASC1_IPG_MASTER_CLK_ENABLE + {2, 12}, // MX6_IPSYNC_IP2APB_TZASC2_IPG_MASTER_CLK_ENABLE + {2, 13}, // MX6_IPSYNC_VDOA_IPG_MASTER_CLK_ENABLE + {3, 0}, // MX6_IPU1_IPU_CLK_ENABLE + {3, 1}, // MX6_IPU1_IPU_DI0_CLK_ENABLE + {3, 2}, // MX6_IPU1_IPU_DI1_CLK_ENABLE + {3, 3}, // MX6_IPU2_IPU_CLK_ENABLE + {3, 4}, // MX6_IPU2_IPU_DI0_CLK_ENABLE + {3, 5}, // MX6_IPU2_IPU_DI1_CLK_ENABLE + {3, 6}, // MX6_LDB_DI0_CLK_ENABLE + {3, 7}, // MX6_LDB_DI1_CLK_ENABLE + {3, 8}, // MX6_MIPI_CORE_CFG_CLK_ENABLE + {3, 9}, // MX6_MLB_CLK_ENABLE + {3, 10}, // MX6_MMDC_CORE_ACLK_FAST_CORE_P0_ENABLE + {3, 12}, // MX6_MMDC_CORE_IPG_CLK_P0_ENABLE + {3, 14}, // MX6_OCRAM_CLK_ENABLE + {3, 15}, // MX6_OPENVGAXICLK_CLK_ROOT_ENABLE + {4, 0}, // MX6_PCIE_ROOT_ENABLE + {4, 4}, // MX6_PL301_MX6QFAST1_S133CLK_ENABLE + {4, 6}, // MX6_PL301_MX6QPER1_BCHCLK_ENABLE + {4, 7}, // MX6_PL301_MX6QPER2_MAINCLK_ENABLE + {4, 8}, // MX6_PWM1_CLK_ENABLE + {4, 9}, // MX6_PWM2_CLK_ENABLE + {4, 10}, // MX6_PWM3_CLK_ENABLE + {4, 11}, // MX6_PWM4_CLK_ENABLE + {4, 12}, // MX6_RAWNAND_U_BCH_INPUT_APB_CLK_ENABLE + {4, 13}, // MX6_RAWNAND_U_GPMI_BCH_INPUT_BCH_CLK_ENABLE + {4, 14}, // MX6_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_CLK_ENABLE + {4, 15}, // MX6_RAWNAND_U_GPMI_INPUT_APB_CLK_ENABLE + {5, 0}, // MX6_ROM_CLK_ENABLE + {5, 2}, // MX6_SATA_CLK_ENABLE + {5, 3}, // MX6_SDMA_CLK_ENABLE + {5, 6}, // MX6_SPBA_CLK_ENABLE + {5, 7}, // MX6_SPDIF_CLK_ENABLE + {5, 9}, // MX6_SSI1_CLK_ENABLE + {5, 10}, // MX6_SSI2_CLK_ENABLE + {5, 11}, // MX6_SSI3_CLK_ENABLE + {5, 12}, // MX6_UART_CLK_ENABLE + {5, 13}, // MX6_UART_SERIAL_CLK_ENABLE + {6, 0}, // MX6_USBOH3_CLK_ENABLE + {6, 1}, // MX6_USDHC1_CLK_ENABLE + {6, 2}, // MX6_USDHC2_CLK_ENABLE + {6, 3}, // MX6_USDHC3_CLK_ENABLE + {6, 4}, // MX6_USDHC4_CLK_ENABLE + {6, 5}, // MX6_EIM_SLOW_CLK_ENABLE + {6, 6}, // MX6_VDOAXICLK_CLK_ENABLE + {6, 7}, // MX6_VPU_CLK_ENABLE + }; + + return ImxpCcgrIndexMap[ClockGate]; +} + +CONST CHAR16 *StringFromImxClk (IMX_CLK Value) +{ + switch (Value) { + case IMX_CLK_NONE: return L"(none)"; + case IMX_OSC_CLK: return L"OSC_CLK"; + case IMX_PLL1_MAIN_CLK: return L"PLL1_MAIN_CLK"; + case IMX_PLL2_MAIN_CLK: return L"PLL2_MAIN_CLK"; + case IMX_PLL2_PFD0: return L"PLL2_PFD0"; + case IMX_PLL2_PFD1: return L"PLL2_PFD1"; + case IMX_PLL2_PFD2: return L"PLL2_PFD2"; + case IMX_PLL3_MAIN_CLK: return L"PLL3_MAIN_CLK"; + case IMX_PLL3_PFD0: return L"PLL3_PFD0"; + case IMX_PLL3_PFD1: return L"PLL3_PFD1"; + case IMX_PLL3_PFD2: return L"PLL3_PFD2"; + case IMX_PLL3_PFD3: return L"PLL3_PFD3"; + case IMX_PLL4_MAIN_CLK: return L"PLL4_MAIN_CLK"; + case IMX_PLL5_MAIN_CLK: return L"PLL5_MAIN_CLK"; + case IMX_CLK1: return L"CLK1"; + case IMX_CLK2: return L"CLK2"; + case IMX_PLL1_SW_CLK: return L"PLL1_SW_CLK"; + case IMX_STEP_CLK: return L"STEP_CLK"; + case IMX_PLL3_SW_CLK: return L"PLL3_SW_CLK"; + case IMX_AXI_ALT: return L"AXI_ALT"; + case IMX_AXI_CLK_ROOT: return L"AXI_CLK_ROOT"; + case IMX_PERIPH_CLK2: return L"PERIPH_CLK2"; + case IMX_PERIPH_CLK: return L"PERIPH_CLK"; + case IMX_PRE_PERIPH_CLK: return L"PRE_PERIPH_CLK"; + case IMX_PRE_PERIPH2_CLK: return L"PRE_PERIPH2_CLK"; + case IMX_PERIPH2_CLK: return L"PERIPH2_CLK"; + case IMX_ARM_CLK_ROOT: return L"ARM_CLK_ROOT"; + case IMX_MMDC_CH0_CLK_ROOT: return L"MMDC_CH0_CLK_ROOT"; + case IMX_MMDC_CH1_CLK_ROOT: return L"MMDC_CH1_CLK_ROOT"; + case IMX_AHB_CLK_ROOT: return L"AHB_CLK_ROOT"; + case IMX_IPG_CLK_ROOT: return L"IPG_CLK_ROOT"; + case IMX_PERCLK_CLK_ROOT: return L"PERCLK_CLK_ROOT"; + case IMX_USDHC1_CLK_ROOT: return L"USDHC1_CLK_ROOT"; + case IMX_USDHC2_CLK_ROOT: return L"USDHC2_CLK_ROOT"; + case IMX_USDHC3_CLK_ROOT: return L"USDHC3_CLK_ROOT"; + case IMX_USDHC4_CLK_ROOT: return L"USDHC4_CLK_ROOT"; + case IMX_SSI1_CLK_ROOT: return L"SSI1_CLK_ROOT"; + case IMX_SSI2_CLK_ROOT: return L"SSI2_CLK_ROOT"; + case IMX_SSI3_CLK_ROOT: return L"SSI3_CLK_ROOT"; + case IMX_GPU2D_AXI_CLK_ROOT: return L"GPU2D_AXI_CLK_ROOT"; + case IMX_GPU3D_AXI_CLK_ROOT: return L"GPU3D_AXI_CLK_ROOT"; + case IMX_PCIE_AXI_CLK_ROOT: return L"PCIE_AXI_CLK_ROOT"; + case IMX_VDO_AXI_CLK_ROOT: return L"VDO_AXI_CLK_ROOT"; + case IMX_IPU1_HSP_CLK_ROOT: return L"IPU1_HSP_CLK_ROOT"; + case IMX_IPU2_HSP_CLK_ROOT: return L"IPU2_HSP_CLK_ROOT"; + case IMX_GPU2D_CORE_CLK_ROOT: return L"GPU2D_CORE_CLK_ROOT"; + case IMX_ACLK_EIM_SLOW_CLK_ROOT: return L"ACLK_EIM_SLOW_CLK_ROOT"; + case IMX_ACLK_CLK_ROOT: return L"ACLK_CLK_ROOT"; + case IMX_ENFC_CLK_ROOT: return L"ENFC_CLK_ROOT"; + case IMX_GPU3D_CORE_CLK_ROOT: return L"GPU3D_CORE_CLK_ROOT"; + case IMX_GPU3D_SHADER_CLK_ROOT: return L"GPU3D_SHADER_CLK_ROOT"; + case IMX_VPU_AXI_CLK_ROOT: return L"VPU_AXI_CLK_ROOT"; + case IMX_IPU1_DI0_CLK_ROOT: return L"IPU1_DI0_CLK_ROOT"; + case IMX_IPU1_DI1_CLK_ROOT: return L"IPU1_DI1_CLK_ROOT"; + case IMX_IPU2_DI0_CLK_ROOT: return L"IPU2_DI0_CLK_ROOT"; + case IMX_IPU2_DI1_CLK_ROOT: return L"IPU2_DI1_CLK_ROOT"; + case IMX_LDB_DI0_SERIAL_CLK_ROOT: return L"LDB_DI0_SERIAL_CLK_ROOT"; + case IMX_LDB_DI0_IPU: return L"LDB_DI0_IPU"; + case IMX_LDB_DI1_SERIAL_CLK_ROOT: return L"LDB_DI1_SERIAL_CLK_ROOT"; + case IMX_LDB_DI1_IPU: return L"LDB_DI1_IPU"; + case IMX_SPDIF0_CLK_ROOT: return L"SPDIF0_CLK_ROOT"; + case IMX_SPDIF1_CLK_ROOT: return L"SPDIF1_CLK_ROOT"; + case IMX_ESAI_CLK_ROOT: return L"ESAI_CLK_ROOT"; + case IMX_HSI_TX_CLK_ROOT: return L"HSI_TX_CLK_ROOT"; + case IMX_CAN_CLK_ROOT: return L"CAN_CLK_ROOT"; + case IMX_ECSPI_CLK_ROOT: return L"ECSPI_CLK_ROOT"; + case IMX_UART_CLK_ROOT: return L"UART_CLK_ROOT"; + case IMX_VIDEO_27M_CLK_ROOT: return L"VIDEO_27M_CLK_ROOT"; + default: + ASSERT (FALSE); + return L"[invalid IMX_CLK value]"; + } +} + +IMX_CLK ImxpClkFromBypassClkSource (IMX_PLL_BYPASS_CLK_SRC BypassClockSour= ce) +{ + switch (BypassClockSource) { + case IMX_PLL_BYPASS_CLK_SRC_REF_CLK_24M: + return IMX_OSC_CLK; + case IMX_PLL_BYPASS_CLK_SRC_CLK1: + return IMX_CLK1; + case IMX_PLL_BYPASS_CLK_SRC_CLK2: + return IMX_CLK2; + case IMX_PLL_BYPASS_CLK_SRC_XOR: + default: + ASSERT (FALSE); + return IMX_CLK_NONE; + } +} + +/** + Configure the GPU clock tree so that GPU2D and GPU3D are clocked from + the AXI clock root and are within the allowed frequency range. + + The GPU must be powered down, and GPU clocks must be gated when this + function is called. +**/ +VOID ImxCcmConfigureGpuClockTree () +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + cbcmrReg.gpu2d_axi_clk_sel =3D IMX_CCM_GPU2D_AXI_CLK_SEL_AXI; + cbcmrReg.gpu3d_axi_clk_sel =3D IMX_CCM_GPU3D_AXI_CLK_SEL_AXI; + + cbcmrReg.gpu2d_core_clk_sel =3D IMX_CCM_GPU2D_CORE_CLK_SEL_PLL2_PFD0; + cbcmrReg.gpu3d_core_clk_sel =3D IMX_CCM_GPU3D_CORE_CLK_SEL_MMDC_CH0_AXI; + cbcmrReg.gpu3d_shader_clk_sel =3D IMX_CCM_GPU3D_SHADER_CLK_SEL_MMDC_CH0_= AXI; + + cbcmrReg.gpu2d_core_clk_podf =3D 0; + cbcmrReg.gpu3d_core_podf =3D 0; + cbcmrReg.gpu3d_shader_podf =3D 0; + + ImxpClkPwrCacheReset (); + MmioWrite32 ((UINTN) &ccmRegisters->CBCMR, cbcmrReg.AsUint32); +} + +/** + Configure all of DIx clock tree for both IPU1 and IPU2. For flexibilit= y + purpose use PLL5 (PLL Video) as main reference clock. PLL 5 has flexib= le + divider making it easily configurable. Muxing and clock programming ne= eds + when to be updated when supporting multiple display. +**/ +VOID ImxCcmConfigureIPUDIxClockTree () +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX= _CCM_BASE; + IMX_CCM_CHSCCDR_REG chscddrReg; chscddrReg.AsUint32 =3D MmioRead32((UI= NTN)&ccmRegisters->CHSCCDR); + + // Setup muxing to pre-mux + if (FeaturePcdGet(PcdLvdsEnable)) { + chscddrReg.ipu1_di0_clk_sel =3D IMX_CHSCCDR_IPU1_DI0_CLK_SEL_LDB_D= I0_CLK; + chscddrReg.ipu1_di1_clk_sel =3D IMX_CHSCCDR_IPU1_DI0_CLK_SEL_LDB_D= I0_CLK; + } else { + chscddrReg.ipu1_di0_clk_sel =3D IMX_CHSCCDR_IPU1_DI0_CLK_SEL_PREMU= X; + chscddrReg.ipu1_di1_clk_sel =3D IMX_CHSCCDR_IPU1_DI0_CLK_SEL_PREMU= X; + } + + chscddrReg.ipu1_di0_podf =3D IMX_CHSCCDR_IPU1_DI0_PODF_DIV_1; + chscddrReg.ipu1_di1_podf =3D IMX_CHSCCDR_IPU1_DI1_PODF_DIV_1; + chscddrReg.ipu1_di0_pre_clk_sel =3D IMX_CHSCCDR_IPU1_DI0_PRE_CLK_SEL_P= LL5; + chscddrReg.ipu1_di1_pre_clk_sel =3D IMX_CHSCCDR_IPU1_DI1_PRE_CLK_SEL_P= LL5; + + MmioWrite32 ((UINTN)&ccmRegisters->CHSCCDR, chscddrReg.AsUint32); +} + +/** + Configure both LDB0/1 to use PLL5 clock +**/ +VOID ImxCcmConfigureIPULDBxClockTree () +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX= _CCM_BASE; + IMX_CCM_CS2CDR_REG cs2cdrReg; cs2cdrReg.AsUint32 =3D MmioRead32((UINTN= )&ccmRegisters->CS2CDR); + + cs2cdrReg.ldb_di0_clk_sel =3D 0x0; + cs2cdrReg.ldb_di1_clk_sel =3D 0x0; + + MmioWrite32 ((UINTN)&ccmRegisters->CS2CDR, cs2cdrReg.AsUint32); +} + +/** + Configure PLL 5 clock rate to the desired clock rate +**/ +VOID ImxSetClockRatePLL5 ( + UINT32 ClockRate, + IMX_CCM_PLL_VIDEO_CTRL_POST_DIV_SELECT PostDivSelect + ) +{ + // Use clock rate as denom for simple fractional calculation + UINT32 denom =3D IMX_REF_CLK_24M_FREQ; + UINT32 divSelect =3D ClockRate / IMX_REF_CLK_24M_FREQ; // Signed value + UINT32 numerator =3D ClockRate % IMX_REF_CLK_24M_FREQ; + volatile IMX_CCM_ANALOG_REGISTERS *ccmAnalogRegisters =3D + (IMX_CCM_ANALOG_REGISTERS *)IMX_CCM_ANALOG_BASE; + IMX_CCM_PLL_VIDEO_CTRL_REG pllVideoCtrlReg; pllVideoCtrlReg.AsUint32 = =3D + MmioRead32 ((UINTN)&ccmAnalogRegisters->PLL_VIDEO); + + ASSERT (numerator < denom); + ASSERT ((divSelect >=3D 27) && (divSelect <=3D 54)); + + // PLL output frequency =3D Fref * (DIV_SELECT + NUM / DENOM) + // Use the clock rate as denomitor to make fractional calulation simpl= e + pllVideoCtrlReg.DIV_SELECT =3D divSelect; + pllVideoCtrlReg.POST_DIV_SELECT =3D PostDivSelect; + + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO, pllVideoCtrlReg.AsUint32); + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO_NUM, numerator); + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO_DENOM, denom); + + pllVideoCtrlReg.AsUint32 =3D MmioRead32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO); + + // Check to see if pll is locked, if not attempt to enable it + if (pllVideoCtrlReg.LOCK =3D=3D 0) { + UINT32 counter =3D 10000; + { + IMX_CCM_PLL_VIDEO_CTRL_REG pllVideoCtrlClearReg =3D { 0 }; + pllVideoCtrlClearReg.POWERDOWN =3D 1; + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO_CLR, + pllVideoCtrlClearReg.AsUint32); + } + pllVideoCtrlReg.AsUint32 =3D MmioRead32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO); + { + IMX_CCM_PLL_VIDEO_CTRL_REG pllVideoCtrlSetReg =3D { 0 }; + pllVideoCtrlSetReg.ENABLE =3D 1; + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO_SET, + pllVideoCtrlSetReg.AsUint32); + } + pllVideoCtrlReg.AsUint32 =3D MmioRead32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO); + { + IMX_CCM_PLL_VIDEO_CTRL_REG pllVideoCtrlClearReg =3D { 0 }; + pllVideoCtrlClearReg.BYPASS =3D 1; + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO_CLR, + pllVideoCtrlClearReg.AsUint32); + } + pllVideoCtrlReg.AsUint32 =3D MmioRead32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO); + do { + pllVideoCtrlReg.AsUint32 =3D MmioRead32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO); + --counter; + } while ((pllVideoCtrlReg.LOCK =3D=3D 0) && (counter > 0)); + ASSERT (counter > 0); + } +} + +EFI_STATUS +ImxpGetPll2PfdClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + IMX_PLL_PFD PfdIndex, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_ANALOG_REGISTERS *ccmAnalogRegisters =3D + (IMX_CCM_ANALOG_REGISTERS *) IMX_CCM_ANALOG_BASE; + + IMX_CCM_PFD_528_REG pfd528Reg; pfd528Reg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmAnalogRegisters->PFD_528); + + UINT32 pfdFrac; + switch (PfdIndex) { + case IMX_PLL_PFD0: + pfdFrac =3D pfd528Reg.PFD0_FRAC; + break; + case IMX_PLL_PFD1: + pfdFrac =3D pfd528Reg.PFD1_FRAC; + break; + case IMX_PLL_PFD2: + pfdFrac =3D pfd528Reg.PFD2_FRAC; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, IMX_PLL2_MAIN_CLK, &paren= tInfo); + if (EFI_ERROR (status)) { + return status; + } + + // The resulting frequency shall be 528*18/PFDn_FRAC + // where PFD0_FRAC is in the range 12-35. + ASSERT ((pfdFrac >=3D 12) && (pfdFrac <=3D 35)); + ClockInfo->Frequency =3D (UINT32) ((UINT64) parentInfo.Frequency * 18 / = pfdFrac); + ClockInfo->Parent =3D IMX_PLL2_MAIN_CLK; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetAxiClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + IMX_CLK parent; + if (cbcdrReg.axi_sel =3D=3D IMX_CCM_AXI_SEL_PERIPH_CLK) { + parent =3D IMX_PERIPH_CLK; + } else { + ASSERT (cbcdrReg.axi_sel =3D=3D IMX_CCM_AXI_SEL_AXI_ALT); + parent =3D IMX_AXI_ALT; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cbcdrReg.axi_podf); + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetGpu2dCoreClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + IMX_CLK parent; + switch (cbcmrReg.gpu2d_core_clk_sel) { + case IMX_CCM_GPU2D_CORE_CLK_SEL_AXI: + parent =3D IMX_AXI_CLK_ROOT; + break; + case IMX_CCM_GPU2D_CORE_CLK_SEL_PLL3_SW: + parent =3D IMX_PLL3_SW_CLK; + break; + case IMX_CCM_GPU2D_CORE_CLK_SEL_PLL2_PFD0: + parent =3D IMX_PLL2_PFD0; + break; + case IMX_CCM_GPU2D_CORE_CLK_SEL_PLL2_PFD2: + parent =3D IMX_PLL2_PFD2; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D + parentInfo.Frequency / (1 + cbcmrReg.gpu2d_core_clk_podf); + ClockInfo->Parent =3D parent; + + if (ClockInfo->Frequency > IMX_GPU2D_CORE_CLK_MAX) { + DEBUG (( + DEBUG_WARN, + "GPU2D_CORE_CLK exceeds maximum. (Value =3D %d, Max =3D %d)\r\n", + ClockInfo->Frequency, + IMX_GPU2D_CORE_CLK_MAX)); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetGpu3dCoreClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + IMX_CLK parent; + switch (cbcmrReg.gpu3d_core_clk_sel) { + case IMX_CCM_GPU3D_CORE_CLK_SEL_MMDC_CH0_AXI: + parent =3D IMX_MMDC_CH0_CLK_ROOT; + break; + case IMX_CCM_GPU3D_CORE_CLK_SEL_PLL3_SW: + parent =3D IMX_PLL3_SW_CLK; + break; + case IMX_CCM_GPU3D_CORE_CLK_SEL_PLL2_PFD1: + parent =3D IMX_PLL2_PFD1; + break; + case IMX_CCM_GPU3D_CORE_CLK_SEL_PLL2_PFD2: + parent =3D IMX_PLL2_PFD2; + break; + default: + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cbcmrReg.gpu3d_core= _podf); + ClockInfo->Parent =3D parent; + + if (ClockInfo->Frequency > IMX_GPU3D_CORE_CLK_MAX) { + DEBUG (( + DEBUG_WARN, + "GPU3D_CORE_CLK exceeds maximum. (Value =3D %d, Max =3D %d)\r\n", + ClockInfo->Frequency, + IMX_GPU3D_CORE_CLK_MAX)); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetGpu3dShaderClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + IMX_CLK parent; + switch (cbcmrReg.gpu3d_shader_clk_sel) { + case IMX_CCM_GPU3D_SHADER_CLK_SEL_MMDC_CH0_AXI: + parent =3D IMX_MMDC_CH0_CLK_ROOT; + break; + case IMX_CCM_GPU3D_SHADER_CLK_SEL_PLL3_SW: + parent =3D IMX_PLL3_SW_CLK; + break; + case IMX_CCM_GPU3D_SHADER_CLK_SEL_PLL2_PFD1: + parent =3D IMX_PLL2_PFD1; + break; + case IMX_CCM_GPU3D_SHADER_CLK_SEL_PLL3_PFD0: + parent =3D IMX_PLL3_PFD0; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D + parentInfo.Frequency / (1 + cbcmrReg.gpu3d_shader_podf); + + ClockInfo->Parent =3D parent; + + if (ClockInfo->Frequency > IMX_GPU3D_SHADER_CLK_MAX) { + DEBUG (( + DEBUG_WARN, + "GPU3D_SHADER_CLK exceeds maximum. (Value =3D %d, Max =3D %d)", + ClockInfo->Frequency, + IMX_GPU3D_SHADER_CLK_MAX)); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetPeriphClk2Info ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg; cbcmrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCMR); + + IMX_CLK parent; + switch (cbcmrReg.periph_clk2_sel) { + case IMX_CCM_PERIPH_CLK2_SEL_PLL3_SW_CLK: + parent =3D IMX_PLL3_SW_CLK; + break; + case IMX_CCM_PERIPH_CLK2_SEL_OSC_CLK: + parent =3D IMX_OSC_CLK; + break; + case IMX_CCM_PERIPH_CLK2_SEL_PLL2: + parent =3D IMX_PLL2_MAIN_CLK; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cbcdrReg.periph_clk= 2_podf); + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetPeriphClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + IMX_CLK parent; + + // NOTE: periph_clk_sel is OR'd with PLL_bypass_en2 (from jtag) to + // produce the input value to the MUX. We assume PLL_bypass_en2 is= 0. + if (cbcdrReg.periph_clk_sel =3D=3D 0) { + parent =3D IMX_PRE_PERIPH_CLK; + } else { + ASSERT (cbcdrReg.periph_clk_sel =3D=3D 1); + parent =3D IMX_PERIPH_CLK2; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cbcdrReg.mmdc_ch0_a= xi_podf); + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetMmdcCh0ClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, IMX_PERIPH_CLK, &parentIn= fo); + if (EFI_ERROR (status)) { + return status; + } + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + ClockInfo->Frequency =3D + parentInfo.Frequency / (1 + cbcdrReg.mmdc_ch0_axi_podf); + ClockInfo->Parent =3D IMX_PERIPH_CLK; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetGpu2dAxiClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + IMX_CLK parent; + if (cbcmrReg.gpu2d_axi_clk_sel =3D=3D IMX_CCM_GPU2D_AXI_CLK_SEL_AXI) { + parent =3D IMX_AXI_CLK_ROOT; + } else { + ASSERT (cbcmrReg.gpu2d_axi_clk_sel =3D=3D IMX_CCM_GPU2D_AXI_CLK_SEL_AH= B); + parent =3D IMX_AHB_CLK_ROOT; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency; + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetGpu3dAxiClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + IMX_CLK parent; + if (cbcmrReg.gpu3d_axi_clk_sel =3D=3D IMX_CCM_GPU3D_AXI_CLK_SEL_AXI) { + parent =3D IMX_AXI_CLK_ROOT; + } else { + ASSERT (cbcmrReg.gpu3d_axi_clk_sel =3D=3D IMX_CCM_GPU3D_AXI_CLK_SEL_AH= B); + parent =3D IMX_AHB_CLK_ROOT; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency; + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +VOID ImxEnableGpuVpuPowerDomain () +{ + volatile IMX_CCM_ANALOG_REGISTERS *analogRegisters =3D + (IMX_CCM_ANALOG_REGISTERS *) IMX_CCM_ANALOG_BASE; + + volatile IMX_GPC_REGISTERS *gpcRegisters =3D (IMX_GPC_REGISTERS *) IMX_G= PC_BASE; + volatile IMX_GPC_PGC_REGISTERS *gpuPgcRegisters =3D &gpcRegisters->PGC_G= PU; + + // Configure GPC/PGC PUPSCR Register SW2ISO bits + { + IMX_GPC_PGC_PUPSCR_REG pupscrReg; pupscrReg.AsUint32 =3D + MmioRead32 ((UINTN) &gpuPgcRegisters->PUPSCR); + + pupscrReg.SW =3D IMX_GPC_PGC_PUPSCR_SW_DEFAULT; + pupscrReg.SW2ISO =3D IMX_GPC_PGC_PUPSCR_SW2ISO_DEFAULT; + + MmioWrite32 ((UINTN) &gpuPgcRegisters->PUPSCR, pupscrReg.AsUint32); + } + + // Turn on LDO_PU to 1.250V + { + IMX_PMU_REG_CORE_REG pmuCoreReg =3D {0}; + pmuCoreReg.REG1_TARG =3D 0x1f; + + MmioWrite32 ((UINTN) &analogRegisters->PMU_REG_CORE_CLR, pmuCoreReg.As= Uint32); + + pmuCoreReg.REG1_TARG =3D 22; + MmioWrite32 ((UINTN) &analogRegisters->PMU_REG_CORE_SET, pmuCoreReg.As= Uint32); + + MicroSecondDelay (100); + } + + // Assert power up request + IMX_GPC_CNTR_REG gpcCntrReg; gpcCntrReg.AsUint32 =3D + MmioRead32 ((UINTN) &gpcRegisters->CNTR); + + gpcCntrReg.gpu_vpu_pdn_req =3D 0; + gpcCntrReg.gpu_vpu_pup_req =3D 1; + + MmioWrite32 ((UINTN) &gpcRegisters->CNTR, gpcCntrReg.AsUint32); + + // Wait for power up request to complete + do { + gpcCntrReg.AsUint32 =3D MmioRead32 ((UINTN) &gpcRegisters->CNTR); + } while (gpcCntrReg.gpu_vpu_pup_req !=3D 0); +} + +VOID ImxDisableGpuVpuPowerDomain () +{ + volatile IMX_GPC_REGISTERS *gpcRegisters =3D (IMX_GPC_REGISTERS *) IMX_G= PC_BASE; + volatile IMX_GPC_PGC_REGISTERS *gpuPgcRegisters =3D &gpcRegisters->PGC_G= PU; + + // Configure GPC/PGC PDNSCR Register ISO bits + { + IMX_GPC_PGC_PDNSCR_REG pdnscrReg; pdnscrReg.AsUint32 =3D + MmioRead32 ((UINTN) &gpuPgcRegisters->PDNSCR); + + pdnscrReg.ISO =3D IMX_GPC_PGC_PDNSCR_ISO_DEFAULT; + pdnscrReg.ISO2SW =3D IMX_GPC_PGC_PDNSCR_ISO2SW_DEFAULT; + + MmioWrite32 ((UINTN) &gpuPgcRegisters->PDNSCR, pdnscrReg.AsUint32); + } + + // Configure GPC/PGC CTRL[PCR] bit to allow power down of the blocks + { + IMX_GPC_PGC_PGCR_REG ctrlReg; ctrlReg.AsUint32 =3D + MmioRead32 ((UINTN) &gpuPgcRegisters->CTRL); + + ctrlReg.PCR =3D 1; // enable powering down of the blocks + + MmioWrite32 ((UINTN) &gpuPgcRegisters->CTRL, ctrlReg.AsUint32); + } + + // Assert power down request + { + IMX_GPC_CNTR_REG gpcCntrReg; gpcCntrReg.AsUint32 =3D + MmioRead32 ((UINTN) &gpcRegisters->CNTR); + + gpcCntrReg.gpu_vpu_pdn_req =3D 1; + gpcCntrReg.gpu_vpu_pup_req =3D 0; + + MmioWrite32 ((UINTN) &gpcRegisters->CNTR, gpcCntrReg.AsUint32); + + // Wait for power down request to complete + do { + gpcCntrReg.AsUint32 =3D MmioRead32 ((UINTN) &gpcRegisters->CNTR); + } while (gpcCntrReg.gpu_vpu_pdn_req !=3D 0); + } +} + +EFI_STATUS +ImxpGetClockInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + IN IMX_CLK ClockId, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + ASSERT (ClockId < ARRAYSIZE(Cache->Table)); + + // First try to satisfy from cache + { + UINTN cacheValidBits =3D Cache->Valid[ClockId / _BITS_PER_UINTN]; + if (cacheValidBits & (1 << (ClockId % _BITS_PER_UINTN))) { + *ClockInfo =3D Cache->Table[ClockId]; + return EFI_SUCCESS; + } + } + + EFI_STATUS status; + switch (ClockId) { + case IMX_OSC_CLK: + ImxpGetOsc24ClkInfo (ClockInfo); + status =3D EFI_SUCCESS; + break; + case IMX_PLL1_MAIN_CLK: + status =3D ImxpGetPll1MainClkInfo (Cache, ClockInfo); + break; + case IMX_PLL2_MAIN_CLK: + status =3D ImxpGetPll2MainClkInfo (Cache, ClockInfo); + break; + case IMX_PLL2_PFD0: + status =3D ImxpGetPll2PfdClkInfo (Cache, IMX_PLL_PFD0, ClockInfo); + break; + case IMX_PLL2_PFD1: + status =3D ImxpGetPll2PfdClkInfo (Cache, IMX_PLL_PFD1, ClockInfo); + break; + case IMX_PLL2_PFD2: + status =3D ImxpGetPll2PfdClkInfo (Cache, IMX_PLL_PFD2, ClockInfo); + break; + case IMX_PLL3_MAIN_CLK: + status =3D ImxpGetPll3MainClkInfo (Cache, ClockInfo); + break; + case IMX_PLL3_PFD0: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD0, ClockInfo); + break; + case IMX_PLL3_PFD1: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD1, ClockInfo); + break; + case IMX_PLL3_PFD2: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD2, ClockInfo); + break; + case IMX_PLL3_PFD3: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD3, ClockInfo); + break; + case IMX_PLL3_SW_CLK: + status =3D ImxpGetPll3SwClkInfo (Cache, ClockInfo); + break; + case IMX_AXI_CLK_ROOT: + status =3D ImxpGetAxiClkRootInfo (Cache, ClockInfo); + break; + case IMX_PERIPH_CLK2: + status =3D ImxpGetPeriphClk2Info (Cache, ClockInfo); + break; + case IMX_PERIPH_CLK: + status =3D ImxpGetPeriphClkInfo (Cache, ClockInfo); + break; + case IMX_PRE_PERIPH_CLK: + status =3D ImxpGetPrePeriphClkInfo (Cache, ClockInfo); + break; + case IMX_ARM_CLK_ROOT: + status =3D ImxpGetArmClkRootInfo (Cache, ClockInfo); + break; + case IMX_MMDC_CH0_CLK_ROOT: + status =3D ImxpGetMmdcCh0ClkRootInfo (Cache, ClockInfo); + break; + case IMX_AHB_CLK_ROOT: + status =3D ImxpGetAhbClkRootInfo (Cache, ClockInfo); + break; + case IMX_IPG_CLK_ROOT: + status =3D ImxpGetIpgClkRootInfo (Cache, ClockInfo); + break; + case IMX_GPU2D_AXI_CLK_ROOT: + status =3D ImxpGetGpu2dAxiClkRootInfo (Cache, ClockInfo); + break; + case IMX_GPU3D_AXI_CLK_ROOT: + status =3D ImxpGetGpu3dAxiClkRootInfo (Cache, ClockInfo); + break; + case IMX_GPU2D_CORE_CLK_ROOT: + status =3D ImxpGetGpu2dCoreClkInfo (Cache, ClockInfo); + break; + case IMX_GPU3D_CORE_CLK_ROOT: + status =3D ImxpGetGpu3dCoreClkInfo (Cache, ClockInfo); + break; + case IMX_GPU3D_SHADER_CLK_ROOT: + status =3D ImxpGetGpu3dShaderClkInfo (Cache, ClockInfo); + break; + default: + return EFI_UNSUPPORTED; + } + + if (EFI_ERROR (status)) { + return status; + } + + // Update the cache + Cache->Table[ClockId] =3D *ClockInfo; + Cache->Valid[ClockId / _BITS_PER_UINTN] |=3D (1 << (ClockId % _BITS_PER_= UINTN)); + + return EFI_SUCCESS; +} + +/** + Power on and clock the GPU2D/GPU3D blocks. + + Follow the datasheet recommended sequence for clocking and powering: + Gate clocks -> unpower module -> + configure muxes/dividers -> power module -> Ungate clocks +**/ +EFI_STATUS ImxClkPwrGpuEnable () +{ +#if !defined(MDEPKG_NDEBUG) + + // + // Precondition: clock and power should be disabled + // + { + ASSERT (ImxClkPwrGetClockGate (IMX_GPU3D_CLK_ENABLE) =3D=3D IMX_CLOCK_= GATE_STATE_OFF); + ASSERT (ImxClkPwrGetClockGate (IMX_GPU2D_CLK_ENABLE) =3D=3D IMX_CLOCK_= GATE_STATE_OFF); + ASSERT (ImxClkPwrGetClockGate (IMX_OPENVGAXICLK_CLK_ROOT_ENABLE) =3D= =3D IMX_CLOCK_GATE_STATE_OFF); + } + +#endif + + // Ensure clocks are gated + ImxClkPwrSetClockGate (IMX_GPU3D_CLK_ENABLE, IMX_CLOCK_GATE_STATE_OFF); + ImxClkPwrSetClockGate (IMX_GPU2D_CLK_ENABLE, IMX_CLOCK_GATE_STATE_OFF); + ImxClkPwrSetClockGate (IMX_OPENVGAXICLK_CLK_ROOT_ENABLE, IMX_CLOCK_GATE_= STATE_OFF); + + // Ensure GPU powered down (GPU should be powered down anyway) + ImxDisableGpuVpuPowerDomain (); + + // Configure clock muxes and dividers for GPU3D, GPU2D, and OpenVG + ImxCcmConfigureGpuClockTree (); + + // Power on the GPU + ImxEnableGpuVpuPowerDomain (); + + // Ungate the GPU clocks + ImxClkPwrSetClockGate (IMX_GPU3D_CLK_ENABLE, IMX_CLOCK_GATE_STATE_ON); + ImxClkPwrSetClockGate (IMX_GPU2D_CLK_ENABLE, IMX_CLOCK_GATE_STATE_ON); + ImxClkPwrSetClockGate (IMX_OPENVGAXICLK_CLK_ROOT_ENABLE, IMX_CLOCK_GATE_= STATE_ON); + + return EFI_SUCCESS; +} + +/** + Setup the clock tree for Display Interface (DI) + Gate clocks -> Configure mux/div -> Ungate clocks -> Setup PLL +**/ +EFI_STATUS ImxClkPwrIpuDIxEnable () +{ + ImxClkPwrSetClockGate(IMX_IPU1_DI0_CLK_ENABLE, IMX_CCM_CCGR_OFF); + ImxClkPwrSetClockGate(IMX_IPU2_DI0_CLK_ENABLE, IMX_CCM_CCGR_OFF); + ImxClkPwrSetClockGate(IMX_IPU1_DI1_CLK_ENABLE, IMX_CCM_CCGR_OFF); + ImxClkPwrSetClockGate(IMX_IPU2_DI1_CLK_ENABLE, IMX_CCM_CCGR_OFF); + + ImxCcmConfigureIPUDIxClockTree(); + + ImxClkPwrSetClockGate(IMX_IPU1_DI0_CLK_ENABLE, IMX_CCM_CCGR_ON); + + // Setup PLL to 65MHz as expected from UBOOT although transition + // might be so fast that UBOOT screen would not be displayed + ImxSetPll5ReferenceRate(65000000); + + return EFI_SUCCESS; +} + +/** + Setup the clock tree for LDB0/1 +**/ +EFI_STATUS ImxClkPwrIpuLDBxEnable () +{ + ImxClkPwrSetClockGate(IMX_LDB_DI0_CLK_ENABLE, IMX_CCM_CCGR_OFF); + ImxClkPwrSetClockGate(IMX_LDB_DI1_CLK_ENABLE, IMX_CCM_CCGR_OFF); + + ImxCcmConfigureIPULDBxClockTree(); + + ImxClkPwrSetClockGate(IMX_LDB_DI0_CLK_ENABLE, IMX_CCM_CCGR_ON); + ImxClkPwrSetClockGate(IMX_LDB_DI1_CLK_ENABLE, IMX_CCM_CCGR_ON); + + return EFI_SUCCESS; +} + +/** + Configure PLL5 to the desired clock rate for all Display Interface (DI= ). + Currently only support one display to IPU1 DI0. +**/ +EFI_STATUS ImxSetPll5ReferenceRate ( + UINT32 ClockRate + ) +{ + BOOLEAN foundConfig =3D FALSE; + UINT32 dxPodfDivider; + UINT32 targetFreq; + UINT32 postDivSelectCount; + UINT32 postDivSelectValue[3] =3D { 1, 2, 4 }; + IMX_CCM_PLL_VIDEO_CTRL_POST_DIV_SELECT postDivSelect[3] =3D { + IMX_POST_DIV_SELECT_DIVIDE_1, + IMX_POST_DIV_SELECT_DIVIDE_2, + IMX_POST_DIV_SELECT_DIVIDE_4}; + + for (postDivSelectCount =3D 0; + postDivSelectCount < ARRAYSIZE (postDivSelectValue); + ++postDivSelectCount) { + + for (dxPodfDivider =3D 1; dxPodfDivider < 9; ++dxPodfDivider) { + + targetFreq =3D + dxPodfDivider * + ClockRate * + postDivSelectValue[postDivSelectCount]; + + // The valid range for PPL loop divider is 27-54 so we + // need to target freq need to fit within the valid range. + if ((targetFreq >=3D PLL5_MIN_FREQ) && + (targetFreq <=3D PLL5_MAX_FREQ)) { + foundConfig =3D TRUE; + break; + } + } + + if (foundConfig =3D=3D TRUE) { + break; + } + } + + if (foundConfig =3D=3D FALSE) { + DEBUG((DEBUG_ERROR, "ClockRate %d\n", ClockRate)); + ASSERT(FALSE); + return EFI_INVALID_PARAMETER; + } + + DEBUG (( + DEBUG_INFO, + "PLL 5 setting (%d) Target Freq %d Divider %d PostDiv %d\n", + ClockRate, + targetFreq, + dxPodfDivider, + postDivSelectValue[postDivSelectCount] + )); + + { + volatile IMX_CCM_REGISTERS *ccmRegisters =3D + (IMX_CCM_REGISTERS *)IMX_CCM_BASE; + IMX_CCM_CHSCCDR_REG chscddrReg; chscddrReg.AsUint32 =3D + MmioRead32 ((UINTN)&ccmRegisters->CHSCCDR) ; + + ImxClkPwrSetClockGate(IMX_IPU1_DI0_CLK_ENABLE, IMX_CCM_CCGR_OFF); + ImxClkPwrSetClockGate(IMX_IPU2_DI0_CLK_ENABLE, IMX_CCM_CCGR_OFF); + ImxClkPwrSetClockGate(IMX_IPU1_DI1_CLK_ENABLE, IMX_CCM_CCGR_OFF); + ImxClkPwrSetClockGate(IMX_IPU2_DI1_CLK_ENABLE, IMX_CCM_CCGR_OFF); + + chscddrReg.ipu1_di0_podf =3D dxPodfDivider - 1; + chscddrReg.ipu1_di1_podf =3D dxPodfDivider - 1; + + MmioWrite32 ((UINTN)&ccmRegisters->CHSCCDR, chscddrReg.AsUint32); + + ImxClkPwrSetClockGate(IMX_IPU1_DI0_CLK_ENABLE, IMX_CCM_CCGR_ON); + } + + ImxSetClockRatePLL5(targetFreq, postDivSelect[postDivSelectCount]); + + return EFI_SUCCESS; +} + +EFI_STATUS ImxClkPwrClkOut1Enable (IMX_CLK Clock, UINT32 Divider) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + if ((Divider < 1) || (Divider > 8)) { + return EFI_INVALID_PARAMETER; + } + + IMX_CCM_CCOSR_REG ccosrReg; ccosrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CCOSR); + + switch (Clock) { + case IMX_OSC_CLK: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_OSC_CLK; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_PLL2_MAIN_CLK: + ccosrReg.CLKO1_SEL =3D IMX_CCM_CLKO1_SEL_PLL2_MAIN_CLK_2; + ccosrReg.CLKO1_DIV =3D Divider - 1; + ccosrReg.CLKO1_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO1; + break; + + case IMX_AXI_CLK_ROOT: + ccosrReg.CLKO1_SEL =3D IMX_CCM_CLKO1_SEL_AXI_CLK_ROOT; + ccosrReg.CLKO1_DIV =3D Divider - 1; + ccosrReg.CLKO1_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO1; + break; + + case IMX_IPG_CLK_ROOT: + ccosrReg.CLKO1_SEL =3D IMX_CCM_CLKO1_SEL_IPG_CLK_ROOT; + ccosrReg.CLKO1_DIV =3D Divider - 1; + ccosrReg.CLKO1_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO1; + break; + + case IMX_GPU2D_AXI_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_GPU2D_AXI_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_GPU3D_AXI_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_GPU3D_AXI_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_GPU2D_CORE_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_GPU2D_CORE_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_GPU3D_CORE_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_GPU3D_CORE_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_GPU3D_SHADER_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_GPU3D_SHADER_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_UART_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_UART_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + default: + return EFI_UNSUPPORTED; + } + + MmioWrite32 ((UINTN) &ccmRegisters->CCOSR, ccosrReg.AsUint32); + + return EFI_SUCCESS; +} + +VOID ImxClkPwrClkOut1Disable () +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CCOSR_REG ccosrReg; ccosrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CCOSR); + + ccosrReg.CLKO1_EN =3D 0; + ccosrReg.CLKO2_EN =3D 0; + + MmioWrite32 ((UINTN) &ccmRegisters->CCOSR, ccosrReg.AsUint32); +} + +EFI_STATUS ImxClkPwrValidateClocks () +{ + struct { + IMX_CLK Clock; + IMX_CLOCK_INFO Info; + } expectedClocks[] =3D { + // Clock, Frequency, Parent + {IMX_OSC_CLK, {24000000, IMX_CLK_NONE}}, + {IMX_PLL1_MAIN_CLK, {792000000, IMX_OSC_CLK}}, + {IMX_PLL2_MAIN_CLK, {528000000, IMX_OSC_CLK}}, + {IMX_PLL2_PFD0, {352000000, IMX_PLL2_MAIN_CLK}}, + {IMX_PLL2_PFD1,{594000000, IMX_PLL2_MAIN_CLK}}, + {IMX_PLL2_PFD2, {396000000, IMX_PLL2_MAIN_CLK}}, + {IMX_PLL3_MAIN_CLK, {480000000, IMX_OSC_CLK}}, + {IMX_PLL3_PFD0, {720000000, IMX_PLL3_MAIN_CLK}}, + {IMX_PLL3_PFD1, {540000000, IMX_PLL3_MAIN_CLK}}, + {IMX_PLL3_PFD2, {508235294, IMX_PLL3_MAIN_CLK}}, + {IMX_PLL3_PFD3, {454736842, IMX_PLL3_MAIN_CLK}}, + {IMX_AXI_CLK_ROOT, {264000000, IMX_PERIPH_CLK}}, + {IMX_MMDC_CH0_CLK_ROOT, {528000000, IMX_PERIPH_CLK}}, + }; + + BOOLEAN invalid =3D FALSE; + + int i; + for (i =3D 0; i < ARRAYSIZE (expectedClocks); ++i) { + DEBUG (( + DEBUG_INFO, + "Validating clock %s. Expecting: Frequency =3D %d (%d Mhz), Parent = =3D %s\r\n", + StringFromImxClk (expectedClocks[i].Clock), + expectedClocks[i].Info.Frequency, + expectedClocks[i].Info.Frequency / 1000000, + StringFromImxClk (expectedClocks[i].Info.Parent) + )); + + IMX_CLOCK_INFO actualInfo; + EFI_STATUS status =3D ImxClkPwrGetClockInfo ( + expectedClocks[i].Clock, + &actualInfo); + + if (EFI_ERROR (status)) { + DEBUG (( + DEBUG_ERROR, + "Failed to get clock info. (Clock =3D %s, status =3D 0x%x)\r\n", + StringFromImxClk (expectedClocks[i].Clock), + status + )); + + return status; + } + + if ((actualInfo.Frequency !=3D expectedClocks[i].Info.Frequency) || + (actualInfo.Parent !=3D expectedClocks[i].Info.Parent)) { + + DEBUG (( + DEBUG_ERROR, + "Clock settings do not match expected! Clock =3D %s (Expected, A= ctual) " + "Frequency: %d, %d. Parent: %s, %s\r\n", + StringFromImxClk (expectedClocks[i].Clock), + expectedClocks[i].Info.Frequency, + actualInfo.Frequency, + StringFromImxClk (expectedClocks[i].Info.Parent), + StringFromImxClk (actualInfo.Parent) + )); + + invalid =3D TRUE; + } + } + + return invalid ? EFI_DEVICE_ERROR : EFI_SUCCESS; +} diff --git a/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6SDLClkPwr.inc b/= Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6SDLClkPwr.inc new file mode 100644 index 000000000000..0d8df235e89e --- /dev/null +++ b/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6SDLClkPwr.inc @@ -0,0 +1,1231 @@ +/** @file +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* This program and the accompanying materials +* are licensed and made available under the terms and conditions of the B= SD 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 IM= PLIED. +* +**/ + +#ifndef CPU_IMX6SDL +#error iMX6SDLClkPwr.inc should not be compiled for non iMX6 SDL platform. +#endif + +/** + Get the CCGR register index and gate number for a clock gate. +**/ + +IMX_CCGR_INDEX ImxpCcgrIndexFromClkGate (IMX_CLK_GATE ClockGate) +{ + static const IMX_CCGR_INDEX ImxpCcgrIndexMap[] =3D { + {0, 0}, // MX6_AIPS_TZ1_CLK_ENABLE + {0, 1}, // MX6_AIPS_TZ2_CLK_ENABLE + {0, 2}, // MX6_APBHDMA_HCLK_ENABLE + {0, 3}, // MX6_ASRC_CLK_ENABLE + {0, 4}, // MX6_CAAM_SECURE_MEM_CLK_ENABLE + {0, 5}, // MX6_CAAM_WRAPPER_ACLK_ENABLE + {0, 6}, // MX6_CAAM_WRAPPER_IPG_ENABLE + {0, 7}, // MX6_CAN1_CLK_ENABLE + {0, 8}, // MX6_CAN1_SERIAL_CLK_ENABLE + {0, 9}, // MX6_CAN2_CLK_ENABLE + {0, 10}, // MX6_CAN2_SERIAL_CLK_ENABLE + {0, 11}, // MX6_ARM_DBG_CLK_ENABLE + {0, 12}, // MX6_DCIC1_CLK_ENABLE + {0, 13}, // MX6_DCIC2_CLK_ENABLE + {0, 14}, // MX6_DTCP_CLK_ENABLE + {1, 0}, // MX6_ECSPI1_CLK_ENABLE + {1, 1}, // MX6_ECSPI2_CLK_ENABLE + {1, 2}, // MX6_ECSPI3_CLK_ENABLE + {1, 3}, // MX6_ECSPI4_CLK_ENABLE + {1, 4}, // MX6_ECSPI5_CLK_ENABLE + {1, 5}, // MX6_ENET_CLK_ENABLE + {1, 6}, // MX6_EPIT1_CLK_ENABLE + {1, 7}, // MX6_EPIT2_CLK_ENABLE + {1, 8}, // MX6_ESAI_CLK_ENABLE + {1, 10}, // MX6_GPT_CLK_ENABLE + {1, 11}, // MX6_GPT_SERIAL_CLK_ENABLE + {1, 12}, // MX6_GPU2D_CLK_ENABLE + {1, 13}, // MX6_GPU3D_CLK_ENABLE + {2, 0}, // MX6_HDMI_TX_ENABLE + {2, 2}, // MX6_HDMI_TX_ISFRCLK_ENABLE + {2, 3}, // MX6_I2C1_SERIAL_CLK_ENABLE + {2, 4}, // MX6_I2C2_SERIAL_CLK_ENABLE + {2, 5}, // MX6_I2C3_SERIAL_CLK_ENABLE + {2, 6}, // MX6_IIM_CLK_ENABLE + {2, 7}, // MX6_IOMUX_IPT_CLK_IO_ENABLE + {2, 8}, // MX6_IPMUX1_CLK_ENABLE + {2, 9}, // MX6_IPMUX2_CLK_ENABLE + {2, 10}, // MX6_IPMUX3_CLK_ENABLE + {2, 11}, // MX6_IPSYNC_IP2APB_TZASC1_IPG_MASTER_CLK_ENABLE + {2, 12}, // MX6_IPSYNC_IP2APB_TZASC2_IPG_MASTER_CLK_ENABLE + {2, 13}, // MX6_IPSYNC_VDOA_IPG_MASTER_CLK_ENABLE + {3, 0}, // MX6_IPU1_IPU_CLK_ENABLE + {3, 1}, // MX6_IPU1_IPU_DI0_CLK_ENABLE + {3, 2}, // MX6_IPU1_IPU_DI1_CLK_ENABLE + {3, 3}, // MX6_IPU2_IPU_CLK_ENABLE + {3, 4}, // MX6_IPU2_IPU_DI0_CLK_ENABLE + {3, 5}, // MX6_IPU2_IPU_DI1_CLK_ENABLE + {3, 6}, // MX6_LDB_DI0_CLK_ENABLE + {3, 7}, // MX6_LDB_DI1_CLK_ENABLE + {3, 8}, // MX6_MIPI_CORE_CFG_CLK_ENABLE + {3, 9}, // MX6_MLB_CLK_ENABLE + {3, 10}, // MX6_MMDC_CORE_ACLK_FAST_CORE_P0_ENABLE + {3, 12}, // MX6_MMDC_CORE_IPG_CLK_P0_ENABLE + {3, 14}, // MX6_OCRAM_CLK_ENABLE + {3, 15}, // MX6_OPENVGAXICLK_CLK_ROOT_ENABLE + {4, 0}, // MX6_PCIE_ROOT_ENABLE + {4, 4}, // MX6_PL301_MX6QFAST1_S133CLK_ENABLE + {4, 6}, // MX6_PL301_MX6QPER1_BCHCLK_ENABLE + {4, 7}, // MX6_PL301_MX6QPER2_MAINCLK_ENABLE + {4, 8}, // MX6_PWM1_CLK_ENABLE + {4, 9}, // MX6_PWM2_CLK_ENABLE + {4, 10}, // MX6_PWM3_CLK_ENABLE + {4, 11}, // MX6_PWM4_CLK_ENABLE + {4, 12}, // MX6_RAWNAND_U_BCH_INPUT_APB_CLK_ENABLE + {4, 13}, // MX6_RAWNAND_U_GPMI_BCH_INPUT_BCH_CLK_ENABLE + {4, 14}, // MX6_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_CLK_ENABLE + {4, 15}, // MX6_RAWNAND_U_GPMI_INPUT_APB_CLK_ENABLE + {5, 0}, // MX6_ROM_CLK_ENABLE + {5, 2}, // MX6_SATA_CLK_ENABLE + {5, 3}, // MX6_SDMA_CLK_ENABLE + {5, 6}, // MX6_SPBA_CLK_ENABLE + {5, 7}, // MX6_SPDIF_CLK_ENABLE + {5, 9}, // MX6_SSI1_CLK_ENABLE + {5, 10}, // MX6_SSI2_CLK_ENABLE + {5, 11}, // MX6_SSI3_CLK_ENABLE + {5, 12}, // MX6_UART_CLK_ENABLE + {5, 13}, // MX6_UART_SERIAL_CLK_ENABLE + {6, 0}, // MX6_USBOH3_CLK_ENABLE + {6, 1}, // MX6_USDHC1_CLK_ENABLE + {6, 2}, // MX6_USDHC2_CLK_ENABLE + {6, 3}, // MX6_USDHC3_CLK_ENABLE + {6, 4}, // MX6_USDHC4_CLK_ENABLE + {6, 5}, // MX6_EIM_SLOW_CLK_ENABLE + {6, 6}, // MX6_VDOAXICLK_CLK_ENABLE + {6, 7}, // MX6_VPU_CLK_ENABLE + }; + + return ImxpCcgrIndexMap[ClockGate]; +} + +CONST CHAR16 *StringFromImxClk (IMX_CLK Value) +{ + switch (Value) { + case IMX_CLK_NONE: return L"(none)"; + case IMX_OSC_CLK: return L"OSC_CLK"; + case IMX_PLL1_MAIN_CLK: return L"PLL1_MAIN_CLK"; + case IMX_PLL2_MAIN_CLK: return L"PLL2_MAIN_CLK"; + case IMX_PLL2_PFD0: return L"PLL2_PFD0"; + case IMX_PLL2_PFD1: return L"PLL2_PFD1"; + case IMX_PLL2_PFD2: return L"PLL2_PFD2"; + case IMX_PLL3_MAIN_CLK: return L"PLL3_MAIN_CLK"; + case IMX_PLL3_PFD0: return L"PLL3_PFD0"; + case IMX_PLL3_PFD1: return L"PLL3_PFD1"; + case IMX_PLL3_PFD2: return L"PLL3_PFD2"; + case IMX_PLL3_PFD3: return L"PLL3_PFD3"; + case IMX_PLL4_MAIN_CLK: return L"PLL4_MAIN_CLK"; + case IMX_PLL5_MAIN_CLK: return L"PLL5_MAIN_CLK"; + case IMX_CLK1: return L"CLK1"; + case IMX_CLK2: return L"CLK2"; + case IMX_PLL1_SW_CLK: return L"PLL1_SW_CLK"; + case IMX_STEP_CLK: return L"STEP_CLK"; + case IMX_PLL3_SW_CLK: return L"PLL3_SW_CLK"; + case IMX_AXI_ALT: return L"AXI_ALT"; + case IMX_AXI_CLK_ROOT: return L"AXI_CLK_ROOT"; + case IMX_PERIPH_CLK2: return L"PERIPH_CLK2"; + case IMX_PERIPH_CLK: return L"PERIPH_CLK"; + case IMX_PRE_PERIPH_CLK: return L"PRE_PERIPH_CLK"; + case IMX_PRE_PERIPH2_CLK: return L"PRE_PERIPH2_CLK"; + case IMX_PERIPH2_CLK: return L"PERIPH2_CLK"; + case IMX_ARM_CLK_ROOT: return L"ARM_CLK_ROOT"; + case IMX_MMDC_CH0_CLK_ROOT: return L"MMDC_CH0_CLK_ROOT"; + case IMX_MMDC_CH1_CLK_ROOT: return L"MMDC_CH1_CLK_ROOT"; + case IMX_AHB_CLK_ROOT: return L"AHB_CLK_ROOT"; + case IMX_IPG_CLK_ROOT: return L"IPG_CLK_ROOT"; + case IMX_PERCLK_CLK_ROOT: return L"PERCLK_CLK_ROOT"; + case IMX_USDHC1_CLK_ROOT: return L"USDHC1_CLK_ROOT"; + case IMX_USDHC2_CLK_ROOT: return L"USDHC2_CLK_ROOT"; + case IMX_USDHC3_CLK_ROOT: return L"USDHC3_CLK_ROOT"; + case IMX_USDHC4_CLK_ROOT: return L"USDHC4_CLK_ROOT"; + case IMX_SSI1_CLK_ROOT: return L"SSI1_CLK_ROOT"; + case IMX_SSI2_CLK_ROOT: return L"SSI2_CLK_ROOT"; + case IMX_SSI3_CLK_ROOT: return L"SSI3_CLK_ROOT"; + case IMX_GPU2D_AXI_CLK_ROOT: return L"GPU2D_AXI_CLK_ROOT"; + case IMX_GPU3D_AXI_CLK_ROOT: return L"GPU3D_AXI_CLK_ROOT"; + case IMX_PCIE_AXI_CLK_ROOT: return L"PCIE_AXI_CLK_ROOT"; + case IMX_VDO_AXI_CLK_ROOT: return L"VDO_AXI_CLK_ROOT"; + case IMX_IPU1_HSP_CLK_ROOT: return L"IPU1_HSP_CLK_ROOT"; + case IMX_GPU2D_CORE_CLK_ROOT: return L"GPU2D_CORE_CLK_ROOT"; + case IMX_ACLK_EIM_SLOW_CLK_ROOT: return L"ACLK_EIM_SLOW_CLK_ROOT"; + case IMX_ACLK_CLK_ROOT: return L"ACLK_CLK_ROOT"; + case IMX_ENFC_CLK_ROOT: return L"ENFC_CLK_ROOT"; + case IMX_GPU3D_CORE_CLK_ROOT: return L"GPU3D_CORE_CLK_ROOT"; + case IMX_GPU3D_SHADER_CLK_ROOT: return L"GPU3D_SHADER_CLK_ROOT"; + case IMX_VPU_AXI_CLK_ROOT: return L"VPU_AXI_CLK_ROOT"; + case IMX_IPU1_DI0_CLK_ROOT: return L"IPU1_DI0_CLK_ROOT"; + case IMX_IPU1_DI1_CLK_ROOT: return L"IPU1_DI1_CLK_ROOT"; + case IMX_LDB_DI0_SERIAL_CLK_ROOT: return L"LDB_DI0_SERIAL_CLK_ROOT"; + case IMX_LDB_DI0_IPU: return L"LDB_DI0_IPU"; + case IMX_LDB_DI1_SERIAL_CLK_ROOT: return L"LDB_DI1_SERIAL_CLK_ROOT"; + case IMX_LDB_DI1_IPU: return L"LDB_DI1_IPU"; + case IMX_SPDIF0_CLK_ROOT: return L"SPDIF0_CLK_ROOT"; + case IMX_SPDIF1_CLK_ROOT: return L"SPDIF1_CLK_ROOT"; + case IMX_ESAI_CLK_ROOT: return L"ESAI_CLK_ROOT"; + case IMX_HSI_TX_CLK_ROOT: return L"HSI_TX_CLK_ROOT"; + case IMX_CAN_CLK_ROOT: return L"CAN_CLK_ROOT"; + case IMX_ECSPI_CLK_ROOT: return L"ECSPI_CLK_ROOT"; + case IMX_UART_CLK_ROOT: return L"UART_CLK_ROOT"; + case IMX_VIDEO_27M_CLK_ROOT: return L"VIDEO_27M_CLK_ROOT"; + default: + ASSERT (FALSE); + return L"[invalid IMX_CLK value]"; + } +} + +IMX_CLK ImxpClkFromBypassClkSource (IMX_PLL_BYPASS_CLK_SRC BypassClockSour= ce) +{ + switch (BypassClockSource) { + case IMX_PLL_BYPASS_CLK_SRC_REF_CLK_24M: + return IMX_OSC_CLK; + case IMX_PLL_BYPASS_CLK_SRC_CLK1: + return IMX_CLK1; + case IMX_PLL_BYPASS_CLK_SRC_CLK2: + return IMX_CLK2; + case IMX_PLL_BYPASS_CLK_SRC_XOR: + default: + ASSERT (FALSE); + return IMX_CLK_NONE; + } +} + +/** + Configure the GPU clock tree so that GPU2D and GPU3D are clocked from + the AXI clock root and are within the allowed frequency range. + + The GPU must be powered down, and GPU clocks must be gated when this + function is called. +**/ +VOID ImxCcmConfigureGpuClockTree () +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + cbcmrReg.gpu2d_axi_clk_sel =3D IMX_CCM_GPU2D_AXI_CLK_SEL_AXI; + cbcmrReg.gpu3d_axi_clk_sel =3D IMX_CCM_GPU3D_AXI_CLK_SEL_AXI; + + cbcmrReg.gpu2d_core_clk_sel =3D IMX_CCM_GPU2D_CORE_CLK_SEL_PLL2_PFD0; + cbcmrReg.gpu3d_core_clk_sel =3D IMX_CCM_GPU3D_CORE_CLK_SEL_MMDC_CH0_AXI; + cbcmrReg.gpu3d_shader_clk_sel =3D IMX_CCM_GPU3D_SHADER_CLK_SEL_MMDC_CH0_= AXI; + + cbcmrReg.gpu2d_core_clk_podf =3D 0; + cbcmrReg.gpu3d_core_podf =3D 0; + cbcmrReg.gpu3d_shader_podf =3D 0; + + ImxpClkPwrCacheReset (); + MmioWrite32 ((UINTN) &ccmRegisters->CBCMR, cbcmrReg.AsUint32); +} + +/** + Configure all of DIx clock tree for IPU1. For flexibility + purpose use PLL5 (PLL Video) as main reference clock. PLL 5 has flexib= le + divider making it easily configurable. Muxing and clock programming ne= eds + when to be updated when supporting multiple display. +**/ +VOID ImxCcmConfigureIPUDIxClockTree () +{ + volatile IMX_CCM_REGISTERS* ccmRegisters =3D (IMX_CCM_REGISTERS*) IMX_= CCM_BASE; + IMX_CCM_CHSCCDR_REG chscddrReg; + + chscddrReg.AsUint32 =3D MmioRead32((UINTN)&ccmRegisters->CHSCCDR); = // CCM HSC Clock Divider Register + + // Setup muxing to pre-mux + chscddrReg.ipu1_di0_clk_sel =3D IMX_CHSCCDR_IPU1_DI0_CLK_SEL_PREMUX; = // 0 - derive clock from mmdc_ch0 clock + chscddrReg.ipu1_di1_clk_sel =3D IMX_CHSCCDR_IPU1_DI1_CLK_SEL_PREMUX; = // 0 - derive clock from divided pre-muxed ipu1 di1 clock + chscddrReg.ipu1_di0_podf =3D IMX_CHSCCDR_IPU1_DI0_PODF_DIV_1; = // 0 - divide by 1 + chscddrReg.ipu1_di1_podf =3D IMX_CHSCCDR_IPU1_DI1_PODF_DIV_1; = // 0 - divide by 1 + chscddrReg.ipu1_di0_pre_clk_sel =3D IMX_CHSCCDR_IPU1_DI0_PRE_CLK_SEL_P= LL5; // 2 - derive clock from pll5 + chscddrReg.ipu1_di1_pre_clk_sel =3D IMX_CHSCCDR_IPU1_DI1_PRE_CLK_SEL_P= LL5; // 2 - derive clock from pll5 + + MmioWrite32 ((UINTN)&ccmRegisters->CHSCCDR, chscddrReg.AsUint32); +} + +/** + Configure PLL 5 clock rate to the desired clock rate +**/ +VOID ImxSetClockRatePLL5 ( + UINT32 ClockRate, + IMX_CCM_PLL_VIDEO_CTRL_POST_DIV_SELECT PostDivSelect + ) +{ + // Use clock rate as denom for simple fractional calculation + UINT32 denom =3D IMX_REF_CLK_24M_FREQ; + UINT32 divSelect =3D ClockRate / IMX_REF_CLK_24M_FREQ; // Signed value + UINT32 numerator =3D ClockRate % IMX_REF_CLK_24M_FREQ; + volatile IMX_CCM_ANALOG_REGISTERS *ccmAnalogRegisters =3D + (IMX_CCM_ANALOG_REGISTERS *)IMX_CCM_ANALOG_BASE; + IMX_CCM_PLL_VIDEO_CTRL_REG pllVideoCtrlReg; pllVideoCtrlReg.AsUint32 = =3D + MmioRead32 ((UINTN)&ccmAnalogRegisters->PLL_VIDEO); + + ASSERT (numerator < denom); + ASSERT ((divSelect >=3D 27) && (divSelect <=3D 54)); + + // PLL output frequency =3D Fref * (DIV_SELECT + NUM / DENOM) + // Use the clock rate as denomitor to make fractional calulation simpl= e + pllVideoCtrlReg.DIV_SELECT =3D divSelect; + pllVideoCtrlReg.POST_DIV_SELECT =3D PostDivSelect; + + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO, pllVideoCtrlReg.AsUint32); + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO_NUM, numerator); + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO_DENOM, denom); + + pllVideoCtrlReg.AsUint32 =3D MmioRead32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO); + + // Check to see if pll is locked, if not attempt to enable it + if (pllVideoCtrlReg.LOCK =3D=3D 0) { + UINT32 counter =3D 10000; + { + IMX_CCM_PLL_VIDEO_CTRL_REG pllVideoCtrlClearReg =3D { 0 }; + pllVideoCtrlClearReg.POWERDOWN =3D 1; + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO_CLR, + pllVideoCtrlClearReg.AsUint32); + } + pllVideoCtrlReg.AsUint32 =3D MmioRead32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO); + { + IMX_CCM_PLL_VIDEO_CTRL_REG pllVideoCtrlSetReg =3D { 0 }; + pllVideoCtrlSetReg.ENABLE =3D 1; + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO_SET, + pllVideoCtrlSetReg.AsUint32); + } + pllVideoCtrlReg.AsUint32 =3D MmioRead32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO); + { + IMX_CCM_PLL_VIDEO_CTRL_REG pllVideoCtrlClearReg =3D { 0 }; + pllVideoCtrlClearReg.BYPASS =3D 1; + MmioWrite32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO_CLR, + pllVideoCtrlClearReg.AsUint32); + } + pllVideoCtrlReg.AsUint32 =3D MmioRead32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO); + do { + pllVideoCtrlReg.AsUint32 =3D MmioRead32( + (UINTN)&ccmAnalogRegisters->PLL_VIDEO); + --counter; + } while ((pllVideoCtrlReg.LOCK =3D=3D 0) && (counter > 0)); + ASSERT (counter > 0); + } +} + +EFI_STATUS +ImxpGetPll2PfdClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + IMX_PLL_PFD PfdIndex, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_ANALOG_REGISTERS *ccmAnalogRegisters =3D + (IMX_CCM_ANALOG_REGISTERS *) IMX_CCM_ANALOG_BASE; + + IMX_CCM_PFD_528_REG pfd528Reg; pfd528Reg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmAnalogRegisters->PFD_528); + + UINT32 pfdFrac; + switch (PfdIndex) { + case IMX_PLL_PFD0: + pfdFrac =3D pfd528Reg.PFD0_FRAC; + break; + case IMX_PLL_PFD1: + pfdFrac =3D pfd528Reg.PFD1_FRAC; + break; + case IMX_PLL_PFD2: + pfdFrac =3D pfd528Reg.PFD2_FRAC; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, IMX_PLL2_MAIN_CLK, &paren= tInfo); + if (EFI_ERROR (status)) { + return status; + } + + // The resulting frequency shall be 528*18/PFDn_FRAC + // where PFD0_FRAC is in the range 12-35. + ASSERT ((pfdFrac >=3D 12) && (pfdFrac <=3D 35)); + ClockInfo->Frequency =3D (UINT32) ((UINT64) parentInfo.Frequency * 18 / = pfdFrac); + ClockInfo->Parent =3D IMX_PLL2_MAIN_CLK; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetAxiClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + IMX_CLK parent; + if (cbcdrReg.axi_sel =3D=3D IMX_CCM_AXI_SEL_PERIPH_CLK) { + parent =3D IMX_PERIPH_CLK; + } else { + ASSERT (cbcdrReg.axi_sel =3D=3D IMX_CCM_AXI_SEL_AXI_ALT); + parent =3D IMX_AXI_ALT; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cbcdrReg.axi_podf); + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetGpu2dCoreClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + IMX_CLK parent; + switch (cbcmrReg.gpu2d_core_clk_sel) { + case IMX_CCM_GPU2D_CORE_CLK_SEL_AXI: + parent =3D IMX_AXI_CLK_ROOT; + break; + case IMX_CCM_GPU2D_CORE_CLK_SEL_PLL3_SW: + parent =3D IMX_PLL3_SW_CLK; + break; + case IMX_CCM_GPU2D_CORE_CLK_SEL_PLL2_PFD0: + parent =3D IMX_PLL2_PFD0; + break; + case IMX_CCM_GPU2D_CORE_CLK_SEL_PLL2_PFD2: + parent =3D IMX_PLL2_PFD2; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D + parentInfo.Frequency / (1 + cbcmrReg.gpu2d_core_clk_podf); + ClockInfo->Parent =3D parent; + + if (ClockInfo->Frequency > IMX_GPU2D_CORE_CLK_MAX) { + DEBUG (( + DEBUG_WARN, + "GPU2D_CORE_CLK exceeds maximum. (Value =3D %d, Max =3D %d)\r\n", + ClockInfo->Frequency, + IMX_GPU2D_CORE_CLK_MAX)); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetGpu3dCoreClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + IMX_CLK parent; + switch (cbcmrReg.gpu3d_core_clk_sel) { + case IMX_CCM_GPU3D_CORE_CLK_SEL_MMDC_CH0_AXI: + parent =3D IMX_MMDC_CH0_CLK_ROOT; + break; + case IMX_CCM_GPU3D_CORE_CLK_SEL_PLL3_SW: + parent =3D IMX_PLL3_SW_CLK; + break; + case IMX_CCM_GPU3D_CORE_CLK_SEL_PLL2_PFD1: + parent =3D IMX_PLL2_PFD1; + break; + case IMX_CCM_GPU3D_CORE_CLK_SEL_PLL2_PFD2: + parent =3D IMX_PLL2_PFD2; + break; + default: + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cbcmrReg.gpu3d_core= _podf); + ClockInfo->Parent =3D parent; + + if (ClockInfo->Frequency > IMX_GPU3D_CORE_CLK_MAX) { + DEBUG (( + DEBUG_WARN, + "GPU3D_CORE_CLK exceeds maximum. (Value =3D %d, Max =3D %d)\r\n", + ClockInfo->Frequency, + IMX_GPU3D_CORE_CLK_MAX)); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetGpu3dShaderClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + IMX_CLK parent; + switch (cbcmrReg.gpu3d_shader_clk_sel) { + case IMX_CCM_GPU3D_SHADER_CLK_SEL_MMDC_CH0_AXI: + parent =3D IMX_MMDC_CH0_CLK_ROOT; + break; + case IMX_CCM_GPU3D_SHADER_CLK_SEL_PLL3_SW: + parent =3D IMX_PLL3_SW_CLK; + break; + case IMX_CCM_GPU3D_SHADER_CLK_SEL_PLL2_PFD1: + parent =3D IMX_PLL2_PFD1; + break; + case IMX_CCM_GPU3D_SHADER_CLK_SEL_PLL3_PFD0: + parent =3D IMX_PLL3_PFD0; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D + parentInfo.Frequency / (1 + cbcmrReg.gpu3d_shader_podf); + + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetPeriphClk2Info ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg; cbcmrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCMR); + + IMX_CLK parent; + switch (cbcmrReg.periph_clk2_sel) { + case IMX_CCM_PERIPH_CLK2_SEL_PLL3_SW_CLK: + parent =3D IMX_PLL3_SW_CLK; + break; + case IMX_CCM_PERIPH_CLK2_SEL_OSC_CLK: + parent =3D IMX_OSC_CLK; + break; + case IMX_CCM_PERIPH_CLK2_SEL_PLL2: + parent =3D IMX_PLL2_MAIN_CLK; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cbcdrReg.periph_clk= 2_podf); + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetPeriphClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + IMX_CLK parent; + + // NOTE: periph_clk_sel is OR'd with PLL_bypass_en2 (from jtag) to + // produce the input value to the MUX. We assume PLL_bypass_en2 is= 0. + if (cbcdrReg.periph_clk_sel =3D=3D 0) { + parent =3D IMX_PRE_PERIPH_CLK; + } else { + ASSERT (cbcdrReg.periph_clk_sel =3D=3D 1); + parent =3D IMX_PERIPH_CLK2; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cbcdrReg.mmdc_ch0_a= xi_podf); + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetMmdcCh0ClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, IMX_PERIPH_CLK, &parentIn= fo); + if (EFI_ERROR (status)) { + return status; + } + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + ClockInfo->Frequency =3D + parentInfo.Frequency / (1 + cbcdrReg.mmdc_ch0_axi_podf); + ClockInfo->Parent =3D IMX_PERIPH_CLK; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetGpu2dAxiClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + IMX_CLK parent; + if (cbcmrReg.gpu2d_axi_clk_sel =3D=3D IMX_CCM_GPU2D_AXI_CLK_SEL_AXI) { + parent =3D IMX_AXI_CLK_ROOT; + } else { + ASSERT (cbcmrReg.gpu2d_axi_clk_sel =3D=3D IMX_CCM_GPU2D_AXI_CLK_SEL_AH= B); + parent =3D IMX_AHB_CLK_ROOT; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency; + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetGpu3dAxiClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg ; cbcmrReg.AsUint32 =3D MmioRead32 ((UINTN) &= ccmRegisters->CBCMR); + + IMX_CLK parent; + if (cbcmrReg.gpu3d_axi_clk_sel =3D=3D IMX_CCM_GPU3D_AXI_CLK_SEL_AXI) { + parent =3D IMX_AXI_CLK_ROOT; + } else { + ASSERT (cbcmrReg.gpu3d_axi_clk_sel =3D=3D IMX_CCM_GPU3D_AXI_CLK_SEL_AH= B); + parent =3D IMX_AHB_CLK_ROOT; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency; + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +VOID ImxEnableGpuVpuPowerDomain () +{ + volatile IMX_CCM_ANALOG_REGISTERS *analogRegisters =3D + (IMX_CCM_ANALOG_REGISTERS *) IMX_CCM_ANALOG_BASE; + + volatile IMX_GPC_REGISTERS *gpcRegisters =3D (IMX_GPC_REGISTERS *) IMX_G= PC_BASE; + volatile IMX_GPC_PGC_REGISTERS *gpuPgcRegisters =3D &gpcRegisters->PGC_G= PU; + + // Configure GPC/PGC PUPSCR Register SW2ISO bits + { + IMX_GPC_PGC_PUPSCR_REG pupscrReg; pupscrReg.AsUint32 =3D + MmioRead32 ((UINTN) &gpuPgcRegisters->PUPSCR); + + pupscrReg.SW =3D IMX_GPC_PGC_PUPSCR_SW_DEFAULT; + pupscrReg.SW2ISO =3D IMX_GPC_PGC_PUPSCR_SW2ISO_DEFAULT; + + MmioWrite32 ((UINTN) &gpuPgcRegisters->PUPSCR, pupscrReg.AsUint32); + } + + // Turn on LDO_PU to 1.250V + { + IMX_PMU_REG_CORE_REG pmuCoreReg =3D {0}; + pmuCoreReg.REG1_TARG =3D 0x1f; + + MmioWrite32 ((UINTN) &analogRegisters->PMU_REG_CORE_CLR, pmuCoreReg.As= Uint32); + + pmuCoreReg.REG1_TARG =3D 22; + MmioWrite32 ((UINTN) &analogRegisters->PMU_REG_CORE_SET, pmuCoreReg.As= Uint32); + + MicroSecondDelay (100); + } + + // Assert power up request + IMX_GPC_CNTR_REG gpcCntrReg; gpcCntrReg.AsUint32 =3D + MmioRead32 ((UINTN) &gpcRegisters->CNTR); + + gpcCntrReg.gpu_vpu_pdn_req =3D 0; + gpcCntrReg.gpu_vpu_pup_req =3D 1; + + MmioWrite32 ((UINTN) &gpcRegisters->CNTR, gpcCntrReg.AsUint32); + + // Wait for power up request to complete + do { + gpcCntrReg.AsUint32 =3D MmioRead32 ((UINTN) &gpcRegisters->CNTR); + } while (gpcCntrReg.gpu_vpu_pup_req !=3D 0); +} + +VOID ImxDisableGpuVpuPowerDomain () +{ + volatile IMX_GPC_REGISTERS *gpcRegisters =3D (IMX_GPC_REGISTERS *) IMX_G= PC_BASE; + volatile IMX_GPC_PGC_REGISTERS *gpuPgcRegisters =3D &gpcRegisters->PGC_G= PU; + + // Configure GPC/PGC PDNSCR Register ISO bits + { + IMX_GPC_PGC_PDNSCR_REG pdnscrReg; pdnscrReg.AsUint32 =3D + MmioRead32 ((UINTN) &gpuPgcRegisters->PDNSCR); + + pdnscrReg.ISO =3D IMX_GPC_PGC_PDNSCR_ISO_DEFAULT; + pdnscrReg.ISO2SW =3D IMX_GPC_PGC_PDNSCR_ISO2SW_DEFAULT; + + MmioWrite32 ((UINTN) &gpuPgcRegisters->PDNSCR, pdnscrReg.AsUint32); + } + + // Configure GPC/PGC CTRL[PCR] bit to allow power down of the blocks + { + IMX_GPC_PGC_PGCR_REG ctrlReg; ctrlReg.AsUint32 =3D + MmioRead32 ((UINTN) &gpuPgcRegisters->CTRL); + + ctrlReg.PCR =3D 1; // enable powering down of the blocks + + MmioWrite32 ((UINTN) &gpuPgcRegisters->CTRL, ctrlReg.AsUint32); + } + + // Assert power down request + { + IMX_GPC_CNTR_REG gpcCntrReg; gpcCntrReg.AsUint32 =3D + MmioRead32 ((UINTN) &gpcRegisters->CNTR); + + gpcCntrReg.gpu_vpu_pdn_req =3D 1; + gpcCntrReg.gpu_vpu_pup_req =3D 0; + + MmioWrite32 ((UINTN) &gpcRegisters->CNTR, gpcCntrReg.AsUint32); + + // Wait for power down request to complete + do { + gpcCntrReg.AsUint32 =3D MmioRead32 ((UINTN) &gpcRegisters->CNTR); + } while (gpcCntrReg.gpu_vpu_pdn_req !=3D 0); + } +} + +EFI_STATUS +ImxpGetClockInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + IN IMX_CLK ClockId, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + ASSERT (ClockId < ARRAYSIZE(Cache->Table)); + + // First try to satisfy from cache + { + UINTN cacheValidBits =3D Cache->Valid[ClockId / _BITS_PER_UINTN]; + if (cacheValidBits & (1 << (ClockId % _BITS_PER_UINTN))) { + *ClockInfo =3D Cache->Table[ClockId]; + return EFI_SUCCESS; + } + } + + EFI_STATUS status; + switch (ClockId) { + case IMX_OSC_CLK: + ImxpGetOsc24ClkInfo (ClockInfo); + status =3D EFI_SUCCESS; + break; + case IMX_PLL1_MAIN_CLK: + status =3D ImxpGetPll1MainClkInfo (Cache, ClockInfo); + break; + case IMX_PLL2_MAIN_CLK: + status =3D ImxpGetPll2MainClkInfo (Cache, ClockInfo); + break; + case IMX_PLL2_PFD0: + status =3D ImxpGetPll2PfdClkInfo (Cache, IMX_PLL_PFD0, ClockInfo); + break; + case IMX_PLL2_PFD1: + status =3D ImxpGetPll2PfdClkInfo (Cache, IMX_PLL_PFD1, ClockInfo); + break; + case IMX_PLL2_PFD2: + status =3D ImxpGetPll2PfdClkInfo (Cache, IMX_PLL_PFD2, ClockInfo); + break; + case IMX_PLL3_MAIN_CLK: + status =3D ImxpGetPll3MainClkInfo (Cache, ClockInfo); + break; + case IMX_PLL3_PFD0: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD0, ClockInfo); + break; + case IMX_PLL3_PFD1: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD1, ClockInfo); + break; + case IMX_PLL3_PFD2: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD2, ClockInfo); + break; + case IMX_PLL3_PFD3: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD3, ClockInfo); + break; + case IMX_PLL3_SW_CLK: + status =3D ImxpGetPll3SwClkInfo (Cache, ClockInfo); + break; + case IMX_AXI_CLK_ROOT: + status =3D ImxpGetAxiClkRootInfo (Cache, ClockInfo); + break; + case IMX_PERIPH_CLK2: + status =3D ImxpGetPeriphClk2Info (Cache, ClockInfo); + break; + case IMX_PERIPH_CLK: + status =3D ImxpGetPeriphClkInfo (Cache, ClockInfo); + break; + case IMX_PRE_PERIPH_CLK: + status =3D ImxpGetPrePeriphClkInfo (Cache, ClockInfo); + break; + case IMX_ARM_CLK_ROOT: + status =3D ImxpGetArmClkRootInfo (Cache, ClockInfo); + break; + case IMX_MMDC_CH0_CLK_ROOT: + status =3D ImxpGetMmdcCh0ClkRootInfo (Cache, ClockInfo); + break; + case IMX_AHB_CLK_ROOT: + status =3D ImxpGetAhbClkRootInfo (Cache, ClockInfo); + break; + case IMX_IPG_CLK_ROOT: + status =3D ImxpGetIpgClkRootInfo (Cache, ClockInfo); + break; + case IMX_GPU2D_AXI_CLK_ROOT: + status =3D ImxpGetGpu2dAxiClkRootInfo (Cache, ClockInfo); + break; + case IMX_GPU3D_AXI_CLK_ROOT: + status =3D ImxpGetGpu3dAxiClkRootInfo (Cache, ClockInfo); + break; + case IMX_GPU2D_CORE_CLK_ROOT: + status =3D ImxpGetGpu2dCoreClkInfo (Cache, ClockInfo); + break; + case IMX_GPU3D_CORE_CLK_ROOT: + status =3D ImxpGetGpu3dCoreClkInfo (Cache, ClockInfo); + break; + case IMX_GPU3D_SHADER_CLK_ROOT: + status =3D ImxpGetGpu3dShaderClkInfo (Cache, ClockInfo); + break; + default: + return EFI_UNSUPPORTED; + } + + if (EFI_ERROR (status)) { + return status; + } + + // Update the cache + Cache->Table[ClockId] =3D *ClockInfo; + Cache->Valid[ClockId / _BITS_PER_UINTN] |=3D (1 << (ClockId % _BITS_PER_= UINTN)); + + return EFI_SUCCESS; +} + +/** + Power on and clock the GPU2D/GPU3D blocks. + + Follow the datasheet recommended sequence for clocking and powering: + Gate clocks -> unpower module -> + configure muxes/dividers -> power module -> Ungate clocks +**/ +EFI_STATUS ImxClkPwrGpuEnable () +{ + //EFI_STATUS status; + +#if !defined(MDEPKG_NDEBUG) + + // + // Precondition: clock and power should be disabled + // + { + ASSERT (ImxClkPwrGetClockGate (IMX_GPU3D_CLK_ENABLE) =3D=3D IMX_CLOCK_= GATE_STATE_OFF); + ASSERT (ImxClkPwrGetClockGate (IMX_GPU2D_CLK_ENABLE) =3D=3D IMX_CLOCK_= GATE_STATE_OFF); + ASSERT (ImxClkPwrGetClockGate (IMX_OPENVGAXICLK_CLK_ROOT_ENABLE) =3D= =3D IMX_CLOCK_GATE_STATE_OFF); + } + +#endif + + // Ensure clocks are gated + ImxClkPwrSetClockGate (IMX_GPU3D_CLK_ENABLE, IMX_CLOCK_GATE_STATE_OFF); + ImxClkPwrSetClockGate (IMX_GPU2D_CLK_ENABLE, IMX_CLOCK_GATE_STATE_OFF); + ImxClkPwrSetClockGate (IMX_OPENVGAXICLK_CLK_ROOT_ENABLE, IMX_CLOCK_GATE_= STATE_OFF); + + // Ensure GPU powered down (GPU should be powered down anyway) + ImxDisableGpuVpuPowerDomain (); + + // Configure clock muxes and dividers for GPU3D, GPU2D, and OpenVG + ImxCcmConfigureGpuClockTree (); + + // Power on the GPU + ImxEnableGpuVpuPowerDomain (); + + // Ungate the GPU clocks + ImxClkPwrSetClockGate (IMX_GPU3D_CLK_ENABLE, IMX_CLOCK_GATE_STATE_ON); + ImxClkPwrSetClockGate (IMX_GPU2D_CLK_ENABLE, IMX_CLOCK_GATE_STATE_ON); + ImxClkPwrSetClockGate (IMX_OPENVGAXICLK_CLK_ROOT_ENABLE, IMX_CLOCK_GATE_= STATE_ON); + + return EFI_SUCCESS; +} + + +/** + Setup the clock tree for Display Interface (DI) for DuaLite and Solo + Gate clocks -> Configure mux/div -> Ungate clocks -> Setup PLL +**/ +EFI_STATUS ImxClkPwrIpuDIxEnable () +{ + ImxClkPwrSetClockGate(IMX_IPU1_DI0_CLK_ENABLE, IMX_CCM_CCGR_OFF); + ImxClkPwrSetClockGate(IMX_IPU1_DI1_CLK_ENABLE, IMX_CCM_CCGR_OFF); + + ImxCcmConfigureIPUDIxClockTree(); + + ImxClkPwrSetClockGate(IMX_IPU1_DI0_CLK_ENABLE, IMX_CCM_CCGR_ON); + + // Setup PLL to 65MHz as expected from UBOOT although transition + // might be so fast that UBOOT screen would not be displayed + ImxSetPll5ReferenceRate(65000000); + + return EFI_SUCCESS; +} + +/** + Configure PLL5 to the desired clock rate for all Display Interface (DI= ). + Currently only support one display to IPU1 DI0. +**/ +EFI_STATUS ImxSetPll5ReferenceRate ( + UINT32 ClockRate + ) +{ + BOOLEAN foundConfig =3D FALSE; + UINT32 dxPodfDivider; + UINT32 targetFreq; + UINT32 postDivSelectCount; + UINT32 postDivSelectValue[3] =3D { 1, 2, 4 }; + IMX_CCM_PLL_VIDEO_CTRL_POST_DIV_SELECT postDivSelect[3] =3D { + IMX_POST_DIV_SELECT_DIVIDE_1, + IMX_POST_DIV_SELECT_DIVIDE_2, + IMX_POST_DIV_SELECT_DIVIDE_4 }; + + for (postDivSelectCount =3D 0; + postDivSelectCount < ARRAYSIZE (postDivSelectValue); + ++postDivSelectCount) { + + for (dxPodfDivider =3D 1; dxPodfDivider < 9; ++dxPodfDivider) { + + targetFreq =3D + dxPodfDivider * + ClockRate * + postDivSelectValue[postDivSelectCount]; + + // The valid range for PPL loop divider is 27-54 so we + // need to target freq need to fit within the valid range. + if ((targetFreq >=3D PLL5_MIN_FREQ) && + (targetFreq <=3D PLL5_MAX_FREQ)) { + foundConfig =3D TRUE; + break; + } + } + + if (foundConfig =3D=3D TRUE) { + break; + } + } + + if (foundConfig =3D=3D FALSE) { + DEBUG((DEBUG_ERROR, "ClockRate %d\n", ClockRate)); + ASSERT(FALSE); + return EFI_INVALID_PARAMETER; + } + + DEBUG (( + DEBUG_INFO, + "PLL 5 setting (%d) Target Freq %d Divider %d PostDiv %d\n", + ClockRate, + targetFreq, + dxPodfDivider, + postDivSelectValue[postDivSelectCount] + )); + + { + volatile IMX_CCM_REGISTERS *ccmRegisters =3D + (IMX_CCM_REGISTERS *)IMX_CCM_BASE; + IMX_CCM_CHSCCDR_REG chscddrReg; chscddrReg.AsUint32 =3D + MmioRead32 ((UINTN)&ccmRegisters->CHSCCDR) ; + + ImxClkPwrSetClockGate(IMX_IPU1_DI0_CLK_ENABLE, IMX_CCM_CCGR_OFF); + ImxClkPwrSetClockGate(IMX_IPU1_DI1_CLK_ENABLE, IMX_CCM_CCGR_OFF); + + chscddrReg.ipu1_di0_podf =3D dxPodfDivider - 1; + chscddrReg.ipu1_di1_podf =3D dxPodfDivider - 1; + + MmioWrite32 ((UINTN)&ccmRegisters->CHSCCDR, chscddrReg.AsUint32); + + ImxClkPwrSetClockGate(IMX_IPU1_DI0_CLK_ENABLE, IMX_CCM_CCGR_ON); + } + + ImxSetClockRatePLL5(targetFreq, postDivSelect[postDivSelectCount]); + + return EFI_SUCCESS; +} + +EFI_STATUS ImxClkPwrClkOut1Enable (IMX_CLK Clock, UINT32 Divider) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + if ((Divider < 1) || (Divider > 8)) { + return EFI_INVALID_PARAMETER; + } + + IMX_CCM_CCOSR_REG ccosrReg; ccosrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CCOSR); + + switch (Clock) { + case IMX_OSC_CLK: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_OSC_CLK; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_PLL2_MAIN_CLK: + ccosrReg.CLKO1_SEL =3D IMX_CCM_CLKO1_SEL_PLL2_MAIN_CLK_2; + ccosrReg.CLKO1_DIV =3D Divider - 1; + ccosrReg.CLKO1_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO1; + break; + + case IMX_AXI_CLK_ROOT: + ccosrReg.CLKO1_SEL =3D IMX_CCM_CLKO1_SEL_AXI_CLK_ROOT; + ccosrReg.CLKO1_DIV =3D Divider - 1; + ccosrReg.CLKO1_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO1; + break; + + case IMX_IPG_CLK_ROOT: + ccosrReg.CLKO1_SEL =3D IMX_CCM_CLKO1_SEL_IPG_CLK_ROOT; + ccosrReg.CLKO1_DIV =3D Divider - 1; + ccosrReg.CLKO1_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO1; + break; + + case IMX_GPU2D_AXI_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_GPU2D_AXI_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_GPU3D_AXI_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_GPU3D_AXI_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_GPU2D_CORE_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_GPU2D_CORE_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_GPU3D_CORE_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_GPU3D_CORE_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_GPU3D_SHADER_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_GPU3D_SHADER_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + case IMX_UART_CLK_ROOT: + ccosrReg.CLKO2_SEL =3D IMX_CCM_CLKO2_SEL_UART_CLK_ROOT; + ccosrReg.CLKO2_DIV =3D Divider - 1; + ccosrReg.CLKO2_EN =3D 1; + ccosrReg.CLK_OUT_SEL =3D IMX_CCM_CLK_OUT_SEL_CCM_CLKO2; + break; + + default: + return EFI_UNSUPPORTED; + } + + MmioWrite32 ((UINTN) &ccmRegisters->CCOSR, ccosrReg.AsUint32); + + return EFI_SUCCESS; +} + +VOID ImxClkPwrClkOut1Disable () +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CCOSR_REG ccosrReg; ccosrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CCOSR); + + ccosrReg.CLKO1_EN =3D 0; + ccosrReg.CLKO2_EN =3D 0; + + MmioWrite32 ((UINTN) &ccmRegisters->CCOSR, ccosrReg.AsUint32); +} + +EFI_STATUS ImxClkPwrValidateClocks () +{ + struct { + IMX_CLK Clock; + IMX_CLOCK_INFO Info; + } expectedClocks[] =3D { + // Clock, Frequency, Parent + {IMX_OSC_CLK, {24000000, IMX_CLK_NONE}}, + {IMX_PLL1_MAIN_CLK, {792000000, IMX_OSC_CLK}}, + {IMX_PLL2_MAIN_CLK, {528000000, IMX_OSC_CLK}}, + {IMX_PLL2_PFD0, {306580645, IMX_PLL2_MAIN_CLK}}, + {IMX_PLL2_PFD1,{528000000, IMX_PLL2_MAIN_CLK}}, + {IMX_PLL2_PFD2, {396000000, IMX_PLL2_MAIN_CLK}}, + {IMX_PLL3_MAIN_CLK, {480000000, IMX_OSC_CLK}}, + {IMX_PLL3_PFD0, {720000000, IMX_PLL3_MAIN_CLK}}, + {IMX_PLL3_PFD1, {540000000, IMX_PLL3_MAIN_CLK}}, + {IMX_PLL3_PFD2, {508235294, IMX_PLL3_MAIN_CLK}}, + {IMX_PLL3_PFD3, {454736842, IMX_PLL3_MAIN_CLK}}, + {IMX_AXI_CLK_ROOT, {198000000, IMX_PERIPH_CLK}}, + {IMX_MMDC_CH0_CLK_ROOT, {396000000, IMX_PERIPH_CLK}}, + }; + + BOOLEAN invalid =3D FALSE; + + int i; + for (i =3D 0; i < ARRAYSIZE (expectedClocks); ++i) { + DEBUG (( + DEBUG_INFO, + "Validating clock %s. Expecting: Frequency =3D %d (%d Mhz), Parent = =3D %s\r\n", + StringFromImxClk (expectedClocks[i].Clock), + expectedClocks[i].Info.Frequency, + expectedClocks[i].Info.Frequency / 1000000, + StringFromImxClk (expectedClocks[i].Info.Parent) + )); + + IMX_CLOCK_INFO actualInfo; + EFI_STATUS status =3D ImxClkPwrGetClockInfo ( + expectedClocks[i].Clock, + &actualInfo); + + if (EFI_ERROR (status)) { + DEBUG (( + DEBUG_ERROR, + "Failed to get clock info. (Clock =3D %s, status =3D 0x%x)\r\n", + StringFromImxClk (expectedClocks[i].Clock), + status + )); + + return status; + } + + if ((actualInfo.Frequency !=3D expectedClocks[i].Info.Frequency) || + (actualInfo.Parent !=3D expectedClocks[i].Info.Parent)) { + + DEBUG (( + DEBUG_ERROR, + "SDL Clock settings do not match expected! Clock =3D %s (Expecte= d, Actual) " + "Frequency: %d, %d. Parent: %s, %s\r\n", + StringFromImxClk (expectedClocks[i].Clock), + expectedClocks[i].Info.Frequency, + actualInfo.Frequency, + StringFromImxClk (expectedClocks[i].Info.Parent), + StringFromImxClk (actualInfo.Parent) + )); + + invalid =3D TRUE; + } + } + + return invalid ? EFI_DEVICE_ERROR : EFI_SUCCESS; +} diff --git a/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6SXClkPwr.inc b/S= ilicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6SXClkPwr.inc new file mode 100644 index 000000000000..c6c6251ebccf --- /dev/null +++ b/Silicon/NXP/iMX6Pkg/Library/iMX6ClkPwrLib/iMX6SXClkPwr.inc @@ -0,0 +1,665 @@ +/** @file +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* This program and the accompanying materials +* are licensed and made available under the terms and conditions of the B= SD 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 IM= PLIED. +* +**/ + +#if !defined(CPU_IMX6SX) +#error iMX6SXClkPwr.inc should not be compiled for non iMX6 SX platform. +#endif + +/** + Get the CCGR register index and gate number for a clock gate. +**/ +IMX_CCGR_INDEX ImxpCcgrIndexFromClkGate (IMX_CLK_GATE ClockGate) +{ + static const IMX_CCGR_INDEX ImxpCcgrIndexMap[] =3D { + {0, 0}, // IMX_AIPS_TZ1_CLK_ENABLE + {0, 1}, // IMX_AIPS_TZ2_CLK_ENABLE + {0, 2}, // IMX_APBHDMA_HCLK_ENABLE + {0, 3}, // IMX_ASRC_CLK_ENABLE + {0, 4}, // IMX_CAAM_SECURE_MEM_CLK_ENABLE + {0, 5}, // IMX_CAAM_WRAPPER_ACLK_ENABLE + {0, 6}, // IMX_CAAM_WRAPPER_IPG_ENABLE + {0, 7}, // IMX_CAN1_CLK_ENABLE + {0, 8}, // IMX_CAN1_SERIAL_CLK_ENABLE + {0, 9}, // IMX_CAN2_CLK_ENABLE + {0, 10}, // IMX_CAN2_SERIAL_CLK_ENABLE + {0, 11}, // IMX_ARM_DBG_CLK_ENABLE + {0, 12}, // IMX_DCIC1_CLK_ENABLE + {0, 13}, // IMX_DCIC2_CLK_ENABLE + {0, 15}, // IMX_AIPS_TZ3_CLK_ENABLE + {1, 0}, // IMX_ECSPI1_CLK_ENABLE + {1, 1}, // IMX_ECSPI2_CLK_ENABLE + {1, 2}, // IMX_ECSPI3_CLK_ENABLE + {1, 3}, // IMX_ECSPI4_CLK_ENABLE + {1, 4}, // IMX_ECSPI5_CLK_ENABLE + {1, 6}, // IMX_EPIT1_CLK_ENABLE + {1, 7}, // IMX_EPIT2_CLK_ENABLE + {1, 8}, // IMX_ESAI_CLK_ENABLE + {1, 9}, // IMX_WAKEUP_CLK_ENABLE + {1, 10}, // IMX_GPT_CLK_ENABLE + {1, 11}, // IMX_GPT_SERIAL_CLK_ENABLE + {1, 13}, // IMX_GPU_CLK_ENABLE + {1, 14}, // IMX_OCRAM_S_CLK_ENABLE + {1, 15}, // IMX_CANFD_CLK_ENABLE + {2, 1}, // IMX_CSI_CLK_ENABLE + {2, 3}, // IMX_I2C1_SERIAL_CLK_ENABLE + {2, 4}, // IMX_I2C2_SERIAL_CLK_ENABLE + {2, 5}, // IMX_I2C3_SERIAL_CLK_ENABLE + {2, 6}, // IMX_IIM_CLK_ENABLE + {2, 7}, // IMX_IOMUX_IPT_CLK_IO_ENABLE + {2, 8}, // IMX_IPMUX1_CLK_ENABLE + {2, 9}, // IMX_IPMUX2_CLK_ENABLE + {2, 10}, // IMX_IPMUX3_CLK_ENABLE + {2, 11}, // IMX_IPSYNC_IP2APB_TZASC1_IPG_MASTER_CLK_ENABLE + {2, 14}, // IMX_LCD_CLK_ENABLE + {2, 15}, // IMX_PXP_CLK_ENABLE + {3, 1}, // IMX_M4_CLK_ENABLE + {3, 2}, // IMX_ENET_CLK_ENABLE + {3, 3}, // IMX_DISP_AXI_CLK_ENABLE + {3, 4}, // IMX_LCDIF2_PIX_CLK_ENABLE + {3, 5}, // IMX_LCDIF1_PIX_CLK_ENABLE + {3, 6}, // IMX_LDB_DI0_CLK_ENABLE + {3, 7}, // IMX_QSPI1_CLK_ENABLE + {3, 9}, // IMX_MLB_CLK_ENABLE + {3, 10}, // IMX_MMDC_CORE_ACLK_FAST_CORE_P0_ENABLE + {3, 12}, // IMX_MMDC_CORE_IPG_CLK_P0_ENABLE + {3, 13}, // IMX_MMDC_CORE_IPG_CLK_P1_ENABLE + {3, 14}, // IMX_OCRAM_CLK_ENABLE + {4, 0}, // IMX_PCIE_ROOT_ENABLE + {4, 5}, // IMX_QSPI2_CLK_ENABLE + {4, 6}, // IMX_PL301_MX6QPER1_BCHCLK_ENABLE + {4, 7}, // IMX_PL301_MX6QPER2_MAINCLK_ENABLE + {4, 8}, // IMX_PWM1_CLK_ENABLE + {4, 9}, // IMX_PWM2_CLK_ENABLE + {4, 10}, // IMX_PWM3_CLK_ENABLE + {4, 11}, // IMX_PWM4_CLK_ENABLE + {4, 12}, // IMX_RAWNAND_U_BCH_INPUT_APB_CLK_ENABLE + {4, 13}, // IMX_RAWNAND_U_GPMI_BCH_INPUT_BCH_CLK_ENABLE + {4, 14}, // IMX_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_CLK_ENABLE + {4, 15}, // IMX_RAWNAND_U_GPMI_INPUT_APB_CLK_ENABLE + {5, 0}, // IMX_ROM_CLK_ENABLE + {5, 3}, // IMX_SDMA_CLK_ENABLE + {5, 6}, // IMX_SPBA_CLK_ENABLE + {5, 7}, // IMX_SPDIF_AND_AUDIO_CLK_ENABLE + {5, 9}, // IMX_SSI1_CLK_ENABLE + {5, 10}, // IMX_SSI2_CLK_ENABLE + {5, 11}, // IMX_SSI3_CLK_ENABLE + {5, 12}, // IMX_UART_CLK_ENABLE + {5, 13}, // IMX_UART_SERIAL_CLK_ENABLE + {5, 14}, // IMX_SAI1_CLK_ENABLE + {5, 15}, // IMX_SAI2_CLK_ENABLE + {6, 0}, // IMX_USBOH3_CLK_ENABLE + {6, 1}, // IMX_USDHC1_CLK_ENABLE + {6, 2}, // IMX_USDHC2_CLK_ENABLE + {6, 3}, // IMX_USDHC3_CLK_ENABLE + {6, 4}, // IMX_USDHC4_CLK_ENABLE + {6, 5}, // IMX_EIM_SLOW_CLK_ENABLE + {6, 8}, // IMX_PWM8_CLK_ENABLE + {6, 10}, // IMX_VADC_CLK_ENABLE + {6, 11}, // IMX_GIS_CLK_ENABLE + {6, 12}, // IMX_I2C4_SERIAL_CLK_ENABLE + {6, 13}, // IMX_PWM5_CLK_ENABLE + {6, 14}, // IMX_PWM6_CLK_ENABLE + {6, 15}, // IMX_PWM7_CLK_ENABLE + }; + + return ImxpCcgrIndexMap[ClockGate]; +} + +CONST CHAR16 *StringFromImxClk (IMX_CLK Value) +{ + switch (Value) { + case IMX_CLK_NONE: return L"(none)"; + case IMX_OSC_CLK: return L"OSC_CLK"; + case IMX_PLL1_MAIN_CLK: return L"PLL1_MAIN_CLK"; + case IMX_PLL2_MAIN_CLK: return L"PLL2_MAIN_CLK"; + case IMX_PLL2_PFD0: return L"PLL2_PFD0"; + case IMX_PLL2_PFD1: return L"PLL2_PFD1"; + case IMX_PLL2_PFD2: return L"PLL2_PFD2"; + case IMX_PLL2_PFD3: return L"PLL2_PFD3"; + case IMX_PLL3_MAIN_CLK: return L"PLL3_MAIN_CLK"; + case IMX_PLL3_PFD0: return L"PLL3_PFD0"; + case IMX_PLL3_PFD1: return L"PLL3_PFD1"; + case IMX_PLL3_PFD2: return L"PLL3_PFD2"; + case IMX_PLL3_PFD3: return L"PLL3_PFD3"; + case IMX_PLL4_MAIN_CLK: return L"PLL4_MAIN_CLK"; + case IMX_PLL5_MAIN_CLK: return L"PLL5_MAIN_CLK"; + case IMX_CLK1: return L"CLK1"; + case IMX_CLK2: return L"CLK2"; + + case IMX_PLL1_SW_CLK: return L"PLL1_SW_CLK"; + case IMX_STEP_CLK: return L"STEP_CLK"; + case IMX_PLL3_SW_CLK: return L"PLL3_SW_CLK"; + + case IMX_PERIPH_CLK2: return L"PERIPH_CLK2"; + case IMX_PERIPH_CLK: return L"_PERIPH_CLK"; + case IMX_PRE_PERIPH_CLK: return L"PRE_PERIPH_CLK"; + + case IMX_ARM_CLK_ROOT: return L"ARM_CLK_ROOT"; + case IMX_MMDC_CLK_ROOT: return L"MMDC_CLK_ROOT"; + case IMX_FABRIC_CLK_ROOT: return L"FABRIC_CLK_ROOT"; + case IMX_OCRAM_CLK_ROOT: return L"OCRAM_CLK_ROOT"; + case IMX_PCIE_CLK_ROOT: return L"PCIE_CLK_ROOT"; + case IMX_AHB_CLK_ROOT: return L"AHB_CLK_ROOT"; + case IMX_PERCLK_CLK_ROOT: return L"PERCLK_CLK_ROOT"; + case IMX_IPG_CLK_ROOT: return L"IPG_CLK_ROOT"; + case IMX_USDHC1_CLK_ROOT: return L"USDHC1_CLK_ROOT"; + case IMX_USDHC2_CLK_ROOT: return L"USDHC2_CLK_ROOT"; + case IMX_USDHC3_CLK_ROOT: return L"USDHC3_CLK_ROOT"; + case IMX_USDHC4_CLK_ROOT: return L"USDHC4_CLK_ROOT"; + case IMX_ACLK_EIM_SLOW_CLK_ROOT: return L"ACLK_EIM_SLOW_CLK_ROOT"; + case IMX_GPU_AXI_CLK_ROOT: return L"GPU_AXI_CLK_ROOT"; + case IMX_GPU_CORE_CLK_ROOT: return L"GPU_CORE_CLK_ROOT"; + case IMX_VID_CLK_ROOT: return L"VID_CLK_ROOT"; + case IMX_ESAI_CLK_ROOT: return L"ESAI_CLK_ROOT"; + case IMX_AUDIO_CLK_ROOT: return L"AUDIO_CLK_ROOT"; + case IMX_SPDIF0_CLK_ROOT: return L"SPDIF0_CLK_ROOT"; + case IMX_SSI1_CLK_ROOT: return L"SSI1_CLK_ROOT"; + case IMX_SSI2_CLK_ROOT: return L"SSI2_CLK_ROOT"; + case IMX_SSI3_CLK_ROOT: return L"SSI3_CLK_ROOT"; + case IMX_LCDIF2_PIX_CLK_ROOT: return L"LCDIF2_PIX_CLK_ROOT"; + case IMX_LCDIF1_PIX_CLK_ROOT: return L"LCDIF1_PIX_CLK_ROOT"; + case IMX_LVDS_CLK_ROOT: return L"LVDS_CLK_ROOT"; + case IMX_M4_CLK_ROOT: return L"M4_CLK_ROOT"; + case IMX_ENET_CLK_ROOT: return L"ENET_CLK_ROOT"; + case IMX_QSPI1_CLK_ROOT: return L"QSPI1_CLK_ROOT"; + case IMX_QSPI2_CLK_ROOT: return L"QSPI2_CLK_ROOT"; + case IMX_DISPLAY_CLK_ROOT: return L"DISPLAY_CLK_ROOT"; + case IMX_CSI_CLK_ROOT: return L"CSI_CLK_ROOT"; + case IMX_CAN_CLK_ROOT: return L"CAN_CLK_ROOT"; + case IMX_ECSPI_CLK_ROOT: return L"ECSPI_CLK_ROOT"; + case IMX_UART_CLK_ROOT: return L"UART_CLK_ROOT"; + default: + ASSERT (FALSE); + return L"[invalid IMX_CLK value]"; + } +} + +IMX_CLK ImxpClkFromBypassClkSource (IMX_PLL_BYPASS_CLK_SRC BypassClockSour= ce) +{ + switch (BypassClockSource) { + case IMX_PLL_BYPASS_CLK_SRC_REF_CLK_24M: + return IMX_OSC_CLK; + case IMX_PLL_BYPASS_CLK_SRC_CLK1: + return IMX_CLK1; + case IMX_PLL_BYPASS_CLK_SRC_GPANAIO: + case IMX_PLL_BYPASS_CLK_SRC_CHRG_DET_B: + default: + ASSERT (FALSE); + return IMX_CLK_NONE; + } +} + + +EFI_STATUS +ImxpGetPll2PfdClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + IMX_PLL_PFD PfdIndex, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_ANALOG_REGISTERS *ccmAnalogRegisters =3D + (IMX_CCM_ANALOG_REGISTERS *) IMX_CCM_ANALOG_BASE; + + IMX_CCM_PFD_528_REG pfd528Reg; pfd528Reg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmAnalogRegisters->PFD_528); + + UINT32 pfdFrac; + switch (PfdIndex) { + case IMX_PLL_PFD0: + pfdFrac =3D pfd528Reg.PFD0_FRAC; + break; + case IMX_PLL_PFD1: + pfdFrac =3D pfd528Reg.PFD1_FRAC; + break; + case IMX_PLL_PFD2: + pfdFrac =3D pfd528Reg.PFD2_FRAC; + break; + case IMX_PLL_PFD3: + pfdFrac =3D pfd528Reg.PFD3_FRAC; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, IMX_PLL2_MAIN_CLK, &paren= tInfo); + if (EFI_ERROR (status)) { + return status; + } + + // The resulting frequency shall be 528*18/PFDn_FRAC + // where PFD0_FRAC is in the range 12-35. + ASSERT ((pfdFrac >=3D 12) && (pfdFrac <=3D 35)); + ClockInfo->Frequency =3D (UINT32) ((UINT64) parentInfo.Frequency * 18 / = pfdFrac); + ClockInfo->Parent =3D IMX_PLL2_MAIN_CLK; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetPerclkClkRootInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CSCMR1_REG cscmr1; cscmr1.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CSCMR1); + + IMX_CLK parent; + switch(cscmr1.perclk_clk_sel) { + case IMX_CCM_PERCLK_CLK_SEL_IPG_CLK_ROOT: + parent =3D IMX_IPG_CLK_ROOT; + break; + case IMX_CCM_PERCLK_CLK_SEL_OSC_CLK: + parent =3D IMX_OSC_CLK; + break; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR(status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cscmr1.perclk_podf)= ; + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetPeriphClk2Info ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCMR_REG cbcmrReg; cbcmrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCMR); + + IMX_CLK parent; + switch (cbcmrReg.periph_clk2_sel) { + case IMX_CCM_PERIPH_CLK2_SEL_PLL3_SW_CLK: + parent =3D IMX_PLL3_SW_CLK; + break; + case IMX_CCM_PERIPH_CLK2_SEL_OSC_CLK: + parent =3D IMX_OSC_CLK; + break; + case IMX_CCM_PERIPH_CLK2_SEL_PLL2_BYPASS_CLK: + parent =3D IMX_PLL2_MAIN_CLK; + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency / (1 + cbcdrReg.periph_clk= 2_podf); + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetPeriphClkInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX_C= CM_BASE; + + IMX_CCM_CBCDR_REG cbcdrReg; cbcdrReg.AsUint32 =3D + MmioRead32 ((UINTN) &ccmRegisters->CBCDR); + + IMX_CLK parent; + + // NOTE: periph_clk_sel is OR'd with PLL_bypass_en2 (from jtag) to + // produce the input value to the MUX. We assume PLL_bypass_en2 is= 0. + if (cbcdrReg.periph_clk_sel =3D=3D 0) { + parent =3D IMX_PRE_PERIPH_CLK; + } else { + ASSERT (cbcdrReg.periph_clk_sel =3D=3D 1); + parent =3D IMX_PERIPH_CLK2; + } + + IMX_CLOCK_INFO parentInfo; + EFI_STATUS status =3D ImxpGetClockInfo (Cache, parent, &parentInfo); + if (EFI_ERROR (status)) { + return status; + } + + ClockInfo->Frequency =3D parentInfo.Frequency; + ClockInfo->Parent =3D parent; + + return EFI_SUCCESS; +} + +EFI_STATUS +ImxpGetClockInfo ( + IN OUT IMX_CLOCK_TREE_CACHE *Cache, + IN IMX_CLK ClockId, + OUT IMX_CLOCK_INFO *ClockInfo + ) +{ + ASSERT (ClockId < ARRAYSIZE(Cache->Table)); + + // First try to satisfy from cache + { + UINTN cacheValidBits =3D Cache->Valid[ClockId / _BITS_PER_UINTN]; + if (cacheValidBits & (1 << (ClockId % _BITS_PER_UINTN))) { + *ClockInfo =3D Cache->Table[ClockId]; + return EFI_SUCCESS; + } + } + + EFI_STATUS status; + switch (ClockId) { + case IMX_OSC_CLK: + ImxpGetOsc24ClkInfo (ClockInfo); + status =3D EFI_SUCCESS; + break; + case IMX_PLL1_MAIN_CLK: + status =3D ImxpGetPll1MainClkInfo (Cache, ClockInfo); + break; + case IMX_PLL2_MAIN_CLK: + status =3D ImxpGetPll2MainClkInfo (Cache, ClockInfo); + break; + case IMX_PLL2_PFD0: + status =3D ImxpGetPll2PfdClkInfo (Cache, IMX_PLL_PFD0, ClockInfo); + break; + case IMX_PLL2_PFD1: + status =3D ImxpGetPll2PfdClkInfo (Cache, IMX_PLL_PFD1, ClockInfo); + break; + case IMX_PLL2_PFD2: + status =3D ImxpGetPll2PfdClkInfo (Cache, IMX_PLL_PFD2, ClockInfo); + break; + case IMX_PLL3_MAIN_CLK: + status =3D ImxpGetPll3MainClkInfo (Cache, ClockInfo); + break; + case IMX_PLL3_PFD0: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD0, ClockInfo); + break; + case IMX_PLL3_PFD1: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD1, ClockInfo); + break; + case IMX_PLL3_PFD2: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD2, ClockInfo); + break; + case IMX_PLL3_PFD3: + status =3D ImxpGetPll3PfdClkInfo (Cache, IMX_PLL_PFD3, ClockInfo); + break; + case IMX_PLL3_SW_CLK: + status =3D ImxpGetPll3SwClkInfo (Cache, ClockInfo); + break; + case IMX_PERIPH_CLK2: + status =3D ImxpGetPeriphClk2Info (Cache, ClockInfo); + break; + case IMX_PERIPH_CLK: + status =3D ImxpGetPeriphClkInfo (Cache, ClockInfo); + break; + case IMX_PRE_PERIPH_CLK: + status =3D ImxpGetPrePeriphClkInfo (Cache, ClockInfo); + break; + case IMX_ARM_CLK_ROOT: + status =3D ImxpGetArmClkRootInfo (Cache, ClockInfo); + break; + case IMX_AHB_CLK_ROOT: + status =3D ImxpGetAhbClkRootInfo (Cache, ClockInfo); + break; + case IMX_IPG_CLK_ROOT: + status =3D ImxpGetIpgClkRootInfo (Cache, ClockInfo); + break; + case IMX_PERCLK_CLK_ROOT: + status =3D ImxpGetPerclkClkRootInfo (Cache, ClockInfo); + break; + + default: + return EFI_UNSUPPORTED; + } + + if (EFI_ERROR (status)) { + return status; + } + + // Update the cache + Cache->Table[ClockId] =3D *ClockInfo; + Cache->Valid[ClockId / _BITS_PER_UINTN] |=3D (1 << (ClockId % _BITS_PER_= UINTN)); + + return EFI_SUCCESS; +} + +// +// Public functions +// + +EFI_STATUS ImxClkPwrClkOut1Enable (IMX_CLK Clock, UINT32 Divider) +{ + return EFI_UNSUPPORTED; +} + +VOID ImxClkPwrClkOut1Disable () +{ +} + +EFI_STATUS ImxClkPwrValidateClocks () +{ + return EFI_UNSUPPORTED; +} + +VOID ImxClkPwrLcdClockDisable () +{ + IMX_CCM_CCGR2_REG value32; + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX= _CCM_BASE; + + value32.AsUint32 =3D MmioRead32((UINTN) &ccmRegisters->CCGR[2]); + value32.lcd_clk_enable =3D IMX6SX_CCM_CLOCK_OFF; + MmioWrite32((UINTN) &ccmRegisters->CCGR[2], value32.AsUint32); +} + +VOID ImxClkPwrLcdClockEnable () +{ + IMX_CCM_CCGR2_REG value32; + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX= _CCM_BASE; + + value32.AsUint32 =3D MmioRead32((UINTN) &ccmRegisters->CCGR[2]); + value32.lcd_clk_enable =3D IMX6SX_RUN_ONLY; + MmioWrite32((UINTN) &ccmRegisters->CCGR[2], value32.AsUint32); +} + +VOID ImxSetClockRatePLL5 ( + UINT32 TargetClockRate, + UINT32 PreDividerLcdif1Val, + UINT32 PostDividerLcdif1Val + ) +{ + IMX_CCM_CCGR3_REG ccgr3; + UINT32 value32; + IMX_CCM_PLL_VIDEO_CTRL_REG videoControl; + volatile IMX_CCM_REGISTERS *ccmRegisters =3D (IMX_CCM_REGISTERS *) IMX= _CCM_BASE; + volatile IMX_CCM_ANALOG_REGISTERS *analogRegisters =3D (IMX_CCM_ANALOG= _REGISTERS *) IMX_CCM_ANALOG_BASE; + + // turn off LCD clocks + ImxClkPwrLcdClockDisable(); + + // gate the LCD pixel and AXI clocks CCGR3.CG5 + ccgr3.AsUint32 =3D MmioRead32((UINTN) &ccmRegisters->CCGR[3]); + ccgr3.lcdif1_pix_clk_enable =3D IMX6SX_CCM_CLOCK_OFF; + ccgr3.disp_axi_clk_enable =3D IMX6SX_CCM_CLOCK_OFF; + MmioWrite32((UINTN) &ccmRegisters->CCGR[3], ccgr3.AsUint32); + + // + // set the divider for the source clock to the video PLL to divide by = 1 + // + MmioWrite32((UINTN) &analogRegisters->MISC0_CLR, 0x80000000); + + // + // fire up the video PLL to the correct frequency + // before division + // + videoControl.AsUint32 =3D 0; + videoControl.POST_DIV_SELECT =3D 0x03; + videoControl.BYPASS =3D 0x01; + videoControl.POWERDOWN =3D 0x01; + videoControl.DIV_SELECT =3D 0x7f; + MmioWrite32((UINTN) &analogRegisters->PLL_VIDEO_CLR, videoControl.AsUi= nt32); + + // + // PLL output frequency =3D (Reference Freq) * (DIV_SELECT + NUM / DEN= OM) + // + // Use clock rate as denominator for simple fractional calculation. + // This way we just need to figure out the target clock rate ratio + // to the 24MHz reference. + // + { + IMX_CCM_PLL_VIDEO_CTRL_REG pllVideoCtrlReg; + UINT32 denom =3D IMX_REF_CLK_24M_FREQ; + UINT32 divSelect =3D TargetClockRate / IMX_REF_CLK_24M_FREQ; + UINT32 numerator =3D TargetClockRate % IMX_REF_CLK_24M_FREQ; + pllVideoCtrlReg.AsUint32 =3D MmioRead32((UINTN) &analogRegisters->= PLL_VIDEO); + + ASSERT (numerator < denom); + ASSERT ((divSelect >=3D 27) && (divSelect <=3D 54)); + + pllVideoCtrlReg.DIV_SELECT =3D divSelect; + pllVideoCtrlReg.POST_DIV_SELECT =3D IMX_POST_DIV_SELECT_DIVIDE_1; + + DEBUG (( + DEBUG_INFO, + "PLL 5 divSelect (%d) numerator (%d) denom %d\n", + divSelect, + numerator, + denom + )); + + MmioWrite32((UINTN) &analogRegisters->PLL_VIDEO, pllVideoCtrlReg.A= sUint32); + MmioWrite32((UINTN) &analogRegisters->PLL_VIDEO_NUM, numerator); + MmioWrite32((UINTN) &analogRegisters->PLL_VIDEO_DENOM, denom); + } + + // wait for PLL to lock + do { + videoControl.AsUint32 =3D MmioRead32((UINTN) &analogRegisters->PLL= _VIDEO); + } while (!(videoControl.LOCK)); + + // + // select the video PLL in the LCDIF clock selector + // and set the CDIF1_PRED value + // + value32 =3D MmioRead32((UINTN) &ccmRegisters->CSCDR2); + // Clear LCDIF1_CLK_SEL, LCDIF1_PRED and LCDIF1_PRE_CLK_SEL + value32 &=3D ~0x0003FE00; + // Set the predivider value and derive clock from PLL5 + value32 |=3D ((PreDividerLcdif1Val - 1) << 12) | (2 << 15); + MmioWrite32((UINTN) &ccmRegisters->CSCDR2, value32); + + // + // set the post divider in CBCMR + // + value32 =3D MmioRead32((UINTN) &ccmRegisters->CBCMR); + // Clear LCDIF1_PODF + value32 &=3D ~0x03800000; + value32 |=3D ((PostDividerLcdif1Val - 1) << 23); + MmioWrite32((UINTN) &ccmRegisters->CBCMR, value32); + + // enable the PLL output + videoControl.AsUint32 =3D 0; + videoControl.ENABLE =3D 1; + MmioWrite32((UINTN) &analogRegisters->PLL_VIDEO_SET, videoControl.AsUi= nt32); + + // Ungate the LCD pixel clock + ccgr3.AsUint32 =3D MmioRead32((UINTN) &ccmRegisters->CCGR[3]); + ccgr3.lcdif1_pix_clk_enable =3D IMX6SX_RUN_ONLY; + ccgr3.disp_axi_clk_enable =3D IMX6SX_RUN_ONLY; + MmioWrite32((UINTN) &ccmRegisters->CCGR[3], ccgr3.AsUint32); + + // turn on LCD clocks + ImxClkPwrLcdClockEnable(); +} + +EFI_STATUS +ImxSetLcdIfClockRate ( + UINT32 ClockRate + ) +{ + BOOLEAN foundConfig =3D FALSE; + UINT32 targetFreq; + UINT32 preDivSelectCount; + UINT32 postDivSelectCount; + UINT32 preDividerLcdif1[8] =3D { 1, 2, 3, 4, 5, 6, 7, 8 }; + UINT32 postDividerLcdif1[8] =3D { 1, 2, 3, 4, 5, 6, 7, 8 }; + + for (preDivSelectCount =3D 0; + preDivSelectCount < ARRAYSIZE (preDividerLcdif1); + ++preDivSelectCount) { + + for (postDivSelectCount =3D 0; + postDivSelectCount < ARRAYSIZE (postDividerLcdif1); + ++postDivSelectCount) { + + targetFreq =3D + ClockRate * + preDividerLcdif1[preDivSelectCount] * + postDividerLcdif1[postDivSelectCount] * + 1; + + // + // The valid range for PLL loop divider is 27-54 so we + // need to target freq need to fit within the valid range. + // + if ((targetFreq >=3D PLL5_MIN_FREQ) && + (targetFreq <=3D PLL5_MAX_FREQ)) { + foundConfig =3D TRUE; + break; + } + } + + if (foundConfig =3D=3D TRUE) { + break; + } + } + + if (foundConfig =3D=3D FALSE) { + DEBUG((DEBUG_ERROR, "No valid configuration found for clock rate %= d\n", ClockRate)); + ASSERT(FALSE); + return EFI_INVALID_PARAMETER; + } + + DEBUG (( + DEBUG_INFO, + "PLL 5 setting (%d) Target Freq (%d) PreDiv %d PostDiv %d\n", + ClockRate, + targetFreq, + preDividerLcdif1[preDivSelectCount], + postDividerLcdif1[postDivSelectCount] + )); + + ImxSetClockRatePLL5( + targetFreq, + preDividerLcdif1[preDivSelectCount], + postDividerLcdif1[postDivSelectCount]); + + return EFI_SUCCESS; +} --=20 2.16.2.gvfs.1.33.gf5370f1