From 106de33bf7f410bade659e110a5a7b187b46b8b2 Mon Sep 17 00:00:00 2001 From: Gary King Date: Fri, 14 May 2010 10:36:33 -0700 Subject: [ARM/tegra] add NvRm, ODM services, ODM kit for harmony & whistler add power rail support to GPIO driver Change-Id: I45d4c1110a635047d68fb14f3e72a28f99acbe1b --- arch/arm/mach-tegra/nvrm/core/ap15/Makefile | 35 + .../nvrm/core/ap15/ap15rm_clock_config.c | 2723 ++++++++++++++++ .../mach-tegra/nvrm/core/ap15/ap15rm_clock_misc.c | 511 +++ arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.c | 931 ++++++ arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.h | 460 +++ .../mach-tegra/nvrm/core/ap15/ap15rm_clocks_info.c | 1673 ++++++++++ arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_fuse.c | 104 + arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_hwmap.c | 51 + arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c | 682 ++++ .../mach-tegra/nvrm/core/ap15/ap15rm_init_common.c | 521 +++ .../mach-tegra/nvrm/core/ap15/ap15rm_interrupt.c | 314 ++ .../nvrm/core/ap15/ap15rm_interrupt_generic.c | 86 + .../arm/mach-tegra/nvrm/core/ap15/ap15rm_memctrl.c | 611 ++++ arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux.c | 398 +++ .../nvrm/core/ap15/ap15rm_pinmux_tables.c | 1185 +++++++ .../nvrm/core/ap15/ap15rm_pinmux_utils.h | 147 + .../nvrm/core/ap15/ap15rm_pmc_scratch_map.h | 73 + arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c | 663 ++++ .../mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.c | 544 ++++ .../mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.h | 314 ++ .../nvrm/core/ap15/ap15rm_power_oalintf.c | 334 ++ .../arm/mach-tegra/nvrm/core/ap15/ap15rm_private.h | 331 ++ .../mach-tegra/nvrm/core/ap15/ap15rm_reloctable.c | 50 + arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c | 432 +++ .../nvrm/core/ap15/ap15rm_xpc_hw_private.c | 165 + .../nvrm/core/ap15/ap15rm_xpc_hw_private.h | 92 + .../nvrm/core/ap15/ap16rm_pinmux_tables.c | 323 ++ .../mach-tegra/nvrm/core/ap15/ap16rm_reloctable.c | 50 + arch/arm/mach-tegra/nvrm/core/ap15/nvrm_clocks.c | 3311 ++++++++++++++++++++ arch/arm/mach-tegra/nvrm/core/ap15/nvrm_diag.c | 1376 ++++++++ 30 files changed, 18490 insertions(+) create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/Makefile create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_config.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_misc.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.h create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks_info.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_fuse.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_hwmap.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init_common.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_interrupt.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_interrupt_generic.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_memctrl.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux_tables.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux_utils.h create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pmc_scratch_map.h create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.h create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_oalintf.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_private.h create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_reloctable.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap16rm_pinmux_tables.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/ap16rm_reloctable.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/nvrm_clocks.c create mode 100644 arch/arm/mach-tegra/nvrm/core/ap15/nvrm_diag.c (limited to 'arch/arm/mach-tegra/nvrm/core/ap15') diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/Makefile b/arch/arm/mach-tegra/nvrm/core/ap15/Makefile new file mode 100644 index 000000000000..2c87c36b3245 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/Makefile @@ -0,0 +1,35 @@ +ccflags-y += -DNV_IS_AVP=0 +ccflags-y += -DNV_OAL=0 +ccflags-y += -DNV_USE_FUSE_CLOCK_ENABLE=0 +ifeq ($(CONFIG_MACH_TEGRA_GENERIC_DEBUG),y) +ccflags-y += -DNV_DEBUG=1 +else +ccflags-y += -DNV_DEBUG=0 +endif + +ccflags-y += -Iarch/arm/mach-tegra/nvrm/core/common +ccflags-y += -Iarch/arm/mach-tegra/nvrm/core + +obj-y += ap15rm_interrupt_generic.o +obj-y += ap15rm_hwmap.o +obj-y += ap15rm_clocks.o +obj-y += ap15rm_clock_config.o +obj-y += ap15rm_clocks_info.o +obj-y += nvrm_clocks.o +obj-y += ap15rm_pinmux.o +obj-y += ap15rm_pinmux_tables.o +obj-y += ap16rm_pinmux_tables.o +obj-y += ap15rm_power.o +obj-y += ap15rm_power_dfs.o +obj-y += ap15rm_power_oalintf.o +obj-y += ap15rm_clock_misc.o +obj-y += ap15rm_memctrl.o +obj-y += ap15rm_fuse.o +obj-y += nvrm_diag.o +obj-y += ap15rm_reloctable.o +obj-y += ap16rm_reloctable.o +obj-y += ap15rm_init.o +obj-y += ap15rm_init_common.o +obj-y += ap15rm_interrupt.o +obj-y += ap15rm_xpc.o +obj-y += ap15rm_xpc_hw_private.o diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_config.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_config.c new file mode 100644 index 000000000000..f13ef4797306 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_config.c @@ -0,0 +1,2723 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvassert.h" +#include "nvrm_clocks.h" +#include "nvrm_hwintf.h" +#include "nvrm_module.h" +#include "nvrm_drf.h" +#include "ap15/aremc.h" +#include "ap15/arclk_rst.h" +#include "ap15/arapb_misc.h" +#include "ap15rm_clocks.h" +#include "ap15rm_private.h" +#include "nvrm_pmu_private.h" +#include "nvodm_query_discovery.h" +#include "nvodm_query_memc.h" +#include "ap20/ap20rm_clocks.h" + +// TODO: CAR and EMC access macros for time critical access + +/*****************************************************************************/ + +static const NvU32 s_Ap15OscFreqKHz[] = { 13000, 19200, 12000, 26000 }; + +static void +Ap15PllPConfigure(NvRmDeviceHandle hRmDevice); + +static void +Ap15MioReconfigure(NvRmDeviceHandle hRmDevice, NvRmFreqKHz MioKHz); + +static void +Ap15AudioSyncInit(NvRmDeviceHandle hRmDevice, NvRmFreqKHz AudioSyncKHz); + +static NvError +NvRmPrivOscDoublerConfigure(NvRmDeviceHandle hRmDevice, NvRmFreqKHz OscKHz) +{ + switch (hRmDevice->ChipId.Id) + { + case 0x15: + case 0x16: + return NvRmPrivAp15OscDoublerConfigure(hRmDevice, OscKHz); + case 0x20: + return NvRmPrivAp20OscDoublerConfigure(hRmDevice, OscKHz); + default: + NV_ASSERT(!"Unsupported chip ID"); + return NvError_NotSupported; + } +} + +void +NvRmPrivClockSourceFreqInit( + NvRmDeviceHandle hRmDevice, + NvU32* pClockSourceFreq) +{ + NvU32 reg; + const NvRmCoreClockInfo* pCore = NULL; + + NV_ASSERT(hRmDevice); + NV_ASSERT(pClockSourceFreq); + + /* + * Fixed clock sources: 32kHz, main oscillator and doubler + * (OSC control should be already configured by the boot code) + */ + pClockSourceFreq[NvRmClockSource_ClkS] = 32; + + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_OSC_CTRL_0); + pClockSourceFreq[NvRmClockSource_ClkM] = + s_Ap15OscFreqKHz[NV_DRF_VAL(CLK_RST_CONTROLLER, OSC_CTRL, OSC_FREQ, reg)]; + + if (NvSuccess == NvRmPrivOscDoublerConfigure( + hRmDevice, pClockSourceFreq[NvRmClockSource_ClkM])) + { + pClockSourceFreq[NvRmClockSource_ClkD] = + pClockSourceFreq[NvRmClockSource_ClkM] << 1; + } + else + pClockSourceFreq[NvRmClockSource_ClkD] = 0; + + /* + * PLLs and secondary PLL dividers + */ + #define INIT_PLL_FREQ(PllId) \ + do\ + {\ + pClockSourceFreq[NvRmClockSource_##PllId] = NvRmPrivAp15PllFreqGet( \ + hRmDevice, NvRmPrivGetClockSourceHandle(NvRmClockSource_##PllId)->pInfo.pPll); \ + } while(0) + + // PLLX (check if present, keep boot settings + // and just init frequency table) + if (NvRmPrivGetClockSourceHandle(NvRmClockSource_PllX0)) + { + INIT_PLL_FREQ(PllX0); + } + // PLLC with output divider (if enabled keep boot settings and just init + // frequency table, if disbled or bypassed - configure) + INIT_PLL_FREQ(PllC0); + if (pClockSourceFreq[NvRmClockSource_PllC0] <= + pClockSourceFreq[NvRmClockSource_ClkM]) + { + NvRmFreqKHz f = NVRM_PLLC_DEFAULT_FREQ_KHZ; + NvRmPrivAp15PllConfigureSimple(hRmDevice, NvRmClockSource_PllC0, f, &f); + } + pClockSourceFreq[NvRmClockSource_PllC1] = NvRmPrivDividerFreqGet(hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllC1)->pInfo.pDivider); + + // PLLM with output divider (keep boot settings + // and just init frequency) + INIT_PLL_FREQ(PllM0); + pClockSourceFreq[NvRmClockSource_PllM1] = NvRmPrivDividerFreqGet(hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllM1)->pInfo.pDivider); +#if !NV_OAL + // PLLD and PLLU with no output dividers (keep boot settings + // and just init frequency table) + INIT_PLL_FREQ(PllD0); + INIT_PLL_FREQ(PllU0); +#endif + + // PLLP and output dividers: set PLLP fixed frequency and enable dividers + // with fixed settings in override mode, so they can be changed later, as + // necessary. Switch system clock to oscillator during PLLP reconfiguration + INIT_PLL_FREQ(PllP0); + if (pClockSourceFreq[NvRmClockSource_PllP0] != NVRM_PLLP_FIXED_FREQ_KHZ) + { + pCore = NvRmPrivGetClockSourceHandle( + NvRmClockSource_SystemBus)->pInfo.pCore; + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCore->SelectorOffset); + NvRmPrivCoreClockSet(hRmDevice, pCore, NvRmClockSource_ClkM, 0, 0); + Ap15PllPConfigure(hRmDevice); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCore->SelectorOffset, reg); + } + NV_ASSERT(pClockSourceFreq[NvRmClockSource_PllP0] == NVRM_PLLP_FIXED_FREQ_KHZ); + NvRmPrivDividerSet( + hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllP1)->pInfo.pDivider, + NVRM_FIXED_PLLP1_SETTING); + NvRmPrivDividerSet( + hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllP2)->pInfo.pDivider, + NVRM_FIXED_PLLP2_SETTING); + NvRmPrivDividerSet( + hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllP3)->pInfo.pDivider, + NVRM_FIXED_PLLP3_SETTING); + NvRmPrivDividerSet( + hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllP4)->pInfo.pDivider, + NVRM_FIXED_PLLP4_SETTING); + + // PLLA and output divider must be init after PLLP1, used as a + // reference (keep boot settings and just init frequency table) + INIT_PLL_FREQ(PllA1); + pClockSourceFreq[NvRmClockSource_PllA0] = NvRmPrivDividerFreqGet(hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllA0)->pInfo.pDivider); + + #undef INIT_PLL_FREQ + + /* + * Core and bus clock sources + * - Leave CPU bus as set by boot-loader + * - Leave System bus as set by boot-loader, make sure all bus dividers are 1:1 + */ + pCore = NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBus)->pInfo.pCore; + pClockSourceFreq[NvRmClockSource_CpuBus] = + NvRmPrivCoreClockFreqGet(hRmDevice, pCore); + if (NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBridge)) + { + pClockSourceFreq[NvRmClockSource_CpuBridge] = NvRmPrivDividerFreqGet(hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBridge)->pInfo.pDivider); + } + pCore = NvRmPrivGetClockSourceHandle(NvRmClockSource_SystemBus)->pInfo.pCore; + pClockSourceFreq[NvRmClockSource_SystemBus] = + NvRmPrivCoreClockFreqGet(hRmDevice, pCore); + NvRmPrivBusClockInit( + hRmDevice, pClockSourceFreq[NvRmClockSource_SystemBus]); + + /* + * Initialize AudioSync clocks (PLLA will be re-configured if necessary) + */ + Ap15AudioSyncInit(hRmDevice, NVRM_AUDIO_SYNC_KHZ); +} + +void +NvRmPrivBusClockInit(NvRmDeviceHandle hRmDevice, NvRmFreqKHz SystemFreq) +{ + /* + * Set all bus clock frequencies equal to the system clock frequency, + * and clear AVP clock skipper i.e., set all bus clock dividers 1:1. + * If APB clock is limited below system clock for a particular SoC, + * set the APB divider to satisfy this limitation. + */ + NvRmFreqKHz AhbFreq, ApbFreq; + NvRmFreqKHz ApbMaxFreq = SystemFreq; + if (hRmDevice->ChipId.Id == 0x20) + { + ApbMaxFreq = NVRM_AP20_APB_MAX_KHZ; // AP20 limitation + } + AhbFreq = SystemFreq; + ApbFreq = NV_MIN(SystemFreq, ApbMaxFreq); + + NvRmPrivBusClockFreqSet( + hRmDevice, SystemFreq, &SystemFreq, &AhbFreq, &ApbFreq, ApbMaxFreq); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_COP_CLK_SKIP_POLICY_0, 0x0); +} + +/*****************************************************************************/ +/*****************************************************************************/ + +static const NvRmFreqKHz s_PllLpCpconSelectionTable[] = +{ + NVRM_PLL_LP_CPCON_SELECT_STEPS_KHZ +}; +static const NvU32 s_PllLpCpconSelectionTableSize = +NV_ARRAY_SIZE(s_PllLpCpconSelectionTable); + +static const NvU32 s_PllMipiCpconSelectionTable[] = +{ + NVRM_PLL_MIPI_CPCON_SELECT_STEPS_N_DIVIDER +}; +static const NvU32 s_PllMipiCpconSelectionTableSize = +NV_ARRAY_SIZE(s_PllMipiCpconSelectionTable); + +static void +PllLpGetTypicalControls( + NvRmFreqKHz InputKHz, + NvU32 M, + NvU32 N, + NvU32* pCpcon) +{ + NvU32 i; + if (N >= NVRM_PLL_LP_MIN_N_FOR_CPCON_SELECTION) + { + // CPCON depends on comparison frequency + for (i = 0; i < s_PllLpCpconSelectionTableSize; i++) + { + if (InputKHz >= s_PllLpCpconSelectionTable[i] * M) + break; + } + *pCpcon = i + 1; + } + else // CPCON is 1, regardless of frequency + { + *pCpcon = 1; + } +} + +static void +PllMipiGetTypicalControls( + NvU32 N, + NvU32* pCpcon, + NvU32* pLfCon) +{ + NvU32 i; + + // CPCON depends on feedback divider + for (i = 0; i < s_PllMipiCpconSelectionTableSize; i++) + { + if (N <= s_PllMipiCpconSelectionTable[i]) + break; + } + *pCpcon = i + 1; + *pLfCon = (N >= NVRM_PLL_MIPI_LFCON_SELECT_N_DIVIDER) ? 1 : 0; +} + +void +NvRmPrivAp15PllSet( + NvRmDeviceHandle hRmDevice, + const NvRmPllClockInfo* pCinfo, + NvU32 M, + NvU32 N, + NvU32 P, + NvU32 StableDelayUs, + NvU32 cpcon, + NvU32 lfcon, + NvBool TypicalControls, + NvU32 flags) +{ + NvU32 base, misc; + NvU32 old_base, old_misc; + NvU32 delay = 0; + NvU32 override = 0; + + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo); + NV_ASSERT(pCinfo->PllBaseOffset); + NV_ASSERT(pCinfo->PllMiscOffset); + + /* + * PLL control fields used below have the same layout for all PLLs with + * the following exceptions: + * + * a) PLLP base register OVERRIDE field has to be set in order to enable + * PLLP re-configuration in diagnostic mode. For other PLLs this field is + * "Don't care". + * b) PLLU HS P divider field is one bit, inverse logic field. Other control + * bits, that are mapped to P divider in common layout should be set to 0. + * + * PLLP h/w field definitions will be used in DRF macros to construct base + * values for all PLLs, with special care of a) and b). All base fields not + * explicitly used below are set to 0 for all PLLs. + * + * c) PLLD/PLLU miscellaneous register has a unique fields determined based + * on the input flags. For other PLLs these fields have different meaning, + * and will be preserved. + * + * PLLP h/w field definitions will be used in DRF macros to construct + * miscellaneous values with common layout. For unique fields PLLD h/w + * definitions will be used. All miscellaneous fields not explicitly used + * below are preserved for all PLLs. + */ + base = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllBaseOffset); + old_base = base; + + // Disable PLL if either input or feedback divider setting is zero + if ((M == 0) || (N == 0)) + { + base = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_BYPASS, DISABLE, base); + base = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_ENABLE, DISABLE, base); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllBaseOffset, base); + NvRmPrivPllFreqUpdate(hRmDevice, pCinfo); + return; + } + + // Determine type-specific controls, construct new misc settings + misc = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllMiscOffset); + old_misc = misc; + if (pCinfo->PllType == NvRmPllType_MIPI) + { + if (flags & NvRmPllConfigFlags_SlowMode) + { + misc = NV_FLD_SET_DRF_NUM( // "1" = slow (/8) MIPI clock output + CLK_RST_CONTROLLER, PLLD_MISC, PLLD_FO_MODE, 1, misc); + } + else if (flags & NvRmPllConfigFlags_FastMode) + { + misc = NV_FLD_SET_DRF_NUM( // "0" = fast MIPI clock output + CLK_RST_CONTROLLER, PLLD_MISC, PLLD_FO_MODE, 0, misc); + } + if (flags & NvRmPllConfigFlags_DiffClkEnable) + { + misc = NV_FLD_SET_DRF_NUM( // Enable differential clocks + CLK_RST_CONTROLLER, PLLD_MISC, PLLD_CLKENABLE, 1, misc); + } + else if (flags & NvRmPllConfigFlags_DiffClkDisable) + { + misc = NV_FLD_SET_DRF_NUM( // Disable differential clocks + CLK_RST_CONTROLLER, PLLD_MISC, PLLD_CLKENABLE, 0, misc); + } + if (TypicalControls) + { + PllMipiGetTypicalControls(N, &cpcon, &lfcon); + } + delay = NVRM_PLL_MIPI_STABLE_DELAY_US; + } + else if (pCinfo->PllType == NvRmPllType_LP) + { + if (flags & NvRmPllConfigFlags_DccEnable) + { + misc = NV_FLD_SET_DRF_NUM( // "1" = enable DCC + CLK_RST_CONTROLLER, PLLP_MISC, PLLP_DCCON, 1, misc); + } + else if (flags & NvRmPllConfigFlags_DccDisable) + { + misc = NV_FLD_SET_DRF_NUM( // "0" = disable DCC + CLK_RST_CONTROLLER, PLLP_MISC, PLLP_DCCON, 0, misc); + } + if (TypicalControls) + { + NvRmFreqKHz InputKHz = NvRmPrivGetClockSourceFreq(pCinfo->InputId); + PllLpGetTypicalControls(InputKHz, M, N, &cpcon); + } + lfcon = 0; // always for LP PLL + delay = NVRM_PLL_LP_STABLE_DELAY_US; + } + else if (pCinfo->PllType == NvRmPllType_UHS) + { + if (TypicalControls) // Same as MIPI typical controls + { + PllMipiGetTypicalControls(N, &cpcon, &lfcon); + } + delay = NVRM_PLL_MIPI_STABLE_DELAY_US; + P = (P == 0) ? 1 : 0; // P-divider is 1 bit, inverse logic + } + else + { + NV_ASSERT(!"Invalid PLL type"); + } + misc = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, PLLP_MISC, PLLP_CPCON, cpcon, misc); + misc = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, PLLP_MISC, PLLP_LFCON, lfcon, misc); + + // Construct new base setting + // Override is PLLP specific, and it is just ignored by other PLLs; + override = ((flags & NvRmPllConfigFlags_Override) != 0) ? + CLK_RST_CONTROLLER_PLLP_BASE_0_PLLP_BASE_OVRRIDE_ENABLE : + CLK_RST_CONTROLLER_PLLP_BASE_0_PLLP_BASE_OVRRIDE_DISABLE; + { // Compiler failed to generate correct code for the base fields + // concatenation without the split below + volatile NvU32 prebase = + NV_DRF_DEF(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_BYPASS, ENABLE) | + NV_DRF_DEF(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_ENABLE, ENABLE) | + NV_DRF_DEF(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_REF_DIS, REF_ENABLE); + base = prebase | + NV_DRF_NUM(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_BASE_OVRRIDE, override) | + NV_DRF_NUM(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_DIVP, P) | + NV_DRF_NUM(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_DIVN, N) | + NV_DRF_NUM(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_DIVM, M); + } + + // If PLL is not bypassed, and new configurations is the same as the old + // one - exit without overwriting h/w. Otherwise, bypass PLL before + // changing configuration. + if (NV_DRF_VAL(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_BYPASS, old_base) == + CLK_RST_CONTROLLER_PLLP_BASE_0_PLLP_BYPASS_DISABLE) + { + old_base = NV_FLD_SET_DRF_DEF( + CLK_RST_CONTROLLER, PLLP_BASE, PLLP_BYPASS, ENABLE, old_base); + if ((base == old_base) && (misc == old_misc)) + { + NvRmPrivPllFreqUpdate(hRmDevice, pCinfo); + return; + } + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->PllBaseOffset, old_base); + } + + // Configure and enable PLL, keep it bypassed + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllMiscOffset, misc); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllBaseOffset, base); + + // Wait for PLL to stabilize and switch to PLL output + NV_ASSERT(StableDelayUs); + if (StableDelayUs > delay) + StableDelayUs = delay; + NvOsWaitUS(StableDelayUs); + + base = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_BYPASS, DISABLE, base); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllBaseOffset, base); + NvRmPrivPllFreqUpdate(hRmDevice, pCinfo); +} + +NvRmFreqKHz +NvRmPrivAp15PllFreqGet( + NvRmDeviceHandle hRmDevice, + const NvRmPllClockInfo* pCinfo) +{ + NvU32 M, N, P; + NvU32 base, misc; + NvRmFreqKHz PllKHz; + + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo); + NV_ASSERT(pCinfo->PllBaseOffset); + NV_ASSERT(pCinfo->PllMiscOffset); + + /* + * PLL control fields used below have the same layout for all PLLs with + * the following exceptions: + * + * a) PLLP base register OVERRIDE field ("Don't care" for other PLLs). + * Respectively, PLLP h/w field definitions will be used in DRF macros + * to construct base values for all PLLs. + * + * b) PLLD/PLLU miscellaneous register fast/slow mode control (does not + * affect output frequency for other PLLs). Respectively, PLLD h/w field + * definitions will be used in DRF macros to construct miscellaneous values. + */ + base = NV_REGR( + hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllBaseOffset); + PllKHz = NvRmPrivGetClockSourceFreq(pCinfo->InputId); + NV_ASSERT(PllKHz); + NV_ASSERT(NV_DRF_VAL(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_REF_DIS, base) == + CLK_RST_CONTROLLER_PLLP_BASE_0_PLLP_REF_DIS_REF_ENABLE); + + if (NV_DRF_VAL(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_BYPASS, base) == + CLK_RST_CONTROLLER_PLLP_BASE_0_PLLP_BYPASS_DISABLE) + { + // Special cases: PLL is disabled, or in fixed mode (PLLP only) + if (NV_DRF_VAL(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_ENABLE, base) == + CLK_RST_CONTROLLER_PLLP_BASE_0_PLLP_ENABLE_DISABLE) + return 0; + if ((pCinfo->SourceId == NvRmClockSource_PllP0) && + (NV_DRF_VAL(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_BASE_OVRRIDE, base) == + CLK_RST_CONTROLLER_PLLP_BASE_0_PLLP_BASE_OVRRIDE_DISABLE)) + return NV_BOOT_PLLP_FIXED_FREQ_KHZ; + + // PLL formula - Output F = (Reference F * N) / (M * 2^P) + M = NV_DRF_VAL(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_DIVM, base); + N = NV_DRF_VAL(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_DIVN, base); + P = NV_DRF_VAL(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_DIVP, base); + NV_ASSERT((M != 0) && (N != 0)); + + if (pCinfo->PllType == NvRmPllType_UHS) + { + // Adjust P divider field size and inverse logic for USB HS PLL + P = (P & 0x1) ? 0 : 1; + } + PllKHz = ((PllKHz * N) / M) >> P; + + // Check slow/fast mode selection for MIPI PLLs + if (pCinfo->PllType == NvRmPllType_MIPI) + { + misc = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->PllMiscOffset); + if (NV_DRF_VAL(CLK_RST_CONTROLLER, PLLD_MISC, PLLD_FO_MODE, misc) == 1) + { + PllKHz = PllKHz >> 3; // In slow mode output is divided by 8 + } + } + } + if (pCinfo->SourceId == NvRmClockSource_PllD0) + { + PllKHz = PllKHz >> 1; // PLLD output always divided by 2 + } + return PllKHz; +} + +static void +Ap15PllControl( + NvRmDeviceHandle hRmDevice, + NvRmClockSource PllId, + NvBool Enable) +{ + NvU32 base; + NvU32 delay = 0; + const NvRmPllClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(PllId)->pInfo.pPll; + + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo->PllBaseOffset); + + /* + * PLL control fields used below have the same layout for all PLLs. + * PLLP h/w field definitions will be used in DRF macros to construct base + * values for all PLLs. + */ + base = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllBaseOffset); + + if (Enable) + { + // No need to enable already enabled PLL - do nothing + if (NV_DRF_VAL(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_ENABLE, base) == + CLK_RST_CONTROLLER_PLLP_BASE_0_PLLP_ENABLE_ENABLE) + return; + + // Get ready stabilization delay + if ((pCinfo->PllType == NvRmPllType_MIPI) || + (pCinfo->PllType == NvRmPllType_UHS)) + delay = NVRM_PLL_MIPI_STABLE_DELAY_US; + else if (pCinfo->PllType == NvRmPllType_LP) + delay = NVRM_PLL_LP_STABLE_DELAY_US; + else + NV_ASSERT(!"Invalid PLL type"); + + // Bypass PLL => Enable PLL => wait for PLL to stabilize + // => switch to PLL output. All other settings preserved. + base = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_BYPASS, ENABLE, base); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllBaseOffset, base); + base = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_ENABLE, ENABLE, base); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllBaseOffset, base); + + NvOsWaitUS(delay); + + base = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_BYPASS, DISABLE, base); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllBaseOffset, base); + } + else + { + // Disable PLL, no bypass. All other settings preserved. + base = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_BYPASS, DISABLE, base); + base = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, PLLP_BASE, PLLP_ENABLE, DISABLE, base); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->PllBaseOffset, base); + } + NvRmPrivPllFreqUpdate(hRmDevice, pCinfo); +} + +void +NvRmPrivAp15PllConfigureSimple( + NvRmDeviceHandle hRmDevice, + NvRmClockSource PllId, + NvRmFreqKHz MaxOutKHz, + NvRmFreqKHz* pPllOutKHz) +{ +#define NVRM_PLL_FCMP_1 (1000) +#define NVRM_PLL_VCO_RANGE_1 (1000000) +#define NVRM_PLL_FCMP_2 (2000) +#define NVRM_PLL_VCO_RANGE_2 (2000000) + + /* + * Simple PLL configuration (assuming that target output frequency is + * always in VCO range, and does not exceed 2GHz). + * - output divider is set 1:1 + * - input divider is set to get comparison frequency equal or slightly + * above 1MHz if VCO is below 1GHz . Otherwise, input divider is set + * to get comparison frequency equal or slightly below 2MHz. + * - feedback divider is calculated based on target output frequency + * With simple configuration the absolute output frequency error does not + * exceed half of comparison frequency. It has been verified that simple + * configuration provides necessary accuracy for all display pixel clocks + * use cases. + */ + NvU32 M, N, P; + NvRmFreqKHz RefKHz, VcoKHz; + const NvRmPllClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(PllId)->pInfo.pPll; + NvU32 flags = 0; + + NV_ASSERT(hRmDevice); + VcoKHz = *pPllOutKHz; + P = 0; + + if (pCinfo->SourceId == NvRmClockSource_PllD0) + { // PLLD output is always divided by 2 (after P-divider) + VcoKHz = VcoKHz << 1; + MaxOutKHz = MaxOutKHz << 1; + while (VcoKHz < pCinfo->PllVcoMin) + { + VcoKHz = VcoKHz << 1; + MaxOutKHz = MaxOutKHz << 1; + P++; + } + NV_ASSERT(P <= CLK_RST_CONTROLLER_PLLD_BASE_0_PLLD_DIVP_DEFAULT_MASK); + flags = NvRmPllConfigFlags_DiffClkEnable; + } + if (pCinfo->SourceId == NvRmClockSource_PllX0) + { + flags = VcoKHz < NVRM_PLLX_DCC_VCO_MIN ? + NvRmPllConfigFlags_DccDisable : NvRmPllConfigFlags_DccEnable; + } + NV_ASSERT((pCinfo->PllVcoMin <= VcoKHz) && (VcoKHz <= pCinfo->PllVcoMax)); + NV_ASSERT(VcoKHz <= NVRM_PLL_VCO_RANGE_2); + NV_ASSERT(VcoKHz <= MaxOutKHz); + + RefKHz = NvRmPrivGetClockSourceFreq(pCinfo->InputId); + NV_ASSERT(RefKHz); + if (VcoKHz <= NVRM_PLL_VCO_RANGE_1) + M = RefKHz / NVRM_PLL_FCMP_1; + else + M = (RefKHz + NVRM_PLL_FCMP_2 - 1) / NVRM_PLL_FCMP_2; + N = (RefKHz + ((VcoKHz * M) << 1) ) / (RefKHz << 1); + if ((RefKHz * N) > (MaxOutKHz * M)) + N--; // use floor if rounding violates client's max limit + + NvRmPrivAp15PllSet( + hRmDevice, pCinfo, M, N, P, (NvU32)-1, 0, 0, NV_TRUE, flags); + *pPllOutKHz = NvRmPrivGetClockSourceFreq(pCinfo->SourceId); +} + + +// Fixed list of PLL HDMI configurations for different reference frequencies +// arranged according to CLK_RST_CONTROLLER_OSC_CTRL_0_OSC_FREQ_FIELD enum +static const NvRmPllFixedConfig s_Ap15HdmiPllConfigurations[] = +{ + NVRM_PLLHD_AT_13MHZ, + NVRM_PLLHD_AT_19MHZ, + NVRM_PLLHD_AT_12MHZ, + NVRM_PLLHD_AT_26MHZ +}; + +void +NvRmPrivAp15PllConfigureHdmi( + NvRmDeviceHandle hRmDevice, + NvRmClockSource PllId, + NvRmFreqKHz* pPllOutKHz) +{ + NvU32 reg; + NvRmPllFixedConfig HdmiConfig = {0}; + const NvRmPllClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(PllId)->pInfo.pPll; + + // Only PLLD or PLLC should be configured here + NV_ASSERT((PllId == NvRmClockSource_PllD0) || + (PllId == NvRmClockSource_PllC0)); + + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_OSC_CTRL_0); + HdmiConfig = s_Ap15HdmiPllConfigurations[NV_DRF_VAL( + CLK_RST_CONTROLLER, OSC_CTRL, OSC_FREQ, reg)]; + + NvRmPrivAp15PllSet(hRmDevice, pCinfo, HdmiConfig.M, HdmiConfig.N, + HdmiConfig.P, (NvU32)-1, 0, 0, NV_TRUE, 0); + *pPllOutKHz = NvRmPrivGetClockSourceFreq(pCinfo->SourceId); +} + +/*****************************************************************************/ + +// Fixed list of PLLP configurations for different reference frequencies +// arranged according to CLK_RST_CONTROLLER_OSC_CTRL_0_OSC_FREQ_FIELD enum +static const NvRmPllFixedConfig s_Ap15PllPConfigurations[] = +{ + NVRM_PLLP_AT_13MHZ, + NVRM_PLLP_AT_19MHZ, + NVRM_PLLP_AT_12MHZ, + NVRM_PLLP_AT_26MHZ +}; + +static void +Ap15PllPConfigure(NvRmDeviceHandle hRmDevice) +{ + NvU32 reg; + NvRmFreqKHz PllKHz; + NvRmPllFixedConfig PllPConfig = {0}; + + const NvRmPllClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllP0)->pInfo.pPll; + NV_ASSERT(hRmDevice); + + // Configure and enable PllP at RM fixed frequency, + // if it is not already enabled + PllKHz = NvRmPrivGetClockSourceFreq(pCinfo->SourceId); + if (PllKHz == NVRM_PLLP_FIXED_FREQ_KHZ) + return; + + // Get fixed PLLP configuration for current oscillator frequency. + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_OSC_CTRL_0); + PllPConfig = s_Ap15PllPConfigurations[NV_DRF_VAL( + CLK_RST_CONTROLLER, OSC_CTRL, OSC_FREQ, reg)]; + + // Configure and enable PLLP + NvRmPrivAp15PllSet(hRmDevice, pCinfo, PllPConfig.M, PllPConfig.N, + PllPConfig.P, (NvU32)-1, 0, 0, NV_TRUE, + NvRmPllConfigFlags_Override); +} + +/*****************************************************************************/ + +// Fixed list of PLLU configurations for different reference frequencies +// arranged according to CLK_RST_CONTROLLER_OSC_CTRL_0_OSC_FREQ_FIELD enum +static const NvRmPllFixedConfig s_Ap15UsbPllConfigurations[] = +{ + NVRM_PLLU_AT_13MHZ, + NVRM_PLLU_AT_19MHZ, + NVRM_PLLU_AT_12MHZ, + NVRM_PLLU_AT_26MHZ +}; + +static const NvRmPllFixedConfig s_Ap15UlpiPllConfigurations[] = +{ + NVRM_PLLU_ULPI_AT_13MHZ, + NVRM_PLLU_ULPI_AT_19MHZ, + NVRM_PLLU_ULPI_AT_12MHZ, + NVRM_PLLU_ULPI_AT_26MHZ +}; + +static const NvRmPllFixedConfig s_Ap15UhsPllConfigurations[] = +{ + NVRM_PLLU_HS_AT_13MHZ, + NVRM_PLLU_HS_AT_19MHZ, + NVRM_PLLU_HS_AT_12MHZ, + NVRM_PLLU_HS_AT_26MHZ +}; + +static void +PllUmipiConfigure(NvRmDeviceHandle hRmDevice, NvRmFreqKHz TargetFreq) +{ + NvU32 reg; + NvRmPllFixedConfig UsbConfig = {0}; + const NvRmPllClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllU0)->pInfo.pPll; + NvRmFreqKHz CurrentFreq = NvRmPrivGetClockSourceFreq(pCinfo->SourceId); + NV_ASSERT(hRmDevice); + + if (CurrentFreq == TargetFreq) + return; // PLLU is already configured at target frequency - exit + + // Index into fixed PLLU configuration tables based on oscillator frequency + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_OSC_CTRL_0); + reg = NV_DRF_VAL(CLK_RST_CONTROLLER, OSC_CTRL, OSC_FREQ, reg); + + if (TargetFreq == NvRmFreqUnspecified) + { + // By default set standard USB frequency, if PLLU is not configured + if ((CurrentFreq == s_Ap15UsbPllConfigurations[reg].OutputKHz) || + (CurrentFreq == s_Ap15UlpiPllConfigurations[reg].OutputKHz)) + { + return; // PLLU is already configured at supported frequency - exit + } + UsbConfig = s_Ap15UsbPllConfigurations[reg]; + } + else if (TargetFreq == s_Ap15UsbPllConfigurations[reg].OutputKHz) + { + UsbConfig = s_Ap15UsbPllConfigurations[reg]; + } + else if (TargetFreq == s_Ap15UlpiPllConfigurations[reg].OutputKHz) + { + UsbConfig = s_Ap15UlpiPllConfigurations[reg]; + } + else + { + NV_ASSERT(!"Invalid target frequency"); + return; + } + // Configure and enable PLLU + NvRmPrivAp15PllSet(hRmDevice, pCinfo, UsbConfig.M, UsbConfig.N, + UsbConfig.P, (NvU32)-1, 0, 0, NV_TRUE, 0); +} + +static void +PllUhsConfigure(NvRmDeviceHandle hRmDevice, NvRmFreqKHz TargetFreq) +{ + NvU32 reg; + NvRmPllFixedConfig UsbConfig = {0}; + const NvRmPllClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllU0)->pInfo.pPll; + NvRmFreqKHz CurrentFreq = NvRmPrivGetClockSourceFreq(pCinfo->SourceId); + NV_ASSERT(hRmDevice); + + // Index into fixed PLLU configuration tables based on oscillator frequency + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_OSC_CTRL_0); + reg = NV_DRF_VAL(CLK_RST_CONTROLLER, OSC_CTRL, OSC_FREQ, reg); + + // If PLLU is already configured - exit + if (CurrentFreq == s_Ap15UhsPllConfigurations[reg].OutputKHz) + return; + + /* + * Target may be unspecified, or any of the standard USB, ULPI, or UHS + * frequencies. In any case, main PLLU HS output is configured at UHS + * frequency, with ULPI and USB frequencies are generated on secondary + * outputs by fixed post dividers + */ + if (!( (TargetFreq == NvRmFreqUnspecified) || + (TargetFreq == s_Ap15UsbPllConfigurations[reg].OutputKHz) || + (TargetFreq == s_Ap15UlpiPllConfigurations[reg].OutputKHz) || + (TargetFreq == s_Ap15UhsPllConfigurations[reg].OutputKHz) ) + ) + { + NV_ASSERT(!"Invalid target frequency"); + return; + } + // Configure and enable PLLU + UsbConfig = s_Ap15UhsPllConfigurations[reg]; + NvRmPrivAp15PllSet(hRmDevice, pCinfo, UsbConfig.M, UsbConfig.N, + UsbConfig.P, (NvU32)-1, 0, 0, NV_TRUE, 0); +} + +static void +Ap15PllUConfigure(NvRmDeviceHandle hRmDevice, NvRmFreqKHz TargetFreq) +{ + const NvRmPllClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllU0)->pInfo.pPll; + + if (pCinfo->PllType == NvRmPllType_MIPI) + PllUmipiConfigure(hRmDevice, TargetFreq); + else if (pCinfo->PllType == NvRmPllType_UHS) + PllUhsConfigure(hRmDevice, TargetFreq); +} + +/*****************************************************************************/ + +// Fixed list of PLLA configurations for supported audio clocks +static const NvRmPllFixedConfig s_Ap15AudioPllConfigurations[] = +{ + NVRM_PLLA_CONFIGURATIONS +}; + +static void +Ap15PllAConfigure( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz* pAudioTargetKHz) +{ +// The reminder bits used to check divisibility +#define REMINDER_BITS (6) + + NvU32 i, rem; + NvRmFreqKHz OutputKHz; + NvU32 BestRem = (0x1 << REMINDER_BITS); + NvU32 BestIndex = NV_ARRAY_SIZE(s_Ap15AudioPllConfigurations) - 1; + + NvRmPllFixedConfig AudioConfig = {0}; + const NvRmPllClockInfo* pPllCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllA1)->pInfo.pPll; + const NvRmDividerClockInfo* pDividerCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllA0)->pInfo.pDivider; + NV_ASSERT(hRmDevice); + NV_ASSERT(*pAudioTargetKHz); + + // Fixed PLLA FPGA configuration + if (NvRmPrivGetExecPlatform(hRmDevice) == ExecPlatform_Fpga) + { + *pAudioTargetKHz = NvRmPrivGetClockSourceFreq(pDividerCinfo->SourceId); + return; + } + // Find PLLA configuration with smallest output frequency that can be + // divided by fractional divider into the closest one to the target. + for (i = 0; i < NV_ARRAY_SIZE(s_Ap15AudioPllConfigurations); i++) + { + OutputKHz = s_Ap15AudioPllConfigurations[i].OutputKHz; + if (*pAudioTargetKHz > OutputKHz) + continue; + rem = ((OutputKHz << (REMINDER_BITS + 1)) / (*pAudioTargetKHz)) & + ((0x1 << REMINDER_BITS) - 1); + if (rem < BestRem) + { + BestRem = rem; + BestIndex = i; + if (rem == 0) + break; + } + } + + // Configure PLLA and output divider + AudioConfig = s_Ap15AudioPllConfigurations[BestIndex]; + NvRmPrivAp15PllSet(hRmDevice, pPllCinfo, AudioConfig.M, AudioConfig.N, + AudioConfig.P, (NvU32)-1, 0, 0, NV_TRUE, 0); + NvRmPrivDividerSet( + hRmDevice, pDividerCinfo, AudioConfig.D); + *pAudioTargetKHz = NvRmPrivGetClockSourceFreq(pDividerCinfo->SourceId); +} + +static void +Ap15PllAControl( + NvRmDeviceHandle hRmDevice, + NvBool Enable) +{ + const NvRmDividerClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllA0)->pInfo.pDivider; + if (NvRmPrivGetExecPlatform(hRmDevice) == ExecPlatform_Fpga) + return; // No PLLA control on FPGA + + if (Enable) + { + Ap15PllControl(hRmDevice, NvRmClockSource_PllA1, NV_TRUE); + } + else + { + // Disable provided PLLA is not used as a source for any clock + if (NvRmPrivGetDfsFlags(hRmDevice) & NvRmDfsStatusFlags_StopPllA0) + Ap15PllControl(hRmDevice, NvRmClockSource_PllA1, NV_FALSE); + } + NvRmPrivDividerFreqUpdate(hRmDevice, pCinfo); +} + +static void +Ap15AudioSyncInit(NvRmDeviceHandle hRmDevice, NvRmFreqKHz AudioSyncKHz) +{ + NvRmFreqKHz AudioTargetKHz; + NvRmClockSource AudioSyncSource; + const NvRmSelectorClockInfo* pCinfo; + NV_ASSERT(hRmDevice); + + // Configure PLLA. Requested frequency must always exactly match one of the + // fixed audio frequencies. + AudioTargetKHz = AudioSyncKHz; + Ap15PllAConfigure(hRmDevice, &AudioTargetKHz); + NV_ASSERT(AudioTargetKHz == AudioSyncKHz); + + // Use PLLA as audio sync source, and disable doublers. + // (verify if SoC supports audio sync selectors) + AudioSyncSource = NvRmClockSource_PllA0; + if (NvRmPrivGetClockSourceHandle(NvRmClockSource_AudioSync)) + { + pCinfo = NvRmPrivGetClockSourceHandle( + NvRmClockSource_AudioSync)->pInfo.pSelector; + NvRmPrivSelectorClockSet(hRmDevice, pCinfo, AudioSyncSource, NV_FALSE); + } + if (NvRmPrivGetClockSourceHandle(NvRmClockSource_MpeAudio)) + { + pCinfo = NvRmPrivGetClockSourceHandle( + NvRmClockSource_MpeAudio)->pInfo.pSelector; + NvRmPrivSelectorClockSet(hRmDevice, pCinfo, AudioSyncSource, NV_FALSE); + } +} + +/*****************************************************************************/ + +static void +Ap15PllDControl( + NvRmDeviceHandle hRmDevice, + NvBool Enable) +{ + NvU32 reg; + NvRmModuleClockInfo* pCinfo = NULL; + NvRmModuleClockState* pCstate = NULL; + NV_ASSERT_SUCCESS(NvRmPrivGetClockState( + hRmDevice, NvRmModuleID_Dsi, &pCinfo, &pCstate)); + + if (Enable) + { + Ap15PllControl(hRmDevice, NvRmClockSource_PllD0, NV_TRUE); + pCstate->actual_freq = + NvRmPrivGetClockSourceFreq(NvRmClockSource_PllD0); + return; + } + + // Disable PLLD if it is not used by either display head or DSI + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->ClkEnableOffset); + if (NvRmPrivIsSourceSelectedByModule(hRmDevice, NvRmClockSource_PllD0, + NVRM_MODULE_ID(NvRmModuleID_Display, 0)) || + NvRmPrivIsSourceSelectedByModule(hRmDevice, NvRmClockSource_PllD0, + NVRM_MODULE_ID(NvRmModuleID_Display, 1)) || + ((reg & pCinfo->ClkEnableField) == pCinfo->ClkEnableField)) + return; + + Ap15PllControl(hRmDevice, NvRmClockSource_PllD0, NV_FALSE); + pCstate->actual_freq = + NvRmPrivGetClockSourceFreq(NvRmClockSource_PllD0); +} + +static void +Ap15PllDConfigure( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz TargetFreq) +{ + NvRmFreqKHz MaxFreq = NvRmPrivGetSocClockLimits(NvRmModuleID_Dsi)->MaxKHz; + NvRmModuleClockInfo* pCinfo = NULL; + NvRmModuleClockState* pCstate = NULL; + NV_ASSERT_SUCCESS(NvRmPrivGetClockState( + hRmDevice, NvRmModuleID_Dsi, &pCinfo, &pCstate)); + + /* + * PLLD is adjusted when DDK/ODM is initializing DSI or reconfiguring + * display clock (for HDMI, DSI, or in some cases CRT). + */ + if (NvRmIsFixedHdmiKHz(TargetFreq)) + { + // 480p or 720p or 1080i/1080p HDMI - use fixed PLLD configuration + NvRmPrivAp15PllConfigureHdmi( + hRmDevice, NvRmClockSource_PllD0, &TargetFreq); + } + else + { + // for other targets use simple variable configuration + NV_ASSERT(TargetFreq <= MaxFreq); + NvRmPrivAp15PllConfigureSimple( + hRmDevice, NvRmClockSource_PllD0, MaxFreq, &TargetFreq); + } + + // Update DSI clock state (PLLD is a single source, no divider) + pCstate->SourceClock = 0; + pCstate->Divider = 1; + pCstate->actual_freq = + NvRmPrivGetClockSourceFreq(NvRmClockSource_PllD0); + NvRmPrivModuleVscaleReAttach(hRmDevice, + pCinfo, pCstate, pCstate->actual_freq, pCstate->actual_freq, NV_FALSE); +} + +/*****************************************************************************/ +/*****************************************************************************/ + +static void +Ap15DisplayClockConfigure( + NvRmDeviceHandle hRmDevice, + NvRmModuleClockInfo *pCinfo, + NvRmFreqKHz MinFreq, + NvRmFreqKHz MaxFreq, + NvRmFreqKHz TargetFreq, + NvRmModuleClockState* pCstate, + NvU32 flags) +{ + NvU32 i; + NvRmClockSource SourceId; + NvRmFreqKHz PixelFreq = TargetFreq; + NvRmFreqKHz SourceClockFreq = NvRmPrivGetClockSourceFreq(NvRmClockSource_ClkM); + + // Clip target to maximum - we still may be able to configure frequency + // within tolearnce range + PixelFreq = TargetFreq = NV_MIN(TargetFreq, MaxFreq); + + /* + * Display clock source selection policy: + * - if MIPI flag is specified - use PLLD, and reconfigure it as necessary + * - else if Oscillator output provides required accuracy - use Oscillator + * - else if PLLP fixed output provides required accuracy - use fixed PLLP + * - else if PPLC is used by other head - use PLLD, and reconfigure it as + * necessary + * - else - use use PLLC, and reconfigure it as necessary + */ + if (flags & NvRmClockConfig_MipiSync) + { + // PLLD requested + SourceId = NvRmClockSource_PllD0; + Ap15PllDConfigure(hRmDevice, TargetFreq); + } + else if (NvRmIsFreqRangeReachable( + SourceClockFreq, MinFreq, MaxFreq, NVRM_DISPLAY_DIVIDER_MAX)) + { + // Target frequency is reachable from Oscillator - nothing to do + SourceId = NvRmClockSource_ClkM; + } + else if (NvRmIsFreqRangeReachable(NVRM_PLLP_FIXED_FREQ_KHZ, + MinFreq, MaxFreq, NVRM_DISPLAY_DIVIDER_MAX)) + { + // Target frequency is reachable from PLLP0 - make sure it is enabled + SourceId = NvRmClockSource_PllP0; + Ap15PllPConfigure(hRmDevice); + } + else if (NvRmPrivIsSourceSelectedByModule(hRmDevice, NvRmClockSource_PllC0, + NVRM_MODULE_ID(pCinfo->Module, (1 - pCinfo->Instance)))) + { + // PLLC is used by the other head - only PLLD left + SourceId = NvRmClockSource_PllD0; + Ap15PllDConfigure(hRmDevice, TargetFreq); + } + else + { + // PLLC is available - use it + SourceId = NvRmClockSource_PllC0; + if (!NvRmIsFixedHdmiKHz(TargetFreq)) // don't touch HDMI targets + { + TargetFreq = NvRmPrivGetMaxFreqPllC(hRmDevice); // Target PLLC max + if (!NvRmIsFreqRangeReachable( + TargetFreq, MinFreq, MaxFreq, NVRM_DISPLAY_DIVIDER_MAX)) + { + TargetFreq = MaxFreq; // Target pixel range max + } + } + NvRmPrivReConfigurePllC(hRmDevice, TargetFreq); + } + + // Fill in clock state + for (i = 0; i < NvRmClockSource_Num; i++) + { + if (pCinfo->Sources[i] == SourceId) + break; + } + NV_ASSERT(i < NvRmClockSource_Num); + pCstate->SourceClock = i; // source index + pCstate->Divider = 1; // no divider (display driver has its own) + pCstate->actual_freq = NvRmPrivGetClockSourceFreq(SourceId); // source KHz + NV_ASSERT(NvRmIsFreqRangeReachable( + pCstate->actual_freq, MinFreq, MaxFreq, NVRM_DISPLAY_DIVIDER_MAX)); + + if (flags & NvRmClockConfig_SubConfig) + { + NvRmModuleClockInfo* pTvDacInfo = NULL; + NvRmModuleClockState* pTvDacState = NULL; + NV_ASSERT_SUCCESS(NvRmPrivGetClockState( + hRmDevice, NvRmModuleID_Tvo, &pTvDacInfo, &pTvDacState)); + + // TVDAC is the 2nd TVO subclock (CVE is the 1st one) + pTvDacInfo += 2; + pTvDacState += 2; + NV_ASSERT(pTvDacInfo->Module == NvRmModuleID_Tvo); + NV_ASSERT(pTvDacInfo->SubClockId == 2); + + // enable the tvdac clock + if ((hRmDevice->ChipId.Id == 0x15) || (hRmDevice->ChipId.Id == 0x16)) + Ap15EnableTvDacClock(hRmDevice, ModuleClockState_Enable); + else if (hRmDevice->ChipId.Id == 0x20) + Ap20EnableTvDacClock(hRmDevice, ModuleClockState_Enable); + + // Set TVDAC = pixel clock (same source index and calculate divider + // exactly as dc_hal.c does) + pTvDacState->SourceClock = i; + pTvDacState->Divider = + (((pCstate->actual_freq * 2 ) + PixelFreq / 2) / PixelFreq) - 2; + pTvDacState->actual_freq = + (pCstate->actual_freq * 2 ) / (pTvDacState->Divider + 2); + NvRmPrivModuleClockSet(hRmDevice, pTvDacInfo, pTvDacState); + } + if (flags & NvRmClockConfig_DisableTvDAC) + { + // disable the tvdac clock + if ((hRmDevice->ChipId.Id == 0x15) || (hRmDevice->ChipId.Id == 0x16)) + Ap15EnableTvDacClock(hRmDevice, ModuleClockState_Disable); + else if (hRmDevice->ChipId.Id == 0x20) + Ap20EnableTvDacClock(hRmDevice, ModuleClockState_Disable); + } +} + +NvBool +NvRmPrivAp15IsModuleClockException( + NvRmDeviceHandle hRmDevice, + NvRmModuleClockInfo *pCinfo, + NvU32 ClockSourceCount, + NvRmFreqKHz MinFreq, + NvRmFreqKHz MaxFreq, + const NvRmFreqKHz* PrefFreqList, + NvU32 PrefCount, + NvRmModuleClockState* pCstate, + NvU32 flags) +{ + NvU32 i; + NvRmFreqKHz FreqKHz; + NvRmClockSource SourceId; + + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo && PrefFreqList && pCstate); + + switch (pCinfo->Module) + { + case NvRmModuleID_Display: + /* + * Special handling for display clocks. Must satisfy requirements + * for the 1st requested frequency and complete configuration. + * Note that AP15 display divider is within module itself, so the + * input request is for pisxel clock, but output *pCstate specifies + * source frequency. Display driver will configure divider. + */ + Ap15DisplayClockConfigure(hRmDevice, pCinfo, + MinFreq, MaxFreq, PrefFreqList[0], pCstate, flags); + return NV_TRUE; + + case NvRmModuleID_Dsi: + /* + * Reconfigure PLLD to match requested frequency, and update DSI + * clock state. + */ + Ap15PllDConfigure(hRmDevice, PrefFreqList[0]); + NV_ASSERT((MinFreq <= pCstate->actual_freq) && + (pCstate->actual_freq <= MaxFreq)); + return NV_TRUE; + + case NvRmModuleID_Hdmi: + /* + * Complete HDMI configuration; choose among possible sources: + * PLLP, PLLD, PLLC in the same order as for display (PLLD or + * PLLC should be already configured properly for display) + */ + if (flags & NvRmClockConfig_MipiSync) + SourceId = NvRmClockSource_PllD0; + else if (NvRmIsFreqRangeReachable(NVRM_PLLP_FIXED_FREQ_KHZ, + MinFreq, MaxFreq, NVRM_DISPLAY_DIVIDER_MAX)) + SourceId = NvRmClockSource_PllP0; + else + SourceId = NvRmClockSource_PllC0; + + // HDMI clock state with selected source + for (i = 0; i < NvRmClockSource_Num; i++) + { + if (pCinfo->Sources[i] == SourceId) + break; + } + NV_ASSERT(i < NvRmClockSource_Num); + pCstate->SourceClock = i; // source index + FreqKHz = NvRmPrivGetClockSourceFreq(SourceId); + pCstate->Divider = ((FreqKHz << 2) + PrefFreqList[0]) / + (PrefFreqList[0] << 1) - 2; + pCstate->actual_freq = (FreqKHz << 1) / (pCstate->Divider + 2); + NV_ASSERT(pCstate->Divider <= pCinfo->DivisorFieldMask); + NV_ASSERT((MinFreq <= pCstate->actual_freq) && + (pCstate->actual_freq <= MaxFreq)); + return NV_TRUE; + + case NvRmModuleID_Spdif: + if (flags & NvRmClockConfig_SubConfig) + return NV_FALSE; // Nothing special for SPDIFIN + // fall through for SPDIFOUT + case NvRmModuleID_I2s: + /* + * If requested, reconfigure PLLA to match target frequency, and + * complete clock configuration with PLLA as a source. Otherwise, + * make sure PLLA is enabled (at current configuration), and + * continue regular configuration for SPDIFOUT and I2S. + */ + if (flags & NvRmClockConfig_AudioAdjust) + { + FreqKHz = PrefFreqList[0]; + Ap15PllAConfigure(hRmDevice, &FreqKHz); + + pCstate->SourceClock = 0; // PLLA source index + pCstate->Divider = ((FreqKHz << 2) + PrefFreqList[0]) / + (PrefFreqList[0] << 1) - 2; + pCstate->actual_freq = (FreqKHz << 1) / (pCstate->Divider + 2); + if (NvRmPrivGetExecPlatform(hRmDevice) == ExecPlatform_Fpga) + { // Fake return on FPGA (PLLA is not configurable, anyway) + pCstate->actual_freq = PrefFreqList[0]; + } + NV_ASSERT(pCinfo->Sources[pCstate->SourceClock] == + NvRmClockSource_PllA0); + NV_ASSERT(pCstate->Divider <= pCinfo->DivisorFieldMask); + NV_ASSERT((MinFreq <= pCstate->actual_freq) && + (pCstate->actual_freq <= MaxFreq)); + return NV_TRUE; + } + Ap15PllAControl(hRmDevice, NV_TRUE); + return NV_FALSE; + + case NvRmModuleID_Usb2Otg: + /* + * Reconfigure PLLU to match requested frequency, and complete USB + * clock configuration (PLLU is a single source, no divider) + */ + Ap15PllUConfigure(hRmDevice, PrefFreqList[0]); + pCstate->SourceClock = 0; + pCstate->Divider = 1; + pCstate->actual_freq = + NvRmPrivGetClockSourceFreq(pCinfo->Sources[0]); + return NV_TRUE; + + default: + // No exception for other modules - continue regular configuration + return NV_FALSE; + } +} + +/*****************************************************************************/ + +void +NvRmPrivAp15DisablePLLs( + NvRmDeviceHandle hRmDevice, + const NvRmModuleClockInfo* pCinfo, + const NvRmModuleClockState* pCstate) +{ +#if !NV_OAL + switch (pCinfo->Module) + { + case NvRmModuleID_Display: + NvRmPrivBoostPllC(hRmDevice); + Ap15PllDControl(hRmDevice, NV_FALSE); + break; + + case NvRmModuleID_Spdif: + case NvRmModuleID_I2s: + Ap15PllAControl(hRmDevice, NV_FALSE); + break; + + default: + break; + } +#endif +} + +void +NvRmPrivAp15PllDPowerControl( + NvRmDeviceHandle hRmDevice, + NvBool ConfigEntry, + NvBool* pMipiPllVddOn) +{ +#if !NV_OAL + if (ConfigEntry) + { + // On entry to display clock configuration get PLLD power ready + if (!(*pMipiPllVddOn)) + { + NvRmPrivPmuRailControl(hRmDevice, NV_VDD_PLLD_ODM_ID, NV_TRUE); + *pMipiPllVddOn = NV_TRUE; + } + } + else + { + // On exit from display clock configuration turn off PLLD power + // if it is disabled + if ((*pMipiPllVddOn) && + (NvRmPrivGetClockSourceFreq(NvRmClockSource_PllD0) == 0)) + { + NvRmPrivPmuRailControl(hRmDevice, NV_VDD_PLLD_ODM_ID, NV_FALSE); + *pMipiPllVddOn = NV_FALSE; + } + } +#endif +} + +void +NvRmPrivConfigureClockSource( + NvRmDeviceHandle hRmDevice, + NvRmModuleID ModuleId, + NvBool enable) +{ + // Extract module and instance from composite module id. + NvU32 Module = NVRM_MODULE_ID_MODULE( ModuleId ); + + switch (Module) + { + case NvRmModuleID_Usb2Otg: + // Do not disable the PLLU clock once it is enabled + // Set PLLU default configuration if it is not already configured + if (enable) + Ap15PllUConfigure(hRmDevice, NvRmFreqUnspecified); + break; +#if !NV_OAL + case NvRmModuleID_Spdif: + case NvRmModuleID_I2s: + if (enable) + { + // Do not enable if PLLA is not used as a source for any clock + if (NvRmPrivGetDfsFlags(hRmDevice) & NvRmDfsStatusFlags_StopPllA0) + break; + } + // fall through + case NvRmModuleID_Mpe: + Ap15PllAControl(hRmDevice, enable); + break; + + case NvRmModuleID_Dsi: + Ap15PllDControl(hRmDevice, enable); + break; + + case NvRmPrivModuleID_Pcie: + NvRmPrivAp20PllEControl(hRmDevice, enable); + break; +#endif + default: + break; + } + return; +} + +/*****************************************************************************/ +/*****************************************************************************/ + +/* + * Basic DFS clock control policy outline: + * - Oscillator ClkM, doubler ClkD, and memory PLLM0 - always available, fixed + * frequency sources. + * - Peripheral PLLP0 may be dynamically enabled / disabled when DFS is stopped + * and CPU is power gated. Hence, when DFS is running it is always enabled + * and configured at fixed PLLP0 frequency. + * - Cpu PLLC0 may be dynamically enabled / disabled when DFS is stopped and + * CPU is power gated. Hence, when DFS is running it is always enabled. PLLC0 + * is commonly configured at maximum CPU domain frequency. If necessary, it + * may be adjusted to provide required display pixel clock frequency. + * - System buses, and MC/EMC configuration, clock source multiplexes and + * dividers, as well as PLLP2, PLLP4 and PLLM1 dividers are under exclusive + * DFS control, and are not accessed by any other code except bootloader + * before RM is open. + */ + +// Limit frequencies ratio for Vpipe : System >= 1 : 2^(value - 1) +#define LIMIT_SYS_TO_VDE_RATIO (2) + +// Limit frequencies ratio for AHB : System >= 1:2 and APB : System >= 1 : 4 +#define LIMIT_SYS_TO_AHB_APB_RATIOS (1) + +// PLLP2 must be used as a variable source for System clock. +#define PLLP_POLICY_ENTRY(KHz) \ + { NvRmClockSource_PllP2,\ + (NVRM_PLLP_FIXED_FREQ_KHZ * 2)/((NVRM_PLLP_FIXED_FREQ_KHZ * 2)/KHz),\ + ((NVRM_PLLP_FIXED_FREQ_KHZ * 2)/KHz - 2)\ + }, +static const NvRmDfsSource s_Ap15PllPSystemClockPolicy[] = +{ + NVRM_AP15_PLLP_POLICY_SYSTEM_CLOCK +}; +static const NvU32 s_Ap15PllPSystemClockPolicyEntries = + NV_ARRAY_SIZE(s_Ap15PllPSystemClockPolicy); +#undef PLLP_POLICY_ENTRY + + +// PLLP4 must be used as a variable source for cpu clock. +#define PLLP_POLICY_ENTRY(KHz) \ + { NvRmClockSource_PllP4,\ + (NVRM_PLLP_FIXED_FREQ_KHZ * 2)/((NVRM_PLLP_FIXED_FREQ_KHZ * 2)/KHz),\ + ((NVRM_PLLP_FIXED_FREQ_KHZ * 2)/KHz - 2)\ + }, +static const NvRmDfsSource s_Ap15PllPCpuClockPolicy[] = +{ + NVRM_AP15_PLLP_POLICY_CPU_CLOCK +}; +static const NvU32 s_Ap15PllPCpuClockPolicyEntries = + NV_ARRAY_SIZE(s_Ap15PllPCpuClockPolicy); +#undef PLLP_POLICY_ENTRY + +/* + * Sorted list of timing parameters for discrete set of EMC frequencies used + * by DFS: entry 0 specifies timing parameters for PLLM0 output frequency, + * entry n (n = 1, 2, ... number of EMC steps-1) specifies timing parameters + * for EMC frequency = PLLM0 frequency / (2 * n); thus only frequencies evenly + * divided down from PLLM0 will be used by DFS + */ +static NvRmAp15EmcTimingConfig +s_Ap15EmcConfigSortedTable[NVRM_AP15_DFS_EMC_FREQ_STEPS]; + +static struct MemClocksRec +{ + // Index of selected EMC configuration entry + NvU32 Index; + + // Pointers to EMC and MC clock descriptors + NvRmModuleClockInfo* pEmcInfo; + NvRmModuleClockInfo* pMcInfo; + + // Pointers to EMC and MC clock state records + NvRmModuleClockState* pEmcState; + NvRmModuleClockState* pMcState; + +} s_MemClocks = {0}; + +static const NvU32 s_Cpu2EmcRatioPolicyTable[] = +{ + NVRM_AP15_CPU_EMC_RATIO_POLICY +}; + +/*****************************************************************************/ + +static void +Ap15Emc2xFreqGet( + NvRmDeviceHandle hRmDevice) +{ + NvU32 reg; + NvRmFreqKHz SourceClockFreq; + NvRmModuleClockInfo* pCinfo = s_MemClocks.pEmcInfo; + NvRmModuleClockState* pCstate = s_MemClocks.pEmcState; + + NV_ASSERT(pCinfo && pCstate); + + // Determine EMC2x source and divider setting; update EMC2x clock state + reg = NV_REGR(hRmDevice, + NvRmPrivModuleID_ClockAndReset, 0, pCinfo->ClkSourceOffset); + pCstate->Divider = + ((reg >> pCinfo->DivisorFieldShift) & pCinfo->DivisorFieldMask); + pCstate->SourceClock = + ((reg >> pCinfo->SourceFieldShift) & pCinfo->SourceFieldMask); + SourceClockFreq = + NvRmPrivGetClockSourceFreq(pCinfo->Sources[pCstate->SourceClock]); + + // Fractional divider output = (Source Frequency * 2) / (divider + 2) + pCstate->actual_freq = ((SourceClockFreq << 1) / (pCstate->Divider + 2)); +} + +// Enable/Disable EMC low-latency return-fifo reservation scheme +// (enable requires confirmation polling) +#define NVRM_AP15_EMCLL_RETRSV_ENABLE \ +do\ +{\ + NvU32 reg; \ + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, \ + 0, EMC_LL_ARB_CONFIG_0); \ + reg = NV_FLD_SET_DRF_DEF( \ + EMC, LL_ARB_CONFIG, LL_RETRSV_ENABLE, ENABLED, reg); \ + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, \ + 0, EMC_LL_ARB_CONFIG_0, reg); \ + while (reg != NV_REGR(hRmDevice, NvRmPrivModuleID_ExternalMemoryController,\ + 0, EMC_LL_ARB_CONFIG_0)) \ + ; \ +} while(0) + +#define NVRM_AP15_EMCLL_RETRSV_DISABLE \ +do\ +{\ + NvU32 reg; \ + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, \ + 0, EMC_LL_ARB_CONFIG_0); \ + reg = NV_FLD_SET_DRF_DEF( \ + EMC, LL_ARB_CONFIG, LL_RETRSV_ENABLE, DISABLED, reg); \ + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, \ + 0, EMC_LL_ARB_CONFIG_0, reg); \ +} while (0) + +void +NvRmPrivAp15SetEmcForCpuSrcSwitch(NvRmDeviceHandle hRmDevice) +{ + NVRM_AP15_EMCLL_RETRSV_ENABLE; +} + +void +NvRmPrivAp15SetEmcForCpuDivSwitch( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz CpuFreq, + NvBool Before) +{ + NvRmFreqKHz EmcFreq = (s_MemClocks.pEmcState->actual_freq >> 1); + if (Before && (CpuFreq < EmcFreq)) + { + NVRM_AP15_EMCLL_RETRSV_ENABLE; + } + else if (!Before && (CpuFreq >= EmcFreq)) + { + NVRM_AP15_EMCLL_RETRSV_DISABLE; + } +} + +static void +Ap15EmcTimingSet( + NvRmDeviceHandle hRmDevice, + NvBool FreqRising, + NvBool BeforeDividerChange, + const NvRmAp15EmcTimingConfig* pEmcConfig) +{ + // Write shadow timing registers + if (FreqRising == BeforeDividerChange) // "overlap down" parameters + { + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_TIMING0_0, pEmcConfig->Timing0Reg); + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_TIMING1_0, pEmcConfig->Timing1Reg); + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_TIMING2_0, pEmcConfig->Timing2Reg); + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_TIMING3_0, pEmcConfig->Timing3Reg); + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_TIMING5_0, pEmcConfig->Timing5Reg); + } + else // "overlap up" parameters + { + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_TIMING4_0, pEmcConfig->Timing4Reg); + + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_FBIO_CFG6_0, pEmcConfig->FbioCfg6Reg); + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_FBIO_DQSIB_DLY_0, pEmcConfig->FbioDqsibDly); + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_FBIO_QUSE_DLY_0, pEmcConfig->FbioQuseDly); + } + // Trigger active register update from shadow + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_TIMING_CONTROL_0, 0x1); + + // Make sure update from shadow is completed + if (FreqRising == BeforeDividerChange) + { + while((NV_REGR(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_TIMING0_0)) != pEmcConfig->Timing0Reg); + } + else + { + while((NV_REGR(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_TIMING4_0)) != pEmcConfig->Timing4Reg); + // Re-trigger active register update (need it for trimmers only) + NV_REGW(hRmDevice, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_TIMING_CONTROL_0, 0x1); + } +} + +static void +Ap15Emc2xClockSet( + NvRmDeviceHandle hRmDevice, + NvBool FreqRising, + const NvRmAp15EmcTimingConfig* pEmcConfig) +{ + NvU32 reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0); + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, CLK_SOURCE_EMC, + EMC_2X_CLK_DIVISOR, pEmcConfig->Emc2xDivisor, reg); + NV_ASSERT(pEmcConfig->Emc2xKHz); // validate table entry + + // Update EMC state + s_MemClocks.pEmcState->actual_freq = pEmcConfig->Emc2xKHz; + s_MemClocks.pEmcState->Divider = pEmcConfig->Emc2xDivisor; + + // Set EMC parameters and EMC divisor (the EMC clock source is always + // PLLM0 starting from BL) + Ap15EmcTimingSet(hRmDevice, FreqRising, NV_TRUE, pEmcConfig); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0, reg); + Ap15EmcTimingSet(hRmDevice, FreqRising, NV_FALSE, pEmcConfig); +} + +static void +Ap15McClockSet( + NvRmDeviceHandle hRmDevice, + const NvRmAp15EmcTimingConfig* pEmcConfig) +{ + NvU32 src, div; + NvU32 reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_SOURCE_MEM_0); + src = NV_DRF_VAL(CLK_RST_CONTROLLER, CLK_SOURCE_MEM, MEM_CLK_SRC, reg); + div = NV_DRF_VAL(CLK_RST_CONTROLLER, CLK_SOURCE_MEM, MEM_CLK_DIVISOR, reg); + + // Update MC state + s_MemClocks.pMcState->actual_freq = pEmcConfig->McKHz; + s_MemClocks.pMcState->SourceClock = pEmcConfig->McClockSource; + s_MemClocks.pMcState->Divider = pEmcConfig->McDivisor; + + // Set MC divisor before source, if new value is bigger than the old one + if (pEmcConfig->McDivisor > div) + { + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, CLK_SOURCE_MEM, + MEM_CLK_DIVISOR, pEmcConfig->McDivisor, reg); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_SOURCE_MEM_0, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + } + + // Modify MC source if it is to be changed + if (pEmcConfig->McClockSource != src) + { + NvRmPrivMemoryClockReAttach( + hRmDevice, s_MemClocks.pMcInfo, s_MemClocks.pMcState); + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, CLK_SOURCE_MEM, + MEM_CLK_SRC, pEmcConfig->McClockSource, reg); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_SOURCE_MEM_0, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + } + + // Set MC divisor after source, if new value is smaller than the old one + if (pEmcConfig->McDivisor < div) + { + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, CLK_SOURCE_MEM, + MEM_CLK_DIVISOR, pEmcConfig->McDivisor, reg); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_SOURCE_MEM_0, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + } +} + +void +NvRmPrivAp15EmcConfigInit(NvRmDeviceHandle hRmDevice) +{ + NvU32 i, j, k, reg=0; + NvU32 ConfigurationsCount; + NvRmFreqKHz Emc2xKHz, McKHz, McMax; + NvRmFreqKHz PllM0KHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllM0); + const NvOdmSdramControllerConfig* pEmcConfigurations = + NvOdmQuerySdramControllerConfigGet(&ConfigurationsCount, ®); + + // Init memory configuration structure + NV_ASSERT_SUCCESS(NvRmPrivGetClockState( + hRmDevice, NvRmPrivModuleID_ExternalMemoryController, + &s_MemClocks.pEmcInfo, &s_MemClocks.pEmcState)); + NV_ASSERT_SUCCESS(NvRmPrivGetClockState( + hRmDevice, NvRmPrivModuleID_MemoryController, + &s_MemClocks.pMcInfo, &s_MemClocks.pMcState)); + s_MemClocks.Index = NVRM_AP15_DFS_EMC_FREQ_STEPS; // invalid index + NvOsMemset(s_Ap15EmcConfigSortedTable, 0, // clean table + sizeof(s_Ap15EmcConfigSortedTable)); + + // Get EMC2x clock state from h/w + Ap15Emc2xFreqGet(hRmDevice); + + // Check if configuration table is provided by ODM + if ((ConfigurationsCount == 0) || (pEmcConfigurations == NULL)) + { + s_Ap15EmcConfigSortedTable[0].Emc2xKHz = 0; // invalidate PLLM0 entry + return; + } + if (reg != NV_EMC_BASIC_REV) + { + s_Ap15EmcConfigSortedTable[0].Emc2xKHz = 0; // invalidate PLLM0 entry + NV_ASSERT(!"Invalid configuration table revision"); + return; + } + + // Check PLLM0 range + NV_ASSERT(PllM0KHz); + if (PllM0KHz > (NvRmPrivGetSocClockLimits( + NvRmPrivModuleID_ExternalMemoryController)->MaxKHz)) + { + s_Ap15EmcConfigSortedTable[0].Emc2xKHz = 0; // invalidate PLLM0 entry + NV_ASSERT(!"PLLM0 is outside supported EMC range"); + return; + } + + // Check if PLLM0 is configured by boot loader as EMC clock source + // (it can not and will not be changed by RM) + if (s_MemClocks.pEmcState->SourceClock != + CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0_EMC_2X_CLK_SRC_PLLM_OUT0) + { + s_Ap15EmcConfigSortedTable[0].Emc2xKHz = 0; // invalidate PLLM0 entry + NV_ASSERT(!"Other than PLLM0 clock source is used for EMC"); + return; + } + + // Sort list of EMC timing parameters in descending order of frequencies + // evenly divided down from PLLM0; find matching entry for boot divisor + for (i = 0, k = 0, Emc2xKHz = PllM0KHz; i < NVRM_AP15_DFS_EMC_FREQ_STEPS; ) + { + s_Ap15EmcConfigSortedTable[i].Emc2xKHz = 0; // mark entry invalid + for (j = 0; j < ConfigurationsCount; j++) + { + // Find match with 1MHz tolerance for allowed configuration + if ((Emc2xKHz <= (pEmcConfigurations[j].SdramKHz * 2 + 1000)) && + (Emc2xKHz >= (pEmcConfigurations[j].SdramKHz * 2 - 1000))) + { + s_Ap15EmcConfigSortedTable[i].Timing0Reg = pEmcConfigurations[j].EmcTiming0; + s_Ap15EmcConfigSortedTable[i].Timing1Reg = pEmcConfigurations[j].EmcTiming1; + s_Ap15EmcConfigSortedTable[i].Timing2Reg = pEmcConfigurations[j].EmcTiming2; + s_Ap15EmcConfigSortedTable[i].Timing3Reg = pEmcConfigurations[j].EmcTiming3; + s_Ap15EmcConfigSortedTable[i].Timing4Reg = pEmcConfigurations[j].EmcTiming4; + s_Ap15EmcConfigSortedTable[i].Timing5Reg = pEmcConfigurations[j].EmcTiming5; + + s_Ap15EmcConfigSortedTable[i].FbioCfg6Reg = + pEmcConfigurations[j].EmcFbioCfg6; + s_Ap15EmcConfigSortedTable[i].FbioDqsibDly = + pEmcConfigurations[j].EmcFbioDqsibDly + + NvRmPrivGetEmcDqsibOffset(hRmDevice); + s_Ap15EmcConfigSortedTable[i].FbioQuseDly = + pEmcConfigurations[j].EmcFbioQuseDly; + s_Ap15EmcConfigSortedTable[i].CoreVoltageMv = + pEmcConfigurations[j].EmcCoreVoltageMv; + + // Determine EMC and MC clock divisors, MC clock source + // (EMC always uses PLLM0 as a source), and CPU clock limit + s_Ap15EmcConfigSortedTable[i].Emc2xKHz = Emc2xKHz; // accurate KHz + if (i == 0) + { + /* + * The first table entry specifies parameters for EMC2xFreq + * = PLLM0 frequency; the divisor field in EMC fractional + * divider register is set to "0". The divisor field in MC + * divider is set to "1", so that Emc1xFreq ~ 75% of McFreq + * using PLLM0 as MC clock source, if maximum MC frequency + * limit is not violated. Otherwise, find the highest MC + * frequency below the limit with PLLP0 as a source. + */ + s_Ap15EmcConfigSortedTable[i].Emc2xDivisor = 0; + McKHz = (PllM0KHz * 2) / 3; + McMax = NvRmPrivGetSocClockLimits( + NvRmPrivModuleID_MemoryController)->MaxKHz; + NV_ASSERT(McMax); + if (McKHz <= McMax) + { + s_Ap15EmcConfigSortedTable[i].McDivisor = 1; + s_Ap15EmcConfigSortedTable[i].McClockSource = + CLK_RST_CONTROLLER_CLK_SOURCE_MEM_0_MEM_CLK_SRC_PLLM_OUT0; + } + else if (NVRM_PLLP_FIXED_FREQ_KHZ <= McMax) + { + McKHz = NVRM_PLLP_FIXED_FREQ_KHZ; + s_Ap15EmcConfigSortedTable[i].McDivisor = 0; + s_Ap15EmcConfigSortedTable[i].McClockSource = + CLK_RST_CONTROLLER_CLK_SOURCE_MEM_0_MEM_CLK_SRC_PLLP_OUT0; + } + else + { + reg = (2 * NVRM_PLLP_FIXED_FREQ_KHZ + McMax - 1) / McMax; + McKHz = (2 * NVRM_PLLP_FIXED_FREQ_KHZ) / reg; + s_Ap15EmcConfigSortedTable[i].McDivisor = reg - 2; + s_Ap15EmcConfigSortedTable[i].McClockSource = + CLK_RST_CONTROLLER_CLK_SOURCE_MEM_0_MEM_CLK_SRC_PLLP_OUT0; + } + } + else + { + /* + * If i = 1, 2, ... the table entry specifies parameters + * for EMC2xFreq = PLLM0 frequency/(2 * k); the divisor + * field in EMC fractional divider register should be set + * as 2 * (2 * k) - 2 = 4 * k - 2. The divisor field in MC + * divider is determined so that Emc1xFreq ~ 85% of McFreq + * using the same PLLM0 as MC clock source + */ + s_Ap15EmcConfigSortedTable[i].Emc2xDivisor = (k << 2) - 2; + s_Ap15EmcConfigSortedTable[i].McDivisor = (19 + + 17 * s_Ap15EmcConfigSortedTable[i].Emc2xDivisor) / 10; + s_Ap15EmcConfigSortedTable[i].McClockSource = + CLK_RST_CONTROLLER_CLK_SOURCE_MEM_0_MEM_CLK_SRC_PLLM_OUT0; + McKHz = 2 * PllM0KHz / + (s_Ap15EmcConfigSortedTable[i].McDivisor + 2); + } + if (s_Ap15EmcConfigSortedTable[i].Emc2xDivisor == + s_MemClocks.pEmcState->Divider) + { + s_MemClocks.Index = i; // Boot configuration found + } + s_Ap15EmcConfigSortedTable[i].McKHz = McKHz; + /* + * H/w CPU clock limit is determined from inequality: + * 1 mcclk period + 12 cpuclk periods >= 2 emcclck periods, or + * CpuKHz <= 11.9 * McKHz * Emc2xKHz / (4 * McKHz - Emc2xKHz) + * with 0.1/12 ~ 0.8% margin + * S/w CPU clock limit is determined per s/w policy: + * CpuKHz <= CpuMax * PolicyTabel[PLLM0/(2*EMC2xKHz)] / 256 + * Final CPU clock limit is minimum of the above limits + */ + s_Ap15EmcConfigSortedTable[i].CpuLimitKHz = + (NvU32)NvDiv64(((NvU64)Emc2xKHz * McKHz * 119), + (((McKHz << 2) - Emc2xKHz) * 10)); + reg = NvRmPrivGetSocClockLimits(NvRmModuleID_Cpu)->MaxKHz; + if (k != 0) + { + NV_ASSERT(k < NV_ARRAY_SIZE(s_Cpu2EmcRatioPolicyTable)); + reg = (reg * s_Cpu2EmcRatioPolicyTable[k]) >> 8; + } + if (s_Ap15EmcConfigSortedTable[i].CpuLimitKHz > reg) + s_Ap15EmcConfigSortedTable[i].CpuLimitKHz = reg; + + break; + } + } + if (s_Ap15EmcConfigSortedTable[i].Emc2xKHz != 0) + i++; // Entry found - advance sorting index + else if (i == 0) + break; // PLLM0 entry not found - abort sorting + + Emc2xKHz = PllM0KHz / ((++k) << 1); + if (Emc2xKHz < NvRmPrivGetSocClockLimits( + NvRmPrivModuleID_ExternalMemoryController)->MinKHz) + break; // Abort sorting at minimum EMC frequency + } + // Check if match for boot configuration found + if (s_MemClocks.Index == NVRM_AP15_DFS_EMC_FREQ_STEPS) + s_Ap15EmcConfigSortedTable[0].Emc2xKHz = 0; // invalidate PLLM0 entry +} + +static NvBool +Ap15Emc2xClockSourceFind( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz MaxKHz, + NvRmFreqKHz DomainKHz, + NvRmFreqKHz* pCpuTargetKHz, + NvRmDfsSource* pDfsSource) +{ + NvU32 i; + NvBool FinalStep = NV_TRUE; + NV_ASSERT(DomainKHz <= MaxKHz); + pDfsSource->DividerSetting = 0; // no divider + + // If PLLM0 entry in EMC frequeuncies table is invalid, EMC frequency + // will not be scaled; just fill in current EMC frequency + if (s_Ap15EmcConfigSortedTable[0].Emc2xKHz == 0) + { + pDfsSource->SourceId = NvRmClockSource_Invalid; + pDfsSource->SourceKHz = s_MemClocks.pEmcState->actual_freq; + pDfsSource->MinMv = NvRmVoltsMaximum; // no v-scaling in this case + return FinalStep; + } + + // Only PLLM0 is used as EMC frequency source by DFS; its frequency is + // always within h/w limits + pDfsSource->SourceId = NvRmClockSource_PllM0; + NV_ASSERT(s_Ap15EmcConfigSortedTable[0].Emc2xKHz <= MaxKHz); + + // Search sorted pre-defind EMC frequencies (divided down from PLLM0) for + // the entry above and closest to the traget that also has CPU limit above + // the CPU target. Use PLLM0 entry if not found. + for (i = NVRM_AP15_DFS_EMC_FREQ_STEPS; i > 0;) + { + i--; + if ((DomainKHz <= s_Ap15EmcConfigSortedTable[i].Emc2xKHz) && + (*pCpuTargetKHz <= s_Ap15EmcConfigSortedTable[i].CpuLimitKHz)) + break; + } + + // Make sure the new entry is adjacent to the current (one step at a time) + if (i > (s_MemClocks.Index + 1)) + { + i = s_MemClocks.Index + 1; + FinalStep = NV_FALSE; // need more steps to reach target + } + else if ((i + 1) < s_MemClocks.Index) + { + i = s_MemClocks.Index - 1; + FinalStep = NV_FALSE; // need more steps to reach target + } + + // Record found EMC entry, and limit CPU target if necessary + pDfsSource->DividerSetting = i; + pDfsSource->SourceKHz = s_Ap15EmcConfigSortedTable[i].Emc2xKHz; + if (*pCpuTargetKHz > s_Ap15EmcConfigSortedTable[i].CpuLimitKHz) + *pCpuTargetKHz = s_Ap15EmcConfigSortedTable[i].CpuLimitKHz; + pDfsSource->MinMv = s_Ap15EmcConfigSortedTable[i].CoreVoltageMv; + return FinalStep; +} + +static void +Ap15Emc2xClockConfigure( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz MaxKHz, + NvRmFreqKHz* pDomainKHz, + const NvRmDfsSource* pDfsSource) +{ + NvU32 Index; + NvRmFreqKHz CpuFreq = NvRmPrivGetClockSourceFreq(NvRmClockSource_CpuBus); + + // Always return the requested source frequency + *pDomainKHz = pDfsSource->SourceKHz; + NV_ASSERT(*pDomainKHz); + + // If other than PLLM0 source is selected, EMC frequency is not scaled. + if (pDfsSource->SourceId != NvRmClockSource_PllM0) + return; + + // Divider settings in EMC source descriptor is an index into the table of + // pre-defined EMC configurations in descending frequency order. + Index = pDfsSource->DividerSetting; + if (Index == s_MemClocks.Index) + return; // do nothing new index is the same as current + + // In case of EMC frequency increase: check if EMC LL reservation should + // be enabled, reconfigure EMC, then MC (make sure MC never exceeds EMC2x) + // In case of EMC frequency decrease: reconfigure MC, then EMC (make sure + // MC never exceeds EMC2x) and check if EMC LL reservation can be disabled + if (Index < s_MemClocks.Index) + { + if (CpuFreq < (*pDomainKHz >> 1)) + { + NVRM_AP15_EMCLL_RETRSV_ENABLE; + } + Ap15Emc2xClockSet( + hRmDevice, NV_TRUE, &s_Ap15EmcConfigSortedTable[Index]); + Ap15McClockSet(hRmDevice, &s_Ap15EmcConfigSortedTable[Index]); + } + else + { + Ap15McClockSet(hRmDevice, &s_Ap15EmcConfigSortedTable[Index]); + Ap15Emc2xClockSet( + hRmDevice, NV_FALSE, &s_Ap15EmcConfigSortedTable[Index]); + + if (CpuFreq >= (*pDomainKHz >> 1)) + { + NVRM_AP15_EMCLL_RETRSV_DISABLE; + } + } + s_MemClocks.Index = Index; +} + +void +NvRmPrivAp15FastClockConfig(NvRmDeviceHandle hRmDevice) +{ +#if !NV_OAL + NvU32 divm1, divp2; + NvRmFreqKHz SclkKHz, CpuKHz, PllP2KHz, PllM1KHz; + NvRmFreqKHz FreqKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllM0); + + // Set fastest EMC/MC configuration provided PLLM0 boot frequency matches + // one of the pre-defined configurations, i.e, it is the first entry in the + // sorted table + if (s_Ap15EmcConfigSortedTable[0].Emc2xKHz == FreqKHz) + { + for (;;) + { + Ap15Emc2xClockSet( + hRmDevice, NV_TRUE, &s_Ap15EmcConfigSortedTable[s_MemClocks.Index]); + Ap15McClockSet(hRmDevice, &s_Ap15EmcConfigSortedTable[s_MemClocks.Index]); + if (s_MemClocks.Index == 0) + break; + s_MemClocks.Index--; + } + } + + // Set AVP/System Bus clock (now, with nominal core voltage it can be up + // to SoC maximum). First determine settings for PLLP and PLLM dividers + // to get maximum possible frequency on PLLP_OUT2 and PLLM_OUT1 outputs. + SclkKHz = NvRmPrivGetSocClockLimits(NvRmPrivModuleID_System)->MaxKHz; + NV_ASSERT(SclkKHz); + + FreqKHz = NVRM_PLLP_FIXED_FREQ_KHZ; + PllP2KHz = SclkKHz; + divp2 = NvRmPrivFindFreqMaxBelow( + NvRmClockDivider_Fractional_2, FreqKHz, PllP2KHz, &PllP2KHz); + + FreqKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllM0); + PllM1KHz = SclkKHz; + divm1 = NvRmPrivFindFreqMaxBelow( + NvRmClockDivider_Fractional_2, FreqKHz, PllM1KHz, &PllM1KHz); + + // Now configure both dividers and select the output with highest frequency + // as a source for the system bus clock; reconfigure MIO as necessary + SclkKHz = NV_MAX(PllM1KHz, PllP2KHz); + FreqKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_SystemBus); + if (FreqKHz < SclkKHz) + { + Ap15MioReconfigure(hRmDevice, SclkKHz); + } + NvRmPrivDividerSet( + hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllP2)->pInfo.pDivider, + divp2); + NvRmPrivDividerSet( + hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_PllM1)->pInfo.pDivider, + divm1); + if (SclkKHz == PllP2KHz) + { + NvRmPrivCoreClockSet(hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_SystemBus)->pInfo.pCore, + NvRmClockSource_PllP2, 0, 0); + } + else + { + NvRmPrivCoreClockSet(hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_SystemBus)->pInfo.pCore, + NvRmClockSource_PllM1, 0, 0); + } + if (FreqKHz >= SclkKHz) + { + Ap15MioReconfigure(hRmDevice, SclkKHz); + } + NvRmPrivBusClockInit(hRmDevice, SclkKHz); + + // Set PLLC and CPU clock to SoC maximum - can be done now, when core + // voltage is guaranteed to be nominal, provided none of the display + // heads is already using PLLC as pixel clock source. + CpuKHz = NvRmPrivGetSocClockLimits(NvRmModuleID_Cpu)->MaxKHz; + FreqKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllC0); + if (CpuKHz != FreqKHz) + { + NvRmPrivBoostPllC(hRmDevice); + } + NvRmPrivCoreClockSet(hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBus)->pInfo.pCore, + NvRmClockSource_PllC0, 0, 0); +#endif +} + +void +NvRmPrivAp15ClipCpuEmcHighLimits( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz* pCpuHighKHz, + NvRmFreqKHz* pEmcHighKHz) +{ +#if !NV_OAL + NvU32 i; + NvRmFreqKHz EmcKHz; + NvRmFreqKHz MinKHz = NvRmPrivDfsGetMinKHz(NvRmDfsClockId_Emc); + NV_ASSERT(pEmcHighKHz && pCpuHighKHz); + + // Nothing to do if no EMC scaling. + if (s_Ap15EmcConfigSortedTable[0].Emc2xKHz == 0) + return; + + // Clip strategy: "throttling" - find the floor for EMC high limit + // (above domain minimum, of course) + if ((*pEmcHighKHz) < MinKHz) + *pEmcHighKHz = MinKHz; + for (i = 0; i < NVRM_AP15_DFS_EMC_FREQ_STEPS; i++) + { + EmcKHz = s_Ap15EmcConfigSortedTable[i].Emc2xKHz >> 1; + if (EmcKHz <= (*pEmcHighKHz)) + break; + } + if ((i == NVRM_AP15_DFS_EMC_FREQ_STEPS) || (EmcKHz < MinKHz)) + { + i--; + EmcKHz = s_Ap15EmcConfigSortedTable[i].Emc2xKHz >> 1; + } + *pEmcHighKHz = EmcKHz; + + // Clip strategy: "throttling" - restrict CPU high limit by EMC + // configuration ((above domain minimum, of course) + if ((*pCpuHighKHz) > s_Ap15EmcConfigSortedTable[i].CpuLimitKHz) + (*pCpuHighKHz) = s_Ap15EmcConfigSortedTable[i].CpuLimitKHz; + if ((*pCpuHighKHz) < NvRmPrivDfsGetMinKHz(NvRmDfsClockId_Cpu)) + *pCpuHighKHz = NvRmPrivDfsGetMinKHz(NvRmDfsClockId_Cpu); +#endif +} + +NvRmFreqKHz +NvRmPrivAp15GetEmcSyncFreq( + NvRmDeviceHandle hRmDevice, + NvRmModuleID Module) +{ + NvRmFreqKHz FreqKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllM0); + + switch (Module) + { + case NvRmModuleID_2D: + case NvRmModuleID_Epp: + // 2D/EPP frequency is dynamically synchronized with current EMC speed + // (high if EMC divisor 0, and low otherwise) + if (s_MemClocks.pEmcState && (s_MemClocks.pEmcState->Divider != 0)) + FreqKHz = FreqKHz / NVRM_PLLM_2D_LOW_SPEED_RATIO; + else + FreqKHz = FreqKHz / NVRM_PLLM_2D_HIGH_SPEED_RATIO; + break; + + case NvRmModuleID_GraphicsHost: + // Host frequency is static, synchronized with EMC range set by BCT + FreqKHz = FreqKHz / NVRM_PLLM_HOST_SPEED_RATIO; + break; + + default: + NV_ASSERT(!"Invalid module for EMC synchronization"); + FreqKHz = NvRmPrivGetSocClockLimits(Module)->MaxKHz; + break; + } + return FreqKHz; +} + +/*****************************************************************************/ + +static void +Ap15SystemClockSourceFind( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz MaxKHz, + NvRmFreqKHz DomainKHz, + NvRmDfsSource* pDfsSource) +{ + NvU32 i; + NvRmFreqKHz SourceKHz; + NV_ASSERT(DomainKHz <= MaxKHz); + pDfsSource->DividerSetting = 0; // no divider + + // 1st try oscillator + SourceKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_ClkM); + NV_ASSERT(SourceKHz <= MaxKHz); + if (DomainKHz <= SourceKHz) + { + pDfsSource->SourceId = NvRmClockSource_ClkM; + pDfsSource->SourceKHz = SourceKHz; + goto get_mv; + } + + // 2nd choice - doubler + SourceKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_ClkD); + NV_ASSERT(SourceKHz <= MaxKHz); + if (DomainKHz <= SourceKHz) + { + pDfsSource->SourceId = NvRmClockSource_ClkD; + pDfsSource->SourceKHz = SourceKHz; + goto get_mv; + } + + /* + * 3rd option - PLLP divider per policy specification. Find + * the policy entry with source frequency closest and above requested. + * If requested frequency exceeds all policy options within domain + * maximum limit, select the entry with the highest possible frequency. + */ + for (i = 0; i < s_Ap15PllPSystemClockPolicyEntries; i++) + { + SourceKHz = s_Ap15PllPSystemClockPolicy[i].SourceKHz; + if (SourceKHz > MaxKHz) + { + NV_ASSERT(i); + i--; + break; + } + if (DomainKHz <= SourceKHz) + { + break; + } + } + if (i == s_Ap15PllPSystemClockPolicyEntries) + { + i--; // last/highest source is the best we can do + } + pDfsSource->SourceId = s_Ap15PllPSystemClockPolicy[i].SourceId; + pDfsSource->SourceKHz = s_Ap15PllPSystemClockPolicy[i].SourceKHz; + pDfsSource->DividerSetting = s_Ap15PllPSystemClockPolicy[i].DividerSetting; + + /* + * 4st and final option - PLLM divider fixed at maximum possible frequency + * during initialization. Select PLLP/PLLM divider according to the + * following rule: select the divider with smaller frequency if it is equal + * or above the target frequency, otherwise select the divider with bigger + * output frequency. + */ + SourceKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllM1); + NV_ASSERT(SourceKHz <= MaxKHz); + if (SourceKHz > pDfsSource->SourceKHz) + { + if (pDfsSource->SourceKHz >= DomainKHz) + goto get_mv; // keep PLLP divider as a source + } + else // SourceKHz <= pDfsSource->SourceKHz + { + if (SourceKHz < DomainKHz) + goto get_mv; // keep PLLP divider as a source + } + // Select PLLM_OUT1 divider as a source (considered as a fixed source - + // divider settings are ignored) + pDfsSource->SourceId = NvRmClockSource_PllM1; + pDfsSource->SourceKHz = SourceKHz; + +get_mv: + // Finally get operational voltage for found source + pDfsSource->MinMv = NvRmPrivModuleVscaleGetMV( + hRmDevice, NvRmPrivModuleID_System, pDfsSource->SourceKHz); +} + +static void +Ap15CpuClockSourceFind( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz MaxKHz, + NvRmFreqKHz DomainKHz, + NvRmDfsSource* pDfsSource) +{ + NvU32 i; + NvRmFreqKHz SourceKHz; + NV_ASSERT(DomainKHz <= MaxKHz); + pDfsSource->DividerSetting = 0; // no divider + + // 1st try oscillator + SourceKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_ClkM); + NV_ASSERT(SourceKHz <= MaxKHz); + if (DomainKHz <= SourceKHz) + { + pDfsSource->SourceId = NvRmClockSource_ClkM; + pDfsSource->SourceKHz = SourceKHz; + goto get_mv; + } + + // 2nd choice - doubler - no longer supported + // 3rd choice - PLLP divider per policy specification + SourceKHz = + s_Ap15PllPCpuClockPolicy[s_Ap15PllPCpuClockPolicyEntries-1].SourceKHz; + NV_ASSERT(SourceKHz <= MaxKHz); + if (DomainKHz <= SourceKHz) + { + // The requested frequency is within PLLP divider policy table, and all + // policy entries are within domain maximum limit. Then, find the entry + // with source frequency closest and above the requested. + for (i = 0; i < s_Ap15PllPCpuClockPolicyEntries; i++) + { + SourceKHz = s_Ap15PllPCpuClockPolicy[i].SourceKHz; + if (DomainKHz <= SourceKHz) + break; + } + if (s_Ap15PllPCpuClockPolicy[i].DividerSetting == 0) + pDfsSource->SourceId = NvRmClockSource_PllP0; // Bypass 1:1 divider + else + pDfsSource->SourceId = s_Ap15PllPCpuClockPolicy[i].SourceId; + pDfsSource->SourceKHz = s_Ap15PllPCpuClockPolicy[i].SourceKHz; + pDfsSource->DividerSetting = s_Ap15PllPCpuClockPolicy[i].DividerSetting; + goto get_mv; + } + + // 4th choice PLLM base output + SourceKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllM0); + NV_ASSERT(SourceKHz <= MaxKHz); + if (DomainKHz <= SourceKHz) + { + pDfsSource->SourceId = NvRmClockSource_PllM0; + pDfsSource->SourceKHz = SourceKHz; + goto get_mv; + } + + // 5th choice PLLP base output (not used - covered by 3rd choice, case 1:1) + // 6th and final choice - PLLC base output at domain limit + pDfsSource->SourceId = NvRmClockSource_PllC0; + pDfsSource->SourceKHz = MaxKHz; + +get_mv: + // Finally get operational voltage for found source + pDfsSource->MinMv = NvRmPrivModuleVscaleGetMV( + hRmDevice, NvRmModuleID_Cpu, pDfsSource->SourceKHz); +} + +static void +Ap15SystemBusClockConfigure( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz MaxKHz, + NvRmFreqKHz* pDomainKHz, + const NvRmDfsSource* pDfsSource) +{ + NvRmClockSource SourceId = pDfsSource->SourceId; + const NvRmCoreClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_SystemBus)->pInfo.pCore; + + switch(SourceId) + { + case NvRmClockSource_PllP2: + // Reconfigure PLLP variable divider if it is used as a source + NvRmPrivDividerSet(hRmDevice, + NvRmPrivGetClockSourceHandle(SourceId)->pInfo.pDivider, + pDfsSource->DividerSetting); + // fall through + case NvRmClockSource_PllM1: + case NvRmClockSource_ClkD: + case NvRmClockSource_ClkM: + break; // fixed sources - do nothing + default: + NV_ASSERT(!"Invalid source (per policy)"); + } + NV_ASSERT_SUCCESS(NvRmPrivCoreClockConfigure( + hRmDevice, pCinfo, MaxKHz, pDomainKHz, &SourceId)); +} + +static void +Ap15CpuBusClockConfigure( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz MaxKHz, + NvRmFreqKHz* pDomainKHz, + const NvRmDfsSource* pDfsSource) +{ + NvRmClockSource SourceId = pDfsSource->SourceId; + const NvRmCoreClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBus)->pInfo.pCore; + + switch(SourceId) + { + case NvRmClockSource_PllC0: + // DFS PLLC policy - configure PLLC if disabled; otherwise keep + // keep it as is (the latter means either DFS has already set it + // to domain limit, or PLLC is used as display pixel clock source) + if (NvRmPrivGetClockSourceFreq(NvRmClockSource_PllC0) <= + NvRmPrivGetClockSourceFreq(NvRmClockSource_ClkM)) + { + NvRmFreqKHz TargetKHz = pDfsSource->SourceKHz; + NvRmPrivAp15PllConfigureSimple( + hRmDevice, SourceId, MaxKHz, &TargetKHz); + } + break; + case NvRmClockSource_PllP4: + // Reconfigure PLLP variable divider if it is used as a source; + // If source frequency is going down, get EMC configuration is ready + if (pDfsSource->SourceKHz < NvRmPrivGetClockSourceFreq(SourceId)) + NvRmPrivAp15SetEmcForCpuSrcSwitch(hRmDevice); + NvRmPrivDividerSet(hRmDevice, + NvRmPrivGetClockSourceHandle(SourceId)->pInfo.pDivider, + pDfsSource->DividerSetting); + // fall through + case NvRmClockSource_PllP0: + case NvRmClockSource_PllM0: + case NvRmClockSource_ClkD: + case NvRmClockSource_ClkM: + break; // fixed sources - do nothing + default: + NV_ASSERT(!"Invalid source (per policy)"); + } + NV_ASSERT_SUCCESS(NvRmPrivCoreClockConfigure( + hRmDevice, pCinfo, MaxKHz, pDomainKHz, &SourceId)); +} + +/*****************************************************************************/ +/* If time is specified in ns, and frequency in KHz, then cycles = + * (ns * KHz / 10^6) = (ns * (KHz * 2^20 / 10^6) / 2^20) = (ns * KiHz / 2^20), + * where KiHz = (KHz * 2^20 / 10^6) ~ (KHz * 4295 / 4096) with error < 0.001%. + */ +#define NVRM_TIME_TO_CYCLES(ns, KiHz) (((ns * KiHz) + (0x1 << 20)- 1) >> 20) + +#define NV_DRF_MAX_NUM(d,r,f,n) \ + ((((n) <= NV_FIELD_MASK(d##_##r##_0_##f##_RANGE)) ? \ + (n) : NV_FIELD_MASK(d##_##r##_0_##f##_RANGE)) << \ + NV_FIELD_SHIFT(d##_##r##_0_##f##_RANGE)) + +static void +Ap15MioReconfigure( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz MioKHz) +{ + NvU32 reg, mask; + NvU32 MioKiHz = ((MioKHz * 4295) >> 12); + NvOdmAsynchMemConfig MemConfig; + /* + * Reconfigure MIO timing when clock frequency changes. Check only Async + * Memory devices connected to CS1/MIO_B and CS3/MIO_A (CS0 is dedicated + * for NOR, we do not care after boot, and CS2 is dedicated to SDRAM with + * its own clock) + */ + if (NvOdmQueryAsynchMemConfig(1, &MemConfig) == NV_TRUE) + { + reg = NV_REGR(hRmDevice, NvRmModuleID_Misc, 0, APB_MISC_PP_XMB_MIO_CFG_0); + mask = + NV_DRF_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_B_WR_DEAD_TIME, 0xFFFFFFFFUL) | + NV_DRF_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_B_WR_TIME, 0xFFFFFFFFUL) | + NV_DRF_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_B_RD_DEAD_TIME, 0xFFFFFFFFUL) | + NV_DRF_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_B_RD_TIME, 0xFFFFFFFFUL); + reg = (reg & (~mask)) | + NV_DRF_MAX_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_B_WR_DEAD_TIME, + NVRM_TIME_TO_CYCLES(MemConfig.WriteDeadTime, MioKiHz)) | + NV_DRF_MAX_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_B_WR_TIME, + NVRM_TIME_TO_CYCLES(MemConfig.WriteAccessTime, MioKiHz)) | + NV_DRF_MAX_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_B_RD_DEAD_TIME, + NVRM_TIME_TO_CYCLES(MemConfig.ReadDeadTime, MioKiHz)) | + NV_DRF_MAX_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_B_RD_TIME, + NVRM_TIME_TO_CYCLES(MemConfig.ReadAccessTime, MioKiHz)); + NV_REGW(hRmDevice, NvRmModuleID_Misc, 0, APB_MISC_PP_XMB_MIO_CFG_0, reg); + } + if (NvOdmQueryAsynchMemConfig(3, &MemConfig) == NV_TRUE) + { + reg = NV_REGR(hRmDevice, NvRmModuleID_Misc, 0, APB_MISC_PP_XMB_MIO_CFG_0); + mask = + NV_DRF_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_A_WR_DEAD_TIME, 0xFFFFFFFFUL) | + NV_DRF_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_A_WR_TIME, 0xFFFFFFFFUL) | + NV_DRF_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_A_RD_DEAD_TIME, 0xFFFFFFFFUL) | + NV_DRF_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_A_RD_TIME, 0xFFFFFFFFUL); + reg = (reg & (~mask)) | + NV_DRF_MAX_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_A_WR_DEAD_TIME, + NVRM_TIME_TO_CYCLES(MemConfig.WriteDeadTime, MioKiHz)) | + NV_DRF_MAX_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_A_WR_TIME, + NVRM_TIME_TO_CYCLES(MemConfig.WriteAccessTime, MioKiHz)) | + NV_DRF_MAX_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_A_RD_DEAD_TIME, + NVRM_TIME_TO_CYCLES(MemConfig.ReadDeadTime, MioKiHz)) | + NV_DRF_MAX_NUM(APB_MISC_PP, XMB_MIO_CFG, MIO_A_RD_TIME, + NVRM_TIME_TO_CYCLES(MemConfig.ReadAccessTime, MioKiHz)); + NV_REGW(hRmDevice, NvRmModuleID_Misc, 0, APB_MISC_PP_XMB_MIO_CFG_0, reg); + } +} + +NvBool NvRmPrivAp15DfsClockConfigure( + NvRmDeviceHandle hRmDevice, + const NvRmDfsFrequencies* pMaxKHz, + NvRmDfsFrequencies* pDfsKHz) +{ + NvU32 i; + NvBool Status; + NvRmFreqKHz FreqKHz; + NvRmDfsSource CpuClockSource; + NvRmDfsSource SystemClockSource; + NvRmDfsSource Emc2xClockSource; + NvBool CpuKHzUp = pDfsKHz->Domains[NvRmDfsClockId_Cpu] > + NvRmPrivGetClockSourceFreq(NvRmClockSource_CpuBus); + + NV_ASSERT(hRmDevice); + NV_ASSERT(pMaxKHz && pDfsKHz); + + /* + * Adjust System bus core clock. It should be sufficient to supply AVP, + * and all bus clocks. Also make sure that AHB bus frequency is above + * the one requested for APB clock. + */ + for (FreqKHz = 0, i = 1; i < NvRmDfsClockId_Num; i++) + { + if ((i != NvRmDfsClockId_Cpu) && + (i != NvRmDfsClockId_Emc)) + { + FreqKHz = (FreqKHz > pDfsKHz->Domains[i]) ? + FreqKHz : pDfsKHz->Domains[i]; + } + } + pDfsKHz->Domains[NvRmDfsClockId_System] = FreqKHz; + +#if LIMIT_SYS_TO_VDE_RATIO + if (pDfsKHz->Domains[NvRmDfsClockId_Vpipe] < + (FreqKHz >> (LIMIT_SYS_TO_VDE_RATIO - 1))) + { + pDfsKHz->Domains[NvRmDfsClockId_Vpipe] = + (FreqKHz >> (LIMIT_SYS_TO_VDE_RATIO - 1)); + } +#endif + +#if LIMIT_SYS_TO_AHB_APB_RATIOS + if (pDfsKHz->Domains[NvRmDfsClockId_Apb] < (FreqKHz >> 2)) + { + pDfsKHz->Domains[NvRmDfsClockId_Apb] = (FreqKHz >> 2); + } + if (pDfsKHz->Domains[NvRmDfsClockId_Ahb] < (FreqKHz >> 1)) + { + pDfsKHz->Domains[NvRmDfsClockId_Ahb] = (FreqKHz >> 1); + } +#endif + if (pDfsKHz->Domains[NvRmDfsClockId_Ahb] < + pDfsKHz->Domains[NvRmDfsClockId_Apb]) + { + pDfsKHz->Domains[NvRmDfsClockId_Ahb] = + pDfsKHz->Domains[NvRmDfsClockId_Apb]; + } + + // Find clock sources for CPU, System and Memory clocks. H/w requirement + // to increase memory clocks in steps, may limit CPU clock as well + Ap15SystemClockSourceFind(hRmDevice, + pMaxKHz->Domains[NvRmDfsClockId_System], + pDfsKHz->Domains[NvRmDfsClockId_System], + &SystemClockSource); + Status = Ap15Emc2xClockSourceFind(hRmDevice, + (pMaxKHz->Domains[NvRmDfsClockId_Emc] << 1), + (pDfsKHz->Domains[NvRmDfsClockId_Emc] << 1), + &pDfsKHz->Domains[NvRmDfsClockId_Cpu], + &Emc2xClockSource); + Ap15CpuClockSourceFind(hRmDevice, + pMaxKHz->Domains[NvRmDfsClockId_Cpu], + pDfsKHz->Domains[NvRmDfsClockId_Cpu], + &CpuClockSource); + +#if !NV_OAL + // Adjust core voltage for the new clock sources before actual change + NvRmPrivVoltageScale(NV_TRUE, CpuClockSource.MinMv, + SystemClockSource.MinMv, Emc2xClockSource.MinMv); +#endif + + // Configure System bus and derived clocks. Note that APB is the only + // clock in system complex that may have different (lower) maximum + // limit - pass it explicitly to set function. + if (FreqKHz < NvRmPrivGetClockSourceFreq(NvRmClockSource_SystemBus)) + FreqKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_SystemBus); + Ap15MioReconfigure(hRmDevice, FreqKHz); // MIO timing for max frequency + Ap15SystemBusClockConfigure(hRmDevice, + pMaxKHz->Domains[NvRmDfsClockId_System], + &pDfsKHz->Domains[NvRmDfsClockId_System], + &SystemClockSource); + pDfsKHz->Domains[NvRmDfsClockId_Avp] = + pDfsKHz->Domains[NvRmDfsClockId_System]; // no AVP clock skipping + NvRmPrivBusClockFreqSet(hRmDevice, + pDfsKHz->Domains[NvRmDfsClockId_System], + &pDfsKHz->Domains[NvRmDfsClockId_Vpipe], + &pDfsKHz->Domains[NvRmDfsClockId_Ahb], + &pDfsKHz->Domains[NvRmDfsClockId_Apb], + pMaxKHz->Domains[NvRmDfsClockId_Apb]); + Ap15MioReconfigure(hRmDevice, pDfsKHz->Domains[NvRmDfsClockId_Ahb]); + + // Configure CPU core clock before Memory if CPU frequency goes down + if (!CpuKHzUp) + { + Ap15CpuBusClockConfigure(hRmDevice, + pMaxKHz->Domains[NvRmDfsClockId_Cpu], + &pDfsKHz->Domains[NvRmDfsClockId_Cpu], + &CpuClockSource); + } + // Configure Memory clocks and convert frequency to DFS EMC 1x domain + FreqKHz = pDfsKHz->Domains[NvRmDfsClockId_Emc] << 1; + Ap15Emc2xClockConfigure(hRmDevice, + (pMaxKHz->Domains[NvRmDfsClockId_Emc] << 1), + &FreqKHz, &Emc2xClockSource); + pDfsKHz->Domains[NvRmDfsClockId_Emc] = FreqKHz >> 1; + // Configure CPU core clock after Memory if CPU frequency goes up + if (CpuKHzUp) + { + Ap15CpuBusClockConfigure(hRmDevice, + pMaxKHz->Domains[NvRmDfsClockId_Cpu], + &pDfsKHz->Domains[NvRmDfsClockId_Cpu], + &CpuClockSource); + } + +#if !NV_OAL + // Adjust core voltage for the new clock sources after actual change + NvRmPrivVoltageScale(NV_FALSE, CpuClockSource.MinMv, + SystemClockSource.MinMv, Emc2xClockSource.MinMv); +#endif + return Status; +} + +void +NvRmPrivAp15DfsClockFreqGet( + NvRmDeviceHandle hRmDevice, + NvRmDfsFrequencies* pDfsKHz) +{ + NvRmFreqKHz SystemFreq; + const NvRmCoreClockInfo* pCinfo; + NV_ASSERT(hRmDevice && pDfsKHz); + + // Get frequencies of the System core clock, AVP clock (the same as System + // - no clock skipping), AHB, APB, and V-pipe bus clock frequencies + pCinfo = NvRmPrivGetClockSourceHandle(NvRmClockSource_SystemBus)->pInfo.pCore; + SystemFreq = NvRmPrivCoreClockFreqGet(hRmDevice, pCinfo); + pDfsKHz->Domains[NvRmDfsClockId_System] = SystemFreq; + pDfsKHz->Domains[NvRmDfsClockId_Avp] = SystemFreq; + + NvRmPrivBusClockFreqGet( + hRmDevice, SystemFreq, + &pDfsKHz->Domains[NvRmDfsClockId_Vpipe], + &pDfsKHz->Domains[NvRmDfsClockId_Ahb], + &pDfsKHz->Domains[NvRmDfsClockId_Apb]); + + // Get CPU core clock frequencies + pCinfo = NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBus)->pInfo.pCore; + pDfsKHz->Domains[NvRmDfsClockId_Cpu] = + NvRmPrivCoreClockFreqGet(hRmDevice, pCinfo); + + // Get EMC clock frequency (DFS monitors EMC 1x domain) + Ap15Emc2xFreqGet(hRmDevice); // Get EMC2x clock state from h/w + pDfsKHz->Domains[NvRmDfsClockId_Emc] = + (s_MemClocks.pEmcState->actual_freq >> 1); +} + +void +NvRmPrivAp15DfsVscaleFreqGet( + NvRmDeviceHandle hRmDevice, + NvRmMilliVolts TargetMv, + NvRmDfsFrequencies* pDfsKHz) +{ + NvU32 i; + NvRmMilliVolts v; + NvRmFreqKHz Fa, Fb, f; + NvRmDfsSource DfsClockSource; + NvRmFreqKHz CpuMaxKHz = NvRmPrivGetSocClockLimits(NvRmModuleID_Cpu)->MaxKHz; + NvRmFreqKHz SysMaxKHz = + NvRmPrivGetSocClockLimits(NvRmPrivModuleID_System)->MaxKHz; + NV_ASSERT(hRmDevice && pDfsKHz); + + // If PLLM0 entry in EMC scaling table is valid, search the table for + // the entry below and closest to the traget voltage. Otherwise, there + // is no EMC scaling - just return current EMC frequency. + pDfsKHz->Domains[NvRmDfsClockId_Emc] = + (s_MemClocks.pEmcState->actual_freq >> 1); + f = NvRmFreqMaximum; // assume CPU is not throttled by EMC + if (s_Ap15EmcConfigSortedTable[0].Emc2xKHz != 0) + { + for (i = 0; i < (NVRM_AP15_DFS_EMC_FREQ_STEPS - 1); i++) + { + if ((s_Ap15EmcConfigSortedTable[i+1].Emc2xKHz == 0) || + (s_Ap15EmcConfigSortedTable[i].CoreVoltageMv <= TargetMv)) + break; // exit if found entry or next entry is invalid + } + pDfsKHz->Domains[NvRmDfsClockId_Emc] = + (s_Ap15EmcConfigSortedTable[i].Emc2xKHz >> 1); + f = s_Ap15EmcConfigSortedTable[i].CpuLimitKHz; // throttle CPU + } + + // Binary search for maximum CPU frequency, with source that can be used + // at target voltage or below + Fb = NV_MIN(CpuMaxKHz, f); + Fa = NvRmPrivGetClockSourceFreq(NvRmClockSource_ClkM); + NV_ASSERT(Fa <= Fb); + while ((Fb - Fa) > 1000) // 1MHz resolution + { + f = (Fa + Fb) >> 1; + Ap15CpuClockSourceFind(hRmDevice, CpuMaxKHz, f, &DfsClockSource); + v = DfsClockSource.MinMv; + if (v <= TargetMv) + Fa = f; + else + Fb = f; + } + pDfsKHz->Domains[NvRmDfsClockId_Cpu] = Fa; + + // Binary search for maximum System/Avp frequency, with source that can be used + // at target voltage or below + Fb = SysMaxKHz; + Fa = NvRmPrivGetClockSourceFreq(NvRmClockSource_ClkM); + NV_ASSERT(Fa <= Fb); + while ((Fb - Fa) > 1000) // 1MHz resolution + { + f = (Fa + Fb) >> 1; + Ap15SystemClockSourceFind(hRmDevice, SysMaxKHz, f, &DfsClockSource); + v = DfsClockSource.MinMv; + if (v <= TargetMv) + Fa = f; + else + Fb = f; + } + pDfsKHz->Domains[NvRmDfsClockId_System] = Fa; + pDfsKHz->Domains[NvRmDfsClockId_Avp] = Fa; + pDfsKHz->Domains[NvRmDfsClockId_Ahb] = Fa; + pDfsKHz->Domains[NvRmDfsClockId_Apb] = Fa; + pDfsKHz->Domains[NvRmDfsClockId_Vpipe] = Fa; +} + +/*****************************************************************************/ + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_misc.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_misc.c new file mode 100644 index 000000000000..97835ad98399 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_misc.c @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvassert.h" +#include "nvos.h" +#include "nvrm_init.h" +#include "nvrm_drf.h" +#include "nvrm_hwintf.h" +#include "nvrm_clocks.h" +#include "nvrm_chiplib.h" +#include "nvrm_hardware_access.h" +#include "ap15rm_private.h" +#include "ap15rm_clocks.h" +#include "ap16/arapb_misc.h" +#include "ap15/arahb_arbc.h" +#include "ap15/armc.h" +#include "ap15/aremc.h" +#include "ap15/arfuse.h" +#include "ap15/arclk_rst.h" + + +// This list requires pre-sorted info in bond-out registers order and bond-out +// register bit shift order (MSB-to-LSB). +static const NvU32 s_Ap15BondOutTable[] = +{ + // BOND_OUT_L bits + NVRM_DEVICE_UNKNOWN, // NV_DEVID_CPU + NVRM_DEVICE_UNKNOWN, + NVRM_DEVICE_UNKNOWN, + NVRM_MODULE_ID( NvRmModuleID_Ac97, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Rtc, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Timer, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Uart, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Uart, 1 ), + NVRM_MODULE_ID( NvRmPrivModuleID_Gpio, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Sdio, 1 ), + NVRM_MODULE_ID( NvRmModuleID_Spdif, 0 ), + NVRM_MODULE_ID( NvRmModuleID_I2s, 0 ), + NVRM_MODULE_ID( NvRmModuleID_I2c, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Nand, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Sdio, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Hsmmc, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Twc, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Pwm, 0 ), + NVRM_MODULE_ID( NvRmModuleID_I2s, 1 ), + NVRM_MODULE_ID( NvRmModuleID_Epp, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Vi, 0 ), + NVRM_MODULE_ID( NvRmModuleID_2D, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Usb2Otg, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Isp, 0 ), + NVRM_MODULE_ID( NvRmModuleID_3D, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Ide, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Display, 1 ), + NVRM_MODULE_ID( NvRmModuleID_Display, 0 ), + NVRM_MODULE_ID( NvRmModuleID_GraphicsHost, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Vcp, 0 ), + NVRM_MODULE_ID( NvRmModuleID_CacheMemCtrl, 0 ), + NVRM_DEVICE_UNKNOWN, // NV_DEVID_COP_CACHE + + // BOND_OUT_H bits + NVRM_MODULE_ID( NvRmPrivModuleID_MemoryController, 0 ), + NVRM_DEVICE_UNKNOWN, // NV_DEVID_AHB_DMA + NVRM_MODULE_ID( NvRmPrivModuleID_ApbDma, 0 ), + NVRM_DEVICE_UNKNOWN, + NVRM_MODULE_ID( NvRmModuleID_Kbc, 0 ), + NVRM_MODULE_ID( NvRmModuleID_SysStatMonitor, 0 ), + NVRM_DEVICE_UNKNOWN, // PMC + NVRM_MODULE_ID( NvRmModuleID_Fuse, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Slink, 0 ), + NVRM_DEVICE_UNKNOWN, // SBC1 + NVRM_MODULE_ID( NvRmModuleID_Nor, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Spi, 0 ), + NVRM_DEVICE_UNKNOWN, // SBC2 + NVRM_MODULE_ID( NvRmModuleID_Xio, 0 ), + NVRM_DEVICE_UNKNOWN, // SBC3 + NVRM_MODULE_ID( NvRmModuleID_Dvc, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Dsi, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Tvo, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Mipi, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Hdmi, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Csi, 0 ), + NVRM_DEVICE_UNKNOWN, // TVDAC + NVRM_MODULE_ID( NvRmModuleID_I2c, 1 ), + NVRM_MODULE_ID( NvRmModuleID_Uart, 2 ), + NVRM_DEVICE_UNKNOWN, // SPROM + NVRM_MODULE_ID( NvRmPrivModuleID_ExternalMemoryController, 0 ), + NVRM_DEVICE_UNKNOWN, + NVRM_DEVICE_UNKNOWN, + NVRM_MODULE_ID( NvRmModuleID_Mpe, 0 ), + NVRM_MODULE_ID( NvRmModuleID_Vde, 0 ), + NVRM_MODULE_ID( NvRmModuleID_BseA, 0 ), + NVRM_DEVICE_UNKNOWN, //BSEV +}; + +/** + * Enable HDCP and Macrovision + */ +static void +NvRmPrivContentProtectionFuses( NvRmDeviceHandle hRm ) +{ + NvU32 reg; + NvU32 clk_rst; + + /* need to set FUSEWRDATA3_RESERVED_PRODUCTION__PRI_ALIAS to 0x3 and + * enable the bypass. + * + * bit 0: macrovision + * bit 1: hdcp + */ + +#if NV_USE_FUSE_CLOCK_ENABLE + // Enable fuse clock + Ap15EnableModuleClock(hRm, NvRmModuleID_Fuse, NV_TRUE); +#endif + + clk_rst = NV_REGR( hRm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0 ); + clk_rst = NV_FLD_SET_DRF_NUM( CLK_RST_CONTROLLER, MISC_CLK_ENB, + CFG_ALL_VISIBLE, 1, clk_rst ); + NV_REGW( hRm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0, clk_rst ); + + reg = NV_DRF_NUM( FUSE, FUSEWRDATA3, + FUSEWRDATA_RESERVED_PRODUCTION__PRI_ALIAS_0, 0x3 ); + NV_REGW( hRm, NvRmModuleID_Fuse, 0, FUSE_FUSEWRDATA3_0, reg ); + + reg = NV_DRF_DEF( FUSE, FUSEBYPASS, FUSEBYPASS_VAL, ENABLED ); + NV_REGW( hRm, NvRmModuleID_Fuse, 0, FUSE_FUSEBYPASS_0, reg ); + + clk_rst = NV_FLD_SET_DRF_NUM( CLK_RST_CONTROLLER, MISC_CLK_ENB, + CFG_ALL_VISIBLE, 0, clk_rst ); + NV_REGW( hRm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0, clk_rst ); + +#if NV_USE_FUSE_CLOCK_ENABLE + // Disable fuse clock + Ap15EnableModuleClock(hRm, NvRmModuleID_Fuse, NV_FALSE); +#endif +} + +#define NVRM_CONFIG_CLOCK(Module, SrcDef, DivNum) \ +do\ +{\ + reg = NV_REGR(rm, NvRmPrivModuleID_ClockAndReset, 0, \ + CLK_RST_CONTROLLER_CLK_SOURCE_##Module##_0); \ + if ((DivNum) > NV_DRF_VAL(CLK_RST_CONTROLLER, CLK_SOURCE_##Module, \ + Module##_CLK_DIVISOR, reg)) \ + {\ + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, CLK_SOURCE_##Module, \ + Module##_CLK_DIVISOR, (DivNum), reg); \ + NV_REGW(rm, NvRmPrivModuleID_ClockAndReset, 0, \ + CLK_RST_CONTROLLER_CLK_SOURCE_##Module##_0, reg); \ + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); \ + }\ + reg = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, CLK_SOURCE_##Module, \ + Module##_CLK_SRC, SrcDef, reg); \ + NV_REGW(rm, NvRmPrivModuleID_ClockAndReset, 0, \ + CLK_RST_CONTROLLER_CLK_SOURCE_##Module##_0, reg); \ + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); \ + if ((DivNum) < NV_DRF_VAL(CLK_RST_CONTROLLER, CLK_SOURCE_##Module, \ + Module##_CLK_DIVISOR, reg))\ + {\ + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, CLK_SOURCE_##Module, \ + Module##_CLK_DIVISOR, (DivNum), reg); \ + NV_REGW(rm, NvRmPrivModuleID_ClockAndReset, 0, \ + CLK_RST_CONTROLLER_CLK_SOURCE_##Module##_0, reg); \ + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY);\ + }\ +} while(0) + +#define NVRM_SET_OSC_CLOCK(ClkModule, RstModule, H_L) \ +do\ +{\ + if (RstOut##H_L & \ + CLK_RST_CONTROLLER_RST_DEVICES_##H_L##_0_SWR_##RstModule##_RST_FIELD) \ + {\ + reg = NV_REGR(rm, NvRmPrivModuleID_ClockAndReset, 0, \ + CLK_RST_CONTROLLER_CLK_SOURCE_##ClkModule##_0); \ + reg = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, CLK_SOURCE_##ClkModule, \ + ClkModule##_CLK_SRC, CLK_M, reg); \ + NV_REGW(rm, NvRmPrivModuleID_ClockAndReset, 0, \ + CLK_RST_CONTROLLER_CLK_SOURCE_##ClkModule##_0, reg); \ + }\ +} while(0) + +/** + * brings the minimum modules out of reset. + */ +void +NvRmPrivAp15BasicReset( NvRmDeviceHandle rm ) +{ +#if !NV_OAL + NvU32 reg, RstOutL, RstOutH, ClkOutL, ClkOutH; + ExecPlatform env; + + if (NvRmIsSimulation()) + { + /* the memory system can't be used until the mem_init_done bit has + * been set. This is done by the bootrom for production systems. + */ + reg = NV_REGR( rm, NvRmPrivModuleID_Ahb_Arb_Ctrl, 0, + AHB_ARBITRATION_XBAR_CTRL_0 ); + reg = NV_FLD_SET_DRF_DEF( AHB_ARBITRATION, XBAR_CTRL, MEM_INIT_DONE, + DONE, reg ); + NV_REGW( rm, NvRmPrivModuleID_Ahb_Arb_Ctrl, 0, + AHB_ARBITRATION_XBAR_CTRL_0, reg ); + } + + // FIXME: this takes the Big Hammer Approach. Take everything out + // of reset and enable all of the clocks. Then keep enabled only boot + // clocks and graphics host. + + // get boot module reset state + RstOutL = NV_REGR(rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0); + RstOutH = NV_REGR(rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0); + + // save boot clock enable state + ClkOutL = NV_REGR(rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0); + ClkOutH = NV_REGR(rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0); + + /* write clk_out_enb_l */ + NV_REGW( rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, 0xFFFFFFFF ); + + /* write clk_out_enb_h */ + NV_REGW( rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, 0xFFFFFFFF ); + + // For AP15 default clock source selection is out of range for many modules + // Just copnfigure clocks so that reset is propagated correctly + env = NvRmPrivGetExecPlatform(rm); + if (env == ExecPlatform_Soc) + { + /* + * For peripheral modules that are not taken from reset, yet, + * use oscillator as a safe clock + */ + NVRM_SET_OSC_CLOCK(I2S1, I2S1, L); + NVRM_SET_OSC_CLOCK(I2S2, I2S2, L); + + NVRM_SET_OSC_CLOCK(I2C1, I2C1, L); + NVRM_SET_OSC_CLOCK(I2C2, I2C2, H); + NVRM_SET_OSC_CLOCK(DVC_I2C, DVC_I2C, H); + + NVRM_SET_OSC_CLOCK(PWM, PWM, L); + NVRM_SET_OSC_CLOCK(XIO, XIO, H); + NVRM_SET_OSC_CLOCK(TWC, TWC, L); + NVRM_SET_OSC_CLOCK(HSMMC, HSMMC, L); + + NVRM_SET_OSC_CLOCK(VFIR, UARTB, L); + NVRM_SET_OSC_CLOCK(UARTA, UARTA, L); + NVRM_SET_OSC_CLOCK(UARTB, UARTB, L); + NVRM_SET_OSC_CLOCK(UARTC, UARTC, H); + + NVRM_SET_OSC_CLOCK(NDFLASH, NDFLASH, L); + NVRM_SET_OSC_CLOCK(IDE, IDE, L); + NVRM_SET_OSC_CLOCK(MIPI, MIPI, H); + NVRM_SET_OSC_CLOCK(SDIO1, SDIO1, L); + NVRM_SET_OSC_CLOCK(SDIO2, SDIO2, L); + + NVRM_SET_OSC_CLOCK(SPI1, SPI1, H); + NVRM_SET_OSC_CLOCK(SBC1, SBC1, H); + NVRM_SET_OSC_CLOCK(SBC2, SBC2, H); + NVRM_SET_OSC_CLOCK(SBC3, SBC3, H); + + NVRM_SET_OSC_CLOCK(DISP1, DISP1, L); + NVRM_SET_OSC_CLOCK(DISP2, DISP2, L); + NVRM_SET_OSC_CLOCK(TVO, TVO, H); + NVRM_SET_OSC_CLOCK(CVE, TVO, H); + NVRM_SET_OSC_CLOCK(HDMI, HDMI, H); + NVRM_SET_OSC_CLOCK(TVDAC, TVDAC, H); + + // Special case SPDIF (set OUT on OSC; IN on PLLP_OUT0/(1+10/2)) + if (RstOutL & CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_SPDIF_RST_FIELD) + { + reg = NV_DRF_DEF(CLK_RST_CONTROLLER, CLK_SOURCE_SPDIF, + SPDIFOUT_CLK_SRC, CLK_M) | + NV_DRF_DEF(CLK_RST_CONTROLLER, CLK_SOURCE_SPDIF, + SPDIFIN_CLK_SRC, PLLP_OUT0) | + NV_DRF_NUM(CLK_RST_CONTROLLER, CLK_SOURCE_SPDIF, + SPDIFIN_CLK_DIVISOR, 10); + NV_REGW( rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_0, reg); + } + + /* + * For graphic clocks use PLLM_OUT0 (max 400MHz) as a source, and set + * divider so that initial frequency is below maximum module limit + * (= PLLM_OUT0 / (1 + DIVIDER/2) + */ + #define G_DIVIDER (2) + NVRM_CONFIG_CLOCK(HOST1X, PLLM_OUT0, G_DIVIDER); + NVRM_CONFIG_CLOCK(EPP, PLLM_OUT0, G_DIVIDER); + NVRM_CONFIG_CLOCK(G2D, PLLM_OUT0, G_DIVIDER); + NVRM_CONFIG_CLOCK(G3D, PLLM_OUT0, G_DIVIDER); + NVRM_CONFIG_CLOCK(MPE, PLLM_OUT0, G_DIVIDER); + #define VI_DIVIDER (4) + NVRM_CONFIG_CLOCK(VI, PLLM_OUT0, VI_DIVIDER); + NVRM_CONFIG_CLOCK(VI_SENSOR, PLLM_OUT0, VI_DIVIDER); + + NvOsWaitUS(NVRM_RESET_DELAY); + } + // Make sure Host1x clock will be kept enabled + ClkOutL = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, CLK_OUT_ENB_L, + CLK_ENB_HOST1X, ENABLE, ClkOutL); + // Make sure VDE, BSEV and BSEA clocks will be kept disabled + ClkOutH = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, CLK_OUT_ENB_H, + CLK_ENB_VDE, DISABLE, ClkOutH); + ClkOutH = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, CLK_OUT_ENB_H, + CLK_ENB_BSEV, DISABLE, ClkOutH); + ClkOutH = NV_FLD_SET_DRF_DEF(CLK_RST_CONTROLLER, CLK_OUT_ENB_H, + CLK_ENB_BSEA, DISABLE, ClkOutH); + + /* write rst_devices_l */ + NV_REGW( rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, 0 ); + + /* write rst_devies_h */ + NV_REGW( rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, 0 ); + + // restore clock enable state (= disable those clocks that + // were disabled on boot) + NV_REGW( rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, ClkOutL ); + NV_REGW( rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, ClkOutH ); + + /* enable hdcp and macrovision */ + NvRmPrivContentProtectionFuses( rm ); + + // 10jun2008: jn turning this back on. Still need to solve the QT + // issue. + // FIXME: On Quickturn and FPGA we are using normal sdram, not mobile + // ram. Need some way to determine if we have normal sdram + // or mobile sdram. Actually these bits should be set by BCT. + reg = NV_REGR(rm, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_CFG_0); + reg = NV_FLD_SET_DRF_DEF(EMC, CFG, DRAM_CLKSTOP, ENABLED, reg); + reg = NV_FLD_SET_DRF_DEF(EMC, CFG, DRAM_ACPD, ACTIVE_POWERDOWN, reg); + NV_REGW( rm, NvRmPrivModuleID_ExternalMemoryController, 0, + EMC_CFG_0, reg); + + // Enable stop clock to CPU, while it is halted + reg = NV_REGR(rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_MASK_ARM_0); + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, CLK_MASK_ARM, + CLK_MASK_CPU_HALT, 1, reg ); + NV_REGW(rm, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_CLK_MASK_ARM_0, reg); + + if (rm->ChipId.Id == 0x16) + { + NvU32 Reg = 0; + // If USB main clock source is not enabled then disable the clocks to USB0 and USB1 + Reg = NV_REGR(rm, NvRmPrivModuleID_ClockAndReset, 0, CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0); + if (!NV_DRF_VAL(CLK_RST_CONTROLLER, CLK_OUT_ENB_L, CLK_ENB_USBD, Reg)) + { + // Disable clocks for USB1 and USB2 controllers.Should be enabled on need basis. + Reg = NV_REGR(rm, NvRmModuleID_Misc, 0, APB_MISC_PP_MISC_USB_CLK_RST_CTL_0); + Reg = NV_FLD_SET_DRF_DEF(APB_MISC_PP,MISC_USB_CLK_RST_CTL, MISC_USB_CE, DISABLE, Reg); + Reg = NV_FLD_SET_DRF_DEF(APB_MISC_PP,MISC_USB_CLK_RST_CTL, MISC_USB2_CE, DISABLE, Reg); + NV_REGW(rm, NvRmModuleID_Misc, 0, APB_MISC_PP_MISC_USB_CLK_RST_CTL_0, Reg); + } + } + +#endif // !NV_OAL +} + +static void +NvRmPrivAp15GetBondOut( NvRmDeviceHandle hDevice, + const NvU32 **pTable, + NvU32 *bondOut ) +{ + *pTable = s_Ap15BondOutTable; + bondOut[0] = NV_REGR(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_BOND_OUT_L_0); + bondOut[1] = NV_REGR(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_BOND_OUT_H_0); +} + + +#define NVRM_MAX_BOND_OUT_REG 3 + +/* + * Check BondOut register to determine which module and/or module instance + * is not available. + */ +void +NvRmPrivCheckBondOut( NvRmDeviceHandle hDevice ) +{ + NvRmModuleTable *mod_table = 0; + NvRmModule *modules = 0; + NvRmModuleInstance *instance = 0; + NvRmChipId *id = 0; + NvU32 bondOut[NVRM_MAX_BOND_OUT_REG] = {0, 0, 0}; + NvU32 j, i, k; + const NvU32 *table = NULL; + NvU8 *pb = NULL; + NvU8 val; + + NV_ASSERT( hDevice ); + + id = NvRmPrivGetChipId( hDevice ); + switch (id->Id) + { + case 0x15: + case 0x16: + NvRmPrivAp15GetBondOut(hDevice, &table, bondOut); + break; + case 0x20: + NvRmPrivAp20GetBondOut(hDevice, &table, bondOut); + break; + default: + return; // no support + } + + if ( !bondOut[0] && !bondOut[1] && !bondOut[2] ) + return; + + mod_table = NvRmPrivGetModuleTable( hDevice ); + modules = mod_table->Modules; + + for ( i = 0, j = 0; j < NVRM_MAX_BOND_OUT_REG; j++ ) + { + if ( !bondOut[j] ) + { + i += 32; // skip full 32-bit + continue; + } + pb = (NvU8 *)&bondOut[j]; + for ( k = 0; k < 4; k++ ) + { + val = *pb++; + if ( !val ) + { + i += 8; + continue; + } + for( ; ; ) + { + if ( val & 1 ) + { + NvU32 moduleIdInst = table[i]; + if ( NVRM_DEVICE_UNKNOWN != moduleIdInst ) + { + if ( NvSuccess == NvRmPrivGetModuleInstance(hDevice, + moduleIdInst, &instance) ) + { + /* Mark instance's DevIdx to invalid value -1. if all + instances for the module are invalid, mark the module + itself INVALID. + Keep instance->DeviceId to maintain instance ordering + since we could be bonding out, say, UARTA but UARTB and + UARTC still available. */ + NvRmModuleID moduleId = + NVRM_MODULE_ID_MODULE( moduleIdInst ); + instance->DevIdx = (NvU8)-1; + if (0 == NvRmModuleGetNumInstances( hDevice, moduleId )) + modules[moduleId].Index = NVRM_MODULE_INVALID; + } + } + } + val = val >> 1; // Use ARM's clz? + if ( !val ) + { + i = (i + 8) & ~7; // skip to next byte + break; + } + i++; + } + } + } +} + +/*****************************************************************************/ + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.c new file mode 100644 index 000000000000..4154c83e3d1b --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.c @@ -0,0 +1,931 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvassert.h" +#include "nvrm_clocks.h" +#include "nvrm_hwintf.h" +#include "nvrm_module.h" +#include "nvrm_drf.h" +#include "ap15/aremc.h" +#include "ap15/arclk_rst.h" +#include "ap15/arapbpm.h" +#include "ap16/arapb_misc.h" +#include "ap15rm_clocks.h" +#include "ap15rm_private.h" + + + +/*****************************************************************************/ + +static void NvRmPrivWaitUS( + NvRmDeviceHandle hDevice, + NvU32 usec) +{ + NvU32 t, start; + + start = NV_REGR(hDevice, NvRmModuleID_TimerUs, 0, 0); + for (;;) + { + t = NV_REGR(hDevice, NvRmModuleID_TimerUs, 0, 0); + if ( ((NvU32)(t - start)) >= usec ) + break; + } +} + +#define CLOCK_ENABLE( rm, offset, field, EnableState ) \ + do { \ + regaddr = (CLK_RST_CONTROLLER_##offset##_0); \ + NvOsMutexLock((rm)->CarMutex); \ + reg = NV_REGR((rm), NvRmPrivModuleID_ClockAndReset, 0, regaddr); \ + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, offset, field, EnableState, reg); \ + NV_REGW((rm), NvRmPrivModuleID_ClockAndReset, 0, regaddr, reg); \ + NvOsMutexUnlock((rm)->CarMutex); \ + } while( 0 ) + +/*****************************************************************************/ +void +Ap15EnableModuleClock( + NvRmDeviceHandle hDevice, + NvRmModuleID ModuleId, + ModuleClockState ClockState) +{ + // Extract module and instance from composite module id. + NvU32 Module = NVRM_MODULE_ID_MODULE( ModuleId ); + NvU32 Instance = NVRM_MODULE_ID_INSTANCE( ModuleId ); + NvU32 reg; + NvU32 regaddr; + + if (ClockState == ModuleClockState_Enable) + { + NvRmPrivConfigureClockSource(hDevice, ModuleId, NV_TRUE); + } + + switch ( Module ) { + case NvRmModuleID_CacheMemCtrl: + NV_ASSERT( Instance < 2 ); + if( Instance == 0 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_CACHE1, ClockState ); + } + else if( Instance == 1 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_CACHE2, ClockState ); + } + break; + case NvRmModuleID_Vcp: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_VCP, ClockState ); + break; + case NvRmModuleID_GraphicsHost: + // FIXME: should this be allowed? + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_HOST1X, ClockState ); + break; + case NvRmModuleID_Display: + NV_ASSERT( Instance < 2 ); + if( Instance == 0 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_DISP1, ClockState ); + } + else if( Instance == 1 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_DISP2, ClockState ); + } + break; + case NvRmModuleID_Ide: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_IDE, ClockState ); + break; + case NvRmModuleID_3D: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_3D, ClockState ); + break; + case NvRmModuleID_Isp: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_ISP, ClockState ); + break; + case NvRmModuleID_Usb2Otg: + NV_ASSERT( Instance < 2 ); + if ((hDevice->ChipId.Id == 0x16) && (ClockState == NV_FALSE)) + { + NvU32 RegVal = 0; + // On AP16 USB clock source is shared for both USB controllers + // Disabling the main clock source will disable both controllers + // when disabling the clock make sure that both controllers are disabled. + RegVal = NV_REGR(hDevice, NvRmModuleID_Misc, 0, APB_MISC_PP_MISC_USB_CLK_RST_CTL_0); + + if (!(NV_DRF_VAL(APB_MISC_PP, MISC_USB_CLK_RST_CTL, MISC_USB_CE, RegVal)) && + !(NV_DRF_VAL(APB_MISC_PP, MISC_USB_CLK_RST_CTL, MISC_USB2_CE, RegVal)) ) + { + /// Disable USBD clock for both the instances 0 and 1 + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_USBD, ClockState ); + } + } + else + { + /// Enable/Disable USBD clock + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_USBD, ClockState ); + } + break; + case NvRmModuleID_2D: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_2D, ClockState ); + break; + case NvRmModuleID_Epp: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_EPP, ClockState ); + break; + case NvRmModuleID_Vi: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_VI, ClockState ); + break; + case NvRmModuleID_I2s: + NV_ASSERT( Instance < 2 ); + if( Instance == 0 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_I2S1, ClockState ); + } + else if( Instance == 1 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_I2S2, ClockState ); + } + break; + case NvRmModuleID_Hsmmc: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_HSMMC, ClockState ); + break; + case NvRmModuleID_Twc: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_TWC, ClockState ); + break; + case NvRmModuleID_Pwm: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_PWM, ClockState ); + break; + case NvRmModuleID_Sdio: + NV_ASSERT( Instance < 2 ); + if( Instance == 0 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_SDIO1, ClockState ); + } + else if( Instance == 1 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_SDIO2, ClockState ); + } + break; + case NvRmModuleID_Spdif: + NV_ASSERT( Instance < 1 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_SPDIF, ClockState ); + break; + case NvRmModuleID_Nand: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_NDFLASH, ClockState ); + break; + case NvRmModuleID_I2c: + NV_ASSERT( Instance < 2 ); + if( Instance == 0 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_I2C1, ClockState ); + } + else if( Instance == 1 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_I2C2, ClockState ); + } + break; + case NvRmPrivModuleID_Gpio: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_GPIO, ClockState ); + break; + case NvRmModuleID_Uart: + NV_ASSERT( Instance < 3 ); + if( Instance == 0 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_UARTA, ClockState ); + } + else if( Instance == 1 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_UARTB, ClockState ); + } + else if ( Instance == 2) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_UARTC, ClockState ); + } + break; + case NvRmModuleID_Vfir: + // Same as UARTB + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_UARTB, ClockState ); + break; + case NvRmModuleID_Ac97: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_AC97, ClockState ); + break; + case NvRmModuleID_Rtc: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_RTC, ClockState ); + break; + case NvRmModuleID_Timer: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_TMR, ClockState ); + break; + case NvRmModuleID_BseA: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_BSEA, ClockState ); + break; + case NvRmModuleID_Vde: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_VDE, ClockState ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_BSEV, ClockState ); + break; + case NvRmModuleID_Mpe: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_MPE, ClockState ); + break; + case NvRmModuleID_Tvo: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_TVO, ClockState ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_TVDAC, ClockState ); + break; + case NvRmModuleID_Csi: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_CSI, ClockState ); + break; + case NvRmModuleID_Hdmi: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_HDMI, ClockState ); + break; + case NvRmModuleID_Mipi: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_MIPI, ClockState ); + break; + case NvRmModuleID_Dsi: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_DSI, ClockState ); + break; + case NvRmModuleID_Xio: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_XIO, ClockState ); + break; + case NvRmModuleID_Spi: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_SPI1, ClockState ); + break; + case NvRmModuleID_Fuse: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_FUSE, ClockState ); + break; + case NvRmModuleID_Slink: + // Supporting only the slink controller. + NV_ASSERT( Instance < 3 ); + if( Instance == 0 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_SBC1, ClockState ); + } + else if( Instance == 1 ) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_SBC2, ClockState ); + } + else if ( Instance == 2) + { + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_SBC3, ClockState ); + } + break; + case NvRmModuleID_Dvc: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_DVC_I2C, ClockState ); + break; + case NvRmModuleID_Pmif: + NV_ASSERT( Instance == 0 ); + // PMC clock must not be disabled + if (ClockState == ModuleClockState_Enable) + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_PMC, ClockState ); + break; + case NvRmModuleID_SysStatMonitor: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_STAT_MON, ClockState ); + break; + case NvRmModuleID_Kbc: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_KBC, ClockState ); + break; + case NvRmPrivModuleID_ApbDma: + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_APBDMA, ClockState ); + break; + case NvRmPrivModuleID_MemoryController: + // FIXME: should this be allowed? + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_MEM, ClockState ); + break; + case NvRmPrivModuleID_ExternalMemoryController: + // FIXME: should this be allowed? + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_EMC, ClockState ); + CLOCK_ENABLE( hDevice, CLK_SOURCE_EMC, EMC_2X_CLK_ENB, ClockState ); + CLOCK_ENABLE( hDevice, CLK_SOURCE_EMC, EMC_1X_CLK_ENB, ClockState ); + break; + case NvRmModuleID_Cpu: + // FIXME: should this be allowed? + NV_ASSERT( Instance == 0 ); + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_L, CLK_ENB_CPU, ClockState ); + break; + default: + NV_ASSERT(!" Unknown NvRmModuleID passed to Ap15EnableModuleClock(). "); + } + + if (ClockState == ModuleClockState_Disable) + { + NvRmPrivConfigureClockSource(hDevice, ModuleId, NV_FALSE); + } +} + +void +Ap15EnableTvDacClock( + NvRmDeviceHandle hDevice, + ModuleClockState ClockState) +{ + NvU32 reg; + NvU32 regaddr; + + CLOCK_ENABLE( hDevice, CLK_OUT_ENB_H, CLK_ENB_TVDAC, ClockState ); +} + +/*****************************************************************************/ + + // Note that VDE has different reset sequence requirement + // FIMXE: NV blocks - hot reset issues + #define RESET( rm, offset, field, delay ) \ + do { \ + regaddr = (CLK_RST_CONTROLLER_##offset##_0); \ + NvOsMutexLock((rm)->CarMutex); \ + reg = NV_REGR((rm), NvRmPrivModuleID_ClockAndReset, 0, regaddr); \ + reg = NV_FLD_SET_DRF_NUM( \ + CLK_RST_CONTROLLER, offset, field, 1, reg); \ + NV_REGW((rm), NvRmPrivModuleID_ClockAndReset, 0, regaddr, reg); \ + if (Hold) \ + {\ + NvOsMutexUnlock((rm)->CarMutex); \ + break; \ + }\ + NvRmPrivWaitUS( (rm), (delay) ); \ + reg = NV_FLD_SET_DRF_NUM( \ + CLK_RST_CONTROLLER, offset, field, 0, reg); \ + NV_REGW((rm), NvRmPrivModuleID_ClockAndReset, 0, regaddr, reg); \ + NvOsMutexUnlock((rm)->CarMutex); \ + } while( 0 ) + +// KBC reset is available in the pmc control register. +static void RESET_KBC(NvRmDeviceHandle rm, NvU32 delay, NvBool Hold) +{ + NvU32 reg; + NvU32 regaddr; + + regaddr = (APBDEV_PMC_CNTRL_0); + NvOsMutexLock((rm)->CarMutex); + reg = NV_REGR((rm), NvRmModuleID_Pmif, 0, regaddr); + reg = NV_FLD_SET_DRF_DEF(APBDEV_PMC, CNTRL, KBC_RST, ENABLE, reg); + NV_REGW((rm), NvRmModuleID_Pmif, 0, regaddr, reg); + if (Hold) + { + NvOsMutexUnlock((rm)->CarMutex); + return; + } + NvRmPrivWaitUS( (rm), (delay) ); + reg = NV_FLD_SET_DRF_DEF(APBDEV_PMC, CNTRL, KBC_RST, DISABLE, reg); + NV_REGW((rm), NvRmModuleID_Pmif, 0, regaddr, reg); + NvOsMutexUnlock((rm)->CarMutex); +} + + +// Use PMC control to reset the entire SoC. Just wait forever after reset is +// issued - h/w would auto-clear it and restart SoC +static void RESET_SOC(NvRmDeviceHandle rm) +{ + NvU32 reg; + NvU32 regaddr; + + volatile NvBool b = NV_TRUE; + regaddr = (APBDEV_PMC_CNTRL_0); + reg = NV_REGR((rm), NvRmModuleID_Pmif, 0, regaddr); + reg = NV_FLD_SET_DRF_DEF(APBDEV_PMC, CNTRL, MAIN_RST, ENABLE, reg); + NV_REGW((rm), NvRmModuleID_Pmif, 0, regaddr, reg); + while (b) { ; } +} + + +void AP15ModuleReset( + NvRmDeviceHandle hDevice, + NvRmModuleID ModuleId, + NvBool Hold) +{ + // Extract module and instance from composite module id. + NvU32 Module = NVRM_MODULE_ID_MODULE( ModuleId ); + NvU32 Instance = NVRM_MODULE_ID_INSTANCE( ModuleId ); + NvU32 reg; + NvU32 regaddr; + + switch( Module ) { + case NvRmPrivModuleID_MemoryController: + // FIXME: should this be allowed? + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_MEM_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Kbc: + NV_ASSERT( Instance == 0 ); + RESET_KBC(hDevice, NVRM_RESET_DELAY, Hold); + break; + case NvRmModuleID_SysStatMonitor: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_STAT_MON_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Pmif: + NV_ASSERT( Instance == 0 ); + NV_ASSERT(!"PMC reset is not allowed, and does nothing on AP15"); + // RESET( hDevice, RST_DEVICES_H, SWR_PMC_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Fuse: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_FUSE_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Slink: + // Supporting only the slink controller. + NV_ASSERT( Instance < 3 ); + if( Instance == 0 ) + { + RESET( hDevice, RST_DEVICES_H, SWR_SBC1_RST, NVRM_RESET_DELAY ); + } + else if( Instance == 1 ) + { + RESET( hDevice, RST_DEVICES_H, SWR_SBC2_RST, NVRM_RESET_DELAY ); + } + else if ( Instance == 2) + { + RESET( hDevice, RST_DEVICES_H, SWR_SBC3_RST, NVRM_RESET_DELAY ); + } + break; + case NvRmModuleID_Spi: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_SPI1_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Xio: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_XIO_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Dvc: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_DVC_I2C_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Dsi: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_DSI_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Tvo: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_TVO_RST, NVRM_RESET_DELAY ); + RESET( hDevice, RST_DEVICES_H, SWR_TVDAC_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Mipi: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_MIPI_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Hdmi: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_HDMI_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Csi: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_CSI_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_I2c: + NV_ASSERT( Instance < 2 ); + if( Instance == 0 ) + { + RESET( hDevice, RST_DEVICES_L, SWR_I2C1_RST, NVRM_RESET_DELAY ); + } + else if( Instance == 1 ) + { + RESET( hDevice, RST_DEVICES_H, SWR_I2C2_RST, NVRM_RESET_DELAY ); + } + break; + case NvRmModuleID_Mpe: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_MPE_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Vde: + NV_ASSERT( Instance == 0 ); + { + NvU32 reg; + NvOsMutexLock(hDevice->CarMutex); + reg = NV_REGR(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0); + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, RST_DEVICES_H, + SWR_VDE_RST, 1, reg); + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, reg); + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, RST_DEVICES_H, + SWR_BSEV_RST, 1, reg); + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, reg); + if (Hold) + { + NvOsMutexUnlock(hDevice->CarMutex); + break; + } + NvRmPrivWaitUS( hDevice, NVRM_RESET_DELAY ); + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, RST_DEVICES_H, + SWR_BSEV_RST, 0, reg); + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, reg); + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, RST_DEVICES_H, + SWR_VDE_RST, 0, reg); + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, reg); + NvOsMutexUnlock(hDevice->CarMutex); + } + break; + case NvRmModuleID_BseA: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_BSEA_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Cpu: + // FIXME: should this be allowed? + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_CPU_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Avp: + // FIXME: should this be allowed? + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_COP_RST, NVRM_RESET_DELAY ); + break; + case NvRmPrivModuleID_System: + // THIS WILL DO A FULL SYSTEM RESET + NV_ASSERT( Instance == 0 ); + RESET_SOC(hDevice); + break; + case NvRmModuleID_Ac97: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_AC97_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Rtc: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_RTC_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Timer: + NV_ASSERT( Instance == 0 ); + // Timer reset (which also affects microsecond timer) is not allowed + // RESET( hDevice, RST_DEVICES_L, SWR_TMR_RST, NVRM_RESET_DELAY ); + NV_ASSERT(!"Timer reset is not allowed"); + break; + case NvRmModuleID_Uart: + NV_ASSERT( Instance < 3 ); + if( Instance == 0 ) + { + RESET( hDevice, RST_DEVICES_L, SWR_UARTA_RST, NVRM_RESET_DELAY ); + } + else if( Instance == 1 ) + { + RESET( hDevice, RST_DEVICES_L, SWR_UARTB_RST, NVRM_RESET_DELAY ); + } + else if ( Instance == 2) + { + RESET( hDevice, RST_DEVICES_H, SWR_UARTC_RST, NVRM_RESET_DELAY ); + } + break; + case NvRmModuleID_Vfir: + // Same as UARTB + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_UARTB_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Sdio: + NV_ASSERT( Instance < 2 ); + if( Instance == 0 ) + { + RESET( hDevice, RST_DEVICES_L, SWR_SDIO1_RST, NVRM_RESET_DELAY ); + } + else if( Instance == 1 ) + { + RESET( hDevice, RST_DEVICES_L, SWR_SDIO2_RST, NVRM_RESET_DELAY ); + } + break; + case NvRmModuleID_Spdif: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_SPDIF_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_I2s: + NV_ASSERT( Instance < 2 ); + if( Instance == 0 ) + { + RESET( hDevice, RST_DEVICES_L, SWR_I2S1_RST, NVRM_RESET_DELAY ); + } + else if( Instance == 1 ) + { + RESET( hDevice, RST_DEVICES_L, SWR_I2S2_RST, NVRM_RESET_DELAY ); + } + break; + case NvRmModuleID_Nand: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_NDFLASH_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Hsmmc: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_HSMMC_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Twc: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_TWC_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Pwm: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_PWM_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Epp: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_EPP_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Vi: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_VI_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_3D: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_3D_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_2D: + NV_ASSERT( Instance == 0 ); + // RESET( hDevice, RST_DEVICES_L, SWR_2D_RST, NVRM_RESET_DELAY ); + // WAR for bug 364497, se also NvRmPrivAP15Reset2D() + NV_ASSERT(!"2D reset after RM open is no longer allowed"); + break; + case NvRmModuleID_Usb2Otg: + { +#if !NV_OAL + NvU32 RegVal = 0; + NV_ASSERT( Instance < 2 ); + if (hDevice->ChipId.Id == 0x16) + { + RegVal = NV_REGR(hDevice, NvRmModuleID_Misc, 0, APB_MISC_PP_MISC_USB_CLK_RST_CTL_0); + if (!(NV_DRF_VAL(APB_MISC_PP, MISC_USB_CLK_RST_CTL, MISC_USB_CE, RegVal)) && + !(NV_DRF_VAL(APB_MISC_PP, MISC_USB_CLK_RST_CTL, MISC_USB2_CE, RegVal)) ) + { + /// Reset USBD if USB1/USB2 is not enabled already + RESET( hDevice, RST_DEVICES_L, SWR_USBD_RST, NVRM_RESET_DELAY ); + } + } + else + { + /// Reset USBD + RESET( hDevice, RST_DEVICES_L, SWR_USBD_RST, NVRM_RESET_DELAY ); + } +#else + /// Reset USBD + RESET( hDevice, RST_DEVICES_L, SWR_USBD_RST, NVRM_RESET_DELAY ); +#endif + } + break; + case NvRmModuleID_Isp: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_ISP_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Ide: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_IDE_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_Display: + NV_ASSERT( Instance < 2 ); + if( Instance == 0 ) + { + RESET( hDevice, RST_DEVICES_L, SWR_DISP1_RST, NVRM_RESET_DELAY ); + } + else if( Instance == 1 ) + { + RESET( hDevice, RST_DEVICES_L, SWR_DISP2_RST, NVRM_RESET_DELAY ); + } + break; + case NvRmModuleID_Vcp: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_VCP_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_CacheMemCtrl: + NV_ASSERT( Instance < 2 ); + if( Instance == 0 ) + { + RESET( hDevice, RST_DEVICES_L, SWR_CACHE1_RST, NVRM_RESET_DELAY ); + } + else if( Instance == 1 ) + { + RESET( hDevice, RST_DEVICES_L, SWR_CACHE2_RST, NVRM_RESET_DELAY ); + } + break; + case NvRmPrivModuleID_ApbDma: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_H, SWR_APBDMA_RST, NVRM_RESET_DELAY ); + break; + case NvRmPrivModuleID_Gpio: + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_GPIO_RST, NVRM_RESET_DELAY ); + break; + case NvRmModuleID_GraphicsHost: + // FIXME: should this be allowed? + NV_ASSERT( Instance == 0 ); + RESET( hDevice, RST_DEVICES_L, SWR_HOST1X_RST, NVRM_RESET_DELAY ); + break; + default: + NV_ASSERT(!"Invalid ModuleId"); + } + + #undef RESET +} + +/*****************************************************************************/ + +void +NvRmPrivAp15Reset2D(NvRmDeviceHandle hRmDevice) +{ +#if !NV_OAL + NvU32 reg, offset; + /* + * WAR for bug 364497: 2D can not be taken out of reset if VI clock is + * running. Therefore, make sure VI clock is disabled and reset 2D here + * during RM initialization. + */ + Ap15EnableModuleClock(hRmDevice, NvRmModuleID_Vi, ModuleClockState_Disable); + + // Assert reset to 2D module + offset = CLK_RST_CONTROLLER_RST_DEVICES_L_0; + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, offset); + reg = NV_FLD_SET_DRF_DEF( + CLK_RST_CONTROLLER, RST_DEVICES_L, SWR_2D_RST, ENABLE, reg); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, offset, reg); + + // Enable "known good" configuartion for 2D clock (PLLM divided by 2) + offset = CLK_RST_CONTROLLER_CLK_SOURCE_G2D_0; + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, offset, + (NV_DRF_NUM(CLK_RST_CONTROLLER, CLK_SOURCE_G2D, G2D_CLK_DIVISOR, 2) | + NV_DRF_DEF(CLK_RST_CONTROLLER, CLK_SOURCE_G2D, G2D_CLK_SRC, PLLM_OUT0))); + Ap15EnableModuleClock(hRmDevice, NvRmModuleID_2D, ModuleClockState_Enable); + NvOsWaitUS(NVRM_RESET_DELAY); + + // Take 2D out of reset and disable 2D clock. Both VI and 2D clocks are + // left disabled -it is up to the resepctive drivers to configure and enable + // them later. + offset = CLK_RST_CONTROLLER_RST_DEVICES_L_0; + reg = NV_FLD_SET_DRF_DEF( + CLK_RST_CONTROLLER, RST_DEVICES_L, SWR_2D_RST, DISABLE, reg); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, offset, reg); + Ap15EnableModuleClock(hRmDevice, NvRmModuleID_2D, ModuleClockState_Disable); +#endif +} + +void +NvRmPrivAp15ClockConfigEx( + NvRmDeviceHandle hDevice, + NvRmModuleID Module, + NvU32 ClkSourceOffset, + NvU32 flags) +{ + NvU32 reg; + + if ((Module == NvRmModuleID_Vi) && + (!(flags & NvRmClockConfig_SubConfig)) && + (flags & (NvRmClockConfig_InternalClockForPads | + NvRmClockConfig_ExternalClockForPads | + NvRmClockConfig_InternalClockForCore | + NvRmClockConfig_ExternalClockForCore))) + { +#ifdef CLK_RST_CONTROLLER_CLK_SOURCE_VI_0_PD2VI_CLK_SEL_FIELD + reg = NV_REGR( + hDevice, NvRmPrivModuleID_ClockAndReset, 0, ClkSourceOffset); + + /* Default is pads use External and Core use internal */ + reg = NV_FLD_SET_DRF_NUM( + CLK_RST_CONTROLLER, CLK_SOURCE_VI, PD2VI_CLK_SEL, 0, reg); + reg = NV_FLD_SET_DRF_NUM( + CLK_RST_CONTROLLER, CLK_SOURCE_VI, VI_CLK_SEL, 0, reg); + + /* This is an invalid setting. */ + NV_ASSERT(!((flags & NvRmClockConfig_InternalClockForPads) && + (flags & NvRmClockConfig_ExternalClockForCore))); + + if (flags & NvRmClockConfig_InternalClockForPads) + reg = NV_FLD_SET_DRF_NUM( + CLK_RST_CONTROLLER, CLK_SOURCE_VI, PD2VI_CLK_SEL, 1, reg); + if (flags & NvRmClockConfig_ExternalClockForCore) + reg = NV_FLD_SET_DRF_NUM( + CLK_RST_CONTROLLER, CLK_SOURCE_VI, VI_CLK_SEL, 1, reg); + + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + ClkSourceOffset, reg); +#endif + } + if (Module == NvRmModuleID_I2s) + { + reg = NV_REGR( + hDevice, NvRmPrivModuleID_ClockAndReset, 0, ClkSourceOffset); + + if (flags & NvRmClockConfig_ExternalClockForCore) + { + // Set I2S in slave mode (field definition is the same for I2S1 and I2S2) + reg = NV_FLD_SET_DRF_NUM( + CLK_RST_CONTROLLER, CLK_SOURCE_I2S1, I2S1_MASTER_CLKEN, 0, reg); + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + ClkSourceOffset, reg); + } + else if (flags & NvRmClockConfig_InternalClockForCore) + { + // Set I2S in master mode (field definition is the same for I2S1 and I2S2) + reg = NV_FLD_SET_DRF_NUM( + CLK_RST_CONTROLLER, CLK_SOURCE_I2S1, I2S1_MASTER_CLKEN, 1, reg); + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + ClkSourceOffset, reg); + } + } +} + +void NvRmPrivAp15SimPllInit(NvRmDeviceHandle hRmDevice) +{ + NvU32 RegData; + + //Enable the plls in simulation. We can just use PLLC as the template + //and replicate across pllM and pllP since the offsets are the same. + RegData = NV_DRF_NUM (CLK_RST_CONTROLLER, PLLC_BASE, PLLC_DIVP, 0) + | NV_DRF_NUM (CLK_RST_CONTROLLER, PLLC_BASE, PLLC_DIVM, 0) + | NV_DRF_NUM (CLK_RST_CONTROLLER, PLLC_BASE, PLLC_DIVN, 0) + | NV_DRF_DEF (CLK_RST_CONTROLLER, PLLC_BASE, PLLC_BYPASS, DISABLE) + | NV_DRF_DEF (CLK_RST_CONTROLLER, PLLC_BASE, PLLC_ENABLE, ENABLE) ; + + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_PLLM_BASE_0, RegData); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_PLLC_BASE_0, RegData); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_PLLP_BASE_0, RegData); +} + +NvError +NvRmPrivAp15OscDoublerConfigure( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz OscKHz) +{ + NvU32 reg, Taps; + NvError error = NvRmPrivGetOscDoublerTaps(hRmDevice, OscKHz, &Taps); + + if (error == NvSuccess) + { + // Program delay + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_PROG_DLY_CLK_0); + reg = NV_FLD_SET_DRF_NUM( + CLK_RST_CONTROLLER, PROG_DLY_CLK, CLK_D_DELCLK_SEL, Taps, reg); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_PROG_DLY_CLK_0, reg); + // Enable doubler + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0); + reg = NV_FLD_SET_DRF_NUM( + CLK_RST_CONTROLLER, MISC_CLK_ENB, CLK_M_DOUBLER_ENB, 1, reg); + } + else + { + // Disable doubler + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0); + reg = NV_FLD_SET_DRF_NUM( + CLK_RST_CONTROLLER, MISC_CLK_ENB, CLK_M_DOUBLER_ENB, 0, reg); + } + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0, reg); + return error; +} + +/*****************************************************************************/ + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.h b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.h new file mode 100644 index 000000000000..9033261ba9ac --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.h @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef INCLUDED_AP15RM_CLOCKS_H +#define INCLUDED_AP15RM_CLOCKS_H + +#include "nvrm_clocks.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +extern const NvRmModuleClockInfo g_Ap15ModuleClockTable[]; +extern const NvU32 g_Ap15ModuleClockTableSize; + +// PLLM ratios for graphic clocks +#define NVRM_PLLM_HOST_SPEED_RATIO (4) +#define NVRM_PLLM_2D_LOW_SPEED_RATIO (3) +#define NVRM_PLLM_2D_HIGH_SPEED_RATIO (2) + +/** + * Defines frequency steps derived from PLLP0 fixed output to be used as System + * clock source frequency. The frequency specified in kHz, and it will be rounded + * up to the closest divider output. + */ +#define NVRM_AP15_PLLP_POLICY_SYSTEM_CLOCK \ + PLLP_POLICY_ENTRY(54000) /* PLLP divider 6, output frequency 54,000kHz */ \ + PLLP_POLICY_ENTRY(72000) /* PLLP divider 4, output frequency 72,000kHz */ \ + PLLP_POLICY_ENTRY(108000) /* PLLP divider 2, output frequency 108,000kHz */ \ + PLLP_POLICY_ENTRY(144000) /* PLLP divider 1, output frequency 144,000kHz */ \ + PLLP_POLICY_ENTRY(216000) /* PLLP divider 0, output frequency 216,000kHz */ + +/** + * Defines frequency steps derived from PLLP0 fixed output to be used as CPU + * clock source frequency. The frequency specified in kHz, and it will be rounded + * up to the closest divider output. + */ +#define NVRM_AP15_PLLP_POLICY_CPU_CLOCK \ + PLLP_POLICY_ENTRY(24000) /* PLLP divider 16, output frequency 24,000kHz */ \ + PLLP_POLICY_ENTRY(54000) /* PLLP divider 6, output frequency 54,000kHz */ \ + PLLP_POLICY_ENTRY(108000) /* PLLP divider 2, output frequency 108,000kHz */ \ + PLLP_POLICY_ENTRY(216000) /* PLLP divider 0, output frequency 216,000kHz */ \ + +/** + * Combines EMC 2x frequency and the respective set of EMC timing parameters for + * pre-defined EMC configurations (DDR clock is running at EMC 1x frequency) + */ +typedef struct NvRmAp15EmcTimingConfigRec +{ + NvRmFreqKHz Emc2xKHz; + NvU32 Timing0Reg; + NvU32 Timing1Reg; + NvU32 Timing2Reg; + NvU32 Timing3Reg; + NvU32 Timing4Reg; + NvU32 Timing5Reg; + NvU32 FbioCfg6Reg; + NvU32 FbioDqsibDly; + NvU32 FbioQuseDly; + NvU32 Emc2xDivisor; + NvRmFreqKHz McKHz; + NvU32 McDivisor; + NvU32 McClockSource; + NvRmFreqKHz CpuLimitKHz; + NvRmMilliVolts CoreVoltageMv; +} NvRmAp15EmcTimingConfig; + +// Defines number of EMC frequency steps for DFS +#define NVRM_AP15_DFS_EMC_FREQ_STEPS (5) + +// Dfines CPU and EMC ratio policy as +// CpuKHz/CpuMax <= PolicyTabel[PLLM0/(2*EMC2xKHz)] / 256 +#define NVRM_AP15_CPU_EMC_RATIO_POLICY \ + 256, 192, 144, 122, 108, 98, 91, 86, 81, 77 + +/*****************************************************************************/ + +/** + * Enables/disables module clock. + * + * @param hDevice The RM device handle. + * @param ModuleId Combined module ID and instance of the target module. + * @param ClockState Target clock state. + */ +void +Ap15EnableModuleClock( + NvRmDeviceHandle hDevice, + NvRmModuleID ModuleId, + ModuleClockState ClockState); + +// Separate API to control TVDAC clock independently of TVO +// (when TVDAC is used for CRT) +void +Ap15EnableTvDacClock( + NvRmDeviceHandle hDevice, + ModuleClockState ClockState); + +/** + * Resets module (assert/delay/deassert reset signal) if the hold paramter is + * NV_FLASE. If the hols paramter is NV_TRUE, just assert the reset and return. + * + * @param hDevice The RM device handle. + * @param Module Combined module ID and instance of the target module. + * @param hold To hold or relese the reset. + */ +void AP15ModuleReset(NvRmDeviceHandle hDevice, NvRmModuleID ModuleId, NvBool hold); + +/*****************************************************************************/ + +/** + * Initializes PLL references table. + * + * @param pPllReferencesTable A pointer to a pointer which this function sets + * to the PLL reference table base. + * @param pPllReferencesTableSize A pointer to a variable which this function + * sets to the PLL reference table size. + */ +void +NvRmPrivAp15PllReferenceTableInit( + NvRmPllReference** pPllReferencesTable, + NvU32* pPllReferencesTableSize); + +/** + * Initializes EMC clocks configuration structures and tables. + * + * @param hRmDevice The RM device handle. + */ +void +NvRmPrivAp15EmcConfigInit(NvRmDeviceHandle hRmDevice); + +/** + * Resets 2D module. + * + * @param hRmDevice The RM device handle. + */ +void +NvRmPrivAp15Reset2D(NvRmDeviceHandle hRmDevice); + +/** + * Initializes clock source table. + * + * @return Pointer to the clock sources descriptor table. + */ +NvRmClockSourceInfo* NvRmPrivAp15ClockSourceTableInit(void); + +/** + * Sets "as is" specified PLL configuration: switches PLL in bypass mode, + * changes PLL settings, waits for PLL stabilization, and switches to PLL + * output. + * + * @param hRmDevice The RM device handle. + * @param pCinfo Pointer to the PLL description structure. + * @param M PLL input divider setting. + * @param N PLL feedback divider setting. + * @param P PLL output divider setting. + * PLL is left disabled (not bypassed) if either M or N setting is zero: + * M = 0 or N = 0; otherwise, M, N, P validation is caller responsibility. + * @param StableDelayUs PLL stabilization delay in microseconds. If specified + * value is above guaranteed stabilization time, the latter one is used. + * @param cpcon PLL charge pump control setting; ignored if TypicalControls + * is true. + * @param lfcon PLL loop filter control setting; ignored if TypicalControls + * is true. + * @param TypicalControls If true, both charge pump and loop filter parameters + * are ignored and typical controls that corresponds to specified M, N, P + * values will be set. If false, the cpcon and lfcon parameters are set; in + * this case parameter validation is caller responsibility. + * @param flags PLL specific flags. Thse flags are valid only for some PLLs, + * see @NvRmPllConfigFlags. + */ +void +NvRmPrivAp15PllSet( + NvRmDeviceHandle hRmDevice, + const NvRmPllClockInfo* pCinfo, + NvU32 M, + NvU32 N, + NvU32 P, + NvU32 StableDelayUs, + NvU32 cpcon, + NvU32 lfcon, + NvBool TypicalControls, + NvU32 flags); + +/** + * Configures output frequency for specified PLL. + * + * @param hRmDevice The RM device handle. + * @param PllId Targeted PLL ID. + * @param MaxOutKHz Upper limit for PLL output frequency. + * @param pPllOutKHz A pointer to the requested PLL frequency on entry, + * and to the actually configured frequency on exit. + */ +void +NvRmPrivAp15PllConfigureSimple( + NvRmDeviceHandle hRmDevice, + NvRmClockSource PllId, + NvRmFreqKHz MaxOutKHz, + NvRmFreqKHz* pPllOutKHz); + +/** + * Configures specified PLL output to the CM of fixed HDMI frequencies. + * + * @param hRmDevice The RM device handle. + * @param PllId Targeted PLL ID. + * @param pPllOutKHz A pointer to the actually configured frequency on exit. + */ +void +NvRmPrivAp15PllConfigureHdmi( + NvRmDeviceHandle hRmDevice, + NvRmClockSource PllId, + NvRmFreqKHz* pPllOutKHz); + +/** + * Gets PLL output frequency. + * + * @param hRmDevice The RM device handle. + * @param pCinfo Pointer to the PLL description structure. + * + * @return PLL output frequency in kHz (reference frequency if PLL + * is by-passed; zero if PLL is disabled and not by-passed). + */ +NvRmFreqKHz +NvRmPrivAp15PllFreqGet( + NvRmDeviceHandle hRmDevice, + const NvRmPllClockInfo* pCinfo); + +/** + * Gets frequencies of DFS controlled clocks + * + * @param hRmDevice The RM device handle. + * @param pDfsKHz Output storage pointer for DFS clock frequencies structure + * (all frequencies returned in kHz). + */ +void +NvRmPrivAp15DfsClockFreqGet( + NvRmDeviceHandle hRmDevice, + NvRmDfsFrequencies* pDfsKHz); + +/** + * Configures DFS controlled clocks + * + * @param hRmDevice The RM device handle. + * @param pMaxKHz Pointer to the DFS clock frequencies upper limits + * @param pDfsKHz Pointer to the target DFS frequencies structure on entry; + * updated with actual DFS clock frequencies on exit. + * + * @return NV_TRUE if clock configuration is completed; NV_FALSE if this + * function has to be called again to complete configuration. + */ +NvBool +NvRmPrivAp15DfsClockConfigure( + NvRmDeviceHandle hRmDevice, + const NvRmDfsFrequencies* pMaxKHz, + NvRmDfsFrequencies* pDfsKHz); + +/** + * Gets maximum DFS domains frequencies that can be used at specified + * core voltage. + * + * @param hRmDevice The RM device handle. + * @param TargetMv Targeted core voltage in mV. + * @param pDfsKHz Pointer to a structure filled in by this function with + * output clock frequencies. + */ +void +NvRmPrivAp15DfsVscaleFreqGet( + NvRmDeviceHandle hRmDevice, + NvRmMilliVolts TargetMv, + NvRmDfsFrequencies* pDfsKHz); + +/** + * Determines if module clock configuration requires AP15-specific handling, + * and configures the clock if yes. + * + * @param hRmDevice The RM device handle. + * @param pCinfo Pointer to the module clock descriptor. + * @param ClockSourceCount Number of module clock sources. + * @param MinFreq Requested minimum module clock frequency. + * @param MaxFreq Requested maximum module clock frequency. + * @param PrefFreqList Pointer to a list of preferred frequencies sorted + * in the decreasing order of priority. + * @param PrefCount Number of entries in the PrefFreqList array. + * @param pCstate Pointer to module state structure filled in if special + * handling is completed. + * @param flags Module specific flags + * + * @return True indicates that module clock is configured, and regular + * configuration should be aborted; False indicates that regular clock + * configuration should proceed. + */ +NvBool +NvRmPrivAp15IsModuleClockException( + NvRmDeviceHandle hRmDevice, + NvRmModuleClockInfo *pCinfo, + NvU32 ClockSourceCount, + NvRmFreqKHz MinFreq, + NvRmFreqKHz MaxFreq, + const NvRmFreqKHz* PrefFreqList, + NvU32 PrefCount, + NvRmModuleClockState* pCstate, + NvU32 flags); + +/** + * Configures EMC low-latency fifo for CPU clock source switch. + * + * @param hRmDevice The RM device handle. + */ +void +NvRmPrivAp15SetEmcForCpuSrcSwitch(NvRmDeviceHandle hRmDevice); + +/** + * Configures EMC low-latency fifo for CPU clock divider switch. + * + * @param hRmDevice The RM device handle. + * @param CpuFreq Resulting CPU frequency after divider switch + * @param Before Specifies if this function is called before (True) + * or after (False) divider changes. + */ +void +NvRmPrivAp15SetEmcForCpuDivSwitch( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz CpuFreq, + NvBool Before); + +/** + * Configures maximum core and memory clocks. + * + * @param hRmDevice The RM device handle. + */ +void +NvRmPrivAp15FastClockConfig(NvRmDeviceHandle hRmDevice); + +/** + * Gets module frequency synchronized with EMC speed. + * + * @param hRmDevice The RM device handle. + * @param Module The target module ID. + * + * @return Module frequency in kHz. + */ +NvRmFreqKHz NvRmPrivAp15GetEmcSyncFreq( + NvRmDeviceHandle hRmDevice, + NvRmModuleID Module); + +/** + * Disables PLLs + * + * @param hRmDevice The RM device handle. + * @param pCinfo Pointer to the last configured module clock descriptor. + * @param pCstate Pointer to the last configured module state structure. + */ +void +NvRmPrivAp15DisablePLLs( + NvRmDeviceHandle hRmDevice, + const NvRmModuleClockInfo* pCinfo, + const NvRmModuleClockState* pCstate); + +/** + * Turns PLLD (MIPI PLL) power On/Off + * + * @param hRmDevice The RM device handle. + * @param ConfigEntry NV_TRUE if this function is called before display + * clock configuration; NV_FALSE otherwise. + * @param Pointer to the current state of MIPI PLL power rail, updated + * by this function. + */ +void +NvRmPrivAp15PllDPowerControl( + NvRmDeviceHandle hRmDevice, + NvBool ConfigEntry, + NvBool* pMipiPllVddOn); + +/** + * Clips EMC frequency high limit to one of the fixed DFS EMC configurations, + * and if necessary adjust CPU high limit respectively. + * + * @param hRmDevice The RM device handle. + * @param pCpuHighKHz A pointer to the variable, which contains CPU frequency + * high limit in KHz (on entry - requested limit, on exit - clipped limit) + * @param pEmcHighKHz A pointer to the variable, which contains EMC frequency + * high limit in KHz (on entry - requested limit, on exit - clipped limit) + */ +void +NvRmPrivAp15ClipCpuEmcHighLimits( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz* pCpuHighKHz, + NvRmFreqKHz* pEmcHighKHz); + + +/** + * Configures some special bits in the clock source register for given module. + * + * @param hRmDevice The RM device handle. + * @param Module Target module ID. + * @param ClkSourceOffset Clock source register offset. + * @param flags Module specific clock configuration flags. + */ +void +NvRmPrivAp15ClockConfigEx( + NvRmDeviceHandle hRmDevice, + NvRmModuleID Module, + NvU32 ClkSourceOffset, + NvU32 flags); + +/** + * Enables PLL in simulation. + * + * @param hRmDevice The RM device handle. + */ +void NvRmPrivAp15SimPllInit(NvRmDeviceHandle hRmDevice); + +/** + * Configures oscillator (main) clock doubler. + * + * @param hRmDevice The RM device handle. + * @param OscKHz Oscillator (main) clock frequency in kHz. + * + * @return NvSuccess if the specified oscillator frequency is supported, and + * NvError_NotSupported, otherwise. + */ +NvError +NvRmPrivAp15OscDoublerConfigure( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz OscKHz); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif // INCLUDED_AP15RM_CLOCKS_H diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks_info.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks_info.c new file mode 100644 index 000000000000..b63198419fd4 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks_info.c @@ -0,0 +1,1673 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvrm_drf.h" +#include "ap15rm_clocks.h" +#include "ap15rm_private.h" +#include "ap15/arclk_rst.h" +#include "ap15/project_relocation_table.h" + +#define NV_COMMON_CLK_RST_FIELDS_INFO(MODULE, H_L) \ + CLK_RST_CONTROLLER_CLK_SOURCE_##MODULE##_0, \ + CLK_RST_CONTROLLER_CLK_SOURCE_##MODULE##_0_##MODULE##_CLK_SRC_DEFAULT_MASK, \ + CLK_RST_CONTROLLER_CLK_SOURCE_##MODULE##_0_##MODULE##_CLK_SRC_SHIFT, \ + CLK_RST_CONTROLLER_CLK_SOURCE_##MODULE##_0_##MODULE##_CLK_DIVISOR_DEFAULT_MASK, \ + CLK_RST_CONTROLLER_CLK_SOURCE_##MODULE##_0_##MODULE##_CLK_DIVISOR_SHIFT, \ + CLK_RST_CONTROLLER_CLK_OUT_ENB_##H_L##_0, \ + CLK_RST_CONTROLLER_CLK_OUT_ENB_##H_L##_0_CLK_ENB_##MODULE##_FIELD, \ + CLK_RST_CONTROLLER_RST_DEVICES_##H_L##_0, \ + CLK_RST_CONTROLLER_RST_DEVICES_##H_L##_0_SWR_##MODULE##_RST_FIELD + +const NvRmModuleClockInfo g_Ap15ModuleClockTable[] = +{ + { /* Invalid module */ + NvRmPrivModuleID_System, 0, 0, + { + NvRmClockSource_SystemBus + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + 0,0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_TRIG_SYS_RST_FIELD, + NvRmDiagModuleID_SystemReset + }, + { /* VI controller module - VI clock */ + NvRmModuleID_Vi, 0 , 0, + { + NvRmClockSource_PllM0, + NvRmClockSource_PllC0, + NvRmClockSource_PllP0, + NvRmClockSource_PllA0 + }, + NvRmClockDivider_Fractional_2, + CLK_RST_CONTROLLER_CLK_SOURCE_VI_0, + CLK_RST_CONTROLLER_CLK_SOURCE_VI_0_VI_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_VI_0_VI_CLK_SRC_SHIFT, + CLK_RST_CONTROLLER_CLK_SOURCE_VI_0_VI_CLK_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_VI_0_VI_CLK_DIVISOR_SHIFT, + + // Combined VI and VI sensor reset and clock enable controls + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_VI_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_VI_RST_FIELD, + NvRmDiagModuleID_Vi + }, + { /* VI controller module - VI sensor clock + * Module sub clock must immediately follow main clock + */ + NvRmModuleID_Vi, 0 , 1, + { + NvRmClockSource_PllM0, + NvRmClockSource_PllC0, + NvRmClockSource_PllP0, + NvRmClockSource_PllA0 + }, + NvRmClockDivider_Fractional_2, + CLK_RST_CONTROLLER_CLK_SOURCE_VI_SENSOR_0, + CLK_RST_CONTROLLER_CLK_SOURCE_VI_SENSOR_0_VI_SENSOR_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_VI_SENSOR_0_VI_SENSOR_CLK_SRC_SHIFT, + CLK_RST_CONTROLLER_CLK_SOURCE_VI_SENSOR_0_VI_SENSOR_CLK_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_VI_SENSOR_0_VI_SENSOR_CLK_DIVISOR_SHIFT, + + // Combined VI and VI sensor reset and clock enable controls + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_VI_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_VI_RST_FIELD, + NvRmDiagModuleID_ViSensor + }, + + { /* I2S1 controller module */ + NvRmModuleID_I2s, 0, 0, + { + NvRmClockSource_PllA0, + NvRmClockSource_AudioSync, + NvRmClockSource_PllP0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(I2S1, L), + NvRmDiagModuleID_I2s + }, + + { /* I2S2 controller module */ + NvRmModuleID_I2s, 1, 0, + { + NvRmClockSource_PllA0, + NvRmClockSource_AudioSync, + NvRmClockSource_PllP0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(I2S2, L), + NvRmDiagModuleID_I2s + }, + + { /* I2C1 controller module */ + NvRmModuleID_I2c, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Integer_1, + NV_COMMON_CLK_RST_FIELDS_INFO(I2C1, L), + NvRmDiagModuleID_I2c + }, + + { /* I2C2 controller module */ + NvRmModuleID_I2c, 1, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Integer_1, + NV_COMMON_CLK_RST_FIELDS_INFO(I2C2, H), + NvRmDiagModuleID_I2c + }, + + { /* Hsmmc controller module */ + NvRmModuleID_Hsmmc, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(HSMMC, L), + NvRmDiagModuleID_Hsmmc + }, + + { /* S/PDIF controller module - S/PDIF OUT clock */ + NvRmModuleID_Spdif, 0, 0, + { + NvRmClockSource_PllA0, + NvRmClockSource_AudioSync, + NvRmClockSource_PllP0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_0, + CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_0_SPDIFOUT_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_0_SPDIFOUT_CLK_SRC_SHIFT, + CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_0_SPDIFOUT_CLK_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_0_SPDIFOUT_CLK_DIVISOR_SHIFT, + + // Combined SPDIF reset and and clock enable controls + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_SPDIF_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_SPDIF_RST_FIELD, + NvRmDiagModuleID_Spdif + }, + { /* S/PDIF controller module - S/PDIF IN clock + * Module sub clock must immediately follow main clock + */ + NvRmModuleID_Spdif, 0, 1, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0 + }, + NvRmClockDivider_Fractional_2, + CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_0, + CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_0_SPDIFIN_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_0_SPDIFIN_CLK_SRC_SHIFT, + CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_0_SPDIFIN_CLK_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_0_SPDIFIN_CLK_DIVISOR_SHIFT, + + // Combined SPDIF reset and and clock enable controls + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_SPDIF_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_SPDIF_RST_FIELD, + NvRmDiagModuleID_SpdifIn + }, + + { /* PWM controller module */ + NvRmModuleID_Pwm, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_AudioSync, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(PWM, L), + NvRmDiagModuleID_Pwm + }, + + { /* SPI controller module */ + NvRmModuleID_Spi, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(SPI1, H), + NvRmDiagModuleID_Spi + }, + + { /* SBC1 controller module */ + NvRmModuleID_Slink, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(SBC1, H), + NvRmDiagModuleID_Sbc + }, + + { /* SBC2 controller module */ + NvRmModuleID_Slink, 1, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(SBC2, H), + NvRmDiagModuleID_Sbc + }, + + { /* SBC3 controller module */ + NvRmModuleID_Slink, 2, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(SBC3, H), + NvRmDiagModuleID_Sbc + }, + + { /* SLC controller module */ + NvRmModuleID_Invalid, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(SLC1, H), + NvRmDiagModuleID_Slc + }, + + { /* TWC controller module */ + NvRmModuleID_Twc, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(TWC, L), + NvRmDiagModuleID_Twc + }, + + { /* XIO controller module */ + NvRmModuleID_Xio, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(XIO, H), + NvRmDiagModuleID_Xio + }, + + { /* IDE controller module */ + NvRmModuleID_Ide, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(IDE, L), + NvRmDiagModuleID_Ide + }, + + { /* SDIO1 controller module */ + NvRmModuleID_Sdio, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(SDIO1, L), + NvRmDiagModuleID_Sdio + }, + + { /* SDIO2 controller module */ + NvRmModuleID_Sdio, 1, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(SDIO2, L), + NvRmDiagModuleID_Sdio + }, + + { /* NAND Flash controller module */ + NvRmModuleID_Nand, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(NDFLASH, L), + NvRmDiagModuleID_NandFlash + }, + + { /* MIPI BB controller module */ + NvRmModuleID_Mipi, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(MIPI, H), + NvRmDiagModuleID_MipiBaseband + }, + + { /* DVC controller module */ + NvRmModuleID_Dvc, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Integer_1, + NV_COMMON_CLK_RST_FIELDS_INFO(DVC_I2C, H), + NvRmDiagModuleID_Dvc + }, + + { /* UARTA controller module */ + NvRmModuleID_Uart, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_None, + CLK_RST_CONTROLLER_CLK_SOURCE_UARTA_0, + CLK_RST_CONTROLLER_CLK_SOURCE_UARTA_0_UARTA_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_UARTA_0_UARTA_CLK_SRC_SHIFT, + 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_UARTA_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_UARTA_RST_FIELD, + NvRmDiagModuleID_Uart + }, + + { /* UARTB controller module */ + NvRmModuleID_Uart, 1, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_None, + CLK_RST_CONTROLLER_CLK_SOURCE_UARTB_0, + CLK_RST_CONTROLLER_CLK_SOURCE_UARTB_0_UARTB_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_UARTB_0_UARTB_CLK_SRC_SHIFT, + 0, 0, + + // Combined UARTB and VFIR reset and clock enable controls + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_UARTB_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_UARTB_RST_FIELD, + NvRmDiagModuleID_Uart + }, + + { /* UARTC controller module */ + NvRmModuleID_Uart, 2, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_None, + CLK_RST_CONTROLLER_CLK_SOURCE_UARTC_0, + CLK_RST_CONTROLLER_CLK_SOURCE_UARTC_0_UARTC_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_UARTC_0_UARTC_CLK_SRC_SHIFT, + 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_UARTC_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_UARTC_RST_FIELD, + NvRmDiagModuleID_Uart + }, + + { /* VFIR controller module */ + NvRmModuleID_Vfir, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllC0, + NvRmClockSource_PllM0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + CLK_RST_CONTROLLER_CLK_SOURCE_VFIR_0, + CLK_RST_CONTROLLER_CLK_SOURCE_VFIR_0_VFIR_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_VFIR_0_VFIR_CLK_SRC_SHIFT, + CLK_RST_CONTROLLER_CLK_SOURCE_VFIR_0_VFIR_CLK_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_VFIR_0_VFIR_CLK_DIVISOR_SHIFT, + + // Combined UARTB and VFIR reset and clock enable controls + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_UARTB_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_UARTB_RST_FIELD, + NvRmDiagModuleID_Vfir + }, + + { /* Host1x module */ + NvRmModuleID_GraphicsHost, 0, 0, + { + NvRmClockSource_PllM0, + NvRmClockSource_PllC0, + NvRmClockSource_PllP0, + NvRmClockSource_PllA0 + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(HOST1X, L), + NvRmDiagModuleID_Host1x + }, + + { /* EPP controller module */ + NvRmModuleID_Epp, 0, 0, + { + NvRmClockSource_PllM0, + NvRmClockSource_PllC0, + NvRmClockSource_PllP0, + NvRmClockSource_PllA0 + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(EPP, L), + NvRmDiagModuleID_Epp + }, + + { /* MPE controller module */ + NvRmModuleID_Mpe, 0, 0, + { + NvRmClockSource_PllM0, + NvRmClockSource_PllC0, + NvRmClockSource_PllP0, + NvRmClockSource_PllA0 + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(MPE, H), + NvRmDiagModuleID_Mpe + }, + + { /* 2D controller module */ + NvRmModuleID_2D, 0, 0, + { + NvRmClockSource_PllM0, + NvRmClockSource_PllC0, + NvRmClockSource_PllP0, + NvRmClockSource_PllA0 + }, + NvRmClockDivider_Fractional_2, + CLK_RST_CONTROLLER_CLK_SOURCE_G2D_0, + CLK_RST_CONTROLLER_CLK_SOURCE_G2D_0_G2D_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_G2D_0_G2D_CLK_SRC_SHIFT, + CLK_RST_CONTROLLER_CLK_SOURCE_G2D_0_G2D_CLK_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_G2D_0_G2D_CLK_DIVISOR_SHIFT, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_2D_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_2D_RST_FIELD, + NvRmDiagModuleID_2d + }, + + { /* 3D controller module */ + NvRmModuleID_3D, 0, 0, + { + NvRmClockSource_PllM0, + NvRmClockSource_PllC0, + NvRmClockSource_PllP0, + NvRmClockSource_PllA0 + }, + NvRmClockDivider_Fractional_2, + CLK_RST_CONTROLLER_CLK_SOURCE_G3D_0, + CLK_RST_CONTROLLER_CLK_SOURCE_G3D_0_G3D_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_G3D_0_G3D_CLK_SRC_SHIFT, + CLK_RST_CONTROLLER_CLK_SOURCE_G3D_0_G3D_CLK_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_G3D_0_G3D_CLK_DIVISOR_SHIFT, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_3D_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_3D_RST_FIELD, + NvRmDiagModuleID_3d + }, + + { /* Display 1 controller module */ + NvRmModuleID_Display, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllD0, + NvRmClockSource_PllC0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_None, + CLK_RST_CONTROLLER_CLK_SOURCE_DISP1_0, + CLK_RST_CONTROLLER_CLK_SOURCE_DISP1_0_DISP1_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_DISP1_0_DISP1_CLK_SRC_SHIFT, + 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_DISP1_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_DISP1_RST_FIELD, + NvRmDiagModuleID_Display + }, + + { /* Display 2 controller module */ + NvRmModuleID_Display, 1, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllD0, + NvRmClockSource_PllC0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_None, + CLK_RST_CONTROLLER_CLK_SOURCE_DISP2_0, + CLK_RST_CONTROLLER_CLK_SOURCE_DISP2_0_DISP2_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_DISP2_0_DISP2_CLK_SRC_SHIFT, + 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_DISP2_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_DISP2_RST_FIELD, + NvRmDiagModuleID_Display + }, + + { /* TVO controller module - TVO clock */ + NvRmModuleID_Tvo, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllD0, + NvRmClockSource_PllC0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + CLK_RST_CONTROLLER_CLK_SOURCE_TVO_0, + CLK_RST_CONTROLLER_CLK_SOURCE_TVO_0_TVO_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_TVO_0_TVO_CLK_SRC_SHIFT, + CLK_RST_CONTROLLER_CLK_SOURCE_TVO_0_TVO_CLK_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_TVO_0_TVO_CLK_DIVISOR_SHIFT, + + // Combined TVO, and CVE reset and and clock enable controls + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_TVO_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_TVO_RST_FIELD, + NvRmDiagModuleID_Tvo + }, + { /* TVO controller module - CVE clock + * Module sub clocks must immediately follow main clock + */ + NvRmModuleID_Tvo, 0, 1, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllD0, + NvRmClockSource_PllC0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + CLK_RST_CONTROLLER_CLK_SOURCE_CVE_0, + CLK_RST_CONTROLLER_CLK_SOURCE_CVE_0_CVE_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_CVE_0_CVE_CLK_SRC_SHIFT, + CLK_RST_CONTROLLER_CLK_SOURCE_CVE_0_CVE_CLK_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_CVE_0_CVE_CLK_DIVISOR_SHIFT, + + // Combined TVO, and CVE reset and and clock enable controls + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_TVO_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_TVO_RST_FIELD, + NvRmDiagModuleID_Cve + }, + { /* TVO controller module - TVDAC clock + * Module sub clocks must immediately follow main clock + */ + NvRmModuleID_Tvo, 0, 2, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllD0, + NvRmClockSource_PllC0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + CLK_RST_CONTROLLER_CLK_SOURCE_TVDAC_0, + CLK_RST_CONTROLLER_CLK_SOURCE_TVDAC_0_TVDAC_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_TVDAC_0_TVDAC_CLK_SRC_SHIFT, + CLK_RST_CONTROLLER_CLK_SOURCE_TVDAC_0_TVDAC_CLK_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_TVDAC_0_TVDAC_CLK_DIVISOR_SHIFT, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_TVDAC_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_TVDAC_RST_FIELD, + NvRmDiagModuleID_Tvdac + }, + + { /* HDMI controller module */ + NvRmModuleID_Hdmi, 0, 0, + { + NvRmClockSource_PllP0, + NvRmClockSource_PllD0, + NvRmClockSource_PllC0, + NvRmClockSource_ClkM + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(HDMI, H), + NvRmDiagModuleID_Hdmi + }, + + { /* VDE controller module (VDE and BSEV clocks) + * These clocks does not have source selector/divider registers, + * and should always be enabled/reset in sync. Threfore, no need + * for separate VDE and BSEV subclock descriptors + */ + NvRmModuleID_Vde, 0, 0, + { + NvRmClockSource_Vbus + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + // Combined VDE and BSEV reset and and clock enable controls + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + (CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_VDE_FIELD | + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_BSEV_FIELD), + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + (CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_VDE_RST_FIELD | + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_BSEV_RST_FIELD), + NvRmDiagModuleID_Vde + }, + + { /* BSEA controller module */ + NvRmModuleID_BseA, 0, 0, + { + NvRmClockSource_SystemBus + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_BSEA_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_BSEA_RST_FIELD, + NvRmDiagModuleID_Bsea + }, + + { /* VCP controller module */ + NvRmModuleID_Vcp, 0, 0, + { + NvRmClockSource_SystemBus + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_VCP_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_VCP_RST_FIELD, + NvRmDiagModuleID_Vcp + }, + + { /* Timer controller module */ + NvRmModuleID_Timer, 0, 0, + { + NvRmClockSource_SystemBus + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_TMR_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_TMR_RST_FIELD, + NvRmDiagModuleID_Timer + }, + + { /* System Monitor controller module */ + NvRmModuleID_SysStatMonitor, 0, 0, + { + NvRmClockSource_SystemBus + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_STAT_MON_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_STAT_MON_RST_FIELD, + NvRmDiagModuleID_StatMon + }, + + { /* GPIO controller module */ + NvRmPrivModuleID_Gpio, 0, 0, + { + NvRmClockSource_SystemBus + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_GPIO_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_GPIO_RST_FIELD, + NvRmDiagModuleID_Gpio + }, + + { /* USB controller module */ + NvRmModuleID_Usb2Otg, 0, 0, + { + NvRmClockSource_PllU0 + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_USBD_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_USBD_RST_FIELD, + NvRmDiagModuleID_Usb + }, + + { /* USB controller module */ + NvRmModuleID_Usb2Otg, 1, 0, + { + NvRmClockSource_PllU0 + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_USBD_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_USBD_RST_FIELD, + NvRmDiagModuleID_Usb + }, + + { /* APB DMA controller module */ + NvRmPrivModuleID_ApbDma, 0, 0, + { + NvRmClockSource_Apb + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_APBDMA_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_APBDMA_RST_FIELD, + NvRmDiagModuleID_ApbDma + }, + + { /* AC97 controller module */ + NvRmModuleID_Ac97, 0, 0, + { + NvRmClockSource_Apb + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_AC97_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_AC97_RST_FIELD, + NvRmDiagModuleID_Ac97 + }, + + { /* Keyboard controller module */ + NvRmModuleID_Kbc, 0, 0, + { + NvRmClockSource_Apb + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_KBC_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_KBC_RST_FIELD, + NvRmDiagModuleID_Kbc + }, + + { /* RTC controller module */ + NvRmModuleID_Rtc, 0, 0, + { + NvRmClockSource_Apb + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_RTC_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_RTC_RST_FIELD, + NvRmDiagModuleID_Rtc + }, + + { /* Fuse controller module */ + NvRmModuleID_Fuse, 0, 0, + { + NvRmClockSource_Apb + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_FUSE_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_FUSE_RST_FIELD, + NvRmDiagModuleID_Fuse + }, + + { /* Power Management controller module */ + NvRmModuleID_Pmif, 0, 0, + { + NvRmClockSource_Apb + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_PMC_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_PMC_RST_FIELD, + NvRmDiagModuleID_Pmc + }, + + { /* CPU cache controller module */ + NvRmModuleID_CacheMemCtrl, 0, 0, + { + NvRmClockSource_CpuBus + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_CACHE1_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_CACHE1_RST_FIELD, + NvRmDiagModuleID_Cache + }, + { /* COP (AVP) cache controller module */ + NvRmModuleID_CacheMemCtrl, 1, 0, + { + NvRmClockSource_SystemBus + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_CACHE2_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_CACHE2_RST_FIELD, + NvRmDiagModuleID_Cache + }, + + { /* DSI controller module */ + NvRmModuleID_Dsi, 0, 0, + { + NvRmClockSource_PllD0 + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_DSI_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_DSI_RST_FIELD, + NvRmDiagModuleID_Dsi + }, + + { /* CSI controller module */ + NvRmModuleID_Csi, 0, 0, + { + NvRmClockSource_SystemBus // TODO: find a proper clock source + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_CSI_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_CSI_RST_FIELD, + NvRmDiagModuleID_Csi + }, + + { /* ISP controller module */ + NvRmModuleID_Isp, 0, 0, + { + NvRmClockSource_SystemBus // TODO: find a proper clock source + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_ISP_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_ISP_RST_FIELD, + NvRmDiagModuleID_Isp + }, + + { /* CPU module */ + NvRmModuleID_Cpu, 0, 0, + { + NvRmClockSource_CpuBus + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0_CLK_ENB_CPU_FIELD, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_CPU_RST_FIELD, + NvRmDiagModuleID_Cpu + }, + + { /* COP (AVP) module */ + NvRmModuleID_Avp, 0, 0, + { + NvRmClockSource_SystemBus // TODO: Add COP skipper source? + }, + NvRmClockDivider_None, + 0, 0, 0, 0, 0, + + 0, 0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0, + CLK_RST_CONTROLLER_RST_DEVICES_L_0_SWR_COP_RST_FIELD, + NvRmDiagModuleID_Coprocessor + }, + + { /* Memory controller module */ + NvRmPrivModuleID_MemoryController, 0, 0, + { + NvRmClockSource_ClkM, + NvRmClockSource_PllC0, + NvRmClockSource_ClkS, + NvRmClockSource_PllM0, + NvRmClockSource_PllP0, + NvRmClockSource_PllP4, + NvRmClockSource_PllP3, + NvRmClockSource_ClkD + }, + NvRmClockDivider_Fractional_2, + NV_COMMON_CLK_RST_FIELDS_INFO(MEM, H), + NvRmDiagModuleID_Mc + }, + + { /* External Memory controller module */ + NvRmPrivModuleID_ExternalMemoryController, 0, 0, + { + NvRmClockSource_PllM0, + NvRmClockSource_PllC0, + NvRmClockSource_PllP0, + NvRmClockSource_ClkM, + }, + NvRmClockDivider_Fractional_2, + CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0, + CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0_EMC_2X_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0_EMC_2X_CLK_SRC_SHIFT, + CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0_EMC_2X_CLK_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0_EMC_2X_CLK_DIVISOR_SHIFT, + + // EMC has 1x and 2x domains clock enable bits located in the source + // register. There is also a gloabl clock enable bit in CLK_OUT_ENB_L_0 + // register, which is not described here. All 3 bits are set/cleared + // in Ap15EnableModuleClock() function below. + CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0, + (CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0_EMC_2X_CLK_ENB_FIELD | + CLK_RST_CONTROLLER_CLK_SOURCE_EMC_0_EMC_1X_CLK_ENB_FIELD), + CLK_RST_CONTROLLER_RST_DEVICES_H_0, + CLK_RST_CONTROLLER_RST_DEVICES_H_0_SWR_EMC_RST_FIELD, + NvRmDiagModuleID_Emc + } +}; + +NvU32 const g_Ap15ModuleClockTableSize = NV_ARRAY_SIZE(g_Ap15ModuleClockTable); + +/*****************************************************************************/ +/*****************************************************************************/ +// Clock sources + +static const NvRmFixedClockInfo s_Ap15FixedClockTable[] = +{ + { + NvRmClockSource_ClkS, + NvRmClockSource_Invalid, + 0, 0 + }, + { + NvRmClockSource_ClkM, + NvRmClockSource_Invalid, + 0, 0 + }, + { + NvRmClockSource_ClkD, + NvRmClockSource_ClkM, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0_CLK_M_DOUBLER_ENB_FIELD + }, + + { + NvRmClockSource_ExtSpdf, + NvRmClockSource_Invalid, + 0, 0 + }, + { + NvRmClockSource_ExtI2s1, + NvRmClockSource_Invalid, + 0, 0 + }, + { + NvRmClockSource_ExtI2s2, + NvRmClockSource_Invalid, + 0, 0 + }, + { + NvRmClockSource_ExtAc97, + NvRmClockSource_Invalid, + 0, 0 + }, + { + NvRmClockSource_ExtAudio1, + NvRmClockSource_Invalid, + 0, 0 + }, + { + NvRmClockSource_ExtAudio2, + NvRmClockSource_Invalid, + 0, 0 + }, + { + NvRmClockSource_ExtVi, + NvRmClockSource_Invalid, + 0, 0 + } +}; + +static const NvU32 s_Ap15FixedClockTableSize = NV_ARRAY_SIZE(s_Ap15FixedClockTable); + +/*****************************************************************************/ + +// TODO: Specify PLL ref divider in OSC control reg as PLL C, D, M, P, U source + +/* + * Notation clarification: in h/w documentation PLL base outputs (except PLLA + * output) are denoted as PllX_OUT0, and the seconadry PLL outputs (if any) + * after fractional dividers are denoted as PllX_OUT1, PllX_OUT2, .... However, + * no h/w name is defined for the base PLLA output, and the output of the PLLA + * secondary divider is marked as PllA_OUT0 (not PllA_OUT1). Threfore, we use + * PllA1 (not PllA0) to denote base PLLA clock. + */ +static const NvRmPllClockInfo s_Ap15PllClockTable[] = +{ + { /* PLLA base output */ + NvRmClockSource_PllA1, + NvRmClockSource_PllP1, + NvRmPllType_LP, + CLK_RST_CONTROLLER_PLLA_BASE_0, + CLK_RST_CONTROLLER_PLLA_MISC_0, + 50000, + 1000000 + }, + + { /* PLLC base output */ + NvRmClockSource_PllC0, + NvRmClockSource_ClkM, + NvRmPllType_LP, + CLK_RST_CONTROLLER_PLLC_BASE_0, + CLK_RST_CONTROLLER_PLLC_MISC_0, + 100000, + 1400000 + }, + + { /* PLLM base output */ + NvRmClockSource_PllM0, + NvRmClockSource_ClkM, + NvRmPllType_LP, + CLK_RST_CONTROLLER_PLLM_BASE_0, + CLK_RST_CONTROLLER_PLLM_MISC_0, + 100000, + 1000000 + }, + + { /* PLLP base output */ + NvRmClockSource_PllP0, + NvRmClockSource_ClkM, + NvRmPllType_LP, + CLK_RST_CONTROLLER_PLLP_BASE_0, + CLK_RST_CONTROLLER_PLLP_MISC_0, + 100000, + 1000000 + }, + + { /* PLLD base output */ + NvRmClockSource_PllD0, + NvRmClockSource_ClkM, + NvRmPllType_MIPI, + CLK_RST_CONTROLLER_PLLD_BASE_0, + CLK_RST_CONTROLLER_PLLD_MISC_0, + 100000, + 1000000 + }, + + { /* PLLU base output */ + NvRmClockSource_PllU0, + NvRmClockSource_ClkM, + NvRmPllType_MIPI, + CLK_RST_CONTROLLER_PLLU_BASE_0, + CLK_RST_CONTROLLER_PLLU_MISC_0, + 100000, + 1000000 + } +}; + +static const NvU32 s_Ap15PllClockTableSize = NV_ARRAY_SIZE(s_Ap15PllClockTable); + +/*****************************************************************************/ + +static const NvRmDividerClockInfo s_Ap15DividerClockTable[] = +{ + { /* PLLA0 - PLLA secondary output */ + NvRmClockSource_PllA0, + NvRmClockSource_PllA1, + NvRmClockDivider_Fractional_2, + + CLK_RST_CONTROLLER_PLLA_OUT_0, + CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_RATIO_DEFAULT_MASK, + CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_RATIO_SHIFT, + + CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_CLKEN_FIELD | + CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_RSTN_FIELD, + + ((CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_CLKEN_ENABLE << + CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_RSTN_SHIFT)), + + ((CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_CLKEN_DISABLE << + CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLA_OUT_0_PLLA_OUT0_RSTN_SHIFT)), + + NVRM_VARIABLE_DIVIDER + }, + + { /* PLLC1 - PLLC secondary output */ + NvRmClockSource_PllC1, + NvRmClockSource_PllC0, + NvRmClockDivider_Fractional_2, + + CLK_RST_CONTROLLER_PLLC_OUT_0, + CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_RATIO_DEFAULT_MASK, + CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_RATIO_SHIFT, + + CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_CLKEN_FIELD | + CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_RSTN_FIELD, + + ((CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_CLKEN_ENABLE << + CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_RSTN_SHIFT)), + + ((CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_CLKEN_DISABLE << + CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLC_OUT_0_PLLC_OUT1_RSTN_SHIFT)), + + NVRM_VARIABLE_DIVIDER + }, + + { /* PLLM1 - PLLM secondary ouput */ + NvRmClockSource_PllM1, + NvRmClockSource_PllM0, + NvRmClockDivider_Fractional_2, + + CLK_RST_CONTROLLER_PLLM_OUT_0, + CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_RATIO_DEFAULT_MASK, + CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_RATIO_SHIFT, + + CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_CLKEN_FIELD | + CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_RSTN_FIELD, + + ((CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_CLKEN_ENABLE << + CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_RSTN_SHIFT)), + + ((CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_CLKEN_DISABLE << + CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLM_OUT_0_PLLM_OUT1_RSTN_SHIFT)), + + NVRM_VARIABLE_DIVIDER + }, + + { /* PLLP1 - PLLP secondary output (overridden) */ + NvRmClockSource_PllP1, + NvRmClockSource_PllP0, + NvRmClockDivider_Fractional_2, + + CLK_RST_CONTROLLER_PLLP_OUTA_0, + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_RATIO_DEFAULT_MASK, + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_RATIO_SHIFT, + + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_OVRRIDE_FIELD | + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_CLKEN_FIELD | + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_RSTN_FIELD, + + ((CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_OVRRIDE_ENABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_OVRRIDE_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_CLKEN_ENABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_RSTN_SHIFT)), + + ((CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_OVRRIDE_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_OVRRIDE_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_CLKEN_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT1_RSTN_SHIFT)), + + NVRM_VARIABLE_DIVIDER + }, + + { /* PLLP2 - PLLP secondary output (overridden) */ + NvRmClockSource_PllP2, + NvRmClockSource_PllP0, + NvRmClockDivider_Fractional_2, + + CLK_RST_CONTROLLER_PLLP_OUTA_0, + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_RATIO_DEFAULT_MASK, + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_RATIO_SHIFT, + + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_OVRRIDE_FIELD | + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_CLKEN_FIELD | + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_RSTN_FIELD, + + ((CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_OVRRIDE_ENABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_OVRRIDE_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_CLKEN_ENABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_RSTN_SHIFT)), + + ((CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_OVRRIDE_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_OVRRIDE_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_CLKEN_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTA_0_PLLP_OUT2_RSTN_SHIFT)), + + NVRM_VARIABLE_DIVIDER + }, + + { /* PLLP3 - PLLP secondary output (overridden) */ + NvRmClockSource_PllP3, + NvRmClockSource_PllP0, + NvRmClockDivider_Fractional_2, + + CLK_RST_CONTROLLER_PLLP_OUTB_0, + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_RATIO_DEFAULT_MASK, + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_RATIO_SHIFT, + + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_OVRRIDE_FIELD | + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_CLKEN_FIELD | + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_RSTN_FIELD, + + ((CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_OVRRIDE_ENABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_OVRRIDE_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_CLKEN_ENABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_RSTN_SHIFT)), + + ((CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_OVRRIDE_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_OVRRIDE_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_CLKEN_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT3_RSTN_SHIFT)), + + NVRM_VARIABLE_DIVIDER + }, + + { /* PLLP4 - PLLP secondary output (overridden) */ + NvRmClockSource_PllP4, + NvRmClockSource_PllP0, + NvRmClockDivider_Fractional_2, + + CLK_RST_CONTROLLER_PLLP_OUTB_0, + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_RATIO_DEFAULT_MASK, + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_RATIO_SHIFT, + + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_OVRRIDE_FIELD | + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_CLKEN_FIELD | + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_RSTN_FIELD, + + ((CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_OVRRIDE_ENABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_OVRRIDE_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_CLKEN_ENABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_RSTN_SHIFT)), + + ((CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_OVRRIDE_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_OVRRIDE_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_CLKEN_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_CLKEN_SHIFT) | + (CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_RSTN_RESET_DISABLE << + CLK_RST_CONTROLLER_PLLP_OUTB_0_PLLP_OUT4_RSTN_SHIFT)), + + NVRM_VARIABLE_DIVIDER + }, + + { /* AHB bus clock divider */ + NvRmClockSource_Ahb, + NvRmClockSource_SystemBus, + NvRmClockDivider_Integer_1, + + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0, + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_AHB_RATE_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_AHB_RATE_SHIFT, + + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_HCLK_DIS_FIELD, + (0x0 << CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_HCLK_DIS_SHIFT), + (0x1 << CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_HCLK_DIS_SHIFT), + NVRM_VARIABLE_DIVIDER + }, + + { /* APB bus clock divider */ + NvRmClockSource_Apb, + NvRmClockSource_Ahb, + NvRmClockDivider_Integer_1, + + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0, + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_APB_RATE_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_APB_RATE_SHIFT, + + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_PCLK_DIS_FIELD, + (0x0 << CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_PCLK_DIS_SHIFT), + (0x1 << CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_PCLK_DIS_SHIFT), + NVRM_VARIABLE_DIVIDER + }, + + { /* V-pipe clock divider */ + NvRmClockSource_Vbus, + NvRmClockSource_SystemBus, + NvRmClockDivider_Keeper16, + + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0, + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_VCLK_RATE_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_VCLK_RATE_SHIFT, + 0, 0, 0, + NVRM_VARIABLE_DIVIDER + }, + + // TODO: PLL ref divider +}; + +static const NvU32 s_Ap15DividerClockTableSize = NV_ARRAY_SIZE(s_Ap15DividerClockTable); + +/*****************************************************************************/ + +static const NvRmCoreClockInfo s_Ap15CoreClockTable[] = +{ + { + NvRmClockSource_CpuBus, + { + NvRmClockSource_ClkM, + NvRmClockSource_PllC0, + NvRmClockSource_ClkS, + NvRmClockSource_PllM0, + NvRmClockSource_PllP0, + NvRmClockSource_PllP4, + NvRmClockSource_PllP3, + NvRmClockSource_ClkD + }, + + CLK_RST_CONTROLLER_CCLK_BURST_POLICY_0, + CLK_RST_CONTROLLER_CCLK_BURST_POLICY_0_CPU_STATE_DEFAULT_MASK, + CLK_RST_CONTROLLER_CCLK_BURST_POLICY_0_CPU_STATE_SHIFT, + { + 0, + CLK_RST_CONTROLLER_CCLK_BURST_POLICY_0_CWAKEUP_IDLE_SOURCE_DEFAULT_MASK, + CLK_RST_CONTROLLER_CCLK_BURST_POLICY_0_CWAKEUP_RUN_SOURCE_DEFAULT_MASK, + CLK_RST_CONTROLLER_CCLK_BURST_POLICY_0_CWAKEUP_IRQ_SOURCE_DEFAULT_MASK, + CLK_RST_CONTROLLER_CCLK_BURST_POLICY_0_CWAKEUP_FIQ_SOURCE_DEFAULT_MASK + + }, + { + 0, + CLK_RST_CONTROLLER_CCLK_BURST_POLICY_0_CWAKEUP_IDLE_SOURCE_SHIFT, + CLK_RST_CONTROLLER_CCLK_BURST_POLICY_0_CWAKEUP_RUN_SOURCE_SHIFT, + CLK_RST_CONTROLLER_CCLK_BURST_POLICY_0_CWAKEUP_IRQ_SOURCE_SHIFT, + CLK_RST_CONTROLLER_CCLK_BURST_POLICY_0_CWAKEUP_FIQ_SOURCE_SHIFT + }, + + CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER_0, + CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER_0_SUPER_CDIV_ENB_DEFAULT_MASK, + CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER_0_SUPER_CDIV_ENB_SHIFT, + CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER_0_SUPER_CDIV_DIVIDEND_DEFAULT_MASK, + CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER_0_SUPER_CDIV_DIVIDEND_SHIFT, + NV_FIELD_SIZE(CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER_0_SUPER_CDIV_DIVIDEND_RANGE), + CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER_0_SUPER_CDIV_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER_0_SUPER_CDIV_DIVISOR_SHIFT, + NV_FIELD_SIZE(CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER_0_SUPER_CDIV_DIVISOR_RANGE) + }, + { + NvRmClockSource_SystemBus, + { + NvRmClockSource_ClkM, + NvRmClockSource_PllC1, + NvRmClockSource_PllP4, + NvRmClockSource_PllP3, + NvRmClockSource_PllP2, + NvRmClockSource_ClkD, + NvRmClockSource_ClkS, + NvRmClockSource_PllM1, + }, + + CLK_RST_CONTROLLER_SCLK_BURST_POLICY_0, + CLK_RST_CONTROLLER_SCLK_BURST_POLICY_0_SYS_STATE_DEFAULT_MASK, + CLK_RST_CONTROLLER_SCLK_BURST_POLICY_0_SYS_STATE_SHIFT, + { + 0, + CLK_RST_CONTROLLER_SCLK_BURST_POLICY_0_SWAKEUP_IDLE_SOURCE_DEFAULT_MASK, + CLK_RST_CONTROLLER_SCLK_BURST_POLICY_0_SWAKEUP_RUN_SOURCE_DEFAULT_MASK, + CLK_RST_CONTROLLER_SCLK_BURST_POLICY_0_SWAKEUP_IRQ_SOURCE_DEFAULT_MASK, + CLK_RST_CONTROLLER_SCLK_BURST_POLICY_0_SWAKEUP_FIQ_SOURCE_DEFAULT_MASK + + }, + { + 0, + CLK_RST_CONTROLLER_SCLK_BURST_POLICY_0_SWAKEUP_IDLE_SOURCE_SHIFT, + CLK_RST_CONTROLLER_SCLK_BURST_POLICY_0_SWAKEUP_RUN_SOURCE_SHIFT, + CLK_RST_CONTROLLER_SCLK_BURST_POLICY_0_SWAKEUP_IRQ_SOURCE_SHIFT, + CLK_RST_CONTROLLER_SCLK_BURST_POLICY_0_SWAKEUP_FIQ_SOURCE_SHIFT + }, + + CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER_0, + CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER_0_SUPER_SDIV_ENB_DEFAULT_MASK, + CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER_0_SUPER_SDIV_ENB_SHIFT, + CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER_0_SUPER_SDIV_DIVIDEND_DEFAULT_MASK, + CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER_0_SUPER_SDIV_DIVIDEND_SHIFT, + NV_FIELD_SIZE(CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER_0_SUPER_SDIV_DIVIDEND_RANGE), + CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER_0_SUPER_SDIV_DIVISOR_DEFAULT_MASK, + CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER_0_SUPER_SDIV_DIVISOR_SHIFT, + NV_FIELD_SIZE(CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER_0_SUPER_SDIV_DIVISOR_RANGE) + } +}; + +static const NvU32 s_Ap15CoreClockTableSize = NV_ARRAY_SIZE(s_Ap15CoreClockTable); + +/*****************************************************************************/ + +static const NvRmSelectorClockInfo s_Ap15SelectorClockTable[] = +{ + { + NvRmClockSource_AudioSync, + { + NvRmClockSource_ExtSpdf, + NvRmClockSource_ExtI2s1, + NvRmClockSource_ExtI2s2, + NvRmClockSource_ExtAc97, + NvRmClockSource_PllA0, + NvRmClockSource_ExtAudio2, + NvRmClockSource_ExtAudio1, + NvRmClockSource_ExtVi + }, + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0, + + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_SYNC_CLK_RATE_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_0_SYNC_CLK_RATE_SHIFT, + + CLK_RST_CONTROLLER_MISC_CLK_ENB_0, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0_SYNC_CLK_DOUBLER_ENB_FIELD + }, + { + NvRmClockSource_MpeAudio, + { + NvRmClockSource_ExtSpdf, + NvRmClockSource_ExtI2s1, + NvRmClockSource_ExtI2s2, + NvRmClockSource_ExtAc97, + NvRmClockSource_PllA0, + NvRmClockSource_ExtAudio2, + NvRmClockSource_ExtAudio1, + NvRmClockSource_ExtVi + }, + CLK_RST_CONTROLLER_CLK_SOURCE_MPE_AUDIO_0, + + CLK_RST_CONTROLLER_CLK_SOURCE_MPE_AUDIO_0_MPE_AUDIO_CLK_SRC_DEFAULT_MASK, + CLK_RST_CONTROLLER_CLK_SOURCE_MPE_AUDIO_0_MPE_AUDIO_CLK_SRC_SHIFT, + 0, 0 + } +}; + +static const NvU32 s_Ap15SelectorClockTableSize = NV_ARRAY_SIZE(s_Ap15SelectorClockTable); + +/*****************************************************************************/ +/*****************************************************************************/ + +static NvRmClockSourceInfo s_Ap15ClockSourceTable[NvRmClockSource_Num] = {{0}}; + +NvRmClockSourceInfo* NvRmPrivAp15ClockSourceTableInit(void) +{ + NvRmClockSourceInfoPtr Src; + +#define PARSE_SOURCE_TABLE(type) \ +do\ +{\ + Src.p##type = (NvRm##type##ClockInfo*)s_Ap15##type##ClockTable;\ + NvRmPrivParseClockSources( \ + s_Ap15ClockSourceTable, NvRmClockSource_Num, \ + Src, s_Ap15##type##ClockTableSize, NvRmClockSourceType_##type); \ +} while(0) + + NvOsMemset(s_Ap15ClockSourceTable, 0, sizeof(s_Ap15ClockSourceTable)); + + PARSE_SOURCE_TABLE(Fixed); + PARSE_SOURCE_TABLE(Pll); + PARSE_SOURCE_TABLE(Divider); + PARSE_SOURCE_TABLE(Core); + PARSE_SOURCE_TABLE(Selector); + +#undef PARSE_SOURCE_TABLE + + return &s_Ap15ClockSourceTable[0]; +} + +/*****************************************************************************/ + +static NvBool s_Ap15PllM0Clocks[NV_ARRAY_SIZE(g_Ap15ModuleClockTable)] = {0}; +static NvBool s_Ap15PllC0Clocks[NV_ARRAY_SIZE(g_Ap15ModuleClockTable)] = {0}; +static NvBool s_Ap15PllP0Clocks[NV_ARRAY_SIZE(g_Ap15ModuleClockTable)] = {0}; +static NvBool s_Ap15PllA0Clocks[NV_ARRAY_SIZE(g_Ap15ModuleClockTable)] = {0}; +static NvBool s_Ap15PllD0Clocks[NV_ARRAY_SIZE(g_Ap15ModuleClockTable)] = {0}; + +static NvRmPllReference s_Ap15PllReferencesTable[] = +{ + { NvRmClockSource_PllM0, NvRmDfsStatusFlags_StopPllM0, 0, s_Ap15PllM0Clocks, 0 }, + { NvRmClockSource_PllC0, NvRmDfsStatusFlags_StopPllC0, 0, s_Ap15PllC0Clocks, 0 }, + { NvRmClockSource_PllP0, NvRmDfsStatusFlags_StopPllP0, 0, s_Ap15PllP0Clocks, 0 }, + { NvRmClockSource_PllA0, NvRmDfsStatusFlags_StopPllA0, 0, s_Ap15PllA0Clocks, 0 }, + { NvRmClockSource_PllD0, NvRmDfsStatusFlags_StopPllD0, 0, s_Ap15PllD0Clocks, 0 }, +}; +static const NvU32 s_Ap15PllReferencesTableSize = + NV_ARRAY_SIZE(s_Ap15PllReferencesTable); + +void +NvRmPrivAp15PllReferenceTableInit( + NvRmPllReference** pPllReferencesTable, + NvU32* pPllReferencesTableSize) +{ + NvU32 i; + for (i = 0; i < s_Ap15PllReferencesTableSize; i++) + { + NvOsMemset(s_Ap15PllReferencesTable[i].AttachedModules, 0, + sizeof(NvBool) * g_Ap15ModuleClockTableSize); + s_Ap15PllReferencesTable[i].ReferenceCnt = 0; + s_Ap15PllReferencesTable[i].ExternalClockRefCnt = 0; + } + *pPllReferencesTable = s_Ap15PllReferencesTable; + *pPllReferencesTableSize = s_Ap15PllReferencesTableSize; +} + +/*****************************************************************************/ + +// Power Gating Ids for each Power Group specified in re-location table header +static const NvU32 s_Ap15PowerGroupIds[] = { NV_POWERGROUP_ENUM_TABLE }; + +void +NvRmPrivAp15PowerGroupTableInit( + const NvU32** pPowerGroupIdsTable, + NvU32* pPowerGroupIdsTableSize) +{ + *pPowerGroupIdsTable = s_Ap15PowerGroupIds; + *pPowerGroupIdsTableSize = NV_ARRAY_SIZE(s_Ap15PowerGroupIds); +} + +/*****************************************************************************/ + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_fuse.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_fuse.c new file mode 100644 index 000000000000..0e0a8b21adab --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_fuse.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * @brief NVIDIA Driver Development Kit: Fuse API + * + * @b Description: Contains the NvRM Chip unique id implementation. + */ +#include "nvassert.h" +#include "nvrm_drf.h" +#include "nvos.h" +#include "nvrm_module.h" +#include "nvrm_hardware_access.h" +#include "nvrm_hwintf.h" +#include "ap15/arclk_rst.h" +#include "ap15/arfuse.h" +#include "ap15/ap15rm_private.h" +#include "ap15rm_clocks.h" + +NvError NvRmPrivAp15ChipUniqueId(NvRmDeviceHandle hDevHandle,void* pId) +{ + NvU32 OldRegData; // Old register contents + NvU32 NewRegData; // New register contents + NvU64 Temp; // Temp buffer to read the contents of fuses + NV_ASSERT(hDevHandle); + NV_ASSERT(pId); + +#if NV_USE_FUSE_CLOCK_ENABLE + // Enable fuse clock + Ap15EnableModuleClock(hDevHandle, NvRmModuleID_Fuse, NV_TRUE); +#endif + + // Access to unique id is protected, so make sure all registers visible + // first. + OldRegData = NV_REGR(hDevHandle, + NvRmPrivModuleID_ClockAndReset, + 0, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0); + NewRegData = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, + MISC_CLK_ENB, + CFG_ALL_VISIBLE, + 1, + OldRegData); + NV_REGW(hDevHandle, + NvRmPrivModuleID_ClockAndReset, + 0, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0, + NewRegData); + + // Read the secure id from the fuse registers in to a local buffer + Temp = ((NvU64)NV_REGR(hDevHandle, + (NvRmPrivModuleID)NvRmModuleID_Fuse, + 0, + FUSE_JTAG_SECUREID_0_0)) | + (((NvU64)NV_REGR(hDevHandle, + (NvRmPrivModuleID)NvRmModuleID_Fuse, + 0, + FUSE_JTAG_SECUREID_1_0)) << 32); + // Copy the read data to output buffer + NvOsMemcpy(pId,&Temp,sizeof(NvU64)); + + // Restore the protected registers enable to the way we found it. + NV_REGW(hDevHandle, + NvRmPrivModuleID_ClockAndReset, + 0, + CLK_RST_CONTROLLER_MISC_CLK_ENB_0, + OldRegData); + +#if NV_USE_FUSE_CLOCK_ENABLE + // Disable fuse clock + Ap15EnableModuleClock(hDevHandle, NvRmModuleID_Fuse, NV_FALSE); +#endif + + return NvError_Success; +} diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_hwmap.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_hwmap.c new file mode 100644 index 000000000000..050abdfd7147 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_hwmap.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvos.h" +#include "nvassert.h" +#include "nvrm_chiplib.h" + +NvError NvRmPhysicalMemMap( + NvRmPhysAddr phys, + size_t size, + NvU32 flags, + NvOsMemAttribute memType, + void **ptr ) +{ + return NvOsPhysicalMemMap(phys, size, memType, flags, ptr); +} + +void NvRmPhysicalMemUnmap(void *ptr, size_t size) +{ + NvOsPhysicalMemUnmap(ptr, size); +} diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c new file mode 100644 index 000000000000..85b6dddd6add --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c @@ -0,0 +1,682 @@ +/* + * Copyright (c) 2007-2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvos.h" +#include "nvutil.h" +#include "nvassert.h" +#include "nvrm_drf.h" +#include "nvrm_init.h" +#include "nvrm_rmctrace.h" +#include "nvrm_configuration.h" +#include "nvrm_chiplib.h" +#include "nvrm_pmu_private.h" +#include "nvrm_processor.h" +#include "nvrm_xpc.h" +#include "ap15rm_private.h" +#include "nvrm_structure.h" +#include "ap15rm_private.h" +#include "ap15rm_clocks.h" +#include "nvodm_query.h" +#include "nvodm_query_pins.h" +#include "common/nvrm_hwintf.h" +#include "ap15/armc.h" +#include "ap15/aremc.h" +#include "ap15/project_relocation_table.h" +#include "ap15/arapb_misc.h" +#include "ap15/arapbpm.h" +#include "nvrm_pinmux_utils.h" +#include "ap15/arfuse.h" +#include "nvbootargs.h" + +static NvRmDevice gs_Rm; + +extern NvRmCfgMap g_CfgMap[]; + +void NvRmPrivMemoryInfo( NvRmDeviceHandle hDevice ); +extern NvError NvRmPrivMapApertures( NvRmDeviceHandle rm ); +extern void NvRmPrivUnmapApertures( NvRmDeviceHandle rm ); +extern NvError NvRmPrivPwmInit(NvRmDeviceHandle hRm); +extern void NvRmPrivPwmDeInit(NvRmDeviceHandle hRm); +extern NvU32 NvRmPrivGetBctCustomerOption(NvRmDeviceHandle hRm); +extern void NvRmPrivReadChipId( NvRmDeviceHandle rm ); +extern NvU32 *NvRmPrivGetRelocationTable( NvRmDeviceHandle hDevice ); +static void NvRmPrivInitPinAttributes(NvRmDeviceHandle rm); +static void NvRmPrivBasicReset( NvRmDeviceHandle rm ); +static NvError NvRmPrivMcErrorMonitorStart( NvRmDeviceHandle rm ); +static void NvRmPrivMcErrorMonitorStop( NvRmDeviceHandle rm ); + +#if !NV_OAL +/* This function sets some performance timings for Mc & Emc. Numbers are from + * the Arch team. + */ +static void +NvRmPrivSetupMc(NvRmDeviceHandle hRm) +{ + switch (hRm->ChipId.Id) { + case 0x15: + case 0x16: + NvRmPrivAp15SetupMc(hRm); + break; + case 0x20: + NvRmPrivAp20SetupMc(hRm); + break; + default: + NV_ASSERT(!"Unsupported chip ID"); + break; + } +} +#endif + +NvError +NvRmOpen(NvRmDeviceHandle *pHandle, NvU32 DeviceId ) { + return NvRmOpenNew(pHandle); +} + +void NvRmInit( + NvRmDeviceHandle * pHandle ) +{ + NvU32 *table = 0; + NvRmDevice *rm = 0; + rm = &gs_Rm; + + if( rm->bPreInit ) + { + return; + } + + /* Read the chip Id and store in the Rm structure. */ + NvRmPrivReadChipId( rm ); + + /* parse the relocation table */ + table = NvRmPrivGetRelocationTable( rm ); + NV_ASSERT(table != NULL); + + NV_ASSERT_SUCCESS(NvRmPrivModuleInit( &rm->ModuleTable, table )); + + NvRmPrivMemoryInfo( rm ); + + NvRmPrivInterruptTableInit( rm ); + + rm->bPreInit = NV_TRUE; + *pHandle = rm; + + return; +} + +NvError +NvRmOpenNew(NvRmDeviceHandle *pHandle) +{ + NvError err; + NvRmDevice *rm = 0; + NvU32 *table = 0; + + NvU32 BctCustomerOption = 0; + NvU64 Uid = 0; + + NvOsMutexHandle rmMutex = NULL; + + /* open the nvos trace file */ + NVOS_TRACE_LOG_START; + + // OAL does not support these mutexes + if (gs_Rm.mutex == NULL) + { + err = NvOsMutexCreate(&rmMutex); + if (err != NvSuccess) + return err; + + if (NvOsAtomicCompareExchange32((NvS32*)&gs_Rm.mutex, 0, + (NvS32)rmMutex) != 0) + NvOsMutexDestroy(rmMutex); + } + + NvOsMutexLock(gs_Rm.mutex); + rm = &gs_Rm; + + if(rm->refcount ) + { + rm->refcount++; + *pHandle = rm; + NvOsMutexUnlock(gs_Rm.mutex); + return NvSuccess; + } + + rmMutex = gs_Rm.mutex; + gs_Rm.mutex = rmMutex; + + // create the memmgr mutex + err = NvOsMutexCreate(&rm->MemMgrMutex); + if (err) + goto fail; + + // create mutex for the clock and reset r-m-w top level registers access + err = NvOsMutexCreate(&rm->CarMutex); + if (err) + goto fail; + + /* NvRmOpen needs to be re-entrant to allow I2C, GPIO and KeyList ODM + * services to be available to the ODM query. Therefore, the refcount is + * bumped extremely early in initialization, and if any initialization + * fails the refcount is reset to 0. + */ + rm->refcount = 1; + + if( !rm->bBasicInit ) + { + /* get the default configuration */ + err = NvRmPrivGetDefaultCfg( g_CfgMap, &rm->cfg ); + if( err != NvSuccess ) + { + goto fail; + } + + /* get the requested configuration */ + err = NvRmPrivReadCfgVars( g_CfgMap, &rm->cfg ); + if( err != NvSuccess ) + { + goto fail; + } + } + + /* start chiplib */ + if (rm->cfg.Chiplib[0] != '\0') + { + err = NvRmPrivChiplibStartup( rm->cfg.Chiplib, rm->cfg.ChiplibArgs, + NULL ); + if( err != NvSuccess ) + { + goto fail; + } + } + + /* open the RMC file */ + err = NvRmRmcOpen( rm->cfg.RMCTraceFileName, &rm->rmc ); + if( err != NvSuccess ) + { + goto fail; + } + + if( !rm->bPreInit ) + { + /* Read the chip Id and store in the Rm structure. */ + NvRmPrivReadChipId( rm ); + + /* parse the relocation table */ + table = NvRmPrivGetRelocationTable( rm ); + if( !table ) + { + goto fail; + } + + err = NvRmPrivModuleInit( &rm->ModuleTable, table ); + if( err != NvSuccess ) + { + goto fail; + } + NvRmPrivMemoryInfo( rm ); + + // Now populate the logical interrupt table. + NvRmPrivInterruptTableInit( rm ); + } + + if( !rm->bBasicInit && !NVOS_IS_WINDOWS_X86 ) + { + err = NvRmPrivMapApertures( rm ); + if( err != NvSuccess ) + { + goto fail; + } + + // Initializing the ODM-defined key list + // This gets initialized first, since the RMs calls into + // the ODM query may result in the ODM query calling + // back into the RM to get this value! + BctCustomerOption = NvRmPrivGetBctCustomerOption(rm); + err = NvRmPrivInitKeyList(rm, &BctCustomerOption, 1); + if (err != NvSuccess) + { + goto fail; + } + } + + // prevent re-inits + rm->bBasicInit = NV_TRUE; + rm->bPreInit = NV_TRUE; + + + if (!NVOS_IS_WINDOWS_X86) + { + NvRmPrivCheckBondOut( rm ); + + /* bring modules out of reset */ + NvRmPrivBasicReset( rm ); + + /* initialize power manager before any other module that may access + * clock or voltage resources + */ + err = NvRmPrivPowerInit(rm); + if( err != NvSuccess ) + { + goto fail; + } + + NvRmPrivInterruptStart( rm ); + + // Initializing pins attributes + NvRmPrivInitPinAttributes(rm); + + // Initialize RM pin-mux (init's the state of internal shadow + // register variables) + NvRmInitPinMux(rm, NV_TRUE); + + // Initalize the module clocks. + err = NvRmPrivClocksInit( rm ); + if( err != NvSuccess ) + { + goto fail; + } + } + + if (!NVOS_IS_WINDOWS_X86) + { + // FIXME: this crashes in simulation + // Enabling only for the non simulation modes. + if ((rm->ChipId.Major == 0) && (rm->ChipId.Netlist == 0)) + { + // this is the csim case, so we don't do this here. + } + else + { + // Initializing the dma. + err = NvRmPrivDmaInit(rm); + if( err != NvSuccess ) + { + goto fail; + } + + // Initializing the Spi and Slink. + err = NvRmPrivSpiSlinkInit(rm); + if( err != NvSuccess ) + { + goto fail; + } + + // Complete pin mux initialization + NvRmInitPinMux(rm, NV_FALSE); + + // Initializing the dfs + err = NvRmPrivDfsInit(rm); + if( err != NvSuccess ) + { + goto fail; + } + } + + // Initializing the Pwm + err = NvRmPrivPwmInit(rm); + if (err != NvSuccess) + { + goto fail; + } + + // PMU interface init utilizes ODM services that reenter NvRmOpen(). + // Therefore, it shall be performed after refcount is set so that + // reentry has no side-effects except bumping refcount. The latter + // is reset below so that RM can be eventually closed. + err = NvRmPrivPmuInit(rm); + if( err != NvSuccess ) + { + goto fail; + } + + // set the mc & emc tuning parameters + NvRmPrivSetupMc(rm); + if (!NvRmIsSimulation()) + { + // Configure PLL rails, boost core power and clocks + // Initialize and start temperature monitoring + NvRmPrivPllRailsInit(rm); + NvRmPrivBoostClocks(rm); + NvRmPrivDttInit(rm); + } + + // Asynchronous interrupts must be disabled until the very end of + // RmOpen. They can be enabled just before releasing rm mutex after + // completion of all initialization calls. + NvRmPrivPmuInterruptEnable(rm); + + // Start Memory Controller Error monitoring. + err = NvRmPrivMcErrorMonitorStart(rm); + if( err != NvSuccess ) + { + goto fail; + } + + // WAR for bug 600821 + if ((rm->ChipId.Id == 0x20) && + (rm->ChipId.Major == 0x1) && (rm->ChipId.Minor == 0x2)) + { + err = NvRmQueryChipUniqueId(rm, sizeof (NvU64), &Uid); + if ((Uid>>32) == 0x08080105) + { + NV_REGW(rm, NvRmModuleID_Pmif, 0, 0xD0, 0xFFFFFFEF); + } + } + } + err = NvRmXpcInitArbSemaSystem(rm); + if( err != NvSuccess ) + { + goto fail; + } + + /* assign the handle pointer */ + *pHandle = rm; + + NvOsMutexUnlock(gs_Rm.mutex); + return NvSuccess; + +fail: + // FIXME: free rm if it becomes dynamically allocated + // BUG: there are about ten places that we go to fail, and we make no + // effort here to clean anything up. + NvOsMutexUnlock(gs_Rm.mutex); + NV_DEBUG_PRINTF(("RM init failed\n")); + rm->refcount = 0; + return err; +} + +void +NvRmClose(NvRmDeviceHandle handle) +{ + if( !handle ) + { + return; + } + + NV_ASSERT( handle->mutex ); + + /* decrement refcount */ + NvOsMutexLock( handle->mutex ); + handle->refcount--; + + /* do deinit if refcount is zero */ + if( handle->refcount == 0 ) + { + if (!NVOS_IS_WINDOWS_X86) + { + // PMU and DTT deinit through ODM services reenters NvRmClose(). + // The refcount will wrap around and this will be the only reentry + // side-effect, which is compensated after deint exit. + NvRmPrivDttDeinit(); + handle->refcount = 0; + NvRmPrivPmuDeinit(handle); + handle->refcount = 0; + + } + + if (!NVOS_IS_WINDOWS_X86) + { + /* disable modules */ + // Enabling only for the non simulation modes. + if ((handle->ChipId.Major == 0) && (handle->ChipId.Netlist == 0)) + { + // this is the csim case, so we don't do this here. + } + else + { + NvRmPrivDmaDeInit(); + + NvRmPrivSpiSlinkDeInit(); + + NvRmPrivDfsDeinit(handle); + } + + /* deinit clock manager */ + NvRmPrivClocksDeinit(handle); + + /* deinit power manager */ + NvRmPrivPowerDeinit(handle); + + NvRmPrivDeInitKeyList(handle); + NvRmPrivPwmDeInit(handle); + // Stop Memory controller error monitoring. + NvRmPrivMcErrorMonitorStop(handle); + + /* if anyone left an interrupt registered, this will clear it. */ + NvRmPrivInterruptShutdown(handle); + + /* unmap the apertures */ + NvRmPrivUnmapApertures( handle ); + + if (NvRmIsSimulation()) + NvRmPrivChiplibShutdown(); + + } + + NvRmRmcClose( &handle->rmc ); + + /* deallocate the instance table */ + NvRmPrivModuleDeinit( &handle->ModuleTable ); + + /* free up the CAR mutex */ + NvOsMutexDestroy(handle->CarMutex); + + /* free up the memmgr mutex */ + NvOsMutexDestroy(handle->MemMgrMutex); + + /* close the nvos trace file */ + NVOS_TRACE_LOG_END; + } + NvOsMutexUnlock( handle->mutex ); + +#if NVOS_IS_WINDOWS && !NVOS_IS_WINDOWS_CE + if( handle->refcount == 0 ) + { + NvOsMutexDestroy(handle->mutex); + gs_Rm.mutex = 0; + } +#endif +} + +void +NvRmPrivMemoryInfo( NvRmDeviceHandle hDevice ) +{ + NvRmModuleTable *tbl; + NvRmModuleInstance *inst; + + tbl = &hDevice->ModuleTable; + + /* Get External memory module info */ + inst = tbl->ModInst + + (tbl->Modules)[NvRmPrivModuleID_ExternalMemory].Index; + + hDevice->ExtMemoryInfo.base = inst->PhysAddr; + hDevice->ExtMemoryInfo.size = inst->Length; + + /* Get Iram Memory Module Info .Special handling since iram has 4 banks + * and each has a different instance in the relocation table + */ + + inst = tbl->ModInst + (tbl->Modules)[NvRmPrivModuleID_Iram].Index; + hDevice->IramMemoryInfo.base = inst->PhysAddr; + hDevice->IramMemoryInfo.size = inst->Length; + + inst++; + // Below loop works assuming that relocation table parsing compacted + // scattered multiple instances into sequential list + while(NvRmPrivDevToModuleID(inst->DeviceId) == NvRmPrivModuleID_Iram) + { + // The IRAM banks are contigous address of memory. Cannot handle + // non-contigous memory for now + NV_ASSERT(hDevice->IramMemoryInfo.base + + hDevice->IramMemoryInfo.size == inst->PhysAddr); + + hDevice->IramMemoryInfo.size += inst->Length; + inst++; + } + +} + +NvError +NvRmGetRmcFile( NvRmDeviceHandle hDevice, NvRmRmcFile **file ) +{ + NV_ASSERT(hDevice); + + *file = &hDevice->rmc; + return NvSuccess; +} + +NvRmDeviceHandle NvRmPrivGetRmDeviceHandle() +{ + return &gs_Rm; +} + +/** + * Initializes pins attributes + * @param hRm The RM device handle + */ +static void +NvRmPrivInitPinAttributes(NvRmDeviceHandle rm) +{ + NvU32 Count = 0, Offset = 0, Value = 0; + NvU32 Major = 0; + NvU32 Minor = 0; + NvOdmPinAttrib *pPinAttribTable = NULL; + NvRmModuleCapability caps[4]; + NvRmModuleCapability *pCap = NULL; + + NV_ASSERT( rm ); + + NvOsMemset(caps, 0, sizeof(caps)); + + caps[0].MajorVersion = 1; + caps[0].MinorVersion = 0; + caps[0].EcoLevel = 0; + caps[0].Capability = &caps[0]; + + caps[1].MajorVersion = 1; + caps[1].MinorVersion = 1; + caps[1].EcoLevel = 0; + + caps[2].MajorVersion = 1; + caps[2].MinorVersion = 2; + caps[2].EcoLevel = 0; + + // the pin attributes for v 1.0 and v1.1 of the misc module + // are fully compatible, so the version comparison is made against 1.0 + // Treating 1.2 same as 1.0/1.1. + caps[1].Capability = &caps[0]; + caps[2].Capability = &caps[0]; + + /* AP20 misc module pin attributes, set differently than AP15 as the pin + * attribute registers in misc module changed */ + caps[3].MajorVersion = 2; + caps[3].MinorVersion = 0; + caps[3].EcoLevel = 0; + caps[3].Capability = &caps[3]; + + NV_ASSERT_SUCCESS(NvRmModuleGetCapabilities( + rm, + NvRmModuleID_Misc, + caps, + sizeof(caps)/sizeof(caps[0]), + (void**)&pCap)); + + Count = NvOdmQueryPinAttributes((const NvOdmPinAttrib **)&pPinAttribTable); + + for ( ; Count ; Count--, pPinAttribTable++) + { + Major = (pPinAttribTable->ConfigRegister >> 28); + Minor = (pPinAttribTable->ConfigRegister >> 24) & 0xF; + if ((Major == pCap->MajorVersion) && (Minor == pCap->MinorVersion)) + { + Offset = pPinAttribTable->ConfigRegister & 0xFFFF; + Value = pPinAttribTable->Value; + NV_REGW(rm, NvRmModuleID_Misc, 0, Offset, Value); + } + } +} + + +static void NvRmPrivBasicReset( NvRmDeviceHandle rm ) +{ + switch (rm->ChipId.Id) { + case 0x15: + case 0x16: + NvRmPrivAp15BasicReset(rm); + return; + case 0x20: + NvRmPrivAp20BasicReset(rm); + return; + default: + NV_ASSERT(!"Unsupported chip ID"); + return; + } +} + +NvError NvRmPrivMcErrorMonitorStart( NvRmDeviceHandle rm ) +{ + NvError e = NvError_NotSupported; + + switch (rm->ChipId.Id) { + case 0x15: + case 0x16: + e = NvRmPrivAp15McErrorMonitorStart(rm); + break; + case 0x20: + e = NvRmPrivAp20McErrorMonitorStart(rm); + break; + default: + NV_ASSERT(!"Unsupported chip ID"); + break; + } + return e; +} + +void NvRmPrivMcErrorMonitorStop( NvRmDeviceHandle rm ) +{ + switch (rm->ChipId.Id) { + case 0x15: + case 0x16: + NvRmPrivAp15McErrorMonitorStop(rm); + break; + case 0x20: + NvRmPrivAp20McErrorMonitorStop(rm); + break; + default: + NV_ASSERT(!"Unsupported chip ID"); + break; + } +} + + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init_common.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init_common.c new file mode 100644 index 000000000000..fe3496ed65cd --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init_common.c @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvos.h" +#include "nvutil.h" +#include "nvassert.h" +#include "nvrm_drf.h" +#include "nvrm_init.h" +#include "nvrm_rmctrace.h" +#include "nvrm_configuration.h" +#include "nvrm_chiplib.h" +#include "nvrm_pmu_private.h" +#include "nvrm_processor.h" +#include "nvrm_structure.h" +#include "ap15rm_private.h" +#include "ap15rm_private.h" +#include "ap15rm_clocks.h" +#include "nvodm_query.h" +#include "nvodm_query_pins.h" +#include "common/nvrm_hwintf.h" +#include "nvrm_pinmux_utils.h" +#include "nvrm_minikernel.h" +#include "ap15/arapb_misc.h" // chipid, has to be the same for all chips +#include "ap15/arapbpm.h" +#include "ap15/arfuse.h" + +extern NvRmCfgMap g_CfgMap[]; + +void NvRmPrivMemoryInfo( NvRmDeviceHandle hDevice ); +void NvRmPrivReadChipId( NvRmDeviceHandle rm ); +void NvRmPrivGetSku( NvRmDeviceHandle rm ); +/** Returns the pointer to the relocation table */ +NvU32 *NvRmPrivGetRelocationTable( NvRmDeviceHandle hDevice ); +NvError NvRmPrivMapApertures( NvRmDeviceHandle rm ); +void NvRmPrivUnmapApertures( NvRmDeviceHandle rm ); +NvU32 NvRmPrivGetBctCustomerOption(NvRmDeviceHandle hRm); + +NvRmCfgMap g_CfgMap[] = +{ + { "NV_CFG_RMC_FILE", NvRmCfgType_String, (void *)"", + STRUCT_OFFSET(RmConfigurationVariables, RMCTraceFileName) }, + + /* don't need chiplib for non-sim builds */ + { "NV_CFG_CHIPLIB", NvRmCfgType_String, (void *)"", + STRUCT_OFFSET(RmConfigurationVariables, Chiplib) }, + + { "NV_CFG_CHIPLIB_ARGS", NvRmCfgType_String, (void *)"", + STRUCT_OFFSET(RmConfigurationVariables, ChiplibArgs) }, + + { 0 } +}; + +NvRmModuleTable * +NvRmPrivGetModuleTable( + NvRmDeviceHandle hDevice ) +{ + return &hDevice->ModuleTable; +} + +NvU32 * +NvRmPrivGetRelocationTable( NvRmDeviceHandle hDevice ) +{ + switch( hDevice->ChipId.Id ) { + case 0x15: + return NvRmPrivAp15GetRelocationTable( hDevice ); + case 0x16: + return NvRmPrivAp16GetRelocationTable( hDevice ); + case 0x20: + return NvRmPrivAp20GetRelocationTable( hDevice ); + default: + NV_ASSERT(!"Invalid Chip" ); + return 0; + } +} + +void +NvRmPrivReadChipId( NvRmDeviceHandle rm ) +{ +#if (NVCPU_IS_X86 && NVOS_IS_WINDOWS) + NvRmChipId *id; + NV_ASSERT( rm ); + + id = &rm->ChipId; + + id->Family = NvRmChipFamily_HandheldSoc; + id->Id = 0x15; + id->Major = 0x0; + id->Minor = 0x0; + id->SKU = 0x0; + id->Netlist = 0x0; + id->Patch = 0x0; +#else + NvU32 reg; + NvRmChipId *id; + NvU32 fam; + char *s; + NvU8 *VirtAddr; + NvError e; + + NV_ASSERT( rm ); + id = &rm->ChipId; + + /* Hard coding the address of the chip ID address space, as we haven't yet + * parsed the relocation table. + */ + e = NvRmPhysicalMemMap(0x70000000, 0x1000, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, (void **)&VirtAddr); + if (e != NvSuccess) + { + NV_DEBUG_PRINTF(("APB misc aperture map failure\n")); + return; + } + + /* chip id is in the misc aperture */ + reg = NV_READ32( VirtAddr + APB_MISC_GP_HIDREV_0 ); + id->Id = (NvU16)NV_DRF_VAL( APB_MISC_GP, HIDREV, CHIPID, reg ); + id->Major = (NvU8)NV_DRF_VAL( APB_MISC_GP, HIDREV, MAJORREV, reg ); + id->Minor = (NvU8)NV_DRF_VAL( APB_MISC_GP, HIDREV, MINORREV, reg ); + + fam = NV_DRF_VAL( APB_MISC_GP, HIDREV, HIDFAM, reg ); + switch( fam ) { + case APB_MISC_GP_HIDREV_0_HIDFAM_GPU: + id->Family = NvRmChipFamily_Gpu; + s = "GPU"; + break; + case APB_MISC_GP_HIDREV_0_HIDFAM_HANDHELD: + id->Family = NvRmChipFamily_Handheld; + s = "Handheld"; + break; + case APB_MISC_GP_HIDREV_0_HIDFAM_BR_CHIPS: + id->Family = NvRmChipFamily_BrChips; + s = "BrChips"; + break; + case APB_MISC_GP_HIDREV_0_HIDFAM_CRUSH: + id->Family = NvRmChipFamily_Crush; + s = "Crush"; + break; + case APB_MISC_GP_HIDREV_0_HIDFAM_MCP: + id->Family = NvRmChipFamily_Mcp; + s = "MCP"; + break; + case APB_MISC_GP_HIDREV_0_HIDFAM_CK: + id->Family = NvRmChipFamily_Ck; + s = "Ck"; + break; + case APB_MISC_GP_HIDREV_0_HIDFAM_VAIO: + id->Family = NvRmChipFamily_Vaio; + s = "Vaio"; + break; + case APB_MISC_GP_HIDREV_0_HIDFAM_HANDHELD_SOC: + id->Family = NvRmChipFamily_HandheldSoc; + s = "Handheld SOC"; + break; + default: + NV_ASSERT( !"bad chip family" ); + NvRmPhysicalMemUnmap(VirtAddr, 0x1000); + return; + } + + reg = NV_READ32( VirtAddr + APB_MISC_GP_EMU_REVID_0 ); + id->Netlist = (NvU16)NV_DRF_VAL( APB_MISC_GP, EMU_REVID, NETLIST, reg ); + id->Patch = (NvU16)NV_DRF_VAL( APB_MISC_GP, EMU_REVID, PATCH, reg ); + + if( id->Major == 0 ) + { + char *emu; + if( id->Netlist == 0 ) + { + NvOsDebugPrintf( "Simulation Chip: 0x%x\n", id->Id ); + } + else + { + if( id->Minor == 0 ) + { + emu = "QuickTurn"; + } + else + { + emu = "FPGA"; + } + + NvOsDebugPrintf( "Emulation (%s) Chip: 0x%x Netlist: 0x%x " + "Patch: 0x%x\n", emu, id->Id, id->Netlist, id->Patch ); + } + } + else + { + // on real silicon + + NvRmPrivGetSku( rm ); + + NvOsDebugPrintf( "Chip Id: 0x%x (%s) Major: 0x%x Minor: 0x%x " + "SKU: 0x%x\n", id->Id, s, id->Major, id->Minor, id->SKU ); + } + + // add a sanity check here, so that if we think we are on sim, but don't + // detect a sim/quickturn netlist bail out with an error + if ( NvRmIsSimulation() && id->Major != 0 ) + { + // this should all get optimized away in release builds because the + // above will get evaluated to if ( 0 ) + NV_ASSERT(!"invalid major version number for simulation"); + } + NvRmPhysicalMemUnmap(VirtAddr, 0x1000); +#endif +} + +void +NvRmPrivGetSku( NvRmDeviceHandle rm ) +{ + NvError e; + NvRmChipId *id; + NvU8 *FuseVirt; + NvU32 reg; + +#if NV_USE_FUSE_CLOCK_ENABLE + NvU8 *CarVirt = 0; +#endif + + NV_ASSERT( rm ); + id = &rm->ChipId; + +#if NV_USE_FUSE_CLOCK_ENABLE + // Enable fuse clock + e = NvRmPhysicalMemMap(0x60006000, 0x1000, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, (void **)&CarVirt); + if (e == NvSuccess) + { + reg = NV_READ32(CarVirt + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0); + reg |= 0x80; + NV_WRITE32(CarVirt + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, reg); + } +#endif + + /* Read the fuse only on real silicon, as it was not gauranteed to be + * preset on the eluation/simulation platforms. + */ + e = NvRmPhysicalMemMap(0x7000f800, 0x400, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, (void **)&FuseVirt); + if (e == NvSuccess) + { + // Read the SKU from the fuse module. + reg = NV_READ32( FuseVirt + FUSE_SKU_INFO_0 ); + id->SKU = (NvU16)reg; + NvRmPhysicalMemUnmap(FuseVirt, 0x400); + +#if NV_USE_FUSE_CLOCK_ENABLE + // Disable fuse clock + if (CarVirt) + { + reg = NV_READ32(CarVirt + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0); + reg &= ~0x80; + NV_WRITE32(CarVirt + CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0, reg); + NvRmPhysicalMemUnmap(CarVirt, 0x1000); + } +#endif + } else + { + NV_ASSERT(!"Cannot map the FUSE aperture to get the SKU"); + id->SKU = 0; + } +} + +NvError +NvRmPrivMapApertures( NvRmDeviceHandle rm ) +{ + NvRmModuleTable *tbl; + NvRmModuleInstance *inst; + NvRmModule *mod; + NvU32 devid; + NvU32 i; + NvError e; + + NV_ASSERT( rm ); + + /* loop over the instance list and map everything */ + tbl = &rm->ModuleTable; + mod = tbl->Modules; + for( i = 0; i < NvRmPrivModuleID_Num; i++ ) + { + if( mod[i].Index == NVRM_MODULE_INVALID ) + { + continue; + } + + if ((i != NvRmPrivModuleID_Ahb_Arb_Ctrl ) && + (i != NvRmPrivModuleID_ApbDma ) && + (i != NvRmPrivModuleID_ApbDmaChannel ) && + (i != NvRmPrivModuleID_ClockAndReset ) && + (i != NvRmPrivModuleID_ExternalMemoryController ) && + (i != NvRmPrivModuleID_Gpio ) && + (i != NvRmPrivModuleID_Interrupt ) && + (i != NvRmPrivModuleID_InterruptArbGnt ) && + (i != NvRmPrivModuleID_InterruptDrq ) && + (i != NvRmPrivModuleID_MemoryController ) && + (i != NvRmModuleID_Misc) && + (i != NvRmPrivModuleID_ArmPerif) && + (i != NvRmModuleID_3D) && + (i != NvRmModuleID_CacheMemCtrl ) && + (i != NvRmModuleID_Display) && + (i != NvRmModuleID_Dvc) && + (i != NvRmModuleID_FlowCtrl ) && + (i != NvRmModuleID_Fuse ) && + (i != NvRmModuleID_GraphicsHost ) && + (i != NvRmModuleID_I2c) && + (i != NvRmModuleID_Isp) && + (i != NvRmModuleID_Mpe) && + (i != NvRmModuleID_Pmif ) && + (i != NvRmModuleID_Mipi ) && + (i != NvRmModuleID_ResourceSema ) && + (i != NvRmModuleID_SysStatMonitor ) && + (i != NvRmModuleID_TimerUs ) && + (i != NvRmModuleID_Vde ) && + (i != NvRmModuleID_ExceptionVector ) && + (i != NvRmModuleID_Usb2Otg ) && + (i != NvRmModuleID_Vi) + ) + { + continue; + } + + /* FIXME If the multiple instances of the same module is adjacent to + * each other then we can do one allocation for all those modules. + */ + + /* map all of the device instances */ + inst = tbl->ModInst + mod[i].Index; + devid = inst->DeviceId; + while( devid == inst->DeviceId ) + { + /* If this is a device that actually has an aperture... */ + if (inst->PhysAddr) + { + e = NvRmPhysicalMemMap( + inst->PhysAddr, inst->Length, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, &inst->VirtAddr); + if (e != NvSuccess) + { + NV_DEBUG_PRINTF(("Device %d at physical addr 0x%X has no " + "virtual mapping\n", devid, inst->PhysAddr)); + return e; + } + } + + inst++; + } + } + + return NvSuccess; +} + +void +NvRmPrivUnmapApertures( NvRmDeviceHandle rm ) +{ + NvRmModuleTable *tbl; + NvRmModuleInstance *inst; + NvRmModule *mod; + NvU32 devid; + NvU32 i; + + NV_ASSERT( rm ); + + /* loop over the instance list and unmap everything */ + tbl = &rm->ModuleTable; + mod = tbl->Modules; + for( i = 0; i < NvRmPrivModuleID_Num; i++ ) + { + if( mod[i].Index == NVRM_MODULE_INVALID ) + { + continue; + } + + /* map all of the device instances */ + inst = tbl->ModInst + mod[i].Index; + devid = inst->DeviceId; + while( devid == inst->DeviceId ) + { + NvRmPhysicalMemUnmap( inst->VirtAddr, inst->Length ); + inst++; + } + } +} + +NvU32 +NvRmPrivGetBctCustomerOption(NvRmDeviceHandle hRm) +{ + if (!NvRmIsSimulation()) + { + return NV_REGR(hRm, NvRmModuleID_Pmif, 0, APBDEV_PMC_SCRATCH20_0); + } + else + { + return 0; + } +} + +NvRmChipId * +NvRmPrivGetChipId( + NvRmDeviceHandle hDevice ) +{ + return &hDevice->ChipId; +} + +#if !NV_OAL +void NvRmBasicInit(NvRmDeviceHandle * pHandle) +{ + NvRmDevice *rm = 0; + NvError err; + NvU32 *table = 0; + NvU32 BctCustomerOption = 0; + + *pHandle = 0; + rm = NvRmPrivGetRmDeviceHandle(); + + if( rm->bBasicInit ) + { + *pHandle = rm; + return; + } + + /* get the default configuration */ + err = NvRmPrivGetDefaultCfg( g_CfgMap, &rm->cfg ); + if( err != NvSuccess ) + { + goto fail; + } + + /* get the requested configuration */ + err = NvRmPrivReadCfgVars( g_CfgMap, &rm->cfg ); + if( err != NvSuccess ) + { + goto fail; + } + + /* Read the chip Id and store in the Rm structure. */ + NvRmPrivReadChipId( rm ); + + // init the module control (relocation table, resets, etc.) + table = NvRmPrivGetRelocationTable( rm ); + if( !table ) + { + goto fail; + } + + err = NvRmPrivModuleInit( &rm->ModuleTable, table ); + if( err != NvSuccess ) + { + goto fail; + } + + NvRmPrivMemoryInfo( rm ); + + // setup the hw apertures + err = NvRmPrivMapApertures( rm ); + if( err != NvSuccess ) + { + goto fail; + } + + BctCustomerOption = NvRmPrivGetBctCustomerOption(rm); + err = NvRmPrivInitKeyList(rm, &BctCustomerOption, 1); + if (err != NvSuccess) + { + goto fail; + } + + // Now populate the logical interrupt table. + NvRmPrivInterruptTableInit( rm ); + + rm->bBasicInit = NV_TRUE; + // basic init is a super-set of preinit + rm->bPreInit = NV_TRUE; + *pHandle = rm; + +fail: + return; +} + +void +NvRmBasicClose(NvRmDeviceHandle handle) +{ + if (!NVOS_IS_WINDOWS_X86) + { + NvRmPrivDeInitKeyList(handle); + /* unmap the apertures */ + NvRmPrivUnmapApertures( handle ); + /* deallocate the instance table */ + NvRmPrivModuleDeinit( &handle->ModuleTable ); + } +} +#endif diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_interrupt.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_interrupt.c new file mode 100644 index 000000000000..db6c1beb2d66 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_interrupt.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvos.h" +#include "nvrm_module.h" +#include "nvrm_interrupt.h" +#include "nvrm_processor.h" +#include "nvassert.h" +#include "nvrm_relocation_table.h" +#include "nvrm_chiplib.h" +#include "nvrm_hwintf.h" +#include "nvrm_drf.h" +#include "nvrm_structure.h" +#include "ap15rm_private.h" +#include "ap15/arictlr.h" + +#define NVRM_ENABLE_PRINTF 0 // Module debug: 0=disable, 1=enable + +#if (NV_DEBUG && NVRM_ENABLE_PRINTF) +#define NVRM_INTERRUPT_PRINTF(x) NvOsDebugPrintf x +#else +#define NVRM_INTERRUPT_PRINTF(x) +#endif + +//----------------------------------------------------------------------------- +// Register access macros +//----------------------------------------------------------------------------- + +#define NV_INTR_REGR(rm,inst,reg) NV_REGR(rm, NvRmPrivModuleID_Interrupt, inst, ICTLR_##reg##_0) +#define NV_INTR_REGW(rm,inst,reg,data) NV_REGW(rm, NvRmPrivModuleID_Interrupt, inst, ICTLR_##reg##_0, data) + +#define NV_REGA(rm, aperture, instance, offset) \ + ((volatile void*)((NvUPtr)(((rm)->ModuleTable.ModInst + (rm)->ModuleTable.Modules[(aperture)].Index + (instance))->VirtAddr) + (offset))) + +#define NV_INTR_REG_READ(pIntr, reg) NV_READ32((((NvUPtr)(pIntr)) + ICTLR_##reg##_0)) + +NvRmIntrDecoder gs_Ap15PrimaryDecoder = + /* AP15 Primary interrupt controller */ + {NvRmPrivModuleID_Interrupt, + NVRM_IRQS_PER_INTR_CTLR, 0, {0}, {0}, {0} }; + +NvRmIntrDecoder gs_Ap20PrimaryDecoder = + /* AP20 Primary interrupt controller */ + {NvRmPrivModuleID_ArmPerif, + NVRM_IRQS_PER_INTR_CTLR * 5, 0, {0}, {0}, {0} }; + + +NvRmIntrDecoder *gs_PrimaryDecoder = &gs_Ap15PrimaryDecoder; + +NvRmIntrDecoder gs_SubDecoder[] = +{ + /* Secondary interrupt controllers */ + + /* Secondary interrupt controller for APB DMA */ + {NvRmPrivModuleID_ApbDma, + NVRM_MAX_DMA_CHANNELS, 0, {0}, {0}, {0}}, + + /* GPIO secondary interrupt controller */ + {NvRmPrivModuleID_Gpio, + NVRM_IRQS_PER_GPIO_CTLR, 0, {0}, {0}, {0}}, +}; + + + +static NvU16 +NvRmPrivSubControllerInit( + NvRmDeviceHandle hRmDevice, + NvRmIntrDecoderHandle pDecoder, + NvU16 Irq) +{ + NvRmModuleInstance *inst; // Pointer to the module instance + NvU8 num; // Number of instances/loop index + NvU32 devid; // Hardware device id + NvError e; + + NV_ASSERT( hRmDevice ); + + NV_CHECK_ERROR_CLEANUP( NvRmPrivGetModuleInstance( hRmDevice, + pDecoder->ModuleID, &inst) ); + NV_ASSERT(inst != NULL); + + num = 0; + devid = inst->DeviceId; + /* Get all the instances of that sub-controller */ + while ( devid == inst->DeviceId ) + { + NV_ASSERT( inst->IrqMap != NULL ); + NV_ASSERT( num < NVRM_MAX_INSTANCES); + + /* For modules which are sub-interrupt controllers, IRQ value in the + * IrqMap[0] represents the IRQ of the main interrupt controller. Sub + * IRQs for that controller can be computed from IndexMax and IndexBase + * members */ + inst->IrqMap->IndexMax = pDecoder->SubIrqCount; + inst->IrqMap->IndexBase = Irq; + + pDecoder->MainIrq[num] = inst->IrqMap->Irq[0]; + pDecoder->SubIrqFirst[num] = Irq; + pDecoder->SubIrqLast[num] = Irq + pDecoder->SubIrqCount - 1; + + Irq += pDecoder->SubIrqCount; + inst++; + num++; + } + pDecoder->NumberOfInstances = num; + + return Irq; +fail: + NV_ASSERT(!"Invalid ModuleID or Instance in ap15rm_interrupt"); + return 0; +} + +static +NvU16 NvRmPrivMainControllerInit(NvRmDeviceHandle hRmDevice, + NvRmIntrDecoder *pDecoder) +{ + NvRmModuleInstance *inst; // Pointer to the module instance + NvU32 num = 0; + NvU16 irq = 0; // Primary controller will start with IRQ 0. + NvU16 devid; + NvError e; + + NV_CHECK_ERROR_CLEANUP( + NvRmPrivGetModuleInstance( hRmDevice, pDecoder->ModuleID, &inst) + ); + + NV_ASSERT(inst != NULL); + devid = inst->DeviceId; + + while( devid == inst->DeviceId ) + { + pDecoder->SubIrqFirst[num] = irq; + pDecoder->SubIrqLast[num] = irq + pDecoder->SubIrqCount - 1; + pDecoder->MainIrq[num] = NVRM_IRQ_INVALID; + + irq += pDecoder->SubIrqCount; + num++; + inst++; + } + + pDecoder->NumberOfInstances = num; + return irq; +fail: + NV_ASSERT(!"Invalid ModuleID or Instance in ap15rm_interrupt"); + return 0; +} + +void NvRmPrivInterruptTableInit( NvRmDeviceHandle hRmDevice ) +{ + NvU16 irq; + NvU32 subDecoder; + + NV_ASSERT( hRmDevice ); + + NVRM_CAP_CLEAR(hRmDevice, NvRmCaps_HasFalconInterruptController); + NVRM_CAP_CLEAR(hRmDevice, NvRmCaps_Has128bitInterruptSerializer); + + // WARNING: the falcon interrupt controller is not in simulation! + if( NvRmIsSimulation() == NV_FALSE && hRmDevice->ChipId.Id >= 0x20) + { + NVRM_CAP_SET(hRmDevice, NvRmCaps_HasFalconInterruptController); + + if (hRmDevice->ChipId.Major == 0 + && hRmDevice->ChipId.Netlist != 0 + && hRmDevice->ChipId.Minor != 0 ) + { + /* PALAU has 128-bit interrupt serializer and needs some WARs to + * compensate for the delays in interrupt arrival at interrupt + * controller */ + NVRM_CAP_SET(hRmDevice, NvRmCaps_Has128bitInterruptSerializer); + } + } + + if (!NVRM_IS_CAP_SET(hRmDevice, NvRmCaps_HasFalconInterruptController)) + { + gs_PrimaryDecoder = &gs_Ap15PrimaryDecoder; + } + else + { + gs_PrimaryDecoder = &gs_Ap20PrimaryDecoder; + } + + irq = NvRmPrivMainControllerInit(hRmDevice, gs_PrimaryDecoder); + + subDecoder = NV_ARRAY_SIZE(gs_SubDecoder); + while (subDecoder) + { + subDecoder --; + irq = NvRmPrivSubControllerInit(hRmDevice, + &(gs_SubDecoder[subDecoder]), irq); + } + + hRmDevice->MaxIrqs = irq; + NVRM_INTERRUPT_PRINTF(("MAX IRQs: %d\n", irq)); +} + +NvU32 NvRmGetIrqForLogicalInterrupt( + NvRmDeviceHandle hRmDevice, + NvRmModuleID ModuleID, + NvU32 Index) +{ + NvRmModuleInstance* inst = NULL; // Pointer to module instance + NvRmModuleIrqMap* pIrqMap; // Pointer to module IRQ map + NvU16 irq = 0; + NvError e; + NV_ASSERT( hRmDevice ); + + + NV_CHECK_ERROR_CLEANUP( NvRmPrivGetModuleInstance( hRmDevice, + ModuleID, &inst) ); + if ( inst == NULL || inst->IrqMap == NULL) + { + // NV_ASSERT(!"Illegal call\n"); + // Is this legal? Some clients like NVBL + // is calling this API blindly as they don't know if this module + // supports interrupt or not. I don't know if this good or bad. Why + // would a clinet request IRQ, if they know the underying module + // doesn'tsupport interrupts. + return NVRM_IRQ_INVALID; + } + + pIrqMap = inst->IrqMap; + + /* Check if this the interrupt for this module is routed to secondary + * interrupt controller or to the main controller */ + /* FIXME rename IndexMax and IndexBase variables to SubInterruptCount and + * SubInterruptBase */ + if (pIrqMap->IndexMax == 0) + { + NV_ASSERT (Index < pIrqMap->IrqCount); + NV_ASSERT(pIrqMap->Irq[Index] != NVRM_IRQ_INVALID); + + irq = pIrqMap->Irq[Index]; + } + /* Secondary interrupt controller */ + else + { + // Requesting controller's main interrupt? This is a hack used by the + // OAL to get the main IRQ line for the sub-deocders. OAL builds a list + // of all the main IRQs for the sub-decoders and asserts if someone + // tries to register an interrupt handler for the main IRQ line. + if (Index == 0xFF) + { + NV_ASSERT (pIrqMap->Irq[0] != NVRM_IRQ_INVALID); + irq = pIrqMap->Irq[0]; + } else + { + /* Index cannot be more than the Max IRQs registered by that + * secondary interrupt controller */ + NV_ASSERT( Index < pIrqMap->IndexMax ); + irq = pIrqMap->IndexBase + Index; + } + } + return irq; +fail: + NV_ASSERT(!"Invalid ModuleID or Instance in ap15rm_interrupt"); + return 0; +} + +NvU32 NvRmGetIrqCountForLogicalInterrupt( + NvRmDeviceHandle hRmDevice, + NvRmModuleID ModuleID) +{ + NvRmModuleInstance *inst = NULL; + NvError e; + + NV_ASSERT( hRmDevice ); + + NV_CHECK_ERROR_CLEANUP( NvRmPrivGetModuleInstance( hRmDevice, + ModuleID, &inst) ); + if ( inst == NULL || inst->IrqMap == NULL) + { + // NV_ASSERT(!"Illegal call\n"); + // Is this legal? Some clients like NVBL are calling this API blindly + // as they don't know if this module supports interrupt or not. + // I don't know if this good or bad. Why would a clinet request IRQ, + // if they know the underying module doesn't support interrupts. + return 0; + } + + return inst->IrqMap->IrqCount; +fail: + NV_ASSERT(!"Invalid ModuleID or Instance in ap15rm_interrupt"); + return 0; +} diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_interrupt_generic.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_interrupt_generic.c new file mode 100644 index 000000000000..e3d6bead5a9f --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_interrupt_generic.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvos.h" +#include "ap15rm_private.h" +#include "nvrm_interrupt.h" + +NvError NvRmInterruptRegister( + NvRmDeviceHandle hRmDevice, + NvU32 IrqListSize, + const NvU32 *pIrqList, + const NvOsInterruptHandler *pIrqHandlerList, + void *context, + NvOsInterruptHandle *handle, + NvBool InterruptEnable) +{ + NvError err; + + err = NvOsInterruptRegister(IrqListSize, + pIrqList, + pIrqHandlerList, + context, + handle, + InterruptEnable); + + return err; +} + +void NvRmInterruptUnregister( + NvRmDeviceHandle hRmDevice, + NvOsInterruptHandle handle) +{ + NvOsInterruptUnregister( handle ); +} + +NvError NvRmInterruptEnable( + NvRmDeviceHandle hRmDevice, + NvOsInterruptHandle handle) +{ + return NvOsInterruptEnable(handle); +} + +void NvRmInterruptDone( NvOsInterruptHandle handle ) +{ + NvOsInterruptDone( handle ); +} + +void NvRmPrivInterruptStart(NvRmDeviceHandle hRmDevice) +{ + return; +} + +void NvRmPrivInterruptShutdown(NvRmDeviceHandle handle) +{ + return; +} + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_memctrl.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_memctrl.c new file mode 100644 index 000000000000..e5da471f42a8 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_memctrl.c @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvrm_init.h" +#include "nvassert.h" +#include "nvos.h" +#include "nvrm_module.h" +#include "ap15/aremc.h" +#include "ap15/armc.h" +#include "ap15/arapb_misc.h" +#include "ap15rm_private.h" +#include "nvrm_drf.h" +#include "nvrm_hwintf.h" +#include "nvrm_memctrl.h" +#include "nvrm_clocks.h" +#include "nvrm_structure.h" +#include "nvrm_arm_cp.h" +#include "nvrm_processor.h" + +//define obs_struct +typedef struct ObsInfoRec +{ + NvRmModuleID modSelect; + NvU32 partSelect; +} ObsInfo; + +#define OBS_INFO_FIELD(modID, partition) \ + { \ + NvRmModuleID_##modID, \ + APB_MISC_GP_OBSCTRL_0_OBS_PART_SEL_##partition \ + } + +// static table correspond to enum NvRmModuleID in \include\nvrm_module.idl +// Expand this table to add more moduleID - partition map entries. +static const ObsInfo ObsInfoTable[] = +{ + OBS_INFO_FIELD(Cpu, CPU), + OBS_INFO_FIELD(Display, DIS), + OBS_INFO_FIELD(Csi, DIS), + OBS_INFO_FIELD(Hdmi, DIS), + OBS_INFO_FIELD(Tvo, DIS), + OBS_INFO_FIELD(Dsi, DIS), + OBS_INFO_FIELD(2D, GR), + OBS_INFO_FIELD(Fuse, GR), + OBS_INFO_FIELD(Vde, VDE), + OBS_INFO_FIELD(Isp, VE) +}; + +static const NvU32 ObsInfoTableSize = + NV_ARRAY_SIZE(ObsInfoTable); + + +static void +McStatAp1x_Start( + NvRmDeviceHandle rm, + NvU32 client_id_0, + NvU32 client_id_1, + NvU32 llc_client_id) +{ + NvU32 emc_ctrl = + (AREMC_STAT_CONTROL_MODE_BANDWIDTH << AREMC_STAT_CONTROL_MODE_SHIFT) | + (AREMC_STAT_CONTROL_EVENT_QUALIFIED << AREMC_STAT_CONTROL_EVENT_SHIFT) | + (AREMC_STAT_CONTROL_CLIENT_TYPE_CMCR << + AREMC_STAT_CONTROL_CLIENT_TYPE_SHIFT) | // default is CMC Read client + (AREMC_STAT_CONTROL_FILTER_CLIENT_ENABLE << + AREMC_STAT_CONTROL_FILTER_CLIENT_SHIFT) | + (AREMC_STAT_CONTROL_FILTER_ADDR_DISABLE << + AREMC_STAT_CONTROL_FILTER_ADDR_SHIFT); + + NvU32 mc_filter_client_0 = (ARMC_STAT_CONTROL_FILTER_CLIENT_ENABLE << + ARMC_STAT_CONTROL_FILTER_CLIENT_SHIFT); + + NvU32 mc_filter_client_1 = (ARMC_STAT_CONTROL_FILTER_CLIENT_ENABLE << + ARMC_STAT_CONTROL_FILTER_CLIENT_SHIFT); + + if (client_id_0 == 0xffffffff) + { + mc_filter_client_0 = (ARMC_STAT_CONTROL_FILTER_CLIENT_DISABLE << + ARMC_STAT_CONTROL_FILTER_CLIENT_SHIFT); + client_id_0 = 0; + } + + if (client_id_1 == 0xffffffff) + { + mc_filter_client_1 = (ARMC_STAT_CONTROL_FILTER_CLIENT_DISABLE << + ARMC_STAT_CONTROL_FILTER_CLIENT_SHIFT); + client_id_1 = 0; + } + + if(llc_client_id == 1) + emc_ctrl |= AREMC_STAT_CONTROL_CLIENT_TYPE_MPCORER << + AREMC_STAT_CONTROL_CLIENT_TYPE_SHIFT; + // overwrite with MPCore read + NV_REGW(rm, NvRmPrivModuleID_ExternalMemoryController, + 0, EMC_STAT_CONTROL_0, + NV_DRF_DEF(EMC, STAT_CONTROL, LLMC_GATHER,DISABLE)); + NV_REGW(rm, NvRmPrivModuleID_ExternalMemoryController, + 0, EMC_STAT_LLMC_CLOCK_LIMIT_0, 0xffffffff); + NV_REGW(rm, NvRmPrivModuleID_ExternalMemoryController, + 0, EMC_STAT_LLMC_CONTROL_0_0, emc_ctrl); + NV_REGW(rm, NvRmPrivModuleID_ExternalMemoryController, + 0, EMC_STAT_CONTROL_0, + NV_DRF_DEF(EMC, STAT_CONTROL, LLMC_GATHER, CLEAR)); + NV_REGW(rm, NvRmPrivModuleID_ExternalMemoryController, + 0, EMC_STAT_CONTROL_0, + NV_DRF_DEF(EMC, STAT_CONTROL, LLMC_GATHER, ENABLE)); + NV_REGW(rm, NvRmPrivModuleID_MemoryController, + 0, MC_STAT_CONTROL_0, + NV_DRF_DEF(MC, STAT_CONTROL, EMC_GATHER, DISABLE)); + NV_REGW(rm, NvRmPrivModuleID_MemoryController, + 0, MC_STAT_EMC_CLOCK_LIMIT_0, 0xffffffff); + NV_REGW(rm, NvRmPrivModuleID_MemoryController, + 0, MC_STAT_EMC_CONTROL_0_0, + (ARMC_STAT_CONTROL_MODE_BANDWIDTH << + ARMC_STAT_CONTROL_MODE_SHIFT) | + (client_id_0 << ARMC_STAT_CONTROL_CLIENT_ID_SHIFT) | + (ARMC_STAT_CONTROL_EVENT_QUALIFIED << + ARMC_STAT_CONTROL_EVENT_SHIFT) | + mc_filter_client_0 | + (ARMC_STAT_CONTROL_FILTER_ADDR_DISABLE << + ARMC_STAT_CONTROL_FILTER_ADDR_SHIFT) | + (ARMC_STAT_CONTROL_FILTER_PRI_DISABLE << + ARMC_STAT_CONTROL_FILTER_PRI_SHIFT) | + (ARMC_STAT_CONTROL_FILTER_COALESCED_DISABLE << + ARMC_STAT_CONTROL_FILTER_COALESCED_SHIFT)); + NV_REGW(rm, NvRmPrivModuleID_MemoryController, + 0, MC_STAT_EMC_CONTROL_1_0, + (ARMC_STAT_CONTROL_MODE_BANDWIDTH << + ARMC_STAT_CONTROL_MODE_SHIFT) | + (client_id_1 << ARMC_STAT_CONTROL_CLIENT_ID_SHIFT) | + (ARMC_STAT_CONTROL_EVENT_QUALIFIED << + ARMC_STAT_CONTROL_EVENT_SHIFT) | + mc_filter_client_1 | + (ARMC_STAT_CONTROL_FILTER_ADDR_DISABLE << + ARMC_STAT_CONTROL_FILTER_ADDR_SHIFT) | + (ARMC_STAT_CONTROL_FILTER_PRI_DISABLE << + ARMC_STAT_CONTROL_FILTER_PRI_SHIFT) | + (ARMC_STAT_CONTROL_FILTER_COALESCED_DISABLE << + ARMC_STAT_CONTROL_FILTER_COALESCED_SHIFT)); + + NV_REGW(rm, NvRmPrivModuleID_MemoryController, + 0, MC_STAT_CONTROL_0, + NV_DRF_DEF(MC, STAT_CONTROL, EMC_GATHER, CLEAR)); + NV_REGW(rm, NvRmPrivModuleID_MemoryController, + 0, MC_STAT_CONTROL_0, + NV_DRF_DEF(MC, STAT_CONTROL, EMC_GATHER, ENABLE)); +} + +void +McStat_Start( + NvRmDeviceHandle rm, + NvU32 client_id_0, + NvU32 client_id_1, + NvU32 llc_client_id) +{ + switch (rm->ChipId.Id) + { + case 0x15: + case 0x16: + McStatAp1x_Start(rm, client_id_0, client_id_1, llc_client_id); + break; + case 0x20: + McStatAp20_Start(rm, client_id_0, client_id_1, llc_client_id); + break; + default: + NV_ASSERT(!"Unsupported chip ID"); + break; + } +} + +static void +McStatAp1x_Stop( + NvRmDeviceHandle rm, + NvU32 *client_0_cycles, + NvU32 *client_1_cycles, + NvU32 *llc_client_cycles, + NvU32 *llc_client_clocks, + NvU32 *mc_clocks) +{ + *llc_client_cycles = NV_REGR(rm, NvRmPrivModuleID_ExternalMemoryController, + 0, EMC_STAT_LLMC_COUNT_0_0); + *llc_client_clocks = NV_REGR(rm, NvRmPrivModuleID_ExternalMemoryController, + 0, EMC_STAT_LLMC_CLOCKS_0); + *client_0_cycles = NV_REGR(rm, NvRmPrivModuleID_MemoryController, + 0, MC_STAT_EMC_COUNT_0_0); + *client_1_cycles = NV_REGR(rm, NvRmPrivModuleID_MemoryController, + 0, MC_STAT_EMC_COUNT_1_0); + *mc_clocks = NV_REGR(rm, NvRmPrivModuleID_MemoryController, + 0, MC_STAT_EMC_CLOCKS_0); +} + +void +McStat_Stop( + NvRmDeviceHandle rm, + NvU32 *client_0_cycles, + NvU32 *client_1_cycles, + NvU32 *llc_client_cycles, + NvU32 *llc_client_clocks, + NvU32 *mc_clocks) +{ + switch (rm->ChipId.Id) + { + case 0x15: + case 0x16: + McStatAp1x_Stop(rm, client_0_cycles, client_1_cycles, llc_client_cycles, llc_client_clocks, mc_clocks ); + break; + case 0x20: + McStatAp20_Stop(rm, client_0_cycles, client_1_cycles, llc_client_cycles, llc_client_clocks, mc_clocks ); + break; + default: + NV_ASSERT(!"Unsupported chip ID"); + break; + } +} + + +void +McStat_Report( + NvU32 client_id_0, + NvU32 client_0_cycles, + NvU32 client_id_1, + NvU32 client_1_cycles, + NvU32 llc_client_id, + NvU32 llc_client_clocks, + NvU32 llc_client_cycles, + NvU32 mc_clocks) +{ + NvOsDebugPrintf("LLC Client %d Count: 0x%.8X, %u\n", + llc_client_id, llc_client_cycles, llc_client_cycles); + NvOsDebugPrintf("LLC Client %d Clocks: 0x%.8X, %u\n", + llc_client_id, llc_client_clocks, llc_client_clocks); + NvOsDebugPrintf("Client %.3d Count: 0x%.8X, %u\n", + client_id_0, client_0_cycles, client_0_cycles); + NvOsDebugPrintf("Client %.3d Count: 0x%.8X, %u\n", + client_id_1, client_1_cycles, client_1_cycles); + NvOsDebugPrintf("Total MC Clocks: 0x%.8X, %u\n", mc_clocks, mc_clocks); +} + +//API to read data from OBS bus +// The OBS_PART_SEL is mapped to the specified modID by obsInfoTable which is public in this file. + +NvError +ReadObsData( + NvRmDeviceHandle rm, + NvRmModuleID modID, + NvU32 start_index, + NvU32 length, + NvU32 *value) +{ + NvU32 i = 0, offset = 0, value1, value2; + NvU32 timeout; + NvU32 partID = 0xffffffff; + NvU32 index, temp; + + for (i = 0; i < ObsInfoTableSize; i++) + { + if (modID == ObsInfoTable[i].modSelect) + { + partID = ObsInfoTable[i].partSelect; + break; + } + } + if (i == ObsInfoTableSize) + { + return NvError_BadParameter; + } + + for(offset = 0; offset < length; offset++) + { + index = start_index + offset; + temp = NV_DRF_DEF(APB_MISC_GP, OBSCTRL, OBS_EN, ENABLE) | + NV_DRF_NUM(APB_MISC_GP, OBSCTRL, OBS_MOD_SEL, modID) | + NV_DRF_NUM(APB_MISC_GP, OBSCTRL, OBS_PART_SEL, partID) | + NV_DRF_NUM(APB_MISC_GP, OBSCTRL, OBS_SIG_SEL, index) ; + NV_REGW(rm, NvRmModuleID_Misc, 0, APB_MISC_GP_OBSCTRL_0, temp); + value1 = NV_REGR(rm, NvRmModuleID_Misc, 0, APB_MISC_GP_OBSCTRL_0); + timeout = 100; + do { + value2 = value1; + value1 = NV_REGR(rm, NvRmModuleID_Misc, 0, APB_MISC_GP_OBSDATA_0); + timeout --; + } while (value1 != value2 && timeout); + NvOsDebugPrintf("OBS bus modID 0x%x index 0x%x = value 0x%x", + modID, index, value1); + value[offset] = value1; + } + return NvSuccess; +} + +/******************************************************************************/ + +#define NVRM_AP15_MONITORED_EVENTS_MAX (2) + +// AP15 CP15 performance monitor control register layout +#define AP15_CP15_PMNC_0_ENABLE_RANGE 0:0 +#define AP15_CP15_PMNC_0_EVENT_CNTS_RESET_RANGE 1:1 +#define AP15_CP15_PMNC_0_CYCLE_CNT_RESET_RANGE 2:2 +#define AP15_CP15_PMNC_0_EVENT0_CNT_OV_RANGE 8:8 +#define AP15_CP15_PMNC_0_EVENT1_CNT_OV_RANGE 9:9 +#define AP15_CP15_PMNC_0_CYCLE_CNT_OV_RANGE 10:10 +#define AP15_CP15_PMNC_0_EVENT0_RANGE 19:12 +#define AP15_CP15_PMNC_0_EVENT1_RANGE 27:20 + +static void Ap15CorePerfMonDisable(void) +{ + // Disable all performance counters + NvU32 RegValue = NV_DRF_NUM(AP15_CP15, PMNC, ENABLE, 0); + MCR(p15, 0, RegValue, c15, c12, 0); +} + +static NvError Ap15CorePerfMonCheckStatus(void) +{ + // Check if performance counters are enabled and no overflow has occurred + NvU32 RegValue; + MRC(p15, 0, RegValue, c15, c12, 0); + if ((NV_DRF_VAL(AP15_CP15, PMNC, ENABLE, RegValue) == 0) || + (NV_DRF_VAL(AP15_CP15, PMNC, CYCLE_CNT_OV, RegValue) == 1) || + (NV_DRF_VAL(AP15_CP15, PMNC, EVENT0_CNT_OV, RegValue) == 1) || + (NV_DRF_VAL(AP15_CP15, PMNC, EVENT1_CNT_OV, RegValue) == 1)) + return NvError_InvalidState; + else + return NvSuccess; +} + +static void Ap15CorePerfMonStart(NvU32* pEventList, NvU32* pEventListSize) +{ + NvU32 RegValue, Event0, Event1; + + // Just return maximum monitored events if no input list, otherwise + // get both events ready (set the same if only one specified) + if (*pEventListSize == 0) + { + *pEventListSize = NVRM_AP15_MONITORED_EVENTS_MAX; + return; + } + Event0 = Event1 = pEventList[0]; + if (*pEventListSize >= NVRM_AP15_MONITORED_EVENTS_MAX) + { + Event1 = pEventList[1]; + *pEventListSize = NVRM_AP15_MONITORED_EVENTS_MAX; + } + + // Reset, clear overflow flags and enable 3 performance counters: + // total cycle counter and 2 event counters + RegValue = + NV_DRF_NUM(AP15_CP15, PMNC, ENABLE, 1) | + NV_DRF_NUM(AP15_CP15, PMNC, EVENT_CNTS_RESET, 1) | + NV_DRF_NUM(AP15_CP15, PMNC, CYCLE_CNT_RESET, 1) | + NV_DRF_NUM(AP15_CP15, PMNC, CYCLE_CNT_OV, 1) | + NV_DRF_NUM(AP15_CP15, PMNC, EVENT0_CNT_OV, 1) | + NV_DRF_NUM(AP15_CP15, PMNC, EVENT1_CNT_OV, 1) | + NV_DRF_NUM(AP15_CP15, PMNC, EVENT0, Event0) | + NV_DRF_NUM(AP15_CP15, PMNC, EVENT1, Event1); + MCR(p15, 0, RegValue, c15, c12, 0); +} + +static NvError Ap15CorePerfMonStop( + NvU32* pCountListSize, + NvU32* pCountList, + NvU32* pTotalCycleCount) +{ + NvU32 ccnt, pmn0, pmn1; + + // Disable monotors and check status + NvError err = Ap15CorePerfMonCheckStatus(); + Ap15CorePerfMonDisable(); + if (err != NvSuccess) + return err; + + // Read back cycle and event counters + MRC(p15, 0, ccnt, c15, c12, 1); + MRC(p15, 0, pmn0, c15, c12, 2); + MRC(p15, 0, pmn1, c15, c12, 3); + + // Return total cycle count always, and event counts depending on + // the room provided by the caller + *pTotalCycleCount = ccnt; + if (*pCountListSize == 0) + return NvSuccess; + + pCountList[0] = pmn1; // ARM spec Event0 <=> Counter 1 (not a typo) + if (*pCountListSize >= NVRM_AP15_MONITORED_EVENTS_MAX) + { + pCountList[1] = pmn0; // ARM spec Event1 <=> Counter 0 (not a typo) + *pCountListSize = NVRM_AP15_MONITORED_EVENTS_MAX; + } + return NvSuccess; +} + +NvError +NvRmCorePerfMonStart( + NvRmDeviceHandle hRmDevice, + NvU32* pEventListSize, + NvU32* pEventList) +{ + NvU32 cpst; + NV_ASSERT(hRmDevice); + NV_ASSERT(pEventListSize); + NV_ASSERT ((*pEventListSize == 0) || pEventList); + + // Monitoring is supported only for SoC environment in one + // of the privileged modes + GET_CPSR(cpst); + if(IS_USER_MODE(cpst)) + return NvError_NotSupported; + if (NvRmPrivGetExecPlatform(hRmDevice) != ExecPlatform_Soc) + return NvError_NotSupported; + + switch (hRmDevice->ChipId.Id) + { + case 0x15: + case 0x16: + Ap15CorePerfMonStart(pEventList, pEventListSize); + return NvSuccess; + case 0x20: + return NvError_NotSupported; + default: + NV_ASSERT(!"Invalid chip ID"); + return NvError_NotSupported; + } +} + +NvError +NvRmCorePerfMonStop( + NvRmDeviceHandle hRmDevice, + NvU32* pCountListSize, + NvU32* pCountList, + NvU32* pTotalCycleCount) +{ + NvU32 cpst; + NV_ASSERT(hRmDevice); + NV_ASSERT(pTotalCycleCount); + NV_ASSERT(pCountListSize); + NV_ASSERT ((*pCountListSize == 0) || pCountList); + + // Monitoring is supported only for SoC environment in one + // of the privileged modes + GET_CPSR(cpst); + if(IS_USER_MODE(cpst)) + return NvError_NotSupported; + if (NvRmPrivGetExecPlatform(hRmDevice) != ExecPlatform_Soc) + return NvError_NotSupported; + + switch (hRmDevice->ChipId.Id) + { + case 0x15: + case 0x16: + return Ap15CorePerfMonStop( + pCountListSize, pCountList, pTotalCycleCount); + case 0x20: + return NvError_NotSupported; + default: + NV_ASSERT(!"Invalid chip ID"); + return NvError_NotSupported; + } +} + +static NvOsInterruptHandle s_McInterruptHandle = NULL; +static void McErrorIntHandler(void* args) +{ + NvU32 RegVal; + NvU32 IntStatus; + NvU32 IntClear = 0; + NvRmDeviceHandle hRm = (NvRmDeviceHandle)args; + + IntStatus = NV_REGR(hRm, NvRmPrivModuleID_MemoryController, 0, MC_INTSTATUS_0); + if ( NV_DRF_VAL(MC, INTSTATUS, DECERR_AXI_INT, IntStatus) ) + { + IntClear |= NV_DRF_DEF(MC, INTSTATUS, DECERR_AXI_INT, SET); + RegVal = NV_REGR(hRm, NvRmPrivModuleID_MemoryController, 0, + MC_DECERR_AXI_ADR_0); + NvOsDebugPrintf("AXI DecErrAddress=0x%x ", RegVal); + RegVal = NV_REGR(hRm, NvRmPrivModuleID_MemoryController, 0, + MC_DECERR_AXI_STATUS_0); + NvOsDebugPrintf("AXI DecErrStatus=0x%x ", RegVal); + } + if ( NV_DRF_VAL(MC, INTSTATUS, DECERR_EMEM_OTHERS_INT, IntStatus) ) + { + IntClear |= NV_DRF_DEF(MC, INTSTATUS, DECERR_EMEM_OTHERS_INT, SET); + RegVal = NV_REGR(hRm, NvRmPrivModuleID_MemoryController, 0, + MC_DECERR_EMEM_OTHERS_ADR_0); + NvOsDebugPrintf("EMEM DecErrAddress=0x%x ", RegVal); + RegVal = NV_REGR(hRm, NvRmPrivModuleID_MemoryController, 0, + MC_DECERR_EMEM_OTHERS_STATUS_0); + NvOsDebugPrintf("EMEM DecErrStatus=0x%x ", RegVal); + } + if ( NV_DRF_VAL(MC, INTSTATUS, INVALID_GART_PAGE_INT, IntStatus) ) + { + IntClear |= NV_DRF_DEF(MC, INTSTATUS, INVALID_GART_PAGE_INT, SET); + RegVal = NV_REGR(hRm, NvRmPrivModuleID_MemoryController, 0, + MC_GART_ERROR_ADDR_0); + NvOsDebugPrintf("GART DecErrAddress=0x%x ", RegVal); + RegVal = NV_REGR(hRm, NvRmPrivModuleID_MemoryController, 0, + MC_GART_ERROR_REQ_0); + NvOsDebugPrintf("GART DecErrStatus=0x%x ", RegVal); + } + + NV_ASSERT(!"MC Decode Error "); + // Clear the interrupt. + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_INTSTATUS_0, IntClear); + NvRmInterruptDone(s_McInterruptHandle); +} + +NvError NvRmPrivAp15McErrorMonitorStart(NvRmDeviceHandle hRm) +{ + NvU32 val; + NvU32 IrqList; + NvError e = NvSuccess; + NvOsInterruptHandler handler; + + if (s_McInterruptHandle == NULL) + { + // Install an interrupt handler. + handler = McErrorIntHandler; + IrqList = NvRmGetIrqForLogicalInterrupt(hRm, + NvRmPrivModuleID_MemoryController, 0); + NV_CHECK_ERROR( NvRmInterruptRegister(hRm, 1, &IrqList, &handler, + hRm, &s_McInterruptHandle, NV_TRUE) ); + // Enable Dec Err interrupts in memory Controller. + val = NV_DRF_DEF(MC, INTMASK, DECERR_AXI_INTMASK, UNMASKED) | + NV_DRF_DEF(MC, INTMASK, DECERR_EMEM_OTHERS_INTMASK, UNMASKED) | + NV_DRF_DEF(MC, INTMASK, INVALID_GART_PAGE_INTMASK, UNMASKED); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_INTMASK_0, val); + } + return e; +} + +void NvRmPrivAp15McErrorMonitorStop(NvRmDeviceHandle hRm) +{ + NvRmInterruptUnregister(hRm, s_McInterruptHandle); + s_McInterruptHandle = NULL; +} + +/* This function sets some performance timings for Mc & Emc. Numbers are from + * the Arch team. + * + */ +void NvRmPrivAp15SetupMc(NvRmDeviceHandle hRm) +{ + NvU32 reg, mask; + reg = NV_REGR(hRm, NvRmPrivModuleID_MemoryController, 0, + MC_LOWLATENCY_CONFIG_0); + mask = NV_DRF_DEF(MC, LOWLATENCY_CONFIG, CMCR_LL_CTRL, ENABLE) | + NV_DRF_DEF(MC, LOWLATENCY_CONFIG, CMCR_LL_SEND_BOTH, ENABLE) | + NV_DRF_DEF(MC, LOWLATENCY_CONFIG, MPCORER_LL_CTRL, ENABLE) | + NV_DRF_DEF(MC, LOWLATENCY_CONFIG, MPCORER_LL_SEND_BOTH, ENABLE); + if ( mask != (reg & mask) ) + NV_ASSERT(!"MC LL Path not enabled!"); + + /* 1) TIMEOUT value for VDE is 256 cycles, 3D, 2D timeouts are disabled, all others 512 cycles. */ + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_CTRL_0, 0x00000028); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_CMC_0, 0x88888888); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_DC_0, 0x88888888); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_DCB_0, 0x88888888); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_EPP_0, 0x88888888); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_G2_0, 0x0); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_HC_0, 0x88888888); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_ISP_0, 0x88888888); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_MPCORE_0, 0x88888888); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_MPEA_0, 0x88888888); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_MPEB_0, 0x88888888); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_MPEC_0, 0x88888888); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_NV_0, 0x0); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_PPCS_0, 0x88888888); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_VDE_0, 0x44444444); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT1_VDE_0, 0x44444444); + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_TIMEOUT_VI_0, 0x88888888); + + /* 2) Command Queue values should be 2,2,6 for better performance. */ + NV_REGW(hRm, NvRmPrivModuleID_ExternalMemoryController, 0, EMC_CMDQ_0, 0x00002206); + + /* 3) MC_EMEM_ARB_CFG0_0 Should have optimal values for 166Mhz DRAM. + * 27:22 EMEM_BANKCNT_NSP_TH (0xC seems to be better for 166Mhz) + * 21:16 EMEM_BANKCNT_TH (0x8 seems to be better for 166Mhz) + * + * MC_EMEM_ARB_CFG0_0 <= 0x0308_1010 + */ + + NV_REGW(hRm, NvRmPrivModuleID_MemoryController, 0, MC_EMEM_ARB_CFG0_0, 0x03081010); +} + +/******************************************************************************/ diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux.c new file mode 100644 index 000000000000..0b4e65ee9f3e --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2009-2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvrm_pinmux.h" +#include "nvrm_drf.h" +#include "nvassert.h" +#include "nvrm_hwintf.h" +#include "ap15/ap15rm_private.h" +#include "ap15/arapb_misc.h" +#include "nvrm_pinmux_utils.h" +#include "ap15/ap15rm_pinmux_utils.h" +#include "nvodm_query_pinmux.h" + +/* FindConfigStart searches through an array of configuration data to find the + * starting position of a particular configuration in a module instance array. + * The stop position is programmable, so that sub-routines can be placed after + * the last valid true configuration */ + +const NvU32* NvRmPrivAp15FindConfigStart( + const NvU32* Instance, + NvU32 Config, + NvU32 EndMarker) +{ + NvU32 Cnt = 0; + while ((Cnt < Config) && (*Instance!=EndMarker)) + { + switch (NV_DRF_VAL(MUX, ENTRY, STATE, *Instance)) + { + case PinMuxConfig_BranchLink: + case PinMuxConfig_OpcodeExtend: + if (*Instance==CONFIGEND()) + Cnt++; + Instance++; + break; + default: + Instance += NVRM_PINMUX_SET_OPCODE_SIZE; + break; + } + } + + /* Ugly postfix. In modules with bonafide subroutines, the last + * configuration CONFIGEND() will be followed by a MODULEDONE() + * token, with the first Set/Unset/Branch of the subroutine + * following that. To avoid leaving the "PC" pointing to a + * MODULEDONE() in the case where the first subroutine should be + * executed, fudge the "PC" up by one, to point to the subroutine. */ + if (EndMarker==SUBROUTINESDONE() && *Instance==MODULEDONE()) + Instance++; + + if (*Instance==EndMarker) + Instance = NULL; + + return Instance; +} + +/* NvRmSetPadTristates will increment/decrement the reference count for + * each pad group's global tristate value for each "ConfigSet" command in + * a pad group configuration, and update the register as needed */ +void NvRmPrivAp15SetPadTristates( + NvRmDeviceHandle hDevice, + const NvU32* Module, + NvU32 Config, + NvBool EnableTristate) +{ + int StackDepth = 0; + const NvU32 *Instance = NULL; + const NvU32 *ReturnStack[MAX_NESTING_DEPTH+1]; + + /* The re-multiplexing configuration is stored in program 0, + * along with the reset config. */ + if (Config==NVODM_QUERY_PINMAP_MULTIPLEXED) + Config = 0; + + Instance = NvRmPrivAp15FindConfigStart(Module, Config, MODULEDONE()); + /* The first stack return entry is NULL, so that when a ConfigEnd is + * encountered in the "main" configuration program, we pop off a NULL + * pointer, which causes the configuration loop to terminate. */ + ReturnStack[0] = NULL; + + /* This loop iterates over all of the pad groups that need to be updated, + * and updates the reference count for each appropriately. */ + + NvOsMutexLock(hDevice->mutex); + + while (Instance) + { + switch (NV_DRF_VAL(MUX,ENTRY, STATE, *Instance)) + { + case PinMuxConfig_OpcodeExtend: + /* Pop the most recent return address off of the return stack + * (which will be NULL if no values have been pushed onto the + * stack) */ + if (NV_DRF_VAL(MUX,ENTRY, OPCODE_EXTENSION, + *Instance)==PinMuxOpcode_ConfigEnd) + { + Instance = ReturnStack[StackDepth--]; + } + /* ModuleDone & SubroutinesDone should never be encountered + * during execution, for properly-formatted tables. */ + else + { + NV_ASSERT(0 && "Logical entry in table!\n"); + } + break; + case PinMuxConfig_BranchLink: + /* Push the next instruction onto the return stack if nesting space + is available, and jump to the target. */ + NV_ASSERT(StackDepthTristateRefCount[TsOffs*32 + TsShift]; + else + SkipUpdate = hDevice->TristateRefCount[TsOffs*32 + TsShift]++; +#else + SkipUpdate = 1; + else + SkipUpdate = 0; +#endif + +#if (SKIP_TRISTATE_REFCNT == 0) + if (SkipUpdate < 0) + { + hDevice->TristateRefCount[TsOffs*32 + TsShift] = 0; + NV_DEBUG_PRINTF(("(%s:%s) Negative reference count detected " + "on TRISTATE_REG_%c_0, bit %u\n", + __FILE__, __LINE__, ('A'+(TsOffs)), TsShift)); + //NV_ASSERT(SkipUpdate>=0); + } +#endif + + if (!SkipUpdate) + { + NvU32 Curr = NV_REGR(hDevice, + NvRmModuleID_Misc, 0, + APB_MISC_PP_TRISTATE_REG_A_0 + 4*TsOffs); + Curr &= ~(1<mutex); +} + +/* NvRmSetPinMuxCtl will apply new pin mux configurations to the pin mux + * control registers. */ +void NvRmPrivAp15SetPinMuxCtl( + NvRmDeviceHandle hDevice, + const NvU32* Module, + NvU32 Config) +{ + NvU32 MuxCtlOffset, MuxCtlShift, MuxCtlMask, MuxCtlSet, MuxCtlUnset; + const NvU32 *ReturnStack[MAX_NESTING_DEPTH+1]; + const NvU32 *Instance; + int StackDepth = 0; + NvU32 Curr; + + ReturnStack[0] = NULL; + Instance = Module; + + NvOsMutexLock(hDevice->mutex); + + /* The re-multiplexing configuration is stored in program 0, + * along with the reset config. */ + if (Config==NVODM_QUERY_PINMAP_MULTIPLEXED) + Config = 0; + + Instance = NvRmPrivAp15FindConfigStart(Module, Config, MODULEDONE()); + + // Apply the new configuration, setting / unsetting as appropriate + while (Instance) + { + switch (NV_DRF_VAL(MUX,ENTRY, STATE, *Instance)) + { + case PinMuxConfig_OpcodeExtend: + if (NV_DRF_VAL(MUX,ENTRY, OPCODE_EXTENSION, + *Instance)==PinMuxOpcode_ConfigEnd) + { + Instance = ReturnStack[StackDepth--]; + } + else + { + NV_ASSERT(0 && "Logical entry in table!\n"); + } + break; + case PinMuxConfig_BranchLink: + NV_ASSERT(StackDepth>MuxCtlShift)&MuxCtlMask)==MuxCtlUnset) + { + NV_ASSERT(NV_DRF_VAL(MUX,ENTRY,STATE, + *Instance)==PinMuxConfig_Unset); + Curr &= ~(MuxCtlMask<mutex); +} + +void NvRmPrivAp15InitTrisateRefCount(NvRmDeviceHandle hDevice) +{ + NvU32 i, j, curr; + + NvOsMutexLock(hDevice->mutex); + NvOsMemset(hDevice->TristateRefCount, 0, + sizeof(hDevice->TristateRefCount)); + + for (i=0; i<=((APB_MISC_PP_TRISTATE_REG_D_0- + APB_MISC_PP_TRISTATE_REG_A_0)>>2); i++) + { + curr = NV_REGR(hDevice, NvRmModuleID_Misc, 0, + APB_MISC_PP_TRISTATE_REG_A_0 + 4*i); + // swap from 0=normal, 1=tristate to 0=tristate, 1=normal + curr = ~curr; + for (j=0; curr; j++, curr>>=1) + { + /* the oppositely-named tristate reference count keeps track + * of the number of active users of each pad group, and + * enables tristate when the count reaches zero. */ + hDevice->TristateRefCount[i*32 + j] = (NvS16)(curr & 0x1); + } + } + NvOsMutexUnlock(hDevice->mutex); +} + +void NvRmAp15SetDefaultTristate(NvRmDeviceHandle hDevice) +{ + return; +} + +void NvRmPrivAp15SetGpioTristate( + NvRmDeviceHandle hDevice, + NvU32 Port, + NvU32 Pin, + NvBool EnableTristate) +{ + NvU32 Mapping = 0; + NvS16 SkipUpdate; + NvBool ret = NV_FALSE; + + NV_ASSERT(hDevice); + + switch (hDevice->ChipId.Id) + { + case 0x15: + case 0x16: + ret = NvRmAp15GetPinGroupForGpio(hDevice, Port, Pin, &Mapping); + break; + default: + NV_ASSERT(!"Chip ID not supported"); + return; + } + + if (ret) + { + NvU32 TsOffs = NV_DRF_VAL(MUX, GPIOMAP, TS_OFFSET, Mapping); + NvU32 TsShift = NV_DRF_VAL(MUX, GPIOMAP, TS_SHIFT, Mapping); + + NvOsMutexLock(hDevice->mutex); + + if (EnableTristate) +#if (SKIP_TRISTATE_REFCNT == 0) + SkipUpdate = --hDevice->TristateRefCount[TsOffs*32 + TsShift]; + else + SkipUpdate = hDevice->TristateRefCount[TsOffs*32 + TsShift]++; +#else + SkipUpdate = 1; + else + SkipUpdate = 0; +#endif + +#if (SKIP_TRISTATE_REFCNT == 0) + if (SkipUpdate < 0) + { + hDevice->TristateRefCount[TsOffs*32 + TsShift] = 0; + NV_DEBUG_PRINTF(("(%s:%s) Negative reference count detected on " + "TRISTATE_REG_%c_0, bit %u\n", __FILE__, __LINE__, + ('A'+(TsOffs)), TsShift)); + //NV_ASSERT(SkipUpdate>=0); + } +#endif + + if (!SkipUpdate) + { + NvU32 Curr = NV_REGR(hDevice, + NvRmModuleID_Misc, 0, + APB_MISC_PP_TRISTATE_REG_A_0 + 4*TsOffs); + Curr &= ~(1<mutex); + } +} + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux_tables.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux_tables.c new file mode 100644 index 000000000000..cb9e81566ecb --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux_tables.c @@ -0,0 +1,1185 @@ +/* + * Copyright (c) 2008-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvrm_pinmux.h" +#include "nvrm_drf.h" +#include "nvassert.h" +#include "nvrm_hwintf.h" +#include "ap15rm_private.h" +#include "ap15/arapb_misc.h" +#include "ap15/arclk_rst.h" +#include "ap15rm_pinmux_utils.h" +#include "nvodm_query_pinmux.h" +#include "nvrm_clocks.h" + +/** + * Each of the pin mux configurations defined in the pin mux spreadsheet are + * stored in tables below. For each configuration, every pad group that + * must be programmed is stored as a single 32b entry, where the register + * offset (for both the tristate and pin mux control registers), field bit + * position (ditto), pin mux mask, and new pin mux state are programmed. + * + * Furthermore, a simple state machine is implemented, so that pin mux + * registers can be "unprogrammed," in order to disown pad groups which + * may be pointing to a controller which is about to be programmed. The + * state machine also has no-op states which indicate when all necessary + * register programming for a configuration is complete, as well as when the + * last configuration for a module instance has been reached. + * + * Each module instance array has a reserved "reset" configuration at index + * zero. This special configuration is used in order to disown all pad + * groups whose reset state refers to the module instance. When a module + * instance configuration is to be applied, the reset configuration will + * first be applied, to ensure that no conflicts will arise between register + * reset values and the new configuration, followed by the application of + * the requested configuration. + * + * Furthermore, for controllers which support dynamic pinmuxing (i.e., + * the "Multiplexed" pin map option), the last table entry is reserved for + * a "global unset," which will ensure that all configurations are disowned. + * This Multiplexed configuration should be applied before transitioning + * from one configuration to a second one. + * + * The table data has been packed into a single 32b entry to minimize code + * footprint using macros similar to the hardware register definitions, so + * that all of the shift and mask operations can be performed with the DRF + * macros. + */ + +/* Below are the tables for all of the pin mux configurations for each + * controller. The first (zero-index) entry in each table is a "reset" + * configuration. This is used to disown all pads whose reset state + * corresponds to the controller function. When a new configuration is + * applied, the driver will first apply the reset configuration to ensure + * that no conflicts will occur due to identical signals being routed to + * multiple pad groups. + */ + +const NvU32 g_Ap15MuxI2c1[] = { + // Reset config -- disown GEN1_I2C pads + UNCONFIG(A, RM,I2C, RSVD1), CONFIGEND(), + // I2C1, Config 1 (GEN1_I2C pads) + CONFIG(A,A,RM,I2C), CONFIGEND(), + // I2C1, Config 2 (SPDIF pads) -- disown GEN1_I2C pads + CONFIG(B,D,SPDO,I2C), CONFIG(B,D,SPDI,I2C), CONFIGEND(), + // I2C1, Config 3 (SPI2 pads) + CONFIG(B,D,SPIG,I2C),CONFIG(B,D,SPIH,I2C), CONFIGEND(), + MODULEDONE() +}; + +/* I2C_2 instance 1 supports dynamic pin-muxing for CAM_I2C and GEN2_I2C; + * PinMap_Multiplex is intended to release all pads to a nominal + * state, so it is implemented at the end of the list using UNCONFIG + * options, so that no pad groups are trying to use I2C_2. + */ +const NvU32 g_Ap15MuxI2c2[] = { + // Reset & multiplexed config -- disown GEN2_I2C2 pads + UNCONFIG(G,PTA,I2C2,RSVD1),UNCONFIG(G,DTF,I2C2,RSVD1),UNCONFIG(E,LVP0,I2C2,RSVD), + UNCONFIG(E,LM1,I2C2,DISPLAYA),UNCONFIG(G,LHP0,I2C2,DISPLAYA), + UNCONFIG(G,LVP1,I2C2,DISPLAYA),CONFIGEND(), + // CAM_I2C pads + CONFIG(D,G,DTF,I2C2), CONFIGEND(), + // GEN2_I2C pads + CONFIG(A,G,PTA,I2C2), CONFIGEND(), + // LCD control pads + CONFIG(C,E,LVP0,I2C2), CONFIG(C,E,LM1,I2C2), CONFIGEND(), + // alternate LCD control pads + CONFIG(C,G,LHP0,I2C2), CONFIG(C,G,LVP1,I2C2), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxI2c[] = { + &g_Ap15MuxI2c1[0], + &g_Ap15MuxI2c2[0], + NULL +}; + +const NvU32 g_Ap15MuxI2c_Pmu[] = { + // Reset config -- disown I2CP pads + UNCONFIG(C,I2CP,I2C, RSVD2), CONFIGEND(), + // I2CP pads + CONFIG(A,C,I2CP,I2C), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxI2cPmu[] = { + &g_Ap15MuxI2c_Pmu[0], + NULL +}; + +const NvU32 g_Ap15Mux_Mmc[] = { + CONFIGEND(), // no pad groups reset to MMC, so nothing to disown for reset config + CONFIG(A,A,ATB,HSMMC), CONFIG(A,A,ATD,HSMMC), CONFIG(B,A,ATE,HSMMC), CONFIGEND(), + CONFIG(A,A,ATB,HSMMC),CONFIG(A,A,ATD,HSMMC),CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxMmc[] = { + &g_Ap15Mux_Mmc[0], + NULL +}; + +const NvU32 g_Ap15MuxSdio2[] = { + // Reset config - abandon SDB, SLXK,SLXA,SLXB,SLXC,SLXD .chosen RSVD,SLINK4B + UNCONFIG(D,SDB,SDIO2,RSVD), UNCONFIG(B,SLXK,SDIO1,SLINK4B), UNCONFIG(B,SLXB,SDIO1,SLINK4B), + UNCONFIG(B,SLXC,SDIO1,SLINK4B),UNCONFIG(B,SLXD,SDIO1,SLINK4B),UNCONFIG(B,SLXA,SDIO1,SLINK4B), + CONFIGEND(), + // config 1 SDB + SLXK,SLXA,SLXB,SLXC,SLXD pads + CONFIG(B,D,SDB,SDIO2), CONFIG(B,B,SLXK,SDIO1), CONFIG(B,B,SLXB,SDIO1), + CONFIG(B,B,SLXC,SDIO1), CONFIG(B,B,SLXD,SDIO1), CONFIG(B,B,SLXA,SDIO1),CONFIGEND(), + // config 2 KBCB,KBCE,KBCD pads + CONFIG(A,C,KBCB,SDIO1),CONFIG(A,A,KBCE,SDIO1),CONFIG(D,G,KBCD,SDIO1), + CONFIGEND(), + //config 3 KBCB pads + CONFIG(A,C,KBCB,SDIO1), CONFIGEND(), + // config 4 DAP1, SPDO, SPDI pads + CONFIG(A,C,DAP1,SDIO1), CONFIG(B,D,SPDO,SDIO1), CONFIG(B,D,SPDI,SDIO1), CONFIGEND(), + // config 5 DTA,DTD pads + CONFIG(A,B,DTA,SDIO1), CONFIG(A,B,DTD,SDIO1), CONFIGEND(), + MODULEDONE() +}; + +const NvU32 g_Ap15MuxSdio3[] = { + // no pad groups reset to SDIO3, so nothing to disown for reset config + CONFIGEND(), + // config1 SDD + SDC+SLXK+SLXA+SLXB pads + CONFIG(B,D,SDD,SDIO2), CONFIG(B,D,SDC,SDIO2), CONFIG(B,D,SDB,SDIO2_ALT), + CONFIG(B,B,SLXA,SDIO2), CONFIG(B,B,SLXK,SDIO2), CONFIG(B,B,SLXB,SDIO2), CONFIGEND(), + // congig 2 SDD, SDC pads + CONFIG(B,D,SDD,SDIO2), CONFIG(B,D,SDC,SDIO2), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxSdio[] = { + &g_Ap15MuxSdio2[0], + &g_Ap15MuxSdio3[0], + NULL +}; + +const NvU32 g_Ap15Mux_Spdif[] = { + // Reset config - abandon SPDO, SPDI .chosen RSVD. + UNCONFIG(D,SPDO,SPDIF,RSVD), UNCONFIG(D,SPDI,SPDIF,RSVD),CONFIGEND(), + // config1 SPDO+ SPDI pads + CONFIG(B,D,SPDO,SPDIF), CONFIG(B,D,SPDI,SPDIF), CONFIGEND(), + // congig 2 SLXD, SLXC pads + CONFIG(B,B,SLXD,SPDIF), CONFIG(B,B,SLXC,SPDIF), CONFIGEND(), + // congig 3 UAD, pads + CONFIG(B,A,UAD,SPDIF), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxSpdif[] = { + &g_Ap15Mux_Spdif[0], + NULL +}; + +static const NvU32 g_Ap15MuxUart1[] = { + // Reset config - abandon IRRX, IRTX & SDD + UNCONFIG(C,IRRX,UARTA,RSVD2), UNCONFIG(C,IRTX,UARTA,RSVD2), UNCONFIG(D,SDD,UARTA,PWM), CONFIGEND(), + // 8b UAA + UAB pads + CONFIG(B,A,UAA,UARTA), CONFIG(B,A,UAB,UARTA), CONFIGEND(), + // 4b UAA pads + CONFIG(B,A,UAA,UARTA_ALT3), CONFIGEND(), + // 8b GPU pads + CONFIG(A,D,GPU,UARTA), CONFIGEND(), + // 4b VFIR + UAD pads + CONFIG(A,C,IRRX,UARTA), CONFIG(A,C,IRTX,UARTA), CONFIG(B,A,UAD,UARTA), CONFIGEND(), + // 2b VFIR pads + CONFIG(A,C,IRRX,UARTA), CONFIG(A,C,IRTX,UARTA), CONFIGEND(), + // 2b SDIO pads + CONFIG(B,D,SDD,UARTA), CONFIGEND(), + MODULEDONE() +}; + +static const NvU32 g_Ap15MuxUart2[] = { +// Reset config - abandon UAD. pads.chosen SFLASH pads + UNCONFIG(A,UAD,IRDA,SFLASH), CONFIGEND(), +// 4b UAD + IRRX + IRTX pads + CONFIG(B,A,UAD,IRDA), CONFIG(A,C,IRRX,UARTB), CONFIG(A,C,IRTX,UARTB), CONFIGEND(), +// 4b UAB pads + CONFIG(B,A,UAB,UARTB), CONFIGEND(), +//..2b UAB pads + CONFIG(B,A,UAD,IRDA), CONFIGEND(), + MODULEDONE() +}; + +static const NvU32 g_Ap15MuxUart3[] = { + // Reset config - abandon UCA. chosen RSVD1 + UNCONFIG(B,UCA,UARTC,RSVD1), CONFIGEND(), + // 4b UCA + UCB pads + CONFIG(B,B,UCA,UARTC), CONFIG(B,B,UCB,UARTC), CONFIGEND(), + // 2b UCA pads + CONFIG(B,B,UCA,UARTC), CONFIGEND(), + MODULEDONE() +}; + +static const NvU32* g_Ap15MuxUart[] = { + &g_Ap15MuxUart1[0], + &g_Ap15MuxUart2[0], + &g_Ap15MuxUart3[0], + NULL +}; + +const NvU32 g_Ap15MuxSpi1[] = { + // Reset config - abandon SPIC, SPIB, SPIA, pads. + UNCONFIG(D,SPIC,SPI1,RSVD), UNCONFIG(D,SPIB,SPI1,RSVD), + UNCONFIG(D,SPIA,SPI1,RSVD), CONFIGEND(), + // SPIE,SPIF,SPID pads + CONFIG(B,D,SPIE,SPI1),CONFIG(B,D,SPIF,SPI1),CONFIG(B,D,SPID,SPI1), CONFIGEND(), + // DTE, DTB pads + CONFIG(A,B,DTE,SPI1), CONFIG(A,B,DTB,SPI1), CONFIGEND(), + // SPIC,SPIB,SPIA pads + CONFIG(B,D,SPIC,SPI1), CONFIG(B,D,SPIB,SPI1), CONFIG(B,D,SPIA,SPI1), CONFIGEND(), + // LHP2,LHP1,LHP0,LVP1,LDI,LPP pads + CONFIG(C,G,LHP2,SPI1), CONFIG(C,G,LHP1,SPI1), CONFIG(C,G,LHP0,SPI1), + CONFIG(C,G,LVP1,SPI1), CONFIG(D,G,LDI,SPI1), CONFIG(D,G,LPP,SPI1), CONFIGEND(), + MODULEDONE() +}; + +const NvU32 g_Ap15MuxSpi2[] = { + // Reset config - abandon UAB, pads. MIPI_HS chosen + UNCONFIG(A,UAB,SPI2,MIPI_HS), UNCONFIG(D,SPID,SPI2,RSVD), + UNCONFIG(D,SPIE,SPI2,RSVD), CONFIGEND(), + //..SPIC,SPIB,SPIA,SPIG, SPIH Pads + CONFIG(B,D,SPIC,SPI2), CONFIG(B,D,SPIB,SPI2), CONFIG(B,D,SPIA,SPI2), + CONFIG(B,D,SPIG,SPI2), CONFIG(B,D,SPIH,SPI2), CONFIGEND(), + // UAB pads + CONFIG(B,A,UAB,SPI2), CONFIGEND(), + // SPIE,SPIF,SPID,SPIG,SPIH pads + CONFIG(B,D,SPIE,SPI2_ALT),CONFIG(B,D,SPIF,SPI2),CONFIG(B,D,SPID,SPI2_ALT), + CONFIG(B,D,SPIG,SPI2_ALT),CONFIG(B,D,SPIH,SPI2_ALT), CONFIGEND(), + // SLXC,SLXK,SLXA,SLXB,SLXD pads + CONFIG(B,B,SLXC,SPI2), CONFIG(B,B,SLXK,SPI2), CONFIG(B,B,SLXA,SPI2), + CONFIG(B,B,SLXB,SPI2),CONFIG(B,B,SLXD,SPI2), CONFIGEND(), + MODULEDONE() +}; + +/* SPI instance 3 supports dynamic pin-muxing for audio-codec & + * display, PinMap_Multiplex is intended to release all pads to a nominal + * state, so it is implemented at the end of the list using UNCONFIG + * options, so that no pad groups are trying to use SPI3. + */ +const NvU32 g_Ap15MuxSpi3[] = { +/* Reset config - abandon UAA, SPIF, SPIG, SPIH pads. SPI2_ALT chosen + * as the reset state for SPIG/SPIH, since this will either be clobbered + * by Spi2 SpiPinMap_Config1, I2c1 I2cPinMap_Config3, correct (for Spi2 + * SpiPinMap_Config3), or irrelevant */ + UNCONFIG(A,UAA,SPI3,MIPI_HS), UNCONFIG(D,SPIF,SPI3,RSVD), + UNCONFIG(D,SPIG,SPI3,SPI2_ALT), UNCONFIG(D,SPIH,SPI3,SPI2_ALT), + // multiplex unconfiguration + UNCONFIG(C,XM2A,SPI3,SPROM), // multiplex config 1 to SPROM + UNCONFIG(E,LSC1,SPI3,DISPLAYA), UNCONFIG(E,LPW2,SPI3,DISPLAYA), // mux config 2 to displaya + UNCONFIG(E,LPW0,SPI3,DISPLAYA), UNCONFIG(E,LM0,SPI3,DISPLAYA), + UNCONFIG(E,LSCK,SPI3,DISPLAYA), UNCONFIG(E,LSDI,SPI3,DISPLAYA), // mux config 3 to displaya + UNCONFIG(D,SPIC,SPI3,RSVD),UNCONFIG(D,SPIB,SPI3,RSVD), // config 5 to rsvd + UNCONFIG(D,SPIA,SPI3,RSVD), + UNCONFIG(D,SDD,SPI3,PWM),UNCONFIG(D,SDC,SPI3,TWC), // config 6 to PWM & TWC + CONFIGEND(), + // XM2A pads + CONFIG(B,C,XM2A,SPI3), CONFIGEND(), + // LCD pads + CONFIG(C,E,LSC1,SPI3), CONFIG(D,E,LPW2,SPI3), CONFIG(D,E,LPW0,SPI3), CONFIG(C,E,LM0,SPI3), CONFIGEND(), + // Alternate LCD pads + CONFIG(C,E,LSCK,SPI3), CONFIG(D,E,LSDI,SPI3), CONFIG(D,E,LSDA,SPI3), CONFIG(C,E,LCSN,SPI3), CONFIGEND(), + // UAA pads + CONFIG(B,A,UAA,SPI3), CONFIGEND(), + // SPI pads + CONFIG(B,D,SPIA,SPI3), CONFIG(B,D,SPIB,SPI3), CONFIG(B,D,SPIC,SPI3), CONFIGEND(), + // 2CS SPI3 on SDIO pads + CONFIG(B,D,SDC,SPI3), CONFIG(B,D,SDD,SPI3), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxSpi[] = { + &g_Ap15MuxSpi1[0], + &g_Ap15MuxSpi2[0], + &g_Ap15MuxSpi3[0], + NULL +}; + +// Sflash should always be after PWM in the module order, since +// the reset value for UCB muxes from both controllers, so the +// reset configuration for Sflash assumes that Pwm has executed first. +NV_CT_ASSERT((NvU32)NvOdmIoModule_Sflash > (NvU32)NvOdmIoModule_Pwm); + +const NvU32 g_Ap15Mux_Sflash[] = { + /* Reset config. Normally, this would disown the UCB pads; HOWEVER, + * the reset value for this pad group actually muxes from 2 controllers: + * PWM goes to UART3_RTS, and SFLASH goes to UART3_CTS. Since the PWM + * controller is initialized before Spi Flash, it is possible for the + * UCB pads to be correctly configured to mux 0 before reaching here. + * Therefore, the correct thing to do is to skip the UNCONFIG for this + * pad group, since PWM will already handle this. + */ + /*UNCONFIG(B,UCB,PWM0,RSVD2),*/ CONFIGEND(), + // config 1 XM2S + XM2A pads + CONFIG(B,C,XM2S,SPI), CONFIG(B,C,XM2A,SPI), CONFIGEND(), + // config2 XM2S + UAD +XM2A pads + CONFIG(B,C,XM2S,SPI), CONFIG(B,A,UAD,SFLASH), CONFIG(B,C,XM2A,SPI), CONFIGEND(), + // config 3 XM2S + UCB +XM2A pads + CONFIG(B,C,XM2S,SPI), CONFIG(B,B,UCB,PWM0), CONFIG(B,C,XM2A,SPI), CONFIGEND(), + // config 4 XM2A UAD UCB XM2A pads + CONFIG(B,C,XM2S,SPI), CONFIG(B,A,UAD,SFLASH), CONFIG(B,B,UCB,PWM0), + CONFIG(B,C,XM2A,SPI), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxSflash[] = { + &g_Ap15Mux_Sflash[0], + NULL +}; + + +const NvU32 g_Ap15Mux_Twc[] = { + // no pad groups reset to TWC, so nothing to disown for reset config + CONFIGEND(), + // DAP2 pads + CONFIG(A,C,DAP2,TWC), CONFIGEND(), + // SDC pads + CONFIG(B,D,SDC,TWC), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxTwc[] = { + &g_Ap15Mux_Twc[0], + NULL +}; + +const NvU32 g_Ap15Mux_Ata[] = { + // Reset config -- abandon ATA, ATC, ATB, ATD, ATE pads. NAND RSVD as chosenpads + UNCONFIG(A,ATC,IDE,RSVD), UNCONFIG(A,ATD,IDE,NAND), UNCONFIG(A,ATE,IDE,NAND), + UNCONFIG(A,ATA,IDE,RSVD), UNCONFIG(A,ATB,IDE,NAND), CONFIGEND(), + // ATA, Config 1 (Nand pads) + CONFIG(A,A,ATC,IDE), CONFIG(A,A,ATD,IDE), CONFIG(B,A,ATE,IDE), CONFIG(A,A,ATA,IDE), + CONFIG(A,A,ATB,IDE), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxAta[] = { + &g_Ap15Mux_Ata[0], + NULL +}; + +const NvU32 g_Ap15Mux_Pwm[] = { + // Reset config -- disown SDC,UCB pads SDIO2, RSVD2 as chosen pads + UNCONFIG(D,SDC,PWM,SDIO2), UNCONFIG(B,UCB,PWM0,RSVD2), CONFIGEND(), + // PWM, Config 1 (SDC pads) + CONFIG(B,D,SDC,PWM), CONFIGEND(), + // PWM, Config 2 (UCB ,SDDpads) + CONFIG(B,B,UCB,PWM0), CONFIG(B,D,SDD,PWM), CONFIGEND(), + // PWM, Config 2 (UCB ,SDDpads) + CONFIG(B,B,UCB,PWM0), CONFIGEND(), + CONFIG(B,D,SDD,PWM), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxPwm[] = { + &g_Ap15Mux_Pwm[0], + NULL +}; + +const NvU32 g_Ap15Mux_Hsi[] = { + CONFIGEND(), // no pad groups reset to HSI, so nothing to disown for reset config + CONFIG(B,A,UAA,MIPI_HS), CONFIG(B,A,UAB,MIPI_HS), CONFIGEND(), + MODULEDONE() +}; + +const NvU32 *g_Ap15MuxHsi[] = { + &g_Ap15Mux_Hsi[0], + NULL +}; + +const NvU32 g_Ap15Mux_Nand[] = { + CONFIGEND(), // no pad groups reset to NAND, so nothing to disown for reset config + // config 1 ATA,ATB,ATC,ATD,ATE pads + CONFIG(A,A,ATA,NAND_ALT), CONFIG(A,A,ATB,NAND_ALT), CONFIG(A,A,ATC,NAND), + CONFIG(A,A,ATD,NAND), CONFIG(B,A,ATE,NAND), CONFIGEND(), + // config 1 ATA,ATB,ATC,ATD,ATE pads + CONFIG(A,A,ATA,NAND), CONFIG(A,A,ATB,NAND), CONFIG(A,A,ATC,NAND), + CONFIG(A,A,ATD,NAND), CONFIG(B,A,ATE,NAND), CONFIGEND(), + // config 1 ATA,ATC,ATE pads + CONFIG(A,A,ATA,NAND), CONFIG(A,A,ATC,NAND), + CONFIG(B,A,ATE,NAND_ALT), CONFIGEND(), + // config 1 ATA,ATB,ATC,ATD,ATE pads + CONFIG(A,A,ATA,NAND), CONFIG(A,A,ATB,NAND), CONFIG(A,A,ATC,NAND), + CONFIG(A,A,ATD,NAND_ALT), CONFIG(B,A,ATE,NAND_ALT), CONFIGEND(), + // config 1 ATA,ATC pads + CONFIG(A,A,ATA,NAND), CONFIG(A,A,ATC,NAND), CONFIGEND(), + // config 1 ATA,ATB,ATC pads + CONFIG(A,A,ATA,NAND), CONFIG(A,A,ATB,NAND), + CONFIG(A,A,ATC,NAND), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxNand[] = { + &g_Ap15Mux_Nand[0], + NULL +}; + +const NvU32 g_Ap15MuxDap1[] = { + // Reset config - abandon ,DAP1.. RSVD2 chosen + UNCONFIG(C,DAP1,DAP1,RSVD2), CONFIGEND(), + // config1 DAP1 pads + CONFIG(A,C,DAP1,DAP1), CONFIGEND(), + MODULEDONE() +}; + +const NvU32 g_Ap15MuxDap2[] = { + // Reset config - abandon ,DAP2... RSVD3 chosen + UNCONFIG(C,DAP2,DAP2,RSVD3), CONFIGEND(), + // config1 DAP2 pads + CONFIG(A,C,DAP2,DAP2), CONFIGEND(), + // congig 2 SLXD, SLXC pads + MODULEDONE() +}; + +const NvU32 g_Ap15MuxDap3[] = { + // Reset config - abandon ,DAP3... RSVD2 chosen + UNCONFIG(C,DAP3,DAP3,RSVD2), CONFIGEND(), + // config1 DAP3 pads + CONFIG(A,C,DAP3,DAP3), CONFIGEND(), + MODULEDONE() +}; + +const NvU32 g_Ap15MuxDap4[] = { + // Reset config - abandon ,DAP4...RSVD2 chosen + UNCONFIG(C,DAP4,DAP4,RSVD2), CONFIGEND(), + // config1 DAP4 pads + CONFIG(A,C,DAP4,DAP4), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxDap[] = { + &g_Ap15MuxDap1[0], + &g_Ap15MuxDap2[0], + &g_Ap15MuxDap3[0], + &g_Ap15MuxDap4[0], + NULL +}; + +const NvU32 g_Ap15Mux_Kbc[] = { + // Reset config - abandon ,RSVD2, RSVD1 chosen + UNCONFIG(C,KBCA,KBC,RSVD2), UNCONFIG(C,KBCB,KBC,RSVD2), UNCONFIG(A,KBCE,KBC,RSVD1), + UNCONFIG(C,KBCC,KBC,RSVD2), UNCONFIG(G,KBCD,KBC,RSVD2), UNCONFIG(A,KBCF,KBC,RSVD1), CONFIGEND(), + // KBCA,KBCB,KBCC,KBCD,KBCE,KBCF pads + CONFIG(A,C,KBCA,KBC), CONFIG(A,C,KBCB,KBC), CONFIG(A,A,KBCE,KBC), + CONFIG(B,C,KBCC,KBC), CONFIG(D,G,KBCD,KBC), CONFIG(A,A,KBCF,KBC), CONFIGEND(), + // KBCA,KBCC,KBCD,KBCE,KBCF pads + CONFIG(A,C,KBCA,KBC), CONFIG(A,A,KBCE,KBC), + CONFIG(B,C,KBCC,KBC), CONFIG(D,G,KBCD,KBC), CONFIG(A,A,KBCF,KBC), CONFIGEND(), + // KBCA,KBCC,KBCF, pads + CONFIG(A,C,KBCA,KBC), CONFIG(B,C,KBCC,KBC), CONFIG(A,A,KBCF,KBC), CONFIGEND(), + // KBCA,KBCC pads + CONFIG(A,C,KBCA,KBC), CONFIG(B,C,KBCC,KBC), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxKbc[] = { + &g_Ap15Mux_Kbc[0], + NULL +}; + +NvU32 g_Ap15Mux_Hdcp[] = { + CONFIGEND(), // no pad groups reset to HDCP, so nothing to disown for reset config + CONFIG(A,G,PTA,HDMI), CONFIGEND(), + CONFIG(C,E,LSCK,HDMI), CONFIG(D,E,LSDA,HDMI), CONFIGEND(), + CONFIG(D,E,LPW2,HDMI), CONFIG(D,E,LPW0,HDMI), CONFIGEND(), + CONFIG(C,E,LSC1,HDMI), CONFIG(D,E,LPW0,HDMI), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxHdcp[] = { + &g_Ap15Mux_Hdcp[0], + NULL +}; + +const NvU32 g_Ap15Mux_Hdmi[] = { + // HDINT resets to HDINT, so move it to a reserved pin + UNCONFIG(B,HDINT,RSVD1,RSVD2), CONFIGEND(), + CONFIG(C,B,HDINT,RSVD1), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxHdmi[] = { + &g_Ap15Mux_Hdmi[0], + NULL +}; + +const NvU32 g_Ap15Mux_Mio[] = { + CONFIGEND(), // no pad groups reset to MIO, so nothing to disown for reset config + CONFIG(A,A,KBCF,MIO), CONFIG(D,G,KBCD,MIO), CONFIG(A,C,KBCB,MIO), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxMio[] = { + &g_Ap15Mux_Mio[0], + NULL +}; + +const NvU32 g_Ap15Mux_Slink[] = { + CONFIGEND(), // no pad groups reset to SLINK, so nothing to disown for reset config + CONFIG(B,B,SLXK,SLINK4B), CONFIG(B,B,SLXA,SLINK4B), CONFIG(B,B,SLXB,SLINK4B), + CONFIG(B,B,SLXC,SLINK4B), CONFIG(B,B,SLXD,SLINK4B), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxSlink[] = { + &g_Ap15Mux_Slink[0], + NULL +}; + +const NvU32 g_Ap15Mux_Vi[] = { + CONFIGEND(), // no pad groups reset to VI so nothing to disown for reset config + // config 1 DTA - DTF pads + BRANCH(NvOdmVideoInputPinMap_Config2), CONFIG(D,G,DTF,VI), CONFIGEND(), + // config 2 DTA - DTE and CSUS pads + CONFIG(A,B,DTA,VI), CONFIG(A,B,DTB,VI), CONFIG(A,B,DTC,VI), + CONFIG(A,B,DTD,VI), CONFIG(A,B,DTE,VI), CONFIGEND(), + MODULEDONE(), + SUBROUTINESDONE(), +}; + +const NvU32* g_Ap15MuxVi[] = { + &g_Ap15Mux_Vi[0], + NULL +}; + +const NvU32 g_Ap15Mux_Crt[] = { + // Need to confirm and fix it ,but none of docs specifies about tv pad group + CONFIGEND(), // no pad groups reset to CRT so nothing to disown for reset config + // config 1 LHS, LVS, pads + CONFIG(D,E,LHS,CRT), CONFIG(C,E,LVS,CRT), CONFIGEND(), + // config 2 LHP2,LPW1 pads + CONFIG(C,G,LHP2,CRT), CONFIG(D,E,LPW1,CRT), CONFIGEND(), + // config 3 LM1,LPW1 pads + CONFIG(C,E,LM1,CRT), CONFIG(D,E,LPW1,CRT), CONFIGEND(), + // config 4 LHP2,LCSN pads + CONFIG(C,G,LHP2,CRT), CONFIG(C,E,LCSN,CRT), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxCrt[] = { + &g_Ap15Mux_Crt[0], + NULL +}; + +const NvU32 g_Ap15Mux_BacklightDisplay1Pwm0[] = { + CONFIGEND(), + // Config 1 LPW0 pad + CONFIG(D,E,LPW0,DISPLAYA), CONFIGEND(), + // Config 2 LPW2 pad + CONFIG(D,E,LPW2,DISPLAYA), CONFIGEND(), + // Config 3 LM0 pad + CONFIG(C,E,LM0,DISPLAYA), CONFIGEND(), + MODULEDONE() +}; + +const NvU32 g_Ap15Mux_BacklightDisplay1Pwm1[] = { + CONFIGEND(), + // Config 1 LM1 pad + CONFIG(C,E,LM1,DISPLAYA), CONFIGEND(), + // Config 2 LDC pad + CONFIG(C,E,LDC,DISPLAYA), CONFIGEND(), + // Config 3 LPW1 pad + CONFIG(D,E,LPW1,DISPLAYA), CONFIGEND(), + MODULEDONE() +}; + +const NvU32 g_Ap15Mux_BacklightDisplay2Pwm0[] = { + CONFIGEND(), + // Config 1 LPW0 pad + CONFIG(D,E,LPW0,DISPLAYB), CONFIGEND(), + // Config 2 LPW2 pad + CONFIG(D,E,LPW2,DISPLAYB), CONFIGEND(), + // Config 3 LM0 pad + CONFIG(C,E,LM0,DISPLAYB), CONFIGEND(), + MODULEDONE() +}; + +const NvU32 g_Ap15Mux_BacklightDisplay2Pwm1[] = { + CONFIGEND(), + // Config 1 LM1 pad + CONFIG(C,E,LM1,DISPLAYB), CONFIGEND(), + // Config 2 LDC pad + CONFIG(C,E,LDC,DISPLAYB), CONFIGEND(), + // Config 3 LPW1 pad + CONFIG(D,E,LPW1,DISPLAYB), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxBacklight[] = { + &g_Ap15Mux_BacklightDisplay1Pwm0[0], + &g_Ap15Mux_BacklightDisplay1Pwm1[0], + &g_Ap15Mux_BacklightDisplay2Pwm0[0], + &g_Ap15Mux_BacklightDisplay2Pwm1[0], + NULL +}; + +const NvU32 g_Ap15Mux_Display1[] = { + CONFIGEND(), + // config 1, 24b RGB. Pure superset of Config2 (18b RGB) + BRANCH(2), + CONFIG(C,G,LHP1,DISPLAYA),CONFIG(C,G,LHP2,DISPLAYA),CONFIG(C,G,LVP1,DISPLAYA), + CONFIG(C,G,LHP0,DISPLAYA),CONFIG(D,G,LDI,DISPLAYA),CONFIG(D,G,LPP,DISPLAYA), + CONFIGEND(), + // config 2, 18b RGB. + BRANCH(7), + CONFIG(C,E,LVS,DISPLAYA), CONFIG(D,E,LHS,DISPLAYA), CONFIG(D,E,LSPI,DISPLAYA), + CONFIGEND(), + // config 3, 8 & 9b CPU. + CONFIG(C,G,LHP1,DISPLAYA), CONFIG(C,G,LHP2,DISPLAYA), CONFIG(C,G,LVP1,DISPLAYA), + CONFIG(C,G,LHP0,DISPLAYA), CONFIG(D,G,LDI,DISPLAYA), CONFIG(D,G,LPP,DISPLAYA), + CONFIG(D,E,LPW0,DISPLAYA), CONFIG(D,E,LPW1,DISPLAYA), CONFIG(D,E,LPW2,DISPLAYA), + CONFIG(C,E,LSC1,DISPLAYA), CONFIG(C,E,LM1,DISPLAYA), + CONFIG(C,E,LVP0,DISPLAYA), CONFIGEND(), + // config 4. SPI + CONFIG(D,E,LPW0,DISPLAYA), CONFIG(D,E,LPW2,DISPLAYA), CONFIG(C,E,LSC1,DISPLAYA), + CONFIG(C,E,LM0,DISPLAYA), CONFIG(C,E,LVP0,DISPLAYA), CONFIGEND(), + // Config 5. Panel 86 + BRANCH(7),CONFIG(C,E,LSC1,DISPLAYA),CONFIG(C,E,LM1,DISPLAYA),CONFIGEND(), + // config 6. 16/18b smart panels + BRANCH(7),CONFIG(C,E,LDC,DISPLAYA),CONFIG(D,E,LSPI,DISPLAYA),CONFIGEND(), + MODULEDONE(), + // subroutine 1. - 18b data + clock + CONFIG(C,F,LD0,DISPLAYA), CONFIG(C,F,LD1,DISPLAYA), CONFIG(C,F,LD2,DISPLAYA), + CONFIG(C,F,LD3,DISPLAYA), CONFIG(C,F,LD4,DISPLAYA), CONFIG(C,F,LD5,DISPLAYA), + CONFIG(C,F,LD6,DISPLAYA), CONFIG(C,F,LD7,DISPLAYA), CONFIG(C,F,LD8,DISPLAYA), + CONFIG(C,F,LD9,DISPLAYA), CONFIG(C,F,LD10,DISPLAYA), CONFIG(C,F,LD11,DISPLAYA), + CONFIG(C,F,LD12,DISPLAYA), CONFIG(C,F,LD13,DISPLAYA), CONFIG(C,F,LD14,DISPLAYA), + CONFIG(C,F,LD15,DISPLAYA), CONFIG(C,G,LD16,DISPLAYA), CONFIG(C,G,LD17,DISPLAYA), + CONFIG(C,E,LSC0,DISPLAYA), CONFIGEND(), + SUBROUTINESDONE(), // This is required, since BRANCH is used. +/* For handy reference, here is the complete list of CONFIG macros for the display + pad groups, in case any more configurations are defined in the future. + CONFIG(C,F,LD0,DISPLAYA), CONFIG(C,F,LD1,DISPLAYA), CONFIG(C,F,LD2,DISPLAYA), + CONFIG(C,F,LD3,DISPLAYA), CONFIG(C,F,LD4,DISPLAYA), CONFIG(C,F,LD5,DISPLAYA), + CONFIG(C,F,LD6,DISPLAYA), CONFIG(C,F,LD7,DISPLAYA), CONFIG(C,F,LD8,DISPLAYA), + CONFIG(C,F,LD9,DISPLAYA), CONFIG(C,F,LD10,DISPLAYA), CONFIG(C,F,LD11,DISPLAYA), + CONFIG(C,F,LD12,DISPLAYA), + CONFIG(C,F,LD13,DISPLAYA), CONFIG(C,F,LD14,DISPLAYA), CONFIG(C,F,LD15,DISPLAYA), + CONFIG(C,G,LD16,DISPLAYA), CONFIG(C,G,LD17,DISPLAYA),CONFIG(C,E,LSC0,DISPLAYA), + CONFIG(C,E,LVS,DISPLAYA), CONFIG(D,E,LHS,DISPLAYA), CONFIG(D,E,LSPI,DISPLAYA), + CONFIG(C,G,LHP1,DISPLAYA), CONFIG(C,G,LHP2,DISPLAYA), CONFIG(C,G,LHP0,DISPLAYA), + CONFIG(C,G,LVP1,DISPLAYA), CONFIG(D,G,LDI,DISPLAYA), CONFIG(D,G,LPP,DISPLAYA), + CONFIG(C,E,LCSN,DISPLAYA), CONFIG(C,E,LM1,DISPLAYA),CONFIG(C,E,LM0,DISPLAYA), + CONFIG(D,E,LPW0,DISPLAYA),CONFIG(D,E,LPW2,DISPLAYA), CONFIG(D,E,LPW1,DISPLAYA), + CONFIG(C,E,LVP0,DISPLAYA), CONFIG(C,E,LDC,DISPLAYA), CONFIG(C,E,LSC1,DISPLAYA), + CONFIG(D,E,LSDI,DISPLAYA), + */ +}; + +const NvU32 g_Ap15Mux_Display2[] = { + CONFIGEND(), + // config 1, 24b RGB. Pure superset of Config2 (18b RGB) + BRANCH(2), + CONFIG(C,G,LHP1,DISPLAYB),CONFIG(C,G,LHP2,DISPLAYB),CONFIG(C,G,LVP1,DISPLAYB), + CONFIG(C,G,LHP0,DISPLAYB),CONFIG(D,G,LDI,DISPLAYB),CONFIG(D,G,LPP,DISPLAYB), + CONFIGEND(), + // config 2, 18b RGB. + BRANCH(7), + CONFIG(C,E,LVS,DISPLAYB), CONFIG(D,E,LHS,DISPLAYB), CONFIG(D,E,LSPI,DISPLAYB), + CONFIGEND(), + // config 3, 8 & 9b CPU. + CONFIG(C,G,LHP1,DISPLAYB), CONFIG(C,G,LHP2,DISPLAYB), CONFIG(C,G,LVP1,DISPLAYB), + CONFIG(C,G,LHP0,DISPLAYB), CONFIG(D,G,LDI,DISPLAYB), CONFIG(D,G,LPP,DISPLAYB), + CONFIG(D,E,LPW0,DISPLAYB), CONFIG(D,E,LPW1,DISPLAYB), CONFIG(D,E,LPW2,DISPLAYB), + CONFIG(C,E,LSC1,DISPLAYB), CONFIG(C,E,LM1,DISPLAYB), + CONFIG(C,E,LVP0,DISPLAYB), CONFIGEND(), + // config 4. SPI + CONFIG(D,E,LPW0,DISPLAYB), CONFIG(D,E,LPW2,DISPLAYB), CONFIG(C,E,LSC1,DISPLAYB), + CONFIG(C,E,LM0,DISPLAYB), CONFIG(C,E,LVP0,DISPLAYB), CONFIGEND(), + // Config 5. USed only for Sony VGA panel + BRANCH(7),CONFIG(C,E,LSC1,DISPLAYB),CONFIG(C,E,LM1,DISPLAYB),CONFIGEND(), + // config 6. 16/18b smart panels + BRANCH(7),CONFIG(C,E,LDC,DISPLAYB),CONFIG(D,E,LSPI,DISPLAYB),CONFIGEND(), + MODULEDONE(), + // subroutine 1. (config 7) + CONFIG(C,F,LD0,DISPLAYB), CONFIG(C,F,LD1,DISPLAYB), CONFIG(C,F,LD2,DISPLAYB), + CONFIG(C,F,LD3,DISPLAYB), CONFIG(C,F,LD4,DISPLAYB), CONFIG(C,F,LD5,DISPLAYB), + CONFIG(C,F,LD6,DISPLAYB), CONFIG(C,F,LD7,DISPLAYB), CONFIG(C,F,LD8,DISPLAYB), + CONFIG(C,F,LD9,DISPLAYB), CONFIG(C,F,LD10,DISPLAYB), CONFIG(C,F,LD11,DISPLAYB), + CONFIG(C,F,LD12,DISPLAYB), CONFIG(C,F,LD13,DISPLAYB), CONFIG(C,F,LD14,DISPLAYB), + CONFIG(C,F,LD15,DISPLAYB), CONFIG(C,G,LD16,DISPLAYB), CONFIG(C,G,LD17,DISPLAYB), + CONFIG(C,E,LSC0,DISPLAYB), CONFIGEND(), + SUBROUTINESDONE(), +}; + +const NvU32* g_Ap15MuxDisplay[] = { + &g_Ap15Mux_Display1[0], + &g_Ap15Mux_Display2[0], + NULL +}; + +const NvU32 g_Ap15Mux_Cdev1[] = { + // reset config - no-op + CONFIGEND(), + CONFIG(A,C,CDEV1,PLLA_OUT), CONFIGEND(), + CONFIG(A,C,CDEV1,OSC), CONFIGEND(), + MODULEDONE() +}; + +const NvU32 g_Ap15Mux_Cdev2[] = { + CONFIGEND(), + CONFIG(A,C,CDEV2,AHB_CLK), CONFIGEND(), + CONFIG(A,C,CDEV2,OSC), CONFIGEND(), + MODULEDONE() +}; + +const NvU32 g_Ap15Mux_Csus[] = { + CONFIGEND(), + CONFIG(A,C,CSUS,VI_SENSOR_CLK), CONFIGEND(), + MODULEDONE() +}; + +const NvU32* g_Ap15MuxCdev[] = +{ + &g_Ap15Mux_Cdev1[0], + &g_Ap15Mux_Cdev2[0], + &g_Ap15Mux_Csus[0], + NULL +}; + +/* Array of all the controller types in the system, pointing to the array of + * instances of each controller. Indexed using the NvRmIoModule value. + */ +static const NvU32** g_Ap15MuxControllers[] = { + &g_Ap15MuxAta[0], + &g_Ap15MuxCrt[0], + NULL, // no options for CSI + &g_Ap15MuxDap[0], + &g_Ap15MuxDisplay[0], + NULL, // no options for DSI + NULL, // no options for GPIO + &g_Ap15MuxHdcp[0], + &g_Ap15MuxHdmi[0], + &g_Ap15MuxHsi[0], + &g_Ap15MuxMmc[0], + NULL, // no options for I2S + &g_Ap15MuxI2c[0], + &g_Ap15MuxI2cPmu[0], + &g_Ap15MuxKbc[0], + &g_Ap15MuxMio[0], + &g_Ap15MuxNand[0], + &g_Ap15MuxPwm[0], + &g_Ap15MuxSdio[0], + &g_Ap15MuxSflash[0], + &g_Ap15MuxSlink[0], + &g_Ap15MuxSpdif[0], + &g_Ap15MuxSpi[0], + &g_Ap15MuxTwc[0], + NULL, // no options for TVO + &g_Ap15MuxUart[0], + NULL, // no options for USB + NULL, // no options for VDD + &g_Ap15MuxVi[0], + NULL, // no options for XIO + &g_Ap15MuxCdev[0], + NULL, // no options for Ulpi + NULL, // no options for one wire + NULL, // no options for sync NOR + NULL, // no options for PCI-E + NULL, // no options for ETM + NULL, // no options for TSENSor + &g_Ap15MuxBacklight[0], +}; + +NV_CT_ASSERT(NV_ARRAY_SIZE(g_Ap15MuxControllers)==NvOdmIoModule_Num); + +const NvU32*** +NvRmAp15GetPinMuxConfigs(NvRmDeviceHandle hDevice) +{ + NV_ASSERT(hDevice); + return (const NvU32***) g_Ap15MuxControllers; +} + +/* Define the GPIO port/pin to tristate mappings */ + +const NvU16 g_Ap15GpioPadGroupMapping[] = +{ + // Port A + GPIO_TRISTATE(B,SDB), GPIO_TRISTATE(B,UCB), GPIO_TRISTATE(A,DAP2), GPIO_TRISTATE(A,DAP2), + GPIO_TRISTATE(A,DAP2), GPIO_TRISTATE(A,DAP2), GPIO_TRISTATE(B,SDD), GPIO_TRISTATE(B,SDD), + // Port B + GPIO_TRISTATE(B,XM2A), GPIO_TRISTATE(B,XM2A), GPIO_TRISTATE(D,LPW0), GPIO_TRISTATE(C,LSC0), + GPIO_TRISTATE(B,SDC), GPIO_TRISTATE(B,SDC), GPIO_TRISTATE(B,SDC), GPIO_TRISTATE(B,SDC), + // Port C + GPIO_TRISTATE(B,UCB), GPIO_TRISTATE(D,LPW1), GPIO_TRISTATE(B,UAD), GPIO_TRISTATE(B,UAD), + GPIO_TRISTATE(A,RM), GPIO_TRISTATE(A,RM), GPIO_TRISTATE(D,LPW2), GPIO_TRISTATE(B,XM2C), + // Port D + GPIO_TRISTATE(B,SLXK), GPIO_TRISTATE(B,SLXA), GPIO_TRISTATE(B,SLXB), GPIO_TRISTATE(B,SLXC), + GPIO_TRISTATE(B,SLXD), GPIO_TRISTATE(A,DTA), GPIO_TRISTATE(A,DTC), GPIO_TRISTATE(A,DTC), + // Port E + GPIO_TRISTATE(C,LD0), GPIO_TRISTATE(C,LD1), GPIO_TRISTATE(C,LD2), GPIO_TRISTATE(C,LD3), + GPIO_TRISTATE(C,LD4), GPIO_TRISTATE(C,LD5), GPIO_TRISTATE(C,LD6), GPIO_TRISTATE(C,LD7), + // Port F + GPIO_TRISTATE(C, LD8), GPIO_TRISTATE(C,LD9), GPIO_TRISTATE(C,LD10), GPIO_TRISTATE(C,LD11), + GPIO_TRISTATE(C, LD12), GPIO_TRISTATE(C,LD13), GPIO_TRISTATE(C, LD14), GPIO_TRISTATE(C,LD15), + // Port G + GPIO_TRISTATE(A,ATC), GPIO_TRISTATE(A,ATC),GPIO_TRISTATE(A,ATC), GPIO_TRISTATE(A,ATC), + GPIO_TRISTATE(A,ATC), GPIO_TRISTATE(A,ATC),GPIO_TRISTATE(A,ATC), GPIO_TRISTATE(A,ATC), + // Port H + GPIO_TRISTATE(A,ATD), GPIO_TRISTATE(A,ATD),GPIO_TRISTATE(A,ATD), GPIO_TRISTATE(A,ATD), + GPIO_TRISTATE(B,ATE), GPIO_TRISTATE(B,ATE),GPIO_TRISTATE(B,ATE), GPIO_TRISTATE(B,ATE), + // Port I + GPIO_TRISTATE(A,ATC), GPIO_TRISTATE(A,ATC), GPIO_TRISTATE(A,ATA), GPIO_TRISTATE(A,ATA), + GPIO_TRISTATE(A,ATA), GPIO_TRISTATE(A,ATB), GPIO_TRISTATE(A,ATB), GPIO_TRISTATE(A,ATC), + // Port J + GPIO_TRISTATE(B,XM2S), GPIO_TRISTATE(D,LSPI), GPIO_TRISTATE(B,XM2S), GPIO_TRISTATE(D,LHS), + GPIO_TRISTATE(C,LVS), GPIO_TRISTATE(A,IRTX), GPIO_TRISTATE(A,IRRX), GPIO_TRISTATE(B,XM2A), + // Port K + GPIO_TRISTATE(A,ATC), GPIO_TRISTATE(A,ATC), GPIO_TRISTATE(A,ATC), GPIO_TRISTATE(A,ATC), + GPIO_TRISTATE(A,ATC), GPIO_TRISTATE(B,SPDO), GPIO_TRISTATE(B,SPDI), GPIO_TRISTATE(B,XM2A), + // Port L + GPIO_TRISTATE(A,DTD), GPIO_TRISTATE(A,DTD), GPIO_TRISTATE(A,DTD), GPIO_TRISTATE(A,DTD), + GPIO_TRISTATE(A,DTD), GPIO_TRISTATE(A,DTD), GPIO_TRISTATE(A,DTD), GPIO_TRISTATE(A,DTD), + // Port M + GPIO_TRISTATE(C,LD16), GPIO_TRISTATE(C,LD17), GPIO_TRISTATE(C,LHP1), GPIO_TRISTATE(C,LHP2), + GPIO_TRISTATE(C,LVP1), GPIO_TRISTATE(C,LHP0), GPIO_TRISTATE(D,LDI), GPIO_TRISTATE(D,LPP), + // Port N + GPIO_TRISTATE(A,DAP1), GPIO_TRISTATE(A,DAP1), GPIO_TRISTATE(A,DAP1), GPIO_TRISTATE(A,DAP1), + GPIO_TRISTATE(C,LCSN), GPIO_TRISTATE(D,LSDA), GPIO_TRISTATE(C,LDC), GPIO_TRISTATE(C,HDINT), + // Port O + GPIO_TRISTATE(B,UAB), GPIO_TRISTATE(B,UAA), GPIO_TRISTATE(B,UAA), GPIO_TRISTATE(B,UAA), + GPIO_TRISTATE(B,UAA), GPIO_TRISTATE(B,UAB), GPIO_TRISTATE(B,UAB), GPIO_TRISTATE(B,UAB), + // Port P + GPIO_TRISTATE(A,DAP3), GPIO_TRISTATE(A,DAP3), GPIO_TRISTATE(A,DAP3), GPIO_TRISTATE(A,DAP3), + GPIO_TRISTATE(A,DAP4), GPIO_TRISTATE(A,DAP4), GPIO_TRISTATE(A,DAP4), GPIO_TRISTATE(A,DAP4), + // Port Q + GPIO_TRISTATE(A,KBCF), GPIO_TRISTATE(A,KBCF), GPIO_TRISTATE(A,KBCF), GPIO_TRISTATE(A,KBCF), + GPIO_TRISTATE(A,PMC), GPIO_TRISTATE(A,PMC), GPIO_TRISTATE(A,I2CP), GPIO_TRISTATE(A,I2CP), + // Port R + GPIO_TRISTATE(A,KBCA), GPIO_TRISTATE(A,KBCA), GPIO_TRISTATE(A,KBCA), GPIO_TRISTATE(A,KBCE), + GPIO_TRISTATE(D,KBCD), GPIO_TRISTATE(D,KBCD), GPIO_TRISTATE(D,KBCD), GPIO_TRISTATE(A,KBCB), + // Port S + GPIO_TRISTATE(A,KBCB), GPIO_TRISTATE(A,KBCB), GPIO_TRISTATE(A,KBCB), GPIO_TRISTATE(A,KBCB), + GPIO_TRISTATE(A,KBCB), GPIO_TRISTATE(B,KBCC), GPIO_TRISTATE(B,KBCC), GPIO_TRISTATE(B,KBCC), + // Port T + GPIO_TRISTATE(A,DTD), GPIO_TRISTATE(A,CSUS), GPIO_TRISTATE(A,DTB), GPIO_TRISTATE(A,DTB), + GPIO_TRISTATE(A,PTA), GPIO_TRISTATE(A,PTA), GPIO_TRISTATE(A,PTA), GPIO_TRISTATE(A,PTA), + // Port U + GPIO_TRISTATE(A,GPU), GPIO_TRISTATE(A,GPU), GPIO_TRISTATE(A,GPU), GPIO_TRISTATE(A,GPU), + GPIO_TRISTATE(A,GPU), GPIO_TRISTATE(A,GPU), GPIO_TRISTATE(A,GPU), GPIO_TRISTATE(D,GPU7), + // Port V + GPIO_TRISTATE(B,UAC), GPIO_TRISTATE(B,UAC), GPIO_TRISTATE(B,UAC), GPIO_TRISTATE(B,UAC), + GPIO_TRISTATE(A,GPV), GPIO_TRISTATE(A,GPV), GPIO_TRISTATE(A,GPV), GPIO_TRISTATE(C,LVP0), + // Port W + GPIO_TRISTATE(C,LM0), GPIO_TRISTATE(C,LM1), GPIO_TRISTATE(B,SPIG), GPIO_TRISTATE(B,SPIH), + GPIO_TRISTATE(A,CDEV1), GPIO_TRISTATE(A,CDEV2), GPIO_TRISTATE(B,UCA), GPIO_TRISTATE(B,UCA), + // Port X + GPIO_TRISTATE(B,SPIA), GPIO_TRISTATE(B,SPIB), GPIO_TRISTATE(B,SPIC), GPIO_TRISTATE(B,SPIC), + GPIO_TRISTATE(B,SPID), GPIO_TRISTATE(B,SPIE), GPIO_TRISTATE(B,SPIE), GPIO_TRISTATE(B,SPIF) +}; + +NvBool +NvRmAp15GetPinGroupForGpio(NvRmDeviceHandle hDevice, + NvU32 Port, + NvU32 Pin, + NvU32 *pMapping) +{ + const NvU32 GpiosPerPort = 8; + NvU32 Index = Port*GpiosPerPort + Pin; + + if ((Pin >= GpiosPerPort) || (Index >= NV_ARRAY_SIZE(g_Ap15GpioPadGroupMapping))) + return NV_FALSE; + + *pMapping = (NvU32)g_Ap15GpioPadGroupMapping[Index]; + return NV_TRUE; +} + +// Top level AP15 clock enable register control macro +#define CLOCK_ENABLE( rm, offset, field, EnableState ) \ + do { \ + regaddr = (CLK_RST_CONTROLLER_##offset##_0); \ + NvOsMutexLock((rm)->CarMutex); \ + reg = NV_REGR((rm), NvRmPrivModuleID_ClockAndReset, 0, regaddr); \ + reg = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, offset, field, EnableState, reg); \ + NV_REGW((rm), NvRmPrivModuleID_ClockAndReset, 0, regaddr, reg); \ + NvOsMutexUnlock((rm)->CarMutex); \ + } while( 0 ) + +void NvRmPrivAp15EnableExternalClockSource( + NvRmDeviceHandle hDevice, + const NvU32* Instance, + NvU32 Config, + NvBool ClockState) +{ + NvU32 MuxCtlShift, MuxCtlSet; + NvU32 reg; + NvU32 regaddr; + + MuxCtlShift = NV_DRF_VAL(MUX,ENTRY, MUX_CTL_SHIFT, *Instance); + MuxCtlSet = NV_DRF_VAL(MUX,ENTRY, MUX_CTL_SET, *Instance); + + if (MuxCtlShift == APB_MISC_PP_PIN_MUX_CTL_C_0_CDEV1_SEL_SHIFT) + { + if (MuxCtlSet == APB_MISC_PP_PIN_MUX_CTL_C_0_CDEV1_SEL_PLLA_OUT) + { + NvRmPrivExternalClockAttach( + hDevice, NvRmClockSource_PllA0, ClockState); + } + CLOCK_ENABLE(hDevice, MISC_CLK_ENB, CLK_ENB_DEV1_OUT, ClockState); + } + else if (MuxCtlShift == APB_MISC_PP_PIN_MUX_CTL_C_0_CDEV2_SEL_SHIFT) + { + CLOCK_ENABLE(hDevice, MISC_CLK_ENB, CLK_ENB_DEV2_OUT, ClockState); + } + else if (MuxCtlShift == APB_MISC_PP_PIN_MUX_CTL_C_0_CSUS_SEL_SHIFT) + { + CLOCK_ENABLE(hDevice, MISC_CLK_ENB, CLK_ENB_SUS_OUT, ClockState); + } +} + +NvU32 +NvRmPrivAp15GetExternalClockSourceFreq( + NvRmDeviceHandle hDevice, + const NvU32* Instance, + NvU32 Config) +{ + NvU32 MuxCtlShift, MuxCtlSet; + NvU32 ClockFreqInKHz = 0; + + MuxCtlShift = NV_DRF_VAL(MUX,ENTRY, MUX_CTL_SHIFT, *Instance); + MuxCtlSet = NV_DRF_VAL(MUX,ENTRY, MUX_CTL_SET, *Instance); + + if (MuxCtlShift == APB_MISC_PP_PIN_MUX_CTL_C_0_CDEV1_SEL_SHIFT) + { + if (MuxCtlSet == APB_MISC_PP_PIN_MUX_CTL_C_0_CDEV1_SEL_PLLA_OUT) + ClockFreqInKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllA0); + + else if (MuxCtlSet == APB_MISC_PP_PIN_MUX_CTL_C_0_CDEV1_SEL_OSC) + ClockFreqInKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_ClkM); + } + else if (MuxCtlShift == APB_MISC_PP_PIN_MUX_CTL_C_0_CDEV2_SEL_SHIFT) + { + if (MuxCtlSet == APB_MISC_PP_PIN_MUX_CTL_C_0_CDEV2_SEL_AHB_CLK) + ClockFreqInKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_Ahb); + + else if (MuxCtlSet == APB_MISC_PP_PIN_MUX_CTL_C_0_CDEV2_SEL_OSC) + ClockFreqInKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_ClkM); + } + else if (MuxCtlShift == APB_MISC_PP_PIN_MUX_CTL_C_0_CSUS_SEL_SHIFT) + { + if (MuxCtlSet == APB_MISC_PP_PIN_MUX_CTL_C_0_CSUS_SEL_VI_SENSOR_CLK) + { + if (NvRmPowerModuleClockConfig(hDevice, NvRmModuleID_Vi, 0, 0, 0, + NULL, 0, &ClockFreqInKHz, NvRmClockConfig_SubConfig) != NvSuccess) + { + ClockFreqInKHz = 0; + } + } + } + return ClockFreqInKHz; +} + +/* These functions will map from the RM's internal definition of module + * instances to the ODM definition. Since the RM is controller-centric, + * and the ODM pin mux query is interface-centric, the mapping is not + * always one-to-one */ + +NvBool NvRmPrivAp15RmModuleToOdmModule( + NvRmModuleID RmModule, + NvOdmIoModule *OdmModule, + NvU32 *OdmInstance, + NvU32 *pCnt) +{ + NvRmModuleID Module = NVRM_MODULE_ID_MODULE(RmModule); + + switch (Module) + { + case NvRmPrivModuleID_Mio_Exio: + *OdmModule = NvOdmIoModule_Mio; + *OdmInstance = 0; // since there is only one MIO bus on AP15/AP16. + *pCnt = 1; + return NV_TRUE; + default: + break; + } + + return NV_FALSE; +} + +NvError +NvRmPrivAp15GetModuleInterfaceCaps( + NvOdmIoModule Module, + NvU32 Instance, + NvU32 PinMap, + void *pCaps) +{ + NvError err = NvError_NotSupported; + + switch (Module) + { + case NvOdmIoModule_Sdio: + { + NvRmModuleSdmmcInterfaceCaps *pSdmmcCaps = + (NvRmModuleSdmmcInterfaceCaps *)pCaps; + if (Instance==0 && + (PinMap == NvOdmSdioPinMap_Config2 || + PinMap == NvOdmSdioPinMap_Config5)) + pSdmmcCaps->MmcInterfaceWidth = 8; + else if (Instance==1 && PinMap==NvOdmSdioPinMap_Config1) + pSdmmcCaps->MmcInterfaceWidth = 8; + else + pSdmmcCaps->MmcInterfaceWidth = 4; + err = NvSuccess; + break; + } + case NvOdmIoModule_Hsmmc: + { + NvRmModuleSdmmcInterfaceCaps *pSdmmcCaps = + (NvRmModuleSdmmcInterfaceCaps *)pCaps; + if (Instance==0 && PinMap==NvOdmHsmmcPinMap_Config2) + pSdmmcCaps->MmcInterfaceWidth = 4; + else + pSdmmcCaps->MmcInterfaceWidth = 8; + err = NvSuccess; + break; + } + case NvOdmIoModule_Pwm: + { + NvRmModulePwmInterfaceCaps *pPwmCaps = + (NvRmModulePwmInterfaceCaps *)pCaps; + err = NvSuccess; + if (Instance == 0 && (PinMap == NvOdmPwmPinMap_Config1)) + pPwmCaps->PwmOutputIdSupported = 15; + else if (Instance == 0 && (PinMap == NvOdmPwmPinMap_Config2)) + pPwmCaps->PwmOutputIdSupported = 13; + else if (Instance == 0 && (PinMap == NvOdmPwmPinMap_Config3)) + pPwmCaps->PwmOutputIdSupported = 1; + else if (Instance == 0 && (PinMap == NvOdmPwmPinMap_Config4)) + pPwmCaps->PwmOutputIdSupported = 12; + else + { + pPwmCaps->PwmOutputIdSupported = 0; + err = NvError_NotSupported; + } + break; + } + case NvOdmIoModule_Nand: + { + NvRmModuleNandInterfaceCaps *pNandCaps = + (NvRmModuleNandInterfaceCaps *)pCaps; + if (Instance == 0) + { + pNandCaps->IsCombRbsyMode = NV_TRUE; + pNandCaps->NandInterfaceWidth = 8; + + if (PinMap == NvOdmNandPinMap_Config4) + pNandCaps->IsCombRbsyMode = NV_FALSE; + + if ((PinMap == NvOdmNandPinMap_Config1) || + (PinMap == NvOdmNandPinMap_Config2)) + pNandCaps->NandInterfaceWidth = 16; + + err = NvSuccess; + } + else + { + NV_ASSERT(NV_FALSE); + err = NvError_NotSupported; + } + break; + } + case NvOdmIoModule_Uart: + { + NvRmModuleUartInterfaceCaps *pUartCaps = + (NvRmModuleUartInterfaceCaps *)pCaps; + err = NvSuccess; + if (Instance == 0) + { + if (PinMap == NvOdmUartPinMap_Config1) + pUartCaps->NumberOfInterfaceLines = 8; + else if (PinMap == NvOdmUartPinMap_Config3) + pUartCaps->NumberOfInterfaceLines = 7; + else if ((PinMap == NvOdmUartPinMap_Config2) || + (PinMap == NvOdmUartPinMap_Config4)) + pUartCaps->NumberOfInterfaceLines = 4; + else if ((PinMap == NvOdmUartPinMap_Config5) || + (PinMap == NvOdmUartPinMap_Config6)) + pUartCaps->NumberOfInterfaceLines = 2; + else + pUartCaps->NumberOfInterfaceLines = 0; + } + else if (Instance == 1) + { + if ((PinMap == NvOdmUartPinMap_Config1) || + (PinMap == NvOdmUartPinMap_Config2)) + pUartCaps->NumberOfInterfaceLines = 4; + else if (PinMap == NvOdmUartPinMap_Config3) + pUartCaps->NumberOfInterfaceLines = 2; + else + pUartCaps->NumberOfInterfaceLines = 0; + } + else if (Instance == 2) + { + if (PinMap == NvOdmUartPinMap_Config1) + pUartCaps->NumberOfInterfaceLines = 4; + else if (PinMap == NvOdmUartPinMap_Config2) + pUartCaps->NumberOfInterfaceLines = 2; + else + pUartCaps->NumberOfInterfaceLines = 0; + } + else + { + NV_ASSERT(NV_FALSE); + err = NvError_NotSupported; + } + break; + } + default: + break; + } + return err; +} + +NvError +NvRmAp15GetStraps( + NvRmDeviceHandle hDevice, + NvRmStrapGroup StrapGroup, + NvU32* pStrapValue) +{ + NvU32 reg = NV_REGR( + hDevice, NvRmModuleID_Misc, 0, APB_MISC_PP_STRAPPING_OPT_A_0); + + switch (StrapGroup) + { + case NvRmStrapGroup_RamCode: + reg = NV_DRF_VAL(APB_MISC_PP, STRAPPING_OPT_A, RAM_CODE, reg); + break; + default: + return NvError_NotSupported; + } + *pStrapValue = reg; + return NvSuccess; +} + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux_utils.h b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux_utils.h new file mode 100644 index 000000000000..f9fd782a3315 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux_utils.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2009-2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef AP15RM_PINMUX_UTILS_H +#define AP15RM_PINMUX_UTILS_H + +/* + * ap15rm_pinmux_utils.h defines the pinmux macros to implement for the resource + * manager. + */ + +#include "nvrm_pinmux_utils.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* When the state is BranchLink, this is the number of words to increment + * the current "PC" + */ +#define MUX_ENTRY_0_BRANCH_ADDRESS_RANGE 31:2 +// The incr1 offset from TRISTATE_REG_A_0 to the pad group's tristate register +#define MUX_ENTRY_0_TS_OFFSET_RANGE 31:26 +// The bit position within the tristate register for the pad group +#define MUX_ENTRY_0_TS_SHIFT_RANGE 25:21 +// The incr1 offset from PIN_MUX_CTL_A_0 to the pad group's pin mux control register +#define MUX_ENTRY_0_MUX_CTL_OFFSET_RANGE 20:17 +// The bit position within the pin mux control register for the pad group +#define MUX_ENTRY_0_MUX_CTL_SHIFT_RANGE 16:12 +// The mask for the pad group -- expanded to 3b for forward-compatibility +#define MUX_ENTRY_0_MUX_CTL_MASK_RANGE 10:8 +// When a pad group needs to be owned (or disowned), this value is applied +#define MUX_ENTRY_0_MUX_CTL_SET_RANGE 7:5 +// This value is compared against, to determine if the pad group should be disowned +#define MUX_ENTRY_0_MUX_CTL_UNSET_RANGE 4:2 +// for extended opcodes, this field is set with the extended opcode +#define MUX_ENTRY_0_OPCODE_EXTENSION_RANGE 3:2 +// The state for this entry +#define MUX_ENTRY_0_STATE_RANGE 1:0 + +/* This macro is used to generate 32b value to program the tristate& pad mux control + * registers for config/unconfig for a padgroup + */ +#define PIN_MUX_ENTRY(TSOFF,TSSHIFT,MUXOFF,MUXSHIFT,MUXMASK,MUXSET,MUXUNSET,STAT) \ + (NV_DRF_NUM(MUX, ENTRY, TS_OFFSET, TSOFF) | NV_DRF_NUM(MUX, ENTRY, TS_SHIFT, TSSHIFT) | \ + NV_DRF_NUM(MUX, ENTRY, MUX_CTL_OFFSET, MUXOFF) | NV_DRF_NUM(MUX, ENTRY, MUX_CTL_SHIFT, MUXSHIFT) | \ + NV_DRF_NUM(MUX, ENTRY,MUX_CTL_MASK, MUXMASK) | NV_DRF_NUM(MUX, ENTRY,MUX_CTL_SET, MUXSET) | \ + NV_DRF_NUM(MUX, ENTRY, MUX_CTL_UNSET,MUXUNSET) | NV_DRF_NUM(MUX, ENTRY, STATE,STAT)) + +// This is used to program the tristate & pad mux control registers for a pad group +#define CONFIG_VAL(TRISTATE_REG, MUXCTL_REG, PADGROUP, MUX) \ + (PIN_MUX_ENTRY(((APB_MISC_PP_TRISTATE_REG_##TRISTATE_REG##_0 - APB_MISC_PP_TRISTATE_REG_A_0)>>2), \ + APB_MISC_PP_TRISTATE_REG_##TRISTATE_REG##_0_Z_##PADGROUP##_SHIFT, \ + ((APB_MISC_PP_PIN_MUX_CTL_##MUXCTL_REG##_0 - APB_MISC_PP_PIN_MUX_CTL_A_0) >> 2), \ + APB_MISC_PP_PIN_MUX_CTL_##MUXCTL_REG##_0_##PADGROUP##_SEL_SHIFT, \ + APB_MISC_PP_PIN_MUX_CTL_##MUXCTL_REG##_0_##PADGROUP##_SEL_DEFAULT_MASK, \ + APB_MISC_PP_PIN_MUX_CTL_##MUXCTL_REG##_0_##PADGROUP##_SEL_##MUX, \ + 0, PinMuxConfig_Set)) + +/* This macro is used to compare a pad group against a potentially conflicting + * enum (where the conflict is caused by setting a new config), and to resolve + * the conflict by setting the conflicting pad group to a different, + * non-conflicting option. Read this as: if (PADGROUP) is equal to + * (CONFLICTMUX), replace it with (RESOLUTIONMUX) + */ +#define UNCONFIG_VAL(MUXCTL_REG, PADGROUP, CONFLICTMUX, RESOLUTIONMUX) \ + (PIN_MUX_ENTRY(0, 0, \ + ((APB_MISC_PP_PIN_MUX_CTL_##MUXCTL_REG##_0 - APB_MISC_PP_PIN_MUX_CTL_A_0) >> 2), \ + APB_MISC_PP_PIN_MUX_CTL_##MUXCTL_REG##_0_##PADGROUP##_SEL_SHIFT, \ + APB_MISC_PP_PIN_MUX_CTL_##MUXCTL_REG##_0_##PADGROUP##_SEL_DEFAULT_MASK, \ + APB_MISC_PP_PIN_MUX_CTL_##MUXCTL_REG##_0_##PADGROUP##_SEL_##RESOLUTIONMUX, \ + APB_MISC_PP_PIN_MUX_CTL_##MUXCTL_REG##_0_##PADGROUP##_SEL_##CONFLICTMUX, \ + PinMuxConfig_Unset)) +// TODO: Need to implement in PINMUX_DEBUG_MODE +#define TRISTATE_UNUSED(PADGROUP, TRISTATE_REG) \ + (PIN_MUX_ENTRY(((APB_MISC_PP_TRISTATE_REG_##TRISTATE_REG##_0 - APB_MISC_PP_TRISTATE_REG_A_0)>>2), \ + APB_MISC_PP_TRISTATE_REG_##TRISTATE_REG##_0_Z_##PADGROUP##_SHIFT, \ + 0, 0, 0, 0, 0, -1)) + + +#if NVRM_PINMUX_DEBUG_FLAG +#define CONFIG(TRISTATE_REG, MUXCTL_REG, PADGROUP, MUX) \ + (CONFIG_VAL(TRISTATE_REG, MUXCTL_REG, PADGROUP, MUX)), \ + (NvU32)(const void*)(#MUXCTL_REG "_0_" #PADGROUP "_SEL to " #MUX), \ + (NvU32)(const void*)(#TRISTATE_REG "_0_Z_" #PADGROUP) + +#define UNCONFIG(MUXCTL_REG, PADGROUP, CONFLICTMUX, RESOLUTIONMUX) \ + (UNCONFIG_VAL(MUXCTL_REG, PADGROUP, CONFLICTMUX, RESOLUTIONMUX)), \ + (NvU32)(const void*)(#MUXCTL_REG "_0_" #PADGROUP "_SEL from " #CONFLICTMUX " to " #RESOLUTIONMUX), \ + (NvU32)(const void*)(NULL) +#else +#define CONFIG(TRISTATE_REG, MUXCTL_REG, PADGROUP, MUX) \ + (CONFIG_VAL(TRISTATE_REG, MUXCTL_REG, PADGROUP, MUX)) +#define UNCONFIG(MUXCTL_REG, PADGROUP, CONFLICTMUX, RESOLUTIONMUX) \ + (UNCONFIG_VAL(MUXCTL_REG, PADGROUP, CONFLICTMUX, RESOLUTIONMUX)) +#endif + +// The below entries define the table format for GPIO Port/Pin-to-Tristate register mappings +// Each table entry is 16b, and one is stored for every GPIO Port/Pin on the chip +#define MUX_GPIOMAP_0_TS_OFFSET_RANGE 15:10 +// Defines where in the 32b register the tristate control is located +#define MUX_GPIOMAP_0_TS_SHIFT_RANGE 4:0 + +#define TRISTATE_ENTRY(TSOFFS, TSSHIFT) \ + ((NvU16)(NV_DRF_NUM(MUX,GPIOMAP,TS_OFFSET,(TSOFFS)) | \ + NV_DRF_NUM(MUX,GPIOMAP,TS_SHIFT,(TSSHIFT)))) + +#define GPIO_TRISTATE(TRIREG,PADGROUP) \ + (TRISTATE_ENTRY(((APB_MISC_PP_TRISTATE_REG_##TRIREG##_0 - APB_MISC_PP_TRISTATE_REG_A_0)>>2), \ + APB_MISC_PP_TRISTATE_REG_##TRIREG##_0_Z_##PADGROUP##_SHIFT)) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif // AP15RM_PINMUX_UTILS_H + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pmc_scratch_map.h b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pmc_scratch_map.h new file mode 100644 index 000000000000..38cae693e547 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pmc_scratch_map.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief nVIDIA Driver Development Kit: + * Power Management Controller (PMC) scratch registers fields + * definitions + * + * @b Description: Defines SW-allocated fields in the PMC scratch registers + * shared by boot and power management code in RM and OAL. + * + */ + + +#ifndef INCLUDED_AP15RM_PMC_SCRATCH_MAP_H +#define INCLUDED_AP15RM_PMC_SCRATCH_MAP_H + +/* + * Scratch registers offsets are part of the HW specification in the below + * include file. Scratch registers fields are defined in this header via + * bit ranges compatible with nvrm_drf macros. + */ +#include "ap15/arapbpm.h" + +// Register APBDEV_PMC_SCRATCH0_0 (this is the only scratch register cleared on reset) +// + +// RM clients combined power state (bits 4-7) +#define APBDEV_PMC_SCRATCH0_0_RM_PWR_STATE_RANGE 11:8 +#define APBDEV_PMC_SCRATCH0_0_RM_LOAD_TRANSPORT_RANGE 15:12 +#define APBDEV_PMC_SCRATCH0_0_RM_DFS_FLAG_RANGE 27:16 +#define APBDEV_PMC_SCRATCH0_0_UPDATE_MODE_FLAG_RANGE 29:28 +#define APBDEV_PMC_SCRATCH0_0_OAL_RTC_INIT_RANGE 30:30 +#define APBDEV_PMC_SCRATCH0_0_RST_PWR_DET_RANGE 31:31 + +// Register APBDEV_PMC_SCRATCH20_0, used to store the ODM customer data from the BCT +#define APBDEV_PMC_SCRATCH20_0_BCT_ODM_DATA_RANGE 31:0 + +// Register APBDEV_PMC_SCRATCH21_0 +// +#define APBDEV_PMC_SCRATCH21_0_LP2_TIME_US 31:0 + +#endif // INCLUDED_AP15RM_PMC_SCRATCH_MAP_H diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c new file mode 100644 index 000000000000..d9fd9f3d1251 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c @@ -0,0 +1,663 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief nVIDIA Driver Development Kit: + * Power Resource manager + * + * @b Description: Implements the interface of the NvRM Power. + * + */ + +#include "nvrm_power_private.h" +#include "nvrm_pmu.h" +#include "nvrm_pmu_private.h" +#include "nvassert.h" +#include "nvrm_drf.h" +#include "nvrm_hwintf.h" +#include "nvodm_query_discovery.h" +#include "ap15rm_private.h" +#include "ap15rm_clocks.h" +#include "ap15/arapbpm.h" +#include "ap15/project_relocation_table.h" + +// TODO: Always Disable before check-in +// Module debug: 0=disable, 1=enable +#define NVRM_ENABLE_PRINTF (0) + +#if (NV_DEBUG && NVRM_ENABLE_PRINTF) +#define NVRM_POWER_PRINTF(x) NvOsDebugPrintf x +#else +#define NVRM_POWER_PRINTF(x) +#endif + + +#if !NV_OAL +/*****************************************************************************/ +/*****************************************************************************/ + +#define NV_POWER_GATE_TD (1) +#define NV_POWER_GATE_PCIE (1) +// TODO: check VDE/BSEV/NSEA voltage control calls before enabling +#define NV_POWER_GATE_VDE (0) +// TODO: check MPE voltage control calls before enabling +#define NV_POWER_GATE_MPE (0) + +// Power Group -to- Power Gating Ids mapping +static const NvU32* s_PowerGroupIds = NULL; +static NvBool s_UngateOnResume[NV_POWERGROUP_MAX] = {0}; + +/*****************************************************************************/ + +static NvBool IsRunTimePowerGateSupported(NvU32 PowerGroup) +{ + // 1st check h/w support capabilities + NV_ASSERT(s_PowerGroupIds); + if (s_PowerGroupIds[PowerGroup] == NV_POWERGROUP_INVALID) + return NV_FALSE; + + // now check s/w support + switch (PowerGroup) + { + case NV_POWERGROUP_TD: + return NV_POWER_GATE_TD; + case NV_POWERGROUP_PCIE: + return NV_POWER_GATE_PCIE; + case NV_POWERGROUP_VDE: + return NV_POWER_GATE_VDE; + case NV_POWERGROUP_MPE: + return NV_POWER_GATE_MPE; + default: + return NV_FALSE; + } +} + +static NvBool IsSuspendPowerGateForced(NvU32 PowerGroup) +{ + // 1st check h/w support capabilities + NV_ASSERT(s_PowerGroupIds); + if (s_PowerGroupIds[PowerGroup] == NV_POWERGROUP_INVALID) + return NV_FALSE; + + // now check s/w support + switch (PowerGroup) + { + case NV_POWERGROUP_TD: + case NV_POWERGROUP_PCIE: + case NV_POWERGROUP_VDE: + case NV_POWERGROUP_VE: + case NV_POWERGROUP_MPE: + return NV_TRUE; + default: + return NV_FALSE; + } +} + +static void PowerGroupResetControl( + NvRmDeviceHandle hRmDeviceHandle, + NvU32 PowerGroup, + NvBool Assert) +{ + switch (PowerGroup) + { + case NV_POWERGROUP_TD: + NvRmModuleResetWithHold(hRmDeviceHandle, NvRmModuleID_3D, Assert); + break; + case NV_POWERGROUP_PCIE: + NvRmModuleResetWithHold( + hRmDeviceHandle, NvRmPrivModuleID_Pcie, Assert); + NvRmModuleResetWithHold( + hRmDeviceHandle, NvRmPrivModuleID_Afi, Assert); + if (Assert) // Keep PCIEXCLK in reset - let driver to take it out + { + NvRmModuleResetWithHold( + hRmDeviceHandle, NvRmPrivModuleID_PcieXclk, Assert); + } + NvRmPrivAp20PowerPcieXclkControl(hRmDeviceHandle, !Assert); + break; + case NV_POWERGROUP_VDE: + NvRmModuleResetWithHold(hRmDeviceHandle, NvRmModuleID_Vde, Assert); + break; + case NV_POWERGROUP_VE: + NvRmModuleResetWithHold(hRmDeviceHandle, NvRmModuleID_Vi, Assert); + NvRmModuleResetWithHold(hRmDeviceHandle, NvRmModuleID_Csi, Assert); + NvRmModuleResetWithHold(hRmDeviceHandle, NvRmModuleID_Isp, Assert); + NvRmModuleResetWithHold(hRmDeviceHandle, NvRmModuleID_Epp, Assert); + break; + case NV_POWERGROUP_MPE: + NvRmModuleResetWithHold(hRmDeviceHandle, NvRmModuleID_Mpe, Assert); + break; + default: + break; + } +} + +static void PowerGroupClockControl( + NvRmDeviceHandle hRmDeviceHandle, + NvU32 PowerGroup, + NvBool Enable) +{ + ModuleClockState ClockState = + Enable ? ModuleClockState_Enable : ModuleClockState_Disable; + + switch (PowerGroup) + { + case NV_POWERGROUP_TD: + NvRmPrivEnableModuleClock( + hRmDeviceHandle, NvRmModuleID_3D, ClockState); + break; + case NV_POWERGROUP_PCIE: + NvRmPrivEnableModuleClock( + hRmDeviceHandle, NvRmPrivModuleID_Pcie, ClockState); + break; + case NV_POWERGROUP_VDE: + NvRmPrivEnableModuleClock( + hRmDeviceHandle, NvRmModuleID_Vde, ClockState); + break; + case NV_POWERGROUP_VE: + NvRmPrivEnableModuleClock( + hRmDeviceHandle, NvRmModuleID_Vi, ClockState); + NvRmPrivEnableModuleClock( + hRmDeviceHandle, NvRmModuleID_Csi, ClockState); + NvRmPrivEnableModuleClock( + hRmDeviceHandle, NvRmModuleID_Isp, ClockState); + NvRmPrivEnableModuleClock( + hRmDeviceHandle, NvRmModuleID_Epp, ClockState); + break; + case NV_POWERGROUP_MPE: + NvRmPrivEnableModuleClock( + hRmDeviceHandle, NvRmModuleID_Mpe, ClockState); + break; + default: + break; + } +} + +static void +PowerGroupPowerControl( + NvRmDeviceHandle hRmDeviceHandle, + NvU32 PowerGroup, + NvBool Enable) +{ + NvU32 reg, Id, Mask, Status; + + // Do nothing if not SoC platform + NV_ASSERT(hRmDeviceHandle); + if (NvRmPrivGetExecPlatform(hRmDeviceHandle) != ExecPlatform_Soc) + return; + + // Do nothing if power group is already in requested state + NV_ASSERT(s_PowerGroupIds[PowerGroup] != NV_POWERGROUP_INVALID); + Id = s_PowerGroupIds[PowerGroup]; + Mask = (0x1 << Id); + Status = Mask & NV_REGR(hRmDeviceHandle, NvRmModuleID_Pmif, 0, + APBDEV_PMC_PWRGATE_STATUS_0); + if (Enable == (Status != 0x0)) + return; + + /* + * Gating procedure: + * - assert resets to all modules in power group + * - toggle power gate + * + * Ungating procedure + * - assert resets to all modules in power group (redundunt) + * - toggle power gate + * - enable clocks to all modules in power group + * - reset propagation delay + * - remove clamping + * - de-assert reset to all modules in power group + * - disable clocks to all modules in power group + * + * Special note on toggle timers( shared with OAL which does CPU power + * gating): per convention with OAL default settings are never changed. + */ + PowerGroupResetControl(hRmDeviceHandle, PowerGroup, NV_TRUE); + + reg = NV_DRF_DEF(APBDEV_PMC, PWRGATE_TOGGLE, START, ENABLE) | Id; + NV_REGW(hRmDeviceHandle, NvRmModuleID_Pmif, 0, + APBDEV_PMC_PWRGATE_TOGGLE_0, reg); + for (;;) + { + reg = NV_REGR(hRmDeviceHandle, NvRmModuleID_Pmif, 0, + APBDEV_PMC_PWRGATE_STATUS_0); + if (Status != (reg & Mask)) + break; + } + if (Enable) + { + PowerGroupClockControl(hRmDeviceHandle, PowerGroup, NV_TRUE); + NvOsWaitUS(NVRM_RESET_DELAY); + + // PCIE and VDE clamping masks are swapped relatively to + // partition Ids (bug 602975) + if (PowerGroup == NV_POWERGROUP_PCIE) + Mask = 0x1 << s_PowerGroupIds[NV_POWERGROUP_VDE]; + else if (PowerGroup == NV_POWERGROUP_VDE) + Mask = 0x1 << s_PowerGroupIds[NV_POWERGROUP_PCIE]; + + NV_REGW(hRmDeviceHandle, NvRmModuleID_Pmif, 0, + APBDEV_PMC_REMOVE_CLAMPING_CMD_0, Mask); + for (;;) + { + reg = NV_REGR(hRmDeviceHandle, NvRmModuleID_Pmif, 0, + APBDEV_PMC_REMOVE_CLAMPING_CMD_0); + if (reg == 0) + break; + } + PowerGroupResetControl(hRmDeviceHandle, PowerGroup, NV_FALSE); + PowerGroupClockControl(hRmDeviceHandle, PowerGroup, NV_FALSE); + } +} + +void +NvRmPrivPowerGroupControl( + NvRmDeviceHandle hRmDeviceHandle, + NvU32 PowerGroup, + NvBool Enable) +{ + NVRM_POWER_PRINTF(("%s Power Group %d\n", + (Enable ? "Enable" : "Disable"), PowerGroup)); + + // Do nothing if dynamic power gating is not supported for this group + if (PowerGroup >= NV_POWERGROUP_MAX) + return; // "virtual" groups are always On + if (!IsRunTimePowerGateSupported(PowerGroup)) + return; + + // Gate/ungate the group + PowerGroupPowerControl(hRmDeviceHandle, PowerGroup, Enable); +} + +NvRmMilliVolts +NvRmPrivPowerGroupGetVoltage( + NvRmDeviceHandle hRmDeviceHandle, + NvU32 PowerGroup) +{ + NvRmMilliVolts Voltage = NvRmVoltsUnspecified; + if (PowerGroup >= NV_POWERGROUP_MAX) + return Voltage; // "virtual" groups are always On + + // Do not check non-gated power group - it is On by definition + if (s_PowerGroupIds[PowerGroup] != NV_POWERGROUP_INVALID) + { + NvU32 reg = NV_REGR( + hRmDeviceHandle, NvRmModuleID_Pmif, 0, APBDEV_PMC_PWRGATE_STATUS_0); + if ((reg & (0x1 << s_PowerGroupIds[PowerGroup])) == 0x0) + { + // Specified power group is gated + Voltage = NvRmVoltsOff; + } + } + return Voltage; +} + +void NvRmPrivPowerGroupSuspend(NvRmDeviceHandle hRmDeviceHandle) +{ + NvU32 i; + + // On suspend entry power gate core group that is still On, but must be Off + for (i = 0; i < NV_POWERGROUP_MAX; i++) + { + if (!IsSuspendPowerGateForced(i) || + (NvRmPrivPowerGroupGetVoltage(hRmDeviceHandle, i) == NvRmVoltsOff)) + { + s_UngateOnResume[i] = NV_FALSE; + continue; + } + + s_UngateOnResume[i] = NV_TRUE; + PowerGroupPowerControl(hRmDeviceHandle, i, NV_FALSE); + } +} + +void NvRmPrivPowerGroupResume(NvRmDeviceHandle hRmDeviceHandle) +{ + NvU32 i; + + // On resume ungate core group that was forcefully gated on suspend entry + for (i = 0; i < NV_POWERGROUP_MAX; i++) + { + if (s_UngateOnResume[i] == NV_TRUE) + PowerGroupPowerControl(hRmDeviceHandle, i, NV_TRUE); + } +} + +void NvRmPrivPowerGroupControlInit(NvRmDeviceHandle hRmDeviceHandle) +{ + NvU32 i, Size; + + // Init chip specific power group map + if ((hRmDeviceHandle->ChipId.Id == 0x15) || + (hRmDeviceHandle->ChipId.Id == 0x16)) + { + NvRmPrivAp15PowerGroupTableInit(&s_PowerGroupIds, &Size); + } + else if (hRmDeviceHandle->ChipId.Id == 0x20) + { + NvRmPrivAp20PowerGroupTableInit(&s_PowerGroupIds, &Size); + } + else + { + NV_ASSERT(!"Unsupported chip ID"); + } + NV_ASSERT(Size == NV_POWERGROUP_MAX); + + // Power gate supported partitions + for (i = 0; i < NV_POWERGROUP_MAX; i++) + NvRmPrivPowerGroupControl(hRmDeviceHandle, i, NV_FALSE); +} + +#endif // !NV_OAL +/*****************************************************************************/ +/*****************************************************************************/ + +#define NV_NO_IOPOWER_CONTROL (1) +#define NV_RAIL_ADDR_INVALID ((NvU32)-1) + +typedef struct IoPowerDetectInfoRec +{ + // SoC Power rail GUID + NvU64 PowerRailId; + + // IO Power rail disable bit mask + NvU32 DisableRailMask; + + // IO Power Detect cell enable bit mask + NvU32 EnablePwrDetMask; + + // PMU Rail Address + NvU32 PmuRailAddress; + +} IoPowerDetectInfo; + +static IoPowerDetectInfo s_IoPowerDetectMap[] = +{ + {NV_VDD_SYS_ODM_ID, (0x1 << 0), (0x1 << 0), 0}, + {NV_VDD_NAND_ODM_ID, (0x1 << 1), (0x1 << 1), 0}, + {NV_VDD_UART_ODM_ID, (0x1 << 2), (0x1 << 2), 0}, + {NV_VDD_BB_ODM_ID, (0x1 << 3), (0x1 << 3), 0}, + + {NV_VDD_VI_ODM_ID, (0x1 << 4), (0x1 << 4), 0}, + {NV_VDD_AUD_ODM_ID, (0x1 << 5), (0x1 << 5), 0}, + {NV_VDD_LCD_ODM_ID, (0x1 << 6), (0x1 << 6), 0}, + {NV_VDD_DDR_ODM_ID, (0x1 << 7), (0x1 << 7), 0}, + + {NV_VDD_SDIO_ODM_ID, (0x1 << 8), (0x1 << 8), 0}, + {NV_VDD_MIPI_ODM_ID, (0x1 << 9), (0x0), 0} // No detect cell +}; + +static void IoPowerMapRail( + NvU32 PmuRailAddress, + NvU32* pIoPwrDetectMask, + NvU32* pNoIoPwrMask) +{ + NvU32 i; + *pIoPwrDetectMask = 0; + *pNoIoPwrMask = 0; + + // Find all power detect cells and controls on this IO rail + for (i = 0; i < NV_ARRAY_SIZE(s_IoPowerDetectMap); i++) + { + if (s_IoPowerDetectMap[i].PmuRailAddress == PmuRailAddress) + { + *pIoPwrDetectMask |= s_IoPowerDetectMap[i].EnablePwrDetMask; + *pNoIoPwrMask |= s_IoPowerDetectMap[i].DisableRailMask; + } + } +} + +void NvRmPrivIoPowerControlInit(NvRmDeviceHandle hRmDeviceHandle) +{ + NvU32 i, v; + NvU32 NoIoPwrMask = 0; + const NvOdmPeripheralConnectivity* pPmuRail = NULL; + + if (NvRmPrivGetExecPlatform(hRmDeviceHandle) != ExecPlatform_Soc) + { + // Invalidate IO Power detect map if not SoC platform + for (i = 0; i < NV_ARRAY_SIZE(s_IoPowerDetectMap); i++) + s_IoPowerDetectMap[i].PmuRailAddress = NV_RAIL_ADDR_INVALID; + return; + } + + for (i = 0; i < NV_ARRAY_SIZE(s_IoPowerDetectMap); i++) + { + // Fill in PMU rail addresses in IO Power detect map + pPmuRail = NvOdmPeripheralGetGuid(s_IoPowerDetectMap[i].PowerRailId); + NV_ASSERT(pPmuRail && pPmuRail->NumAddress); + s_IoPowerDetectMap[i].PmuRailAddress = pPmuRail->AddressList[0].Address; + + // Find all unpowered rails + v = NvRmPrivPmuRailGetVoltage( + hRmDeviceHandle, s_IoPowerDetectMap[i].PowerRailId); + if (v == ODM_VOLTAGE_OFF) + NoIoPwrMask |= s_IoPowerDetectMap[i].DisableRailMask; + } + + // Latch already powered IO rails + NvRmPrivIoPowerDetectLatch(hRmDeviceHandle); + + // Disable IO pads for unpowered rails + if (NoIoPwrMask) + NvRmPrivIoPowerControl(hRmDeviceHandle, NoIoPwrMask, NV_FALSE); +} + +void NvRmPrivIoPowerDetectStart( + NvRmDeviceHandle hRmDeviceHandle, + NvU32 PwrDetMask) +{ +// (1) Enable specified power detect cell + NV_REGW(hRmDeviceHandle, + NvRmModuleID_Pmif, 0, APBDEV_PMC_PWR_DET_0, PwrDetMask); + +// (2-3) Set power detect latches for enabled cells to safe "1" (high) value + if ((hRmDeviceHandle->ChipId.Id == 0x15) || + (hRmDeviceHandle->ChipId.Id == 0x16)) + { + // On AP15/AP16 set/clear reset bit in PMC scratch0 + NvRmPrivAp15IoPowerDetectReset(hRmDeviceHandle); + + // For AP15 A01 chip the above reset does nothing, therefore + // need to set latch "pass-thru" before transition + if ((hRmDeviceHandle->ChipId.Id == 0x15) && + (hRmDeviceHandle->ChipId.Major == 0x01) && + (hRmDeviceHandle->ChipId.Minor == 0x01)) + { + NvOsWaitUS(NVRM_PWR_DET_DELAY_US); + NV_REGW(hRmDeviceHandle, + NvRmModuleID_Pmif, 0, APBDEV_PMC_PWR_DET_LATCH_0, 1); + } + } + else + { + // On AP20+ reset high values directly + NvRmPrivAp20IoPowerDetectReset(hRmDeviceHandle); + } +} +// +// (4) Power rail OFF -> ON transition and stabilization +// +void NvRmPrivIoPowerDetectLatch(NvRmDeviceHandle hRmDeviceHandle) +{ +// (5) Set latch "pass-thru" +// (6) Latch results +// (7) Disable all power detect cells + NV_REGW(hRmDeviceHandle, + NvRmModuleID_Pmif, 0, APBDEV_PMC_PWR_DET_LATCH_0, 1); + NV_REGW(hRmDeviceHandle, + NvRmModuleID_Pmif, 0, APBDEV_PMC_PWR_DET_LATCH_0, 0); + NV_REGW(hRmDeviceHandle, + NvRmModuleID_Pmif, 0, APBDEV_PMC_PWR_DET_0, 0); +} + +void NvRmPrivIoPowerControl( + NvRmDeviceHandle hRmDeviceHandle, + NvU32 NoIoPwrMask, + NvBool Enable) +{ + NvU32 reg = NV_REGR( + hRmDeviceHandle, NvRmModuleID_Pmif, 0, APBDEV_PMC_NO_IOPOWER_0); + reg = Enable ? (reg & (~NoIoPwrMask)) : (reg | NoIoPwrMask); + +#if NV_NO_IOPOWER_CONTROL + NV_REGW(hRmDeviceHandle, + NvRmModuleID_Pmif, 0, APBDEV_PMC_NO_IOPOWER_0, reg); +#endif +} + +void +NvRmPrivSetSocRailPowerState( + NvRmDeviceHandle hRmDeviceHandle, + NvU32 PmuRailAddress, + NvBool Enable, + NvU32* pIoPwrDetectMask, + NvU32* pNoIoPwrMask) +{ + IoPowerMapRail(PmuRailAddress, pIoPwrDetectMask, pNoIoPwrMask); + if ((*pIoPwrDetectMask == 0) && (*pNoIoPwrMask == 0)) + return; // Exit if not mapped rail + + if (Enable) + { + // On/Off transition: activate power detect cells and keep control + // masks so that the results can be latched and IO pads enabled after + // the transition is completed + if (*pIoPwrDetectMask != 0) + NvRmPrivIoPowerDetectStart(hRmDeviceHandle, *pIoPwrDetectMask); + } + else + { + // Off/On transition: disable IO pads, and clear control masks, + // as no action is required after the transition is completed + if (*pNoIoPwrMask != 0) + NvRmPrivIoPowerControl(hRmDeviceHandle, *pNoIoPwrMask, NV_FALSE); + *pIoPwrDetectMask = *pNoIoPwrMask = 0; + } +} + +/*****************************************************************************/ + +void NvRmPrivCoreVoltageInit(NvRmDeviceHandle hRmDevice) +{ + NvU32 CoreRailAddress, RtcRailAddress, CpuRailAddress; + const NvOdmPeripheralConnectivity* pPmuRail; + NvRmMilliVolts CurrentCoreMv = 0; + NvRmMilliVolts CurrentRtcMv = 0; + NvRmMilliVolts NominalCoreMv = NvRmPrivGetNominalMV(hRmDevice); + + NV_ASSERT(hRmDevice); + + if (NvRmPrivGetExecPlatform(hRmDevice) != ExecPlatform_Soc) + { + return; + } + + pPmuRail = NvOdmPeripheralGetGuid(NV_VDD_CORE_ODM_ID); + NV_ASSERT(pPmuRail); + NV_ASSERT(pPmuRail->NumAddress); + CoreRailAddress = pPmuRail->AddressList[0].Address; + + pPmuRail = NvOdmPeripheralGetGuid(NV_VDD_RTC_ODM_ID); + NV_ASSERT(pPmuRail); + NV_ASSERT(pPmuRail->NumAddress); + RtcRailAddress = pPmuRail->AddressList[0].Address; + + // This function is called during PMU initialization when current (= boot) + // core voltage is expected to be within one safe step from nominal, and + // RTC voltage must be within one safe step from the core. Set nominal + // voltage (bump PMU ref count), if the above conditions are true. + NvRmPmuGetVoltage(hRmDevice, CoreRailAddress, &CurrentCoreMv); + NvRmPmuGetVoltage(hRmDevice, RtcRailAddress, &CurrentRtcMv); + if((CurrentCoreMv > (NominalCoreMv + NVRM_SAFE_VOLTAGE_STEP_MV)) || + ((CurrentCoreMv + NVRM_SAFE_VOLTAGE_STEP_MV) < NominalCoreMv)) + { + NV_ASSERT(!"Unexpected initial core voltage"); + return; + } + if((CurrentRtcMv > (CurrentCoreMv + NVRM_SAFE_VOLTAGE_STEP_MV)) || + ((CurrentRtcMv + NVRM_SAFE_VOLTAGE_STEP_MV) < CurrentCoreMv)) + { + NV_ASSERT(!"Unexpected initial RTC voltage"); + return; + } + // If core voltage is going up, update it before CPU + if (CurrentCoreMv <= NominalCoreMv) + { + NvRmPmuSetVoltage(hRmDevice, RtcRailAddress, NominalCoreMv, NULL); + NvRmPmuSetVoltage(hRmDevice, CoreRailAddress, NominalCoreMv, NULL); + } + + // If the platform has dedicated CPU voltage rail, make sure it is set to + // nominal level as well (bump PMU ref count along the way). + if (NvRmPrivIsCpuRailDedicated(hRmDevice)) + { + NvRmPmuVddRailCapabilities cap; + NvRmMilliVolts NominalCpuMv = NvRmPrivModuleVscaleGetMV( + hRmDevice, NvRmModuleID_Cpu, + NvRmPrivGetSocClockLimits(NvRmModuleID_Cpu)->MaxKHz); + + pPmuRail = NvOdmPeripheralGetGuid(NV_VDD_CPU_ODM_ID); + NV_ASSERT(pPmuRail); + NV_ASSERT(pPmuRail->NumAddress); + CpuRailAddress = pPmuRail->AddressList[0].Address; + + // Clip nominal CPU voltage to minimal PMU capabilities, and set it. + // (note: PMU with CPU voltage range above nominal is temporary + // accepted exception; for other limit violations: PMU maximum level + // for CPU is not high enough, or PMU core range does not include + // nominal core voltage, assert is fired inside NvRmPmuSetVoltage()) + NvRmPmuGetCapabilities(hRmDevice, CpuRailAddress, &cap); + NominalCpuMv = NV_MAX(NominalCpuMv, cap.MinMilliVolts); + NvRmPmuSetVoltage(hRmDevice, CpuRailAddress, NominalCpuMv, NULL); + if (CurrentCoreMv > NominalCoreMv) + NvOsWaitUS(NVRM_CPU_TO_CORE_DOWN_US); // delay if core to go down + } + + // If core voltage is going down, update it after CPU voltage + if (CurrentCoreMv > NominalCoreMv) + { + NvRmPmuSetVoltage(hRmDevice, RtcRailAddress, NominalCoreMv, NULL); + NvRmPmuSetVoltage(hRmDevice, CoreRailAddress, NominalCoreMv, NULL); + } + + // Always On System I/O, DDR IO and RX DDR (if exist) - set nominal, + // bump ref count + NvRmPrivPmuRailControl(hRmDevice, NV_VDD_SYS_ODM_ID, NV_TRUE); + NvRmPrivPmuRailControl(hRmDevice, NV_VDD_DDR_ODM_ID, NV_TRUE); + if (NvOdmPeripheralGetGuid(NV_VDD_DDR_RX_ODM_ID)) + NvRmPrivPmuRailControl(hRmDevice, NV_VDD_DDR_RX_ODM_ID, NV_TRUE); +} + +/*****************************************************************************/ + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.c new file mode 100644 index 000000000000..9f19d6c9a12a --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.c @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief nVIDIA Driver Development Kit: + * Dynamic Frequency Scaling manager + * + * @b Description: Implements NvRM Dynamic Frequency Scaling (DFS) + * manager for SOC-wide clock domains. + * + */ + +#include "nvrm_power_dfs.h" +#include "nvassert.h" +#include "nvrm_drf.h" +#include "nvrm_hwintf.h" +#include "nvrm_pmu.h" +#include "ap15rm_power_dfs.h" +#include "ap15/arstat_mon.h" +#include "ap15/arvde_mon.h" +#include "ap15/aremc.h" +#include "ap15/arclk_rst.h" +#include "ap15/arapb_misc.h" +#include "ap15/artimerus.h" + +/*****************************************************************************/ + +// Regsiter access macros for System Statistic module +#define NV_SYSTAT_REGR(pSystatRegs, reg) \ + NV_READ32((((NvU32)(pSystatRegs)) + STAT_MON_##reg##_0)) +#define NV_SYSTAT_REGW(pSystatRegs, reg, val) \ + NV_WRITE32((((NvU32)(pSystatRegs)) + STAT_MON_##reg##_0), (val)) + +// Regsiter access macros for VDE module +#define NV_VDE_REGR(pVdeRegs, reg) \ + NV_READ32((((NvU32)(pVdeRegs)) + ARVDE_PPB_##reg##_0)) +#define NV_VDE_REGW(pVdeRegs, reg, val) \ + NV_WRITE32((((NvU32)(pVdeRegs)) + ARVDE_PPB_##reg##_0), (val)) + +// Regsiter access macros for EMC module +#define NV_EMC_REGR(pEmcRegs, reg) \ + NV_READ32((((NvU32)(pEmcRegs)) + EMC_##reg##_0)) +#define NV_EMC_REGW(pEmcRegs, reg, val) \ + NV_WRITE32((((NvU32)(pEmcRegs)) + EMC_##reg##_0), (val)) + +// Regsiter access macros for CAR module +#define NV_CAR_REGR(pCarRegs, reg) \ + NV_READ32((((NvU32)(pCarRegs)) + CLK_RST_CONTROLLER_##reg##_0)) +#define NV_CAR_REGW(pCarRegs, reg, val) \ + NV_WRITE32((((NvU32)(pCarRegs)) + CLK_RST_CONTROLLER_##reg##_0), (val)) + +// Regsiter access macros for APB MISC module +#define NV_APB_REGR(pApbRegs, reg) \ + NV_READ32((((NvU32)(pApbRegs)) + APB_MISC_##reg##_0)) +#define NV_APB_REGW(pApbRegs, reg, val) \ + NV_WRITE32((((NvU32)(pApbRegs)) + APB_MISC_##reg##_0), (val)) + +/*****************************************************************************/ +// SYSTEM STATISTIC MODULE INTERFACES +/*****************************************************************************/ + +NvError NvRmPrivAp15SystatMonitorsInit(NvRmDfs* pDfs) +{ + NvError error; + NvU32 RegValue; + void* pSystatRegs = pDfs->Modules[NvRmDfsModuleId_Systat].pBaseReg; + NV_ASSERT(pSystatRegs); + + /* + * System Statistic Monitor module belongs to DFS, therefore it is full + * initialization: Enable Clock => Reset => clear all control registers + * including interrupt status flags (cleared by writing "1"). Note that + * all monitors - used, or not used by DFS - are initialized. (The VPIPE + * monitor in this module does not provide neccessary data for DFS; the + * VDE idle monitor is employed for video-pipe domain control) + */ + error = NvRmPowerModuleClockControl( + pDfs->hRm, NvRmModuleID_SysStatMonitor, pDfs->PowerClientId, NV_TRUE); + if (error != NvSuccess) + { + return error; + } + NvRmModuleReset(pDfs->hRm, NvRmModuleID_SysStatMonitor); + + RegValue = NV_DRF_NUM(STAT_MON, CPU_MON_CTRL, INT, 1); + NV_SYSTAT_REGW(pSystatRegs, CPU_MON_CTRL, RegValue); + + RegValue = NV_DRF_NUM(STAT_MON, COP_MON_CTRL, INT, 1); + NV_SYSTAT_REGW(pSystatRegs, COP_MON_CTRL, RegValue); + + RegValue = NV_DRF_NUM(STAT_MON, CACHE2_MON_CTRL, INT, 1); + NV_SYSTAT_REGW(pSystatRegs, CACHE2_MON_CTRL, RegValue); + + RegValue = NV_DRF_NUM(STAT_MON, AHB_MON_CTRL, INT, 1); + NV_SYSTAT_REGW(pSystatRegs, AHB_MON_CTRL, RegValue); + + RegValue = NV_DRF_NUM(STAT_MON, APB_MON_CTRL, INT, 1); + NV_SYSTAT_REGW(pSystatRegs, APB_MON_CTRL, RegValue); + + RegValue = NV_DRF_NUM(STAT_MON, VPIPE_MON_CTRL, INT, 1); + NV_SYSTAT_REGW(pSystatRegs, VPIPE_MON_CTRL, RegValue); + + RegValue = NV_DRF_NUM(STAT_MON, SMP_MON_CTRL, INT, 1); + NV_SYSTAT_REGW(pSystatRegs, SMP_MON_CTRL, RegValue); + + return NvSuccess; +} + +void NvRmPrivAp15SystatMonitorsDeinit(NvRmDfs* pDfs) +{ + // First initialize monitor, and then just turn off the clock (twice to + // balance the clock control) + (void)NvRmPrivAp15SystatMonitorsInit(pDfs); + (void)NvRmPowerModuleClockControl( + pDfs->hRm, NvRmModuleID_SysStatMonitor, pDfs->PowerClientId, NV_FALSE); + (void)NvRmPowerModuleClockControl( + pDfs->hRm, NvRmModuleID_SysStatMonitor, pDfs->PowerClientId, NV_FALSE); + +} + +void +NvRmPrivAp15SystatMonitorsStart( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + const NvU32 IntervalMs) +{ + NvU32 RegValue; + NvU32 msec = IntervalMs - 1; // systat monitors use (n+1) ms counters + void* pSystatRegs = pDfs->Modules[NvRmDfsModuleId_Systat].pBaseReg; + + /* + * Start AVP (COP) monitor for the next sample period. Interrupt is + * cleared (by writing "1") and left disabled. Monitor is counting + * System clock cycles while AVP is halted by flow controller. Note + * that AVP monitor is counting System (not AVP!) clock cycles + */ + RegValue = NV_DRF_DEF(STAT_MON, COP_MON_CTRL, ENB, ENABLE) | + NV_DRF_NUM(STAT_MON, COP_MON_CTRL, INT, 1) | + NV_DRF_NUM(STAT_MON, COP_MON_CTRL, SAMPLE_PERIOD, msec); + NV_SYSTAT_REGW(pSystatRegs, COP_MON_CTRL, RegValue); + + /* + * Start AHB monitor for the next sample period. Interrupt is cleared + * (by writing "1") and left disabled. Monitor is counting AHB clock + * cycles while there is no data transfer on AHB initiated by any master + */ + RegValue = NV_DRF_DEF(STAT_MON, AHB_MON_CTRL, ENB, ENABLE) | + NV_DRF_NUM(STAT_MON, AHB_MON_CTRL, INT, 1) | + NV_DRF_DEF(STAT_MON, AHB_MON_CTRL, MST_NUMBER, DEFAULT_MASK) | + NV_DRF_NUM(STAT_MON, AHB_MON_CTRL, SAMPLE_PERIOD, msec); + NV_SYSTAT_REGW(pSystatRegs, AHB_MON_CTRL, RegValue); + + /* + * Start APB monitor for the next sample period. Interrupt is cleared + * (by writing "1") and left disabled. Monitor is counting APB clock + * cycles while there is no data transfer on APB targeted to any slave + */ + RegValue = NV_DRF_DEF(STAT_MON, APB_MON_CTRL, ENB, ENABLE) | + NV_DRF_NUM(STAT_MON, APB_MON_CTRL, INT, 1) | + NV_DRF_DEF(STAT_MON, APB_MON_CTRL, SLV_NUMBER, DEFAULT_MASK) | + NV_DRF_NUM(STAT_MON, APB_MON_CTRL, SAMPLE_PERIOD, msec); + NV_SYSTAT_REGW(pSystatRegs, APB_MON_CTRL, RegValue); + + /* + * Start CPU monitor for the next sample period. Interrupt is cleared + * (by writing "1") and enabled, since CPU monitor is used to generate + * DFS interrupt. Monitor is counting System clock cycles while CPU is + * halted by flow controller. Note: CPU monitor is counting System (not + * CPU!) clock cycles + */ + RegValue = NV_DRF_DEF(STAT_MON, CPU_MON_CTRL, ENB, ENABLE) | + NV_DRF_DEF(STAT_MON, CPU_MON_CTRL, INT_EN, ENABLE) | + NV_DRF_NUM(STAT_MON, CPU_MON_CTRL, INT, 1) | + NV_DRF_NUM(STAT_MON, CPU_MON_CTRL, SAMPLE_PERIOD, msec); + NV_SYSTAT_REGW(pSystatRegs, CPU_MON_CTRL, RegValue); + + // Initialize LP2 time storage (WAR for bug 429585) + NvRmPrivSetLp2TimeUS(pDfs->hRm, 0); +} + +void +NvRmPrivAp15SystatMonitorsRead( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + NvRmDfsIdleData* pIdleData) +{ + NvU32 RegValue; + NvU64 temp; + void* pSystatRegs = pDfs->Modules[NvRmDfsModuleId_Systat].pBaseReg; + NvBool NoLp2Offset = pDfs->Modules[NvRmDfsModuleId_Systat].Offset != + NVRM_CPU_IDLE_LP2_OFFSET; + + /* + * Read AVP (COP) monitor: disable it (=stop, the readings are preserved) + * and clear interrupt status bit (by writing "1"). Then, read AVP idle + * count. Since AVP monitor is counting System (not AVP!) clock cycles, + * the monitor reading is converted to AVP clocks before storing it in + * idle data packet. + */ + RegValue = NV_DRF_NUM(STAT_MON, COP_MON_CTRL, INT, 1); + NV_SYSTAT_REGW(pSystatRegs, COP_MON_CTRL, RegValue); + + RegValue = NV_SYSTAT_REGR(pSystatRegs, COP_MON_STATUS); + RegValue = NV_DRF_VAL(STAT_MON, COP_MON_STATUS, COUNT, RegValue); + + temp = ((NvU64)RegValue * pDfsKHz->Domains[NvRmDfsClockId_Avp]); + temp = NvDiv64(temp, pDfsKHz->Domains[NvRmDfsClockId_System]); + + pIdleData->Readings[NvRmDfsClockId_Avp] = (NvU32)temp; + + /* + * Read AHB monitor: disable it (=stop, the readings are preserved) and + * clear interrupt status bit (by writing "1"). Then, read AHB idle count + * (in AHB clock cycles) and store it in idle data packet. + */ + RegValue = NV_DRF_NUM(STAT_MON, AHB_MON_CTRL, INT, 1); + NV_SYSTAT_REGW(pSystatRegs, AHB_MON_CTRL, RegValue); + + RegValue = NV_SYSTAT_REGR(pSystatRegs, AHB_MON_STATUS); + pIdleData->Readings[NvRmDfsClockId_Ahb] = + NV_DRF_VAL(STAT_MON, AHB_MON_STATUS, COUNT, RegValue); + + /* + * Read APB monitor: disable it (=stop, the readings are preserved) and + * clear interrupt status bit (by writing "1"). Then, read APB idle count + * (in APB clock cycles) and store it in idle data packet. + */ + RegValue = NV_DRF_NUM(STAT_MON, APB_MON_CTRL, INT, 1); + NV_SYSTAT_REGW(pSystatRegs, APB_MON_CTRL, RegValue); + + RegValue = NV_SYSTAT_REGR(pSystatRegs, APB_MON_STATUS); + pIdleData->Readings[NvRmDfsClockId_Apb] = + NV_DRF_VAL(STAT_MON, APB_MON_STATUS, COUNT, RegValue); + + /* + * Read CPU monitor: read current sampling period and store it in idle + * data packet. Disable monitor (=stop, the readings are preserved) and + * clear interrupt status bit (by writing "1"). Read CPU idle count. + * Since CPU monitor is counting System (not CPU!) cycles, the monitor + * readings are converted to CPU clocks before storing in idle data packet + */ + RegValue = NV_SYSTAT_REGR(pSystatRegs, CPU_MON_CTRL); + pIdleData->CurrentIntervalMs = 1 + // systat monitors use (n+1) ms counters + NV_DRF_VAL(STAT_MON, CPU_MON_CTRL, SAMPLE_PERIOD, RegValue); + + RegValue = NV_DRF_NUM(STAT_MON, CPU_MON_CTRL, INT, 1); + NV_SYSTAT_REGW(pSystatRegs, CPU_MON_CTRL, RegValue); + + // Add LP2 time to idle measurements (WAR for bug 429585) + // For logging only - use 2^10 ~ 1000, and round up + RegValue = NvRmPrivGetLp2TimeUS(pDfs->hRm); + pIdleData->Lp2TimeMs = (RegValue + (0x1 << 10) - 1) >> 10; + if ((RegValue == 0) || NoLp2Offset) + { + pIdleData->Readings[NvRmDfsClockId_Cpu] = 0; + } + else if (RegValue < NVRM_DFS_MAX_SAMPLE_MS * 1000) + { // (US * KHz) / 1000 ~ (US * 131 * KHz) / (128 * 1024) + pIdleData->Readings[NvRmDfsClockId_Cpu] = + (NvU32)(((NvU64)(RegValue * 131) * + pDfsKHz->Domains[NvRmDfsClockId_Cpu]) >> 17); + } + else + { + pIdleData->Readings[NvRmDfsClockId_Cpu] = 0xFFFFFFFFUL; + return; // the entire sample is idle, anyway + } + RegValue = NV_SYSTAT_REGR(pSystatRegs, CPU_MON_STATUS); + RegValue = NV_DRF_VAL(STAT_MON, CPU_MON_STATUS, COUNT, RegValue); + + temp = ((NvU64)RegValue * pDfsKHz->Domains[NvRmDfsClockId_Cpu]); + temp = NvDiv64(temp, pDfsKHz->Domains[NvRmDfsClockId_System]); + + pIdleData->Readings[NvRmDfsClockId_Cpu] += (NvU32)temp; +} + +/*****************************************************************************/ +// VDE MODULE INTERFACES +/*****************************************************************************/ + +NvError NvRmPrivAp15VdeMonitorsInit(NvRmDfs* pDfs) +{ + NvU32 RegValue; + void* pVdeRegs = pDfs->Modules[NvRmDfsModuleId_Vde].pBaseReg; + NV_ASSERT(pVdeRegs); + + /* + * Video pipe monitor belongs to VDE module - just clear monitor control + * register including interrupt status bit, and do not touch anything + * else in VDE + */ + RegValue = NV_DRF_NUM(ARVDE_PPB, IDLE_MON, INT_STATUS, 1); + NV_VDE_REGW(pVdeRegs, IDLE_MON, RegValue); + + return NvSuccess; +} + +void NvRmPrivAp15VdeMonitorsDeinit(NvRmDfs* pDfs) +{ + // Stop monitor using initialization procedure + (void)NvRmPrivAp15VdeMonitorsInit(pDfs); +} + +void +NvRmPrivAp15VdeMonitorsStart( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + const NvU32 IntervalMs) +{ + NvU32 RegValue; + NvU32 cycles = IntervalMs * pDfsKHz->Domains[NvRmDfsClockId_Vpipe]; + void* pVdeRegs = pDfs->Modules[NvRmDfsModuleId_Vde].pBaseReg; + + /* + * Start VDE vpipe monitor for the next sample period. Interrupt status bit + * is cleared by writing "1" (it is not connected to interrupt controller, + * just "count end" status bit). Monitor is counting v-clock cycles while + * all VDE submodules are idle. The sample period is specified in v-clock + * cycles rather than in time units. + */ + RegValue = NV_DRF_NUM(ARVDE_PPB, IDLE_MON, ENB, 1) | + NV_DRF_NUM(ARVDE_PPB, IDLE_MON, INT_STATUS, 1) | + NV_DRF_NUM(ARVDE_PPB, IDLE_MON, SAMPLE_PERIOD, cycles); + NV_VDE_REGW(pVdeRegs, IDLE_MON, RegValue); +} + +void +NvRmPrivAp15VdeMonitorsRead( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + NvRmDfsIdleData* pIdleData) +{ + // CAR module virtual base address + static void* s_pCarBaseReg = NULL; + NvU32 RegValue; + void* pVdeRegs = pDfs->Modules[NvRmDfsModuleId_Vde].pBaseReg; + + if (s_pCarBaseReg == NULL) + { + NvRmModuleTable *tbl = NvRmPrivGetModuleTable(pDfs->hRm); + s_pCarBaseReg = (tbl->ModInst + + tbl->Modules[NvRmPrivModuleID_ClockAndReset].Index)->VirtAddr; + } + RegValue = NV_CAR_REGR(s_pCarBaseReg, CLK_OUT_ENB_H); + + // If VDE clock is disabled set idle count to maximum + if (!(RegValue & CLK_RST_CONTROLLER_CLK_OUT_ENB_H_0_CLK_ENB_VDE_FIELD)) + { + pIdleData->Readings[NvRmDfsClockId_Vpipe] = (NvU32)-1; + return; + } + + /* + * Read VDE vpipe monitor: disable it (=stop, the readings are preserved) and + * clear count done status bit (by writing "1"). Then, read VDE idle count (in + * v-clock cycles) and store it in idle data packet. + */ + RegValue = NV_DRF_NUM(ARVDE_PPB, IDLE_MON, INT_STATUS, 1); + NV_VDE_REGW(pVdeRegs, IDLE_MON, RegValue); + + RegValue = NV_VDE_REGR(pVdeRegs, IDLE_STATUS); + pIdleData->Readings[NvRmDfsClockId_Vpipe] = + NV_DRF_VAL(ARVDE_PPB, IDLE_STATUS, COUNT, RegValue); +} + +/*****************************************************************************/ +// EMC MODULE INTERFACES +/*****************************************************************************/ + +NvError NvRmPrivAp15EmcMonitorsInit(NvRmDfs* pDfs) +{ + NvU32 RegValue; + void* pEmcRegs = pDfs->Modules[NvRmDfsModuleId_Emc].pBaseReg; + NV_ASSERT(pEmcRegs); + + /* + * EMC power management monitor belongs to EMC module - just reset it, + * and do not touch anything else in EMC. + */ + RegValue = NV_EMC_REGR(pEmcRegs, STAT_CONTROL); + RegValue = NV_FLD_SET_DRF_DEF(EMC, STAT_CONTROL, PWR_GATHER, RST, RegValue); + NV_EMC_REGW(pEmcRegs, STAT_CONTROL, RegValue); + + /* + * EMC active clock cycles = EMC monitor reading * 2^M, where M depends + * on DRAM type and bus width. Power M is stored as EMC readouts scale + */ + #define COUNT_SHIFT_SDRAM_X32 (2) + #define COUNT_SHIFT_DDR1_X32 (1) + RegValue = NV_EMC_REGR(pEmcRegs, FBIO_CFG5); + switch (NV_DRF_VAL(EMC, FBIO_CFG5, DRAM_TYPE, RegValue)) + { + case EMC_FBIO_CFG5_0_DRAM_TYPE_SDR: + pDfs->Modules[NvRmDfsModuleId_Emc].Scale = COUNT_SHIFT_SDRAM_X32; + break; + case EMC_FBIO_CFG5_0_DRAM_TYPE_DDR1: + pDfs->Modules[NvRmDfsModuleId_Emc].Scale = COUNT_SHIFT_DDR1_X32; + break; + default: + NV_ASSERT(!"Not supported DRAM type"); + } + if (NV_DRF_VAL(EMC, FBIO_CFG5, DRAM_WIDTH, RegValue) == + EMC_FBIO_CFG5_0_DRAM_WIDTH_X16) + { + pDfs->Modules[NvRmDfsModuleId_Emc].Scale++; + } + return NvSuccess; +} + +void NvRmPrivAp15EmcMonitorsDeinit(NvRmDfs* pDfs) +{ + // Stop monitor using initialization procedure + (void)NvRmPrivAp15EmcMonitorsInit(pDfs); +} + +void +NvRmPrivAp15EmcMonitorsStart( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + const NvU32 IntervalMs) +{ + NvU32 RegValue, SavedRegValue; + void* pEmcRegs = pDfs->Modules[NvRmDfsModuleId_Emc].pBaseReg; + + // EMC sample period is specified in EMC clock cycles, accuracy 0-16 cycles. + #define MEAN_EMC_LIMIT_ERROR (8) + NvU32 cycles = IntervalMs * pDfsKHz->Domains[NvRmDfsClockId_Emc] + + MEAN_EMC_LIMIT_ERROR; + /* + * Start EMC power monitor for the next sample period: clear EMC counters, + * set sample interval limit in EMC cycles, enable monitoring. Monitor is + * counting EMC 1x clock cycles while any memory access is detected. + */ + SavedRegValue = NV_EMC_REGR(pEmcRegs, STAT_CONTROL); + RegValue = NV_FLD_SET_DRF_DEF(EMC, STAT_CONTROL, PWR_GATHER, CLEAR, SavedRegValue); + NV_EMC_REGW(pEmcRegs, STAT_CONTROL, RegValue); + + RegValue = NV_DRF_NUM(EMC, STAT_PWR_CLOCK_LIMIT, PWR_CLOCK_LIMIT, cycles); + NV_EMC_REGW(pEmcRegs, STAT_PWR_CLOCK_LIMIT, RegValue); + + RegValue = NV_FLD_SET_DRF_DEF(EMC, STAT_CONTROL, PWR_GATHER, ENABLE, SavedRegValue); + NV_EMC_REGW(pEmcRegs, STAT_CONTROL, RegValue); +} + +void +NvRmPrivAp15EmcMonitorsRead( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + NvRmDfsIdleData* pIdleData) +{ + NvU32 RegValue, TotalClocks; + NvU32 CountShift = pDfs->Modules[NvRmDfsModuleId_Emc].Scale; + void* pEmcRegs = pDfs->Modules[NvRmDfsModuleId_Emc].pBaseReg; + + /* + * Read EMC monitor: disable it (=stop, the readings are preserved), and + * determine idle count based on total and active clock counts. Monitor + * readings are multiplied by 2^M factor to determine active count, where + * power M depends on DRAM type and bus width. Store result in the idle + * data packet. + */ + RegValue = NV_EMC_REGR(pEmcRegs, STAT_CONTROL); + RegValue = NV_FLD_SET_DRF_DEF(EMC, STAT_CONTROL, PWR_GATHER, DISABLE, RegValue); + NV_EMC_REGW(pEmcRegs, STAT_CONTROL, RegValue); + + RegValue = NV_EMC_REGR(pEmcRegs, STAT_PWR_CLOCKS); + TotalClocks = NV_DRF_VAL(EMC, STAT_PWR_CLOCKS, PWR_CLOCKS, RegValue); + RegValue = NV_EMC_REGR(pEmcRegs, STAT_PWR_COUNT); + RegValue = NV_DRF_VAL(EMC, STAT_PWR_COUNT, PWR_COUNT, RegValue) << CountShift; + + pIdleData->Readings[NvRmDfsClockId_Emc] = + (TotalClocks > RegValue) ? (TotalClocks - RegValue) : 0; +} + +/*****************************************************************************/ + +void +NvRmPrivAp15SetSvopControls( + NvRmDeviceHandle hRm, + NvU32 SvopSetting) +{ +#define SVOP_MASK \ + (NV_DRF_NUM(APB_MISC, GP_ASDBGREG, CFG2TMC_RAM_SVOP_DP, 0xFFFFFFFFUL) | \ + NV_DRF_NUM(APB_MISC, GP_ASDBGREG, CFG2TMC_RAM_SVOP_PDP, 0xFFFFFFFFUL) | \ + NV_DRF_NUM(APB_MISC, GP_ASDBGREG, CFG2TMC_RAM_SVOP_REG, 0xFFFFFFFFUL) | \ + NV_DRF_NUM(APB_MISC, GP_ASDBGREG, CFG2TMC_RAM_SVOP_SP, 0xFFFFFFFFUL)) + + NvU32 reg; + static void* s_pApbBaseReg = NULL; // APB MISC module virtual base address + + if (s_pApbBaseReg == NULL) + { + NvRmModuleTable *tbl = NvRmPrivGetModuleTable(hRm); + s_pApbBaseReg = (tbl->ModInst + + tbl->Modules[NvRmModuleID_Misc].Index)->VirtAddr; + } + NV_ASSERT((SvopSetting & (~SVOP_MASK)) == 0); + reg = NV_APB_REGR(s_pApbBaseReg, GP_ASDBGREG); // RAM timing control + reg = (reg & (~SVOP_MASK)) | SvopSetting; + NV_APB_REGW(s_pApbBaseReg, GP_ASDBGREG, reg); +} + +/*****************************************************************************/ + +void* NvRmPrivAp15GetTimerUsVirtAddr(NvRmDeviceHandle hRm) +{ + NvRmModuleTable *tbl = NvRmPrivGetModuleTable(hRm); + void* va = (void*)((NvUPtr)((tbl->ModInst + + tbl->Modules[NvRmModuleID_TimerUs].Index)->VirtAddr) + + TIMERUS_CNTR_1US_0); + return va; +} + +/*****************************************************************************/ diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.h b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.h new file mode 100644 index 000000000000..10889666d25c --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.h @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief nVIDIA Driver Development Kit: + * Power Resource manager + * + * @b Description: NvRM DFS parameters. + * + */ + +#ifndef INCLUDED_AP15RM_POWER_DFS_H +#define INCLUDED_AP15RM_POWER_DFS_H + +#include "nvrm_power.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +// Min KHz for CPU and AVP with regards to JTAG support - 1MHz * 8 = 8MHz +// TODO: any other limitations on min KHz? +// TODO: adjust boost parameters based on testing + +/** + * Default DFS algorithm parameters for CPU domain + */ +#define NVRM_DFS_PARAM_CPU_AP15 \ + NvRmFreqMaximum, /* Maximum domain frequency set to h/w limit */ \ + 10000, /* Minimum domain frequency 10 MHz */ \ + 1000, /* Frequency change upper band 1 MHz */ \ + 1000, /* Frequency change lower band 1 MHz */ \ + { /* RT starvation control parameters */ \ + 32000, /* Fixed frequency boost increase 32 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 128, /* Proportional frequency boost decrease 128/256 ~ 50% */ \ + },\ + { /* NRT starvation control parameters */ \ + 4000, /* Fixed frequency boost increase 4 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 128, /* Proportional frequency boost decrease 128/256 ~ 50% */ \ + },\ + 3, /* Relative adjustement of average freqiency 1/2^3 ~ 12% */ \ + 1, /* Number of smaple intervals with NRT to trigger boost = 2 */ \ + 1 /* NRT idle cycles threshold = 1 */ + +/** + * Default DFS algorithm parameters for AVP domain + */ +#define NVRM_DFS_PARAM_AVP_AP15 \ + NvRmFreqMaximum, /* Maximum domain frequency set to h/w limit */ \ + 24000, /* Minimum domain frequency 24 MHz */ \ + 1000, /* Frequency change upper band 1 MHz */ \ + 1000, /* Frequency change lower band 1 MHz */ \ + { /* RT starvation control parameters */ \ + 8000, /* Fixed frequency boost increase 8 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 128, /* Proportional frequency boost decrease 128/256 ~ 50% */ \ + },\ + { /* NRT starvation control parameters */ \ + 1000, /* Fixed frequency NRT boost increase 1 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 128, /* Proportional frequency boost decrease 128/256 ~ 50% */ \ + },\ + 3, /* Relative adjustement of average freqiency 1/2^3 ~ 12% */ \ + 2, /* Number of smaple intervals with NRT to trigger boost = 3 */ \ + 1 /* NRT idle cycles threshold = 1 */ + +/** + * Default DFS algorithm parameters for System clock domain + */ +#define NVRM_DFS_PARAM_SYSTEM_AP15 \ + NvRmFreqMaximum, /* Maximum domain frequency set to h/w limit */ \ + 24000, /* Minimum domain frequency 24 MHz */ \ + 1000, /* Frequency change upper band 1 MHz */ \ + 1000, /* Frequency change lower band 1 MHz */ \ + { /* RT starvation control parameters */ \ + 8000, /* Fixed frequency boost increase 8 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 128, /* Proportional frequency boost decrease 128/256 ~ 50% */ \ + },\ + { /* NRT starvation control parameters */ \ + 1000, /* Fixed frequency NRT boost increase 1 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 32, /* Proportional frequency boost decrease 32/256 ~ 12% */ \ + },\ + 5, /* Relative adjustement of average freqiency 1/2^5 ~ 3% */ \ + 2, /* Number of smaple intervals with NRT to trigger boost = 3 */ \ + 1 /* NRT idle cycles threshold = 1 */ + +/** + * Default DFS algorithm parameters for AHB clock domain + */ +#define NVRM_DFS_PARAM_AHB_AP15 \ + NvRmFreqMaximum, /* Maximum domain frequency set to h/w limit */ \ + 24000, /* Minimum domain frequency 24 MHz */ \ + 1000, /* Frequency change upper band 1 MHz */ \ + 1000, /* Frequency change lower band 1 MHz */ \ + { /* RT starvation control parameters */ \ + 8000, /* Fixed frequency boost increase 8 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 128, /* Proportional frequency boost decrease 128/256 ~ 50% */ \ + },\ + { /* NRT starvation control parameters */ \ + 1000, /* Fixed frequency NRT boost increase 1 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 32, /* Proportional frequency boost decrease 32/256 ~ 12% */ \ + },\ + 0, /* Relative adjustement of average freqiency 1/2^0 ~ 100% */ \ + 0, /* Number of smaple intervals with NRT to trigger boost = 1 */ \ + 1 /* NRT idle cycles threshold = 1 */ + +/** + * Default DFS algorithm parameters for APB clock domain + */ +#define NVRM_DFS_PARAM_APB_AP15 \ + NvRmFreqMaximum, /* Maximum domain frequency set to h/w limit */ \ + 15000, /* Minimum domain frequency 15 MHz */ \ + 1000, /* Frequency change upper band 1 MHz */ \ + 1000, /* Frequency change lower band 1 MHz */ \ + { /* RT starvation control parameters */ \ + 8000, /* Fixed frequency boost increase 8 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 128, /* Proportional frequency boost decrease 128/256 ~ 50% */ \ + },\ + { /* NRT starvation control parameters */ \ + 1000, /* Fixed frequency NRT boost increase 1 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 32, /* Proportional frequency boost decrease 32/256 ~ 12% */ \ + },\ + 0, /* Relative adjustement of average freqiency 1/2^0 ~ 100% */ \ + 0, /* Number of smaple intervals with NRT to trigger boost = 1 */ \ + 1 /* NRT idle cycles threshold = 1 */ + +/** + * Default DFS algorithm parameters for Video-pipe clock domain + */ +#define NVRM_DFS_PARAM_VPIPE_AP15 \ + NvRmFreqMaximum, /* Maximum domain frequency set to h/w limit */ \ + 24000, /* Minimum domain frequency 24 MHz */ \ + 1000, /* Frequency change upper band 1 MHz */ \ + 1000, /* Frequency change lower band 1 MHz */ \ + { /* RT starvation control parameters */ \ + 16000, /* Fixed frequency RT boost increase 16 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 128, /* Proportional frequency boost decrease 128/256 ~ 50% */ \ + },\ + { /* NRT starvation control parameters */ \ + 1000, /* Fixed frequency NRT boost increase 1 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 128, /* Proportional frequency boost decrease 128/256 ~ 50% */ \ + },\ + 5, /* Relative adjustement of average freqiency 1/2^5 ~ 3% */ \ + 3, /* Number of smaple intervals with NRT to trigger boost = 4 */ \ + 1 /* NRT idle cycles threshold = 1 */ + +/** + * Default DFS algorithm parameters for EMC clock domain + */ +#define NVRM_DFS_PARAM_EMC_AP15 \ + NvRmFreqMaximum, /* Maximum domain frequency set to h/w limit */ \ + 16000, /* Minimum domain frequency 16 MHz */ \ + 1000, /* Frequency change upper band 1 MHz */ \ + 1000, /* Frequency change lower band 1 MHz */ \ + { /* RT starvation control parameters */ \ + 16000, /* Fixed frequency RT boost increase 16 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 128, /* Proportional frequency boost decrease 128/256 ~ 50% */ \ + },\ + { /* NRT starvation control parameters */ \ + 1000, /* Fixed frequency NRT boost increase 1 MHz */ \ + 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ + 128, /* Proportional frequency boost decrease 128/256 ~ 50% */ \ + },\ + 0, /* Relative adjustement of average freqiency 1/2^0 ~ 100% */ \ + 0, /* Number of smaple intervals with NRT to trigger boost = 1 */ \ + 1 /* NRT idle cycles threshold = 1 */ + +/// Default low corner for core voltage +#define NVRM_AP15_LOW_CORE_MV (950) + +/// Core voltage in suspend +#define NVRM_AP15_SUSPEND_CORE_MV (1000) + +/*****************************************************************************/ + +/** + * Initializes activity monitors within the DFS module. Only activity + * monitors are affected. The rest of module's h/w is preserved. + * + * @param pDfs - A pointer to DFS structure. + * + * @return NvSuccess if initialization completed successfully + * or one of common error codes on failure. + */ +NvError NvRmPrivAp15SystatMonitorsInit(NvRmDfs* pDfs); +NvError NvRmPrivAp15VdeMonitorsInit(NvRmDfs* pDfs); +NvError NvRmPrivAp15EmcMonitorsInit(NvRmDfs* pDfs); + +/** + * Deinitializes activity monitors within the DFS module. Only activity + * monitors are affected. The rest of module's h/w is preserved. + * + * @param pDfs - A pointer to DFS structure. + */ +void NvRmPrivAp15SystatMonitorsDeinit(NvRmDfs* pDfs); +void NvRmPrivAp15VdeMonitorsDeinit(NvRmDfs* pDfs); +void NvRmPrivAp15EmcMonitorsDeinit(NvRmDfs* pDfs); + +/** + * Starts activity monitors in the DFS module for the next sample interval. + * + * @param pDfs - A pointer to DFS structure. + * @param pDfsKHz - A pointer to current DFS clock frequencies structure. + * @param IntervalMs Next sampling interval in ms. + */ +void +NvRmPrivAp15SystatMonitorsStart( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + const NvU32 IntervalMs); +void +NvRmPrivAp15VdeMonitorsStart( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + const NvU32 IntervalMs); +void +NvRmPrivAp15EmcMonitorsStart( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + const NvU32 IntervalMs); + +/** + * Reads idle count from activity monitors in the DFS module. The monitors are + * stopped. + * + * @param pDfs - A pointer to DFS structure. + * @param pDfsKHz - A pointer to current DFS clock frequencies structure. + * @param pIdleData - A pointer to idle cycles structure to be filled in with + * data read from the monitor. + * + */ +void +NvRmPrivAp15SystatMonitorsRead( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + NvRmDfsIdleData* pIdleData); +void +NvRmPrivAp15VdeMonitorsRead( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + NvRmDfsIdleData* pIdleData); +void +NvRmPrivAp15EmcMonitorsRead( + const NvRmDfs* pDfs, + const NvRmDfsFrequencies* pDfsKHz, + NvRmDfsIdleData* pIdleData); + +/** + * Changes RAM timing SVOP settings. + * + * @param hRm The RM device handle. + * @param SvopSetting New SVOP setting. + */ +void +NvRmPrivAp15SetSvopControls( + NvRmDeviceHandle hRm, + NvU32 SvopSetting); + +/** + * Gets uS Timer RM virtual address, + * + * @param hRm The RM device handle. + * + * @return uS Timer RM virtual address mapped by RM + */ +void* NvRmPrivAp15GetTimerUsVirtAddr(NvRmDeviceHandle hRm); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif // INCLUDED_AP15RM_POWER_DFS_H diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_oalintf.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_oalintf.c new file mode 100644 index 000000000000..e26d5b6e8376 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_oalintf.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief nVIDIA Driver Development Kit: + * Power Resource manager API shared with OS adaptation layer + * + * @b Description: Implements private HW interface shared by the NvRM Power + * manager and OS adaptation layer (OAL). + * + */ + +#include "nvrm_power.h" +#include "nvrm_clocks.h" +#include "nvrm_module.h" +#include "nvrm_drf.h" +#include "nvrm_hwintf.h" +#include "ap15rm_private.h" +#include "nvrm_structure.h" +#include "ap15/arapb_misc.h" +#include "ap15rm_pmc_scratch_map.h" +#include "common/nvrm_chiplib.h" +#include "nvassert.h" + +/*****************************************************************************/ + +/* + * Macros for power state register field access. + * AP15+: a dedicated bits in PMC scratch register 0 are allocated for RM power + * state fields. + */ +#define SET_POWER_FLD_AP15(rm, FieldName, FieldValue) \ + do \ + { \ + if (!NvRmIsSimulation())\ + {\ + NvU32 RegValue; \ + NvU32 RegOffset = APBDEV_PMC_SCRATCH0_0; \ + NvOsSpinMutexLock(s_hPmcScratchMutex); \ + RegValue = NV_REGR(rm, NvRmModuleID_Pmif, 0, RegOffset); \ + RegValue = NV_FLD_SET_DRF_NUM(\ + APBDEV_PMC, SCRATCH0, FieldName, FieldValue, RegValue); \ + NV_REGW(rm, NvRmModuleID_Pmif, 0, RegOffset, RegValue); \ + NvOsSpinMutexUnlock(s_hPmcScratchMutex); \ + }\ + } while (0) + +#define GET_POWER_FLD_AP15(rm, FieldName) \ + NV_DRF_VAL(APBDEV_PMC, SCRATCH0, FieldName, \ + (NV_REGR(rm, NvRmModuleID_Pmif, 0, APBDEV_PMC_SCRATCH0_0))); + +/*****************************************************************************/ + +// Mutex for thread-safe access to PMC scratch fields +static NvOsSpinMutexHandle s_hPmcScratchMutex = NULL; + +// Pointer to LP2 Time storage +static NvUPtr s_pLp2Time = 0; + +NvError NvRmPrivOalIntfInit(NvRmDeviceHandle hRmDeviceHandle) +{ + NvError e; + NV_ASSERT(hRmDeviceHandle); + + // Create PMC scratch register access mutex + s_pLp2Time = 0; + s_hPmcScratchMutex = NULL; + NV_CHECK_ERROR_CLEANUP(NvOsSpinMutexCreate(&s_hPmcScratchMutex)); + + // Clear DFS flags; other fields initialized by OAL and preserved by RM + SET_POWER_FLD_AP15(hRmDeviceHandle, RM_DFS_FLAG, 0); + return NvSuccess; + +fail: + NvRmPrivOalIntfDeinit(hRmDeviceHandle); + return e; +} + +void NvRmPrivOalIntfDeinit(NvRmDeviceHandle hRmDeviceHandle) +{ + NvOsSpinMutexDestroy(s_hPmcScratchMutex); + s_hPmcScratchMutex = NULL; +} + +/*****************************************************************************/ + +/* + * Write synchronization with the OAL is responsibility of the OAL, i.e., OAL + * calls set state function only on entry to LPx state in single-thread + * environment + */ +void +NvRmPrivPowerSetState(NvRmDeviceHandle hRmDeviceHandle, NvRmPowerState RmState) +{ + SET_POWER_FLD_AP15(hRmDeviceHandle, RM_PWR_STATE, RmState); +} + +NvRmPowerState +NvRmPrivPowerGetState(NvRmDeviceHandle hRmDeviceHandle) +{ + NvRmPowerState state = 0; + + if (!NvRmIsSimulation()) + { + state = GET_POWER_FLD_AP15(hRmDeviceHandle, RM_PWR_STATE); + } + return state; +} + +/*****************************************************************************/ + +/* + * Read synchronization with the OAL is responsibility of the OAL, i.e., OAL + * calls get flags function only on entry to LPx state in single-thread + * environment + */ +NvU32 +NvRmPrivGetDfsFlags(NvRmDeviceHandle hRmDeviceHandle) +{ + NvU32 Flags = 0; + if (!NvRmIsSimulation()) + { + Flags = GET_POWER_FLD_AP15(hRmDeviceHandle, RM_DFS_FLAG); + if (!(Flags & NvRmDfsStatusFlags_StopPllA0)) + Flags &= (~NvRmDfsStatusFlags_StopPllP0); // PLLA input from PLLP + } + return Flags; +} + +void +NvRmPrivUpdateDfsPauseFlag( + NvRmDeviceHandle hRmDeviceHandle, + NvBool Pause) +{ + if (!NvRmIsSimulation()) + { + NvU32 RegValue; + NvU32 RegOffset = APBDEV_PMC_SCRATCH0_0; + NvU32 mask = (NvRmDfsStatusFlags_Pause << + NV_FIELD_SHIFT(APBDEV_PMC_SCRATCH0_0_RM_DFS_FLAG_RANGE)); + + NvOsSpinMutexLock(s_hPmcScratchMutex); + + RegValue = NV_REGR(hRmDeviceHandle, NvRmModuleID_Pmif, 0, RegOffset); + if (Pause) + RegValue |= mask; + else + RegValue &= ~mask; + NV_REGW(hRmDeviceHandle, NvRmModuleID_Pmif, 0, RegOffset, RegValue); + + NvOsSpinMutexUnlock(s_hPmcScratchMutex); + } +} + +void +NvRmPrivPllRefUpdate( + NvRmDeviceHandle hRmDeviceHandle, + NvRmPllReference* pPllRef, + NvBool Increment) +{ +#if !NV_OAL + NvU32 RegValue, mask; + NvU32 RegOffset = APBDEV_PMC_SCRATCH0_0; + + // Do nothing for platforms other, than SoC + if (NvRmPrivGetExecPlatform(hRmDeviceHandle) != ExecPlatform_Soc) + return; + + NV_ASSERT(pPllRef); + NV_ASSERT(pPllRef->StopFlag <= + NV_FIELD_MASK(APBDEV_PMC_SCRATCH0_0_RM_DFS_FLAG_RANGE)); + mask = (pPllRef->StopFlag << + NV_FIELD_SHIFT(APBDEV_PMC_SCRATCH0_0_RM_DFS_FLAG_RANGE)); + + NvOsSpinMutexLock(s_hPmcScratchMutex); + + if (Increment) + { + pPllRef->ReferenceCnt++; + if (pPllRef->ReferenceCnt == 1) + { + RegValue = (~mask) & + (NV_REGR(hRmDeviceHandle, NvRmModuleID_Pmif, 0, RegOffset)); + NV_REGW(hRmDeviceHandle, NvRmModuleID_Pmif, 0, RegOffset, RegValue); + } + } + else + { + NV_ASSERT(pPllRef->ReferenceCnt); + if (pPllRef->ReferenceCnt) + { + pPllRef->ReferenceCnt--; + if (pPllRef->ReferenceCnt == 0) + { + RegValue = mask | + (NV_REGR(hRmDeviceHandle, NvRmModuleID_Pmif, 0, RegOffset)); + NV_REGW( + hRmDeviceHandle, NvRmModuleID_Pmif, 0, RegOffset, RegValue); + } + } + } + NvOsSpinMutexUnlock(s_hPmcScratchMutex); +#endif +} + +/*****************************************************************************/ + +/* + * Write synchronization with the OAL is responsibility of the OAL, i.e., OAL + * calls set state function only in OEMInit() single-thread environment + */ +void +NvRmPrivSetDownloadTransport( + NvRmDeviceHandle hRmDeviceHandle, + NvOdmDownloadTransport Transport) +{ + NV_ASSERT(Transport <= + NV_FIELD_MASK(APBDEV_PMC_SCRATCH0_0_RM_LOAD_TRANSPORT_RANGE)); + SET_POWER_FLD_AP15(hRmDeviceHandle, RM_LOAD_TRANSPORT, Transport); +} + +NvOdmDownloadTransport +NvRmPrivGetDownloadTransport(NvRmDeviceHandle hRmDeviceHandle) +{ + NvOdmDownloadTransport Transport = NvOdmDownloadTransport_None; + if (!NvRmIsSimulation()) + { + Transport = GET_POWER_FLD_AP15(hRmDeviceHandle, RM_LOAD_TRANSPORT); + } + return Transport; +} + +/*****************************************************************************/ + +void NvRmPrivAp15IoPowerDetectReset(NvRmDeviceHandle hRmDeviceHandle) +{ + if (!NvRmIsSimulation()) + { + NvU32 RegValue; + NvU32 RegOffset = APBDEV_PMC_SCRATCH0_0; + NvOsSpinMutexLock(s_hPmcScratchMutex); + + RegValue = + NV_REGR(hRmDeviceHandle, NvRmModuleID_Pmif, 0, RegOffset); + RegValue = NV_FLD_SET_DRF_NUM( + APBDEV_PMC, SCRATCH0, RST_PWR_DET, 1, RegValue); + NV_REGW(hRmDeviceHandle, + NvRmModuleID_Pmif, 0, RegOffset, RegValue); + RegValue = NV_FLD_SET_DRF_NUM( + APBDEV_PMC, SCRATCH0, RST_PWR_DET, 0, RegValue); + NV_REGW(hRmDeviceHandle, + NvRmModuleID_Pmif, 0, RegOffset, RegValue); + + NvOsSpinMutexUnlock(s_hPmcScratchMutex); + } +} + +/*****************************************************************************/ + +/* + * PMC scratch register 21 is dedicated as LP2 time storage. + * Write synchronization with the OAL is responsibility of the OAL, i.e., OAL + * calls access this register only in single-thread environment. + */ +void +NvRmPrivSetLp2TimeUS( + NvRmDeviceHandle hRmDeviceHandle, + NvU32 TimeUS) +{ + if (NvRmIsSimulation()) + return; + + { + if (s_pLp2Time == 0) + { + NvRmModuleTable* tbl = NvRmPrivGetModuleTable(hRmDeviceHandle); + s_pLp2Time = ((NvUPtr)(tbl->ModInst + + tbl->Modules[NvRmModuleID_Pmif].Index)->VirtAddr) + + APBDEV_PMC_SCRATCH21_0; + } + NV_WRITE32(s_pLp2Time, TimeUS); + } +} + +NvU32 +NvRmPrivGetLp2TimeUS(NvRmDeviceHandle hRmDeviceHandle) +{ + if (NvRmIsSimulation()) + return 0; + + { + if (s_pLp2Time == 0) + { + NvRmModuleTable* tbl = NvRmPrivGetModuleTable(hRmDeviceHandle); + s_pLp2Time = ((NvUPtr)(tbl->ModInst + + tbl->Modules[NvRmModuleID_Pmif].Index)->VirtAddr) + + APBDEV_PMC_SCRATCH21_0; + } + return NV_READ32(s_pLp2Time); + } +} + +/*****************************************************************************/ + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_private.h b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_private.h new file mode 100644 index 000000000000..ed39fe1826b5 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_private.h @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef AP15RM_PRIVATE_H +#define AP15RM_PRIVATE_H + +/* + * ap15rm_private.h defines the private implementation functions for the + * resource manager. + */ + +#include "nvcommon.h" +#include "nvrm_structure.h" +#include "nvrm_power_private.h" +#include "nvodm_query.h" +#include "nvos.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +// Enable this macro to catch spurious interrupts. By default this is disabled +// as we allow spurious interrupts from GPIO controller. +#if 0 +#define NVRM_INTR_DECODE_ASSERT(x) NV_ASSERT(x) +#else +#define NVRM_INTR_DECODE_ASSERT(x) +#endif + +/** + * Find a module given its physical register address + * + * @param hDevice The RM instance + * @param Address Physical base address of the module's registers + * @param ModuleId Output parameter to hold the Id of the module (includes + * instance). + * + * @retval NvSuccess The module id was successfully identified. + * @retval NvError_NotSupported No module exists at the specified + * physical base address. + * @retval NvError_BadValue Invalid input parameters. + */ +NvError +NvRmPrivFindModule(NvRmDeviceHandle hDevice, NvU32 Address, + NvRmPrivModuleID* ModuleId); + +/** Driver init for interrupts. + */ +void +NvRmPrivInterruptTableInit( NvRmDeviceHandle hDevice ); + +/** + * Enable interrupt source for interrupt decoder. + */ +/** + * Disable interrupt source for interrupt decoder. + */ + +/** + * Main controller interrupt enable/disable for sub-controllers. + */ + +/** + * Interrupt source enable/disable for AP15 main interrupt controllers. + */ + +/** + * Chip unque id for AP15 and ap16. + */ +NvError +NvRmPrivAp15ChipUniqueId( + NvRmDeviceHandle hDevHandle, + void* pId); + +// Initialize/deinitialize for various RM submodules. +NvError NvRmPrivDmaInit(NvRmDeviceHandle hDevice); +void NvRmPrivDmaDeInit(void); + +NvError NvRmPrivSpiSlinkInit(NvRmDeviceHandle hDevice); +void NvRmPrivSpiSlinkDeInit(void); + +/** + * Retrieves module instance record pointer given module ID + * + * @param hDevice The RM device handle + * @param ModuleId The combined module ID and instance of the target module + * @param out Output storage pointer for instance record pointer + * + * @retval NvSuccess if instance pointer was successfully retrieved + * @retval NvError_BadValue if module ID is invalid + */ +NvError +NvRmPrivGetModuleInstance( + NvRmDeviceHandle hDevice, + NvRmModuleID ModuleId, + NvRmModuleInstance **out); + +/* + * OS specific interrupt initialization + */ +void +NvRmPrivInterruptStart(NvRmDeviceHandle hDevice); + +/** + * Clear out anything that registered for an interrupt but didn't clean up + * afteritself. + */ + +void +NvRmPrivInterruptShutdown(NvRmDeviceHandle hDevice); + +/** + * Initializes the RM's internal state for tracking the pin-mux register + * configurations. This is done by iteratively applying the pre-defined + * configurations from ODM Query (see nvodm_query_pinmux.c). This function + * applies an "enable" setting when there's a match against the static + * declarations (in ODM Query). + * + * As this function walks the configuration list defined in ODM Query, it does + * *not* disable (apply tristate settings to) unused pin-groups for a given I/O + * module's configuration. That would be an exercise in futility, since the + * current I/O module cannot know if another I/O module is using any unclaimed + * pin-groups which the current I/O module configuration might otherwise use. + * That system-wide view of pin-group resources is the responsibility of the + * System Designer who selects pin-group combinations from the pin-mux + * documentation (see //sw/mobile/docs/hw/ap15/pin_mux_configurations.xls). + * The selected combination of pin-mux settings (which cannot be in conflict) + * are then saved to the configuration tables in ODM Query. + * + * Further, this initialization routine enables the configuration identified by + * the ODM Query tables. Any pre-existing settings are not changed, except as + * defined by the static configuration tables in ODM Query. Therefore, the + * System Designer *must* also account for pre-existing power-on-reset (POR) + * values when determining the valid pin-mux configurations saved in ODM Query. + * + * Finally, any use of the pin-mux registers prior to RM initialization *must* + * be consistent with the ODM Query tables, otherwise the system configuration + * is not deterministic (and may violate the definition applied by the System + * Designer). Once RM initializes its pin-mux state, any direct access to the + * pin-mux registers (ie, not using the RM PinMux API) is strictly prohibited. + * + * @param hDevice The RM device handle. + */ +void +NvRmPrivInitPinMux(NvRmDeviceHandle hDevice); + +/** + * Initializes the clock manager. + * + * @param hRmDevice The RM device handle + * + * @return NvSuccess if initialization completed successfully + * or one of common error codes on failure + */ +NvError +NvRmPrivClocksInit(NvRmDeviceHandle hRmDevice); + +/** + * Deinitializes the clock manager. + * + * @param hRmDevice The RM device handle + */ +void +NvRmPrivClocksDeinit(NvRmDeviceHandle hRmDevice); + + +/*** Private Interrupt API's ***/ + + +/** + * Performs primary interrupt decode for IRQ interrupts in the main + * interrupt controllers. + * + * @param hRmDevice The RM device handle. + * @returns The IRQ number of the interrupting device or NVRM_IRQ_INVALID + * if no interrupting device was found. + */ + + +/** + * Performs secondary IRQ interrupt decode for interrupting devices + * that are interrupt sub-controllers. + * + * @param hRmDevice The RM device handle. + * @param Irq Primary IRQ number returned from NvRmInterruptPrimaryDecodeIrq(). + * @returns The IRQ number of the interrupting device. + */ + + + +/** + * Performs primary interrupt decode for FIQ interrupts in the main + * interrupt controllers. + * + * @param hRmDevice The RM device handle. + * @returns The IRQ number of the interrupting device or NVRM_IRQ_INVALID + * if no interrupting device was found. + */ + + + +/** + * Performs secondary FIQ interrupt decode for interrupting devices + * that are interrupt sub-controllers. + * + * @param hRmDevice The RM device handle. + * @param Fiq Primary FIQ number returned from NvRmInterruptPrimaryDecodeFiq(). + * @returns The FIQ number of the interrupting device. + */ + + +/** + * Suspend the dma. + */ +NvError NvRmPrivDmaSuspend(void); + +/** + * Resume the dma. + */ +NvError NvRmPrivDmaResume(void); + +/** + * Check Bond Out to make a module/instance invalid. + * + * @param hRm The RM device handle + */ +void NvRmPrivCheckBondOut( NvRmDeviceHandle hDevice ); + +/** Returns bond out values and table for AP20 */ +void NvRmPrivAp20GetBondOut( NvRmDeviceHandle hDevice, + const NvU32 **pTable, NvU32 *bondOut ); + +/** + * This API should be sapringly used. There is a bug in the chiplib where the + * interrupt handler is not passed an argument. So, the handler will call this + * function to get the Rm handle. + */ +NvRmDeviceHandle NvRmPrivGetRmDeviceHandle( void ); + +/** Returns the pointer to the relocation table of AP15 chip */ +NvU32 *NvRmPrivAp15GetRelocationTable( NvRmDeviceHandle hDevice ); + +/** Returns the pointer to the relocation table of AP16 chip */ +NvU32 *NvRmPrivAp16GetRelocationTable( NvRmDeviceHandle hDevice ); + +/** Returns the pointer to the relocation table of AP20 chip */ +NvU32 *NvRmPrivAp20GetRelocationTable( NvRmDeviceHandle hDevice ); + +/** Basic reset of AP15 chip modules */ +void NvRmPrivAp15BasicReset( NvRmDeviceHandle hDevice ); +/** Basic reset of AP20 chip modules */ +void NvRmPrivAp20BasicReset( NvRmDeviceHandle hDevice ); + +/** This API starts the memory controller error monitoring for AP15/AP16. */ +NvError NvRmPrivAp15McErrorMonitorStart( NvRmDeviceHandle hDevice ); + +/** This API stops the memory controller error monitoring for AP15/AP16. */ +void NvRmPrivAp15McErrorMonitorStop( NvRmDeviceHandle hDevice ); + +/** This API starts the memory controller error monitoring for AP20. */ +NvError NvRmPrivAp20McErrorMonitorStart( NvRmDeviceHandle hDevice ); + +/** This API stops the memory controller error monitoring for AP20. */ +void NvRmPrivAp20McErrorMonitorStop( NvRmDeviceHandle hDevice ); + +/** This API sets up the memory controller for AP15/AP16. */ +void NvRmPrivAp15SetupMc(NvRmDeviceHandle hRm); + +/** This API sets up the memory controller for AP20. */ +void NvRmPrivAp20SetupMc(NvRmDeviceHandle hRm); + +/** This API sets up AP20 MC for stat collection */ +void McStatAp20_Start(NvRmDeviceHandle rm, NvU32 client_id_0, NvU32 client_id_1, NvU32 llc_client_id); + +/** This API stops stat collection for AP20 MC */ +void McStatAp20_Stop(NvRmDeviceHandle rm, NvU32 *client_0_cycles, + NvU32 *client_1_cycles, NvU32 *llc_client_cycles, + NvU32 *llc_client_clocks, NvU32 *mc_clocks); + + +/* init and deinit the keylist */ +NvError NvRmPrivInitKeyList(NvRmDeviceHandle hRm, const NvU32*, NvU32); +void NvRmPrivDeInitKeyList(NvRmDeviceHandle hRm); + +/** + * @brief Query the max interface freq supported by the board for a given + * Module. + * + * This API returns the max interface freq supported by the board based on the + * ODM query. + */ +NvRmFreqKHz +NvRmPrivGetInterfaceMaxClock( + NvRmDeviceHandle hRmDevice, + NvRmModuleID ModuleId); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif // AP15RM_PRIVATE_H diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_reloctable.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_reloctable.c new file mode 100644 index 000000000000..1854820e6de9 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_reloctable.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvos.h" +#include "nvrm_init.h" +#include "common/nvrm_hwintf.h" +#include "ap15/project_relocation_table.h" +#include "ap15rm_private.h" + +static NvU32 s_RelocationTable[] = +{ + NV_RELOCATION_TABLE_INIT +}; + +NvU32 * +NvRmPrivAp15GetRelocationTable( NvRmDeviceHandle hDevice ) +{ + return s_RelocationTable; +} + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c new file mode 100644 index 000000000000..8b92ae4b5933 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief nVIDIA Driver Development Kit: + * Cross Proc Communication driver + * + * @b Description: Implements the interface to the NvDdk XPC. + * + */ + +#include "nvrm_xpc.h" +#include "nvrm_memmgr.h" +#include "ap15rm_xpc_hw_private.h" +#include "nvrm_hardware_access.h" +#include "nvassert.h" +#include "ap15/ararb_sema.h" +#include "ap15/arictlr_arbgnt.h" +#include "nvrm_avp_shrd_interrupt.h" + +// Minimum sdram offset required so that avp can access the address which is +// passed. +// AVP can not access the 0x0000:0000 to 0x0000:0040 +enum { MIN_SDRAM_OFFSET = 0x100}; + + +//There are only 32 arb semaphores +#define MAX_ARB_NUM 32 + +#define ARBSEMA_REG_READ(pArbSemaVirtAdd, reg) \ + NV_READ32(pArbSemaVirtAdd + (ARB_SEMA_##reg##_0)) + +#define ARBSEMA_REG_WRITE(pArbSemaVirtAdd, reg, data) \ + NV_WRITE32(pArbSemaVirtAdd + (ARB_SEMA_##reg##_0), (data)); + +#define ARBGNT_REG_READ(pArbGntVirtAdd, reg) \ + NV_READ32(pArbGntVirtAdd + (ARBGNT_##reg##_0)) + +#define ARBGNT_REG_WRITE(pArbGntVirtAdd, reg, data) \ + NV_WRITE32(pArbGntVirtAdd + (ARBGNT_##reg##_0), (data)); + +static NvOsInterruptHandle s_arbInterruptHandle = NULL; + +// Combines the Processor Xpc system details. This contains the details of the +// receive/send message queue and messaging system. +typedef struct NvRmPrivXpcMessageRec +{ + NvRmDeviceHandle hDevice; + + // Hw mail box register. + CpuAvpHwMailBoxReg HwMailBoxReg; + +} NvRmPrivXpcMessage; + +typedef struct NvRmPrivXpcArbSemaRec +{ + NvRmDeviceHandle hDevice; + NvU8 *pArbSemaVirtAddr; + NvU8 *pArbGntVirtAddr; + NvOsSemaphoreHandle semaphore[MAX_ARB_NUM]; + NvOsMutexHandle mutex[MAX_ARB_NUM]; + NvOsIntrMutexHandle hIntrMutex; + +} NvRmPrivXpcArbSema; + +static NvRmPrivXpcArbSema s_ArbSema; + +//Forward declarations +static NvError InitArbSemaSystem(NvRmDeviceHandle hDevice); +static void ArbSemaIsr(void *args); +NvU32 GetArbIdFromRmModuleId(NvRmModuleID modId); +/** + * Initialize the cpu avp hw mail box address and map the hw register address + * to virtual address. + * Thread Safety: Caller responsibility + */ +static NvError +InitializeCpuAvpHwMailBoxRegister(NvRmPrivXpcMessageHandle hXpcMessage) +{ + NvError e; + NvRmPhysAddr ResourceSemaPhysAddr; + + // Get base address of the hw mail box register. This register is in the set + // of resource semaphore module Id. + NvRmModuleGetBaseAddress(hXpcMessage->hDevice, + NVRM_MODULE_ID(NvRmModuleID_ResourceSema, 0), + &ResourceSemaPhysAddr, &hXpcMessage->HwMailBoxReg.BankSize); + + // Map the base address to the virtual address. + hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL; + NV_CHECK_ERROR(NvRmPhysicalMemMap( + ResourceSemaPhysAddr, hXpcMessage->HwMailBoxReg.BankSize, + NVOS_MEM_READ_WRITE, NvOsMemAttribute_Uncached, + (void **)&hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr)); + + NvRmPrivXpcHwResetOutbox(&hXpcMessage->HwMailBoxReg); + + return NvSuccess; +} + +/** + * DeInitialize the cpu avp hw mail box address and unmap the hw register address + * virtual address. + * Thread Safety: Caller responsibility + */ +static void DeInitializeCpuAvpHwMailBoxRegister(NvRmPrivXpcMessageHandle hXpcMessage) +{ + // Unmap the hw register base virtual address + NvRmPhysicalMemUnmap(hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr, + hXpcMessage->HwMailBoxReg.BankSize); + hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL; +} + +/** + * Create the cpu-avp messaging system. + * This function will call other helper function to create the messaging technique + * used for cpu-avp communication. + * Thread Safety: Caller responsibility + */ +static NvError +CreateCpuAvpMessagingSystem(NvRmPrivXpcMessageHandle hXpcMessage) +{ + NvError Error = NvSuccess; + + Error = InitializeCpuAvpHwMailBoxRegister(hXpcMessage); + +#if NV_IS_AVP + hXpcMessage->HwMailBoxReg.IsCpu = NV_FALSE; +#else + hXpcMessage->HwMailBoxReg.IsCpu = NV_TRUE; +#endif + + // If error found then destroy all the allocation and initialization, + if (Error) + DeInitializeCpuAvpHwMailBoxRegister(hXpcMessage); + + return Error; +} + + +/** + * Destroy the cpu-avp messaging system. + * This function destroy all the allocation/initialization done for creating + * the cpu-avp messaging system. + * Thread Safety: Caller responsibility + */ +static void DestroyCpuAvpMessagingSystem(NvRmPrivXpcMessageHandle hXpcMessage) +{ + // Destroy the cpu-avp hw mail box registers. + DeInitializeCpuAvpHwMailBoxRegister(hXpcMessage); + hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL; + hXpcMessage->HwMailBoxReg.BankSize = 0; +} + + +NvError +NvRmPrivXpcCreate( + NvRmDeviceHandle hDevice, + NvRmPrivXpcMessageHandle *phXpcMessage) +{ + NvError Error = NvSuccess; + NvRmPrivXpcMessageHandle hNewXpcMsgHandle = NULL; + + *phXpcMessage = NULL; + + // Allocates the memory for the xpc message handle. + hNewXpcMsgHandle = NvOsAlloc(sizeof(*hNewXpcMsgHandle)); + if (!hNewXpcMsgHandle) + { + return NvError_InsufficientMemory; + } + + // Initialize all the members of the xpc message handle. + hNewXpcMsgHandle->hDevice = hDevice; + hNewXpcMsgHandle->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL; + hNewXpcMsgHandle->HwMailBoxReg.BankSize = 0; + + // Create the messaging system between the processors. + Error = CreateCpuAvpMessagingSystem(hNewXpcMsgHandle); + + // if error the destroy all allocations done here. + if (Error) + { + NvOsFree(hNewXpcMsgHandle); + hNewXpcMsgHandle = NULL; + } + +#if NV_IS_AVP + Error = InitArbSemaSystem(hDevice); + if (Error) + { + NvOsFree(hNewXpcMsgHandle); + hNewXpcMsgHandle = NULL; + } +#endif + + // Copy the new xpc message handle into the passed parameter. + *phXpcMessage = hNewXpcMsgHandle; + return Error; +} + + +/** + * Destroy the Rm Xpc message handle. + * Thread Safety: It is provided inside the function. + */ +void NvRmPrivXpcDestroy(NvRmPrivXpcMessageHandle hXpcMessage) +{ + // If not a null pointer then destroy. + if (hXpcMessage) + { + // Destroy the messaging system between processor. + DestroyCpuAvpMessagingSystem(hXpcMessage); + + // Free the allocated memory for the xpc message handle. + NvOsFree(hXpcMessage); + } +} + + +// Set the outbound mailbox with the given data. We might have to spin until +// it's safe to send the message. +NvError +NvRmPrivXpcSendMessage(NvRmPrivXpcMessageHandle hXpcMessage, NvU32 data) +{ + NvRmPrivXpcHwSendMessageToTarget(&hXpcMessage->HwMailBoxReg, data); + return NvSuccess; +} + + +// Get the value currently in the inbox register. This read clears the incoming +// interrupt. +NvU32 +NvRmPrivXpcGetMessage(NvRmPrivXpcMessageHandle hXpcMessage) +{ + NvU32 data; + NvRmPrivXpcHwReceiveMessageFromTarget(&hXpcMessage->HwMailBoxReg, &data); + return data; +} + +NvError NvRmXpcInitArbSemaSystem(NvRmDeviceHandle hDevice) +{ +#if NV_IS_AVP + return NvSuccess; +#else + return InitArbSemaSystem(hDevice); +#endif +} + +static NvError InitArbSemaSystem(NvRmDeviceHandle hDevice) +{ + NvOsInterruptHandler ArbSemaHandler; + NvRmPhysAddr ArbSemaBase, ArbGntBase; + NvU32 ArbSemaSize, ArbGntSize; + NvU32 irq; + NvError e; + NvU32 i = 0; + + irq = NvRmGetIrqForLogicalInterrupt( + hDevice, NvRmModuleID_ArbitrationSema, 0); + + ArbSemaHandler = ArbSemaIsr; + + NV_CHECK_ERROR_CLEANUP( + NvRmInterruptRegister(hDevice, 1, &irq, &ArbSemaHandler, + hDevice, &s_arbInterruptHandle, NV_TRUE) + ); + + NvRmModuleGetBaseAddress(hDevice, + NVRM_MODULE_ID(NvRmModuleID_ArbitrationSema, 0), + &ArbSemaBase, &ArbSemaSize); + + NvRmModuleGetBaseAddress(hDevice, + NVRM_MODULE_ID(NvRmPrivModuleID_InterruptArbGnt, 0), + &ArbGntBase, &ArbGntSize); + + NV_CHECK_ERROR_CLEANUP( + NvRmPhysicalMemMap(ArbSemaBase, ArbSemaSize, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, (void**)&s_ArbSema.pArbSemaVirtAddr) + ); + + NV_CHECK_ERROR_CLEANUP( + NvRmPhysicalMemMap(ArbGntBase, ArbGntSize, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, (void**)&s_ArbSema.pArbGntVirtAddr) + ); + + //Initialize all the semaphores and mutexes + for (i=0;i>= 1; + i++; + + } while (int_mask); + + NvOsIntrMutexUnlock(s_ArbSema.hIntrMutex); + NvRmInterruptDone(s_arbInterruptHandle); +} + +NvU32 GetArbIdFromRmModuleId(NvRmModuleID modId) +{ + NvU32 arbId; + + switch(modId) + { + case NvRmModuleID_BseA: + arbId = NvRmArbSema_Bsea; + break; + case NvRmModuleID_Vde: + default: + arbId = NvRmArbSema_Vde; + break; + } + + return arbId; +} + +void NvRmXpcModuleAcquire(NvRmModuleID modId) +{ + NvU32 RequestedSemaNum; + NvU32 reg; + + RequestedSemaNum = GetArbIdFromRmModuleId(modId); + + NvOsMutexLock(s_ArbSema.mutex[RequestedSemaNum]); + NvOsIntrMutexLock(s_ArbSema.hIntrMutex); + + //Try to grab the lock + ARBSEMA_REG_WRITE(s_ArbSema.pArbSemaVirtAddr, SMP_GET, 1 << RequestedSemaNum); + + //Enable arb sema interrupt +#if NV_IS_AVP + reg = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, COP_ENABLE); + reg |= (1 << RequestedSemaNum); + ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr, COP_ENABLE, reg); +#else + reg = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, CPU_ENABLE); + reg |= (1 << RequestedSemaNum); + ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr, CPU_ENABLE, reg); +#endif + + NvOsIntrMutexUnlock(s_ArbSema.hIntrMutex); + NvOsSemaphoreWait(s_ArbSema.semaphore[RequestedSemaNum]); +} + +void NvRmXpcModuleRelease(NvRmModuleID modId) +{ + NvU32 RequestedSemaNum; + + RequestedSemaNum = GetArbIdFromRmModuleId(modId); + + //Release the lock + ARBSEMA_REG_WRITE(s_ArbSema.pArbSemaVirtAddr, SMP_PUT, 1 << RequestedSemaNum); + + NvOsMutexUnlock(s_ArbSema.mutex[RequestedSemaNum]); +} diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c new file mode 100644 index 000000000000..ffd1dc5d6ebd --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief nVIDIA Driver Development Kit: + * Cross Processor Communication driver + * + * @b Description: Implements the cross processor communication Hw Access APIs + * + */ + +#include "nvcommon.h" +#include "nvassert.h" +#include "nvrm_drf.h" +#include "nvrm_hardware_access.h" +#include "ap15rm_xpc_hw_private.h" +#include "ap15/arres_sema.h" + +enum {MESSAGE_BOX_MESSAGE_LENGTH_BITS = 28}; +#define RESSEMA_REG_READ32(pResSemaHwRegVirtBaseAdd, reg) \ + NV_READ32((pResSemaHwRegVirtBaseAdd) + (RES_SEMA_##reg##_0)/4) + +#define RESSEMA_REG_WRITE32(pResSemaHwRegVirtBaseAdd, reg, val) \ + do { \ + NV_WRITE32(((pResSemaHwRegVirtBaseAdd) + ((RES_SEMA_##reg##_0)/4)), (val)); \ + } while(0) + +void NvRmPrivXpcHwResetOutbox(CpuAvpHwMailBoxReg *pHwMailBoxReg) +{ + NvU32 OutboxMessage; + NvU32 OutboxVal; + + OutboxMessage = 0; + + // Write Outbox in the message box + // Enable the Valid tag + // Enable interrupt +#if NV_IS_AVP + OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_INBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_DATA, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_CMD, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IE_IBE, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, TAG, 0, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_INBOX, OutboxVal); +#else + OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_OUTBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_CMD, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_DATA, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, IE_OBE, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, TAG, 0, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_OUTBOX, OutboxVal); +#endif +} + + +/** + * Send message to the target. + */ +void +NvRmPrivXpcHwSendMessageToTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr MessageAddress) +{ + NvU32 OutboxMessage; + NvU32 OutboxVal = 0; + + OutboxMessage = ((NvU32)(MessageAddress)) >> (32 - MESSAGE_BOX_MESSAGE_LENGTH_BITS); + + // Write Outbox in the message box + // Enable the Valid tag + // Enable interrupt +#if NV_IS_AVP + // !!! not sure why this would need to be read/modify/write +// OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_INBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_DATA, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_CMD, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_INBOX, IE_IBF, FULL, OutboxVal); +// OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_INBOX, IE_IBE, EMPTY, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_INBOX, TAG, VALID, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_INBOX, OutboxVal); +#else + // !!! not sure why this would need to be read/modify/write +// OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_OUTBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_CMD, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_DATA, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_OUTBOX, IE_OBF, FULL, OutboxVal); +// OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_OUTBOX, IE_OBE, EMPTY, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_OUTBOX, TAG, VALID, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_OUTBOX, OutboxVal); +#endif +} + + + +/** + * Receive message from the target. + */ +void +NvRmPrivXpcHwReceiveMessageFromTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr *pMessageAddress) +{ + NvU32 InboxMessage = 0; + NvU32 InboxVal; + + // Read the inbox. Lower 28 bit contains the message. +#if NV_IS_AVP + InboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_OUTBOX); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_OUTBOX, 0); +#else + InboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_INBOX); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_INBOX, 0); +#endif + if (InboxVal & NV_DRF_DEF(RES_SEMA, SHRD_INBOX, TAG, VALID)) + { + pHwMailBoxReg->MailBoxData = InboxVal; + } + + InboxVal = (pHwMailBoxReg->MailBoxData) & (0xFFFFFFFFUL >> (32 - MESSAGE_BOX_MESSAGE_LENGTH_BITS)); + InboxMessage = (InboxVal << (32 - MESSAGE_BOX_MESSAGE_LENGTH_BITS)); + + *pMessageAddress = InboxMessage; +} + + + + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h new file mode 100644 index 000000000000..c5822526b9c8 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief nVIDIA Driver Development Kit: + * Priate Hw access function for XPC driver + * + * @b Description: Defines the private interface functions for the xpc + * + */ + +#ifndef INCLUDED_RM_XPC_HW_PRIVATE_H +#define INCLUDED_RM_XPC_HW_PRIVATE_H + + +#include "nvcommon.h" +#include "nvrm_init.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// Combines the cpu avp hw mail baox system information. +typedef struct CpuAvpHwMailBoxRegRec +{ + // Hw mail box register virtual base address. + NvU32 *pHwMailBoxRegBaseVirtAddr; + + // Bank size of the hw regsiter. + NvU32 BankSize; + + // Tells whether this is on cpu or on Avp + NvBool IsCpu; + + // Mail box data which was read last time. + NvU32 MailBoxData; +} CpuAvpHwMailBoxReg; + +void NvRmPrivXpcHwResetOutbox(CpuAvpHwMailBoxReg *pHwMailBoxReg); + +/** + * Send message to the target. + */ +void +NvRmPrivXpcHwSendMessageToTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr MessageAddress); + +/** + * Receive message from the target. + */ +void +NvRmPrivXpcHwReceiveMessageFromTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr *pMessageAddress); + + +#if defined(__cplusplus) + } +#endif + +#endif // INCLUDED_RM_XPC_HW_PRIVATE_H diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap16rm_pinmux_tables.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap16rm_pinmux_tables.c new file mode 100644 index 000000000000..09c53e93d9af --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap16rm_pinmux_tables.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2008-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvrm_pinmux.h" +#include "nvrm_drf.h" +#include "nvassert.h" +#include "nvrm_hwintf.h" +#include "ap15rm_private.h" +#include "ap16/arapb_misc.h" +#include "ap15/arclk_rst.h" +#include "ap15rm_pinmux_utils.h" +#include "nvodm_query_pinmux.h" +#include "nvrm_clocks.h" + +extern const NvU32 g_Ap15MuxI2c1[]; +extern const NvU32 g_Ap15MuxI2c2[]; +extern const NvU32* g_Ap15MuxI2c[]; + +extern const NvU32 g_Ap15MuxI2c_Pmu[]; +extern const NvU32* g_Ap15MuxI2cPmu[]; + +extern const NvU32 g_Ap15Mux_Mmc[]; +extern const NvU32* g_Ap15MuxMmc[]; + +extern const NvU32 g_Ap15MuxSdio2[]; +extern const NvU32 g_Ap15MuxSdio3[]; +extern const NvU32* g_Ap15MuxSdio[]; + +extern const NvU32 g_Ap15Mux_Spdif[]; +extern const NvU32* g_Ap15MuxSpdif[]; + +static const NvU32 g_Ap16MuxUart1[] = { + // Reset config - abandon IRRX, IRTX & SDD + UNCONFIG(C,IRRX,UARTA,RSVD2), UNCONFIG(C,IRTX,UARTA,RSVD2), UNCONFIG(D,SDD,UARTA,PWM), CONFIGEND(), + // 8b UAA + UAB pads + CONFIG(B,A,UAA,UARTA), CONFIG(B,A,UAB,UARTA), CONFIGEND(), + // 4b UAA pads + CONFIG(B,A,UAA,UARTA), CONFIGEND(), + // 7b GPU pads + CONFIG(A,D,GPU,UARTA), CONFIGEND(), + // 4b VFIR + UAD pads + CONFIG(A,C,IRRX,UARTA), CONFIG(A,C,IRTX,UARTA), CONFIG(B,A,UAD,UARTA), CONFIGEND(), + // 2b VFIR pads + CONFIG(A,C,IRRX,UARTA), CONFIG(A,C,IRTX,UARTA), CONFIGEND(), + // 2b SDIO pads + CONFIG(B,D,SDD,UARTA), CONFIGEND(), + MODULEDONE() +}; +static const NvU32 g_Ap16MuxUart2[] = { +// Reset config - abandon UAD. pads.chosen SFLASH pads + UNCONFIG(A,UAD,IRDA,SFLASH), CONFIGEND(), +// 4b UAD + IRRX + IRTX pads + CONFIG(B,A,UAD,IRDA), CONFIG(A,C,IRRX,UARTB), CONFIG(A,C,IRTX,UARTB), CONFIGEND(), +//..2b UAD pads + CONFIG(B,A,UAD,IRDA), CONFIGEND(), + MODULEDONE() +}; + +static const NvU32 g_Ap16MuxUart3[] = { + // Reset config - abandon UCA. chosen RSVD1 + UNCONFIG(B,UCA,UARTC,RSVD1), CONFIGEND(), + // 4b UCA + UCB pads + CONFIG(B,B,UCA,UARTC), CONFIG(B,B,UCB,UARTC), CONFIGEND(), + // 2b UCA pads + CONFIG(B,B,UCA,UARTC), CONFIGEND(), + MODULEDONE() +}; + +static const NvU32* g_Ap16MuxUart[] = { + &g_Ap16MuxUart1[0], + &g_Ap16MuxUart2[0], + &g_Ap16MuxUart3[0], + NULL +}; +extern const NvU32 g_Ap15MuxSpi1[]; +extern const NvU32 g_Ap15MuxSpi2[]; +extern const NvU32 g_Ap15MuxSpi3[]; +extern const NvU32* g_Ap15MuxSpi[]; + +extern const NvU32 g_Ap15Mux_Sflash[]; +extern const NvU32* g_Ap15MuxSflash[]; + +extern const NvU32 g_Ap15Mux_Twc[]; +extern const NvU32* g_Ap15MuxTwc[]; + +extern const NvU32 g_Ap15Mux_Ata[]; +extern const NvU32* g_Ap15MuxAta[]; + +extern const NvU32 g_Ap15Mux_Pwm[]; +extern const NvU32* g_Ap15MuxPwm[]; + +extern const NvU32 g_Ap15Mux_Hsi[]; +extern const NvU32 *g_Ap15MuxHsi[]; + +extern const NvU32 g_Ap15Mux_Nand[]; +extern const NvU32* g_Ap15MuxNand[]; + +extern const NvU32 g_Ap15MuxDap1[]; +extern const NvU32 g_Ap15MuxDap2[]; +extern const NvU32 g_Ap15MuxDap3[]; +extern const NvU32 g_Ap15MuxDap4[]; +extern const NvU32* g_Ap15MuxDap[]; + +extern const NvU32 g_Ap15Mux_Kbc[]; +extern const NvU32* g_Ap15MuxKbc[]; + +extern const NvU32 g_Ap15Mux_Hdcp[]; +extern const NvU32* g_Ap15MuxHdcp[]; + +extern const NvU32 g_Ap15Mux_Hdmi[]; +extern const NvU32* g_Ap15MuxHdmi[]; + +extern const NvU32 g_Ap15Mux_Mio[]; +extern const NvU32* g_Ap15MuxMio[]; + +extern const NvU32 g_Ap15Mux_Slink[]; +extern const NvU32* g_Ap15MuxSlink[]; + +extern const NvU32 g_Ap15Mux_Vi[]; +extern const NvU32* g_Ap15MuxVi[]; + +extern const NvU32 g_Ap15Mux_Crt[]; +extern const NvU32* g_Ap15MuxCrt[]; + +extern const NvU32 g_Ap15Mux_Display1[]; +extern const NvU32 g_Ap15Mux_Display2[]; +extern const NvU32* g_Ap15MuxDisplay[]; + +extern const NvU32 g_Ap15Mux_Cdev1[]; + +extern const NvU32 g_Ap15Mux_Cdev2[]; +extern const NvU32 g_Ap15Mux_Csus[]; +extern const NvU32* g_Ap15MuxCdev[]; + +extern const NvU32 g_Ap15Mux_BacklightDisplay1Pwm0[]; +extern const NvU32 g_Ap15Mux_BacklightDisplay1Pwm1[]; +extern const NvU32 g_Ap15Mux_BacklightDisplay2Pwm0[]; +extern const NvU32 g_Ap15Mux_BacklightDisplay2Pwm1[]; +extern const NvU32* g_Ap15MuxBacklight[]; + +static const NvU32 g_Ap16Mux_Ulpi[] = { + CONFIGEND(), // no pad groups reset to ULPI, so nothing to disown for reset config + CONFIG(B,A,UAA,ULPI), CONFIG(B,A,UAB,ULPI), CONFIG(B,A,UAC,ULPI), CONFIGEND(), + MODULEDONE() +}; +static const NvU32* g_Ap16MuxUlpi[] = { + &g_Ap16Mux_Ulpi[0], + NULL +}; +/* Array of all the controller types in the system, pointing to the array of + * instances of each controller. Indexed using the NvRmIoModule value. + */ +static const NvU32** g_Ap16MuxControllers[] = { + &g_Ap15MuxAta[0], + &g_Ap15MuxCrt[0], + NULL, // no options for CSI + &g_Ap15MuxDap[0], + &g_Ap15MuxDisplay[0], + NULL, // no options for DSI + NULL, // no options for GPIO + &g_Ap15MuxHdcp[0], + &g_Ap15MuxHdmi[0], + &g_Ap15MuxHsi[0], + &g_Ap15MuxMmc[0], + NULL, // no options for I2S + &g_Ap15MuxI2c[0], + &g_Ap15MuxI2cPmu[0], + &g_Ap15MuxKbc[0], + &g_Ap15MuxMio[0], + &g_Ap15MuxNand[0], + &g_Ap15MuxPwm[0], + &g_Ap15MuxSdio[0], + &g_Ap15MuxSflash[0], + &g_Ap15MuxSlink[0], + &g_Ap15MuxSpdif[0], + &g_Ap15MuxSpi[0], + &g_Ap15MuxTwc[0], + NULL, // no options for TVO + &g_Ap16MuxUart[0], + NULL, // no options for USB + NULL, // no options for VDD + &g_Ap15MuxVi[0], + NULL, // no options for XIO + &g_Ap15MuxCdev[0], + &g_Ap16MuxUlpi[0], + NULL, + NULL, + NULL, + NULL, + NULL, // no options for TSENSor + &g_Ap15MuxBacklight[0], +}; + +NV_CT_ASSERT(NV_ARRAY_SIZE(g_Ap16MuxControllers)==NvOdmIoModule_Num); + +const NvU32*** +NvRmAp16GetPinMuxConfigs(NvRmDeviceHandle hDevice) +{ + NV_ASSERT(hDevice); + return (const NvU32***) g_Ap16MuxControllers; +} + +NvBool NvRmPrivAp16RmModuleToOdmModule( + NvRmModuleID RmModule, + NvOdmIoModule *OdmModule, + NvU32 *OdmInstance, + NvU32 *pCnt) +{ + NvRmModuleID Module = NVRM_MODULE_ID_MODULE(RmModule); + NvU32 Instance = NVRM_MODULE_ID_INSTANCE(RmModule); + + *OdmInstance = Instance; + + switch (Module) + { + case NvRmModuleID_Usb2Otg: + if (Instance == 0) + { + *OdmModule = NvOdmIoModule_Usb; + *OdmInstance = 0; + } + else + { + // stop here for instance otherthan one + NV_ASSERT(Instance == 1); + *OdmModule = NvOdmIoModule_Ulpi; + *OdmInstance = 0; + } + *pCnt = 1; + return NV_TRUE; + default: + break; + } + + return NvRmPrivAp15RmModuleToOdmModule(RmModule, + OdmModule, OdmInstance, pCnt); +} + + +NvError +NvRmPrivAp16GetModuleInterfaceCaps( + NvOdmIoModule Module, + NvU32 Instance, + NvU32 PinMap, + void *pCaps) +{ + switch (Module) + { + case NvOdmIoModule_Uart: + if (Instance == 0) + { + if (PinMap == NvOdmUartPinMap_Config1) + ((NvRmModuleUartInterfaceCaps *)pCaps)->NumberOfInterfaceLines = 8; + else if (PinMap == NvOdmUartPinMap_Config3) + ((NvRmModuleUartInterfaceCaps *)pCaps)->NumberOfInterfaceLines = 7; + else if ((PinMap == NvOdmUartPinMap_Config2) || (PinMap == NvOdmUartPinMap_Config4)) + ((NvRmModuleUartInterfaceCaps *)pCaps)->NumberOfInterfaceLines = 4; + else if ((PinMap == NvOdmUartPinMap_Config5) || (PinMap == NvOdmUartPinMap_Config6)) + ((NvRmModuleUartInterfaceCaps *)pCaps)->NumberOfInterfaceLines = 2; + else + ((NvRmModuleUartInterfaceCaps *)pCaps)->NumberOfInterfaceLines = 0; + } + else if (Instance == 1) + { + if (PinMap == NvOdmUartPinMap_Config1) + ((NvRmModuleUartInterfaceCaps *)pCaps)->NumberOfInterfaceLines = 4; + else if (PinMap == NvOdmUartPinMap_Config2) + ((NvRmModuleUartInterfaceCaps *)pCaps)->NumberOfInterfaceLines = 2; + else + ((NvRmModuleUartInterfaceCaps *)pCaps)->NumberOfInterfaceLines = 0; + } + else if (Instance == 2) + { + if (PinMap == NvOdmUartPinMap_Config1) + ((NvRmModuleUartInterfaceCaps *)pCaps)->NumberOfInterfaceLines = 4; + else if (PinMap == NvOdmUartPinMap_Config2) + ((NvRmModuleUartInterfaceCaps *)pCaps)->NumberOfInterfaceLines = 2; + else + ((NvRmModuleUartInterfaceCaps *)pCaps)->NumberOfInterfaceLines = 0; + } + else + { + NV_ASSERT(NV_FALSE); + return NvError_NotSupported; + } + return NvSuccess; + + default: + break; + } + return NvRmPrivAp15GetModuleInterfaceCaps(Module, Instance, PinMap, pCaps); +} + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap16rm_reloctable.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap16rm_reloctable.c new file mode 100644 index 000000000000..99a689921161 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap16rm_reloctable.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvos.h" +#include "nvrm_init.h" +#include "common/nvrm_hwintf.h" +#include "ap16/project_relocation_table.h" +#include "ap15rm_private.h" + +static NvU32 s_RelocationTable[] = +{ + NV_RELOCATION_TABLE_INIT +}; + +NvU32 * +NvRmPrivAp16GetRelocationTable( NvRmDeviceHandle hDevice ) +{ + return s_RelocationTable; +} + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/nvrm_clocks.c b/arch/arm/mach-tegra/nvrm/core/ap15/nvrm_clocks.c new file mode 100644 index 000000000000..40e8837f9e11 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/nvrm_clocks.c @@ -0,0 +1,3311 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief nVIDIA Driver Development Kit: + * Clock Resource manager + * + * @b Description: Implements Clock control API. All code in this file chip + * independent. All chip dependent code should move to ap15rm_clocks.c file. + */ + +#include "nvrm_clocks.h" +#include "nvassert.h" +#include "nvrm_drf.h" +#include "nvrm_chiplib.h" +#include "nvrm_hwintf.h" +#include "ap15rm_private.h" +#include "ap15rm_clocks.h" +#include "ap20/ap20rm_clocks.h" +#include "nvrm_pmu_private.h" +#include "nvrm_pinmux_utils.h" +#include "nvodm_query_pinmux.h" +#include "nvodm_query_discovery.h" + +// Module debug: 0=disable, 1=enable +#define NVRM_ENABLE_PRINTF (0) + +#if (NV_DEBUG && NVRM_ENABLE_PRINTF) +#define NVRM_POWER_PRINTF(x) NvOsDebugPrintf x +#else +#define NVRM_POWER_PRINTF(x) +#endif + +// TODO: Replace NvOsWaitUS() with NvRmPrivWaitUS() +// TODO: CAR access macro + +// Actual FPGA clock frequency for all modules is 8.33MHz +// (display is exception) +#define FPGA_MODULE_KHZ_AP15 (8330) +#define FPGA_MODULE_KHZ_AP20 (13000) +#define FPGA_DISPLAY_KHZ (27000) + +// QT clock frequency used as a special value (actual frequency is irrelevant) +#define QT_MODULE_KHZ (1) + +// UART rate divider is part of the UART module and it is not discribed +// in central module clock information table. Hence, need this define. +#define NVRM_UART_TIMING_DIVISOR_MAX (0xFFFFUL) + +/*****************************************************************************/ + +// Clock source descriptors and frequencies +static NvRmClockSourceInfo* s_ClockSourceTable = NULL; +static NvU32 s_ClockSourceFreq[NvRmClockSource_Num]; +static NvRmSystemBusComplexInfo s_SystemBusComplex = {0}; + +// Module clocks frequency limits +static const NvRmModuleClockLimits* s_ModuleClockLimits; + +// Module clocks descriptors and module clock state arrays of the same size +static const NvRmModuleClockInfo *s_moduleClockTable; +static NvU32 s_moduleClockTableSize; +static NvRmModuleClockState *s_moduleClockState = NULL; + +// PLL references +static NvRmPllReference* s_PllReferencesTable; +static NvU32 s_PllReferencesTableSize; +static NvBool s_MipiPllVddOn = NV_FALSE; + +// Mutex for thread-safe access to clock control records and h/w +static NvOsSpinMutexHandle s_hClockMutex = NULL; + +// Mutex for thread-safe access to shared PLLs +static NvOsMutexHandle s_hPllMutex = NULL; + +/*****************************************************************************/ + +NvError +NvRmPrivGetClockState( + NvRmDeviceHandle hDevice, + NvRmModuleID ModuleId, + NvRmModuleClockInfo** CinfoOut, + NvRmModuleClockState** StateOut) +{ + NvRmModuleInstance* inst; + + NV_ASSERT( hDevice ); + NV_ASSERT(s_moduleClockState); + + if (NvRmPrivGetModuleInstance(hDevice, ModuleId, &inst) != NvSuccess) + { + return NvError_ModuleNotPresent; + } + if (inst->ModuleData) + { + *CinfoOut = (NvRmModuleClockInfo*)inst->ModuleData; + *StateOut = &s_moduleClockState[(*CinfoOut) - s_moduleClockTable]; + return NvSuccess; + } + else + { + // Starting with AP20 no dedicated HSMMC clock (mapped to SDMMC) + if ((ModuleId == NvRmModuleID_Hsmmc) && + (hDevice->ChipId.Id != 0x15) && (hDevice->ChipId.Id != 0x16)) + { + return NvError_ModuleNotPresent; + } + NV_ASSERT(!"module clock info missing --" + " fillup the [apxx]rm_clocks_info.c file"); + return NvError_NotSupported; + } +} + +/*****************************************************************************/ + +static void +NvRmPrivPllDPowerControl( + NvRmDeviceHandle hDevice, + NvBool ConfigEntry, + NvBool* pMipiPllVddOn) +{ + NvRmPrivAp15PllDPowerControl(hDevice, ConfigEntry, pMipiPllVddOn); +} + +static void +NvRmPrivDisablePLLs( + NvRmDeviceHandle hDevice, + const NvRmModuleClockInfo* cinfo, + const NvRmModuleClockState* state) +{ + NvRmPrivAp15DisablePLLs(hDevice, cinfo, state); +} + +static NvRmClockSource +NvRmPrivGetImplicitPllSource( + NvRmDeviceHandle hRmDevice, + NvRmModuleID Module) +{ + switch (Module) + { + // DSI, CSI, I2C and UART modules are implicitely attached to PLLP3 + // output derived from primary PLLP0. + case NvRmModuleID_Dsi: + case NvRmModuleID_Csi: + case NvRmModuleID_I2c: + case NvRmModuleID_Uart: + return NvRmClockSource_PllP0; + + // MPE depends on PLLA for audio in AP15/16 + case NvRmModuleID_Mpe: + if ((hRmDevice->ChipId.Id == 0x15) || (hRmDevice->ChipId.Id == 0x16)) + return NvRmClockSource_PllA0; + // fall through + + // No implicit dependencies for other modules + default: + return NvRmClockSource_Invalid; + } +} + +static void +NvRmPrivModuleClockAttach( + NvRmDeviceHandle hDevice, + const NvRmModuleClockInfo* cinfo, + const NvRmModuleClockState* state, + NvBool Enable) +{ + NvU32 i, reg; + NvBool Enabled; + NvRmClockSource SubSourceId = NvRmClockSource_Invalid; + NvRmClockSource SourceId = cinfo->Sources[state->SourceClock]; + + if ((cinfo->Module == NvRmModuleID_Spdif) || + (cinfo->Module == NvRmModuleID_Vi) || + (cinfo->Module == NvRmModuleID_Tvo)) + { + // Find secondary source for modules with explicit subclocks; subclock + // descriptor and state are located after main ones, respectively + SubSourceId = (cinfo + 1)->Sources[(state + 1)->SourceClock]; + } + else + { + // Find implicit secondary source (if any) for other modules + SubSourceId = NvRmPrivGetImplicitPllSource(hDevice, cinfo->Module); + + } + + NV_ASSERT(cinfo->ClkEnableOffset); + reg = NV_REGR(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + cinfo->ClkEnableOffset); + Enabled = ((reg & cinfo->ClkEnableField) == cinfo->ClkEnableField); + if (Enabled == Enable) + return; // Exit if no changes in clock status + + for (i = 0; i < s_PllReferencesTableSize; i++) + { + // If module clock is to be enabled - attach sources (inc ref count) + // If module clock is to be disabled - detach sources (dec ref count) + if (s_PllReferencesTable[i].SourceId == SourceId) + NvRmPrivPllRefUpdate(hDevice, &s_PllReferencesTable[i], Enable); + if (s_PllReferencesTable[i].SourceId == SubSourceId) + NvRmPrivPllRefUpdate(hDevice, &s_PllReferencesTable[i], Enable); + } +} + +void +NvRmPrivModuleClockReAttach( + NvRmDeviceHandle hDevice, + const NvRmModuleClockInfo* cinfo, + const NvRmModuleClockState* state) +{ + NvU32 i, reg; + NvRmClockSource SourceId = cinfo->Sources[state->SourceClock]; + + for (i = 0; i < s_PllReferencesTableSize; i++) + { + NvBool* pAttached = + &s_PllReferencesTable[i].AttachedModules[cinfo - s_moduleClockTable]; + NvBool WasAttached = *pAttached; + NvBool IsAttached = (s_PllReferencesTable[i].SourceId == SourceId); + + if (WasAttached != IsAttached) + { + // Changes in source reference always recorded but affect + // ref count only when the module clock is enabled + if(cinfo->ClkEnableOffset != 0) + { + reg = NV_REGR(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + cinfo->ClkEnableOffset); + if ((reg & cinfo->ClkEnableField) == cinfo->ClkEnableField) + { + NvRmPrivPllRefUpdate( + hDevice, &s_PllReferencesTable[i], IsAttached); + } + } + *pAttached = IsAttached; + } + } +} + +static void +NvRmPrivCoreClockReAttach( + NvRmDeviceHandle hDevice, + NvRmClockSource CoreId, + NvRmClockSource SourceId) +{ + static NvU32 s_CpuModuleIndex = (NvU32)-1; + static NvU32 s_AvpModuleIndex = (NvU32)-1; + + NvU32 i, ModuleIndex; + + // Map core bus clock to processor module. CPU, AVP are not in relocation + // table, can not use module instance shortcut - search clock descriptors. + if (CoreId == NvRmClockSource_CpuBus) + { + if (s_CpuModuleIndex == (NvU32)-1) + { + for (i = 0; i < s_moduleClockTableSize; i++) + { + if (s_moduleClockTable[i].Module == NvRmModuleID_Cpu) + break; + } + s_CpuModuleIndex = i; + } + NV_ASSERT(s_CpuModuleIndex < s_moduleClockTableSize); + ModuleIndex = s_CpuModuleIndex; + } + else if (CoreId == NvRmClockSource_SystemBus) + { + if (s_AvpModuleIndex == (NvU32)-1) + { + for (i = 0; i < s_moduleClockTableSize; i++) + { + if (s_moduleClockTable[i].Module == NvRmModuleID_Avp) + break; + } + s_AvpModuleIndex = i; + } + NV_ASSERT(s_AvpModuleIndex < s_moduleClockTableSize); + ModuleIndex = s_AvpModuleIndex; + } + else + { + NV_ASSERT(!"Invalid core id"); + return; + } + + // Map secondary divided PLL outputs to primary PLLs + switch (SourceId) + { + case NvRmClockSource_PllC1: + SourceId = NvRmClockSource_PllC0; + break; + case NvRmClockSource_PllM1: + SourceId = NvRmClockSource_PllM0; + break; + case NvRmClockSource_PllP1: + case NvRmClockSource_PllP2: + case NvRmClockSource_PllP3: + case NvRmClockSource_PllP4: + SourceId = NvRmClockSource_PllP0; + break; + default: + break; + } + + // Record changes in PLL references and update ref count + for (i = 0; i < s_PllReferencesTableSize; i++) + { + NvBool* pAttached = + &s_PllReferencesTable[i].AttachedModules[ModuleIndex]; + NvBool WasAttached = *pAttached; + NvBool IsAttached = (s_PllReferencesTable[i].SourceId == SourceId); + + if (WasAttached != IsAttached) + { + *pAttached = IsAttached; + NvRmPrivPllRefUpdate(hDevice, &s_PllReferencesTable[i], IsAttached); + } + } +} + +void +NvRmPrivMemoryClockReAttach( + NvRmDeviceHandle hDevice, + const NvRmModuleClockInfo* cinfo, + const NvRmModuleClockState* state) +{ + NvU32 i; + NvRmClockSource SourceId = cinfo->Sources[state->SourceClock]; + + // MC clock on AP20 and newer chips is always the same as EMC1x domain clock + // So there is no need for source reference double-counting. + if ((hDevice->ChipId.Id >= 0x20) && + (cinfo->Module == NvRmPrivModuleID_MemoryController)) + return; + + for (i = 0; i < s_PllReferencesTableSize; i++) + { + NvBool* pAttached = + &s_PllReferencesTable[i].AttachedModules[cinfo - s_moduleClockTable]; + NvBool WasAttached = *pAttached; + NvBool IsAttached = (s_PllReferencesTable[i].SourceId == SourceId); + + // Record changes in PLL references and update ref count. + // TODO: secondary PLL outputs mapping (only primary PLLs are used now) + if (WasAttached != IsAttached) + { + *pAttached = IsAttached; + NvRmPrivPllRefUpdate(hDevice, &s_PllReferencesTable[i], IsAttached); + } + } +} + +void +NvRmPrivExternalClockAttach( + NvRmDeviceHandle hDevice, + NvRmClockSource SourceId, + NvBool Enable) +{ + NvU32 i; + + // Map secondary divided PLL outputs to primary PLLs + switch (SourceId) + { + case NvRmClockSource_PllC1: + SourceId = NvRmClockSource_PllC0; + break; + case NvRmClockSource_PllM1: + SourceId = NvRmClockSource_PllM0; + break; + case NvRmClockSource_PllP1: + case NvRmClockSource_PllP2: + case NvRmClockSource_PllP3: + case NvRmClockSource_PllP4: + SourceId = NvRmClockSource_PllP0; + break; + default: + break; + } + + // Attach external clock + for (i = 0; i < s_PllReferencesTableSize; i++) + { + if (s_PllReferencesTable[i].SourceId == SourceId) + { + // If ext clock is enabled - attach source (inc ref count) + // If ext clock is disabled - detach source (dec ref count) + NvOsSpinMutexLock(s_hClockMutex); + s_PllReferencesTable[i].ExternalClockRefCnt += (Enable ? 1 : (-1)); + NvRmPrivPllRefUpdate(hDevice, &s_PllReferencesTable[i], Enable); + + // Configure clock source if necessary (required for PLLA) + if (SourceId == NvRmClockSource_PllA0) + NvRmPrivConfigureClockSource(hDevice, NvRmModuleID_I2s, Enable); + NvOsSpinMutexUnlock(s_hClockMutex); + } + } +} + +/*****************************************************************************/ + +void +NvRmPrivEnableModuleClock( + NvRmDeviceHandle hRmDevice, + NvRmModuleID ModuleId, + ModuleClockState ClockState) +{ + switch (hRmDevice->ChipId.Id) + { + case 0x15: + case 0x16: + Ap15EnableModuleClock(hRmDevice, ModuleId, ClockState); + break; + case 0x20: + Ap20EnableModuleClock(hRmDevice, ModuleId, ClockState); + break; + default: + NV_ASSERT(!"Unsupported chip ID"); + } +} + +NvError NvRmPowerModuleClockControl( + NvRmDeviceHandle hDevice, + NvRmModuleID ModuleId, + NvU32 ClientId, + NvBool Enable) +{ + NvRmModuleClockInfo *cinfo; + NvRmModuleClockState *state; + NvRmMilliVolts v = NvRmVoltsUnspecified; + NvError err; + ModuleClockState ClockState = + Enable ? ModuleClockState_Enable : ModuleClockState_Disable; + NvRmModuleID ModuleName = NVRM_MODULE_ID_MODULE(ModuleId); + // Clock control/configurations shared between drivers and DVFS + NvBool SharedModule = ((ModuleName == NvRmModuleID_Display) || + (ModuleName == NvRmModuleID_Dsi) || + (ModuleName == NvRmModuleID_Vde)); + + if (NvRmPrivIsDiagMode(ModuleId)) + return NvSuccess; + + // Get pointers to module clock info and current module clock state + err = NvRmPrivGetClockState(hDevice, ModuleId, &cinfo, &state); + if (err != NvSuccess) + return err; + + // Protect access to clocks that shared control (directly, or via PLLs) + // with DVFS. Made sure DSI power rail is up if DSI clock is enabled. + if (SharedModule) + { + NvOsMutexLock(s_hPllMutex); + if (Enable && (ModuleName == NvRmModuleID_Dsi)) + NvRmPrivPllDPowerControl(hDevice, NV_TRUE, &s_MipiPllVddOn); + } + NvOsSpinMutexLock(s_hClockMutex); + + // Check if voltage scaling is required before module clock is enabled. + // Core voltage access is shared with DVFS. PMU access transport must + // *not* be scalable. + if (Enable) + { + // Preview, don't update scaling ref counts if voltage going up + v = NvRmPrivModuleVscaleAttach( + hDevice, cinfo, state, Enable, NV_TRUE); + + if ((v != NvRmVoltsUnspecified) && + (v != NvRmVoltsOff)) + { + // Preview reported voltage increase - set target pending to + // prevent DVFS scaling down, while lock is released + NvRmPrivModuleVscaleSetPending(hDevice, v); + + NvOsSpinMutexUnlock(s_hClockMutex); + if (!SharedModule) + NvOsMutexLock(s_hPllMutex); + NvRmPrivDvsRequest(v); + if (!SharedModule) + NvOsMutexUnlock(s_hPllMutex); + NvOsSpinMutexLock(s_hClockMutex); + + // Now, after voltage is increased - update scaling ref counts + // and cancel pending request + v = NvRmPrivModuleVscaleAttach( + hDevice, cinfo, state, Enable, NV_FALSE); + NvRmPrivModuleVscaleSetPending(hDevice, NvRmVoltsOff); + } + } + + // Restart reference counting if it is the 1st clock control call + if (!state->FirstReference) + { + state->FirstReference = NV_TRUE; + state->refCount = 0; + } + + // Update reference count, and exit if + // - clock enable requested and module clock is already enabled + // - clock disable requested, but not all enable requests have been matched + if (Enable) + { + if (state->refCount != 0) + { + state->refCount++; + goto leave; // err = NvSuccess already + } + state->refCount = 1; + } + else if (state->refCount != 0) + { + state->refCount --; + if (state->refCount != 0) + { + goto leave; // err = NvSuccess already + } + } + else + { + // TODO: assert on disable without enable + NvOsDebugPrintf( + "Clock control balance failed for module %d, instance %d\n", + NVRM_MODULE_ID_MODULE(ModuleId), NVRM_MODULE_ID_INSTANCE(ModuleId)); + // NV_ASSERT(!"Clock control balance violation"); + } + NvRmPrivModuleClockAttach(hDevice, cinfo, state, Enable); + NvRmPrivEnableModuleClock(hDevice, ModuleId, ClockState); + + // Check if voltage can be lowered after module clock is disabled. + if (!Enable) + { + v = NvRmPrivModuleVscaleAttach( + hDevice, cinfo, state, Enable, NV_FALSE); + if (v == NvRmVoltsOff) + NvRmPrivDvsRequest(v); // No transaction, just set update flag + } + + // Common exit +leave: + NvOsSpinMutexUnlock(s_hClockMutex); + if (SharedModule) + { + if (!Enable && (ModuleName == NvRmModuleID_Dsi)) + NvRmPrivPllDPowerControl(hDevice, NV_FALSE, &s_MipiPllVddOn); + NvOsMutexUnlock(s_hPllMutex); + } + return err; +} + +/*****************************************************************************/ + +ExecPlatform NvRmPrivGetExecPlatform(NvRmDeviceHandle hRmDeviceHandle) +{ + if (hRmDeviceHandle->ChipId.Major != 0) + { + return ExecPlatform_Soc; + } + if (NvRmIsSimulation()) + { + return ExecPlatform_Sim; + } + if (hRmDeviceHandle->ChipId.Minor != 0) + { + return ExecPlatform_Fpga; + } + return ExecPlatform_Qt; +} + +/*****************************************************************************/ + +/* Sets module clock source/divider register */ +void NvRmPrivModuleClockSet( + NvRmDeviceHandle hDevice, + const NvRmModuleClockInfo* cinfo, + const NvRmModuleClockState* state) +{ + NvU32 reg, divisor; + + NV_ASSERT(cinfo->ClkSourceOffset); + reg = NV_REGR(hDevice, NvRmPrivModuleID_ClockAndReset, 0, cinfo->ClkSourceOffset); + divisor = (reg >> cinfo->DivisorFieldShift) & cinfo->DivisorFieldMask; + if ((cinfo->Divider != NvRmClockDivider_None) && + (state->Divider > divisor)) + { + // Switch divider 1st, source 2nd, if new divisor is bigger + NV_ASSERT(state->Divider <= cinfo->DivisorFieldMask); + reg &= ~(cinfo->DivisorFieldMask << cinfo->DivisorFieldShift); + reg |= state->Divider << cinfo->DivisorFieldShift; + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, cinfo->ClkSourceOffset, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + } + + NV_ASSERT(state->SourceClock <= cinfo->SourceFieldMask); + reg &= (~(cinfo->SourceFieldMask << cinfo->SourceFieldShift)); + reg |= ( state->SourceClock << cinfo->SourceFieldShift); + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, cinfo->ClkSourceOffset, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + + if ((cinfo->Divider != NvRmClockDivider_None) && + (state->Divider < divisor)) + { + // Switch source 1st, divider 2nd, if new divisor is smaller + reg &= ~(cinfo->DivisorFieldMask << cinfo->DivisorFieldShift); + reg |= state->Divider << cinfo->DivisorFieldShift; + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, cinfo->ClkSourceOffset, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + } +} + +static NvRmFreqKHz +NvRmPrivGetEmcSyncFreq( + NvRmDeviceHandle hDevice, + NvRmModuleID Module) +{ + switch (hDevice->ChipId.Id) + { + case 0x15: + case 0x16: + return NvRmPrivAp15GetEmcSyncFreq(hDevice, Module); + case 0x20: + return NvRmPrivAp20GetEmcSyncFreq(hDevice, Module); + default: + NV_ASSERT(!"Unsupported chip ID"); + return NvRmFreqMaximum; + } +} + +static NvBool +NvRmPrivIsModuleClockException( + NvRmDeviceHandle hDevice, + NvRmModuleClockInfo *cinfo, + NvU32 clockSourceCount, + NvU32 MinFreq, + NvU32 MaxFreq, + const NvRmFreqKHz* PrefFreqList, + NvU32 PrefCount, + NvRmModuleClockState *state, + NvU32 flags) +{ + return NvRmPrivAp15IsModuleClockException( + hDevice, cinfo, clockSourceCount, MinFreq, MaxFreq, + PrefFreqList, PrefCount, state, flags); +} + +/* Returns the best source clock and the best divider */ +static NvError NvRmFindBestClockSource( + NvRmDeviceHandle hDevice, + NvRmModuleClockInfo *cinfo, + NvU32 clockSourceCount, + NvU32 MinFreq, + NvU32 MaxFreq, + const NvRmFreqKHz* PrefFreqList, + NvU32 PrefCount, + NvRmModuleClockState *state, + NvU32 flags) +{ + NvU32 bestdiff = 0x7FFFFFFF; + NvU32 bestdiv = 0x0; + NvU32 SourceClock = (NvU32)-1; + NvU32 SourceClockFreq = 0; + NvU32 i = 0,j = 0; + NvRmFreqKHz freq = 0, ReachedFreq = 0; + NvU32 temp = 0, div = 0, mantissa = 0; + NvS32 diff = 0; + + NV_ASSERT((MinFreq != 0) && (MinFreq <= MaxFreq)); + + // Check if exceptional handling is required this module clock, and exit + // if it is completed + if (NvRmPrivIsModuleClockException(hDevice, cinfo, clockSourceCount, + MinFreq, MaxFreq, PrefFreqList, PrefCount, state, flags)) + return NvSuccess; + + for (j=0; j< PrefCount; j++) // loop through target frequencies + { + freq = (PrefFreqList[j] == NvRmFreqMaximum) ? MaxFreq : PrefFreqList[j]; + if (flags & NvRmClockConfig_QuietOverClock) + freq = (PrefFreqList[j] > MaxFreq) ? MaxFreq : PrefFreqList[j]; + if ((freq < MinFreq) || (freq > MaxFreq)) + continue; + + for (i=0; i< clockSourceCount; i++) // loop through avilable sources + { + NV_ASSERT(cinfo->Sources[i] < NvRmClockSource_Num); + if (cinfo->Sources[i] == NvRmClockSource_Invalid) + break; + + SourceClockFreq = s_ClockSourceFreq[(cinfo->Sources[i])]; + if (SourceClockFreq < MinFreq) + continue; + if (NvRmPrivIsSourceProtected( + hDevice, cinfo->Module, cinfo->Sources[i])) + continue; + + if ((cinfo->Divider == NvRmClockDivider_None) || + (SourceClockFreq <= freq)) + { + div = 1; + if (cinfo->Module == NvRmModuleID_Uart) + { + // If target is not reachable from the source by integer + // division - reject the source + if (!NvRmIsFreqRangeReachable(SourceClockFreq, + MinFreq, MaxFreq, NVRM_UART_TIMING_DIVISOR_MAX)) + continue; + } + else if (SourceClockFreq > MaxFreq) + continue; + } + else // Divider, SourceClockFreq > freq + { + // Default integer divider: Freq = SourceClockFreq / div + // where div = h/w divisor field + NvU32 MaxDivisor = cinfo->DivisorFieldMask; + NV_ASSERT(MaxDivisor); + + if (cinfo->Divider == NvRmClockDivider_Integer_1) + { + // Integer divider: Freq = SourceClockFreq / div + // where div = h/w divisor field + 1 + MaxDivisor += 1; + } + else if (cinfo->Divider == NvRmClockDivider_Fractional_2) + { + // Fractional divider: Freq = (SourceClockFreq * 2) / div + // where div = h/w divisor field + 2 + SourceClockFreq = (SourceClockFreq << 1); + MaxDivisor += 2; + } + + // Find divisor floor / freq ceiling, and + // the 1st bit of the fractional part + temp = (SourceClockFreq << 1) / freq; + div = temp >> 1; + mantissa = temp & 0x01; + + // Check if divisor value fits divisor field + if (div >= MaxDivisor) + { + div = MaxDivisor; + if (SourceClockFreq > div * (NvU64)MaxFreq) + continue; // max boundary violation at max divisor + } + else if (SourceClockFreq > div * (NvU64)MaxFreq) + { + div += 1; // divisor ceiling / freq floor + if (SourceClockFreq < div * (NvU64)MinFreq) + continue; // both max and min boundaries violation + } + else if (mantissa) + { + div += 1; // divisor ceiling / freq floor + if (SourceClockFreq < div * (NvU64)MinFreq) + div -= 1; // fall back to divisor floor / freq ceiling + } + } + // Check if new traget frequency approximation is the best, so far + ReachedFreq = SourceClockFreq / div; + diff = freq - ReachedFreq; + if (diff < 0) + diff *= -1; + if ( ((NvU32) diff < bestdiff) || + (((NvU32) diff == bestdiff) && (div < bestdiv)) ) + { + SourceClock = i; + bestdiv = div; + bestdiff = (NvU32)diff; + } + } + // stop searching if "perfect" match found + if (!bestdiff) + break; + } + + if ((bestdiv == 0) || (SourceClock == (NvU32) -1)) + { + NV_ASSERT(!"No clock source found for this panel"); + return NvError_NotSupported; + } + + // Fill in clock state taking into account different types of dividers + state->Divider = bestdiv; + state->SourceClock = SourceClock; + SourceClockFreq = s_ClockSourceFreq[cinfo->Sources[SourceClock]]; + + if (cinfo->Divider == NvRmClockDivider_Integer_1) + { + state->Divider = bestdiv - 1; + } + else if (cinfo->Divider == NvRmClockDivider_Fractional_2) + { + if (bestdiv == 1) + bestdiv = 2; // cast pass thru case into generic formula + state->Divider = (bestdiv - 2); + SourceClockFreq = (SourceClockFreq << 1); + } + + state->actual_freq = SourceClockFreq / bestdiv; + + return NvSuccess; +} + +/*****************************************************************************/ + +static void RmReset2D(NvRmDeviceHandle hRmDevice) +{ + switch (hRmDevice->ChipId.Id) + { + case 0x15: + case 0x16: + NvRmPrivAp15Reset2D(hRmDevice); + return; + case 0x20: + NvRmPrivAp20Reset2D(hRmDevice); + return; + default: + NV_ASSERT(!"Unsupported chip ID"); + return; + } +} + +static void ScaledClockConfigInit(NvRmDeviceHandle hRmDevice) +{ + if (NvRmPrivGetExecPlatform(hRmDevice) != ExecPlatform_Soc) + return; // Initialize scaled clock configuration only on SoC + + switch (hRmDevice->ChipId.Id) + { + case 0x15: + case 0x16: + NvRmPrivAp15EmcConfigInit(hRmDevice); + return; + case 0x20: + NvRmPrivAp20ScaledClockConfigInit(hRmDevice); + return; + default: + NV_ASSERT(!"Unsupported chip ID"); + return; + } +} + +static void ModuleClockStateInit(NvRmDeviceHandle hRmDevice) +{ + NvError e; + NvU32 i, j, flags, reg; + NvRmModuleID ModuleId; + NvRmClockSource ImplicitPll; + const NvRmModuleClockInfo* cinfo; + NvRmModuleClockState *state; + + for (i = 0; i < s_moduleClockTableSize; i++) + { + flags = 0; + ImplicitPll = NvRmClockSource_Invalid; + cinfo = &s_moduleClockTable[i]; + state = &s_moduleClockState[i]; + ModuleId = NVRM_MODULE_ID(cinfo->Module, cinfo->Instance); + + if (cinfo->SubClockId) + { + // Check module subclock configuration + if ((cinfo->Module == NvRmModuleID_Spdif) || + (cinfo->Module == NvRmModuleID_Vi) || + (cinfo->Module == NvRmModuleID_Tvo)) + flags = NvRmClockConfig_SubConfig; + } + else + { + // Check implicit attachment to PLLs for main clocks only + ImplicitPll = + NvRmPrivGetImplicitPllSource(hRmDevice, cinfo->Module); + NV_ASSERT((ImplicitPll == NvRmClockSource_Invalid) || + (cinfo->ClkEnableOffset)); + } + + // Fill in module clock state, attach explicit PLL sources for clocks + // and subclocks. Special cases: CPU and AVP are not in the relocation + // table, and attached to PLL via CPU and System bus, respectively + e = NvRmPowerModuleClockConfig( + hRmDevice, ModuleId, 0, 0, 0, NULL, 0, NULL, flags); + NV_ASSERT((e == NvSuccess) || (e == NvError_ModuleNotPresent)); + if (e == NvSuccess) + { + NvRmMilliVolts v; // can be ignored as we always boot at max V + NvRmFreqKHz SourceClockFreq = + s_ClockSourceFreq[(cinfo->Sources[state->SourceClock])]; + NvRmPrivModuleSetScalingAttribute(hRmDevice, cinfo, state); + v = NvRmPrivModuleVscaleReAttach(hRmDevice, + cinfo, state, state->actual_freq, SourceClockFreq, NV_FALSE); + (void)v; + } + else if ((cinfo->Module == NvRmModuleID_Cpu) || + (cinfo->Module == NvRmModuleID_Avp)) + { + const NvRmCoreClockInfo* pCore = + NvRmPrivGetClockSourceHandle(cinfo->Sources[0])->pInfo.pCore; + NvRmClockSource SourceId = + NvRmPrivCoreClockSourceGet(hRmDevice, pCore); + NvRmPrivCoreClockReAttach(hRmDevice, pCore->SourceId, SourceId); + } + + // Attach implicit PLL sources and update reference count + // for enabled main clocks + if (flags == NvRmClockConfig_SubConfig) + continue; + + if (cinfo->ClkEnableOffset) + { + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + cinfo->ClkEnableOffset); + if ((reg & cinfo->ClkEnableField) == cinfo->ClkEnableField) + { + for (j = 0; j < s_PllReferencesTableSize; j++) + { + if (s_PllReferencesTable[j].SourceId == ImplicitPll) + NvRmPrivPllRefUpdate( + hRmDevice, &s_PllReferencesTable[j], NV_TRUE); + } + state->refCount = 1; + } + } + else + state->refCount = 1; // free running clock + } +} + +NvError +NvRmPrivClocksInit(NvRmDeviceHandle hRmDevice) +{ + NvRmModuleID ModuleId; + NvU32 i = 0; + NvU32 fpgaModuleFreq = 0; + ExecPlatform env; + NvError e; + + NV_ASSERT(hRmDevice); + env = NvRmPrivGetExecPlatform(hRmDevice); + + NV_CHECK_ERROR_CLEANUP(NvOsSpinMutexCreate(&s_hClockMutex)); + NV_CHECK_ERROR_CLEANUP(NvOsMutexCreate(&s_hPllMutex)); + + /* + * Clock tree descriptors and reference tables initialization + */ + if ((hRmDevice->ChipId.Id == 0x15) || (hRmDevice->ChipId.Id == 0x16)) + { + s_moduleClockTable = g_Ap15ModuleClockTable; + s_moduleClockTableSize = g_Ap15ModuleClockTableSize; + NvRmPrivAp15PllReferenceTableInit(&s_PllReferencesTable, + &s_PllReferencesTableSize); + s_ClockSourceTable = NvRmPrivAp15ClockSourceTableInit(); + fpgaModuleFreq = FPGA_MODULE_KHZ_AP15; + } + else if (hRmDevice->ChipId.Id == 0x20) + { + s_moduleClockTable = g_Ap20ModuleClockTable; + s_moduleClockTableSize = g_Ap20ModuleClockTableSize; + NvRmPrivAp20PllReferenceTableInit(&s_PllReferencesTable, + &s_PllReferencesTableSize); + s_ClockSourceTable = NvRmPrivAp20ClockSourceTableInit(); + fpgaModuleFreq = FPGA_MODULE_KHZ_AP20; + } + else + NV_ASSERT(!"Unsupported chip ID"); + + /* + * Allocate module clock state array, and map module clock descriptors + * to module instances. + */ + s_moduleClockState = (NvRmModuleClockState *) + NvOsAlloc(sizeof (NvRmModuleClockState) * s_moduleClockTableSize); + if (s_moduleClockState == NULL) + { + e = NvError_InsufficientMemory; + goto fail; + } + NvOsMemset(s_moduleClockState, 0, + sizeof(NvRmModuleClockState) * s_moduleClockTableSize); + + for (i = 0; i < s_moduleClockTableSize; i++) + { + NvRmModuleInstance* inst; + ModuleId = NVRM_MODULE_ID( + s_moduleClockTable[i].Module, s_moduleClockTable[i].Instance); + + if (s_moduleClockTable[i].SubClockId) + continue; // skip subclocks + + if (NvRmPrivGetModuleInstance(hRmDevice, ModuleId, &inst) == NvSuccess) + { + inst->ModuleData = (void *)&s_moduleClockTable[i]; + } + else + { + // NvOsDebugPrintf( + // "No module found for clock descriptor with module ID %d\n", ModuleID); + } + } + + /* + * Clock limits and sources initialization + */ + s_ModuleClockLimits = NvRmPrivClockLimitsInit(hRmDevice); + s_ClockSourceFreq[NvRmClockSource_Invalid] = 0; + s_SystemBusComplex.BusRateOffset = 0; + { + if (env == ExecPlatform_Fpga) + { + s_ClockSourceFreq[NvRmClockSource_ClkS] = 32; + s_ClockSourceFreq[NvRmClockSource_ClkM] = fpgaModuleFreq; + s_ClockSourceFreq[NvRmClockSource_ClkD] = fpgaModuleFreq; + s_ClockSourceFreq[NvRmClockSource_PllA0] = 12288; + s_ClockSourceFreq[NvRmClockSource_PllP0] = fpgaModuleFreq; + s_ClockSourceFreq[NvRmClockSource_PllC0] = fpgaModuleFreq; + s_ClockSourceFreq[NvRmClockSource_PllM0] = fpgaModuleFreq; + s_ClockSourceFreq[NvRmClockSource_PllX0] = fpgaModuleFreq; + s_ClockSourceFreq[NvRmClockSource_CpuBus] = fpgaModuleFreq; + s_ClockSourceFreq[NvRmClockSource_SystemBus] = fpgaModuleFreq; + NvRmPrivBusClockInit( + hRmDevice, s_ClockSourceFreq[NvRmClockSource_SystemBus]); + } + else if ((env == ExecPlatform_Qt) || (env == ExecPlatform_Sim)) + { + s_ClockSourceFreq[NvRmClockSource_ClkS] = 32; + if (env == ExecPlatform_Sim) // On sim set main frequency 13MHz + { + s_ClockSourceFreq[NvRmClockSource_ClkM] = 13000; + s_ClockSourceFreq[NvRmClockSource_ClkD] = 26000; + } + else // On Qt keep 12MHz + { + s_ClockSourceFreq[NvRmClockSource_ClkM] = 12000; + s_ClockSourceFreq[NvRmClockSource_ClkD] = 24000; + } + s_ClockSourceFreq[NvRmClockSource_PllA0] = 12288; + s_ClockSourceFreq[NvRmClockSource_PllP0] = 432000; + s_ClockSourceFreq[NvRmClockSource_PllP1] = 28800; + s_ClockSourceFreq[NvRmClockSource_PllP2] = 48000; + s_ClockSourceFreq[NvRmClockSource_PllP3] = 72000; + s_ClockSourceFreq[NvRmClockSource_PllP4] = 108000; + s_ClockSourceFreq[NvRmClockSource_PllC0] = 600000; + s_ClockSourceFreq[NvRmClockSource_PllM0] = 333000; + s_ClockSourceFreq[NvRmClockSource_SystemBus] = 150000; + NvRmPrivAp15SimPllInit(hRmDevice); // Enable plls in simulation + NvRmPrivBusClockInit( + hRmDevice, s_ClockSourceFreq[NvRmClockSource_SystemBus]); + } + else if (env == ExecPlatform_Soc) + { + NvRmPrivClockSourceFreqInit(hRmDevice, s_ClockSourceFreq); + } + else + { + NV_ASSERT(!"Not supported execution platform"); + } + RmReset2D(hRmDevice); + } + + /* + * Initialize current modules clock state + */ + if (env == ExecPlatform_Fpga) + { + for (i = 0; i < s_moduleClockTableSize; i++) + { + s_moduleClockState[i].actual_freq = fpgaModuleFreq; + } + } + else if (env == ExecPlatform_Qt) + { + for (i = 0; i < s_moduleClockTableSize; i++) + { + s_moduleClockState[i].actual_freq = QT_MODULE_KHZ; + } + } + ModuleClockStateInit(hRmDevice); + ScaledClockConfigInit(hRmDevice); + + /* debug info... print out some initial frequencies */ + { + NvU32 freq; + + if (NvRmPrivGetClockSourceHandle(NvRmClockSource_PllX0)) + { + NvOsDebugPrintf("NVRM CLOCKS: PLLX0: %d Khz\n", + s_ClockSourceFreq[NvRmClockSource_PllX0]); + } + NvOsDebugPrintf("NVRM CLOCKS: PLLM0: %d Khz\n", + s_ClockSourceFreq[NvRmClockSource_PllM0]); + NvOsDebugPrintf("NVRM CLOCKS: PLLC0: %d Khz\n", + s_ClockSourceFreq[NvRmClockSource_PllC0]); + NvOsDebugPrintf("NVRM CLOCKS: PLLP0: %d Khz\n", + s_ClockSourceFreq[NvRmClockSource_PllP0]); + NvOsDebugPrintf("NVRM CLOCKS: PLLA0: %d Khz\n", + s_ClockSourceFreq[NvRmClockSource_PllA0]); + NvOsDebugPrintf("NVRM CLOCKS: CPU: %d Khz\n", + s_ClockSourceFreq[NvRmClockSource_CpuBus]); + NvOsDebugPrintf("NVRM CLOCKS: AVP: %d Khz\n", + s_ClockSourceFreq[NvRmClockSource_SystemBus]); + NvOsDebugPrintf("NVRM CLOCKS: System Bus: %d Khz\n", + s_ClockSourceFreq[NvRmClockSource_SystemBus]); + + NV_ASSERT_SUCCESS(NvRmPowerModuleClockConfig( + hRmDevice, NvRmPrivModuleID_MemoryController, + 0, 0, 0, NULL, 0, &freq, 0)); + NvOsDebugPrintf("NVRM CLOCKS: Memory Controller: %d\n", freq); + + NV_ASSERT_SUCCESS(NvRmPowerModuleClockConfig( + hRmDevice, NvRmPrivModuleID_ExternalMemoryController, + 0, 0, 0, NULL, 0, &freq, 0)); + NvOsDebugPrintf("NVRM CLOCKS: External Memory Controller: %d\n", freq); + } + + return NvSuccess; + +fail: + NvOsFree(s_moduleClockState); + s_moduleClockState = NULL; + NvOsMutexDestroy(s_hPllMutex); + s_hPllMutex = NULL; + NvOsSpinMutexDestroy(s_hClockMutex); + s_hClockMutex = NULL; + return e; +} + +void +NvRmPrivClocksDeinit(NvRmDeviceHandle hRmDevice) +{ + NV_ASSERT(hRmDevice); + + if (s_moduleClockState != NULL) + { + // TODO: check refrence counts for "clock leakage" + } + NvOsFree(s_moduleClockState); + s_moduleClockState = NULL; + NvOsMutexDestroy(s_hPllMutex); + s_hPllMutex = NULL; + NvOsSpinMutexDestroy(s_hClockMutex); + s_hClockMutex = NULL; +} + +void +NvRmPrivBoostClocks(NvRmDeviceHandle hRmDevice) +{ + NvRmFreqKHz FreqKHz; + + // Initialize core voltage control + NvRmPrivDvsInit(); + + // Configure fast memory and core clocks (nominal core, CPU and memory + // voltages are already set by this time during PMU initialization) + if ((hRmDevice->ChipId.Id == 0x15) || (hRmDevice->ChipId.Id == 0x16)) + { + NvRmPrivAp15FastClockConfig(hRmDevice); + } + else if (hRmDevice->ChipId.Id == 0x20) + { + NvRmPrivAp20FastClockConfig(hRmDevice); + } + + // Print fast clocks + NvOsDebugPrintf("ADJUSTED CLOCKS:\n"); + NV_ASSERT_SUCCESS(NvRmPowerModuleClockConfig( + hRmDevice, NvRmPrivModuleID_MemoryController, + 0, 0, 0, NULL, 0, &FreqKHz, 0)); + NvOsDebugPrintf("MC clock is set to %6d KHz\n", FreqKHz); + + NV_ASSERT_SUCCESS(NvRmPowerModuleClockConfig( + hRmDevice, NvRmPrivModuleID_ExternalMemoryController, + 0, 0, 0, NULL, 0, &FreqKHz, 0)); + NvOsDebugPrintf("EMC clock is set to %6d KHz (DDR clock is at %6d KHz)\n", + FreqKHz, FreqKHz/2); + + if (NvRmPrivGetClockSourceHandle(NvRmClockSource_PllX0)) + { + FreqKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllX0); + NvOsDebugPrintf("PLLX0 clock is set to %6d KHz\n", FreqKHz); + } + FreqKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllC0); + NvOsDebugPrintf("PLLC0 clock is set to %6d KHz\n", FreqKHz); + FreqKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_CpuBus); + NvOsDebugPrintf("CPU clock is set to %6d KHz\n", FreqKHz); + FreqKHz = NvRmPrivGetClockSourceFreq(NvRmClockSource_SystemBus); + NvOsDebugPrintf("System and AVP clock is set to %6d KHz\n", FreqKHz); + + // Print GPU clocks + #define DEBUG_PRINT_MODULE_CLOCK(Name) \ + do\ + {\ + NV_ASSERT_SUCCESS(NvRmPowerModuleClockConfig( \ + hRmDevice, NvRmModuleID_##Name, 0, 0, 0, NULL, 0, &FreqKHz, 0)); \ + NvOsDebugPrintf(#Name " clock is set to %6d KHz\n", FreqKHz); \ + } while (0) + + DEBUG_PRINT_MODULE_CLOCK(GraphicsHost); + DEBUG_PRINT_MODULE_CLOCK(3D); + DEBUG_PRINT_MODULE_CLOCK(2D); + DEBUG_PRINT_MODULE_CLOCK(Epp); + DEBUG_PRINT_MODULE_CLOCK(Mpe); + DEBUG_PRINT_MODULE_CLOCK(Vde); + #undef DEBUG_PRINT_MODULE_CLOCK +} + +typedef struct NvRmPllRailMapRec +{ + // PLL Clock Source Id + NvRmClockSource PllId; + + // Power rail GUID + NvU64 PllRailId; +} NvRmPllRailMap; + +static const NvRmPllRailMap s_PllRailMap[] = +{ + { NvRmClockSource_ClkM, NV_VDD_OSC_ODM_ID}, + { NvRmClockSource_PllA1, NV_VDD_PLLA_ODM_ID}, + { NvRmClockSource_PllC0, NV_VDD_PLLC_ODM_ID}, + { NvRmClockSource_PllD0, NV_VDD_PLLD_ODM_ID}, + { NvRmClockSource_PllM0, NV_VDD_PLLM_ODM_ID}, + { NvRmClockSource_PllP0, NV_VDD_PLLP_ODM_ID}, + { NvRmClockSource_PllU0, NV_VDD_PLLU1_ODM_ID}, + { NvRmClockSource_PllX0, NV_VDD_PLLX_ODM_ID}, +}; + +void +NvRmPrivPllRailsInit(NvRmDeviceHandle hRmDevice) +{ + NvU32 i; + + for (i = 0; i < NV_ARRAY_SIZE(s_PllRailMap); i++) + { + NvU64 PllRailId = s_PllRailMap[i].PllRailId; + NvRmClockSource PllId = s_PllRailMap[i].PllId; + switch (PllId) + { + // If present PLLX is treated as other boot PLLs + case NvRmClockSource_PllX0: + if (!NvRmPrivGetClockSourceHandle(NvRmClockSource_PllX0)) + break; + // fall through + + // Oscillator and boot PLLs are already running - turn the + // respective rails On, anyway, to sync ref count + case NvRmClockSource_ClkM: + case NvRmClockSource_PllC0: + case NvRmClockSource_PllM0: + case NvRmClockSource_PllP0: + NvRmPrivPmuRailControl(hRmDevice, PllRailId, NV_TRUE); + break; + + // If PLLA rail is turned On by BL - update ref count, otherwise + // turn rail On, but leave PLLA disabled + case NvRmClockSource_PllA1: + if (NvRmPrivPmuRailGetVoltage(hRmDevice, PllRailId) == 0) + { + NvRmPrivAp15PllSet(hRmDevice, + NvRmPrivGetClockSourceHandle(PllId)->pInfo.pPll, + 0, 0, 0, (NvU32)-1, 0, 0, NV_TRUE, 0); + } + NvRmPrivPmuRailControl(hRmDevice, PllRailId, NV_TRUE); + break; + + // If PLLD rail is turned On by BL - update ref count, otherwise + // keep it Off and disable PLLD; initialize PLLD rail status + case NvRmClockSource_PllD0: + if (NvRmPrivPmuRailGetVoltage(hRmDevice, PllRailId) != 0) + { + s_MipiPllVddOn = NV_TRUE; + NvRmPrivPmuRailControl(hRmDevice, PllRailId, NV_TRUE); + } + else + { + s_MipiPllVddOn = NV_FALSE; + NvRmPrivAp15PllSet(hRmDevice, + NvRmPrivGetClockSourceHandle(PllId)->pInfo.pPll, + 0, 0, 0, (NvU32)-1, 0, 0, NV_TRUE, 0); + } + break; + + // PLLU rail is controlled by USB stack - don't touch it, unless + // USB download transport is active. In the latter case update ref + // counts for PLLU and USB power rails + case NvRmClockSource_PllU0: + if (NvRmPrivGetDownloadTransport(hRmDevice) == + NvOdmDownloadTransport_Usb) + { + NvRmPrivPmuRailControl(hRmDevice, PllRailId, NV_TRUE); + NvRmPrivPmuRailControl(hRmDevice, NV_VDD_USB_ODM_ID, NV_TRUE); + } + break; + + default: + NV_ASSERT(!"Invalid Id"); + } + } +} + +void +NvRmPrivClocksResume(NvRmDeviceHandle hRmDevice) +{ + // Sync clock sources after LP0 + NvRmPrivClockSourceFreqInit(hRmDevice, s_ClockSourceFreq); + ScaledClockConfigInit(hRmDevice); + if ((hRmDevice->ChipId.Id == 0x15) || (hRmDevice->ChipId.Id == 0x16)) + NvRmPrivAp15FastClockConfig(hRmDevice); + else if (hRmDevice->ChipId.Id == 0x20) + NvRmPrivAp20FastClockConfig(hRmDevice); + +} + +/*****************************************************************************/ + +NvRmFreqKHz +NvRmPrivGetInterfaceMaxClock(NvRmDeviceHandle hRmDevice, NvRmModuleID ModuleId) +{ + + NvU32 OdmModules[4]; + NvU32 OdmInstances[4]; + NvU32* pMaxClockSpeed = NULL; + NvU32 count = 0; + NvU32 i = 0; + NvU32 instance = 0; + NvU32 NumOdmModules = 0; + NvU32 MaxFreq = 0; + + MaxFreq = NvRmFreqMaximum; + + NumOdmModules = NvRmPrivRmModuleToOdmModule(hRmDevice->ChipId.Id, + ModuleId, (NvOdmIoModule *)OdmModules, OdmInstances); + + for(i = 0; i < NumOdmModules; i++) + { + instance = OdmInstances[i]; + NvOdmQueryClockLimits(OdmModules[i], (const NvU32 **)&pMaxClockSpeed, &count); + if ((pMaxClockSpeed) && (instance < count)) + { + MaxFreq = pMaxClockSpeed[instance]; + } + } + + return MaxFreq; +} + +NvRmFreqKHz +NvRmPrivModuleGetMaxSrcKHz( + NvRmDeviceHandle hRmDevice, + const NvRmModuleClockInfo* cinfo) +{ + NvU32 i; + NvRmFreqKHz SourceClockFreq = 0; + + for (i=0; i < NvRmClockSource_Num; i++) + { + NV_ASSERT(cinfo->Sources[i] < NvRmClockSource_Num); + if (cinfo->Sources[i] == NvRmClockSource_Invalid) + break; + if (NvRmPrivIsSourceProtected( + hRmDevice, cinfo->Module, cinfo->Sources[i])) + continue; + SourceClockFreq = + NV_MAX(SourceClockFreq, s_ClockSourceFreq[(cinfo->Sources[i])]); + } + return SourceClockFreq; +} + +static NvRmMilliVolts ModuleVscaleConfig( + NvRmDeviceHandle hRmDevice, + const NvRmModuleClockInfo* cinfo, + NvRmModuleClockState *state, + NvRmFreqKHz MaxFreq, + NvBool Preview) +{ + NvRmFreqKHz f, SourceClockFreq; + NvRmMilliVolts v = NvRmVoltsUnspecified; + NvRmModuleID ModuleName = cinfo->Module; + + if (!state->Vscale) + return v; + + // Find voltage level for the actually configured frequency. For Display, + // UART and USB use maximum requested frequency, instead (Display and UART + // actual clock configuration is completed outside CAR but it will not + // exceed maximum requested boundary level; actual USB frequency is always + // set to fixed PLLU output, but maximum boundary is used by driver to + // communicate scaled voltage requirements). + SourceClockFreq = + s_ClockSourceFreq[(cinfo->Sources[state->SourceClock])]; + + if ((ModuleName == NvRmModuleID_Display) || + (ModuleName == NvRmModuleID_Uart) || + (ModuleName == NvRmModuleID_Usb2Otg)) + f = MaxFreq; + else + f = state->actual_freq; + + v = NvRmPrivModuleVscaleReAttach( + hRmDevice, cinfo, state, f, SourceClockFreq, Preview); + return v; +} + +NvError +NvRmPowerModuleClockConfig ( + NvRmDeviceHandle hDevice, + NvRmModuleID ModuleId, + NvU32 ClientId, + NvRmFreqKHz MinFreq, + NvRmFreqKHz MaxFreq, + const NvRmFreqKHz* PrefFreqList, + NvU32 PrefFreqListCount, + NvRmFreqKHz* CurrentFreq, + NvU32 flags) +{ + NvError err = NvSuccess; + NvRmModuleClockInfo *cinfo = NULL; + NvU32 divisor = 0x0; + NvU32 reg = 0x0; + NvRmFreqKHz f, SourceClockFreq; + ExecPlatform env; + NvRmModuleClockState *state; + NvRmMilliVolts v = NvRmVoltsOff; + NvRmModuleID ModuleName = NVRM_MODULE_ID_MODULE( ModuleId ); + NvU32 MaxInterfaceClock = 0; + + NvBool DiagMode = NvRmPrivIsDiagMode(ModuleId); + + /* validate the Rm Handle */ + NV_ASSERT(hDevice); + env = NvRmPrivGetExecPlatform(hDevice); + + // Get pointers to module clock info and current module clock state + err = NvRmPrivGetClockState(hDevice, ModuleId, &cinfo, &state); + if (err != NvSuccess) + return err; + + if ((flags & NvRmClockConfig_SubConfig) && + ((ModuleName == NvRmModuleID_Spdif) || + (ModuleName == NvRmModuleID_Vi) || + (ModuleName == NvRmModuleID_Tvo))) + { + // Module subclock is to be configured. Use subclock descriptor + // and subclock state (located immediately after main descriptor, + // and state, respectively) + state++; + cinfo++; + NV_ASSERT(cinfo->Module == ModuleName); + NV_ASSERT(cinfo->SubClockId == 1); + } + else if (PrefFreqList && (PrefFreqList[0] == NvRmFreqMaximum) && + ((ModuleName == NvRmModuleID_2D) || + (ModuleName == NvRmModuleID_Epp) || + (ModuleName == NvRmModuleID_GraphicsHost))) + { + // Maximum frequency for these modules is synchronized with EMC + f = NvRmPrivGetEmcSyncFreq(hDevice, ModuleName); + if (f == state->actual_freq) + { + if (CurrentFreq) + *CurrentFreq = f; + return err; // already in sync + } + MaxFreq = f + 1; // 1 kHz margin + } + else if (PrefFreqList && + ((ModuleName == NvRmModuleID_Vde) || + (ModuleName == NvRmPrivModuleID_MemoryController) || + (ModuleName == NvRmPrivModuleID_ExternalMemoryController))) + { // CPU, AVP are not allowed too, but failed get state if tried + NV_ASSERT(!"MC/EMC, VDE clock configuration is not allowed here"); + return NvError_NotSupported; + } + + // Clip frequency boundaries to h/w limitations + if (PrefFreqList) + { + const NvRmModuleClockLimits* pClimits = + NvRmPrivGetSocClockLimits(cinfo->Module); + if ((MinFreq == NvRmFreqUnspecified) || + (MinFreq < pClimits->MinKHz)) + { + MinFreq = pClimits->MinKHz; + } + MaxInterfaceClock = NV_MIN(pClimits->MaxKHz, + NvRmPrivGetInterfaceMaxClock(hDevice, ModuleId)); + if ((MaxFreq == NvRmFreqUnspecified) || + (MaxFreq > MaxInterfaceClock)) + { + MaxFreq = MaxInterfaceClock; + } + } + +#if NVRM_DIAG_LOCK_SUPPORTED + // Check/set individual diag lock for this clock only + DiagMode |= state->DiagLock; + if (flags & NvRmClockConfig_DiagLock) + state->DiagLock = NV_TRUE; +#endif + + // Display/DSI clock configuration also affects PLLs shared with DVFS + // and involves PLLD power control. Always perform at nominal voltage. + // PMU access transport must *not* be scalable (PMU transport API must + // be called outside clock mutex). + if (PrefFreqList && (!DiagMode) && + ((ModuleName == NvRmModuleID_Display) || + (ModuleName == NvRmModuleID_Dsi))) + { + NvOsMutexLock(s_hPllMutex); + NvRmPrivPllDPowerControl(hDevice, NV_TRUE, &s_MipiPllVddOn); + NvRmPrivDvsRequest(NvRmVoltsMaximum); + } + + NvOsSpinMutexLock(s_hClockMutex); + { + if (env == ExecPlatform_Fpga || env == ExecPlatform_Qt) + { + // Clock configuration only supported for the i2s, VI, i2c, + // dvc and HSMMC on this environment + if (!(ModuleName == NvRmModuleID_I2s || + ModuleName == NvRmModuleID_Vi || + ModuleName == NvRmModuleID_Dvc || + ModuleName == NvRmModuleID_I2c || + ModuleName == NvRmModuleID_Hsmmc || + ModuleName == NvRmModuleID_OneWire + )) + { + // Return actual display clock only on FPGA + if ((env == ExecPlatform_Fpga) && + (ModuleName == NvRmModuleID_Display)) + { + state->actual_freq = FPGA_DISPLAY_KHZ; + } + + goto end; + } + } + if (PrefFreqList && (!DiagMode)) + { + if ((ModuleName != NvRmModuleID_Dsi) && + (ModuleName != NvRmModuleID_Usb2Otg)) + NV_ASSERT(cinfo->SourceFieldMask || cinfo->DivisorFieldMask); + + // Get the best module source clock and divider + err = NvRmFindBestClockSource(hDevice, cinfo, NvRmClockSource_Num, + MinFreq, MaxFreq, PrefFreqList, PrefFreqListCount, state, flags); + if (err != NvSuccess) + { + goto leave; + } + NV_ASSERT(state->SourceClock <= cinfo->SourceFieldMask); + + // For "shared" clocks (Display/DSI) voltage is already at max; + // just record new voltage requirements + if ((ModuleName == NvRmModuleID_Display) || + (ModuleName == NvRmModuleID_Dsi)) + { + NvRmPrivModuleVscaleSetPending(hDevice, NvRmVoltsMaximum); + v = ModuleVscaleConfig( + hDevice, cinfo, state, MaxFreq, NV_FALSE); + NvRmPrivModuleVscaleSetPending(hDevice, NvRmVoltsOff); + } + else + { + // Preview, don't update scaling ref counts if voltage going up + v = ModuleVscaleConfig( + hDevice, cinfo, state, MaxFreq, NV_TRUE); + + if ((v != NvRmVoltsOff) && + (v != NvRmVoltsUnspecified)) + { + // Preview reported voltage increase - set target + // pending to prevent DVFS scaling down + NvRmPrivModuleVscaleSetPending(hDevice, v); + + NvOsSpinMutexUnlock(s_hClockMutex); + NvOsMutexLock(s_hPllMutex); + NvRmPrivDvsRequest(v); + NvOsMutexUnlock(s_hPllMutex); + NvOsSpinMutexLock(s_hClockMutex); + + // Now, after voltage is increased - update scaling counts + // and cancel pending request + v = ModuleVscaleConfig( + hDevice, cinfo, state, MaxFreq, NV_FALSE); + NvRmPrivModuleVscaleSetPending(hDevice, NvRmVoltsOff); + } + } + + // Finally change clock configuration + if ((ModuleName != NvRmModuleID_Dsi) && + (ModuleName != NvRmModuleID_Usb2Otg)) + { + // Set new clock state + NvRmPrivModuleClockSet(hDevice, cinfo, state); + if ((ModuleName == NvRmModuleID_Tvo) && + (cinfo->SubClockId == 1)) // if CVE - sync TVDAC + { + NV_ASSERT(((cinfo + 1)->Module == NvRmModuleID_Tvo) && + ((cinfo + 1)->SubClockId == 2)); + *(state + 1) = *state; + NvRmPrivModuleClockSet(hDevice, (cinfo + 1), state); + } + NvRmPrivModuleClockReAttach(hDevice, cinfo, state); + NvRmPrivDisablePLLs(hDevice, cinfo, state); + } + if (v == NvRmVoltsOff) + NvRmPrivDvsRequest(v); // No transaction, just set update flag + + // FIXME is this a hack just for the AP15 FPGA + // Special treatment to the i2s on the fpga to do the workaround + // for the i2s recording, the clock source to i2s should be less than + // the system clock frequency 8.33MHz for the fpga, so dividing by 2 + // if its more than + if ((hDevice->ChipId.Id == 0x15 || hDevice->ChipId.Id == 0x16) && + (env == ExecPlatform_Fpga) && (ModuleName == NvRmModuleID_I2s)) + { + reg = NV_REGR(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + cinfo->ClkSourceOffset); + if (!(reg & 0x7f)) + { + reg |= 1; + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + cinfo->ClkSourceOffset, reg); + state->actual_freq = state->actual_freq/2; + } + } + // Hack: on FPGA OneWire divider is implemented as integer divider + // (on SoC it is fractional divider) + if ((env == ExecPlatform_Fpga) && + (ModuleName == NvRmModuleID_OneWire)) + { + reg = NV_REGR(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + cinfo->ClkSourceOffset); + reg &= ~(cinfo->DivisorFieldMask << cinfo->DivisorFieldShift); + reg |= (state->Divider >> 1) << cinfo->DivisorFieldShift; + NV_REGW(hDevice, NvRmPrivModuleID_ClockAndReset, 0, + cinfo->ClkSourceOffset, reg); + } + } + else // No target list just update state from h/w and return current frequency + { + if (cinfo->SourceFieldMask != 0) + { + NV_ASSERT(cinfo->ClkSourceOffset); + state->SourceClock = NV_REGR( + hDevice, NvRmPrivModuleID_ClockAndReset, 0, cinfo->ClkSourceOffset); + state->SourceClock >>= cinfo->SourceFieldShift; + state->SourceClock &= cinfo->SourceFieldMask; + SourceClockFreq = s_ClockSourceFreq[(cinfo->Sources[state->SourceClock])]; + } + else + { + // If source is Fixed source always at index 0 + SourceClockFreq = s_ClockSourceFreq[(cinfo->Sources[0])]; + } + if ((ModuleName == NvRmPrivModuleID_MemoryController) || + (ModuleName == NvRmPrivModuleID_ExternalMemoryController)) + NvRmPrivMemoryClockReAttach(hDevice, cinfo, state); + else + NvRmPrivModuleClockReAttach(hDevice, cinfo, state); + + if ( cinfo->Divider != NvRmClockDivider_None ) + { + NV_ASSERT(cinfo->ClkSourceOffset); + state->Divider = NV_REGR( + hDevice, NvRmPrivModuleID_ClockAndReset, 0, cinfo->ClkSourceOffset); + state->Divider >>= cinfo->DivisorFieldShift; + state->Divider &= cinfo->DivisorFieldMask; + + divisor = state->Divider; + if (cinfo->Divider == NvRmClockDivider_Integer_1) + { + divisor += 1; + } + else if (cinfo->Divider == NvRmClockDivider_Fractional_2) + { + divisor += 2; + SourceClockFreq = (SourceClockFreq << 1); + } + else if (cinfo->Divider == NvRmClockDivider_Integer_2) + { + divisor += 2; + } + } + else + { + state->Divider = 1; + divisor = 1; + } + NV_ASSERT(divisor); + state->actual_freq = SourceClockFreq / divisor; + } + + /* + * VI and I2S has some special bits in the clock register + */ + NvRmPrivAp15ClockConfigEx( + hDevice, ModuleName, cinfo->ClkSourceOffset, flags); + + + /* + * SDMMC internal feedback tap delay adjustment + * This is required for the ap20 based boards. + */ + if ((PrefFreqListCount) && (hDevice->ChipId.Id == 0x20) && + (ModuleName == NvRmModuleID_Sdio)) + { + NvRmPrivAp20SdioTapDelayConfigure(hDevice, ModuleId, + cinfo->ClkSourceOffset, state->actual_freq); + } + } + +end: + if (CurrentFreq) + { + *CurrentFreq = state->actual_freq; + } +leave: + NvOsSpinMutexUnlock(s_hClockMutex); + if (PrefFreqList && (!DiagMode) && + ((ModuleName == NvRmModuleID_Display) || + (ModuleName == NvRmModuleID_Dsi))) + { + NvRmPrivPllDPowerControl(hDevice, NV_FALSE, &s_MipiPllVddOn); + NvRmPrivDvsRequest(NvRmVoltsOff); + NvOsMutexUnlock(s_hPllMutex); + } + return err; +} + +/*****************************************************************************/ + +NvRmClockSource +NvRmPrivCoreClockSourceGet( + NvRmDeviceHandle hRmDevice, + const NvRmCoreClockInfo* pCinfo) +{ + NvU32 i, reg; + NvU32 ModeField; + + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo); + + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->SelectorOffset); + ModeField = (reg >> pCinfo->ModeFieldShift) & pCinfo->ModeFieldMask; + if (ModeField == 0) + { + // One fixed 32kHz clock source, if mode field is cleared + return NvRmClockSource_ClkS; + } + // Selected Clock Mode = 1 + LOG2(mode field) + for (i = 0; ModeField != 0; ModeField >>= 1, i++); + NV_ASSERT(i < NvRmCoreClockMode_Num); + + // Source selection index = source field value for currently selected mode + reg = (reg >> pCinfo->SourceFieldShifts[i]) & pCinfo->SourceFieldMasks[i]; + NV_ASSERT(reg < NvRmClockSource_Num); + + return pCinfo->Sources[reg]; +} + +NvRmFreqKHz +NvRmPrivCoreClockFreqGet( + NvRmDeviceHandle hRmDevice, + const NvRmCoreClockInfo* pCinfo) +{ + NvU32 reg, n, m; + NvRmFreqKHz ClkFreq; + NvRmClockSource ClkSrcId; + + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo); + + // Get source frequency + ClkSrcId = NvRmPrivCoreClockSourceGet(hRmDevice, pCinfo); + ClkFreq = s_ClockSourceFreq[ClkSrcId]; + NV_ASSERT(ClkFreq); + + // Get divider settings and calculate clock frequency + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->DividerOffset); + m = (reg >> pCinfo->DividendFieldShift) & pCinfo->DividendFieldMask; + n = (reg >> pCinfo->DivisorFieldShift) & pCinfo->DivisorFieldMask; + if ((reg >> pCinfo->DividerEnableFiledShift) & pCinfo->DividerEnableFiledMask) + { + if (m < n) // if enabled and dividend below divisor + { + if (n == pCinfo->DivisorFieldMask) + { + // special divisor DFS is using + ClkFreq = (ClkFreq * (m + 1)) >> pCinfo->DivisorFieldSize; + } + else + { + // initially may be general divisor + ClkFreq = (ClkFreq * (m + 1)) / (n + 1); + } + } + } + return ClkFreq; +} + +static void +CoreClockSwitch( + NvRmDeviceHandle hRmDevice, + const NvRmCoreClockInfo* pCinfo, + NvU32 SourceIndex, + NvU32 Divider, + NvBool SrcFirst, + NvRmFreqKHz CoreFreq) +{ + NvU32 reg; + + // Construct core source control register settings. + // Always use Idle clock mode; mode field = 2 ^ (Mode - 1) + NV_ASSERT(pCinfo->SelectorOffset); + NV_ASSERT(SourceIndex <= pCinfo->SourceFieldMasks[NvRmCoreClockMode_Idle]); + + reg = ( ((0x1 << (NvRmCoreClockMode_Idle - 1)) << pCinfo->ModeFieldShift) | + (SourceIndex << pCinfo->SourceFieldShifts[NvRmCoreClockMode_Idle]) ); + + if (reg != NV_REGR( + hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->SelectorOffset)) + { + // Update PLL reference + NvRmPrivCoreClockReAttach( + hRmDevice, pCinfo->SourceId, pCinfo->Sources[SourceIndex]); + } + + // Switch source and divider according to specified order. This guarantees + // that core frequency stays below maximum of "old" and "new" settings. + // Configure EMC LL path before and after clock switch. + if (pCinfo->SourceId == NvRmClockSource_CpuBus) + if ((hRmDevice->ChipId.Id == 0x15) || (hRmDevice->ChipId.Id == 0x16)) + NvRmPrivAp15SetEmcForCpuSrcSwitch(hRmDevice); + if (SrcFirst) + { + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->SelectorOffset, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + } + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->DividerOffset, Divider); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + if (!SrcFirst) + { + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->SelectorOffset, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + } + if (pCinfo->SourceId == NvRmClockSource_CpuBus) + if ((hRmDevice->ChipId.Id == 0x15) || (hRmDevice->ChipId.Id == 0x16)) + NvRmPrivAp15SetEmcForCpuDivSwitch(hRmDevice, CoreFreq, NV_FALSE); +} + +void +NvRmPrivCoreClockSourceIndexFind( + const NvRmCoreClockInfo* pCinfo, + NvRmClockSource SourceId, + NvU32* pSourceIndex) +{ + NvU32 i; + NV_ASSERT(pSourceIndex && pCinfo); + *pSourceIndex = NvRmClockSource_Num; // source index out of valid range + + // Find core descriptor index for the specified clock source + for (i = 0; i < NvRmClockSource_Num; i++) + { + if (pCinfo->Sources[i] == SourceId) + { + *pSourceIndex = i; + break; + } + } +} + +void +NvRmPrivCoreClockBestSourceFind( + const NvRmCoreClockInfo* pCinfo, + NvRmFreqKHz MaxFreq, + NvRmFreqKHz TargetFreq, + NvRmFreqKHz* pSourceFreq, + NvU32* pSourceIndex) +{ + NvU32 i; + NvRmFreqKHz SrcFreq = 0; + NvRmFreqKHz BestSrcFreq = 0; + NvU32 SrcIndex = NvRmClockSource_Num; // source index out of valid range + + NV_ASSERT(pSourceFreq && pSourceIndex && pCinfo); + + /* + * Find valid source with frequency closest to the requested one from + * the above; if such source does not exist, find source with frequency + * closest to the requested one from the below + */ + for (i = 0; i < NvRmClockSource_Num; i++) + { + SrcFreq = s_ClockSourceFreq[pCinfo->Sources[i]]; + if (SrcFreq == 0) + continue; + if (SrcFreq <= MaxFreq) + { + if (((BestSrcFreq < SrcFreq) && (BestSrcFreq < TargetFreq)) || + ((BestSrcFreq >= SrcFreq) && (SrcFreq >= TargetFreq))) + { + SrcIndex = i; + BestSrcFreq = SrcFreq; + } + } + } + *pSourceIndex = SrcIndex; + *pSourceFreq = BestSrcFreq; +} + +NvError +NvRmPrivCoreClockConfigure( + NvRmDeviceHandle hRmDevice, + const NvRmCoreClockInfo* pCinfo, + NvRmFreqKHz MaxFreq, + NvRmFreqKHz* pFreq, + NvRmClockSource* pSourceId) +{ + NvU32 m, n, reg; + NvBool SrcFirst; + NvRmFreqKHz ClkFreq; + NvRmFreqKHz SrcFreq = 0; + NvU32 SrcIndex = NvRmClockSource_Num; // source index out of valid range + + NV_ASSERT(hRmDevice); + NV_ASSERT(pFreq && pSourceId && pCinfo); + NV_ASSERT(*pSourceId < NvRmClockSource_Num); + + // 0 kHz is not achievable, anyway; changing target to 1 kHz will result in + // minimum configurable frequency + ClkFreq = *pFreq; + if (ClkFreq == 0) + ClkFreq = 1; + + /* + * If no valid source explicitly specified by the caller, determine the + * best clock source for the requested frequency. Otherwise, just use the + * requested source. + */ + if (*pSourceId == NvRmClockSource_Invalid) + { + NvRmPrivCoreClockBestSourceFind( + pCinfo, MaxFreq, ClkFreq, &SrcFreq, &SrcIndex); + } + else + { + SrcFreq = s_ClockSourceFreq[*pSourceId]; + if (SrcFreq <= MaxFreq) + { + NvRmPrivCoreClockSourceIndexFind(pCinfo, *pSourceId, &SrcIndex); + } + } + if (SrcIndex >= NvRmClockSource_Num) + { + // Could not find source + return NvError_NotSupported; + } + NV_ASSERT(SrcFreq); + + /* + * Determine super divider settings and enable divider if necessary. Always + * use maximum possible divisor n = divisor mask, so n+1 = 2^(divisor size). + * Hence, Fout = Fin * (m+1) / (n+1) = (Fin * (m+1)) >> (divisor size), and + * respectively, m = ((Fout << (divisor size)) / Fin) - do not subtract 1 + * as integer division would round down, anyway. Determine switching order: + * switch source 1st if new divider quotient is bigger than the old one. + */ + n = pCinfo->DivisorFieldMask; + m = (ClkFreq << pCinfo->DivisorFieldSize) / (SrcFreq + 1); + if ((m < n) && (m <= pCinfo->DividendFieldMask)) + { + NvU32 m_old, n_old; + SrcFirst = NV_FALSE; + reg = NV_REGR( + hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->DividerOffset); + if ( ((reg >> pCinfo->DividerEnableFiledShift) & + pCinfo->DividerEnableFiledMask) == pCinfo->DividerEnableFiledMask ) + { + m_old = (reg >> pCinfo->DividendFieldShift) & pCinfo->DividendFieldMask; + n_old = (reg >> pCinfo->DivisorFieldShift) & pCinfo->DivisorFieldMask; + if ( ((m + 1) * (n_old + 1)) > ((n + 1) * (m_old + 1)) ) + SrcFirst = NV_TRUE; + } + reg = (pCinfo->DividerEnableFiledMask << pCinfo->DividerEnableFiledShift) | + (m << pCinfo->DividendFieldShift) | (n << pCinfo->DivisorFieldShift); + // return actual clock frequency from the divider + *pFreq = (SrcFreq * (m + 1)) >> pCinfo->DivisorFieldSize; + } + else + { + SrcFirst = NV_TRUE; + reg = 0; // clear = disable divider + // return actual clock frequency from the source directly + *pFreq = SrcFreq; + } + // Finally set new core clock + CoreClockSwitch(hRmDevice, pCinfo, SrcIndex, reg, SrcFirst, *pFreq); + + // return selected source id and update core bus frequency + *pSourceId = pCinfo->Sources[SrcIndex]; + s_ClockSourceFreq[pCinfo->SourceId] = *pFreq; + if ((pCinfo->SourceId == NvRmClockSource_CpuBus) && + NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBridge)) + { + s_ClockSourceFreq[NvRmClockSource_CpuBridge] = NvRmPrivDividerFreqGet( + hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBridge)->pInfo.pDivider); + } + return NvSuccess; +} + +void +NvRmPrivCoreClockSet( + NvRmDeviceHandle hRmDevice, + const NvRmCoreClockInfo* pCinfo, + NvRmClockSource SourceId, + NvU32 m, + NvU32 n) +{ + NvU32 reg; + NvBool SrcFirst; + NvRmFreqKHz CoreFreq = 0; + NvU32 SrcIndex = NvRmClockSource_Num; // source index out of valid range + ExecPlatform env; + + + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo); + + env = NvRmPrivGetExecPlatform(hRmDevice); + + if (env == ExecPlatform_Fpga) + return; + + NvRmPrivCoreClockSourceIndexFind(pCinfo, SourceId, &SrcIndex); + NV_ASSERT(SrcIndex < NvRmClockSource_Num); + + /* + * Set divide: just cut off MSbits out of dividend and divisor range, and + * enable divider if m/n ration is below 1. Update new core frequency. + * Determine switching order: switch source 1st if new divider quotient is + * bigger than the old one. + */ + m &= pCinfo->DividendFieldMask; + n &= pCinfo->DivisorFieldMask; + CoreFreq = s_ClockSourceFreq[SourceId]; + if (m < n) + { + NvU32 m_old, n_old; + SrcFirst = NV_FALSE; + reg = NV_REGR( + hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->DividerOffset); + if ( ((reg >> pCinfo->DividerEnableFiledShift) & + pCinfo->DividerEnableFiledMask) == pCinfo->DividerEnableFiledMask ) + { + m_old = (reg >> pCinfo->DividendFieldShift) & pCinfo->DividendFieldMask; + n_old = (reg >> pCinfo->DivisorFieldShift) & pCinfo->DivisorFieldMask; + if ( ((m + 1) * (n_old + 1)) > ((n + 1) * (m_old + 1)) ) + SrcFirst = NV_TRUE; + } + reg = (pCinfo->DividerEnableFiledMask << pCinfo->DividerEnableFiledShift) | + (m << pCinfo->DividendFieldShift) | (n << pCinfo->DivisorFieldShift); + CoreFreq = (CoreFreq * (m + 1)) / (n + 1); + } + else + { + SrcFirst = NV_TRUE; + reg = 0; // clear = disable divider + } + // Finally set new core clock + CoreClockSwitch(hRmDevice, pCinfo, SrcIndex, reg, SrcFirst, CoreFreq); + + // update core bus frequency + s_ClockSourceFreq[pCinfo->SourceId] = CoreFreq; + if ((pCinfo->SourceId == NvRmClockSource_CpuBus) && + NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBridge)) + { + s_ClockSourceFreq[NvRmClockSource_CpuBridge] = NvRmPrivDividerFreqGet( + hRmDevice, + NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBridge)->pInfo.pDivider); + } +} + +/*****************************************************************************/ + +static NvRmSystemBusComplexInfo* +GetSystemBusComplexHandle(NvRmDeviceHandle hRmDevice) +{ + if (s_SystemBusComplex.BusRateOffset == 0) + { + NvU32 i, m; + const NvRmDividerClockInfo* pAhb = + NvRmPrivGetClockSourceHandle(NvRmClockSource_Ahb)->pInfo.pDivider; + const NvRmDividerClockInfo* pApb = + NvRmPrivGetClockSourceHandle(NvRmClockSource_Apb)->pInfo.pDivider; + NvOsMemset(&s_SystemBusComplex, 0, sizeof(s_SystemBusComplex)); + + // Confirm implied fixed AHB and APB dividers configuration and + // fill in other AHB and APB dividers parameters + NV_ASSERT(pAhb->Divider == NvRmClockDivider_Integer_1); + NV_ASSERT(pAhb->ClkControlField == pAhb->ClkDisableSettings); + NV_ASSERT(pApb->Divider == NvRmClockDivider_Integer_1); + NV_ASSERT(pApb->ClkControlField == pApb->ClkDisableSettings); + NV_ASSERT(pAhb->ClkControlOffset == pApb->ClkControlOffset); + + s_SystemBusComplex.BusRateOffset = pAhb->ClkControlOffset; + s_SystemBusComplex.BusClockDisableFields = + pAhb->ClkControlField | pApb->ClkControlField; + + s_SystemBusComplex.HclkDivisorFieldShift = pAhb->ClkRateFieldShift; + s_SystemBusComplex.HclkDivisorFieldMask = pAhb->ClkRateFieldMask; + for (i = 0, m = pAhb->ClkRateFieldMask; (m >> i) != 0; i++); + s_SystemBusComplex.HclkDivisorFieldSize = i; + + s_SystemBusComplex.PclkDivisorFieldShift = pApb->ClkRateFieldShift; + s_SystemBusComplex.PclkDivisorFieldMask = pApb->ClkRateFieldMask; + for (i = 0, m = pApb->ClkRateFieldMask; (m >> i) != 0; i++); + s_SystemBusComplex.PclkDivisorFieldSize = i; + + // Comfirm implied VDE divider configuration, and fill in VDE divider + // parameters provided System bus complex includes VDE clock; otherwise + // leave all VDE parameters cleared. + if (NvRmPrivGetClockSourceHandle(NvRmClockSource_Vbus)) + { + const NvRmDividerClockInfo* pVbus = + NvRmPrivGetClockSourceHandle(NvRmClockSource_Vbus)->pInfo.pDivider; + + NV_ASSERT(pVbus->Divider == NvRmClockDivider_Keeper16); + NV_ASSERT(pAhb->ClkControlOffset == pVbus->ClkControlOffset); + + s_SystemBusComplex.VclkDividendFieldShift = pVbus->ClkRateFieldShift; + s_SystemBusComplex.VclkDividendFieldMask = pVbus->ClkRateFieldMask; + for (i = 0, m = pVbus->ClkRateFieldMask; (m >> i) != 0; i++); + s_SystemBusComplex.VclkDividendFieldSize = i; + } + } + return &s_SystemBusComplex; +} + +void +NvRmPrivBusClockFreqSet( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz SystemFreq, + NvRmFreqKHz* pVclkFreq, + NvRmFreqKHz* pHclkFreq, + NvRmFreqKHz* pPclkFreq, + NvRmFreqKHz PclkMaxFreq) +{ + NvU32 VclkDividend, HclkDivisor, PclkDivisor, reg; + NvRmFreqKHz ClkFreq; + const NvRmSystemBusComplexInfo* pCinfo = + GetSystemBusComplexHandle(hRmDevice); + + NV_ASSERT(hRmDevice); + NV_ASSERT(SystemFreq); + NV_ASSERT(pHclkFreq && pPclkFreq); + + /* + * AHB clock divider: Fout = System Frequency / (n+1). Divider settings + * n = System Frequency / Hclk Frequency - 1. Avoid division for extreme + * cases of very small, or very large request via direct comparison. + */ + ClkFreq = *pHclkFreq; + if ((ClkFreq << pCinfo->HclkDivisorFieldSize) <= SystemFreq) + { + HclkDivisor = pCinfo->HclkDivisorFieldMask; + *pHclkFreq = SystemFreq >> pCinfo->HclkDivisorFieldSize; + } + else if ((ClkFreq << 1) > SystemFreq) + { + HclkDivisor = 0; + *pHclkFreq = SystemFreq; + } + else + { + HclkDivisor = (SystemFreq / ClkFreq) - 1; + *pHclkFreq = SystemFreq / (HclkDivisor + 1); + } + s_ClockSourceFreq[NvRmClockSource_Ahb] = *pHclkFreq; + + /* + * APB clock divider: Fout = AHB Frequency / (n+1). Divider settings + * n = AHB Frequency / Pclk Frequency - 1. Avoid division for extreme + * cases of very small, or very large request via direct comparison. + * Check against clock frequency maximum - this the only one bus clock + * that may have different (lower) maximum limit. + */ + ClkFreq = *pPclkFreq; + NV_ASSERT(ClkFreq <= PclkMaxFreq); + if ((ClkFreq << pCinfo->PclkDivisorFieldSize) <= (*pHclkFreq)) + { + PclkDivisor = pCinfo->PclkDivisorFieldMask; + *pPclkFreq = (*pHclkFreq) >> pCinfo->PclkDivisorFieldSize; + NV_ASSERT(*pPclkFreq <= PclkMaxFreq); + } + else if ((ClkFreq << 1) > (*pHclkFreq)) + { + PclkDivisor = ((*pHclkFreq) <= PclkMaxFreq)? 0 : 1; + *pPclkFreq = (*pHclkFreq) >> PclkDivisor; + } + else + { + PclkDivisor = ((*pHclkFreq) / ClkFreq); + if ((*pHclkFreq) <= PclkMaxFreq * PclkDivisor) + PclkDivisor--; + *pPclkFreq = (*pHclkFreq) / (PclkDivisor + 1); + } + s_ClockSourceFreq[NvRmClockSource_Apb] = *pPclkFreq; + + /* + * V-clock divider: Fout = System Frequency * (n + 1) / 2 ^ dividend size. + * Divider settings n = (Vclk Frequency << dividend size) / System Frequency. + * Do not subtract 1 as integer division would round down, anyway. If VDE + * clock is decoupled from the System bus, clear dividend and return 0 kHz. + */ + if (pCinfo->VclkDividendFieldMask) + { + NV_ASSERT(pVclkFreq); + if ((*pVclkFreq) >= SystemFreq) + { + VclkDividend = pCinfo->VclkDividendFieldMask; + *pVclkFreq = SystemFreq; + } + else + { + VclkDividend = + ((*pVclkFreq) << pCinfo->VclkDividendFieldSize) / (SystemFreq + 1); + *pVclkFreq = + (SystemFreq * (VclkDividend + 1)) >> pCinfo->VclkDividendFieldSize; + } + s_ClockSourceFreq[NvRmClockSource_Vbus] = *pVclkFreq; + } + else + { + VclkDividend = 0; + if (pVclkFreq) + *pVclkFreq = 0; + s_ClockSourceFreq[NvRmClockSource_Vbus] = 0; + } + + /* + * Set bus clocks dividers in bus rate control register. + * Always enable all bus clocks. + */ + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->BusRateOffset); + reg &= ((~pCinfo->BusClockDisableFields) & + (~(pCinfo->HclkDivisorFieldMask << pCinfo->HclkDivisorFieldShift)) & + (~(pCinfo->PclkDivisorFieldMask << pCinfo->PclkDivisorFieldShift)) & + (~(pCinfo->VclkDividendFieldMask << pCinfo->VclkDividendFieldShift))); + reg |= ((HclkDivisor << pCinfo->HclkDivisorFieldShift) | + (PclkDivisor << pCinfo->PclkDivisorFieldShift) | + (VclkDividend << pCinfo->VclkDividendFieldShift)); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->BusRateOffset, reg); +} + +void +NvRmPrivBusClockFreqGet( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz SystemFreq, + NvRmFreqKHz* pVclkFreq, + NvRmFreqKHz* pHclkFreq, + NvRmFreqKHz* pPclkFreq) +{ + NvU32 VclkDividend, HclkDivisor, PclkDivisor, reg; + const NvRmSystemBusComplexInfo* pCinfo = + GetSystemBusComplexHandle(hRmDevice); + + NV_ASSERT(hRmDevice); + NV_ASSERT(SystemFreq); + NV_ASSERT(pHclkFreq && pPclkFreq); + + // Get current bus dividers settings + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->BusRateOffset); + NV_ASSERT((reg & pCinfo->BusClockDisableFields) == 0); + + HclkDivisor = (reg >> pCinfo->HclkDivisorFieldShift) & pCinfo->HclkDivisorFieldMask; + PclkDivisor = (reg >> pCinfo->PclkDivisorFieldShift) & pCinfo->PclkDivisorFieldMask; + VclkDividend = (reg >> pCinfo->VclkDividendFieldShift) & pCinfo->VclkDividendFieldMask; + + /* + * AHB clock divider: Fout = System Frequency / (n+1). Avoid division + * for extreme cases of min/max divider values. + */ + if (HclkDivisor == 0) + *pHclkFreq = SystemFreq; + else if (HclkDivisor == pCinfo->HclkDivisorFieldMask) + *pHclkFreq = SystemFreq >> pCinfo->HclkDivisorFieldSize; + else + *pHclkFreq = SystemFreq / (HclkDivisor + 1); + + /* + * APB clock divider: Fout = AHB Frequency / (n+1). Avoid division + * for extreme cases of min/max divider values. + */ + if (PclkDivisor == 0) + *pPclkFreq = *pHclkFreq; + else if (PclkDivisor == pCinfo->PclkDivisorFieldMask) + *pPclkFreq = (*pHclkFreq) >> pCinfo->PclkDivisorFieldSize; + else + *pPclkFreq = (*pHclkFreq) / (PclkDivisor + 1); + + /* + * V-clock divider: Fout = System Frequency * (n + 1) / 2 ^ dividend size. + * If VDE clock is decoupled from the System bus, return 0 kHz. + */ + if (pCinfo->VclkDividendFieldMask) + { + NV_ASSERT(pVclkFreq); + *pVclkFreq = + (SystemFreq * (VclkDividend + 1)) >> pCinfo->VclkDividendFieldSize; + } + else if (pVclkFreq) + *pVclkFreq = 0; +} + +/*****************************************************************************/ + +void +NvRmPrivPllFreqUpdate( + NvRmDeviceHandle hRmDevice, + const NvRmPllClockInfo* pCinfo) +{ + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo); + + s_ClockSourceFreq[pCinfo->SourceId] = + NvRmPrivAp15PllFreqGet(hRmDevice, pCinfo); +} + +void +NvRmPrivDividerFreqUpdate( + NvRmDeviceHandle hRmDevice, + const NvRmDividerClockInfo* pCinfo) +{ + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo); + + s_ClockSourceFreq[pCinfo->SourceId] = + NvRmPrivDividerFreqGet(hRmDevice, pCinfo); +} + +void +NvRmPrivDividerSet( + NvRmDeviceHandle hRmDevice, + const NvRmDividerClockInfo* pCinfo, + NvU32 setting) +{ + NvU32 reg; + + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo); + NV_ASSERT(pCinfo->ClkControlOffset); + + reg = NV_REGR( + hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->ClkControlOffset); + + // Make sure divider is enabled. Update rate field for divider with + // variable divisor + reg &= (~(pCinfo->ClkControlField)); + reg |= pCinfo->ClkEnableSettings; + if (pCinfo->FixedRateSetting == NVRM_VARIABLE_DIVIDER) + { + reg &= (~(pCinfo->ClkRateFieldMask << pCinfo->ClkRateFieldShift)); + reg |= ((setting & pCinfo->ClkRateFieldMask) << pCinfo->ClkRateFieldShift); + } + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->ClkControlOffset, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + s_ClockSourceFreq[pCinfo->SourceId] = NvRmPrivDividerFreqGet(hRmDevice, pCinfo); +} + +NvRmFreqKHz +NvRmPrivDividerFreqGet( + NvRmDeviceHandle hRmDevice, + const NvRmDividerClockInfo* pCinfo) +{ + NvRmFreqKHz DividerKHz; + NvU32 reg, n; + + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo); + NV_ASSERT(pCinfo->ClkControlOffset); + + reg = NV_REGR( + hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->ClkControlOffset); + + // Return 0 kHz if divider is disabled + if ((pCinfo->ClkControlField != 0) && + ((reg & pCinfo->ClkControlField) == pCinfo->ClkDisableSettings)) + { + return 0; + } + // Determine divider rate setting + n = pCinfo->FixedRateSetting; + if (n == NVRM_VARIABLE_DIVIDER) + { + n = ((reg >> pCinfo->ClkRateFieldShift) & pCinfo->ClkRateFieldMask); + } + + // Calculate output frequency + DividerKHz = s_ClockSourceFreq[pCinfo->InputId]; + switch (pCinfo->Divider) + { + case NvRmClockDivider_Keeper16: + return ((DividerKHz * (n + 1)) >> 4); + case NvRmClockDivider_Skipper16: + return ((DividerKHz * (16 - n)) >> 4); + case NvRmClockDivider_Fractional_2: + n += 2; + DividerKHz = DividerKHz << 1; + break; + case NvRmClockDivider_Integer_1: + n += 1; + break; + case NvRmClockDivider_Integer: + break; + default: + NV_ASSERT(!"Invalid divider type"); + return 0; + } + NV_ASSERT(n != 0); + return (DividerKHz / n); +} + + +// Shortcut (this mask can be retrieved from module clock information table) +#define NVRM_FRACTIONAL_DIVISOR_FIELD_MASK (0xFF) + +NvU32 +NvRmPrivFindFreqMinAbove( + NvRmClockDivider DividerType, + NvRmFreqKHz SourceKHz, + NvRmFreqKHz MaxKHz, + NvRmFreqKHz* pTargetKHz) +{ + NvU32 n; + NV_ASSERT(pTargetKHz); + NV_ASSERT( ((*pTargetKHz) != 0) && ((*pTargetKHz) <= MaxKHz) ); + NV_ASSERT(DividerType == NvRmClockDivider_Fractional_2); // only this type + + /* + * Get fractional divider setting n for the best target approximation from + * the above. Fractional divider: FoutKHz = (2 * FinKHz) / (n + 2) + */ + if ((*pTargetKHz) < SourceKHz) + { + SourceKHz = SourceKHz << 1; + n = SourceKHz / (*pTargetKHz); + if (SourceKHz > n * MaxKHz) + n++; + *pTargetKHz = SourceKHz / n; + n = n - 2; + } + else + { + n = 0; + *pTargetKHz = SourceKHz; + } + NV_ASSERT(n <= NVRM_FRACTIONAL_DIVISOR_FIELD_MASK); + return n; +} + +NvU32 +NvRmPrivFindFreqMaxBelow( + NvRmClockDivider DividerType, + NvRmFreqKHz SourceKHz, + NvRmFreqKHz MaxKHz, + NvRmFreqKHz* pTargetKHz) +{ + NvU32 n; + NV_ASSERT(pTargetKHz); + NV_ASSERT( ((*pTargetKHz) != 0) && ((*pTargetKHz) <= MaxKHz) ); + NV_ASSERT(DividerType == NvRmClockDivider_Fractional_2); // only this type + + /* + * Get fractional divider setting n for the best target approximation from + * the below. Fractional divider: FoutKHz = (2 * FinKHz) / (n + 2) + */ + if ((*pTargetKHz) < SourceKHz) + { + SourceKHz = SourceKHz << 1; + n = (SourceKHz + (*pTargetKHz) - 1) / (*pTargetKHz); + *pTargetKHz = SourceKHz / n; + n = n - 2; + } + else + { + n = 0; + *pTargetKHz = SourceKHz; + } + NV_ASSERT(n <= NVRM_FRACTIONAL_DIVISOR_FIELD_MASK); + return n; +} + +void +NvRmPrivSelectorClockSet( + NvRmDeviceHandle hRmDevice, + const NvRmSelectorClockInfo* pCinfo, + NvRmClockSource SourceId, + NvBool Double) +{ + NvU32 i, reg; + NvRmFreqKHz SourceFreq; + NvU32 SrcIndex = NvRmClockSource_Num; // source index out of valid range + + NV_ASSERT(hRmDevice); + NV_ASSERT(pCinfo); + NV_ASSERT(pCinfo->SelectorOffset); + + // Find selector index for the specified input clock source + for (i = 0; i < NvRmClockSource_Num; i++) + { + if (pCinfo->Sources[i] == SourceId) + { + SrcIndex = i; + break; + } + } + NV_ASSERT(SrcIndex < NvRmClockSource_Num); + + // Select specified clock source + NV_ASSERT(SrcIndex <= pCinfo->SourceFieldMask); + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->SelectorOffset); + reg &= (~(pCinfo->SourceFieldMask << pCinfo->SourceFieldShift)); + reg |= (SrcIndex << pCinfo->SourceFieldShift); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->SelectorOffset, reg); + SourceFreq = s_ClockSourceFreq[SourceId]; + + // Enable/Disable doubler + if (pCinfo->DoublerEnableField != 0) + { + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->DoublerEnableOffset); + if (Double) + { + reg |= pCinfo->DoublerEnableField; + SourceFreq = SourceFreq << 1; + } + else + { + reg &= (~pCinfo->DoublerEnableField); + SourceFreq = 0; // no clock out if doubler disabled + } + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->DoublerEnableOffset, reg); + } + s_ClockSourceFreq[pCinfo->SourceId] = SourceFreq; +} + +/*****************************************************************************/ + +void NvRmPrivParseClockSources( + NvRmClockSourceInfo* pDst, + NvU32 DestinationTableSize, + NvRmClockSourceInfoPtr Src, + NvU32 SourceTableSize, + NvRmClockSourceType SourceType) +{ + NvU32 i; + NvRmClockSource id = NvRmClockSource_Invalid; + NV_ASSERT(pDst); + + for (i = 0; i < SourceTableSize; i++) + { + // Bsed on specified source type retrieve source id + // from the source table + switch (SourceType) + { + case NvRmClockSourceType_Fixed: + id = Src.pFixed[i].SourceId; + pDst[id].pInfo.pFixed = &Src.pFixed[i]; + break; + case NvRmClockSourceType_Pll: + id = Src.pPll[i].SourceId; + pDst[id].pInfo.pPll = &Src.pPll[i]; + break; + case NvRmClockSourceType_Divider: + id = Src.pDivider[i].SourceId; + pDst[id].pInfo.pDivider = &Src.pDivider[i]; + break; + case NvRmClockSourceType_Core: + id = Src.pCore[i].SourceId; + pDst[id].pInfo.pCore = &Src.pCore[i]; + break; + case NvRmClockSourceType_Selector: + id = Src.pSelector[i].SourceId; + pDst[id].pInfo.pSelector = &Src.pSelector[i]; + break; + default: + NV_ASSERT(!"Not defined source type"); + } + // Fill in destination table + NV_ASSERT((NvU32)id < DestinationTableSize); + NV_ASSERT(pDst[id].SourceId == NvRmClockSource_Invalid); + pDst[id].SourceId = id; + pDst[id].SourceType = SourceType; + } +} + +NvRmClockSourceInfo* NvRmPrivGetClockSourceHandle(NvRmClockSource id) +{ + NvRmClockSourceInfo* pSource = NULL; + + NV_ASSERT((id != NvRmClockSource_Invalid) && (id < NvRmClockSource_Num)); + if (s_ClockSourceTable[id].SourceId == id) + { + pSource = &s_ClockSourceTable[id]; + NV_ASSERT(pSource->pInfo.pFixed); + } + return pSource; +} + +NvRmFreqKHz +NvRmPrivGetClockSourceFreq(NvRmClockSource id) +{ + NV_ASSERT(id < NvRmClockSource_Num); + return s_ClockSourceFreq[id]; +} + +NvRmFreqKHz +NvRmPowerGetPrimaryFrequency( + NvRmDeviceHandle hRmDeviceHandle) +{ + return s_ClockSourceFreq[NvRmClockSource_ClkM]; +} + +NvBool +NvRmPrivIsSourceSelectedByModule( + NvRmDeviceHandle hRmDevice, + NvRmClockSource SourceId, + NvRmModuleID ModuleId) +{ + NvError Error; + NvU32 SourceIndex = 0; + NvRmModuleClockInfo* pCinfo; + NvRmModuleInstance* pInst = NULL; + NV_ASSERT(hRmDevice); + + Error = NvRmPrivGetModuleInstance(hRmDevice, ModuleId, &pInst); + if (Error != NvSuccess) + return NV_FALSE; // Module is not present - not using anything + + pCinfo = (NvRmModuleClockInfo*)pInst->ModuleData; + if (pCinfo->ClkSourceOffset != 0) + { + SourceIndex = NV_REGR( + hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, pCinfo->ClkSourceOffset); + SourceIndex = + (SourceIndex >> pCinfo->SourceFieldShift) & pCinfo->SourceFieldMask; + } + return (pCinfo->Sources[SourceIndex] == SourceId); +} + +NvBool +NvRmIsFreqRangeReachable( + NvRmFreqKHz SourceFreq, + NvRmFreqKHz MinFreq, + NvRmFreqKHz MaxFreq, + NvU32 MaxDivisor) +{ + NvU32 divisor; + NV_ASSERT(SourceFreq && MaxFreq); + NV_ASSERT(MinFreq <= MaxFreq); + + // Determine minimum divisor that satisfies maximum boundary + divisor = SourceFreq / MaxFreq; + if ((divisor * MaxFreq) < SourceFreq) + { + divisor += 1; + } + // The specified range is reachable if minimum divisor is + // fits divisor field and satisfies minimum boundary + if ((divisor <= MaxDivisor) && + ((divisor * MinFreq) <= SourceFreq)) + { + return NV_TRUE; + } + return NV_FALSE; +} + +const NvRmModuleClockLimits* +NvRmPrivGetSocClockLimits(NvRmModuleID Module) +{ + NV_ASSERT(Module < NvRmPrivModuleID_Num); + return &s_ModuleClockLimits[Module]; +} + +void NvRmPrivLockSharedPll(void) +{ + NvOsMutexLock(s_hPllMutex); +} + +void NvRmPrivUnlockSharedPll(void) +{ + NvOsMutexUnlock(s_hPllMutex); +} + +void NvRmPrivLockModuleClockState(void) +{ + NvOsSpinMutexLock(s_hClockMutex); +} + +void NvRmPrivUnlockModuleClockState(void) +{ + NvOsSpinMutexUnlock(s_hClockMutex); +} + +/*****************************************************************************/ +/*****************************************************************************/ + +// PLLC may be selected as a source only for Display, TVO, GPU, and VDE +// modules. (It is also used for CPU and System/Avp core clocks, controlled +// by DFS with its own configuration path - no need to specify here) +static const NvRmModuleID s_Ap15PllC0UsagePolicy[] = +{ + NvRmModuleID_Display, + NvRmModuleID_3D, + NvRmModuleID_2D, + NvRmModuleID_Mpe, + NvRmModuleID_Hdmi, +}; + +static const NvRmModuleID s_Ap20PllC0UsagePolicy[] = +{ + NvRmModuleID_Display, + NvRmModuleID_Tvo, + NvRmModuleID_3D, + NvRmModuleID_2D, + NvRmModuleID_Epp, + NvRmModuleID_Mpe, + NvRmModuleID_Hdmi, + NvRmModuleID_Vde +}; + +// PLLM may be selected as a source for GPU, UART and VDE modules. (It is also +// used for EMC, CPU and System/Avp core clocks, controlled by DFS with its +// own configuration path - no need to specify here) +static const NvRmModuleID s_Ap15PllM0UsagePolicy[] = +{ + NvRmModuleID_GraphicsHost, + NvRmModuleID_Vi, + NvRmModuleID_3D, + NvRmModuleID_2D, + NvRmModuleID_Epp, + NvRmModuleID_Mpe, + NvRmModuleID_Vde, + NvRmModuleID_Uart +}; + +// PLLD may be selected as a source only for Display, HDMI, and DSI modules. +static const NvRmModuleID s_Ap15PllD0UsagePolicy[] = +{ + NvRmModuleID_Display, + NvRmModuleID_Hdmi, + NvRmModuleID_Dsi +}; + +// PLLA may be selected as a source only for I2S and SPDIF modules. +static const NvRmModuleID s_Ap15PllA0UsagePolicy[] = +{ + NvRmModuleID_I2s, + NvRmModuleID_Spdif, +}; + +static const NvRmModuleID* +GetPolicySourceToModuleList( + NvRmDeviceHandle hRmDevice, + NvRmClockSource SourceId, + NvU32* pListSize) +{ + NV_ASSERT(hRmDevice && pListSize); + + // Unless explicitly overwritten, use AP15 policy as a base for all SoCs; + // return list of modules that may use specified source + switch (SourceId) + { + case NvRmClockSource_PllC0: + if (hRmDevice->ChipId.Id == 0x20) + { + *pListSize = NV_ARRAY_SIZE(s_Ap20PllC0UsagePolicy); + return s_Ap20PllC0UsagePolicy; + } + *pListSize = NV_ARRAY_SIZE(s_Ap15PllC0UsagePolicy); + return s_Ap15PllC0UsagePolicy; + + case NvRmClockSource_PllM0: + *pListSize = NV_ARRAY_SIZE(s_Ap15PllM0UsagePolicy); + return s_Ap15PllM0UsagePolicy; + + case NvRmClockSource_PllD0: + *pListSize = NV_ARRAY_SIZE(s_Ap15PllD0UsagePolicy); + return s_Ap15PllD0UsagePolicy; + + case NvRmClockSource_PllA0: + case NvRmClockSource_AudioSync: + *pListSize = NV_ARRAY_SIZE(s_Ap15PllA0UsagePolicy); + return s_Ap15PllA0UsagePolicy; + + default: + *pListSize = 0; + return NULL; // No policy - any module may use the source + } +} + +NvBool +NvRmPrivIsSourceProtected( + NvRmDeviceHandle hRmDevice, + NvRmModuleID Module, + NvRmClockSource SourceId) +{ + NvU32 i, ListSize; + const NvRmModuleID* pModuleList = GetPolicySourceToModuleList( + hRmDevice, SourceId, &ListSize); + + if (pModuleList) + { + // Policy in place - check the module against it + NV_ASSERT(ListSize); + for (i = 0; i < ListSize; i++) + { + if (Module == pModuleList[i]) + return NV_FALSE; + } + return NV_TRUE; + } + else + { + // No policy for this source - just make sure I2C module is + // on main clock only + if (SourceId != NvRmClockSource_ClkM) + { + if ((Module == NvRmModuleID_Dvc) || + (Module == NvRmModuleID_I2c)) + return NV_TRUE; + } + return NV_FALSE; + } +} + +/*****************************************************************************/ + +void +NvRmPrivReConfigurePllX( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz TargetFreq) +{ + NvRmClockSource SourceId; + NvRmFreqKHz f = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllX0); + const NvRmCoreClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBus)->pInfo.pCore; + NvRmFreqKHz MaxFreq = NvRmPrivGetSocClockLimits(NvRmModuleID_Cpu)->MaxKHz; + + NV_ASSERT(NvRmPrivGetClockSourceHandle(NvRmClockSource_PllX0)); + NV_ASSERT(TargetFreq <= MaxFreq); + + // Do nothing if current PLLX frequency is below + // and close enough to the target + if (f <= TargetFreq) // if below - DVS-safe + { + f += (MaxFreq >> pCinfo->DivisorFieldSize); // CPU divider resolution + if (f >= TargetFreq) + return; + } + + /* + * If PLLX is in use by CPU switch CPU to back-up PLLP0 source during PLLX + * reconfiguration. This is DVS safe as per DFS policy, PLLX is used for + * high frequencies above PLLP0 output. In any case, configure PLLX target + * frequency, and let the caller to complete CPU clock configuration (PLLX + * is used for CPU only, so the caller is always CPU DVFS) + */ + SourceId = NvRmPrivCoreClockSourceGet(hRmDevice, pCinfo); + if (SourceId == NvRmClockSource_PllX0) + { + SourceId = NvRmClockSource_PllP0; + f = NvRmPrivGetClockSourceFreq(SourceId); + NV_ASSERT(f <= MaxFreq); + NV_ASSERT_SUCCESS(NvRmPrivCoreClockConfigure( + hRmDevice, pCinfo, MaxFreq, &f, &SourceId)); + } + SourceId = NvRmClockSource_PllX0; + NvRmPrivAp15PllConfigureSimple(hRmDevice, SourceId, TargetFreq, &TargetFreq); +} + +/*****************************************************************************/ + +static void BackupClockSource( + NvRmDeviceHandle hRmDevice, + NvRmModuleClockInfo* pCinfo, + NvRmClockSource BackupSource) +{ + NvBool Disabled; + NvU32 reg, SourceIndex; + NvRmModuleID ModuleId; + + NV_ASSERT(pCinfo); + ModuleId = NVRM_MODULE_ID(pCinfo->Module, pCinfo->Instance); + + // Check if currently clock is disabled + NV_ASSERT(pCinfo->ClkEnableOffset); + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->ClkEnableOffset); + Disabled = ((reg & pCinfo->ClkEnableField) != pCinfo->ClkEnableField); + + // Find backup source index + for (SourceIndex = 0; SourceIndex < NvRmClockSource_Num; SourceIndex++) + { + if (pCinfo->Sources[SourceIndex] == BackupSource) + break; + } + NV_ASSERT(SourceIndex < NvRmClockSource_Num); + + // Switch module to backup source clock. If module clock is disabled, + // temporarily enable it. + if (Disabled) + { + NvRmPrivEnableModuleClock(hRmDevice, ModuleId, ModuleClockState_Enable); + } + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->ClkSourceOffset); + reg &= (~(pCinfo->SourceFieldMask << pCinfo->SourceFieldShift)); + reg |= (SourceIndex << pCinfo->SourceFieldShift); + NV_REGW(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->ClkSourceOffset, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + if (Disabled) + { + NvRmPrivEnableModuleClock(hRmDevice, ModuleId, ModuleClockState_Disable); + } +} + +static void RestoreClockSource( + NvRmDeviceHandle hRmDevice, + NvRmModuleClockInfo* pCinfo, + NvRmModuleClockState* pCstate, + NvRmFreqKHz NewSourceFreq) +{ + NvU32 reg; + NvBool Disabled; + NvRmModuleID ModuleId; + + NV_ASSERT(pCinfo && pCstate); + ModuleId = NVRM_MODULE_ID(pCinfo->Module, pCinfo->Instance); + + // Check if currently clock is disabled + NV_ASSERT(pCinfo->ClkEnableOffset); + reg = NV_REGR(hRmDevice, NvRmPrivModuleID_ClockAndReset, 0, + pCinfo->ClkEnableOffset); + Disabled = ((reg & pCinfo->ClkEnableField) != pCinfo->ClkEnableField); + + // Restore module clock source If module clock is disabled, temporarily + // enable it. Update module v-scale requirements. + if (Disabled) + { + NvRmPrivEnableModuleClock(hRmDevice, ModuleId, ModuleClockState_Enable); + } + NvRmPrivModuleClockSet(hRmDevice, pCinfo, pCstate); + if (Disabled) + { + NvRmPrivEnableModuleClock(hRmDevice, ModuleId, ModuleClockState_Disable); + } + NvRmPrivModuleVscaleReAttach(hRmDevice, + pCinfo, pCstate, pCstate->actual_freq, NewSourceFreq, NV_FALSE); +} + +static void BackupModuleClocks( + NvRmDeviceHandle hRmDevice, + NvRmModuleID Module, + NvRmClockSource UpdatedSource, + NvRmClockSource BackupSource) +{ + NvU32 j; + NvBool SubClock = NV_FALSE; + NvRmModuleClockInfo* pCinfo = NULL; + NvRmModuleClockState* pCstate = NULL; + + for (j = NvRmModuleGetNumInstances(hRmDevice, Module); j != 0; j--) + { + NV_ASSERT_SUCCESS(NvRmPrivGetClockState( + hRmDevice, NVRM_MODULE_ID(Module, j-1), &pCinfo, &pCstate)); + do + { + // If on updated source, switch module to backup source. Note + // that module clock state records are preserved and will be used + // to restore clock configuration after source update completed. + NV_ASSERT(NvRmPrivGetClockSourceFreq(BackupSource) <= + NvRmPrivGetClockSourceFreq(UpdatedSource)); + if (pCinfo->Sources[pCstate->SourceClock] == UpdatedSource) + BackupClockSource(hRmDevice, pCinfo, BackupSource); + + // Check if module subclock should be backed up as well + // TODO: boundary check + pCinfo++; + pCstate++; + SubClock = (pCinfo->Module == Module) && + (pCinfo->Instance == (j - 1)); + } while (SubClock); + } +} + +static void +RestoreModuleClocks( + NvRmDeviceHandle hRmDevice, + NvRmModuleID Module, + NvRmClockSource UpdatedSource, + NvRmFreqKHz NewSourceFreq) +{ + NvU32 j; + NvRmFreqKHz MaxFreq; + NvBool SubClock = NV_FALSE; + NvRmModuleClockInfo* pCinfo = NULL; + NvRmModuleClockState* pCstate = NULL; + + MaxFreq = NvRmPrivGetSocClockLimits(Module)->MaxKHz; + for (j = NvRmModuleGetNumInstances(hRmDevice, Module); j != 0; j--) + { + NV_ASSERT_SUCCESS(NvRmPrivGetClockState( + hRmDevice, NVRM_MODULE_ID(Module, j-1), &pCinfo, &pCstate)); + do + { + // Restore updated module clock source, and set divider to get as + // close/above to previous frequency as new source output allows. + if (pCinfo->Sources[pCstate->SourceClock] == UpdatedSource) + { + pCstate->Divider = NvRmPrivFindFreqMinAbove( + pCinfo->Divider, NewSourceFreq, MaxFreq, &pCstate->actual_freq); + RestoreClockSource(hRmDevice, pCinfo, pCstate, NewSourceFreq); + } + + // Check if module subclock should be backed up as well + // TODO: boundary check + pCinfo++; + pCstate++; + SubClock = (pCinfo->Module == Module) && + (pCinfo->Instance == (j - 1)); + } while (SubClock); + } +} + +/*****************************************************************************/ + +static void PllCBackupModuleClocks(NvRmDeviceHandle hRmDevice) +{ + NvU32 i, ListSize; + NvRmModuleID Module; + const NvRmModuleID* pModuleList = GetPolicySourceToModuleList( + hRmDevice, NvRmClockSource_PllC0, &ListSize); + NV_ASSERT(pModuleList && ListSize); + + // Check all modules that can use PLLC0 as a source, and switch to PLLP0 + // as a backcup source + for (i = 0; i < ListSize; i++) + { + Module = pModuleList[i]; + BackupModuleClocks( + hRmDevice, Module, NvRmClockSource_PllC0, NvRmClockSource_PllP0); + } +} + +static void +PllCRestoreModuleClocks( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz NewPllCFreq) +{ + NvU32 i, ListSize; + NvRmModuleID Module; + const NvRmModuleID* pModuleList = GetPolicySourceToModuleList( + hRmDevice, NvRmClockSource_PllC0, &ListSize); + NV_ASSERT(pModuleList && ListSize); + + // Check all modules that can use PLLC0 as a source, and restore source + // configuration + for (i = 0; i < ListSize; i++) + { + // Skip display (PLLC is adjusted as part of display configuration) + Module = pModuleList[i]; + if (Module == NvRmModuleID_Display) + continue; + + RestoreModuleClocks( + hRmDevice, Module, NvRmClockSource_PllC0, NewPllCFreq); + } +} + +static NvRmFreqKHz PllCBackupCpuClock(NvRmDeviceHandle hRmDevice) +{ + NvRmClockSource SourceId; + NvRmFreqKHz OldCpuFreq = 0; + const NvRmCoreClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBus)->pInfo.pCore; + + // If PLLC0 is used as a source for CPU clock - switch CPU to PLLP0, and + // return saved CPU clock frequency (to be restored). Note that DVFS uses + // PLLC0 as a source only for frequencies above PLLP0 + SourceId = NvRmPrivCoreClockSourceGet(hRmDevice, pCinfo); + if (SourceId == NvRmClockSource_PllC0) + { + OldCpuFreq = NvRmPrivGetClockSourceFreq(NvRmClockSource_CpuBus); + NV_ASSERT(NvRmPrivGetClockSourceFreq(NvRmClockSource_PllP0) <= + OldCpuFreq); + NV_ASSERT(NvRmPrivGetClockSourceFreq(NvRmClockSource_PllP0) <= + NvRmPrivGetSocClockLimits(NvRmModuleID_Cpu)->MaxKHz); + NvRmPrivCoreClockSet(hRmDevice, pCinfo, NvRmClockSource_PllP0, 0, 0); + } + return OldCpuFreq; // frequency for restoration, or 0 if no restoration +} + +static void +PllCRestoreCpuClock( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz NewPllCFreq, + NvRmFreqKHz OldCpuFreq) +{ + // Restore CPU clock as high as new PLLC0 output allows, provoded PLLC0 + // was used as a source for CPU + if (OldCpuFreq != 0) + { + NvRmClockSource SourceId = NvRmClockSource_PllC0; + NvRmFreqKHz CpuFreq = NV_MIN(NewPllCFreq, OldCpuFreq); + const NvRmCoreClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBus)->pInfo.pCore; + NvRmFreqKHz MaxFreq = + NvRmPrivGetSocClockLimits(NvRmModuleID_Cpu)->MaxKHz; + + NV_ASSERT_SUCCESS(NvRmPrivCoreClockConfigure( + hRmDevice, pCinfo, MaxFreq, &CpuFreq, &SourceId)); + } +} + +static NvRmFreqKHz PllCBackupSystemClock(NvRmDeviceHandle hRmDevice) +{ + NvRmClockSource SourceId; + NvRmFreqKHz OldSysFreq = 0; + const NvRmCoreClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_SystemBus)->pInfo.pCore; + + // If PLLC1 divider output is used as a source for System clock - switch + // System clock to to PLLP2, and return saved System clock frequency (to + // be restored). Note that DVFS uses PLLC1 as a source starting with AP20 + SourceId = NvRmPrivCoreClockSourceGet(hRmDevice, pCinfo); + if (SourceId == NvRmClockSource_PllC1) + { + OldSysFreq = NvRmPrivGetClockSourceFreq(NvRmClockSource_SystemBus); + NV_ASSERT(hRmDevice->ChipId.Id >= 0x20); + NV_ASSERT(NvRmPrivGetClockSourceFreq(NvRmClockSource_PllP2) <= + NvRmPrivGetSocClockLimits(NvRmPrivModuleID_System)->MaxKHz); + NvRmPrivCoreClockSet(hRmDevice, pCinfo, NvRmClockSource_PllP2, 0, 0); + } + return OldSysFreq; // frequency for restoration, or 0 if no restoration +} + +static void +PllCRestoreSystemClock( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz NewPllCFreq, + NvRmFreqKHz OldSysFreq) +{ + NvU32 divc1; + NvRmFreqKHz SysFreq; + const NvRmClockSourceInfo* pSrcCinfo; + NvRmClockSource SourceId = NvRmClockSource_PllC1; + NvRmFreqKHz MaxFreq = + NvRmPrivGetSocClockLimits(NvRmPrivModuleID_System)->MaxKHz; + + // Reconfigure PLLC1 divider at maximum possible frequency + SysFreq = MaxFreq; + divc1 = NvRmPrivFindFreqMaxBelow( + NvRmClockDivider_Fractional_2, NewPllCFreq, MaxFreq, &SysFreq); + pSrcCinfo = NvRmPrivGetClockSourceHandle(NvRmClockSource_PllC1); + NvRmPrivDividerSet(hRmDevice, pSrcCinfo->pInfo.pDivider, divc1); + + // Restore System clock as high as new PLLC1 output allows, provoded PLLC1 + // was used as a source for System clock + if (OldSysFreq != 0) + { + SysFreq = NV_MIN(SysFreq, OldSysFreq); + pSrcCinfo = NvRmPrivGetClockSourceHandle(NvRmClockSource_SystemBus); + NV_ASSERT_SUCCESS(NvRmPrivCoreClockConfigure( + hRmDevice, pSrcCinfo->pInfo.pCore, MaxFreq, &SysFreq, &SourceId)); + NvRmPrivBusClockInit(hRmDevice, SysFreq); + } +} + +NvRmFreqKHz NvRmPrivGetMaxFreqPllC(NvRmDeviceHandle hRmDevice) +{ + // PLLC maximum limit is fixed for SoC with dedicated CPU PLLX; otherwise + // it is equal to CPU maximum frequency, as PLLC is a primary CPU source. + if (NvRmPrivGetClockSourceHandle(NvRmClockSource_PllX0)) + return NVRM_PLLC_DEFAULT_FREQ_KHZ; + else + return NvRmPrivGetSocClockLimits(NvRmModuleID_Cpu)->MaxKHz; +} + +/* + * PLLC is reconfigured: + * (a) when RM is setting fast clocks during boot/resume from deep sleep, + * provided PLLC is not already in use by any of the display heads + * (b) when DDK/ODM is reconfiguring display clock (typically PLLC is required + * for CRT) + * + * In both cases core voltage is set at nominal - reconfiguration is DVS-save. + * Core clocks that use PLLC: CPU and System bus (starting with AP20) - are + * switched to PLLP during reconfiguration and restored afterwards. Module + * clocks that use PLLC are backed up to PLLP and then restored as well, with + * the exception of display, which does not need restoration in a process of + * reconfiguration (case b). + */ +void +NvRmPrivReConfigurePllC( + NvRmDeviceHandle hRmDevice, + NvRmFreqKHz TargetFreq) +{ + NvRmFreqKHz CpuFreq, SysFreq, MaxFreq; + const NvRmCoreClockInfo* pCinfo = + NvRmPrivGetClockSourceHandle(NvRmClockSource_CpuBus)->pInfo.pCore; + NvBool IsHdmi = NvRmIsFixedHdmiKHz(TargetFreq); + + // If maximum PLLC target is requested, and current PLLC output is close + // enough - exit without adjusting PLLC (use CPU divider resolution as + // "close enough" criteria). For specific PLLC target, find multiple of + // target frequency as close as possible to PLLC maximum limit. + MaxFreq = NvRmPrivGetMaxFreqPllC(hRmDevice); + if (TargetFreq == NvRmFreqMaximum) + { + TargetFreq = NvRmPrivGetClockSourceFreq(NvRmClockSource_PllC0); + if (TargetFreq <= MaxFreq) + { + TargetFreq += (MaxFreq >> pCinfo->DivisorFieldSize); + if (TargetFreq >= MaxFreq) + return; + } + TargetFreq = MaxFreq; + } + NV_ASSERT(TargetFreq <= MaxFreq); + NV_ASSERT((TargetFreq * NVRM_DISPLAY_DIVIDER_MAX) >= MaxFreq); + TargetFreq = (MaxFreq / TargetFreq) * TargetFreq; + + // Backup core and/or module clocks that are using PLLC as a clock source + // at the moment. Reconfigure PLLC to the new target, and restore backuped + // clocks as close as possible with the new PLLC output frequency + CpuFreq = PllCBackupCpuClock(hRmDevice); + SysFreq = PllCBackupSystemClock(hRmDevice); + PllCBackupModuleClocks(hRmDevice); + + // For 720p or 1080i/1080p HDMI - use fixed PLLC configuration; + // for other targets use simple variable configuration + if (IsHdmi) + NvRmPrivAp15PllConfigureHdmi( + hRmDevice, NvRmClockSource_PllC0, &TargetFreq); + else + NvRmPrivAp15PllConfigureSimple( + hRmDevice, NvRmClockSource_PllC0, TargetFreq, &TargetFreq); + + PllCRestoreCpuClock(hRmDevice, TargetFreq, CpuFreq); + PllCRestoreSystemClock(hRmDevice, TargetFreq, SysFreq); + PllCRestoreModuleClocks(hRmDevice, TargetFreq); + +#if !NV_OAL + // Resync DFS as PLLC may be reconfigured for display "behind DFS back" + if (NvRmPrivGetExecPlatform(hRmDevice) == ExecPlatform_Soc) + NvRmPrivDfsResync(); +#endif +} + +void NvRmPrivBoostPllC(NvRmDeviceHandle hRmDevice) +{ + // Boost PLLC to maximum output, if it is not used as pixel clock source + if (!NvRmPrivIsSourceSelectedByModule(hRmDevice, NvRmClockSource_PllC0, + NVRM_MODULE_ID(NvRmModuleID_Display, 0)) && + !NvRmPrivIsSourceSelectedByModule(hRmDevice, NvRmClockSource_PllC0, + NVRM_MODULE_ID(NvRmModuleID_Display, 1)) + ) + NvRmPrivReConfigurePllC(hRmDevice, NvRmFreqMaximum); +} + +/*****************************************************************************/ + + + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/nvrm_diag.c b/arch/arm/mach-tegra/nvrm/core/ap15/nvrm_diag.c new file mode 100644 index 000000000000..f8b1b7322232 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/nvrm_diag.c @@ -0,0 +1,1376 @@ +/* + * Copyright (c) 2007-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvrm_diag.h" +#include "nvrm_clocks.h" +#include "nvassert.h" +#include "nvrm_hwintf.h" +#include "nvrm_pmu.h" +#include "nvrm_pmu_private.h" +#include "nvodm_query_discovery.h" +#include "ap15rm_private.h" +#include "ap15/ap15rm_clocks.h" +#include "ap20/ap20rm_clocks.h" +#include "ap15/project_relocation_table.h" + +#if (NV_DEBUG) +#define NVRM_DIAG_PRINTF(x) NvOsDebugPrintf x +#else +#define NVRM_DIAG_PRINTF(x) +#endif + +// TODO: remove this define when it is added to re-location table header +#if !defined(NV_POWERGROUP_INVALID) +#define NV_POWERGROUP_INVALID (0xFFFF) +#endif + +/* + * Holds mapping information between diagnostic module Ids and pointers to + * clock information structures + */ +typedef struct DiagModuleMappingRec +{ + // Index mapping diagnostic module Id into the base pointer to the + // respective module clock information structure + NvU32 BaseIndex; + + // Total number of the module instances + NvU32 InstancesNum; +} DiagModuleMapping; + +/* + * Combines modules diagnostic information + */ +typedef struct NvRmDiagModulesRec +{ + // Size of module information table + NvU32 ModuleClockTableSize; + + // Module clock and reset information table + const NvRmModuleClockInfo* ModuleClockTable; + + // Table of module instnace pointers into the information table + const NvRmModuleClockInfo** pInstancePtrs; + + // Mapping indexes of module insatances + DiagModuleMapping InstancesMap[NvRmDiagModuleID_Num]; +} NvRmDiagModules; + +/* + * Combines clock sources diagnostic information + */ +typedef struct NvRmDiagSourcesRec +{ + // Total number of available clock sources + NvU32 ClockSourcesNum; + + // Map between clock source IDs and handles + NvRmDiagClockSourceHandle hSources[NvRmClockSource_Num]; +} NvRmDiagSources; + +// RM handle for diagnostic mode +NvRmDeviceHandle s_hDiagRm = NULL; + +/* + * Holds mapping information between power rails and module power + * groups + */ +typedef struct NvRmDiagPowerRailRec +{ + // Power rail GUID + NvU64 PowerRailId; + + // List of power group IDs mapped to this rail, terminated + // by invalid power group ID + NvU32 PowerRailGroups[NV_POWERGROUP_MAX + 1]; +} NvRmDiagPowerRail; + +/* + * Combines power rails diagnostic information + */ +typedef struct NvRmDiagRailsRec +{ + // Total number of available module rails + NvU32 PowerRailsNum; + + // Power Rails information table + const NvRmDiagPowerRail* PowerRailsTable; + + // Combined Module ID and instance of the PMU communication + // interface controller + NvRmDiagModuleID PmuBusHostDiagId; + NvRmModuleID PmuBusHostRmId; +} NvRmDiagRails; + +/*****************************************************************************/ + +static const NvRmDiagPowerRail s_Ap15PowerRailsTable[] = +{ + { + NV_VDD_RTC_ODM_ID, + { + NV_POWERGROUP_AO, + NV_POWERGROUP_INVALID + } + }, + + { + NV_VDD_CORE_ODM_ID, + { + NV_POWERGROUP_NPG, + NV_POWERGROUP_CPU, + NV_POWERGROUP_TD, + NV_POWERGROUP_VE, + NV_POWERGROUP_INVALID + } + }, + + { + NV_VDD_PLLA_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLM_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLP_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLC_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLD_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLU_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLU1_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLHDMI_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_OSC_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + + { + NV_VDD_SYS_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_USB_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_HDMI_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_MIPI_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_LCD_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_AUD_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_DDR_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_NAND_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_UART_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_SDIO_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_VDAC_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_VI_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_BB_ODM_ID, + { + NV_POWERGROUP_INVALID + } + } +}; +static const NvU32 s_Ap15PowerRailsTableSize = NV_ARRAY_SIZE(s_Ap15PowerRailsTable); + +static const NvRmDiagPowerRail s_Ap20PowerRailsTable[] = +{ + { + NV_VDD_RTC_ODM_ID, + { + NV_POWERGROUP_AO, + NV_POWERGROUP_INVALID + } + }, + + { + NV_VDD_CORE_ODM_ID, + { + NV_POWERGROUP_NPG, + NV_POWERGROUP_TD, + NV_POWERGROUP_VE, + NV_POWERGROUP_INVALID + } + }, + + { + NV_VDD_CPU_ODM_ID, + { + NV_POWERGROUP_CPU, + NV_POWERGROUP_INVALID + } + }, + + { + NV_VDD_PLLA_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLM_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLP_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLC_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLD_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLU_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLU1_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLHDMI_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_PLLX_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_OSC_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + + { + NV_VDD_SYS_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_USB_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_HDMI_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_MIPI_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_LCD_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_AUD_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_DDR_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_NAND_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_UART_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_SDIO_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_VDAC_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_VI_ODM_ID, + { + NV_POWERGROUP_INVALID + } + }, + { + NV_VDD_BB_ODM_ID, + { + NV_POWERGROUP_INVALID + } + } +}; +static const NvU32 s_Ap20PowerRailsTableSize = NV_ARRAY_SIZE(s_Ap20PowerRailsTable); + +static const NvU64 s_ApClockSourceNames[] = +{ + 0x0, +#define NVRM_CLOCK_SOURCE(A, B, C, D, E, F, G, H, x) \ +((NvU64)((((A)&0xFFULL)<<56) | \ + (((B)&0xFFULL)<<48) | \ + (((C)&0xFFULL)<<40) | \ + (((D)&0xFFULL)<<32) | \ + (((E)&0xFFULL)<<24) | \ + (((F)&0xFFULL)<<16) | \ + (((G)&0xFFULL)<<8) | \ + (((H)&0xFFULL))) ), + #include "nvrm_clockids.h" +#undef NVRM_CLOCK_SOURCE +}; + +// Power rails diagnostic information +NvRmDiagRails s_Rails = {0}; + +// Modules diagnostic information +NvRmDiagModules s_Modules = {0}; + +// Clock sources diagnostic information +NvRmDiagSources s_Sources = {0}; + +/*****************************************************************************/ + +static NvRmModuleID MapDiagIdToRmId(NvRmDiagModuleID DiagId); + +/*****************************************************************************/ + +NvError +NvRmDiagEnable(NvRmDeviceHandle hRmDevice) +{ + NvU32 i, index; + size_t s; + NvError error; + void* p = NULL; + + /* + * Initialize RM handle, which indicates enabled diagnastic mode + */ + NV_ASSERT(hRmDevice); + if (s_hDiagRm != NULL) + return NvSuccess; // Already enabled and initialized + s_hDiagRm = hRmDevice; + + /* + * Fill in modules information clear instance map, and allocate + * module instance pointers table + */ + if (hRmDevice->ChipId.Id == 0x20) + { + s_Modules.ModuleClockTableSize = g_Ap20ModuleClockTableSize; + s_Modules.ModuleClockTable = g_Ap20ModuleClockTable; + s_Rails.PowerRailsNum = s_Ap20PowerRailsTableSize; + s_Rails.PowerRailsTable = s_Ap20PowerRailsTable; + } else + { + s_Modules.ModuleClockTableSize = g_Ap15ModuleClockTableSize; + s_Modules.ModuleClockTable = g_Ap15ModuleClockTable; + s_Rails.PowerRailsNum = s_Ap15PowerRailsTableSize; + s_Rails.PowerRailsTable = s_Ap15PowerRailsTable; + } + + s_Rails.PmuBusHostDiagId = NvRmDiagModuleID_Dvc; // Default for AP15 + + + NV_ASSERT(s_Modules.ModuleClockTableSize); + + NvOsMemset(s_Modules.InstancesMap, 0, sizeof(s_Modules.InstancesMap)); + s = sizeof(NvRmModuleClockInfo*) * s_Modules.ModuleClockTableSize; + p = NvOsAlloc(s); + if (!p) + { + error = NvError_InsufficientMemory; + goto failed; + } + NvOsMemset(p, 0, s); + s_Modules.pInstancePtrs = p; + + /* + * Parse module clock/reset information table and fill in mapping arrays. + * The table lists all valid (present) modules and only valid modules. + */ + // 1st pass - count module instances + for (i = 0; i < s_Modules.ModuleClockTableSize; i++) + { + NvRmDiagModuleID id = s_Modules.ModuleClockTable[i].DiagModuleID; + NV_ASSERT((0 < id) && (id < NvRmDiagModuleID_Num)); + s_Modules.InstancesMap[id].InstancesNum++; + } + + // 2nd pass - fill in mapping indexes + for (index = 0, i = 0; i < NvRmDiagModuleID_Num; i++) + { + DiagModuleMapping* pMapping = &s_Modules.InstancesMap[i]; + if (pMapping->InstancesNum != 0) + { + pMapping->BaseIndex = index; + index += pMapping->InstancesNum; + NV_ASSERT(index <= s_Modules.ModuleClockTableSize); + } + } + + // 3rd pass - fill in instance pointers + for (i = 0; i < s_Modules.ModuleClockTableSize; i++) + { + DiagModuleMapping* pMapping = + &s_Modules.InstancesMap[s_Modules.ModuleClockTable[i].DiagModuleID]; + NvU32 instance = s_Modules.ModuleClockTable[i].Instance; + index = pMapping->BaseIndex + instance; + + NV_ASSERT(instance < pMapping->InstancesNum); + NV_ASSERT(s_Modules.pInstancePtrs[index] == NULL); + + s_Modules.pInstancePtrs[index] = &s_Modules.ModuleClockTable[i]; + } + + // Convert PMU Host diagnostic ID to common RM ID + s_Rails.PmuBusHostRmId = MapDiagIdToRmId(s_Rails.PmuBusHostDiagId); + + /* + * Parse clock sources information table and map clock source IDs + * to handles. Count total available sources. + */ + NvOsMemset(s_Sources.hSources, 0, sizeof(s_Sources.hSources)); + for (s_Sources.ClockSourcesNum = 0, i = 1; i < NvRmClockSource_Num; i++) + { + s_Sources.hSources[i] = NvRmPrivGetClockSourceHandle(i); + if (s_Sources.hSources[i] != NULL) + s_Sources.ClockSourcesNum++; + } + + // Make sure DFS is not running + NvRmDfsSetState(s_hDiagRm, NvRmDfsRunState_Stopped); + return NvSuccess; + +failed: + NvOsFree(p); + NvOsMemset(&s_Modules, 0, sizeof(s_Modules)); + NvOsMemset(&s_Sources, 0, sizeof(s_Sources)); + s_hDiagRm = NULL; + return error; +} + +/*****************************************************************************/ + +NvError +NvRmDiagListModules( + NvU32* pListSize, + NvRmDiagModuleID* pIdList) +{ + NvU32 ModulesNum, i; + + NV_ASSERT(pListSize); + NV_ASSERT(pIdList); + + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + ModulesNum = s_Modules.ModuleClockTableSize; + + // Return total number of modules if no room for the output list + if ((*pListSize) == 0) + { + *pListSize = ModulesNum; + return NvSuccess; + } + + // Return modules list (min of requested and total size) + if ((*pListSize) > ModulesNum) + { + (*pListSize) = ModulesNum; + } + for (i = 0; i < (*pListSize); i++, pIdList++) + { + const NvRmModuleClockInfo* pCinfo = &s_Modules.ModuleClockTable[i]; + *pIdList = NVRM_DIAG_MODULE(pCinfo->DiagModuleID, pCinfo->Instance); + } + return NvSuccess; +} + +NvError +NvRmDiagListClockSources( + NvU32* pListSize, + NvRmDiagClockSourceHandle* phSourceList) +{ + NvU32 SourcesNum, i; + NV_ASSERT(pListSize); + NV_ASSERT(phSourceList); + + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + + // Return total number of sources if no room for the output list + if ((*pListSize) == 0) + { + *pListSize = s_Sources.ClockSourcesNum; + return NvSuccess; + } + + // Return sources list (min of requested and total size) + for (SourcesNum = 0, i = 0; i < NvRmClockSource_Num; i++) + { + NvRmDiagClockSourceHandle hSource = s_Sources.hSources[i]; + if (hSource != NULL) + { + SourcesNum++; + *(phSourceList++) = hSource; + if (SourcesNum >= (*pListSize)) + break; + } + } + *pListSize = SourcesNum; + return NvSuccess; +} + +/*****************************************************************************/ + +NvError +NvRmDiagModuleListClockSources( + NvRmDiagModuleID id, + NvU32 * pListSize, + NvRmDiagClockSourceHandle* phSourceList) +{ + NvU32 SourcesNum, i; + const NvRmModuleClockInfo* pCinfo = NULL; + NvU32 Instance = NVRM_DIAG_MODULE_INSTANCE(id); + NvRmDiagModuleID Module = NVRM_DIAG_MODULE_ID(id); + + NV_ASSERT(pListSize); + NV_ASSERT(phSourceList); + + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + + // Verify module id and get module info + NV_ASSERT((Module < NvRmDiagModuleID_Num) && + (Instance < s_Modules.InstancesMap[Module].InstancesNum)); + pCinfo = + s_Modules.pInstancePtrs[s_Modules.InstancesMap[Module].BaseIndex + Instance]; + + /* + * Return total number of module sources if no room for the output list, + * otherwise return module sources list (min of requested and total size) + */ + for (SourcesNum = 0, i = 0; i < NvRmClockSource_Num; i++) + { + NvRmClockSource source = pCinfo->Sources[i]; + NV_ASSERT(source < NvRmClockSource_Num); + if (source != NvRmClockSource_Invalid) + { + SourcesNum++; + if ((*pListSize) != 0) + { + *phSourceList = s_Sources.hSources[source]; + NV_ASSERT(*phSourceList); + phSourceList++; + if (SourcesNum >= (*pListSize)) + break; + } + } + } + *pListSize = SourcesNum; + return NvSuccess; +} + +NvError +NvRmDiagModuleClockEnable( + NvRmDiagModuleID id, + NvBool enable) +{ + NvU32 reg, offset; + const NvRmModuleClockInfo* pCinfo = NULL; + NvU32 Instance = NVRM_DIAG_MODULE_INSTANCE(id); + NvRmDiagModuleID Module = NVRM_DIAG_MODULE_ID(id); + + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + + // Verify module id and get module info + NV_ASSERT((Module < NvRmDiagModuleID_Num) && + (Instance < s_Modules.InstancesMap[Module].InstancesNum)); + pCinfo = + s_Modules.pInstancePtrs[s_Modules.InstancesMap[Module].BaseIndex + Instance]; + + // Set/Clear clock control bit(s), if any + if (pCinfo->ClkEnableField != 0) + { + offset = pCinfo->ClkEnableOffset; + NV_ASSERT(offset); + reg = NV_REGR(s_hDiagRm, NvRmPrivModuleID_ClockAndReset, 0, offset); + reg = enable ? + (reg | pCinfo->ClkEnableField) : (reg & (~ pCinfo->ClkEnableField)); + NV_REGW(s_hDiagRm, NvRmPrivModuleID_ClockAndReset, 0, offset, reg); + } + return NvSuccess; +} + +NvError +NvRmDiagModuleClockConfigure( + NvRmDiagModuleID id, + NvRmDiagClockSourceHandle hSource, + NvU32 divider, + NvBool Source1st) +{ + NvU32 reg, offset, SrcIndex; + const NvRmModuleClockInfo* pCinfo = NULL; + NvU32 Instance = NVRM_DIAG_MODULE_INSTANCE(id); + NvRmDiagModuleID Module = NVRM_DIAG_MODULE_ID(id); + + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + + // Verify source handle, module id, and get module info + NV_ASSERT((hSource != NULL) && + (Module < NvRmDiagModuleID_Num) && + (Instance < s_Modules.InstancesMap[Module].InstancesNum)); + pCinfo = + s_Modules.pInstancePtrs[s_Modules.InstancesMap[Module].BaseIndex + Instance]; + + /* + * Find source index for the specified module and source handle. If not + * found report invalid handle. If module has fixed clock source and no + * divider, return success. + */ + for (SrcIndex = 0; SrcIndex < NvRmClockSource_Num; SrcIndex++) + { + if (hSource->SourceId == pCinfo->Sources[SrcIndex]) + break; + } + NV_ASSERT(SrcIndex != NvRmClockSource_Num); + if ((pCinfo->SourceFieldMask == 0) && (pCinfo->DivisorFieldMask == 0)) + { + return NvSuccess; + } + NV_ASSERT(SrcIndex <= pCinfo->SourceFieldMask); + + /* + * Adjust divider valuse: if module divider is not fractional, shift out + * half step bit. In any case truncate high divider bits to fit module + * divider field. + */ + if (pCinfo->Divider != NvRmClockDivider_Fractional_2) + { + divider >>= 1; + } + divider &= pCinfo->DivisorFieldMask; + + /* + * Update clock control register. The order of source and divider fields + * update is specified by the caller. Insert delay between the updates. + */ + offset = pCinfo->ClkSourceOffset; + NV_ASSERT(offset); + reg = NV_REGR(s_hDiagRm, NvRmPrivModuleID_ClockAndReset, 0, offset); + if (Source1st) + { + reg &= (~(pCinfo->SourceFieldMask << pCinfo->SourceFieldShift)); + reg |= (SrcIndex << pCinfo->SourceFieldShift); + NV_REGW(s_hDiagRm, NvRmPrivModuleID_ClockAndReset, 0, offset, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + } + if (pCinfo->Divider != NvRmClockDivider_None) + { + reg &= (~(pCinfo->DivisorFieldMask << pCinfo->DivisorFieldShift)); + reg |= (divider << pCinfo->DivisorFieldShift); + NV_REGW(s_hDiagRm, NvRmPrivModuleID_ClockAndReset, 0, offset, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + } + if (!Source1st) + { + reg &= (~(pCinfo->SourceFieldMask << pCinfo->SourceFieldShift)); + reg |= (SrcIndex << pCinfo->SourceFieldShift); + NV_REGW(s_hDiagRm, NvRmPrivModuleID_ClockAndReset, 0, offset, reg); + NvOsWaitUS(NVRM_CLOCK_CHANGE_DELAY); + } + return NvSuccess; +} + +NvError +NvRmDiagModuleReset( + NvRmDiagModuleID id, + NvBool KeepAsserted) +{ + NvU32 reg, offset; + const NvRmModuleClockInfo* pCinfo = NULL; + NvU32 Instance = NVRM_DIAG_MODULE_INSTANCE(id); + NvRmDiagModuleID Module = NVRM_DIAG_MODULE_ID(id); + + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + + // Verify module id and get module info + NV_ASSERT((Module < NvRmDiagModuleID_Num) && + (Instance < s_Modules.InstancesMap[Module].InstancesNum)); + pCinfo = + s_Modules.pInstancePtrs[s_Modules.InstancesMap[Module].BaseIndex + Instance]; + + /* + * Assert reset bit and keep it asserted if requested by the caller. + * Otherwise de-assert reset after the delay. + */ + offset = pCinfo->ClkResetOffset; + NV_ASSERT(offset); + reg = NV_REGR(s_hDiagRm, NvRmPrivModuleID_ClockAndReset, 0, offset); + reg |= pCinfo->ClkResetField; + NV_REGW(s_hDiagRm, NvRmPrivModuleID_ClockAndReset, 0, offset, reg); + if (!KeepAsserted) + { + NvOsWaitUS(NVRM_RESET_DELAY); + reg &= (~(pCinfo->ClkResetField)); + NV_REGW(s_hDiagRm, NvRmPrivModuleID_ClockAndReset, 0, offset, reg); + } + return NvSuccess; +} + +/*****************************************************************************/ + +NvU64 NvRmDiagClockSourceGetName( + NvRmDiagClockSourceHandle hSource) +{ + if ((s_hDiagRm == NULL) || + (hSource == NULL) || + (hSource->SourceId == NvRmClockSource_Invalid) || + (hSource->SourceId >= NvRmClockSource_Num)) + { + return 0; + } + return s_ApClockSourceNames[hSource->SourceId]; +} + +NvRmDiagClockSourceType +NvRmDiagClockSourceGetType(NvRmDiagClockSourceHandle hSource) +{ + if ((s_hDiagRm == NULL) || (hSource == NULL)) + { + return 0; + } + // Map RM source types to diagnostic source types + switch (hSource->SourceType) + { + case NvRmClockSourceType_Fixed: + return NvRmDiagClockSourceType_Oscillator; + case NvRmClockSourceType_Pll: + return NvRmDiagClockSourceType_Pll; + case NvRmClockSourceType_Divider: + case NvRmClockSourceType_Core: + case NvRmClockSourceType_Selector: + return NvRmDiagClockSourceType_Scaler; + default: + NV_ASSERT(!"Invalid source type"); + return 0; + } +} + +// TODO: does diagnostic scripts really need these details on scaler types? +NvRmDiagClockScalerType +NvRmDiagClockSourceGetScaler(NvRmDiagClockSourceHandle hSource) +{ + if ((s_hDiagRm == NULL) || (hSource == NULL)) + { + return 0; + } + // Map RM divider types to diagnostic scaler types + switch (hSource->SourceType) + { + case NvRmClockSourceType_Fixed: + case NvRmClockSourceType_Pll: + return NvRmDiagClockScalerType_NoScaler; + case NvRmClockSourceType_Divider: + switch (hSource->pInfo.pDivider->Divider) + { + case NvRmClockDivider_Keeper16: + case NvRmClockDivider_Skipper16: + return NvRmDiagClockScalerType_Divider_M_16; + case NvRmClockDivider_Fractional_2: + case NvRmClockDivider_Integer_1: + case NvRmClockDivider_Integer: + return NvRmDiagClockScalerType_Divider_1_N; + default: + NV_ASSERT(!"Invalid divider type"); + return 0; + } + case NvRmClockSourceType_Core: + return NvRmDiagClockScalerType_Divider_M_N; + case NvRmClockSourceType_Selector: + return NvRmDiagClockScalerType_Doubler; + default: + NV_ASSERT(!"Invalid source type"); + return 0; + } +} + +NvError +NvRmDiagClockSourceListSources( + NvRmDiagClockSourceHandle hSource, + NvU32* pListSize, + NvRmDiagClockSourceHandle * phSourceList) +{ + NvRmClockSource source = NvRmClockSource_Invalid; + NvRmClockSource* Sources = NULL; + + NV_ASSERT(pListSize); + NV_ASSERT(phSourceList); + + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + NV_ASSERT((hSource != NULL) && + (hSource->SourceId != NvRmClockSource_Invalid) && + (hSource->SourceId < NvRmClockSource_Num)); + + switch (hSource->SourceType) + { + // Get input clock ID for single-input clock sources; + // (may be invalid for primary sources) + case NvRmClockSourceType_Fixed: + source = hSource->pInfo.pFixed->InputId; + break; + case NvRmClockSourceType_Pll: + source = hSource->pInfo.pPll->InputId; + break; + case NvRmClockSourceType_Divider: + source = hSource->pInfo.pDivider->InputId; + break; + // Get pointer to the source array for core and selector sources + // (must be valid) + case NvRmClockSourceType_Core: + Sources = hSource->pInfo.pCore->Sources; + NV_ASSERT(Sources); + break; + case NvRmClockSourceType_Selector: + Sources = hSource->pInfo.pSelector->Sources; + NV_ASSERT(Sources); + break; + default: + NV_ASSERT(!"Invalid source type"); + } + if (Sources != NULL) + { + // Return total number of input sources if no room for the output list, + // otherwise return sources list (min of requested and total size) + NvU32 SourcesNum, i; + for (SourcesNum = 0, i = 0; i < NvRmClockSource_Num; i++) + { + NvRmClockSource source = Sources[i]; + NV_ASSERT(source < NvRmClockSource_Num); + if (source != NvRmClockSource_Invalid) + { + SourcesNum++; + if ((*pListSize) != 0) + { + *phSourceList = s_Sources.hSources[source]; + NV_ASSERT(*phSourceList); + phSourceList++; + if (SourcesNum >= (*pListSize)) + break; + } + } + } + *pListSize = SourcesNum; + } + else if (source != NvRmClockSource_Invalid) + { + //Only one input source is available. Return the resepctive handle + // if requested. + NV_ASSERT(source < NvRmClockSource_Num); + if ((*pListSize) != 0) + *phSourceList = s_Sources.hSources[source]; + *pListSize = 1; + } + else + { + // Primary source (e.g., oscillator). No (= zero) input sources. + *pListSize = 0; + } + return NvSuccess; +} + +/*****************************************************************************/ + +NvU32 NvRmDiagOscillatorGetFreq(NvRmDiagClockSourceHandle hOscillator) +{ + + if ((s_hDiagRm == NULL) || (hOscillator == NULL) || + (hOscillator->SourceId == NvRmClockSource_Invalid) || + (hOscillator->SourceType != NvRmClockSourceType_Fixed)) + { + return 0; + } + return NvRmPrivGetClockSourceFreq(hOscillator->SourceId); +} + +NvError +NvRmDiagPllConfigure( + NvRmDiagClockSourceHandle hPll, + NvU32 M, + NvU32 N, + NvU32 P) +{ + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + NV_ASSERT((hPll != NULL) && + (hPll->SourceId != NvRmClockSource_Invalid) && + (hPll->SourceType == NvRmClockSourceType_Pll)); + + NvRmPrivAp15PllSet(s_hDiagRm, hPll->pInfo.pPll, M, N, P, (NvU32)-1, + 0, 0, NV_TRUE, NvRmPllConfigFlags_Override); + return NvSuccess; +} + +NvError +NvRmDiagClockScalerConfigure( + NvRmDiagClockSourceHandle hScaler, + NvRmDiagClockSourceHandle hInput, + NvU32 M, + NvU32 N) +{ + NvU32 setting = 0; + + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + NV_ASSERT(hScaler != NULL); + + switch (hScaler->SourceType) + { + case NvRmClockSourceType_Divider: + switch (hScaler->pInfo.pDivider->Divider) + { + case NvRmClockDivider_Keeper16: + setting = M >> 1; + break; + case NvRmClockDivider_Skipper16: + setting = (~(M >> 1)); + break; + case NvRmClockDivider_Fractional_2: + setting = N; + break; + case NvRmClockDivider_Integer_1: + case NvRmClockDivider_Integer: + setting = N >> 1; + break; + default: + NV_ASSERT(!"Invalid divider type"); + } + NvRmPrivDividerSet(s_hDiagRm, hScaler->pInfo.pDivider, setting); + return NvSuccess; + + case NvRmClockSourceType_Core: + NvRmPrivCoreClockSet(s_hDiagRm, hScaler->pInfo.pCore, + hInput->SourceId, (M >> 1), (N >> 1)); + break; + case NvRmClockSourceType_Selector: + NvRmPrivSelectorClockSet(s_hDiagRm, hScaler->pInfo.pSelector, + hInput->SourceId, (M != 0)); + break; + case NvRmClockSourceType_Pll: + case NvRmClockSourceType_Fixed: + NV_ASSERT(!" Diag Clock Scaler Config: illegal clock source. "); + break; + default: + NV_ASSERT(!"Invalid source type"); + break; + } + return NvSuccess; +} + +/*****************************************************************************/ + +/* + * Gets power group for the specified module if it is one of system modules, not + * present in the relocation table. Otherwise, returns NV_POWERGROUP_INVALID. + */ +static NvU32 +DiagGetSystemModulePowerGroup(const NvRmModuleClockInfo* pCinfo); + +static NvU32 +DiagGetSystemModulePowerGroup(const NvRmModuleClockInfo* pCinfo) +{ + NvU32 PowerGroup = NV_POWERGROUP_INVALID; + switch (pCinfo->Module) + { + case NvRmModuleID_CacheMemCtrl: + if (pCinfo->Instance == 0) + break; // CPU cache controller is present + // fall through if AVP cache controller + case NvRmModuleID_Invalid: + case NvRmPrivModuleID_System: + case NvRmModuleID_Avp: + PowerGroup = NV_POWERGROUP_NPG; + break; + case NvRmModuleID_Cpu: + PowerGroup = NV_POWERGROUP_CPU; + break; + default: + break; + } + return PowerGroup; +} + +NvError +NvRmDiagListPowerRails( + NvU32* pListSize, + NvRmDiagPowerRailHandle* phRailList) +{ + NvU32 RailsNum, i; + + NV_ASSERT(pListSize); + NV_ASSERT(phRailList); + + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + RailsNum = s_Rails.PowerRailsNum; + + // Return total number of rails if no room for the output list + if ((*pListSize) == 0) + { + *pListSize = RailsNum; + return NvSuccess; + } + + // Return rails list (min of requested and total size) + if ((*pListSize) > RailsNum) + { + (*pListSize) = RailsNum; + } + for (i = 0; i < (*pListSize); i++, phRailList++) + { + *phRailList = (NvRmDiagPowerRailHandle)&s_Rails.PowerRailsTable[i]; + } + return NvSuccess; +} + +NvU64 +NvRmDiagPowerRailGetName(NvRmDiagPowerRailHandle hRail) +{ + if ((s_hDiagRm == NULL) || (hRail == NULL)) + { + return 0; + } + return hRail->PowerRailId; +} + +NvError +NvRmDiagModuleListPowerRails( + NvRmDiagModuleID id, + NvU32* pListSize, + NvRmDiagPowerRailHandle* phRailList) +{ + + NvU32 ModulePowerGroup, i; + const NvRmDiagPowerRail* pRail = NULL; + const NvRmModuleClockInfo* pCinfo = NULL; + NvU32 Instance = NVRM_DIAG_MODULE_INSTANCE(id); + NvRmDiagModuleID Module = NVRM_DIAG_MODULE_ID(id); + + NV_ASSERT(pListSize); + NV_ASSERT(phRailList); + + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + + // Verify module id + NV_ASSERT((Module < NvRmDiagModuleID_Num) && + (Instance < s_Modules.InstancesMap[Module].InstancesNum)); + + // One rail per module; just return if no room to return handle + if ((*pListSize) == 0) + { + *pListSize = 1; + return NvSuccess; + } + + // Get module power group + pCinfo = + s_Modules.pInstancePtrs[s_Modules.InstancesMap[Module].BaseIndex + Instance]; + ModulePowerGroup = DiagGetSystemModulePowerGroup(pCinfo); + if (ModulePowerGroup == NV_POWERGROUP_INVALID) + { + NvRmModuleInstance* pInst = NULL; + NV_ASSERT_SUCCESS(NvRmPrivGetModuleInstance( + s_hDiagRm, NVRM_MODULE_ID(pCinfo->Module, pCinfo->Instance), &pInst)); + ModulePowerGroup = pInst->DevPowerGroup; + } + NV_ASSERT(ModulePowerGroup != NV_POWERGROUP_INVALID); + + // Find the power rail for the group + for (i = 0; i < s_Rails.PowerRailsNum; i++) + { + const NvU32* pPowerGroup = s_Rails.PowerRailsTable[i].PowerRailGroups; + while ((*pPowerGroup) != NV_POWERGROUP_INVALID) + { + if ((*pPowerGroup) == ModulePowerGroup) + { + pRail = &s_Rails.PowerRailsTable[i]; + break; + } + pPowerGroup++; + NV_ASSERT(pPowerGroup < (s_Rails.PowerRailsTable[i].PowerRailGroups + + NV_ARRAY_SIZE(s_Rails.PowerRailsTable[i].PowerRailGroups))); + } + if (pRail) + break; + } + + // Return power rail found + NV_ASSERT(pRail); + *phRailList = (NvRmDiagPowerRailHandle)pRail; + *pListSize = 1; + return NvSuccess; +} + +NvError +NvRmDiagConfigurePowerRail( + NvRmDiagPowerRailHandle hRail, + NvU32 VoltageMV) +{ + NvU32 TimeUs = 0; + NvU32 RailAddress = 0; + const NvOdmPeripheralConnectivity* pPmuRail = NULL; + + if (s_hDiagRm == NULL) + { + return NvError_NotInitialized; + } + + // Verify that targeted rail can be found on the board, and + // it is connected to PMU + if (hRail != NULL) + { + pPmuRail = NvOdmPeripheralGetGuid(hRail->PowerRailId); + } + if((pPmuRail == NULL) || (pPmuRail->NumAddress == 0)) + { + NV_ASSERT(!"Invalid power rail"); + return NvError_NotSupported; + } + + // Change voltage, and wait for settling time. + RailAddress = pPmuRail->AddressList[0].Address; + NVRM_DIAG_PRINTF(("Setting PMU rail %2d to %5dmV\n", RailAddress, VoltageMV)); + if (NvRmPrivDiagPmuSetVoltage(s_hDiagRm, RailAddress, VoltageMV, &TimeUs)) + { + NvOsWaitUS(TimeUs); + return NvSuccess; + } + return NvError_Busy; +} + +/*****************************************************************************/ + +static NvRmModuleID +MapDiagIdToRmId(NvRmDiagModuleID DiagId) +{ + const NvRmModuleClockInfo* pCinfo = NULL; + NvU32 Instance = NVRM_DIAG_MODULE_INSTANCE(DiagId); + NvRmDiagModuleID Module = NVRM_DIAG_MODULE_ID(DiagId); + + NvRmModuleID RmId = NvRmModuleID_Invalid; + if ((Module < NvRmDiagModuleID_Num) && + (Instance < s_Modules.InstancesMap[Module].InstancesNum)) + { + pCinfo = s_Modules.pInstancePtrs[ + s_Modules.InstancesMap[Module].BaseIndex + Instance]; + RmId = NVRM_MODULE_ID(pCinfo->Module, pCinfo->Instance); + } + return RmId; +} + +NvBool NvRmPrivIsDiagMode(NvRmModuleID ModuleId) +{ + if (s_hDiagRm == NULL) + return NV_FALSE; // Report no diagnostic in progress + + if (ModuleId == NvRmModuleID_Invalid) + return NV_TRUE; // Report diagnostic is in progress + + // Report diagnostic is in progress for any module except PMU bus host + return (ModuleId != s_Rails.PmuBusHostRmId); +} + +NvBool NvRmDiagIsLockSupported(void) +{ +#if NVRM_DIAG_LOCK_SUPPORTED + return NV_TRUE; +#else + return NV_FALSE; +#endif +} + +/*****************************************************************************/ + -- cgit v1.2.3