summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/nvrm/core/ap15
diff options
context:
space:
mode:
authorGary King <gking@nvidia.com>2010-05-14 10:36:33 -0700
committerGary King <gking@nvidia.com>2010-05-14 20:04:06 -0700
commit106de33bf7f410bade659e110a5a7b187b46b8b2 (patch)
tree4d8231dc38fb3c05b6ccb911ff1e3b840d1d444b /arch/arm/mach-tegra/nvrm/core/ap15
parente0426ba3077eae7e326c56487f34719f9638ddb5 (diff)
[ARM/tegra] add NvRm, ODM services, ODM kit for harmony & whistler
add power rail support to GPIO driver Change-Id: I45d4c1110a635047d68fb14f3e72a28f99acbe1b
Diffstat (limited to 'arch/arm/mach-tegra/nvrm/core/ap15')
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/Makefile35
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_config.c2723
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clock_misc.c511
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.c931
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks.h460
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_clocks_info.c1673
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_fuse.c104
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_hwmap.c51
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c682
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init_common.c521
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_interrupt.c314
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_interrupt_generic.c86
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_memctrl.c611
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux.c398
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux_tables.c1185
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pinmux_utils.h147
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_pmc_scratch_map.h73
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power.c663
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.c544
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_dfs.h314
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_power_oalintf.c334
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_private.h331
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_reloctable.c50
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c432
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c165
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h92
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap16rm_pinmux_tables.c323
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap16rm_reloctable.c50
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/nvrm_clocks.c3311
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/nvrm_diag.c1376
30 files changed, 18490 insertions, 0 deletions
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, &reg);
+
+ // 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 <b>NVIDIA Driver Development Kit: Fuse API</b>
+ *
+ * @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(StackDepth<MAX_NESTING_DEPTH);
+ ReturnStack[++StackDepth] = Instance+1;
+ Instance = NvRmPrivAp15FindConfigStart(Module,
+ NV_DRF_VAL(MUX,ENTRY,BRANCH_ADDRESS,*Instance),
+ SUBROUTINESDONE());
+ NV_ASSERT(Instance && "Invalid branch configuration in table!\n");
+ break;
+ case PinMuxConfig_Set:
+ {
+ NvS16 SkipUpdate;
+ NvU32 TsOffs = NV_DRF_VAL(MUX,ENTRY, TS_OFFSET, *Instance);
+ NvU32 TsShift = NV_DRF_VAL(MUX,ENTRY, TS_SHIFT, *Instance);
+
+/* abuse pre/post-increment, to ensure that skipUpdate is 0 when the
+ * register needs to be programmed (i.e., enabling and previous value was 0,
+ * or disabling and new value is 0).
+ */
+ 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<<TsShift);
+#if (SKIP_TRISTATE_REFCNT == 0)
+ Curr |= (EnableTristate?1:0)<<TsShift;
+#endif
+ NV_REGW(hDevice, NvRmModuleID_Misc, 0,
+ APB_MISC_PP_TRISTATE_REG_A_0 + 4*TsOffs, Curr);
+
+#if NVRM_PINMUX_DEBUG_FLAG
+ NV_DEBUG_PRINTF(("Setting TRISTATE_REG_%s to %s\n",
+ (const char*)Instance[2],
+ (EnableTristate)?"TRISTATE" : "NORMAL"));
+#endif
+ }
+ }
+ /* fall through.
+ * The "Unset" configurations are not applicable to tristate
+ * configuration, so skip over them. */
+ case PinMuxConfig_Unset:
+ Instance += NVRM_PINMUX_SET_OPCODE_SIZE;
+ break;
+ }
+ }
+ NvOsMutexUnlock(hDevice->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<MAX_NESTING_DEPTH);
+ ReturnStack[++StackDepth] = Instance+1;
+ Instance = NvRmPrivAp15FindConfigStart(Module,
+ NV_DRF_VAL(MUX,ENTRY,BRANCH_ADDRESS,*Instance),
+ SUBROUTINESDONE());
+ NV_ASSERT(Instance && "Invalid branch configuration in table!\n");
+ break;
+ default:
+ {
+ MuxCtlOffset = NV_DRF_VAL(MUX,ENTRY, MUX_CTL_OFFSET, *Instance);
+ MuxCtlShift = NV_DRF_VAL(MUX,ENTRY, MUX_CTL_SHIFT, *Instance);
+ MuxCtlUnset = NV_DRF_VAL(MUX,ENTRY, MUX_CTL_UNSET, *Instance);
+ MuxCtlSet = NV_DRF_VAL(MUX,ENTRY, MUX_CTL_SET, *Instance);
+ MuxCtlMask = NV_DRF_VAL(MUX, ENTRY, MUX_CTL_MASK, *Instance);
+
+ Curr = NV_REGR(hDevice, NvRmModuleID_Misc, 0,
+ APB_MISC_PP_PIN_MUX_CTL_A_0 + 4*MuxCtlOffset);
+
+ if (NV_DRF_VAL(MUX,ENTRY,STATE,*Instance)==PinMuxConfig_Set)
+ {
+ Curr &= ~(MuxCtlMask<<MuxCtlShift);
+ Curr |= (MuxCtlSet<<MuxCtlShift);
+#if NVRM_PINMUX_DEBUG_FLAG
+ NV_DEBUG_PRINTF(("Configuring PINMUX_CTL_%s\n",
+ (const char *)Instance[1]));
+#endif
+
+ }
+ else if (((Curr>>MuxCtlShift)&MuxCtlMask)==MuxCtlUnset)
+ {
+ NV_ASSERT(NV_DRF_VAL(MUX,ENTRY,STATE,
+ *Instance)==PinMuxConfig_Unset);
+ Curr &= ~(MuxCtlMask<<MuxCtlShift);
+ Curr |= (MuxCtlSet<<MuxCtlShift);
+#if NVRM_PINMUX_DEBUG_FLAG
+ NV_DEBUG_PRINTF(("Unconfiguring PINMUX_CTL_%s\n",
+ (const char *)Instance[1]));
+#endif
+ }
+
+ NV_REGW(hDevice, NvRmModuleID_Misc, 0,
+ APB_MISC_PP_PIN_MUX_CTL_A_0 + 4*MuxCtlOffset, Curr);
+ Instance += NVRM_PINMUX_SET_OPCODE_SIZE;
+ break;
+ }
+ }
+ }
+ NvOsMutexUnlock(hDevice->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<<TsShift);
+#if (SKIP_TRISTATE_REFCNT == 0)
+ Curr |= (EnableTristate?1:0)<<TsShift;
+#endif
+ NV_REGW(hDevice, NvRmModuleID_Misc, 0,
+ APB_MISC_PP_TRISTATE_REG_A_0 + 4*TsOffs, Curr);
+ }
+
+ NvOsMutexUnlock(hDevice->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 &amp; 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 &amp;
+ * 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 <b>nVIDIA Driver Development Kit:
+ * Power Management Controller (PMC) scratch registers fields
+ * definitions</b>
+ *
+ * @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 <b>nVIDIA Driver Development Kit:
+ * Power Resource manager </b>
+ *
+ * @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 <b>nVIDIA Driver Development Kit:
+ * Dynamic Frequency Scaling manager </b>
+ *
+ * @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 <b>nVIDIA Driver Development Kit:
+ * Power Resource manager </b>
+ *
+ * @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 <b>nVIDIA Driver Development Kit:
+ * Power Resource manager API shared with OS adaptation layer</b>
+ *
+ * @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 <b>nVIDIA Driver Development Kit:
+ * Cross Proc Communication driver </b>
+ *
+ * @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<MAX_ARB_NUM;i++)
+ {
+ NV_CHECK_ERROR_CLEANUP(
+ NvOsSemaphoreCreate(&s_ArbSema.semaphore[i], 0)
+ );
+
+ NV_CHECK_ERROR_CLEANUP(
+ NvOsMutexCreate(&s_ArbSema.mutex[i])
+ );
+ }
+
+ NV_CHECK_ERROR_CLEANUP(
+ NvOsIntrMutexCreate(&s_ArbSema.hIntrMutex)
+ );
+
+fail:
+
+ return e;
+}
+
+
+static void ArbSemaIsr(void *args)
+{
+ NvU32 int_mask, proc_int_enable, arb_gnt, i = 0;
+
+ NvOsIntrMutexLock(s_ArbSema.hIntrMutex);
+ //Check which arb semaphores have been granted to this processor
+ arb_gnt = ARBSEMA_REG_READ(s_ArbSema.pArbSemaVirtAddr, SMP_GNT_ST);
+
+ //Figure out which arb semaphores were signalled and then disable them.
+#if NV_IS_AVP
+ proc_int_enable = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, COP_ENABLE);
+ int_mask = arb_gnt & proc_int_enable;
+ ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr,
+ COP_ENABLE, (proc_int_enable & ~int_mask));
+#else
+ proc_int_enable = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, CPU_ENABLE);
+ int_mask = arb_gnt & proc_int_enable;
+ ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr,
+ CPU_ENABLE, (proc_int_enable & ~int_mask));
+#endif
+
+ //Signal all the required semaphores
+ do
+ {
+ if (int_mask & 0x1)
+ {
+ NvOsSemaphoreSignal(s_ArbSema.semaphore[i]);
+ }
+ int_mask >>= 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 <b>nVIDIA Driver Development Kit:
+ * Cross Processor Communication driver </b>
+ *
+ * @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 <b>nVIDIA Driver Development Kit:
+ * Priate Hw access function for XPC driver </b>
+ *
+ * @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 &amp; 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 <b>nVIDIA Driver Development Kit:
+ * Clock Resource manager </b>
+ *
+ * @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
+}
+
+/*****************************************************************************/
+